diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 08def88577..e708ae41b2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,7 @@ - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) +- [ ] New developer-facing feature (adds functionality for component developers; no end-user configuration change) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) — [policy](https://developers.esphome.io/contributing/code/#what-constitutes-a-c-breaking-change) - [ ] Developer breaking change (an API change that could break external components) — [policy](https://developers.esphome.io/contributing/code/#what-is-considered-public-c-api) - [ ] Undocumented C++ API change (removal or change of undocumented public methods that lambda users may depend on) — [policy](https://developers.esphome.io/contributing/code/#c-user-expectations) @@ -20,6 +21,10 @@ - esphome/esphome.io# +**Pull request in [developers.esphome.io](https://github.com/esphome/developers.esphome.io) with developer documentation (if applicable):** + +- esphome/developers.esphome.io# + ## Test Environment - [ ] ESP32 diff --git a/.github/actions/cache-esp-idf/action.yml b/.github/actions/cache-esp-idf/action.yml index f566ba4c43..b884e1e4c6 100644 --- a/.github/actions/cache-esp-idf/action.yml +++ b/.github/actions/cache-esp-idf/action.yml @@ -3,8 +3,10 @@ description: > Resolve the pinned ESP-IDF version and cache the native ESP-IDF install (toolchains + source) at ~/.esphome-idf. Every job that installs ESP-IDF natively (clang-tidy for IDF/Arduino and the component test batches) shares - one cache, since the install is identical (ESPHOME_IDF_DEFAULT_TARGETS - defaults to "all", so all toolchains are present regardless of the chip). + one cache, since the install is identical: ESPHOME_IDF_DEFAULT_TARGETS + defaults to "all", and _get_configured_targets() in espidf/toolchain.py + skips per-variant narrowing whenever CI is set, so all toolchains are + present regardless of the chip a job builds. Callers must set env ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf and have the Python venv already restored. inputs: diff --git a/.github/actions/restore-python/action.yml b/.github/actions/restore-python/action.yml index 9d6dc5301c..daf041819c 100644 --- a/.github/actions/restore-python/action.yml +++ b/.github/actions/restore-python/action.yml @@ -17,7 +17,7 @@ runs: steps: - name: Set up Python ${{ inputs.python-version }} id: python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ inputs.python-version }} - name: Restore Python virtual environment @@ -32,7 +32,7 @@ runs: # detects the activated venv via ``VIRTUAL_ENV`` so the venv layout # downstream jobs rely on is preserved. if: steps.cache-venv.outputs.cache-hit != 'true' - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pull request saves land in per-PR scopes nothing else can diff --git a/.github/scripts/auto-label-pr/constants.js b/.github/scripts/auto-label-pr/constants.js index 2938fd923c..5e8acb09c9 100644 --- a/.github/scripts/auto-label-pr/constants.js +++ b/.github/scripts/auto-label-pr/constants.js @@ -13,6 +13,7 @@ module.exports = { 'merging-to-release', 'merging-to-beta', 'chained-pr', + 'stacked-pr', 'core', 'small-pr', 'medium-pr', @@ -22,11 +23,13 @@ module.exports = { 'has-tests', 'needs-tests', 'needs-docs', + 'needs-developer-docs', 'needs-codeowners', 'too-big', 'labeller-recheck', 'bugfix', 'new-feature', + 'new-feature-developer', 'breaking-change', 'developer-breaking-change', 'undocumented-api-change', @@ -40,5 +43,17 @@ module.exports = { // Keep matching the old esphome-docs name during the transition period /https:\/\/github\.com\/esphome\/esphome-docs\/pull\/\d+/, /esphome\/esphome-docs#\d+/ + ], + + DEVELOPER_DOCS_PR_PATTERNS: [ + /https:\/\/github\.com\/esphome\/developers\.esphome\.io\/pull\/\d+/, + /esphome\/developers\.esphome\.io#\d+/ + ], + + // Files whose developer-facing changes are documented via Python docstrings + // only - developers.esphome.io has no reference page for them yet, so PRs + // touching nothing but these files (and tests/) skip needs-developer-docs. + DEV_DOCS_EXEMPT_FILES: [ + 'esphome/config_validation.py' ] }; diff --git a/.github/scripts/auto-label-pr/detectors.js b/.github/scripts/auto-label-pr/detectors.js index 4406370a27..bb85ccd681 100644 --- a/.github/scripts/auto-label-pr/detectors.js +++ b/.github/scripts/auto-label-pr/detectors.js @@ -1,4 +1,4 @@ -const { DOCS_PR_PATTERNS } = require('./constants'); +const { DOCS_PR_PATTERNS, DEVELOPER_DOCS_PR_PATTERNS, DEV_DOCS_EXEMPT_FILES } = require('./constants'); const { COMPONENT_REGEX, detectComponents, @@ -33,8 +33,41 @@ async function fetchPrFileContent(github, context, path) { } } +// Check whether a pull request is part of a GitHub stack. +// +// GitHub's stacked pull request feature adds a `stack` object to the pull +// request resource. It is present on every pull request in the stack - +// including the bottom one, whose base is already `dev` - and is absent +// entirely on standalone pull requests. +// +// The `pull_request_target` webhook payload is not guaranteed to carry this +// field, so fall back to asking the API when it is missing. Guessing wrong +// here is costly: a stacked pull request mistaken for a manually chained one +// gets a label that blocks merging. +async function isStackedPr(github, context) { + const pr = context.payload.pull_request; + if (pr.stack != null) { + return true; + } + + try { + const { owner, repo } = context.repo; + const { data } = await github.rest.pulls.get({ + owner, + repo, + pull_number: pr.number, + }); + return data.stack != null; + } catch (error) { + // Treat an API failure as "not stacked" so a chained pull request still + // gets its blocking label rather than silently slipping through. + console.log('Failed to check stack membership:', error.message); + return false; + } +} + // Strategy: Merge branch detection -async function detectMergeBranch(context) { +async function detectMergeBranch(github, context) { const labels = new Set(); const baseRef = context.payload.pull_request.base.ref; @@ -42,7 +75,11 @@ async function detectMergeBranch(context) { labels.add('merging-to-release'); } else if (baseRef === 'beta') { labels.add('merging-to-beta'); + } else if (await isStackedPr(github, context)) { + // GitHub manages the merge order for a stack, so these are not blocked. + labels.add('stacked-pr'); } else if (baseRef !== 'dev') { + // A chain built by hand: it must not merge until its base branch does. labels.add('chained-pr'); } @@ -245,6 +282,7 @@ async function detectPRTemplateCheckboxes(context) { const checkboxPatterns = [ { pattern: /- \[x\] Bugfix \(non-breaking change which fixes an issue\)/i, label: 'bugfix' }, { pattern: /- \[x\] New feature \(non-breaking change which adds functionality\)/i, label: 'new-feature' }, + { pattern: /- \[x\] New developer-facing feature \(adds functionality for component developers; no end-user configuration change\)/i, label: 'new-feature-developer' }, { pattern: /- \[x\] Breaking change \(fix or feature that would cause existing functionality to not work as expected\)/i, label: 'breaking-change' }, { pattern: /- \[x\] Developer breaking change \(an API change that could break external components\)/i, label: 'developer-breaking-change' }, { pattern: /- \[x\] Undocumented C\+\+ API change \(removal or change of undocumented public methods that lambda users may depend on\)/i, label: 'undocumented-api-change' }, @@ -355,12 +393,14 @@ async function detectRequirements(allLabels, prFiles, context, hasYamlLoadable) const labels = new Set(); // Check for missing tests - if ((allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature')) && !allLabels.has('has-tests')) { + if ((allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature') || allLabels.has('new-feature-developer')) && !allLabels.has('has-tests')) { labels.add('needs-tests'); } // Check for missing docs. - // `new-feature` (PR-body checkbox) always counts. `new-component` / `new-platform` + // `new-feature` (PR-body checkbox) always counts. `new-feature-developer` is + // deliberately excluded here: its docs live on developers.esphome.io and are + // checked separately below. `new-component` / `new-platform` // only count when at least one newly added file defines a top-level CONFIG_SCHEMA, // i.e. the new component/platform is actually loadable from YAML. const docsEligible = @@ -376,6 +416,22 @@ async function detectRequirements(allLabels, prFiles, context, hasYamlLoadable) } } + // Check for missing developer docs. `new-feature-developer` requires a + // developers.esphome.io PR link, unless every changed file outside tests/ is + // in DEV_DOCS_EXEMPT_FILES (core validators documented via docstrings only). + if (allLabels.has('new-feature-developer')) { + const prBody = context.payload.pull_request.body || ''; + const nonTestFiles = prFiles + .map(file => file.filename) + .filter(file => !file.startsWith('tests/')); + const onlyExemptFiles = nonTestFiles.every(file => DEV_DOCS_EXEMPT_FILES.includes(file)); + const hasDevDocsLink = DEVELOPER_DOCS_PR_PATTERNS.some(pattern => pattern.test(prBody)); + + if (!onlyExemptFiles && !hasDevDocsLink) { + labels.add('needs-developer-docs'); + } + } + // Check for missing CODEOWNERS if (allLabels.has('new-component')) { const codeownersModified = prFiles.some(file => diff --git a/.github/scripts/auto-label-pr/index.js b/.github/scripts/auto-label-pr/index.js index c8bdcfb2f3..8b0c821503 100644 --- a/.github/scripts/auto-label-pr/index.js +++ b/.github/scripts/auto-label-pr/index.js @@ -88,7 +88,7 @@ module.exports = async ({ github, context }) => { // Early exit for release and beta branches only if (baseRef === 'release' || baseRef === 'beta') { - const branchLabels = await detectMergeBranch(context); + const branchLabels = await detectMergeBranch(github, context); const finalLabels = Array.from(branchLabels); console.log('Computed labels (merge branch only):', finalLabels.join(', ')); @@ -118,7 +118,7 @@ module.exports = async ({ github, context }) => { deprecatedResult, maintainerAccess ] = await Promise.all([ - detectMergeBranch(context), + detectMergeBranch(github, context), detectComponentPlatforms(changedFiles, apiData), detectNewComponents(github, context, prFiles), detectNewPlatforms(github, context, prFiles, apiData), diff --git a/.github/scripts/auto-label-pr/tests/detectors.test.js b/.github/scripts/auto-label-pr/tests/detectors.test.js index aab1827c44..f30ceff8c1 100644 --- a/.github/scripts/auto-label-pr/tests/detectors.test.js +++ b/.github/scripts/auto-label-pr/tests/detectors.test.js @@ -1,6 +1,14 @@ const { describe, it } = require('node:test'); const assert = require('node:assert/strict'); -const { detectNewPlatforms, detectNewComponents, detectPRSize } = require('../detectors'); +const { + detectMergeBranch, + detectNewPlatforms, + detectNewComponents, + detectPRSize, + detectPRTemplateCheckboxes, + detectRequirements, +} = require('../detectors'); +const { MANAGED_LABELS } = require('../constants'); // Minimal GitHub API mock — only repos.getContent is called by detectNewPlatforms/detectNewComponents // to check for CONFIG_SCHEMA in newly added files. @@ -29,6 +37,107 @@ const API_DATA = { const WITH_SCHEMA = 'CONFIG_SCHEMA = cv.Schema({})'; const WITHOUT_SCHEMA = 'CODEOWNERS = ["@esphome/core"]'; +// --------------------------------------------------------------------------- +// detectMergeBranch +// --------------------------------------------------------------------------- + +// Builds a fresh context for detectMergeBranch tests instead of mutating the +// shared CONTEXT fixture above (which other describe blocks rely on). +function makeMergeContext(baseRef, { stack } = {}) { + const pull_request = { number: 1, base: { ref: baseRef } }; + if (stack !== undefined) { + pull_request.stack = stack; + } + return { + repo: { owner: 'esphome', repo: 'esphome' }, + payload: { pull_request } + }; +} + +// A GitHub API mock exposing only rest.pulls.get, with a call counter so +// tests can assert whether the API fallback was actually invoked. +function makeStackGithub({ stack = null, error = null } = {}) { + const state = { calls: 0 }; + const github = { + rest: { + pulls: { + get: async () => { + state.calls++; + if (error) throw error; + return { data: { stack } }; + } + } + } + }; + return { github, state }; +} + +const STACK_INFO = { base: { ref: 'dev' }, id: 71540, number: 17978, position: 3, size: 3 }; + +describe('detectMergeBranch', () => { + it('base ref release adds merging-to-release only and never checks the stack', async () => { + const { github, state } = makeStackGithub({ stack: STACK_INFO }); + const context = makeMergeContext('release', { stack: STACK_INFO }); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['merging-to-release']); + assert.equal(state.calls, 0); + }); + + it('base ref beta adds merging-to-beta only and never checks the stack', async () => { + const { github, state } = makeStackGithub({ stack: STACK_INFO }); + const context = makeMergeContext('beta', { stack: STACK_INFO }); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['merging-to-beta']); + assert.equal(state.calls, 0); + }); + + it('stack present on the webhook payload adds stacked-pr without calling the API', async () => { + const { github, state } = makeStackGithub(); + const context = makeMergeContext('feature-branch', { stack: STACK_INFO }); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['stacked-pr']); + assert.equal(state.calls, 0); + }); + + it('stack absent from payload falls back to the API and adds stacked-pr', async () => { + const { github, state } = makeStackGithub({ stack: STACK_INFO }); + const context = makeMergeContext('feature-branch'); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['stacked-pr']); + assert.equal(state.calls, 1); + }); + + it('bottom of a stack (base ref dev, stack present) still adds stacked-pr', async () => { + const { github, state } = makeStackGithub(); + const context = makeMergeContext('dev', { stack: STACK_INFO }); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['stacked-pr']); + assert.equal(state.calls, 0); + }); + + it('not stacked, base ref not dev adds chained-pr', async () => { + const { github } = makeStackGithub({ stack: null }); + const context = makeMergeContext('feature-branch'); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['chained-pr']); + }); + + it('not stacked, base ref dev adds no labels', async () => { + const { github } = makeStackGithub({ stack: null }); + const context = makeMergeContext('dev'); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), []); + }); + + it('a failed stack lookup falls back to not-stacked, so a feature-branch base adds chained-pr', async () => { + const { github, state } = makeStackGithub({ error: new Error('API unavailable') }); + const context = makeMergeContext('feature-branch'); + const labels = await detectMergeBranch(github, context); + assert.deepEqual(Array.from(labels).sort(), ['chained-pr']); + assert.equal(state.calls, 1); + }); +}); + // --------------------------------------------------------------------------- // detectNewPlatforms // --------------------------------------------------------------------------- @@ -146,6 +255,125 @@ describe('detectNewComponents', () => { }); }); +// --------------------------------------------------------------------------- +// detectPRTemplateCheckboxes +// --------------------------------------------------------------------------- + +const NEW_FEATURE_LINE = '- [x] New feature (non-breaking change which adds functionality)'; +const DEV_FEATURE_LINE = '- [x] New developer-facing feature (adds functionality for component developers; no end-user configuration change)'; +const DEV_FEATURE_LINE_UNTICKED = '- [ ] New developer-facing feature (adds functionality for component developers; no end-user configuration change)'; + +function makeBodyContext(body) { + return { payload: { pull_request: { body } } }; +} + +describe('detectPRTemplateCheckboxes', () => { + it('ticked developer-facing feature checkbox adds new-feature-developer only', async () => { + const labels = await detectPRTemplateCheckboxes(makeBodyContext(DEV_FEATURE_LINE)); + assert.ok(labels.has('new-feature-developer')); + assert.ok(!labels.has('new-feature')); + }); + + it('unticked developer-facing feature checkbox adds no label', async () => { + const labels = await detectPRTemplateCheckboxes(makeBodyContext(DEV_FEATURE_LINE_UNTICKED)); + assert.ok(!labels.has('new-feature-developer')); + }); + + it('ticked new feature checkbox does not add new-feature-developer', async () => { + const labels = await detectPRTemplateCheckboxes(makeBodyContext(NEW_FEATURE_LINE)); + assert.ok(labels.has('new-feature')); + assert.ok(!labels.has('new-feature-developer')); + }); +}); + +// --------------------------------------------------------------------------- +// detectRequirements +// --------------------------------------------------------------------------- + +describe('detectRequirements', () => { + // PR body without any docs-PR link. + const NO_DOCS_CONTEXT = makeBodyContext('Just a description, no docs link.'); + const USER_DOCS_CONTEXT = makeBodyContext('Docs: esphome/esphome.io#1234'); + const DEV_DOCS_CONTEXT = makeBodyContext('Docs: esphome/developers.esphome.io#1234'); + const DEV_DOCS_URL_CONTEXT = makeBodyContext('Docs: https://github.com/esphome/developers.esphome.io/pull/1234'); + + // File sets: a normal source change vs. one confined to the exempt core validators. + const SOURCE_FILES = [ + { filename: 'esphome/components/foo/foo.py' }, + { filename: 'tests/components/foo/common.yaml' }, + ]; + const VALIDATOR_FILES = [ + { filename: 'esphome/config_validation.py' }, + { filename: 'tests/unit_tests/test_config_validation.py' }, + ]; + + it('new-feature-developer without has-tests adds needs-tests but not needs-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer']), SOURCE_FILES, NO_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-tests')); + assert.ok(!labels.has('needs-docs')); + }); + + it('new-feature-developer with has-tests does not add needs-tests', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), SOURCE_FILES, NO_DOCS_CONTEXT, false); + assert.ok(!labels.has('needs-tests')); + }); + + it('new-feature without a docs link still adds needs-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature', 'has-tests']), [], NO_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-docs')); + }); + + it('new-feature-developer without a developer docs link adds needs-developer-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), SOURCE_FILES, NO_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-developer-docs')); + }); + + it('a developers.esphome.io shorthand link satisfies needs-developer-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), SOURCE_FILES, DEV_DOCS_CONTEXT, false); + assert.ok(!labels.has('needs-developer-docs')); + }); + + it('a developers.esphome.io URL link satisfies needs-developer-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), SOURCE_FILES, DEV_DOCS_URL_CONTEXT, false); + assert.ok(!labels.has('needs-developer-docs')); + }); + + it('a user docs (esphome.io) link does not satisfy needs-developer-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), SOURCE_FILES, USER_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-developer-docs')); + }); + + it('a developer docs link does not satisfy needs-docs for new-feature', async () => { + const labels = await detectRequirements(new Set(['new-feature', 'has-tests']), [], DEV_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-docs')); + }); + + it('changes confined to core validator files are exempt from needs-developer-docs', async () => { + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), VALIDATOR_FILES, NO_DOCS_CONTEXT, false); + assert.ok(!labels.has('needs-developer-docs')); + }); + + it('validator changes mixed with other source files are not exempt', async () => { + const prFiles = [...VALIDATOR_FILES, { filename: 'esphome/components/foo/foo.py' }]; + const labels = await detectRequirements(new Set(['new-feature-developer', 'has-tests']), prFiles, NO_DOCS_CONTEXT, false); + assert.ok(labels.has('needs-developer-docs')); + }); +}); + +// --------------------------------------------------------------------------- +// MANAGED_LABELS +// --------------------------------------------------------------------------- + +describe('MANAGED_LABELS', () => { + it('includes new-feature-developer so the workflow syncs it', () => { + assert.ok(MANAGED_LABELS.includes('new-feature-developer')); + }); + + it('includes needs-developer-docs so the workflow syncs it', () => { + assert.ok(MANAGED_LABELS.includes('needs-developer-docs')); + }); +}); + // --------------------------------------------------------------------------- // detectPRSize // --------------------------------------------------------------------------- diff --git a/.github/workflows/auto-label-pr.yml b/.github/workflows/auto-label-pr.yml index d034227ef6..b49c66b976 100644 --- a/.github/workflows/auto-label-pr.yml +++ b/.github/workflows/auto-label-pr.yml @@ -24,7 +24,7 @@ jobs: if: github.event.pull_request.state == 'open' && (github.event.action != 'labeled' || github.event.sender.type != 'Bot') steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Generate a token id: generate-token @@ -35,7 +35,7 @@ jobs: # Scope the minted App token to the minimum needed by auto-label-pr/*.js. permission-contents: read # repos.getContent for CODEOWNERS and file lookups in detectors.js permission-issues: write # listLabelsOnIssue, addLabels, removeLabel, list/createComment - permission-pull-requests: write # pulls.listFiles, list/create/update/dismissReview + permission-pull-requests: write # pulls.get, pulls.listFiles, list/create/update/dismissReview - name: Auto Label PR uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/ci-api-proto.yml b/.github/workflows/ci-api-proto.yml index 58fc83e3f5..820081cc46 100644 --- a/.github/workflows/ci-api-proto.yml +++ b/.github/workflows/ci-api-proto.yml @@ -21,15 +21,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12" - name: Set up uv # ``--system`` (below) installs into the setup-python interpreter; # no venv is created or restored by this workflow. - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pull-request-only workflow: a save could never be shared and diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 2740ca76ca..30aa511e29 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -61,9 +61,9 @@ jobs: tag: ${{ steps.tag.outputs.tag }} push: ${{ steps.tag.outputs.push }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12" - name: Set up Docker Buildx @@ -71,11 +71,13 @@ jobs: - name: Determine tag and whether to push id: tag + env: + HEAD_REF: ${{ github.head_ref || github.ref_name }} run: | # Sanitize the branch name into a valid docker tag: replace invalid # characters, ensure the first character is valid (tags must start # with [A-Za-z0-9_]), and cap the length at 128 characters. - branch="${{ github.head_ref || github.ref_name }}" + branch="$HEAD_REF" tag="${branch//[^a-zA-Z0-9_.-]/-}" case "$tag" in [a-zA-Z0-9_]*) ;; @@ -96,7 +98,7 @@ jobs: - name: Log in to the GitHub container registry if: steps.tag.outputs.push == 'true' - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -145,16 +147,16 @@ jobs: - "ha-addon" - "docker" steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12" - name: Set up Docker Buildx uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - name: Log in to the GitHub container registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -202,7 +204,7 @@ jobs: - nrf52 - host steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Download image artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: diff --git a/.github/workflows/ci-github-scripts.yml b/.github/workflows/ci-github-scripts.yml index 3313ced690..ea039de9b9 100644 --- a/.github/workflows/ci-github-scripts.yml +++ b/.github/workflows/ci-github-scripts.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run tests working-directory: .github/scripts/auto-label-pr diff --git a/.github/workflows/ci-memory-impact-comment.yml b/.github/workflows/ci-memory-impact-comment.yml index ac0322e2fa..0dc653bd39 100644 --- a/.github/workflows/ci-memory-impact-comment.yml +++ b/.github/workflows/ci-memory-impact-comment.yml @@ -49,7 +49,7 @@ jobs: - name: Check out code from base repository if: steps.pr.outputs.skip != 'true' - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Always check out from the base repository (esphome/esphome), never from forks # Use the PR's target branch to ensure we run trusted code from the main repo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e98999741..b6fba50625 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,13 +28,13 @@ jobs: cache-key: ${{ steps.cache-key.outputs.key }} steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Generate cache-key id: cache-key run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment @@ -49,7 +49,7 @@ jobs: # detects the activated venv via ``VIRTUAL_ENV`` so downstream jobs # that ``. venv/bin/activate`` see an identical layout. if: steps.cache-venv.outputs.cache-hit != 'true' - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pull request saves land in per-PR scopes nothing else can @@ -77,7 +77,7 @@ jobs: if: needs.determine-jobs.outputs.python-linters == 'true' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -100,7 +100,7 @@ jobs: if: needs.determine-jobs.outputs.core-ci == 'true' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -117,6 +117,7 @@ jobs: script/generate-esp32-boards.py --check script/generate-rp2-boards.py --check script/ci_check_duplicate_test_ids.py + script/ci_check_test_fixture_list_form.py import-time: name: Check import esphome.__main__ time @@ -127,7 +128,7 @@ jobs: if: needs.determine-jobs.outputs.import-time == 'true' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -155,17 +156,17 @@ jobs: if: needs.determine-jobs.outputs.device-builder == 'true' steps: - name: Check out esphome (this PR) - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: path: esphome - name: Check out esphome/device-builder - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: esphome/device-builder ref: main path: device-builder - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" - name: Set up uv @@ -174,7 +175,7 @@ jobs: # install step (order-of-magnitude faster on cold boots, # with its own wheel cache). actions/setup-python still # provides the interpreter. - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pull request saves land in per-PR scopes nothing else can @@ -231,7 +232,7 @@ jobs: if: needs.determine-jobs.outputs.core-ci == 'true' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python id: restore-python uses: ./.github/actions/restore-python @@ -261,6 +262,34 @@ jobs: path: venv key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }} + codecov-empty-upload: + name: Report no coverage to Codecov + runs-on: ubuntu-24.04 + needs: + - determine-jobs + # ``pytest`` is the only job that uploads coverage, and it is skipped when + # every changed file is CI-irrelevant (see ``should_run_core_ci`` in + # ``script/determine-jobs.py``). With no upload Codecov never reports a + # result, so the required ``codecov/patch`` status stays pending forever and + # the pull request can never be merged. Tell Codecov up front that this + # commit has nothing to cover so it publishes a passing status instead. + # + # ``force`` skips Codecov's own check that every changed file is ignorable; + # ``determine-jobs`` has already decided none of these files can affect + # coverage, and Codecov would otherwise fail the status for paths it does + # not recognise as non-testable (``docker/**``, ``.yamllint``). + if: needs.determine-jobs.outputs.core-ci == 'false' + steps: + - name: Check out code from GitHub + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Report empty upload to Codecov + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + run_command: empty-upload + force: true + fail_ci_if_error: true + determine-jobs: name: Determine which jobs to run runs-on: ubuntu-24.04 @@ -291,7 +320,7 @@ jobs: benchmarks: ${{ steps.determine.outputs.benchmarks }} steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Fetch enough history to find the merge base fetch-depth: 2 @@ -363,10 +392,28 @@ jobs: bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }} steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install ccache + # Speeds up the host compiles: tests in a bucket compile overlapping + # component sets, so later tests reuse earlier tests' objects. + run: | + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends ccache + - name: Restore ccache (restore-only) + # esphome stores the PlatformIO ccache under the machine-global cache + # dir (see _ccache_env() in esphome/platformio/toolchain.py). The + # bucket-name prefix prefers a same-bucket seed; the bare prefix falls + # back to any seed when the bucket layout differs from dev. + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/esphome/platformio-ccache + key: integration-ccache-${{ matrix.bucket.name }}-${{ github.sha }} + restore-keys: | + integration-ccache-${{ matrix.bucket.name }}- + integration-ccache- - name: Set up Python 3.13 id: python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" - name: Restore Python virtual environment @@ -378,7 +425,7 @@ jobs: - name: Set up uv # Only needed on cache miss to populate the venv. if: steps.cache-venv.outputs.cache-hit != 'true' - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pull request saves land in per-PR scopes nothing else can @@ -408,6 +455,18 @@ jobs: mapfile -t test_files < <(echo "$BUCKET_TESTS" | jq -r '.[]') echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests" pytest -vv --no-cov --tb=native --durations=30 -n auto "${test_files[@]}" + - name: Print ccache statistics + # esphome stores the PlatformIO ccache under the machine-global cache + # dir (see _ccache_env() in esphome/platformio/toolchain.py). + run: CCACHE_DIR="$HOME/.cache/esphome/platformio-ccache" ccache -s + - name: Save ccache + # Pull request saves land in per-PR scopes nothing else can reuse; + # dev pushes seed the shared copy instead. + if: github.event_name != 'pull_request' + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/esphome/platformio-ccache + key: integration-ccache-${{ matrix.bucket.name }}-${{ github.sha }} cpp-unit-tests: name: Run C++ unit tests @@ -418,7 +477,7 @@ jobs: if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]') steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python @@ -447,7 +506,7 @@ jobs: (github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true') steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python @@ -465,7 +524,7 @@ jobs: echo "binary=$BINARY" >> $GITHUB_OUTPUT - name: Run CodSpeed benchmarks - uses: CodSpeedHQ/action@f99becdce5e5d51fd556489ebef684f4ecfd6286 # v4.18.5 + uses: CodSpeedHQ/action@88472375d0a4572cf70a9f1fe3a4e0ab8da1b924 # v5.0.1 with: run: | . venv/bin/activate @@ -504,10 +563,19 @@ jobs: options: --environment nrf52-tidy --grep USE_ZEPHYR --grep USE_NRF52 cache_sdk_nrf: true ignore_errors: false + - id: clang-tidy + name: Run script/clang-tidy for RP2 + options: --environment rp2-tidy --grep USE_RP2 + pio_cache_key: tidyrp2 + - id: clang-tidy + name: Run script/clang-tidy for LibreTiny + environments: bk72xx-tidy ln882h-tidy rtl87xxb-tidy rtl87xxc-tidy + options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X + pio_cache_key: tidylibretiny steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Need history for HEAD~1 to work for checking changed files fetch-depth: 2 @@ -567,10 +635,21 @@ jobs: . venv/bin/activate if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then echo "Running FULL clang-tidy scan (reason: ${{ steps.check_full_scan.outputs.reason }})" - script/clang-tidy --all-headers --fix ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }} + changed="" else echo "Running clang-tidy on changed files only" - script/clang-tidy --all-headers --fix --changed ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }} + changed="--changed" + fi + if [ -n "${{ matrix.environments }}" ]; then + rc=0 + for env in ${{ matrix.environments }}; do + echo "::group::clang-tidy $env" + script/clang-tidy --all-headers --fix $changed --environment "$env" ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }} || rc=1 + echo "::endgroup::" + done + exit $rc + else + script/clang-tidy --all-headers --fix $changed ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }} fi env: # Also cache libdeps, store them in a ~/.platformio subfolder @@ -594,7 +673,7 @@ jobs: ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Need history for HEAD~1 to work for checking changed files fetch-depth: 2 @@ -674,7 +753,7 @@ jobs: steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Need history for HEAD~1 to work for checking changed files fetch-depth: 2 @@ -744,7 +823,8 @@ jobs: include: - id: clang-tidy name: Run script/clang-tidy for ESP32 S3 - options: --environment esp32s3-idf-tidy --grep USE_ESP32_VARIANT_ESP32S3 + # yamllint disable-line rule:line-length + options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC - id: clang-tidy name: Run script/clang-tidy for ESP32 P4 # P4 has no native Wi-Fi/BLE; those run over the hosted co-processor, @@ -754,11 +834,11 @@ jobs: - id: clang-tidy name: Run script/clang-tidy for ESP32 C6 # yamllint disable-line rule:line-length - options: --environment esp32c6-idf-tidy --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE + options: --environment esp32c6-idf-tidy --grep SOC_LP_I2C_SUPPORTED --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Need history for HEAD~1 to work for checking changed files fetch-depth: 2 @@ -845,7 +925,7 @@ jobs: sudo apt-get install -y --no-install-recommends libsdl2-dev ccache - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -990,7 +1070,7 @@ jobs: TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp32-platformio-components }} steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python @@ -1026,7 +1106,7 @@ jobs: if: github.event_name == 'push' && github.ref == 'refs/heads/dev' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -1055,7 +1135,7 @@ jobs: if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true' steps: - name: Check out code from GitHub - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -1094,7 +1174,7 @@ jobs: skip: ${{ steps.check-script.outputs.skip || steps.check-tests.outputs.skip }} steps: - name: Check out target branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.base_ref }} @@ -1276,7 +1356,7 @@ jobs: flash_usage: ${{ steps.extract.outputs.flash_usage }} steps: - name: Check out PR branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -1345,7 +1425,7 @@ jobs: GH_TOKEN: ${{ github.token }} steps: - name: Check out code - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Restore Python uses: ./.github/actions/restore-python with: @@ -1384,6 +1464,7 @@ jobs: - ci-custom - pylint - pytest + - codecov-empty-upload - integration-tests - clang-tidy-single - clang-tidy-nosplit diff --git a/.github/workflows/codeowner-approved-label-update.yml b/.github/workflows/codeowner-approved-label-update.yml index 9b1333734e..bb1d1e2d7a 100644 --- a/.github/workflows/codeowner-approved-label-update.yml +++ b/.github/workflows/codeowner-approved-label-update.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout base branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.pull_request.base.sha }} sparse-checkout: | diff --git a/.github/workflows/codeowner-review-request.yml b/.github/workflows/codeowner-review-request.yml index da9c5f63d6..38a4b8ff0e 100644 --- a/.github/workflows/codeowner-review-request.yml +++ b/.github/workflows/codeowner-review-request.yml @@ -29,7 +29,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout base branch - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event.pull_request.base.sha }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e718b481e0..3ffb4a633f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -52,11 +52,11 @@ jobs: # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -84,6 +84,6 @@ jobs: exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 + uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 5e70117652..ec736a2002 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -14,4 +14,4 @@ jobs: permissions: issues: write # issues.lock on closed issues pull-requests: write # issues.lock on closed pull requests - uses: esphome/workflows/.github/workflows/lock.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1 + uses: esphome/workflows/.github/workflows/lock.yml@9f6577fd37b5cf773ab1b9be929714a0dcd15661 # 2026.7.0 diff --git a/.github/workflows/pr-title-check.yml b/.github/workflows/pr-title-check.yml index 2bb6505b74..3a89c26cd3 100644 --- a/.github/workflows/pr-title-check.yml +++ b/.github/workflows/pr-title-check.yml @@ -16,7 +16,7 @@ jobs: name: Validate PR title runs-on: ubuntu-latest steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b63067ab4b..839b805237 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: branch_build: ${{ steps.tag.outputs.branch_build }} deploy_env: ${{ steps.tag.outputs.deploy_env }} steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Get tag id: tag # yamllint disable rule:line-length @@ -60,9 +60,9 @@ jobs: contents: read # actions/checkout to build the sdist/wheel id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish) steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.x" - name: Build @@ -70,7 +70,7 @@ jobs: pip3 install build python3 -m build - name: Publish - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: skip-existing: true @@ -92,9 +92,9 @@ jobs: os: "ubuntu-24.04-arm" steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12" @@ -102,12 +102,12 @@ jobs: uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 - name: Log in to docker hub - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the GitHub container registry - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -168,7 +168,7 @@ jobs: - ghcr - dockerhub steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Download digests uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -182,13 +182,13 @@ jobs: - name: Log in to docker hub if: matrix.registry == 'dockerhub' - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Log in to the GitHub container registry if: matrix.registry == 'ghcr' - uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ef79b2705a..8ec88238a9 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,61 +6,46 @@ on: - cron: "30 0 * * *" workflow_dispatch: -permissions: - issues: write # actions/stale labels, comments on, and closes stale issues - pull-requests: write # actions/stale labels, comments on, and closes stale pull requests - -concurrency: - group: lock +# The reusable workflow authenticates as the ESPHome GitHub App, so GITHUB_TOKEN +# needs no permissions at all. +permissions: {} jobs: stale: if: github.repository_owner == 'esphome' - runs-on: ubuntu-latest - steps: - - name: Stale - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 - with: - debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch - remove-stale-when-updated: true - operations-per-run: 400 + # No GITHUB_TOKEN permissions: the reusable workflow mints an ESPHome + # GitHub App token so the labels, comments and closures come from + # esphome[bot] instead of github-actions[bot]. + uses: esphome/workflows/.github/workflows/stale.yml@203cea60ebfd18e2b966e57750750e0417a9feec # main + secrets: + ESPHOME_GITHUB_APP_PRIVATE_KEY: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }} + with: + # Live only on dev: a workflow_dispatch from any other branch is a dry run + dry-run: ${{ github.ref != 'refs/heads/dev' }} + days-before-stale: 90 + days-before-close: 7 + stale-label: stale + exempt-label: not-stale + ignored-users: esphbot,codecov-commenter + stale-pr-message: > + There hasn't been any activity on this pull request recently. This + pull request has been automatically marked as stale because of that + and will be closed if no further activity occurs within 7 days. - # The 90 day stale policy for PRs - # - PRs - # - No PRs marked as "not-stale" - # - No Issues (see below) - days-before-pr-stale: 90 - days-before-pr-close: 7 - stale-pr-label: "stale" - exempt-pr-labels: "not-stale" - stale-pr-message: > - There hasn't been any activity on this pull request recently. This - pull request has been automatically marked as stale because of that - and will be closed if no further activity occurs within 7 days. + If you are the author of this PR, please leave a comment if you want + to keep it open. Also, please rebase your PR onto the latest dev + branch to ensure that it's up to date with the latest changes. - If you are the author of this PR, please leave a comment if you want - to keep it open. Also, please rebase your PR onto the latest dev - branch to ensure that it's up to date with the latest changes. + Thank you for your contribution! + stale-issue-message: > + There hasn't been any activity on this issue recently. Due to the + high number of incoming GitHub notifications, we have to clean some + of the old issues, as many of them have already been resolved with + the latest updates. - Thank you for your contribution! + Please make sure to update to the latest ESPHome version and + check if that solves the issue. Let us know if that works for you by + adding a comment 👍 - # The 90 day stale policy for Issues - # - Issues - # - No Issues marked as "not-stale" - # - No PRs (see above) - days-before-issue-stale: 90 - days-before-issue-close: 7 - stale-issue-label: "stale" - exempt-issue-labels: "not-stale" - stale-issue-message: > - There hasn't been any activity on this issue recently. Due to the - high number of incoming GitHub notifications, we have to clean some - of the old issues, as many of them have already been resolved with - the latest updates. - - Please make sure to update to the latest ESPHome version and - check if that solves the issue. Let us know if that works for you by - adding a comment 👍 - - This issue has now been marked as stale and will be closed if no - further activity occurs. Thank you for your contributions. + This issue has now been marked as stale and will be closed if no + further activity occurs. Thank you for your contributions. diff --git a/.github/workflows/status-check-labels.yml b/.github/workflows/status-check-labels.yml index d27cc0cbec..72987c25b1 100644 --- a/.github/workflows/status-check-labels.yml +++ b/.github/workflows/status-check-labels.yml @@ -5,7 +5,7 @@ on: types: [opened, reopened, labeled, unlabeled, synchronize] permissions: - pull-requests: read # issues.listLabelsOnIssue to detect blocking labels (needs-docs, merge-after-release, chained-pr) + pull-requests: read # issues.listLabelsOnIssue to detect blocking labels (needs-docs, needs-developer-docs, merge-after-release, chained-pr) concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} @@ -20,7 +20,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | - const blockingLabels = ['needs-docs', 'merge-after-release', 'chained-pr']; + const blockingLabels = ['needs-docs', 'needs-developer-docs', 'merge-after-release', 'chained-pr']; const { data: labels } = await github.rest.issues.listLabelsOnIssue({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/sync-device-classes.yml b/.github/workflows/sync-device-classes.yml index 2f350d09b3..5d250b97eb 100644 --- a/.github/workflows/sync-device-classes.yml +++ b/.github/workflows/sync-device-classes.yml @@ -28,16 +28,16 @@ jobs: permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR - name: Checkout - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Checkout Home Assistant - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: home-assistant/core path: lib/home-assistant - name: Setup Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.14" @@ -47,7 +47,7 @@ jobs: # setup-python interpreter so subsequent ``pre-commit`` / # ``script/run-in-env.py`` steps find the deps without a # ``uv run`` prefix. - uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true # Pin uv version so the action does not have to fetch the @@ -96,8 +96,8 @@ jobs: uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: commit-message: "Synchronise Device Classes from Home Assistant" - committer: esphomebot - author: esphomebot + committer: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com> + author: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com> branch: sync/device-classes delete-branch: true title: "Synchronise Device Classes from Home Assistant" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index da424f516f..99a4f40201 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,7 +11,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.15 + rev: v0.16.0 hooks: # Run the linter. - id: ruff diff --git a/AGENTS.md b/AGENTS.md index 75a9cdb2bf..b067482d18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -191,11 +191,14 @@ This document provides essential context for AI models interacting with this pro my_component_ns = cg.esphome_ns.namespace("my_component") MyComponent = my_component_ns.class_("MyComponent", cg.Component) - CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MyComponent), - cv.Required(CONF_KEY): cv.string, - cv.Optional(CONF_PARAM, default=42): cv.int_, - }).extend(cv.COMPONENT_SCHEMA) + CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MyComponent), + cv.Required(CONF_KEY): cv.string, + cv.Optional(CONF_PARAM, default=42): cv.int_, + } + ).extend(cv.COMPONENT_SCHEMA) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -229,7 +232,12 @@ This document provides essential context for AI models interacting with this pro - **Sensor:** ```python from esphome.components import sensor - CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend(cv.polling_component_schema("60s")) + + CONFIG_SCHEMA = sensor.sensor_schema(MySensor).extend( + cv.polling_component_schema("60s") + ) + + async def to_code(config): var = await sensor.new_sensor(config) await cg.register_component(var, config) @@ -238,7 +246,10 @@ This document provides essential context for AI models interacting with this pro - **Binary Sensor:** ```python from esphome.components import binary_sensor - CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({ ... }) + + CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend({...}) + + async def to_code(config): var = await binary_sensor.new_binary_sensor(config) ``` @@ -246,7 +257,10 @@ This document provides essential context for AI models interacting with this pro - **Switch:** ```python from esphome.components import switch - CONFIG_SCHEMA = switch.switch_schema().extend({ ... }) + + CONFIG_SCHEMA = switch.switch_schema().extend({...}) + + async def to_code(config): var = await switch.new_switch(config) ``` @@ -263,10 +277,13 @@ This document provides essential context for AI models interacting with this pro ```python from esphome import automation - CONFIG_SCHEMA = cv.Schema({ - cv.GenerateID(): cv.declare_id(MyComponent), - cv.Optional(CONF_ON_STATE): automation.validate_automation({}), - }).extend(cv.COMPONENT_SCHEMA) + CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(MyComponent), + cv.Optional(CONF_ON_STATE): automation.validate_automation({}), + } + ).extend(cv.COMPONENT_SCHEMA) + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) @@ -316,11 +333,14 @@ This document provides essential context for AI models interacting with this pro ```python TurnOnTrigger = my_ns.class_("TurnOnTrigger", automation.Trigger.template()) - CONFIG_SCHEMA = cv.Schema({ - cv.Optional(CONF_ON_TURN_ON): automation.validate_automation( - {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TurnOnTrigger)} - ), - }) + CONFIG_SCHEMA = cv.Schema( + { + cv.Optional(CONF_ON_TURN_ON): automation.validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(TurnOnTrigger)} + ), + } + ) + async def to_code(config): for conf in config.get(CONF_ON_TURN_ON, []): @@ -368,7 +388,10 @@ This document provides essential context for AI models interacting with this pro ``` Register with `@automation.register_condition("my_component.is_active", MyCondition, schema)`. +* **Type Hints:** Type-hint all function signatures, including test functions and config validators (e.g. `def validate_x(config: ConfigType) -> ConfigType:`, `def test_x() -> None:`). Import `ConfigType` from `esphome.types`. + * **Configuration Validation:** + * **Reuse existing validators:** Before writing a custom validator, check for an existing one in `config_validation.py` and compose it in `cv.All(...)` rather than duplicating logic across components. For example, rename a config key with `cv.rename_key(CONF_OLD, CONF_NEW, removed_in="2026.6.0")`, and reject mutually-exclusive keys with `cv.has_at_most_one_key(...)` / `cv.has_exactly_one_key(...)`. See how `api` composes `cv.has_exactly_one_key` + `cv.rename_key`. * **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`. * **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`. * **Platform-Specific:** `cv.only_on(["esp32", "esp8266"])`, `esp32.only_on_variant(...)`, `cv.only_on_esp32`, `cv.only_on_esp8266`, `cv.only_on_rp2040`. @@ -381,6 +404,7 @@ This document provides essential context for AI models interacting with this pro .extend(i2c.i2c_device_schema(0x48)) .extend(spi.spi_device_schema(cs_pin_required=True)) ``` + * **Constants:** `esphome/const.py` is frozen — do not add new `CONF_` constants there. Define a component-local constant in the component's own `.py` (as with `CONF_PARAM` above); for a constant shared by multiple components, add it to `esphome/components/const/__init__.py`. CI (`lint_constants_usage`) fails if the same constant is defined in three or more component files. Constants used in core files (i.e. those not under `esphome/components`) may be added to `esphome/const.py` but will require adjustment to the CI validation check. ## 5. Key Files & Entrypoints @@ -471,7 +495,7 @@ This document provides essential context for AI models interacting with this pro 3. **Test:** Create component tests for all supported platforms and run the full test suite locally. 4. **Lint:** Run `pre-commit` to ensure code is compliant. 5. **Commit:** Commit your changes. There is no strict format for commit messages. - 6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title should have a prefix of the component being worked on (e.g., `[display] Fix bug`, `[abc123] Add new component`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template. + 6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title must start with a `[tag]` prefix. For component work, use the component name (e.g., `[display] Fix bug`, `[abc123] Add new component`); for changes to shared/core code that isn't tied to a single component, use `[core]` (e.g., `[core] Add validator`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template. * **Documentation Contributions:** * Documentation is hosted in the separate `esphome/esphome.io` repository. @@ -617,6 +641,7 @@ This document provides essential context for AI models interacting with this pro _component_state = [] _use_feature = None + def enable_feature(): global _use_feature _use_feature = True @@ -636,20 +661,24 @@ This document provides essential context for AI models interacting with this pro DOMAIN = "my_component" + @dataclass class MyComponentData: feature_enabled: bool = False item_count: int = 0 items: list[str] = field(default_factory=list) + def _get_data() -> MyComponentData: if DOMAIN not in CORE.data: CORE.data[DOMAIN] = MyComponentData() return CORE.data[DOMAIN] + def request_feature() -> None: _get_data().feature_enabled = True + def add_item(item: str) -> None: _get_data().items.append(item) ``` @@ -704,10 +733,22 @@ This document provides essential context for AI models interacting with this pro ``` * **Deprecation Pattern (Python):** + For a renamed config key, use the shared `cv.rename_key` validator with `removed_in` (and `component` for context) — it warns and auto-migrates: + ```python + CONFIG_SCHEMA = cv.All( + cv.rename_key( + CONF_OLD_KEY, CONF_NEW_KEY, removed_in="2026.6.0", component="my_component" + ), + cv.Schema({ ... }), + ) + ``` + For other deprecations, warn manually during validation: ```python # Remove before 2026.6.0 if CONF_OLD_KEY in config: - _LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0") + _LOGGER.warning( + f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0" + ) config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate ``` ## 9. English Language diff --git a/CODEOWNERS b/CODEOWNERS index 0f43cd9749..1a963d8ea6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -69,11 +69,14 @@ esphome/components/bh1750/* @OttoWinter esphome/components/bh1900nux/* @B48D81EFCC esphome/components/binary_sensor/* @esphome/core esphome/components/bk72xx/* @kuba2k2 +esphome/components/bk72xx_ble/* @Bl00d-B0b +esphome/components/bk72xx_ble_tracker/* @Bl00d-B0b esphome/components/bl0906/* @athom-tech @jesserockz @tarontop esphome/components/bl0939/* @ziceva esphome/components/bl0940/* @dan-s-github @tobias- esphome/components/bl0942/* @dbuezas @dwmw2 esphome/components/ble_client/* @buxtronix @clydebarrow +esphome/components/ble_device_base/* @Bl00d-B0b esphome/components/ble_nus/* @tomaszduda23 esphome/components/bluetooth_proxy/* @bdraco @jesserockz esphome/components/bm8563/* @abmantis @@ -145,6 +148,7 @@ esphome/components/dlms_meter/* @latonita @PolarGoose @SimonFischer04 @Tomer27cz esphome/components/dps310/* @kbx81 esphome/components/ds1307/* @badbadc0ffee esphome/components/ds2484/* @mrk-its +esphome/components/ds248x/* @tomwellnitz esphome/components/dsmr/* @glmnet @PolarGoose esphome/components/duty_time/* @dudanov esphome/components/ee895/* @Stock-M diff --git a/MANIFEST.in b/MANIFEST.in index e426627e8d..1626261fb6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,3 +6,4 @@ recursive-include esphome *.cpp *.h *.tcc *.c recursive-include esphome *.py.script recursive-include esphome *.jinja recursive-include esphome LICENSE.txt +recursive-include esphome requirements.txt diff --git a/docker/Dockerfile b/docker/Dockerfile index 01ff53a463..4aa589fc5e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,7 +22,7 @@ RUN \ -r /requirements.txt # Install the ESPHome Device Builder dashboard. -RUN uv pip install --no-cache-dir esphome-device-builder==1.5.0 +RUN uv pip install --no-cache-dir esphome-device-builder==1.8.1 RUN \ platformio settings set enable_telemetry No \ diff --git a/esphome/__main__.py b/esphome/__main__.py index 4abd18d239..e56b504398 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -16,14 +16,10 @@ import sys import time from typing import Protocol -import argcomplete - # Note: Do not import modules from esphome.components here, as this would # cause them to be loaded before external components are processed, resulting # in the built-in version being used instead of the external component one. from esphome import const -import esphome.codegen as cg -from esphome.config import iter_component_configs, read_config, strip_default_ids from esphome.const import ( ALLOWED_NAME_CHARS, ARGUMENT_HELP_DEVICE, @@ -704,6 +700,8 @@ def run_miniterm(config: ConfigType, port: str, args) -> int: def _wrap_to_code(name, comp, yaml_util): + import esphome.codegen as cg + coro = coroutine(comp.to_code) @functools.wraps(comp.to_code) @@ -739,6 +737,7 @@ def write_cpp(config: ConfigType) -> int: def generate_cpp_contents(config: ConfigType) -> None: from esphome import yaml_util + from esphome.config import iter_component_configs _LOGGER.info("Generating C++ source...") @@ -776,6 +775,13 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int: check_placeholder_credentials(config) + # Keep this here, NOT in codegen: config-hash and --only-generate must keep + # working on machines that cannot run the toolchain. + if CORE.is_esp8266: + from esphome.components.esp8266 import check_rosetta + + check_rosetta() + # NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py # If you change this format, update the regex in that script as well _LOGGER.info("Compiling app... Build path: %s", CORE.build_path) @@ -1457,6 +1463,7 @@ def command_wizard(args: ArgsProtocol) -> int | None: def command_config(args: ArgsProtocol, config: ConfigType) -> int | None: from esphome import yaml_util + from esphome.config import strip_default_ids if getattr(args, "no_defaults", False): user_config = getattr(config, "user_config", None) @@ -1510,10 +1517,18 @@ def _redact_with_legacy_fallback(output: str) -> str: m = _LEGACY_REDACTION_RE.search(line) if m is None: continue + key = m.group("key") if not in_substitutions: - unmarked.add(m.group("key")) + # Public keys (e.g. wireguard's peer_public_key) are not secret; + # redacting them and telling maintainers to mark them cv.sensitive + # would be wrong on both counts. Substitution keys are user-named + # with no schema behind them, so anything secret-shaped there + # (public or not) stays conservatively redacted. + if "public" in key.split("_"): + continue + unmarked.add(key) lines[i] = ( - f"{line[: m.start()]}{m.group('key')}: " + f"{line[: m.start()]}{key}: " f"\\033[8m{m.group('val')}\\033[28m{line[m.end() :]}" ) output = "\n".join(lines) @@ -2483,7 +2498,12 @@ def parse_args(argv): # a deprecation warning). arguments = argv[1:] - argcomplete.autocomplete(parser) + # argcomplete only does anything when the shell-completion machinery + # invokes us with _ARGCOMPLETE set; skip the import otherwise. + if "_ARGCOMPLETE" in os.environ: + import argcomplete + + argcomplete.autocomplete(parser) if len(arguments) > 0 and arguments[0] in SIMPLE_CONFIG_ACTIONS: args, unknown_args = parser.parse_known_args(arguments) @@ -2582,6 +2602,8 @@ def run_esphome(argv): ) if config is None: + from esphome.config import read_config + config = read_config( command_line_substitutions, skip_external_update=skip_external, diff --git a/esphome/analyze_memory/cli.py b/esphome/analyze_memory/cli.py index 4fbceb7e5e..ab20e4d076 100644 --- a/esphome/analyze_memory/cli.py +++ b/esphome/analyze_memory/cli.py @@ -20,6 +20,7 @@ from . import ( RAM_SECTIONS, MemoryAnalyzer, ) +from .toolchain import find_elf_path, find_idedata_path, idedata_candidates if TYPE_CHECKING: from . import ComponentMemory @@ -759,45 +760,25 @@ def main(): print(f"Error: {build_path} is not a directory", file=sys.stderr) sys.exit(1) - # Find firmware.elf - elf_file = None - for elf_candidate in [ - build_path / "firmware.elf", - build_path / ".pioenvs" / build_path.name / "firmware.elf", - ]: - if elf_candidate.exists(): - elf_file = str(elf_candidate) - break - - if not elf_file: - print(f"Error: firmware.elf not found in {build_dir}", file=sys.stderr) + elf_path = find_elf_path(build_path) + if not elf_path: + print(f"Error: no firmware ELF found in {build_dir}", file=sys.stderr) sys.exit(1) - - # Find idedata.json - check current directory first, then home - device_name = build_path.name - idedata_candidates = [ - Path.cwd() / ".esphome" / "idedata" / f"{device_name}.json", - Path.home() / ".esphome" / "idedata" / f"{device_name}.json", - ] + elf_file = str(elf_path) idedata = None - for idedata_path in idedata_candidates: - if not idedata_path.exists(): - continue + if idedata_path := find_idedata_path(build_path): try: with idedata_path.open(encoding="utf-8") as f: raw_data = json.load(f) idedata = IDEData(raw_data) print(f"Loaded idedata from: {idedata_path}", file=sys.stderr) - break except (json.JSONDecodeError, OSError) as e: print(f"Warning: Failed to load idedata: {e}", file=sys.stderr) if not idedata: - print( - f"Warning: idedata not found (searched {idedata_candidates[0]} and {idedata_candidates[1]})", - file=sys.stderr, - ) + searched = "\n ".join(str(p) for p in idedata_candidates(build_path)) + print(f"Warning: idedata not found, searched:\n {searched}", file=sys.stderr) analyzer = MemoryAnalyzerCLI(elf_file, idedata=idedata) analyzer.analyze() diff --git a/esphome/analyze_memory/toolchain.py b/esphome/analyze_memory/toolchain.py index a724d52f25..19041ac807 100644 --- a/esphome/analyze_memory/toolchain.py +++ b/esphome/analyze_memory/toolchain.py @@ -23,6 +23,78 @@ TOOLCHAIN_PREFIXES = [ ] +def find_elf_path(build_path: Path) -> Path | None: + """Locate the firmware ELF inside an ESPHome build directory. + + The layout depends on the toolchain that produced the build, so try each + known one in turn. + + Args: + build_path: Path to an ESPHome build directory + + Returns: + Path to the ELF file, or None if no known layout matches + """ + name = build_path.name + for candidate in ( + # Native ESP-IDF: idf.py writes build/.elf, which ESPHome copies + # to build/firmware.elf (see espidf.toolchain.create_elf_copy) + build_path / "build" / "firmware.elf", + # PlatformIO + build_path / "firmware.elf", + build_path / ".pioenvs" / name / "firmware.elf", + # LibreTiny uses raw_firmware.elf + build_path / "raw_firmware.elf", + build_path / ".pioenvs" / name / "raw_firmware.elf", + # Zephyr (nRF52); the SDK nests the artifacts one level deeper from 2.9.2 + build_path / ".pioenvs" / name / "zephyr" / "zephyr" / "zephyr.elf", + build_path / ".pioenvs" / name / "zephyr" / "zephyr.elf", + ): + if candidate.is_file(): + return candidate + return None + + +def idedata_candidates(build_path: Path) -> list[Path]: + """Return the idedata locations searched for a build directory, in order. + + Exposed so a caller reporting "not found" can name the paths it tried + without keeping its own copy of the list. + + Args: + build_path: Path to an ESPHome build directory + + Returns: + The candidate idedata JSON paths, most specific first + """ + name = build_path.name + return [ + # In .pioenvs for test builds + build_path / ".pioenvs" / name / "idedata.json", + # Both toolchains cache it in the data dir, which holds this build dir: + # /idedata/.json next to /build/ + build_path.parent.parent / "idedata" / f"{name}.json", + # Regular builds, invoked from the config dir or from anywhere + Path.cwd() / ".esphome" / "idedata" / f"{name}.json", + Path.home() / ".esphome" / "idedata" / f"{name}.json", + ] + + +def find_idedata_path(build_path: Path) -> Path | None: + """Locate the idedata JSON belonging to an ESPHome build directory. + + Args: + build_path: Path to an ESPHome build directory + + Returns: + Path to the idedata JSON, or None if it was not found + """ + for candidate in idedata_candidates(build_path): + if candidate.is_file(): + return candidate + return None + + def _find_in_platformio_packages(tool_name: str) -> str | None: """Search for a tool in PlatformIO package directories. diff --git a/esphome/build_gen/espidf.py b/esphome/build_gen/espidf.py index cc2fc5c4cd..cf476555e7 100644 --- a/esphome/build_gen/espidf.py +++ b/esphome/build_gen/espidf.py @@ -210,15 +210,27 @@ def get_component_cmakelists() -> str: if(CMAKE_SCRIPT_MODE_FILE) file(GLOB_RECURSE app_sources "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cc" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cxx" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.c++" "${{CMAKE_CURRENT_SOURCE_DIR}}/*.c" "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cc" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cxx" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c++" "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c" ) else() file(GLOB_RECURSE app_sources CONFIGURE_DEPENDS "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cc" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.cxx" + "${{CMAKE_CURRENT_SOURCE_DIR}}/*.c++" "${{CMAKE_CURRENT_SOURCE_DIR}}/*.c" "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cc" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cxx" + "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c++" "${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c" ) endif() diff --git a/esphome/bundle.py b/esphome/bundle.py index d38f68ebfd..dcaea03646 100644 --- a/esphome/bundle.py +++ b/esphome/bundle.py @@ -7,12 +7,12 @@ and compiled directly: ``esphome compile my_device.esphomebundle.tar.gz`` from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import StrEnum import io import json import logging -from pathlib import Path +from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath import re import shutil import tarfile @@ -32,6 +32,8 @@ from esphome.core import CORE, EsphomeError _LOGGER = logging.getLogger(__name__) +DOMAIN = "bundle" + BUNDLE_EXTENSION = ".esphomebundle.tar.gz" MANIFEST_FILENAME = "manifest.json" CURRENT_MANIFEST_VERSION = 1 @@ -49,6 +51,7 @@ class ManifestKey(StrEnum): MANIFEST_VERSION = "manifest_version" ESPHOME_VERSION = "esphome_version" CONFIG_FILENAME = "config_filename" + CONFIG_DIR = "config_dir" FILES = "files" HAS_SECRETS = "has_secrets" @@ -120,6 +123,126 @@ def _find_used_secret_keys(yaml_files: list[Path]) -> set[str]: return keys +@dataclass +class BundleData: + """Files components asked to include, keyed under DOMAIN in CORE.data.""" + + extra_files: list[Path] = field(default_factory=list) + # Original config dir parsed from an extracted bundle's manifest.json, + # kept in the path flavor of the machine the bundle was created on. + # The checked flag makes the manifest lookup happen at most once per run; + # CORE.data is cleared between runs. + original_config_dir: PurePath | None = None + original_config_dir_checked: bool = False + + +def _get_data() -> BundleData: + if DOMAIN not in CORE.data: + CORE.data[DOMAIN] = BundleData() + return CORE.data[DOMAIN] + + +def add_bundle_file(path: Path) -> None: + """Register a file that a bundle must include. + + Bundle discovery walks the validated config, so it only finds files the config + names. Components call this during validation for files it cannot see, such as a + file that is referenced from inside another file. + + A relative path is taken as relative to the config directory. Files outside the + config directory are skipped when the bundle is built. + """ + _get_data().extra_files.append(CORE.relative_config_path(path)) + + +# Windows paths start with a drive letter or contain backslashes; POSIX +# paths do neither in practice, so this is how the flavor of a recorded +# path string is recognized on any host. +_WINDOWS_DRIVE_RE = re.compile(r"^[A-Za-z]:") + + +def _path_flavor(value: str) -> type[PurePath]: + """Pick the pure path class matching the flavor ``value`` was written in.""" + if "\\" in value or _WINDOWS_DRIVE_RE.match(value): + return PureWindowsPath + return PurePosixPath + + +def _load_original_config_dir() -> PurePath | None: + """Read the original config dir from an extracted bundle's manifest. + + Returns None when the current config dir is not an extracted bundle or + the manifest does not record the original config dir. + """ + manifest_path = CORE.config_dir / MANIFEST_FILENAME + try: + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + except FileNotFoundError: + # The common case: this config dir is not an extracted bundle. + return None + except (OSError, UnicodeDecodeError, json.JSONDecodeError) as err: + # A manifest.json is present but unreadable or malformed. Say so + # instead of letting it look identical to "not a bundle". + _LOGGER.warning("Bundle: ignoring unreadable %s: %s", manifest_path, err) + return None + if not isinstance(manifest, dict): + return None + # A manifest.json in the config dir does not have to be ours. Only trust + # one that looks like a bundle manifest for exactly this config file. + version = manifest.get(ManifestKey.MANIFEST_VERSION) + if not isinstance(version, int) or version < 1: + return None + if manifest.get(ManifestKey.CONFIG_FILENAME) != CORE.config_path.name: + return None + config_dir = manifest.get(ManifestKey.CONFIG_DIR) + if not isinstance(config_dir, str) or not config_dir: + return None + return _path_flavor(config_dir)(config_dir) + + +def remap_bundle_path(value: str) -> Path | None: + """Remap an absolute path from the machine a bundle was created on. + + A bundled config may reference files by absolute path. The referenced + files ship inside the bundle at their config-relative locations, but the + YAML text is copied verbatim, so after extraction on another machine the + absolute reference points at a path that only existed on the creating + machine. The bundle manifest records that machine's config dir; when + ``value`` names a path that lived under it, return the corresponding + file next to the extracted config. + + ``value`` is the raw path string from the config. It is parsed with the + original machine's path flavor, so a bundle created on Windows remaps on + a POSIX build server and vice versa. + + Returns None when not compiling an extracted bundle, when ``value`` was + not under the original config dir, or when the bundle does not contain + the file. + """ + data = _get_data() + if not data.original_config_dir_checked: + data.original_config_dir_checked = True + data.original_config_dir = _load_original_config_dir() + original_dir = data.original_config_dir + if original_dir is None: + return None + path = type(original_dir)(value) + if not path.is_absolute(): + return None + try: + rel = path.relative_to(original_dir) + except ValueError: + return None + # relative_to is lexical, so ".." segments survive it. Refuse them: the + # remapped file must land strictly inside the extracted config tree. + if ".." in rel.parts: + return None + remapped = CORE.relative_config_path(Path(*rel.parts)) + if not remapped.exists(): + return None + return remapped + + @dataclass class BundleFile: """A file to include in the bundle.""" @@ -146,6 +269,7 @@ class BundleManifest: config_filename: str files: list[str] has_secrets: bool + config_dir: str | None = None class ConfigBundleCreator: @@ -286,13 +410,18 @@ class ConfigBundleCreator: with known file extensions are also resolved and checked. Core ESPHome concepts that use relative paths or directories - are handled explicitly. + are handled explicitly. Files the config does not name at all are + registered by their component with add_bundle_file(). """ config = self._config # Generic walk: find all file paths in the validated config self._walk_config_for_files(config) + # Files registered by components during validation + for extra_file in _get_data().extra_files: + self._add_file(extra_file) + # --- Core ESPHome concepts needing explicit handling --- # esphome.includes / includes_c - can be relative paths and directories @@ -405,6 +534,7 @@ class ConfigBundleCreator: ManifestKey.MANIFEST_VERSION: CURRENT_MANIFEST_VERSION, ManifestKey.ESPHOME_VERSION: const.__version__, ManifestKey.CONFIG_FILENAME: self._config_path.name, + ManifestKey.CONFIG_DIR: str(self._config_dir), ManifestKey.FILES: [f.path for f in files], ManifestKey.HAS_SECRETS: has_secrets, } @@ -489,12 +619,14 @@ def read_bundle_manifest(bundle_path: Path) -> BundleManifest: except tarfile.TarError as err: raise EsphomeError(f"Failed to read bundle: {err}") from err + config_dir = manifest.get(ManifestKey.CONFIG_DIR) return BundleManifest( manifest_version=manifest[ManifestKey.MANIFEST_VERSION], esphome_version=manifest.get(ManifestKey.ESPHOME_VERSION, "unknown"), config_filename=manifest[ManifestKey.CONFIG_FILENAME], files=manifest.get(ManifestKey.FILES, []), has_secrets=manifest.get(ManifestKey.HAS_SECRETS, False), + config_dir=config_dir if isinstance(config_dir, str) else None, ) diff --git a/esphome/codegen.py b/esphome/codegen.py index 56a47d146e..0694eb4d84 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -53,6 +53,7 @@ from esphome.cpp_helpers import ( # noqa: F401 past_safe_mode, register_component, register_parented, + set_setup_priority, ) from esphome.cpp_types import ( # noqa: F401 NAN, diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 880b7cc404..9aac7bd7d1 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -794,6 +794,7 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection msg.supports_action = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION); // Current feature flags and other supported parameters msg.feature_flags = traits.get_feature_flags(); + msg.temperature_unit = static_cast(traits.get_temperature_unit()); msg.supported_modes = &traits.get_supported_modes(); msg.visual_min_temperature = traits.get_visual_min_temperature(); msg.visual_max_temperature = traits.get_visual_max_temperature(); @@ -1328,7 +1329,8 @@ void APIConnection::on_voice_assistant_announce_request(const VoiceAssistantAnno } } -bool APIConnection::send_voice_assistant_get_configuration_response_(const VoiceAssistantConfigurationRequest &msg) { +bool APIConnection::send_voice_assistant_get_configuration_response_( + const VoiceAssistantConfigurationRequest & /*msg*/) { VoiceAssistantConfigurationResponse resp; if (!this->check_voice_assistant_api_connection_()) { // send_message encodes synchronously, so this stack local outlives the encode @@ -1348,22 +1350,6 @@ bool APIConnection::send_voice_assistant_get_configuration_response_(const Voice } } - // Filter external wake words - for (auto &wake_word : msg.external_wake_words) { - if (wake_word.model_type != "micro") { - // microWakeWord only - continue; - } - - resp.available_wake_words.emplace_back(); - auto &resp_wake_word = resp.available_wake_words.back(); - resp_wake_word.id = StringRef(wake_word.id); - resp_wake_word.wake_word = StringRef(wake_word.wake_word); - for (const auto &lang : wake_word.trained_languages) { - resp_wake_word.trained_languages.push_back(lang); - } - } - resp.active_wake_words = &config.active_wake_words; resp.max_active_wake_words = config.max_active_wake_words; return this->send_message(resp); @@ -1468,6 +1454,7 @@ uint16_t APIConnection::try_send_water_heater_info(EntityBase *entity, APIConnec msg.target_temperature_step = traits.get_target_temperature_step(); msg.supported_modes = &traits.get_supported_modes(); msg.supported_features = traits.get_feature_flags(); + msg.temperature_unit = static_cast(traits.get_temperature_unit()); return fill_and_encode_entity_info(wh, msg, conn, remaining_size); } @@ -1746,12 +1733,6 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) { ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu16 ".%" PRIu16, this->helper_->get_client_name(), this->helper_->get_peername_to(peername), this->client_api_version_major_, this->client_api_version_minor_); - // TODO: Remove before 2026.8.0 (one version after get_object_id backward compat removal) - if (!this->client_supports_api_version(1, 14)) { - ESP_LOGW(TAG, "'%s' using outdated API %" PRIu16 ".%" PRIu16 ", update to 1.14+", this->helper_->get_client_name(), - this->client_api_version_major_, this->client_api_version_minor_); - } - HelloResponse resp; resp.api_version_major = 1; resp.api_version_minor = 14; diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 144973fa9d..7df7ea1429 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -166,10 +166,14 @@ class APIConnection final : public APIServerConnectionBase { #endif bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len); #ifdef USE_API_HOMEASSISTANT_SERVICES - void send_homeassistant_action(const HomeassistantActionRequest &call) { + // Returns whether this client has subscribed to Home Assistant actions; the message + // is only handed to the send path when subscribed. A true return does not guarantee + // delivery - it lets the caller warn when no connected client has the subscription. + bool send_homeassistant_action(const HomeassistantActionRequest &call) { if (!this->flags_.service_call_subscription) - return; + return false; this->send_message(call); + return true; } #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES void on_homeassistant_action_response(const HomeassistantActionResponse &msg); diff --git a/esphome/components/api/api_overflow_buffer.cpp b/esphome/components/api/api_overflow_buffer.cpp index e242d4553e..a57a2fb1bb 100644 --- a/esphome/components/api/api_overflow_buffer.cpp +++ b/esphome/components/api/api_overflow_buffer.cpp @@ -12,6 +12,22 @@ APIOverflowBuffer::~APIOverflowBuffer() { } ssize_t APIOverflowBuffer::try_drain(socket::Socket *socket) { + // socket->write() can re-enter this function: a log message emitted from an + // lwip callback during the write goes out over the API and lands back in the + // frame helper's write/drain path. If a nested drain ran here it would send + // and free the entry the outer drain is still holding, causing a double free. + // Report "no progress" instead; the outer drain keeps draining, and the + // nested send is enqueued behind the existing backlog. + if (this->draining_) + return 0; + + // RAII so the flag is cleared on every return path + struct DrainGuard { + explicit DrainGuard(bool &flag) : flag_(flag) { flag_ = true; } + ~DrainGuard() { this->flag_ = false; } + bool &flag_; + } guard(this->draining_); + while (this->count_ > 0) { Entry *front = this->queue_[this->head_]; @@ -29,11 +45,12 @@ ssize_t APIOverflowBuffer::try_drain(socket::Socket *socket) { return sent; } - // Entry fully sent — free it and advance - Entry::destroy(front); + // Entry fully sent — unlink it before freeing so a freed pointer is never + // reachable from the queue this->queue_[this->head_] = nullptr; this->head_ = (this->head_ + 1) % API_MAX_SEND_QUEUE; this->count_--; + Entry::destroy(front); } return 0; // All drained diff --git a/esphome/components/api/api_overflow_buffer.h b/esphome/components/api/api_overflow_buffer.h index 19aae680f0..1227e83126 100644 --- a/esphome/components/api/api_overflow_buffer.h +++ b/esphome/components/api/api_overflow_buffer.h @@ -69,6 +69,10 @@ class APIOverflowBuffer { uint8_t head_{0}; uint8_t tail_{0}; uint8_t count_{0}; + // Guards against re-entrant drains: socket->write() can re-enter the API + // send path (e.g. a log message emitted from an lwip callback), and a nested + // drain would free the entry the outer drain is still holding. + bool draining_{false}; }; } // namespace esphome::api diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 1062dfeb39..6e3448121c 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -426,8 +426,16 @@ void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = bat #ifdef USE_API_HOMEASSISTANT_SERVICES void APIServer::send_homeassistant_action(const HomeassistantActionRequest &call) { + bool has_subscriber = false; for (auto &client : this->active_clients()) { - client->send_homeassistant_action(call); + has_subscriber |= client->send_homeassistant_action(call); + } + if (!has_subscriber) { + // Home Assistant subscribes to actions shortly *after* authenticating, so actions + // fired right at connection time (on_client_connected, on_time_sync, ...) can + // arrive before the subscription and are lost - warn instead of failing silently. + ESP_LOGW(TAG, "Home Assistant %s '%s' dropped; %s", call.is_event ? "event" : "action", call.service.c_str(), + this->is_connected() ? "client has not subscribed to actions (yet)" : "no client connected"); } } #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES diff --git a/esphome/components/api/client.py b/esphome/components/api/client.py index 44edc035f9..7f07146dba 100644 --- a/esphome/components/api/client.py +++ b/esphome/components/api/client.py @@ -18,7 +18,7 @@ with warnings.catch_warnings(): import contextlib from esphome.const import CONF_KEY, CONF_PORT, __version__ -from esphome.core import CORE, EsphomeError +from esphome.core import CORE from esphome.util import safe_print from . import CONF_ENCRYPTION @@ -36,15 +36,17 @@ class _LogLineProcessor: """Feeds incoming log lines to the stack-trace decoder. Two responsibilities beyond just calling the decoder: - 1. Catch EsphomeError. on_log runs inside an asyncio protocol - callback; if an exception escapes, the loop tears the transport - down with "Fatal error: protocol.data_received() call failed." - and ReconnectLogic immediately reconnects, the device replays - the same crash trace, and we loop forever. - 2. Disable decoding after the first failure. _decode_pc shells out - to PlatformIO via _run_idedata, which is expensive; a single - crash dump can contain many PC/BT lines and we don't want to - retry the failing subprocess for each one. + 1. Catch everything the decoder can raise. aioesphomeapi isolates + exceptions raised by log handlers, so an escaping one no longer + kills the session, but it does log a full traceback per line. A + crash dump carries a PC line plus one per backtrace frame, so the + tracebacks bury the dump the user is trying to read. Decoding is a + diagnostic nicety; nothing it raises is worth that noise. + 2. Disable decoding after the first failure. _decode_pc shells out to + the toolchain to resolve addr2line, which is expensive; a single + crash dump can contain many PC/BT lines and we don't want to retry + the failing subprocess for each one. This only works if every + failure is caught, which is why 1 is not narrowed to EsphomeError. """ def __init__(self, config: dict[str, Any], platform_handler: Any | None) -> None: @@ -61,12 +63,13 @@ class _LogLineProcessor: self.backtrace_state = self._platform_handler( self._config, raw_line, self.backtrace_state ) - except EsphomeError as exc: + except Exception as exc: # noqa: BLE001 # pylint: disable=broad-except self._decode_enabled = False self.backtrace_state = False # _run_idedata raises EsphomeError with no message; fall back # to a generic explanation when str(exc) is empty. detail = str(exc) or "build artifacts not found locally" + _LOGGER.debug("Stack-trace decoding failed", exc_info=True) _LOGGER.warning( "Crash trace decoding unavailable: %s. " "Run 'esphome compile' for this device to enable PC decoding.", @@ -152,6 +155,9 @@ async def async_run_logs( name=name, subscribe_states=subscribe_states, allow_plaintext_fallback=True, + # A top-level ``deep_sleep:`` block means the device is only awake + # briefly; cap the reconnect backoff so a wake window is not missed. + deep_sleep="deep_sleep" in config, ) try: await asyncio.Event().wait() diff --git a/esphome/components/aqi/__init__.py b/esphome/components/aqi/__init__.py index 4b979ab406..17d434294a 100644 --- a/esphome/components/aqi/__init__.py +++ b/esphome/components/aqi/__init__.py @@ -7,6 +7,7 @@ AQICalculatorType = aqi_ns.enum("AQICalculatorType") CONF_AQI = "aqi" CONF_CALCULATION_TYPE = "calculation_type" +CONF_EXTENDED_RANGE = "extended_range" AQI_CALCULATION_TYPE = { "CAQI": AQICalculatorType.CAQI_TYPE, diff --git a/esphome/components/aqi/abstract_aqi_calculator.h b/esphome/components/aqi/abstract_aqi_calculator.h index 299962fa17..6b4c9c5e04 100644 --- a/esphome/components/aqi/abstract_aqi_calculator.h +++ b/esphome/components/aqi/abstract_aqi_calculator.h @@ -6,7 +6,7 @@ namespace esphome::aqi { class AbstractAQICalculator { public: - virtual uint16_t get_aqi(float pm2_5_value, float pm10_0_value) = 0; + virtual uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool extended_range) = 0; }; } // namespace esphome::aqi diff --git a/esphome/components/aqi/aqi_calculator.h b/esphome/components/aqi/aqi_calculator.h index bb8e402280..56b6069118 100644 --- a/esphome/components/aqi/aqi_calculator.h +++ b/esphome/components/aqi/aqi_calculator.h @@ -11,10 +11,12 @@ namespace esphome::aqi { class AQICalculator : public AbstractAQICalculator { public: - uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override { - float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID); - float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID); + uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool extended_range) override { + float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID, extended_range); + float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID, extended_range); float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f}); + // extended_range lets the index run past the standard maximum, so clamp to the sensor's range. + aqi = std::min(aqi, static_cast(std::numeric_limits::max())); return static_cast(std::lround(aqi)); } @@ -30,7 +32,7 @@ class AQICalculator : public AbstractAQICalculator { {35.5f, 55.5f}, {55.5f, 125.5f}, {125.5f, 225.5f}, - {225.5f, std::numeric_limits::max()} + {225.5f, 500.4f} // EPA 2024: AQI 301-500 maps to PM2.5 225.5-500.4 ug/m3 // clang-format on }; @@ -41,11 +43,11 @@ class AQICalculator : public AbstractAQICalculator { {155.0f, 255.0f}, {255.0f, 355.0f}, {355.0f, 425.0f}, - {425.0f, std::numeric_limits::max()} + {425.0f, 604.0f} // EPA: AQI 301-500 maps to PM10 425-604 ug/m3 (top of the 401-500 band) // clang-format on }; - static float calculate_index(float value, const float array[NUM_LEVELS][2]) { + static float calculate_index(float value, const float array[NUM_LEVELS][2], bool extended_range) { int grid_index = get_grid_index(value, array); if (grid_index == -1) { return -1.0f; @@ -55,14 +57,22 @@ class AQICalculator : public AbstractAQICalculator { float conc_lo = array[grid_index][0]; float conc_hi = array[grid_index][1]; - return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo; + float index = (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo; + + // Concentrations above the highest breakpoint run the linear fit past aqi_hi. By default we + // clamp to the standard maximum; with extended_range we keep the extrapolated "over-range" + // value so heavy pollution reports numbers beyond what the standard defines. + if (grid_index == NUM_LEVELS - 1 && !extended_range && index > aqi_hi) { + return aqi_hi; + } + return index; } static int get_grid_index(float value, const float array[NUM_LEVELS][2]) { for (int i = 0; i < NUM_LEVELS; i++) { - const bool in_range = - (value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive - : (value < array[i][1])); // others exclusive on hi + // The top band is open-ended: any value at or above its lower breakpoint falls into it, + // and calculate_index() decides whether to clamp or extrapolate. + const bool in_range = (value >= array[i][0]) && (i == NUM_LEVELS - 1 || value < array[i][1]); if (in_range) { return i; } diff --git a/esphome/components/aqi/aqi_sensor.cpp b/esphome/components/aqi/aqi_sensor.cpp index 2d8a780cc7..4bb964d5ee 100644 --- a/esphome/components/aqi/aqi_sensor.cpp +++ b/esphome/components/aqi/aqi_sensor.cpp @@ -24,6 +24,7 @@ void AQISensor::setup() { void AQISensor::dump_config() { ESP_LOGCONFIG(TAG, "AQI Sensor:"); ESP_LOGCONFIG(TAG, " Calculation Type: %s", this->aqi_calc_type_ == AQI_TYPE ? "AQI" : "CAQI"); + ESP_LOGCONFIG(TAG, " Extended Range: %s", this->extended_range_ ? "enabled" : "disabled"); if (this->pm_2_5_sensor_ != nullptr) { ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", this->pm_2_5_sensor_->get_name().c_str()); } @@ -44,7 +45,7 @@ void AQISensor::calculate_aqi_() { return; } - uint16_t aqi = calculator->get_aqi(this->pm_2_5_value_, this->pm_10_0_value_); + uint16_t aqi = calculator->get_aqi(this->pm_2_5_value_, this->pm_10_0_value_, this->extended_range_); this->publish_state(aqi); } diff --git a/esphome/components/aqi/aqi_sensor.h b/esphome/components/aqi/aqi_sensor.h index aa64fa5a4d..464c088188 100644 --- a/esphome/components/aqi/aqi_sensor.h +++ b/esphome/components/aqi/aqi_sensor.h @@ -14,6 +14,7 @@ class AQISensor final : public sensor::Sensor, public Component { void set_pm_2_5_sensor(sensor::Sensor *sensor) { this->pm_2_5_sensor_ = sensor; } void set_pm_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; } void set_aqi_calculation_type(AQICalculatorType type) { this->aqi_calc_type_ = type; } + void set_extended_range(bool extended_range) { this->extended_range_ = extended_range; } protected: void calculate_aqi_(); @@ -21,6 +22,7 @@ class AQISensor final : public sensor::Sensor, public Component { sensor::Sensor *pm_2_5_sensor_{nullptr}; sensor::Sensor *pm_10_0_sensor_{nullptr}; AQICalculatorType aqi_calc_type_{AQI_TYPE}; + bool extended_range_{false}; AQICalculatorFactory aqi_calculator_factory_; float pm_2_5_value_{NAN}; diff --git a/esphome/components/aqi/caqi_calculator.h b/esphome/components/aqi/caqi_calculator.h index 3f6da45aa9..56a98682d9 100644 --- a/esphome/components/aqi/caqi_calculator.h +++ b/esphome/components/aqi/caqi_calculator.h @@ -9,25 +9,28 @@ namespace esphome::aqi { class CAQICalculator : public AbstractAQICalculator { public: - uint16_t get_aqi(float pm2_5_value, float pm10_0_value) override { + // The CAQI (CITEAIR) scale defines no maximum: its top "Very high" class is simply ">100". We + // therefore always extrapolate the top band past 100 without limit, so the extended_range flag + // (which lifts the AQI calculator's fixed 500 cap) has no meaning here and is ignored. + uint16_t get_aqi(float pm2_5_value, float pm10_0_value, bool /*extended_range*/) override { float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID); float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID); float aqi = std::max({pm2_5_index, pm10_0_index, 0.0f}); + aqi = std::min(aqi, static_cast(std::numeric_limits::max())); return static_cast(std::lround(aqi)); } protected: - static constexpr int NUM_LEVELS = 5; + static constexpr int NUM_LEVELS = 4; - static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}, {101, 400}}; + static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}}; static constexpr float PM2_5_GRID[NUM_LEVELS][2] = { // clang-format off {0.0f, 15.1f}, {15.1f, 30.1f}, {30.1f, 55.1f}, - {55.1f, 110.1f}, - {110.1f, std::numeric_limits::max()} + {55.1f, 110.1f} // clang-format on }; @@ -36,8 +39,7 @@ class CAQICalculator : public AbstractAQICalculator { {0.0f, 25.1f}, {25.1f, 50.1f}, {50.1f, 90.1f}, - {90.1f, 180.1f}, - {180.1f, std::numeric_limits::max()} + {90.1f, 180.1f} // clang-format on }; @@ -52,14 +54,15 @@ class CAQICalculator : public AbstractAQICalculator { float conc_lo = array[grid_index][0]; float conc_hi = array[grid_index][1]; + // The top band is open-ended (see get_grid_index), so for concentrations above the last + // breakpoint this linear fit extrapolates past 100 unbounded, matching CAQI's open ">100" class. return (value - conc_lo) * (aqi_hi - aqi_lo) / (conc_hi - conc_lo) + aqi_lo; } static int get_grid_index(float value, const float array[NUM_LEVELS][2]) { for (int i = 0; i < NUM_LEVELS; i++) { - const bool in_range = - (value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive - : (value < array[i][1])); // others exclusive on hi + // The top band is open-ended: any value at or above its lower breakpoint falls into it. + const bool in_range = (value >= array[i][0]) && (i == NUM_LEVELS - 1 || value < array[i][1]); if (in_range) { return i; } diff --git a/esphome/components/aqi/sensor.py b/esphome/components/aqi/sensor.py index 5842aea88c..9c361560df 100644 --- a/esphome/components/aqi/sensor.py +++ b/esphome/components/aqi/sensor.py @@ -8,14 +8,25 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, ) -from . import AQI_CALCULATION_TYPE, CONF_CALCULATION_TYPE, aqi_ns +from . import AQI_CALCULATION_TYPE, CONF_CALCULATION_TYPE, CONF_EXTENDED_RANGE, aqi_ns CODEOWNERS = ["@jasstrong"] DEPENDENCIES = ["sensor"] AQISensor = aqi_ns.class_("AQISensor", sensor.Sensor, cg.Component) -CONFIG_SCHEMA = ( + +def _validate_extended_range(config): + if CONF_EXTENDED_RANGE in config and config[CONF_CALCULATION_TYPE] == "CAQI": + raise cv.Invalid( + f"'{CONF_EXTENDED_RANGE}' is not supported with 'calculation_type: CAQI'. " + "CAQI has no maximum value by specification, so it is always reported unbounded.", + [CONF_EXTENDED_RANGE], + ) + return config + + +CONFIG_SCHEMA = cv.All( sensor.sensor_schema( AQISensor, accuracy_decimals=0, @@ -29,9 +40,11 @@ CONFIG_SCHEMA = ( cv.Required(CONF_CALCULATION_TYPE): cv.enum( AQI_CALCULATION_TYPE, upper=True ), + cv.Optional(CONF_EXTENDED_RANGE): cv.boolean, } ) - .extend(cv.COMPONENT_SCHEMA) + .extend(cv.COMPONENT_SCHEMA), + _validate_extended_range, ) @@ -46,3 +59,4 @@ async def to_code(config): cg.add(var.set_pm_10_0_sensor(pm_10_0_sensor)) cg.add(var.set_aqi_calculation_type(config[CONF_CALCULATION_TYPE])) + cg.add(var.set_extended_range(config.get(CONF_EXTENDED_RANGE, False))) diff --git a/esphome/components/as3935_i2c/as3935_i2c.cpp b/esphome/components/as3935_i2c/as3935_i2c.cpp index 4c1020daa7..b3d015114f 100644 --- a/esphome/components/as3935_i2c/as3935_i2c.cpp +++ b/esphome/components/as3935_i2c/as3935_i2c.cpp @@ -24,11 +24,7 @@ void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t I2CAS3935Component::read_register(uint8_t reg) { uint8_t value; - if (write(®, 1) != i2c::ERROR_OK) { - ESP_LOGW(TAG, "Writing register failed!"); - return 0; - } - if (read(&value, 1) != i2c::ERROR_OK) { + if (!this->read_byte(reg, &value)) { ESP_LOGW(TAG, "Reading register failed!"); return 0; } diff --git a/esphome/components/beken_spi_led_strip/led_strip.cpp b/esphome/components/beken_spi_led_strip/led_strip.cpp index 4e22489844..9e14615d7a 100644 --- a/esphome/components/beken_spi_led_strip/led_strip.cpp +++ b/esphome/components/beken_spi_led_strip/led_strip.cpp @@ -37,15 +37,15 @@ namespace esphome::beken_spi_led_strip { static const char *const TAG = "beken_spi_led_strip"; -struct spi_data_t { +struct SpiData { SemaphoreHandle_t dma_tx_semaphore; volatile bool tx_in_progress; bool first_run; }; -static spi_data_t *spi_data = nullptr; +static SpiData *spi_data = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -static void set_spi_ctrl_register(unsigned long bit, bool val) { +static void set_spi_ctrl_register(uint32_t bit, bool val) { uint32_t value = REG_READ(SPI_CTRL); if (val == 0) { value &= ~bit; @@ -55,7 +55,7 @@ static void set_spi_ctrl_register(unsigned long bit, bool val) { REG_WRITE(SPI_CTRL, value); } -static void set_spi_config_register(unsigned long bit, bool val) { +static void set_spi_config_register(uint32_t bit, bool val) { uint32_t value = REG_READ(SPI_CONFIG); if (val == 0) { value &= ~bit; @@ -67,7 +67,7 @@ static void set_spi_config_register(unsigned long bit, bool val) { void spi_dma_tx_enable(bool enable) { GDMA_CFG_ST en_cfg; - set_spi_config_register(SPI_TX_EN, enable ? 1 : 0); + set_spi_config_register(SPI_TX_EN, enable); en_cfg.channel = SPI_TX_DMA_CHANNEL; en_cfg.param = enable ? 1 : 0; sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg); @@ -110,13 +110,13 @@ static void spi_set_clock(uint32_t max_hz) { param &= ~(SPI_CKR_MASK << SPI_CKR_POSI); param |= (div << SPI_CKR_POSI); REG_WRITE(SPI_CTRL, param); - ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div); + ESP_LOGD(TAG, "target frequency: %" PRIu32 ", actual frequency: %d", max_hz, source_clk / 2 / div); } void spi_dma_tx_finish_callback(unsigned int param) { spi_data->tx_in_progress = false; xSemaphoreGive(spi_data->dma_tx_semaphore); - spi_dma_tx_enable(0); + spi_dma_tx_enable(false); } void BekenSPILEDStripLightOutput::setup() { @@ -161,7 +161,7 @@ void BekenSPILEDStripLightOutput::setup() { return; } - spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t)); + spi_data = (SpiData *) calloc(1, sizeof(SpiData)); // NOLINT(cppcoreguidelines-no-malloc) if (spi_data == nullptr) { ESP_LOGE(TAG, "Cannot allocate spi_data!"); this->mark_failed(); @@ -177,20 +177,20 @@ void BekenSPILEDStripLightOutput::setup() { spi_data->first_run = true; - set_spi_ctrl_register(MSTEN, 0); - set_spi_ctrl_register(BIT_WDTH, 0); + set_spi_ctrl_register(MSTEN, false); + set_spi_ctrl_register(BIT_WDTH, false); spi_set_clock(this->spi_frequency_); - set_spi_ctrl_register(CKPOL, 0); - set_spi_ctrl_register(CKPHA, 0); - set_spi_ctrl_register(MSTEN, 1); - set_spi_ctrl_register(SPIEN, 1); + set_spi_ctrl_register(CKPOL, false); + set_spi_ctrl_register(CKPHA, false); + set_spi_ctrl_register(MSTEN, true); + set_spi_ctrl_register(SPIEN, true); - set_spi_ctrl_register(TXINT_EN, 0); - set_spi_ctrl_register(RXINT_EN, 0); - set_spi_config_register(SPI_TX_FINISH_EN, 1); - set_spi_config_register(SPI_RX_FINISH_EN, 1); - set_spi_ctrl_register(RXOVR_EN, 0); - set_spi_ctrl_register(TXOVR_EN, 0); + set_spi_ctrl_register(TXINT_EN, false); + set_spi_ctrl_register(RXINT_EN, false); + set_spi_config_register(SPI_TX_FINISH_EN, true); + set_spi_config_register(SPI_RX_FINISH_EN, true); + set_spi_ctrl_register(RXOVR_EN, false); + set_spi_ctrl_register(TXOVR_EN, false); value = REG_READ(SPI_CTRL); value &= ~CTRL_NSSMD_3; @@ -199,7 +199,7 @@ void BekenSPILEDStripLightOutput::setup() { value = GFUNC_MODE_SPI_DMA; sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value); - set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0); + set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, false); GDMA_CFG_ST en_cfg; GDMACFG_TPYES_ST init_cfg; @@ -210,7 +210,7 @@ void BekenSPILEDStripLightOutput::setup() { init_cfg.dstptr_incr = 0; init_cfg.srcptr_incr = 1; init_cfg.src_start_addr = this->dma_buf_; - init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO + init_cfg.dst_start_addr = (void *) SPI_DAT; // NOLINT(performance-no-int-to-ptr) SPI_DMA_REG4_TXFIFO init_cfg.channel = SPI_TX_DMA_CHANNEL; init_cfg.prio = 0; // 10 init_cfg.u.type4.src_loop_start_addr = this->dma_buf_; @@ -230,7 +230,7 @@ void BekenSPILEDStripLightOutput::setup() { en_cfg.param = 0; sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg); - spi_dma_tx_enable(0); + spi_dma_tx_enable(false); value = REG_READ(SPI_CONFIG); value &= ~(0xFFF << 8); @@ -247,7 +247,8 @@ void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uin void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { // protect from refreshing too often uint32_t now = micros(); - if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) { + if (this->max_refresh_rate_.has_value() && *this->max_refresh_rate_ != 0 && + (now - this->last_refresh_) < *this->max_refresh_rate_) { // try again next loop iteration, so that this change won't get lost this->schedule_show(); return; @@ -293,7 +294,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) { } spi_data->first_run = false; - spi_dma_tx_enable(1); + spi_dma_tx_enable(true); this->status_clear_warning(); } @@ -376,7 +377,7 @@ void BekenSPILEDStripLightOutput::dump_config() { " RGB Order: %s\n" " Max refresh rate: %" PRIu32 "\n" " Number of LEDs: %u", - rgb_order, *this->max_refresh_rate_, this->num_leds_); + rgb_order, this->max_refresh_rate_.value_or(0), this->num_leds_); } float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; } diff --git a/esphome/components/bk72xx_ble/__init__.py b/esphome/components/bk72xx_ble/__init__.py new file mode 100644 index 0000000000..29e2a6b13d --- /dev/null +++ b/esphome/components/bk72xx_ble/__init__.py @@ -0,0 +1,94 @@ +"""BK72xx BLE — BLE controller support for the BLE-5.x LibreTiny Beken chips. + +The platform analog of esp32_ble / rp2040_ble: owns the Beken BDK BLE stack +bring-up and the controller BLE address. Consumers (bk72xx_ble_tracker) build +on this component and contain no SDK calls of their own. + +Supported SoCs (BLE 5.x): BK7231N/BK7236 (BLE 5.1), BK7238/BK7252N/BK7253 +(BLE 5.2), and any future BLE-5.x SoC. Capability is detected at compile time, +not by a chip list: the C++ guards on `__has_include("ble_api.h")` — the Beken +BLE 5.x public API header, which the LibreTiny beken-72xx builder ships only +for BLE-5.x SoCs. BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) fail +with a clear #error. + +No framework patch is needed: the LibreTiny beken-72xx builder already compiles +and links the BLE 5.x stack (CFG_SUPPORT_BLE=1 + CFG_BLE_VERSION=BLE_VERSION_5_x; +prebuilt libble_.a per SoC). This component only calls into it via the +public ble_api.h. +""" + +import logging + +import esphome.codegen as cg +from esphome.components import libretiny +from esphome.components.libretiny.const import FAMILY_BK7231N, FAMILY_BK7238 +import esphome.config_validation as cv +from esphome.const import CONF_ENABLE_ON_BOOT, CONF_ID +from esphome.types import ConfigType + +DEPENDENCIES = ["bk72xx"] +CODEOWNERS = ["@Bl00d-B0b"] + +_LOGGER = logging.getLogger(__name__) + +bk72xx_ble_ns = cg.esphome_ns.namespace("bk72xx_ble") +BK72xxBLE = bk72xx_ble_ns.class_("BK72xxBLE", cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BK72xxBLE), + # Default off: on the single-core BK72xx, bringing the BLE stack up during + # boot competes with the WiFi connection handshake. Consumers enable the + # stack lazily on first use (e.g. the tracker's first scan start). + cv.Optional(CONF_ENABLE_ON_BOOT, default=False): cv.boolean, + } +).extend(cv.COMPONENT_SCHEMA) + + +async def to_code(config: ConfigType) -> None: + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + cg.add(var.set_enable_on_boot(config[CONF_ENABLE_ON_BOOT])) + + # Enable the BLE stack in the build (the '#h' maps to sys_config.h; the value + # is a list). ESPHome's libretiny platform normally appends CFG_SUPPORT_BLE=0 + # on BK7231N/BK7238 (saves ~21KB RAM/~200KB Flash when BLE is unused) to this + # SAME key — and add_platformio_option appends list values, it never replaces. + # The platform therefore skips its disable when this component is configured, + # so this =1 is the single CFG_SUPPORT_BLE define emitted. + cg.add_platformio_option("custom_options.sys_config#h", ["CFG_SUPPORT_BLE=1"]) + + # Pin the Beken BDK release the BLE 5.x stack is validated against. The + # bundled 3.0.33 has an older BLE header/library layout — and with + # CFG_SUPPORT_BLE=1 the SDK runs its BLE init unconditionally during boot + # (the reason the libretiny platform sets =0 when BLE is unused), so a + # mismatched BDK can crash the device before WiFi comes up regardless of + # enable_on_boot. Pinning here makes a plain config build against the + # validated BDK without any manual platformio_options. + _LOGGER.warning( + "bk72xx_ble builds with beken-bdk 3.0.78 instead of the platform's bundled " + "default: the default's older BLE layout can crash the device at boot when " + "BLE is compiled in" + ) + cg.add_platformio_option("custom_versions.beken-bdk", "3.0.78") + + # The BDK exposes the controller's BLE address as `common_default_bdaddr` on + # BK7231N, but NOT on BK7238 (its BLE stack has no such symbol; the address is + # derived from the WiFi MAC instead — the BDK's own fallback). Tell the C++ + # which path is available so it doesn't reference a missing symbol. + family = libretiny.get_libretiny_family() + if family == FAMILY_BK7231N: + cg.add_define("BK72XX_BLE_HAS_COMMON_BDADDR") + elif family == FAMILY_BK7238: + # ESPHome's LibreTiny disables BLE on BK7238 because the SDK can hang at + # WiFi STA startup when BLE init runs. This component re-enables BLE, so + # warn loudly: BK7238 is accepted but not hardware-verified and may be + # WiFi-unstable with BLE on. + _LOGGER.warning( + "bk72xx_ble on BK7238: enabling BLE is known to risk a WiFi STA startup " + "hang on this family and is not yet hardware-verified. Expect possible " + "instability." + ) + + cg.add_define("USE_BK72XX_BLE") diff --git a/esphome/components/bk72xx_ble/bk72xx_ble.cpp b/esphome/components/bk72xx_ble/bk72xx_ble.cpp new file mode 100644 index 0000000000..a5ecaf4abb --- /dev/null +++ b/esphome/components/bk72xx_ble/bk72xx_ble.cpp @@ -0,0 +1,292 @@ +// bk72xx_ble.cpp +// +// BLE controller support for the BK72xx BLE-5.x chips (LibreTiny beken-72xx +// family) — the platform analog of esp32_ble / rp2040_ble. Owns everything that +// talks to the Beken BDK BLE stack: +// - one-time stack bring-up (ble_set_notice_cb() + ble_entry()), +// - the controller BLE address, +// - the raw controller scan primitives (bk_ble_scan_start/stop), +// - the scan-report ring: the BDK notice callback (BLE task) takes a report +// from a fixed pool and pushes it on a lock-free SPSC queue; loop() drains, +// dispatches on the main task and returns reports to the pool — the same +// EventPool + LockFreeQueue handoff esp32_ble uses, zero allocation at +// steady state. +// Consumers contain no SDK calls of their own. +// +// NOTE: the Beken BDK BLE 5.x stack is compiled and linked by the LibreTiny +// beken-72xx builder itself (prebuilt libble_.a + ble_5_x sources, gated +// on CFG_SUPPORT_BLE / CFG_BLE_VERSION in sys_config.h). This component only +// calls into it via the public ble_api.h — no framework patch is required. + +#include "bk72xx_ble.h" // pulls esphome/core/defines.h for USE_BK72XX_BLE + +#ifdef USE_BK72XX_BLE + +#include + +#include "esphome/core/hal.h" +#include "esphome/core/helpers.h" // get_mac_address_raw() +#include "esphome/core/log.h" + +// --------------------------------------------------------------------------- +// SDK-capability gate (not a chip allowlist). +// This component drives the Beken BLE *5.x* controller via its public API, +// `ble_api.h`, which the LibreTiny beken-72xx builder ships only for the +// BLE-5.x SoCs (it selects the `ble_pub` 5.x stack from CFG_BLE_VERSION; the +// 4.2 SoCs build a different, older API with no ble_api.h). Gate on the header +// itself so any BLE-5.x Beken chip — present or future — is supported without a +// hard-coded list, and a non-5.x build fails here with a clear message instead +// of a cryptic "ble_api.h: No such file or directory". +// --------------------------------------------------------------------------- +#if defined(CLANG_TIDY) +// The clang-tidy environment does not carry the full Beken BDK BLE 5.x API +// (its ble_api.h variant lacks parts of the 5.x surface), so there is nothing +// accurate to analyze the SDK calls against — skip the file under analysis. +#define BK72XX_BLE_NO_SDK +#elif !__has_include("ble_api.h") +#error \ + "bk72xx_ble requires a BLE 5.x Beken SDK (ble_api.h). Supported SoCs: BK7231N/BK7236 (BLE 5.1) and BK7238/BK7252N/BK7253 (BLE 5.2). BK7231T/BK7251/BK7271 (BLE 4.2) and BK7231Q (no BLE) are not supported." +#endif + +#ifndef BK72XX_BLE_NO_SDK + +// --------------------------------------------------------------------------- +// Beken BDK BLE 5.x SDK — public API. +// Exposed on the include path by the LibreTiny beken-72xx builder +// (cores/.../ble_5_x_rw + driver/include). Wrapped in extern "C" because these +// are C headers consumed from C++ (a standard C-header-from-C++ pattern). +// --------------------------------------------------------------------------- +extern "C" { +#include "ble_api.h" // bk_ble_scan_start/stop, ble_entry, ble_set_notice_cb, + // app_ble_get_idle_actv_idx_handle, struct scan_param, + // recv_adv_t, ble_notice_t, BLE_5_REPORT_ADV, SCAN_ACTV +#ifdef BK72XX_BLE_HAS_COMMON_BDADDR +#include "common_bt_defines.h" // struct bd_addr +// The controller's public BLE address, populated by the BDK during ble_entry(). +// Present on BK7231N; the other BLE-5.x chips' stacks have no such symbol — there the +// address is derived from the WiFi MAC instead (matching the BDK's own fallback). +extern struct bd_addr common_default_bdaddr; +#endif +// ble_entry() brings up the BDK BLE stack; it is not declared in ble_api.h, so +// declare it here. +void ble_entry(void); +} + +namespace esphome::bk72xx_ble { + +static const char *const TAG = "bk72xx_ble"; + +// The BDK notice callback is a plain C function pointer with no user argument, +// so it reaches the (single) component instance through a file-static pointer. +static BK72xxBLE *s_ble = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +// --------------------------------------------------------------------------- +// BLE notice callback — runs in the BDK BLE task context. +// The BK controller reports every advertisement as a BLE_5_REPORT_ADV notice +// carrying a recv_adv_t. Copy it into the queue and return; all dispatch +// happens in loop() on the main task. +// --------------------------------------------------------------------------- +static void ble_notice_callback(ble_notice_t notice, void *param) { + if (s_ble == nullptr || param == nullptr) + return; + if (notice != BLE_5_REPORT_ADV) + return; + + const recv_adv_t *info = reinterpret_cast(param); + // rssi is a signed dBm carried in a uint8_t; cast through int8_t (standard for + // a signed dBm value packed in a uint8_t). + s_ble->enqueue_scan_report(info->adv_addr, static_cast(info->rssi), info->adv_addr_type, info->data, + info->data_len); +} + +void BK72xxBLE::enqueue_scan_report(const uint8_t *mac, int8_t rssi, uint8_t addr_type, const uint8_t *data, + uint16_t data_len) { + BLEScanReport *report = this->report_pool_.allocate(); + if (report == nullptr) { + // Pool exhausted — the queue is full; count and drop. + this->report_queue_.increment_dropped_count(); + return; + } + memcpy(report->mac, mac, 6); + report->rssi = rssi; + report->addr_type = addr_type; + report->data_len = + (data_len <= sizeof(report->data)) ? static_cast(data_len) : static_cast(sizeof(report->data)); + memcpy(report->data, data, report->data_len); + // Cannot fail: the pool is sized to the queue capacity. + this->report_queue_.push(report); +} + +// --------------------------------------------------------------------------- +// Component lifecycle +// --------------------------------------------------------------------------- + +void BK72xxBLE::setup() { + s_ble = this; + // Resolve the MAC early so get_mac_lsb_first() is valid for consumers before + // the stack is up (it is re-read once ble_entry() has run). + this->resolve_mac_(); + if (this->enable_on_boot_) { + this->enable(); + } +} + +// AFTER_WIFI, not BLUETOOTH: replicates the proven pre-split timing — the BDK +// is first touched only once WiFi is up (single-core WiFi/BLE bring-up order). +float BK72xxBLE::get_setup_priority() const { return setup_priority::AFTER_WIFI; } + +void BK72xxBLE::enable() { + if (this->state_ != BLEComponentState::STATE_OFF) + return; + this->state_ = BLEComponentState::ENABLING; + + // One-time BLE stack init: register the notice callback, then bring up the + // BDK BLE stack. The BDK has no teardown path — init happens at most once. + ble_set_notice_cb(ble_notice_callback); + ble_entry(); + + delay(100); // NOLINT — one-time BLE stack init; the SDK needs this settle time + + // Re-read the BLE MAC now that the controller is up (common_default_bdaddr is + // populated by ble_entry()); resolve_mac_() may have fallen back earlier. + this->resolve_mac_(); + +#ifdef BK72XX_BLE_HAS_COMMON_BDADDR + // Liveness heuristic (BK7231N): a healthy ble_entry() populates + // common_default_bdaddr during init, so all-zero after the settle delay + // suggests the stack did not come up. The BDK entry point returns void — no + // return code exists — so warn rather than fail: scan starts against a dead + // stack already fail cleanly downstream (no idle activity handle). + bool bdaddr_live = false; + for (uint8_t b : common_default_bdaddr.addr) { + if (b != 0) { + bdaddr_live = true; + break; + } + } + if (!bdaddr_live) + ESP_LOGW(TAG, "Controller address still unset after init; BLE stack may not have started"); +#endif + + this->state_ = BLEComponentState::ACTIVE; + ESP_LOGD(TAG, "BLE stack initialised"); +} + +void BK72xxBLE::loop() { + // Drain the lock-free ring filled by the BLE task; all per-report work runs + // here on the main task, then the report returns to the pool. + BLEScanReport *report = this->report_queue_.pop(); + if (report == nullptr) + return; + do { + for (auto *listener : this->scan_listeners_) + listener->on_scan_report(*report); + this->report_pool_.release(report); + } while ((report = this->report_queue_.pop()) != nullptr); + + // Log dropped reports — only reachable when reports were processed; drops can + // only occur while the queue is full, and only this loop drains it. + uint16_t dropped = this->report_queue_.get_and_reset_dropped_count(); + if (dropped > 0) + ESP_LOGW(TAG, "Dropped %u scan reports due to queue overflow", dropped); +} + +void BK72xxBLE::get_mac_lsb_first(uint8_t out[6]) const { + for (int i = 0; i < 6; i++) + out[i] = this->ble_mac_[i]; +} + +void BK72xxBLE::dump_config() { + // ble_mac_ is stored LSB-first (BLE convention); print [5..0] for the + // MSB-first order Home Assistant shows. + ESP_LOGCONFIG(TAG, + "BK72xx BLE:\n" + " MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" + " Active: %s", + this->ble_mac_[5], this->ble_mac_[4], this->ble_mac_[3], this->ble_mac_[2], this->ble_mac_[1], + this->ble_mac_[0], YESNO(this->is_active())); +} + +// --------------------------------------------------------------------------- +// MAC resolution +// --------------------------------------------------------------------------- + +void BK72xxBLE::resolve_mac_() { +#ifdef BK72XX_BLE_HAS_COMMON_BDADDR + // BK7231N: the BDK populates common_default_bdaddr (LSB-first, BLE convention) + // during ble_entry(). It may still be zero before the stack is up; if so, fall + // through to the WiFi-derived MAC below. + bool nonzero = false; + for (uint8_t b : common_default_bdaddr.addr) { + if (b != 0) { + nonzero = true; + break; + } + } + if (nonzero) { + memcpy(this->ble_mac_, common_default_bdaddr.addr, 6); + return; + } +#endif + // Chips whose BLE stack does not export common_default_bdaddr (BK7238 and the other + // BLE-5.x SoCs), or BK7231N before the stack is up: derive the BLE MAC exactly as the + // Beken BDK does in bdaddr_env_init() — the WiFi STA MAC with only its last byte + // incremented (sta_mac[5] += 1, a plain byte increment with no carry into the next + // byte), OUI unchanged. This reproduces the address the controller advertises with + // (verified against the BK7231N BLE-5.1 and BK7252N/BK7238 BLE-5.2 SDK sources), so it + // matches on every device, including the last-byte == 0xFF edge that a 24-bit increment + // would carry differently. + uint8_t wifi_mac[6]; + get_mac_address_raw(wifi_mac); // MSB-first + const uint8_t ble[6] = {wifi_mac[0], wifi_mac[1], wifi_mac[2], + wifi_mac[3], wifi_mac[4], static_cast(wifi_mac[5] + 1)}; + // Store LSB-first to match recv_adv_t adv_addr ordering. + for (int i = 0; i < 6; i++) + this->ble_mac_[i] = ble[5 - i]; +} + +// --------------------------------------------------------------------------- +// Controller scan primitives +// --------------------------------------------------------------------------- + +bool BK72xxBLE::scan_start(uint16_t interval, uint16_t window) { + if (!this->is_active()) + this->enable(); + + if (this->scan_actv_idx_ != 0xFF) { + // Already scanning — stop first so this call cleanly restarts with the new + // parameters (the BDK cannot start a second scan on a busy activity). + this->scan_stop(); + } + + struct scan_param sp; + memset(&sp, 0, sizeof(sp)); + sp.channel_map = 7; // advertising channels 37/38/39 + sp.interval = interval; + sp.window = window; + + this->scan_actv_idx_ = app_ble_get_idle_actv_idx_handle(SCAN_ACTV); + if (this->scan_actv_idx_ == 0xFF) { + ESP_LOGE(TAG, "Scan start failed: no idle activity handle"); + return false; + } + ble_err_t ret = bk_ble_scan_start(this->scan_actv_idx_, &sp, nullptr); + if (ret != ERR_SUCCESS) { + ESP_LOGE(TAG, "Scan start failed (err %d)", static_cast(ret)); + this->scan_actv_idx_ = 0xFF; + return false; + } + return true; +} + +void BK72xxBLE::scan_stop() { + if (this->scan_actv_idx_ != 0xFF) { + bk_ble_scan_stop(this->scan_actv_idx_, nullptr); + this->scan_actv_idx_ = 0xFF; + } +} + +} // namespace esphome::bk72xx_ble + +#endif // BK72XX_BLE_NO_SDK +#endif // USE_BK72XX_BLE diff --git a/esphome/components/bk72xx_ble/bk72xx_ble.h b/esphome/components/bk72xx_ble/bk72xx_ble.h new file mode 100644 index 0000000000..2654f4e68e --- /dev/null +++ b/esphome/components/bk72xx_ble/bk72xx_ble.h @@ -0,0 +1,98 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_BK72XX_BLE + +#include "esphome/core/component.h" +#include "esphome/core/event_pool.h" +#include "esphome/core/lock_free_queue.h" + +#include +#include + +namespace esphome::bk72xx_ble { + +enum class BLEComponentState : uint8_t { + STATE_OFF = 0, + ENABLING, + ACTIVE, +}; + +/// One advertisement report from the controller. +struct BLEScanReport { + uint8_t mac[6]; // LSB-first, as the controller delivers it + int8_t rssi; // signed dBm + uint8_t addr_type; + uint8_t data_len; // bytes valid in data[] + uint8_t data[62]; // legacy advertisement (31) + scan response (31) + + // EventPool contract: nothing is heap-allocated inside a report. + void release() {} +}; + +/// Consumer interface for controller scan reports. on_scan_report() always runs +/// on the ESPHome main task: reports are queued from the BDK BLE task and +/// drained by the controller's loop(), so consumers never deal with cross-task +/// state (the esp32_ble event-queue pattern). +class BLEScanListener { + public: + virtual void on_scan_report(const BLEScanReport &report) = 0; + + protected: + ~BLEScanListener() = default; // deletion via this interface is not part of the contract +}; + +// Maximum reports buffered between the BLE task and loop(). +static constexpr uint8_t MAX_SCAN_REPORT_QUEUE_SIZE = 64; + +class BK72xxBLE final : public Component { + public: + void setup() override; + void loop() override; + void dump_config() override; + float get_setup_priority() const override; + + /// Bring up the BDK BLE stack (one-time; the BDK has no teardown path). + void enable(); + bool is_active() const { return this->state_ == BLEComponentState::ACTIVE; } + + void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } + + /// Controller BLE address, least-significant octet first (BLE convention). + void get_mac_lsb_first(uint8_t out[6]) const; + + /// Register a consumer for scan reports (delivered on the main task via loop()). + void register_scan_listener(BLEScanListener *listener) { this->scan_listeners_.push_back(listener); } + + /// Start the controller scan. Interval/window are in BLE units (0.625 ms). + /// Enables the stack first if needed. Returns false on controller failure. + bool scan_start(uint16_t interval, uint16_t window); + /// Stop the controller scan (no-op when not scanning). + void scan_stop(); + + /// Internal: buffer one controller report (BDK notice callback, BLE task + /// context — bounded copy under the scheduler lock, nothing else). + void enqueue_scan_report(const uint8_t *mac, int8_t rssi, uint8_t addr_type, const uint8_t *data, uint16_t data_len); + + protected: + void resolve_mac_(); + + std::vector scan_listeners_; + // Report ring: the BDK notice callback (BLE task) allocates a report from the + // pool, fills it and pushes the pointer; loop() pops, dispatches and releases. + // Lock-free SPSC, zero allocation at steady state — the esp32_ble pattern. + esphome::LockFreeQueue report_queue_; + // Pool sized to queue capacity (SIZE-1): the ring reserves one slot, so + // allocate() returns nullptr before push() can fail. This prevents leaking a + // pool slot on a failed push and keeps release() off the producer path. + esphome::EventPool report_pool_; + uint8_t ble_mac_[6]{0}; // LSB-first (BLE convention) + uint8_t scan_actv_idx_{0xFF}; + BLEComponentState state_{BLEComponentState::STATE_OFF}; + bool enable_on_boot_{false}; +}; + +} // namespace esphome::bk72xx_ble + +#endif // USE_BK72XX_BLE diff --git a/esphome/components/bk72xx_ble_tracker/__init__.py b/esphome/components/bk72xx_ble_tracker/__init__.py new file mode 100644 index 0000000000..39c9ada731 --- /dev/null +++ b/esphome/components/bk72xx_ble_tracker/__init__.py @@ -0,0 +1,151 @@ +"""BK72xx BLE Tracker — ESPHome BLE 5.x scanner for the BLE-5.x-capable +LibreTiny Beken chips (beken-72xx family). + +Builds on the bk72xx_ble controller component (stack bring-up, BLE address, +scan primitives) and implements the platform-neutral ble_device_base BLEHub +contract: the shared BLE sensors (ble_presence, ble_rssi, ble_scanner, +bthome_mithermometer, xiaomi_*, …) bind to this tracker through +cv.use_id(BLEHub) with no BK-specific code. + +Scan modes: + continuous: true — scan runs forever; never stops automatically. + Use this when the radio is dedicated to BLE. + continuous: false — a started scan runs for `duration` ms, then stops. The + FIRST start is external too: nothing in this component + starts a non-continuous scan on boot, so until the + automation actions land (follow-up PR) the radio stays + idle. start_scan() is called from code (e.g. an api + client-connected automation) so the single-core radio + can service WiFi in between scans. +""" + +import esphome.codegen as cg +from esphome.components import bk72xx_ble, ble_device_base, ota +from esphome.components.const import CONF_SCAN_PARAMETERS, CONF_WINDOW +import esphome.config_validation as cv +from esphome.const import CONF_CONTINUOUS, CONF_DURATION, CONF_ID, CONF_INTERVAL +from esphome.core import CORE, CoroPriority, coroutine_with_priority +from esphome.types import ConfigType + +CONF_BK72XX_BLE_ID = "bk72xx_ble_id" + +DEPENDENCIES = ["bk72xx"] +AUTO_LOAD = ["ble_device_base", "bk72xx_ble"] +CODEOWNERS = ["@Bl00d-B0b"] + +bk72xx_ble_tracker_ns = cg.esphome_ns.namespace("bk72xx_ble_tracker") +BK72xxBLETracker = bk72xx_ble_tracker_ns.class_( + "BK72xxBLETracker", ble_device_base.BLEHub, cg.Component +) + + +def to_ble_units(value: cv.TimePeriod) -> int: + """Convert a scan time to the controller's 0.625 ms units. + + Used by both validation and codegen so what is validated is exactly what is + programmed — the truncation here is what makes the duty-cycle check below + meaningful. + """ + return value.total_microseconds // 625 + + +def validate_scan_parameters(config: ConfigType) -> ConfigType: + """Reject impossible window/interval/duration combinations at config time. + + Mirrors esp32_ble_tracker: the controller cannot scan for longer than the + interval, and a too-short duration would end the scan period almost + immediately. Catching it here gives a clear error instead of a runtime + controller failure and the 1/sec retry loop. + """ + duration = config[CONF_DURATION] + interval = config[CONF_INTERVAL] + window = config[CONF_WINDOW] + + if window > interval: + raise cv.Invalid( + f"Scan window ({window}) needs to be smaller than scan interval ({interval})" + ) + + # BLE scan interval/window are programmed in 0.625 ms units as a 16-bit value; the + # controller only accepts 2.5 ms .. 10240 ms (0x0004 .. 0x4000). Reject out-of-range + # values here instead of letting the unit conversion silently overflow. + for name, value in (("interval", interval), ("window", window)): + if value.total_microseconds < 2500 or value.total_microseconds > 10_240_000: + raise cv.Invalid( + f"Scan {name} ({value}) must be between 2.5 ms and 10240 ms" + ) + + # Validate what actually reaches the controller: both values are truncated to + # whole 0.625 ms units, so a window/interval pair that differs by less than one + # unit collapses to the same value — silently programming a 100 % duty cycle + # (radio permanently on) from a config that asked for less. + interval_units = to_ble_units(interval) + window_units = to_ble_units(window) + if window_units == interval_units and window < interval: + raise cv.Invalid( + f"Scan window ({window}) and interval ({interval}) both round to " + f"{interval_units} x 0.625 ms, which the controller scans at a 100 % duty " + f"cycle. Separate them by at least 0.625 ms." + ) + + if interval.total_microseconds * 3 > duration.total_microseconds: + raise cv.Invalid( + f"Scan duration ({duration}) must cover at least three scan intervals " + f"({interval}): the scanner listens on one of the three BLE advertising " + f"channels per interval, so a shorter duration can miss devices entirely." + ) + + return config + + +SCAN_PARAMETERS_SCHEMA = cv.All( + cv.Schema( + { + cv.Optional(CONF_DURATION, default="5min"): cv.positive_time_period_seconds, + # interval/window default to the BK reference scan rate — 100 ms / 30 ms, + # a 30 % duty cycle. Converted to the controller's 0.625 ms BLE units in + # to_code(). (LN882H's SDK recommends a different 100 / 50 ms = 50 %.) + cv.Optional(CONF_INTERVAL, default="100ms"): cv.positive_time_period, + cv.Optional(CONF_WINDOW, default="30ms"): cv.positive_time_period, + cv.Optional(CONF_CONTINUOUS, default=True): cv.boolean, + } + ), + validate_scan_parameters, +) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(BK72xxBLETracker), + cv.GenerateID(CONF_BK72XX_BLE_ID): cv.use_id(bk72xx_ble.BK72xxBLE), + cv.Optional(CONF_SCAN_PARAMETERS, default={}): SCAN_PARAMETERS_SCHEMA, + } +).extend(cv.COMPONENT_SCHEMA) + + +# Runs at FINAL priority so every BLE sensor has registered through +# ble_device_base (and any tracker-owned listeners have been counted) before +# the StaticVector size is emitted. Same pattern as esp32_ble_tracker. +@coroutine_with_priority(CoroPriority.FINAL) +async def _emit_listener_count() -> None: + count = ble_device_base.get_listener_count() + if count > 0: + cg.add_define("ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT", count) + + +async def to_code(config: ConfigType) -> None: + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + parent = await cg.get_variable(config[CONF_BK72XX_BLE_ID]) + cg.add(var.set_parent(parent)) + + # Get notified when an OTA update starts, to pause scanning (esp32_ble_tracker parity) + ota.request_ota_state_listeners() + + scan = config[CONF_SCAN_PARAMETERS] + cg.add(var.set_scan_interval(to_ble_units(scan[CONF_INTERVAL]))) + cg.add(var.set_scan_window(to_ble_units(scan[CONF_WINDOW]))) + cg.add(var.set_scan_duration(scan[CONF_DURATION].total_milliseconds)) + cg.add(var.set_scan_continuous(scan[CONF_CONTINUOUS])) + + CORE.add_job(_emit_listener_count) diff --git a/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.cpp b/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.cpp new file mode 100644 index 0000000000..634285d530 --- /dev/null +++ b/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.cpp @@ -0,0 +1,211 @@ +// bk72xx_ble_tracker.cpp +// +// BLE scan policy for the BK72xx BLE-5.x chips: parameters, duration/period +// timers and the rate-limited start retry. All controller access (stack +// bring-up, scan primitives, the BLE-task → main-task report queue) goes +// through the bk72xx_ble component — no SDK calls and no cross-task state here. + +#ifdef USE_LIBRETINY + +#include "bk72xx_ble_tracker.h" + +#include +#include + +#include "esphome/core/hal.h" +#include "esphome/core/log.h" + +namespace esphome::bk72xx_ble_tracker { + +static const char *const TAG = "bk72xx_ble_tracker"; + +// Minimum interval between scan (re)start attempts, so a failing controller start +// cannot be retried every main-loop iteration (single-core CPU starvation). The +// interval doubles with consecutive failed starts (1 s up to 64 s) so a controller +// that never comes up — the controller logs each failure at ERROR — settles into a +// slow, quiet poll instead of an error line every second for the rest of uptime; +// a single WARN is emitted when the retry interval first saturates. +static constexpr uint32_t SCAN_START_RETRY_MS = 1000; +static constexpr uint8_t SCAN_START_RETRY_MAX_DOUBLINGS = 6; // 1 s << 6 = 64 s + +// --------------------------------------------------------------------------- +// Component lifecycle +// --------------------------------------------------------------------------- + +void BK72xxBLETracker::setup() { + // Receive the controller's scan reports; the controller queues them from the + // BLE task and delivers here on the main task. + this->parent_->register_scan_listener(this); +#ifdef USE_OTA_STATE_LISTENER + // Pause scanning while an OTA update is in flight — on the single-core BK72xx the + // BLE scan competes with the OTA flash writes. Mirrors esp32_ble_tracker. + ota::get_global_ota_callback()->add_global_state_listener(this); +#endif +} + +#ifdef USE_OTA_STATE_LISTENER +void BK72xxBLETracker::on_ota_global_state(ota::OTAState state, float progress, uint8_t error, + ota::OTAComponent *comp) { + if (state == ota::OTA_STARTED) { + this->scan_continuous_before_ota_ = this->scan_continuous_; + this->stop_scan(); + } else if ((state == ota::OTA_ERROR || state == ota::OTA_ABORT) && this->scan_continuous_before_ota_) { + // On success the device reboots, so restore only on a failed/aborted update; + // loop() restarts the scan on its next iteration (continuous idle branch). + this->scan_continuous_before_ota_ = false; + this->scan_continuous_ = true; + } +} +#endif // USE_OTA_STATE_LISTENER + +void BK72xxBLETracker::loop() { + const uint32_t now = millis(); + if (this->scan_continuous_) { + if (!this->scan_running_) { + // Rate-limit (re)start attempts. The controller start can fail (no idle activity + // handle, WiFi/BLE coexistence) and leave scan_running_ false; retrying every + // main-loop iteration would spin the single-core CPU and starve WiFi (device + // becomes unresponsive). The interval backs off with consecutive failures so a + // controller that never comes up polls slowly and quietly. + const uint8_t doublings = std::min(this->failed_start_count_, SCAN_START_RETRY_MAX_DOUBLINGS); + if (now - this->last_scan_start_attempt_ >= (SCAN_START_RETRY_MS << doublings)) { + this->last_scan_start_attempt_ = now; + this->start_scan_(); + if (this->scan_running_) { + this->failed_start_count_ = 0; + } else if (this->failed_start_count_ < SCAN_START_RETRY_MAX_DOUBLINGS) { + ++this->failed_start_count_; + if (this->failed_start_count_ == SCAN_START_RETRY_MAX_DOUBLINGS) { + ESP_LOGW(TAG, "Scan start keeps failing; retrying every %" PRIu32 " s", + (SCAN_START_RETRY_MS << SCAN_START_RETRY_MAX_DOUBLINGS) / 1000); + } + } + } + } + // Period timer: fire on_scan_end() once per scan_duration_ window, mirroring + // esp32_ble_tracker::cleanup_scan_state_(). Gated on scan_started_once_ so a scan + // that never came up (start kept failing) does not fire spurious on_scan_end events. + if (this->scan_started_once_ && now - this->scan_period_start_ >= this->scan_duration_) { +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + for (auto *listener : this->listeners_) + listener->on_scan_end(); + this->discovered_log_.clear(); // reset per-scan "Found device" dedup (esp32_ble_tracker parity) +#endif + this->scan_period_start_ = now; + } + return; + } + + // Non-continuous mode: run for scan_duration_ ms, then stop and fire on_scan_end. + // Restart is driven externally (e.g. api: on_client_connected:). + if (this->scan_running_ && now - this->scan_start_time_ >= this->scan_duration_) { + this->stop_scan_(); + } +} + +void BK72xxBLETracker::dump_config() { + ESP_LOGCONFIG(TAG, + "BK72xx BLE Tracker:\n" + " Scan Duration: %" PRIu32 " s\n" + " Scan Interval: %.0f ms (%" PRIu32 " BLE units)\n" + " Scan Window: %.0f ms (%" PRIu32 " BLE units)\n" + " Scan Type: PASSIVE\n" + " Continuous Scanning: %s", + this->scan_duration_ / 1000, this->scan_interval_ * 0.625f, this->scan_interval_, + this->scan_window_ * 0.625f, this->scan_window_, YESNO(this->scan_continuous_)); +} + +// --------------------------------------------------------------------------- +// Scan report — delivered by the controller's loop() on the ESPHome main task +// (the controller queues reports from the BLE task), so publish_state() and +// listener dispatch run in main-loop context with no cross-task handling here. +// --------------------------------------------------------------------------- + +void BK72xxBLETracker::on_scan_report(const bk72xx_ble::BLEScanReport &report) { + // Raw callback (the raw-advertisement path). + if (this->raw_advertisement_callback_.is_set()) { + const ble_device_base::RawAdvertisement adv{.mac = report.mac, + .data = report.data, + .data_len = report.data_len, + .rssi = report.rssi, + .addr_type = report.addr_type}; + this->raw_advertisement_callback_.invoke(adv); + } + +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + ble_device_base::ESPBTDevice device; + device.from_scan_result(report.mac, report.rssi, report.addr_type, report.data, report.data_len); + bool found = false; + for (auto *listener : this->listeners_) + if (listener->parse_device(device)) + found = true; + // Mirror esp32_ble_tracker: log a newly-seen device only when nothing claimed + // it and the scan is one-shot (continuous scans would spam). + if (!found && !this->scan_continuous_) + this->discovered_log_.log_device(TAG, device); +#endif // ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT +} + +// --------------------------------------------------------------------------- +// Public scan control +// --------------------------------------------------------------------------- + +void BK72xxBLETracker::start_scan() { + // Mirrors esp32_ble_tracker::start_scan(): caller sets scan_continuous_ via + // set_scan_continuous() first, then calls start_scan() to begin scanning. + if (!this->scan_running_) { + this->start_scan_(); + } +} + +void BK72xxBLETracker::stop_scan() { + this->scan_continuous_ = false; + this->stop_scan_(); +} + +// --------------------------------------------------------------------------- +// Internal scan start / stop +// --------------------------------------------------------------------------- + +void BK72xxBLETracker::start_scan_() { + if (this->scan_running_) + return; + + if (!this->parent_->scan_start(static_cast(this->scan_interval_), + static_cast(this->scan_window_))) + return; + + const uint32_t now = millis(); + this->scan_running_ = true; + this->scan_start_time_ = now; + // Log every explicit start at DEBUG — stop_scan_() logs every stop at DEBUG, and + // in non-continuous mode each period is an explicit start, so asymmetric logging + // would read as the scanner failing to come back up. + ESP_LOGD(TAG, "Scan started (passive, window=%.0fms, interval=%.0fms)", this->scan_window_ * 0.625f, + this->scan_interval_ * 0.625f); + // Re-anchor the on_scan_end period to every successful start — first start (so the + // period counts from the scan, not from boot) and every restart after a stop (so + // resuming after longer than scan_duration, e.g. a failed OTA restoring continuous + // mode 10 minutes later, does not fire on_scan_end before an advertisement can + // arrive). scan_started_once_ purely gates the period timer. + this->scan_period_start_ = now; + this->scan_started_once_ = true; +} + +void BK72xxBLETracker::stop_scan_() { + if (!this->scan_running_) + return; + this->parent_->scan_stop(); + this->scan_running_ = false; + ESP_LOGD(TAG, "Scan stopped"); +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + for (auto *listener : this->listeners_) + listener->on_scan_end(); + this->discovered_log_.clear(); // reset per-scan "Found device" dedup (esp32_ble_tracker parity) +#endif + this->scan_period_start_ = millis(); // reset period clock so on_scan_end does not double-fire +} + +} // namespace esphome::bk72xx_ble_tracker + +#endif // USE_LIBRETINY diff --git a/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.h b/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.h new file mode 100644 index 0000000000..9c70246f05 --- /dev/null +++ b/esphome/components/bk72xx_ble_tracker/bk72xx_ble_tracker.h @@ -0,0 +1,150 @@ +// bk72xx_ble_tracker.h +// +// ESPHome BLE scanner for the BK72xx BLE-5.x chips (LibreTiny beken-72xx family). +// Implements the platform-neutral ble_device_base::BLEHub contract on top of the +// bk72xx_ble controller component: parsed ESPBTDevice objects go to registered +// listeners (bthome_mithermometer, ble_presence, …) and every raw frame to the +// hub's raw-advertisement callback. +// +// This component contains no Beken SDK calls and no cross-task state: the +// controller (stack bring-up, BLE address, scan primitives, and the BLE-task → +// main-task report queue) is owned by bk72xx_ble, which delivers every scan +// report on the ESPHome main task. The tracker owns scan policy — parameters, +// duration/period timers and the rate-limited start retry. +// +// YAML config (values shown are the defaults; interval/window are a 30 % duty +// cycle, the BK reference scan rate): +// +// bk72xx_ble_tracker: +// scan_parameters: +// interval: 100ms +// window: 30ms +// duration: 5min +// continuous: true + +#pragma once + +#ifdef USE_LIBRETINY + +#include "esphome/components/bk72xx_ble/bk72xx_ble.h" +#include "esphome/components/ble_device_base/ble_device.h" +#include "esphome/components/ble_device_base/ble_hub.h" +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +#include + +#ifdef USE_OTA_STATE_LISTENER +#include "esphome/components/ota/ota_backend.h" +#endif + +namespace esphome::bk72xx_ble_tracker { + +// --------------------------------------------------------------------------- +// BK72xxBLETracker +// --------------------------------------------------------------------------- + +class BK72xxBLETracker : public Component, + public ble_device_base::BLEHub, + public bk72xx_ble::BLEScanListener, + public Parented +#ifdef USE_OTA_STATE_LISTENER + , + public ota::OTAGlobalStateListener +#endif +{ + public: + // ---- ESPHome Component ---- + void setup() override; + void loop() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } + +#ifdef USE_OTA_STATE_LISTENER + // Pause scanning while an OTA update runs (single-core WiFi/BLE/flash contention); + // mirrors esp32_ble_tracker. + void on_ota_global_state(ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) override; +#endif + + // ---- YAML configuration setters ---- + void set_scan_interval(uint32_t scan_interval) { this->scan_interval_ = scan_interval; } + void set_scan_window(uint32_t scan_window) { this->scan_window_ = scan_window; } + void set_scan_duration(uint32_t scan_duration) { this->scan_duration_ = scan_duration; } + void set_scan_continuous(bool scan_continuous) { this->scan_continuous_ = scan_continuous; } + + // ---- Public scan control ---- + // Mirrors esp32_ble_tracker: set_scan_continuous() + start_scan() / stop_scan(). + void start_scan(); + void stop_scan(); + + // ---- ble_device_base::BLEHub contract ---- + void register_listener(ble_device_base::ESPBTDeviceListener *listener) override { +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + this->listeners_.push_back(listener); +#endif + } + void set_raw_advertisement_callback(ble_device_base::RawAdvertisementCallback callback) override { + this->raw_advertisement_callback_ = callback; + } + ble_device_base::HubCapabilities get_capabilities() const override { + // The Beken BDK exposes no active-scan path (passive scanning only), so the + // controller never solicits scan responses and never merges them; consumers + // relying on scan-response fields (device names) get them only where the + // receiver merges per address (Home Assistant does). No GATT client either. + return {.active_scan = false, .merges_scan_response = false, .gatt = false}; + } + // The controller stores the address LSB-first (BLE convention); the contract + // wants printable (MSB-first) order. + void get_adapter_mac(uint8_t out[6]) override { + uint8_t mac[6]; + this->parent_->get_mac_lsb_first(mac); + for (int i = 0; i < 6; i++) + out[i] = mac[5 - i]; + } + bool scan_running() override { return this->scan_running_; } + bool scan_active() override { return false; } // BK72xx scan is passive-only + + // ---- bk72xx_ble::BLEScanListener ---- + // Delivered by the controller's loop() on the ESPHome main task — the + // BLE-task → main-task handoff already happened in the controller's queue. + void on_scan_report(const bk72xx_ble::BLEScanReport &report) override; + + protected: + void start_scan_(); + void stop_scan_(); + + bool scan_running_{false}; + // Defaults: the BK reference — 30 % duty cycle + // (interval 100 ms / window 30 ms), in 0.625 ms BLE units. + uint32_t scan_interval_{160}; // 160 × 0.625 ms = 100 ms + uint32_t scan_window_{48}; // 48 × 0.625 ms = 30 ms (30/100 = 30 %) + uint32_t scan_duration_{300000}; + bool scan_continuous_{true}; +#ifdef USE_OTA_STATE_LISTENER + bool scan_continuous_before_ota_{false}; // continuous mode saved at OTA start, restored on OTA failure +#endif + uint32_t scan_start_time_{0}; + + uint32_t last_scan_start_attempt_{0}; // millis() of last start_scan_() attempt; rate-limits retries + uint8_t failed_start_count_{0}; // consecutive failed starts; drives the retry backoff (reset on success) + uint32_t scan_period_start_{0}; // millis() at start of current scan period; used to rate-limit on_scan_end() + bool scan_started_once_{false}; // true after first successful scan start; gates the period timer + + ble_device_base::RawAdvertisementCallback raw_advertisement_callback_{}; +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + // Parsed-advertisement consumers registered through ble_device_base. + // Codegen-sized: no heap allocation, no std::vector template instantiations. + StaticVector listeners_; +#endif + +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + // Per-period "Found device" DEBUG log with MAC dedup — shared implementation + // in ble_device_base, identical output on every tracker backend. Guarded like + // its only writer so a no-listener build does not carry an unused vector. + ble_device_base::DiscoveredDeviceLog discovered_log_{}; +#endif +}; + +} // namespace esphome::bk72xx_ble_tracker + +#endif // USE_LIBRETINY diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index d41fb17961..25001c8f74 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -51,7 +51,9 @@ bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es for (auto *node : this->nodes_) node->gattc_event_handler(event, esp_gattc_if, param); - if (!this->services_.empty() && this->all_nodes_established_()) { + // The release frees the GATT cache that BLEClientBase's CCCD lookup still needs. + // The last REG_FOR_NOTIFY event clears the counter before node dispatch, so the release still runs here. + if (!this->services_.empty() && !this->notify_registration_pending() && this->all_nodes_established_()) { this->release_services(); ESP_LOGD(TAG, "All clients established, services released"); } diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index f27bef332b..f20df31816 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -34,6 +34,11 @@ class BLEClientNode { // This should be transitioned to Established once the node no longer needs // the services/descriptors/characteristics of the parent client. This will // allow some memory to be freed. + // The parent frees the peer's GATT cache once every node reports Established. + // Never report Established while an operation that reads that cache is outstanding. + // - esp_ble_gattc_register_for_notify() completes asynchronously. + // - Register from ESP_GATTC_SEARCH_CMPL_EVT, then set this from ESP_GATTC_REG_FOR_NOTIFY_EVT. + // - BLEClientBase::register_for_notify() holds the release until the registration completes. espbt::ClientState node_state; BLEClient *parent() { return this->parent_; } diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 4bd871dc81..60992f282e 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -77,8 +77,7 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga this->handle = descr->handle; } if (this->notify_) { - auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(), - this->parent()->get_remote_bda(), chr->handle); + auto status = this->parent()->register_for_notify(chr->handle); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); } diff --git a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp index 7eaa6af076..6f09281922 100644 --- a/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +++ b/esphome/components/ble_client/text_sensor/ble_text_sensor.cpp @@ -77,8 +77,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ this->handle = descr->handle; } if (this->notify_) { - auto status = esp_ble_gattc_register_for_notify(this->parent()->get_gattc_if(), - this->parent()->get_remote_bda(), chr->handle); + auto status = this->parent()->register_for_notify(chr->handle); if (status) { ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status); } diff --git a/esphome/components/ble_device_base/__init__.py b/esphome/components/ble_device_base/__init__.py new file mode 100644 index 0000000000..d9789b0e9f --- /dev/null +++ b/esphome/components/ble_device_base/__init__.py @@ -0,0 +1,134 @@ +""" +ble_device_base — the platform-neutral BLE layer. + +Owns the shared advertisement types (ESPBTUUID / ESPBTDevice / ServiceData / +ESPBLEiBeacon / ESPBTDeviceListener, in ble_device.h) and the tracker contract +(BLEHub, in ble_hub.h) on every platform. + +BLE consumers (sensor components, bluetooth_proxy) bind to whichever tracker the +configuration declares via `cv.use_id(BLEHub)` — ESPHome resolves any declared +subclass, so there is no platform table here and no dependency in either +direction. A sensor appends inject_ble_hub to its CONFIG_SCHEMA (via cv.All) and +calls register_ble_device() in to_code; a tracker component subclasses BLEHub +(C++ and codegen class). Adding a new BLE chip requires only a new tracker +component. + +AES-CCM decryption for encrypted advertisements is provided portably in +ble_aes_ccm.h. +""" + +import re + +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.core import CORE +from esphome.types import ConfigType + +CODEOWNERS = ["@Bl00d-B0b"] + +CONF_BLE_HUB_ID = "ble_hub_id" + +# CORE.data key: number of parsed-advertisement listeners registered in this +# build. Trackers whose codegen sizes storage at compile time (esp32's +# StaticVector count define) read it in their final coroutine. +KEY_BLE_LISTENER_COUNT = "ble_device_base_listener_count" + +ble_device_base_ns = cg.esphome_ns.namespace("ble_device_base") + +# The neutral tracker contract. Every tracker's codegen class declares this as a +# parent, which is what lets cv.use_id(BLEHub) resolve any of them. +BLEHub = ble_device_base_ns.class_("BLEHub") + +# The neutral listener base (C++: ble_device_base::ESPBTDeviceListener). +ESPBTDeviceListener = ble_device_base_ns.class_("ESPBTDeviceListener") + + +def inject_ble_hub(config: ConfigType) -> ConfigType: + """Validator: auto-resolve the configured BLE tracker into the config. + + Append via cv.All to a BLE consumer's CONFIG_SCHEMA. Uses cv.GenerateID + + cv.use_id(BLEHub): an omitted id resolves to the single declared tracker on + any platform; multiple trackers can be disambiguated with an explicit + ble_hub_id. + """ + return cv.Schema( + {cv.GenerateID(CONF_BLE_HUB_ID): cv.use_id(BLEHub)}, extra=cv.ALLOW_EXTRA + )(config) + + +def request_irk_support() -> None: + """Compile in resolve_irk()'s software-AES path. Called by sensors with an + irk: option so builds without IRK do not carry the resolution code.""" + cg.add_define("USE_BLE_DEVICE_IRK") + + +def get_listener_count() -> int: + """Number of parsed listeners registered so far (for tracker codegen).""" + return CORE.data.get(KEY_BLE_LISTENER_COUNT, 0) + + +async def register_ble_device(var: cg.MockObj, config: ConfigType) -> cg.MockObj: + """Register `var` as a parsed-advertisement listener on the configured hub.""" + hub = await cg.get_variable(config[CONF_BLE_HUB_ID]) + cg.add(hub.register_listener(var)) + CORE.data[KEY_BLE_LISTENER_COUNT] = CORE.data.get(KEY_BLE_LISTENER_COUNT, 0) + 1 + return var + + +# ---- shared validation / codegen helpers (platform-neutral) ---- +BT_UUID16_FORMAT = "XXXX" +BT_UUID32_FORMAT = "XXXXXXXX" +BT_UUID128_FORMAT = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" + +_BT_UUID16_RE = re.compile("^[A-F0-9]{4,}$") +_BT_UUID32_RE = re.compile("^[A-F0-9]{8,}$") +_BT_UUID128_RE = re.compile( + "^[A-F0-9]{8,}-[A-F0-9]{4,}-[A-F0-9]{4,}-[A-F0-9]{4,}-[A-F0-9]{12,}$" +) + + +# Validator table keyed by input length: (compiled pattern, label used in errors). +_BT_UUID_FORMATS = { + len(BT_UUID16_FORMAT): (_BT_UUID16_RE, "16 bit"), + len(BT_UUID32_FORMAT): (_BT_UUID32_RE, "32 bit"), + len(BT_UUID128_FORMAT): (_BT_UUID128_RE, "128"), +} + + +def bt_uuid(value: str) -> str: + in_value = cv.string_strict(value) + value = in_value.upper() + + fmt = _BT_UUID_FORMATS.get(len(value)) + if fmt is None: + raise cv.Invalid( + f"Bluetooth UUID must be in 16 bit '{BT_UUID16_FORMAT}', 32 bit '{BT_UUID32_FORMAT}', or 128 bit '{BT_UUID128_FORMAT}' format" + ) + pattern, label = fmt + if not pattern.match(value): + raise cv.Invalid( + f"Invalid hexadecimal value for {label} UUID format: '{in_value}'" + ) + return value + + +def as_hex(value: str) -> cg.RawExpression: + return cg.RawExpression(f"0x{value}ULL") + + +def _hex_array_expression(value: str, reverse: bool) -> cg.RawExpression: + value = value.replace("-", "") + cpp_array = [ + f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] + ] + if reverse: + cpp_array.reverse() + return cg.RawExpression(f"(uint8_t*)(const uint8_t[16]){{{','.join(cpp_array)}}}") + + +def as_hex_array(value: str) -> cg.RawExpression: + return _hex_array_expression(value, reverse=False) + + +def as_reversed_hex_array(value: str) -> cg.RawExpression: + return _hex_array_expression(value, reverse=True) diff --git a/esphome/components/ble_device_base/ble_aes_ccm.cpp b/esphome/components/ble_device_base/ble_aes_ccm.cpp new file mode 100644 index 0000000000..3ff34acc34 --- /dev/null +++ b/esphome/components/ble_device_base/ble_aes_ccm.cpp @@ -0,0 +1,202 @@ +#include "ble_aes_ccm.h" + +#include +#include + +namespace esphome::ble_device_base { + +namespace { + +// AES-128 forward cipher only — CCM uses the block cipher in the encrypt +// direction for both the CTR keystream and the CBC-MAC. +const uint8_t SBOX[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, // + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, // + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, // + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, // + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, // + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, // + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, // + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, // + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, // + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, // + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, // + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, // + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, // + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, // + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, // + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, // +}; + +const uint8_t RCON[11] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36}; + +inline uint8_t xtime(uint8_t x) { return static_cast((x << 1) ^ ((x & 0x80) ? 0x1b : 0x00)); } + +// AES-128 forward cipher with on-the-fly key schedule. +class Aes128 { + public: + explicit Aes128(const uint8_t key[16]) { + memcpy(this->rk_, key, 16); + for (size_t i = 16; i < 176; i += 4) { + uint8_t t[4] = {this->rk_[i - 4], this->rk_[i - 3], this->rk_[i - 2], this->rk_[i - 1]}; + if (i % 16 == 0) { + const uint8_t tmp = t[0]; + t[0] = static_cast(SBOX[t[1]] ^ RCON[i / 16]); + t[1] = SBOX[t[2]]; + t[2] = SBOX[t[3]]; + t[3] = SBOX[tmp]; + } + for (size_t j = 0; j < 4; j++) + this->rk_[i + j] = static_cast(this->rk_[i - 16 + j] ^ t[j]); + } + } + + void encrypt(const uint8_t in[16], uint8_t out[16]) const { + uint8_t s[16]; + memcpy(s, in, 16); + for (size_t i = 0; i < 16; i++) + s[i] ^= this->rk_[i]; + + for (size_t round = 1; round < 10; round++) { + for (uint8_t &b : s) + b = SBOX[b]; + shift_rows(s); + for (size_t c = 0; c < 4; c++) { + uint8_t *col = s + c * 4; + const uint8_t a0 = col[0], a1 = col[1], a2 = col[2], a3 = col[3]; + const uint8_t h = static_cast(a0 ^ a1 ^ a2 ^ a3); + col[0] ^= static_cast(h ^ xtime(static_cast(a0 ^ a1))); + col[1] ^= static_cast(h ^ xtime(static_cast(a1 ^ a2))); + col[2] ^= static_cast(h ^ xtime(static_cast(a2 ^ a3))); + col[3] ^= static_cast(h ^ xtime(static_cast(a3 ^ a0))); + } + for (size_t i = 0; i < 16; i++) + s[i] ^= this->rk_[round * 16 + i]; + } + + for (uint8_t &b : s) + b = SBOX[b]; + shift_rows(s); + for (size_t i = 0; i < 16; i++) + s[i] ^= this->rk_[160 + i]; + memcpy(out, s, 16); + } + + protected: + static void shift_rows(uint8_t s[16]) { + uint8_t t = s[1]; + s[1] = s[5]; + s[5] = s[9]; + s[9] = s[13]; + s[13] = t; + t = s[2]; + s[2] = s[10]; + s[10] = t; + t = s[6]; + s[6] = s[14]; + s[14] = t; + t = s[3]; + s[3] = s[15]; + s[15] = s[11]; + s[11] = s[7]; + s[7] = t; + } + + uint8_t rk_[176]; +}; + +} // namespace + +void aes128_encrypt_block(const uint8_t key[16], const uint8_t in[16], uint8_t out[16]) { + Aes128 aes(key); + aes.encrypt(in, out); +} + +bool aes_ccm_auth_decrypt(const uint8_t key[16], const uint8_t *nonce, size_t nonce_len, const uint8_t *aad, + size_t aad_len, const uint8_t *ciphertext, size_t ct_len, uint8_t *plaintext, + const uint8_t *tag, size_t tag_len) { + // CCM length field width L and tag width M (RFC 3610 §2.2). For a 13-byte + // nonce L = 2; BTHome uses M = 4. + if (nonce_len < 7 || nonce_len > 13 || tag_len < 4 || tag_len > 16) + return false; + const size_t l = 15 - nonce_len; + const size_t m = tag_len; + + const Aes128 aes(key); + + // Build CTR block A_i = [L-1] | nonce | counter(L bytes, big-endian). + uint8_t a[16]; + auto build_ctr = [&](uint32_t counter) { + a[0] = static_cast(l - 1); + memcpy(a + 1, nonce, nonce_len); + memset(a + 1 + nonce_len, 0, l); + for (size_t i = 0; i < l; i++) + a[15 - i] = static_cast((counter >> (8 * i)) & 0xff); + }; + + // S_0 = E(A_0); its first m bytes mask the transmitted tag. + uint8_t s0[16]; + build_ctr(0); + aes.encrypt(a, s0); + + // CTR-decrypt ciphertext into plaintext using S_1, S_2, ... + uint8_t ks[16]; + for (size_t off = 0; off < ct_len; off += 16) { + build_ctr(static_cast(off / 16) + 1); + aes.encrypt(a, ks); + const size_t n = std::min(static_cast(16), ct_len - off); + for (size_t i = 0; i < n; i++) + plaintext[off + i] = static_cast(ciphertext[off + i] ^ ks[i]); + } + + // CBC-MAC over B_0 | (formatted AAD) | plaintext. + uint8_t x[16]; + uint8_t b0[16]; + const uint8_t flags = static_cast((aad_len > 0 ? 0x40 : 0x00) | (((m - 2) / 2) << 3) | (l - 1)); + b0[0] = flags; + memcpy(b0 + 1, nonce, nonce_len); + memset(b0 + 1 + nonce_len, 0, l); + for (size_t i = 0; i < l; i++) + b0[15 - i] = static_cast((ct_len >> (8 * i)) & 0xff); + aes.encrypt(b0, x); // X_1 = E(B_0) + + if (aad_len > 0) { + // Only the < 2^16-2^8 encoding is needed for BLE-sized AAD. + uint8_t blk[16] = {0}; + blk[0] = static_cast((aad_len >> 8) & 0xff); + blk[1] = static_cast(aad_len & 0xff); + size_t ai = 0; + size_t pos = 2; + while (pos < 16 && ai < aad_len) + blk[pos++] = aad[ai++]; + for (size_t i = 0; i < 16; i++) + x[i] ^= blk[i]; + aes.encrypt(x, x); + while (ai < aad_len) { + memset(blk, 0, 16); + const size_t n = std::min(static_cast(16), aad_len - ai); + memcpy(blk, aad + ai, n); + ai += n; + for (size_t i = 0; i < 16; i++) + x[i] ^= blk[i]; + aes.encrypt(x, x); + } + } + + for (size_t off = 0; off < ct_len; off += 16) { + uint8_t blk[16] = {0}; + const size_t n = std::min(static_cast(16), ct_len - off); + memcpy(blk, plaintext + off, n); + for (size_t i = 0; i < 16; i++) + x[i] ^= blk[i]; + aes.encrypt(x, x); + } + + // Expected tag U = T XOR S_0[0..m). Constant-time compare with the received tag. + uint8_t diff = 0; + for (size_t i = 0; i < m; i++) + diff |= static_cast((x[i] ^ s0[i]) ^ tag[i]); + return diff == 0; +} + +} // namespace esphome::ble_device_base diff --git a/esphome/components/ble_device_base/ble_aes_ccm.h b/esphome/components/ble_device_base/ble_aes_ccm.h new file mode 100644 index 0000000000..d337ad8ecd --- /dev/null +++ b/esphome/components/ble_device_base/ble_aes_ccm.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +namespace esphome::ble_device_base { + +// Self-contained AES-128-CCM authenticated decryption (RFC 3610). +// +// Encrypted BLE advertisements (BTHome, several Xiaomi/ATC variants) use +// AES-128-CCM. The platform crypto that provides it is inconsistent across BLE +// targets: ESP-IDF exposes PSA/mbedtls, but a LibreTiny SDK may keep its mbedtls +// internal (e.g. the beken-72xx SDK ships mbedtls with CCM enabled but does not +// put it on the application include path), so a sensor cannot rely on +// being available. This software implementation makes +// encrypted-advertisement decryption work on every BLE platform without a +// per-chip crypto dependency. Decryption volume is tiny (one short block per +// matching advertisement), so software AES is not a meaningful cost. +// +// Verifies the CCM authentication tag and, on success, writes `ct_len` decrypted +// bytes to `plaintext` and returns true. Returns false when authentication fails +// (the caller must then discard `plaintext`). The CCM parameters follow the +// caller (BTHome: 13-byte nonce, 4-byte tag, no associated data); `aad` may be +// null when `aad_len` is 0. +/// AES-128 single-block encrypt (the same software cipher CCM uses). Used by +/// ESPBTDevice::resolve_irk() for the Bluetooth "ah" RPA hash, so IRK matching +/// works identically on every platform with no chip crypto dependency. +void aes128_encrypt_block(const uint8_t key[16], const uint8_t in[16], uint8_t out[16]); + +bool aes_ccm_auth_decrypt(const uint8_t key[16], const uint8_t *nonce, size_t nonce_len, const uint8_t *aad, + size_t aad_len, const uint8_t *ciphertext, size_t ct_len, uint8_t *plaintext, + const uint8_t *tag, size_t tag_len); + +} // namespace esphome::ble_device_base diff --git a/esphome/components/ble_device_base/ble_device.cpp b/esphome/components/ble_device_base/ble_device.cpp new file mode 100644 index 0000000000..9c3e1d4397 --- /dev/null +++ b/esphome/components/ble_device_base/ble_device.cpp @@ -0,0 +1,530 @@ +// ble_device.cpp +// +// Platform-neutral implementation of the shared BLE advertisement types. +// Parses raw BLE advertisement data into ESPBTDevice. + +#include "ble_device.h" + +#include "ble_aes_ccm.h" + +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +#include + +namespace esphome::ble_device_base { + +static const char *const TAG = "ble_device_base"; + +// Longest advertisement payload worth hex-dumping at VERY_VERBOSE +// (legacy advertising: 31-byte adv + 31-byte scan response). +static constexpr size_t BLE_ADV_MAX_LOG_BYTES = 62; + +// --------------------------------------------------------------------------- +// ESPBTUUID +// --------------------------------------------------------------------------- + +ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { + ESPBTUUID ret; + ret.type_ = Type::UUID16; + ret.uuid_.uuid16 = uuid; + return ret; +} + +ESPBTUUID ESPBTUUID::from_uint32(uint32_t uuid) { + ESPBTUUID ret; + ret.type_ = Type::UUID32; + ret.uuid_.uuid32 = uuid; + return ret; +} + +ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { + ESPBTUUID ret; + ret.type_ = Type::UUID128; + memcpy(ret.uuid_.uuid128, data, 16); + return ret; +} + +ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { + ESPBTUUID ret; + ret.type_ = Type::UUID128; + for (int i = 0; i < 16; i++) + ret.uuid_.uuid128[i] = data[15 - i]; + return ret; +} + +ESPBTUUID ESPBTUUID::from_raw(const char *data, size_t length) { + // Same text-parsing semantics as the historical esp32_ble::ESPBTUUID::from_raw. + ESPBTUUID ret; + if (length == 4) { + // 16-bit UUID as 4-character hex string + auto parsed = parse_hex(data, length); + if (parsed.has_value()) { + ret.type_ = Type::UUID16; + ret.uuid_.uuid16 = parsed.value(); + } + } else if (length == 8) { + // 32-bit UUID as 8-character hex string + auto parsed = parse_hex(data, length); + if (parsed.has_value()) { + ret.type_ = Type::UUID32; + ret.uuid_.uuid32 = parsed.value(); + } + } else if (length == 16) { + // 16 raw bytes (little-endian 128-bit UUID) + ret.type_ = Type::UUID128; + memcpy(ret.uuid_.uuid128, reinterpret_cast(data), 16); + } else if (length == 36) { + // Dashed text form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + ret.type_ = Type::UUID128; + int n = 0; + for (size_t i = 0; i < length; i += 2) { + if (data[i] == '-') + i++; + uint8_t msb = data[i]; + uint8_t lsb = data[i + 1]; + if (msb > '9') + msb -= 7; + if (lsb > '9') + lsb -= 7; + ret.uuid_.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); + } + } else { + ESP_LOGE(TAG, "ERROR: UUID value not 4, 8, 16 or 36 bytes - %s", data); + } + return ret; +} + +#ifdef USE_ESP32 +ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) { + if (uuid.len == ESP_UUID_LEN_16) + return ESPBTUUID::from_uint16(uuid.uuid.uuid16); + if (uuid.len == ESP_UUID_LEN_32) + return ESPBTUUID::from_uint32(uuid.uuid.uuid32); + return ESPBTUUID::from_raw(uuid.uuid.uuid128); +} + +esp_bt_uuid_t ESPBTUUID::get_uuid() const { + esp_bt_uuid_t ret; + switch (this->type_) { + case Type::UUID16: + ret.len = ESP_UUID_LEN_16; + ret.uuid.uuid16 = this->uuid_.uuid16; + break; + case Type::UUID32: + ret.len = ESP_UUID_LEN_32; + ret.uuid.uuid32 = this->uuid_.uuid32; + break; + default: + case Type::UUID128: + ret.len = ESP_UUID_LEN_128; + memcpy(ret.uuid.uuid128, this->uuid_.uuid128, ESP_UUID_LEN_128); + break; + } + return ret; +} + +void ESPBTDevice::parse_scan_rst(const esp32_ble::BLEScanResult &scan_result) { + this->scan_result_ = &scan_result; + // BLEScanResult's bda is most-significant octet first; the neutral ingest + // takes the BLE controller (LSB-first) order, so reverse — address_uint64()/ + // address_str() then produce exactly the historical esp32 values. + uint8_t mac_lsb_first[6]; + for (uint8_t i = 0; i < 6; i++) + mac_lsb_first[i] = scan_result.bda[5 - i]; + this->from_scan_result(mac_lsb_first, scan_result.rssi, scan_result.ble_addr_type, scan_result.ble_adv, + scan_result.adv_data_len + scan_result.scan_rsp_len); +} +#endif // USE_ESP32 + +ESPBTUUID ESPBTUUID::as_128bit() const { + if (this->type_ == Type::UUID128) + return *this; + uint8_t data[16]; + this->to_128bit_(data); + return ESPBTUUID::from_raw(data); +} + +bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const { + // Adjacent byte-pair search — identical semantics to esp32_ble::ESPBTUUID::contains. + switch (this->type_) { + case Type::UUID16: + return (this->uuid_.uuid16 >> 8) == data2 && (this->uuid_.uuid16 & 0xFF) == data1; + case Type::UUID32: + for (uint8_t i = 0; i < 3; i++) { + bool a = ((this->uuid_.uuid32 >> i * 8) & 0xFF) == data1; + bool b = ((this->uuid_.uuid32 >> (i + 1) * 8) & 0xFF) == data2; + if (a && b) + return true; + } + return false; + case Type::UUID128: + for (uint8_t i = 0; i < 15; i++) { + if (this->uuid_.uuid128[i] == data1 && this->uuid_.uuid128[i + 1] == data2) + return true; + } + return false; + } + return false; +} + +const char *ESPBTUUID::to_str(char *buf) const { + // Identical output format to esp32_ble::ESPBTUUID::to_str. + char *pos = buf; + switch (this->type_) { + case Type::UUID16: + *pos++ = '0'; + *pos++ = 'x'; + *pos++ = format_hex_pretty_char(this->uuid_.uuid16 >> 12); + *pos++ = format_hex_pretty_char((this->uuid_.uuid16 >> 8) & 0x0F); + *pos++ = format_hex_pretty_char((this->uuid_.uuid16 >> 4) & 0x0F); + *pos++ = format_hex_pretty_char(this->uuid_.uuid16 & 0x0F); + *pos = 0; // NUL-terminate + return buf; + case Type::UUID32: + *pos++ = '0'; + *pos++ = 'x'; + for (int shift = 28; shift >= 0; shift -= 4) + *pos++ = format_hex_pretty_char((this->uuid_.uuid32 >> shift) & 0x0F); + *pos = 0; // NUL-terminate + return buf; + default: + case Type::UUID128: + // Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + for (int8_t i = 15; i >= 0; i--) { + uint8_t byte = this->uuid_.uuid128[i]; + *pos++ = format_hex_pretty_char(byte >> 4); + *pos++ = format_hex_pretty_char(byte & 0x0F); + if (i == 12 || i == 10 || i == 8 || i == 6) + *pos++ = '-'; + } + *pos = 0; // NUL-terminate + return buf; + } +} + +void ESPBTUUID::to_128bit_(uint8_t out[16]) const { + // Bluetooth Base UUID 00000000-0000-1000-8000-00805F9B34FB (LSB-first), with the 16/32-bit + // value placed at bytes 12..; identical expansion to esp32_ble::ESPBTUUID::as_128bit(). + static const uint8_t BASE[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (this->type_ == Type::UUID128) { + memcpy(out, this->uuid_.uuid128, 16); + return; + } + memcpy(out, BASE, 16); + const uint32_t value = (this->type_ == Type::UUID32) ? this->uuid_.uuid32 : this->uuid_.uuid16; + const size_t len = (this->type_ == Type::UUID32) ? 4 : 2; + for (size_t i = 0; i < len; i++) + out[12 + i] = (value >> (i * 8)) & 0xFF; +} + +bool ESPBTUUID::operator==(const ESPBTUUID &other) const { + if (this->type_ == other.type_) { + switch (this->type_) { + case Type::UUID16: + return this->uuid_.uuid16 == other.uuid_.uuid16; + case Type::UUID32: + return this->uuid_.uuid32 == other.uuid_.uuid32; + case Type::UUID128: + return memcmp(this->uuid_.uuid128, other.uuid_.uuid128, 16) == 0; + } + return false; + } + // Different widths: expand both to the 128-bit Bluetooth Base UUID form and compare, so a + // configured 16/32-bit UUID matches the equivalent 128-bit advertisement (esp32 parity). + uint8_t a[16]; + uint8_t b[16]; + this->to_128bit_(a); + other.to_128bit_(b); + return memcmp(a, b, 16) == 0; +} + +// --------------------------------------------------------------------------- +// ESPBLEiBeacon +// --------------------------------------------------------------------------- + +ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(this->beacon_data_)); } + +optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) { + // iBeacon manufacturer specific data (after company-ID bytes have been stripped): + // [0x02][0x15][16-byte UUID][2-byte major][2-byte minor][1-byte power] = exactly 23 bytes + // Parity with esp32_ble_tracker: gate on the Apple company ID and length only. + // (Checking the 0x02/0x15 sub-type prefix would be stricter, but is a behavior + // change; it belongs to a follow-up, not this refactor.) + if (!data.uuid.contains(0x4C, 0x00)) // Apple company ID 0x004C + return {}; + if (data.data.size() != 23) + return {}; + return ESPBLEiBeacon(data.data.data()); +} + +// --------------------------------------------------------------------------- +// ESPBTDevice +// --------------------------------------------------------------------------- + +const char *ESPBTDevice::address_type_str() const { + switch (this->address_type_) { + case BLE_ADDR_TYPE_PUBLIC: + return "PUBLIC"; + case BLE_ADDR_TYPE_RANDOM: + return "RANDOM"; + case BLE_ADDR_TYPE_RPA_PUBLIC: + return "RPA_PUBLIC"; + case BLE_ADDR_TYPE_RPA_RANDOM: + return "RPA_RANDOM"; + default: + return "UNKNOWN"; + } +} + +void ESPBTDevice::from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data, + uint16_t data_len) { + // Ingest is BLE controller order (LSB-first); store in printable (MSB-first) + // order so the raw address() accessor matches the historical esp32 layout. + for (uint8_t i = 0; i < 6; i++) + this->address_[i] = mac[5 - i]; + this->address_type_ = addr_type; + this->rssi_ = rssi; + this->name_.clear(); + this->service_uuids_.clear(); + this->manufacturer_datas_.clear(); + this->service_datas_.clear(); + this->tx_powers_.clear(); + this->appearance_.reset(); + this->ad_flag_.reset(); + this->parse_adv_(data, data_len); + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE + char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGVV(TAG, + "Parse Result:\n" + " Address: %s (%s)\n" + " RSSI: %d\n" + " Name: '%s'", + this->address_str_to(addr_buf), this->address_type_str(), this->rssi_, this->name_.c_str()); + for (auto &it : this->tx_powers_) { + ESP_LOGVV(TAG, " TX Power: %d", it); + } + if (this->appearance_.has_value()) { + ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_); + } + if (this->ad_flag_.has_value()) { + ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_); + } + char uuid_buf[UUID_STR_LEN]; + for (auto &uuid : this->service_uuids_) { + ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_str(uuid_buf)); + } + char hex_buf[format_hex_pretty_size(BLE_ADV_MAX_LOG_BYTES)]; + for (auto &mfg_data : this->manufacturer_datas_) { + auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(mfg_data); + if (ibeacon.has_value()) { + ESP_LOGVV(TAG, + " Manufacturer iBeacon:\n" + " UUID: %s\n" + " Major: %u\n" + " Minor: %u\n" + " TXPower: %d", + ibeacon.value().get_uuid().to_str(uuid_buf), ibeacon.value().get_major(), ibeacon.value().get_minor(), + ibeacon.value().get_signal_power()); + } else { + ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", mfg_data.uuid.to_str(uuid_buf), + format_hex_pretty_to(hex_buf, mfg_data.data.data(), mfg_data.data.size())); + } + } + for (auto &svc_data : this->service_datas_) { + ESP_LOGVV(TAG, + " Service data:\n" + " UUID: %s\n" + " Data: %s", + svc_data.uuid.to_str(uuid_buf), + format_hex_pretty_to(hex_buf, svc_data.data.data(), svc_data.data.size())); + } + ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty_to(hex_buf, data, data_len)); +#endif // ESPHOME_LOG_HAS_VERY_VERBOSE +} + +std::string ESPBTDevice::address_str() const { + char buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + return std::string(this->address_str_to(buf)); +} + +const char *ESPBTDevice::address_str_to(char *buf) const { + // address_ is stored in printable (MSB-first) order. + format_mac_addr_upper(this->address_, buf); + return buf; +} + +uint64_t ESPBTDevice::address_uint64() const { + // address_ is MSB-first; byte 0 of the result is the LSB (esp32 semantics). + uint64_t addr = 0; + for (int i = 0; i < 6; i++) + addr |= static_cast(this->address_[i]) << ((5 - i) * 8); + return addr; +} + +bool ESPBTDevice::resolve_irk(const uint8_t *irk) const { +#ifdef USE_BLE_DEVICE_IRK + // Bluetooth Core 5.x "ah" function: hash = e(IRK, padding | prand)[low 24 bits]. + // The resolvable private address is prand (top 3 bytes) | hash (bottom 3 bytes). + // Uses the portable software AES-128 shared with the CCM decryptor, so IRK + // matching behaves identically on every platform (volume is one block per + // advertisement from a matching RPA device — software AES is not a cost). + uint8_t ecb_plaintext[16] = {0}; + uint8_t ecb_ciphertext[16]; + const uint64_t addr64 = this->address_uint64(); + ecb_plaintext[13] = (addr64 >> 40) & 0xff; + ecb_plaintext[14] = (addr64 >> 32) & 0xff; + ecb_plaintext[15] = (addr64 >> 24) & 0xff; + aes128_encrypt_block(irk, ecb_plaintext, ecb_ciphertext); + return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) && + ecb_ciphertext[13] == ((addr64 >> 16) & 0xff); +#else + // No sensor configured an irk: in this build; the AES core is compiled out. + (void) irk; + return false; +#endif +} + +void ESPBTDevice::parse_adv_(const uint8_t *payload, uint16_t len) { + // BLE AD structure TLV: [length][type][value...] + // length includes the type byte. + uint16_t offset = 0; + while (offset < len) { + uint8_t ad_len = payload[offset++]; + if (ad_len == 0) + continue; // possible zero-padded advertisement data (esp32_ble_tracker skips these too) + if (offset + ad_len > len) + break; + uint8_t ad_type = payload[offset]; + const uint8_t *ad_data = &payload[offset + 1]; + uint8_t ad_data_len = ad_len - 1; + offset += ad_len; + + switch (ad_type) { + case 0x01: // Flags + if (ad_data_len >= 1) + this->ad_flag_ = ad_data[0]; + break; + + case 0x08: // Shortened Local Name + case 0x09: // Complete Local Name + // Keep the longest name seen — a merged adv + scan-response frame may carry both the + // shortened and the complete name, and the shortened form must never replace the + // complete one (same rule as esp32_ble_tracker's parse_adv_). + if (ad_data_len > this->name_.length()) + this->name_.assign(reinterpret_cast(ad_data), ad_data_len); + break; + + case 0x0A: // TX Power Level + if (ad_data_len >= 1) + this->tx_powers_.push_back(static_cast(ad_data[0])); + break; + + case 0x19: // Appearance + if (ad_data_len >= 2) + this->appearance_ = static_cast(ad_data[0]) | (static_cast(ad_data[1]) << 8); + break; + + case 0x02: // Incomplete List of 16-bit Service UUIDs + case 0x03: // Complete List of 16-bit Service UUIDs + for (uint8_t i = 0; (i + 1) < ad_data_len; i += 2) { + uint16_t uuid = (static_cast(ad_data[i + 1]) << 8) | ad_data[i]; + this->service_uuids_.push_back(ESPBTUUID::from_uint16(uuid)); + } + break; + + case 0x04: // Incomplete List of 32-bit Service UUIDs + case 0x05: // Complete List of 32-bit Service UUIDs + for (uint8_t i = 0; (i + 3) < ad_data_len; i += 4) { + uint32_t uuid = (static_cast(ad_data[i + 3]) << 24) | + (static_cast(ad_data[i + 2]) << 16) | (static_cast(ad_data[i + 1]) << 8) | + ad_data[i]; + this->service_uuids_.push_back(ESPBTUUID::from_uint32(uuid)); + } + break; + + case 0x06: // Incomplete List of 128-bit Service UUIDs + case 0x07: // Complete List of 128-bit Service UUIDs + for (uint8_t i = 0; (i + 15) < ad_data_len; i += 16) + this->service_uuids_.push_back(ESPBTUUID::from_raw(&ad_data[i])); + break; + + case 0xFF: // Manufacturer Specific Data + if (ad_data_len >= 2) { + uint16_t company_id = (static_cast(ad_data[1]) << 8) | ad_data[0]; + ServiceData sd; + sd.uuid = ESPBTUUID::from_uint16(company_id); + sd.data.assign(ad_data + 2, ad_data + ad_data_len); + this->manufacturer_datas_.push_back(std::move(sd)); + } + break; + + case 0x16: // Service Data — 16-bit UUID + if (ad_data_len >= 2) { + uint16_t uuid = (static_cast(ad_data[1]) << 8) | ad_data[0]; + ServiceData sd; + sd.uuid = ESPBTUUID::from_uint16(uuid); + sd.data.assign(ad_data + 2, ad_data + ad_data_len); + this->service_datas_.push_back(std::move(sd)); + } + break; + + case 0x20: // Service Data — 32-bit UUID + if (ad_data_len >= 4) { + uint32_t uuid = (static_cast(ad_data[3]) << 24) | (static_cast(ad_data[2]) << 16) | + (static_cast(ad_data[1]) << 8) | ad_data[0]; + ServiceData sd; + sd.uuid = ESPBTUUID::from_uint32(uuid); + sd.data.assign(ad_data + 4, ad_data + ad_data_len); + this->service_datas_.push_back(std::move(sd)); + } + break; + + case 0x21: // Service Data — 128-bit UUID + if (ad_data_len >= 16) { + ServiceData sd; + sd.uuid = ESPBTUUID::from_raw(ad_data); + sd.data.assign(ad_data + 16, ad_data + ad_data_len); + this->service_datas_.push_back(std::move(sd)); + } + break; + + default: + break; + } + } +} + +// --------------------------------------------------------------------------- +// DiscoveredDeviceLog +// --------------------------------------------------------------------------- + +void DiscoveredDeviceLog::log_device(const char *tag, const ESPBTDevice &device) { +#ifdef ESPHOME_LOG_HAS_DEBUG + // Everything here feeds ESP_LOGD: below DEBUG the whole body (including the + // dedup vector growth) would be pure overhead, so compile it out entirely. + const uint64_t address = device.address_uint64(); + for (auto &disc : this->already_discovered_) { + if (disc == address) + return; + } + this->already_discovered_.push_back(address); + + char addr_buf[ESPBTDevice::MAC_ADDRESS_PRETTY_BUFFER_SIZE]; + ESP_LOGD(tag, + "Found device %s RSSI=%d\n" + " Address Type: %s", + device.address_str_to(addr_buf), device.get_rssi(), device.address_type_str()); + if (!device.get_name().empty()) { + ESP_LOGD(tag, " Name: '%s'", device.get_name().c_str()); + } + for (auto &tx_power : device.get_tx_powers()) { + ESP_LOGD(tag, " TX Power: %d", tx_power); + } +#endif // ESPHOME_LOG_HAS_DEBUG +} + +} // namespace esphome::ble_device_base diff --git a/esphome/components/ble_device_base/ble_device.h b/esphome/components/ble_device_base/ble_device.h new file mode 100644 index 0000000000..2d2cb5796b --- /dev/null +++ b/esphome/components/ble_device_base/ble_device.h @@ -0,0 +1,274 @@ +// ble_device.h +// +// Platform-neutral BLE advertisement types — the generic base every BLE consumer +// (sensor components, bluetooth_proxy, automation triggers) builds against: +// ESPBTUUID / ServiceData / ESPBLEiBeacon / ESPBTDevice / ESPBTDeviceListener +// +// These types are owned here on EVERY platform, with no chip-SDK types in their +// public surface. Platform trackers produce them: +// - esp32_ble_tracker adapts ESP-IDF scan results into ESPBTDevice and +// re-exports these names (esp32 only) for backward compatibility; +// - the LibreTiny trackers (bk72xx / ln882h) feed from_scan_result() directly. + +#pragma once + +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" + +#include +#include +#include +#include +#include + +#if defined(__cpp_lib_span) +#include +#endif + +#ifdef USE_ESP32 +// Historical esp32_ble API surface (below, under the same define) uses the +// ESP-IDF UUID/address/scan-result types directly; never referenced off-esp32. +#include "esphome/components/esp32_ble/ble_scan_result.h" +#include +#endif + +namespace esphome::ble_device_base { + +using adv_data_t = std::vector; + +// Bluetooth Core address types (spec values; matches ESP-IDF's esp_ble_addr_type_t). +static constexpr uint8_t BLE_ADDR_TYPE_PUBLIC = 0; +static constexpr uint8_t BLE_ADDR_TYPE_RANDOM = 1; +static constexpr uint8_t BLE_ADDR_TYPE_RPA_PUBLIC = 2; +static constexpr uint8_t BLE_ADDR_TYPE_RPA_RANDOM = 3; + +/// Buffer size for UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\0" +static constexpr size_t UUID_STR_LEN = 37; + +// --------------------------------------------------------------------------- +// ESPBTUUID — 16/32/128-bit Bluetooth UUID value type. +// API-compatible with the historical esp32_ble::ESPBTUUID; the esp_bt_uuid_t +// conversions live in esp32_ble (esp32-only adapters), not here. +// --------------------------------------------------------------------------- + +class ESPBTUUID { + public: + ESPBTUUID() = default; + + static ESPBTUUID from_uint16(uint16_t uuid); + static ESPBTUUID from_uint32(uint32_t uuid); + /// Construct from raw 16-byte little-endian UUID. + static ESPBTUUID from_raw(const uint8_t *data); + /// Construct from raw 16-byte big-endian UUID (reversed on store). + static ESPBTUUID from_raw_reversed(const uint8_t *data); + /// Parse from text: 4 hex chars (16-bit), 8 hex chars (32-bit), 16 raw bytes, + /// or the 36-char dashed UUID form. Same semantics as esp32_ble historically. + static ESPBTUUID from_raw(const char *data, size_t length); + static ESPBTUUID from_raw(const char *data) { return from_raw(data, strlen(data)); } + static ESPBTUUID from_raw(const std::string &data) { return from_raw(data.c_str(), data.length()); } + static ESPBTUUID from_raw(std::initializer_list data) { + return from_raw(reinterpret_cast(data.begin()), data.size()); + } + +#ifdef USE_ESP32 + /// Source compatibility with the historical esp32_ble API (esp32 builds only). + static ESPBTUUID from_uuid(esp_bt_uuid_t uuid); + esp_bt_uuid_t get_uuid() const; +#endif + + /// Expand to the 128-bit Bluetooth Base UUID form. + ESPBTUUID as_128bit() const; + + /// True if the UUID value contains the adjacent byte pair (data1, data2). + bool contains(uint8_t data1, uint8_t data2) const; + + bool operator==(const ESPBTUUID &other) const; + bool operator!=(const ESPBTUUID &other) const { return !(*this == other); } + + /// Write "0xABCD" / "0xABCDEF01" / the dashed 128-bit form into buf + /// (>= UUID_STR_LEN bytes) and return buf. + const char *to_str(char *buf) const; +#if defined(__cpp_lib_span) + const char *to_str(std::span output) const { return this->to_str(output.data()); } +#endif + enum class Type : uint8_t { UUID16, UUID32, UUID128 }; + Type type() const { return this->type_; } + uint16_t uuid16() const { return this->uuid_.uuid16; } + uint32_t uuid32() const { return this->uuid_.uuid32; } + const uint8_t *uuid128() const { return this->uuid_.uuid128; } + + protected: + // Expand to the 128-bit Bluetooth Base UUID byte form (out is 16 bytes, little-endian). + void to_128bit_(uint8_t out[16]) const; + + Type type_{Type::UUID16}; + union { + uint16_t uuid16; + uint32_t uuid32; + uint8_t uuid128[16]; + } uuid_{}; +}; + +// --------------------------------------------------------------------------- +// ServiceData — UUID-tagged advertisement payload (0x16 / 0xFF AD types) +// --------------------------------------------------------------------------- + +struct ServiceData { + ESPBTUUID uuid; + adv_data_t data; +}; + +// --------------------------------------------------------------------------- +// ESPBLEiBeacon +// --------------------------------------------------------------------------- + +class ESPBLEiBeacon { + public: + ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } + explicit ESPBLEiBeacon(const uint8_t *data); + static optional from_manufacturer_data(const ServiceData &data); + + uint16_t get_major() const { return byteswap(this->beacon_data_.major); } + uint16_t get_minor() const { return byteswap(this->beacon_data_.minor); } + int8_t get_signal_power() const { return this->beacon_data_.signal_power; } + ESPBTUUID get_uuid() const { return ESPBTUUID::from_raw_reversed(this->beacon_data_.proximity_uuid); } + + protected: + struct PACKED BeaconData { + uint8_t sub_type; + uint8_t length; + uint8_t proximity_uuid[16]; + uint16_t major; + uint16_t minor; + int8_t signal_power; + } beacon_data_; +}; + +/// Pack a controller-order (LSB-first) MAC into the uint64 the API speaks. +/// +/// The result is the printable-order value esp32 has always sent +/// (esp32_ble::ble_addr_to_uint64), so both proxy paths agree on the wire. +/// This takes the raw controller order delivered by BLEHub's raw-advertisement +/// callback; ESPBTDevice::address_uint64() is the equivalent for an already +/// parsed device, whose address is stored MSB-first. +inline uint64_t mac_lsb_first_to_uint64(const uint8_t *mac) { + uint64_t addr = 0; + for (int i = 0; i < 6; i++) + addr |= static_cast(mac[i]) << (i * 8); + return addr; +} + +// --------------------------------------------------------------------------- +// ESPBTDevice — parsed BLE advertisement +// --------------------------------------------------------------------------- + +class ESPBTDevice { + public: + /// Populate from a raw scan result delivered by a BLE tracker backend. + /// mac is least-significant octet first (BLE controller convention). + void from_scan_result(const uint8_t *mac, int rssi, uint8_t addr_type, const uint8_t *data, uint16_t data_len); + + // Alias the core constant so the two cannot drift apart. + static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = esphome::MAC_ADDRESS_PRETTY_BUFFER_SIZE; + + /// Return MAC as "XX:XX:XX:XX:XX:XX" string. + std::string address_str() const; + /// Buffer overload: writes "XX:XX:XX:XX:XX:XX\0" into buf (>= 18 bytes), returns buf. + const char *address_str_to(char *buf) const; +#if defined(__cpp_lib_span) + const char *address_str_to(std::span buf) const { + return this->address_str_to(buf.data()); + } +#endif + /// Return MAC as packed uint64 (byte 0 in LSB — matches esp32's address_uint64). + uint64_t address_uint64() const; + /// Raw MAC bytes in printable (MSB-first) order — matches the historical + /// esp32 layout (ESP-IDF bda order). + const uint8_t *address() const { return address_; } +#ifdef USE_ESP32 + // Historical esp32 signature: consumers assign the result to esp_ble_addr_type_t. + esp_ble_addr_type_t get_address_type() const { return static_cast(this->address_type_); } + /// Historical esp32 ingest (esp32 builds only): parse an ESP-IDF scan result. + void parse_scan_rst(const esp32_ble::BLEScanResult &scan_result); + // Exposed through a function for use in lambdas + const esp32_ble::BLEScanResult &get_scan_result() const { return *scan_result_; } +#else + uint8_t get_address_type() const { return this->address_type_; } +#endif + /// Human-readable address type ("PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM" or + /// "UNKNOWN"), backed by the shared BLE_ADDR_TYPE_* constants above. + const char *address_type_str() const; + + int get_rssi() const { return rssi_; } + const std::string &get_name() const { return name_; } + + const std::vector &get_service_uuids() const { return service_uuids_; } + const std::vector &get_manufacturer_datas() const { return manufacturer_datas_; } + const std::vector &get_service_datas() const { return service_datas_; } + const std::vector &get_tx_powers() const { return tx_powers_; } + const optional &get_appearance() const { return appearance_; } + const optional &get_ad_flag() const { return ad_flag_; } + + /// Resolve a Resolvable Private Address against a 16-byte IRK (Bluetooth "ah" + /// function, AES-128). Uses the portable software AES shared with the CCM + /// decryptor; compiled only when a sensor configures irk: (request_irk_support). + bool resolve_irk(const uint8_t *irk) const; + + optional get_ibeacon() const { + for (const auto &it : this->manufacturer_datas_) { + auto res = ESPBLEiBeacon::from_manufacturer_data(it); + if (res.has_value()) + return res; + } + return {}; + } + + protected: + void parse_adv_(const uint8_t *payload, uint16_t len); + + uint8_t address_[6]{0}; + uint8_t address_type_{0}; + int rssi_{0}; + std::string name_{}; + std::vector service_uuids_{}; + std::vector manufacturer_datas_{}; + std::vector service_datas_{}; +#ifdef USE_ESP32 + const esp32_ble::BLEScanResult *scan_result_{nullptr}; +#endif + std::vector tx_powers_{}; + optional appearance_{}; + optional ad_flag_{}; +}; + +// --------------------------------------------------------------------------- +// DiscoveredDeviceLog — shared per-scan-period "Found device" DEBUG logger +// --------------------------------------------------------------------------- + +/// Per-scan-period "Found device" DEBUG logger, deduplicated by MAC address. +/// Shared by all tracker backends so the output format and dedup behaviour stay +/// identical by construction (single implementation instead of per-chip copies). +class DiscoveredDeviceLog { + public: + /// Log the device at DEBUG the first time its MAC is seen this scan period. + void log_device(const char *tag, const ESPBTDevice &device); + /// Reset the per-period dedup list (call when a scan period ends). + void clear() { this->already_discovered_.clear(); } + + protected: + std::vector already_discovered_; +}; + +// --------------------------------------------------------------------------- +// ESPBTDeviceListener — base class for BLE consumers (sensors, proxy, triggers) +// --------------------------------------------------------------------------- + +class ESPBTDeviceListener { + public: + virtual ~ESPBTDeviceListener() = default; + /// Called at the end of each scan duration period. + virtual void on_scan_end() {} + virtual bool parse_device(const ESPBTDevice &device) = 0; +}; + +} // namespace esphome::ble_device_base diff --git a/esphome/components/ble_device_base/ble_hub.h b/esphome/components/ble_device_base/ble_hub.h new file mode 100644 index 0000000000..c02b491237 --- /dev/null +++ b/esphome/components/ble_device_base/ble_hub.h @@ -0,0 +1,84 @@ +// ble_hub.h +// +// BLEHub — the platform-neutral BLE tracker contract. +// +// Every BLE tracker component (esp32_ble_tracker, bk72xx_ble_tracker, +// ln882h_ble_tracker, future chips) implements this interface; every BLE +// consumer (sensor components, bluetooth_proxy) binds to it — in YAML via +// `cv.use_id(BLEHub)`, which resolves whichever tracker the config declares. +// Adding a new BLE chip therefore requires only a new tracker component that +// implements BLEHub: no consumer, registry, or base changes. +// +// Chip differences are expressed as data (HubCapabilities), never as +// platform conditionals in consumers. + +#pragma once + +#include "ble_device.h" + +#include + +namespace esphome::ble_device_base { + +/// One raw advertisement as delivered by the controller — a borrowed view, +/// valid only for the duration of the invoke() callback. +struct RawAdvertisement { + /// Least-significant octet first (BLE controller convention). + const uint8_t *mac; + const uint8_t *data; + uint16_t data_len; + int8_t rssi; // signed dBm + uint8_t addr_type; +}; + +/// Subscriber slot for the raw-advertisement stream (the bluetooth_proxy +/// path). The hub delivers on the ESPHome main loop. Same shape as +/// logger.h's LogCallback: an instance pointer plus a plain function +/// pointer — no virtuals, no std::function. +/// +/// Usage: +/// hub->set_raw_advertisement_callback({this, [](void *self, const RawAdvertisement &adv) { +/// static_cast(self)->on_raw_advertisement(adv); +/// }}); +struct RawAdvertisementCallback { + void *instance{nullptr}; + void (*fn)(void *instance, const RawAdvertisement &adv){nullptr}; + /// A default-constructed slot is "no subscriber"; hubs must guard on this. + bool is_set() const { return this->fn != nullptr; } + void invoke(const RawAdvertisement &adv) const { this->fn(this->instance, adv); } +}; + +/// What a tracker's controller/SDK can do — consumers branch on data, not #ifdefs. +struct HubCapabilities { + /// Controller can send scan requests (active scanning). + bool active_scan; + /// Controller (or tracker) delivers advertisement + scan response as one merged + /// frame. When false, consumers relying on scan-response fields (e.g. names) + /// may only see them where the receiver merges per address (Home Assistant does). + bool merges_scan_response; + /// GATT client connections are available (today: esp32 only, but a chip SDK + /// gaining GATT support only has to flip this bit). + bool gatt; +}; + +class BLEHub { + public: + virtual ~BLEHub() = default; + + /// Register a parsed-advertisement consumer (BLE sensors, automation triggers). + virtual void register_listener(ESPBTDeviceListener *listener) = 0; + + /// Wire the raw-advertisement stream (bluetooth_proxy). One consumer at a time. + virtual void set_raw_advertisement_callback(RawAdvertisementCallback callback) = 0; + + virtual HubCapabilities get_capabilities() const = 0; + + /// Adapter MAC in printable (MSB-first) order, out[0] = MSB. + virtual void get_adapter_mac(uint8_t out[6]) = 0; + + virtual bool scan_running() = 0; + /// True when the current/configured scan mode is active (scan requests sent). + virtual bool scan_active() = 0; +}; + +} // namespace esphome::ble_device_base diff --git a/esphome/components/ble_scanner/ble_scanner.h b/esphome/components/ble_scanner/ble_scanner.h index c70ee637ef..106171d38f 100644 --- a/esphome/components/ble_scanner/ble_scanner.h +++ b/esphome/components/ble_scanner/ble_scanner.h @@ -5,6 +5,8 @@ #include #include "esphome/core/component.h" +#include "esphome/core/helpers.h" +#include "esphome/core/string_ref.h" #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/text_sensor/text_sensor.h" @@ -18,22 +20,10 @@ class BLEScanner final : public text_sensor::TextSensor, public: bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override { char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; - // Escape special characters in the device name for valid JSON - const char *name = device.get_name().c_str(); + // Escape special characters in the device name for valid JSON. Control characters stay in the \u00XX form this + // sensor has always published. char escaped_name[128]; - size_t pos = 0; - for (; *name != '\0' && pos < sizeof(escaped_name) - 7; name++) { - uint8_t c = static_cast(*name); - if (c == '"' || c == '\\') { - escaped_name[pos++] = '\\'; - escaped_name[pos++] = c; - } else if (c < 0x20) { - pos += snprintf(escaped_name + pos, sizeof(escaped_name) - pos, "\\u%04x", c); - } else { - escaped_name[pos++] = c; - } - } - escaped_name[pos] = '\0'; + json_escape_into_buffer(escaped_name, StringRef(device.get_name()), /*short_control_escapes=*/false); char buf[256]; snprintf(buf, sizeof(buf), "{\"timestamp\":%" PRId64 ",\"address\":\"%s\",\"rssi\":%d,\"name\":\"%s\"}", diff --git a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp index 7ba9e61e19..9820977a13 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_connection.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_connection.cpp @@ -245,7 +245,9 @@ void BluetoothConnection::send_service_for_discovery_() { service_resp.characteristics.init(total_char_count); uint16_t char_offset = 0; esp_gattc_char_elem_t char_result; - while (true) { // characteristics + // Bound by total_char_count: the vector is sized for it, and a malicious peripheral + // can make enumeration return more entries than the count query reported + while (char_offset < total_char_count) { // characteristics uint16_t char_count = 1; esp_gatt_status_t char_status = esp_ble_gattc_get_all_char(this->gattc_if_, this->conn_id_, service_result.start_handle, @@ -287,7 +289,7 @@ void BluetoothConnection::send_service_for_discovery_() { characteristic_resp.descriptors.init(total_desc_count); uint16_t desc_offset = 0; esp_gattc_descr_elem_t desc_result; - while (true) { // descriptors + while (desc_offset < total_desc_count) { // descriptors uint16_t desc_count = 1; esp_gatt_status_t desc_status = esp_ble_gattc_get_all_descr( this->gattc_if_, this->conn_id_, char_result.char_handle, &desc_result, &desc_count, desc_offset); diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 37ebcad8b4..f1a30cdfa2 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -247,7 +247,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest esp_bd_addr_t address; uint64_to_bd_addr(msg.address, address); esp_err_t ret = esp_ble_remove_bond_device(address); - this->send_device_pairing(msg.address, ret == ESP_OK, ret); + this->send_device_unpairing(msg.address, ret == ESP_OK, ret); break; } case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: { diff --git a/esphome/components/captive_portal/__init__.py b/esphome/components/captive_portal/__init__.py index 703ae98392..d62c718097 100644 --- a/esphome/components/captive_portal/__init__.py +++ b/esphome/components/captive_portal/__init__.py @@ -1,7 +1,7 @@ import logging import esphome.codegen as cg -from esphome.components import web_server_base +from esphome.components import web_server_base, wifi from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv @@ -101,6 +101,9 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID], paren) await cg.register_component(var, config) cg.add_define("USE_CAPTIVE_PORTAL") + # The portal reads wifi scan results from the web server task; this makes the + # wifi component guard them with a lock on multi-threaded platforms. + wifi.request_wifi_scan_results_lock() if config[CONF_COMPRESSION] == "gzip": cg.add_define("USE_CAPTIVE_PORTAL_GZIP") diff --git a/esphome/components/captive_portal/captive_index.h b/esphome/components/captive_portal/captive_index.h index a81edc1900..a25ac8d010 100644 --- a/esphome/components/captive_portal/captive_index.h +++ b/esphome/components/captive_portal/captive_index.h @@ -7,145 +7,146 @@ namespace esphome::captive_portal { #ifdef USE_CAPTIVE_PORTAL_GZIP constexpr uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x95, 0x16, 0x6b, 0x8f, 0xdb, 0x36, 0xf2, 0x7b, 0x7e, - 0x05, 0x8f, 0x49, 0xbb, 0x52, 0xb3, 0x7a, 0x7a, 0xed, 0x6c, 0x24, 0x51, 0x45, 0x9a, 0xbb, 0xa2, 0x05, 0x9a, 0x36, - 0xc0, 0x6e, 0x73, 0x1f, 0x82, 0x00, 0x4b, 0x53, 0x23, 0x8b, 0x31, 0x45, 0xea, 0x48, 0xca, 0x8f, 0x18, 0xbe, 0xdf, - 0x7e, 0xa0, 0x24, 0x7b, 0x9d, 0x45, 0x73, 0xb8, 0xb3, 0x60, 0x61, 0x38, 0xef, 0x19, 0xcd, 0x83, 0xc5, 0xdf, 0x2a, - 0xc5, 0xec, 0xbe, 0x03, 0xd4, 0xd8, 0x56, 0x94, 0x85, 0x7b, 0x23, 0x41, 0xe5, 0x8a, 0x80, 0x2c, 0x8b, 0x06, 0x68, - 0x55, 0x16, 0x2d, 0x58, 0x8a, 0x58, 0x43, 0xb5, 0x01, 0x4b, 0xfe, 0xbc, 0xff, 0x39, 0xb8, 0x2d, 0x0b, 0xc1, 0xe5, - 0x1a, 0x69, 0x10, 0x84, 0x33, 0x25, 0x51, 0xa3, 0xa1, 0x26, 0x15, 0xb5, 0x34, 0xe3, 0x2d, 0x5d, 0xc1, 0x24, 0x22, - 0x69, 0x0b, 0x64, 0xc3, 0x61, 0xdb, 0x29, 0x6d, 0x11, 0x53, 0xd2, 0x82, 0xb4, 0x04, 0x6f, 0x79, 0x65, 0x1b, 0x52, - 0xc1, 0x86, 0x33, 0x08, 0x86, 0xc3, 0x35, 0x97, 0xdc, 0x72, 0x2a, 0x02, 0xc3, 0xa8, 0x00, 0x92, 0x5c, 0xf7, 0x06, - 0xf4, 0x70, 0xa0, 0x4b, 0x01, 0x44, 0x2a, 0x5c, 0x16, 0x86, 0x69, 0xde, 0x59, 0xe4, 0x5c, 0x25, 0xad, 0xaa, 0x7a, - 0x01, 0x65, 0x14, 0x51, 0x63, 0xc0, 0x9a, 0x88, 0xcb, 0x0a, 0x76, 0xe1, 0x32, 0x66, 0x2c, 0x86, 0xdb, 0xdb, 0xf0, - 0xb3, 0x79, 0x56, 0x29, 0xd6, 0xb7, 0x20, 0x6d, 0x28, 0x14, 0xa3, 0x96, 0x2b, 0x19, 0x1a, 0xa0, 0x9a, 0x35, 0x84, - 0x10, 0xfc, 0xa3, 0xa1, 0x1b, 0xc0, 0xdf, 0x7f, 0xef, 0x9d, 0x99, 0x56, 0x60, 0xff, 0x21, 0xc0, 0x81, 0xe6, 0xa7, - 0xfd, 0x3d, 0x5d, 0xfd, 0x4e, 0x5b, 0xf0, 0x30, 0x35, 0xbc, 0x02, 0xec, 0x7f, 0x8c, 0x3f, 0x85, 0xc6, 0xee, 0x05, - 0x84, 0x15, 0x37, 0x9d, 0xa0, 0x7b, 0x82, 0x97, 0x42, 0xb1, 0x35, 0xf6, 0xf3, 0xba, 0x97, 0xcc, 0x29, 0x47, 0xc6, - 0x03, 0xff, 0x20, 0xc0, 0x22, 0x4b, 0xde, 0x51, 0xdb, 0x84, 0x2d, 0xdd, 0x79, 0x23, 0xc0, 0xa5, 0x97, 0xfe, 0xe0, - 0xc1, 0xcb, 0x24, 0x8e, 0xfd, 0xeb, 0xe1, 0x15, 0xfb, 0x51, 0x12, 0xc7, 0xb9, 0x06, 0xdb, 0x6b, 0x89, 0xa8, 0xf7, - 0x50, 0x74, 0xd4, 0x36, 0xa8, 0x22, 0xf8, 0x5d, 0x92, 0xa2, 0xe4, 0x75, 0x98, 0xce, 0x7f, 0x0b, 0x5f, 0xa1, 0x9b, - 0x30, 0x9d, 0xb3, 0x57, 0xc1, 0x1c, 0x25, 0x37, 0xc1, 0x1c, 0xa5, 0x69, 0x38, 0x47, 0xf1, 0x17, 0x8c, 0x6a, 0x2e, - 0x04, 0xc1, 0x52, 0x49, 0xc0, 0xc8, 0x58, 0xad, 0xd6, 0x40, 0x30, 0xeb, 0xb5, 0x06, 0x69, 0xdf, 0x2a, 0xa1, 0x34, - 0x8e, 0xca, 0x67, 0xff, 0x97, 0x42, 0xab, 0xa9, 0x34, 0xb5, 0xd2, 0x2d, 0xc1, 0x43, 0xf6, 0xbd, 0x17, 0x07, 0x7b, - 0x44, 0xee, 0xe5, 0x5f, 0x10, 0x03, 0xa5, 0xf9, 0x8a, 0x4b, 0x82, 0x9d, 0xc6, 0x5b, 0x1c, 0x95, 0x0f, 0xfe, 0xf1, - 0x1c, 0x3d, 0x75, 0xd1, 0x4f, 0xf1, 0x28, 0xef, 0xe3, 0x43, 0x61, 0x36, 0x2b, 0xb4, 0x6b, 0x85, 0x34, 0x04, 0x37, - 0xd6, 0x76, 0x59, 0x14, 0x6d, 0xb7, 0xdb, 0x70, 0x3b, 0x0b, 0x95, 0x5e, 0x45, 0x69, 0x1c, 0xc7, 0x91, 0xd9, 0xac, - 0x30, 0x1a, 0x0b, 0x01, 0xa7, 0x37, 0x18, 0x35, 0xc0, 0x57, 0x8d, 0x1d, 0xe0, 0xf2, 0xc5, 0x01, 0x8e, 0x85, 0xe3, - 0x28, 0x1f, 0x3e, 0x5d, 0x58, 0xe1, 0x17, 0x56, 0xe0, 0x47, 0xea, 0xe1, 0x53, 0x98, 0x57, 0x43, 0x98, 0xaf, 0x68, - 0x8a, 0x52, 0x14, 0x0f, 0x4f, 0x1a, 0x38, 0x78, 0x3a, 0x05, 0x4f, 0x4e, 0xe8, 0xe2, 0xe4, 0xa0, 0x76, 0x11, 0xbc, - 0x3e, 0xcb, 0x26, 0x0e, 0xb3, 0x49, 0xe2, 0x47, 0x84, 0x13, 0xf8, 0x65, 0x71, 0x79, 0x0e, 0xd2, 0x0f, 0x97, 0x0c, - 0xce, 0x5a, 0x93, 0x7c, 0x58, 0xd0, 0x39, 0x9a, 0x4f, 0x98, 0x79, 0xe0, 0xe0, 0xf3, 0x09, 0xcd, 0x37, 0x69, 0x93, - 0xb4, 0xc1, 0x22, 0x98, 0xd3, 0x19, 0x9a, 0x4d, 0x8e, 0xcc, 0xd0, 0x6c, 0x93, 0x36, 0x8b, 0x0f, 0x8b, 0x4b, 0x5c, - 0x30, 0xfb, 0x72, 0x15, 0x95, 0xd8, 0xcf, 0x30, 0x7e, 0x8c, 0x5c, 0x5d, 0x46, 0x1e, 0x7e, 0x56, 0x5c, 0x7a, 0x18, - 0xfb, 0xc7, 0x1a, 0x2c, 0x6b, 0x3c, 0x1c, 0x31, 0x25, 0x6b, 0xbe, 0x0a, 0x3f, 0x1b, 0x25, 0xb1, 0x1f, 0xda, 0x06, - 0xa4, 0x77, 0x12, 0x75, 0x82, 0x30, 0x50, 0xbc, 0xa7, 0x14, 0xeb, 0x1f, 0xce, 0xf5, 0x6f, 0xb9, 0x15, 0x40, 0x6c, - 0xe8, 0x1a, 0xf6, 0xfa, 0x8c, 0x5d, 0xaa, 0x6a, 0xff, 0x8d, 0xd6, 0x68, 0x92, 0xb1, 0x2f, 0xb8, 0x94, 0xa0, 0xef, - 0x61, 0x67, 0x09, 0x7e, 0xf7, 0xe6, 0x2d, 0x7a, 0x53, 0x55, 0x1a, 0x8c, 0xc9, 0x10, 0x7e, 0x69, 0xc3, 0x96, 0xb2, - 0xff, 0x5d, 0x57, 0xf2, 0x95, 0xae, 0x7f, 0xf2, 0x9f, 0x39, 0xfa, 0x1d, 0xec, 0x56, 0xe9, 0xf5, 0xa4, 0xcd, 0xb9, - 0x96, 0xbb, 0x0e, 0xd3, 0xc4, 0x86, 0xb4, 0x33, 0xa1, 0x11, 0x9c, 0x81, 0x97, 0xf8, 0x61, 0x4b, 0xbb, 0xc7, 0xa8, - 0xe4, 0x29, 0x51, 0x0f, 0x45, 0xc5, 0x37, 0x88, 0x09, 0x6a, 0x0c, 0xc1, 0x72, 0x54, 0x85, 0xd1, 0x33, 0x34, 0xfc, - 0x94, 0x64, 0x82, 0xb3, 0x35, 0xc1, 0x7f, 0x31, 0x01, 0x7e, 0xda, 0xff, 0x5a, 0x79, 0x57, 0xc6, 0xf0, 0xea, 0xca, - 0x0f, 0x37, 0x54, 0xf4, 0x80, 0x08, 0xb2, 0x0d, 0x37, 0x8f, 0x0e, 0xe6, 0xdf, 0x14, 0xeb, 0xcc, 0xfa, 0xca, 0x0f, - 0x6b, 0xc5, 0x7a, 0xe3, 0xf9, 0xb8, 0x9c, 0xcc, 0x15, 0x74, 0x1c, 0x90, 0xf8, 0x39, 0x7e, 0xe2, 0x51, 0x20, 0xa0, - 0xb6, 0x67, 0x3e, 0x84, 0x5e, 0x1c, 0x8c, 0x27, 0x43, 0x6d, 0x0c, 0xf7, 0x8f, 0x67, 0x64, 0x61, 0x3a, 0x2a, 0x9f, - 0x0a, 0x3a, 0x07, 0x5d, 0xab, 0xc8, 0xd0, 0x41, 0xae, 0x5f, 0x3a, 0x2a, 0xcf, 0x06, 0x23, 0x7a, 0x02, 0x5f, 0x1c, - 0xb8, 0x27, 0xdd, 0x14, 0x5c, 0x9f, 0x35, 0x16, 0x51, 0xc5, 0x37, 0xe5, 0xc3, 0xd1, 0x7f, 0x8c, 0xe3, 0x5f, 0x3d, - 0xe8, 0xfd, 0x1d, 0x08, 0x60, 0x56, 0x69, 0x0f, 0x3f, 0x97, 0x60, 0xb1, 0x3f, 0x06, 0xfc, 0xcb, 0xfd, 0xbb, 0xdf, - 0x88, 0xf2, 0xb4, 0x7f, 0xfd, 0x2d, 0x6e, 0xb7, 0x0a, 0x3e, 0x6a, 0x10, 0xff, 0x26, 0x57, 0x6e, 0x19, 0x5c, 0x7d, - 0xc2, 0x7e, 0x38, 0xc4, 0xfb, 0xf0, 0xb8, 0x11, 0x5c, 0x3b, 0xbf, 0xdc, 0xb5, 0xe2, 0xda, 0x45, 0x18, 0x2c, 0xe6, - 0xfe, 0xf1, 0xe1, 0xe8, 0x1f, 0xfd, 0xbc, 0x88, 0xc6, 0xb9, 0x5e, 0x16, 0xc3, 0x88, 0x2d, 0x7f, 0x38, 0x2c, 0xd5, - 0x2e, 0x30, 0xfc, 0x0b, 0x97, 0xab, 0x8c, 0xcb, 0x06, 0x34, 0xb7, 0xc7, 0x8a, 0x6f, 0xae, 0xb9, 0xec, 0x7a, 0x7b, - 0xe8, 0x68, 0x55, 0x39, 0xca, 0xbc, 0xdb, 0xe5, 0xb5, 0x92, 0xd6, 0x71, 0x42, 0x96, 0x40, 0x7b, 0x1c, 0xe9, 0xc3, - 0x44, 0xc9, 0x5e, 0xcf, 0xbf, 0x3b, 0xba, 0x82, 0x3b, 0x58, 0xd8, 0xd9, 0x80, 0x0a, 0xbe, 0x92, 0x19, 0x03, 0x69, - 0x41, 0x8f, 0x42, 0x35, 0x6d, 0xb9, 0xd8, 0x67, 0x86, 0x4a, 0x13, 0x18, 0xd0, 0xbc, 0x3e, 0x2e, 0x7b, 0x6b, 0x95, - 0x3c, 0x2c, 0x95, 0xae, 0x40, 0x67, 0x71, 0x3e, 0x02, 0x81, 0xa6, 0x15, 0xef, 0x4d, 0x16, 0xce, 0x34, 0xb4, 0xf9, - 0x92, 0xb2, 0xf5, 0x4a, 0xab, 0x5e, 0x56, 0x01, 0x73, 0x93, 0x36, 0x7b, 0x9e, 0xd4, 0x74, 0x06, 0x2c, 0x9f, 0x4e, - 0x75, 0x5d, 0xe7, 0x82, 0x4b, 0x08, 0xc6, 0x59, 0x96, 0xa5, 0xe1, 0x8d, 0x13, 0xbb, 0x70, 0x33, 0x4c, 0x1d, 0x62, - 0xf4, 0x31, 0x89, 0xe3, 0xef, 0xf2, 0x53, 0x38, 0x71, 0xce, 0x7a, 0x6d, 0x94, 0xce, 0x3a, 0xc5, 0x9d, 0x9b, 0xc7, - 0x96, 0x72, 0x79, 0xe9, 0xbd, 0x2b, 0x93, 0x7c, 0x5a, 0x3f, 0x19, 0x97, 0x83, 0x99, 0x61, 0x09, 0xe5, 0x2d, 0x97, - 0xe3, 0x0e, 0xcd, 0xd2, 0x45, 0xdc, 0xed, 0x8e, 0xe1, 0x54, 0x20, 0x87, 0x13, 0x77, 0x2d, 0x60, 0x97, 0x7f, 0xee, - 0x8d, 0xe5, 0xf5, 0x3e, 0x98, 0x76, 0x70, 0x66, 0x3a, 0xca, 0x20, 0x58, 0x82, 0xdd, 0x02, 0xc8, 0x7c, 0xb0, 0x11, - 0x70, 0x0b, 0xad, 0x99, 0xf2, 0x74, 0x56, 0x33, 0x14, 0xe8, 0xd7, 0xba, 0xfe, 0x1b, 0xb7, 0xab, 0xc5, 0x43, 0x4b, - 0xf5, 0x8a, 0xcb, 0x60, 0xa9, 0xac, 0x55, 0x6d, 0x16, 0xbc, 0xea, 0x76, 0xf9, 0x84, 0x72, 0xca, 0xb2, 0xc4, 0xb9, - 0x39, 0xec, 0xd6, 0x53, 0xbe, 0x93, 0x6e, 0x87, 0x8c, 0x12, 0xbc, 0x9a, 0xf8, 0x06, 0x16, 0x14, 0x9f, 0xd3, 0x93, - 0xcc, 0xbb, 0x1d, 0x72, 0xb8, 0x53, 0xaa, 0x6f, 0xea, 0x5b, 0x9a, 0xc4, 0x7f, 0xf1, 0x45, 0xaa, 0xba, 0x4e, 0x97, - 0xf5, 0x39, 0x53, 0x6e, 0x4d, 0xba, 0xd6, 0x18, 0x4a, 0xab, 0x88, 0xc6, 0xdb, 0x8c, 0xab, 0x8c, 0xb2, 0x70, 0x19, - 0x2e, 0x8b, 0x26, 0x41, 0xbc, 0x22, 0x2d, 0x65, 0xe5, 0xc5, 0xf8, 0x2a, 0xa2, 0x26, 0x39, 0x91, 0x9a, 0xa4, 0xfc, - 0x6a, 0x18, 0x8d, 0xb4, 0xc1, 0xfb, 0xf2, 0xad, 0x92, 0x12, 0x98, 0xe5, 0x72, 0x85, 0xac, 0x42, 0x53, 0x0a, 0xc2, - 0x30, 0x2c, 0x96, 0xba, 0x7c, 0x2f, 0x80, 0x1a, 0x40, 0x5b, 0xca, 0x6d, 0x58, 0x44, 0x23, 0xff, 0xd8, 0xc7, 0xbc, - 0x22, 0x12, 0x6c, 0x39, 0x35, 0x6c, 0xd1, 0xcc, 0x46, 0x03, 0x77, 0x60, 0x9d, 0x26, 0x67, 0x60, 0x56, 0x16, 0x6e, - 0xe5, 0x22, 0x3a, 0x8c, 0x34, 0x12, 0x6d, 0x79, 0xcd, 0xdd, 0x95, 0xa5, 0x2c, 0x86, 0x22, 0x77, 0x1a, 0x5c, 0x9e, - 0xc7, 0xeb, 0xd5, 0x00, 0x09, 0x90, 0x2b, 0xdb, 0x90, 0x59, 0x8a, 0x3a, 0x41, 0x19, 0x34, 0x4a, 0x54, 0xa0, 0xc9, - 0xdd, 0xdd, 0xaf, 0x7f, 0x2f, 0x9d, 0x33, 0x8f, 0x72, 0x9d, 0x59, 0x8f, 0x62, 0x0e, 0x98, 0xa4, 0x16, 0x37, 0xe3, - 0xa5, 0xaa, 0xa3, 0xc6, 0x6c, 0x95, 0xae, 0xbe, 0xd2, 0xf1, 0x7e, 0x42, 0x8e, 0x7a, 0x86, 0xff, 0xd0, 0x2a, 0xe5, - 0x1d, 0xdd, 0x40, 0x11, 0x4d, 0x87, 0x22, 0x72, 0x0e, 0x8f, 0xf4, 0x66, 0xe2, 0x6b, 0x92, 0xf2, 0x8f, 0xfb, 0x37, - 0xe8, 0xcf, 0xae, 0xa2, 0x16, 0xc6, 0xb4, 0x0d, 0x51, 0xb5, 0x60, 0x1b, 0x55, 0x91, 0xf7, 0x7f, 0xdc, 0xdd, 0x9f, - 0x23, 0xec, 0x07, 0x26, 0x04, 0x92, 0x8d, 0xd7, 0xbb, 0x5e, 0x58, 0xde, 0x51, 0x6d, 0x07, 0xb5, 0x81, 0x9b, 0x22, - 0xa7, 0x18, 0x06, 0x7a, 0xcd, 0x05, 0x8c, 0x61, 0x8c, 0x82, 0x25, 0x3a, 0x79, 0x75, 0xb2, 0xf6, 0xc4, 0xaf, 0x68, - 0xfc, 0xda, 0xd1, 0xf8, 0xe9, 0xa3, 0xe1, 0xa6, 0xfb, 0x1f, 0x53, 0x58, 0x46, 0xb2, 0xf9, 0x0a, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x95, 0x16, 0x6b, 0x8f, 0xdb, 0x36, 0xf2, 0x7b, 0x7f, + 0x05, 0x8f, 0x4d, 0x1b, 0xa9, 0xb1, 0xa8, 0x87, 0xd7, 0xde, 0x44, 0x96, 0x54, 0xa4, 0x7b, 0x2d, 0x5a, 0xa0, 0x69, + 0x03, 0xec, 0x36, 0xf7, 0x21, 0x08, 0xb0, 0x34, 0x39, 0xb2, 0x98, 0xa5, 0x48, 0x1d, 0x49, 0xbf, 0x62, 0xf8, 0x7e, + 0xfb, 0x81, 0x92, 0xec, 0xf5, 0x2e, 0x9a, 0x03, 0x0e, 0x86, 0x85, 0x19, 0xce, 0x7b, 0x38, 0x0f, 0x16, 0xff, 0xe0, + 0x9a, 0xb9, 0x7d, 0x07, 0xa8, 0x71, 0xad, 0xac, 0x0a, 0xff, 0x45, 0x92, 0xaa, 0x55, 0x09, 0xaa, 0x2a, 0x1a, 0xa0, + 0xbc, 0x2a, 0x5a, 0x70, 0x14, 0xb1, 0x86, 0x1a, 0x0b, 0xae, 0xfc, 0xeb, 0xee, 0x97, 0xe8, 0x75, 0x55, 0x48, 0xa1, + 0x1e, 0x90, 0x01, 0x59, 0x0a, 0xa6, 0x15, 0x6a, 0x0c, 0xd4, 0x25, 0xa7, 0x8e, 0xe6, 0xa2, 0xa5, 0x2b, 0x18, 0x45, + 0x14, 0x6d, 0xa1, 0xdc, 0x08, 0xd8, 0x76, 0xda, 0x38, 0xc4, 0xb4, 0x72, 0xa0, 0x5c, 0x89, 0xb7, 0x82, 0xbb, 0xa6, + 0xe4, 0xb0, 0x11, 0x0c, 0xa2, 0x1e, 0x99, 0x08, 0x25, 0x9c, 0xa0, 0x32, 0xb2, 0x8c, 0x4a, 0x28, 0xd3, 0xc9, 0xda, + 0x82, 0xe9, 0x11, 0xba, 0x94, 0x50, 0x2a, 0x8d, 0xab, 0xc2, 0x32, 0x23, 0x3a, 0x87, 0xbc, 0xab, 0x65, 0xab, 0xf9, + 0x5a, 0x42, 0x15, 0xc7, 0xd4, 0x5a, 0x70, 0x36, 0x16, 0x8a, 0xc3, 0x8e, 0xd0, 0x6b, 0xb8, 0xa6, 0x2c, 0x4d, 0xc8, + 0x67, 0xfb, 0x0d, 0xd7, 0x6c, 0xdd, 0x82, 0x72, 0x44, 0x6a, 0x46, 0x9d, 0xd0, 0x8a, 0x58, 0xa0, 0x86, 0x35, 0x65, + 0x59, 0xe2, 0x1f, 0x2d, 0xdd, 0x00, 0xfe, 0xfe, 0xfb, 0xe0, 0xcc, 0xb4, 0x02, 0xf7, 0xb3, 0x04, 0x0f, 0xda, 0x9f, + 0xf6, 0x77, 0x74, 0xf5, 0x07, 0x6d, 0x21, 0xc0, 0xd4, 0x0a, 0x0e, 0x38, 0xfc, 0x98, 0x7c, 0x22, 0xd6, 0xed, 0x25, + 0x10, 0x2e, 0x6c, 0x27, 0xe9, 0xbe, 0xc4, 0x4b, 0xa9, 0xd9, 0x03, 0x0e, 0x17, 0xf5, 0x5a, 0x31, 0xaf, 0x1c, 0xe9, + 0x00, 0xc2, 0x83, 0x04, 0x87, 0x5c, 0xf9, 0x8e, 0xba, 0x86, 0xb4, 0x74, 0x17, 0x0c, 0x80, 0x50, 0x41, 0xf6, 0x43, + 0x00, 0xaf, 0xd2, 0x24, 0x09, 0x27, 0xfd, 0x27, 0x09, 0xe3, 0x34, 0x49, 0x16, 0x06, 0xdc, 0xda, 0x28, 0x44, 0x83, + 0xfb, 0xa2, 0xa3, 0xae, 0x41, 0xbc, 0xc4, 0xef, 0xd2, 0x0c, 0xa5, 0x6f, 0x48, 0x36, 0xfb, 0x9d, 0x5c, 0xa3, 0x2b, + 0x92, 0xcd, 0xd8, 0x75, 0x34, 0x43, 0xe9, 0x55, 0x34, 0x43, 0x59, 0x46, 0x66, 0x28, 0xf9, 0x82, 0x51, 0x2d, 0xa4, + 0x2c, 0xb1, 0xd2, 0x0a, 0x30, 0xb2, 0xce, 0xe8, 0x07, 0x28, 0x31, 0x5b, 0x1b, 0x03, 0xca, 0xdd, 0x68, 0xa9, 0x0d, + 0x8e, 0xab, 0x6f, 0xfe, 0x2f, 0x85, 0xce, 0x50, 0x65, 0x6b, 0x6d, 0xda, 0x12, 0xf7, 0xd9, 0x0f, 0x5e, 0x1c, 0xdc, + 0x11, 0xf9, 0x4f, 0x78, 0x41, 0x8c, 0xb4, 0x11, 0x2b, 0xa1, 0x4a, 0xec, 0x35, 0xbe, 0xc6, 0x71, 0x75, 0x1f, 0x1e, + 0xcf, 0xd1, 0x53, 0x1f, 0xfd, 0x18, 0x0f, 0x0f, 0x3e, 0xde, 0x17, 0x76, 0xb3, 0x42, 0xbb, 0x56, 0x2a, 0x5b, 0xe2, + 0xc6, 0xb9, 0x2e, 0x8f, 0xe3, 0xed, 0x76, 0x4b, 0xb6, 0x53, 0xa2, 0xcd, 0x2a, 0xce, 0x92, 0x24, 0x89, 0xed, 0x66, + 0x85, 0xd1, 0x50, 0x08, 0x38, 0xbb, 0xc2, 0xa8, 0x01, 0xb1, 0x6a, 0x5c, 0x0f, 0x57, 0x2f, 0x0e, 0x70, 0x2c, 0x3c, + 0x47, 0x75, 0xff, 0xe9, 0xc2, 0x8a, 0xb9, 0xb0, 0x02, 0x3f, 0xd2, 0x00, 0x9f, 0xc2, 0x7c, 0xd9, 0x87, 0x79, 0x4d, + 0x33, 0x94, 0xa1, 0xa4, 0xff, 0x65, 0x91, 0x87, 0x47, 0x2c, 0x7a, 0x86, 0xa1, 0x0b, 0xcc, 0x43, 0xed, 0x3c, 0x7a, + 0x73, 0x96, 0x4d, 0xfd, 0xc9, 0x26, 0x4d, 0x1e, 0x0f, 0xbc, 0xc0, 0xaf, 0xf3, 0x4b, 0x3c, 0xca, 0x3e, 0x5c, 0x32, + 0x78, 0x6b, 0x4d, 0xfa, 0x61, 0x4e, 0x67, 0x68, 0x36, 0x9e, 0xcc, 0x22, 0x0f, 0x9f, 0x31, 0x34, 0xdb, 0x64, 0x4d, + 0xda, 0x46, 0xf3, 0x68, 0x46, 0xa7, 0x68, 0x3a, 0x3a, 0x32, 0x45, 0xd3, 0x4d, 0xd6, 0xcc, 0x3f, 0xcc, 0x2f, 0xcf, + 0xa2, 0xe9, 0x97, 0x97, 0x71, 0x85, 0xc3, 0x1c, 0xe3, 0xc7, 0xc8, 0xf9, 0x65, 0xe4, 0xe4, 0xb3, 0x16, 0x2a, 0xc0, + 0x38, 0x3c, 0xd6, 0xe0, 0x58, 0x13, 0xe0, 0x98, 0x69, 0x55, 0x8b, 0x15, 0xf9, 0x6c, 0xb5, 0xc2, 0x21, 0x71, 0x0d, + 0xa8, 0xe0, 0x24, 0xea, 0x05, 0xa1, 0xa7, 0x04, 0xcf, 0x29, 0x2e, 0x3c, 0x9c, 0xeb, 0xdf, 0x09, 0x27, 0xa1, 0x74, + 0xc4, 0x37, 0xec, 0xe4, 0x6f, 0xba, 0xe2, 0xa7, 0xfd, 0x6f, 0x3c, 0xc0, 0x2d, 0x65, 0x38, 0x24, 0x42, 0x29, 0x30, + 0x77, 0xb0, 0x73, 0x25, 0x7e, 0xf7, 0xf6, 0x06, 0xbd, 0xe5, 0xdc, 0x80, 0xb5, 0x39, 0xc2, 0xaf, 0x1c, 0x69, 0x29, + 0xfb, 0xba, 0x78, 0x93, 0x3e, 0x95, 0xfe, 0x97, 0xf8, 0x45, 0xa0, 0x3f, 0xc0, 0x6d, 0xb5, 0x79, 0x18, 0xe5, 0xbd, + 0xfd, 0x85, 0x6f, 0x23, 0x56, 0x7e, 0x55, 0x8d, 0x02, 0x87, 0xc3, 0x89, 0xf8, 0x3a, 0x83, 0xb5, 0x82, 0xe3, 0x70, + 0x22, 0xbf, 0xce, 0xd1, 0x59, 0xdf, 0xbc, 0x8e, 0xd0, 0xce, 0x12, 0x2b, 0x05, 0x83, 0x20, 0x0d, 0x49, 0xad, 0xcd, + 0xcf, 0x94, 0x35, 0x8f, 0x09, 0xb2, 0x43, 0x47, 0xab, 0x47, 0x3d, 0xcc, 0x00, 0x75, 0x30, 0xaa, 0x0a, 0x30, 0x17, + 0x1b, 0x1c, 0x2e, 0x14, 0x61, 0x92, 0x5a, 0xeb, 0x47, 0x46, 0xe9, 0x9d, 0xf3, 0xe1, 0xe0, 0x89, 0x1a, 0x22, 0xfd, + 0xf5, 0xee, 0xdd, 0xef, 0xe5, 0x7d, 0x41, 0x87, 0x01, 0x89, 0xbf, 0xc5, 0xa8, 0x67, 0x3e, 0x33, 0x46, 0x12, 0x6a, + 0xe7, 0x2b, 0x5e, 0x07, 0x96, 0x18, 0x6b, 0x45, 0x78, 0x2c, 0x6c, 0x47, 0xd5, 0x73, 0xb6, 0x3e, 0xa6, 0xaa, 0x88, + 0x3d, 0xad, 0x2a, 0x62, 0x5a, 0xbd, 0x38, 0x98, 0xc0, 0xfa, 0xe1, 0xf6, 0x10, 0x1e, 0xef, 0x27, 0x8a, 0xfc, 0x7b, + 0x0d, 0x66, 0x7f, 0x0b, 0x12, 0x98, 0xd3, 0x26, 0xc0, 0xe4, 0x89, 0x60, 0x48, 0x1c, 0xec, 0xdc, 0xcd, 0x38, 0x7f, + 0x2d, 0xf1, 0x87, 0x13, 0x45, 0xb4, 0x62, 0x52, 0xb0, 0x87, 0xf2, 0x1c, 0x71, 0x78, 0x10, 0x64, 0x43, 0xe5, 0x1a, + 0x4e, 0x3c, 0x92, 0xd4, 0x9a, 0xad, 0x6d, 0x10, 0x1e, 0x27, 0x8c, 0xd0, 0xae, 0x03, 0xc5, 0x6f, 0x1a, 0x21, 0x79, + 0xa0, 0xc2, 0x63, 0xf8, 0x78, 0xd3, 0xcf, 0x8c, 0xfb, 0xd5, 0xf0, 0xd1, 0x80, 0xfc, 0x4f, 0xf9, 0xd2, 0x2f, 0x87, + 0x97, 0x9f, 0x70, 0x48, 0xfa, 0xf8, 0xef, 0x1f, 0x37, 0x84, 0x6f, 0xef, 0x57, 0xbb, 0x56, 0x4e, 0x7c, 0xe8, 0xd1, + 0x7c, 0x16, 0x1e, 0xef, 0x8f, 0xe1, 0x31, 0x5c, 0x14, 0xf1, 0x30, 0xe7, 0xab, 0xa2, 0x1f, 0xb9, 0xd5, 0x0f, 0x87, + 0xa5, 0xde, 0x45, 0x56, 0x7c, 0x11, 0x6a, 0x95, 0x0b, 0xd5, 0x80, 0x11, 0xee, 0xc8, 0xc5, 0x66, 0x22, 0x54, 0xb7, + 0x76, 0x87, 0x8e, 0x72, 0xee, 0x29, 0xb3, 0x6e, 0xb7, 0xa8, 0xb5, 0x72, 0x9e, 0x13, 0xf2, 0x14, 0xda, 0xe3, 0x40, + 0xef, 0x27, 0x4c, 0xfe, 0x66, 0xf6, 0xdd, 0x71, 0xa9, 0xf9, 0xfe, 0xe0, 0xd3, 0x10, 0x51, 0x29, 0x56, 0x2a, 0x67, + 0xa0, 0x1c, 0x98, 0x41, 0xa8, 0xa6, 0xad, 0x90, 0xfb, 0xdc, 0x52, 0x65, 0x23, 0x0b, 0x46, 0xd4, 0xc7, 0xe5, 0xda, + 0x39, 0xad, 0x0e, 0x4b, 0x6d, 0x38, 0x98, 0x3c, 0x59, 0x0c, 0x40, 0x64, 0x28, 0x17, 0x6b, 0x9b, 0x93, 0xa9, 0x81, + 0x76, 0xb1, 0xa4, 0xec, 0x61, 0x65, 0xf4, 0x5a, 0xf1, 0x88, 0xf9, 0xc9, 0x9b, 0x7f, 0x9b, 0xd6, 0x74, 0x0a, 0x6c, + 0x31, 0x62, 0x75, 0x5d, 0x2f, 0xa4, 0x50, 0x10, 0x0d, 0xb3, 0x2d, 0xcf, 0xc8, 0x95, 0x17, 0xbb, 0x70, 0x93, 0x64, + 0xfe, 0x60, 0xf0, 0x31, 0x4d, 0x92, 0xef, 0x16, 0xa7, 0x70, 0x92, 0x05, 0x5b, 0x1b, 0xab, 0x4d, 0xde, 0x69, 0xe1, + 0xdd, 0x3c, 0xb6, 0x54, 0xa8, 0x4b, 0xef, 0x7d, 0xd9, 0x2c, 0xc6, 0x75, 0x94, 0x0b, 0xd5, 0x9b, 0xe9, 0x97, 0xd2, + 0xa2, 0x15, 0x6a, 0xd8, 0xa9, 0x79, 0x36, 0x4f, 0xba, 0xdd, 0xf1, 0x54, 0x09, 0x87, 0x13, 0x77, 0x2d, 0x61, 0xb7, + 0xf8, 0xbc, 0xb6, 0x4e, 0xd4, 0xfb, 0x68, 0xdc, 0xc9, 0xb9, 0xed, 0x28, 0x83, 0x68, 0x09, 0x6e, 0x0b, 0xa0, 0x16, + 0xbd, 0x8d, 0x48, 0x38, 0x68, 0xed, 0x98, 0xa7, 0xb3, 0x9a, 0xbe, 0x60, 0x9f, 0xea, 0xfa, 0x5f, 0xdc, 0xbe, 0x8a, + 0x0e, 0x2d, 0x35, 0x2b, 0xa1, 0xa2, 0xa5, 0x76, 0x4e, 0xb7, 0x79, 0x74, 0xdd, 0xed, 0x16, 0xe3, 0x91, 0x57, 0x96, + 0xa7, 0xde, 0xcd, 0x7e, 0xd7, 0x9e, 0xf2, 0x9d, 0x76, 0x3b, 0x64, 0xb5, 0x14, 0x7c, 0xe4, 0xeb, 0x59, 0x50, 0x72, + 0x4e, 0x4f, 0x3a, 0xeb, 0x76, 0xc8, 0x9f, 0x9d, 0x52, 0x7d, 0x55, 0xbf, 0xa6, 0x69, 0xf2, 0x37, 0x37, 0xc2, 0xeb, + 0x3a, 0x5b, 0xd6, 0xe7, 0x4c, 0xf9, 0xb5, 0xe9, 0x57, 0x4b, 0x5f, 0x5a, 0x45, 0x3c, 0xbc, 0x6e, 0x7c, 0x65, 0x54, + 0x85, 0xcf, 0x70, 0x55, 0x34, 0x29, 0x12, 0xbc, 0x6c, 0x29, 0xab, 0x2e, 0x66, 0x5b, 0x11, 0x37, 0xe9, 0x89, 0xd4, + 0xa4, 0xd5, 0x93, 0xb9, 0x35, 0xd0, 0x7a, 0xef, 0xab, 0x1b, 0xad, 0x14, 0x30, 0x27, 0xd4, 0x0a, 0x39, 0x8d, 0xc6, + 0x14, 0x10, 0x42, 0x8a, 0xa5, 0xa9, 0xde, 0x4b, 0xa0, 0x16, 0xd0, 0x96, 0x0a, 0x47, 0x8a, 0x78, 0xe0, 0x1f, 0x3a, + 0x5d, 0xf0, 0x52, 0x81, 0x3b, 0xf7, 0x76, 0x33, 0x1d, 0x0c, 0xdc, 0x82, 0xf3, 0x9a, 0xbc, 0x81, 0x69, 0x55, 0xf8, + 0x15, 0x8c, 0x68, 0xdf, 0xa5, 0x65, 0xbc, 0x15, 0xb5, 0xf0, 0x4f, 0x98, 0xaa, 0xe8, 0x8b, 0xdc, 0x6b, 0xf0, 0x79, + 0x1e, 0x9e, 0x5b, 0x3d, 0x24, 0x41, 0xad, 0x5c, 0x53, 0x4e, 0x33, 0xd4, 0x49, 0xca, 0xa0, 0xd1, 0x92, 0x83, 0x29, + 0x6f, 0x6f, 0x7f, 0xfb, 0x67, 0xe5, 0x9d, 0x79, 0x94, 0xeb, 0xec, 0xc3, 0x20, 0xe6, 0x81, 0x51, 0x6a, 0x7e, 0x35, + 0x3c, 0xb2, 0x3a, 0x6a, 0xed, 0x56, 0x1b, 0xfe, 0x44, 0xc7, 0xfb, 0xf1, 0x70, 0xd0, 0xd3, 0xff, 0xfb, 0x56, 0xa9, + 0x6e, 0xe9, 0x06, 0x8a, 0x78, 0x44, 0x8a, 0xd8, 0x3b, 0x3c, 0xd0, 0x9b, 0x91, 0xaf, 0x49, 0xab, 0x3f, 0xef, 0xde, + 0xa2, 0xbf, 0x3a, 0x4e, 0x1d, 0x0c, 0x69, 0xeb, 0xa3, 0x6a, 0xc1, 0x35, 0x9a, 0x97, 0xef, 0xff, 0xbc, 0xbd, 0x3b, + 0x47, 0xb8, 0xee, 0x99, 0x10, 0x28, 0x36, 0x3c, 0xf7, 0xd6, 0xd2, 0x89, 0x8e, 0x1a, 0xd7, 0xab, 0x8d, 0xfc, 0x14, + 0x39, 0xc5, 0xd0, 0xd3, 0x6b, 0x21, 0x61, 0x08, 0x63, 0x10, 0xac, 0xd0, 0xc9, 0xab, 0x93, 0xb5, 0x67, 0x7e, 0xc5, + 0xc3, 0x6d, 0xc7, 0xc3, 0xd5, 0xc7, 0xfd, 0xcb, 0xf7, 0xbf, 0x81, 0xdb, 0x13, 0xb5, 0x09, 0x0b, 0x00, 0x00}; #else // Brotli (default, smaller) constexpr uint8_t INDEX_BR[] PROGMEM = { - 0x1b, 0xf8, 0x0a, 0x00, 0x64, 0x5a, 0xd3, 0xfa, 0xe7, 0xf3, 0x62, 0xd8, 0x06, 0x1b, 0xe9, 0x6a, 0x8a, 0x81, 0x2b, - 0xb5, 0x49, 0x14, 0x37, 0xdc, 0x9e, 0x1a, 0xcb, 0x56, 0x87, 0xfb, 0xff, 0xf7, 0x73, 0x75, 0x12, 0x0a, 0xd6, 0x48, - 0x84, 0xc6, 0x21, 0xa4, 0x6d, 0xb5, 0x71, 0xef, 0x13, 0xbe, 0x4e, 0x54, 0xf1, 0x64, 0x8f, 0x3f, 0xcc, 0x9a, 0x78, - 0xa5, 0x89, 0x25, 0xb3, 0xda, 0x2c, 0xa2, 0x32, 0x9c, 0x57, 0x07, 0x56, 0xbc, 0x34, 0x13, 0xff, 0x5c, 0x0a, 0xa1, - 0x67, 0x82, 0xb8, 0x6b, 0x4c, 0x76, 0x31, 0x6c, 0xe3, 0x40, 0x46, 0xea, 0xb0, 0xd4, 0xf4, 0x3b, 0x02, 0x65, 0x18, - 0xa4, 0xaf, 0xac, 0x6d, 0x55, 0xd6, 0xbe, 0x59, 0x66, 0x7a, 0x7c, 0x60, 0xb2, 0x83, 0x33, 0x23, 0xc9, 0x79, 0x82, - 0x47, 0xb4, 0x28, 0xf4, 0x24, 0xb5, 0x23, 0x5a, 0x44, 0xe1, 0xc3, 0x27, 0x04, 0xe8, 0x0c, 0xdd, 0xb4, 0xd0, 0x8c, - 0xfb, 0x10, 0x39, 0x93, 0x04, 0x2a, 0x66, 0x18, 0x4b, 0x74, 0xca, 0x31, 0x7f, 0xb2, 0xe5, 0x45, 0xc1, 0xdd, 0x72, - 0x49, 0xff, 0x0e, 0xb3, 0xf0, 0x93, 0x18, 0xab, 0x68, 0xad, 0xe1, 0x9d, 0xe4, 0x29, 0xc0, 0xe3, 0x63, 0x54, 0x61, - 0x1b, 0x45, 0xb9, 0x6c, 0x23, 0x0f, 0x99, 0x7f, 0x8e, 0x69, 0xaa, 0xc1, 0xb8, 0x4e, 0x42, 0x9c, 0xc5, 0x6e, 0x69, - 0x40, 0x0e, 0x4f, 0x97, 0xd3, 0x23, 0x18, 0xf5, 0xc8, 0x75, 0x73, 0xb5, 0xbd, 0x46, 0x8a, 0x97, 0x7d, 0x83, 0xe4, - 0x29, 0x72, 0x73, 0xc1, 0x39, 0x8e, 0x7e, 0x84, 0x39, 0x66, 0x57, 0xc6, 0x85, 0x19, 0x8b, 0xf2, 0x4d, 0xd9, 0xfe, - 0x75, 0xa9, 0xe1, 0x2b, 0x21, 0x81, 0x58, 0x51, 0x99, 0xbc, 0xa4, 0x0b, 0x10, 0x6f, 0x86, 0x17, 0x0b, 0x92, 0x00, - 0x11, 0x6f, 0x3b, 0xa4, 0xa4, 0x11, 0x7e, 0x0b, 0x97, 0x85, 0x23, 0x0c, 0x01, 0x6f, 0x2a, 0x18, 0xc6, 0xbe, 0x3d, - 0x77, 0x1a, 0xe6, 0x00, 0x5c, 0x1a, 0x14, 0x47, 0xc6, 0xcc, 0xcc, 0x52, 0xbe, 0x04, 0x19, 0x31, 0x05, 0x46, 0xa0, - 0xc3, 0x69, 0x0c, 0x60, 0xb7, 0x14, 0x57, 0xa0, 0x92, 0xbf, 0xb7, 0x0c, 0xd8, 0x3a, 0x79, 0x09, 0x99, 0xc9, 0x71, - 0x88, 0x01, 0x8b, 0xa5, 0x61, 0x0a, 0xb5, 0xe8, 0xc7, 0x71, 0xe7, 0x70, 0x79, 0xb6, 0xe4, 0x01, 0xfc, 0x1a, 0x4a, - 0x7b, 0x60, 0x6e, 0xef, 0x95, 0x62, 0x59, 0x28, 0xb5, 0x25, 0x56, 0x15, 0xe7, 0xca, 0xad, 0x32, 0xe6, 0xf7, 0x01, - 0x31, 0x34, 0x87, 0x93, 0x0b, 0x9b, 0x9d, 0x26, 0xff, 0xe5, 0x92, 0xad, 0x6f, 0xb8, 0x3b, 0x16, 0xc1, 0xa0, 0x5a, - 0x4f, 0x52, 0x0b, 0x2b, 0xc1, 0xa7, 0x95, 0x7b, 0x24, 0x51, 0xd3, 0xb3, 0x23, 0x62, 0x0b, 0xcc, 0xa0, 0x58, 0xa7, - 0x64, 0x45, 0x2f, 0x0b, 0xdd, 0x1d, 0x97, 0x82, 0x1f, 0xcc, 0x64, 0xdb, 0xd3, 0xf4, 0xb0, 0x8b, 0xc8, 0xcf, 0x15, - 0x81, 0x8b, 0xa1, 0x9d, 0xf8, 0xfc, 0xec, 0x49, 0x40, 0x12, 0x01, 0x09, 0x51, 0xf3, 0x73, 0x18, 0x24, 0x97, 0x55, - 0x85, 0x6a, 0x92, 0x1a, 0xf5, 0x5a, 0x05, 0x54, 0x1f, 0x27, 0x0a, 0xa8, 0xa1, 0x94, 0x58, 0x78, 0x7d, 0x87, 0xa8, - 0xdb, 0x13, 0x66, 0x20, 0x5e, 0x43, 0x18, 0x7a, 0xbb, 0x16, 0x16, 0x07, 0xc8, 0xab, 0x10, 0xe2, 0x50, 0xb9, 0xb1, - 0xd8, 0x21, 0xc8, 0x4a, 0x2e, 0x99, 0x0e, 0x23, 0x52, 0xc6, 0xcb, 0x29, 0x84, 0x91, 0x03, 0xb1, 0xe2, 0x4c, 0x1d, - 0x22, 0xd3, 0xc8, 0x79, 0x00, 0x8b, 0x8b, 0x88, 0x1e, 0x29, 0xd3, 0xae, 0x10, 0x15, 0x22, 0x6d, 0xb0, 0x87, 0x6f, - 0x27, 0x2e, 0x7c, 0xc2, 0x7a, 0x61, 0xbd, 0x22, 0xe5, 0x5f, 0xdd, 0x7b, 0x00, 0x04, 0xf2, 0x7d, 0x5a, 0x03, 0x38, - 0x1f, 0x69, 0x6d, 0x0b, 0xfb, 0xec, 0x45, 0xfe, 0x8b, 0x7f, 0xec, 0x7b, 0xad, 0xc2, 0x33, 0xf1, 0x9e, 0x9c, 0x71, - 0xd9, 0xe8, 0x5e, 0x8f, 0xd4, 0xee, 0x87, 0x45, 0x6c, 0xe2, 0x12, 0xf8, 0xb8, 0xc5, 0xee, 0x43, 0xa6, 0x37, 0x91, - 0xb5, 0x2c, 0x2f, 0xe9, 0xe8, 0x24, 0xd0, 0x45, 0xc1, 0x0c, 0x7c, 0xf0, 0xb2, 0xb5, 0x2d, 0x10, 0x36, 0x7e, 0x18, - 0x7c, 0x79, 0x82, 0x69, 0x3d, 0x35, 0xca, 0x52, 0xee, 0xc9, 0xb5, 0x65, 0xa4, 0xa1, 0xfd, 0x70, 0x7e, 0xe0, 0x7d, - 0x67, 0xf9, 0xa1, 0x71, 0xd2, 0x08, 0x74, 0x33, 0x5f, 0x69, 0xa4, 0x59, 0x03, 0xfd, 0xf8, 0xf0, 0x70, 0x1a, 0x50, - 0x43, 0xfb, 0x61, 0xf0, 0x38, 0x18, 0x88, 0x85, 0x36, 0x23, 0x06, 0x4f, 0x02, 0xbb, 0x78, 0x1a, 0xaa, 0xd2, 0x02, - 0x5e, 0xa0, 0x74, 0x30, 0xc8, 0x7a, 0x66, 0xab, 0xd9, 0x43, 0x99, 0x45, 0xb7, 0x0c, 0x5c, 0xec, 0xc8, 0x03, 0x0e, - 0x0b, 0xca, 0x4a, 0x22, 0x48, 0xfb, 0xb7, 0x3d, 0x82, 0x07, 0x8d, 0x1b, 0x21, 0x87, 0x4d, 0x57, 0xa4, 0x5b, 0xd4, - 0xe3, 0x88, 0x02, 0xc4, 0x81, 0xf9, 0x47, 0xe4, 0xbf, 0x3e, 0x39, 0xbb, 0x4f, 0x7e, 0x91, 0x63, 0x98, 0x97, 0xe4, - 0x52, 0x01, 0x58, 0xba, 0x32, 0xbf, 0xae, 0xff, 0x45, 0xa1, 0xbc, 0x9b, 0xa4, 0x09, 0x0e, 0x79, 0xc0, 0x41, 0x86, - 0x52, 0x88, 0x55, 0x39, 0x9d, 0xb6, 0xed, 0x35, 0x68, 0x29, 0xfa, 0xe6, 0x6c, 0x3d, 0x0a, 0xcd, 0x6a, 0x28, 0xfd, - 0x65, 0x24, 0xce, 0x38, 0x98, 0x01, 0xd9, 0x3f, 0x1b, 0x4c, 0xc4, 0x5c, 0x1d, 0xaa, 0x21, 0x78, 0x67, 0xaf, 0x55, - 0x72, 0x34, 0xf8, 0x1b, 0x03, 0x21, 0x27, 0x08, 0xbd, 0x59, 0x60, 0x48, 0x0d, 0xe2, 0x56, 0x9b, 0x30, 0x92, 0x8f, - 0x67, 0x8a, 0x7f, 0x20, 0xbd, 0x2d, 0xfd, 0xc5, 0xb0, 0xa6, 0xaa, 0x77, 0x75, 0x26, 0x33, 0x2f, 0x20, 0x2a, 0xab, - 0x5c, 0xd1, 0x3b, 0xda, 0xb2, 0x4c, 0xa4, 0x86, 0x25, 0x8d, 0x49, 0x05, 0xaf, 0x7a, 0xa8, 0xd4, 0x9c, 0x0d, 0xd3, - 0x38, 0xa6, 0x5c, 0x29, 0x6b, 0x16, 0x27, 0x07, 0xf1, 0xbe, 0xe2, 0x24, 0xc1, 0x8d, 0x25, 0x76, 0xbc, 0xf6, 0x0d, - 0xc2, 0x94, 0x25, 0xb8, 0xf3, 0x07, 0x9a, 0x49, 0xf4, 0x89, 0x82, 0x4d, 0x51, 0xb1, 0x96, 0x61, 0x62, 0x8d, 0xc8, - 0x61, 0x65, 0x0d, 0x14, 0x34, 0x02, 0x65, 0x94, 0xcc, 0x1d, 0x85, 0x00, 0x0f, 0x1a, 0x57, 0x68, 0x15, 0xcf, 0xa4, - 0xa2, 0x7d, 0x6d, 0x53, 0x60, 0xce, 0x5c, 0x61, 0x82, 0x17, 0x32, 0xc1, 0x87, 0x02, 0x0c, 0x91, 0x85, 0x57, 0x51, - 0xbe, 0xb2, 0x38, 0x9f, 0x3d, 0x2a, 0x52, 0x5a, 0xad, 0xba, 0x46, 0x9e, 0x3c, 0x8a, 0xa0, 0x46, 0x15, 0xf4, 0x59, - 0x74, 0x5f, 0x2a, 0xae, 0x96, 0x56, 0xf0, 0x54, 0x39, 0xaf, 0xac, 0x2a, 0xb9, 0xad, 0x32, 0x50, 0xc9, 0xc1, 0xee, - 0xd2, 0x0d, 0x34, 0xaa, 0x98, 0x4d, 0x6d, 0x3d, 0xc6, 0xb9, 0x5b, 0x00, 0x5f, 0xea, 0xda, 0x16, 0xa6, 0x08, 0x43, - 0x58, 0x4d, 0x8d, 0x07, 0x55, 0x62, 0x81, 0x44, 0xcc, 0x31, 0x04, 0x4b, 0x4c, 0x8b, 0x3e, 0xff, 0xd8, 0xf6, 0x65, - 0x19, 0xa1, 0x94, 0x62, 0x65, 0x0a, 0xdd, 0x60, 0x38, 0xd3, 0xbe, 0x0d, 0xa3, 0x99, 0xd5, 0x37, 0x68, 0xa1, 0x71, - 0xa3, 0x41, 0xe7, 0xbe, 0x9d, 0x72, 0x84, 0x75, 0xb6, 0x8d, 0x98, 0xd6, 0xb8, 0x2d, 0x43, 0x85, 0x5d, 0xf9, 0xca, - 0xc3, 0x96, 0xa5, 0xa6, 0xe7, 0x50, 0x88, 0x6b, 0x84, 0x58, 0x44, 0x45, 0x20, 0xdf, 0x1e, 0x5a, 0xc9, 0xce, 0x42, - 0x2a, 0x1f, 0x3e, 0x3c, 0x7b, 0x68, 0x3c, 0x34, 0x8b, 0x36, 0xba, 0x1f, 0xce, 0x0f, 0xa0, 0x60, 0x37, 0x5f, 0x1a, - 0x03, 0x2b, 0x86, 0x29, 0x45, 0x7b, 0xb4, 0xb7, 0x06, 0x68, 0x17, 0x7e, 0x13, 0x76, 0x91, 0x4d, 0x27, 0xee, 0xbc, - 0x7e, 0x80, 0xc2, 0x66, 0xac, 0xc6, 0xbf, 0xeb, 0x7f, 0xd7, 0x84, 0x79, 0xf3, 0xf1, 0xde, 0xec, 0xa6, 0x93, 0xa8, - 0x13, 0x3b, 0x4a, 0x81, 0xfa, 0x11, 0x1e, 0x4a, 0xd2, 0x50, 0x2a, 0xea, 0x9a, 0xc2, 0x37, 0x08, 0xed, 0x01, 0xf5, - 0xa2, 0xd5, 0x32, 0x29, 0x49, 0xc4, 0x1a, 0x11, 0xc0, 0xda, 0x24, 0x28, 0x84, 0x38, 0x60, 0x80, 0xcf, 0xd0, 0x45, - 0x83, 0xa7, 0xca, 0x52, 0x5c, 0xac, 0x23, 0x01}; + 0x1b, 0x08, 0x0b, 0x00, 0xe4, 0x7f, 0x9b, 0xad, 0xbb, 0x97, 0x53, 0xde, 0xb7, 0x25, 0x0e, 0x69, 0xd4, 0x69, 0x89, + 0xba, 0xa5, 0x55, 0x22, 0x04, 0x27, 0xeb, 0x00, 0x52, 0xac, 0xbc, 0xec, 0x5f, 0xfb, 0xb5, 0x7a, 0x12, 0x0a, 0xd6, + 0x48, 0x84, 0x4a, 0x48, 0x5a, 0xcb, 0xbd, 0xb7, 0x72, 0x66, 0x4e, 0x62, 0xd8, 0xbd, 0x7f, 0x88, 0x68, 0x86, 0x28, + 0xd6, 0xf1, 0xda, 0x2c, 0xa2, 0x32, 0x5c, 0xdd, 0xab, 0xb2, 0x15, 0xbc, 0x24, 0x13, 0xe6, 0xbd, 0x0c, 0x52, 0x63, + 0x82, 0x88, 0x6d, 0x74, 0x71, 0x51, 0x9c, 0xe2, 0x40, 0x56, 0xea, 0xb0, 0x5c, 0xb7, 0x1d, 0x81, 0x3a, 0x0c, 0xf2, + 0xd7, 0xa8, 0x5c, 0x35, 0x2d, 0x43, 0xb3, 0x42, 0x37, 0x7c, 0x60, 0xd8, 0xc1, 0x95, 0x91, 0x94, 0x3c, 0x29, 0x20, + 0x96, 0x20, 0xf6, 0x24, 0xb7, 0x83, 0x4a, 0x06, 0xf1, 0xc3, 0x2f, 0x88, 0x50, 0x55, 0x35, 0x2d, 0xe8, 0xb6, 0x21, + 0x38, 0x61, 0x8e, 0x44, 0x2a, 0xac, 0x39, 0xcf, 0x74, 0xaa, 0x31, 0x7f, 0x62, 0x32, 0x9b, 0x99, 0x42, 0x0a, 0xf6, + 0x6f, 0xd8, 0x89, 0x3f, 0x49, 0xb1, 0xa2, 0x52, 0x0a, 0x8e, 0xb3, 0x27, 0x0b, 0x07, 0x07, 0x58, 0xb0, 0x8f, 0xaa, + 0xdc, 0x68, 0x12, 0x0f, 0x95, 0xbf, 0xc7, 0x36, 0xd5, 0x60, 0x5a, 0xc7, 0x80, 0xac, 0x52, 0xf7, 0xa7, 0x2d, 0xb6, + 0x64, 0xda, 0xda, 0x11, 0x8d, 0x6a, 0xe5, 0xba, 0xe9, 0xda, 0xdc, 0x62, 0xc3, 0xcb, 0xae, 0xc1, 0xe1, 0x11, 0xb6, + 0x33, 0x29, 0x04, 0x09, 0xfe, 0x09, 0x08, 0xc2, 0x1f, 0x98, 0x16, 0x26, 0x0d, 0xce, 0xd7, 0x75, 0xfb, 0xd7, 0xa5, + 0x82, 0xd7, 0x32, 0x44, 0x72, 0xc1, 0xc2, 0xe4, 0x15, 0xcb, 0x50, 0xfc, 0xd3, 0x58, 0x64, 0x34, 0x41, 0x32, 0xbe, + 0x1f, 0x08, 0x43, 0x16, 0x14, 0xf7, 0x70, 0x2c, 0x1c, 0x61, 0x09, 0x78, 0x73, 0xd1, 0x30, 0xf6, 0xed, 0x85, 0x55, + 0x50, 0x02, 0xf0, 0x68, 0x50, 0x5c, 0x1a, 0xd7, 0x3b, 0x8e, 0xf2, 0x23, 0xc8, 0x88, 0x39, 0xd0, 0x84, 0xf7, 0xa6, + 0xd1, 0xa3, 0xfb, 0xe5, 0x44, 0xc0, 0x4c, 0x2f, 0x6f, 0x19, 0x70, 0x75, 0xf6, 0x1c, 0xb8, 0xce, 0x89, 0x4f, 0x01, + 0x8d, 0xa1, 0x71, 0xf2, 0x97, 0xf8, 0xe7, 0xd3, 0xf1, 0xe1, 0xfa, 0xfc, 0xc8, 0x03, 0x78, 0x14, 0xa0, 0x3d, 0x28, + 0xb7, 0xeb, 0x26, 0x52, 0x59, 0xa8, 0xb5, 0x0d, 0x5c, 0x8a, 0x6b, 0xe5, 0xf6, 0x30, 0xd6, 0xf7, 0x71, 0x31, 0xe8, + 0xbd, 0xc9, 0xfa, 0xf5, 0x71, 0x9d, 0xff, 0xf6, 0x69, 0x62, 0x5f, 0xb5, 0xc7, 0x06, 0x43, 0x54, 0xb5, 0x87, 0xf1, + 0xcc, 0x84, 0xe8, 0xb7, 0x56, 0x38, 0x43, 0x6a, 0xa6, 0x2f, 0x53, 0xd1, 0x89, 0x48, 0x8d, 0x72, 0x75, 0x4a, 0x17, + 0xf6, 0x8d, 0xb2, 0xeb, 0xb1, 0x6b, 0x29, 0x1e, 0xd5, 0x74, 0xc7, 0xb3, 0xf4, 0xf1, 0x04, 0x0d, 0xbf, 0x08, 0x81, + 0x8f, 0xfe, 0x8d, 0xfc, 0xf2, 0x35, 0x92, 0xa0, 0x24, 0x88, 0x12, 0x6a, 0xe6, 0x2f, 0x01, 0x94, 0x5c, 0x4b, 0xf9, + 0x6b, 0x9a, 0xf6, 0x1a, 0x35, 0x11, 0x8a, 0xc6, 0x04, 0x8d, 0x50, 0x64, 0x48, 0x2d, 0x8b, 0xdf, 0xdf, 0xa1, 0xd1, + 0xfd, 0x21, 0xd7, 0x40, 0x96, 0x00, 0xb1, 0x9f, 0x54, 0xc2, 0xe1, 0x00, 0x79, 0x15, 0x80, 0xf8, 0xca, 0x8e, 0xc5, + 0x06, 0x03, 0xdf, 0x72, 0x49, 0xc0, 0x08, 0x27, 0x90, 0xe3, 0x14, 0xc2, 0xac, 0xc3, 0x83, 0xc9, 0xf6, 0x9d, 0x12, + 0xab, 0x46, 0xae, 0x03, 0x74, 0x1d, 0x27, 0xce, 0x91, 0x1d, 0x4e, 0xb4, 0xbe, 0x80, 0xe7, 0x6a, 0x6d, 0x0a, 0x20, + 0x47, 0x6b, 0x00, 0x4e, 0x25, 0x52, 0xe6, 0xf5, 0xe9, 0x43, 0x84, 0xc1, 0x0d, 0x4b, 0x04, 0xb3, 0x91, 0x49, 0xf5, + 0xe9, 0xc4, 0x2e, 0x1b, 0xf9, 0x98, 0xf9, 0xea, 0x9e, 0xb8, 0xf6, 0x53, 0xf8, 0x42, 0xc2, 0x60, 0x5c, 0xa9, 0xd2, + 0xfe, 0x0a, 0xe5, 0xd4, 0x7d, 0x36, 0x76, 0x04, 0x12, 0x38, 0xa1, 0xc4, 0x30, 0xb8, 0xf2, 0x69, 0x86, 0x5b, 0xe7, + 0xe5, 0xa0, 0xc0, 0xfe, 0x91, 0x19, 0x03, 0x1e, 0xa9, 0x93, 0x26, 0x49, 0x58, 0xd5, 0xf6, 0x33, 0x4e, 0x0a, 0x89, + 0x64, 0x1e, 0xb4, 0x7a, 0x5d, 0x0d, 0x12, 0xcf, 0x2b, 0xdd, 0x35, 0x90, 0x55, 0xc3, 0x0e, 0xcd, 0x0e, 0xad, 0x6b, + 0x8f, 0x43, 0xd0, 0xb4, 0x6a, 0xdb, 0x4b, 0xe5, 0xa4, 0x9b, 0x09, 0x23, 0x1a, 0x43, 0xa9, 0xff, 0x61, 0x8b, 0x07, + 0xd6, 0x0f, 0x83, 0x23, 0xbe, 0x8a, 0x66, 0xa2, 0x10, 0x2f, 0x11, 0x01, 0x0d, 0xc6, 0x96, 0xba, 0x87, 0xaa, 0xa8, + 0x44, 0x7c, 0x1e, 0x34, 0xdb, 0xba, 0x41, 0x90, 0xf0, 0x7a, 0xdb, 0x63, 0x60, 0x96, 0x8d, 0x64, 0xcb, 0x2f, 0x28, + 0x96, 0x04, 0xd5, 0xc0, 0x7a, 0xe8, 0xec, 0xd1, 0x61, 0x1b, 0x09, 0x4b, 0x4c, 0x6e, 0x1b, 0x31, 0x98, 0x9c, 0x6d, + 0xdb, 0xd0, 0xec, 0xa4, 0x0f, 0x0a, 0xe0, 0xc8, 0x35, 0xc4, 0x93, 0xdc, 0xee, 0x19, 0x00, 0x80, 0x07, 0xf5, 0xcf, + 0xe0, 0x7f, 0x75, 0xf8, 0x52, 0x3a, 0xfc, 0x0d, 0x84, 0xa5, 0xc1, 0xb2, 0x1c, 0x25, 0x40, 0xc5, 0x8b, 0xb3, 0xdb, + 0x7a, 0x1b, 0x44, 0xff, 0x79, 0x9a, 0x26, 0xc4, 0xe7, 0x9e, 0x78, 0x4c, 0xa5, 0x94, 0xab, 0x78, 0x34, 0x9d, 0xb5, + 0xb7, 0x24, 0x26, 0xe7, 0x9a, 0xf3, 0xe5, 0x2a, 0x35, 0x4b, 0xbe, 0x74, 0xd7, 0x01, 0xbc, 0x71, 0x72, 0x03, 0x5c, + 0x40, 0x24, 0x17, 0x61, 0x5b, 0x7b, 0x19, 0xc2, 0x7f, 0x4e, 0x5b, 0x24, 0xfb, 0x8b, 0xc3, 0x51, 0x00, 0x3d, 0x09, + 0x04, 0x05, 0x72, 0x64, 0xf6, 0xf0, 0x6b, 0x99, 0x38, 0x93, 0x5b, 0xac, 0x0c, 0xff, 0x40, 0x7b, 0x53, 0xba, 0xab, + 0x61, 0xc9, 0xa2, 0xde, 0xd6, 0x2b, 0x0c, 0xbd, 0x85, 0xac, 0x4c, 0x64, 0x8b, 0xe6, 0x69, 0x2b, 0x56, 0x10, 0x1b, + 0x08, 0x59, 0x6c, 0x55, 0x0a, 0xaa, 0x93, 0x85, 0x1d, 0x45, 0xda, 0xc6, 0x39, 0xe6, 0x6a, 0xab, 0x71, 0x73, 0x46, + 0x0f, 0xf7, 0xab, 0x4e, 0x88, 0xae, 0x8c, 0xe0, 0x91, 0xda, 0x35, 0x8c, 0xd3, 0x18, 0x92, 0x3d, 0xab, 0x2f, 0x0d, + 0xd6, 0xc9, 0x06, 0xdb, 0xc2, 0x62, 0xcb, 0x8a, 0x23, 0x5b, 0x28, 0x2e, 0x9b, 0x96, 0xc4, 0xc1, 0x42, 0xa9, 0x8d, + 0x69, 0xe5, 0x8f, 0x89, 0x12, 0x11, 0x9a, 0x56, 0x3b, 0x3c, 0x2b, 0xb4, 0xb2, 0x7b, 0xfd, 0xdb, 0x80, 0x92, 0xb4, + 0xca, 0x44, 0x37, 0x8c, 0x94, 0x2f, 0x4a, 0xb4, 0xc4, 0x28, 0x83, 0x8a, 0x78, 0xcb, 0xd2, 0x5c, 0x5c, 0x35, 0x29, + 0x95, 0x35, 0x2f, 0x99, 0x28, 0x4f, 0x22, 0x18, 0x70, 0x85, 0xee, 0x2c, 0xb9, 0xef, 0x14, 0x57, 0x73, 0x23, 0x45, + 0xae, 0xdc, 0x54, 0x56, 0x55, 0x78, 0x56, 0xad, 0x48, 0x26, 0x27, 0xbf, 0xcb, 0xd7, 0x54, 0xa9, 0xa8, 0xd7, 0xb5, + 0x71, 0x9c, 0xe7, 0x79, 0x89, 0x5c, 0xa9, 0x6a, 0x53, 0xe8, 0xfa, 0x0d, 0x69, 0x36, 0xed, 0xad, 0x33, 0xc9, 0x75, + 0x17, 0xe9, 0x8f, 0x31, 0x58, 0xa6, 0x27, 0xfc, 0x79, 0xc6, 0xb6, 0xd5, 0x65, 0x90, 0x31, 0xc6, 0x9d, 0x29, 0x95, + 0x83, 0xe5, 0x4e, 0xbb, 0xd7, 0xdc, 0x8e, 0xd0, 0x3a, 0x54, 0x27, 0xce, 0xf5, 0xdb, 0xbd, 0x89, 0x3c, 0x61, 0xb3, + 0x71, 0x23, 0xc7, 0x55, 0x9e, 0xea, 0x50, 0xe4, 0x37, 0xae, 0x72, 0x34, 0x66, 0xb9, 0x6e, 0x2c, 0x0a, 0x69, 0x8d, + 0x94, 0x8b, 0x98, 0x08, 0x05, 0x3c, 0x45, 0x45, 0xe1, 0x6c, 0x22, 0xc5, 0x8f, 0x1f, 0x9f, 0x3f, 0xd2, 0x01, 0x12, + 0xec, 0x86, 0x2f, 0x87, 0x8b, 0x47, 0x30, 0xd0, 0x77, 0x77, 0x1a, 0x13, 0x2d, 0xc6, 0x31, 0x65, 0x77, 0xd8, 0x8c, + 0xe7, 0xe0, 0x16, 0xf9, 0x4b, 0xd4, 0xc5, 0xa8, 0x67, 0x79, 0xe7, 0xc3, 0x07, 0x94, 0x46, 0xa3, 0x8c, 0x67, 0xd3, + 0xff, 0x5f, 0x96, 0xfa, 0xed, 0xa7, 0xd3, 0xdd, 0x4f, 0x27, 0x49, 0x27, 0xcf, 0xa4, 0x02, 0xc3, 0x27, 0x3c, 0x96, + 0x64, 0x24, 0x55, 0xc8, 0x36, 0x45, 0x68, 0x90, 0xea, 0x03, 0x0b, 0x46, 0xa7, 0x8d, 0xb4, 0x26, 0x91, 0x07, 0x4c, + 0x80, 0x7b, 0x93, 0xa8, 0x10, 0xcb, 0x5e, 0x8d, 0x42, 0x86, 0x3e, 0x2a, 0x7c, 0x99, 0x79, 0x8e, 0xcb, 0x43, 0x28}; // Backwards compatibility alias #define INDEX_GZ INDEX_BR diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 365e5f64db..8094903008 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -2,6 +2,8 @@ #ifdef USE_CAPTIVE_PORTAL #include "esphome/core/log.h" #include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/string_ref.h" #include "esphome/components/wifi/wifi_component.h" #include "captive_index.h" @@ -24,23 +26,30 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) { stream->printf(R"({"mac":"%s","name":"%s","aps":[{})", mac_str, App.get_name().c_str()); #endif - for (auto &scan : wifi::global_wifi_component->get_scan_result()) { - if (scan.get_is_hidden()) - continue; + // An SSID can contain a " or \ that would break the JSON, so escape it before writing it out. An SSID is at most + // 32 bytes (IEEE 802.11), so this is large enough that nothing is ever dropped. Reused for every scan result. + char escaped_ssid[32 * JSON_ESCAPE_MAX_EXPANSION + 1]; + { + // Invariant: only bounded in-memory work under the lock; the network send + // happens later in request->send() + wifi::ScanResultsLock lock(wifi::global_wifi_component); + for (const auto &scan : wifi::global_wifi_component->get_scan_result()) { + if (scan.get_is_hidden()) + continue; - // Assumes no " in ssid, possible unicode isses? + json_escape_into_buffer(escaped_ssid, scan.get_ssid()); #ifdef USE_ESP8266 - stream->print(ESPHOME_F(",{\"ssid\":\"")); - stream->print(scan.get_ssid().c_str()); - stream->print(ESPHOME_F("\",\"rssi\":")); - stream->print(scan.get_rssi()); - stream->print(ESPHOME_F(",\"lock\":")); - stream->print(scan.get_with_auth()); - stream->print(ESPHOME_F("}")); + stream->print(ESPHOME_F(",{\"ssid\":\"")); + stream->print(escaped_ssid); + stream->print(ESPHOME_F("\",\"rssi\":")); + stream->print(scan.get_rssi()); + stream->print(ESPHOME_F(",\"lock\":")); + stream->print(scan.get_with_auth()); + stream->print(ESPHOME_F("}")); #else - stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", scan.get_ssid().c_str(), scan.get_rssi(), - scan.get_with_auth()); + stream->printf(R"(,{"ssid":"%s","rssi":%d,"lock":%d})", escaped_ssid, scan.get_rssi(), scan.get_with_auth()); #endif + } } stream->print(ESPHOME_F("]}")); request->send(stream); diff --git a/esphome/components/cc1101/__init__.py b/esphome/components/cc1101/__init__.py index 0feb384ac2..01e3ed0cd5 100644 --- a/esphome/components/cc1101/__init__.py +++ b/esphome/components/cc1101/__init__.py @@ -21,6 +21,7 @@ MULTI_CONF = True ns = cg.esphome_ns.namespace("cc1101") CC1101Component = ns.class_("CC1101Component", cg.Component, spi.SPIDevice) +CC1101Listener = ns.class_("CC1101Listener") # Config keys CONF_RX_ATTENUATION = "rx_attenuation" @@ -48,6 +49,15 @@ CONF_FILTER_LENGTH_FSK_MSK = "filter_length_fsk_msk" CONF_FILTER_LENGTH_ASK_OOK = "filter_length_ask_ook" CONF_FREEZE = "freeze" CONF_HYST_LEVEL = "hyst_level" +CONF_FOC_BS_CS_GATE = "foc_bs_cs_gate" +CONF_FOC_LIMIT = "foc_limit" +CONF_FOC_PRE_K = "foc_pre_k" +CONF_FOC_POST_K = "foc_post_k" +CONF_BS_LIMIT = "bs_limit" +CONF_BS_PRE_KI = "bs_pre_ki" +CONF_BS_PRE_KP = "bs_pre_kp" +CONF_BS_POST_KI = "bs_post_ki" +CONF_BS_POST_KP = "bs_post_kp" # Packet mode config keys CONF_PACKET_MODE = "packet_mode" @@ -161,6 +171,64 @@ HYST_LEVEL = { "High": HystLevel.HYST_LEVEL_HIGH, } +FocLimit = ns.enum("FocLimit", True) +FOC_LIMIT = { + "Disabled": FocLimit.FOC_LIMIT_DISABLED, + "BW/8": FocLimit.FOC_LIMIT_BW_8, + "BW/4": FocLimit.FOC_LIMIT_BW_4, + "BW/2": FocLimit.FOC_LIMIT_BW_2, +} + +FocPreK = ns.enum("FocPreK", True) +FOC_PRE_K = { + "K": FocPreK.FOC_PRE_K_K, + "2K": FocPreK.FOC_PRE_K_2K, + "3K": FocPreK.FOC_PRE_K_3K, + "4K": FocPreK.FOC_PRE_K_4K, +} + +FocPostK = ns.enum("FocPostK", True) +FOC_POST_K = { + "Same": FocPostK.FOC_POST_K_SAME, + "K/2": FocPostK.FOC_POST_K_K_2, +} + +BsLimit = ns.enum("BsLimit", True) +BS_LIMIT = { + "Disabled": BsLimit.BS_LIMIT_DISABLED, + "3.125%": BsLimit.BS_LIMIT_3P125_PERCENT, + "6.25%": BsLimit.BS_LIMIT_6P25_PERCENT, + "12.5%": BsLimit.BS_LIMIT_12P5_PERCENT, +} + +BsPreKi = ns.enum("BsPreKi", True) +BS_PRE_KI = { + "KI": BsPreKi.BS_PRE_KI_KI, + "2KI": BsPreKi.BS_PRE_KI_2KI, + "3KI": BsPreKi.BS_PRE_KI_3KI, + "4KI": BsPreKi.BS_PRE_KI_4KI, +} + +BsPreKp = ns.enum("BsPreKp", True) +BS_PRE_KP = { + "KP": BsPreKp.BS_PRE_KP_KP, + "2KP": BsPreKp.BS_PRE_KP_2KP, + "3KP": BsPreKp.BS_PRE_KP_3KP, + "4KP": BsPreKp.BS_PRE_KP_4KP, +} + +BsPostKi = ns.enum("BsPostKi", True) +BS_POST_KI = { + "Same": BsPostKi.BS_POST_KI_SAME, + "KI/2": BsPostKi.BS_POST_KI_KI_2, +} + +BsPostKp = ns.enum("BsPostKp", True) +BS_POST_KP = { + "Same": BsPostKp.BS_POST_KP_SAME, + "KP": BsPostKp.BS_POST_KP_KP, +} + # Optional settings to generate setter calls for CONFIG_MAP = { cv.Optional(CONF_OUTPUT_POWER, default=10): cv.float_range(min=-30.0, max=11.0), @@ -214,6 +282,15 @@ CONFIG_MAP = { cv.Optional(CONF_FREEZE): cv.enum(FREEZE, upper=False), cv.Optional(CONF_WAIT_TIME, default="32"): cv.enum(WAIT_TIME, upper=False), cv.Optional(CONF_HYST_LEVEL): cv.enum(HYST_LEVEL, upper=False), + cv.Optional(CONF_FOC_BS_CS_GATE): cv.boolean, + cv.Optional(CONF_FOC_LIMIT): cv.enum(FOC_LIMIT, upper=False), + cv.Optional(CONF_FOC_PRE_K): cv.enum(FOC_PRE_K, upper=False), + cv.Optional(CONF_FOC_POST_K): cv.enum(FOC_POST_K, upper=False), + cv.Optional(CONF_BS_LIMIT): cv.enum(BS_LIMIT, upper=False), + cv.Optional(CONF_BS_PRE_KI): cv.enum(BS_PRE_KI, upper=False), + cv.Optional(CONF_BS_PRE_KP): cv.enum(BS_PRE_KP, upper=False), + cv.Optional(CONF_BS_POST_KI): cv.enum(BS_POST_KI, upper=False), + cv.Optional(CONF_BS_POST_KP): cv.enum(BS_POST_KP, upper=False), cv.Optional(CONF_PACKET_MODE, default=False): cv.boolean, cv.Optional(CONF_PACKET_LENGTH): cv.uint8_t, cv.Optional(CONF_CRC_ENABLE, default=False): cv.boolean, diff --git a/esphome/components/cc1101/cc1101.cpp b/esphome/components/cc1101/cc1101.cpp index ea0138e1dd..f7b90b91cf 100644 --- a/esphome/components/cc1101/cc1101.cpp +++ b/esphome/components/cc1101/cc1101.cpp @@ -672,6 +672,69 @@ void CC1101Component::set_hyst_level(HystLevel value) { } } +void CC1101Component::set_foc_bs_cs_gate(bool value) { + this->state_.FOC_BS_CS_GATE = value ? 1 : 0; + if (this->initialized_) { + this->write_(Register::FOCCFG); + } +} + +void CC1101Component::set_foc_limit(FocLimit value) { + this->state_.FOC_LIMIT = static_cast(value); + if (this->initialized_) { + this->write_(Register::FOCCFG); + } +} + +void CC1101Component::set_foc_pre_k(FocPreK value) { + this->state_.FOC_PRE_K = static_cast(value); + if (this->initialized_) { + this->write_(Register::FOCCFG); + } +} + +void CC1101Component::set_foc_post_k(FocPostK value) { + this->state_.FOC_POST_K = static_cast(value); + if (this->initialized_) { + this->write_(Register::FOCCFG); + } +} + +void CC1101Component::set_bs_limit(BsLimit value) { + this->state_.BS_LIMIT = static_cast(value); + if (this->initialized_) { + this->write_(Register::BSCFG); + } +} + +void CC1101Component::set_bs_pre_ki(BsPreKi value) { + this->state_.BS_PRE_KI = static_cast(value); + if (this->initialized_) { + this->write_(Register::BSCFG); + } +} + +void CC1101Component::set_bs_pre_kp(BsPreKp value) { + this->state_.BS_PRE_KP = static_cast(value); + if (this->initialized_) { + this->write_(Register::BSCFG); + } +} + +void CC1101Component::set_bs_post_ki(BsPostKi value) { + this->state_.BS_POST_KI = static_cast(value); + if (this->initialized_) { + this->write_(Register::BSCFG); + } +} + +void CC1101Component::set_bs_post_kp(BsPostKp value) { + this->state_.BS_POST_KP = static_cast(value); + if (this->initialized_) { + this->write_(Register::BSCFG); + } +} + void CC1101Component::set_packet_mode(bool value) { this->state_.PKT_FORMAT = static_cast(value ? PacketFormat::PACKET_FORMAT_FIFO : PacketFormat::PACKET_FORMAT_ASYNC_SERIAL); diff --git a/esphome/components/cc1101/cc1101.h b/esphome/components/cc1101/cc1101.h index 065ffd5250..79bfc9cb33 100644 --- a/esphome/components/cc1101/cc1101.h +++ b/esphome/components/cc1101/cc1101.h @@ -71,6 +71,17 @@ class CC1101Component final : public Component, void set_wait_time(WaitTime value); void set_hyst_level(HystLevel value); + // Frequency offset compensation and bit synchronization settings + void set_foc_bs_cs_gate(bool value); + void set_foc_limit(FocLimit value); + void set_foc_pre_k(FocPreK value); + void set_foc_post_k(FocPostK value); + void set_bs_limit(BsLimit value); + void set_bs_pre_ki(BsPreKi value); + void set_bs_pre_kp(BsPreKp value); + void set_bs_post_ki(BsPostKi value); + void set_bs_post_kp(BsPostKp value); + // Packet mode settings void set_packet_mode(bool value); void set_packet_length(uint8_t value); diff --git a/esphome/components/cc1101/cc1101defs.h b/esphome/components/cc1101/cc1101defs.h index 59b29f7478..6748f4369a 100644 --- a/esphome/components/cc1101/cc1101defs.h +++ b/esphome/components/cc1101/cc1101defs.h @@ -231,6 +231,56 @@ enum class HystLevel : uint8_t { HYST_LEVEL_HIGH, }; +enum class FocLimit : uint8_t { + FOC_LIMIT_DISABLED, + FOC_LIMIT_BW_8, + FOC_LIMIT_BW_4, + FOC_LIMIT_BW_2, +}; + +enum class FocPreK : uint8_t { + FOC_PRE_K_K, + FOC_PRE_K_2K, + FOC_PRE_K_3K, + FOC_PRE_K_4K, +}; + +enum class FocPostK : uint8_t { + FOC_POST_K_SAME, + FOC_POST_K_K_2, +}; + +enum class BsLimit : uint8_t { + BS_LIMIT_DISABLED, + BS_LIMIT_3P125_PERCENT, + BS_LIMIT_6P25_PERCENT, + BS_LIMIT_12P5_PERCENT, +}; + +enum class BsPreKi : uint8_t { + BS_PRE_KI_KI, + BS_PRE_KI_2KI, + BS_PRE_KI_3KI, + BS_PRE_KI_4KI, +}; + +enum class BsPreKp : uint8_t { + BS_PRE_KP_KP, + BS_PRE_KP_2KP, + BS_PRE_KP_3KP, + BS_PRE_KP_4KP, +}; + +enum class BsPostKi : uint8_t { + BS_POST_KI_SAME, + BS_POST_KI_KI_2, +}; + +enum class BsPostKp : uint8_t { + BS_POST_KP_SAME, + BS_POST_KP_KP, +}; + enum class PacketFormat : uint8_t { PACKET_FORMAT_FIFO, PACKET_FORMAT_SYNC_SERIAL, diff --git a/esphome/components/climate/__init__.py b/esphome/components/climate/__init__.py index fc1b0f368e..fe050fca22 100644 --- a/esphome/components/climate/__init__.py +++ b/esphome/components/climate/__init__.py @@ -274,7 +274,7 @@ def climate_schema( @setup_entity("climate") async def setup_climate_core_(var, config): - visual = config[CONF_VISUAL] + visual = config.get(CONF_VISUAL, {}) if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: cg.add_define("USE_CLIMATE_VISUAL_OVERRIDES") cg.add(var.set_visual_min_temperature_override(min_temp)) diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index 599894c8a9..6c776e0228 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -205,6 +205,9 @@ class ClimateTraits { float get_visual_max_humidity() const { return this->visual_max_humidity_; } void set_visual_max_humidity(float visual_max_humidity) { this->visual_max_humidity_ = visual_max_humidity; } + TemperatureUnit get_temperature_unit() const { return this->temperature_unit_; } + void set_temperature_unit(TemperatureUnit unit) { this->temperature_unit_ = unit; } + protected: void set_mode_support_(climate::ClimateMode mode, bool supported) { if (supported) { @@ -274,6 +277,7 @@ class ClimateTraits { climate::ClimateFanModeMask supported_fan_modes_; climate::ClimateSwingModeMask supported_swing_modes_; climate::ClimatePresetMask supported_presets_; + TemperatureUnit temperature_unit_{TemperatureUnit::CELSIUS}; /** Custom mode storage - pointers to vectors owned by the Climate base class. * diff --git a/esphome/components/const/__init__.py b/esphome/components/const/__init__.py index 6f4fa9aaa7..7476385563 100644 --- a/esphome/components/const/__init__.py +++ b/esphome/components/const/__init__.py @@ -24,6 +24,7 @@ CONF_IAQ = "iaq" CONF_IGNORE_NOT_FOUND = "ignore_not_found" CONF_LIBRETINY = "libretiny" CONF_LOOP = "loop" +CONF_NOX_INDEX = "nox_index" CONF_ON_PACKET = "on_packet" CONF_ON_RECEIVE = "on_receive" CONF_ON_STATE_CHANGE = "on_state_change" @@ -31,14 +32,17 @@ CONF_PARITY = "parity" CONF_RECEIVER_FREQUENCY = "receiver_frequency" CONF_REQUEST_HEADERS = "request_headers" CONF_ROWS = "rows" +CONF_SCAN_PARAMETERS = "scan_parameters" CONF_SHA256 = "sha256" CONF_STATE_SAVE_INTERVAL = "state_save_interval" CONF_STOP_BITS = "stop_bits" CONF_USE_PSRAM = "use_psram" +CONF_VOC_INDEX = "voc_index" CONF_VOLUME_INCREMENT = "volume_increment" CONF_VOLUME_INITIAL = "volume_initial" CONF_VOLUME_MAX = "volume_max" CONF_VOLUME_MIN = "volume_min" +CONF_WINDOW = "window" ICON_CURRENT_DC = "mdi:current-dc" ICON_SOLAR_PANEL = "mdi:solar-panel" diff --git a/esphome/components/debug/debug_libretiny.cpp b/esphome/components/debug/debug_libretiny.cpp index 1cc04dcbd8..55b29310a1 100644 --- a/esphome/components/debug/debug_libretiny.cpp +++ b/esphome/components/debug/debug_libretiny.cpp @@ -28,7 +28,7 @@ size_t DebugComponent::get_device_info_(std::span ESP_LOGD(TAG, "LibreTiny debug info:\n" " Version: %s\n" - " Chip: %s (%04x) @ %u MHz\n" + " Chip: %s (%04x) @ %" PRIu32 " MHz\n" " Chip ID: 0x%06" PRIX32 "\n" " Board: %s\n" " Flash: %" PRIu32 " KiB\n" @@ -38,7 +38,7 @@ size_t DebugComponent::get_device_info_(std::span lt_get_board_code(), flash_kib, ram_kib, reset_reason); pos = buf_append_str(buf, size, pos, "|Version: "); - pos = buf_append_str(buf, size, pos, LT_BANNER_STR + 10); + pos = buf_append_str(buf, size, pos, <_BANNER_STR[10]); pos = buf_append_str(buf, size, pos, "|Reset Reason: "); pos = buf_append_str(buf, size, pos, reset_reason); pos = buf_append_str(buf, size, pos, "|Chip Name: "); diff --git a/esphome/components/debug/debug_rp2.cpp b/esphome/components/debug/debug_rp2.cpp index ba6081963f..336e9c7e06 100644 --- a/esphome/components/debug/debug_rp2.cpp +++ b/esphome/components/debug/debug_rp2.cpp @@ -74,7 +74,7 @@ size_t DebugComponent::get_device_info_(std::span constexpr size_t size = DEVICE_INFO_BUFFER_SIZE; char *buf = buffer.data(); - uint32_t cpu_freq = ::rp2040.f_cpu(); + uint32_t cpu_freq = RP2040::f_cpu(); ESP_LOGD(TAG, "CPU Frequency: %" PRIu32, cpu_freq); pos = buf_append_printf(buf, size, pos, "|CPU Frequency: %" PRIu32, cpu_freq); diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 9666c8e507..3b70f947d2 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -30,6 +30,7 @@ from esphome.const import ( CONF_SECOND, CONF_SLEEP_DURATION, CONF_TIME_ID, + CONF_TRIGGER_ID, CONF_WAKEUP_PIN, PLATFORM_BK72XX, PLATFORM_ESP32, @@ -234,6 +235,15 @@ EXT1_WAKEUP_MODES = { } WakeupCauseToRunDuration = deep_sleep_ns.struct("WakeupCauseToRunDuration") +WakeupCause = deep_sleep_ns.enum("WakeupCause") +WakeTrigger = deep_sleep_ns.class_( + "WakeTrigger", automation.Trigger.template(WakeupCause), cg.Component +) +Ext1WakeTrigger = deep_sleep_ns.class_( + "Ext1WakeTrigger", automation.Trigger.template(), cg.Component +) + +CONF_ON_WAKE = "on_wake" CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode" CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup" CONF_TOUCH_WAKEUP = "touch_wakeup" @@ -256,6 +266,22 @@ WAKEUP_PIN_SCHEMA = cv.Schema( } ) +EXT1_WAKEUP_PIN_SCHEMA = cv.Schema( + { + cv.Required(CONF_PIN): cv.All( + pins.internal_gpio_input_pin_schema, validate_pin_number_esp32 + ), + cv.Optional(CONF_ON_WAKE): automation.validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Ext1WakeTrigger)} + ), + } +) + +# Entries that are not in the {pin: ..., on_wake: ...} form are treated as a +# bare pin config (the original syntax, e.g. a plain "GPIO5" or {number: 5}). +validate_ext1_wakeup_pin = cv.maybe_simple_value(EXT1_WAKEUP_PIN_SCHEMA, key=CONF_PIN) + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -282,8 +308,7 @@ CONFIG_SCHEMA = cv.All( cv.Schema( { cv.Required(CONF_PINS): cv.ensure_list( - pins.internal_gpio_input_pin_schema, - validate_pin_number_esp32, + validate_ext1_wakeup_pin, ), cv.Required(CONF_MODE): cv.All( cv.enum(EXT1_WAKEUP_MODES, upper=True), @@ -292,6 +317,12 @@ CONFIG_SCHEMA = cv.All( } ), ), + cv.Optional(CONF_ON_WAKE): cv.All( + cv.only_on([PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_BK72XX]), + automation.validate_automation( + {cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger)} + ), + ), cv.Optional(CONF_TOUCH_WAKEUP): cv.All( cv.only_on_esp32, esp32.only_on_variant( @@ -323,7 +354,7 @@ async def to_code(config): if CONF_WAKEUP_PIN in config: pins_as_list = config.get(CONF_WAKEUP_PIN, []) if CORE.is_bk72xx: - cg.add(var.init_wakeup_pins_(len(pins_as_list))) + cg.add(var.init_wakeup_pins(len(pins_as_list))) for item in pins_as_list: cg.add( var.add_wakeup_pin( @@ -362,16 +393,27 @@ async def to_code(config): ) cg.add(var.set_run_duration(wakeup_cause_to_run_duration)) - if CONF_ESP32_EXT1_WAKEUP in config: - conf = config[CONF_ESP32_EXT1_WAKEUP] + if (ext1_conf := config.get(CONF_ESP32_EXT1_WAKEUP)) is not None: mask = 0 - for pin in conf[CONF_PINS]: - mask |= 1 << pin[CONF_NUMBER] + for pin_conf in ext1_conf[CONF_PINS]: + number = pin_conf[CONF_PIN][CONF_NUMBER] + mask |= 1 << number + for wake_conf in pin_conf.get(CONF_ON_WAKE, []): + trigger = cg.new_Pvariable(wake_conf[CONF_TRIGGER_ID], number) + await cg.register_component(trigger, wake_conf) + await automation.build_automation(trigger, [], wake_conf) + cg.add_define("USE_DEEP_SLEEP_ON_WAKE") struct = cg.StructInitializer( - Ext1Wakeup, ("mask", mask), ("wakeup_mode", conf[CONF_MODE]) + Ext1Wakeup, ("mask", mask), ("wakeup_mode", ext1_conf[CONF_MODE]) ) cg.add(var.set_ext1_wakeup(struct)) + for wake_conf in config.get(CONF_ON_WAKE, []): + trigger = cg.new_Pvariable(wake_conf[CONF_TRIGGER_ID]) + await cg.register_component(trigger, wake_conf) + await automation.build_automation(trigger, [(WakeupCause, "cause")], wake_conf) + cg.add_define("USE_DEEP_SLEEP_ON_WAKE") + if CONF_TOUCH_WAKEUP in config: cg.add(var.set_touch_wakeup(config[CONF_TOUCH_WAKEUP])) if CORE.using_zephyr and "zigbee" not in CORE.loaded_integrations: diff --git a/esphome/components/deep_sleep/deep_sleep_bk72xx.cpp b/esphome/components/deep_sleep/deep_sleep_bk72xx.cpp index 8dca32689b..73e0331c76 100644 --- a/esphome/components/deep_sleep/deep_sleep_bk72xx.cpp +++ b/esphome/components/deep_sleep/deep_sleep_bk72xx.cpp @@ -7,6 +7,21 @@ namespace esphome::deep_sleep { static const char *const TAG = "deep_sleep.bk72xx"; +#ifdef USE_DEEP_SLEEP_ON_WAKE +WakeupCause get_wakeup_cause() { + switch (lt_get_reboot_reason()) { + case REBOOT_REASON_SLEEP_GPIO: + return WAKEUP_CAUSE_GPIO; + case REBOOT_REASON_SLEEP_RTC: + return WAKEUP_CAUSE_TIMER; + case REBOOT_REASON_SLEEP_USB: + return WAKEUP_CAUSE_UNKNOWN; + default: + return WAKEUP_CAUSE_NONE; + } +} +#endif // USE_DEEP_SLEEP_ON_WAKE + optional DeepSleepComponent::get_run_duration_() const { return this->run_duration_; } void DeepSleepComponent::dump_config_platform_() { @@ -15,15 +30,15 @@ void DeepSleepComponent::dump_config_platform_() { } } -bool DeepSleepComponent::pin_prevents_sleep_(WakeUpPinItem &pinItem) const { - return (pinItem.wakeup_pin_mode == WAKEUP_PIN_MODE_KEEP_AWAKE && pinItem.wakeup_pin != nullptr && - !this->sleep_duration_.has_value() && (pinItem.wakeup_level == get_real_pin_state_(*pinItem.wakeup_pin))); +bool DeepSleepComponent::pin_prevents_sleep_(WakeUpPinItem &pin_item) const { + return (pin_item.wakeup_pin_mode == WAKEUP_PIN_MODE_KEEP_AWAKE && pin_item.wakeup_pin != nullptr && + !this->sleep_duration_.has_value() && (pin_item.wakeup_level == get_real_pin_state_(*pin_item.wakeup_pin))); } bool DeepSleepComponent::prepare_to_sleep_() { - if (wakeup_pins_.size() > 0) { + if (!this->wakeup_pins_.empty()) { for (WakeUpPinItem &item : this->wakeup_pins_) { - if (pin_prevents_sleep_(item)) { + if (this->pin_prevents_sleep_(item)) { // Defer deep sleep until inactive if (!this->next_enter_deep_sleep_) { this->status_set_warning(); @@ -44,7 +59,7 @@ void DeepSleepComponent::deep_sleep_() { item.wakeup_level = !item.wakeup_level; } } - ESP_LOGI(TAG, "Wake-up on P%u %s (%d)", item.wakeup_pin->get_pin(), item.wakeup_level ? "HIGH" : "LOW", + ESP_LOGI(TAG, "Wake-up on P%u %s (%" PRId32 ")", item.wakeup_pin->get_pin(), item.wakeup_level ? "HIGH" : "LOW", static_cast(item.wakeup_pin_mode)); } diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 896ed092aa..a620d52a02 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -60,6 +60,65 @@ struct WakeupCauseToRunDuration { #endif // USE_ESP32 +#ifdef USE_DEEP_SLEEP_ON_WAKE + +/// Why the device woke from deep sleep. Passed to on_wake automations. +enum WakeupCause : uint8_t { + /// The device did not wake from deep sleep (for example a cold boot, reset or OTA restart). + WAKEUP_CAUSE_NONE = 0, + /// The device woke from deep sleep, but the source could not be identified. + WAKEUP_CAUSE_UNKNOWN, + /// The device was woken by the sleep timer. + WAKEUP_CAUSE_TIMER, + /// The device was woken by a GPIO pin (wakeup_pin or esp32_ext1_wakeup). + WAKEUP_CAUSE_GPIO, + /// The device was woken by a touch pad. + WAKEUP_CAUSE_TOUCH, +}; + +/// Return why the device woke from deep sleep. Implemented per platform. +WakeupCause get_wakeup_cause(); + +/** Setup priority of on_wake triggers. + * + * Between restoring global variables (setup_priority::HARDWARE, 800) and on_boot automations at + * their default priority (600), so on_wake automations can update state (e.g. globals) that + * on_boot automations then use. + */ +inline constexpr float ON_WAKE_TRIGGER_SETUP_PRIORITY = 700.0f; + +/// Fires once on boot when the device woke from deep sleep, with the wakeup cause. +class WakeTrigger : public Trigger, public Component { + public: + void setup() override { + const WakeupCause cause = get_wakeup_cause(); + if (cause != WAKEUP_CAUSE_NONE) { + this->trigger(cause); + } + } + float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; } +}; + +#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3) +/// Fires once on boot when the device was woken from deep sleep by the given ext1 pin. +class Ext1WakeTrigger : public Trigger<>, public Component { + public: + explicit Ext1WakeTrigger(uint8_t pin) : pin_(pin) {} + void setup() override { + if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1 && + (esp_sleep_get_ext1_wakeup_status() & (1ULL << this->pin_))) { + this->trigger(); + } + } + float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; } + + protected: + uint8_t pin_; +}; +#endif + +#endif // USE_DEEP_SLEEP_ON_WAKE + template class EnterDeepSleepAction; template class PreventDeepSleepAction; @@ -84,7 +143,7 @@ class DeepSleepComponent final : public Component { #endif // USE_ESP32 #if defined(USE_BK72XX) - void init_wakeup_pins_(size_t capacity) { this->wakeup_pins_.init(capacity); } + void init_wakeup_pins(size_t capacity) { this->wakeup_pins_.init(capacity); } void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode) { this->wakeup_pins_.emplace_back(WakeUpPinItem{wakeup_pin, wakeup_pin_mode, !wakeup_pin->is_inverted()}); } @@ -132,7 +191,7 @@ class DeepSleepComponent final : public Component { bool should_teardown_(); #ifdef USE_BK72XX - bool pin_prevents_sleep_(WakeUpPinItem &pinItem) const; + bool pin_prevents_sleep_(WakeUpPinItem &pin_item) const; bool get_real_pin_state_(InternalGPIOPin &pin) const { return (pin.digital_read() ^ pin.is_inverted()); } #endif // USE_BK72XX diff --git a/esphome/components/deep_sleep/deep_sleep_esp32.cpp b/esphome/components/deep_sleep/deep_sleep_esp32.cpp index 7cb8e53efd..f64e1f37e1 100644 --- a/esphome/components/deep_sleep/deep_sleep_esp32.cpp +++ b/esphome/components/deep_sleep/deep_sleep_esp32.cpp @@ -30,6 +30,25 @@ namespace esphome::deep_sleep { static const char *const TAG = "deep_sleep"; +#ifdef USE_DEEP_SLEEP_ON_WAKE +WakeupCause get_wakeup_cause() { + switch (esp_sleep_get_wakeup_cause()) { + case ESP_SLEEP_WAKEUP_EXT0: + case ESP_SLEEP_WAKEUP_EXT1: + case ESP_SLEEP_WAKEUP_GPIO: + return WAKEUP_CAUSE_GPIO; + case ESP_SLEEP_WAKEUP_TIMER: + return WAKEUP_CAUSE_TIMER; + case ESP_SLEEP_WAKEUP_TOUCHPAD: + return WAKEUP_CAUSE_TOUCH; + case ESP_SLEEP_WAKEUP_UNDEFINED: + return WAKEUP_CAUSE_NONE; + default: + return WAKEUP_CAUSE_UNKNOWN; + } +} +#endif // USE_DEEP_SLEEP_ON_WAKE + optional DeepSleepComponent::get_run_duration_() const { if (this->wakeup_cause_to_run_duration_.has_value()) { esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause(); diff --git a/esphome/components/deep_sleep/deep_sleep_esp8266.cpp b/esphome/components/deep_sleep/deep_sleep_esp8266.cpp index 9239a7fb31..2b98f4b855 100644 --- a/esphome/components/deep_sleep/deep_sleep_esp8266.cpp +++ b/esphome/components/deep_sleep/deep_sleep_esp8266.cpp @@ -3,10 +3,27 @@ #include +#ifdef USE_DEEP_SLEEP_ON_WAKE +extern "C" { +#include +} +#endif + namespace esphome::deep_sleep { static const char *const TAG = "deep_sleep"; +#ifdef USE_DEEP_SLEEP_ON_WAKE +WakeupCause get_wakeup_cause() { + // The ESP8266 can only wake from deep sleep through the RTC timer (via GPIO16 -> RST). + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + if (ESP.getResetInfoPtr()->reason == REASON_DEEP_SLEEP_AWAKE) { + return WAKEUP_CAUSE_TIMER; + } + return WAKEUP_CAUSE_NONE; +} +#endif // USE_DEEP_SLEEP_ON_WAKE + optional DeepSleepComponent::get_run_duration_() const { return this->run_duration_; } void DeepSleepComponent::dump_config_platform_() {} diff --git a/esphome/components/ds248x/__init__.py b/esphome/components/ds248x/__init__.py new file mode 100644 index 0000000000..5a26ceab50 --- /dev/null +++ b/esphome/components/ds248x/__init__.py @@ -0,0 +1,112 @@ +from esphome import pins +import esphome.codegen as cg +from esphome.components import i2c +import esphome.config_validation as cv +from esphome.const import CONF_ID, CONF_SLEEP_PIN, CONF_TYPE + +CODEOWNERS = ["@tomwellnitz"] +MULTI_CONF = True +DEPENDENCIES = ["i2c"] + +CONF_DS248X_ID = "ds248x_id" +CONF_BUS_SLEEP = "bus_sleep" +CONF_HUB_SLEEP = "hub_sleep" +CONF_ACTIVE_PULLUP = "active_pullup" + +CONF_RESET_LOW_TIME = "reset_low_time" +CONF_MASTER_SAMPLE_TIME = "master_sample_time" +CONF_WRITE_0_LOW_TIME = "write_0_low_time" +CONF_RECOVERY_TIME = "recovery_time" +CONF_ACTIVE_PULLUP_RESISTANCE = "active_pullup_resistance" + +TYPE_DS2482_100 = "ds2482-100" +TYPE_DS2482_101 = "ds2482-101" +TYPE_DS2482_800 = "ds2482-800" +TYPE_DS2484 = "ds2484" + +CHANNEL_COUNTS = { + TYPE_DS2482_100: 1, + TYPE_DS2482_101: 1, + TYPE_DS2482_800: 8, + TYPE_DS2484: 1, +} + +ds248x_ns = cg.esphome_ns.namespace("ds248x") +DS248xComponent = ds248x_ns.class_("DS248xComponent", cg.Component, i2c.I2CDevice) + + +def _component_schema(*extras): + schema = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DS248xComponent), + cv.Optional(CONF_ACTIVE_PULLUP, default=False): cv.boolean, + } + ) + for extra in extras: + schema = schema.extend(extra) + return schema.extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x18)) + + +SLEEP_SCHEMA = { + cv.Optional(CONF_SLEEP_PIN): pins.internal_gpio_output_pin_schema, + cv.Optional(CONF_BUS_SLEEP, default=False): cv.boolean, + cv.Optional(CONF_HUB_SLEEP, default=False): cv.boolean, +} + +DS2484_SCHEMA = { + cv.Optional(CONF_RESET_LOW_TIME): cv.int_range(min=0, max=15), + cv.Optional(CONF_MASTER_SAMPLE_TIME): cv.int_range(min=0, max=15), + cv.Optional(CONF_WRITE_0_LOW_TIME): cv.int_range(min=0, max=15), + cv.Optional(CONF_RECOVERY_TIME): cv.int_range(min=0, max=15), + cv.Optional(CONF_ACTIVE_PULLUP_RESISTANCE): cv.enum( + { + # DS2484 Table 7: value codes 0-5 map to 500 ohm, 6-15 map to 1000 ohm. + "500ohm": 0, + "1000ohm": 6, + } + ), +} + +CONFIG_SCHEMA = cv.typed_schema( + { + TYPE_DS2482_100: _component_schema(), + TYPE_DS2482_101: _component_schema(SLEEP_SCHEMA), + TYPE_DS2482_800: _component_schema(), + TYPE_DS2484: _component_schema(SLEEP_SCHEMA, DS2484_SCHEMA), + }, + key=CONF_TYPE, + lower=True, +) + + +def get_channel_count(config): + return CHANNEL_COUNTS[config[CONF_TYPE]] + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await i2c.register_i2c_device(var, config) + + cg.add(var.set_active_pullup(config[CONF_ACTIVE_PULLUP])) + cg.add(var.set_channel_count(get_channel_count(config))) + + if CONF_BUS_SLEEP in config: + cg.add(var.set_bus_sleep(config[CONF_BUS_SLEEP])) + if CONF_HUB_SLEEP in config: + cg.add(var.set_hub_sleep(config[CONF_HUB_SLEEP])) + + if CONF_RESET_LOW_TIME in config: + cg.add(var.set_val_trstl(config[CONF_RESET_LOW_TIME])) + if CONF_MASTER_SAMPLE_TIME in config: + cg.add(var.set_val_tmsp(config[CONF_MASTER_SAMPLE_TIME])) + if CONF_WRITE_0_LOW_TIME in config: + cg.add(var.set_val_tw0l(config[CONF_WRITE_0_LOW_TIME])) + if CONF_RECOVERY_TIME in config: + cg.add(var.set_val_trec0(config[CONF_RECOVERY_TIME])) + if CONF_ACTIVE_PULLUP_RESISTANCE in config: + cg.add(var.set_val_rwpu(config[CONF_ACTIVE_PULLUP_RESISTANCE])) + + if CONF_SLEEP_PIN in config: + pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN]) + cg.add(var.set_sleep_pin(pin)) diff --git a/esphome/components/ds248x/ds248x.cpp b/esphome/components/ds248x/ds248x.cpp new file mode 100644 index 0000000000..c8f7395119 --- /dev/null +++ b/esphome/components/ds248x/ds248x.cpp @@ -0,0 +1,320 @@ +#include "ds248x.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome::ds248x { + +static const char *const TAG = "ds248x"; + +void DS248xComponent::setup() { + ESP_LOGCONFIG(TAG, "Setting up DS248x..."); + + // Wake up device if sleep pin is configured + if (this->sleep_pin_) { + this->sleep_pin_->setup(); + this->sleep_pin_->pin_mode(esphome::gpio::FLAG_OUTPUT); + this->sleep_pin_->digital_write(true); // Wake up + delay(1); // DS2482-101 Datasheet: tOSCWUP = 100μs (using 10x margin) + } + + // Probe device + ESP_LOGD(TAG, "Probing DS248x..."); + uint8_t status = 0; + if (this->read(&status, 1) == i2c::ERROR_OK) { + ESP_LOGD(TAG, "Device responded! Status: 0x%02x", status); + } else { + ESP_LOGW(TAG, "Device did not respond. Trying reset anyway..."); + } + + if (!this->device_reset_()) { + ESP_LOGW(TAG, "DS248x reset failed during setup!"); + } + + // Configure device + if (!this->device_configure_()) { + ESP_LOGE(TAG, "DS248x configuration failed!"); + this->mark_failed(); + return; + } + + // Reset to Channel 0 + this->select_channel(0); + + ESP_LOGI(TAG, "DS248x initialized successfully."); +} + +void DS248xComponent::on_shutdown() { + if (this->sleep_pin_ && (this->hub_sleep_ || this->bus_sleep_)) { + this->sleep_pin_->digital_write(false); // Sleep + } +} + +void DS248xComponent::dump_config() { + ESP_LOGCONFIG(TAG, "DS248x:"); + LOG_I2C_DEVICE(this); + ESP_LOGCONFIG(TAG, " Channel Count: %d", this->channel_count_); + ESP_LOGCONFIG(TAG, " Active Pullup: %s", YESNO(this->active_pullup_)); + if (this->ds2484_mode_) { + ESP_LOGCONFIG(TAG, " DS2484 Mode: enabled"); + } +} + +// --- Internal Helpers --- + +// Datasheet command durations are sub-2ms; allow a little margin before forcing recovery. +static constexpr uint32_t BUSY_TIMEOUT_MS = 5; + +bool DS248xComponent::set_read_pointer_(uint8_t ptr) { return this->write_byte(DS248X_COMMAND_SETREADPTR, ptr); } + +bool DS248xComponent::wait_busy_() { + uint32_t start = millis(); + do { + uint8_t status; + if (this->read(&status, 1) == i2c::ERROR_OK && !(status & DS248X_STATUS_BUSY)) + return true; + delayMicroseconds(100); + } while (millis() - start < BUSY_TIMEOUT_MS); + ESP_LOGW(TAG, "DS248x busy timeout"); + bool recovered = this->device_reset_() && this->device_configure_(); + this->current_channel_ = -1; + if (!recovered) { + ESP_LOGE(TAG, "DS248x recovery failed after busy timeout"); + this->mark_failed(); + } + return false; +} + +bool DS248xComponent::device_reset_() { + ESP_LOGD(TAG, "Resetting device..."); + uint8_t cmd = DS248X_COMMAND_RESET; + if (this->write(&cmd, 1) != i2c::ERROR_OK) + return false; + + uint8_t status; + if (this->read(&status, 1) != i2c::ERROR_OK) + return false; + + if (!(status & DS248X_STATUS_RST)) { + ESP_LOGW(TAG, "Device reset failed (RST bit not set)"); + return false; + } + + this->current_channel_ = -1; + return true; +} + +bool DS248xComponent::device_configure_() { + ESP_LOGD(TAG, "Configuring device..."); + + if (!this->write_config_()) { + ESP_LOGW(TAG, "Config write/verify failed"); + return false; + } + + ESP_LOGD(TAG, "Configured successfully"); + + // DS2484 Configuration + if (this->ds2484_mode_) { + if (this->ds2484_trstl_ != DS2484_PARAM_UNSET && + !this->configure_ds2484_port_(DS2484_PORT_PARAM_TRSTL, this->ds2484_trstl_)) + return false; + if (this->ds2484_tmsp_ != DS2484_PARAM_UNSET && + !this->configure_ds2484_port_(DS2484_PORT_PARAM_TMSP, this->ds2484_tmsp_)) + return false; + if (this->ds2484_tw0l_ != DS2484_PARAM_UNSET && + !this->configure_ds2484_port_(DS2484_PORT_PARAM_TW0L, this->ds2484_tw0l_)) + return false; + if (this->ds2484_trec0_ != DS2484_PARAM_UNSET && + !this->configure_ds2484_port_(DS2484_PORT_PARAM_TREC0, this->ds2484_trec0_)) + return false; + if (this->ds2484_rwpu_ != DS2484_PARAM_UNSET && + !this->configure_ds2484_port_(DS2484_PORT_PARAM_RWPU, this->ds2484_rwpu_)) + return false; + } + + return true; +} + +bool DS248xComponent::configure_ds2484_port_(uint8_t param, uint8_t val) { + uint8_t cmd = DS2484_COMMAND_ADJUSTPORT; + // Control Byte format (DS2484 Table 6): P[2:0] in bits 7:5, OD in bit 4, VAL[3:0] in bits 3:0 + uint8_t data = ((param & 0x07) << 5) | (val & 0x0F); + + // The DS2484 always acknowledges the Adjust 1-Wire Port control byte (datasheet "Adjust + // 1-Wire Port"), so a successful write confirms the update. We deliberately do not read + // back to verify: a single read of the Port Configuration register always returns the + // fixed 8-byte report starting at Byte 1 (tRSTL standard speed), not the parameter that + // was just written, so a per-parameter readback comparison would spuriously fail for + // tMSP/tW0L/tREC0/RWPU. + if (!this->write_byte(cmd, data)) { + ESP_LOGW(TAG, "DS2484 port config failed (param %d)", param); + return false; + } + + return this->set_read_pointer_(DS248X_POINTER_STATUS); +} + +bool DS248xComponent::write_config_() { + uint8_t config = 0; + if (this->active_pullup_) + config |= DS248X_CONFIG_ACTIVE_PULLUP; + + // The DS248x only accepts the config byte if the upper nibble is the one's-complement of the lower nibble. + uint8_t config_byte = (config & 0x0F) | ((~config & 0x0F) << 4); + + if (!this->write_byte(DS248X_COMMAND_WRITECONFIG, config_byte)) { + ESP_LOGW(TAG, "Failed to write config byte"); + return false; + } + + if (!this->set_read_pointer_(DS248X_POINTER_CONFIG)) { + return false; + } + + uint8_t read_config; + if (this->read(&read_config, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "Failed to read back config byte"); + return false; + } + + if ((read_config & 0x0F) != (config_byte & 0x0F)) { + ESP_LOGW(TAG, "Config mismatch! Wrote 0x%02x, Read 0x%02x", config_byte, read_config); + return false; + } + + return this->set_read_pointer_(DS248X_POINTER_STATUS); +} + +// --- Channel Selection --- + +// Channel select codes: write code -> expected read code +static constexpr uint8_t CHANNEL_WRITE_CODES[8] = {0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87}; +static constexpr uint8_t CHANNEL_READ_CODES[8] = {0xB8, 0xB1, 0xAA, 0xA3, 0x9C, 0x95, 0x8E, 0x87}; + +bool DS248xComponent::select_channel(uint8_t channel) { + if (this->channel_count_ <= 1) + return true; + if (channel >= this->channel_count_) + return false; + + if (this->current_channel_ == channel) + return true; + + if (!this->write_byte(DS248X_COMMAND_CHANNELSELECT, CHANNEL_WRITE_CODES[channel])) { + this->current_channel_ = -1; + return false; + } + + uint8_t read_code; + if (this->read(&read_code, 1) != i2c::ERROR_OK) { + this->current_channel_ = -1; + return false; + } + + if (read_code != CHANNEL_READ_CODES[channel]) { + ESP_LOGW(TAG, "Channel select failed! Expected 0x%02x, got 0x%02x", CHANNEL_READ_CODES[channel], read_code); + this->current_channel_ = -1; + return false; + } + + if (!this->set_read_pointer_(DS248X_POINTER_STATUS)) + return false; + + this->current_channel_ = channel; + return true; +} + +// --- 1-Wire Bus Operations --- + +bool DS248xComponent::ow_reset(bool &presence) { + if (!this->set_read_pointer_(DS248X_POINTER_STATUS)) + return false; + + uint8_t cmd = DS248X_COMMAND_RESETWIRE; + if (this->write(&cmd, 1) != i2c::ERROR_OK) + return false; + + if (!this->wait_busy_()) { + ESP_LOGW(TAG, "ow_reset: wait busy failed"); + return false; + } + + uint8_t status; + if (this->read(&status, 1) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "ow_reset: read status failed"); + return false; + } + + if (status & DS248X_STATUS_SD) { + ESP_LOGW(TAG, "Short detected on 1-Wire bus!"); + return false; + } + + presence = (status & DS248X_STATUS_PPD); + return true; +} + +bool DS248xComponent::ow_write_byte(uint8_t byte) { + if (!this->set_read_pointer_(DS248X_POINTER_STATUS)) + return false; + + if (!this->wait_busy_()) { + ESP_LOGW(TAG, "Device busy before writing byte 0x%02x", byte); + return false; + } + + uint8_t cmd[2] = {DS248X_COMMAND_WRITEBYTE, byte}; + if (this->write(cmd, 2) != i2c::ERROR_OK) { + ESP_LOGW(TAG, "I2C write failed for byte 0x%02x", byte); + return false; + } + + if (!this->wait_busy_()) { + ESP_LOGW(TAG, "Timeout waiting for write byte to complete!"); + return false; + } + + return true; +} + +bool DS248xComponent::ow_read_byte(uint8_t &byte) { + if (!this->set_read_pointer_(DS248X_POINTER_STATUS)) + return false; + + uint8_t cmd = DS248X_COMMAND_READBYTE; + if (this->write(&cmd, 1) != i2c::ERROR_OK) + return false; + + if (!this->wait_busy_()) + return false; + + if (!this->set_read_pointer_(DS248X_POINTER_DATA)) + return false; + + if (this->read(&byte, 1) != i2c::ERROR_OK) + return false; + + return true; +} + +bool DS248xComponent::search_triplet(bool search_direction, uint8_t &status) { + if (!this->set_read_pointer_(DS248X_POINTER_STATUS)) + return false; + + // DS248x Datasheet: 1-Wire Triplet command requires 2 bytes: + // Byte 1: Command code 0x78 + // Byte 2: Direction byte (bit 7 = V, search direction if discrepancy) + uint8_t buffer[2] = {DS248X_COMMAND_TRIPLET, static_cast(search_direction ? 0x80 : 0x00)}; + if (this->write(buffer, 2) != i2c::ERROR_OK) + return false; + + if (!this->wait_busy_()) + return false; + + if (this->read(&status, 1) != i2c::ERROR_OK) + return false; + + return true; +} + +} // namespace esphome::ds248x diff --git a/esphome/components/ds248x/ds248x.h b/esphome/components/ds248x/ds248x.h new file mode 100644 index 0000000000..0873ee0e2a --- /dev/null +++ b/esphome/components/ds248x/ds248x.h @@ -0,0 +1,133 @@ +#pragma once + +// DS248x I2C-to-1-Wire Bridge Family +// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-100.pdf +// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2482-800.pdf +// Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ds2484.pdf + +#include "esphome/core/component.h" +#include "esphome/core/hal.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome::ds248x { + +// DS248x I2C Commands +static constexpr uint8_t DS248X_COMMAND_RESET = 0xF0; +static constexpr uint8_t DS248X_COMMAND_SETREADPTR = 0xE1; +static constexpr uint8_t DS248X_COMMAND_WRITECONFIG = 0xD2; +static constexpr uint8_t DS248X_COMMAND_CHANNELSELECT = 0xC3; +static constexpr uint8_t DS248X_COMMAND_RESETWIRE = 0xB4; +static constexpr uint8_t DS248X_COMMAND_WRITEBYTE = 0xA5; +static constexpr uint8_t DS248X_COMMAND_READBYTE = 0x96; +static constexpr uint8_t DS248X_COMMAND_TRIPLET = 0x78; +static constexpr uint8_t DS2484_COMMAND_ADJUSTPORT = 0xC3; + +// DS2484 "Adjust 1-Wire Port" parameter codes (datasheet Table 6, control byte P[2:0]) +static constexpr uint8_t DS2484_PORT_PARAM_TRSTL = 0x0; +static constexpr uint8_t DS2484_PORT_PARAM_TMSP = 0x1; +static constexpr uint8_t DS2484_PORT_PARAM_TW0L = 0x2; +static constexpr uint8_t DS2484_PORT_PARAM_TREC0 = 0x3; +static constexpr uint8_t DS2484_PORT_PARAM_RWPU = 0x4; + +// DS248x Status Register Bits +static constexpr uint8_t DS248X_STATUS_BUSY = 0x01; +static constexpr uint8_t DS248X_STATUS_PPD = 0x02; +static constexpr uint8_t DS248X_STATUS_SD = 0x04; +static constexpr uint8_t DS248X_STATUS_RST = 0x10; +static constexpr uint8_t DS248X_STATUS_SBR = 0x20; +static constexpr uint8_t DS248X_STATUS_TSB = 0x40; +static constexpr uint8_t DS248X_STATUS_DIR = 0x80; + +// DS248x Register Pointers +static constexpr uint8_t DS248X_POINTER_STATUS = 0xF0; +static constexpr uint8_t DS248X_POINTER_DATA = 0xE1; +static constexpr uint8_t DS248X_POINTER_CONFIG = 0xC3; + +// DS248x Configuration Bits +static constexpr uint8_t DS248X_CONFIG_ACTIVE_PULLUP = 0x01; + +/** + * @brief DS248x I2C-to-1-Wire Bridge Component. + * + * This component manages the DS248x chip (DS2482-100, DS2482-800, DS2484). + * It provides low-level 1-Wire bus operations via I2C. + * + * Usage: Configure DS248xOneWireBus instances for each channel. + * These buses implement the one_wire::OneWireBus interface for compatibility + * with all existing 1-Wire device components (dallas_temp, etc.). + */ +class DS248xComponent : public Component, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + void on_shutdown() override; + float get_setup_priority() const override { return setup_priority::BUS; } + + void set_sleep_pin(InternalGPIOPin *pin) { this->sleep_pin_ = pin; } + void set_bus_sleep(bool enabled) { this->bus_sleep_ = enabled; } + void set_hub_sleep(bool enabled) { this->hub_sleep_ = enabled; } + void set_channel_count(uint8_t count) { this->channel_count_ = count; } + void set_active_pullup(bool enabled) { this->active_pullup_ = enabled; } + + // DS2484 Timing Parameters + void set_val_trstl(uint8_t val) { + this->ds2484_trstl_ = val; + this->ds2484_mode_ = true; + } + void set_val_tmsp(uint8_t val) { + this->ds2484_tmsp_ = val; + this->ds2484_mode_ = true; + } + void set_val_tw0l(uint8_t val) { + this->ds2484_tw0l_ = val; + this->ds2484_mode_ = true; + } + void set_val_trec0(uint8_t val) { + this->ds2484_trec0_ = val; + this->ds2484_mode_ = true; + } + void set_val_rwpu(uint8_t val) { + this->ds2484_rwpu_ = val; + this->ds2484_mode_ = true; + } + + /// Get the channel count (1 for DS2482-100/DS2484, 8 for DS2482-800) + uint8_t get_channel_count() const { return this->channel_count_; } + + // --- Core 1-Wire API (used by DS248xOneWireBus) --- + bool select_channel(uint8_t channel); + bool ow_reset(bool &presence); + bool ow_write_byte(uint8_t byte); + bool ow_read_byte(uint8_t &byte); + + // --- Search support (used by DS248xOneWireBus) --- + bool search_triplet(bool search_direction, uint8_t &status); + + protected: + InternalGPIOPin *sleep_pin_{nullptr}; + uint8_t channel_count_ = 1; + bool bus_sleep_{false}; + bool hub_sleep_{false}; + bool active_pullup_ = false; + + // DS2484 Config + bool ds2484_mode_ = false; + static constexpr uint8_t DS2484_PARAM_UNSET = 0xFF; + uint8_t ds2484_trstl_{DS2484_PARAM_UNSET}; + uint8_t ds2484_tmsp_{DS2484_PARAM_UNSET}; + uint8_t ds2484_tw0l_{DS2484_PARAM_UNSET}; + uint8_t ds2484_trec0_{DS2484_PARAM_UNSET}; + uint8_t ds2484_rwpu_{DS2484_PARAM_UNSET}; + + int8_t current_channel_{-1}; + + // Internal helpers + bool set_read_pointer_(uint8_t ptr); + bool wait_busy_(); + bool device_reset_(); + bool device_configure_(); + bool configure_ds2484_port_(uint8_t param, uint8_t val); + bool write_config_(); +}; + +} // namespace esphome::ds248x diff --git a/esphome/components/ds248x/ds248x_one_wire_bus.cpp b/esphome/components/ds248x/ds248x_one_wire_bus.cpp new file mode 100644 index 0000000000..ed5b05bab7 --- /dev/null +++ b/esphome/components/ds248x/ds248x_one_wire_bus.cpp @@ -0,0 +1,171 @@ +#include "ds248x_one_wire_bus.h" +#include "ds248x.h" +#include "esphome/core/log.h" + +namespace esphome::ds248x { + +static const char *const TAG = "ds248x.one_wire"; + +void DS248xOneWireBus::setup() { + ESP_LOGCONFIG(TAG, "Setting up DS248x 1-Wire Bus (Channel %d)...", this->channel_); + + // Parent setup happens in DS248xComponent::setup() + // We just need to scan for devices on this channel + if (!this->ensure_channel_()) { + ESP_LOGE(TAG, "Failed to select channel %d during setup", this->channel_); + this->mark_failed(); + return; + } + + // Perform device search on this channel + this->search(); + + ESP_LOGCONFIG(TAG, "Found %zu devices on channel %d", this->devices_.size(), this->channel_); +} + +void DS248xOneWireBus::dump_config() { + ESP_LOGCONFIG(TAG, "DS248x 1-Wire Bus (Channel %d):", this->channel_); + this->dump_devices_(TAG); +} + +bool DS248xOneWireBus::ensure_channel_() { + if (this->parent_ == nullptr) { + ESP_LOGE(TAG, "Parent not set!"); + return false; + } + return this->parent_->select_channel(this->channel_); +} + +int DS248xOneWireBus::reset_int() { + if (!this->ensure_channel_()) { + return -1; + } + + bool presence = false; + if (!this->parent_->ow_reset(presence)) { + return -1; + } + return presence ? 1 : 0; +} + +void DS248xOneWireBus::write8(uint8_t val) { + if (!this->ensure_channel_()) { + return; + } + if (!this->parent_->ow_write_byte(val)) { + ESP_LOGE(TAG, "Failed to write byte 0x%02X on channel %d", val, this->channel_); + } +} + +void DS248xOneWireBus::write64(uint64_t val) { + if (!this->ensure_channel_()) { + return; + } + for (uint8_t i = 0; i < 8; i++) { + uint8_t byte = static_cast(val >> (i * 8)); + if (!this->parent_->ow_write_byte(byte)) { + ESP_LOGE(TAG, "Failed to write byte %d/8 (0x%02X) on channel %d - aborting write64", i + 1, byte, this->channel_); + return; // Stop writing to prevent sending corrupted data + } + } +} + +uint8_t DS248xOneWireBus::read8() { + if (!this->ensure_channel_()) { + return 0; + } + uint8_t value = 0; + if (!this->parent_->ow_read_byte(value)) { + ESP_LOGE(TAG, "Failed to read byte on channel %d", this->channel_); + } + return value; +} + +uint64_t DS248xOneWireBus::read64() { + if (!this->ensure_channel_()) { + return 0; + } + uint64_t value = 0; + for (uint8_t i = 0; i < 8; i++) { + uint8_t byte = 0; + if (!this->parent_->ow_read_byte(byte)) { + ESP_LOGE(TAG, "Failed to read byte %d/8 on channel %d - returning partial data", i + 1, this->channel_); + return value; // Return partial data to avoid blocking, caller should validate + } + value |= (static_cast(byte) << (i * 8)); + } + return value; +} + +void DS248xOneWireBus::reset_search() { + this->search_last_discrepancy_ = 0; + this->search_last_device_flag_ = false; + this->search_address_ = 0; +} + +uint64_t DS248xOneWireBus::search_int() { + if (!this->ensure_channel_()) { + return 0; + } + + if (this->search_last_device_flag_) { + return 0; + } + + uint8_t last_zero = 0; + uint64_t address = this->search_address_; + + // Iterate through all 64 bits + for (uint8_t bit_number = 1; bit_number <= 64; bit_number++) { + uint64_t bit_mask = 1ULL << (bit_number - 1); + + // Determine search direction + bool search_direction; + if (bit_number < this->search_last_discrepancy_) { + search_direction = (address & bit_mask) != 0; + } else { + search_direction = (bit_number == this->search_last_discrepancy_); + } + + // Perform triplet operation + uint8_t status = 0; + if (!this->parent_->search_triplet(search_direction, status)) { + ESP_LOGW(TAG, "1-Wire triplet failed at bit %d on channel %d - aborting search", bit_number, this->channel_); + this->reset_search(); + return 0; + } + + bool id_bit = (status & DS248X_STATUS_SBR) != 0; + bool cmp_id_bit = (status & DS248X_STATUS_TSB) != 0; + bool dir_taken = (status & DS248X_STATUS_DIR) != 0; + + if (id_bit && cmp_id_bit) { + // No devices participating + this->reset_search(); + return 0; + } + + if (!id_bit && !cmp_id_bit && !dir_taken) { + // Discrepancy, went 0 - record position + last_zero = bit_number; + } + + // Update address based on direction taken + if (dir_taken) { + address |= bit_mask; + } else { + address &= ~bit_mask; + } + } + + // Search successful + this->search_last_discrepancy_ = last_zero; + if (last_zero == 0) { + this->search_last_device_flag_ = true; + } + this->search_address_ = address; + + return address; +} + +} // namespace esphome::ds248x diff --git a/esphome/components/ds248x/ds248x_one_wire_bus.h b/esphome/components/ds248x/ds248x_one_wire_bus.h new file mode 100644 index 0000000000..0591796d60 --- /dev/null +++ b/esphome/components/ds248x/ds248x_one_wire_bus.h @@ -0,0 +1,57 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/one_wire/one_wire_bus.h" + +namespace esphome::ds248x { + +class DS248xComponent; + +/** + * @brief OneWireBus implementation for DS248x I2C-to-1-Wire bridges. + * + * This class wraps the DS248xComponent to provide the one_wire::OneWireBus interface, + * enabling compatibility with all existing 1-Wire device components (dallas_temp, etc.). + * + * For DS2482-800, multiple instances of this class can be created (one per channel). + * For DS2482-100/DS2484, a single instance is used. + */ +class DS248xOneWireBus : public one_wire::OneWireBus, public Component { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::BUS - 1.0f; } + + /// Set the parent DS248x component + void set_parent(DS248xComponent *parent) { this->parent_ = parent; } + + /// Set the 1-Wire channel (0-7, only relevant for DS2482-800) + void set_channel(uint8_t channel) { this->channel_ = channel; } + + /// Get the channel number + uint8_t get_channel() const { return this->channel_; } + + // OneWireBus interface implementation + int reset_int() override; + void write8(uint8_t val) override; + void write64(uint64_t val) override; + uint8_t read8() override; + uint64_t read64() override; + + protected: + void reset_search() override; + uint64_t search_int() override; + + /// Select the channel on the DS248x before any 1-Wire operation + bool ensure_channel_(); + + DS248xComponent *parent_{nullptr}; + uint8_t channel_{0}; + + // Search state + uint64_t search_address_{0}; + uint8_t search_last_discrepancy_{0}; + bool search_last_device_flag_{false}; +}; + +} // namespace esphome::ds248x diff --git a/esphome/components/ds248x/one_wire.py b/esphome/components/ds248x/one_wire.py new file mode 100644 index 0000000000..19861eae36 --- /dev/null +++ b/esphome/components/ds248x/one_wire.py @@ -0,0 +1,56 @@ +"""DS248x 1-Wire Bus Platform. + +This platform creates one_wire bus instances backed by a DS248x I2C-to-1-Wire bridge. +It supports DS2482-100/101 (single channel), DS2482-800 (8 channels), and DS2484 (single channel). + +For multi-channel devices (DS2482-800), create one platform entry per channel. +Each entry becomes a separate one_wire bus that can be used by dallas_temp and other 1-Wire devices. +""" + +from esphome import final_validate as fv +import esphome.codegen as cg +from esphome.components.one_wire import OneWireBus +import esphome.config_validation as cv +from esphome.const import CONF_CHANNEL, CONF_ID + +from . import CONF_DS248X_ID, DS248xComponent, ds248x_ns, get_channel_count + +CODEOWNERS = ["@tomwellnitz"] +DEPENDENCIES = ["ds248x"] + +DS248xOneWireBus = ds248x_ns.class_("DS248xOneWireBus", OneWireBus, cg.Component) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(DS248xOneWireBus), + cv.GenerateID(CONF_DS248X_ID): cv.use_id(DS248xComponent), + cv.Optional(CONF_CHANNEL, default=0): cv.int_range(min=0, max=7), + } +).extend(cv.COMPONENT_SCHEMA) + + +def _final_validate(config): + """Validate that the channel is within the parent's channel count.""" + fconf = fv.full_config.get() + path = fconf.get_path_for_id(config[CONF_DS248X_ID])[:-1] + parent_config = fconf.get_config_for_path(path) + channel_count = get_channel_count(parent_config) + channel = config[CONF_CHANNEL] + + if channel >= channel_count: + raise cv.Invalid( + f"Channel {channel} is invalid for DS248x with {channel_count} channel(s). " + f"Valid range: 0-{channel_count - 1}" + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + parent = await cg.get_variable(config[CONF_DS248X_ID]) + cg.add(var.set_parent(parent)) + cg.add(var.set_channel(config[CONF_CHANNEL])) diff --git a/esphome/components/emc2101/emc2101.cpp b/esphome/components/emc2101/emc2101.cpp index 464f49fe51..f46082f5e7 100644 --- a/esphome/components/emc2101/emc2101.cpp +++ b/esphome/components/emc2101/emc2101.cpp @@ -145,9 +145,9 @@ float Emc2101Component::get_external_temperature() { return NAN; } - // join msb and lsb (5 least significant bits are not used) - uint16_t raw = (msb << 8 | lsb) >> 5; - return raw * 0.125; + // join msb and lsb (5 least significant bits are not used); msb is signed, so read as int16_t + int16_t raw = static_cast((msb << 8) | lsb) >> 5; + return raw * 0.125f; } float Emc2101Component::get_speed() { diff --git a/esphome/components/epaper_spi/colorconv.h b/esphome/components/epaper_spi/colorconv.h index d4ffd034a1..7b7c48c0b0 100644 --- a/esphome/components/epaper_spi/colorconv.h +++ b/esphome/components/epaper_spi/colorconv.h @@ -81,4 +81,134 @@ constexpr NATIVE_COLOR color_to_bwr(Color color, NATIVE_COLOR hw_black, NATIVE_C return color_to_bwyr(color, hw_black, hw_white, /*hw_yellow=*/hw_white, hw_red); } +/** Map RGB color to discrete BWYRGB hex 6 color key + * + * Divides the RGB cube into 8 corners by which components are "on" (over 128), same as + * color_to_bwyr, but also resolves the green and blue corners instead of folding them into + * white/black. + * + * @tparam NATIVE_COLOR Type of native hardware color values + * @param color RGB color to convert from + * @param hw_black Native value for black + * @param hw_white Native value for white + * @param hw_yellow Native value for yellow + * @param hw_red Native value for red + * @param hw_green Native value for green + * @param hw_blue Native value for blue + * @return Converted native hardware color value + * @internal Constexpr. Does not depend on side effects ("pure"). + */ +template +constexpr NATIVE_COLOR color_to_bwyrgb(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, + NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green, + NATIVE_COLOR hw_blue) { + const auto [min_rgb, max_rgb] = std::minmax({color.r, color.g, color.b}); + + if ((max_rgb - min_rgb) < COLORCONV_GRAY_THRESHOLD) { + if ((static_cast(color.r) + color.g + color.b) > 382) { + return hw_white; + } + return hw_black; + } + + const bool r_on = (color.r > 128); + const bool g_on = (color.g > 128); + const bool b_on = (color.b > 128); + + if (r_on && g_on && !b_on) { + return hw_yellow; + } + if (r_on && !g_on && !b_on) { + return hw_red; + } + if (!r_on && g_on && !b_on) { + return hw_green; + } + if (!r_on && !g_on && b_on) { + return hw_blue; + } + // Handle "impure" colors (cyan, magenta) by folding into the closest primary. + if (!r_on && g_on && b_on) { + return hw_green; // cyan + } + if (r_on && !g_on) { + return hw_red; // magenta + } + if (r_on) { + // All high (but not gray) -> white + return hw_white; + } + // !r_on && !g_on && !b_on + // All low (but not gray) -> black + return hw_black; +} + +/** Map RGB color to discrete BWYRGBO hex 7 color key + * + * Same corner logic as color_to_bwyrgb, except the red/yellow corner is split three ways + * instead of two, for panels with a dedicated orange ink. + * + * @tparam NATIVE_COLOR Type of native hardware color values + * @param color RGB color to convert from + * @param hw_black Native value for black + * @param hw_white Native value for white + * @param hw_yellow Native value for yellow + * @param hw_red Native value for red + * @param hw_green Native value for green + * @param hw_blue Native value for blue + * @param hw_orange Native value for orange + * @return Converted native hardware color value + * @internal Constexpr. Does not depend on side effects ("pure"). + */ +template +constexpr NATIVE_COLOR color_to_bwyrgbo(Color color, NATIVE_COLOR hw_black, NATIVE_COLOR hw_white, + NATIVE_COLOR hw_yellow, NATIVE_COLOR hw_red, NATIVE_COLOR hw_green, + NATIVE_COLOR hw_blue, NATIVE_COLOR hw_orange) { + const auto [min_rgb, max_rgb] = std::minmax({color.r, color.g, color.b}); + + if ((max_rgb - min_rgb) < COLORCONV_GRAY_THRESHOLD) { + if ((static_cast(color.r) + color.g + color.b) > 382) { + return hw_white; + } + return hw_black; + } + + const bool r_on = (color.r > 128); + const bool g_on = (color.g > 128); + const bool b_on = (color.b > 128); + + if (r_on && !b_on) { + // Between red and yellow: split the gradient in three instead of two, since this panel has a + // dedicated orange ink. Named orange (e.g. 0xFFA500) has g close to the midpoint, so the + // plain g_on (>128) threshold used by color_to_bwyrgb can't tell it apart from yellow. + if (color.g > 170) { + return hw_yellow; + } + if (color.g > 85) { + return hw_orange; + } + return hw_red; + } + if (!r_on && g_on && !b_on) { + return hw_green; + } + if (!r_on && !g_on && b_on) { + return hw_blue; + } + // Handle "impure" colors (cyan, magenta) by folding into the closest primary. + if (!r_on && g_on && b_on) { + return hw_green; // cyan + } + if (r_on && !g_on) { + return hw_red; // magenta + } + if (r_on) { + // All high (but not gray) -> white + return hw_white; + } + // !r_on && !g_on && !b_on + // All low (but not gray) -> black + return hw_black; +} + } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_4bpp.cpp b/esphome/components/epaper_spi/epaper_spi_4bpp.cpp new file mode 100644 index 0000000000..820dc3c77a --- /dev/null +++ b/esphome/components/epaper_spi/epaper_spi_4bpp.cpp @@ -0,0 +1,82 @@ +#include "epaper_spi_4bpp.h" + +#include "esphome/core/log.h" + +namespace esphome::epaper_spi { + +static constexpr const char *const TAG = "epaper_spi.4bpp"; + +void EPaper4bpp::fill(Color color) { + // If clipping is active, fall back to base implementation + if (this->get_clipping().is_set()) { + EPaperBase::fill(color); + return; + } + + auto pixel_color = this->color_to_native(color); + + // We store 2 pixels per byte + this->buffer_.fill(pixel_color + (pixel_color << 4)); + + // Whole buffer just changed; mark the entire canvas dirty. + this->x_low_ = 0; + this->y_low_ = 0; + this->x_high_ = this->width_; + this->y_high_ = this->height_; +} + +void EPaper4bpp::clear() { + // clear buffer to white, just like real paper. + this->fill(COLOR_ON); +} + +void HOT EPaper4bpp::draw_pixel_at(int x, int y, Color color) { + if (!this->rotate_coordinates_(x, y)) + return; + auto pixel_bits = this->color_to_native(color); + uint32_t pixel_position = x + y * this->get_width_internal(); + uint32_t byte_position = pixel_position / 2; + auto original = this->buffer_[byte_position]; + if ((pixel_position & 1) != 0) { + this->buffer_[byte_position] = (original & 0xF0) | pixel_bits; + } else { + this->buffer_[byte_position] = (original & 0x0F) | (pixel_bits << 4); + } +} + +bool HOT EPaper4bpp::transfer_data() { + const uint32_t start_time = App.get_loop_component_start_time(); + const size_t buffer_length = this->buffer_length_; + if (this->current_data_index_ == 0) { + this->command(CMD_TRANSFER_DATA); + } + + size_t buf_idx = 0; + uint8_t bytes_to_send[MAX_TRANSFER_SIZE]; + while (this->current_data_index_ != buffer_length) { + bytes_to_send[buf_idx++] = this->buffer_[this->current_data_index_++]; + + if (buf_idx == sizeof bytes_to_send) { + this->start_data_(); + this->write_array(bytes_to_send, buf_idx); + this->disable(); + ESP_LOGV(TAG, "Wrote %d bytes at %ums", buf_idx, (unsigned) millis()); + buf_idx = 0; + + if (millis() - start_time > MAX_TRANSFER_TIME) { + // Let the main loop run and come back next loop + return false; + } + } + } + // Finished the entire dataset + if (buf_idx != 0) { + this->start_data_(); + this->write_array(bytes_to_send, buf_idx); + this->disable(); + } + this->current_data_index_ = 0; + return true; +} + +} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_4bpp.h b/esphome/components/epaper_spi/epaper_spi_4bpp.h new file mode 100644 index 0000000000..6eebe24c91 --- /dev/null +++ b/esphome/components/epaper_spi/epaper_spi_4bpp.h @@ -0,0 +1,35 @@ +#pragma once + +#include "epaper_spi.h" + +namespace esphome::epaper_spi { + +/** + * Intermediate base for panels with a 4-bit-per-pixel native buffer layout (2 pixels per byte). + * + * Owns buffer sizing, fill()/clear()/draw_pixel_at() and the chunked SPI transfer loop shared by + * this family of controllers. Concrete subclasses supply only their RGB -> 4-bit color mapping via + * color_to_native() plus their IC-specific power/refresh/sleep command sequences. + */ +class EPaper4bpp : public EPaperBase { + public: + EPaper4bpp(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, + size_t init_sequence_length) + : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { + this->buffer_length_ = width * height / 2; // 2 pixels per byte + } + + void fill(Color color) override; + void clear() override; + + protected: + void draw_pixel_at(int x, int y, Color color) override; + bool transfer_data() override; + + /// Map an RGB color to this panel's native 4-bit color key (low nibble). + virtual uint8_t color_to_native(Color color) = 0; + + static constexpr uint8_t CMD_TRANSFER_DATA = 0x10; +}; + +} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_inkplate6color.cpp b/esphome/components/epaper_spi/epaper_spi_inkplate6color.cpp new file mode 100644 index 0000000000..b81542b6c1 --- /dev/null +++ b/esphome/components/epaper_spi/epaper_spi_inkplate6color.cpp @@ -0,0 +1,47 @@ +// Reference: https://github.com/SolderedElectronics/Inkplate-Arduino-library (src/boards/Inkplate6COLOR) + +#include "epaper_spi_inkplate6color.h" +#include "colorconv.h" + +#include "esphome/core/log.h" + +namespace esphome::epaper_spi { + +static constexpr const char *const TAG = "epaper_spi.inkplate6color"; + +// Native hardware color codes for this panel's 4-bit color values. +enum Inkplate6ColorHex : uint8_t { + BLACK = 0, + WHITE = 1, + GREEN = 2, + BLUE = 3, + RED = 4, + YELLOW = 5, + ORANGE = 6, +}; + +uint8_t EPaperInkplate6Color::color_to_native(Color color) { + return color_to_bwyrgbo(color, BLACK, WHITE, YELLOW, RED, GREEN, BLUE, ORANGE); +} + +void EPaperInkplate6Color::power_on() { + ESP_LOGV(TAG, "Power on"); + this->command(0x04); +} + +void EPaperInkplate6Color::power_off() { + ESP_LOGV(TAG, "Power off"); + this->command(0x02); +} + +void EPaperInkplate6Color::refresh_screen(bool partial) { + ESP_LOGV(TAG, "Refresh"); // full refresh only; partial is unused + this->cmd_data(0x12, {0x00}); +} + +void EPaperInkplate6Color::deep_sleep() { + ESP_LOGV(TAG, "Deep sleep"); + this->cmd_data(0x07, {0xA5}); +} + +} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_inkplate6color.h b/esphome/components/epaper_spi/epaper_spi_inkplate6color.h new file mode 100644 index 0000000000..8b00552ff8 --- /dev/null +++ b/esphome/components/epaper_spi/epaper_spi_inkplate6color.h @@ -0,0 +1,22 @@ +#pragma once + +#include "epaper_spi_4bpp.h" + +namespace esphome::epaper_spi { + +// Soldered Inkplate 6COLOR: 600x448 7-color (black/white/green/blue/red/yellow/orange) e-paper, +// UC8159-family controller. +class EPaperInkplate6Color final : public EPaper4bpp { + public: + using EPaper4bpp::EPaper4bpp; + + protected: + uint8_t color_to_native(Color color) override; + + void refresh_screen(bool partial) override; + void power_on() override; + void power_off() override; + void deep_sleep() override; +}; + +} // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp b/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp index 1ef2dd12c3..f47d37d550 100644 --- a/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +++ b/esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp @@ -1,76 +1,23 @@ #include "epaper_spi_spectra_e6.h" - -#include +#include "colorconv.h" #include "esphome/core/log.h" namespace esphome::epaper_spi { static constexpr const char *const TAG = "epaper_spi.6c"; -static constexpr unsigned char GRAY_THRESHOLD = 50; -enum E6Color { - BLACK, - WHITE, - YELLOW, - RED, - SKIP_1, - BLUE, - GREEN, - CYAN, - SKIP_2, +// Native hardware color codes for this panel's 4-bit color values. +enum E6Color : uint8_t { + BLACK = 0, + WHITE = 1, + YELLOW = 2, + RED = 3, + BLUE = 5, + GREEN = 6, }; -static uint8_t color_to_hex(Color color) { - // --- Step 1: Check for Grayscale (Black or White) --- - // We define "grayscale" as a color where the min and max components - // are close to each other. - unsigned char max_rgb = std::max({color.r, color.g, color.b}); - unsigned char min_rgb = std::min({color.r, color.g, color.b}); - - if ((max_rgb - min_rgb) < GRAY_THRESHOLD) { - // It's a shade of gray. Map to BLACK or WHITE. - // We split the luminance at the halfway point (382 = (255*3)/2) - if ((static_cast(color.r) + color.g + color.b) > 382) { - return WHITE; - } - return BLACK; - } - // --- Step 2: Check for Primary/Secondary Colors --- - // If it's not gray, it's a color. We check which components are - // "on" (over 128) vs "off". This divides the RGB cube into 8 corners. - bool r_on = (color.r > 128); - bool g_on = (color.g > 128); - bool b_on = (color.b > 128); - - if (r_on && g_on && !b_on) { - return YELLOW; - } - if (r_on && !g_on && !b_on) { - return RED; - } - if (!r_on && g_on && !b_on) { - return GREEN; - } - if (!r_on && !g_on && b_on) { - return BLUE; - } - // Handle "impure" colors (Cyan, Magenta) - if (!r_on && g_on && b_on) { - // Cyan (G+B) -> Closest is Green or Blue. Pick Green. - return GREEN; - } - if (r_on && !g_on) { - // Magenta (R+B) -> Closest is Red or Blue. Pick Red. - return RED; - } - // Handle the remaining corners (White-ish, Black-ish) - if (r_on) { - // All high (but not gray) -> White - return WHITE; - } - // !r_on && !g_on && !b_on - // All low (but not gray) -> Black - return BLACK; +uint8_t EPaperSpectraE6::color_to_native(Color color) { + return color_to_bwyrgb(color, BLACK, WHITE, YELLOW, RED, GREEN, BLUE); } void EPaperSpectraE6::power_on() { @@ -92,71 +39,4 @@ void EPaperSpectraE6::deep_sleep() { ESP_LOGV(TAG, "Deep sleep"); this->cmd_data(0x07, {0xA5}); } - -void EPaperSpectraE6::fill(Color color) { - // If clipping is active, fall back to base implementation - if (this->get_clipping().is_set()) { - EPaperBase::fill(color); - return; - } - - auto pixel_color = color_to_hex(color); - - // We store 2 pixels per byte - this->buffer_.fill(pixel_color + (pixel_color << 4)); -} - -void EPaperSpectraE6::clear() { - // clear buffer to white, just like real paper. - this->fill(COLOR_ON); -} - -void HOT EPaperSpectraE6::draw_pixel_at(int x, int y, Color color) { - if (!this->rotate_coordinates_(x, y)) - return; - auto pixel_bits = color_to_hex(color); - uint32_t pixel_position = x + y * this->get_width_internal(); - uint32_t byte_position = pixel_position / 2; - auto original = this->buffer_[byte_position]; - if ((pixel_position & 1) != 0) { - this->buffer_[byte_position] = (original & 0xF0) | pixel_bits; - } else { - this->buffer_[byte_position] = (original & 0x0F) | (pixel_bits << 4); - } -} - -bool HOT EPaperSpectraE6::transfer_data() { - const uint32_t start_time = App.get_loop_component_start_time(); - const size_t buffer_length = this->buffer_length_; - if (this->current_data_index_ == 0) { - this->command(0x10); - } - - size_t buf_idx = 0; - uint8_t bytes_to_send[MAX_TRANSFER_SIZE]; - while (this->current_data_index_ != buffer_length) { - bytes_to_send[buf_idx++] = this->buffer_[this->current_data_index_++]; - - if (buf_idx == sizeof bytes_to_send) { - this->start_data_(); - this->write_array(bytes_to_send, buf_idx); - this->disable(); - ESP_LOGV(TAG, "Wrote %d bytes at %ums", buf_idx, (unsigned) millis()); - buf_idx = 0; - - if (millis() - start_time > MAX_TRANSFER_TIME) { - // Let the main loop run and come back next loop - return false; - } - } - } - // Finished the entire dataset - if (buf_idx != 0) { - this->start_data_(); - this->write_array(bytes_to_send, buf_idx); - this->disable(); - } - this->current_data_index_ = 0; - return true; -} } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/epaper_spi_spectra_e6.h b/esphome/components/epaper_spi/epaper_spi_spectra_e6.h index 9c251068af..e1e3d3d763 100644 --- a/esphome/components/epaper_spi/epaper_spi_spectra_e6.h +++ b/esphome/components/epaper_spi/epaper_spi_spectra_e6.h @@ -1,28 +1,20 @@ #pragma once -#include "epaper_spi.h" +#include "epaper_spi_4bpp.h" namespace esphome::epaper_spi { -class EPaperSpectraE6 final : public EPaperBase { +class EPaperSpectraE6 final : public EPaper4bpp { public: - EPaperSpectraE6(const char *name, uint16_t width, uint16_t height, const uint8_t *init_sequence, - size_t init_sequence_length) - : EPaperBase(name, width, height, init_sequence, init_sequence_length, DISPLAY_TYPE_COLOR) { - this->buffer_length_ = width * height / 2; // 2 pixels per byte - } - - void fill(Color color) override; - void clear() override; + using EPaper4bpp::EPaper4bpp; protected: + uint8_t color_to_native(Color color) override; + void refresh_screen(bool partial) override; void power_on() override; void power_off() override; void deep_sleep() override; - void draw_pixel_at(int x, int y, Color color) override; - - bool transfer_data() override; }; } // namespace esphome::epaper_spi diff --git a/esphome/components/epaper_spi/models/__init__.py b/esphome/components/epaper_spi/models/__init__.py index 2360b090ff..34e65061f3 100644 --- a/esphome/components/epaper_spi/models/__init__.py +++ b/esphome/components/epaper_spi/models/__init__.py @@ -15,7 +15,7 @@ class EpaperModel: self, name: str, class_name: str, - initsequence=None, + initsequence=(), **defaults, ): name = name.upper() diff --git a/esphome/components/epaper_spi/models/inkplate6color.py b/esphome/components/epaper_spi/models/inkplate6color.py new file mode 100644 index 0000000000..33fecab848 --- /dev/null +++ b/esphome/components/epaper_spi/models/inkplate6color.py @@ -0,0 +1,57 @@ +# Reference: https://github.com/SolderedElectronics/Inkplate-Arduino-library (src/boards/Inkplate6COLOR) + +from esphome.components.mipi import delay + +from . import EpaperModel + + +class Inkplate6ColorModel(EpaperModel): + def __init__(self, name, class_name="EPaperInkplate6Color", **kwargs): + super().__init__(name, class_name, **kwargs) + + # fmt: off + def get_init_sequence(self, config: dict): + width, height = self.get_dimensions(config) + return ( + (0x00, 0xEF, 0x08,), # panel setting + (0x01, 0x37, 0x00, 0x05, 0x05,), # power setting + (0x03, 0x00,), # power off sequence setting + (0x06, 0xC7, 0xC7, 0x1D,), # booster soft start + (0x41, 0x00,), # temperature sensor enable + (0x50, 0x37,), # VCOM and data interval + (0x60, 0x20,), # TCON setting + (0x61, width // 256, width % 256, height // 256, height % 256,), # resolution set + (0xE3, 0xAA,), # power saving + delay(100), + (0x50, 0x37,), # VCOM and data interval, resent once the power-saving setting settles + ) + + +# Native orientation is landscape (600x448). +inkplate6color = Inkplate6ColorModel( + "inkplate6color", + width=600, + height=448, + # Vendor library drives the panel at 2MHz; the controller doesn't reliably support faster rates. + data_rate="2MHz", + # Vendor library waits 200ms after releasing reset before talking to the panel. + reset_duration="200ms", + # A full 7-color refresh takes tens of seconds; disallow faster updates to avoid FSM update loops. + minimum_update_interval="30s", + # Panel's native buffer orientation is rotated 180 degrees relative to the logical + # rotation=0 orientation; confirmed on real hardware. + mirror_x=True, + mirror_y=True, + # Default GPIO pins for the on-board Inkplate 6COLOR wiring. + reset_pin=19, + dc_pin=33, + cs_pin=27, + busy_pin={ + "number": 32, + "inverted": True, # hardware: LOW=busy, HIGH=idle + "mode": { + "input": True, + "pullup": True, + }, + }, +) diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 9b568dd629..6491b9a5e6 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -814,14 +814,16 @@ def _is_framework_url(source: str) -> bool: # The default/recommended arduino framework version # - https://github.com/espressif/arduino-esp32/releases ARDUINO_FRAMEWORK_VERSION_LOOKUP = { - "recommended": cv.Version(3, 3, 9), - "latest": cv.Version(3, 3, 9), - "dev": cv.Version(3, 3, 9), + "recommended": cv.Version(3, 3, 11), + "latest": cv.Version(3, 3, 11), + "dev": cv.Version(3, 3, 11), } ARDUINO_PLATFORM_VERSION_LOOKUP = { cv.Version( 4, 0, 0, "alpha1" ): "https://github.com/pioarduino/platform-espressif32.git#prep_IDF6", + cv.Version(3, 3, 11): cv.Version(55, 3, 311), + cv.Version(3, 3, 10): cv.Version(55, 3, 39), cv.Version(3, 3, 9): cv.Version(55, 3, 39), cv.Version(3, 3, 8): cv.Version(55, 3, 38, "1"), cv.Version(3, 3, 7): cv.Version(55, 3, 37), @@ -844,6 +846,8 @@ ARDUINO_PLATFORM_VERSION_LOOKUP = { # See: https://github.com/pioarduino/esp-idf/releases ARDUINO_IDF_VERSION_LOOKUP = { cv.Version(4, 0, 0, "alpha1"): cv.Version(6, 0, 1), + cv.Version(3, 3, 11): cv.Version(5, 5, 5), + cv.Version(3, 3, 10): cv.Version(5, 5, 5), cv.Version(3, 3, 9): cv.Version(5, 5, 4), cv.Version(3, 3, 8): cv.Version(5, 5, 4), cv.Version(3, 3, 7): cv.Version(5, 5, 3, "1"), @@ -865,9 +869,9 @@ ARDUINO_IDF_VERSION_LOOKUP = { # The default/recommended esp-idf framework version # - https://github.com/espressif/esp-idf/releases ESP_IDF_FRAMEWORK_VERSION_LOOKUP = { - "recommended": cv.Version(5, 5, 4), - "latest": cv.Version(5, 5, 4), - "dev": cv.Version(5, 5, 4), + "recommended": cv.Version(5, 5, 5), + "latest": cv.Version(5, 5, 5), + "dev": cv.Version(5, 5, 5), } ESP_IDF_PLATFORM_VERSION_LOOKUP = { @@ -877,6 +881,7 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { cv.Version( 6, 0, 0 ): "https://github.com/pioarduino/platform-espressif32.git#prep_IDF6", + cv.Version(5, 5, 5): cv.Version(55, 3, 311), cv.Version(5, 5, 4): cv.Version(55, 3, 39), cv.Version(5, 5, 3, "1"): cv.Version(55, 3, 37), cv.Version(5, 5, 3): cv.Version(55, 3, 37), @@ -897,8 +902,8 @@ ESP_IDF_PLATFORM_VERSION_LOOKUP = { # The platform-espressif32 version # - https://github.com/pioarduino/platform-espressif32/releases PLATFORM_VERSION_LOOKUP = { - "recommended": cv.Version(55, 3, 39), - "latest": cv.Version(55, 3, 39), + "recommended": cv.Version(55, 3, 311), + "latest": cv.Version(55, 3, 311), "dev": "https://github.com/pioarduino/platform-espressif32.git#develop", } @@ -1026,7 +1031,9 @@ def _check_esp_idf_versions(config: ConfigType) -> ConfigType: def _validate_toolchain(value) -> Toolchain: - return Toolchain(cv.one_of(*(t.value for t in Toolchain), lower=True)(value)) + return Toolchain( + cv.one_of(Toolchain.PLATFORMIO, Toolchain.ESP_IDF, lower=True)(value) + ) def _resolve_toolchain(value: ConfigType) -> ConfigType: @@ -1234,6 +1241,13 @@ def final_validate(config): from .gpio import final_validate_pins + # Remove before 2027.2.0 + if CORE.using_toolchain_platformio: + _LOGGER.warning( + "The 'platformio' toolchain for ESP32 is deprecated and will be removed " + "in ESPHome 2027.2.0. Please use 'toolchain: esp-idf' instead." + ) + errs = [] conf_fw = config[CONF_FRAMEWORK] advanced = conf_fw[CONF_ADVANCED] @@ -3058,19 +3072,23 @@ def copy_files(): def _decode_pc(config, addr): - # _decode_pc runs from the api log processor's asyncio callback, which - # only catches EsphomeError. Any other exception escaping here tears down - # the protocol and triggers an infinite reconnect/replay loop. Convert - # toolchain-resolution errors (e.g. missing build dir / cmake cache) into - # EsphomeError so the caller can disable decoding cleanly. + # Convert toolchain-resolution errors (e.g. missing build dir / cmake + # cache) into EsphomeError. The api log processor stops decoding on any + # exception, so this is about the message it reports rather than about + # catching it at all: EsphomeError carries an explanation worth showing + # the user, where a raw OSError repr does not. if CORE.using_toolchain_esp_idf: from esphome.espidf import toolchain as idf_toolchain try: addr2line_path = idf_toolchain.get_addr2line_path() firmware_elf_path = idf_toolchain.get_elf_path() - except RuntimeError as err: + except (RuntimeError, OSError) as err: + # OSError covers a missing build directory or a cmake that isn't + # on PATH; both surface from the subprocess call, not as RuntimeError. raise EsphomeError(f"ESP-IDF toolchain not available: {err}") from err + if not firmware_elf_path.is_file(): + raise EsphomeError(f"Firmware ELF not found: {firmware_elf_path}") else: from esphome.platformio import toolchain @@ -3101,9 +3119,10 @@ def _parse_register(config, regex, line): STACKTRACE_ESP32_PC_RE = re.compile(r".*PC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7}).*") -STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r"EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_EXCVADDR_RE = re.compile(r".*EXCVADDR\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_PC_RE = re.compile(r"MEPC\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_ESP32_C3_RA_RE = re.compile(r"RA\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") +STACKTRACE_ESP32_C3_MTVAL_RE = re.compile(r".*MTVAL\s*:\s*(?:0x)?(4[0-9a-fA-F]{7})") STACKTRACE_BAD_ALLOC_RE = re.compile( r"^last failed alloc call: (4[0-9a-fA-F]{7})\((\d+)\)$" ) @@ -3121,9 +3140,10 @@ def process_stacktrace(config, line, backtrace_state): # ESP32 PC/EXCVADDR _parse_register(config, STACKTRACE_ESP32_PC_RE, line) _parse_register(config, STACKTRACE_ESP32_EXCVADDR_RE, line) - # ESP32-C3 PC/RA + # ESP32-C3 PC/RA/MTVAL _parse_register(config, STACKTRACE_ESP32_C3_PC_RE, line) _parse_register(config, STACKTRACE_ESP32_C3_RA_RE, line) + _parse_register(config, STACKTRACE_ESP32_C3_MTVAL_RE, line) # bad alloc match = re.match(STACKTRACE_BAD_ALLOC_RE, line) diff --git a/esphome/components/esp32/const.py b/esphome/components/esp32/const.py index 83fcfd233e..248f84c6bc 100644 --- a/esphome/components/esp32/const.py +++ b/esphome/components/esp32/const.py @@ -63,4 +63,10 @@ VARIANT_FRIENDLY = { VARIANT_ESP32S31: "ESP32-S31", } + +def variant_to_idf_target(variant: str) -> str: + """Map an esp32 variant name (e.g. "ESP32S3") to its ESP-IDF target name.""" + return variant.lower().replace("-", "") + + esp32_ns = cg.esphome_ns.namespace("esp32") diff --git a/esphome/components/esp32/crash_handler.cpp b/esphome/components/esp32/crash_handler.cpp index a7de48a6ee..4c0f430daf 100644 --- a/esphome/components/esp32/crash_handler.cpp +++ b/esphome/components/esp32/crash_handler.cpp @@ -122,7 +122,7 @@ static uint8_t IRAM_ATTR capture_riscv_backtrace(RvExcFrame *frame, uint32_t *ou // Magic is second to validate the data. Remaining fields can change between versions. // Version is uint32_t because it would be padded to 4 bytes anyway before the next // uint32_t field, so we use the full width rather than wasting 3 bytes of padding. -static constexpr uint32_t CRASH_DATA_VERSION = 2; +static constexpr uint32_t CRASH_DATA_VERSION = 3; struct RawCrashData { uint32_t version; uint32_t magic; @@ -132,7 +132,8 @@ struct RawCrashData { uint8_t exception; // panic_exception_t enum (FAULT/ABORT/IWDT/TWDT/DEBUG) uint8_t pseudo_excause; // Whether cause is a pseudo exception (Xtensa SoC-level panic) uint32_t backtrace[MAX_BACKTRACE]; - uint32_t cause; // Architecture-specific: exccause (Xtensa) or mcause (RISC-V) + uint32_t cause; // Architecture-specific: exccause (Xtensa) or mcause (RISC-V) + uint32_t fault_addr; // Faulting memory address: excvaddr (Xtensa) or mtval (RISC-V) uint8_t crashed_core; #if SOC_CPU_CORES_NUM > 1 static_assert(SOC_CPU_CORES_NUM == 2, "Dual-core logic assumes exactly 2 cores"); @@ -240,6 +241,16 @@ static const char *get_exception_reason() { nullptr, "LoadProhibited", "StoreProhibited", + nullptr, + nullptr, + "Cp0Dis", + "Cp1Dis", + "Cp2Dis", + "Cp3Dis", + "Cp4Dis", + "Cp5Dis", + "Cp6Dis", + "Cp7Dis", }; uint32_t cause = s_raw_crash_data.cause; if (cause < sizeof(REASON) / sizeof(REASON[0]) && REASON[cause] != nullptr) @@ -332,12 +343,24 @@ void crash_handler_log() { ESP_LOGE(TAG, "*** CRASH DETECTED ON PREVIOUS BOOT ***"); const char *reason = get_exception_reason(); if (reason != nullptr) { - ESP_LOGE(TAG, " Reason: %s - %s", get_exception_type(), reason); + ESP_LOGE(TAG, " Reason: %s - %s (cause %" PRIu32 ")", get_exception_type(), reason, s_raw_crash_data.cause); } else { ESP_LOGE(TAG, " Reason: %s", get_exception_type()); } ESP_LOGE(TAG, " Crashed core: %d", s_raw_crash_data.crashed_core); ESP_LOGE(TAG, " PC: 0x%08" PRIX32 " (fault location)", s_raw_crash_data.pc); + // Faulting memory address — only meaningful for real CPU faults, not + // aborts/watchdogs or SoC-level pseudo exceptions. Uses the same register + // name as ESP-IDF's live register dump for the architecture (EXCVADDR on + // Xtensa, MTVAL on RISC-V) so the CLI decodes it when it happens to be a + // code address. + if (s_raw_crash_data.exception == PANIC_EXCEPTION_FAULT && !s_raw_crash_data.pseudo_excause) { +#if CONFIG_IDF_TARGET_ARCH_XTENSA + ESP_LOGE(TAG, " EXCVADDR: 0x%08" PRIX32 " (faulting address)", s_raw_crash_data.fault_addr); +#elif CONFIG_IDF_TARGET_ARCH_RISCV + ESP_LOGE(TAG, " MTVAL: 0x%08" PRIX32 " (faulting address)", s_raw_crash_data.fault_addr); +#endif + } log_backtrace(s_raw_crash_data.backtrace, s_raw_crash_data.backtrace_count, s_raw_crash_data.reg_frame_count); #if SOC_CPU_CORES_NUM > 1 @@ -382,6 +405,9 @@ void IRAM_ATTR __wrap_esp_panic_handler(panic_info_t *info) { s_raw_crash_data.exception = (uint8_t) info->exception; s_raw_crash_data.pseudo_excause = info->pseudo_excause ? 1 : 0; s_raw_crash_data.crashed_core = (uint8_t) info->core; + // Zero unconditionally so a null frame doesn't leave stale .noinit data from a previous boot + s_raw_crash_data.cause = 0; + s_raw_crash_data.fault_addr = 0; #if SOC_CPU_CORES_NUM > 1 s_raw_crash_data.other_backtrace_count = 0; s_raw_crash_data.other_reg_frame_count = 0; @@ -392,6 +418,7 @@ void IRAM_ATTR __wrap_esp_panic_handler(panic_info_t *info) { if (info->frame != nullptr) { auto *xt_frame = (XtExcFrame *) info->frame; s_raw_crash_data.cause = xt_frame->exccause; + s_raw_crash_data.fault_addr = xt_frame->excvaddr; s_raw_crash_data.backtrace_count = walk_xtensa_backtrace(xt_frame, s_raw_crash_data.backtrace, MAX_BACKTRACE); } @@ -414,6 +441,7 @@ void IRAM_ATTR __wrap_esp_panic_handler(panic_info_t *info) { if (info->frame != nullptr) { auto *rv_frame = (RvExcFrame *) info->frame; s_raw_crash_data.cause = rv_frame->mcause; + s_raw_crash_data.fault_addr = rv_frame->mtval; s_raw_crash_data.backtrace_count = capture_riscv_backtrace(rv_frame, s_raw_crash_data.backtrace, MAX_BACKTRACE, &s_raw_crash_data.reg_frame_count); } diff --git a/esphome/components/esp32_ble/__init__.py b/esphome/components/esp32_ble/__init__.py index c9fb42fde4..c8613963b9 100644 --- a/esphome/components/esp32_ble/__init__.py +++ b/esphome/components/esp32_ble/__init__.py @@ -2,11 +2,19 @@ from collections.abc import Callable, MutableMapping from dataclasses import dataclass from enum import Enum import logging -import re from typing import Any from esphome import automation import esphome.codegen as cg + +# bt_uuid validation lives in the platform-neutral ble_device_base; re-exported +# here for backward compatibility. +from esphome.components.ble_device_base import ( # noqa: F401 # pylint: disable=unused-import + BT_UUID16_FORMAT as bt_uuid16_format, + BT_UUID32_FORMAT as bt_uuid32_format, + BT_UUID128_FORMAT as bt_uuid128_format, + bt_uuid, +) from esphome.components.const import CONF_USE_PSRAM from esphome.components.esp32 import ( add_idf_sdkconfig_option, @@ -28,6 +36,7 @@ from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority import esphome.final_validate as fv from esphome.types import ConfigType +AUTO_LOAD = ["ble_device_base"] # ble_uuid.h builds on the neutral ESPBTUUID DEPENDENCIES = ["esp32"] CODEOWNERS = ["@jesserockz", "@Rapsssito", "@bdraco"] DOMAIN = "esp32_ble" @@ -372,43 +381,6 @@ def _validate_key_sizes(config: ConfigType) -> ConfigType: CONFIG_SCHEMA = cv.All(CONFIG_SCHEMA, _validate_key_sizes) -bt_uuid16_format = "XXXX" -bt_uuid32_format = "XXXXXXXX" -bt_uuid128_format = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" - - -def bt_uuid(value): - in_value = cv.string_strict(value) - value = in_value.upper() - - if len(value) == len(bt_uuid16_format): - pattern = re.compile("^[A-F0-9]{4,}$") - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'" - ) - return value - if len(value) == len(bt_uuid32_format): - pattern = re.compile("^[A-F0-9]{8,}$") - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'" - ) - return value - if len(value) == len(bt_uuid128_format): - pattern = re.compile( - "^[A-F0-9]{8,}-[A-F0-9]{4,}-[A-F0-9]{4,}-[A-F0-9]{4,}-[A-F0-9]{12,}$" - ) - if not pattern.match(value): - raise cv.Invalid( - f"Invalid hexadecimal value for 128 UUID format: '{in_value}'" - ) - return value - raise cv.Invalid( - f"Bluetooth UUID must be in 16 bit '{bt_uuid16_format}', 32 bit '{bt_uuid32_format}', or 128 bit '{bt_uuid128_format}' format" - ) - - def validate_variant(_): variant = get_esp32_variant() if variant in NO_BLUETOOTH_VARIANTS: diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index a2d19f1042..fb75e8837f 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -58,6 +58,7 @@ static constexpr uint32_t HOSTED_BT_WDT_TIMEOUT_MS = 60000; case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: \ case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: \ case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: \ + case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: \ case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: \ case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT diff --git a/esphome/components/esp32_ble/ble_advertising.h b/esphome/components/esp32_ble/ble_advertising.h index 3cfa6f548a..6c8a97f453 100644 --- a/esphome/components/esp32_ble/ble_advertising.h +++ b/esphome/components/esp32_ble/ble_advertising.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/core/defines.h" +#include "ble_uuid.h" #include #include @@ -15,8 +16,6 @@ namespace esphome::esp32_ble { -class ESPBTUUID; - class BLEAdvertising { public: BLEAdvertising(uint32_t advertising_cycle_time); diff --git a/esphome/components/esp32_ble/ble_event.h b/esphome/components/esp32_ble/ble_event.h index ba87fd8805..babfa937c7 100644 --- a/esphome/components/esp32_ble/ble_event.h +++ b/esphome/components/esp32_ble/ble_event.h @@ -207,7 +207,7 @@ class BLEEvent { StatusOnlyData scan_complete; // 1 byte // Advertising complete events all have same structure // Used by: esp32_ble_beacon, esp32_ble server components - // ADV_DATA_SET, SCAN_RSP_DATA_SET, ADV_DATA_RAW_SET, ADV_START, ADV_STOP + // ADV_DATA_SET, SCAN_RSP_DATA_SET, ADV_DATA_RAW_SET, SCAN_RSP_DATA_RAW_SET, ADV_START, ADV_STOP StatusOnlyData adv_complete; // 1 byte // RSSI complete event // Used by: ble_client (ble_rssi_sensor component) @@ -324,6 +324,9 @@ class BLEEvent { case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: // Used by: esp32_ble_beacon this->event_.gap.adv_complete.status = p->adv_data_raw_cmpl.status; break; + case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT: // Used by: raw advertisers with scan response + this->event_.gap.adv_complete.status = p->scan_rsp_data_raw_cmpl.status; + break; case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: // Used by: esp32_ble_beacon this->event_.gap.adv_complete.status = p->adv_start_cmpl.status; break; diff --git a/esphome/components/esp32_ble/ble_uuid.cpp b/esphome/components/esp32_ble/ble_uuid.cpp deleted file mode 100644 index 886f8237ad..0000000000 --- a/esphome/components/esp32_ble/ble_uuid.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "ble_uuid.h" - -#ifdef USE_ESP32 -#ifdef USE_ESP32_BLE_UUID - -#include -#include -#include -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" - -namespace esphome::esp32_ble { - -static const char *const TAG = "esp32_ble"; - -ESPBTUUID::ESPBTUUID() : uuid_() {} -ESPBTUUID ESPBTUUID::from_uint16(uint16_t uuid) { - ESPBTUUID ret; - ret.uuid_.len = ESP_UUID_LEN_16; - ret.uuid_.uuid.uuid16 = uuid; - return ret; -} -ESPBTUUID ESPBTUUID::from_uint32(uint32_t uuid) { - ESPBTUUID ret; - ret.uuid_.len = ESP_UUID_LEN_32; - ret.uuid_.uuid.uuid32 = uuid; - return ret; -} -ESPBTUUID ESPBTUUID::from_raw(const uint8_t *data) { - ESPBTUUID ret; - ret.uuid_.len = ESP_UUID_LEN_128; - memcpy(ret.uuid_.uuid.uuid128, data, ESP_UUID_LEN_128); - return ret; -} -ESPBTUUID ESPBTUUID::from_raw_reversed(const uint8_t *data) { - ESPBTUUID ret; - ret.uuid_.len = ESP_UUID_LEN_128; - for (uint8_t i = 0; i < ESP_UUID_LEN_128; i++) - ret.uuid_.uuid.uuid128[ESP_UUID_LEN_128 - 1 - i] = data[i]; - return ret; -} -ESPBTUUID ESPBTUUID::from_raw(const char *data, size_t length) { - ESPBTUUID ret; - if (length == 4) { - // 16-bit UUID as 4-character hex string - auto parsed = parse_hex(data, length); - if (parsed.has_value()) { - ret.uuid_.len = ESP_UUID_LEN_16; - ret.uuid_.uuid.uuid16 = parsed.value(); - } - } else if (length == 8) { - // 32-bit UUID as 8-character hex string - auto parsed = parse_hex(data, length); - if (parsed.has_value()) { - ret.uuid_.len = ESP_UUID_LEN_32; - ret.uuid_.uuid.uuid32 = parsed.value(); - } - } else if (length == 16) { // how we can have 16 byte length string reprezenting 128 bit uuid??? needs to be - // investigated (lack of time) - ret.uuid_.len = ESP_UUID_LEN_128; - memcpy(ret.uuid_.uuid.uuid128, reinterpret_cast(data), 16); - } else if (length == 36) { - // If the length of the string is 36 bytes then we will assume it is a long hex string in - // UUID format. - ret.uuid_.len = ESP_UUID_LEN_128; - int n = 0; - for (size_t i = 0; i < length; i += 2) { - if (data[i] == '-') - i++; - uint8_t msb = data[i]; - uint8_t lsb = data[i + 1]; - - if (msb > '9') - msb -= 7; - if (lsb > '9') - lsb -= 7; - ret.uuid_.uuid.uuid128[15 - n++] = ((msb & 0x0F) << 4) | (lsb & 0x0F); - } - } else { - ESP_LOGE(TAG, "ERROR: UUID value not 2, 4, 16 or 36 bytes - %s", data); - } - return ret; -} -ESPBTUUID ESPBTUUID::from_uuid(esp_bt_uuid_t uuid) { - ESPBTUUID ret; - ret.uuid_.len = uuid.len; - if (uuid.len == ESP_UUID_LEN_16) { - ret.uuid_.uuid.uuid16 = uuid.uuid.uuid16; - } else if (uuid.len == ESP_UUID_LEN_32) { - ret.uuid_.uuid.uuid32 = uuid.uuid.uuid32; - } else if (uuid.len == ESP_UUID_LEN_128) { - memcpy(ret.uuid_.uuid.uuid128, uuid.uuid.uuid128, ESP_UUID_LEN_128); - } - return ret; -} -ESPBTUUID ESPBTUUID::as_128bit() const { - if (this->uuid_.len == ESP_UUID_LEN_128) { - return *this; - } - uint8_t data[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint32_t uuid32; - if (this->uuid_.len == ESP_UUID_LEN_32) { - uuid32 = this->uuid_.uuid.uuid32; - } else { - uuid32 = this->uuid_.uuid.uuid16; - } - for (uint16_t i = 0; i < this->uuid_.len; i++) { - data[12 + i] = ((uuid32 >> i * 8) & 0xFF); - } - return ESPBTUUID::from_raw(data); -} -bool ESPBTUUID::contains(uint8_t data1, uint8_t data2) const { - if (this->uuid_.len == ESP_UUID_LEN_16) { - return (this->uuid_.uuid.uuid16 >> 8) == data2 && (this->uuid_.uuid.uuid16 & 0xFF) == data1; - } else if (this->uuid_.len == ESP_UUID_LEN_32) { - for (uint8_t i = 0; i < 3; i++) { - bool a = ((this->uuid_.uuid.uuid32 >> i * 8) & 0xFF) == data1; - bool b = ((this->uuid_.uuid.uuid32 >> (i + 1) * 8) & 0xFF) == data2; - if (a && b) - return true; - } - } else { - for (uint8_t i = 0; i < 15; i++) { - if (this->uuid_.uuid.uuid128[i] == data1 && this->uuid_.uuid.uuid128[i + 1] == data2) - return true; - } - } - return false; -} -bool ESPBTUUID::operator==(const ESPBTUUID &uuid) const { - if (this->uuid_.len == uuid.uuid_.len) { - switch (this->uuid_.len) { - case ESP_UUID_LEN_16: - return this->uuid_.uuid.uuid16 == uuid.uuid_.uuid.uuid16; - case ESP_UUID_LEN_32: - return this->uuid_.uuid.uuid32 == uuid.uuid_.uuid.uuid32; - case ESP_UUID_LEN_128: - return memcmp(this->uuid_.uuid.uuid128, uuid.uuid_.uuid.uuid128, ESP_UUID_LEN_128) == 0; - default: - return false; - } - } - return this->as_128bit() == uuid.as_128bit(); -} -esp_bt_uuid_t ESPBTUUID::get_uuid() const { return this->uuid_; } -const char *ESPBTUUID::to_str(std::span output) const { - char *pos = output.data(); - - switch (this->uuid_.len) { - case ESP_UUID_LEN_16: - *pos++ = '0'; - *pos++ = 'x'; - *pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 >> 12); - *pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 8) & 0x0F); - *pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid16 >> 4) & 0x0F); - *pos++ = format_hex_pretty_char(this->uuid_.uuid.uuid16 & 0x0F); - *pos = '\0'; - return output.data(); - - case ESP_UUID_LEN_32: - *pos++ = '0'; - *pos++ = 'x'; - for (int shift = 28; shift >= 0; shift -= 4) { - *pos++ = format_hex_pretty_char((this->uuid_.uuid.uuid32 >> shift) & 0x0F); - } - *pos = '\0'; - return output.data(); - - default: - case ESP_UUID_LEN_128: - // Format: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - for (int8_t i = 15; i >= 0; i--) { - uint8_t byte = this->uuid_.uuid.uuid128[i]; - *pos++ = format_hex_pretty_char(byte >> 4); - *pos++ = format_hex_pretty_char(byte & 0x0F); - if (i == 12 || i == 10 || i == 8 || i == 6) { - *pos++ = '-'; - } - } - *pos = '\0'; - return output.data(); - } -} -std::string ESPBTUUID::to_string() const { - char buf[UUID_STR_LEN]; - this->to_str(buf); - return std::string(buf); -} - -} // namespace esphome::esp32_ble - -#endif // USE_ESP32_BLE_UUID -#endif // USE_ESP32 diff --git a/esphome/components/esp32_ble/ble_uuid.h b/esphome/components/esp32_ble/ble_uuid.h index 503fde6945..fd8da4baee 100644 --- a/esphome/components/esp32_ble/ble_uuid.h +++ b/esphome/components/esp32_ble/ble_uuid.h @@ -1,59 +1,21 @@ #pragma once #include "esphome/core/defines.h" -#include "esphome/core/hal.h" -#include "esphome/core/helpers.h" #ifdef USE_ESP32 #ifdef USE_ESP32_BLE_UUID -#include -#include -#include -#include +// The BLE UUID type is owned by the platform-neutral ble_device_base layer; +// this header re-exports it under the historical esp32_ble name (esp32 only). +// The full historical API surface — including from_uuid()/get_uuid() with the +// ESP-IDF esp_bt_uuid_t type — is preserved on esp32 builds. + +#include "esphome/components/ble_device_base/ble_device.h" namespace esphome::esp32_ble { -/// Buffer size for UUID string: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\0" -static constexpr size_t UUID_STR_LEN = 37; - -class ESPBTUUID { - public: - ESPBTUUID(); - - static ESPBTUUID from_uint16(uint16_t uuid); - - static ESPBTUUID from_uint32(uint32_t uuid); - - static ESPBTUUID from_raw(const uint8_t *data); - static ESPBTUUID from_raw_reversed(const uint8_t *data); - - static ESPBTUUID from_raw(const char *data, size_t length); - static ESPBTUUID from_raw(const char *data) { return from_raw(data, strlen(data)); } - static ESPBTUUID from_raw(const std::string &data) { return from_raw(data.c_str(), data.length()); } - static ESPBTUUID from_raw(std::initializer_list data) { - return from_raw(reinterpret_cast(data.begin()), data.size()); - } - - static ESPBTUUID from_uuid(esp_bt_uuid_t uuid); - - ESPBTUUID as_128bit() const; - - bool contains(uint8_t data1, uint8_t data2) const; - - bool operator==(const ESPBTUUID &uuid) const; - bool operator!=(const ESPBTUUID &uuid) const { return !(*this == uuid); } - - esp_bt_uuid_t get_uuid() const; - - // Remove before 2026.8.0 - ESPDEPRECATED("Use to_str() instead. Removed in 2026.8.0", "2026.2.0") - std::string to_string() const; // NOLINT - const char *to_str(std::span output) const; - - protected: - esp_bt_uuid_t uuid_; -}; +using ble_device_base::UUID_STR_LEN; +using ESPBTUUID = ble_device_base::ESPBTUUID; } // namespace esphome::esp32_ble diff --git a/esphome/components/esp32_ble_client/ble_client_base.cpp b/esphome/components/esp32_ble_client/ble_client_base.cpp index 3fb9632e9a..bd80f71a49 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.cpp +++ b/esphome/components/esp32_ble_client/ble_client_base.cpp @@ -125,6 +125,9 @@ void BLEClientBase::connect() { } ESP_LOGI(TAG, "[%d] [%s] 0x%02x Connecting", this->connection_index_, this->address_str_, this->remote_addr_type_); this->paired_ = false; + // A registration whose event never arrived must not block this connection's release. + this->services_released_ = false; + this->pending_notify_regs_ = 0; // Enable loop for state processing this->enable_loop(); // Immediately transition to CONNECTING to prevent duplicate connection attempts @@ -200,10 +203,26 @@ void BLEClientBase::release_services() { this->services_.clear(); #endif #ifndef CONFIG_BT_GATTC_CACHE_NVS_FLASH + // Only the cache clean makes the stack's database unsafe to walk. + this->services_released_ = true; esp_ble_gattc_cache_clean(this->remote_bda_); #endif } +esp_err_t BLEClientBase::register_for_notify(uint16_t char_handle) { + esp_err_t err = esp_ble_gattc_register_for_notify(this->gattc_if_, this->remote_bda_, char_handle); + if (err != ESP_OK) + return err; + if (this->pending_notify_regs_ == UINT8_MAX) { + // Saturating undercounts, so the release can run before the last registration completes. + // Wrapping to zero would undercount by the full range instead, which is worse. + this->log_warning_("Too many outstanding notify registrations to track"); + return err; + } + this->pending_notify_regs_++; + return err; +} + void BLEClientBase::log_event_(const char *name) { ESP_LOGD(TAG, "[%d] [%s] %s", this->connection_index_, this->address_str_, name); } @@ -498,12 +517,20 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_ } case ESP_GATTC_REG_FOR_NOTIFY_EVT: { this->log_gattc_data_event_("REG_FOR_NOTIFY"); + // The event carries no conn_id, so this is the only place the request can be retired. + if (this->pending_notify_regs_ > 0) + this->pending_notify_regs_--; if (this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE || this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) { // Client is responsible for flipping the descriptor value // when using the cache break; } + if (this->services_released_) { + // The lookup below walks the freed GATT cache, and Bluedroid asserts on it rather than erroring. + this->log_warning_("REG_FOR_NOTIFY after services released, notifications not enabled"); + break; + } esp_gattc_descr_elem_t desc_result; uint16_t count = 1; esp_gatt_status_t descr_status = esp_ble_gattc_get_descr_by_char_handle( diff --git a/esphome/components/esp32_ble_client/ble_client_base.h b/esphome/components/esp32_ble_client/ble_client_base.h index 0291a4b993..0902aad924 100644 --- a/esphome/components/esp32_ble_client/ble_client_base.h +++ b/esphome/components/esp32_ble_client/ble_client_base.h @@ -44,6 +44,12 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { void unconditional_disconnect(); void release_services(); + /// Register for notifications, holding the service release until the registration completes. + esp_err_t register_for_notify(uint16_t char_handle); + + /// True while a register_for_notify() request has not completed. + bool notify_registration_pending() const { return this->pending_notify_regs_ > 0; } + bool connected() { return this->state() == espbt::ClientState::ESTABLISHED; } void set_auto_connect(bool auto_connect) { this->auto_connect_ = auto_connect; } @@ -125,9 +131,15 @@ class BLEClientBase : public espbt::ESPBTClient, public Component { espbt::ConnectionType connection_type_{espbt::ConnectionType::V1}; uint8_t connection_index_; uint8_t service_count_{0}; // ESP32 has max handles < 255, typical devices have < 50 services + // Outstanding register_for_notify() requests + // A count, not per-request state, so a raw esp_ble_gattc_register_for_notify() on the same client can retire one + // services_released_ is the backstop if that ever lets the release run early + uint8_t pending_notify_regs_{0}; bool auto_connect_{false}; bool paired_{false}; - // 6 bytes used, 2 bytes padding + // Set only when release_services() cleans the stack's GATT cache, which no API may then walk + bool services_released_{false}; + // 8 bytes used, no padding void log_event_(const char *name); void log_gattc_lifecycle_event_(const char *name); diff --git a/esphome/components/esp32_ble_server/ble_descriptor.cpp b/esphome/components/esp32_ble_server/ble_descriptor.cpp index 5ca80d6a7a..3dcac3691c 100644 --- a/esphome/components/esp32_ble_server/ble_descriptor.cpp +++ b/esphome/components/esp32_ble_server/ble_descriptor.cpp @@ -77,6 +77,10 @@ void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_ case ESP_GATTS_WRITE_EVT: { if (this->handle_ != param->write.handle) break; + if (param->write.len > this->value_.attr_max_len) { + ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", param->write.len, this->value_.attr_max_len); + break; + } this->value_.attr_len = param->write.len; memcpy(this->value_.attr_value, param->write.value, param->write.len); if (this->on_write_callback_) { diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index e4139bed65..e462da1a49 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -5,7 +5,8 @@ import logging from esphome import automation import esphome.codegen as cg -from esphome.components import esp32_ble, ota +from esphome.components import ble_device_base, esp32_ble, ota +from esphome.components.const import CONF_SCAN_PARAMETERS, CONF_WINDOW from esphome.components.esp32 import ( add_idf_sdkconfig_option, request_bluetooth, @@ -39,13 +40,11 @@ from esphome.core import CORE, CoroPriority, coroutine_with_priority from esphome.enum import StrEnum from esphome.types import ConfigType -AUTO_LOAD = ["esp32_ble"] +AUTO_LOAD = ["ble_device_base", "esp32_ble"] DEPENDENCIES = ["esp32"] CODEOWNERS = ["@bdraco"] CONF_ESP32_BLE_ID = "esp32_ble_id" -CONF_SCAN_PARAMETERS = "scan_parameters" -CONF_WINDOW = "window" CONF_ON_SCAN_END = "on_scan_end" CONF_SOFTWARE_COEXISTENCE = "software_coexistence" @@ -93,6 +92,7 @@ def register_ble_features(features: set[BLEFeatures]) -> None: esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") ESP32BLETracker = esp32_ble_tracker_ns.class_( "ESP32BLETracker", + ble_device_base.BLEHub, cg.Component, cg.Parented.template(esp32_ble.ESP32BLE), ) @@ -153,26 +153,11 @@ def validate_max_connections_deprecated(config: ConfigType) -> ConfigType: return config -def as_hex(value): - return cg.RawExpression(f"0x{value}ULL") - - -def as_hex_array(value): - value = value.replace("-", "") - cpp_array = [ - f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] - ] - return cg.RawExpression(f"(uint8_t*)(const uint8_t[16]){{{','.join(cpp_array)}}}") - - -def as_reversed_hex_array(value): - value = value.replace("-", "") - cpp_array = [ - f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)] - ] - return cg.RawExpression( - f"(uint8_t*)(const uint8_t[16]){{{','.join(reversed(cpp_array))}}}" - ) +# Codegen helpers are owned by ble_device_base; kept under the historical names +# here for the components that import them from this module. +as_hex = ble_device_base.as_hex +as_hex_array = ble_device_base.as_hex_array +as_reversed_hex_array = ble_device_base.as_reversed_hex_array CONFIG_SCHEMA = cv.All( @@ -254,6 +239,10 @@ async def to_code(config): # Register the loggers this component needs esp32_ble.register_bt_logger(BTLoggers.BLE_SCAN) + # Behavior parity with the pre-split tracker: IRK resolution is always + # available on esp32 (sensors with irk: worked without opting in). + ble_device_base.request_irk_support() + var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) @@ -346,6 +335,14 @@ async def to_code(config): async def _add_ble_features(): # Add feature-specific defines based on what's needed required_features = _get_required_features() + # Sensors registered through the neutral ble_device_base path (BLEHub) need + # the parsed-device pipeline compiled in, exactly like esp32-path listeners. + neutral_listener_count = ble_device_base.get_listener_count() + if neutral_listener_count > 0: + required_features.add(BLEFeatures.ESP_BT_DEVICE) + # StaticVector sizing for the neutral (BLEHub) listener list — same + # pattern as the esp32-path registration counts below. + cg.add_define("ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT", neutral_listener_count) if BLEFeatures.ESP_BT_DEVICE in required_features: cg.add_define("USE_ESP32_BLE_DEVICE") cg.add_define("USE_ESP32_BLE_UUID") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index f57cb7f5dc..0c1a98c4be 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -27,15 +27,6 @@ #include #endif -#ifdef USE_ESP32_BLE_DEVICE -#ifdef USE_BLE_TRACKER_PSA_AES -#include -#else -#define MBEDTLS_AES_ALT -#include -#endif -#endif // USE_ESP32_BLE_DEVICE - // bt_trace.h #undef TAG @@ -43,9 +34,6 @@ namespace esphome::esp32_ble_tracker { static const char *const TAG = "esp32_ble_tracker"; -// BLE advertisement max: 31 bytes adv data + 31 bytes scan response -static constexpr size_t BLE_ADV_MAX_LOG_BYTES = 62; - ESP32BLETracker *global_esp32_ble_tracker = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) const char *client_state_to_string(ClientState state) { @@ -263,10 +251,14 @@ void ESP32BLETracker::start_scan_(bool first) { #ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) listener->on_scan_end(); +#endif +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + for (auto *listener : this->neutral_listeners_) + listener->on_scan_end(); #endif } #ifdef USE_ESP32_BLE_DEVICE - this->already_discovered_.clear(); + this->discovered_log_.clear(); #endif this->scan_params_.scan_type = this->scan_active_ ? BLE_SCAN_TYPE_ACTIVE : BLE_SCAN_TYPE_PASSIVE; this->scan_params_.own_addr_type = BLE_ADDR_TYPE_PUBLIC; @@ -304,6 +296,21 @@ void ESP32BLETracker::register_client(ESPBTClient *client) { #endif } +void ESP32BLETracker::register_listener(ble_device_base::ESPBTDeviceListener *listener) { +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + // Neutral BLEHub path (migrated sensors): parsed-advertisement consumers only. + this->neutral_listeners_.push_back(listener); + this->parse_advertisements_ = true; +#endif +} + +void ESP32BLETracker::get_adapter_mac(uint8_t out[6]) { + get_mac_address_raw(out); // WiFi base MAC, MSB-first + // BT MAC = base MAC + 2 on the last octet only, wrapping without carry — + // exactly ESP-IDF's esp_read_mac(ESP_MAC_BT): mac[5] += MAC_ADDR_UNIVERSE_BT_OFFSET. + out[5] += 2; +} + void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) { #ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT listener->set_parent(this); @@ -315,6 +322,13 @@ void ESP32BLETracker::register_listener(ESPBTDeviceListener *listener) { void ESP32BLETracker::recalculate_advertisement_parser_types() { this->raw_advertisements_ = false; this->parse_advertisements_ = false; +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + // Neutral (BLEHub) listeners are parsed-advertisement consumers and are not in + // listeners_; without this, any later esp32-path registration (e.g. the proxy's + // GATT clients) would recompute the flags and silently drop parsed dispatch. + if (!this->neutral_listeners_.empty()) + this->parse_advertisements_ = true; +#endif #ifdef ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT for (auto *listener : this->listeners_) { if (listener->get_advertisement_parser_type() == AdvertisementParserType::PARSED_ADVERTISEMENTS) { @@ -434,270 +448,6 @@ void ESP32BLETracker::set_scanner_state_(ScannerState state) { } } -#ifdef USE_ESP32_BLE_DEVICE -ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); } -optional ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) { - if (!data.uuid.contains(0x4C, 0x00)) - return {}; - - if (data.data.size() != 23) - return {}; - return ESPBLEiBeacon(data.data.data()); -} - -void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) { - this->scan_result_ = &scan_result; - for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) - this->address_[i] = scan_result.bda[i]; - this->address_type_ = static_cast(scan_result.ble_addr_type); - this->rssi_ = scan_result.rssi; - - // Parse advertisement data directly - uint8_t total_len = scan_result.adv_data_len + scan_result.scan_rsp_len; - this->parse_adv_(scan_result.ble_adv, total_len); - -#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - ESP_LOGVV(TAG, "Parse Result:"); - const char *address_type; - switch (this->address_type_) { - case BLE_ADDR_TYPE_PUBLIC: - address_type = "PUBLIC"; - break; - case BLE_ADDR_TYPE_RANDOM: - address_type = "RANDOM"; - break; - case BLE_ADDR_TYPE_RPA_PUBLIC: - address_type = "RPA_PUBLIC"; - break; - case BLE_ADDR_TYPE_RPA_RANDOM: - address_type = "RPA_RANDOM"; - break; - default: - address_type = "UNKNOWN"; - break; - } - ESP_LOGVV(TAG, " Address: %02X:%02X:%02X:%02X:%02X:%02X (%s)", this->address_[0], this->address_[1], - this->address_[2], this->address_[3], this->address_[4], this->address_[5], address_type); - - ESP_LOGVV(TAG, " RSSI: %d", this->rssi_); - ESP_LOGVV(TAG, " Name: '%s'", this->name_.c_str()); - for (auto &it : this->tx_powers_) { - ESP_LOGVV(TAG, " TX Power: %d", it); - } - if (this->appearance_.has_value()) { - ESP_LOGVV(TAG, " Appearance: %u", *this->appearance_); - } - if (this->ad_flag_.has_value()) { - ESP_LOGVV(TAG, " Ad Flag: %u", *this->ad_flag_); - } - for (auto &uuid : this->service_uuids_) { - char uuid_buf[esp32_ble::UUID_STR_LEN]; - uuid.to_str(uuid_buf); - ESP_LOGVV(TAG, " Service UUID: %s", uuid_buf); - } - char hex_buf[format_hex_pretty_size(BLE_ADV_MAX_LOG_BYTES)]; - for (auto &data : this->manufacturer_datas_) { - auto ibeacon = ESPBLEiBeacon::from_manufacturer_data(data); - if (ibeacon.has_value()) { - ESP_LOGVV(TAG, " Manufacturer iBeacon:"); - char uuid_buf[esp32_ble::UUID_STR_LEN]; - ibeacon.value().get_uuid().to_str(uuid_buf); - ESP_LOGVV(TAG, " UUID: %s", uuid_buf); - ESP_LOGVV(TAG, " Major: %u", ibeacon.value().get_major()); - ESP_LOGVV(TAG, " Minor: %u", ibeacon.value().get_minor()); - ESP_LOGVV(TAG, " TXPower: %d", ibeacon.value().get_signal_power()); - } else { - char uuid_buf[esp32_ble::UUID_STR_LEN]; - data.uuid.to_str(uuid_buf); - ESP_LOGVV(TAG, " Manufacturer ID: %s, data: %s", uuid_buf, - format_hex_pretty_to(hex_buf, data.data.data(), data.data.size())); - } - } - for (auto &data : this->service_datas_) { - ESP_LOGVV(TAG, " Service data:"); - char uuid_buf[esp32_ble::UUID_STR_LEN]; - data.uuid.to_str(uuid_buf); - ESP_LOGVV(TAG, " UUID: %s", uuid_buf); - ESP_LOGVV(TAG, " Data: %s", format_hex_pretty_to(hex_buf, data.data.data(), data.data.size())); - } - - ESP_LOGVV(TAG, " Adv data: %s", - format_hex_pretty_to(hex_buf, scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len)); -#endif -} - -void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) { - size_t offset = 0; - - while (offset + 2 < len) { - const uint8_t field_length = payload[offset++]; // First byte is length of adv record - if (field_length == 0) { - continue; // Possible zero padded advertisement data - } - - // Validate field fits in remaining payload - if (offset + field_length > len) { - break; - } - - // first byte of adv record is adv record type - const uint8_t record_type = payload[offset++]; - const uint8_t *record = &payload[offset]; - const uint8_t record_length = field_length - 1; - offset += record_length; - - // See also Generic Access Profile Assigned Numbers: - // https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/ See also ADVERTISING AND SCAN - // RESPONSE DATA FORMAT: https://www.bluetooth.com/specifications/bluetooth-core-specification/ (vol 3, part C, 11) - // See also Core Specification Supplement: https://www.bluetooth.com/specifications/bluetooth-core-specification/ - // (called CSS here) - - switch (record_type) { - case ESP_BLE_AD_TYPE_NAME_SHORT: - case ESP_BLE_AD_TYPE_NAME_CMPL: { - // CSS 1.2 LOCAL NAME - // "The Local Name data type shall be the same as, or a shortened version of, the local name assigned to the - // device." CSS 1: Optional in this context; shall not appear more than once in a block. - // SHORTENED LOCAL NAME - // "The Shortened Local Name data type defines a shortened version of the Local Name data type. The Shortened - // Local Name data type shall not be used to advertise a name that is longer than the Local Name data type." - if (record_length > this->name_.length()) { - this->name_ = std::string(reinterpret_cast(record), record_length); - } - break; - } - case ESP_BLE_AD_TYPE_TX_PWR: { - // CSS 1.5 TX POWER LEVEL - // "The TX Power Level data type indicates the transmitted power level of the packet containing the data type." - // CSS 1: Optional in this context (may appear more than once in a block). - this->tx_powers_.push_back(*record); - break; - } - case ESP_BLE_AD_TYPE_APPEARANCE: { - // CSS 1.12 APPEARANCE - // "The Appearance data type defines the external appearance of the device." - // See also https://www.bluetooth.com/specifications/gatt/characteristics/ - // CSS 1: Optional in this context; shall not appear more than once in a block and shall not appear in both - // the AD and SRD of the same extended advertising interval. - this->appearance_ = *reinterpret_cast(record); - break; - } - case ESP_BLE_AD_TYPE_FLAG: { - // CSS 1.3 FLAGS - // "The Flags data type contains one bit Boolean flags. The Flags data type shall be included when any of the - // Flag bits are non-zero and the advertising packet is connectable, otherwise the Flags data type may be - // omitted." - // CSS 1: Optional in this context; shall not appear more than once in a block. - this->ad_flag_ = *record; - break; - } - // CSS 1.1 SERVICE UUID - // The Service UUID data type is used to include a list of Service or Service Class UUIDs. - // There are six data types defined for the three sizes of Service UUIDs that may be returned: - // CSS 1: Optional in this context (may appear more than once in a block). - case ESP_BLE_AD_TYPE_16SRV_CMPL: - case ESP_BLE_AD_TYPE_16SRV_PART: { - // • 16-bit Bluetooth Service UUIDs - for (uint8_t i = 0; i < record_length / 2; i++) { - this->service_uuids_.push_back(ESPBTUUID::from_uint16(*reinterpret_cast(record + 2 * i))); - } - break; - } - case ESP_BLE_AD_TYPE_32SRV_CMPL: - case ESP_BLE_AD_TYPE_32SRV_PART: { - // • 32-bit Bluetooth Service UUIDs - for (uint8_t i = 0; i < record_length / 4; i++) { - this->service_uuids_.push_back(ESPBTUUID::from_uint32(*reinterpret_cast(record + 4 * i))); - } - break; - } - case ESP_BLE_AD_TYPE_128SRV_CMPL: - case ESP_BLE_AD_TYPE_128SRV_PART: { - // • Global 128-bit Service UUIDs - this->service_uuids_.push_back(ESPBTUUID::from_raw(record)); - break; - } - case ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE: { - // CSS 1.4 MANUFACTURER SPECIFIC DATA - // "The Manufacturer Specific data type is used for manufacturer specific data. The first two data octets shall - // contain a company identifier from Assigned Numbers. The interpretation of any other octets within the data - // shall be defined by the manufacturer specified by the company identifier." - // CSS 1: Optional in this context (may appear more than once in a block). - if (record_length < 2) { - ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE"); - break; - } - ServiceData data{}; - data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast(record)); - data.data.assign(record + 2UL, record + record_length); - this->manufacturer_datas_.push_back(data); - break; - } - - // CSS 1.11 SERVICE DATA - // "The Service Data data type consists of a service UUID with the data associated with that service." - // CSS 1: Optional in this context (may appear more than once in a block). - case ESP_BLE_AD_TYPE_SERVICE_DATA: { - // «Service Data - 16 bit UUID» - // Size: 2 or more octets - // The first 2 octets contain the 16 bit Service UUID fol- lowed by additional service data - if (record_length < 2) { - ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_SERVICE_DATA"); - break; - } - ServiceData data{}; - data.uuid = ESPBTUUID::from_uint16(*reinterpret_cast(record)); - data.data.assign(record + 2UL, record + record_length); - this->service_datas_.push_back(data); - break; - } - case ESP_BLE_AD_TYPE_32SERVICE_DATA: { - // «Service Data - 32 bit UUID» - // Size: 4 or more octets - // The first 4 octets contain the 32 bit Service UUID fol- lowed by additional service data - if (record_length < 4) { - ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_32SERVICE_DATA"); - break; - } - ServiceData data{}; - data.uuid = ESPBTUUID::from_uint32(*reinterpret_cast(record)); - data.data.assign(record + 4UL, record + record_length); - this->service_datas_.push_back(data); - break; - } - case ESP_BLE_AD_TYPE_128SERVICE_DATA: { - // «Service Data - 128 bit UUID» - // Size: 16 or more octets - // The first 16 octets contain the 128 bit Service UUID followed by additional service data - if (record_length < 16) { - ESP_LOGV(TAG, "Record length too small for ESP_BLE_AD_TYPE_128SERVICE_DATA"); - break; - } - ServiceData data{}; - data.uuid = ESPBTUUID::from_raw(record); - data.data.assign(record + 16UL, record + record_length); - this->service_datas_.push_back(data); - break; - } - case ESP_BLE_AD_TYPE_INT_RANGE: - // Avoid logging this as it's very verbose - break; - default: { - ESP_LOGV(TAG, "Unhandled type: advType: 0x%02x", record_type); - break; - } - } - } -} - -std::string ESPBTDevice::address_str() const { - char buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; - return this->address_str_to(buf); -} - -uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); } -#endif // USE_ESP32_BLE_DEVICE - void ESP32BLETracker::dump_config() { ESP_LOGCONFIG(TAG, "BLE Tracker:"); ESP_LOGCONFIG(TAG, @@ -721,102 +471,12 @@ void ESP32BLETracker::dump_config() { #ifdef USE_ESP32_BLE_DEVICE void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) { - const uint64_t address = device.address_uint64(); - for (auto &disc : this->already_discovered_) { - if (disc == address) - return; - } - this->already_discovered_.push_back(address); - - char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE]; - ESP_LOGD(TAG, "Found device %s RSSI=%d", device.address_str_to(addr_buf), device.get_rssi()); - - const char *address_type_s; - switch (device.get_address_type()) { - case BLE_ADDR_TYPE_PUBLIC: - address_type_s = "PUBLIC"; - break; - case BLE_ADDR_TYPE_RANDOM: - address_type_s = "RANDOM"; - break; - case BLE_ADDR_TYPE_RPA_PUBLIC: - address_type_s = "RPA_PUBLIC"; - break; - case BLE_ADDR_TYPE_RPA_RANDOM: - address_type_s = "RPA_RANDOM"; - break; - default: - address_type_s = "UNKNOWN"; - break; - } - - ESP_LOGD(TAG, " Address Type: %s", address_type_s); - if (!device.get_name().empty()) { - ESP_LOGD(TAG, " Name: '%s'", device.get_name().c_str()); - } - for (auto &tx_power : device.get_tx_powers()) { - ESP_LOGD(TAG, " TX Power: %d", tx_power); - } + // Shared implementation in ble_device_base — identical output and per-period + // MAC dedup on every tracker backend. + this->discovered_log_.log_device(TAG, device); } -bool ESPBTDevice::resolve_irk(const uint8_t *irk) const { - static constexpr size_t AES_BLOCK_SIZE = 16; - static constexpr size_t AES_KEY_BITS = 128; - - uint8_t ecb_key[AES_BLOCK_SIZE]; - uint8_t ecb_plaintext[AES_BLOCK_SIZE]; - uint8_t ecb_ciphertext[AES_BLOCK_SIZE]; - - uint64_t addr64 = esp32_ble::ble_addr_to_uint64(this->address_); - - memcpy(&ecb_key, irk, AES_BLOCK_SIZE); - memset(&ecb_plaintext, 0, AES_BLOCK_SIZE); - - ecb_plaintext[13] = (addr64 >> 40) & 0xff; - ecb_plaintext[14] = (addr64 >> 32) & 0xff; - ecb_plaintext[15] = (addr64 >> 24) & 0xff; - -#ifdef USE_BLE_TRACKER_PSA_AES - // Use PSA Crypto API (mbedtls 4.0 / IDF 6.0+) - psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attributes, AES_KEY_BITS); - psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&attributes, PSA_ALG_ECB_NO_PADDING); - - mbedtls_svc_key_id_t key_id; - if (psa_import_key(&attributes, ecb_key, AES_BLOCK_SIZE, &key_id) != PSA_SUCCESS) { - return false; - } - - size_t output_length; - psa_status_t status = psa_cipher_encrypt(key_id, PSA_ALG_ECB_NO_PADDING, ecb_plaintext, AES_BLOCK_SIZE, - ecb_ciphertext, AES_BLOCK_SIZE, &output_length); - psa_destroy_key(key_id); - if (status != PSA_SUCCESS || output_length != AES_BLOCK_SIZE) { - return false; - } -#else - // Use legacy mbedtls AES API (IDF < 6.0) - mbedtls_aes_context ctx = {0, 0, {0}}; - mbedtls_aes_init(&ctx); - - if (mbedtls_aes_setkey_enc(&ctx, ecb_key, AES_KEY_BITS) != 0) { - mbedtls_aes_free(&ctx); - return false; - } - - if (mbedtls_aes_crypt_ecb(&ctx, ESP_AES_ENCRYPT, ecb_plaintext, ecb_ciphertext) != 0) { - mbedtls_aes_free(&ctx); - return false; - } - - mbedtls_aes_free(&ctx); -#endif - - return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) && - ecb_ciphertext[13] == ((addr64 >> 16) & 0xff); -} +// resolve_irk() is provided by ble_device_base (portable software AES). #endif // USE_ESP32_BLE_DEVICE @@ -848,6 +508,12 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { found = true; } #endif +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + for (auto *listener : this->neutral_listeners_) { + if (listener->parse_device(device)) + found = true; + } +#endif #ifdef ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT for (auto *client : this->clients_) { @@ -867,7 +533,7 @@ void ESP32BLETracker::process_scan_result_(const BLEScanResult &scan_result) { void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) { ESP_LOGV(TAG, "Scan %scomplete, set scanner state to IDLE.", is_stop_complete ? "stop " : ""); #ifdef USE_ESP32_BLE_DEVICE - this->already_discovered_.clear(); + this->discovered_log_.clear(); #endif // Reset timeout state machine instead of cancelling scheduler timeout this->scan_timeout_state_ = ScanTimeoutState::INACTIVE; @@ -876,6 +542,10 @@ void ESP32BLETracker::cleanup_scan_state_(bool is_stop_complete) { for (auto *listener : this->listeners_) listener->on_scan_end(); #endif +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + for (auto *listener : this->neutral_listeners_) + listener->on_scan_end(); +#endif this->set_scanner_state_(ScannerState::IDLE); } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 3415196a11..b0357289f1 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -12,13 +12,6 @@ #ifdef USE_ESP32 -#include -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0) -// mbedtls 4.0 (IDF 6.0) removed the legacy mbedtls AES API. -// Use the PSA Crypto API instead. -#define USE_BLE_TRACKER_PSA_AES -#endif - #include #include #include @@ -26,6 +19,8 @@ #include #include +#include "esphome/components/ble_device_base/ble_device.h" +#include "esphome/components/ble_device_base/ble_hub.h" #include "esphome/components/esp32_ble/ble.h" #include "esphome/components/esp32_ble/ble_uuid.h" #include "esphome/components/esp32_ble/ble_scan_result.h" @@ -38,7 +33,7 @@ namespace esphome::esp32_ble_tracker { using namespace esp32_ble; -using adv_data_t = std::vector; +using adv_data_t = ble_device_base::adv_data_t; enum AdvertisementParserType { PARSED_ADVERTISEMENTS, @@ -46,105 +41,27 @@ enum AdvertisementParserType { }; #ifdef USE_ESP32_BLE_UUID -struct ServiceData { - ESPBTUUID uuid; - adv_data_t data; -}; +using ServiceData = ble_device_base::ServiceData; #endif #ifdef USE_ESP32_BLE_DEVICE -class ESPBLEiBeacon { - public: - ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); } - ESPBLEiBeacon(const uint8_t *data); - static optional from_manufacturer_data(const ServiceData &data); - - uint16_t get_major() { return byteswap(this->beacon_data_.major); } - uint16_t get_minor() { return byteswap(this->beacon_data_.minor); } - int8_t get_signal_power() { return this->beacon_data_.signal_power; } - ESPBTUUID get_uuid() { return ESPBTUUID::from_raw_reversed(this->beacon_data_.proximity_uuid); } - - protected: - struct { - uint8_t sub_type; - uint8_t length; - uint8_t proximity_uuid[16]; - uint16_t major; - uint16_t minor; - int8_t signal_power; - } PACKED beacon_data_; -}; - -class ESPBTDevice { - public: - void parse_scan_rst(const BLEScanResult &scan_result); - - std::string address_str() const; - - /// Format MAC address into provided buffer, returns pointer to buffer for convenience - const char *address_str_to(std::span buf) const { - format_mac_addr_upper(this->address_, buf.data()); - return buf.data(); - } - - uint64_t address_uint64() const; - - const uint8_t *address() const { return address_; } - - esp_ble_addr_type_t get_address_type() const { return this->address_type_; } - int get_rssi() const { return rssi_; } - const std::string &get_name() const { return this->name_; } - - const std::vector &get_tx_powers() const { return tx_powers_; } - - const optional &get_appearance() const { return appearance_; } - const optional &get_ad_flag() const { return ad_flag_; } - const std::vector &get_service_uuids() const { return service_uuids_; } - - const std::vector &get_manufacturer_datas() const { return manufacturer_datas_; } - - const std::vector &get_service_datas() const { return service_datas_; } - - // Exposed through a function for use in lambdas - const BLEScanResult &get_scan_result() const { return *scan_result_; } - - bool resolve_irk(const uint8_t *irk) const; - - optional get_ibeacon() const { - for (auto &it : this->manufacturer_datas_) { - auto res = ESPBLEiBeacon::from_manufacturer_data(it); - if (res.has_value()) - return res; - } - return {}; - } - - protected: - void parse_adv_(const uint8_t *payload, uint8_t len); - - esp_bd_addr_t address_{ - 0, - }; - esp_ble_addr_type_t address_type_{BLE_ADDR_TYPE_PUBLIC}; - int rssi_{0}; - std::string name_{}; - std::vector tx_powers_{}; - optional appearance_{}; - optional ad_flag_{}; - std::vector service_uuids_{}; - std::vector manufacturer_datas_{}; - std::vector service_datas_{}; - const BLEScanResult *scan_result_{nullptr}; -}; +// The advertisement device types are owned by the platform-neutral +// ble_device_base layer; re-exported here (esp32 only) for backward +// compatibility. ESPBTDevice::parse_scan_rst() (esp32-only) adapts BLEScanResult. +using ESPBLEiBeacon = ble_device_base::ESPBLEiBeacon; +using ESPBTDevice = ble_device_base::ESPBTDevice; #endif // USE_ESP32_BLE_DEVICE class ESP32BLETracker; -class ESPBTDeviceListener { +// esp32-flavored listener: the neutral parse_device/on_scan_end come from +// ble_device_base; this subclass adds the esp32-only raw-advertisement path +// (BLEScanResult batches) and the tracker back-pointer. +class ESPBTDeviceListener : public ble_device_base::ESPBTDeviceListener { public: - virtual void on_scan_end() {} -#ifdef USE_ESP32_BLE_DEVICE - virtual bool parse_device(const ESPBTDevice &device) = 0; +#ifndef USE_ESP32_BLE_DEVICE + // Raw-only build: no parsed-device support is compiled in. + bool parse_device(const ble_device_base::ESPBTDevice &device) override { return false; } #endif virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; }; virtual AdvertisementParserType get_advertisement_parser_type() { @@ -295,6 +212,7 @@ class ESPBTClient : public ESPBTDeviceListener { }; class ESP32BLETracker final : public Component, + public ble_device_base::BLEHub, #ifdef USE_OTA_STATE_LISTENER public ota::OTAGlobalStateListener, #endif @@ -314,10 +232,23 @@ class ESP32BLETracker final : public Component, void loop() override; + // esp32-flavored path (unmigrated esp32 sensors; sets the tracker back-pointer). void register_listener(ESPBTDeviceListener *listener); void register_client(ESPBTClient *client); void recalculate_advertisement_parser_types(); + // ---- ble_device_base::BLEHub (the platform-neutral tracker contract) ---- + void register_listener(ble_device_base::ESPBTDeviceListener *listener) override; + void set_raw_advertisement_callback(ble_device_base::RawAdvertisementCallback callback) override { + this->raw_advertisement_callback_ = callback; + } + ble_device_base::HubCapabilities get_capabilities() const override { + return {/* active_scan = */ true, /* merges_scan_response = */ true, /* gatt = */ true}; + } + void get_adapter_mac(uint8_t out[6]) override; + bool scan_running() override { return this->scanner_state_ == ScannerState::RUNNING; } + bool scan_active() override { return this->scan_active_; } + #ifdef USE_ESP32_BLE_DEVICE void print_bt_device_info(const ESPBTDevice &device); #endif @@ -405,9 +336,15 @@ class ESP32BLETracker final : public Component, StaticVector clients_; #endif std::vector scanner_state_listeners_; + // Parsed listeners registered through the neutral BLEHub contract (migrated + // sensors); dispatched alongside listeners_. +#ifdef ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT + StaticVector neutral_listeners_; +#endif + ble_device_base::RawAdvertisementCallback raw_advertisement_callback_{}; #ifdef USE_ESP32_BLE_DEVICE - /// Vector of addresses that have already been printed in print_bt_device_info - std::vector already_discovered_; + /// Per-period "Found device" DEBUG log with MAC dedup (shared ble_device_base impl) + ble_device_base::DiscoveredDeviceLog discovered_log_; #endif // Group 2: Structs (aligned to 4 bytes) diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index 0e0e2f77d7..618bf775a0 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -1,5 +1,6 @@ import logging from pathlib import Path +import platform import re import subprocess @@ -20,9 +21,16 @@ from esphome.const import ( PLATFORM_ESP8266, ThreadModel, ) -from esphome.core import CORE, CoroPriority, Lambda, coroutine_with_priority +from esphome.core import ( + CORE, + CoroPriority, + EsphomeError, + Lambda, + coroutine_with_priority, +) from esphome.core.config import BOARD_MAX_LENGTH -from esphome.helpers import copy_file_if_changed +from esphome.helpers import IS_MACOS, copy_file_if_changed +from esphome.platformio.toolchain import copy_ccache_script from esphome.types import ConfigType from .boards import BOARDS, ESP8266_LD_SCRIPTS @@ -237,6 +245,32 @@ CONFIG_SCHEMA = cv.All( ) +def check_rosetta() -> None: + """Fail fast when the x86_64 ESP8266 toolchain cannot run on this Mac. + + There is no native arm64 build of the xtensa-lx106 toolchain; on Apple + Silicon it runs under Rosetta 2, which macOS updates can remove. + """ + if not IS_MACOS or platform.machine() != "arm64": + return + try: + result = subprocess.run( + ["/usr/bin/arch", "-x86_64", "/usr/bin/true"], + capture_output=True, + close_fds=False, + check=False, + ) + except OSError: + return # arch(1) unavailable; let the build proceed + if result.returncode != 0: + raise EsphomeError( + "ESP8266 builds on Apple Silicon Macs use an Intel (x86_64) " + "compiler that requires Rosetta 2, which is not installed on " + "this system. Install it with:\n" + " softwareupdate --install-rosetta --agree-to-license" + ) + + @coroutine_with_priority(CoroPriority.PLATFORM) async def to_code(config): cg.add(esp8266_ns.setup_preferences()) @@ -261,6 +295,7 @@ async def to_code(config): ) extra_scripts = [ + "pre:ccache.py", "pre:testing_mode.py", "pre:exclude_updater.py", "pre:exclude_waveform.py", @@ -410,31 +445,18 @@ async def finalize_serial_config() -> None: # Called by writer.py def copy_files() -> None: dir = Path(__file__).parent - post_build_file = dir / "post_build.py.script" - copy_file_if_changed( - post_build_file, - CORE.relative_build_path("post_build.py"), - ) - testing_mode_file = dir / "testing_mode.py.script" - copy_file_if_changed( - testing_mode_file, - CORE.relative_build_path("testing_mode.py"), - ) - exclude_updater_file = dir / "exclude_updater.py.script" - copy_file_if_changed( - exclude_updater_file, - CORE.relative_build_path("exclude_updater.py"), - ) - exclude_waveform_file = dir / "exclude_waveform.py.script" - copy_file_if_changed( - exclude_waveform_file, - CORE.relative_build_path("exclude_waveform.py"), - ) - remove_float_scanf_file = dir / "remove_float_scanf.py.script" - copy_file_if_changed( - remove_float_scanf_file, - CORE.relative_build_path("remove_float_scanf.py"), - ) + for script in ( + "post_build", + "testing_mode", + "exclude_updater", + "exclude_waveform", + "remove_float_scanf", + ): + copy_file_if_changed( + dir / f"{script}.py.script", + CORE.relative_build_path(f"{script}.py"), + ) + copy_ccache_script() # ESP logs stack trace decoder, based on https://github.com/me-no-dev/EspExceptionDecoder diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 03fba7164d..d1d5e45c6b 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -4,7 +4,12 @@ import logging from esphome import automation, pins from esphome.automation import Condition import esphome.codegen as cg -from esphome.components.network import add_use_address, ip_address_literal +from esphome.components.network import ( + add_use_address, + get_network_priority, + get_priority_interfaces_from_full_config, + ip_address_literal, +) from esphome.config_helpers import filter_source_files_from_platform import esphome.config_validation as cv from esphome.const import ( @@ -50,7 +55,6 @@ from esphome.core import ( import esphome.final_validate as fv from esphome.types import ConfigType -CONFLICTS_WITH = ["wifi"] AUTO_LOAD = ["network"] LOGGER = logging.getLogger(__name__) @@ -433,37 +437,48 @@ GENERIC_SCHEMA = cv.All( cv.only_on([Platform.ESP32]), ) -SPI_SCHEMA = cv.All( - BASE_SCHEMA.extend( - cv.Schema( - { - cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_number, - cv.Required(CONF_MISO_PIN): pins.internal_gpio_input_pin_number, - cv.Required(CONF_MOSI_PIN): pins.internal_gpio_output_pin_number, - cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number, - cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_number, - cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, - cv.SplitDefault(CONF_CLOCK_SPEED, esp32="26.67MHz"): cv.All( - cv.only_on_esp32, - cv.frequency, - cv.int_range(int(8e6), int(80e6)), - ), - cv.Optional(CONF_INTERFACE): cv.All( - cv.only_on_esp32, - cv.one_of(*SPI_INTERFACE_MAP.keys(), lower=True), - ), - # Set default value (SPI_ETHERNET_DEFAULT_POLLING_INTERVAL) at _validate() - cv.Optional(CONF_POLLING_INTERVAL): cv.All( - cv.only_on_esp32, - cv.positive_time_period_milliseconds, - cv.Range(min=TimePeriodMilliseconds(milliseconds=1)), - ), - } + +def _spi_schema(default_clock: str = "26.67MHz", max_clock: int = int(80e6)): + return cv.All( + BASE_SCHEMA.extend( + cv.Schema( + { + cv.Required(CONF_CLK_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_MISO_PIN): pins.internal_gpio_input_pin_number, + cv.Required(CONF_MOSI_PIN): pins.internal_gpio_output_pin_number, + cv.Required(CONF_CS_PIN): pins.internal_gpio_output_pin_number, + cv.Optional( + CONF_INTERRUPT_PIN + ): pins.internal_gpio_input_pin_number, + cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, + cv.SplitDefault(CONF_CLOCK_SPEED, esp32=default_clock): cv.All( + cv.only_on_esp32, + cv.frequency, + cv.int_range(int(8e6), max_clock), + ), + cv.Optional(CONF_INTERFACE): cv.All( + cv.only_on_esp32, + cv.one_of(*SPI_INTERFACE_MAP.keys(), lower=True), + ), + # Set default value (SPI_ETHERNET_DEFAULT_POLLING_INTERVAL) at _validate() + cv.Optional(CONF_POLLING_INTERVAL): cv.All( + cv.only_on_esp32, + cv.positive_time_period_milliseconds, + cv.Range(min=TimePeriodMilliseconds(milliseconds=1)), + ), + } + ), ), - ), - cv.only_on([Platform.ESP32, Platform.RP2]), - _validate_spi_interface, -) + cv.only_on([Platform.ESP32, Platform.RP2]), + _validate_spi_interface, + ) + + +SPI_SCHEMA = _spi_schema() + +# The ENC28J60's SCK maximum is 20 MHz, so the shared 26.67 MHz default is out +# of spec for it and makes the driver's CS hold time helper compute no hold +SPI_SCHEMA_ENC28J60 = _spi_schema(default_clock="20MHz", max_clock=int(20e6)) CONFIG_SCHEMA = cv.All( cv.typed_schema( @@ -479,7 +494,7 @@ CONFIG_SCHEMA = cv.All( "W5500": SPI_SCHEMA, "OPENETH": cv.All(BASE_SCHEMA, cv.only_on([Platform.ESP32])), "DM9051": SPI_SCHEMA, - "ENC28J60": SPI_SCHEMA, + "ENC28J60": SPI_SCHEMA_ENC28J60, "W6100": cv.All(SPI_SCHEMA, cv.only_on([Platform.RP2])), "W6300": cv.All(SPI_SCHEMA, cv.only_on([Platform.RP2])), "LAN8670": RMII_SCHEMA, @@ -535,6 +550,14 @@ def phy_register(address: int, value: int, page: int): @coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + + # Apply network priority before register_component (which emits the user's + # explicit setup_priority: if set) so that, as in wifi, an explicit + # setup_priority: still wins over the network-priority-derived value. + prio = get_network_priority("ethernet") + if prio is not None: + cg.set_setup_priority(var, prio) + await cg.register_component(var, config) if CORE.is_esp32: @@ -644,8 +667,9 @@ async def _to_code_esp32(var: cg.Pvariable, config: ConfigType) -> None: ) cg.add(var.add_phy_register(reg)) - # Register Ethernet with the esp32 sdkconfig reconciler, which disables the - # WiFi stack and WiFi/BT coexistence when Ethernet is used without WiFi. + # Register Ethernet with the esp32 sdkconfig reconciler. It disables the + # WiFi stack and WiFi/BT coexistence only when Ethernet runs without WiFi, + # so multi-interface configs (network: priority: with both) keep WiFi. request_ethernet() # Re-enable ESP-IDF's Ethernet driver (excluded by default to save compile time) @@ -732,6 +756,22 @@ def _final_validate_rmii_pins(config: ConfigType) -> None: def _final_validate(config: ConfigType) -> ConfigType: """Final validation for Ethernet component.""" + # Allow ethernet + wifi coexistence only when both are declared in network: priority:. + if "wifi" in fv.full_config.get(): + priority_ifaces = get_priority_interfaces_from_full_config(fv.full_config.get()) + missing = [i for i in ("ethernet", "wifi") if i not in priority_ifaces] + if missing and priority_ifaces: + # A priority list exists but is incomplete: point at what to add. + raise cv.Invalid( + "When ethernet and wifi are used together, 'network: priority:' must " + f"list both interfaces; missing: {', '.join(missing)}" + ) + if missing: + raise cv.Invalid( + "Component ethernet cannot be used together with component wifi " + "unless both are listed under 'network: priority:'" + ) + _final_validate_spi(config) _final_validate_rmii_pins(config) return config diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 7160351727..9f4398c621 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -112,8 +112,10 @@ enum class EthernetComponentState : uint8_t { // Platform-neutral duplex/speed types #ifndef USE_ESP32 +// NOLINTBEGIN(readability-identifier-naming) enum eth_duplex_t { ETH_DUPLEX_HALF, ETH_DUPLEX_FULL }; enum eth_speed_t { ETH_SPEED_10M, ETH_SPEED_100M }; +// NOLINTEND(readability-identifier-naming) #endif class EthernetComponent final : public Component { diff --git a/esphome/components/ethernet/ethernet_component_esp32.cpp b/esphome/components/ethernet/ethernet_component_esp32.cpp index 5ad1e7d483..94f4c23479 100644 --- a/esphome/components/ethernet/ethernet_component_esp32.cpp +++ b/esphome/components/ethernet/ethernet_component_esp32.cpp @@ -232,8 +232,10 @@ void EthernetComponent::ethernet_lazy_init_() { dm9051_config.poll_period_ms = this->polling_interval_; #endif #elif defined(USE_ETHERNET_ENC28J60) + // ENC28J60 does not support poll_period_ms. CS must stay asserted for the chip's CS hold + // time (t10, 210 ns) after the last clock or MAC/MII register reads fail ("wrong chip ID") + enc28j60_config.spi_devcfg->cs_ena_posttrans = enc28j60_cal_spi_cs_hold_time((this->clock_speed_ + 999999) / 1000000); enc28j60_config.int_gpio_num = this->interrupt_pin_; - // ENC28J60 does not support poll_period_ms #endif phy_config.phy_addr = this->phy_addr_spi_; diff --git a/esphome/components/ethernet/ethernet_component_rp2.cpp b/esphome/components/ethernet/ethernet_component_rp2.cpp index d2e3f14e02..4d6d6c4f5b 100644 --- a/esphome/components/ethernet/ethernet_component_rp2.cpp +++ b/esphome/components/ethernet/ethernet_component_rp2.cpp @@ -187,17 +187,18 @@ void EthernetComponent::loop() { } void EthernetComponent::dump_config() { - const char *type_str = "Unknown"; #if defined(USE_ETHERNET_W5500) - type_str = "W5500"; + const char *type_str = "W5500"; #elif defined(USE_ETHERNET_W5100) - type_str = "W5100"; + const char *type_str = "W5100"; #elif defined(USE_ETHERNET_W6100) - type_str = "W6100"; + const char *type_str = "W6100"; #elif defined(USE_ETHERNET_W6300) - type_str = "W6300"; + const char *type_str = "W6300"; #elif defined(USE_ETHERNET_ENC28J60) - type_str = "ENC28J60"; + const char *type_str = "ENC28J60"; +#else + const char *type_str = "Unknown"; #endif #if defined(USE_ETHERNET_W6300) // W6300 uses PIO QSPI with hardcoded pins — SPI pin fields are not used diff --git a/esphome/components/ezo_pmp/sensor.py b/esphome/components/ezo_pmp/sensor.py index a0473b292c..ed4efeeabc 100644 --- a/esphome/components/ezo_pmp/sensor.py +++ b/esphome/components/ezo_pmp/sensor.py @@ -23,8 +23,8 @@ CONF_PUMP_VOLTAGE = "pump_voltage" CONF_LAST_VOLUME_REQUESTED = "last_volume_requested" CONF_MAX_FLOW_RATE = "max_flow_rate" -UNIT_MILILITER = "ml" -UNIT_MILILITERS_PER_MINUTE = "ml/min" +UNIT_MILILITER = "mL" +UNIT_MILILITERS_PER_MINUTE = "mL/min" CONFIG_SCHEMA = cv.Schema( { diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 0fa69a23b4..da4dbf2ed7 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -1,4 +1,4 @@ -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #include "fastled_light.h" #include "esphome/core/log.h" diff --git a/esphome/components/fastled_base/fastled_light.h b/esphome/components/fastled_base/fastled_light.h index 1261b742a1..9f903b4530 100644 --- a/esphome/components/fastled_base/fastled_light.h +++ b/esphome/components/fastled_base/fastled_light.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #include "esphome/core/component.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/growatt_solar/growatt_solar.cpp b/esphome/components/growatt_solar/growatt_solar.cpp index fc35271017..d2102496a2 100644 --- a/esphome/components/growatt_solar/growatt_solar.cpp +++ b/esphome/components/growatt_solar/growatt_solar.cpp @@ -7,7 +7,6 @@ namespace esphome::growatt_solar { static const char *const TAG = "growatt_solar"; -static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion void GrowattSolar::loop() { @@ -31,11 +30,12 @@ void GrowattSolar::update() { } this->waiting_to_update_ = false; - this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]); + this->read_input_registers(0, MODBUS_REGISTER_COUNT[this->protocol_version_]); this->last_send_ = millis(); } -void GrowattSolar::on_modbus_data(const std::vector &data) { +void GrowattSolar::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); // Other components might be sending commands to our device. But we don't get called with enough // context to know what is what. So if we didn't do a send, we ignore the data. if (!this->last_send_) diff --git a/esphome/components/growatt_solar/growatt_solar.h b/esphome/components/growatt_solar/growatt_solar.h index 18a7c917d5..a172f49001 100644 --- a/esphome/components/growatt_solar/growatt_solar.h +++ b/esphome/components/growatt_solar/growatt_solar.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::growatt_solar { @@ -69,7 +69,7 @@ class GrowattSolar final : public PollingComponent, public modbus::ModbusClientD public: void loop() override; void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; void set_protocol_version(GrowattProtocolVersion protocol_version) { this->protocol_version_ = protocol_version; } diff --git a/esphome/components/gsl3670/touchscreen.py b/esphome/components/gsl3670/touchscreen.py index 11bb24ce44..fc0318f076 100644 --- a/esphome/components/gsl3670/touchscreen.py +++ b/esphome/components/gsl3670/touchscreen.py @@ -68,6 +68,21 @@ MODELS = { CONF_SHA256: "2e50501ad83656fb6fa3d92591f9f31add4d442c8e8a79f29f5c4d335bd127a4", }, }, + "GUITION-JC8012P4A1": { + CONF_SWAP_XY: True, + CONF_MIRROR_X: True, + CONF_MIRROR_Y: False, + CONF_X_MIN: 20, + CONF_Y_MIN: 20, + CONF_X_MAX: 880, + CONF_Y_MAX: 1648, + CONF_RESET_PIN: 22, + CONF_INTERRUPT_PIN: 21, + CONF_FIRMWARE: { + CONF_URL: f"{FIRMWARE_BASE_URL}/seeed-d1001-fw.bin", + CONF_SHA256: "2e50501ad83656fb6fa3d92591f9f31add4d442c8e8a79f29f5c4d335bd127a4", + }, + }, "CUSTOM": {}, } diff --git a/esphome/components/haier/haier_base.cpp b/esphome/components/haier/haier_base.cpp index 74a218263d..294aa53b03 100644 --- a/esphome/components/haier/haier_base.cpp +++ b/esphome/components/haier/haier_base.cpp @@ -132,7 +132,7 @@ void HaierClimateBase::save_settings() { } bool HaierClimateBase::get_display_state() const { - return (this->display_status_ == SwitchState::ON) || (this->display_status_ == SwitchState::PENDING_ON); + return (this->display_status_ == SwitchState::SWITCH_ON) || (this->display_status_ == SwitchState::PENDING_ON); } void HaierClimateBase::set_display_state(bool state) { @@ -144,7 +144,7 @@ void HaierClimateBase::set_display_state(bool state) { } bool HaierClimateBase::get_health_mode() const { - return (this->health_mode_ == SwitchState::ON) || (this->health_mode_ == SwitchState::PENDING_ON); + return (this->health_mode_ == SwitchState::SWITCH_ON) || (this->health_mode_ == SwitchState::PENDING_ON); } void HaierClimateBase::set_health_mode(bool state) { diff --git a/esphome/components/haier/haier_base.h b/esphome/components/haier/haier_base.h index 13e8d7548d..db4c1abceb 100644 --- a/esphome/components/haier/haier_base.h +++ b/esphome/components/haier/haier_base.h @@ -147,8 +147,8 @@ class HaierClimateBase : public esphome::Component, esphome::optional message; }; enum class SwitchState { - OFF = 0b00, - ON = 0b01, + SWITCH_OFF = 0b00, + SWITCH_ON = 0b01, PENDING_OFF = 0b10, PENDING_ON = 0b11, }; @@ -157,8 +157,8 @@ class HaierClimateBase : public esphome::Component, esphome::optional action_request_; uint8_t fan_mode_speed_; uint8_t other_modes_fan_speed_; - SwitchState display_status_{SwitchState::ON}; - SwitchState health_mode_{SwitchState::OFF}; + SwitchState display_status_{SwitchState::SWITCH_ON}; + SwitchState health_mode_{SwitchState::SWITCH_OFF}; bool force_send_control_; bool forced_request_status_; bool reset_protocol_request_; diff --git a/esphome/components/haier/hon_climate.cpp b/esphome/components/haier/hon_climate.cpp index f68404afd9..881a2328cb 100644 --- a/esphome/components/haier/hon_climate.cpp +++ b/esphome/components/haier/hon_climate.cpp @@ -50,7 +50,7 @@ void HonClimate::set_quiet_mode_state(bool state) { this->quiet_mode_state_ = state ? SwitchState::PENDING_ON : SwitchState::PENDING_OFF; this->force_send_control_ = true; } else { - this->quiet_mode_state_ = state ? SwitchState::ON : SwitchState::OFF; + this->quiet_mode_state_ = state ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; } this->settings_.quiet_mode_state = state; #ifdef USE_SWITCH @@ -63,7 +63,7 @@ void HonClimate::set_quiet_mode_state(bool state) { } bool HonClimate::get_quiet_mode_state() const { - return (this->quiet_mode_state_ == SwitchState::ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON); + return (this->quiet_mode_state_ == SwitchState::SWITCH_ON) || (this->quiet_mode_state_ == SwitchState::PENDING_ON); } esphome::optional HonClimate::get_vertical_airflow() const { @@ -513,7 +513,7 @@ void HonClimate::initialization() { } this->current_vertical_swing_ = this->settings_.last_vertiacal_swing; this->current_horizontal_swing_ = this->settings_.last_horizontal_swing; - this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::ON : SwitchState::OFF; + this->quiet_mode_state_ = this->settings_.quiet_mode_state ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; } haier_protocol::HaierMessage HonClimate::get_control_message() { @@ -825,7 +825,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * #ifdef USE_SENSOR this->update_sub_sensor_(SubSensorType::INDOOR_COIL_TEMPERATURE, bd_packet->indoor_coil_temperature / 2.0 - 20); this->update_sub_sensor_(SubSensorType::OUTDOOR_COIL_TEMPERATURE, bd_packet->outdoor_coil_temperature - 64); - this->update_sub_sensor_(SubSensorType::OUTDOOR_DEFROST_TEMPERATURE, bd_packet->outdoor_coil_temperature - 64); + this->update_sub_sensor_(SubSensorType::OUTDOOR_DEFROST_TEMPERATURE, bd_packet->outdoor_defrost_temperature - 64); this->update_sub_sensor_(SubSensorType::OUTDOOR_IN_AIR_TEMPERATURE, bd_packet->outdoor_in_air_temperature - 64); this->update_sub_sensor_(SubSensorType::OUTDOOR_OUT_AIR_TEMPERATURE, bd_packet->outdoor_out_air_temperature - 64); this->update_sub_sensor_(SubSensorType::POWER, encode_uint16(bd_packet->power[0], bd_packet->power[1])); @@ -939,14 +939,14 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * // AC just turned on from remote need to turn off display this->force_send_control_ = true; } else if ((((uint8_t) this->display_status_) & 0b10) == 0) { - this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; + this->display_status_ = disp_status ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; } } } // Health mode if ((((uint8_t) this->health_mode_) & 0b10) == 0) { bool old_health_mode = this->get_health_mode(); - this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; + this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; should_publish = should_publish || (old_health_mode != this->get_health_mode()); } { @@ -1008,7 +1008,7 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t * // In proper mode and not in pending state bool new_quiet_mode = packet.control.quiet_mode != 0; if (new_quiet_mode != this->get_quiet_mode_state()) { - this->quiet_mode_state_ = new_quiet_mode ? SwitchState::ON : SwitchState::OFF; + this->quiet_mode_state_ = new_quiet_mode ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; this->settings_.quiet_mode_state = new_quiet_mode; #ifdef USE_SWITCH if (this->quiet_mode_switch_ != nullptr) { diff --git a/esphome/components/haier/hon_climate.h b/esphome/components/haier/hon_climate.h index ba36e6a8fb..a34b4422c6 100644 --- a/esphome/components/haier/hon_climate.h +++ b/esphome/components/haier/hon_climate.h @@ -197,7 +197,7 @@ class HonClimate final : public HaierClimateBase { esphome::optional current_horizontal_swing_{}; HonSettings settings_{}; ESPPreferenceObject hon_rtc_; - SwitchState quiet_mode_state_{SwitchState::OFF}; + SwitchState quiet_mode_state_{SwitchState::SWITCH_OFF}; }; } // namespace esphome::haier diff --git a/esphome/components/haier/smartair2_climate.cpp b/esphome/components/haier/smartair2_climate.cpp index a013371649..fdb3b779e2 100644 --- a/esphome/components/haier/smartair2_climate.cpp +++ b/esphome/components/haier/smartair2_climate.cpp @@ -464,14 +464,14 @@ haier_protocol::HandlerError Smartair2Climate::process_status_message_(const uin // AC just turned on from remote need to turn off display this->force_send_control_ = true; } else if ((((uint8_t) this->health_mode_) & 0b10) == 0) { - this->display_status_ = disp_status ? SwitchState::ON : SwitchState::OFF; + this->display_status_ = disp_status ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; } } } // Health mode if ((((uint8_t) this->health_mode_) & 0b10) == 0) { bool old_health_mode = this->get_health_mode(); - this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::ON : SwitchState::OFF; + this->health_mode_ = packet.control.health_mode == 1 ? SwitchState::SWITCH_ON : SwitchState::SWITCH_OFF; should_publish = should_publish || (old_health_mode != this->get_health_mode()); } { diff --git a/esphome/components/havells_solar/havells_solar.cpp b/esphome/components/havells_solar/havells_solar.cpp index 9257a37fd9..6af72c352b 100644 --- a/esphome/components/havells_solar/havells_solar.cpp +++ b/esphome/components/havells_solar/havells_solar.cpp @@ -7,10 +7,10 @@ namespace esphome::havells_solar { static const char *const TAG = "havells_solar"; -static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x03; static const uint8_t MODBUS_REGISTER_COUNT = 48; // 48 x 16-bit registers -void HavellsSolar::on_modbus_data(const std::vector &data) { +void HavellsSolar::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); if (data.size() < MODBUS_REGISTER_COUNT * 2) { ESP_LOGW(TAG, "Invalid size for HavellsSolar!"); return; @@ -121,7 +121,7 @@ void HavellsSolar::on_modbus_data(const std::vector &data) { this->dci_of_t_sensor_->publish_state(dci_of_t); } -void HavellsSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } +void HavellsSolar::update() { this->read_holding_registers(0, MODBUS_REGISTER_COUNT); } void HavellsSolar::dump_config() { ESP_LOGCONFIG(TAG, "HAVELLS Solar:\n" diff --git a/esphome/components/havells_solar/havells_solar.h b/esphome/components/havells_solar/havells_solar.h index 02e999c56c..ed5d13b8b6 100644 --- a/esphome/components/havells_solar/havells_solar.h +++ b/esphome/components/havells_solar/havells_solar.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::havells_solar { @@ -77,7 +77,7 @@ class HavellsSolar final : public PollingComponent, public modbus::ModbusClientD void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; diff --git a/esphome/components/hlk_fm22x/hlk_fm22x.cpp b/esphome/components/hlk_fm22x/hlk_fm22x.cpp index 7a0dc0690c..964d26dfbc 100644 --- a/esphome/components/hlk_fm22x/hlk_fm22x.cpp +++ b/esphome/components/hlk_fm22x/hlk_fm22x.cpp @@ -242,7 +242,7 @@ void HlkFm22xComponent::handle_reply_(const uint8_t *data, size_t length) { return; } - if (data[1] != HlkFm22xResult::SUCCESS) { + if (data[1] != HlkFm22xResult::SUCCEEDED) { ESP_LOGE(TAG, "Command <0x%.2X> failed. Error: 0x%.2X", data[0], data[1]); switch (expected) { case HlkFm22xCommand::ENROLL: diff --git a/esphome/components/hlk_fm22x/hlk_fm22x.h b/esphome/components/hlk_fm22x/hlk_fm22x.h index 34246f52f0..3bdf6e2c71 100644 --- a/esphome/components/hlk_fm22x/hlk_fm22x.h +++ b/esphome/components/hlk_fm22x/hlk_fm22x.h @@ -41,7 +41,7 @@ enum HlkFm22xNoteType { }; enum HlkFm22xResult { - SUCCESS = 0x00, + SUCCEEDED = 0x00, REJECTED = 0x01, ABORTED = 0x02, FAILED4_CAMERA = 0x04, diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index f46a6b8580..02c6e75146 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -61,7 +61,7 @@ void HM3301Component::update() { int16_t aqi_value = -1; if (this->aqi_sensor_ != nullptr && pm_2_5_value != -1 && pm_10_0_value != -1) { aqi::AbstractAQICalculator *calculator = this->aqi_calculator_factory_.get_calculator(this->aqi_calc_type_); - aqi_value = calculator->get_aqi(pm_2_5_value, pm_10_0_value); + aqi_value = calculator->get_aqi(pm_2_5_value, pm_10_0_value, /*extended_range=*/false); } if (pm_1_0_value != -1) { diff --git a/esphome/components/host/__init__.py b/esphome/components/host/__init__.py index 50deb1acf6..795c1a556d 100644 --- a/esphome/components/host/__init__.py +++ b/esphome/components/host/__init__.py @@ -10,6 +10,7 @@ from esphome.const import ( ThreadModel, ) from esphome.core import CORE +from esphome.platformio.toolchain import copy_ccache_script from .const import KEY_HOST @@ -49,3 +50,9 @@ async def to_code(config): cg.add_platformio_option("platform", "platformio/native") cg.add_platformio_option("lib_ldf_mode", "off") cg.add_platformio_option("lib_compat_mode", "strict") + cg.add_platformio_option("extra_scripts", ["pre:ccache.py"]) + + +# Called by writer.py +def copy_files() -> None: + copy_ccache_script() diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index df1bb462ab..4471dffdc2 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -488,10 +488,10 @@ template class HttpRequestSendAction final : public Actionbody_.value(x...); } if (!this->json_.empty()) { - body = json::build_json([this, x...](JsonObject root) { this->encode_json_(x..., root); }); + body = json::build_json([this, x...](JsonObject root) mutable { this->encode_json_(x..., root); }); } if (this->json_func_ != nullptr) { - body = json::build_json([this, x...](JsonObject root) { this->json_func_(x..., root); }); + body = json::build_json([this, x...](JsonObject root) mutable { this->json_func_(x..., root); }); } std::vector
request_headers; request_headers.reserve(this->request_headers_.size()); diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 1760cb9395..84333e7169 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -1,6 +1,6 @@ #include "http_request_arduino.h" -#if defined(USE_ARDUINO) && !defined(USE_ESP32) +#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY) #include "esphome/components/network/util.h" #include "esphome/components/watchdog/watchdog.h" diff --git a/esphome/components/http_request/http_request_arduino.h b/esphome/components/http_request/http_request_arduino.h index c109de8a39..028b9f44a1 100644 --- a/esphome/components/http_request/http_request_arduino.h +++ b/esphome/components/http_request/http_request_arduino.h @@ -2,7 +2,7 @@ #include "http_request.h" -#if defined(USE_ARDUINO) && !defined(USE_ESP32) +#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY) #if defined(USE_RP2) #include diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 3e341395a4..a437540241 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -19,11 +19,6 @@ namespace esphome::http_request { static const char *const TAG = "http_request.idf"; static constexpr uint32_t ERROR_DURATION_MS = 1000; -struct UserData { - const std::vector &lower_case_collect_headers; - std::vector
&response_headers; -}; - void HttpRequestIDF::dump_config() { HttpRequestComponent::dump_config(); ESP_LOGCONFIG(TAG, @@ -34,15 +29,15 @@ void HttpRequestIDF::dump_config() { } esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { - UserData *user_data = (UserData *) evt->user_data; + auto *container = (HttpContainerIDF *) evt->user_data; switch (evt->event_id) { case HTTP_EVENT_ON_HEADER: { const std::string header_name = str_lower_case(evt->header_key); // NOLINT - if (should_collect_header(user_data->lower_case_collect_headers, header_name)) { + if (should_collect_header(container->collect_headers_, header_name)) { const std::string header_value = evt->header_value; ESP_LOGD(TAG, "Received response header, name: %s, value: %s", header_name.c_str(), header_value.c_str()); - user_data->response_headers.push_back({header_name, header_value}); + container->response_headers_.push_back({header_name, header_value}); } break; } @@ -124,8 +119,8 @@ std::shared_ptr HttpRequestIDF::perform(const std::string &url, c container->set_secure(secure); - auto user_data = UserData{lower_case_collect_headers, container->response_headers_}; - esp_http_client_set_user_data(client, static_cast(&user_data)); + container->collect_headers_ = lower_case_collect_headers; + esp_http_client_set_user_data(client, static_cast(container.get())); for (const auto &header : request_headers) { esp_http_client_set_header(client, header.name.c_str(), header.value.c_str()); diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 8a803b5469..16a5b6a161 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -24,6 +24,8 @@ class HttpContainerIDF : public HttpContainer { protected: friend class HttpRequestIDF; esp_http_client_handle_t client_; + // Owned copy (not a reference): must outlive perform() for the response-header event handler + std::vector collect_headers_; }; class HttpRequestIDF final : public HttpRequestComponent { diff --git a/esphome/components/i2c/i2c_bus_arduino.cpp b/esphome/components/i2c/i2c_bus_arduino.cpp index 871f67a4c8..cc036b12c3 100644 --- a/esphome/components/i2c/i2c_bus_arduino.cpp +++ b/esphome/components/i2c/i2c_bus_arduino.cpp @@ -1,4 +1,4 @@ -#if defined(USE_ARDUINO) && !defined(USE_ESP32) +#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY) #include "i2c_bus_arduino.h" #include diff --git a/esphome/components/i2c/i2c_bus_arduino.h b/esphome/components/i2c/i2c_bus_arduino.h index ded28dd80c..71e91e770b 100644 --- a/esphome/components/i2c/i2c_bus_arduino.h +++ b/esphome/components/i2c/i2c_bus_arduino.h @@ -1,6 +1,6 @@ #pragma once -#if defined(USE_ARDUINO) && !defined(USE_ESP32) +#if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY) #include #include "esphome/core/component.h" diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 66ca32b830..7b074b2e8f 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -245,7 +245,7 @@ void I2SAudioMicrophone::mic_task(void *params) { while (!(xEventGroupGetBits(this_microphone->event_group_) & MicrophoneEventGroupBits::COMMAND_STOP)) { if (this_microphone->data_callbacks_.size() > 0) { samples.resize(bytes_to_read); - size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * pdMS_TO_TICKS(READ_DURATION_MS)); + size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * READ_DURATION_MS); samples.resize(bytes_read); if (this_microphone->correct_dc_offset_) { this_microphone->fix_dc_offset_(samples); @@ -318,12 +318,11 @@ void I2SAudioMicrophone::fix_dc_offset_(std::vector &data) { } } -size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait) { +size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, uint32_t timeout_ms) { size_t bytes_read = 0; - // i2s_channel_read expects the timeout value in ms, not ticks - esp_err_t err = i2s_channel_read(this->rx_handle_, buf, len, &bytes_read, pdTICKS_TO_MS(ticks_to_wait)); - if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (ticks_to_wait != 0))) { - // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call + esp_err_t err = i2s_channel_read(this->rx_handle_, buf, len, &bytes_read, timeout_ms); + if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (timeout_ms != 0))) { + // Ignore ESP_ERR_TIMEOUT if timeout_ms = 0, as it will read the data on the next call if (!this->status_has_warning()) { // Avoid spamming the logs with the error message if its repeated ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err)); @@ -331,7 +330,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w this->status_set_warning(); return 0; } - if ((bytes_read == 0) && (ticks_to_wait > 0)) { + if ((bytes_read == 0) && (timeout_ms > 0)) { this->status_set_warning(); return 0; } diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 65ad7df1af..2c6528d8bf 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -42,7 +42,7 @@ class I2SAudioMicrophone final : public I2SAudioIn, public microphone::Microphon /// @param data void fix_dc_offset_(std::vector &data); - size_t read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait); + size_t read_(uint8_t *buf, size_t len, uint32_t timeout_ms); /// @brief Sets the Microphone ``audio_stream_info_`` member variable to the configured I2S settings. void configure_stream_settings_(); diff --git a/esphome/components/improv_base/__init__.py b/esphome/components/improv_base/__init__.py index e175aa2220..5929f2b60a 100644 --- a/esphome/components/improv_base/__init__.py +++ b/esphome/components/improv_base/__init__.py @@ -42,4 +42,4 @@ async def setup_improv_core(var: MockObj, config: ConfigType, component: str): cg.add(var.set_next_url(_process_next_url(next_url))) cg.add_define(f"USE_{component.upper()}_NEXT_URL") - cg.add_library("improv/Improv", "1.2.4") + cg.add_library("improv/Improv", "1.2.6") diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index 4ee703f363..a191889138 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -16,6 +16,7 @@ void ImprovSerialComponent::setup() { global_improv_serial_component = this; #ifdef USE_ESP32 this->uart_num_ = logger::global_logger->get_uart_num(); + this->uart_selection_ = logger::global_logger->get_uart(); #elif defined(USE_ARDUINO) this->hw_serial_ = logger::global_logger->get_hw_serial(); #endif @@ -30,21 +31,23 @@ void ImprovSerialComponent::setup() { } void ImprovSerialComponent::loop() { - if (this->last_read_byte_ && (millis() - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) { + const uint32_t now = App.get_loop_component_start_time(); + if (this->last_read_byte_ && (now - this->last_read_byte_ > IMPROV_SERIAL_TIMEOUT)) { this->last_read_byte_ = 0; this->rx_buffer_.clear(); ESP_LOGV(TAG, "Timeout"); } - auto byte = this->read_byte_(); - while (byte.has_value()) { + while (true) { + auto byte = this->read_byte_(); + if (!byte.has_value()) + break; if (this->parse_improv_serial_byte_(byte.value())) { - this->last_read_byte_ = millis(); + this->last_read_byte_ = now; } else { this->last_read_byte_ = 0; this->rx_buffer_.clear(); } - byte = this->read_byte_(); } if (this->state_ == improv::STATE_PROVISIONING) { @@ -63,53 +66,6 @@ void ImprovSerialComponent::loop() { void ImprovSerialComponent::dump_config() { ESP_LOGCONFIG(TAG, "Improv Serial:"); } -optional ImprovSerialComponent::read_byte_() { - optional byte; - uint8_t data = 0; -#ifdef USE_ESP32 - switch (logger::global_logger->get_uart()) { - case logger::UART_SELECTION_UART0: - case logger::UART_SELECTION_UART1: -#if defined(USE_ESP32_VARIANT_ESP32) - case logger::UART_SELECTION_UART2: -#endif - if (this->uart_num_ >= 0) { - size_t available; - uart_get_buffered_data_len(this->uart_num_, &available); - if (available) { - uart_read_bytes(this->uart_num_, &data, 1, 0); - byte = data; - } - } - break; -#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) - case logger::UART_SELECTION_USB_CDC: - if (esp_usb_console_available_for_read()) { - esp_usb_console_read_buf((char *) &data, 1); - byte = data; - } - break; -#endif // USE_LOGGER_USB_CDC -#ifdef USE_LOGGER_USB_SERIAL_JTAG - case logger::UART_SELECTION_USB_SERIAL_JTAG: { - if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) { - byte = data; - } - break; - } -#endif // USE_LOGGER_USB_SERIAL_JTAG - default: - break; - } -#elif defined(USE_ARDUINO) - if (this->hw_serial_->available()) { - this->hw_serial_->readBytes(&data, 1); - byte = data; - } -#endif - return byte; -} - void ImprovSerialComponent::write_data_(const uint8_t *data, const size_t size) { // First, set length field this->tx_header_[TX_LENGTH_IDX] = this->tx_header_[TX_TYPE_IDX] == TYPE_RPC_RESPONSE ? size : 1; @@ -133,7 +89,7 @@ void ImprovSerialComponent::write_data_(const uint8_t *data, const size_t size) this->tx_header_[TX_CHECKSUM_IDX] = checksum; #ifdef USE_ESP32 - switch (logger::global_logger->get_uart()) { + switch (this->uart_selection_) { case logger::UART_SELECTION_UART0: case logger::UART_SELECTION_UART1: #if defined(USE_ESP32_VARIANT_ESP32) diff --git a/esphome/components/improv_serial/improv_serial_component.h b/esphome/components/improv_serial/improv_serial_component.h index 4df6f6df2d..00c40c4c7e 100644 --- a/esphome/components/improv_serial/improv_serial_component.h +++ b/esphome/components/improv_serial/improv_serial_component.h @@ -1,6 +1,7 @@ #pragma once #include "esphome/components/improv_base/improv_base.h" +#include "esphome/components/logger/logger.h" #include "esphome/components/wifi/wifi_component.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" @@ -65,7 +66,52 @@ class ImprovSerialComponent final : public Component, public improv_base::Improv std::vector build_rpc_settings_response_(improv::Command command); std::vector build_version_info_(); - optional read_byte_(); + ESPHOME_ALWAYS_INLINE optional read_byte_() { + optional byte; + uint8_t data = 0; +#ifdef USE_ESP32 + switch (this->uart_selection_) { + case logger::UART_SELECTION_UART0: + case logger::UART_SELECTION_UART1: +#if defined(USE_ESP32_VARIANT_ESP32) + case logger::UART_SELECTION_UART2: +#endif + if (this->uart_num_ >= 0) { + size_t available; + uart_get_buffered_data_len(this->uart_num_, &available); + if (available) { + uart_read_bytes(this->uart_num_, &data, 1, 0); + byte = data; + } + } + break; +#if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) + case logger::UART_SELECTION_USB_CDC: + if (esp_usb_console_available_for_read()) { + esp_usb_console_read_buf((char *) &data, 1); + byte = data; + } + break; +#endif +#ifdef USE_LOGGER_USB_SERIAL_JTAG + case logger::UART_SELECTION_USB_SERIAL_JTAG: { + if (usb_serial_jtag_read_bytes((char *) &data, 1, 0)) { + byte = data; + } + break; + } +#endif + default: + break; + } +#elif defined(USE_ARDUINO) + if (this->hw_serial_->available()) { + this->hw_serial_->readBytes(&data, 1); + byte = data; + } +#endif + return byte; + } void write_data_(const uint8_t *data = nullptr, size_t size = 0); uint8_t tx_header_[TX_BUFFER_SIZE] = { @@ -85,6 +131,7 @@ class ImprovSerialComponent final : public Component, public improv_base::Improv #ifdef USE_ESP32 uart_port_t uart_num_; + logger::UARTSelection uart_selection_{logger::UART_SELECTION_UART0}; #elif defined(USE_ARDUINO) Stream *hw_serial_{nullptr}; #endif diff --git a/esphome/components/kuntze/kuntze.cpp b/esphome/components/kuntze/kuntze.cpp index 6df114e93c..c47a80777c 100644 --- a/esphome/components/kuntze/kuntze.cpp +++ b/esphome/components/kuntze/kuntze.cpp @@ -7,13 +7,13 @@ namespace esphome::kuntze { static const char *const TAG = "kuntze"; -static const uint8_t CMD_READ_REG = 0x03; static const uint16_t REGISTER[] = {4136, 4160, 4680, 6000, 4688, 4728, 5832}; // Maximum bytes to log for Modbus responses (2 registers = 4, plus count = 5) static constexpr size_t KUNTZE_MAX_LOG_BYTES = 8; -void Kuntze::on_modbus_data(const std::vector &data) { +void Kuntze::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); auto get_16bit = [&](int i) -> uint16_t { return (uint16_t(data[i * 2]) << 8) | uint16_t(data[i * 2 + 1]); }; this->waiting_ = false; @@ -76,7 +76,7 @@ void Kuntze::loop() { if (this->waiting_ || (this->state_ == 0)) return; this->last_send_ = now; - send(CMD_READ_REG, REGISTER[this->state_ - 1], 2); + this->read_holding_registers(REGISTER[this->state_ - 1], 2); this->waiting_ = true; } diff --git a/esphome/components/kuntze/kuntze.h b/esphome/components/kuntze/kuntze.h index 46681843d2..28c8089748 100644 --- a/esphome/components/kuntze/kuntze.h +++ b/esphome/components/kuntze/kuntze.h @@ -4,6 +4,8 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" +#include + namespace esphome::kuntze { class Kuntze final : public PollingComponent, public modbus::ModbusClientDevice { @@ -19,7 +21,7 @@ class Kuntze final : public PollingComponent, public modbus::ModbusClientDevice void loop() override; void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; diff --git a/esphome/components/libretiny/__init__.py b/esphome/components/libretiny/__init__.py index 62cef331fd..7dbce7a07c 100644 --- a/esphome/components/libretiny/__init__.py +++ b/esphome/components/libretiny/__init__.py @@ -26,6 +26,7 @@ from esphome.const import ( from esphome.core import CORE from esphome.core.config import BOARD_MAX_LENGTH from esphome.helpers import copy_file_if_changed +from esphome.platformio.toolchain import copy_ccache_script from esphome.storage_json import StorageJSON from . import gpio # noqa: F401 @@ -506,6 +507,7 @@ async def component_to_code(config): # it for project source files only. GCC uses the last -O flag. build_src_flags += " -Os" cg.add_platformio_option("build_src_flags", build_src_flags) + cg.add_platformio_option("extra_scripts", ["pre:ccache.py"]) # IRAM_ATTR is a no-op on BK72xx (SDK masks FIQ+IRQ around flash ops). # On other families, patch_linker.py routes .sram.text into the right # RAM-executable output section and prints a post-link placement summary. @@ -580,8 +582,13 @@ async def component_to_code(config): cg.add_platformio_option("custom_fw_name", "esphome") cg.add_platformio_option("custom_fw_version", __version__) - # Apply chip-specific SDK options to save RAM/Flash - if config[CONF_FAMILY] in (FAMILY_BK7231N, FAMILY_BK7238): + # Apply chip-specific SDK options to save RAM/Flash. + # Skipped when bk72xx_ble is configured: add_platformio_option APPENDS list + # values (it never replaces), so emitting the disable here as well would put + # both CFG_SUPPORT_BLE=0 and =1 into the generated sys_config.h and rely on + # last-wins emission order. Skipping keeps it a single unambiguous define. + ble_requested = "bk72xx_ble" in CORE.config + if config[CONF_FAMILY] in (FAMILY_BK7231N, FAMILY_BK7238) and not ble_requested: cg.add_platformio_option( "custom_options.sys_config#h", _BLE5_BK_SYS_CONFIG_OPTIONS ) @@ -605,3 +612,4 @@ def copy_files() -> None: patch_linker_file, CORE.relative_build_path("patch_linker.py"), ) + copy_ccache_script() diff --git a/esphome/components/libretiny/hal.cpp b/esphome/components/libretiny/hal.cpp index 67e902024d..01b276005d 100644 --- a/esphome/components/libretiny/hal.cpp +++ b/esphome/components/libretiny/hal.cpp @@ -44,7 +44,7 @@ void arch_init() { void arch_restart() { lt_reboot(); - while (1) { + while (true) { } } diff --git a/esphome/components/libretiny/hal.h b/esphome/components/libretiny/hal.h index 01a7b5450b..48b94a5214 100644 --- a/esphome/components/libretiny/hal.h +++ b/esphome/components/libretiny/hal.h @@ -44,6 +44,7 @@ // it is callable from Thumb code via interworking. The MRS CPSR instruction // is ARM-only and user code here may be built in Thumb, so in_isr_context() // defers to this port helper on BK72xx instead of reading CPSR inline. +// NOLINTNEXTLINE(readability-redundant-declaration) extern "C" uint32_t platform_is_in_interrupt_context(void); #endif @@ -59,9 +60,11 @@ extern "C" void delayMicroseconds(unsigned int us); // Forward decls from libretiny's family for the inline arch_* // wrappers below. Pulling the full header would drag in the rest of the // LibreTiny C API. +// NOLINTBEGIN(readability-redundant-declaration) extern "C" void lt_wdt_feed(void); extern "C" uint32_t lt_cpu_get_cycle_count(void); extern "C" uint32_t lt_cpu_get_freq(void); +// NOLINTEND(readability-redundant-declaration) namespace esphome::libretiny {} diff --git a/esphome/components/libretiny/lt_component.cpp b/esphome/components/libretiny/lt_component.cpp index 9bbbd66be4..0ab064e3e1 100644 --- a/esphome/components/libretiny/lt_component.cpp +++ b/esphome/components/libretiny/lt_component.cpp @@ -13,14 +13,14 @@ void LTComponent::dump_config() { "LibreTiny:\n" " Version: %s\n" " Loglevel: %u", - LT_BANNER_STR + 10, LT_LOGLEVEL); + <_BANNER_STR[10], LT_LOGLEVEL); #if defined(__OPTIMIZE_SIZE__) && __OPTIMIZE_LEVEL__ > 0 && __OPTIMIZE_LEVEL__ <= 3 ESP_LOGCONFIG(TAG, " Optimization: -Os, SDK: -O" STRINGIFY_MACRO(__OPTIMIZE_LEVEL__)); #endif #ifdef USE_TEXT_SENSOR if (this->version_ != nullptr) { - this->version_->publish_state(LT_BANNER_STR + 10); + this->version_->publish_state(<_BANNER_STR[10]); } #endif // USE_TEXT_SENSOR } diff --git a/esphome/components/libretiny/preference_backend.h b/esphome/components/libretiny/preference_backend.h index 66b6847bee..f7f8279ac0 100644 --- a/esphome/components/libretiny/preference_backend.h +++ b/esphome/components/libretiny/preference_backend.h @@ -5,8 +5,10 @@ #include // Forward declare FlashDB types to avoid pulling in flashdb.h +// NOLINTBEGIN(readability-identifier-naming) struct fdb_kvdb; struct fdb_blob; +// NOLINTEND(readability-identifier-naming) namespace esphome::libretiny { diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp index 2b13b40a16..4251565e85 100644 --- a/esphome/components/light/light_call.cpp +++ b/esphome/components/light/light_call.cpp @@ -219,14 +219,23 @@ LightColorValues LightCall::validate_() { this->set_flag_(FLAG_HAS_STATE); } - // Make sure a turn-on makes the light visible: if the resulting brightness would be zero - // (e.g. restored from a brightness=0 turn-off), reset it to full brightness. - if (this->has_state() && this->state_ && (color_mode & ColorCapability::BRIGHTNESS)) { - float brightness = this->has_brightness() ? this->brightness_ : this->parent_->remote_values.get_brightness(); - if (brightness == 0.0f) { - this->brightness_ = 1.0f; - this->set_flag_(FLAG_HAS_BRIGHTNESS); - } + // A light without brightness control has no way to represent "on but dark", so zero + // brightness -- how effects encode their dark phase -- means the light is off. Clear the + // brightness as well, so a zero can't linger in remote_values and leave the light stuck + // off: a later turn-on can't heal it, because the capability check below drops any + // brightness this mode doesn't support. explicit_turn_off_request was captured above, so + // a running effect is not stopped by this. + if (this->has_brightness() && this->brightness_ == 0.0f && !(color_mode & ColorCapability::BRIGHTNESS)) { + this->state_ = false; + this->set_flag_(FLAG_HAS_STATE); + this->clear_flag_(FLAG_HAS_BRIGHTNESS); + } + + // Make sure a simple (no specific brightness) turn-on makes the light visible + if (this->has_state() && this->state_ && (color_mode & ColorCapability::BRIGHTNESS) && !this->has_brightness() && + this->parent_->remote_values.get_brightness() == 0.0f) { + this->brightness_ = 1.0f; + this->set_flag_(FLAG_HAS_BRIGHTNESS); } // Set color brightness to 100% if currently zero and a color is set. diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index bd778926d5..9d0181a05c 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -71,6 +71,14 @@ void LightState::setup() { break; } + // A light coming up on boot must never end up on-but-invisible: if the resolved restore + // state is on but its brightness is zero (e.g. a stale/persisted value from before a + // forced-on restore mode, or an inverted restore flipping a dim-to-0 off state to on), + // reset it to full brightness. + if (recovered.state && recovered.brightness == 0.0f) { + recovered.brightness = 1.0f; + } + call.set_color_mode_if_supported(recovered.color_mode); call.set_state(recovered.state); call.set_brightness_if_supported(recovered.brightness); diff --git a/esphome/components/logger/logger_host.cpp b/esphome/components/logger/logger_host.cpp index fe094f6e9e..708ab883e7 100644 --- a/esphome/components/logger/logger_host.cpp +++ b/esphome/components/logger/logger_host.cpp @@ -21,6 +21,7 @@ void HOT Logger::write_msg_(const char *msg, uint16_t len) { // Single write for everything fwrite(buffer, 1, pos, stdout); + fflush(stdout); } void Logger::pre_setup() { global_logger = this; } diff --git a/esphome/components/logger/task_log_buffer_libretiny.cpp b/esphome/components/logger/task_log_buffer_libretiny.cpp index b6d6b22ab5..5cde18d19e 100644 --- a/esphome/components/logger/task_log_buffer_libretiny.cpp +++ b/esphome/components/logger/task_log_buffer_libretiny.cpp @@ -20,7 +20,7 @@ TaskLogBuffer::~TaskLogBuffer() { } } -size_t TaskLogBuffer::available_contiguous_space() const { +size_t TaskLogBuffer::available_contiguous_space_() const { if (this->head_ >= this->tail_) { // head is ahead of or equal to tail // Available space is from head to end, plus from start to tail @@ -81,7 +81,7 @@ void TaskLogBuffer::release_message_main_loop() { this->tail_ = 0; } - this->message_count_--; + this->message_count_ = this->message_count_ - 1; this->current_message_size_ = 0; xSemaphoreGive(this->mutex_); @@ -117,7 +117,7 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin } // Check if we have enough contiguous space - size_t contiguous = this->available_contiguous_space(); + size_t contiguous = this->available_contiguous_space_(); if (contiguous < total_size) { // Not enough contiguous space at end @@ -128,9 +128,9 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin } // Need at least enough space to safely write padding marker (level field is at end of struct) - constexpr size_t PADDING_MARKER_MIN_SPACE = offsetof(LogMessage, level) + 1; + constexpr size_t padding_marker_min_space = offsetof(LogMessage, level) + 1; - if (space_at_start >= total_size && this->head_ > 0 && contiguous >= PADDING_MARKER_MIN_SPACE) { + if (space_at_start >= total_size && this->head_ > 0 && contiguous >= padding_marker_min_space) { // Add padding marker (set level field to indicate this is padding, not a real message) LogMessage *padding = reinterpret_cast(this->storage_ + this->head_); padding->level = PADDING_MARKER_LEVEL; @@ -180,7 +180,7 @@ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uin this->head_ = 0; } - this->message_count_++; + this->message_count_ = this->message_count_ + 1; xSemaphoreGive(this->mutex_); return true; diff --git a/esphome/components/logger/task_log_buffer_libretiny.h b/esphome/components/logger/task_log_buffer_libretiny.h index b42894502a..ce469a1a18 100644 --- a/esphome/components/logger/task_log_buffer_libretiny.h +++ b/esphome/components/logger/task_log_buffer_libretiny.h @@ -84,7 +84,7 @@ class TaskLogBuffer { static inline size_t message_total_size(size_t text_length) { return sizeof(LogMessage) + text_length + 1; } // Calculate available contiguous space at write position - size_t available_contiguous_space() const; + size_t available_contiguous_space_() const; uint8_t storage_[ESPHOME_TASK_LOG_BUFFER_SIZE]; // Embedded in Logger (no separate heap allocation) size_t head_{0}; // Write position diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py index 256bf4bb3a..bfe91eedd5 100644 --- a/esphome/components/lvgl/__init__.py +++ b/esphome/components/lvgl/__init__.py @@ -1,4 +1,3 @@ -import functools import importlib from pathlib import Path import pkgutil @@ -86,7 +85,7 @@ from .schemas import ( any_widget_schema, container_schema, container_schema_value, - obj_dict, + theme_schema, ) from .styles import styles_to_code, theme_to_code from .touchscreens import touchscreen_schema, touchscreens_to_code @@ -215,6 +214,18 @@ def multi_conf_validate(configs: list[dict]): raise cv.Invalid( f"'{item}' must have an explicit group set when using multiple LVGL instances" ) + # The hidden styles a `theme:` block creates are tracked in a single map shared + # by all LVGL instances (keyed only by widget type, not by instance), so a + # second instance's `theme:` would silently lose to whichever instance is + # processed first instead of doing what its config implies. + themed_configs = sum( + 1 for config in configs if config.get(df.CONF_THEME) is not None + ) + if themed_configs > 1: + raise cv.Invalid( + "'theme' may only be set on one LVGL instance when using multiple LVGL " + "instances -- combine both themes into a single instance's 'theme:' block" + ) base_config = configs[0] for config in configs[1:]: for item in ( @@ -552,34 +563,6 @@ def add_hello_world(config): return config -@functools.cache -def _build_theme_schema( - widget_types: tuple[tuple[str, widgets.WidgetType], ...], -) -> cv.Schema: - # The theme schema is value-independent: it depends only on the set of - # registered widget types. Key the cache on a snapshot of WIDGET_TYPES so - # that an external component registering a new widget after the first - # validation (legal per any_widget_schema's lazy-evaluation contract) - # produces a fresh tuple, a cache miss, and a rebuilt schema -- the cache - # self-heals instead of stale-rejecting valid themes. See obj_dict() in - # schemas.py for why chained .extend() is avoided here. - return cv.Schema( - { - cv.Optional(df.CONF_DARK_MODE, default=False): cv.boolean, - **{ - cv.Optional(name): cv.Schema( - {**obj_dict(w), **FULL_STYLE_SCHEMA.schema} - ) - for name, w in widget_types - }, - } - ) - - -def _theme_schema(value: dict) -> dict: - return _build_theme_schema(tuple(WIDGET_TYPES.items()))(value) - - FINAL_VALIDATE_SCHEMA = final_validation # The options accepted at the top level of an `lvgl:` block, on top of the base @@ -647,7 +630,7 @@ LVGL_TOP_LEVEL_SCHEMA = ( cv.Optional(df.CONF_TOP_LAYER): container_schema(obj_spec), cv.Optional(df.CONF_BOTTOM_LAYER): container_schema(obj_spec), cv.Optional(df.CONF_TRANSPARENCY_KEY, default=0x000400): lvalid.lv_color, - cv.Optional(df.CONF_THEME): _theme_schema, + cv.Optional(df.CONF_THEME): theme_schema, cv.Optional(df.CONF_GRADIENTS): GRADIENT_SCHEMA, cv.Optional(df.CONF_TOUCHSCREENS, default=None): touchscreen_schema, cv.Optional(df.CONF_ENCODERS, default=None): ENCODERS_CONFIG, diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py index 4f734fe20c..65e975ad6d 100644 --- a/esphome/components/lvgl/defines.py +++ b/esphome/components/lvgl/defines.py @@ -33,6 +33,7 @@ KEY_NAMED_STYLES = "named_styles" KEY_REFRESHED_WIDGETS = "refreshed_widgets" KEY_REMAPPED_USES = "remapped_uses" KEY_STYLES_USED = "styles_used" +KEY_THEME_UPDATE_REQUESTS = "theme_update_requests" KEY_THEME_WIDGET_MAP = "theme_widget_map" KEY_UPDATED_WIDGETS = "updated_widgets" KEY_WIDGET_MAP = "widget_map" @@ -118,6 +119,14 @@ def get_theme_widget_map() -> dict[str, Any]: return _get_data(KEY_THEME_WIDGET_MAP, {}) +def get_theme_update_requests() -> dict[str, dict[tuple[str, str], None]]: + # Values are dicts used as ordered sets (insertion order is deterministic, + # unlike a plain `set` of strings/tuples, whose iteration order depends on + # per-process string hash randomization) so codegen output doesn't churn + # between builds of the same config. + return _get_data(KEY_THEME_UPDATE_REQUESTS, {}) + + def get_styles_used() -> set[str]: return _get_data(KEY_STYLES_USED, set()) diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py index dd4f71a346..e400dae50f 100644 --- a/esphome/components/lvgl/schemas.py +++ b/esphome/components/lvgl/schemas.py @@ -1,4 +1,5 @@ from collections.abc import Callable +import functools from typing import Any from esphome import config_validation as cv @@ -8,6 +9,7 @@ from esphome.components.time import RealTimeClock from esphome.config_validation import prepend_path from esphome.const import ( CONF_ARGS, + CONF_DEFAULT, CONF_FORMAT, CONF_GROUP, CONF_ID, @@ -69,7 +71,7 @@ from .types import ( lv_pseudo_button_t, lv_style_t, ) -from .widgets import WidgetType +from .widgets import WidgetType, collect_parts # this will be populated later, in __init__.py to avoid circular imports. WIDGET_TYPES: dict = {} @@ -591,6 +593,96 @@ def obj_schema(widget_type: WidgetType) -> cv.Schema: return schema +@functools.cache +def _build_theme_schema( + widget_types: tuple[tuple[str, WidgetType], ...], + include_dark_mode: bool = True, +) -> cv.Schema: + # The theme schema is value-independent: it depends only on the set of + # registered widget types. Key the cache on a snapshot of WIDGET_TYPES so + # that an external component registering a new widget after the first + # validation (legal per any_widget_schema's lazy-evaluation contract) + # produces a fresh tuple, a cache miss, and a rebuilt schema -- the cache + # self-heals instead of stale-rejecting valid themes. See obj_dict() above + # for why chained .extend() is avoided here. + return cv.Schema( + { + **( + {cv.Optional(df.CONF_DARK_MODE, default=False): cv.boolean} + if include_dark_mode + else {} + ), + **{ + cv.Optional(name): cv.Schema( + {**obj_dict(w), **FULL_STYLE_SCHEMA.schema} + ) + for name, w in widget_types + }, + } + ) + + +def _reject_theme_styles_key(validated: dict) -> dict: + """ + `styles:` (a list of already-declared named styles) is not allowed inside + `theme:` -- the hidden style objects theme: creates only carry direct + style properties, so a `styles:` reference there would be silently + dropped by `style_set`, which only walks `ALL_STYLES`. + """ + for w_name, style in validated.items(): + if w_name not in WIDGET_TYPES: + continue + for part, states in collect_parts(style).items(): + for state, props in states.items(): + if df.CONF_STYLES not in props: + continue + path = [w_name] + if part != df.CONF_MAIN: + path.append(part) + if state != CONF_DEFAULT: + path.append(state) + path.append(df.CONF_STYLES) + raise cv.Invalid( + "'styles:' is not allowed in LVGL theme styles. " + "Set style properties directly instead.", + path, + ) + return validated + + +def theme_schema(value: dict) -> dict: + return _reject_theme_styles_key( + _build_theme_schema(tuple(WIDGET_TYPES.items()))(value) + ) + + +def theme_update_schema(value: dict) -> dict: + """ + Schema for `lvgl.theme.update`: same shape as `theme:` minus `dark_mode`. + As a validation side effect, records which (widget type, part, state) + combos are targeted so `theme_to_code` can make sure a hidden style + exists for each -- even ones never mentioned under `theme:` -- and gets + it attached to widgets at the same point real theme styles are. + """ + validated = _reject_theme_styles_key( + _build_theme_schema(tuple(WIDGET_TYPES.items()), include_dark_mode=False)(value) + ) + for w_name, style in validated.items(): + for part, states in collect_parts(style).items(): + for state, props in states.items(): + # collect_parts() unconditionally seeds a main/default entry + # even when nothing was set for it (e.g. `{pressed: {...}}` + # alone) -- skip combos with no properties so a request for + # one state doesn't also create an unused, empty main/default + # style that gets attached to every widget of this type. + if not props: + continue + df.get_theme_update_requests().setdefault(w_name, {})[(part, state)] = ( + None + ) + return validated + + ALIGN_TO_SCHEMA = { cv.Optional(df.CONF_ALIGN_TO): cv.Schema( { diff --git a/esphome/components/lvgl/styles.py b/esphome/components/lvgl/styles.py index 5911505555..ad42028327 100644 --- a/esphome/components/lvgl/styles.py +++ b/esphome/components/lvgl/styles.py @@ -10,11 +10,18 @@ from .defines import ( LValidator, add_lv_use, get_styles_used, + get_theme_update_requests, get_theme_widget_map, literal, ) from .lvcode import LambdaContext, lv -from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, WIDGET_TYPES, remap_property +from .schemas import ( + ALL_STYLES, + FULL_STYLE_SCHEMA, + WIDGET_TYPES, + remap_property, + theme_update_schema, +) from .types import ObjUpdateAction, lv_style_t from .widgets import collect_parts, wait_for_widgets @@ -86,23 +93,91 @@ async def style_update_to_code(config, action_id, template_arg, args): style = await cg.get_variable(config[CONF_ID]) async with LambdaContext(parameters=args, where=action_id) as context: await style_set(style, config) + # Refresh and redraw every widget using this style -- otherwise the + # updated properties would sit unused until something else happens to + # invalidate the affected widgets. + lv.obj_report_style_change(style) return cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) async def theme_to_code(config): - if theme := config.get(CONF_THEME): - add_lv_use(CONF_THEME) - for w_name, style in ((k, v) for k, v in theme.items() if k in WIDGET_TYPES): - # Work around Python 3.10 bug with nested async comprehensions - # With Python 3.11 this could be simplified - # TODO: Now that we require Python 3.11+, this can be updated to use nested comprehensions - styles = {} - for part, states in collect_parts(style).items(): - styles[part] = { - state: await create_style( + theme = config.get(CONF_THEME) or {} + requests = get_theme_update_requests() + # Iterate in WIDGET_TYPES' (deterministic, registration-order) sequence rather + # than a set -- a set of strings/tuples iterates in an order that depends on + # per-process hash randomization, which would otherwise churn the order hidden + # style variables are declared in main.cpp between builds of the same config. + widget_names = [ + w_name for w_name in WIDGET_TYPES if w_name in theme or w_name in requests + ] + if not widget_names: + return + add_lv_use(CONF_THEME) + theme_map = get_theme_widget_map() + for w_name in widget_names: + declared_parts = collect_parts(theme[w_name]) if w_name in theme else {} + parts = {part: dict(states) for part, states in declared_parts.items()} + for part, state in requests.get(w_name, {}): + parts.setdefault(part, {}).setdefault(state, {}) + widget_styles = theme_map.setdefault(w_name, {}) + for part, states in parts.items(): + part_styles = widget_styles.setdefault(part, {}) + declared_states = declared_parts.get(part, {}) + for state, props in states.items(): + if state not in part_styles: + part_styles[state] = await create_style( "_lv_theme_style_" + w_name + "_" + part + "_" + state, props ) - for state, props in states.items() - } - get_theme_widget_map()[w_name] = styles + elif state in declared_states: + # A `theme.update` request for this combo (possibly from + # another LVGL instance) already created the style as an + # empty placeholder before this instance's real `theme:` + # declaration was reached -- apply the real values now + # instead of silently leaving it empty. + await style_set(part_styles[state], props) + + +@automation.register_action( + "lvgl.theme.update", + ObjUpdateAction, + theme_update_schema, + synchronous=True, +) +async def theme_update_to_code(config, action_id, template_arg, args): + await wait_for_widgets() + theme_map = get_theme_widget_map() + # Invariant this relies on: theme_update_schema() records every (widget + # type, part, state) combo this action targets as a request during config + # validation (which completes for the whole config tree before any + # to_code runs), and theme_to_code() -- which runs for every LVGL + # instance before any action's own to_code -- materialises a style for + # each recorded request. If that handshake is ever broken by a future + # change, fail with a diagnosable message rather than a bare KeyError. + to_update = [] + for w_name, style in config.items(): + for part, states in collect_parts(style).items(): + for state, props in states.items(): + # collect_parts() unconditionally seeds an (empty) main/default + # entry even when this action didn't target it -- skip it, both + # because there's nothing to update and because + # theme_update_schema no longer pre-creates a placeholder style + # for combos with no properties. + if not props: + continue + style_var = theme_map.get(w_name, {}).get(part, {}).get(state) + if style_var is None: + raise cv.Invalid( + f"No theme style exists for '{w_name}' {part}/{state}. " + "This is an internal error -- please report it." + ) + to_update.append((style_var, props)) + async with LambdaContext(parameters=args, where=action_id) as context: + for style_var, props in to_update: + await style_set(style_var, props) + # Refresh and redraw every widget using this style -- otherwise the + # updated properties would sit unused until something else happens + # to invalidate the affected widgets. + lv.obj_report_style_change(style_var) + + return cg.new_Pvariable(action_id, template_arg, await context.get_lambda()) diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 3670098bcf..2d4f6085e5 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -209,7 +209,7 @@ async def to_code(config): ethernet.request_ethernet_ip_state_listener() if CORE.is_esp32: - add_idf_component(name="espressif/mdns", ref="1.11.0") + add_idf_component(name="espressif/mdns", ref="1.11.3") cg.add_define("USE_MDNS") diff --git a/esphome/components/mdns/mdns_libretiny.cpp b/esphome/components/mdns/mdns_libretiny.cpp index a543a3809a..5354bd241c 100644 --- a/esphome/components/mdns/mdns_libretiny.cpp +++ b/esphome/components/mdns/mdns_libretiny.cpp @@ -27,8 +27,8 @@ static void register_libretiny(MDNSComponent *, StaticVector ConfigType: + """Register the model file that the manifest points to, so bundles include it. - manifest_data = json.loads(json_contents) - if not isinstance(manifest_data, dict): - raise cv.Invalid("Manifest file must contain a JSON object") - - model = manifest_data[CONF_MODEL] - model_url = urljoin(url, model) - - model_path = path / model - - external_files.download_content(str(model_url), model_path) + The manifest names its model file relative to itself, so that path never appears + in the YAML and bundle discovery cannot find it on its own. + Problems with the manifest are logged and ignored here rather than raised. Loading + the manifest later reports them with better messages, and raising would be + swallowed by the shorthand validator, which then reports a confusing error about a + missing file in a git repository. Logging keeps the skipped registration + diagnosable if the manifest is only briefly unreadable, since the bundle would + then be built without the model file. + """ + manifest_path: Path = config[CONF_PATH] + try: + manifest = json.loads(manifest_path.read_text(encoding="utf-8")) + model = manifest[CONF_MODEL] + except (OSError, ValueError, KeyError, TypeError) as err: + _LOGGER.debug("Not registering a model file from %s: %s", manifest_path, err) + return config + if not isinstance(model, str): + _LOGGER.debug( + "Not registering a model file from %s: 'model' is %s, expected a string", + manifest_path, + type(model).__name__, + ) + return config + add_bundle_file(manifest_path.parent / model) return config -HTTP_SCHEMA = cv.All( - { - cv.Required(CONF_URL): cv.url, - }, - _process_http_source, +LOCAL_SCHEMA = cv.All( + cv.Schema( + { + cv.Required(CONF_PATH): cv.All(_validate_json_filename, cv.file_), + } + ), + _register_local_model_file, ) -LOCAL_SCHEMA = cv.Schema( - { - cv.Required(CONF_PATH): cv.All(_validate_json_filename, cv.file_), - } -) + +# Bare model names in the official model repository ("okay_nabu"). Must not +# overlap with local paths, http(s) urls, or git shorthands +# ("github://user/repo/file.json@ref"), which the shorthand validator tries +# next; anything containing "/", ":" or "@" is not a model name. +_MODEL_NAME_RE = re.compile(r"[A-Za-z0-9_.-]+") def _validate_source_model_name(value): @@ -250,6 +276,9 @@ def _validate_source_model_name(value): if value.endswith(".json"): raise cv.Invalid("Model name must not end with .json") + if not _MODEL_NAME_RE.fullmatch(value): + raise cv.Invalid("Model name may only contain letters, numbers, . _ -") + return MODEL_SOURCE_SCHEMA( { CONF_TYPE: TYPE_HTTP, @@ -339,6 +368,58 @@ def _maybe_empty_vad_schema(value): return VAD_MODEL_SCHEMA(value) +def _download_http_models(config: ConfigType) -> ConfigType: + """Download every http-sourced manifest and model file in two concurrent + batches (all manifests, then all model files). + + The model file's URL only becomes known once its manifest has been + fetched and parsed, so the two stages cannot be merged into one batch. + """ + model_parameters = [*config[CONF_MODELS]] + if vad := config.get(CONF_VAD): + model_parameters.append(vad) + # Keyed by cache path so a URL referenced twice is fetched and parsed once + http_models: dict[Path, str] = { + _compute_local_file_path(model_config): model_config[CONF_URL] + for parameters in model_parameters + if (model_config := parameters.get(CONF_MODEL)) is not None + and model_config.get(CONF_TYPE) == TYPE_HTTP + } + if not http_models: + return config + + external_files.download_content_many( + ((url, path / "manifest.json") for path, url in http_models.items()), + description="wake word manifest(s)", + ) + + model_files: list[tuple[str, Path]] = [] + errors: list[cv.Invalid] = [] + for path, url in http_models.items(): + try: + manifest_data = json.loads((path / "manifest.json").read_bytes()) + except (OSError, ValueError) as e: + errors.append(cv.Invalid(f"Invalid manifest file at {url}: {e}")) + continue + if not isinstance(manifest_data, dict): + errors.append( + cv.Invalid(f"Manifest file at {url} must contain a JSON object") + ) + continue + model = manifest_data.get(CONF_MODEL) + if not isinstance(model, str): + errors.append( + cv.Invalid(f"Manifest file at {url} is missing the 'model' key") + ) + continue + model_files.append((urljoin(url, model), path / model)) + if errors: + raise cv.MultipleInvalid(errors) + + external_files.download_content_many(model_files, description="wake word model(s)") + return config + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -372,6 +453,7 @@ CONFIG_SCHEMA = cv.All( } ).extend(cv.COMPONENT_SCHEMA), cv.only_on_esp32, + _download_http_models, ) diff --git a/esphome/components/midea/ac_adapter.cpp b/esphome/components/midea/ac_adapter.cpp index 2f4ef5c948..77bb9bbe86 100644 --- a/esphome/components/midea/ac_adapter.cpp +++ b/esphome/components/midea/ac_adapter.cpp @@ -1,4 +1,4 @@ -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #include "esphome/core/log.h" #include "ac_adapter.h" diff --git a/esphome/components/midea/ac_adapter.h b/esphome/components/midea/ac_adapter.h index a7924ae51e..4545743564 100644 --- a/esphome/components/midea/ac_adapter.h +++ b/esphome/components/midea/ac_adapter.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) // MideaUART #include diff --git a/esphome/components/midea/ac_automations.h b/esphome/components/midea/ac_automations.h index acd9191916..b595a018b3 100644 --- a/esphome/components/midea/ac_automations.h +++ b/esphome/components/midea/ac_automations.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #include "esphome/core/automation.h" #include "air_conditioner.h" diff --git a/esphome/components/midea/air_conditioner.cpp b/esphome/components/midea/air_conditioner.cpp index 7603dd5254..e55afedd8a 100644 --- a/esphome/components/midea/air_conditioner.cpp +++ b/esphome/components/midea/air_conditioner.cpp @@ -1,4 +1,4 @@ -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #include "esphome/core/helpers.h" #include "esphome/core/log.h" diff --git a/esphome/components/midea/air_conditioner.h b/esphome/components/midea/air_conditioner.h index bea6c2eadb..089928902e 100644 --- a/esphome/components/midea/air_conditioner.h +++ b/esphome/components/midea/air_conditioner.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) // MideaUART #include diff --git a/esphome/components/midea/appliance_base.h b/esphome/components/midea/appliance_base.h index d36f5a322c..1b45561fab 100644 --- a/esphome/components/midea/appliance_base.h +++ b/esphome/components/midea/appliance_base.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) // MideaUART #include diff --git a/esphome/components/midea/climate.py b/esphome/components/midea/climate.py index 4a75464b90..b0c102af6d 100644 --- a/esphome/components/midea/climate.py +++ b/esphome/components/midea/climate.py @@ -25,6 +25,8 @@ from esphome.const import ( ICON_POWER, ICON_THERMOMETER, ICON_WATER_PERCENT, + PLATFORM_ESP32, + PLATFORM_ESP8266, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, UNIT_PERCENT, @@ -152,6 +154,12 @@ CONFIG_SCHEMA = cv.All( .extend(uart.UART_DEVICE_SCHEMA) .extend(cv.COMPONENT_SCHEMA), cv.only_with_arduino, + cv.only_on( + [ + PLATFORM_ESP32, + PLATFORM_ESP8266, + ] + ), ) # Actions diff --git a/esphome/components/midea/ir_transmitter.h b/esphome/components/midea/ir_transmitter.h index f11682230d..ecf3fa1c1a 100644 --- a/esphome/components/midea/ir_transmitter.h +++ b/esphome/components/midea/ir_transmitter.h @@ -1,6 +1,6 @@ #pragma once -#ifdef USE_ARDUINO +#if defined(USE_ARDUINO) && !defined(USE_RP2) && !defined(USE_LIBRETINY) #ifdef USE_REMOTE_TRANSMITTER #include "esphome/components/remote_base/midea_protocol.h" diff --git a/esphome/components/mipi_spi/mipi_spi.h b/esphome/components/mipi_spi/mipi_spi.h index 701bcd7169..b269f46dc9 100644 --- a/esphome/components/mipi_spi/mipi_spi.h +++ b/esphome/components/mipi_spi/mipi_spi.h @@ -385,10 +385,10 @@ class MipiSpi : public display::Display, * @param ptr The pointer to the pixel data * @param w Width of each line in bytes * @param h Height of the buffer in rows - * @param pad Padding in bytes after each line + * @param stride Total length of each line in bytes, including any padding */ - void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad) { - if (pad == 0) { + void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t stride) { + if (stride == w) { if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) { this->write_array(ptr, w * h); } else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) { @@ -405,7 +405,7 @@ class MipiSpi : public display::Display, } else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) { this->write_cmd_addr_data(0, 0, 0, 0, ptr, w, 8); } - ptr += w + pad; + ptr += stride; } } } @@ -423,7 +423,7 @@ class MipiSpi : public display::Display, ptr += y_offset * (x_offset + w + x_pad) + x_offset; if constexpr (BUFFERPIXEL == DISPLAYPIXEL) { this->write_display_data_(reinterpret_cast(ptr), w * sizeof(BUFFERTYPE), h, - x_pad * sizeof(BUFFERTYPE)); + (x_offset + w + x_pad) * sizeof(BUFFERTYPE)); } else { // type conversion required, do it in chunks uint8_t dbuffer[DISPLAYPIXEL * 48]; @@ -459,14 +459,14 @@ class MipiSpi : public display::Display, } // buffer full? Flush. if (dptr == dbuffer + sizeof(dbuffer)) { - this->write_display_data_(dbuffer, sizeof(dbuffer), 1, 0); + this->write_display_data_(dbuffer, sizeof(dbuffer), 1, sizeof(dbuffer)); dptr = dbuffer; } } } // flush any remaining data if (dptr != dbuffer) { - this->write_display_data_(dbuffer, dptr - dbuffer, 1, 0); + this->write_display_data_(dbuffer, dptr - dbuffer, 1, dptr - dbuffer); } } this->disable(); diff --git a/esphome/components/mipi_spi/models/st77916.py b/esphome/components/mipi_spi/models/st77916.py new file mode 100644 index 0000000000..38852f135f --- /dev/null +++ b/esphome/components/mipi_spi/models/st77916.py @@ -0,0 +1,269 @@ +# Init sequence sourced from Espressif's esp-bsp repository: +# https://github.com/espressif/esp-bsp/blob/master/bsp/esp_vocat/priv_include/disp_init_data.h +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +from esphome.components.mipi import MODE_RGB, DriverChip +from esphome.components.spi import TYPE_QUAD +from esphome.const import CONF_INVERTED, CONF_NUMBER + +# Init sequence for the ST77916 QSPI display on the ESP-VoCat v1.2 board. +# Source: disp_init_data.h from espressif/esp-bsp (bsp/esp_vocat/priv_include). +# The standard INVON/INVOFF and SLPOUT+DISPON commands (and required delays) are omitted because +# the mipi_spi framework appends them automatically based on the invert_colors and no_slpout settings. +# (So this sequence only contains panel-specific setup commands.) +_ESP_VOCAT_INIT = ( + # Page 1a — startup unlock + (0xF0, 0x28), + (0xF2, 0x28), + (0x73, 0xF0), + (0x7C, 0xD1), + (0x83, 0xE0), + (0x84, 0x61), + (0xF2, 0x82), + # Switch to page 0 → page 1 + (0xF0, 0x00), + (0xF0, 0x01), + (0xF1, 0x01), + # Power settings (Bx) + (0xB0, 0x56), + (0xB1, 0x4D), + (0xB2, 0x24), + (0xB4, 0x87), + (0xB5, 0x44), + (0xB6, 0x8B), + (0xB7, 0x40), + (0xB8, 0x86), + (0xBA, 0x00), + (0xBB, 0x08), + (0xBC, 0x08), + (0xBD, 0x00), + # VCOM / gate settings (Cx) + (0xC0, 0x80), + (0xC1, 0x10), + (0xC2, 0x37), + (0xC3, 0x80), + (0xC4, 0x10), + (0xC5, 0x37), + (0xC6, 0xA9), + (0xC7, 0x41), + (0xC8, 0x01), + (0xC9, 0xA9), + (0xCA, 0x41), + (0xCB, 0x01), + # Source settings (Dx) + (0xD0, 0x91), + (0xD1, 0x68), + (0xD2, 0x68), + # Misc + (0xF5, 0x00, 0xA5), + (0xDD, 0x4F), + (0xDE, 0x4F), + (0xF1, 0x10), + (0xF0, 0x00), + # Switch to page 2 — gamma + (0xF0, 0x02), + ( + 0xE0, + 0xF0, + 0x0A, + 0x10, + 0x09, + 0x09, + 0x36, + 0x35, + 0x33, + 0x4A, + 0x29, + 0x15, + 0x15, + 0x2E, + 0x34, + ), + ( + 0xE1, + 0xF0, + 0x0A, + 0x0F, + 0x08, + 0x08, + 0x05, + 0x34, + 0x33, + 0x4A, + 0x39, + 0x15, + 0x15, + 0x2D, + 0x33, + ), + # Switch to page 10 — GIP / timing + (0xF0, 0x10), + (0xF3, 0x10), + # Page 10: Exxx + (0xE0, 0x07), + (0xE1, 0x00), + (0xE2, 0x00), + (0xE3, 0x00), + (0xE4, 0xE0), + (0xE5, 0x06), + (0xE6, 0x21), + (0xE7, 0x01), + (0xE8, 0x05), + (0xE9, 0x02), + (0xEA, 0xDA), + (0xEB, 0x00), + (0xEC, 0x00), + (0xED, 0x0F), + (0xEE, 0x00), + (0xEF, 0x00), + # Page 10: Fxxx + (0xF8, 0x00), + (0xF9, 0x00), + (0xFA, 0x00), + (0xFB, 0x00), + (0xFC, 0x00), + (0xFD, 0x00), + (0xFE, 0x00), + (0xFF, 0x00), + # GIP section A (0x60–0x6B) + (0x60, 0x40), + (0x61, 0x04), + (0x62, 0x00), + (0x63, 0x42), + (0x64, 0xD9), + (0x65, 0x00), + (0x66, 0x00), + (0x67, 0x00), + (0x68, 0x00), + (0x69, 0x00), + (0x6A, 0x00), + (0x6B, 0x00), + # GIP section B (0x70–0x7B) + (0x70, 0x40), + (0x71, 0x03), + (0x72, 0x00), + (0x73, 0x42), + (0x74, 0xD8), + (0x75, 0x00), + (0x76, 0x00), + (0x77, 0x00), + (0x78, 0x00), + (0x79, 0x00), + (0x7A, 0x00), + (0x7B, 0x00), + # GIP timing (0x80–0x9F) + (0x80, 0x48), + (0x81, 0x00), + (0x82, 0x06), + (0x83, 0x02), + (0x84, 0xD6), + (0x85, 0x04), + (0x86, 0x00), + (0x87, 0x00), + (0x88, 0x48), + (0x89, 0x00), + (0x8A, 0x08), + (0x8B, 0x02), + (0x8C, 0xD8), + (0x8D, 0x04), + (0x8E, 0x00), + (0x8F, 0x00), + (0x90, 0x48), + (0x91, 0x00), + (0x92, 0x0A), + (0x93, 0x02), + (0x94, 0xDA), + (0x95, 0x04), + (0x96, 0x00), + (0x97, 0x00), + (0x98, 0x48), + (0x99, 0x00), + (0x9A, 0x0C), + (0x9B, 0x02), + (0x9C, 0xDC), + (0x9D, 0x04), + (0x9E, 0x00), + (0x9F, 0x00), + # GIP timing (0xA0–0xBF) + (0xA0, 0x48), + (0xA1, 0x00), + (0xA2, 0x05), + (0xA3, 0x02), + (0xA4, 0xD5), + (0xA5, 0x04), + (0xA6, 0x00), + (0xA7, 0x00), + (0xA8, 0x48), + (0xA9, 0x00), + (0xAA, 0x07), + (0xAB, 0x02), + (0xAC, 0xD7), + (0xAD, 0x04), + (0xAE, 0x00), + (0xAF, 0x00), + (0xB0, 0x48), + (0xB1, 0x00), + (0xB2, 0x09), + (0xB3, 0x02), + (0xB4, 0xD9), + (0xB5, 0x04), + (0xB6, 0x00), + (0xB7, 0x00), + (0xB8, 0x48), + (0xB9, 0x00), + (0xBA, 0x0B), + (0xBB, 0x02), + (0xBC, 0xDB), + (0xBD, 0x04), + (0xBE, 0x00), + (0xBF, 0x00), + # Source timing (0xC0–0xC9) + (0xC0, 0x10), + (0xC1, 0x47), + (0xC2, 0x56), + (0xC3, 0x65), + (0xC4, 0x74), + (0xC5, 0x88), + (0xC6, 0x99), + (0xC7, 0x01), + (0xC8, 0xBB), + (0xC9, 0xAA), + # Source timing (0xD0–0xD9) + (0xD0, 0x10), + (0xD1, 0x47), + (0xD2, 0x56), + (0xD3, 0x65), + (0xD4, 0x74), + (0xD5, 0x88), + (0xD6, 0x99), + (0xD7, 0x01), + (0xD8, 0xBB), + (0xD9, 0xAA), + # Finalise page 10, return to page 0 + (0xF3, 0x01), + (0xF0, 0x00), + # INVON (0x21) and SLPOUT (0x11) are appended by the framework. +) + +DriverChip( + "ESP-VOCAT", + width=360, + height=360, + # SPI pins for the ESP-VoCat v1.2 board. + # PCLK (GPIO18) and data pins (GPIO46/13/11/12) are configured on the spi: bus. + cs_pin=14, + # RST is active-HIGH on this panel; invert the ESPHome pin so the framework's + # active-low reset pulse (HIGH→LOW→HIGH) maps to the correct physical sequence + # (LOW→HIGH→LOW) on the wire. + reset_pin={CONF_NUMBER: 47, CONF_INVERTED: True}, + # Note: GPIO9 behaviour varies by board revision (may be POWER_CTRL, not LCD_EN). + # Do not set a default enable_pin — manage LCD power in on_boot if needed. + # GPIO44 is the backlight; manage it separately via an output: or light:. + bus_mode=TYPE_QUAD, + data_rate="80MHz", + invert_colors=True, + color_order=MODE_RGB, + requires={"psram"}, + initsequence=_ESP_VOCAT_INIT, +) diff --git a/esphome/components/mipi_spi/models/waveshare.py b/esphome/components/mipi_spi/models/waveshare.py index 0caae5b939..bdd0c3c90b 100644 --- a/esphome/components/mipi_spi/models/waveshare.py +++ b/esphome/components/mipi_spi/models/waveshare.py @@ -203,6 +203,54 @@ AXS15231.extend( requires={"psram"}, ) +# Init sequence and pins taken from the vendor demo: +# "ESP32-S3-Touch-LCD-3.5B-Demo/Arduino/examples/09_lvgl_arduino_v8" +# The panel has no reset line (demo passes RST = -1); the sleep-out, display-on +# and first RAM write are issued by the framework, so they are omitted here. +# fmt: off +AXS15231.extend( + "WAVESHARE-ESP32-S3-TOUCH-LCD-3.5B", + width=320, + height=480, + # Vendor demo runs the AXS15231B at 32MHz; the ESP32 SPI clock cannot hit + # that exactly, data sheet says max 50MHz, so use 40MHz (proven on the same controller, JC3248W535) + data_rate="40MHz", + cs_pin=12, + requires={"psram"}, + initsequence=( + (0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5), + (0xA0, 0xC0, 0x10, 0x00, 0x02, 0x00, 0x00, 0x04, 0x3F, 0x20, 0x05, 0x3F, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00), + (0xA2, 0x30, 0x3C, 0x24, 0x14, 0xD0, 0x20, 0xFF, 0xE0, 0x40, 0x19, 0x80, 0x80, 0x80, 0x20, 0xF9, 0x10, 0x02, 0xFF, 0xFF, 0xF0, 0x90, 0x01, 0x32, 0xA0, 0x91, 0xE0, 0x20, 0x7F, 0xFF, 0x00, 0x5A), + (0xD0, 0xE0, 0x40, 0x51, 0x24, 0x08, 0x05, 0x10, 0x01, 0x20, 0x15, 0xC2, 0x42, 0x22, 0x22, 0xAA, 0x03, 0x10, 0x12, 0x60, 0x14, 0x1E, 0x51, 0x15, 0x00, 0x8A, 0x20, 0x00, 0x03, 0x3A, 0x12), + (0xA3, 0xA0, 0x06, 0xAA, 0x00, 0x08, 0x02, 0x0A, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x55, 0x55), + (0xC1, 0x31, 0x04, 0x02, 0x02, 0x71, 0x05, 0x24, 0x55, 0x02, 0x00, 0x41, 0x00, 0x53, 0xFF, 0xFF, 0xFF, 0x4F, 0x52, 0x00, 0x4F, 0x52, 0x00, 0x45, 0x3B, 0x0B, 0x02, 0x0D, 0x00, 0xFF, 0x40), + (0xC3, 0x00, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01), + (0xC4, 0x00, 0x24, 0x33, 0x80, 0x00, 0xEA, 0x64, 0x32, 0xC8, 0x64, 0xC8, 0x32, 0x90, 0x90, 0x11, 0x06, 0xDC, 0xFA, 0x00, 0x00, 0x80, 0xFE, 0x10, 0x10, 0x00, 0x0A, 0x0A, 0x44, 0x50), + (0xC5, 0x18, 0x00, 0x00, 0x03, 0xFE, 0x3A, 0x4A, 0x20, 0x30, 0x10, 0x88, 0xDE, 0x0D, 0x08, 0x0F, 0x0F, 0x01, 0x3A, 0x4A, 0x20, 0x10, 0x10, 0x00), + (0xC6, 0x05, 0x0A, 0x05, 0x0A, 0x00, 0xE0, 0x2E, 0x0B, 0x12, 0x22, 0x12, 0x22, 0x01, 0x03, 0x00, 0x3F, 0x6A, 0x18, 0xC8, 0x22), + (0xC7, 0x50, 0x32, 0x28, 0x00, 0xA2, 0x80, 0x8F, 0x00, 0x80, 0xFF, 0x07, 0x11, 0x9C, 0x67, 0xFF, 0x24, 0x0C, 0x0D, 0x0E, 0x0F), + (0xC9, 0x33, 0x44, 0x44, 0x01), + (0xCF, 0x2C, 0x1E, 0x88, 0x58, 0x13, 0x18, 0x56, 0x18, 0x1E, 0x68, 0x88, 0x00, 0x65, 0x09, 0x22, 0xC4, 0x0C, 0x77, 0x22, 0x44, 0xAA, 0x55, 0x08, 0x08, 0x12, 0xA0, 0x08), + (0xD5, 0x40, 0x8E, 0x8D, 0x01, 0x35, 0x04, 0x92, 0x74, 0x04, 0x92, 0x74, 0x04, 0x08, 0x6A, 0x04, 0x46, 0x03, 0x03, 0x03, 0x03, 0x82, 0x01, 0x03, 0x00, 0xE0, 0x51, 0xA1, 0x00, 0x00, 0x00), + (0xD6, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, 0x93, 0x00, 0x01, 0x83, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x84, 0x00, 0x20, 0x01, 0x00), + (0xD7, 0x03, 0x01, 0x0B, 0x09, 0x0F, 0x0D, 0x1E, 0x1F, 0x18, 0x1D, 0x1F, 0x19, 0x40, 0x8E, 0x04, 0x00, 0x20, 0xA0, 0x1F), + (0xD8, 0x02, 0x00, 0x0A, 0x08, 0x0E, 0x0C, 0x1E, 0x1F, 0x18, 0x1D, 0x1F, 0x19), + (0xD9, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F), + (0xDD, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F), + (0xDF, 0x44, 0x73, 0x4B, 0x69, 0x00, 0x0A, 0x02, 0x90), + (0xE0, 0x3B, 0x28, 0x10, 0x16, 0x0C, 0x06, 0x11, 0x28, 0x5C, 0x21, 0x0D, 0x35, 0x13, 0x2C, 0x33, 0x28, 0x0D), + (0xE1, 0x37, 0x28, 0x10, 0x16, 0x0B, 0x06, 0x11, 0x28, 0x5C, 0x21, 0x0D, 0x35, 0x14, 0x2C, 0x33, 0x28, 0x0F), + (0xE2, 0x3B, 0x07, 0x12, 0x18, 0x0E, 0x0D, 0x17, 0x35, 0x44, 0x32, 0x0C, 0x14, 0x14, 0x36, 0x3A, 0x2F, 0x0D), + (0xE3, 0x37, 0x07, 0x12, 0x18, 0x0E, 0x0D, 0x17, 0x35, 0x44, 0x32, 0x0C, 0x14, 0x14, 0x36, 0x32, 0x2F, 0x0F), + (0xE4, 0x3B, 0x07, 0x12, 0x18, 0x0E, 0x0D, 0x17, 0x39, 0x44, 0x2E, 0x0C, 0x14, 0x14, 0x36, 0x3A, 0x2F, 0x0D), + (0xE5, 0x37, 0x07, 0x12, 0x18, 0x0E, 0x0D, 0x17, 0x39, 0x44, 0x2E, 0x0C, 0x14, 0x14, 0x36, 0x3A, 0x2F, 0x0F), + (0xA4, 0x85, 0x85, 0x95, 0x82, 0xAF, 0xAA, 0xAA, 0x80, 0x10, 0x30, 0x40, 0x40, 0x20, 0xFF, 0x60, 0x30), + (0xA4, 0x85, 0x85, 0x95, 0x85), + (0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00), + ), +) +# fmt: on + # Waveshare 1.83-v2 # # Do not use on 1.83-v1: Vendor warning on different chip! diff --git a/esphome/components/mlx90393/sensor_mlx90393.cpp b/esphome/components/mlx90393/sensor_mlx90393.cpp index 7048302124..2288e8ff84 100644 --- a/esphome/components/mlx90393/sensor_mlx90393.cpp +++ b/esphome/components/mlx90393/sensor_mlx90393.cpp @@ -1,3 +1,5 @@ +#ifndef USE_BK72XX + #include "sensor_mlx90393.h" #include "esphome/core/log.h" @@ -270,3 +272,5 @@ void MLX90393Cls::verify_settings_timeout_(MLX90393Setting stage) { } } // namespace esphome::mlx90393 + +#endif // USE_BK72XX diff --git a/esphome/components/mlx90393/sensor_mlx90393.h b/esphome/components/mlx90393/sensor_mlx90393.h index e3b7ae5d93..03e78f51cc 100644 --- a/esphome/components/mlx90393/sensor_mlx90393.h +++ b/esphome/components/mlx90393/sensor_mlx90393.h @@ -1,5 +1,7 @@ #pragma once +#ifndef USE_BK72XX + #include #include #include "esphome/components/i2c/i2c.h" @@ -76,3 +78,5 @@ class MLX90393Cls final : public PollingComponent, public i2c::I2CDevice, public }; } // namespace esphome::mlx90393 + +#endif // USE_BK72XX diff --git a/esphome/components/modbus/helpers.py b/esphome/components/modbus/helpers.py index 9d7dc71547..e3029b2648 100644 --- a/esphome/components/modbus/helpers.py +++ b/esphome/components/modbus/helpers.py @@ -3,33 +3,34 @@ import esphome.codegen as cg modbus_ns = cg.esphome_ns.namespace("modbus") modbus_helpers_ns = modbus_ns.namespace("helpers") -ModbusFunctionCode_ns = modbus_ns.namespace("ModbusFunctionCode") -ModbusFunctionCode = ModbusFunctionCode_ns.enum("ModbusFunctionCode") +FunctionCode_ns = modbus_ns.namespace("FunctionCode") +FunctionCode = FunctionCode_ns.enum("FunctionCode") MODBUS_FUNCTION_CODE = { - "read_coils": ModbusFunctionCode.READ_COILS, - "read_discrete_inputs": ModbusFunctionCode.READ_DISCRETE_INPUTS, - "read_holding_registers": ModbusFunctionCode.READ_HOLDING_REGISTERS, - "read_input_registers": ModbusFunctionCode.READ_INPUT_REGISTERS, - "write_single_coil": ModbusFunctionCode.WRITE_SINGLE_COIL, - "write_single_register": ModbusFunctionCode.WRITE_SINGLE_REGISTER, - "write_multiple_coils": ModbusFunctionCode.WRITE_MULTIPLE_COILS, - "write_multiple_registers": ModbusFunctionCode.WRITE_MULTIPLE_REGISTERS, + "read_coils": FunctionCode.READ_COILS, + "read_discrete_inputs": FunctionCode.READ_DISCRETE_INPUTS, + "read_holding_registers": FunctionCode.READ_HOLDING_REGISTERS, + "read_input_registers": FunctionCode.READ_INPUT_REGISTERS, + "write_single_coil": FunctionCode.WRITE_SINGLE_COIL, + "write_single_register": FunctionCode.WRITE_SINGLE_REGISTER, + "write_multiple_coils": FunctionCode.WRITE_MULTIPLE_COILS, + "write_multiple_registers": FunctionCode.WRITE_MULTIPLE_REGISTERS, } -ModbusRegisterType_ns = modbus_ns.namespace("ModbusRegisterType") -ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType") +EntityType_ns = modbus_ns.namespace("EntityType") +EntityType = EntityType_ns.enum("EntityType") MODBUS_WRITE_REGISTER_TYPE = { - "custom": ModbusRegisterType.CUSTOM, - "coil": ModbusRegisterType.COIL, - "holding": ModbusRegisterType.HOLDING, + "custom": EntityType.CUSTOM, + "coil": EntityType.COIL, + "holding": EntityType.HOLDING, } MODBUS_REGISTER_TYPE = { **MODBUS_WRITE_REGISTER_TYPE, - "discrete_input": ModbusRegisterType.DISCRETE_INPUT, - "read": ModbusRegisterType.INPUT_REGISTER, + "discrete_input": EntityType.DISCRETE_INPUT, + "read": EntityType.INPUT_REGISTER, + "input": EntityType.INPUT_REGISTER, } SensorValueType_ns = modbus_helpers_ns.namespace("SensorValueType") diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index ecb2e4461c..2561ee9069 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -51,7 +51,7 @@ void ModbusClientHub::loop() { // If we're past the send_wait_time timeout and response buffer doesn't have the start of the expected response if (this->waiting_for_response_.has_value()) { ModbusDeviceCommand &wfr = this->waiting_for_response_.value(); - uint8_t expected_address = wfr.frame.data.data()[0]; + uint8_t expected_address = wfr.frame.address(); if (this->last_receive_check_ - this->last_send_ > this->last_send_tx_offset_ + this->send_wait_time_ && (this->rx_buffer_.empty() || this->rx_buffer_[0] != expected_address)) { ESP_LOGW(TAG, "Stop waiting for response from %" PRIu8 " %" PRIu32 "ms after last send", expected_address, @@ -214,11 +214,10 @@ bool Modbus::parse_modbus_server_frame_() { // Process before clearing: process_modbus_server_frame (receiving a response or peer message) never sends a reply // synchronously. We can safely point directly into rx_buffer_ and avoid a copy. - uint8_t data_offset = helpers::server_frame_data_offset(this->rx_buffer_.data(), this->rx_buffer_.size()); - const uint8_t *data = this->rx_buffer_.data() + data_offset; - uint16_t data_len = frame_length - 2 - data_offset; + // The PDU is the frame without the leading address and the trailing CRC. + std::span pdu(this->rx_buffer_.data() + 1, frame_length - 3); - this->process_modbus_server_frame(address, function_code, data, data_len); + this->process_modbus_server_frame(address, pdu); this->clear_rx_buffer_(LOG_STR("parse succeeded"), false, frame_length); return true; @@ -258,8 +257,16 @@ bool ModbusServerHub::parse_modbus_client_frame_() { return true; } -void ModbusClientHub::process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, - uint16_t len) { +// Bounds contract, enforced by the parser (parse_modbus_server_frame_) rather than locally: +// - pdu is never empty: helpers::server_pdu_length() returns at least MIN_PDU_SIZE (1) on every +// branch, and find_custom_frame_end_() only ever lengthens the frame, so the PDU always holds +// at least the function code. +// - When the exception bit is set, pdu has at least 2 bytes: server_pdu_length() checks the +// exception bit before anything else and pins those PDUs to 2 bytes, so the exception code +// read below is always present. +// Keep those guarantees in mind when changing server_pdu_length() or adding callers. +void ModbusClientHub::process_modbus_server_frame(uint8_t address, std::span pdu) { + const uint8_t function_code = pdu[0]; if (!this->waiting_for_response_.has_value()) { ESP_LOGW(TAG, "Received unexpected frame from address %" PRIu8 ", function code 0x%X, %" PRIu32 "ms after last send", @@ -269,8 +276,8 @@ void ModbusClientHub::process_modbus_server_frame(uint8_t address, uint8_t funct // Check if the response matches the expected address and function code ModbusDeviceCommand &wfr = this->waiting_for_response_.value(); - uint8_t expected_address = wfr.frame.data.data()[0]; - uint8_t expected_function_code = wfr.frame.data.data()[1]; + uint8_t expected_address = wfr.frame.address(); + uint8_t expected_function_code = wfr.frame.pdu()[0]; if (expected_address != address || expected_function_code != (function_code & FUNCTION_CODE_MASK)) { ESP_LOGW(TAG, "Received incorrect frame address %" PRIu8 " <> %" PRIu8 " or function code 0x%X <> 0x%X, %" PRIu32 @@ -292,20 +299,22 @@ void ModbusClientHub::process_modbus_server_frame(uint8_t address, uint8_t funct return; } else { // We have a valid device waiting for this response - ModbusClientDevice *device = wfr.device; + // Move the command out of the waiting slot so the request PDU stays alive for the callback. + ModbusDeviceCommand command = std::move(this->waiting_for_response_.value()); this->waiting_for_response_.reset(); + ModbusClientDevice *device = command.device; + // The request PDU is the sent frame without the leading address and the trailing CRC. + std::span request_pdu = command.frame.pdu(); // Is it an error response? if (helpers::is_function_code_exception(function_code)) { - uint8_t exception = len > 0 ? data[0] : 0; + uint8_t exception = pdu[1]; // exception frames are fixed-length, so the code is always present ESP_LOGW(TAG, "Error function code: 0x%X exception: %" PRIu8 ", address: %" PRIu8 ", %" PRIu32 "ms after last send", function_code, exception, address, this->last_modbus_byte_ - this->last_send_); if (device) - device->on_modbus_error(function_code & FUNCTION_CODE_MASK, exception); - + device->on_error(request_pdu, static_cast(exception)); } else if (device) { // Not an error response - // on_modbus_data is existing public API taking const std::vector& - device->on_modbus_data(std::vector(data, data + len)); + device->on_response(request_pdu, pdu); } else { // Not an error response, but no device to respond to ESP_LOGV(TAG, "Ignoring response from %" PRIu8 " - no callback device set, %" PRIu32 "ms after last send", address, this->last_modbus_byte_ - this->last_send_); @@ -314,7 +323,7 @@ void ModbusClientHub::process_modbus_server_frame(uint8_t address, uint8_t funct } } -void ModbusServerHub::process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *, uint16_t) { +void ModbusServerHub::process_modbus_server_frame(uint8_t address, std::span) { if (this->find_device_(address) != nullptr) { ESP_LOGE(TAG, "Unexpected response from address %" PRIu8 ", which is mapped to this device.", address); } @@ -344,7 +353,7 @@ bool ModbusServerHub::check_register_range_(uint8_t address, uint8_t function_co if ((uint32_t) start_address + number_of_registers > 0x10000u) { ESP_LOGW(TAG, "Register address out of range - start: %" PRIu16 " num: %" PRIu16, start_address, number_of_registers); - this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + this->send_exception_(address, function_code, ExceptionCode::ILLEGAL_DATA_ADDRESS); return false; } return true; @@ -363,22 +372,22 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func const uint8_t *response_data = response_buffer; uint16_t response_len = 0; - switch (static_cast(function_code)) { - case ModbusFunctionCode::READ_HOLDING_REGISTERS: - case ModbusFunctionCode::READ_INPUT_REGISTERS: { + switch (static_cast(function_code)) { + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: { // PDU data: start address(2) + quantity(2). uint16_t start_address = helpers::get_data(data, 0); uint16_t number_of_registers = helpers::get_data(data, 2); if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_READ) { ESP_LOGW(TAG, "Invalid number of registers %" PRIu16, number_of_registers); - this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); + this->send_exception_(address, function_code, ExceptionCode::ILLEGAL_DATA_VALUE); return; } if (!this->check_register_range_(address, function_code, start_address, number_of_registers)) { return; } RegisterValues registers; - if (static_cast(function_code) == ModbusFunctionCode::READ_HOLDING_REGISTERS) { + if (static_cast(function_code) == FunctionCode::READ_HOLDING_REGISTERS) { status = device->on_read_holding_registers(start_address, number_of_registers, registers); } else { status = device->on_read_input_registers(start_address, number_of_registers, registers); @@ -393,7 +402,7 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func if (registers.size() != number_of_registers) { ESP_LOGE(TAG, "Incorrect response %" PRIu16 " requested, %zu returned", number_of_registers, registers.size()); - this->send_exception_(address, function_code, ModbusExceptionCode::SERVICE_DEVICE_FAILURE); + this->send_exception_(address, function_code, ExceptionCode::SERVICE_DEVICE_FAILURE); return; } @@ -405,8 +414,8 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func } break; } - case ModbusFunctionCode::WRITE_SINGLE_REGISTER: - case ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS: { + case FunctionCode::WRITE_SINGLE_REGISTER: + case FunctionCode::WRITE_MULTIPLE_REGISTERS: { // PDU data: start address(2) [+ quantity(2) + byte count(1)] + register values. // A single-register write always targets one register; for a multiple-register write the // quantity is in the frame and its byte count must equal quantity * 2. The register values are @@ -414,7 +423,7 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func uint16_t start_address = helpers::get_data(data, 0); uint16_t number_of_registers = 1; uint16_t values_offset = 2; // single write: values follow the 2-byte start address - if (static_cast(function_code) == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) { + if (static_cast(function_code) == FunctionCode::WRITE_MULTIPLE_REGISTERS) { number_of_registers = helpers::get_data(data, 2); uint8_t number_of_bytes = helpers::get_data(data, 4); values_offset = 5; // multiple write: values follow start address(2) + quantity(2) + byte count(1) @@ -422,7 +431,7 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func number_of_registers * 2 != number_of_bytes) { ESP_LOGW(TAG, "Invalid number of registers %" PRIu16 " or bytes %" PRIu8, number_of_registers, number_of_bytes); - this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE); + this->send_exception_(address, function_code, ExceptionCode::ILLEGAL_DATA_VALUE); return; } if (!this->check_register_range_(address, function_code, start_address, number_of_registers)) { @@ -441,7 +450,7 @@ void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t func } default: ESP_LOGW(TAG, "Unsupported function code %" PRIu8, function_code); - this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_FUNCTION); + this->send_exception_(address, function_code, ExceptionCode::ILLEGAL_FUNCTION); return; } if (status.has_value()) { @@ -497,16 +506,25 @@ void ModbusClientHub::send_next_frame_() { return; } - ModbusDeviceCommand &command = this->tx_buffer_.front(); - - if (this->send_frame_(command.frame)) { - this->waiting_for_response_ = std::move(command); - } else { - if (command.device) - command.device->on_modbus_not_sent(); - } - + // Move the command out and pop BEFORE attempting the send: no callback may run while the frame still + // sits in the queue (the same principle as the clear sweep). A failure callback that sends would + // otherwise queue a new frame and pop_front() could discard the wrong one - and the deque + // reference / PDU span could be invalidated mid-callback. + ModbusDeviceCommand command = std::move(this->tx_buffer_.front()); this->tx_buffer_.pop_front(); + ModbusClientDevice *device = command.device; + const bool sent = this->send_frame_(command.frame); + + if (sent) { + // The frame now lives in the waiting slot; its PDU is the frame without the leading address and + // trailing CRC. + ModbusDeviceCommand &wfr = this->waiting_for_response_.emplace(std::move(command)); + if (device != nullptr) + device->on_sent(wfr.frame.pdu()); + } else { + if (device != nullptr) + device->trigger_not_sent(command.frame.pdu()); + } if (!this->tx_buffer_.empty()) { ESP_LOGV(TAG, "Write queue contains %zu items.", this->tx_buffer_.size()); @@ -553,7 +571,7 @@ void ModbusServerHub::send_response_(uint8_t address, uint8_t function_code, con this->send_raw_(raw_frame, payload_len + 2); } -void ModbusServerHub::send_exception_(uint8_t address, uint8_t function_code, ModbusExceptionCode exception_code) { +void ModbusServerHub::send_exception_(uint8_t address, uint8_t function_code, ExceptionCode exception_code) { uint8_t raw_frame[3]; raw_frame[0] = address; raw_frame[1] = function_code | FUNCTION_CODE_EXCEPTION_MASK; @@ -561,11 +579,10 @@ void ModbusServerHub::send_exception_(uint8_t address, uint8_t function_code, Mo this->send_raw_(raw_frame, 3); } -// Raw send for client: pushes to tx queue. Everything except the CRC must be contained in payload. void ModbusClientHub::notify_no_response_(ModbusDeviceCommand &wfr) { if (wfr.device == nullptr) return; - const bool retry = wfr.device->on_modbus_no_response(); + const bool retry = wfr.device->on_no_response(wfr.frame.pdu()); // The callback may have detached the device (e.g. clear_tx_queue_for_device()); honor the detach // over the retry request rather than re-queueing a frame that can no longer be routed. if (retry && wfr.device != nullptr) @@ -577,19 +594,28 @@ void ModbusClientHub::notify_no_response_(ModbusDeviceCommand &wfr) { void ModbusClientHub::requeue_waiting_frame_(ModbusDeviceCommand &wfr) { const ModbusFrame &frame = wfr.frame; if (this->tx_buffer_.size() >= MODBUS_TX_BUFFER_SIZE) { - ESP_LOGE(TAG, "Write buffer full, dropped retry for address %" PRIu8, frame.data.data()[0]); + ESP_LOGE(TAG, "Write buffer full, dropped retry for address %" PRIu8, frame.address()); if (wfr.device != nullptr) - wfr.device->on_modbus_not_sent(); + wfr.device->trigger_not_sent(frame.pdu()); return; } // Re-queue a copy (not a move): the waiting entry may have to survive as an interrupted shell. - this->tx_buffer_.emplace_back(wfr.device, frame.data.data()[0], frame.data.data() + 1, frame.size() - 3); + this->tx_buffer_.emplace_back(wfr.device, frame.address(), frame.pdu()); } -void ModbusClientHub::queue_raw_(uint8_t address, const uint8_t *pdu, uint16_t pdu_len, ModbusClientDevice *device) { - if (pdu_len == 0) { - if (device) - device->on_modbus_not_sent(); +// Raw send for client: pushes to tx queue. Everything except the CRC must be contained in payload. +void ModbusClientHub::send_pdu(uint8_t address, std::span pdu, ModbusClientDevice *device) { + if (pdu.empty()) { + if (device != nullptr) + device->trigger_not_sent(pdu); + return; + } + + // Bound the PDU so the wire frame (address + pdu + CRC) stays within the Modbus RTU 256-byte limit. + if (pdu.size() > MAX_PDU_SIZE) { + ESP_LOGE(TAG, "Frame too large, dropped: %" PRIu8 ":%zu bytes", address, pdu.size()); + if (device != nullptr) + device->trigger_not_sent(pdu); return; } @@ -597,28 +623,48 @@ void ModbusClientHub::queue_raw_(uint8_t address, const uint8_t *pdu, uint16_t p #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE char hex_buf[format_hex_pretty_size(MODBUS_MAX_LOG_BYTES)]; #endif - ESP_LOGV(TAG, "Adding frame to tx queue: %" PRIu8 ":%s", address, format_hex_pretty_to(hex_buf, pdu, pdu_len)); - this->tx_buffer_.emplace_back(device, address, pdu, pdu_len); + ESP_LOGV(TAG, "Adding frame to tx queue: %" PRIu8 ":%s", address, + format_hex_pretty_to(hex_buf, pdu.data(), pdu.size())); + this->tx_buffer_.emplace_back(device, address, pdu); } else { #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR char hex_buf[format_hex_pretty_size(MODBUS_MAX_LOG_BYTES)]; #endif - ESP_LOGE(TAG, "Write buffer full, dropped: %" PRIu8 ":%s", address, format_hex_pretty_to(hex_buf, pdu, pdu_len)); - if (device) - device->on_modbus_not_sent(); + ESP_LOGE(TAG, "Write buffer full, dropped: %" PRIu8 ":%s", address, + format_hex_pretty_to(hex_buf, pdu.data(), pdu.size())); + if (device != nullptr) + device->trigger_not_sent(pdu); } } void ModbusClientHub::clear_tx_queue_for_address(uint8_t address, bool clear_sent) { - // Remove any pending commands for this address from the tx buffer - auto &tx_buffer = this->tx_buffer_; - tx_buffer.erase( - std::remove_if(tx_buffer.begin(), tx_buffer.end(), - [address](const ModbusDeviceCommand &cmd) { return cmd.frame.data.data()[0] == address; }), - tx_buffer.end()); + // Drop the queued frames for this address, delivering on_not_sent() to each frame's owner: other + // devices talking to the same physical device (e.g. a modbus_client action alongside a controller that + // just went offline) must observe the drop, or their command never resolves. Mark first, then sweep + // only marked frames: anything a callback re-queues is unmarked, so it + // is never swept - or re-notified - by the clear that triggered it. Each marked frame is moved out and erased BEFORE + // its callback runs, so handlers see a consistent queue; termination is guaranteed because only the initially-marked + // frames are ever swept. + for (auto &cmd : this->tx_buffer_) { + if (cmd.frame.address() == address) + cmd.marked_for_deletion = true; + } + for (;;) { + auto it = std::find_if(this->tx_buffer_.begin(), this->tx_buffer_.end(), + [](const ModbusDeviceCommand &cmd) { return cmd.marked_for_deletion; }); + if (it == this->tx_buffer_.end()) + break; + ModbusDeviceCommand dropped = std::move(*it); + this->tx_buffer_.erase(it); + // The sweep delivers through the same per-device guard as refusals: a device clearing from inside + // its own on_not_sent() gets its remaining frames resolved silently (documented in the lifecycle + // contract), other owners are notified normally, and every nested clear stays bounded. + if (dropped.device != nullptr) + dropped.device->trigger_not_sent(dropped.frame.pdu()); + } if (clear_sent && this->waiting_for_response_.has_value() && this->waiting_for_response_.value().device) { - if (this->waiting_for_response_.value().frame.data.data()[0] == address) { + if (this->waiting_for_response_.value().frame.address() == address) { ESP_LOGV(TAG, "Clearing waiting for response for address %" PRIu8, address); // Invalidate the waiting device so it won't process a response. this->waiting_for_response_.value().device = nullptr; @@ -643,11 +689,11 @@ void ModbusClientHub::clear_tx_queue_for_device(ModbusClientDevice *device) { void ModbusClientHub::send_raw(const std::vector &payload, ModbusClientDevice *device) { if (payload.size() < 2) { - if (device) - device->on_modbus_not_sent(); + if (device != nullptr) + device->trigger_not_sent({}); // too short to contain a PDU return; } - this->queue_raw_(payload[0], payload.data() + 1, static_cast(payload.size() - 1), device); + this->send_pdu(payload[0], std::span(payload).subspan(1), device); } // Send raw command for server replies immediately. Except CRC everything must be contained in payload @@ -699,4 +745,150 @@ void Modbus::clear_rx_buffer_(const LogString *reason, bool warn, size_t bytes_t } } +void ModbusClientDevice::dispatch_response_(std::span request_pdu, std::span response_pdu, + ResponseStatus status) { + if (request_pdu.empty()) + return; + auto function_code = static_cast(request_pdu[0]); + // All standard requests handled below are function code + start address + count/value (5 bytes); + // anything shorter cannot be parsed and is handed to the catch-all. + if (request_pdu.size() < READ_PDU_SIZE) { + this->on_custom_response(request_pdu, response_pdu, status); + return; + } + const uint16_t start_address = helpers::get_data(request_pdu.data(), 1); + // count for reads/multi-writes, value for single writes + const uint16_t count_or_value = helpers::get_data(request_pdu.data(), 3); + + // Gatekeeper for the typed dispatch below: anything that is not a standard-conformant transaction is + // handed to on_custom_response() with the raw PDUs, so the decode cases can trust every length, byte + // count, and quantity field without re-clamping. + // - The REQUEST must be standard: nothing upstream validates a caller-built request PDU, so its + // internal byte count, quantity, and address range are checked here (is_client_pdu_standard()). + // - On success, the RESPONSE must be standard (self-consistent; the frame parser already guarantees + // most of this, but the check keeps the safety proof local), and a read response's length must also + // match the REQUESTED count - the per-PDU checks cannot see that relationship, and a short but + // self-consistent response must be diverted, never silently clamped and delivered as complete. + // - On failure (status engaged) the response is empty by design (see on_error()), so only the request + // is validated. + bool custom = !helpers::is_client_pdu_standard(request_pdu.data(), request_pdu.size()); + if (!custom && !status.has_value()) { + custom = !helpers::is_server_pdu_standard(response_pdu.data(), response_pdu.size()); + if (!custom && helpers::is_function_code_read(static_cast(function_code))) { + const bool bits = + function_code == FunctionCode::READ_COILS || function_code == FunctionCode::READ_DISCRETE_INPUTS; + const size_t expected_data_size = + bits ? (static_cast(count_or_value) + 7) / 8 : static_cast(count_or_value) * 2; + if (response_pdu.size() != expected_data_size + 2) { + ESP_LOGD(TAG, "Response length %zu does not match request (expected %zu) for function code 0x%X", + response_pdu.size(), expected_data_size + 2, static_cast(function_code)); + custom = true; + } + } + } + if (custom) { + this->on_custom_response(request_pdu, response_pdu, status); + return; + } + + switch (function_code) { + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: { + // Decode the big-endian register words into host byte order. The gate guarantees a success response + // carries exactly count_or_value registers (and count_or_value <= MAX_NUM_OF_REGISTERS_TO_READ, the + // capacity of RegisterValues); a mismatch was diverted to on_custom_response(), never clamped. On + // failure the registers span is empty. + RegisterValues registers; + if (!status.has_value()) { + for (size_t i = 0; i != count_or_value; i++) { + registers.push_back(helpers::get_data(response_pdu.data(), 2 + 2 * i)); + } + } + std::span register_span(registers.data(), registers.size()); + if (function_code == FunctionCode::READ_HOLDING_REGISTERS) { + this->on_read_holding_registers(start_address, register_span, status); + } else { + this->on_read_input_registers(start_address, register_span, status); + } + break; + } + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: { + // Deliver the bits packed as on the wire; the gate guarantees a success response carries exactly + // (count_or_value + 7) / 8 data bytes. On failure the view is empty AND the count is zero - + // PackedBits::operator[] is unchecked, so size() must never promise bits with no bytes behind them. + std::span packed_bytes; + uint16_t count = 0; + if (!status.has_value()) { + packed_bytes = response_pdu.subspan(2); + count = count_or_value; + } + PackedBits bits(packed_bytes, count); + if (function_code == FunctionCode::READ_COILS) { + this->on_read_coils(start_address, bits, status); + } else { + this->on_read_discrete_inputs(start_address, bits, status); + } + break; + } + // Single-write acks echo the value: on success that echo is device-confirmed state - the one + // write whose acknowledgement carries a real read-back - so it is preferred over the request + // copy. On an exception the response has no value and the request copy is the only one. + case FunctionCode::WRITE_SINGLE_REGISTER: + case FunctionCode::WRITE_SINGLE_COIL: { + const uint16_t value = (!status.has_value() && response_pdu.size() >= WRITE_SINGLE_PDU_SIZE) + ? helpers::get_data(response_pdu.data(), 3) + : count_or_value; + if (function_code == FunctionCode::WRITE_SINGLE_REGISTER) { + this->on_write_single_register(start_address, value, status); + } else { + this->on_write_single_coil(start_address, value == 0xFF00, status); + } + break; + } + case FunctionCode::WRITE_MULTIPLE_REGISTERS: { + // Request layout: [0] function code, [1..2] start address, [3..4] register count, [5] byte count, + // [6..] register data. The gate guarantees the request carries exactly count_or_value registers + // (<= MAX_NUM_OF_REGISTERS_TO_WRITE, within RegisterValues capacity). Decoded from the request and + // delivered regardless of status - see the write-acknowledgement note in modbus.h. + RegisterValues registers; + for (size_t i = 0; i != count_or_value; i++) { + registers.push_back(helpers::get_data(request_pdu.data(), 6 + 2 * i)); + } + std::span register_span(registers.data(), registers.size()); + this->on_write_multiple_registers(start_address, register_span, status); + break; + } + case FunctionCode::WRITE_MULTIPLE_COILS: { + // Request layout: [0] function code, [1..2] start address, [3..4] coil count, [5] byte count, + // [6..] packed bits. The gate guarantees the request carries exactly (count_or_value + 7) / 8 packed + // bytes. Decoded from the request and delivered regardless of status - see the write-acknowledgement + // note in modbus.h. + std::span packed_bytes = request_pdu.subspan(6); + PackedBits bits(packed_bytes, count_or_value); + this->on_write_multiple_coils(start_address, bits, status); + break; + } + default: + this->on_custom_response(request_pdu, response_pdu, status); + break; + } +} + +// Default on_custom_response handler to warn when responses unexpectedly trigger on_custom_response +void ModbusClientDevice::on_custom_response(std::span request_pdu, std::span response_pdu, + ResponseStatus status) { + // The dispatcher never calls this with an empty request, but this is a public virtual - stay safe. + const uint8_t function_code = request_pdu.empty() ? 0 : request_pdu[0]; + // Warn once per device, then drop to VERBOSE: a mildly non-conformant peer answers every poll, + // and an unhandled-response warning per transaction would flood the log permanently. + if (!this->custom_response_warned_) { + this->custom_response_warned_ = true; + ESP_LOGW(TAG, "Non-standard request or response for function code 0x%X. No on_custom_response handler declared", + function_code); + } else { + ESP_LOGV(TAG, "Non-standard request or response for function code 0x%X (unhandled)", function_code); + } +} + } // namespace esphome::modbus diff --git a/esphome/components/modbus/modbus.h b/esphome/components/modbus/modbus.h index eeba00f6b1..2204c0f82b 100644 --- a/esphome/components/modbus/modbus.h +++ b/esphome/components/modbus/modbus.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,13 @@ struct ModbusFrame { } uint16_t size() const { return static_cast(this->data.size()); } + + // A frame is [address][PDU...][CRC lo][CRC hi]. These are the only places that need to know that layout + uint8_t address() const { return this->data.data()[0]; } + /// The PDU: function code + data, without address or CRC. Only valid while the frame is alive. + /// Requires a complete frame (size() >= MIN_FRAME_SIZE, guaranteed by the constructors) - the + /// subtraction would wrap on anything shorter. + std::span pdu() const { return std::span(this->data.data() + 1, this->size() - 3u); } }; class Modbus : public uart::UARTDevice, public Component { @@ -59,8 +67,8 @@ class Modbus : public uart::UARTDevice, public Component { virtual int32_t tx_delay_remaining(); virtual void parse_modbus_frames() = 0; bool parse_modbus_server_frame_(); - virtual void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, - uint16_t len) = 0; + // pdu is the whole PDU (function code + payload, no address/CRC); pdu[0] is the (standard or custom) function code. + virtual void process_modbus_server_frame(uint8_t address, std::span pdu) = 0; void clear_rx_buffer_(const LogString *reason, bool warn = false, size_t bytes_to_clear = 0); bool send_frame_(const ModbusFrame &frame); // Scans forward from min_length to find a frame boundary by CRC match for custom function codes. @@ -86,9 +94,16 @@ struct ModbusDeviceCommand { ModbusClientDevice *device; ModbusFrame frame; bool interrupted{false}; + /// Marked by clear_tx_queue_for_address() before it starts notifying, so frames re-queued by an + /// on_not_sent() callback (which are unmarked) are never swept by the clear that triggered them. + bool marked_for_deletion{false}; ModbusDeviceCommand(ModbusClientDevice *device, uint8_t address, const uint8_t *src, uint16_t len) : device(device), frame(address, src, len) {} + /// Build a command from a PDU span: a caller-supplied PDU, or an existing frame's own pdu() when re-queueing + /// Callers must bound the PDU to MAX_PDU_SIZE + ModbusDeviceCommand(ModbusClientDevice *device, uint8_t address, std::span pdu) + : device(device), frame(address, pdu.data(), static_cast(pdu.size())) {} }; class ModbusClientHub : public Modbus { @@ -104,28 +119,29 @@ class ModbusClientHub : public Modbus { void send(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0, const uint8_t *payload = nullptr, ModbusClientDevice *device = nullptr) { this->send_pdu(address, - helpers::create_client_pdu((ModbusFunctionCode) function_code, start_address, number_of_entities, - payload, payload_len), + helpers::create_client_pdu((FunctionCode) function_code, start_address, number_of_entities, payload, + payload_len), device); }; - void send_pdu(uint8_t address, std::span pdu, ModbusClientDevice *device = nullptr) { - this->queue_raw_(address, pdu.data(), pdu.size(), device); - } + void send_pdu(uint8_t address, std::span pdu, ModbusClientDevice *device = nullptr); + ESPDEPRECATED("Use send_pdu(payload[0], , device) instead. Removed in 2027.2.0", "2026.8.0") void send_raw(const std::vector &payload, ModbusClientDevice *device = nullptr); + // Drop the queued commands for an address; every dropped frame resolves via its owner's on_not_sent(), + // so other devices sharing the address observe the drop. The in-flight frame is only detached (silently) + // when clear_sent is set. clear_tx_queue_for_device() SILENTLY discards the caller's own frames + // (supersede/teardown semantics); see the lifecycle note on ModbusClientDevice. void clear_tx_queue_for_address(uint8_t address, bool clear_sent = true); void clear_tx_queue_for_device(ModbusClientDevice *device); protected: int32_t tx_delay_remaining() override; void parse_modbus_frames() override; - // Parsers need to handle standard (ModbusFunctionCode) and custom (uint8_t) function codes, so we use uint8_t here. - void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) override; + void process_modbus_server_frame(uint8_t address, std::span pdu) override; void send_next_frame_(); - // Notify the waiting device of no response; re-queues the frame if on_modbus_no_response() returns true. + // Notify the waiting device of no response; re-queues the frame if on_no_response() returns true. // wfr is the caller's checked reference to waiting_for_response_. void notify_no_response_(ModbusDeviceCommand &wfr); void requeue_waiting_frame_(ModbusDeviceCommand &wfr); - void queue_raw_(uint8_t address, const uint8_t *pdu, uint16_t pdu_len, ModbusClientDevice *device = nullptr); uint16_t send_wait_time_{2000}; uint16_t turnaround_delay_ms_{0}; @@ -146,8 +162,7 @@ class ModbusServerHub : public Modbus { protected: void parse_modbus_frames() override; bool parse_modbus_client_frame_(); - // Parsers need to handle standard (ModbusFunctionCode) and custom (uint8_t) function codes, so we use uint8_t here. - void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) override; + void process_modbus_server_frame(uint8_t address, std::span pdu) override; void process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data); ModbusServerDevice *find_device_(uint8_t address); // Returns true if [start_address, start_address + number_of_registers) fits in the 16-bit address space. @@ -155,7 +170,7 @@ class ModbusServerHub : public Modbus { bool check_register_range_(uint8_t address, uint8_t function_code, uint16_t start_address, uint16_t number_of_registers); void send_raw_(const uint8_t *payload, uint16_t len); - void send_exception_(uint8_t address, uint8_t function_code, ModbusExceptionCode exception_code); + void send_exception_(uint8_t address, uint8_t function_code, ExceptionCode exception_code); void send_response_(uint8_t address, uint8_t function_code, const uint8_t *payload, uint16_t payload_len); uint8_t expecting_peer_response_{0}; std::vector devices_; @@ -166,6 +181,28 @@ class ModbusServerHub : public Modbus { uint16_t deferred_payload_len_{0}; }; +// Transaction status: std::nullopt on success, otherwise a Modbus exception code +using ResponseStatus = std::optional; + +/// Command lifecycle: each accepted command (a send_pdu()/typed-helper call, or a hub re-queue from +/// a retry) ends in exactly ONE terminal callback: on_response() (valid response), on_error() +/// (exception response), on_no_response() (timeout or interrupted transaction), or on_not_sent() +/// (never transmitted: send failure or full queue). on_sent() is additional, not +/// terminal: it fires once per wire transmission, before whichever of data/error/no_response follows, +/// and never for a command that ends in on_not_sent(). +/// The exceptions to "exactly one terminal": +/// - clear_tx_queue_for_device() drops the caller's OWN queued commands SILENTLY (supersede/teardown +/// semantics), and both clear variants detach the in-flight frame silently. +/// clear_tx_queue_for_address() DOES resolve every queued frame it drops via the owner's +/// on_not_sent() (delivered one at a time, after that frame leaves the queue). +/// - while a device's own on_not_sent() is on the stack, further on_not_sent() deliveries to THAT +/// device are dropped (see trigger_not_sent()). In particular, a clear issued from inside your own +/// on_not_sent() resolves your remaining frames silently - treat it like +/// clear_tx_queue_for_device(): you cleared them, you know. Other owners are still notified. +/// Sending from inside on_not_sent() is hazardous: the notification may itself mean the queue is full +/// or refusing, and this device's retry that is refused again is dropped WITHOUT a callback (the +/// guard above, which bounds what would otherwise be unbounded re-entry) - prefer re-sending from a +/// later trigger or the component's update()/loop(). class ModbusClientDevice { public: ModbusClientDevice() = default; @@ -180,22 +217,158 @@ class ModbusClientDevice { ModbusClientDevice &operator=(ModbusClientDevice &&) = delete; void set_parent(ModbusClientHub *parent) { this->parent_ = parent; } void set_address(uint8_t address) { this->address_ = address; } - virtual void on_modbus_data(const std::vector &data) {} - virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code) {} + /// Low-level response hook: called with the request PDU this device sent and the response PDU received + /// The spans are only valid for the duration of the call - copy the bytes if they must outlive it. + /// The default implementation decodes standard responses and dispatches to on_read_* / on_write_* callbacks below. + /// Override it to handle raw PDUs directly. + virtual void on_response(std::span request_pdu, std::span response_pdu) { + this->dispatch_response_(request_pdu, response_pdu, std::nullopt); + } + /// Low-level error hook: called with the request PDU and the modbus exception code from the error response. + /// The default implementation dispatches to the same typed callbacks with the exception code as status. + /// Devices implementing the High-level typed callbacks see success and failure through one interface. + virtual void on_error(std::span request_pdu, ExceptionCode exception_code) { + this->dispatch_response_(request_pdu, {}, exception_code); + } + /// Called when no request could be sent (e.g. queue full, transmission blocked). + /// Do not attempt to queue a command in this callback. + /// (The on_modbus_* names below are deprecated pre-rename spellings; the defaults forward so + /// external devices overriding them keep working through the deprecation window.) + virtual void on_not_sent(std::span request_pdu) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + this->on_modbus_not_sent(); +#pragma GCC diagnostic pop + } + /// Non-virtual entry point the hub uses for EVERY on_not_sent() delivery (refusals and clear-queue + /// sweeps alike). While this device's on_not_sent() is on the stack, further deliveries to it are + /// dropped: this bounds every send->refuse and clear->sweep recursion, including cycles through + /// multiple devices (each device can appear on the stack at most once). The documented cost: a clear + /// issued from inside your own on_not_sent() resolves your remaining frames SILENTLY, while other + /// owners are still notified (their guards are not set) - see the lifecycle contract above. + void trigger_not_sent(std::span request_pdu) { + if (this->notifying_not_sent_) + return; + this->notifying_not_sent_ = true; + this->on_not_sent(request_pdu); + this->notifying_not_sent_ = false; + } + /// Called when this device's frame is actually written to the wire + virtual void on_sent(std::span request_pdu) {} + /// Called when no matching, uninterrupted response arrived; return true to have the hub re-queue the frame for a + /// retry. The hub does not bound retries: the device is responsible for limiting them. + virtual bool on_no_response(std::span request_pdu) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + return this->on_modbus_no_response(); +#pragma GCC diagnostic pop + } + // Remove before 2027.2.0 + ESPDEPRECATED("Override on_not_sent() instead. Removed in 2027.2.0", "2026.8.0") virtual void on_modbus_not_sent() {} - /// Called when no (valid) response arrived; return true to have the hub re-queue the frame for a retry. - /// The hub does not bound retries: the device is responsible for limiting them (e.g. track a counter and - /// return false when exhausted), or an unresponsive peer will starve other traffic on the bus. + // Remove before 2027.2.0 + ESPDEPRECATED("Override on_no_response() instead. Removed in 2027.2.0", "2026.8.0") virtual bool on_modbus_no_response() { return false; } + + /// High-level typed response callbacks, fired by the default on_response()/on_error() with arguments + /// parsed from the request and response PDUs. + /// Status is std::nullopt on success; holds the exception code on failure. + /// Register values are in host byte order; spans are only valid for the duration of the call. + virtual void on_read_registers(EntityType entity_type, uint16_t start_address, std::span registers, + ResponseStatus status) {} + virtual void on_read_holding_registers(uint16_t start_address, std::span registers, + ResponseStatus status) { + this->on_read_registers(EntityType::HOLDING, start_address, registers, status); + } + virtual void on_read_input_registers(uint16_t start_address, std::span registers, + ResponseStatus status) { + this->on_read_registers(EntityType::INPUT_REGISTER, start_address, registers, status); + } + /// Coil/discrete-input reads are delivered as a PackedBits view (bit 0 = the bit at start_address, + /// bits.size() = the count requested). The view points into the hub's receive buffer and is only + /// valid during the call. + virtual void on_read_bits(EntityType entity_type, uint16_t start_address, PackedBits bits, ResponseStatus status) {} + virtual void on_read_coils(uint16_t start_address, PackedBits bits, ResponseStatus status) { + this->on_read_bits(EntityType::COIL, start_address, bits, status); + } + virtual void on_read_discrete_inputs(uint16_t start_address, PackedBits bits, ResponseStatus status) { + this->on_read_bits(EntityType::DISCRETE_INPUT, start_address, bits, status); + } + /// Write acknowledgements. These deliberately mirror the read callbacks' shapes, so a write ack can be fed + /// through the same handler as a read (registers.size() / bits.size() gives the count) + /// + /// IMPORTANT - for the multi-writes these are the values that were REQUESTED, not device-confirmed + /// state: a multi-write ack only echoes the start address and count, so the values are decoded from + /// the request PDU, and they are delivered even when status holds an exception code. Always check + /// status, and treat publishing them as an optimistic update rather than a read-back. The single + /// writes are the exception: their successful ack echoes the value, so on success the delivered + /// value is the device's echo (on an exception it falls back to the request copy). + virtual void on_write_single_register(uint16_t address, uint16_t value, ResponseStatus status) {} + virtual void on_write_single_coil(uint16_t address, bool value, ResponseStatus status) {} + virtual void on_write_multiple_registers(uint16_t start_address, std::span registers, + ResponseStatus status) {} + virtual void on_write_multiple_coils(uint16_t start_address, PackedBits bits, ResponseStatus status) {} + /// Catch-all for custom function codes and anything that is not a standard-conformant transaction + /// (see dispatch_response_()); on failure the response is empty and the exception code is in status. + /// The default implementation only logs a warning that the response is going unhandled - override it + /// to handle custom traffic (which also silences the warning). + virtual void on_custom_response(std::span request_pdu, std::span response_pdu, + ResponseStatus status); + ESPDEPRECATED("Use the typed read_*/write_* helpers or send_pdu() instead. Removed in 2027.2.0", "2026.8.0") void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len = 0, const uint8_t *payload = nullptr) { - this->parent_->send_pdu(this->address_, - helpers::create_client_pdu((ModbusFunctionCode) function, start_address, number_of_entities, - payload, payload_len), - this); + this->parent_->send_pdu( + this->address_, + helpers::create_client_pdu((FunctionCode) function, start_address, number_of_entities, payload, payload_len), + this); } void send_pdu(std::span pdu) { this->parent_->send_pdu(this->address_, pdu, this); } - void send_raw(const std::vector &payload) { this->parent_->send_raw(payload, this); } + ESPDEPRECATED("Use send_pdu() instead (the device address is prepended for you). Removed in 2027.2.0", "2026.8.0") + void send_raw(const std::vector &payload) { + if (payload.empty()) { + // Through the guard like every other delivery, so a handler calling send_raw({}) cannot recurse. + this->trigger_not_sent({}); + return; + } + this->parent_->send_pdu(payload[0], std::span(payload).subspan(1), this); + } + // Reads via the table-appropriate function code; an unreadable entity type maps to INVALID, which + // create_read_pdu() rejects into an empty PDU and send_pdu() signals via on_not_sent(). + void read_entities(EntityType entity_type, uint16_t start_address, uint16_t number_of_entities) { + this->send_pdu(helpers::create_read_pdu(helpers::modbus_register_read_function(entity_type), start_address, + number_of_entities)); + } + void read_input_registers(uint16_t start_address, uint16_t number_of_registers) { + this->send_pdu(helpers::create_read_pdu(FunctionCode::READ_INPUT_REGISTERS, start_address, number_of_registers)); + } + void read_holding_registers(uint16_t start_address, uint16_t number_of_registers) { + this->send_pdu(helpers::create_read_pdu(FunctionCode::READ_HOLDING_REGISTERS, start_address, number_of_registers)); + } + void read_coils(uint16_t start_address, uint16_t number_of_coils) { + this->send_pdu(helpers::create_read_pdu(FunctionCode::READ_COILS, start_address, number_of_coils)); + } + void read_discrete_inputs(uint16_t start_address, uint16_t number_of_inputs) { + this->send_pdu(helpers::create_read_pdu(FunctionCode::READ_DISCRETE_INPUTS, start_address, number_of_inputs)); + } + void write_single_register(uint16_t start_address, uint16_t value) { + this->send_pdu(helpers::create_write_single_register_pdu(start_address, value)); + } + void write_single_coil(uint16_t address, bool value) { + this->send_pdu(helpers::create_write_single_coil_pdu(address, value)); + } + void write_multiple_registers(uint16_t start_address, std::span values) { + this->send_pdu(helpers::create_write_registers_pdu(start_address, values)); + } + /// Note: std::vector cannot bind to std::span; use a contiguous bool container or the packed + /// overload. + void write_multiple_coils(uint16_t start_address, std::span values) { + this->send_pdu(helpers::create_write_coils_pdu(start_address, values)); + } + /// Packed variant: a PackedBits view (the same layout on_read_coils() delivers), so + /// read-modify-write needs no unpack/repack. + void write_multiple_coils(uint16_t start_address, PackedBits bits) { + this->send_pdu(helpers::create_write_coils_pdu(start_address, bits)); + } inline void clear_tx_queue_for_address(bool clear_sent = true) { this->parent_->clear_tx_queue_for_address(this->address_, clear_sent); } @@ -207,18 +380,39 @@ class ModbusClientDevice { bool ready_for_immediate_send() { return this->parent_->tx_buffer_empty() && !this->parent_->tx_blocked(); } protected: + /// Parses the request/response PDU pair and dispatches to the matching high-level typed callback + void dispatch_response_(std::span request_pdu, std::span response_pdu, + ResponseStatus status); + ModbusClientHub *parent_{nullptr}; + /// True while this device's on_not_sent() is on the stack (see trigger_not_sent()). + bool notifying_not_sent_{false}; uint8_t address_{0}; + bool custom_response_warned_{false}; // first unhandled custom response warns; repeats log at VERBOSE }; -// This is for compatibility with external components using the former class name -// Remove before 2026.12.0 -using ModbusDevice ESPDEPRECATED("Use ModbusClientDevice instead. Removed in 2026.12.0", - "2026.6.0") = ModbusClientDevice; +// Compatibility shim for external components written against the pre-2026.8 API, which subclassed +// ModbusDevice and overrode on_modbus_data()/on_modbus_error(). The name is free (nothing in-tree +// uses it), so instead of a plain alias it adapts the new span-based hooks back to the old +// signatures: on_modbus_data() receives the response payload as an owning vector (the heap copy +// exists only on this deprecated path) and on_modbus_error() the function code and exception code. +// Remove before 2027.2.0 (window restarted when the plain alias became a behavior shim in 2026.8.0) +class ESPDEPRECATED("Subclass ModbusClientDevice and override on_response()/on_error() instead. Removed in 2027.2.0", + "2026.8.0") ModbusDevice : public ModbusClientDevice { + public: + using ModbusClientDevice::ModbusClientDevice; + virtual void on_modbus_data(const std::vector &data) {} + virtual void on_modbus_error(uint8_t function_code, uint8_t exception_code) {} + + void on_response(std::span request_pdu, std::span response_pdu) override { + auto payload = helpers::server_pdu_payload(response_pdu); + this->on_modbus_data(std::vector(payload.begin(), payload.end())); + } + void on_error(std::span request_pdu, ExceptionCode exception_code) override { + this->on_modbus_error(request_pdu.empty() ? 0 : request_pdu[0], static_cast(exception_code)); + } +}; -// Transaction status: std::nullopt on success, otherwise the Modbus exception code. Server handlers return it; -// (future) client response callbacks receive it. Named without a side prefix so both directions share it. -using ResponseStatus = std::optional; // Register values exchanged with server handlers, in host byte order. Sized at the larger of the two protocol // maxima (read = 125 / 0x7D, write = 123 / 0x7B); the per-direction count limit is enforced by the hub, not by // the capacity of this type. @@ -237,7 +431,7 @@ class ModbusServerDevice { uint8_t get_address() const { return this->address_; } virtual ResponseStatus on_read_registers(uint16_t start_address, uint16_t number_of_registers, RegisterValues ®isters) { - return ModbusExceptionCode::ILLEGAL_FUNCTION; + return ExceptionCode::ILLEGAL_FUNCTION; }; virtual ResponseStatus on_read_input_registers(uint16_t start_address, uint16_t number_of_registers, RegisterValues ®isters) { @@ -248,7 +442,7 @@ class ModbusServerDevice { return this->on_read_registers(start_address, number_of_registers, registers); }; virtual ResponseStatus on_write_registers(uint16_t start_address, const RegisterValues ®isters) { - return ModbusExceptionCode::ILLEGAL_FUNCTION; + return ExceptionCode::ILLEGAL_FUNCTION; }; protected: diff --git a/esphome/components/modbus/modbus_definitions.h b/esphome/components/modbus/modbus_definitions.h index d11748bcd9..fd99055ca2 100644 --- a/esphome/components/modbus/modbus_definitions.h +++ b/esphome/components/modbus/modbus_definitions.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #include "esphome/core/component.h" #include "esphome/core/helpers.h" @@ -14,7 +18,7 @@ const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_1_END = 72; // 0x48 const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_INIT = 100; // 0x64 const uint8_t FUNCTION_CODE_USER_DEFINED_SPACE_2_END = 110; // 0x6E -enum class ModbusFunctionCode : uint8_t { +enum class FunctionCode : uint8_t { INVALID = 0x00, // 0x00 is not a valid function code (even for custom functions). CUSTOM = 0x00, // The CUSTOM alias should be removed in future. READ_COILS = 0x01, @@ -37,14 +41,20 @@ enum class ModbusFunctionCode : uint8_t { READ_FIFO_QUEUE = 0x18, // not implemented }; -/*Allow direct comparison operators between ModbusFunctionCode and uint8_t*/ -inline bool operator==(ModbusFunctionCode lhs, uint8_t rhs) { return static_cast(lhs) == rhs; } -inline bool operator==(uint8_t lhs, ModbusFunctionCode rhs) { return lhs == static_cast(rhs); } -inline bool operator!=(ModbusFunctionCode lhs, uint8_t rhs) { return !(static_cast(lhs) == rhs); } -inline bool operator!=(uint8_t lhs, ModbusFunctionCode rhs) { return !(lhs == static_cast(rhs)); } +// Remove before 2027.2.0 +using ModbusFunctionCode ESPDEPRECATED("Use modbus::FunctionCode instead. Removed in 2027.2.0", + "2026.8.0") = FunctionCode; -// 4.3 MODBUS Data model -enum class ModbusRegisterType : uint8_t { +/*Allow direct comparison operators between FunctionCode and uint8_t*/ +inline bool operator==(FunctionCode lhs, uint8_t rhs) { return static_cast(lhs) == rhs; } +inline bool operator==(uint8_t lhs, FunctionCode rhs) { return lhs == static_cast(rhs); } +inline bool operator!=(FunctionCode lhs, uint8_t rhs) { return !(static_cast(lhs) == rhs); } +inline bool operator!=(uint8_t lhs, FunctionCode rhs) { return !(lhs == static_cast(rhs)); } + +// 4.3 MODBUS Data model. "Entity" is the spec's umbrella for the four primary tables; only the +// 16-bit tables are registers (coils and discrete inputs are bits), so the enum is not named +// RegisterType. +enum class EntityType : uint8_t { CUSTOM = 0x00, COIL = 0x01, DISCRETE_INPUT = 0x02, @@ -52,15 +62,17 @@ enum class ModbusRegisterType : uint8_t { // Named INPUT_REGISTER (not INPUT) because Arduino cores define INPUT as a macro. INPUT_REGISTER = 0x04, // Remove before 2027.2.0 - READ ESPDEPRECATED("Use ModbusRegisterType::INPUT_REGISTER instead. Removed in 2027.2.0", "2026.7.0") = - INPUT_REGISTER, + READ ESPDEPRECATED("Use EntityType::INPUT_REGISTER instead. Removed in 2027.2.0", "2026.7.0") = INPUT_REGISTER, }; +// Remove before 2027.2.0 +using ModbusRegisterType ESPDEPRECATED("Use modbus::EntityType instead. Removed in 2027.2.0", "2026.8.0") = EntityType; + // 7 MODBUS Exception Responses: const uint8_t FUNCTION_CODE_MASK = 0x7F; const uint8_t FUNCTION_CODE_EXCEPTION_MASK = 0x80; -enum class ModbusExceptionCode : uint8_t { +enum class ExceptionCode : uint8_t { ILLEGAL_FUNCTION = 0x01, ILLEGAL_DATA_ADDRESS = 0x02, ILLEGAL_DATA_VALUE = 0x03, @@ -72,9 +84,19 @@ enum class ModbusExceptionCode : uint8_t { GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 0x0B, }; +// Remove before 2027.2.0 +using ModbusExceptionCode ESPDEPRECATED("Use modbus::ExceptionCode instead. Removed in 2027.2.0", + "2026.8.0") = ExceptionCode; + +// 6.11 15 (0x0F) Write Multiple Coils +static constexpr uint16_t MAX_NUM_OF_COILS_TO_WRITE = 1968; // 0x7B0 + // 6.12 16 (0x10) Write Multiple registers: static constexpr uint16_t MAX_NUM_OF_REGISTERS_TO_WRITE = 123; // 0x7B +// 6.17 23 (0x17) Read/Write Multiple Registers: +static constexpr uint16_t MAX_NUM_OF_REGISTERS_TO_WRITE_RW = 121; // 0x79 + // 6.1 01 (0x01) Read Coils // 6.2 02 (0x02) Read Discrete Inputs static constexpr uint16_t MAX_NUM_OF_COILS_TO_READ = 2000; // 0x7D0 @@ -86,8 +108,68 @@ static constexpr uint16_t MAX_NUM_OF_REGISTERS_TO_READ = 125; // 0x7D // Smallest possible frame is 4 bytes (custom function with no data): address(1) + function(1) + CRC(2) static constexpr uint16_t MIN_FRAME_SIZE = 4; +static constexpr uint16_t MIN_PDU_SIZE = 1; static constexpr uint16_t MAX_PDU_SIZE = 253; // Max PDU size is 256 - address(1) - CRC(2) = 253 static constexpr uint16_t MAX_RAW_SIZE = 254; // Max RAW size is 256 - CRC(2) = 254 +// A read request PDU is always function code(1) + start address(2) + quantity(2) +static constexpr uint16_t READ_PDU_SIZE = 5; +// A single-write PDU is always function code(1) + address(2) + value(2) +static constexpr uint16_t WRITE_SINGLE_PDU_SIZE = 5; static constexpr uint16_t MAX_FRAME_SIZE = 256; +/** Read-only view of Modbus-packed bits: bit 0 of byte 0 is the first bit (LSB first), the layout + * coil/discrete-input values use on the wire. Bundles the bit count with the packed bytes so the + * two cannot desynchronize. The view does not own the bytes - it is only valid while they are. + * Reads (operator[]) are unchecked by design - the caller owns the bit < size() precondition, as + * with any subscript. Writes and forwarding are defensive: set() drops out-of-range bits and + * bytes() clamps to the real span, because those paths touch buffers and the wire directly. + */ +/// Bits pack 8 per data byte, rounded up to whole bytes. +constexpr size_t packed_bit_bytes(size_t bits) { return (bits + 7) / 8; } + +class PackedBits { + public: + PackedBits(std::span data, uint16_t count) : data_(data), count_(count) {} + /// Value of the given bit; bit must be < size(). + bool operator[](size_t bit) const { return (this->data_[bit / 8] & (1 << (bit % 8))) != 0; } + /// Number of bits in the view. + uint16_t size() const { return this->count_; } + /// The underlying packed bytes: exactly ceil(size() / 8) bytes, even when the view was constructed + /// over a larger buffer - forwarding this span onto the wire can never leak trailing buffer content. + /// Clamped to the actual span so a view over a too-short buffer stays detectable instead of UB. + std::span bytes() const { + return this->data_.first(std::min(packed_bit_bytes(this->count_), this->data_.size())); + } + + private: + std::span data_; // must cover ceil(count_ / 8) bytes + uint16_t count_; +}; + +/** Mutable counterpart of PackedBits: set() writes bits in place (deliberately no proxy operator[]=). + * Converts implicitly to PackedBits for read access. + */ +class MutablePackedBits { + public: + MutablePackedBits(std::span data, uint16_t count) : data_(data), count_(count) {} + bool operator[](size_t bit) const { return (this->data_[bit / 8] & (1 << (bit % 8))) != 0; } + /// Set or clear the given bit. Out-of-range bits are dropped: on the server read path the span wraps a + /// stack response buffer, so a handler looping past size() must not be able to smash the frame. + void set(size_t bit, bool value) { + if (bit >= this->count_ || bit / 8 >= this->data_.size()) + return; + if (value) { + this->data_[bit / 8] |= (1 << (bit % 8)); + } else { + this->data_[bit / 8] &= ~(1 << (bit % 8)); + } + } + uint16_t size() const { return this->count_; } + operator PackedBits() const { return PackedBits(this->data_, this->count_); } + + private: + std::span data_; // must cover ceil(count_ / 8) bytes + uint16_t count_; +}; + /// End of Modbus definitions } // namespace esphome::modbus diff --git a/esphome/components/modbus/modbus_helpers.cpp b/esphome/components/modbus/modbus_helpers.cpp index de109606cb..85c5d3e882 100644 --- a/esphome/components/modbus/modbus_helpers.cpp +++ b/esphome/components/modbus/modbus_helpers.cpp @@ -7,74 +7,162 @@ namespace esphome::modbus::helpers { static const char *const TAG = "modbus_helpers"; -uint16_t server_frame_length(const uint8_t *frame, size_t size) { - if (size < 2) - return MIN_FRAME_SIZE; - if (is_function_code_exception(frame[1])) { - return 5; // address(1) + function(1) + exception(1) + CRC(2) +// A quantity/address pair is standard when the quantity is non-zero, within the per-table maximum, +// and the range [start_address, start_address + quantity) stays inside the 16-bit address space +// (the 32-bit promotion is the overflow guard - a 16-bit sum could wrap and pass). +static bool quantity_in_range(uint16_t start_address, uint16_t quantity, uint16_t max_quantity) { + return quantity != 0 && quantity <= max_quantity && uint32_t(start_address) + quantity <= 0x10000u; +} + +// The spec allows exactly ON (0xFF00) and OFF (0x0000) for a single-coil value, on the request and +// on its echoed response alike. +static bool is_canonical_coil_value(uint8_t high_byte, uint8_t low_byte) { + return (high_byte == 0xFF || high_byte == 0x00) && low_byte == 0x00; +} + +uint16_t server_pdu_length(const uint8_t *frame, size_t size) { + if (size < MIN_PDU_SIZE) + return MIN_PDU_SIZE; + if (is_function_code_exception(frame[0])) { + return 2; // function(1) + exception(1) } - switch (static_cast(frame[1])) { - case ModbusFunctionCode::READ_COILS: - case ModbusFunctionCode::READ_DISCRETE_INPUTS: - case ModbusFunctionCode::READ_HOLDING_REGISTERS: - case ModbusFunctionCode::READ_INPUT_REGISTERS: - // address(1) + function(1) + byte count(1) + data + CRC(2) - return 5 + (size > 2 ? std::min(frame[2], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0); - case ModbusFunctionCode::WRITE_SINGLE_COIL: - case ModbusFunctionCode::WRITE_SINGLE_REGISTER: - case ModbusFunctionCode::WRITE_MULTIPLE_COILS: - case ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS: - return 8; // address(1) + function(1) + output/register address(2) + value(2) + CRC(2) + switch (static_cast(frame[0])) { + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: + // function(1) + byte count(1) + data + return 2 + (size > 1 ? std::min(frame[1], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0); + case FunctionCode::WRITE_SINGLE_COIL: + case FunctionCode::WRITE_SINGLE_REGISTER: + case FunctionCode::WRITE_MULTIPLE_COILS: + case FunctionCode::WRITE_MULTIPLE_REGISTERS: + return 5; // function(1) + output/register address(2) + value(2) // Unsupported function codes. Included here to prevent parser failures. Excluding Serial Line specific functions. - case ModbusFunctionCode::READ_FILE_RECORD: - case ModbusFunctionCode::WRITE_FILE_RECORD: - // address(1) + function(1) + byte count(1) + data + CRC(2) - return 5 + (size > 2 ? std::min(frame[2], uint8_t(MAX_FRAME_SIZE - 5)) : 0); - case ModbusFunctionCode::MASK_WRITE_REGISTER: - return 10; // address(1) + function(1) + reference address(2) + AND mask(2) + OR mask(2) + CRC(2) - case ModbusFunctionCode::READ_WRITE_MULTIPLE_REGISTERS: - // address(1) + function(1) + byte count(1) + data + CRC(2) - return 5 + (size > 2 ? std::min(frame[2], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0); - case ModbusFunctionCode::READ_FIFO_QUEUE: - // address(1) + function(1) + fifo address(2) CRC(2) - return 6; + case FunctionCode::READ_FILE_RECORD: + case FunctionCode::WRITE_FILE_RECORD: + // function(1) + byte count(1) + data + return 2 + (size > 1 ? std::min(frame[1], uint8_t(MAX_PDU_SIZE - 2)) : 0); + case FunctionCode::MASK_WRITE_REGISTER: + return 7; // function(1) + reference address(2) + AND mask(2) + OR mask(2) + case FunctionCode::READ_WRITE_MULTIPLE_REGISTERS: + // function(1) + byte count(1) + data + return 2 + (size > 1 ? std::min(frame[1], uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2)) : 0); + case FunctionCode::READ_FIFO_QUEUE: + // function(1) + fifo address(2) + return 3; default: - return MIN_FRAME_SIZE; // unknown length + return MIN_PDU_SIZE; // unknown length } } -uint16_t client_frame_length(const uint8_t *frame, size_t size) { - if (size < 2) - return MIN_FRAME_SIZE; - switch (static_cast(frame[1])) { - case ModbusFunctionCode::READ_COILS: - case ModbusFunctionCode::READ_DISCRETE_INPUTS: - case ModbusFunctionCode::READ_HOLDING_REGISTERS: - case ModbusFunctionCode::READ_INPUT_REGISTERS: - // address(1) + function(1) + start address(2) + quantity(2) + CRC(2) - case ModbusFunctionCode::WRITE_SINGLE_COIL: - case ModbusFunctionCode::WRITE_SINGLE_REGISTER: - return 8; // address(1) + function(1) + output/register address(2) + value(2) + CRC(2) - case ModbusFunctionCode::WRITE_MULTIPLE_COILS: - case ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS: - // address(1) + function(1) + start address(2) + quantity(2) + byte count(1) + data + CRC(2) - return 9 + (size > 6 ? std::min(frame[6], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE * 2)) : 0); +uint16_t client_pdu_length(const uint8_t *frame, size_t size) { + if (size < MIN_PDU_SIZE) + return MIN_PDU_SIZE; + switch (static_cast(frame[0])) { + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: + // function(1) + start address(2) + quantity(2) + case FunctionCode::WRITE_SINGLE_COIL: + case FunctionCode::WRITE_SINGLE_REGISTER: + return 5; // function(1) + output/register address(2) + value(2) + case FunctionCode::WRITE_MULTIPLE_COILS: + case FunctionCode::WRITE_MULTIPLE_REGISTERS: + // function(1) + start address(2) + quantity(2) + byte count(1) + data + return 6 + (size > 5 ? std::min(frame[5], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE * 2)) : 0); // Unsupported function codes. Included here to prevent parser failures. Excluding Serial Line specific functions. - case ModbusFunctionCode::READ_FILE_RECORD: - case ModbusFunctionCode::WRITE_FILE_RECORD: - // address(1) + function(1) + byte count(1) + data + CRC(2) - return 5 + (size > 2 ? std::min(frame[2], uint8_t(MAX_FRAME_SIZE - 5)) : 0); - case ModbusFunctionCode::MASK_WRITE_REGISTER: - return 10; // address(1) + function(1) + reference address(2) + AND mask(2) + OR mask(2) + CRC(2) - case ModbusFunctionCode::READ_WRITE_MULTIPLE_REGISTERS: - // address(1) + function(1) + read start address(2) + read quantity(2) + write start address(2) + - // write quantity(2) + byte count(1) + data + CRC(2) - return 13 + (size > 10 ? std::min(frame[10], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE * 2)) : 0); - case ModbusFunctionCode::READ_FIFO_QUEUE: - // address(1) + function(1) + fifo address(2) CRC(2) - return 6; + case FunctionCode::READ_FILE_RECORD: + case FunctionCode::WRITE_FILE_RECORD: + // function(1) + byte count(1) + data + return 2 + (size > 1 ? std::min(frame[1], uint8_t(MAX_PDU_SIZE - 2)) : 0); + case FunctionCode::MASK_WRITE_REGISTER: + return 7; // function(1) + reference address(2) + AND mask(2) + OR mask(2) + case FunctionCode::READ_WRITE_MULTIPLE_REGISTERS: + // function(1) + read start address(2) + read quantity(2) + write start address(2) + + // write quantity(2) + byte count(1) + data + return 10 + (size > 9 ? std::min(frame[9], uint8_t(MAX_NUM_OF_REGISTERS_TO_WRITE_RW * 2)) : 0); + case FunctionCode::READ_FIFO_QUEUE: + // function(1) + fifo address(2) + return 3; default: - return MIN_FRAME_SIZE; // unknown length + return MIN_PDU_SIZE; // unknown length + } +} + +bool is_server_pdu_standard(const uint8_t *pdu, size_t size) { + if (server_pdu_length(pdu, size) != size) + return false; + + const auto function_code = static_cast(pdu[0]); + switch (function_code) { + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: + // A conformant bit-read response carries at least one packed byte (up to 2000 bits = 250 bytes). + return pdu[1] != 0 && pdu[1] <= uint8_t(packed_bit_bytes(MAX_NUM_OF_COILS_TO_READ)); + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: + // Registers are 2 bytes each: the byte count must be a non-zero even count within the read maximum. + return pdu[1] != 0 && pdu[1] % 2 == 0 && pdu[1] <= uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2); + case FunctionCode::READ_FILE_RECORD: + case FunctionCode::WRITE_FILE_RECORD: + return pdu[1] <= uint8_t(MAX_PDU_SIZE - 2); + case FunctionCode::READ_WRITE_MULTIPLE_REGISTERS: + return pdu[1] != 0 && pdu[1] % 2 == 0 && pdu[1] <= uint8_t(MAX_NUM_OF_REGISTERS_TO_READ * 2); + case FunctionCode::WRITE_MULTIPLE_COILS: + case FunctionCode::WRITE_MULTIPLE_REGISTERS: { + // The response echoes start address and quantity: bound them like the request side does. + const bool bits = function_code == FunctionCode::WRITE_MULTIPLE_COILS; + const uint16_t max_quantity = bits ? MAX_NUM_OF_COILS_TO_WRITE : MAX_NUM_OF_REGISTERS_TO_WRITE; + return quantity_in_range(get_data(pdu, 1), get_data(pdu, 3), max_quantity); + } + case FunctionCode::WRITE_SINGLE_COIL: + // The response echoes the request, so the same ON/OFF constraint applies. + return is_canonical_coil_value(pdu[3], pdu[4]); + default: + return true; // All other function codes validated by length alone + } +} + +bool is_client_pdu_standard(const uint8_t *pdu, size_t size) { + if (client_pdu_length(pdu, size) != size) + return false; + + const auto function_code = static_cast(pdu[0]); + switch (function_code) { + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: { + const bool bits = + function_code == FunctionCode::READ_COILS || function_code == FunctionCode::READ_DISCRETE_INPUTS; + const uint16_t max_quantity = bits ? MAX_NUM_OF_COILS_TO_READ : MAX_NUM_OF_REGISTERS_TO_READ; + return quantity_in_range(get_data(pdu, 1), get_data(pdu, 3), max_quantity); + } + case FunctionCode::WRITE_MULTIPLE_COILS: + case FunctionCode::WRITE_MULTIPLE_REGISTERS: { + const bool bits = function_code == FunctionCode::WRITE_MULTIPLE_COILS; + const uint16_t quantity = get_data(pdu, 3); + const uint16_t max_quantity = bits ? MAX_NUM_OF_COILS_TO_WRITE : MAX_NUM_OF_REGISTERS_TO_WRITE; + // Coils are packed 8 per data byte; registers are 2 bytes each. + const size_t expected_data_bytes = bits ? packed_bit_bytes(quantity) : quantity * 2; + return quantity_in_range(get_data(pdu, 1), quantity, max_quantity) && pdu[5] == expected_data_bytes; + } + case FunctionCode::READ_FILE_RECORD: + case FunctionCode::WRITE_FILE_RECORD: + return pdu[1] <= MAX_PDU_SIZE - 2; + case FunctionCode::READ_WRITE_MULTIPLE_REGISTERS: { + const uint16_t quantity_write = get_data(pdu, 7); + return quantity_in_range(get_data(pdu, 1), get_data(pdu, 3), MAX_NUM_OF_REGISTERS_TO_READ) && + quantity_in_range(get_data(pdu, 5), quantity_write, MAX_NUM_OF_REGISTERS_TO_WRITE_RW) && + pdu[9] == quantity_write * 2; + } + case FunctionCode::WRITE_SINGLE_COIL: + // The one variable field in an otherwise fixed-shape PDU: the spec allows exactly ON/OFF. + return is_canonical_coil_value(pdu[3], pdu[4]); + default: + return true; // All other function codes validated by length alone } } @@ -197,101 +285,250 @@ std::optional registers_to_number(const uint16_t *registers, size_t cou return payload_to_number(bytes, required_size, sensor_value_type, 0, 0xFFFFFFFF); } -StaticVector create_client_pdu(ModbusFunctionCode function_code, uint16_t start_address, - uint16_t number_of_entities, const uint8_t *values, - size_t values_len) { +// Every request PDU opens with the same 5-byte layout: function code, then two big-endian 16-bit +// fields (start address + quantity for reads and multi-writes, address + value for single writes). +template +static void append_pdu_header(StaticVector &pdu, FunctionCode function_code, uint16_t first, + uint16_t second) { + pdu.push_back(static_cast(function_code)); + pdu.push_back(first >> 8); + pdu.push_back(first >> 0); + pdu.push_back(second >> 8); + pdu.push_back(second >> 0); +} + +// Zero the unused bits of a multi-coil write's final data byte, as the spec requires. Kept in one +// place so the generic and typed coil builders produce identical wire bytes for the same write. +static void mask_trailing_pad_bits(std::span data, uint16_t bit_count) { + if (data.empty() || bit_count % 8 == 0) + return; + data.back() &= static_cast((1 << (bit_count % 8)) - 1); +} + +ReadPdu create_read_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities) { + ReadPdu pdu; // declared before every return so NRVO fires (all paths return the same object) + if (number_of_entities == 0) { + ESP_LOGE(TAG, "Number of entities is zero for function code %02X", static_cast(function_code)); + return pdu; + } + if (uint32_t(start_address) + number_of_entities > 0x10000u) { + ESP_LOGE(TAG, "Read of %u entities at %u runs past the 16-bit address space, dropping request", number_of_entities, + start_address); + return pdu; + } + + switch (function_code) { + case FunctionCode::READ_COILS: + if (number_of_entities > MAX_NUM_OF_COILS_TO_READ) { + ESP_LOGE(TAG, "number_of_entities %u exceeds maximum coils to read %u for function code %02X", + number_of_entities, MAX_NUM_OF_COILS_TO_READ, static_cast(function_code)); + return pdu; + } + break; + case FunctionCode::READ_DISCRETE_INPUTS: + if (number_of_entities > MAX_NUM_OF_DISCRETE_INPUTS_TO_READ) { + ESP_LOGE(TAG, "number_of_entities %u exceeds maximum discrete inputs to read %u for function code %02X", + number_of_entities, MAX_NUM_OF_DISCRETE_INPUTS_TO_READ, static_cast(function_code)); + return pdu; + } + break; + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: + if (number_of_entities > MAX_NUM_OF_REGISTERS_TO_READ) { + ESP_LOGE(TAG, "number_of_entities %u exceeds maximum registers to read %u for function code %02X", + number_of_entities, MAX_NUM_OF_REGISTERS_TO_READ, static_cast(function_code)); + return pdu; + } + break; + default: + ESP_LOGE(TAG, "Unsupported function code %02X for read PDU creation", static_cast(function_code)); + return pdu; + } + + append_pdu_header(pdu, function_code, start_address, number_of_entities); + return pdu; +} + +PduBuffer create_client_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities, + const uint8_t *values, size_t values_len) { + PduBuffer pdu; // declared before every return so NRVO fires (all paths return the same object) + // Generic entry point; prefer the direction- and type-specific builders (create_read_pdu(), + // create_write_registers_pdu(), etc.) which bound their inputs per spec. if (is_function_code_read(static_cast(function_code))) { if (values != nullptr || values_len > 0) { ESP_LOGW(TAG, "Values provided for read function code %02X, but will be ignored", static_cast(function_code)); } - } else if (is_function_code_write(static_cast(function_code))) { - if (values == nullptr || values_len == 0) { - ESP_LOGE(TAG, "No values provided for write function code %02X", static_cast(function_code)); - return {}; - } - } else { + auto read_pdu = create_read_pdu(function_code, start_address, number_of_entities); + pdu.assign(read_pdu.begin(), read_pdu.end()); + return pdu; + } + // Exact codes only: is_function_code_write() masks the exception bit, which would let the + // exception-flagged forms (0x85/0x86/0x8F/0x90) build a request announcing itself as an exception. + const bool is_single = + function_code == FunctionCode::WRITE_SINGLE_COIL || function_code == FunctionCode::WRITE_SINGLE_REGISTER; + const bool is_multi = + function_code == FunctionCode::WRITE_MULTIPLE_COILS || function_code == FunctionCode::WRITE_MULTIPLE_REGISTERS; + if (!is_single && !is_multi) { ESP_LOGE(TAG, "Unsupported function code %02X for client PDU creation", static_cast(function_code)); - return {}; + return pdu; } + // Generic write builder: raw caller-supplied bytes, so we can only guard against the PDU byte capacity here. + if (values == nullptr || values_len == 0) { + ESP_LOGE(TAG, "No values provided for write function code %02X", static_cast(function_code)); + return pdu; + } if (number_of_entities == 0) { ESP_LOGE(TAG, "Number of entities is zero for function code %02X", static_cast(function_code)); - return {}; + return pdu; + } + // number_of_entities is ignored for single write, so only validate it for the multiple variants. + // The bound is per function code (coils pack 8 per byte, so their quantity limit is far higher) - + // the same limits is_client_pdu_standard() accepts, so builder and validator agree. + const uint16_t max_entities = + function_code == FunctionCode::WRITE_MULTIPLE_COILS ? MAX_NUM_OF_COILS_TO_WRITE : MAX_NUM_OF_REGISTERS_TO_WRITE; + if (!is_single && number_of_entities > max_entities) { + ESP_LOGE(TAG, "number_of_entities %u exceeds maximum %u for function code %02X", number_of_entities, max_entities, + static_cast(function_code)); + return pdu; + } + if (!is_single && uint32_t(start_address) + number_of_entities > 0x10000u) { + ESP_LOGE(TAG, "Write of %u entities at %u runs past the 16-bit address space, dropping request", number_of_entities, + start_address); + return pdu; } - switch (function_code) { - case ModbusFunctionCode::READ_COILS: - if (number_of_entities > MAX_NUM_OF_COILS_TO_READ) { - ESP_LOGE(TAG, "number_of_entities %u exceeds maximum coils to read %u for function code %02X", - number_of_entities, MAX_NUM_OF_COILS_TO_READ, static_cast(function_code)); - return {}; - } - break; - case ModbusFunctionCode::READ_DISCRETE_INPUTS: - if (number_of_entities > MAX_NUM_OF_DISCRETE_INPUTS_TO_READ) { - ESP_LOGE(TAG, "number_of_entities %u exceeds maximum discrete inputs to read %u for function code %02X", - number_of_entities, MAX_NUM_OF_DISCRETE_INPUTS_TO_READ, static_cast(function_code)); - return {}; - } - break; - case ModbusFunctionCode::READ_HOLDING_REGISTERS: - case ModbusFunctionCode::READ_INPUT_REGISTERS: - if (number_of_entities > MAX_NUM_OF_REGISTERS_TO_READ) { - ESP_LOGE(TAG, "number_of_entities %u exceeds maximum registers to read %u for function code %02X", - number_of_entities, MAX_NUM_OF_REGISTERS_TO_READ, static_cast(function_code)); - return {}; - } - break; - case ModbusFunctionCode::WRITE_SINGLE_COIL: - case ModbusFunctionCode::WRITE_SINGLE_REGISTER: - break; // number_of_entities is ignored for single write, so no need to validate - case ModbusFunctionCode::WRITE_MULTIPLE_COILS: - case ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS: - if (number_of_entities > MAX_NUM_OF_REGISTERS_TO_WRITE) { - ESP_LOGE(TAG, "number_of_entities %u exceeds maximum registers to write %u for function code %02X", - number_of_entities, MAX_NUM_OF_REGISTERS_TO_WRITE, static_cast(function_code)); - return {}; - } - break; - default: - ESP_LOGE(TAG, "Unsupported function code %u for client PDU creation", static_cast(function_code)); - return {}; - } - - StaticVector pdu; - pdu.push_back(static_cast(function_code)); - pdu.push_back(start_address >> 8); - pdu.push_back(start_address >> 0); - if (function_code != ModbusFunctionCode::WRITE_SINGLE_COIL && - function_code != ModbusFunctionCode::WRITE_SINGLE_REGISTER) { - pdu.push_back(number_of_entities >> 8); - pdu.push_back(number_of_entities >> 0); - } - - if (is_function_code_write(static_cast(function_code))) { - if (function_code == ModbusFunctionCode::WRITE_MULTIPLE_COILS || - function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) { - // 6 bytes of overhead (fc + start_addr×2 + qty×2 + byte_count) leave MAX_PDU_SIZE-6 bytes for values - static constexpr size_t MAX_WRITE_MULTIPLE_VALUES_LEN = MAX_PDU_SIZE - 6; - if (values_len > MAX_WRITE_MULTIPLE_VALUES_LEN) { - ESP_LOGE(TAG, "values_len %zu exceeds PDU capacity %zu, dropping request", values_len, - MAX_WRITE_MULTIPLE_VALUES_LEN); - return {}; - } - pdu.push_back(values_len); // Byte count is required for write multiple - for (size_t i = 0; i < values_len; i++) - pdu.push_back(values[i]); - } else { - // Write single register or coil (2 bytes) - if (values_len < 2) { - ESP_LOGE(TAG, "values_len %zu too small for write-single command (need 2), dropping request", values_len); - return {}; - } - pdu.push_back(values[0]); - pdu.push_back(values[1]); + if (is_single) { + // Write single register or coil: the two value bytes are the header's second field. + if (values_len < 2) { + ESP_LOGE(TAG, "values_len %zu too small for write-single command (need 2), dropping request", values_len); + return pdu; } + // The spec allows exactly ON (0xFF00) and OFF (0x0000) for a single-coil write - the same rule + // is_client_pdu_standard() enforces, so a built frame cannot be misclassified on reply. + if (function_code == FunctionCode::WRITE_SINGLE_COIL && !is_canonical_coil_value(values[0], values[1])) { + ESP_LOGE(TAG, "Invalid single-coil value %02X%02X (must be FF00 or 0000), dropping request", values[0], + values[1]); + return pdu; + } + append_pdu_header(pdu, function_code, start_address, uint16_t((values[0] << 8) | values[1])); + return pdu; + } + // The quantity is spec-bounded above, so the data length just has to agree with it exactly + // (registers are 2 bytes each, coils pack 8 per byte). This is the same consistency the response + // dispatch enforces via is_client_pdu_standard(), so a frame built here can never be classified + // non-standard on reply, and the spec bound keeps the PDU within capacity by construction. + // Checked before the header append: a failed check must return an empty PDU, not a 5-byte partial one. + const bool bits = function_code == FunctionCode::WRITE_MULTIPLE_COILS; + const size_t expected_len = bits ? packed_bit_bytes(number_of_entities) : static_cast(number_of_entities) * 2; + if (values_len != expected_len) { + ESP_LOGE(TAG, "values_len %zu does not match %u entities (expected %zu) for function code %02X, dropping request", + values_len, number_of_entities, expected_len, static_cast(function_code)); + return pdu; + } + append_pdu_header(pdu, function_code, start_address, number_of_entities); + pdu.push_back(values_len); // Byte count is required for write multiple + for (size_t i = 0; i < values_len; i++) + pdu.push_back(values[i]); + if (bits) + mask_trailing_pad_bits(pdu, number_of_entities); + return pdu; +} + +PduBuffer create_write_registers_pdu(uint16_t start_address, std::span values) { + PduBuffer pdu; // declared before every return so NRVO fires (all paths return the same object) + if (values.empty()) { + ESP_LOGE(TAG, "No values provided for write multiple registers, dropping request"); + return pdu; + } + // Byte count is registers × 2 (per spec); bounding the register count keeps the PDU within MAX_PDU_SIZE. + if (values.size() > MAX_NUM_OF_REGISTERS_TO_WRITE) { + ESP_LOGE(TAG, "values.size() %zu exceeds maximum registers to write %u, dropping request", values.size(), + MAX_NUM_OF_REGISTERS_TO_WRITE); + return pdu; + } + if (uint32_t(start_address) + values.size() > 0x10000u) { + ESP_LOGE(TAG, "Write of %zu registers at %u runs past the 16-bit address space, dropping request", values.size(), + start_address); + return pdu; + } + append_pdu_header(pdu, FunctionCode::WRITE_MULTIPLE_REGISTERS, start_address, values.size()); + pdu.push_back(static_cast(values.size() * 2)); // byte count + for (auto v : values) { + auto decoded_value = decode_value(v); + pdu.push_back(decoded_value[0]); + pdu.push_back(decoded_value[1]); } return pdu; } + +WriteSinglePdu create_write_single_register_pdu(uint16_t start_address, uint16_t value) { + WriteSinglePdu pdu; + append_pdu_header(pdu, FunctionCode::WRITE_SINGLE_REGISTER, start_address, value); + return pdu; +} + +WriteSinglePdu create_write_single_coil_pdu(uint16_t address, bool value) { + WriteSinglePdu pdu; + append_pdu_header(pdu, FunctionCode::WRITE_SINGLE_COIL, address, value ? 0xFF00 : 0x0000); + return pdu; +} + +// Shared core for the two coil-write overloads: validates, then builds into the caller's named +// pdu (left empty on failure). Each overload's returns all name one local, so NRVO fires. +static void build_write_coils_pdu(PduBuffer &pdu, uint16_t start_address, PackedBits bits) { + const uint16_t count = bits.size(); + const std::span packed_bits = bits.bytes(); + if (count == 0) { + ESP_LOGE(TAG, "No coils requested for write multiple coils, dropping request"); + return; + } + if (count > MAX_NUM_OF_COILS_TO_WRITE) { + ESP_LOGE(TAG, "count %u exceeds maximum coils to write %u, dropping request", count, MAX_NUM_OF_COILS_TO_WRITE); + return; + } + if (uint32_t(start_address) + count > 0x10000u) { + ESP_LOGE(TAG, "Write of %u coils at %u runs past the 16-bit address space, dropping request", count, start_address); + return; + } + const size_t byte_count = packed_bit_bytes(count); + if (packed_bits.size() < byte_count) { + ESP_LOGE(TAG, "packed_bits (%zu bytes) does not cover %u coils (%zu bytes), dropping request", packed_bits.size(), + count, byte_count); + return; + } + append_pdu_header(pdu, FunctionCode::WRITE_MULTIPLE_COILS, start_address, count); + pdu.push_back(static_cast(byte_count)); + for (size_t i = 0; i != byte_count; i++) { + pdu.push_back(packed_bits[i]); + } + mask_trailing_pad_bits(pdu, count); +} + +PduBuffer create_write_coils_pdu(uint16_t start_address, PackedBits bits) { + PduBuffer pdu; + build_write_coils_pdu(pdu, start_address, bits); + return pdu; +} + +PduBuffer create_write_coils_pdu(uint16_t start_address, std::span values) { + PduBuffer pdu; // declared before every return so NRVO fires (all paths return the same object) + // Bound before packing so the transient buffer below cannot overflow; the shared core validates the rest. + if (values.size() > MAX_NUM_OF_COILS_TO_WRITE) { + ESP_LOGE(TAG, "values.size() %zu exceeds maximum coils to write %u, dropping request", values.size(), + MAX_NUM_OF_COILS_TO_WRITE); + return pdu; + } + StaticVector packed; + for (size_t i = 0; i != values.size(); i++) { + if (i % 8 == 0) + packed.push_back(0); + if (values[i]) + packed[i / 8] |= (1 << (i % 8)); + } + build_write_coils_pdu(pdu, start_address, + PackedBits(std::span(packed.data(), packed.size()), values.size())); + return pdu; +} } // namespace esphome::modbus::helpers diff --git a/esphome/components/modbus/modbus_helpers.h b/esphome/components/modbus/modbus_helpers.h index 45a13f7582..3dd933c4d7 100644 --- a/esphome/components/modbus/modbus_helpers.h +++ b/esphome/components/modbus/modbus_helpers.h @@ -12,19 +12,19 @@ namespace esphome::modbus::helpers { inline bool is_function_code_read(uint8_t function_code) { - ModbusFunctionCode masked_function_code = static_cast(function_code & FUNCTION_CODE_MASK); - return masked_function_code == ModbusFunctionCode::READ_COILS || - masked_function_code == ModbusFunctionCode::READ_DISCRETE_INPUTS || - masked_function_code == ModbusFunctionCode::READ_HOLDING_REGISTERS || - masked_function_code == ModbusFunctionCode::READ_INPUT_REGISTERS; + FunctionCode masked_function_code = static_cast(function_code & FUNCTION_CODE_MASK); + return masked_function_code == FunctionCode::READ_COILS || + masked_function_code == FunctionCode::READ_DISCRETE_INPUTS || + masked_function_code == FunctionCode::READ_HOLDING_REGISTERS || + masked_function_code == FunctionCode::READ_INPUT_REGISTERS; } inline bool is_function_code_write(uint8_t function_code) { - ModbusFunctionCode masked_function_code = static_cast(function_code & FUNCTION_CODE_MASK); - return masked_function_code == ModbusFunctionCode::WRITE_SINGLE_COIL || - masked_function_code == ModbusFunctionCode::WRITE_SINGLE_REGISTER || - masked_function_code == ModbusFunctionCode::WRITE_MULTIPLE_COILS || - masked_function_code == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS; + FunctionCode masked_function_code = static_cast(function_code & FUNCTION_CODE_MASK); + return masked_function_code == FunctionCode::WRITE_SINGLE_COIL || + masked_function_code == FunctionCode::WRITE_SINGLE_REGISTER || + masked_function_code == FunctionCode::WRITE_MULTIPLE_COILS || + masked_function_code == FunctionCode::WRITE_MULTIPLE_REGISTERS; } inline bool is_function_code_exception(uint8_t function_code) { @@ -39,28 +39,70 @@ inline bool is_function_code_custom(uint8_t function_code) { masked_function_code <= FUNCTION_CODE_USER_DEFINED_SPACE_2_END); } -// Returns the expected length of a server response frame based on the function code -// If the frame is too short to determine the length, returns the minimum length -uint16_t server_frame_length(const uint8_t *frame, size_t size); +// Returns the expected length of a server response PDU based on the function code. +// If too few bytes have arrived to determine the length, returns the minimum length. `size` is the +// number of bytes available so far, which may exceed the eventual PDU (e.g. include the frame's CRC +// bytes): only fixed header positions are interpreted, so surplus bytes are never misread. +uint16_t server_pdu_length(const uint8_t *frame, size_t size); +// Frame counterpart: address(1) + PDU + CRC(2). Passes every received byte after the address through, +// so header fields (e.g. a byte count) are interpreted as soon as they arrive. +inline uint16_t server_frame_length(const uint8_t *frame, size_t size) { + if (size < 2) + return MIN_FRAME_SIZE; // function code not received yet + return server_pdu_length(frame + 1, size - 1) + 3; +} -// Returns the expected length of a client request frame based on the function code -// If the frame is too short to determine the length, returns the minimum length -uint16_t client_frame_length(const uint8_t *frame, size_t size); +// Returns the expected length of a client request PDU based on the function code. +// Same contract as server_pdu_length(): `size` is bytes available so far, may exceed the PDU. +uint16_t client_pdu_length(const uint8_t *frame, size_t size); +inline uint16_t client_frame_length(const uint8_t *frame, size_t size) { + if (size < 2) + return MIN_FRAME_SIZE; // function code not received yet + return client_pdu_length(frame + 1, size - 1) + 3; +} +// Returns true if pdu is a complete transaction whose shape is consistent with its function code. +// Unlike *_pdu_length(), `size` here is the exact PDU length: a size mismatch is non-conformant. +// Function codes with nothing variable to cross-check are validated by their fixed length alone: the +// single writes (except 0x05's value field, which must be 0x0000 or 0xFF00), mask-write and FIFO, and +// - deliberately - custom/unknown codes and exception responses, so a dispatcher can still route +// them by function code rather than reject them outright. Tests pin this contract. +bool is_server_pdu_standard(const uint8_t *pdu, size_t size); + +// Client counterpart: additionally checks quantity bounds and address-range arithmetic per function code. +// The same acceptance rule applies to custom/unknown function codes. +bool is_client_pdu_standard(const uint8_t *pdu, size_t size); + +// Remove before 2027.2.0 +ESPDEPRECATED("Use server_pdu_payload() on the response PDU instead. Removed in 2027.2.0", "2026.8.0") inline uint8_t server_frame_data_offset(const uint8_t *frame, size_t size) { if (size < 2) return 0; - switch (static_cast(frame[1])) { - case ModbusFunctionCode::READ_COILS: - case ModbusFunctionCode::READ_DISCRETE_INPUTS: - case ModbusFunctionCode::READ_HOLDING_REGISTERS: - case ModbusFunctionCode::READ_INPUT_REGISTERS: + switch (static_cast(frame[1])) { + case FunctionCode::READ_COILS: + case FunctionCode::READ_DISCRETE_INPUTS: + case FunctionCode::READ_HOLDING_REGISTERS: + case FunctionCode::READ_INPUT_REGISTERS: return 3; // address(1) + function(1) + byte count(1) + data + CRC(2) default: return 2; } } +/** Returns the payload portion of a server response PDU: the bytes after the function code, and for the + * standard read responses (0x01-0x04) also after the byte-count byte. Responses to 0x14/0x17 also carry a + * byte-count byte, but those codes are not implemented and their count byte is left in the payload. For + * an exception PDU the payload is the exception code byte (the read check must not see the masked + * function code, or an exception-of-read would classify as a read and return an empty span). Returns an + * empty span if the PDU is too short. + */ +inline std::span server_pdu_payload(std::span pdu) { + if (pdu.empty()) + return {}; + const size_t offset = (!is_function_code_exception(pdu[0]) && is_function_code_read(pdu[0])) ? 2 : 1; + return pdu.size() > offset ? pdu.subspan(offset) : std::span(); +} + inline uint8_t client_frame_data_offset(const uint8_t *, size_t) { return 2; } enum class SensorValueType : uint8_t { @@ -84,32 +126,32 @@ inline bool value_type_is_float(SensorValueType v) { return v == SensorValueType::FP32 || v == SensorValueType::FP32_R; } -inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { +inline FunctionCode modbus_register_read_function(EntityType reg_type) { switch (reg_type) { - case ModbusRegisterType::COIL: - return ModbusFunctionCode::READ_COILS; - case ModbusRegisterType::DISCRETE_INPUT: - return ModbusFunctionCode::READ_DISCRETE_INPUTS; - case ModbusRegisterType::HOLDING: - return ModbusFunctionCode::READ_HOLDING_REGISTERS; - case ModbusRegisterType::INPUT_REGISTER: - return ModbusFunctionCode::READ_INPUT_REGISTERS; + case EntityType::COIL: + return FunctionCode::READ_COILS; + case EntityType::DISCRETE_INPUT: + return FunctionCode::READ_DISCRETE_INPUTS; + case EntityType::HOLDING: + return FunctionCode::READ_HOLDING_REGISTERS; + case EntityType::INPUT_REGISTER: + return FunctionCode::READ_INPUT_REGISTERS; default: - return ModbusFunctionCode::INVALID; + return FunctionCode::INVALID; } } -inline ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type, bool multiple = false) { +inline FunctionCode modbus_register_write_function(EntityType reg_type, bool multiple = false) { switch (reg_type) { - case ModbusRegisterType::COIL: - return multiple ? ModbusFunctionCode::WRITE_MULTIPLE_COILS : ModbusFunctionCode::WRITE_SINGLE_COIL; - case ModbusRegisterType::HOLDING: - return multiple ? ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS : ModbusFunctionCode::WRITE_SINGLE_REGISTER; + case EntityType::COIL: + return multiple ? FunctionCode::WRITE_MULTIPLE_COILS : FunctionCode::WRITE_SINGLE_COIL; + case EntityType::HOLDING: + return multiple ? FunctionCode::WRITE_MULTIPLE_REGISTERS : FunctionCode::WRITE_SINGLE_REGISTER; // These register types can't be written (per spec) - case ModbusRegisterType::INPUT_REGISTER: - case ModbusRegisterType::DISCRETE_INPUT: + case EntityType::INPUT_REGISTER: + case EntityType::DISCRETE_INPUT: default: - return ModbusFunctionCode::INVALID; + return FunctionCode::INVALID; } } @@ -308,7 +350,24 @@ inline int64_t payload_to_number(const std::vector &data, SensorValueTy */ std::optional registers_to_number(const uint16_t *registers, size_t count, SensorValueType sensor_value_type); -/** Create a modbus clinet pdu for reading/writing single/multiple coils/register/inputs. +// Named PDU buffer types: the builders' storage strategy (currently stack-allocated StaticVector, +// right-sized per shape) can be swapped in one place without touching every signature. +using PduBuffer = StaticVector; +using ReadPdu = StaticVector; +using WriteSinglePdu = StaticVector; + +/** Create a modbus read request PDU. + * @param function_code one of READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS, READ_INPUT_REGISTERS + * @param start_address coil/register/input starting address + * @param number_of_entities number of coils/registers/inputs to read + * @return PDU (function code + data, no address, no CRC); empty on invalid input + */ +ReadPdu create_read_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities); + +/** Create a modbus client pdu for reading/writing single/multiple coils/register/inputs. + * Generic entry point; prefer the direction- and type-specific builders (create_read_pdu(), + * create_write_registers_pdu(), create_write_single_register_pdu(), create_write_coils_pdu(), + * create_write_single_coil_pdu()) which bound their inputs per spec. * @param function_code the modbus function code to use. One of: * READ_COILS * READ_DISCRETE_INPUTS @@ -324,11 +383,59 @@ std::optional registers_to_number(const uint16_t *registers, size_t cou * @param values_len length of values array * @return PDU (function code + data, no address, no CRC) */ -StaticVector create_client_pdu(ModbusFunctionCode function_code, uint16_t start_address, - uint16_t number_of_entities, const uint8_t *values = nullptr, - size_t values_len = 0); +PduBuffer create_client_pdu(FunctionCode function_code, uint16_t start_address, uint16_t number_of_entities, + const uint8_t *values = nullptr, size_t values_len = 0); -inline std::vector float_to_payload(float value, SensorValueType value_type) { +/** Create modbus write multiple registers command + * Function 0x10 Write Multiple Registers + * @param start_address modbus address of the first register to write + * @param values register values to write; the register count is values.size() (at most + * MAX_NUM_OF_REGISTERS_TO_WRITE, an over-long set is rejected and an empty PDU is returned). + * Any contiguous uint16_t container converts (std::vector, std::array). + * @return PDU (function code + data, no address, no CRC) + */ +PduBuffer create_write_registers_pdu(uint16_t start_address, std::span values); + +/** Create modbus write single register command + * Function 0x06 Write Single Register + * @param start_address modbus address of the register to write + * @param value uint16_t value to write + * @return PDU (function code + data, no address, no CRC) + */ +WriteSinglePdu create_write_single_register_pdu(uint16_t start_address, uint16_t value); + +/** Create modbus write single coil command + * Function 0x05 Write Single Coil + * @param address modbus address of the coil to write + * @param value coil value to write + * @return PDU (function code + data, no address, no CRC) + */ +WriteSinglePdu create_write_single_coil_pdu(uint16_t address, bool value); + +/** Create modbus write multiple coils command + * Function 0x0F Write Multiple Coils + * @param start_address modbus address of the first coil to write + * @param values coil values to write; the coil count is values.size() (at most MAX_NUM_OF_COILS_TO_WRITE, an + * over-long set is rejected and an empty PDU is returned). Note std::vector is bit-packed and + * does not convert to a span; pass a std::array or other contiguous bool container. + * @return PDU (function code + data, no address, no CRC) + */ +PduBuffer create_write_coils_pdu(uint16_t start_address, std::span values); + +/** Create modbus write multiple coils command (function 0x0F) from bits packed as on the wire. + * @param start_address modbus address of the first coil to write + * @param bits PackedBits view of the coils to write (at most MAX_NUM_OF_COILS_TO_WRITE); invalid + * input returns an empty PDU + * @return PDU (function code + data, no address, no CRC) + */ +PduBuffer create_write_coils_pdu(uint16_t start_address, PackedBits bits); + +/** Append a float converted to register words to any push_back container (heap-free with StaticVector). + * @param data container the register words are appended to + * @param value value to convert + * @param value_type defines if 16/32/64 bits or FP32 is used + */ +template void float_to_payload(Container &data, float value, SensorValueType value_type) { int64_t val; if (value_type_is_float(value_type)) { @@ -337,8 +444,14 @@ inline std::vector float_to_payload(float value, SensorValueType value val = llroundf(value); } - std::vector data; number_to_payload(data, val, value_type); +} + +// Remove before 2027.2.0 +ESPDEPRECATED("Use the container overload of float_to_payload() instead. Removed in 2027.2.0", "2026.8.0") +inline std::vector float_to_payload(float value, SensorValueType value_type) { + std::vector data; + float_to_payload(data, value, value_type); return data; } diff --git a/esphome/components/modbus_controller/__init__.py b/esphome/components/modbus_controller/__init__.py index 527e9b047f..35a5479ecb 100644 --- a/esphome/components/modbus_controller/__init__.py +++ b/esphome/components/modbus_controller/__init__.py @@ -6,7 +6,7 @@ from esphome.components import modbus from esphome.components.modbus.helpers import ( MODBUS_REGISTER_TYPE, TYPE_REGISTER_MAP, - ModbusRegisterType, + EntityType, ) import esphome.config_validation as cv from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_OFFSET @@ -217,13 +217,13 @@ async def register_modbus_device(var, config): def function_code_to_register(function_code): FUNCTION_CODE_TYPE_MAP = { - "read_coils": ModbusRegisterType.COIL, - "read_discrete_inputs": ModbusRegisterType.DISCRETE_INPUT, - "read_holding_registers": ModbusRegisterType.HOLDING, - "read_input_registers": ModbusRegisterType.INPUT_REGISTER, - "write_single_coil": ModbusRegisterType.COIL, - "write_single_register": ModbusRegisterType.HOLDING, - "write_multiple_coils": ModbusRegisterType.COIL, - "write_multiple_registers": ModbusRegisterType.HOLDING, + "read_coils": EntityType.COIL, + "read_discrete_inputs": EntityType.DISCRETE_INPUT, + "read_holding_registers": EntityType.HOLDING, + "read_input_registers": EntityType.INPUT_REGISTER, + "write_single_coil": EntityType.COIL, + "write_single_register": EntityType.HOLDING, + "write_multiple_coils": EntityType.COIL, + "write_multiple_registers": EntityType.HOLDING, } return FUNCTION_CODE_TYPE_MAP[function_code] diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp index 9656013a5f..d3caaaa3d9 100644 --- a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.cpp @@ -11,8 +11,8 @@ void ModbusBinarySensor::parse_and_publish(const std::vector &data) { bool value; switch (this->register_type) { - case ModbusRegisterType::DISCRETE_INPUT: - case ModbusRegisterType::COIL: + case modbus::EntityType::DISCRETE_INPUT: + case modbus::EntityType::COIL: // offset for coil is the actual number of the coil not the byte offset value = modbus::helpers::bit_from_packed(this->offset, data); break; diff --git a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h index 3f7c6b4dd6..f56a32a5ec 100644 --- a/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h +++ b/esphome/components/modbus_controller/binary_sensor/modbus_binarysensor.h @@ -10,7 +10,7 @@ namespace esphome::modbus_controller { class ModbusBinarySensor final : public Component, public binary_sensor::BinarySensor, public SensorItem { public: - ModbusBinarySensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + ModbusBinarySensor(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, uint16_t skip_updates, bool force_new_range) { this->register_type = register_type; this->start_address = start_address; @@ -20,7 +20,7 @@ class ModbusBinarySensor final : public Component, public binary_sensor::BinaryS this->skip_updates = skip_updates; this->force_new_range = force_new_range; - if (register_type == ModbusRegisterType::COIL || register_type == ModbusRegisterType::DISCRETE_INPUT) { + if (register_type == modbus::EntityType::COIL || register_type == modbus::EntityType::DISCRETE_INPUT) { this->register_count = offset + 1; } else { this->register_count = 1; diff --git a/esphome/components/modbus_controller/modbus_controller.cpp b/esphome/components/modbus_controller/modbus_controller.cpp index 9246239ef9..8822b7b40a 100644 --- a/esphome/components/modbus_controller/modbus_controller.cpp +++ b/esphome/components/modbus_controller/modbus_controller.cpp @@ -57,7 +57,7 @@ bool ModbusController::send_next_command_() { } // Queue incoming response -void ModbusController::on_modbus_data(const std::vector &data) { +void ModbusController::on_response(std::span request_pdu, std::span response_pdu) { if (this->command_queue_.empty()) { ESP_LOGW(TAG, "Received modbus data but command queue is empty"); return; @@ -78,8 +78,10 @@ void ModbusController::on_modbus_data(const std::vector &data) { this->online_callback_.call((int) current_command->function_code, current_command->register_address); } - // Move the commandItem to the response queue - current_command->payload = data; + // Move the commandItem to the response queue. The span points into the hub's receive buffer, so + // copy the payload into the command for deferred processing in loop(). + auto data = modbus::helpers::server_pdu_payload(response_pdu); + current_command->payload.assign(data.begin(), data.end()); this->incoming_queue_.push(std::move(current_command)); ESP_LOGV(TAG, "Modbus response queued"); this->command_queue_.pop_front(); @@ -93,8 +95,11 @@ void ModbusController::process_modbus_data_(const ModbusCommandItem *response) { response->on_data_func(response->register_type, response->register_address, response->payload); } -void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_code) { - ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, exception_code); +void ModbusController::on_error(std::span request_pdu, modbus::ExceptionCode exception_code) { + // The request function code (request_pdu[0]) already carries what the log needs; the exception bit only + // ever appears on the response, so no masking is needed here. + const uint8_t function_code = request_pdu.empty() ? 0 : request_pdu[0]; + ESP_LOGE(TAG, "Modbus error function code: 0x%X exception: %d ", function_code, static_cast(exception_code)); if (this->command_queue_.empty()) { return; } @@ -111,7 +116,7 @@ void ModbusController::on_modbus_error(uint8_t function_code, uint8_t exception_ } } -SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const { +SensorSet ModbusController::find_sensors_(modbus::EntityType register_type, uint16_t start_address) const { auto reg_it = std::find_if( std::begin(this->register_ranges_), std::end(this->register_ranges_), [=](RegisterRange const &r) { return (r.start_address == start_address && r.register_type == register_type); }); @@ -125,7 +130,7 @@ SensorSet ModbusController::find_sensors_(ModbusRegisterType register_type, uint // not found return {}; } -void ModbusController::on_register_data(ModbusRegisterType register_type, uint16_t start_address, +void ModbusController::on_register_data(modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { ESP_LOGV(TAG, "data for register address : 0x%X : ", start_address); @@ -159,18 +164,18 @@ void ModbusController::update_range_(RegisterRange &r) { r.skip_updates_counter); if (r.skip_updates_counter == 0) { // if a custom command is used the user supplied custom_data is only available in the SensorItem. - if (r.register_type == ModbusRegisterType::CUSTOM) { + if (r.register_type == modbus::EntityType::CUSTOM) { auto sensors = this->find_sensors_(r.register_type, r.start_address); if (!sensors.empty()) { auto sensor = sensors.cbegin(); auto command_item = ModbusCommandItem::create_custom_command( this, (*sensor)->custom_data, - [this](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { - this->on_register_data(ModbusRegisterType::CUSTOM, start_address, data); + [this](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { + this->on_register_data(modbus::EntityType::CUSTOM, start_address, data); }); command_item.register_address = (*sensor)->start_address; command_item.register_count = (*sensor)->register_count; - command_item.function_code = ModbusFunctionCode::CUSTOM; + command_item.function_code = FunctionCode::CUSTOM; queue_command(command_item); } } else { @@ -232,7 +237,7 @@ size_t ModbusController::create_register_ranges_() { // this is not the first register in range so it might be possible // to reuse the last register or extend the current range if (!curr->force_new_range && r.register_type == curr->register_type && - curr->register_type != ModbusRegisterType::CUSTOM) { + curr->register_type != modbus::EntityType::CUSTOM) { if (curr->start_address == (r.start_address + r.register_count - prev->register_count) && curr->register_count == prev->register_count && curr->get_register_size() == prev->get_register_size()) { // this register can re-use the data from the previous register @@ -342,7 +347,7 @@ void ModbusController::loop() { } } -void ModbusController::on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, +void ModbusController::on_write_register_response(modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { ESP_LOGV(TAG, "Command ACK 0x%X %d ", modbus::helpers::get_data(data, 0), modbus::helpers::get_data(data, 1)); @@ -357,8 +362,8 @@ void ModbusController::dump_sensors_() { } ModbusCommandItem ModbusCommandItem::create_read_command( - ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count, - std::function &data)> + ModbusController *modbusdevice, modbus::EntityType register_type, uint16_t start_address, uint16_t register_count, + std::function &data)> &&handler) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; @@ -371,7 +376,7 @@ ModbusCommandItem ModbusCommandItem::create_read_command( } ModbusCommandItem ModbusCommandItem::create_read_command(ModbusController *modbusdevice, - ModbusRegisterType register_type, uint16_t start_address, + modbus::EntityType register_type, uint16_t start_address, uint16_t register_count) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; @@ -379,7 +384,7 @@ ModbusCommandItem ModbusCommandItem::create_read_command(ModbusController *modbu cmd.function_code = modbus::helpers::modbus_register_read_function(register_type); cmd.register_address = start_address; cmd.register_count = register_count; - cmd.on_data_func = [modbusdevice](ModbusRegisterType register_type, uint16_t start_address, + cmd.on_data_func = [modbusdevice](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { modbusdevice->on_register_data(register_type, start_address, data); }; @@ -391,11 +396,11 @@ ModbusCommandItem ModbusCommandItem::create_write_multiple_command(ModbusControl const std::vector &values) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; - cmd.register_type = ModbusRegisterType::HOLDING; - cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS; + cmd.register_type = modbus::EntityType::HOLDING; + cmd.function_code = FunctionCode::WRITE_MULTIPLE_REGISTERS; cmd.register_address = start_address; cmd.register_count = register_count; - cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + cmd.on_data_func = [modbusdevice, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { modbusdevice->on_write_register_response(cmd.register_type, start_address, data); }; @@ -411,11 +416,11 @@ ModbusCommandItem ModbusCommandItem::create_write_single_coil(ModbusController * bool value) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; - cmd.register_type = ModbusRegisterType::COIL; - cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_COIL; + cmd.register_type = modbus::EntityType::COIL; + cmd.function_code = FunctionCode::WRITE_SINGLE_COIL; cmd.register_address = address; cmd.register_count = 1; - cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + cmd.on_data_func = [modbusdevice, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { modbusdevice->on_write_register_response(cmd.register_type, start_address, data); }; @@ -428,11 +433,11 @@ ModbusCommandItem ModbusCommandItem::create_write_multiple_coils(ModbusControlle const std::vector &values) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; - cmd.register_type = ModbusRegisterType::COIL; - cmd.function_code = ModbusFunctionCode::WRITE_MULTIPLE_COILS; + cmd.register_type = modbus::EntityType::COIL; + cmd.function_code = FunctionCode::WRITE_MULTIPLE_COILS; cmd.register_address = start_address; cmd.register_count = values.size(); - cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + cmd.on_data_func = [modbusdevice, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { modbusdevice->on_write_register_response(cmd.register_type, start_address, data); }; @@ -460,11 +465,11 @@ ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusControlle uint16_t value) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; - cmd.register_type = ModbusRegisterType::HOLDING; - cmd.function_code = ModbusFunctionCode::WRITE_SINGLE_REGISTER; + cmd.register_type = modbus::EntityType::HOLDING; + cmd.function_code = FunctionCode::WRITE_SINGLE_REGISTER; cmd.register_address = start_address; cmd.register_count = 1; // not used here anyways - cmd.on_data_func = [modbusdevice, cmd](ModbusRegisterType register_type, uint16_t start_address, + cmd.on_data_func = [modbusdevice, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { modbusdevice->on_write_register_response(cmd.register_type, start_address, data); }; @@ -477,13 +482,13 @@ ModbusCommandItem ModbusCommandItem::create_write_single_command(ModbusControlle ModbusCommandItem ModbusCommandItem::create_custom_command( ModbusController *modbusdevice, const std::vector &values, - std::function &data)> + std::function &data)> &&handler) { ModbusCommandItem cmd; cmd.modbusdevice = modbusdevice; - cmd.function_code = ModbusFunctionCode::CUSTOM; + cmd.function_code = FunctionCode::CUSTOM; if (handler == nullptr) { - cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { + cmd.on_data_func = [](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { ESP_LOGI(TAG, "Custom Command sent"); }; } else { @@ -496,13 +501,13 @@ ModbusCommandItem ModbusCommandItem::create_custom_command( ModbusCommandItem ModbusCommandItem::create_custom_command( ModbusController *modbusdevice, const std::vector &values, - std::function &data)> + std::function &data)> &&handler) { ModbusCommandItem cmd = {}; cmd.modbusdevice = modbusdevice; - cmd.function_code = ModbusFunctionCode::CUSTOM; + cmd.function_code = FunctionCode::CUSTOM; if (handler == nullptr) { - cmd.on_data_func = [](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { + cmd.on_data_func = [](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { ESP_LOGI(TAG, "Custom Command sent"); }; } else { @@ -517,9 +522,10 @@ ModbusCommandItem ModbusCommandItem::create_custom_command( } bool ModbusCommandItem::send() { - if (this->function_code != ModbusFunctionCode::CUSTOM) { - modbusdevice->send(uint8_t(this->function_code), this->register_address, this->register_count, this->payload.size(), - this->payload.empty() ? nullptr : &this->payload[0]); + if (this->function_code != FunctionCode::CUSTOM) { + modbusdevice->send_pdu( + modbus::helpers::create_client_pdu(this->function_code, this->register_address, this->register_count, + this->payload.empty() ? nullptr : &this->payload[0], this->payload.size())); } else { modbusdevice->send_raw(this->payload); } @@ -532,7 +538,7 @@ bool ModbusCommandItem::send() { bool ModbusCommandItem::is_equal(const ModbusCommandItem &other) { // for custom commands we have to check for identical payloads, since // address/count/type fields will be set to zero - return this->function_code == ModbusFunctionCode::CUSTOM + return this->function_code == FunctionCode::CUSTOM ? this->payload == other.payload : other.register_address == this->register_address && other.register_count == this->register_count && other.register_type == this->register_type && other.function_code == this->function_code; diff --git a/esphome/components/modbus_controller/modbus_controller.h b/esphome/components/modbus_controller/modbus_controller.h index 484b59ede3..928054f1ab 100644 --- a/esphome/components/modbus_controller/modbus_controller.h +++ b/esphome/components/modbus_controller/modbus_controller.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -16,22 +17,29 @@ namespace esphome::modbus_controller { class ModbusController; +using modbus::ExceptionCode; +using modbus::FunctionCode; +using modbus::helpers::SensorValueType; + +// Remove before 2027.2.0 - deprecated names re-exported so external components keep their warning window +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +using modbus::ModbusExceptionCode; using modbus::ModbusFunctionCode; using modbus::ModbusRegisterType; -using modbus::ModbusExceptionCode; -using modbus::helpers::SensorValueType; +#pragma GCC diagnostic pop // Remove before 2026.10.0 — these helpers have moved to modbus::helpers ESPDEPRECATED("Use modbus::helpers::value_type_is_float() instead. Removed in 2026.10.0", "2026.4.0") inline bool value_type_is_float(SensorValueType v) { return modbus::helpers::value_type_is_float(v); } ESPDEPRECATED("Use modbus::helpers::modbus_register_read_function() instead. Removed in 2026.10.0", "2026.4.0") -inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { +inline FunctionCode modbus_register_read_function(modbus::EntityType reg_type) { return modbus::helpers::modbus_register_read_function(reg_type); } ESPDEPRECATED("Use modbus::helpers::modbus_register_write_function() instead. Removed in 2026.10.0", "2026.4.0") -inline ModbusFunctionCode modbus_register_write_function(ModbusRegisterType reg_type) { +inline FunctionCode modbus_register_write_function(modbus::EntityType reg_type) { return modbus::helpers::modbus_register_write_function(reg_type); } @@ -90,7 +98,9 @@ inline int64_t payload_to_number(const std::vector &data, SensorValueTy ESPDEPRECATED("Use modbus::helpers::float_to_payload() instead. Removed in 2026.10.0", "2026.4.0") inline std::vector float_to_payload(float value, SensorValueType value_type) { - return modbus::helpers::float_to_payload(value, value_type); + std::vector data; + modbus::helpers::float_to_payload(data, value, value_type); + return data; } class ModbusController; @@ -101,7 +111,7 @@ class SensorItem { void set_custom_data(const std::vector &data) { custom_data = data; } size_t virtual get_register_size() const { - if (register_type == ModbusRegisterType::COIL || register_type == ModbusRegisterType::DISCRETE_INPUT) { + if (register_type == modbus::EntityType::COIL || register_type == modbus::EntityType::DISCRETE_INPUT) { return 1; } else { // if CONF_RESPONSE_BYTES is used override the default return response_bytes > 0 ? response_bytes : register_count * 2; @@ -109,7 +119,7 @@ class SensorItem { } // Override register size for modbus devices not using 1 register for one dword void set_register_size(uint8_t register_size) { response_bytes = register_size; } - ModbusRegisterType register_type{ModbusRegisterType::CUSTOM}; + modbus::EntityType register_type{modbus::EntityType::CUSTOM}; SensorValueType sensor_value_type{SensorValueType::RAW}; uint16_t start_address{0}; uint32_t bitmask{0}; @@ -156,7 +166,7 @@ using SensorSet = std::set; struct RegisterRange { uint16_t start_address; - ModbusRegisterType register_type; + modbus::EntityType register_type; uint8_t register_count; uint16_t skip_updates; // the config value SensorSet sensors; // all sensors of this range @@ -169,9 +179,9 @@ class ModbusCommandItem { ModbusController *modbusdevice{nullptr}; uint16_t register_address{0}; uint16_t register_count{0}; - ModbusFunctionCode function_code{ModbusFunctionCode::CUSTOM}; - ModbusRegisterType register_type{ModbusRegisterType::CUSTOM}; - std::function &data)> + FunctionCode function_code{FunctionCode::CUSTOM}; + modbus::EntityType register_type{modbus::EntityType::CUSTOM}; + std::function &data)> on_data_func; std::vector payload = {}; bool send(); @@ -189,8 +199,8 @@ class ModbusCommandItem { * @return ModbusCommandItem with the prepared command */ static ModbusCommandItem create_read_command( - ModbusController *modbusdevice, ModbusRegisterType register_type, uint16_t start_address, uint16_t register_count, - std::function &data)> + ModbusController *modbusdevice, modbus::EntityType register_type, uint16_t start_address, uint16_t register_count, + std::function &data)> &&handler); /** Create modbus read command * Function code 02-04 @@ -200,7 +210,7 @@ class ModbusCommandItem { * @param register_count number of registers to read * @return ModbusCommandItem with the prepared command */ - static ModbusCommandItem create_read_command(ModbusController *modbusdevice, ModbusRegisterType register_type, + static ModbusCommandItem create_read_command(ModbusController *modbusdevice, modbus::EntityType register_type, uint16_t start_address, uint16_t register_count); /** Create modbus read command * Function code 02-04 @@ -250,7 +260,7 @@ class ModbusCommandItem { */ static ModbusCommandItem create_custom_command( ModbusController *modbusdevice, const std::vector &values, - std::function &data)> + std::function &data)> &&handler = nullptr); /** Create custom modbus command @@ -262,7 +272,7 @@ class ModbusCommandItem { */ static ModbusCommandItem create_custom_command( ModbusController *modbusdevice, const std::vector &values, - std::function &data)> + std::function &data)> &&handler = nullptr); bool is_equal(const ModbusCommandItem &other); @@ -290,17 +300,29 @@ class ModbusController final : public PollingComponent, public modbus::ModbusCli /// queues a modbus command in the send queue void queue_command(const ModbusCommandItem &command); + /// Sends a raw payload (address byte + PDU, no CRC) with responses routed back to this controller. + /// The payload carries its own address byte, which may differ from this controller's address. + /// Deliberately shadows the deprecated ModbusClientDevice::send_raw() with identical semantics: + /// controller-level raw sends stay supported until the command machinery is replaced. + void send_raw(const std::vector &payload) { + if (payload.empty()) { + // Through the guard like every other delivery, so a handler calling send_raw({}) cannot recurse. + this->trigger_not_sent({}); + return; + } + this->parent_->send_pdu(payload[0], std::span(payload).subspan(1), this); + } /// Registers a sensor with the controller. Called by esphomes code generator void add_sensor_item(SensorItem *item) { sensorset_.insert(item); } /// called when a modbus response was parsed without errors - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; /// called when a modbus error response was received - void on_modbus_error(uint8_t function_code, uint8_t exception_code) override; + void on_error(std::span request_pdu, modbus::ExceptionCode exception_code) override; /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue - void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector &data); + void on_register_data(modbus::EntityType register_type, uint16_t start_address, const std::vector &data); /// default delegate called by process_modbus_data when a response for a write response has retrieved from the /// incoming queue - void on_write_register_response(ModbusRegisterType register_type, uint16_t start_address, + void on_write_register_response(modbus::EntityType register_type, uint16_t start_address, const std::vector &data); /// Allow a duplicate command to be sent void set_allow_duplicate_commands(bool allow_duplicate_commands) { @@ -337,7 +359,7 @@ class ModbusController final : public PollingComponent, public modbus::ModbusCli /// parse sensormap_ and create range of sequential addresses size_t create_register_ranges_(); // find register in sensormap. Returns iterator with all registers having the same start address - SensorSet find_sensors_(ModbusRegisterType register_type, uint16_t start_address) const; + SensorSet find_sensors_(modbus::EntityType register_type, uint16_t start_address) const; /// submit the read command for the address range to the send queue void update_range_(RegisterRange &r); /// parse incoming modbus data diff --git a/esphome/components/modbus_controller/number/modbus_number.cpp b/esphome/components/modbus_controller/number/modbus_number.cpp index 2c81dd6830..7b18b9e9fc 100644 --- a/esphome/components/modbus_controller/number/modbus_number.cpp +++ b/esphome/components/modbus_controller/number/modbus_number.cpp @@ -57,11 +57,12 @@ void ModbusNumber::control(float value) { format_hex_pretty_to(hex_buf, sizeof(hex_buf), data.data(), data.size())); write_cmd = ModbusCommandItem::create_custom_command( this->parent_, data, - [this, write_cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { + [this, write_cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { this->parent_->on_write_register_response(write_cmd.register_type, this->start_address, data); }); } else { - data = modbus::helpers::float_to_payload(write_value, this->sensor_value_type); + std::vector payload; + modbus::helpers::float_to_payload(payload, write_value, this->sensor_value_type); ESP_LOGD(TAG, "Updating register: connected Sensor=%s start address=0x%X register count=%d new value=%.02f (val=%.02f)", @@ -71,13 +72,13 @@ void ModbusNumber::control(float value) { if (this->register_count == 1 && !this->use_write_multiple_) { // since offset is in bytes and a register is 16 bits we get the start by adding offset/2 write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset / 2, - data[0]); + payload[0]); } else { write_cmd = ModbusCommandItem::create_write_multiple_command( - this->parent_, this->start_address + this->offset / 2, this->register_count, data); + this->parent_, this->start_address + this->offset / 2, this->register_count, payload); } // publish new value - write_cmd.on_data_func = [this, write_cmd, value](ModbusRegisterType register_type, uint16_t start_address, + write_cmd.on_data_func = [this, write_cmd, value](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { // gets called when the write command is ack'd from the device this->parent_->on_write_register_response(write_cmd.register_type, start_address, data); diff --git a/esphome/components/modbus_controller/number/modbus_number.h b/esphome/components/modbus_controller/number/modbus_number.h index ce64099170..582b042caf 100644 --- a/esphome/components/modbus_controller/number/modbus_number.h +++ b/esphome/components/modbus_controller/number/modbus_number.h @@ -12,7 +12,7 @@ using value_to_data_t = std::function(float); class ModbusNumber final : public number::Number, public Component, public SensorItem { public: - ModbusNumber(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + ModbusNumber(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, uint16_t skip_updates, bool force_new_range) { this->register_type = register_type; this->start_address = start_address; diff --git a/esphome/components/modbus_controller/output/modbus_output.cpp b/esphome/components/modbus_controller/output/modbus_output.cpp index 504e09a093..95618a7505 100644 --- a/esphome/components/modbus_controller/output/modbus_output.cpp +++ b/esphome/components/modbus_controller/output/modbus_output.cpp @@ -33,12 +33,30 @@ void ModbusFloatOutput::write_state(float value) { } // lambda didn't set payload if (data.empty()) { - data = modbus::helpers::float_to_payload(value, this->sensor_value_type); + modbus::helpers::float_to_payload(data, value, this->sensor_value_type); } ESP_LOGD(TAG, "Updating register: start address=0x%X register count=%d new value=%.02f (val=%.02f)", this->start_address, this->register_count, value, original_value); + // The command declares register_count registers, so the payload must be exactly that many words; + // anything else would put a byte count on the wire that disagrees with the quantity field. + // number_to_payload() appends nothing for RAW, so an empty payload must be caught before data[0]. + if (data.empty()) { + ESP_LOGW(TAG, "No payload was created for updating output"); + return; + } + + // register_count declares the READ range width - it may pull neighboring registers into one poll - + // so a write covers exactly the registers the value occupies: the quantity comes from the payload, + // never from register_count (padding to it would zero registers the user only declared for reading). + // A payload wider than the declared range means the config and the lambda disagree - drop it. + if (data.size() > this->register_count) { + ESP_LOGE(TAG, "Payload has %zu registers but register_count is %u; dropping write", data.size(), + this->register_count); + return; + } + // Create and send the write command ModbusCommandItem write_cmd; if (this->register_count == 1 && !this->use_write_multiple_) { @@ -46,7 +64,7 @@ void ModbusFloatOutput::write_state(float value) { ModbusCommandItem::create_write_single_command(this->parent_, this->start_address + this->offset, data[0]); } else { write_cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, this->start_address + this->offset, - this->register_count, data); + data.size(), data); } this->parent_->queue_command(write_cmd); } @@ -89,7 +107,7 @@ void ModbusBinaryOutput::write_state(bool state) { format_hex_pretty_to(hex_buf, sizeof(hex_buf), data.data(), data.size())); cmd = ModbusCommandItem::create_custom_command( this->parent_, data, - [this, cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { + [this, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { this->parent_->on_write_register_response(cmd.register_type, this->start_address, data); }); } else { diff --git a/esphome/components/modbus_controller/output/modbus_output.h b/esphome/components/modbus_controller/output/modbus_output.h index d904e58bd7..c9efd42224 100644 --- a/esphome/components/modbus_controller/output/modbus_output.h +++ b/esphome/components/modbus_controller/output/modbus_output.h @@ -11,7 +11,7 @@ namespace esphome::modbus_controller { class ModbusFloatOutput final : public output::FloatOutput, public Component, public SensorItem { public: ModbusFloatOutput(uint16_t start_address, uint8_t offset, SensorValueType value_type, int register_count) { - this->register_type = ModbusRegisterType::HOLDING; + this->register_type = modbus::EntityType::HOLDING; this->start_address = start_address; this->offset = offset; this->bitmask = 0xFFFFFFFF; @@ -44,7 +44,7 @@ class ModbusFloatOutput final : public output::FloatOutput, public Component, pu class ModbusBinaryOutput final : public output::BinaryOutput, public Component, public SensorItem { public: ModbusBinaryOutput(uint16_t start_address, uint8_t offset) { - this->register_type = ModbusRegisterType::COIL; + this->register_type = modbus::EntityType::COIL; this->start_address = start_address; this->bitmask = 0xFFFFFFFF; this->sensor_value_type = SensorValueType::BIT; diff --git a/esphome/components/modbus_controller/select/modbus_select.cpp b/esphome/components/modbus_controller/select/modbus_select.cpp index c650ca7641..daa6b10da4 100644 --- a/esphome/components/modbus_controller/select/modbus_select.cpp +++ b/esphome/components/modbus_controller/select/modbus_select.cpp @@ -72,13 +72,26 @@ void ModbusSelect::control(size_t index) { return; } + // The command declares register_count registers, so the payload must be exactly that many words: + // a value type narrower than the declared width is zero-padded (the config deliberately allows + // register_count larger than the value type). Anything else would put a byte count on the wire + // that disagrees with the quantity field, which conformant devices reject. + // register_count declares the READ range width - it may pull neighboring registers into one poll - + // so a write covers exactly the registers the value occupies: the quantity comes from the payload, + // never from register_count (padding to it would zero registers the user only declared for reading). + // A payload wider than the declared range means the config and the lambda disagree - drop it. + if (data.size() > this->register_count) { + ESP_LOGE(TAG, "Payload has %zu registers but register_count is %u; dropping write", data.size(), + this->register_count); + return; + } + const uint16_t write_address = this->start_address + this->offset / 2; ModbusCommandItem write_cmd; if ((this->register_count == 1) && (!this->use_write_multiple_)) { write_cmd = ModbusCommandItem::create_write_single_command(this->parent_, write_address, data[0]); } else { - write_cmd = - ModbusCommandItem::create_write_multiple_command(this->parent_, write_address, this->register_count, data); + write_cmd = ModbusCommandItem::create_write_multiple_command(this->parent_, write_address, data.size(), data); } this->parent_->queue_command(write_cmd); diff --git a/esphome/components/modbus_controller/select/modbus_select.h b/esphome/components/modbus_controller/select/modbus_select.h index fb9283305c..b4834ba4c6 100644 --- a/esphome/components/modbus_controller/select/modbus_select.h +++ b/esphome/components/modbus_controller/select/modbus_select.h @@ -13,7 +13,7 @@ class ModbusSelect final : public Component, public select::Select, public Senso public: ModbusSelect(SensorValueType sensor_value_type, uint16_t start_address, uint8_t register_count, uint16_t skip_updates, bool force_new_range, std::vector mapping) { - this->register_type = ModbusRegisterType::HOLDING; // not configurable + this->register_type = modbus::EntityType::HOLDING; // not configurable this->sensor_value_type = sensor_value_type; this->start_address = start_address; this->offset = 0; // not configurable diff --git a/esphome/components/modbus_controller/sensor/modbus_sensor.h b/esphome/components/modbus_controller/sensor/modbus_sensor.h index ea4f560b9c..1d11aa4d66 100644 --- a/esphome/components/modbus_controller/sensor/modbus_sensor.h +++ b/esphome/components/modbus_controller/sensor/modbus_sensor.h @@ -10,7 +10,7 @@ namespace esphome::modbus_controller { class ModbusSensor final : public Component, public sensor::Sensor, public SensorItem { public: - ModbusSensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + ModbusSensor(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, SensorValueType value_type, int register_count, uint16_t skip_updates, bool force_new_range) { this->register_type = register_type; this->start_address = start_address; diff --git a/esphome/components/modbus_controller/switch/modbus_switch.cpp b/esphome/components/modbus_controller/switch/modbus_switch.cpp index c8b3868bdc..b8cdbf018d 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.cpp +++ b/esphome/components/modbus_controller/switch/modbus_switch.cpp @@ -30,8 +30,8 @@ bool ModbusSwitch::assumed_state() { return this->assumed_state_; } void ModbusSwitch::parse_and_publish(const std::vector &data) { bool value = false; switch (this->register_type) { - case ModbusRegisterType::DISCRETE_INPUT: - case ModbusRegisterType::COIL: + case modbus::EntityType::DISCRETE_INPUT: + case modbus::EntityType::COIL: // offset for coil is the actual number of the coil not the byte offset value = modbus::helpers::bit_from_packed(this->offset, data); break; @@ -82,13 +82,13 @@ void ModbusSwitch::write_state(bool state) { format_hex_pretty_to(hex_buf, sizeof(hex_buf), data.data(), data.size())); cmd = ModbusCommandItem::create_custom_command( this->parent_, data, - [this, cmd](ModbusRegisterType register_type, uint16_t start_address, const std::vector &data) { + [this, cmd](modbus::EntityType register_type, uint16_t start_address, const std::vector &data) { this->parent_->on_write_register_response(cmd.register_type, this->start_address, data); }); } else { ESP_LOGV(TAG, "write_state '%s': new value = %s type = %d address = %X offset = %x", this->get_name().c_str(), ONOFF(state), (int) this->register_type, this->start_address, this->offset); - if (this->register_type == ModbusRegisterType::COIL) { + if (this->register_type == modbus::EntityType::COIL) { // offset for coil and discrete inputs is the coil/register number not bytes if (this->use_write_multiple_) { std::vector states{state}; diff --git a/esphome/components/modbus_controller/switch/modbus_switch.h b/esphome/components/modbus_controller/switch/modbus_switch.h index d6e991582d..0d5456aa63 100644 --- a/esphome/components/modbus_controller/switch/modbus_switch.h +++ b/esphome/components/modbus_controller/switch/modbus_switch.h @@ -10,7 +10,7 @@ namespace esphome::modbus_controller { class ModbusSwitch final : public Component, public switch_::Switch, public SensorItem { public: - ModbusSwitch(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, + ModbusSwitch(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint32_t bitmask, uint16_t skip_updates, bool force_new_range) { this->register_type = register_type; this->start_address = start_address; @@ -19,7 +19,7 @@ class ModbusSwitch final : public Component, public switch_::Switch, public Sens this->sensor_value_type = SensorValueType::BIT; this->skip_updates = skip_updates; this->register_count = 1; - if (register_type == ModbusRegisterType::HOLDING || register_type == ModbusRegisterType::COIL) { + if (register_type == modbus::EntityType::HOLDING || register_type == modbus::EntityType::COIL) { this->start_address += offset; this->offset = 0; } diff --git a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h index e9130c98d4..c7381d7ddd 100644 --- a/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h +++ b/esphome/components/modbus_controller/text_sensor/modbus_textsensor.h @@ -12,7 +12,7 @@ enum class RawEncoding { NONE = 0, HEXBYTES = 1, COMMA = 2, ANSI = 3 }; class ModbusTextSensor final : public Component, public text_sensor::TextSensor, public SensorItem { public: - ModbusTextSensor(ModbusRegisterType register_type, uint16_t start_address, uint8_t offset, uint8_t register_count, + ModbusTextSensor(modbus::EntityType register_type, uint16_t start_address, uint8_t offset, uint8_t register_count, uint16_t response_bytes, RawEncoding encode, uint16_t skip_updates, bool force_new_range) { this->register_type = register_type; this->start_address = start_address; diff --git a/esphome/components/modbus_server/modbus_server.cpp b/esphome/components/modbus_server/modbus_server.cpp index 4c4e72a086..e649635848 100644 --- a/esphome/components/modbus_server/modbus_server.cpp +++ b/esphome/components/modbus_server/modbus_server.cpp @@ -3,7 +3,7 @@ #include "esphome/core/log.h" namespace esphome::modbus_server { -using modbus::ModbusExceptionCode; +using modbus::ExceptionCode; using modbus::helpers::registers_to_number; static const char *const TAG = "modbus_server"; @@ -50,13 +50,13 @@ modbus::ResponseStatus ModbusServer::on_read_registers(uint16_t start_address, u } ESP_LOGW(TAG, "No register at 0x%04X and courtesy default not allowed. Sending exception response.", static_cast(current_address)); - return ModbusExceptionCode::ILLEGAL_DATA_ADDRESS; + return ExceptionCode::ILLEGAL_DATA_ADDRESS; } if (!server_register->read_lambda) { // Registered but not readable (write-only); don't mask it with the courtesy default. ESP_LOGW(TAG, "Register at 0x%04X is not readable. Sending exception response.", server_register->address); - return ModbusExceptionCode::ILLEGAL_DATA_ADDRESS; + return ExceptionCode::ILLEGAL_DATA_ADDRESS; } // A multi-register value is normally atomic: the request must start at its first register and cover all of @@ -72,7 +72,7 @@ modbus::ResponseStatus ModbusServer::on_read_registers(uint16_t start_address, u "Read clips the multi-register value at 0x%04X, which does not allow partial reads. " "Sending exception response.", server_register->address); - return ModbusExceptionCode::ILLEGAL_DATA_ADDRESS; + return ExceptionCode::ILLEGAL_DATA_ADDRESS; } int64_t value = server_register->read_lambda(); @@ -89,7 +89,7 @@ modbus::ResponseStatus ModbusServer::on_read_registers(uint16_t start_address, u // The value encoded to fewer words than its register span (e.g. a RAW register); treat as a device fault. ESP_LOGE(TAG, "Register at 0x%04X did not encode to %u registers", server_register->address, server_register->register_count); - return ModbusExceptionCode::SERVICE_DEVICE_FAILURE; + return ExceptionCode::SERVICE_DEVICE_FAILURE; } for (uint16_t i = 0; i < take; i++) { registers.push_back(value_words[value_offset + i]); @@ -132,7 +132,7 @@ modbus::ResponseStatus ModbusServer::on_write_registers(uint16_t start_address, // so we never apply a partial write before discovering a problem. The commit pass below re-runs // registers_to_number rather than caching the decoded values: using the same function for the check and // the write keeps a single source of truth for the decode bound, independent of how register_count was set. - ModbusExceptionCode precheck = ModbusExceptionCode::ILLEGAL_DATA_ADDRESS; // unmatched or unwritable register + ExceptionCode precheck = ExceptionCode::ILLEGAL_DATA_ADDRESS; // unmatched or unwritable register if (!for_each_register([&precheck, ®isters](ServerRegister *server_register, uint16_t register_offset) -> bool { if (server_register->write_lambda == nullptr) { return false; // unwritable -> ILLEGAL_DATA_ADDRESS @@ -140,7 +140,7 @@ modbus::ResponseStatus ModbusServer::on_write_registers(uint16_t start_address, if (!registers_to_number(registers.data() + register_offset, registers.size() - register_offset, server_register->value_type) .has_value()) { - precheck = ModbusExceptionCode::ILLEGAL_DATA_VALUE; // request doesn't supply the full value + precheck = ExceptionCode::ILLEGAL_DATA_VALUE; // request doesn't supply the full value return false; } return true; @@ -158,7 +158,7 @@ modbus::ResponseStatus ModbusServer::on_write_registers(uint16_t start_address, return server_register->write_lambda(number); })) { ESP_LOGW(TAG, "A register write callback failed mid-sequence; earlier writes were already applied."); - return ModbusExceptionCode::SERVICE_DEVICE_FAILURE; + return ExceptionCode::SERVICE_DEVICE_FAILURE; } // Success: the caller builds the write response (an echo of the request header). diff --git a/esphome/components/network/__init__.py b/esphome/components/network/__init__.py index 0f4bcb3e16..24e9aa45e1 100644 --- a/esphome/components/network/__init__.py +++ b/esphome/components/network/__init__.py @@ -1,13 +1,20 @@ import ipaddress import logging +from typing import Any import esphome.codegen as cg from esphome.components.esp32 import add_idf_sdkconfig_option from esphome.components.psram import is_guaranteed as psram_is_guaranteed from esphome.components.zephyr import zephyr_add_prj_conf import esphome.config_validation as cv -from esphome.const import CONF_ENABLE_IPV6, CONF_ID, CONF_MIN_IPV6_ADDR_COUNT +from esphome.const import ( + CONF_ENABLE_IPV6, + CONF_ID, + CONF_MIN_IPV6_ADDR_COUNT, + CONF_PRIORITY, +) from esphome.core import CORE, CoroPriority, coroutine_with_priority +import esphome.final_validate as fv from esphome.types import ConfigType CODEOWNERS = ["@esphome/core"] @@ -20,6 +27,48 @@ _LOGGER = logging.getLogger(__name__) KEY_HIGH_PERFORMANCE_NETWORKING = "high_performance_networking" CONF_ENABLE_HIGH_PERFORMANCE = "enable_high_performance" +# Network priority tracking infrastructure +# Components can query this to determine their relative setup priority. +# CORE.data[KEY_NETWORK_PRIORITY] is a list of dicts of the form +# {"interface": "ethernet"}, in user-declared order. +KEY_NETWORK_PRIORITY = "network_priority" + +# Only interfaces whose component already calls get_network_priority() are +# accepted in the priority list. openthread and modem will be added here when +# they wire up their setup-priority consumer in their own to_code — see +# NETWORK_PLAN.md for the full multi-interface roadmap. +VALID_NETWORK_TYPES = ["ethernet", "wifi"] + +# Setup priority base values — first in list gets the highest priority. +# +# The base equals the historical setup_priority::WIFI / ::ETHERNET default +# (250.0), so a single-entry priority list yields exactly the same setup order +# as a config with no priority block. Subsequent entries step down by a small +# amount to break ties without crossing other priority bands. +# +# Important: must stay strictly less than setup_priority::AFTER_BLUETOOTH +# (300.0), which NetworkComponent itself uses — otherwise the highest-priority +# interface could tie with NetworkComponent and run before esp_netif_init(). +NETWORK_PRIORITY_BASE = 250.0 +NETWORK_PRIORITY_STEP = 5.0 + +# Lower-bound guard. The lowest-priority entry gets +# NETWORK_PRIORITY_BASE - (len - 1) * NETWORK_PRIORITY_STEP, which must stay +# strictly above setup_priority::AFTER_WIFI (200.0, see esphome/core/component.h) +# so a long priority list never drops an interface into the band used by +# components that expect to run after the network is up. There is ample headroom +# for the two types today; this check raises if a future expansion of +# VALID_NETWORK_TYPES would silently cross that band. Uses an explicit raise +# rather than a bare assert so the guard isn't stripped under python -O/-OO. +_SETUP_PRIORITY_AFTER_WIFI = 200.0 +if ( + NETWORK_PRIORITY_BASE - (len(VALID_NETWORK_TYPES) - 1) * NETWORK_PRIORITY_STEP + <= _SETUP_PRIORITY_AFTER_WIFI +): + raise ValueError( + "network: priority: list is long enough to cross setup_priority::AFTER_WIFI" + ) + network_ns = cg.esphome_ns.namespace("network") NetworkComponent = network_ns.class_("NetworkComponent", cg.Component) IPAddress = network_ns.class_("IPAddress") @@ -142,6 +191,83 @@ def validate_ipv6(value: bool) -> bool: return value +def get_network_priority(iface: str) -> float | None: + """Get the setup priority for the given network interface type. + + Returns the float setup priority for ``iface`` based on the order declared + under ``network: priority:``. Interfaces listed first receive a higher + setup priority so they are initialised before lower-priority ones. + + If no ``network: priority:`` has been configured this returns ``None`` and + the calling component should fall back to its own default setup priority. + + Args: + iface: Interface type string (case-insensitive). Currently ``"ethernet"`` + or ``"wifi"`` — the only types the priority-list validator + accepts; ``"openthread"`` / ``"modem"`` are planned but not yet + supported. An interface not present in the configured list + returns ``None``. + + Returns: + float setup priority, or None if no priority list was configured. + + Example usage inside a component's ``to_code``. Emit the override before + ``register_component`` so an explicit ``setup_priority:`` on the component + still wins:: + + from esphome.components import network + + async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + + prio = network.get_network_priority("ethernet") + if prio is not None: + cg.set_setup_priority(var, prio) + + await cg.register_component(var, config) + ... + """ + priority_list = CORE.data.get(KEY_NETWORK_PRIORITY) + if priority_list is None: + return None + iface_lower = iface.lower() + for idx, entry in enumerate(priority_list): + if entry["interface"] == iface_lower: + return NETWORK_PRIORITY_BASE - (idx * NETWORK_PRIORITY_STEP) + return None + + +def get_priority_interfaces_from_full_config(full_config: ConfigType) -> set[str]: + """Return the set of interface names declared in ``network: priority:``. + + Reads from the full validated config (``fv.full_config.get()``) and is + intended for use inside ``FINAL_VALIDATE_SCHEMA`` hooks, before + ``to_code`` has run and ``CORE.data`` has been populated. Returns an + empty set if no priority list was configured. + """ + return { + entry["interface"] + for entry in full_config.get("network", {}).get(CONF_PRIORITY, []) + } + + +def _validate_priority_list(value: Any) -> list[dict[str, str]]: + """Validate and normalize the priority list, rejecting duplicates. + + Each entry is the name of one network interface (one of + ``VALID_NETWORK_TYPES``). Mixed-case input is accepted and normalized + to lowercase. The normalized list is a list of dicts of the form + ``{"interface": "ethernet"}`` so that future per-entry options can be + added without breaking call sites. + """ + raw = cv.ensure_list(cv.one_of(*VALID_NETWORK_TYPES, lower=True))(value) + entries = [{"interface": iface} for iface in raw] + interfaces = [e["interface"] for e in entries] + if len(interfaces) != len(set(interfaces)): + raise cv.Invalid("Duplicate entries are not allowed in 'priority'") + return entries + + CONFIG_SCHEMA = cv.All( cv.Schema( { @@ -174,17 +300,52 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_ENABLE_HIGH_PERFORMANCE): cv.All( cv.boolean, cv.only_on_esp32 ), + cv.Optional(CONF_PRIORITY): _validate_priority_list, } ), _register_provisioning_source, ) +def _final_validate(config: ConfigType) -> None: + """Check that every interface named in 'priority' has a corresponding component block.""" + full = fv.full_config.get() + for entry in config.get(CONF_PRIORITY, []): + iface = entry["interface"] + if iface not in full: + raise cv.Invalid( + f"'{iface}' is listed in 'network: priority:' but no '{iface}:' " + f"component is configured", + [CONF_PRIORITY], + ) + + +FINAL_VALIDATE_SCHEMA = _final_validate + + @coroutine_with_priority(CoroPriority.NETWORK) async def to_code(config): cg.add_define("USE_NETWORK") # ESP32 with Arduino uses ESP-IDF network APIs directly, no Arduino Network library needed + # Store the user-declared network priority list in CORE.data so that ethernet, + # wifi and other network components can query it via get_network_priority() + # during their own to_code phase. + if CONF_PRIORITY in config: + priority_list = config[CONF_PRIORITY] + CORE.data[KEY_NETWORK_PRIORITY] = priority_list + # network/util.cpp resolves the reported address (get_use_address_to, + # get_ip_addresses) in a fixed ethernet-first order; a wifi-first priority + # list is the only case that deviates from it, so it is the only case that + # needs a define. Runtime (active-interface) selection is a planned follow-up. + if priority_list[0]["interface"] == "wifi": + cg.add_define("USE_NETWORK_PRIMARY_INTERFACE_WIFI") + + _LOGGER.info( + "Network interface priority: %s", + " > ".join(entry["interface"] for entry in priority_list), + ) + # Apply high performance networking settings # Config can explicitly enable/disable, or default to component-driven behavior enable_high_perf = config.get(CONF_ENABLE_HIGH_PERFORMANCE) diff --git a/esphome/components/network/ip_address.h b/esphome/components/network/ip_address.h index d8a127f4a0..f7aa7daf99 100644 --- a/esphome/components/network/ip_address.h +++ b/esphome/components/network/ip_address.h @@ -19,10 +19,37 @@ #ifdef USE_HOST #include +#if USE_NETWORK_IPV6 +using ip4_addr_t = struct in_addr; +using ip6_addr_t = struct in6_addr; +struct ip_addr_t { + union { + struct in6_addr ip6; + struct in_addr ip4; + } u_addr; + uint8_t type; +}; +enum : uint8_t { IPADDR_TYPE_V4 = 0, IPADDR_TYPE_V6 = 6 }; +static inline int ipaddr_aton(const char *cp, ip_addr_t *addr) { + if (strchr(cp, ':') != nullptr) { + if (inet_pton(AF_INET6, cp, &addr->u_addr.ip6) != 1) { + return 0; + } + addr->type = IPADDR_TYPE_V6; + return 1; + } + if (inet_aton(cp, &addr->u_addr.ip4) != 1) { + return 0; + } + addr->type = IPADDR_TYPE_V4; + return 1; +} +#else using ip_addr_t = in_addr; using ip4_addr_t = in_addr; #define ipaddr_aton(x, y) inet_aton((x), (y)) -#endif +#endif // USE_NETWORK_IPV6 +#endif // USE_HOST #ifdef USE_ZEPHYR #include @@ -71,15 +98,6 @@ struct IPAddress { bool is_ip4() const { return false; } bool is_ip6() const { return this->is_set(); } bool is_multicast() const { return net_ipv6_is_addr_mcast(&ip_addr_); } - // Remove before 2026.8.0 - ESPDEPRECATED( - "str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0", - "2026.2.0") - std::string str() const { - char buf[IP_ADDRESS_BUFFER_SIZE]; - this->str_to(buf); - return buf; - } char *str_to(char *buf) const { if (inet_ntop(AF_INET6, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE) == nullptr) buf[0] = '\0'; @@ -89,12 +107,46 @@ struct IPAddress { bool operator!=(const IPAddress &other) const { return !net_ipv6_addr_cmp(&ip_addr_, &other.ip_addr_); } #elif defined(USE_HOST) - IPAddress() { ip_addr_.s_addr = 0; } +#if USE_NETWORK_IPV6 + IPAddress() { memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); } IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { - this->ip_addr_.s_addr = htonl((first << 24) | (second << 16) | (third << 8) | fourth); + memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); + this->ip_addr_.u_addr.ip4.s_addr = + htonl(((uint32_t) first << 24) | ((uint32_t) second << 16) | ((uint32_t) third << 8) | fourth); + this->ip_addr_.type = IPADDR_TYPE_V4; + } + IPAddress(const char *in_address) { + memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); + ipaddr_aton(in_address, &this->ip_addr_); + } + IPAddress(const std::string &in_address) : IPAddress(in_address.c_str()) {} + IPAddress(const ip_addr_t *other_ip) { memcpy(&this->ip_addr_, other_ip, sizeof(ip_addr_t)); } + IPAddress(ip4_addr_t *other_ip) { + memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); + this->ip_addr_.u_addr.ip4 = *other_ip; + this->ip_addr_.type = IPADDR_TYPE_V4; + } + IPAddress(ip6_addr_t *other_ip) { + memset(&this->ip_addr_, 0, sizeof(this->ip_addr_)); + this->ip_addr_.u_addr.ip6 = *other_ip; + this->ip_addr_.type = IPADDR_TYPE_V6; + } + operator ip_addr_t() const { return this->ip_addr_; } + bool is_set() const { + if (this->ip_addr_.type == IPADDR_TYPE_V6) { + static constexpr uint8_t zero[sizeof(struct in6_addr)] = {}; + return memcmp(this->ip_addr_.u_addr.ip6.s6_addr, zero, sizeof(zero)) != 0; + } + return this->ip_addr_.u_addr.ip4.s_addr != 0; + } + bool is_ip4() const { return this->ip_addr_.type == IPADDR_TYPE_V4; } + bool is_ip6() const { return this->ip_addr_.type == IPADDR_TYPE_V6; } + bool is_multicast() const { + if (this->ip_addr_.type == IPADDR_TYPE_V6) { + return this->ip_addr_.u_addr.ip6.s6_addr[0] == 0xff; + } + return (ntohl(this->ip_addr_.u_addr.ip4.s_addr) & 0xF0000000UL) == 0xE0000000UL; } - IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); } - IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; } // Remove before 2026.8.0 ESPDEPRECATED( "str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0", @@ -104,11 +156,44 @@ struct IPAddress { this->str_to(buf); return buf; } + char *str_to(char *buf) const { + if (this->ip_addr_.type == IPADDR_TYPE_V6) { + inet_ntop(AF_INET6, &this->ip_addr_.u_addr.ip6, buf, IP_ADDRESS_BUFFER_SIZE); + } else { + inet_ntop(AF_INET, &this->ip_addr_.u_addr.ip4, buf, IP_ADDRESS_BUFFER_SIZE); + } + lowercase_ip_str(buf); + return buf; + } + bool operator==(const IPAddress &other) const { + if (this->ip_addr_.type != other.ip_addr_.type) { + return false; + } + if (this->ip_addr_.type == IPADDR_TYPE_V6) { + return memcmp(&this->ip_addr_.u_addr.ip6, &other.ip_addr_.u_addr.ip6, sizeof(struct in6_addr)) == 0; + } + return this->ip_addr_.u_addr.ip4.s_addr == other.ip_addr_.u_addr.ip4.s_addr; + } + bool operator!=(const IPAddress &other) const { return !(*this == other); } + IPAddress &operator+=(uint8_t increase) { + if (this->ip_addr_.type == IPADDR_TYPE_V4) { + (((uint8_t *) (&this->ip_addr_.u_addr.ip4))[3]) += increase; + } + return *this; + } +#else + IPAddress() { ip_addr_.s_addr = 0; } + IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { + this->ip_addr_.s_addr = htonl((first << 24) | (second << 16) | (third << 8) | fourth); + } + IPAddress(const std::string &in_address) { inet_aton(in_address.c_str(), &ip_addr_); } + IPAddress(const ip_addr_t *other_ip) { ip_addr_ = *other_ip; } /// Write IP address to buffer. Buffer must be at least IP_ADDRESS_BUFFER_SIZE bytes. char *str_to(char *buf) const { inet_ntop(AF_INET, &ip_addr_, buf, IP_ADDRESS_BUFFER_SIZE); return buf; // IPv4 only, no hex letters to lowercase } +#endif // USE_NETWORK_IPV6 #else IPAddress() { ip_addr_set_zero(&ip_addr_); } IPAddress(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) { @@ -186,15 +271,6 @@ struct IPAddress { bool is_ip4() const { return IP_IS_V4(&ip_addr_); } bool is_ip6() const { return IP_IS_V6(&ip_addr_); } bool is_multicast() const { return ip_addr_ismulticast(&ip_addr_); } - // Remove before 2026.8.0 - ESPDEPRECATED( - "str() is deprecated: use 'char buf[IP_ADDRESS_BUFFER_SIZE]; ip.str_to(buf);' instead. Removed in 2026.8.0", - "2026.2.0") - std::string str() const { - char buf[IP_ADDRESS_BUFFER_SIZE]; - this->str_to(buf); - return buf; - } /// Write IP address to buffer. Buffer must be at least IP_ADDRESS_BUFFER_SIZE bytes. /// Output is lowercased per RFC 5952 (IPv6 hex digits a-f). char *str_to(char *buf) const { diff --git a/esphome/components/network/util.cpp b/esphome/components/network/util.cpp index ae250c6a1f..d90c28801e 100644 --- a/esphome/components/network/util.cpp +++ b/esphome/components/network/util.cpp @@ -23,9 +23,14 @@ bool is_disabled() { } const char *get_use_address_to(std::span buf) { - // Global component pointers are guaranteed to be set by component constructors when USE_* is defined + // Global component pointers are guaranteed to be set by component constructors when USE_* is defined. + // A wifi-first network: priority: list sets USE_NETWORK_PRIMARY_INTERFACE_WIFI to lift + // wifi ahead of the fixed ethernet-first order below; an ethernet-first list already + // matches that order, so no define exists for it. const char *addr = nullptr; -#if defined(USE_ETHERNET) +#if defined(USE_NETWORK_PRIMARY_INTERFACE_WIFI) && defined(USE_WIFI) + addr = wifi::global_wifi_component->get_use_address(); +#elif defined(USE_ETHERNET) addr = ethernet::global_eth_component->get_use_address(); #elif defined(USE_MODEM) addr = modem::global_modem_component->get_use_address(); @@ -44,6 +49,19 @@ const char *get_use_address_to(std::span buf) { } network::IPAddresses get_ip_addresses() { + // With a wifi-first network: priority: list, prefer wifi while it has a valid IP; + // otherwise fall through to the fixed ethernet-first order below. Selection based + // on the runtime-active interface is a planned follow-up. +#if defined(USE_NETWORK_PRIMARY_INTERFACE_WIFI) && defined(USE_WIFI) + if (wifi::global_wifi_component != nullptr) { + auto ips = wifi::global_wifi_component->get_ip_addresses(); + for (const auto &ip : ips) { + if (ip.is_set()) + return ips; + } + } +#endif + #ifdef USE_ETHERNET if (ethernet::global_eth_component != nullptr) return ethernet::global_eth_component->get_ip_addresses(); diff --git a/esphome/components/network/util.h b/esphome/components/network/util.h index 17a2ff0977..df7e164bda 100644 --- a/esphome/components/network/util.h +++ b/esphome/components/network/util.h @@ -57,7 +57,8 @@ bool is_disabled(); /// Buffer size for get_use_address_to(): 63-char DNS label + ".local" + null terminator static constexpr size_t USE_ADDRESS_BUFFER_SIZE = 70; /// Get the active network address for logging. Returns the explicitly configured -/// use_address when one was set, otherwise formats ".local" from the runtime +/// use_address when one was set (from the highest-priority interface when +/// network: priority: is configured), otherwise formats ".local" from the runtime /// device name into buf (so it includes the MAC suffix from name_add_mac_suffix). const char *get_use_address_to(std::span buf); IPAddresses get_ip_addresses(); diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index d51155b0a4..efb6c88d28 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -19,10 +19,6 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform( }, "nextion_upload_arduino.cpp": { PlatformFramework.ESP8266_ARDUINO, - PlatformFramework.RP2_ARDUINO, - PlatformFramework.BK72XX_ARDUINO, - PlatformFramework.RTL87XX_ARDUINO, - PlatformFramework.LN882X_ARDUINO, }, } ) diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 89e9b93520..4ab123c354 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -4,7 +4,14 @@ from esphome import automation import esphome.codegen as cg from esphome.components import display, esp32, uart import esphome.config_validation as cv -from esphome.const import CONF_BRIGHTNESS, CONF_ID, CONF_LAMBDA, CONF_ON_TOUCH +from esphome.const import ( + CONF_BRIGHTNESS, + CONF_ID, + CONF_LAMBDA, + CONF_ON_TOUCH, + PLATFORM_ESP32, + PLATFORM_ESP8266, +) from esphome.core import CORE, TimePeriod from . import ( # noqa: F401 pylint: disable=unused-import @@ -135,7 +142,17 @@ CONFIG_SCHEMA = cv.All( cv.Optional( CONF_TFT_UPLOAD_WATCHDOG_TIMEOUT ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_TFT_URL): cv.url, + # TFT upload needs an HTTP client and runtime UART reconfiguration, + # neither of which is implemented for the RP2 or host platforms. + cv.Optional(CONF_TFT_URL): cv.All( + cv.url, + cv.only_on( + [ + PLATFORM_ESP32, + PLATFORM_ESP8266, + ] + ), + ), cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.Any( 0, cv.int_range(min=3, max=65535) ), diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 7dc5a4fe44..aa9fe8abb3 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -23,7 +23,9 @@ #elif defined(USE_ESP8266) #include #include -#endif // USE_ESP32 vs USE_ESP8266 +#elif defined(USE_LIBRETINY) +#include +#endif // USE_ESP32 vs USE_ESP8266 vs USE_LIBRETINY #endif // USE_NEXTION_TFT_UPLOAD namespace esphome::nextion { @@ -1564,7 +1566,7 @@ class Nextion final : public NextionBase, public PollingComponent, public uart:: * @return position of last byte transferred, -1 for failure. */ int upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &range_start); -#elif defined(USE_ARDUINO) +#elif defined(USE_ESP8266) || defined(USE_LIBRETINY) /** * will request chunk_size chunks from the web server * and send each to the nextion @@ -1573,7 +1575,7 @@ class Nextion final : public NextionBase, public PollingComponent, public uart:: * @return position of last byte transferred, -1 for failure. */ int upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start); -#endif // USE_ESP32 vs USE_ARDUINO +#endif // USE_ESP32 vs USE_ESP8266/USE_LIBRETINY /** * Ends the upload process, restart Nextion and, if successful, diff --git a/esphome/components/nextion/nextion_upload_arduino.cpp b/esphome/components/nextion/nextion_upload_arduino.cpp index 41379c2345..2f3377d950 100644 --- a/esphome/components/nextion/nextion_upload_arduino.cpp +++ b/esphome/components/nextion/nextion_upload_arduino.cpp @@ -1,7 +1,7 @@ #include "nextion.h" #ifdef USE_NEXTION_TFT_UPLOAD -#ifndef USE_ESP32 +#ifdef USE_ESP8266 #include #include "esphome/components/network/util.h" @@ -209,7 +209,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { http_client.setTimeout(this->tft_upload_http_timeout_); bool begin_status = false; -#ifdef USE_ESP8266 #if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0) http_client.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); #elif USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 6, 0) @@ -219,7 +218,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { http_client.setRedirectLimit(3); #endif begin_status = http_client.begin(*this->get_wifi_client_(), this->tft_url_.c_str()); -#endif // USE_ESP8266 if (!begin_status) { this->connection_state_.is_updating_ = false; ESP_LOGD(TAG, "Connection failed"); @@ -356,7 +354,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) { return upload_end_(true); } -#ifdef USE_ESP8266 WiFiClient *Nextion::get_wifi_client_() { if (this->tft_url_.starts_with("https:")) { if (this->wifi_client_secure_ == nullptr) { @@ -374,9 +371,8 @@ WiFiClient *Nextion::get_wifi_client_() { } return this->wifi_client_; } -#endif // USE_ESP8266 } // namespace esphome::nextion -#endif // NOT USE_ESP32 +#endif // USE_ESP8266 #endif // USE_NEXTION_TFT_UPLOAD diff --git a/esphome/components/nrf52/__init__.py b/esphome/components/nrf52/__init__.py index 8d522a8740..4002d1cc04 100644 --- a/esphome/components/nrf52/__init__.py +++ b/esphome/components/nrf52/__init__.py @@ -69,7 +69,12 @@ from .const import ( BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, ) -from .framework import check_and_install, get_build_env, get_build_paths +from .framework import ( + check_and_install, + get_build_env, + get_build_paths, + setup_platformio_python_env, +) # force import gpio to register pin schema from .gpio import nrf52_pin_to_code # noqa: F401 @@ -278,6 +283,13 @@ def _validate_mcumgr(config): def _final_validate(config): + # Remove before 2027.2.0 + if CORE.using_toolchain_platformio: + _LOGGER.warning( + "The 'platformio' toolchain for nRF52 is deprecated and will be removed in ESPHome 2027.2.0. " + "Please use 'toolchain: sdk-nrf' instead." + ) + if CONF_DFU in config: _validate_mcumgr(config) if config[KEY_BOOTLOADER] == BOOTLOADER_ADAFRUIT: @@ -514,6 +526,7 @@ def _upload_using_platformio( ) -> int | str: from esphome.platformio import toolchain + setup_platformio_python_env() if port is not None: upload_args += ["--upload-port", port] return toolchain.run_platformio_cli_run(config, CORE.verbose, *upload_args) @@ -809,6 +822,10 @@ def _copy_if_exists(src: Path, dst: Path) -> None: def run_compile(args, config: ConfigType) -> bool: if CORE.using_toolchain_platformio: + # The actual build is done by PlatformIO (the caller falls through to + # it when this returns False); prepare the Python environment its + # Zephyr build script expects first. + setup_platformio_python_env() return False if not CORE.using_toolchain_sdk_nrf: raise EsphomeError( @@ -860,6 +877,21 @@ def run_compile(args, config: ConfigType) -> bool: zephyr_dir = build_dir / "zephyr" framework_ver = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + bootloader = zephyr_data()[KEY_BOOTLOADER] + + # (dev_type, sd_req) per bootloader — values from Nordic SoftDevice release notes + _GENPKG_PARAMS = { + BOOTLOADER_ADAFRUIT_NRF52_SD132: ("0x0051", "0x009D"), + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6: ("0x0052", "0x00B6"), + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7: ("0x0052", "0x00CA"), + } + # UF2 family IDs — nRF52832 vs nRF52840 per SoftDevice variant + _UF2_FAMILY_IDS = { + BOOTLOADER_ADAFRUIT_NRF52_SD132: "0x7EAED30A", + BOOTLOADER_ADAFRUIT_NRF52_SD140_V6: "0xADA52840", + BOOTLOADER_ADAFRUIT_NRF52_SD140_V7: "0xADA52840", + } + # SDK < 2.9.2 places artifacts directly in build_dir/zephyr/. # SDK >= 2.9.2 nests them one level deeper (build_dir/zephyr/zephyr/); # copy files to match get_download_types layout. @@ -871,20 +903,43 @@ def run_compile(args, config: ConfigType) -> bool: _copy_if_exists(west_out / "zephyr.signed.bin", zephyr_dir / "app_update.bin") _copy_if_exists(build_dir / "merged.hex", zephyr_dir / "merged.hex") - # (dev_type, sd_req) per bootloader — values from Nordic SoftDevice release notes - _GENPKG_PARAMS = { - BOOTLOADER_ADAFRUIT_NRF52_SD132: ("0x0051", "0x009D"), - BOOTLOADER_ADAFRUIT_NRF52_SD140_V6: ("0x0052", "0x00B6"), - BOOTLOADER_ADAFRUIT_NRF52_SD140_V7: ("0x0052", "0x00CA"), - } - bootloader = zephyr_data()[KEY_BOOTLOADER] + # For Adafruit bootloader builds, regenerate the UF2 from merged.hex, + # whose records carry the correct flash addresses. The build's own + # zephyr.uf2 uses the board's default offset, which is wrong in some cases. + merged_hex = zephyr_dir / "merged.hex" + if bootloader in _UF2_FAMILY_IDS and merged_hex.is_file(): + # Drop the build's own wrong-offset UF2 so it isn't shipped alongside. + app_uf2 = west_out / "zephyr.uf2" + if app_uf2.is_file(): + app_uf2.unlink() + uf2conv = ( + paths["framework_path"] / "zephyr" / "scripts" / "build" / "uf2conv.py" + ) + if not run_command_ok( + [ + str(paths["python_executable"]), + str(uf2conv), + "-f", + _UF2_FAMILY_IDS[bootloader], + "-c", + "-o", + str(zephyr_dir / "zephyr.uf2"), + str(merged_hex), + ], + env=env, + stream_output=True, + ): + raise EsphomeError("Failed to generate UF2 from merged hex") + if bootloader in ( BOOTLOADER_ADAFRUIT, BOOTLOADER_ADAFRUIT_NRF52_SD132, BOOTLOADER_ADAFRUIT_NRF52_SD140_V6, BOOTLOADER_ADAFRUIT_NRF52_SD140_V7, ): - hex_file = west_out / "zephyr.hex" + # no fallback is needed for adafruit case. merged merged.hex is always generated. + # get_download_types needs fallback for mcuboot (non adafruit) + hex_file = zephyr_dir / "merged.hex" dfu_package = build_dir / "firmware.zip" genpkg_cmd = [ str(paths["python_executable"]), diff --git a/esphome/components/nrf52/framework.py b/esphome/components/nrf52/framework.py index fa6f7d57ad..6b32fe1fea 100644 --- a/esphome/components/nrf52/framework.py +++ b/esphome/components/nrf52/framework.py @@ -4,6 +4,7 @@ import os from pathlib import Path import platform import shutil +import sys import tempfile import platformdirs @@ -27,6 +28,11 @@ _LOGGER = logging.getLogger(__name__) _REQUIREMENTS = Path(__file__).parent / "requirements.txt" TOOLCHAIN_VERSION = "0.17.4" +# Packages the PlatformIO toolchain's Zephyr build script needs beyond west +# (which comes from requirements.txt). Keep the pin in sync with +# framework-sdk-nrf scripts/platformio/platformio-build.py. +_PLATFORMIO_PENV_REQUIREMENTS: tuple[str, ...] = ("cbor2==5.6.5",) + SDK_NG_TOOLCHAIN_MIRRORS = str_to_lst_of_str( os.environ.get( "ESPHOME_SDK_NG_TOOLCHAIN_MIRRORS", @@ -133,10 +139,94 @@ def get_build_env() -> dict: env = os.environ.copy() env["PATH"] = str(venv_bin_dir) + os.pathsep + env.get("PATH", "") env["ZEPHYR_BASE"] = str(_get_framework_path(version) / "zephyr") - env["Zephyr-sdk_DIR"] = str(_get_toolchain_path(TOOLCHAIN_VERSION) / "cmake") + # ZEPHYR_SDK_INSTALL_DIR is the variable Zephyr documents for pointing at + # the SDK: FindZephyr-sdk.cmake reads it (from the environment, via + # zephyr_get) and passes it straight to find_package as a HINT. This + # matters because the SDK lives in the esphome cache dir, which is not on + # the module's static search path (/usr, /opt, $HOME, ...). A generic + # "Zephyr-sdk_DIR" environment hint proved unreliable here: containerized + # non-root builds failed to locate the SDK with it, while + # ZEPHYR_SDK_INSTALL_DIR fixed the same invocation. + env["ZEPHYR_SDK_INSTALL_DIR"] = str(_get_toolchain_path(TOOLCHAIN_VERSION)) return env +def _get_platformio_penv_path() -> Path: + return get_sdk_nrf_tools_path() / "penvs" / "platformio" + + +def _get_penv_site_packages(penv_path: Path) -> Path: + if os.name == "nt": + return penv_path / "Lib" / "site-packages" + python_dir = f"python{sys.version_info.major}.{sys.version_info.minor}" + return penv_path / "lib" / python_dir / "site-packages" + + +def _prepend_env_path(name: str, entry: str) -> None: + """Prepend ``entry`` to the ``os.pathsep``-separated env var ``name``.""" + current = os.environ.get(name, "") + entries = current.split(os.pathsep) if current else [] + if entry not in entries: + os.environ[name] = os.pathsep.join([entry, *entries]) + + +def setup_platformio_python_env() -> None: + """Make the Zephyr build's Python packages available to PlatformIO. + + The PlatformIO toolchain's Zephyr framework build script pip-installs + west and cbor2 (and pyocd on x86_64) into the Python environment running + PlatformIO whenever they are not importable. That environment is not + always writable — for example the docker image run as a non-root user, + where ESPHome lives in the system Python — so the install fails with + "Permission denied". Instead, pre-install those packages into a dedicated + venv under the sdk-nrf tools dir and expose it to the PlatformIO + subprocesses through the environment: + + * PYTHONPATH makes the venv's packages importable from the interpreter + that runs PlatformIO/SCons, so the build script skips its installs. + * VIRTUAL_ENV redirects any install the build script still performs via + uv (pyocd is fetched on demand) into the writable venv. + * PATH exposes console scripts installed into the venv (e.g. pyocd). + """ + penv_path = _get_platformio_penv_path() + env_python_path = get_python_env_executable_path(penv_path, "python") + sentinel = penv_path / ".ready" + # Include the Python version: the venv breaks when the interpreter it + # was created from is upgraded, so it must be rebuilt. + requirements_hash = hashlib.sha256( + _REQUIREMENTS.read_bytes() + + "\n".join(_PLATFORMIO_PENV_REQUIREMENTS).encode() + + f"python{sys.version_info.major}.{sys.version_info.minor}".encode() + ).hexdigest() + if ( + not sentinel.exists() + or sentinel.read_text(encoding="utf-8") != requirements_hash + ): + rmdir(penv_path, msg="Clean up PlatformIO toolchain Python environment") + + create_venv(penv_path, msg="PlatformIO toolchain") + + _LOGGER.info("Installing PlatformIO toolchain requirements ...") + cmd = [ + str(env_python_path), + "-m", + "pip", + "install", + "-r", + str(_REQUIREMENTS), + *_PLATFORMIO_PENV_REQUIREMENTS, + ] + if not run_command_ok(cmd): + raise EsphomeError( + "Install requirements for PlatformIO toolchain Python environment failure" + ) + sentinel.write_text(requirements_hash, encoding="utf-8") + + os.environ["VIRTUAL_ENV"] = str(penv_path) + _prepend_env_path("PYTHONPATH", str(_get_penv_site_packages(penv_path))) + _prepend_env_path("PATH", str(env_python_path.parent)) + + def _patch_uf2conv_escape_sequences(framework_path: Path) -> None: # SDK v2.6.1 ships uf2conv.py with '\s+' — an unrecognised escape that # Python 3.12+ flags with SyntaxWarning (a future version will reject it). @@ -199,6 +289,7 @@ def check_and_install() -> None: "init", "-m", "https://github.com/nrfconnect/sdk-nrf", + "-o=--depth=1", "--mr", version, str(framework_path), diff --git a/esphome/components/opentherm/hub.cpp b/esphome/components/opentherm/hub.cpp index e2828a9e30..f8b515fa05 100644 --- a/esphome/components/opentherm/hub.cpp +++ b/esphome/components/opentherm/hub.cpp @@ -27,11 +27,11 @@ uint8_t parse_u8_lb(OpenthermData &data) { return data.valueLB; } uint8_t parse_u8_hb(OpenthermData &data) { return data.valueHB; } int8_t parse_s8_lb(OpenthermData &data) { return (int8_t) data.valueLB; } int8_t parse_s8_hb(OpenthermData &data) { return (int8_t) data.valueHB; } -uint16_t parse_u16(OpenthermData &data) { return data.u16(); } +uint16_t parse_u16(OpenthermData &data) { return data.get_u16(); } uint16_t parse_u8_lb_60(OpenthermData &data) { return data.valueLB * 60; } uint16_t parse_u8_hb_60(OpenthermData &data) { return data.valueHB * 60; } -int16_t parse_s16(OpenthermData &data) { return data.s16(); } -float parse_f88(OpenthermData &data) { return data.f88(); } +int16_t parse_s16(OpenthermData &data) { return data.get_s16(); } +float parse_f88(OpenthermData &data) { return data.get_f88(); } void write_flag8_lb_0(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 0, value); } void write_flag8_lb_1(const bool value, OpenthermData &data) { data.valueLB = write_bit(data.valueLB, 1, value); } @@ -53,9 +53,9 @@ void write_u8_lb(const uint8_t value, OpenthermData &data) { data.valueLB = valu void write_u8_hb(const uint8_t value, OpenthermData &data) { data.valueHB = value; } void write_s8_lb(const int8_t value, OpenthermData &data) { data.valueLB = (uint8_t) value; } void write_s8_hb(const int8_t value, OpenthermData &data) { data.valueHB = (uint8_t) value; } -void write_u16(const uint16_t value, OpenthermData &data) { data.u16(value); } -void write_s16(const int16_t value, OpenthermData &data) { data.s16(value); } -void write_f88(const float value, OpenthermData &data) { data.f88(value); } +void write_u16(const uint16_t value, OpenthermData &data) { data.set_u16(value); } +void write_s16(const int16_t value, OpenthermData &data) { data.set_s16(value); } +void write_f88(const float value, OpenthermData &data) { data.set_f88(value); } } // namespace message_data diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 5cf7c19880..3343871cd0 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -114,6 +114,7 @@ bool OpenTherm::get_protocol_error(OpenThermError &error) { void OpenTherm::stop() { this->stop_timer_(); this->mode_ = OperationMode::IDLE; + this->out_pin_->digital_write(true); } void IRAM_ATTR OpenTherm::read_() { @@ -533,34 +534,34 @@ void OpenTherm::debug_data(OpenthermData &data) { ESP_LOGD(TAG, "%s %s %s %s", format_bin_to(type_buf, data.type), format_bin_to(id_buf, data.id), format_bin_to(hb_buf, data.valueHB), format_bin_to(lb_buf, data.valueLB)); ESP_LOGD(TAG, "type: %s; id: %u; HB: %u; LB: %u; uint_16: %u; float: %f", - this->message_type_to_str((MessageType) data.type), data.id, data.valueHB, data.valueLB, data.u16(), - data.f88()); + this->message_type_to_str((MessageType) data.type), data.id, data.valueHB, data.valueLB, data.get_u16(), + data.get_f88()); } void OpenTherm::debug_error(OpenThermError &error) const { ESP_LOGD(TAG, "data: 0x%08" PRIx32 "; clock: %u; capture: 0x%08" PRIx32 "; bit_pos: %u", error.data, this->clock_, error.capture, error.bit_pos); } -float OpenthermData::f88() { return ((float) this->s16()) / 256.0f; } +float OpenthermData::get_f88() { return ((float) this->get_s16()) / 256.0f; } -void OpenthermData::f88(float value) { this->s16((int16_t) (value * 256)); } +void OpenthermData::set_f88(float value) { this->set_s16((int16_t) (value * 256)); } -uint16_t OpenthermData::u16() { +uint16_t OpenthermData::get_u16() { uint16_t const value = this->valueHB; return (value << 8) | this->valueLB; } -void OpenthermData::u16(uint16_t value) { +void OpenthermData::set_u16(uint16_t value) { this->valueLB = value & 0xFF; this->valueHB = (value >> 8) & 0xFF; } -int16_t OpenthermData::s16() { +int16_t OpenthermData::get_s16() { int16_t const value = this->valueHB; return (value << 8) | this->valueLB; } -void OpenthermData::s16(int16_t value) { +void OpenthermData::set_s16(int16_t value) { this->valueLB = value & 0xFF; this->valueHB = (value >> 8) & 0xFF; } diff --git a/esphome/components/opentherm/opentherm.h b/esphome/components/opentherm/opentherm.h index 3078e92c9d..7aa81cd8a2 100644 --- a/esphome/components/opentherm/opentherm.h +++ b/esphome/components/opentherm/opentherm.h @@ -178,7 +178,7 @@ enum BitPositions { STOP_BIT = 33 }; /** * Structure to hold Opentherm data packet content. - * Use f88(), u16() or s16() functions to get appropriate value of data packet accoridng to id of message. + * Use get_f88(), get_u16() or get_s16() functions to get appropriate value of data packet according to id of message. */ struct OpenthermData { uint8_t type; @@ -191,32 +191,32 @@ struct OpenthermData { /** * @return float representation of data packet value */ - float f88(); + float get_f88(); /** * @param float number to set as value of this data packet */ - void f88(float value); + void set_f88(float value); /** * @return unsigned 16b integer representation of data packet value */ - uint16_t u16(); + uint16_t get_u16(); /** * @param unsigned 16b integer number to set as value of this data packet */ - void u16(uint16_t value); + void set_u16(uint16_t value); /** * @return signed 16b integer representation of data packet value */ - int16_t s16(); + int16_t get_s16(); /** * @param signed 16b integer number to set as value of this data packet */ - void s16(int16_t value); + void set_s16(int16_t value); }; struct OpenThermError { diff --git a/esphome/components/opentherm/schema.py b/esphome/components/opentherm/schema.py index f70c8e24db..7f3ea2df36 100644 --- a/esphome/components/opentherm/schema.py +++ b/esphome/components/opentherm/schema.py @@ -91,7 +91,7 @@ SENSORS: dict[str, SensorSchema] = { ), "dhw_flow_rate": SensorSchema( description="Water flow rate in DHW circuit", - unit_of_measurement="l/min", + unit_of_measurement="L/min", accuracy_decimals=2, icon="mdi:waves-arrow-right", state_class=STATE_CLASS_MEASUREMENT, diff --git a/esphome/components/packages/__init__.py b/esphome/components/packages/__init__.py index 44a1ebf36e..6cb9d5f03a 100644 --- a/esphome/components/packages/__init__.py +++ b/esphome/components/packages/__init__.py @@ -1,6 +1,7 @@ from collections import UserDict from collections.abc import Callable from functools import reduce +import logging from pathlib import Path from typing import Any @@ -35,6 +36,8 @@ from esphome.const import ( ) from esphome.core import EsphomeError +_LOGGER = logging.getLogger(__name__) + DOMAIN = CONF_PACKAGES # Guard against infinite include chains (e.g. A includes B includes A). MAX_INCLUDE_DEPTH = 20 @@ -267,8 +270,23 @@ def _process_remote_package(config: dict[str, Any]) -> dict[str, Any]: # If loading fails, the cached checkout may be stale — revert and retry once. try: return {CONF_PACKAGES: get_packages(files)} - except cv.Invalid: - revert() + except cv.Invalid as err: + if not revert(): + # The pre-update content is out of reach (lock timeout, the + # checkout moved, or the reset failed; see the log), so a + # retry could not see it. + raise cv.Invalid( + f"Failed to load packages and could not revert the cached " + f"checkout to retry. {err}", + path=err.path, + ) from err + # If the retry succeeds this is the only trace that the + # refreshed upstream content was broken. + _LOGGER.warning( + "Loading packages failed (%s), reverted the cached checkout " + "and retrying", + err, + ) try: return {CONF_PACKAGES: get_packages(files)} except cv.Invalid as err: diff --git a/esphome/components/pzemac/pzemac.cpp b/esphome/components/pzemac/pzemac.cpp index d36e5d0250..5651e07af0 100644 --- a/esphome/components/pzemac/pzemac.cpp +++ b/esphome/components/pzemac/pzemac.cpp @@ -5,11 +5,11 @@ namespace esphome::pzemac { static const char *const TAG = "pzemac"; -static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t PZEM_CMD_RESET_ENERGY = 0x42; static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers -void PZEMAC::on_modbus_data(const std::vector &data) { +void PZEMAC::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); if (data.size() < 20) { ESP_LOGW(TAG, "Invalid size for PZEM AC!"); return; @@ -61,7 +61,7 @@ void PZEMAC::on_modbus_data(const std::vector &data) { this->power_factor_sensor_->publish_state(power_factor); } -void PZEMAC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, PZEM_REGISTER_COUNT); } +void PZEMAC::update() { this->read_input_registers(0, PZEM_REGISTER_COUNT); } void PZEMAC::dump_config() { ESP_LOGCONFIG(TAG, "PZEMAC:\n" @@ -76,10 +76,8 @@ void PZEMAC::dump_config() { } void PZEMAC::reset_energy_() { - std::vector cmd; - cmd.push_back(this->address_); - cmd.push_back(PZEM_CMD_RESET_ENERGY); - this->send_raw(cmd); + const uint8_t pdu[] = {PZEM_CMD_RESET_ENERGY}; + this->send_pdu(pdu); } } // namespace esphome::pzemac diff --git a/esphome/components/pzemac/pzemac.h b/esphome/components/pzemac/pzemac.h index a3ad7e1167..171212d3ee 100644 --- a/esphome/components/pzemac/pzemac.h +++ b/esphome/components/pzemac/pzemac.h @@ -5,7 +5,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::pzemac { @@ -22,7 +22,7 @@ class PZEMAC final : public PollingComponent, public modbus::ModbusClientDevice void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; diff --git a/esphome/components/pzemdc/pzemdc.cpp b/esphome/components/pzemdc/pzemdc.cpp index 6ded9b3a34..5e505cde0c 100644 --- a/esphome/components/pzemdc/pzemdc.cpp +++ b/esphome/components/pzemdc/pzemdc.cpp @@ -5,11 +5,11 @@ namespace esphome::pzemdc { static const char *const TAG = "pzemdc"; -static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t PZEM_CMD_RESET_ENERGY = 0x42; static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers -void PZEMDC::on_modbus_data(const std::vector &data) { +void PZEMDC::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); if (data.size() < 16) { ESP_LOGW(TAG, "Invalid size for PZEM DC!"); return; @@ -51,7 +51,7 @@ void PZEMDC::on_modbus_data(const std::vector &data) { this->energy_sensor_->publish_state(energy); } -void PZEMDC::update() { this->send(PZEM_CMD_READ_IN_REGISTERS, 0, 8); } +void PZEMDC::update() { this->read_input_registers(0, 8); } void PZEMDC::dump_config() { ESP_LOGCONFIG(TAG, "PZEMDC:\n" @@ -64,10 +64,8 @@ void PZEMDC::dump_config() { } void PZEMDC::reset_energy() { - std::vector cmd; - cmd.push_back(this->address_); - cmd.push_back(PZEM_CMD_RESET_ENERGY); - this->send_raw(cmd); + const uint8_t pdu[] = {PZEM_CMD_RESET_ENERGY}; + this->send_pdu(pdu); } } // namespace esphome::pzemdc diff --git a/esphome/components/pzemdc/pzemdc.h b/esphome/components/pzemdc/pzemdc.h index 7d14a5ed4b..b7657608e6 100644 --- a/esphome/components/pzemdc/pzemdc.h +++ b/esphome/components/pzemdc/pzemdc.h @@ -5,7 +5,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::pzemdc { @@ -18,7 +18,7 @@ class PZEMDC final : public PollingComponent, public modbus::ModbusClientDevice void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; diff --git a/esphome/components/rc522_i2c/__init__.py b/esphome/components/rc522_i2c/__init__.py index 7c42a12429..c67615e2d8 100644 --- a/esphome/components/rc522_i2c/__init__.py +++ b/esphome/components/rc522_i2c/__init__.py @@ -16,7 +16,7 @@ CONFIG_SCHEMA = cv.All( { cv.GenerateID(): cv.declare_id(RC522I2C), } - ).extend(i2c.i2c_device_schema(0x2C)) + ).extend(i2c.i2c_device_schema(0x28)) ) diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index cbf82e6f44..19b8549f75 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -2012,7 +2012,14 @@ HaierData, HaierBinarySensor, HaierTrigger, HaierAction, HaierDumper = declare_p HaierAction = ns.class_("HaierAction", RemoteTransmitterActionBase) HAIER_SCHEMA = cv.Schema( { - cv.Required(CONF_CODE): cv.All([cv.hex_uint8_t], cv.Length(min=13, max=13)), + cv.Required(CONF_CODE): cv.All( + [cv.hex_uint8_t], + cv.Any( + cv.Length(min=8, max=8), + cv.Length(min=13, max=13), + msg="must be a list of length 8 or 13", + ), + ), } ) diff --git a/esphome/components/remote_base/haier_protocol.cpp b/esphome/components/remote_base/haier_protocol.cpp index fa4cec773f..8801f1049a 100644 --- a/esphome/components/remote_base/haier_protocol.cpp +++ b/esphome/components/remote_base/haier_protocol.cpp @@ -11,9 +11,12 @@ constexpr uint32_t HEADER_HIGH_US = 4400; constexpr uint32_t BIT_MARK_US = 540; constexpr uint32_t BIT_ONE_SPACE_US = 1650; constexpr uint32_t BIT_ZERO_SPACE_US = 580; -constexpr unsigned int HAIER_IR_PACKET_BIT_SIZE = 112; +// 8 bytes + checksum +constexpr unsigned int HAIER_IR_PACKET_BIT_SIZE_SHORT = 72; +// 13 bytes + checksum +constexpr unsigned int HAIER_IR_PACKET_BIT_SIZE_LONG = 112; // Max data bytes in packet (excluding checksum) -constexpr size_t HAIER_MAX_DATA_BYTES = (HAIER_IR_PACKET_BIT_SIZE / 8); +constexpr size_t HAIER_MAX_DATA_BYTES = HAIER_IR_PACKET_BIT_SIZE_LONG / 8 - 1; void HaierProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t item) { for (uint8_t mask = 1 << 7; mask != 0; mask >>= 1) { @@ -28,7 +31,7 @@ void HaierProtocol::encode_byte_(RemoteTransmitData *dst, uint8_t item) { void HaierProtocol::encode(RemoteTransmitData *dst, const HaierData &data) { dst->set_carrier_frequency(38000); - dst->reserve(5 + ((data.data.size() + 1) * 2)); + dst->reserve(5 + ((data.data.size() + 1) * 16)); dst->mark(HEADER_LOW_US); dst->space(HEADER_LOW_US); dst->mark(HEADER_LOW_US); @@ -50,11 +53,16 @@ optional HaierProtocol::decode(RemoteReceiveData src) { return {}; } size_t size = src.size() - src.get_index() - 1; - if (size < HAIER_IR_PACKET_BIT_SIZE * 2) + if (size >= HAIER_IR_PACKET_BIT_SIZE_LONG * 2) { + size = HAIER_IR_PACKET_BIT_SIZE_LONG * 2; + } else if (size >= HAIER_IR_PACKET_BIT_SIZE_SHORT * 2) { + size = HAIER_IR_PACKET_BIT_SIZE_SHORT * 2; + } else { return {}; - size = HAIER_IR_PACKET_BIT_SIZE * 2; + } uint8_t checksum = 0; HaierData out; + out.data.reserve(size / 16 - 1); while (size > 0) { uint8_t data = 0; for (uint8_t mask = 0x80; mask != 0; mask >>= 1) { diff --git a/esphome/components/rp2/__init__.py b/esphome/components/rp2/__init__.py index fad9d3d25b..8bba6bc27d 100644 --- a/esphome/components/rp2/__init__.py +++ b/esphome/components/rp2/__init__.py @@ -33,6 +33,7 @@ from esphome.core import ( ) from esphome.core.config import BOARD_MAX_LENGTH from esphome.helpers import copy_file_if_changed, read_file, write_file_if_changed +from esphome.platformio.toolchain import copy_ccache_script from esphome.types import ConfigType from . import boards @@ -338,7 +339,7 @@ async def to_code(config): cg.add_define("ESPHOME_VARIANT", VARIANT_FRIENDLY[variant]) cg.add_define(ThreadModel.SINGLE) - cg.add_platformio_option("extra_scripts", ["post:post_build.py"]) + cg.add_platformio_option("extra_scripts", ["pre:ccache.py", "post:post_build.py"]) conf = config[CONF_FRAMEWORK] cg.add_platformio_option("framework", "arduino") @@ -594,6 +595,7 @@ def copy_files(): inject_lwip_file, CORE.relative_build_path("inject_lwip_include.py"), ) + copy_ccache_script() _generate_lwipopts_h() if generate_pio_files(): path = CORE.relative_src_path("esphome.h") diff --git a/esphome/components/rp2/core.h b/esphome/components/rp2/core.h index c53c3719eb..4ce9151d41 100644 --- a/esphome/components/rp2/core.h +++ b/esphome/components/rp2/core.h @@ -5,6 +5,7 @@ #include #include +// NOLINTNEXTLINE(google-runtime-int,readability-identifier-naming,readability-redundant-declaration) extern "C" unsigned long ulMainGetRunTimeCounterValue(); namespace esphome::rp2 {} // namespace esphome::rp2 diff --git a/esphome/components/rp2/crash_handler.cpp b/esphome/components/rp2/crash_handler.cpp index 5553a24a60..a0fea21637 100644 --- a/esphome/components/rp2/crash_handler.cpp +++ b/esphome/components/rp2/crash_handler.cpp @@ -64,7 +64,7 @@ static struct CrashData { uint32_t sp; uint32_t backtrace[MAX_BACKTRACE]; uint8_t backtrace_count; -} s_crash_data __attribute__((section(".noinit"))); +} s_crash_data __attribute__((section(".noinit"))); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) bool crash_handler_has_data() { return s_crash_data.valid; } diff --git a/esphome/components/rp2/hal.cpp b/esphome/components/rp2/hal.cpp index 28535cacbb..8eb1b469bc 100644 --- a/esphome/components/rp2/hal.cpp +++ b/esphome/components/rp2/hal.cpp @@ -20,8 +20,7 @@ namespace esphome { // arch_feed_wdt(), arch_get_cpu_cycle_count() inlined in components/rp2/hal.h. void arch_restart() { watchdog_reboot(0, 0, 10); - while (1) { - continue; + while (true) { } } diff --git a/esphome/components/rp2/hal.h b/esphome/components/rp2/hal.h index b16f31d797..ec46937bab 100644 --- a/esphome/components/rp2/hal.h +++ b/esphome/components/rp2/hal.h @@ -17,13 +17,13 @@ extern "C" unsigned long micros(void); extern "C" unsigned long millis(void); // NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration) -// Forward decl from . +// Forward decls from and the pico-sdk / FreeRTOS port for the +// inline arch_* wrappers below. +// NOLINTBEGIN(google-runtime-int,readability-identifier-naming,readability-redundant-declaration) extern "C" uint64_t time_us_64(void); - -// Forward decls from pico-sdk / FreeRTOS port for the inline arch_* -// wrappers below. extern "C" void watchdog_update(void); extern "C" unsigned long ulMainGetRunTimeCounterValue(void); +// NOLINTEND(google-runtime-int,readability-identifier-naming,readability-redundant-declaration) namespace esphome::rp2 {} diff --git a/esphome/components/rp2/preferences.cpp b/esphome/components/rp2/preferences.cpp index 778ce070a9..d1e0bc555f 100644 --- a/esphome/components/rp2/preferences.cpp +++ b/esphome/components/rp2/preferences.cpp @@ -26,6 +26,7 @@ static bool s_flash_dirty = false; // NOLINT(cppcoreguidelines-avo // No preference can exceed the total flash storage, so stack buffer covers all cases. static constexpr size_t PREF_MAX_BUFFER_SIZE = RP2040_FLASH_STORAGE_SIZE; +// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) extern "C" uint8_t _EEPROM_start; template uint8_t calculate_crc(It first, It last, uint32_t type) { @@ -38,9 +39,9 @@ template uint8_t calculate_crc(It first, It last, uint32_t type) { } bool RP2PreferenceBackend::save(const uint8_t *data, size_t len) { - const size_t buffer_size = len + 1; - if (buffer_size > PREF_MAX_BUFFER_SIZE) + if (len >= PREF_MAX_BUFFER_SIZE) return false; + const size_t buffer_size = len + 1; uint8_t buffer[PREF_MAX_BUFFER_SIZE]; memcpy(buffer, data, len); buffer[len] = calculate_crc(buffer, buffer + len, this->type); @@ -59,9 +60,9 @@ bool RP2PreferenceBackend::save(const uint8_t *data, size_t len) { } bool RP2PreferenceBackend::load(uint8_t *data, size_t len) { - const size_t buffer_size = len + 1; - if (buffer_size > PREF_MAX_BUFFER_SIZE) + if (len >= PREF_MAX_BUFFER_SIZE) return false; + const size_t buffer_size = len + 1; uint8_t buffer[PREF_MAX_BUFFER_SIZE]; for (size_t i = 0; i < buffer_size; i++) { diff --git a/esphome/components/rp2/printf_stubs.cpp b/esphome/components/rp2/printf_stubs.cpp index bf03565f30..47cf30b263 100644 --- a/esphome/components/rp2/printf_stubs.cpp +++ b/esphome/components/rp2/printf_stubs.cpp @@ -33,8 +33,8 @@ static int write_printf_buffer(FILE *stream, char *buf, int len) { if (write_len >= PRINTF_BUFFER_SIZE) { fwrite(buf, 1, PRINTF_BUFFER_SIZE - 1, stream); // Use fwrite for the message to avoid recursive __wrap_printf call - static const char msg[] = "\nprintf buffer overflow\n"; - fwrite(msg, 1, sizeof(msg) - 1, stream); + static const char MSG[] = "\nprintf buffer overflow\n"; + fwrite(MSG, 1, sizeof(MSG) - 1, stream); abort(); } if (fwrite(buf, 1, write_len, stream) < write_len || ferror(stream)) { diff --git a/esphome/components/rp2040_ble/rp2040_ble.cpp b/esphome/components/rp2040_ble/rp2040_ble.cpp index 4125da7ec0..f3896f7b9c 100644 --- a/esphome/components/rp2040_ble/rp2040_ble.cpp +++ b/esphome/components/rp2040_ble/rp2040_ble.cpp @@ -35,10 +35,10 @@ void RP2040BLE::enable() { l2cap_init(); sm_init(); - this->hci_event_callback_registration_.callback = &RP2040BLE::packet_handler_; + this->hci_event_callback_registration_.callback = &RP2040BLE::packet_handler; hci_add_event_handler(&this->hci_event_callback_registration_); - this->sm_event_callback_registration_.callback = &RP2040BLE::packet_handler_; + this->sm_event_callback_registration_.callback = &RP2040BLE::packet_handler; sm_add_event_handler(&this->sm_event_callback_registration_); this->btstack_initialized_ = true; @@ -48,7 +48,7 @@ void RP2040BLE::enable() { } void RP2040BLE::disable() { - if (this->state_ == BLEComponentState::DISABLED || this->state_ == BLEComponentState::OFF) { + if (this->state_ == BLEComponentState::DISABLED || this->state_ == BLEComponentState::STATE_OFF) { return; } @@ -70,7 +70,7 @@ void RP2040BLE::loop() { static const char *state_to_str(BLEComponentState state) { switch (state) { - case BLEComponentState::OFF: + case BLEComponentState::STATE_OFF: return "OFF"; case BLEComponentState::ENABLING: return "ENABLING"; @@ -95,7 +95,7 @@ void RP2040BLE::dump_config() { float RP2040BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; } -void RP2040BLE::packet_handler_(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) { +void RP2040BLE::packet_handler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size) { if (global_ble == nullptr) { return; } diff --git a/esphome/components/rp2040_ble/rp2040_ble.h b/esphome/components/rp2040_ble/rp2040_ble.h index 885e49f690..a77b5fc26c 100644 --- a/esphome/components/rp2040_ble/rp2040_ble.h +++ b/esphome/components/rp2040_ble/rp2040_ble.h @@ -11,7 +11,7 @@ namespace esphome::rp2040_ble { enum class BLEComponentState : uint8_t { - OFF = 0, + STATE_OFF = 0, ENABLING, ACTIVE, DISABLING, @@ -32,12 +32,12 @@ class RP2040BLE final : public Component { void set_enable_on_boot(bool enable_on_boot) { this->enable_on_boot_ = enable_on_boot; } protected: - static void packet_handler_(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size); + static void packet_handler(uint8_t type, uint16_t channel, uint8_t *packet, uint16_t size); btstack_packet_callback_registration_t hci_event_callback_registration_{}; btstack_packet_callback_registration_t sm_event_callback_registration_{}; - BLEComponentState state_{BLEComponentState::OFF}; + BLEComponentState state_{BLEComponentState::STATE_OFF}; bool enable_on_boot_{true}; bool btstack_initialized_{false}; bool active_logged_{false}; diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.cpp b/esphome/components/rp2040_pio_led_strip/led_strip.cpp index b9c0a9c257..cf7041931e 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.cpp +++ b/esphome/components/rp2040_pio_led_strip/led_strip.cpp @@ -14,26 +14,15 @@ namespace esphome::rp2040_pio_led_strip { -static const char *TAG = "rp2040_pio_led_strip"; - -static uint8_t num_instance_[2] = {0, 0}; -static std::map chipset_offsets_ = { - {CHIPSET_WS2812, 0}, {CHIPSET_WS2812B, 0}, {CHIPSET_SK6812, 0}, {CHIPSET_SM16703, 0}, {CHIPSET_CUSTOM, 0}, -}; -static std::map conf_count_ = { - {CHIPSET_WS2812, false}, {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false}, - {CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false}, -}; -static bool dma_chan_active_[12]; -static struct semaphore dma_write_complete_sem_[12]; +static const char *const TAG = "rp2040_pio_led_strip"; // DMA interrupt service routine -void RP2040PIOLEDStripLightOutput::dma_write_complete_handler_() { +void RP2040PIOLEDStripLightOutput::dma_write_complete_handler() { uint32_t channel = dma_hw->ints0; for (uint dma_chan = 0; dma_chan < 12; ++dma_chan) { - if (RP2040PIOLEDStripLightOutput::dma_chan_active_[dma_chan] && (channel & (1u << dma_chan))) { - dma_hw->ints0 = (1u << dma_chan); // Clear the interrupt - sem_release(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[dma_chan]); // Handle the interrupt + if (RP2040PIOLEDStripLightOutput::dma_chan_active[dma_chan] && (channel & (1u << dma_chan))) { + dma_hw->ints0 = (1u << dma_chan); // Clear the interrupt + sem_release(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem[dma_chan]); // Handle the interrupt } } } @@ -69,22 +58,22 @@ void RP2040PIOLEDStripLightOutput::setup() { // but there are only 4 state machines on each PIO so we can only have 4 strips per PIO uint offset = 0; - if (RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1] >= 4) { + if (RP2040PIOLEDStripLightOutput::num_instance[this->pio_ == pio0 ? 0 : 1] >= 4) { ESP_LOGE(TAG, "Too many instances of PIO program"); this->mark_failed(); return; } // keep track of how many instances of the PIO program are running on each PIO - RP2040PIOLEDStripLightOutput::num_instance_[this->pio_ == pio0 ? 0 : 1]++; + RP2040PIOLEDStripLightOutput::num_instance[this->pio_ == pio0 ? 0 : 1]++; // if there are multiple strips of the same chipset, we can reuse the same PIO program and save space - if (this->conf_count_[this->chipset_]) { - offset = RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_]; + if (RP2040PIOLEDStripLightOutput::conf_count[this->chipset_]) { + offset = RP2040PIOLEDStripLightOutput::chipset_offsets[this->chipset_]; } else { // Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it offset = pio_add_program(this->pio_, this->program_); - RP2040PIOLEDStripLightOutput::chipset_offsets_[this->chipset_] = offset; - RP2040PIOLEDStripLightOutput::conf_count_[this->chipset_] = true; + RP2040PIOLEDStripLightOutput::chipset_offsets[this->chipset_] = offset; + RP2040PIOLEDStripLightOutput::conf_count[this->chipset_] = true; } // Configure the state machine's PIO, and start it @@ -106,7 +95,7 @@ void RP2040PIOLEDStripLightOutput::setup() { } // Mark the DMA channel as active - RP2040PIOLEDStripLightOutput::dma_chan_active_[this->dma_chan_] = true; + RP2040PIOLEDStripLightOutput::dma_chan_active[this->dma_chan_] = true; this->dma_config_ = dma_channel_get_default_config(this->dma_chan_); channel_config_set_transfer_data_size( @@ -125,11 +114,11 @@ void RP2040PIOLEDStripLightOutput::setup() { ); // Initialize the semaphore for this DMA channel - sem_init(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_], 1, 1); + sem_init(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem[this->dma_chan_], 1, 1); - irq_set_exclusive_handler(DMA_IRQ_0, dma_write_complete_handler_); // after DMA all data, raise an interrupt - dma_channel_set_irq0_enabled(this->dma_chan_, true); // map DMA channel to interrupt - irq_set_enabled(DMA_IRQ_0, true); // enable interrupt + irq_set_exclusive_handler(DMA_IRQ_0, dma_write_complete_handler); // after DMA all data, raise an interrupt + dma_channel_set_irq0_enabled(this->dma_chan_, true); // map DMA channel to interrupt + irq_set_enabled(DMA_IRQ_0, true); // enable interrupt this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_); } @@ -148,12 +137,12 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) { } // the bits are already in the correct order for the pio program so we can just copy the buffer using DMA - sem_acquire_blocking(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem_[this->dma_chan_]); + sem_acquire_blocking(&RP2040PIOLEDStripLightOutput::dma_write_complete_sem[this->dma_chan_]); dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_()); } light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t index) const { - int32_t r = 0, g = 0, b = 0, w = 0; + int32_t r = 0, g = 0, b = 0; switch (this->rgb_order_) { case ORDER_RGB: r = 0; diff --git a/esphome/components/rp2040_pio_led_strip/led_strip.h b/esphome/components/rp2040_pio_led_strip/led_strip.h index b74dd14108..c499f0a7ca 100644 --- a/esphome/components/rp2040_pio_led_strip/led_strip.h +++ b/esphome/components/rp2040_pio_led_strip/led_strip.h @@ -95,7 +95,7 @@ class RP2040PIOLEDStripLightOutput final : public light::AddressableLight { size_t get_buffer_size_() const { return this->num_leds_ * (3 + this->is_rgbw_); } - static void dma_write_complete_handler_(); + static void dma_write_complete_handler(); uint8_t *buf_{nullptr}; uint8_t *effect_data_{nullptr}; @@ -119,11 +119,11 @@ class RP2040PIOLEDStripLightOutput final : public light::AddressableLight { init_fn init_; private: - inline static int num_instance_[2]; - inline static std::map conf_count_; - inline static std::map chipset_offsets_; - inline static bool dma_chan_active_[12]; - inline static struct semaphore dma_write_complete_sem_[12]; + inline static int num_instance[2]; + inline static std::map conf_count; + inline static std::map chipset_offsets; + inline static bool dma_chan_active[12]; + inline static struct semaphore dma_write_complete_sem[12]; }; } // namespace esphome::rp2040_pio_led_strip diff --git a/esphome/components/runtime_image/__init__.py b/esphome/components/runtime_image/__init__.py index 8db69aa53e..d8517d4493 100644 --- a/esphome/components/runtime_image/__init__.py +++ b/esphome/components/runtime_image/__init__.py @@ -82,7 +82,7 @@ class JPEGFormat(Format): # JPEGDEC uses ESP32-S3 SIMD optimizations (guarded by board-level # ARDUINO_ESP32S3_DEV define) that require esp-dsp headers. # On Arduino this overwrites the stub; on IDF it adds the component. - add_idf_component(name="espressif/esp-dsp", ref="1.7.1") + add_idf_component(name="espressif/esp-dsp", ref="1.8.2") class PNGFormat(Format): diff --git a/esphome/components/runtime_image/image_decoder.cpp b/esphome/components/runtime_image/image_decoder.cpp index 8d3320b5d1..f2c4f5c8cd 100644 --- a/esphome/components/runtime_image/image_decoder.cpp +++ b/esphome/components/runtime_image/image_decoder.cpp @@ -10,12 +10,16 @@ static const char *const TAG = "image_decoder"; bool ImageDecoder::set_size(int width, int height) { bool success = this->image_->resize(width, height) > 0; + this->size_valid_ = success; this->x_scale_ = static_cast(this->image_->get_buffer_width()) / width; this->y_scale_ = static_cast(this->image_->get_buffer_height()) / height; return success; } void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { + if (!this->size_valid_) { + return; + } auto width = std::min(this->image_->get_buffer_width(), static_cast(std::ceil((x + w) * this->x_scale_))); auto height = std::min(this->image_->get_buffer_height(), static_cast(std::ceil((y + h) * this->y_scale_))); for (int i = x * this->x_scale_; i < width; i++) { diff --git a/esphome/components/runtime_image/image_decoder.h b/esphome/components/runtime_image/image_decoder.h index c68ea5720b..6d351a10aa 100644 --- a/esphome/components/runtime_image/image_decoder.h +++ b/esphome/components/runtime_image/image_decoder.h @@ -108,6 +108,7 @@ class ImageDecoder { size_t decoded_bytes_ = 0; // Bytes processed so far double x_scale_ = 1.0; double y_scale_ = 1.0; + bool size_valid_ = true; // Last set_size() result; draw() no-ops while false }; } // namespace esphome::runtime_image diff --git a/esphome/components/runtime_image/png_decoder.cpp b/esphome/components/runtime_image/png_decoder.cpp index 12bce0d284..9501702711 100644 --- a/esphome/components/runtime_image/png_decoder.cpp +++ b/esphome/components/runtime_image/png_decoder.cpp @@ -96,6 +96,8 @@ int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { if (fed < 0) { ESP_LOGE(TAG, "Error decoding image: %s", pngle_error(this->pngle_)); return DECODE_ERROR_INTERNAL_DECODER_ERROR; + } else if (!this->size_valid_) { + return DECODE_ERROR_OUT_OF_MEMORY; } else { this->decoded_bytes_ += fed; } diff --git a/esphome/components/runtime_image/runtime_image.cpp b/esphome/components/runtime_image/runtime_image.cpp index 4c7f1bfb6f..8fe9be4c8c 100644 --- a/esphome/components/runtime_image/runtime_image.cpp +++ b/esphome/components/runtime_image/runtime_image.cpp @@ -3,7 +3,9 @@ #include "esphome/core/log.h" #include "esphome/core/helpers.h" #include +#include #include +#include #ifdef USE_RUNTIME_IMAGE_BMP #include "bmp_decoder.h" @@ -19,6 +21,13 @@ namespace esphome::runtime_image { static const char *const TAG = "runtime_image"; +// Widest supported format is 4 bytes/pixel, so 32767 * 32767 * 4 still fits a 32-bit size_t +static constexpr int MAX_IMAGE_DIMENSION = 32767; +static constexpr int MAX_IMAGE_BPP = 32; +static_assert((static_cast(MAX_IMAGE_BPP) * MAX_IMAGE_DIMENSION + 7) / 8 * MAX_IMAGE_DIMENSION <= + std::numeric_limits::max(), + "MAX_IMAGE_DIMENSION must keep the worst-case buffer size within size_t"); + inline bool is_color_on(const Color &color) { // This produces the most accurate monochrome conversion, but is slightly slower. // return (0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b) > 127; @@ -239,9 +248,15 @@ void RuntimeImage::release() { void RuntimeImage::release_buffer_() { if (this->buffer_) { - ESP_LOGV(TAG, "Releasing buffer of size %zu", this->get_buffer_size_(this->buffer_width_, this->buffer_height_)); - RAMAllocator allocator; - allocator.deallocate(this->buffer_, this->get_buffer_size_(this->buffer_width_, this->buffer_height_)); + if (this->external_buffer_) { + // The caller owns this memory and goes on using it after the image lets go of it. + ESP_LOGV(TAG, "Letting go of the external %dx%d buffer", this->buffer_width_, this->buffer_height_); + this->external_buffer_ = false; + } else { + ESP_LOGV(TAG, "Releasing buffer of size %zu", this->get_buffer_size(this->buffer_width_, this->buffer_height_)); + RAMAllocator allocator; + allocator.deallocate(this->buffer_, this->get_buffer_size(this->buffer_width_, this->buffer_height_)); + } this->buffer_ = nullptr; this->data_start_ = nullptr; this->width_ = 0; @@ -254,14 +269,46 @@ void RuntimeImage::release_buffer_() { } } -size_t RuntimeImage::resize_buffer_(int width, int height) { - size_t new_size = this->get_buffer_size_(width, height); +bool RuntimeImage::set_external_buffer(uint8_t *buffer, int width, int height) { + this->release_buffer_(); + if (buffer == nullptr || this->get_buffer_size(width, height) == 0) { + // Keep the released state rather than remembering a buffer that cannot be decoded into: an + // external buffer that is never handed back would otherwise block every later allocation. + ESP_LOGE(TAG, "Refusing an invalid external buffer for %dx%d", width, height); + return false; + } + this->buffer_ = buffer; + this->external_buffer_ = true; + this->buffer_width_ = width; + this->buffer_height_ = height; + return true; +} +size_t RuntimeImage::resize_buffer_(int width, int height) { + size_t new_size = this->get_buffer_size(width, height); + + // A buffer only ever exists with dimensions the image can decode at, so a match here means + // new_size is non-zero. Checking it before the invalid dimension case below lets the external + // buffer be let go of for every decode it cannot serve, not just for valid other dimensions. if (this->buffer_ && this->buffer_width_ == width && this->buffer_height_ == height) { // Buffer already allocated with correct size return new_size; } + if (this->external_buffer_) { + ESP_LOGE(TAG, "Image decoded to %dx%d, but the external buffer is %dx%d", width, height, this->buffer_width_, + this->buffer_height_); + // Let the buffer go rather than free memory that belongs to the caller. Dropping it also stops + // a decoder that ignores this failure from publishing a picture it never painted. + this->release_buffer_(); + return 0; + } + + if (new_size == 0) { + ESP_LOGE(TAG, "Refusing to allocate buffer for invalid image dimensions %dx%d", width, height); + return 0; + } + // Release old buffer if dimensions changed if (this->buffer_) { this->release_buffer_(); @@ -286,12 +333,16 @@ size_t RuntimeImage::resize_buffer_(int width, int height) { return new_size; } -size_t RuntimeImage::get_buffer_size_(int width, int height) const { +size_t RuntimeImage::get_buffer_size(int width, int height) const { + // Dimensions come from a remote image header; reject absurd values so the size math cannot overflow + if (width <= 0 || height <= 0 || width > MAX_IMAGE_DIMENSION || height > MAX_IMAGE_DIMENSION) { + return 0; + } if (this->get_type() == image::IMAGE_TYPE_RGB565 && this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) { // Add extra alpha channel for RGB565 with alpha - return width * height * 3; + return static_cast(width) * height * 3; } - return (this->get_bpp() * width + 7u) / 8u * height; + return (static_cast(this->get_bpp()) * width + 7u) / 8u * height; } int RuntimeImage::get_position_(int x, int y) const { return (x + y * this->buffer_width_) * this->get_bpp() / 8; } diff --git a/esphome/components/runtime_image/runtime_image.h b/esphome/components/runtime_image/runtime_image.h index 4bdcdcac9e..10ce980be2 100644 --- a/esphome/components/runtime_image/runtime_image.h +++ b/esphome/components/runtime_image/runtime_image.h @@ -121,9 +121,48 @@ class RuntimeImage : public image::Image { /** * @brief Release the image buffer and free memory. + * + * An external buffer is let go of rather than freed. */ void release(); + /** + * @brief Decode into a buffer the caller owns, instead of one allocated here. + * + * The image never frees an external buffer and never resizes it: a decode that needs other + * dimensions fails as if the allocation had failed, and the buffer is let go of so a decoder + * that ignores that failure cannot publish a picture it did not paint. The caller keeps the + * buffer alive for as long as anything can draw the image, and calls release() (or hands over + * another buffer) before reusing it. + * + * Hand a buffer over before every decode. The image lets go of one whenever a decode fails and + * whenever release() is called, and it does not remember that it ever had one: a decode that + * starts without a buffer allocates its own, which is the runtime allocation this method exists + * to avoid. + * + * The buffer is decoded into as it is handed over, so the caller owns its initial contents. + * Zero it first if anything can draw the image before a decode has painted every pixel. + * + * Do not hand a buffer over while is_decoding() is true. A running decoder keeps scaling values + * for the buffer it started with. + * + * A null buffer or dimensions the image cannot decode at are refused, leaving the image with + * no buffer at all. + * + * @param buffer Memory for a picture of the given size, at least get_buffer_size() bytes. + * @param width Width of the buffer in pixels. + * @param height Height of the buffer in pixels. + * @return true if the image took the buffer, false if it was refused. + */ + bool set_external_buffer(uint8_t *buffer, int width, int height); + + /** + * @brief Get the buffer size in bytes needed for a picture of the given dimensions. + * + * Returns 0 for dimensions the image cannot decode at. + */ + size_t get_buffer_size(int width, int height) const; + /** * @brief Set whether to allow progressive display during decode. * @@ -149,11 +188,6 @@ class RuntimeImage : public image::Image { */ void release_buffer_(); - /** - * @brief Get the buffer size in bytes for given dimensions. - */ - size_t get_buffer_size_(int width, int height) const; - /** * @brief Get the position in the buffer for a pixel. */ @@ -208,6 +242,8 @@ class RuntimeImage : public image::Image { * This is used to determine how to store 16 bit colors in the buffer. */ bool is_big_endian_{false}; + /** Whether buffer_ belongs to the caller, so it must not be freed or resized here. */ + bool external_buffer_{false}; }; } // namespace esphome::runtime_image diff --git a/esphome/components/safe_mode/__init__.py b/esphome/components/safe_mode/__init__.py index c11447e604..70096a56bc 100644 --- a/esphome/components/safe_mode/__init__.py +++ b/esphome/components/safe_mode/__init__.py @@ -16,6 +16,7 @@ from esphome.cpp_generator import RawExpression CODEOWNERS = ["@paulmonigatti", "@jsuanet", "@kbx81"] CONF_BOOT_IS_GOOD_AFTER = "boot_is_good_after" +CONF_BOOT_IS_GOOD_ON_SHUTDOWN = "boot_is_good_on_shutdown" CONF_ON_SAFE_MODE = "on_safe_mode" safe_mode_ns = cg.esphome_ns.namespace("safe_mode") @@ -37,6 +38,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional( CONF_BOOT_IS_GOOD_AFTER, default="1min" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_BOOT_IS_GOOD_ON_SHUTDOWN, default=True): cv.boolean, cv.Optional(CONF_DISABLED, default=False): cv.boolean, cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, cv.Optional( @@ -78,6 +80,9 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + if config[CONF_BOOT_IS_GOOD_ON_SHUTDOWN]: + cg.add_define("USE_SAFE_MODE_BOOT_IS_GOOD_ON_SHUTDOWN") + if on_safe_mode := config.get(CONF_ON_SAFE_MODE): cg.add_define("USE_SAFE_MODE_CALLBACK") cg.add_define("ESPHOME_SAFE_MODE_CALLBACK_COUNT", len(on_safe_mode)) diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp index 2eb1085ee5..ce029b4f55 100644 --- a/esphome/components/safe_mode/safe_mode.cpp +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -117,19 +117,31 @@ void SafeModeComponent::dump_config() { float SafeModeComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } -void SafeModeComponent::mark_successful() { - this->clean_rtc(); - this->boot_successful_ = true; -#if defined(USE_OTA_ROLLBACK) -// Mark OTA partition as valid to prevent rollback +#ifdef USE_OTA_ROLLBACK +void SafeModeComponent::confirm_app_image_() { + // Mark the running app image as valid so the bootloader will not roll back + // to the previously flashed image #if defined(USE_ZEPHYR) if (!boot_is_img_confirmed()) { boot_write_img_confirmed(); } #elif defined(USE_ESP32) - // Mark OTA partition as valid to prevent rollback - esp_ota_mark_app_valid_cancel_rollback(); + // esp_ota_mark_app_valid_cancel_rollback() acts on the partition selected + // for the next boot, not the running one. After an OTA update those differ: + // the new image is already selected, and marking it valid before it has ever + // booted would disable rollback protection for that update. + if (esp_ota_get_running_partition() == esp_ota_get_boot_partition()) { + esp_ota_mark_app_valid_cancel_rollback(); + } #endif +} +#endif + +void SafeModeComponent::mark_successful() { + this->clean_rtc(); + this->boot_successful_ = true; +#ifdef USE_OTA_ROLLBACK + this->confirm_app_image_(); #endif // Disable loop since we no longer need to check this->disable_loop(); @@ -266,6 +278,15 @@ void SafeModeComponent::clean_rtc() { void SafeModeComponent::on_safe_shutdown() { if (this->read_rtc_() != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) this->clean_rtc(); +#if defined(USE_OTA_ROLLBACK) && defined(USE_SAFE_MODE_BOOT_IS_GOOD_ON_SHUTDOWN) + // An orderly shutdown (deep sleep, restart, power off) means the firmware is + // functional, so confirm the running app image even if boot_is_good_after has + // not elapsed yet. Without this, a device that enters deep sleep shortly + // after waking would have every OTA update rolled back by the bootloader on + // the next wake. Can be turned off with boot_is_good_on_shutdown: false for + // strict rollback semantics. + this->confirm_app_image_(); +#endif } } // namespace esphome::safe_mode diff --git a/esphome/components/safe_mode/safe_mode.h b/esphome/components/safe_mode/safe_mode.h index d81b8a42d1..0633c92a78 100644 --- a/esphome/components/safe_mode/safe_mode.h +++ b/esphome/components/safe_mode/safe_mode.h @@ -42,6 +42,9 @@ class SafeModeComponent final : public Component { protected: void write_rtc_(uint32_t val); uint32_t read_rtc_(); +#ifdef USE_OTA_ROLLBACK + void confirm_app_image_(); +#endif // Group all 4-byte aligned members together to avoid padding uint32_t safe_mode_boot_is_good_after_{60000}; ///< The amount of time after which the boot is considered successful diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index a4fe6e7d35..1ebc7fa3d8 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -7,10 +7,10 @@ namespace esphome::sdm_meter { static const char *const TAG = "sdm_meter"; -static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t MODBUS_REGISTER_COUNT = 80; // 74 x 16-bit registers -void SDMMeter::on_modbus_data(const std::vector &data) { +void SDMMeter::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); if (data.size() < MODBUS_REGISTER_COUNT * 2) { ESP_LOGW(TAG, "Invalid size for SDMMeter!"); return; @@ -82,7 +82,7 @@ void SDMMeter::on_modbus_data(const std::vector &data) { this->export_reactive_energy_sensor_->publish_state(export_reactive_energy); } -void SDMMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } +void SDMMeter::update() { this->read_input_registers(0, MODBUS_REGISTER_COUNT); } void SDMMeter::dump_config() { ESP_LOGCONFIG(TAG, "SDM Meter:\n" diff --git a/esphome/components/sdm_meter/sdm_meter.h b/esphome/components/sdm_meter/sdm_meter.h index aa71fcaa47..e09b74bbc0 100644 --- a/esphome/components/sdm_meter/sdm_meter.h +++ b/esphome/components/sdm_meter/sdm_meter.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::sdm_meter { @@ -55,7 +55,7 @@ class SDMMeter final : public PollingComponent, public modbus::ModbusClientDevic void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; diff --git a/esphome/components/selec_meter/selec_meter.cpp b/esphome/components/selec_meter/selec_meter.cpp index f612b89934..688923d8e6 100644 --- a/esphome/components/selec_meter/selec_meter.cpp +++ b/esphome/components/selec_meter/selec_meter.cpp @@ -7,10 +7,10 @@ namespace esphome::selec_meter { static const char *const TAG = "selec_meter"; -static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t MODBUS_REGISTER_COUNT = 34; // 34 x 16-bit registers -void SelecMeter::on_modbus_data(const std::vector &data) { +void SelecMeter::on_response(std::span request_pdu, std::span response_pdu) { + auto data = modbus::helpers::server_pdu_payload(response_pdu); if (data.size() < MODBUS_REGISTER_COUNT * 2) { ESP_LOGW(TAG, "Invalid size for SelecMeter!"); return; @@ -81,7 +81,7 @@ void SelecMeter::on_modbus_data(const std::vector &data) { this->maximum_demand_apparent_power_sensor_->publish_state(maximum_demand_apparent_power); } -void SelecMeter::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } +void SelecMeter::update() { this->read_input_registers(0, MODBUS_REGISTER_COUNT); } void SelecMeter::dump_config() { ESP_LOGCONFIG(TAG, "SELEC Meter:\n" diff --git a/esphome/components/selec_meter/selec_meter.h b/esphome/components/selec_meter/selec_meter.h index c367d1d15d..5ae1f9bf99 100644 --- a/esphome/components/selec_meter/selec_meter.h +++ b/esphome/components/selec_meter/selec_meter.h @@ -4,7 +4,7 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/modbus/modbus.h" -#include +#include namespace esphome::selec_meter { @@ -37,7 +37,7 @@ class SelecMeter final : public PollingComponent, public modbus::ModbusClientDev void update() override; - void on_modbus_data(const std::vector &data) override; + void on_response(std::span request_pdu, std::span response_pdu) override; void dump_config() override; }; diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index 588650e630..f8df89ee33 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -101,25 +101,31 @@ void SEN5XComponent::setup() { ESP_LOGV(TAG, "Serial number %s", this->serial_number_); uint16_t raw_product_name[16]; - if (!this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) { - ESP_LOGE(TAG, "Failed to read product name"); - this->error_code_ = PRODUCT_NAME_FAILED; - this->mark_failed(); - return; + Sen5xType detected_type = Sen5xType::UNKNOWN; + if (this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) { + const char *product_name = sensirion_convert_to_string_in_place(raw_product_name, 16); + if (strncmp(product_name, "SEN50", 5) == 0) { + detected_type = Sen5xType::SEN50; + } else if (strncmp(product_name, "SEN54", 5) == 0) { + detected_type = Sen5xType::SEN54; + } else if (strncmp(product_name, "SEN55", 5) == 0) { + detected_type = Sen5xType::SEN55; + } } - const char *product_name = sensirion_convert_to_string_in_place(raw_product_name, 16); - if (strncmp(product_name, "SEN50", 5) == 0) { - this->type_ = Sen5xType::SEN50; - } else if (strncmp(product_name, "SEN54", 5) == 0) { - this->type_ = Sen5xType::SEN54; - } else if (strncmp(product_name, "SEN55", 5) == 0) { - this->type_ = Sen5xType::SEN55; - } else { + + if (this->model_override_.has_value()) { + if (detected_type != this->model_override_.value()) { + ESP_LOGW(TAG, "Detected %s, using %s", LOG_STR_ARG(type_to_string(detected_type)), + LOG_STR_ARG(type_to_string(this->model_override_.value()))); + } + this->type_ = this->model_override_.value(); + } else if (detected_type == Sen5xType::UNKNOWN) { this->type_ = Sen5xType::UNKNOWN; - ESP_LOGE(TAG, "Unknown product name: %.32s", product_name); this->error_code_ = PRODUCT_NAME_FAILED; this->mark_failed(); return; + } else { + this->type_ = detected_type; } ESP_LOGD(TAG, "Type: %s", LOG_STR_ARG(type_to_string(this->type_))); @@ -255,10 +261,12 @@ void SEN5XComponent::dump_config() { } } ESP_LOGCONFIG(TAG, - " Type: %s\n" + " Type: %s%s\n" " Firmware version: %d\n" " Serial number: %s", - LOG_STR_ARG(type_to_string(this->type_)), this->firmware_version_, this->serial_number_); + LOG_STR_ARG(type_to_string(this->type_)), + this->model_override_.has_value() ? LOG_STR_LITERAL(" (overridden)") : LOG_STR_LITERAL(""), + this->firmware_version_, this->serial_number_); if (this->auto_cleaning_interval_.has_value()) { ESP_LOGCONFIG(TAG, " Auto cleaning interval: %" PRId32 "s", this->auto_cleaning_interval_.value()); } diff --git a/esphome/components/sen5x/sen5x.h b/esphome/components/sen5x/sen5x.h index 6b5a1f8510..ed7689af52 100644 --- a/esphome/components/sen5x/sen5x.h +++ b/esphome/components/sen5x/sen5x.h @@ -95,6 +95,7 @@ class SEN5XComponent final : public PollingComponent, public sensirion_common::S temp_comp.time_constant = time_constant; this->temperature_compensation_ = temp_comp; } + void set_model(Sen5xType model) { this->model_override_ = model; } bool start_fan_cleaning(); protected: @@ -126,6 +127,7 @@ class SEN5XComponent final : public PollingComponent, public sensirion_common::S optional voc_tuning_params_; optional nox_tuning_params_; optional temperature_compensation_; + optional model_override_; ESPPreferenceObject pref_; }; diff --git a/esphome/components/sen5x/sensor.py b/esphome/components/sen5x/sensor.py index 480654ee1b..761a1885ea 100644 --- a/esphome/components/sen5x/sensor.py +++ b/esphome/components/sen5x/sensor.py @@ -2,6 +2,7 @@ from esphome import automation from esphome.automation import maybe_simple_id import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor +from esphome.components.const import CONF_NOX_INDEX, CONF_VOC_INDEX import esphome.config_validation as cv from esphome.const import ( CONF_ALGORITHM_TUNING, @@ -12,6 +13,7 @@ from esphome.const import ( CONF_INDEX_OFFSET, CONF_LEARNING_TIME_GAIN_HOURS, CONF_LEARNING_TIME_OFFSET_HOURS, + CONF_MODEL, CONF_NORMALIZED_OFFSET_SLOPE, CONF_NOX, CONF_OFFSET, @@ -39,6 +41,7 @@ from esphome.const import ( UNIT_MICROGRAMS_PER_CUBIC_METER, UNIT_PERCENT, ) +from esphome.types import ConfigType CODEOWNERS = ["@martgras"] DEPENDENCIES = ["i2c"] @@ -49,6 +52,7 @@ SEN5XComponent = sen5x_ns.class_( "SEN5XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice ) RhtAccelerationMode = sen5x_ns.enum("RhtAccelerationMode") +Sen5xType = sen5x_ns.enum("Sen5xType", is_class=True) CONF_ACCELERATION_MODE = "acceleration_mode" CONF_AUTO_CLEANING_INTERVAL = "auto_cleaning_interval" @@ -63,6 +67,12 @@ ACCELERATION_MODES = { "high": RhtAccelerationMode.HIGH_ACCELERATION, } +MODELS = { + "SEN50": Sen5xType.SEN50, + "SEN54": Sen5xType.SEN54, + "SEN55": Sen5xType.SEN55, +} + def _gas_sensor( *, @@ -113,7 +123,9 @@ def float_previously_pct(value): return value -CONFIG_SCHEMA = ( +CONFIG_SCHEMA = cv.All( + cv.rename_key(CONF_VOC, CONF_VOC_INDEX, removed_in="2027.2.0", component="sen5x"), + cv.rename_key(CONF_NOX, CONF_NOX_INDEX, removed_in="2027.2.0", component="sen5x"), cv.Schema( { cv.GenerateID(): cv.declare_id(SEN5XComponent), @@ -145,7 +157,7 @@ CONFIG_SCHEMA = ( state_class=STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_AUTO_CLEANING_INTERVAL): cv.update_interval, - cv.Optional(CONF_VOC): _gas_sensor( + cv.Optional(CONF_VOC_INDEX): _gas_sensor( index_offset=100, learning_time_offset=12, learning_time_gain=12, @@ -153,7 +165,7 @@ CONFIG_SCHEMA = ( std_initial=50, gain_factor=230, ), - cv.Optional(CONF_NOX): _gas_sensor( + cv.Optional(CONF_NOX_INDEX): _gas_sensor( index_offset=1, learning_time_offset=12, learning_time_gain=12, @@ -186,10 +198,11 @@ CONFIG_SCHEMA = ( } ), cv.Optional(CONF_ACCELERATION_MODE): cv.enum(ACCELERATION_MODES), + cv.Optional(CONF_MODEL): cv.enum(MODELS, upper=True), } ) .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x69)) + .extend(i2c.i2c_device_schema(0x69)), ) SENSOR_MAP = { @@ -197,8 +210,8 @@ SENSOR_MAP = { CONF_PM_2_5: "set_pm_2_5_sensor", CONF_PM_4_0: "set_pm_4_0_sensor", CONF_PM_10_0: "set_pm_10_0_sensor", - CONF_VOC: "set_voc_sensor", - CONF_NOX: "set_nox_sensor", + CONF_VOC_INDEX: "set_voc_sensor", + CONF_NOX_INDEX: "set_nox_sensor", CONF_TEMPERATURE: "set_temperature_sensor", CONF_HUMIDITY: "set_humidity_sensor", } @@ -210,7 +223,7 @@ SETTING_MAP = { } -async def to_code(config): +async def to_code(config: ConfigType) -> None: var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) await i2c.register_i2c_device(var, config) @@ -219,12 +232,15 @@ async def to_code(config): if cfg := config.get(key): cg.add(getattr(var, funcName)(cfg)) + if (model := config.get(CONF_MODEL)) is not None: + cg.add(var.set_model(model)) + for key, funcName in SENSOR_MAP.items(): if cfg := config.get(key): sens = await sensor.new_sensor(cfg) cg.add(getattr(var, funcName)(sens)) - if cfg := config.get(CONF_VOC, {}).get(CONF_ALGORITHM_TUNING): + if cfg := config.get(CONF_VOC_INDEX, {}).get(CONF_ALGORITHM_TUNING): cg.add( var.set_voc_algorithm_tuning( cfg[CONF_INDEX_OFFSET], @@ -235,7 +251,7 @@ async def to_code(config): cfg[CONF_GAIN_FACTOR], ) ) - if cfg := config.get(CONF_NOX, {}).get(CONF_ALGORITHM_TUNING): + if cfg := config.get(CONF_NOX_INDEX, {}).get(CONF_ALGORITHM_TUNING): cg.add( var.set_nox_algorithm_tuning( cfg[CONF_INDEX_OFFSET], diff --git a/esphome/components/sen6x/sensor.py b/esphome/components/sen6x/sensor.py index 5eb34add65..832a2188ee 100644 --- a/esphome/components/sen6x/sensor.py +++ b/esphome/components/sen6x/sensor.py @@ -1,5 +1,6 @@ import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor +from esphome.components.const import CONF_NOX_INDEX, CONF_VOC_INDEX import esphome.config_validation as cv from esphome.const import ( CONF_CO2, @@ -41,7 +42,10 @@ SEN6XComponent = sen6x_ns.class_( "SEN6XComponent", cg.PollingComponent, sensirion_common.SensirionI2CDevice ) -CONFIG_SCHEMA = ( + +CONFIG_SCHEMA = cv.All( + cv.rename_key(CONF_VOC, CONF_VOC_INDEX, removed_in="2027.2.0", component="sen6x"), + cv.rename_key(CONF_NOX, CONF_NOX_INDEX, removed_in="2027.2.0", component="sen6x"), cv.Schema( { cv.GenerateID(): cv.declare_id(SEN6XComponent), @@ -89,12 +93,12 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_VOC): sensor.sensor_schema( + cv.Optional(CONF_VOC_INDEX): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_NOX): sensor.sensor_schema( + cv.Optional(CONF_NOX_INDEX): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, @@ -115,7 +119,7 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x6B)) + .extend(i2c.i2c_device_schema(0x6B)), ) SENSOR_MAP = { @@ -125,8 +129,8 @@ SENSOR_MAP = { CONF_PM_10_0: "set_pm_10_0_sensor", CONF_TEMPERATURE: "set_temperature_sensor", CONF_HUMIDITY: "set_humidity_sensor", - CONF_VOC: "set_voc_sensor", - CONF_NOX: "set_nox_sensor", + CONF_VOC_INDEX: "set_voc_sensor", + CONF_NOX_INDEX: "set_nox_sensor", CONF_CO2: "set_co2_sensor", CONF_FORMALDEHYDE: "set_hcho_sensor", } diff --git a/esphome/components/sendspin/__init__.py b/esphome/components/sendspin/__init__.py index 97e7f4e22c..e20925f323 100644 --- a/esphome/components/sendspin/__init__.py +++ b/esphome/components/sendspin/__init__.py @@ -129,12 +129,12 @@ def _request_high_performance_networking(config: ConfigType) -> ConfigType: """ network.require_high_performance_networking() # Socket consumption varies by mode: - # - Server mode: 1 listening socket + 2 client connections (for handoff) + # - Server mode: 1 listening socket + 4 client connections (established connection, unproven connections, and a spare) # - Client mode: 1 outbound connection socket.consume_sockets( 1, "sendspin_websocket_server", socket.SocketType.TCP_LISTEN )(config) - socket.consume_sockets(2, "sendspin_websocket_server")(config) + socket.consume_sockets(4, "sendspin_websocket_server")(config) socket.consume_sockets(1, "sendspin_websocket_client")(config) wifi.enable_runtime_power_save_control() @@ -198,7 +198,7 @@ async def to_code(config: ConfigType) -> None: psram.request_external_task_stack() # sendspin-cpp library - esp32.add_idf_component(name="sendspin/sendspin-cpp", ref="0.6.1") + esp32.add_idf_component(name="sendspin/sendspin-cpp", ref="0.7.0") cg.add_define("USE_SENDSPIN", True) # for MDNS @@ -255,9 +255,6 @@ async def to_code(config: ConfigType) -> None: if psram_stack: psram.request_external_task_stack() - # Library defaults: priority 18 (one above httpd_priority 17 so the decoder is not - # starved by the HTTP server during the initial encoded-audio burst at stream start), - # decode buffer location PREFER_EXTERNAL. player_struct_fields = [ ("audio_formats", audio_format_structs), ("audio_buffer_capacity", player_cfg[CONF_BUFFER_SIZE]), diff --git a/esphome/components/sendspin/sendspin_hub.cpp b/esphome/components/sendspin/sendspin_hub.cpp index b95d95b2bc..04dbab0080 100644 --- a/esphome/components/sendspin/sendspin_hub.cpp +++ b/esphome/components/sendspin/sendspin_hub.cpp @@ -179,7 +179,12 @@ std::optional SendspinHub::load_last_server_hash() { void SendspinHub::send_client_command(sendspin::SendspinControllerCommand command, std::optional volume, std::optional mute) { if (this->is_ready()) { - this->controller_role_->send_command(command, volume, mute); + sendspin::ClientCommandControllerObject obj = { + .command = command, + .volume = volume, + .muted = mute, + }; + this->controller_role_->send_command(obj); } } diff --git a/esphome/components/sgp4x/sensor.py b/esphome/components/sgp4x/sensor.py index d407f20a4e..87ef050bc1 100644 --- a/esphome/components/sgp4x/sensor.py +++ b/esphome/components/sgp4x/sensor.py @@ -1,5 +1,6 @@ import esphome.codegen as cg from esphome.components import i2c, sensirion_common, sensor +from esphome.components.const import CONF_NOX_INDEX, CONF_VOC_INDEX import esphome.config_validation as cv from esphome.const import ( CONF_ALGORITHM_TUNING, @@ -35,9 +36,9 @@ CONF_HUMIDITY_SOURCE = "humidity_source" def validate_sensors(config): - if CONF_VOC not in config and CONF_NOX not in config: + if CONF_VOC_INDEX not in config and CONF_NOX_INDEX not in config: raise cv.Invalid( - f"At least one sensor is required. Define {CONF_VOC} and/or {CONF_NOX}" + f"At least one sensor is required. Define {CONF_VOC_INDEX} and/or {CONF_NOX_INDEX}" ) return config @@ -65,15 +66,17 @@ VOC_SENSOR = _gas_sensor_schema(100) NOX_SENSOR = _gas_sensor_schema(1) CONFIG_SCHEMA = cv.All( + cv.rename_key(CONF_VOC, CONF_VOC_INDEX, removed_in="2027.2.0", component="sgp4x"), + cv.rename_key(CONF_NOX, CONF_NOX_INDEX, removed_in="2027.2.0", component="sgp4x"), cv.Schema( { cv.GenerateID(): cv.declare_id(SGP4xComponent), - cv.Optional(CONF_VOC): sensor.sensor_schema( + cv.Optional(CONF_VOC_INDEX): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, ).extend(VOC_SENSOR), - cv.Optional(CONF_NOX): sensor.sensor_schema( + cv.Optional(CONF_NOX_INDEX): sensor.sensor_schema( icon=ICON_RADIATOR, accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, @@ -107,11 +110,11 @@ async def to_code(config): cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE])) - if CONF_VOC in config: - sens = await sensor.new_sensor(config[CONF_VOC]) + if CONF_VOC_INDEX in config: + sens = await sensor.new_sensor(config[CONF_VOC_INDEX]) cg.add(var.set_voc_sensor(sens)) - if CONF_ALGORITHM_TUNING in config[CONF_VOC]: - cfg = config[CONF_VOC][CONF_ALGORITHM_TUNING] + if CONF_ALGORITHM_TUNING in config[CONF_VOC_INDEX]: + cfg = config[CONF_VOC_INDEX][CONF_ALGORITHM_TUNING] cg.add( var.set_voc_algorithm_tuning( cfg[CONF_INDEX_OFFSET], @@ -123,11 +126,11 @@ async def to_code(config): ) ) - if CONF_NOX in config: - sens = await sensor.new_sensor(config[CONF_NOX]) + if CONF_NOX_INDEX in config: + sens = await sensor.new_sensor(config[CONF_NOX_INDEX]) cg.add(var.set_nox_sensor(sens)) - if CONF_ALGORITHM_TUNING in config[CONF_NOX]: - cfg = config[CONF_NOX][CONF_ALGORITHM_TUNING] + if CONF_ALGORITHM_TUNING in config[CONF_NOX_INDEX]: + cfg = config[CONF_NOX_INDEX][CONF_ALGORITHM_TUNING] cg.add( var.set_nox_algorithm_tuning( cfg[CONF_INDEX_OFFSET], diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index db56bd13f0..bc6fe794a0 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -18,7 +18,7 @@ void SGP4xComponent::setup() { this->mark_failed(); return; } - this->serial_number_ = (uint64_t(raw_serial_number[0]) << 24) | (uint64_t(raw_serial_number[1]) << 16) | + this->serial_number_ = (uint64_t(raw_serial_number[0]) << 32) | (uint64_t(raw_serial_number[1]) << 16) | (uint64_t(raw_serial_number[2])); ESP_LOGD(TAG, "Serial number: %" PRIu64, this->serial_number_); @@ -32,7 +32,6 @@ void SGP4xComponent::setup() { featureset &= 0x1FF; if (featureset == SGP40_FEATURESET) { this->sgp_type_ = SGP40; - this->self_test_time_ = SPG40_SELFTEST_TIME; this->measure_time_ = SGP40_MEASURE_TIME; if (this->nox_sensor_) { ESP_LOGE(TAG, "SGP41 required for NOx, disabling NOx sensor"); @@ -42,7 +41,6 @@ void SGP4xComponent::setup() { } } else if (featureset == SGP41_FEATURESET) { this->sgp_type_ = SGP41; - this->self_test_time_ = SPG41_SELFTEST_TIME; this->measure_time_ = SGP41_MEASURE_TIME; } else { ESP_LOGD(TAG, "Unknown feature set 0x%0X", featureset); @@ -52,29 +50,6 @@ void SGP4xComponent::setup() { ESP_LOGD(TAG, "Version 0x%0X", featureset); - if (this->store_baseline_) { - // Hash with config hash, version, and serial number - // This ensures the baseline storage is cleared after OTA - // Serial numbers are unique to each sensor, so multiple sensors can be used without conflict - uint32_t hash = fnv1a_hash_extend(App.get_config_version_hash(), this->serial_number_); - this->pref_ = global_preferences->make_preference(hash, true); - - if (this->pref_.load(&this->voc_baselines_storage_)) { - this->voc_state0_ = this->voc_baselines_storage_.state0; - this->voc_state1_ = this->voc_baselines_storage_.state1; - ESP_LOGV(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, - this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); - } - - // Initialize storage timestamp - this->seconds_since_last_store_ = 0; - - if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) { - ESP_LOGV(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, - this->voc_baselines_storage_.state0, voc_baselines_storage_.state1); - voc_algorithm_.set_states(this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); - } - } if (this->voc_sensor_ && this->voc_tuning_params_.has_value()) { voc_algorithm_.set_tuning_parameters( voc_tuning_params_.value().index_offset, voc_tuning_params_.value().learning_time_offset_hours, @@ -89,6 +64,33 @@ void SGP4xComponent::setup() { nox_tuning_params_.value().std_initial, nox_tuning_params_.value().gain_factor); } + if (this->store_baseline_) { + // Initialize storage timestamp + this->seconds_since_last_store_ = 0; + + // Hash with config hash, version, and serial number + // This ensures the baseline storage is cleared after OTA + // Serial numbers are unique to each sensor, so multiple sensors can be used without conflict + uint32_t hash = fnv1a_hash_extend(App.get_config_version_hash(), this->serial_number_); + this->pref_ = global_preferences->make_preference(hash, true); + + if (this->pref_.load(&this->voc_baselines_storage_)) { + this->voc_state0_ = this->voc_baselines_storage_.state0; + this->voc_state1_ = this->voc_baselines_storage_.state1; + + ESP_LOGV(TAG, "Loaded VOC baseline state0: %f, state1: %f", this->voc_baselines_storage_.state0, + this->voc_baselines_storage_.state1); + + if (std::isnormal(this->voc_baselines_storage_.state0) && std::isnormal(this->voc_baselines_storage_.state1)) { + ESP_LOGV(TAG, "Setting VOC baseline from save state0: %f, state1: %f", this->voc_baselines_storage_.state0, + this->voc_baselines_storage_.state1); + // Sensirion advises restoring states only after interruptions shorter than 10 minutes; with no way to know + // how long the device was off, restoring a stale state still beats a fresh 12-hour learning phase + voc_algorithm_.set_states(this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); + } + } + } + this->self_test_(); /* The official spec for this sensor at @@ -112,11 +114,15 @@ void SGP4xComponent::self_test_() { this->error_code_ = COMMUNICATION_FAILED; ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); this->mark_failed(); + return; } - this->set_timeout(this->self_test_time_, [this]() { + this->set_timeout(SGP4X_SELF_TEST_TIME, [this]() { uint16_t reply = 0; - if (!this->read_data(reply) || (reply != 0xD400)) { + // SGP40: MSB is 0xD4 on success, LSB is undefined; SGP41: MSB is undefined, LSB bits 0/1 flag VOC/NOx pixel + // failures + bool passed = this->read_data(reply) && (this->sgp_type_ == SGP41 ? (reply & 0x0003) == 0 : (reply >> 8) == 0xD4); + if (!passed) { this->error_code_ = SELF_TEST_FAILED; ESP_LOGW(TAG, "Self-test failed (0x%X)", reply); this->mark_failed(); @@ -134,19 +140,19 @@ void SGP4xComponent::update_gas_indices_() { if (this->nox_sensor_ != nullptr) this->nox_index_ = this->nox_algorithm_.process(this->nox_sraw_); ESP_LOGV(TAG, "VOC: %" PRId32 ", NOx: %" PRId32, this->voc_index_, this->nox_index_); - // Store baselines after defined interval or if the difference between current and stored baseline becomes too - // much + // Store baselines once the minimum interval has passed and the state has drifted from the stored copy; + // both conditions limit flash wear if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) { this->voc_algorithm_.get_states(this->voc_state0_, this->voc_state1_); - if (std::abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF || - std::abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF) { + if (std::abs(this->voc_baselines_storage_.state0 - this->voc_state0_) > MAXIMUM_STORAGE_DIFF_STATE0 || + std::abs(this->voc_baselines_storage_.state1 - this->voc_state1_) > MAXIMUM_STORAGE_DIFF_STATE1) { this->seconds_since_last_store_ = 0; this->voc_baselines_storage_.state0 = this->voc_state0_; this->voc_baselines_storage_.state1 = this->voc_state1_; if (this->pref_.save(&this->voc_baselines_storage_)) { - ESP_LOGV(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32, - this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1); + ESP_LOGV(TAG, "Stored VOC baseline state0: %f, state1: %f", this->voc_baselines_storage_.state0, + this->voc_baselines_storage_.state1); } else { ESP_LOGW(TAG, "Storing VOC baselines failed"); } @@ -185,27 +191,28 @@ void SGP4xComponent::measure_raw_() { uint16_t command; uint16_t data[2]; size_t response_words; - // Use SGP40 measure command if we don't care about NOx - if (nox_sensor_ == nullptr) { + if (this->sgp_type_ == SGP40) { command = SGP40_CMD_MEASURE_RAW; response_words = 1; + } else if (this->nox_conditioning_start_.has_value() && millis() - *this->nox_conditioning_start_ < 10000) { + // SGP41 must run the NOx conditioning command for the first 10 seconds + command = SGP41_CMD_NOX_CONDITIONING; + response_words = 1; } else { - // SGP41 sensor must use NOx conditioning command for the first 10 seconds - if (this->nox_conditioning_start_.has_value() && millis() - *this->nox_conditioning_start_ < 10000) { - command = SGP41_CMD_NOX_CONDITIONING; - response_words = 1; - } else { - this->nox_conditioning_start_.reset(); - command = SGP41_CMD_MEASURE_RAW; - response_words = 2; - } + this->nox_conditioning_start_.reset(); + command = SGP41_CMD_MEASURE_RAW; + response_words = 2; + } + if (command == SGP41_CMD_NOX_CONDITIONING) { + // Conditioning requires the default parameters (compensation disabled) + data[0] = 0x8000; + data[1] = 0x6666; + } else { + // first parameter are the relative humidity ticks + data[0] = (uint16_t) std::llround((humidity * 65535) / 100); + // second parameter are the temperature ticks + data[1] = (uint16_t) (((temperature + 45) * 65535) / 175); } - uint16_t rhticks = (uint16_t) std::llround((humidity * 65535) / 100); - uint16_t tempticks = (uint16_t) (((temperature + 45) * 65535) / 175); - // first parameter are the relative humidity ticks - data[0] = rhticks; - // secomd parameter are the temperature ticks - data[1] = tempticks; if (!this->write_command(command, data, 2)) { ESP_LOGD(TAG, "write error (%d)", this->last_error_); @@ -232,7 +239,9 @@ void SGP4xComponent::measure_raw_() { void SGP4xComponent::take_sample() { if (!this->self_test_complete_) return; - this->seconds_since_last_store_ += 1; + if (this->store_baseline_) { + this->seconds_since_last_store_ += 1; + } this->measure_raw_(); } @@ -275,7 +284,8 @@ void SGP4xComponent::dump_config() { " Type: %s\n" " Serial number: %" PRIu64 "\n" " Minimum Samples: %f", - sgp_type_ == SGP41 ? "SGP41" : "SPG40", this->serial_number_, GasIndexAlgorithm_INITIAL_BLACKOUT); + this->sgp_type_ == SGP41 ? "SGP41" : "SGP40", this->serial_number_, + GasIndexAlgorithm_INITIAL_BLACKOUT); } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index a40188e629..2aaf06601b 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -14,9 +14,9 @@ namespace esphome::sgp4x { struct SGP4xBaselines { - int32_t state0; - int32_t state1; -} PACKED; // NOLINT + float state0; + float state1; +}; enum SgpType { SGP40, SGP41 }; @@ -39,17 +39,19 @@ static const uint16_t SGP4X_CMD_SELF_TEST = 0x280e; static const uint16_t SGP40_CMD_MEASURE_RAW = 0x260F; static const uint16_t SGP41_CMD_MEASURE_RAW = 0x2619; static const uint16_t SGP41_CMD_NOX_CONDITIONING = 0x2612; -static const uint8_t SGP41_SUBCMD_NOX_CONDITIONING = 0x12; // Shortest time interval of 3H for storing baseline values. // Prevents wear of the flash because of too many write operations const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800; -static const uint16_t SPG40_SELFTEST_TIME = 250; // 250 ms for self test -static const uint16_t SPG41_SELFTEST_TIME = 320; // 320 ms for self test +static const uint16_t SGP4X_SELF_TEST_TIME = 320; // maximum self-test duration for both SGP40 and SGP41 static const uint16_t SGP40_MEASURE_TIME = 30; static const uint16_t SGP41_MEASURE_TIME = 55; -// Store anyway if the baseline difference exceeds the max storage diff value -const float MAXIMUM_STORAGE_DIFF = 50.0f; +// Once the store interval has passed, store only if the baseline drifted from the stored copy by more than these +// state0 is mean of variance estimator, hence can have larger absolute values and a larger diff threshold +const float MAXIMUM_STORAGE_DIFF_STATE0 = 50.0f; +// state1 is std of variance estimator, so it typically has smaller absolute values than state0, hence we use a smaller +// diff threshold +const float MAXIMUM_STORAGE_DIFF_STATE1 = 5.0f; class SGP4xComponent; @@ -111,7 +113,6 @@ class SGP4xComponent final : public PollingComponent, uint64_t serial_number_; bool self_test_complete_; - uint16_t self_test_time_; sensor::Sensor *voc_sensor_{nullptr}; VOCGasIndexAlgorithm voc_algorithm_; diff --git a/esphome/components/spi/spi.h b/esphome/components/spi/spi.h index c038426f61..0358ed278f 100644 --- a/esphome/components/spi/spi.h +++ b/esphome/components/spi/spi.h @@ -13,7 +13,7 @@ using SPIInterface = spi_host_device_t; -#elif defined(USE_ARDUINO) +#elif defined(USE_ARDUINO) && !defined(USE_LIBRETINY) #include diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index 8ff908fe7a..00c864a217 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -41,17 +41,17 @@ void I2CSSD1306::command(uint8_t value) { this->write_byte(0x00, value); } void HOT I2CSSD1306::write_display_data() { if (this->is_sh1106_() || this->is_sh1107_()) { uint32_t i = 0; + // Some panels wire their visible columns to a window of the controller RAM + // that does not start at column 0 (e.g. SH1107 M5Stack Unit OLED needs offset_x: 32). + // SH1106 keeps its historical 0x02 base column on top of any offset. + uint8_t start_column = this->offset_x_; + if (this->is_sh1106_()) { + start_column += 0x02; + } for (uint8_t page = 0; page < (uint8_t) this->get_height_internal() / 8; page++) { - this->command(0xB0 + page); // row - if (this->is_sh1106_()) { - this->command(0x02); // lower column - 0x02 is historical SH1106 value - } else { - // Other SH1107 drivers use 0x00 - // Column values dont change and it seems they can be set only once, - // but we follow SH1106 implementation and resend them - this->command(0x00); - } - this->command(0x10); // higher column + this->command(0xB0 + page); // row + this->command(start_column & 0x0F); // lower column + this->command(0x10 | (start_column >> 4)); // higher column for (uint8_t x = 0; x < (uint8_t) this->get_width_internal() / 16; x++) { uint8_t data[16]; for (uint8_t &j : data) diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index 5c9369f1a2..0534deeb03 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -38,14 +38,17 @@ void SPISSD1306::command(uint8_t value) { } void HOT SPISSD1306::write_display_data() { if (this->is_sh1106_() || this->is_sh1107_()) { + // Some panels wire their visible columns to a window of the controller RAM + // that does not start at column 0 (e.g. SH1107 M5Stack Unit OLED needs offset_x: 32). + // SH1106 keeps its historical 0x02 base column on top of any offset. + uint8_t start_column = this->offset_x_; + if (this->is_sh1106_()) { + start_column += 0x02; + } for (uint8_t y = 0; y < (uint8_t) this->get_height_internal() / 8; y++) { this->command(0xB0 + y); - if (this->is_sh1106_()) { - this->command(0x02); - } else { - this->command(0x00); - } - this->command(0x10); + this->command(start_column & 0x0F); // lower column + this->command(0x10 | (start_column >> 4)); // higher column this->dc_pin_->digital_write(true); for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x++) { this->enable(); diff --git a/esphome/components/teleinfo/teleinfo.cpp b/esphome/components/teleinfo/teleinfo.cpp index cd2ddbbb38..e00895d162 100644 --- a/esphome/components/teleinfo/teleinfo.cpp +++ b/esphome/components/teleinfo/teleinfo.cpp @@ -57,7 +57,7 @@ bool TeleInfo::read_chars_until_(bool drop, uint8_t c) { */ if (buf_index_ >= (MAX_BUF_SIZE - 1)) { ESP_LOGW(TAG, "Internal buffer full"); - state_ = OFF; + state_ = STATE_OFF; return false; } buf_[buf_index_++] = received; @@ -65,18 +65,18 @@ bool TeleInfo::read_chars_until_(bool drop, uint8_t c) { return false; } -void TeleInfo::setup() { state_ = OFF; } +void TeleInfo::setup() { state_ = STATE_OFF; } void TeleInfo::update() { - if (state_ == OFF) { + if (state_ == STATE_OFF) { buf_index_ = 0; - state_ = ON; + state_ = STATE_ON; } } void TeleInfo::loop() { switch (state_) { - case OFF: + case STATE_OFF: break; - case ON: + case STATE_ON: /* Dequeue chars until start frame (0x2) */ if (read_chars_until_(true, 0x2)) state_ = START_FRAME_RECEIVED; @@ -173,7 +173,7 @@ void TeleInfo::loop() { publish_value_(std::string(tag_), std::string(val_)); } - state_ = OFF; + state_ = STATE_OFF; break; } } diff --git a/esphome/components/teleinfo/teleinfo.h b/esphome/components/teleinfo/teleinfo.h index 83ea1474f2..4aab3bf2cd 100644 --- a/esphome/components/teleinfo/teleinfo.h +++ b/esphome/components/teleinfo/teleinfo.h @@ -40,11 +40,11 @@ class TeleInfo final : public PollingComponent, public uart::UARTDevice { char val_[MAX_VAL_SIZE]; char timestamp_[MAX_TIMESTAMP_SIZE]; enum State { - OFF, - ON, + STATE_OFF, + STATE_ON, START_FRAME_RECEIVED, END_FRAME_RECEIVED, - } state_{OFF}; + } state_{STATE_OFF}; bool read_chars_until_(bool drop, uint8_t c); bool check_crc_(const char *grp, const char *grp_end); void publish_value_(const std::string &tag, const std::string &val); diff --git a/esphome/components/uart/uart_component_libretiny.cpp b/esphome/components/uart/uart_component_libretiny.cpp index 4172e7c164..fbf0c20ded 100644 --- a/esphome/components/uart/uart_component_libretiny.cpp +++ b/esphome/components/uart/uart_component_libretiny.cpp @@ -18,7 +18,7 @@ namespace esphome::uart { static const char *const TAG = "uart.lt"; -static const char *UART_TYPE[] = { +static const char *const UART_TYPE[] = { "hardware", "software", }; @@ -45,19 +45,19 @@ uint16_t LibreTinyUARTComponent::get_config() { } void LibreTinyUARTComponent::setup() { - int8_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin(); - int8_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin(); - bool tx_inverted = tx_pin_ != nullptr && tx_pin_->is_inverted(); - bool rx_inverted = rx_pin_ != nullptr && rx_pin_->is_inverted(); + int16_t tx_pin = tx_pin_ == nullptr ? -1 : tx_pin_->get_pin(); + int16_t rx_pin = rx_pin_ == nullptr ? -1 : rx_pin_->get_pin(); - auto shouldFallbackToSoftwareSerial = [&]() -> bool { - auto hasFlags = [](InternalGPIOPin *pin, const gpio::Flags mask) -> bool { + auto should_fallback_to_software_serial = [&]() -> bool { + auto has_flags = [](InternalGPIOPin *pin, const gpio::Flags mask) -> bool { return pin && (pin->get_flags() & mask) != gpio::Flags::FLAG_NONE; }; - if (hasFlags(this->tx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN) || - hasFlags(this->rx_pin_, gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN)) { + if (has_flags(this->tx_pin_, + gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN) || + has_flags(this->rx_pin_, + gpio::Flags::FLAG_OPEN_DRAIN | gpio::Flags::FLAG_PULLUP | gpio::Flags::FLAG_PULLDOWN)) { #if LT_ARD_HAS_SOFTSERIAL - ESP_LOGI(TAG, "Pins has flags set. Using Software Serial"); + ESP_LOGI(TAG, "Pins have flags set. Using Software Serial"); return true; #else ESP_LOGW(TAG, "Pin flags are set but not supported for hardware serial. Ignoring"); @@ -66,25 +66,26 @@ void LibreTinyUARTComponent::setup() { return false; }; - if (false) + if (false) { // NOLINT(readability-simplify-boolean-expr) return; + } #if LT_HW_UART0 else if ((tx_pin == -1 || tx_pin == PIN_SERIAL0_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL0_RX) && - !shouldFallbackToSoftwareSerial()) { + !should_fallback_to_software_serial()) { this->serial_ = &Serial0; this->hardware_idx_ = 0; } #endif #if LT_HW_UART1 else if ((tx_pin == -1 || tx_pin == PIN_SERIAL1_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL1_RX) && - !shouldFallbackToSoftwareSerial()) { + !should_fallback_to_software_serial()) { this->serial_ = &Serial1; this->hardware_idx_ = 1; } #endif #if LT_HW_UART2 else if ((tx_pin == -1 || tx_pin == PIN_SERIAL2_TX) && (rx_pin == -1 || rx_pin == PIN_SERIAL2_RX) && - !shouldFallbackToSoftwareSerial()) { + !should_fallback_to_software_serial()) { this->serial_ = &Serial2; this->hardware_idx_ = 2; } @@ -97,6 +98,8 @@ void LibreTinyUARTComponent::setup() { if (this->tx_pin_ && this->rx_pin_ != this->tx_pin_) { this->tx_pin_->setup(); } + bool tx_inverted = tx_pin_ != nullptr && tx_pin_->is_inverted(); + bool rx_inverted = rx_pin_ != nullptr && rx_pin_->is_inverted(); this->serial_ = new SoftwareSerial(rx_pin, tx_pin, rx_inverted || tx_inverted); #else this->serial_ = &Serial; @@ -133,7 +136,7 @@ void LibreTinyUARTComponent::dump_config() { ESP_LOGCONFIG(TAG, " RX Buffer Size: %u", this->rx_buffer_size_); } ESP_LOGCONFIG(TAG, - " Baud Rate: %u baud\n" + " Baud Rate: %" PRIu32 " baud\n" " Data Bits: %u\n" " Parity: %s\n" " Stop bits: %u", diff --git a/esphome/components/veml3235/sensor.py b/esphome/components/veml3235/sensor.py index 862fac302f..08d3685d1f 100644 --- a/esphome/components/veml3235/sensor.py +++ b/esphome/components/veml3235/sensor.py @@ -22,13 +22,13 @@ veml3235_ns = cg.esphome_ns.namespace("veml3235") VEML3235Sensor = veml3235_ns.class_( "VEML3235Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice ) -VEML3235IntegrationTime = veml3235_ns.enum("VEML3235IntegrationTime") +VEML3235ComponentIntegrationTime = veml3235_ns.enum("VEML3235ComponentIntegrationTime") VEML3235_INTEGRATION_TIMES = { - "50ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_50MS, - "100ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_100MS, - "200ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_200MS, - "400ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_400MS, - "800ms": VEML3235IntegrationTime.VEML3235_INTEGRATION_TIME_800MS, + "50ms": VEML3235ComponentIntegrationTime.VEML3235_INTEGRATION_TIME_50MS, + "100ms": VEML3235ComponentIntegrationTime.VEML3235_INTEGRATION_TIME_100MS, + "200ms": VEML3235ComponentIntegrationTime.VEML3235_INTEGRATION_TIME_200MS, + "400ms": VEML3235ComponentIntegrationTime.VEML3235_INTEGRATION_TIME_400MS, + "800ms": VEML3235ComponentIntegrationTime.VEML3235_INTEGRATION_TIME_800MS, } VEML3235ComponentDigitalGain = veml3235_ns.enum("VEML3235ComponentDigitalGain") DIGITAL_GAINS = { @@ -40,10 +40,18 @@ GAINS = { "1X": VEML3235ComponentGain.VEML3235_GAIN_1X, "2X": VEML3235ComponentGain.VEML3235_GAIN_2X, "4X": VEML3235ComponentGain.VEML3235_GAIN_4X, - "AUTO": VEML3235ComponentGain.VEML3235_GAIN_AUTO, } -CONFIG_SCHEMA = ( + +def _validate_auto_gain_thresholds(config): + if config[CONF_AUTO_GAIN_THRESHOLD_LOW] >= config[CONF_AUTO_GAIN_THRESHOLD_HIGH]: + raise cv.Invalid( + f"'{CONF_AUTO_GAIN_THRESHOLD_LOW}' must be less than '{CONF_AUTO_GAIN_THRESHOLD_HIGH}'" + ) + return config + + +CONFIG_SCHEMA = cv.All( sensor.sensor_schema( VEML3235Sensor, unit_of_measurement=UNIT_LUX, @@ -67,7 +75,8 @@ CONFIG_SCHEMA = ( } ) .extend(cv.polling_component_schema("60s")) - .extend(i2c.i2c_device_schema(0x10)) + .extend(i2c.i2c_device_schema(0x10)), + _validate_auto_gain_thresholds, ) diff --git a/esphome/components/veml3235/veml3235.cpp b/esphome/components/veml3235/veml3235.cpp index 59892936b0..b3170469b9 100644 --- a/esphome/components/veml3235/veml3235.cpp +++ b/esphome/components/veml3235/veml3235.cpp @@ -6,6 +6,16 @@ namespace esphome::veml3235 { static const char *const TAG = "veml3235.sensor"; +// ADC counts at or above this value (98% of full scale) are treated as clipped: the true light level cannot +// be estimated from such a reading, so auto-gain restarts from minimum sensitivity instead +static const uint16_t CLIPPED_COUNTS = 64224; + +// Maximum sensitivity multiplier: integration time 800 ms (16x) * gain 4x * digital gain 2x +static const uint16_t MAX_SENSITIVITY_FACTOR = 128; + +// At most one restart from clipping plus one proportional adjustment per update cycle +static const uint8_t MAX_ADJUSTMENTS_PER_UPDATE = 2; + void VEML3235Sensor::setup() { uint8_t device_id[] = {0, 0}; if (!this->refresh_config_reg()) { @@ -22,186 +32,156 @@ void VEML3235Sensor::setup() { } } -bool VEML3235Sensor::refresh_config_reg(bool force_on) { - uint16_t data = this->power_on_ || force_on ? 0 : SHUTDOWN_BITS; +bool VEML3235Sensor::refresh_config_reg() { + uint16_t data = 0x1; // mandatory 1 per RM; shutdown bits cleared (device powered on) - data |= (uint16_t(this->integration_time_ << CONFIG_REG_IT_BIT)); - data |= (uint16_t(this->digital_gain_ << CONFIG_REG_DG_BIT)); - data |= (uint16_t(this->gain_ << CONFIG_REG_G_BIT)); - data |= 0x1; // mandatory 1 here per RM + data |= (uint16_t(this->integration_time_) << CONFIG_REG_IT_BIT); + data |= (uint16_t(this->digital_gain_) << CONFIG_REG_DG_BIT); + data |= (uint16_t(this->gain_) << CONFIG_REG_G_BIT); ESP_LOGVV(TAG, "Writing 0x%.4x to register 0x%.2x", data, CONFIG_REG); return this->write_byte_16(CONFIG_REG, data); } -float VEML3235Sensor::read_lx_() { - if (!this->power_on_) { // if off, turn on - if (!this->refresh_config_reg(true)) { - ESP_LOGW(TAG, "Turning on failed"); - this->status_set_warning(); - return NAN; - } - delay(4); // from RM: a wait time of 4 ms should be observed before the first measurement is picked up, to allow - // for a correct start of the signal processor and oscillator +void VEML3235Sensor::update() { + if (this->measurement_in_progress_) { + ESP_LOGV(TAG, "'%s': Previous measurement still in progress; skipping update", this->get_name().c_str()); + return; } + this->measurement_in_progress_ = true; + this->read_and_publish_(MAX_ADJUSTMENTS_PER_UPDATE); +} +void VEML3235Sensor::read_and_publish_(uint8_t adjustments_left) { uint8_t als_regs[] = {0, 0}; if ((this->read_register(ALS_REG, als_regs, sizeof als_regs) != i2c::ERROR_OK)) { this->status_set_warning(); - return NAN; + this->publish_state(NAN); + this->measurement_in_progress_ = false; + return; } this->status_clear_warning(); - float als_raw_value_multiplier = LUX_MULTIPLIER_BASE; - uint16_t als_raw_value = encode_uint16(als_regs[1], als_regs[0]); - // determine multiplier value based on gains and integration time - if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_1X) { - als_raw_value_multiplier *= 2; - } - switch (this->gain_) { - case VEML3235_GAIN_1X: - als_raw_value_multiplier *= 4; - break; - case VEML3235_GAIN_2X: - als_raw_value_multiplier *= 2; - break; - default: - break; - } - switch (this->integration_time_) { - case VEML3235_INTEGRATION_TIME_50MS: - als_raw_value_multiplier *= 16; - break; - case VEML3235_INTEGRATION_TIME_100MS: - als_raw_value_multiplier *= 8; - break; - case VEML3235_INTEGRATION_TIME_200MS: - als_raw_value_multiplier *= 4; - break; - case VEML3235_INTEGRATION_TIME_400MS: - als_raw_value_multiplier *= 2; - break; - default: - break; - } - // finally, determine and return the actual lux value - float lx = float(als_raw_value) * als_raw_value_multiplier; - ESP_LOGVV(TAG, "'%s': ALS raw = %u, multiplier = %.5f", this->get_name().c_str(), als_raw_value, - als_raw_value_multiplier); - ESP_LOGD(TAG, "'%s': Illuminance = %.4flx", this->get_name().c_str(), lx); + uint16_t als_counts = encode_uint16(als_regs[1], als_regs[0]); - if (!this->power_on_) { // turn off if required - if (!this->refresh_config_reg()) { - ESP_LOGW(TAG, "Turning off failed"); - this->status_set_warning(); + if (this->auto_gain_ && adjustments_left > 0) { + // A sample integrated with the previous settings may still be in the data register after the + // configuration changes, so wait out the old integration period plus two new ones before re-reading + const uint32_t old_integration_time_ms = this->integration_time_ms_(); + if (this->adjust_sensitivity_(als_counts)) { + const uint32_t wait_ms = old_integration_time_ms + 2 * this->integration_time_ms_(); + this->set_timeout("reread", wait_ms, + [this, adjustments_left]() { this->read_and_publish_(adjustments_left - 1); }); + return; } } - if (this->auto_gain_) { - this->adjust_gain_(als_raw_value); - } - - return lx; + float lux = this->counts_to_lux_(als_counts); + ESP_LOGVV(TAG, "'%s': ALS counts = %u, sensitivity = %ux", this->get_name().c_str(), als_counts, + this->sensitivity_factor_()); + ESP_LOGV(TAG, "'%s': Illuminance = %.4flx", this->get_name().c_str(), lux); + this->publish_state(lux); + this->measurement_in_progress_ = false; } -void VEML3235Sensor::adjust_gain_(const uint16_t als_raw_value) { - if ((als_raw_value > UINT16_MAX * this->auto_gain_threshold_low_) && - (als_raw_value < UINT16_MAX * this->auto_gain_threshold_high_)) { - return; +float VEML3235Sensor::counts_to_lux_(uint16_t counts) const { + float resolution = LUX_MULTIPLIER_BASE * (float(MAX_SENSITIVITY_FACTOR) / float(this->sensitivity_factor_())); + return float(counts) * resolution; +} + +uint8_t VEML3235Sensor::gain_factor_() const { + switch (this->gain_) { + case VEML3235_GAIN_4X: + return 4; + case VEML3235_GAIN_2X: + return 2; + default: + return 1; + } +} + +uint16_t VEML3235Sensor::sensitivity_factor_() const { + const uint8_t digital_gain_factor = this->digital_gain_ == VEML3235_DIGITAL_GAIN_2X ? 2 : 1; + return (1 << this->integration_time_) * this->gain_factor_() * digital_gain_factor; +} + +void VEML3235Sensor::set_sensitivity_factor_(uint16_t factor) { + // The factor is a power of two in [1, 128]. Prefer integration time (improves the signal-to-noise ratio), + // then analog gain; digital gain is a plain doubling of the output and is used only as a last resort. + uint8_t it_exponent = 0; // integration time is 2^n * 50 ms + while (it_exponent < VEML3235_INTEGRATION_TIME_800MS && (1u << it_exponent) < factor) { + it_exponent++; + } + this->integration_time_ = static_cast(it_exponent); + factor >>= it_exponent; + + if (factor >= 4) { + this->gain_ = VEML3235_GAIN_4X; + factor >>= 2; + } else if (factor == 2) { + this->gain_ = VEML3235_GAIN_2X; + factor >>= 1; + } else { + this->gain_ = VEML3235_GAIN_1X; } - if (als_raw_value >= UINT16_MAX * 0.9) { // over-saturated, reset all gains and start over - this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X; - this->gain_ = VEML3235_GAIN_1X; - this->integration_time_ = VEML3235_INTEGRATION_TIME_50MS; - this->refresh_config_reg(); - return; + this->digital_gain_ = factor >= 2 ? VEML3235_DIGITAL_GAIN_2X : VEML3235_DIGITAL_GAIN_1X; +} + +bool VEML3235Sensor::adjust_sensitivity_(uint16_t counts) { + // Test for clipping before the window test: with an upper threshold configured at or above the clip + // point, a saturated reading would otherwise count as "in window" and sensitivity would never recover + const bool clipped = counts >= CLIPPED_COUNTS; + const uint16_t low = uint16_t(UINT16_MAX * this->auto_gain_threshold_low_); + const uint16_t high = uint16_t(UINT16_MAX * this->auto_gain_threshold_high_); + if (!clipped && counts >= low && counts <= high) { + return false; } - if (this->gain_ != VEML3235_GAIN_4X) { // increase gain if possible - switch (this->gain_) { - case VEML3235_GAIN_1X: - this->gain_ = VEML3235_GAIN_2X; - break; - case VEML3235_GAIN_2X: - this->gain_ = VEML3235_GAIN_4X; - break; - default: - break; + const uint16_t current_factor = this->sensitivity_factor_(); + uint16_t new_factor; + if (clipped) { + new_factor = 1; + } else if (counts == 0) { + new_factor = MAX_SENSITIVITY_FACTOR; + } else { + // Counts scale linearly with the sensitivity factor: in one step, pick the power of two that puts the + // next reading closest below the middle of the configured window. Rounding down means the target is + // never overshot, which also keeps the sensitivity stable when the window is narrower than one step. + float desired = float(current_factor) * ((float(low) + float(high)) * 0.5f / float(counts)); + desired = clamp(desired, 1.0f, float(MAX_SENSITIVITY_FACTOR)); + new_factor = 1; + while (new_factor * 2 <= uint16_t(desired)) { + new_factor *= 2; } - this->refresh_config_reg(); - return; } - // gain is maxed out; reset it and try to increase digital gain - if (this->digital_gain_ != VEML3235_DIGITAL_GAIN_2X) { // increase digital gain if possible - this->digital_gain_ = VEML3235_DIGITAL_GAIN_2X; - this->gain_ = VEML3235_GAIN_1X; - this->refresh_config_reg(); - return; + + if (new_factor == current_factor) { + return false; } - // digital gain is maxed out; reset it and try to increase integration time - if (this->integration_time_ != VEML3235_INTEGRATION_TIME_800MS) { // increase integration time if possible - switch (this->integration_time_) { - case VEML3235_INTEGRATION_TIME_50MS: - this->integration_time_ = VEML3235_INTEGRATION_TIME_100MS; - break; - case VEML3235_INTEGRATION_TIME_100MS: - this->integration_time_ = VEML3235_INTEGRATION_TIME_200MS; - break; - case VEML3235_INTEGRATION_TIME_200MS: - this->integration_time_ = VEML3235_INTEGRATION_TIME_400MS; - break; - case VEML3235_INTEGRATION_TIME_400MS: - this->integration_time_ = VEML3235_INTEGRATION_TIME_800MS; - break; - default: - break; - } - this->digital_gain_ = VEML3235_DIGITAL_GAIN_1X; - this->gain_ = VEML3235_GAIN_1X; - this->refresh_config_reg(); - return; + + const VEML3235ComponentIntegrationTime old_integration_time = this->integration_time_; + const VEML3235ComponentGain old_gain = this->gain_; + const VEML3235ComponentDigitalGain old_digital_gain = this->digital_gain_; + + this->set_sensitivity_factor_(new_factor); + if (!this->refresh_config_reg()) { + // Keep our state consistent with the device, which still has the old configuration + this->integration_time_ = old_integration_time; + this->gain_ = old_gain; + this->digital_gain_ = old_digital_gain; + this->status_set_warning(); + return false; } + + ESP_LOGV(TAG, "'%s': Sensitivity adjusted from %ux to %ux (ALS counts = %u)", this->get_name().c_str(), + current_factor, new_factor, counts); + return true; } void VEML3235Sensor::dump_config() { - uint8_t digital_gain = 1; - uint8_t gain = 1; - uint16_t integration_time = 0; - - if (this->digital_gain_ == VEML3235_DIGITAL_GAIN_2X) { - digital_gain = 2; - } - switch (this->gain_) { - case VEML3235_GAIN_2X: - gain = 2; - break; - case VEML3235_GAIN_4X: - gain = 4; - break; - default: - break; - } - switch (this->integration_time_) { - case VEML3235_INTEGRATION_TIME_50MS: - integration_time = 50; - break; - case VEML3235_INTEGRATION_TIME_100MS: - integration_time = 100; - break; - case VEML3235_INTEGRATION_TIME_200MS: - integration_time = 200; - break; - case VEML3235_INTEGRATION_TIME_400MS: - integration_time = 400; - break; - case VEML3235_INTEGRATION_TIME_800MS: - integration_time = 800; - break; - default: - break; - } + const uint8_t digital_gain = this->digital_gain_ == VEML3235_DIGITAL_GAIN_2X ? 2 : 1; LOG_SENSOR("", "VEML3235", this); LOG_I2C_DEVICE(this); @@ -212,8 +192,9 @@ void VEML3235Sensor::dump_config() { ESP_LOGCONFIG(TAG, " Auto-gain enabled: %s", YESNO(this->auto_gain_)); if (this->auto_gain_) { ESP_LOGCONFIG(TAG, - " Auto-gain upper threshold: %f%%\n" - " Auto-gain lower threshold: %f%%\n" + " Auto-gain thresholds:\n" + " Upper: %.0f%%\n" + " Lower: %.0f%%\n" " Values below will be used as initial values only", this->auto_gain_threshold_high_ * 100.0f, this->auto_gain_threshold_low_ * 100.0f); } @@ -221,7 +202,7 @@ void VEML3235Sensor::dump_config() { " Digital gain: %uX\n" " Gain: %uX\n" " Integration time: %ums", - digital_gain, gain, integration_time); + digital_gain, this->gain_factor_(), this->integration_time_ms_()); } } // namespace esphome::veml3235 diff --git a/esphome/components/veml3235/veml3235.h b/esphome/components/veml3235/veml3235.h index cda6d177aa..c19fc17b65 100644 --- a/esphome/components/veml3235/veml3235.h +++ b/esphome/components/veml3235/veml3235.h @@ -1,7 +1,6 @@ #pragma once #include "esphome/core/component.h" -#include "esphome/core/hal.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" @@ -16,6 +15,10 @@ static const uint8_t ID_REG = 0x09; // Bit offsets within CONFIG_REG // +// The device expects the low data byte first while write_byte_16() sends the high byte first, so the 16-bit +// configuration word used here is byte-swapped relative to the datasheet: datasheet low-byte bits are word +// bits 15:8 here and datasheet high-byte bits are word bits 7:0. +// static const uint8_t CONFIG_REG_IT_BIT = 12; static const uint8_t CONFIG_REG_DG_BIT = 5; static const uint8_t CONFIG_REG_G_BIT = 3; @@ -23,18 +26,17 @@ static const uint8_t CONFIG_REG_G_BIT = 3; // Other important constants // static const uint8_t DEVICE_ID = 0x35; -static const uint16_t SHUTDOWN_BITS = 0x0018; -// Base multiplier value for lux computation +// Resolution (lx/count) at maximum sensitivity (integration time 800 ms, gain 4x, digital gain 2x) // -static const float LUX_MULTIPLIER_BASE = 0.00213; +static const float LUX_MULTIPLIER_BASE = 0.00213f; // Enum for conversion/integration time settings for the VEML3235. // // Specific values of the enum constants are register values taken from the VEML3235 datasheet. // Longer times mean more accurate results, but will take more energy/more time. // -enum VEML3235ComponentIntegrationTime { +enum VEML3235ComponentIntegrationTime : uint8_t { VEML3235_INTEGRATION_TIME_50MS = 0b000, VEML3235_INTEGRATION_TIME_100MS = 0b001, VEML3235_INTEGRATION_TIME_200MS = 0b010, @@ -45,7 +47,7 @@ enum VEML3235ComponentIntegrationTime { // Enum for digital gain settings for the VEML3235. // Higher values are better for low light situations, but can increase noise. // -enum VEML3235ComponentDigitalGain { +enum VEML3235ComponentDigitalGain : uint8_t { VEML3235_DIGITAL_GAIN_1X = 0b0, VEML3235_DIGITAL_GAIN_2X = 0b1, }; @@ -53,7 +55,7 @@ enum VEML3235ComponentDigitalGain { // Enum for gain settings for the VEML3235. // Higher values are better for low light situations, but can increase noise. // -enum VEML3235ComponentGain { +enum VEML3235ComponentGain : uint8_t { VEML3235_GAIN_1X = 0b00, VEML3235_GAIN_2X = 0b01, VEML3235_GAIN_4X = 0b11, @@ -63,7 +65,7 @@ class VEML3235Sensor final : public sensor::Sensor, public PollingComponent, pub public: void setup() override; void dump_config() override; - void update() override { this->publish_state(this->read_lx_()); } + void update() override; // Used by ESPHome framework. Does NOT actually set the value on the device. void set_auto_gain(bool auto_gain) { this->auto_gain_ = auto_gain; } @@ -73,7 +75,6 @@ class VEML3235Sensor final : public sensor::Sensor, public PollingComponent, pub void set_auto_gain_threshold_low(float auto_gain_threshold_low) { this->auto_gain_threshold_low_ = auto_gain_threshold_low; } - void set_power_on(bool power_on) { this->power_on_ = power_on; } void set_digital_gain(VEML3235ComponentDigitalGain digital_gain) { this->digital_gain_ = digital_gain; } void set_gain(VEML3235ComponentGain gain) { this->gain_ = gain; } void set_integration_time(VEML3235ComponentIntegrationTime integration_time) { @@ -88,19 +89,32 @@ class VEML3235Sensor final : public sensor::Sensor, public PollingComponent, pub VEML3235ComponentIntegrationTime integration_time() { return this->integration_time_; } // Updates the configuration register on the device - bool refresh_config_reg(bool force_on = false); + bool refresh_config_reg(); protected: - float read_lx_(); - void adjust_gain_(uint16_t als_raw_value); + // One measurement pass: reads the ALS counts, possibly adjusts the sensitivity and schedules a re-read, + // otherwise publishes the result + void read_and_publish_(uint8_t adjustments_left); + // Chooses a new sensitivity for the given ALS reading and writes it to the device. + // Returns true only if the device configuration was changed. + bool adjust_sensitivity_(uint16_t counts); + float counts_to_lux_(uint16_t counts) const; - bool auto_gain_{true}; - bool power_on_{true}; + // Overall sensitivity multiplier (1x-128x, always a power of two) relative to the least sensitive + // configuration (integration time 50 ms, gain 1x, digital gain 1x). ALS counts scale linearly with it. + uint16_t sensitivity_factor_() const; + void set_sensitivity_factor_(uint16_t factor); + uint8_t gain_factor_() const; + uint16_t integration_time_ms_() const { return 50 << this->integration_time_; } + + // Members are ordered largest to smallest to minimize padding float auto_gain_threshold_high_{0.9}; float auto_gain_threshold_low_{0.2}; VEML3235ComponentDigitalGain digital_gain_{VEML3235_DIGITAL_GAIN_1X}; VEML3235ComponentGain gain_{VEML3235_GAIN_1X}; VEML3235ComponentIntegrationTime integration_time_{VEML3235_INTEGRATION_TIME_50MS}; + bool auto_gain_{true}; + bool measurement_in_progress_{false}; }; } // namespace esphome::veml3235 diff --git a/esphome/components/voice_assistant/voice_assistant.cpp b/esphome/components/voice_assistant/voice_assistant.cpp index f13ea39fa2..76ae145b16 100644 --- a/esphome/components/voice_assistant/voice_assistant.cpp +++ b/esphome/components/voice_assistant/voice_assistant.cpp @@ -978,11 +978,12 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { void VoiceAssistant::on_audio(const api::VoiceAssistantAudio &msg) { #ifdef USE_SPEAKER // We should never get to this function if there is no speaker anyway if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) { - if (this->speaker_buffer_index_ + msg.data_len < SPEAKER_BUFFER_SIZE) { + if (this->speaker_buffer_index_ + msg.data_len <= SPEAKER_BUFFER_SIZE) { memcpy(this->speaker_buffer_ + this->speaker_buffer_index_, msg.data, msg.data_len); this->speaker_buffer_index_ += msg.data_len; this->speaker_buffer_size_ += msg.data_len; this->speaker_bytes_received_ += msg.data_len; + this->write_speaker_(); ESP_LOGV(TAG, "Received audio: %u bytes from API", msg.data_len); } else { ESP_LOGE(TAG, "Cannot receive audio, buffer is full"); diff --git a/esphome/components/voice_assistant/voice_assistant.h b/esphome/components/voice_assistant/voice_assistant.h index dd9d205aff..d46b089c2e 100644 --- a/esphome/components/voice_assistant/voice_assistant.h +++ b/esphome/components/voice_assistant/voice_assistant.h @@ -81,12 +81,6 @@ struct Timer { this->id.c_str(), this->name.c_str(), this->total_seconds, this->seconds_left, YESNO(this->is_active)); return buffer.data(); } - // Remove before 2026.8.0 - ESPDEPRECATED("Use to_str() instead. Removed in 2026.8.0", "2026.2.0") - std::string to_string() const { // NOLINT - char buffer[TO_STR_BUFFER_SIZE]; - return this->to_str(buffer); - } }; struct WakeWord { diff --git a/esphome/components/water_heater/__init__.py b/esphome/components/water_heater/__init__.py index f3eec16a40..6bb2b2f8fe 100644 --- a/esphome/components/water_heater/__init__.py +++ b/esphome/components/water_heater/__init__.py @@ -76,7 +76,7 @@ def water_heater_schema( @setup_entity("water_heater") async def setup_water_heater_core_(var: cg.Pvariable, config: ConfigType) -> None: """Set up the core water heater properties in C++.""" - visual = config[CONF_VISUAL] + visual = config.get(CONF_VISUAL, {}) if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: cg.add_define("USE_WATER_HEATER_VISUAL_OVERRIDES") cg.add(var.set_visual_min_temperature_override(min_temp)) diff --git a/esphome/components/water_heater/water_heater.h b/esphome/components/water_heater/water_heater.h index a1e1ca10a6..995b815440 100644 --- a/esphome/components/water_heater/water_heater.h +++ b/esphome/components/water_heater/water_heater.h @@ -90,10 +90,6 @@ class WaterHeaterCall { float get_target_temperature() const { return this->target_temperature_; } float get_target_temperature_low() const { return this->target_temperature_low_; } float get_target_temperature_high() const { return this->target_temperature_high_; } - /// Get state flags value - ESPDEPRECATED("get_state() is deprecated, use get_away() and get_on() instead. (Removed in 2026.8.0)", "2026.2.0") - uint32_t get_state() const { return this->state_; } - optional get_away() const { if (this->state_mask_ & WATER_HEATER_STATE_AWAY) { return (this->state_ & WATER_HEATER_STATE_AWAY) != 0; @@ -187,6 +183,9 @@ class WaterHeaterTraits { const WaterHeaterModeMask &get_supported_modes() const { return this->supported_modes_; } bool supports_mode(WaterHeaterMode mode) const { return this->supported_modes_.count(mode); } + TemperatureUnit get_temperature_unit() const { return this->temperature_unit_; } + void set_temperature_unit(TemperatureUnit unit) { this->temperature_unit_ = unit; } + protected: // Ordered to minimize padding: 4-byte members first uint32_t feature_flags_{0}; @@ -194,6 +193,7 @@ class WaterHeaterTraits { float max_temperature_{0.0f}; float target_temperature_step_{0.0f}; WaterHeaterModeMask supported_modes_; + TemperatureUnit temperature_unit_{TemperatureUnit::CELSIUS}; }; class WaterHeater : public EntityBase { diff --git a/esphome/components/web_server/server_index_v2.h b/esphome/components/web_server/server_index_v2.h index ac2195f387..0c1a6c7f79 100644 --- a/esphome/components/web_server/server_index_v2.h +++ b/esphome/components/web_server/server_index_v2.h @@ -10,1308 +10,1310 @@ namespace esphome::web_server { #ifdef USE_WEBSERVER_GZIP constexpr uint8_t INDEX_GZ[] PROGMEM = { - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x7d, 0xd9, 0x72, 0xdb, 0x48, 0xb6, 0xe0, 0xf3, - 0xd4, 0x57, 0x40, 0x28, 0xb5, 0x8c, 0x2c, 0x26, 0xc1, 0x45, 0x92, 0x2d, 0x83, 0x4a, 0xb2, 0x65, 0xd9, 0xd5, 0x76, - 0x97, 0xb7, 0xb6, 0xec, 0xda, 0x58, 0x6c, 0x09, 0x02, 0x92, 0x44, 0x96, 0x41, 0x80, 0x05, 0x24, 0xb5, 0x14, 0x89, - 0x1b, 0xf3, 0x01, 0x13, 0x31, 0x11, 0xf3, 0x34, 0x2f, 0x13, 0x73, 0x1f, 0xe6, 0x23, 0xe6, 0xf9, 0x7e, 0xca, 0xfd, - 0x81, 0x99, 0x4f, 0x98, 0x38, 0xb9, 0x00, 0x09, 0x2e, 0xb2, 0x5c, 0x55, 0x7d, 0xef, 0x7d, 0x98, 0xa8, 0x28, 0x99, - 0x48, 0xe4, 0x72, 0xf2, 0xe4, 0xc9, 0xb3, 0x67, 0xe2, 0x78, 0x27, 0x4c, 0x03, 0x7e, 0x3b, 0xa3, 0x56, 0xc4, 0xa7, - 0x71, 0xff, 0x58, 0xfd, 0xa5, 0x7e, 0xd8, 0x3f, 0x8e, 0x59, 0xf2, 0xd1, 0xca, 0x68, 0x4c, 0x58, 0x90, 0x26, 0x56, - 0x94, 0xd1, 0x31, 0x09, 0x7d, 0xee, 0x7b, 0x6c, 0xea, 0x4f, 0xa8, 0xd5, 0xea, 0x1f, 0x4f, 0x29, 0xf7, 0xad, 0x20, - 0xf2, 0xb3, 0x9c, 0x72, 0xf2, 0xe1, 0xfd, 0xd7, 0xcd, 0xa3, 0xfe, 0x71, 0x1e, 0x64, 0x6c, 0xc6, 0x2d, 0xe8, 0x92, - 0x4c, 0xd3, 0x70, 0x1e, 0xd3, 0x7e, 0xab, 0x75, 0x7d, 0x7d, 0xed, 0xfe, 0x9c, 0x7f, 0x11, 0xa4, 0x49, 0xce, 0xad, - 0xa7, 0xe4, 0x9a, 0x25, 0x61, 0x7a, 0x8d, 0x73, 0x4e, 0x9e, 0xba, 0x67, 0x91, 0x1f, 0xa6, 0xd7, 0xef, 0xd2, 0x94, - 0xef, 0xed, 0x39, 0xf2, 0xf1, 0xf6, 0xf4, 0xec, 0x8c, 0x10, 0x72, 0x95, 0xb2, 0xd0, 0x6a, 0x2f, 0x97, 0x55, 0xa1, - 0x9b, 0xf8, 0x9c, 0x5d, 0x51, 0xd9, 0x04, 0xed, 0xed, 0xd9, 0x7e, 0x98, 0xce, 0x38, 0x0d, 0xcf, 0xf8, 0x6d, 0x4c, - 0xcf, 0x22, 0x4a, 0x79, 0x6e, 0xb3, 0xc4, 0x7a, 0x9a, 0x06, 0xf3, 0x29, 0x4d, 0xb8, 0x3b, 0xcb, 0x52, 0x9e, 0x02, - 0x24, 0x7b, 0x7b, 0x76, 0x46, 0x67, 0xb1, 0x1f, 0x50, 0x78, 0x7f, 0x7a, 0x76, 0x56, 0xb5, 0xa8, 0x2a, 0xe1, 0x84, - 0x93, 0xb3, 0xdb, 0xe9, 0x65, 0x1a, 0x3b, 0x08, 0x47, 0x9c, 0x24, 0xf4, 0xda, 0xfa, 0x8e, 0xfa, 0x1f, 0x5f, 0xf9, - 0xb3, 0x5e, 0x10, 0xfb, 0x79, 0x6e, 0x9d, 0xf0, 0x85, 0x98, 0x42, 0x36, 0x0f, 0x78, 0x9a, 0x39, 0x1c, 0x53, 0xcc, - 0xd0, 0x82, 0x8d, 0x1d, 0x1e, 0xb1, 0xdc, 0x3d, 0xdf, 0x0d, 0xf2, 0xfc, 0x1d, 0xcd, 0xe7, 0x31, 0xdf, 0x25, 0x3b, - 0x6d, 0xcc, 0x76, 0x08, 0x49, 0x38, 0xe2, 0x51, 0x96, 0x5e, 0x5b, 0xcf, 0xb2, 0x2c, 0xcd, 0x1c, 0xfb, 0xf4, 0xec, - 0x4c, 0xd6, 0xb0, 0x58, 0x6e, 0x25, 0x29, 0xb7, 0xca, 0xfe, 0xfc, 0xcb, 0x98, 0xba, 0xd6, 0x87, 0x9c, 0x5a, 0x17, - 0xf3, 0x24, 0xf7, 0xc7, 0xf4, 0xf4, 0xec, 0xec, 0xc2, 0x4a, 0x33, 0xeb, 0x22, 0xc8, 0xf3, 0x0b, 0x8b, 0x25, 0x39, - 0xa7, 0x7e, 0xe8, 0xda, 0xa8, 0x27, 0x06, 0x0b, 0xf2, 0xfc, 0x3d, 0xbd, 0xe1, 0x84, 0x63, 0xf1, 0xc8, 0x09, 0x2d, - 0x26, 0x94, 0x5b, 0x79, 0x39, 0x2f, 0x07, 0x2d, 0x62, 0xca, 0x2d, 0x4e, 0xc4, 0xfb, 0xb4, 0x27, 0x71, 0x4f, 0xe5, - 0x23, 0xef, 0xb1, 0xb1, 0x93, 0xf3, 0xbd, 0x3d, 0x5e, 0xe2, 0x19, 0xc9, 0xa9, 0x59, 0x8c, 0xd0, 0x1d, 0x5d, 0xb6, - 0xb7, 0x47, 0xdd, 0x98, 0x26, 0x13, 0x1e, 0x11, 0x42, 0x3a, 0x3d, 0xb6, 0xb7, 0xe7, 0x70, 0x12, 0x71, 0x77, 0x42, - 0xb9, 0x43, 0x11, 0xc2, 0x55, 0xeb, 0xbd, 0x3d, 0x47, 0x22, 0x21, 0x25, 0x12, 0x71, 0x35, 0x1c, 0x23, 0x57, 0x61, - 0xff, 0xec, 0x36, 0x09, 0x1c, 0x13, 0x7e, 0x84, 0xd9, 0xde, 0x5e, 0xc4, 0xdd, 0x1c, 0x7a, 0xc4, 0x1c, 0xa1, 0x22, - 0xa3, 0x7c, 0x9e, 0x25, 0x16, 0x2f, 0x78, 0x7a, 0xc6, 0x33, 0x96, 0x4c, 0x1c, 0xb4, 0xd0, 0x65, 0x46, 0xc3, 0xa2, - 0x90, 0xe0, 0xbe, 0xe3, 0x24, 0x21, 0x7d, 0x18, 0xf1, 0x84, 0x3b, 0xb0, 0x8a, 0xe9, 0xd8, 0x4a, 0x08, 0xb1, 0x73, - 0xd1, 0xd6, 0x1e, 0x24, 0x5e, 0xd2, 0xb0, 0x6d, 0x2c, 0xa1, 0xc4, 0x09, 0x47, 0xf8, 0x23, 0x71, 0x12, 0xec, 0xba, - 0x2e, 0x47, 0xa4, 0xbf, 0xd0, 0x58, 0x49, 0x8c, 0x79, 0x0e, 0x92, 0x61, 0x7b, 0xe4, 0x71, 0x37, 0xa3, 0xe1, 0x3c, - 0xa0, 0x8e, 0xc3, 0x70, 0x8e, 0x33, 0x44, 0xfa, 0xac, 0xe1, 0xa4, 0xa4, 0x0f, 0xcb, 0x9d, 0xd6, 0xd7, 0x9a, 0x90, - 0x9d, 0x36, 0x52, 0x30, 0xa6, 0x1a, 0x40, 0xc0, 0xb0, 0x82, 0x27, 0x25, 0xc4, 0x4e, 0xe6, 0xd3, 0x4b, 0x9a, 0xd9, - 0x65, 0xb5, 0x5e, 0x8d, 0x2c, 0xe6, 0x39, 0xb5, 0x82, 0x3c, 0xb7, 0xc6, 0xf3, 0x24, 0xe0, 0x2c, 0x4d, 0x2c, 0xbb, - 0x91, 0x36, 0x6c, 0x49, 0x0e, 0x25, 0x35, 0xd8, 0xa8, 0x40, 0x4e, 0x8e, 0x1a, 0xc9, 0x30, 0x6b, 0x74, 0x46, 0x18, - 0xa0, 0x44, 0x3d, 0xd5, 0x9f, 0x42, 0x00, 0xc5, 0x09, 0xcc, 0xb1, 0xc0, 0x4f, 0x38, 0xcc, 0x52, 0x4c, 0x31, 0xe7, - 0x83, 0xc4, 0x5d, 0xdf, 0x28, 0x84, 0xbb, 0x53, 0x7f, 0xe6, 0x50, 0xd2, 0xa7, 0x82, 0xb8, 0xfc, 0x24, 0x00, 0x58, - 0x6b, 0xeb, 0x36, 0xa0, 0x1e, 0x75, 0x2b, 0x92, 0x42, 0x1e, 0x77, 0xc7, 0x69, 0xf6, 0xcc, 0x0f, 0x22, 0x68, 0x57, - 0x12, 0x4c, 0xa8, 0xf7, 0x5b, 0x90, 0x51, 0x9f, 0xd3, 0x67, 0x31, 0x85, 0x27, 0xc7, 0x16, 0x2d, 0x6d, 0x84, 0x73, - 0xf2, 0xd4, 0x8d, 0x19, 0x7f, 0x9d, 0x26, 0x01, 0xed, 0xe5, 0x06, 0x75, 0x31, 0x58, 0xf7, 0x13, 0xce, 0x33, 0x76, - 0x39, 0xe7, 0xd4, 0xb1, 0x13, 0xa8, 0x61, 0xe3, 0x1c, 0x61, 0xe6, 0x72, 0x7a, 0xc3, 0x4f, 0xd3, 0x84, 0xd3, 0x84, - 0x13, 0xaa, 0x91, 0x8a, 0x13, 0xd7, 0x9f, 0xcd, 0x68, 0x12, 0x9e, 0x46, 0x2c, 0x0e, 0x1d, 0x86, 0x0a, 0x54, 0xe0, - 0x80, 0x13, 0x98, 0x23, 0xe9, 0x27, 0x1e, 0xfc, 0xd9, 0x3e, 0x1b, 0x87, 0x93, 0xbe, 0xd8, 0x14, 0x94, 0xd8, 0x76, - 0x6f, 0x9c, 0x66, 0x8e, 0x9a, 0x81, 0x95, 0x8e, 0x2d, 0x0e, 0x63, 0xbc, 0x9b, 0xc7, 0x34, 0x47, 0xb4, 0x41, 0x58, - 0xb9, 0x8c, 0x0a, 0xc1, 0xef, 0x80, 0xe2, 0x0b, 0xe4, 0x24, 0xc8, 0x4b, 0x7a, 0x57, 0x7e, 0x66, 0xfd, 0xa8, 0x76, - 0xd4, 0xcf, 0x9a, 0x9b, 0x85, 0x9c, 0xfc, 0xec, 0xf2, 0x6c, 0x9e, 0x73, 0x1a, 0xbe, 0xbf, 0x9d, 0xd1, 0x1c, 0x3f, - 0xe7, 0x24, 0xe4, 0x83, 0x90, 0xbb, 0x74, 0x3a, 0xe3, 0xb7, 0x67, 0x82, 0x31, 0x7a, 0xb6, 0x8d, 0xe7, 0x50, 0x33, - 0xa3, 0x7e, 0x00, 0xcc, 0x4c, 0x61, 0xeb, 0x6d, 0x1a, 0xdf, 0x8e, 0x59, 0x1c, 0x9f, 0xcd, 0x67, 0xb3, 0x34, 0xe3, - 0x98, 0x73, 0xb2, 0xe0, 0x69, 0x85, 0x1b, 0x58, 0xcc, 0x45, 0x7e, 0xcd, 0x78, 0x10, 0x39, 0x1c, 0x2d, 0x02, 0x3f, - 0xa7, 0xd6, 0x93, 0x34, 0x8d, 0xa9, 0x0f, 0xb3, 0x4e, 0x06, 0xcf, 0xb9, 0x97, 0xcc, 0xe3, 0xb8, 0x77, 0x99, 0x51, - 0xff, 0x63, 0x4f, 0xbc, 0x7e, 0x73, 0xf9, 0x33, 0x0d, 0xb8, 0x27, 0x7e, 0x9f, 0x64, 0x99, 0x7f, 0x0b, 0x15, 0x09, - 0x81, 0x6a, 0x83, 0xc4, 0xfb, 0xeb, 0xd9, 0x9b, 0xd7, 0xae, 0xdc, 0x25, 0x6c, 0x7c, 0xeb, 0x24, 0xe5, 0xce, 0x4b, - 0x0a, 0x3c, 0xce, 0xd2, 0xe9, 0xca, 0xd0, 0x12, 0x6d, 0x49, 0x6f, 0x0b, 0x08, 0x94, 0x24, 0x3b, 0xb2, 0x6b, 0x13, - 0x82, 0xd7, 0x82, 0xe8, 0xe1, 0x25, 0xd1, 0xe3, 0xce, 0xe3, 0xd8, 0x93, 0xc5, 0x4e, 0x82, 0xee, 0x86, 0x96, 0x67, - 0xb7, 0x0b, 0x4a, 0x04, 0x9c, 0x33, 0x10, 0x31, 0x00, 0x63, 0xe0, 0xf3, 0x20, 0x5a, 0x50, 0xd1, 0x59, 0xa1, 0x21, - 0xa6, 0x45, 0x81, 0x9f, 0x95, 0x04, 0xcf, 0x01, 0x10, 0xc1, 0xa9, 0x08, 0x5f, 0x2e, 0x61, 0xc2, 0x08, 0xff, 0x95, - 0x2c, 0x7c, 0x3d, 0x1f, 0x6f, 0xa7, 0x8d, 0x61, 0x63, 0x7a, 0x92, 0xbd, 0xe0, 0x20, 0x4d, 0xae, 0x68, 0xc6, 0x69, - 0xe6, 0x71, 0x8e, 0x33, 0x3a, 0x8e, 0x01, 0x8c, 0x9d, 0x0e, 0x8e, 0xfc, 0xfc, 0x34, 0xf2, 0x93, 0x09, 0x0d, 0xbd, - 0x67, 0xbc, 0xc0, 0x94, 0x13, 0x7b, 0xcc, 0x12, 0x3f, 0x66, 0xbf, 0xd2, 0xd0, 0x56, 0x02, 0xe1, 0x99, 0x45, 0x6f, - 0x38, 0x4d, 0xc2, 0xdc, 0x7a, 0xfe, 0xfe, 0xd5, 0x4b, 0xb5, 0x94, 0x35, 0x19, 0x81, 0x16, 0xf9, 0x7c, 0x46, 0x33, - 0x07, 0x61, 0x25, 0x23, 0x9e, 0x31, 0xc1, 0x1f, 0x5f, 0xf9, 0x33, 0x59, 0xc2, 0xf2, 0x0f, 0xb3, 0xd0, 0xe7, 0xf4, - 0x2d, 0x4d, 0x42, 0x96, 0x4c, 0xc8, 0x4e, 0x47, 0x96, 0x47, 0xbe, 0x7a, 0x11, 0x96, 0x45, 0xe7, 0xbb, 0xcf, 0x62, - 0x31, 0xf3, 0xf2, 0x71, 0xee, 0xa0, 0x22, 0xe7, 0x3e, 0x67, 0x81, 0xe5, 0x87, 0xe1, 0x8b, 0x84, 0x71, 0x26, 0x00, - 0xcc, 0x60, 0x81, 0x80, 0x4a, 0xa9, 0x94, 0x16, 0x1a, 0x70, 0x07, 0x61, 0xc7, 0x51, 0x32, 0x20, 0x42, 0x6a, 0xc5, - 0xf6, 0xf6, 0x2a, 0x8e, 0x3f, 0xa0, 0x9e, 0x7c, 0x49, 0x86, 0x23, 0xe4, 0xce, 0xe6, 0x39, 0x2c, 0xb5, 0x1e, 0x02, - 0x04, 0x4c, 0x7a, 0x99, 0xd3, 0xec, 0x8a, 0x86, 0x25, 0x79, 0xe4, 0x0e, 0x5a, 0xac, 0x8c, 0xa1, 0x76, 0x06, 0x27, - 0xc3, 0x51, 0xcf, 0x64, 0xdd, 0x54, 0x91, 0x7a, 0x96, 0xce, 0x68, 0xc6, 0x19, 0xcd, 0x4b, 0x6e, 0xe2, 0x80, 0x20, - 0x2d, 0x39, 0x4a, 0x4e, 0xf4, 0xfc, 0x66, 0x0e, 0xc3, 0x14, 0xd5, 0x78, 0x86, 0x96, 0xb5, 0xcf, 0xae, 0x84, 0xd0, - 0xc8, 0x31, 0x43, 0x98, 0x4b, 0x48, 0x73, 0x84, 0x0a, 0x84, 0xb9, 0x06, 0x57, 0x72, 0x23, 0x35, 0xda, 0x2d, 0x48, - 0x6b, 0xf2, 0x57, 0x21, 0xad, 0x81, 0xa7, 0xf9, 0x9c, 0xee, 0xed, 0x39, 0xd4, 0x2d, 0xc9, 0x82, 0xec, 0x74, 0xd4, - 0x1a, 0x19, 0xc8, 0xda, 0x02, 0x36, 0x0c, 0xcc, 0x31, 0x45, 0x78, 0x87, 0xba, 0x49, 0x7a, 0x12, 0x04, 0x34, 0xcf, - 0xd3, 0x6c, 0x6f, 0x6f, 0x47, 0xd4, 0x2f, 0x15, 0x0a, 0x58, 0xc3, 0x37, 0xd7, 0x49, 0x05, 0x01, 0xaa, 0x84, 0xac, - 0x12, 0x0d, 0x1c, 0x44, 0x95, 0xd0, 0x39, 0xec, 0x81, 0xd6, 0x3d, 0x3c, 0xfb, 0xfc, 0xdc, 0x6e, 0x70, 0xac, 0xd0, - 0x30, 0xa1, 0x7a, 0xe8, 0xdb, 0xa7, 0x54, 0x6a, 0x57, 0x42, 0xf7, 0x58, 0xc3, 0x8c, 0xdc, 0x41, 0x6e, 0x48, 0xc7, - 0x2c, 0x31, 0xa6, 0x5d, 0x03, 0x09, 0x73, 0x9c, 0xa3, 0xc2, 0x58, 0xd0, 0x8d, 0x5d, 0x0b, 0xb5, 0x46, 0xae, 0xdc, - 0x62, 0x22, 0x54, 0x09, 0x63, 0x19, 0x87, 0x74, 0x54, 0x60, 0x81, 0x7a, 0x3d, 0x9b, 0x4c, 0x00, 0x3a, 0xe4, 0xa3, - 0x9e, 0x7a, 0x4f, 0x72, 0x89, 0xb9, 0x8c, 0xfe, 0x32, 0xa7, 0x39, 0x97, 0x74, 0xec, 0x70, 0x9c, 0x61, 0x06, 0xfc, - 0x3a, 0x4d, 0xc6, 0x6c, 0x32, 0xcf, 0x40, 0xe3, 0x81, 0xcd, 0x48, 0x93, 0xf9, 0x94, 0xea, 0xa7, 0x4d, 0xb0, 0xbd, - 0x99, 0x81, 0x4c, 0xcc, 0x81, 0xa6, 0xef, 0x26, 0x27, 0x80, 0x95, 0xa3, 0xe5, 0xf2, 0xaf, 0xba, 0x93, 0x6a, 0x29, - 0x4b, 0x2d, 0x6d, 0x65, 0x4d, 0x28, 0x47, 0x4a, 0x26, 0xef, 0x74, 0x14, 0xf8, 0x7c, 0x44, 0x76, 0xda, 0x25, 0x0d, - 0x2b, 0xac, 0x4a, 0x70, 0x24, 0x12, 0xdf, 0xc8, 0xae, 0x90, 0x10, 0xf1, 0x35, 0x72, 0x71, 0xa3, 0x35, 0x4a, 0x8d, - 0xc8, 0x10, 0x94, 0x0d, 0x37, 0x1a, 0x6d, 0x23, 0x27, 0xcd, 0x0f, 0x1c, 0xbe, 0xfe, 0xae, 0x62, 0x1b, 0x57, 0x75, - 0xb6, 0xb1, 0x32, 0x0d, 0x7b, 0x56, 0x36, 0xb1, 0x4b, 0x2a, 0x53, 0x1b, 0xbd, 0x7a, 0x85, 0x99, 0x00, 0xa6, 0x9a, - 0x92, 0xd1, 0xc5, 0x6b, 0x7f, 0x4a, 0x73, 0x87, 0x22, 0xbc, 0xad, 0x82, 0x24, 0x4f, 0xa8, 0x32, 0x32, 0x64, 0x67, - 0x0e, 0xb2, 0x93, 0x21, 0xa9, 0x9a, 0xd5, 0x37, 0x5c, 0x8e, 0xe9, 0x30, 0x1f, 0x55, 0x1a, 0x9d, 0x31, 0x79, 0x21, - 0x94, 0x15, 0x7d, 0x6b, 0xfc, 0xc9, 0x32, 0x89, 0x34, 0xa1, 0x39, 0xe4, 0x08, 0xef, 0xb4, 0x57, 0x57, 0x52, 0xd7, - 0xaa, 0xe6, 0x38, 0x1c, 0xc1, 0x3a, 0x08, 0x91, 0xe1, 0xb2, 0x5c, 0xfc, 0x5b, 0xdb, 0x69, 0x80, 0xb6, 0x33, 0x20, - 0x0c, 0x77, 0x1c, 0xfb, 0xdc, 0xe9, 0xb4, 0xda, 0xa0, 0x8e, 0x5e, 0x51, 0x90, 0x28, 0x08, 0xad, 0x4f, 0x85, 0xba, - 0xf3, 0x24, 0x8f, 0xd8, 0x98, 0x3b, 0x01, 0x17, 0x2c, 0x85, 0xc6, 0x39, 0xb5, 0x78, 0x4d, 0x29, 0x16, 0xec, 0x26, - 0x00, 0x62, 0x2b, 0x35, 0x30, 0xaa, 0x21, 0x15, 0x6c, 0x0b, 0xb8, 0x43, 0xa5, 0x50, 0x57, 0x5c, 0x46, 0xd7, 0x66, - 0xa0, 0x34, 0x76, 0x06, 0xb2, 0x47, 0x4f, 0x31, 0x03, 0x66, 0xe8, 0xad, 0xcc, 0x33, 0x39, 0x84, 0x2a, 0xe4, 0x2e, - 0x4f, 0x5f, 0xa6, 0xd7, 0x34, 0x3b, 0xf5, 0x01, 0x78, 0x4f, 0x36, 0x2f, 0xa4, 0x20, 0x10, 0xfc, 0x9e, 0xf7, 0x34, - 0xbd, 0x9c, 0x8b, 0x89, 0xbf, 0xcd, 0xd2, 0x29, 0xcb, 0x29, 0xa8, 0x6b, 0x12, 0xff, 0x09, 0xec, 0x33, 0xb1, 0x21, - 0x41, 0xd8, 0xd0, 0x92, 0xbe, 0x4e, 0x5e, 0xd6, 0xe9, 0xeb, 0x7c, 0xf7, 0xd9, 0x44, 0x33, 0xc0, 0xfa, 0x36, 0x46, - 0xd8, 0x51, 0x46, 0x85, 0x21, 0xe7, 0xdc, 0x08, 0x29, 0x11, 0xbf, 0x5c, 0x72, 0xc3, 0x76, 0xab, 0x29, 0x8c, 0x54, - 0x6e, 0x1b, 0x54, 0xf8, 0x61, 0x08, 0xaa, 0x5d, 0x96, 0xc6, 0xb1, 0x21, 0xaa, 0x30, 0xeb, 0x95, 0xc2, 0xe9, 0x7c, - 0xf7, 0xd9, 0xd9, 0x5d, 0xf2, 0x09, 0xde, 0x9b, 0x22, 0x4a, 0x03, 0x9a, 0x84, 0x34, 0x03, 0x5b, 0xd2, 0x58, 0x2d, - 0x25, 0x65, 0x4f, 0xd3, 0x24, 0xa1, 0x01, 0xa7, 0x21, 0x98, 0x2a, 0x8c, 0x70, 0x37, 0x4a, 0x73, 0x5e, 0x16, 0x56, - 0xd0, 0x33, 0x03, 0x7a, 0xe6, 0x06, 0x7e, 0x1c, 0x3b, 0xd2, 0x2c, 0x99, 0xa6, 0x57, 0x74, 0x03, 0xd4, 0xbd, 0x1a, - 0xc8, 0x65, 0x37, 0xd4, 0xe8, 0x86, 0xba, 0xf9, 0x2c, 0x66, 0x01, 0x2d, 0x45, 0xd7, 0x99, 0xcb, 0x92, 0x90, 0xde, - 0x00, 0x1f, 0x41, 0xfd, 0x7e, 0xbf, 0x8d, 0x3b, 0xa8, 0x90, 0x08, 0x5f, 0xac, 0x21, 0xf6, 0x0e, 0xa1, 0x09, 0x44, - 0x46, 0xfa, 0x8b, 0x8d, 0x6c, 0x0d, 0x19, 0x92, 0x92, 0x69, 0xf3, 0x4a, 0x72, 0x67, 0x84, 0x43, 0x1a, 0x53, 0x4e, - 0x35, 0x37, 0x07, 0x25, 0x5a, 0x6e, 0xdd, 0x77, 0x25, 0xfe, 0x4a, 0x72, 0xd2, 0xbb, 0x4c, 0xaf, 0x79, 0x5e, 0x9a, - 0xeb, 0xd5, 0xf2, 0x54, 0xd8, 0x1e, 0x70, 0xb9, 0x3c, 0x3e, 0xe7, 0x7e, 0x10, 0x49, 0x3b, 0xdd, 0x59, 0x9b, 0x52, - 0xd5, 0x87, 0xe2, 0xec, 0xe5, 0x26, 0x7a, 0xa2, 0xc1, 0xdc, 0x84, 0x82, 0x33, 0xc5, 0x14, 0x28, 0x98, 0x7e, 0x72, - 0xd9, 0x4e, 0xfd, 0x38, 0xbe, 0xf4, 0x83, 0x8f, 0x75, 0xea, 0xaf, 0xc8, 0x80, 0xac, 0x72, 0x63, 0xe3, 0x95, 0xc1, - 0xb2, 0xcc, 0x79, 0x6b, 0x2e, 0x5d, 0xdb, 0x28, 0xce, 0x4e, 0xbb, 0x22, 0xfb, 0xfa, 0x42, 0x6f, 0xa5, 0x76, 0x01, - 0x11, 0x53, 0x33, 0x73, 0x80, 0x0b, 0x7c, 0x92, 0xe2, 0x34, 0x3f, 0x50, 0x74, 0x07, 0x06, 0x47, 0xb1, 0x02, 0x08, - 0x47, 0x8b, 0x22, 0x64, 0xf9, 0x76, 0x0c, 0xfc, 0x21, 0x50, 0x3e, 0x35, 0x46, 0xb8, 0x2f, 0xa0, 0x25, 0x8f, 0x53, - 0x5a, 0x73, 0x09, 0x99, 0xd2, 0x27, 0x34, 0xa3, 0xf9, 0x06, 0x74, 0x17, 0x41, 0xef, 0x6f, 0xe4, 0x2b, 0xd0, 0xca, - 0x00, 0x8a, 0xbc, 0x67, 0xaa, 0x13, 0x35, 0x0a, 0x50, 0x3c, 0x95, 0x09, 0x91, 0x9b, 0xd5, 0x2c, 0x48, 0xa5, 0xb1, - 0x4b, 0x23, 0x5c, 0xb1, 0xdc, 0x94, 0x38, 0x8e, 0x93, 0x83, 0x11, 0xa7, 0x75, 0xfb, 0x6a, 0x12, 0xf9, 0xda, 0x24, - 0x72, 0xd7, 0x30, 0xb4, 0x50, 0x45, 0xcb, 0x46, 0x73, 0x8f, 0x73, 0x64, 0xd6, 0x02, 0x7d, 0xd5, 0x05, 0x06, 0x8d, - 0x4a, 0x7e, 0x1b, 0x13, 0x8e, 0x53, 0x65, 0xe5, 0x28, 0x52, 0x03, 0x8e, 0x51, 0x35, 0xc9, 0x90, 0xdc, 0x1b, 0x35, - 0x93, 0x37, 0xc3, 0x29, 0x5a, 0x51, 0xee, 0x8b, 0x42, 0x21, 0x89, 0x22, 0xb5, 0x38, 0x35, 0xad, 0xd8, 0x40, 0x0b, - 0xce, 0x88, 0xd2, 0x84, 0xa5, 0xe2, 0xb3, 0x8a, 0x9c, 0xb2, 0xdf, 0x1d, 0x42, 0xb2, 0x0a, 0x37, 0x35, 0x95, 0x52, - 0xeb, 0x56, 0x19, 0xc2, 0x91, 0x56, 0x4a, 0xd3, 0x6a, 0xe2, 0x84, 0xd8, 0xda, 0x27, 0x61, 0x0f, 0x16, 0x35, 0xbb, - 0xd0, 0x33, 0xaa, 0x15, 0x1e, 0xf0, 0xd4, 0x74, 0x13, 0xbe, 0x37, 0x11, 0x4d, 0xad, 0x1f, 0x03, 0xe3, 0x69, 0x0d, - 0xe3, 0x06, 0x6a, 0x33, 0xc9, 0xbb, 0xb2, 0x11, 0x89, 0xea, 0x8d, 0x1d, 0x8a, 0x53, 0xb9, 0x10, 0x6b, 0x58, 0x5c, - 0x55, 0x3e, 0x05, 0x11, 0x82, 0x19, 0x9b, 0x83, 0x7a, 0x67, 0x4a, 0x08, 0x07, 0x80, 0x67, 0xcb, 0xe5, 0x1a, 0xd9, - 0x6d, 0xd4, 0x41, 0x91, 0x5b, 0x59, 0x86, 0xcb, 0xe5, 0x33, 0x8e, 0x1c, 0xa5, 0xfd, 0x62, 0x8a, 0x06, 0x9a, 0xe7, - 0x9e, 0xbc, 0x84, 0x5a, 0x42, 0x19, 0xad, 0x4a, 0x4a, 0xb3, 0xa1, 0x4e, 0xb5, 0xf5, 0x85, 0xe2, 0x06, 0xe3, 0x3e, - 0x5d, 0xe3, 0x5f, 0xa2, 0x50, 0x09, 0xea, 0x6a, 0xca, 0xa7, 0xaa, 0x6b, 0x86, 0x10, 0xf2, 0x72, 0x61, 0xc9, 0xec, - 0x6c, 0x32, 0x2e, 0xf7, 0xf6, 0x72, 0xa3, 0xa3, 0xf3, 0x92, 0x51, 0xfc, 0xec, 0x80, 0x50, 0xce, 0x6f, 0x13, 0xa1, - 0xbd, 0xfc, 0xac, 0xc5, 0xd0, 0x9a, 0x69, 0xda, 0xee, 0x81, 0x4d, 0xee, 0x5f, 0xfb, 0x8c, 0x5b, 0x65, 0x2f, 0xd2, - 0x26, 0x77, 0x28, 0x5a, 0x28, 0x65, 0xc3, 0xcd, 0x28, 0xa8, 0x8f, 0xc0, 0x15, 0xb4, 0x12, 0x2d, 0x09, 0x3f, 0x88, - 0x28, 0xf8, 0x83, 0xb5, 0x1e, 0x51, 0xda, 0x86, 0x3b, 0x4a, 0x8e, 0xa8, 0x8e, 0x37, 0xc3, 0x5e, 0xac, 0x36, 0xaf, - 0xd9, 0x02, 0x33, 0x9a, 0x8d, 0xd3, 0x6c, 0xaa, 0xdf, 0x15, 0x2b, 0xcf, 0x8a, 0x37, 0xb2, 0xb1, 0xb3, 0xb1, 0x6f, - 0x65, 0x01, 0xf4, 0x56, 0x0c, 0xef, 0xca, 0x64, 0xaf, 0x09, 0xd3, 0x52, 0xfe, 0x4a, 0xb7, 0xa0, 0xa6, 0xcc, 0xdc, - 0x34, 0xf1, 0x95, 0x4f, 0xb5, 0x27, 0xdd, 0x26, 0x3b, 0x9d, 0x5e, 0x69, 0xf7, 0x69, 0x6a, 0xe8, 0x49, 0xf7, 0x86, - 0x12, 0xaa, 0xe9, 0x3c, 0x0e, 0x15, 0xb0, 0x0c, 0x61, 0xaa, 0xe8, 0xe8, 0x9a, 0xc5, 0x71, 0x55, 0xfa, 0x39, 0x9c, - 0x3d, 0x57, 0x9c, 0x3d, 0xd3, 0x9c, 0x1d, 0x58, 0x05, 0x70, 0x76, 0xd9, 0x5d, 0xd5, 0x3c, 0x5b, 0xdb, 0x9e, 0x99, - 0xe4, 0xe9, 0xb9, 0xb0, 0xa5, 0x61, 0xbc, 0xb9, 0x86, 0x00, 0x95, 0xba, 0xd7, 0x47, 0x47, 0xb9, 0x62, 0xc0, 0x08, - 0x94, 0x9e, 0x4c, 0x6a, 0xba, 0x29, 0x3e, 0x3a, 0x08, 0xe7, 0x05, 0x2d, 0x29, 0xfb, 0xe4, 0x19, 0xf8, 0xea, 0x8c, - 0xe9, 0x80, 0x18, 0x13, 0xc5, 0x9f, 0xa5, 0x46, 0xe9, 0xd9, 0x31, 0x35, 0xbb, 0x5c, 0xcf, 0x0e, 0x78, 0x7d, 0x35, - 0xbb, 0xf0, 0x6e, 0x6e, 0x2f, 0xa6, 0xc7, 0xca, 0xe9, 0x55, 0xeb, 0xbd, 0x5c, 0x3a, 0x2b, 0x25, 0xe0, 0xc6, 0x57, - 0x46, 0x4a, 0x56, 0xf6, 0x0e, 0x3c, 0xc0, 0xc4, 0x0c, 0x14, 0x14, 0x72, 0xd2, 0xa5, 0x90, 0x7b, 0xf9, 0x29, 0x27, - 0x8f, 0xf0, 0xd6, 0xcb, 0xf6, 0xa7, 0xe9, 0x74, 0x06, 0xfa, 0xd8, 0x0a, 0x49, 0x4f, 0xa8, 0x1a, 0xb0, 0x7a, 0x5f, - 0x6c, 0x28, 0xab, 0xb5, 0x11, 0xfb, 0xb1, 0x46, 0x4d, 0xa5, 0xcd, 0xbc, 0xd3, 0x2e, 0xe6, 0x65, 0x51, 0xc9, 0x38, - 0x36, 0x39, 0x56, 0x4e, 0x57, 0xdd, 0x32, 0xfa, 0xc5, 0x1b, 0x87, 0x49, 0x3e, 0xcc, 0x80, 0xd7, 0x19, 0xec, 0x47, - 0x93, 0xbb, 0xb9, 0xfe, 0x45, 0x85, 0x9c, 0x45, 0xb1, 0x82, 0xbe, 0x45, 0x51, 0x3c, 0x53, 0x76, 0x36, 0x7e, 0xb6, - 0xdd, 0x20, 0xae, 0xde, 0x29, 0x7b, 0x71, 0x38, 0xc2, 0xcf, 0xd6, 0xb5, 0x47, 0xb2, 0x98, 0xa6, 0x21, 0xf5, 0xec, - 0x74, 0x46, 0x13, 0xbb, 0x00, 0xef, 0xaa, 0x5a, 0xfc, 0x39, 0x77, 0x16, 0xef, 0xea, 0x6e, 0x56, 0xef, 0x59, 0x01, - 0x2e, 0xb0, 0x1f, 0xd7, 0x1d, 0xb0, 0xdf, 0xd2, 0x2c, 0x17, 0xba, 0x68, 0xa9, 0xd6, 0xfe, 0x58, 0x09, 0xa6, 0x1f, - 0xbd, 0xad, 0xf5, 0x2b, 0x2b, 0xc4, 0xee, 0xb8, 0x0f, 0xdd, 0x7d, 0x1b, 0x09, 0xf7, 0xf0, 0x37, 0x6a, 0xc7, 0xff, - 0xa2, 0xdd, 0xc3, 0x67, 0xe4, 0x97, 0xba, 0x77, 0x78, 0xc6, 0xc9, 0xd9, 0xe0, 0x4c, 0x1b, 0xcd, 0x69, 0xcc, 0x82, - 0x5b, 0xc7, 0x8e, 0x19, 0x6f, 0x42, 0x08, 0xce, 0xc6, 0x0b, 0xf9, 0x02, 0xfc, 0x8a, 0xc2, 0xad, 0x5d, 0x68, 0x73, - 0x0f, 0x33, 0x4e, 0xec, 0xdd, 0x98, 0xf1, 0x5d, 0x1b, 0xdf, 0x92, 0x0b, 0xf8, 0xb1, 0xbb, 0x70, 0x5e, 0xf9, 0x3c, - 0x72, 0x33, 0x3f, 0x09, 0xd3, 0xa9, 0x83, 0x1a, 0xb6, 0x8d, 0xdc, 0x5c, 0x98, 0x1c, 0x8f, 0x51, 0xb1, 0x7b, 0x81, - 0xcf, 0x38, 0xb1, 0x07, 0x76, 0xe3, 0x16, 0xbf, 0xe2, 0xe4, 0xe2, 0x78, 0x77, 0x71, 0xc6, 0x8b, 0xfe, 0x05, 0x3e, - 0x29, 0x3d, 0xf7, 0xf8, 0x35, 0x71, 0x10, 0xe9, 0x9f, 0x28, 0x68, 0x4e, 0xd3, 0xa9, 0xf4, 0xe0, 0xdb, 0x08, 0xbf, - 0x13, 0xf1, 0x95, 0x8a, 0xdd, 0xa8, 0x10, 0xcb, 0x0e, 0xb1, 0x53, 0xe1, 0x25, 0xb0, 0xf7, 0xf6, 0x8c, 0xb2, 0x52, - 0x59, 0xc0, 0xa7, 0x9c, 0xd4, 0x6c, 0x72, 0xfc, 0x52, 0x44, 0x6a, 0x4e, 0xb9, 0x93, 0x20, 0xdd, 0x8d, 0xa3, 0xdd, - 0xd1, 0x6a, 0x6f, 0x26, 0x43, 0xe9, 0x64, 0x70, 0x19, 0xa7, 0x99, 0xcf, 0xd3, 0x6c, 0x84, 0x4c, 0x05, 0x04, 0xff, - 0x8d, 0x5c, 0x0c, 0xad, 0xff, 0xf4, 0xc5, 0x4f, 0xe3, 0x9f, 0xb2, 0xd1, 0x05, 0xfe, 0x40, 0x5a, 0xc7, 0xce, 0xc0, - 0x73, 0x76, 0x9a, 0xcd, 0xe5, 0x4f, 0xad, 0xe1, 0xdf, 0xfd, 0xe6, 0xaf, 0x27, 0xcd, 0x1f, 0x47, 0x68, 0xe9, 0xfc, - 0xd4, 0x1a, 0x0c, 0xd5, 0xd3, 0xf0, 0xef, 0xfd, 0x9f, 0xf2, 0xd1, 0x57, 0xb2, 0x70, 0x17, 0xa1, 0xd6, 0x04, 0x4f, - 0x38, 0x69, 0x35, 0x9b, 0xfd, 0xd6, 0x04, 0x4f, 0x39, 0x69, 0xc1, 0xbf, 0xd7, 0xe4, 0x1d, 0x9d, 0x3c, 0xbb, 0x99, - 0x39, 0x17, 0xfd, 0xe5, 0xee, 0xe2, 0x6f, 0x05, 0xf4, 0x3a, 0xfc, 0xfb, 0x4f, 0x3f, 0xe5, 0xf6, 0x83, 0x3e, 0x69, - 0x8d, 0x1a, 0xc8, 0x81, 0xd2, 0xaf, 0x88, 0xf8, 0xeb, 0x0c, 0xbc, 0xe1, 0xdf, 0x15, 0x14, 0xf6, 0x83, 0x9f, 0x2e, - 0x8e, 0xfb, 0x64, 0xb4, 0x74, 0xec, 0xe5, 0x03, 0xb4, 0x44, 0x68, 0xb9, 0x8b, 0x2e, 0xb0, 0x3d, 0xb1, 0x11, 0x1e, - 0x73, 0xd2, 0x7a, 0xd0, 0x9a, 0xe0, 0x73, 0x4e, 0x5a, 0x76, 0x6b, 0x82, 0xdf, 0x70, 0xd2, 0xfa, 0xbb, 0x33, 0xf0, - 0xa4, 0x9b, 0x6d, 0x29, 0x3c, 0x1c, 0x4b, 0x08, 0x72, 0xf8, 0x19, 0xf5, 0x97, 0x9c, 0xf1, 0x98, 0xa2, 0xdd, 0x16, - 0xc3, 0x1f, 0x05, 0x9a, 0x1c, 0x0e, 0x7e, 0x18, 0x30, 0xef, 0x9c, 0xc5, 0x39, 0x2c, 0x36, 0xd0, 0xcc, 0xae, 0x97, - 0x60, 0xe9, 0x0a, 0xc8, 0x3d, 0x8e, 0xaf, 0xfc, 0x78, 0x4e, 0x73, 0x8f, 0x16, 0x08, 0xc7, 0xe4, 0x23, 0x77, 0x3a, - 0x08, 0xbf, 0xe0, 0xf0, 0xa3, 0x8b, 0xf0, 0xa9, 0x0a, 0x64, 0xc2, 0x4e, 0x96, 0x44, 0x95, 0xa4, 0x52, 0x65, 0xb1, - 0x11, 0x9e, 0x6c, 0x78, 0xc9, 0x23, 0x70, 0x30, 0x20, 0x7c, 0x55, 0x0b, 0x7b, 0xe2, 0x1b, 0xa2, 0x49, 0xe2, 0x7d, - 0x46, 0xe9, 0x77, 0x7e, 0xfc, 0x91, 0x66, 0xce, 0x09, 0xee, 0x74, 0x1f, 0x63, 0xe1, 0x87, 0xde, 0xe9, 0xa0, 0x5e, - 0x19, 0xb3, 0x7a, 0xcb, 0x65, 0xa8, 0x00, 0xa4, 0x6c, 0xdd, 0x1d, 0x03, 0x2b, 0xbe, 0x93, 0xac, 0xf9, 0xac, 0x32, - 0xff, 0xda, 0x46, 0xf5, 0xf8, 0x28, 0x4b, 0xae, 0xfc, 0x98, 0x85, 0x16, 0xa7, 0xd3, 0x59, 0xec, 0x73, 0x6a, 0xa9, - 0xf9, 0x5a, 0x3e, 0x74, 0x64, 0x97, 0x3a, 0xc3, 0xcc, 0xb0, 0x39, 0x67, 0x3a, 0xf0, 0x04, 0x7b, 0xc5, 0x81, 0x28, - 0x95, 0xd2, 0x3b, 0x9e, 0x56, 0x41, 0xb0, 0xd5, 0x38, 0x5f, 0xb3, 0x03, 0xbe, 0xb0, 0x91, 0x90, 0xcf, 0x39, 0xce, - 0x08, 0x48, 0xd1, 0xee, 0xc0, 0x3e, 0xce, 0xaf, 0x26, 0x7d, 0x1b, 0x62, 0x34, 0x29, 0xf9, 0x20, 0x5c, 0x43, 0x50, - 0x21, 0x22, 0xed, 0x5e, 0x74, 0x4c, 0x7b, 0x51, 0xa3, 0xa1, 0xb5, 0x68, 0x9f, 0x24, 0xc3, 0x48, 0x36, 0x0f, 0x70, - 0x88, 0xe7, 0xa4, 0xd9, 0xc1, 0x33, 0xd2, 0x16, 0x4d, 0x7a, 0xb3, 0x63, 0x5f, 0x0d, 0xb3, 0xb7, 0xe7, 0xa4, 0x6e, - 0xec, 0xe7, 0xfc, 0x05, 0xd8, 0xfb, 0x64, 0x86, 0x43, 0x92, 0xba, 0xf4, 0x86, 0x06, 0x8e, 0x8f, 0x70, 0xa8, 0x38, - 0x0d, 0xea, 0xa1, 0x19, 0x31, 0xaa, 0x81, 0x19, 0x41, 0x3e, 0x0c, 0xc2, 0x61, 0x67, 0x44, 0x08, 0xb1, 0x77, 0x9a, - 0x4d, 0x7b, 0x90, 0x92, 0x09, 0xf7, 0xa0, 0xc4, 0x50, 0x96, 0xc9, 0x14, 0x8a, 0xba, 0x46, 0x91, 0xf3, 0x86, 0xbb, - 0x9c, 0xe6, 0xdc, 0x81, 0x62, 0xf0, 0x00, 0xe4, 0x9a, 0xb0, 0xed, 0xe3, 0x96, 0xdd, 0x80, 0x52, 0x41, 0x9c, 0x08, - 0xa7, 0xe4, 0x1a, 0x79, 0xe1, 0x70, 0x7f, 0x64, 0x0a, 0x00, 0x51, 0x08, 0x83, 0x5f, 0x0f, 0xc2, 0x61, 0x5b, 0x0c, - 0xde, 0xb7, 0x07, 0x4e, 0x4a, 0x72, 0xa9, 0xa1, 0x0d, 0x72, 0xef, 0x83, 0x98, 0x2a, 0xf2, 0x14, 0x70, 0x6a, 0xdc, - 0x39, 0x69, 0x76, 0x3d, 0x67, 0x6e, 0x4e, 0xa2, 0x09, 0x83, 0x29, 0x2c, 0xe0, 0x80, 0x40, 0x7d, 0x9c, 0x12, 0x18, - 0xb1, 0x6a, 0x76, 0xed, 0xa9, 0xe7, 0x07, 0xf6, 0x83, 0xc1, 0x39, 0xf7, 0xc6, 0x5c, 0x0e, 0x7f, 0xce, 0x97, 0x4b, - 0xf8, 0x77, 0xcc, 0x07, 0x29, 0xb9, 0x16, 0x45, 0x13, 0x55, 0x34, 0x85, 0xa2, 0x0f, 0x1e, 0x80, 0x8a, 0xf3, 0x52, - 0xcb, 0x92, 0x6b, 0x32, 0x25, 0x02, 0xf6, 0xbd, 0xbd, 0x64, 0x18, 0x35, 0x3a, 0x23, 0x70, 0xf2, 0x67, 0x3c, 0xff, - 0x8e, 0xf1, 0xc8, 0xb1, 0x5b, 0x7d, 0x1b, 0x0d, 0x6c, 0x0b, 0x96, 0xb6, 0x97, 0x35, 0x88, 0xc4, 0xb0, 0xdf, 0x78, - 0xc5, 0xbd, 0x79, 0x9f, 0xb4, 0x07, 0x0e, 0x53, 0x2e, 0x3d, 0x84, 0x7d, 0xc5, 0x38, 0xdb, 0x78, 0x8e, 0x1a, 0x8c, - 0x37, 0xf4, 0xf3, 0x1c, 0x35, 0x6e, 0x1b, 0x53, 0xe4, 0xf9, 0x8d, 0xdb, 0x86, 0x33, 0x27, 0x84, 0x34, 0xbb, 0x65, - 0x33, 0x2d, 0xfe, 0x22, 0xe4, 0x4d, 0xb5, 0xbf, 0x73, 0x28, 0xb6, 0x43, 0xd6, 0x70, 0x92, 0x21, 0x1d, 0x2d, 0x97, - 0xf6, 0xf1, 0xa0, 0x6f, 0xa3, 0x86, 0xa3, 0x09, 0xad, 0xa5, 0x29, 0x0d, 0x21, 0xcc, 0x46, 0x85, 0x8a, 0x27, 0x3d, - 0xa9, 0xc5, 0x8e, 0x16, 0xd5, 0x66, 0x37, 0x78, 0x00, 0x2d, 0x4a, 0x43, 0x46, 0x2a, 0xac, 0x33, 0x98, 0xa6, 0x26, - 0xe6, 0x8c, 0xb4, 0x71, 0x4a, 0xb4, 0xfb, 0x3a, 0x22, 0xbc, 0x22, 0x78, 0x9f, 0x54, 0xd5, 0xf1, 0x30, 0xc0, 0xe1, - 0x88, 0x3c, 0x95, 0x06, 0x49, 0x4f, 0x3b, 0xc7, 0x69, 0x4c, 0x9e, 0xac, 0x44, 0x71, 0x03, 0x08, 0xb0, 0xdc, 0xb8, - 0xc1, 0x3c, 0xcb, 0x68, 0xc2, 0x5f, 0xa7, 0xa1, 0xd2, 0xd3, 0x68, 0x0c, 0xa6, 0x12, 0x84, 0x67, 0x31, 0x28, 0x69, - 0x5d, 0xbd, 0x33, 0xe6, 0x6b, 0xaf, 0x67, 0x64, 0x2e, 0xf5, 0x27, 0x11, 0xb4, 0xed, 0xcd, 0x94, 0x65, 0xec, 0x20, - 0x3c, 0x57, 0xd1, 0x5c, 0xc7, 0x75, 0xdd, 0x99, 0x1b, 0xc0, 0x6b, 0x18, 0x20, 0x47, 0x85, 0xd8, 0x47, 0x4e, 0x4e, - 0x6e, 0xdc, 0x84, 0xde, 0x88, 0x51, 0x1d, 0x54, 0x49, 0x66, 0xbd, 0xbd, 0x8e, 0xa3, 0x9e, 0x60, 0x37, 0xb9, 0x9b, - 0xa4, 0x21, 0x05, 0xf4, 0x40, 0xfc, 0x5e, 0x15, 0x45, 0x7e, 0x6e, 0x06, 0xa9, 0x2a, 0xf8, 0x86, 0xa6, 0xff, 0x7a, - 0x06, 0x4e, 0x5f, 0xa1, 0x6c, 0x95, 0x95, 0xa5, 0x27, 0x1c, 0x21, 0x36, 0x76, 0x66, 0x2e, 0x04, 0xf7, 0x04, 0x09, - 0x31, 0xb0, 0xe5, 0x66, 0x26, 0x51, 0xdd, 0x96, 0x7d, 0x4e, 0x49, 0x38, 0x4c, 0x1b, 0x0d, 0xe1, 0x88, 0x9e, 0x4b, - 0x92, 0x98, 0x21, 0x3c, 0x2d, 0xf7, 0x96, 0xae, 0xf7, 0x96, 0xd4, 0x47, 0x72, 0xa6, 0x75, 0x87, 0x6e, 0x83, 0x71, - 0x24, 0x7c, 0x85, 0xdc, 0xb9, 0x45, 0x78, 0x4c, 0x5a, 0xce, 0xd0, 0x1d, 0xfc, 0x79, 0x84, 0x06, 0x8e, 0xfb, 0x15, - 0x6a, 0x49, 0xc6, 0x31, 0x45, 0x3d, 0x5f, 0x0e, 0xb1, 0x10, 0x51, 0xcc, 0x0e, 0x16, 0xbe, 0x44, 0x2f, 0xc3, 0x89, - 0x3f, 0xa5, 0xde, 0x18, 0xf6, 0xb8, 0xa6, 0x9b, 0xb7, 0x18, 0xe8, 0xc8, 0x1b, 0x2b, 0x4e, 0xe2, 0xda, 0x83, 0x5f, - 0x78, 0xf9, 0x34, 0xb0, 0x07, 0x5f, 0x57, 0x4f, 0x7f, 0xb6, 0x07, 0xdf, 0x72, 0xef, 0xdb, 0x42, 0xb9, 0xbb, 0x6b, - 0x43, 0x3c, 0xd4, 0x43, 0x14, 0x72, 0x61, 0x0c, 0xcc, 0xcd, 0xd1, 0xba, 0xa3, 0x63, 0x86, 0x0a, 0x36, 0x2e, 0x59, - 0x51, 0xee, 0x72, 0x7f, 0x02, 0x28, 0x35, 0x56, 0x20, 0x37, 0xa3, 0xfb, 0xd5, 0x84, 0x81, 0x50, 0x34, 0xb5, 0x02, - 0x2a, 0x67, 0xfd, 0x36, 0x5a, 0xd4, 0xea, 0x0a, 0x8d, 0xa9, 0x1e, 0x4d, 0x2f, 0xb9, 0xf4, 0x94, 0xb4, 0x7b, 0xd3, - 0xe3, 0x59, 0x6f, 0xda, 0x68, 0xa0, 0x5c, 0x13, 0xd6, 0x7c, 0x38, 0x1d, 0xe1, 0xd7, 0xe0, 0xd5, 0x33, 0x29, 0x09, - 0xd7, 0xa6, 0xd7, 0x55, 0xd3, 0x6b, 0x34, 0xb2, 0x02, 0xf5, 0x8c, 0xa6, 0x33, 0xd9, 0xb4, 0x28, 0x24, 0x4e, 0x56, - 0x09, 0xed, 0x08, 0x89, 0x12, 0x48, 0x89, 0x22, 0x84, 0x9c, 0x71, 0xb4, 0xb1, 0x57, 0xe8, 0x13, 0x9a, 0x8b, 0x1d, - 0x0b, 0xcc, 0x53, 0xca, 0x08, 0x07, 0xb0, 0x00, 0x4d, 0x4b, 0x57, 0xf0, 0x2d, 0x9e, 0x37, 0x3a, 0x82, 0xc8, 0x9b, - 0x9d, 0x5e, 0xbd, 0xaf, 0x47, 0x55, 0x5f, 0x78, 0xde, 0x20, 0xb7, 0x25, 0x96, 0x8a, 0xac, 0xd1, 0x28, 0xea, 0xf1, - 0x4e, 0xbd, 0x6f, 0x6b, 0x11, 0x88, 0x93, 0xd5, 0xd4, 0x0c, 0x2d, 0x5f, 0x2b, 0x89, 0xca, 0x5c, 0x96, 0x24, 0x34, - 0x03, 0x19, 0x4a, 0x38, 0x66, 0x45, 0x51, 0xca, 0xf5, 0x37, 0x20, 0x44, 0x31, 0x25, 0x09, 0xf0, 0x1d, 0x61, 0x76, - 0xe1, 0x0c, 0xa7, 0x38, 0x12, 0x5c, 0x83, 0x10, 0x72, 0xaa, 0x93, 0x5a, 0xb8, 0xe0, 0x40, 0x3e, 0x61, 0x86, 0x44, - 0xca, 0x09, 0x75, 0xcf, 0x77, 0x4f, 0xd3, 0x3b, 0x4d, 0xb2, 0x21, 0x1b, 0x79, 0xa2, 0x5a, 0xac, 0xf8, 0x56, 0x40, - 0xde, 0x39, 0x1c, 0x95, 0xe1, 0x11, 0x57, 0xb0, 0xbf, 0xa7, 0x2c, 0xa3, 0x42, 0x03, 0xdf, 0xd5, 0x66, 0x9f, 0x5f, - 0x57, 0x1f, 0x7d, 0xd3, 0x79, 0x03, 0x88, 0x0c, 0xc0, 0xb7, 0x93, 0x91, 0xb5, 0x6a, 0xe7, 0xbb, 0x27, 0x6f, 0x36, - 0x99, 0xc0, 0xcb, 0xa5, 0x32, 0x7e, 0x7d, 0xd0, 0x6c, 0x70, 0x50, 0x41, 0xea, 0xab, 0x1f, 0x9e, 0xe3, 0x0b, 0x05, - 0x29, 0x70, 0x12, 0xa0, 0xa2, 0xf3, 0xdd, 0x93, 0xf7, 0x4e, 0x22, 0x5c, 0x4b, 0x08, 0x9b, 0xd3, 0x76, 0x52, 0xe2, - 0x44, 0x84, 0x22, 0x39, 0xf7, 0x92, 0x71, 0xa5, 0x86, 0xf8, 0xf6, 0x22, 0xf1, 0x12, 0xec, 0x87, 0x21, 0x1b, 0x11, - 0x5f, 0x61, 0x80, 0xf8, 0x08, 0xfb, 0x35, 0xb3, 0x8c, 0xc0, 0x02, 0x88, 0xb1, 0xce, 0x60, 0x25, 0x5c, 0xa9, 0xf8, - 0x21, 0xec, 0x8b, 0x51, 0x79, 0x21, 0x45, 0xc7, 0xcf, 0x6b, 0xb9, 0x69, 0x95, 0x35, 0xfa, 0x2d, 0x58, 0x4e, 0xfa, - 0xe1, 0xb5, 0xea, 0xba, 0x2c, 0x78, 0xaa, 0x93, 0xc8, 0xce, 0x77, 0x4f, 0x5e, 0xa9, 0x3c, 0xb2, 0x99, 0xaf, 0xb9, - 0xfd, 0x9a, 0x85, 0x79, 0xf2, 0xca, 0xad, 0xde, 0x8a, 0xca, 0xe7, 0xbb, 0x27, 0x1f, 0x36, 0x55, 0x83, 0xf2, 0x62, - 0x5e, 0x99, 0xf8, 0x02, 0xbe, 0x05, 0x8d, 0xbd, 0x85, 0x12, 0x0d, 0x1e, 0x2b, 0xb0, 0x10, 0x47, 0x5e, 0x5e, 0x94, - 0x9e, 0x91, 0xa7, 0x38, 0x23, 0x22, 0x0e, 0x54, 0x5f, 0x35, 0xa5, 0xe4, 0xb1, 0x34, 0x39, 0x0b, 0xd2, 0x19, 0xdd, - 0x12, 0x1c, 0x3a, 0x41, 0x2e, 0x9b, 0x42, 0x02, 0x8d, 0x00, 0x9d, 0xe1, 0x9d, 0x36, 0xea, 0xd5, 0x85, 0x57, 0x26, - 0x88, 0x34, 0xad, 0x49, 0x16, 0x1c, 0x91, 0x36, 0xf6, 0x49, 0x1b, 0x07, 0x24, 0x1f, 0xb6, 0xa5, 0x78, 0xe8, 0x05, - 0x65, 0xbf, 0x52, 0xc8, 0x40, 0x6e, 0x58, 0x20, 0x77, 0xab, 0x14, 0xbf, 0x61, 0x2f, 0x10, 0xae, 0x47, 0x21, 0xd1, - 0x43, 0x69, 0xb4, 0x3a, 0x29, 0x4e, 0x45, 0xc7, 0x67, 0xec, 0x32, 0x86, 0xec, 0x12, 0x98, 0x15, 0xe6, 0xc8, 0x2b, - 0xab, 0x76, 0x54, 0xd5, 0xc0, 0x15, 0xeb, 0x94, 0xe2, 0xc0, 0x05, 0xc6, 0x8d, 0x03, 0x95, 0x8c, 0x93, 0xaf, 0x37, - 0x79, 0xb8, 0xb7, 0xe7, 0xc8, 0x46, 0xdf, 0x71, 0x27, 0xd5, 0xef, 0xab, 0xd0, 0xdd, 0xb7, 0x92, 0x57, 0x84, 0x48, - 0xc0, 0xdf, 0x68, 0xf8, 0xa3, 0x02, 0xe2, 0xd0, 0x4e, 0x50, 0xc7, 0xa0, 0x06, 0x5e, 0x68, 0x7a, 0xf5, 0xe9, 0x37, - 0x1a, 0x65, 0x98, 0xb6, 0x8e, 0xad, 0x13, 0x9c, 0x15, 0x57, 0x4e, 0x99, 0xff, 0xd3, 0x5e, 0xcb, 0x9a, 0xd2, 0x20, - 0x20, 0x66, 0xd2, 0x2c, 0xd3, 0x93, 0x31, 0xb6, 0x04, 0x83, 0x7a, 0x2f, 0x54, 0xe2, 0x02, 0x16, 0x39, 0x56, 0xaa, - 0x92, 0x66, 0x67, 0x5d, 0xe4, 0xe9, 0x4a, 0x10, 0x96, 0x82, 0x4a, 0x8d, 0x42, 0x91, 0xf7, 0xab, 0xf5, 0xcc, 0x4b, - 0x9c, 0x23, 0xe5, 0xe3, 0x12, 0x50, 0x08, 0x64, 0x75, 0x4b, 0xa4, 0x3c, 0x27, 0x93, 0xed, 0x24, 0x7f, 0x62, 0x90, - 0xfc, 0x13, 0x42, 0x0d, 0xf2, 0x97, 0x1e, 0x0e, 0x37, 0x55, 0xae, 0x85, 0x5c, 0xbf, 0x3a, 0x9d, 0x11, 0xf0, 0xa1, - 0xd5, 0x31, 0x5a, 0x8b, 0x2b, 0x6e, 0x61, 0x28, 0xe6, 0x0e, 0x11, 0x5e, 0x48, 0xac, 0x83, 0xc0, 0x4e, 0x15, 0x55, - 0x83, 0xa1, 0x37, 0xb9, 0xf4, 0x4c, 0x0e, 0x78, 0xf2, 0xe1, 0xee, 0x80, 0xe8, 0xe9, 0x6c, 0x7d, 0xe7, 0x1a, 0x19, - 0xa0, 0x30, 0x6b, 0x63, 0xe3, 0xd6, 0xf3, 0x41, 0x61, 0xfc, 0x32, 0x90, 0x5d, 0x67, 0x3e, 0x2b, 0x9b, 0x50, 0xcb, - 0x3f, 0x80, 0xb6, 0xd3, 0x11, 0x35, 0xa8, 0xd1, 0x2d, 0xf0, 0x23, 0x99, 0x87, 0xea, 0x67, 0x5b, 0xd8, 0xc7, 0x89, - 0xa8, 0x40, 0x93, 0x70, 0xf3, 0xeb, 0x27, 0x85, 0x22, 0x13, 0x09, 0x1a, 0x5a, 0x00, 0xff, 0x93, 0x24, 0x0f, 0x74, - 0x23, 0xe4, 0x02, 0x20, 0x68, 0x22, 0xf0, 0x54, 0x21, 0xcc, 0xb6, 0x2b, 0xe7, 0xfb, 0xf3, 0x1d, 0x42, 0x26, 0x95, - 0xf3, 0xf1, 0x5d, 0x95, 0x7d, 0x05, 0x64, 0x81, 0x3c, 0x30, 0x1e, 0xcb, 0x02, 0x19, 0xbf, 0x3c, 0xd5, 0xd5, 0x85, - 0x01, 0xe9, 0x56, 0xfa, 0xb6, 0x11, 0xdb, 0x14, 0x5e, 0x39, 0xf9, 0x5e, 0xa3, 0x61, 0xe5, 0xed, 0x2e, 0xbc, 0x7d, - 0xc9, 0x05, 0x8c, 0xf0, 0xfc, 0x5e, 0xd4, 0xd6, 0xfd, 0x16, 0x1f, 0x57, 0x53, 0x58, 0x56, 0x16, 0xc5, 0x65, 0x49, - 0x4e, 0x33, 0xfe, 0x84, 0x8e, 0xd3, 0x0c, 0x42, 0x16, 0x25, 0x4e, 0x50, 0xb1, 0x6b, 0xb8, 0xed, 0xc4, 0xfc, 0x8c, - 0x38, 0xc1, 0xca, 0x04, 0xc5, 0xaf, 0x8f, 0x22, 0x6a, 0x7d, 0xbe, 0xda, 0x6a, 0xb2, 0xb7, 0xf7, 0xae, 0x42, 0x93, - 0x82, 0x52, 0x40, 0x61, 0x30, 0x2d, 0xa9, 0xd2, 0xa8, 0x50, 0xee, 0xae, 0x53, 0xba, 0x00, 0x34, 0xc3, 0x30, 0x79, - 0xcf, 0x73, 0xc2, 0x8b, 0xc9, 0x2a, 0x8b, 0x57, 0xae, 0x09, 0x66, 0x9a, 0x2d, 0xc0, 0xe1, 0xc1, 0xd0, 0x96, 0xbe, - 0xa2, 0xbc, 0x4a, 0x89, 0x2d, 0x61, 0x38, 0x05, 0x64, 0x39, 0xc2, 0x08, 0x31, 0x28, 0x70, 0xa3, 0x51, 0xf2, 0x16, - 0xf4, 0xca, 0x08, 0xe7, 0x6e, 0x04, 0x49, 0xb0, 0xb5, 0x2d, 0x8b, 0x10, 0x96, 0x99, 0x39, 0x46, 0x2e, 0xc1, 0xc9, - 0xf3, 0x4d, 0x1e, 0x65, 0x4d, 0xd4, 0x54, 0x48, 0x1d, 0xa8, 0x91, 0xa1, 0xb2, 0x81, 0x7b, 0xe5, 0x30, 0xa5, 0xb8, - 0xe9, 0xb8, 0x19, 0x30, 0xe0, 0x9f, 0xb9, 0x23, 0x63, 0x51, 0x20, 0x33, 0x52, 0x77, 0xee, 0xd4, 0x86, 0xee, 0xa5, - 0xa2, 0x19, 0x56, 0x88, 0x8b, 0x4c, 0x34, 0xa5, 0x22, 0xae, 0x77, 0x5a, 0xf1, 0xd2, 0x2b, 0x99, 0x47, 0xcd, 0x35, - 0x17, 0xac, 0x32, 0x49, 0x8c, 0xe9, 0x5f, 0xc9, 0xd4, 0xe8, 0xb2, 0x12, 0xa8, 0x61, 0xf4, 0xda, 0x7a, 0x22, 0xd6, - 0x80, 0x16, 0x40, 0x5f, 0x8b, 0x53, 0x6e, 0xac, 0xa8, 0xf6, 0x61, 0x8b, 0x31, 0x0d, 0xa9, 0xff, 0x0e, 0x72, 0x5d, - 0x56, 0xf7, 0xfc, 0x73, 0x21, 0x0b, 0x19, 0xce, 0x6b, 0x8c, 0x3d, 0x13, 0x8c, 0x1d, 0x81, 0x9e, 0xa6, 0xd3, 0xbf, - 0x07, 0x2a, 0xe5, 0x45, 0xe5, 0x2e, 0x3a, 0x8a, 0xc4, 0x5e, 0x97, 0xe1, 0x72, 0xe3, 0xf7, 0xca, 0x6a, 0x78, 0x8c, - 0x40, 0x1a, 0x10, 0x56, 0x9c, 0x3d, 0x43, 0x38, 0x6f, 0x34, 0x7a, 0xf9, 0x31, 0xad, 0x5c, 0x24, 0x15, 0x8c, 0x0c, - 0x22, 0xba, 0x40, 0xf0, 0x35, 0x19, 0x0a, 0x31, 0x7f, 0x9d, 0x9f, 0x9d, 0x83, 0xab, 0xfd, 0xe4, 0x9d, 0x63, 0x72, - 0x35, 0xb3, 0x6e, 0x19, 0x34, 0x85, 0xf9, 0x38, 0x55, 0xbc, 0xe5, 0xed, 0xdd, 0x19, 0x1e, 0x00, 0xf7, 0x4e, 0x07, - 0x43, 0x36, 0x1a, 0xea, 0x71, 0xc9, 0x12, 0xca, 0xdd, 0xd7, 0x43, 0x55, 0x62, 0xa2, 0x39, 0x58, 0x8f, 0x57, 0xa6, - 0x2c, 0x27, 0x79, 0x51, 0xe4, 0xb4, 0x8a, 0xef, 0xaf, 0x64, 0x60, 0x0a, 0xe1, 0xb2, 0xee, 0x6c, 0x3f, 0x9d, 0x11, - 0x8e, 0x0d, 0x42, 0x7d, 0xbb, 0x2d, 0xf4, 0x51, 0x81, 0x09, 0xfb, 0x5a, 0x09, 0xc5, 0x6f, 0x37, 0x09, 0x45, 0x9c, - 0xa9, 0x2d, 0x2f, 0x04, 0x62, 0xe7, 0x1e, 0x02, 0x51, 0x39, 0xd9, 0xb5, 0x4c, 0x04, 0x75, 0xa4, 0x26, 0x13, 0xeb, - 0x4b, 0x4a, 0x32, 0xcc, 0xd4, 0x6a, 0xf4, 0xbb, 0xcb, 0x25, 0x1b, 0xb6, 0xc1, 0x89, 0x64, 0xdb, 0xf0, 0xb3, 0x23, - 0x7f, 0x1a, 0x9c, 0x58, 0x3a, 0x81, 0x1d, 0x56, 0x9a, 0x2c, 0xc8, 0x85, 0x34, 0x67, 0x47, 0x64, 0x65, 0x09, 0x9a, - 0x56, 0x14, 0xa4, 0x08, 0x9c, 0xb0, 0x32, 0xca, 0x04, 0x10, 0x0b, 0x59, 0xa1, 0x0c, 0x48, 0x67, 0x63, 0xfa, 0x9f, - 0x36, 0x2f, 0x3f, 0xad, 0x89, 0xd6, 0xe4, 0x8a, 0x54, 0x1f, 0x6a, 0x09, 0x07, 0x0a, 0x02, 0xa5, 0x1f, 0xee, 0x08, - 0x13, 0xb4, 0x12, 0xe5, 0xc8, 0x94, 0x43, 0xb8, 0x0d, 0x2e, 0xb4, 0x9d, 0x77, 0x32, 0xc0, 0xbb, 0x41, 0x9a, 0xe0, - 0xd4, 0xa0, 0xeb, 0xe7, 0x84, 0xd7, 0x58, 0x49, 0x44, 0x94, 0xa5, 0x84, 0x03, 0x41, 0xa6, 0x9c, 0x64, 0xc3, 0xf6, - 0x08, 0x14, 0xd0, 0x9e, 0x7f, 0x9c, 0x55, 0x26, 0xb0, 0xdf, 0x68, 0xa0, 0x40, 0x8f, 0x1a, 0x0d, 0x59, 0xc3, 0x1f, - 0x61, 0x8a, 0x7d, 0x69, 0x98, 0x9c, 0xee, 0xed, 0x39, 0x41, 0x35, 0xee, 0xd0, 0x1f, 0x21, 0x9c, 0x2e, 0x97, 0x8e, - 0x00, 0x2b, 0x40, 0xcb, 0x65, 0x60, 0x82, 0x25, 0x5e, 0x43, 0xb3, 0xc9, 0x80, 0x93, 0x89, 0x10, 0x80, 0x13, 0x80, - 0xb0, 0x41, 0x9c, 0x40, 0x39, 0xf7, 0x02, 0x70, 0x46, 0x35, 0xb2, 0xa1, 0xdf, 0xe8, 0x8c, 0x0c, 0xc6, 0x35, 0xf4, - 0x47, 0x24, 0x28, 0xd2, 0xbd, 0xbd, 0x9d, 0x5c, 0x89, 0xc8, 0x9f, 0x41, 0x94, 0xfd, 0x2c, 0x24, 0x8b, 0xec, 0xd0, - 0x5c, 0x8d, 0x55, 0x67, 0x40, 0x49, 0x51, 0x6a, 0x59, 0x75, 0xbd, 0x5a, 0x16, 0x44, 0x59, 0x09, 0xab, 0x58, 0xf0, - 0x00, 0x2c, 0xfb, 0x92, 0xcc, 0x7f, 0xe1, 0x65, 0x9a, 0xf5, 0xb7, 0x1b, 0x93, 0xab, 0x5d, 0xd7, 0xf5, 0xb3, 0x89, - 0x88, 0x64, 0xe8, 0x28, 0xac, 0x20, 0xfe, 0x7d, 0x05, 0xa6, 0x31, 0xf0, 0xb0, 0x1c, 0x6b, 0x44, 0x24, 0xf8, 0x5a, - 0xb5, 0xd1, 0x27, 0x4a, 0x7e, 0xdd, 0xe8, 0x65, 0x90, 0x90, 0x7c, 0xfd, 0x5b, 0x21, 0x39, 0x50, 0x90, 0x48, 0xf2, - 0x58, 0xc1, 0xd9, 0x16, 0x5c, 0xfc, 0xca, 0x57, 0x70, 0xb6, 0x1d, 0xb7, 0x25, 0x43, 0xd8, 0x06, 0x9f, 0xc1, 0x1b, - 0x24, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0xe1, 0xaa, 0xee, 0x25, 0x59, 0x29, 0x08, 0x53, 0x4e, 0x1c, 0x56, 0xdf, 0x00, - 0x95, 0x36, 0x6a, 0x18, 0xbe, 0xcc, 0x9b, 0x20, 0xc3, 0x25, 0x50, 0x4f, 0x5d, 0x01, 0x72, 0x52, 0xbe, 0x76, 0x48, - 0x45, 0xd8, 0x91, 0x4a, 0x9c, 0x1b, 0xf8, 0x33, 0x3e, 0xcf, 0x40, 0x95, 0xca, 0xf5, 0x6f, 0x28, 0x86, 0xb3, 0x20, - 0xa2, 0x0c, 0x7e, 0x40, 0xc1, 0xcc, 0xcf, 0x73, 0x76, 0x25, 0xcb, 0xd4, 0x6f, 0x9c, 0x12, 0x4d, 0xca, 0xb9, 0xd4, - 0x09, 0x33, 0xd4, 0xcb, 0x14, 0x9d, 0xd6, 0xd1, 0xf6, 0xec, 0x8a, 0x26, 0xfc, 0x25, 0xcb, 0x39, 0x4d, 0x60, 0xfa, - 0x15, 0xc5, 0xc1, 0x8c, 0x72, 0x04, 0x1b, 0xb6, 0xd6, 0xca, 0x0f, 0xc3, 0x3b, 0x9b, 0xf0, 0xba, 0x0e, 0x14, 0xf9, - 0x49, 0x18, 0xcb, 0x41, 0xcc, 0x84, 0x46, 0x9d, 0xc4, 0x59, 0xd6, 0x34, 0xf3, 0x69, 0x2a, 0x65, 0x43, 0x70, 0x77, - 0x87, 0x11, 0x2d, 0x09, 0xb4, 0xf4, 0xbc, 0x53, 0x6b, 0x81, 0x80, 0xf7, 0x96, 0x45, 0x30, 0x67, 0x82, 0xb9, 0xc1, - 0x51, 0xdd, 0x3a, 0x9c, 0x9a, 0x6e, 0xbe, 0xdb, 0x78, 0xb0, 0x6d, 0x93, 0x70, 0x10, 0x74, 0xf2, 0x70, 0xbb, 0x65, - 0xf5, 0x4a, 0x4b, 0x0e, 0x2d, 0x2d, 0xd8, 0x7d, 0x19, 0x33, 0x5a, 0x68, 0xf2, 0x42, 0x7a, 0x2b, 0xee, 0x72, 0xf2, - 0x0b, 0x9c, 0x1c, 0x7a, 0xce, 0xa7, 0xf1, 0xca, 0x01, 0x99, 0xde, 0x6e, 0xa9, 0xfd, 0xef, 0x72, 0xe7, 0x09, 0x7e, - 0x05, 0x61, 0xdd, 0x6f, 0xaa, 0xea, 0xeb, 0xe1, 0xdc, 0x6f, 0x2a, 0x04, 0x7d, 0xe3, 0xad, 0xd5, 0x33, 0xc2, 0xb8, - 0x5d, 0xf7, 0xc8, 0x6d, 0xdb, 0x5a, 0x5b, 0xfa, 0x51, 0x06, 0x91, 0x64, 0xaa, 0xa5, 0xd8, 0x0f, 0xb8, 0x4a, 0x54, - 0x83, 0x84, 0xb9, 0xba, 0x85, 0x44, 0x55, 0x8a, 0xa1, 0xd4, 0xe1, 0xb7, 0x2d, 0x8f, 0x92, 0x31, 0x99, 0xb4, 0x33, - 0xde, 0xfa, 0x19, 0xdf, 0x85, 0x5d, 0x96, 0xae, 0x9d, 0xc6, 0x8b, 0x08, 0x78, 0xd0, 0xee, 0x37, 0x84, 0x61, 0x6c, - 0xe7, 0xf2, 0x30, 0x90, 0xd9, 0x3f, 0x49, 0xb5, 0xee, 0x56, 0xb7, 0x32, 0x5e, 0x83, 0xfd, 0x8f, 0x70, 0xa4, 0x8f, - 0xc8, 0x51, 0xc5, 0x81, 0xa9, 0xb7, 0x28, 0x4a, 0xa7, 0x40, 0x2a, 0x95, 0xb7, 0x04, 0xe1, 0xb4, 0x10, 0xe1, 0xed, - 0xef, 0xf1, 0x0f, 0x8a, 0x25, 0x9e, 0x97, 0x1c, 0xe7, 0xd9, 0x7d, 0x39, 0xa2, 0x04, 0xbf, 0x8c, 0xde, 0x03, 0x1d, - 0x0b, 0x0a, 0x2d, 0x34, 0x15, 0x3d, 0x4d, 0xd5, 0x44, 0xb6, 0xe6, 0xa5, 0x62, 0x5a, 0x66, 0xd4, 0x88, 0x61, 0x36, - 0x24, 0x72, 0x6a, 0x2b, 0x9b, 0x97, 0xbb, 0xaa, 0x36, 0x2e, 0xda, 0x82, 0xc5, 0x2a, 0xb0, 0xb8, 0x5c, 0x3a, 0x75, - 0x54, 0x13, 0x66, 0xc4, 0x31, 0x10, 0x66, 0x46, 0x42, 0x45, 0x4d, 0xb3, 0x96, 0x6d, 0x1c, 0xb4, 0x9a, 0x4f, 0xa4, - 0x75, 0xf3, 0x1a, 0x1c, 0xa6, 0x0b, 0x41, 0x36, 0x37, 0x7d, 0x0a, 0x58, 0xce, 0xae, 0x1c, 0xc8, 0xc0, 0xd0, 0x8f, - 0x65, 0xae, 0x6c, 0x95, 0xd4, 0xba, 0x01, 0xbf, 0xe8, 0x8e, 0x6c, 0x59, 0x85, 0xba, 0xf5, 0xf7, 0x46, 0xae, 0xd1, - 0xd3, 0x74, 0x5b, 0xae, 0x51, 0x4d, 0xdb, 0xdd, 0x69, 0xa3, 0xbb, 0xf3, 0x52, 0xe5, 0x58, 0x9b, 0xab, 0xfc, 0x86, - 0xe1, 0x3a, 0x40, 0x9b, 0x12, 0xcd, 0x9a, 0xab, 0x9c, 0x16, 0xc5, 0x79, 0x79, 0x9a, 0x40, 0xa4, 0xee, 0x9c, 0x4b, - 0xfa, 0x57, 0x56, 0xa3, 0x38, 0x94, 0xeb, 0x7c, 0x4f, 0x26, 0x71, 0x7a, 0xe9, 0xc7, 0xef, 0x61, 0xbc, 0xea, 0xe5, - 0xf3, 0xdb, 0x30, 0xf3, 0x39, 0x55, 0xdc, 0xa5, 0x82, 0xe1, 0x7b, 0x03, 0x86, 0xef, 0x25, 0x9f, 0xae, 0xda, 0xe3, - 0xc5, 0xcb, 0xb2, 0x03, 0xef, 0xbc, 0xd0, 0x2c, 0xe3, 0x96, 0x6f, 0x1e, 0x63, 0x95, 0x85, 0xdd, 0x96, 0x2c, 0xec, - 0x96, 0x3b, 0xab, 0x5d, 0x39, 0xce, 0x0f, 0x9b, 0x7b, 0x59, 0xe7, 0x6c, 0x3f, 0x54, 0x1b, 0xff, 0x07, 0xef, 0xce, - 0x36, 0x06, 0x97, 0xdb, 0x77, 0xf7, 0x45, 0xb2, 0x8a, 0x04, 0xf9, 0x25, 0x24, 0x1d, 0x70, 0xd2, 0x37, 0x0e, 0x1d, - 0x54, 0x72, 0x4a, 0xe7, 0x01, 0x39, 0xc1, 0x3c, 0xe7, 0xe9, 0x54, 0xf5, 0x99, 0xab, 0x93, 0x46, 0xe2, 0x25, 0xb8, - 0xa2, 0x45, 0xac, 0xdd, 0xab, 0x9f, 0xe5, 0x5a, 0x7c, 0x64, 0x49, 0xe8, 0xe5, 0x58, 0x49, 0x91, 0xdc, 0xcb, 0x0a, - 0xa2, 0xb3, 0x8d, 0xd7, 0xdf, 0xe1, 0x31, 0x4b, 0x58, 0x1e, 0xd1, 0xcc, 0x49, 0xd1, 0x62, 0xdb, 0x60, 0x29, 0x04, - 0x64, 0xe4, 0x60, 0xf8, 0xaf, 0xd5, 0xa9, 0x3f, 0x17, 0x7a, 0x03, 0x3f, 0xd0, 0x94, 0xf2, 0x28, 0x0d, 0x21, 0x2d, - 0xc5, 0x0d, 0xcb, 0x43, 0x4d, 0x7b, 0x7b, 0x3b, 0x8e, 0x2d, 0xdc, 0x12, 0x70, 0x00, 0xdc, 0x7c, 0x83, 0x06, 0x0b, - 0x38, 0x9f, 0x53, 0x0d, 0x4d, 0xd1, 0x82, 0xae, 0x1e, 0x65, 0xe1, 0xee, 0x47, 0x7a, 0x8b, 0x13, 0x54, 0x14, 0x9e, - 0x84, 0xda, 0x1e, 0x33, 0x1a, 0x87, 0x36, 0xfe, 0x48, 0x6f, 0xbd, 0xf2, 0xcc, 0xb8, 0x38, 0xe2, 0x2c, 0x16, 0xd0, - 0x4e, 0xaf, 0x13, 0x1b, 0x57, 0x83, 0x78, 0x8b, 0x02, 0xa7, 0x19, 0x9b, 0x00, 0x71, 0x7e, 0x43, 0x6f, 0x3d, 0xd9, - 0x1f, 0x33, 0xce, 0xeb, 0xa1, 0x85, 0x46, 0xbd, 0x6b, 0x14, 0x9b, 0xcb, 0xa0, 0x0c, 0x8a, 0xa1, 0x68, 0x3b, 0x22, - 0xb5, 0x7a, 0x95, 0x79, 0x88, 0x50, 0x71, 0xdf, 0xa9, 0xe0, 0x6f, 0x4c, 0xd1, 0xc6, 0x6b, 0x99, 0xaf, 0x2b, 0x8d, - 0x28, 0x34, 0xa8, 0x32, 0x3d, 0x76, 0x9d, 0x44, 0xef, 0x3a, 0x75, 0x08, 0xc1, 0x70, 0x84, 0x7d, 0xc3, 0x55, 0xa7, - 0xde, 0x5f, 0x65, 0x42, 0x48, 0x15, 0x49, 0x7a, 0x51, 0xb5, 0xb3, 0x76, 0x1d, 0xc0, 0x3b, 0x24, 0xb4, 0xf8, 0xe2, - 0x4c, 0x66, 0xa1, 0xb3, 0x45, 0xff, 0xc6, 0x89, 0xb3, 0xd0, 0x53, 0xf0, 0x12, 0x13, 0x8b, 0xbc, 0x00, 0x2a, 0x54, - 0xf4, 0x25, 0x13, 0x00, 0xd9, 0xd8, 0x61, 0x6b, 0x52, 0x33, 0x13, 0x52, 0xd3, 0x35, 0x30, 0xbe, 0x45, 0x4a, 0x52, - 0x81, 0x0c, 0xa1, 0x44, 0x0a, 0xa1, 0xa7, 0x16, 0x57, 0x91, 0x90, 0xb9, 0xa0, 0xe5, 0x09, 0x3a, 0xb9, 0xe6, 0x59, - 0x0d, 0x2c, 0x47, 0xf4, 0x83, 0x0a, 0x0f, 0xa6, 0x44, 0x65, 0x85, 0xa2, 0x3c, 0x9a, 0xad, 0xd3, 0x5b, 0x9d, 0xd4, - 0xd5, 0xd3, 0x22, 0x1a, 0x25, 0x4e, 0x84, 0x16, 0x89, 0x13, 0xe1, 0x0c, 0xd2, 0x11, 0xd3, 0xa2, 0x84, 0x9f, 0x9a, - 0xab, 0x51, 0x4b, 0x56, 0xde, 0x7c, 0xca, 0x0f, 0x94, 0x79, 0x0e, 0x29, 0x9a, 0x38, 0xd1, 0x3c, 0x25, 0x71, 0xc4, - 0x71, 0x3b, 0x63, 0xd9, 0xbe, 0x57, 0x09, 0x3a, 0x0a, 0xb0, 0xbf, 0x71, 0x67, 0x61, 0xcc, 0xc2, 0x3c, 0xd1, 0xad, - 0x4e, 0xfd, 0xa9, 0x60, 0x5f, 0x95, 0x43, 0xea, 0xe4, 0x64, 0x45, 0xe2, 0xdc, 0x9d, 0x6a, 0xf9, 0xcb, 0x9c, 0x66, - 0xb7, 0x67, 0x14, 0x52, 0x9d, 0x53, 0x38, 0xf0, 0x5b, 0x2d, 0x43, 0x95, 0xa7, 0x3e, 0xc8, 0x84, 0xb2, 0x52, 0xd4, - 0xcf, 0x01, 0xae, 0x9e, 0x12, 0x2c, 0x44, 0xb4, 0xd1, 0x70, 0xc4, 0xc8, 0xdd, 0x42, 0xb7, 0x9e, 0x9f, 0xa4, 0x3d, - 0x06, 0xfe, 0xb5, 0x0a, 0xd3, 0x2a, 0x58, 0x80, 0x53, 0xf3, 0x4c, 0xea, 0x30, 0x1f, 0xad, 0x7a, 0x65, 0xa0, 0x08, - 0xc2, 0x77, 0xd9, 0xf6, 0xa9, 0x6e, 0x4a, 0x9a, 0xdd, 0x3e, 0xd5, 0x5a, 0xd0, 0x4f, 0x24, 0xfc, 0x60, 0x35, 0x4e, - 0x79, 0x82, 0x99, 0x15, 0x05, 0x2a, 0x00, 0xbc, 0xbf, 0xf4, 0x1c, 0xe7, 0x2f, 0x2a, 0x65, 0xd0, 0x85, 0x58, 0xec, - 0x59, 0x9c, 0x6a, 0x26, 0x5e, 0x8d, 0xff, 0x97, 0xb5, 0xf1, 0xff, 0x62, 0x9c, 0x3a, 0x05, 0xd3, 0x68, 0x92, 0xd0, - 0x50, 0xb3, 0x4e, 0x24, 0x09, 0x50, 0xe8, 0x6d, 0x19, 0x27, 0x1f, 0x2f, 0x3c, 0xd0, 0xb8, 0x16, 0xe3, 0x34, 0xe1, - 0xcd, 0xb1, 0x3f, 0x65, 0xf1, 0xad, 0x37, 0x67, 0xcd, 0x69, 0x9a, 0xa4, 0xf9, 0xcc, 0x0f, 0x28, 0xce, 0x6f, 0x73, - 0x4e, 0xa7, 0xcd, 0x39, 0xc3, 0xcf, 0x69, 0x7c, 0x45, 0x39, 0x0b, 0x7c, 0x6c, 0x9f, 0x64, 0xcc, 0x8f, 0xad, 0xd7, - 0x7e, 0x96, 0xa5, 0xd7, 0x36, 0x7e, 0x97, 0x5e, 0xa6, 0x3c, 0xc5, 0x6f, 0x6e, 0x6e, 0x27, 0x34, 0xc1, 0x1f, 0x2e, - 0xe7, 0x09, 0x9f, 0xe3, 0xdc, 0x4f, 0xf2, 0x66, 0x4e, 0x33, 0x36, 0xee, 0x05, 0x69, 0x9c, 0x66, 0x4d, 0xc8, 0xd8, - 0x9e, 0x52, 0x2f, 0x66, 0x93, 0x88, 0x5b, 0xa1, 0x9f, 0x7d, 0xec, 0x35, 0x9b, 0xb3, 0x8c, 0x4d, 0xfd, 0xec, 0xb6, - 0x29, 0x6a, 0x78, 0x5f, 0xb6, 0xf7, 0xfd, 0xc7, 0xe3, 0x83, 0x1e, 0xcf, 0xfc, 0x24, 0x67, 0xb0, 0x4c, 0x9e, 0x1f, - 0xc7, 0xd6, 0xfe, 0x61, 0x7b, 0x9a, 0xef, 0xc8, 0x40, 0x9e, 0x9f, 0xf0, 0xe2, 0x02, 0xbf, 0x07, 0xb8, 0xdd, 0x4b, - 0x9e, 0xe0, 0xcb, 0x39, 0xe7, 0x69, 0xb2, 0x08, 0xe6, 0x59, 0x9e, 0x66, 0xde, 0x2c, 0x65, 0x09, 0xa7, 0x59, 0xef, - 0x32, 0xcd, 0x42, 0x9a, 0x35, 0x33, 0x3f, 0x64, 0xf3, 0xdc, 0x3b, 0x98, 0xdd, 0xf4, 0x40, 0xb3, 0x98, 0x64, 0xe9, - 0x3c, 0x09, 0xd5, 0x58, 0x2c, 0x89, 0x68, 0xc6, 0xb8, 0xf9, 0x42, 0x5c, 0x64, 0xe2, 0xc5, 0x2c, 0xa1, 0x7e, 0xd6, - 0x9c, 0x40, 0x63, 0x30, 0x8b, 0xda, 0x21, 0x9d, 0xe0, 0x6c, 0x72, 0xe9, 0x3b, 0x9d, 0xee, 0x23, 0xac, 0xff, 0x77, - 0x0f, 0x91, 0xd5, 0xde, 0x5c, 0xdc, 0x69, 0xb7, 0xff, 0x84, 0x7a, 0x2b, 0xa3, 0x08, 0x80, 0xbc, 0xce, 0xec, 0xc6, - 0xca, 0x53, 0xc8, 0x68, 0xdb, 0xd4, 0xb2, 0x37, 0xf3, 0x43, 0xc8, 0x07, 0xf6, 0xba, 0xb3, 0x9b, 0x02, 0x66, 0xe7, - 0xc9, 0x14, 0x53, 0x35, 0x49, 0xf5, 0xb4, 0xf8, 0xad, 0x10, 0x1f, 0x6d, 0x86, 0xb8, 0xab, 0x21, 0xae, 0xb0, 0xde, - 0x0c, 0xe7, 0x99, 0x88, 0xad, 0x7a, 0x9d, 0x5c, 0x02, 0x12, 0xa5, 0x57, 0x34, 0xd3, 0x70, 0x88, 0x87, 0xdf, 0x0c, - 0x46, 0x77, 0x33, 0x18, 0x47, 0x9f, 0x02, 0x23, 0x4b, 0xc2, 0x45, 0x7d, 0x5d, 0x3b, 0x19, 0x9d, 0xf6, 0x22, 0x0a, - 0xf4, 0xe4, 0x75, 0xe1, 0xf7, 0x35, 0x0b, 0x79, 0x24, 0x7f, 0x0a, 0x72, 0xbe, 0x96, 0xef, 0x0e, 0xdb, 0x6d, 0xf9, - 0x9c, 0xb3, 0x5f, 0xa9, 0xd7, 0x71, 0xa1, 0x42, 0x71, 0x81, 0x7f, 0x28, 0x4f, 0xf3, 0xd6, 0xb9, 0x27, 0xfe, 0x8b, - 0x79, 0xcc, 0xd7, 0x48, 0x51, 0xac, 0x0e, 0x45, 0xe3, 0x54, 0xcb, 0x4a, 0x29, 0x7c, 0xc0, 0x6d, 0x27, 0xb8, 0x23, - 0x61, 0xfd, 0xf2, 0x18, 0x27, 0x1b, 0xfc, 0x45, 0xe6, 0x5d, 0x78, 0x10, 0xe9, 0x30, 0x52, 0x0d, 0xd3, 0x5e, 0xd6, - 0x27, 0xed, 0x5e, 0xd6, 0x6c, 0x22, 0x27, 0x25, 0xc9, 0x30, 0x53, 0xc9, 0x79, 0x0e, 0x1b, 0xa4, 0xc2, 0xd8, 0xce, - 0x91, 0x97, 0xc2, 0x59, 0xd3, 0xe5, 0xb2, 0x0a, 0x03, 0x30, 0x71, 0x5a, 0xe3, 0x07, 0xae, 0x2a, 0xe0, 0xdc, 0xe0, - 0xe4, 0xbe, 0xbe, 0xde, 0x25, 0xd1, 0xbc, 0x22, 0x4e, 0x03, 0x81, 0x39, 0x77, 0xe6, 0xf3, 0x08, 0xbc, 0x14, 0xa5, - 0xf8, 0xa9, 0x52, 0x98, 0xec, 0x96, 0x8d, 0x06, 0x49, 0x99, 0xdf, 0x06, 0x79, 0x7c, 0x49, 0x01, 0xbd, 0x5c, 0x72, - 0x02, 0x3d, 0x56, 0xfd, 0x7f, 0xe0, 0x86, 0xa4, 0x4e, 0x5c, 0x96, 0x04, 0xf1, 0x3c, 0xa4, 0xb9, 0xe8, 0xa1, 0x12, - 0xe7, 0x70, 0x37, 0x44, 0x59, 0x4b, 0x34, 0x81, 0xde, 0x45, 0x36, 0x0f, 0x54, 0x84, 0x5b, 0x54, 0xca, 0xe7, 0xa6, - 0x78, 0xae, 0xda, 0xbe, 0xae, 0x92, 0x45, 0xa1, 0xa5, 0x3b, 0x4f, 0xd8, 0x2f, 0x73, 0x7a, 0xce, 0x42, 0xe3, 0xe4, - 0x2e, 0x4d, 0x82, 0x34, 0xa4, 0x1f, 0xde, 0xbd, 0x80, 0x6c, 0xf7, 0x34, 0x01, 0x12, 0x4b, 0xa4, 0xbf, 0x0b, 0xe7, - 0x24, 0x71, 0x43, 0x7a, 0xc5, 0x02, 0x3a, 0xb8, 0xd8, 0x5d, 0x6c, 0xac, 0x28, 0x5f, 0xa3, 0xa2, 0x75, 0x21, 0x92, - 0xfe, 0x04, 0x94, 0x17, 0xbb, 0x8b, 0x4b, 0x5e, 0xb4, 0x76, 0x17, 0x89, 0x1b, 0xa6, 0x53, 0x9f, 0x25, 0xf0, 0x3b, - 0x2f, 0x76, 0x17, 0x0c, 0x7e, 0xf0, 0xe2, 0xa2, 0xa8, 0x12, 0x45, 0x4b, 0x88, 0x8c, 0x29, 0x28, 0xdc, 0x75, 0x90, - 0xfb, 0x73, 0xca, 0x12, 0x51, 0x74, 0x57, 0xcf, 0x54, 0xf7, 0x0a, 0x48, 0xfe, 0x95, 0x48, 0x83, 0x59, 0x9b, 0xcb, - 0xe7, 0xf7, 0x35, 0x97, 0x69, 0xc2, 0x99, 0x48, 0x8b, 0xd7, 0xe1, 0x9c, 0xc8, 0xcf, 0xcf, 0x03, 0x79, 0x12, 0x35, - 0xaf, 0x4e, 0x5d, 0xf8, 0x02, 0xb1, 0xd2, 0x02, 0xa6, 0x99, 0x30, 0xf6, 0xe9, 0xf6, 0xa3, 0x92, 0xc9, 0x5d, 0xc6, - 0x5f, 0x49, 0x55, 0x79, 0x3a, 0xcf, 0x02, 0x88, 0xf5, 0x2a, 0x95, 0x62, 0xdd, 0x2b, 0x66, 0x0b, 0xfd, 0xcd, 0xc6, - 0xdc, 0x48, 0xb2, 0xe5, 0x70, 0xa6, 0xaf, 0xba, 0xb6, 0x83, 0x8a, 0x78, 0x22, 0xac, 0x19, 0x13, 0xab, 0x77, 0xce, - 0x42, 0x08, 0xbc, 0xb0, 0x50, 0x25, 0x2c, 0xd6, 0x26, 0x09, 0x2a, 0x52, 0x28, 0x32, 0x48, 0xe1, 0xb2, 0x9d, 0xb4, - 0x5a, 0x05, 0x42, 0x88, 0x8c, 0xeb, 0x81, 0xf0, 0x6d, 0x76, 0xf6, 0xf6, 0xf2, 0xea, 0x44, 0x1b, 0x53, 0x38, 0x5f, - 0x2e, 0x39, 0x75, 0x72, 0x79, 0xea, 0x26, 0x22, 0xa0, 0x8c, 0x31, 0x2c, 0xdf, 0x78, 0x29, 0x2e, 0x7b, 0xf2, 0xf2, - 0xa2, 0x17, 0x09, 0x24, 0x4a, 0x94, 0x11, 0x8d, 0xd4, 0x13, 0xad, 0x92, 0x61, 0xf3, 0x75, 0x79, 0x90, 0xbf, 0x86, - 0xf5, 0xf6, 0xca, 0xe2, 0x48, 0xab, 0x2a, 0x5a, 0x2d, 0xcd, 0xd3, 0x8c, 0x3b, 0x8e, 0x8f, 0x03, 0x44, 0xfa, 0xbe, - 0x98, 0xfd, 0xb1, 0xcc, 0xf7, 0x18, 0x34, 0x3b, 0x5e, 0xa7, 0xf4, 0x87, 0xd4, 0xce, 0x57, 0xcb, 0x6c, 0x33, 0x75, - 0x46, 0x17, 0xf0, 0x84, 0xcb, 0xdf, 0x0a, 0x7d, 0x55, 0x81, 0x9c, 0x5d, 0xf5, 0x5c, 0x4e, 0x12, 0x2b, 0x86, 0x26, - 0x95, 0x01, 0xa7, 0x06, 0xd5, 0x30, 0x1b, 0x61, 0xb6, 0x65, 0x6c, 0x54, 0x54, 0x88, 0x28, 0x37, 0xf7, 0x85, 0x54, - 0x82, 0xce, 0x0d, 0xea, 0xbe, 0x60, 0xda, 0x8d, 0x57, 0xa7, 0xbb, 0x42, 0xa1, 0xc8, 0xe0, 0x0c, 0x9b, 0xaa, 0x49, - 0x58, 0x6e, 0x49, 0xb2, 0x91, 0x78, 0x5d, 0xf9, 0x48, 0x25, 0x6d, 0x6c, 0xae, 0x22, 0x92, 0x21, 0x37, 0x01, 0x06, - 0x8e, 0x81, 0x9c, 0xeb, 0x29, 0x00, 0x8f, 0x19, 0x53, 0x38, 0xa9, 0xa4, 0x38, 0x0e, 0x5e, 0x48, 0xed, 0xde, 0xb3, - 0xdf, 0xbe, 0x39, 0x7b, 0x6f, 0x63, 0xb8, 0xea, 0x8c, 0x66, 0xb9, 0xb7, 0xb0, 0x55, 0x8e, 0x61, 0x13, 0xe2, 0xd5, - 0xb6, 0x67, 0xfb, 0x33, 0x38, 0xb4, 0x2d, 0x98, 0x6a, 0xeb, 0xa6, 0x79, 0x7d, 0x7d, 0xdd, 0x84, 0x13, 0x65, 0xcd, - 0x79, 0x16, 0x4b, 0x76, 0x13, 0xda, 0x45, 0x81, 0x5c, 0x1e, 0xd1, 0xa4, 0xbc, 0x0c, 0x29, 0x8d, 0xa9, 0x1b, 0xa7, - 0x13, 0x79, 0x1e, 0x76, 0xd5, 0x3d, 0x11, 0x5f, 0x1c, 0x8b, 0x4b, 0xbe, 0xfa, 0xc7, 0x5c, 0x5e, 0xaf, 0xc6, 0x33, - 0xf8, 0xd9, 0x87, 0xe0, 0xd5, 0x71, 0x8b, 0x47, 0xe2, 0xe1, 0x0c, 0x76, 0x93, 0x78, 0xda, 0x5d, 0xac, 0x51, 0xdd, - 0x00, 0xba, 0x88, 0xfa, 0x72, 0x6a, 0xb9, 0xa8, 0x75, 0xe1, 0xc5, 0x17, 0x17, 0xc5, 0x71, 0x0b, 0xfa, 0x6a, 0xe9, - 0x7e, 0x2f, 0xd3, 0xf0, 0x56, 0xb7, 0x2f, 0x29, 0x11, 0x2e, 0x7b, 0x4a, 0x48, 0x1f, 0xba, 0x80, 0x71, 0xc3, 0xbe, - 0xc0, 0x99, 0x62, 0xa1, 0xc3, 0xea, 0xa1, 0x18, 0x59, 0xc0, 0x30, 0x0b, 0x28, 0x01, 0x72, 0x83, 0xce, 0xc3, 0xb2, - 0x81, 0xd8, 0xed, 0xb2, 0x68, 0x1b, 0x80, 0xb2, 0x62, 0xb5, 0x7f, 0xa4, 0x9b, 0xbb, 0x22, 0x0b, 0x0d, 0x71, 0x68, - 0x02, 0x7f, 0x81, 0xe0, 0x5f, 0x01, 0xf8, 0x71, 0x4b, 0xa2, 0xe9, 0xc2, 0xbc, 0x76, 0x46, 0x5e, 0x08, 0x51, 0x22, - 0x73, 0x98, 0x71, 0xfc, 0x9e, 0xe3, 0x8f, 0x17, 0xa2, 0xaa, 0xd6, 0x12, 0x40, 0x7d, 0x05, 0x6d, 0xaa, 0xad, 0xd5, - 0xc1, 0x20, 0x8d, 0x63, 0x7f, 0x96, 0x53, 0x4f, 0xff, 0x50, 0x0a, 0x03, 0xe8, 0x1d, 0xeb, 0x1a, 0x9a, 0xca, 0x7b, - 0x3a, 0x05, 0x3d, 0x6e, 0x5d, 0x7d, 0xbc, 0xf2, 0x33, 0xa7, 0xd9, 0x0c, 0x9a, 0x97, 0x13, 0x54, 0xf0, 0x68, 0x61, - 0xaa, 0x1b, 0x0f, 0xdb, 0xed, 0x1e, 0x24, 0xa9, 0x36, 0xfd, 0x98, 0x4d, 0x12, 0x2f, 0xa6, 0x63, 0x5e, 0x70, 0x38, - 0x3d, 0xb8, 0xd0, 0xfa, 0x9d, 0xdb, 0x3d, 0xcc, 0xe8, 0xd4, 0x72, 0xe1, 0xef, 0xdd, 0x03, 0x17, 0x3c, 0xf4, 0x12, - 0x1e, 0x35, 0x45, 0x32, 0x34, 0x1c, 0xe5, 0xe0, 0x51, 0xed, 0x79, 0x61, 0x0c, 0x14, 0x50, 0xd0, 0x7d, 0x0b, 0x9e, - 0x59, 0x3c, 0xc2, 0x3c, 0x33, 0xeb, 0x25, 0x68, 0xb1, 0x36, 0x83, 0x75, 0x15, 0x6c, 0x1f, 0x15, 0xb9, 0xb0, 0x58, - 0x16, 0x6b, 0x78, 0x31, 0x54, 0xe9, 0x82, 0x25, 0xb3, 0x39, 0x1f, 0x0a, 0xcf, 0x7f, 0x06, 0x67, 0x48, 0x46, 0xd8, - 0x28, 0x01, 0x78, 0x46, 0xaa, 0x7d, 0xe0, 0xc7, 0x81, 0x03, 0x9d, 0x58, 0x4d, 0xeb, 0x28, 0xa3, 0x53, 0xd4, 0x9b, - 0xb2, 0xa4, 0x29, 0xdf, 0x1d, 0x1a, 0xba, 0x9b, 0xfb, 0x08, 0x9e, 0x0a, 0x57, 0xf4, 0x86, 0x45, 0x82, 0xef, 0x86, - 0x79, 0x5d, 0x8c, 0x8a, 0xa2, 0x97, 0x72, 0x67, 0xf8, 0xc2, 0x41, 0x23, 0xfc, 0xab, 0x71, 0x89, 0x8d, 0xad, 0xa9, - 0xda, 0xc6, 0x5d, 0xb4, 0xa5, 0x8a, 0x49, 0x97, 0xa2, 0xda, 0xaf, 0x04, 0x2a, 0xbe, 0x74, 0x6c, 0x9a, 0xcf, 0x9a, - 0x92, 0xfd, 0x34, 0x05, 0xf9, 0xd8, 0xd0, 0x14, 0x29, 0x77, 0x36, 0xa5, 0x0b, 0xc1, 0x59, 0xd4, 0x39, 0x16, 0xe9, - 0x71, 0x19, 0x95, 0xe7, 0x9e, 0xd4, 0xb3, 0x79, 0xd2, 0x09, 0xd5, 0xb6, 0xfe, 0xc5, 0x49, 0x9d, 0x4d, 0x81, 0xfc, - 0x2f, 0xef, 0xfa, 0xf3, 0xe3, 0x18, 0x06, 0xbc, 0xd0, 0x4a, 0x83, 0x79, 0x35, 0xca, 0x90, 0x8f, 0x1c, 0x54, 0xa8, - 0x3d, 0xf3, 0x44, 0xe8, 0xdd, 0xc6, 0x05, 0x83, 0x3b, 0x5c, 0x47, 0xd4, 0xe4, 0x09, 0x66, 0x06, 0x39, 0x01, 0xb5, - 0xdc, 0xf1, 0x5e, 0xc5, 0x66, 0xa4, 0xd6, 0x6e, 0x89, 0x09, 0x11, 0x3b, 0x4b, 0x42, 0xdb, 0xfa, 0x73, 0x10, 0xb3, - 0xe0, 0x23, 0xb1, 0x77, 0x17, 0x0e, 0x5a, 0x3f, 0x1a, 0x2a, 0x76, 0xa8, 0xe6, 0xb9, 0xa8, 0x1e, 0x6d, 0xc8, 0x5c, - 0x83, 0x9d, 0xca, 0xdb, 0x83, 0xec, 0x3e, 0xa8, 0x36, 0xc7, 0x2d, 0x39, 0x4e, 0xff, 0xa2, 0x38, 0xaf, 0x6e, 0x05, - 0xab, 0xa0, 0x00, 0x34, 0xcb, 0x72, 0x4b, 0xd0, 0x1f, 0xb1, 0xe5, 0x16, 0xaa, 0x59, 0x80, 0xd8, 0xa4, 0x7d, 0x64, - 0x5b, 0x92, 0xc1, 0x00, 0x9c, 0x5c, 0xf1, 0x1a, 0xdb, 0xfa, 0x73, 0x59, 0x46, 0x4b, 0xb7, 0x8f, 0xc8, 0x5b, 0x21, - 0x36, 0x8c, 0x05, 0xb6, 0xbe, 0x1b, 0x52, 0xee, 0xb3, 0x58, 0x36, 0xe9, 0x69, 0x2f, 0xc5, 0xca, 0x8c, 0x96, 0xcb, - 0xbc, 0x3e, 0x17, 0x56, 0xc7, 0xa0, 0x98, 0xd9, 0x71, 0xab, 0x82, 0x5b, 0xcc, 0x4c, 0xec, 0x0f, 0x33, 0x7e, 0x5a, - 0xcd, 0x50, 0xbe, 0xb3, 0xfe, 0x1c, 0x88, 0x93, 0x55, 0x00, 0x60, 0xaa, 0x00, 0x84, 0xc8, 0xbe, 0x54, 0x42, 0x1c, - 0x9f, 0xa4, 0x2e, 0xf7, 0xb3, 0x09, 0xe5, 0x2b, 0x88, 0xf5, 0x65, 0x22, 0x6f, 0x4f, 0x47, 0xf1, 0xd7, 0xa0, 0x0d, - 0xea, 0xd0, 0x82, 0x9e, 0x5b, 0x0c, 0x40, 0x55, 0x25, 0x1b, 0x35, 0xde, 0x08, 0x81, 0xec, 0x13, 0x8b, 0x23, 0xb9, - 0x7d, 0x2a, 0xb8, 0xbd, 0x8c, 0xc3, 0x59, 0x62, 0x2c, 0x01, 0x62, 0x61, 0x5b, 0x03, 0x09, 0x39, 0x0d, 0x25, 0xcc, - 0x24, 0x13, 0xad, 0xd2, 0xe2, 0xb8, 0x25, 0x6b, 0x4b, 0x76, 0x2c, 0x2b, 0x01, 0x12, 0xc4, 0x3e, 0xad, 0x70, 0x00, - 0xc9, 0xdf, 0x26, 0x1e, 0x42, 0x76, 0x55, 0x12, 0x9b, 0x38, 0x63, 0xd6, 0x3f, 0x8e, 0xfd, 0x4b, 0x1a, 0xf7, 0x77, - 0x17, 0xd9, 0x72, 0xd9, 0x2e, 0x8e, 0x5b, 0xf2, 0xd1, 0x3a, 0x16, 0x7c, 0x43, 0xde, 0x0d, 0x2a, 0x96, 0x18, 0x0e, - 0x6e, 0x42, 0x4a, 0xac, 0xce, 0x05, 0xf3, 0x54, 0x07, 0x85, 0x6d, 0x89, 0x2c, 0x14, 0x51, 0xa9, 0xd4, 0x69, 0x0a, - 0xdb, 0x62, 0xe1, 0x7a, 0x59, 0xce, 0xe9, 0x0c, 0x4a, 0xa3, 0xe5, 0xb2, 0x53, 0xd8, 0xd6, 0x94, 0x25, 0xf0, 0x94, - 0x2d, 0x97, 0xe2, 0x4c, 0xe4, 0x94, 0x25, 0x4e, 0x1b, 0xc8, 0xd6, 0xb6, 0xa6, 0xfe, 0x8d, 0x98, 0xb0, 0x7e, 0xe3, - 0xdf, 0x38, 0x1d, 0xf5, 0xca, 0x2d, 0xf1, 0x93, 0x03, 0xc5, 0x55, 0x2b, 0xea, 0xab, 0x15, 0x0d, 0xf1, 0x5c, 0x9e, - 0xf6, 0x22, 0x4e, 0x48, 0xfc, 0xcd, 0x2b, 0x1a, 0xea, 0x15, 0x9d, 0x6f, 0x59, 0xd1, 0xf9, 0x1d, 0x2b, 0x1a, 0xa8, - 0xd5, 0xb3, 0x4a, 0xdc, 0xa5, 0xcb, 0x65, 0xa7, 0x5d, 0x61, 0xef, 0xb8, 0x15, 0xb2, 0x2b, 0x58, 0x0d, 0xd0, 0xd4, - 0x38, 0x9b, 0xd2, 0xcd, 0x44, 0x59, 0x47, 0x31, 0xfd, 0x2c, 0x4c, 0x56, 0x58, 0xc8, 0xea, 0x58, 0x30, 0xe9, 0xba, - 0x0c, 0x4c, 0xfe, 0x91, 0x94, 0xcd, 0x00, 0x0f, 0x39, 0xe0, 0x21, 0xd2, 0x77, 0x85, 0x3a, 0xf6, 0x7b, 0x1b, 0xdb, - 0x96, 0xad, 0xc9, 0xfa, 0xa2, 0x38, 0x07, 0x19, 0x21, 0xe6, 0x77, 0x2f, 0x5a, 0x84, 0xda, 0x76, 0x7f, 0x3b, 0xcd, - 0x41, 0x0e, 0xc1, 0x75, 0x9a, 0x85, 0xb6, 0x27, 0xab, 0x7e, 0x16, 0xaa, 0xa6, 0x2c, 0x51, 0x19, 0x69, 0x5b, 0x69, - 0xad, 0x7a, 0x6f, 0x52, 0x5c, 0xf7, 0xf0, 0x50, 0xd6, 0x98, 0xf9, 0x9c, 0xd3, 0x2c, 0x51, 0x94, 0x6b, 0xdb, 0xff, - 0x21, 0xa8, 0x70, 0x03, 0x5f, 0x09, 0xf4, 0x02, 0x68, 0x02, 0x54, 0x3a, 0xb7, 0xe2, 0xf9, 0x52, 0x3c, 0xed, 0x54, - 0xca, 0xe6, 0x2d, 0x32, 0xf5, 0x7e, 0x59, 0x04, 0x66, 0xc8, 0x7c, 0x4a, 0xc3, 0x73, 0xc1, 0xa0, 0x07, 0xf1, 0x85, - 0x52, 0x1e, 0x57, 0xc4, 0x5d, 0xd5, 0x00, 0xdb, 0x3f, 0xcd, 0xbb, 0x8f, 0x0e, 0x4e, 0x6d, 0x2c, 0x79, 0x7c, 0x3a, - 0x1e, 0xdb, 0xa8, 0xb0, 0xee, 0xd7, 0xac, 0x73, 0xf0, 0xd3, 0xfc, 0xeb, 0x67, 0xed, 0xaf, 0xcb, 0xc6, 0x09, 0x10, - 0x91, 0x4a, 0x82, 0xd0, 0xa2, 0xca, 0x80, 0x57, 0xcf, 0x68, 0xec, 0x27, 0xdb, 0xa7, 0x33, 0x34, 0xa7, 0x93, 0xcf, - 0x28, 0x0d, 0x81, 0x38, 0xf1, 0x5a, 0xe9, 0x79, 0x4c, 0xaf, 0xa8, 0xbe, 0xa1, 0x71, 0xc3, 0x60, 0x1b, 0x5a, 0x04, - 0xe9, 0x3c, 0xe1, 0x2a, 0x1b, 0x44, 0xb1, 0x5a, 0x63, 0x4a, 0x17, 0x62, 0x0e, 0xa6, 0x3a, 0x7f, 0x2b, 0xe5, 0x5c, - 0x5d, 0x7a, 0x15, 0x17, 0xd8, 0x36, 0x00, 0xd8, 0x0a, 0xd9, 0x60, 0x4b, 0xb9, 0xd7, 0xc6, 0xed, 0x6d, 0xb0, 0xe1, - 0x0e, 0xf2, 0x6c, 0x7b, 0xa4, 0xf1, 0x24, 0x1c, 0xba, 0xb5, 0x4b, 0x35, 0xb6, 0xe2, 0xeb, 0x93, 0x18, 0xb8, 0xcc, - 0xa0, 0xb3, 0x84, 0xe6, 0xf9, 0x56, 0x04, 0x94, 0x8b, 0x88, 0xed, 0xaa, 0xb6, 0xbd, 0xa5, 0x17, 0xdc, 0xc6, 0xb0, - 0xc3, 0x04, 0xc0, 0x65, 0x58, 0x59, 0xd5, 0xa2, 0xe3, 0x31, 0x0d, 0x4a, 0x7f, 0x38, 0x04, 0x08, 0xc7, 0x2c, 0xe6, - 0x10, 0x27, 0x13, 0x01, 0x2c, 0xfb, 0x75, 0x9a, 0x50, 0x1b, 0xe9, 0x94, 0x57, 0x05, 0xbf, 0x92, 0xff, 0x9b, 0xe1, - 0x91, 0x3d, 0xd6, 0x61, 0x51, 0xa3, 0x2c, 0x97, 0xda, 0x5d, 0x53, 0x2b, 0xaf, 0x23, 0x32, 0x15, 0xfe, 0x98, 0x6d, - 0x1b, 0xe8, 0x7e, 0xdb, 0x64, 0xd1, 0xf9, 0xfa, 0xb0, 0xd3, 0x2e, 0x6c, 0x6c, 0x43, 0x77, 0xf7, 0xdd, 0x25, 0xa2, - 0xd5, 0x3e, 0xb4, 0x9a, 0x27, 0x9f, 0xd3, 0xae, 0xdb, 0x79, 0xdc, 0xb1, 0xb1, 0xbc, 0x6b, 0x01, 0x15, 0x25, 0x33, - 0x08, 0xc0, 0x43, 0xfc, 0xbb, 0xa7, 0x52, 0xef, 0xfc, 0x7e, 0xf0, 0x3c, 0xec, 0xb4, 0x6d, 0x6c, 0xe7, 0x3c, 0x9d, - 0x7d, 0xc6, 0x14, 0xf6, 0x6d, 0x6c, 0x07, 0x71, 0x9a, 0x53, 0x73, 0x0e, 0x52, 0x9d, 0xfd, 0xfd, 0x93, 0x90, 0x10, - 0xcd, 0x32, 0x9a, 0xe7, 0x96, 0xd9, 0xbf, 0x22, 0xa5, 0x4f, 0x30, 0xcc, 0x8d, 0x14, 0x97, 0x53, 0x2e, 0xf0, 0x22, - 0xaf, 0x41, 0x30, 0xa9, 0x4a, 0x96, 0xad, 0x11, 0x9b, 0x10, 0x01, 0x25, 0x63, 0x93, 0xda, 0xd5, 0x27, 0x47, 0xde, - 0xb0, 0xf5, 0xe4, 0xc0, 0x32, 0x70, 0xbe, 0x3e, 0x40, 0xad, 0x64, 0xca, 0x92, 0xf3, 0x0d, 0xa5, 0xfe, 0xcd, 0x86, - 0x52, 0x50, 0xd9, 0x4a, 0xe8, 0xd4, 0x15, 0x3d, 0x9f, 0xc6, 0x7a, 0xa5, 0xf8, 0x98, 0x20, 0x86, 0xc2, 0xff, 0xf8, - 0x09, 0x48, 0x8d, 0x65, 0x10, 0x3d, 0xfc, 0xf6, 0xe1, 0xa0, 0xe4, 0x73, 0x86, 0x2b, 0x7b, 0xf9, 0x7d, 0x33, 0x84, - 0xd2, 0x26, 0x38, 0xf9, 0xe3, 0xcf, 0x9a, 0x2b, 0xbd, 0xf9, 0x34, 0xc1, 0x19, 0x5a, 0xd5, 0xef, 0x58, 0x7a, 0x75, - 0xd4, 0x7f, 0x75, 0xed, 0x37, 0x14, 0x2b, 0xc5, 0xa7, 0x5c, 0xff, 0x20, 0x66, 0xd3, 0x8a, 0x04, 0xd6, 0xc1, 0x14, - 0x1a, 0x0f, 0x64, 0x7c, 0x99, 0x9d, 0x48, 0xd5, 0xe7, 0x1c, 0xce, 0xb1, 0xc2, 0x55, 0x21, 0xf3, 0x8c, 0x9e, 0xc7, - 0xe9, 0xf5, 0xea, 0xe5, 0x67, 0xdb, 0x2b, 0x47, 0x6c, 0x12, 0x19, 0x87, 0xd3, 0x28, 0x29, 0x17, 0xe1, 0xce, 0x01, - 0x8a, 0x7f, 0xf9, 0x67, 0xd7, 0xfd, 0x97, 0x7f, 0xfe, 0x64, 0x55, 0xe8, 0xbe, 0xb8, 0xc0, 0xbc, 0xea, 0x76, 0xfb, - 0xee, 0xda, 0x3c, 0x52, 0x1d, 0xe7, 0x9b, 0xeb, 0xac, 0x2d, 0x02, 0xbc, 0x5f, 0x5b, 0x82, 0xb5, 0x42, 0xb9, 0xfb, - 0xac, 0xdf, 0x02, 0x18, 0xcc, 0xeb, 0x93, 0x90, 0x41, 0xa5, 0xdf, 0x05, 0xda, 0x05, 0xf2, 0xee, 0xb5, 0x22, 0xbf, - 0x1d, 0xc3, 0x9f, 0x9a, 0xc3, 0xef, 0x04, 0x5f, 0xf9, 0x27, 0xe2, 0x8b, 0x8b, 0x32, 0x0b, 0xd1, 0x6c, 0x0a, 0x77, - 0x1c, 0x0c, 0xd6, 0x4a, 0x94, 0xe2, 0xe1, 0xb5, 0x51, 0x5f, 0x9c, 0xa1, 0x24, 0xf1, 0xc5, 0x2b, 0xb8, 0xd8, 0xe8, - 0xf8, 0x32, 0xd3, 0xce, 0xd6, 0x3b, 0x84, 0x03, 0x74, 0x51, 0x9f, 0x95, 0xe8, 0x74, 0x4d, 0x32, 0x40, 0x29, 0x98, - 0x1b, 0x00, 0x26, 0x8e, 0x2f, 0x94, 0xb5, 0x79, 0x2a, 0xdd, 0x30, 0xde, 0x2a, 0x69, 0x2b, 0xf7, 0x4c, 0x0d, 0xe9, - 0xd8, 0x7a, 0x2f, 0xf0, 0x25, 0x2a, 0xd3, 0xca, 0xba, 0x17, 0xae, 0x2e, 0xb0, 0x23, 0x4a, 0xf6, 0x73, 0xe5, 0xc7, - 0x57, 0xf7, 0x63, 0x7c, 0xdb, 0x05, 0xea, 0xd2, 0x5a, 0xfe, 0xa3, 0x55, 0x82, 0x65, 0x73, 0xb9, 0x49, 0x1f, 0xb8, - 0xf6, 0x39, 0xcd, 0xce, 0x23, 0x48, 0x84, 0xca, 0x3e, 0xc1, 0x9c, 0x60, 0xa5, 0x31, 0x15, 0x7f, 0x19, 0x51, 0x17, - 0x49, 0xff, 0x83, 0x38, 0x15, 0x83, 0x2c, 0x46, 0x18, 0xca, 0x58, 0x84, 0xff, 0xcf, 0xb7, 0xfe, 0xc3, 0xf0, 0xad, - 0xbb, 0x87, 0xa8, 0x9d, 0x91, 0xfe, 0xec, 0x85, 0xfc, 0x8f, 0xcd, 0xee, 0x72, 0xc1, 0xee, 0x7e, 0x03, 0xa3, 0xcb, - 0xff, 0x31, 0x8c, 0x4e, 0xd8, 0xc8, 0x9a, 0xd3, 0xad, 0x85, 0x9a, 0x6f, 0x5d, 0xff, 0xda, 0xbf, 0xad, 0xf6, 0x55, - 0x7c, 0x71, 0x72, 0xed, 0xdf, 0x56, 0x8b, 0xb0, 0x9d, 0x5d, 0xac, 0xf6, 0x31, 0xb0, 0xdf, 0xbc, 0xb6, 0x3d, 0xfb, - 0xcd, 0xd7, 0x5f, 0xdb, 0xf8, 0x22, 0xa7, 0x7c, 0x00, 0x85, 0x64, 0x77, 0xb1, 0xb3, 0x5a, 0x11, 0xdc, 0x28, 0x30, - 0x45, 0x11, 0xf6, 0x82, 0xa4, 0x43, 0xe3, 0x3d, 0xcb, 0xcf, 0xd3, 0xc4, 0x84, 0xe6, 0x2d, 0x58, 0xf6, 0x9f, 0x0b, - 0x8e, 0xe8, 0x65, 0x0d, 0x1e, 0x51, 0xba, 0x0a, 0x90, 0x28, 0xac, 0x41, 0x54, 0x5d, 0x19, 0x74, 0x37, 0xff, 0xaf, - 0xae, 0x45, 0x90, 0xb7, 0x7d, 0x44, 0x83, 0xf8, 0xe2, 0x73, 0xc4, 0x87, 0x1c, 0xac, 0xf2, 0xd8, 0x69, 0x77, 0xa7, - 0x5f, 0xec, 0x2e, 0xa2, 0xbd, 0x3d, 0x36, 0xb0, 0xb1, 0xb8, 0xa7, 0xa9, 0xd8, 0x24, 0x5c, 0x72, 0xf8, 0x93, 0xc1, - 0x9f, 0xb4, 0x62, 0xd4, 0x2c, 0x19, 0x67, 0x7e, 0x46, 0xc3, 0xed, 0x4c, 0xba, 0xbc, 0xdf, 0x48, 0x91, 0x86, 0x4c, - 0xc0, 0xce, 0xcf, 0x45, 0xea, 0xd1, 0x94, 0x81, 0x3e, 0xba, 0x63, 0x7e, 0xc5, 0x47, 0x5d, 0x88, 0x56, 0x7e, 0x04, - 0xc0, 0x44, 0x38, 0x25, 0x79, 0x99, 0xeb, 0x00, 0xb7, 0x6a, 0xaa, 0xec, 0x10, 0x6c, 0x23, 0xe1, 0x75, 0x0f, 0x49, - 0x5f, 0xa4, 0x3d, 0xbc, 0x48, 0xb8, 0x13, 0xba, 0x3c, 0x63, 0x53, 0x07, 0xe1, 0x4e, 0x1b, 0x21, 0xed, 0x6c, 0x08, - 0x49, 0x7f, 0x87, 0xe5, 0xaf, 0xfd, 0xd7, 0x4e, 0x28, 0x2e, 0xe2, 0x12, 0x9f, 0xee, 0x81, 0x43, 0x92, 0x4f, 0xe6, - 0xe3, 0x31, 0xcd, 0x1c, 0x7d, 0x00, 0xf0, 0xab, 0x03, 0x38, 0x63, 0x0c, 0x6f, 0x9f, 0xfa, 0xdc, 0xff, 0x96, 0xd1, - 0x6b, 0x27, 0x45, 0xbd, 0xac, 0xba, 0x9c, 0x31, 0xc4, 0x73, 0x44, 0xfa, 0x11, 0x24, 0xc6, 0xbf, 0x48, 0xf8, 0x7e, - 0xd7, 0x99, 0x7f, 0x75, 0x80, 0x43, 0xb8, 0xf2, 0x42, 0x67, 0x75, 0xcb, 0xbb, 0x4a, 0x3e, 0xb0, 0x84, 0x1f, 0xc9, - 0x63, 0x98, 0x29, 0x52, 0xee, 0xc3, 0x32, 0x23, 0xc6, 0xf2, 0xcb, 0x0e, 0x43, 0xd2, 0x0f, 0x1a, 0x44, 0x1e, 0xca, - 0x14, 0xb7, 0xec, 0x9e, 0x46, 0x7e, 0x76, 0x0a, 0x07, 0xbe, 0x01, 0xd0, 0x4b, 0x9e, 0xfa, 0x4e, 0x50, 0x7e, 0xc9, - 0xc9, 0x69, 0xfd, 0xd4, 0x68, 0x4d, 0xb0, 0x48, 0x8a, 0xa9, 0x8a, 0x5a, 0x50, 0x74, 0x6e, 0x16, 0x91, 0xc6, 0x6e, - 0x0b, 0xc3, 0x1e, 0xec, 0x6d, 0xf4, 0xd1, 0xea, 0xa5, 0x6b, 0x5e, 0x67, 0xfe, 0xac, 0x8c, 0x1b, 0x9c, 0xfa, 0x59, - 0xc6, 0x68, 0x66, 0x39, 0xcf, 0x7f, 0x45, 0xde, 0xbf, 0xfc, 0xf3, 0xe6, 0xf8, 0x81, 0x0a, 0x19, 0x58, 0x90, 0x5c, - 0xd2, 0x14, 0xe9, 0xd8, 0xc4, 0x0e, 0x64, 0x43, 0x5b, 0x87, 0x3b, 0xf6, 0x8f, 0xda, 0xed, 0xb6, 0x0a, 0x09, 0x74, - 0xe4, 0x4f, 0x88, 0x01, 0xc0, 0x4f, 0x78, 0x10, 0x51, 0x65, 0x62, 0xcb, 0x00, 0xe5, 0x51, 0x7b, 0x76, 0x63, 0xf7, - 0x61, 0x3b, 0x28, 0x28, 0xde, 0xd1, 0x19, 0xf5, 0xf9, 0x67, 0x8d, 0x9f, 0x89, 0x26, 0xe5, 0xf0, 0x1d, 0x3d, 0x74, - 0x35, 0xee, 0xca, 0xa0, 0x87, 0xab, 0x83, 0xbe, 0x67, 0x53, 0x71, 0x75, 0xd3, 0xb6, 0x51, 0x85, 0xa7, 0xba, 0x36, - 0x26, 0x97, 0x2d, 0x6c, 0x4b, 0x60, 0x3c, 0x4a, 0xe3, 0x90, 0x66, 0xc4, 0xa6, 0xee, 0xc4, 0xb5, 0x1e, 0xb7, 0xdb, - 0x6d, 0xdc, 0x3c, 0x38, 0x6c, 0xb7, 0xf1, 0xe1, 0xc3, 0x36, 0x6e, 0xc2, 0x1f, 0xd7, 0x75, 0x57, 0x60, 0xb8, 0x2b, - 0x6a, 0xdb, 0x69, 0x67, 0x74, 0xaa, 0x00, 0xbc, 0x33, 0xac, 0x58, 0xed, 0x09, 0xb8, 0x60, 0x5a, 0xed, 0x7b, 0x29, - 0xd9, 0xd4, 0x05, 0x07, 0x2a, 0x1d, 0x55, 0xf8, 0x0b, 0xd3, 0x2a, 0x68, 0x4a, 0xe5, 0xc5, 0x7f, 0x2f, 0x14, 0x21, - 0x78, 0xd6, 0x29, 0xdc, 0x5e, 0x2a, 0xe2, 0xa5, 0x90, 0x0a, 0x04, 0x1f, 0x48, 0xe3, 0x3e, 0x4b, 0xe0, 0xdb, 0x59, - 0x3a, 0x6a, 0xaa, 0x19, 0x55, 0xba, 0x92, 0x74, 0xfb, 0x40, 0x86, 0xa5, 0x37, 0x11, 0xc4, 0xe8, 0x01, 0xc2, 0xfe, - 0x7d, 0x1a, 0xa8, 0x15, 0x84, 0xfa, 0xc1, 0x7d, 0xea, 0x6b, 0xec, 0x8f, 0x1e, 0x88, 0xe4, 0xa4, 0x9d, 0x68, 0xb9, - 0xdc, 0xf1, 0x97, 0xcb, 0x9d, 0xe0, 0xfe, 0x33, 0x94, 0xcb, 0xab, 0x4f, 0x41, 0xc0, 0xcd, 0x9f, 0x12, 0xe8, 0x17, - 0x50, 0xee, 0x45, 0x58, 0x82, 0x24, 0x9f, 0x7c, 0xac, 0x06, 0x94, 0x8f, 0x41, 0xb1, 0x82, 0x94, 0x90, 0x44, 0xd2, - 0x3e, 0x5f, 0x2e, 0x15, 0xf1, 0xe3, 0x39, 0xf1, 0xcb, 0xa2, 0x8e, 0x8d, 0x67, 0x24, 0x28, 0x1f, 0x6d, 0x01, 0xf2, - 0x4c, 0x71, 0xa9, 0x0a, 0xe2, 0x6b, 0x3f, 0x4b, 0x4c, 0x80, 0x5f, 0xa7, 0x96, 0x1a, 0xd6, 0x9a, 0x65, 0xe9, 0x15, - 0x83, 0xe4, 0x97, 0x95, 0x81, 0xa7, 0x04, 0x2e, 0xfe, 0xea, 0x99, 0xa1, 0x70, 0xa3, 0x83, 0xf7, 0x9a, 0xcf, 0xc2, - 0x2d, 0x93, 0xe5, 0x04, 0xbd, 0x50, 0xcd, 0xcd, 0x9b, 0xeb, 0x69, 0xbd, 0xf3, 0xaf, 0xbd, 0x99, 0x7e, 0x78, 0x26, - 0xf3, 0x6c, 0xbc, 0x69, 0x79, 0xb2, 0xe6, 0x2d, 0x79, 0x0d, 0xb1, 0x1f, 0x5b, 0xf3, 0x6d, 0xb8, 0x67, 0x53, 0xf2, - 0xb8, 0x77, 0x2f, 0xcf, 0xa8, 0x9f, 0x05, 0xd1, 0x5b, 0x3f, 0xf3, 0xa7, 0x79, 0x6f, 0xac, 0x6f, 0xf1, 0xd2, 0x14, - 0x70, 0x3e, 0x16, 0x99, 0x4e, 0x49, 0x70, 0x6b, 0xe3, 0x10, 0xe1, 0xea, 0xbd, 0x84, 0x40, 0xfa, 0xb9, 0x6d, 0x3c, - 0x37, 0x5f, 0xc1, 0x3a, 0xdb, 0x78, 0x8a, 0xb0, 0x4c, 0x20, 0x7a, 0xfb, 0x47, 0xa6, 0x0e, 0x61, 0xc8, 0x75, 0xf1, - 0xc6, 0x6e, 0xf5, 0x95, 0x3b, 0x9d, 0x4c, 0xf4, 0x7e, 0x25, 0x99, 0x68, 0x03, 0x1a, 0xad, 0x8c, 0xe6, 0xb3, 0x34, - 0xc9, 0xa9, 0x8d, 0xdf, 0x43, 0x3b, 0x79, 0x15, 0xb3, 0xd9, 0x70, 0x8d, 0xe6, 0xca, 0xa6, 0xe2, 0x8d, 0x6c, 0x07, - 0x41, 0x9d, 0xf7, 0xdf, 0x97, 0x71, 0x7c, 0x1d, 0xdf, 0x11, 0x89, 0xe8, 0x8c, 0x6e, 0xc9, 0x95, 0xcd, 0xe9, 0x27, - 0x73, 0x65, 0xe3, 0x7b, 0xe5, 0xca, 0xe6, 0xf4, 0x8f, 0xce, 0x95, 0x65, 0xd4, 0xc8, 0x95, 0x05, 0x39, 0xf7, 0xf5, - 0xbd, 0x52, 0x2e, 0x75, 0x26, 0x5c, 0x7a, 0x9d, 0x93, 0x8e, 0x8a, 0x81, 0xc4, 0xe9, 0x04, 0xf2, 0x2d, 0xff, 0xf1, - 0xe9, 0x93, 0x71, 0x3a, 0x31, 0x93, 0x27, 0xe1, 0xc3, 0x24, 0x40, 0x76, 0x38, 0x23, 0x0b, 0xfb, 0xa7, 0x9b, 0xce, - 0x93, 0x61, 0xa7, 0xb7, 0xdf, 0x99, 0xda, 0x9e, 0x0d, 0x4e, 0x47, 0x51, 0xd0, 0xee, 0xed, 0xef, 0x43, 0xc1, 0xb5, - 0x51, 0xd0, 0x85, 0x02, 0x66, 0x14, 0x1c, 0x42, 0x41, 0x60, 0x14, 0x3c, 0x84, 0x82, 0xd0, 0x28, 0x78, 0x04, 0x05, - 0x57, 0x76, 0x31, 0x64, 0x65, 0x42, 0xf0, 0x23, 0x24, 0x6e, 0x30, 0xdc, 0xc9, 0xea, 0xa7, 0xb7, 0x23, 0xa2, 0xab, - 0x3c, 0x2a, 0x6f, 0x7e, 0x68, 0x1e, 0xe8, 0x8b, 0x0a, 0x2f, 0xbe, 0xb8, 0x00, 0xd6, 0x0a, 0x17, 0xb1, 0x60, 0x88, - 0x49, 0xca, 0x9a, 0xfb, 0xfa, 0xb5, 0xed, 0x95, 0x59, 0xb3, 0x6d, 0xdc, 0xd5, 0x79, 0xb3, 0x9e, 0x8d, 0x04, 0x5f, - 0x92, 0x2f, 0x0e, 0x1b, 0xa1, 0xea, 0x16, 0xee, 0x00, 0xac, 0x2e, 0xe0, 0xdc, 0x47, 0x78, 0xaa, 0x15, 0x20, 0xea, - 0xc0, 0x07, 0x18, 0xde, 0xb3, 0x29, 0xd5, 0xfb, 0x45, 0x0f, 0x60, 0x89, 0xcc, 0xe2, 0x5e, 0x54, 0x29, 0x46, 0x6f, - 0xf1, 0xb8, 0xba, 0xf3, 0xf5, 0x3d, 0x91, 0x77, 0xe8, 0x65, 0x58, 0x86, 0xb9, 0x66, 0x98, 0xfb, 0x13, 0x0f, 0x52, - 0x28, 0x21, 0x63, 0xc4, 0x1b, 0x13, 0x42, 0xda, 0x83, 0xb9, 0xf7, 0x16, 0x5f, 0x47, 0x34, 0xf1, 0xa6, 0x45, 0xaf, - 0x5c, 0x7f, 0x99, 0xd2, 0xf9, 0xbe, 0xbc, 0x28, 0x5c, 0xd0, 0x44, 0xf5, 0x56, 0x42, 0xd9, 0x2c, 0x69, 0x67, 0x4b, - 0xce, 0x9f, 0xa1, 0xec, 0x8c, 0xe3, 0xf4, 0xba, 0x09, 0xe2, 0x7e, 0x63, 0x1e, 0x20, 0xcc, 0xad, 0xcc, 0x03, 0x7c, - 0x09, 0xb0, 0x96, 0x4f, 0xef, 0xfd, 0x49, 0xf9, 0xfb, 0x15, 0xcd, 0x73, 0x7f, 0xa2, 0x6a, 0x6e, 0xcf, 0xfb, 0x13, - 0x20, 0x9a, 0x39, 0x7f, 0x1a, 0x08, 0x48, 0xce, 0x03, 0x84, 0x40, 0x40, 0x57, 0xe5, 0xea, 0xc1, 0xcc, 0xeb, 0x69, - 0x7e, 0x02, 0x55, 0xf5, 0x22, 0xee, 0x4f, 0xaa, 0x82, 0xe3, 0x59, 0x46, 0x55, 0x02, 0x21, 0x60, 0xb1, 0x38, 0x6e, - 0x41, 0x81, 0x7c, 0xbd, 0x25, 0x9d, 0x4f, 0x73, 0x97, 0xed, 0x49, 0x7d, 0x96, 0x4e, 0xe7, 0x33, 0x4f, 0xa6, 0x94, - 0xc7, 0x52, 0xd6, 0x33, 0xf2, 0xbe, 0xec, 0x04, 0xf0, 0x9f, 0x3a, 0x78, 0xf1, 0xe5, 0x78, 0x3c, 0xbe, 0x33, 0xbd, - 0xef, 0xcb, 0x70, 0x4c, 0xbb, 0xf4, 0xb0, 0x07, 0xa7, 0x16, 0x9a, 0x2a, 0x11, 0xad, 0x53, 0x08, 0xdc, 0x2d, 0xee, - 0x57, 0x19, 0x72, 0xd6, 0x78, 0xb4, 0xb8, 0x7f, 0xaa, 0x5f, 0x31, 0xcb, 0xe8, 0x62, 0xea, 0x67, 0x13, 0x96, 0x78, - 0xed, 0xc2, 0xbd, 0x5a, 0x28, 0x50, 0x8f, 0x8e, 0x8e, 0x0a, 0x37, 0xd4, 0x4f, 0xed, 0x30, 0x2c, 0xdc, 0x60, 0x51, - 0x4e, 0xa3, 0xdd, 0x1e, 0x8f, 0x0b, 0x97, 0xe9, 0x82, 0xfd, 0x6e, 0x10, 0xee, 0x77, 0x0b, 0xf7, 0xda, 0xa8, 0x51, - 0xb8, 0x54, 0x3d, 0x65, 0x34, 0xac, 0x1d, 0x7d, 0x78, 0xd4, 0x6e, 0x17, 0xae, 0x24, 0xb4, 0x05, 0xc4, 0xe4, 0xe4, - 0x4f, 0xcf, 0x9f, 0x73, 0x30, 0x98, 0x8a, 0x5e, 0xcc, 0x9d, 0xe1, 0xae, 0xba, 0x56, 0x52, 0x7e, 0x87, 0xb1, 0x40, - 0x23, 0xfc, 0xb5, 0x99, 0x39, 0x07, 0xc4, 0x2c, 0x32, 0xe6, 0x62, 0x9d, 0x58, 0x57, 0x7b, 0x0d, 0x94, 0x25, 0x5e, - 0x7f, 0x4d, 0xe2, 0x2a, 0xa1, 0x0e, 0xf8, 0x18, 0xd4, 0x94, 0xb7, 0x9f, 0x27, 0xdb, 0xa4, 0x47, 0xf6, 0x69, 0xe9, - 0x71, 0x79, 0x1f, 0xe1, 0x91, 0xfd, 0xe1, 0xc2, 0x23, 0x31, 0x85, 0x87, 0x64, 0x1d, 0xd7, 0x9c, 0xd8, 0x41, 0x44, - 0x83, 0x8f, 0x97, 0xe9, 0x4d, 0x13, 0xb6, 0x44, 0x66, 0x0b, 0xb1, 0x72, 0xf5, 0x5b, 0x33, 0xf9, 0x75, 0x67, 0xc6, - 0x47, 0x1c, 0x85, 0x8e, 0xff, 0x26, 0x21, 0xf6, 0x1b, 0x1d, 0xd8, 0x93, 0x25, 0xe3, 0x31, 0xb1, 0xdf, 0x8c, 0xc7, - 0xb6, 0xbe, 0x1c, 0xc7, 0xe7, 0x54, 0xd4, 0x7a, 0x5d, 0x2b, 0x11, 0xb5, 0xc0, 0xd0, 0xaf, 0xca, 0xcc, 0x02, 0x95, - 0x77, 0x67, 0xe6, 0xd8, 0xa9, 0x37, 0x21, 0xcb, 0x61, 0xab, 0xc1, 0xb7, 0x25, 0xeb, 0x97, 0xf3, 0x27, 0xb5, 0x2f, - 0x29, 0x95, 0x00, 0x6f, 0xf8, 0xfc, 0xd3, 0xea, 0xcd, 0x70, 0x13, 0xaa, 0x55, 0xfc, 0x27, 0xb7, 0x2f, 0x42, 0xe7, - 0x9a, 0xa3, 0x82, 0xe5, 0x6f, 0x92, 0x95, 0x5b, 0x1f, 0x24, 0x8c, 0x84, 0x98, 0xd3, 0x2a, 0x78, 0x3a, 0x99, 0xc4, - 0xe2, 0x30, 0x49, 0xcd, 0xe0, 0x96, 0xcd, 0x07, 0xb5, 0xf9, 0x7a, 0x66, 0x43, 0xf5, 0x79, 0x0d, 0xf1, 0xbd, 0x61, - 0x79, 0x5a, 0xf8, 0x4a, 0x7d, 0x78, 0x56, 0xc4, 0x04, 0x17, 0x8a, 0xc7, 0x2f, 0xe4, 0x19, 0x53, 0x8e, 0x59, 0x28, - 0x9b, 0xb3, 0xb0, 0x28, 0xd4, 0xe9, 0xfc, 0x90, 0xe5, 0x33, 0xd0, 0x9e, 0x64, 0x4b, 0xfa, 0x29, 0x16, 0x9e, 0x5f, - 0x1b, 0xc9, 0x6d, 0xb5, 0xe5, 0x2a, 0xb4, 0x9d, 0x26, 0xb3, 0x85, 0xae, 0x79, 0x61, 0x2b, 0x93, 0x4d, 0x23, 0xd1, - 0xb6, 0x24, 0x3e, 0x65, 0xda, 0x9d, 0x31, 0x43, 0xc8, 0xfc, 0x29, 0x17, 0x44, 0xbf, 0xd2, 0x05, 0x85, 0x69, 0x65, - 0x89, 0x37, 0x12, 0x5b, 0x22, 0x55, 0x2c, 0x9f, 0xf9, 0x89, 0x36, 0xe6, 0x24, 0x3f, 0xd8, 0x5d, 0x54, 0x2b, 0x5f, - 0xd8, 0x1a, 0x6c, 0x49, 0xbc, 0xfd, 0xe3, 0x16, 0x34, 0xe8, 0x5b, 0x35, 0xd0, 0x93, 0xb5, 0x0c, 0xb3, 0xbb, 0xf3, - 0xae, 0x3f, 0x5e, 0xb8, 0xf9, 0x35, 0x76, 0xf3, 0x6b, 0xeb, 0xab, 0x45, 0xf3, 0x9a, 0x5e, 0x7e, 0x64, 0xbc, 0xc9, - 0xfd, 0x59, 0x13, 0xbc, 0xa7, 0x22, 0x33, 0x44, 0xb1, 0x67, 0xa1, 0xa3, 0x4b, 0xd3, 0xaf, 0x37, 0xcf, 0x21, 0x3d, - 0x5b, 0x98, 0x51, 0x5e, 0x92, 0x26, 0xb4, 0x57, 0x3f, 0xbe, 0x67, 0x66, 0x18, 0x6b, 0x6c, 0x8d, 0x16, 0x29, 0xa4, - 0x73, 0xf3, 0x5b, 0xaf, 0xad, 0xd8, 0x7a, 0x5b, 0xa7, 0x0f, 0xb7, 0x37, 0xd6, 0xf7, 0x14, 0x72, 0x1b, 0x42, 0x7a, - 0x65, 0xeb, 0xf9, 0xcf, 0xdb, 0xf2, 0xbb, 0x3f, 0x75, 0x98, 0x0d, 0xf2, 0x49, 0xf4, 0xff, 0xc6, 0x29, 0xc0, 0xd5, - 0x62, 0x71, 0x98, 0xed, 0x3e, 0x90, 0x79, 0xfe, 0x98, 0xd3, 0x0c, 0xdf, 0xa7, 0xe6, 0xa5, 0xb8, 0x77, 0x62, 0x01, - 0x62, 0xc6, 0xeb, 0x1c, 0xd5, 0x53, 0xb1, 0xef, 0xee, 0xfe, 0xee, 0xe9, 0x17, 0x0a, 0x47, 0xfa, 0x1e, 0x56, 0xdb, - 0xee, 0xc1, 0x46, 0x88, 0xfd, 0x5b, 0x8f, 0x25, 0x42, 0xe6, 0x5d, 0x42, 0x52, 0x48, 0x6f, 0x96, 0xaa, 0x53, 0x99, - 0x19, 0x8d, 0xc5, 0xa7, 0xd7, 0xd5, 0x52, 0xec, 0x3f, 0x9c, 0xdd, 0xe8, 0xd5, 0xe8, 0xac, 0x9c, 0xb6, 0xfc, 0x43, - 0x0f, 0x55, 0x6e, 0x3f, 0xc5, 0x59, 0x3f, 0x18, 0x78, 0x38, 0xbb, 0xe9, 0x49, 0x41, 0xdb, 0xcc, 0x24, 0x54, 0xed, - 0xd9, 0x8d, 0x79, 0xac, 0xb4, 0xea, 0xc8, 0x72, 0xf7, 0x73, 0x8b, 0xfa, 0x39, 0xed, 0xc1, 0x97, 0xa6, 0x58, 0xe0, - 0xc7, 0x4a, 0x98, 0x4f, 0x59, 0x18, 0xc6, 0xb4, 0xa7, 0xe5, 0xb5, 0xd5, 0x79, 0x08, 0xa7, 0x32, 0xcd, 0x25, 0xab, - 0xaf, 0x8a, 0x81, 0xbc, 0x12, 0x4f, 0xfe, 0x65, 0x9e, 0xc6, 0xf0, 0x9d, 0xc7, 0x8d, 0xe8, 0x54, 0xc7, 0x15, 0xdb, - 0x15, 0xf2, 0xc4, 0xef, 0xfa, 0x5c, 0x0e, 0xdb, 0x7f, 0xea, 0x89, 0x05, 0x6f, 0xf7, 0x78, 0x3a, 0xf3, 0x9a, 0xfb, - 0xf5, 0x89, 0xc0, 0xab, 0x72, 0x0a, 0x78, 0xc3, 0xb4, 0x30, 0x48, 0x2b, 0xc9, 0xa7, 0x2d, 0xb7, 0xa3, 0xca, 0x44, - 0x07, 0x60, 0x84, 0x96, 0x45, 0x45, 0x7d, 0x32, 0xff, 0x98, 0xdd, 0xf2, 0x78, 0xf3, 0x6e, 0x79, 0xac, 0x77, 0xcb, - 0xdd, 0x14, 0xfb, 0xe5, 0xb8, 0x03, 0xff, 0xf5, 0xaa, 0x09, 0x79, 0x6d, 0x6b, 0x7f, 0x76, 0x63, 0x81, 0x9e, 0xd6, - 0xec, 0xce, 0x6e, 0xe4, 0xa1, 0x5a, 0x48, 0x5c, 0x6b, 0xc3, 0x31, 0x53, 0xdc, 0xb6, 0xa0, 0x10, 0xfe, 0x6f, 0xd7, - 0x5e, 0x75, 0x0e, 0xe0, 0x1d, 0xb4, 0x3a, 0x5c, 0x7f, 0xd7, 0xbd, 0x7b, 0xd3, 0x7a, 0x49, 0xca, 0x1d, 0x4f, 0x73, - 0x63, 0xe4, 0x72, 0xff, 0xf2, 0x92, 0x86, 0xde, 0x38, 0x0d, 0xe6, 0xf9, 0x3f, 0x29, 0xf8, 0x15, 0x12, 0xef, 0xdc, - 0xd2, 0x2b, 0xfd, 0xe8, 0xa6, 0xf2, 0x88, 0xaf, 0xee, 0x61, 0x51, 0xae, 0x93, 0x97, 0x07, 0x7e, 0x4c, 0x9d, 0xae, - 0x7b, 0xb0, 0x61, 0x13, 0xfc, 0x9b, 0xac, 0xcd, 0xc6, 0xc9, 0xfc, 0x5e, 0x64, 0xdc, 0x89, 0x84, 0xcf, 0xc2, 0x81, - 0xb9, 0x86, 0xed, 0xa3, 0xcd, 0xe0, 0x0e, 0xf5, 0x48, 0x23, 0x2d, 0x14, 0x94, 0xdc, 0x09, 0xe9, 0xd8, 0x9f, 0xc7, - 0xfc, 0xee, 0x5e, 0xb7, 0x51, 0xc6, 0x5a, 0xaf, 0x77, 0x30, 0xf4, 0xaa, 0xee, 0x3d, 0xb9, 0xf4, 0x97, 0x8f, 0x0f, - 0xe0, 0x3f, 0x79, 0xf8, 0xe5, 0xb2, 0xd2, 0xd5, 0xa5, 0xd5, 0x0b, 0xba, 0xfa, 0x55, 0x4d, 0x19, 0x97, 0x22, 0x5c, - 0xe8, 0xe3, 0xf7, 0xad, 0x0d, 0x5a, 0xe5, 0xbd, 0xaa, 0x2b, 0x2d, 0xeb, 0xb3, 0x6a, 0x7f, 0x5e, 0xe7, 0xf7, 0xac, - 0x1b, 0x48, 0xcd, 0xb5, 0x5e, 0x57, 0x7d, 0x7a, 0x7e, 0xad, 0xb2, 0xc6, 0xb8, 0xa8, 0x7f, 0x45, 0x2e, 0x4b, 0x13, - 0x45, 0xa6, 0xa2, 0x82, 0x95, 0x72, 0x25, 0xad, 0x94, 0x94, 0x92, 0x8b, 0xe3, 0xc1, 0xcd, 0x34, 0xb6, 0xae, 0xe4, - 0xfd, 0x38, 0xc4, 0xee, 0xb8, 0x6d, 0xdb, 0x12, 0x4e, 0x3a, 0xf8, 0x4c, 0x97, 0xfd, 0xe1, 0xfd, 0xd7, 0xcd, 0x23, - 0x7b, 0x00, 0x9a, 0xd6, 0xd5, 0x44, 0x68, 0x76, 0x2f, 0xfd, 0x5b, 0x9a, 0x9d, 0x77, 0x95, 0x0b, 0x5e, 0xe6, 0x8b, - 0x8b, 0x32, 0xab, 0x6b, 0x5b, 0x37, 0xd3, 0x38, 0xc9, 0x89, 0x1d, 0x71, 0x3e, 0xf3, 0x5a, 0xad, 0xeb, 0xeb, 0x6b, - 0xf7, 0x7a, 0xdf, 0x4d, 0xb3, 0x49, 0xab, 0xdb, 0x6e, 0xb7, 0xe1, 0x8b, 0x1f, 0xb6, 0x75, 0xc5, 0xe8, 0xf5, 0x93, - 0xf4, 0x86, 0xd8, 0x6d, 0xab, 0x6d, 0x75, 0xba, 0x47, 0x56, 0xa7, 0x7b, 0xe0, 0x3e, 0x3c, 0xb2, 0xfb, 0x5f, 0x58, - 0xd6, 0x71, 0x48, 0xc7, 0x39, 0xfc, 0xb0, 0xac, 0x63, 0xa1, 0x78, 0xc9, 0xdf, 0x96, 0xe5, 0x06, 0x71, 0xde, 0xec, - 0x58, 0x0b, 0xf5, 0x68, 0x59, 0x70, 0x8b, 0x90, 0x67, 0x7d, 0x39, 0xee, 0x8e, 0x0f, 0xc6, 0x8f, 0x7b, 0xaa, 0xb8, - 0xf8, 0xa2, 0x56, 0x1d, 0xcb, 0x7f, 0xbb, 0x46, 0xb3, 0x9c, 0x67, 0xe9, 0x47, 0xaa, 0x5c, 0xfb, 0x16, 0x88, 0x9e, - 0x8d, 0x4d, 0xbb, 0xeb, 0x23, 0x75, 0x8e, 0x2e, 0x83, 0x71, 0xb7, 0xaa, 0x2e, 0x60, 0x6c, 0x95, 0x40, 0x1e, 0xb7, - 0x34, 0xe8, 0xc7, 0x26, 0x9a, 0x3a, 0xcd, 0x4d, 0x88, 0xea, 0xd8, 0x6a, 0x8e, 0x13, 0x3d, 0xbf, 0x63, 0x38, 0xb4, - 0xae, 0x75, 0x55, 0x01, 0x81, 0x6d, 0x85, 0xc4, 0x7e, 0xd5, 0xe9, 0x1e, 0xe1, 0x4e, 0xe7, 0xa1, 0xfb, 0xf0, 0x28, - 0x68, 0xe3, 0x03, 0xf7, 0xa0, 0xb9, 0xef, 0x3e, 0xc4, 0x47, 0xcd, 0x23, 0x7c, 0xf4, 0xfc, 0x28, 0x68, 0x1e, 0xb8, - 0x07, 0xb8, 0xdd, 0x3c, 0x82, 0xc2, 0xe6, 0x51, 0xf3, 0xe8, 0xaa, 0x79, 0x70, 0x14, 0xb4, 0x45, 0x69, 0xd7, 0x3d, - 0x3c, 0x6c, 0x76, 0xda, 0xee, 0xe1, 0x21, 0x3e, 0x74, 0x1f, 0x3e, 0x6c, 0x76, 0xf6, 0xdd, 0x87, 0x0f, 0x5f, 0x1e, - 0x1e, 0xb9, 0xfb, 0xf0, 0x6e, 0x7f, 0x3f, 0xd8, 0x77, 0x3b, 0x9d, 0x26, 0xfc, 0xc1, 0x47, 0x6e, 0x57, 0xfe, 0xe8, - 0x74, 0xdc, 0xfd, 0x0e, 0x6e, 0xc7, 0x87, 0x5d, 0xf7, 0xe1, 0x63, 0x2c, 0xfe, 0x8a, 0x6a, 0x58, 0xfc, 0x81, 0x6e, - 0xf0, 0x63, 0xb7, 0xfb, 0x50, 0xfe, 0x12, 0x1d, 0x5e, 0x1d, 0x1c, 0xfd, 0x68, 0xb7, 0xb6, 0xce, 0xa1, 0x23, 0xe7, - 0x70, 0x74, 0xe8, 0xee, 0xef, 0xe3, 0x83, 0x8e, 0x7b, 0xb4, 0x1f, 0x35, 0x0f, 0xba, 0xee, 0xc3, 0x47, 0x41, 0xb3, - 0xe3, 0x3e, 0x7a, 0x84, 0xdb, 0xcd, 0x7d, 0xb7, 0x8b, 0x3b, 0xee, 0xc1, 0xbe, 0xf8, 0xb1, 0xef, 0x76, 0xaf, 0x1e, - 0x3d, 0x76, 0x1f, 0x1e, 0x46, 0x0f, 0xdd, 0x83, 0x6f, 0x0f, 0x8e, 0xdc, 0xee, 0x7e, 0xb4, 0xff, 0xd0, 0xed, 0x3e, - 0xba, 0x7a, 0xe8, 0x1e, 0x44, 0xcd, 0xee, 0xc3, 0x3b, 0x5b, 0x76, 0xba, 0x2e, 0xe0, 0x48, 0xbc, 0x86, 0x17, 0x58, - 0xbd, 0x80, 0xff, 0x23, 0xd1, 0xf6, 0xdf, 0xb0, 0x9b, 0x7c, 0xbd, 0xe9, 0x63, 0xf7, 0xe8, 0x51, 0x20, 0xab, 0x43, - 0x41, 0x53, 0xd7, 0x80, 0x26, 0x57, 0x4d, 0x39, 0xac, 0xe8, 0xae, 0xa9, 0x3b, 0xd2, 0xff, 0xab, 0xc1, 0xae, 0x9a, - 0x30, 0xb0, 0x1c, 0xf7, 0xdf, 0xb5, 0x9f, 0x72, 0xc9, 0x8f, 0x5b, 0x13, 0x49, 0xfa, 0x93, 0xfe, 0x17, 0xf2, 0x73, - 0x3e, 0x5f, 0x5c, 0x60, 0x7f, 0x9b, 0xe3, 0x23, 0xfe, 0xb4, 0xe3, 0x23, 0xa2, 0xf7, 0xf1, 0x7c, 0xc4, 0x7f, 0xb8, - 0xe7, 0xc3, 0x5f, 0x75, 0x9b, 0xdf, 0xf0, 0x35, 0x07, 0xc7, 0xaa, 0x55, 0xfc, 0x82, 0x3b, 0xc3, 0x14, 0x3e, 0x1d, - 0x5d, 0xf4, 0x6e, 0x38, 0x89, 0xa8, 0xe9, 0x07, 0x4a, 0x81, 0xc5, 0xde, 0x70, 0xc9, 0x63, 0x83, 0x6d, 0x08, 0x09, - 0x3f, 0x8d, 0x90, 0xef, 0xee, 0x83, 0x8f, 0xf0, 0x0f, 0xc7, 0x47, 0x60, 0xe2, 0xa3, 0xe6, 0xc9, 0x17, 0x9e, 0x06, - 0xe1, 0x29, 0x38, 0x13, 0xcf, 0x0e, 0xdc, 0x9a, 0xd1, 0xb0, 0x5b, 0xf4, 0x4a, 0x44, 0xee, 0x64, 0x70, 0xfd, 0xf9, - 0xe7, 0x04, 0x1d, 0xe4, 0x15, 0x39, 0xc4, 0x56, 0x6e, 0x99, 0x99, 0x90, 0x3a, 0xea, 0xa1, 0x14, 0x4a, 0x5d, 0xb7, - 0xed, 0xb6, 0x4b, 0x97, 0x0e, 0x5c, 0x8b, 0x44, 0x16, 0x29, 0xf7, 0xbd, 0x9d, 0x0e, 0x8e, 0xd3, 0x09, 0x5c, 0x96, - 0x24, 0x3e, 0x1f, 0x07, 0x27, 0x1e, 0x02, 0xf9, 0xe5, 0x3e, 0x48, 0x9f, 0x50, 0x8e, 0x1e, 0x3f, 0xfb, 0xf8, 0x37, - 0x08, 0x62, 0xea, 0x98, 0xc4, 0x14, 0xbc, 0x1d, 0xaf, 0x68, 0xc8, 0x7c, 0xc7, 0x76, 0x66, 0x19, 0x1d, 0xd3, 0x2c, - 0x6f, 0xd6, 0xee, 0xeb, 0x11, 0x57, 0xf5, 0x20, 0x5b, 0x41, 0x38, 0xce, 0xe0, 0x73, 0x48, 0x64, 0xa8, 0xfc, 0x8d, - 0xb6, 0x32, 0xc0, 0xec, 0x02, 0xeb, 0x92, 0x0c, 0x64, 0x6d, 0xa5, 0xb4, 0xd9, 0x52, 0x6b, 0xeb, 0xb8, 0xdd, 0x43, - 0x64, 0x89, 0x62, 0xf8, 0xd0, 0xcc, 0x0f, 0x4e, 0x73, 0xbf, 0xfd, 0x27, 0x64, 0x34, 0x2b, 0x3b, 0x1a, 0x29, 0x77, - 0x5b, 0x52, 0x7e, 0x8e, 0x70, 0x25, 0xec, 0x6a, 0x4b, 0x8a, 0xf8, 0x52, 0xce, 0xdd, 0x46, 0xbd, 0x44, 0x25, 0xcd, - 0xc9, 0x2b, 0x01, 0xc7, 0x6c, 0xe2, 0x18, 0xd7, 0x4d, 0x24, 0xf2, 0x43, 0x36, 0x70, 0x5b, 0x3d, 0x42, 0x45, 0x55, - 0x25, 0x41, 0x0b, 0x11, 0x6d, 0x61, 0x89, 0x95, 0x2c, 0x97, 0x4e, 0x02, 0x2e, 0x72, 0x62, 0xe0, 0x14, 0x9e, 0x51, - 0x0d, 0xc9, 0x09, 0x2e, 0x01, 0x12, 0x08, 0x26, 0x89, 0xfc, 0xb7, 0x2a, 0xd6, 0x3f, 0x94, 0xe3, 0xcb, 0x8d, 0xfd, - 0x64, 0x02, 0x54, 0xe8, 0x27, 0x93, 0x35, 0xb7, 0x9a, 0x0c, 0x18, 0xad, 0x94, 0x56, 0x5d, 0x55, 0xee, 0xb3, 0xfc, - 0xc9, 0xed, 0x7b, 0x75, 0xe3, 0xb5, 0x0d, 0xde, 0x69, 0x11, 0xdf, 0xa8, 0xbe, 0xce, 0xd3, 0x20, 0x0f, 0x8e, 0xa7, - 0x94, 0xfb, 0xf2, 0xb0, 0x1a, 0xe8, 0x13, 0x90, 0xcb, 0x62, 0x29, 0x6b, 0x54, 0x05, 0xf5, 0x89, 0x3c, 0xcc, 0x2f, - 0x45, 0x3d, 0xb6, 0xd4, 0x55, 0x71, 0x4d, 0xb1, 0x34, 0xa4, 0x83, 0xa5, 0x3f, 0x26, 0xf0, 0xc5, 0x71, 0x64, 0x92, - 0xa4, 0x76, 0xff, 0x41, 0x99, 0xeb, 0xb2, 0x6d, 0x11, 0x62, 0x96, 0x7c, 0x1c, 0x66, 0x34, 0xfe, 0x27, 0xf2, 0x80, - 0x05, 0x69, 0xf2, 0x60, 0x64, 0xa3, 0x1e, 0x77, 0xa3, 0x8c, 0x8e, 0xc9, 0x03, 0x90, 0xf1, 0x9e, 0xb0, 0x3e, 0x80, - 0x11, 0x36, 0x6e, 0xa6, 0x31, 0x16, 0x1a, 0xd3, 0x3d, 0x14, 0x22, 0x09, 0xae, 0xdd, 0x3d, 0xb4, 0x2d, 0x69, 0x13, - 0x8b, 0xdf, 0x7d, 0x29, 0x4e, 0x85, 0x12, 0x60, 0x75, 0xba, 0xee, 0x61, 0xd4, 0x75, 0x1f, 0x5f, 0x3d, 0x72, 0x8f, - 0xa2, 0xce, 0xa3, 0xab, 0x26, 0xfc, 0xdb, 0x75, 0x1f, 0xc7, 0xcd, 0xae, 0xfb, 0x18, 0xfe, 0xff, 0xf6, 0xc0, 0x3d, - 0x8c, 0x9a, 0x1d, 0xf7, 0xe8, 0x6a, 0xdf, 0xdd, 0x7f, 0xd9, 0xe9, 0xba, 0xfb, 0x56, 0xc7, 0x92, 0xed, 0x80, 0x5d, - 0x4b, 0xee, 0xfc, 0x60, 0x65, 0x43, 0x6c, 0x08, 0xc6, 0xc9, 0x03, 0x77, 0x36, 0x16, 0x67, 0xa4, 0xcd, 0xfd, 0xa9, - 0x9c, 0x75, 0x4f, 0xfd, 0x0c, 0xbe, 0x6c, 0x5a, 0xdf, 0xbb, 0xb5, 0x77, 0xb8, 0xc6, 0x2f, 0x36, 0x0c, 0x31, 0x13, - 0x11, 0x70, 0xf3, 0xae, 0x35, 0x2a, 0xee, 0xb0, 0x93, 0xdf, 0x82, 0x52, 0x51, 0xb0, 0x32, 0xbb, 0xc8, 0x20, 0x6b, - 0x59, 0x03, 0x12, 0x80, 0x04, 0x0d, 0xae, 0xe6, 0x8f, 0x56, 0x74, 0x9e, 0xc1, 0xfd, 0x04, 0x9a, 0x97, 0x30, 0xf1, - 0x45, 0x3e, 0x01, 0xc3, 0x8b, 0xb0, 0x58, 0x05, 0x0f, 0x8e, 0x05, 0x66, 0xa9, 0x71, 0x1b, 0x1d, 0xad, 0x72, 0x00, - 0x42, 0x06, 0xf7, 0x07, 0x16, 0x85, 0x9e, 0x59, 0xcd, 0x8b, 0x5b, 0x21, 0x51, 0xb0, 0x13, 0x9a, 0x0f, 0x6c, 0x28, - 0xb2, 0x3d, 0x5b, 0x78, 0x00, 0xed, 0xf2, 0xe3, 0xaf, 0x25, 0xdd, 0x57, 0x05, 0x58, 0x5c, 0x0e, 0x01, 0x9b, 0x1a, - 0xd0, 0x67, 0xa3, 0xbd, 0xbd, 0xad, 0xdb, 0x49, 0xe8, 0x97, 0x30, 0xb5, 0xea, 0x9b, 0x91, 0x26, 0xa7, 0xb2, 0xcd, - 0x75, 0x28, 0xfb, 0x15, 0x18, 0x46, 0x0a, 0x2d, 0x97, 0xd4, 0xe7, 0xae, 0x9f, 0xc8, 0x03, 0x06, 0x06, 0x3f, 0xc3, - 0x1d, 0xba, 0x8f, 0x8a, 0x94, 0xfb, 0x32, 0x67, 0xcc, 0x64, 0x03, 0x29, 0xf7, 0xf5, 0xdd, 0x4a, 0x3e, 0xaf, 0x9d, - 0xab, 0x8f, 0xba, 0xfd, 0x37, 0xef, 0x4f, 0x2c, 0xb9, 0x7b, 0x8f, 0x5b, 0x51, 0xb7, 0x7f, 0x2c, 0x5c, 0x2a, 0x32, - 0x2b, 0x80, 0xc8, 0xac, 0x00, 0x4b, 0x5d, 0x2a, 0x03, 0x81, 0xb6, 0xa2, 0x25, 0xa7, 0x2d, 0x4c, 0x0a, 0xe9, 0x0c, - 0x9e, 0xce, 0x63, 0xce, 0xe0, 0x9b, 0x47, 0x2d, 0x91, 0x12, 0x20, 0x52, 0x0c, 0xf4, 0x19, 0x55, 0xa5, 0x3c, 0x5e, - 0xf2, 0x44, 0xbb, 0x8e, 0xc7, 0x2c, 0xa6, 0xfa, 0x54, 0xaa, 0xea, 0xaa, 0xcc, 0x07, 0x5a, 0xaf, 0x9d, 0xcf, 0x2f, - 0x21, 0x27, 0x42, 0x67, 0x1f, 0x7d, 0x50, 0x0d, 0x8e, 0xc5, 0x50, 0x10, 0xd8, 0x97, 0x52, 0x5c, 0x7f, 0xdd, 0xb5, - 0xbe, 0xa4, 0x6a, 0xf6, 0x4a, 0x80, 0xc0, 0x4d, 0x1e, 0xd1, 0x7e, 0xbf, 0xf4, 0x26, 0x9b, 0xef, 0x8a, 0xe3, 0x56, - 0xb4, 0xdf, 0xbf, 0xf0, 0x26, 0xaa, 0xbf, 0x97, 0xe9, 0x64, 0x73, 0x5f, 0x71, 0x3a, 0x19, 0x88, 0x63, 0xf2, 0xf2, - 0xca, 0x27, 0xad, 0x1b, 0xa7, 0xb1, 0xdd, 0x3f, 0x56, 0xba, 0x82, 0x25, 0xa2, 0xee, 0xf6, 0x61, 0x5b, 0x9f, 0xbc, - 0x8f, 0xd3, 0x09, 0xec, 0x57, 0xd9, 0xc4, 0x18, 0xa4, 0xe6, 0x90, 0x8f, 0x3a, 0xfd, 0x63, 0xdf, 0x12, 0xac, 0x47, - 0xf0, 0x96, 0xdc, 0x6b, 0x41, 0xe3, 0x28, 0x9d, 0x52, 0x97, 0xa5, 0xad, 0x6b, 0x7a, 0xd9, 0xf4, 0x67, 0xac, 0xf2, - 0x7e, 0x83, 0x4e, 0x52, 0x0e, 0x99, 0xae, 0x64, 0x60, 0x75, 0x2b, 0x6f, 0xdc, 0x01, 0x98, 0x44, 0xda, 0x73, 0x27, - 0x5c, 0x76, 0x06, 0x58, 0x69, 0xff, 0xb8, 0xe5, 0xaf, 0x60, 0x44, 0x6c, 0xc5, 0x42, 0xf9, 0xe1, 0xc1, 0xee, 0xb9, - 0x14, 0xe9, 0x5f, 0x52, 0x5a, 0x68, 0x7f, 0xbd, 0x92, 0xe3, 0x85, 0xdd, 0xff, 0xd7, 0xff, 0xf1, 0xbf, 0x94, 0x0b, - 0xfe, 0xb8, 0x15, 0x75, 0x74, 0x5f, 0x2b, 0xab, 0x52, 0x1c, 0xc3, 0x3d, 0x36, 0x55, 0xcc, 0x98, 0xde, 0x34, 0x27, - 0x19, 0x0b, 0x9b, 0x91, 0x1f, 0x8f, 0xed, 0xfe, 0x76, 0x6c, 0xca, 0xf4, 0xc4, 0xa6, 0x8e, 0xb6, 0xae, 0x17, 0x01, - 0xbd, 0xfe, 0xa6, 0x4b, 0x19, 0x74, 0xc6, 0x97, 0xd8, 0xda, 0xe6, 0x15, 0x0d, 0xd5, 0xee, 0xab, 0x5d, 0xd3, 0x90, - 0xa8, 0x4f, 0x46, 0x2b, 0x06, 0x99, 0xd4, 0x6e, 0x67, 0x28, 0x6c, 0xab, 0x8c, 0x79, 0xfd, 0xdf, 0xff, 0xf9, 0x5f, - 0xfe, 0x9b, 0x7e, 0x84, 0x50, 0xd6, 0xbf, 0xfe, 0xf7, 0xff, 0xfc, 0x7f, 0xfe, 0xf7, 0x7f, 0x85, 0xf4, 0x34, 0x15, - 0xee, 0x12, 0x4c, 0xc5, 0xaa, 0x62, 0x5d, 0x92, 0xbb, 0x58, 0x70, 0xe8, 0x6d, 0xca, 0x72, 0xce, 0x82, 0xfa, 0x7d, - 0x0d, 0x67, 0x62, 0x40, 0xb1, 0x33, 0x15, 0x74, 0x62, 0x87, 0x17, 0x15, 0x41, 0xd5, 0x50, 0x2e, 0x08, 0xb7, 0x38, - 0x6e, 0x01, 0xbe, 0xef, 0x77, 0xdd, 0x8c, 0x5b, 0x2e, 0xc7, 0x42, 0x93, 0x09, 0x94, 0x14, 0x55, 0xb9, 0x05, 0xa1, - 0x97, 0x05, 0x3c, 0x7a, 0x5d, 0xa3, 0x58, 0xac, 0x5e, 0xad, 0x4d, 0xef, 0xe7, 0x79, 0xce, 0xd9, 0x18, 0x50, 0x2e, - 0xdd, 0xc8, 0x22, 0xca, 0xdd, 0x04, 0x55, 0x32, 0xbe, 0x2d, 0x44, 0x2f, 0x92, 0x40, 0x0f, 0x8e, 0xfe, 0x54, 0xfc, - 0x79, 0x0a, 0x0a, 0x9b, 0xe5, 0x4c, 0xfd, 0x1b, 0x65, 0xbd, 0x3f, 0x6c, 0xb7, 0x67, 0x37, 0x68, 0x51, 0x8d, 0x80, - 0xb7, 0x0d, 0x26, 0xe8, 0xd8, 0xec, 0x50, 0x84, 0xc7, 0x4b, 0x2f, 0x77, 0xdb, 0x02, 0x57, 0xb9, 0xd5, 0x2e, 0x8a, - 0xaf, 0x16, 0xc2, 0xd1, 0xca, 0x7e, 0x85, 0x30, 0xb6, 0xf2, 0x49, 0x5f, 0xa6, 0xe6, 0xe4, 0x16, 0x46, 0xab, 0xae, - 0x6c, 0x15, 0x75, 0xd6, 0x6f, 0x6e, 0x31, 0xc3, 0xf0, 0x66, 0x00, 0xfd, 0x00, 0x42, 0xe2, 0x51, 0x07, 0x47, 0xdd, - 0x45, 0xd9, 0x3d, 0xe7, 0xe9, 0xd4, 0x8c, 0xbb, 0x53, 0x9f, 0x06, 0x74, 0xac, 0x7d, 0xf9, 0xea, 0xbd, 0x8c, 0xa9, - 0x17, 0xd1, 0xfe, 0x86, 0xb1, 0x14, 0x48, 0x22, 0xde, 0x6e, 0xb5, 0x8b, 0x2f, 0x61, 0x07, 0x2e, 0xc6, 0x71, 0xea, - 0x73, 0x4f, 0x10, 0x6c, 0xcf, 0x8c, 0xde, 0xfb, 0xc0, 0x93, 0xd2, 0x85, 0x01, 0x4f, 0x4f, 0x56, 0x05, 0xaf, 0x7a, - 0xfd, 0x06, 0xc7, 0xc2, 0x15, 0xcd, 0xcd, 0xae, 0xa4, 0x53, 0xee, 0x3b, 0x15, 0x14, 0x7f, 0x5e, 0xf3, 0x66, 0x29, - 0x81, 0xd4, 0x45, 0x9b, 0xdf, 0x4b, 0xb1, 0x2f, 0xdf, 0x7e, 0xcf, 0x1d, 0x5b, 0x80, 0x69, 0xaf, 0xd6, 0x12, 0x85, - 0x50, 0xeb, 0x39, 0xf9, 0xae, 0xb4, 0xa8, 0xfc, 0xd9, 0x4c, 0x54, 0x44, 0xbd, 0xe3, 0x96, 0x54, 0x84, 0x81, 0x7b, - 0x88, 0x8c, 0x0f, 0x99, 0x60, 0xa1, 0x2a, 0xa9, 0xad, 0x20, 0x7f, 0xa9, 0xd4, 0x0b, 0xf8, 0x94, 0x78, 0xff, 0xff, - 0x01, 0x65, 0x21, 0x07, 0x4b, 0xe3, 0x97, 0x00, 0x00}; + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xed, 0x7d, 0xdb, 0x72, 0xdb, 0xc6, 0xb6, 0xe0, 0xf3, + 0xe4, 0x2b, 0x20, 0x44, 0x5b, 0x46, 0x87, 0x4d, 0xf0, 0x22, 0xc9, 0x96, 0x41, 0x35, 0xb9, 0x65, 0xd9, 0xd9, 0xf6, + 0x8e, 0x6f, 0xdb, 0xb2, 0x73, 0x53, 0xb8, 0x25, 0x08, 0x68, 0x12, 0x1d, 0x83, 0x00, 0x03, 0x34, 0x45, 0x29, 0x24, + 0x4e, 0xcd, 0x07, 0x4c, 0xd5, 0x54, 0xcd, 0xd3, 0xbc, 0x4c, 0xcd, 0x79, 0x98, 0x8f, 0x98, 0xe7, 0xf3, 0x29, 0xe7, + 0x07, 0x66, 0x3e, 0x61, 0x6a, 0xf5, 0x05, 0x68, 0xf0, 0x22, 0xcb, 0x49, 0xf6, 0x39, 0xe7, 0x61, 0x2a, 0x15, 0x99, + 0x68, 0xf4, 0x65, 0xf5, 0xea, 0xd5, 0xeb, 0xde, 0x8d, 0xe3, 0x9d, 0x30, 0x0d, 0xf8, 0xed, 0x94, 0x5a, 0x11, 0x9f, + 0xc4, 0xfd, 0x63, 0xf5, 0x97, 0xfa, 0x61, 0xff, 0x38, 0x66, 0xc9, 0x47, 0x2b, 0xa3, 0x31, 0x61, 0x41, 0x9a, 0x58, + 0x51, 0x46, 0x47, 0x24, 0xf4, 0xb9, 0xef, 0xb1, 0x89, 0x3f, 0xa6, 0x56, 0xab, 0x7f, 0x3c, 0xa1, 0xdc, 0xb7, 0x82, + 0xc8, 0xcf, 0x72, 0xca, 0xc9, 0x87, 0xf7, 0x5f, 0x37, 0x8f, 0xfa, 0xc7, 0x79, 0x90, 0xb1, 0x29, 0xb7, 0xa0, 0x4b, + 0x32, 0x49, 0xc3, 0x59, 0x4c, 0xfb, 0xad, 0xd6, 0x7c, 0x3e, 0x77, 0x7f, 0xce, 0xbf, 0x08, 0xd2, 0x24, 0xe7, 0xd6, + 0x53, 0x32, 0x67, 0x49, 0x98, 0xce, 0x71, 0xc2, 0xc9, 0x53, 0xf7, 0x2c, 0xf2, 0xc3, 0x74, 0xfe, 0x2e, 0x4d, 0xf9, + 0xde, 0x9e, 0x23, 0x1f, 0x6f, 0x4f, 0xcf, 0xce, 0x08, 0x21, 0xd7, 0x29, 0x0b, 0xad, 0xf6, 0x72, 0x59, 0x15, 0xba, + 0x89, 0xcf, 0xd9, 0x35, 0x95, 0x4d, 0xd0, 0xde, 0x9e, 0xed, 0x87, 0xe9, 0x94, 0xd3, 0xf0, 0x8c, 0xdf, 0xc6, 0xf4, + 0x2c, 0xa2, 0x94, 0xe7, 0x36, 0x4b, 0xac, 0xa7, 0x69, 0x30, 0x9b, 0xd0, 0x84, 0xbb, 0xd3, 0x2c, 0xe5, 0x29, 0x40, + 0xb2, 0xb7, 0x67, 0x67, 0x74, 0x1a, 0xfb, 0x01, 0x85, 0xf7, 0xa7, 0x67, 0x67, 0x55, 0x8b, 0xaa, 0x12, 0xce, 0x39, + 0x39, 0xbb, 0x9d, 0x5c, 0xa5, 0xb1, 0x83, 0x70, 0xc4, 0x49, 0x42, 0xe7, 0xd6, 0x77, 0xd4, 0xff, 0xf8, 0xca, 0x9f, + 0xf6, 0x82, 0xd8, 0xcf, 0x73, 0xeb, 0x84, 0x2f, 0xc4, 0x14, 0xb2, 0x59, 0xc0, 0xd3, 0xcc, 0xe1, 0x98, 0x62, 0x86, + 0x16, 0x6c, 0xe4, 0xf0, 0x88, 0xe5, 0xee, 0xc5, 0x6e, 0x90, 0xe7, 0xef, 0x68, 0x3e, 0x8b, 0xf9, 0x2e, 0xd9, 0x69, + 0x63, 0xb6, 0x43, 0x48, 0xce, 0x11, 0x8f, 0xb2, 0x74, 0x6e, 0x3d, 0xcb, 0xb2, 0x34, 0x73, 0xec, 0xd3, 0xb3, 0x33, + 0x59, 0xc3, 0x62, 0xb9, 0x95, 0xa4, 0xdc, 0x2a, 0xfb, 0xf3, 0xaf, 0x62, 0xea, 0x5a, 0x1f, 0x72, 0x6a, 0x5d, 0xce, + 0x92, 0xdc, 0x1f, 0xd1, 0xd3, 0xb3, 0xb3, 0x4b, 0x2b, 0xcd, 0xac, 0xcb, 0x20, 0xcf, 0x2f, 0x2d, 0x96, 0xe4, 0x9c, + 0xfa, 0xa1, 0x6b, 0xa3, 0x9e, 0x18, 0x2c, 0xc8, 0xf3, 0xf7, 0xf4, 0x86, 0x13, 0x8e, 0xc5, 0x23, 0x27, 0xb4, 0x18, + 0x53, 0x6e, 0xe5, 0xe5, 0xbc, 0x1c, 0xb4, 0x88, 0x29, 0xb7, 0x38, 0x11, 0xef, 0xd3, 0x9e, 0xc4, 0x3d, 0x95, 0x8f, + 0xbc, 0xc7, 0x46, 0x4e, 0xc2, 0xf7, 0xf6, 0x78, 0x89, 0x67, 0x24, 0xa7, 0x66, 0x31, 0x42, 0x77, 0x74, 0xd9, 0xde, + 0x1e, 0x75, 0x63, 0x9a, 0x8c, 0x79, 0x44, 0x08, 0xe9, 0xf4, 0xd8, 0xde, 0x9e, 0xc3, 0x49, 0xc4, 0xdd, 0x31, 0xe5, + 0x0e, 0x45, 0x08, 0x57, 0xad, 0xf7, 0xf6, 0x1c, 0x89, 0x84, 0x94, 0x48, 0xc4, 0xd5, 0x70, 0x8c, 0x5c, 0x85, 0xfd, + 0xb3, 0xdb, 0x24, 0x70, 0x4c, 0xf8, 0x11, 0x66, 0x7b, 0x7b, 0x11, 0x77, 0x73, 0xe8, 0x11, 0x73, 0x84, 0x8a, 0x8c, + 0xf2, 0x59, 0x96, 0x58, 0xbc, 0xe0, 0xe9, 0x19, 0xcf, 0x58, 0x32, 0x76, 0xd0, 0x42, 0x97, 0x19, 0x0d, 0x8b, 0x42, + 0x82, 0xfb, 0x8e, 0x93, 0x9c, 0xf4, 0x61, 0xc4, 0x13, 0xee, 0xc0, 0x2a, 0xa6, 0x23, 0x2b, 0x27, 0xc4, 0xce, 0x45, + 0x5b, 0x7b, 0x90, 0x7b, 0x79, 0xc3, 0xb6, 0xb1, 0x84, 0x12, 0xe7, 0x1c, 0xe1, 0x8f, 0xc4, 0xc9, 0xb1, 0xeb, 0xba, + 0x1c, 0x91, 0xfe, 0x42, 0x63, 0x25, 0x37, 0xe6, 0x39, 0xc8, 0xcf, 0xdb, 0x43, 0x8f, 0xbb, 0x19, 0x0d, 0x67, 0x01, + 0x75, 0x1c, 0x86, 0x13, 0x9c, 0x21, 0xd2, 0x67, 0x0d, 0x27, 0x25, 0x7d, 0x58, 0xee, 0xb4, 0xbe, 0xd6, 0x84, 0xec, + 0xb4, 0x91, 0x82, 0x31, 0xd5, 0x00, 0x02, 0x86, 0x15, 0x3c, 0x29, 0x21, 0x76, 0x32, 0x9b, 0x5c, 0xd1, 0xcc, 0x2e, + 0xab, 0xf5, 0x6a, 0x64, 0x31, 0xcb, 0xa9, 0x15, 0xe4, 0xb9, 0x35, 0x9a, 0x25, 0x01, 0x67, 0x69, 0x62, 0xd9, 0x8d, + 0xb4, 0x61, 0x4b, 0x72, 0x28, 0xa9, 0xc1, 0x46, 0x05, 0x72, 0x12, 0xd4, 0xc8, 0xcf, 0xb3, 0x46, 0x67, 0x88, 0x01, + 0x4a, 0xd4, 0x53, 0xfd, 0x29, 0x04, 0x50, 0x9c, 0xc3, 0x1c, 0x0b, 0xfc, 0x84, 0xc3, 0x2c, 0xc5, 0x14, 0x13, 0x3e, + 0xc8, 0xdd, 0xf5, 0x8d, 0x42, 0xb8, 0x3b, 0xf1, 0xa7, 0x0e, 0x25, 0x7d, 0x2a, 0x88, 0xcb, 0x4f, 0x02, 0x80, 0xb5, + 0xb6, 0x6e, 0x03, 0xea, 0x51, 0xb7, 0x22, 0x29, 0xe4, 0x71, 0x77, 0x94, 0x66, 0xcf, 0xfc, 0x20, 0x82, 0x76, 0x25, + 0xc1, 0x84, 0x7a, 0xbf, 0x05, 0x19, 0xf5, 0x39, 0x7d, 0x16, 0x53, 0x78, 0x72, 0x6c, 0xd1, 0xd2, 0x46, 0x38, 0x21, + 0x4f, 0xdd, 0x98, 0xf1, 0xd7, 0x69, 0x12, 0xd0, 0x5e, 0x62, 0x50, 0x17, 0x83, 0x75, 0x3f, 0xe1, 0x3c, 0x63, 0x57, + 0x33, 0x4e, 0x1d, 0x3b, 0x81, 0x1a, 0x36, 0x4e, 0x10, 0x66, 0x2e, 0xa7, 0x37, 0xfc, 0x34, 0x4d, 0x38, 0x4d, 0x38, + 0xa1, 0x1a, 0xa9, 0x38, 0x77, 0xfd, 0xe9, 0x94, 0x26, 0xe1, 0x69, 0xc4, 0xe2, 0xd0, 0x61, 0xa8, 0x40, 0x05, 0x0e, + 0x38, 0x81, 0x39, 0x92, 0x7e, 0xee, 0xc1, 0x9f, 0xed, 0xb3, 0x71, 0x38, 0xe9, 0x8b, 0x4d, 0x41, 0x89, 0x6d, 0xf7, + 0x46, 0x69, 0xe6, 0xa8, 0x19, 0x58, 0xe9, 0xc8, 0xe2, 0x30, 0xc6, 0xbb, 0x59, 0x4c, 0x73, 0x44, 0x1b, 0x84, 0x95, + 0xcb, 0xa8, 0x10, 0xfc, 0x0e, 0x28, 0xbe, 0x40, 0x4e, 0x8e, 0xbc, 0xbc, 0x77, 0xed, 0x67, 0xd6, 0x8f, 0x6a, 0x47, + 0xfd, 0xac, 0xb9, 0x59, 0xc8, 0xc9, 0xcf, 0x2e, 0xcf, 0x66, 0x39, 0xa7, 0xe1, 0xfb, 0xdb, 0x29, 0xcd, 0xf1, 0x73, + 0x4e, 0x42, 0x3e, 0x08, 0xb9, 0x4b, 0x27, 0x53, 0x7e, 0x7b, 0x26, 0x18, 0xa3, 0x67, 0xdb, 0x78, 0x06, 0x35, 0x33, + 0xea, 0x07, 0xc0, 0xcc, 0x14, 0xb6, 0xde, 0xa6, 0xf1, 0xed, 0x88, 0xc5, 0xf1, 0xd9, 0x6c, 0x3a, 0x4d, 0x33, 0x8e, + 0x39, 0x27, 0x0b, 0x9e, 0x56, 0xb8, 0x81, 0xc5, 0x5c, 0xe4, 0x73, 0xc6, 0x83, 0xc8, 0xe1, 0x68, 0x11, 0xf8, 0x39, + 0xb5, 0x9e, 0xa4, 0x69, 0x4c, 0xfd, 0xc4, 0xcb, 0x49, 0x3e, 0x78, 0xce, 0xbd, 0x64, 0x16, 0xc7, 0xbd, 0xab, 0x8c, + 0xfa, 0x1f, 0x7b, 0xe2, 0xf5, 0x9b, 0xab, 0x9f, 0x69, 0xc0, 0x3d, 0xf1, 0xfb, 0x24, 0xcb, 0xfc, 0x5b, 0xa8, 0x48, + 0x08, 0x54, 0x1b, 0xe4, 0xde, 0x5f, 0xcf, 0xde, 0xbc, 0x76, 0xe5, 0x2e, 0x61, 0xa3, 0x5b, 0x27, 0x2f, 0x77, 0x5e, + 0x5e, 0xe0, 0x51, 0x96, 0x4e, 0x56, 0x86, 0x96, 0x68, 0xcb, 0x7b, 0x5b, 0x40, 0xa0, 0x24, 0xdf, 0x91, 0x5d, 0x9b, + 0x10, 0xbc, 0x16, 0x44, 0x0f, 0x2f, 0x89, 0x1a, 0x17, 0xfe, 0x78, 0xb2, 0xd8, 0xc9, 0xd1, 0xdd, 0xd0, 0xf2, 0xec, + 0x76, 0x41, 0x89, 0x80, 0x73, 0x0a, 0x22, 0x06, 0x60, 0x0c, 0x7c, 0x1e, 0x44, 0x0b, 0x2a, 0x3a, 0x2b, 0x34, 0xc4, + 0xb4, 0x28, 0xf0, 0xb3, 0x92, 0xe0, 0x39, 0xb0, 0x5d, 0xc1, 0xa9, 0x08, 0x5f, 0x2e, 0x73, 0x42, 0x72, 0x84, 0xff, + 0x4a, 0x16, 0xbe, 0x9e, 0x8f, 0xb7, 0xd3, 0xc6, 0xb0, 0x31, 0x3d, 0xc9, 0x5e, 0x70, 0x90, 0x26, 0xd7, 0x34, 0xe3, + 0x34, 0xf3, 0x38, 0xc7, 0x19, 0x1d, 0xc5, 0x00, 0xc6, 0x4e, 0x07, 0x47, 0x7e, 0x7e, 0x1a, 0xf9, 0xc9, 0x98, 0x86, + 0xde, 0x33, 0x5e, 0x60, 0xca, 0x89, 0x3d, 0x62, 0x89, 0x1f, 0xb3, 0x5f, 0x69, 0x68, 0x2b, 0x81, 0xf0, 0xcc, 0xa2, + 0x37, 0x9c, 0x26, 0x61, 0x6e, 0x3d, 0x7f, 0xff, 0xea, 0xa5, 0x5a, 0xca, 0x9a, 0x8c, 0x40, 0x8b, 0x7c, 0x36, 0xa5, + 0x99, 0x83, 0xb0, 0x92, 0x11, 0xcf, 0x98, 0xe0, 0x8f, 0xaf, 0xfc, 0xa9, 0x2c, 0x61, 0xf9, 0x87, 0x69, 0xe8, 0x73, + 0xfa, 0x96, 0x26, 0x21, 0x4b, 0xc6, 0x64, 0xa7, 0x23, 0xcb, 0x23, 0x5f, 0xbd, 0x08, 0xcb, 0xa2, 0x8b, 0xdd, 0x67, + 0xb1, 0x98, 0x79, 0xf9, 0x38, 0x73, 0x50, 0x91, 0x73, 0x9f, 0xb3, 0xc0, 0xf2, 0xc3, 0xf0, 0x45, 0xc2, 0x38, 0x13, + 0x00, 0x66, 0xb0, 0x40, 0x40, 0xa5, 0x54, 0x4a, 0x0b, 0x0d, 0xb8, 0x83, 0xb0, 0xe3, 0x28, 0x19, 0x10, 0x21, 0xb5, + 0x62, 0x7b, 0x7b, 0x15, 0xc7, 0x1f, 0x50, 0x4f, 0xbe, 0x24, 0xe7, 0x43, 0xe4, 0x4e, 0x67, 0x39, 0x2c, 0xb5, 0x1e, + 0x02, 0x04, 0x4c, 0x7a, 0x95, 0xd3, 0xec, 0x9a, 0x86, 0x25, 0x79, 0xe4, 0x0e, 0x5a, 0xac, 0x8c, 0xa1, 0x76, 0x06, + 0x27, 0xe7, 0xc3, 0x9e, 0xc9, 0xba, 0xa9, 0x22, 0xf5, 0x2c, 0x9d, 0xd2, 0x8c, 0x33, 0x9a, 0x97, 0xdc, 0xc4, 0x01, + 0x41, 0x5a, 0x72, 0x94, 0x84, 0xe8, 0xf9, 0x4d, 0x1d, 0x86, 0x29, 0xaa, 0xf1, 0x0c, 0x2d, 0x6b, 0x9f, 0x5d, 0x0b, + 0xa1, 0x91, 0x60, 0x86, 0x30, 0x97, 0x90, 0x26, 0x08, 0x15, 0x08, 0x73, 0x0d, 0xae, 0xe4, 0x46, 0x6a, 0xb4, 0x5b, + 0x90, 0xd6, 0xe4, 0xaf, 0x42, 0x5a, 0x03, 0x4f, 0xf3, 0x39, 0xdd, 0xdb, 0x73, 0xa8, 0x5b, 0x92, 0x05, 0xd9, 0xe9, + 0xa8, 0x35, 0x32, 0x90, 0xb5, 0x05, 0x6c, 0x18, 0x98, 0x63, 0x8a, 0xf0, 0x0e, 0x75, 0x93, 0xf4, 0x24, 0x08, 0x68, + 0x9e, 0xa7, 0xd9, 0xde, 0xde, 0x8e, 0xa8, 0x5f, 0x2a, 0x14, 0xb0, 0x86, 0x6f, 0xe6, 0x49, 0x05, 0x01, 0xaa, 0x84, + 0xac, 0x12, 0x0d, 0x1c, 0x44, 0x95, 0xd0, 0x39, 0xec, 0x81, 0xd6, 0x3d, 0x3c, 0xfb, 0xe2, 0xc2, 0x6e, 0x70, 0xac, + 0xd0, 0x30, 0xa6, 0x7a, 0xe8, 0xdb, 0xa7, 0x54, 0x6a, 0x57, 0x42, 0xf7, 0x58, 0xc3, 0x8c, 0xdc, 0x41, 0x6e, 0x48, + 0x47, 0x2c, 0x31, 0xa6, 0x5d, 0x03, 0x09, 0x73, 0x9c, 0xa0, 0xc2, 0x58, 0xd0, 0x8d, 0x5d, 0x0b, 0xb5, 0x46, 0xae, + 0xdc, 0x62, 0x2c, 0x54, 0x09, 0x63, 0x19, 0xcf, 0xe9, 0xb0, 0xc0, 0x02, 0xf5, 0x7a, 0x36, 0x99, 0x00, 0xf4, 0x9c, + 0x0f, 0x7b, 0xea, 0x3d, 0x49, 0x24, 0xe6, 0x32, 0xfa, 0xcb, 0x8c, 0xe6, 0x5c, 0xd2, 0xb1, 0xc3, 0x71, 0x86, 0x19, + 0xf0, 0xeb, 0x34, 0x19, 0xb1, 0xf1, 0x2c, 0x03, 0x8d, 0x07, 0x36, 0x23, 0x4d, 0x66, 0x13, 0xaa, 0x9f, 0x36, 0xc1, + 0xf6, 0x66, 0x0a, 0x32, 0x31, 0x07, 0x9a, 0xbe, 0x9b, 0x9c, 0x00, 0x56, 0x8e, 0x96, 0xcb, 0xbf, 0xea, 0x4e, 0xaa, + 0xa5, 0x2c, 0xb5, 0xb4, 0x95, 0x35, 0xa1, 0x1c, 0x29, 0x99, 0xbc, 0xd3, 0x51, 0xe0, 0xf3, 0x21, 0xd9, 0x69, 0x97, + 0x34, 0xac, 0xb0, 0x2a, 0xc1, 0x91, 0x48, 0x7c, 0x23, 0xbb, 0x42, 0x42, 0xc4, 0xd7, 0xc8, 0xc5, 0x8d, 0xd6, 0x28, + 0x35, 0x22, 0xe7, 0xa0, 0x6c, 0xb8, 0xd1, 0x70, 0x1b, 0x39, 0x69, 0x7e, 0xe0, 0xf0, 0xf5, 0x77, 0x15, 0xdb, 0xb8, + 0xae, 0xb3, 0x8d, 0x95, 0x69, 0xd8, 0xd3, 0xb2, 0x89, 0x5d, 0x52, 0x99, 0xda, 0xe8, 0xd5, 0x2b, 0xcc, 0x04, 0x30, + 0xd5, 0x94, 0x8c, 0x2e, 0x5e, 0xfb, 0x13, 0x9a, 0x3b, 0x14, 0xe1, 0x6d, 0x15, 0x24, 0x79, 0x42, 0x95, 0xa1, 0x21, + 0x3b, 0x13, 0x90, 0x9d, 0x0c, 0x49, 0xd5, 0xac, 0xbe, 0xe1, 0x12, 0x4c, 0xcf, 0x93, 0x61, 0xa5, 0xd1, 0x19, 0x93, + 0x17, 0x42, 0x39, 0x27, 0xb5, 0xed, 0x26, 0xcb, 0x24, 0xd2, 0x84, 0xe6, 0x90, 0x23, 0xbc, 0xd3, 0x5e, 0x5d, 0x49, + 0x5d, 0xab, 0x9a, 0xe3, 0xf9, 0x10, 0xd6, 0x41, 0x88, 0x0c, 0x97, 0xe5, 0xe2, 0xdf, 0xda, 0x4e, 0x03, 0xb4, 0x9d, + 0x01, 0x61, 0xb8, 0xa3, 0xd8, 0xe7, 0x4e, 0xa7, 0xd5, 0x06, 0x75, 0xf4, 0x9a, 0x82, 0x44, 0x41, 0x68, 0x7d, 0x2a, + 0xd4, 0x9d, 0x25, 0x79, 0xc4, 0x46, 0xdc, 0x09, 0xb8, 0x60, 0x29, 0x34, 0xce, 0xa9, 0xc5, 0x6b, 0x4a, 0xb1, 0x60, + 0x37, 0x01, 0x10, 0x5b, 0xa9, 0x81, 0x51, 0x0d, 0xa9, 0x60, 0x5b, 0xc0, 0x1d, 0x2a, 0x85, 0xba, 0xe2, 0x32, 0xba, + 0x36, 0x03, 0xa5, 0xb1, 0x33, 0x90, 0x3d, 0x7a, 0x8a, 0x19, 0x30, 0x43, 0x6f, 0x65, 0x9e, 0xc9, 0x21, 0x54, 0x21, + 0x77, 0x79, 0xfa, 0x32, 0x9d, 0xd3, 0xec, 0xd4, 0x07, 0xe0, 0x3d, 0xd9, 0xbc, 0x90, 0x82, 0x40, 0xf0, 0x7b, 0xde, + 0xd3, 0xf4, 0x72, 0x21, 0x26, 0xfe, 0x36, 0x4b, 0x27, 0x2c, 0xa7, 0xa0, 0xae, 0x49, 0xfc, 0x27, 0xb0, 0xcf, 0xc4, + 0x86, 0x04, 0x61, 0x43, 0x4b, 0xfa, 0x3a, 0x79, 0x59, 0xa7, 0xaf, 0x8b, 0xdd, 0x67, 0x63, 0xcd, 0x00, 0xeb, 0xdb, + 0x18, 0x61, 0x47, 0x19, 0x15, 0x86, 0x9c, 0x73, 0x23, 0xa4, 0x44, 0xfc, 0x72, 0xc9, 0x0d, 0xdb, 0xad, 0xa6, 0x30, + 0x52, 0xb9, 0x6d, 0x50, 0xe1, 0x87, 0x21, 0xa8, 0x76, 0x59, 0x1a, 0xc7, 0x86, 0xa8, 0xc2, 0xac, 0x57, 0x0a, 0xa7, + 0x8b, 0xdd, 0x67, 0x67, 0x77, 0xc9, 0x27, 0x78, 0x6f, 0x8a, 0x28, 0x0d, 0x68, 0x12, 0xd2, 0x0c, 0x6c, 0x49, 0x63, + 0xb5, 0x94, 0x94, 0x3d, 0x4d, 0x93, 0x84, 0x06, 0x9c, 0x86, 0x60, 0xaa, 0x30, 0xc2, 0xdd, 0x28, 0xcd, 0x79, 0x59, + 0x58, 0x41, 0xcf, 0x0c, 0xe8, 0x99, 0x1b, 0xf8, 0x71, 0xec, 0x48, 0xb3, 0x64, 0x92, 0x5e, 0xd3, 0x0d, 0x50, 0xf7, + 0x6a, 0x20, 0x97, 0xdd, 0x50, 0xa3, 0x1b, 0xea, 0xe6, 0xd3, 0x98, 0x05, 0xb4, 0x14, 0x5d, 0x67, 0x2e, 0x4b, 0x42, + 0x7a, 0x03, 0x7c, 0x04, 0xf5, 0xfb, 0xfd, 0x36, 0xee, 0xa0, 0x42, 0x22, 0x7c, 0xb1, 0x86, 0xd8, 0x3b, 0x84, 0x26, + 0x10, 0x19, 0xe9, 0x2f, 0x36, 0xb2, 0x35, 0x64, 0x48, 0x4a, 0xa6, 0xcd, 0x2b, 0xc9, 0x9d, 0x11, 0x0e, 0x69, 0x4c, + 0x39, 0xd5, 0xdc, 0x1c, 0x94, 0x68, 0xb9, 0x75, 0xdf, 0x95, 0xf8, 0x2b, 0xc9, 0x49, 0xef, 0x32, 0xbd, 0xe6, 0x79, + 0x69, 0xae, 0x57, 0xcb, 0x53, 0x61, 0x7b, 0xc0, 0xe5, 0xf2, 0xf8, 0x9c, 0xfb, 0x41, 0x24, 0xed, 0x74, 0x67, 0x6d, + 0x4a, 0x55, 0x1f, 0x8a, 0xb3, 0x97, 0x9b, 0xe8, 0x89, 0x06, 0x73, 0x13, 0x0a, 0xce, 0x14, 0x53, 0xa0, 0x60, 0xfa, + 0xc9, 0x65, 0x3b, 0xf5, 0xe3, 0xf8, 0xca, 0x0f, 0x3e, 0xd6, 0xa9, 0xbf, 0x22, 0x03, 0xb2, 0xca, 0x8d, 0x8d, 0x57, + 0x06, 0xcb, 0x32, 0xe7, 0xad, 0xb9, 0x74, 0x6d, 0xa3, 0x38, 0x3b, 0xed, 0x8a, 0xec, 0xeb, 0x0b, 0xbd, 0x95, 0xda, + 0x05, 0x44, 0x4c, 0xcd, 0xcc, 0x01, 0x2e, 0xf0, 0x49, 0x8a, 0xd3, 0xfc, 0x40, 0xd1, 0x1d, 0x18, 0x1c, 0xc5, 0x0a, + 0x20, 0x1c, 0x2d, 0x8a, 0x90, 0xe5, 0xdb, 0x31, 0xf0, 0x87, 0x40, 0xf9, 0xd4, 0x18, 0xe1, 0xbe, 0x80, 0x96, 0x3c, + 0x4e, 0x69, 0xcd, 0x25, 0x64, 0x4a, 0x9f, 0xd0, 0x8c, 0xe6, 0x1b, 0xd0, 0x5d, 0x04, 0xbd, 0xbf, 0x91, 0xaf, 0x40, + 0x2b, 0x03, 0x28, 0x92, 0x9e, 0xa9, 0x4e, 0xd4, 0x28, 0x40, 0xf1, 0x54, 0x26, 0x44, 0x6e, 0x56, 0xb3, 0x20, 0x95, + 0xc6, 0x2e, 0x8d, 0x70, 0xc5, 0x72, 0x53, 0xe2, 0x38, 0x4e, 0x02, 0x46, 0x9c, 0xd6, 0xed, 0xab, 0x49, 0x24, 0x6b, + 0x93, 0x48, 0x5c, 0xc3, 0xd0, 0x42, 0x15, 0x2d, 0x1b, 0xcd, 0x3d, 0xce, 0x91, 0x59, 0x0b, 0xf4, 0x55, 0x17, 0x18, + 0x34, 0x2a, 0xf9, 0x6d, 0x4c, 0x38, 0x4e, 0x95, 0x95, 0xa3, 0x48, 0x0d, 0x38, 0x46, 0xd5, 0x24, 0x43, 0x72, 0x6f, + 0xd4, 0x4c, 0xde, 0x0c, 0xa7, 0x68, 0x45, 0xb9, 0x2f, 0x0a, 0x85, 0x24, 0x8a, 0xd4, 0xe2, 0xd4, 0xb4, 0x62, 0x03, + 0x2d, 0x38, 0x23, 0x89, 0xd4, 0x84, 0xa5, 0xe2, 0xb3, 0x8a, 0x9c, 0xb2, 0xdf, 0x1d, 0x42, 0xb2, 0x0a, 0x37, 0x89, + 0xbb, 0x41, 0xb7, 0xca, 0x10, 0x8e, 0xb4, 0x52, 0x9a, 0x56, 0x13, 0x27, 0xc4, 0xd6, 0x3e, 0x09, 0x7b, 0xb0, 0xa8, + 0xd9, 0x85, 0x9e, 0x51, 0xad, 0xf0, 0x80, 0xa7, 0xa6, 0x9b, 0xf0, 0xbd, 0x89, 0x68, 0x6a, 0xfd, 0x18, 0x18, 0x4f, + 0x6b, 0x18, 0x37, 0x50, 0x9b, 0x49, 0xde, 0x95, 0x0d, 0x49, 0x54, 0x6f, 0xec, 0x50, 0x9c, 0xca, 0x85, 0x58, 0xc3, + 0xe2, 0xaa, 0xf2, 0x29, 0x88, 0x10, 0xcc, 0xd8, 0x04, 0xd4, 0x3b, 0x53, 0x42, 0x38, 0x00, 0x3c, 0x5b, 0x2e, 0xd7, + 0xc8, 0x6e, 0xa3, 0x0e, 0x8a, 0xdc, 0xca, 0x32, 0x5c, 0x2e, 0x9f, 0x71, 0xe4, 0x28, 0xed, 0x17, 0x53, 0x34, 0xd0, + 0x3c, 0xf7, 0xe4, 0x25, 0xd4, 0x12, 0xca, 0x68, 0x55, 0x52, 0x9a, 0x0d, 0x75, 0xaa, 0xad, 0x2f, 0x14, 0x37, 0x18, + 0xf7, 0xe9, 0x1a, 0xff, 0x12, 0x85, 0x4a, 0x50, 0x57, 0x53, 0x3e, 0x55, 0x5d, 0x33, 0x84, 0x90, 0x97, 0x08, 0x4b, + 0x66, 0x67, 0x93, 0x71, 0xb9, 0xb7, 0x97, 0x18, 0x1d, 0x5d, 0x94, 0x8c, 0xe2, 0x67, 0x07, 0x84, 0x72, 0x7e, 0x9b, + 0x08, 0xed, 0xe5, 0x67, 0x2d, 0x86, 0xd6, 0x4c, 0xd3, 0x76, 0x0f, 0x6c, 0x72, 0x7f, 0xee, 0x33, 0x6e, 0x95, 0xbd, + 0x48, 0x9b, 0xdc, 0xa1, 0x68, 0xa1, 0x94, 0x0d, 0x37, 0xa3, 0xa0, 0x3e, 0x02, 0x57, 0xd0, 0x4a, 0xb4, 0x24, 0xfc, + 0x20, 0xa2, 0xe0, 0x0f, 0xd6, 0x7a, 0x44, 0x69, 0x1b, 0xee, 0x28, 0x39, 0xa2, 0x3a, 0xde, 0x0c, 0x7b, 0xb1, 0xda, + 0xbc, 0x66, 0x0b, 0x4c, 0x69, 0x36, 0x4a, 0xb3, 0x89, 0x7e, 0x57, 0xac, 0x3c, 0x2b, 0xde, 0xc8, 0x46, 0xce, 0xc6, + 0xbe, 0x95, 0x05, 0xd0, 0x5b, 0x31, 0xbc, 0x2b, 0x93, 0xbd, 0x26, 0x4c, 0x4b, 0xf9, 0x2b, 0xdd, 0x82, 0x9a, 0x32, + 0x13, 0xd3, 0xc4, 0x57, 0x3e, 0xd5, 0x9e, 0x74, 0x9b, 0xec, 0x74, 0x7a, 0xa5, 0xdd, 0xa7, 0xa9, 0xa1, 0x27, 0xdd, + 0x1b, 0x4a, 0xa8, 0xa6, 0xb3, 0x38, 0x54, 0xc0, 0x32, 0x84, 0xa9, 0xa2, 0xa3, 0x39, 0x8b, 0xe3, 0xaa, 0xf4, 0x73, + 0x38, 0x7b, 0xa2, 0x38, 0x7b, 0xa6, 0x39, 0x3b, 0xb0, 0x0a, 0xe0, 0xec, 0xb2, 0xbb, 0xaa, 0x79, 0xb6, 0xb6, 0x3d, + 0x33, 0xc9, 0xd3, 0x13, 0x61, 0x4b, 0xc3, 0x78, 0x33, 0x0d, 0x01, 0x2a, 0x75, 0xaf, 0x8f, 0x8e, 0x72, 0xc5, 0x80, + 0x11, 0x28, 0x3d, 0x99, 0xd4, 0x74, 0x53, 0x7c, 0x74, 0x10, 0x4e, 0x0a, 0x5a, 0x52, 0xf6, 0xc9, 0x33, 0xf0, 0xd5, + 0x19, 0xd3, 0x01, 0x31, 0x26, 0x8a, 0x3f, 0x4b, 0x8d, 0xd2, 0xb3, 0x63, 0x6a, 0x76, 0x89, 0x9e, 0x1d, 0xf0, 0xfa, + 0x6a, 0x76, 0xe1, 0xdd, 0xdc, 0x5e, 0x4c, 0x8f, 0x95, 0xd3, 0xab, 0xd6, 0x7b, 0xb9, 0x74, 0x56, 0x4a, 0xc0, 0x8d, + 0xaf, 0x8c, 0x94, 0xac, 0xec, 0x1d, 0x78, 0x80, 0x89, 0x19, 0x28, 0x28, 0xe4, 0xa4, 0x4b, 0x21, 0xf7, 0xf2, 0x53, + 0x4e, 0x1e, 0xe1, 0xad, 0x97, 0xed, 0x4f, 0xd3, 0xc9, 0x14, 0xf4, 0xb1, 0x15, 0x92, 0x1e, 0x53, 0x35, 0x60, 0xf5, + 0xbe, 0xd8, 0x50, 0x56, 0x6b, 0x23, 0xf6, 0x63, 0x8d, 0x9a, 0x4a, 0x9b, 0x79, 0xa7, 0x5d, 0xcc, 0xca, 0xa2, 0x92, + 0x71, 0x6c, 0x72, 0xac, 0x9c, 0xae, 0xba, 0x65, 0xf4, 0x8b, 0x37, 0x0e, 0x93, 0x7c, 0x98, 0x01, 0xaf, 0x33, 0xd8, + 0x8f, 0x26, 0x77, 0x73, 0xfd, 0x8b, 0x0a, 0x39, 0x8b, 0x62, 0x05, 0x7d, 0x8b, 0xa2, 0x78, 0xa6, 0xec, 0x6c, 0xfc, + 0x6c, 0xbb, 0x41, 0x5c, 0xbd, 0x53, 0xf6, 0xe2, 0xf9, 0x10, 0x3f, 0x5b, 0xd7, 0x1e, 0xc9, 0x62, 0x92, 0x86, 0xd4, + 0xb3, 0xd3, 0x29, 0x4d, 0xec, 0x02, 0xbc, 0xab, 0x6a, 0xf1, 0x67, 0xdc, 0x59, 0xbc, 0xab, 0xbb, 0x59, 0xbd, 0x67, + 0x05, 0xb8, 0xc0, 0x7e, 0x5c, 0x77, 0xc0, 0x7e, 0x4b, 0xb3, 0x5c, 0xe8, 0xa2, 0xa5, 0x5a, 0xfb, 0x63, 0x25, 0x98, + 0x7e, 0xf4, 0xb6, 0xd6, 0xaf, 0xac, 0x10, 0xbb, 0xe3, 0x3e, 0x74, 0xf7, 0x6d, 0x24, 0xdc, 0xc3, 0xdf, 0xa8, 0x1d, + 0xff, 0x8b, 0x76, 0x0f, 0x9f, 0x91, 0x5f, 0xea, 0xde, 0xe1, 0x29, 0x27, 0x67, 0x83, 0x33, 0x6d, 0x34, 0xa7, 0x31, + 0x0b, 0x6e, 0x1d, 0x3b, 0x66, 0xbc, 0x09, 0x21, 0x38, 0x1b, 0x2f, 0xe4, 0x0b, 0xf0, 0x2b, 0x0a, 0xb7, 0x76, 0xa1, + 0xcd, 0x3d, 0xcc, 0x38, 0xb1, 0x77, 0x63, 0xc6, 0x77, 0x6d, 0xbc, 0x4b, 0x2e, 0xe1, 0xc7, 0xee, 0xc2, 0x79, 0xe5, + 0xf3, 0xc8, 0xcd, 0xfc, 0x24, 0x4c, 0x27, 0x0e, 0x6a, 0xd8, 0x36, 0x72, 0x73, 0x61, 0x72, 0x3c, 0x46, 0xc5, 0xee, + 0x25, 0x3e, 0xe3, 0xc4, 0x1e, 0xd8, 0x8d, 0x5d, 0xfc, 0x8a, 0x93, 0xcb, 0xe3, 0xdd, 0xc5, 0x19, 0x2f, 0xfa, 0x97, + 0xf8, 0xa4, 0xf4, 0xdc, 0xe3, 0xd7, 0xc4, 0x41, 0xa4, 0x7f, 0xa2, 0xa0, 0x39, 0x4d, 0x27, 0xd2, 0x83, 0x6f, 0x23, + 0xfc, 0x0e, 0xe2, 0x2b, 0x79, 0xc5, 0x6e, 0x54, 0x88, 0x65, 0x87, 0xd8, 0xa9, 0xf0, 0x12, 0xd8, 0x7b, 0x7b, 0x46, + 0x59, 0xa9, 0x2c, 0xe0, 0x53, 0x4e, 0x6a, 0x36, 0x39, 0x7e, 0x29, 0x22, 0x35, 0xa7, 0xdc, 0xc9, 0x91, 0xee, 0xc6, + 0xd1, 0xee, 0x68, 0xb5, 0x37, 0xf3, 0x73, 0xe9, 0x64, 0x70, 0x19, 0xa7, 0x99, 0xcf, 0xd3, 0x6c, 0x88, 0x4c, 0x05, + 0x04, 0xff, 0x8d, 0x5c, 0x9e, 0x5b, 0xff, 0xe9, 0x8b, 0x9f, 0x46, 0x3f, 0x65, 0xc3, 0x4b, 0xfc, 0x81, 0xb4, 0x8e, + 0x9d, 0x81, 0xe7, 0xec, 0x34, 0x9b, 0xcb, 0x9f, 0x5a, 0xe7, 0x7f, 0xf7, 0x9b, 0xbf, 0x9e, 0x34, 0x7f, 0x1c, 0xa2, + 0xa5, 0xf3, 0x53, 0x6b, 0x70, 0xae, 0x9e, 0xce, 0xff, 0xde, 0xff, 0x29, 0x1f, 0x7e, 0x25, 0x0b, 0x77, 0x11, 0x6a, + 0x8d, 0xf1, 0x98, 0x93, 0x56, 0xb3, 0xd9, 0x6f, 0x8d, 0xf1, 0x84, 0x93, 0x16, 0xfc, 0x3b, 0x27, 0xef, 0xe8, 0xf8, + 0xd9, 0xcd, 0xd4, 0xb9, 0xec, 0x2f, 0x77, 0x17, 0x7f, 0x2b, 0xa0, 0xd7, 0xf3, 0xbf, 0xff, 0xf4, 0x53, 0x6e, 0x3f, + 0xe8, 0x93, 0xd6, 0xb0, 0x81, 0x1c, 0x28, 0xfd, 0x8a, 0x88, 0xbf, 0xce, 0xc0, 0x3b, 0xff, 0xbb, 0x82, 0xc2, 0x7e, + 0xf0, 0xd3, 0xe5, 0x71, 0x9f, 0x0c, 0x97, 0x8e, 0xbd, 0x7c, 0x80, 0x96, 0x08, 0x2d, 0x77, 0xd1, 0x25, 0xb6, 0xc7, + 0x36, 0xc2, 0x17, 0x9c, 0xb4, 0x1e, 0xb4, 0xc6, 0x78, 0xc4, 0x49, 0xcb, 0x6e, 0x8d, 0xf1, 0x1b, 0x4e, 0x5a, 0x7f, + 0x77, 0x06, 0x9e, 0x74, 0xb3, 0x2d, 0x85, 0x87, 0x63, 0x09, 0x41, 0x0e, 0x3f, 0xa3, 0xfe, 0x92, 0x33, 0x1e, 0x53, + 0xb4, 0xdb, 0x62, 0xf8, 0xa3, 0x40, 0x93, 0xc3, 0xc1, 0x0f, 0x03, 0xe6, 0x9d, 0xb3, 0xb8, 0x80, 0xc5, 0x06, 0x9a, + 0xd9, 0xf5, 0x20, 0xba, 0x03, 0xae, 0x80, 0xdc, 0xe3, 0xf8, 0xda, 0x8f, 0x67, 0x34, 0xf7, 0x68, 0x81, 0x70, 0x4c, + 0x3e, 0x72, 0xa7, 0x83, 0xf0, 0x0b, 0x0e, 0x3f, 0xba, 0x08, 0x9f, 0xaa, 0x40, 0x26, 0xec, 0x64, 0x49, 0x54, 0x49, + 0x2a, 0x55, 0x16, 0x1b, 0xe1, 0xf1, 0x86, 0x97, 0x3c, 0x02, 0x07, 0x03, 0xc2, 0xd7, 0xb5, 0xb0, 0x27, 0xbe, 0x21, + 0x9a, 0x24, 0xde, 0x67, 0x94, 0x7e, 0xe7, 0xc7, 0x1f, 0x69, 0xe6, 0x9c, 0xe0, 0x4e, 0xf7, 0x31, 0x16, 0x7e, 0xe8, + 0x9d, 0x0e, 0xea, 0x95, 0x31, 0xab, 0xb7, 0x5c, 0x86, 0x0a, 0x40, 0xca, 0xd6, 0xdd, 0x31, 0xb0, 0xe2, 0x3b, 0xeb, + 0x3e, 0xab, 0xcc, 0x9f, 0xdb, 0xa8, 0x1e, 0x1f, 0x65, 0xc9, 0xb5, 0x1f, 0xb3, 0xd0, 0xe2, 0x74, 0x32, 0x8d, 0x7d, + 0x4e, 0x2d, 0x35, 0x5f, 0xcb, 0x87, 0x8e, 0xec, 0x52, 0x67, 0x98, 0x1a, 0x36, 0xe7, 0x54, 0x07, 0x9e, 0x60, 0xaf, + 0x38, 0x10, 0xa5, 0x52, 0x7a, 0xc7, 0xd3, 0x2a, 0x08, 0xb6, 0x1a, 0xe7, 0x6b, 0x76, 0xc0, 0x17, 0x36, 0x14, 0xf2, + 0x39, 0xc1, 0x19, 0x01, 0x29, 0xda, 0x1d, 0xd8, 0xc7, 0xf9, 0xf5, 0xb8, 0x6f, 0x43, 0x8c, 0x26, 0x25, 0x1f, 0x84, + 0x6b, 0x08, 0x2a, 0x44, 0xa4, 0xdd, 0x8b, 0x8e, 0x69, 0x2f, 0x6a, 0x34, 0xb4, 0x16, 0xed, 0x93, 0xfc, 0x3c, 0x92, + 0xcd, 0x03, 0x1c, 0xe2, 0x19, 0x69, 0x76, 0xf0, 0x94, 0xb4, 0x45, 0x93, 0xde, 0xf4, 0xd8, 0x57, 0xc3, 0xec, 0xed, + 0x39, 0xa9, 0x1b, 0xfb, 0x39, 0x7f, 0x01, 0xf6, 0x3e, 0x99, 0xe2, 0x90, 0xa4, 0x2e, 0xbd, 0xa1, 0x81, 0xe3, 0x23, + 0x1c, 0x2a, 0x4e, 0x83, 0x7a, 0x68, 0x4a, 0x8c, 0x6a, 0x60, 0x46, 0x90, 0x0f, 0x83, 0xf0, 0xbc, 0x33, 0x24, 0x84, + 0xd8, 0x3b, 0xcd, 0xa6, 0x3d, 0x48, 0xc9, 0x98, 0x7b, 0x50, 0x62, 0x28, 0xcb, 0x64, 0x02, 0x45, 0x5d, 0xa3, 0xc8, + 0x79, 0xc3, 0x5d, 0x4e, 0x73, 0xee, 0x40, 0x31, 0x78, 0x00, 0x12, 0x4d, 0xd8, 0xf6, 0x71, 0xcb, 0x6e, 0x40, 0xa9, + 0x20, 0x4e, 0x84, 0x53, 0x32, 0x47, 0x5e, 0x78, 0xbe, 0x3f, 0x34, 0x05, 0x80, 0x28, 0x84, 0xc1, 0xe7, 0x83, 0xf0, + 0xbc, 0x2d, 0x06, 0xef, 0xdb, 0x03, 0x27, 0x25, 0xc9, 0x8e, 0x8a, 0xde, 0x78, 0x1f, 0xc4, 0x54, 0x91, 0xa7, 0x80, + 0x53, 0xe3, 0xce, 0x48, 0xb3, 0xeb, 0x39, 0x33, 0x73, 0x12, 0x4d, 0x18, 0x4c, 0x61, 0x01, 0x07, 0x04, 0xea, 0xe3, + 0x94, 0xc0, 0x88, 0x55, 0xb3, 0xb9, 0xa7, 0x9e, 0x1f, 0xd8, 0x0f, 0x06, 0x23, 0xee, 0x5d, 0x70, 0x39, 0xfc, 0x88, + 0x2f, 0x97, 0xf0, 0xef, 0x05, 0x1f, 0xa4, 0x64, 0x2e, 0x8a, 0xc6, 0xaa, 0x68, 0x02, 0x45, 0x1f, 0x3c, 0x00, 0x15, + 0x27, 0xa5, 0x96, 0x25, 0xd7, 0x64, 0x42, 0x04, 0xec, 0x7b, 0x7b, 0xf9, 0x79, 0xd4, 0xe8, 0x0c, 0xc1, 0xc9, 0x9f, + 0xf1, 0xfc, 0x3b, 0xc6, 0x23, 0xc7, 0x6e, 0xf5, 0x6d, 0x34, 0xb0, 0x2d, 0x58, 0xda, 0x5e, 0xd6, 0x20, 0x12, 0xc3, + 0x7e, 0xe3, 0x15, 0xf7, 0x66, 0x7d, 0xd2, 0x1e, 0x38, 0x4c, 0xb9, 0xf4, 0x10, 0xf6, 0x15, 0xe3, 0x6c, 0xe3, 0x19, + 0x6a, 0x30, 0xde, 0xd0, 0xcf, 0x33, 0xd4, 0xd8, 0x6d, 0x4c, 0x90, 0xe7, 0x37, 0x76, 0x1b, 0xce, 0x8c, 0x10, 0xd2, + 0xec, 0x96, 0xcd, 0xb4, 0xf8, 0x8b, 0x90, 0x37, 0xd1, 0xfe, 0xce, 0x73, 0xb1, 0x1d, 0xb2, 0x86, 0x03, 0x2e, 0x96, + 0xe5, 0xd2, 0x3e, 0x1e, 0xf4, 0x6d, 0xd4, 0x70, 0x34, 0xa1, 0xb5, 0x34, 0xa5, 0x21, 0x84, 0xd9, 0xb0, 0x50, 0xf1, + 0xa4, 0x27, 0xb5, 0xd8, 0xd1, 0xa2, 0xda, 0xec, 0x06, 0x0f, 0xa0, 0x45, 0x69, 0xc8, 0x48, 0x85, 0x75, 0x0a, 0xd3, + 0xd4, 0xc4, 0x9c, 0x91, 0x36, 0x4e, 0x89, 0x76, 0x5f, 0x47, 0x84, 0x57, 0x04, 0xef, 0x93, 0xaa, 0x3a, 0x3e, 0x0f, + 0x70, 0x38, 0x24, 0x4f, 0xa5, 0x41, 0xd2, 0xd3, 0xce, 0x71, 0x1a, 0x93, 0x27, 0x2b, 0x51, 0xdc, 0x00, 0x02, 0x2c, + 0x37, 0x6e, 0x30, 0xcb, 0x32, 0x9a, 0xf0, 0xd7, 0x69, 0xa8, 0xf4, 0x34, 0x1a, 0x83, 0xa9, 0x04, 0xe1, 0x59, 0x0c, + 0x4a, 0x5a, 0x57, 0xef, 0x8c, 0xd9, 0xda, 0xeb, 0x29, 0x99, 0x49, 0xfd, 0x49, 0x04, 0x6d, 0x7b, 0x53, 0x65, 0x19, + 0x3b, 0x08, 0xcf, 0x54, 0x34, 0xd7, 0x71, 0x5d, 0x77, 0xea, 0x06, 0xf0, 0x1a, 0x06, 0xc8, 0x51, 0x21, 0xf6, 0x91, + 0x93, 0x90, 0x1b, 0x37, 0xa1, 0x37, 0x62, 0x54, 0x07, 0x55, 0x92, 0x59, 0x6f, 0xaf, 0xe3, 0xa8, 0x27, 0xd8, 0x4d, + 0xe2, 0x26, 0x69, 0x48, 0x01, 0x3d, 0x10, 0xbf, 0x57, 0x45, 0x91, 0x9f, 0x9b, 0x41, 0xaa, 0x0a, 0xbe, 0x73, 0xd3, + 0x7f, 0x3d, 0x05, 0xa7, 0xaf, 0xb0, 0x88, 0xcb, 0xca, 0xd2, 0x13, 0x8e, 0x10, 0x1b, 0x39, 0x53, 0x17, 0x82, 0x7b, + 0x82, 0x84, 0x18, 0xd8, 0x72, 0x53, 0x93, 0xa8, 0x76, 0xcb, 0x3e, 0x27, 0x24, 0x3c, 0x4f, 0x1b, 0x0d, 0xe1, 0x88, + 0x9e, 0x49, 0x92, 0x98, 0x22, 0x3c, 0x29, 0xf7, 0x96, 0xae, 0xf7, 0x96, 0xd4, 0x47, 0x72, 0x26, 0x75, 0x87, 0x6e, + 0x83, 0x71, 0x24, 0x7c, 0x85, 0xdc, 0xd9, 0x45, 0xf8, 0x82, 0xb4, 0x9c, 0x73, 0x77, 0xf0, 0xe7, 0x21, 0x1a, 0x38, + 0xee, 0x57, 0xa8, 0x25, 0x19, 0xc7, 0x04, 0xf5, 0x7c, 0x39, 0xc4, 0x42, 0x44, 0x31, 0x3b, 0x58, 0xf8, 0x12, 0xbd, + 0x0c, 0x27, 0xfe, 0x84, 0x7a, 0x17, 0xb0, 0xc7, 0x35, 0xdd, 0xbc, 0xc5, 0x40, 0x47, 0xde, 0x85, 0xe2, 0x24, 0xae, + 0x3d, 0xf8, 0x85, 0x97, 0x4f, 0x03, 0x7b, 0xf0, 0x75, 0xf5, 0xf4, 0x67, 0x7b, 0xf0, 0x2d, 0xf7, 0xbe, 0x2d, 0x94, + 0xbb, 0xbb, 0x36, 0xc4, 0x43, 0x3d, 0x44, 0x21, 0x17, 0xc6, 0xc0, 0xdc, 0x0c, 0x25, 0x6b, 0x8e, 0x8e, 0x29, 0x2a, + 0xd8, 0xa8, 0x64, 0x45, 0x89, 0xcb, 0xfd, 0x31, 0xa0, 0xd4, 0x58, 0x81, 0xc4, 0x8c, 0xee, 0x57, 0x13, 0x06, 0x42, + 0xd1, 0xd4, 0x0a, 0xa8, 0x9c, 0xf6, 0xdb, 0x68, 0x51, 0xab, 0x2b, 0x34, 0xa6, 0x7a, 0x34, 0xbd, 0xe4, 0xd2, 0x13, + 0xd2, 0xee, 0x4d, 0x8e, 0xa7, 0xbd, 0x49, 0xa3, 0x81, 0x12, 0x4d, 0x58, 0xb3, 0xf3, 0xc9, 0x10, 0xbf, 0x06, 0xaf, + 0x9e, 0x49, 0x49, 0xb8, 0x36, 0xbd, 0xae, 0x9a, 0x5e, 0xa3, 0x91, 0x15, 0xa8, 0x67, 0x34, 0x9d, 0xca, 0xa6, 0x45, + 0x21, 0x71, 0xb2, 0x4a, 0x68, 0x47, 0x48, 0x94, 0x40, 0x4a, 0x14, 0x21, 0xe4, 0x8c, 0xa3, 0x8d, 0xbd, 0x42, 0x9f, + 0xd0, 0x5c, 0xec, 0x58, 0x60, 0x9e, 0x52, 0x46, 0x38, 0x80, 0x05, 0x68, 0x5a, 0xba, 0x82, 0x77, 0xf1, 0xac, 0xd1, + 0x11, 0x44, 0xde, 0xec, 0xf4, 0xea, 0x7d, 0x3d, 0xaa, 0xfa, 0xc2, 0xb3, 0x06, 0xd9, 0x2d, 0xb1, 0x54, 0x64, 0x8d, + 0x46, 0x51, 0x8f, 0x77, 0xea, 0x7d, 0x5b, 0x8b, 0x40, 0x9c, 0xac, 0xa6, 0x66, 0x68, 0xf9, 0x5a, 0x49, 0x54, 0xe6, + 0xb2, 0x24, 0xa1, 0x19, 0xc8, 0x50, 0xc2, 0x31, 0x2b, 0x8a, 0x52, 0xae, 0xbf, 0x01, 0x21, 0x8a, 0x29, 0xc9, 0x81, + 0xef, 0x08, 0xb3, 0x0b, 0x67, 0x38, 0xc5, 0x91, 0xe0, 0x1a, 0x84, 0x90, 0x53, 0x9d, 0xd4, 0xc2, 0x05, 0x07, 0xf2, + 0x09, 0x33, 0x24, 0x52, 0x42, 0xa8, 0x7b, 0xb1, 0x7b, 0x9a, 0xde, 0x69, 0x92, 0x9d, 0xb3, 0xa1, 0x27, 0xaa, 0xc5, + 0x8a, 0x6f, 0x05, 0xe4, 0x9d, 0xc3, 0x51, 0x19, 0x1e, 0x71, 0x05, 0xfb, 0x7b, 0xca, 0x32, 0x2a, 0x34, 0xf0, 0x5d, + 0x6d, 0xf6, 0xf9, 0x75, 0xf5, 0xd1, 0x37, 0x9d, 0x37, 0x80, 0xc8, 0x00, 0x7c, 0x3b, 0x19, 0x59, 0xab, 0x76, 0xb1, + 0x7b, 0xf2, 0x66, 0x93, 0x09, 0xbc, 0x5c, 0x2a, 0xe3, 0xd7, 0x07, 0xcd, 0x06, 0x07, 0x15, 0xa4, 0xbe, 0xfa, 0xe1, + 0x39, 0xbe, 0x50, 0x90, 0x02, 0x27, 0x07, 0x2a, 0xba, 0xd8, 0x3d, 0x79, 0xef, 0xe4, 0xc2, 0xb5, 0x84, 0xb0, 0x39, + 0x6d, 0x27, 0x25, 0x4e, 0x44, 0x28, 0x92, 0x73, 0x2f, 0x19, 0x57, 0x6a, 0x88, 0x6f, 0x2f, 0x12, 0x2f, 0xc1, 0x7e, + 0x38, 0x67, 0x43, 0xe2, 0x2b, 0x0c, 0x10, 0x1f, 0x61, 0xbf, 0x66, 0x96, 0x11, 0x58, 0x00, 0x31, 0xd6, 0x19, 0xac, + 0x84, 0x2b, 0x15, 0x3f, 0x84, 0x7d, 0x31, 0x2a, 0x2f, 0xa4, 0xe8, 0xf8, 0x79, 0x2d, 0x37, 0xad, 0xb2, 0x46, 0xbf, + 0x05, 0xcb, 0x49, 0x3f, 0xbc, 0x56, 0x5d, 0x97, 0x05, 0x4f, 0x75, 0x12, 0xd9, 0xc5, 0xee, 0xc9, 0x2b, 0x95, 0x47, + 0x36, 0xf5, 0x35, 0xb7, 0x5f, 0xb3, 0x30, 0x4f, 0x5e, 0xb9, 0xd5, 0x5b, 0x51, 0xf9, 0x62, 0xf7, 0xe4, 0xc3, 0xa6, + 0x6a, 0x50, 0x5e, 0xcc, 0x2a, 0x13, 0x5f, 0xc0, 0xb7, 0xa0, 0xb1, 0xb7, 0x50, 0xa2, 0xc1, 0x63, 0x05, 0x16, 0xe2, + 0xc8, 0x4b, 0x8a, 0xd2, 0x33, 0xf2, 0x14, 0x67, 0x44, 0xc4, 0x81, 0xea, 0xab, 0xa6, 0x94, 0x3c, 0x96, 0x26, 0x67, + 0x41, 0x3a, 0xa5, 0x5b, 0x82, 0x43, 0x27, 0xc8, 0x65, 0x13, 0x48, 0xa0, 0x11, 0xa0, 0x33, 0xbc, 0xd3, 0x46, 0xbd, + 0xba, 0xf0, 0xca, 0x04, 0x91, 0xa6, 0x35, 0xc9, 0x82, 0x23, 0xd2, 0xc6, 0x3e, 0x69, 0xe3, 0x80, 0x24, 0xe7, 0x6d, + 0x29, 0x1e, 0x7a, 0x41, 0xd9, 0xaf, 0x14, 0x32, 0x90, 0x1b, 0x16, 0xc8, 0xdd, 0x2a, 0xc5, 0x6f, 0xd8, 0x0b, 0x84, + 0xeb, 0x51, 0x48, 0xf4, 0x50, 0x1a, 0xad, 0x4e, 0x8a, 0x53, 0xd1, 0xf1, 0x19, 0xbb, 0x8a, 0x21, 0xbb, 0x04, 0x66, + 0x85, 0x39, 0xf2, 0xca, 0xaa, 0x1d, 0x55, 0x35, 0x70, 0xc5, 0x3a, 0xa5, 0x38, 0x70, 0x81, 0x71, 0xe3, 0x40, 0x25, + 0xe3, 0xe4, 0xeb, 0x4d, 0x1e, 0xee, 0xed, 0x39, 0xb2, 0xd1, 0x77, 0xdc, 0x49, 0xf5, 0xfb, 0x2a, 0x74, 0xf7, 0xad, + 0xe4, 0x15, 0x21, 0x12, 0xf0, 0x37, 0x1a, 0xfe, 0xb0, 0x80, 0x38, 0xb4, 0x13, 0xd4, 0x31, 0xa8, 0x81, 0x17, 0x9a, + 0x5e, 0x7d, 0xfa, 0x8d, 0x46, 0x19, 0xa6, 0xad, 0x63, 0xeb, 0x04, 0x67, 0xc5, 0xb5, 0x53, 0xe6, 0xff, 0xb4, 0xd7, + 0xb2, 0xa6, 0x34, 0x08, 0x88, 0x99, 0x34, 0xcb, 0xf4, 0x64, 0x8c, 0x2d, 0xc1, 0xa0, 0xde, 0x0b, 0x95, 0xb8, 0x80, + 0x45, 0x8e, 0x95, 0xaa, 0xa4, 0xd9, 0x59, 0x17, 0x79, 0xba, 0x12, 0x84, 0xa5, 0xa0, 0x52, 0xa3, 0x50, 0xe4, 0xfd, + 0x6a, 0x3d, 0xf3, 0x12, 0x27, 0x48, 0xf9, 0xb8, 0x04, 0x14, 0x02, 0x59, 0xdd, 0x12, 0x29, 0xcf, 0xc9, 0x78, 0x3b, + 0xc9, 0x9f, 0x18, 0x24, 0xff, 0x84, 0x50, 0x83, 0xfc, 0xa5, 0x87, 0xc3, 0x4d, 0x95, 0x6b, 0x21, 0xd1, 0xaf, 0x4e, + 0xa7, 0x04, 0x7c, 0x68, 0x75, 0x8c, 0x26, 0x66, 0x5c, 0x71, 0x0b, 0x43, 0x31, 0x77, 0x88, 0xf0, 0x42, 0x62, 0x1d, + 0x04, 0x76, 0xaa, 0xa8, 0x1a, 0x0c, 0xbd, 0xc9, 0xa5, 0x67, 0x72, 0xc0, 0x93, 0x0f, 0x77, 0x07, 0x44, 0x4f, 0xa7, + 0xeb, 0x3b, 0xd7, 0xc8, 0x00, 0x85, 0x59, 0x1b, 0x1b, 0xb7, 0x9e, 0x0f, 0x0a, 0xe3, 0x97, 0x81, 0xec, 0x3a, 0xf3, + 0x59, 0xd9, 0x84, 0x5a, 0xfe, 0x01, 0xb4, 0x9d, 0x8e, 0xa8, 0x41, 0x8d, 0x6e, 0x81, 0x1f, 0xc9, 0x3c, 0x54, 0x3f, + 0xdb, 0xc2, 0x3e, 0x4e, 0x44, 0x05, 0x9a, 0x84, 0x9b, 0x5f, 0x3f, 0x29, 0x14, 0x99, 0x48, 0xd0, 0xd0, 0x02, 0xf8, + 0x9f, 0x24, 0x79, 0xa0, 0x1b, 0x21, 0x17, 0x00, 0x41, 0x63, 0x81, 0xa7, 0x0a, 0x61, 0xb6, 0x5d, 0x39, 0xdf, 0x9f, + 0xef, 0x10, 0x32, 0xae, 0x9c, 0x8f, 0xef, 0xaa, 0xec, 0x2b, 0x20, 0x0b, 0xe4, 0x81, 0xf1, 0x58, 0x16, 0xc8, 0xf8, + 0xe5, 0xa9, 0xae, 0x2e, 0x0c, 0x48, 0xb7, 0xd2, 0xb7, 0x8d, 0xd8, 0xa6, 0xf0, 0xca, 0xc9, 0xf7, 0x1a, 0x0d, 0x2b, + 0x6f, 0x77, 0xe1, 0xed, 0x4b, 0x2e, 0x60, 0x84, 0xe7, 0xf7, 0xa2, 0xb6, 0xee, 0xb7, 0xf8, 0xb8, 0x9a, 0xc2, 0xb2, + 0xb2, 0x28, 0x2e, 0x4b, 0x72, 0x9a, 0xf1, 0x27, 0x74, 0x94, 0x66, 0x10, 0xb2, 0x28, 0x71, 0x82, 0x8a, 0x5d, 0xc3, + 0x6d, 0x27, 0xe6, 0x67, 0xc4, 0x09, 0x56, 0x26, 0x28, 0x7e, 0x7d, 0x14, 0x51, 0xeb, 0x8b, 0xd5, 0x56, 0xe3, 0xbd, + 0xbd, 0x77, 0x15, 0x9a, 0x14, 0x94, 0x02, 0x0a, 0x83, 0x69, 0x49, 0x95, 0x46, 0x85, 0x72, 0x77, 0x9d, 0xd2, 0x05, + 0xa0, 0x19, 0x86, 0xc9, 0x7b, 0x9e, 0x13, 0x5e, 0x8c, 0x57, 0x59, 0xbc, 0x72, 0x4d, 0x30, 0xd3, 0x6c, 0x01, 0x0e, + 0x0f, 0x86, 0xb6, 0xf4, 0x15, 0x25, 0x55, 0x4a, 0x6c, 0x09, 0xc3, 0x29, 0x20, 0xcb, 0x49, 0xc0, 0x08, 0x31, 0x28, + 0x30, 0xd9, 0x64, 0x94, 0xbc, 0x05, 0xbd, 0x32, 0xc2, 0x89, 0x1b, 0x41, 0x12, 0x6c, 0x6d, 0xcb, 0x22, 0x84, 0x13, + 0x61, 0xd0, 0x18, 0xb9, 0x04, 0x27, 0xcf, 0x37, 0x79, 0x94, 0x35, 0x51, 0x53, 0x21, 0x75, 0xa0, 0x46, 0x86, 0xca, + 0x06, 0xee, 0xb5, 0xc3, 0x94, 0xe2, 0x56, 0xc6, 0xcd, 0xe8, 0xdc, 0xfa, 0x99, 0x3b, 0x32, 0x16, 0x05, 0x32, 0x23, + 0x75, 0x67, 0x4e, 0x6d, 0xe8, 0x5e, 0x2a, 0x9a, 0x61, 0x85, 0xb8, 0xc8, 0x44, 0x53, 0x2a, 0xe2, 0x7a, 0xa7, 0x15, + 0x2f, 0xbd, 0x96, 0x79, 0xd4, 0x5c, 0x73, 0xc1, 0x2a, 0x93, 0xc4, 0x98, 0xfe, 0xb5, 0x4c, 0x8d, 0x2e, 0x2b, 0x61, + 0x2a, 0xc0, 0x78, 0x22, 0xd6, 0x80, 0x16, 0x40, 0x5f, 0x8b, 0x53, 0x6e, 0xac, 0xa8, 0xf6, 0x61, 0x8b, 0x31, 0x0d, + 0xa9, 0xff, 0x0e, 0x72, 0x5d, 0x56, 0xf7, 0xfc, 0x73, 0x21, 0x0b, 0x19, 0x4e, 0x6a, 0x8c, 0x3d, 0x13, 0x8c, 0x1d, + 0x81, 0x9e, 0xa6, 0xd3, 0xbf, 0x07, 0x2a, 0xe5, 0x45, 0xe5, 0x2e, 0x3a, 0x8a, 0xc4, 0x5e, 0x97, 0xe1, 0x72, 0xe3, + 0xf7, 0xca, 0x6a, 0x78, 0x8c, 0x40, 0x1a, 0x10, 0x56, 0x9c, 0x3d, 0x43, 0x38, 0x69, 0x34, 0x7a, 0xc9, 0x31, 0xad, + 0x5c, 0x24, 0x15, 0x8c, 0x0c, 0x22, 0xba, 0x40, 0xf0, 0x35, 0x19, 0x9a, 0x20, 0x5c, 0xe6, 0xa1, 0x27, 0xe0, 0x6a, + 0x3f, 0x79, 0xe7, 0x98, 0x5c, 0xcd, 0xac, 0x5b, 0x06, 0x4d, 0x61, 0x3e, 0x4e, 0x15, 0x6f, 0x79, 0x7b, 0x77, 0x86, + 0x07, 0xc0, 0xbd, 0xd3, 0xc1, 0x90, 0x8d, 0x86, 0x7a, 0x5c, 0xb2, 0x84, 0x72, 0xf7, 0xf5, 0x50, 0x95, 0x98, 0x68, + 0x0e, 0xd6, 0xe3, 0x95, 0x29, 0xcb, 0x49, 0x52, 0x14, 0x39, 0xad, 0xe2, 0xfb, 0x2b, 0x19, 0x98, 0x42, 0xb8, 0xac, + 0x3b, 0xdb, 0x4f, 0xa7, 0x84, 0x63, 0x83, 0x50, 0xdf, 0x6e, 0x0b, 0x7d, 0x54, 0x60, 0xc2, 0xbe, 0x56, 0x42, 0xf1, + 0xdb, 0x4d, 0x42, 0x11, 0x67, 0x6a, 0xcb, 0x0b, 0x81, 0xd8, 0xb9, 0x87, 0x40, 0x54, 0x4e, 0x76, 0x2d, 0x13, 0x41, + 0x1d, 0xa9, 0xc9, 0xc4, 0xa4, 0x2e, 0x13, 0x33, 0xcc, 0xd4, 0x6a, 0xf4, 0xbb, 0xcb, 0x25, 0x3b, 0x6f, 0x83, 0x13, + 0xc9, 0xb6, 0xe1, 0x67, 0x47, 0xfe, 0x34, 0x38, 0xb1, 0x74, 0x02, 0x3b, 0xac, 0x34, 0x59, 0x90, 0x0b, 0x69, 0xce, + 0x8e, 0xc8, 0xca, 0x12, 0x34, 0xad, 0x28, 0x48, 0x11, 0x38, 0x61, 0x65, 0x94, 0x09, 0x20, 0x16, 0xb2, 0x42, 0x19, + 0x90, 0xce, 0xc6, 0xf4, 0x3f, 0x6d, 0x5e, 0x7e, 0x5a, 0x13, 0xad, 0xc9, 0x15, 0xa9, 0x3e, 0xd4, 0x12, 0x0e, 0x14, + 0x04, 0x4a, 0x3f, 0xdc, 0x11, 0x26, 0x68, 0x25, 0xca, 0x91, 0x29, 0x87, 0x70, 0x1b, 0x5c, 0x68, 0x3b, 0xef, 0x64, + 0x80, 0x77, 0x83, 0x34, 0xc1, 0xa9, 0x41, 0xd7, 0xcf, 0x09, 0xaf, 0xb1, 0x92, 0x88, 0x28, 0x4b, 0x09, 0x07, 0x82, + 0x4c, 0x39, 0xc9, 0xce, 0xdb, 0x43, 0x50, 0x40, 0x7b, 0xfe, 0x71, 0x56, 0x99, 0xc0, 0x7e, 0xa3, 0x81, 0x02, 0x3d, + 0x6a, 0x74, 0xce, 0x1a, 0xfe, 0x10, 0x53, 0xec, 0x4b, 0xc3, 0xe4, 0x74, 0x6f, 0xcf, 0x09, 0xaa, 0x71, 0xcf, 0xfd, + 0x21, 0xc2, 0xe9, 0x72, 0xe9, 0x08, 0xb0, 0x02, 0xb4, 0x5c, 0x06, 0x26, 0x58, 0xe2, 0x35, 0x34, 0x1b, 0x0f, 0x38, + 0x19, 0x0b, 0x01, 0x38, 0x06, 0x08, 0x1b, 0xc4, 0x09, 0x94, 0x73, 0x2f, 0x00, 0x67, 0x54, 0x23, 0x3b, 0xf7, 0x1b, + 0x9d, 0xa1, 0xc1, 0xb8, 0xce, 0xfd, 0x21, 0x09, 0x8a, 0x74, 0x6f, 0x6f, 0x27, 0x51, 0x22, 0xf2, 0x67, 0x10, 0x65, + 0x3f, 0x0b, 0xc9, 0x22, 0x3b, 0x34, 0x57, 0x63, 0xd5, 0x19, 0x50, 0x52, 0x94, 0x5a, 0x56, 0x5d, 0xaf, 0x96, 0x05, + 0x51, 0x56, 0xc2, 0x2a, 0x16, 0x3c, 0x00, 0xcb, 0xbe, 0x24, 0xf3, 0x5f, 0x78, 0x99, 0x66, 0xfd, 0xed, 0xc6, 0xe4, + 0x6a, 0xd7, 0x75, 0xfd, 0x6c, 0x2c, 0x22, 0x19, 0x3a, 0x63, 0x52, 0x10, 0xff, 0xbe, 0x02, 0xd3, 0x18, 0xf8, 0xbc, + 0x1c, 0x6b, 0x48, 0x24, 0xf8, 0x5a, 0xb5, 0xd1, 0x27, 0x4a, 0x7e, 0xdd, 0xe8, 0x65, 0x90, 0x90, 0x7c, 0xfd, 0x5b, + 0x21, 0x39, 0x50, 0x90, 0x48, 0xf2, 0x58, 0xc1, 0xd9, 0x16, 0x5c, 0xfc, 0xca, 0x57, 0x70, 0xb6, 0x1d, 0xb7, 0x25, + 0x43, 0xd8, 0x06, 0x9f, 0xc1, 0x1b, 0x24, 0xa0, 0x55, 0x81, 0x01, 0xe5, 0xe1, 0xaa, 0xee, 0x25, 0x59, 0x29, 0x08, + 0x53, 0x4e, 0x1c, 0x56, 0xdf, 0x00, 0x95, 0x36, 0x6a, 0x18, 0xbe, 0xcc, 0x1b, 0x23, 0xc3, 0x25, 0x50, 0x4f, 0x5d, + 0x01, 0x72, 0x52, 0xbe, 0x76, 0x48, 0x45, 0xd8, 0x91, 0x4a, 0x9c, 0x1b, 0xf8, 0x53, 0x3e, 0xcb, 0x40, 0x95, 0x4a, + 0xf4, 0x6f, 0x28, 0x86, 0xb3, 0x20, 0xa2, 0x0c, 0x7e, 0x40, 0xc1, 0xd4, 0xcf, 0x73, 0x76, 0x2d, 0xcb, 0xd4, 0x6f, + 0x9c, 0x12, 0x4d, 0xca, 0x89, 0xd4, 0x09, 0x33, 0xd4, 0xcb, 0x14, 0x9d, 0xd6, 0xd1, 0xf6, 0xec, 0x9a, 0x26, 0xfc, + 0x25, 0xcb, 0x39, 0x4d, 0x60, 0xfa, 0x15, 0xc5, 0xc1, 0x8c, 0x12, 0x04, 0x1b, 0xb6, 0xd6, 0xca, 0x0f, 0xc3, 0x3b, + 0x9b, 0xf0, 0xba, 0x0e, 0x14, 0xf9, 0x49, 0x18, 0xcb, 0x41, 0xcc, 0x84, 0x46, 0x9d, 0xc4, 0x59, 0xd6, 0x34, 0xf3, + 0x69, 0x2a, 0x65, 0x43, 0x70, 0x77, 0x87, 0x11, 0x2d, 0x09, 0xb4, 0xf4, 0xbc, 0x53, 0x6b, 0x81, 0x80, 0xf7, 0x96, + 0x45, 0x30, 0x67, 0x82, 0xb9, 0xc1, 0x51, 0xdd, 0x3a, 0x9c, 0x9a, 0x6e, 0xbe, 0xdb, 0x78, 0xb0, 0x6d, 0x93, 0x70, + 0x10, 0x74, 0xf2, 0x70, 0xbb, 0x65, 0xf5, 0x4a, 0x4b, 0x0e, 0x2d, 0x2d, 0xd8, 0x7d, 0x19, 0x33, 0x5a, 0x68, 0xf2, + 0x42, 0x7a, 0x2b, 0xde, 0x72, 0xf2, 0x0b, 0x9c, 0x1c, 0x7a, 0xce, 0x27, 0xf1, 0xca, 0x01, 0x99, 0xde, 0x6d, 0xa9, + 0xfd, 0xdf, 0x72, 0xe7, 0x09, 0x7e, 0x05, 0x61, 0xdd, 0x6f, 0xaa, 0xea, 0xeb, 0xe1, 0xdc, 0x6f, 0x2a, 0x04, 0x7d, + 0xe3, 0xad, 0xd5, 0x33, 0xc2, 0xb8, 0x5d, 0xf7, 0xc8, 0x6d, 0xdb, 0x5a, 0x5b, 0xfa, 0x51, 0x06, 0x91, 0x64, 0xaa, + 0xa5, 0xd8, 0x0f, 0xb8, 0x4a, 0x54, 0x83, 0x84, 0xb9, 0xba, 0x85, 0x44, 0x55, 0x8a, 0xa1, 0xd4, 0xe1, 0xb7, 0x2d, + 0x8f, 0x92, 0x31, 0x99, 0xb4, 0x33, 0xde, 0xfa, 0x19, 0xdf, 0x85, 0x5d, 0x96, 0xae, 0x9d, 0xc6, 0x8b, 0x08, 0x78, + 0xd0, 0xee, 0x37, 0x44, 0x75, 0x16, 0x60, 0x90, 0xc8, 0xc3, 0x40, 0x66, 0xff, 0x24, 0xd5, 0xba, 0x5b, 0xdd, 0xca, + 0x78, 0x0d, 0xf6, 0x3f, 0xc2, 0x91, 0x3e, 0x22, 0x47, 0x15, 0x07, 0xa6, 0xde, 0xa2, 0x28, 0x9d, 0x02, 0xa9, 0x54, + 0xde, 0x72, 0x84, 0xd3, 0x42, 0x84, 0xb7, 0xbf, 0xc7, 0x3f, 0x28, 0x96, 0x38, 0x2a, 0x39, 0xce, 0xb3, 0xfb, 0x72, + 0x44, 0x09, 0x7e, 0x19, 0xbd, 0x07, 0x3a, 0x16, 0x14, 0x5a, 0x68, 0x2a, 0x7a, 0x9a, 0xaa, 0x89, 0x6c, 0xcd, 0x4b, + 0xc5, 0xb4, 0xcc, 0xa8, 0x11, 0xc3, 0x6c, 0x48, 0xe4, 0xd4, 0x56, 0x36, 0x2f, 0x77, 0x55, 0x6d, 0x5c, 0xb4, 0x05, + 0x8b, 0x55, 0x60, 0x71, 0xb9, 0x74, 0xea, 0xa8, 0x26, 0xcc, 0x88, 0x63, 0x20, 0xcc, 0x8c, 0x84, 0x8a, 0x9a, 0x66, + 0x2d, 0xdb, 0x38, 0x68, 0x35, 0x9f, 0x48, 0xeb, 0xe6, 0x35, 0x38, 0x4c, 0x17, 0x82, 0x6c, 0x6e, 0xfa, 0x14, 0xb0, + 0x9c, 0x5d, 0x39, 0x90, 0x81, 0xa1, 0x1f, 0xcb, 0x5c, 0xd9, 0x2a, 0xa9, 0x75, 0x03, 0x7e, 0xd1, 0x1d, 0xd9, 0xb2, + 0x0a, 0x75, 0xeb, 0xef, 0x8d, 0x5c, 0xa3, 0xa7, 0xe9, 0xb6, 0x5c, 0xa3, 0x9a, 0xb6, 0xbb, 0xd3, 0x46, 0x77, 0xe7, + 0xa5, 0xca, 0xb1, 0x36, 0x57, 0xf9, 0x0d, 0xc3, 0x75, 0x80, 0x36, 0x25, 0x9a, 0x35, 0x57, 0x39, 0x2d, 0x8a, 0x51, + 0x79, 0x9a, 0x40, 0xa4, 0xee, 0x8c, 0x24, 0xfd, 0x2b, 0xab, 0x51, 0x1c, 0xca, 0x75, 0xbe, 0x27, 0xe3, 0x38, 0xbd, + 0xf2, 0xe3, 0xf7, 0x30, 0x5e, 0xf5, 0xf2, 0xf9, 0x6d, 0x98, 0xf9, 0x9c, 0x2a, 0xee, 0x52, 0xc1, 0xf0, 0xbd, 0x01, + 0xc3, 0xf7, 0x92, 0x4f, 0x57, 0xed, 0xf1, 0xe2, 0x65, 0xd9, 0x81, 0x37, 0x2a, 0x34, 0xcb, 0xd8, 0xe5, 0x9b, 0xc7, + 0x58, 0x65, 0x61, 0xbb, 0x25, 0x0b, 0xdb, 0xe5, 0xce, 0x6a, 0x57, 0x8e, 0xf3, 0xc3, 0xe6, 0x5e, 0xd6, 0x39, 0xdb, + 0x0f, 0xd5, 0xc6, 0xff, 0xc1, 0xbb, 0xb3, 0x8d, 0xc1, 0xe5, 0xf6, 0xdd, 0x7d, 0x91, 0xac, 0x22, 0x41, 0x7e, 0x09, + 0x49, 0x07, 0x9c, 0xf4, 0x8d, 0x43, 0x07, 0x95, 0x9c, 0xd2, 0x79, 0x40, 0x4e, 0x30, 0xcb, 0x79, 0x3a, 0x51, 0x7d, + 0xe6, 0xea, 0xa4, 0x91, 0x78, 0x09, 0xae, 0x68, 0x11, 0x6b, 0xf7, 0xea, 0x67, 0xb9, 0x16, 0x1f, 0x59, 0x12, 0x7a, + 0x09, 0x56, 0x52, 0x24, 0xf7, 0xb2, 0x82, 0xe8, 0x6c, 0xe3, 0xf5, 0x77, 0x78, 0xc4, 0x12, 0x96, 0x47, 0x34, 0x73, + 0x52, 0xb4, 0xd8, 0x36, 0x58, 0x0a, 0x01, 0x19, 0x39, 0x18, 0xfe, 0x6b, 0x75, 0xea, 0xcf, 0x85, 0xde, 0xc0, 0x0f, + 0x34, 0xa1, 0x3c, 0x4a, 0x43, 0x48, 0x4b, 0x71, 0xc3, 0xf2, 0x50, 0xd3, 0xde, 0xde, 0x8e, 0x63, 0x0b, 0xb7, 0x04, + 0x1c, 0x00, 0x37, 0xdf, 0xa0, 0xc1, 0x02, 0xce, 0xe7, 0x54, 0x43, 0x53, 0xb4, 0xa0, 0xab, 0x47, 0x59, 0xb8, 0xfb, + 0x91, 0xde, 0xe2, 0x1c, 0x15, 0x85, 0x27, 0xa1, 0xb6, 0x47, 0x8c, 0xc6, 0xa1, 0x8d, 0x3f, 0xd2, 0x5b, 0xaf, 0x3c, + 0x33, 0x2e, 0x8e, 0x38, 0x8b, 0x05, 0xb4, 0xd3, 0x79, 0x62, 0xe3, 0x6a, 0x10, 0x6f, 0x51, 0xe0, 0x34, 0x63, 0x63, + 0x20, 0xce, 0x6f, 0xe8, 0xad, 0x27, 0xfb, 0x63, 0xc6, 0x79, 0x3d, 0xb4, 0xd0, 0xa8, 0x77, 0x8d, 0x62, 0x73, 0x19, + 0x94, 0x41, 0x71, 0x2e, 0xda, 0x0e, 0x49, 0xad, 0x5e, 0x65, 0x1e, 0x22, 0x54, 0xdc, 0x77, 0x2a, 0xf8, 0x1b, 0x53, + 0xb4, 0xf1, 0x5a, 0xe6, 0xeb, 0x4a, 0x23, 0x0a, 0x0d, 0xaa, 0x4c, 0x0f, 0xc8, 0xe8, 0x58, 0x68, 0xf6, 0x2a, 0x9a, + 0x1b, 0x8e, 0xb0, 0x6f, 0xb8, 0xea, 0xd4, 0xfb, 0xab, 0x4c, 0x08, 0xa9, 0x22, 0x49, 0x2f, 0xaa, 0x76, 0xd6, 0xad, + 0x03, 0x78, 0x87, 0x84, 0x16, 0x5f, 0x9c, 0xc9, 0x2c, 0x74, 0xb6, 0xe8, 0xdf, 0x38, 0x71, 0x16, 0x7a, 0x0a, 0x5e, + 0x6e, 0x62, 0x91, 0x17, 0x40, 0x85, 0x8a, 0xbe, 0x64, 0x02, 0x20, 0x1b, 0x39, 0x6c, 0x4d, 0x6a, 0x66, 0x42, 0x6a, + 0xba, 0x06, 0xc6, 0xb7, 0x48, 0x49, 0x2a, 0x90, 0x21, 0x94, 0x48, 0x21, 0xf4, 0xd4, 0xe2, 0x2a, 0x12, 0x32, 0x17, + 0xb4, 0x3c, 0x41, 0x27, 0xd7, 0x3c, 0xab, 0x81, 0xe5, 0x88, 0x7e, 0x50, 0xe1, 0xc1, 0x94, 0xa8, 0xac, 0x50, 0x68, + 0x77, 0x4e, 0xae, 0xd3, 0x5b, 0x9d, 0xd4, 0xd5, 0xd3, 0x22, 0x1a, 0x25, 0x4e, 0x84, 0x16, 0xb9, 0x13, 0xe1, 0x0c, + 0xd2, 0x11, 0xd3, 0xa2, 0x84, 0x9f, 0x9a, 0xab, 0x51, 0x4b, 0x56, 0xde, 0x7c, 0xca, 0x0f, 0x94, 0x79, 0x0e, 0x29, + 0x9a, 0x38, 0xd7, 0x3c, 0x25, 0x77, 0xc4, 0x71, 0x3b, 0x63, 0xd9, 0xbe, 0x57, 0x09, 0x3a, 0x0a, 0xb0, 0xbf, 0x71, + 0x67, 0x61, 0xcc, 0xc2, 0x3c, 0xd1, 0xad, 0x4e, 0xfd, 0xa9, 0x60, 0x5f, 0x95, 0x43, 0xea, 0x24, 0x64, 0x45, 0xe2, + 0xdc, 0x9d, 0x6a, 0xf9, 0xcb, 0x8c, 0x66, 0xb7, 0x67, 0x14, 0x52, 0x9d, 0x53, 0x38, 0xf0, 0x5b, 0x2d, 0x43, 0x95, + 0xa7, 0x3e, 0xc8, 0x84, 0xb2, 0x52, 0xd4, 0xcf, 0x01, 0xae, 0x9e, 0x12, 0x2c, 0x44, 0xb4, 0xd1, 0x70, 0xc4, 0xc8, + 0xdd, 0x42, 0xb7, 0x9e, 0x9f, 0xa4, 0x3d, 0x06, 0xfe, 0xb5, 0x0a, 0xd3, 0x2a, 0x58, 0x80, 0x53, 0xf3, 0x4c, 0xea, + 0x79, 0x32, 0x5c, 0xf5, 0xca, 0x40, 0x11, 0x84, 0xef, 0xb2, 0xed, 0x53, 0xdd, 0x94, 0x34, 0xbb, 0x7d, 0xaa, 0xb5, + 0xa0, 0x9f, 0x48, 0xf8, 0xc1, 0x6a, 0x9c, 0xf2, 0x04, 0x33, 0x2b, 0x0a, 0x54, 0x00, 0x78, 0x7f, 0xe9, 0x39, 0xce, + 0x5f, 0x54, 0xca, 0xa0, 0x0b, 0xb1, 0xd8, 0xb3, 0x38, 0xd5, 0x4c, 0xbc, 0x1a, 0xff, 0x2f, 0x6b, 0xe3, 0xff, 0xc5, + 0x38, 0x75, 0x0a, 0xa6, 0xd1, 0x38, 0xa1, 0xa1, 0x66, 0x9d, 0x48, 0x12, 0xa0, 0xd0, 0xdb, 0x32, 0x4e, 0x3e, 0x5e, + 0x7a, 0xa0, 0x71, 0x2d, 0x46, 0x69, 0xc2, 0x9b, 0x23, 0x7f, 0xc2, 0xe2, 0x5b, 0x6f, 0xc6, 0x9a, 0x93, 0x34, 0x49, + 0xf3, 0xa9, 0x1f, 0x50, 0x9c, 0xdf, 0xe6, 0x9c, 0x4e, 0x9a, 0x33, 0x86, 0x9f, 0xd3, 0xf8, 0x9a, 0x72, 0x16, 0xf8, + 0xd8, 0x3e, 0xc9, 0x98, 0x1f, 0x5b, 0xaf, 0xfd, 0x2c, 0x4b, 0xe7, 0x36, 0x7e, 0x97, 0x5e, 0xa5, 0x3c, 0xc5, 0x6f, + 0x6e, 0x6e, 0xc7, 0x34, 0xc1, 0x1f, 0xae, 0x66, 0x09, 0x9f, 0xe1, 0xdc, 0x4f, 0xf2, 0x66, 0x4e, 0x33, 0x36, 0xea, + 0x05, 0x69, 0x9c, 0x66, 0x4d, 0xc8, 0xd8, 0x9e, 0x50, 0x2f, 0x66, 0xe3, 0x88, 0x5b, 0xa1, 0x9f, 0x7d, 0xec, 0x35, + 0x9b, 0xd3, 0x8c, 0x4d, 0xfc, 0xec, 0xb6, 0x29, 0x6a, 0x78, 0x5f, 0xb6, 0xf7, 0xfd, 0xc7, 0xa3, 0x83, 0x1e, 0xcf, + 0xfc, 0x24, 0x67, 0xb0, 0x4c, 0x9e, 0x1f, 0xc7, 0xd6, 0xfe, 0x61, 0x7b, 0x92, 0xef, 0xc8, 0x40, 0x9e, 0x9f, 0xf0, + 0xe2, 0x12, 0xbf, 0x07, 0xb8, 0xdd, 0x2b, 0x9e, 0xe0, 0xab, 0x19, 0xe7, 0x69, 0xb2, 0x08, 0x66, 0x59, 0x9e, 0x66, + 0xde, 0x34, 0x65, 0x09, 0xa7, 0x59, 0xef, 0x2a, 0xcd, 0x42, 0x9a, 0x35, 0x33, 0x3f, 0x64, 0xb3, 0xdc, 0x3b, 0x98, + 0xde, 0xf4, 0x40, 0xb3, 0x18, 0x67, 0xe9, 0x2c, 0x09, 0xd5, 0x58, 0x2c, 0x89, 0x68, 0xc6, 0xb8, 0xf9, 0x42, 0x5c, + 0x64, 0xe2, 0xc5, 0x2c, 0xa1, 0x7e, 0xd6, 0x1c, 0x43, 0x63, 0x30, 0x8b, 0xda, 0x21, 0x1d, 0xe3, 0x6c, 0x7c, 0xe5, + 0x3b, 0x9d, 0xee, 0x23, 0xac, 0xff, 0x77, 0x0f, 0x91, 0xd5, 0xde, 0x5c, 0xdc, 0x69, 0xb7, 0xff, 0x84, 0x7a, 0x2b, + 0xa3, 0x08, 0x80, 0xbc, 0xce, 0xf4, 0xc6, 0xca, 0x53, 0xc8, 0x68, 0xdb, 0xd4, 0xb2, 0x37, 0xf5, 0x43, 0xc8, 0x07, + 0xf6, 0xba, 0xd3, 0x9b, 0x02, 0x66, 0xe7, 0xc9, 0x14, 0x53, 0x35, 0x49, 0xf5, 0xb4, 0xf8, 0xad, 0x10, 0x1f, 0x6d, + 0x86, 0xb8, 0xab, 0x21, 0xae, 0xb0, 0xde, 0x0c, 0x67, 0x99, 0x88, 0xad, 0x7a, 0x9d, 0x5c, 0x02, 0x12, 0xa5, 0xd7, + 0x34, 0xd3, 0x70, 0x88, 0x87, 0xdf, 0x0c, 0x46, 0x77, 0x33, 0x18, 0x47, 0x9f, 0x02, 0x23, 0x4b, 0xc2, 0x45, 0x7d, + 0x5d, 0x3b, 0x19, 0x9d, 0xf4, 0x22, 0x0a, 0xf4, 0xe4, 0x75, 0xe1, 0xf7, 0x9c, 0x85, 0x3c, 0x92, 0x3f, 0x05, 0x39, + 0xcf, 0xe5, 0xbb, 0xc3, 0x76, 0x5b, 0x3e, 0xe7, 0xec, 0x57, 0xea, 0x75, 0x5c, 0xa8, 0x50, 0x5c, 0xe2, 0x1f, 0xca, + 0xd3, 0xbc, 0x75, 0xee, 0x89, 0xff, 0x62, 0x1e, 0xf3, 0x35, 0x52, 0x14, 0xab, 0x43, 0xd1, 0x38, 0xd5, 0xb2, 0x52, + 0x0a, 0x1f, 0x70, 0xdb, 0x09, 0xee, 0x48, 0x58, 0xbf, 0x3c, 0xc6, 0xc9, 0x06, 0x7f, 0x91, 0x79, 0x17, 0x1e, 0x44, + 0x3a, 0x8c, 0x54, 0xc3, 0xb4, 0x97, 0xf5, 0x49, 0xbb, 0x97, 0x35, 0x9b, 0xc8, 0x49, 0x09, 0x9c, 0x16, 0x90, 0xc9, + 0x79, 0x0e, 0x1b, 0xa4, 0xc2, 0xd8, 0x4e, 0x90, 0x97, 0xc2, 0x59, 0xd3, 0xe5, 0x32, 0xa9, 0x12, 0x32, 0xc4, 0x69, + 0x8d, 0x1f, 0xb8, 0xaa, 0x80, 0x13, 0x83, 0x93, 0xfb, 0xfa, 0x7a, 0x97, 0x5c, 0xf3, 0x8a, 0x38, 0x0d, 0x04, 0xe6, + 0xdc, 0xa9, 0xcf, 0x23, 0xf0, 0x52, 0x94, 0xe2, 0xa7, 0x4a, 0x61, 0xb2, 0x5b, 0x36, 0x1a, 0xe4, 0x65, 0x7e, 0x1b, + 0xe4, 0xf1, 0xe5, 0x05, 0xf4, 0x72, 0xc5, 0x09, 0xf4, 0x58, 0xf5, 0xff, 0x81, 0x1b, 0x92, 0x3a, 0x77, 0x59, 0x12, + 0xc4, 0xb3, 0x90, 0xe6, 0xa2, 0x87, 0x4a, 0x9c, 0xc3, 0xdd, 0x10, 0x65, 0x2d, 0xd1, 0x04, 0x7a, 0x17, 0xd9, 0x3c, + 0x50, 0x11, 0x6e, 0x51, 0x29, 0x9f, 0x9b, 0xe2, 0xb9, 0x6a, 0xfb, 0xba, 0x4a, 0x16, 0x85, 0x96, 0xee, 0x2c, 0x61, + 0xbf, 0xcc, 0xe8, 0x05, 0x0b, 0x8d, 0x93, 0xbb, 0x34, 0x09, 0xd2, 0x90, 0x7e, 0x78, 0xf7, 0x02, 0xb2, 0xdd, 0xd3, + 0x04, 0x48, 0x4c, 0xf9, 0xbb, 0x70, 0x42, 0x40, 0x23, 0xbc, 0x66, 0x01, 0x1d, 0x5c, 0xee, 0x2e, 0x36, 0x56, 0x94, + 0xaf, 0x51, 0xd1, 0xba, 0x14, 0x49, 0x7f, 0x02, 0xca, 0xcb, 0xdd, 0xc5, 0x15, 0x2f, 0x5a, 0xbb, 0x8b, 0xdc, 0x0d, + 0xd3, 0x89, 0xcf, 0x12, 0xf8, 0x9d, 0x14, 0xbb, 0x0b, 0x06, 0x3f, 0x78, 0x71, 0x59, 0x54, 0x89, 0xa2, 0x25, 0x44, + 0xc6, 0x14, 0x14, 0xee, 0x3a, 0xc8, 0xfd, 0x39, 0x65, 0x89, 0x28, 0xba, 0xab, 0x67, 0xaa, 0x7b, 0x05, 0x24, 0xff, + 0x4a, 0xa4, 0xc1, 0xac, 0xcd, 0xe5, 0xd1, 0x7d, 0xcd, 0x65, 0x9a, 0x70, 0x26, 0xd2, 0xe2, 0x75, 0x38, 0x27, 0xf2, + 0xf3, 0x8b, 0x40, 0x9e, 0x44, 0xcd, 0xab, 0x53, 0x17, 0xbe, 0x40, 0xac, 0xb4, 0x80, 0x69, 0x26, 0x8c, 0x7d, 0xba, + 0xfd, 0xa8, 0x64, 0x7e, 0x97, 0xf1, 0x57, 0x52, 0x55, 0x9e, 0xce, 0xb2, 0x00, 0x62, 0xbd, 0x4a, 0xa5, 0x58, 0xf7, + 0x8a, 0xd9, 0x42, 0x7f, 0xb3, 0x31, 0x37, 0x92, 0x6c, 0x39, 0x9c, 0xe9, 0xab, 0xae, 0xed, 0xa0, 0x22, 0x9e, 0x08, + 0x6b, 0xc6, 0xc4, 0xea, 0x5d, 0xb0, 0x10, 0x02, 0x2f, 0x2c, 0x54, 0x09, 0x8b, 0xb5, 0x49, 0x82, 0x8a, 0x14, 0x8a, + 0x0c, 0x52, 0xb8, 0x6c, 0x27, 0xad, 0x56, 0x01, 0x84, 0x1f, 0xd2, 0x2e, 0xf9, 0x66, 0x67, 0x6f, 0x2f, 0xa9, 0x4e, + 0xb4, 0x31, 0x85, 0xf3, 0xe5, 0x92, 0x53, 0x27, 0x91, 0xa7, 0x6e, 0x22, 0x02, 0xca, 0x18, 0xc3, 0xf2, 0x8d, 0x97, + 0xe2, 0xb2, 0x27, 0x2f, 0x29, 0x7a, 0x91, 0x40, 0xa2, 0x44, 0x19, 0xd1, 0x48, 0x3d, 0xd1, 0x2a, 0x19, 0x36, 0x5f, + 0x97, 0x07, 0xf9, 0x6b, 0x58, 0x6f, 0xaf, 0x2c, 0x8e, 0xb4, 0xaa, 0xa2, 0xd5, 0xd2, 0x3c, 0xcd, 0xb8, 0xe3, 0xf8, + 0x38, 0x40, 0xa4, 0xef, 0x8b, 0xd9, 0x1f, 0xcb, 0x7c, 0x8f, 0x41, 0xb3, 0xe3, 0x75, 0x4a, 0x7f, 0x48, 0xed, 0x7c, + 0xb5, 0xcc, 0x36, 0x53, 0x67, 0x74, 0x01, 0x4f, 0xb8, 0xfc, 0xad, 0xd0, 0x57, 0x15, 0xc8, 0xd9, 0x55, 0xcf, 0xe5, + 0x24, 0xb1, 0x62, 0x68, 0x52, 0x19, 0x70, 0x6a, 0x50, 0x9d, 0x67, 0x43, 0xcc, 0xb6, 0x8c, 0x8d, 0x8a, 0x0a, 0x11, + 0xe5, 0xe6, 0xbe, 0x94, 0x4a, 0xd0, 0x85, 0x41, 0xdd, 0x97, 0x4c, 0xbb, 0xf1, 0xea, 0x74, 0x57, 0x28, 0x14, 0x19, + 0x9c, 0x61, 0x53, 0x35, 0x09, 0xcb, 0x2d, 0xc9, 0x37, 0x12, 0xaf, 0x2b, 0x1f, 0xa9, 0xa4, 0x8d, 0xcd, 0x55, 0x44, + 0x32, 0xe4, 0x26, 0xc0, 0xc0, 0x31, 0x90, 0x73, 0x3d, 0x05, 0xe0, 0x31, 0x23, 0x0a, 0x27, 0x95, 0x14, 0xc7, 0xc1, + 0x0b, 0xa9, 0xdd, 0x7b, 0xf6, 0xdb, 0x37, 0x67, 0xef, 0x6d, 0x0c, 0x57, 0x9d, 0xd1, 0x2c, 0xf7, 0x16, 0xb6, 0xca, + 0x31, 0x6c, 0x42, 0xbc, 0xda, 0xf6, 0x6c, 0x7f, 0x0a, 0x87, 0xb6, 0x05, 0x53, 0x6d, 0xdd, 0x34, 0xe7, 0xf3, 0x79, + 0x13, 0x4e, 0x94, 0x35, 0x67, 0x59, 0x2c, 0xd9, 0x4d, 0x68, 0x17, 0x05, 0x72, 0x79, 0x44, 0x93, 0xf2, 0x32, 0xa4, + 0x34, 0xa6, 0x6e, 0x9c, 0x8e, 0xe5, 0x79, 0xd8, 0x55, 0xf7, 0x44, 0x7c, 0x79, 0x2c, 0x2e, 0xf9, 0xea, 0x1f, 0x73, + 0x79, 0xbd, 0x1a, 0xcf, 0xe0, 0x67, 0x1f, 0x82, 0x57, 0xc7, 0x2d, 0x1e, 0x89, 0x87, 0x33, 0xd8, 0x4d, 0xe2, 0x69, + 0x77, 0xb1, 0x46, 0x75, 0x03, 0xe8, 0x22, 0xea, 0xcb, 0xa9, 0xe5, 0xa2, 0xd6, 0xa5, 0x17, 0x5f, 0x5e, 0x16, 0xc7, + 0x2d, 0xe8, 0xab, 0xa5, 0xfb, 0xbd, 0x4a, 0xc3, 0x5b, 0xdd, 0xbe, 0xa4, 0x44, 0xb8, 0xec, 0x29, 0x27, 0x7d, 0xe8, + 0x02, 0xc6, 0x0d, 0xfb, 0x02, 0x67, 0x8a, 0x85, 0x9e, 0x57, 0x0f, 0xc5, 0xd0, 0x02, 0x86, 0x59, 0x40, 0x09, 0x90, + 0x1b, 0x74, 0x1e, 0x96, 0x0d, 0xc4, 0x6e, 0x97, 0x45, 0xdb, 0x00, 0x94, 0x15, 0xab, 0xfd, 0x23, 0xdd, 0xdc, 0x15, + 0x59, 0x68, 0x88, 0x43, 0x13, 0xf8, 0x4b, 0x04, 0xff, 0x0a, 0xc0, 0x8f, 0x5b, 0x12, 0x4d, 0x97, 0xe6, 0xb5, 0x33, + 0xf2, 0x42, 0x88, 0x12, 0x99, 0xe7, 0x19, 0xc7, 0xef, 0x39, 0xfe, 0x78, 0x29, 0xaa, 0x6a, 0x2d, 0x01, 0xd4, 0x57, + 0xd0, 0xa6, 0xda, 0x5a, 0x1d, 0x0c, 0xd2, 0x38, 0xf6, 0xa7, 0x39, 0xf5, 0xf4, 0x0f, 0xa5, 0x30, 0x80, 0xde, 0xb1, + 0xae, 0xa1, 0xa9, 0xbc, 0xa7, 0x53, 0xd0, 0xe3, 0xd6, 0xd5, 0xc7, 0x6b, 0x3f, 0x73, 0x9a, 0xcd, 0xa0, 0x79, 0x35, + 0x46, 0x05, 0x8f, 0x16, 0xa6, 0xba, 0xf1, 0xb0, 0xdd, 0xee, 0x41, 0x92, 0x6a, 0xd3, 0x8f, 0xd9, 0x38, 0xf1, 0x62, + 0x3a, 0xe2, 0x05, 0x87, 0xd3, 0x83, 0x0b, 0xad, 0xdf, 0xb9, 0xdd, 0xc3, 0x8c, 0x4e, 0x2c, 0x17, 0xfe, 0xde, 0x3d, + 0x70, 0xc1, 0x43, 0x2f, 0xe1, 0x51, 0x53, 0x24, 0x43, 0xc3, 0x51, 0x0e, 0x1e, 0xd5, 0x9e, 0x17, 0xc6, 0x40, 0x01, + 0x05, 0xdd, 0xb7, 0xe0, 0x99, 0xc5, 0x23, 0xcc, 0x33, 0xb3, 0x5e, 0x82, 0x16, 0x6b, 0x33, 0x58, 0x57, 0xc1, 0xf6, + 0x51, 0x91, 0x0b, 0x8b, 0x65, 0xb1, 0x86, 0x17, 0x43, 0x95, 0x2e, 0x58, 0x32, 0x9d, 0xf1, 0x73, 0xe1, 0xf9, 0xcf, + 0xe0, 0x0c, 0xc9, 0x10, 0x1b, 0x25, 0x00, 0xcf, 0x50, 0xb5, 0x0f, 0xfc, 0x38, 0x70, 0xa0, 0x13, 0xab, 0x69, 0x1d, + 0x65, 0x74, 0x82, 0x7a, 0x13, 0x96, 0x34, 0xe5, 0xbb, 0x43, 0x43, 0x77, 0x73, 0x1f, 0xc1, 0x53, 0xe1, 0x8a, 0xde, + 0xb0, 0x48, 0xf0, 0xdd, 0x30, 0xaf, 0xcb, 0x61, 0x51, 0xf4, 0x52, 0xee, 0x9c, 0xbf, 0x70, 0xd0, 0x10, 0xff, 0x6a, + 0x5c, 0x62, 0x63, 0x6b, 0xaa, 0xb6, 0x71, 0x17, 0x6d, 0xa9, 0x62, 0xd2, 0xa5, 0xa8, 0xf6, 0x2b, 0x81, 0x8a, 0x2f, + 0x1d, 0x9b, 0xe6, 0xd3, 0xa6, 0x64, 0x3f, 0x4d, 0x41, 0x3e, 0x36, 0x34, 0x45, 0xca, 0x9d, 0x4d, 0xe9, 0x42, 0x70, + 0x16, 0x75, 0x8e, 0x45, 0x7a, 0x5c, 0x86, 0xe5, 0xb9, 0x27, 0xf5, 0x6c, 0x9e, 0x74, 0x42, 0xb5, 0xad, 0x7f, 0x79, + 0x52, 0x67, 0x53, 0x20, 0xff, 0xcb, 0xbb, 0xfe, 0xfc, 0x38, 0x86, 0x01, 0x2f, 0xb5, 0xd2, 0x60, 0x5e, 0x8d, 0x72, + 0xce, 0x87, 0x0e, 0x2a, 0xd4, 0x9e, 0x79, 0x22, 0xf4, 0x6e, 0xe3, 0x82, 0xc1, 0x1d, 0xae, 0x23, 0x6a, 0xf2, 0x04, + 0x33, 0x83, 0x9c, 0x80, 0x5a, 0xee, 0x78, 0xaf, 0x62, 0x33, 0x52, 0x6b, 0xb7, 0xc4, 0x84, 0x88, 0x9d, 0x25, 0xa1, + 0x6d, 0xfd, 0x39, 0x88, 0x59, 0xf0, 0x91, 0xd8, 0xbb, 0x0b, 0x07, 0xad, 0x1f, 0x0d, 0x15, 0x3b, 0x54, 0xf3, 0x5c, + 0x54, 0x8f, 0x36, 0x64, 0xae, 0xc1, 0x4e, 0xe5, 0xed, 0x41, 0x76, 0x1f, 0x54, 0x9b, 0xe3, 0x96, 0x1c, 0xa7, 0x7f, + 0x59, 0x5c, 0x54, 0xb7, 0x82, 0x55, 0x50, 0x00, 0x9a, 0x65, 0xb9, 0x25, 0xe8, 0x8f, 0xd8, 0x72, 0x0b, 0xd5, 0x2c, + 0x40, 0x6c, 0xd2, 0x3e, 0xb2, 0x2d, 0xc9, 0x60, 0x00, 0x4e, 0xae, 0x78, 0x8d, 0x6d, 0xfd, 0xb9, 0x2c, 0xa3, 0xa5, + 0xdb, 0x47, 0xe4, 0xad, 0x10, 0x1b, 0xc6, 0x02, 0x5b, 0xdf, 0x0d, 0x29, 0xf7, 0x59, 0x2c, 0x9b, 0xf4, 0xb4, 0x97, + 0x62, 0x65, 0x46, 0xcb, 0x65, 0x52, 0x9f, 0x0b, 0xab, 0x63, 0x50, 0xcc, 0xec, 0xb8, 0x55, 0xc1, 0x2d, 0x66, 0x26, + 0xf6, 0x87, 0x19, 0x3f, 0xad, 0x66, 0x28, 0xdf, 0x59, 0x7f, 0x0e, 0xc4, 0xc9, 0x2a, 0x00, 0x30, 0x55, 0x00, 0x42, + 0x64, 0x5f, 0x2a, 0x21, 0x8e, 0x4f, 0x52, 0x97, 0xfb, 0xd9, 0x98, 0xf2, 0x15, 0xc4, 0xfa, 0x32, 0x91, 0xb7, 0xa7, + 0xa3, 0xf8, 0x6b, 0xd0, 0x06, 0x75, 0x68, 0x41, 0xcf, 0x2d, 0x06, 0xa0, 0xaa, 0x92, 0x8d, 0x1a, 0x6f, 0x84, 0x40, + 0xf6, 0x89, 0xc5, 0x49, 0x04, 0xb7, 0x4f, 0x05, 0xb7, 0x97, 0x71, 0x38, 0x4b, 0x8c, 0x25, 0x40, 0x2c, 0x6c, 0x6b, + 0x20, 0x21, 0xa7, 0xa1, 0x84, 0x99, 0x64, 0xa2, 0x55, 0x5a, 0x1c, 0xb7, 0x64, 0x6d, 0xc9, 0x8e, 0x65, 0x25, 0x40, + 0x82, 0xd8, 0xa7, 0x15, 0x0e, 0x20, 0xf9, 0xdb, 0xc4, 0x43, 0xc8, 0xae, 0x4b, 0x62, 0x13, 0x67, 0xcc, 0xfa, 0xc7, + 0xb1, 0x7f, 0x45, 0xe3, 0xfe, 0xee, 0x22, 0x5b, 0x2e, 0xdb, 0xc5, 0x71, 0x4b, 0x3e, 0x5a, 0xc7, 0x82, 0x6f, 0xc8, + 0xbb, 0x41, 0xc5, 0x12, 0xc3, 0xc1, 0x4d, 0x48, 0x89, 0xd5, 0xb9, 0x60, 0x9e, 0xea, 0xa0, 0xb0, 0x2d, 0x91, 0x85, + 0x22, 0x2a, 0x95, 0x3a, 0x4d, 0x61, 0x5b, 0x2c, 0x5c, 0x2f, 0xcb, 0x39, 0x9d, 0x42, 0x69, 0xb4, 0x5c, 0x76, 0x0a, + 0xdb, 0x9a, 0xb0, 0x04, 0x9e, 0xb2, 0xe5, 0x52, 0x9c, 0x89, 0x9c, 0xb0, 0xc4, 0x69, 0x03, 0xd9, 0xda, 0xd6, 0xc4, + 0xbf, 0x11, 0x13, 0xd6, 0x6f, 0xfc, 0x1b, 0xa7, 0xa3, 0x5e, 0xb9, 0x25, 0x7e, 0x12, 0xa0, 0xb8, 0x6a, 0x45, 0x7d, + 0xb5, 0xa2, 0x21, 0x9e, 0xc9, 0xd3, 0x5e, 0xc4, 0x09, 0x89, 0xbf, 0x79, 0x45, 0x43, 0xbd, 0xa2, 0xb3, 0x2d, 0x2b, + 0x3a, 0xbb, 0x63, 0x45, 0x03, 0xb5, 0x7a, 0x56, 0x89, 0xbb, 0x74, 0xb9, 0xec, 0xb4, 0x2b, 0xec, 0x1d, 0xb7, 0x42, + 0x76, 0x0d, 0xab, 0x01, 0x9a, 0x1a, 0x67, 0x13, 0xba, 0x99, 0x28, 0xeb, 0x28, 0xa6, 0x9f, 0x85, 0xc9, 0x0a, 0x0b, + 0x59, 0x1d, 0x0b, 0x26, 0x5d, 0x97, 0x81, 0xc9, 0x3f, 0x92, 0xb2, 0x19, 0xe0, 0x21, 0x01, 0x3c, 0x44, 0xfa, 0xae, + 0x50, 0xc7, 0x7e, 0x6f, 0x63, 0xdb, 0xb2, 0x35, 0x59, 0x5f, 0x16, 0x17, 0x20, 0x23, 0xc4, 0xfc, 0xee, 0x45, 0x8b, + 0x50, 0xdb, 0xee, 0x6f, 0xa7, 0x39, 0xc8, 0x21, 0x98, 0xa7, 0x59, 0x68, 0x7b, 0xb2, 0xea, 0x67, 0xa1, 0x6a, 0xc2, + 0x12, 0x95, 0x91, 0xb6, 0x95, 0xd6, 0xaa, 0xf7, 0x26, 0xc5, 0x75, 0x0f, 0x0f, 0x65, 0x8d, 0xa9, 0xcf, 0x39, 0xcd, + 0x12, 0x45, 0xb9, 0xb6, 0xfd, 0x1f, 0x82, 0x0a, 0x37, 0xf0, 0x95, 0x40, 0x2f, 0x80, 0x26, 0x40, 0xa5, 0x73, 0x2b, + 0x9e, 0x2f, 0xc5, 0xd3, 0x4e, 0xa5, 0x6c, 0xde, 0x22, 0x53, 0xef, 0x97, 0x45, 0x60, 0x86, 0xcc, 0x26, 0x34, 0xbc, + 0x10, 0x0c, 0x7a, 0x10, 0x5f, 0x2a, 0xe5, 0x71, 0x45, 0xdc, 0x55, 0x0d, 0xb0, 0xfd, 0xd3, 0xac, 0xfb, 0xe8, 0xe0, + 0xd4, 0xc6, 0x92, 0xc7, 0xa7, 0xa3, 0x91, 0x8d, 0x0a, 0xeb, 0x7e, 0xcd, 0x3a, 0x07, 0x3f, 0xcd, 0xbe, 0x7e, 0xd6, + 0xfe, 0xba, 0x6c, 0x9c, 0x00, 0x11, 0xa9, 0x24, 0x08, 0x2d, 0xaa, 0x0c, 0x78, 0xf5, 0x8c, 0x46, 0x7e, 0xb2, 0x7d, + 0x3a, 0xe7, 0xe6, 0x74, 0xf2, 0x29, 0xa5, 0x21, 0x10, 0x27, 0x5e, 0x2b, 0xbd, 0x88, 0xe9, 0x35, 0xd5, 0x37, 0x34, + 0x6e, 0x18, 0x6c, 0x43, 0x8b, 0x20, 0x9d, 0x25, 0x5c, 0x65, 0x83, 0x28, 0x56, 0x6b, 0x4c, 0xe9, 0x52, 0xcc, 0xc1, + 0x54, 0xe7, 0x6f, 0xa5, 0x9c, 0xab, 0x4b, 0xaf, 0xe2, 0x12, 0xdb, 0x06, 0x00, 0x5b, 0x21, 0x1b, 0x6c, 0x29, 0xf7, + 0xda, 0xb8, 0xbd, 0x0d, 0x36, 0xdc, 0x41, 0x9e, 0x6d, 0x0f, 0x35, 0x9e, 0x84, 0x43, 0xb7, 0x76, 0xa9, 0xc6, 0x56, + 0x7c, 0x7d, 0x12, 0x03, 0x57, 0x19, 0x74, 0x96, 0xd0, 0x3c, 0xdf, 0x8a, 0x80, 0x72, 0x11, 0xb1, 0x5d, 0xd5, 0xb6, + 0xb7, 0xf4, 0x82, 0xdb, 0x18, 0x76, 0x98, 0x00, 0xb8, 0x56, 0x45, 0xa8, 0x1b, 0x17, 0x70, 0xee, 0xe9, 0x3e, 0x03, + 0x55, 0xb5, 0xb7, 0xf5, 0x82, 0x3b, 0x87, 0x07, 0x78, 0xff, 0x51, 0x5b, 0x0d, 0xa5, 0x23, 0xd8, 0xaa, 0x1e, 0x1d, + 0x8d, 0x68, 0x50, 0xba, 0xde, 0x21, 0x16, 0x39, 0x62, 0x31, 0x87, 0x90, 0x9c, 0x88, 0x95, 0xd9, 0xaf, 0xd3, 0x84, + 0xda, 0x48, 0x67, 0xd7, 0x2a, 0x54, 0x29, 0x55, 0x63, 0x33, 0x44, 0xb2, 0xc7, 0x3a, 0x34, 0x6a, 0x94, 0xe5, 0x52, + 0x7b, 0x86, 0x6a, 0xe5, 0xf5, 0x35, 0x4b, 0x85, 0xeb, 0x67, 0xdb, 0x5e, 0xbd, 0xdf, 0x8e, 0x5c, 0x74, 0xbe, 0x3e, + 0xec, 0xb4, 0x0b, 0x1b, 0xdb, 0xd0, 0xdd, 0x7d, 0x37, 0xa4, 0x68, 0xb5, 0x0f, 0xad, 0x66, 0xc9, 0xe7, 0xb4, 0xeb, + 0x76, 0x1e, 0x77, 0x6c, 0x2c, 0xaf, 0x75, 0x40, 0x45, 0xc9, 0x77, 0x02, 0x70, 0x46, 0xff, 0xee, 0xa9, 0xd4, 0x3b, + 0xbf, 0x1f, 0x3c, 0x0f, 0x3b, 0x6d, 0x1b, 0xdb, 0x39, 0x4f, 0xa7, 0x9f, 0x31, 0x85, 0x7d, 0xa0, 0xa6, 0x38, 0xcd, + 0xa9, 0x39, 0x07, 0xa9, 0x39, 0xff, 0xfe, 0x49, 0x48, 0x88, 0xa6, 0x19, 0xcd, 0x73, 0xcb, 0xec, 0x5f, 0x91, 0xd2, + 0x27, 0x78, 0xf3, 0x46, 0x8a, 0xcb, 0x29, 0x17, 0x78, 0x91, 0x37, 0x2e, 0x98, 0x54, 0x25, 0xcb, 0xd6, 0x88, 0x4d, + 0x48, 0x9b, 0x92, 0x87, 0x4a, 0x45, 0xee, 0x93, 0x23, 0x6f, 0xd8, 0x7c, 0x72, 0x60, 0x19, 0xa3, 0x5f, 0x1f, 0xa0, + 0x56, 0x32, 0x61, 0xc9, 0xc5, 0x86, 0x52, 0xff, 0x66, 0x43, 0x29, 0x68, 0x87, 0x25, 0x74, 0xea, 0x36, 0xa0, 0x4f, + 0x63, 0xbd, 0xd2, 0xb1, 0x4c, 0x10, 0x43, 0xe1, 0xea, 0xfc, 0x04, 0xa4, 0xc6, 0x32, 0x88, 0x1e, 0x7e, 0xfb, 0x70, + 0x50, 0xf2, 0x39, 0xc3, 0x95, 0xbd, 0xfc, 0xbe, 0x19, 0x42, 0x69, 0x13, 0xe2, 0x09, 0xf1, 0x67, 0xcd, 0x95, 0xde, + 0x7c, 0x9a, 0xe0, 0x0c, 0x05, 0xee, 0x77, 0x2c, 0xbd, 0xba, 0x55, 0x60, 0x75, 0xed, 0x37, 0x14, 0x2b, 0x1d, 0xab, + 0x5c, 0xff, 0x20, 0x66, 0x93, 0x8a, 0x04, 0xd6, 0xc1, 0x14, 0xca, 0x15, 0x24, 0x97, 0x99, 0x9d, 0x48, 0x2d, 0x4b, + 0x30, 0x7d, 0xb8, 0x95, 0x64, 0x96, 0xd1, 0x8b, 0x38, 0x9d, 0xaf, 0xde, 0xb3, 0xb6, 0xbd, 0x72, 0xc4, 0xc6, 0x91, + 0x71, 0x0e, 0x8e, 0x92, 0x72, 0x11, 0xee, 0x1c, 0xa0, 0xf8, 0x97, 0x7f, 0x76, 0xdd, 0x7f, 0xf9, 0xe7, 0x4f, 0x56, + 0x85, 0xee, 0x8b, 0x4b, 0xcc, 0xab, 0x6e, 0xb7, 0xef, 0xae, 0xcd, 0x23, 0xd5, 0x71, 0xbe, 0xb9, 0xce, 0xda, 0x22, + 0x08, 0x19, 0xb8, 0xba, 0x04, 0x6b, 0x85, 0x72, 0xf7, 0x59, 0xbf, 0x05, 0x30, 0x98, 0xd7, 0x27, 0x21, 0x83, 0x4a, + 0xbf, 0x0b, 0xb4, 0x4b, 0xe4, 0xdd, 0x6b, 0x45, 0x7e, 0x3b, 0x86, 0x3f, 0x35, 0x87, 0xdf, 0x09, 0xbe, 0x72, 0x85, + 0xc4, 0x97, 0x97, 0x65, 0xc2, 0xa3, 0xd9, 0x14, 0xae, 0x53, 0x18, 0xac, 0x95, 0x28, 0xc5, 0xc3, 0x6b, 0xa3, 0xbe, + 0x38, 0xae, 0x49, 0xe2, 0xcb, 0x57, 0x70, 0x87, 0xd2, 0xf1, 0x55, 0xa6, 0xfd, 0xba, 0x77, 0x08, 0x07, 0xe8, 0xa2, + 0x3e, 0x2b, 0xd1, 0xe9, 0x9a, 0x64, 0x80, 0x52, 0xb0, 0x6c, 0x00, 0x4c, 0x1c, 0x5f, 0x2a, 0xc3, 0xf6, 0x54, 0x7a, + 0x7c, 0xbc, 0x55, 0xd2, 0x56, 0x9e, 0xa0, 0x1a, 0xd2, 0xb1, 0xf5, 0x5e, 0xe0, 0x4b, 0x54, 0xa6, 0x95, 0x23, 0x41, + 0x78, 0xd5, 0xc0, 0x64, 0x29, 0xd9, 0xcf, 0xb5, 0x1f, 0x5f, 0xdf, 0x8f, 0xf1, 0x6d, 0x17, 0xa8, 0x4b, 0x6b, 0xf9, + 0x8f, 0x56, 0x09, 0x96, 0xcd, 0xe5, 0x26, 0x7d, 0x60, 0xee, 0x73, 0x9a, 0x5d, 0x44, 0x90, 0x73, 0x95, 0x7d, 0x82, + 0x39, 0xc1, 0x4a, 0x63, 0x2a, 0xfe, 0x32, 0xa2, 0xee, 0xac, 0xfe, 0x07, 0x71, 0x2a, 0x06, 0x09, 0x93, 0x30, 0x94, + 0xb1, 0x08, 0xff, 0x9f, 0x6f, 0xfd, 0x87, 0xe1, 0x5b, 0x77, 0x0f, 0x51, 0x3b, 0x8e, 0xfd, 0xd9, 0x0b, 0xf9, 0x1f, + 0x9b, 0xdd, 0x25, 0x82, 0xdd, 0xfd, 0x06, 0x46, 0x97, 0xfc, 0x63, 0x18, 0x9d, 0x30, 0xc7, 0x35, 0xa7, 0x5b, 0x8b, + 0x6a, 0xdf, 0xba, 0xfe, 0xdc, 0xbf, 0xad, 0xf6, 0x55, 0x7c, 0x79, 0x32, 0xf7, 0x6f, 0xab, 0x45, 0xd8, 0xce, 0x2e, + 0x56, 0xfb, 0x18, 0xd8, 0x6f, 0x5e, 0xdb, 0x9e, 0xfd, 0xe6, 0xeb, 0xaf, 0x6d, 0x7c, 0x99, 0x53, 0x3e, 0x80, 0x42, + 0xb2, 0xbb, 0xd8, 0x59, 0xad, 0x08, 0x1e, 0x1b, 0x98, 0xa2, 0x88, 0xb0, 0x41, 0x7e, 0xa3, 0xf1, 0x9e, 0xe5, 0x17, + 0x69, 0x62, 0x42, 0xf3, 0x16, 0x9c, 0x08, 0x9f, 0x0b, 0x8e, 0xe8, 0x65, 0x0d, 0x1e, 0x51, 0xba, 0x0a, 0x90, 0x28, + 0xac, 0x41, 0x54, 0xdd, 0x4e, 0x74, 0x37, 0xff, 0xaf, 0x6e, 0x60, 0x90, 0x17, 0x8b, 0x44, 0x83, 0xf8, 0xf2, 0x73, + 0xc4, 0x87, 0x1c, 0xac, 0x72, 0x0e, 0x6a, 0xcf, 0xaa, 0x5f, 0xec, 0x2e, 0xa2, 0xbd, 0x3d, 0x36, 0xb0, 0xb1, 0xb8, + 0x12, 0xaa, 0xd8, 0x24, 0x5c, 0x12, 0xf8, 0x93, 0xc1, 0x9f, 0xb4, 0x62, 0xd4, 0x2c, 0x19, 0x65, 0x7e, 0x46, 0xc3, + 0xed, 0x4c, 0xba, 0xbc, 0x4a, 0x49, 0x91, 0x86, 0xcc, 0xf5, 0xce, 0x2f, 0x44, 0x96, 0xd3, 0x84, 0x81, 0x3e, 0xba, + 0x63, 0x7e, 0x30, 0x48, 0xdd, 0xbd, 0x56, 0x7e, 0x6f, 0xc0, 0x44, 0x38, 0x25, 0x49, 0x99, 0x56, 0x01, 0x17, 0x78, + 0xaa, 0x44, 0x14, 0x6c, 0x23, 0xe1, 0xe0, 0x0f, 0x49, 0x5f, 0x64, 0x58, 0xbc, 0x48, 0xb8, 0x13, 0xba, 0x3c, 0x63, + 0x13, 0x07, 0xe1, 0x4e, 0x1b, 0x21, 0xed, 0x6c, 0x08, 0x49, 0x7f, 0x87, 0xe5, 0xaf, 0xfd, 0xd7, 0x4e, 0x28, 0xee, + 0xfc, 0x12, 0x5f, 0x09, 0x82, 0xf3, 0x98, 0x4f, 0x66, 0xa3, 0x11, 0xcd, 0x1c, 0x7d, 0xd6, 0xf0, 0xab, 0x03, 0x38, + 0xce, 0x0c, 0x6f, 0x9f, 0xfa, 0xdc, 0xff, 0x96, 0xd1, 0xb9, 0x93, 0xa2, 0x5e, 0x56, 0xdd, 0x03, 0x19, 0xe2, 0x19, + 0x22, 0xfd, 0x08, 0x72, 0xf0, 0x5f, 0x24, 0x7c, 0xbf, 0xeb, 0xcc, 0xbe, 0x3a, 0xc0, 0x21, 0xdc, 0xae, 0xa1, 0x13, + 0xc8, 0xe5, 0xb5, 0x28, 0x1f, 0x58, 0xc2, 0x8f, 0xe4, 0x89, 0xcf, 0x14, 0x29, 0x4f, 0x65, 0x99, 0x7c, 0x63, 0xf9, + 0x65, 0x87, 0x21, 0xe9, 0x07, 0x0d, 0x22, 0xcf, 0x7f, 0x8a, 0x0b, 0x7d, 0x4f, 0x23, 0x3f, 0x3b, 0x85, 0xb3, 0xe5, + 0x00, 0xe8, 0x15, 0x4f, 0x7d, 0x27, 0x28, 0x3f, 0x1a, 0xe5, 0xb4, 0x7e, 0x6a, 0xb4, 0xc6, 0x58, 0xe4, 0xdf, 0x54, + 0x45, 0x2d, 0x28, 0xba, 0x30, 0x8b, 0x48, 0x63, 0xb7, 0x85, 0x61, 0x0f, 0xf6, 0x36, 0xba, 0x83, 0xf5, 0xd2, 0x35, + 0xe7, 0x99, 0x3f, 0x2d, 0x43, 0x14, 0xa7, 0x7e, 0x96, 0x31, 0x9a, 0x59, 0xce, 0xf3, 0x5f, 0x91, 0xf7, 0x2f, 0xff, + 0xbc, 0x39, 0x54, 0xa1, 0xa2, 0x13, 0x16, 0xe4, 0xb1, 0x34, 0x45, 0xe6, 0x37, 0xb1, 0x03, 0xd9, 0xd0, 0xd6, 0x91, + 0x95, 0xfd, 0xa3, 0x76, 0xbb, 0xad, 0xa2, 0x0f, 0x1d, 0xf9, 0x13, 0xc2, 0x0d, 0xf0, 0x13, 0x1e, 0x44, 0x00, 0x9b, + 0xd8, 0x32, 0x16, 0x7a, 0xd4, 0x9e, 0xde, 0xd8, 0x7d, 0xd8, 0x0e, 0x0a, 0x8a, 0x77, 0x74, 0x4a, 0x7d, 0xfe, 0x59, + 0xe3, 0x67, 0xa2, 0x49, 0x39, 0x7c, 0x47, 0x0f, 0x5d, 0x8d, 0xbb, 0x32, 0xe8, 0xe1, 0xea, 0xa0, 0xef, 0xd9, 0x44, + 0xdc, 0x12, 0xb5, 0x6d, 0x54, 0xe1, 0x14, 0xaf, 0x8d, 0xc9, 0x65, 0x0b, 0xdb, 0x12, 0x18, 0x8f, 0xd2, 0x38, 0xa4, + 0x19, 0xb1, 0xa9, 0x3b, 0x76, 0xad, 0xc7, 0xed, 0x76, 0x1b, 0x37, 0x0f, 0x0e, 0xdb, 0x6d, 0x7c, 0xf8, 0xb0, 0x8d, + 0x9b, 0xf0, 0xc7, 0x75, 0xdd, 0x15, 0x18, 0xee, 0x0a, 0x10, 0x77, 0xda, 0x19, 0x9d, 0x28, 0x00, 0xef, 0x8c, 0x60, + 0x56, 0x7b, 0x02, 0xee, 0xb2, 0x56, 0xfb, 0x5e, 0x4a, 0x36, 0x75, 0x97, 0x82, 0xca, 0x7c, 0x15, 0xae, 0xc9, 0xb4, + 0x8a, 0xcf, 0x52, 0x79, 0xc7, 0xe0, 0x0b, 0x45, 0x08, 0x9e, 0x75, 0x0a, 0x17, 0xa5, 0x8a, 0xd0, 0x2c, 0x64, 0x1d, + 0xc1, 0xb7, 0xd8, 0xb8, 0xcf, 0x12, 0xf8, 0x4c, 0x97, 0x0e, 0xd0, 0x6a, 0x46, 0x95, 0xae, 0xe4, 0xf7, 0x3e, 0x90, + 0x11, 0xf0, 0x4d, 0x04, 0x31, 0x7c, 0x80, 0xb0, 0x7f, 0x9f, 0x06, 0x6a, 0x05, 0xa1, 0x7e, 0x70, 0x9f, 0xfa, 0x1a, + 0xfb, 0xc3, 0x07, 0x22, 0x0f, 0x6a, 0x27, 0x5a, 0x2e, 0x77, 0xfc, 0xe5, 0x72, 0x27, 0xb8, 0xff, 0x0c, 0xe5, 0xf2, + 0xea, 0x03, 0x17, 0x70, 0xc9, 0xa8, 0x04, 0xfa, 0x05, 0x94, 0x7b, 0x11, 0x96, 0x20, 0xc9, 0x27, 0x1f, 0xab, 0x01, + 0xe5, 0x63, 0x50, 0xac, 0x20, 0x25, 0x24, 0x91, 0xb4, 0xcf, 0x97, 0x4b, 0x45, 0xfc, 0x78, 0x46, 0xfc, 0xb2, 0xa8, + 0x63, 0xe3, 0x29, 0x09, 0xca, 0x47, 0x5b, 0x80, 0x3c, 0x55, 0x5c, 0xaa, 0x82, 0x78, 0xee, 0x67, 0x89, 0x09, 0xf0, + 0xeb, 0xd4, 0x52, 0xc3, 0x5a, 0xd3, 0x2c, 0xbd, 0x66, 0x90, 0x67, 0xb3, 0x32, 0xf0, 0x84, 0xc0, 0x1d, 0x63, 0x3d, + 0x33, 0xea, 0x6e, 0x74, 0xf0, 0x5e, 0xf3, 0x59, 0xb8, 0xd0, 0xb2, 0x9c, 0xa0, 0x17, 0xaa, 0xb9, 0x79, 0x33, 0x3d, + 0xad, 0x77, 0xfe, 0xdc, 0x9b, 0xea, 0x87, 0x67, 0x32, 0xa5, 0xc7, 0x9b, 0x94, 0x87, 0x78, 0xde, 0x92, 0xd7, 0x10, + 0x66, 0xb2, 0x35, 0xdf, 0x86, 0x2b, 0x3d, 0x25, 0x8f, 0x7b, 0xf7, 0xf2, 0x8c, 0xfa, 0x59, 0x10, 0xbd, 0xf5, 0x33, + 0x7f, 0x92, 0xf7, 0x2e, 0xf4, 0x85, 0x61, 0x9a, 0x02, 0x2e, 0x46, 0x22, 0xa9, 0x2a, 0x09, 0x6e, 0x6d, 0x1c, 0x22, + 0x5c, 0xbd, 0x97, 0x10, 0x48, 0x97, 0xba, 0x8d, 0x67, 0xe6, 0x2b, 0x58, 0x67, 0x1b, 0x4f, 0x10, 0x96, 0xb9, 0x4a, + 0x6f, 0xff, 0xc8, 0x2c, 0x25, 0x0c, 0x69, 0x35, 0xde, 0x85, 0x5b, 0x7d, 0x50, 0x4f, 0xe7, 0x2d, 0xbd, 0x5f, 0xc9, + 0x5b, 0xda, 0x80, 0x46, 0x2b, 0xa3, 0xf9, 0x34, 0x4d, 0x72, 0x6a, 0xe3, 0xf7, 0xd0, 0x4e, 0xde, 0xfa, 0x6c, 0x36, + 0x5c, 0xa3, 0xb9, 0xb2, 0xa9, 0x78, 0x23, 0xdb, 0x41, 0xfc, 0xe8, 0xfd, 0xf7, 0x65, 0xca, 0x80, 0x0e, 0x25, 0x89, + 0x9c, 0x77, 0x46, 0xb7, 0xa4, 0xe5, 0x26, 0xf4, 0x93, 0x69, 0xb9, 0xf1, 0xbd, 0xd2, 0x72, 0x13, 0xfa, 0x47, 0xa7, + 0xe5, 0x32, 0x6a, 0xa4, 0xe5, 0x82, 0x9c, 0xfb, 0xfa, 0x5e, 0xd9, 0x9d, 0x3a, 0xe9, 0x2e, 0x9d, 0xe7, 0xa4, 0xa3, + 0xc2, 0x2d, 0x71, 0x3a, 0x86, 0xd4, 0xce, 0x7f, 0x7c, 0xa6, 0x66, 0x9c, 0x8e, 0xcd, 0x3c, 0x4d, 0xf8, 0x06, 0x0a, + 0x90, 0x1d, 0xce, 0xc8, 0xc2, 0xfe, 0xe9, 0xa6, 0xf3, 0xe4, 0xbc, 0xd3, 0xdb, 0xef, 0x4c, 0x6c, 0xcf, 0x06, 0xa7, + 0xa3, 0x28, 0x68, 0xf7, 0xf6, 0xf7, 0xa1, 0x60, 0x6e, 0x14, 0x74, 0xa1, 0x80, 0x19, 0x05, 0x87, 0x50, 0x10, 0x18, + 0x05, 0x0f, 0xa1, 0x20, 0x34, 0x0a, 0x1e, 0x41, 0xc1, 0xb5, 0x5d, 0x9c, 0xb3, 0x32, 0xf7, 0xf8, 0x11, 0x12, 0x97, + 0x25, 0xee, 0x64, 0xf5, 0x83, 0xe2, 0x11, 0xd1, 0x55, 0x1e, 0x95, 0x97, 0x4c, 0x34, 0x0f, 0xf4, 0x9d, 0x88, 0x97, + 0x5f, 0x5c, 0x02, 0x6b, 0x85, 0x3b, 0x5f, 0x30, 0x84, 0x3f, 0x65, 0xcd, 0x7d, 0xfd, 0xda, 0xf6, 0xca, 0x04, 0xdd, + 0x36, 0xee, 0xea, 0x14, 0x5d, 0xcf, 0x46, 0x82, 0x2f, 0xc9, 0x17, 0x87, 0x8d, 0x50, 0x75, 0x0b, 0xd7, 0x0d, 0x56, + 0x77, 0x7d, 0xee, 0x23, 0x3c, 0xd1, 0x0a, 0x10, 0x75, 0xe0, 0x5b, 0x0f, 0xef, 0xd9, 0x84, 0xea, 0xfd, 0xa2, 0x07, + 0xb0, 0x44, 0x12, 0x73, 0x2f, 0xaa, 0x14, 0xa3, 0xb7, 0xf8, 0xa2, 0xba, 0x5e, 0xf6, 0x3d, 0x91, 0xd7, 0xf5, 0x65, + 0x58, 0x46, 0xd4, 0xa6, 0x98, 0xfb, 0x63, 0x0f, 0xb2, 0x35, 0x21, 0x39, 0xc5, 0xbb, 0x20, 0x84, 0xb4, 0x07, 0x33, + 0xef, 0x2d, 0x9e, 0x47, 0x34, 0xf1, 0x26, 0x45, 0xaf, 0x5c, 0x7f, 0x99, 0x3d, 0xfa, 0xbe, 0xbc, 0x93, 0x5c, 0xd0, + 0x44, 0xf5, 0x56, 0x42, 0xd9, 0x2c, 0x69, 0x67, 0x4b, 0x7a, 0xa1, 0xa1, 0xec, 0x8c, 0xe2, 0x74, 0xde, 0x04, 0x71, + 0xbf, 0x31, 0xe5, 0x10, 0xe6, 0x56, 0xa6, 0x1c, 0xbe, 0x04, 0x58, 0xcb, 0xa7, 0xf7, 0xfe, 0xb8, 0xfc, 0xfd, 0x8a, + 0xe6, 0xb9, 0x3f, 0x56, 0x35, 0xb7, 0xa7, 0x18, 0x0a, 0x10, 0xcd, 0xf4, 0x42, 0x0d, 0x04, 0xe4, 0x01, 0x02, 0x42, + 0x20, 0x76, 0xac, 0xd2, 0x02, 0x61, 0xe6, 0xf5, 0x8c, 0x42, 0x81, 0xaa, 0x7a, 0x11, 0xf7, 0xc7, 0x55, 0xc1, 0xf1, + 0x34, 0xa3, 0x2a, 0x57, 0x11, 0xb0, 0x58, 0x1c, 0xb7, 0xa0, 0x40, 0xbe, 0xde, 0x92, 0x39, 0xa8, 0xb9, 0xcb, 0xf6, + 0xfc, 0x41, 0x4b, 0x67, 0x0e, 0x9a, 0x87, 0x60, 0xca, 0x13, 0x30, 0xeb, 0xc9, 0x7f, 0x5f, 0x76, 0x02, 0xf8, 0x4f, + 0x9d, 0xf1, 0xf8, 0x72, 0x34, 0x1a, 0xdd, 0x99, 0x49, 0xf8, 0x65, 0x38, 0xa2, 0x5d, 0x7a, 0xd8, 0x83, 0x03, 0x12, + 0x4d, 0x95, 0xf3, 0xd6, 0x29, 0x04, 0xee, 0x16, 0xf7, 0xab, 0x0c, 0xe9, 0x71, 0x3c, 0x5a, 0xdc, 0x3f, 0xab, 0xb0, + 0x98, 0x66, 0x74, 0x31, 0xf1, 0xb3, 0x31, 0x4b, 0xbc, 0x76, 0xe1, 0x5e, 0x2f, 0x14, 0xa8, 0x47, 0x47, 0x47, 0x85, + 0x1b, 0xea, 0xa7, 0x76, 0x18, 0x16, 0x6e, 0xb0, 0x28, 0xa7, 0xd1, 0x6e, 0x8f, 0x46, 0x85, 0xcb, 0x74, 0xc1, 0x7e, + 0x37, 0x08, 0xf7, 0xbb, 0x85, 0x3b, 0x37, 0x6a, 0x14, 0x2e, 0x55, 0x4f, 0x19, 0x0d, 0x6b, 0xa7, 0x2c, 0x1e, 0xb5, + 0xdb, 0x85, 0x2b, 0x09, 0x6d, 0x01, 0x31, 0x39, 0xf9, 0xd3, 0xf3, 0x67, 0x1c, 0x0c, 0xa6, 0xa2, 0x17, 0x73, 0xe7, + 0xfc, 0x56, 0xdd, 0x60, 0x29, 0x3f, 0xf9, 0x58, 0xa0, 0x21, 0xfe, 0xda, 0x4c, 0xd2, 0x03, 0x62, 0x16, 0xc9, 0x79, + 0xb1, 0xce, 0xe1, 0xab, 0xbd, 0x06, 0xca, 0x12, 0xaf, 0xbf, 0x26, 0x71, 0x95, 0xbb, 0x07, 0x7c, 0x0c, 0x6a, 0xca, + 0x8b, 0xd6, 0xf3, 0x6d, 0xd2, 0x23, 0xfb, 0xb4, 0xf4, 0xb8, 0xba, 0x8f, 0xf0, 0xc8, 0xfe, 0x70, 0xe1, 0x91, 0x9b, + 0xc2, 0x43, 0xb2, 0x8e, 0x39, 0x27, 0x76, 0x10, 0xd1, 0xe0, 0xe3, 0x55, 0x7a, 0xd3, 0x84, 0x2d, 0x91, 0xd9, 0x42, + 0xac, 0x5c, 0xff, 0xd6, 0x43, 0x03, 0xba, 0x33, 0xe3, 0x7b, 0x91, 0x42, 0xc7, 0x7f, 0x93, 0x10, 0xfb, 0x8d, 0x0e, + 0xec, 0xc9, 0x92, 0xd1, 0x88, 0xd8, 0x6f, 0x46, 0x23, 0x5b, 0xdf, 0xc3, 0xe3, 0x73, 0x2a, 0x6a, 0xbd, 0xae, 0x95, + 0x88, 0x5a, 0x60, 0xe8, 0x57, 0x65, 0x66, 0x81, 0x4a, 0xf1, 0x33, 0xd3, 0xf9, 0xd4, 0x9b, 0x90, 0xe5, 0xb0, 0xd5, + 0xe0, 0x33, 0x96, 0xf5, 0xef, 0x00, 0xe4, 0xb5, 0x8f, 0x36, 0x95, 0x00, 0x6f, 0xf8, 0xd2, 0xd4, 0xea, 0x25, 0x74, + 0x63, 0xaa, 0x55, 0xfc, 0x27, 0xb7, 0x2f, 0x42, 0x67, 0xce, 0x51, 0xc1, 0xf2, 0x37, 0xc9, 0xca, 0x05, 0x13, 0x12, + 0x46, 0x42, 0xcc, 0x69, 0x15, 0x3c, 0x1d, 0x8f, 0x63, 0x71, 0x6e, 0xa5, 0x66, 0x70, 0xcb, 0xe6, 0x83, 0xda, 0x7c, + 0x3d, 0xb3, 0xa1, 0xfa, 0x92, 0x87, 0xf8, 0xb4, 0xb1, 0x3c, 0x98, 0x7c, 0xad, 0xbe, 0x71, 0x2b, 0x62, 0x82, 0x0b, + 0xc5, 0xe3, 0x17, 0xf2, 0x38, 0x2b, 0xc7, 0x2c, 0x94, 0xcd, 0x59, 0x58, 0x14, 0xea, 0x22, 0x80, 0x90, 0xe5, 0x53, + 0xd0, 0x9e, 0x64, 0x4b, 0xfa, 0x29, 0x16, 0x9e, 0xcf, 0x8d, 0x3c, 0xba, 0xda, 0x72, 0x15, 0xda, 0x4e, 0x93, 0x89, + 0x49, 0x73, 0x5e, 0xd8, 0xca, 0x64, 0xd3, 0x48, 0xb4, 0x2d, 0x89, 0x4f, 0x99, 0xe1, 0x67, 0xcc, 0x10, 0x92, 0x8c, + 0xca, 0x05, 0xd1, 0xaf, 0x74, 0x41, 0x61, 0x5a, 0x59, 0xe2, 0x8d, 0xc4, 0x96, 0xc8, 0x4a, 0xcb, 0xa7, 0x7e, 0xa2, + 0x8d, 0x39, 0xc9, 0x0f, 0x76, 0x17, 0xd5, 0xca, 0x17, 0xb6, 0x06, 0x5b, 0x12, 0x6f, 0xff, 0xb8, 0x05, 0x0d, 0xfa, + 0x56, 0x0d, 0xf4, 0x64, 0x2d, 0x99, 0xed, 0xee, 0x14, 0xef, 0x8f, 0x97, 0x6e, 0x3e, 0xc7, 0x6e, 0x3e, 0xb7, 0xbe, + 0x5a, 0x34, 0xe7, 0xf4, 0xea, 0x23, 0xe3, 0x4d, 0xee, 0x4f, 0x9b, 0xe0, 0x3d, 0x15, 0x49, 0x28, 0x8a, 0x3d, 0x0b, + 0x1d, 0x5d, 0x9a, 0x7e, 0xbd, 0x59, 0x0e, 0x99, 0xe0, 0xc2, 0x8c, 0xf2, 0x92, 0x34, 0xa1, 0xbd, 0xfa, 0x49, 0x41, + 0x33, 0x99, 0x59, 0x63, 0x6b, 0xb8, 0x48, 0x21, 0x73, 0x9c, 0xdf, 0x7a, 0x6d, 0xc5, 0xd6, 0xdb, 0x3a, 0x53, 0xb9, + 0xbd, 0xb1, 0xbe, 0xa7, 0x90, 0xdb, 0x10, 0xd2, 0x2b, 0x5b, 0x4f, 0xb5, 0xde, 0x96, 0x4a, 0xfe, 0xa9, 0x73, 0x73, + 0x90, 0xba, 0xa2, 0xff, 0x37, 0x0e, 0x1c, 0xae, 0x16, 0x8b, 0x73, 0x73, 0xf7, 0x81, 0xcc, 0xf3, 0x47, 0x9c, 0x66, + 0xf8, 0x3e, 0x35, 0xaf, 0xc4, 0x15, 0x17, 0x0b, 0x10, 0x33, 0x5e, 0xe7, 0xa8, 0x9e, 0xf5, 0x7d, 0x77, 0xf7, 0x77, + 0x4f, 0xbf, 0x50, 0x38, 0xd2, 0x57, 0xbe, 0xda, 0x76, 0x0f, 0x36, 0x42, 0xec, 0xdf, 0x7a, 0x2c, 0x11, 0x32, 0xef, + 0x0a, 0x92, 0x42, 0x7a, 0xd3, 0x54, 0x1d, 0x00, 0xcd, 0x68, 0x2c, 0xbe, 0xf2, 0xae, 0x96, 0x62, 0xff, 0xe1, 0xf4, + 0x46, 0xaf, 0x46, 0x67, 0xe5, 0x60, 0xe7, 0x1f, 0x7a, 0x7e, 0x73, 0xfb, 0x81, 0xd1, 0xfa, 0x19, 0xc4, 0xc3, 0xe9, + 0x4d, 0x4f, 0x0a, 0xda, 0x66, 0x26, 0xa1, 0x6a, 0x4f, 0x6f, 0xcc, 0x13, 0xac, 0x55, 0x47, 0x96, 0xbb, 0x9f, 0x5b, + 0xd4, 0xcf, 0x69, 0x0f, 0x3e, 0x6a, 0xc5, 0x02, 0x3f, 0x56, 0xc2, 0x7c, 0xc2, 0xc2, 0x30, 0xa6, 0x3d, 0x2d, 0xaf, + 0xad, 0xce, 0x43, 0x38, 0x00, 0x6a, 0x2e, 0x59, 0x7d, 0x55, 0x0c, 0xe4, 0x95, 0x78, 0xf2, 0xaf, 0xf2, 0x34, 0x86, + 0x4f, 0x4a, 0x6e, 0x44, 0xa7, 0x3a, 0x19, 0xd9, 0xae, 0x90, 0x27, 0x7e, 0xd7, 0xe7, 0x72, 0xd8, 0xfe, 0x53, 0x4f, + 0x2c, 0x78, 0xbb, 0xc7, 0xd3, 0xa9, 0xd7, 0xdc, 0xaf, 0x4f, 0x04, 0x5e, 0x95, 0x53, 0xc0, 0x1b, 0xa6, 0x85, 0x41, + 0x5a, 0x49, 0x3e, 0x6d, 0xb9, 0x1d, 0x55, 0x26, 0x3a, 0x00, 0x23, 0xb4, 0x2c, 0x2a, 0xea, 0x93, 0xf9, 0xc7, 0xec, + 0x96, 0xc7, 0x9b, 0x77, 0xcb, 0x63, 0xbd, 0x5b, 0xee, 0xa6, 0xd8, 0x2f, 0x47, 0x1d, 0xf8, 0xaf, 0x57, 0x4d, 0xc8, + 0x6b, 0x5b, 0xfb, 0xd3, 0x1b, 0x0b, 0xf4, 0xb4, 0x66, 0x77, 0x7a, 0x23, 0xcf, 0xef, 0x42, 0x8e, 0x5c, 0x1b, 0x4e, + 0xb4, 0xe2, 0xb6, 0x05, 0x85, 0xf0, 0x7f, 0xbb, 0xf6, 0xaa, 0x73, 0x00, 0xef, 0xa0, 0xd5, 0xe1, 0xfa, 0xbb, 0xee, + 0xdd, 0x9b, 0xd6, 0x4b, 0x52, 0xee, 0x78, 0x9a, 0x1b, 0x23, 0x97, 0xfb, 0x57, 0x57, 0x34, 0xf4, 0x46, 0x69, 0x30, + 0xcb, 0xff, 0x49, 0xc1, 0xaf, 0x90, 0x78, 0xe7, 0x96, 0x5e, 0xe9, 0x47, 0x37, 0x95, 0xa7, 0x89, 0x75, 0x0f, 0x8b, + 0x72, 0x9d, 0xbc, 0x3c, 0xf0, 0x63, 0xea, 0x74, 0xdd, 0x83, 0x0d, 0x9b, 0xe0, 0xdf, 0x64, 0x6d, 0x36, 0x4e, 0xe6, + 0xf7, 0x22, 0xe3, 0x4e, 0x24, 0x7c, 0x16, 0x0e, 0xcc, 0x35, 0x6c, 0x1f, 0x6d, 0x06, 0xf7, 0x5c, 0x8f, 0x34, 0xd4, + 0x42, 0x41, 0xc9, 0x9d, 0x90, 0x8e, 0xfc, 0x59, 0xcc, 0xef, 0xee, 0x75, 0x1b, 0x65, 0xac, 0xf5, 0x7a, 0x07, 0x43, + 0xaf, 0xea, 0xde, 0x93, 0x4b, 0x7f, 0xf9, 0xf8, 0x00, 0xfe, 0x93, 0xe7, 0x6c, 0xae, 0x2a, 0x5d, 0x5d, 0x5a, 0xbd, + 0xa0, 0xab, 0x5f, 0xd7, 0x94, 0x71, 0x29, 0xc2, 0x85, 0x3e, 0x7e, 0xdf, 0xda, 0xa0, 0x55, 0xde, 0xab, 0xba, 0xd2, + 0xb2, 0x3e, 0xab, 0xf6, 0xe7, 0x75, 0x7e, 0xcf, 0xba, 0x81, 0xd4, 0x5c, 0xeb, 0x75, 0xd5, 0x57, 0xee, 0xd7, 0x2a, + 0x6b, 0x8c, 0x8b, 0xfa, 0xd7, 0xe4, 0xaa, 0x34, 0x51, 0x64, 0xd6, 0x2b, 0x58, 0x29, 0xd7, 0xd2, 0x4a, 0x49, 0x29, + 0xb9, 0x3c, 0x1e, 0xdc, 0x4c, 0x62, 0xeb, 0x5a, 0x5e, 0xc5, 0x43, 0xec, 0x8e, 0xdb, 0xb6, 0x2d, 0xe1, 0xa4, 0x83, + 0x2f, 0x82, 0xd9, 0x1f, 0xde, 0x7f, 0xdd, 0x3c, 0xb2, 0x07, 0xa0, 0x69, 0x5d, 0x8f, 0x85, 0x66, 0xf7, 0xd2, 0xbf, + 0xa5, 0xd9, 0x45, 0x57, 0xb9, 0xe0, 0x65, 0x6a, 0xba, 0x28, 0xb3, 0xba, 0xb6, 0x75, 0x33, 0x89, 0x93, 0x9c, 0xd8, + 0x11, 0xe7, 0x53, 0xaf, 0xd5, 0x9a, 0xcf, 0xe7, 0xee, 0x7c, 0xdf, 0x4d, 0xb3, 0x71, 0xab, 0xdb, 0x6e, 0xb7, 0xe1, + 0xe3, 0x22, 0xb6, 0x75, 0xcd, 0xe8, 0xfc, 0x49, 0x7a, 0x43, 0xec, 0xb6, 0xd5, 0xb6, 0x3a, 0xdd, 0x23, 0xab, 0xd3, + 0x3d, 0x70, 0x1f, 0x1e, 0xd9, 0xfd, 0x2f, 0x2c, 0xeb, 0x38, 0xa4, 0xa3, 0x1c, 0x7e, 0x58, 0xd6, 0xb1, 0x50, 0xbc, + 0xe4, 0x6f, 0xcb, 0x72, 0x83, 0x38, 0x6f, 0x76, 0xac, 0x85, 0x7a, 0xb4, 0x2c, 0xb8, 0xb0, 0xc8, 0xb3, 0xbe, 0x1c, + 0x75, 0x47, 0x07, 0xa3, 0xc7, 0x3d, 0x55, 0x5c, 0x7c, 0x51, 0xab, 0x8e, 0xe5, 0xbf, 0x5d, 0xa3, 0x59, 0xce, 0xb3, + 0xf4, 0x23, 0x55, 0xae, 0x7d, 0x0b, 0x44, 0xcf, 0xc6, 0xa6, 0xdd, 0xf5, 0x91, 0x3a, 0x47, 0x57, 0xc1, 0xa8, 0x5b, + 0x55, 0x17, 0x30, 0xb6, 0x4a, 0x20, 0x8f, 0x5b, 0x1a, 0xf4, 0x63, 0x13, 0x4d, 0x9d, 0xe6, 0x26, 0x44, 0x75, 0x6c, + 0x35, 0xc7, 0xb1, 0x9e, 0xdf, 0x31, 0x9c, 0x8f, 0xd7, 0xba, 0xaa, 0x80, 0xc0, 0xb6, 0x42, 0x62, 0xbf, 0xea, 0x74, + 0x8f, 0x70, 0xa7, 0xf3, 0xd0, 0x7d, 0x78, 0x14, 0xb4, 0xf1, 0x81, 0x7b, 0xd0, 0xdc, 0x77, 0x1f, 0xe2, 0xa3, 0xe6, + 0x11, 0x3e, 0x7a, 0x7e, 0x14, 0x34, 0x0f, 0xdc, 0x03, 0xdc, 0x6e, 0x1e, 0x41, 0x61, 0xf3, 0xa8, 0x79, 0x74, 0xdd, + 0x3c, 0x38, 0x0a, 0xda, 0xa2, 0xb4, 0xeb, 0x1e, 0x1e, 0x36, 0x3b, 0x6d, 0xf7, 0xf0, 0x10, 0x1f, 0xba, 0x0f, 0x1f, + 0x36, 0x3b, 0xfb, 0xee, 0xc3, 0x87, 0x2f, 0x0f, 0x8f, 0xdc, 0x7d, 0x78, 0xb7, 0xbf, 0x1f, 0xec, 0xbb, 0x9d, 0x4e, + 0x13, 0xfe, 0xe0, 0x23, 0xb7, 0x2b, 0x7f, 0x74, 0x3a, 0xee, 0x7e, 0x07, 0xb7, 0xe3, 0xc3, 0xae, 0xfb, 0xf0, 0x31, + 0x16, 0x7f, 0x45, 0x35, 0x2c, 0xfe, 0x40, 0x37, 0xf8, 0xb1, 0xdb, 0x7d, 0x28, 0x7f, 0x89, 0x0e, 0xaf, 0x0f, 0x8e, + 0x7e, 0xb4, 0x5b, 0x5b, 0xe7, 0xd0, 0x91, 0x73, 0x38, 0x3a, 0x74, 0xf7, 0xf7, 0xf1, 0x41, 0xc7, 0x3d, 0xda, 0x8f, + 0x9a, 0x07, 0x5d, 0xf7, 0xe1, 0xa3, 0xa0, 0xd9, 0x71, 0x1f, 0x3d, 0xc2, 0xed, 0xe6, 0xbe, 0xdb, 0xc5, 0x1d, 0xf7, + 0x60, 0x5f, 0xfc, 0xd8, 0x77, 0xbb, 0xd7, 0x8f, 0x1e, 0xbb, 0x0f, 0x0f, 0xa3, 0x87, 0xee, 0xc1, 0xb7, 0x07, 0x47, + 0x6e, 0x77, 0x3f, 0xda, 0x7f, 0xe8, 0x76, 0x1f, 0x5d, 0x3f, 0x74, 0x0f, 0xa2, 0x66, 0xf7, 0xe1, 0x9d, 0x2d, 0x3b, + 0x5d, 0x17, 0x70, 0x24, 0x5e, 0xc3, 0x0b, 0xac, 0x5e, 0xc0, 0xff, 0x91, 0x68, 0xfb, 0x6f, 0xd8, 0x4d, 0xbe, 0xde, + 0xf4, 0xb1, 0x7b, 0xf4, 0x28, 0x90, 0xd5, 0xa1, 0xa0, 0xa9, 0x6b, 0x40, 0x93, 0xeb, 0xa6, 0x1c, 0x56, 0x74, 0xd7, + 0xd4, 0x1d, 0xe9, 0xff, 0xd5, 0x60, 0xd7, 0x4d, 0x18, 0x58, 0x8e, 0xfb, 0xef, 0xda, 0x4f, 0xb9, 0xe4, 0xc7, 0xad, + 0xb1, 0x24, 0xfd, 0x71, 0xff, 0x0b, 0xf9, 0xe5, 0xa0, 0x2f, 0x2e, 0xb1, 0xbf, 0xcd, 0xf1, 0x11, 0x7f, 0xda, 0xf1, + 0x11, 0xd1, 0xfb, 0x78, 0x3e, 0xe2, 0x3f, 0xdc, 0xf3, 0xe1, 0xaf, 0xba, 0xcd, 0x6f, 0xf8, 0x9a, 0x83, 0x63, 0xd5, + 0x2a, 0x7e, 0xc1, 0x9d, 0xf3, 0x14, 0xbe, 0x52, 0x5d, 0xf4, 0x6e, 0x38, 0x89, 0xa8, 0xe9, 0x07, 0x4a, 0x81, 0xc5, + 0xde, 0x70, 0xc9, 0x63, 0x83, 0x6d, 0x08, 0x09, 0x3f, 0x8d, 0x90, 0xef, 0xee, 0x83, 0x8f, 0xf0, 0x0f, 0xc7, 0x47, + 0x60, 0xe2, 0xa3, 0xe6, 0xc9, 0x17, 0x9e, 0x06, 0xe1, 0x29, 0x38, 0x13, 0xcf, 0x0e, 0x5c, 0xd0, 0xd1, 0xb0, 0x5b, + 0xf4, 0x5a, 0x44, 0xee, 0x64, 0x70, 0xfd, 0xf9, 0xe7, 0x04, 0x1d, 0xe4, 0x6d, 0x3c, 0x44, 0x1f, 0x8b, 0x98, 0x0a, + 0xa9, 0xa3, 0x1e, 0x4a, 0xa1, 0xd4, 0x75, 0xdb, 0x6e, 0xbb, 0x74, 0xe9, 0xc0, 0x0d, 0x4c, 0x64, 0x91, 0x72, 0xdf, + 0xdb, 0xe9, 0xe0, 0x38, 0x1d, 0xc3, 0xbd, 0x4c, 0xe2, 0x4b, 0x75, 0x70, 0xe2, 0x21, 0x90, 0x1f, 0x09, 0x84, 0xf4, + 0x09, 0xe5, 0xe8, 0xf1, 0xb3, 0x8f, 0x7f, 0x83, 0x20, 0xa6, 0x8e, 0x49, 0x4c, 0xc0, 0xdb, 0xf1, 0x8a, 0x86, 0xcc, + 0x77, 0x6c, 0x67, 0x9a, 0xd1, 0x11, 0xcd, 0xf2, 0x66, 0xed, 0x6a, 0x20, 0x71, 0x2b, 0x10, 0xb2, 0x15, 0x84, 0xa3, + 0x0c, 0xbe, 0xbc, 0x44, 0xce, 0x95, 0xbf, 0xd1, 0x56, 0x06, 0x98, 0x5d, 0x60, 0x5d, 0x92, 0x81, 0xac, 0xad, 0x94, + 0x36, 0x5b, 0x6a, 0x6d, 0x1d, 0xb7, 0x7b, 0x88, 0x2c, 0x51, 0x0c, 0xdf, 0xb4, 0xf9, 0xc1, 0x69, 0xee, 0xb7, 0xff, + 0x84, 0x8c, 0x66, 0x65, 0x47, 0x43, 0xe5, 0x6e, 0xcb, 0xcb, 0x2f, 0x1f, 0xae, 0x84, 0x5d, 0x6d, 0x49, 0x11, 0x5f, + 0xca, 0xb9, 0xdb, 0xa8, 0x97, 0xab, 0xa4, 0x39, 0x79, 0xfb, 0xe0, 0x88, 0x8d, 0x1d, 0xe3, 0x66, 0x8b, 0x5c, 0x7e, + 0x33, 0x07, 0x2e, 0xc6, 0x47, 0xa8, 0xa8, 0xaa, 0xe4, 0x68, 0x21, 0xa2, 0x2d, 0x2c, 0xb1, 0xf2, 0xe5, 0xd2, 0x11, + 0x2e, 0x72, 0x62, 0xe0, 0x14, 0x9e, 0x51, 0x0d, 0xc9, 0x39, 0x2e, 0x01, 0x12, 0x08, 0x26, 0xb9, 0xfc, 0xb7, 0x2a, + 0xd6, 0x3f, 0x94, 0xe3, 0xcb, 0x8d, 0xfd, 0x64, 0x0c, 0x54, 0xe8, 0x27, 0xe3, 0x35, 0xb7, 0x9a, 0x0c, 0x18, 0xad, + 0x94, 0x56, 0x5d, 0x55, 0xee, 0xb3, 0xfc, 0xc9, 0xed, 0x7b, 0x75, 0xb9, 0xb6, 0x0d, 0xde, 0x69, 0x11, 0xdf, 0xa8, + 0x3e, 0x04, 0xd4, 0x20, 0x0f, 0x8e, 0x27, 0x94, 0xfb, 0xf2, 0x5c, 0x1c, 0xe8, 0x13, 0x90, 0xcb, 0x62, 0x29, 0x6b, + 0x54, 0x05, 0xf5, 0x89, 0xbc, 0x37, 0x40, 0x8a, 0x7a, 0x6c, 0xa9, 0x5b, 0xe9, 0x9a, 0x62, 0x69, 0x48, 0x07, 0x4b, + 0x7f, 0x4c, 0xe0, 0x8b, 0x93, 0xcf, 0x24, 0x49, 0xed, 0xfe, 0x83, 0x32, 0xd7, 0x65, 0xdb, 0x22, 0xc4, 0x2c, 0xf9, + 0x78, 0x9e, 0xd1, 0xf8, 0x9f, 0xc8, 0x03, 0x16, 0xa4, 0xc9, 0x83, 0xa1, 0x8d, 0x7a, 0xdc, 0x8d, 0x32, 0x3a, 0x22, + 0x0f, 0x40, 0xc6, 0x7b, 0xc2, 0xfa, 0x00, 0x46, 0xd8, 0xb8, 0x99, 0xc4, 0x58, 0x68, 0x4c, 0xf7, 0x50, 0x88, 0x24, + 0xb8, 0x76, 0xf7, 0xd0, 0xb6, 0xa4, 0x4d, 0x2c, 0x7e, 0xf7, 0xa5, 0x38, 0x15, 0x4a, 0x80, 0xd5, 0xe9, 0xba, 0x87, + 0x51, 0xd7, 0x7d, 0x7c, 0xfd, 0xc8, 0x3d, 0x8a, 0x3a, 0x8f, 0xae, 0x9b, 0xf0, 0x6f, 0xd7, 0x7d, 0x1c, 0x37, 0xbb, + 0xee, 0x63, 0xf8, 0xff, 0xdb, 0x03, 0xf7, 0x30, 0x6a, 0x76, 0xdc, 0xa3, 0xeb, 0x7d, 0x77, 0xff, 0x65, 0xa7, 0xeb, + 0xee, 0x5b, 0x1d, 0x4b, 0xb6, 0x03, 0x76, 0x2d, 0xb9, 0xf3, 0x83, 0x95, 0x0d, 0xb1, 0x21, 0x18, 0x27, 0xcf, 0xf6, + 0xd9, 0x58, 0x1c, 0xc7, 0x36, 0xf7, 0xa7, 0x72, 0xd6, 0x3d, 0xf5, 0x33, 0xf8, 0x88, 0x6a, 0x7d, 0xef, 0xd6, 0xde, + 0xe1, 0x1a, 0xbf, 0xd8, 0x30, 0xc4, 0x54, 0x44, 0xc0, 0xcd, 0x6b, 0xdd, 0xa8, 0xb8, 0x2e, 0x4f, 0x7e, 0x76, 0x4a, + 0x45, 0xc1, 0xca, 0xec, 0x22, 0x83, 0xac, 0x65, 0x0d, 0x48, 0x00, 0x12, 0x34, 0xb8, 0x9a, 0x3f, 0x5a, 0xd1, 0x79, + 0x06, 0x57, 0x21, 0x68, 0x5e, 0xc2, 0xc4, 0xc7, 0xff, 0x04, 0x0c, 0x2f, 0xc2, 0x62, 0x15, 0x3c, 0x38, 0x81, 0x98, + 0xa5, 0xc6, 0xc5, 0x77, 0xb4, 0xca, 0x01, 0x08, 0x19, 0x5c, 0x55, 0x58, 0x14, 0x7a, 0x66, 0x35, 0x2f, 0x6e, 0x85, + 0x44, 0xc1, 0x4e, 0x68, 0x3e, 0xb0, 0xa1, 0xc8, 0xf6, 0x6c, 0xe1, 0x01, 0xb4, 0xcb, 0xef, 0xcc, 0x96, 0x74, 0x5f, + 0x15, 0x60, 0x71, 0x0f, 0x05, 0x6c, 0x6a, 0x40, 0x9f, 0x8d, 0xf6, 0xf6, 0xb6, 0x6e, 0x27, 0xa1, 0x5f, 0xc2, 0xd4, + 0xaa, 0xcf, 0x53, 0x9a, 0x9c, 0xca, 0x36, 0xd7, 0xa1, 0xec, 0x57, 0x60, 0x18, 0x29, 0xb4, 0x5c, 0x51, 0x9f, 0xbb, + 0x7e, 0x22, 0x0f, 0x18, 0x18, 0xfc, 0x0c, 0x77, 0xe8, 0x3e, 0x2a, 0x52, 0xee, 0xcb, 0x9c, 0x31, 0x93, 0x0d, 0xa4, + 0xdc, 0xd7, 0xd7, 0x38, 0xf9, 0xbc, 0x76, 0x84, 0x3f, 0xea, 0xf6, 0xdf, 0xbc, 0x3f, 0xb1, 0xe4, 0xee, 0x3d, 0x6e, + 0x45, 0xdd, 0xfe, 0xb1, 0x70, 0xa9, 0xc8, 0xac, 0x00, 0x22, 0xb3, 0x02, 0x2c, 0x75, 0x7f, 0x0d, 0x04, 0xda, 0x8a, + 0x96, 0x9c, 0xb6, 0x30, 0x29, 0xa4, 0x33, 0x78, 0x32, 0x8b, 0x39, 0x83, 0xcf, 0x2b, 0xb5, 0x44, 0x4a, 0x80, 0x48, + 0x31, 0xd0, 0xc7, 0x61, 0x95, 0xf2, 0x78, 0xc5, 0x13, 0xed, 0x3a, 0x1e, 0xb1, 0x98, 0xea, 0x03, 0xb0, 0xaa, 0xab, + 0x32, 0x1f, 0x68, 0xbd, 0x76, 0x3e, 0xbb, 0x82, 0x9c, 0x08, 0x9d, 0x7d, 0xf4, 0x41, 0x35, 0x38, 0x16, 0x43, 0x41, + 0x60, 0x5f, 0x4a, 0x71, 0xfd, 0x21, 0xd9, 0xfa, 0x92, 0xaa, 0xd9, 0x2b, 0x01, 0x02, 0x97, 0x86, 0x44, 0xfb, 0xfd, + 0xd2, 0x9b, 0x6c, 0xbe, 0x2b, 0x8e, 0x5b, 0xd1, 0x7e, 0xff, 0xd2, 0x1b, 0xab, 0xfe, 0x5e, 0xa6, 0xe3, 0xcd, 0x7d, + 0xc5, 0xe9, 0x78, 0x20, 0x4e, 0xe4, 0xcb, 0xdb, 0xa5, 0xb4, 0x6e, 0x9c, 0xc6, 0x76, 0xff, 0x58, 0xe9, 0x0a, 0x96, + 0x88, 0xba, 0xdb, 0x87, 0x6d, 0x7d, 0xc8, 0x3f, 0x4e, 0xc7, 0xb0, 0x5f, 0x65, 0x13, 0x63, 0x90, 0x9a, 0x43, 0x3e, + 0xea, 0xf4, 0x8f, 0x7d, 0x4b, 0xb0, 0x1e, 0xc1, 0x5b, 0x72, 0xaf, 0x05, 0x8d, 0xa3, 0x74, 0x42, 0x5d, 0x96, 0xb6, + 0xe6, 0xf4, 0xaa, 0xe9, 0x4f, 0x59, 0xe5, 0xfd, 0x06, 0x9d, 0xa4, 0x1c, 0x32, 0x5d, 0xc9, 0xc0, 0xea, 0x56, 0xde, + 0xb8, 0x03, 0x30, 0x89, 0xb4, 0xe7, 0x4e, 0xb8, 0xec, 0x0c, 0xb0, 0xd2, 0xfe, 0x71, 0xcb, 0x5f, 0xc1, 0x88, 0xd8, + 0x8a, 0x85, 0xf2, 0xc3, 0x83, 0xdd, 0x73, 0x25, 0xd2, 0xbf, 0xa4, 0xb4, 0xd0, 0xfe, 0x7a, 0x25, 0xc7, 0x0b, 0xbb, + 0xff, 0xaf, 0xff, 0xe3, 0x7f, 0x29, 0x17, 0xfc, 0x71, 0x2b, 0xea, 0xe8, 0xbe, 0x56, 0x56, 0xa5, 0x38, 0x86, 0x2b, + 0x73, 0xaa, 0x98, 0x31, 0xbd, 0x69, 0x8e, 0x33, 0x16, 0x36, 0x23, 0x3f, 0x1e, 0xd9, 0xfd, 0xed, 0xd8, 0x94, 0xe9, + 0x89, 0x4d, 0x1d, 0x6d, 0x5d, 0x2f, 0x02, 0x7a, 0xfd, 0x4d, 0xf7, 0x3f, 0xe8, 0x8c, 0x2f, 0xb1, 0xb5, 0xcd, 0xdb, + 0x20, 0xaa, 0xdd, 0x57, 0xbb, 0x11, 0x22, 0x57, 0x5f, 0xa7, 0x56, 0x0c, 0x32, 0xaf, 0x5d, 0x04, 0x51, 0xd8, 0x56, + 0x19, 0xf3, 0xfa, 0xbf, 0xff, 0xf3, 0xbf, 0xfc, 0x37, 0xfd, 0x08, 0xa1, 0xac, 0x7f, 0xfd, 0xef, 0xff, 0xf9, 0xff, + 0xfc, 0xef, 0xff, 0x0a, 0xe9, 0x69, 0x2a, 0xdc, 0x25, 0x98, 0x8a, 0x55, 0xc5, 0xba, 0x24, 0x77, 0xb1, 0xe0, 0xd0, + 0xdb, 0x84, 0xe5, 0x9c, 0x05, 0xf5, 0xab, 0x21, 0xce, 0xc4, 0x80, 0x62, 0x67, 0x2a, 0xe8, 0xc4, 0x0e, 0x2f, 0x2a, + 0x82, 0xaa, 0xa1, 0x5c, 0x10, 0x6e, 0x71, 0xdc, 0x02, 0x7c, 0xdf, 0xef, 0x66, 0x1b, 0xb7, 0x5c, 0x8e, 0x85, 0x26, + 0x13, 0x28, 0x29, 0xaa, 0x72, 0x0b, 0x42, 0x2f, 0x0b, 0x78, 0xf4, 0xba, 0x46, 0xb1, 0x58, 0xbd, 0x5a, 0x9b, 0xde, + 0xcf, 0xb3, 0x9c, 0xb3, 0x11, 0xa0, 0x5c, 0xba, 0x91, 0x45, 0x94, 0xbb, 0x09, 0xaa, 0x64, 0x7c, 0x5b, 0x88, 0x5e, + 0x24, 0x81, 0x1e, 0x1c, 0xfd, 0xa9, 0xf8, 0xf3, 0x04, 0x14, 0x36, 0xcb, 0x99, 0xf8, 0x37, 0xca, 0x7a, 0x7f, 0xd8, + 0x6e, 0x4f, 0x6f, 0xd0, 0xa2, 0x1a, 0x01, 0x6f, 0x1b, 0x4c, 0xd0, 0xb1, 0xd9, 0xa1, 0x08, 0x8f, 0x97, 0x5e, 0xee, + 0xb6, 0x05, 0xae, 0x72, 0xab, 0x5d, 0x14, 0x5f, 0x2d, 0x84, 0xa3, 0x95, 0xfd, 0x0a, 0x61, 0x6c, 0xe5, 0x93, 0xbe, + 0x4a, 0xcd, 0xc9, 0x2d, 0x8c, 0x56, 0x5d, 0xd9, 0x2a, 0xea, 0xac, 0x5f, 0x12, 0x63, 0x86, 0xe1, 0xcd, 0x00, 0xfa, + 0x01, 0x84, 0xc4, 0xa3, 0x0e, 0x8e, 0xba, 0x8b, 0xb2, 0x7b, 0xce, 0xd3, 0x89, 0x19, 0x77, 0xa7, 0x3e, 0x0d, 0xe8, + 0x48, 0xfb, 0xf2, 0xd5, 0x7b, 0x19, 0x53, 0x2f, 0xa2, 0xfd, 0x0d, 0x63, 0x29, 0x90, 0x44, 0xbc, 0xdd, 0x6a, 0x17, + 0x5f, 0xc2, 0x0e, 0x5c, 0x8c, 0xe2, 0xd4, 0xe7, 0x9e, 0x20, 0xd8, 0x9e, 0x19, 0xbd, 0xf7, 0x81, 0x27, 0xa5, 0x0b, + 0x03, 0x9e, 0x9e, 0xac, 0x0a, 0x5e, 0xf5, 0xfa, 0x65, 0x91, 0x85, 0x2b, 0x9a, 0x9b, 0x5d, 0x49, 0xa7, 0xdc, 0x77, + 0x2a, 0x28, 0xfe, 0xbc, 0xe6, 0xcd, 0x52, 0x02, 0xa9, 0x8b, 0x36, 0xbf, 0x97, 0x62, 0x5f, 0xbe, 0xfd, 0x9e, 0x3b, + 0xb6, 0x00, 0xd3, 0x5e, 0xad, 0x25, 0x0a, 0xa1, 0xd6, 0x73, 0xf2, 0x5d, 0x69, 0x51, 0xf9, 0xd3, 0xa9, 0xa8, 0x88, + 0x7a, 0xc7, 0x2d, 0xa9, 0x08, 0x03, 0xf7, 0x10, 0x19, 0x1f, 0x32, 0xc1, 0x42, 0x55, 0x52, 0x5b, 0x41, 0xfe, 0x52, + 0xa9, 0x17, 0xf0, 0xd5, 0xf2, 0xfe, 0xff, 0x03, 0x0c, 0xbd, 0x72, 0x0a, 0x4e, 0x98, 0x00, 0x00}; #else // Brotli (default, smaller) constexpr uint8_t INDEX_BR[] PROGMEM = { - 0x1b, 0xe2, 0x97, 0xa3, 0x90, 0xa2, 0x95, 0x55, 0x51, 0x04, 0x1b, 0x07, 0x80, 0x20, 0x79, 0x0e, 0x50, 0xab, 0x02, - 0xdb, 0x98, 0x16, 0xf4, 0x7b, 0x22, 0xa3, 0x4d, 0xd3, 0x86, 0xc1, 0x26, 0x48, 0x49, 0x60, 0xbe, 0xb3, 0xc9, 0xa1, - 0x8c, 0x96, 0x10, 0x1b, 0x21, 0xcf, 0x48, 0x68, 0xce, 0x10, 0x34, 0x32, 0x7c, 0xbf, 0x71, 0x7b, 0x03, 0x8f, 0xdd, - 0x37, 0x06, 0x9a, 0x30, 0x50, 0xe4, 0x08, 0x47, 0x68, 0xec, 0x93, 0xdc, 0x7d, 0x53, 0xf5, 0x4f, 0xd7, 0x8a, 0xcf, - 0x2f, 0x85, 0x3a, 0x6c, 0xa9, 0x63, 0xcb, 0xf5, 0xc8, 0x18, 0xe3, 0xf5, 0xdb, 0x0c, 0x05, 0x9b, 0x48, 0x2c, 0x42, - 0x21, 0xa0, 0x2c, 0x25, 0xf7, 0xcb, 0xb7, 0xaa, 0x65, 0xd5, 0x7f, 0x3e, 0x2f, 0x94, 0x2b, 0x53, 0xa7, 0x0f, 0x4e, - 0x56, 0xa9, 0xf7, 0xce, 0x54, 0x88, 0x39, 0xef, 0xea, 0x3d, 0x69, 0x56, 0xd0, 0x52, 0x96, 0x0a, 0x5b, 0x35, 0xd4, - 0x42, 0xd6, 0x35, 0x10, 0xf3, 0x7f, 0xec, 0x95, 0xd3, 0x2a, 0xfe, 0x4d, 0x22, 0x6a, 0xd6, 0x69, 0x6e, 0x7b, 0x5a, - 0xdd, 0x33, 0x58, 0x21, 0xa4, 0x33, 0xd6, 0x61, 0x05, 0xf5, 0x08, 0xa9, 0x10, 0x32, 0xf5, 0xdc, 0x04, 0x59, 0x72, - 0x41, 0xf4, 0x09, 0xfa, 0x08, 0x08, 0x9b, 0xcc, 0xf3, 0x2d, 0x71, 0xb4, 0x1c, 0xe3, 0x04, 0x64, 0x9a, 0x96, 0x5b, - 0x16, 0x58, 0xed, 0xbf, 0x37, 0xd5, 0x2a, 0x6d, 0x80, 0x66, 0xcf, 0xba, 0xd4, 0xb8, 0xd4, 0x38, 0x65, 0x10, 0x57, - 0x3a, 0xe7, 0x93, 0xe0, 0x82, 0x90, 0x78, 0xef, 0xfd, 0xff, 0x96, 0xed, 0x30, 0x44, 0x77, 0x13, 0x3b, 0x70, 0x92, - 0xe8, 0xb0, 0xf4, 0x25, 0x5a, 0xc9, 0xff, 0xff, 0xbb, 0x01, 0x76, 0x03, 0x94, 0x16, 0x20, 0xa5, 0x2d, 0x8a, 0xe2, - 0x56, 0x51, 0xd4, 0xdc, 0x18, 0xcb, 0x99, 0xb3, 0x4e, 0x3b, 0x55, 0x67, 0x5d, 0x64, 0xa3, 0xc4, 0xf8, 0xec, 0x2a, - 0xb7, 0x51, 0x68, 0x6c, 0x7a, 0xd9, 0x85, 0x97, 0x9e, 0xcb, 0x61, 0x26, 0xbe, 0xeb, 0x3a, 0x6b, 0xe5, 0x38, 0xd8, - 0x63, 0xa8, 0xb5, 0xba, 0xbe, 0xbd, 0x1d, 0xe3, 0xc4, 0x11, 0xa3, 0x08, 0xc4, 0xfe, 0x26, 0x3a, 0xfb, 0x01, 0x5d, - 0xb4, 0xfc, 0x1d, 0x78, 0xca, 0xb2, 0xac, 0x61, 0x39, 0x21, 0xe5, 0x6b, 0x5a, 0x70, 0x76, 0x57, 0x91, 0x4c, 0x8c, - 0x3d, 0x0e, 0x50, 0x4e, 0x41, 0x1c, 0xda, 0x62, 0xd2, 0xf1, 0x25, 0xce, 0x03, 0xf4, 0xfa, 0x3b, 0xc1, 0xc4, 0xed, - 0xc1, 0xdf, 0x8f, 0xe1, 0xc0, 0x0e, 0x34, 0x72, 0x3c, 0xb3, 0x3f, 0xfa, 0xc0, 0xe6, 0xcd, 0xf4, 0x01, 0x19, 0xf4, - 0x28, 0x5b, 0xde, 0x02, 0x6e, 0xa2, 0x24, 0x59, 0x76, 0x94, 0x8d, 0x00, 0x35, 0xab, 0xbe, 0xd9, 0xc0, 0xfb, 0xfa, - 0x97, 0x4f, 0x8f, 0x6e, 0xa4, 0x28, 0x0a, 0xfd, 0x43, 0xd9, 0xf2, 0xb2, 0xc2, 0x75, 0x49, 0x97, 0x8c, 0xcd, 0x61, - 0xb3, 0x64, 0x52, 0x1e, 0x46, 0x1e, 0xa2, 0xe0, 0xb5, 0x26, 0xd3, 0xf5, 0x3c, 0x9e, 0x19, 0x32, 0x45, 0xb9, 0x28, - 0xf5, 0x40, 0xcf, 0xa5, 0xf1, 0xcd, 0x8d, 0x29, 0x55, 0xe3, 0x2c, 0x43, 0x12, 0xa2, 0x4d, 0x37, 0xda, 0x94, 0x3e, - 0x49, 0x88, 0x4f, 0x2c, 0xa5, 0x67, 0xbe, 0xb7, 0x75, 0x28, 0xb8, 0x2f, 0x0d, 0x75, 0xce, 0x87, 0x3f, 0xe3, 0x30, - 0x5a, 0x25, 0x92, 0x30, 0xd3, 0x2d, 0x45, 0xca, 0xe5, 0x49, 0xc7, 0x4d, 0x13, 0x95, 0xa5, 0x9f, 0x7f, 0x2d, 0x49, - 0x46, 0x5a, 0x49, 0x11, 0x12, 0xd2, 0xdd, 0x38, 0x3c, 0x31, 0x63, 0x5e, 0xb6, 0xe6, 0xde, 0xcd, 0xcd, 0x6d, 0x67, - 0x46, 0x53, 0x16, 0x86, 0xd4, 0xad, 0x07, 0xac, 0xdb, 0xd7, 0x9f, 0x48, 0xcc, 0xa6, 0x4d, 0x9f, 0x6c, 0x8b, 0xf2, - 0xcb, 0xcd, 0x1b, 0x1e, 0xa9, 0x39, 0x37, 0x4d, 0xcd, 0x80, 0x9b, 0x19, 0x67, 0x64, 0x70, 0xb0, 0x38, 0x00, 0x9f, - 0xab, 0x26, 0xca, 0xb7, 0xbb, 0x55, 0x50, 0xcf, 0x31, 0x65, 0x12, 0xb6, 0x2b, 0x36, 0x9d, 0x17, 0x2b, 0x50, 0xd1, - 0x13, 0x72, 0x80, 0x7f, 0x88, 0x62, 0xe4, 0xee, 0x40, 0xb7, 0x56, 0xd2, 0x66, 0xa3, 0xb0, 0x0e, 0x31, 0xeb, 0x9a, - 0x27, 0xc1, 0xd5, 0x7b, 0x63, 0xb3, 0x84, 0x1b, 0xc8, 0xbf, 0x35, 0x29, 0x8a, 0x3c, 0xd0, 0x66, 0x03, 0x2a, 0x3e, - 0xb9, 0x79, 0x4c, 0x16, 0x01, 0xaa, 0xe8, 0x0e, 0x01, 0x1c, 0x73, 0x05, 0x78, 0x3a, 0x9c, 0x23, 0xb8, 0x18, 0x78, - 0xc5, 0xcd, 0x53, 0x7f, 0xb4, 0x61, 0xb8, 0x11, 0xa4, 0xcd, 0xc6, 0x27, 0x27, 0xb6, 0xae, 0x51, 0x01, 0x1d, 0xec, - 0xe4, 0x6b, 0x99, 0xe4, 0xdb, 0x2d, 0xbb, 0x66, 0x38, 0x54, 0xf5, 0xf2, 0xba, 0xc3, 0x24, 0x41, 0xfa, 0xae, 0x46, - 0x43, 0xdd, 0xfd, 0x9d, 0x0d, 0x52, 0x98, 0x1c, 0x7f, 0xab, 0x29, 0x83, 0x0f, 0xb9, 0x11, 0xe0, 0xd3, 0x49, 0x86, - 0xef, 0xbb, 0xdf, 0x0a, 0x04, 0x76, 0x11, 0x07, 0x48, 0x7f, 0x86, 0x4c, 0x3a, 0x84, 0xf5, 0xb6, 0x32, 0x54, 0x59, - 0xed, 0xd5, 0xf1, 0xf0, 0xf8, 0x39, 0x2d, 0x10, 0x85, 0x11, 0xd2, 0xef, 0x72, 0xcb, 0xd2, 0x8f, 0xf2, 0x2e, 0x7c, - 0x9b, 0x28, 0xa6, 0x07, 0x7f, 0x7a, 0x7c, 0x43, 0x28, 0x0b, 0x3f, 0xe5, 0x98, 0x64, 0x6f, 0x63, 0xad, 0xd9, 0x90, - 0x34, 0x84, 0x30, 0xf9, 0x53, 0x6e, 0xea, 0xe3, 0x5f, 0x36, 0x39, 0xe7, 0x26, 0x49, 0xf0, 0xe9, 0xe7, 0x32, 0x50, - 0x58, 0x41, 0x1e, 0xaa, 0x98, 0x6f, 0x6b, 0xfa, 0x94, 0x4b, 0x20, 0x01, 0x84, 0x2a, 0x32, 0x84, 0x73, 0x5a, 0x39, - 0x4a, 0x57, 0xbc, 0xbd, 0x86, 0xa4, 0xb2, 0x77, 0x99, 0xe5, 0xdd, 0x44, 0x5d, 0xd5, 0xde, 0x5b, 0x94, 0x7e, 0xec, - 0x53, 0xdd, 0x67, 0xb8, 0x8d, 0xbb, 0x1d, 0x65, 0xf4, 0xe8, 0xe4, 0x73, 0x3d, 0xbc, 0xba, 0xd9, 0x30, 0xbe, 0x1f, - 0xeb, 0x0b, 0x21, 0xaf, 0x24, 0x9a, 0x44, 0xa2, 0x0a, 0xbf, 0xfe, 0xfa, 0x06, 0x14, 0x59, 0x76, 0x6d, 0x97, 0x7e, - 0xe0, 0x70, 0x8c, 0x41, 0x28, 0xac, 0x0b, 0xad, 0x4b, 0xb5, 0xbb, 0x54, 0xeb, 0x0d, 0x05, 0x24, 0xc7, 0xad, 0x04, - 0xfb, 0x9b, 0x12, 0x44, 0xec, 0x20, 0x03, 0xff, 0xba, 0x91, 0xa0, 0x50, 0xba, 0x24, 0xed, 0x9c, 0x96, 0x7e, 0xef, - 0x6f, 0x24, 0x6c, 0xd1, 0x8c, 0x55, 0xe9, 0x0f, 0x8c, 0x2f, 0x8b, 0x62, 0x44, 0x3c, 0x1b, 0x46, 0x3b, 0x49, 0x99, - 0xdc, 0xd6, 0x7a, 0x70, 0x5d, 0xe5, 0x2a, 0x62, 0x2e, 0x54, 0xab, 0x44, 0xf2, 0xf4, 0x61, 0xb2, 0xd8, 0x07, 0x8b, - 0x01, 0x3e, 0x04, 0x19, 0xe1, 0x5d, 0x8e, 0x2a, 0xff, 0x86, 0xa3, 0x59, 0xe5, 0xcc, 0x8d, 0xd3, 0x51, 0x6f, 0xc1, - 0x15, 0x9f, 0x37, 0x73, 0x3d, 0x49, 0x99, 0xca, 0x53, 0x3a, 0x96, 0x0c, 0x92, 0x2b, 0x8b, 0xde, 0x08, 0x68, 0x52, - 0x87, 0x31, 0xb2, 0x68, 0x81, 0xb1, 0xe9, 0x9f, 0x78, 0xf1, 0x22, 0xe8, 0x84, 0x48, 0xdb, 0x49, 0x4d, 0xd2, 0xea, - 0x80, 0x1f, 0xec, 0x50, 0x77, 0x66, 0xe7, 0x13, 0x36, 0x02, 0x85, 0x6f, 0xdd, 0x68, 0xe0, 0x4b, 0x6c, 0x5b, 0xbe, - 0x18, 0xca, 0xaf, 0x92, 0x97, 0xdd, 0x4e, 0x90, 0x28, 0x4e, 0x48, 0x42, 0x62, 0xc3, 0xf1, 0xf7, 0x71, 0x59, 0x2b, - 0x24, 0x2e, 0x4b, 0xf1, 0x52, 0x2d, 0x7b, 0xbf, 0x8f, 0x5d, 0x1a, 0x29, 0x6b, 0xdd, 0xed, 0x8b, 0x0d, 0xa3, 0xaf, - 0x1a, 0x94, 0x32, 0xc4, 0x54, 0x3d, 0xa1, 0xee, 0x41, 0x42, 0x00, 0xc3, 0xc2, 0x23, 0x57, 0x52, 0x9c, 0x48, 0x54, - 0x42, 0x82, 0x61, 0xb1, 0xcb, 0x1b, 0xae, 0x8f, 0xfa, 0x30, 0x6c, 0x00, 0xc4, 0x1b, 0x74, 0x7c, 0x99, 0x51, 0x60, - 0x45, 0x6d, 0x05, 0xe0, 0x44, 0x15, 0x24, 0x98, 0xb1, 0x40, 0x5f, 0xa1, 0x5e, 0x43, 0x55, 0xae, 0x10, 0xbd, 0x9d, - 0x80, 0x41, 0x6e, 0x35, 0x5d, 0xe8, 0xb2, 0x34, 0x7a, 0x1b, 0xb8, 0x29, 0xad, 0x6d, 0xd3, 0xb4, 0x4f, 0x32, 0x0e, - 0x4e, 0xd7, 0xb3, 0x98, 0x12, 0x37, 0xd4, 0x5c, 0x19, 0xbd, 0x26, 0xaa, 0xbb, 0x5b, 0x7d, 0x92, 0xd3, 0xb7, 0xd3, - 0x2e, 0xfa, 0x6e, 0xf6, 0x2b, 0xaa, 0xc4, 0x24, 0x46, 0x6d, 0x58, 0xe9, 0x7e, 0x8d, 0x27, 0x23, 0x14, 0xbc, 0x15, - 0xaf, 0x1b, 0x88, 0x7b, 0xd1, 0x9d, 0xba, 0x9c, 0x08, 0xd2, 0xf9, 0x9b, 0x81, 0xfd, 0xf8, 0x94, 0xc1, 0x0a, 0xf1, - 0xc8, 0xfa, 0x4e, 0x5b, 0x87, 0x86, 0x34, 0xed, 0x92, 0xcf, 0xfd, 0x49, 0xda, 0xd7, 0x25, 0xc9, 0xe3, 0x22, 0xdf, - 0x9e, 0xdd, 0x53, 0x30, 0x15, 0xe0, 0x2c, 0x5a, 0xcf, 0x41, 0xb3, 0x0d, 0xa4, 0x3a, 0x7b, 0x70, 0xc8, 0x9e, 0x4e, - 0x6b, 0xa7, 0xeb, 0xa3, 0x55, 0xd5, 0xc6, 0x45, 0x89, 0x21, 0x81, 0x5f, 0xb1, 0x29, 0x01, 0xe4, 0x40, 0xe4, 0xd1, - 0x6b, 0xe3, 0x4b, 0xe1, 0xfa, 0xf5, 0x12, 0x7d, 0x82, 0x59, 0xb9, 0xfa, 0x07, 0x0d, 0xa9, 0xa4, 0xd5, 0x80, 0x90, - 0x91, 0xfa, 0x8c, 0xf2, 0xcc, 0x0a, 0xee, 0x97, 0xce, 0x17, 0x31, 0x3a, 0x3c, 0xfd, 0x6e, 0x3f, 0x34, 0xf6, 0x2d, - 0x94, 0x17, 0x65, 0xa5, 0x32, 0x73, 0x94, 0x13, 0x80, 0x24, 0x96, 0x3c, 0x25, 0xd2, 0xc6, 0xb7, 0xad, 0x2d, 0x11, - 0xc1, 0x37, 0x7c, 0x88, 0x77, 0xee, 0x05, 0xc7, 0x26, 0x21, 0x81, 0x0a, 0xed, 0x76, 0x01, 0x14, 0x54, 0x90, 0x89, - 0x23, 0xc9, 0xd5, 0xd1, 0x20, 0xb1, 0x3f, 0x56, 0x36, 0x1d, 0x3c, 0x22, 0x92, 0xb5, 0xcd, 0x06, 0xd0, 0x91, 0xc6, - 0xab, 0x4a, 0x92, 0x83, 0x08, 0x4b, 0x00, 0x3a, 0x56, 0xfe, 0x49, 0x4a, 0x5c, 0x4e, 0xd0, 0x85, 0x41, 0xc1, 0x5d, - 0x1a, 0xc6, 0xcd, 0x26, 0xb9, 0xb0, 0x52, 0x01, 0xfd, 0x78, 0xe8, 0xc7, 0x63, 0x0f, 0x45, 0x0a, 0x82, 0x56, 0x88, - 0x87, 0x9c, 0xd2, 0x81, 0x22, 0xfa, 0xa5, 0xfe, 0x71, 0x9b, 0x37, 0xbf, 0x26, 0xe6, 0x46, 0x89, 0x8a, 0xe6, 0x3c, - 0xa6, 0x52, 0xd4, 0xc7, 0x88, 0xc1, 0x3f, 0x66, 0xec, 0xd0, 0x61, 0xa2, 0x92, 0x5e, 0xaa, 0x54, 0xac, 0x83, 0x75, - 0x26, 0x95, 0x02, 0xed, 0xd4, 0xf8, 0xe2, 0x9b, 0x48, 0x12, 0xbc, 0x13, 0xb3, 0xce, 0x20, 0x85, 0x97, 0x2a, 0xac, - 0x95, 0xe8, 0x97, 0x2d, 0x0a, 0xa2, 0xc4, 0x35, 0xb4, 0x0e, 0x69, 0x42, 0x11, 0xec, 0x09, 0x1d, 0x94, 0x68, 0xf9, - 0x87, 0xb6, 0xca, 0x48, 0x82, 0x72, 0xdf, 0xf3, 0xc1, 0xbb, 0xcb, 0x80, 0xf4, 0xf0, 0x51, 0x0f, 0x29, 0x24, 0x16, - 0x3e, 0x61, 0xcb, 0x01, 0x5d, 0xb7, 0x41, 0x52, 0x00, 0xef, 0xaa, 0x62, 0x79, 0xd9, 0x2c, 0x88, 0xbb, 0x93, 0x35, - 0x35, 0x63, 0xbf, 0x4c, 0x60, 0xa7, 0x82, 0xa3, 0xd5, 0xb6, 0x09, 0x6b, 0xa9, 0x96, 0x24, 0xa3, 0x63, 0x81, 0x59, - 0x02, 0x89, 0x10, 0xe9, 0xfe, 0x58, 0x9c, 0x03, 0x31, 0xaf, 0x93, 0xcc, 0x80, 0xf3, 0xd4, 0x2a, 0x47, 0x13, 0x28, - 0x1c, 0xc7, 0x72, 0xbe, 0x26, 0x29, 0xc9, 0x13, 0x0e, 0xb0, 0x1a, 0xaf, 0xb0, 0x8e, 0x82, 0xfb, 0xb8, 0xa6, 0xa4, - 0xcc, 0xee, 0x7f, 0x99, 0xd2, 0xc4, 0x60, 0x57, 0xa2, 0x03, 0x12, 0x40, 0x4a, 0xb3, 0xd4, 0x62, 0xf0, 0x79, 0x44, - 0x3c, 0x16, 0x82, 0x89, 0x88, 0x44, 0xe1, 0x2b, 0x5d, 0xcb, 0xcf, 0xbc, 0x04, 0x84, 0xca, 0x4c, 0x83, 0xce, 0x92, - 0xd7, 0xaa, 0xa4, 0x86, 0xf6, 0x1b, 0xed, 0x46, 0x35, 0x2b, 0x9f, 0x14, 0x3e, 0x64, 0x1d, 0xb9, 0x7f, 0x1a, 0x98, - 0x64, 0xbd, 0xc9, 0x29, 0x95, 0x76, 0x96, 0xaf, 0xfe, 0xf5, 0x05, 0x8a, 0x8d, 0xaa, 0xa3, 0xe9, 0xb6, 0x3e, 0xda, - 0x10, 0x75, 0xf6, 0x11, 0x71, 0xc0, 0x13, 0x56, 0x33, 0x97, 0x5f, 0x65, 0xf8, 0xe1, 0x32, 0x39, 0x20, 0x45, 0x73, - 0x66, 0xa2, 0x6b, 0xfa, 0xef, 0x22, 0x39, 0x70, 0x89, 0xad, 0xc0, 0x14, 0x50, 0x46, 0x15, 0x63, 0x64, 0x39, 0x90, - 0xc4, 0x52, 0xc9, 0xe5, 0x7c, 0x84, 0x16, 0x59, 0x57, 0x4e, 0x19, 0x0a, 0x95, 0xd3, 0xc8, 0x1c, 0x36, 0x29, 0x8e, - 0x61, 0x5e, 0x96, 0xea, 0x79, 0x86, 0x90, 0x26, 0xdd, 0xd5, 0xa7, 0x88, 0x42, 0xcd, 0xaa, 0x7d, 0x17, 0xa6, 0xbe, - 0x08, 0x57, 0x85, 0x01, 0xf2, 0xfc, 0x61, 0x2d, 0xb2, 0xce, 0xa4, 0xf1, 0x62, 0x67, 0xbc, 0xa0, 0xb2, 0x61, 0x24, - 0x59, 0x96, 0x38, 0x28, 0x41, 0xe0, 0x94, 0x90, 0xc6, 0x3e, 0x71, 0xb8, 0x2d, 0x3f, 0x1e, 0x33, 0xb7, 0xe9, 0x50, - 0x46, 0x31, 0xe2, 0xea, 0x49, 0x95, 0x75, 0x0d, 0xe7, 0x21, 0xe6, 0x0f, 0x2f, 0x8b, 0xda, 0x6f, 0xba, 0xd2, 0xe8, - 0xc6, 0xa1, 0xb3, 0x02, 0x6d, 0x4f, 0x27, 0x73, 0x3a, 0x7d, 0x11, 0x57, 0x75, 0x52, 0x10, 0x50, 0x04, 0xc2, 0x1e, - 0x8f, 0xfe, 0x51, 0x1a, 0xed, 0x1f, 0x01, 0x4b, 0xd6, 0x31, 0xd8, 0x93, 0x6a, 0x8f, 0x09, 0x49, 0xcb, 0xdb, 0x1f, - 0x81, 0xb9, 0x52, 0x25, 0xd1, 0x43, 0xf0, 0xe1, 0x08, 0xa5, 0x05, 0x85, 0x64, 0xd3, 0x93, 0x6e, 0x43, 0xa6, 0x09, - 0x98, 0xe8, 0x71, 0x90, 0x67, 0xc3, 0x1b, 0x17, 0x55, 0x88, 0x3e, 0x3e, 0x30, 0xd9, 0xa5, 0x63, 0xb4, 0x69, 0x95, - 0x6d, 0xf6, 0x9f, 0xa1, 0xd8, 0xef, 0xf7, 0xd7, 0xcc, 0xa1, 0x48, 0xef, 0x3a, 0x23, 0x37, 0xb1, 0xe0, 0xfc, 0x14, - 0x25, 0xc6, 0xb3, 0xb6, 0x34, 0xa4, 0xe5, 0x10, 0x45, 0x48, 0x0e, 0x1d, 0x82, 0xbe, 0x0c, 0x19, 0x56, 0x57, 0xe8, - 0xf0, 0x2d, 0xfd, 0x82, 0x43, 0x26, 0x29, 0x39, 0xd2, 0x64, 0xbf, 0x97, 0xc4, 0x64, 0x57, 0xba, 0xa8, 0x40, 0x87, - 0xd5, 0xb4, 0x13, 0x43, 0xb2, 0xd5, 0xbb, 0xda, 0x66, 0xa9, 0xe5, 0x08, 0xee, 0xce, 0x03, 0xc9, 0x1f, 0x81, 0xaa, - 0xe7, 0xd1, 0x19, 0x47, 0x0b, 0x44, 0x9d, 0x4b, 0x92, 0xdb, 0x49, 0x31, 0xc8, 0x26, 0x52, 0x28, 0x90, 0xae, 0x10, - 0x8d, 0x61, 0x31, 0x6d, 0x3f, 0x08, 0x1c, 0x2c, 0x75, 0x9b, 0x64, 0xa4, 0xcf, 0x9d, 0xdd, 0x26, 0xc5, 0x23, 0x54, - 0x1e, 0xb5, 0xee, 0xbb, 0x69, 0x49, 0x90, 0xea, 0x24, 0x4f, 0x10, 0xb4, 0x67, 0x63, 0xef, 0x98, 0x80, 0xf9, 0xde, - 0x54, 0xcc, 0xaf, 0xa7, 0x6e, 0xc2, 0xc2, 0xee, 0x43, 0x8a, 0x5b, 0x66, 0x76, 0xf2, 0x9d, 0xf9, 0x1c, 0x69, 0xce, - 0x0c, 0x9d, 0xd4, 0x29, 0x24, 0xb3, 0xb1, 0xb7, 0xf4, 0x17, 0xa4, 0x79, 0x77, 0x2f, 0x3a, 0x94, 0x4d, 0xf8, 0x3d, - 0x21, 0xb8, 0x1e, 0x91, 0xc3, 0x08, 0xbe, 0xea, 0x90, 0xd8, 0xcd, 0x46, 0x2b, 0x52, 0x68, 0xed, 0x68, 0x88, 0x4b, - 0xb6, 0x7b, 0x37, 0x0b, 0x00, 0x88, 0x90, 0xd3, 0xef, 0x95, 0x86, 0x8c, 0x2d, 0xfd, 0xe2, 0x8c, 0xad, 0x14, 0xe8, - 0x59, 0x2d, 0xe2, 0x09, 0xaf, 0x09, 0x29, 0x41, 0x67, 0x85, 0x63, 0x86, 0xea, 0x43, 0xd0, 0xce, 0x1b, 0x4a, 0xb6, - 0x74, 0x30, 0x9c, 0xb8, 0x86, 0x92, 0x2d, 0x8c, 0x18, 0x1f, 0xba, 0xd9, 0x7b, 0x5a, 0x24, 0x43, 0xc1, 0x8f, 0x54, - 0x11, 0xe5, 0x22, 0x6a, 0x46, 0x68, 0x6c, 0x69, 0x30, 0x8a, 0x36, 0x9c, 0x9b, 0x77, 0x57, 0x04, 0x71, 0xd9, 0x27, - 0x56, 0x52, 0xc4, 0x8f, 0x83, 0xc4, 0xe9, 0x57, 0xab, 0x11, 0xf4, 0x32, 0x67, 0xa1, 0x34, 0xbe, 0x29, 0x85, 0x79, - 0xe4, 0x81, 0xc1, 0xb2, 0xb5, 0x0d, 0xd3, 0x3e, 0x69, 0x59, 0x3d, 0x5f, 0x55, 0x03, 0xdb, 0x20, 0x1c, 0xb5, 0x2c, - 0x1d, 0xeb, 0xe7, 0xb3, 0x8a, 0x5e, 0x37, 0xf2, 0xaf, 0x56, 0xac, 0xc5, 0x17, 0x20, 0x3b, 0x63, 0x98, 0xcd, 0x98, - 0x34, 0x2a, 0xa0, 0x16, 0x92, 0x29, 0x6b, 0x8b, 0x8a, 0xa7, 0x49, 0x09, 0x1b, 0x1a, 0x70, 0x34, 0x2d, 0x0b, 0xe9, - 0xc5, 0xeb, 0xa1, 0x7d, 0x70, 0xd6, 0xe1, 0x73, 0xcb, 0xd2, 0x23, 0x58, 0x0d, 0x78, 0x8d, 0x88, 0x12, 0x44, 0x2a, - 0xa4, 0x44, 0x85, 0x94, 0x43, 0x15, 0xd3, 0x41, 0xa7, 0x5c, 0x53, 0x67, 0xa5, 0x95, 0x79, 0x97, 0xc6, 0xf8, 0xd3, - 0x22, 0xa4, 0xb0, 0xae, 0x80, 0xc1, 0xa2, 0xf8, 0x0d, 0x04, 0xc0, 0x8b, 0x35, 0xd3, 0x33, 0x31, 0x30, 0xc7, 0x4b, - 0x5a, 0xde, 0x4b, 0x13, 0x66, 0xb1, 0x74, 0x63, 0x53, 0xa8, 0x8f, 0x8c, 0x42, 0x7a, 0xce, 0x25, 0x20, 0xea, 0xa6, - 0xc7, 0x97, 0xd9, 0x7a, 0xcf, 0xb8, 0x24, 0xff, 0x75, 0xbe, 0xdd, 0x9b, 0x15, 0x0e, 0xcf, 0x3d, 0x72, 0x38, 0x70, - 0x06, 0xa9, 0x48, 0x63, 0x06, 0x39, 0x05, 0x2f, 0x7a, 0x85, 0x19, 0x7f, 0xa4, 0x2b, 0x59, 0x22, 0x0a, 0x4f, 0x00, - 0x7f, 0x57, 0x2d, 0x42, 0xb7, 0x07, 0x84, 0xef, 0x42, 0xc6, 0x67, 0x35, 0x4c, 0xf2, 0x47, 0x18, 0x23, 0xf1, 0xe5, - 0x7b, 0x70, 0x53, 0x99, 0x8c, 0x6f, 0x7e, 0xcb, 0x92, 0x40, 0x65, 0x19, 0x4c, 0x53, 0x83, 0x92, 0x3a, 0xfb, 0x04, - 0x79, 0xe4, 0xbc, 0xaa, 0x1b, 0xa6, 0x4e, 0x9a, 0x49, 0x1e, 0xf4, 0x41, 0xa6, 0x08, 0x44, 0xa7, 0x8b, 0x61, 0xe4, - 0x81, 0x10, 0x00, 0xcf, 0x11, 0x88, 0xb4, 0x04, 0xce, 0x00, 0x8e, 0xe9, 0x9c, 0x0c, 0x1a, 0x91, 0xd1, 0xf8, 0xa9, - 0x51, 0x84, 0x8a, 0x54, 0xae, 0x63, 0xc7, 0xe1, 0x68, 0x89, 0x68, 0x94, 0xdf, 0x40, 0x31, 0x05, 0xff, 0xd2, 0xb8, - 0xb5, 0x69, 0xd7, 0x7b, 0xe2, 0x19, 0xc6, 0x96, 0xa6, 0x99, 0xa6, 0x45, 0xd1, 0x48, 0xdd, 0x67, 0x0c, 0x57, 0x2c, - 0x41, 0x9b, 0x84, 0xa2, 0x0c, 0xa3, 0x3a, 0xa6, 0x4a, 0x71, 0x0b, 0x47, 0x68, 0x54, 0xbe, 0xb5, 0x08, 0xed, 0xfd, - 0xc4, 0xf1, 0xe9, 0x32, 0x42, 0x5a, 0x9f, 0x1f, 0xbd, 0x2c, 0x30, 0xfd, 0x32, 0x9c, 0xa1, 0xaf, 0x44, 0x44, 0x34, - 0xad, 0x02, 0x3b, 0x1c, 0xe8, 0x6a, 0xc3, 0x4b, 0x73, 0x17, 0xb7, 0x35, 0xd1, 0x83, 0x33, 0xf6, 0x54, 0x86, 0xf4, - 0xed, 0x99, 0xc8, 0xba, 0x28, 0xea, 0xf6, 0xb7, 0x93, 0xaf, 0xe1, 0xb1, 0xf9, 0x78, 0x4c, 0xea, 0x14, 0xe5, 0x6b, - 0xa2, 0xd6, 0xea, 0x5a, 0x1f, 0x82, 0x99, 0x79, 0xf4, 0x5c, 0x31, 0x19, 0xe3, 0xd4, 0x8c, 0x8c, 0xac, 0xef, 0x59, - 0xe2, 0xc5, 0x36, 0xf1, 0x3b, 0x85, 0xe4, 0x47, 0xc7, 0x19, 0xd2, 0x88, 0x82, 0xa0, 0xca, 0xfc, 0x8a, 0x42, 0x19, - 0x18, 0xe9, 0xe7, 0xb6, 0xf6, 0x03, 0x72, 0xc5, 0x28, 0x96, 0xf1, 0x6c, 0x33, 0x3e, 0xe5, 0xea, 0x1f, 0x57, 0x34, - 0xc8, 0xb2, 0xb4, 0xdf, 0x89, 0xa7, 0x6d, 0x1e, 0xda, 0x66, 0x5e, 0xd9, 0x24, 0x02, 0x78, 0x95, 0x26, 0xd9, 0xf6, - 0x70, 0xaa, 0xf5, 0x47, 0xe0, 0x57, 0x5e, 0x41, 0x80, 0xcb, 0x49, 0x58, 0xb9, 0x8b, 0x02, 0x45, 0xb5, 0x2d, 0xb8, - 0x7c, 0xb0, 0x4b, 0x9f, 0x47, 0xb1, 0x44, 0x36, 0xf7, 0xc0, 0x6c, 0x51, 0x44, 0x78, 0x4a, 0xbd, 0xad, 0x51, 0xef, - 0xdf, 0x4d, 0x11, 0x1f, 0x71, 0x24, 0x77, 0x27, 0xab, 0x6e, 0x1c, 0x95, 0x47, 0x5a, 0x28, 0xfd, 0x00, 0x2f, 0x2e, - 0x9a, 0x4b, 0x97, 0x8a, 0xc7, 0x5e, 0x0a, 0xd9, 0x46, 0xc2, 0xdc, 0x22, 0x4e, 0x6d, 0xfb, 0x6a, 0xf2, 0xfd, 0x5c, - 0xd0, 0x24, 0x31, 0xeb, 0x4b, 0x97, 0xd6, 0x86, 0x4f, 0x32, 0xbd, 0xb3, 0xcf, 0x7a, 0xf6, 0x64, 0xce, 0xe4, 0xc6, - 0xe0, 0x39, 0xa8, 0xfa, 0xbd, 0xfd, 0x94, 0xba, 0x6e, 0x78, 0x94, 0xc4, 0x94, 0x26, 0x7f, 0xe1, 0x4e, 0x92, 0xe9, - 0xae, 0x33, 0x1f, 0x25, 0xdd, 0x37, 0x1c, 0xce, 0xde, 0xdf, 0xc6, 0x5d, 0x81, 0x54, 0x16, 0x1f, 0x43, 0x24, 0x3c, - 0xf1, 0xeb, 0xad, 0x31, 0xe0, 0xa1, 0x40, 0xc0, 0x83, 0x4a, 0xba, 0x59, 0xac, 0x15, 0x1d, 0xe7, 0x74, 0xff, 0x66, - 0x13, 0xce, 0x0a, 0xc3, 0x93, 0x1c, 0x27, 0xb1, 0xcb, 0xab, 0xdc, 0x4e, 0xa5, 0xad, 0x7e, 0x9a, 0x6c, 0xc0, 0x5b, - 0x68, 0x43, 0xd1, 0x72, 0x7c, 0x86, 0x5d, 0xf5, 0x43, 0x53, 0xf9, 0x27, 0xa1, 0x14, 0xc7, 0x36, 0x0d, 0x63, 0x0d, - 0xb9, 0xfe, 0xbe, 0x1d, 0xc8, 0xb4, 0x7a, 0xf3, 0xcf, 0xd9, 0xf7, 0xea, 0xed, 0x58, 0x37, 0x3c, 0x91, 0xde, 0x0e, - 0xfe, 0x7a, 0x48, 0x8a, 0x62, 0x79, 0x7b, 0x55, 0xfd, 0xd7, 0x16, 0xbf, 0x7e, 0xac, 0x2e, 0x6b, 0x2c, 0x96, 0xf9, - 0xf8, 0xb2, 0x1a, 0x4f, 0x2d, 0xef, 0xdf, 0x4e, 0xf5, 0x87, 0x2f, 0x3f, 0xf5, 0x1a, 0xb8, 0x3a, 0x73, 0x9e, 0xa4, - 0x57, 0x14, 0xfb, 0x28, 0x57, 0xc1, 0x4b, 0xf8, 0x20, 0x3f, 0x6d, 0x8f, 0xeb, 0xa7, 0xfb, 0x65, 0x3d, 0x1f, 0x68, - 0xc9, 0xe3, 0x66, 0xeb, 0xed, 0x8d, 0xe6, 0xd5, 0x5e, 0xa6, 0x75, 0x0e, 0x1b, 0x13, 0x7c, 0x28, 0xb7, 0x8a, 0x82, - 0xf1, 0x26, 0x20, 0xf9, 0x03, 0x31, 0xaf, 0xde, 0x36, 0xbb, 0xbe, 0xfc, 0x58, 0xac, 0x59, 0x5e, 0x61, 0x01, 0x96, - 0x35, 0x1a, 0x9a, 0xb3, 0x01, 0x67, 0x49, 0x7a, 0xaf, 0xae, 0xce, 0x9c, 0xe0, 0x9c, 0xc9, 0xed, 0x4d, 0xfc, 0xc7, - 0x4f, 0x53, 0x6d, 0xce, 0x32, 0xcb, 0xe1, 0x2f, 0x8e, 0x62, 0x67, 0x71, 0xd8, 0x6e, 0xc0, 0xfa, 0xaa, 0xe3, 0x2d, - 0xaa, 0xca, 0x56, 0xe7, 0x62, 0x26, 0x4b, 0x44, 0xe5, 0x76, 0xd2, 0xe1, 0x40, 0x37, 0x73, 0x6b, 0x1f, 0xf8, 0xdf, - 0x63, 0x17, 0x2a, 0x9d, 0xc2, 0x3f, 0x97, 0x47, 0x05, 0x17, 0x72, 0x9b, 0x6c, 0x2e, 0xb9, 0x91, 0xee, 0x58, 0x9f, - 0xbc, 0xb1, 0x33, 0x13, 0x46, 0x33, 0x11, 0x56, 0xd8, 0x0f, 0x47, 0xc0, 0x3d, 0x2e, 0x18, 0x7b, 0x2e, 0xfc, 0xd6, - 0xc5, 0x96, 0xbd, 0x77, 0x7d, 0x36, 0xf9, 0x28, 0x64, 0x01, 0xfb, 0x0d, 0x81, 0x1d, 0x68, 0xdc, 0x1c, 0x47, 0x3b, - 0x24, 0xeb, 0x08, 0xe6, 0xa2, 0x5b, 0xc9, 0x56, 0x06, 0xbf, 0x55, 0xb8, 0x9f, 0xdc, 0x05, 0x20, 0x69, 0xf5, 0xee, - 0xc7, 0x5e, 0xdf, 0x7f, 0xd5, 0xcf, 0x5b, 0xbd, 0xca, 0xd8, 0x3e, 0x19, 0xf8, 0x48, 0x83, 0xae, 0x77, 0xc3, 0xcb, - 0x95, 0x6a, 0xa2, 0xcd, 0xb8, 0x59, 0x5e, 0xc9, 0xe8, 0x0d, 0xe9, 0xda, 0xee, 0x3c, 0x54, 0x27, 0x37, 0x5e, 0xb6, - 0x4c, 0x06, 0x78, 0x55, 0x67, 0x33, 0xf9, 0x05, 0x62, 0x7d, 0x7d, 0xd3, 0xc5, 0x16, 0x9a, 0xd9, 0x23, 0xd4, 0x09, - 0x7f, 0xbd, 0x8c, 0xa2, 0x52, 0x24, 0x7a, 0x69, 0x76, 0xf2, 0x78, 0xde, 0x1b, 0x6f, 0x0c, 0x59, 0x4e, 0xa6, 0x87, - 0x20, 0xd1, 0x47, 0xf4, 0x92, 0xc5, 0xf5, 0x0e, 0x83, 0xcf, 0x73, 0x94, 0x66, 0xd9, 0xb0, 0xf2, 0x45, 0xfc, 0x8c, - 0x8e, 0x25, 0x1b, 0xf9, 0xb8, 0x85, 0xad, 0x64, 0xd9, 0xe6, 0x7e, 0xd7, 0x3b, 0x5b, 0xd5, 0xcb, 0x37, 0x1b, 0xcb, - 0xee, 0x58, 0x79, 0x7e, 0x51, 0xa9, 0x59, 0x0a, 0x3b, 0x7c, 0x8e, 0x47, 0x6b, 0xc1, 0x92, 0xdb, 0xbc, 0x1c, 0x21, - 0xbd, 0x97, 0xa4, 0xbf, 0x76, 0xb2, 0x14, 0xc9, 0x87, 0xb2, 0xac, 0xf2, 0x1f, 0x92, 0x24, 0x62, 0x55, 0x14, 0xa6, - 0x1c, 0x64, 0x9e, 0x7c, 0x28, 0x37, 0xbd, 0x78, 0xe7, 0xab, 0xc2, 0x4f, 0x75, 0xd8, 0x4b, 0xeb, 0x37, 0x20, 0x0a, - 0x66, 0x01, 0x7c, 0x21, 0xee, 0x45, 0xb3, 0xeb, 0x99, 0x3c, 0x06, 0x09, 0xf4, 0x39, 0xf0, 0x0f, 0x3e, 0xfa, 0x01, - 0x05, 0x9f, 0x8b, 0x0e, 0x8c, 0x00, 0x00, 0x72, 0xc7, 0xa1, 0xec, 0xf9, 0xbd, 0x29, 0x57, 0x28, 0xab, 0xa1, 0x5a, - 0x9b, 0xfc, 0x49, 0x73, 0x1d, 0x09, 0xce, 0x7b, 0xa4, 0xc8, 0x3f, 0xf1, 0x7a, 0x36, 0x69, 0x1a, 0x62, 0x17, 0x15, - 0x0a, 0x4c, 0x54, 0xe9, 0x24, 0x4f, 0x95, 0x2c, 0xb5, 0x2a, 0x59, 0xa3, 0x07, 0x1f, 0x76, 0xeb, 0xb5, 0x79, 0x55, - 0x1e, 0x92, 0x9f, 0x25, 0x10, 0xa6, 0x7f, 0xa5, 0x65, 0xb9, 0xbd, 0xa1, 0x4a, 0xe5, 0x20, 0x0f, 0xc1, 0x23, 0xab, - 0xfc, 0xba, 0x7c, 0xe2, 0xc1, 0x8d, 0x28, 0x7f, 0xa5, 0xcf, 0x21, 0xa8, 0x04, 0xfe, 0x5b, 0x36, 0x9b, 0xbe, 0xf2, - 0xf9, 0xf6, 0xc7, 0x73, 0x41, 0x04, 0x4f, 0x97, 0xec, 0x0d, 0xe6, 0x6a, 0x6f, 0x50, 0x9a, 0xac, 0x7b, 0x67, 0x43, - 0xcc, 0x05, 0xcb, 0x1f, 0xd1, 0xe6, 0xdf, 0x27, 0xdf, 0xec, 0x85, 0x96, 0x40, 0xd3, 0x7a, 0x0d, 0xd0, 0x32, 0xcf, - 0x3b, 0x32, 0xd8, 0x23, 0xef, 0x52, 0x1e, 0x56, 0x1c, 0x57, 0xbd, 0xe4, 0xef, 0xe1, 0x2d, 0xdc, 0x69, 0x1b, 0x29, - 0x12, 0xf5, 0x3a, 0xab, 0x14, 0x81, 0xf3, 0x1e, 0xbe, 0x9a, 0xf3, 0x74, 0xaa, 0x21, 0xf1, 0x4b, 0xc7, 0xdc, 0x86, - 0x0a, 0xeb, 0x65, 0x31, 0xbb, 0xbf, 0x7b, 0x83, 0xdc, 0x12, 0x9b, 0xdf, 0x3b, 0x6f, 0x01, 0x48, 0xb5, 0xfa, 0x94, - 0xb2, 0x23, 0xff, 0x51, 0xaa, 0x1d, 0xf0, 0x2a, 0x1f, 0xa8, 0xa0, 0x1a, 0x33, 0xa4, 0xb4, 0xe2, 0xaa, 0x13, 0x49, - 0xd0, 0xdb, 0xa2, 0x64, 0xb0, 0x91, 0x29, 0xec, 0x43, 0x5e, 0x94, 0xe8, 0xfb, 0x52, 0xc9, 0x72, 0x0b, 0xaf, 0x1c, - 0xcb, 0x5a, 0xee, 0x1b, 0x25, 0x7e, 0x98, 0x20, 0x3f, 0x0d, 0x2f, 0x32, 0xf2, 0xe4, 0x6e, 0x71, 0x24, 0xf0, 0xf1, - 0x49, 0x26, 0x82, 0x7d, 0x03, 0x79, 0x92, 0x5c, 0x3c, 0x89, 0x44, 0x65, 0x62, 0xbb, 0xe4, 0xe8, 0xe8, 0xde, 0x24, - 0xa9, 0xd7, 0xd2, 0xe8, 0x50, 0xe8, 0xb8, 0x8d, 0x42, 0x6d, 0x1d, 0xcf, 0xd9, 0x94, 0x8d, 0x47, 0x77, 0xc9, 0x76, - 0x11, 0xb7, 0x87, 0xd2, 0x08, 0x95, 0xd4, 0x26, 0xe8, 0x58, 0x9a, 0x06, 0x91, 0xc7, 0x03, 0x5b, 0x84, 0x88, 0x3e, - 0x9b, 0x4a, 0x67, 0x39, 0xc4, 0x6a, 0x3b, 0x1f, 0x58, 0x8e, 0x2d, 0x87, 0x2c, 0x09, 0x28, 0x9a, 0x95, 0x22, 0xe1, - 0x60, 0xe0, 0x38, 0x9a, 0xa3, 0x4a, 0x81, 0x31, 0x73, 0x35, 0x87, 0x9d, 0xaf, 0x33, 0x72, 0x2c, 0x8d, 0x34, 0x1b, - 0xbe, 0x2e, 0x45, 0x77, 0x6b, 0xeb, 0x63, 0x6d, 0x44, 0x32, 0xb2, 0xc9, 0x9e, 0xcb, 0xdc, 0x13, 0x56, 0xe9, 0xa9, - 0xdc, 0x8d, 0x95, 0x94, 0x15, 0xe7, 0xf9, 0x64, 0xb4, 0xdb, 0x90, 0x45, 0xab, 0x06, 0xab, 0xf1, 0x25, 0xd3, 0xee, - 0xb3, 0x2f, 0xb5, 0x0d, 0xda, 0x6a, 0x52, 0x17, 0x68, 0xce, 0xc3, 0xba, 0xc2, 0xdf, 0xc8, 0x13, 0x38, 0x93, 0x9e, - 0xbd, 0xea, 0x2e, 0x3f, 0xaa, 0x51, 0xda, 0xee, 0x2d, 0x5c, 0x31, 0x8f, 0xb9, 0x0a, 0xc1, 0xae, 0xd5, 0x44, 0x4f, - 0x66, 0x90, 0xc3, 0xf7, 0x04, 0x5c, 0x8e, 0xfc, 0x66, 0x80, 0xed, 0xd9, 0x38, 0x97, 0xb4, 0x53, 0xde, 0x27, 0x2d, - 0x45, 0x7e, 0xc6, 0x4d, 0xd6, 0x9c, 0x28, 0xff, 0x97, 0x42, 0xac, 0xb2, 0xe0, 0x0b, 0xeb, 0x23, 0xeb, 0xef, 0xd1, - 0xa9, 0x4e, 0x79, 0xeb, 0xd5, 0x8c, 0x7e, 0x2b, 0x2a, 0x4c, 0xd8, 0x3b, 0xa0, 0xc1, 0x64, 0xc7, 0x5a, 0x0d, 0xed, - 0x6e, 0xd9, 0xd1, 0xa2, 0x38, 0x6d, 0xcf, 0x68, 0x55, 0x7b, 0x21, 0x23, 0x1e, 0x7e, 0x6e, 0x34, 0x12, 0x8b, 0xa4, - 0x58, 0x40, 0xe7, 0x2b, 0xea, 0xfd, 0xb5, 0x9c, 0xd3, 0x93, 0xd6, 0x64, 0xf0, 0xa8, 0x33, 0xa7, 0xce, 0x55, 0x9f, - 0xbd, 0xde, 0xd5, 0xa1, 0xf2, 0x27, 0xe2, 0xfa, 0x13, 0x34, 0x55, 0x55, 0x73, 0xd7, 0x4a, 0x90, 0x9a, 0xb2, 0x6c, - 0xe2, 0xc8, 0xa7, 0xc9, 0xf7, 0xf9, 0x4c, 0x87, 0xec, 0xc3, 0x75, 0xa8, 0x8a, 0x17, 0x23, 0xb1, 0x05, 0x21, 0xb9, - 0x70, 0x82, 0x65, 0x51, 0xdf, 0x63, 0x29, 0x8a, 0xa3, 0x84, 0x92, 0xe1, 0x05, 0x8a, 0xc0, 0x51, 0x0b, 0x0c, 0x94, - 0xe4, 0x44, 0x1d, 0x1a, 0xc9, 0xce, 0xd3, 0xc1, 0x8b, 0x4f, 0x6c, 0xf2, 0x2d, 0xa1, 0x43, 0x3a, 0x43, 0x79, 0x05, - 0xdf, 0x8b, 0x77, 0x45, 0x9e, 0x30, 0xd5, 0xd2, 0x31, 0xcf, 0xbd, 0x66, 0xfa, 0xd8, 0x35, 0x78, 0x21, 0xba, 0x0e, - 0x97, 0x67, 0x48, 0x19, 0xab, 0x48, 0xd5, 0x34, 0xed, 0x87, 0x77, 0x87, 0x28, 0x49, 0x55, 0xb6, 0xdb, 0x7b, 0xa7, - 0x2d, 0x44, 0x09, 0xa4, 0xc9, 0xba, 0x0d, 0x8c, 0x64, 0x7a, 0xce, 0xb1, 0x2a, 0x45, 0x31, 0x86, 0x19, 0x82, 0x5c, - 0xe8, 0xb0, 0x15, 0x92, 0x4a, 0x3f, 0xca, 0x22, 0xe8, 0x26, 0x9d, 0xf1, 0x60, 0x96, 0x81, 0x51, 0xe1, 0xf8, 0xa4, - 0xde, 0x85, 0x77, 0x6d, 0x73, 0x72, 0x68, 0x15, 0x69, 0x02, 0x75, 0x2c, 0x90, 0x1e, 0xe5, 0x6f, 0xbe, 0x7b, 0x8c, - 0x6b, 0xd3, 0xaf, 0x8b, 0x55, 0x21, 0x33, 0x76, 0x02, 0x07, 0x90, 0x69, 0xbb, 0xf9, 0x9d, 0x7d, 0xa3, 0x24, 0x05, - 0x23, 0xad, 0xe7, 0x9e, 0x19, 0x5c, 0x8c, 0xc9, 0x42, 0xb4, 0xad, 0x76, 0xd3, 0x47, 0x07, 0x6d, 0x64, 0xe5, 0x35, - 0x00, 0xab, 0x24, 0x2d, 0x39, 0x1b, 0xc0, 0xc2, 0x6a, 0xc7, 0x36, 0x4c, 0x2f, 0x0d, 0x80, 0x4d, 0xbf, 0x05, 0x58, - 0xc0, 0x0b, 0xa2, 0xfd, 0xcc, 0xbc, 0xd2, 0x2c, 0xc2, 0x03, 0xef, 0x13, 0x80, 0x0d, 0xb1, 0x52, 0x51, 0x2d, 0x16, - 0x0b, 0xaf, 0x14, 0x0b, 0x5a, 0xd3, 0xcc, 0x96, 0x4e, 0xc0, 0xfa, 0x72, 0x47, 0xa1, 0x3a, 0xe5, 0xaf, 0xa8, 0xc4, - 0x9a, 0x43, 0x55, 0xf1, 0xd1, 0xfd, 0x66, 0x7d, 0x9a, 0x62, 0x91, 0xda, 0xa7, 0xd6, 0x93, 0x0c, 0xf0, 0x76, 0x8b, - 0xe7, 0xc0, 0x4b, 0xcb, 0xa2, 0xf7, 0xbd, 0x9d, 0xb5, 0x64, 0x51, 0xdd, 0x72, 0x7c, 0xd5, 0xca, 0xe4, 0x74, 0x25, - 0x97, 0x82, 0x32, 0x14, 0xf9, 0x9e, 0x27, 0x49, 0x21, 0xae, 0x4f, 0x1f, 0xcc, 0x7c, 0x84, 0x71, 0x65, 0xc6, 0x8b, - 0x6f, 0xc5, 0xc3, 0x8c, 0x27, 0xa5, 0x48, 0x6a, 0x79, 0x51, 0x01, 0xa9, 0xc6, 0x28, 0xbe, 0x20, 0x43, 0xcf, 0xf9, - 0x7a, 0xd4, 0xa7, 0xb8, 0x73, 0xb6, 0x24, 0x0e, 0xa2, 0x70, 0x98, 0x3e, 0x2b, 0x54, 0x08, 0xfe, 0x3b, 0x20, 0x26, - 0x19, 0x82, 0x00, 0x73, 0x22, 0xa1, 0x0e, 0xb2, 0xf0, 0x84, 0x67, 0x7b, 0xc9, 0x68, 0x25, 0xa3, 0x13, 0xa9, 0x32, - 0x11, 0x51, 0xf9, 0xed, 0xca, 0x26, 0x41, 0xb3, 0xec, 0xa5, 0x28, 0xdc, 0x88, 0x25, 0x11, 0x5c, 0x2b, 0x83, 0x9b, - 0x7e, 0x44, 0xa9, 0xee, 0x96, 0x86, 0xeb, 0x8c, 0x65, 0x72, 0xe1, 0x1b, 0x6f, 0xe8, 0xfa, 0xd2, 0xf5, 0x67, 0xe4, - 0xb6, 0xa8, 0xf0, 0xc9, 0x47, 0xf2, 0xc0, 0xb9, 0xbe, 0x9a, 0x66, 0x3a, 0xc7, 0xbc, 0x8b, 0x50, 0x5c, 0x63, 0xb2, - 0xcd, 0x7e, 0xdb, 0x2c, 0xc1, 0xeb, 0xfc, 0x59, 0xb2, 0x9c, 0xa6, 0x02, 0x76, 0x31, 0xf1, 0x8b, 0xa0, 0x44, 0x1b, - 0xae, 0x7a, 0xff, 0xde, 0xd6, 0x7a, 0xf7, 0xd9, 0x07, 0x1c, 0x16, 0xe5, 0x6f, 0xd4, 0xf6, 0x0c, 0x28, 0xa8, 0xe4, - 0xb9, 0xab, 0xd6, 0x80, 0xb1, 0x1d, 0xc3, 0x0f, 0x51, 0x1f, 0xed, 0x1a, 0xa0, 0xae, 0xd9, 0xca, 0x29, 0x06, 0x63, - 0x00, 0x1d, 0xdd, 0xfa, 0xd4, 0x20, 0x75, 0x58, 0xd8, 0x94, 0xd6, 0xdb, 0x58, 0xbc, 0xd0, 0x22, 0xe6, 0x72, 0x95, - 0x2c, 0xad, 0xf9, 0x1a, 0xfe, 0x37, 0xb8, 0xa6, 0xe0, 0x77, 0x94, 0xbf, 0x92, 0x78, 0xd7, 0x9d, 0xd3, 0x0a, 0x89, - 0x82, 0x79, 0x2e, 0xbc, 0x22, 0xc2, 0x4a, 0x9f, 0x10, 0x73, 0xcc, 0x75, 0x99, 0x93, 0x7d, 0xe1, 0xd0, 0x1a, 0xa9, - 0x43, 0xc0, 0xa5, 0xfb, 0x1e, 0x4f, 0x2f, 0xf8, 0x12, 0x5d, 0x1d, 0xdf, 0xcb, 0x3c, 0x9a, 0x01, 0xab, 0xba, 0x6f, - 0x31, 0x09, 0x53, 0x51, 0x46, 0x09, 0x41, 0xdc, 0x54, 0x22, 0x0b, 0x43, 0xcf, 0x1a, 0x57, 0x1f, 0x3b, 0xad, 0xa7, - 0x0c, 0x00, 0x28, 0x25, 0x09, 0xdd, 0x33, 0x94, 0x31, 0xa3, 0x97, 0x56, 0x81, 0x72, 0xcb, 0xd5, 0xc1, 0xcb, 0xce, - 0x3d, 0x86, 0x81, 0x9d, 0xd9, 0x5a, 0x67, 0x1a, 0x07, 0x22, 0xcb, 0x40, 0x80, 0x38, 0xc8, 0xb7, 0xa9, 0xd2, 0x58, - 0x74, 0x03, 0xd4, 0xb5, 0xa8, 0x0f, 0xd2, 0x8e, 0x2c, 0xc4, 0x19, 0x26, 0x3a, 0x06, 0xf6, 0xa7, 0x97, 0x68, 0xa4, - 0xa4, 0x42, 0xe0, 0x95, 0x31, 0xf3, 0x40, 0x22, 0x7b, 0xa0, 0x1d, 0x94, 0x0d, 0x80, 0x24, 0x67, 0x8e, 0x2b, 0x05, - 0x69, 0x2d, 0x23, 0x96, 0xd0, 0xff, 0x13, 0x2b, 0x8d, 0x32, 0x01, 0xf9, 0xc8, 0xa1, 0x4d, 0x49, 0xe3, 0x79, 0x78, - 0x2d, 0x1c, 0x48, 0x3e, 0x4c, 0x7f, 0x9c, 0x05, 0x8d, 0xc8, 0x94, 0xd3, 0xb9, 0x15, 0x6c, 0xe3, 0x8b, 0x3b, 0x4f, - 0x23, 0x51, 0x61, 0xfa, 0xcc, 0x77, 0x96, 0xb7, 0x93, 0x55, 0x84, 0xe5, 0x8f, 0xb9, 0xec, 0xa7, 0xe8, 0x7f, 0xad, - 0xa2, 0x24, 0xc9, 0x06, 0x5f, 0x2a, 0x99, 0x8e, 0xdb, 0xf3, 0x6b, 0xad, 0x8d, 0x96, 0xee, 0x01, 0xce, 0x78, 0x0f, - 0xaa, 0x3b, 0x12, 0x7a, 0xc8, 0x69, 0xcd, 0x53, 0x87, 0xa8, 0xaa, 0xa0, 0x84, 0x44, 0x63, 0x5c, 0xa4, 0xd6, 0x44, - 0xea, 0x9b, 0xc5, 0xd3, 0x00, 0x92, 0x69, 0x0c, 0x2a, 0xaa, 0xdc, 0x57, 0xcf, 0x6c, 0x5e, 0x4c, 0x4f, 0xa4, 0xc9, - 0x74, 0x41, 0x2e, 0x3f, 0xc5, 0xe8, 0x66, 0x4a, 0xc9, 0xb2, 0x58, 0x86, 0xc3, 0x1e, 0x23, 0xcc, 0x01, 0x43, 0x44, - 0xa5, 0xc2, 0x78, 0xc3, 0xa4, 0xbc, 0x9f, 0x94, 0xfc, 0x69, 0x8a, 0xf3, 0x0b, 0x61, 0xf5, 0x61, 0x5b, 0x07, 0xb8, - 0x3a, 0x07, 0xe5, 0x08, 0xb7, 0x96, 0x07, 0xd8, 0xd8, 0x98, 0x47, 0x7b, 0x39, 0x55, 0x25, 0x22, 0xd3, 0xac, 0x3b, - 0x58, 0x52, 0xde, 0xa4, 0xef, 0x0c, 0x99, 0xb0, 0x75, 0x33, 0x14, 0x41, 0x6e, 0x3c, 0xc9, 0xae, 0xb1, 0xd5, 0x07, - 0x81, 0xb3, 0x7a, 0x44, 0x5e, 0xc6, 0xa3, 0xaa, 0xce, 0xaa, 0xed, 0x94, 0xfc, 0x34, 0xbc, 0x4f, 0xae, 0x3b, 0xc9, - 0x09, 0x95, 0xd5, 0x24, 0x2a, 0x0a, 0x8a, 0xc2, 0xf3, 0x24, 0x20, 0x82, 0xe2, 0x8c, 0xfa, 0xa1, 0x8a, 0xfa, 0xa6, - 0xc8, 0xfe, 0x71, 0xea, 0xd5, 0xbe, 0xe5, 0x25, 0x80, 0x24, 0x3f, 0x93, 0x49, 0x77, 0x8c, 0x7b, 0x67, 0x5d, 0x1e, - 0x3e, 0x4a, 0x14, 0xe6, 0x3e, 0x14, 0x57, 0x31, 0x49, 0x51, 0x2c, 0x01, 0xf7, 0xca, 0x65, 0x2f, 0x1e, 0x49, 0x3a, - 0x41, 0x66, 0xa8, 0x5c, 0x1b, 0xef, 0x9b, 0x7a, 0xa4, 0xea, 0x49, 0x96, 0x02, 0x79, 0xe4, 0xee, 0x77, 0x46, 0x50, - 0xf2, 0xfc, 0xb7, 0xec, 0x8a, 0xd7, 0x49, 0x79, 0x86, 0x36, 0x1b, 0x22, 0x7a, 0x5d, 0x8e, 0x36, 0x05, 0xcc, 0x31, - 0x23, 0x02, 0xfd, 0x73, 0xb0, 0x0a, 0xf9, 0xb2, 0xe3, 0x14, 0xdb, 0x54, 0x01, 0x4a, 0xb9, 0xf7, 0xfd, 0x55, 0x1e, - 0x08, 0xfb, 0x33, 0x92, 0x9c, 0x49, 0x46, 0x44, 0x50, 0xb6, 0xc0, 0x23, 0xb0, 0x2f, 0xa4, 0x8b, 0x13, 0xb5, 0x0a, - 0xfb, 0x82, 0x9a, 0xb3, 0x67, 0xa9, 0x88, 0xea, 0x14, 0x7d, 0xfc, 0xca, 0xb8, 0x60, 0x2e, 0xab, 0x91, 0xb6, 0x22, - 0x5a, 0x9e, 0xaa, 0x04, 0x04, 0xb5, 0x10, 0x0b, 0xb1, 0xa9, 0x80, 0x60, 0x7c, 0xaf, 0xa7, 0x27, 0x18, 0x31, 0x0b, - 0xc5, 0x8b, 0xcc, 0xe5, 0x44, 0xbb, 0xfc, 0x87, 0x9b, 0x30, 0x9d, 0x33, 0x86, 0x34, 0x22, 0xa9, 0x47, 0xd6, 0xef, - 0x5f, 0x2f, 0x2f, 0x54, 0x65, 0x13, 0x49, 0x65, 0x23, 0xee, 0xe6, 0x73, 0x9d, 0x1b, 0xd3, 0x44, 0x70, 0xc5, 0x92, - 0xd9, 0x62, 0xe3, 0xe9, 0xfc, 0xc3, 0x95, 0x59, 0x48, 0xd7, 0x44, 0x39, 0x92, 0xc8, 0x4f, 0x0a, 0xc1, 0x43, 0x8d, - 0xf2, 0x42, 0x18, 0x91, 0xfa, 0x6f, 0x86, 0xdc, 0x75, 0x29, 0xda, 0xd5, 0x46, 0x75, 0xd9, 0x02, 0xd8, 0xd2, 0xd7, - 0x30, 0x32, 0x14, 0x42, 0x47, 0x0c, 0x92, 0xdc, 0xa5, 0x3e, 0x2a, 0x19, 0xc8, 0xa2, 0x2b, 0xcc, 0x40, 0x99, 0x7b, - 0xe8, 0xe4, 0x8d, 0x93, 0x28, 0x01, 0xb9, 0x9f, 0x99, 0x4f, 0xea, 0xec, 0x24, 0xf2, 0x62, 0x2d, 0x05, 0x74, 0xa4, - 0xba, 0x4e, 0x25, 0x56, 0x59, 0xad, 0x84, 0x9e, 0x08, 0x76, 0x17, 0xcd, 0xe0, 0x55, 0x9b, 0xe7, 0xe9, 0xb1, 0xe6, - 0x9f, 0xc7, 0x57, 0x94, 0xb7, 0x35, 0x91, 0x16, 0x74, 0x22, 0xb4, 0xfa, 0xc0, 0x7d, 0xdd, 0x5c, 0xd9, 0x1a, 0xe4, - 0x65, 0x01, 0xd0, 0x72, 0xce, 0x72, 0x7a, 0x12, 0xca, 0xba, 0x79, 0x5e, 0x26, 0x99, 0x7b, 0x15, 0x07, 0x5b, 0x40, - 0x73, 0x01, 0x37, 0xc1, 0x67, 0x1f, 0x27, 0xa4, 0x7e, 0xa8, 0x3c, 0x56, 0x36, 0xdf, 0xd6, 0x60, 0xee, 0xdb, 0xc4, - 0xe9, 0xb0, 0xd9, 0x24, 0x22, 0x26, 0x73, 0x37, 0xb6, 0xde, 0x08, 0x67, 0x2d, 0x54, 0xed, 0x11, 0xf3, 0x84, 0x00, - 0x53, 0xd5, 0x20, 0x7c, 0xda, 0xc7, 0x49, 0x4c, 0x6f, 0x11, 0x15, 0xa0, 0x5c, 0x62, 0x52, 0xaf, 0xdc, 0xa5, 0xa5, - 0xd6, 0xbd, 0x4f, 0x17, 0x58, 0x57, 0xba, 0x78, 0xbc, 0xd3, 0x7d, 0xe0, 0x00, 0x70, 0x3f, 0x83, 0xaa, 0x55, 0x5e, - 0xaa, 0xea, 0x0b, 0x6a, 0x69, 0x82, 0x94, 0x04, 0xbc, 0x55, 0x49, 0xef, 0xe7, 0x99, 0x06, 0x82, 0xe6, 0x6b, 0x64, - 0x75, 0xe4, 0x0b, 0x91, 0xc8, 0x43, 0xcf, 0x4b, 0x7c, 0xbc, 0x08, 0xcf, 0x09, 0x1e, 0xbf, 0x8c, 0xad, 0x0b, 0x3a, - 0x65, 0xfe, 0x20, 0x81, 0xe5, 0x40, 0xed, 0xda, 0xe5, 0xeb, 0x38, 0x11, 0xec, 0x14, 0x05, 0xea, 0x29, 0x2a, 0x40, - 0x83, 0x40, 0xd1, 0x48, 0x0b, 0xe8, 0x24, 0xf9, 0x39, 0xa6, 0x05, 0x84, 0xd4, 0x29, 0x10, 0x31, 0xdf, 0x0e, 0xcb, - 0x11, 0xdc, 0x95, 0x22, 0x27, 0x9e, 0x38, 0x37, 0x6b, 0xe5, 0xcb, 0x7d, 0x88, 0xaa, 0x73, 0x7f, 0x7c, 0x83, 0x3b, - 0x70, 0x15, 0xdb, 0x8d, 0xe3, 0x1f, 0x71, 0xbf, 0x49, 0x16, 0x72, 0x0e, 0x44, 0x8a, 0xbc, 0x1c, 0x11, 0x22, 0x13, - 0x87, 0x3a, 0xdc, 0x84, 0x90, 0x8e, 0x2f, 0xa0, 0x3f, 0x8e, 0x98, 0xc6, 0x56, 0x9d, 0x26, 0x20, 0xe7, 0x3c, 0xbe, - 0x3d, 0x9d, 0xde, 0xba, 0xa8, 0x1e, 0x44, 0xdb, 0x22, 0xe2, 0x87, 0xb6, 0xa8, 0x51, 0xa8, 0x3c, 0x9c, 0x5a, 0x5f, - 0x53, 0xc3, 0x31, 0xc4, 0xe1, 0xdf, 0x06, 0x48, 0x00, 0x85, 0xdd, 0x26, 0xd7, 0x5c, 0xd0, 0xe9, 0x9d, 0xa4, 0x23, - 0xb4, 0xd6, 0xf4, 0x53, 0xb9, 0x6a, 0xd6, 0xc1, 0xca, 0xb4, 0xd3, 0xfb, 0x6c, 0xe3, 0xb6, 0x38, 0x01, 0x41, 0xb4, - 0xd2, 0xeb, 0x9b, 0x30, 0x61, 0x89, 0x31, 0x06, 0xde, 0x17, 0x62, 0xce, 0x53, 0x98, 0x49, 0xcc, 0xc7, 0x70, 0xb4, - 0x3a, 0x8b, 0x77, 0x6e, 0xd1, 0xa5, 0xbd, 0xd1, 0x9b, 0x36, 0x92, 0xa9, 0x84, 0x8e, 0x05, 0xf0, 0xc7, 0xe9, 0xa8, - 0x1d, 0x71, 0x07, 0x04, 0xd8, 0xca, 0x12, 0xe3, 0xd2, 0x2d, 0xd8, 0xaa, 0x6c, 0xf9, 0xb4, 0x71, 0xae, 0xdc, 0xcf, - 0xd6, 0x2e, 0x74, 0x44, 0x70, 0x58, 0x97, 0x34, 0x07, 0xe6, 0x63, 0xc1, 0x5c, 0x8a, 0x8b, 0xd5, 0x4e, 0x01, 0x12, - 0xb4, 0x92, 0x3c, 0x5c, 0x66, 0x48, 0x7a, 0x7c, 0xa2, 0x2e, 0x12, 0x72, 0xc6, 0x8d, 0xb6, 0x06, 0xec, 0xe0, 0xdd, - 0x5e, 0x8f, 0xb4, 0xee, 0xbc, 0x45, 0xde, 0x8b, 0xe2, 0x05, 0xa4, 0x9a, 0x02, 0x71, 0x65, 0x83, 0x20, 0xed, 0x3a, - 0x25, 0xac, 0xbf, 0x19, 0x2c, 0x8d, 0xdb, 0x77, 0x6d, 0x4a, 0x0f, 0x7a, 0x76, 0xa6, 0x87, 0x5c, 0xf8, 0xb3, 0xa2, - 0x6f, 0x5f, 0x79, 0xcb, 0x36, 0xec, 0xa7, 0xa5, 0x00, 0xb2, 0xba, 0xb8, 0x1b, 0xe7, 0xbc, 0x60, 0x8b, 0xa5, 0xc9, - 0xe9, 0xab, 0x65, 0x85, 0x9a, 0xc0, 0x1e, 0x7c, 0xa0, 0x65, 0xa4, 0x52, 0x5f, 0x29, 0x29, 0x5a, 0x1e, 0x1a, 0x93, - 0x6c, 0x6d, 0x6a, 0x85, 0xb8, 0xaa, 0x06, 0xab, 0xea, 0xe1, 0x12, 0x4b, 0x4b, 0xdb, 0x52, 0x0b, 0xcd, 0x75, 0xef, - 0x05, 0x98, 0x7c, 0x8f, 0x1e, 0xb1, 0xbc, 0x00, 0xba, 0xfb, 0x85, 0x7c, 0x19, 0x87, 0x41, 0x5a, 0x54, 0x41, 0x00, - 0xe9, 0x75, 0x1d, 0xc3, 0xa6, 0x61, 0x8d, 0x89, 0x0e, 0x8b, 0x3e, 0x8d, 0x40, 0x45, 0xa8, 0x81, 0x21, 0xd8, 0x42, - 0xae, 0x4c, 0xc5, 0xd2, 0xa9, 0x97, 0xc9, 0xe2, 0xd2, 0xe7, 0x5e, 0x6c, 0x6b, 0xd2, 0x15, 0xb3, 0x54, 0x41, 0x5c, - 0x1a, 0x75, 0xbd, 0xd1, 0x37, 0x6a, 0x79, 0xd0, 0x35, 0xde, 0xe3, 0x26, 0x19, 0xd6, 0xa6, 0x72, 0x7d, 0x94, 0x6c, - 0xb7, 0xff, 0x59, 0xb9, 0x44, 0xb5, 0xe4, 0x2c, 0xad, 0xb1, 0xea, 0x61, 0x8b, 0x02, 0x5c, 0xbe, 0xe3, 0x4e, 0xc6, - 0x00, 0x59, 0x8e, 0xb4, 0x61, 0x6e, 0x1d, 0xce, 0x65, 0x1b, 0x68, 0xfb, 0xcd, 0xa7, 0x92, 0x60, 0xeb, 0x57, 0x4f, - 0xa7, 0xb1, 0x4d, 0x91, 0xc3, 0x28, 0x70, 0x14, 0x9e, 0xbb, 0xe7, 0xbc, 0x5a, 0x29, 0xe3, 0x12, 0xdb, 0xed, 0x73, - 0xd3, 0x4f, 0x5e, 0xd9, 0x86, 0xf5, 0x14, 0x5f, 0x8f, 0x91, 0x8d, 0xbd, 0xe7, 0xc9, 0x7a, 0x32, 0x16, 0x77, 0x0c, - 0x80, 0x8b, 0x92, 0x62, 0xb8, 0x5b, 0xc1, 0xc5, 0x83, 0x5f, 0xf8, 0x7c, 0x2a, 0xa7, 0x9b, 0x34, 0xee, 0xcd, 0xbf, - 0xed, 0xed, 0x85, 0x07, 0xcd, 0x24, 0x7d, 0x99, 0xa5, 0xcb, 0x2a, 0xb9, 0x16, 0xc8, 0xb5, 0x1b, 0x9e, 0x8b, 0x72, - 0xbd, 0x69, 0x6b, 0x23, 0x4c, 0x50, 0xbc, 0x1c, 0xf8, 0xdb, 0xbb, 0xf8, 0xed, 0xb9, 0xec, 0xf9, 0xb9, 0xb7, 0x68, - 0x08, 0x31, 0xdf, 0xbc, 0x8f, 0x2a, 0x2b, 0x8e, 0x63, 0xe2, 0x7d, 0x3e, 0x34, 0xde, 0xeb, 0x1a, 0x2d, 0x63, 0xae, - 0xf0, 0xf3, 0x18, 0xaa, 0xda, 0xc7, 0xcd, 0x2d, 0xff, 0x6c, 0x77, 0x9d, 0x95, 0xa2, 0x30, 0x9b, 0xc9, 0xc3, 0xd2, - 0x94, 0xb4, 0x3b, 0x0e, 0x36, 0xdc, 0x3e, 0x2b, 0x00, 0x73, 0x00, 0x2c, 0x8f, 0x74, 0x7d, 0x16, 0x7b, 0x16, 0xca, - 0xb6, 0x8b, 0x38, 0x54, 0x6f, 0x61, 0x57, 0xdc, 0x9c, 0xe5, 0x59, 0xea, 0x6e, 0xe3, 0x0b, 0x03, 0x54, 0x3d, 0xe4, - 0x8e, 0x39, 0x92, 0x96, 0x09, 0xa6, 0x4a, 0x7e, 0xbb, 0x71, 0xdc, 0xcc, 0x19, 0x3b, 0xf1, 0x0c, 0xb3, 0x79, 0xea, - 0x0d, 0x2b, 0x9a, 0xf7, 0xed, 0x2b, 0xf7, 0x34, 0x30, 0xf1, 0xad, 0x8d, 0xca, 0x54, 0xf6, 0x75, 0x00, 0x94, 0x2c, - 0xd1, 0x9f, 0x76, 0x51, 0x5a, 0x57, 0x08, 0xa3, 0xc2, 0xa9, 0xf2, 0x0f, 0xd6, 0x92, 0x56, 0x31, 0x11, 0x8b, 0xa3, - 0x23, 0xcd, 0x19, 0xe0, 0x96, 0x78, 0xcb, 0xa8, 0x03, 0xc5, 0x98, 0xd1, 0xc6, 0x4c, 0xca, 0x6a, 0x8f, 0x66, 0x07, - 0xc2, 0xc8, 0x73, 0x6d, 0x11, 0xe9, 0x28, 0x60, 0xbd, 0x54, 0x70, 0xe0, 0x37, 0xef, 0x55, 0xa0, 0x79, 0xdf, 0xb3, - 0x01, 0xe5, 0x00, 0xee, 0x37, 0x74, 0x94, 0xd4, 0xa6, 0x8d, 0xbf, 0xe4, 0x8a, 0xd1, 0xd5, 0x83, 0x24, 0xd0, 0x36, - 0x63, 0xc0, 0x07, 0x4c, 0xae, 0xa8, 0x42, 0xfa, 0x34, 0x46, 0xde, 0x28, 0x90, 0x9c, 0x63, 0xd3, 0x50, 0x4c, 0x3b, - 0xac, 0x27, 0x91, 0x94, 0x0e, 0x22, 0x64, 0x8a, 0xc5, 0xf4, 0xa0, 0x0e, 0x96, 0x64, 0xa4, 0x75, 0x2a, 0x6f, 0x45, - 0x47, 0xfd, 0x9e, 0x8d, 0xa0, 0x39, 0xb6, 0xac, 0x2a, 0xd4, 0x37, 0xcb, 0x2d, 0x13, 0x95, 0x74, 0xf3, 0x6c, 0x2a, - 0x1f, 0x97, 0x83, 0xc8, 0xa6, 0x69, 0xc7, 0x6f, 0xfb, 0xbc, 0xc7, 0x0c, 0xee, 0x62, 0x84, 0x82, 0xac, 0x6d, 0xc8, - 0x60, 0x8f, 0x3c, 0x5c, 0xd0, 0x2d, 0xfd, 0x40, 0xa1, 0xdf, 0xae, 0x96, 0x00, 0x7e, 0x4a, 0xe0, 0x2b, 0x41, 0x6f, - 0x37, 0xb9, 0x53, 0xbb, 0xce, 0x3d, 0xef, 0x13, 0xd9, 0x0b, 0x27, 0x0f, 0x92, 0x6d, 0x5b, 0xa2, 0x6d, 0xd5, 0x8d, - 0x5b, 0xfe, 0xb1, 0xc3, 0x4f, 0x4a, 0x53, 0x44, 0xad, 0x49, 0xea, 0xb4, 0xb1, 0xdc, 0x12, 0xb5, 0xa3, 0xc1, 0x51, - 0xba, 0x11, 0x5e, 0xb8, 0xdf, 0x86, 0xfd, 0x86, 0x82, 0xb1, 0x1c, 0xbd, 0x72, 0x17, 0x1d, 0x0b, 0x68, 0x1c, 0x29, - 0xe8, 0xd8, 0x1e, 0x47, 0xb5, 0x31, 0x86, 0x72, 0xcc, 0xde, 0x70, 0x0c, 0x65, 0x35, 0x06, 0x6a, 0x63, 0xeb, 0x26, - 0x74, 0x37, 0x9e, 0x88, 0xe4, 0x30, 0xa0, 0x71, 0x40, 0xea, 0x96, 0x19, 0xa9, 0xfc, 0x3a, 0x27, 0x2c, 0x10, 0x83, - 0x3b, 0xf6, 0x78, 0xa1, 0x7d, 0xc1, 0x30, 0xc4, 0x11, 0xe8, 0x16, 0x8f, 0x62, 0xb6, 0xa8, 0x0c, 0xf5, 0xe2, 0xca, - 0x5a, 0x98, 0xc0, 0xda, 0x11, 0xa2, 0x42, 0x7f, 0x6c, 0xf3, 0x5d, 0x3b, 0x14, 0xe4, 0x8a, 0x1f, 0xc6, 0xfe, 0x32, - 0xfd, 0x68, 0xe4, 0xa9, 0xa4, 0xff, 0x22, 0x8c, 0x7e, 0xea, 0x84, 0x95, 0x13, 0x40, 0xf0, 0x27, 0x48, 0x72, 0xdb, - 0x78, 0x3f, 0x4c, 0x69, 0xa6, 0xff, 0xb1, 0xb1, 0xe9, 0x8a, 0xf7, 0x43, 0x3f, 0xcc, 0x1f, 0x3a, 0x51, 0x07, 0xf9, - 0xa7, 0x5f, 0x3c, 0x74, 0x5c, 0x8f, 0xed, 0x63, 0x4c, 0xdd, 0xb1, 0xa5, 0xf9, 0x78, 0xec, 0xda, 0x4b, 0xb6, 0xdb, - 0xf6, 0xe3, 0xf0, 0x64, 0x78, 0x38, 0x64, 0x43, 0x1a, 0xb8, 0xf7, 0xfc, 0x72, 0x8e, 0x3d, 0x4f, 0xde, 0x3d, 0xf4, - 0xe9, 0x81, 0x9c, 0x8b, 0x94, 0x31, 0xd9, 0x2d, 0x9e, 0xb6, 0x5d, 0xa4, 0x34, 0x02, 0xd4, 0xd1, 0x1b, 0xe1, 0x63, - 0x41, 0xd7, 0x24, 0x55, 0xc8, 0xa0, 0x7c, 0x86, 0x49, 0xa3, 0xea, 0x0f, 0xf1, 0x0c, 0x85, 0x88, 0x83, 0xc0, 0x7f, - 0xf9, 0x67, 0x8f, 0xd6, 0x13, 0xa7, 0xd5, 0x69, 0x6d, 0x78, 0xec, 0xf7, 0x65, 0x97, 0xa5, 0x9e, 0x94, 0x51, 0xba, - 0xcd, 0xc4, 0x4b, 0x8c, 0xcc, 0x4d, 0x7e, 0xc8, 0xb1, 0x3d, 0x75, 0x0f, 0x93, 0xff, 0x42, 0x04, 0x45, 0xb8, 0xc7, - 0x02, 0x19, 0xef, 0x21, 0x50, 0x39, 0x15, 0xa2, 0x98, 0x96, 0x8b, 0xb3, 0x05, 0xb0, 0x6b, 0xf4, 0x4b, 0x64, 0x45, - 0x3c, 0x33, 0x9e, 0xdf, 0xb5, 0x36, 0xd7, 0x01, 0xfc, 0x7e, 0x6d, 0xf4, 0x64, 0x46, 0xab, 0x80, 0xac, 0xfb, 0xa0, - 0x0c, 0x2e, 0x09, 0x4f, 0xa5, 0x3d, 0x97, 0xd5, 0x58, 0xd3, 0x7e, 0xa0, 0x57, 0x33, 0xfd, 0x69, 0xfb, 0xac, 0x21, - 0x74, 0x3d, 0x9a, 0x29, 0x05, 0x54, 0xaa, 0x7c, 0x50, 0x66, 0x5f, 0x5f, 0x40, 0x38, 0xa2, 0x55, 0xc8, 0x2f, 0x15, - 0xa7, 0x87, 0xb1, 0x8d, 0x82, 0x20, 0xdf, 0x79, 0x86, 0xc8, 0x0f, 0xc9, 0x13, 0x2a, 0xec, 0xce, 0xfd, 0x02, 0xf4, - 0x45, 0x85, 0xa7, 0xf4, 0xfd, 0x69, 0x8e, 0xdb, 0xd5, 0xbc, 0x8f, 0xef, 0x03, 0x19, 0x25, 0x58, 0x46, 0xba, 0x39, - 0x74, 0xd2, 0xa8, 0x1d, 0x3d, 0xf2, 0x95, 0x48, 0x8e, 0x2e, 0xd0, 0xf4, 0x3d, 0xd6, 0x86, 0x17, 0x49, 0x4a, 0xd0, - 0xa7, 0x72, 0x2d, 0xc9, 0xb0, 0x57, 0x75, 0x60, 0x74, 0x44, 0xde, 0x5e, 0x8a, 0x0d, 0x90, 0x24, 0xd5, 0xd3, 0x12, - 0xa1, 0xfd, 0x50, 0xce, 0x7a, 0x53, 0x7e, 0x89, 0x7b, 0xf1, 0x84, 0x57, 0x46, 0x74, 0xc3, 0x5f, 0x7c, 0x13, 0xe2, - 0x5e, 0x28, 0xee, 0x8b, 0x02, 0x96, 0x25, 0x54, 0x11, 0x41, 0x6f, 0x1a, 0xa8, 0x1c, 0x0c, 0xfd, 0xb1, 0x28, 0xf0, - 0x6c, 0x05, 0x58, 0x96, 0x09, 0x29, 0x03, 0x47, 0x6c, 0x44, 0xff, 0x4a, 0x9a, 0xfa, 0x29, 0xa5, 0xb9, 0x6f, 0x49, - 0xbc, 0xec, 0x17, 0x84, 0x94, 0x37, 0x10, 0x0a, 0x82, 0x96, 0x0a, 0xde, 0x04, 0x29, 0x68, 0x4c, 0x3b, 0xcc, 0x95, - 0x41, 0xd9, 0xe3, 0xb8, 0x01, 0x2e, 0x5f, 0x39, 0xa8, 0x4d, 0xd5, 0xeb, 0x24, 0xb6, 0x2a, 0x6e, 0xf4, 0x9f, 0xe8, - 0xd6, 0xda, 0x0f, 0x07, 0x28, 0x82, 0xb6, 0x28, 0x9b, 0xf4, 0x8a, 0xc6, 0xb3, 0x30, 0x16, 0x96, 0x3d, 0x46, 0x0f, - 0x6a, 0x06, 0x4a, 0x2a, 0xac, 0x36, 0x54, 0x28, 0xe6, 0x53, 0xbb, 0x08, 0xa3, 0xf0, 0x41, 0x53, 0x19, 0x79, 0xf8, - 0xd0, 0x9d, 0x46, 0xef, 0xc6, 0x51, 0x2c, 0x72, 0x43, 0x9b, 0xd7, 0x2c, 0x45, 0xc2, 0xa4, 0x49, 0x3e, 0xbd, 0x6c, - 0xd6, 0xb3, 0x66, 0xd2, 0xb2, 0x15, 0x7a, 0xd5, 0x78, 0x63, 0x20, 0x52, 0xd4, 0x6f, 0xbe, 0x4e, 0x7a, 0xb5, 0x9e, - 0xc3, 0xec, 0x47, 0xc2, 0xf2, 0xa2, 0xe8, 0x7a, 0xa6, 0xdb, 0xbc, 0x6a, 0xa3, 0x3b, 0x73, 0xaa, 0xaf, 0xd4, 0x60, - 0x08, 0xf8, 0x95, 0x73, 0x79, 0x50, 0x26, 0xa8, 0x9c, 0xd8, 0x76, 0x0f, 0x6d, 0x46, 0x40, 0x07, 0xcf, 0xb2, 0xd3, - 0xcc, 0x97, 0xaf, 0x96, 0x49, 0x31, 0xac, 0x77, 0xa9, 0x43, 0x81, 0x97, 0x7b, 0x95, 0xfe, 0x81, 0x46, 0x95, 0x32, - 0xf2, 0x82, 0xa8, 0x3a, 0xd1, 0x5e, 0x70, 0x10, 0xc7, 0x1d, 0xfe, 0x3d, 0xe2, 0x70, 0xc9, 0x3d, 0x87, 0x1d, 0x40, - 0x4e, 0x59, 0x44, 0x3a, 0xca, 0xc7, 0x77, 0x8f, 0xbe, 0x65, 0xcc, 0x31, 0xd2, 0x65, 0xf5, 0x53, 0x11, 0x6d, 0x1f, - 0x51, 0x12, 0xe9, 0x0e, 0x07, 0xfb, 0x14, 0x21, 0xde, 0x6c, 0x8a, 0x41, 0x00, 0x2b, 0x74, 0xbe, 0x44, 0x74, 0x42, - 0x5a, 0xd4, 0x03, 0x0a, 0x87, 0xad, 0x82, 0xcf, 0x72, 0xc1, 0x09, 0x96, 0xfe, 0x10, 0x13, 0xab, 0x52, 0x24, 0x3b, - 0x34, 0xcb, 0xbf, 0x4c, 0x6d, 0xaf, 0x96, 0xa6, 0x51, 0x6d, 0x1e, 0xc1, 0x7d, 0xe3, 0xb2, 0xa4, 0x68, 0x05, 0x76, - 0x97, 0xbd, 0x54, 0xc8, 0xc2, 0x86, 0x6b, 0x2f, 0x79, 0xa6, 0x6d, 0x4b, 0x5e, 0x34, 0x78, 0x40, 0x12, 0xd8, 0x7c, - 0x01, 0xac, 0xff, 0x71, 0xb5, 0x2c, 0x43, 0x2d, 0x54, 0x35, 0x30, 0x42, 0xbe, 0xdb, 0x75, 0x04, 0xd1, 0x9e, 0x55, - 0x37, 0xbf, 0x06, 0x26, 0x5a, 0xf6, 0x26, 0xb0, 0x74, 0x90, 0x45, 0x0b, 0x81, 0x60, 0xe7, 0xfe, 0x7c, 0xed, 0xb2, - 0xd8, 0xce, 0x78, 0x8c, 0x35, 0x61, 0xe1, 0x11, 0xb9, 0x71, 0x80, 0x95, 0xc7, 0x65, 0x09, 0x42, 0x56, 0x94, 0x61, - 0x57, 0xee, 0x1c, 0x50, 0x8f, 0x85, 0x1a, 0x55, 0x08, 0xb2, 0xd6, 0x67, 0xaf, 0xa7, 0x8a, 0x35, 0xc9, 0xfd, 0x3e, - 0x28, 0x30, 0x38, 0x83, 0xbb, 0x4d, 0x45, 0x28, 0x7d, 0x48, 0xe1, 0x4f, 0x6d, 0xba, 0x3e, 0x4b, 0x7b, 0x9e, 0x82, - 0x49, 0xb1, 0x20, 0x5e, 0x2b, 0xf9, 0xe7, 0xe9, 0x2f, 0x12, 0xa8, 0x83, 0x94, 0xdc, 0x98, 0x3e, 0xe2, 0xb5, 0x11, - 0x42, 0x64, 0xac, 0xe7, 0xa0, 0x71, 0x20, 0x9c, 0x52, 0x30, 0xa8, 0x9c, 0xd9, 0x32, 0x8b, 0xe9, 0x78, 0x67, 0x4b, - 0x9d, 0x90, 0x6d, 0x0d, 0x3f, 0xf0, 0x66, 0x1a, 0xfb, 0x89, 0x70, 0xdd, 0xdc, 0xe4, 0x5b, 0x83, 0x67, 0xe8, 0x14, - 0x33, 0x7e, 0x93, 0x31, 0x14, 0xd3, 0xd6, 0x3d, 0x17, 0x4f, 0x4f, 0x4f, 0xc5, 0xa8, 0xb2, 0xb9, 0xe2, 0x61, 0xbc, - 0x1c, 0xab, 0x6a, 0x55, 0x15, 0xd3, 0x42, 0x2b, 0xab, 0xcf, 0x7f, 0x16, 0xc3, 0x25, 0xba, 0xc5, 0x70, 0xb6, 0x08, - 0x6d, 0xa2, 0x88, 0x16, 0x8d, 0x74, 0xcd, 0xd5, 0xfd, 0x4e, 0xdd, 0x95, 0xec, 0xe3, 0xab, 0x77, 0xfb, 0x1f, 0x12, - 0x46, 0xad, 0x97, 0xee, 0x14, 0x90, 0x57, 0x23, 0x9e, 0xf7, 0x5f, 0xcf, 0x29, 0xaf, 0x5a, 0x5e, 0x1a, 0x7d, 0x14, - 0x3c, 0x67, 0xfa, 0xdc, 0xd0, 0xf8, 0x45, 0xd3, 0x28, 0xcd, 0x3e, 0x50, 0x23, 0xbb, 0x81, 0xd6, 0x9b, 0xb4, 0x43, - 0xc6, 0x3b, 0x12, 0x7c, 0xb2, 0x42, 0x78, 0x69, 0xdc, 0x9e, 0x38, 0x89, 0x94, 0x62, 0x34, 0x55, 0x29, 0x54, 0xb5, - 0xce, 0x0a, 0x4d, 0x5b, 0x55, 0x21, 0xc9, 0x81, 0x03, 0xa5, 0x93, 0x21, 0xcc, 0xf1, 0xa4, 0x9c, 0xc4, 0x93, 0xa4, - 0x59, 0xcd, 0x43, 0x4e, 0x79, 0x51, 0x92, 0x86, 0xf4, 0x75, 0xe6, 0x14, 0x80, 0x66, 0x03, 0x25, 0x70, 0x28, 0x49, - 0x01, 0x66, 0x1a, 0xd2, 0x33, 0x44, 0x14, 0x82, 0x01, 0x7a, 0x73, 0x15, 0x13, 0x8f, 0x13, 0x6f, 0x1b, 0xed, 0xb2, - 0xa6, 0x20, 0x9e, 0x7c, 0xec, 0x7d, 0xb3, 0x98, 0xd6, 0x9d, 0x5c, 0x50, 0xc9, 0xf3, 0xc5, 0xd4, 0xd2, 0x04, 0xee, - 0x13, 0x32, 0xd5, 0x8c, 0xa9, 0x42, 0xfe, 0x4d, 0xee, 0xdb, 0xd1, 0x7e, 0x2c, 0x8e, 0xc5, 0xbb, 0x33, 0x34, 0xdd, - 0xcd, 0x55, 0x8e, 0xdc, 0x37, 0x23, 0xb9, 0xd5, 0xb2, 0xa6, 0x11, 0x84, 0x2c, 0x7c, 0xe1, 0x7a, 0xed, 0xf5, 0xf1, - 0x7d, 0xd6, 0xfd, 0xab, 0x0d, 0xc7, 0x8b, 0xe6, 0x25, 0x1f, 0xd2, 0x5d, 0x31, 0xb1, 0x68, 0xaf, 0xfc, 0x24, 0xa9, - 0x77, 0x6a, 0x3d, 0x66, 0xc2, 0xdd, 0x3f, 0x94, 0xa6, 0x31, 0xd3, 0x3b, 0xea, 0x78, 0x3f, 0xba, 0xc3, 0x1c, 0x8a, - 0x98, 0x6a, 0x58, 0xdd, 0x48, 0xa5, 0x5c, 0x98, 0x9e, 0x61, 0x63, 0xae, 0x4e, 0x3b, 0x4a, 0x4a, 0xd0, 0xa9, 0x5a, - 0xff, 0x51, 0x1e, 0xe1, 0x34, 0x55, 0xc1, 0x4f, 0x5e, 0x6d, 0xc7, 0xa2, 0x6b, 0x2f, 0x47, 0x6b, 0xd1, 0xb3, 0x1d, - 0xe5, 0x84, 0x7d, 0x7c, 0x8f, 0x50, 0x75, 0x7d, 0xb1, 0x3e, 0xfd, 0xb5, 0xfe, 0x56, 0xee, 0x06, 0x2a, 0x81, 0x3a, - 0x1b, 0xcb, 0xec, 0x5a, 0x13, 0x17, 0xb6, 0xbf, 0x6e, 0x53, 0xab, 0x06, 0x4e, 0xf6, 0x6a, 0xc3, 0xca, 0x9a, 0xcf, - 0x84, 0x6c, 0x7c, 0x93, 0xb2, 0x5f, 0x88, 0xe1, 0x27, 0xa9, 0x4d, 0x4d, 0x9b, 0xa4, 0xb5, 0xfc, 0x2c, 0xd7, 0xcd, - 0xdb, 0x56, 0xc4, 0xe9, 0xbe, 0x28, 0x82, 0x9c, 0x22, 0x09, 0xd9, 0xc6, 0x78, 0x84, 0xb0, 0x85, 0x0e, 0xe2, 0x5c, - 0xba, 0x88, 0xb1, 0x2c, 0x62, 0x78, 0x2f, 0x8f, 0x7d, 0x12, 0x6a, 0xda, 0x68, 0xa7, 0x2c, 0xb2, 0xff, 0x3e, 0xd3, - 0x8f, 0x8b, 0x2a, 0xa8, 0x03, 0x30, 0xbd, 0xbf, 0x6a, 0x7b, 0xb9, 0x38, 0xea, 0x37, 0x15, 0x07, 0x57, 0xff, 0x94, - 0x36, 0x37, 0x6c, 0xaa, 0xf9, 0x86, 0xa8, 0x54, 0xca, 0xbe, 0x18, 0xf4, 0x8c, 0xec, 0x55, 0xa3, 0x51, 0xcc, 0xa7, - 0xd0, 0xb2, 0x44, 0xfc, 0xf1, 0x54, 0x28, 0x6a, 0xa8, 0xe6, 0x2e, 0xe4, 0xe4, 0xd8, 0x30, 0xf6, 0x27, 0x93, 0xdd, - 0x9e, 0xb6, 0xea, 0xa7, 0xac, 0x67, 0x48, 0x87, 0x87, 0x82, 0x1f, 0xb8, 0xdc, 0x75, 0xf1, 0xa6, 0xec, 0xdd, 0xaa, - 0x45, 0x2a, 0x51, 0x10, 0x2a, 0x9b, 0x7d, 0xf5, 0x86, 0xa9, 0x81, 0x1e, 0x6a, 0xf4, 0x40, 0x19, 0x4c, 0xf1, 0x09, - 0x80, 0x9a, 0xd6, 0xe1, 0xd3, 0xd4, 0x42, 0xd9, 0x48, 0xdf, 0x0b, 0xcc, 0x30, 0xfd, 0xd7, 0x61, 0xb2, 0x42, 0x06, - 0xfc, 0xea, 0x69, 0x79, 0x33, 0xce, 0xbf, 0xe7, 0xb6, 0x87, 0xde, 0xa7, 0x7e, 0xfa, 0x2a, 0x89, 0x71, 0x0f, 0xf6, - 0xf7, 0x69, 0xe6, 0x4c, 0xc9, 0xd8, 0x51, 0x01, 0x24, 0x54, 0xdc, 0x4c, 0x61, 0x08, 0x4f, 0x17, 0x82, 0x22, 0x86, - 0xae, 0x6f, 0xd7, 0xf3, 0x3b, 0xbe, 0x62, 0x1e, 0x51, 0xbb, 0x4c, 0xd5, 0x50, 0xd2, 0xfa, 0x30, 0x1b, 0x10, 0xd6, - 0x04, 0x4f, 0x8e, 0x70, 0xc3, 0xd2, 0x55, 0x44, 0x66, 0xc1, 0x0a, 0xcf, 0xc0, 0xa9, 0x09, 0xb8, 0x6e, 0x8a, 0xcc, - 0x7b, 0x9c, 0x00, 0xce, 0xc7, 0x63, 0x1c, 0xed, 0x29, 0xe0, 0xed, 0xb2, 0xba, 0xda, 0x5b, 0x6a, 0xbd, 0x73, 0x1e, - 0xda, 0x44, 0x10, 0x95, 0xf8, 0x79, 0x36, 0x91, 0xfb, 0x07, 0x6f, 0xce, 0xab, 0xc9, 0x96, 0xa4, 0x43, 0xc9, 0xdf, - 0x41, 0xd1, 0x9b, 0xac, 0xb0, 0x92, 0x1b, 0xc5, 0x22, 0x99, 0x34, 0x02, 0x20, 0x30, 0xaf, 0xf2, 0x1d, 0x11, 0xc0, - 0x55, 0x58, 0x68, 0x34, 0x45, 0x51, 0x5e, 0x51, 0x6d, 0x9e, 0xd1, 0xee, 0xd8, 0xaf, 0xe7, 0xb8, 0x2c, 0xd7, 0x96, - 0xd4, 0x6a, 0x2c, 0xeb, 0x48, 0x8a, 0x66, 0x18, 0xbc, 0x39, 0x2f, 0x05, 0x2f, 0xf1, 0xc1, 0x3c, 0x6f, 0x89, 0xaf, - 0x54, 0x5a, 0x41, 0x23, 0xd7, 0x6b, 0x8a, 0x99, 0x03, 0x9a, 0xd3, 0x65, 0x7a, 0x97, 0xe2, 0xfd, 0xeb, 0x15, 0xbf, - 0x2c, 0x5b, 0xaa, 0xba, 0xee, 0xfa, 0x93, 0x15, 0x71, 0x5c, 0x64, 0xb1, 0x6f, 0x59, 0xb4, 0x19, 0xec, 0x10, 0xfb, - 0x31, 0xed, 0xf3, 0x28, 0xcf, 0xb5, 0xcf, 0x36, 0x3f, 0x97, 0x10, 0x47, 0x96, 0x68, 0xbd, 0x3a, 0x62, 0x3f, 0xb5, - 0x64, 0x63, 0xb9, 0xef, 0x44, 0x29, 0x76, 0xb4, 0xb8, 0x90, 0xe6, 0x42, 0x1f, 0x3c, 0xd7, 0x83, 0xa5, 0x0c, 0x7f, - 0x16, 0x57, 0xb6, 0xf4, 0xaa, 0x1c, 0xad, 0xf4, 0x9f, 0x75, 0xa3, 0x87, 0x53, 0x9b, 0x62, 0xea, 0xde, 0x47, 0xc2, - 0x34, 0xa1, 0xf9, 0xbe, 0x21, 0x36, 0x55, 0x4c, 0x14, 0x44, 0x23, 0x6d, 0x03, 0xc7, 0xfb, 0xe7, 0xf5, 0x95, 0xa7, - 0xbc, 0x94, 0xfc, 0xe1, 0x3a, 0x6e, 0x79, 0x63, 0x68, 0x32, 0xf1, 0x06, 0xad, 0x07, 0x39, 0x81, 0x6d, 0x6c, 0x9f, - 0x1e, 0x69, 0x8f, 0xc2, 0x09, 0xe9, 0x4e, 0x39, 0xb4, 0x0e, 0xd7, 0x27, 0xef, 0xd0, 0x85, 0x28, 0x8d, 0x4c, 0xfc, - 0x84, 0xf4, 0xc6, 0x69, 0x74, 0xaa, 0xab, 0x7f, 0xf2, 0xbc, 0xb3, 0xd8, 0x37, 0xb0, 0xa0, 0xde, 0xff, 0xe9, 0xc6, - 0x50, 0x62, 0x3c, 0x6f, 0x19, 0x71, 0x4c, 0x84, 0xa4, 0xdc, 0x4a, 0xbe, 0x4f, 0x22, 0x2a, 0xb5, 0x52, 0x38, 0xa3, - 0x17, 0xf4, 0x88, 0x1a, 0x2c, 0x9e, 0x9f, 0x5a, 0xe7, 0xc0, 0xa4, 0x1b, 0xe5, 0xa5, 0x51, 0x20, 0x0d, 0x22, 0x4f, - 0xcd, 0xf4, 0x0c, 0x9a, 0xb7, 0x0f, 0xaf, 0x03, 0xf7, 0x9e, 0x20, 0x9f, 0xff, 0xfe, 0x30, 0xdc, 0xde, 0x1a, 0x68, - 0x96, 0xf5, 0x39, 0x76, 0x51, 0xeb, 0x8b, 0x15, 0x7a, 0x58, 0x80, 0xdd, 0x13, 0x92, 0xeb, 0x3f, 0x05, 0xe8, 0x1a, - 0xcc, 0xb2, 0x55, 0xc7, 0xbc, 0x6d, 0xfb, 0xb7, 0xf3, 0x2a, 0xdc, 0x1d, 0x33, 0x10, 0x68, 0x77, 0xc6, 0x38, 0x87, - 0xff, 0x67, 0x89, 0x64, 0x15, 0xc6, 0xe4, 0xa2, 0xbd, 0x6e, 0x0f, 0x97, 0xc4, 0x6e, 0xb5, 0x66, 0x39, 0xd3, 0x76, - 0x60, 0xeb, 0x39, 0x2f, 0xa2, 0xd2, 0x20, 0xc1, 0x4e, 0x6a, 0x43, 0x03, 0x44, 0x32, 0xe8, 0xf6, 0x52, 0xc6, 0xbd, - 0x20, 0x9f, 0x01, 0x7d, 0x6d, 0x67, 0x2e, 0xbd, 0x31, 0x35, 0xae, 0x70, 0x52, 0x97, 0x9d, 0xbb, 0xc9, 0x70, 0xd6, - 0x3e, 0x16, 0xca, 0xd7, 0x63, 0x81, 0x2f, 0xac, 0x8f, 0xd3, 0xf4, 0xc1, 0x1d, 0xd9, 0x47, 0x93, 0x63, 0x2f, 0xa6, - 0xa4, 0x2a, 0x33, 0x18, 0x65, 0x08, 0xb4, 0x74, 0x2d, 0xcb, 0x94, 0x62, 0x8f, 0xde, 0x3e, 0x9c, 0x32, 0x6e, 0xfa, - 0x79, 0x98, 0x73, 0xd0, 0x89, 0x65, 0x8b, 0xe7, 0x15, 0xd9, 0xc3, 0xd4, 0x9d, 0x00, 0x89, 0x04, 0x61, 0xa2, 0x0b, - 0x95, 0x7a, 0x90, 0x61, 0x4d, 0x78, 0x84, 0x34, 0x71, 0x71, 0x3a, 0x32, 0x61, 0x77, 0xe4, 0x49, 0x07, 0x51, 0x07, - 0x86, 0xca, 0xd5, 0x73, 0xfe, 0xd0, 0x63, 0xb2, 0x17, 0x14, 0xd9, 0xf6, 0x48, 0xe1, 0x9c, 0x79, 0xf3, 0x21, 0x7b, - 0xe8, 0x5f, 0x37, 0xbd, 0xe6, 0x88, 0x05, 0xf7, 0xb7, 0x50, 0x81, 0x32, 0x04, 0xdc, 0x1f, 0xfa, 0xee, 0x36, 0x47, - 0xad, 0xa0, 0x33, 0x30, 0x7d, 0xb2, 0xcf, 0xf4, 0x62, 0x4d, 0x69, 0xb8, 0x6f, 0x46, 0xce, 0xe0, 0x4e, 0xd0, 0xb5, - 0x33, 0xa9, 0xb4, 0xbb, 0x7c, 0x21, 0xa8, 0xf0, 0xe1, 0x1a, 0xb4, 0x3a, 0x88, 0x9c, 0x92, 0xfe, 0x4e, 0x48, 0x75, - 0xb5, 0x29, 0x26, 0xdc, 0x40, 0xcd, 0x06, 0x8a, 0xa3, 0x70, 0xe3, 0x07, 0x89, 0x01, 0x66, 0x6e, 0xa4, 0x61, 0x25, - 0xaf, 0x9d, 0x87, 0x5f, 0xec, 0x07, 0x39, 0xcf, 0x63, 0x2a, 0xd1, 0x43, 0x9f, 0x56, 0x75, 0xfd, 0x21, 0xe6, 0x1b, - 0x6a, 0x9f, 0x41, 0x6d, 0x93, 0x10, 0xa2, 0x4e, 0xd3, 0x3e, 0xe6, 0x59, 0xf9, 0xd1, 0xc1, 0x84, 0x98, 0x7b, 0x32, - 0xd0, 0xaa, 0x5d, 0x81, 0xa5, 0xec, 0x52, 0x95, 0x70, 0xed, 0xd4, 0x6f, 0x2a, 0x69, 0x17, 0xab, 0x95, 0x57, 0xa7, - 0xd8, 0xb3, 0x7f, 0xe7, 0xda, 0xfb, 0x90, 0xf1, 0x99, 0xe8, 0x58, 0xb3, 0xda, 0xbd, 0xee, 0x27, 0xce, 0x69, 0xbc, - 0xc4, 0x46, 0x09, 0xe5, 0x87, 0x69, 0x40, 0x3c, 0x78, 0x83, 0x78, 0xd7, 0x4f, 0x6c, 0xf6, 0xe2, 0xaa, 0x2f, 0x35, - 0x5a, 0xa8, 0x3f, 0xe9, 0xc3, 0xa3, 0x1a, 0x9c, 0x3c, 0x5c, 0x86, 0x27, 0x5f, 0x79, 0x3b, 0x19, 0xe0, 0xb1, 0x12, - 0xf8, 0xdc, 0x5a, 0x02, 0x4a, 0x47, 0x24, 0xaf, 0xe4, 0x03, 0xfa, 0x7f, 0x0e, 0xcf, 0x87, 0x5d, 0x8f, 0x9f, 0x2d, - 0x6d, 0xa8, 0x45, 0x27, 0x1d, 0x61, 0x09, 0x6a, 0x7b, 0x48, 0x43, 0x88, 0x8c, 0x1d, 0x81, 0x69, 0xcc, 0x9f, 0x14, - 0x61, 0x1e, 0x81, 0xf7, 0x39, 0x03, 0x8e, 0xda, 0x96, 0xf8, 0xc2, 0x09, 0x77, 0xef, 0xf2, 0xe1, 0x37, 0xf0, 0xbd, - 0xb2, 0x4b, 0x58, 0x6e, 0xab, 0x1d, 0xbb, 0xd9, 0x04, 0x9a, 0xa3, 0x28, 0x6e, 0xbf, 0x99, 0x68, 0xd1, 0xb3, 0xc3, - 0x7e, 0x0e, 0xba, 0x97, 0xa1, 0x42, 0xf9, 0x98, 0xf6, 0x99, 0xdc, 0xaf, 0x47, 0x80, 0x22, 0xe0, 0x10, 0x43, 0x6c, - 0xff, 0xd8, 0x2b, 0x0f, 0xb5, 0x9e, 0x05, 0x04, 0x14, 0xc3, 0x9f, 0x5c, 0x70, 0x66, 0xfa, 0xe0, 0x18, 0x30, 0x39, - 0x00, 0xd4, 0x06, 0x17, 0x8d, 0xc5, 0x29, 0xfe, 0xbf, 0xf3, 0x8d, 0xe4, 0xed, 0xba, 0x38, 0x1d, 0xf1, 0x2e, 0x9f, - 0x51, 0x54, 0xcc, 0x90, 0x42, 0x0b, 0xbf, 0xe8, 0x06, 0xc2, 0x4a, 0x11, 0x0b, 0x7a, 0x2b, 0x1f, 0xdb, 0xcb, 0x63, - 0x14, 0xaa, 0xff, 0xab, 0x97, 0xec, 0x8f, 0x5a, 0xf0, 0xd8, 0xa5, 0x58, 0xde, 0xf0, 0x91, 0x53, 0xaa, 0x87, 0xbb, - 0x78, 0xb3, 0x1d, 0x06, 0x05, 0xbd, 0x1d, 0x10, 0x6f, 0xfd, 0x9f, 0x25, 0x49, 0xb6, 0xdc, 0x6a, 0x86, 0x24, 0xb9, - 0xae, 0x8e, 0x3b, 0xe2, 0xdf, 0x8f, 0x78, 0x57, 0x1b, 0x1d, 0xaa, 0xf6, 0x7c, 0x5c, 0x67, 0xfe, 0x2b, 0xce, 0xf2, - 0x86, 0xa4, 0xd3, 0xcc, 0xee, 0x6b, 0x5c, 0xce, 0x65, 0x3b, 0x99, 0x2f, 0x66, 0x77, 0xb3, 0xfd, 0xf2, 0xfd, 0x96, - 0x2a, 0x63, 0xeb, 0xf9, 0x45, 0xf3, 0x31, 0xc7, 0x1d, 0x91, 0x94, 0x65, 0x18, 0xcb, 0xf9, 0xb9, 0x4b, 0xf3, 0xe3, - 0x0f, 0xc2, 0x9b, 0x1f, 0xbf, 0x78, 0x28, 0x38, 0x9d, 0x62, 0x2a, 0x23, 0x4e, 0x95, 0xce, 0x9c, 0x24, 0x86, 0xa9, - 0x14, 0x68, 0x26, 0xba, 0xbe, 0x06, 0xc9, 0x00, 0xbd, 0x82, 0xa6, 0xc3, 0xd0, 0x9f, 0xf1, 0x01, 0xae, 0x3a, 0x79, - 0xa6, 0x92, 0xcc, 0x17, 0x8c, 0x31, 0x5e, 0xf0, 0x43, 0xbf, 0xf0, 0xe4, 0x5e, 0x3b, 0x32, 0x80, 0x21, 0x15, 0x7b, - 0xfc, 0x78, 0xd1, 0x7c, 0x79, 0x69, 0x44, 0x08, 0x55, 0xc8, 0x52, 0x80, 0xa7, 0x3c, 0x7f, 0x26, 0xab, 0xeb, 0xd9, - 0x6f, 0x36, 0x5d, 0x69, 0xb8, 0xaf, 0xa6, 0x9e, 0x2a, 0x60, 0x6c, 0xb9, 0x91, 0x8f, 0x29, 0x66, 0xd6, 0x06, 0xeb, - 0x74, 0x50, 0xab, 0xc7, 0x1c, 0xe3, 0xa9, 0xa0, 0x2e, 0xa6, 0xd4, 0x93, 0x3c, 0xd6, 0xd9, 0xf4, 0x41, 0x36, 0xb8, - 0x81, 0x71, 0xc5, 0xc9, 0x47, 0x10, 0x45, 0x13, 0x60, 0x39, 0x4f, 0x5b, 0x44, 0x11, 0x7c, 0x87, 0x66, 0x14, 0xc1, - 0x10, 0xb1, 0x88, 0x2d, 0xef, 0x56, 0xc9, 0xbc, 0xbd, 0xec, 0x72, 0x92, 0xe9, 0xb7, 0xa5, 0xcc, 0x49, 0xa2, 0xc1, - 0xc1, 0x2a, 0x9f, 0xb5, 0xea, 0xa6, 0x1f, 0xec, 0x4b, 0x28, 0x00, 0x8e, 0xcc, 0xc0, 0x81, 0x92, 0x62, 0x56, 0xaa, - 0x8a, 0x1a, 0x39, 0x08, 0x70, 0xf2, 0xc3, 0x3f, 0x54, 0x5f, 0x84, 0xa5, 0xb3, 0xdb, 0x29, 0x08, 0x3d, 0xc1, 0x08, - 0x91, 0x40, 0xe3, 0x27, 0x97, 0x6c, 0xfa, 0xef, 0xdc, 0xcc, 0x48, 0x5f, 0xfe, 0xbd, 0x9e, 0xec, 0x6d, 0x6b, 0x50, - 0x30, 0xb9, 0x1e, 0xed, 0xeb, 0x58, 0x2b, 0x96, 0x4e, 0xa8, 0x4b, 0x7f, 0x71, 0x05, 0x3e, 0xa9, 0x09, 0x91, 0xb1, - 0x62, 0xa6, 0x32, 0x6b, 0x29, 0x78, 0xae, 0x7e, 0xcc, 0x65, 0x60, 0x26, 0x52, 0xda, 0x15, 0x93, 0xa6, 0x34, 0xf3, - 0x29, 0x17, 0xd1, 0xb3, 0x67, 0x5d, 0xa7, 0xa1, 0xb5, 0x0e, 0xac, 0xcb, 0x7e, 0x88, 0xb7, 0xf9, 0xd5, 0x99, 0xa6, - 0x30, 0xca, 0xf9, 0xab, 0xf3, 0x0e, 0x8b, 0x72, 0xb3, 0xbe, 0x62, 0x3e, 0xec, 0x1d, 0xda, 0x69, 0x65, 0xf4, 0xf1, - 0x5c, 0xad, 0x70, 0xdf, 0x81, 0x90, 0xf3, 0xe8, 0x7b, 0x03, 0x1e, 0xff, 0x0a, 0xff, 0xbf, 0x3e, 0x04, 0xda, 0xb1, - 0x15, 0x0c, 0xdd, 0xf0, 0x89, 0x4d, 0x70, 0x8f, 0x86, 0x99, 0xd3, 0xd9, 0xca, 0xef, 0x43, 0x22, 0xea, 0x16, 0x70, - 0xb7, 0x8b, 0x1f, 0xd7, 0x3e, 0xc3, 0xd5, 0xc8, 0xc6, 0x18, 0x0e, 0xb9, 0x01, 0xb2, 0x84, 0xf0, 0x09, 0x09, 0x63, - 0xdd, 0x39, 0x3f, 0x38, 0xa3, 0x31, 0xbe, 0xfb, 0x5b, 0xe7, 0xf9, 0x66, 0xbc, 0x8d, 0xf9, 0x75, 0xf2, 0x4d, 0xe7, - 0x7a, 0xa0, 0xf3, 0xf4, 0xa0, 0xd6, 0x6a, 0xfd, 0xc3, 0x4d, 0xef, 0x5d, 0x0c, 0x4b, 0xb8, 0x9f, 0x3a, 0xba, 0xb9, - 0x7b, 0x13, 0x11, 0x11, 0xa8, 0x3f, 0x78, 0x68, 0xd1, 0xf3, 0x09, 0xd4, 0xe9, 0x12, 0x22, 0xfa, 0xa3, 0xcd, 0x9e, - 0xdb, 0xc9, 0x9c, 0x3a, 0x79, 0xb2, 0x8d, 0xae, 0x45, 0x25, 0x5f, 0x58, 0x2c, 0xf3, 0x3e, 0x6d, 0xdd, 0x88, 0xc8, - 0x81, 0xc4, 0x64, 0xc5, 0x36, 0xc3, 0xd4, 0xd0, 0x71, 0xea, 0x22, 0xf1, 0x3f, 0xef, 0xeb, 0xc4, 0x50, 0xf2, 0xb2, - 0xd4, 0x02, 0x0b, 0x4b, 0x55, 0xd8, 0x3e, 0xee, 0x39, 0x95, 0x85, 0x55, 0x37, 0x46, 0xbc, 0x75, 0xdf, 0x76, 0x4d, - 0xc7, 0x26, 0x8a, 0xd7, 0x5f, 0xbf, 0x02, 0xad, 0x21, 0x3d, 0x16, 0xf1, 0x7e, 0x91, 0x8e, 0x63, 0x00, 0xde, 0x31, - 0x74, 0x0b, 0x77, 0xcb, 0xb2, 0x6a, 0xcf, 0xfb, 0x74, 0x0c, 0x25, 0x45, 0xb1, 0x94, 0xdc, 0x3d, 0x62, 0xeb, 0x71, - 0x94, 0xe0, 0xa9, 0xee, 0x3d, 0xbd, 0x45, 0x2a, 0x91, 0xa5, 0xa3, 0xf4, 0xd8, 0xcf, 0x29, 0x60, 0xea, 0xa5, 0xf8, - 0x7d, 0xf4, 0x68, 0x59, 0x32, 0x40, 0x8b, 0x8d, 0x58, 0xe5, 0x1d, 0x5b, 0xc1, 0x5a, 0x9c, 0x92, 0x63, 0xbc, 0xed, - 0xdd, 0x97, 0x54, 0xca, 0x5d, 0xcc, 0x6c, 0x94, 0x76, 0x6a, 0xbc, 0x1c, 0x1c, 0xa7, 0xa5, 0xb0, 0x22, 0xc6, 0x18, - 0x39, 0xbb, 0x12, 0xb4, 0x35, 0x42, 0x77, 0xb8, 0x66, 0x89, 0xff, 0xbe, 0xac, 0x2d, 0x6e, 0x25, 0x90, 0x91, 0x2f, - 0xc3, 0x37, 0xe5, 0x9b, 0xa0, 0xad, 0xfe, 0x62, 0x8f, 0xbe, 0x56, 0x10, 0x32, 0xe1, 0x57, 0x7c, 0x35, 0xba, 0xe6, - 0xf6, 0x7d, 0xd9, 0x4d, 0x56, 0x69, 0x92, 0x9d, 0x40, 0x6b, 0x93, 0xca, 0xb9, 0xf0, 0xf0, 0x39, 0x77, 0x47, 0x92, - 0x3e, 0x7d, 0x2a, 0xcc, 0x28, 0x79, 0xc9, 0x54, 0x50, 0x3a, 0xc8, 0x66, 0x7f, 0x82, 0x25, 0xa8, 0x87, 0x7c, 0x41, - 0x6d, 0xdd, 0xe3, 0xe9, 0xf3, 0x1a, 0x88, 0xeb, 0x65, 0xc3, 0x0a, 0x44, 0x22, 0xfa, 0x6f, 0xb3, 0x8f, 0x3e, 0x64, - 0x73, 0x42, 0xf6, 0xfa, 0x66, 0x8e, 0xd3, 0x9d, 0x44, 0x28, 0xca, 0x1d, 0xb7, 0x03, 0x4a, 0x29, 0x0e, 0x4a, 0xd5, - 0xf0, 0xd8, 0x2c, 0x91, 0x63, 0xe6, 0x07, 0xa7, 0xbb, 0xd8, 0x4f, 0x5c, 0x8b, 0x5f, 0xd8, 0xb1, 0x53, 0x79, 0xf3, - 0xcf, 0xbe, 0x7c, 0xd9, 0xc7, 0x83, 0xc8, 0xe8, 0x0f, 0x42, 0x11, 0x5f, 0xf6, 0x9b, 0x26, 0xf1, 0xe2, 0x17, 0xdf, - 0xd2, 0x53, 0x3c, 0xf7, 0x6b, 0x75, 0x11, 0xb7, 0x75, 0xf7, 0xbe, 0x8a, 0x76, 0x29, 0xb1, 0xe1, 0x36, 0x0c, 0x4f, - 0x93, 0xe4, 0xf4, 0x00, 0xe0, 0x03, 0xce, 0xe5, 0x3f, 0x73, 0x14, 0xca, 0x47, 0x2e, 0xc3, 0xf9, 0x62, 0x11, 0x62, - 0x4c, 0xfe, 0xc6, 0x18, 0xa5, 0x35, 0x6f, 0x9f, 0xb7, 0x77, 0xbf, 0x71, 0x6c, 0x78, 0x6d, 0xbc, 0x89, 0x86, 0x8a, - 0x16, 0xe5, 0x4d, 0xe1, 0x53, 0x5e, 0x17, 0x76, 0x79, 0xaf, 0xf0, 0x98, 0xf7, 0x0b, 0x4f, 0xf9, 0x60, 0xed, 0xd1, - 0x68, 0x45, 0x48, 0xc1, 0xb5, 0x40, 0xd6, 0x85, 0x42, 0x97, 0x71, 0x04, 0xf7, 0x94, 0x17, 0x6d, 0xcd, 0xef, 0xd0, - 0x44, 0x96, 0xff, 0x07, 0x62, 0x85, 0xd5, 0xe9, 0x07, 0x4d, 0xf1, 0x0a, 0xc4, 0x58, 0xe6, 0x58, 0x8a, 0xd5, 0xed, - 0x7f, 0xd6, 0x52, 0x31, 0x1e, 0x73, 0xb6, 0x99, 0x81, 0xbe, 0x5a, 0xbe, 0xc2, 0xc6, 0x40, 0xe3, 0xeb, 0x4d, 0x69, - 0xf5, 0x1a, 0x58, 0x8b, 0xfd, 0x7c, 0x4d, 0x23, 0x59, 0x89, 0xb0, 0x52, 0xe5, 0x61, 0x60, 0xa2, 0x2a, 0xf3, 0x8c, - 0x74, 0x04, 0xc5, 0xf3, 0xe9, 0x0b, 0xbe, 0x72, 0xd4, 0xda, 0x67, 0x05, 0xa8, 0x86, 0xc7, 0x42, 0x47, 0x2f, 0x8c, - 0xec, 0xea, 0xba, 0xa5, 0xa6, 0xb6, 0x67, 0x5f, 0x12, 0x6b, 0xe4, 0xb7, 0xe3, 0x67, 0x52, 0x24, 0xb4, 0x6c, 0xfc, - 0x3e, 0x8f, 0x77, 0xb1, 0xf7, 0x95, 0x86, 0x34, 0x40, 0x68, 0x9d, 0x90, 0x59, 0xd4, 0x74, 0xc1, 0x4b, 0xc2, 0xa7, - 0xa5, 0x8f, 0xe9, 0x47, 0xc7, 0xfb, 0x8b, 0xaf, 0xf0, 0x00, 0x47, 0x5a, 0xbb, 0xd8, 0xe4, 0xc7, 0xe3, 0x02, 0x7e, - 0xed, 0x37, 0x1d, 0x0a, 0x6b, 0xc6, 0x2a, 0x97, 0xde, 0xb4, 0xab, 0x8b, 0xe0, 0x6b, 0x4b, 0x9f, 0xf1, 0xb8, 0x7f, - 0xec, 0x4d, 0x1d, 0xef, 0x4f, 0x7a, 0x04, 0xbe, 0x01, 0x28, 0x15, 0x35, 0x88, 0x7d, 0x10, 0x7a, 0xbc, 0xb3, 0x2a, - 0x82, 0xcb, 0xf0, 0x38, 0xa4, 0xed, 0xf9, 0x32, 0xb3, 0xab, 0xc7, 0xf8, 0x8d, 0x90, 0x04, 0xdd, 0xf0, 0x4e, 0x5a, - 0x12, 0xa0, 0xf4, 0x51, 0x09, 0x93, 0x1c, 0xb1, 0xcf, 0x2f, 0x5a, 0xf6, 0xa6, 0x8d, 0x4e, 0xe1, 0x5b, 0x8f, 0x98, - 0x67, 0x6d, 0x99, 0xf3, 0x9f, 0x06, 0x71, 0x30, 0x93, 0xa3, 0xf8, 0xfd, 0x10, 0xe7, 0x45, 0x15, 0x75, 0xe9, 0xc5, - 0x6c, 0x6f, 0x03, 0xb6, 0xf0, 0xbb, 0x0f, 0xb3, 0x81, 0xef, 0x4f, 0x7d, 0xb9, 0xd6, 0xa1, 0x9e, 0xd1, 0xfd, 0x56, - 0x75, 0xdb, 0xc7, 0x91, 0x75, 0xf2, 0x9c, 0xc5, 0xc3, 0xe8, 0xdd, 0xf7, 0x85, 0xaf, 0x71, 0x66, 0xb4, 0xf8, 0x24, - 0x2a, 0x0a, 0x2b, 0x97, 0x41, 0xb9, 0x7c, 0x4d, 0x55, 0xb5, 0x47, 0x9b, 0x2f, 0x62, 0x74, 0x5e, 0xfc, 0x5e, 0xa7, - 0x8f, 0xba, 0xc6, 0xeb, 0x48, 0xf9, 0x68, 0x5f, 0x16, 0xc3, 0x1f, 0xac, 0x20, 0xb4, 0x98, 0xd8, 0xec, 0xb1, 0x5f, - 0x8e, 0x16, 0xa7, 0x67, 0x69, 0x33, 0xec, 0x34, 0x6d, 0xb5, 0x71, 0x3b, 0xd8, 0x6f, 0x1d, 0xd2, 0x92, 0xc4, 0x8b, - 0xf1, 0x15, 0x2a, 0x7f, 0xc0, 0x43, 0xec, 0x39, 0x48, 0xd0, 0x88, 0x35, 0xe7, 0xb7, 0xc8, 0x75, 0xba, 0x16, 0x48, - 0x5d, 0xf8, 0x7a, 0xe8, 0x61, 0xd2, 0x22, 0xd5, 0x41, 0x59, 0x06, 0xba, 0x89, 0x02, 0xfa, 0x9e, 0xba, 0x2d, 0xc8, - 0x45, 0xf6, 0xf7, 0x9c, 0x9d, 0xbe, 0xc6, 0xfb, 0x73, 0x0b, 0x3b, 0x51, 0xf8, 0xcd, 0x1f, 0x93, 0x18, 0xd6, 0xdc, - 0x76, 0x91, 0x2d, 0x82, 0xde, 0x6c, 0x5a, 0x3e, 0x28, 0x07, 0x6c, 0x7e, 0x69, 0xa1, 0xca, 0xc8, 0x11, 0xeb, 0xf9, - 0x6f, 0xf7, 0x63, 0x97, 0x98, 0x57, 0x41, 0xa8, 0x5e, 0xa9, 0x2a, 0x31, 0x80, 0x3e, 0xa9, 0x3d, 0x03, 0x75, 0x66, - 0x76, 0x55, 0xe9, 0xf5, 0xeb, 0xac, 0x3e, 0xd4, 0xee, 0x02, 0xf7, 0x4e, 0xc3, 0xb3, 0x13, 0x6b, 0x25, 0x8b, 0xe8, - 0x23, 0x24, 0x61, 0x02, 0xfd, 0x7e, 0xd7, 0xb5, 0xaf, 0x7b, 0x3a, 0x96, 0x05, 0x94, 0x89, 0x3a, 0x5c, 0x9c, 0x20, - 0x18, 0x3f, 0xc8, 0x71, 0x80, 0x6d, 0xe4, 0xc7, 0x2e, 0x8b, 0xab, 0xfe, 0x1c, 0x28, 0x92, 0xa0, 0xb9, 0x96, 0xfb, - 0x35, 0xb8, 0xaf, 0xef, 0x74, 0x93, 0x15, 0xd9, 0x65, 0x98, 0x33, 0xde, 0x30, 0xc6, 0x08, 0x51, 0xc5, 0x22, 0x9e, - 0xe7, 0xb8, 0x81, 0xe5, 0x71, 0x09, 0xde, 0x58, 0xce, 0x3b, 0xa3, 0xda, 0xf2, 0x6c, 0x80, 0xa6, 0xb4, 0x62, 0x1b, - 0x95, 0x6a, 0x65, 0x0c, 0x0c, 0x64, 0xcb, 0x4e, 0xa6, 0xef, 0xa9, 0x2c, 0xc6, 0xfb, 0x77, 0x47, 0x04, 0x37, 0x3d, - 0xca, 0x7c, 0x7d, 0x10, 0xc6, 0xd0, 0xdc, 0xc3, 0xa0, 0x62, 0xb7, 0x4d, 0x39, 0x06, 0x17, 0x5c, 0x74, 0xa2, 0x26, - 0x35, 0x94, 0x45, 0xb5, 0x8c, 0x14, 0x5e, 0xcd, 0x8a, 0xbe, 0xee, 0x69, 0xf1, 0x5a, 0x84, 0x18, 0x94, 0xe1, 0xba, - 0x24, 0x21, 0x54, 0x26, 0x08, 0x7d, 0xa8, 0x30, 0xa5, 0xc2, 0xeb, 0x94, 0x80, 0xfd, 0x3d, 0xcf, 0x79, 0xdd, 0xfb, - 0x5d, 0x3b, 0x2c, 0xb3, 0xe4, 0xb8, 0xd7, 0x70, 0xbb, 0x82, 0xbb, 0x23, 0xcf, 0x46, 0x76, 0x6b, 0x64, 0xf2, 0xbe, - 0x56, 0x0c, 0xe9, 0xb6, 0x60, 0x2a, 0x2e, 0x8a, 0x68, 0x95, 0xc5, 0xb8, 0x1d, 0xf8, 0x95, 0xbb, 0x45, 0xb3, 0x9e, - 0x3a, 0x93, 0xf5, 0x86, 0x21, 0x7c, 0x1a, 0x96, 0xb1, 0x84, 0x58, 0xbd, 0x1e, 0xf9, 0x7f, 0x97, 0x85, 0x47, 0x45, - 0xbb, 0x4f, 0x28, 0xc4, 0xbd, 0xc9, 0x8c, 0x37, 0x03, 0x70, 0x90, 0x63, 0x88, 0x63, 0x70, 0xa0, 0xb5, 0xac, 0xd0, - 0xa9, 0x91, 0x80, 0x88, 0xb5, 0x25, 0x7f, 0xd3, 0x5b, 0xec, 0x2a, 0x7a, 0x6d, 0xdb, 0x77, 0x8e, 0x7f, 0xfe, 0xb6, - 0xda, 0xd6, 0x4d, 0x2c, 0xe4, 0x9d, 0x91, 0x41, 0x3d, 0xb0, 0xbf, 0xef, 0x88, 0x13, 0x6d, 0x81, 0xc0, 0xd5, 0x07, - 0xd3, 0x62, 0x7d, 0xbc, 0x10, 0x31, 0x3f, 0xf8, 0x18, 0x26, 0xf1, 0x14, 0x1d, 0x7d, 0xc6, 0xe7, 0x86, 0x8f, 0xc2, - 0x0f, 0xff, 0xb3, 0x1c, 0x58, 0x99, 0x74, 0x24, 0xa7, 0x8e, 0xa9, 0x8e, 0x02, 0x02, 0xe8, 0x4c, 0xee, 0x91, 0xef, - 0xbf, 0x3a, 0xb4, 0x54, 0xb1, 0x6c, 0x3a, 0x43, 0xb3, 0x93, 0x4e, 0xac, 0x5b, 0xcc, 0x06, 0x9f, 0x38, 0xf7, 0x8b, - 0xcb, 0x0f, 0xe9, 0xc9, 0x61, 0x7f, 0x7b, 0xd2, 0x68, 0xd3, 0x63, 0x46, 0x03, 0x60, 0x0c, 0x2b, 0xfd, 0x78, 0x90, - 0xd2, 0xeb, 0x27, 0x6a, 0xa2, 0x65, 0x43, 0x78, 0x66, 0x3c, 0xba, 0x0c, 0x91, 0xfe, 0xc3, 0xa0, 0x78, 0xd8, 0x6c, - 0xbd, 0x32, 0x5f, 0xb0, 0x9a, 0x83, 0xd1, 0x0b, 0x82, 0x66, 0xc3, 0x16, 0x8b, 0xca, 0xea, 0x71, 0x7e, 0x84, 0x59, - 0x50, 0x00, 0x3e, 0x65, 0x6d, 0x80, 0xfe, 0x39, 0xe6, 0x98, 0x0b, 0x88, 0x46, 0xa3, 0x36, 0x52, 0x6d, 0xf5, 0xbc, - 0xe2, 0x9f, 0xa9, 0x38, 0x50, 0xeb, 0x3d, 0x39, 0x66, 0x7b, 0xca, 0xea, 0x6a, 0x93, 0x4a, 0x03, 0xb4, 0xbe, 0x4c, - 0xf0, 0xb5, 0x0e, 0xb5, 0x04, 0x72, 0x56, 0xc0, 0x67, 0x96, 0x56, 0x97, 0xd9, 0x3d, 0xe7, 0xf8, 0xbd, 0x78, 0xf7, - 0xa0, 0x33, 0xee, 0x36, 0xdf, 0x6d, 0x06, 0x3b, 0x2b, 0x91, 0xdf, 0x0f, 0x1c, 0xb0, 0xf5, 0xce, 0xf1, 0xb2, 0x16, - 0x78, 0xbf, 0x85, 0x41, 0x00, 0xf2, 0x7e, 0x81, 0x5d, 0xd2, 0x38, 0x0d, 0xf3, 0x95, 0xb6, 0x94, 0xc6, 0xb8, 0x72, - 0xfc, 0x94, 0x33, 0xff, 0x3f, 0xd4, 0x58, 0x19, 0xc7, 0x4f, 0x6c, 0x80, 0x76, 0x15, 0x20, 0xc9, 0x01, 0xd1, 0xc1, - 0x93, 0x16, 0x8f, 0xdf, 0x08, 0x0a, 0xfd, 0x6f, 0xae, 0xf9, 0xf5, 0x86, 0x41, 0x6c, 0x7b, 0x84, 0xf0, 0x0b, 0x6d, - 0xd8, 0xfc, 0x4d, 0x67, 0xcd, 0x25, 0x44, 0x72, 0xfd, 0x1d, 0x29, 0xa9, 0xab, 0xe7, 0x91, 0xfb, 0x93, 0x06, 0xc0, - 0xa4, 0xb2, 0xfa, 0x3a, 0xed, 0xf9, 0xc2, 0xeb, 0x79, 0x07, 0xb1, 0x19, 0xc7, 0xef, 0x8e, 0x98, 0xf8, 0x50, 0x54, - 0xd5, 0x59, 0xd4, 0xb4, 0x3a, 0xf6, 0xd6, 0x49, 0x07, 0x3a, 0x71, 0x41, 0xf0, 0x18, 0xbf, 0x04, 0xfb, 0x79, 0xf3, - 0x43, 0x42, 0x1d, 0xbf, 0xeb, 0x87, 0xe4, 0x7a, 0x37, 0x85, 0x07, 0x76, 0xc0, 0xf7, 0xf0, 0xc1, 0xda, 0x44, 0xd3, - 0xb9, 0x10, 0x1f, 0x42, 0x52, 0x11, 0x90, 0xf5, 0x24, 0x4e, 0x6e, 0x4a, 0x92, 0x60, 0xc3, 0x5e, 0xd6, 0xb6, 0x82, - 0xc3, 0xb9, 0x76, 0x87, 0x22, 0x9c, 0x46, 0x07, 0xdd, 0x0c, 0x8f, 0x38, 0xe3, 0xa4, 0x6e, 0x65, 0xea, 0xb3, 0x6d, - 0x10, 0x89, 0x91, 0x70, 0x05, 0x04, 0x9f, 0x08, 0x1e, 0x8c, 0x98, 0x1a, 0x20, 0xa9, 0x08, 0x70, 0xfd, 0xb0, 0x8d, - 0x50, 0x76, 0x3f, 0xe5, 0x27, 0x7c, 0x12, 0x43, 0x0e, 0x39, 0xac, 0xc3, 0xf3, 0xe7, 0x70, 0xd1, 0x50, 0x2c, 0xce, - 0x1c, 0x67, 0x5e, 0x94, 0xd5, 0xb4, 0x50, 0x9c, 0x58, 0xf9, 0x82, 0x07, 0x5c, 0x6f, 0xc0, 0xbc, 0x9d, 0x0a, 0x76, - 0xc6, 0x33, 0x5e, 0x61, 0x4a, 0x4c, 0x6f, 0x77, 0xce, 0x2b, 0x5d, 0xb9, 0x55, 0x14, 0xaf, 0x1a, 0xb4, 0x67, 0x46, - 0x5c, 0xf8, 0x3b, 0xad, 0x8d, 0x6e, 0xd9, 0xa5, 0x71, 0xf8, 0x37, 0x4a, 0x24, 0x04, 0x9b, 0x9f, 0x78, 0xe3, 0x3d, - 0xb4, 0x6b, 0xdf, 0x05, 0x87, 0x59, 0x7e, 0xfb, 0x1a, 0xfd, 0xe9, 0x4d, 0xcf, 0xb0, 0x28, 0xbd, 0x9f, 0x99, 0x83, - 0xea, 0x40, 0x56, 0x57, 0x87, 0x03, 0x0c, 0xda, 0xe1, 0x8e, 0x57, 0x90, 0x6e, 0xc5, 0x2c, 0x43, 0xa4, 0x33, 0x19, - 0xfd, 0xdd, 0x8b, 0x79, 0xc1, 0x3a, 0x04, 0x66, 0x1f, 0x0d, 0x73, 0x02, 0x17, 0xab, 0x0c, 0x0a, 0xa1, 0x0a, 0x21, - 0x7c, 0x1c, 0xe6, 0x8a, 0x9c, 0x06, 0x52, 0xe1, 0x8a, 0x9c, 0xfa, 0xa4, 0x83, 0x72, 0x1d, 0x3a, 0x5f, 0xad, 0x71, - 0x3c, 0xc5, 0x84, 0xbe, 0x18, 0x78, 0xa8, 0xaf, 0xd8, 0x2c, 0x3e, 0xf7, 0x42, 0x64, 0xfd, 0x0d, 0x98, 0xdc, 0xe0, - 0x65, 0x75, 0x9f, 0x85, 0x10, 0xb3, 0x70, 0x99, 0x19, 0xa9, 0x5f, 0x8a, 0x5a, 0x4f, 0xa3, 0x11, 0xa0, 0xd6, 0x3c, - 0xa0, 0x55, 0xcb, 0x10, 0x61, 0xfc, 0x25, 0xb4, 0xf4, 0x7b, 0xed, 0xe0, 0x86, 0x5f, 0xc5, 0x34, 0x1c, 0xc3, 0xfc, - 0x47, 0x11, 0x7a, 0x88, 0x01, 0x97, 0x71, 0x4d, 0xad, 0x5c, 0x8d, 0x06, 0xb9, 0x62, 0x7c, 0x01, 0x90, 0x32, 0x18, - 0x60, 0xac, 0x59, 0x28, 0x9e, 0x7f, 0xc7, 0x1f, 0x82, 0x08, 0xf5, 0x6a, 0x1f, 0xfb, 0xd1, 0x0d, 0x31, 0xa6, 0x36, - 0x3e, 0x26, 0x38, 0xf8, 0xd8, 0x5a, 0x69, 0xdf, 0x74, 0x95, 0x35, 0xc2, 0x09, 0xb4, 0xe0, 0xca, 0x3c, 0x88, 0x0f, - 0xa7, 0x36, 0xff, 0x2f, 0xc5, 0xaa, 0x1e, 0xbb, 0xfb, 0xfb, 0x23, 0x5c, 0x0f, 0x9d, 0x72, 0x90, 0x57, 0xb8, 0x00, - 0x2e, 0xbb, 0xea, 0x9c, 0x57, 0xbe, 0xb2, 0x4c, 0xfe, 0x16, 0x0e, 0x96, 0x0f, 0xca, 0x71, 0x3a, 0xfd, 0xcb, 0xb5, - 0x8b, 0xa3, 0x3d, 0x98, 0x4f, 0xd3, 0x30, 0xfe, 0x49, 0x2c, 0x7d, 0x5e, 0xd0, 0xd9, 0x6f, 0x48, 0x1b, 0x3f, 0x2e, - 0xb2, 0x7d, 0xe8, 0xba, 0x3c, 0x7f, 0x8d, 0xb7, 0xe7, 0x76, 0x4d, 0x9b, 0xce, 0xf7, 0x3f, 0xa5, 0xb3, 0x71, 0xcf, - 0xf8, 0x6f, 0xf4, 0x44, 0x27, 0xdf, 0x18, 0x7f, 0x48, 0x6b, 0xe3, 0xd3, 0x20, 0xbe, 0x6c, 0x0b, 0xb2, 0x87, 0x73, - 0x78, 0x1a, 0xce, 0x17, 0x94, 0x5f, 0x64, 0x71, 0xd1, 0x9f, 0xbe, 0xc6, 0x8b, 0x73, 0xcf, 0xcb, 0xb5, 0xd6, 0x7c, - 0x6a, 0x6d, 0xc0, 0xd6, 0x02, 0xe7, 0x46, 0xed, 0x96, 0x49, 0xaa, 0x56, 0xde, 0x88, 0xe9, 0x6c, 0x1a, 0x51, 0x07, - 0xfb, 0x7d, 0x7b, 0xdc, 0xf1, 0x40, 0xff, 0xb3, 0x79, 0x5d, 0x71, 0x6d, 0xd5, 0x4d, 0x77, 0x56, 0xe0, 0x0d, 0x93, - 0xa5, 0x23, 0x3c, 0x2b, 0x88, 0x34, 0xd2, 0x07, 0xa4, 0x65, 0x6d, 0xdb, 0x12, 0x43, 0xbb, 0x59, 0xc9, 0x34, 0x71, - 0x5b, 0x33, 0x5c, 0xe2, 0x4c, 0x08, 0x10, 0x49, 0xa6, 0x18, 0xba, 0xd6, 0x0c, 0x90, 0xde, 0x41, 0x49, 0x88, 0x65, - 0xbf, 0x04, 0x8a, 0x25, 0x83, 0x4f, 0xff, 0x61, 0x45, 0x4c, 0x8e, 0x37, 0x74, 0x70, 0x2a, 0x68, 0xf6, 0xd8, 0x8e, - 0xb9, 0x08, 0xc2, 0x97, 0x28, 0xf4, 0x4c, 0x63, 0x27, 0x57, 0x6d, 0x8e, 0x9e, 0xd8, 0x09, 0x6b, 0x1a, 0x05, 0x55, - 0xbb, 0xdf, 0xde, 0x2a, 0x15, 0x37, 0x57, 0x9c, 0xcf, 0x60, 0x8c, 0x27, 0x1d, 0x41, 0xe4, 0xcf, 0xfe, 0x02, 0xca, - 0xd0, 0x25, 0x8c, 0xb2, 0x65, 0xde, 0x8f, 0x26, 0xb7, 0x52, 0xc7, 0x92, 0xd0, 0xd4, 0xf5, 0xea, 0x8a, 0x54, 0xe1, - 0xfe, 0x2e, 0xfc, 0xb3, 0x06, 0x71, 0x87, 0x38, 0x87, 0x64, 0x01, 0x51, 0x3d, 0x63, 0x25, 0xc5, 0x20, 0x66, 0x36, - 0x28, 0x61, 0x4a, 0x9f, 0xb4, 0xda, 0x6a, 0x9d, 0x1c, 0x7b, 0x5c, 0xae, 0xea, 0x42, 0xd6, 0x2d, 0x7f, 0xa4, 0x45, - 0x22, 0x2d, 0x70, 0x85, 0xef, 0x2c, 0x00, 0x5d, 0x09, 0xe0, 0x29, 0x04, 0x72, 0x98, 0x84, 0xbf, 0x95, 0x55, 0xf4, - 0xe0, 0xfe, 0x6d, 0x98, 0x5b, 0x8e, 0x40, 0xc2, 0x87, 0xb9, 0x69, 0x8d, 0x3a, 0x8d, 0x4c, 0x6b, 0xd8, 0xba, 0x04, - 0xe2, 0x24, 0x41, 0x0b, 0x35, 0xf6, 0x71, 0x28, 0x1c, 0x7a, 0x1e, 0xb9, 0x49, 0xae, 0xe5, 0xca, 0x97, 0xa2, 0x39, - 0x89, 0x3d, 0x52, 0xd1, 0xb1, 0x9f, 0x91, 0xe3, 0xbc, 0x10, 0xe4, 0xe2, 0x48, 0x9a, 0x9e, 0x6a, 0x92, 0x43, 0x9b, - 0x0c, 0x2a, 0x94, 0xdb, 0x2c, 0x68, 0x73, 0x1b, 0xb1, 0xbf, 0x8e, 0x88, 0x0b, 0x1b, 0x40, 0x22, 0x9c, 0x5c, 0x55, - 0xfd, 0x2d, 0xb9, 0xbe, 0x6e, 0x7c, 0x55, 0x0b, 0x19, 0x0f, 0x28, 0x19, 0x4e, 0xea, 0xed, 0x19, 0x0a, 0xc3, 0xc5, - 0xfc, 0xb4, 0xbe, 0xb0, 0xd6, 0xd4, 0x6e, 0xa5, 0x48, 0x0a, 0x43, 0x9a, 0xf2, 0x44, 0xe2, 0x87, 0x65, 0x77, 0xb1, - 0x49, 0xc5, 0x8a, 0xc0, 0xfb, 0x9c, 0xf9, 0x73, 0xe1, 0xd4, 0x1a, 0xff, 0x21, 0xc0, 0xad, 0x39, 0x38, 0xa8, 0xbf, - 0x8b, 0xdc, 0x64, 0xab, 0x1e, 0x38, 0x4d, 0x7e, 0x74, 0x45, 0x3f, 0x8b, 0x62, 0xdc, 0x83, 0x41, 0x9e, 0xb3, 0x46, - 0x1c, 0x27, 0x5e, 0xa1, 0xc8, 0xa6, 0x12, 0xba, 0xdb, 0x75, 0xa6, 0x88, 0xeb, 0x90, 0xa3, 0x19, 0x72, 0x72, 0x38, - 0x4e, 0x5a, 0xcd, 0xa3, 0xb2, 0x49, 0x12, 0x9e, 0xe2, 0x47, 0xee, 0x13, 0x8a, 0x5d, 0x9f, 0x85, 0x32, 0x23, 0xce, - 0x19, 0x67, 0xdb, 0x0b, 0xae, 0xd1, 0x5b, 0x73, 0x90, 0x8e, 0x1d, 0xf6, 0xfc, 0x89, 0x22, 0x4c, 0x21, 0x65, 0xa7, - 0x26, 0x6d, 0xd2, 0x55, 0x97, 0x71, 0x9f, 0x0e, 0x75, 0x1c, 0x52, 0x3d, 0x3b, 0x1c, 0xea, 0xa5, 0x2d, 0x4f, 0x1c, - 0xe2, 0xca, 0x87, 0xfe, 0x38, 0xf2, 0xeb, 0xc2, 0x7a, 0x51, 0xc8, 0xf8, 0xa4, 0xd0, 0x49, 0x4b, 0x95, 0x78, 0x00, - 0xb7, 0x95, 0x4d, 0x6f, 0xcb, 0xd4, 0xda, 0xd0, 0x71, 0xe9, 0x6f, 0x02, 0xa4, 0x90, 0xc5, 0xa9, 0x5c, 0x0a, 0xe5, - 0x9a, 0xf1, 0xe2, 0xb0, 0xe2, 0xf6, 0xd5, 0x7d, 0xda, 0x57, 0x14, 0x1d, 0x20, 0x10, 0x11, 0x5a, 0x01, 0xc2, 0x17, - 0x26, 0x70, 0x75, 0x95, 0xa5, 0xb0, 0x8e, 0x09, 0xc1, 0x53, 0xf8, 0x46, 0x6a, 0xa5, 0x55, 0x46, 0xc4, 0x05, 0xdb, - 0x8d, 0x50, 0xf6, 0x00, 0x1a, 0x10, 0xc3, 0x49, 0xfc, 0x2f, 0x4f, 0x55, 0xcb, 0xb4, 0x5b, 0xc9, 0xa5, 0x91, 0x76, - 0xa3, 0x2d, 0xde, 0x98, 0x56, 0x14, 0x14, 0x13, 0x92, 0xbe, 0xd2, 0xa0, 0xd5, 0xb1, 0xf5, 0x9b, 0xbd, 0x5e, 0xbc, - 0x3a, 0xbe, 0xe3, 0xe4, 0x60, 0x94, 0x63, 0xc9, 0x20, 0x53, 0x11, 0xca, 0xc5, 0x45, 0xd8, 0x7a, 0xd8, 0xd9, 0x16, - 0xda, 0x69, 0xd0, 0x71, 0xb7, 0x82, 0x1a, 0x84, 0xf9, 0xd0, 0x73, 0xa7, 0xdb, 0x3e, 0x5d, 0x19, 0xb7, 0x8b, 0x78, - 0x95, 0xe3, 0x54, 0x55, 0x09, 0xa4, 0x64, 0xf3, 0x31, 0x48, 0x95, 0x24, 0x47, 0xa6, 0x0a, 0xeb, 0x1e, 0x6c, 0xef, - 0x98, 0x30, 0x09, 0x79, 0xe4, 0x7d, 0xf8, 0x27, 0x84, 0x5a, 0x8a, 0x7e, 0xdb, 0xf6, 0x6d, 0xc9, 0xe1, 0x95, 0xa3, - 0x55, 0x83, 0x80, 0xd8, 0x88, 0x00, 0x35, 0x8f, 0x8f, 0xf6, 0x26, 0x6e, 0xbd, 0xa3, 0x72, 0x37, 0x35, 0x7e, 0xcf, - 0x56, 0x76, 0x1e, 0xf9, 0x1d, 0xaf, 0xec, 0xe3, 0x42, 0x15, 0xec, 0x92, 0x12, 0x3d, 0xc9, 0xfa, 0xf1, 0xca, 0xa6, - 0x35, 0xfb, 0x79, 0x7d, 0x41, 0xc8, 0xe6, 0x55, 0xf6, 0xc8, 0xab, 0x42, 0xbd, 0x18, 0x09, 0x63, 0xaa, 0x43, 0x78, - 0xe3, 0xc8, 0xd8, 0x9f, 0x17, 0x32, 0x8d, 0x81, 0x05, 0x28, 0xb4, 0xd4, 0xbb, 0x11, 0x4f, 0x8f, 0x65, 0x56, 0xa4, - 0x75, 0x27, 0x5c, 0xc5, 0x7a, 0x09, 0x3f, 0xba, 0x0d, 0x58, 0x58, 0x29, 0xdd, 0x22, 0x97, 0x77, 0x75, 0x91, 0xf5, - 0xd9, 0x6b, 0x13, 0x43, 0xef, 0x92, 0x42, 0x85, 0xb2, 0x63, 0xca, 0x8a, 0xf9, 0x0a, 0x69, 0x8e, 0x05, 0x6f, 0x42, - 0xfd, 0xbc, 0x2d, 0x7f, 0x87, 0x2a, 0x16, 0x7f, 0x5d, 0xd1, 0x5b, 0xa7, 0x6a, 0xb6, 0xcf, 0x14, 0x33, 0x65, 0x3b, - 0x17, 0xee, 0x8b, 0xfb, 0x8d, 0x6f, 0x88, 0xa7, 0x62, 0xd5, 0x77, 0x45, 0x71, 0xe4, 0xa0, 0xc9, 0x20, 0xaa, 0x93, - 0xb5, 0x10, 0x77, 0x5d, 0x19, 0x92, 0x70, 0xe7, 0x09, 0x33, 0x48, 0xe7, 0xb0, 0x71, 0x55, 0x23, 0xd3, 0xa0, 0xe6, - 0x40, 0x9d, 0x54, 0x83, 0x15, 0xb4, 0x42, 0x52, 0xf6, 0x14, 0x33, 0x51, 0x07, 0xee, 0xf7, 0x9a, 0xfd, 0x3f, 0xa0, - 0x12, 0x7d, 0xdd, 0xf5, 0x57, 0x7d, 0x0b, 0xe7, 0x82, 0x05, 0x4b, 0x2a, 0xfb, 0x72, 0x5b, 0x6f, 0xfc, 0x29, 0x6c, - 0xea, 0xd4, 0xad, 0xbb, 0x5d, 0xea, 0x72, 0x9a, 0x0d, 0xce, 0x3b, 0x47, 0x31, 0x77, 0x0a, 0x1f, 0x62, 0x2e, 0x2f, - 0xd9, 0x44, 0x25, 0x57, 0x71, 0xe2, 0x45, 0x0d, 0x00, 0xf3, 0x0e, 0x90, 0x9c, 0x29, 0x61, 0x94, 0xf8, 0x73, 0x52, - 0x01, 0xd5, 0x94, 0xae, 0xb3, 0xb3, 0xee, 0x17, 0x7b, 0xfe, 0x8a, 0xbc, 0xbe, 0x72, 0x0c, 0xea, 0xe6, 0xbc, 0x20, - 0xa7, 0x98, 0x5f, 0x34, 0x25, 0x63, 0x4f, 0xb7, 0xad, 0xaa, 0x93, 0xb5, 0xcb, 0x8b, 0xda, 0x44, 0x89, 0x74, 0xc9, - 0x0d, 0x2f, 0xf5, 0xb6, 0xbc, 0x66, 0xcb, 0x93, 0x75, 0x7a, 0x2a, 0xd6, 0xd8, 0xbe, 0x08, 0x63, 0x7d, 0x18, 0x5d, - 0xe9, 0x49, 0x07, 0x39, 0x2d, 0x4b, 0x4b, 0xb9, 0x8b, 0x9c, 0x5b, 0xba, 0x5d, 0x3a, 0xcc, 0x8f, 0x19, 0x6b, 0x6f, - 0x8d, 0x8d, 0xad, 0xe5, 0xe6, 0xbf, 0xae, 0x6c, 0xc3, 0x54, 0xa1, 0x68, 0x01, 0x4c, 0xcf, 0x26, 0x87, 0xf5, 0x01, - 0x35, 0x53, 0x6f, 0x51, 0xbb, 0xe2, 0xf5, 0x4e, 0xf4, 0xbc, 0xfb, 0x0e, 0x6a, 0x86, 0x5a, 0x8f, 0x92, 0x68, 0xa9, - 0x7d, 0xef, 0x5b, 0x4a, 0x5b, 0xe6, 0xb1, 0xf2, 0xa2, 0xd4, 0x43, 0xfd, 0xea, 0x8f, 0xd3, 0xda, 0xb8, 0x27, 0xbc, - 0x65, 0xa3, 0xae, 0xe2, 0x63, 0x9f, 0xe7, 0xc2, 0xcc, 0x2c, 0x3e, 0x97, 0xd6, 0x83, 0x5f, 0x4e, 0xbb, 0x99, 0x39, - 0x3d, 0xbe, 0xa7, 0x83, 0xc4, 0x5c, 0x7a, 0x2f, 0x43, 0xa0, 0x68, 0x85, 0x66, 0x1d, 0x35, 0xcc, 0x79, 0x9f, 0x3a, - 0xc6, 0xcf, 0x7b, 0x4c, 0xc9, 0x1d, 0x3f, 0xe3, 0xf5, 0xd0, 0xa6, 0x9f, 0x3e, 0x66, 0xce, 0x87, 0x89, 0xf0, 0x6a, - 0x57, 0xa3, 0x13, 0x56, 0xe0, 0xeb, 0xa5, 0xc7, 0xc9, 0xa7, 0xbd, 0xaa, 0x5a, 0x5a, 0xdf, 0x7f, 0x6b, 0x62, 0x80, - 0xa9, 0x52, 0x7e, 0x45, 0xfb, 0xb9, 0xc6, 0x62, 0x86, 0x97, 0x74, 0xd9, 0xcb, 0x00}; + 0x1b, 0x4d, 0x98, 0xa3, 0x10, 0xd8, 0x38, 0x00, 0x40, 0x74, 0x87, 0x5d, 0x14, 0x95, 0xac, 0x9e, 0x80, 0x5a, 0x16, + 0xd8, 0x44, 0x44, 0xed, 0xc1, 0xec, 0xbf, 0x23, 0x3c, 0x6d, 0x9a, 0x66, 0x3e, 0x6c, 0xc2, 0x28, 0x09, 0x3c, 0x6e, + 0x5f, 0x78, 0xfd, 0x3f, 0x03, 0xf2, 0xf9, 0xd8, 0x63, 0x23, 0xd6, 0x47, 0x4b, 0xb8, 0x52, 0xbd, 0x19, 0x5a, 0x7e, + 0xdb, 0x62, 0xfe, 0x45, 0xae, 0x2e, 0x1e, 0x2d, 0x75, 0x91, 0x2c, 0x66, 0xae, 0xa8, 0x95, 0x97, 0xe5, 0x64, 0x1c, + 0xa1, 0xb1, 0x4f, 0x72, 0x37, 0x53, 0xad, 0xd7, 0x77, 0xc5, 0xf3, 0xe4, 0xa0, 0x14, 0x9b, 0xb4, 0xb7, 0x34, 0x79, + 0x53, 0x5a, 0x99, 0x3d, 0x9b, 0xa1, 0x10, 0x13, 0x89, 0x05, 0x2a, 0x24, 0x94, 0xa6, 0xe4, 0xff, 0xb9, 0xff, 0x75, + 0xd9, 0xfb, 0x75, 0xa6, 0xa9, 0x98, 0x1b, 0x1f, 0x5b, 0x7a, 0x5c, 0x81, 0xb1, 0xb3, 0x0a, 0xae, 0xc9, 0xb2, 0xec, + 0x1e, 0x07, 0x18, 0x23, 0x63, 0x4e, 0x30, 0xf8, 0x20, 0x79, 0x99, 0x27, 0x6e, 0xfa, 0xe2, 0x57, 0x65, 0x8a, 0xea, + 0x5b, 0x96, 0x99, 0xfe, 0xf3, 0x79, 0x49, 0xca, 0x42, 0xe8, 0x32, 0x2b, 0xe3, 0xe3, 0xd9, 0xb3, 0x25, 0xe6, 0xbc, + 0x95, 0xbc, 0x08, 0x22, 0xcb, 0xac, 0xb8, 0x36, 0x11, 0x41, 0x34, 0xc4, 0xb6, 0x9d, 0x84, 0x6a, 0x33, 0x6d, 0x59, + 0xb5, 0x0e, 0xb0, 0xb4, 0x63, 0x81, 0x75, 0xba, 0x40, 0x8f, 0x35, 0x96, 0x4f, 0x21, 0x76, 0xb0, 0x49, 0x5c, 0x24, + 0xdf, 0x39, 0xe5, 0x80, 0xb5, 0x79, 0x5b, 0x9a, 0xb4, 0x82, 0x0b, 0x5c, 0xab, 0x35, 0x95, 0x7d, 0xa0, 0x4c, 0x23, + 0x72, 0x77, 0x0f, 0x25, 0x36, 0x0e, 0xd8, 0x95, 0x65, 0x67, 0xff, 0xde, 0x54, 0xab, 0xb4, 0x01, 0x9a, 0xb3, 0x36, + 0x35, 0x2e, 0x35, 0x4e, 0x19, 0xc4, 0x95, 0xce, 0xf9, 0x24, 0xb8, 0x20, 0x64, 0xbf, 0xf7, 0xfe, 0x7f, 0xcb, 0x76, + 0x18, 0xa2, 0xbb, 0x89, 0x1d, 0x38, 0x8d, 0xe8, 0xb0, 0xf4, 0x35, 0xb4, 0x72, 0x9c, 0xf9, 0xff, 0x77, 0x03, 0xec, + 0x06, 0x28, 0x2d, 0x40, 0x51, 0x5b, 0x24, 0x87, 0x5b, 0x25, 0xb7, 0xc6, 0x6b, 0xe6, 0xac, 0xd3, 0x4c, 0xd5, 0x69, + 0xce, 0xd9, 0xc8, 0x46, 0x89, 0xf1, 0xd9, 0x55, 0x6e, 0xa3, 0xd0, 0xd8, 0xf4, 0xb2, 0x0b, 0x2f, 0x3d, 0x9d, 0x63, + 0x9b, 0xa9, 0x66, 0x65, 0x06, 0x9c, 0xbf, 0x4d, 0x72, 0xd6, 0x90, 0x03, 0xc2, 0x41, 0xca, 0x7d, 0xd8, 0x75, 0x91, + 0x65, 0x59, 0x72, 0x91, 0x0b, 0x9b, 0x37, 0x91, 0xcd, 0x94, 0x37, 0xa3, 0x12, 0x2a, 0xa9, 0x25, 0xac, 0xdc, 0xc4, + 0xf4, 0xc4, 0xbd, 0x7f, 0x17, 0x64, 0x62, 0x6c, 0x71, 0x00, 0x3e, 0x05, 0x71, 0xe8, 0xc8, 0xa6, 0xeb, 0x1c, 0xe7, + 0x18, 0xbd, 0x61, 0x21, 0x98, 0x66, 0x7b, 0x08, 0x0e, 0x7d, 0x38, 0xb0, 0x01, 0x8d, 0x3c, 0xcf, 0x1d, 0x5e, 0x7d, + 0x60, 0xeb, 0x7e, 0xf9, 0x68, 0x18, 0xf4, 0x78, 0xb3, 0x2a, 0x01, 0xb7, 0x91, 0x43, 0xab, 0x65, 0x64, 0x23, 0x50, + 0xcd, 0x6a, 0xec, 0xe7, 0xf0, 0x67, 0xf3, 0x7f, 0x5f, 0x9c, 0x1c, 0x55, 0x14, 0x4c, 0xff, 0x84, 0xbe, 0xbd, 0xef, + 0xf0, 0x0a, 0xe9, 0x92, 0xb5, 0x05, 0xec, 0x67, 0x14, 0xe5, 0x61, 0xe4, 0xa1, 0x0a, 0x5e, 0x7b, 0xd9, 0x4d, 0x97, + 0x39, 0x9e, 0x19, 0x33, 0x36, 0x5d, 0x70, 0x3d, 0x30, 0x73, 0x65, 0xe5, 0x78, 0xb4, 0xa5, 0x1a, 0x9c, 0x67, 0x0a, + 0x0d, 0xda, 0x74, 0xa3, 0xe5, 0xf4, 0x21, 0x1e, 0x3f, 0x4c, 0x29, 0x3d, 0xf7, 0x93, 0xad, 0x2b, 0xe3, 0xbe, 0xb2, + 0x24, 0x6b, 0x3a, 0xfc, 0x39, 0xe7, 0xab, 0x09, 0x91, 0x84, 0x95, 0x9e, 0x46, 0xa4, 0x5c, 0x1d, 0x75, 0xdc, 0xb2, + 0x8c, 0xb2, 0xf4, 0xe6, 0x77, 0x94, 0x69, 0xd2, 0x96, 0x8a, 0x14, 0x88, 0x37, 0xd7, 0xf9, 0xc3, 0x64, 0xcc, 0xab, + 0xa6, 0x3e, 0x3e, 0x1e, 0x6f, 0x7b, 0x37, 0xb4, 0x6c, 0x78, 0x8e, 0x66, 0x3d, 0x98, 0xba, 0x7d, 0xf3, 0x59, 0x1a, + 0x37, 0xe3, 0xe6, 0x42, 0xb6, 0x36, 0xfd, 0x75, 0x1e, 0x3e, 0x85, 0xeb, 0x66, 0xec, 0x96, 0xe5, 0xa1, 0xc3, 0xcd, + 0x5c, 0x51, 0xb2, 0x7a, 0x68, 0x77, 0xd0, 0xe7, 0xaa, 0x4b, 0xb8, 0xe9, 0xe5, 0x22, 0xcc, 0xd7, 0x63, 0x6c, 0x52, + 0xd8, 0xae, 0x5a, 0xbe, 0x4e, 0x57, 0x28, 0x32, 0x13, 0x85, 0x8a, 0xff, 0x08, 0x65, 0xe5, 0xe5, 0x95, 0x9e, 0x8c, + 0x63, 0x3f, 0x17, 0x5c, 0x47, 0x58, 0x4d, 0x2d, 0x92, 0x70, 0xfb, 0xa7, 0xb9, 0x9f, 0x41, 0x01, 0xf9, 0xb7, 0xa6, + 0xa2, 0x30, 0x95, 0xf6, 0x73, 0x10, 0xf9, 0x28, 0x93, 0x31, 0x0c, 0x01, 0x8a, 0x4c, 0x47, 0x00, 0x9e, 0x79, 0x42, + 0x9e, 0x8e, 0xe7, 0x18, 0x25, 0x06, 0xde, 0xef, 0xf8, 0x7a, 0x79, 0xb4, 0x30, 0xdc, 0x08, 0xd2, 0x7e, 0xee, 0xa3, + 0x24, 0x57, 0x37, 0x28, 0x40, 0x76, 0x76, 0x0a, 0x92, 0x27, 0x05, 0x26, 0xd9, 0x35, 0xcb, 0x51, 0x26, 0xc8, 0x9b, + 0x8e, 0x43, 0x49, 0x43, 0x5f, 0xe3, 0x25, 0x29, 0xfe, 0xce, 0x27, 0x15, 0x2a, 0xc5, 0xdf, 0xba, 0x6b, 0x93, 0xa7, + 0x06, 0x00, 0x21, 0x9d, 0x66, 0xba, 0xde, 0xf8, 0x41, 0x90, 0xd8, 0x2d, 0x25, 0xa0, 0xe1, 0x8a, 0x99, 0x6c, 0x0a, + 0xeb, 0xbe, 0x36, 0x75, 0xba, 0x77, 0x29, 0x73, 0x78, 0x3c, 0xd3, 0x80, 0xc8, 0x8c, 0xd0, 0x70, 0x6a, 0x44, 0xd0, + 0x30, 0xca, 0x7b, 0x84, 0xdb, 0x54, 0x31, 0x7c, 0xc0, 0xe9, 0x87, 0x1b, 0x62, 0x59, 0xc0, 0x54, 0x08, 0xb4, 0xf6, + 0x36, 0x36, 0x9a, 0xaf, 0xa4, 0x21, 0x16, 0x93, 0x3f, 0xe3, 0x96, 0x36, 0xfe, 0x55, 0xd5, 0x84, 0x06, 0x48, 0x82, + 0xcf, 0xcf, 0xda, 0x21, 0x61, 0x8c, 0x26, 0x75, 0xb1, 0x49, 0x7a, 0x42, 0xca, 0x1c, 0x48, 0x20, 0xa1, 0x86, 0x4c, + 0xe1, 0x9c, 0x4d, 0x2e, 0xc7, 0x3b, 0xde, 0x3e, 0x08, 0x47, 0x6b, 0x4b, 0x62, 0x79, 0x23, 0x49, 0xa9, 0x86, 0xb7, + 0x86, 0x1a, 0x8e, 0x6d, 0xaa, 0x47, 0xcc, 0x4f, 0x71, 0xb7, 0xa7, 0x35, 0x8e, 0x25, 0x7d, 0x6e, 0x86, 0x4b, 0xb9, + 0x9b, 0xaf, 0xfa, 0x85, 0x60, 0x57, 0x12, 0x4d, 0x2a, 0x51, 0x85, 0x9f, 0x7f, 0xfd, 0x1d, 0xc8, 0x5a, 0x2d, 0x0f, + 0x53, 0xfc, 0x1c, 0xf8, 0x71, 0xac, 0x4c, 0x61, 0x3d, 0x48, 0x2f, 0xd5, 0xe9, 0x4d, 0x6d, 0xde, 0x91, 0x41, 0x2a, + 0xdc, 0x4a, 0xb0, 0xbf, 0x19, 0x21, 0x62, 0x87, 0x99, 0xf8, 0xd7, 0x8d, 0x84, 0x44, 0xd2, 0x85, 0xc2, 0x39, 0xab, + 0xe1, 0xe2, 0x3f, 0xa4, 0x0c, 0xd1, 0x9c, 0x10, 0x0d, 0x13, 0xc6, 0x57, 0x46, 0x29, 0x48, 0xef, 0xe6, 0xab, 0x4d, + 0xd3, 0x86, 0xee, 0x88, 0x3f, 0xa8, 0x57, 0xb9, 0x8e, 0xb1, 0x21, 0xf2, 0x55, 0x22, 0x79, 0xf6, 0x38, 0x0c, 0xe3, + 0x60, 0x39, 0xc1, 0x53, 0x95, 0x11, 0xfe, 0x75, 0xaa, 0x0a, 0xee, 0x39, 0x9a, 0x55, 0xce, 0xdd, 0x17, 0xb9, 0xe6, + 0x5b, 0x50, 0xe3, 0xf3, 0x66, 0x6e, 0x86, 0xca, 0x68, 0xeb, 0x58, 0x4b, 0x06, 0xc9, 0x95, 0x65, 0x57, 0x80, 0x8d, + 0xe9, 0x28, 0x8e, 0x2c, 0x5a, 0x60, 0x6c, 0xf6, 0xd7, 0x30, 0xfd, 0x4f, 0xd0, 0x09, 0x91, 0xb6, 0x97, 0x12, 0x6a, + 0x65, 0xc6, 0x0f, 0x46, 0xa8, 0xb7, 0xb2, 0xf3, 0x29, 0x8b, 0x20, 0xc3, 0xf7, 0xac, 0xe5, 0x39, 0x9c, 0xc7, 0xe1, + 0xe2, 0x49, 0xa9, 0xfd, 0x22, 0x5c, 0x76, 0xbb, 0x55, 0xa2, 0xb8, 0xd5, 0x08, 0x89, 0x0d, 0xd7, 0x3f, 0x51, 0xad, + 0x15, 0x0c, 0x57, 0x54, 0x5c, 0x6b, 0xad, 0xf5, 0x31, 0x76, 0x29, 0xa5, 0x6c, 0x4c, 0x4f, 0xff, 0x59, 0x4a, 0x7d, + 0xb5, 0x94, 0x94, 0x21, 0x26, 0xef, 0x09, 0x75, 0xc7, 0x49, 0x15, 0x0c, 0x0b, 0xcf, 0xdc, 0x52, 0x71, 0x26, 0x51, + 0x09, 0x06, 0x46, 0xe5, 0xb4, 0x3f, 0x78, 0xbe, 0xeb, 0x5d, 0xb0, 0x04, 0x88, 0xb7, 0xc8, 0xf5, 0xbf, 0x15, 0x05, + 0x2b, 0xe6, 0x56, 0x00, 0x4e, 0xcc, 0x82, 0x84, 0x2b, 0x3a, 0x18, 0x24, 0xd4, 0x1b, 0x98, 0xc9, 0x35, 0xa2, 0x77, + 0x82, 0xf5, 0x22, 0xb7, 0xfa, 0x25, 0x0a, 0xb9, 0x4d, 0x49, 0x45, 0x02, 0xb7, 0xd5, 0xda, 0x30, 0xcd, 0x7a, 0xa6, + 0x99, 0x38, 0x3d, 0x67, 0x31, 0x65, 0x76, 0xd4, 0x5c, 0xbb, 0xba, 0x04, 0xb3, 0xbb, 0x3b, 0x3d, 0x33, 0xf4, 0xc3, + 0x32, 0x44, 0xdf, 0xcd, 0xbe, 0x26, 0x49, 0x0c, 0xa9, 0x6c, 0xc3, 0xda, 0xf4, 0xff, 0x78, 0xda, 0x09, 0x05, 0x7f, + 0xad, 0xd5, 0x0d, 0xc4, 0xbd, 0x88, 0x4e, 0x5d, 0x4e, 0x84, 0xf9, 0xfa, 0x62, 0x60, 0x3f, 0x29, 0x67, 0xb8, 0x46, + 0x3c, 0xb2, 0xb1, 0x37, 0xd4, 0x5b, 0x23, 0x5a, 0x86, 0xe4, 0xf3, 0x7e, 0x95, 0xf6, 0x0d, 0x65, 0x66, 0x5f, 0xec, + 0x87, 0x8b, 0x77, 0xda, 0x4c, 0x0e, 0xb8, 0xd3, 0xd0, 0xf3, 0xa6, 0xf9, 0x06, 0xf2, 0xac, 0x3d, 0x38, 0x61, 0x4f, + 0x27, 0xdd, 0xe9, 0xc6, 0xd5, 0x24, 0x6b, 0xe3, 0xa1, 0xc4, 0x90, 0xc0, 0xaf, 0x59, 0x4e, 0x00, 0x39, 0x10, 0x7b, + 0xc4, 0xda, 0xe4, 0x52, 0xb8, 0x7e, 0xa3, 0x45, 0x37, 0x30, 0xaf, 0x9b, 0xbf, 0xc8, 0x21, 0x95, 0xc5, 0x1b, 0x10, + 0x32, 0x32, 0x3f, 0xa3, 0x1c, 0x59, 0xc1, 0xa3, 0xf2, 0xf5, 0x21, 0x52, 0x87, 0x2f, 0xaf, 0xf6, 0x43, 0x63, 0xdf, + 0x22, 0xf3, 0xa2, 0x68, 0x2a, 0x33, 0x47, 0xb9, 0x0f, 0x90, 0xc4, 0x92, 0x67, 0x58, 0x61, 0x7c, 0xd5, 0xda, 0x88, + 0x08, 0xbe, 0x11, 0xc0, 0x7e, 0xf7, 0x49, 0x70, 0x6c, 0x63, 0x12, 0x28, 0xd0, 0xee, 0x66, 0x20, 0x41, 0x01, 0x99, + 0x38, 0x92, 0xd4, 0x8e, 0x06, 0x89, 0xfd, 0x09, 0xda, 0x76, 0x71, 0x45, 0x24, 0x1b, 0xfb, 0x39, 0x60, 0x21, 0x8d, + 0x0f, 0xa8, 0xcc, 0x80, 0x08, 0x4b, 0x01, 0x3a, 0x7a, 0xfe, 0xa9, 0x42, 0x5c, 0xcd, 0xb0, 0xf0, 0x9c, 0xc1, 0x5d, + 0x99, 0xaf, 0xfb, 0x79, 0xf6, 0xe0, 0x4c, 0x05, 0xc4, 0xe3, 0x89, 0x5f, 0x6e, 0x17, 0x28, 0x32, 0x10, 0xb4, 0x42, + 0x3c, 0x14, 0x84, 0x16, 0x8a, 0x18, 0xb4, 0xf9, 0x8f, 0x7d, 0xae, 0x8a, 0x91, 0x0a, 0x85, 0xa8, 0x68, 0x4d, 0xc6, + 0x70, 0x45, 0x9d, 0x23, 0x06, 0xdf, 0xcc, 0xd8, 0xa1, 0x65, 0xa2, 0x52, 0xbe, 0x54, 0xf1, 0x58, 0x07, 0xeb, 0x89, + 0x14, 0x32, 0x32, 0x52, 0x93, 0x9d, 0x6f, 0x21, 0x49, 0xf0, 0x4e, 0xad, 0x3c, 0x83, 0x14, 0x5e, 0xe9, 0xb0, 0x4f, + 0xa2, 0x5f, 0x86, 0x28, 0x8c, 0xda, 0xd7, 0x94, 0xbe, 0x9a, 0x89, 0xc4, 0xd8, 0x13, 0x79, 0x50, 0xa2, 0xe5, 0x1f, + 0xd9, 0x84, 0x91, 0x84, 0xe4, 0xd8, 0xf3, 0xe1, 0xdf, 0xe7, 0x04, 0xe9, 0xe3, 0xac, 0x87, 0xb4, 0x25, 0x11, 0x3e, + 0x51, 0x96, 0x03, 0xba, 0xee, 0x80, 0xa4, 0x00, 0xde, 0x75, 0xc1, 0xed, 0x7d, 0xdb, 0x21, 0x8e, 0x4e, 0xce, 0xa9, + 0x19, 0xe3, 0x65, 0x0a, 0x1b, 0x39, 0x1c, 0x6f, 0x93, 0x20, 0x6c, 0x44, 0xaf, 0x4c, 0xd3, 0xb1, 0xc0, 0x2c, 0x81, + 0x44, 0x88, 0xf4, 0x7e, 0x71, 0xce, 0x85, 0x98, 0xd7, 0x49, 0x66, 0xa8, 0x78, 0x6a, 0x95, 0xa9, 0x09, 0x32, 0x1c, + 0xe7, 0x2a, 0xbe, 0x27, 0x29, 0xc9, 0x13, 0xee, 0x62, 0xb2, 0x5f, 0x61, 0x1d, 0x25, 0x4f, 0x49, 0x41, 0xc9, 0xa8, + 0xe1, 0x7f, 0x99, 0xd2, 0x44, 0x62, 0x57, 0x76, 0x87, 0x24, 0x80, 0x94, 0x60, 0xa9, 0xce, 0xe0, 0x71, 0x44, 0x3c, + 0x17, 0x82, 0x86, 0x88, 0x44, 0xe1, 0x33, 0xdb, 0xcb, 0xcf, 0x22, 0x87, 0x04, 0xcf, 0x4c, 0x89, 0xce, 0xe2, 0x0f, + 0xd6, 0x71, 0x8f, 0x8c, 0x37, 0x1a, 0x46, 0x35, 0xaf, 0x0f, 0xda, 0x3e, 0x62, 0x6e, 0x7a, 0xfe, 0x30, 0x30, 0xd3, + 0xb1, 0xc9, 0x26, 0x95, 0x70, 0x56, 0x6e, 0xfe, 0xc6, 0x05, 0x8a, 0x8d, 0x5a, 0xa1, 0xe5, 0x67, 0x3d, 0xb5, 0x21, + 0xea, 0x9c, 0x13, 0xe2, 0x80, 0x03, 0x56, 0xb3, 0x60, 0x9e, 0x6b, 0xfc, 0xcf, 0x65, 0x72, 0x97, 0x1c, 0xc1, 0x99, + 0x1b, 0x4b, 0x73, 0x79, 0x15, 0xc9, 0xa1, 0x0b, 0xb6, 0x02, 0x55, 0x40, 0x39, 0xc9, 0x18, 0x23, 0xcb, 0x01, 0x23, + 0x96, 0x48, 0x2e, 0x17, 0x20, 0xb4, 0xc8, 0xba, 0x0a, 0xc2, 0x50, 0xa8, 0x9c, 0x46, 0xda, 0x70, 0x28, 0xe3, 0x18, + 0x99, 0xd6, 0x55, 0xdf, 0x19, 0x42, 0x96, 0xf2, 0xae, 0x01, 0xed, 0x28, 0x95, 0xbc, 0x94, 0xef, 0xa2, 0xdc, 0x9d, + 0xf0, 0x52, 0x18, 0x20, 0xcf, 0x1f, 0x15, 0x1b, 0x75, 0x47, 0x81, 0x17, 0x83, 0xf1, 0x42, 0x96, 0x0d, 0x77, 0x52, + 0xc9, 0x12, 0x13, 0x25, 0x08, 0x9c, 0x32, 0xd2, 0xd8, 0xa7, 0x2c, 0xed, 0xca, 0xfb, 0x2b, 0x4c, 0x2c, 0x4f, 0xca, + 0x28, 0x46, 0x3c, 0x39, 0xab, 0xb2, 0xae, 0x59, 0x3c, 0xc4, 0xfc, 0xc9, 0xdb, 0x24, 0xe5, 0x37, 0x3d, 0xd3, 0xe8, + 0x8d, 0x49, 0x67, 0x0d, 0x39, 0x9c, 0x4e, 0xc5, 0xe9, 0xec, 0x59, 0x5c, 0x35, 0x48, 0x55, 0x40, 0x11, 0x08, 0x87, + 0x3c, 0xf9, 0x26, 0x33, 0xda, 0x37, 0x01, 0x4b, 0xa5, 0x63, 0x28, 0x4f, 0xaa, 0x31, 0x26, 0x24, 0x2d, 0xf7, 0x3f, + 0x82, 0xe2, 0x4a, 0x8d, 0x24, 0x4b, 0xf0, 0xe1, 0x1d, 0x4a, 0x08, 0x4a, 0xc9, 0xa1, 0x83, 0x6e, 0x43, 0x45, 0x13, + 0x28, 0xa2, 0x27, 0x41, 0x9e, 0xaf, 0x37, 0x76, 0xaa, 0x14, 0x03, 0x9c, 0x98, 0xec, 0xca, 0xe3, 0x68, 0x66, 0x95, + 0x3e, 0xfb, 0x4f, 0x11, 0x1c, 0x0e, 0x87, 0x17, 0x34, 0x48, 0xa4, 0xf7, 0x5c, 0x91, 0x9b, 0x5a, 0x70, 0x7e, 0xba, + 0x10, 0x93, 0x59, 0x5b, 0x16, 0xd2, 0x72, 0x84, 0x62, 0x24, 0x87, 0x8e, 0xc0, 0xb6, 0x0c, 0xb9, 0xad, 0x91, 0xc8, + 0xe4, 0x5b, 0xfe, 0x1d, 0x87, 0x4c, 0x52, 0x32, 0xa5, 0xc9, 0x78, 0x2f, 0xa7, 0x22, 0xbb, 0x12, 0x45, 0x25, 0x32, + 0x2a, 0xa6, 0x41, 0x0c, 0xa9, 0xac, 0xde, 0xd3, 0x82, 0xa5, 0xba, 0x23, 0xb8, 0x3b, 0x27, 0xa4, 0x60, 0x19, 0x54, + 0xdd, 0x8e, 0xce, 0x38, 0xda, 0x20, 0x66, 0x5d, 0x92, 0xec, 0x27, 0xc5, 0x20, 0x9b, 0x48, 0xa1, 0x44, 0x3d, 0x61, + 0x37, 0x6e, 0x4b, 0x08, 0xfb, 0xdd, 0xc0, 0xc4, 0xd2, 0xb2, 0x4c, 0x93, 0x3e, 0x45, 0x62, 0xa7, 0x14, 0x8f, 0x50, + 0xf9, 0x14, 0xba, 0x77, 0xd3, 0x48, 0x48, 0x75, 0x92, 0x27, 0x08, 0xda, 0x73, 0x30, 0x76, 0x4c, 0xc0, 0x7c, 0x7f, + 0x0a, 0xd6, 0x8f, 0xd3, 0xb4, 0x60, 0xe1, 0xe0, 0x21, 0xc5, 0x9e, 0x99, 0xdd, 0xfc, 0xcb, 0x7c, 0x8e, 0x72, 0xce, + 0x0c, 0x9d, 0xcc, 0x53, 0x48, 0x66, 0xe3, 0xec, 0xe4, 0x5f, 0x90, 0xe6, 0xbd, 0x83, 0xdd, 0x91, 0xb6, 0xe1, 0xf7, + 0x99, 0xe0, 0xfa, 0x44, 0x0e, 0x23, 0xf8, 0xaa, 0x4b, 0x62, 0x37, 0x1f, 0x23, 0x8c, 0x22, 0x45, 0xaf, 0x1d, 0x07, + 0xe2, 0xb2, 0xda, 0x7d, 0x79, 0x10, 0x00, 0xb0, 0xa8, 0xf4, 0xef, 0x95, 0x88, 0x4c, 0xcc, 0x83, 0x5c, 0x06, 0x5b, + 0x19, 0xf0, 0xb3, 0x4a, 0xe2, 0x01, 0x97, 0x80, 0x4b, 0xe8, 0xb3, 0x02, 0x66, 0xa8, 0x01, 0xd4, 0xde, 0x79, 0x53, + 0x18, 0x46, 0x3a, 0x68, 0x4e, 0xb5, 0x86, 0xe2, 0x2d, 0x8a, 0x28, 0x1f, 0xfa, 0xb0, 0xf7, 0x61, 0x91, 0x01, 0x1d, + 0xfc, 0x38, 0x33, 0xa1, 0x3c, 0x4c, 0x9a, 0x31, 0x9a, 0x98, 0xe7, 0x19, 0xc5, 0xbd, 0xe1, 0xc2, 0xa4, 0xb7, 0x24, + 0x10, 0xd3, 0xbe, 0x6f, 0x4b, 0x45, 0x7c, 0xbf, 0x1b, 0x97, 0xfe, 0xd5, 0x7a, 0x04, 0xbd, 0x64, 0x16, 0x4a, 0xe4, + 0x5b, 0x2a, 0xd4, 0x91, 0x07, 0x86, 0xdb, 0x76, 0x6c, 0x98, 0x75, 0xa7, 0x95, 0xf4, 0x7a, 0x55, 0x35, 0xec, 0x80, + 0x71, 0x54, 0x5a, 0x7a, 0xaa, 0x5f, 0x1c, 0xd4, 0xe4, 0xf5, 0x62, 0xfd, 0xd5, 0x8e, 0xbd, 0x3c, 0x01, 0x99, 0x19, + 0xa3, 0xc1, 0x9c, 0x92, 0xc6, 0x0e, 0xa8, 0x85, 0x34, 0x94, 0x75, 0xb8, 0x8b, 0xa7, 0xb5, 0x12, 0x0e, 0x44, 0xe0, + 0x6c, 0xba, 0x4d, 0xac, 0x97, 0xdc, 0x0f, 0x1d, 0x40, 0x19, 0x1d, 0x3e, 0x77, 0x9b, 0x5a, 0x0c, 0xeb, 0x01, 0x6f, + 0x10, 0xd1, 0x42, 0x93, 0x0a, 0x2e, 0xb1, 0x43, 0xca, 0xa6, 0xca, 0xd0, 0x41, 0xe7, 0x5c, 0x53, 0x66, 0x65, 0xa5, + 0xf2, 0x2e, 0xaf, 0xa4, 0x9f, 0x66, 0x21, 0x1b, 0xeb, 0x2a, 0x68, 0x2c, 0xc8, 0x6f, 0x21, 0x00, 0xce, 0xa3, 0x99, + 0xbe, 0xd9, 0x00, 0x73, 0xb2, 0x64, 0xf9, 0xad, 0x3c, 0xaa, 0x2c, 0x56, 0xee, 0x2d, 0x47, 0xea, 0xc8, 0xc8, 0xa4, + 0xef, 0x4a, 0x01, 0x92, 0x0e, 0xc6, 0xe5, 0x8e, 0xd5, 0x9e, 0x31, 0x25, 0xba, 0x5f, 0x30, 0xc4, 0xda, 0xe1, 0xf0, + 0xcb, 0x91, 0xc3, 0xa1, 0x66, 0x90, 0x1d, 0x69, 0xf4, 0x20, 0x45, 0xf0, 0x22, 0x57, 0xb8, 0xe2, 0x8f, 0x65, 0xdb, + 0x96, 0x08, 0xe2, 0x29, 0xc2, 0xdf, 0x33, 0x49, 0xe8, 0xe3, 0x01, 0xa1, 0xbb, 0x90, 0xf6, 0xf9, 0x34, 0x93, 0xf5, + 0x23, 0x94, 0x91, 0x64, 0xfa, 0x3e, 0xd4, 0x54, 0xa6, 0xc1, 0x37, 0xbf, 0xe6, 0xa9, 0x41, 0xe5, 0x36, 0x98, 0x44, + 0x83, 0x92, 0x3b, 0x07, 0x18, 0x7e, 0xa4, 0x5c, 0xd5, 0xab, 0xa2, 0x93, 0x56, 0x66, 0x6a, 0x7f, 0x90, 0x39, 0x02, + 0x93, 0xd3, 0x43, 0x33, 0xd2, 0x40, 0x88, 0x00, 0x2f, 0x10, 0x88, 0xbc, 0x04, 0xca, 0x00, 0xb6, 0xe9, 0x5e, 0x1b, + 0x34, 0xc6, 0xe3, 0xf1, 0x33, 0xa2, 0x98, 0x48, 0x2a, 0xdf, 0x13, 0xc7, 0xd1, 0x68, 0xb1, 0x88, 0x54, 0xd0, 0x84, + 0x62, 0x06, 0xfe, 0xdc, 0x7c, 0xb0, 0x3c, 0xeb, 0x7d, 0xd6, 0x0c, 0x63, 0x4c, 0xb3, 0x74, 0xd3, 0x26, 0xe7, 0xc8, + 0xdd, 0x4f, 0x58, 0x5a, 0x33, 0x42, 0x46, 0x09, 0x9b, 0x32, 0xb4, 0xea, 0x5a, 0x57, 0x8a, 0x63, 0x38, 0x46, 0xe3, + 0xfc, 0x1d, 0x59, 0x74, 0xf8, 0x53, 0x8d, 0x4f, 0x1f, 0x63, 0xa4, 0xe5, 0xf9, 0xd9, 0xb7, 0x09, 0xc4, 0x2f, 0xa3, + 0x1a, 0x75, 0x25, 0xc2, 0xa2, 0x65, 0x82, 0xd4, 0x61, 0x43, 0x2f, 0x23, 0x5e, 0x5e, 0xb3, 0xb8, 0x23, 0x41, 0x0f, + 0x4a, 0xec, 0x89, 0x86, 0xd4, 0xed, 0x99, 0xd8, 0xda, 0x26, 0xf5, 0xfa, 0xf3, 0xc9, 0x4f, 0xf3, 0x64, 0x7f, 0x5c, + 0x26, 0x75, 0x8e, 0x0a, 0xc4, 0x51, 0x7b, 0xbb, 0xcc, 0x77, 0xc6, 0x5c, 0x79, 0xf4, 0xdd, 0x56, 0x32, 0x46, 0xd1, + 0x8c, 0xb4, 0x6c, 0x1c, 0x18, 0xd5, 0xc5, 0x0e, 0xd5, 0x77, 0x0a, 0xcb, 0x8f, 0xaf, 0xe4, 0xc8, 0x23, 0x4a, 0x02, + 0x55, 0xd7, 0x8f, 0x24, 0x94, 0x86, 0x71, 0x7e, 0x35, 0xd6, 0x3e, 0x26, 0xd7, 0x06, 0xc5, 0xd2, 0x9e, 0xc7, 0x8c, + 0x8f, 0xb8, 0xf9, 0xcb, 0xb5, 0x1e, 0x64, 0x45, 0xed, 0x39, 0xf1, 0x74, 0xd4, 0xa1, 0x6d, 0x16, 0x93, 0x4d, 0x30, + 0xc0, 0x07, 0x68, 0xc2, 0xda, 0x63, 0x51, 0xeb, 0x8f, 0xc1, 0xd7, 0x3e, 0x40, 0x80, 0x6b, 0x21, 0xac, 0x9c, 0xa2, + 0x40, 0xe9, 0xda, 0x96, 0x5c, 0x1f, 0xef, 0xda, 0xfd, 0x28, 0x23, 0x91, 0xed, 0x2a, 0x29, 0x51, 0x6c, 0xa7, 0x29, + 0xf5, 0x77, 0x4a, 0x7d, 0xf0, 0x28, 0x22, 0x3e, 0xe3, 0x44, 0x8f, 0x4f, 0x56, 0xdd, 0x3c, 0x69, 0x4f, 0x7a, 0xa1, + 0x74, 0x03, 0x5e, 0x5c, 0x56, 0xdd, 0x14, 0x9f, 0x1c, 0x7b, 0x29, 0x62, 0x6b, 0x09, 0xb2, 0x45, 0x14, 0x6d, 0x07, + 0x39, 0xe4, 0x7b, 0x16, 0x29, 0xa4, 0x66, 0xd3, 0x29, 0xee, 0x00, 0x3b, 0xc5, 0x78, 0xe5, 0x88, 0x59, 0xab, 0xc9, + 0x9c, 0x6b, 0x14, 0xc8, 0xcb, 0xa0, 0xea, 0xf7, 0xf6, 0x03, 0x79, 0x37, 0x9e, 0x3f, 0x09, 0xa9, 0x5c, 0x85, 0x9d, + 0xf9, 0xbd, 0xd0, 0xf8, 0x77, 0xa7, 0x3d, 0x89, 0x3c, 0x3c, 0xcc, 0x2f, 0x49, 0xef, 0xf7, 0x71, 0x5f, 0x90, 0x6b, + 0xf8, 0x59, 0x88, 0x84, 0x26, 0x7e, 0xb3, 0x29, 0x90, 0x3c, 0x56, 0x08, 0xb8, 0x50, 0x49, 0x35, 0x8b, 0xb5, 0x25, + 0x9c, 0xd3, 0x83, 0xfb, 0x19, 0x73, 0xee, 0x30, 0x3c, 0xc8, 0x95, 0xd0, 0xb8, 0xbc, 0xc6, 0xdd, 0xa0, 0xb6, 0xfe, + 0x45, 0x58, 0xc2, 0x6b, 0x64, 0x89, 0xb4, 0x2c, 0x9f, 0x51, 0xea, 0x04, 0x0d, 0x5f, 0xba, 0x50, 0x8c, 0xd7, 0x21, + 0x4e, 0xf5, 0x10, 0xdd, 0xdf, 0xb7, 0x23, 0xb5, 0x32, 0xde, 0x7e, 0x7a, 0xe3, 0xe1, 0xe9, 0xf0, 0x34, 0xee, 0x4a, + 0x3c, 0xa3, 0x5e, 0x06, 0x7f, 0x34, 0x64, 0x4a, 0x4d, 0x4f, 0xf1, 0xf6, 0xbf, 0x4a, 0xbd, 0xfe, 0x70, 0xe1, 0x7a, + 0x87, 0x49, 0x20, 0x9f, 0x94, 0x6f, 0x27, 0x53, 0xab, 0x9b, 0x27, 0xbb, 0x7b, 0xf5, 0xfc, 0x33, 0xcf, 0xa4, 0x8c, + 0x1b, 0x9c, 0x38, 0xea, 0x29, 0xb5, 0x89, 0x0a, 0x15, 0x3c, 0x47, 0xcf, 0x74, 0x6b, 0x7b, 0xdc, 0x3c, 0xde, 0x4c, + 0x33, 0x7f, 0xc4, 0x94, 0x27, 0xc5, 0xd6, 0xd3, 0x8d, 0x50, 0x6e, 0x28, 0xde, 0x85, 0x52, 0xd8, 0x84, 0xcf, 0xe8, + 0x3f, 0x9b, 0x30, 0x59, 0x45, 0x48, 0xfe, 0x40, 0xa0, 0x7c, 0x2a, 0xb3, 0x21, 0xed, 0x26, 0xa1, 0xa6, 0x85, 0x9c, + 0xa4, 0x9c, 0x66, 0xb2, 0x44, 0xd5, 0x00, 0x70, 0xe4, 0xa8, 0xb7, 0x88, 0x1b, 0xbc, 0xf3, 0x0b, 0x50, 0x38, 0x98, + 0xfa, 0x5b, 0x4f, 0xa2, 0x36, 0x77, 0x92, 0x72, 0x04, 0x93, 0xa2, 0xd8, 0x9d, 0x14, 0xb6, 0x5b, 0xe4, 0x2c, 0x6e, + 0xf1, 0x21, 0xa9, 0x2a, 0x42, 0x64, 0x31, 0x30, 0xc4, 0xab, 0x89, 0x76, 0x94, 0xe1, 0xc0, 0x37, 0x0b, 0x33, 0x9d, + 0xf0, 0xea, 0xb1, 0x8b, 0x04, 0x95, 0xc2, 0xcf, 0xd2, 0xc8, 0x12, 0xa7, 0xf4, 0xe0, 0x84, 0x01, 0xb7, 0xdc, 0x8a, + 0xd5, 0xf7, 0x57, 0x94, 0x99, 0x50, 0x9a, 0x89, 0xb1, 0xa2, 0x7e, 0x40, 0xc0, 0x3d, 0x49, 0x98, 0x78, 0x22, 0xf4, + 0xd6, 0x76, 0xcd, 0x3f, 0xa9, 0x3e, 0x5b, 0xf8, 0x42, 0x6c, 0x01, 0xf3, 0x86, 0xc0, 0x04, 0x1a, 0x37, 0x9b, 0x51, + 0x2c, 0xa1, 0xf1, 0x03, 0xca, 0xa2, 0xdb, 0x59, 0x82, 0xaa, 0xb7, 0x8a, 0x0e, 0x43, 0x5d, 0x00, 0x2d, 0xad, 0x9e, + 0xfd, 0x98, 0xeb, 0x7d, 0x1e, 0xe5, 0x56, 0x1f, 0x60, 0xac, 0x6e, 0x00, 0x1d, 0x69, 0xd8, 0xf6, 0x6a, 0x78, 0xb9, + 0xa7, 0x9a, 0x88, 0x33, 0x9e, 0x2c, 0xaf, 0x0c, 0xfd, 0x86, 0x6c, 0x3d, 0xee, 0x3c, 0x51, 0xbb, 0xa8, 0xbc, 0xec, + 0x00, 0x91, 0x5a, 0x58, 0xd9, 0x8c, 0x7a, 0x81, 0x64, 0x5d, 0xdf, 0xac, 0x4a, 0x48, 0xd2, 0x23, 0xec, 0x13, 0xfe, + 0x7a, 0x19, 0x49, 0xa8, 0xa0, 0xd5, 0x4c, 0x65, 0xe9, 0xda, 0x6c, 0x40, 0x2b, 0xc0, 0x40, 0x67, 0xe2, 0x21, 0x70, + 0xf4, 0xba, 0x5e, 0x7a, 0xe4, 0x33, 0x4c, 0x7d, 0x18, 0x4a, 0x6a, 0x96, 0x8d, 0xb6, 0x9e, 0xc4, 0xcf, 0xe9, 0x58, + 0x62, 0x43, 0x0b, 0x09, 0x6b, 0xd2, 0xde, 0x16, 0x7e, 0xd5, 0x99, 0xdd, 0xd4, 0xfb, 0xce, 0xe7, 0x22, 0x44, 0x58, + 0x79, 0x7e, 0x51, 0xaa, 0xb1, 0xa4, 0x10, 0xe1, 0xdd, 0xec, 0x85, 0x95, 0x58, 0xd6, 0x36, 0xef, 0x2b, 0xd3, 0xfc, + 0x4c, 0x4e, 0x7f, 0xed, 0x18, 0xa8, 0xa0, 0x5f, 0xf3, 0x72, 0x6b, 0x76, 0x22, 0x82, 0x47, 0xa5, 0x20, 0x1f, 0x68, + 0xe2, 0xb4, 0x29, 0x47, 0xdd, 0xbe, 0x8b, 0x55, 0x69, 0xbf, 0x01, 0x07, 0x6e, 0xff, 0x0d, 0xb0, 0x02, 0x29, 0x40, + 0xc0, 0xcc, 0xbd, 0xac, 0xb2, 0x1e, 0x84, 0x36, 0xc8, 0xa0, 0xcf, 0x49, 0xfc, 0xc1, 0xc7, 0x3d, 0xcb, 0x92, 0x81, + 0xad, 0x40, 0x0b, 0x08, 0x40, 0xe1, 0x36, 0xa2, 0x9f, 0xdf, 0x40, 0xbe, 0x62, 0x7e, 0xd4, 0xe0, 0x84, 0xfa, 0x2c, + 0xba, 0x2e, 0x82, 0xf3, 0x31, 0xb2, 0xf1, 0x07, 0x56, 0x43, 0x68, 0x22, 0xe2, 0xa8, 0x0d, 0x8a, 0x94, 0xa8, 0xa1, + 0x23, 0x3f, 0x35, 0x06, 0xda, 0xaa, 0xe2, 0x35, 0x7e, 0xd6, 0x66, 0xb7, 0x2e, 0x60, 0x91, 0x1f, 0x9c, 0x1e, 0xb9, + 0x20, 0xcc, 0x1e, 0xdc, 0x34, 0xfd, 0xbf, 0xa5, 0x70, 0xf9, 0x40, 0xcf, 0xc6, 0x63, 0x4d, 0xf1, 0x54, 0x39, 0xd3, + 0xc1, 0x8d, 0x91, 0x1f, 0xa5, 0xce, 0x21, 0xac, 0x14, 0xfe, 0x5b, 0xe6, 0x73, 0xbb, 0xf5, 0x61, 0xb2, 0xbb, 0x2c, + 0x88, 0xe0, 0xe2, 0x92, 0xbd, 0x41, 0xc5, 0x1b, 0x90, 0x39, 0x04, 0xd9, 0x3b, 0x9f, 0x6a, 0x7f, 0x6c, 0x28, 0x95, + 0x5f, 0xd7, 0x36, 0xdf, 0x86, 0x37, 0x07, 0xe9, 0x16, 0x48, 0xac, 0xd7, 0x04, 0x6d, 0xe5, 0xf9, 0x12, 0xcd, 0x06, + 0x0d, 0x45, 0x63, 0x66, 0xf7, 0x17, 0x75, 0xe6, 0x2a, 0xb8, 0x75, 0xf7, 0x42, 0xa3, 0xa2, 0x58, 0xe8, 0x7b, 0x95, + 0x4d, 0xe0, 0xa2, 0x87, 0x57, 0x32, 0x4f, 0xb7, 0x2b, 0x12, 0xb5, 0xd8, 0x08, 0x31, 0xcb, 0x1b, 0xdc, 0xde, 0x55, + 0xf6, 0x67, 0xb8, 0x93, 0x0d, 0x30, 0x5b, 0xd0, 0x5b, 0x76, 0x48, 0x90, 0xfa, 0xd4, 0x29, 0xe5, 0x97, 0xf5, 0x47, + 0x99, 0xb6, 0xc0, 0xab, 0xf5, 0x40, 0x15, 0x73, 0x30, 0x43, 0x9d, 0x56, 0xdc, 0xeb, 0x44, 0x32, 0xf4, 0xae, 0x28, + 0xcd, 0x20, 0x11, 0xf6, 0x09, 0x2f, 0x61, 0xfa, 0x01, 0x2b, 0x2f, 0xb7, 0xf0, 0xc6, 0xb1, 0xec, 0xb5, 0x3a, 0x28, + 0x09, 0xaa, 0x80, 0xfc, 0x61, 0x78, 0xd6, 0xb2, 0x26, 0x77, 0x87, 0x23, 0x81, 0x2f, 0x17, 0x32, 0x11, 0xcc, 0x0d, + 0xe4, 0xcb, 0xb9, 0xb8, 0x10, 0x89, 0x2a, 0xc4, 0x78, 0xc9, 0xd2, 0xd1, 0xbb, 0x71, 0xd2, 0xa8, 0xd5, 0xf4, 0xa1, + 0x50, 0x71, 0x1b, 0xd7, 0x7a, 0x74, 0xbc, 0x60, 0x39, 0x1b, 0x8d, 0xee, 0x8a, 0x75, 0x4b, 0x79, 0x0b, 0xa5, 0x11, + 0x36, 0x52, 0x5f, 0x90, 0x65, 0x69, 0x16, 0x58, 0x2f, 0xc0, 0x16, 0xc1, 0x62, 0xc0, 0xf2, 0xd6, 0x59, 0x16, 0xb1, + 0xfa, 0xbd, 0xaf, 0x55, 0x8e, 0xc3, 0x90, 0x25, 0x21, 0x89, 0xe6, 0x55, 0x14, 0xc6, 0x18, 0x6a, 0x1c, 0x4d, 0x51, + 0xa5, 0x84, 0x31, 0x77, 0x23, 0xc3, 0x2e, 0xd6, 0x39, 0xc6, 0xd2, 0x48, 0xd2, 0xf0, 0x4d, 0x39, 0xa6, 0x27, 0xab, + 0xb1, 0x36, 0x22, 0x1b, 0x39, 0x34, 0x9e, 0xcb, 0xd5, 0x8c, 0x55, 0xee, 0xd0, 0xdd, 0x5a, 0xa9, 0xec, 0x42, 0x13, + 0x0a, 0xa3, 0xbd, 0xc6, 0x35, 0xc9, 0xa2, 0x5d, 0x83, 0x55, 0xfa, 0x92, 0x66, 0x8f, 0x38, 0x94, 0x6f, 0xc3, 0x56, + 0x55, 0xea, 0x02, 0xcd, 0xf9, 0xd0, 0x2b, 0xfc, 0x8d, 0x74, 0x72, 0x8e, 0x8a, 0x1e, 0xdc, 0x74, 0xdb, 0xc5, 0xbf, + 0x68, 0xa1, 0xfb, 0x2c, 0x7f, 0xce, 0x3c, 0x16, 0x2a, 0x54, 0xab, 0xab, 0x89, 0x2d, 0x99, 0xa1, 0xe1, 0x6b, 0x02, + 0xae, 0x44, 0xbe, 0x18, 0x60, 0x67, 0x94, 0xce, 0x25, 0xed, 0x54, 0x0e, 0x49, 0x4b, 0x36, 0x4e, 0xdc, 0x64, 0x23, + 0xda, 0xe5, 0x8f, 0xb1, 0xc5, 0xca, 0x4b, 0xd6, 0xad, 0x0f, 0xac, 0xf3, 0xf8, 0x3c, 0xab, 0xbc, 0x75, 0x6f, 0xc6, + 0xbf, 0xda, 0x0c, 0x13, 0xf6, 0xce, 0x6e, 0x70, 0xa9, 0xec, 0xd8, 0xa8, 0x91, 0xd3, 0x13, 0x3b, 0x5a, 0xe6, 0x22, + 0xc3, 0x6b, 0xb4, 0xaa, 0xb1, 0x90, 0xe3, 0x16, 0x7e, 0x0e, 0x34, 0x12, 0x8b, 0xa4, 0x58, 0x40, 0xe7, 0xfb, 0xd5, + 0x87, 0x17, 0x58, 0xcd, 0x63, 0xae, 0xc9, 0xd4, 0xa2, 0xce, 0x9c, 0xba, 0x50, 0x7d, 0x5e, 0x75, 0x5f, 0xd7, 0x2a, + 0xb8, 0x10, 0xd7, 0x9f, 0xa0, 0xe9, 0xaa, 0x9e, 0xfb, 0x96, 0x83, 0xd4, 0x94, 0x67, 0x10, 0xc7, 0xfa, 0xd3, 0x73, + 0x73, 0x23, 0x5b, 0xad, 0x8f, 0xd6, 0x51, 0x26, 0x5e, 0x8c, 0xc4, 0x16, 0x7e, 0xc7, 0x19, 0xd4, 0xa2, 0xbe, 0xcf, + 0x2a, 0x8a, 0x93, 0x80, 0xcb, 0x70, 0x05, 0x27, 0x30, 0xd5, 0x02, 0x03, 0x25, 0x39, 0xd1, 0x80, 0x46, 0xd6, 0xb9, + 0x3a, 0x78, 0xb9, 0x33, 0xdf, 0x34, 0x09, 0xa1, 0x83, 0x39, 0x83, 0x7b, 0x25, 0xdf, 0xec, 0xbb, 0x4a, 0x1d, 0x4c, + 0xb5, 0xf3, 0xda, 0x84, 0xad, 0x66, 0x7a, 0xda, 0x35, 0xb4, 0x42, 0xf4, 0x5c, 0x52, 0xcf, 0x90, 0x32, 0x56, 0x91, + 0xaa, 0x59, 0x1a, 0x87, 0x77, 0x8f, 0x84, 0x94, 0x29, 0xdb, 0x9d, 0x83, 0xf3, 0x0e, 0xa2, 0x12, 0xa9, 0xb2, 0x6e, + 0x0b, 0x23, 0x03, 0x3d, 0xe7, 0x58, 0x57, 0x51, 0xac, 0xa0, 0x18, 0x82, 0x5c, 0xe8, 0xa4, 0x15, 0x49, 0xa5, 0x1f, + 0x77, 0x16, 0x96, 0x51, 0x67, 0x65, 0x2e, 0x96, 0xcd, 0x75, 0xd4, 0xbb, 0x51, 0xff, 0xcc, 0xbb, 0x76, 0x39, 0x1d, + 0x9b, 0xc0, 0x4c, 0x28, 0x85, 0x05, 0xd2, 0x2c, 0x7f, 0x8b, 0xd3, 0xfb, 0xf1, 0xae, 0xe8, 0xd7, 0xc3, 0x66, 0x21, + 0x73, 0xb6, 0x02, 0x07, 0x90, 0xe9, 0xb8, 0xfa, 0x9d, 0x23, 0xa3, 0x8c, 0x42, 0x21, 0xad, 0xef, 0x41, 0x31, 0xd8, + 0x8e, 0xa9, 0x84, 0xe8, 0xd8, 0xdc, 0xcd, 0x00, 0x1d, 0xb4, 0xb1, 0xd5, 0x7b, 0x08, 0x36, 0x93, 0xb4, 0xe2, 0x2c, + 0x81, 0x8e, 0xd5, 0x4f, 0x2d, 0x55, 0x2f, 0x0d, 0x81, 0x41, 0xbf, 0x05, 0x82, 0xc0, 0x0b, 0x11, 0x7e, 0x66, 0x5e, + 0xd9, 0x20, 0xc2, 0x43, 0xf7, 0x06, 0xa0, 0x0c, 0xb1, 0xd6, 0x51, 0x2f, 0x8b, 0x85, 0xf7, 0x97, 0x05, 0x6d, 0xd1, + 0xcc, 0x51, 0x24, 0xa0, 0x7f, 0x85, 0x13, 0x57, 0x96, 0xf1, 0x09, 0x20, 0xa0, 0xcf, 0x91, 0xa4, 0xf8, 0xe8, 0x7d, + 0xaf, 0x9f, 0xa6, 0x94, 0x48, 0x9d, 0xf3, 0xd2, 0x93, 0xdc, 0xe0, 0xef, 0x3b, 0xcf, 0x1b, 0xaf, 0xac, 0x4a, 0x9e, + 0xfb, 0x7b, 0xba, 0x64, 0x71, 0x3d, 0x70, 0x7c, 0xb5, 0x94, 0xc9, 0xe6, 0xca, 0xc5, 0x04, 0x59, 0xb0, 0xf1, 0xbe, + 0x67, 0x46, 0x61, 0xdf, 0x40, 0xbe, 0x2b, 0xe6, 0x23, 0x8c, 0x6b, 0x2b, 0x9e, 0xbd, 0x15, 0x0f, 0x73, 0x4e, 0x49, + 0x91, 0xd4, 0x76, 0x4e, 0x81, 0x54, 0x67, 0x54, 0x5b, 0x90, 0x21, 0xe6, 0x02, 0x59, 0xf5, 0x29, 0x0e, 0xce, 0x96, + 0xa6, 0x81, 0x28, 0x5a, 0xca, 0x8f, 0x0a, 0x15, 0x82, 0xff, 0x1a, 0x88, 0x99, 0x46, 0x15, 0x60, 0x6e, 0x24, 0xd4, + 0xe1, 0x20, 0x9e, 0xf0, 0x74, 0x2f, 0x4d, 0x2b, 0x4d, 0x27, 0xee, 0xb4, 0x88, 0xa8, 0xfe, 0x72, 0x6e, 0x93, 0xa0, + 0x59, 0xf5, 0x2a, 0x0a, 0x97, 0x62, 0x49, 0x04, 0xd7, 0xcb, 0xea, 0xaa, 0x1f, 0x51, 0xaa, 0x7b, 0x65, 0xc1, 0x75, + 0xce, 0x02, 0x83, 0xe3, 0x5b, 0x8f, 0x74, 0x7b, 0x9e, 0x2e, 0xaf, 0x91, 0xdb, 0xa6, 0xc0, 0x8d, 0x8f, 0x99, 0xd0, + 0x95, 0xb8, 0x9a, 0x0d, 0x74, 0x85, 0x79, 0xdb, 0xae, 0xf8, 0x4a, 0xb0, 0x36, 0xff, 0x75, 0x3f, 0x03, 0xef, 0x8b, + 0x17, 0x61, 0xc1, 0x4c, 0x15, 0x8c, 0x62, 0xe2, 0x17, 0x61, 0x89, 0x30, 0xbc, 0x68, 0x6e, 0xce, 0xf6, 0xf9, 0xe6, + 0x3c, 0x02, 0x1c, 0x16, 0xe5, 0x09, 0x73, 0x7b, 0x06, 0x14, 0x54, 0x9b, 0xb0, 0xa9, 0xd6, 0x80, 0xb1, 0x3d, 0x4b, + 0xf3, 0x31, 0xdf, 0x9b, 0x0e, 0x50, 0x4f, 0xad, 0x39, 0xc5, 0x60, 0x0c, 0x61, 0xa2, 0xdb, 0x80, 0x02, 0xa4, 0x26, + 0x0b, 0x87, 0xcc, 0xfa, 0x5b, 0xca, 0x0b, 0x6d, 0x62, 0x43, 0x5f, 0x92, 0xa5, 0xb5, 0x56, 0xf0, 0x13, 0x34, 0x4d, + 0xc1, 0x29, 0x0e, 0x3f, 0x48, 0xbc, 0xe7, 0xde, 0x79, 0x8d, 0x44, 0x46, 0x3d, 0x17, 0x7e, 0x21, 0xc2, 0xca, 0x7d, + 0xc4, 0x9c, 0x73, 0x53, 0x13, 0xb2, 0x2f, 0x5d, 0xb2, 0x96, 0xd5, 0x24, 0xe0, 0xd1, 0x73, 0xa1, 0x42, 0x3b, 0x23, + 0xde, 0x5d, 0x5b, 0x79, 0xab, 0x7a, 0x34, 0x03, 0x56, 0x73, 0xdc, 0xb6, 0x98, 0x86, 0xa9, 0x28, 0xa9, 0x84, 0x20, + 0x6e, 0x09, 0x91, 0x85, 0x61, 0xcb, 0x1a, 0x7b, 0x9f, 0x58, 0xad, 0xa7, 0x24, 0x00, 0x70, 0x25, 0x0d, 0xdd, 0x33, + 0x94, 0x09, 0xa9, 0x97, 0xb4, 0x40, 0x39, 0xe4, 0x6a, 0xe2, 0xe5, 0xc6, 0x3d, 0x86, 0x81, 0x1b, 0xb3, 0xb5, 0xc8, + 0x34, 0x26, 0x44, 0x96, 0x81, 0x00, 0x71, 0x68, 0x5e, 0x9a, 0xca, 0xa2, 0xd3, 0x4d, 0x50, 0x74, 0x51, 0x8f, 0x33, + 0x5c, 0x59, 0x88, 0xbb, 0x64, 0xe8, 0x1c, 0x78, 0x39, 0x5d, 0xe3, 0xe5, 0x24, 0x15, 0x02, 0xaf, 0x82, 0x95, 0x07, + 0x12, 0xd9, 0x03, 0xed, 0xa0, 0x6c, 0x00, 0x24, 0xb9, 0x13, 0x5c, 0x29, 0x48, 0x6b, 0x2b, 0xc8, 0x21, 0xfe, 0xa7, + 0xb6, 0x1c, 0xa5, 0x02, 0xf2, 0xd4, 0xb1, 0xe5, 0xa4, 0xf1, 0x3c, 0x5c, 0x0a, 0x6f, 0xa4, 0x36, 0xcc, 0x60, 0xc5, + 0x0a, 0x16, 0x22, 0x33, 0x25, 0xcf, 0xad, 0x60, 0x1b, 0xaf, 0xde, 0xc4, 0x8c, 0x44, 0x85, 0xe9, 0xa3, 0xd8, 0x59, + 0xdd, 0x0d, 0x13, 0x6c, 0x2b, 0x9e, 0xb2, 0xdb, 0x8f, 0xc8, 0x7f, 0x4c, 0x50, 0x92, 0xa6, 0xc3, 0x97, 0x4a, 0xa6, + 0x93, 0xf2, 0xe2, 0x9d, 0x16, 0x46, 0x4b, 0x0e, 0x01, 0x17, 0x7c, 0x06, 0xde, 0x9d, 0x89, 0xfc, 0xcb, 0xa6, 0x35, + 0xc9, 0x1c, 0xa3, 0xaa, 0x8a, 0x16, 0x12, 0x8d, 0x71, 0x51, 0xb6, 0x26, 0x16, 0x0f, 0x16, 0x57, 0x03, 0x48, 0xa6, + 0x31, 0x2c, 0xf0, 0xf2, 0xc8, 0x7c, 0xcd, 0xe6, 0x45, 0xf5, 0x44, 0x96, 0x8a, 0x2e, 0xc8, 0xe5, 0x67, 0x18, 0x9b, + 0x99, 0x32, 0xac, 0x16, 0xcb, 0x70, 0x38, 0x2b, 0x08, 0x7b, 0x3f, 0xe0, 0x82, 0xe7, 0xa6, 0x8f, 0xbe, 0x5c, 0x30, + 0xa9, 0x1c, 0x46, 0x26, 0x7f, 0x96, 0x5c, 0xbc, 0x22, 0xac, 0x9e, 0x6c, 0xe7, 0x00, 0x72, 0xa1, 0x06, 0xe5, 0x08, + 0x87, 0x96, 0x13, 0xd8, 0xc4, 0x58, 0x44, 0x67, 0xd5, 0x54, 0x35, 0xc2, 0xd2, 0x7c, 0xe9, 0x46, 0x99, 0x37, 0xd9, + 0x76, 0x86, 0x4c, 0xd8, 0xba, 0x1f, 0x89, 0x20, 0x37, 0x1e, 0x0c, 0xa5, 0x31, 0xef, 0xc3, 0x1a, 0xac, 0xfa, 0x44, + 0x5e, 0xce, 0xa3, 0xaa, 0x11, 0x32, 0xc3, 0x29, 0xf9, 0x69, 0xf4, 0x14, 0x4d, 0x77, 0x92, 0x13, 0x2a, 0xda, 0x24, + 0x2a, 0x0a, 0x6c, 0xe2, 0x45, 0x29, 0x24, 0x82, 0xe2, 0x2e, 0xc7, 0x43, 0x0d, 0xf3, 0x0f, 0x27, 0x07, 0x57, 0x22, + 0x56, 0x07, 0x6e, 0xef, 0x1b, 0x48, 0xf2, 0x33, 0x99, 0xf4, 0x46, 0xba, 0x77, 0x37, 0xe5, 0xe1, 0x69, 0x22, 0x33, + 0xf7, 0x91, 0xb8, 0x8e, 0x8b, 0x8a, 0x42, 0x05, 0xdc, 0x6f, 0xd5, 0x5e, 0x7c, 0x92, 0x74, 0x82, 0xcc, 0x30, 0x73, + 0x6d, 0x7c, 0x6e, 0x8a, 0x91, 0x9a, 0x93, 0x54, 0x81, 0x7c, 0x72, 0xf7, 0x97, 0x46, 0x90, 0x9b, 0xf0, 0x17, 0xdf, + 0x3b, 0xaf, 0x93, 0xf2, 0x1c, 0xed, 0xe7, 0x44, 0xf4, 0xba, 0x1c, 0x6d, 0x31, 0x68, 0x63, 0x4e, 0x8c, 0x7c, 0xbb, + 0xbb, 0x88, 0x7c, 0xb9, 0xe1, 0x14, 0xc3, 0x54, 0x05, 0x32, 0x79, 0xf8, 0x43, 0x2d, 0x0f, 0x84, 0xfd, 0x29, 0x99, + 0x61, 0xa6, 0x89, 0xa8, 0xc2, 0x16, 0x38, 0x05, 0x0e, 0xd4, 0x5c, 0x39, 0x51, 0xf3, 0x70, 0xa0, 0x4a, 0x71, 0xf6, + 0x49, 0x22, 0xce, 0x5c, 0xc6, 0x36, 0x7e, 0x25, 0x5d, 0x30, 0x97, 0xd5, 0x48, 0x5b, 0x11, 0x2d, 0x8f, 0x14, 0x02, + 0x82, 0x5a, 0x8a, 0xa5, 0xd8, 0x12, 0x40, 0x30, 0xbe, 0xc5, 0xf3, 0xfb, 0x18, 0xb1, 0x0a, 0xc5, 0xcb, 0x34, 0xb2, + 0xa2, 0x5d, 0x7e, 0x63, 0x17, 0xa6, 0x0b, 0x26, 0xe0, 0x66, 0x24, 0xc5, 0xc8, 0x73, 0x87, 0x57, 0xe5, 0x46, 0xea, + 0x74, 0xbf, 0x2d, 0x0a, 0x05, 0x4f, 0x8b, 0x46, 0xe7, 0xc6, 0x54, 0x11, 0x5c, 0x35, 0x2a, 0xb6, 0x38, 0x38, 0x9c, + 0x7f, 0xa8, 0x99, 0x85, 0x74, 0x4d, 0x94, 0x23, 0x89, 0xfc, 0x7e, 0x11, 0x1c, 0x6a, 0x94, 0x17, 0xa2, 0x10, 0xa9, + 0x9f, 0x18, 0x72, 0x59, 0xc4, 0xec, 0x30, 0x37, 0xaa, 0xcb, 0x16, 0xc0, 0x96, 0xae, 0xc3, 0xc8, 0x50, 0x88, 0x3c, + 0x62, 0x98, 0x99, 0x26, 0xf5, 0x71, 0xe5, 0x20, 0x8b, 0xae, 0x52, 0x83, 0x34, 0xef, 0xb8, 0x91, 0x37, 0x49, 0xa2, + 0x84, 0x0c, 0xf1, 0xcc, 0x7c, 0x52, 0x67, 0x27, 0xb1, 0x57, 0x69, 0x29, 0xa4, 0x23, 0xd5, 0x4d, 0xa2, 0xd8, 0xe9, + 0x3e, 0x13, 0x7a, 0x5f, 0xb5, 0xf7, 0xb1, 0x18, 0xbc, 0x6e, 0x9b, 0x30, 0x7f, 0xf4, 0xf9, 0x4d, 0x7c, 0x47, 0x4d, + 0xd5, 0x13, 0x69, 0x41, 0x27, 0xa1, 0x35, 0x00, 0xee, 0xf3, 0xe6, 0xce, 0xf6, 0x60, 0xb8, 0x4d, 0x00, 0x5a, 0xc1, + 0x59, 0x4e, 0x37, 0x42, 0x56, 0xb7, 0x4f, 0x5a, 0xa7, 0x89, 0x8b, 0x38, 0xd8, 0x01, 0xd2, 0x10, 0xb8, 0x0a, 0x3e, + 0x67, 0x5f, 0x21, 0xf5, 0x23, 0x35, 0xb1, 0xb3, 0x4d, 0xd2, 0x83, 0xb6, 0xf7, 0xc9, 0xe5, 0xbc, 0x9f, 0x67, 0x2c, + 0x26, 0x0b, 0xf7, 0x4e, 0xfe, 0x10, 0xae, 0xe2, 0xa8, 0x16, 0x23, 0xe6, 0x0a, 0x01, 0xa6, 0xaa, 0x61, 0xb8, 0xd9, + 0x57, 0x4a, 0x63, 0xdc, 0x62, 0x0a, 0x40, 0x05, 0xc7, 0xa4, 0x5e, 0x7d, 0x0c, 0x55, 0xeb, 0xfe, 0xe7, 0x0a, 0xd6, + 0xd5, 0x6e, 0x59, 0xef, 0xf4, 0x00, 0x98, 0x00, 0xfc, 0x01, 0xa8, 0xaa, 0xe7, 0xe5, 0xce, 0xbf, 0xb0, 0x57, 0x10, + 0xa4, 0x24, 0xe0, 0x5e, 0x25, 0xfd, 0xdf, 0x6a, 0x1a, 0x08, 0x9a, 0xaf, 0x97, 0xf5, 0xb1, 0xcf, 0x44, 0x22, 0xf7, + 0x3c, 0x69, 0xf1, 0xf1, 0x1e, 0x78, 0x0b, 0x38, 0x7e, 0x19, 0x5b, 0x97, 0x74, 0xce, 0xfc, 0x41, 0x02, 0xcb, 0x1b, + 0xb5, 0xaf, 0x1e, 0x5f, 0xd2, 0x89, 0x60, 0xa7, 0x28, 0x50, 0x1f, 0x22, 0x02, 0x4a, 0x04, 0x4a, 0x8e, 0xb4, 0x84, + 0xee, 0x27, 0x9f, 0xa0, 0x5a, 0x40, 0x48, 0x9d, 0x12, 0x16, 0xf5, 0xed, 0xa0, 0x8e, 0xe0, 0x6d, 0x33, 0x72, 0xe2, + 0xc0, 0xb9, 0x81, 0x93, 0xf2, 0x39, 0xec, 0x6a, 0x84, 0xcb, 0xe3, 0x0d, 0x9e, 0xc0, 0x97, 0xe8, 0x37, 0x8e, 0x6f, + 0xe2, 0x79, 0x8b, 0x41, 0xe4, 0x1c, 0xb2, 0x9c, 0x7c, 0x21, 0xa2, 0x46, 0x24, 0x09, 0x75, 0xd8, 0x85, 0x90, 0xd6, + 0x17, 0x30, 0x38, 0x5e, 0x31, 0x8d, 0xa1, 0x7a, 0x18, 0x83, 0xc1, 0xe6, 0xf9, 0xed, 0xe9, 0x74, 0xeb, 0x21, 0xf9, + 0x20, 0xea, 0x8b, 0x88, 0x77, 0x4d, 0xa9, 0x51, 0x64, 0x79, 0xd8, 0xb4, 0xae, 0x53, 0xc3, 0x7b, 0x88, 0xc3, 0xbf, + 0x0a, 0x90, 0x00, 0xc5, 0x6e, 0xd3, 0xe7, 0x5c, 0xb0, 0xd1, 0x3b, 0x4d, 0x44, 0x68, 0xa1, 0x19, 0xa4, 0x70, 0xd5, + 0x7c, 0x81, 0x95, 0x69, 0xa7, 0xff, 0x45, 0xe7, 0xb6, 0x24, 0x01, 0x41, 0xb4, 0xd2, 0xef, 0xab, 0x30, 0x61, 0x89, + 0x31, 0x01, 0xde, 0x11, 0x62, 0xce, 0x33, 0x58, 0x49, 0x2c, 0x40, 0x72, 0xb4, 0x5e, 0x97, 0x1f, 0xcb, 0x74, 0x8a, + 0xd1, 0xe8, 0x4d, 0x9d, 0x64, 0xaa, 0xf5, 0xb5, 0x04, 0xf0, 0xc7, 0x79, 0x0d, 0x5b, 0xe6, 0x1e, 0x08, 0xb0, 0x63, + 0x25, 0xa1, 0x49, 0xb7, 0x64, 0xa7, 0xba, 0xe3, 0x66, 0x93, 0x9a, 0x72, 0x3f, 0x6f, 0x55, 0xb2, 0x54, 0x82, 0xc3, + 0xba, 0xf6, 0xa0, 0xfc, 0x21, 0x15, 0xe6, 0x32, 0x54, 0x56, 0x7b, 0x08, 0x90, 0xb0, 0x94, 0xe4, 0xa3, 0x9a, 0x21, + 0xe5, 0xe3, 0x53, 0x45, 0x91, 0x90, 0x33, 0x5e, 0x2c, 0x6b, 0xc0, 0x00, 0xef, 0xce, 0x5d, 0x4a, 0xeb, 0x1d, 0x7a, + 0xe4, 0xbd, 0x47, 0xbc, 0x80, 0xbd, 0x29, 0x61, 0x8f, 0x3b, 0x04, 0x69, 0x5f, 0x33, 0x14, 0xf2, 0x6f, 0x86, 0x92, + 0xc6, 0xfe, 0x7d, 0xcb, 0xe9, 0x41, 0xcf, 0xc8, 0xf4, 0x91, 0x0b, 0x7f, 0xae, 0xf1, 0xf6, 0x83, 0x7b, 0xb6, 0x61, + 0x3c, 0xad, 0x24, 0x30, 0x64, 0x13, 0x77, 0xf3, 0x92, 0x57, 0x6c, 0xb1, 0x7c, 0x77, 0xfe, 0x3a, 0x59, 0xa3, 0x20, + 0x70, 0x0b, 0x3e, 0xd0, 0x32, 0x52, 0x69, 0x90, 0x94, 0x14, 0xaf, 0xce, 0x81, 0x49, 0xa7, 0x9b, 0x5a, 0x25, 0x6a, + 0xd5, 0xa0, 0x57, 0x7d, 0x8a, 0x61, 0x59, 0xd2, 0xb6, 0xc4, 0x42, 0xb3, 0xdf, 0x87, 0x01, 0x26, 0x3f, 0x46, 0xce, + 0xb8, 0xbd, 0x03, 0xba, 0x07, 0x45, 0x6d, 0x19, 0x27, 0x41, 0x52, 0xaa, 0x20, 0x80, 0x74, 0xbf, 0xce, 0x63, 0x79, + 0xd5, 0x31, 0xd1, 0x61, 0xd1, 0xaa, 0x11, 0xc8, 0x09, 0x75, 0x63, 0x04, 0x86, 0x90, 0x3d, 0x53, 0xb1, 0xf4, 0xd0, + 0xeb, 0x30, 0x54, 0x7d, 0xee, 0xc7, 0xb0, 0xa6, 0xd5, 0x98, 0x25, 0x0f, 0x92, 0xcc, 0xa8, 0xfa, 0x46, 0xdf, 0xa2, + 0xd5, 0x59, 0xcf, 0xf1, 0x9e, 0x37, 0xd3, 0xd0, 0x4d, 0x65, 0xff, 0xd0, 0xd8, 0x81, 0x7f, 0x5b, 0x46, 0xa2, 0x5a, + 0x72, 0x96, 0xf6, 0x4a, 0xe6, 0x53, 0x8f, 0x02, 0x54, 0xdf, 0xf1, 0xee, 0xd2, 0x80, 0x28, 0x39, 0x3a, 0x77, 0x9b, + 0x1b, 0x70, 0xa9, 0x3e, 0xd0, 0x38, 0x3d, 0x86, 0x62, 0x60, 0xe7, 0xb7, 0xaf, 0xa7, 0xeb, 0x10, 0x23, 0x87, 0x51, + 0xe0, 0x28, 0xbd, 0xf4, 0x2e, 0x79, 0xb5, 0xe2, 0xc6, 0x15, 0xb6, 0xbb, 0x97, 0x96, 0xdf, 0x25, 0xdb, 0xb0, 0x3a, + 0xc9, 0xfe, 0x18, 0xd9, 0xd8, 0xc7, 0x74, 0xac, 0x8e, 0xd1, 0xb9, 0x73, 0x00, 0x5c, 0xb9, 0x94, 0xc0, 0xdd, 0x4a, + 0xae, 0x8e, 0x7f, 0xe5, 0xf6, 0x54, 0x4e, 0x37, 0xbd, 0x2e, 0x5f, 0x3e, 0x39, 0xbb, 0x8a, 0x07, 0xad, 0xd0, 0x50, + 0x66, 0xe9, 0xb2, 0x4a, 0xea, 0x02, 0x79, 0xd6, 0xf1, 0x5c, 0xb8, 0xeb, 0x2f, 0xbd, 0x8d, 0xd0, 0x80, 0x3d, 0x43, + 0x58, 0xcd, 0xa5, 0xa1, 0x3f, 0x97, 0xb3, 0x1e, 0x7b, 0x8b, 0x26, 0x13, 0xed, 0x2d, 0x7a, 0x4c, 0x69, 0x1c, 0x27, + 0xec, 0x0f, 0x38, 0x35, 0xde, 0x87, 0x74, 0xb5, 0x80, 0xd5, 0xc3, 0x2f, 0x0c, 0xc8, 0xcc, 0x01, 0x6e, 0xf7, 0xfc, + 0x73, 0xca, 0xd7, 0xbc, 0x8a, 0x42, 0x75, 0x93, 0x07, 0xd5, 0x94, 0x6c, 0x59, 0x07, 0x1b, 0xf6, 0xcf, 0x0a, 0x41, + 0x2d, 0x80, 0xe5, 0xd4, 0x74, 0xd9, 0xec, 0x7d, 0x12, 0xda, 0xb6, 0x5b, 0x4a, 0x78, 0x6f, 0x61, 0x4f, 0xec, 0xce, + 0xf2, 0x34, 0x29, 0x0f, 0xe3, 0x7f, 0x4c, 0xc8, 0x74, 0xc8, 0x5d, 0xb5, 0x92, 0x96, 0x29, 0xa6, 0xca, 0xde, 0x6f, + 0x1c, 0xbb, 0x39, 0x63, 0x24, 0x3e, 0x41, 0x0d, 0x1f, 0x2e, 0x3b, 0x7a, 0xb4, 0xe8, 0xed, 0x07, 0x47, 0x1a, 0x98, + 0xfa, 0x41, 0x46, 0x6e, 0x2a, 0x63, 0x1d, 0x00, 0x25, 0x4b, 0xf4, 0x67, 0xcb, 0x2e, 0x2d, 0x2a, 0x44, 0xa1, 0xc2, + 0xed, 0xec, 0x0f, 0xf7, 0x32, 0xab, 0x14, 0x11, 0xed, 0xde, 0x95, 0xe0, 0x0c, 0x71, 0x47, 0xbc, 0xe5, 0xa4, 0x01, + 0xc5, 0x68, 0xd1, 0x41, 0x4b, 0x8a, 0xb6, 0x47, 0xeb, 0xd5, 0x52, 0xca, 0xf3, 0xcc, 0x89, 0xec, 0x28, 0x60, 0xfd, + 0x70, 0x38, 0xf4, 0xed, 0x67, 0x55, 0xa4, 0xdd, 0x8f, 0xd9, 0x02, 0x77, 0x00, 0xf7, 0x5b, 0x16, 0xa6, 0x18, 0xa2, + 0xf3, 0x97, 0xd4, 0x18, 0x5d, 0x3f, 0x0a, 0x41, 0x1b, 0x8c, 0x21, 0x4f, 0x98, 0x5c, 0x93, 0x84, 0x86, 0x34, 0x46, + 0xad, 0x51, 0x20, 0x39, 0x27, 0xa6, 0x91, 0x98, 0x2d, 0x58, 0x4f, 0x23, 0x29, 0x5d, 0x44, 0xc8, 0x4c, 0x50, 0xd1, + 0x83, 0x22, 0x58, 0x92, 0x91, 0x16, 0xa9, 0xdc, 0x8b, 0x8e, 0xe2, 0x3d, 0x1f, 0x41, 0x73, 0xcd, 0xad, 0x1a, 0xd2, + 0x83, 0xe5, 0x8d, 0x86, 0x82, 0xac, 0xd2, 0xf1, 0x92, 0xfb, 0xa8, 0x0e, 0x22, 0x83, 0xa6, 0xad, 0xdf, 0xf6, 0x97, + 0xf1, 0x58, 0x93, 0x79, 0x46, 0x24, 0x18, 0x32, 0x0c, 0x39, 0x8c, 0x91, 0x7b, 0xab, 0xd2, 0xd3, 0x0f, 0x32, 0xf4, + 0xbb, 0xc5, 0x08, 0x60, 0xe2, 0x2b, 0x61, 0xb2, 0x2e, 0x77, 0x6a, 0xd4, 0x79, 0x97, 0x71, 0x22, 0x63, 0xe1, 0xfe, + 0xa3, 0xb0, 0x36, 0x24, 0x5a, 0xaf, 0x6e, 0xec, 0xf9, 0xc7, 0x0d, 0x7e, 0x52, 0x9a, 0x22, 0x6a, 0x4d, 0x52, 0xa7, + 0x03, 0x75, 0x4b, 0x1c, 0x83, 0xa3, 0x7c, 0x5c, 0xbc, 0xf0, 0xa0, 0xa5, 0x72, 0x43, 0x49, 0xac, 0x44, 0xdf, 0xdc, + 0x23, 0xfb, 0x02, 0x1a, 0x7b, 0x0a, 0xba, 0xd9, 0xe2, 0xa8, 0x56, 0xc6, 0x50, 0x8a, 0x39, 0x1c, 0xf6, 0xa1, 0xac, + 0x61, 0xa5, 0x3a, 0xb6, 0x5e, 0x1a, 0x77, 0xe3, 0x81, 0xc8, 0x50, 0x3b, 0x34, 0x0e, 0x71, 0x5f, 0x33, 0x23, 0x37, + 0x43, 0x13, 0xde, 0x21, 0x63, 0x70, 0x27, 0x8e, 0x97, 0x1a, 0x4b, 0xc2, 0x48, 0x88, 0x41, 0xbf, 0xb8, 0x17, 0xb3, + 0x45, 0x15, 0x24, 0x88, 0x6b, 0x1b, 0x15, 0x60, 0xe3, 0x15, 0xa2, 0x42, 0x7b, 0x6c, 0xeb, 0x78, 0x9e, 0x19, 0xb9, + 0x02, 0xc3, 0xc4, 0x1b, 0xd9, 0x8d, 0x9e, 0xa7, 0x72, 0xfc, 0x17, 0x61, 0xf5, 0x33, 0x16, 0x6c, 0xdd, 0x8a, 0x82, + 0x3f, 0x41, 0xe8, 0xe1, 0x41, 0xfb, 0x79, 0x89, 0x75, 0xfc, 0x8f, 0xad, 0xdf, 0x50, 0xd3, 0xaa, 0xd3, 0xd0, 0x0f, + 0xc7, 0x0f, 0x9d, 0x46, 0x07, 0xf9, 0xa7, 0xaf, 0x2e, 0x2d, 0x6e, 0x9a, 0xee, 0x6a, 0x5c, 0xbb, 0xaf, 0x50, 0x7d, + 0x38, 0xb6, 0x55, 0x17, 0xec, 0x0f, 0xe3, 0x38, 0xdc, 0x80, 0xc7, 0xc3, 0xf3, 0xe0, 0x06, 0x3c, 0xb8, 0xbf, 0x34, + 0xa6, 0xc7, 0xb3, 0xe7, 0x4b, 0xef, 0x2e, 0xc3, 0xb9, 0xc8, 0x35, 0x26, 0x7b, 0xea, 0xd7, 0xb6, 0x8b, 0x23, 0x8d, + 0xc0, 0xe8, 0xe8, 0xcd, 0x74, 0x41, 0x8d, 0x6b, 0x92, 0x51, 0x6b, 0x50, 0x7e, 0x42, 0x38, 0xbd, 0x7f, 0x7f, 0x6b, + 0x74, 0x84, 0x42, 0xc4, 0x8b, 0xc0, 0x7f, 0xdf, 0xc1, 0xdb, 0x7a, 0xd8, 0x99, 0x56, 0x67, 0xb9, 0xc4, 0x53, 0xd8, + 0x57, 0xa3, 0x5b, 0xd7, 0xe3, 0xc8, 0x28, 0xbd, 0xfc, 0xe0, 0x25, 0xc6, 0xc9, 0x4d, 0x7e, 0xc4, 0xb1, 0xaa, 0xdb, + 0x8b, 0xd5, 0x9f, 0x07, 0x41, 0x11, 0xfe, 0xf1, 0x82, 0x8c, 0x0f, 0x91, 0x8e, 0x72, 0x2a, 0x96, 0x62, 0x5a, 0x51, + 0x8d, 0x03, 0x50, 0x34, 0xfa, 0x25, 0xf4, 0xd5, 0x34, 0x18, 0x9b, 0xe7, 0x4a, 0x18, 0xdf, 0xf1, 0xbf, 0x1f, 0xbc, + 0xfb, 0x05, 0x9b, 0xe5, 0x2e, 0x18, 0xd6, 0x7d, 0x18, 0xa9, 0x4f, 0x02, 0xa8, 0xac, 0x9e, 0x65, 0x35, 0xd1, 0x76, + 0x50, 0xc7, 0xab, 0x99, 0xed, 0xbf, 0xef, 0x1c, 0x42, 0x4f, 0xab, 0x99, 0x52, 0x40, 0xe5, 0x96, 0x77, 0x88, 0x87, + 0xfa, 0x12, 0xbe, 0x8f, 0xf5, 0x55, 0xcc, 0xaf, 0xa8, 0xfa, 0x32, 0x56, 0x51, 0x10, 0x9a, 0x1f, 0x30, 0x34, 0xfc, + 0x90, 0x3c, 0xe3, 0x86, 0x83, 0xb9, 0x5f, 0x42, 0xff, 0xb2, 0xbe, 0x3f, 0x24, 0xf6, 0xb5, 0x8f, 0xdb, 0x75, 0xf3, + 0x35, 0xa7, 0x74, 0x18, 0x25, 0x78, 0x8e, 0xe3, 0xe6, 0xd0, 0x59, 0x1b, 0xed, 0xe8, 0xd4, 0x17, 0x69, 0x1d, 0x5d, + 0x60, 0xe8, 0xfb, 0xcc, 0x25, 0x5e, 0x39, 0xe2, 0xa0, 0x8f, 0xc4, 0x0d, 0x47, 0xdd, 0x5e, 0xd5, 0x8e, 0xd1, 0x31, + 0x06, 0x79, 0x29, 0x04, 0x90, 0x1c, 0xaa, 0xa7, 0xcd, 0xa2, 0x4d, 0x57, 0xce, 0x06, 0xe5, 0x9f, 0xeb, 0x5e, 0x3c, + 0xa0, 0x05, 0xa3, 0xba, 0xe1, 0x2f, 0x1e, 0xd2, 0xb8, 0xa1, 0xe5, 0x28, 0x2a, 0x25, 0x45, 0xa0, 0xb4, 0x8d, 0x0a, + 0x7a, 0xb3, 0x40, 0xf9, 0x60, 0xe9, 0x8f, 0x85, 0x2c, 0x75, 0x10, 0x2c, 0xe5, 0x34, 0xf5, 0x4a, 0x19, 0xd8, 0x63, + 0x23, 0xfe, 0xd3, 0x19, 0x1a, 0x44, 0xe6, 0xe6, 0x81, 0x1d, 0xe2, 0xe5, 0xa8, 0xa4, 0xa1, 0xbc, 0x61, 0xa0, 0x20, + 0xa8, 0xa9, 0x60, 0x11, 0xa4, 0xa8, 0x31, 0xed, 0x51, 0x31, 0xc8, 0xdc, 0xea, 0xb8, 0x81, 0x2e, 0x5f, 0x25, 0xb1, + 0x4b, 0xb5, 0xdb, 0x20, 0x57, 0x15, 0x3f, 0x06, 0xcf, 0x44, 0x5a, 0x07, 0xe9, 0x05, 0x8a, 0xa0, 0x2b, 0x8a, 0x48, + 0xaf, 0xca, 0x78, 0x11, 0xd6, 0xa2, 0xdc, 0x6a, 0xf4, 0xa0, 0x61, 0x18, 0x49, 0x85, 0xb7, 0x8d, 0x28, 0xc5, 0x7e, + 0x66, 0x5f, 0x61, 0x14, 0x3e, 0xe8, 0x50, 0x46, 0x9e, 0x2c, 0xda, 0xba, 0xf7, 0x6e, 0xd2, 0x88, 0x45, 0xa2, 0xce, + 0x6b, 0x1e, 0x99, 0xd2, 0x41, 0x93, 0x7c, 0x74, 0x5e, 0xce, 0xbc, 0x61, 0x32, 0xb2, 0x53, 0x72, 0x5c, 0x6a, 0x05, + 0x18, 0xb1, 0xf9, 0xdb, 0x6f, 0x1d, 0xc7, 0x33, 0x9f, 0x8e, 0x7e, 0x24, 0x3c, 0x5f, 0x66, 0x9e, 0x79, 0xba, 0x2d, + 0x0a, 0x97, 0x5c, 0x98, 0x53, 0xa1, 0x52, 0x83, 0x21, 0xf0, 0x57, 0x31, 0x78, 0x51, 0x26, 0xb8, 0x39, 0xb5, 0xeb, + 0x3e, 0xba, 0x8c, 0x88, 0x0e, 0xdf, 0x54, 0x68, 0xe6, 0xeb, 0xd7, 0xc9, 0x9d, 0x5c, 0x28, 0xa7, 0xd7, 0xaa, 0xc0, + 0xcb, 0x52, 0x65, 0x50, 0x8c, 0x51, 0xa5, 0xf4, 0xbc, 0xa0, 0x51, 0x9d, 0xa8, 0x14, 0x1c, 0x9a, 0xb1, 0xc0, 0x7f, + 0x48, 0xec, 0x2e, 0x79, 0xe8, 0x54, 0x00, 0x64, 0xca, 0xa2, 0xa1, 0xa3, 0x02, 0xf9, 0xdd, 0xc7, 0xd6, 0x8c, 0xb9, + 0x6a, 0x75, 0x59, 0x83, 0x14, 0x45, 0xdb, 0x53, 0x82, 0x34, 0x74, 0x87, 0x8b, 0x6d, 0x8a, 0x10, 0x6f, 0x0e, 0xc5, + 0x20, 0xa0, 0x15, 0x1a, 0x5f, 0x62, 0xaa, 0x95, 0x16, 0xf5, 0x80, 0xc2, 0xcb, 0x56, 0xc1, 0xdf, 0x72, 0xc1, 0x7d, + 0x81, 0x86, 0x43, 0x4c, 0x80, 0x00, 0x0c, 0x64, 0xb5, 0xfc, 0xfb, 0xa8, 0xa4, 0x98, 0xe9, 0x7b, 0xb5, 0xf9, 0x84, + 0xf7, 0xa5, 0x69, 0x72, 0x46, 0x30, 0x49, 0x71, 0x17, 0x32, 0x64, 0x11, 0xe1, 0xde, 0x2b, 0x3a, 0xa0, 0x6b, 0x2b, + 0x9a, 0x39, 0xf5, 0x88, 0x24, 0xb4, 0x05, 0x84, 0xd8, 0xe0, 0xc3, 0x6c, 0x59, 0x0e, 0x8d, 0x60, 0xd6, 0xc0, 0x8c, + 0xf9, 0x5e, 0xcb, 0x08, 0xa2, 0x92, 0x55, 0x2f, 0xbf, 0x07, 0x0e, 0xb4, 0xec, 0x4d, 0x60, 0xd1, 0x49, 0xda, 0x54, + 0x18, 0x08, 0x33, 0xf7, 0xe3, 0x07, 0xcf, 0x55, 0x32, 0x34, 0x7d, 0xac, 0x49, 0x0b, 0x8f, 0x86, 0x1b, 0x07, 0x5c, + 0xf9, 0xf8, 0x5c, 0xa2, 0x90, 0x37, 0xca, 0xb0, 0x2b, 0x77, 0x0e, 0xa8, 0x8f, 0x4c, 0x8d, 0x32, 0x04, 0x39, 0x01, + 0x19, 0xf0, 0xa0, 0xe3, 0x20, 0xf9, 0x3f, 0x20, 0x19, 0x19, 0x9c, 0xc0, 0xbd, 0x32, 0x23, 0x94, 0x2d, 0x28, 0xfc, + 0x91, 0x65, 0xdb, 0x07, 0xb4, 0xe7, 0x33, 0x9a, 0x14, 0x07, 0x92, 0x8d, 0x12, 0x3c, 0x8f, 0x7e, 0xa1, 0x84, 0x26, + 0x68, 0x93, 0x67, 0xe8, 0x23, 0xd9, 0x18, 0x29, 0x44, 0x26, 0x02, 0x07, 0x95, 0x03, 0xb1, 0x75, 0xc1, 0x40, 0x3e, + 0xb3, 0xe3, 0xce, 0xb8, 0xfd, 0x51, 0x70, 0x9d, 0x08, 0xdb, 0x1c, 0x7e, 0xa8, 0xd5, 0x61, 0xec, 0xa7, 0x81, 0xeb, + 0x16, 0xac, 0x6e, 0x95, 0x9e, 0xa1, 0xab, 0x8e, 0xf8, 0x4d, 0x4e, 0x8d, 0x98, 0xb6, 0xe9, 0xae, 0x6e, 0xb7, 0x9b, + 0xea, 0x55, 0xb6, 0xa0, 0x2e, 0x63, 0xf7, 0x5a, 0x55, 0x6b, 0xc6, 0xf2, 0xb0, 0xd0, 0xca, 0xec, 0xf3, 0x9f, 0xc5, + 0xd0, 0x99, 0x68, 0x3a, 0x34, 0x02, 0x25, 0x57, 0x51, 0xc4, 0xd3, 0x87, 0xd5, 0x35, 0xd7, 0x36, 0x99, 0xf8, 0x2b, + 0xa7, 0x8f, 0xaf, 0x1d, 0x37, 0xdf, 0x11, 0x46, 0xbd, 0xe7, 0x8e, 0x1b, 0x70, 0xae, 0x46, 0xbc, 0x1c, 0x3d, 0xf3, + 0x94, 0x57, 0xcb, 0xbb, 0xd2, 0x1c, 0x05, 0xcf, 0xb5, 0x9f, 0x5b, 0x4a, 0x3d, 0x2d, 0x4b, 0x1e, 0xb3, 0x0f, 0xb6, + 0x91, 0xdb, 0x30, 0xd6, 0x9b, 0x74, 0x43, 0xc6, 0x3b, 0x0e, 0xf8, 0x64, 0xa5, 0xa8, 0x2b, 0xfd, 0x9e, 0xaa, 0x49, + 0x0a, 0x1b, 0xcd, 0x6c, 0x37, 0xd4, 0x78, 0x17, 0x30, 0x4d, 0x87, 0xb7, 0x02, 0xc9, 0x81, 0x07, 0xe5, 0xda, 0x12, + 0xa6, 0x78, 0xdc, 0x9c, 0x0a, 0x48, 0x32, 0xac, 0xa6, 0x21, 0x37, 0xbf, 0x2a, 0xa4, 0x21, 0xa1, 0xce, 0xd5, 0x01, + 0x68, 0x95, 0x92, 0x07, 0x38, 0x94, 0x43, 0x01, 0xe6, 0xca, 0xa1, 0x67, 0x68, 0x50, 0x08, 0x46, 0xe8, 0xcd, 0xdb, + 0xe8, 0xf0, 0xd4, 0xe1, 0x43, 0x69, 0x5c, 0xe6, 0x14, 0xc4, 0x2f, 0x1f, 0xfb, 0x48, 0x3d, 0x1a, 0xeb, 0x4e, 0x3e, + 0x51, 0x87, 0xe7, 0x4b, 0xc8, 0xa5, 0x09, 0xdd, 0x27, 0x9c, 0x54, 0x33, 0x21, 0x0b, 0xf9, 0x37, 0x79, 0xaa, 0x46, + 0xb1, 0xa0, 0xf6, 0xea, 0xb9, 0x91, 0xec, 0x8e, 0x3e, 0xcb, 0x51, 0xf8, 0x6a, 0x1c, 0x6e, 0xb5, 0xc2, 0xae, 0x07, + 0x21, 0x2f, 0xbe, 0x70, 0x73, 0xbf, 0xf9, 0x9a, 0x53, 0xd0, 0xfd, 0xa9, 0x03, 0xcf, 0x6d, 0xf1, 0x8a, 0x66, 0x77, + 0x54, 0x07, 0x16, 0xed, 0xfd, 0xfb, 0xa0, 0x1c, 0xb7, 0xf5, 0x59, 0x07, 0xee, 0xfe, 0x91, 0xd8, 0x8d, 0x81, 0xbc, + 0x41, 0x19, 0xef, 0x67, 0x3f, 0xa5, 0x0f, 0x45, 0x42, 0x36, 0xac, 0x31, 0x40, 0x8e, 0x5c, 0x98, 0xf5, 0xb8, 0x31, + 0x67, 0xa7, 0x5d, 0x1e, 0x4a, 0xd0, 0xdd, 0xd6, 0xfe, 0xe3, 0x7a, 0x84, 0xb3, 0xb8, 0x15, 0x60, 0xf2, 0x77, 0x6e, + 0x2c, 0xbb, 0xaa, 0xdb, 0x0b, 0x87, 0x9e, 0x1e, 0xa9, 0xe0, 0xbd, 0xd1, 0x9c, 0x64, 0x5a, 0xb5, 0xbd, 0xda, 0x9f, + 0xfd, 0x92, 0x7f, 0xab, 0x74, 0xbf, 0x6d, 0x09, 0x39, 0x72, 0xb1, 0x82, 0x5d, 0x67, 0x92, 0xc2, 0xf6, 0xd7, 0x2d, + 0x77, 0xcc, 0x69, 0x70, 0xe2, 0x66, 0x4b, 0xe4, 0x3b, 0x7c, 0x1b, 0xc8, 0x26, 0x50, 0x94, 0xfd, 0x38, 0xc0, 0x3e, + 0x8c, 0xa9, 0xb4, 0x49, 0x46, 0x2b, 0x6f, 0xf4, 0xbe, 0x7d, 0x57, 0x28, 0x63, 0xcf, 0x8a, 0x05, 0xb9, 0x8a, 0x84, + 0x1c, 0xb0, 0x1e, 0xcb, 0x54, 0x42, 0x87, 0xc6, 0x73, 0x17, 0xd1, 0x97, 0x45, 0x74, 0xef, 0xe5, 0xbe, 0x4f, 0x62, + 0x9b, 0xd6, 0xdb, 0x29, 0x8f, 0xe4, 0x7f, 0xc4, 0xf8, 0x43, 0x56, 0x05, 0x79, 0x00, 0x1e, 0xef, 0xaf, 0x36, 0x74, + 0x9d, 0xa7, 0x41, 0x99, 0x71, 0x10, 0x45, 0x40, 0xe9, 0x72, 0x53, 0xe4, 0x9c, 0x6e, 0x68, 0x94, 0x4a, 0xd9, 0x16, + 0x83, 0xc0, 0xc8, 0x56, 0x35, 0xea, 0xc5, 0xfc, 0x10, 0x9a, 0x26, 0xa3, 0x3f, 0x9e, 0x49, 0x59, 0x0d, 0xe5, 0xdc, + 0xc5, 0x3a, 0x39, 0xb6, 0x8c, 0x7d, 0x0d, 0xd1, 0x07, 0x87, 0xad, 0xfa, 0x11, 0x33, 0x0f, 0x79, 0xf7, 0x50, 0x80, + 0x81, 0xf9, 0xae, 0x27, 0xdf, 0x94, 0xd2, 0xad, 0xca, 0x52, 0x69, 0x04, 0xa1, 0x0a, 0x6c, 0xb2, 0x37, 0x3c, 0x1a, + 0xe8, 0x89, 0x92, 0x8b, 0x91, 0xc1, 0x14, 0x48, 0x00, 0xd5, 0xb4, 0x0f, 0x7f, 0x4d, 0x2d, 0x94, 0x8c, 0xf4, 0x52, + 0x60, 0x0e, 0xe9, 0xbf, 0x21, 0x21, 0x60, 0x32, 0x00, 0xab, 0x2f, 0xfc, 0x66, 0x12, 0xff, 0x98, 0x0f, 0x7c, 0x04, + 0x9f, 0x30, 0x51, 0x23, 0x52, 0xfe, 0x41, 0x79, 0x9f, 0x8e, 0x9c, 0x29, 0x59, 0x3b, 0x2b, 0x05, 0x0e, 0x15, 0x57, + 0x53, 0x18, 0xc2, 0xd3, 0x83, 0xb0, 0x88, 0xa1, 0x1b, 0xc8, 0x7a, 0xb0, 0xe3, 0x09, 0xd3, 0x88, 0xda, 0x64, 0xaa, + 0x86, 0x92, 0xf6, 0x47, 0xc1, 0xe2, 0xc0, 0x9a, 0x00, 0xe4, 0x58, 0x68, 0x5a, 0x74, 0x19, 0x91, 0x79, 0xb0, 0x14, + 0x8e, 0xc0, 0xa9, 0x09, 0xb9, 0x9e, 0x55, 0xe6, 0x3d, 0x4f, 0x0a, 0x0e, 0xe2, 0x09, 0xf6, 0xce, 0x18, 0xf1, 0x4e, + 0x9e, 0x5d, 0xed, 0x4f, 0xb9, 0xde, 0x05, 0x2f, 0xb9, 0x8c, 0x20, 0x97, 0x39, 0x7e, 0x31, 0x98, 0x86, 0xfb, 0x07, + 0x30, 0x17, 0x19, 0x82, 0x7c, 0xe8, 0x50, 0x82, 0x3b, 0x2c, 0x46, 0x9b, 0xd5, 0xc0, 0xc3, 0x8d, 0x22, 0x4b, 0x26, + 0x83, 0x80, 0x08, 0x4c, 0xab, 0x7c, 0x47, 0x05, 0x70, 0x15, 0x17, 0xda, 0x98, 0xa2, 0xb8, 0x5e, 0x51, 0xed, 0x38, + 0xa3, 0xbd, 0x64, 0x33, 0xf3, 0x71, 0x9a, 0x96, 0x36, 0xd4, 0x6a, 0xe2, 0xd4, 0x91, 0x14, 0xcd, 0xd0, 0x79, 0x73, + 0x91, 0x8a, 0x64, 0xa6, 0x0f, 0xe6, 0x0f, 0x1d, 0x09, 0x6c, 0x94, 0x56, 0x30, 0xc8, 0xf9, 0x1a, 0x3b, 0x73, 0x97, + 0xb6, 0xbe, 0xce, 0xda, 0x30, 0xe7, 0xd3, 0x55, 0x3f, 0x4d, 0x09, 0x54, 0x3b, 0x4d, 0xfd, 0xd9, 0x8a, 0xd8, 0x2f, + 0xd2, 0x2e, 0xcb, 0x42, 0x93, 0xe5, 0xde, 0x8f, 0x1f, 0xee, 0xe3, 0x61, 0xa1, 0xba, 0x0b, 0x73, 0x29, 0x47, 0x38, + 0xb2, 0x58, 0x8b, 0xd5, 0x31, 0xfb, 0x19, 0x25, 0x1b, 0xcb, 0x7d, 0x0f, 0x4a, 0xb2, 0xe3, 0xe5, 0xa5, 0x34, 0x97, + 0x7a, 0xf1, 0x5d, 0x0c, 0x96, 0x03, 0xfc, 0x59, 0xa1, 0x9a, 0xe8, 0x5d, 0x59, 0xad, 0xf4, 0x9f, 0x75, 0xc9, 0x45, + 0x5d, 0x39, 0xe3, 0xda, 0x93, 0x21, 0x4c, 0x13, 0x9a, 0xef, 0x18, 0x62, 0x53, 0xc5, 0x44, 0x49, 0x34, 0xd2, 0x36, + 0x70, 0xbc, 0x7f, 0x5e, 0x9f, 0x45, 0x2a, 0x72, 0xd9, 0x2f, 0xd7, 0x71, 0xc7, 0x2f, 0x40, 0x15, 0xc0, 0x0d, 0x42, + 0x0f, 0x72, 0x02, 0xc3, 0xd8, 0x39, 0x3d, 0xd2, 0x88, 0xc2, 0x29, 0xe9, 0x4e, 0x59, 0x5a, 0x87, 0x37, 0x34, 0xde, + 0xa5, 0x07, 0x51, 0x1a, 0x15, 0xf1, 0x53, 0xd2, 0x1b, 0x9b, 0xd1, 0xa9, 0xae, 0xd1, 0x6f, 0x9a, 0x8b, 0x18, 0x1b, + 0x58, 0x50, 0xef, 0xff, 0x74, 0x00, 0x4a, 0x4c, 0xe6, 0x2d, 0x63, 0x8e, 0x89, 0x90, 0x32, 0xb7, 0x92, 0xef, 0x93, + 0x88, 0xca, 0x3c, 0x66, 0x38, 0xe3, 0x17, 0x19, 0x23, 0xea, 0x66, 0x71, 0x7c, 0x6a, 0xdd, 0x82, 0x49, 0x37, 0xf3, + 0xae, 0xcc, 0x40, 0x1a, 0x44, 0x9e, 0x6a, 0xe9, 0x29, 0xa8, 0x9e, 0x2e, 0xab, 0xae, 0x5e, 0x29, 0xf2, 0xf9, 0x1f, + 0x0c, 0xc3, 0xe1, 0x00, 0xe0, 0xc0, 0xea, 0x73, 0xae, 0xf6, 0xda, 0x9f, 0xad, 0x69, 0xeb, 0x80, 0xd3, 0x13, 0x92, + 0xa7, 0x3f, 0x04, 0xe8, 0x1a, 0xcc, 0x32, 0x54, 0xe7, 0x3c, 0x54, 0xfd, 0xed, 0xa2, 0x2d, 0x0e, 0xc7, 0x0c, 0x04, + 0xda, 0x9b, 0x7b, 0xdc, 0xe2, 0xf7, 0x2c, 0x91, 0xce, 0xc3, 0x04, 0x5b, 0x34, 0xea, 0xf6, 0x48, 0x4e, 0xec, 0x56, + 0x0f, 0x96, 0x3b, 0x0e, 0x07, 0x86, 0x9e, 0xed, 0x22, 0x2a, 0x0d, 0x12, 0xec, 0x7e, 0x2e, 0x51, 0x01, 0x91, 0x0e, + 0xba, 0xc3, 0xe4, 0xfb, 0x1e, 0x4b, 0x6b, 0xea, 0xcf, 0xdd, 0x68, 0xe0, 0x0a, 0x76, 0xb8, 0xc2, 0x4a, 0x5d, 0x6e, + 0xdc, 0x4d, 0x87, 0xb3, 0xce, 0xb1, 0x50, 0xb9, 0x1d, 0x1d, 0x7c, 0xbc, 0xde, 0x58, 0x7b, 0xe7, 0x88, 0x1c, 0xa0, + 0xb2, 0x71, 0x18, 0x70, 0xa9, 0x86, 0x9a, 0xa9, 0x0c, 0x81, 0xd6, 0x8d, 0x61, 0x96, 0xc3, 0x29, 0xfa, 0x3e, 0x75, + 0xec, 0x99, 0x62, 0x23, 0x95, 0x4b, 0xfb, 0xd0, 0x34, 0x35, 0x07, 0x9d, 0xbc, 0x3b, 0x05, 0x42, 0x7f, 0xc5, 0xe0, + 0xe1, 0x81, 0x6c, 0xff, 0xf1, 0xdc, 0xff, 0x7a, 0x73, 0x2e, 0xb6, 0x97, 0x39, 0x70, 0x08, 0x93, 0x7d, 0xd4, 0x12, + 0x0a, 0xa0, 0x48, 0xe6, 0xa6, 0x7a, 0x90, 0xab, 0x77, 0x03, 0xfa, 0x84, 0x8b, 0xb1, 0x49, 0xda, 0xa7, 0xc7, 0x1b, + 0x8c, 0x7c, 0x9e, 0x36, 0xbd, 0x76, 0x21, 0x55, 0xbe, 0xd8, 0x9b, 0xed, 0x17, 0x27, 0x9b, 0xe0, 0x24, 0x27, 0xca, + 0x8e, 0x6d, 0x16, 0xc3, 0x3b, 0xed, 0xd2, 0xbf, 0x6f, 0x5b, 0xb9, 0x81, 0x4b, 0x38, 0xb4, 0x43, 0x15, 0xcc, 0x7b, + 0x70, 0xe8, 0xf5, 0x83, 0x0d, 0x8e, 0xea, 0x41, 0x77, 0x60, 0xfa, 0xb4, 0xd6, 0x09, 0x06, 0x84, 0xef, 0x56, 0x30, + 0x0a, 0x01, 0xc7, 0x5b, 0xd7, 0x2e, 0x94, 0x7b, 0x3e, 0xe0, 0x07, 0x41, 0x85, 0x33, 0x43, 0x68, 0x7e, 0x10, 0x39, + 0xa5, 0x3d, 0xa5, 0xa4, 0xba, 0xba, 0x35, 0x0e, 0x37, 0xe0, 0xb3, 0x81, 0xe2, 0x68, 0xbb, 0xf1, 0x4e, 0x12, 0x87, + 0xf9, 0x28, 0x65, 0xe6, 0xd2, 0xfb, 0xce, 0x09, 0x30, 0xf1, 0x4e, 0xed, 0xf4, 0x09, 0x9e, 0xe8, 0x5b, 0x1f, 0x55, + 0xb5, 0x7d, 0xb1, 0xe7, 0x9b, 0xab, 0xee, 0x90, 0x43, 0x94, 0x10, 0x62, 0xf6, 0xa9, 0x73, 0xcc, 0x73, 0x3e, 0x4b, + 0x07, 0x13, 0xf6, 0xdc, 0x16, 0x40, 0xab, 0x46, 0x05, 0xba, 0x72, 0x40, 0x5e, 0xc2, 0x57, 0xb7, 0x4e, 0xe8, 0xd2, + 0x41, 0x7a, 0x2b, 0xbf, 0x5c, 0x35, 0x89, 0x40, 0xf7, 0xc2, 0x7b, 0x8f, 0xe6, 0x4e, 0x74, 0x9c, 0x89, 0x3b, 0xb8, + 0xe8, 0x27, 0xce, 0x69, 0x7c, 0x24, 0xee, 0x12, 0xf9, 0x2c, 0xa6, 0x01, 0x31, 0x4f, 0x84, 0xf8, 0xab, 0x9f, 0xb9, + 0x84, 0x8d, 0x0a, 0x66, 0xea, 0x6e, 0x91, 0xd3, 0xca, 0x16, 0x13, 0x28, 0xdc, 0x5f, 0x74, 0xc3, 0xad, 0x59, 0xbe, + 0x13, 0x0b, 0x30, 0x2d, 0x03, 0x5f, 0xda, 0x39, 0x20, 0x45, 0x44, 0x7a, 0x4f, 0xde, 0xce, 0xff, 0x9b, 0xda, 0x7b, + 0xc5, 0x7f, 0xb4, 0xb9, 0x44, 0x71, 0x3a, 0x6d, 0x0a, 0x4b, 0xe1, 0xdb, 0x3d, 0x02, 0x21, 0x32, 0x46, 0x04, 0x9a, + 0x31, 0x7f, 0xd0, 0x0e, 0x73, 0x0a, 0xbc, 0xc3, 0x01, 0x70, 0x14, 0xb6, 0xd4, 0x0f, 0x36, 0x78, 0x70, 0x8f, 0x77, + 0xbd, 0x94, 0x3a, 0x56, 0x0e, 0x08, 0xcb, 0x1d, 0x85, 0xe3, 0x20, 0x83, 0x40, 0xd5, 0x21, 0xf6, 0x0e, 0xca, 0x3a, + 0x1d, 0xdd, 0x3a, 0x0c, 0xa9, 0xd0, 0x3b, 0xdf, 0x2a, 0x32, 0x1f, 0xb3, 0x5a, 0xe3, 0xa0, 0xfa, 0x00, 0x4e, 0xc0, + 0x6a, 0x46, 0x1c, 0x3d, 0xcd, 0xcb, 0x3d, 0xcd, 0xbc, 0x80, 0x00, 0x67, 0xf8, 0x83, 0x1d, 0xce, 0xd9, 0x3b, 0xef, + 0x81, 0xd2, 0x0d, 0x80, 0xda, 0xc4, 0x69, 0x59, 0xb8, 0x15, 0xbf, 0x5a, 0x7d, 0x23, 0x79, 0x7b, 0x6e, 0x9f, 0x8e, + 0x78, 0x0f, 0x0f, 0x5e, 0x2a, 0x5a, 0xc8, 0x60, 0x65, 0x82, 0xad, 0x06, 0xc2, 0xca, 0x10, 0x0b, 0xfa, 0x68, 0x5e, + 0xab, 0xee, 0x6a, 0x84, 0xea, 0xff, 0xea, 0x29, 0x98, 0xb2, 0x05, 0xaf, 0x38, 0xa9, 0xe9, 0x86, 0x93, 0xb4, 0xd4, + 0x8a, 0xa3, 0xf9, 0xb1, 0x13, 0x06, 0x05, 0xb1, 0x1d, 0x22, 0xfe, 0xf4, 0x7f, 0x96, 0x28, 0x4b, 0xb8, 0xd5, 0x1c, + 0x51, 0xb6, 0xf4, 0x8e, 0x23, 0xe2, 0xdf, 0x8f, 0x78, 0x57, 0x07, 0x11, 0xaa, 0xc6, 0x7c, 0x52, 0x64, 0xfe, 0x2b, + 0xce, 0xf2, 0x46, 0xb8, 0xdb, 0xcc, 0xee, 0xeb, 0x9d, 0x2f, 0xe4, 0x22, 0x39, 0x3f, 0xcc, 0x2d, 0xdb, 0xce, 0xcb, + 0x4b, 0x3b, 0x55, 0xd2, 0xd6, 0xf3, 0xd3, 0xf2, 0x43, 0x8c, 0x23, 0x22, 0x2d, 0xcb, 0x30, 0xba, 0xf3, 0xec, 0x1c, + 0xbe, 0xff, 0x4e, 0xb9, 0xfa, 0xfe, 0xb3, 0x75, 0xc5, 0xb1, 0x35, 0x2e, 0xdf, 0xf1, 0x50, 0xea, 0xf8, 0xcc, 0x30, + 0xd4, 0xda, 0x40, 0x30, 0xb1, 0x95, 0x6d, 0x18, 0x03, 0x40, 0xef, 0x47, 0xb6, 0x18, 0xfa, 0x0b, 0xce, 0xa5, 0xd5, + 0xcd, 0x0b, 0xb9, 0x64, 0x7e, 0xe0, 0x1e, 0xe3, 0x03, 0xef, 0xfa, 0x83, 0xeb, 0x11, 0x3b, 0x91, 0x01, 0x0c, 0xa9, + 0x18, 0x5c, 0xe4, 0x3d, 0xe6, 0xf3, 0xae, 0x14, 0x21, 0xe4, 0x21, 0x4b, 0x01, 0xae, 0x5d, 0xfd, 0xb9, 0x2c, 0xcf, + 0xbc, 0x9f, 0xcf, 0xdb, 0x1c, 0x70, 0x58, 0xa8, 0xbe, 0x2c, 0x60, 0x1c, 0xfe, 0xa1, 0x18, 0x33, 0x81, 0x59, 0x1b, + 0x5e, 0x3f, 0xeb, 0x39, 0x47, 0x53, 0x33, 0x6a, 0x3b, 0xa5, 0x9e, 0xe5, 0xcb, 0xaa, 0xcd, 0x16, 0xcb, 0x90, 0x1b, + 0x1a, 0x1f, 0x27, 0x0d, 0x12, 0xe3, 0xaf, 0x09, 0xb4, 0x5c, 0x24, 0x6d, 0x44, 0x62, 0xd5, 0x8a, 0x56, 0x14, 0x2b, + 0xa3, 0x58, 0x88, 0x95, 0xfc, 0x56, 0xc9, 0xd3, 0x88, 0x39, 0xe5, 0xf1, 0xac, 0xdf, 0x95, 0xa3, 0x11, 0x50, 0xe1, + 0x60, 0x95, 0x4f, 0x7b, 0x6c, 0xed, 0x77, 0xf6, 0x3b, 0x28, 0xa5, 0xc4, 0x4e, 0x20, 0xd8, 0x27, 0x53, 0x1c, 0x00, + 0x2b, 0x9b, 0x23, 0xfb, 0x1b, 0x4e, 0xbf, 0x7a, 0xeb, 0x08, 0x68, 0x5d, 0x76, 0x4e, 0x75, 0xb4, 0x43, 0xef, 0x0b, + 0x32, 0x8d, 0x63, 0x41, 0x7e, 0x5a, 0x89, 0xcd, 0xe0, 0x31, 0x3a, 0x08, 0xd3, 0x2f, 0xac, 0x7d, 0xd5, 0xc8, 0x36, + 0x58, 0x62, 0xb6, 0xec, 0x58, 0xec, 0x5a, 0x27, 0x56, 0xce, 0xa8, 0x77, 0xef, 0xf9, 0x02, 0x9f, 0x54, 0x5b, 0xc9, + 0x0a, 0x38, 0x33, 0x81, 0x75, 0x14, 0x80, 0x6b, 0xec, 0x43, 0xea, 0x03, 0x8a, 0x83, 0xfa, 0x8a, 0xe3, 0xb3, 0x04, + 0x8b, 0x12, 0x42, 0x60, 0x9f, 0x74, 0xeb, 0x86, 0x4a, 0x38, 0x39, 0x4b, 0xda, 0x8f, 0xf0, 0x14, 0xc6, 0x0d, 0x2a, + 0x05, 0x9b, 0x8b, 0xf1, 0x45, 0xc5, 0x32, 0x85, 0xb3, 0x18, 0xd3, 0x61, 0xff, 0xb4, 0x4a, 0x58, 0x46, 0x1f, 0x1f, + 0x16, 0x16, 0x6e, 0xa6, 0x10, 0x4b, 0x4a, 0xfa, 0xfe, 0x80, 0xcd, 0xd7, 0x52, 0xff, 0xbf, 0xe6, 0x0a, 0x2a, 0xd8, + 0x8a, 0xb9, 0xe3, 0xf0, 0x77, 0xa8, 0xe0, 0xed, 0x2d, 0xf3, 0xa4, 0x6a, 0x6b, 0xeb, 0xbe, 0xa4, 0x16, 0x08, 0x2f, + 0x79, 0xfa, 0x89, 0xc3, 0x1d, 0xde, 0x8d, 0x33, 0x26, 0xc3, 0xab, 0x7b, 0x80, 0x24, 0x21, 0x20, 0xa1, 0x75, 0x5f, + 0x77, 0x0f, 0x06, 0x77, 0xb4, 0xc6, 0xf7, 0x20, 0xf1, 0x9e, 0x6f, 0xc6, 0xdb, 0x84, 0x5f, 0xa6, 0x7f, 0x69, 0x55, + 0xc7, 0x6a, 0xec, 0x83, 0x2a, 0xc6, 0xf5, 0x0f, 0xcf, 0xfd, 0xe9, 0x01, 0xb3, 0xcf, 0xfd, 0xcc, 0x26, 0x9a, 0x44, + 0x9f, 0xde, 0x5f, 0x4a, 0x5c, 0x78, 0xab, 0xf1, 0x96, 0xbf, 0xb4, 0xe2, 0xc2, 0xf9, 0x4a, 0xb7, 0xdb, 0x85, 0xa3, + 0xc3, 0x46, 0xe2, 0x69, 0xa3, 0x26, 0x80, 0x4c, 0xdf, 0x8a, 0xa9, 0xa4, 0x0b, 0x2b, 0xc8, 0xb4, 0x4f, 0xd2, 0x8d, + 0xc6, 0x53, 0x90, 0x4a, 0xb3, 0x58, 0x3d, 0x99, 0x19, 0xda, 0x68, 0x3d, 0x1c, 0x67, 0xd0, 0xff, 0x92, 0x18, 0xca, + 0x7a, 0xd9, 0xb6, 0x30, 0x5b, 0xa6, 0xba, 0xae, 0x3f, 0x6e, 0xa4, 0x95, 0xcc, 0xaa, 0x57, 0xd0, 0xf1, 0xf5, 0xfe, + 0x6d, 0x05, 0x4f, 0x24, 0x8a, 0x0f, 0x7b, 0xb7, 0x90, 0x68, 0x2d, 0x51, 0x2c, 0xe2, 0xfd, 0x32, 0x1d, 0xc7, 0x00, + 0xfc, 0xc1, 0xd0, 0x2d, 0x1d, 0xa7, 0xe9, 0xb1, 0xb2, 0x77, 0x68, 0x1f, 0x4a, 0x8a, 0x62, 0xb9, 0x48, 0xf8, 0x98, + 0x2d, 0xe0, 0xb8, 0x58, 0xa8, 0x86, 0xf6, 0x08, 0x16, 0x6d, 0x89, 0x2d, 0x7a, 0x4a, 0x8f, 0xa3, 0x1c, 0x23, 0xa6, + 0x51, 0xca, 0x97, 0xd1, 0xe3, 0x69, 0x4a, 0x00, 0x6d, 0x36, 0x64, 0x37, 0xef, 0x49, 0x12, 0xd6, 0xe4, 0x36, 0xb9, + 0x00, 0xb6, 0x7f, 0xe6, 0x54, 0xca, 0x5d, 0x1c, 0x44, 0x29, 0xed, 0xdc, 0xfc, 0x6e, 0x0e, 0xbc, 0x96, 0xeb, 0x22, + 0x31, 0xc6, 0xc8, 0x93, 0x2b, 0xa1, 0xa8, 0x65, 0xda, 0xf2, 0xae, 0x39, 0x12, 0xfc, 0xc2, 0x6b, 0xed, 0xad, 0x04, + 0x32, 0xd6, 0x65, 0xf8, 0x66, 0x74, 0x13, 0xb4, 0xf5, 0x9f, 0xec, 0x4d, 0xd7, 0x1b, 0xc4, 0xf2, 0xf3, 0xab, 0xba, + 0xea, 0xc8, 0xb3, 0xff, 0x50, 0xfb, 0x4e, 0x08, 0x2a, 0xe7, 0x26, 0x0c, 0xeb, 0x49, 0x7c, 0x2e, 0x3a, 0xde, 0x9d, + 0x48, 0x92, 0x16, 0xba, 0x3e, 0x93, 0x8e, 0x06, 0x0d, 0x93, 0x54, 0x50, 0x2e, 0x96, 0x81, 0xdf, 0x66, 0x29, 0xd1, + 0x8c, 0x88, 0x74, 0xaa, 0x56, 0x9f, 0x4c, 0x5f, 0xd4, 0x40, 0x5c, 0xaf, 0x02, 0x2b, 0x89, 0xfa, 0x4a, 0xff, 0x6d, + 0x0e, 0x35, 0x95, 0x1c, 0x3c, 0xf6, 0x7b, 0x03, 0xa3, 0x68, 0x52, 0x3d, 0xa9, 0xbb, 0x54, 0x38, 0xed, 0x04, 0x95, + 0x72, 0xe5, 0x29, 0x35, 0xe0, 0xa9, 0x5d, 0x1c, 0xf9, 0x99, 0x1f, 0x4c, 0x77, 0xc9, 0x9f, 0xb8, 0x17, 0xbf, 0xb0, + 0x0d, 0xa9, 0xfa, 0xcb, 0x1f, 0x6a, 0x03, 0xb2, 0x39, 0x09, 0xf5, 0xde, 0x8f, 0x43, 0x46, 0x35, 0xf7, 0x5b, 0xc7, + 0xe6, 0xf2, 0xa7, 0xdf, 0xde, 0xbd, 0xb9, 0xf0, 0x1b, 0xb4, 0x06, 0x65, 0xdd, 0xed, 0xaf, 0x6b, 0x78, 0x4a, 0xc5, + 0xbb, 0x2d, 0xc3, 0xcd, 0x92, 0xd1, 0x03, 0x90, 0x0f, 0xea, 0x9d, 0xff, 0xcc, 0xb5, 0x35, 0x4f, 0x75, 0x43, 0x73, + 0xb5, 0x08, 0x95, 0x33, 0x7f, 0x63, 0x18, 0xa9, 0x55, 0x4f, 0xf7, 0x64, 0x79, 0x63, 0x46, 0x03, 0xf7, 0x80, 0xa1, + 0x32, 0xc0, 0x8d, 0x96, 0x2e, 0x86, 0xd2, 0x5b, 0xd1, 0x97, 0xb6, 0xc5, 0xa6, 0x74, 0x5f, 0x6c, 0x4b, 0xeb, 0x62, + 0xb7, 0x71, 0x05, 0xdb, 0x92, 0xfe, 0x71, 0xfd, 0x1b, 0x7a, 0xa6, 0xd0, 0x63, 0xec, 0x2c, 0x3e, 0xe3, 0x65, 0xac, + 0xf5, 0x8d, 0x14, 0x59, 0xc1, 0x5b, 0xe3, 0x22, 0xd6, 0xc6, 0x0f, 0x25, 0x7b, 0x85, 0x71, 0x5f, 0x16, 0x58, 0x92, + 0x35, 0x1d, 0x0c, 0x8c, 0x54, 0x95, 0x56, 0xd2, 0x6d, 0x69, 0xf6, 0xdf, 0x2d, 0x5d, 0xc5, 0xc6, 0x42, 0xf3, 0x4b, + 0xaf, 0x74, 0x7a, 0x43, 0x62, 0x4d, 0xf6, 0xf3, 0x83, 0x0e, 0xc0, 0x8a, 0xd6, 0x45, 0x55, 0x91, 0x61, 0xbe, 0x56, + 0x91, 0x66, 0xd8, 0x13, 0x5c, 0x09, 0xd0, 0x40, 0xf5, 0x99, 0xa3, 0xf6, 0x21, 0x8e, 0x24, 0x56, 0xa3, 0x53, 0x0d, + 0xd9, 0x17, 0x45, 0x6c, 0x55, 0xbb, 0xa5, 0x46, 0xa9, 0x1a, 0x5d, 0xa2, 0x82, 0xca, 0x6f, 0x47, 0x8f, 0x88, 0x68, + 0x39, 0x6b, 0xf4, 0x21, 0x3e, 0x1f, 0x4d, 0xaf, 0x2b, 0x4e, 0x69, 0x80, 0x34, 0x48, 0x21, 0xb1, 0xa8, 0x74, 0xc1, + 0xcf, 0x04, 0xa4, 0xe5, 0x05, 0xfa, 0xd1, 0x55, 0x0c, 0x93, 0x33, 0x3c, 0x30, 0xf9, 0xad, 0xa3, 0x44, 0x7e, 0xb2, + 0xda, 0xe1, 0x37, 0xfc, 0xa5, 0xc5, 0x75, 0xa1, 0xf1, 0x96, 0x2b, 0xbf, 0x54, 0xcd, 0x55, 0x4c, 0xa1, 0x4b, 0x9f, + 0xc9, 0x6a, 0x86, 0x0c, 0xa6, 0xae, 0x62, 0x28, 0x01, 0x81, 0x6f, 0x40, 0x4a, 0x95, 0x41, 0x87, 0x10, 0x42, 0x6f, + 0xd0, 0x2a, 0x44, 0x74, 0x19, 0x36, 0x9f, 0x90, 0xaa, 0xb9, 0xce, 0x65, 0xf5, 0x68, 0xfe, 0x20, 0x26, 0xc1, 0x34, + 0xfc, 0x41, 0x23, 0x69, 0xb4, 0x07, 0x89, 0xc3, 0xa4, 0xd7, 0x21, 0xf4, 0x83, 0x37, 0xbd, 0x23, 0x0c, 0xdf, 0xfa, + 0xa4, 0xd3, 0xe3, 0x56, 0x92, 0xcb, 0xbf, 0x86, 0x95, 0x67, 0xa6, 0xd7, 0x26, 0xfc, 0x11, 0xfe, 0xa9, 0x0f, 0x66, + 0x75, 0x7b, 0x7b, 0x34, 0xc3, 0x6e, 0x68, 0x0c, 0x7f, 0x77, 0xc0, 0x5b, 0xf8, 0xc1, 0xf4, 0x35, 0x27, 0x76, 0x47, + 0xaf, 0x59, 0xb8, 0xce, 0xe7, 0xd1, 0xf8, 0x59, 0x9a, 0x17, 0xac, 0x3c, 0x79, 0x70, 0xdf, 0xfa, 0xde, 0x67, 0x12, + 0x19, 0x2f, 0x3f, 0x75, 0x79, 0xad, 0x2d, 0x27, 0x83, 0xf2, 0xc2, 0x52, 0xf7, 0xc3, 0x1e, 0xa7, 0x2f, 0xb5, 0xf6, + 0x97, 0x7a, 0xed, 0xb3, 0xcf, 0xa6, 0x26, 0x8f, 0x31, 0x3c, 0x1d, 0x4d, 0xdd, 0xd3, 0xc2, 0xfa, 0x16, 0x59, 0x21, + 0x36, 0xc7, 0xb7, 0xcb, 0xd1, 0xd3, 0x59, 0x6d, 0x2f, 0xae, 0xd0, 0xb4, 0x93, 0xd3, 0xa9, 0x13, 0x37, 0xaf, 0xa3, + 0x58, 0xd2, 0xb4, 0x8f, 0xef, 0xc7, 0x72, 0x87, 0xeb, 0x8a, 0x7a, 0x40, 0xd0, 0xa8, 0xa0, 0x17, 0x4c, 0xf5, 0xf8, + 0x74, 0x23, 0x40, 0x5d, 0x78, 0x3a, 0xb1, 0x4e, 0x53, 0xfd, 0x3d, 0xe0, 0x65, 0x60, 0x9a, 0x06, 0x5b, 0x3f, 0x54, + 0x92, 0x20, 0x97, 0x19, 0x6f, 0xfb, 0xf6, 0xfc, 0xf5, 0x3e, 0x5e, 0x58, 0x6a, 0x05, 0xf3, 0x5b, 0x7c, 0x0e, 0x52, + 0xb3, 0x80, 0x3b, 0x2a, 0x59, 0x84, 0x23, 0x88, 0x96, 0x77, 0xc8, 0x53, 0xc7, 0x01, 0xe9, 0xa0, 0x3a, 0x67, 0x24, + 0xe6, 0xf3, 0x5f, 0xed, 0x7b, 0x26, 0xf5, 0x7d, 0x0f, 0x5b, 0xaf, 0xde, 0x1d, 0x48, 0x39, 0xf4, 0x49, 0xf5, 0x19, + 0x68, 0x32, 0xf7, 0xdd, 0x56, 0x3a, 0x7d, 0xa3, 0xcf, 0xd6, 0xb5, 0xbb, 0x50, 0xf3, 0xd3, 0x54, 0xfa, 0xc4, 0x5e, + 0x39, 0x1b, 0xf5, 0x19, 0x94, 0x25, 0x73, 0x01, 0x04, 0x49, 0x8b, 0x40, 0x07, 0x3a, 0x71, 0xb6, 0x29, 0xd3, 0x40, + 0x74, 0x49, 0x6b, 0xce, 0xf8, 0x61, 0x9e, 0x9d, 0xe4, 0xd6, 0x7e, 0xcf, 0x49, 0x5c, 0x85, 0x73, 0xa8, 0xa0, 0xa0, + 0x79, 0x3c, 0xd1, 0x36, 0xf8, 0xaf, 0x17, 0xba, 0xc9, 0x89, 0x7c, 0x1e, 0xe6, 0x9c, 0xb6, 0x8c, 0x31, 0x42, 0x03, + 0x70, 0xd1, 0xf4, 0xea, 0x28, 0x60, 0xb9, 0x0b, 0x84, 0xdf, 0xf2, 0x79, 0xb7, 0xdd, 0xb6, 0xaa, 0x05, 0xa9, 0x76, + 0x62, 0x17, 0xd5, 0xcc, 0x32, 0x45, 0x06, 0xce, 0x00, 0x4f, 0xb6, 0x6f, 0x0b, 0xd9, 0xf8, 0xa0, 0xbd, 0xe9, 0xd2, + 0xe9, 0x51, 0x16, 0xf0, 0x83, 0x94, 0x93, 0x16, 0x9e, 0x1d, 0x43, 0xb1, 0x4d, 0x79, 0xb9, 0x2f, 0xf8, 0xd4, 0x35, + 0x86, 0xd4, 0x50, 0xda, 0x6c, 0x19, 0x29, 0xbc, 0x9b, 0x37, 0x06, 0x5c, 0xd2, 0xe2, 0xbd, 0x88, 0x31, 0xe0, 0xe1, + 0xfa, 0xa2, 0x45, 0x88, 0x27, 0x08, 0x73, 0xb8, 0x61, 0x86, 0x01, 0x74, 0x22, 0xe0, 0x60, 0x3a, 0xbd, 0xbd, 0x0e, + 0x7e, 0x4f, 0x56, 0x68, 0x4b, 0xaf, 0xe6, 0x0d, 0xb7, 0xab, 0x51, 0xba, 0xa1, 0x6d, 0x06, 0xb3, 0x7e, 0x3e, 0xf9, + 0x0d, 0xe5, 0xaa, 0xb3, 0x9a, 0xbf, 0x5c, 0xb0, 0x68, 0x75, 0x36, 0x73, 0x27, 0x9d, 0x1a, 0xdd, 0x53, 0xd5, 0x7a, + 0xea, 0x41, 0xb3, 0x37, 0xf4, 0x16, 0xd4, 0x14, 0x9a, 0x25, 0xc6, 0x1a, 0x3b, 0x1f, 0xfe, 0x47, 0xb6, 0xf0, 0x35, + 0x6b, 0x0f, 0xb4, 0xb6, 0x72, 0x7f, 0x6d, 0xc7, 0xd7, 0x08, 0x0e, 0xc3, 0x28, 0xc4, 0x09, 0xea, 0xd6, 0x5a, 0x52, + 0xe8, 0x56, 0xa7, 0x43, 0x54, 0x10, 0x93, 0xff, 0xa5, 0x37, 0xf3, 0x2e, 0x3e, 0x75, 0x0c, 0x9d, 0xab, 0x7f, 0x55, + 0x5c, 0x1d, 0x9b, 0xa6, 0xd9, 0xea, 0x5d, 0x3f, 0x17, 0x3e, 0xcc, 0xb4, 0xdf, 0x15, 0x2f, 0x3a, 0x42, 0x81, 0xc7, + 0x0f, 0x1e, 0xf6, 0xf5, 0x95, 0x15, 0xa4, 0x53, 0xcf, 0x27, 0xcc, 0x47, 0x4f, 0xd1, 0x31, 0x70, 0x43, 0x16, 0x13, + 0xef, 0xe3, 0x3a, 0x8b, 0xff, 0x59, 0xf6, 0xe1, 0x4c, 0xdb, 0x69, 0x54, 0x57, 0x8a, 0xc7, 0xb5, 0x08, 0xe8, 0xf3, + 0xe9, 0xe3, 0x12, 0x03, 0xd4, 0x5e, 0xac, 0x8a, 0x63, 0xb3, 0x41, 0x37, 0xbc, 0x2f, 0x84, 0xac, 0x57, 0x3a, 0xe3, + 0x3e, 0x2d, 0x12, 0x40, 0x5c, 0x7f, 0x44, 0x5d, 0x8b, 0xf9, 0xfa, 0xf2, 0xcd, 0xd1, 0xa6, 0xc7, 0x8c, 0x86, 0xc0, + 0x84, 0x59, 0xfb, 0x93, 0x51, 0x4a, 0xa7, 0x4f, 0xd1, 0x8a, 0xcf, 0x4d, 0xe1, 0x99, 0x6b, 0x75, 0x6d, 0x14, 0xe9, + 0x3f, 0x8a, 0xba, 0xf7, 0x31, 0x9c, 0x35, 0xaf, 0xbf, 0x60, 0x37, 0x07, 0xa3, 0x1f, 0x06, 0xcd, 0x41, 0x89, 0x45, + 0xbc, 0x7a, 0x12, 0x1f, 0x73, 0xbc, 0x26, 0x01, 0x3e, 0xe7, 0x39, 0x40, 0xff, 0x1c, 0x53, 0xcc, 0x25, 0x8c, 0xe3, + 0x63, 0x07, 0x54, 0x5b, 0x5b, 0x39, 0x24, 0xff, 0x66, 0xf6, 0x02, 0xb5, 0x59, 0xd7, 0x32, 0xa8, 0xbf, 0x83, 0xbc, + 0xda, 0xf4, 0xc2, 0xca, 0x41, 0xe7, 0x2b, 0x4b, 0xfa, 0xda, 0x04, 0xdd, 0xe2, 0xb2, 0xec, 0x80, 0xcf, 0xbc, 0xde, + 0x5d, 0x11, 0xbf, 0x14, 0xcc, 0x5b, 0xf8, 0x72, 0x1b, 0x9a, 0x70, 0x77, 0xe9, 0xa7, 0xc1, 0x09, 0xcd, 0x91, 0xdf, + 0x26, 0xa3, 0x0f, 0xdf, 0x7a, 0x76, 0xf5, 0xb2, 0x0e, 0xfc, 0x7f, 0xc3, 0x20, 0x10, 0x79, 0xa7, 0xd0, 0x2d, 0x69, + 0x9d, 0x7a, 0x14, 0x4b, 0x57, 0xca, 0x3e, 0xae, 0x5c, 0x7d, 0x74, 0x9b, 0xff, 0x1f, 0xae, 0xe0, 0x5b, 0xa3, 0xf8, + 0x49, 0x0c, 0xd0, 0x81, 0x22, 0x24, 0x3d, 0x22, 0xba, 0x78, 0xd6, 0xe2, 0xf1, 0x5b, 0x50, 0x33, 0xd8, 0xfa, 0x16, + 0xec, 0x04, 0x83, 0x90, 0x3d, 0x62, 0x9d, 0x0d, 0x1d, 0xb8, 0xfc, 0xad, 0x17, 0x65, 0x0e, 0x91, 0xde, 0x7c, 0x57, + 0x38, 0x75, 0x6d, 0xe5, 0x7d, 0xff, 0x97, 0xfa, 0xda, 0x64, 0x9e, 0xf3, 0xeb, 0x54, 0xf2, 0x85, 0xd3, 0x45, 0x57, + 0x21, 0xc6, 0xf1, 0xbb, 0x2b, 0x36, 0xde, 0x19, 0xf7, 0xc5, 0x45, 0xe4, 0xb4, 0xba, 0xf6, 0xd6, 0x4d, 0x0f, 0xba, + 0x71, 0x45, 0xf4, 0x18, 0xbf, 0xc4, 0x4c, 0xf7, 0xe6, 0x87, 0xc4, 0x3a, 0x7e, 0x37, 0xae, 0xf4, 0x5c, 0x4c, 0xe1, + 0x3e, 0x24, 0xf0, 0x3d, 0x7a, 0xb5, 0x42, 0x5c, 0x66, 0xdd, 0xf0, 0x82, 0x08, 0x50, 0x24, 0x00, 0x2b, 0x25, 0x09, + 0xa2, 0x25, 0x81, 0xe5, 0x70, 0xf2, 0xde, 0x56, 0x78, 0x6d, 0x7a, 0x77, 0x88, 0x16, 0x35, 0x2e, 0x54, 0x0c, 0x8f, + 0xbb, 0xa7, 0x93, 0xb9, 0x15, 0xa8, 0x57, 0xec, 0x41, 0x4c, 0x00, 0xa6, 0x05, 0x30, 0x56, 0x84, 0xcf, 0x6b, 0x44, + 0x1c, 0x00, 0x8a, 0x04, 0x0e, 0x30, 0xe2, 0x00, 0xfe, 0xbb, 0x9f, 0xf1, 0xa3, 0xf9, 0x85, 0x60, 0x59, 0xf4, 0x27, + 0xd3, 0x4f, 0xfb, 0x0c, 0x47, 0xe4, 0xf2, 0xe6, 0x21, 0x08, 0xb2, 0xda, 0x1c, 0xec, 0x8a, 0x1f, 0x60, 0x1b, 0xb7, + 0x27, 0xbc, 0xdc, 0x10, 0x5d, 0x3a, 0xab, 0xa4, 0xb4, 0x0b, 0xbe, 0xc0, 0xa5, 0x6f, 0xba, 0xbf, 0xa4, 0x87, 0xd5, + 0xc2, 0x17, 0xe3, 0x9e, 0xd5, 0x30, 0x3f, 0x78, 0xf1, 0xe8, 0xff, 0xac, 0x7a, 0xdd, 0x61, 0x63, 0x1c, 0xfe, 0x31, + 0xe0, 0x87, 0xa0, 0xf9, 0x49, 0xf6, 0xde, 0x47, 0xb7, 0xf6, 0xbd, 0x24, 0x39, 0x99, 0x1e, 0x56, 0x18, 0x4e, 0x3f, + 0x5e, 0x60, 0x55, 0x06, 0x3f, 0x97, 0x25, 0xd5, 0x5d, 0x85, 0x5f, 0x5c, 0x13, 0x61, 0x70, 0x0e, 0xef, 0xf8, 0x02, + 0x40, 0x5a, 0xcc, 0x70, 0x25, 0x5d, 0xeb, 0xf5, 0x77, 0x2f, 0xf8, 0xd6, 0x69, 0x92, 0x48, 0x20, 0x72, 0x5a, 0xc9, + 0xe1, 0x6c, 0x08, 0x4a, 0x4e, 0xca, 0xc3, 0x9c, 0x32, 0x38, 0x4b, 0x95, 0xd3, 0xa2, 0xc0, 0x9f, 0xda, 0xd9, 0xdd, + 0xba, 0xbc, 0x58, 0xd1, 0x1a, 0x4b, 0xf5, 0xbe, 0x0c, 0x35, 0x44, 0xb0, 0xd8, 0xf2, 0x69, 0x4b, 0x98, 0xfd, 0x0d, + 0x66, 0x53, 0x83, 0x08, 0xbf, 0xcf, 0x53, 0x42, 0x57, 0xde, 0x44, 0x04, 0x26, 0x54, 0x1f, 0x9a, 0x22, 0x46, 0x7a, + 0x44, 0xa7, 0x45, 0x42, 0x52, 0xab, 0x34, 0x42, 0x63, 0x0d, 0x89, 0x7e, 0xbf, 0x75, 0xcf, 0xab, 0xe5, 0x38, 0x1e, + 0xa3, 0xf2, 0x47, 0xd1, 0x6f, 0x30, 0x23, 0x17, 0xa4, 0xdd, 0xb0, 0x2b, 0x62, 0x98, 0xb2, 0x60, 0x18, 0xa8, 0xb2, + 0x41, 0x49, 0xe0, 0xb6, 0x62, 0xdb, 0xbf, 0xe3, 0xfb, 0x30, 0x22, 0xda, 0x4d, 0xc0, 0xcf, 0x3c, 0xa1, 0x76, 0x63, + 0x01, 0x1d, 0x7a, 0xc0, 0x6f, 0x58, 0xc3, 0x77, 0x4d, 0x14, 0xe9, 0x04, 0x4e, 0xd0, 0xb2, 0x48, 0xe2, 0xd3, 0xbd, + 0xf1, 0xff, 0x2f, 0x85, 0x54, 0x9f, 0xf7, 0xf7, 0xb7, 0x8d, 0x48, 0x0d, 0x3d, 0x15, 0xa8, 0xc8, 0xb8, 0x02, 0x5b, + 0xf6, 0x78, 0x29, 0x72, 0xc0, 0xc4, 0xe4, 0x5f, 0xb1, 0xc1, 0x4a, 0xe7, 0x8d, 0xe3, 0xd3, 0xbf, 0x60, 0x5a, 0x9c, + 0xed, 0x61, 0x16, 0xf3, 0x30, 0xfe, 0x4b, 0x47, 0x0f, 0x7a, 0xac, 0x87, 0x12, 0x2b, 0xe1, 0xc7, 0x65, 0x3e, 0xdc, + 0xf3, 0x8d, 0x59, 0xbe, 0xde, 0x1f, 0x2e, 0xec, 0x59, 0x89, 0xce, 0x8f, 0x7e, 0x89, 0xc5, 0x38, 0x32, 0xfe, 0x1b, + 0x6d, 0xc9, 0xe6, 0x36, 0xe0, 0x4e, 0x32, 0xa7, 0x77, 0x47, 0x47, 0x23, 0x0b, 0x72, 0x86, 0x25, 0xba, 0xbb, 0xe5, + 0x92, 0xdc, 0x65, 0xce, 0x2e, 0xfb, 0xfc, 0xeb, 0x7d, 0x76, 0xe1, 0x45, 0x7b, 0x4d, 0x9a, 0x4f, 0xd2, 0x06, 0x94, + 0x16, 0xb8, 0x3f, 0x9b, 0xdd, 0x22, 0x2a, 0x11, 0x32, 0x84, 0xf8, 0x82, 0x3b, 0x22, 0x05, 0xfb, 0x1d, 0xdb, 0x54, + 0x3c, 0xd0, 0x8d, 0xa8, 0xd7, 0x83, 0x97, 0x76, 0xdd, 0xf6, 0x8d, 0x01, 0x37, 0x4c, 0xd6, 0x2a, 0x46, 0xb5, 0xa0, + 0x59, 0x98, 0xde, 0x4e, 0x3e, 0x48, 0x55, 0x57, 0x12, 0x7a, 0x18, 0x1a, 0xf8, 0x14, 0xfb, 0x5a, 0xd7, 0x19, 0xbd, + 0x0c, 0x88, 0x7e, 0xc6, 0x0e, 0x3d, 0xf6, 0x03, 0xb3, 0xfc, 0x20, 0xe8, 0x62, 0xa9, 0x97, 0x40, 0x04, 0x34, 0x78, + 0x4d, 0x23, 0x56, 0x41, 0x9c, 0xb5, 0xd1, 0x61, 0xab, 0xa6, 0x07, 0x72, 0x8a, 0xbf, 0x58, 0x42, 0x28, 0x11, 0x5f, + 0x4d, 0xd3, 0xd2, 0x56, 0xe6, 0xe8, 0x2f, 0x0f, 0xc2, 0x5a, 0x90, 0x68, 0xea, 0x8c, 0xed, 0xad, 0xd2, 0x71, 0xf3, + 0x96, 0x97, 0x27, 0x24, 0xd0, 0xb6, 0x15, 0x61, 0x9e, 0x7f, 0xf2, 0x9f, 0xa6, 0xd6, 0x75, 0x0d, 0x5e, 0x99, 0x98, + 0xbf, 0x13, 0xb9, 0x95, 0xd3, 0xd1, 0x0f, 0x4d, 0xaa, 0x57, 0x0f, 0xb8, 0xc2, 0x7b, 0x33, 0xfe, 0xf3, 0x80, 0xd4, + 0xee, 0x38, 0x87, 0x33, 0x10, 0xa2, 0x79, 0x4e, 0x80, 0xd2, 0xa0, 0xe3, 0xe6, 0x20, 0x98, 0x95, 0x01, 0xc9, 0xce, + 0xea, 0x56, 0x7a, 0x8d, 0xcb, 0xd6, 0x89, 0x83, 0x74, 0xfb, 0x17, 0x62, 0xf2, 0x2c, 0xa5, 0x2b, 0x98, 0xe5, 0x65, + 0x42, 0x57, 0x2d, 0x06, 0x0a, 0x13, 0x39, 0x22, 0xfb, 0xbf, 0x62, 0x45, 0x1f, 0xac, 0xdf, 0x86, 0x8b, 0xb1, 0x23, + 0x24, 0xfb, 0x69, 0x16, 0xad, 0x91, 0xd2, 0xc8, 0x64, 0xc3, 0xe4, 0x52, 0x20, 0x57, 0x02, 0x09, 0x35, 0xea, 0x38, + 0x94, 0x83, 0x01, 0x9d, 0xda, 0x39, 0x28, 0x21, 0xec, 0x4b, 0x14, 0x50, 0x62, 0x44, 0x2a, 0x14, 0xfb, 0x39, 0x3a, + 0x4b, 0x19, 0x62, 0x66, 0x3a, 0x02, 0xee, 0x53, 0xa3, 0x84, 0x64, 0x32, 0x68, 0x00, 0xbd, 0xa5, 0x1d, 0xd4, 0x0f, + 0x70, 0x58, 0x64, 0xc4, 0xa5, 0x09, 0x80, 0xcf, 0x29, 0x6c, 0x6b, 0xff, 0x1e, 0x94, 0x2f, 0x5b, 0x17, 0x3d, 0xc8, + 0xd4, 0x45, 0x20, 0x74, 0x32, 0x8b, 0x05, 0x2a, 0xc3, 0xe5, 0xf0, 0xfb, 0xd4, 0x61, 0xaf, 0xa9, 0xd3, 0x4e, 0x91, + 0xc4, 0x5d, 0x9a, 0x69, 0xe8, 0xfb, 0x61, 0xdd, 0xdb, 0x34, 0xa9, 0xd8, 0x11, 0x78, 0x6b, 0x19, 0xcf, 0x42, 0xbb, + 0x51, 0x8c, 0x7d, 0x40, 0xde, 0xc8, 0xd0, 0xf9, 0x7f, 0x1b, 0x9b, 0x7e, 0xe0, 0x17, 0x9e, 0xa6, 0xcf, 0x9c, 0xf4, + 0xf3, 0xb0, 0x20, 0xbb, 0xc1, 0x4e, 0x45, 0x61, 0x45, 0x89, 0x2f, 0x50, 0x65, 0x53, 0x0d, 0xdd, 0x6b, 0x51, 0x28, + 0x92, 0x14, 0x72, 0x74, 0x61, 0x3c, 0xb9, 0x3c, 0x49, 0xb6, 0x5a, 0x46, 0xa5, 0x48, 0x12, 0xae, 0x4d, 0x48, 0xd6, + 0x09, 0x25, 0xda, 0xe7, 0xb1, 0xce, 0x48, 0xda, 0x8c, 0x0b, 0x76, 0xd6, 0x82, 0x6b, 0x53, 0xbb, 0xb9, 0x38, 0x65, + 0x1e, 0x6a, 0xfe, 0x44, 0x15, 0xa6, 0xcc, 0x9a, 0xa7, 0xb2, 0x36, 0xb9, 0x6a, 0xc8, 0x34, 0xf2, 0xa1, 0xbe, 0x0f, + 0xa9, 0x5e, 0x1c, 0x4e, 0x44, 0xc9, 0xf5, 0x89, 0x4b, 0x07, 0x00, 0xc4, 0x70, 0x9c, 0xf9, 0x65, 0xc9, 0x41, 0x14, + 0x70, 0xa2, 0x94, 0x29, 0xd9, 0x32, 0xb8, 0x1f, 0xc0, 0xbe, 0xb2, 0x1d, 0x05, 0x4a, 0xe6, 0xd8, 0x71, 0xed, 0x6f, + 0x4a, 0x48, 0x01, 0xfb, 0xa9, 0x92, 0x83, 0x71, 0x1d, 0x86, 0xea, 0x1c, 0xcc, 0x1d, 0x69, 0x17, 0xde, 0x57, 0x0c, + 0x5d, 0x9c, 0x8b, 0x22, 0x12, 0x2a, 0x09, 0x1f, 0x0f, 0x30, 0x9f, 0x17, 0x29, 0xec, 0x63, 0x42, 0xf4, 0x94, 0x43, + 0x54, 0x6a, 0xb5, 0x55, 0x0e, 0xf2, 0x82, 0x69, 0x63, 0x2a, 0xfb, 0x48, 0x1a, 0x40, 0xfc, 0x24, 0x6e, 0x50, 0xaa, + 0x6a, 0x9d, 0x76, 0x2b, 0x9a, 0xdb, 0x1a, 0x39, 0xb8, 0x6a, 0xbf, 0x31, 0xad, 0x2a, 0xb0, 0x13, 0x72, 0x2a, 0xa7, + 0x61, 0xeb, 0x4e, 0xc6, 0xbf, 0xdd, 0xaf, 0xa6, 0xbf, 0xfc, 0xb2, 0x14, 0x95, 0x60, 0x84, 0xcc, 0x64, 0x80, 0x6f, + 0x84, 0x10, 0xbc, 0x68, 0x6f, 0x3d, 0x54, 0xb6, 0x45, 0x1c, 0x47, 0x1d, 0x87, 0x15, 0x24, 0x10, 0xe6, 0x73, 0xb9, + 0x3b, 0x5b, 0x8d, 0x2e, 0xf6, 0xee, 0xa8, 0x7c, 0x95, 0x27, 0x89, 0x55, 0xc1, 0x4e, 0x49, 0xf1, 0x31, 0x00, 0x58, + 0x92, 0x27, 0x82, 0x15, 0xe4, 0x8e, 0x37, 0x0d, 0x7c, 0x98, 0xf0, 0x24, 0xf9, 0xbf, 0xbe, 0x09, 0xfc, 0x4c, 0x71, + 0xc9, 0xf2, 0x6d, 0x79, 0x30, 0x59, 0xce, 0x56, 0x2d, 0x05, 0x44, 0x23, 0x02, 0x13, 0x87, 0x7c, 0x9c, 0x5f, 0x27, + 0xd9, 0xbb, 0x0c, 0xf1, 0xa9, 0xf9, 0xa0, 0x27, 0x34, 0xcf, 0xfc, 0x26, 0x34, 0xf4, 0xb8, 0x52, 0x05, 0x5a, 0x02, + 0x42, 0x13, 0xf7, 0x8f, 0xd7, 0x86, 0x0e, 0xa6, 0x59, 0x7f, 0x41, 0xc0, 0x00, 0xab, 0xbc, 0xad, 0xa0, 0x0a, 0x73, + 0x3b, 0x12, 0xde, 0xd4, 0x0d, 0xe1, 0x2b, 0x67, 0xc6, 0xd1, 0xc9, 0x14, 0x3e, 0x19, 0x10, 0x40, 0x7c, 0x54, 0x6f, + 0x44, 0x43, 0x7c, 0x33, 0xcf, 0xaa, 0x3a, 0xb7, 0xb0, 0x55, 0xec, 0x97, 0xf0, 0x47, 0xaf, 0x61, 0x2f, 0xac, 0x8c, + 0x97, 0xc8, 0x15, 0x3f, 0xeb, 0xe8, 0xf8, 0x39, 0x68, 0x53, 0x43, 0xeb, 0x51, 0xa5, 0x0a, 0x15, 0xc7, 0x0c, 0x23, + 0x8a, 0x05, 0x9e, 0x63, 0x8c, 0x4f, 0xe8, 0x9e, 0xdb, 0xf2, 0xd7, 0xc8, 0xb0, 0xf9, 0x2f, 0x87, 0xf2, 0x75, 0xe6, + 0x98, 0xd0, 0x33, 0xe5, 0x4c, 0x85, 0x33, 0x1c, 0x61, 0xac, 0x37, 0xbe, 0xc1, 0xdc, 0x55, 0x33, 0xb6, 0xb5, 0x3a, + 0x93, 0xa2, 0xe9, 0x52, 0x54, 0x9f, 0x41, 0x43, 0xbc, 0xeb, 0xc6, 0xc0, 0xc2, 0xdd, 0x9f, 0x03, 0x42, 0x6e, 0x0e, + 0x85, 0xab, 0xda, 0x8c, 0x10, 0x6a, 0x09, 0xd4, 0x67, 0x85, 0xb0, 0x92, 0x56, 0x49, 0x4a, 0x4d, 0x31, 0xcf, 0x1f, + 0xc1, 0x7a, 0xaf, 0xf9, 0xff, 0x97, 0x19, 0xd1, 0xf7, 0xcb, 0xfe, 0x33, 0x7e, 0x41, 0xf4, 0x8c, 0x15, 0x4b, 0x26, + 0xfa, 0xf6, 0xba, 0x60, 0xc0, 0x09, 0xdf, 0x5e, 0xc3, 0xa9, 0xb5, 0xae, 0xdd, 0x4f, 0x0f, 0xe1, 0xfe, 0xbc, 0x51, + 0x2c, 0x9d, 0x22, 0x84, 0x58, 0xca, 0xcb, 0xcc, 0x54, 0xd2, 0x8a, 0x99, 0x17, 0x1d, 0x40, 0x9a, 0x77, 0x61, 0x76, + 0x9b, 0x72, 0x94, 0x25, 0x81, 0x67, 0x15, 0x30, 0xcd, 0xb0, 0x9d, 0x13, 0xa8, 0x5f, 0x1c, 0xff, 0x1d, 0xeb, 0xfe, + 0x0b, 0xe7, 0xa0, 0xee, 0xcf, 0x4f, 0x21, 0x91, 0x05, 0x4a, 0x94, 0x8c, 0x9a, 0x6e, 0x47, 0x75, 0x27, 0xeb, 0xdd, + 0x0b, 0x53, 0x22, 0x26, 0x5d, 0xf9, 0xdc, 0xcf, 0xed, 0x03, 0x68, 0x68, 0xab, 0x50, 0x55, 0x77, 0x65, 0xe3, 0x7c, + 0x45, 0x4b, 0x36, 0x20, 0xfd, 0x56, 0xfa, 0xe2, 0x06, 0x99, 0x97, 0x25, 0x51, 0x56, 0x91, 0xb3, 0xa4, 0xdb, 0xd3, + 0x39, 0x0a, 0x99, 0xe3, 0x7c, 0xe5, 0x85, 0xad, 0x95, 0xf6, 0xb5, 0x2a, 0xdb, 0x70, 0xa9, 0xa4, 0x68, 0x11, 0xcc, + 0x7a, 0x9f, 0xa3, 0xfe, 0x2e, 0x6f, 0x92, 0x89, 0x62, 0x54, 0x55, 0xbc, 0xae, 0x44, 0x2f, 0x7e, 0x7e, 0x0d, 0xc7, + 0x84, 0x7e, 0xf5, 0x07, 0xbd, 0xa5, 0xea, 0xde, 0x77, 0x98, 0xca, 0xec, 0xcd, 0x21, 0x88, 0xd2, 0x0d, 0xe9, 0xd5, + 0x5f, 0x89, 0x8f, 0xeb, 0xed, 0x89, 0x60, 0x39, 0x5d, 0x57, 0xf6, 0xeb, 0x7c, 0x5c, 0x0a, 0x73, 0x1e, 0xa9, 0x97, + 0xa6, 0xc1, 0xaf, 0x54, 0x51, 0x61, 0xce, 0xfa, 0xc7, 0x6c, 0x0a, 0xce, 0x4b, 0xd7, 0x32, 0x84, 0x1c, 0x91, 0xd0, + 0xc8, 0x91, 0x60, 0xce, 0xbf, 0x50, 0x8c, 0x5f, 0xb4, 0x49, 0xec, 0x8e, 0x5f, 0xc9, 0x6e, 0xa8, 0xe9, 0xa7, 0xcf, + 0xb9, 0x4b, 0x27, 0x54, 0x50, 0x7b, 0x82, 0x4b, 0xb0, 0xc0, 0xfb, 0x2b, 0x9b, 0x74, 0x31, 0xaa, 0xaa, 0x57, 0xe7, + 0xf3, 0x8f, 0x86, 0x38, 0x4c, 0x05, 0x14, 0x16, 0x6f, 0x32, 0x87, 0x76, 0x86, 0xd7, 0x74, 0x98, 0x67}; // Backwards compatibility alias #define INDEX_GZ INDEX_BR diff --git a/esphome/components/web_server/server_index_v3.h b/esphome/components/web_server/server_index_v3.h index a1cafe8707..37c80ddf96 100644 --- a/esphome/components/web_server/server_index_v3.h +++ b/esphome/components/web_server/server_index_v3.h @@ -3634,4058 +3634,4060 @@ constexpr uint8_t INDEX_GZ[] PROGMEM = { 0x36, 0x16, 0x43, 0x14, 0xa2, 0x85, 0xb1, 0x9f, 0x44, 0x7a, 0x6a, 0xf7, 0x8b, 0xa8, 0x63, 0x84, 0xe7, 0x2e, 0x8e, 0x40, 0xbb, 0x60, 0xb2, 0xd8, 0xed, 0x3a, 0x06, 0xb0, 0x93, 0x12, 0x46, 0xf3, 0x4c, 0x91, 0xc8, 0x45, 0x3d, 0x95, 0x78, 0xf0, 0xa9, 0x53, 0x8d, 0x99, 0x83, 0xf0, 0x54, 0x31, 0xd4, 0xc0, 0xc7, 0x7a, 0xaf, 0x4d, 0xc8, 0x99, 0xff, - 0xaf, 0xbd, 0xa7, 0xdd, 0x6e, 0x13, 0x49, 0xf6, 0xff, 0x3c, 0x05, 0x26, 0xd9, 0x04, 0x12, 0xc0, 0x20, 0xf9, 0x43, - 0x91, 0x8c, 0x3c, 0x93, 0xc4, 0x99, 0x8f, 0xf5, 0x4c, 0xe6, 0x24, 0x9e, 0xec, 0xbd, 0xeb, 0xf5, 0xb1, 0x90, 0xd4, - 0x92, 0xd8, 0x20, 0xd0, 0x01, 0x64, 0xd9, 0xa3, 0xb0, 0xcf, 0xb2, 0x8f, 0x70, 0x9f, 0x61, 0x9f, 0xec, 0x9e, 0xaa, - 0xea, 0x86, 0x06, 0x81, 0x2c, 0x4f, 0x32, 0xb3, 0x7b, 0xcf, 0xb9, 0x67, 0x26, 0x89, 0x68, 0xba, 0x9b, 0xea, 0xaf, + 0xaf, 0xbd, 0xa7, 0xdd, 0x6e, 0x13, 0x49, 0xf6, 0xff, 0x3c, 0x05, 0x26, 0xd9, 0x04, 0x12, 0xc0, 0x20, 0x59, 0xb6, + 0x22, 0x19, 0x79, 0x26, 0x89, 0x33, 0x1f, 0xeb, 0x99, 0xcc, 0x49, 0x3c, 0xd9, 0x7b, 0xd7, 0xeb, 0x63, 0x21, 0xa9, + 0x25, 0xb1, 0x41, 0xa0, 0x03, 0xc8, 0x1f, 0xa3, 0xb0, 0xcf, 0xb2, 0x8f, 0x70, 0x9f, 0x61, 0x9f, 0xec, 0x9e, 0xaa, + 0xea, 0x86, 0x06, 0x81, 0x2c, 0x4f, 0x32, 0xb3, 0x7b, 0xcf, 0xb9, 0x67, 0x26, 0x89, 0x68, 0x9a, 0xee, 0xea, 0xaf, 0xaa, 0xea, 0xfa, 0x2c, 0x53, 0x4a, 0xbb, 0x82, 0x7f, 0x85, 0x15, 0x72, 0xa5, 0x94, 0xb6, 0x1c, 0x88, 0x39, 0xdd, - 0x3e, 0xae, 0x1a, 0x58, 0x15, 0x8a, 0x7b, 0x32, 0x98, 0x09, 0x56, 0x76, 0x9d, 0x98, 0x87, 0x59, 0x97, 0x36, 0xbc, - 0x11, 0xb8, 0x60, 0x7a, 0xd8, 0x50, 0x6b, 0xdc, 0xf5, 0x4a, 0xa1, 0x30, 0xe3, 0xf2, 0xa4, 0x9e, 0x78, 0xe5, 0x67, - 0xd8, 0xd2, 0x95, 0x2a, 0xe0, 0x1b, 0x53, 0xa9, 0x04, 0x52, 0xb0, 0xe0, 0x94, 0x3e, 0x6f, 0xa5, 0xd1, 0x79, 0xb4, - 0x12, 0x82, 0xe3, 0x13, 0xaf, 0xa6, 0x10, 0xcf, 0x49, 0x77, 0x74, 0x12, 0xd0, 0x0f, 0x27, 0x6b, 0xa0, 0x00, 0x59, - 0x31, 0xc1, 0x39, 0xdb, 0x3a, 0xa0, 0xcb, 0xd4, 0xf5, 0xe3, 0xb5, 0xd8, 0x72, 0xd9, 0xc0, 0xcf, 0xd3, 0xc2, 0x96, + 0x01, 0xae, 0x1a, 0x58, 0x15, 0x8a, 0x7b, 0x32, 0x98, 0x09, 0x56, 0x76, 0x9d, 0x98, 0x87, 0x79, 0x8f, 0x36, 0xbc, + 0x11, 0xb8, 0x60, 0x7a, 0xd8, 0x50, 0x6b, 0xd2, 0xf3, 0x4a, 0xa1, 0x30, 0xe3, 0xf2, 0xa4, 0x1e, 0x7b, 0xe5, 0x67, + 0xd8, 0xd2, 0x95, 0x2a, 0xe0, 0x1b, 0x53, 0xa9, 0x04, 0x52, 0xb0, 0xe0, 0x84, 0xba, 0xb7, 0xd2, 0xe8, 0x2c, 0xba, + 0x11, 0x82, 0xe3, 0x63, 0xaf, 0xa6, 0x10, 0xcf, 0x49, 0x6f, 0x7c, 0x1c, 0xd0, 0x0f, 0x27, 0x6b, 0xa0, 0x00, 0x59, + 0x31, 0xc1, 0x39, 0xdb, 0x3a, 0xa4, 0xcb, 0xd4, 0xd5, 0xe3, 0xb5, 0xd8, 0x72, 0xd9, 0xd0, 0xcf, 0xd3, 0xc2, 0x96, 0x58, 0x8e, 0x8c, 0x4f, 0xbd, 0x1c, 0x85, 0xb4, 0xa6, 0x1a, 0xdf, 0x1f, 0xae, 0x5f, 0xcb, 0xb7, 0x58, 0xf4, 0xc8, - 0x88, 0xa6, 0xd7, 0x57, 0x61, 0xb7, 0x6c, 0xa4, 0xd5, 0x01, 0x46, 0x82, 0x27, 0x31, 0x04, 0x0c, 0xcc, 0x8c, 0xa8, - 0xff, 0xdb, 0xb8, 0x8a, 0xfa, 0xd1, 0xfe, 0x2e, 0x47, 0xfe, 0x3f, 0xbf, 0x7d, 0x7f, 0x01, 0x7a, 0x2d, 0x0f, 0x15, - 0xd1, 0x6b, 0x95, 0xdb, 0xb0, 0x98, 0xa0, 0x29, 0x52, 0xbb, 0xaa, 0xb7, 0x00, 0xea, 0x8c, 0x37, 0x86, 0xfd, 0x5b, - 0x73, 0xb5, 0x5a, 0x99, 0x60, 0xd1, 0x6a, 0x2e, 0xe3, 0x80, 0xb8, 0xc3, 0xb1, 0x9a, 0x09, 0xa4, 0xce, 0x2a, 0x48, - 0x1d, 0xc2, 0xe1, 0xf2, 0x7c, 0x2a, 0xef, 0x67, 0xd1, 0xea, 0x9b, 0x20, 0x90, 0xc5, 0x36, 0x82, 0x89, 0xe3, 0x92, - 0x8c, 0x12, 0x32, 0xd0, 0x40, 0xfb, 0x64, 0xf9, 0xc9, 0x35, 0xb7, 0x17, 0x18, 0x5f, 0x0f, 0xef, 0xae, 0xb9, 0x4e, - 0x22, 0x8f, 0x47, 0xfc, 0x7e, 0x70, 0x32, 0xf6, 0x6f, 0x14, 0xe4, 0x34, 0x5d, 0x15, 0x9c, 0xb9, 0x02, 0x36, 0x5c, - 0xa6, 0x69, 0x14, 0x9a, 0x71, 0xb4, 0x52, 0xfb, 0x27, 0xf4, 0x20, 0x2a, 0x78, 0xf4, 0xa8, 0x2a, 0x5f, 0x8f, 0x02, - 0x7f, 0xf4, 0xd1, 0x55, 0x1f, 0xaf, 0x7d, 0xb7, 0x5f, 0xe1, 0x27, 0xed, 0x4c, 0xed, 0x03, 0xac, 0xca, 0x37, 0x41, - 0x70, 0xb2, 0x4f, 0x2d, 0xfa, 0x27, 0xfb, 0x63, 0xff, 0xa6, 0x2f, 0xa5, 0x86, 0xe1, 0x7a, 0x53, 0x97, 0x87, 0xe0, - 0xcc, 0x2d, 0xcd, 0x12, 0x8c, 0xe9, 0x30, 0x62, 0x5a, 0x71, 0xf9, 0x85, 0x58, 0x33, 0x04, 0xaf, 0x36, 0x42, 0x71, - 0x7a, 0x00, 0x57, 0xbd, 0x4f, 0x9f, 0xb4, 0xdc, 0x0e, 0x75, 0x26, 0x05, 0x69, 0x43, 0x35, 0x1f, 0x56, 0x31, 0x30, - 0xd2, 0x8c, 0xae, 0x89, 0x50, 0x72, 0x81, 0x6e, 0x8c, 0x32, 0x03, 0x33, 0xec, 0x78, 0x0b, 0xd0, 0x38, 0xf2, 0x9f, - 0xd2, 0x8d, 0x78, 0x04, 0x59, 0xb5, 0x25, 0x24, 0xae, 0x4b, 0x3a, 0x17, 0x3a, 0x85, 0x3c, 0x4e, 0x20, 0x28, 0x4b, - 0xf0, 0x3b, 0xa4, 0x07, 0xd1, 0x02, 0x1d, 0xb2, 0xba, 0xe5, 0xc1, 0x79, 0xbc, 0x4c, 0xe4, 0x51, 0x13, 0xf3, 0x72, - 0x5a, 0x5a, 0xa1, 0x6e, 0x75, 0xbd, 0x44, 0xd4, 0xc8, 0xbd, 0xa4, 0x69, 0xc9, 0x40, 0x87, 0xa7, 0xa5, 0x46, 0x85, - 0xe6, 0x82, 0x57, 0x9f, 0xa4, 0x38, 0x62, 0x86, 0x76, 0x99, 0x18, 0xd1, 0x55, 0x41, 0xa7, 0x12, 0x42, 0x94, 0xdd, - 0x28, 0x2b, 0x02, 0x38, 0xd3, 0xaa, 0xf7, 0x1f, 0xaf, 0x43, 0x24, 0x6c, 0x89, 0xdb, 0x2f, 0xef, 0x83, 0xd4, 0x1b, - 0x9a, 0xb4, 0x99, 0x55, 0xe5, 0xeb, 0xf1, 0x30, 0xc8, 0x17, 0x9b, 0x0e, 0xc1, 0xcc, 0x0b, 0xc7, 0x01, 0xbb, 0xf0, - 0x86, 0xdf, 0x61, 0x9d, 0xd7, 0xc3, 0xe0, 0x15, 0x54, 0xc8, 0xd4, 0xfe, 0xe3, 0x35, 0x91, 0xee, 0x3a, 0x84, 0x9d, - 0xd1, 0x16, 0xa8, 0x7e, 0x87, 0xa7, 0x5c, 0x62, 0x31, 0xb5, 0x46, 0x60, 0x89, 0xdc, 0x52, 0x1c, 0xdb, 0x32, 0x64, - 0x3c, 0xe5, 0x0f, 0xec, 0x4d, 0x85, 0x9f, 0x5a, 0x80, 0x2b, 0x12, 0x27, 0x58, 0xde, 0x99, 0x32, 0xb0, 0x44, 0x56, - 0xdf, 0x45, 0x2b, 0x01, 0x29, 0x9f, 0x00, 0x0a, 0x51, 0x79, 0xfa, 0x7e, 0x70, 0x22, 0xab, 0x85, 0x50, 0x76, 0x4e, - 0xfd, 0xc2, 0xaf, 0x4c, 0x55, 0x8a, 0x04, 0x50, 0x8b, 0x5b, 0xb5, 0x7f, 0xb2, 0x2f, 0xd7, 0xee, 0x0f, 0xba, 0x67, - 0xd2, 0xe0, 0xb0, 0x57, 0x71, 0x6f, 0xbe, 0x2c, 0x1e, 0xb2, 0x2b, 0x05, 0x6e, 0xc9, 0x19, 0x94, 0xc0, 0x1c, 0x95, - 0x9b, 0x6c, 0x90, 0x1f, 0x48, 0x99, 0x58, 0x10, 0x28, 0xda, 0x3d, 0x02, 0x3f, 0x46, 0x7a, 0x37, 0x5f, 0x42, 0xb2, - 0xcc, 0x14, 0xbd, 0x0d, 0xf8, 0xbf, 0xc5, 0x94, 0xa0, 0xa4, 0x9b, 0x85, 0x49, 0x14, 0xab, 0x30, 0xcc, 0x6a, 0xde, - 0x24, 0x45, 0xca, 0xd7, 0x86, 0x03, 0xae, 0x25, 0xab, 0x30, 0x61, 0xfb, 0xd5, 0xa6, 0xd2, 0xb8, 0x07, 0x7a, 0xf1, - 0x43, 0xe1, 0x83, 0xa9, 0x20, 0xad, 0x1c, 0xc0, 0xe6, 0x7c, 0x54, 0x97, 0x8f, 0x7d, 0xe3, 0x2f, 0x91, 0x31, 0xf4, - 0x8c, 0x6b, 0xcf, 0xf8, 0x31, 0xbc, 0xca, 0x6a, 0x17, 0x2f, 0xcf, 0x25, 0x67, 0xb0, 0x9e, 0x06, 0x11, 0x98, 0xca, - 0x97, 0x0a, 0xdf, 0xe2, 0x36, 0x23, 0x17, 0x5e, 0x3c, 0x65, 0x22, 0x85, 0x9b, 0x78, 0x2b, 0x64, 0x07, 0xba, 0x34, - 0x2d, 0x10, 0x9e, 0x6c, 0x8f, 0x9b, 0xd6, 0xf9, 0xd6, 0x28, 0x8d, 0x83, 0x3f, 0xb3, 0x3b, 0x60, 0xb3, 0x92, 0x34, - 0x5a, 0x80, 0xcc, 0xca, 0x9b, 0x72, 0x1d, 0x84, 0xa1, 0xb1, 0xdd, 0x3e, 0xf7, 0xe9, 0x13, 0x93, 0xb2, 0x8a, 0xa5, - 0xd1, 0x74, 0x1a, 0x30, 0x4d, 0xca, 0x3e, 0x96, 0x7f, 0xe6, 0x74, 0xcf, 0x16, 0x91, 0xab, 0xf5, 0xac, 0xe9, 0x60, - 0x89, 0x11, 0xb3, 0x9c, 0x1b, 0x04, 0xc4, 0x45, 0xc6, 0x55, 0xc8, 0x90, 0x6b, 0xe2, 0x5c, 0x14, 0x07, 0xd7, 0x1c, - 0x47, 0xcb, 0x61, 0xc0, 0x4c, 0x3c, 0x0d, 0xf0, 0xc9, 0xf5, 0x70, 0x39, 0x1c, 0x06, 0x94, 0x2e, 0x0c, 0xe2, 0xaf, - 0x45, 0x09, 0xca, 0x45, 0x33, 0xbd, 0x07, 0x83, 0xb2, 0xd2, 0x2a, 0xf8, 0x60, 0x33, 0x09, 0x37, 0x07, 0xfa, 0x40, - 0x0a, 0x32, 0xd0, 0xcd, 0x33, 0xed, 0xaa, 0x70, 0x63, 0x61, 0x89, 0xda, 0xab, 0x61, 0xe9, 0xdc, 0x4b, 0xf5, 0x3d, - 0xce, 0xb0, 0xe2, 0x85, 0x63, 0xe5, 0x15, 0xed, 0x5d, 0xd5, 0x50, 0xc9, 0xf4, 0x8b, 0x67, 0x97, 0x53, 0x0d, 0xf5, - 0xb5, 0xef, 0x4d, 0xc3, 0x28, 0x49, 0xfd, 0x91, 0x7a, 0xd5, 0x7b, 0xed, 0x6b, 0x97, 0xf3, 0x54, 0xd3, 0xaf, 0x8c, - 0x6f, 0xe5, 0x3c, 0x60, 0x02, 0x53, 0x62, 0x1a, 0xb0, 0x86, 0x3a, 0xf2, 0xe9, 0xd9, 0x56, 0x4f, 0x60, 0x64, 0xac, - 0xf3, 0xad, 0x0b, 0xb5, 0x2a, 0x19, 0xc5, 0x30, 0x55, 0x24, 0x64, 0x14, 0xfb, 0x56, 0xef, 0x91, 0x10, 0xe6, 0x9b, - 0xe5, 0x1a, 0x99, 0x86, 0xb4, 0x20, 0xbe, 0x18, 0x04, 0x5f, 0x78, 0x8e, 0xd2, 0xf3, 0x9e, 0xec, 0xf5, 0x50, 0x22, - 0xe3, 0x83, 0x6f, 0xca, 0x1c, 0xc8, 0xe3, 0x75, 0x9a, 0x81, 0xc9, 0x61, 0x18, 0xa5, 0x0a, 0x44, 0x76, 0x83, 0x0f, - 0x0e, 0xaa, 0x56, 0xd2, 0xbc, 0x57, 0x4d, 0xcf, 0x38, 0x16, 0x78, 0x89, 0xb4, 0x14, 0x25, 0x97, 0x10, 0x88, 0x02, - 0x82, 0x94, 0x96, 0xe2, 0x38, 0x71, 0xdf, 0x3c, 0x58, 0xbe, 0x12, 0xff, 0x26, 0xe1, 0xfd, 0x32, 0x3d, 0x7f, 0xbc, - 0x4e, 0x4e, 0x05, 0x51, 0xff, 0x3e, 0xc1, 0xb5, 0x04, 0x76, 0x85, 0x53, 0xf9, 0x4c, 0x55, 0x4e, 0x05, 0x25, 0xc2, - 0xba, 0x25, 0xf4, 0xaa, 0x09, 0x76, 0x37, 0x16, 0x31, 0xf3, 0xb9, 0x18, 0x45, 0x30, 0x60, 0x95, 0xa3, 0x07, 0xc1, - 0x9a, 0x72, 0xde, 0x2a, 0x05, 0x8b, 0x6b, 0x24, 0x18, 0x80, 0xb9, 0x38, 0x8f, 0x30, 0xc8, 0xae, 0x81, 0x91, 0x84, - 0x08, 0x66, 0x62, 0x8c, 0x46, 0x24, 0x27, 0x91, 0xf3, 0xc3, 0xc5, 0x32, 0xc5, 0xc8, 0xf4, 0x00, 0x00, 0xcb, 0x54, - 0x05, 0x2f, 0x8c, 0x80, 0xeb, 0x8b, 0x0b, 0x4f, 0xa6, 0x2a, 0xfe, 0x78, 0xb3, 0x8c, 0x4b, 0x67, 0x00, 0xc7, 0xe1, - 0x30, 0x50, 0xaf, 0x03, 0x8f, 0x31, 0x1f, 0xc6, 0xc8, 0x28, 0xd2, 0xba, 0x68, 0x23, 0xb4, 0x7f, 0xa8, 0x41, 0x20, - 0x23, 0xea, 0xa7, 0xa7, 0x05, 0xb5, 0x83, 0x85, 0x68, 0xd5, 0xa5, 0x61, 0x0e, 0x40, 0x46, 0x79, 0x0a, 0x73, 0xe7, - 0xc2, 0xa5, 0x5e, 0x98, 0xd6, 0xa9, 0x17, 0x2a, 0xd9, 0xd5, 0x0d, 0x70, 0x1a, 0x06, 0xd9, 0x75, 0xe1, 0xe8, 0x5a, - 0x8c, 0x17, 0xb6, 0x24, 0x95, 0x2b, 0x68, 0xe9, 0xe6, 0x72, 0x7b, 0xb6, 0x45, 0xec, 0xcf, 0xbd, 0xf8, 0x8e, 0xcc, - 0xdf, 0x0c, 0xd9, 0x46, 0x4e, 0x57, 0x15, 0xa2, 0x07, 0x34, 0x01, 0x44, 0x1a, 0x54, 0xe5, 0xeb, 0xbc, 0x8c, 0xf1, - 0xd1, 0xe6, 0x36, 0x40, 0xf0, 0xad, 0x6b, 0xf5, 0x39, 0xb3, 0x48, 0xfe, 0x48, 0x4d, 0x7a, 0x5a, 0xd2, 0x30, 0xbc, - 0xa4, 0x3c, 0xbc, 0xb0, 0xbc, 0xd1, 0x70, 0x30, 0x44, 0x29, 0x08, 0x6e, 0x1c, 0x19, 0x26, 0xc1, 0xac, 0x5f, 0x51, - 0x7a, 0xf7, 0x87, 0x2e, 0x07, 0x83, 0xe5, 0x08, 0x61, 0x39, 0x6a, 0x44, 0xb3, 0x9e, 0x58, 0x11, 0xe0, 0x45, 0x80, - 0x0b, 0x89, 0x91, 0x03, 0xa1, 0xfc, 0x98, 0x4a, 0xbe, 0x85, 0x62, 0x38, 0x1a, 0x04, 0x3b, 0x1d, 0x8d, 0xd8, 0x75, - 0x23, 0x6c, 0x15, 0x67, 0x27, 0xfb, 0x54, 0x9b, 0x88, 0x22, 0x55, 0x82, 0x69, 0x88, 0x61, 0x84, 0xc5, 0x2c, 0x40, - 0x82, 0x70, 0xd7, 0x29, 0x2e, 0x3a, 0xd6, 0x1c, 0xd5, 0xd2, 0xce, 0x69, 0x99, 0xe1, 0xc1, 0x56, 0x6a, 0xff, 0x04, - 0x53, 0x7e, 0x02, 0x59, 0x87, 0xa0, 0x58, 0x27, 0xfb, 0xf4, 0xa8, 0x54, 0x4e, 0x44, 0xd1, 0x89, 0x90, 0x41, 0x76, - 0x79, 0x07, 0x0f, 0x3a, 0x2a, 0x49, 0xca, 0x16, 0x50, 0xea, 0x65, 0xaa, 0x32, 0xe7, 0x0c, 0x16, 0x8f, 0xbe, 0x07, - 0xa1, 0x79, 0x6c, 0x70, 0x89, 0x50, 0x95, 0xb9, 0x77, 0x8b, 0x23, 0x17, 0x6f, 0xbc, 0x5b, 0xcd, 0xe1, 0xaf, 0x8a, - 0xb3, 0x96, 0x94, 0xcf, 0xda, 0x68, 0xe3, 0x86, 0x1c, 0xc0, 0x0d, 0x79, 0x54, 0xbf, 0xb8, 0x33, 0xb1, 0xb8, 0xe3, - 0x86, 0xc5, 0x1d, 0x6f, 0x59, 0xdc, 0x80, 0x2f, 0xa4, 0x92, 0x4f, 0x5d, 0x8c, 0xbe, 0xd4, 0xf9, 0xe4, 0x71, 0x7e, - 0xa4, 0xcb, 0xcf, 0x19, 0xce, 0x93, 0x99, 0x04, 0x60, 0x4b, 0xdc, 0x30, 0x57, 0x75, 0xf3, 0x22, 0x4d, 0xc4, 0xe6, - 0xc0, 0xf3, 0x53, 0x27, 0xc6, 0x0d, 0x29, 0xbc, 0xb5, 0xa0, 0x3a, 0x5e, 0xd8, 0xa5, 0xd8, 0xd0, 0xd0, 0x66, 0x0d, - 0x23, 0x9d, 0x6d, 0x19, 0xe9, 0xa8, 0x74, 0x74, 0xf9, 0xb0, 0xe9, 0x10, 0xca, 0x83, 0x82, 0x3d, 0x08, 0xfe, 0x15, - 0xb8, 0x65, 0xca, 0xfb, 0xb0, 0x19, 0xc7, 0x4a, 0x3b, 0x6a, 0xe1, 0x25, 0xc9, 0x2a, 0x8a, 0xc1, 0x40, 0x01, 0xba, - 0x79, 0xd8, 0x96, 0x9a, 0xfb, 0x21, 0x8f, 0x7d, 0xd6, 0xb8, 0x99, 0x8a, 0xf7, 0xf2, 0x96, 0x6a, 0x1d, 0x1e, 0x52, - 0x8d, 0x85, 0x97, 0xa6, 0x2c, 0xc6, 0x49, 0xf7, 0x20, 0x49, 0xc6, 0x7f, 0xc8, 0x36, 0xab, 0xc1, 0x21, 0x81, 0x84, - 0xd5, 0x11, 0x43, 0x2f, 0x80, 0x05, 0x23, 0x8d, 0x64, 0xa8, 0xaf, 0xa5, 0x38, 0xaa, 0x71, 0x3e, 0xf1, 0x3f, 0xe1, - 0x71, 0xd5, 0x62, 0xc9, 0xd3, 0xd7, 0x39, 0xd2, 0xad, 0x85, 0x37, 0x7e, 0x0f, 0x76, 0x30, 0x5a, 0xcb, 0x00, 0x9f, - 0x16, 0x39, 0x6a, 0x6a, 0x4c, 0x3c, 0xe1, 0xa8, 0x40, 0x92, 0x88, 0x25, 0xb9, 0xc5, 0x30, 0x04, 0x1b, 0xf0, 0xcc, - 0xc9, 0xd5, 0xba, 0x95, 0xed, 0x4f, 0x7d, 0x7d, 0x03, 0x6b, 0x02, 0x6a, 0x0b, 0xdc, 0x7e, 0x2e, 0x74, 0x0b, 0x0c, - 0xe7, 0x48, 0x07, 0x45, 0xe9, 0x25, 0xa4, 0x43, 0xb7, 0xc5, 0x65, 0x7a, 0x10, 0x03, 0xd5, 0x02, 0xb5, 0xe2, 0x93, - 0x29, 0xfe, 0x72, 0xae, 0xb2, 0x27, 0x43, 0xfc, 0xd5, 0xba, 0xca, 0x95, 0x58, 0x15, 0x29, 0x82, 0x34, 0x66, 0xb5, - 0x5f, 0xda, 0x4f, 0x64, 0xae, 0xfd, 0x80, 0x6d, 0xc3, 0x17, 0xf8, 0xd1, 0xe3, 0x75, 0x02, 0x01, 0x0a, 0xe4, 0x31, - 0x84, 0x56, 0xac, 0x67, 0xb5, 0xe5, 0xd3, 0x86, 0xf2, 0xa1, 0xfe, 0x07, 0x13, 0x7e, 0xdc, 0x25, 0x51, 0x41, 0x53, - 0xca, 0x32, 0x90, 0xeb, 0xa1, 0x1f, 0x7a, 0xf1, 0xdd, 0x35, 0xdd, 0x42, 0x34, 0xc1, 0xe2, 0xe7, 0xb2, 0x1d, 0xe2, - 0x45, 0xcb, 0xd6, 0x21, 0xa9, 0xa4, 0xa8, 0xba, 0xe3, 0x84, 0xde, 0xfd, 0x73, 0x2c, 0xf1, 0x77, 0xa5, 0x6b, 0x2c, - 0x5f, 0x90, 0xd2, 0x87, 0xae, 0x1f, 0xaf, 0x35, 0xb6, 0xd9, 0x4d, 0x65, 0xb4, 0x15, 0x06, 0x12, 0x96, 0x07, 0xaf, - 0xc4, 0xf3, 0xb1, 0xdf, 0x45, 0xf3, 0x8f, 0x61, 0x74, 0x6b, 0x3e, 0x5e, 0xa7, 0xa7, 0xea, 0xdc, 0x8b, 0x3f, 0xb2, - 0xb1, 0x39, 0xf2, 0xe3, 0x51, 0x00, 0xcc, 0xe3, 0x30, 0xf0, 0xc2, 0x8f, 0xfc, 0xd1, 0x8c, 0x96, 0x29, 0x1a, 0x74, - 0xdd, 0x7b, 0x83, 0x16, 0x73, 0x42, 0x82, 0x44, 0xe4, 0x6a, 0x6b, 0x66, 0x41, 0x79, 0x3f, 0x10, 0xd7, 0xfa, 0x82, - 0x51, 0x2c, 0x6a, 0x19, 0xe0, 0x8f, 0x00, 0x36, 0x66, 0x10, 0xe0, 0xc1, 0x50, 0x71, 0xbd, 0x54, 0x43, 0x1e, 0x2a, - 0x69, 0xd5, 0xf2, 0x0c, 0xc5, 0xd7, 0xd8, 0xc3, 0x6f, 0xff, 0x1c, 0x94, 0x3c, 0xe4, 0x73, 0x79, 0x2f, 0x9f, 0x37, - 0x42, 0x28, 0x35, 0xc9, 0xb1, 0xf0, 0x01, 0x1f, 0xe7, 0x0c, 0x66, 0xf3, 0xa7, 0xe5, 0xc6, 0x5e, 0x92, 0x2c, 0xe7, - 0x6c, 0x4c, 0x2a, 0xb1, 0xd3, 0x02, 0xa8, 0xf2, 0x3d, 0x44, 0x06, 0xec, 0x6f, 0xcb, 0xd6, 0xf1, 0xc1, 0x2b, 0x30, - 0xf0, 0x03, 0x86, 0x32, 0x9a, 0x4c, 0xd4, 0x42, 0x14, 0x70, 0x4f, 0x33, 0xe7, 0xe0, 0x6f, 0xcb, 0x37, 0x67, 0xf6, - 0x9b, 0xbc, 0x71, 0x08, 0x8c, 0xb1, 0xb0, 0x56, 0xe2, 0x7c, 0xb1, 0x04, 0xaf, 0x18, 0xd1, 0xc4, 0x0b, 0x9b, 0x87, - 0x73, 0x59, 0xda, 0xe2, 0x0b, 0xc6, 0xc6, 0xc0, 0x70, 0x1b, 0x1b, 0xa5, 0xd7, 0x01, 0xbb, 0x61, 0xb9, 0x25, 0xd4, - 0xe6, 0xc7, 0x6a, 0x5a, 0x60, 0xa8, 0x56, 0xae, 0x7b, 0xe4, 0x5c, 0x9d, 0x34, 0xa4, 0x01, 0x8e, 0x81, 0x8f, 0x5c, - 0x3e, 0x62, 0x95, 0x23, 0x35, 0x30, 0x54, 0x09, 0x80, 0x46, 0xc8, 0x4e, 0x1b, 0xca, 0xbb, 0x80, 0xa8, 0x1b, 0x60, - 0x33, 0x1c, 0xbd, 0x0b, 0xa9, 0x2d, 0xf8, 0x3c, 0x05, 0x70, 0xf2, 0xb4, 0x42, 0x6a, 0xd2, 0x34, 0x63, 0x75, 0xa2, - 0x36, 0x95, 0x84, 0x34, 0xc2, 0x39, 0x00, 0xbd, 0x64, 0x84, 0xb8, 0xaa, 0x76, 0x6d, 0x94, 0xf2, 0xc8, 0x87, 0x98, - 0xf8, 0x3d, 0x64, 0x49, 0xd2, 0x38, 0x61, 0xf9, 0xa2, 0x1b, 0x6a, 0x51, 0xbb, 0x3c, 0x1f, 0x45, 0xb9, 0x61, 0x1b, - 0xc0, 0x12, 0xe0, 0x00, 0xab, 0xdf, 0x42, 0xf2, 0x72, 0x3d, 0xe7, 0xe6, 0x9d, 0xf1, 0x74, 0xa8, 0x72, 0xd3, 0xbb, - 0xa6, 0xf7, 0x2b, 0x95, 0x03, 0x55, 0x22, 0xd3, 0xb5, 0xa0, 0x69, 0x25, 0xd4, 0xbb, 0x21, 0x55, 0xc2, 0x0e, 0x04, - 0x4c, 0x15, 0xfc, 0xca, 0x26, 0x13, 0x36, 0x4a, 0x13, 0x5d, 0xc8, 0x98, 0xf2, 0x60, 0xeb, 0xe0, 0x64, 0xbb, 0xe7, - 0xaa, 0x3f, 0x41, 0xc8, 0x19, 0x11, 0x93, 0x90, 0x03, 0x24, 0xee, 0x4c, 0xf5, 0xd3, 0x44, 0x3d, 0x96, 0xa7, 0x88, - 0x7f, 0x05, 0xa4, 0xd0, 0x35, 0xe5, 0x08, 0x1a, 0xa7, 0x3f, 0xc5, 0xbe, 0x88, 0x72, 0x23, 0xd0, 0xed, 0xa8, 0x68, - 0xdb, 0xf1, 0x5d, 0x3b, 0x6f, 0x0e, 0x1d, 0x3b, 0x53, 0x0d, 0x70, 0x75, 0xfe, 0x58, 0xd9, 0xc6, 0x44, 0xa0, 0x5c, - 0xf5, 0xfc, 0xed, 0xab, 0x3f, 0x9f, 0xbd, 0xde, 0x15, 0x23, 0x60, 0x97, 0x6d, 0xe8, 0x72, 0x19, 0x6e, 0xe9, 0xf4, - 0x97, 0x9f, 0x1e, 0xd6, 0x6d, 0xcb, 0x79, 0xe1, 0xa8, 0x06, 0x59, 0xa7, 0x4b, 0x78, 0x71, 0x14, 0xdd, 0xb0, 0xf8, - 0xb3, 0xa7, 0x41, 0xee, 0xbc, 0x1e, 0xdc, 0xb7, 0x3f, 0x9f, 0xfd, 0xb4, 0x33, 0xa8, 0x47, 0x8e, 0x0d, 0xb8, 0x3d, - 0x8d, 0x16, 0x0f, 0x18, 0x5d, 0x5b, 0x35, 0xd4, 0x51, 0x10, 0x25, 0xac, 0x01, 0x82, 0x57, 0xe7, 0x6f, 0xdf, 0xe3, - 0x74, 0x15, 0x2c, 0x08, 0x75, 0xf5, 0x79, 0x83, 0xff, 0xf9, 0xdd, 0xd9, 0xfb, 0xf7, 0xaa, 0x81, 0xc9, 0xba, 0x13, - 0xb9, 0x77, 0xbe, 0x89, 0xef, 0xa1, 0x38, 0xb5, 0x7b, 0x9d, 0xa8, 0x1a, 0x5d, 0xa4, 0xcb, 0xa3, 0xa1, 0xb2, 0x8d, - 0x6d, 0xce, 0xa9, 0x1d, 0xff, 0x32, 0xdd, 0x7e, 0x77, 0x1a, 0x57, 0x0d, 0x3e, 0xda, 0x4e, 0x52, 0x4b, 0x25, 0x73, - 0x3f, 0xbc, 0xae, 0x29, 0xf5, 0x6e, 0x6b, 0x4a, 0xe1, 0xfa, 0xb8, 0x81, 0x1f, 0x97, 0xd1, 0x5c, 0x62, 0x47, 0xd8, - 0xed, 0xfd, 0xd3, 0x25, 0xdd, 0xe1, 0x3e, 0x03, 0x68, 0x9e, 0x6c, 0xa5, 0x0a, 0x75, 0x4d, 0x31, 0xbf, 0x78, 0xe5, - 0x73, 0x3b, 0x0a, 0xc0, 0x26, 0x9f, 0xc9, 0x6a, 0xc8, 0x32, 0xab, 0xca, 0x3d, 0x6a, 0xdc, 0xca, 0xad, 0x80, 0x9a, - 0x91, 0xea, 0x86, 0xd3, 0x94, 0x85, 0x37, 0x06, 0x43, 0x77, 0x73, 0x18, 0xa5, 0x69, 0x34, 0xef, 0x3a, 0xf6, 0xe2, - 0x56, 0x55, 0x7a, 0x42, 0xd8, 0xc1, 0xed, 0xf0, 0xbb, 0xff, 0xfa, 0x67, 0x05, 0xcd, 0x53, 0xf9, 0x75, 0xca, 0xe6, - 0x0b, 0x16, 0x7b, 0xe9, 0x32, 0x66, 0x99, 0xf2, 0xaf, 0xff, 0x79, 0x55, 0xb9, 0xd8, 0xf7, 0xe4, 0x36, 0xc4, 0xd2, - 0xcb, 0x4d, 0xae, 0x83, 0x68, 0xb5, 0x57, 0x78, 0xdc, 0xdd, 0x53, 0x79, 0xe6, 0x4f, 0x67, 0x79, 0xed, 0xd3, 0x74, - 0xcb, 0xd8, 0x04, 0xf4, 0xa4, 0x0f, 0x50, 0xce, 0xa3, 0x55, 0xf7, 0x5f, 0xff, 0xcc, 0x05, 0x36, 0xf7, 0xee, 0xba, - 0x7a, 0x40, 0xcb, 0x2b, 0x5a, 0x5f, 0x67, 0x63, 0x89, 0xe1, 0xfd, 0xc6, 0x02, 0x6f, 0x14, 0xd2, 0xae, 0xdc, 0xd4, - 0xcd, 0x6d, 0x19, 0xd3, 0x77, 0xfe, 0x74, 0xf6, 0xb9, 0x83, 0x82, 0x09, 0xbd, 0x77, 0x54, 0x50, 0xe9, 0x0b, 0x0c, - 0x6b, 0xd0, 0xdd, 0x7d, 0xc1, 0x3e, 0x73, 0x5c, 0xf7, 0x0d, 0xe9, 0x4b, 0x8c, 0x86, 0x4b, 0x6e, 0xdf, 0x0f, 0x06, - 0x79, 0xb2, 0x5a, 0xb9, 0x3d, 0xf8, 0x0c, 0x9e, 0x6e, 0x94, 0x70, 0xf6, 0xa2, 0x6b, 0xeb, 0x14, 0xcc, 0x67, 0x87, - 0x09, 0x41, 0xeb, 0xf7, 0x9a, 0xe9, 0x68, 0xc6, 0xd7, 0xe4, 0xc4, 0xb6, 0xf1, 0xed, 0x0d, 0x64, 0x0d, 0xa5, 0x98, - 0xe8, 0x34, 0xd7, 0x1a, 0x1a, 0xf5, 0xe0, 0xac, 0x62, 0x6f, 0x41, 0x4a, 0x02, 0x05, 0x35, 0x26, 0x20, 0x74, 0xa9, - 0xdc, 0xa2, 0x6f, 0xbc, 0xe0, 0x66, 0xb7, 0x0b, 0x55, 0x33, 0x05, 0x43, 0xd2, 0xfc, 0xef, 0x23, 0xde, 0x48, 0x97, - 0x1f, 0x4c, 0xbb, 0x57, 0x5e, 0xca, 0xe2, 0xeb, 0x19, 0x78, 0xfb, 0x0a, 0xe9, 0x01, 0xc4, 0xd1, 0xdd, 0x86, 0x94, - 0x4b, 0x6c, 0x69, 0x0d, 0x1a, 0x2d, 0x30, 0xdc, 0x6f, 0xc3, 0xdd, 0x5f, 0x08, 0x73, 0x77, 0xcf, 0xc0, 0x1f, 0xf3, - 0x77, 0xc3, 0xde, 0xdb, 0x28, 0xd3, 0xff, 0x63, 0xef, 0xff, 0x44, 0xec, 0xbd, 0xf5, 0x3b, 0xbf, 0x65, 0x61, 0xff, - 0x0f, 0x60, 0xf9, 0x2e, 0x73, 0xcf, 0x38, 0xa6, 0xd7, 0x34, 0xcf, 0xd5, 0xe2, 0xd2, 0xe1, 0x45, 0xbc, 0xba, 0xa1, - 0x60, 0xe5, 0xc1, 0xd6, 0xb8, 0xe5, 0xa0, 0x87, 0xc8, 0x7e, 0xcb, 0x51, 0xfe, 0xfd, 0x11, 0x7d, 0x42, 0x19, 0xaa, - 0x24, 0x4c, 0xdf, 0x3d, 0x33, 0x92, 0xd2, 0x48, 0xbc, 0x95, 0x77, 0xb7, 0x0b, 0xde, 0x11, 0xc0, 0x7e, 0xb3, 0xf2, - 0xee, 0xea, 0x80, 0x6d, 0x44, 0xaf, 0xd5, 0x8f, 0x9d, 0x82, 0x97, 0x4f, 0x17, 0x5d, 0x7c, 0x8c, 0x41, 0xc2, 0xd2, - 0x53, 0x28, 0x74, 0x1f, 0xaf, 0xf7, 0xaa, 0x15, 0xb3, 0x01, 0xf8, 0x3f, 0x4b, 0x80, 0x47, 0x25, 0xc0, 0xfd, 0xe4, - 0x3a, 0x0a, 0x1f, 0x02, 0xf9, 0xcf, 0x20, 0xfc, 0xf9, 0xcd, 0xa0, 0xe3, 0xe7, 0x36, 0x60, 0xc7, 0xd2, 0x2a, 0xf0, - 0x58, 0x58, 0x85, 0xbe, 0x57, 0x2f, 0xab, 0xaf, 0x10, 0x5a, 0xa4, 0xb1, 0x8c, 0x08, 0xad, 0x02, 0x7a, 0x15, 0x05, - 0x74, 0x5c, 0x15, 0x92, 0xeb, 0x87, 0x93, 0xd8, 0x8b, 0xd9, 0xb8, 0xf9, 0x0a, 0x50, 0xb2, 0x4e, 0xbe, 0xb3, 0x92, - 0xe5, 0x62, 0x11, 0xc5, 0x69, 0x72, 0x8d, 0x71, 0x5a, 0xe6, 0x3e, 0x5c, 0x28, 0x20, 0xa3, 0x58, 0x1e, 0xb5, 0xf7, - 0xac, 0x4e, 0xbe, 0x6d, 0x30, 0xb7, 0x9c, 0x6c, 0x83, 0x7b, 0xdf, 0x18, 0xdc, 0x7f, 0x67, 0x26, 0xe9, 0x2f, 0x66, - 0x56, 0x1a, 0xfb, 0x73, 0x4d, 0x37, 0x1c, 0x5b, 0xd7, 0x85, 0x7c, 0x65, 0xe6, 0xf6, 0xf7, 0x28, 0xda, 0xf0, 0x4c, - 0x87, 0xa8, 0x85, 0xe8, 0xd1, 0x02, 0xb6, 0x72, 0x2f, 0x97, 0x93, 0x09, 0x8b, 0x35, 0x11, 0x96, 0x11, 0xe2, 0xc2, - 0x92, 0x31, 0x20, 0xf8, 0x39, 0x7e, 0xf0, 0xd9, 0x0a, 0xf2, 0x3f, 0x15, 0x61, 0xd5, 0xc1, 0xd7, 0x93, 0xcc, 0xc9, - 0x21, 0xb7, 0x5c, 0xda, 0x6e, 0x69, 0xe3, 0x67, 0x07, 0xc6, 0x0c, 0x82, 0x31, 0x15, 0xee, 0xf1, 0x18, 0xe7, 0xcf, - 0x0f, 0xd3, 0x0e, 0x7e, 0x01, 0x3a, 0x80, 0xc3, 0x1b, 0xb8, 0xb9, 0x5f, 0x94, 0x32, 0xca, 0x3b, 0x9c, 0xb9, 0xfd, - 0xe0, 0xb9, 0x4b, 0x7a, 0x1e, 0xb4, 0xdb, 0x7b, 0x35, 0xf3, 0xe2, 0x57, 0xd1, 0x98, 0x21, 0xa0, 0xc3, 0x34, 0x02, - 0x6f, 0x4d, 0x29, 0x0c, 0x0f, 0x46, 0xe1, 0x31, 0x4b, 0x91, 0x79, 0xf6, 0xa1, 0xe8, 0x5a, 0x2e, 0x72, 0x9f, 0x3f, - 0xde, 0x37, 0xe0, 0xa4, 0xd5, 0xaf, 0xb4, 0x58, 0x34, 0xbe, 0xd4, 0xb5, 0xaf, 0xe4, 0xdd, 0xfa, 0xca, 0x8b, 0x63, - 0x9f, 0xc5, 0x8a, 0xf6, 0xdd, 0xaf, 0xba, 0xbc, 0x69, 0x4b, 0x0a, 0x1d, 0xae, 0x65, 0x56, 0x30, 0x1a, 0xdd, 0xc4, - 0x67, 0xc1, 0xd8, 0x55, 0x47, 0xd4, 0x30, 0x57, 0xde, 0xb4, 0x3b, 0xb6, 0x6d, 0x73, 0x85, 0xa9, 0x43, 0x3f, 0x41, - 0x61, 0x0a, 0x3f, 0xe1, 0xa1, 0x24, 0x5e, 0xec, 0x10, 0x17, 0xb1, 0x41, 0xce, 0x6a, 0x21, 0x7c, 0x47, 0xf1, 0x7c, - 0x1e, 0x02, 0x1b, 0x8f, 0xfb, 0x23, 0x40, 0x73, 0x04, 0x58, 0x05, 0x4c, 0x15, 0x80, 0x0e, 0x1f, 0x02, 0xd0, 0x85, - 0x3f, 0xf7, 0xc3, 0x69, 0xd2, 0x08, 0x11, 0xaa, 0x4d, 0x4b, 0xf0, 0xa4, 0xd4, 0x42, 0x55, 0x70, 0x0d, 0x67, 0x51, - 0x00, 0x79, 0x88, 0x54, 0x66, 0x4d, 0x2d, 0xe5, 0x85, 0x6d, 0xdb, 0x86, 0x79, 0x00, 0x19, 0xff, 0x0e, 0x8f, 0x6c, - 0xc3, 0x84, 0xbf, 0x2c, 0xcb, 0xaa, 0x91, 0xc7, 0xf6, 0xe6, 0x7e, 0x68, 0xd2, 0x63, 0xcb, 0xde, 0x0d, 0xde, 0x7b, - 0xad, 0x7a, 0x13, 0xae, 0x1b, 0x1b, 0xe6, 0xae, 0xa3, 0xda, 0xd0, 0x4d, 0xca, 0xb6, 0x6e, 0x16, 0x05, 0x5c, 0xe2, - 0xf1, 0x30, 0x2a, 0xc4, 0x68, 0x58, 0x7e, 0x8b, 0x6c, 0x69, 0x5c, 0xcd, 0x63, 0xa1, 0x7e, 0xcf, 0xc1, 0xea, 0x2a, - 0xaf, 0xa2, 0x65, 0x30, 0x46, 0x73, 0x28, 0xb0, 0x5d, 0x56, 0x0a, 0xab, 0xd0, 0x4a, 0xb2, 0x29, 0xc8, 0x25, 0x8e, - 0x89, 0xd6, 0xde, 0x23, 0x71, 0x8a, 0x62, 0xed, 0x29, 0x4e, 0xf1, 0x65, 0xdd, 0x16, 0xbc, 0x7a, 0x0a, 0xf1, 0x84, - 0x76, 0x68, 0xc0, 0xf7, 0x05, 0xd4, 0x0f, 0x76, 0xa9, 0x2f, 0xd6, 0xed, 0xea, 0x29, 0x05, 0x9d, 0xf5, 0x3e, 0x7d, - 0xda, 0x1b, 0x7d, 0xfa, 0xb4, 0xb7, 0x91, 0xa9, 0xa3, 0x79, 0x84, 0xb4, 0x31, 0x18, 0x0f, 0x31, 0x02, 0x71, 0x83, - 0x08, 0xe8, 0xef, 0xa1, 0xbc, 0xeb, 0xf1, 0x68, 0x55, 0xf4, 0x34, 0x32, 0xf8, 0x07, 0xe9, 0x31, 0xc8, 0x2a, 0x93, - 0x32, 0x73, 0x3d, 0x12, 0xf3, 0x7c, 0xfa, 0xc4, 0x8f, 0x9b, 0x31, 0x76, 0x47, 0x79, 0x91, 0xa3, 0x1a, 0x4b, 0x37, - 0xc8, 0x1f, 0x55, 0x04, 0x79, 0xc9, 0x31, 0x66, 0x01, 0xf1, 0xca, 0x8b, 0x43, 0x19, 0xe0, 0x9f, 0x22, 0x85, 0x7f, - 0x56, 0xe1, 0x11, 0x51, 0xc7, 0xd5, 0xd5, 0x98, 0xb8, 0x4c, 0x5b, 0x12, 0x0e, 0x14, 0x96, 0x6e, 0x52, 0x07, 0x17, - 0x02, 0xdb, 0x63, 0x5a, 0x54, 0x31, 0x40, 0xf4, 0xaf, 0xc6, 0x93, 0x3b, 0x16, 0xc3, 0x7a, 0xe7, 0xad, 0xba, 0x4b, - 0xf1, 0x70, 0x46, 0x26, 0xf1, 0xdd, 0x49, 0xee, 0xb7, 0xbc, 0x20, 0xaf, 0xc3, 0xa9, 0xfb, 0x6d, 0xac, 0x2d, 0x8c, - 0xd4, 0x50, 0x05, 0x19, 0x51, 0x75, 0x63, 0x5e, 0x17, 0x60, 0xb5, 0x37, 0xe7, 0xe1, 0x66, 0x34, 0xb1, 0x15, 0xae, - 0x27, 0xe8, 0xab, 0x10, 0x8e, 0xee, 0x30, 0x80, 0x72, 0xf1, 0x9e, 0x40, 0xb9, 0xe6, 0xd9, 0xf7, 0xc6, 0xf2, 0x2b, - 0x58, 0x70, 0xd5, 0x98, 0xe8, 0x06, 0xf9, 0x00, 0x4c, 0xbf, 0xa4, 0xb9, 0x3f, 0xc5, 0x54, 0x9e, 0x4b, 0xe1, 0x5e, - 0x85, 0x03, 0xc0, 0x75, 0xc5, 0x01, 0xa0, 0x66, 0x3e, 0x95, 0x98, 0x25, 0x8b, 0x28, 0x84, 0xbb, 0xe2, 0x75, 0xe1, - 0xe1, 0x75, 0xbd, 0xe9, 0xe1, 0x55, 0xd3, 0x14, 0xdf, 0x50, 0x3b, 0x50, 0x49, 0x5f, 0xfc, 0x57, 0xc5, 0x42, 0x5f, - 0x90, 0x7a, 0xcc, 0x52, 0x7e, 0xd6, 0xe4, 0xd9, 0xfd, 0xfd, 0xfd, 0x9e, 0xdd, 0xe7, 0x3b, 0x79, 0x76, 0x7f, 0xff, - 0xc5, 0x3d, 0xbb, 0xcf, 0x64, 0xcf, 0x6e, 0x20, 0xc1, 0x67, 0x6c, 0x27, 0x47, 0x5a, 0xe1, 0xd2, 0x12, 0xad, 0x12, - 0xd7, 0xe1, 0x9a, 0xb5, 0x64, 0x34, 0x63, 0x60, 0xaa, 0xc0, 0x59, 0xdd, 0x20, 0x9a, 0x82, 0xbf, 0x6b, 0xb3, 0x47, - 0xeb, 0x97, 0xf2, 0x67, 0x0d, 0xa2, 0xa9, 0x2a, 0xe5, 0x69, 0x0b, 0x45, 0x9e, 0x36, 0x88, 0x4d, 0xf7, 0xb7, 0x5b, - 0xe7, 0xe5, 0xa5, 0xd3, 0x6b, 0x3b, 0x10, 0xe7, 0x14, 0xb4, 0xcf, 0x58, 0x60, 0xf7, 0xda, 0x6d, 0x28, 0x58, 0x49, - 0x05, 0x2d, 0x28, 0xf0, 0xa5, 0x82, 0x43, 0x28, 0x18, 0x49, 0x05, 0x47, 0x50, 0x30, 0x96, 0x0a, 0x8e, 0xa1, 0xe0, - 0x46, 0xcd, 0x2e, 0xc3, 0xdc, 0x6f, 0xfd, 0x58, 0xbf, 0x2a, 0xa5, 0xe8, 0xcc, 0x4d, 0x25, 0x44, 0x95, 0x63, 0x43, - 0xe4, 0x8b, 0x30, 0x0f, 0x74, 0xce, 0xa3, 0x0d, 0xbe, 0x1a, 0x00, 0xe6, 0x05, 0xcb, 0x11, 0x03, 0xec, 0x6e, 0xa8, - 0x66, 0x5b, 0xbc, 0x56, 0xbb, 0xb9, 0x9f, 0xb7, 0x6d, 0xb4, 0x84, 0xdf, 0x74, 0x17, 0xa3, 0x78, 0x88, 0xca, 0x87, - 0xcf, 0x67, 0x79, 0xf0, 0xe8, 0xa5, 0x5b, 0x04, 0xc3, 0x69, 0x43, 0x0a, 0x1d, 0xce, 0xab, 0x31, 0x0d, 0xec, 0x65, - 0x20, 0xd6, 0x89, 0x38, 0x45, 0xe2, 0x03, 0x0a, 0x3a, 0xc3, 0xf7, 0xbc, 0x82, 0x87, 0xe3, 0xa1, 0xd6, 0x09, 0xfa, - 0x79, 0x1e, 0xc1, 0x9a, 0x74, 0xa9, 0x4b, 0x23, 0xf5, 0xa6, 0xdd, 0x99, 0x41, 0x86, 0x54, 0xdd, 0x29, 0xa4, 0x24, - 0x39, 0x1d, 0x77, 0x17, 0xc6, 0x6a, 0xc6, 0xc2, 0xee, 0x84, 0xbb, 0x1d, 0xc2, 0xfa, 0x93, 0x27, 0xc9, 0x5c, 0x17, - 0x2e, 0x50, 0xb8, 0x27, 0x8a, 0xb7, 0x04, 0xa5, 0x99, 0x6f, 0xa5, 0xc2, 0x7b, 0x47, 0x93, 0x8d, 0xac, 0xbe, 0x84, - 0xaf, 0xc5, 0x6b, 0x36, 0x5c, 0x4e, 0x95, 0xf3, 0x68, 0x7a, 0xaf, 0x5f, 0x85, 0xfc, 0x0a, 0xa0, 0x54, 0xc9, 0x9a, - 0xd4, 0x14, 0xdb, 0x9b, 0x7f, 0x8b, 0x1e, 0xb3, 0x72, 0xfd, 0x14, 0x60, 0x53, 0x52, 0x62, 0x1b, 0xe0, 0x3b, 0x30, - 0xdb, 0x92, 0xe7, 0xc2, 0x39, 0xcc, 0x9f, 0xf4, 0x7c, 0xe1, 0x49, 0xf0, 0xf4, 0x7f, 0x64, 0x49, 0xe2, 0x4d, 0x99, - 0x8c, 0x5a, 0x4a, 0x9d, 0x03, 0x16, 0xcc, 0xd5, 0xc9, 0x38, 0x81, 0xc0, 0xd8, 0xfb, 0x1b, 0xfe, 0x28, 0xe0, 0x32, - 0x0b, 0x7e, 0x5a, 0xb0, 0x68, 0x85, 0xf3, 0x86, 0x6f, 0xc1, 0xf2, 0x94, 0xfd, 0x28, 0x00, 0x89, 0xdc, 0xb0, 0xa0, - 0x5a, 0x98, 0x7a, 0xd3, 0x6a, 0x11, 0xad, 0x75, 0x56, 0x42, 0x7b, 0x7a, 0xe9, 0x51, 0xe0, 0xc2, 0xcf, 0xb0, 0xcb, - 0x0f, 0xa2, 0xe9, 0xef, 0x6a, 0x94, 0xbf, 0xc5, 0x99, 0xe2, 0xc7, 0xd0, 0x08, 0xd3, 0x81, 0x85, 0x73, 0xac, 0x58, - 0x30, 0x85, 0xdd, 0x30, 0x9d, 0x99, 0x18, 0x58, 0x4e, 0x6b, 0x85, 0xba, 0x61, 0xe1, 0xda, 0xae, 0xab, 0xe1, 0x34, - 0xbb, 0xf1, 0x74, 0xe8, 0x69, 0x4e, 0xeb, 0xd8, 0x10, 0x7f, 0x2c, 0xfb, 0x50, 0xcf, 0xb0, 0x07, 0x65, 0xec, 0xdf, - 0xac, 0x27, 0x51, 0x98, 0x9a, 0x13, 0x6f, 0xee, 0x07, 0x77, 0xdd, 0x79, 0x14, 0x46, 0xc9, 0xc2, 0x1b, 0xb1, 0x9e, - 0xc4, 0x8f, 0x62, 0xa0, 0x66, 0x1e, 0x2b, 0xd0, 0xb1, 0x5a, 0x31, 0x9b, 0x53, 0xeb, 0x3c, 0x0e, 0xf3, 0x24, 0x60, - 0xb7, 0x19, 0xff, 0x7c, 0xa9, 0x32, 0x55, 0xc5, 0x2d, 0x47, 0x2d, 0x80, 0x65, 0xe6, 0x41, 0x9e, 0x21, 0xb5, 0x41, - 0x8f, 0x4b, 0x1d, 0xbb, 0x56, 0xeb, 0x30, 0x66, 0x73, 0xc5, 0x3a, 0x6c, 0xec, 0x3c, 0x8e, 0x56, 0x7d, 0x80, 0x16, - 0x1b, 0x9b, 0x09, 0x0b, 0x26, 0xf8, 0xc6, 0xc4, 0xb8, 0x52, 0xa2, 0x1f, 0x13, 0xed, 0x0a, 0xa0, 0x37, 0x36, 0xef, - 0xc1, 0xeb, 0x6e, 0x4b, 0xb1, 0x25, 0x7e, 0xfa, 0xd8, 0x5e, 0x48, 0x7d, 0xc9, 0xf3, 0xa7, 0xaf, 0xb1, 0xba, 0xa3, - 0xd8, 0x3d, 0xd0, 0x1f, 0x4f, 0x82, 0x68, 0xd5, 0x9d, 0xf9, 0xe3, 0x31, 0x0b, 0x7b, 0x08, 0x73, 0x5e, 0xc8, 0x82, - 0xc0, 0x5f, 0x24, 0x7e, 0xd2, 0x9b, 0x7b, 0xb7, 0xbc, 0xd7, 0x83, 0xa6, 0x5e, 0xdb, 0xbc, 0xd7, 0xf6, 0xce, 0xbd, - 0x4a, 0xdd, 0x40, 0x0c, 0x2b, 0xea, 0x87, 0x83, 0x76, 0xa8, 0xd8, 0x95, 0x71, 0xee, 0xdc, 0xeb, 0x22, 0x66, 0xeb, - 0xb9, 0x17, 0x4f, 0xfd, 0xb0, 0x6b, 0x67, 0xd6, 0xcd, 0x9a, 0x36, 0xc6, 0xa3, 0x4e, 0xa7, 0x93, 0x59, 0x63, 0xf1, - 0x64, 0x8f, 0xc7, 0x99, 0x35, 0x12, 0x4f, 0x93, 0x89, 0x6d, 0x4f, 0x26, 0x99, 0xe5, 0x8b, 0x82, 0x76, 0x6b, 0x34, - 0x6e, 0xb7, 0x32, 0x6b, 0x25, 0xd5, 0xc8, 0x2c, 0xc6, 0x9f, 0x62, 0x36, 0xee, 0xe1, 0x46, 0xe2, 0xfe, 0xcf, 0xc7, - 0xb6, 0x9d, 0x21, 0x06, 0xb8, 0x2c, 0xe1, 0x26, 0x34, 0x5d, 0xb9, 0x5a, 0xef, 0x5c, 0x53, 0x29, 0x3e, 0x37, 0x1a, - 0xd5, 0xd6, 0x1b, 0x7b, 0xf1, 0xc7, 0x2b, 0x45, 0x1a, 0x85, 0xe7, 0x51, 0xb5, 0xb5, 0x98, 0x06, 0xf3, 0xb6, 0x0b, - 0x09, 0x3b, 0x7a, 0xc3, 0x28, 0x86, 0x33, 0x1b, 0x7b, 0x63, 0x7f, 0x99, 0x74, 0x9d, 0xd6, 0xe2, 0x56, 0x14, 0xf1, - 0xbd, 0x5e, 0x14, 0xe0, 0xd9, 0xeb, 0x26, 0x51, 0xe0, 0x8f, 0x45, 0x51, 0xd3, 0x59, 0x72, 0x5a, 0x7a, 0x0f, 0xf9, - 0x57, 0x1f, 0x83, 0x2e, 0x7b, 0x41, 0xa0, 0x58, 0xed, 0x44, 0x61, 0x5e, 0x82, 0xe6, 0x72, 0x8a, 0x9d, 0xd0, 0xbc, - 0x60, 0x68, 0x5a, 0xe7, 0x60, 0x71, 0x9b, 0xef, 0x79, 0xe7, 0x68, 0x71, 0x9b, 0x7d, 0x3d, 0x67, 0x63, 0xdf, 0x53, - 0xb4, 0x62, 0x37, 0x39, 0x36, 0x98, 0xd4, 0xe9, 0xeb, 0x86, 0x6d, 0x2a, 0x8e, 0x05, 0x24, 0x36, 0xda, 0xf3, 0xe7, - 0x20, 0x87, 0xf1, 0xc2, 0x34, 0xcb, 0x06, 0x57, 0x59, 0xd6, 0x3b, 0xf7, 0xb5, 0xcb, 0xff, 0xd6, 0x88, 0x16, 0x92, - 0x09, 0x6a, 0xa6, 0x5f, 0x19, 0x67, 0x4c, 0x76, 0x97, 0x01, 0x32, 0x86, 0xae, 0x32, 0x72, 0x65, 0xa2, 0xb7, 0x9b, - 0x95, 0x69, 0x92, 0xf3, 0xea, 0xe4, 0x7d, 0x53, 0xae, 0x82, 0x14, 0x08, 0x2a, 0x9c, 0x31, 0xf7, 0x5c, 0xf2, 0xbd, - 0x01, 0xa6, 0x07, 0x2b, 0x53, 0x54, 0xa1, 0xd7, 0x4d, 0xbc, 0xe7, 0xc5, 0xfd, 0xbc, 0xe7, 0x5f, 0xd3, 0x5d, 0x78, - 0xcf, 0x8b, 0x2f, 0xce, 0x7b, 0xbe, 0xde, 0x8c, 0x2a, 0x74, 0x11, 0xb9, 0x6a, 0x6e, 0x30, 0x09, 0xa4, 0x29, 0xa6, - 0x78, 0xfd, 0xaf, 0xd3, 0xdf, 0x1a, 0xde, 0x45, 0xf4, 0x86, 0x44, 0x81, 0xf3, 0xa9, 0x20, 0x66, 0x7d, 0x1b, 0xba, - 0x7f, 0x8e, 0xe5, 0xe7, 0xc9, 0xc4, 0x7d, 0x1d, 0x49, 0x05, 0xf9, 0x13, 0xf7, 0x25, 0x29, 0xc5, 0x56, 0xa6, 0x37, - 0xb9, 0xb7, 0x0f, 0x64, 0x9f, 0x86, 0xd0, 0xac, 0xe4, 0xda, 0x3d, 0xce, 0x7d, 0xee, 0x7a, 0x65, 0x10, 0xb4, 0xdc, - 0xc9, 0x55, 0x04, 0xe0, 0xda, 0xb0, 0x8c, 0x9a, 0x32, 0x21, 0x03, 0x78, 0x79, 0xf7, 0xfd, 0x58, 0xbb, 0x88, 0xf4, - 0xcc, 0x4f, 0xde, 0x56, 0xc3, 0x5f, 0x09, 0x3d, 0x97, 0x3c, 0x9c, 0x8c, 0xfb, 0xcd, 0x49, 0x51, 0x6e, 0xf1, 0x35, - 0x35, 0x3f, 0x2d, 0x8d, 0xb4, 0x2b, 0x37, 0xec, 0x51, 0xcc, 0xef, 0x0d, 0x62, 0xcc, 0xc3, 0xc4, 0xac, 0x39, 0x97, - 0xb7, 0xc6, 0x67, 0x88, 0x1a, 0x3a, 0xa6, 0xe6, 0xfe, 0x38, 0xcb, 0xf4, 0x9e, 0x98, 0x08, 0x89, 0xd0, 0xb2, 0xfb, - 0x98, 0xb8, 0xa4, 0x10, 0x02, 0x71, 0x89, 0x0f, 0x59, 0x33, 0x5f, 0x80, 0x7f, 0x00, 0xb7, 0x7d, 0xe6, 0x73, 0xa6, - 0x2a, 0x34, 0x7d, 0xe4, 0x37, 0x22, 0x0d, 0x08, 0x0c, 0xda, 0x65, 0x6f, 0xab, 0xd2, 0x82, 0x6c, 0x3a, 0xb6, 0xd2, - 0xe4, 0xa0, 0x83, 0x03, 0xc4, 0xf8, 0x15, 0x62, 0x21, 0x42, 0x3b, 0xbc, 0x0e, 0x3e, 0x64, 0x6a, 0xce, 0xfb, 0xe1, - 0xf6, 0xeb, 0x9f, 0xec, 0x43, 0x83, 0x7e, 0x45, 0xe9, 0x76, 0x8f, 0x5f, 0x26, 0xb0, 0x12, 0xc9, 0xca, 0xb0, 0x92, - 0x95, 0xf2, 0x6c, 0x2d, 0xe2, 0x63, 0xa7, 0xde, 0xc2, 0x04, 0x2d, 0x0f, 0xe2, 0x5e, 0x8e, 0xf1, 0xa4, 0x50, 0xdc, - 0xbd, 0x65, 0x02, 0xb8, 0x11, 0xe5, 0x28, 0x88, 0x7f, 0x7a, 0xa3, 0x65, 0x9c, 0x44, 0x71, 0x77, 0x11, 0xf9, 0x61, - 0xca, 0xe2, 0x8c, 0x04, 0x2b, 0x38, 0x3f, 0x62, 0x7a, 0xae, 0xd6, 0xd1, 0xc2, 0x1b, 0xf9, 0xe9, 0x5d, 0xd7, 0xe6, - 0x2c, 0x85, 0xdd, 0xe3, 0xdc, 0x81, 0x5d, 0x5b, 0xbf, 0xcb, 0x67, 0xf3, 0x39, 0x32, 0x7e, 0xf1, 0x26, 0x3b, 0x23, - 0x6f, 0xf3, 0x9e, 0xf4, 0x96, 0x22, 0x84, 0x03, 0xfb, 0xe1, 0xc5, 0xe6, 0x14, 0xb0, 0x3c, 0x2c, 0xb5, 0x3d, 0x66, - 0x53, 0x03, 0xb1, 0x36, 0x98, 0x19, 0x8a, 0x3f, 0xd6, 0xa1, 0xae, 0xd8, 0xf5, 0xc5, 0xc0, 0xf1, 0xe8, 0xbb, 0x40, - 0xd6, 0xf5, 0x26, 0x29, 0x8b, 0x8d, 0x5d, 0x6a, 0x0e, 0xd9, 0x24, 0x8a, 0x19, 0x65, 0x93, 0x73, 0x3a, 0x8b, 0xdb, - 0xdd, 0xbb, 0xdf, 0x3e, 0xfc, 0xfa, 0x7e, 0xc2, 0x28, 0xd5, 0x44, 0x67, 0xfa, 0x3d, 0xbd, 0x6d, 0xd2, 0x33, 0x60, - 0x0d, 0x69, 0xe6, 0x47, 0x24, 0x05, 0x81, 0x48, 0x60, 0xb5, 0x49, 0x3b, 0x16, 0x11, 0xa7, 0x79, 0x31, 0x0b, 0xbc, - 0xd4, 0xbf, 0x11, 0x3c, 0x63, 0xfb, 0x68, 0x71, 0x2b, 0xd6, 0x18, 0x09, 0xde, 0x03, 0x16, 0xa9, 0x02, 0x8a, 0x58, - 0xa4, 0x6a, 0x31, 0x2e, 0x52, 0x6f, 0x63, 0x34, 0x22, 0x8e, 0x75, 0x85, 0xd2, 0x1f, 0x2e, 0x6e, 0x65, 0x12, 0x5d, - 0x34, 0xcb, 0x29, 0x75, 0x35, 0x01, 0xc9, 0xdc, 0x1f, 0x8f, 0x03, 0x96, 0x95, 0x16, 0xba, 0xbc, 0x96, 0xd2, 0xe4, - 0xe4, 0xf3, 0xe0, 0x0d, 0x93, 0x28, 0x58, 0xa6, 0xac, 0x7e, 0xba, 0x84, 0x44, 0xb7, 0x98, 0x1c, 0xfc, 0x5d, 0x86, - 0xf5, 0x10, 0xd8, 0x6d, 0xd8, 0x26, 0x76, 0x0f, 0xf2, 0x0d, 0x9a, 0xed, 0x32, 0xe8, 0xf0, 0x2a, 0x07, 0xda, 0xa8, - 0x19, 0x88, 0x01, 0x64, 0x89, 0xb0, 0xb7, 0x62, 0x39, 0xbc, 0x2c, 0xcf, 0xb9, 0x96, 0x17, 0x65, 0xe5, 0xc1, 0xfc, - 0x3e, 0x67, 0xec, 0x45, 0xfd, 0x19, 0x7b, 0x21, 0xce, 0xd8, 0xf6, 0x9d, 0xf9, 0x68, 0xe2, 0xc0, 0x7f, 0xbd, 0x62, - 0x40, 0x5d, 0x5b, 0x69, 0x2f, 0x6e, 0x15, 0x67, 0x71, 0xab, 0x98, 0xad, 0xc5, 0xad, 0x82, 0x5d, 0xa3, 0x7b, 0x8b, - 0x61, 0xb5, 0x74, 0xc3, 0x56, 0xa0, 0x10, 0xfe, 0xd8, 0xa5, 0x57, 0xce, 0x01, 0xbc, 0x83, 0x56, 0x87, 0x9b, 0xef, - 0x5a, 0xdb, 0x8f, 0x3a, 0x9d, 0x25, 0x81, 0xb4, 0x75, 0x2b, 0xf5, 0x86, 0x43, 0x10, 0x65, 0x46, 0xa3, 0x65, 0xf2, - 0x0f, 0x0e, 0x3f, 0x9f, 0xc4, 0xad, 0x88, 0xa0, 0xd2, 0x8f, 0x68, 0x0a, 0x8a, 0xc2, 0x1b, 0x26, 0x7a, 0x58, 0xe7, - 0xeb, 0xd4, 0xa5, 0xe4, 0x88, 0x2d, 0xeb, 0xa0, 0x66, 0x93, 0xd7, 0x4f, 0xf4, 0xef, 0xb6, 0x4a, 0xcd, 0x28, 0xe6, - 0x33, 0xa6, 0x65, 0xeb, 0x74, 0x3c, 0x7c, 0x36, 0xf8, 0x6a, 0xda, 0x9d, 0x7a, 0x70, 0x2f, 0xc5, 0x97, 0xae, 0x04, - 0x51, 0xe1, 0x74, 0x8b, 0x87, 0xe2, 0xd8, 0xde, 0x6b, 0xd3, 0x1e, 0xd9, 0xe8, 0x75, 0x0b, 0x41, 0x28, 0xea, 0xee, - 0x88, 0xe5, 0x1f, 0xbd, 0x38, 0x80, 0xff, 0x88, 0xab, 0xff, 0x6b, 0x5a, 0xc7, 0xa8, 0xbf, 0x4e, 0x4b, 0x8c, 0x3a, - 0xb1, 0x4a, 0xc8, 0x88, 0xef, 0x5e, 0x7f, 0x32, 0x79, 0x58, 0x83, 0x9d, 0x6b, 0x93, 0x67, 0x58, 0xb5, 0xf6, 0xcb, - 0x28, 0x0a, 0x98, 0x17, 0x6e, 0x56, 0x17, 0xd3, 0x43, 0x6e, 0xfe, 0xa9, 0x0b, 0x8d, 0xc4, 0x3d, 0x82, 0x9c, 0x12, - 0x54, 0x6c, 0x43, 0x57, 0x89, 0xf3, 0xa6, 0xab, 0xc4, 0xbb, 0xfb, 0xaf, 0x12, 0x3f, 0xec, 0x74, 0x95, 0x78, 0xf7, - 0xc5, 0xaf, 0x12, 0xe7, 0x9b, 0x57, 0x89, 0xf3, 0x48, 0xb8, 0x03, 0x1b, 0x6f, 0x96, 0xfc, 0xe7, 0x07, 0xb2, 0xf7, - 0x7d, 0x17, 0xb9, 0x87, 0x36, 0x25, 0x3c, 0xbc, 0xf8, 0xcd, 0x17, 0x0b, 0xdc, 0x88, 0xef, 0xd0, 0x3b, 0xae, 0xb8, - 0x5a, 0x70, 0xcc, 0x8e, 0xdf, 0x91, 0x8a, 0x83, 0x28, 0x9c, 0xfe, 0x0c, 0xf6, 0xde, 0x20, 0x0e, 0x8c, 0xa5, 0x17, - 0x7e, 0xf2, 0x73, 0xb4, 0x58, 0x2e, 0x50, 0x51, 0xf5, 0xc1, 0x4f, 0xfc, 0x61, 0xc0, 0xf2, 0x08, 0x93, 0xa4, 0x75, - 0xe5, 0xb2, 0x75, 0x50, 0xbc, 0x8a, 0x9f, 0xde, 0xad, 0xf8, 0x89, 0x2e, 0xb6, 0xfc, 0x37, 0xb9, 0x09, 0xaa, 0xf5, - 0x17, 0x11, 0x61, 0x21, 0x26, 0x01, 0xfd, 0xf0, 0xcb, 0xc8, 0xb9, 0x88, 0xe5, 0x55, 0x1a, 0xa5, 0x70, 0xdf, 0x68, - 0xec, 0x87, 0x55, 0xfb, 0x79, 0xb3, 0xd4, 0x8d, 0x3c, 0x01, 0xc7, 0xa6, 0x38, 0x7f, 0x1e, 0x2d, 0x13, 0x36, 0x8e, - 0x56, 0xa1, 0x6a, 0x84, 0x5c, 0xaf, 0x1a, 0xa1, 0x4c, 0x3d, 0x6f, 0x53, 0x56, 0x38, 0xaa, 0xd6, 0x02, 0xe6, 0xd0, - 0x24, 0x0d, 0xb6, 0x89, 0x43, 0x54, 0x45, 0xc8, 0xa6, 0xde, 0x9e, 0xa6, 0x45, 0xee, 0xc3, 0x5a, 0x0a, 0xcf, 0x93, - 0xc8, 0xe2, 0x52, 0xe1, 0x44, 0x0b, 0x85, 0x70, 0x51, 0x44, 0xc1, 0xae, 0x59, 0x38, 0xfe, 0x86, 0x22, 0x44, 0x16, - 0x6f, 0x41, 0x57, 0x95, 0x2d, 0xf9, 0x7a, 0xf0, 0x98, 0xd0, 0xf4, 0xf8, 0x4a, 0x9a, 0xc6, 0xb7, 0x37, 0x2c, 0x0e, - 0xbc, 0x3b, 0x4d, 0xcf, 0xa2, 0xf0, 0x47, 0x98, 0x80, 0xd7, 0xd1, 0x2a, 0x94, 0x2b, 0x60, 0xaa, 0xf6, 0x9a, 0xbd, - 0x54, 0x1b, 0xbd, 0x1c, 0x62, 0x76, 0x48, 0x10, 0xf8, 0xd6, 0xc2, 0x9b, 0xb2, 0xff, 0x32, 0xe8, 0xdf, 0xff, 0xd6, - 0x33, 0xe3, 0x5d, 0x94, 0x7f, 0xe8, 0x97, 0xc5, 0x0e, 0x9f, 0x79, 0xf2, 0x64, 0xaf, 0x79, 0xd8, 0xda, 0x28, 0x60, - 0x5e, 0x2c, 0xa0, 0xa8, 0x69, 0xad, 0x37, 0x9e, 0x02, 0x80, 0xe2, 0x22, 0x5a, 0x8e, 0x66, 0xe8, 0xb7, 0xfb, 0xe5, - 0xc6, 0x9b, 0x42, 0x9f, 0x2c, 0xb9, 0xb4, 0xaf, 0xf2, 0xa1, 0x57, 0x8a, 0x8a, 0x59, 0xc0, 0xef, 0x9f, 0x41, 0xfa, - 0xad, 0x7f, 0xe3, 0x34, 0x6c, 0xee, 0x9a, 0x3c, 0xe4, 0xd7, 0x83, 0x36, 0x6f, 0xcf, 0x87, 0xa8, 0x3c, 0x14, 0xd8, - 0x5a, 0x28, 0xe9, 0xea, 0x91, 0x4c, 0x56, 0x9d, 0x34, 0x39, 0x89, 0x4c, 0x53, 0x7e, 0x1c, 0xf1, 0x15, 0x66, 0x95, - 0xac, 0x46, 0x0c, 0xc6, 0xb1, 0x55, 0x05, 0xc9, 0x70, 0x6f, 0x0a, 0x86, 0xe8, 0xab, 0xfa, 0x6e, 0xee, 0x87, 0x06, - 0xe6, 0x80, 0xdd, 0x7c, 0xe3, 0xdd, 0x42, 0x16, 0x44, 0x40, 0x6e, 0xd5, 0x57, 0x50, 0x68, 0xc8, 0xd1, 0x82, 0xbc, - 0xf1, 0x58, 0x53, 0x6b, 0x67, 0x42, 0x68, 0x03, 0x07, 0x5f, 0x29, 0x8a, 0xa2, 0xe4, 0xd7, 0x08, 0x25, 0xbf, 0x47, - 0x60, 0x39, 0x5e, 0x07, 0x40, 0x5b, 0x92, 0x2d, 0x6e, 0xa9, 0x04, 0x6e, 0x06, 0x68, 0x3f, 0x2d, 0x0a, 0x78, 0xa2, - 0x1f, 0x30, 0x6e, 0xa1, 0x02, 0x71, 0xa1, 0x07, 0xd5, 0xb7, 0x17, 0x43, 0x3e, 0xc0, 0xae, 0x82, 0x17, 0x76, 0x7c, - 0xcb, 0x25, 0xc1, 0x8a, 0x4d, 0x8f, 0x83, 0x1e, 0xab, 0xcf, 0x08, 0x13, 0x4a, 0x58, 0x10, 0xb4, 0x0e, 0x95, 0x04, - 0x8f, 0x06, 0xab, 0xc1, 0x8d, 0x78, 0x2f, 0xba, 0x4d, 0xe7, 0x2c, 0x5c, 0xaa, 0x06, 0x58, 0x9d, 0x60, 0x86, 0x1e, - 0xa8, 0xf3, 0x9a, 0x98, 0x2d, 0xc0, 0x36, 0xf5, 0x2d, 0x67, 0x44, 0x0b, 0x85, 0xa9, 0x8a, 0x67, 0x8c, 0x78, 0x00, - 0x9c, 0x84, 0xe3, 0xb6, 0x2a, 0x85, 0xe0, 0x4b, 0x1a, 0x95, 0xb1, 0x39, 0x0f, 0x79, 0x85, 0x9c, 0x02, 0xd9, 0x88, - 0x71, 0x71, 0x91, 0x98, 0x76, 0xcd, 0xab, 0x2e, 0x5a, 0xae, 0x91, 0xf1, 0x2a, 0x82, 0xa2, 0x58, 0xdf, 0xec, 0x86, - 0xc3, 0x09, 0x69, 0x09, 0x1a, 0xfb, 0x19, 0x6d, 0xf4, 0xd3, 0x30, 0xe8, 0x8f, 0xec, 0x8e, 0x08, 0x09, 0x4d, 0xd5, - 0x47, 0x76, 0x07, 0xc6, 0xe1, 0x67, 0x20, 0x4d, 0x51, 0xb7, 0xa0, 0x6b, 0x03, 0x12, 0xfd, 0x8e, 0x20, 0x55, 0xc5, - 0x96, 0x03, 0x64, 0x67, 0x5b, 0xb0, 0x38, 0x85, 0x23, 0x35, 0x92, 0x9e, 0x38, 0xc4, 0x3c, 0x62, 0x81, 0x56, 0x3b, - 0xc7, 0x66, 0xcd, 0xd1, 0xd0, 0x9f, 0x39, 0xb6, 0xbd, 0xbf, 0x51, 0x1f, 0x04, 0xd9, 0x75, 0xb5, 0x75, 0x23, 0x75, - 0x1d, 0xdb, 0xf4, 0x9f, 0x59, 0xad, 0xde, 0x06, 0x8d, 0x96, 0x32, 0x49, 0x0d, 0x50, 0xfc, 0xd5, 0x7f, 0xbc, 0xd6, - 0x36, 0x0e, 0xa4, 0x5e, 0x8d, 0x00, 0x80, 0xb0, 0x65, 0x5c, 0xfe, 0x35, 0xd8, 0x24, 0xfd, 0x94, 0xc7, 0x8a, 0xb2, - 0x9a, 0x0f, 0x20, 0x17, 0xa2, 0x06, 0xc7, 0xe8, 0x4f, 0xca, 0x73, 0x45, 0xa3, 0xe3, 0xa3, 0xeb, 0x83, 0x9e, 0xc0, - 0x28, 0x22, 0x44, 0x8e, 0xdc, 0x41, 0xe5, 0x8b, 0x49, 0x15, 0xc3, 0xf1, 0xac, 0x6b, 0xac, 0xd0, 0xe8, 0x6d, 0xe5, - 0x16, 0xb0, 0xff, 0x06, 0xf2, 0x69, 0x0d, 0x21, 0xc6, 0x23, 0xd4, 0x80, 0xcc, 0xa9, 0xf7, 0x76, 0x08, 0xe1, 0x79, - 0xe5, 0xee, 0xca, 0x44, 0x72, 0xf7, 0xce, 0x90, 0xe8, 0xa0, 0x0e, 0x2d, 0xef, 0xaf, 0x9e, 0xdc, 0x3d, 0xb0, 0x4b, - 0x16, 0x8e, 0xcb, 0x1d, 0x56, 0xe8, 0xd7, 0xee, 0xdd, 0x95, 0x30, 0x0a, 0xa4, 0x14, 0x8e, 0x6a, 0x30, 0x4a, 0x16, - 0x85, 0xb8, 0xf9, 0xe9, 0xb8, 0xf9, 0x3b, 0x71, 0x31, 0xd8, 0x80, 0xf2, 0x81, 0xe4, 0xcd, 0x24, 0xa1, 0x38, 0xe4, - 0xad, 0xc4, 0x08, 0x5a, 0x9a, 0x60, 0x44, 0x1b, 0x77, 0x62, 0x2a, 0xdc, 0x15, 0x8b, 0x36, 0x3e, 0xcf, 0x44, 0xb5, - 0xab, 0xd4, 0xda, 0xbf, 0x5f, 0x6a, 0x9d, 0xde, 0x27, 0xb5, 0xa6, 0xe8, 0x30, 0xdc, 0x1e, 0x54, 0x44, 0xc9, 0x11, - 0xcc, 0xb9, 0x1c, 0x67, 0xa8, 0x24, 0xea, 0xc6, 0x60, 0x32, 0x35, 0x56, 0xa4, 0xd4, 0x1b, 0x39, 0x20, 0xa2, 0xf8, - 0x5b, 0xba, 0xa0, 0x08, 0x85, 0xba, 0x2c, 0x1b, 0x3f, 0x2f, 0x64, 0xe3, 0x74, 0xab, 0x29, 0xe2, 0x82, 0x08, 0xee, - 0x5f, 0x8a, 0xb9, 0x93, 0xdf, 0x0e, 0x8a, 0xd8, 0x3b, 0x05, 0xa4, 0x52, 0x34, 0x99, 0xe2, 0xa2, 0x21, 0xc5, 0x28, - 0x12, 0xb7, 0x8c, 0x72, 0xa8, 0xa2, 0x72, 0xd5, 0x22, 0x98, 0x4c, 0x51, 0x0e, 0x52, 0x77, 0x04, 0x39, 0x2f, 0x96, - 0xb7, 0x4d, 0x39, 0x9a, 0x88, 0xfc, 0x5a, 0xda, 0x24, 0x79, 0xd8, 0x0f, 0x9a, 0x60, 0x21, 0xa6, 0xaf, 0xe8, 0xb5, - 0x73, 0x1b, 0x08, 0x04, 0xb2, 0x26, 0x4a, 0xd1, 0xfd, 0xd2, 0x79, 0xca, 0x96, 0x5c, 0xa8, 0xae, 0x1d, 0xa4, 0xee, - 0xa4, 0x09, 0x96, 0xe5, 0x11, 0x38, 0xd7, 0x57, 0x92, 0x04, 0xa1, 0x6b, 0x2b, 0x76, 0xaf, 0x86, 0x01, 0x40, 0xfa, - 0x5f, 0x7d, 0xe6, 0xac, 0x00, 0x48, 0x22, 0x15, 0x5b, 0xd6, 0xf9, 0xe3, 0x21, 0x36, 0xc9, 0x92, 0x1d, 0xab, 0x6e, - 0x7e, 0x93, 0xe4, 0x3d, 0x6b, 0x1e, 0x13, 0xa4, 0x2c, 0xce, 0xe7, 0x35, 0xba, 0x02, 0x0e, 0xbe, 0xcb, 0xe2, 0x65, - 0x88, 0x49, 0x70, 0xcd, 0x34, 0xf6, 0x46, 0x1f, 0xd7, 0xd2, 0xf7, 0xb8, 0x48, 0x14, 0xc4, 0xc5, 0x65, 0xa5, 0x42, - 0xcf, 0xc3, 0x9c, 0x51, 0xac, 0x6b, 0xb5, 0x12, 0x49, 0x50, 0xd3, 0x7d, 0x64, 0xb7, 0xbd, 0x17, 0x93, 0x83, 0x8a, - 0xfc, 0xb4, 0x75, 0x58, 0x96, 0xae, 0xe7, 0x70, 0xcc, 0xa3, 0x5f, 0x79, 0xf4, 0xa4, 0x3f, 0xfe, 0xd3, 0x09, 0xff, - 0x66, 0x65, 0x8d, 0x3e, 0x07, 0x04, 0x68, 0x5f, 0x52, 0x4c, 0xcb, 0x6a, 0x9a, 0x8d, 0x92, 0x26, 0xb0, 0x26, 0x7e, - 0x10, 0x98, 0x01, 0xb8, 0x31, 0xac, 0x3f, 0x6b, 0x78, 0xd8, 0xcf, 0x12, 0xb2, 0x15, 0x7e, 0x46, 0x3f, 0xe5, 0x9d, - 0x92, 0xce, 0x96, 0xf3, 0xe1, 0x5a, 0x16, 0x94, 0x4b, 0xf2, 0xf3, 0x4d, 0x99, 0xb9, 0xfc, 0xd9, 0xc9, 0x64, 0x52, - 0x96, 0x1a, 0xdb, 0xca, 0x01, 0x4a, 0x7e, 0x1f, 0xd9, 0xb6, 0x5d, 0x9d, 0xdf, 0xa6, 0x83, 0x42, 0x07, 0xc3, 0x44, - 0x21, 0x7c, 0xe7, 0xfe, 0x3d, 0xf5, 0x07, 0x41, 0x4b, 0x5d, 0x35, 0x9d, 0x47, 0xda, 0x6a, 0xff, 0x11, 0xa0, 0x20, - 0x6a, 0xb8, 0xef, 0xf8, 0x6f, 0xee, 0x95, 0x2d, 0x3d, 0x55, 0x0f, 0xf0, 0xc3, 0x1a, 0xdf, 0xb3, 0xd7, 0x77, 0x68, - 0xda, 0xb4, 0xbd, 0x33, 0xab, 0x20, 0xbb, 0x25, 0x9b, 0xa5, 0x1e, 0x59, 0x2a, 0xf9, 0x29, 0x9b, 0x27, 0xdd, 0x11, - 0x43, 0x05, 0xa9, 0x25, 0x51, 0x5b, 0xb4, 0xea, 0x31, 0xa7, 0x60, 0xc7, 0xe5, 0x08, 0x3c, 0x6c, 0x2b, 0xa8, 0xac, - 0xda, 0xd0, 0xac, 0x89, 0x8f, 0x20, 0x15, 0x5b, 0x6f, 0x2a, 0x9c, 0x70, 0x9b, 0x1e, 0xda, 0x7f, 0x2a, 0xd5, 0x53, - 0x80, 0x3b, 0x5d, 0x0b, 0x6b, 0x13, 0x52, 0x9e, 0xe0, 0xdf, 0xb9, 0x72, 0xee, 0xc5, 0xe2, 0xb6, 0x6c, 0xdc, 0xd5, - 0x01, 0x75, 0x53, 0x41, 0xca, 0x08, 0xea, 0x3a, 0xd4, 0x97, 0x9b, 0x00, 0x4d, 0x64, 0xeb, 0x16, 0xb0, 0xa0, 0x11, - 0x53, 0x50, 0xd1, 0x11, 0xe6, 0xa0, 0xe2, 0x75, 0x16, 0x76, 0x5e, 0x21, 0xdf, 0xc7, 0x5f, 0x90, 0xa5, 0x1c, 0xd2, - 0x9d, 0xfc, 0xc9, 0x78, 0xde, 0x41, 0xe5, 0x5e, 0x69, 0xab, 0xa2, 0xa9, 0x0c, 0xee, 0x01, 0x71, 0x23, 0x55, 0x96, - 0x71, 0x60, 0x52, 0xe2, 0x7a, 0x4d, 0x5f, 0x6f, 0x8e, 0xbb, 0xb9, 0x7b, 0xe7, 0x10, 0xf4, 0x1a, 0x9b, 0x53, 0xb5, - 0x93, 0x6a, 0xaf, 0xaa, 0xc3, 0x16, 0x70, 0xc2, 0x0a, 0x80, 0xcf, 0xac, 0x82, 0x46, 0x43, 0x4a, 0x05, 0xf7, 0xd1, - 0xa0, 0xf3, 0xb7, 0x32, 0xb2, 0x16, 0xe3, 0xc4, 0xee, 0xea, 0xab, 0x50, 0xdf, 0x42, 0x33, 0x08, 0x73, 0xc7, 0xb1, - 0x13, 0x3e, 0x9b, 0xb0, 0x63, 0x64, 0x74, 0xe5, 0xe0, 0x0e, 0xc2, 0x53, 0x6a, 0x52, 0xf2, 0x13, 0x3a, 0xa5, 0xa8, - 0x4b, 0xf8, 0xa1, 0x56, 0x78, 0x7f, 0x51, 0x92, 0xc6, 0xf3, 0xa0, 0x13, 0x2d, 0x7d, 0xa7, 0xda, 0x73, 0x3f, 0xdc, - 0xbd, 0xae, 0x77, 0xbb, 0x73, 0x5d, 0x60, 0x0e, 0x77, 0xae, 0x0c, 0xdc, 0x25, 0x56, 0xbe, 0x48, 0xdd, 0x1f, 0x24, - 0xe5, 0x81, 0x1c, 0x30, 0x51, 0xc5, 0x56, 0x74, 0xa3, 0xff, 0x69, 0xe9, 0x0e, 0x4e, 0x4e, 0x6f, 0xe7, 0x81, 0x72, - 0xc3, 0xe2, 0x04, 0x12, 0x4a, 0xa8, 0x8e, 0x65, 0xab, 0x0a, 0x1a, 0xf4, 0xfb, 0xe1, 0xd4, 0x55, 0x7f, 0xb9, 0x78, - 0x63, 0x76, 0xd4, 0x53, 0x30, 0xc7, 0xb8, 0x99, 0x22, 0x8b, 0x7b, 0xee, 0xdd, 0xb1, 0xf8, 0xba, 0xc5, 0x3d, 0x7e, - 0x88, 0xb9, 0xc5, 0x32, 0xa5, 0xa5, 0xee, 0x90, 0x12, 0x5e, 0xb9, 0xf1, 0xd9, 0xea, 0x65, 0x74, 0xeb, 0xaa, 0x80, - 0x58, 0x9d, 0x56, 0x47, 0x71, 0x5a, 0x07, 0xd6, 0x51, 0x47, 0xed, 0x7f, 0xa5, 0x28, 0x27, 0x63, 0x36, 0x49, 0xfa, - 0x28, 0x8e, 0x39, 0x41, 0x7e, 0x90, 0x7e, 0x2b, 0x8a, 0x35, 0x0a, 0x12, 0xd3, 0x51, 0xd6, 0xfc, 0x51, 0x51, 0x00, - 0x19, 0x75, 0x95, 0x47, 0x93, 0xd6, 0xe4, 0x60, 0xf2, 0xa2, 0xc7, 0x8b, 0xb3, 0xaf, 0x4a, 0xd5, 0x0d, 0xfa, 0xb7, - 0x25, 0x35, 0x4b, 0xd2, 0x38, 0xfa, 0xc8, 0x38, 0x2f, 0xa9, 0xe4, 0x82, 0xa2, 0x6a, 0xd3, 0xd6, 0xe6, 0x97, 0x9c, - 0xce, 0x70, 0x34, 0x69, 0x15, 0xd5, 0x11, 0xc6, 0xfd, 0x1c, 0xc8, 0x93, 0x7d, 0x01, 0xfa, 0x89, 0x3c, 0x4d, 0x8e, - 0x59, 0x37, 0x51, 0x8e, 0xca, 0xc7, 0x38, 0x15, 0xe3, 0x3b, 0x81, 0x8c, 0x6b, 0x85, 0xf7, 0x62, 0x82, 0xcd, 0x5c, - 0xf5, 0x47, 0xa7, 0xd5, 0x31, 0x1c, 0xe7, 0xc8, 0x3a, 0xea, 0x8c, 0x6c, 0xe3, 0xc0, 0x3a, 0x30, 0xdb, 0xd6, 0x91, - 0xd1, 0x31, 0x3b, 0x46, 0xe7, 0xbb, 0xce, 0xc8, 0x3c, 0xb0, 0x0e, 0x0c, 0xdb, 0xec, 0x40, 0xa1, 0xd9, 0x31, 0x3b, - 0x37, 0xe6, 0x41, 0x67, 0x64, 0x63, 0x69, 0xcb, 0x3a, 0x3c, 0x34, 0x1d, 0xdb, 0x3a, 0x3c, 0x34, 0x0e, 0xad, 0xa3, - 0x23, 0xd3, 0x69, 0x5b, 0x47, 0x47, 0xe7, 0x87, 0x1d, 0xab, 0x0d, 0xef, 0xda, 0xed, 0x51, 0xdb, 0x72, 0x1c, 0x13, - 0xfe, 0x32, 0x3a, 0x56, 0x8b, 0x7e, 0x38, 0x8e, 0xd5, 0x76, 0x0c, 0x3b, 0x38, 0x6c, 0x59, 0x47, 0x2f, 0x0c, 0xfc, - 0x1b, 0xab, 0x19, 0xf8, 0x17, 0x74, 0x63, 0xbc, 0xb0, 0x5a, 0x47, 0xf4, 0x0b, 0x3b, 0xbc, 0x39, 0xe8, 0xfc, 0x55, - 0xdd, 0x6f, 0x1c, 0x83, 0x43, 0x63, 0xe8, 0x1c, 0x5a, 0xed, 0xb6, 0x71, 0xe0, 0x58, 0x9d, 0xf6, 0xcc, 0x3c, 0x68, - 0x59, 0x47, 0xc7, 0x23, 0xd3, 0xb1, 0x8e, 0x8f, 0x0d, 0xdb, 0x6c, 0x5b, 0x2d, 0xc3, 0xb1, 0x0e, 0xda, 0xf8, 0xa3, - 0x6d, 0xb5, 0x6e, 0x8e, 0x5f, 0x58, 0x47, 0x87, 0xb3, 0x23, 0xeb, 0xe0, 0xc3, 0x41, 0xc7, 0x6a, 0xb5, 0x67, 0xed, - 0x23, 0xab, 0x75, 0x7c, 0x73, 0x64, 0x1d, 0xcc, 0xcc, 0xd6, 0xd1, 0xd6, 0x96, 0x4e, 0xcb, 0x82, 0x39, 0xc2, 0xd7, - 0xf0, 0xc2, 0xe0, 0x2f, 0xe0, 0xcf, 0x0c, 0xdb, 0xfe, 0x81, 0xdd, 0x24, 0x9b, 0x4d, 0x5f, 0x58, 0x9d, 0xe3, 0x11, - 0x55, 0x87, 0x02, 0x53, 0xd4, 0x80, 0x26, 0x37, 0x26, 0x7d, 0x16, 0xbb, 0x33, 0x45, 0x47, 0xe2, 0x0f, 0xff, 0xd8, - 0x8d, 0x09, 0x1f, 0xa6, 0xef, 0xfe, 0x5b, 0xfb, 0xc9, 0x97, 0xfc, 0x64, 0x7f, 0x4a, 0x5b, 0x7f, 0xda, 0xff, 0xea, - 0x04, 0x0e, 0x77, 0x7f, 0x60, 0xfc, 0xda, 0xa4, 0x94, 0xfc, 0xfb, 0xfd, 0x4a, 0xc9, 0x97, 0xcb, 0x5d, 0x94, 0x92, - 0x7f, 0xff, 0xe2, 0x4a, 0xc9, 0x5f, 0xab, 0xbe, 0x35, 0x6f, 0xaa, 0x59, 0xa8, 0x7f, 0x58, 0x57, 0x45, 0x0e, 0x89, - 0xa7, 0x5d, 0xfe, 0xb4, 0xbc, 0x82, 0xf8, 0xf1, 0x6f, 0x22, 0xf7, 0xe5, 0xb2, 0x64, 0xf0, 0x19, 0x01, 0x8e, 0x7d, - 0x13, 0x11, 0x8e, 0xfd, 0xb0, 0x74, 0xc1, 0xca, 0x8c, 0xb3, 0x39, 0xfe, 0xd8, 0x9c, 0x79, 0xc1, 0x24, 0x67, 0x91, - 0xa0, 0xa4, 0x87, 0xc5, 0xe0, 0x37, 0x0f, 0xe4, 0x19, 0x6e, 0x32, 0xcb, 0x79, 0x98, 0x80, 0x45, 0x30, 0x58, 0x72, - 0x4c, 0xe2, 0xac, 0xd2, 0xd8, 0x12, 0x11, 0xf7, 0xaf, 0xb9, 0x47, 0x71, 0xe3, 0x7b, 0x34, 0x00, 0xae, 0xef, 0xdd, - 0xd9, 0xec, 0x57, 0x01, 0xcb, 0x3a, 0x61, 0x20, 0x0d, 0xdc, 0x7e, 0xdd, 0xfb, 0xb2, 0x19, 0x6e, 0xc5, 0xf0, 0xba, - 0x19, 0x52, 0x80, 0xa4, 0xda, 0xde, 0x29, 0x9b, 0xf1, 0xde, 0x37, 0xcc, 0x9a, 0xcf, 0x97, 0x9a, 0x6f, 0xb1, 0x21, - 0xce, 0x3b, 0xae, 0x4e, 0xd5, 0xba, 0xc4, 0xa7, 0xd5, 0x4f, 0x48, 0x71, 0x41, 0x2d, 0x0c, 0x8d, 0x0b, 0x4e, 0xd5, - 0x56, 0x90, 0xdf, 0xb1, 0xa5, 0x77, 0xa5, 0x3e, 0x65, 0xe3, 0xe4, 0x67, 0x6b, 0xbc, 0x57, 0xf8, 0xbf, 0x02, 0x27, - 0xca, 0x39, 0x9e, 0x61, 0x24, 0xcf, 0xf3, 0x5a, 0xea, 0x97, 0xa4, 0x11, 0xd9, 0xcc, 0x59, 0x6f, 0xf2, 0xa2, 0x8d, - 0x6e, 0x09, 0x0e, 0x9b, 0x0b, 0x2e, 0x08, 0x3f, 0x4f, 0x4e, 0x00, 0x19, 0x39, 0x6a, 0xa0, 0x9f, 0xc3, 0xb6, 0xce, - 0x44, 0xbd, 0x47, 0xb0, 0x89, 0xb9, 0x27, 0xa0, 0x22, 0x87, 0x34, 0x5d, 0x4f, 0x82, 0xc8, 0x4b, 0xbb, 0xc8, 0xa6, - 0x49, 0x2c, 0x6f, 0x0b, 0x3d, 0x16, 0x7a, 0x5b, 0x8c, 0xe9, 0xe4, 0x8e, 0x79, 0x27, 0xe8, 0xf9, 0xb0, 0xcd, 0xfe, - 0x2e, 0x77, 0x38, 0x5b, 0x97, 0xcc, 0x51, 0x9c, 0xc3, 0x63, 0xc3, 0x39, 0x32, 0xac, 0xe3, 0x43, 0x3d, 0x13, 0x07, - 0x4e, 0xee, 0xb2, 0x34, 0x21, 0xe0, 0x00, 0x91, 0x83, 0xe9, 0x87, 0x7e, 0xea, 0x7b, 0x41, 0x06, 0xfc, 0x70, 0xf9, - 0x92, 0xf2, 0xf7, 0x65, 0x92, 0xc2, 0x18, 0x05, 0xd3, 0x8b, 0xce, 0x1f, 0xe6, 0x90, 0xa5, 0x2b, 0xc6, 0xc2, 0x06, - 0xc3, 0x98, 0xaa, 0x2f, 0xc9, 0xef, 0x67, 0x59, 0x9f, 0x91, 0xd5, 0xda, 0x30, 0x0d, 0xf9, 0xfe, 0x10, 0x8e, 0x0f, - 0xd9, 0xc0, 0xf8, 0xae, 0x09, 0xe1, 0xfe, 0x72, 0x3f, 0xc2, 0x4d, 0xd9, 0x2e, 0x08, 0xf7, 0x97, 0x2f, 0x8e, 0x70, - 0xbf, 0x93, 0x11, 0x6e, 0xc9, 0x7f, 0xb0, 0xd0, 0x30, 0xbd, 0xc7, 0x67, 0x0d, 0x5c, 0x64, 0x9f, 0xab, 0xfb, 0xc4, - 0xc0, 0xab, 0x7a, 0x91, 0xbd, 0xf6, 0x2f, 0x4b, 0xd9, 0x82, 0x1a, 0x05, 0xa0, 0x98, 0xd7, 0xd1, 0x47, 0xd7, 0x65, - 0x1f, 0x5c, 0xdd, 0x44, 0x18, 0x06, 0xe8, 0xf3, 0xfb, 0x30, 0x0d, 0xac, 0x77, 0xfc, 0x1e, 0x09, 0x0a, 0xdd, 0x37, - 0x51, 0x3c, 0xf7, 0x30, 0xc5, 0x88, 0xaa, 0x83, 0x3b, 0x1d, 0x3c, 0xd8, 0x10, 0x08, 0x64, 0x14, 0x85, 0xe3, 0x5c, - 0x2b, 0xc9, 0xdc, 0x4b, 0xe2, 0xb8, 0xd5, 0x3b, 0xe6, 0xc5, 0xaa, 0x41, 0xaf, 0x61, 0x71, 0x9f, 0xb5, 0xed, 0x67, - 0xad, 0x83, 0x67, 0x47, 0x36, 0xfc, 0xef, 0xb0, 0x76, 0x66, 0xf0, 0x8a, 0xf3, 0x28, 0x4c, 0x67, 0x45, 0xcd, 0xa6, - 0x6a, 0x2b, 0xc6, 0x3e, 0x16, 0xb5, 0x8e, 0xeb, 0x2b, 0x8d, 0xbd, 0xbb, 0xa2, 0x4e, 0x6d, 0x8d, 0x59, 0xb4, 0x94, - 0xc0, 0xaa, 0x81, 0xc6, 0x0f, 0x97, 0x20, 0x67, 0x97, 0x6a, 0xc8, 0xaf, 0xf9, 0x70, 0x8b, 0x71, 0xb1, 0x76, 0x76, - 0x25, 0x72, 0x28, 0xa8, 0x3d, 0x91, 0x56, 0xef, 0xde, 0x19, 0xe4, 0x2a, 0x4a, 0x1b, 0x73, 0x4e, 0x61, 0x66, 0x43, - 0xc8, 0x38, 0xc5, 0xc4, 0x02, 0x79, 0xb4, 0x40, 0x69, 0xbc, 0x0c, 0x47, 0x1a, 0xfe, 0xf4, 0x86, 0x89, 0xe6, 0xef, - 0xc7, 0x16, 0xff, 0xb0, 0x8e, 0xab, 0xe6, 0xf5, 0xed, 0x22, 0xe9, 0x7c, 0x22, 0x56, 0xc5, 0x7b, 0x96, 0x1a, 0x31, - 0xea, 0xb1, 0x69, 0x69, 0x4d, 0xd7, 0x7b, 0x96, 0x37, 0x7c, 0x96, 0x1a, 0xe1, 0x73, 0xd0, 0x7d, 0xba, 0xf6, 0x93, - 0x27, 0x54, 0x6b, 0xcf, 0x15, 0xc3, 0x3a, 0x1d, 0x15, 0x99, 0x29, 0x14, 0x6f, 0x1a, 0x51, 0x72, 0x8a, 0xee, 0xc8, - 0x88, 0x9e, 0x3f, 0xef, 0xbb, 0x8e, 0x3e, 0x8c, 0x99, 0xf7, 0x31, 0x13, 0xe1, 0xbe, 0x43, 0xcc, 0x4f, 0x7b, 0xbe, - 0x9b, 0xa1, 0x91, 0x5e, 0xeb, 0x4a, 0xbb, 0x80, 0x3b, 0x93, 0x2d, 0xdc, 0x11, 0x38, 0xf6, 0x82, 0xdc, 0xf5, 0x64, - 0x50, 0xe0, 0x09, 0x83, 0x1f, 0x51, 0xe7, 0x7a, 0xe6, 0x25, 0x3f, 0x24, 0x51, 0xf8, 0xcb, 0x02, 0x82, 0x1f, 0x17, - 0x16, 0x45, 0xe2, 0x32, 0xd6, 0xb6, 0x6c, 0xcb, 0x56, 0xf3, 0xfe, 0x26, 0xfe, 0xd4, 0x5d, 0x47, 0xa9, 0xd7, 0xdd, - 0x73, 0x8c, 0x20, 0x9a, 0x82, 0x7b, 0x5d, 0xea, 0xa7, 0x01, 0xeb, 0xaa, 0x2a, 0xf8, 0xd9, 0xcd, 0xe9, 0xba, 0x9e, - 0x71, 0xa7, 0x07, 0x2f, 0x86, 0x6c, 0xe6, 0xf1, 0x9d, 0xf0, 0xd0, 0xc5, 0x18, 0xea, 0x3f, 0x02, 0x8d, 0xd4, 0x54, - 0x0d, 0x44, 0x06, 0x2c, 0x4e, 0x4c, 0xd9, 0x89, 0xa8, 0xab, 0x40, 0x1b, 0x5d, 0xe5, 0x63, 0x9b, 0xc4, 0xde, 0x1c, - 0xd2, 0xed, 0xae, 0x33, 0x83, 0x23, 0x60, 0x95, 0x63, 0x60, 0xc5, 0x79, 0x71, 0x64, 0x28, 0x2d, 0xc7, 0x50, 0x6c, - 0xc0, 0xc2, 0x6a, 0x66, 0xac, 0xb3, 0xab, 0xde, 0x7d, 0x76, 0x10, 0x84, 0x76, 0x1e, 0xd1, 0x38, 0xc8, 0x02, 0x82, - 0x6b, 0x98, 0x52, 0xca, 0x9d, 0xa3, 0x49, 0x89, 0x35, 0x7d, 0xd2, 0x85, 0x5e, 0xb0, 0xdb, 0x54, 0x07, 0x85, 0x92, - 0xa8, 0xe2, 0xeb, 0x6b, 0xf4, 0x23, 0xf6, 0x43, 0xc5, 0xff, 0xf4, 0x49, 0xf3, 0xc1, 0xc7, 0xc9, 0x95, 0xe6, 0x07, - 0x9e, 0xf5, 0xd2, 0x84, 0xf9, 0x85, 0xf6, 0x1e, 0x27, 0x0b, 0x1c, 0x10, 0xe1, 0xdf, 0xa2, 0x58, 0xfc, 0xe0, 0xd6, - 0x13, 0x56, 0xe0, 0x85, 0x53, 0xc0, 0x74, 0x5e, 0x38, 0xdd, 0xb0, 0xd2, 0x22, 0x57, 0xe8, 0x4a, 0x69, 0xd1, 0x55, - 0x61, 0x41, 0x95, 0xbc, 0xbc, 0xbb, 0xf0, 0xa6, 0x3f, 0x79, 0x73, 0xa6, 0xa9, 0x40, 0xfc, 0xd0, 0x73, 0xb7, 0x50, - 0xf0, 0x3e, 0x77, 0x9f, 0x9e, 0xcc, 0x59, 0xea, 0x91, 0x76, 0x08, 0xee, 0xc4, 0xc0, 0x25, 0x28, 0x9c, 0xfe, 0xf0, - 0x38, 0x18, 0x2e, 0xa5, 0xd8, 0x22, 0xf2, 0x61, 0x28, 0x9c, 0x7c, 0x99, 0x68, 0x08, 0xea, 0x3a, 0x06, 0xf9, 0x21, - 0x8c, 0x3c, 0x4c, 0xb3, 0xe3, 0x86, 0x91, 0xda, 0x7f, 0x9a, 0xbb, 0x6c, 0x36, 0x2d, 0x42, 0xe0, 0x87, 0x1f, 0x2f, - 0x63, 0x16, 0xfc, 0xc3, 0x7d, 0x0a, 0xf4, 0xfc, 0xe9, 0x95, 0xaa, 0xf7, 0x52, 0x6b, 0x16, 0xb3, 0x89, 0xfb, 0x14, - 0xee, 0xa9, 0x5d, 0xb4, 0x9a, 0x05, 0x66, 0xfe, 0xf9, 0xed, 0x3c, 0x30, 0xf0, 0xd6, 0x4f, 0xb0, 0xa8, 0xed, 0x56, - 0x11, 0xee, 0xbc, 0xbd, 0xd3, 0x5d, 0xbf, 0xcf, 0x2f, 0xf1, 0x70, 0x31, 0x5c, 0x97, 0xae, 0xde, 0x4e, 0x0f, 0xaf, - 0xd5, 0xc3, 0xc0, 0x1b, 0x7d, 0xec, 0xd1, 0x9b, 0xd2, 0x83, 0x09, 0x44, 0x7c, 0xe4, 0x2d, 0xba, 0x48, 0x75, 0xe5, - 0x42, 0x70, 0xaa, 0xa6, 0xd2, 0x9c, 0xe1, 0xab, 0xdd, 0xcb, 0xb8, 0x95, 0xd7, 0xf8, 0x65, 0xfc, 0xd4, 0x6a, 0xe6, - 0xa7, 0x4c, 0x7c, 0x0a, 0x1f, 0xb2, 0x4c, 0xdc, 0xdf, 0xe9, 0xe6, 0x8a, 0xf7, 0x6d, 0xab, 0xad, 0x38, 0x9d, 0xef, - 0x0e, 0x6f, 0x1c, 0x7b, 0xd6, 0x72, 0xac, 0xce, 0x07, 0xa7, 0x33, 0x6b, 0x5b, 0xc7, 0x81, 0xd9, 0xb6, 0x8e, 0xe1, - 0xcf, 0x87, 0x63, 0xab, 0x33, 0x33, 0x5b, 0xd6, 0xc1, 0x07, 0xa7, 0x15, 0x98, 0x1d, 0xeb, 0x18, 0xfe, 0x9c, 0x53, - 0x2b, 0xb8, 0x17, 0xd1, 0x35, 0xe8, 0x69, 0x09, 0x39, 0x48, 0xbf, 0x73, 0x55, 0xad, 0x51, 0xa2, 0x7a, 0x35, 0xea, - 0xde, 0x05, 0x06, 0x97, 0x10, 0x69, 0x6d, 0x30, 0xf4, 0x90, 0x16, 0xba, 0x8c, 0xd2, 0xcd, 0x0a, 0xc3, 0x37, 0xe1, - 0xa1, 0x5e, 0xe4, 0x3f, 0x95, 0x4e, 0x10, 0xaf, 0xdb, 0x4b, 0x68, 0xbb, 0x4b, 0x61, 0xe5, 0xb4, 0xca, 0xb1, 0x6b, - 0xc8, 0x7c, 0xac, 0x1b, 0xa0, 0x3a, 0x06, 0xc4, 0x54, 0x84, 0x83, 0xd2, 0x6a, 0xd1, 0x96, 0xc0, 0x66, 0x09, 0x4b, - 0xa9, 0x48, 0x13, 0x2d, 0x81, 0xd8, 0xe8, 0xba, 0x48, 0x58, 0x8c, 0x1d, 0xf3, 0x1a, 0x8c, 0x67, 0x56, 0xae, 0x7d, - 0xd5, 0xab, 0xe2, 0x4b, 0xf0, 0x8a, 0xb7, 0xc2, 0x68, 0x85, 0x56, 0x1f, 0xf7, 0xcd, 0x1d, 0xc6, 0x19, 0x60, 0xc2, - 0xe6, 0xac, 0x0c, 0x2c, 0x0f, 0x9d, 0x5d, 0xfd, 0xe0, 0x06, 0x82, 0x7e, 0xd0, 0x07, 0xa5, 0x44, 0xdd, 0x9f, 0xd5, - 0x0f, 0x8f, 0x62, 0x21, 0x27, 0xcb, 0x1c, 0xfb, 0x71, 0x0e, 0x9e, 0x44, 0x51, 0x9c, 0xfa, 0x4c, 0xa5, 0xba, 0x41, - 0xb1, 0x9c, 0x58, 0x7c, 0xe3, 0x05, 0x92, 0xdd, 0x9d, 0xd4, 0x72, 0x2f, 0x27, 0x54, 0x4f, 0x9e, 0x14, 0xc0, 0x99, - 0x15, 0xb8, 0x4f, 0x9c, 0x43, 0xe0, 0x12, 0x0e, 0x59, 0x7b, 0xab, 0x09, 0x28, 0x5d, 0xcc, 0xb6, 0xb9, 0x82, 0x17, - 0x89, 0x99, 0x84, 0x99, 0x97, 0x30, 0x30, 0x69, 0xb4, 0x43, 0xdd, 0x30, 0x2f, 0x81, 0xcc, 0x76, 0x95, 0x9b, 0x99, - 0xaa, 0xf7, 0x42, 0x61, 0x2d, 0x11, 0x6e, 0xc9, 0x49, 0xc7, 0xaf, 0x8e, 0x2a, 0x4c, 0xcd, 0x96, 0x71, 0xdc, 0xe3, - 0xcf, 0xfe, 0xef, 0x1e, 0x04, 0xfa, 0x96, 0x82, 0x79, 0x47, 0x05, 0x8b, 0x94, 0x7c, 0x0d, 0x73, 0x7a, 0x4f, 0x84, - 0x9e, 0x25, 0xa7, 0x2a, 0x14, 0xa9, 0x5d, 0x15, 0xfd, 0xd8, 0xd4, 0xdc, 0xb6, 0x35, 0xa7, 0x62, 0x45, 0x81, 0xe1, - 0x63, 0xfe, 0x4f, 0xe1, 0xe7, 0xaa, 0x3f, 0x79, 0xd2, 0x48, 0x1c, 0xc9, 0x96, 0x28, 0x61, 0xa9, 0xb8, 0x4f, 0x68, - 0xaa, 0x8c, 0x77, 0x55, 0x19, 0xf5, 0xe5, 0xfd, 0x22, 0x36, 0x13, 0x26, 0xb9, 0xb4, 0xf7, 0xf0, 0xe7, 0x90, 0x79, - 0xa9, 0xc5, 0x75, 0xbb, 0x9a, 0xc4, 0x74, 0x18, 0x80, 0x36, 0x32, 0x42, 0x21, 0xf9, 0x30, 0x07, 0x8f, 0xd7, 0x7f, - 0x59, 0xf2, 0x20, 0x14, 0xd0, 0xc7, 0xa7, 0x4f, 0x76, 0x11, 0x37, 0xf4, 0x6d, 0xea, 0x51, 0xdc, 0x36, 0x99, 0x17, - 0x88, 0x52, 0x8f, 0xec, 0x4f, 0x7c, 0x0c, 0xb5, 0x53, 0x1f, 0x41, 0x4c, 0x8a, 0x54, 0xd1, 0x7f, 0x7b, 0xf1, 0x8d, - 0xc2, 0x0f, 0x00, 0x59, 0x37, 0xe0, 0xc5, 0x8b, 0xe2, 0xe3, 0xb8, 0x14, 0x1f, 0x47, 0xe1, 0x99, 0x97, 0x21, 0x47, - 0x6c, 0xb6, 0x4f, 0x53, 0x88, 0x02, 0x73, 0xb2, 0xf9, 0x98, 0x2f, 0x83, 0xd4, 0x5f, 0x78, 0x71, 0xba, 0x8f, 0xc1, - 0x71, 0x30, 0xd8, 0x4e, 0x53, 0xfc, 0x0a, 0x32, 0x1b, 0x11, 0xd9, 0x4c, 0xd2, 0x50, 0xd8, 0x8d, 0x4c, 0xfc, 0x20, - 0x37, 0x1b, 0x11, 0x1f, 0xf0, 0x46, 0x23, 0xb6, 0x48, 0xdd, 0x52, 0x10, 0x9e, 0x68, 0x94, 0xb2, 0xd4, 0x4c, 0xd2, - 0x98, 0x79, 0x73, 0x35, 0x0f, 0xca, 0xb5, 0xd9, 0x5f, 0xb2, 0x1c, 0x42, 0x54, 0x21, 0x11, 0x1e, 0x8c, 0x06, 0x08, - 0x06, 0x1c, 0x00, 0x22, 0x04, 0xc5, 0xa1, 0x29, 0x3c, 0x8f, 0xa6, 0x95, 0x2d, 0x55, 0xb0, 0x54, 0xa7, 0x98, 0xd4, - 0x8c, 0x6e, 0x5e, 0x20, 0xdd, 0x1e, 0x45, 0xc1, 0x35, 0x8f, 0xb9, 0x91, 0x67, 0xc7, 0x51, 0xfb, 0x27, 0xfc, 0x3a, - 0xae, 0x60, 0xb8, 0x19, 0xf5, 0xd0, 0x86, 0xb4, 0x6d, 0x4d, 0xd1, 0x38, 0xf6, 0x79, 0x65, 0xa0, 0x99, 0xd4, 0x33, - 0x66, 0xde, 0x24, 0x58, 0x2e, 0x80, 0x64, 0x95, 0x0c, 0x7c, 0x66, 0x4e, 0x3f, 0x77, 0xff, 0x44, 0xa8, 0x90, 0xaa, - 0x7d, 0xfa, 0xf4, 0x7e, 0xf0, 0xaf, 0x7f, 0x42, 0x7a, 0xd0, 0x99, 0x23, 0x62, 0x60, 0x5c, 0xca, 0xb5, 0x38, 0x5b, - 0x6c, 0x0c, 0xd0, 0xb8, 0x8b, 0x8d, 0x45, 0x74, 0x42, 0xb1, 0xb7, 0xb2, 0xc1, 0x95, 0x88, 0xab, 0x07, 0x89, 0x85, - 0x75, 0x11, 0xa9, 0x63, 0x00, 0xcb, 0x3b, 0x10, 0x31, 0x5c, 0x94, 0xbf, 0xdd, 0xbe, 0x3c, 0x56, 0x8a, 0x70, 0x8f, - 0x75, 0x16, 0x48, 0xb4, 0x87, 0xfa, 0x27, 0x9e, 0x82, 0xdc, 0x14, 0xf2, 0x45, 0x49, 0x77, 0x1f, 0x86, 0x39, 0x8b, - 0xe6, 0xcc, 0xf2, 0xa3, 0xfd, 0x15, 0x1b, 0x9a, 0xde, 0xc2, 0x27, 0x3b, 0x22, 0x94, 0x13, 0x2a, 0xc4, 0x92, 0xe6, - 0xe6, 0x39, 0xc4, 0xf8, 0x67, 0xc5, 0x54, 0x46, 0x95, 0xc0, 0x6d, 0xad, 0x42, 0x6f, 0x79, 0xc0, 0x83, 0xa2, 0x89, - 0x9a, 0xfd, 0x93, 0x7d, 0xaf, 0x5f, 0xce, 0x94, 0x63, 0x89, 0x8c, 0xaf, 0x65, 0x2a, 0x70, 0x4a, 0x09, 0x6f, 0x44, - 0x6e, 0x9b, 0xe2, 0xc1, 0x8c, 0x26, 0x13, 0x39, 0xbb, 0x8d, 0x55, 0x06, 0x2f, 0x9f, 0xb4, 0x62, 0x4b, 0x47, 0x0b, - 0xfa, 0xd2, 0xe6, 0x27, 0xf2, 0x9f, 0x6a, 0x17, 0xd3, 0x5a, 0xc1, 0x98, 0xe1, 0xbc, 0x6f, 0x64, 0xc9, 0xc9, 0x67, - 0xec, 0x11, 0x55, 0xe2, 0x88, 0xa4, 0x9a, 0x93, 0xb1, 0x81, 0xa5, 0xda, 0x73, 0x5d, 0xc2, 0x73, 0x55, 0x74, 0x07, - 0x93, 0x58, 0x93, 0xfd, 0x16, 0x06, 0x9b, 0x42, 0x43, 0x93, 0xdc, 0x7b, 0xb1, 0x51, 0x75, 0x38, 0x9b, 0x30, 0xee, - 0x7b, 0x62, 0xfb, 0x95, 0x36, 0x28, 0x6c, 0x3c, 0xbe, 0xee, 0x80, 0xe0, 0x45, 0x3f, 0x15, 0x3c, 0xaf, 0x7c, 0x4d, - 0x28, 0xdd, 0x0c, 0xbc, 0xbb, 0x48, 0x32, 0xbb, 0xe2, 0x11, 0x58, 0xce, 0xb1, 0xf4, 0x42, 0x78, 0x3e, 0x6f, 0x1c, - 0x34, 0xa4, 0x61, 0x90, 0x25, 0x74, 0xf3, 0xb0, 0x15, 0x04, 0x38, 0x60, 0xf7, 0x9d, 0x35, 0xb9, 0x6e, 0x79, 0x30, - 0x88, 0x3c, 0xb3, 0xe2, 0x1c, 0x96, 0x5e, 0x22, 0x5a, 0xc8, 0x4e, 0xf6, 0x61, 0x7c, 0x94, 0xf7, 0x50, 0x30, 0x79, - 0xc2, 0xbe, 0x10, 0x6f, 0xbd, 0x7e, 0xd3, 0xad, 0xb7, 0xca, 0xa3, 0x94, 0x59, 0x2f, 0x5f, 0x87, 0xd8, 0x36, 0x5e, - 0x42, 0xb6, 0x67, 0xdf, 0x8f, 0x39, 0x61, 0x90, 0xbe, 0x92, 0x07, 0xc3, 0x2c, 0xd5, 0xd3, 0xb7, 0x06, 0x89, 0xa1, - 0x8c, 0xbb, 0x1f, 0x96, 0x98, 0x6e, 0x37, 0xeb, 0xa5, 0x4c, 0xc4, 0x6c, 0x38, 0x4f, 0x1b, 0xc2, 0x3a, 0x34, 0x55, - 0x21, 0x3e, 0x7c, 0x4b, 0x85, 0x62, 0x9b, 0x6f, 0xab, 0x55, 0x70, 0x56, 0x45, 0x35, 0x4f, 0x53, 0x1f, 0xe1, 0x81, - 0xd8, 0xa8, 0x8d, 0xa5, 0x18, 0x6c, 0x22, 0x75, 0xa1, 0xaa, 0x50, 0x2d, 0x78, 0x8b, 0x05, 0x55, 0xd6, 0x7b, 0x27, - 0xfb, 0x74, 0x9d, 0xee, 0xd3, 0x06, 0xec, 0x9f, 0x80, 0x65, 0x3a, 0xed, 0x09, 0x6f, 0xb1, 0xe0, 0x2b, 0x4e, 0xbf, - 0xe8, 0xcd, 0xfe, 0x2c, 0x9d, 0x07, 0xfd, 0xff, 0x05, 0x64, 0x23, 0xa6, 0xdb, 0x06, 0x7b, 0x03, 0x00}; + 0x88, 0xa6, 0xd7, 0x57, 0x61, 0xb7, 0x6c, 0xac, 0xd5, 0x01, 0x46, 0x82, 0x27, 0x31, 0x04, 0x0c, 0xcc, 0x8c, 0xa8, + 0xff, 0xdb, 0xb8, 0x8a, 0xfa, 0xd1, 0xfe, 0x2e, 0x47, 0xfe, 0x3f, 0xbf, 0x7d, 0x7f, 0x0e, 0x7a, 0x2d, 0x0f, 0x15, + 0xd1, 0x6b, 0x95, 0xdb, 0xb0, 0x98, 0xa0, 0x29, 0x52, 0x7b, 0xaa, 0xb7, 0x04, 0xea, 0x8c, 0x37, 0x86, 0xfd, 0x5b, + 0xf3, 0xe6, 0xe6, 0xc6, 0x04, 0x8b, 0x56, 0x73, 0x15, 0x07, 0xc4, 0x1d, 0x4e, 0xd4, 0x4c, 0x20, 0x75, 0x56, 0x41, + 0xea, 0x10, 0x0e, 0x97, 0xe7, 0x53, 0x79, 0x3f, 0x8f, 0x6e, 0xbe, 0x09, 0x02, 0x59, 0x6c, 0x23, 0x98, 0x38, 0x2e, + 0xc9, 0x28, 0x21, 0x03, 0x0d, 0xb4, 0x4f, 0x96, 0x9f, 0x5c, 0x71, 0x7b, 0x81, 0xc9, 0xd5, 0xe8, 0xee, 0x8a, 0xeb, + 0x24, 0xf2, 0x78, 0xc4, 0xef, 0x87, 0xc7, 0x13, 0xff, 0x5a, 0x41, 0x4e, 0xd3, 0x55, 0xc1, 0x99, 0x2b, 0x60, 0xa3, + 0x55, 0x9a, 0x46, 0xa1, 0x19, 0x47, 0x37, 0xea, 0xe0, 0x98, 0x1e, 0x44, 0x05, 0x8f, 0x1e, 0x55, 0xe5, 0xeb, 0x71, + 0xe0, 0x8f, 0x3f, 0xba, 0xea, 0xe3, 0xb5, 0xef, 0x0e, 0x2a, 0xfc, 0xa4, 0x9d, 0xa9, 0x03, 0x80, 0x55, 0xf9, 0x26, + 0x08, 0x8e, 0xf7, 0xe9, 0x8b, 0xc1, 0xf1, 0xfe, 0xc4, 0xbf, 0x1e, 0x48, 0xa9, 0x61, 0xb8, 0xde, 0xd4, 0xe5, 0x21, + 0x38, 0x73, 0x4b, 0xb3, 0x04, 0x63, 0x3a, 0x8c, 0x99, 0x56, 0x5c, 0x7e, 0x21, 0xd6, 0x0c, 0xc1, 0xab, 0x8d, 0x51, + 0x9c, 0x1e, 0xc0, 0x55, 0xef, 0xd3, 0x27, 0x2d, 0xb7, 0x43, 0x9d, 0x4b, 0x41, 0xda, 0x50, 0xcd, 0x87, 0x55, 0x0c, + 0x8c, 0x34, 0xa3, 0x6b, 0x22, 0x94, 0x5c, 0xa0, 0x1b, 0xe3, 0xcc, 0xc0, 0x0c, 0x3b, 0xde, 0x12, 0x34, 0x8e, 0xfc, + 0xa7, 0x74, 0x23, 0x1e, 0x43, 0x56, 0x6d, 0x09, 0x89, 0xeb, 0x92, 0xce, 0x85, 0x4e, 0x21, 0x8f, 0x13, 0x08, 0xca, + 0x12, 0xec, 0x87, 0xf4, 0x20, 0x5a, 0xa0, 0x43, 0x56, 0xb7, 0x3c, 0x38, 0x8f, 0x97, 0x89, 0x3c, 0x6a, 0x62, 0x5e, + 0x4e, 0x4a, 0x2b, 0xd4, 0xab, 0xae, 0x97, 0x88, 0x1a, 0xb9, 0x97, 0x34, 0x2d, 0x19, 0xe8, 0xf0, 0xb4, 0xd4, 0xa8, + 0xd0, 0x5c, 0xf0, 0xea, 0x93, 0x14, 0x47, 0xcc, 0xd0, 0x2e, 0x12, 0x23, 0xba, 0x2c, 0xe8, 0x54, 0x42, 0x88, 0xb2, + 0x17, 0x65, 0x45, 0x00, 0x67, 0x5a, 0xf5, 0xc1, 0xe3, 0x75, 0x88, 0x84, 0x2d, 0x71, 0x07, 0xe5, 0x7d, 0x90, 0x7a, + 0x23, 0x93, 0x36, 0xb3, 0xaa, 0x7c, 0x3d, 0x19, 0x05, 0xf9, 0x62, 0xd3, 0x21, 0x98, 0x7b, 0xe1, 0x24, 0x60, 0xe7, + 0xde, 0xe8, 0x3b, 0xac, 0xf3, 0x7a, 0x14, 0xbc, 0x82, 0x0a, 0x99, 0x3a, 0x78, 0xbc, 0x26, 0xd2, 0x5d, 0x87, 0xb0, + 0x33, 0xda, 0x02, 0xd5, 0x7e, 0x78, 0xca, 0x25, 0x16, 0xd3, 0xd7, 0x08, 0x2c, 0x91, 0x5b, 0x8a, 0x63, 0x5b, 0x86, + 0x8c, 0xa7, 0xfc, 0x81, 0xbd, 0xa9, 0xf0, 0x53, 0x0b, 0x70, 0x45, 0xe2, 0x04, 0xcb, 0x3b, 0x53, 0x06, 0x96, 0xc8, + 0xea, 0xbb, 0xe8, 0x46, 0x40, 0xca, 0x27, 0x80, 0x42, 0x54, 0x9e, 0xbc, 0x1f, 0x1e, 0xcb, 0x6a, 0x21, 0x94, 0x9d, + 0x53, 0xbb, 0xf0, 0x2b, 0x53, 0x95, 0x22, 0x01, 0xd4, 0xf2, 0x56, 0x1d, 0x1c, 0xef, 0xcb, 0xb5, 0x07, 0xc3, 0xde, + 0xa9, 0x34, 0x38, 0x6c, 0x55, 0xdc, 0x9b, 0x2f, 0x8a, 0x87, 0xec, 0x52, 0x81, 0x5b, 0x72, 0x06, 0x25, 0x30, 0x47, + 0xe5, 0x4f, 0x36, 0xc8, 0x0f, 0xa4, 0x4c, 0x2c, 0x08, 0x14, 0xed, 0x1e, 0x81, 0x1f, 0x23, 0xbd, 0x97, 0x2f, 0x21, + 0x59, 0x66, 0x8a, 0xd6, 0x86, 0xfc, 0xdf, 0x62, 0x4a, 0x50, 0xd2, 0xcd, 0xc2, 0x24, 0x8a, 0x55, 0x18, 0x66, 0x35, + 0x6f, 0x92, 0x22, 0xe5, 0x6b, 0xc3, 0x01, 0xd7, 0x92, 0x55, 0x98, 0xb0, 0xfd, 0xea, 0xa7, 0xd2, 0xb8, 0x87, 0x7a, + 0xf1, 0x43, 0xe1, 0x83, 0xa9, 0x20, 0xad, 0x1c, 0xc0, 0xe6, 0x7c, 0x54, 0x17, 0x8f, 0x7d, 0xe3, 0x2f, 0x91, 0x31, + 0xf2, 0x8c, 0x2b, 0xcf, 0xf8, 0x31, 0xbc, 0xcc, 0x6a, 0x17, 0x2f, 0xcf, 0x25, 0x67, 0xb0, 0xbe, 0x06, 0x11, 0x98, + 0xca, 0x97, 0x0a, 0xdf, 0xe2, 0x36, 0x23, 0xe7, 0x5e, 0x3c, 0x63, 0x22, 0x85, 0x9b, 0x78, 0x2b, 0x64, 0x07, 0xba, + 0x34, 0x2d, 0x10, 0x9e, 0x6c, 0x8f, 0x9b, 0xd6, 0xf9, 0xd6, 0x38, 0x8d, 0x83, 0x3f, 0xb3, 0x3b, 0x60, 0xb3, 0x92, + 0x34, 0x5a, 0x82, 0xcc, 0xca, 0x9b, 0x71, 0x1d, 0x84, 0xa1, 0xb1, 0xdd, 0xba, 0xfb, 0xf4, 0x89, 0x49, 0x59, 0xc5, + 0xd2, 0x68, 0x36, 0x0b, 0x98, 0x26, 0x65, 0x1f, 0xcb, 0xbb, 0x39, 0xd9, 0xb3, 0x45, 0xe4, 0x6a, 0x3d, 0x6b, 0x3a, + 0x58, 0x62, 0xc4, 0x2c, 0xe7, 0x06, 0x01, 0x71, 0x91, 0x71, 0x15, 0x32, 0xe4, 0x9a, 0x38, 0x17, 0xc5, 0xc1, 0x35, + 0x27, 0xd1, 0x6a, 0x14, 0x30, 0x13, 0x4f, 0x03, 0x74, 0xb9, 0x1e, 0xad, 0x46, 0xa3, 0x80, 0xd2, 0x85, 0x41, 0xfc, + 0xb5, 0x28, 0x41, 0xb9, 0x68, 0xa6, 0xf7, 0x61, 0x50, 0x56, 0x5a, 0x05, 0x1f, 0x6c, 0x26, 0xe1, 0xe6, 0x40, 0x1d, + 0xa4, 0x20, 0x03, 0xdd, 0x3c, 0xd3, 0xae, 0x0a, 0x37, 0x16, 0x96, 0xa8, 0xfd, 0x1a, 0x96, 0xce, 0xbd, 0x50, 0xdf, + 0xe3, 0x0c, 0x2b, 0x5e, 0x38, 0x51, 0x5e, 0xd1, 0xde, 0x55, 0x0d, 0x95, 0x4c, 0xbf, 0x78, 0x76, 0x39, 0xd5, 0x50, + 0x5f, 0xfb, 0xde, 0x2c, 0x8c, 0x92, 0xd4, 0x1f, 0xab, 0x97, 0xfd, 0xd7, 0xbe, 0x76, 0xb1, 0x48, 0x35, 0xfd, 0xd2, + 0xf8, 0x56, 0xce, 0x03, 0x26, 0x30, 0x25, 0xa6, 0x01, 0x6b, 0xa8, 0x23, 0x9f, 0x9e, 0x6d, 0xf5, 0x04, 0x46, 0xc6, + 0x3a, 0xdf, 0xba, 0x50, 0xab, 0x92, 0x51, 0x0c, 0x53, 0x45, 0x42, 0x46, 0xb1, 0x6f, 0xf5, 0x3e, 0x09, 0x61, 0xbe, + 0x59, 0xad, 0x91, 0x69, 0x48, 0x0b, 0xe2, 0x8b, 0x41, 0xf0, 0x85, 0xe7, 0x28, 0x3d, 0xef, 0xc9, 0x5e, 0x0f, 0x25, + 0x32, 0x3e, 0xfc, 0xa6, 0xcc, 0x81, 0x3c, 0x5e, 0xa7, 0x19, 0x98, 0x1c, 0x86, 0x51, 0xaa, 0x40, 0x64, 0x37, 0xe8, + 0x70, 0x58, 0xb5, 0x92, 0xe6, 0xad, 0x6a, 0x7a, 0xc6, 0xb1, 0xc0, 0x4b, 0xa4, 0xa5, 0x28, 0xb9, 0x84, 0x40, 0x14, + 0x10, 0xa4, 0xb4, 0x14, 0xc7, 0x89, 0xfb, 0xe6, 0xc1, 0xf2, 0x95, 0xf8, 0x37, 0x09, 0xef, 0x97, 0xe9, 0xf9, 0xe3, + 0x75, 0x72, 0x22, 0x88, 0xfa, 0xf7, 0x09, 0xae, 0x25, 0xb0, 0x2b, 0x9c, 0xca, 0x67, 0xaa, 0x72, 0x22, 0x28, 0x11, + 0xd6, 0x2d, 0xa1, 0x57, 0x4d, 0xb0, 0xbb, 0xb1, 0x88, 0x99, 0xcf, 0xc5, 0x28, 0x82, 0x01, 0xab, 0x1c, 0x3d, 0x08, + 0xd6, 0x94, 0xf3, 0x56, 0x29, 0x58, 0x5c, 0x23, 0xc1, 0x00, 0xcc, 0xc5, 0x79, 0x84, 0x61, 0x76, 0x05, 0x8c, 0x24, + 0x44, 0x30, 0x13, 0x63, 0x34, 0x22, 0x39, 0x89, 0x9c, 0x1f, 0x2e, 0x57, 0x29, 0x46, 0xa6, 0x07, 0x00, 0x58, 0xa6, + 0x2a, 0x78, 0x61, 0x04, 0x5c, 0x5f, 0x5c, 0x78, 0x32, 0x55, 0xf1, 0x27, 0x9b, 0x65, 0x5c, 0x3a, 0x03, 0x38, 0x0e, + 0x87, 0x81, 0x7a, 0x1d, 0x78, 0x8c, 0xf9, 0x30, 0xc6, 0x46, 0x91, 0xd6, 0x45, 0x1b, 0xa3, 0xfd, 0x43, 0x0d, 0x02, + 0x19, 0x53, 0x3b, 0x7d, 0x2d, 0xa8, 0x1d, 0x2c, 0x44, 0xab, 0x2e, 0x0d, 0x73, 0x08, 0x32, 0xca, 0x13, 0x98, 0x3b, + 0x17, 0x2e, 0xf5, 0xc2, 0xb4, 0x4e, 0x3d, 0x57, 0xc9, 0xae, 0x6e, 0x88, 0xd3, 0x30, 0xcc, 0xae, 0x0a, 0x47, 0xd7, + 0x62, 0xbc, 0xb0, 0x25, 0xa9, 0x5c, 0x41, 0x4b, 0x37, 0x97, 0xdb, 0xb3, 0x2d, 0x63, 0x7f, 0xe1, 0xc5, 0x77, 0x64, + 0xfe, 0x66, 0xc8, 0x36, 0x72, 0xba, 0xaa, 0x10, 0x3d, 0xa0, 0x09, 0x20, 0xd2, 0xa0, 0x2a, 0x5f, 0xe7, 0x65, 0x8c, + 0x8f, 0x36, 0xb7, 0x01, 0x82, 0xbe, 0xae, 0xd4, 0xe7, 0xcc, 0x22, 0xf9, 0x23, 0x7d, 0xd2, 0xd7, 0x92, 0x86, 0xe1, + 0x25, 0xe5, 0xe1, 0x85, 0xe5, 0x8d, 0x86, 0x83, 0x21, 0x4a, 0x41, 0x70, 0xe3, 0xc8, 0x30, 0x09, 0x66, 0xfd, 0x8a, + 0xd2, 0xbb, 0x3f, 0x74, 0x39, 0x18, 0x2c, 0x47, 0x08, 0xcb, 0x51, 0x23, 0x9a, 0xf5, 0xc4, 0x8a, 0x00, 0x2f, 0x02, + 0x5c, 0x48, 0x8c, 0x1c, 0x08, 0xe5, 0xc7, 0x54, 0xf2, 0x2d, 0x14, 0xc3, 0xd1, 0x20, 0xd8, 0xe9, 0x68, 0xc4, 0xae, + 0x1b, 0xe1, 0x57, 0x71, 0x76, 0xbc, 0x4f, 0xb5, 0x89, 0x28, 0x52, 0x25, 0x98, 0x86, 0x18, 0x46, 0x58, 0xcc, 0x02, + 0x24, 0x08, 0x77, 0x9d, 0xe2, 0xa2, 0x63, 0x2d, 0x50, 0x2d, 0xed, 0x9c, 0x94, 0x19, 0x1e, 0xfc, 0x4a, 0x1d, 0x1c, + 0x63, 0xca, 0x4f, 0x20, 0xeb, 0x10, 0x14, 0xeb, 0x78, 0x9f, 0x1e, 0x95, 0xca, 0x89, 0x28, 0x1a, 0x11, 0x32, 0xc8, + 0x1e, 0x6f, 0xe0, 0x41, 0x47, 0x25, 0x49, 0xd9, 0x12, 0x4a, 0xbd, 0x4c, 0x55, 0x16, 0x9c, 0xc1, 0xe2, 0xd1, 0xf7, + 0x20, 0x34, 0x8f, 0x0d, 0x2e, 0x11, 0xaa, 0xb2, 0xf0, 0x6e, 0x71, 0xe4, 0xe2, 0x8d, 0x77, 0xab, 0x39, 0xfc, 0x55, + 0x71, 0xd6, 0x92, 0xf2, 0x59, 0x1b, 0x6f, 0xdc, 0x90, 0x03, 0xb8, 0x21, 0x8f, 0xeb, 0x17, 0x77, 0x2e, 0x16, 0x77, + 0xd2, 0xb0, 0xb8, 0x93, 0x2d, 0x8b, 0x1b, 0xf0, 0x85, 0x54, 0xf2, 0xa9, 0x8b, 0xd1, 0x97, 0x3a, 0x9f, 0x3c, 0xce, + 0x8f, 0xf4, 0xf8, 0x39, 0xc3, 0x79, 0x32, 0x93, 0x00, 0x6c, 0x89, 0x1b, 0xe6, 0xaa, 0x6e, 0x5e, 0xa4, 0x89, 0xd8, + 0x1c, 0x78, 0x7e, 0xea, 0xc4, 0xb8, 0x21, 0x85, 0xb7, 0x16, 0x54, 0xc7, 0x0b, 0xbb, 0x14, 0x3f, 0x34, 0xb4, 0x79, + 0xc3, 0x48, 0xe7, 0x5b, 0x46, 0x3a, 0x2e, 0x1d, 0x5d, 0x3e, 0x6c, 0x3a, 0x84, 0xf2, 0xa0, 0x60, 0x0f, 0x82, 0x7f, + 0x05, 0x6e, 0x99, 0xf2, 0x3e, 0x6c, 0xc6, 0xb1, 0xd2, 0x8e, 0x5a, 0x7a, 0x49, 0x72, 0x13, 0xc5, 0x60, 0xa0, 0x00, + 0xcd, 0x3c, 0x6c, 0x4b, 0x2d, 0xfc, 0x90, 0xc7, 0x3e, 0x6b, 0xdc, 0x4c, 0xc5, 0x7b, 0x79, 0x4b, 0xb5, 0x3a, 0x1d, + 0xaa, 0xb1, 0xf4, 0xd2, 0x94, 0xc5, 0x38, 0xe9, 0x1e, 0x24, 0xc9, 0xf8, 0x0f, 0xd9, 0x66, 0x35, 0x38, 0x24, 0x90, + 0xb0, 0x3a, 0x62, 0xe8, 0x25, 0xb0, 0x60, 0xa4, 0x91, 0x0c, 0xf5, 0xb5, 0x14, 0x47, 0x35, 0xce, 0x27, 0xfe, 0x27, + 0x3c, 0xae, 0x5a, 0x2c, 0x79, 0xfa, 0x3a, 0x87, 0xba, 0xb5, 0xf4, 0x26, 0xef, 0xc1, 0x0e, 0x46, 0x6b, 0x19, 0xe0, + 0xd3, 0x22, 0x47, 0x4d, 0x8d, 0x89, 0x27, 0x1c, 0x17, 0x48, 0x12, 0xb1, 0x24, 0xb7, 0x18, 0x86, 0x60, 0x03, 0x9e, + 0x39, 0xbd, 0x5c, 0xb7, 0xb2, 0xfd, 0x99, 0xaf, 0x6f, 0x60, 0x4d, 0x40, 0x6d, 0x81, 0x3b, 0xc8, 0x85, 0x6e, 0x81, + 0xe1, 0x1c, 0xea, 0xa0, 0x28, 0xbd, 0x80, 0x74, 0xe8, 0xb6, 0xb8, 0x4c, 0x0f, 0x63, 0xa0, 0x5a, 0xa0, 0x56, 0x7c, + 0x32, 0xc3, 0x5f, 0xce, 0x65, 0xf6, 0x64, 0x84, 0xbf, 0x5a, 0x97, 0xb9, 0x12, 0xab, 0x22, 0x45, 0x90, 0xc6, 0xac, + 0x0e, 0x4a, 0xfb, 0x89, 0xcc, 0xb5, 0x1f, 0xb0, 0x6d, 0xf8, 0x02, 0x3f, 0x7a, 0xbc, 0x4e, 0x20, 0x40, 0x81, 0x3c, + 0x86, 0xd0, 0x8a, 0xf5, 0xac, 0xb6, 0x7c, 0xd6, 0x50, 0x3e, 0xd2, 0xff, 0x60, 0xc2, 0x8f, 0xbb, 0x24, 0x2a, 0x68, + 0x4a, 0x59, 0x06, 0x72, 0x35, 0xf2, 0x43, 0x2f, 0xbe, 0xbb, 0xa2, 0x5b, 0x88, 0x26, 0x58, 0xfc, 0x5c, 0xb6, 0x43, + 0xbc, 0x68, 0xd9, 0x3a, 0x24, 0x95, 0x14, 0x55, 0x77, 0x9c, 0xd0, 0xbb, 0x7f, 0x8e, 0x25, 0xfe, 0xae, 0x74, 0x8d, + 0xe5, 0x0b, 0x52, 0xea, 0xe8, 0xea, 0xf1, 0x5a, 0x63, 0x9b, 0xcd, 0x54, 0x46, 0x5b, 0x61, 0x20, 0x61, 0x79, 0xf0, + 0x4a, 0xbc, 0x98, 0xf8, 0x3d, 0x34, 0xff, 0x18, 0x45, 0xb7, 0xe6, 0xe3, 0x75, 0x7a, 0xa2, 0x2e, 0xbc, 0xf8, 0x23, + 0x9b, 0x98, 0x63, 0x3f, 0x1e, 0x07, 0xc0, 0x3c, 0x8e, 0x02, 0x2f, 0xfc, 0xc8, 0x1f, 0xcd, 0x68, 0x95, 0xa2, 0x41, + 0xd7, 0xbd, 0x37, 0x68, 0x31, 0x27, 0x24, 0x48, 0x44, 0xae, 0xb6, 0x66, 0x16, 0x94, 0xf7, 0x43, 0x71, 0xad, 0x2f, + 0x18, 0xc5, 0xa2, 0x96, 0x01, 0xfe, 0x08, 0x60, 0x63, 0x06, 0x01, 0x1e, 0x0c, 0x15, 0xd7, 0x4b, 0x35, 0xe4, 0xa1, + 0x92, 0x56, 0x2d, 0xcf, 0x50, 0x7c, 0x85, 0x2d, 0xfc, 0xf6, 0xee, 0xa0, 0xe4, 0x21, 0xdd, 0xe5, 0xad, 0x7c, 0xde, + 0x08, 0xa1, 0xd4, 0x24, 0xc7, 0xc2, 0x07, 0x74, 0xce, 0x19, 0xcc, 0xe6, 0xae, 0xe5, 0x8f, 0xbd, 0x24, 0x59, 0x2d, + 0xd8, 0x84, 0x54, 0x62, 0x27, 0x05, 0x50, 0xe5, 0x7b, 0x88, 0x0c, 0xd8, 0xdf, 0x56, 0xad, 0xa3, 0x83, 0x57, 0x60, + 0xe0, 0x07, 0x0c, 0x65, 0x34, 0x9d, 0xaa, 0x85, 0x28, 0xe0, 0x9e, 0xcf, 0x9c, 0x83, 0xbf, 0xad, 0xde, 0x9c, 0xda, + 0x6f, 0xf2, 0x8f, 0x43, 0x60, 0x8c, 0x85, 0xb5, 0x12, 0xe7, 0x8b, 0x25, 0x78, 0xc5, 0x88, 0xa6, 0x5e, 0xd8, 0x3c, + 0x9c, 0x8b, 0xd2, 0x16, 0x5f, 0x32, 0x36, 0x01, 0x86, 0xdb, 0xd8, 0x28, 0xbd, 0x0a, 0xd8, 0x35, 0xcb, 0x2d, 0xa1, + 0x36, 0x3b, 0xab, 0xf9, 0x02, 0x43, 0xb5, 0x72, 0xdd, 0x23, 0xe7, 0xea, 0xa4, 0x21, 0x0d, 0x71, 0x0c, 0x7c, 0xe4, + 0xf2, 0x11, 0xab, 0x1c, 0xa9, 0xa1, 0xa1, 0x4a, 0x00, 0x34, 0x42, 0x76, 0xd2, 0x50, 0xde, 0x03, 0x44, 0xdd, 0x00, + 0x9b, 0xe1, 0xe8, 0x3d, 0x48, 0x6d, 0xc1, 0xe7, 0x29, 0x80, 0x93, 0xa7, 0x15, 0x52, 0x93, 0xa6, 0x19, 0xab, 0x13, + 0xb5, 0xa9, 0x24, 0xa4, 0x11, 0xce, 0x01, 0xe8, 0x25, 0x23, 0xc4, 0x55, 0xb5, 0x6b, 0xa3, 0x94, 0x47, 0x3e, 0xc2, + 0xc4, 0xef, 0x21, 0x4b, 0x92, 0xc6, 0x09, 0xcb, 0x17, 0xdd, 0x50, 0x8b, 0xda, 0xe5, 0xf9, 0x28, 0xca, 0x81, 0x0e, + 0x1a, 0x6a, 0xab, 0xd3, 0x51, 0x69, 0x90, 0xd5, 0xfe, 0x90, 0xc4, 0x5c, 0xa5, 0x6c, 0xb1, 0xdc, 0xa5, 0xbf, 0xa2, + 0x76, 0xb9, 0xbf, 0xa2, 0xdc, 0x50, 0x9d, 0xce, 0x81, 0x6a, 0xa8, 0xed, 0x23, 0x7b, 0x6b, 0x8f, 0x0b, 0x6e, 0x54, + 0x1a, 0xcf, 0x46, 0x2a, 0x37, 0xf8, 0x6b, 0x7a, 0x7f, 0xa3, 0x72, 0xd0, 0x4a, 0xcc, 0x41, 0x2d, 0x80, 0x5a, 0x09, + 0xe1, 0x6f, 0xc8, 0xb2, 0xb0, 0x01, 0x01, 0x53, 0x05, 0xab, 0xb3, 0xe9, 0x94, 0x8d, 0xd3, 0x44, 0x17, 0x92, 0xad, + 0x3c, 0xc4, 0x3b, 0xb8, 0xf6, 0xee, 0xb9, 0xea, 0x4f, 0x10, 0xe8, 0x46, 0x44, 0x42, 0xe4, 0x00, 0x89, 0x9b, 0x5a, + 0xfd, 0x64, 0x51, 0x8b, 0xe5, 0x89, 0xe2, 0xbd, 0x80, 0xec, 0xbb, 0xa6, 0x1c, 0x41, 0xe3, 0x54, 0xaf, 0xd8, 0x8d, + 0x51, 0x6e, 0x7a, 0xba, 0x1d, 0x01, 0x6e, 0x43, 0x1a, 0x6b, 0xe7, 0x4d, 0xc7, 0xb1, 0x33, 0xd5, 0x00, 0x07, 0xeb, + 0x8f, 0x95, 0xc3, 0x43, 0x64, 0xd1, 0x55, 0xcf, 0xde, 0xbe, 0xfa, 0xf3, 0xe9, 0xeb, 0x5d, 0xf1, 0x10, 0x36, 0xd9, + 0x86, 0x26, 0x57, 0xe1, 0x96, 0x46, 0x7f, 0xf9, 0xe9, 0x61, 0xcd, 0xb6, 0x9c, 0x17, 0x8e, 0x6a, 0x90, 0x4d, 0xbc, + 0x84, 0x8d, 0xc7, 0xd1, 0x35, 0x8b, 0x3f, 0x7b, 0x1a, 0xe4, 0xc6, 0xeb, 0xc1, 0x7d, 0xfb, 0xf3, 0xe9, 0x4f, 0x3b, + 0x83, 0x7a, 0xe8, 0xc0, 0xe1, 0x02, 0xb1, 0xe7, 0x03, 0x46, 0xd7, 0x86, 0x73, 0x14, 0x44, 0x09, 0x6b, 0x80, 0xe0, + 0xd5, 0xd9, 0xdb, 0xf7, 0x38, 0x5d, 0x05, 0xe3, 0x43, 0x4d, 0x7d, 0xde, 0xe0, 0x7f, 0x7e, 0x77, 0xfa, 0xfe, 0xbd, + 0x6a, 0x60, 0x8a, 0xf0, 0x44, 0x6e, 0x9d, 0x6f, 0xe2, 0x7b, 0xe8, 0x5c, 0xed, 0x5e, 0x27, 0x5a, 0x4a, 0xd7, 0xf7, + 0xf2, 0x68, 0xa8, 0x6c, 0x63, 0x9b, 0x73, 0x1a, 0xcb, 0x7b, 0xa6, 0x3b, 0xf7, 0x4e, 0xe3, 0xaa, 0xc1, 0x4a, 0xdb, + 0x09, 0x79, 0xa9, 0x64, 0xe1, 0x87, 0x57, 0x35, 0xa5, 0xde, 0x6d, 0x4d, 0x29, 0x5c, 0x5a, 0x37, 0xb0, 0xf2, 0x2a, + 0x5a, 0x48, 0x4c, 0x10, 0xbb, 0xbd, 0x7f, 0xba, 0xa4, 0x9b, 0xe3, 0x67, 0x00, 0xcd, 0x53, 0xbc, 0x54, 0xa1, 0xae, + 0x29, 0xe6, 0xd7, 0xbd, 0x7c, 0x6e, 0xc7, 0x01, 0x78, 0x02, 0x30, 0x59, 0xf9, 0x59, 0x66, 0x90, 0xb9, 0x1f, 0x8f, + 0x5b, 0xb9, 0x8b, 0xd0, 0x67, 0xa4, 0x30, 0xe2, 0x94, 0x6c, 0xe9, 0x4d, 0xc0, 0xbc, 0xde, 0x1c, 0x45, 0x69, 0x1a, + 0x2d, 0x7a, 0x8e, 0xbd, 0xbc, 0x55, 0x95, 0xbe, 0x10, 0xb1, 0x70, 0xeb, 0xff, 0xde, 0xbf, 0xfe, 0x59, 0x41, 0xf3, + 0x54, 0x8e, 0x44, 0x81, 0xc5, 0x5e, 0xba, 0x8a, 0x59, 0xa6, 0xfc, 0xeb, 0x7f, 0x5e, 0x55, 0xc4, 0x09, 0x7d, 0xf9, + 0x1b, 0xba, 0x48, 0xc8, 0x9f, 0x5c, 0x05, 0xd1, 0xcd, 0x5e, 0xe1, 0xe7, 0x77, 0x4f, 0xe5, 0xb9, 0x3f, 0x9b, 0xe7, + 0xb5, 0x4f, 0xd2, 0x2d, 0x63, 0x13, 0xd0, 0x93, 0x16, 0x42, 0x39, 0x8b, 0x6e, 0x7a, 0xff, 0xfa, 0x67, 0x2e, 0x26, + 0xba, 0x77, 0xd7, 0xd5, 0x03, 0x5a, 0x5e, 0xd1, 0xfa, 0x3a, 0x1b, 0x4b, 0x8c, 0x44, 0xb3, 0xba, 0xc0, 0x1b, 0x85, + 0xb4, 0x2b, 0x37, 0x35, 0x82, 0x5b, 0xc6, 0xf4, 0x9d, 0x3f, 0x9b, 0x7f, 0xee, 0xa0, 0x60, 0x42, 0xef, 0x1d, 0x15, + 0x54, 0xfa, 0x02, 0xc3, 0x1a, 0xf6, 0x76, 0x5f, 0xb0, 0xcf, 0x1c, 0xd7, 0x7d, 0x43, 0xfa, 0x12, 0xa3, 0xe1, 0xf2, + 0xe2, 0xf7, 0xc3, 0x61, 0x9e, 0x22, 0x57, 0xfe, 0x1e, 0x3c, 0x15, 0x4f, 0x36, 0x4a, 0x38, 0x7b, 0xd1, 0xb3, 0x75, + 0x0a, 0x21, 0xb4, 0xc3, 0x84, 0xa0, 0xcd, 0x7d, 0xcd, 0x74, 0x34, 0xe3, 0x6b, 0x72, 0x9d, 0xdb, 0xe8, 0x7b, 0x03, + 0x59, 0x43, 0x29, 0xa6, 0x57, 0xcd, 0x75, 0x95, 0x46, 0x3d, 0x38, 0x37, 0xb1, 0xb7, 0x24, 0xd5, 0x84, 0x82, 0x7a, + 0x1a, 0x10, 0xf5, 0x54, 0xee, 0xee, 0xd7, 0x5e, 0x70, 0xbd, 0xdb, 0x35, 0xae, 0x99, 0x82, 0x21, 0x69, 0xfe, 0xf7, + 0x11, 0x6f, 0xa4, 0xcb, 0x0f, 0xa6, 0xdd, 0x37, 0x5e, 0xca, 0xe2, 0xab, 0x39, 0xf8, 0x18, 0x0b, 0x99, 0x05, 0x44, + 0xef, 0xdd, 0x86, 0x94, 0x4b, 0x6c, 0x69, 0x0d, 0x1a, 0x2d, 0x30, 0xdc, 0x6f, 0xc3, 0xdd, 0x5f, 0x08, 0x73, 0xf7, + 0x4e, 0xc1, 0x0b, 0xf4, 0x77, 0xc3, 0xde, 0xdb, 0x28, 0xd3, 0xff, 0x63, 0xef, 0xff, 0x44, 0xec, 0xbd, 0xb5, 0x9f, + 0xdf, 0xb2, 0xb0, 0xff, 0x07, 0xb0, 0x7c, 0x8f, 0xb9, 0xa7, 0x1c, 0xd3, 0x6b, 0x9a, 0xe7, 0x6a, 0x71, 0xe9, 0xf0, + 0x22, 0x5e, 0xdd, 0x50, 0xeb, 0xf2, 0x10, 0x6f, 0xdc, 0x5e, 0xd1, 0x43, 0x64, 0xbf, 0xe5, 0x28, 0xff, 0xfe, 0x88, + 0x3e, 0xa1, 0xbc, 0x58, 0x12, 0xa6, 0xef, 0x9d, 0x1a, 0x49, 0x69, 0x24, 0xde, 0x8d, 0x77, 0xb7, 0x0b, 0xde, 0x11, + 0xc0, 0x7e, 0x73, 0xe3, 0xdd, 0xd5, 0x01, 0xdb, 0x88, 0x5e, 0xab, 0x9d, 0x9d, 0x80, 0x6f, 0x51, 0x0f, 0x1d, 0x8b, + 0x8c, 0x61, 0xc2, 0xd2, 0x13, 0x28, 0x74, 0x1f, 0xaf, 0xf7, 0xaa, 0x15, 0xb3, 0x21, 0x78, 0x5d, 0x4b, 0x80, 0x47, + 0x25, 0xc0, 0xfd, 0xe4, 0x2a, 0x0a, 0x1f, 0x02, 0xf9, 0xcf, 0x20, 0x72, 0xfa, 0xcd, 0xa0, 0x63, 0x77, 0x1b, 0xb0, + 0x63, 0x69, 0x15, 0x78, 0x2c, 0xac, 0x42, 0xdf, 0xaf, 0xd7, 0x10, 0x54, 0x08, 0x2d, 0xd2, 0x58, 0x46, 0x84, 0x56, + 0x01, 0x6d, 0x8e, 0x02, 0x9a, 0xb5, 0x0a, 0xc9, 0xf5, 0xc3, 0x69, 0xec, 0xc5, 0x6c, 0xd2, 0x7c, 0x05, 0x28, 0xd9, + 0x44, 0xdf, 0x59, 0xc9, 0x6a, 0xb9, 0x8c, 0xe2, 0x34, 0xb9, 0xc2, 0xe8, 0x30, 0x0b, 0x1f, 0x2e, 0x14, 0x90, 0xc7, + 0x2c, 0x8f, 0x15, 0x7c, 0x5a, 0x27, 0x55, 0x37, 0x98, 0x5b, 0x4e, 0xf1, 0xc1, 0x7d, 0x7e, 0x0c, 0xee, 0x35, 0x34, + 0x97, 0xb4, 0x26, 0x73, 0x2b, 0x8d, 0xfd, 0x85, 0xa6, 0x1b, 0x8e, 0xad, 0xeb, 0x42, 0xbe, 0x32, 0x77, 0x07, 0x7b, + 0x14, 0xe3, 0x78, 0xae, 0x43, 0xac, 0x44, 0xf4, 0xa3, 0x01, 0x0b, 0xbd, 0x97, 0xab, 0xe9, 0x94, 0xc5, 0x9a, 0x08, + 0x06, 0x09, 0xd1, 0x68, 0xc9, 0x04, 0x11, 0xbc, 0x2b, 0x3f, 0xf8, 0xec, 0x06, 0xb2, 0x4e, 0x15, 0xc1, 0xdc, 0xc1, + 0xc3, 0x94, 0x8c, 0xd8, 0x21, 0xa3, 0x5d, 0xda, 0x6e, 0x69, 0x93, 0x67, 0x07, 0xc6, 0x1c, 0x42, 0x40, 0x15, 0x4e, + 0xf9, 0x18, 0x5d, 0xd0, 0x0f, 0xd3, 0x2e, 0xf6, 0x00, 0x0d, 0xc0, 0xe1, 0x0d, 0xdc, 0xdc, 0x1b, 0x4b, 0x19, 0xe7, + 0x0d, 0xce, 0xdd, 0x41, 0xf0, 0xdc, 0x25, 0xed, 0x12, 0x5a, 0x0b, 0xbe, 0x9a, 0x7b, 0xf1, 0xab, 0x68, 0xc2, 0x10, + 0xd0, 0x51, 0x1a, 0x81, 0x8f, 0xa8, 0x14, 0xfc, 0x07, 0x63, 0xff, 0x98, 0xa5, 0x78, 0x40, 0xfb, 0x50, 0x74, 0x25, + 0x17, 0xb9, 0xcf, 0x1f, 0xef, 0x1b, 0x70, 0xd2, 0xea, 0x57, 0x5a, 0x2c, 0x1a, 0x5f, 0xea, 0xda, 0x57, 0xf2, 0x6e, + 0x7d, 0xe5, 0xc5, 0xb1, 0xcf, 0x62, 0x45, 0xfb, 0xee, 0x57, 0x5d, 0xde, 0xb4, 0x25, 0x35, 0x12, 0xd7, 0x6d, 0x2b, + 0x18, 0x03, 0x6f, 0xea, 0xb3, 0x60, 0xe2, 0xaa, 0x63, 0xfa, 0x30, 0x57, 0x19, 0xb5, 0xbb, 0xb6, 0x6d, 0x73, 0x35, + 0xad, 0x43, 0x3f, 0x41, 0x4d, 0x0b, 0x3f, 0xe1, 0xa1, 0x24, 0xd4, 0xec, 0x12, 0x17, 0xb1, 0x41, 0xce, 0x6a, 0x21, + 0x7c, 0x47, 0x51, 0x84, 0x1e, 0x02, 0x1b, 0x8f, 0x36, 0x24, 0x40, 0x73, 0x04, 0x58, 0x05, 0x4c, 0x15, 0x80, 0x3a, + 0x0f, 0x01, 0xe8, 0xdc, 0x5f, 0xf8, 0xe1, 0x2c, 0x69, 0x84, 0x08, 0x95, 0xb5, 0x25, 0x78, 0x52, 0xfa, 0x42, 0x55, + 0x70, 0x0d, 0xe7, 0x51, 0x00, 0xd9, 0x8f, 0x54, 0x66, 0xcd, 0x2c, 0xe5, 0x85, 0x6d, 0xdb, 0x86, 0x79, 0x00, 0x79, + 0x06, 0x3b, 0x87, 0xb6, 0x61, 0xc2, 0x5f, 0x96, 0x65, 0xd5, 0x48, 0x81, 0xfb, 0x0b, 0x3f, 0x34, 0xe9, 0xb1, 0x65, + 0xef, 0x06, 0xef, 0xbd, 0xb6, 0xc4, 0x09, 0xd7, 0xc8, 0x8d, 0x72, 0x87, 0x55, 0x6d, 0xe4, 0x26, 0x65, 0x0b, 0x3b, + 0x8b, 0xc2, 0x3c, 0xf1, 0x28, 0x1c, 0x15, 0x62, 0x34, 0x2a, 0xbf, 0x45, 0xb6, 0x34, 0xae, 0x66, 0xcf, 0x50, 0xbf, + 0xe7, 0x60, 0xf5, 0x94, 0x57, 0xd1, 0x2a, 0x98, 0xa0, 0x11, 0x16, 0x58, 0x4c, 0x2b, 0x85, 0x2d, 0x6a, 0x25, 0xc5, + 0x15, 0x64, 0x30, 0xc7, 0xf4, 0x6e, 0xef, 0x91, 0x38, 0x45, 0xb1, 0xf6, 0x14, 0xa7, 0xf8, 0xa2, 0x6e, 0x0b, 0x5e, + 0x3e, 0x85, 0x28, 0x46, 0x3b, 0x7c, 0xc0, 0xf7, 0x05, 0xd4, 0x0f, 0x76, 0xa9, 0x2f, 0xd6, 0xed, 0xf2, 0x29, 0x85, + 0xba, 0xf5, 0x3e, 0x7d, 0xda, 0x1b, 0x7f, 0xfa, 0xb4, 0xb7, 0x91, 0x1f, 0xa4, 0x79, 0x84, 0xb4, 0x31, 0x18, 0x0f, + 0x6c, 0x02, 0xd1, 0x8a, 0x08, 0xe8, 0xef, 0xa1, 0xbc, 0xe7, 0xf1, 0x18, 0x59, 0xf4, 0x34, 0x36, 0x78, 0x87, 0xf4, + 0x18, 0x64, 0x95, 0x49, 0x99, 0xbb, 0x1e, 0x89, 0x79, 0x3e, 0x7d, 0xe2, 0xc7, 0xcd, 0x98, 0xb8, 0xe3, 0xbc, 0xc8, + 0x51, 0x8d, 0x95, 0x1b, 0xe4, 0x8f, 0x2a, 0x82, 0xbc, 0xe2, 0x18, 0xb3, 0x80, 0xf8, 0xc6, 0x8b, 0x43, 0x19, 0xe0, + 0x9f, 0x22, 0x85, 0x77, 0xab, 0xf0, 0x38, 0xac, 0x93, 0xea, 0x6a, 0x4c, 0x5d, 0xa6, 0xad, 0x08, 0x07, 0x0a, 0xfb, + 0x3a, 0xa9, 0x81, 0x73, 0x81, 0xed, 0x31, 0x19, 0xab, 0x18, 0x20, 0x7a, 0x75, 0xe3, 0xc9, 0x9d, 0x88, 0x61, 0xbd, + 0xf3, 0x6e, 0x7a, 0x2b, 0xf1, 0x70, 0x4a, 0x86, 0xf8, 0xbd, 0x69, 0xee, 0x2d, 0xbd, 0x24, 0x5f, 0xc7, 0x99, 0xfb, + 0x6d, 0xac, 0x2d, 0x8d, 0xd4, 0x50, 0x05, 0x19, 0x51, 0x75, 0x63, 0x51, 0x17, 0xd6, 0xb5, 0xbf, 0xe0, 0x41, 0x6e, + 0x34, 0xb1, 0x15, 0xae, 0xa6, 0xe8, 0x21, 0x11, 0x8e, 0xef, 0x30, 0x6c, 0x73, 0xf1, 0x9e, 0x40, 0xb9, 0xe2, 0x39, + 0xff, 0x26, 0xf2, 0x2b, 0x58, 0x70, 0xd5, 0x98, 0xea, 0x06, 0x79, 0x1e, 0xcc, 0xbe, 0xa4, 0x93, 0x01, 0x45, 0x72, + 0x5e, 0x48, 0x41, 0x66, 0x85, 0xdb, 0xc1, 0x55, 0xc5, 0xed, 0xa0, 0x66, 0x3e, 0x95, 0x98, 0x25, 0xcb, 0x28, 0x84, + 0xbb, 0xe2, 0x55, 0xe1, 0x57, 0x76, 0xb5, 0xe9, 0x57, 0x56, 0xf3, 0x29, 0xbe, 0xa1, 0xef, 0x40, 0x11, 0x7e, 0xfe, + 0x5f, 0x15, 0xbf, 0x00, 0x41, 0xea, 0x31, 0x37, 0xfa, 0x69, 0x93, 0x3f, 0xf9, 0xf7, 0xf7, 0xfb, 0x93, 0x9f, 0xed, + 0xe4, 0x4f, 0xfe, 0xfd, 0x17, 0xf7, 0x27, 0x3f, 0x95, 0xfd, 0xc9, 0x81, 0x04, 0x9f, 0xb2, 0x9d, 0xdc, 0x77, 0x85, + 0x23, 0x4d, 0x74, 0x93, 0xb8, 0x0e, 0xd7, 0xe7, 0x25, 0xe3, 0x39, 0x03, 0x03, 0x09, 0xce, 0xea, 0x06, 0xd1, 0x0c, + 0xbc, 0x6c, 0x9b, 0xfd, 0x68, 0xbf, 0x94, 0x17, 0x6d, 0x10, 0xcd, 0x54, 0x29, 0x3b, 0x5c, 0x28, 0xb2, 0xc3, 0x41, + 0x44, 0xbc, 0xbf, 0xdd, 0x3a, 0x2f, 0x2f, 0x9c, 0x7e, 0xdb, 0x81, 0xe8, 0xaa, 0xa0, 0xf3, 0xc6, 0x02, 0xbb, 0xdf, + 0x6e, 0x43, 0xc1, 0x8d, 0x54, 0xd0, 0x82, 0x02, 0x5f, 0x2a, 0xe8, 0x40, 0xc1, 0x58, 0x2a, 0x38, 0x84, 0x82, 0x89, + 0x54, 0x70, 0x04, 0x05, 0xd7, 0x6a, 0x76, 0x11, 0xe6, 0xde, 0xf2, 0x47, 0xfa, 0x65, 0x29, 0x31, 0x68, 0x6e, 0xa0, + 0x21, 0xaa, 0x1c, 0x19, 0x22, 0x4b, 0x85, 0x79, 0xa0, 0x73, 0x1e, 0x6d, 0xf8, 0xd5, 0x10, 0x30, 0x2f, 0xd8, 0xab, + 0x18, 0x60, 0xed, 0x43, 0x35, 0xdb, 0xe2, 0xb5, 0xda, 0xcb, 0xbd, 0xcb, 0x6d, 0xa3, 0x25, 0xbc, 0xb5, 0x7b, 0x18, + 0x3b, 0x44, 0x54, 0xee, 0x3c, 0x9f, 0xe7, 0x21, 0xab, 0x57, 0x6e, 0x11, 0x82, 0xa7, 0x0d, 0x89, 0x7b, 0x38, 0xaf, + 0xc6, 0x34, 0xb0, 0xd2, 0x81, 0x08, 0x2b, 0xe2, 0x14, 0x89, 0x0e, 0x14, 0x74, 0xc1, 0xef, 0x7b, 0x05, 0x0f, 0xc7, + 0x03, 0xbc, 0x13, 0xf4, 0x8b, 0x3c, 0x6e, 0x36, 0x69, 0x70, 0x57, 0x46, 0xea, 0xcd, 0x7a, 0x73, 0x83, 0xcc, 0xb7, + 0x7a, 0x33, 0x48, 0x84, 0x72, 0x32, 0xe9, 0x2d, 0x8d, 0x9b, 0x39, 0x0b, 0x7b, 0x53, 0xee, 0xec, 0x08, 0xeb, 0x4f, + 0xfe, 0x2b, 0x0b, 0x5d, 0x38, 0x5e, 0xe1, 0x9e, 0x28, 0xde, 0x12, 0x94, 0x66, 0xbe, 0x95, 0x0a, 0x9f, 0x21, 0x4d, + 0x36, 0xed, 0xfa, 0x12, 0x1e, 0x1e, 0xaf, 0xd9, 0x68, 0x35, 0x53, 0xce, 0xa2, 0xd9, 0xbd, 0xde, 0x1c, 0xf2, 0x2b, + 0x80, 0x52, 0x25, 0x1b, 0x56, 0x53, 0x6c, 0x6f, 0xde, 0x17, 0x3d, 0x66, 0xe5, 0xfa, 0x29, 0xc0, 0xa6, 0xa4, 0xc4, + 0x36, 0x40, 0x3f, 0x30, 0xdb, 0x92, 0xbf, 0xc4, 0x19, 0xcc, 0x9f, 0xf4, 0x7c, 0xee, 0x49, 0xf0, 0x0c, 0x7e, 0x64, + 0x49, 0xe2, 0xcd, 0x98, 0x8c, 0x5a, 0x4a, 0x8d, 0x03, 0x16, 0xcc, 0x95, 0xd8, 0x38, 0x81, 0xc0, 0xd8, 0xfb, 0x1b, + 0x5e, 0x30, 0xe0, 0xa8, 0x0b, 0xde, 0x61, 0xb0, 0x68, 0x85, 0xcb, 0x88, 0x6f, 0xc1, 0xf2, 0x94, 0xbd, 0x37, 0x00, + 0x89, 0x5c, 0xb3, 0xa0, 0x5a, 0x98, 0x7a, 0xb3, 0x6a, 0x11, 0xad, 0x75, 0x56, 0x42, 0x7b, 0x7a, 0xe9, 0x51, 0xe0, + 0xc2, 0xcf, 0xf0, 0x06, 0x08, 0xa2, 0xd9, 0xef, 0xea, 0x0a, 0xb0, 0xc5, 0x85, 0xe3, 0xc7, 0xd0, 0x08, 0xd3, 0xa1, + 0x85, 0x73, 0xac, 0x58, 0x30, 0x85, 0xbd, 0x30, 0x9d, 0x9b, 0x18, 0xce, 0x4e, 0x6b, 0x85, 0xba, 0x61, 0xe1, 0xda, + 0xae, 0xab, 0x41, 0x3c, 0x7b, 0xf1, 0x6c, 0xe4, 0x69, 0x4e, 0xeb, 0xc8, 0x10, 0x7f, 0x2c, 0xbb, 0xa3, 0x67, 0xd8, + 0x82, 0x32, 0xf1, 0xaf, 0xd7, 0xd3, 0x28, 0x4c, 0xcd, 0xa9, 0xb7, 0xf0, 0x83, 0xbb, 0xde, 0x22, 0x0a, 0xa3, 0x64, + 0xe9, 0x8d, 0x59, 0x5f, 0xe2, 0x47, 0x31, 0x3c, 0x34, 0x8f, 0x50, 0xe8, 0x58, 0xad, 0x98, 0x2d, 0xe8, 0xeb, 0x3c, + 0xfa, 0xf3, 0x34, 0x60, 0xb7, 0x19, 0xef, 0xbe, 0x54, 0x99, 0xaa, 0xe2, 0x96, 0xa3, 0x2f, 0x80, 0x65, 0xe6, 0xa1, + 0xa5, 0x21, 0xa1, 0x42, 0x9f, 0x4b, 0x1d, 0x7b, 0x56, 0xab, 0x13, 0xb3, 0x85, 0x62, 0x75, 0x1a, 0x1b, 0x8f, 0xa3, + 0x9b, 0x01, 0x40, 0x8b, 0x1f, 0x9b, 0x09, 0x0b, 0xa6, 0xf8, 0xc6, 0xc4, 0x68, 0x56, 0xa2, 0x1d, 0x13, 0xad, 0x19, + 0xa0, 0x35, 0xb6, 0xe8, 0xc3, 0xeb, 0x5e, 0x4b, 0xb1, 0x25, 0x7e, 0xfa, 0xc8, 0x5e, 0x4a, 0x6d, 0xc9, 0xf3, 0xa7, + 0xaf, 0xb1, 0xba, 0xa3, 0xd8, 0x7d, 0xd0, 0x1f, 0x4f, 0x83, 0xe8, 0xa6, 0x37, 0xf7, 0x27, 0x13, 0x16, 0xf6, 0x11, + 0xe6, 0xbc, 0x90, 0x05, 0x81, 0xbf, 0x4c, 0xfc, 0xa4, 0xbf, 0xf0, 0x6e, 0x79, 0xab, 0x07, 0x4d, 0xad, 0xb6, 0x79, + 0xab, 0xed, 0x9d, 0x5b, 0x95, 0x9a, 0x81, 0xc8, 0x59, 0xd4, 0x0e, 0x07, 0xad, 0xa3, 0xd8, 0x95, 0x71, 0xee, 0xdc, + 0xea, 0x32, 0x66, 0xeb, 0x85, 0x17, 0xcf, 0xfc, 0xb0, 0x67, 0x67, 0xd6, 0xf5, 0x9a, 0x36, 0xc6, 0xa3, 0x6e, 0xb7, + 0x9b, 0x59, 0x13, 0xf1, 0x64, 0x4f, 0x26, 0x99, 0x35, 0x16, 0x4f, 0xd3, 0xa9, 0x6d, 0x4f, 0xa7, 0x99, 0xe5, 0x8b, + 0x82, 0x76, 0x6b, 0x3c, 0x69, 0xb7, 0x32, 0xeb, 0x46, 0xaa, 0x91, 0x59, 0x8c, 0x3f, 0xc5, 0x6c, 0xd2, 0xc7, 0x8d, + 0xc4, 0xbd, 0xae, 0x8f, 0x6c, 0x3b, 0x43, 0x0c, 0x70, 0x51, 0xc2, 0x4d, 0x68, 0x30, 0x73, 0xb9, 0xde, 0xb9, 0xa6, + 0x52, 0x74, 0x37, 0x1e, 0xd7, 0xd6, 0x9b, 0x78, 0xf1, 0xc7, 0x4b, 0x45, 0x1a, 0x85, 0xe7, 0x51, 0xb5, 0xb5, 0x98, + 0x06, 0xf3, 0xb6, 0x07, 0x69, 0x42, 0xfa, 0xa3, 0x28, 0x86, 0x33, 0x1b, 0x7b, 0x13, 0x7f, 0x95, 0xf4, 0x9c, 0xd6, + 0xf2, 0x56, 0x14, 0xf1, 0xbd, 0x5e, 0x14, 0xe0, 0xd9, 0xeb, 0x25, 0x51, 0xe0, 0x4f, 0x44, 0x51, 0xd3, 0x59, 0x72, + 0x5a, 0x7a, 0x1f, 0xf9, 0x57, 0x1f, 0x43, 0x3d, 0x7b, 0x41, 0xa0, 0x58, 0xed, 0x44, 0x61, 0x5e, 0x82, 0x46, 0x7a, + 0x8a, 0x9d, 0xd0, 0xbc, 0x60, 0x40, 0x5c, 0xe7, 0x60, 0x79, 0x9b, 0xef, 0x79, 0xe7, 0x70, 0x79, 0x9b, 0x7d, 0xbd, + 0x60, 0x13, 0xdf, 0x53, 0xb4, 0x62, 0x37, 0x39, 0x36, 0x18, 0xf2, 0xe9, 0xeb, 0x86, 0x6d, 0x2a, 0x8e, 0x05, 0xa4, + 0x53, 0xda, 0xf3, 0x17, 0x20, 0x87, 0xf1, 0xc2, 0x34, 0xcb, 0x86, 0x97, 0x59, 0xd6, 0x3f, 0xf3, 0xb5, 0x8b, 0xff, + 0xd6, 0x88, 0x16, 0x92, 0xe1, 0x6b, 0xa6, 0x5f, 0x1a, 0xa7, 0x4c, 0x76, 0xd2, 0x01, 0x32, 0x86, 0x0e, 0x3a, 0x72, + 0x65, 0xa2, 0xb7, 0x9b, 0x95, 0x69, 0x92, 0xf3, 0xea, 0xe4, 0xf3, 0x53, 0xae, 0x82, 0x14, 0x08, 0x2a, 0x9c, 0x32, + 0xf7, 0x4c, 0xf2, 0xf8, 0x01, 0xa6, 0x07, 0x2b, 0x53, 0x2c, 0xa3, 0xd7, 0x4d, 0xbc, 0xe7, 0xf9, 0xfd, 0xbc, 0xe7, + 0x5f, 0xd3, 0x5d, 0x78, 0xcf, 0xf3, 0x2f, 0xce, 0x7b, 0xbe, 0xde, 0x8c, 0x65, 0x74, 0x1e, 0xb9, 0x6a, 0x6e, 0xa6, + 0x09, 0xa4, 0x29, 0xa6, 0x2c, 0x01, 0xaf, 0xd3, 0xdf, 0x1a, 0x54, 0x46, 0xb4, 0x86, 0x44, 0x81, 0xf3, 0xa9, 0x20, + 0x66, 0x7d, 0x1b, 0xba, 0x7f, 0x8e, 0xe5, 0xe7, 0xe9, 0xd4, 0x7d, 0x1d, 0x49, 0x05, 0xf9, 0x13, 0xf7, 0x60, 0x29, + 0x45, 0x74, 0xa6, 0x37, 0xb9, 0x8f, 0x11, 0xe4, 0xbc, 0x86, 0x80, 0xb0, 0xe4, 0x50, 0x3e, 0xc9, 0x3d, 0xfd, 0xfa, + 0x65, 0x10, 0xb4, 0xdc, 0xb5, 0x56, 0x84, 0xfd, 0xda, 0xb0, 0x8c, 0x9a, 0x31, 0x21, 0x03, 0x78, 0x79, 0xf7, 0xfd, + 0x44, 0x3b, 0x8f, 0xf4, 0xcc, 0x4f, 0xde, 0x56, 0x83, 0x6e, 0x09, 0x3d, 0x97, 0x3c, 0x9c, 0x8c, 0x7b, 0xeb, 0x49, + 0xb1, 0x75, 0xf1, 0x35, 0x7d, 0x7e, 0x52, 0x1a, 0x69, 0x4f, 0xfe, 0xb0, 0x4f, 0x91, 0xc6, 0x37, 0x88, 0x31, 0x0f, + 0x4e, 0xb3, 0xe6, 0x5c, 0xde, 0x1a, 0x9f, 0x21, 0x56, 0xe9, 0x84, 0x3e, 0xf7, 0x27, 0x59, 0xa6, 0xf7, 0xc5, 0x44, + 0x48, 0x84, 0x96, 0xdd, 0xc7, 0xc4, 0x25, 0x85, 0x10, 0x88, 0x4b, 0x7c, 0xc8, 0x86, 0xfa, 0x1c, 0xbc, 0x12, 0xb8, + 0xc5, 0x35, 0x9f, 0x33, 0x55, 0xa1, 0xe9, 0x23, 0x6f, 0x15, 0x69, 0x40, 0x60, 0x46, 0x2f, 0xfb, 0x78, 0x95, 0x16, + 0x64, 0xd3, 0x9d, 0x96, 0x26, 0x07, 0xdd, 0x2a, 0x20, 0xb2, 0xb0, 0x10, 0x0b, 0x11, 0xda, 0xe1, 0x75, 0xf0, 0x21, + 0x53, 0x73, 0xde, 0x0f, 0xb7, 0xdf, 0xe0, 0x78, 0x1f, 0x3e, 0x18, 0x54, 0x94, 0x6e, 0xf7, 0x78, 0x83, 0x02, 0x2b, + 0x91, 0xdc, 0x18, 0x56, 0x72, 0xa3, 0x3c, 0x5b, 0x8b, 0xa8, 0xdc, 0xa9, 0xb7, 0x34, 0x41, 0xcb, 0x83, 0xb8, 0x97, + 0x63, 0x3c, 0x29, 0x00, 0x78, 0x7f, 0x95, 0x00, 0x6e, 0x44, 0x39, 0x0a, 0xe2, 0x9f, 0xfe, 0x78, 0x15, 0x27, 0x51, + 0xdc, 0x5b, 0x46, 0x7e, 0x98, 0xb2, 0x38, 0x23, 0xc1, 0x0a, 0xce, 0x8f, 0x98, 0x9e, 0xcb, 0x75, 0xb4, 0xf4, 0xc6, + 0x7e, 0x7a, 0xd7, 0xb3, 0x39, 0x4b, 0x61, 0xf7, 0x39, 0x77, 0x60, 0xd7, 0xd6, 0xef, 0xf1, 0xd9, 0x7c, 0x8e, 0x8c, + 0x5f, 0xbc, 0xc9, 0xce, 0xc8, 0xdb, 0xbc, 0x2f, 0xbd, 0xa5, 0xb8, 0xe4, 0xc0, 0x7e, 0x78, 0xb1, 0x39, 0x03, 0x2c, + 0x0f, 0x4b, 0x6d, 0x4f, 0xd8, 0xcc, 0x40, 0xac, 0x0d, 0xfe, 0x0e, 0xe2, 0x8f, 0xd5, 0xd1, 0x15, 0xbb, 0xbe, 0x18, + 0x38, 0x1e, 0x7d, 0x17, 0xc8, 0x7a, 0xde, 0x34, 0x65, 0xb1, 0xb1, 0x4b, 0xcd, 0x11, 0x9b, 0x46, 0x31, 0xa3, 0x1c, + 0x76, 0x4e, 0x77, 0x79, 0xbb, 0x7b, 0xf3, 0xdb, 0x87, 0x5f, 0xdf, 0x4e, 0x18, 0xa5, 0x9a, 0x68, 0x4c, 0xbf, 0xa7, + 0xb5, 0x4d, 0x7a, 0x06, 0xac, 0x21, 0xcd, 0xfc, 0x98, 0xa4, 0x20, 0x10, 0x7f, 0xac, 0x36, 0x55, 0xc8, 0x32, 0xe2, + 0x34, 0x2f, 0x66, 0x81, 0x97, 0xfa, 0xd7, 0x82, 0x67, 0x6c, 0x1f, 0x2e, 0x6f, 0xc5, 0x1a, 0x23, 0xc1, 0x7b, 0xc0, + 0x22, 0x55, 0x40, 0x11, 0x8b, 0x54, 0x2d, 0xc6, 0x45, 0xea, 0x6f, 0x8c, 0x46, 0x44, 0xcf, 0xae, 0x50, 0xfa, 0xce, + 0xf2, 0x56, 0x26, 0xd1, 0xc5, 0x67, 0x39, 0xa5, 0xae, 0xa6, 0x3d, 0x59, 0xf8, 0x93, 0x49, 0xc0, 0xb2, 0xd2, 0x42, + 0x97, 0xd7, 0x52, 0x9a, 0x9c, 0x7c, 0x1e, 0xbc, 0x51, 0x12, 0x05, 0xab, 0x94, 0xd5, 0x4f, 0x97, 0x90, 0xe8, 0x16, + 0x93, 0x83, 0xbf, 0xcb, 0xb0, 0x76, 0x80, 0xdd, 0x86, 0x6d, 0x62, 0xf7, 0x21, 0xcb, 0xa1, 0xd9, 0x2e, 0x83, 0x0e, + 0xaf, 0x72, 0xa0, 0x8d, 0x9a, 0x81, 0x18, 0x40, 0x96, 0x08, 0x7b, 0x2b, 0x96, 0xc3, 0xcb, 0xf2, 0x4c, 0x6f, 0x79, + 0x51, 0x56, 0x1e, 0xcc, 0xef, 0x73, 0xc6, 0x5e, 0xd4, 0x9f, 0xb1, 0x17, 0xe2, 0x8c, 0x6d, 0xdf, 0x99, 0x8f, 0xa6, + 0x0e, 0xfc, 0xd7, 0x2f, 0x06, 0xd4, 0xb3, 0x95, 0xf6, 0xf2, 0x56, 0x71, 0x96, 0xb7, 0x8a, 0xd9, 0x5a, 0xde, 0x2a, + 0xd8, 0x34, 0x3a, 0xd5, 0x18, 0x56, 0x4b, 0x37, 0x6c, 0x05, 0x0a, 0xe1, 0x8f, 0x5d, 0x7a, 0xe5, 0x1c, 0xc0, 0x3b, + 0xf8, 0xaa, 0xb3, 0xf9, 0xae, 0xb5, 0xfd, 0xa8, 0xd3, 0x59, 0x12, 0x48, 0x5b, 0xb7, 0x52, 0x6f, 0x34, 0x02, 0x51, + 0x66, 0x34, 0x5e, 0x25, 0xff, 0xe0, 0xf0, 0xf3, 0x49, 0xdc, 0x8a, 0x08, 0x2a, 0xed, 0x88, 0x4f, 0x41, 0x51, 0x78, + 0xcd, 0x44, 0x0b, 0xeb, 0x7c, 0x9d, 0x7a, 0x94, 0x92, 0xb1, 0x65, 0x1d, 0xd4, 0x6c, 0xf2, 0xfa, 0x89, 0xfe, 0xdd, + 0x56, 0xa9, 0x19, 0xc5, 0x7c, 0xc6, 0xb4, 0x6c, 0x9d, 0x8e, 0x87, 0xcf, 0x06, 0x5f, 0x4d, 0xbb, 0x5b, 0x0f, 0xee, + 0x85, 0xe8, 0xe9, 0x52, 0x10, 0x15, 0x4e, 0xb7, 0x78, 0x00, 0x90, 0xed, 0xad, 0x36, 0xed, 0x91, 0x8d, 0x56, 0xb7, + 0x10, 0x84, 0xa2, 0xee, 0x8e, 0x58, 0xfe, 0xd1, 0x8b, 0x03, 0xf8, 0x8f, 0xb8, 0xfa, 0xbf, 0xa6, 0x75, 0x8c, 0xfa, + 0xeb, 0xb4, 0xc4, 0xa8, 0x13, 0xab, 0x84, 0x8c, 0xf8, 0xee, 0xf5, 0xa7, 0xd3, 0x87, 0x7d, 0xb0, 0x73, 0x6d, 0xf2, + 0x47, 0xab, 0xd6, 0x7e, 0x19, 0x45, 0x01, 0xf3, 0xc2, 0xcd, 0xea, 0x62, 0x7a, 0x28, 0xb8, 0x40, 0xea, 0xc2, 0x47, + 0xe2, 0x1e, 0x41, 0xae, 0x10, 0x2a, 0x7e, 0x43, 0x57, 0x89, 0xb3, 0xa6, 0xab, 0xc4, 0xbb, 0xfb, 0xaf, 0x12, 0x3f, + 0xec, 0x74, 0x95, 0x78, 0xf7, 0xc5, 0xaf, 0x12, 0x67, 0x9b, 0x57, 0x89, 0xb3, 0x48, 0x38, 0x21, 0x1b, 0x6f, 0x56, + 0xfc, 0xe7, 0x07, 0xb2, 0xf7, 0x7d, 0x17, 0xb9, 0x1d, 0x9b, 0xd2, 0x2c, 0x9e, 0xff, 0xe6, 0x8b, 0x05, 0x6e, 0xc4, + 0x77, 0xe8, 0x93, 0x57, 0x5c, 0x2d, 0x38, 0x66, 0xc7, 0x7e, 0xa4, 0xe2, 0x20, 0x0a, 0x67, 0x3f, 0x83, 0xbd, 0x37, + 0x88, 0x03, 0x63, 0xe9, 0x85, 0x9f, 0xfc, 0x1c, 0x2d, 0x57, 0x4b, 0x54, 0x54, 0x7d, 0xf0, 0x13, 0x7f, 0x14, 0xb0, + 0x3c, 0xae, 0x25, 0x69, 0x5d, 0xb9, 0x6c, 0x1d, 0x14, 0xaf, 0xe2, 0xa7, 0x77, 0x2b, 0x7e, 0xa2, 0x63, 0x2f, 0xff, + 0x4d, 0xce, 0x89, 0x6a, 0xfd, 0x45, 0x44, 0x58, 0x88, 0x49, 0x40, 0x3f, 0xfc, 0x32, 0x72, 0x26, 0x22, 0x88, 0x95, + 0x46, 0x29, 0xdc, 0x37, 0x1a, 0xdb, 0x61, 0xd5, 0x76, 0xde, 0xac, 0x74, 0x23, 0x4f, 0xfb, 0xb1, 0x29, 0xce, 0x5f, + 0x44, 0xab, 0x84, 0x4d, 0xa2, 0x9b, 0x50, 0x35, 0x42, 0xae, 0x57, 0x8d, 0x50, 0xa6, 0x9e, 0x7f, 0x53, 0x56, 0x38, + 0xaa, 0xd6, 0x12, 0xe6, 0xd0, 0x24, 0x0d, 0xb6, 0x89, 0x43, 0x54, 0x45, 0xa0, 0xa8, 0xfe, 0x9e, 0xa6, 0x45, 0xee, + 0xc3, 0xbe, 0x14, 0x9e, 0x27, 0x91, 0xc5, 0xa5, 0xc2, 0x89, 0x16, 0x0a, 0xe1, 0xa2, 0x88, 0xbd, 0x5d, 0xb3, 0x70, + 0xfc, 0x0d, 0xc5, 0xa5, 0x2c, 0xde, 0x82, 0xae, 0x2a, 0x5b, 0xf1, 0xf5, 0xe0, 0x91, 0xa8, 0xe9, 0xf1, 0x95, 0x34, + 0x8d, 0x6f, 0xaf, 0x59, 0x1c, 0x78, 0x77, 0x9a, 0x9e, 0x45, 0xe1, 0x8f, 0x30, 0x01, 0xaf, 0xa3, 0x9b, 0x50, 0xae, + 0x80, 0x09, 0xe2, 0x6b, 0xf6, 0x52, 0x6d, 0xcc, 0x74, 0x88, 0x14, 0x22, 0x41, 0xe0, 0x5b, 0x4b, 0x6f, 0xc6, 0xfe, + 0xcb, 0xa0, 0x7f, 0xff, 0x5b, 0xcf, 0x8c, 0x77, 0x51, 0xde, 0xd1, 0x2f, 0xcb, 0x1d, 0xba, 0x79, 0xf2, 0x64, 0xaf, + 0x79, 0xd8, 0xda, 0x38, 0x60, 0x5e, 0x2c, 0xa0, 0xa8, 0xf9, 0x5a, 0x6f, 0x3c, 0x05, 0x00, 0xc5, 0x79, 0xb4, 0x1a, + 0xcf, 0xd1, 0x5b, 0xf8, 0xcb, 0x8d, 0x37, 0x85, 0x36, 0x59, 0x72, 0x61, 0x5f, 0xe6, 0x43, 0xaf, 0x14, 0x15, 0xb3, + 0x80, 0xfd, 0x9f, 0x42, 0xd2, 0xaf, 0x7f, 0xe3, 0x34, 0x6c, 0xee, 0x9a, 0x3c, 0xd0, 0xd8, 0x83, 0x36, 0x6f, 0xdf, + 0x87, 0x58, 0x40, 0x14, 0x4e, 0x5b, 0x28, 0xe9, 0xea, 0x91, 0x4c, 0x56, 0x9d, 0x34, 0x39, 0x75, 0x4d, 0x53, 0x56, + 0x1e, 0xd1, 0x0b, 0xb3, 0x4a, 0x56, 0x23, 0x06, 0xe3, 0xd8, 0xaa, 0x82, 0x64, 0xb8, 0x37, 0x05, 0x43, 0xf4, 0x55, + 0x7d, 0xb7, 0xf0, 0x43, 0x03, 0x33, 0xcf, 0x6e, 0xbe, 0xf1, 0x6e, 0x21, 0xf7, 0x22, 0x20, 0xb7, 0xea, 0x2b, 0x28, + 0x34, 0xe4, 0x18, 0x45, 0xde, 0x64, 0xa2, 0xa9, 0xb5, 0x33, 0x21, 0xb4, 0x81, 0xc3, 0xaf, 0x14, 0x45, 0x51, 0xf2, + 0x6b, 0x84, 0x92, 0xdf, 0x23, 0xb0, 0x1c, 0xaf, 0x03, 0xa0, 0x2d, 0xc9, 0x96, 0xb7, 0x54, 0x02, 0x37, 0x03, 0xb4, + 0x9f, 0x16, 0x05, 0x3c, 0xbd, 0x10, 0x18, 0xb7, 0x50, 0x81, 0xb8, 0xd0, 0x83, 0xea, 0xdb, 0x8b, 0x21, 0x0b, 0x61, + 0x4f, 0xc1, 0x0b, 0x3b, 0xbe, 0xe5, 0x92, 0x60, 0xc5, 0xa6, 0xc7, 0x61, 0x9f, 0xd5, 0xe7, 0xa1, 0x09, 0x25, 0x2c, + 0x08, 0x5a, 0x87, 0x4a, 0x5a, 0x49, 0x83, 0xd5, 0xe0, 0x46, 0xbc, 0x17, 0xdd, 0xa6, 0x0b, 0x16, 0xae, 0x54, 0x03, + 0xac, 0x4e, 0x30, 0x2f, 0x10, 0xd4, 0x79, 0x4d, 0xcc, 0x16, 0x60, 0x9b, 0xfa, 0x2f, 0xe7, 0x44, 0x0b, 0x85, 0xa9, + 0x8a, 0x67, 0x8c, 0x79, 0xd8, 0x9d, 0x84, 0xe3, 0xb6, 0x2a, 0x85, 0xe0, 0x4b, 0x1a, 0x95, 0xb1, 0x39, 0x0f, 0xb4, + 0x85, 0x9c, 0x02, 0xd9, 0x88, 0x71, 0x71, 0x91, 0x98, 0x76, 0xcd, 0xab, 0x2e, 0x5a, 0xae, 0x91, 0xf1, 0x2a, 0x82, + 0xa2, 0x58, 0xdf, 0x6c, 0x86, 0xc3, 0x09, 0xc9, 0x10, 0x1a, 0xdb, 0x19, 0x6f, 0xb4, 0xd3, 0x30, 0xe8, 0x8f, 0xec, + 0x8e, 0x08, 0x09, 0x4d, 0xd5, 0x47, 0x76, 0x07, 0xc6, 0xe1, 0xa7, 0x20, 0x4d, 0x51, 0xb7, 0xa0, 0x6b, 0x03, 0xd2, + 0x0b, 0x8f, 0x21, 0x41, 0xc6, 0x96, 0x03, 0x64, 0x67, 0x5b, 0xb0, 0x38, 0x05, 0x41, 0x35, 0x92, 0xbe, 0x38, 0xc4, + 0x3c, 0x4e, 0x82, 0x56, 0x3b, 0xc7, 0x66, 0xcd, 0xd1, 0xd0, 0x9f, 0x39, 0xb6, 0xbd, 0xbf, 0x51, 0x1f, 0x04, 0xd9, + 0x75, 0xb5, 0x75, 0x23, 0x75, 0x1d, 0xdb, 0xf4, 0x9f, 0x59, 0xad, 0xfe, 0x06, 0x8d, 0x96, 0xf2, 0x57, 0x0d, 0x51, + 0xfc, 0x35, 0x78, 0xbc, 0xd6, 0x36, 0x0e, 0xa4, 0x5e, 0x8d, 0x3b, 0x80, 0xb0, 0x65, 0x5c, 0xfe, 0x35, 0xdc, 0x24, + 0xfd, 0x94, 0x3d, 0x8b, 0x72, 0xa9, 0x0f, 0x21, 0x03, 0xa3, 0x06, 0xc7, 0xe8, 0x4f, 0xca, 0x73, 0x45, 0xa3, 0xe3, + 0xa3, 0xeb, 0xc3, 0xbe, 0xc0, 0x28, 0x22, 0x30, 0x8f, 0xdc, 0x40, 0xa5, 0xc7, 0xa4, 0x8a, 0xe1, 0x78, 0xae, 0x37, + 0x56, 0x68, 0xf4, 0xb6, 0x72, 0x0b, 0xd8, 0x7e, 0x03, 0xf9, 0xb4, 0x46, 0x10, 0x59, 0x12, 0x6a, 0x40, 0xbe, 0xd6, + 0x7b, 0x1b, 0x5c, 0x2d, 0xcb, 0xcd, 0x95, 0x89, 0xe4, 0xee, 0x8d, 0x21, 0xd1, 0x41, 0x1d, 0x5a, 0xde, 0x5e, 0x3d, + 0xb9, 0x7b, 0x60, 0x93, 0x2c, 0x9c, 0x94, 0x1b, 0xac, 0xd0, 0xaf, 0xdd, 0x9b, 0x2b, 0x61, 0x14, 0x48, 0x64, 0x1c, + 0xd5, 0x60, 0x94, 0x2c, 0x0a, 0x71, 0xf3, 0xd3, 0x71, 0xf3, 0x77, 0xe2, 0x62, 0xf0, 0x03, 0xca, 0x42, 0x92, 0x7f, + 0x26, 0x09, 0xc5, 0x21, 0x5b, 0x26, 0xc6, 0xed, 0xd2, 0x04, 0x23, 0xda, 0xb8, 0x13, 0x53, 0xe1, 0xae, 0x58, 0x7c, + 0xe3, 0xf3, 0xfc, 0x57, 0xbb, 0x4a, 0xad, 0xfd, 0xfb, 0xa5, 0xd6, 0xe9, 0x7d, 0x52, 0x6b, 0x8a, 0x49, 0xc3, 0xed, + 0x41, 0x45, 0x6c, 0x1e, 0xc1, 0x9c, 0xcb, 0xd1, 0x8d, 0x4a, 0xa2, 0x6e, 0x0c, 0x61, 0x53, 0x63, 0x45, 0x4a, 0xad, + 0x91, 0x03, 0x22, 0x8a, 0xbf, 0xa5, 0x0b, 0x8a, 0x50, 0xa8, 0xcb, 0xb2, 0xf1, 0xb3, 0x42, 0x36, 0x4e, 0xb7, 0x9a, + 0x22, 0x1a, 0x89, 0xe0, 0xfe, 0xa5, 0x48, 0x3f, 0xf9, 0xed, 0xa0, 0x88, 0xf8, 0x53, 0x40, 0x2a, 0xc5, 0xb0, 0x29, + 0x2e, 0x1a, 0x52, 0x64, 0x24, 0x71, 0xcb, 0x28, 0x07, 0x48, 0x2a, 0x57, 0x2d, 0x42, 0xd8, 0x14, 0xe5, 0x20, 0x75, + 0x47, 0x90, 0xf3, 0x62, 0x79, 0xdb, 0x94, 0x63, 0x98, 0xc8, 0xaf, 0xa5, 0x4d, 0x92, 0x07, 0x1b, 0xa1, 0x09, 0x16, + 0x62, 0xfa, 0x8a, 0x5e, 0x3b, 0xb7, 0x81, 0x40, 0x20, 0x6b, 0x62, 0x23, 0xdd, 0x2f, 0x9d, 0xa7, 0x1c, 0xcd, 0x85, + 0xea, 0xda, 0x41, 0xea, 0x4e, 0x9a, 0x60, 0x59, 0x1e, 0x81, 0x73, 0x7d, 0x29, 0x49, 0x10, 0x7a, 0xb6, 0x62, 0xf7, + 0x6b, 0x18, 0x00, 0xa4, 0xff, 0xd5, 0x67, 0xce, 0x0a, 0x80, 0x24, 0x52, 0xb1, 0x65, 0x9d, 0x3f, 0x1e, 0x62, 0x93, + 0x2c, 0xd9, 0xb1, 0xea, 0x66, 0x9f, 0x24, 0xef, 0x59, 0xf3, 0x48, 0x24, 0x65, 0x71, 0x3e, 0xaf, 0xd1, 0x13, 0x70, + 0xf0, 0x5d, 0x16, 0xaf, 0x42, 0x4c, 0xbd, 0x6b, 0xa6, 0xb1, 0x37, 0xfe, 0xb8, 0x96, 0xfa, 0xe3, 0x22, 0x51, 0x10, + 0x17, 0x97, 0x95, 0x0a, 0x7d, 0x0f, 0x33, 0x55, 0xb1, 0x9e, 0xd5, 0x4a, 0x24, 0x41, 0x4d, 0xef, 0x91, 0xdd, 0xf6, + 0x5e, 0x4c, 0x0f, 0x2a, 0xf2, 0xd3, 0x56, 0xa7, 0x2c, 0x5d, 0xcf, 0xe1, 0x58, 0x44, 0xbf, 0xf2, 0x98, 0x4d, 0x7f, + 0x7c, 0xd7, 0x09, 0xef, 0xb3, 0xb2, 0x46, 0x9f, 0x03, 0x02, 0x7c, 0x5f, 0x52, 0x4c, 0xcb, 0x6a, 0x9a, 0x8d, 0x92, + 0x26, 0xb0, 0xa6, 0x7e, 0x10, 0x98, 0x01, 0xb8, 0x31, 0xac, 0x3f, 0x6b, 0x78, 0xd8, 0xce, 0x0a, 0x72, 0x24, 0x7e, + 0x46, 0x3b, 0xe5, 0x9d, 0x92, 0xce, 0x57, 0x8b, 0xd1, 0x5a, 0x16, 0x94, 0x4b, 0xf2, 0xf3, 0x4d, 0x99, 0xb9, 0xdc, + 0xed, 0x74, 0x3a, 0x2d, 0x4b, 0x8d, 0x6d, 0xe5, 0x00, 0x25, 0xbf, 0x8f, 0x6c, 0xdb, 0xae, 0xce, 0x6f, 0xd3, 0x41, + 0xa1, 0x83, 0x61, 0xa2, 0x10, 0xbe, 0x7b, 0xff, 0x9e, 0xfa, 0x83, 0xa0, 0xa5, 0xa6, 0x9a, 0xce, 0x23, 0x6d, 0xb5, + 0xff, 0x08, 0x50, 0x10, 0x35, 0xdc, 0x77, 0xfc, 0x37, 0xf7, 0xca, 0x96, 0x96, 0xaa, 0x07, 0xf8, 0x61, 0x1f, 0xdf, + 0xb3, 0xd7, 0x77, 0xf8, 0xb4, 0x69, 0x7b, 0x67, 0x56, 0x41, 0x76, 0x4b, 0x36, 0x4b, 0x7d, 0xb2, 0x54, 0xf2, 0x53, + 0xb6, 0x48, 0x7a, 0x63, 0x86, 0x0a, 0x52, 0x4b, 0xa2, 0xb6, 0x68, 0xd5, 0x63, 0xce, 0xc0, 0x8e, 0xcb, 0x11, 0x78, + 0xd8, 0x56, 0x50, 0x59, 0xb5, 0xa1, 0x59, 0x13, 0x9d, 0x20, 0x15, 0x5b, 0x6f, 0x2a, 0x9c, 0x70, 0x9b, 0x76, 0xec, + 0x3f, 0x95, 0xea, 0x29, 0xc0, 0x9d, 0xae, 0x85, 0xb5, 0x09, 0x29, 0x4f, 0xf0, 0xef, 0x5c, 0x39, 0xf7, 0x62, 0x79, + 0x5b, 0x36, 0xee, 0xea, 0x82, 0xba, 0xa9, 0x20, 0x65, 0x04, 0x75, 0x1d, 0xea, 0xcb, 0x4d, 0x80, 0xa6, 0xb2, 0x75, + 0x0b, 0x58, 0xd0, 0x88, 0x29, 0xa8, 0xe8, 0x08, 0x73, 0x50, 0xf1, 0x3a, 0x0b, 0x3b, 0xaf, 0x90, 0xef, 0xe3, 0x2f, + 0xc8, 0x8d, 0x0e, 0x49, 0x56, 0xfe, 0x64, 0x3c, 0xef, 0xa2, 0x72, 0xaf, 0xb4, 0x55, 0xd1, 0x54, 0x06, 0xf7, 0x80, + 0xb8, 0x91, 0x2a, 0xab, 0x38, 0x30, 0x97, 0x31, 0x9b, 0xfa, 0xb7, 0x9a, 0xbe, 0xde, 0x1c, 0x77, 0x73, 0xf3, 0x4e, + 0x07, 0xf4, 0x1a, 0x9b, 0x53, 0xb5, 0x93, 0x6a, 0xaf, 0xaa, 0xc3, 0x16, 0x70, 0xc2, 0x0a, 0x80, 0xcf, 0xac, 0x82, + 0x46, 0x43, 0x4a, 0x05, 0xf7, 0xd1, 0xa0, 0xf3, 0xb7, 0x32, 0xb2, 0x16, 0xe3, 0xc4, 0xe6, 0xea, 0xab, 0x50, 0xdb, + 0x42, 0x33, 0x08, 0x73, 0xc7, 0xb1, 0x13, 0x3e, 0x9b, 0xb0, 0x63, 0x64, 0x74, 0xe5, 0xe0, 0x0e, 0xc2, 0x53, 0x6a, + 0x52, 0xca, 0x15, 0x3a, 0xa5, 0xa8, 0x4b, 0xf8, 0xa1, 0x56, 0x78, 0x7f, 0x5e, 0x92, 0xc6, 0xf3, 0xa0, 0x13, 0x2d, + 0x7d, 0xa7, 0xda, 0x0b, 0x3f, 0xdc, 0xbd, 0xae, 0x77, 0xbb, 0x73, 0x5d, 0x60, 0x0e, 0x77, 0xae, 0x0c, 0xdc, 0x25, + 0x56, 0x3e, 0x4f, 0xdd, 0x1f, 0x24, 0xe5, 0x81, 0x1c, 0xa6, 0x51, 0xc5, 0xaf, 0xe8, 0x46, 0xff, 0xd3, 0xca, 0x1d, + 0x1e, 0x9f, 0xdc, 0x2e, 0x02, 0xe5, 0x9a, 0xc5, 0x09, 0xa4, 0xb1, 0x50, 0x1d, 0xcb, 0x56, 0x15, 0x34, 0xe8, 0xf7, + 0xc3, 0x99, 0xab, 0xfe, 0x72, 0xfe, 0xc6, 0xec, 0xaa, 0x27, 0x60, 0x8e, 0x71, 0x3d, 0x43, 0x16, 0xf7, 0xcc, 0xbb, + 0x63, 0xf1, 0x55, 0x8b, 0x7b, 0xfc, 0x10, 0x73, 0x8b, 0x65, 0x4a, 0x4b, 0xdd, 0x21, 0x11, 0xbd, 0x72, 0xed, 0xb3, + 0x9b, 0x97, 0xd1, 0xad, 0xab, 0x02, 0x62, 0x75, 0x5a, 0x5d, 0xc5, 0x69, 0x1d, 0x58, 0x87, 0x5d, 0x75, 0xf0, 0x95, + 0xa2, 0x1c, 0x4f, 0xd8, 0x34, 0x19, 0xa0, 0x38, 0xe6, 0x18, 0xf9, 0x41, 0xfa, 0xad, 0x28, 0xd6, 0x38, 0x48, 0x4c, + 0x47, 0x59, 0xf3, 0x47, 0x45, 0x01, 0x64, 0xd4, 0x53, 0x1e, 0x4d, 0x5b, 0xd3, 0x83, 0xe9, 0x8b, 0x3e, 0x2f, 0xce, + 0xbe, 0x2a, 0x55, 0x37, 0xe8, 0xdf, 0x96, 0xf4, 0x59, 0x92, 0xc6, 0xd1, 0x47, 0xc6, 0x79, 0x49, 0x25, 0x17, 0x14, + 0x55, 0x3f, 0x6d, 0x6d, 0xf6, 0xe4, 0x74, 0x47, 0xe3, 0x69, 0xab, 0xa8, 0x8e, 0x30, 0xee, 0xe7, 0x40, 0x1e, 0xef, + 0x0b, 0xd0, 0x8f, 0xe5, 0x69, 0x72, 0xcc, 0xba, 0x89, 0x72, 0x54, 0x3e, 0xc6, 0x99, 0x18, 0xdf, 0x31, 0xe4, 0x79, + 0x2b, 0xbc, 0x17, 0x13, 0xfc, 0xcc, 0x55, 0x7f, 0x74, 0x5a, 0x5d, 0xc3, 0x71, 0x0e, 0xad, 0xc3, 0xee, 0xd8, 0x36, + 0x0e, 0xac, 0x03, 0xb3, 0x6d, 0x1d, 0x1a, 0x5d, 0xb3, 0x6b, 0x74, 0xbf, 0xeb, 0x8e, 0xcd, 0x03, 0xeb, 0xc0, 0xb0, + 0xcd, 0x2e, 0x14, 0x9a, 0x5d, 0xb3, 0x7b, 0x6d, 0x1e, 0x74, 0xc7, 0x36, 0x96, 0xb6, 0xac, 0x4e, 0xc7, 0x74, 0x6c, + 0xab, 0xd3, 0x31, 0x3a, 0xd6, 0xe1, 0xa1, 0xe9, 0xb4, 0xad, 0xc3, 0xc3, 0xb3, 0x4e, 0xd7, 0x6a, 0xc3, 0xbb, 0x76, + 0x7b, 0xdc, 0xb6, 0x1c, 0xc7, 0x84, 0xbf, 0x8c, 0xae, 0xd5, 0xa2, 0x1f, 0x8e, 0x63, 0xb5, 0x1d, 0xc3, 0x0e, 0x3a, + 0x2d, 0xeb, 0xf0, 0x85, 0x81, 0x7f, 0x63, 0x35, 0x03, 0xff, 0x82, 0x66, 0x8c, 0x17, 0x56, 0xeb, 0x90, 0x7e, 0x61, + 0x83, 0xd7, 0x07, 0xdd, 0xbf, 0xaa, 0xfb, 0x8d, 0x63, 0x70, 0x68, 0x0c, 0xdd, 0x8e, 0xd5, 0x6e, 0x1b, 0x07, 0x8e, + 0xd5, 0x6d, 0xcf, 0xcd, 0x83, 0x96, 0x75, 0x78, 0x34, 0x36, 0x1d, 0xeb, 0xe8, 0xc8, 0xb0, 0xcd, 0xb6, 0xd5, 0x32, + 0x1c, 0xeb, 0xa0, 0x8d, 0x3f, 0xda, 0x56, 0xeb, 0xfa, 0xe8, 0x85, 0x75, 0xd8, 0x99, 0x1f, 0x5a, 0x07, 0x1f, 0x0e, + 0xba, 0x56, 0xab, 0x3d, 0x6f, 0x1f, 0x5a, 0xad, 0xa3, 0xeb, 0x43, 0xeb, 0x60, 0x6e, 0xb6, 0x0e, 0xb7, 0x7e, 0xe9, + 0xb4, 0x2c, 0x98, 0x23, 0x7c, 0x0d, 0x2f, 0x0c, 0xfe, 0x02, 0xfe, 0xcc, 0xf1, 0xdb, 0x3f, 0xb0, 0x99, 0x64, 0xf3, + 0xd3, 0x17, 0x56, 0xf7, 0x68, 0x4c, 0xd5, 0xa1, 0xc0, 0x14, 0x35, 0xe0, 0x93, 0x6b, 0x93, 0xba, 0xc5, 0xe6, 0x4c, + 0xd1, 0x90, 0xf8, 0xc3, 0x3b, 0xbb, 0x36, 0xa1, 0x63, 0xea, 0xf7, 0xdf, 0xda, 0x4e, 0xbe, 0xe4, 0xc7, 0xfb, 0x33, + 0xda, 0xfa, 0xb3, 0xc1, 0x57, 0xc7, 0x70, 0xb8, 0x07, 0x43, 0xe3, 0xd7, 0x26, 0xa5, 0xe4, 0xdf, 0xef, 0x57, 0x4a, + 0xbe, 0x5c, 0xed, 0xa2, 0x94, 0xfc, 0xfb, 0x17, 0x57, 0x4a, 0xfe, 0x5a, 0xf5, 0xad, 0x79, 0x53, 0xcd, 0x7d, 0xfd, + 0xc3, 0xba, 0x2a, 0x72, 0x48, 0x3c, 0xed, 0xe2, 0xa7, 0xd5, 0x25, 0x44, 0xad, 0x7f, 0x13, 0xb9, 0x2f, 0x57, 0x25, + 0x83, 0xcf, 0x08, 0x70, 0xec, 0x9b, 0x88, 0x70, 0xec, 0x87, 0x95, 0x0b, 0x56, 0x66, 0x9c, 0xcd, 0xf1, 0x27, 0xe6, + 0xdc, 0x0b, 0xa6, 0x39, 0x8b, 0x04, 0x25, 0x7d, 0x2c, 0x06, 0xbf, 0x79, 0x20, 0xcf, 0x70, 0x93, 0x59, 0x2d, 0xc2, + 0x04, 0x2c, 0x82, 0xc1, 0x92, 0x63, 0x1a, 0x67, 0x95, 0x8f, 0x2d, 0x11, 0xe7, 0xff, 0x8a, 0x7b, 0x14, 0x37, 0xbe, + 0x47, 0x03, 0xe0, 0xfa, 0xd6, 0x9d, 0xcd, 0x76, 0x15, 0xb0, 0xac, 0x13, 0x06, 0xd2, 0xc0, 0xed, 0xd7, 0xbd, 0x2f, + 0x9b, 0xe1, 0x56, 0x0c, 0xaf, 0x9b, 0x21, 0x05, 0x48, 0xaa, 0xdf, 0x3b, 0x65, 0x33, 0xde, 0xfb, 0x86, 0x59, 0xd3, + 0x7d, 0xe9, 0xf3, 0x2d, 0x36, 0xc4, 0x79, 0xc3, 0xd5, 0xa9, 0x5a, 0x97, 0xf8, 0xb4, 0xfa, 0x09, 0x29, 0x2e, 0xa8, + 0x85, 0xa1, 0x71, 0xc1, 0xa9, 0xda, 0x0a, 0xf2, 0x3b, 0xb6, 0xf4, 0xae, 0xd4, 0xa6, 0x6c, 0x9c, 0xfc, 0x6c, 0x8d, + 0xf7, 0x0a, 0xff, 0x57, 0xe0, 0x44, 0x39, 0xc7, 0x33, 0x8a, 0xe4, 0x79, 0x5e, 0x4b, 0xed, 0x92, 0x34, 0x22, 0x9b, + 0x3b, 0xeb, 0x4d, 0x5e, 0xb4, 0xd1, 0x2d, 0xc1, 0x61, 0x0b, 0xc1, 0x05, 0x61, 0xf7, 0xe4, 0x04, 0x90, 0x91, 0xa3, + 0x06, 0xfa, 0x39, 0x6c, 0x6b, 0x4c, 0xd4, 0x7b, 0x04, 0x9b, 0x98, 0x7b, 0x02, 0x2a, 0x72, 0x20, 0xd5, 0xf5, 0x34, + 0x88, 0xbc, 0xb4, 0x87, 0x6c, 0x9a, 0xc4, 0xf2, 0xb6, 0xd0, 0x63, 0xa1, 0xbf, 0xc5, 0x98, 0x4e, 0x6e, 0x98, 0x37, + 0x82, 0x9e, 0x0f, 0xdb, 0xec, 0xef, 0x72, 0x87, 0xb3, 0x75, 0xc9, 0x1c, 0xc5, 0xe9, 0x1c, 0x19, 0xce, 0xa1, 0x61, + 0x1d, 0x75, 0xf4, 0x4c, 0x1c, 0x38, 0xb9, 0xc9, 0xd2, 0x84, 0x80, 0x03, 0x44, 0x0e, 0xa6, 0x1f, 0xfa, 0xa9, 0xef, + 0x05, 0x19, 0xf0, 0xc3, 0xe5, 0x4b, 0xca, 0xdf, 0x57, 0x49, 0x0a, 0x63, 0x14, 0x4c, 0x2f, 0x3a, 0x7f, 0x98, 0x23, + 0x96, 0xde, 0x30, 0x16, 0x36, 0x18, 0xc6, 0x54, 0x7d, 0x49, 0x7e, 0x3f, 0xcb, 0xfa, 0x8c, 0xac, 0xd6, 0x46, 0x69, + 0xc8, 0xf7, 0x87, 0x70, 0x7c, 0xc8, 0x86, 0xc6, 0x77, 0x4d, 0x08, 0xf7, 0x97, 0xfb, 0x11, 0x6e, 0xca, 0x76, 0x41, + 0xb8, 0xbf, 0x7c, 0x71, 0x84, 0xfb, 0x9d, 0x8c, 0x70, 0x4b, 0xfe, 0x83, 0x85, 0x86, 0xe9, 0x3d, 0x3e, 0x6b, 0xe0, + 0x22, 0xfb, 0x5c, 0xdd, 0x27, 0x06, 0x5e, 0xd5, 0x8b, 0x9c, 0xb9, 0x7f, 0x59, 0xc9, 0x16, 0xd4, 0x28, 0x00, 0xc5, + 0x6c, 0x92, 0x3e, 0xba, 0x2e, 0xfb, 0xe0, 0xea, 0x26, 0xc2, 0x30, 0x40, 0x9b, 0xdf, 0x87, 0x69, 0x60, 0xbd, 0xe3, + 0xf7, 0x48, 0x50, 0xe8, 0xbe, 0x89, 0xe2, 0x85, 0x87, 0x89, 0x4d, 0x54, 0x1d, 0xdc, 0xe9, 0xe0, 0xc1, 0x86, 0x40, + 0x20, 0xe3, 0x28, 0x9c, 0xe4, 0x5a, 0x49, 0xe6, 0x5e, 0x10, 0xc7, 0xad, 0xde, 0x31, 0x2f, 0x56, 0x0d, 0x7a, 0x0d, + 0x8b, 0xfb, 0xac, 0x6d, 0x3f, 0x6b, 0x1d, 0x3c, 0x3b, 0xb4, 0xe1, 0x7f, 0x87, 0xb5, 0x33, 0x83, 0x57, 0x5c, 0x44, + 0x61, 0x3a, 0x2f, 0x6a, 0x36, 0x55, 0xbb, 0x61, 0xec, 0x63, 0x51, 0xeb, 0xa8, 0xbe, 0xd2, 0xc4, 0xbb, 0x2b, 0xea, + 0xd4, 0xd6, 0x98, 0x47, 0x2b, 0x09, 0xac, 0x1a, 0x68, 0xfc, 0x70, 0x05, 0x72, 0x76, 0xa9, 0x86, 0xfc, 0x9a, 0x0f, + 0xb7, 0x18, 0x17, 0x6b, 0x67, 0x97, 0x22, 0x73, 0x83, 0xda, 0x17, 0xc9, 0xfc, 0xee, 0x9d, 0x41, 0xae, 0xa2, 0xb4, + 0x31, 0xd3, 0x15, 0xe6, 0x53, 0x84, 0x3c, 0x57, 0x4c, 0x2c, 0x90, 0x47, 0x0b, 0x94, 0xc6, 0xab, 0x70, 0xac, 0xe1, + 0x4f, 0x6f, 0x94, 0x68, 0xfe, 0x7e, 0x6c, 0xf1, 0x8e, 0x75, 0x5c, 0x35, 0x6f, 0x60, 0x17, 0xa9, 0xee, 0x13, 0xb1, + 0x2a, 0xde, 0xb3, 0xd4, 0x88, 0x51, 0x8f, 0x4d, 0x4b, 0x6b, 0xba, 0xde, 0xb3, 0xfc, 0xc3, 0x67, 0xa9, 0x11, 0x3e, + 0x07, 0xdd, 0xa7, 0x6b, 0x3f, 0x79, 0x42, 0xb5, 0xf6, 0x5c, 0x31, 0xac, 0x93, 0x71, 0x91, 0x0f, 0x43, 0xf1, 0x66, + 0x11, 0xa5, 0xc4, 0xe8, 0x8d, 0x8d, 0xe8, 0xf9, 0xf3, 0x81, 0xeb, 0xe8, 0xa3, 0x98, 0x79, 0x1f, 0x33, 0x11, 0x64, + 0x3c, 0xc4, 0xac, 0xb8, 0x67, 0xbb, 0x19, 0x1a, 0xe9, 0xb5, 0xae, 0xb4, 0x4b, 0xb8, 0x33, 0xd9, 0xc2, 0x1d, 0x81, + 0x63, 0x2f, 0x77, 0x8f, 0x97, 0x80, 0x2b, 0x13, 0x19, 0xfc, 0x88, 0x3a, 0x57, 0x73, 0x2f, 0xf9, 0x21, 0x89, 0xc2, + 0x5f, 0x96, 0x10, 0x72, 0xb9, 0xb0, 0x28, 0x12, 0x97, 0xb1, 0xb6, 0x65, 0x5b, 0xb6, 0x9a, 0xb7, 0x37, 0xf5, 0x67, + 0xee, 0x3a, 0x4a, 0xbd, 0xde, 0x9e, 0x63, 0x04, 0xd1, 0x0c, 0xdc, 0xeb, 0x52, 0x3f, 0x0d, 0x58, 0x4f, 0x55, 0xc1, + 0xcf, 0x6e, 0x41, 0xd7, 0xf5, 0x8c, 0x3b, 0x3d, 0x78, 0x31, 0xe4, 0x50, 0x8f, 0xef, 0x84, 0x87, 0x2e, 0x46, 0x6e, + 0xff, 0x11, 0x68, 0xa4, 0xa6, 0x6a, 0x20, 0x32, 0x60, 0x71, 0x62, 0xca, 0x4e, 0x44, 0x3d, 0x05, 0xbe, 0xd1, 0x55, + 0x3e, 0xb6, 0x69, 0xec, 0x2d, 0x20, 0xc9, 0xef, 0x3a, 0x33, 0x38, 0x02, 0x56, 0x39, 0x06, 0x56, 0x9c, 0x17, 0x87, + 0x86, 0xd2, 0x72, 0x0c, 0xc5, 0x06, 0x2c, 0xac, 0x66, 0xc6, 0x3a, 0xbb, 0xec, 0xdf, 0x67, 0x07, 0x41, 0x68, 0xe7, + 0x11, 0x8d, 0x83, 0x2c, 0x20, 0xb8, 0x86, 0x29, 0xa5, 0x8c, 0x3d, 0x9a, 0x94, 0xce, 0xd3, 0x27, 0x5d, 0xe8, 0x39, + 0xbb, 0x4d, 0x75, 0x50, 0x28, 0x89, 0x2a, 0xbe, 0xbe, 0x46, 0x3f, 0x62, 0x3f, 0x54, 0xfc, 0x4f, 0x9f, 0x34, 0x1f, + 0x7c, 0x9c, 0x5c, 0x69, 0x7e, 0xe0, 0x59, 0x2f, 0x4d, 0x98, 0x5f, 0x68, 0xef, 0x71, 0xb2, 0xc0, 0x01, 0x11, 0xfe, + 0x2d, 0x8a, 0xc5, 0x0f, 0x6e, 0x3d, 0x61, 0x05, 0x5e, 0x38, 0x03, 0x4c, 0xe7, 0x85, 0xb3, 0x0d, 0x2b, 0x2d, 0x72, + 0x85, 0xae, 0x94, 0x16, 0x4d, 0x15, 0x16, 0x54, 0xc9, 0xcb, 0xbb, 0x73, 0x6f, 0xf6, 0x93, 0xb7, 0x60, 0x9a, 0x0a, + 0xc4, 0x0f, 0x3d, 0x77, 0x0b, 0x05, 0xef, 0x73, 0xf7, 0xe9, 0xf1, 0x82, 0xa5, 0x1e, 0x69, 0x87, 0xe0, 0x4e, 0x0c, + 0x5c, 0x82, 0xc2, 0xe9, 0x0f, 0x8f, 0x83, 0xe1, 0x52, 0x62, 0x2f, 0x22, 0x1f, 0x86, 0xc2, 0xc9, 0x97, 0x89, 0x86, + 0xa0, 0xae, 0x63, 0x90, 0x1f, 0xc2, 0xd8, 0xc3, 0xe4, 0x3e, 0x6e, 0x18, 0xa9, 0x83, 0xa7, 0xb9, 0xcb, 0x66, 0xd3, + 0x22, 0x04, 0x7e, 0xf8, 0xf1, 0x22, 0x66, 0xc1, 0x3f, 0xdc, 0xa7, 0x40, 0xcf, 0x9f, 0x5e, 0xaa, 0x7a, 0x3f, 0xb5, + 0xe6, 0x31, 0x9b, 0xba, 0x4f, 0xe1, 0x9e, 0xda, 0x43, 0xab, 0x59, 0x60, 0xe6, 0x9f, 0xdf, 0x2e, 0x02, 0x03, 0x6f, + 0xfd, 0x04, 0x8b, 0xda, 0x6e, 0x15, 0x41, 0xd6, 0xdb, 0x3b, 0xdd, 0xf5, 0x07, 0xfc, 0x12, 0x0f, 0x17, 0xc3, 0x75, + 0xe9, 0xea, 0xed, 0xf4, 0xf1, 0x5a, 0x3d, 0x0a, 0xbc, 0xf1, 0xc7, 0x3e, 0xbd, 0x29, 0x3d, 0x98, 0x40, 0xc4, 0xc7, + 0xde, 0xb2, 0x87, 0x54, 0x57, 0x2e, 0x04, 0xa7, 0x6a, 0x2a, 0xcd, 0x19, 0xbe, 0xda, 0xbd, 0x8c, 0x5b, 0x79, 0x8d, + 0x3d, 0x63, 0x57, 0x37, 0x73, 0x3f, 0x65, 0xa2, 0x2b, 0x7c, 0xc8, 0x32, 0x71, 0x7f, 0xa7, 0x9b, 0x2b, 0xde, 0xb7, + 0xad, 0xb6, 0xe2, 0x74, 0xbf, 0xeb, 0x5c, 0x3b, 0xf6, 0xbc, 0xe5, 0x58, 0xdd, 0x0f, 0x4e, 0x77, 0xde, 0xb6, 0x8e, + 0x02, 0xb3, 0x6d, 0x1d, 0xc1, 0x9f, 0x0f, 0x47, 0x56, 0x77, 0x6e, 0xb6, 0xac, 0x83, 0x0f, 0x4e, 0x2b, 0x30, 0xbb, + 0xd6, 0x11, 0xfc, 0x39, 0xa3, 0xaf, 0xe0, 0x5e, 0x44, 0xd7, 0xa0, 0xa7, 0x25, 0xe4, 0x20, 0xfd, 0xce, 0x55, 0xb5, + 0x46, 0x89, 0xea, 0xd5, 0xa8, 0x7b, 0x97, 0x18, 0x5c, 0x42, 0x24, 0xd3, 0xc1, 0xd0, 0x43, 0x5a, 0xe8, 0x32, 0x4a, + 0x72, 0x2b, 0x0c, 0xdf, 0x84, 0x87, 0x7a, 0x91, 0x75, 0x55, 0x3a, 0x41, 0xbc, 0x6e, 0x3f, 0xa1, 0xed, 0x2e, 0x85, + 0x95, 0xd3, 0x2a, 0xc7, 0xae, 0x21, 0xdf, 0xb2, 0x6e, 0x80, 0xea, 0x18, 0x10, 0x53, 0x11, 0x0e, 0x4a, 0xab, 0x45, + 0x5b, 0x02, 0x9b, 0x25, 0x2c, 0xa5, 0x22, 0x4d, 0x7c, 0x09, 0xc4, 0x46, 0xd7, 0x45, 0x9a, 0x64, 0x6c, 0x98, 0xd7, + 0x60, 0x3c, 0x9f, 0x73, 0xed, 0xab, 0x7e, 0x15, 0x5f, 0x82, 0x57, 0xbc, 0x15, 0x46, 0x37, 0x68, 0xf5, 0x71, 0xdf, + 0xdc, 0x61, 0x9c, 0x01, 0x26, 0x6c, 0xce, 0xca, 0xc0, 0xf2, 0xd0, 0xd9, 0xd5, 0x0e, 0x37, 0x10, 0xf4, 0x83, 0x3a, + 0x94, 0xd2, 0x83, 0x7f, 0x56, 0x3b, 0x3c, 0x8a, 0x85, 0x9c, 0xa2, 0x73, 0xe2, 0xc7, 0x39, 0x78, 0x12, 0x45, 0x71, + 0xea, 0xf3, 0xa3, 0xea, 0x06, 0xc5, 0x72, 0x62, 0xf1, 0xb5, 0x17, 0x48, 0x76, 0x77, 0xd2, 0x97, 0x7b, 0x39, 0xa1, + 0x7a, 0xf2, 0xa4, 0x00, 0xce, 0xac, 0xc0, 0x7d, 0xec, 0x74, 0x80, 0x4b, 0xe8, 0xb0, 0xf6, 0x56, 0x13, 0x50, 0xba, + 0x98, 0x6d, 0x73, 0x05, 0x2f, 0xd2, 0x41, 0x09, 0x33, 0x2f, 0x61, 0x60, 0xd2, 0x68, 0x87, 0xba, 0x61, 0x5e, 0x02, + 0xf9, 0xf4, 0x2a, 0x37, 0x33, 0x55, 0xef, 0x87, 0xc2, 0x5a, 0x22, 0xdc, 0x92, 0x09, 0x8f, 0x5f, 0x1d, 0x55, 0x98, + 0x9a, 0x2d, 0xe3, 0xb8, 0xc7, 0x9f, 0xfd, 0xdf, 0x3d, 0x08, 0xf4, 0x2d, 0x05, 0xf3, 0x8e, 0x0a, 0x16, 0x29, 0xf9, + 0x1a, 0xe6, 0xf4, 0x9e, 0x08, 0x3d, 0x4b, 0x4e, 0x54, 0x28, 0x52, 0x7b, 0x2a, 0xfa, 0xb1, 0xa9, 0xb9, 0x6d, 0x6b, + 0x4e, 0xc5, 0x8a, 0x02, 0xc3, 0xc7, 0xac, 0xa3, 0xc2, 0xcf, 0x55, 0x7f, 0xf2, 0xa4, 0x91, 0x38, 0x92, 0x2d, 0x51, + 0xc2, 0x52, 0x71, 0x9f, 0xd0, 0x54, 0x19, 0xef, 0xaa, 0x32, 0xea, 0xcb, 0xdb, 0x45, 0x6c, 0x26, 0x4c, 0x72, 0x69, + 0xef, 0xe1, 0xcf, 0x11, 0xf3, 0x52, 0x8b, 0xeb, 0x76, 0x35, 0x89, 0xe9, 0x30, 0x00, 0x6d, 0x64, 0x84, 0x42, 0xf2, + 0x61, 0x0e, 0x1f, 0xaf, 0xff, 0xb2, 0xe2, 0x41, 0x28, 0xa0, 0x8d, 0x4f, 0x9f, 0xec, 0x22, 0x6e, 0xe8, 0xdb, 0xd4, + 0xa3, 0xb8, 0x6d, 0x32, 0x2f, 0x10, 0xa5, 0x1e, 0xd9, 0x9f, 0xf8, 0x18, 0x6a, 0xa7, 0x3e, 0x82, 0x98, 0x14, 0xa9, + 0x62, 0xf0, 0xf6, 0xfc, 0x1b, 0x85, 0x1f, 0x00, 0xb2, 0x6e, 0xc0, 0x8b, 0x17, 0xc5, 0xc7, 0x71, 0x29, 0x3e, 0x8e, + 0xc2, 0xf3, 0x3d, 0x43, 0x66, 0xda, 0x6c, 0x9f, 0xa6, 0x10, 0x05, 0xe6, 0x64, 0xf3, 0xb1, 0x58, 0x05, 0xa9, 0xbf, + 0xf4, 0xe2, 0x74, 0x1f, 0x83, 0xe3, 0x60, 0xb0, 0x9d, 0xa6, 0xf8, 0x15, 0x64, 0x36, 0x22, 0x72, 0xa8, 0xa4, 0xa1, + 0xb0, 0x1b, 0x99, 0xfa, 0x41, 0x6e, 0x36, 0x22, 0x3a, 0xf0, 0xc6, 0x63, 0xb6, 0x4c, 0xdd, 0x52, 0x10, 0x9e, 0x68, + 0x9c, 0xb2, 0xd4, 0x4c, 0xd2, 0x98, 0x79, 0x0b, 0x35, 0x0f, 0xca, 0xb5, 0xd9, 0x5e, 0xb2, 0x1a, 0x41, 0x54, 0x21, + 0x11, 0x1e, 0x8c, 0x06, 0x08, 0x06, 0x1c, 0x00, 0x22, 0x04, 0xc5, 0xa1, 0x29, 0x3c, 0x8b, 0x66, 0x95, 0x2d, 0x55, + 0xb0, 0x54, 0x27, 0x98, 0x4a, 0x8d, 0x6e, 0x5e, 0x20, 0xdd, 0x1e, 0x47, 0xc1, 0x15, 0x8f, 0xb9, 0x91, 0xe7, 0xe4, + 0x51, 0x07, 0xc7, 0xfc, 0x3a, 0xae, 0x60, 0xb8, 0x19, 0xb5, 0x63, 0x43, 0xb2, 0xb8, 0xa6, 0x68, 0x1c, 0xfb, 0xbc, + 0x32, 0xd0, 0x4c, 0x6a, 0x19, 0xf3, 0x7d, 0x12, 0x2c, 0xe7, 0x40, 0xb2, 0x4a, 0x06, 0x3e, 0x73, 0x67, 0x90, 0xbb, + 0x7f, 0x22, 0x54, 0x48, 0xd5, 0x3e, 0x7d, 0x7a, 0x3f, 0xfc, 0xd7, 0x3f, 0x21, 0x29, 0xe9, 0xdc, 0x11, 0x31, 0x30, + 0x2e, 0xe4, 0x5a, 0x9c, 0x2d, 0x36, 0x86, 0x68, 0xdc, 0xc5, 0x26, 0x22, 0x3a, 0xa1, 0xd8, 0x5b, 0xd9, 0xf0, 0x52, + 0xc4, 0xd5, 0x83, 0x74, 0xc6, 0xba, 0x88, 0xd4, 0x31, 0x84, 0xe5, 0x1d, 0x8a, 0x18, 0x2e, 0xca, 0xdf, 0x6e, 0x5f, + 0x1e, 0x29, 0x45, 0xb8, 0xc7, 0x3a, 0x0b, 0x24, 0xda, 0x43, 0x83, 0x63, 0x4f, 0x41, 0x6e, 0x0a, 0xf9, 0xa2, 0xa4, + 0xb7, 0x0f, 0xc3, 0x9c, 0x47, 0x0b, 0x66, 0xf9, 0xd1, 0xfe, 0x0d, 0x1b, 0x99, 0xde, 0xd2, 0x27, 0x3b, 0x22, 0x94, + 0x13, 0x2a, 0xc4, 0x92, 0xe6, 0xe6, 0x39, 0xc4, 0xf8, 0x67, 0xc5, 0x54, 0x46, 0x95, 0xc0, 0x6d, 0xad, 0x42, 0x6f, + 0x79, 0xc0, 0x83, 0xa2, 0x89, 0x9a, 0x83, 0xe3, 0x7d, 0x6f, 0x50, 0xce, 0xcf, 0x63, 0x89, 0x3c, 0xb3, 0x65, 0x2a, + 0x70, 0x42, 0x69, 0x76, 0x44, 0x46, 0x9d, 0xe2, 0xc1, 0x8c, 0xa6, 0x53, 0x39, 0xa7, 0x8e, 0x55, 0x06, 0x2f, 0x9f, + 0xb4, 0x62, 0x4b, 0x47, 0x4b, 0xea, 0x69, 0xb3, 0x8b, 0xfc, 0xa7, 0xda, 0xc3, 0x64, 0x5a, 0x30, 0x66, 0x38, 0xef, + 0x1b, 0xb9, 0x79, 0xf2, 0x19, 0x7b, 0x44, 0x95, 0x38, 0x22, 0xa9, 0x66, 0x82, 0x6c, 0x60, 0xa9, 0xf6, 0x5c, 0x97, + 0xf0, 0x5c, 0x15, 0xdd, 0xc1, 0x24, 0xd6, 0xe4, 0xdc, 0x85, 0xc1, 0xa6, 0xf0, 0xa1, 0x49, 0xee, 0xbd, 0xf8, 0x51, + 0x75, 0x38, 0x9b, 0x30, 0xee, 0x7b, 0x62, 0xfb, 0x95, 0x36, 0x28, 0x6c, 0x3c, 0xbe, 0xee, 0x80, 0xe0, 0x45, 0x3b, + 0x15, 0x3c, 0xaf, 0x7c, 0x4d, 0x28, 0xdd, 0x0c, 0xbc, 0xbb, 0x48, 0x32, 0xbb, 0xe2, 0x11, 0x58, 0xce, 0xb0, 0xf4, + 0x5c, 0x78, 0x3e, 0x6f, 0x1c, 0x34, 0xa4, 0x61, 0x90, 0x9b, 0x74, 0xf3, 0xb0, 0x15, 0x04, 0x38, 0x60, 0xf7, 0x9d, + 0x35, 0xb9, 0x6e, 0x79, 0x30, 0x88, 0x3c, 0xb3, 0xe2, 0x1c, 0x96, 0x5e, 0x22, 0x5a, 0xc8, 0x8e, 0xf7, 0x61, 0x7c, + 0x94, 0x6d, 0x51, 0x30, 0x79, 0xc2, 0xbe, 0x10, 0x6f, 0xbd, 0x7e, 0xd3, 0xad, 0xb7, 0xca, 0xa3, 0x94, 0x59, 0x2f, + 0x5f, 0x87, 0xd8, 0x36, 0x5e, 0x42, 0xb6, 0x67, 0xdf, 0x4f, 0x38, 0x61, 0x90, 0x7a, 0xc9, 0x83, 0x61, 0x96, 0xea, + 0xe9, 0x5b, 0x83, 0xc4, 0x50, 0x9e, 0xdf, 0x0f, 0x2b, 0x4c, 0xf2, 0x9b, 0xf5, 0x53, 0x26, 0x62, 0x36, 0x9c, 0xa5, + 0x0d, 0x61, 0x1d, 0x9a, 0xaa, 0x10, 0x1f, 0xbe, 0xa5, 0x42, 0xb1, 0xcd, 0xb7, 0xd5, 0x2a, 0x38, 0xab, 0xa2, 0x9a, + 0xa7, 0xa9, 0x8f, 0xf0, 0x40, 0x6c, 0xd4, 0xc6, 0x52, 0x0c, 0x36, 0x91, 0xba, 0x50, 0x55, 0xa8, 0x16, 0xbc, 0xe5, + 0x92, 0x2a, 0xeb, 0xfd, 0xe3, 0x7d, 0xba, 0x4e, 0x0f, 0x68, 0x03, 0x0e, 0x8e, 0xc1, 0x32, 0x9d, 0xf6, 0x84, 0xb7, + 0x5c, 0xf2, 0x15, 0xa7, 0x5f, 0xf4, 0x66, 0x7f, 0x9e, 0x2e, 0x82, 0xc1, 0xff, 0x02, 0xf1, 0xc9, 0x8b, 0x98, 0x7c, + 0x7b, 0x03, 0x00}; #else // Brotli (default, smaller) constexpr uint8_t INDEX_BR[] PROGMEM = { - 0x5b, 0x05, 0x7b, 0x53, 0xc1, 0xb6, 0x69, 0x3d, 0x41, 0xeb, 0x04, 0x30, 0xf6, 0xd6, 0x77, 0x35, 0xdb, 0xa3, 0x08, - 0x36, 0x0e, 0x04, 0x80, 0x90, 0x4f, 0xf1, 0xb2, 0x21, 0xa4, 0x82, 0xee, 0x00, 0xaa, 0x20, 0x7f, 0x3b, 0xff, 0x00, - 0xaa, 0x9a, 0x73, 0x74, 0x8c, 0xe1, 0xa6, 0x1f, 0xa0, 0xa2, 0x59, 0xf5, 0xaa, 0x92, 0x79, 0x50, 0x43, 0x1f, 0xe8, - 0x3c, 0x52, 0x68, 0x2c, 0xf6, 0x36, 0x31, 0x90, 0x4e, 0x2b, 0x91, 0x69, 0x38, 0xb4, 0x61, 0xa7, 0xbb, 0x57, 0x79, - 0x3b, 0x6d, 0x62, 0x85, 0x9f, 0x24, 0x24, 0xda, 0x45, 0xc1, 0xe2, 0x85, 0x44, 0x50, 0x6c, 0x8c, 0x1e, 0xab, 0xbb, - 0x7b, 0x1e, 0x30, 0x98, 0x59, 0xe5, 0xb9, 0xd1, 0x34, 0x03, 0x0b, 0x79, 0x37, 0x8c, 0x4b, 0x68, 0x2c, 0xbe, 0x21, - 0xcc, 0x30, 0xc4, 0x9c, 0x4d, 0x34, 0xd6, 0x01, 0xc1, 0xe1, 0x66, 0x48, 0x3c, 0x94, 0xf4, 0x3c, 0x5b, 0x12, 0xfd, - 0x15, 0xb4, 0x05, 0xdb, 0xaf, 0xc1, 0x59, 0x5d, 0x9f, 0xc5, 0x8d, 0xaf, 0xf7, 0xc6, 0x5f, 0x58, 0xfb, 0xc3, 0x74, - 0x45, 0x97, 0x23, 0xac, 0x2e, 0x4a, 0xb9, 0xab, 0xd7, 0x5b, 0xe5, 0x85, 0x6f, 0x21, 0xba, 0x7e, 0xa3, 0x57, 0xa5, - 0xb3, 0x24, 0x7e, 0x60, 0x30, 0x0e, 0x15, 0x1f, 0x04, 0x5e, 0xba, 0x06, 0x04, 0xff, 0x02, 0x26, 0x2d, 0x03, 0x6c, - 0x97, 0x1b, 0x23, 0x84, 0x28, 0x25, 0xb4, 0xe2, 0xca, 0xbd, 0xc8, 0xc3, 0x96, 0x4a, 0xef, 0xed, 0xe7, 0x65, 0x4f, - 0xb2, 0x2d, 0x6c, 0x02, 0x91, 0x04, 0x76, 0xae, 0xba, 0x67, 0x9c, 0xe3, 0x38, 0xd6, 0x96, 0x61, 0x0c, 0xd3, 0x80, - 0xe4, 0x4a, 0x43, 0x2e, 0x92, 0xff, 0xa7, 0xdb, 0xd2, 0xec, 0xf5, 0xf5, 0x90, 0x4a, 0x22, 0x74, 0x92, 0x40, 0x80, - 0xed, 0x2b, 0xb5, 0xbc, 0xb6, 0xe0, 0x71, 0xda, 0x35, 0x3b, 0xe9, 0xec, 0xeb, 0xac, 0xbe, 0x6a, 0x2f, 0xf0, 0xde, - 0x96, 0xf7, 0x43, 0x51, 0x4d, 0xce, 0xbb, 0xd3, 0x70, 0xc2, 0x08, 0x2c, 0xf0, 0xc8, 0xac, 0x25, 0x82, 0x67, 0xeb, - 0xcd, 0xf5, 0x7d, 0x7d, 0xb7, 0x8b, 0xca, 0x2a, 0x15, 0x66, 0x68, 0xf3, 0x6e, 0xb6, 0xda, 0x5b, 0x81, 0x7b, 0x2e, - 0xe6, 0xc1, 0xdc, 0x5e, 0x0a, 0x7a, 0xda, 0x4a, 0x2c, 0x68, 0xa4, 0xd0, 0x92, 0xe7, 0xb2, 0xb3, 0xef, 0xab, 0xda, - 0xd7, 0xef, 0x89, 0xe7, 0x22, 0xd0, 0x93, 0xe5, 0x08, 0xcc, 0xea, 0x82, 0xdb, 0xde, 0x1a, 0xdd, 0x49, 0x47, 0x26, - 0x23, 0xc1, 0x16, 0x7b, 0x2a, 0x99, 0x23, 0xe9, 0x4c, 0xf8, 0x65, 0xaa, 0xfe, 0xe9, 0x6a, 0x41, 0x0d, 0x8f, 0xfb, - 0x48, 0x9a, 0x49, 0x4e, 0x0b, 0x2e, 0x91, 0xfc, 0x76, 0x6e, 0x55, 0xee, 0xb4, 0xbb, 0x3c, 0x63, 0xc1, 0x95, 0x5a, - 0xf8, 0x37, 0x35, 0xb1, 0xaa, 0xcd, 0xa9, 0x99, 0x2f, 0x3d, 0x52, 0xd0, 0x01, 0xd9, 0x06, 0x27, 0x41, 0x92, 0xdd, - 0x6d, 0x4a, 0xee, 0xad, 0xa6, 0x33, 0xe1, 0xff, 0xeb, 0x6b, 0x5a, 0xff, 0xfd, 0xf3, 0x05, 0x0a, 0xbb, 0xc4, 0x55, - 0x1d, 0x28, 0x69, 0x66, 0x7f, 0x4f, 0x49, 0xce, 0xb2, 0xec, 0x8a, 0x0a, 0x15, 0x2d, 0x63, 0x1b, 0x6f, 0x28, 0x00, - 0xf5, 0x44, 0x47, 0xd6, 0xf5, 0xb5, 0xd7, 0x6f, 0xff, 0xf5, 0x0b, 0x3d, 0xd9, 0x1d, 0x42, 0xe9, 0xb3, 0x32, 0xa5, - 0xf6, 0xa3, 0x1b, 0xc3, 0x58, 0x8a, 0x7c, 0x24, 0xc7, 0x03, 0xbc, 0x92, 0xd5, 0x4f, 0xbf, 0xfc, 0xfa, 0x3d, 0x3b, - 0x6d, 0x8c, 0xc5, 0xdb, 0x9d, 0x77, 0x97, 0xd2, 0x5e, 0x82, 0x85, 0x53, 0x9a, 0xaf, 0xb6, 0x09, 0x45, 0x51, 0x25, - 0x90, 0x44, 0x85, 0xa4, 0xc6, 0x23, 0x18, 0xfb, 0xbb, 0x56, 0xef, 0xe9, 0x3c, 0x13, 0x62, 0xf0, 0xaf, 0x6b, 0xe9, - 0xa2, 0xa7, 0x26, 0x32, 0xa1, 0x8d, 0x5d, 0x56, 0x22, 0xa0, 0xe8, 0xb8, 0x06, 0xc1, 0x07, 0xb4, 0x7e, 0xf5, 0xbe, - 0xae, 0xff, 0xfa, 0x95, 0xa7, 0xb8, 0xe2, 0x34, 0x6a, 0xc9, 0x3c, 0xee, 0xb3, 0x45, 0x67, 0xae, 0x93, 0x6c, 0x20, - 0xbc, 0x42, 0xc5, 0xe1, 0x91, 0x52, 0xb9, 0x8c, 0x62, 0x0d, 0x46, 0xbb, 0x46, 0x72, 0x69, 0x26, 0xe0, 0xac, 0x67, - 0xec, 0xb5, 0x51, 0x5f, 0x9d, 0xae, 0x5d, 0x25, 0x15, 0x72, 0xf0, 0xc2, 0x2f, 0x67, 0xe6, 0x68, 0x08, 0xac, 0xdd, - 0xcb, 0xd5, 0xe3, 0x03, 0x9d, 0x49, 0x4d, 0xa1, 0xcd, 0x81, 0xbf, 0x09, 0xd9, 0xdd, 0x2d, 0xe1, 0x7b, 0x7f, 0x9a, - 0x7d, 0xfd, 0x92, 0xd9, 0x26, 0x19, 0x8d, 0x9d, 0x2b, 0x6d, 0x38, 0x99, 0x2b, 0x2d, 0xb5, 0xfa, 0xed, 0xe3, 0xb0, - 0x11, 0x2c, 0x17, 0x79, 0xe0, 0x24, 0xb1, 0x86, 0x68, 0x15, 0xb3, 0x7f, 0x4b, 0xf3, 0xeb, 0xd7, 0xc3, 0xdd, 0x24, - 0x64, 0x69, 0x0f, 0x4e, 0xae, 0xdb, 0xc7, 0x71, 0x4e, 0xaa, 0x64, 0x18, 0xec, 0xd1, 0x7b, 0x59, 0xa8, 0x31, 0x05, - 0x80, 0x2b, 0xcb, 0x0c, 0x91, 0x02, 0x85, 0x0d, 0x16, 0x1d, 0x1b, 0xa6, 0x6e, 0x48, 0x13, 0x04, 0xa1, 0xc5, 0xff, - 0x33, 0xa7, 0xdf, 0xb4, 0x67, 0x98, 0xb0, 0x60, 0xdb, 0x52, 0xfa, 0xaf, 0x26, 0xbf, 0x98, 0xed, 0xc1, 0x7b, 0x18, - 0x2e, 0x02, 0x9e, 0x60, 0x3c, 0xef, 0xff, 0xef, 0xad, 0xb4, 0xdc, 0xfe, 0x88, 0x74, 0x45, 0x88, 0xec, 0x01, 0xc8, - 0x71, 0x86, 0x23, 0xeb, 0xf7, 0x9d, 0x99, 0x55, 0xe0, 0x34, 0x40, 0xb2, 0xc7, 0x99, 0x95, 0xb4, 0xd6, 0x62, 0x53, - 0x71, 0xef, 0x7d, 0xef, 0x32, 0xbf, 0x8b, 0xce, 0xf8, 0x61, 0x58, 0x61, 0x32, 0x1b, 0x69, 0x87, 0x65, 0xbb, 0xb2, - 0x9c, 0x06, 0x00, 0xc1, 0xfb, 0xde, 0xfb, 0x51, 0xf8, 0xff, 0x47, 0x16, 0xe7, 0x47, 0x64, 0x81, 0x8a, 0xcc, 0x2a, - 0xce, 0xc9, 0x2c, 0x60, 0x46, 0x55, 0x00, 0x67, 0x54, 0x00, 0xfb, 0xe8, 0x80, 0x1c, 0x0b, 0x82, 0x46, 0xd3, 0x64, - 0xb3, 0x8f, 0x86, 0x6c, 0x79, 0xbf, 0xd2, 0x62, 0x05, 0x51, 0xae, 0x67, 0x64, 0x5b, 0xf2, 0xbb, 0x1d, 0x28, 0x6b, - 0x96, 0xd2, 0x4a, 0x47, 0xff, 0xeb, 0x96, 0xeb, 0x89, 0x6e, 0x15, 0x9f, 0x68, 0xa7, 0xdc, 0x30, 0x8c, 0xfc, 0x9f, - 0xc0, 0x23, 0xe1, 0x0c, 0x4e, 0xc3, 0x69, 0xa8, 0xc2, 0xd5, 0xa0, 0xa6, 0x5b, 0xc7, 0x8e, 0xb3, 0xe9, 0x34, 0x54, - 0xfd, 0x31, 0xfc, 0x1e, 0x4a, 0x0b, 0x83, 0xa9, 0x33, 0x4d, 0xd3, 0x7f, 0x77, 0x8d, 0x72, 0x55, 0x71, 0x4d, 0x74, - 0xe3, 0xaa, 0x55, 0x19, 0xf8, 0x9b, 0xa6, 0x69, 0xf8, 0x87, 0xe4, 0x6e, 0x5f, 0xac, 0xf9, 0x3d, 0xda, 0x61, 0xa0, - 0x50, 0xca, 0x2e, 0x93, 0x38, 0x3e, 0xe4, 0x5b, 0x96, 0x25, 0xd9, 0xf0, 0x75, 0x2c, 0xad, 0x37, 0x37, 0x2a, 0x15, - 0x48, 0xe8, 0xbd, 0xf6, 0x19, 0xb5, 0x55, 0xf0, 0x10, 0x5d, 0x14, 0x49, 0xa4, 0xa7, 0xff, 0xa7, 0xaa, 0x7a, 0xbc, - 0x43, 0xa6, 0x66, 0xdd, 0xd8, 0xc4, 0x05, 0xf2, 0xe9, 0x1b, 0x34, 0x4f, 0x12, 0x1a, 0x1b, 0xac, 0xf3, 0x32, 0x32, - 0xad, 0x2b, 0xeb, 0xd8, 0x29, 0x4e, 0xfe, 0x6f, 0x80, 0xa1, 0x0d, 0xad, 0x4a, 0xc2, 0xb6, 0x83, 0xa0, 0xff, 0x50, - 0x68, 0x62, 0xad, 0x71, 0x00, 0x77, 0x5a, 0x66, 0xb4, 0x05, 0x9d, 0x01, 0x51, 0x9c, 0xbb, 0xf8, 0xd3, 0x64, 0x82, - 0x69, 0xb6, 0x87, 0x82, 0xef, 0x73, 0x14, 0x62, 0x0c, 0x22, 0xcb, 0x73, 0xdb, 0xb3, 0x0f, 0x4c, 0xfe, 0x77, 0x60, - 0x45, 0xff, 0x14, 0xea, 0x9f, 0xb0, 0x02, 0xa0, 0x6e, 0xbd, 0xe4, 0xd7, 0x0a, 0xbc, 0xe7, 0xf7, 0x00, 0x44, 0x26, - 0xc2, 0x96, 0xe5, 0x37, 0xda, 0x32, 0xca, 0xef, 0x42, 0xb4, 0xdd, 0xe2, 0x70, 0xf0, 0xe0, 0xc1, 0x93, 0xd4, 0xed, - 0x18, 0xbf, 0x54, 0xdd, 0xb0, 0x5b, 0x86, 0x3e, 0x46, 0x7f, 0xbe, 0x1b, 0x61, 0x83, 0x52, 0xe1, 0x6a, 0x62, 0x7a, - 0xa6, 0x7e, 0x6f, 0xc4, 0x5b, 0x63, 0x55, 0x1a, 0xb8, 0xa5, 0x87, 0xd9, 0x84, 0xd4, 0xcf, 0xd7, 0xd4, 0x62, 0x26, - 0x28, 0x13, 0x41, 0x44, 0xca, 0x76, 0xf6, 0x21, 0x63, 0x4b, 0xae, 0xaf, 0xe7, 0xc7, 0x4d, 0x79, 0x5d, 0x8f, 0xc3, - 0x04, 0x0d, 0xc9, 0x06, 0x39, 0xa0, 0xd8, 0xde, 0x87, 0xb3, 0xd7, 0x4b, 0x65, 0x4e, 0xa3, 0xa6, 0xff, 0x79, 0xab, - 0x0c, 0xed, 0xb4, 0x01, 0x69, 0xdc, 0xa6, 0x15, 0x98, 0x98, 0x82, 0xc8, 0x86, 0x4d, 0x06, 0x77, 0xbd, 0x39, 0x6c, - 0x73, 0x52, 0x73, 0x48, 0xd6, 0xe4, 0x8a, 0x4a, 0x83, 0x9a, 0xf5, 0xc9, 0xeb, 0xa5, 0x2e, 0xa9, 0x70, 0x4b, 0x9d, - 0xb9, 0xbe, 0xc8, 0xe4, 0x9e, 0x17, 0xf2, 0xca, 0x95, 0x97, 0xd3, 0x14, 0xd8, 0x9b, 0xef, 0xb1, 0xaf, 0x13, 0x9a, - 0xf5, 0x3d, 0x8f, 0x74, 0xe3, 0x5d, 0x47, 0x07, 0x26, 0xd2, 0x60, 0xa2, 0xef, 0x11, 0x34, 0x2f, 0x6e, 0xb3, 0xfe, - 0xf0, 0xa3, 0x42, 0x7d, 0xfb, 0x47, 0x5c, 0x75, 0x98, 0x0f, 0xe6, 0x0f, 0x4e, 0xe5, 0x67, 0x1a, 0x4f, 0x93, 0x47, - 0x5f, 0x04, 0x7c, 0xff, 0x7a, 0xf9, 0x79, 0x6b, 0x46, 0x47, 0xc6, 0x0c, 0xd5, 0x90, 0x30, 0xd5, 0xfc, 0xde, 0x8b, - 0xd5, 0x65, 0x7f, 0x87, 0x2d, 0x9a, 0xd5, 0xe4, 0x3f, 0xf9, 0xed, 0x07, 0xfb, 0xa2, 0xb6, 0xd3, 0xe1, 0xbf, 0xbd, - 0x40, 0x78, 0x7a, 0x67, 0x24, 0x0b, 0x6b, 0x0e, 0xed, 0xcf, 0x7a, 0x6a, 0x7c, 0xdb, 0x36, 0x61, 0x5b, 0xc3, 0x7c, - 0x5d, 0xfc, 0xf6, 0x1c, 0xc2, 0xa9, 0xba, 0x12, 0x15, 0x35, 0x11, 0x87, 0x41, 0x13, 0xa5, 0xe5, 0x73, 0x07, 0xfa, - 0xc6, 0xe3, 0x96, 0x43, 0x01, 0x76, 0x4b, 0x53, 0x28, 0xad, 0x09, 0x7e, 0x88, 0x0f, 0x26, 0x90, 0x04, 0xfd, 0x2a, - 0x0d, 0x4c, 0xcd, 0x5c, 0xbf, 0x10, 0xd6, 0x47, 0xaf, 0xe2, 0x12, 0x80, 0x00, 0x59, 0xaa, 0x9b, 0x18, 0x58, 0x96, - 0xc8, 0x80, 0x67, 0xc2, 0xb9, 0x4e, 0x5d, 0x86, 0x1e, 0x79, 0xf5, 0xcf, 0xb0, 0x81, 0x1f, 0x9e, 0x4f, 0x34, 0x1d, - 0x7c, 0xf2, 0x4a, 0x4d, 0xbd, 0x42, 0x06, 0xbc, 0x73, 0x56, 0xbc, 0x9d, 0x43, 0xa9, 0xe6, 0x44, 0x0c, 0xcd, 0xcd, - 0xe4, 0x4e, 0xde, 0xb3, 0x0e, 0x25, 0x35, 0xb6, 0xb6, 0xf6, 0xcc, 0xae, 0x6f, 0x91, 0x82, 0x59, 0xa1, 0xdc, 0x8b, - 0xaa, 0x4f, 0x64, 0x26, 0xd0, 0xa5, 0xe7, 0x38, 0xf3, 0xf5, 0xcd, 0x4f, 0x05, 0x62, 0x8c, 0x38, 0xc3, 0x96, 0x13, - 0x68, 0xb2, 0xe4, 0xd9, 0xcf, 0x4a, 0x5f, 0x44, 0x57, 0xf6, 0x49, 0x47, 0xae, 0x16, 0x81, 0xa1, 0xa7, 0x2d, 0xd8, - 0xb3, 0x35, 0x74, 0x6a, 0xc2, 0xbc, 0xc0, 0x7d, 0xae, 0xf0, 0x88, 0xe4, 0xd0, 0x28, 0x7c, 0x22, 0x98, 0x95, 0xa3, - 0x2a, 0x81, 0x16, 0x0b, 0xc7, 0x4a, 0xf3, 0x07, 0xb8, 0xa1, 0x56, 0xbf, 0xdf, 0x36, 0x6b, 0xa3, 0x84, 0x8b, 0xbf, - 0x24, 0x99, 0xc1, 0x09, 0x7e, 0xff, 0x99, 0x8c, 0x1c, 0xd1, 0x43, 0x7c, 0xb1, 0x46, 0x9d, 0x2e, 0x65, 0x92, 0xa9, - 0xa0, 0xd0, 0x45, 0x92, 0x47, 0x37, 0x9c, 0x3c, 0x5f, 0xf1, 0xf3, 0x0d, 0x1e, 0x37, 0xeb, 0x3d, 0xb6, 0x7c, 0x33, - 0x35, 0xaf, 0xf3, 0x08, 0x54, 0x33, 0x56, 0x02, 0x4f, 0x18, 0xda, 0xc0, 0xbb, 0xc5, 0x4a, 0x42, 0xd7, 0xef, 0xbd, - 0xa4, 0xec, 0x61, 0x77, 0x1b, 0xa2, 0x57, 0x47, 0x00, 0xee, 0x8b, 0xd3, 0x56, 0xd4, 0xbd, 0x01, 0xa2, 0x8f, 0xee, - 0xef, 0xb1, 0xac, 0xe1, 0x03, 0x87, 0x8d, 0x2b, 0x3c, 0x8e, 0x15, 0x84, 0x96, 0xb4, 0xfe, 0x56, 0xd5, 0x1e, 0xc0, - 0x83, 0x66, 0x79, 0x15, 0x8a, 0x60, 0xb7, 0x15, 0x21, 0x3b, 0xce, 0x44, 0x71, 0x9f, 0x6f, 0xe0, 0x70, 0xde, 0xb8, - 0x7a, 0x74, 0x43, 0xcd, 0x4d, 0x7a, 0x90, 0xd2, 0x4b, 0x9b, 0xc8, 0x6a, 0xa2, 0xc6, 0xdf, 0x1a, 0x55, 0x85, 0x34, - 0xdd, 0x1d, 0x1a, 0x7c, 0x88, 0x7c, 0x81, 0x3d, 0xd8, 0x12, 0xf4, 0x32, 0x8d, 0xc6, 0xc1, 0x56, 0x0d, 0xe5, 0x8d, - 0x75, 0x00, 0x03, 0x61, 0x93, 0xa0, 0x44, 0x06, 0x5b, 0x67, 0x8b, 0x58, 0xf5, 0x9c, 0xb0, 0x79, 0x7f, 0xbd, 0x2e, - 0xb1, 0x17, 0xb3, 0x85, 0xcd, 0x54, 0x5f, 0xc8, 0x38, 0xeb, 0xa7, 0xcd, 0xfe, 0xbf, 0x2d, 0x80, 0x83, 0x12, 0xc3, - 0x0b, 0x02, 0x41, 0x44, 0xd5, 0x07, 0xe5, 0xcd, 0xb0, 0x24, 0x2c, 0x0a, 0x6c, 0x1b, 0x1f, 0xb9, 0x7b, 0x48, 0x9e, - 0x55, 0x42, 0x7c, 0x2b, 0x63, 0xd3, 0xd1, 0x76, 0x18, 0x61, 0xa8, 0x86, 0x2d, 0x11, 0x5a, 0x41, 0x04, 0x6c, 0xea, - 0xcf, 0x34, 0xf6, 0x71, 0xe7, 0xda, 0x41, 0xba, 0x28, 0xcb, 0x2c, 0x1c, 0x47, 0x50, 0xa7, 0x83, 0x41, 0xad, 0x84, - 0x9e, 0xec, 0x1e, 0xfc, 0xc6, 0xc6, 0xb8, 0xa0, 0xb8, 0xa1, 0x70, 0xeb, 0x5a, 0x9f, 0x46, 0x06, 0xa6, 0xf8, 0x72, - 0xa5, 0xff, 0xfe, 0x80, 0x1e, 0x07, 0xbb, 0xd4, 0x48, 0xf9, 0x6c, 0xd6, 0x13, 0xf8, 0xee, 0x86, 0x06, 0x67, 0x78, - 0x38, 0xf2, 0x83, 0xc3, 0x3b, 0x25, 0xf0, 0xa0, 0x60, 0x56, 0xbe, 0x7d, 0x10, 0x8a, 0xd4, 0x17, 0x81, 0x0e, 0x17, - 0x5f, 0x53, 0xaf, 0x87, 0x23, 0xe4, 0x56, 0xe4, 0xb1, 0xc0, 0x9d, 0xc8, 0x38, 0x25, 0x47, 0x18, 0x18, 0x27, 0x57, - 0xdf, 0x84, 0xcd, 0x7c, 0xdb, 0x31, 0xff, 0xc2, 0xe5, 0x83, 0x03, 0xd1, 0xac, 0x9f, 0x2d, 0xd8, 0xa5, 0xa4, 0x40, - 0xe0, 0x1e, 0xe9, 0x18, 0x3d, 0x85, 0xa9, 0x1b, 0x9c, 0xa6, 0x14, 0x51, 0x9a, 0x88, 0xd9, 0x42, 0x70, 0x6c, 0x6b, - 0x63, 0x2e, 0xfd, 0x46, 0xd9, 0xd5, 0xb4, 0xd9, 0x8f, 0x2c, 0xf8, 0x42, 0xf9, 0xb6, 0x27, 0xb8, 0x69, 0xc5, 0xed, - 0x5c, 0xea, 0xff, 0x76, 0x9d, 0x49, 0x1b, 0x5a, 0xf7, 0xaa, 0x2d, 0x04, 0x36, 0x45, 0x05, 0xa8, 0x99, 0x5e, 0x24, - 0x53, 0x3b, 0x89, 0xd9, 0x0f, 0x4d, 0x24, 0x27, 0x44, 0x2b, 0xfb, 0x3b, 0xd9, 0x8b, 0x36, 0xe9, 0x18, 0x4a, 0x30, - 0xfc, 0xc8, 0xa5, 0xf4, 0xd5, 0xd5, 0x72, 0x2d, 0x3b, 0x5f, 0xc3, 0x4e, 0x98, 0x0c, 0x08, 0xb2, 0xff, 0x59, 0x68, - 0x6b, 0xc0, 0xe4, 0x52, 0x8f, 0x29, 0x78, 0x74, 0x6d, 0xbe, 0xfb, 0x13, 0x8a, 0x25, 0x0b, 0x31, 0xe5, 0xd0, 0xae, - 0xbf, 0x1e, 0x13, 0x23, 0xa0, 0x2c, 0x89, 0x10, 0x6e, 0xa5, 0x1c, 0xa8, 0x7f, 0xbf, 0x62, 0x52, 0xb0, 0xa5, 0xf6, - 0x46, 0x9c, 0x5d, 0xbd, 0xac, 0x81, 0xc4, 0x46, 0xf3, 0x61, 0x62, 0x47, 0x08, 0x27, 0x4d, 0xed, 0x4b, 0x45, 0x91, - 0x48, 0xcf, 0x52, 0xc4, 0x20, 0xe3, 0x8a, 0xe9, 0x12, 0x2d, 0xac, 0x99, 0xb0, 0x1c, 0x1b, 0x91, 0xa4, 0xcc, 0x6d, - 0x11, 0x3f, 0xbe, 0xe9, 0x06, 0x24, 0x40, 0x3d, 0x62, 0x90, 0x0f, 0xbe, 0x25, 0x20, 0xd7, 0x25, 0x09, 0xca, 0xb5, - 0xcf, 0x25, 0x64, 0x42, 0x3b, 0x19, 0x09, 0x13, 0xf3, 0x46, 0x90, 0x72, 0xf7, 0x74, 0x4a, 0xd7, 0x00, 0x4b, 0x39, - 0x59, 0xcd, 0x21, 0x62, 0xe4, 0x78, 0x5d, 0x75, 0xb5, 0x80, 0x58, 0x0a, 0xb7, 0xa3, 0xed, 0xc8, 0xe4, 0x5c, 0xdc, - 0xa1, 0xf3, 0xce, 0x99, 0x5f, 0x18, 0xa7, 0x1c, 0x9c, 0x1e, 0xe6, 0x2e, 0x20, 0x20, 0xa7, 0xae, 0xfa, 0xc1, 0x19, - 0x19, 0xa4, 0xb8, 0x9a, 0x77, 0x5a, 0x24, 0x9a, 0x11, 0xf9, 0xac, 0x18, 0xaa, 0xdb, 0x2a, 0x37, 0xc2, 0x62, 0xad, - 0x5c, 0x82, 0x29, 0x72, 0x72, 0x1b, 0x7c, 0xb3, 0x83, 0xc7, 0xcd, 0x13, 0x16, 0xc0, 0x59, 0x8f, 0xe5, 0x62, 0xc2, - 0xa1, 0xea, 0x36, 0x7e, 0x0d, 0x64, 0x0a, 0xbc, 0x72, 0xd4, 0x59, 0x92, 0xe3, 0x0b, 0x0d, 0xaa, 0x81, 0xbf, 0xf6, - 0x91, 0xe7, 0x41, 0x6e, 0x50, 0x35, 0xd5, 0x34, 0x8b, 0x42, 0x4f, 0x31, 0xcf, 0x84, 0xcc, 0x5f, 0x35, 0x8a, 0x4e, - 0xc2, 0x8c, 0xa7, 0xc9, 0x96, 0x3a, 0xdd, 0xa7, 0x32, 0xa1, 0x80, 0xd8, 0x43, 0xe0, 0x14, 0xb8, 0xf7, 0xa6, 0xc2, - 0x3c, 0x9d, 0x92, 0x49, 0x1c, 0x9e, 0xcc, 0xb3, 0x59, 0x03, 0x66, 0xc0, 0x8d, 0x12, 0xe8, 0xe6, 0x8c, 0xfc, 0x70, - 0x0b, 0xb7, 0x55, 0x71, 0x1e, 0x93, 0x15, 0x68, 0xc9, 0x4d, 0x04, 0xc9, 0xf0, 0xca, 0xb8, 0x80, 0xd2, 0x7b, 0x13, - 0x67, 0xc6, 0xdd, 0xe2, 0xab, 0x8a, 0x4f, 0xc0, 0x79, 0xdc, 0x97, 0xdb, 0x8a, 0x53, 0x9a, 0x2a, 0x1c, 0x80, 0x92, - 0x17, 0xc4, 0x63, 0xe1, 0x9b, 0xd3, 0x2b, 0x99, 0x61, 0xe0, 0x62, 0x46, 0x35, 0x15, 0xdd, 0x85, 0x74, 0xc2, 0x74, - 0x90, 0xf0, 0x96, 0x34, 0x06, 0x77, 0x40, 0xf1, 0xbe, 0x00, 0x14, 0x11, 0x8e, 0xc2, 0x77, 0x76, 0x4c, 0x47, 0xab, - 0x92, 0xf0, 0x68, 0x99, 0x2d, 0xda, 0x79, 0xf9, 0x46, 0x25, 0xab, 0x1c, 0x70, 0x34, 0x00, 0x6c, 0x5e, 0x7f, 0x48, - 0x7c, 0x06, 0x81, 0x1c, 0x1f, 0x27, 0x76, 0x3e, 0x34, 0x4d, 0x95, 0xc2, 0x9f, 0x8d, 0xf6, 0x26, 0x2c, 0x70, 0xc7, - 0x29, 0x13, 0x3a, 0x1e, 0x1b, 0xd1, 0x0d, 0x41, 0xe7, 0x5f, 0xb0, 0x03, 0xb6, 0xda, 0x66, 0x7b, 0xbf, 0x7a, 0xbd, - 0x2c, 0x4e, 0x0e, 0x98, 0xe4, 0x9d, 0xcd, 0xbd, 0xb7, 0xdb, 0xf9, 0x2f, 0x27, 0x1f, 0x99, 0xb0, 0x40, 0xe1, 0x55, - 0x4e, 0x59, 0x64, 0x24, 0x3a, 0xa0, 0xc4, 0x2b, 0x4d, 0xe7, 0x62, 0x74, 0x2d, 0x12, 0xaf, 0x4a, 0xb1, 0x2b, 0x24, - 0xa9, 0x61, 0xe6, 0x0d, 0x38, 0xc8, 0x66, 0x9d, 0xa6, 0x46, 0x41, 0x11, 0xb2, 0xcc, 0xc5, 0xc6, 0x2c, 0xb1, 0x58, - 0xf3, 0x96, 0x33, 0x6d, 0x4e, 0x61, 0x44, 0xe0, 0xe4, 0x80, 0xa8, 0xfe, 0xac, 0xd6, 0xd8, 0xe0, 0xd6, 0xf3, 0x6a, - 0x18, 0x61, 0xe0, 0xdd, 0x50, 0x92, 0xf2, 0xc4, 0x18, 0x2b, 0x21, 0xc9, 0xa9, 0x23, 0x8e, 0xfd, 0xc8, 0xf2, 0x15, - 0xdf, 0xef, 0xb9, 0xc6, 0x14, 0x97, 0x07, 0x13, 0x63, 0x16, 0x43, 0xa6, 0x76, 0x83, 0xca, 0x22, 0xa9, 0x9f, 0x8e, - 0x6a, 0x59, 0x39, 0x93, 0x7b, 0x0c, 0xf7, 0x51, 0xe7, 0x92, 0xbc, 0x7b, 0x74, 0x05, 0x01, 0xd9, 0x5d, 0x82, 0xd5, - 0x27, 0x87, 0x24, 0x8a, 0x9c, 0xb0, 0x9f, 0xeb, 0x5f, 0x56, 0x23, 0x7f, 0x95, 0x63, 0x29, 0x5f, 0x0f, 0x79, 0x4b, - 0x19, 0x62, 0x2a, 0xad, 0xe5, 0x9e, 0x53, 0x90, 0x71, 0xe9, 0xb2, 0x6c, 0xf1, 0x40, 0x2c, 0x21, 0x7c, 0xb0, 0x98, - 0x7d, 0xde, 0x2e, 0x1c, 0xc8, 0xa4, 0x50, 0x7e, 0xdf, 0xe5, 0xf2, 0xfc, 0xf0, 0xc9, 0x46, 0x2d, 0xc6, 0x18, 0xa7, - 0x54, 0x5b, 0xdf, 0x38, 0xa8, 0x15, 0xa3, 0x0d, 0x3c, 0xbf, 0xb0, 0xdd, 0x6e, 0x6f, 0xd9, 0x2b, 0x20, 0x2b, 0x6b, - 0x2b, 0x70, 0x53, 0x27, 0x9d, 0x1f, 0x36, 0xc2, 0x49, 0x13, 0xca, 0x40, 0x7a, 0x39, 0x43, 0x2d, 0x90, 0x44, 0x37, - 0x45, 0x2d, 0x8c, 0x16, 0xeb, 0x5b, 0x8e, 0xbe, 0xf5, 0x6b, 0x18, 0x32, 0x4a, 0xe9, 0x98, 0xa6, 0xc3, 0xbd, 0x2e, - 0xd9, 0x77, 0x11, 0x44, 0x8b, 0x6c, 0xd6, 0x6c, 0xc3, 0x2f, 0x52, 0xae, 0xbd, 0x10, 0xde, 0x92, 0xe6, 0x28, 0xf2, - 0xce, 0x11, 0xa9, 0xa5, 0x70, 0xaa, 0xa9, 0xc7, 0x24, 0x1c, 0xbb, 0x04, 0x5a, 0x29, 0x98, 0xee, 0xe1, 0xc5, 0x4e, - 0xb6, 0xa1, 0xc2, 0x22, 0x2d, 0x04, 0x69, 0x14, 0x73, 0xf8, 0xfd, 0x20, 0x11, 0x59, 0x06, 0xd8, 0x79, 0xd4, 0xe7, - 0xc0, 0x1e, 0x0e, 0xe3, 0xd1, 0x55, 0x11, 0xfb, 0xee, 0x0f, 0x13, 0x14, 0x62, 0x9d, 0xa6, 0x09, 0x0a, 0xf7, 0x7c, - 0x84, 0x8e, 0xcd, 0x31, 0x3f, 0x9d, 0x85, 0xb6, 0x7d, 0xb3, 0x7a, 0x28, 0xbc, 0xfc, 0x2e, 0xc5, 0x3e, 0xa5, 0xb7, - 0xf3, 0x2f, 0xd3, 0x39, 0xc5, 0x3c, 0xb3, 0xeb, 0x14, 0x53, 0xa1, 0x89, 0xcd, 0x85, 0x97, 0x28, 0x12, 0xe7, 0xc3, - 0x4b, 0x69, 0xe0, 0xcd, 0xd0, 0x29, 0x91, 0x60, 0xdc, 0x08, 0x4f, 0x62, 0x14, 0x61, 0x0d, 0x98, 0xee, 0xe6, 0x3d, - 0x3b, 0xa9, 0x38, 0x77, 0xca, 0x92, 0x2b, 0xba, 0xfb, 0xb9, 0x41, 0x00, 0x40, 0xc5, 0xce, 0xe3, 0x0b, 0x9f, 0x2c, - 0xaf, 0xe5, 0xf4, 0x1c, 0x57, 0x86, 0x10, 0x5a, 0x1a, 0x71, 0x42, 0xb0, 0x91, 0x4a, 0x60, 0x5b, 0x61, 0xb9, 0x53, - 0x25, 0x4a, 0x06, 0x7d, 0x60, 0x8c, 0xec, 0x0e, 0x8a, 0x13, 0xd9, 0x10, 0xda, 0x9a, 0x8e, 0x08, 0x62, 0xe6, 0x7f, - 0x1f, 0xe4, 0x0c, 0xf0, 0xf8, 0xad, 0x64, 0x48, 0x85, 0x00, 0xcd, 0x1c, 0x2f, 0x2f, 0x03, 0x76, 0x31, 0xc4, 0x95, - 0xb2, 0xb8, 0x00, 0xc4, 0x66, 0x1f, 0x9b, 0x71, 0x90, 0xfb, 0x41, 0xea, 0x1c, 0xd6, 0x65, 0x07, 0xa2, 0xd2, 0x0b, - 0xe1, 0xf9, 0x35, 0x0d, 0xd5, 0xa4, 0x09, 0xe3, 0x75, 0xcc, 0x20, 0x66, 0x45, 0x69, 0x23, 0x05, 0x61, 0x95, 0x82, - 0x43, 0x60, 0x8e, 0x34, 0xcb, 0x84, 0xfa, 0xd2, 0x22, 0xc1, 0x1b, 0x0a, 0x01, 0xdb, 0xf1, 0x5a, 0xa2, 0x01, 0x1c, - 0x18, 0xb3, 0x61, 0x87, 0xa4, 0x95, 0x05, 0xf5, 0x40, 0xf9, 0x59, 0x5f, 0x78, 0x3c, 0x23, 0x99, 0xf0, 0xc1, 0x03, - 0x47, 0xf3, 0xa5, 0xa8, 0xe7, 0xe6, 0x44, 0x4b, 0xda, 0xe4, 0x10, 0x7f, 0x22, 0xaa, 0xb5, 0x68, 0xc5, 0x03, 0x5f, - 0x01, 0xa8, 0x90, 0xa6, 0x82, 0x4a, 0x64, 0x49, 0x59, 0x54, 0x64, 0x1e, 0x4c, 0xcc, 0xb5, 0x05, 0x56, 0xd6, 0xa8, - 0x77, 0xfd, 0x08, 0x7e, 0xa6, 0x84, 0x4a, 0xad, 0x3b, 0xc4, 0x3f, 0x65, 0xfc, 0x35, 0x84, 0x14, 0x99, 0x9f, 0x19, - 0xb9, 0xcb, 0x63, 0x9e, 0x3d, 0xb2, 0xfa, 0xb7, 0x7d, 0x1b, 0x8e, 0x2e, 0xdc, 0x63, 0x92, 0xab, 0xf7, 0x48, 0x64, - 0x64, 0x33, 0x01, 0x41, 0xb7, 0xf1, 0x7a, 0x38, 0x4a, 0xc5, 0x1f, 0x9b, 0x14, 0x6f, 0xab, 0x0a, 0xa2, 0xf6, 0xa2, - 0x85, 0x54, 0x93, 0x9e, 0x03, 0x69, 0xd0, 0x9c, 0x34, 0x6a, 0xd0, 0xb5, 0x07, 0x2a, 0x54, 0x04, 0xe5, 0x8f, 0x0e, - 0x27, 0x26, 0xca, 0xf0, 0x14, 0xee, 0x2f, 0x4c, 0x46, 0x98, 0x39, 0x02, 0x55, 0xed, 0xa2, 0xe5, 0xf3, 0x16, 0x63, - 0x02, 0x79, 0xef, 0xfe, 0x0d, 0xf3, 0x23, 0xaa, 0x61, 0xb3, 0xe5, 0xbf, 0xf9, 0x33, 0x4f, 0x29, 0x2a, 0x27, 0xc2, - 0x54, 0x48, 0xa8, 0xb1, 0xb3, 0xa4, 0xd0, 0xa3, 0x8b, 0x58, 0xc3, 0x2c, 0x3f, 0x59, 0x73, 0x75, 0x63, 0x08, 0x19, - 0x23, 0x10, 0x9c, 0x21, 0xc4, 0xa8, 0xf2, 0x44, 0x39, 0x78, 0x9b, 0x00, 0xb8, 0x04, 0xf5, 0x18, 0x2c, 0x73, 0xfb, - 0x12, 0xa1, 0xb9, 0x9c, 0xf7, 0x1f, 0x71, 0x29, 0x19, 0x29, 0x7e, 0x96, 0x97, 0x59, 0xf7, 0x52, 0x79, 0x2a, 0x6e, - 0x5d, 0xd1, 0x56, 0xdc, 0x06, 0x0f, 0x98, 0x82, 0x3e, 0xca, 0x4a, 0x6d, 0xf4, 0x69, 0x16, 0x35, 0xbb, 0x64, 0xdb, - 0x63, 0x77, 0x63, 0x79, 0x52, 0x70, 0x27, 0xbc, 0x84, 0x69, 0x19, 0x4a, 0xdd, 0x36, 0x5e, 0x8a, 0x7d, 0x38, 0xc7, - 0x79, 0xf9, 0x2d, 0x53, 0xbc, 0xff, 0x8e, 0xe2, 0xd3, 0xd7, 0x2c, 0xcd, 0x30, 0x3f, 0x3a, 0x2a, 0x94, 0xc0, 0xcc, - 0x7a, 0xac, 0xe6, 0x51, 0x12, 0x6b, 0x28, 0x40, 0x87, 0x05, 0x43, 0x7b, 0xbc, 0xde, 0x82, 0x78, 0xa8, 0xb2, 0x1e, - 0x2c, 0xbc, 0x27, 0x33, 0x74, 0xb5, 0xa4, 0x0d, 0xad, 0x6b, 0xa2, 0xa2, 0x4f, 0xef, 0x91, 0xc5, 0xdd, 0xfa, 0x38, - 0x4e, 0x9a, 0x18, 0x15, 0x97, 0xa2, 0xdd, 0x59, 0x37, 0x5e, 0x84, 0x99, 0x78, 0x8c, 0x9c, 0x11, 0x19, 0x6b, 0xe9, - 0x8c, 0x96, 0xdc, 0xba, 0x04, 0x99, 0x4f, 0x06, 0x7a, 0x27, 0x50, 0x7e, 0xfe, 0x28, 0x07, 0x8e, 0x08, 0x00, 0xb5, - 0xdb, 0x16, 0x84, 0x2c, 0x38, 0xf0, 0xbf, 0x2c, 0xb7, 0x7d, 0x9f, 0xbc, 0xb3, 0x7c, 0x31, 0x2b, 0xe0, 0x7c, 0x63, - 0xa3, 0xbd, 0x11, 0xb7, 0xb3, 0x91, 0x0d, 0x1d, 0x4f, 0x24, 0xd4, 0x1f, 0x66, 0xe6, 0xd9, 0x31, 0x1f, 0x18, 0x09, - 0xce, 0x46, 0x47, 0x60, 0xbb, 0x2a, 0x73, 0xfd, 0x37, 0x49, 0xde, 0x59, 0x12, 0x23, 0x5c, 0x51, 0x81, 0xac, 0x1e, - 0x64, 0x41, 0xb0, 0xb8, 0xa5, 0x2b, 0xba, 0xde, 0xe7, 0x15, 0x89, 0x36, 0x91, 0x29, 0xb9, 0x1e, 0x20, 0xa0, 0xab, - 0x57, 0x3d, 0x3e, 0x55, 0xf7, 0xc6, 0xfb, 0x52, 0xe3, 0x85, 0xf6, 0xf7, 0xb5, 0x9d, 0x3a, 0xdc, 0xbc, 0xe7, 0x7a, - 0x9e, 0xf6, 0x8f, 0x12, 0x75, 0x7a, 0x2d, 0x32, 0x9e, 0x87, 0xfb, 0x35, 0x94, 0xd3, 0x48, 0x35, 0x69, 0x25, 0xa1, - 0xb8, 0x11, 0xcb, 0xbc, 0xe3, 0x96, 0x07, 0xbc, 0x0a, 0xbf, 0x69, 0xb5, 0x10, 0xef, 0x7d, 0x64, 0x58, 0x9e, 0x7a, - 0x77, 0x34, 0x61, 0xf6, 0x50, 0x2b, 0xbb, 0x29, 0x26, 0x60, 0x3f, 0xad, 0xfe, 0xc9, 0xae, 0xca, 0x17, 0x17, 0xf3, - 0x3f, 0xbb, 0xae, 0xb2, 0x91, 0xf1, 0x64, 0x9a, 0xf5, 0xdd, 0xf3, 0xf9, 0x87, 0x3f, 0x7b, 0x1a, 0x4e, 0xe6, 0xc3, - 0x2c, 0xda, 0x7f, 0x29, 0xbf, 0x2c, 0x1a, 0x42, 0x5b, 0xfe, 0xe8, 0x2c, 0xf5, 0xcc, 0x42, 0xef, 0x85, 0x00, 0x3e, - 0x2d, 0xda, 0x1d, 0x1c, 0xd5, 0x74, 0x3d, 0x8c, 0xf7, 0x41, 0x0b, 0xb7, 0x2e, 0x77, 0x20, 0xce, 0x6c, 0xc4, 0x49, - 0x7e, 0x51, 0xb3, 0xeb, 0x5d, 0x36, 0xb3, 0x69, 0x97, 0xbf, 0x23, 0x08, 0x9f, 0x4d, 0x90, 0xd1, 0x3a, 0xd6, 0x36, - 0xa9, 0xbc, 0x0a, 0x2c, 0xff, 0x8d, 0xe4, 0xd3, 0xb9, 0x36, 0x8c, 0x6e, 0x05, 0xfb, 0xf9, 0xa7, 0xf0, 0x6d, 0xd5, - 0x77, 0xc7, 0x9d, 0xff, 0x7c, 0xba, 0x5f, 0xfd, 0x3b, 0xd5, 0x93, 0xb9, 0x59, 0xf4, 0xde, 0xf3, 0xe0, 0xfb, 0xd3, - 0xa0, 0xdb, 0xb6, 0x7a, 0xb2, 0x7d, 0xfc, 0x7f, 0x17, 0xef, 0xff, 0x27, 0xfa, 0xfa, 0x15, 0x7b, 0x64, 0x3e, 0xf4, - 0xe2, 0xf8, 0x6c, 0xea, 0xe2, 0xf2, 0xff, 0x5c, 0xab, 0xdb, 0x3b, 0xcf, 0x6a, 0xb4, 0x6b, 0x6e, 0xb9, 0xee, 0xb5, - 0xed, 0x32, 0xaa, 0x9c, 0xc0, 0xad, 0xff, 0x7a, 0xea, 0x2b, 0x08, 0xf2, 0x79, 0xe3, 0xe5, 0x7e, 0xf6, 0xf0, 0xb6, - 0x26, 0xb3, 0xcf, 0x96, 0x7b, 0x27, 0x2f, 0xfc, 0xbc, 0x1e, 0x6c, 0xfb, 0x54, 0xea, 0x28, 0xd5, 0xac, 0xf8, 0x61, - 0x4d, 0x72, 0xb6, 0x2b, 0xe7, 0xfc, 0xd3, 0xf2, 0x79, 0xfd, 0xff, 0xc5, 0xf3, 0x6f, 0x76, 0xba, 0x47, 0xd8, 0x6f, - 0x61, 0x5f, 0x63, 0x3f, 0xab, 0x4f, 0xe6, 0x70, 0xf5, 0x29, 0xde, 0xba, 0xee, 0x6d, 0xb5, 0x6b, 0xee, 0xe2, 0x8a, - 0x39, 0x75, 0xc9, 0x95, 0xb3, 0x7a, 0x2a, 0x88, 0x0b, 0x62, 0x11, 0x48, 0x7c, 0x57, 0xff, 0x1d, 0xdc, 0xd5, 0x43, - 0xef, 0x95, 0x8b, 0x6d, 0xcf, 0x6d, 0x02, 0xec, 0x9a, 0xc8, 0x8c, 0x11, 0x2b, 0x62, 0x1b, 0xed, 0x28, 0xf0, 0x01, - 0x8b, 0xb6, 0xcf, 0x9f, 0x12, 0x9f, 0xf5, 0xbb, 0x90, 0x6b, 0x70, 0x7d, 0x7f, 0xfd, 0x5a, 0x3c, 0x93, 0x3f, 0x08, - 0x29, 0xfa, 0xf9, 0xc0, 0x53, 0xfd, 0x8b, 0x54, 0x98, 0x42, 0x54, 0x27, 0x2c, 0x6b, 0x86, 0x7d, 0x65, 0xdf, 0x47, - 0x8b, 0x43, 0x05, 0x6a, 0x3d, 0xde, 0x27, 0x44, 0x3e, 0xaa, 0x48, 0x77, 0xf9, 0x77, 0x2a, 0x82, 0xc0, 0x88, 0x28, - 0x30, 0x34, 0xc8, 0xa7, 0x9d, 0x03, 0x7f, 0x81, 0xe5, 0xfe, 0xa2, 0xb8, 0x7a, 0xb7, 0x4b, 0x4a, 0x35, 0x5c, 0xcd, - 0x1b, 0xeb, 0x14, 0x20, 0x25, 0x36, 0x11, 0x01, 0xc3, 0x7f, 0x72, 0xe5, 0x0b, 0xb6, 0x5e, 0xe7, 0x69, 0x3e, 0x19, - 0x55, 0xa7, 0x26, 0x37, 0x5b, 0x0b, 0x23, 0x5d, 0x0b, 0xaf, 0x3a, 0x3a, 0xe1, 0x94, 0x63, 0xcc, 0x57, 0x94, 0x36, - 0x1e, 0x71, 0xa7, 0xfe, 0xf6, 0x2a, 0xfe, 0xdc, 0x80, 0x84, 0x72, 0xcb, 0x6c, 0x70, 0x95, 0x99, 0xbd, 0x56, 0x70, - 0xa3, 0x24, 0x4c, 0xf1, 0x12, 0xf2, 0x8c, 0xdf, 0x73, 0xb1, 0x32, 0x05, 0xb6, 0x69, 0x7a, 0xff, 0xc7, 0xf6, 0x24, - 0x28, 0x04, 0x3a, 0xf1, 0x52, 0x80, 0x04, 0xaa, 0xbe, 0x21, 0xc8, 0x37, 0x3d, 0xe2, 0xa0, 0xad, 0xa9, 0x8d, 0xf3, - 0x4c, 0xb3, 0x68, 0x33, 0xde, 0xd5, 0x95, 0x92, 0xb9, 0x9e, 0x11, 0x8d, 0xbc, 0xb6, 0xed, 0xf5, 0x66, 0xcc, 0xec, - 0x7a, 0x38, 0x07, 0x74, 0xbe, 0x0e, 0xa0, 0x9f, 0x55, 0x07, 0x96, 0x2a, 0x4e, 0x7f, 0xb9, 0x03, 0x32, 0xb0, 0xa4, - 0x43, 0x0f, 0x53, 0x0a, 0x9f, 0xa5, 0xf4, 0x1f, 0x01, 0x3b, 0x05, 0x0e, 0x4b, 0x39, 0x16, 0xd9, 0x8d, 0xd9, 0xcc, - 0xda, 0xd6, 0xe4, 0xa4, 0x33, 0x17, 0x51, 0xf6, 0x7c, 0x6d, 0x57, 0xff, 0x5d, 0xac, 0xfa, 0x78, 0xc9, 0xc6, 0x29, - 0x20, 0x05, 0x05, 0x05, 0xb1, 0xc7, 0xf2, 0xf3, 0xd0, 0x2f, 0x19, 0x91, 0x41, 0x34, 0xbd, 0x1a, 0x74, 0xfa, 0x79, - 0xd2, 0x5f, 0x58, 0x94, 0xd4, 0x4a, 0xe8, 0x0f, 0xb8, 0xe1, 0x03, 0x77, 0xe7, 0x8c, 0x38, 0x37, 0x84, 0xfc, 0x34, - 0xf5, 0x23, 0xe6, 0x6e, 0xe6, 0x64, 0x16, 0x3f, 0x07, 0x72, 0xd2, 0x82, 0xd5, 0xf8, 0x13, 0x36, 0x82, 0xdc, 0x37, - 0x7e, 0x69, 0xa9, 0x02, 0x47, 0x97, 0x2b, 0xe9, 0xe9, 0xd2, 0xc9, 0x62, 0xa1, 0x75, 0x70, 0x77, 0x8a, 0xe0, 0x8b, - 0xb7, 0xc2, 0x3a, 0xff, 0xe5, 0x72, 0xeb, 0xae, 0x38, 0x1b, 0x36, 0xa2, 0x7e, 0x76, 0xbf, 0xba, 0x75, 0x1a, 0xbe, - 0x47, 0xbf, 0x8b, 0x57, 0xdf, 0xf5, 0x78, 0x21, 0x47, 0xb7, 0x6e, 0x0d, 0x56, 0xf3, 0x74, 0x05, 0x45, 0x33, 0xb0, - 0x2f, 0x5e, 0x94, 0x83, 0x2f, 0x60, 0x34, 0xee, 0x78, 0x11, 0x6c, 0x0a, 0xed, 0xf3, 0xce, 0xf3, 0xe5, 0x15, 0x55, - 0x93, 0x85, 0xf4, 0x76, 0xcd, 0xc6, 0xf0, 0xfe, 0xba, 0xbd, 0xf9, 0x59, 0xfc, 0x54, 0x7c, 0x85, 0x46, 0xe8, 0xb7, - 0x0b, 0x40, 0xba, 0xfa, 0x92, 0x15, 0x8f, 0x3e, 0x6f, 0x85, 0x58, 0xcd, 0xf7, 0x8e, 0x67, 0xb1, 0x42, 0xe0, 0xe3, - 0x5b, 0xd1, 0xeb, 0x25, 0x3c, 0xb3, 0x9a, 0x75, 0x82, 0xb9, 0x03, 0x88, 0x50, 0x44, 0x1a, 0x34, 0x11, 0xe4, 0xbb, - 0xb3, 0x61, 0x2e, 0x54, 0x67, 0x35, 0x2f, 0xff, 0xbc, 0xbc, 0xa2, 0x1e, 0x51, 0x2f, 0x7e, 0x73, 0xbd, 0x81, 0x44, - 0xab, 0xae, 0x85, 0x1a, 0xbf, 0x4b, 0x8d, 0x53, 0xe6, 0x41, 0x03, 0x24, 0x69, 0xe8, 0x50, 0xcb, 0xfb, 0x10, 0x8f, - 0x8b, 0xed, 0x6b, 0x34, 0x7e, 0xdf, 0xc4, 0x5a, 0x11, 0x15, 0x79, 0x4f, 0x81, 0x2c, 0x0e, 0x94, 0x50, 0x5a, 0x5e, - 0xc8, 0xdf, 0x15, 0xa6, 0x5a, 0x64, 0xce, 0xc3, 0xc4, 0x59, 0xa0, 0xfe, 0x7a, 0xf4, 0xd4, 0xfb, 0x52, 0x07, 0x7c, - 0x63, 0x61, 0x03, 0xd7, 0x73, 0x43, 0x8d, 0x60, 0x1c, 0xa4, 0x48, 0x35, 0xa8, 0x0d, 0x33, 0x6a, 0x30, 0x79, 0x4d, - 0xc7, 0x96, 0x62, 0xaf, 0xa4, 0x3f, 0xe4, 0x99, 0x2e, 0xac, 0xf2, 0x6d, 0xd5, 0x23, 0x3b, 0xbd, 0x8d, 0x0b, 0xfe, - 0x59, 0xf3, 0xde, 0x7c, 0x8d, 0x78, 0xba, 0x6c, 0x48, 0xb0, 0xa4, 0xe7, 0xc9, 0x71, 0xeb, 0xfc, 0xa2, 0xba, 0x9e, - 0xab, 0x78, 0x04, 0x99, 0x12, 0x2e, 0x09, 0xb9, 0x8e, 0x73, 0xbd, 0x97, 0x30, 0x10, 0x21, 0x5f, 0xd7, 0x67, 0x09, - 0x50, 0xda, 0xd6, 0x97, 0x50, 0x0b, 0x1f, 0x4a, 0xb3, 0xaf, 0xdd, 0x12, 0x8e, 0xcc, 0x7d, 0x57, 0xec, 0x5d, 0x1d, - 0x39, 0x5d, 0x38, 0x24, 0x15, 0xa0, 0xef, 0xb9, 0xe8, 0xf0, 0x8d, 0xb1, 0xb0, 0x48, 0xc4, 0xa6, 0x37, 0xd1, 0x8d, - 0x66, 0xdd, 0xe0, 0x57, 0x27, 0x9d, 0x06, 0x5f, 0x1b, 0x38, 0x15, 0xb1, 0xa6, 0x00, 0x3b, 0xf4, 0xb8, 0xb1, 0x3b, - 0x40, 0x88, 0x6f, 0x5a, 0xed, 0x84, 0xce, 0xd6, 0x8d, 0x23, 0x14, 0x04, 0x03, 0xea, 0x45, 0xd4, 0x8a, 0xf2, 0xfb, - 0x4a, 0x27, 0x11, 0xf5, 0x06, 0xbf, 0x7b, 0x7d, 0xef, 0xe5, 0xa9, 0x2a, 0xbe, 0xde, 0xab, 0x07, 0x7a, 0xb2, 0x1d, - 0x90, 0xd8, 0x34, 0x15, 0x6b, 0x40, 0x93, 0x67, 0x4e, 0x81, 0xb8, 0xc1, 0x3c, 0xf7, 0x61, 0x3f, 0xc6, 0x7c, 0x8d, - 0x40, 0x8d, 0x4e, 0x84, 0x6a, 0x49, 0xa4, 0x2f, 0x57, 0x2a, 0x63, 0xdd, 0x2c, 0xe2, 0xd9, 0x45, 0x93, 0xf7, 0x7f, - 0x6f, 0x1c, 0x5c, 0xd7, 0x54, 0x50, 0x6e, 0xda, 0x33, 0xff, 0xeb, 0x9a, 0xc2, 0x06, 0xc0, 0x03, 0x7c, 0x5d, 0x42, - 0x72, 0x65, 0xc0, 0x87, 0x6f, 0x0a, 0x75, 0x5e, 0xdd, 0xe6, 0x2d, 0x64, 0x65, 0x40, 0xe4, 0xb4, 0x7d, 0x80, 0xf0, - 0x36, 0x60, 0x0c, 0x23, 0x2e, 0x40, 0xf4, 0xd1, 0x31, 0xdd, 0xaa, 0x69, 0x20, 0xee, 0xca, 0x56, 0x1f, 0xcf, 0x04, - 0xbc, 0x12, 0x7e, 0x63, 0x18, 0xc7, 0x10, 0xe2, 0x33, 0x8e, 0xed, 0xf1, 0x25, 0x54, 0x0e, 0x34, 0xca, 0x83, 0x55, - 0x79, 0xfc, 0x39, 0xf7, 0xfe, 0xea, 0xab, 0x66, 0x64, 0xd3, 0x80, 0xb9, 0xd1, 0xb4, 0xa1, 0x63, 0xc5, 0xba, 0xe4, - 0x6f, 0xbd, 0x43, 0x63, 0xb0, 0x2f, 0x5b, 0x5f, 0xc2, 0xfd, 0x4e, 0x46, 0xc3, 0x0d, 0x8c, 0xc0, 0x18, 0x0c, 0x54, - 0x95, 0x52, 0x90, 0xfe, 0xe2, 0xa5, 0x9d, 0x8b, 0x92, 0xf7, 0xa4, 0x93, 0xbd, 0x11, 0xca, 0x83, 0x42, 0x8b, 0x81, - 0x8b, 0x7e, 0x53, 0x6b, 0x25, 0xee, 0x63, 0xf9, 0xae, 0x8f, 0xd6, 0x54, 0xba, 0x99, 0x11, 0xd9, 0x52, 0x87, 0x51, - 0xdc, 0x9c, 0x3b, 0x69, 0xe6, 0xa1, 0x53, 0x60, 0x91, 0xe6, 0x66, 0x79, 0x00, 0xe2, 0x5b, 0xd4, 0xc8, 0xe6, 0x94, - 0xff, 0x89, 0x47, 0xdd, 0x40, 0x88, 0xc8, 0xb2, 0xbe, 0x6b, 0x8a, 0x33, 0x28, 0x94, 0xe4, 0x06, 0x85, 0xf0, 0xde, - 0xc8, 0xa0, 0x40, 0xc9, 0x52, 0xd9, 0x48, 0xfa, 0xfd, 0x27, 0x1e, 0x54, 0xe8, 0xe9, 0xce, 0x91, 0x64, 0xeb, 0x36, - 0x0b, 0x6b, 0x28, 0x8d, 0x32, 0x31, 0xbb, 0xd9, 0xc9, 0xb7, 0x05, 0x05, 0x45, 0x49, 0x39, 0x51, 0xa4, 0x19, 0x0e, - 0x77, 0xfa, 0x5f, 0xee, 0x51, 0xde, 0xb1, 0x40, 0xb9, 0xcd, 0x9c, 0x96, 0x00, 0x01, 0x44, 0xfd, 0x5c, 0x40, 0x34, - 0x51, 0xa4, 0x14, 0x72, 0x79, 0x23, 0x2f, 0xf3, 0xd1, 0xad, 0x79, 0xca, 0x41, 0xfb, 0xca, 0xfe, 0x94, 0x30, 0xe7, - 0xb6, 0x92, 0x3e, 0x92, 0x31, 0x31, 0x52, 0x17, 0xdc, 0xd0, 0x81, 0x61, 0xbd, 0x77, 0xe8, 0xe5, 0x53, 0x63, 0xc2, - 0xef, 0x2f, 0x82, 0x22, 0x88, 0x42, 0x00, 0x80, 0x69, 0x59, 0xb6, 0xa4, 0xf0, 0x49, 0x12, 0x05, 0x90, 0xf5, 0xb8, - 0xf4, 0xc0, 0xb5, 0x24, 0x30, 0x3c, 0xaa, 0x09, 0x68, 0xde, 0x2e, 0x50, 0x38, 0xa0, 0x85, 0x95, 0xeb, 0xb0, 0x76, - 0x42, 0xaa, 0x26, 0x45, 0xab, 0x9b, 0xd5, 0x92, 0x94, 0x67, 0x06, 0x4c, 0x15, 0x91, 0xa7, 0xf5, 0x3f, 0x64, 0xbe, - 0xb4, 0x40, 0xf4, 0xc6, 0x7c, 0x16, 0x5c, 0x3f, 0x56, 0x3b, 0x8e, 0x5e, 0x37, 0x4c, 0x6b, 0x37, 0x48, 0x02, 0x44, - 0x3e, 0x95, 0xd5, 0xd5, 0x2b, 0x15, 0xa4, 0xa1, 0xc6, 0x8f, 0x7c, 0xaf, 0x14, 0xe4, 0x4a, 0xe9, 0xa5, 0xa0, 0x00, - 0xdd, 0x78, 0xe9, 0x88, 0xa7, 0x6c, 0xe9, 0xc5, 0xa6, 0xb0, 0x71, 0xc2, 0xd8, 0xeb, 0xd9, 0x8a, 0x93, 0x7a, 0xec, - 0xaa, 0x4e, 0xb2, 0x04, 0x5d, 0x5c, 0x4b, 0x5e, 0xc5, 0x91, 0xe9, 0xd2, 0xd4, 0x31, 0xf5, 0xef, 0x1a, 0xed, 0x89, - 0x15, 0xba, 0xff, 0x2d, 0x91, 0x3b, 0xaf, 0x4c, 0xd3, 0x02, 0x41, 0xd6, 0x82, 0x90, 0xe0, 0x7c, 0x27, 0x44, 0x1e, - 0x95, 0xc7, 0xa4, 0x65, 0xee, 0xf1, 0xb5, 0x6e, 0xc6, 0x53, 0xda, 0x03, 0x51, 0x3e, 0xcc, 0x71, 0x97, 0x12, 0xe6, - 0x9e, 0x3d, 0xb0, 0x32, 0x4c, 0x4c, 0xec, 0x83, 0x1e, 0x3d, 0xae, 0x59, 0x01, 0xc1, 0x30, 0xfd, 0xda, 0xa5, 0xdd, - 0xed, 0xfa, 0x61, 0x0b, 0xe0, 0x5d, 0x2e, 0x84, 0xfa, 0xb9, 0x3a, 0x71, 0x53, 0x78, 0x75, 0x83, 0xb6, 0x8a, 0xd5, - 0x1a, 0x54, 0xb4, 0xdc, 0xa1, 0x6d, 0xeb, 0xcf, 0x69, 0x06, 0x1f, 0x3b, 0x39, 0x10, 0x6a, 0x3a, 0x22, 0x98, 0x89, - 0x72, 0x24, 0x0d, 0x9e, 0xb8, 0xea, 0x6c, 0x91, 0xaa, 0x77, 0x73, 0x02, 0x64, 0x48, 0xea, 0x0b, 0x32, 0x84, 0x36, - 0x20, 0x74, 0xac, 0xa9, 0xf2, 0xb5, 0x41, 0xed, 0xd6, 0x13, 0x63, 0x6f, 0xdf, 0x84, 0x16, 0x45, 0x85, 0xbe, 0x2d, - 0x16, 0xbb, 0x64, 0x8c, 0xbe, 0xe0, 0x6f, 0xcc, 0x7e, 0x92, 0xd1, 0xc3, 0x67, 0xb5, 0xd1, 0x45, 0x3b, 0x88, 0x5d, - 0x60, 0xfc, 0xa3, 0xc9, 0xdb, 0x9a, 0x55, 0x5c, 0x7e, 0x75, 0x41, 0x55, 0xab, 0xd9, 0x62, 0xfa, 0xb3, 0xae, 0x70, - 0x89, 0x64, 0xa2, 0xc4, 0x0c, 0x7f, 0x10, 0x68, 0xf5, 0x7d, 0xe0, 0xdc, 0xe7, 0xb9, 0x9e, 0xa0, 0xff, 0xe2, 0xa5, - 0x77, 0x28, 0xa3, 0x42, 0xc8, 0xc7, 0x91, 0x2f, 0xa4, 0x88, 0x55, 0xba, 0x7b, 0xb4, 0xd1, 0x12, 0x39, 0x0b, 0x64, - 0xcd, 0x6a, 0xca, 0x34, 0xd4, 0x85, 0x63, 0x8b, 0x2e, 0xd3, 0x6c, 0x17, 0xd0, 0x32, 0xac, 0xa4, 0x63, 0xeb, 0x9d, - 0x05, 0x84, 0x22, 0x22, 0x80, 0x29, 0x69, 0xf0, 0x9f, 0x5d, 0x69, 0x8b, 0xc5, 0xdc, 0xb4, 0x94, 0x7d, 0x2e, 0x23, - 0x31, 0xd7, 0x13, 0xb2, 0x1b, 0xb8, 0x5e, 0xdc, 0x08, 0x4d, 0x6b, 0x84, 0xe4, 0x24, 0xd1, 0xa3, 0x5e, 0xa8, 0x37, - 0x55, 0x59, 0x34, 0x7a, 0x40, 0x54, 0x03, 0xd7, 0xbb, 0x69, 0x27, 0x52, 0x12, 0x2c, 0x15, 0xdd, 0x07, 0x6e, 0xdd, - 0xad, 0x75, 0x98, 0x70, 0x85, 0x9c, 0x97, 0x50, 0x23, 0x86, 0x84, 0xfb, 0x80, 0x3d, 0x94, 0x4c, 0x00, 0x98, 0x82, - 0x13, 0x68, 0x09, 0xb0, 0xed, 0x56, 0x50, 0x02, 0x06, 0xac, 0xcc, 0x34, 0xa2, 0x30, 0xf3, 0xd0, 0x15, 0x26, 0xe4, - 0x38, 0x37, 0x8f, 0x3a, 0x58, 0x90, 0x2a, 0x44, 0xdb, 0xef, 0x4d, 0x0f, 0xe6, 0x38, 0x33, 0xce, 0x91, 0x0b, 0x80, - 0xe3, 0x2d, 0x28, 0xd5, 0x30, 0x34, 0x6c, 0xff, 0xaa, 0xc9, 0x2a, 0x67, 0x44, 0x62, 0xd5, 0x2b, 0x9b, 0xfd, 0x2a, - 0xfe, 0x18, 0x0a, 0x2a, 0x69, 0x3a, 0xbe, 0x49, 0x4c, 0x3d, 0x5b, 0x5e, 0x7d, 0x65, 0x78, 0xd2, 0xb3, 0x7d, 0xc0, - 0x15, 0x8f, 0xc0, 0xba, 0x29, 0xf9, 0x51, 0x27, 0x83, 0x06, 0xe0, 0xa8, 0x45, 0x3b, 0x54, 0x9d, 0x62, 0x10, 0x30, - 0xe2, 0x74, 0x5a, 0x96, 0xfc, 0x25, 0x8a, 0x0d, 0x34, 0xf1, 0x18, 0x2f, 0x58, 0x3a, 0xb1, 0xbd, 0xa3, 0xf9, 0xaa, - 0x44, 0x23, 0xcb, 0xac, 0x0d, 0x92, 0xfc, 0x36, 0xbd, 0xd6, 0x2a, 0x23, 0xed, 0x6d, 0xd9, 0x21, 0xfe, 0x11, 0xc8, - 0x82, 0x31, 0xa3, 0x22, 0x51, 0x31, 0x45, 0xc6, 0xa5, 0xf1, 0x56, 0xb2, 0x00, 0x5d, 0xa6, 0x67, 0x6b, 0xf3, 0x8a, - 0xbc, 0x7d, 0x12, 0xcd, 0x7d, 0x30, 0x55, 0x61, 0xff, 0x72, 0x34, 0x5b, 0x1e, 0xab, 0xf0, 0x8f, 0x55, 0x75, 0x04, - 0x9a, 0xb6, 0xab, 0xa7, 0x40, 0x8e, 0x4e, 0xd5, 0xc5, 0x21, 0x39, 0xf6, 0xc2, 0x8c, 0x43, 0x12, 0x72, 0xb2, 0x78, - 0x1b, 0xac, 0x4f, 0x32, 0xb4, 0x46, 0x80, 0x2f, 0x17, 0x61, 0xd5, 0x2b, 0xcd, 0x1e, 0x65, 0xb2, 0x1a, 0x59, 0x2b, - 0x28, 0x4d, 0x10, 0x45, 0xf3, 0x14, 0x09, 0x03, 0xcf, 0x72, 0xa2, 0x30, 0x61, 0x38, 0x25, 0xec, 0x43, 0xa2, 0x8b, - 0xf6, 0x0f, 0x33, 0xcb, 0x87, 0x12, 0xb0, 0xa5, 0x79, 0x12, 0x20, 0x46, 0x80, 0x51, 0xa5, 0x58, 0xd1, 0x3f, 0x38, - 0x4f, 0x1c, 0x0f, 0x73, 0x49, 0x22, 0x3f, 0xe3, 0xfd, 0x91, 0x79, 0xd3, 0xcd, 0xcb, 0x23, 0xdb, 0x90, 0x26, 0x66, - 0xaa, 0xa7, 0x70, 0x8d, 0xd8, 0x6e, 0xbb, 0x80, 0x2d, 0x54, 0xba, 0x41, 0xb5, 0x2f, 0x8a, 0x20, 0xf4, 0x2f, 0x75, - 0x90, 0xd6, 0xfc, 0x37, 0x47, 0x1b, 0x4c, 0x8c, 0xde, 0x64, 0x07, 0x8c, 0xfb, 0x66, 0xaa, 0xba, 0x96, 0x40, 0xc7, - 0xa6, 0x2a, 0xfc, 0x76, 0x70, 0x09, 0x89, 0xb9, 0x32, 0x16, 0xbd, 0xd5, 0x19, 0x59, 0xe5, 0xfe, 0xdf, 0x36, 0x1d, - 0x41, 0xb7, 0x7f, 0x9d, 0x5d, 0xcd, 0xce, 0x03, 0x64, 0x91, 0x07, 0x8e, 0x88, 0xa5, 0x7a, 0x6a, 0xf3, 0x68, 0x58, - 0x58, 0xaa, 0x2b, 0xc7, 0xfb, 0xb8, 0x92, 0x36, 0x9f, 0x97, 0x86, 0x03, 0x22, 0x72, 0x30, 0xbd, 0x35, 0xf0, 0x5b, - 0x24, 0x32, 0xaf, 0x6a, 0x1c, 0xd1, 0xa9, 0x8b, 0x71, 0x31, 0xae, 0x15, 0x94, 0x46, 0x7e, 0xdc, 0x49, 0x3f, 0x46, - 0x47, 0x4b, 0x1f, 0x9f, 0x6e, 0xad, 0x8a, 0xee, 0xd5, 0x2f, 0x72, 0x28, 0xe6, 0x65, 0x19, 0x1d, 0x08, 0x19, 0x24, - 0x7b, 0x4f, 0xbe, 0xf3, 0x9e, 0xb8, 0xcc, 0x45, 0x4f, 0x8d, 0x0a, 0x0e, 0xbd, 0xbd, 0x8d, 0x2c, 0x53, 0x39, 0x72, - 0x07, 0xcc, 0xce, 0xf8, 0xda, 0xde, 0x40, 0x6c, 0xef, 0x85, 0xc8, 0xad, 0xf0, 0x48, 0x61, 0xfa, 0x71, 0x65, 0x84, - 0xab, 0x31, 0xe9, 0x50, 0x99, 0x4c, 0xf3, 0xc2, 0x2e, 0x57, 0x59, 0xd0, 0x61, 0x19, 0x54, 0x33, 0x99, 0x99, 0x66, - 0xb2, 0x69, 0xa4, 0xe1, 0x0a, 0xc5, 0x34, 0x06, 0x2e, 0x97, 0x2a, 0x52, 0xf6, 0xbc, 0x92, 0xa5, 0xe7, 0x38, 0x0b, - 0x1d, 0xa6, 0x4d, 0x07, 0xcf, 0x53, 0xe2, 0x92, 0x70, 0x84, 0x35, 0x13, 0x4c, 0x93, 0xac, 0xb4, 0x40, 0xb9, 0xa8, - 0xa4, 0x18, 0xba, 0x3e, 0xaf, 0x24, 0x65, 0xee, 0x68, 0x19, 0x4f, 0x69, 0xf4, 0x8c, 0xf2, 0x15, 0xb5, 0x66, 0xfe, - 0xc9, 0xf2, 0xef, 0x20, 0x85, 0xd6, 0x57, 0x40, 0x05, 0xa6, 0x14, 0xac, 0x04, 0xf9, 0xfb, 0xc5, 0x8d, 0x56, 0x11, - 0x97, 0x82, 0xf3, 0x2a, 0xe6, 0x65, 0x53, 0x0d, 0x69, 0xbe, 0xfe, 0xe4, 0x7f, 0xa6, 0x93, 0x83, 0x4a, 0x1c, 0x6e, - 0x00, 0x33, 0x86, 0x5c, 0x2c, 0xe8, 0x4f, 0xa5, 0x57, 0x5f, 0xa9, 0x97, 0xa2, 0x46, 0x5d, 0xe8, 0xee, 0x96, 0xdc, - 0x5a, 0xcf, 0x46, 0x9a, 0x68, 0x56, 0x2a, 0xdf, 0x0f, 0x92, 0x66, 0x86, 0x1a, 0xe1, 0x62, 0x2f, 0x36, 0x60, 0xdc, - 0x1a, 0xa7, 0x50, 0x7b, 0x2f, 0x59, 0xc2, 0x67, 0x8b, 0xcb, 0x41, 0x95, 0xc2, 0x18, 0xdf, 0x81, 0xb9, 0x21, 0xf7, - 0xc1, 0x93, 0xde, 0x7e, 0xbb, 0xf3, 0x53, 0xbc, 0x0c, 0xec, 0x12, 0x11, 0x0f, 0xa2, 0xdf, 0xdc, 0x2a, 0x6d, 0xaf, - 0x37, 0x16, 0x36, 0x57, 0xc5, 0x0f, 0x2a, 0x55, 0xe0, 0xce, 0x2b, 0x77, 0x61, 0x50, 0x1e, 0x41, 0x0e, 0xfa, 0x4d, - 0xe3, 0xe6, 0x7e, 0x27, 0x54, 0x61, 0xd8, 0xa5, 0x87, 0x49, 0x59, 0xe7, 0x4b, 0x7a, 0x18, 0x33, 0xc4, 0xce, 0xcc, - 0x32, 0xa9, 0xd0, 0xae, 0x65, 0x41, 0xe3, 0xa7, 0xe0, 0x8f, 0x28, 0xa3, 0x48, 0x2b, 0x26, 0xb0, 0x0f, 0x32, 0x01, - 0xc7, 0x07, 0xc1, 0xa8, 0x2e, 0xe2, 0x13, 0x9c, 0xee, 0xce, 0x0b, 0x0e, 0x54, 0x32, 0xb4, 0x48, 0xb0, 0xc4, 0x1e, - 0xf1, 0xb0, 0xa9, 0x1f, 0xec, 0x9d, 0xda, 0x55, 0x38, 0x6f, 0x16, 0xeb, 0x31, 0x48, 0xf5, 0xfc, 0xb6, 0xf9, 0x84, - 0x03, 0xfc, 0x51, 0x9d, 0xea, 0xf1, 0x4d, 0x1d, 0xaf, 0x71, 0x08, 0xab, 0x43, 0xe5, 0x16, 0x7f, 0x52, 0x90, 0xce, - 0xb8, 0xa0, 0x87, 0xfd, 0x2b, 0x69, 0xf1, 0x05, 0x65, 0x37, 0x01, 0x1b, 0xbd, 0xf5, 0xa0, 0x04, 0xa1, 0xf3, 0xfe, - 0xe1, 0xd1, 0x7d, 0x16, 0x14, 0x6b, 0x44, 0x1d, 0x35, 0xf1, 0x6e, 0xb4, 0x9b, 0x54, 0x5c, 0x10, 0xab, 0x36, 0x5b, - 0xed, 0xb0, 0x0c, 0xd1, 0xfb, 0x37, 0x19, 0x59, 0x80, 0xa2, 0xbd, 0xe9, 0x79, 0x19, 0xac, 0x56, 0x4f, 0x13, 0x12, - 0x86, 0x6f, 0x20, 0xab, 0x29, 0x6c, 0x33, 0xdd, 0xca, 0xe8, 0x73, 0x60, 0x8e, 0x9e, 0x74, 0xd6, 0xd4, 0x82, 0xb1, - 0x65, 0xd4, 0x9f, 0x29, 0x0b, 0x27, 0x1f, 0xcb, 0xe0, 0xe7, 0x85, 0x29, 0x75, 0x07, 0x0d, 0xc9, 0x62, 0xc4, 0xca, - 0x4d, 0x3c, 0x74, 0xe8, 0xaa, 0x04, 0x83, 0xf5, 0xdb, 0x7a, 0xe3, 0xac, 0xd7, 0x38, 0x20, 0xf4, 0xde, 0x0f, 0x5c, - 0x2d, 0xfc, 0x10, 0x89, 0x11, 0xde, 0x90, 0x36, 0x47, 0x9d, 0xf1, 0xe2, 0x37, 0xde, 0x1b, 0x43, 0xb9, 0xbd, 0xae, - 0xf8, 0xa3, 0x5f, 0xd7, 0x95, 0x2a, 0x74, 0x25, 0x71, 0x66, 0xee, 0x63, 0x49, 0xd1, 0x23, 0x53, 0xda, 0xc5, 0x3d, - 0x00, 0x58, 0x98, 0x8d, 0x8a, 0xd0, 0xa4, 0x91, 0xb8, 0xfc, 0x54, 0x61, 0xa5, 0x52, 0x9f, 0x50, 0x72, 0x22, 0x30, - 0x0c, 0xbe, 0xff, 0x28, 0xd2, 0x15, 0x47, 0x3f, 0xc0, 0x3f, 0x22, 0x20, 0x50, 0x6b, 0x16, 0x69, 0xa8, 0x1d, 0x90, - 0x8c, 0x9f, 0x0e, 0x17, 0xce, 0xce, 0xcc, 0x88, 0x20, 0x53, 0x77, 0x03, 0x02, 0x84, 0xe1, 0x1a, 0x81, 0x2e, 0xff, - 0x4a, 0x49, 0xdb, 0x96, 0x3b, 0xd4, 0x61, 0x90, 0x5d, 0xe8, 0x20, 0x5a, 0x2d, 0xfa, 0xa5, 0xca, 0xf8, 0x16, 0xd1, - 0xc9, 0xfa, 0xfa, 0xfd, 0xc7, 0xe5, 0x5e, 0xe4, 0x0f, 0x6e, 0x2d, 0x00, 0x98, 0x8d, 0x38, 0x1b, 0x27, 0xbc, 0x6a, - 0x1d, 0x8b, 0x8f, 0xd2, 0x35, 0x86, 0xed, 0x14, 0xb4, 0xe2, 0x41, 0xab, 0x46, 0x54, 0x8a, 0x75, 0x7e, 0xdc, 0x2b, - 0x0f, 0xed, 0x76, 0xef, 0x87, 0xc0, 0xb9, 0x60, 0x47, 0xcc, 0x13, 0xc0, 0xbc, 0xac, 0x5c, 0x15, 0x32, 0x4d, 0xb0, - 0x11, 0x07, 0x39, 0xc8, 0xb4, 0xeb, 0x1e, 0x98, 0xb2, 0x4d, 0x8b, 0xdd, 0x2d, 0x66, 0x61, 0x03, 0x19, 0x21, 0x05, - 0x9b, 0x84, 0x0f, 0xd9, 0x32, 0x09, 0xa5, 0x07, 0x0e, 0x33, 0xd0, 0xd6, 0x7a, 0x14, 0xfb, 0x35, 0x6d, 0x13, 0x5d, - 0xb2, 0xc0, 0xa5, 0x46, 0xb6, 0x6f, 0xfa, 0x84, 0x8e, 0xc5, 0xe4, 0x86, 0xef, 0x77, 0xe7, 0xd5, 0x18, 0x96, 0xed, - 0x63, 0x65, 0x2f, 0xeb, 0xaf, 0x57, 0x2b, 0x50, 0x33, 0x9d, 0xb9, 0x7c, 0xab, 0xe4, 0xdf, 0xf5, 0x61, 0xa0, 0xf6, - 0x42, 0xe1, 0xa3, 0x98, 0x40, 0x59, 0x4b, 0xea, 0x14, 0xbc, 0x35, 0xde, 0xaf, 0xda, 0x61, 0xdc, 0xbf, 0xb9, 0x0b, - 0x15, 0x37, 0xbe, 0xfe, 0xa7, 0x9b, 0xda, 0xe0, 0xe8, 0x8d, 0x70, 0x49, 0xe7, 0x7e, 0xbd, 0x82, 0x00, 0x51, 0x6f, - 0x61, 0xca, 0x3a, 0x6f, 0xaf, 0xae, 0x6b, 0xf2, 0x68, 0x4b, 0x3f, 0xee, 0x82, 0x0e, 0x9b, 0xac, 0x64, 0x6d, 0xa1, - 0x7b, 0x64, 0x35, 0xee, 0x7e, 0x46, 0x38, 0x74, 0xb0, 0x83, 0xd4, 0x2d, 0x3e, 0xf4, 0x0e, 0xd3, 0xeb, 0x94, 0x54, - 0x6f, 0xf5, 0x9b, 0xfa, 0xcb, 0x97, 0xc6, 0xb9, 0x1a, 0x35, 0xac, 0x5d, 0xd4, 0xa4, 0x64, 0x66, 0x0a, 0xa6, 0xdb, - 0x20, 0x85, 0xab, 0xbe, 0xfa, 0xca, 0xe0, 0xc8, 0xf7, 0x73, 0x42, 0x05, 0x9b, 0x10, 0xaa, 0xc7, 0x2f, 0x88, 0xae, - 0x64, 0xfe, 0x71, 0xbb, 0x32, 0x06, 0x49, 0xe8, 0xdb, 0x11, 0x6f, 0xa5, 0xa9, 0xb3, 0x43, 0x3e, 0xe6, 0x6c, 0x82, - 0x5f, 0xd2, 0x84, 0x40, 0xb3, 0xf0, 0x2f, 0x0d, 0xd8, 0xee, 0x70, 0x6c, 0x3d, 0xd0, 0xb8, 0xf8, 0x0f, 0x94, 0x1b, - 0xd1, 0x99, 0x85, 0x1d, 0xef, 0x66, 0xe6, 0x4b, 0x87, 0xc3, 0x9e, 0x61, 0x09, 0x54, 0x65, 0x18, 0xd0, 0x0f, 0xdd, - 0x90, 0xed, 0x52, 0x1d, 0x3b, 0x07, 0x09, 0xeb, 0x3d, 0x14, 0x62, 0x3f, 0x9a, 0xab, 0xcb, 0xeb, 0xe5, 0x81, 0xfb, - 0x0a, 0xc3, 0xe5, 0xc1, 0xb0, 0xf8, 0x98, 0x15, 0x52, 0x75, 0xe9, 0xba, 0x8e, 0x3d, 0xad, 0x31, 0x20, 0x1f, 0x33, - 0x0c, 0x7f, 0x0e, 0x06, 0x8b, 0x76, 0x64, 0xdb, 0x32, 0x38, 0xc3, 0xca, 0xdb, 0x32, 0x65, 0xa6, 0xec, 0x2e, 0xd8, - 0x9e, 0x1a, 0xf8, 0xb3, 0x93, 0x94, 0x11, 0x14, 0x2a, 0xd3, 0x11, 0x34, 0xfa, 0xc7, 0x57, 0x45, 0xad, 0xc8, 0x46, - 0xd6, 0xfc, 0xb6, 0x78, 0x67, 0x8c, 0x53, 0x5a, 0x97, 0xb3, 0x7a, 0x17, 0xab, 0x46, 0x1f, 0x9a, 0x44, 0x2b, 0x92, - 0xb5, 0xaf, 0xb1, 0xc7, 0xc0, 0x10, 0x46, 0xc8, 0x72, 0xb3, 0xd6, 0x66, 0x36, 0x38, 0x89, 0xe3, 0x51, 0x07, 0xd6, - 0xdb, 0x79, 0xe5, 0x15, 0x0c, 0x02, 0xe0, 0x5f, 0x43, 0xcc, 0xb3, 0x0d, 0xfd, 0xce, 0x74, 0x53, 0xd5, 0xcb, 0x25, - 0x14, 0x46, 0x7f, 0x8c, 0x49, 0x07, 0xe5, 0xa5, 0x6a, 0x2a, 0x0d, 0x92, 0x51, 0x3d, 0x16, 0xa4, 0xa3, 0xcb, 0x73, - 0xc6, 0x67, 0x1d, 0xec, 0x69, 0xd2, 0xcd, 0x00, 0x10, 0x69, 0x87, 0x32, 0x26, 0x2a, 0x84, 0x15, 0x1e, 0x19, 0xa1, - 0xca, 0x1d, 0xf8, 0x28, 0xe0, 0xf3, 0xee, 0xb4, 0x20, 0x98, 0xd5, 0x25, 0x87, 0xb7, 0x42, 0x54, 0x14, 0xb7, 0xb2, - 0x9f, 0x90, 0xcc, 0xc7, 0x66, 0x26, 0xda, 0x6b, 0xc6, 0x9a, 0x77, 0x7f, 0x0e, 0x41, 0xa3, 0x20, 0x74, 0x58, 0x44, - 0xf3, 0x43, 0x0e, 0x83, 0xe4, 0x95, 0xd5, 0xd5, 0xc9, 0xf0, 0x5b, 0x81, 0x64, 0x05, 0x42, 0x74, 0xe2, 0x12, 0x84, - 0xde, 0x7e, 0x32, 0xec, 0x82, 0x57, 0xa0, 0x70, 0x28, 0x1c, 0x2f, 0x81, 0xcd, 0x27, 0x46, 0xc7, 0x72, 0xec, 0x74, - 0xc8, 0x55, 0x85, 0x3a, 0x59, 0x45, 0xd0, 0xda, 0x92, 0x9f, 0xf4, 0x95, 0x82, 0x98, 0x64, 0xcb, 0x96, 0x20, 0xa6, - 0x26, 0xc7, 0xc7, 0x09, 0xbd, 0x3f, 0xb1, 0x48, 0x1a, 0x92, 0x84, 0xef, 0x3e, 0xc1, 0x19, 0x23, 0x18, 0x63, 0x95, - 0x12, 0x63, 0x43, 0x59, 0x66, 0x7f, 0x38, 0x7d, 0x33, 0xc1, 0x81, 0x5f, 0x42, 0x91, 0xf2, 0x2a, 0x39, 0xe1, 0x19, - 0xc3, 0x5c, 0xca, 0xf1, 0xac, 0xe8, 0x1b, 0x75, 0xf8, 0x4b, 0x84, 0x22, 0x90, 0xe0, 0x2e, 0x2f, 0x67, 0xea, 0x8b, - 0xca, 0x4c, 0x69, 0x11, 0x6e, 0xf1, 0x1c, 0x6a, 0x8f, 0x1b, 0xb2, 0x13, 0x6f, 0xba, 0xf7, 0x7a, 0x84, 0x0f, 0x54, - 0x8a, 0x70, 0xde, 0x15, 0x83, 0xe5, 0x6e, 0x2c, 0xcc, 0xa5, 0x2f, 0x26, 0x7f, 0xa0, 0xe7, 0xb3, 0xb4, 0x9c, 0xf8, - 0xaa, 0xf9, 0xe6, 0xa8, 0x8b, 0x3f, 0xe2, 0x69, 0x89, 0x6d, 0xb9, 0xbd, 0x49, 0x2f, 0x3e, 0xdf, 0xe7, 0x7f, 0xd7, - 0x78, 0xb0, 0x50, 0xfd, 0x6c, 0x96, 0xe2, 0x06, 0xab, 0x07, 0x3e, 0x04, 0xb9, 0xc8, 0x4c, 0xe5, 0xda, 0xa6, 0xc7, - 0x9a, 0x3c, 0x6c, 0xc6, 0x3a, 0xb1, 0x5a, 0xf9, 0x36, 0xd8, 0xf4, 0x6a, 0x5b, 0xab, 0x5b, 0x13, 0xfa, 0x43, 0xad, - 0x64, 0x5b, 0xfd, 0x82, 0x07, 0x75, 0x34, 0x5f, 0x76, 0x86, 0xd5, 0xc5, 0xfe, 0x94, 0xc3, 0xa4, 0xf8, 0xbb, 0xb4, - 0xa2, 0x88, 0xfb, 0x4b, 0x06, 0x35, 0x75, 0x7e, 0x8c, 0x5f, 0xac, 0x0e, 0xa1, 0xdf, 0xc7, 0x11, 0xb5, 0x54, 0xfe, - 0x87, 0x13, 0xa5, 0x93, 0x49, 0x1e, 0xed, 0xf9, 0x34, 0x2d, 0x5e, 0xd1, 0xa5, 0xdb, 0xf1, 0x32, 0xf9, 0x56, 0x14, - 0x79, 0xbc, 0x58, 0x65, 0xc0, 0xec, 0x75, 0x68, 0xfa, 0xc9, 0xd3, 0x28, 0x39, 0x16, 0x57, 0x0c, 0x6e, 0x3d, 0x0e, - 0x1e, 0x91, 0x37, 0x35, 0xec, 0x4d, 0x28, 0x58, 0xec, 0xc0, 0x20, 0x3f, 0x06, 0x87, 0xa1, 0x43, 0x3d, 0x22, 0xde, - 0x35, 0xbe, 0x9c, 0xd4, 0x47, 0x58, 0x30, 0xab, 0x89, 0xf0, 0xdb, 0x82, 0xbc, 0x47, 0x22, 0x5a, 0x9f, 0x24, 0x8d, - 0xad, 0x74, 0xe0, 0xed, 0x2b, 0x41, 0x36, 0x39, 0xd0, 0x93, 0xde, 0xc2, 0x6c, 0xe7, 0x7c, 0xb4, 0xf2, 0xf7, 0x22, - 0xf9, 0x29, 0x24, 0xd2, 0x55, 0x15, 0x34, 0x75, 0x64, 0x1f, 0x91, 0x61, 0xed, 0x4b, 0xdd, 0xaf, 0x7d, 0x2d, 0xa4, - 0x24, 0xf8, 0xff, 0x69, 0x14, 0x0b, 0xba, 0x0b, 0x98, 0x77, 0x57, 0xc3, 0x30, 0x91, 0x93, 0xd5, 0xc4, 0x65, 0x89, - 0x6e, 0xea, 0x40, 0x61, 0x0c, 0xfb, 0x6d, 0xb4, 0x38, 0x5c, 0xd8, 0x97, 0x3c, 0x50, 0xa9, 0x5b, 0x8e, 0xe7, 0xb7, - 0x32, 0xa7, 0xf2, 0x62, 0x39, 0xa8, 0x0c, 0x5b, 0x03, 0xf0, 0x0c, 0x61, 0x18, 0x10, 0x0d, 0x39, 0x2e, 0x49, 0xb8, - 0xc2, 0xb0, 0x46, 0xf6, 0x58, 0x34, 0x52, 0x86, 0xd5, 0xc5, 0x8d, 0x2c, 0x4e, 0xa6, 0x89, 0x18, 0xce, 0xa7, 0x69, - 0x71, 0x02, 0xfc, 0xc0, 0x84, 0x1b, 0xec, 0xfa, 0xaa, 0xb0, 0x1c, 0xd0, 0x6c, 0x13, 0x1a, 0x2d, 0x52, 0x93, 0x66, - 0x95, 0x74, 0x52, 0xfe, 0x2f, 0xc7, 0x34, 0xd6, 0x19, 0xe9, 0x9c, 0x30, 0x22, 0xf2, 0x0f, 0x8d, 0x52, 0x76, 0x10, - 0xb6, 0x3a, 0x3c, 0x9c, 0x4d, 0x7e, 0x40, 0xd5, 0x46, 0xf7, 0x83, 0xaf, 0x2e, 0x64, 0xb0, 0x07, 0xc1, 0x91, 0xeb, - 0xe6, 0xa8, 0x49, 0xfe, 0x97, 0xa3, 0xfc, 0x73, 0x2e, 0x4e, 0x3a, 0x92, 0xb4, 0xb1, 0x62, 0x44, 0x64, 0x63, 0xfa, - 0x83, 0x4d, 0x9b, 0xc2, 0xa1, 0x0f, 0x0b, 0x3a, 0x9e, 0xa2, 0x40, 0x1a, 0xbd, 0xaa, 0xe4, 0xa0, 0x30, 0x19, 0x20, - 0x7b, 0x25, 0x65, 0xde, 0x2f, 0xe4, 0x32, 0xc8, 0x7c, 0xab, 0x56, 0x66, 0xcb, 0x67, 0x0c, 0xc4, 0x08, 0x37, 0xc2, - 0xe0, 0x57, 0x8a, 0xa9, 0xaf, 0x59, 0xa6, 0xb8, 0x9f, 0x86, 0x90, 0x1d, 0x12, 0x86, 0x1f, 0x68, 0x53, 0x26, 0xa7, - 0x4d, 0x13, 0x2f, 0xf5, 0xd5, 0xd5, 0x30, 0x79, 0x81, 0x4c, 0xde, 0xe0, 0x3e, 0x72, 0xdd, 0x8f, 0xfa, 0x6d, 0x93, - 0x60, 0x7f, 0x95, 0xe0, 0xff, 0x0e, 0x21, 0x5b, 0x0b, 0x60, 0x06, 0x89, 0x8d, 0xbe, 0x5d, 0xf0, 0x71, 0x51, 0xe4, - 0xeb, 0x44, 0xac, 0xa8, 0x8c, 0x4b, 0xb2, 0x5b, 0x07, 0xbb, 0xd2, 0x36, 0x18, 0x6c, 0x90, 0x68, 0x68, 0x32, 0x0d, - 0x9d, 0x2c, 0xdf, 0x2c, 0x0d, 0xc9, 0xb7, 0x65, 0x9d, 0x3b, 0x14, 0x1a, 0x49, 0x0d, 0x6e, 0xd3, 0xf2, 0x39, 0xf5, - 0x94, 0x6c, 0x78, 0x6c, 0x2b, 0x79, 0x50, 0x61, 0x3a, 0x36, 0xb3, 0x79, 0xb4, 0x62, 0x3d, 0xaf, 0xd6, 0x39, 0x24, - 0x76, 0x5b, 0x56, 0x0e, 0x56, 0x64, 0x88, 0x4f, 0x5c, 0x8f, 0xd6, 0xf6, 0xa3, 0xef, 0x63, 0x44, 0x25, 0x39, 0x18, - 0x96, 0x2d, 0x95, 0xa7, 0x16, 0x27, 0x9e, 0xda, 0xfd, 0x60, 0xfa, 0xb2, 0x48, 0x85, 0x17, 0x4d, 0xe6, 0x8b, 0xd4, - 0xa4, 0x38, 0xcc, 0xc5, 0x48, 0xcc, 0x81, 0xa5, 0x7d, 0x84, 0x8c, 0xc1, 0x52, 0x7a, 0xa4, 0xb1, 0x8d, 0x91, 0xb7, - 0xe8, 0x50, 0x2e, 0x3a, 0xfb, 0x9f, 0x59, 0x7a, 0x0a, 0x43, 0xda, 0x2c, 0x8d, 0x6b, 0xb7, 0x54, 0x5e, 0xd9, 0x0f, - 0xae, 0xb2, 0x6d, 0x1b, 0x8b, 0xa0, 0x7e, 0xb2, 0xde, 0x92, 0x4c, 0x2a, 0x70, 0x30, 0x31, 0xd0, 0x43, 0x65, 0x37, - 0x5a, 0x6b, 0x7a, 0x3c, 0x4a, 0x5b, 0xa4, 0x89, 0xb0, 0x36, 0x32, 0xaa, 0xba, 0x8a, 0xb8, 0xbd, 0xd2, 0x97, 0x7c, - 0x05, 0x18, 0x6f, 0xbc, 0xb9, 0xb9, 0x46, 0xc3, 0x1d, 0x73, 0x12, 0xc5, 0x20, 0xde, 0xad, 0x8c, 0x27, 0x4d, 0xeb, - 0xf2, 0xdb, 0x72, 0xe2, 0x53, 0xbd, 0x6e, 0x31, 0x81, 0x0c, 0xce, 0x82, 0x78, 0x3d, 0x03, 0x58, 0x65, 0x57, 0x11, - 0xd5, 0x66, 0x41, 0x82, 0x60, 0x1a, 0x5c, 0xc7, 0x1a, 0xb7, 0x39, 0xca, 0x36, 0x09, 0x7a, 0xe6, 0x68, 0x2c, 0xff, - 0x9d, 0x59, 0xe0, 0xe5, 0x45, 0x81, 0xf6, 0xc4, 0x81, 0xef, 0xc6, 0x33, 0xf6, 0xfa, 0x9f, 0x8f, 0x8c, 0x01, 0x1c, - 0xd4, 0x98, 0x93, 0xd9, 0xe1, 0x55, 0xda, 0xaa, 0xf4, 0x2a, 0xa9, 0xb2, 0x3d, 0xc4, 0xab, 0xde, 0x8c, 0x24, 0x4a, - 0xa9, 0x0b, 0xb6, 0x6c, 0xe6, 0x41, 0xe2, 0x35, 0x47, 0x7f, 0xac, 0x25, 0x92, 0xbd, 0x82, 0x86, 0x91, 0x33, 0xf1, - 0x8b, 0x4f, 0x89, 0x79, 0xa7, 0x7d, 0xc5, 0xe4, 0x79, 0x83, 0x8f, 0xe0, 0x8b, 0x4a, 0x5d, 0x34, 0xde, 0x38, 0x84, - 0x3a, 0xd6, 0x30, 0x41, 0x0a, 0x30, 0x73, 0x80, 0x8b, 0x62, 0xe5, 0x93, 0x51, 0x52, 0xec, 0x9d, 0x16, 0xdb, 0xbc, - 0x16, 0x8a, 0x57, 0xfe, 0xa2, 0x94, 0xa6, 0xf6, 0xf2, 0xa9, 0x0d, 0xe5, 0xfb, 0x0d, 0x74, 0x1a, 0xeb, 0x8d, 0xe8, - 0xa3, 0x94, 0xf2, 0xec, 0x3a, 0xe1, 0x0c, 0x8f, 0xad, 0x8a, 0x59, 0x3c, 0x58, 0x35, 0xce, 0x3e, 0x84, 0x12, 0x1e, - 0x2d, 0x5b, 0x52, 0x76, 0xf3, 0x14, 0x86, 0xbf, 0xc3, 0x4a, 0xa1, 0xa8, 0x45, 0x80, 0xc0, 0xba, 0x8d, 0x7f, 0xdc, - 0x69, 0x3a, 0x6f, 0xa7, 0xb3, 0xc1, 0xc6, 0xe2, 0x68, 0xd8, 0x1e, 0xa9, 0x32, 0xf0, 0xcb, 0xc7, 0x33, 0x6b, 0x4d, - 0x0a, 0x17, 0x78, 0xa8, 0xc2, 0xf6, 0xaf, 0xa3, 0x3d, 0x69, 0xbe, 0x23, 0xe0, 0x8e, 0x41, 0x3a, 0x01, 0xdf, 0x79, - 0x08, 0x5d, 0x00, 0xed, 0x14, 0x62, 0x17, 0x21, 0x75, 0x09, 0x72, 0x97, 0xa1, 0xed, 0x5a, 0x78, 0x40, 0x4c, 0xf0, - 0xb0, 0x32, 0xc3, 0xa3, 0xca, 0x02, 0x8f, 0x2b, 0x7b, 0x78, 0x42, 0x1c, 0xe0, 0x69, 0x65, 0x85, 0x85, 0x8a, 0xea, - 0xb2, 0xba, 0xaa, 0xae, 0xab, 0xa5, 0x32, 0xf4, 0x2b, 0x96, 0x05, 0xc6, 0xbf, 0x20, 0x46, 0xb0, 0xf8, 0xa0, 0x31, - 0xe5, 0xf6, 0xc1, 0xc3, 0x47, 0x8f, 0x9f, 0x3c, 0x0d, 0x55, 0xa6, 0xf3, 0x75, 0xd6, 0xa4, 0xe6, 0x71, 0x6b, 0xa8, - 0x23, 0xef, 0x7c, 0x39, 0xf4, 0x2d, 0x03, 0xea, 0xec, 0x69, 0x1a, 0x86, 0x2f, 0x5d, 0x7e, 0x3d, 0xf1, 0x4b, 0xc5, - 0xeb, 0xc6, 0x37, 0x66, 0x7c, 0xef, 0x8b, 0x4f, 0x13, 0x5a, 0x3c, 0xbd, 0xe0, 0x82, 0x15, 0x3c, 0x98, 0x8f, 0xf3, - 0x2e, 0x0b, 0x11, 0x75, 0xaa, 0x87, 0xc2, 0x5a, 0xf1, 0xfc, 0x83, 0x0e, 0x63, 0x2f, 0x4e, 0x11, 0xd9, 0x37, 0xe7, - 0x86, 0x88, 0xa6, 0xc9, 0xbb, 0x88, 0xaa, 0x7f, 0xb0, 0x7b, 0x29, 0x1a, 0x5d, 0x52, 0xee, 0xd3, 0xdf, 0x9f, 0xad, - 0xc8, 0xbe, 0xa9, 0xe8, 0x41, 0x71, 0x70, 0x56, 0x2d, 0x4c, 0x66, 0x93, 0xa6, 0x4e, 0x36, 0xc0, 0xf7, 0x4c, 0x0a, - 0xaa, 0x92, 0xa1, 0xf6, 0xcf, 0x1e, 0xf8, 0x4f, 0x5a, 0x77, 0xe1, 0xa7, 0xb2, 0x7e, 0xbb, 0xff, 0x9a, 0xbe, 0x2d, - 0x4e, 0x58, 0x9c, 0x58, 0x94, 0x54, 0xd2, 0x99, 0xc9, 0xa2, 0x9e, 0xd7, 0x57, 0xce, 0xcd, 0x3c, 0xc9, 0x2c, 0x9f, - 0xf6, 0x56, 0xd0, 0xd9, 0x6d, 0x80, 0x98, 0x04, 0xea, 0x35, 0x3e, 0x65, 0xf5, 0x66, 0x3c, 0xfd, 0xc4, 0xb2, 0x29, - 0x85, 0x00, 0xa7, 0xc5, 0x17, 0xff, 0xdb, 0xf1, 0x2d, 0xf9, 0x6e, 0xa6, 0x97, 0xf9, 0xfd, 0x9a, 0x6c, 0xe6, 0x46, - 0x0a, 0xdc, 0xf1, 0xcb, 0xd9, 0xe8, 0xb9, 0xc6, 0x7f, 0x33, 0xc8, 0xdb, 0xe4, 0xe9, 0x52, 0xca, 0xd5, 0xc6, 0x0e, - 0x5d, 0x6e, 0xfe, 0xa9, 0x2a, 0x8f, 0x5d, 0x5b, 0xea, 0xf8, 0xe7, 0x5d, 0x1c, 0x8f, 0x5f, 0xf4, 0x3f, 0xde, 0xe7, - 0x1e, 0xef, 0xa2, 0x66, 0x51, 0xfe, 0x3d, 0x54, 0xa9, 0xe3, 0xf7, 0xa7, 0xf9, 0xd2, 0x51, 0x3e, 0x4e, 0x4d, 0xf3, - 0x41, 0x5e, 0x46, 0x2c, 0x8f, 0xd6, 0xf4, 0xf6, 0xcf, 0x9f, 0x12, 0xff, 0x6b, 0x73, 0x9d, 0xed, 0xe1, 0xfe, 0xa4, - 0x5e, 0x6c, 0x63, 0x31, 0x22, 0x66, 0x63, 0xc1, 0x0e, 0xa8, 0xde, 0x3f, 0x3d, 0x87, 0x94, 0x65, 0xfc, 0xa7, 0xc7, - 0x0b, 0x2c, 0x9d, 0xc0, 0x9a, 0xbf, 0xed, 0x98, 0x96, 0xbc, 0xcb, 0x96, 0x63, 0x0c, 0x7d, 0xc6, 0xff, 0xe6, 0x2e, - 0xee, 0xb0, 0x5a, 0x12, 0xa5, 0x18, 0x11, 0x16, 0xfa, 0xf9, 0x8d, 0x0f, 0x42, 0xf4, 0x61, 0xe5, 0xc1, 0x54, 0xaa, - 0x4d, 0xa6, 0x9c, 0x38, 0x81, 0x0f, 0xbe, 0x1d, 0x78, 0x5a, 0x81, 0x37, 0x7f, 0xdb, 0x37, 0x2d, 0xa3, 0x63, 0xf7, - 0x92, 0xbe, 0xbc, 0x9c, 0xb7, 0x4c, 0x4b, 0xbc, 0xd4, 0xc5, 0xed, 0x49, 0x13, 0x7a, 0x67, 0x41, 0x35, 0xc9, 0xc9, - 0xa7, 0xe5, 0x13, 0x0c, 0xfb, 0x45, 0x49, 0xac, 0x9a, 0x56, 0x43, 0x63, 0xd4, 0x9b, 0x26, 0x53, 0x37, 0xb8, 0xd6, - 0xa8, 0x57, 0x81, 0x7f, 0x66, 0x40, 0xee, 0x39, 0x13, 0x7b, 0xce, 0xa0, 0x97, 0x6f, 0x7e, 0x4c, 0x9a, 0xb7, 0xa6, - 0xde, 0x16, 0x5f, 0x2b, 0xa6, 0x52, 0xa2, 0xf0, 0x7d, 0x5d, 0xb8, 0x10, 0x1f, 0x0b, 0x97, 0xab, 0x07, 0xa6, 0xe9, - 0xd3, 0x29, 0xe4, 0xfb, 0x56, 0xe0, 0x9c, 0x91, 0xa3, 0xa0, 0x0d, 0x86, 0x3c, 0x62, 0xde, 0x0f, 0x21, 0xe5, 0x73, - 0x1f, 0xc6, 0xe8, 0xf7, 0xd8, 0x08, 0x28, 0x96, 0xa3, 0xec, 0x2e, 0xc4, 0x3c, 0xcf, 0x62, 0x76, 0xd0, 0xd5, 0x72, - 0xb3, 0xee, 0x98, 0x63, 0x26, 0x56, 0xfc, 0x8c, 0xeb, 0x58, 0xc1, 0x8c, 0x7c, 0xd0, 0xa2, 0x3b, 0xfa, 0x23, 0x2b, - 0xfb, 0x3a, 0x50, 0x77, 0x12, 0x5a, 0x3b, 0x4c, 0xdf, 0x66, 0x19, 0x0e, 0x28, 0x96, 0x08, 0x12, 0x85, 0xe6, 0xf7, - 0x2a, 0x31, 0xb1, 0x52, 0x9c, 0x1a, 0xcd, 0xf1, 0x86, 0xaa, 0xc4, 0x98, 0xa0, 0xe5, 0xed, 0xea, 0x8b, 0xcc, 0x74, - 0x09, 0x1b, 0x37, 0x58, 0xd2, 0x8a, 0xfd, 0xa2, 0xa4, 0x7e, 0xdf, 0x96, 0x85, 0x8a, 0x77, 0xd9, 0xd5, 0x51, 0x5d, - 0xda, 0xa1, 0xa3, 0x22, 0x66, 0xa0, 0xe8, 0xbb, 0x9d, 0x57, 0xce, 0x46, 0xf6, 0x81, 0xdb, 0x09, 0x9c, 0x05, 0x55, - 0x79, 0x9d, 0xea, 0x50, 0x0a, 0x97, 0x05, 0xe0, 0x16, 0x38, 0x3d, 0x3d, 0x09, 0xca, 0xb3, 0xae, 0xac, 0x44, 0x57, - 0x7a, 0x37, 0xe4, 0x3f, 0xd2, 0xe8, 0xc7, 0x39, 0xe2, 0xd9, 0xe7, 0xdd, 0xe9, 0x46, 0xa7, 0xf9, 0x5d, 0xfe, 0x26, - 0xe6, 0xef, 0x2a, 0xfa, 0xf2, 0x97, 0x2d, 0x6f, 0x5f, 0x07, 0xc2, 0x60, 0x69, 0x86, 0xf8, 0xda, 0x52, 0x79, 0x8d, - 0xb9, 0x0f, 0x31, 0x4d, 0xc2, 0xc6, 0xc8, 0xa3, 0x29, 0xe0, 0x3c, 0xdf, 0xbb, 0x51, 0x7a, 0x6d, 0x4b, 0x82, 0x50, - 0xe2, 0x3d, 0xdf, 0x4a, 0xbf, 0xe7, 0x71, 0x11, 0x0b, 0x32, 0xdb, 0xd0, 0xe9, 0xf9, 0xa8, 0x8e, 0x21, 0xe6, 0xc6, - 0xd3, 0x2e, 0xec, 0xa9, 0x5a, 0xab, 0x05, 0x49, 0xd2, 0xeb, 0x3c, 0xdf, 0xfd, 0xce, 0xa4, 0x8c, 0xe0, 0x51, 0x13, - 0x24, 0x7e, 0xee, 0xeb, 0x3f, 0xef, 0x4c, 0x0d, 0x7a, 0x0b, 0x78, 0x5a, 0x3a, 0xe8, 0xc9, 0x27, 0x8e, 0x5f, 0x0b, - 0xcb, 0x6d, 0xa3, 0x4b, 0xff, 0x7d, 0x9e, 0xad, 0x4b, 0x26, 0x4c, 0xba, 0x45, 0x32, 0x1f, 0x2b, 0x13, 0xe8, 0xa9, - 0x69, 0x9d, 0x90, 0xaa, 0xfe, 0x89, 0x15, 0xd4, 0x28, 0x70, 0xa0, 0x94, 0xba, 0x9a, 0xb0, 0x10, 0xdc, 0x09, 0x8f, - 0xcf, 0x4b, 0x01, 0xd1, 0xdc, 0xbd, 0x08, 0x92, 0x8b, 0xc4, 0x6b, 0x41, 0x25, 0x56, 0x65, 0xc5, 0x65, 0x55, 0x52, - 0x09, 0xa6, 0xf0, 0x03, 0x00, 0xcd, 0x7b, 0x05, 0x4c, 0x72, 0xa0, 0x47, 0x54, 0x3a, 0xf9, 0xa8, 0x11, 0x1f, 0xf0, - 0x64, 0x93, 0xfe, 0xa1, 0xa7, 0xb8, 0x24, 0x4e, 0x9c, 0xe9, 0x50, 0x82, 0xfb, 0x63, 0xd2, 0xfa, 0x53, 0xc5, 0x7c, - 0x8b, 0x89, 0x0e, 0xea, 0xf2, 0xf6, 0xfa, 0x3a, 0xab, 0x89, 0x84, 0x1a, 0x70, 0xc3, 0x9a, 0xd6, 0x94, 0xba, 0x6e, - 0x03, 0x4d, 0x1f, 0x40, 0xdf, 0xb3, 0xea, 0xf6, 0x47, 0x85, 0xe5, 0x40, 0x6e, 0x72, 0x5b, 0x8d, 0xa2, 0x8d, 0xff, - 0x38, 0xb8, 0xa9, 0xc6, 0x29, 0x70, 0x5d, 0xcd, 0x64, 0x99, 0x80, 0xab, 0x6a, 0x8a, 0x00, 0x2e, 0xab, 0xf9, 0x09, - 0xf5, 0xbd, 0xfa, 0x82, 0x8c, 0x5f, 0xea, 0xf0, 0xf5, 0x7e, 0xae, 0x20, 0xf4, 0xce, 0x4f, 0x59, 0x5b, 0x39, 0xf3, - 0x9c, 0xf7, 0xeb, 0x94, 0x83, 0xf7, 0x1b, 0xba, 0xee, 0xf0, 0xa9, 0xce, 0xbc, 0x0d, 0xd0, 0xfe, 0x29, 0x6f, 0xde, - 0xe9, 0x29, 0x62, 0xef, 0xe4, 0x6a, 0xee, 0xee, 0xff, 0x69, 0xe8, 0x79, 0x6f, 0x78, 0xaa, 0xb4, 0x95, 0xe3, 0x4a, - 0x8f, 0xde, 0xd1, 0xbf, 0x96, 0xda, 0x02, 0x87, 0xd5, 0x54, 0x0a, 0x1c, 0x54, 0x93, 0x2b, 0xb0, 0x5f, 0xcd, 0xab, - 0x5a, 0x3b, 0x00, 0x9b, 0xd5, 0x20, 0x03, 0x7b, 0xd5, 0x64, 0x0d, 0xd8, 0xaa, 0x46, 0xbb, 0x4f, 0x1e, 0xd8, 0xae, - 0x06, 0x6b, 0x60, 0x67, 0x64, 0x5e, 0xe7, 0xed, 0xfe, 0x33, 0xf1, 0xdc, 0xc0, 0x58, 0xaf, 0xdb, 0xcb, 0x3e, 0x33, - 0xef, 0x75, 0x0d, 0x4c, 0x97, 0x65, 0x5b, 0xac, 0x5a, 0x80, 0x0d, 0xfa, 0xef, 0x48, 0xd3, 0xd6, 0x0a, 0x7e, 0x23, - 0x90, 0xc0, 0xfc, 0xec, 0x2c, 0x60, 0x01, 0xa3, 0xff, 0xd0, 0xe3, 0xd8, 0xc0, 0x50, 0x4b, 0xac, 0x4f, 0xd6, 0x31, - 0x6d, 0xd3, 0xff, 0xc7, 0xc6, 0x66, 0x1b, 0x25, 0x48, 0xb7, 0xed, 0x35, 0xbe, 0xbc, 0x65, 0x47, 0x68, 0x3f, 0x52, - 0xd4, 0xa5, 0xb4, 0x0a, 0xf0, 0x57, 0x4b, 0x72, 0xf1, 0xe8, 0xb2, 0x3d, 0x13, 0xbe, 0x57, 0x67, 0xb1, 0xce, 0x94, - 0x90, 0x88, 0x1d, 0xe2, 0xea, 0xfe, 0xbf, 0x3c, 0xf5, 0x95, 0x42, 0x65, 0x11, 0x27, 0xc5, 0x47, 0x7c, 0x4c, 0xb6, - 0x0a, 0x4e, 0x11, 0xc7, 0x42, 0x8c, 0x43, 0x37, 0x4f, 0xce, 0x61, 0xd5, 0x24, 0x0a, 0xfb, 0x47, 0xc2, 0x6b, 0xd0, - 0xf0, 0x9b, 0x6c, 0x39, 0xc0, 0xce, 0x19, 0x86, 0xd0, 0x8c, 0xbe, 0xb3, 0x9c, 0x49, 0x95, 0x93, 0x57, 0x30, 0x8e, - 0xe8, 0x58, 0xfd, 0x8e, 0x94, 0x97, 0xf7, 0xa5, 0xca, 0x97, 0x43, 0x50, 0xb6, 0x1f, 0xe0, 0x1d, 0x14, 0xc5, 0xf4, - 0xb9, 0x0c, 0x16, 0xf8, 0x5e, 0x77, 0x0f, 0x0f, 0xf0, 0xc2, 0x26, 0xfc, 0x58, 0x2b, 0x4f, 0x78, 0x67, 0xab, 0x96, - 0xd1, 0x63, 0xe1, 0x60, 0x06, 0xeb, 0x20, 0xfe, 0xaf, 0x79, 0xdd, 0x32, 0x80, 0x12, 0x38, 0xe1, 0xe6, 0x4c, 0x7b, - 0x7d, 0x9d, 0x3e, 0xb0, 0x32, 0x69, 0x8a, 0x5d, 0xf4, 0x07, 0xe6, 0x2a, 0xca, 0x6b, 0xa8, 0xe2, 0x34, 0xbb, 0x91, - 0xba, 0xc0, 0xe3, 0x7b, 0xa5, 0x3b, 0x8f, 0x3b, 0x3c, 0x74, 0xed, 0x0a, 0x8a, 0x5c, 0x23, 0x06, 0xda, 0x5d, 0x00, - 0xdd, 0x2e, 0xc3, 0xc6, 0xbf, 0xea, 0x27, 0xa9, 0x08, 0x44, 0x34, 0xe3, 0x5e, 0xf8, 0x17, 0xb0, 0xad, 0x1b, 0xdd, - 0xa2, 0x94, 0x8e, 0x2e, 0x72, 0xeb, 0xad, 0xc4, 0x9d, 0xa9, 0x64, 0xa3, 0x41, 0x0d, 0x58, 0xce, 0xbd, 0xff, 0x3b, - 0xd5, 0x5c, 0x52, 0x7f, 0xc8, 0xc4, 0x41, 0xdf, 0x9a, 0xad, 0x90, 0x05, 0xff, 0x32, 0xc9, 0x7f, 0xc6, 0x3d, 0x3e, - 0xf1, 0xd5, 0xf3, 0x90, 0x36, 0xdd, 0xb4, 0x02, 0x76, 0xd7, 0x14, 0x59, 0x9f, 0xf2, 0x71, 0x11, 0x46, 0xce, 0x82, - 0x47, 0xf8, 0x03, 0x3a, 0x09, 0x71, 0xd9, 0xfb, 0xda, 0x8b, 0xbd, 0xe8, 0xb9, 0x4c, 0xfa, 0xc7, 0xac, 0x5e, 0xa4, - 0xe3, 0x3c, 0xd4, 0x66, 0xef, 0xa8, 0x5f, 0xa3, 0x0c, 0xd5, 0x9b, 0xb3, 0x9e, 0xc3, 0x36, 0x30, 0xea, 0x4e, 0x89, - 0x56, 0x11, 0xa5, 0x1b, 0x31, 0x68, 0x28, 0x39, 0x19, 0x5f, 0xe9, 0x6c, 0xbf, 0xbb, 0xbc, 0xe3, 0x56, 0x53, 0x0a, - 0x14, 0xfb, 0x46, 0xb9, 0xc6, 0x87, 0x9f, 0x25, 0x2b, 0x22, 0xcb, 0x7a, 0xc1, 0x78, 0xf6, 0x1a, 0xd6, 0x82, 0x69, - 0x8a, 0xb6, 0x50, 0x7b, 0x71, 0x71, 0xd1, 0x84, 0x6b, 0x9d, 0xdc, 0xb8, 0x3b, 0x1f, 0x16, 0x76, 0x41, 0x09, 0xb7, - 0xbd, 0xc2, 0xd3, 0xc1, 0xdc, 0x67, 0x46, 0x28, 0x89, 0x06, 0xe0, 0x04, 0x5c, 0x3a, 0x86, 0xeb, 0xc6, 0xf1, 0x5e, - 0x03, 0x78, 0x58, 0x2f, 0xe0, 0x0c, 0x50, 0xa9, 0x78, 0x45, 0x77, 0xab, 0x1e, 0xbf, 0x3b, 0x4b, 0xf3, 0xeb, 0x44, - 0x19, 0x14, 0x96, 0x83, 0x87, 0x96, 0x32, 0xcf, 0x34, 0xa8, 0x51, 0x83, 0x8d, 0x54, 0xac, 0x90, 0x17, 0xaf, 0x79, - 0xd0, 0xba, 0xea, 0x85, 0x3d, 0x41, 0x4a, 0x3f, 0x2f, 0x73, 0x0b, 0xaa, 0x81, 0x81, 0x84, 0x58, 0x36, 0x99, 0xd5, - 0x1f, 0x1d, 0x3c, 0x77, 0xaf, 0xa6, 0x9a, 0x6c, 0xb2, 0x05, 0x99, 0xf7, 0xeb, 0xef, 0xad, 0x24, 0x5b, 0x7e, 0x71, - 0x27, 0xdb, 0x5f, 0xef, 0x97, 0x42, 0xc9, 0xe8, 0xcd, 0x38, 0x3b, 0xd6, 0x83, 0x72, 0xd6, 0x81, 0x40, 0x44, 0x7e, - 0x5b, 0x1d, 0x0a, 0xc4, 0x62, 0x32, 0x82, 0x32, 0x85, 0x19, 0x61, 0xdf, 0xd0, 0x5d, 0x1e, 0xc6, 0xdb, 0xb7, 0x13, - 0xd8, 0x4c, 0x2c, 0x28, 0xc0, 0x58, 0xdc, 0x4e, 0x4e, 0x27, 0xd7, 0x50, 0x09, 0xe6, 0x31, 0xc7, 0x50, 0x09, 0xc9, - 0xbd, 0x72, 0x66, 0xfd, 0x58, 0x64, 0x15, 0x35, 0x06, 0xc9, 0x95, 0x55, 0xc0, 0xb2, 0xb7, 0x90, 0x90, 0x71, 0xc8, - 0x28, 0x72, 0x02, 0xfb, 0x45, 0x6a, 0x46, 0x41, 0x49, 0xfc, 0x6f, 0xe6, 0x9e, 0xb9, 0x99, 0x7b, 0xe5, 0xc3, 0xb2, - 0x34, 0x29, 0x49, 0x56, 0x69, 0x13, 0xf4, 0x13, 0x3a, 0x7e, 0x89, 0xd8, 0xe9, 0xb4, 0x21, 0x34, 0xb0, 0xc7, 0x38, - 0x32, 0x82, 0xa9, 0x30, 0xa1, 0x7a, 0x57, 0x6f, 0x40, 0x12, 0x78, 0x80, 0xd1, 0xce, 0x44, 0x4e, 0x68, 0xe0, 0xc3, - 0xd9, 0x68, 0xe3, 0xe6, 0xe1, 0x77, 0x4e, 0x54, 0xaa, 0x85, 0x9d, 0xe5, 0xdf, 0x5b, 0x70, 0xa5, 0xfd, 0x4d, 0x40, - 0x95, 0xc0, 0xc5, 0xe4, 0xdf, 0xbf, 0xfb, 0x71, 0xe8, 0xdf, 0x2a, 0xe7, 0x8f, 0x16, 0x8b, 0x6f, 0x23, 0x3b, 0x5c, - 0x82, 0xb5, 0xae, 0x23, 0xe7, 0x22, 0x3f, 0x33, 0x1b, 0xaa, 0xe1, 0xf7, 0x43, 0xdd, 0xee, 0xe4, 0x42, 0xa9, 0x5a, - 0x7c, 0x32, 0x72, 0xdc, 0x51, 0xc9, 0x13, 0x89, 0x86, 0x58, 0xd1, 0xf2, 0x2b, 0x26, 0x9e, 0xd1, 0xf8, 0x52, 0xf1, - 0x48, 0x16, 0xee, 0x1f, 0xb9, 0xf6, 0x92, 0xa9, 0x83, 0x6c, 0xc0, 0x64, 0x64, 0xae, 0xfc, 0x76, 0xdc, 0x68, 0x1f, - 0xe6, 0x0f, 0x7f, 0xaa, 0x4f, 0xc4, 0xde, 0xff, 0x9a, 0x51, 0x76, 0xbd, 0x2c, 0x24, 0x3f, 0x61, 0xe1, 0x09, 0x7f, - 0x1c, 0xa1, 0xe0, 0xe5, 0xf4, 0xf1, 0x82, 0x38, 0x8e, 0x9e, 0x2b, 0x76, 0x20, 0x4b, 0x25, 0x2a, 0xee, 0x22, 0x48, - 0x42, 0x14, 0xe1, 0x09, 0xa0, 0xe4, 0x7d, 0x5c, 0x89, 0x4a, 0xb3, 0x98, 0x98, 0x4f, 0x46, 0x2a, 0x00, 0x67, 0x07, - 0xd1, 0xd0, 0x4a, 0xa4, 0x27, 0xea, 0x24, 0xa2, 0x21, 0x47, 0x67, 0x83, 0xfe, 0x9d, 0x78, 0x16, 0x1b, 0xa2, 0x22, - 0x40, 0x4c, 0x41, 0xd4, 0xb1, 0x15, 0x6f, 0x94, 0x81, 0x2b, 0xea, 0xa1, 0x44, 0x82, 0xd6, 0xd4, 0x19, 0xa0, 0x29, - 0x21, 0x20, 0xc7, 0x5c, 0xee, 0xa1, 0x87, 0x19, 0xa6, 0x6d, 0xb7, 0xab, 0xa8, 0x30, 0xde, 0x1f, 0xce, 0xcb, 0xa8, - 0xd4, 0x76, 0x0a, 0x44, 0xa9, 0x7a, 0x1a, 0xc6, 0x38, 0x1d, 0x2b, 0x84, 0x77, 0x01, 0xc5, 0x25, 0xc9, 0x4a, 0x8c, - 0xfa, 0x6e, 0x34, 0x6d, 0xaa, 0x11, 0x12, 0x38, 0xe9, 0xdd, 0xf4, 0x3a, 0x40, 0x49, 0x0c, 0x26, 0x58, 0x1c, 0xc1, - 0x66, 0xf0, 0xc9, 0xb1, 0x46, 0x90, 0x94, 0x50, 0xa0, 0x54, 0x99, 0xf1, 0x47, 0xad, 0xa4, 0x20, 0xf1, 0x3e, 0xfc, - 0xfc, 0x53, 0x65, 0xf1, 0xc4, 0x0a, 0x1b, 0xef, 0x63, 0x7e, 0x5f, 0xc9, 0xab, 0x1e, 0x84, 0x89, 0x07, 0xc4, 0xb7, - 0x09, 0x1c, 0x61, 0x99, 0xca, 0x65, 0x83, 0x0c, 0x05, 0x28, 0x38, 0xd5, 0x9a, 0xb3, 0x38, 0x13, 0x9b, 0x46, 0xaa, - 0x30, 0xe8, 0x11, 0x4c, 0x91, 0xfc, 0x8b, 0xb8, 0x7f, 0xdf, 0x06, 0xc4, 0xf8, 0x14, 0x1f, 0xfa, 0xc9, 0xeb, 0x9d, - 0x85, 0x2a, 0x98, 0x10, 0xf5, 0xe2, 0x3f, 0x8f, 0x97, 0xc5, 0xae, 0x1f, 0x1f, 0x36, 0x15, 0x48, 0x20, 0xf6, 0x3c, - 0x0d, 0xce, 0x7f, 0xc6, 0x2c, 0x09, 0x03, 0x69, 0x71, 0x6f, 0x4a, 0xe0, 0x4d, 0x2f, 0x58, 0x9a, 0x4d, 0x80, 0x09, - 0x52, 0x9c, 0x8c, 0xb6, 0x16, 0xac, 0x53, 0x4f, 0x8d, 0xd2, 0x99, 0x13, 0x60, 0x52, 0xa9, 0x87, 0x90, 0x9e, 0x7a, - 0x81, 0xde, 0xb1, 0xa2, 0x1f, 0xe3, 0xad, 0x86, 0xb7, 0xec, 0x6a, 0x91, 0x0a, 0x8b, 0xd0, 0x19, 0x08, 0x2e, 0x0b, - 0x4e, 0xf1, 0xfb, 0x08, 0x75, 0x0d, 0xc3, 0x9a, 0xb1, 0xc9, 0x89, 0x1a, 0x2a, 0xc8, 0xb4, 0xee, 0xe9, 0x60, 0xef, - 0x65, 0xde, 0x24, 0xcc, 0x9c, 0x0f, 0x47, 0x94, 0x6c, 0x78, 0xe3, 0x1e, 0x4f, 0x3f, 0x20, 0xdd, 0x19, 0xa4, 0x4f, - 0x30, 0x3d, 0xa6, 0x50, 0x12, 0x41, 0x73, 0x5f, 0x5e, 0x92, 0xab, 0x47, 0x87, 0xbd, 0x52, 0x37, 0xdd, 0x2f, 0xf7, - 0x72, 0xd2, 0xb8, 0x85, 0xe9, 0x2a, 0xec, 0x76, 0xf7, 0xa3, 0x7e, 0x10, 0x38, 0xea, 0xa5, 0x9a, 0x66, 0xa0, 0x66, - 0x92, 0x62, 0x67, 0xf2, 0x56, 0xca, 0x37, 0x8e, 0xa5, 0x17, 0xd4, 0x79, 0x5a, 0x33, 0x6f, 0x72, 0x9f, 0x15, 0x8a, - 0xb2, 0xc2, 0xda, 0xa1, 0x8c, 0x61, 0xa8, 0x92, 0xe1, 0x71, 0x1a, 0xc1, 0xba, 0x28, 0x8a, 0xb6, 0xe8, 0x3a, 0x73, - 0x34, 0xa7, 0x5d, 0xa5, 0x4b, 0xb2, 0xc4, 0xf7, 0xe8, 0x47, 0x13, 0x95, 0xfd, 0x25, 0x7a, 0x91, 0x5a, 0x5a, 0x99, - 0xb6, 0x77, 0xd7, 0xd4, 0xf1, 0x3b, 0x1b, 0x85, 0x3f, 0xba, 0xc1, 0x67, 0xa6, 0xf3, 0xd1, 0x06, 0x37, 0x96, 0xee, - 0x08, 0x67, 0xb0, 0x6d, 0x63, 0x3f, 0xef, 0x03, 0x46, 0x85, 0x0f, 0xa8, 0xbe, 0x9e, 0xe6, 0x1d, 0xae, 0x1d, 0x71, - 0x53, 0x5b, 0xcf, 0xff, 0x66, 0x3a, 0x07, 0xe5, 0x07, 0x7c, 0xc0, 0xa6, 0xcf, 0xf6, 0x16, 0x2c, 0x95, 0xaf, 0x3b, - 0x01, 0x63, 0x49, 0xd5, 0x55, 0x98, 0xad, 0xd9, 0xd2, 0x60, 0x80, 0xe2, 0x22, 0x3f, 0x73, 0x2a, 0x88, 0x70, 0x89, - 0x4c, 0x70, 0x03, 0x45, 0x1f, 0x50, 0x42, 0x99, 0xb1, 0x08, 0x0f, 0xc6, 0x57, 0x98, 0x48, 0x8c, 0xf4, 0x27, 0x14, - 0xc6, 0xe3, 0x8e, 0x7f, 0x7e, 0xce, 0xcf, 0xbb, 0x66, 0xa7, 0xbd, 0xc2, 0xd8, 0xc4, 0xee, 0x59, 0xe8, 0xf8, 0x83, - 0x9c, 0xfd, 0xce, 0xfc, 0x45, 0x30, 0xad, 0xf3, 0x17, 0xc2, 0x7e, 0xe6, 0x44, 0x82, 0xbf, 0xf6, 0xd4, 0xf6, 0x06, - 0x62, 0x39, 0x11, 0xa5, 0x94, 0xfa, 0xc6, 0xbc, 0x55, 0x83, 0x05, 0x50, 0xc2, 0xec, 0xa7, 0x51, 0x7f, 0x6d, 0xe3, - 0x4f, 0x1d, 0x5a, 0x17, 0x4c, 0xbd, 0x16, 0x30, 0x64, 0x86, 0x11, 0x2d, 0x58, 0x13, 0x69, 0x64, 0xe6, 0x8f, 0x33, - 0xb9, 0x90, 0xf3, 0x3e, 0xf3, 0xe7, 0x19, 0x9f, 0x73, 0xea, 0xb7, 0xd5, 0xe6, 0x4a, 0xce, 0xd0, 0x5f, 0xc5, 0xb3, - 0xb7, 0x97, 0x18, 0xdd, 0xf5, 0x0e, 0x62, 0x24, 0xf6, 0x95, 0x3e, 0x9c, 0xfe, 0x73, 0x77, 0x8e, 0x5c, 0x06, 0x3c, - 0x9e, 0x8b, 0x15, 0x67, 0x7e, 0x80, 0xf2, 0x34, 0xb7, 0x36, 0x4e, 0x10, 0xa9, 0xc9, 0xd2, 0xb8, 0xf2, 0x7c, 0x2f, - 0xe3, 0x60, 0x41, 0x76, 0x6c, 0x87, 0x3d, 0xd3, 0xf7, 0xb4, 0xd7, 0xd7, 0x1e, 0xc8, 0x88, 0x3f, 0x97, 0xbe, 0x9f, - 0x40, 0x94, 0xab, 0xad, 0xa7, 0x2a, 0xaa, 0xe6, 0x5e, 0x1e, 0xac, 0xf3, 0x2b, 0x2f, 0xfd, 0xc9, 0xd3, 0x4e, 0xf7, - 0xe5, 0x6e, 0xdd, 0x80, 0x4c, 0x71, 0xa2, 0xf6, 0xc9, 0xc7, 0x0c, 0x11, 0xc5, 0xc5, 0x6f, 0x5c, 0x4f, 0xef, 0x30, - 0xe1, 0x7b, 0x5f, 0x20, 0xcb, 0x47, 0x2a, 0x71, 0xc1, 0x7a, 0x4e, 0x53, 0x63, 0xf9, 0x2e, 0xbb, 0xfd, 0xa2, 0x6d, - 0x83, 0x31, 0xe9, 0xd1, 0x8c, 0xcf, 0xfa, 0x6f, 0xac, 0x03, 0x00, 0x16, 0x7f, 0x97, 0xf8, 0x4a, 0xac, 0xe6, 0xb9, - 0xc9, 0x12, 0x5b, 0x71, 0xd5, 0x78, 0x5e, 0x27, 0x72, 0x90, 0xdd, 0x64, 0x87, 0x08, 0x4d, 0x11, 0x31, 0xed, 0xa5, - 0x7a, 0xbd, 0x41, 0xdf, 0x41, 0x84, 0x85, 0x8a, 0xea, 0x4c, 0x3f, 0x69, 0x0a, 0x33, 0x46, 0xa7, 0x23, 0x40, 0xe5, - 0x80, 0xa4, 0x2f, 0x0f, 0xbe, 0x7d, 0x25, 0x64, 0x05, 0xee, 0x72, 0xe7, 0xb2, 0x90, 0x02, 0xfb, 0xf0, 0xa1, 0xe9, - 0x6f, 0xcf, 0xf3, 0x3c, 0x57, 0x59, 0x3c, 0x8f, 0x7f, 0x92, 0x1c, 0x26, 0x26, 0xda, 0x1a, 0x45, 0x3c, 0xd1, 0x02, - 0x6f, 0x7f, 0xd1, 0x14, 0x09, 0x57, 0x95, 0xc2, 0x8a, 0x02, 0xbd, 0x6a, 0x74, 0x49, 0x50, 0xbc, 0x2b, 0x93, 0x40, - 0x67, 0x70, 0x5d, 0x32, 0x58, 0xb0, 0x3b, 0x15, 0xd2, 0x73, 0x6a, 0x2e, 0xd8, 0xce, 0x04, 0x78, 0x7d, 0x48, 0xe5, - 0xc6, 0x2c, 0x55, 0x48, 0x59, 0x54, 0xe7, 0x05, 0x78, 0xdb, 0xe3, 0xa0, 0xd5, 0x89, 0xc2, 0xde, 0x6b, 0x01, 0x68, - 0xc0, 0xf2, 0xb9, 0xcd, 0x03, 0x5b, 0x72, 0x80, 0xa6, 0x41, 0xd3, 0x31, 0x80, 0xec, 0xef, 0x7c, 0x95, 0xf4, 0xbf, - 0xbd, 0xe3, 0x4f, 0xeb, 0x16, 0x0c, 0x21, 0x63, 0x36, 0x4f, 0x9f, 0xb5, 0x68, 0x08, 0x14, 0x6f, 0xa3, 0x48, 0xc4, - 0x9f, 0x59, 0xab, 0x2a, 0x0d, 0x0c, 0xb9, 0x0e, 0x83, 0x5a, 0xa5, 0x9d, 0xcc, 0x99, 0x96, 0x59, 0xa9, 0x53, 0xb5, - 0xb0, 0x7b, 0x80, 0x0a, 0x3c, 0xa8, 0xde, 0x4b, 0x0d, 0x2a, 0x07, 0x18, 0x8a, 0xe2, 0xa2, 0x0c, 0x9d, 0x8a, 0x7b, - 0xfa, 0x3e, 0x4f, 0xb1, 0x09, 0xff, 0xf9, 0xb2, 0xf2, 0x8d, 0xe7, 0x20, 0x5e, 0x4a, 0xff, 0xb9, 0x53, 0x7e, 0x8c, - 0xbc, 0x86, 0xfa, 0xea, 0x2a, 0x0a, 0x73, 0x3c, 0x62, 0xbc, 0xb3, 0x31, 0x34, 0x02, 0xb1, 0x94, 0xe2, 0x09, 0xe1, - 0xc5, 0x36, 0x0a, 0x3e, 0x87, 0x0a, 0xb4, 0x19, 0x01, 0x58, 0xb8, 0xbb, 0x16, 0xfc, 0xcb, 0xd7, 0x90, 0xbf, 0x57, - 0x3c, 0xa2, 0xa9, 0x4c, 0x6e, 0x57, 0xa7, 0xec, 0x6b, 0xb8, 0xc2, 0xe8, 0xed, 0xfb, 0xbe, 0x5e, 0xe6, 0xeb, 0x4e, - 0xff, 0xe1, 0x13, 0x3e, 0x76, 0xa7, 0xfd, 0x65, 0xe7, 0x61, 0x9d, 0x91, 0x7f, 0x3c, 0x36, 0x79, 0x1b, 0xd6, 0xb4, - 0x1b, 0xec, 0x65, 0x8f, 0x89, 0xc3, 0x4c, 0x3c, 0x14, 0xe3, 0xdf, 0x16, 0x15, 0x38, 0xe6, 0x85, 0x06, 0x52, 0x6b, - 0x7f, 0x4e, 0x1f, 0xff, 0xaa, 0xb0, 0x43, 0xcc, 0x0e, 0xac, 0x04, 0x81, 0xee, 0x7b, 0x05, 0x6e, 0x29, 0x5d, 0x0a, - 0xb4, 0xf5, 0x68, 0x51, 0x2e, 0xae, 0xef, 0x01, 0x21, 0xb5, 0xb6, 0x59, 0xd5, 0x65, 0xa2, 0x71, 0x29, 0x8e, 0x57, - 0xa7, 0x3e, 0xfa, 0x03, 0x2d, 0xf6, 0xd9, 0xc5, 0x16, 0x9a, 0x5a, 0x88, 0x33, 0x71, 0x36, 0x2e, 0xb8, 0x19, 0x37, - 0xeb, 0xce, 0x81, 0x0a, 0x7d, 0x35, 0x18, 0xca, 0x92, 0xd0, 0xda, 0xc0, 0x10, 0x4c, 0x2b, 0x37, 0x58, 0x14, 0x25, - 0xdd, 0x51, 0x2f, 0x8e, 0xb6, 0x0d, 0x2e, 0xdc, 0x46, 0x8c, 0x72, 0xbc, 0x65, 0x7f, 0x81, 0x2a, 0x61, 0xfe, 0x92, - 0x59, 0xe4, 0x5d, 0x93, 0xce, 0x9f, 0x23, 0x3b, 0x10, 0xb3, 0xd4, 0x96, 0xb5, 0x5a, 0x85, 0x9b, 0x09, 0xf9, 0xa6, - 0x7b, 0x8a, 0x42, 0x81, 0x6d, 0x53, 0xb9, 0x8b, 0xff, 0x3d, 0xe2, 0x6a, 0xe7, 0x1c, 0xf9, 0x37, 0x9b, 0xe6, 0xb9, - 0x9e, 0x89, 0x03, 0xa2, 0x9c, 0xae, 0x82, 0x99, 0xc3, 0x70, 0xc7, 0x3d, 0xf5, 0xf3, 0x22, 0x3d, 0xcf, 0xa8, 0x63, - 0x25, 0xb7, 0xef, 0xd4, 0x2f, 0xfc, 0x52, 0x3c, 0xf6, 0x0b, 0x7d, 0x61, 0xcf, 0x95, 0x78, 0x4b, 0xb7, 0x4f, 0x4e, - 0xa2, 0xd5, 0xa8, 0x9c, 0x9a, 0x76, 0xe1, 0x25, 0xd4, 0x6c, 0x6f, 0x68, 0xaf, 0xcc, 0x2d, 0x7a, 0xd9, 0x1d, 0xee, - 0x57, 0x76, 0x8a, 0x22, 0x76, 0xa5, 0x60, 0x9f, 0x56, 0xd2, 0xe3, 0x4a, 0x9c, 0x73, 0xa1, 0xb3, 0x4e, 0x2d, 0xa0, - 0x28, 0xcb, 0x00, 0x2b, 0x2f, 0x15, 0xfa, 0x6d, 0xc5, 0x13, 0x94, 0x1c, 0xa6, 0x36, 0x1b, 0x47, 0xcf, 0x7a, 0x49, - 0x25, 0xf7, 0xd5, 0x06, 0xf7, 0x92, 0x91, 0xd6, 0xde, 0xe1, 0xf2, 0xce, 0x56, 0x1f, 0xf1, 0xb4, 0x87, 0xfb, 0x8f, - 0x59, 0x81, 0x7f, 0xab, 0xca, 0xfb, 0x86, 0x31, 0x14, 0x02, 0xb5, 0xce, 0xa5, 0xd4, 0xb4, 0x49, 0xb8, 0x64, 0x10, - 0xee, 0x1d, 0xac, 0x11, 0x51, 0x27, 0xb0, 0xbf, 0x46, 0xc9, 0x6a, 0x67, 0xc0, 0xe7, 0x49, 0x88, 0x98, 0x13, 0xe5, - 0xb1, 0x7f, 0x7e, 0xb7, 0xb2, 0xc9, 0x9f, 0x98, 0x43, 0x9d, 0xa1, 0x4a, 0x58, 0x87, 0xf8, 0x83, 0xb8, 0x79, 0xd5, - 0xeb, 0xdb, 0x25, 0xca, 0xdf, 0x4c, 0x4f, 0x2c, 0xcc, 0x0a, 0x2b, 0xf8, 0x4b, 0x29, 0x5f, 0xdf, 0xf1, 0x01, 0xd4, - 0x67, 0x93, 0x1f, 0xff, 0xbc, 0xa1, 0x7b, 0xd9, 0x95, 0xff, 0x72, 0x87, 0x40, 0x31, 0xed, 0xe2, 0x70, 0x8e, 0x4b, - 0x87, 0xc2, 0xec, 0x02, 0xc3, 0xe2, 0x45, 0x55, 0x1d, 0xf0, 0xe1, 0x39, 0xe2, 0xe3, 0xf3, 0x24, 0x2e, 0x88, 0x84, - 0xd2, 0xfc, 0x79, 0x50, 0x37, 0xa3, 0xe3, 0xc2, 0x86, 0x3e, 0x38, 0x9c, 0xd1, 0x00, 0x84, 0xac, 0xb1, 0xc9, 0xf6, - 0x63, 0x95, 0x52, 0x99, 0x59, 0x3a, 0x72, 0xc7, 0xc6, 0x76, 0xb5, 0xd4, 0xe3, 0xbb, 0x7e, 0x1f, 0xee, 0x64, 0xd3, - 0xb2, 0x7e, 0xca, 0x10, 0xfb, 0xa8, 0x0b, 0xc3, 0x05, 0xc8, 0xda, 0x0f, 0xb9, 0xf6, 0x62, 0x19, 0xdb, 0x80, 0x3e, - 0xc4, 0xfe, 0xa7, 0x5d, 0x9c, 0xd1, 0xad, 0xf0, 0xb1, 0x58, 0xb4, 0x3a, 0xdb, 0x90, 0x24, 0x07, 0x46, 0x73, 0x48, - 0x30, 0x6e, 0x44, 0x68, 0x1b, 0x94, 0xe0, 0xb1, 0x62, 0x0a, 0x37, 0xe2, 0xfe, 0x38, 0x58, 0x68, 0x55, 0xd1, 0x8d, - 0xb9, 0x8d, 0x6d, 0x14, 0x67, 0xf0, 0xf5, 0x80, 0x7f, 0x15, 0x65, 0x7c, 0x80, 0xbb, 0x41, 0xe4, 0xce, 0x9e, 0x97, - 0x92, 0x48, 0x2d, 0xa7, 0xdb, 0x56, 0x6c, 0xe7, 0x97, 0xd8, 0xed, 0x82, 0x3d, 0x5f, 0x16, 0xe6, 0xcc, 0xd6, 0x20, - 0x33, 0x8c, 0xbd, 0xb7, 0xc0, 0xbb, 0x6d, 0x29, 0x4c, 0x76, 0xe9, 0x1b, 0xa8, 0x84, 0x56, 0xe7, 0xa3, 0xc3, 0x78, - 0x53, 0x07, 0xae, 0xe2, 0x78, 0x1a, 0xdb, 0x56, 0x62, 0x34, 0x46, 0x1e, 0xc9, 0x30, 0x95, 0xe1, 0x10, 0x7f, 0x24, - 0xbb, 0x29, 0x37, 0xd3, 0xa5, 0xce, 0x2e, 0x0d, 0x10, 0x74, 0x85, 0x55, 0x0f, 0x85, 0x24, 0x11, 0x70, 0xcb, 0x15, - 0xc8, 0xc3, 0x70, 0x3f, 0xaf, 0xc6, 0xc8, 0xe1, 0x6c, 0x81, 0xd0, 0xf0, 0xb2, 0x51, 0x90, 0x39, 0xf1, 0xed, 0x21, - 0xf1, 0x72, 0x6a, 0x7a, 0xc7, 0x11, 0x0f, 0x86, 0xea, 0x36, 0x0f, 0x5e, 0x8e, 0xaa, 0x4e, 0x67, 0xe9, 0x91, 0x70, - 0x4b, 0xe7, 0x59, 0x49, 0xca, 0x51, 0x35, 0x05, 0x7a, 0x8e, 0x34, 0x1a, 0xca, 0x5b, 0x5b, 0x29, 0x81, 0x55, 0xd0, - 0x32, 0x39, 0xfd, 0xbc, 0x23, 0x12, 0x91, 0x70, 0x21, 0xa0, 0xf8, 0xcb, 0x28, 0xa4, 0xd1, 0x5b, 0xcd, 0xc7, 0x30, - 0xc8, 0x70, 0x9d, 0x57, 0x5c, 0x0a, 0xfe, 0xf8, 0xc5, 0x01, 0xf4, 0x50, 0x94, 0x0d, 0xdc, 0x2f, 0x16, 0xfe, 0xcc, - 0x2e, 0x46, 0xf4, 0x36, 0x9b, 0xa1, 0xba, 0xcd, 0xe8, 0x27, 0xd6, 0xe7, 0x95, 0x22, 0x76, 0xcf, 0x0e, 0xed, 0xc1, - 0x8e, 0x19, 0x59, 0x5d, 0x25, 0x1d, 0xcd, 0x04, 0x99, 0xf4, 0x32, 0xf2, 0x0c, 0x41, 0x84, 0x0e, 0xd8, 0x27, 0x87, - 0x36, 0x4a, 0x87, 0xf9, 0x0a, 0xc2, 0x3f, 0xc7, 0x7c, 0x0e, 0x89, 0x6e, 0x76, 0x09, 0x8e, 0x7a, 0x8d, 0x48, 0x3a, - 0x89, 0x50, 0x04, 0x5e, 0xc8, 0x7a, 0x22, 0x98, 0x78, 0x41, 0xef, 0x26, 0xb8, 0xe2, 0xc5, 0xd1, 0x4d, 0x07, 0xcb, - 0x61, 0x46, 0xfc, 0x1b, 0x13, 0x46, 0xee, 0x12, 0xe2, 0xdb, 0x03, 0x84, 0x3b, 0xd8, 0x29, 0x88, 0xea, 0xe5, 0x56, - 0x9b, 0x5e, 0x9c, 0xa2, 0x9e, 0xc7, 0xf9, 0x78, 0x36, 0xd6, 0x91, 0x17, 0x72, 0x38, 0x5b, 0xc4, 0x30, 0x40, 0x16, - 0xc2, 0x93, 0x9b, 0x76, 0x97, 0x10, 0x90, 0x9c, 0x4c, 0x65, 0x59, 0xde, 0xc4, 0x4d, 0x27, 0x60, 0x91, 0xe7, 0x76, - 0x69, 0x4a, 0xf1, 0xf4, 0x9f, 0xaa, 0xb1, 0x0d, 0x67, 0x8b, 0x8c, 0x63, 0xd0, 0x62, 0x95, 0xce, 0x20, 0x10, 0x17, - 0xe5, 0x46, 0x4b, 0x2f, 0x0e, 0x73, 0xb8, 0xf9, 0xf7, 0xb8, 0xbc, 0x8d, 0x09, 0x20, 0x1f, 0xbc, 0x49, 0x3a, 0x40, - 0xcf, 0xf2, 0x7c, 0xce, 0xd0, 0x4b, 0x6f, 0x73, 0x91, 0x4c, 0x84, 0xff, 0xd3, 0xc9, 0x47, 0xa2, 0x1c, 0xe9, 0x15, - 0x72, 0x9c, 0x50, 0x51, 0xb2, 0x9d, 0x10, 0xd5, 0xcb, 0xc3, 0x7f, 0x61, 0xd5, 0x11, 0x02, 0xe7, 0xdb, 0x84, 0x2f, - 0x5f, 0x6e, 0xf9, 0xc1, 0xd7, 0x97, 0xec, 0x44, 0x28, 0xe5, 0x1f, 0x18, 0x87, 0x98, 0x56, 0x32, 0xb1, 0x63, 0x40, - 0x64, 0x7a, 0x58, 0xc0, 0x72, 0xe0, 0x66, 0xe4, 0xf1, 0xe3, 0xd6, 0x38, 0xd3, 0x14, 0x9f, 0xab, 0xff, 0x9f, 0xd8, - 0x7a, 0x10, 0xd7, 0x6e, 0x2f, 0x8d, 0x48, 0x62, 0x1a, 0xa3, 0x01, 0xf3, 0x8a, 0x06, 0x68, 0x9c, 0x94, 0x01, 0xc3, - 0x5e, 0x59, 0xfa, 0x85, 0x1e, 0x63, 0x93, 0x47, 0xaa, 0x99, 0x98, 0x1f, 0x21, 0x64, 0xbb, 0x46, 0xc1, 0x44, 0x12, - 0x8c, 0xf6, 0x2d, 0x50, 0xd8, 0x81, 0x14, 0x53, 0xdd, 0x01, 0xf9, 0x9c, 0xcb, 0xc8, 0x6b, 0x20, 0x1b, 0x7d, 0xbe, - 0xb9, 0xd7, 0xaf, 0x13, 0xfb, 0xd2, 0xa3, 0x39, 0x84, 0x48, 0x23, 0xf2, 0xfb, 0xf0, 0xfe, 0x58, 0xaf, 0x99, 0x77, - 0xbd, 0xca, 0x14, 0x2f, 0xc1, 0xc8, 0x07, 0x37, 0x96, 0x90, 0x0f, 0x1a, 0xac, 0x02, 0x73, 0xf2, 0x35, 0xaa, 0xb5, - 0x43, 0xcc, 0xce, 0xf3, 0x26, 0x47, 0xde, 0x76, 0x75, 0x54, 0x51, 0x58, 0xad, 0xc0, 0xf9, 0x55, 0x03, 0xad, 0xc4, - 0x07, 0xf2, 0x2f, 0x43, 0xa2, 0x8a, 0x09, 0x61, 0x80, 0x1e, 0x19, 0xe7, 0x1f, 0x84, 0x28, 0xe8, 0x32, 0xa9, 0x5a, - 0x36, 0xfb, 0x97, 0x9a, 0xc3, 0x55, 0x60, 0x04, 0xec, 0x36, 0xa6, 0x31, 0x8d, 0xe7, 0xe3, 0x28, 0x66, 0xd6, 0xbc, - 0x2b, 0x89, 0xaf, 0x70, 0x2e, 0x08, 0x2a, 0xac, 0xe1, 0xbe, 0xcb, 0xff, 0xfd, 0x7c, 0xfc, 0x90, 0x97, 0x62, 0xe7, - 0xd7, 0xe5, 0x1a, 0xfa, 0x61, 0xff, 0x75, 0x29, 0x56, 0xbd, 0x49, 0x2d, 0x7a, 0x37, 0x9a, 0x36, 0x8e, 0xff, 0x7c, - 0x76, 0xb1, 0x91, 0x4e, 0xef, 0x78, 0xcb, 0x7b, 0xd0, 0x37, 0xa7, 0xe9, 0x69, 0x5c, 0xe0, 0xe7, 0x2c, 0x2f, 0x67, - 0xff, 0x95, 0xbb, 0x94, 0xc7, 0xf5, 0x7b, 0x76, 0xdd, 0xa1, 0x39, 0xad, 0xbd, 0xb1, 0xec, 0xdd, 0xb3, 0x2b, 0xfe, - 0x1e, 0x81, 0x2c, 0xbe, 0x08, 0xc9, 0xa4, 0x52, 0x09, 0x20, 0xd0, 0x5c, 0x0f, 0x7e, 0xf7, 0xc4, 0x28, 0xa5, 0x1e, - 0xef, 0x3f, 0x26, 0x5f, 0x95, 0x75, 0xb8, 0x3b, 0xb7, 0x40, 0xd6, 0x23, 0xfd, 0x3b, 0x4f, 0x37, 0xba, 0x5f, 0xd0, - 0xa8, 0x3a, 0x75, 0x90, 0x19, 0x8d, 0x33, 0x2d, 0x0d, 0xf9, 0xb7, 0x8d, 0xe6, 0x8c, 0xc2, 0xb7, 0x82, 0x46, 0x74, - 0x13, 0xe1, 0x1f, 0x57, 0x8d, 0x03, 0x4a, 0x0a, 0xf8, 0x61, 0x9b, 0xf6, 0x6d, 0xf7, 0x72, 0x2f, 0xa4, 0xa9, 0xf2, - 0xcb, 0x33, 0x16, 0x18, 0xb4, 0x0f, 0x74, 0x66, 0x47, 0xff, 0x7f, 0x0a, 0x68, 0xbd, 0x88, 0x51, 0xb2, 0x95, 0x3a, - 0x40, 0x5c, 0x6c, 0xe3, 0xe6, 0x0b, 0xbd, 0x71, 0x9a, 0x0b, 0x67, 0x1e, 0xf5, 0xe8, 0x24, 0xdd, 0x02, 0x18, 0xd5, - 0xfc, 0x7e, 0xc4, 0xab, 0x53, 0x57, 0x46, 0x7c, 0x54, 0xbc, 0xa3, 0xbb, 0x0b, 0xcc, 0xf6, 0xbf, 0xf2, 0x2e, 0x46, - 0x34, 0x7f, 0xf7, 0x11, 0xe8, 0x86, 0x1f, 0xb3, 0xd3, 0x37, 0x9f, 0xf9, 0xe3, 0x03, 0x3e, 0x0c, 0xed, 0x1e, 0xa3, - 0x79, 0x67, 0xdc, 0x9a, 0x27, 0x3c, 0x31, 0xc8, 0x0c, 0xe0, 0xb2, 0xcf, 0xde, 0x7b, 0x2c, 0xe3, 0xc0, 0x77, 0x20, - 0x56, 0x26, 0xf3, 0x16, 0x30, 0x29, 0x17, 0x23, 0xa4, 0x35, 0x32, 0xfa, 0x37, 0xe0, 0x45, 0xc9, 0xe8, 0x9f, 0xce, - 0x3d, 0x8a, 0x6e, 0x48, 0xf4, 0xc9, 0x93, 0x01, 0xcb, 0x3a, 0x28, 0x5a, 0x62, 0x52, 0x21, 0x3a, 0x84, 0x2c, 0x13, - 0xa0, 0xf4, 0x49, 0xa0, 0xa1, 0xf0, 0x77, 0x2d, 0x27, 0xbd, 0x9f, 0x7b, 0x66, 0x82, 0xa4, 0xc7, 0xe4, 0x28, 0x8d, - 0x4c, 0x18, 0xf9, 0x73, 0xcd, 0xcb, 0xeb, 0xeb, 0xa7, 0x76, 0x7b, 0xd0, 0x7c, 0x64, 0xbf, 0x95, 0xe6, 0xc4, 0xe4, - 0x6b, 0xad, 0x06, 0x2b, 0x79, 0x03, 0x28, 0x9b, 0x7d, 0x41, 0x2b, 0x60, 0xf1, 0x5b, 0x0d, 0x61, 0xe9, 0x99, 0x0c, - 0xb4, 0x06, 0x4e, 0xd2, 0x73, 0x36, 0xb8, 0x6e, 0x98, 0x1f, 0x91, 0x5e, 0xaf, 0x98, 0xa8, 0x32, 0xa7, 0x27, 0x7d, - 0xba, 0xb9, 0x1e, 0x7b, 0xb1, 0xd0, 0x87, 0xd4, 0x13, 0xfa, 0x93, 0x17, 0xe1, 0x6c, 0xf9, 0xb9, 0xec, 0x3f, 0x4d, - 0x20, 0x75, 0xd5, 0x18, 0x2d, 0x74, 0x7e, 0x3d, 0xbe, 0x9b, 0x35, 0x3e, 0x1a, 0xd9, 0xea, 0x6d, 0xbb, 0x73, 0x64, - 0xb9, 0x77, 0x8b, 0x59, 0x5f, 0x42, 0x3e, 0xa3, 0x58, 0x33, 0x99, 0x83, 0x9c, 0x23, 0xb4, 0xbf, 0xd6, 0x95, 0xe4, - 0xb8, 0xf6, 0x61, 0x4e, 0x41, 0x7a, 0x6c, 0x0d, 0xeb, 0x20, 0x6a, 0xbe, 0xad, 0x7d, 0x06, 0x2d, 0xbf, 0x9e, 0x7a, - 0x9d, 0x16, 0x4c, 0xf2, 0xa4, 0x73, 0x5f, 0xf7, 0x8f, 0x34, 0xe2, 0x5e, 0x7a, 0x59, 0x13, 0x45, 0xb7, 0x48, 0x40, - 0xd7, 0x2a, 0x2d, 0xf4, 0xb2, 0xe2, 0x3c, 0xad, 0xe8, 0x4f, 0x33, 0xe6, 0x51, 0xc9, 0xaa, 0x51, 0xa9, 0x9e, 0x5c, - 0x63, 0x9c, 0x29, 0xeb, 0x09, 0x20, 0x17, 0x45, 0x02, 0xc7, 0x59, 0x6f, 0xd7, 0xa7, 0x4b, 0x43, 0x07, 0xf1, 0xd1, - 0xdb, 0xb8, 0xe9, 0xbc, 0x83, 0x69, 0x2c, 0xdd, 0x9f, 0x48, 0x67, 0x19, 0xc3, 0x89, 0x2a, 0x4b, 0xf2, 0xb4, 0x1c, - 0x85, 0xba, 0xa3, 0xbb, 0x20, 0x29, 0x4b, 0xf6, 0x46, 0x3b, 0xfb, 0xe3, 0x7a, 0xf2, 0x28, 0xfb, 0x30, 0xec, 0xa1, - 0x0a, 0xdc, 0x43, 0xaa, 0xef, 0x72, 0xff, 0xba, 0xcc, 0x94, 0xa6, 0xc1, 0xfe, 0xc7, 0xd7, 0xa1, 0x03, 0x3f, 0x0e, - 0x6e, 0xc7, 0x11, 0x12, 0x28, 0xb7, 0x98, 0xa6, 0x0c, 0x5b, 0x4e, 0x30, 0xd9, 0xee, 0x0d, 0x37, 0xc5, 0xd5, 0x9e, - 0x4b, 0x14, 0x83, 0x25, 0xf7, 0xc0, 0xcb, 0x67, 0xb4, 0x7f, 0x62, 0xeb, 0x26, 0xe6, 0xa9, 0x6b, 0xe1, 0xa3, 0xd4, - 0xc2, 0x34, 0x74, 0x60, 0xb0, 0xc8, 0x59, 0x92, 0x8c, 0x04, 0xbb, 0xfc, 0xd2, 0x6a, 0x27, 0xcf, 0x72, 0x25, 0xd3, - 0xd7, 0x5e, 0x4f, 0x4c, 0x31, 0xd2, 0xb0, 0xa5, 0xed, 0x70, 0xd8, 0xc9, 0x79, 0x02, 0x22, 0x44, 0x54, 0xcf, 0x97, - 0xb8, 0xa6, 0xbe, 0x10, 0x67, 0xdd, 0xf9, 0x32, 0x56, 0xb4, 0xe7, 0x41, 0x01, 0x08, 0xad, 0x36, 0xad, 0x54, 0x17, - 0xdc, 0xd0, 0x23, 0x48, 0x77, 0xeb, 0xe5, 0x1d, 0x14, 0x55, 0xcd, 0xf4, 0x60, 0xd2, 0x8b, 0x1f, 0xe7, 0x5d, 0xe1, - 0x61, 0x16, 0x19, 0x2a, 0x80, 0x1b, 0xa3, 0xef, 0xe0, 0x72, 0x7d, 0xcf, 0x43, 0xb8, 0xb5, 0xe6, 0x4c, 0xcf, 0x4f, - 0x5b, 0x8f, 0x78, 0xf1, 0xe6, 0x61, 0x1c, 0xc2, 0x5d, 0x6c, 0x7d, 0xfa, 0x24, 0x5f, 0x3b, 0x6c, 0xe7, 0xd1, 0xa2, - 0xd0, 0xd6, 0xe5, 0xd4, 0xf6, 0xe2, 0xce, 0xe7, 0xf9, 0x27, 0xb5, 0x05, 0xca, 0x0a, 0xbc, 0xf6, 0xea, 0x3e, 0x22, - 0x14, 0x73, 0x07, 0xdf, 0xfe, 0x2f, 0x1d, 0xb5, 0xdd, 0x7c, 0xde, 0x0e, 0x67, 0x46, 0x0f, 0xcf, 0x48, 0x88, 0xba, - 0x3c, 0xd8, 0x24, 0xd7, 0xaf, 0xfe, 0xe9, 0x29, 0x7e, 0xa5, 0x9d, 0xe6, 0x5f, 0x73, 0xce, 0x0b, 0x63, 0x53, 0x3e, - 0xdb, 0x47, 0x9a, 0x30, 0xba, 0x46, 0x84, 0xcb, 0xef, 0xdb, 0xd0, 0x4a, 0x83, 0x8c, 0x48, 0x08, 0x79, 0xbd, 0x75, - 0x05, 0xb8, 0xef, 0x2f, 0xdb, 0x1d, 0xbc, 0xa5, 0x44, 0xe2, 0x8d, 0xea, 0x38, 0x6e, 0xcf, 0xc8, 0xc2, 0xf5, 0xfd, - 0x5b, 0x07, 0x82, 0x7d, 0xad, 0x7d, 0x25, 0xbf, 0xdc, 0x39, 0x7a, 0x01, 0x06, 0x94, 0x30, 0x84, 0x27, 0x51, 0xff, - 0x97, 0xd8, 0x88, 0xd4, 0x6d, 0xc6, 0x74, 0xc2, 0x84, 0xfd, 0x59, 0xd1, 0xaa, 0xad, 0xf4, 0x00, 0x28, 0xa6, 0x4e, - 0xae, 0x06, 0x51, 0x74, 0x87, 0x26, 0xe2, 0x8e, 0x39, 0x5a, 0xde, 0x13, 0x9a, 0xb5, 0x40, 0x15, 0x4e, 0x61, 0xcf, - 0xa3, 0x50, 0x9a, 0xe1, 0x19, 0xf4, 0x01, 0xf6, 0x52, 0x84, 0x9c, 0xb9, 0x24, 0x79, 0xe6, 0xc0, 0x6b, 0x13, 0x04, - 0x69, 0x74, 0xb7, 0x7a, 0x4a, 0xb1, 0x8b, 0x6c, 0xb7, 0x20, 0xe9, 0xcd, 0x22, 0x74, 0x2b, 0x56, 0x49, 0x8a, 0xbb, - 0x99, 0x8a, 0xad, 0x0e, 0x1e, 0x61, 0x8f, 0x48, 0xdf, 0x96, 0xfd, 0xbd, 0x75, 0xc0, 0x42, 0x17, 0x45, 0x4d, 0x4a, - 0xed, 0xbf, 0x29, 0x1d, 0x38, 0xa1, 0x86, 0x09, 0x05, 0x05, 0xfb, 0x6c, 0xdc, 0x62, 0xbc, 0x7b, 0x6b, 0x6d, 0x6f, - 0x21, 0xf0, 0x2a, 0x34, 0x37, 0xd5, 0x82, 0x5c, 0xe1, 0x0b, 0x64, 0xc9, 0xb5, 0x15, 0x42, 0xd7, 0x37, 0x2d, 0xbb, - 0xf0, 0xfc, 0xc2, 0xf4, 0xc7, 0x56, 0x29, 0xea, 0x52, 0x90, 0x4b, 0x38, 0xb5, 0xb2, 0x46, 0x57, 0x1f, 0xd8, 0x9a, - 0x8e, 0x51, 0xbb, 0x33, 0xce, 0x5e, 0x21, 0x90, 0xfc, 0x89, 0x4a, 0x9d, 0x53, 0x9a, 0x11, 0x18, 0x5e, 0x0f, 0x8a, - 0xd5, 0x2f, 0xb9, 0x16, 0x30, 0x0e, 0x0f, 0xf4, 0xc7, 0xa0, 0x48, 0x9e, 0x64, 0x62, 0x0e, 0x03, 0x4f, 0xe5, 0xb0, - 0x73, 0xcf, 0xe9, 0x4e, 0xe6, 0xf7, 0xbe, 0xb1, 0xb7, 0xc7, 0xae, 0xe3, 0x96, 0x31, 0x3f, 0x8c, 0x20, 0x6a, 0x25, - 0xc2, 0x48, 0x45, 0x1e, 0x31, 0x80, 0x12, 0x4e, 0xae, 0x1b, 0x70, 0xa8, 0xa9, 0x36, 0xdc, 0xa7, 0xe8, 0x08, 0xcc, - 0xa9, 0xcb, 0x34, 0xaa, 0x39, 0x55, 0x99, 0x20, 0x84, 0xcf, 0x8d, 0x5b, 0xe7, 0x78, 0x02, 0x33, 0xed, 0x80, 0xd5, - 0x26, 0xaf, 0x53, 0x1c, 0x84, 0xcc, 0xd4, 0x9d, 0x2d, 0x1a, 0x13, 0x49, 0x4d, 0xb5, 0x4b, 0xad, 0x05, 0xe3, 0x64, - 0xb3, 0x6b, 0xd4, 0x6e, 0x2b, 0x32, 0xb8, 0x88, 0x15, 0x0f, 0x64, 0x04, 0x38, 0xba, 0x96, 0x6b, 0x94, 0x27, 0x47, - 0x5a, 0x10, 0xe6, 0x26, 0x39, 0x8e, 0x98, 0xb6, 0x7f, 0xdc, 0x8d, 0xe8, 0x66, 0x9e, 0x99, 0x8a, 0xc3, 0x5f, 0xbd, - 0xe7, 0xb6, 0x5e, 0x59, 0x2a, 0xd6, 0xf3, 0x2c, 0x25, 0xeb, 0x95, 0xcf, 0x2c, 0xa5, 0x21, 0xb9, 0xb0, 0x16, 0xd8, - 0x6c, 0x9a, 0xa5, 0xd9, 0x72, 0x7a, 0xde, 0xb9, 0x45, 0x66, 0x5e, 0xf0, 0x08, 0x53, 0xde, 0xae, 0xbc, 0x44, 0x67, - 0x03, 0xf6, 0x3f, 0xfb, 0x7c, 0x09, 0x9a, 0x19, 0x2b, 0x34, 0xc7, 0xbb, 0xc2, 0x1c, 0x12, 0x59, 0x61, 0xd4, 0x8f, - 0x4b, 0xf9, 0xec, 0x5d, 0x70, 0xda, 0x6a, 0xe7, 0x46, 0x05, 0x85, 0xef, 0x4d, 0x52, 0x60, 0x22, 0x09, 0x6c, 0x72, - 0x34, 0xee, 0x83, 0xf3, 0xac, 0x9c, 0xe9, 0x97, 0x03, 0x04, 0xff, 0x89, 0x6d, 0xc6, 0x35, 0x27, 0x30, 0x77, 0x06, - 0x77, 0x4a, 0xa8, 0x6e, 0x88, 0xe1, 0xf5, 0xd9, 0x75, 0x4e, 0x56, 0x1c, 0x73, 0x4b, 0xb2, 0x10, 0xe0, 0xb5, 0x07, - 0xb7, 0xcf, 0x33, 0x6b, 0x71, 0xa7, 0xe2, 0x34, 0xd4, 0x66, 0x5f, 0xfa, 0xcc, 0xd7, 0x83, 0x5f, 0x8d, 0x1c, 0x65, - 0x5c, 0xe0, 0x66, 0xd7, 0x8b, 0x81, 0x21, 0x34, 0x9e, 0x05, 0xe8, 0x11, 0x4f, 0xe9, 0xbf, 0x80, 0x10, 0xbf, 0x1b, - 0xfc, 0x2a, 0x33, 0x83, 0xd5, 0xd7, 0x2a, 0x06, 0x89, 0x9e, 0x64, 0x42, 0x81, 0x91, 0x61, 0xe8, 0xba, 0x2a, 0x8b, - 0x84, 0x37, 0xbc, 0xd8, 0xcd, 0xee, 0xcd, 0x98, 0x3f, 0x60, 0xa8, 0x43, 0xf8, 0x25, 0xb1, 0x27, 0xe6, 0x39, 0x9c, - 0x6a, 0xe6, 0x65, 0x76, 0x56, 0x45, 0x63, 0xbd, 0x59, 0xe3, 0x89, 0x09, 0xd5, 0x87, 0x68, 0xdb, 0x37, 0xc5, 0xdc, - 0x6e, 0xf7, 0xd6, 0x87, 0xd3, 0x44, 0x8d, 0x98, 0x99, 0x9a, 0x8f, 0xfb, 0xc6, 0x0a, 0x69, 0x33, 0x52, 0x64, 0x12, - 0xaa, 0x0c, 0x56, 0xc2, 0xc8, 0x3d, 0xbd, 0x6d, 0x75, 0x74, 0x5a, 0x00, 0x4e, 0x34, 0xcb, 0xdb, 0x4a, 0x64, 0xa3, - 0xbd, 0xb6, 0x1b, 0x85, 0xa8, 0x17, 0x3d, 0x9e, 0x51, 0x28, 0x15, 0x37, 0x34, 0x70, 0x6e, 0x06, 0x02, 0x4b, 0x3f, - 0xc5, 0x4b, 0xd8, 0x8b, 0xae, 0x3d, 0x6b, 0xc2, 0xb5, 0x51, 0x7b, 0x87, 0xb4, 0xac, 0x54, 0x4b, 0xd9, 0x77, 0x8e, - 0x74, 0xe3, 0x85, 0xaa, 0x97, 0xb9, 0xd0, 0xb9, 0xda, 0x4f, 0x7c, 0x6c, 0x1b, 0x23, 0x4d, 0xed, 0x9a, 0xfe, 0x66, - 0xce, 0x36, 0xd7, 0x99, 0xac, 0x90, 0x1f, 0x2c, 0x43, 0xfe, 0x04, 0xe9, 0xb6, 0x91, 0x4d, 0xac, 0xc4, 0xfa, 0x85, - 0x1f, 0xf0, 0x0e, 0x3a, 0x67, 0x2d, 0x3b, 0xb0, 0x36, 0xdb, 0x2e, 0x58, 0x26, 0x3f, 0x58, 0xae, 0x5d, 0xe3, 0x37, - 0x7c, 0x08, 0x57, 0xb2, 0x3a, 0x97, 0x9d, 0xec, 0x3d, 0xfe, 0x45, 0xfd, 0xf2, 0xfb, 0x19, 0x3d, 0x8b, 0x0f, 0x96, - 0x35, 0xde, 0x4c, 0x9f, 0xb2, 0x32, 0xfb, 0xc5, 0xed, 0x5b, 0x8b, 0x8f, 0x37, 0x97, 0x36, 0x38, 0x8f, 0x61, 0x68, - 0xef, 0xc5, 0xdd, 0x83, 0xfa, 0xc3, 0x70, 0x56, 0x4e, 0xd0, 0x6a, 0x18, 0x19, 0xe0, 0xce, 0xd6, 0xf3, 0x05, 0xbd, - 0xc7, 0xc6, 0x4c, 0x1f, 0xee, 0xf9, 0xd0, 0xbb, 0xfc, 0xc7, 0xcb, 0x7e, 0x24, 0x9c, 0x3d, 0x3a, 0xbb, 0x40, 0xd0, - 0x5a, 0xd7, 0x56, 0x4a, 0xf5, 0x98, 0xd7, 0x2e, 0x8e, 0xd0, 0x92, 0x3d, 0x2f, 0x75, 0x34, 0xff, 0xd0, 0x2a, 0x87, - 0x0d, 0x1a, 0x63, 0xf5, 0xbe, 0xd5, 0x96, 0x46, 0x6f, 0x3f, 0x10, 0x16, 0xa6, 0xa1, 0x52, 0x81, 0x80, 0x4a, 0xff, - 0xcc, 0x26, 0x5c, 0x7b, 0x9b, 0xc9, 0x28, 0x7d, 0x8a, 0x30, 0x7b, 0xd4, 0x93, 0xc5, 0xfb, 0x8e, 0x9d, 0xac, 0xd5, - 0x1b, 0xca, 0x74, 0x58, 0x69, 0x33, 0x59, 0xa9, 0x11, 0x46, 0x0c, 0x33, 0x9b, 0xe1, 0x85, 0x2a, 0xa7, 0xc3, 0xae, - 0x04, 0x81, 0xa9, 0xda, 0x78, 0xe2, 0xdc, 0xc1, 0xf3, 0xdf, 0xb2, 0x3e, 0x40, 0x8c, 0xe9, 0xe1, 0xc4, 0xde, 0x81, - 0x2e, 0xb5, 0x8b, 0x27, 0xfc, 0xcb, 0xdf, 0x92, 0x43, 0xb0, 0x42, 0xb5, 0xf1, 0xfd, 0x00, 0xd6, 0xd7, 0x20, 0xe6, - 0x6d, 0x77, 0xe2, 0xe0, 0x8e, 0x5d, 0x59, 0x3e, 0xcd, 0xcb, 0x03, 0x88, 0x52, 0x36, 0x72, 0xb3, 0x61, 0xcd, 0xaf, - 0x66, 0x16, 0xbb, 0x36, 0x9e, 0x1f, 0xc8, 0xfa, 0xb0, 0xcd, 0x9f, 0x0b, 0x90, 0xa2, 0x28, 0xd0, 0x96, 0xbb, 0xda, - 0xa2, 0x8d, 0x80, 0x69, 0xd7, 0x3a, 0x6f, 0x6d, 0x21, 0xeb, 0xa4, 0x76, 0xca, 0xa0, 0x2b, 0x65, 0x8a, 0x9c, 0x9a, - 0x51, 0x23, 0x44, 0xc7, 0xf8, 0x41, 0x0e, 0xfd, 0x62, 0xf5, 0xdd, 0xf5, 0x3b, 0x5d, 0x80, 0xb8, 0xe2, 0x54, 0xe6, - 0x59, 0x49, 0xac, 0x0f, 0x37, 0x79, 0xcf, 0x1b, 0xf4, 0xbf, 0xd4, 0x95, 0xef, 0xcb, 0xda, 0x13, 0x24, 0x03, 0x41, - 0x3a, 0x0e, 0xfe, 0x18, 0xc0, 0xf0, 0xc7, 0x06, 0x46, 0x2f, 0x7a, 0x78, 0x1e, 0x54, 0xbf, 0x76, 0xc2, 0x77, 0x96, - 0x5f, 0xaa, 0xd0, 0xfb, 0x49, 0xf5, 0x0b, 0x58, 0x5f, 0x83, 0xa0, 0x8e, 0x44, 0xcd, 0xef, 0x69, 0x5b, 0xf7, 0x2b, - 0x8c, 0x78, 0x91, 0x0f, 0x15, 0xf9, 0xeb, 0xba, 0xfa, 0x3c, 0x87, 0x01, 0x39, 0xf6, 0x09, 0x06, 0x36, 0xfd, 0xb2, - 0x0f, 0x21, 0x78, 0x5f, 0x5f, 0xd5, 0x42, 0xe3, 0x97, 0x22, 0x4e, 0x50, 0xe1, 0x81, 0x2c, 0x74, 0x3c, 0xb5, 0x72, - 0x6b, 0x1d, 0x99, 0x68, 0x6c, 0x62, 0x14, 0x3a, 0x8b, 0x15, 0x6c, 0xcc, 0x27, 0xa3, 0xba, 0xf2, 0x86, 0x09, 0x86, - 0x5f, 0xad, 0x3f, 0x9d, 0xa5, 0x57, 0x5b, 0x85, 0xbd, 0xaa, 0xf0, 0x5f, 0x75, 0x13, 0xbe, 0xc9, 0x70, 0x58, 0x05, - 0x2f, 0x08, 0x15, 0xfc, 0x40, 0x27, 0x55, 0xa8, 0xa3, 0xd3, 0x10, 0xa1, 0x55, 0xb3, 0x82, 0x1c, 0x15, 0xda, 0xef, - 0xdb, 0xd4, 0xd6, 0x9b, 0xea, 0xec, 0xed, 0x58, 0xd5, 0x54, 0x98, 0x1f, 0x8f, 0x59, 0x4d, 0x33, 0x12, 0x95, 0x2c, - 0xbf, 0x83, 0xdd, 0x69, 0x0b, 0x6f, 0x9f, 0xc0, 0xfb, 0x9b, 0xfa, 0x31, 0xe3, 0xb3, 0x6c, 0xd2, 0x04, 0xba, 0x33, - 0xd7, 0x02, 0xb5, 0x4f, 0x4d, 0xdd, 0x91, 0xb9, 0x0e, 0xec, 0x5d, 0xcd, 0x97, 0xf8, 0x4c, 0x84, 0xbb, 0x5f, 0x93, - 0xa8, 0xcc, 0x69, 0x06, 0x6d, 0x2c, 0xa5, 0x89, 0xaa, 0xdb, 0x70, 0xca, 0xb0, 0xf7, 0x0c, 0xed, 0x02, 0x6a, 0xf4, - 0x44, 0x77, 0x62, 0x8c, 0x90, 0xc6, 0xfd, 0x22, 0xb4, 0x1f, 0xe9, 0x79, 0x2b, 0x90, 0x8e, 0xed, 0x18, 0xa6, 0x9b, - 0x06, 0xc8, 0x5a, 0xe8, 0xe3, 0x5f, 0x5f, 0xed, 0xc3, 0xd8, 0xe6, 0xfd, 0x06, 0x61, 0xa9, 0xde, 0x1e, 0x1d, 0x20, - 0xf9, 0x9e, 0x52, 0x58, 0x5c, 0xd1, 0x1a, 0xad, 0x86, 0x8d, 0x83, 0x5c, 0x61, 0x30, 0xca, 0x54, 0xe9, 0x3c, 0x62, - 0x38, 0x1a, 0xc2, 0x08, 0x85, 0x42, 0x5e, 0x7d, 0xc4, 0x9a, 0x79, 0xdc, 0x9e, 0x3d, 0x94, 0x56, 0x07, 0xbf, 0x7a, - 0xb2, 0x46, 0x7d, 0xe9, 0x5d, 0x6e, 0xc6, 0x52, 0x8b, 0x8f, 0x57, 0xbc, 0xd1, 0xeb, 0xcb, 0x84, 0x66, 0x6e, 0xd1, - 0xa0, 0x14, 0x1b, 0x12, 0xbb, 0x95, 0xdf, 0x13, 0xeb, 0xb1, 0x59, 0x21, 0x09, 0x99, 0x5f, 0x5e, 0x99, 0xca, 0x53, - 0x79, 0x7f, 0x65, 0x39, 0xc3, 0x51, 0x3c, 0x78, 0x07, 0x7e, 0xd1, 0xcb, 0x9f, 0xa4, 0xde, 0xaa, 0x6e, 0x4b, 0x1b, - 0x14, 0xb5, 0x73, 0xcb, 0x86, 0x73, 0xe1, 0x3a, 0x29, 0x54, 0xc1, 0x0d, 0x16, 0x49, 0x23, 0x6f, 0x1d, 0x2f, 0x3e, - 0xc5, 0x60, 0xca, 0xc2, 0x19, 0x94, 0xb5, 0xcc, 0x05, 0xd6, 0x68, 0x1f, 0x86, 0x67, 0x8b, 0xcc, 0x18, 0x33, 0x18, - 0xdb, 0x70, 0x6e, 0xf9, 0xac, 0xfb, 0xfa, 0x85, 0xe0, 0xfd, 0xc6, 0x48, 0x44, 0x2c, 0x1f, 0xa0, 0x0f, 0x06, 0xa4, - 0x7f, 0x59, 0x62, 0xe4, 0xc3, 0x73, 0x05, 0x7e, 0xd2, 0xb2, 0x70, 0x00, 0x36, 0x6b, 0xef, 0x30, 0x2e, 0x92, 0x79, - 0xab, 0xdb, 0x31, 0x3b, 0x04, 0x37, 0x6c, 0x8d, 0x22, 0x18, 0x15, 0xa3, 0x25, 0x18, 0xac, 0xa0, 0x21, 0xb8, 0x80, - 0xf3, 0x75, 0xc4, 0xaa, 0xc7, 0x29, 0x2e, 0x33, 0x75, 0x86, 0x7f, 0x76, 0x37, 0xcd, 0xb2, 0x1a, 0xc4, 0x07, 0xa1, - 0xc8, 0x16, 0xec, 0xc1, 0xc5, 0x63, 0xe1, 0xcf, 0x21, 0xdf, 0x45, 0x61, 0xe9, 0x1a, 0xff, 0xaf, 0x43, 0xaa, 0xf7, - 0x3d, 0xec, 0x9e, 0x60, 0x0f, 0x3a, 0xa9, 0x2d, 0x34, 0x7f, 0x85, 0x55, 0x15, 0x55, 0xf3, 0xcd, 0x08, 0x8f, 0x16, - 0x5c, 0xab, 0x23, 0xd0, 0x41, 0x20, 0xd4, 0x6a, 0x06, 0x03, 0xb4, 0xe3, 0x07, 0xf8, 0xd2, 0xf1, 0xf8, 0x25, 0x89, - 0x09, 0xcf, 0xef, 0x9b, 0x10, 0xc4, 0xe3, 0xe8, 0x71, 0xe7, 0xfa, 0x43, 0x95, 0x21, 0xb2, 0x48, 0xea, 0x7e, 0x84, - 0xb9, 0xfd, 0x34, 0x17, 0x2e, 0x16, 0x27, 0xe8, 0xb1, 0x5c, 0x71, 0xc7, 0x3d, 0xea, 0x6e, 0xda, 0x3d, 0x9f, 0xb2, - 0x27, 0x31, 0x96, 0x52, 0xc4, 0x1d, 0xad, 0xcd, 0xb8, 0x22, 0x45, 0xae, 0x36, 0x81, 0x5e, 0x8e, 0xf4, 0x1c, 0x8f, - 0x64, 0x29, 0x51, 0xc7, 0x12, 0x44, 0xad, 0xe2, 0x3b, 0x23, 0x05, 0xd5, 0x28, 0xef, 0x72, 0xf7, 0xad, 0xd3, 0xd4, - 0xdd, 0xcf, 0xee, 0xa7, 0xc1, 0xcb, 0x54, 0xe7, 0x8c, 0x77, 0x5e, 0xb4, 0x5a, 0xfb, 0x22, 0x46, 0xaf, 0x1f, 0x0b, - 0x32, 0x9c, 0xf6, 0x5d, 0x67, 0x01, 0x6a, 0x95, 0xe5, 0xbf, 0x41, 0x20, 0x53, 0x74, 0x97, 0x9e, 0x8e, 0x68, 0xae, - 0x74, 0xf9, 0x8e, 0x0e, 0x54, 0x26, 0x0a, 0x31, 0xd3, 0x68, 0xf6, 0x80, 0xce, 0x2d, 0xcf, 0x75, 0x19, 0xf5, 0x2e, - 0xa2, 0x0d, 0x0a, 0xb5, 0xcf, 0xd1, 0x5d, 0x2f, 0x3a, 0x87, 0xeb, 0x94, 0xdb, 0x47, 0xcb, 0x45, 0xe5, 0xb3, 0xf1, - 0x70, 0x61, 0x97, 0x48, 0x22, 0x1f, 0x78, 0x09, 0x31, 0x74, 0xdf, 0xce, 0x30, 0x83, 0xb3, 0xda, 0xbb, 0x5d, 0xaa, - 0x1b, 0x3e, 0x84, 0x1e, 0xc5, 0xc2, 0xb5, 0x59, 0xce, 0xff, 0x97, 0xde, 0x45, 0xf5, 0xb7, 0x3a, 0x25, 0xee, 0x17, - 0xfe, 0x5d, 0x24, 0x8a, 0x84, 0x1e, 0xd2, 0x90, 0xde, 0x9f, 0x95, 0x1d, 0x98, 0x0f, 0xed, 0xa1, 0x32, 0x35, 0x79, - 0x9e, 0x05, 0xa0, 0xf5, 0xaa, 0x50, 0x46, 0x0e, 0x46, 0x4f, 0xce, 0x3b, 0xa4, 0x10, 0x86, 0x90, 0xc3, 0x20, 0x11, - 0x73, 0x1d, 0x70, 0x73, 0xd5, 0xed, 0x2c, 0x45, 0x85, 0xee, 0x1a, 0x96, 0x12, 0xd0, 0x11, 0x1d, 0x92, 0xcc, 0x9c, - 0xd0, 0x10, 0x14, 0x28, 0xf2, 0x1e, 0x31, 0x18, 0x4d, 0xe0, 0x3f, 0x98, 0x7d, 0x14, 0xd2, 0x08, 0x08, 0xe3, 0x14, - 0xc5, 0x7b, 0x20, 0x0e, 0x94, 0xd6, 0x3d, 0x98, 0x56, 0xe1, 0xaa, 0x57, 0xda, 0xca, 0x18, 0xbe, 0xc6, 0xb9, 0x33, - 0xc8, 0x05, 0x9e, 0xea, 0x5e, 0xcc, 0x80, 0x28, 0x40, 0x29, 0x68, 0xc1, 0x49, 0x90, 0x7c, 0xa8, 0x15, 0x48, 0xc0, - 0x21, 0xae, 0x41, 0xa9, 0xb1, 0xe0, 0xd5, 0x78, 0xa3, 0x10, 0x96, 0x62, 0x24, 0x02, 0x21, 0xd9, 0x30, 0xac, 0x98, - 0x0a, 0xb4, 0xfb, 0xc5, 0xbe, 0xf7, 0xc2, 0xe3, 0x43, 0x7d, 0x23, 0xe6, 0x02, 0x09, 0xa3, 0xb3, 0x93, 0x7b, 0x81, - 0x24, 0x7f, 0xb5, 0xa7, 0x2b, 0xb3, 0xbc, 0xf0, 0x8d, 0x85, 0x73, 0xb5, 0x12, 0x10, 0xf6, 0x6f, 0x8c, 0x03, 0x01, - 0x30, 0x97, 0xce, 0x6a, 0x2d, 0x91, 0x95, 0x0b, 0x69, 0xd6, 0x63, 0x29, 0xd6, 0xdd, 0x3c, 0x54, 0x80, 0x29, 0xb5, - 0xb8, 0x20, 0x95, 0x15, 0xde, 0x68, 0x0e, 0xa6, 0xf0, 0xa6, 0x83, 0xae, 0xcd, 0x67, 0xff, 0x43, 0xee, 0x1e, 0x1e, - 0x87, 0x57, 0xaa, 0x5b, 0x82, 0x51, 0x67, 0x92, 0xc1, 0x89, 0x4c, 0xa5, 0x9e, 0x06, 0xb1, 0x93, 0xbe, 0x13, 0x20, - 0x90, 0xd0, 0x38, 0x25, 0x9d, 0x8d, 0x74, 0xe8, 0x03, 0xf7, 0x43, 0x59, 0x50, 0x7c, 0x1d, 0x75, 0x7c, 0x11, 0x45, - 0x58, 0x64, 0xa5, 0x67, 0x97, 0x57, 0x37, 0x8d, 0xce, 0xcc, 0x4b, 0xcb, 0x9c, 0xc6, 0x4f, 0x60, 0xc9, 0x0a, 0x51, - 0xf2, 0x92, 0xb4, 0xb0, 0x9c, 0xe0, 0x7a, 0xa0, 0xe9, 0xb0, 0x20, 0x73, 0xe3, 0xb8, 0xfe, 0x51, 0x31, 0x8e, 0xa9, - 0xc3, 0x9e, 0xd2, 0x9b, 0x0a, 0x3c, 0x75, 0x64, 0x15, 0x3a, 0x10, 0x9e, 0x61, 0xbc, 0xa6, 0x81, 0x37, 0xfb, 0xf5, - 0xfc, 0xdf, 0x01, 0x8d, 0xe3, 0xc3, 0x25, 0x6d, 0xb8, 0x0e, 0xab, 0x70, 0x21, 0x8e, 0xc9, 0x0f, 0x26, 0x93, 0xb8, - 0x26, 0x71, 0xe0, 0xf7, 0x61, 0x89, 0x54, 0x88, 0x0c, 0xea, 0x58, 0xb9, 0x1d, 0xfb, 0x0b, 0x40, 0x8f, 0x87, 0x4c, - 0xe7, 0x81, 0x2f, 0x58, 0xe0, 0x38, 0xa8, 0x66, 0x37, 0x87, 0x8a, 0x05, 0xc0, 0x85, 0x59, 0x29, 0x5c, 0x8c, 0xd6, - 0x28, 0xc5, 0xec, 0x19, 0x1f, 0xda, 0x55, 0x03, 0x96, 0x99, 0x77, 0x66, 0xe5, 0xdc, 0x5a, 0x48, 0xc1, 0xed, 0xfa, - 0x46, 0x4c, 0x70, 0xbb, 0x46, 0xb2, 0x2d, 0xea, 0xd7, 0xe0, 0x58, 0x5c, 0x5c, 0xd7, 0xf8, 0xac, 0xcc, 0xdc, 0x49, - 0xfb, 0xc4, 0x75, 0x94, 0x56, 0x20, 0x89, 0xe7, 0x79, 0x18, 0x89, 0x05, 0xd3, 0xe7, 0x84, 0xa8, 0xc4, 0xb0, 0xf4, - 0xb1, 0xec, 0x0c, 0x83, 0xc7, 0x1c, 0x1d, 0x79, 0x66, 0xe7, 0x1c, 0xfe, 0xc7, 0x05, 0x60, 0x59, 0x7c, 0x2a, 0xe3, - 0x5f, 0x1c, 0x8f, 0xb2, 0x27, 0xf2, 0xfe, 0x4a, 0xe2, 0x4e, 0xc5, 0x1c, 0x48, 0x23, 0x5b, 0xc6, 0xd2, 0x16, 0xc8, - 0x45, 0xc6, 0x33, 0xec, 0xfc, 0xd4, 0xfa, 0x98, 0xfd, 0xd8, 0xc7, 0xaa, 0xe1, 0xd7, 0x81, 0x6e, 0x93, 0x12, 0xf4, - 0xad, 0x94, 0xe9, 0xec, 0xbd, 0x99, 0xd2, 0xdc, 0x89, 0xab, 0x7a, 0x65, 0x6b, 0x1b, 0x6a, 0x9b, 0xc4, 0xf5, 0x5b, - 0xf3, 0x18, 0x98, 0xb6, 0x4e, 0x5c, 0x19, 0x0a, 0x6d, 0xb2, 0x3c, 0xd3, 0x20, 0x55, 0x31, 0x74, 0xf7, 0x8a, 0x0f, - 0x9d, 0xee, 0x70, 0x36, 0x5f, 0x9a, 0xf4, 0x30, 0x9e, 0xc5, 0xb5, 0x5c, 0x92, 0xc1, 0x07, 0x85, 0xc3, 0x21, 0x49, - 0xd1, 0x22, 0x97, 0x21, 0x80, 0xdc, 0xed, 0xe0, 0x6e, 0xb2, 0xdd, 0x94, 0x77, 0xcc, 0x5e, 0x9a, 0xa3, 0xcf, 0xdb, - 0x72, 0x31, 0xa1, 0x46, 0x4c, 0xd5, 0x79, 0x6b, 0xbb, 0x6e, 0x0a, 0x4a, 0x39, 0x0a, 0xa4, 0x53, 0x16, 0xa2, 0x82, - 0x9f, 0x98, 0xef, 0xff, 0xa0, 0x28, 0x37, 0x04, 0xdc, 0xf2, 0x3a, 0x7e, 0xdc, 0x69, 0x2d, 0x63, 0x58, 0x8e, 0x8c, - 0x0b, 0xd3, 0xbf, 0xa4, 0x59, 0xcd, 0x96, 0x65, 0xe2, 0x75, 0x9d, 0x3d, 0x28, 0x2e, 0xe1, 0x5c, 0xad, 0x65, 0xe1, - 0x3a, 0xd2, 0xd0, 0x84, 0xfe, 0x10, 0x0a, 0xdb, 0xa6, 0x32, 0x70, 0xa2, 0x94, 0x21, 0x3f, 0x97, 0x86, 0x29, 0x18, - 0x7e, 0x13, 0x60, 0x9d, 0x66, 0x18, 0x85, 0xb4, 0x00, 0xaa, 0x0f, 0x47, 0x93, 0x6e, 0x08, 0x3b, 0x07, 0x1d, 0x47, - 0xe9, 0xec, 0xc0, 0x7a, 0x40, 0xce, 0xf3, 0xd9, 0x5e, 0xef, 0xcd, 0xfa, 0xda, 0xf8, 0x07, 0x04, 0x3e, 0xf3, 0x2d, - 0xfa, 0xda, 0x06, 0xe2, 0x7c, 0x39, 0x23, 0xc6, 0xb6, 0x0c, 0xd8, 0x52, 0xe5, 0x10, 0xb6, 0x54, 0x0c, 0x13, 0x33, - 0x75, 0x62, 0x8a, 0x17, 0x65, 0xdd, 0x79, 0x3e, 0x04, 0x2c, 0x50, 0x7e, 0xc0, 0x91, 0x25, 0xc7, 0x74, 0x14, 0x29, - 0x3a, 0x0d, 0x14, 0x2c, 0x50, 0x7e, 0x7d, 0x5b, 0xfe, 0x61, 0x08, 0xb0, 0x1c, 0x69, 0x95, 0x81, 0x64, 0x6a, 0x63, - 0x39, 0xa9, 0xc5, 0xa9, 0x38, 0x8b, 0xca, 0x30, 0xfa, 0xdd, 0xb8, 0x78, 0xe9, 0xbd, 0x56, 0x6f, 0xe1, 0x29, 0x57, - 0xb0, 0x46, 0x13, 0xd3, 0x13, 0xb1, 0xbf, 0xe0, 0x7c, 0x30, 0xc8, 0x6f, 0x78, 0x77, 0x08, 0xa9, 0x8d, 0x62, 0x8f, - 0xda, 0x0f, 0x4c, 0x46, 0xa5, 0xd5, 0x25, 0x2f, 0xea, 0x45, 0xb6, 0x65, 0x17, 0xbb, 0x72, 0x8f, 0x81, 0xcb, 0x8b, - 0x11, 0xe8, 0xf1, 0xf6, 0x1a, 0x1c, 0x00, 0x1f, 0x2d, 0x8a, 0xab, 0x61, 0x5b, 0xa4, 0x40, 0xd8, 0xd6, 0x7b, 0xde, - 0xea, 0x53, 0x2b, 0xc8, 0x63, 0x10, 0x5a, 0x97, 0x13, 0xde, 0xb9, 0x75, 0xca, 0x90, 0x16, 0x31, 0xce, 0xb3, 0xa8, - 0xd0, 0x87, 0x49, 0x55, 0xc9, 0x86, 0x7f, 0xc0, 0x60, 0xe4, 0x16, 0x53, 0xe1, 0xdf, 0xe2, 0xaf, 0xcc, 0x0d, 0xf7, - 0x6a, 0x98, 0xce, 0xa9, 0x36, 0xef, 0xba, 0xed, 0xf0, 0xc3, 0xf0, 0xdd, 0x12, 0x7a, 0x54, 0x60, 0x9c, 0xe6, 0x89, - 0xd9, 0x1a, 0x7e, 0xa5, 0x80, 0x6f, 0x1f, 0xca, 0xb4, 0x0d, 0x37, 0xd3, 0xaa, 0xbd, 0xe9, 0xb6, 0x1b, 0x40, 0xe6, - 0xac, 0x66, 0xf9, 0xe6, 0x83, 0x3b, 0x09, 0x69, 0x11, 0xfe, 0x58, 0x26, 0xea, 0x11, 0xb6, 0x74, 0xe8, 0x04, 0x3c, - 0xd3, 0xd3, 0xaa, 0xc6, 0xf3, 0x75, 0x56, 0x22, 0x7f, 0xb4, 0x37, 0xfe, 0xe4, 0x83, 0xb7, 0xbe, 0x83, 0x1a, 0x79, - 0xa2, 0x47, 0x84, 0x0b, 0xd5, 0x25, 0xb4, 0xad, 0x1a, 0xb2, 0x28, 0x96, 0xdc, 0x06, 0xde, 0x13, 0x53, 0x84, 0xc3, - 0x4f, 0xed, 0xe9, 0x52, 0xd4, 0xfe, 0x98, 0x19, 0xfc, 0x07, 0x80, 0x44, 0xe5, 0xf2, 0xbf, 0xc3, 0xe3, 0x1d, 0x85, - 0x88, 0x78, 0x0b, 0xc9, 0x82, 0x05, 0x18, 0x79, 0xa8, 0xcc, 0x48, 0x4a, 0xca, 0xb5, 0x12, 0x80, 0xef, 0xc3, 0xd0, - 0x56, 0x5d, 0x83, 0x1c, 0x6c, 0xf0, 0xb7, 0x0c, 0xe2, 0x61, 0xd7, 0x23, 0xad, 0xf1, 0xf2, 0xf8, 0xd2, 0xa7, 0x9a, - 0xd0, 0xe2, 0xdb, 0x48, 0x59, 0xbc, 0x5c, 0x3d, 0x10, 0x1d, 0x49, 0x0c, 0x71, 0x23, 0x27, 0xc9, 0x9b, 0xc4, 0xfb, - 0x69, 0x63, 0x44, 0x72, 0x62, 0x9d, 0xbd, 0x20, 0xe5, 0x17, 0x62, 0xf3, 0xdd, 0xb8, 0x73, 0xb8, 0x73, 0xbd, 0xaf, - 0x94, 0x45, 0x5d, 0x8b, 0x7a, 0x68, 0x76, 0x1d, 0xfd, 0xd9, 0x94, 0x30, 0xa4, 0x43, 0xa2, 0x41, 0x21, 0x2d, 0x2a, - 0x0b, 0xa4, 0x81, 0x9e, 0x44, 0xf6, 0x71, 0x58, 0xcc, 0xde, 0xbd, 0x4a, 0x7d, 0x92, 0x48, 0x49, 0x6c, 0x0f, 0x58, - 0x9a, 0x4c, 0xbc, 0xb9, 0x30, 0xfb, 0xbf, 0xb2, 0xf3, 0xf2, 0x21, 0xd2, 0x98, 0xaa, 0x63, 0x64, 0xa1, 0x06, 0x4a, - 0x59, 0x0b, 0xa7, 0x2d, 0xbe, 0x14, 0x45, 0x5b, 0x85, 0x9e, 0x6a, 0x1e, 0x78, 0x5a, 0x58, 0x13, 0xc5, 0x16, 0xf4, - 0x74, 0x98, 0x96, 0x25, 0xb5, 0x09, 0x4f, 0x5f, 0x7a, 0x9e, 0xe5, 0x39, 0xdb, 0x5d, 0x9a, 0x7d, 0xeb, 0xa0, 0x5e, - 0x53, 0xcb, 0xf6, 0x53, 0x95, 0x69, 0xd0, 0x12, 0x04, 0xf5, 0x10, 0xe4, 0x56, 0x61, 0xe2, 0xc6, 0x38, 0x4f, 0x77, - 0xed, 0xd6, 0x9d, 0xf9, 0xa7, 0x5d, 0x10, 0x17, 0x12, 0x18, 0x34, 0x12, 0xad, 0x26, 0xf4, 0x63, 0xc3, 0x52, 0x18, - 0x72, 0xb6, 0x64, 0x96, 0xf3, 0x6a, 0x20, 0x3f, 0xd3, 0x56, 0x70, 0x40, 0xc2, 0xe8, 0x1c, 0x63, 0x66, 0xf0, 0x39, - 0x12, 0xc3, 0x57, 0x6d, 0xd2, 0x73, 0x24, 0xf7, 0x34, 0xc1, 0x54, 0x00, 0xf3, 0x4a, 0xc1, 0x74, 0xd6, 0x37, 0x8b, - 0x0a, 0x56, 0xfc, 0xf0, 0xe3, 0x2f, 0xa8, 0xde, 0x07, 0x05, 0x9d, 0x04, 0x57, 0xea, 0xb6, 0x9d, 0xf1, 0x6d, 0xf7, - 0x41, 0x01, 0x5e, 0xa8, 0x21, 0xf3, 0x12, 0xff, 0x57, 0x2f, 0xd6, 0xd4, 0x2f, 0xf2, 0xd9, 0x61, 0xa2, 0xef, 0x32, - 0x69, 0xe6, 0xf7, 0xa5, 0x01, 0x65, 0x7e, 0xc9, 0xe3, 0x8a, 0x69, 0xde, 0x23, 0xfe, 0xd3, 0x98, 0xdb, 0xc2, 0x84, - 0x76, 0x98, 0x3e, 0x4a, 0xd4, 0xdc, 0x3e, 0x13, 0x54, 0xfb, 0x86, 0x97, 0xea, 0x31, 0x17, 0xac, 0x63, 0x72, 0x4b, - 0x89, 0xf5, 0x95, 0xc0, 0x83, 0x2c, 0x92, 0x89, 0x7b, 0xa9, 0xf6, 0x8e, 0xf2, 0x7c, 0xa7, 0xf6, 0x34, 0x39, 0x61, - 0x5d, 0x5c, 0x5d, 0xc9, 0xd7, 0x31, 0xc2, 0x6e, 0xbd, 0x59, 0x5e, 0xab, 0x62, 0xcc, 0x28, 0xd9, 0xd4, 0x6e, 0xef, - 0x62, 0x31, 0xe3, 0x26, 0x0c, 0x45, 0xb6, 0x28, 0x97, 0x8f, 0x5c, 0x3c, 0xe4, 0xfb, 0x94, 0x5f, 0xfd, 0x67, 0x0b, - 0x71, 0xf3, 0xf9, 0xf9, 0x1b, 0x23, 0x2c, 0x08, 0x03, 0xdb, 0xad, 0x22, 0x3e, 0x9d, 0x09, 0x14, 0xc6, 0xc6, 0x04, - 0x9b, 0xd7, 0xba, 0x09, 0xbc, 0x48, 0x94, 0x91, 0x34, 0xcc, 0xcf, 0xf2, 0x10, 0xa8, 0x62, 0xe8, 0x49, 0x6b, 0x25, - 0x8a, 0xd6, 0xf7, 0x63, 0x9f, 0x01, 0x21, 0x55, 0xb2, 0xac, 0x88, 0x2b, 0x57, 0x28, 0x04, 0x22, 0x09, 0x07, 0x47, - 0x60, 0x9b, 0x26, 0x84, 0x4f, 0x0f, 0xe9, 0xa5, 0x2e, 0x73, 0xc9, 0xc5, 0x35, 0x38, 0x0a, 0x60, 0x69, 0x32, 0xe2, - 0xd7, 0xbb, 0x55, 0x5e, 0xfa, 0xa5, 0x9d, 0x6e, 0xfe, 0x9e, 0x03, 0x8e, 0x0b, 0xdd, 0x17, 0x05, 0x68, 0x0d, 0x58, - 0x56, 0x28, 0x6f, 0x1f, 0x83, 0x8b, 0xd2, 0x61, 0xf4, 0x72, 0x5c, 0x2d, 0xa2, 0xba, 0x42, 0x59, 0xbb, 0x5d, 0x11, - 0x95, 0xb7, 0xf3, 0xd7, 0x34, 0xa9, 0x45, 0x04, 0x71, 0xde, 0x47, 0x34, 0xcb, 0x44, 0x98, 0x5d, 0xdc, 0x75, 0xa8, - 0xc7, 0x90, 0xf4, 0xa1, 0x15, 0x17, 0x11, 0xf8, 0xb4, 0x02, 0x69, 0x63, 0x6e, 0x0f, 0xe9, 0xb7, 0xb6, 0xa3, 0x00, - 0xe8, 0x85, 0xb0, 0x90, 0xb9, 0x91, 0x14, 0x3c, 0x7b, 0x0f, 0x54, 0x92, 0xf4, 0xb9, 0x1a, 0xb3, 0xae, 0xc7, 0x17, - 0xaf, 0x95, 0xbe, 0x05, 0x79, 0x6f, 0x8a, 0xe0, 0xe9, 0xaa, 0xbd, 0x74, 0x21, 0xa0, 0xbd, 0xce, 0x74, 0xb6, 0x34, - 0x7b, 0x1f, 0xbc, 0x17, 0x1d, 0x78, 0x0d, 0xf5, 0x66, 0x89, 0x3c, 0x66, 0xf0, 0x65, 0x93, 0x90, 0xe4, 0xb5, 0x91, - 0x0a, 0xa2, 0xa0, 0x07, 0xae, 0x51, 0x91, 0x8c, 0x92, 0x8b, 0x6e, 0xfb, 0xb3, 0x19, 0xa4, 0x5c, 0x5e, 0x7d, 0xcd, - 0xdb, 0x9d, 0x83, 0x28, 0xa5, 0xf9, 0xeb, 0x85, 0x4f, 0xbb, 0x67, 0x74, 0xe5, 0x35, 0x81, 0x56, 0x33, 0x7a, 0x4b, - 0x8d, 0x6a, 0xa4, 0xa9, 0x48, 0x05, 0xb1, 0x77, 0x59, 0x83, 0xb5, 0xf1, 0x78, 0x30, 0x95, 0x1a, 0xbc, 0xcf, 0xf4, - 0xa4, 0x75, 0xfa, 0xf6, 0x69, 0x39, 0x84, 0x57, 0xdf, 0x6d, 0x37, 0x2a, 0xf5, 0x5c, 0x7c, 0x2f, 0xdb, 0x45, 0xa6, - 0x75, 0x9c, 0xe8, 0x64, 0xd2, 0x57, 0x69, 0xb7, 0x27, 0x55, 0x3d, 0x73, 0xe1, 0x00, 0xfd, 0xbb, 0x51, 0xa6, 0x94, - 0xa5, 0xf1, 0xda, 0x25, 0xac, 0xa7, 0xde, 0x08, 0xbb, 0x2e, 0x0a, 0xc0, 0x3f, 0x67, 0x1c, 0x68, 0xa2, 0x63, 0xc5, - 0x3a, 0xbe, 0x2e, 0x75, 0x3c, 0x94, 0xfc, 0x80, 0x6d, 0x66, 0x92, 0x77, 0x28, 0x6e, 0xde, 0x10, 0xd8, 0x9f, 0x29, - 0xd6, 0x76, 0xab, 0xc4, 0x19, 0xab, 0xd8, 0x2b, 0xb1, 0xd7, 0x1e, 0x6f, 0x98, 0x40, 0x99, 0xad, 0x2b, 0x4c, 0x99, - 0x33, 0xbf, 0xc9, 0x67, 0x2f, 0xaf, 0x6f, 0x9e, 0xfd, 0x65, 0xe7, 0x35, 0xc3, 0xb0, 0x92, 0x3d, 0x27, 0xcb, 0x21, - 0xac, 0xca, 0xf8, 0x99, 0x9e, 0x6a, 0x1e, 0xdf, 0x51, 0x6f, 0xdc, 0x9b, 0xa4, 0x07, 0xe3, 0xdb, 0x13, 0x92, 0x87, - 0x82, 0x09, 0x18, 0xe9, 0xcf, 0x47, 0xa3, 0x00, 0x9d, 0x5c, 0x76, 0x0d, 0x2e, 0x12, 0x93, 0x3f, 0xa6, 0x5e, 0xc6, - 0x22, 0x05, 0xa9, 0x3a, 0xc2, 0x5d, 0x83, 0x48, 0x8a, 0x2a, 0xe8, 0xc8, 0x8b, 0x02, 0x4c, 0x5a, 0x70, 0x60, 0x01, - 0x0a, 0x3a, 0x5f, 0x79, 0x89, 0x25, 0x7e, 0x88, 0xfe, 0xf1, 0x1f, 0x56, 0x27, 0xbd, 0x68, 0xfe, 0x31, 0xbd, 0xf0, - 0xff, 0x4f, 0xe7, 0xb3, 0x75, 0x8e, 0x0d, 0x08, 0xd6, 0xff, 0x05, 0x62, 0x17, 0x9a, 0xa0, 0x04, 0xe9, 0x01, 0x84, - 0x43, 0xfe, 0xf4, 0x4e, 0x33, 0x9c, 0xb0, 0x7c, 0xaa, 0x26, 0xc3, 0x78, 0x2a, 0xce, 0xc9, 0x84, 0xc6, 0x71, 0x1a, - 0x4d, 0x29, 0xc0, 0x33, 0x6e, 0xcc, 0x5e, 0xc3, 0xd0, 0x7b, 0xdd, 0xa3, 0x40, 0xe8, 0x24, 0xdd, 0xcc, 0x58, 0x8a, - 0x49, 0xb4, 0xac, 0x57, 0x6d, 0x9c, 0x1c, 0xf6, 0xe0, 0x0c, 0xb4, 0xcc, 0x32, 0x27, 0x5a, 0x0f, 0x16, 0x42, 0x73, - 0x6e, 0x6c, 0x30, 0xdd, 0x5b, 0x28, 0xdd, 0x11, 0x41, 0x63, 0xf7, 0x98, 0xca, 0x56, 0x44, 0x25, 0xa5, 0x88, 0x78, - 0x63, 0x20, 0x4c, 0x2f, 0x7e, 0x9d, 0x9a, 0x53, 0xdf, 0xf6, 0x51, 0xbe, 0x32, 0xa9, 0x94, 0xb5, 0x5c, 0x48, 0x83, - 0xf1, 0xea, 0xcb, 0x48, 0x08, 0xcd, 0x44, 0x48, 0x91, 0x17, 0xb2, 0x27, 0x60, 0x13, 0x23, 0xbe, 0xc7, 0xa5, 0x84, - 0x32, 0x39, 0x28, 0x55, 0x50, 0x3c, 0x3e, 0xd4, 0x8f, 0x18, 0x10, 0xba, 0x1a, 0x23, 0x89, 0xe5, 0x58, 0xe8, 0x27, - 0xd2, 0x17, 0x34, 0x71, 0x08, 0x5c, 0xe3, 0x07, 0xd1, 0x67, 0x4f, 0xc6, 0xcb, 0x5e, 0x81, 0xcd, 0x39, 0xb0, 0x89, - 0xdc, 0x1c, 0xb4, 0xee, 0x3f, 0x65, 0x02, 0x4d, 0x14, 0x59, 0xeb, 0x39, 0x4b, 0xf6, 0x2c, 0xc5, 0x3c, 0x54, 0x4b, - 0x96, 0x40, 0xa3, 0xa8, 0x88, 0xd0, 0x5f, 0x0e, 0xf6, 0x50, 0xcf, 0x2a, 0x23, 0xb6, 0x30, 0xaf, 0x4e, 0xa8, 0xb2, - 0xd5, 0x51, 0xd2, 0x2b, 0x0b, 0xf5, 0x70, 0x37, 0x80, 0xf1, 0xb5, 0x9f, 0x7b, 0xe3, 0xce, 0xfa, 0x84, 0x6d, 0xcf, - 0x37, 0xb9, 0x38, 0xfe, 0x7a, 0xe6, 0xe5, 0xe1, 0xe0, 0xb9, 0x67, 0xb1, 0xd9, 0x50, 0x09, 0x81, 0x48, 0x26, 0x82, - 0x4a, 0xf5, 0xdb, 0x6c, 0x38, 0x20, 0xb0, 0x83, 0x62, 0x2b, 0xe1, 0xa5, 0x52, 0x51, 0xee, 0xad, 0xd5, 0x58, 0x0e, - 0x1c, 0x8e, 0x21, 0x8d, 0x63, 0x97, 0x98, 0x82, 0x38, 0xd6, 0x67, 0xe9, 0x21, 0xf0, 0x6b, 0x6b, 0x34, 0x4f, 0xec, - 0x90, 0x21, 0xd3, 0xa4, 0x4a, 0x45, 0xfa, 0x39, 0x00, 0x6e, 0x23, 0xa7, 0x17, 0xe9, 0x1f, 0x50, 0x02, 0xfc, 0x2a, - 0x16, 0x7b, 0xa3, 0x32, 0x16, 0xc9, 0xaa, 0xac, 0x56, 0x0a, 0xa7, 0xe3, 0x03, 0xf0, 0xd2, 0xbf, 0x2c, 0x58, 0xa0, - 0x6a, 0xa4, 0x67, 0x0b, 0x77, 0x08, 0xd4, 0x4a, 0xdf, 0x8d, 0x10, 0xb5, 0xee, 0xd7, 0xf5, 0xa8, 0xba, 0xb9, 0x58, - 0x8c, 0x31, 0xb6, 0xdc, 0x9b, 0x4f, 0x2a, 0xb7, 0x6d, 0x31, 0xfa, 0x36, 0x77, 0x60, 0x6b, 0xd2, 0x3d, 0xfd, 0xb0, - 0x3d, 0xe5, 0xe1, 0x25, 0x7e, 0xf3, 0x6a, 0x22, 0x33, 0x79, 0x7c, 0x26, 0x74, 0xef, 0xa9, 0x42, 0xcd, 0x8d, 0x85, - 0x3c, 0xf5, 0xbb, 0xf7, 0x34, 0xe1, 0x87, 0xfa, 0xaf, 0xd4, 0x78, 0x52, 0x93, 0x93, 0xbf, 0x99, 0xc5, 0x64, 0x13, - 0x86, 0xfd, 0xb0, 0x81, 0x20, 0x10, 0x50, 0x25, 0x80, 0xed, 0x51, 0x94, 0xeb, 0x6f, 0x4a, 0xaa, 0x5b, 0xc0, 0x85, - 0xa2, 0x53, 0x51, 0xf7, 0x29, 0xe1, 0x59, 0xcf, 0xb6, 0x5b, 0x28, 0x22, 0x2e, 0xaa, 0x33, 0x71, 0xa2, 0xcd, 0xcf, - 0xd9, 0xfe, 0x51, 0x75, 0x40, 0x87, 0x2c, 0x3e, 0xea, 0x9a, 0x50, 0x10, 0x37, 0xbc, 0x50, 0x86, 0x09, 0x92, 0x08, - 0x65, 0xd3, 0x6c, 0xf7, 0x65, 0xba, 0xc6, 0xad, 0xfd, 0xb9, 0xd0, 0xf7, 0x76, 0x20, 0x12, 0x17, 0x33, 0xd1, 0xac, - 0x08, 0x57, 0xf2, 0xe0, 0x54, 0xd1, 0xe6, 0x2c, 0xc8, 0xd6, 0xec, 0xad, 0x9e, 0x93, 0x39, 0xe0, 0x28, 0xd2, 0x72, - 0xcc, 0x8f, 0x3c, 0x17, 0xcf, 0xd5, 0x67, 0x8b, 0xe4, 0x7e, 0xc9, 0xc6, 0xb6, 0xdd, 0x4b, 0xd2, 0x93, 0x1c, 0x60, - 0x22, 0x04, 0xdf, 0x94, 0x90, 0x0e, 0xc0, 0xfa, 0xda, 0xb2, 0x04, 0x99, 0xa9, 0x02, 0x22, 0x93, 0xe9, 0xfc, 0xda, - 0x93, 0x83, 0xa0, 0x33, 0x15, 0x70, 0xc0, 0x10, 0x7e, 0x06, 0x06, 0x1a, 0xdf, 0x73, 0x90, 0xa4, 0x44, 0x43, 0x32, - 0xc5, 0x3d, 0x20, 0x52, 0xc0, 0xcd, 0x37, 0x4f, 0xb6, 0x68, 0x5d, 0x19, 0xa7, 0x90, 0x5e, 0xd2, 0x7a, 0x30, 0x25, - 0x31, 0x9e, 0x49, 0xb8, 0xc5, 0xf1, 0xcf, 0x96, 0xc0, 0xeb, 0x44, 0x5e, 0xa5, 0x64, 0x4b, 0xce, 0x09, 0x0c, 0x2e, - 0x47, 0x2b, 0x36, 0xb0, 0xb8, 0x7e, 0x0a, 0x6b, 0x8c, 0x27, 0x59, 0x1e, 0x8b, 0x9b, 0xbf, 0xef, 0xbf, 0xf5, 0xc8, - 0xee, 0xb3, 0xdd, 0x9d, 0x4f, 0x4d, 0x6b, 0x05, 0x83, 0x18, 0x53, 0xb2, 0x01, 0x6a, 0x05, 0xcf, 0x6c, 0xc8, 0xd8, - 0x95, 0xe6, 0x59, 0xe0, 0xa8, 0x67, 0xbb, 0x6f, 0xcd, 0xee, 0x58, 0xbe, 0x41, 0xfc, 0x44, 0x43, 0xd8, 0x78, 0xa7, - 0x02, 0x9b, 0xa8, 0xe5, 0xf7, 0xe8, 0xb7, 0xbb, 0xb3, 0x1d, 0xda, 0xab, 0x78, 0x37, 0x8d, 0x67, 0x19, 0x53, 0x59, - 0x96, 0xbf, 0x8f, 0x9e, 0xf9, 0xd1, 0xd1, 0x12, 0x86, 0xfa, 0xc8, 0x39, 0x4e, 0xd3, 0x38, 0xe8, 0x08, 0xf5, 0x84, - 0x1d, 0x7f, 0x87, 0x88, 0xff, 0xef, 0x0c, 0xff, 0x77, 0xf0, 0xbf, 0xe7, 0xf8, 0x17, 0xa1, 0xae, 0xf3, 0xd5, 0xe6, - 0xaf, 0x28, 0xff, 0x8d, 0x8f, 0x64, 0x9d, 0xbf, 0xda, 0xdb, 0x7d, 0x61, 0x5a, 0x6f, 0x92, 0x47, 0x6b, 0x13, 0x05, - 0xf4, 0xd5, 0xe3, 0xce, 0xe9, 0x04, 0x51, 0xe6, 0xd4, 0xf9, 0x88, 0xf2, 0xfb, 0x6e, 0x70, 0xe1, 0x17, 0xe3, 0x06, - 0x96, 0xad, 0x2f, 0xff, 0xf4, 0xaf, 0x90, 0xcb, 0x7d, 0x7e, 0xce, 0xb5, 0xad, 0x68, 0x94, 0xfd, 0x7d, 0x68, 0xdc, - 0x11, 0x41, 0x0d, 0xe7, 0xfe, 0xf3, 0x44, 0xf3, 0x4f, 0x06, 0x97, 0x22, 0x47, 0x6b, 0x85, 0xc5, 0x67, 0xfe, 0x4c, - 0x2b, 0x7b, 0xdf, 0xb8, 0x75, 0x65, 0x1b, 0x5a, 0x8c, 0x36, 0xdd, 0x00, 0x83, 0x49, 0x05, 0xad, 0x95, 0xb5, 0xfb, - 0xfc, 0x97, 0x89, 0x31, 0x24, 0x3c, 0xfa, 0xd9, 0xaf, 0x81, 0xfa, 0x8d, 0x6b, 0xb3, 0x06, 0x12, 0x5b, 0xa6, 0xe7, - 0x2f, 0x05, 0xb3, 0x09, 0x0d, 0x0f, 0xf5, 0x74, 0xa9, 0xf7, 0xea, 0xb3, 0x48, 0x34, 0xc6, 0xed, 0x50, 0x5c, 0x5f, - 0x01, 0x0a, 0xa4, 0x38, 0x4f, 0x97, 0xff, 0xc7, 0x5f, 0x88, 0xa2, 0xeb, 0x12, 0x3a, 0x2a, 0xe7, 0xa4, 0xe6, 0xa7, - 0xd0, 0x4b, 0x54, 0x7a, 0xa9, 0x12, 0x75, 0x54, 0x4c, 0x3c, 0x0b, 0x82, 0xeb, 0x8a, 0x9c, 0x9b, 0x5c, 0x0d, 0xe7, - 0x64, 0xdc, 0xe7, 0xff, 0x38, 0xcb, 0x49, 0x82, 0x87, 0xc0, 0x8c, 0xef, 0xec, 0x0a, 0x61, 0x49, 0x5e, 0xc3, 0xc1, - 0xec, 0xc1, 0xb0, 0x7c, 0x52, 0x03, 0x0d, 0x86, 0xf9, 0xf3, 0x05, 0x24, 0xe0, 0x1b, 0xdf, 0x0c, 0x52, 0xa3, 0xe2, - 0xa9, 0x3d, 0xad, 0x43, 0x95, 0x36, 0x06, 0x86, 0x21, 0x11, 0xc1, 0x21, 0xe7, 0x00, 0xf1, 0x41, 0xa7, 0xb3, 0xa3, - 0x8d, 0x63, 0x26, 0xa7, 0xe4, 0xa0, 0x1f, 0xe7, 0x52, 0x15, 0xca, 0xa1, 0x96, 0xd4, 0x51, 0xd1, 0x90, 0xe3, 0x32, - 0x3a, 0xd0, 0xa2, 0xdb, 0x2b, 0xc8, 0xfa, 0x32, 0x17, 0xef, 0xb2, 0xd9, 0xea, 0x91, 0x08, 0x33, 0x89, 0x18, 0xa9, - 0xe0, 0xb4, 0x64, 0x55, 0x0c, 0xfd, 0xa2, 0x25, 0xa6, 0x36, 0xe2, 0xe9, 0xde, 0x5a, 0x3a, 0x75, 0x1e, 0x79, 0x70, - 0x65, 0x90, 0xbd, 0xe2, 0x35, 0xe0, 0xde, 0x24, 0x1b, 0x66, 0x19, 0x2b, 0xb8, 0xb0, 0xbe, 0xf6, 0x69, 0xf5, 0xc1, - 0xbe, 0x43, 0xc1, 0x79, 0xf6, 0x5e, 0x0c, 0xba, 0x19, 0xec, 0x82, 0xea, 0x8d, 0xfd, 0x3c, 0x0d, 0xf8, 0xdd, 0x03, - 0xa5, 0xa0, 0xc0, 0x31, 0xa8, 0xa7, 0x3b, 0x7f, 0x43, 0x04, 0x7e, 0x60, 0x37, 0x60, 0x9d, 0xb3, 0x6d, 0xc1, 0x95, - 0x84, 0x6a, 0x20, 0xd5, 0x65, 0x2c, 0x0b, 0x6c, 0xa7, 0x87, 0x2d, 0xae, 0x7b, 0x8e, 0x06, 0xa5, 0xba, 0x37, 0x13, - 0x94, 0x89, 0xa3, 0x65, 0xae, 0x03, 0x5b, 0x04, 0x30, 0xb2, 0x15, 0xe1, 0x36, 0x20, 0xaf, 0x2e, 0x66, 0x34, 0xf4, - 0x87, 0xb8, 0x9a, 0x04, 0x34, 0xa0, 0x91, 0x8b, 0x8e, 0x17, 0x5d, 0x2f, 0x9d, 0xd2, 0xdb, 0xe3, 0x09, 0x72, 0xb0, - 0x73, 0x16, 0x86, 0xf2, 0x7a, 0x92, 0xf3, 0xa5, 0xe7, 0xbf, 0x9e, 0x10, 0xed, 0xf5, 0x31, 0x86, 0xb3, 0x85, 0x94, - 0xc4, 0x06, 0x32, 0xb4, 0x3a, 0x8e, 0xe8, 0xe0, 0x21, 0x0f, 0xe8, 0x49, 0x69, 0x96, 0xd7, 0x4d, 0x88, 0x8a, 0xb9, - 0x08, 0x95, 0xc8, 0xcb, 0xe2, 0x9a, 0xad, 0x2b, 0x10, 0x98, 0xd9, 0x2e, 0xf8, 0x82, 0xa3, 0xd1, 0xca, 0xf0, 0x82, - 0x30, 0x27, 0x8c, 0x90, 0xc5, 0xfa, 0x27, 0xc0, 0x1b, 0xbe, 0x06, 0x45, 0xfe, 0x64, 0x5c, 0x12, 0x9e, 0x59, 0xe6, - 0x0e, 0xe1, 0x84, 0x9f, 0x2a, 0x61, 0x75, 0x17, 0x15, 0xfa, 0xc7, 0xf3, 0x52, 0x50, 0x1c, 0x35, 0x06, 0x0c, 0xfb, - 0x22, 0x04, 0x91, 0x8a, 0xd2, 0xec, 0x3b, 0x88, 0x54, 0xf7, 0x03, 0xb1, 0x1e, 0x8e, 0x68, 0xc3, 0x23, 0x11, 0xc8, - 0x1f, 0x49, 0xb5, 0xd0, 0xc6, 0x47, 0x22, 0x24, 0xbd, 0x7d, 0xf3, 0xc1, 0x79, 0xb9, 0x29, 0xaa, 0x2c, 0xa3, 0xd0, - 0x27, 0x3a, 0xa8, 0x9c, 0xea, 0xf1, 0x7a, 0x96, 0xda, 0x0a, 0xae, 0x5b, 0x2b, 0x81, 0x8a, 0x8d, 0xe9, 0x32, 0xf7, - 0x91, 0xed, 0xb5, 0x4b, 0x29, 0xf1, 0xe7, 0x79, 0xc6, 0x1a, 0x8e, 0x04, 0xec, 0xa6, 0x17, 0x86, 0xb1, 0x93, 0x1d, - 0x43, 0x47, 0x92, 0xf4, 0x82, 0xd2, 0x74, 0x8c, 0x91, 0x9b, 0xd7, 0xdd, 0x60, 0xbb, 0x8a, 0xc1, 0x57, 0x03, 0xc3, - 0x39, 0x34, 0x69, 0x38, 0x29, 0x38, 0xd7, 0xad, 0xfb, 0xeb, 0x78, 0xe9, 0x3c, 0x2f, 0xfb, 0x18, 0x96, 0x47, 0x3c, - 0xd7, 0xf6, 0x5f, 0x2f, 0xe4, 0xf9, 0x7a, 0x09, 0xcb, 0xd6, 0x5f, 0x93, 0xe4, 0x08, 0xd9, 0xcf, 0x2a, 0xa2, 0xe6, - 0x17, 0x8c, 0x43, 0xe4, 0x4f, 0x71, 0x4c, 0x15, 0xcd, 0x5c, 0x75, 0x7a, 0x54, 0x7a, 0x83, 0x5e, 0x3c, 0x3c, 0x20, - 0x38, 0x09, 0x90, 0x27, 0x4e, 0x42, 0x18, 0x30, 0xe4, 0x14, 0xd6, 0x7c, 0x05, 0x3c, 0xe6, 0x71, 0xc3, 0x53, 0x9a, - 0xab, 0x3f, 0x01, 0x34, 0xa0, 0x02, 0xea, 0xc3, 0x0e, 0x5f, 0x2c, 0xc1, 0x86, 0x46, 0x08, 0x21, 0x9f, 0x10, 0xfc, - 0x2e, 0xff, 0x4a, 0x50, 0x16, 0x3b, 0x05, 0x32, 0x5a, 0xa7, 0xda, 0x15, 0x93, 0x95, 0x36, 0xa8, 0xff, 0x96, 0x76, - 0x53, 0x5e, 0x10, 0x39, 0x88, 0x50, 0x01, 0x06, 0xb2, 0xda, 0x50, 0xe0, 0xd5, 0x65, 0x9b, 0x69, 0x06, 0x63, 0xf5, - 0x51, 0xd3, 0x66, 0xef, 0xf6, 0x4a, 0x4f, 0x65, 0x40, 0xd4, 0x06, 0x86, 0x77, 0xfd, 0xcf, 0xe1, 0x69, 0x19, 0xcf, - 0x8b, 0x41, 0x35, 0x4e, 0x87, 0xc8, 0x70, 0x9d, 0x3a, 0x56, 0xad, 0xfc, 0xcf, 0x94, 0x12, 0xe3, 0x53, 0x51, 0xe1, - 0xc0, 0xba, 0x6a, 0xa8, 0x5a, 0x10, 0xa6, 0x8d, 0xd2, 0x3f, 0x28, 0xca, 0xa0, 0x05, 0x58, 0x1a, 0xb5, 0xc6, 0x01, - 0x9b, 0x9a, 0x5e, 0x9e, 0x1b, 0xd3, 0x89, 0x57, 0xfb, 0x9b, 0xc0, 0x84, 0xbb, 0xfa, 0x31, 0x87, 0xba, 0xb6, 0x78, - 0xae, 0xef, 0xbb, 0x8f, 0xa7, 0x3c, 0x24, 0x7a, 0x26, 0x16, 0x9a, 0xb2, 0xb9, 0xce, 0xe7, 0x1d, 0x14, 0x1b, 0x5e, - 0xd8, 0xe7, 0x2b, 0x24, 0xc8, 0x01, 0x87, 0x4d, 0x71, 0x35, 0x0e, 0xcf, 0x38, 0x27, 0x2a, 0x7d, 0x2e, 0xf6, 0x83, - 0x03, 0x38, 0x3c, 0xe7, 0x4a, 0x59, 0xcb, 0xe7, 0x6f, 0xd3, 0x19, 0x4f, 0xfa, 0x12, 0x9c, 0x94, 0x32, 0xf1, 0x8a, - 0xc5, 0x9f, 0xf1, 0xf1, 0x11, 0x67, 0x78, 0x7f, 0xa3, 0xf3, 0x69, 0x7f, 0x9b, 0xae, 0x8d, 0x79, 0x0f, 0xf2, 0x46, - 0x05, 0xcf, 0x5d, 0x58, 0xc6, 0x36, 0x51, 0x40, 0xf0, 0x37, 0x3a, 0xa7, 0x69, 0x1e, 0x42, 0x3c, 0xac, 0xa2, 0x22, - 0xbf, 0xa3, 0x48, 0x96, 0x68, 0xe1, 0x6d, 0x4e, 0x8b, 0xf4, 0x6a, 0x7a, 0xde, 0xe6, 0x4b, 0x99, 0x0e, 0xed, 0xc6, - 0xd4, 0x49, 0xb4, 0xaa, 0x30, 0x21, 0x88, 0xc0, 0x9d, 0x2e, 0x91, 0x60, 0xb7, 0x26, 0xb3, 0x27, 0xfc, 0x6b, 0x4c, - 0xe3, 0x20, 0x6e, 0xd9, 0x73, 0x8b, 0x7d, 0x4f, 0xcd, 0x1c, 0xe8, 0x85, 0x9c, 0xa1, 0x38, 0x64, 0x0c, 0xfa, 0x42, - 0xae, 0x1f, 0x8f, 0xd0, 0x49, 0x05, 0xf8, 0x55, 0xc9, 0xaf, 0x7a, 0xbc, 0x96, 0x0a, 0x95, 0xea, 0x6c, 0x22, 0x47, - 0xfb, 0xb3, 0xb4, 0xf1, 0x18, 0xfb, 0xb1, 0x3c, 0x7d, 0xed, 0xe9, 0x04, 0x50, 0xb1, 0x25, 0xb7, 0xb2, 0x6f, 0xc8, - 0x43, 0x4b, 0xbf, 0x79, 0x9c, 0x61, 0xe6, 0x08, 0xd0, 0xd9, 0xaa, 0xe0, 0xa9, 0x8b, 0xd9, 0xdb, 0x5b, 0x6e, 0x2a, - 0xb6, 0xdf, 0xf0, 0x19, 0x60, 0x27, 0x89, 0x40, 0x34, 0xaa, 0xf6, 0x59, 0xa7, 0x31, 0x79, 0x2a, 0x9e, 0x42, 0x23, - 0x60, 0x29, 0x22, 0xd7, 0x94, 0x68, 0x5d, 0xcb, 0xd0, 0x45, 0x72, 0x7c, 0xbb, 0x1c, 0x82, 0x04, 0x77, 0x68, 0xd6, - 0x48, 0xe8, 0xa9, 0xba, 0x62, 0xae, 0x54, 0x95, 0x50, 0x77, 0x3a, 0x4c, 0x79, 0x6e, 0xac, 0xf0, 0x28, 0x73, 0xd1, - 0xb9, 0x8e, 0x55, 0x25, 0xc2, 0x22, 0x2e, 0xce, 0x02, 0x2f, 0x02, 0xfc, 0x08, 0x0c, 0xe2, 0x52, 0x95, 0x65, 0xa6, - 0x08, 0x49, 0x93, 0x6d, 0x81, 0xb1, 0xe2, 0xd0, 0x6f, 0xa2, 0x9a, 0x07, 0x5f, 0xff, 0x34, 0xa1, 0x28, 0xec, 0x04, - 0x44, 0xa3, 0x41, 0xb7, 0x16, 0xd0, 0xcc, 0xfc, 0x9b, 0x72, 0x78, 0x0c, 0x9d, 0x27, 0xf1, 0x86, 0x25, 0xc9, 0xa2, - 0x3f, 0xff, 0x97, 0x06, 0x61, 0xd2, 0x3b, 0x90, 0x52, 0x95, 0x40, 0xbb, 0x61, 0x6e, 0x3e, 0x89, 0x0c, 0xd9, 0xa2, - 0x5c, 0xec, 0x71, 0x94, 0x73, 0x1b, 0xd2, 0x0a, 0x66, 0x5e, 0x42, 0xc9, 0x3b, 0x5a, 0xaf, 0xbc, 0x0e, 0xab, 0x6e, - 0x79, 0x8d, 0x97, 0x38, 0xd1, 0x2f, 0x58, 0x74, 0x4d, 0xae, 0xc1, 0x6d, 0x42, 0x03, 0x32, 0x2b, 0x13, 0xd5, 0x1b, - 0x82, 0xa7, 0x90, 0xb2, 0x31, 0xe0, 0xe2, 0xdc, 0xe0, 0xd1, 0xf9, 0x9c, 0x90, 0xb2, 0x90, 0x0b, 0xcd, 0x00, 0x27, - 0x31, 0x92, 0xd1, 0xa6, 0x2e, 0xe5, 0x42, 0x9d, 0x7e, 0xe0, 0xad, 0x83, 0x1e, 0xd6, 0x66, 0x67, 0x2b, 0xf6, 0xb2, - 0x5e, 0x31, 0xb2, 0xa6, 0xea, 0x72, 0x52, 0x6e, 0x4a, 0x68, 0x68, 0x3f, 0xc6, 0x7b, 0x19, 0x87, 0x1c, 0x66, 0xd5, - 0x18, 0x89, 0x3d, 0x23, 0x39, 0xb3, 0x49, 0x92, 0x65, 0xd6, 0x65, 0x2d, 0xfd, 0x6c, 0xae, 0xfd, 0x8f, 0x6c, 0xe1, - 0x11, 0xcb, 0x38, 0xbf, 0xd2, 0x43, 0x49, 0xb8, 0xb2, 0x4e, 0x93, 0xe7, 0xe9, 0x07, 0xe1, 0xba, 0xdb, 0xd4, 0x8c, - 0x86, 0xf7, 0x80, 0x84, 0x2c, 0x6d, 0xd9, 0xaa, 0x36, 0x36, 0x36, 0xf8, 0xb9, 0xcf, 0x03, 0x0b, 0xf1, 0xa0, 0x21, - 0x19, 0xfd, 0x7c, 0x9b, 0xc7, 0x86, 0x85, 0xce, 0xe8, 0xa0, 0x94, 0x5e, 0x4b, 0x71, 0xee, 0x05, 0x4a, 0x2c, 0xf8, - 0x72, 0xaf, 0xa4, 0xc9, 0x99, 0x07, 0x7e, 0x10, 0x67, 0x46, 0x17, 0x99, 0x77, 0xbe, 0x46, 0xdd, 0x4a, 0xef, 0x95, - 0x96, 0xf4, 0x83, 0x5f, 0xfd, 0x6c, 0x95, 0x5e, 0xa7, 0x5f, 0x22, 0x73, 0xe6, 0x2b, 0x5c, 0xa2, 0x5d, 0x81, 0x1d, - 0xc6, 0x49, 0x5d, 0xf3, 0xeb, 0x0e, 0xd0, 0xfb, 0xc2, 0x9b, 0x81, 0x46, 0x89, 0xc0, 0x0b, 0x4d, 0x2e, 0x71, 0x25, - 0xbe, 0x10, 0xde, 0x14, 0x7b, 0x98, 0xc1, 0x44, 0x6c, 0x14, 0x41, 0x3c, 0x00, 0xff, 0x1c, 0xe2, 0x97, 0x31, 0xbf, - 0xd7, 0x41, 0x6d, 0xb4, 0x20, 0x34, 0x45, 0xe6, 0x26, 0x7b, 0x11, 0x5b, 0x90, 0xc3, 0x2b, 0x81, 0x04, 0xb9, 0x66, - 0x8d, 0x1d, 0xb0, 0x65, 0x5b, 0xa1, 0x6c, 0xde, 0x70, 0x6c, 0xb0, 0x3b, 0x7b, 0x69, 0x6d, 0x12, 0x84, 0xb8, 0x44, - 0xd4, 0x9a, 0x1e, 0x19, 0xe5, 0xab, 0x96, 0x82, 0x4a, 0xf4, 0xf3, 0xf4, 0xbf, 0xb1, 0x49, 0x6f, 0x17, 0x04, 0xfd, - 0x52, 0x64, 0xda, 0x2f, 0x34, 0x0c, 0x4a, 0x8b, 0xbc, 0xff, 0x43, 0x09, 0x7c, 0x1d, 0xac, 0xe9, 0x3a, 0xd5, 0x6e, - 0x7d, 0xf1, 0x4f, 0x50, 0x3d, 0x4f, 0xc6, 0x1d, 0x46, 0x2e, 0x0c, 0x68, 0x7e, 0x6f, 0x24, 0x9d, 0xb7, 0xfe, 0xcf, - 0x12, 0x02, 0x67, 0x90, 0x25, 0x14, 0xae, 0x11, 0xf9, 0x26, 0xff, 0xc0, 0x30, 0xed, 0x15, 0xc1, 0xce, 0x8e, 0xbc, - 0x37, 0xb6, 0x93, 0x75, 0x88, 0x36, 0xf4, 0x0d, 0x98, 0xb2, 0x7e, 0xa3, 0x59, 0x36, 0x40, 0x6d, 0x3b, 0xe3, 0xb6, - 0x2b, 0x69, 0x16, 0x92, 0xe1, 0x99, 0xc5, 0x20, 0xd5, 0x46, 0x7e, 0xe6, 0x95, 0x85, 0x33, 0x73, 0x77, 0xa1, 0x91, - 0x9f, 0x3d, 0x9d, 0xe1, 0xf0, 0xc6, 0x46, 0x7a, 0xe1, 0x09, 0x4b, 0x73, 0x43, 0x07, 0x00, 0x20, 0x5c, 0x2a, 0x3a, - 0xde, 0xb1, 0x54, 0xd9, 0x4a, 0xd4, 0x57, 0x82, 0xe6, 0xe0, 0x38, 0x1b, 0x5d, 0xc8, 0x27, 0x4c, 0x18, 0xe9, 0x79, - 0x0e, 0x11, 0x8f, 0x63, 0xe5, 0x32, 0x18, 0x32, 0x07, 0x3f, 0x91, 0x60, 0x47, 0xb3, 0xc3, 0x59, 0x0e, 0x57, 0xa9, - 0x5f, 0x27, 0xa8, 0x86, 0xd4, 0x58, 0x65, 0x6c, 0xc3, 0xfa, 0xff, 0x0a, 0x27, 0x2e, 0xeb, 0x79, 0x42, 0x2f, 0x3a, - 0x2c, 0xdc, 0xc2, 0x47, 0x2e, 0xf9, 0x87, 0x38, 0x38, 0xc4, 0xd9, 0x18, 0xe7, 0x19, 0x48, 0x9e, 0x36, 0x50, 0x18, - 0x79, 0x39, 0xbe, 0x54, 0x72, 0xb6, 0x1b, 0xaa, 0x4a, 0x4a, 0x97, 0x74, 0xab, 0xbd, 0x67, 0xb2, 0x1f, 0x91, 0x13, - 0x27, 0x45, 0x24, 0x99, 0x4c, 0x2a, 0xa8, 0x53, 0x1a, 0x6c, 0xfa, 0x15, 0xc0, 0x08, 0xe6, 0x5a, 0xd7, 0x34, 0x45, - 0x69, 0x99, 0x70, 0xf8, 0x1c, 0x2d, 0xd6, 0x99, 0x43, 0xd1, 0xc1, 0x60, 0x50, 0x48, 0x33, 0xb4, 0x0b, 0x3c, 0xc8, - 0xa8, 0xcc, 0x1e, 0x7f, 0x9e, 0x9f, 0x14, 0x4d, 0xd8, 0x64, 0xc5, 0x2a, 0x51, 0xa4, 0x96, 0xb1, 0xa4, 0x54, 0x75, - 0xfe, 0xed, 0x3e, 0x1a, 0x1a, 0x1d, 0x58, 0x03, 0x3a, 0x0a, 0x25, 0x9c, 0xf7, 0x50, 0x3c, 0xe9, 0x4e, 0xcd, 0xde, - 0x7e, 0xeb, 0x77, 0x57, 0x1f, 0xe2, 0x54, 0xdf, 0x25, 0x9d, 0xdc, 0x37, 0x9e, 0xb0, 0x9d, 0xb1, 0xd7, 0xba, 0xbe, - 0x63, 0x0b, 0x60, 0x97, 0x19, 0xcf, 0xc6, 0xf0, 0xbe, 0x8e, 0xbd, 0xf8, 0x82, 0x5c, 0x3d, 0x7f, 0xd6, 0x6a, 0xf9, - 0x8c, 0x5b, 0x0a, 0xa7, 0x1c, 0xa1, 0x85, 0x45, 0x40, 0x76, 0xc0, 0x28, 0x20, 0x2f, 0xa1, 0xcb, 0x18, 0xc2, 0x83, - 0x5f, 0x1b, 0x14, 0xad, 0x0e, 0x38, 0x13, 0xe8, 0x51, 0x3f, 0xe8, 0xc1, 0xdf, 0x74, 0x12, 0x6f, 0x4c, 0xbc, 0xb7, - 0x68, 0x4a, 0xbf, 0x5a, 0x69, 0x53, 0xa6, 0x3c, 0xbb, 0xe4, 0xc3, 0xd8, 0x0e, 0x55, 0x5b, 0xbb, 0x0d, 0x35, 0xc4, - 0x67, 0xb8, 0x9f, 0xb8, 0xa2, 0x78, 0x6c, 0x06, 0xea, 0x6f, 0xfc, 0xc8, 0x3b, 0x6d, 0x3f, 0x92, 0xfb, 0x10, 0x87, - 0x1e, 0xcb, 0xbe, 0x2a, 0xfb, 0x00, 0xae, 0xd9, 0xd9, 0x58, 0x89, 0x9f, 0x15, 0x61, 0xb8, 0x1c, 0x82, 0xf1, 0x67, - 0xb5, 0x0d, 0x3d, 0xa9, 0xa7, 0xac, 0x79, 0xfc, 0x3a, 0x32, 0xbf, 0xc9, 0xc2, 0xa4, 0x5e, 0xc8, 0x9a, 0x1e, 0xa7, - 0x33, 0x43, 0xe7, 0x4c, 0xe4, 0xee, 0xd9, 0xaf, 0xd1, 0xda, 0xc8, 0x48, 0xed, 0x6c, 0xd1, 0xc7, 0xdf, 0x35, 0x55, - 0x36, 0x9a, 0x34, 0xe3, 0xb1, 0x73, 0xad, 0x4b, 0x97, 0xba, 0x8c, 0x4c, 0xf7, 0x67, 0xbd, 0x58, 0x50, 0xa5, 0x55, - 0x66, 0xde, 0x27, 0x52, 0xc3, 0xb8, 0xd6, 0x6a, 0xe2, 0x9d, 0x5e, 0x7c, 0xcb, 0x8e, 0xdd, 0x74, 0x96, 0x64, 0x30, - 0x98, 0x8e, 0xdc, 0x0c, 0x1d, 0xc9, 0x08, 0x53, 0xbf, 0x7c, 0x32, 0x30, 0xeb, 0x3a, 0x7f, 0x6f, 0x5f, 0x33, 0x8e, - 0xe2, 0x5b, 0x8f, 0x15, 0xfd, 0xb6, 0x4e, 0xb7, 0xef, 0x09, 0x70, 0x6d, 0x53, 0xfb, 0xd4, 0x5b, 0xa5, 0x9c, 0x8d, - 0xe4, 0x58, 0x4e, 0xe2, 0x09, 0x14, 0x3c, 0x00, 0x49, 0x04, 0x4c, 0x9a, 0xf9, 0xc5, 0x52, 0xc9, 0x84, 0x8d, 0xba, - 0xdb, 0xef, 0x15, 0x17, 0x18, 0xb5, 0x7f, 0x94, 0xb9, 0x9a, 0x5e, 0x8e, 0x14, 0x25, 0xd3, 0x81, 0x81, 0xa6, 0x30, - 0x96, 0x3e, 0xff, 0x62, 0x5b, 0x89, 0x05, 0x95, 0x45, 0xb1, 0xb0, 0x66, 0x74, 0x00, 0x22, 0xe8, 0x63, 0x2a, 0xa8, - 0x1a, 0x7e, 0x43, 0xca, 0xe7, 0xf4, 0xeb, 0xd9, 0xf9, 0x88, 0x6d, 0xba, 0x29, 0x7d, 0x8c, 0x07, 0xab, 0xec, 0x75, - 0x7e, 0x1f, 0x9f, 0xfa, 0x42, 0x2f, 0x5e, 0x49, 0xde, 0x59, 0xf7, 0x4f, 0xf3, 0xcf, 0xe7, 0x6c, 0xfe, 0xf9, 0xac, - 0x19, 0x60, 0x98, 0xd9, 0x18, 0xf7, 0x2a, 0x7a, 0xb5, 0xc0, 0x35, 0x4e, 0x65, 0x78, 0x1a, 0xcb, 0x7f, 0x74, 0x16, - 0xae, 0x32, 0xc0, 0x99, 0x6c, 0xa0, 0x4d, 0x65, 0x10, 0x7c, 0x73, 0xc3, 0x82, 0xa6, 0x2b, 0x27, 0x26, 0x9a, 0x99, - 0x48, 0x5c, 0x82, 0x9c, 0xc4, 0x1c, 0xec, 0xc9, 0xa5, 0xea, 0x3a, 0x25, 0x33, 0x7e, 0x66, 0xda, 0x82, 0x9e, 0xa0, - 0xf0, 0x53, 0x90, 0xa9, 0x00, 0x41, 0x74, 0xbe, 0x4f, 0xf3, 0x38, 0x4a, 0x7f, 0xe7, 0x98, 0xa7, 0xfc, 0xff, 0x89, - 0x2c, 0x11, 0xa9, 0x6b, 0x19, 0xc2, 0x01, 0xbf, 0x46, 0x34, 0x75, 0xc6, 0xef, 0xc5, 0xa0, 0x7e, 0x91, 0xde, 0xaa, - 0x11, 0x38, 0x17, 0xa0, 0xae, 0x46, 0x98, 0x06, 0xf2, 0x8e, 0xf6, 0x1a, 0xf9, 0xc5, 0xa9, 0xd2, 0x2d, 0x7b, 0x5a, - 0xf2, 0x8a, 0x2c, 0x54, 0xfe, 0xc9, 0x98, 0x62, 0x56, 0x15, 0x1d, 0x47, 0x9a, 0xf7, 0x0d, 0x36, 0xc9, 0x17, 0x4e, - 0x12, 0x7a, 0xca, 0xa9, 0xb1, 0x30, 0x6e, 0x15, 0x5e, 0xda, 0x85, 0xd1, 0x07, 0x64, 0x0e, 0x34, 0x17, 0x65, 0x3f, - 0x0e, 0x71, 0x44, 0x7c, 0xa0, 0x9f, 0xf1, 0x2d, 0x64, 0xdc, 0xf6, 0x1c, 0x27, 0xc4, 0xa9, 0xfa, 0x62, 0x28, 0x5e, - 0xc6, 0x26, 0x15, 0x92, 0x1b, 0xb2, 0x18, 0xca, 0xaa, 0x85, 0xe7, 0x67, 0xf0, 0x7c, 0xe1, 0xf9, 0x69, 0x9e, 0x1b, - 0xbc, 0x25, 0x53, 0x51, 0x46, 0x62, 0xed, 0x0a, 0x3b, 0x6a, 0xf9, 0x4d, 0x5e, 0x61, 0xef, 0x73, 0x00, 0x12, 0xd5, - 0x5e, 0x15, 0x0b, 0x72, 0x01, 0x1b, 0xd0, 0xa0, 0x3a, 0x0c, 0xf2, 0x12, 0x5f, 0x0a, 0x20, 0x00, 0xa3, 0x07, 0xef, - 0xd9, 0x71, 0x3a, 0x6d, 0x0c, 0xe3, 0x2e, 0xc7, 0x04, 0x4a, 0xae, 0xd9, 0x54, 0x12, 0xf2, 0x26, 0x27, 0x9c, 0x7d, - 0x01, 0x2a, 0x84, 0x31, 0x80, 0x3c, 0x35, 0xb6, 0x58, 0xbd, 0x7e, 0x2b, 0xd5, 0xe7, 0x04, 0xa3, 0xb0, 0x97, 0x98, - 0x7d, 0xa1, 0x1b, 0x11, 0x11, 0x39, 0x8b, 0x39, 0xb9, 0xf4, 0xda, 0x55, 0xea, 0xfb, 0xd1, 0xd9, 0x27, 0x04, 0xf4, - 0xed, 0xe4, 0x5b, 0x73, 0x13, 0x0e, 0xc7, 0x97, 0x2c, 0x33, 0xd1, 0x7f, 0xae, 0x16, 0xc8, 0xb3, 0x32, 0xe7, 0x3d, - 0x57, 0xc7, 0xae, 0x89, 0x34, 0xc5, 0xa6, 0xa0, 0x53, 0x70, 0x4a, 0x10, 0x41, 0x5b, 0x70, 0x92, 0xe4, 0x90, 0x88, - 0xca, 0x40, 0x7f, 0x36, 0x15, 0x4b, 0x30, 0x5b, 0xc3, 0x52, 0x9d, 0xd7, 0x5a, 0xec, 0x9a, 0x1f, 0xb2, 0x27, 0x99, - 0x65, 0xc3, 0x45, 0xea, 0x4c, 0x27, 0xc5, 0x75, 0x64, 0x8d, 0xc2, 0xd4, 0xb8, 0x8b, 0xc5, 0xab, 0x84, 0x2b, 0xa9, - 0xdc, 0xa4, 0x4a, 0xbc, 0xd9, 0xa8, 0x19, 0x8f, 0xc6, 0xe5, 0x8f, 0x6d, 0x76, 0xf4, 0x46, 0x4a, 0xc0, 0xe3, 0x21, - 0xd5, 0xde, 0xa6, 0x33, 0x6e, 0x43, 0xfe, 0xed, 0xea, 0x69, 0x0b, 0x58, 0xfc, 0x4f, 0x7a, 0x18, 0xe2, 0xff, 0x8d, - 0x0a, 0x8a, 0xc8, 0x36, 0x42, 0x78, 0x14, 0x02, 0x84, 0xfb, 0xc2, 0xc9, 0x0b, 0x62, 0x51, 0xc4, 0xa3, 0xb0, 0xf7, - 0x3a, 0x6b, 0xe5, 0x12, 0x16, 0x7f, 0x1f, 0x74, 0xff, 0x8e, 0x42, 0xe7, 0x5c, 0xaf, 0xf1, 0xa7, 0xe2, 0xc7, 0x1f, - 0x39, 0x76, 0x74, 0x28, 0xc2, 0x4d, 0x0c, 0xad, 0x04, 0xcf, 0xb6, 0x44, 0x75, 0xac, 0x0a, 0x46, 0x84, 0x30, 0x07, - 0xa9, 0x6a, 0xc1, 0x04, 0xbb, 0xf0, 0x13, 0x2c, 0x3e, 0x10, 0x4e, 0xff, 0x1b, 0x1a, 0xb7, 0x09, 0x61, 0x36, 0x58, - 0xe5, 0xa8, 0x93, 0x27, 0xf1, 0x6a, 0x9f, 0xf9, 0x23, 0xe3, 0xd6, 0x57, 0x5f, 0xa4, 0xd5, 0x48, 0xf6, 0x14, 0x33, - 0x38, 0xbc, 0x76, 0x4b, 0x99, 0x19, 0x9f, 0x23, 0x26, 0x55, 0xf3, 0xe4, 0x45, 0x41, 0xa9, 0xa1, 0xae, 0x59, 0x9e, - 0x0b, 0xf3, 0x9c, 0xe1, 0xb9, 0xc0, 0x60, 0x2c, 0xeb, 0xb2, 0xc6, 0x19, 0x9f, 0xc6, 0x56, 0x0c, 0x8c, 0x3b, 0x1e, - 0x0e, 0x7a, 0x8e, 0x39, 0x54, 0x80, 0x5e, 0x04, 0x6e, 0x22, 0x3e, 0xe9, 0x25, 0x70, 0xea, 0xd4, 0x86, 0xf3, 0xcc, - 0x5c, 0x8e, 0x98, 0xf2, 0x0c, 0xa7, 0x98, 0xd2, 0xef, 0xdc, 0x26, 0xd2, 0x6e, 0x7d, 0xad, 0xc9, 0x47, 0xc1, 0xad, - 0x7a, 0x3a, 0xac, 0x23, 0x1a, 0x97, 0x16, 0xc1, 0x93, 0x45, 0x79, 0xd8, 0xbf, 0xf1, 0x9c, 0x5f, 0x89, 0xb4, 0x93, - 0x52, 0x25, 0x3a, 0x9f, 0x79, 0x0e, 0x42, 0x48, 0x93, 0x96, 0xd0, 0xf6, 0xb9, 0x20, 0x25, 0xc6, 0xb5, 0x53, 0x8a, - 0xd8, 0xcd, 0xc1, 0xfe, 0xd7, 0x53, 0xb9, 0x48, 0xab, 0xeb, 0x87, 0x77, 0x8b, 0x85, 0xd8, 0x4e, 0xc8, 0x79, 0x2e, - 0x64, 0xef, 0xfd, 0x6b, 0x12, 0x5e, 0x27, 0xe5, 0x97, 0xfd, 0x20, 0x71, 0xef, 0x5c, 0x6a, 0xfe, 0x5f, 0x38, 0x6c, - 0x7a, 0xf4, 0xca, 0xc1, 0x6c, 0x33, 0x01, 0x93, 0xa3, 0x52, 0x55, 0xdf, 0x8f, 0x80, 0xd4, 0xf0, 0x30, 0x40, 0x59, - 0xc5, 0xfe, 0x41, 0xbd, 0x25, 0x00, 0xb8, 0xd4, 0xb3, 0xfd, 0xb0, 0xb4, 0xbf, 0xfb, 0x61, 0xab, 0x13, 0x2e, 0x57, - 0x4a, 0xfe, 0x1b, 0x18, 0x42, 0x97, 0x86, 0xe4, 0xb0, 0x75, 0xd6, 0x03, 0x21, 0x77, 0x9e, 0x73, 0x8a, 0x51, 0x5c, - 0x4b, 0xbe, 0x60, 0xfb, 0x91, 0x94, 0xd7, 0xc9, 0x7c, 0x7e, 0xb1, 0xb5, 0x82, 0xcf, 0x3a, 0xa9, 0xbf, 0xa8, 0x44, - 0xff, 0x05, 0xec, 0x3b, 0x88, 0x0f, 0xcf, 0x13, 0xab, 0xca, 0x0f, 0x1d, 0x87, 0x23, 0xac, 0xb0, 0x95, 0x2a, 0x0d, - 0xcd, 0x23, 0xd3, 0xc7, 0x94, 0x9c, 0x9a, 0x80, 0x71, 0x48, 0x72, 0xb6, 0x56, 0x91, 0x4b, 0x92, 0x6a, 0x16, 0xee, - 0xeb, 0x06, 0x9f, 0x84, 0xe9, 0x92, 0x56, 0xe1, 0x1e, 0xa0, 0x7f, 0x0c, 0x7a, 0xfe, 0xcf, 0x4a, 0x71, 0xc0, 0xb0, - 0xea, 0x85, 0x4c, 0xfc, 0x64, 0x00, 0xd7, 0x66, 0x9e, 0xcc, 0x71, 0xe3, 0x6d, 0xd4, 0x47, 0x6d, 0x4b, 0xc0, 0x4f, - 0xa2, 0x27, 0x8f, 0x61, 0x4c, 0x16, 0x86, 0x02, 0x77, 0x2e, 0xf5, 0x73, 0xb0, 0x70, 0xc3, 0x31, 0xfa, 0x86, 0xe0, - 0x5b, 0xfe, 0xfc, 0x0e, 0xd4, 0x71, 0x8c, 0x86, 0x2e, 0x8d, 0xa4, 0xf4, 0x3b, 0x14, 0x44, 0x1a, 0x48, 0xa0, 0x79, - 0xee, 0x6b, 0x28, 0x9c, 0x4e, 0x22, 0x3b, 0xc9, 0x11, 0xd6, 0xce, 0x82, 0x59, 0xab, 0x9c, 0xbe, 0x9e, 0xed, 0x3b, - 0x0c, 0xd0, 0xb5, 0xcb, 0xe9, 0x7d, 0xae, 0xbf, 0x95, 0xa9, 0x9f, 0x2b, 0x84, 0x96, 0x65, 0x9b, 0x9d, 0x43, 0x20, - 0x94, 0x6b, 0xf5, 0x34, 0x44, 0xc1, 0x10, 0xef, 0x74, 0xac, 0x6e, 0xd0, 0x47, 0x2a, 0x5a, 0xe7, 0xab, 0x0d, 0xda, - 0x7c, 0xab, 0xf0, 0xd2, 0xf5, 0xca, 0xf6, 0x34, 0x24, 0xc5, 0x34, 0x62, 0x38, 0xf8, 0x66, 0x42, 0x67, 0xf2, 0x3e, - 0x6e, 0x90, 0x4d, 0x9c, 0x21, 0x79, 0xb8, 0x8e, 0x58, 0xba, 0x4a, 0x58, 0x54, 0xb6, 0xf0, 0x74, 0x4a, 0x3b, 0x5c, - 0xdd, 0x15, 0xe1, 0xe6, 0x4c, 0x06, 0x6a, 0xb9, 0x8e, 0xbe, 0x89, 0xc4, 0x20, 0x07, 0x6c, 0xb9, 0x4e, 0x1f, 0x1d, - 0x55, 0xaa, 0x90, 0xe9, 0x76, 0xde, 0xbc, 0x86, 0x05, 0x87, 0x2d, 0x63, 0x29, 0x0d, 0x30, 0xf0, 0x5d, 0x7b, 0x79, - 0x5f, 0x6d, 0x76, 0x2a, 0x3c, 0xe6, 0xbc, 0x4b, 0x69, 0x91, 0x57, 0xa9, 0x7f, 0x6e, 0xe3, 0xb5, 0xf9, 0xd5, 0xdc, - 0x46, 0x17, 0xbc, 0x39, 0x27, 0x3a, 0xad, 0x6f, 0x31, 0x5e, 0x76, 0x88, 0x68, 0xe7, 0x3e, 0x97, 0x79, 0xba, 0x85, - 0xd4, 0x62, 0xcc, 0x3a, 0xc7, 0x4c, 0x4f, 0x4b, 0x8b, 0xf2, 0x11, 0x63, 0x50, 0xc9, 0x9d, 0xf2, 0xcd, 0xc8, 0x53, - 0x8d, 0xc2, 0x10, 0xab, 0x6d, 0xe8, 0x19, 0xb4, 0xd2, 0x4f, 0x34, 0xfb, 0xf6, 0x76, 0xb3, 0x06, 0xa8, 0xc4, 0x62, - 0x1f, 0x99, 0x65, 0xe1, 0xe3, 0x72, 0x07, 0xa1, 0x83, 0x34, 0xb7, 0x93, 0xc3, 0x38, 0x3d, 0x46, 0x2b, 0xf4, 0xbb, - 0xf6, 0x14, 0xb3, 0x80, 0x48, 0x52, 0x2f, 0x90, 0xce, 0x01, 0x77, 0x49, 0xfd, 0x59, 0x9c, 0x19, 0x61, 0x3e, 0x7a, - 0x29, 0xa1, 0xdc, 0x25, 0x94, 0x1b, 0xaf, 0xf3, 0x24, 0x00, 0x3e, 0x89, 0xb6, 0xd7, 0x56, 0x9c, 0xe6, 0x9c, 0xd7, - 0xb6, 0x08, 0xc3, 0xae, 0xeb, 0xc5, 0x48, 0x72, 0x33, 0x7b, 0x61, 0xcc, 0x18, 0x04, 0x22, 0xa8, 0xe8, 0xe6, 0x80, - 0xc9, 0x98, 0x3a, 0xc2, 0x41, 0xe7, 0x4f, 0xb2, 0xa9, 0xa6, 0xb4, 0x98, 0xda, 0xd1, 0xff, 0xce, 0x1e, 0xfc, 0xf0, - 0x68, 0x7a, 0xfe, 0xeb, 0xdb, 0xfc, 0x1a, 0x34, 0x41, 0x0f, 0xe1, 0x7e, 0x77, 0x35, 0x53, 0xa1, 0xa0, 0xb0, 0xb2, - 0x7d, 0x69, 0x01, 0x50, 0x63, 0x2a, 0x4a, 0x57, 0xd7, 0xd6, 0xfd, 0x49, 0xc6, 0xa7, 0x35, 0x6d, 0x7c, 0x1d, 0xa8, - 0xf2, 0xb5, 0xa1, 0x6c, 0xdd, 0xe1, 0xf3, 0x50, 0xcd, 0x78, 0x0a, 0xda, 0x5c, 0x18, 0xfc, 0x0a, 0x39, 0x6e, 0x42, - 0x27, 0x43, 0x95, 0x4d, 0xb3, 0x13, 0x6f, 0x59, 0x25, 0x6b, 0x29, 0x39, 0x91, 0x12, 0xf6, 0xaa, 0xf2, 0x47, 0xdd, - 0x93, 0xd4, 0x96, 0x6a, 0x70, 0x83, 0x13, 0x94, 0x36, 0x3a, 0xfa, 0x2a, 0x6e, 0xe4, 0xa7, 0x1b, 0x40, 0x44, 0x4d, - 0x4f, 0x87, 0xf6, 0x76, 0xfe, 0x79, 0x6f, 0x8c, 0xb0, 0x49, 0x05, 0x3f, 0xca, 0xa8, 0xa9, 0x1a, 0x9e, 0xfb, 0x53, - 0xaf, 0x6c, 0x87, 0x67, 0xba, 0x9b, 0x2c, 0x6a, 0x8b, 0x3e, 0xe3, 0x02, 0xac, 0x9a, 0x68, 0xaa, 0x71, 0xa1, 0x08, - 0x63, 0x1a, 0x6f, 0xce, 0xda, 0x89, 0xb5, 0xea, 0xd5, 0xed, 0xac, 0x97, 0x1e, 0x6d, 0xb3, 0x05, 0x6a, 0xbc, 0x68, - 0xda, 0xf2, 0x2a, 0x7c, 0xb7, 0xa4, 0x9b, 0x95, 0x23, 0xc8, 0xdc, 0x04, 0xe8, 0x67, 0x3f, 0x64, 0x26, 0x7a, 0x07, - 0xe1, 0x4e, 0x2a, 0xd9, 0x93, 0x8a, 0xcd, 0x78, 0xbe, 0xbd, 0x72, 0x7e, 0x5a, 0x92, 0x6d, 0xc4, 0xda, 0x8d, 0x2a, - 0xe3, 0xb1, 0x35, 0xc8, 0xdb, 0x35, 0xd3, 0x59, 0xa2, 0xbf, 0x86, 0x7b, 0xfd, 0xf9, 0x76, 0x0d, 0x4d, 0xb7, 0xd5, - 0xdc, 0x79, 0xe9, 0xe4, 0x28, 0x29, 0x79, 0xf1, 0x03, 0x7b, 0x6c, 0xe1, 0x7c, 0xb0, 0xf1, 0x90, 0x60, 0x99, 0x8a, - 0x55, 0x9f, 0x55, 0xac, 0xf2, 0x2c, 0x11, 0x85, 0xd9, 0xd3, 0x2e, 0x80, 0xe3, 0x0f, 0x2b, 0xb9, 0x8a, 0x1f, 0x66, - 0x5a, 0xb5, 0x7c, 0x58, 0x08, 0xd5, 0xf4, 0x24, 0x10, 0x19, 0x98, 0x35, 0xf2, 0x70, 0x61, 0xea, 0x98, 0x4c, 0x4a, - 0x49, 0x20, 0x57, 0x58, 0x4d, 0xab, 0x6a, 0xd5, 0x87, 0x1f, 0x6e, 0xb8, 0x41, 0x5d, 0xf9, 0x67, 0x33, 0xae, 0xff, - 0xaf, 0x99, 0x89, 0xe5, 0xa0, 0xb1, 0xd0, 0xfa, 0x27, 0x2d, 0xcc, 0xfd, 0x08, 0xdd, 0x35, 0xc3, 0x95, 0xe9, 0x4b, - 0x14, 0xf3, 0x2b, 0x46, 0x66, 0x5b, 0xe4, 0xb5, 0x32, 0xd8, 0xc5, 0xde, 0x98, 0x49, 0x5b, 0x27, 0xc6, 0x34, 0x36, - 0x24, 0x56, 0x67, 0x51, 0x23, 0x43, 0x6a, 0x6b, 0xf3, 0x9d, 0xbe, 0xa2, 0xf9, 0x25, 0x2f, 0xf1, 0x97, 0x69, 0xc8, - 0xc2, 0x7c, 0xab, 0xe8, 0x8d, 0xf4, 0x1a, 0x7a, 0x09, 0xfe, 0x62, 0xe1, 0xde, 0x04, 0x78, 0x8e, 0x22, 0x2a, 0x46, - 0x63, 0xf0, 0x4d, 0x16, 0x07, 0x7a, 0xbf, 0xc2, 0xad, 0x20, 0xc3, 0x94, 0x15, 0xff, 0x5f, 0x03, 0xad, 0xf4, 0x2f, - 0x20, 0xf6, 0x0d, 0xbe, 0xc0, 0xca, 0x5b, 0x41, 0xb9, 0x87, 0xe9, 0xfe, 0x02, 0xdf, 0x0a, 0x71, 0x30, 0x68, 0x44, - 0x4d, 0x58, 0xeb, 0x3d, 0xc5, 0x38, 0x31, 0x3d, 0xf8, 0x67, 0x7a, 0x68, 0x25, 0x08, 0x87, 0x31, 0x86, 0x0e, 0x52, - 0x80, 0x60, 0xa5, 0x7c, 0xf5, 0xf5, 0xa1, 0x55, 0xa1, 0x64, 0xe4, 0xab, 0x66, 0x83, 0x4f, 0xb1, 0x9d, 0xa7, 0xd8, - 0x3e, 0x2b, 0x5f, 0x5a, 0x6b, 0x4d, 0x67, 0xab, 0x46, 0x7a, 0xbe, 0x0a, 0x2e, 0x30, 0xec, 0x60, 0xe0, 0xbc, 0x0a, - 0xbe, 0x22, 0x41, 0x93, 0x40, 0x58, 0x40, 0x83, 0xe7, 0x36, 0x94, 0x93, 0x0f, 0x09, 0x94, 0xba, 0x04, 0xbb, 0xcd, - 0x8f, 0x48, 0x6e, 0x03, 0x21, 0xb5, 0x18, 0x67, 0x0b, 0x7b, 0x70, 0x26, 0xcd, 0xfa, 0xb9, 0xb1, 0x1e, 0x5a, 0x89, - 0x92, 0x36, 0x5d, 0x9e, 0x0d, 0xae, 0x19, 0x29, 0xac, 0x93, 0xff, 0x2f, 0x39, 0x6e, 0xf3, 0xbd, 0x2a, 0x08, 0xae, - 0xc4, 0x49, 0x5f, 0xeb, 0x29, 0x28, 0x77, 0x3a, 0x74, 0xe0, 0x8e, 0x20, 0x4b, 0xd9, 0xf3, 0xcc, 0xd3, 0xe7, 0x76, - 0x81, 0x9d, 0x98, 0xed, 0x9e, 0x95, 0xce, 0x70, 0xc2, 0xf1, 0xfb, 0x05, 0xef, 0x2e, 0xfd, 0x39, 0xa4, 0xdc, 0xdf, - 0x57, 0x96, 0x8c, 0x77, 0xc7, 0xdd, 0xb3, 0x3f, 0xc7, 0x1b, 0xb7, 0xfc, 0x29, 0xf7, 0xc3, 0x6d, 0x24, 0xdd, 0xf0, - 0xaa, 0xf9, 0x17, 0x8f, 0x6c, 0xe5, 0x56, 0x42, 0xd0, 0x3a, 0x9d, 0xe3, 0x9b, 0xaf, 0x41, 0xa7, 0x12, 0xb6, 0xf8, - 0xf1, 0x5d, 0xf2, 0x5b, 0x63, 0x2f, 0x46, 0x61, 0x70, 0x68, 0xa6, 0x76, 0x95, 0x9c, 0xb7, 0xe0, 0xb6, 0x4c, 0xed, - 0x56, 0x81, 0x34, 0x7a, 0xe7, 0xb9, 0xfa, 0x3a, 0xfd, 0x54, 0x35, 0xff, 0x31, 0x73, 0x13, 0xf6, 0xb1, 0x42, 0x91, - 0xf8, 0x67, 0x6a, 0x74, 0x83, 0x91, 0xca, 0x03, 0x75, 0x81, 0x55, 0x4b, 0x82, 0xca, 0xdb, 0x11, 0x3f, 0xe5, 0xd7, - 0xdc, 0xe5, 0x48, 0x6c, 0x84, 0xfd, 0x69, 0x6c, 0x3e, 0x53, 0x9f, 0xed, 0x6c, 0x99, 0x65, 0xb7, 0x8f, 0x99, 0x87, - 0x47, 0xf9, 0x5b, 0xaa, 0x5b, 0xe4, 0x7b, 0xb3, 0x2c, 0x2f, 0xca, 0xec, 0xd4, 0x3e, 0x87, 0x4f, 0xb4, 0xd1, 0x24, - 0x7f, 0xe3, 0x71, 0x2f, 0xb1, 0x21, 0xd9, 0x34, 0x2f, 0x33, 0x87, 0xd8, 0xc3, 0xf3, 0x1b, 0x3f, 0x65, 0x53, 0x99, - 0x28, 0x38, 0x6d, 0x87, 0xa6, 0x03, 0xf7, 0x0d, 0xd4, 0x41, 0x15, 0x20, 0xa7, 0x2b, 0xb0, 0xd1, 0x80, 0x59, 0x6d, - 0xd4, 0x97, 0xa5, 0xb2, 0x8a, 0xb3, 0xae, 0xdb, 0x75, 0xfa, 0xc5, 0x46, 0x12, 0xb8, 0xb1, 0x47, 0x91, 0x3c, 0x9d, - 0x95, 0xae, 0xf1, 0x57, 0x79, 0xc9, 0x1c, 0xa5, 0x0f, 0xf8, 0xfc, 0x5b, 0xf0, 0x48, 0xfe, 0x52, 0x74, 0x8d, 0xab, - 0xdc, 0x66, 0x34, 0x6a, 0xcc, 0xc9, 0x78, 0x43, 0xd8, 0x58, 0x14, 0x55, 0x31, 0x35, 0xa7, 0xfc, 0x9c, 0x19, 0x8d, - 0xe4, 0x05, 0x09, 0xf2, 0xb9, 0xb7, 0xfb, 0x99, 0x5c, 0xf0, 0x2b, 0xd7, 0xa1, 0x57, 0x56, 0xa3, 0xe2, 0x4b, 0xe7, - 0xb8, 0xb7, 0xee, 0x50, 0x80, 0x0c, 0xd8, 0x43, 0x86, 0x9c, 0xf2, 0x56, 0xd3, 0xbe, 0x5e, 0x7e, 0xff, 0x82, 0x91, - 0x14, 0xab, 0xb2, 0xef, 0xc6, 0x26, 0x4c, 0x22, 0x3b, 0xc2, 0x5e, 0x35, 0xb2, 0x9b, 0x63, 0x11, 0x21, 0x21, 0x19, - 0xf7, 0x4c, 0xc8, 0xe8, 0xad, 0x5e, 0x26, 0x80, 0x73, 0xe2, 0x49, 0xe1, 0x4c, 0x4e, 0x9c, 0xc8, 0xb2, 0xb4, 0x15, - 0x5b, 0x62, 0xe1, 0x08, 0x5f, 0x6b, 0xca, 0x6c, 0xdb, 0x18, 0x2b, 0x68, 0x70, 0xcc, 0x01, 0xa2, 0x33, 0x9f, 0xf1, - 0x86, 0x09, 0xe7, 0xfc, 0xa9, 0xf2, 0xbe, 0x59, 0xa6, 0x41, 0x5f, 0x15, 0x2c, 0x6c, 0xb7, 0x9e, 0x20, 0xca, 0x34, - 0x03, 0x03, 0xf2, 0x6d, 0x85, 0x6a, 0xfe, 0x95, 0x97, 0x28, 0xf8, 0xee, 0x32, 0xf2, 0xf3, 0xe7, 0x70, 0x1b, 0x21, - 0x1a, 0x04, 0x8d, 0xed, 0x85, 0x51, 0xb2, 0xb3, 0xac, 0x86, 0x5c, 0x84, 0x93, 0xe0, 0x83, 0xdd, 0x53, 0xb8, 0x3e, - 0x87, 0xa4, 0x97, 0xe0, 0x29, 0x30, 0x4f, 0x68, 0xa4, 0xb4, 0xdf, 0xf7, 0x2e, 0x08, 0x84, 0xe6, 0x2d, 0x8f, 0x70, - 0x40, 0x32, 0x62, 0x36, 0x16, 0x1e, 0x37, 0x0b, 0x97, 0x56, 0xc1, 0xa9, 0xcb, 0x6a, 0xd4, 0x15, 0xc9, 0x25, 0x85, - 0x34, 0x63, 0xf0, 0x60, 0x74, 0x24, 0x24, 0x96, 0x85, 0x60, 0xcc, 0x86, 0xbf, 0xf5, 0x02, 0xeb, 0xa7, 0x39, 0x00, - 0xf6, 0x04, 0xf1, 0xd8, 0x84, 0xe9, 0x0d, 0x44, 0x78, 0xb6, 0x2d, 0x0f, 0x98, 0xf7, 0x05, 0x1a, 0xcf, 0xf9, 0x98, - 0x52, 0xe6, 0x7c, 0x01, 0x2e, 0x33, 0xf1, 0x62, 0x20, 0x87, 0x80, 0x7b, 0x88, 0x87, 0xfc, 0xe0, 0xb1, 0x97, 0x60, - 0x67, 0x64, 0xa5, 0xbb, 0x5d, 0xbb, 0x71, 0xcc, 0x2d, 0x45, 0x0e, 0xbc, 0xd8, 0x3f, 0x38, 0xac, 0x6b, 0xb1, 0x4a, - 0xbf, 0x69, 0xa0, 0xd2, 0xbf, 0xfa, 0xf7, 0xcd, 0xe7, 0xc8, 0xb7, 0xcf, 0xb5, 0xd4, 0xa2, 0xf8, 0x81, 0x77, 0x56, - 0x93, 0x82, 0x76, 0xff, 0xab, 0x26, 0x23, 0x5f, 0x52, 0x5a, 0x2d, 0x8b, 0x4f, 0xb5, 0x8b, 0x9e, 0xa2, 0x5a, 0x36, - 0x79, 0x6e, 0x0f, 0x49, 0x8e, 0x5e, 0x6b, 0x19, 0x56, 0xb5, 0x3d, 0xea, 0x83, 0xbd, 0xd7, 0x7b, 0x41, 0x1e, 0x52, - 0x0f, 0x89, 0xaa, 0x37, 0x5e, 0x40, 0xc5, 0x7f, 0xf5, 0x95, 0xea, 0x99, 0x5f, 0xd0, 0x90, 0x37, 0xca, 0x31, 0xbe, - 0x6c, 0x9c, 0xb6, 0xae, 0x1e, 0xc5, 0x14, 0xac, 0x14, 0x65, 0x65, 0x88, 0x1b, 0x59, 0xa1, 0x6b, 0x44, 0x5a, 0x03, - 0x7f, 0x63, 0xa3, 0x14, 0x5b, 0x4d, 0xb5, 0x91, 0xf4, 0xdf, 0x99, 0xfe, 0x0f, 0x89, 0xec, 0xff, 0x14, 0xc7, 0xfe, - 0x8f, 0x73, 0x84, 0x16, 0xf6, 0x8d, 0x65, 0x44, 0xc0, 0x15, 0x4d, 0x8a, 0xe3, 0x2b, 0x83, 0xb3, 0x44, 0x50, 0xa3, - 0x89, 0xc8, 0x6e, 0x3c, 0x51, 0x99, 0x11, 0x79, 0x6e, 0x15, 0x3c, 0x4b, 0x7b, 0x74, 0x4f, 0xee, 0xef, 0x9c, 0x40, - 0x94, 0x63, 0x52, 0x6d, 0x6f, 0xc6, 0x9c, 0xc3, 0x10, 0x17, 0x93, 0x8b, 0x0a, 0x60, 0x04, 0x37, 0x84, 0x6c, 0x2c, - 0x81, 0x8e, 0x92, 0xec, 0x47, 0x23, 0xe6, 0x00, 0x34, 0xc0, 0x7e, 0xd9, 0x20, 0xb0, 0x1c, 0xcc, 0x30, 0x43, 0x30, - 0x3a, 0xaf, 0x0e, 0xf0, 0x39, 0x66, 0x7b, 0xc7, 0x26, 0xf8, 0xb0, 0x32, 0x07, 0x3b, 0xd0, 0x20, 0x1c, 0x30, 0x8f, - 0x66, 0x79, 0x26, 0x28, 0x9a, 0xe0, 0x23, 0xea, 0x2c, 0xfb, 0x5c, 0xfc, 0x92, 0xa5, 0xad, 0xd7, 0x25, 0x87, 0x2e, - 0x16, 0x9f, 0x66, 0xd6, 0x50, 0xfa, 0x13, 0xf8, 0x5f, 0x83, 0x3b, 0xb0, 0xc7, 0x1d, 0x24, 0xc2, 0x56, 0x14, 0x4e, - 0xa5, 0xea, 0x9f, 0x5d, 0x06, 0x84, 0x92, 0x9e, 0x66, 0x48, 0xb9, 0x40, 0xeb, 0x1a, 0xe2, 0x1a, 0x6c, 0x0c, 0x86, - 0xed, 0x8c, 0x67, 0x9a, 0xdb, 0xd6, 0x33, 0xe3, 0xa4, 0x5e, 0xa3, 0x4d, 0x46, 0x87, 0x64, 0x5e, 0x44, 0x99, 0xbb, - 0xc8, 0x2f, 0x47, 0x4a, 0xad, 0xb9, 0x51, 0xac, 0x31, 0xe5, 0xa5, 0xd3, 0xec, 0xc3, 0x39, 0x82, 0xd3, 0x45, 0x50, - 0xf5, 0xc3, 0x1e, 0x9c, 0xb5, 0x31, 0xf8, 0x91, 0x62, 0x51, 0x20, 0xbd, 0x5d, 0x11, 0x52, 0xf3, 0x93, 0x1d, 0xab, - 0x59, 0x4c, 0x4b, 0x6f, 0x17, 0x1e, 0xce, 0xb7, 0xc5, 0x14, 0x64, 0x1c, 0x08, 0xf9, 0x23, 0x18, 0xd8, 0x14, 0x77, - 0x66, 0xa2, 0x2d, 0x82, 0xe0, 0x04, 0xa1, 0x8c, 0x48, 0x87, 0x6f, 0x45, 0x29, 0x2a, 0x02, 0x91, 0xfb, 0xd1, 0x7b, - 0x4c, 0xcb, 0x6a, 0x28, 0xad, 0x81, 0x3d, 0x2a, 0x2a, 0x06, 0xca, 0xeb, 0x4c, 0x68, 0x38, 0x45, 0x8b, 0x90, 0xa9, - 0x78, 0xb3, 0x00, 0x2b, 0x37, 0xf8, 0xa4, 0xc5, 0x4a, 0xcf, 0x58, 0xf6, 0x07, 0xf9, 0x4a, 0x59, 0xd1, 0x30, 0x81, - 0xee, 0x31, 0x55, 0xdb, 0x7f, 0xe3, 0xa2, 0x8d, 0xb9, 0x01, 0x7e, 0x06, 0x8c, 0xe2, 0x7a, 0x85, 0x09, 0xab, 0x15, - 0xdc, 0x01, 0x2c, 0xbd, 0x71, 0x6f, 0xd5, 0x4c, 0xc9, 0xa7, 0x5c, 0x49, 0x29, 0x13, 0xac, 0x77, 0x2a, 0x4b, 0x70, - 0xf2, 0x21, 0x04, 0x43, 0x7c, 0xf7, 0x65, 0xe6, 0xd7, 0x6b, 0x9e, 0xda, 0x84, 0x27, 0xd1, 0x9e, 0xf6, 0xd1, 0x37, - 0x6e, 0xc3, 0xab, 0x16, 0x43, 0x5c, 0x9d, 0x65, 0xe7, 0xe4, 0x4b, 0x64, 0x4d, 0xe5, 0x80, 0x1f, 0xb1, 0x1a, 0xea, - 0x12, 0xf8, 0x8c, 0x98, 0x37, 0x0c, 0xe6, 0x6f, 0x74, 0x8a, 0xc5, 0xbc, 0xa9, 0x22, 0xc5, 0xe1, 0xfe, 0x08, 0x8b, - 0x8c, 0x4b, 0x94, 0x23, 0x7d, 0xc8, 0xbf, 0x83, 0xc1, 0xa8, 0xd2, 0xcd, 0x8a, 0xfa, 0x9a, 0xf1, 0xbc, 0xed, 0x63, - 0x6b, 0x12, 0xb6, 0x00, 0x6b, 0x91, 0xf1, 0x04, 0xe8, 0xbe, 0x56, 0x6f, 0x0b, 0xd9, 0x9a, 0x68, 0x89, 0x86, 0xa6, - 0x50, 0xd4, 0x0d, 0x04, 0x13, 0x93, 0xd2, 0xee, 0xc0, 0x48, 0x82, 0xf1, 0xac, 0xa9, 0xfc, 0x82, 0xfc, 0xb8, 0x5e, - 0xc4, 0xd6, 0x98, 0x0b, 0x1d, 0x33, 0xa2, 0x49, 0x4d, 0x9a, 0x09, 0x88, 0x05, 0xf0, 0x72, 0x16, 0x3d, 0xac, 0xf3, - 0x84, 0x53, 0x73, 0xef, 0xd4, 0x11, 0x33, 0x30, 0x40, 0xa7, 0x10, 0xa9, 0x14, 0x3f, 0xbc, 0x0f, 0x52, 0x0a, 0x04, - 0xa0, 0xec, 0x98, 0x0d, 0x2d, 0x29, 0xa8, 0x4f, 0x6b, 0x97, 0x99, 0x5a, 0xdb, 0x1a, 0xfe, 0x94, 0xd9, 0xac, 0x45, - 0x55, 0xcc, 0x1f, 0x2e, 0xf3, 0x8b, 0x98, 0x71, 0xd1, 0xf0, 0x09, 0x43, 0xd5, 0x61, 0x05, 0x7a, 0x8f, 0x9b, 0xbc, - 0x5e, 0x99, 0xf0, 0x7e, 0x5e, 0xef, 0x9b, 0xfb, 0x22, 0x16, 0x5e, 0x14, 0x38, 0xf7, 0xa5, 0x82, 0x97, 0x86, 0x13, - 0xb8, 0xc4, 0x43, 0x99, 0xf9, 0x54, 0xb6, 0x95, 0x99, 0xa2, 0x04, 0x25, 0xb5, 0x88, 0x5c, 0x92, 0x0b, 0x82, 0x94, - 0x8a, 0x97, 0x81, 0x50, 0xdb, 0xb7, 0x0b, 0x90, 0xbd, 0xaf, 0x2d, 0xe3, 0xb5, 0x64, 0xe7, 0x22, 0x94, 0xcd, 0x66, - 0x5c, 0xdb, 0xcb, 0x69, 0xb7, 0xbf, 0x95, 0x41, 0x35, 0x00, 0x25, 0xb3, 0xe1, 0x32, 0xe2, 0x9b, 0x9b, 0x1d, 0x0a, - 0x08, 0xed, 0xfc, 0x6d, 0x57, 0xca, 0x2c, 0xcc, 0x41, 0xd7, 0xec, 0xa8, 0xf8, 0x17, 0xe5, 0xdd, 0x59, 0xed, 0x66, - 0x7c, 0xc9, 0x3b, 0x18, 0xdf, 0x14, 0xca, 0x01, 0x2e, 0x79, 0x21, 0xeb, 0x07, 0x6e, 0x94, 0xc8, 0x12, 0xc2, 0x32, - 0xbb, 0xa8, 0x15, 0x95, 0xc9, 0x98, 0x36, 0xbd, 0xad, 0xc8, 0x66, 0x23, 0x63, 0x5d, 0x96, 0x68, 0x79, 0x6e, 0xc5, - 0xc9, 0xab, 0x87, 0x3f, 0x52, 0xe5, 0xf4, 0x7d, 0x89, 0xfa, 0xd4, 0x07, 0x77, 0x6f, 0x2b, 0xb3, 0x84, 0x4f, 0x4d, - 0x13, 0x45, 0x70, 0x07, 0xcc, 0x55, 0xb6, 0x22, 0x6a, 0x61, 0x48, 0xfd, 0x17, 0x5e, 0xfa, 0xc6, 0x13, 0xaa, 0xb6, - 0x73, 0xd5, 0xab, 0x39, 0x24, 0x66, 0x8c, 0xe7, 0x68, 0xf5, 0x01, 0xb2, 0x81, 0xae, 0xcd, 0xbe, 0x02, 0x02, 0xaf, - 0x99, 0xfc, 0xf2, 0xdb, 0x61, 0xcc, 0xd9, 0x6d, 0x5e, 0x68, 0x64, 0x2b, 0x67, 0xd9, 0xc1, 0x8d, 0xb4, 0x55, 0x7b, - 0x3c, 0xd3, 0x4d, 0x5c, 0x69, 0x99, 0x8c, 0x31, 0xbf, 0xad, 0xea, 0xab, 0x05, 0xdf, 0x99, 0xdb, 0xce, 0x4a, 0xa6, - 0x91, 0xab, 0xd5, 0x57, 0x78, 0x5e, 0x34, 0x41, 0x27, 0x2e, 0x31, 0x53, 0xab, 0xea, 0xb7, 0xaa, 0x1c, 0x15, 0x15, - 0xcb, 0xb9, 0xd5, 0xa6, 0xca, 0x6b, 0xb7, 0xa8, 0xdf, 0x9f, 0x8d, 0x09, 0x41, 0x65, 0x8a, 0xcc, 0x58, 0xa2, 0x8b, - 0x78, 0xa1, 0x9f, 0xd3, 0xba, 0xa8, 0xe8, 0xf1, 0xca, 0xaa, 0x86, 0x18, 0x02, 0xb7, 0xda, 0xc9, 0x20, 0x65, 0x16, - 0x89, 0x79, 0x16, 0x31, 0xdb, 0xeb, 0xd1, 0xb6, 0x39, 0x1b, 0x06, 0x6e, 0xd2, 0x39, 0x81, 0x73, 0x12, 0xfe, 0xa6, - 0x32, 0xdb, 0xb0, 0xa2, 0x6e, 0x79, 0x8d, 0xae, 0xa2, 0x6e, 0xcd, 0x79, 0x3d, 0x55, 0xc5, 0x0f, 0xd4, 0x71, 0xb5, - 0x5e, 0xdd, 0x88, 0x0e, 0x41, 0x81, 0x0b, 0xeb, 0xe7, 0x00, 0xfe, 0x6f, 0xab, 0x18, 0x1e, 0xec, 0xaa, 0x0f, 0xd5, - 0xaa, 0x69, 0x63, 0xdf, 0x38, 0x20, 0xb0, 0x58, 0x15, 0x5c, 0xa0, 0x33, 0xac, 0x50, 0x2f, 0x33, 0x6d, 0x30, 0x4c, - 0x8c, 0x53, 0x4b, 0xcf, 0xa5, 0x13, 0x11, 0x7d, 0x1a, 0x5e, 0xcc, 0x34, 0x77, 0x68, 0xb3, 0x55, 0x6e, 0x11, 0x6a, - 0x47, 0xb0, 0x08, 0x26, 0xd3, 0x65, 0x1c, 0x8c, 0xfc, 0x36, 0xb4, 0xd1, 0x00, 0x13, 0x97, 0x36, 0x65, 0x21, 0xc4, - 0xca, 0x02, 0xaa, 0xf7, 0x5d, 0xb0, 0x40, 0x30, 0xb3, 0x27, 0xa4, 0x5f, 0x41, 0x54, 0xf9, 0x69, 0x7c, 0x3e, 0xa9, - 0xa4, 0xb6, 0x9d, 0xaf, 0x73, 0x0d, 0x1c, 0xaa, 0xa6, 0xa2, 0x2a, 0x57, 0xb6, 0x21, 0x72, 0x18, 0xe4, 0x3a, 0x52, - 0x42, 0x2d, 0x91, 0x2f, 0x33, 0x4a, 0x27, 0x13, 0x46, 0xcb, 0xb5, 0x29, 0x56, 0x81, 0xb4, 0xd6, 0x85, 0x77, 0xf9, - 0x1f, 0x86, 0xb2, 0x0d, 0x7a, 0x21, 0x4a, 0xe4, 0xa2, 0x67, 0xf1, 0xe7, 0x6b, 0x9d, 0x03, 0xa4, 0xfa, 0x5f, 0xad, - 0x5c, 0x2a, 0x63, 0x03, 0xa7, 0xd8, 0x95, 0x91, 0xf8, 0x20, 0xad, 0x25, 0xfa, 0x3e, 0xd7, 0x31, 0xea, 0xba, 0x07, - 0x8d, 0xeb, 0x55, 0xb1, 0x18, 0xfa, 0xc5, 0x7b, 0x12, 0xdd, 0xc2, 0x97, 0x05, 0x25, 0x1d, 0xf4, 0xd3, 0x5d, 0xdd, - 0x5f, 0xa7, 0x84, 0x44, 0x65, 0x31, 0x31, 0x84, 0x55, 0x7e, 0x66, 0x38, 0x6c, 0x15, 0xd1, 0xac, 0xb6, 0xeb, 0xec, - 0xfb, 0x03, 0x05, 0x13, 0x88, 0xa0, 0xb0, 0xf8, 0xff, 0x73, 0x1f, 0x14, 0x68, 0xe0, 0xa4, 0xce, 0x8d, 0x4a, 0x4e, - 0xfb, 0xb9, 0x76, 0x7d, 0xa8, 0xbc, 0x04, 0x40, 0x56, 0x8f, 0x37, 0xb2, 0xbe, 0xe5, 0x77, 0x2b, 0x8b, 0x17, 0x30, - 0x96, 0x3f, 0x85, 0x4d, 0x56, 0x23, 0xda, 0x8c, 0xae, 0xd5, 0x6c, 0x94, 0x4f, 0x99, 0x18, 0x8e, 0x36, 0xd0, 0xf5, - 0xbb, 0x6d, 0x6f, 0x50, 0x5d, 0x58, 0x4b, 0xec, 0x3e, 0x60, 0x5d, 0xa9, 0x80, 0xfd, 0xac, 0xa9, 0xa5, 0x3b, 0x15, - 0xfc, 0xfc, 0x56, 0x4f, 0xa5, 0x59, 0xd8, 0x3a, 0x02, 0x7a, 0xf6, 0xd5, 0x15, 0x07, 0xc0, 0x07, 0x74, 0x0d, 0x0b, - 0x3d, 0x76, 0x2c, 0xf5, 0x99, 0x45, 0x94, 0x7d, 0xe6, 0x1e, 0x5f, 0xdf, 0x0c, 0x85, 0x87, 0x9d, 0xdb, 0x6f, 0xbd, - 0x2a, 0xc6, 0xf1, 0xc2, 0xba, 0xba, 0xc8, 0x05, 0xc5, 0x13, 0x92, 0x9c, 0x5f, 0xce, 0x61, 0xa8, 0x5a, 0x49, 0xc4, - 0x5c, 0x85, 0x04, 0x41, 0xed, 0xbb, 0x22, 0xe0, 0x31, 0x39, 0x5e, 0x81, 0xbb, 0x07, 0xa6, 0xa8, 0x9b, 0x1d, 0x42, - 0xe8, 0x96, 0xb4, 0xba, 0x5b, 0x91, 0x00, 0xd0, 0x2e, 0xd8, 0xfe, 0xc6, 0x79, 0x89, 0x2d, 0x68, 0x19, 0xad, 0x17, - 0x21, 0x0c, 0x44, 0x22, 0x85, 0x31, 0x72, 0x7a, 0x38, 0x5b, 0xd7, 0xa0, 0x18, 0xfa, 0x53, 0x17, 0x38, 0xf5, 0xe3, - 0xd9, 0xb6, 0x4b, 0x85, 0x64, 0x22, 0xe8, 0xd0, 0x14, 0x58, 0x96, 0x9d, 0x38, 0xed, 0x91, 0xe4, 0xfd, 0x7d, 0x9e, - 0x92, 0x15, 0x15, 0x3f, 0x94, 0xc3, 0xcf, 0x27, 0x24, 0x38, 0xd4, 0x13, 0x30, 0x83, 0x0e, 0x78, 0xa6, 0xf7, 0xa9, - 0x13, 0x23, 0x95, 0xf5, 0x0e, 0x38, 0x8a, 0x88, 0x32, 0xd3, 0x25, 0xbb, 0xc7, 0xed, 0xf1, 0x14, 0x70, 0x23, 0x63, - 0xda, 0x65, 0x8e, 0x61, 0x26, 0x30, 0x8e, 0xf9, 0x6a, 0x7c, 0x3e, 0xa2, 0x1f, 0xc7, 0x7d, 0x44, 0xc9, 0x45, 0xa5, - 0x86, 0xc2, 0x36, 0x66, 0x8b, 0x5a, 0xf4, 0xd4, 0xbe, 0x91, 0x48, 0x47, 0xaf, 0x60, 0x2c, 0x17, 0x98, 0x06, 0x2b, - 0x9d, 0xf3, 0x8a, 0x82, 0x15, 0x4d, 0x80, 0xb8, 0x0a, 0xe3, 0x94, 0x51, 0x6b, 0xcc, 0x52, 0x18, 0x5c, 0x51, 0x93, - 0x11, 0xc1, 0xaf, 0x26, 0xf4, 0xec, 0x63, 0x31, 0xdc, 0x93, 0x97, 0xc3, 0xa1, 0x1e, 0xad, 0xa7, 0x75, 0xf7, 0x06, - 0x16, 0x63, 0xc1, 0xb2, 0xc0, 0x9c, 0x9d, 0x0e, 0x3a, 0xaf, 0xd8, 0xd6, 0xd4, 0x3a, 0x5d, 0xad, 0x1f, 0x5a, 0xd9, - 0x28, 0x96, 0xd3, 0x24, 0x92, 0x38, 0x6f, 0xa6, 0x51, 0x8c, 0x3f, 0x34, 0x5c, 0xea, 0x86, 0xfa, 0xc4, 0x6f, 0xcd, - 0x5f, 0x4b, 0xa5, 0xbf, 0xce, 0x3f, 0x8a, 0x85, 0x1d, 0x9b, 0xd8, 0x6f, 0xb4, 0x60, 0x65, 0xd1, 0xd8, 0x40, 0xa8, - 0xea, 0x0b, 0x9e, 0x25, 0x2b, 0x95, 0x27, 0xdf, 0x8d, 0xd8, 0xd2, 0x02, 0x3f, 0x8f, 0xf2, 0x6a, 0xea, 0xcd, 0x88, - 0x41, 0xb5, 0x7c, 0x8a, 0x6a, 0x77, 0x72, 0x20, 0x5c, 0x26, 0x37, 0x56, 0x95, 0x07, 0x88, 0x9e, 0x5f, 0x96, 0x1e, - 0x09, 0x73, 0xa9, 0x98, 0x92, 0x06, 0xcf, 0x89, 0xa0, 0xb7, 0x30, 0x85, 0x18, 0x1e, 0x49, 0xdf, 0xa0, 0xf2, 0xee, - 0x8f, 0xeb, 0x7d, 0xaf, 0x0b, 0xdc, 0x19, 0x3b, 0xdf, 0x74, 0x2f, 0x9d, 0x19, 0x34, 0x7a, 0xfd, 0x73, 0xa8, 0x5a, - 0x84, 0xc1, 0x4e, 0xd3, 0x85, 0xa0, 0x09, 0xea, 0x5f, 0x8c, 0x06, 0xd6, 0x32, 0x5d, 0xeb, 0xed, 0x20, 0x53, 0x21, - 0x31, 0xfe, 0x5f, 0x64, 0xbc, 0x0c, 0x98, 0x9c, 0x8c, 0xe2, 0x16, 0x3c, 0x00, 0x37, 0xd3, 0x90, 0x0b, 0x94, 0xd9, - 0xc3, 0x13, 0xa8, 0xc9, 0x58, 0x84, 0x67, 0x39, 0xe6, 0x3a, 0x75, 0x60, 0x3d, 0xb2, 0x79, 0x58, 0xa3, 0x70, 0xb5, - 0x9c, 0x4d, 0x4e, 0xc9, 0x8e, 0x59, 0x5d, 0xed, 0x63, 0x77, 0xc6, 0x25, 0x9e, 0x39, 0x1f, 0xf2, 0x6d, 0xec, 0x71, - 0xc0, 0x1c, 0x87, 0x07, 0x0e, 0x33, 0xe7, 0x21, 0x82, 0x5c, 0x37, 0xc0, 0x16, 0x60, 0xb2, 0x93, 0xb5, 0x6b, 0x94, - 0xb0, 0x78, 0x73, 0x03, 0x20, 0x8f, 0x64, 0x12, 0x42, 0x2e, 0x1b, 0x7e, 0x96, 0x5a, 0xaa, 0x8f, 0x80, 0x8f, 0xd5, - 0x87, 0x9a, 0x06, 0x42, 0xdc, 0x36, 0x42, 0x1e, 0x30, 0x26, 0xae, 0xcc, 0x2f, 0xaa, 0x09, 0x2e, 0xf8, 0x2f, 0x7b, - 0x1d, 0x7f, 0xdd, 0xac, 0xfb, 0x9e, 0x21, 0x22, 0x1d, 0xac, 0x45, 0x64, 0xbd, 0x22, 0x85, 0xff, 0x86, 0xdf, 0x03, - 0x45, 0x09, 0xc5, 0x52, 0xb1, 0xfc, 0x88, 0xea, 0x1e, 0xe3, 0x1e, 0xf2, 0xde, 0x4e, 0x7e, 0x1f, 0x09, 0x83, 0x1e, - 0x50, 0x63, 0x94, 0xa4, 0x38, 0x52, 0xab, 0x9e, 0x7b, 0x14, 0x2c, 0x85, 0xa5, 0x86, 0xe7, 0x88, 0xd2, 0xd5, 0xf7, - 0x0a, 0xb5, 0xf0, 0x1f, 0x2d, 0x6d, 0x9e, 0x86, 0x7d, 0x44, 0xf2, 0x4d, 0x46, 0xd7, 0xc8, 0x42, 0x25, 0x51, 0x78, - 0x23, 0x04, 0x9e, 0x73, 0xc6, 0x53, 0x7d, 0x84, 0x98, 0x07, 0xa2, 0xc9, 0xc8, 0xf5, 0x80, 0xde, 0xd1, 0xe6, 0xe8, - 0x45, 0x72, 0x4c, 0xdf, 0xb4, 0x0f, 0x83, 0xb0, 0x1d, 0x5b, 0x5c, 0x6a, 0x4c, 0x44, 0x6b, 0x5a, 0x75, 0xd9, 0x23, - 0x52, 0xef, 0x3c, 0x15, 0xa3, 0x04, 0x25, 0x72, 0x13, 0x15, 0xdd, 0x39, 0x4e, 0xed, 0xa2, 0xe8, 0xf6, 0x19, 0x4b, - 0xb8, 0x18, 0x55, 0x7c, 0x5f, 0x06, 0x9f, 0x44, 0x52, 0x34, 0x60, 0xd8, 0x80, 0xaf, 0xf7, 0xff, 0x60, 0xe8, 0x66, - 0x20, 0x97, 0xda, 0x30, 0x65, 0xf3, 0xe9, 0x9c, 0x66, 0x5b, 0xc3, 0x7d, 0x83, 0xd6, 0x97, 0x50, 0xcf, 0xb9, 0xcf, - 0x88, 0xdf, 0x4a, 0xcd, 0x2d, 0x26, 0xab, 0x36, 0x1b, 0x59, 0x4c, 0xd6, 0x61, 0xd5, 0x3d, 0x46, 0xa6, 0x10, 0xff, - 0x42, 0x93, 0x5c, 0x10, 0x1e, 0x55, 0xc9, 0x82, 0x7f, 0xd0, 0xcc, 0x60, 0xc3, 0x79, 0x9d, 0xfe, 0x8d, 0x32, 0x80, - 0xf7, 0x3b, 0xcb, 0x5a, 0x41, 0x35, 0x25, 0xb5, 0xe3, 0xba, 0x4b, 0xc7, 0x2f, 0x5d, 0xa0, 0x07, 0x99, 0x89, 0x67, - 0x47, 0x25, 0x21, 0x66, 0x81, 0x75, 0x2f, 0x91, 0xfa, 0xe6, 0x27, 0xe9, 0x91, 0x36, 0x78, 0xce, 0x42, 0xb4, 0xa0, - 0x17, 0x15, 0xfb, 0x7b, 0xa5, 0x34, 0xbd, 0x57, 0x76, 0x06, 0xaf, 0x8d, 0x99, 0xca, 0x41, 0xb0, 0xee, 0x12, 0x7a, - 0xb9, 0x3c, 0xc9, 0x8f, 0x6d, 0xe6, 0x92, 0xe6, 0x23, 0xe9, 0xef, 0xaa, 0x14, 0xeb, 0xc7, 0x80, 0xd6, 0xbf, 0xa6, - 0xcc, 0x92, 0x14, 0x68, 0x30, 0x58, 0x8d, 0x15, 0xdb, 0x04, 0x1c, 0x52, 0x68, 0x22, 0xa2, 0x89, 0x76, 0xdc, 0xee, - 0x68, 0x7c, 0x86, 0xd4, 0x47, 0xb8, 0x40, 0x32, 0xe0, 0x91, 0x43, 0x4c, 0x56, 0xc5, 0x2e, 0xc0, 0x17, 0xb8, 0x7d, - 0x3c, 0x83, 0x7e, 0xd8, 0x6e, 0xdd, 0x20, 0xe5, 0xa6, 0x1c, 0x17, 0x01, 0x6b, 0x08, 0x80, 0xa7, 0x5c, 0x13, 0xad, - 0x06, 0x52, 0x7d, 0x69, 0x04, 0xec, 0xbb, 0x83, 0xfa, 0x38, 0x9a, 0xa6, 0x8c, 0x65, 0xd3, 0xc4, 0x4b, 0xc9, 0x23, - 0xc4, 0x88, 0x7d, 0x85, 0x53, 0x8e, 0xc0, 0xbc, 0xc3, 0xef, 0xac, 0xd7, 0x42, 0x7a, 0x9b, 0xe8, 0x73, 0x93, 0x81, - 0x87, 0xe1, 0xc7, 0xd8, 0x7e, 0xd1, 0xd3, 0xce, 0xd6, 0x9c, 0xbf, 0x0a, 0x48, 0x46, 0x47, 0xe1, 0x5f, 0x85, 0x67, - 0xa5, 0x6d, 0x12, 0x42, 0xfc, 0x03, 0xd1, 0x75, 0x86, 0x33, 0x48, 0xb0, 0x48, 0x5f, 0x2e, 0x6a, 0x17, 0x39, 0x05, - 0x95, 0xf6, 0x99, 0xd5, 0xca, 0xb2, 0x7c, 0x7b, 0xfb, 0x8f, 0x73, 0x6b, 0x53, 0x8d, 0x0b, 0x1e, 0x72, 0x8d, 0xcb, - 0x56, 0x36, 0xfc, 0xa2, 0x8d, 0xf7, 0x96, 0x6c, 0x36, 0x72, 0xd5, 0x57, 0x2e, 0xe1, 0x9f, 0xf8, 0x51, 0x46, 0xb2, - 0xf1, 0x02, 0xac, 0x45, 0x6a, 0x79, 0xe4, 0xea, 0xa9, 0xed, 0x7b, 0xbd, 0x50, 0xac, 0x0c, 0xee, 0xfc, 0xe2, 0x38, - 0x41, 0x92, 0xca, 0x43, 0xfe, 0x9c, 0xaf, 0xe3, 0x1c, 0x3b, 0xab, 0xe9, 0x68, 0x45, 0xef, 0x48, 0x5d, 0x0e, 0x16, - 0x5b, 0x8e, 0x92, 0xf3, 0xc1, 0xb9, 0x6b, 0x86, 0x1e, 0x1c, 0x45, 0x3d, 0x9f, 0x29, 0x89, 0x05, 0x5c, 0x9a, 0xbb, - 0xa7, 0x08, 0x7a, 0x84, 0x48, 0x8c, 0xd0, 0xbb, 0xa0, 0x21, 0x18, 0xb6, 0x39, 0xdf, 0x64, 0x82, 0x36, 0x6b, 0xd1, - 0x2e, 0xe2, 0x17, 0xc3, 0xa2, 0xf0, 0xda, 0xbb, 0x7a, 0xe4, 0x8a, 0xe5, 0x12, 0x1a, 0x03, 0x59, 0x83, 0x62, 0xff, - 0x9a, 0x04, 0x3f, 0xe8, 0x9f, 0xad, 0x16, 0x1a, 0x2b, 0x53, 0xe6, 0xc7, 0x8c, 0x55, 0xea, 0x9c, 0xb5, 0xf4, 0x6e, - 0x6a, 0x17, 0x94, 0x9c, 0x6c, 0xe5, 0xfc, 0x5a, 0xcc, 0xbf, 0x1f, 0xaa, 0x9f, 0x66, 0xca, 0x3b, 0x84, 0x27, 0xcc, - 0xd7, 0x89, 0xa2, 0x80, 0xac, 0x9b, 0x25, 0xce, 0x52, 0x52, 0xc7, 0x2a, 0x89, 0x12, 0x63, 0x3b, 0x87, 0x47, 0x08, - 0x42, 0xd2, 0xd9, 0xac, 0xce, 0x8c, 0xc9, 0x95, 0x14, 0x6f, 0x87, 0x72, 0x25, 0x9c, 0xc5, 0x22, 0x4d, 0x50, 0x74, - 0xf9, 0x86, 0x5c, 0x2a, 0xf4, 0x54, 0x97, 0x76, 0x74, 0xa8, 0xf4, 0x80, 0x7f, 0x74, 0x73, 0x89, 0x99, 0x54, 0xa0, - 0x11, 0x9f, 0xc7, 0x96, 0x54, 0x22, 0x59, 0x15, 0x39, 0x0c, 0xd4, 0xca, 0xe4, 0x27, 0xdb, 0xe7, 0x32, 0x5a, 0x37, - 0x21, 0x70, 0xdd, 0xe6, 0x4a, 0xe2, 0xee, 0x5f, 0x26, 0xf3, 0x74, 0x00, 0xf6, 0xcb, 0x72, 0x9d, 0x37, 0x3a, 0xe1, - 0xf2, 0xe8, 0xec, 0x53, 0x13, 0xec, 0x78, 0x07, 0x2f, 0x26, 0x12, 0x04, 0x07, 0x89, 0x44, 0xa4, 0x82, 0x33, 0x90, - 0x78, 0x02, 0x03, 0x70, 0x72, 0xfe, 0x88, 0x9f, 0x17, 0x64, 0x79, 0x01, 0x5c, 0xe1, 0xa8, 0x02, 0x44, 0x82, 0x04, - 0x8d, 0x2e, 0xbc, 0x9b, 0x63, 0xf5, 0x9a, 0x2d, 0xb7, 0xab, 0xd2, 0x79, 0x50, 0x73, 0x24, 0x85, 0x92, 0x30, 0xe2, - 0x0c, 0x8b, 0x1f, 0x6c, 0x4a, 0x94, 0xaf, 0x1a, 0x81, 0x30, 0xb2, 0x58, 0xe2, 0x85, 0x46, 0x83, 0x00, 0x8f, 0x8f, - 0x90, 0x32, 0xd9, 0x36, 0xe3, 0x98, 0x7d, 0x4d, 0x89, 0x73, 0x86, 0xcc, 0x10, 0x4a, 0x06, 0xe6, 0x68, 0x09, 0x60, - 0x9d, 0xc5, 0x18, 0x4d, 0xa5, 0x29, 0x3e, 0x3f, 0x47, 0xad, 0xd6, 0x91, 0x57, 0x36, 0x43, 0xbd, 0x0d, 0x56, 0x4c, - 0x89, 0x00, 0xc7, 0x21, 0xa4, 0x97, 0xc0, 0x82, 0x32, 0xe6, 0xb6, 0x24, 0x97, 0x22, 0x3f, 0x24, 0x6b, 0x49, 0xd3, - 0x66, 0x60, 0x70, 0xae, 0x9b, 0x7c, 0x31, 0x1f, 0x8e, 0x92, 0x69, 0x15, 0x6c, 0x1a, 0xeb, 0xfe, 0x3d, 0x6c, 0x9a, - 0x6e, 0x72, 0xe5, 0xb6, 0x0a, 0xc6, 0x6a, 0xe6, 0x78, 0xc4, 0xe6, 0x6c, 0xc0, 0xcb, 0xef, 0x41, 0x1a, 0x2e, 0x1e, - 0x42, 0x64, 0xda, 0x4f, 0xfb, 0xa7, 0xd8, 0xbb, 0xe5, 0xf2, 0x64, 0x06, 0xce, 0xda, 0xd8, 0x1d, 0xa7, 0xa8, 0xae, - 0x25, 0x51, 0x2e, 0x09, 0x9b, 0x0f, 0x80, 0xa1, 0xd6, 0xf7, 0xa2, 0xec, 0xff, 0x2e, 0xe9, 0x89, 0xa2, 0xc2, 0x73, - 0x9d, 0xd7, 0x67, 0xa9, 0x3f, 0x80, 0x76, 0x1f, 0xc7, 0xe4, 0xce, 0x38, 0xcc, 0x11, 0x50, 0x99, 0xbd, 0x5f, 0xbf, - 0xa2, 0x83, 0xb6, 0x95, 0xea, 0x4f, 0x28, 0xce, 0x1f, 0x94, 0xd1, 0x3a, 0x5b, 0xe6, 0xfc, 0x6c, 0xc1, 0x40, 0x67, - 0x92, 0x96, 0x92, 0xca, 0x27, 0xfe, 0x87, 0xea, 0xf0, 0x31, 0xb5, 0x47, 0x8c, 0x4d, 0x24, 0x09, 0x7e, 0x92, 0x27, - 0x7c, 0x4c, 0x35, 0x13, 0xb5, 0x3d, 0x43, 0x8a, 0x7a, 0x63, 0x44, 0xa6, 0x52, 0x4d, 0x96, 0x15, 0x9b, 0x58, 0xe4, - 0x04, 0xbb, 0xba, 0xb0, 0x2e, 0x7d, 0xa2, 0x3e, 0xa5, 0xa6, 0xe6, 0x20, 0x5c, 0x18, 0x48, 0x77, 0xdb, 0x55, 0x8f, - 0x16, 0x4b, 0x5a, 0x28, 0x52, 0x12, 0x39, 0x89, 0xa4, 0x69, 0x1c, 0xa9, 0x0b, 0x60, 0x9e, 0xa3, 0xec, 0x56, 0xb2, - 0x06, 0x6b, 0x6b, 0x3c, 0x51, 0x27, 0xd5, 0x91, 0x9b, 0x3a, 0xcc, 0xac, 0xa7, 0x9a, 0xf9, 0x3f, 0x3b, 0x26, 0x52, - 0xd0, 0x71, 0xe5, 0x91, 0x27, 0x94, 0xe6, 0xcd, 0x54, 0xed, 0x64, 0x48, 0x8f, 0x3c, 0xd7, 0xdb, 0xa4, 0x63, 0x9f, - 0x0b, 0x25, 0x0e, 0xdd, 0x74, 0x39, 0xd5, 0x25, 0xf0, 0xf1, 0x55, 0xfc, 0x91, 0x50, 0x2c, 0x49, 0xa4, 0x61, 0xee, - 0x6c, 0x34, 0xb6, 0x34, 0x56, 0x97, 0x8a, 0x2c, 0x0e, 0x4b, 0x43, 0xd5, 0x55, 0x94, 0xc5, 0x1b, 0x35, 0xd8, 0x44, - 0xd4, 0x45, 0x7d, 0x0d, 0x3a, 0xa5, 0xf3, 0x1c, 0x68, 0xa9, 0xc5, 0xdd, 0x53, 0x41, 0xef, 0x27, 0x5c, 0x53, 0x01, - 0x0e, 0x26, 0x1e, 0xb9, 0x78, 0xc1, 0xe9, 0x32, 0xa7, 0x2b, 0xd8, 0x5f, 0x34, 0x52, 0x8d, 0x33, 0x0b, 0x55, 0x39, - 0x33, 0xaa, 0xda, 0x99, 0x75, 0xd3, 0x0d, 0x86, 0x39, 0x57, 0xbb, 0x1c, 0x59, 0x94, 0x25, 0x59, 0x1c, 0x57, 0xa6, - 0x89, 0xe7, 0xf6, 0xca, 0x65, 0xa4, 0xf3, 0x4a, 0xb6, 0x98, 0x8c, 0x89, 0x4b, 0x3d, 0x32, 0x3d, 0x31, 0xb2, 0x6c, - 0xa0, 0xb6, 0x4b, 0xaf, 0xa9, 0x3a, 0xf6, 0x8b, 0x3b, 0x16, 0x85, 0x97, 0xb9, 0xc6, 0xf4, 0x38, 0x09, 0x19, 0xf2, - 0xa5, 0x35, 0x50, 0x62, 0x1b, 0xfc, 0x58, 0x8e, 0xf6, 0xd3, 0x69, 0x09, 0xac, 0x49, 0x44, 0xa0, 0xea, 0xb5, 0x81, - 0xfc, 0xb8, 0x4d, 0x0f, 0xe9, 0xcb, 0x16, 0x2e, 0xca, 0x1f, 0xca, 0x61, 0x73, 0xe0, 0x10, 0x66, 0x02, 0xa3, 0x60, - 0xa1, 0xbc, 0x92, 0xc0, 0x26, 0xf0, 0x3b, 0x46, 0xcd, 0x76, 0xbb, 0xd2, 0xfb, 0x00, 0x32, 0x19, 0x37, 0x21, 0x3c, - 0x80, 0xc2, 0xeb, 0x29, 0x28, 0x57, 0x88, 0x03, 0xcd, 0x14, 0xa0, 0xc3, 0x0f, 0xe9, 0xc3, 0x13, 0x90, 0x1f, 0xd3, - 0xe1, 0x47, 0xb7, 0x72, 0x1b, 0x6d, 0x73, 0x2c, 0x4f, 0x95, 0x87, 0x6a, 0x1c, 0x21, 0x4a, 0x72, 0x61, 0xb1, 0xa8, - 0xdb, 0x2b, 0x57, 0xb4, 0xbd, 0xf1, 0x5e, 0xdf, 0xb0, 0x4d, 0x3a, 0xfe, 0x18, 0xe6, 0xb8, 0xc2, 0xa8, 0x46, 0x15, - 0x6c, 0xe9, 0x1d, 0xb0, 0xd5, 0x5d, 0x25, 0xb0, 0xc7, 0xa6, 0xb1, 0xb9, 0x00, 0x1d, 0x1a, 0xa2, 0x0c, 0xa4, 0x54, - 0x35, 0x0b, 0x64, 0x72, 0xf5, 0x29, 0xec, 0xb6, 0xa6, 0x31, 0x9b, 0x90, 0xf7, 0xbf, 0xa1, 0x79, 0x15, 0x96, 0x7c, - 0xc2, 0xfe, 0x10, 0xc9, 0x67, 0xf8, 0xd2, 0x47, 0x8d, 0xf8, 0x1e, 0xe0, 0x6a, 0x5f, 0x0a, 0x45, 0xa6, 0x38, 0xb6, - 0xc7, 0x6b, 0xd4, 0x26, 0xf3, 0xf0, 0x50, 0x47, 0x17, 0x36, 0xe4, 0x47, 0x38, 0x61, 0xfb, 0x31, 0xce, 0x93, 0x0b, - 0x8c, 0xe8, 0xbb, 0x18, 0x37, 0x07, 0xe8, 0xca, 0x00, 0xbc, 0x2d, 0xa3, 0x5e, 0x2a, 0x1f, 0xec, 0xf0, 0x16, 0x75, - 0xb3, 0xe3, 0x34, 0xd8, 0x66, 0xc4, 0xa1, 0x1c, 0x0a, 0x70, 0x97, 0xbd, 0xaa, 0x52, 0xe4, 0xf4, 0xd6, 0xf4, 0x8e, - 0xeb, 0x0d, 0xf2, 0x45, 0x13, 0x4d, 0x1d, 0x4c, 0x7a, 0x00, 0x13, 0x6a, 0x39, 0xa0, 0x31, 0x7a, 0xb5, 0x25, 0x5b, - 0x5c, 0x0b, 0x9e, 0xd9, 0x02, 0xd2, 0xbc, 0x22, 0xd5, 0x6e, 0x94, 0x46, 0x53, 0x32, 0x34, 0x69, 0x13, 0x8b, 0xd4, - 0x40, 0x32, 0xab, 0x57, 0x75, 0x22, 0x55, 0x83, 0x2d, 0x70, 0x60, 0xb3, 0x20, 0xc3, 0x37, 0xfb, 0x93, 0x01, 0x63, - 0x4b, 0xaf, 0x7d, 0x4f, 0x76, 0x1f, 0x35, 0xe4, 0x1a, 0x12, 0x19, 0xc7, 0x39, 0xd1, 0x6c, 0xaa, 0x88, 0xf8, 0x64, - 0x1d, 0x15, 0xb0, 0x36, 0x97, 0x6d, 0xe5, 0x83, 0x0a, 0x7a, 0x6c, 0xb0, 0xc9, 0x06, 0xd7, 0x8e, 0x19, 0xd6, 0x17, - 0x9b, 0x8a, 0xd4, 0x65, 0xfa, 0x40, 0xc4, 0x34, 0x83, 0x44, 0xa8, 0x3b, 0x36, 0xbe, 0xae, 0x4a, 0xbb, 0x20, 0xec, - 0xfb, 0xab, 0x74, 0xae, 0x61, 0xe3, 0x15, 0x90, 0xb9, 0xbe, 0xea, 0x00, 0x03, 0xbc, 0x02, 0x71, 0x31, 0xe0, 0xe3, - 0x2d, 0x90, 0x29, 0xf9, 0x77, 0xd4, 0x73, 0xa3, 0x94, 0x47, 0x2d, 0xef, 0x86, 0x19, 0xde, 0x6a, 0x2f, 0xf3, 0x0f, - 0x4b, 0x1f, 0xf2, 0x21, 0x41, 0x85, 0x2c, 0xa4, 0xe6, 0x49, 0xd4, 0xcd, 0x1d, 0x88, 0x6d, 0xdd, 0xe7, 0x22, 0xa3, - 0x58, 0xc4, 0xca, 0x23, 0xc0, 0x5d, 0x04, 0xbc, 0xdb, 0xac, 0x94, 0x88, 0x6f, 0x0e, 0xa5, 0x55, 0xde, 0x12, 0xcd, - 0x55, 0x60, 0xde, 0x45, 0x2b, 0x3b, 0x9c, 0xea, 0x90, 0x81, 0x9d, 0x4a, 0xed, 0x94, 0x44, 0xef, 0xb1, 0xc2, 0x5e, - 0xb3, 0x8d, 0xf5, 0x5b, 0x3b, 0xfa, 0x10, 0x56, 0x0b, 0x56, 0xdb, 0x73, 0x85, 0xe6, 0x66, 0xa2, 0x80, 0x58, 0x60, - 0xcd, 0xde, 0xbe, 0x49, 0x14, 0x42, 0xe1, 0x82, 0xcb, 0x52, 0x2a, 0x91, 0x62, 0xdb, 0x01, 0x83, 0x44, 0x13, 0x26, - 0xaa, 0x8a, 0x73, 0x23, 0xf6, 0x3c, 0x6b, 0xb0, 0xc0, 0xb3, 0x92, 0x0c, 0x8a, 0xd0, 0xba, 0xad, 0x76, 0xa9, 0x40, - 0xef, 0x67, 0x81, 0x95, 0x53, 0xe5, 0x04, 0x0e, 0xa9, 0x46, 0x1c, 0x9f, 0x05, 0x23, 0xb7, 0x4a, 0xf9, 0x16, 0x61, - 0xce, 0xe0, 0xcc, 0x7f, 0x5d, 0x7a, 0x6d, 0x7a, 0x52, 0x0a, 0x0e, 0xd3, 0xf7, 0xe7, 0x30, 0xe9, 0x15, 0x18, 0x9d, - 0xf7, 0x9e, 0xf7, 0x4a, 0x16, 0xf8, 0xeb, 0xb5, 0xbe, 0x14, 0x45, 0xb3, 0x29, 0x4f, 0x3d, 0xb9, 0x94, 0xf9, 0x96, - 0x06, 0xae, 0x53, 0x1c, 0xad, 0xee, 0xd9, 0x9b, 0x15, 0x30, 0x13, 0xcb, 0xf0, 0xef, 0xc1, 0xd6, 0xbe, 0x81, 0xf3, - 0x62, 0x09, 0x44, 0x7e, 0x63, 0x7c, 0x7d, 0xc8, 0xd3, 0xe2, 0x85, 0xcf, 0xcf, 0x08, 0x0b, 0x15, 0xe6, 0x8a, 0x84, - 0xc7, 0xa7, 0x4a, 0xab, 0x2c, 0x41, 0xc3, 0xb4, 0x7c, 0xa6, 0xc7, 0x8b, 0xfa, 0x56, 0x39, 0x7a, 0xeb, 0x2f, 0xb3, - 0xd2, 0x98, 0xcf, 0xcf, 0x93, 0x47, 0x4a, 0xbc, 0x7e, 0xf2, 0x89, 0xaa, 0xf2, 0x67, 0xc3, 0x6d, 0xbd, 0xef, 0xe1, - 0xd7, 0xde, 0x7e, 0x90, 0x65, 0x81, 0xc8, 0xaa, 0x71, 0x6d, 0xe3, 0xa8, 0xf2, 0x34, 0xed, 0x85, 0x58, 0x28, 0xc2, - 0x0f, 0x8a, 0x0e, 0x2c, 0x2f, 0x73, 0xa9, 0xe6, 0x5c, 0x85, 0x8a, 0x46, 0xec, 0x69, 0xfc, 0x7c, 0x08, 0x4b, 0x61, - 0x2a, 0x32, 0x08, 0xe3, 0x0e, 0xed, 0x92, 0x64, 0xec, 0x96, 0x32, 0x6d, 0xeb, 0x77, 0x0b, 0xe5, 0x35, 0x15, 0x13, - 0x30, 0x85, 0x77, 0x20, 0xb9, 0x99, 0x2d, 0xb6, 0xd6, 0x39, 0xa9, 0xa3, 0x02, 0xfb, 0x31, 0xa6, 0xc0, 0x61, 0xa7, - 0x9b, 0xe9, 0x73, 0x81, 0x1b, 0x9a, 0xf2, 0x50, 0x6f, 0x3a, 0xc3, 0x95, 0xd7, 0xf1, 0x43, 0x75, 0xe6, 0x6c, 0x81, - 0x96, 0x6b, 0xe4, 0x2b, 0xbe, 0xaa, 0x96, 0x20, 0xf2, 0x40, 0xf2, 0xb7, 0xaf, 0xbe, 0x7b, 0xab, 0x4b, 0x45, 0xd2, - 0x19, 0x6e, 0xd5, 0xb0, 0x3b, 0x58, 0xe8, 0x6e, 0x75, 0x26, 0x91, 0x04, 0x1a, 0x90, 0x5d, 0x1b, 0xde, 0x0b, 0x1b, - 0xe8, 0x4e, 0x3b, 0x5c, 0x3b, 0xa9, 0x22, 0x68, 0x9d, 0x5d, 0x65, 0x0c, 0x6d, 0xd9, 0x45, 0xc4, 0x2d, 0xbb, 0x0e, - 0x47, 0xd1, 0xcd, 0xb4, 0x10, 0xd6, 0xc6, 0xe3, 0x1e, 0x54, 0x6f, 0x33, 0x20, 0x25, 0x22, 0x12, 0x28, 0x17, 0xe2, - 0x6f, 0x5d, 0xa8, 0x59, 0xc6, 0xdd, 0xa6, 0x43, 0xec, 0x26, 0x89, 0xeb, 0x83, 0x66, 0xf0, 0xd6, 0xa5, 0x95, 0xd7, - 0x19, 0x52, 0xf8, 0x48, 0x45, 0x06, 0xce, 0x0d, 0x53, 0x7b, 0xda, 0x65, 0x1d, 0xc6, 0xbc, 0x54, 0xca, 0xaa, 0x08, - 0xb8, 0xd5, 0x00, 0xcf, 0xda, 0xb7, 0x70, 0x4c, 0x13, 0x1b, 0x9a, 0xa7, 0xbe, 0xcf, 0xd1, 0x76, 0x37, 0x5e, 0xb4, - 0xe2, 0xab, 0xd7, 0xd6, 0x71, 0xd9, 0x3c, 0xeb, 0x6e, 0x13, 0x56, 0xb1, 0x9f, 0x22, 0x29, 0x6c, 0x1a, 0x8b, 0xb9, - 0x26, 0x71, 0x4c, 0x02, 0xa3, 0x05, 0xb0, 0x37, 0xd1, 0x2c, 0xbb, 0x58, 0x22, 0xb5, 0x75, 0x58, 0x77, 0x73, 0xc0, - 0xe1, 0xdb, 0xce, 0x57, 0xaa, 0x76, 0x53, 0x83, 0x12, 0x39, 0xe7, 0xc3, 0xfe, 0xc2, 0xed, 0xfe, 0x50, 0xe2, 0x4d, - 0xdd, 0xc6, 0xe2, 0x48, 0x34, 0x16, 0x10, 0x5c, 0x66, 0x8c, 0xda, 0x2c, 0x8b, 0x10, 0x9d, 0x5a, 0x59, 0xff, 0x40, - 0x05, 0x48, 0x25, 0xd4, 0x6a, 0x71, 0x33, 0x81, 0x05, 0xc7, 0xa4, 0xd4, 0xc6, 0xe1, 0xc7, 0x3f, 0x89, 0xa7, 0x54, - 0xb4, 0x69, 0xd4, 0x13, 0xcd, 0x05, 0xfb, 0x72, 0x88, 0x46, 0x20, 0x77, 0x1b, 0xd6, 0x38, 0xf5, 0xa2, 0xb3, 0xb9, - 0x51, 0xe8, 0xb0, 0x32, 0x55, 0x60, 0xfc, 0xad, 0xc2, 0x6c, 0x20, 0xe7, 0x2a, 0x89, 0x95, 0xdb, 0x19, 0x78, 0x61, - 0x84, 0x0e, 0x62, 0x00, 0xbd, 0x9d, 0xfc, 0x54, 0x7f, 0x5a, 0x5d, 0x94, 0x71, 0x22, 0x4c, 0x4e, 0xdf, 0xdb, 0xc1, - 0x83, 0xda, 0x6c, 0xe7, 0x52, 0xbc, 0xe6, 0x39, 0x81, 0xf6, 0x95, 0x9f, 0xfd, 0x5e, 0xbf, 0x3f, 0x71, 0x13, 0x5a, - 0x56, 0x20, 0x75, 0x8a, 0x7f, 0xd5, 0x9d, 0x51, 0xee, 0x76, 0xce, 0xb3, 0x09, 0xcb, 0x63, 0x92, 0xec, 0x1b, 0x7f, - 0x5b, 0xb8, 0xe4, 0x68, 0xc9, 0x9f, 0x38, 0x0f, 0x8c, 0x62, 0x31, 0x4d, 0x16, 0xa6, 0x7e, 0x4d, 0xd2, 0xde, 0xc4, - 0x75, 0x6c, 0xc4, 0xf1, 0x9f, 0xe3, 0x10, 0x3d, 0x49, 0x84, 0xd4, 0x7a, 0x4b, 0x51, 0x3d, 0xaa, 0x7b, 0x27, 0xea, - 0x42, 0x36, 0x0f, 0x39, 0x5a, 0x93, 0x31, 0x9d, 0xd4, 0x5d, 0xaa, 0x91, 0x68, 0x04, 0x4b, 0xb7, 0x76, 0x3b, 0x39, - 0x3c, 0xc4, 0x4b, 0xfb, 0x28, 0x12, 0x15, 0xbd, 0x0b, 0x4d, 0x71, 0x68, 0xa3, 0x58, 0x5f, 0x59, 0x89, 0x05, 0xd6, - 0x5e, 0x2a, 0xad, 0xe6, 0x82, 0xae, 0xbc, 0x1c, 0x15, 0x3a, 0x27, 0x00, 0x5b, 0xcb, 0x39, 0x91, 0x01, 0x74, 0x62, - 0xb1, 0x70, 0x1d, 0x72, 0x03, 0xca, 0x10, 0xa1, 0x72, 0x4b, 0x8f, 0x53, 0x69, 0xd5, 0x28, 0x96, 0x80, 0xc4, 0x70, - 0xc6, 0x7c, 0xeb, 0x63, 0x36, 0x6e, 0x64, 0x0c, 0xae, 0x5a, 0x76, 0x6d, 0x19, 0x6b, 0x6b, 0x85, 0xb4, 0x0e, 0x98, - 0x5a, 0x65, 0x3f, 0x35, 0xbe, 0xf3, 0xe7, 0xbd, 0x23, 0x4d, 0x6f, 0x71, 0x24, 0x11, 0x06, 0x6d, 0xaf, 0x18, 0x0b, - 0x53, 0x84, 0xdb, 0xec, 0xf6, 0x8a, 0xd0, 0xdd, 0x5f, 0x0a, 0x7c, 0x5b, 0xb8, 0x31, 0x15, 0x37, 0x8e, 0x1e, 0x5f, - 0x14, 0x4c, 0x04, 0xe3, 0xd0, 0x54, 0x95, 0xf0, 0x6e, 0xba, 0x0a, 0x0a, 0x72, 0x2a, 0x2a, 0x1c, 0x78, 0xb0, 0x5e, - 0x66, 0xf4, 0x28, 0x12, 0x8a, 0x2d, 0xae, 0x6a, 0x4d, 0x14, 0x77, 0x19, 0x17, 0xa4, 0x2f, 0x87, 0xf9, 0xb7, 0xaa, - 0x1b, 0xba, 0x66, 0x55, 0xba, 0x45, 0xe2, 0x6b, 0x53, 0x8d, 0x46, 0x44, 0xe5, 0x7b, 0xe9, 0x03, 0xf3, 0x58, 0x4b, - 0x77, 0x3e, 0xed, 0x13, 0xae, 0x0c, 0x1c, 0x18, 0xfa, 0x48, 0xf7, 0x57, 0xeb, 0xea, 0x24, 0x1f, 0x57, 0x9f, 0x7c, - 0x0d, 0xcf, 0x1f, 0x8c, 0x9d, 0x76, 0x70, 0xcb, 0x21, 0x7d, 0xcc, 0xf9, 0x35, 0x33, 0xbd, 0x45, 0xab, 0xbd, 0x51, - 0xa3, 0x2e, 0xb0, 0x99, 0x4c, 0xd3, 0x63, 0xfe, 0xe9, 0xad, 0xe8, 0xc1, 0x05, 0x27, 0x89, 0x2f, 0x20, 0xe1, 0x86, - 0xed, 0xd5, 0xc7, 0x47, 0xaa, 0xeb, 0xd6, 0x09, 0x25, 0x76, 0x23, 0x95, 0xed, 0xa0, 0x42, 0xc8, 0x0e, 0xf7, 0xc4, - 0xd5, 0x1b, 0xec, 0x73, 0x88, 0xd3, 0xd1, 0x00, 0x29, 0x93, 0x26, 0xf6, 0x25, 0x8c, 0xf7, 0xc5, 0x1c, 0x36, 0x2b, - 0x48, 0xbc, 0xea, 0xc2, 0x29, 0x94, 0x58, 0xd9, 0xf3, 0xea, 0x78, 0x1d, 0xdd, 0xe4, 0x23, 0x9a, 0x32, 0xd0, 0xdc, - 0xbd, 0xe5, 0x2e, 0x17, 0x18, 0xdd, 0x6b, 0xf3, 0xf1, 0xdd, 0xce, 0x68, 0x4d, 0x12, 0xf9, 0xc6, 0xb7, 0xf9, 0x70, - 0xf3, 0xf8, 0x85, 0x86, 0xe2, 0x00, 0xd7, 0x71, 0xb8, 0xfd, 0xe1, 0xb2, 0x2a, 0xf7, 0xaa, 0x1f, 0xd4, 0xc0, 0x91, - 0x52, 0x4f, 0x0d, 0x67, 0x61, 0x4f, 0x30, 0xe1, 0xd4, 0x81, 0xb3, 0xe6, 0x83, 0x90, 0x73, 0xf9, 0xd7, 0x2e, 0x94, - 0x73, 0x37, 0x6e, 0x16, 0x9e, 0x06, 0x36, 0x76, 0x86, 0x3a, 0x5c, 0xea, 0xce, 0x6c, 0x49, 0x3c, 0xc3, 0x8f, 0xbb, - 0x9a, 0x08, 0x4b, 0xed, 0x81, 0xaf, 0x57, 0xbc, 0x9c, 0xfb, 0xdb, 0xe1, 0x8d, 0xe2, 0x82, 0x30, 0x33, 0xbe, 0x88, - 0x4a, 0x93, 0x2c, 0x69, 0xf8, 0x36, 0xb2, 0x19, 0x75, 0xed, 0xbb, 0x68, 0x45, 0x50, 0x32, 0x22, 0x54, 0x71, 0x68, - 0xc6, 0x50, 0x06, 0xe8, 0x58, 0x45, 0x69, 0xcf, 0xd7, 0x06, 0xb3, 0x4e, 0x36, 0x73, 0x04, 0x74, 0x44, 0xdf, 0x2f, - 0x5f, 0xd4, 0xb3, 0x7f, 0xdd, 0x1f, 0x1e, 0xbe, 0xc8, 0x55, 0x7d, 0xd4, 0x00, 0xfc, 0x8e, 0x54, 0xf5, 0xe8, 0x8d, - 0xe5, 0x57, 0x5a, 0x82, 0xad, 0x66, 0x07, 0x46, 0x9d, 0xa4, 0x8d, 0xd4, 0x86, 0xcc, 0x32, 0x67, 0x52, 0x28, 0x04, - 0x2d, 0x3d, 0x9e, 0x63, 0x31, 0x05, 0x50, 0xd2, 0xe5, 0x8a, 0x88, 0x0b, 0x06, 0x61, 0x15, 0x87, 0x31, 0x2c, 0xa4, - 0x69, 0x3d, 0xdb, 0xce, 0xa2, 0x51, 0x83, 0xd0, 0x35, 0x86, 0x44, 0x85, 0x99, 0xf5, 0x8c, 0x83, 0x5c, 0x6a, 0xbb, - 0x20, 0x4f, 0x7e, 0x73, 0x15, 0x03, 0xd0, 0x63, 0x22, 0x79, 0x5a, 0xd5, 0x44, 0x96, 0x90, 0xcf, 0xa4, 0x61, 0xd3, - 0xfb, 0xc3, 0x37, 0x31, 0x3d, 0xfa, 0xd8, 0xd5, 0xd6, 0x1f, 0xa2, 0xe4, 0xb9, 0x17, 0x29, 0x5f, 0xeb, 0xb4, 0x65, - 0x77, 0xa2, 0x4d, 0xd0, 0x44, 0xdb, 0x82, 0xb0, 0x05, 0x3a, 0xd0, 0x67, 0x3c, 0x1a, 0x2e, 0x1b, 0x51, 0x16, 0x8b, - 0x94, 0x28, 0x87, 0xfc, 0xe6, 0xec, 0x11, 0xe2, 0xb4, 0x16, 0x46, 0x03, 0xcb, 0xbd, 0x60, 0x18, 0x45, 0x17, 0xec, - 0x81, 0x4f, 0x2b, 0x52, 0x5c, 0x7d, 0xbb, 0x58, 0xf3, 0xba, 0x32, 0xd1, 0x36, 0x98, 0xf0, 0x0e, 0x1a, 0x1e, 0x61, - 0xaf, 0x71, 0x4e, 0x83, 0xae, 0xeb, 0xc9, 0xd3, 0x8a, 0x8c, 0x4d, 0x65, 0xa5, 0x1e, 0x01, 0xb7, 0xd8, 0xdb, 0x7e, - 0xd4, 0x36, 0x07, 0x7b, 0x36, 0xb6, 0xea, 0xc6, 0xb6, 0xc7, 0x10, 0xdc, 0x3a, 0x41, 0x3e, 0xdd, 0x29, 0x7d, 0x9e, - 0xe7, 0x4b, 0x6b, 0x13, 0xe8, 0xf0, 0xba, 0x35, 0xed, 0x71, 0x84, 0x11, 0x11, 0xb7, 0x99, 0x2e, 0x58, 0x58, 0x4a, - 0x6f, 0xa9, 0xae, 0x88, 0xe1, 0xd9, 0x0e, 0x59, 0x0d, 0x40, 0x2f, 0xb0, 0x3f, 0x94, 0xe7, 0x25, 0x5c, 0xe8, 0xf9, - 0xf0, 0xd1, 0x45, 0x95, 0x97, 0xa5, 0x1d, 0x66, 0x7b, 0xd6, 0xdd, 0xa0, 0xc2, 0xf5, 0xa9, 0x5a, 0x9d, 0x50, 0x20, - 0x96, 0x9e, 0xa3, 0xbf, 0xef, 0x53, 0x47, 0x3c, 0xcf, 0x08, 0x61, 0x27, 0x36, 0x9b, 0x07, 0x20, 0xf6, 0x41, 0xc7, - 0x04, 0x01, 0x42, 0xd0, 0x10, 0xab, 0x3d, 0xa0, 0x1e, 0xbf, 0x33, 0xf4, 0x7d, 0x44, 0x7a, 0x13, 0xa0, 0x32, 0x05, - 0xc5, 0x89, 0xda, 0xa7, 0x24, 0x22, 0x27, 0x3f, 0xc9, 0x2e, 0x9b, 0xb1, 0xa8, 0x93, 0xc0, 0xf9, 0x88, 0x53, 0xb0, - 0x14, 0x0a, 0xe7, 0xc5, 0x33, 0x01, 0x7c, 0x3a, 0x67, 0x8b, 0x69, 0xe1, 0x8b, 0x1c, 0x94, 0xcd, 0xa4, 0xc7, 0xf5, - 0x38, 0xb7, 0x7d, 0x4c, 0x38, 0x2a, 0xca, 0xd8, 0xd8, 0x5b, 0x75, 0x66, 0x8c, 0xf0, 0xd5, 0x44, 0xa8, 0xf7, 0x63, - 0x9c, 0xb7, 0xf7, 0x3d, 0x3e, 0xe2, 0x52, 0x6c, 0x2a, 0x84, 0xc2, 0x82, 0x9a, 0xa7, 0xf4, 0x47, 0x59, 0xe7, 0xd4, - 0xc8, 0x82, 0xf2, 0xb8, 0x82, 0x91, 0xa2, 0x4c, 0xfb, 0xec, 0xc9, 0x9e, 0x32, 0x48, 0x6c, 0xe7, 0x65, 0xa2, 0x2b, - 0x05, 0x0c, 0xa2, 0x54, 0x0a, 0x76, 0xb5, 0x2d, 0x14, 0xc9, 0x20, 0x1c, 0x43, 0xbb, 0x11, 0x47, 0x55, 0xe6, 0x90, - 0x84, 0x7c, 0xcd, 0xd7, 0x38, 0xb3, 0xdd, 0x1c, 0x52, 0x18, 0x6c, 0x51, 0x4d, 0x46, 0x81, 0xd0, 0x6e, 0x41, 0x40, - 0xe8, 0xd2, 0x85, 0xdf, 0xe0, 0x97, 0x47, 0xa9, 0x6c, 0x26, 0x38, 0x4f, 0x17, 0x6e, 0xe1, 0x97, 0x1e, 0xb5, 0x62, - 0xc7, 0x5b, 0x6b, 0xe3, 0x12, 0xe5, 0xa2, 0x65, 0xfe, 0x23, 0xf6, 0xb8, 0x80, 0x03, 0x5b, 0x60, 0x6d, 0xe8, 0x0e, - 0x95, 0x61, 0x34, 0x70, 0xe2, 0x01, 0x24, 0xb5, 0xbb, 0x61, 0x49, 0x5b, 0xd4, 0x7f, 0x32, 0xd7, 0xea, 0x1a, 0x34, - 0x81, 0x59, 0xab, 0xc5, 0x36, 0x4d, 0x85, 0x1c, 0x32, 0xaa, 0x1a, 0xb0, 0x52, 0x6d, 0x87, 0x34, 0x59, 0x22, 0x87, - 0x24, 0x71, 0x27, 0x73, 0x06, 0x55, 0x56, 0x7a, 0xd1, 0xff, 0xa8, 0x44, 0xe4, 0x43, 0x5e, 0xff, 0xa4, 0x8a, 0x67, - 0x99, 0xd4, 0x8f, 0xc2, 0xa1, 0x4a, 0x63, 0x93, 0x0d, 0x6d, 0x00, 0xa3, 0x0f, 0x73, 0xa8, 0x2c, 0x74, 0x6c, 0x95, - 0xfb, 0x6e, 0x5a, 0xc9, 0xb1, 0x21, 0x9f, 0xcc, 0x18, 0x30, 0xdf, 0x7e, 0x0b, 0x62, 0x8f, 0x5b, 0xcc, 0x38, 0xdc, - 0x4b, 0x7e, 0xbe, 0x4c, 0x44, 0xc1, 0x1f, 0x4e, 0xc3, 0x0e, 0x7c, 0xd3, 0x21, 0xa6, 0xcd, 0x15, 0x33, 0x64, 0x06, - 0xa5, 0x6d, 0x09, 0x31, 0x2d, 0x78, 0x4a, 0xa4, 0xfb, 0xf7, 0xfe, 0xc4, 0xde, 0xd3, 0x7c, 0x21, 0x3f, 0x59, 0x9d, - 0x83, 0xbe, 0x55, 0x97, 0x3e, 0x86, 0x17, 0xc6, 0x7d, 0x00, 0x50, 0xb9, 0xbd, 0x6d, 0xc5, 0x71, 0x7b, 0x5f, 0x85, - 0xf8, 0x83, 0x19, 0x66, 0x1c, 0xaf, 0x52, 0x64, 0x63, 0x81, 0xe9, 0x94, 0x59, 0xa9, 0x5b, 0x35, 0x2b, 0xfb, 0xc7, - 0xf4, 0x5f, 0x1a, 0x0d, 0xb0, 0x2f, 0x17, 0xf9, 0xf9, 0x76, 0x99, 0x28, 0xb0, 0xc2, 0x22, 0xd1, 0x7b, 0x17, 0x40, - 0xba, 0x83, 0x48, 0xc6, 0x9f, 0xf7, 0x70, 0xd1, 0xf0, 0xf0, 0x17, 0x39, 0x68, 0xd9, 0x79, 0xe5, 0x44, 0x69, 0x3e, - 0xaf, 0xd8, 0x09, 0x44, 0x9e, 0x3a, 0x45, 0x98, 0xfe, 0x7d, 0x72, 0xe5, 0xb5, 0x47, 0x4e, 0xce, 0x5e, 0x40, 0xbf, - 0x26, 0x6e, 0x9f, 0x9f, 0x65, 0x1d, 0xfe, 0xb1, 0x44, 0x85, 0xb0, 0x52, 0xe8, 0x41, 0x55, 0x08, 0x5f, 0x70, 0xe2, - 0x00, 0x4e, 0x3d, 0x7c, 0x78, 0xc6, 0xdf, 0x0e, 0x43, 0xfb, 0xf8, 0x99, 0xb3, 0x69, 0x89, 0xf1, 0x12, 0x83, 0x45, - 0xb5, 0xd4, 0x78, 0x7e, 0xff, 0x34, 0xeb, 0xe9, 0x9e, 0xb1, 0x4f, 0x8b, 0x9e, 0xac, 0x6a, 0x9a, 0x37, 0x24, 0xce, - 0x7f, 0xd8, 0xfc, 0x5a, 0x1b, 0x1f, 0xec, 0xdc, 0x56, 0x25, 0x47, 0xd6, 0x05, 0xae, 0xcb, 0xaa, 0x55, 0xd5, 0x37, - 0x03, 0xce, 0x49, 0x8f, 0xc5, 0x4b, 0x9d, 0xdd, 0x2f, 0xe8, 0x8f, 0x66, 0x3a, 0x5a, 0x1f, 0x7d, 0x70, 0x2d, 0x42, - 0xd5, 0xa4, 0x33, 0xba, 0x37, 0xbf, 0xc3, 0x39, 0xe5, 0x33, 0xd7, 0xf1, 0xb9, 0x5b, 0x0b, 0xe5, 0x09, 0x8f, 0x16, - 0x1a, 0x85, 0xa1, 0x3b, 0x77, 0x8f, 0xe0, 0x5a, 0x24, 0xcd, 0xc8, 0xde, 0xc2, 0x39, 0xd3, 0xf8, 0x4c, 0x7f, 0x36, - 0x0b, 0xf5, 0xa7, 0x3e, 0x14, 0x14, 0x11, 0xf3, 0x2b, 0xa6, 0x62, 0x28, 0x25, 0xc1, 0x43, 0x44, 0x04, 0x5a, 0x47, - 0x51, 0x3e, 0x55, 0x57, 0x57, 0xca, 0xea, 0x97, 0xb3, 0x2c, 0x28, 0x92, 0xd9, 0x14, 0x59, 0xb9, 0xe2, 0x8f, 0x4e, - 0x72, 0x96, 0xeb, 0x42, 0x40, 0xce, 0x3e, 0xc0, 0x89, 0xfd, 0x9b, 0x41, 0xe0, 0xb6, 0xb6, 0xd6, 0xfe, 0x48, 0x50, - 0x63, 0x14, 0x7c, 0x8b, 0x00, 0x8c, 0xc4, 0xd0, 0x46, 0xf9, 0xe4, 0x56, 0xba, 0x50, 0x51, 0xbd, 0x3f, 0x71, 0xf7, - 0x3f, 0xbf, 0xbb, 0xc9, 0x0d, 0x1b, 0x77, 0x69, 0x0e, 0x4d, 0xe6, 0xc9, 0x39, 0xda, 0xc8, 0xee, 0xba, 0x5f, 0x06, - 0xf9, 0x2d, 0x5f, 0x92, 0xf4, 0x74, 0x44, 0x60, 0x4b, 0xcb, 0x8f, 0x48, 0x45, 0x49, 0x22, 0x90, 0x63, 0xad, 0x00, - 0x50, 0x33, 0x21, 0x95, 0x8a, 0x1c, 0x45, 0x9e, 0x8c, 0x7a, 0x33, 0xa7, 0x24, 0x2d, 0x69, 0x37, 0xa8, 0x31, 0x2c, - 0x87, 0xaf, 0xb9, 0x36, 0x4b, 0x7d, 0xad, 0xa4, 0xec, 0xc4, 0xf6, 0x82, 0x05, 0x94, 0x38, 0xa6, 0xe0, 0x82, 0xd5, - 0x58, 0x9a, 0x36, 0xaf, 0x27, 0x18, 0xd0, 0x32, 0x97, 0x76, 0xd9, 0x12, 0xf2, 0x95, 0xfa, 0x7d, 0x58, 0x8c, 0x90, - 0x7c, 0x63, 0xa1, 0x58, 0xda, 0xaa, 0x55, 0xb9, 0xf3, 0x1c, 0x3f, 0xd0, 0xa4, 0x48, 0x1d, 0xed, 0x61, 0xfa, 0x16, - 0x8e, 0xc4, 0xe0, 0x66, 0x4e, 0xb9, 0xa4, 0x4c, 0xe3, 0xd2, 0x9f, 0xa4, 0xff, 0xaa, 0x2f, 0x43, 0x3e, 0xc1, 0x51, - 0xac, 0xfe, 0x83, 0x6a, 0xcc, 0x40, 0x40, 0xea, 0x4b, 0x10, 0x15, 0xc3, 0x68, 0xe6, 0x10, 0xdd, 0xa0, 0xf5, 0x99, - 0x3a, 0x91, 0xce, 0x5e, 0x6c, 0x70, 0xd2, 0x97, 0x73, 0xa2, 0x79, 0xe1, 0x3b, 0x8c, 0xf7, 0x81, 0x01, 0x0c, 0x0a, - 0xf3, 0x60, 0x0c, 0xec, 0xb2, 0x26, 0x6d, 0x29, 0xb8, 0x41, 0x0d, 0x34, 0x81, 0x07, 0x78, 0x3a, 0x89, 0x90, 0x8b, - 0x7c, 0x66, 0x71, 0x27, 0xbb, 0x98, 0x52, 0x6b, 0x7e, 0x2c, 0x84, 0x85, 0xfe, 0xdd, 0x62, 0x2b, 0xcb, 0x1d, 0x3c, - 0x13, 0x11, 0x54, 0x05, 0x0a, 0xbc, 0x72, 0x79, 0x43, 0xa7, 0x25, 0x70, 0xf0, 0x3e, 0xb5, 0xe2, 0x06, 0x07, 0xbe, - 0x0d, 0x2a, 0x5d, 0xb0, 0x1f, 0xb4, 0xeb, 0xdf, 0x7b, 0xae, 0xc2, 0x22, 0xea, 0x21, 0xde, 0x6a, 0xae, 0x57, 0x77, - 0xf7, 0xbe, 0xd7, 0xf1, 0x59, 0x53, 0xcb, 0x1e, 0x7f, 0xc6, 0x10, 0x0a, 0x4e, 0xd0, 0x2a, 0x15, 0x12, 0x30, 0xf0, - 0xc7, 0x2d, 0x6c, 0xfc, 0x92, 0xa5, 0xdb, 0x11, 0x4b, 0x7f, 0xfd, 0xba, 0xa2, 0xc9, 0xae, 0xba, 0xa9, 0x27, 0xa0, - 0x88, 0xbd, 0xa3, 0x55, 0x76, 0xb8, 0x4a, 0xcd, 0x7b, 0xc5, 0xbb, 0x1e, 0xf8, 0x94, 0x0e, 0xcc, 0x28, 0xb0, 0x17, - 0xc4, 0x1c, 0x18, 0xeb, 0xc7, 0x46, 0x79, 0xd7, 0x4f, 0xbe, 0x4b, 0xd1, 0x46, 0xad, 0xaf, 0xfc, 0x41, 0x10, 0xdf, - 0x67, 0x46, 0xac, 0xbd, 0x04, 0x66, 0x30, 0xba, 0xd3, 0x36, 0x1d, 0x76, 0xe5, 0x3e, 0x9e, 0x1f, 0xb2, 0xde, 0x41, - 0x40, 0xa5, 0xe8, 0x47, 0x81, 0x4b, 0x26, 0x30, 0x98, 0x83, 0x23, 0xdb, 0x8b, 0x3d, 0xf9, 0x44, 0xcc, 0x85, 0x28, - 0x45, 0x33, 0x46, 0x01, 0xc1, 0xc8, 0x61, 0x85, 0xed, 0x3f, 0xc2, 0x76, 0x01, 0x70, 0x8b, 0x87, 0x0c, 0x7b, 0x5e, - 0xe3, 0x4d, 0xbc, 0x1d, 0x35, 0xcc, 0x99, 0xd4, 0x5b, 0xd0, 0x4e, 0x8f, 0x21, 0xf9, 0x7d, 0x1a, 0x24, 0xa3, 0x22, - 0xf7, 0x28, 0x12, 0x84, 0xd7, 0x45, 0x4e, 0x5a, 0x80, 0x75, 0x77, 0xe8, 0xa6, 0x5f, 0x01, 0x62, 0xfa, 0x5e, 0x02, - 0xfe, 0x44, 0x6e, 0x22, 0x16, 0xbc, 0xdd, 0x34, 0xc4, 0x1d, 0x4c, 0x80, 0xa1, 0x11, 0x9e, 0x41, 0xd0, 0x08, 0x92, - 0x11, 0xdd, 0x6d, 0xee, 0xa7, 0xcc, 0x7f, 0x56, 0xe4, 0xc7, 0xb2, 0x31, 0xae, 0x79, 0xd3, 0xde, 0xc5, 0x6f, 0x11, - 0xa9, 0x00, 0x62, 0x67, 0xca, 0x2c, 0x54, 0x89, 0xc9, 0xd7, 0x85, 0x8d, 0x7d, 0x6e, 0x94, 0x25, 0xdb, 0xe7, 0xf5, - 0xd7, 0x66, 0xd8, 0x92, 0x66, 0xb6, 0xb7, 0x39, 0xe3, 0xb3, 0x8a, 0x89, 0x85, 0x17, 0x05, 0xce, 0xfd, 0xed, 0xd7, - 0xfd, 0xf9, 0x70, 0x95, 0x2d, 0xdb, 0x29, 0x53, 0x8f, 0x23, 0x25, 0xcb, 0x5a, 0x7f, 0xbb, 0x32, 0x93, 0xb7, 0x6e, - 0xd1, 0x13, 0xec, 0xa8, 0x35, 0xf3, 0x25, 0x47, 0xda, 0xba, 0x87, 0x93, 0xec, 0xba, 0xc0, 0x2e, 0xef, 0x04, 0xd0, - 0xb4, 0x74, 0x42, 0xf1, 0x73, 0x25, 0xb4, 0xac, 0x1d, 0xe0, 0x24, 0x7e, 0xfa, 0x62, 0xe2, 0xa5, 0x98, 0xad, 0xc1, - 0x36, 0xbf, 0x62, 0x5e, 0xc4, 0x60, 0xcf, 0x8d, 0x0a, 0xe1, 0x8c, 0xf3, 0xbe, 0x05, 0xb3, 0xf4, 0x1b, 0xaf, 0xdc, - 0xe6, 0x73, 0x82, 0xfd, 0x96, 0x16, 0xc1, 0xc0, 0xc4, 0x5d, 0xf5, 0x5a, 0xe3, 0x2c, 0x84, 0xa8, 0x6b, 0xb9, 0x2f, - 0x62, 0xe6, 0x36, 0xa7, 0xe9, 0x5d, 0xad, 0xc9, 0x8c, 0xfd, 0xe2, 0x4a, 0x33, 0xeb, 0xbb, 0xef, 0x20, 0x6b, 0xad, - 0x2a, 0xf4, 0x2b, 0x52, 0xcf, 0x64, 0xfd, 0x27, 0xb0, 0x19, 0x8b, 0x1d, 0x16, 0x4b, 0x2b, 0x75, 0xe7, 0xaa, 0xf4, - 0x03, 0x9e, 0x54, 0x00, 0x72, 0x11, 0xd0, 0x99, 0x6e, 0x3d, 0x77, 0x8b, 0x45, 0x3d, 0xea, 0xc1, 0xad, 0xbf, 0xb7, - 0x1a, 0x06, 0x41, 0xac, 0x63, 0xbf, 0x22, 0x78, 0x3c, 0x5e, 0x89, 0xdf, 0x0b, 0xaf, 0xc8, 0x0f, 0x5b, 0x1e, 0xff, - 0x7c, 0x01, 0x65, 0xfa, 0x49, 0x34, 0xed, 0xfc, 0x6c, 0xc3, 0xc2, 0xa4, 0x7c, 0x3a, 0x8f, 0xfc, 0x1e, 0xcd, 0xcd, - 0x15, 0xb4, 0xdc, 0xf3, 0x03, 0x97, 0xf2, 0x7f, 0x16, 0x29, 0x4b, 0x6a, 0x85, 0x66, 0xd9, 0x36, 0xc1, 0xd1, 0xdd, - 0x9e, 0xe2, 0xc1, 0x73, 0x9c, 0x50, 0x68, 0x6f, 0x4a, 0xbd, 0x55, 0x85, 0x9a, 0xa8, 0xb5, 0x85, 0x02, 0x65, 0xfd, - 0x88, 0xf6, 0x51, 0x71, 0xc4, 0x0d, 0x23, 0x3d, 0xea, 0x6f, 0x6a, 0x6d, 0x91, 0x5d, 0x47, 0xed, 0x97, 0xb5, 0xfb, - 0x7d, 0x12, 0x24, 0xff, 0x6d, 0x05, 0xc8, 0xac, 0x0d, 0xd5, 0x9b, 0x80, 0x69, 0x44, 0x31, 0x47, 0xc1, 0x8f, 0xd1, - 0x92, 0x42, 0xa3, 0x0c, 0x2e, 0x1c, 0x11, 0x66, 0x2d, 0xb5, 0xe4, 0x19, 0x43, 0xf0, 0xbc, 0xd1, 0xd0, 0x91, 0xf0, - 0xb5, 0xe9, 0x5d, 0x76, 0x66, 0x36, 0x4c, 0xce, 0x3d, 0xa2, 0x21, 0x9b, 0x7a, 0xaa, 0x28, 0x01, 0xf7, 0xcd, 0x72, - 0x7c, 0x75, 0x50, 0xb2, 0x26, 0xb5, 0x57, 0xc1, 0x6e, 0x1f, 0x72, 0x73, 0x19, 0xbd, 0x35, 0x54, 0x6b, 0xf8, 0xde, - 0x48, 0xd6, 0xb0, 0xca, 0x35, 0x90, 0xd8, 0xce, 0x8f, 0x30, 0x49, 0x45, 0x77, 0xf9, 0x16, 0x34, 0xde, 0x51, 0x95, - 0xcb, 0x4e, 0xeb, 0xda, 0xbb, 0x03, 0x37, 0x61, 0xd8, 0xfa, 0xd4, 0x8d, 0x8e, 0xf4, 0xfd, 0x80, 0x0d, 0x9a, 0x95, - 0x4a, 0x03, 0x4e, 0xf9, 0x05, 0x15, 0xad, 0xf3, 0xbb, 0x25, 0x5f, 0xec, 0x19, 0xee, 0x83, 0x11, 0x32, 0x26, 0x8e, - 0xc0, 0x8e, 0x1a, 0xe0, 0x29, 0x61, 0xc6, 0xe1, 0xc7, 0x9e, 0xdb, 0xd7, 0xc6, 0xa0, 0x7f, 0xa5, 0xd9, 0x50, 0x40, - 0x8d, 0xf6, 0xb8, 0x92, 0x54, 0x3a, 0x86, 0x19, 0x93, 0xc2, 0x87, 0x54, 0x28, 0x73, 0xfc, 0xbb, 0x73, 0x4d, 0xb1, - 0x66, 0x38, 0x57, 0x23, 0xd3, 0x86, 0xf3, 0xbf, 0x1a, 0xf3, 0x5b, 0x8e, 0xef, 0x20, 0xaa, 0x9e, 0x8e, 0x41, 0x87, - 0x50, 0x4a, 0x50, 0x76, 0x65, 0x42, 0x55, 0x03, 0xfd, 0xa2, 0x19, 0x6d, 0x9a, 0xd6, 0x8f, 0x91, 0xf3, 0xbf, 0x6e, - 0xbf, 0xb6, 0x93, 0x8b, 0xd6, 0x0a, 0xeb, 0xe2, 0x07, 0x3f, 0x30, 0xe4, 0xb5, 0x7b, 0x7e, 0x76, 0xab, 0x5c, 0xd9, - 0x53, 0x5b, 0x3c, 0x75, 0xc1, 0x97, 0xe9, 0xfa, 0x18, 0xbc, 0x2c, 0x20, 0x35, 0x8d, 0xaa, 0x75, 0xec, 0x13, 0x16, - 0x5a, 0xec, 0x3a, 0x6f, 0xd7, 0x2f, 0x4f, 0xaa, 0x89, 0x57, 0x2c, 0x03, 0x3a, 0x3f, 0xb3, 0x29, 0xf6, 0x91, 0x16, - 0x97, 0x0d, 0xff, 0x32, 0xa0, 0xe7, 0xd9, 0x40, 0x9e, 0xf9, 0x59, 0x7f, 0xae, 0x3f, 0xbe, 0xe5, 0x21, 0xa1, 0x14, - 0xb7, 0x35, 0x4e, 0xef, 0x1a, 0xdb, 0xcc, 0x3b, 0xb3, 0xb4, 0x8f, 0x9d, 0x66, 0x3e, 0xa2, 0x22, 0x5d, 0x70, 0x12, - 0xb6, 0xa7, 0x43, 0xba, 0x92, 0x6d, 0x16, 0x9a, 0x39, 0xf5, 0xa5, 0x71, 0x59, 0x9c, 0xd7, 0x69, 0x73, 0x31, 0xf7, - 0x82, 0xae, 0x03, 0x38, 0xd7, 0x29, 0x47, 0x70, 0x55, 0x11, 0x28, 0x9a, 0x9a, 0xb9, 0xa2, 0x78, 0x68, 0x2d, 0x76, - 0x73, 0x6b, 0xf7, 0x53, 0x8c, 0xb8, 0xd4, 0xa5, 0x2a, 0x51, 0x92, 0x6c, 0x59, 0x29, 0x90, 0xc9, 0x82, 0xac, 0x39, - 0x49, 0x15, 0x0e, 0xfa, 0x37, 0x87, 0x74, 0xf6, 0x62, 0xd8, 0x87, 0x70, 0xe6, 0x6b, 0xa9, 0x0a, 0xa3, 0x59, 0xac, - 0xe6, 0x39, 0x88, 0x54, 0x6d, 0x1f, 0x28, 0xa7, 0xca, 0x7d, 0x5b, 0x40, 0xe6, 0x46, 0xca, 0x4b, 0x51, 0x47, 0x6e, - 0x78, 0x4a, 0xbf, 0x36, 0x3d, 0x10, 0xa3, 0xd5, 0xb0, 0xa3, 0x8d, 0x66, 0xb3, 0x59, 0x14, 0x53, 0x8f, 0x43, 0x9b, - 0xd4, 0x7c, 0x1b, 0x51, 0xaf, 0x50, 0x35, 0xb3, 0x6f, 0x4c, 0x3d, 0x62, 0xc9, 0x9c, 0xe2, 0x35, 0x14, 0x26, 0xc9, - 0x3d, 0x8b, 0x2d, 0xea, 0x16, 0x6d, 0x6e, 0xce, 0x1c, 0x1b, 0x92, 0xb8, 0x8a, 0x4b, 0x99, 0xae, 0x34, 0x1e, 0x05, - 0xc2, 0x61, 0x25, 0xda, 0x4e, 0x30, 0x1b, 0xf3, 0xf4, 0x83, 0x9f, 0x92, 0x9f, 0x7b, 0x04, 0x4c, 0xb3, 0x2f, 0x60, - 0x2b, 0xe9, 0xce, 0x8c, 0xf8, 0x48, 0x41, 0xce, 0xe1, 0x2b, 0x86, 0xe9, 0x7b, 0x9b, 0xc8, 0x72, 0x1f, 0xe7, 0x53, - 0x82, 0x32, 0x39, 0xa9, 0x76, 0x68, 0xbc, 0x81, 0xd8, 0x1b, 0x20, 0x9e, 0x86, 0xb0, 0x04, 0x4f, 0x23, 0x60, 0x90, - 0xf8, 0xbc, 0x5c, 0xc5, 0x43, 0x2d, 0x6e, 0x7c, 0x97, 0x79, 0x08, 0x70, 0xb6, 0x0c, 0x43, 0x6d, 0x62, 0x92, 0xfb, - 0xb3, 0x06, 0x74, 0x27, 0xa2, 0x62, 0x49, 0x66, 0x97, 0x75, 0x15, 0x85, 0xf9, 0x77, 0x5e, 0x2f, 0x53, 0x27, 0x9c, - 0x2d, 0xde, 0xb8, 0x0d, 0x80, 0xe9, 0x42, 0x7b, 0xaa, 0x93, 0x13, 0x93, 0xe2, 0xd7, 0x50, 0x1a, 0xd6, 0x09, 0x0d, - 0x14, 0x89, 0xfa, 0x79, 0xb4, 0x9e, 0x98, 0xa2, 0x38, 0xff, 0x11, 0x91, 0x0c, 0x4c, 0x12, 0xc8, 0x60, 0xb4, 0x7b, - 0xc5, 0x9a, 0x50, 0xac, 0xfd, 0xa4, 0x65, 0xd3, 0x99, 0xfb, 0x36, 0x83, 0x98, 0xbd, 0x1f, 0x04, 0x0f, 0x04, 0xff, - 0xe5, 0x56, 0x27, 0x8c, 0xa0, 0x04, 0xc3, 0xec, 0x30, 0xff, 0x89, 0xac, 0xba, 0x2d, 0xe8, 0xa8, 0x57, 0xe4, 0xaa, - 0x77, 0xe2, 0x52, 0x67, 0x12, 0x55, 0x4f, 0x7e, 0x9e, 0xb8, 0xfb, 0x56, 0x36, 0x46, 0x0b, 0xdc, 0xe7, 0xc8, 0x27, - 0x57, 0x6e, 0x66, 0xdb, 0xc8, 0xa8, 0xa6, 0x28, 0x12, 0x8b, 0x98, 0xca, 0xbc, 0x79, 0x8e, 0xfb, 0xf0, 0xaa, 0xb9, - 0x83, 0xef, 0xb7, 0x39, 0xd8, 0xca, 0xac, 0xdc, 0xe5, 0xf2, 0x6d, 0x7a, 0x68, 0xd0, 0xfd, 0xae, 0x73, 0xa4, 0x4b, - 0xef, 0xa6, 0x72, 0x5b, 0xb7, 0x3b, 0x53, 0xe7, 0x23, 0xf5, 0xd4, 0xf1, 0xf9, 0x99, 0x74, 0x63, 0xb7, 0xfe, 0x53, - 0x35, 0xc1, 0x4f, 0xf9, 0x02, 0xb4, 0x34, 0x55, 0x7c, 0x2c, 0x28, 0xa3, 0x16, 0xdd, 0xc1, 0x57, 0x6e, 0xb9, 0x15, - 0xf4, 0x2b, 0x9f, 0xab, 0xbc, 0x70, 0x8d, 0x5c, 0x3a, 0x7b, 0xe1, 0x04, 0x36, 0xf5, 0xe0, 0x9d, 0xf1, 0x57, 0xc1, - 0x25, 0xe0, 0xda, 0x10, 0x07, 0x23, 0x25, 0x89, 0xf7, 0xd5, 0xc0, 0x1b, 0x11, 0xf1, 0x8f, 0x82, 0xa1, 0x51, 0xfa, - 0x96, 0xfa, 0x18, 0x5b, 0x0c, 0xb3, 0x3e, 0xaa, 0x94, 0xab, 0xb3, 0xb9, 0xe0, 0xd4, 0x39, 0xfa, 0x13, 0x46, 0xdc, - 0xf3, 0x00, 0xe7, 0xac, 0xae, 0x7f, 0x06, 0xe7, 0xb5, 0xfd, 0xcc, 0x38, 0x1f, 0x8a, 0xa6, 0x44, 0xeb, 0xdd, 0x78, - 0xa7, 0x3c, 0x9b, 0x2d, 0xe7, 0x67, 0x15, 0x5e, 0x0d, 0xf7, 0x19, 0x9f, 0x5e, 0xfa, 0x1d, 0x98, 0x3c, 0x0e, 0xba, - 0xf4, 0xbe, 0x72, 0x78, 0xef, 0x4e, 0xc5, 0x4a, 0x15, 0x35, 0xe2, 0xd8, 0xa1, 0x7b, 0x8d, 0xc7, 0xbd, 0x0b, 0xcc, - 0x1a, 0xab, 0x93, 0xc3, 0x43, 0x6b, 0xab, 0x7c, 0x5d, 0x65, 0x84, 0x9d, 0xc4, 0x37, 0xcb, 0xc6, 0x94, 0x48, 0xb0, - 0xbf, 0x0d, 0x94, 0x63, 0x38, 0x10, 0xe1, 0x61, 0xc2, 0x9b, 0xac, 0xc2, 0xbc, 0x96, 0x8a, 0x32, 0xab, 0x1d, 0xfe, - 0x5a, 0x71, 0x8d, 0x68, 0x07, 0x4b, 0xae, 0xa4, 0x0d, 0x66, 0xd1, 0xa5, 0xa4, 0x61, 0x75, 0xc3, 0xa1, 0x5e, 0xdf, - 0x15, 0x5c, 0xd5, 0xb6, 0x66, 0x91, 0xfc, 0x45, 0xd9, 0x8e, 0x95, 0x08, 0x9b, 0x29, 0xf3, 0x3c, 0xfb, 0x3f, 0xa2, - 0x4a, 0x87, 0xdc, 0x02, 0xa6, 0xf6, 0x43, 0xba, 0x42, 0x52, 0x8c, 0x0d, 0xda, 0x4f, 0x4a, 0x57, 0x32, 0xef, 0xf8, - 0x8d, 0xc5, 0x75, 0xcb, 0x50, 0xe4, 0x62, 0x33, 0x56, 0x17, 0x1b, 0xc0, 0xc2, 0x2a, 0x07, 0xcc, 0x46, 0x14, 0xcd, - 0xa2, 0x6c, 0xca, 0xa3, 0xed, 0x16, 0xaf, 0x5b, 0xb4, 0xfa, 0xfb, 0x33, 0xd1, 0x33, 0x5b, 0x27, 0x55, 0x9d, 0x65, - 0xbd, 0x7f, 0x65, 0xc5, 0x5c, 0xe1, 0xe1, 0x47, 0x7b, 0x6e, 0xe7, 0x88, 0xce, 0xfb, 0xbb, 0x9b, 0xfe, 0x85, 0xdd, - 0xfc, 0x7f, 0x49, 0x37, 0x61, 0x86, 0xf5, 0xe4, 0xf6, 0xd3, 0x4b, 0xac, 0x09, 0xe7, 0x3f, 0xb2, 0x89, 0x61, 0xdb, - 0x15, 0xd4, 0x16, 0x35, 0x66, 0x9c, 0x12, 0x3c, 0xf6, 0x81, 0x0a, 0xed, 0x61, 0xe2, 0x0a, 0x61, 0x54, 0x79, 0xaa, - 0x44, 0xfa, 0x5c, 0xfc, 0xb2, 0x4d, 0x64, 0xd0, 0x19, 0x87, 0xb2, 0x81, 0x9d, 0xdb, 0xb5, 0xca, 0xcc, 0xd6, 0xd2, - 0xfa, 0x8f, 0x99, 0x62, 0xf3, 0x7f, 0xc0, 0x12, 0xf5, 0x90, 0x47, 0x7e, 0x59, 0xb5, 0x08, 0xef, 0x0d, 0xe5, 0xe6, - 0x21, 0xc8, 0x2d, 0x8b, 0x0e, 0x7f, 0x60, 0x3e, 0x40, 0x8e, 0x60, 0x8c, 0x1a, 0xb0, 0x52, 0x4e, 0x21, 0x97, 0xf9, - 0x71, 0xaa, 0xc9, 0x50, 0xcb, 0x72, 0x9d, 0xb1, 0x4a, 0x23, 0xaf, 0x59, 0x99, 0xa7, 0x59, 0x91, 0x6b, 0x94, 0x0d, - 0x15, 0xd7, 0x9f, 0x91, 0xa3, 0x51, 0x1b, 0xd0, 0x10, 0xbb, 0xe3, 0x9c, 0xd8, 0x28, 0x73, 0xd4, 0x71, 0x72, 0x4b, - 0x9e, 0x59, 0x57, 0x33, 0x5b, 0x89, 0x93, 0x8b, 0x77, 0x9b, 0xb1, 0x6d, 0x77, 0x34, 0x2e, 0x99, 0x27, 0x8e, 0x73, - 0x74, 0x7d, 0xa3, 0xcd, 0x9e, 0x97, 0xec, 0xb8, 0xf8, 0x3f, 0x48, 0x0e, 0xdd, 0x3c, 0x1a, 0x11, 0xcc, 0xc5, 0x25, - 0x45, 0xa9, 0xe9, 0xe6, 0x48, 0x02, 0x1b, 0x1e, 0xff, 0xb9, 0x89, 0xae, 0xf8, 0x78, 0x6e, 0x56, 0x46, 0x14, 0x5b, - 0x9c, 0xd8, 0x9f, 0xed, 0x61, 0xd5, 0x7a, 0x44, 0xc2, 0x81, 0xb3, 0xce, 0xfa, 0x60, 0x9f, 0xeb, 0xd2, 0xff, 0xe0, - 0x07, 0x36, 0x12, 0x82, 0x8d, 0x61, 0xf5, 0xce, 0xfe, 0xa7, 0x66, 0xc5, 0x85, 0xae, 0x35, 0x3b, 0x5e, 0xf8, 0x57, - 0x5c, 0xe1, 0x2d, 0x49, 0x65, 0x25, 0x37, 0x2e, 0x77, 0x2a, 0xe3, 0x05, 0x55, 0x3a, 0x66, 0x61, 0xe8, 0x58, 0x4c, - 0xaf, 0x0e, 0x4a, 0xaf, 0x08, 0x68, 0xa8, 0xce, 0xb9, 0xab, 0x95, 0xd9, 0x04, 0x97, 0x11, 0x92, 0x4a, 0x81, 0xbb, - 0xc2, 0x90, 0xe9, 0x9d, 0x6f, 0x86, 0x7e, 0x30, 0x14, 0x66, 0x6e, 0x40, 0xd8, 0x32, 0x41, 0xa5, 0xc3, 0x9a, 0x15, - 0x7b, 0x41, 0x9b, 0x0c, 0xe6, 0x3c, 0xa2, 0xde, 0x6b, 0xa4, 0xbf, 0x73, 0xc2, 0x05, 0x38, 0x4a, 0x81, 0xc2, 0x80, - 0x2e, 0x6f, 0x3c, 0x40, 0x72, 0x89, 0x10, 0x63, 0x0d, 0x85, 0xd4, 0x26, 0x7e, 0x39, 0xbf, 0xe2, 0x9e, 0xf7, 0xb3, - 0xe3, 0xac, 0xeb, 0x5b, 0x03, 0x79, 0x98, 0x5f, 0xbf, 0xbd, 0xce, 0x7a, 0x90, 0xb3, 0x21, 0x71, 0xb1, 0xb2, 0xf3, - 0x8a, 0x76, 0x76, 0x45, 0x5b, 0xea, 0x6a, 0x54, 0xe1, 0xb6, 0x86, 0x29, 0x52, 0x54, 0xb1, 0xe1, 0x7a, 0x1b, 0xba, - 0x20, 0xe9, 0x8b, 0x35, 0x85, 0x84, 0x19, 0xbb, 0xa6, 0x30, 0x95, 0x3b, 0xa1, 0x47, 0x67, 0xc3, 0x40, 0x5f, 0x6c, - 0xfd, 0x02, 0xf4, 0xa7, 0x8d, 0x8d, 0x36, 0x7d, 0x4f, 0x54, 0x46, 0xcc, 0x29, 0xfa, 0xbc, 0xc3, 0xec, 0xd3, 0xfe, - 0x44, 0x77, 0xb0, 0x5a, 0x5f, 0xc6, 0x5f, 0x56, 0x6c, 0xd4, 0xc7, 0xd6, 0x33, 0x26, 0x89, 0x53, 0xc9, 0xed, 0x41, - 0x49, 0x41, 0x66, 0xde, 0x44, 0x0d, 0x19, 0x29, 0xad, 0x39, 0x8f, 0x20, 0xfe, 0x77, 0xae, 0x98, 0x99, 0x98, 0xf6, - 0x63, 0x5c, 0x52, 0x1f, 0x7f, 0xf7, 0xc4, 0x5b, 0xbb, 0x77, 0x9a, 0xa1, 0x63, 0xf6, 0x00, 0x81, 0x9c, 0x57, 0x5e, - 0xba, 0x60, 0x68, 0x6e, 0xad, 0x54, 0xb3, 0xa6, 0x51, 0xfe, 0xb3, 0xbb, 0x32, 0x05, 0x03, 0xfb, 0x44, 0xad, 0x3f, - 0xdb, 0xe5, 0x66, 0xea, 0x1b, 0xb3, 0x57, 0x03, 0x4e, 0x04, 0x66, 0x36, 0xdd, 0x54, 0xfa, 0xaf, 0xfb, 0xfe, 0x3b, - 0x16, 0xa0, 0xd8, 0xd9, 0xc8, 0x1f, 0x9a, 0x8a, 0xe0, 0xc6, 0x77, 0x67, 0x2f, 0x86, 0x2d, 0x0a, 0x05, 0x5f, 0x46, - 0x99, 0xee, 0x32, 0xf2, 0x07, 0x0d, 0x6d, 0xf0, 0x4b, 0x7a, 0x63, 0x1b, 0x97, 0x61, 0x1f, 0xed, 0x61, 0x12, 0xbb, - 0x60, 0x68, 0x6b, 0x62, 0x41, 0x50, 0x35, 0x75, 0xde, 0x30, 0x22, 0xa1, 0x6f, 0xad, 0x95, 0xcf, 0xeb, 0xd8, 0x33, - 0xde, 0x71, 0x3e, 0x64, 0x62, 0x04, 0x7e, 0x8b, 0xb6, 0x5b, 0x12, 0xca, 0xb8, 0x74, 0x0c, 0x32, 0xb5, 0x47, 0x6d, - 0xc7, 0xc9, 0xb4, 0xed, 0x76, 0xd4, 0xee, 0xd1, 0xdd, 0xcd, 0x6f, 0x06, 0xa5, 0xed, 0x8e, 0xf0, 0x2d, 0xbc, 0x3a, - 0x73, 0xe4, 0x7e, 0xeb, 0xee, 0x24, 0x5b, 0xa0, 0x37, 0x33, 0x15, 0x14, 0x75, 0xc2, 0xc9, 0x33, 0xd6, 0xf8, 0xbf, - 0xd0, 0x54, 0xc1, 0x10, 0x98, 0xcc, 0x44, 0xb2, 0xdb, 0x82, 0x7c, 0x16, 0xfa, 0xfb, 0x14, 0x6e, 0x15, 0xb2, 0xb4, - 0x2d, 0x66, 0x08, 0xa7, 0x7a, 0xd0, 0x0c, 0x5e, 0x42, 0x81, 0x28, 0xed, 0x9d, 0xa1, 0x32, 0xe8, 0x41, 0xa5, 0x03, - 0x99, 0x28, 0x06, 0x35, 0x4b, 0x61, 0xca, 0x9b, 0x90, 0x7a, 0xf7, 0x7b, 0xbd, 0xf5, 0x77, 0xf9, 0xde, 0x8c, 0x22, - 0x1e, 0xf5, 0xd6, 0x49, 0x02, 0x82, 0x5f, 0x71, 0x20, 0x13, 0xe5, 0xf5, 0x92, 0x18, 0xb1, 0x8e, 0xc7, 0x49, 0xae, - 0x16, 0x1d, 0xaf, 0xc4, 0x39, 0x25, 0x15, 0x42, 0xce, 0x01, 0x0c, 0x13, 0x05, 0xee, 0xe5, 0x38, 0x82, 0xf5, 0x80, - 0x67, 0x72, 0x45, 0x3d, 0x1b, 0x8b, 0xbb, 0xfd, 0xef, 0xe5, 0xd5, 0xed, 0x9a, 0xf6, 0x36, 0x49, 0x01, 0x56, 0x5d, - 0x54, 0x82, 0xef, 0xfe, 0xfc, 0x29, 0xe4, 0xb1, 0x64, 0x87, 0x5a, 0x2a, 0x73, 0x30, 0x5b, 0x74, 0x1d, 0x72, 0xd6, - 0xa7, 0xaa, 0x3a, 0x36, 0x39, 0xa0, 0x86, 0xd3, 0xb4, 0x73, 0xc1, 0x78, 0x9c, 0xb0, 0x86, 0x73, 0xc2, 0x1a, 0x76, - 0xa8, 0x68, 0x23, 0x8c, 0x6e, 0x68, 0x31, 0x96, 0xb4, 0xc6, 0x7c, 0x3b, 0x20, 0x24, 0xf8, 0x7a, 0xa1, 0x95, 0x8b, - 0x8c, 0xe3, 0x8f, 0x2d, 0x06, 0x13, 0xec, 0x12, 0x2b, 0xdd, 0x84, 0x7f, 0x0d, 0xcf, 0x95, 0xbe, 0x95, 0x27, 0x71, - 0x73, 0x6f, 0xce, 0xe1, 0x44, 0xe3, 0x51, 0x93, 0x8c, 0xfc, 0x94, 0xf5, 0xa8, 0x94, 0xe4, 0x3f, 0x37, 0x8f, 0x81, - 0x33, 0x73, 0x8b, 0x7d, 0x25, 0x30, 0x26, 0x54, 0x3a, 0x96, 0xf1, 0x2f, 0x11, 0xf5, 0xd9, 0x68, 0xc4, 0x0c, 0x0a, - 0xe3, 0x5c, 0x25, 0x56, 0xe2, 0x3e, 0xdb, 0xa2, 0x97, 0xf2, 0xae, 0x31, 0x46, 0x25, 0x4c, 0xc5, 0x2f, 0x46, 0xf6, - 0x18, 0xa9, 0xb7, 0x73, 0xb6, 0xfd, 0x5c, 0x13, 0xdd, 0x73, 0x3a, 0x90, 0x04, 0x8d, 0x4b, 0x66, 0x0a, 0x90, 0xc4, - 0x04, 0x63, 0x72, 0x07, 0x2c, 0xda, 0xa6, 0x75, 0x9e, 0xc2, 0xab, 0x56, 0xe3, 0x49, 0x65, 0x7b, 0xdf, 0x65, 0x65, - 0x2e, 0xdb, 0x8e, 0x4e, 0x5b, 0x12, 0x24, 0x8d, 0x1a, 0xa7, 0x48, 0x48, 0xd5, 0xd3, 0xac, 0x0c, 0x0b, 0x84, 0xb5, - 0xe2, 0x9c, 0xbe, 0xb9, 0x35, 0x99, 0x9d, 0x17, 0xb1, 0x57, 0x78, 0x15, 0x85, 0x08, 0x6e, 0x67, 0x13, 0x89, 0x0f, - 0x63, 0xcb, 0x3a, 0x59, 0xc8, 0xd2, 0xb7, 0x6e, 0xad, 0x4b, 0xc0, 0x0f, 0xde, 0xea, 0xb7, 0xfb, 0xf1, 0x38, 0xb4, - 0x30, 0xd6, 0x47, 0xb8, 0xf8, 0xa8, 0x17, 0x2c, 0xad, 0x7c, 0x89, 0x08, 0x4a, 0x9b, 0xa5, 0xd7, 0xbf, 0x60, 0xb1, - 0x29, 0x2f, 0x57, 0x2c, 0x34, 0x36, 0x74, 0x33, 0x0d, 0xd5, 0x32, 0x31, 0x27, 0x15, 0x55, 0x31, 0xc7, 0x00, 0x3d, - 0xee, 0x20, 0x73, 0xcb, 0x22, 0x6b, 0xd2, 0xc3, 0x59, 0x09, 0xcc, 0xd7, 0x60, 0xe7, 0x38, 0x03, 0xea, 0xd8, 0xa4, - 0xea, 0x17, 0x0b, 0xa0, 0x24, 0x6e, 0xe0, 0x5b, 0x21, 0x77, 0xa1, 0xca, 0x1e, 0x29, 0xa4, 0xb0, 0x0e, 0x2c, 0xe1, - 0xac, 0x60, 0xc5, 0xd8, 0x3e, 0x6c, 0xe6, 0x8f, 0x51, 0x6f, 0x01, 0xd3, 0x43, 0x08, 0xf3, 0xdd, 0x1d, 0xb8, 0x11, - 0x1d, 0xad, 0xc9, 0xe4, 0x1e, 0x27, 0xc8, 0xa2, 0x9f, 0xfb, 0x25, 0x31, 0x14, 0x4f, 0xc8, 0xcb, 0x51, 0x33, 0x16, - 0xb5, 0x60, 0x5a, 0xa6, 0xcd, 0x2d, 0xdf, 0x7d, 0x6d, 0x23, 0xaa, 0x47, 0xc4, 0xa5, 0x42, 0x48, 0x1d, 0x14, 0xe8, - 0x0e, 0x73, 0xa9, 0xeb, 0xc9, 0xb3, 0x45, 0xf1, 0x2c, 0x9b, 0xae, 0x12, 0xfc, 0xe9, 0xe3, 0x0d, 0xb5, 0xbd, 0x09, - 0xa8, 0xf4, 0x5e, 0x77, 0x9c, 0x93, 0xde, 0x51, 0x89, 0x88, 0x26, 0x19, 0x7f, 0xfb, 0xc8, 0xbc, 0x05, 0x91, 0x58, - 0xeb, 0xe1, 0xd2, 0xeb, 0xb7, 0xaf, 0x51, 0xb0, 0x6a, 0x22, 0x9c, 0xbd, 0xa5, 0x49, 0x1c, 0xbc, 0x14, 0x21, 0x19, - 0x8a, 0x60, 0xe4, 0xa3, 0x82, 0xd8, 0x8a, 0xad, 0x12, 0x75, 0xb5, 0x86, 0x40, 0xc4, 0x39, 0xd8, 0x20, 0xb3, 0x8c, - 0xce, 0x99, 0xd7, 0xbe, 0x3c, 0x44, 0xf1, 0xd2, 0x14, 0xf5, 0xbf, 0x5a, 0x16, 0x7e, 0xf4, 0x70, 0xe0, 0x75, 0x64, - 0xe5, 0xac, 0x77, 0xbd, 0x54, 0x6e, 0xcb, 0x3a, 0x6e, 0xad, 0x7a, 0x4f, 0x9e, 0x20, 0xa7, 0xd1, 0xa6, 0x97, 0xe2, - 0xd6, 0x21, 0xa9, 0x31, 0xbc, 0x56, 0xb5, 0xa8, 0x8f, 0x0b, 0x77, 0xd8, 0x8b, 0x5a, 0xa9, 0x77, 0x30, 0x11, 0x5d, - 0xf7, 0xed, 0x9f, 0x88, 0x6a, 0xc8, 0x98, 0x8e, 0x35, 0xe4, 0x0e, 0x6c, 0xc1, 0xf4, 0x54, 0xd2, 0x77, 0x02, 0xf1, - 0xf8, 0x48, 0xb2, 0xab, 0xff, 0x94, 0xd1, 0xfd, 0x85, 0x8c, 0x81, 0x91, 0xd1, 0x1d, 0x61, 0x2d, 0xc2, 0xbd, 0x34, - 0xe8, 0x18, 0x23, 0x94, 0x4f, 0x89, 0x66, 0x66, 0xd9, 0x6d, 0x5e, 0x90, 0xd8, 0xe7, 0x5a, 0xcd, 0xde, 0x72, 0x9d, - 0x48, 0xd0, 0xa2, 0x04, 0xe2, 0xe5, 0x96, 0x19, 0x17, 0x80, 0xae, 0x8d, 0x9b, 0x14, 0x71, 0xb8, 0xb1, 0xd9, 0xdb, - 0x00, 0xa0, 0x7d, 0xfe, 0xfd, 0x4c, 0xe9, 0xe2, 0x76, 0x41, 0x09, 0x9b, 0x1f, 0x2c, 0x26, 0x8b, 0x5b, 0x19, 0x14, - 0x62, 0x23, 0x04, 0x0f, 0x64, 0x13, 0x8d, 0xdd, 0x7a, 0x8a, 0xd8, 0x3c, 0x5f, 0x20, 0x6d, 0x51, 0x78, 0x26, 0x67, - 0x93, 0xfd, 0x8b, 0x76, 0xb0, 0x81, 0xb1, 0x6e, 0x52, 0x94, 0xdf, 0x95, 0xa6, 0xa3, 0x8c, 0xda, 0xc7, 0x2f, 0x37, - 0x5c, 0x94, 0xa5, 0x26, 0x30, 0x9a, 0x46, 0xdd, 0xf2, 0xf7, 0x89, 0x13, 0x0c, 0x5d, 0x19, 0x01, 0xca, 0xb9, 0x94, - 0x09, 0x9f, 0xb3, 0x6f, 0x90, 0x16, 0x00, 0xf2, 0x9b, 0x1f, 0xb5, 0xe3, 0x63, 0x73, 0xbd, 0xfc, 0xd2, 0xb6, 0xa5, - 0x44, 0xf4, 0x5f, 0xda, 0x2a, 0xdb, 0xb1, 0x0f, 0x54, 0xf1, 0x30, 0x6a, 0x44, 0xcb, 0x9a, 0x0f, 0x59, 0xfb, 0x14, - 0x0f, 0x9b, 0x7b, 0x6f, 0x76, 0xa6, 0xc8, 0x86, 0xda, 0x25, 0xfb, 0xcb, 0x4b, 0x3a, 0x2f, 0xaf, 0xd6, 0x0c, 0x5e, - 0xed, 0x11, 0xea, 0x2a, 0x02, 0x05, 0x8f, 0xc1, 0x01, 0xbe, 0x36, 0xfb, 0x9e, 0x2d, 0x28, 0xf0, 0xcf, 0x8e, 0x9d, - 0xbf, 0x3c, 0x9f, 0x43, 0x02, 0x59, 0x9f, 0x35, 0x49, 0x04, 0x44, 0x24, 0x74, 0x3a, 0xdb, 0x1a, 0x82, 0x3c, 0x8c, - 0x2c, 0x1e, 0xb1, 0x59, 0xc6, 0x7f, 0xb1, 0x98, 0x8b, 0xcb, 0x7b, 0x36, 0xb9, 0x9f, 0x9b, 0xb7, 0xce, 0x00, 0xa9, - 0x6d, 0x9a, 0xc9, 0x48, 0x75, 0x64, 0x1a, 0x40, 0x05, 0xed, 0x85, 0x52, 0x4a, 0x46, 0xa9, 0x1c, 0x23, 0xb6, 0x6b, - 0x23, 0xe3, 0xe2, 0x64, 0x49, 0xc3, 0xb0, 0x24, 0xf8, 0x35, 0x11, 0x04, 0xbd, 0x54, 0x44, 0xf5, 0x70, 0x51, 0xca, - 0xdb, 0x21, 0x8f, 0x06, 0xd0, 0x52, 0xe3, 0x6d, 0x92, 0xa7, 0xdd, 0x8b, 0x73, 0x17, 0x59, 0x71, 0xf3, 0xa7, 0xc4, - 0x0f, 0x95, 0x63, 0x3c, 0x29, 0x90, 0x18, 0xe7, 0x5d, 0xb9, 0xf3, 0xa0, 0x0e, 0xc4, 0x1c, 0x13, 0x3c, 0xd2, 0xb3, - 0xaa, 0x3d, 0x98, 0x19, 0x68, 0x53, 0x1a, 0x4d, 0x15, 0xb5, 0x01, 0xe5, 0xff, 0x80, 0xbe, 0xca, 0xa7, 0xe5, 0x91, - 0x6b, 0x10, 0x86, 0xd2, 0x7a, 0x4b, 0xc3, 0x4b, 0x42, 0x68, 0x71, 0xae, 0x4c, 0x32, 0x08, 0xbc, 0xf1, 0xa1, 0xd7, - 0x35, 0x7e, 0x10, 0x25, 0x40, 0x73, 0xe6, 0x27, 0x1f, 0x3e, 0x9e, 0x03, 0x14, 0xce, 0x5a, 0x32, 0xfa, 0xb3, 0xab, - 0x09, 0x4b, 0xba, 0x5d, 0x34, 0xbb, 0x11, 0xca, 0x57, 0x29, 0x58, 0x5a, 0x58, 0x8a, 0xde, 0xa2, 0x3c, 0x30, 0x6c, - 0xb7, 0xb2, 0x7d, 0xfb, 0x5f, 0x1e, 0xde, 0x2b, 0x74, 0x91, 0xb0, 0x1d, 0xe2, 0xa7, 0xa8, 0xe9, 0x2f, 0x3e, 0x9c, - 0x9e, 0x8c, 0x61, 0xbb, 0x2b, 0x61, 0xee, 0x30, 0xcf, 0xb1, 0xbf, 0x74, 0xe4, 0x86, 0xb6, 0x12, 0x31, 0xf9, 0x5a, - 0x36, 0x61, 0x11, 0x07, 0x0c, 0x64, 0xae, 0x06, 0xb9, 0x83, 0x23, 0x04, 0xa6, 0xd6, 0x7c, 0xf2, 0xff, 0x54, 0x2d, - 0x1e, 0x9f, 0x2d, 0x8b, 0x4a, 0x82, 0x7c, 0x2b, 0xed, 0xf3, 0xd8, 0x87, 0xa4, 0x1d, 0xd8, 0xf7, 0x08, 0x16, 0xbd, - 0xdd, 0x61, 0x51, 0x68, 0xa1, 0x83, 0xb8, 0xa4, 0xce, 0xa7, 0xf0, 0xea, 0xe5, 0x32, 0x85, 0xd0, 0x29, 0x0b, 0x3c, - 0x5f, 0x45, 0x38, 0xa6, 0xf7, 0xc7, 0x03, 0x95, 0x05, 0xa5, 0x5c, 0x4e, 0xf0, 0x29, 0x6f, 0xea, 0x70, 0x06, 0xd4, - 0x90, 0xf6, 0xa9, 0x70, 0xc5, 0x3f, 0x4a, 0x59, 0x17, 0x3a, 0xb3, 0x90, 0xaa, 0x30, 0xd9, 0x91, 0xf0, 0xbf, 0x54, - 0xcc, 0x90, 0xe1, 0x85, 0x50, 0xa5, 0x0d, 0x7c, 0x6d, 0x8b, 0xae, 0x94, 0x17, 0x6d, 0x0b, 0x7d, 0x2c, 0x76, 0x65, - 0x4e, 0x00, 0xba, 0x01, 0x5a, 0x7b, 0xed, 0x82, 0xbb, 0x1b, 0xee, 0x65, 0x9f, 0x15, 0xf7, 0x6e, 0xda, 0x00, 0x07, - 0x5f, 0x20, 0xa7, 0xbe, 0x7f, 0x45, 0x71, 0xfe, 0x69, 0x2b, 0x1e, 0x2d, 0xc4, 0x94, 0x80, 0x09, 0x24, 0xe4, 0x1b, - 0x3e, 0xb6, 0x66, 0xc4, 0x3e, 0x7e, 0x08, 0x37, 0x4a, 0x09, 0x2b, 0x8d, 0x3c, 0x38, 0xca, 0xed, 0x37, 0x55, 0x86, - 0xe4, 0xb6, 0x9c, 0x83, 0xc2, 0x10, 0x0b, 0x07, 0xdc, 0x65, 0xae, 0x6c, 0x7f, 0xbc, 0x4a, 0x8f, 0xc2, 0x9e, 0xb8, - 0x50, 0xb1, 0x18, 0x6a, 0x64, 0xc4, 0x2b, 0x1e, 0xaa, 0xb3, 0xd2, 0xc4, 0x00, 0x19, 0x61, 0x80, 0x8e, 0x29, 0x6d, - 0x84, 0x40, 0x09, 0x01, 0x5b, 0x7e, 0xa8, 0xa3, 0x42, 0x13, 0xa1, 0x08, 0xa1, 0x25, 0xd2, 0x1c, 0x1d, 0x64, 0x65, - 0x86, 0xa4, 0xd2, 0x63, 0x76, 0x4c, 0x07, 0x96, 0x05, 0x58, 0x52, 0x29, 0x0a, 0x20, 0x9f, 0x8c, 0x51, 0xab, 0x88, - 0x50, 0xe2, 0xae, 0xbc, 0x4c, 0x1a, 0x0e, 0x58, 0xc3, 0x5c, 0x34, 0x17, 0x4b, 0xd6, 0x75, 0x38, 0x94, 0x21, 0x4d, - 0xae, 0x5a, 0x05, 0x79, 0xa7, 0x3f, 0x4f, 0x63, 0xce, 0x57, 0x04, 0x42, 0x9b, 0xfb, 0x91, 0xcb, 0x05, 0xc2, 0x8f, - 0x74, 0x6c, 0x8c, 0x91, 0x91, 0xb4, 0x76, 0x20, 0x75, 0x51, 0x22, 0x24, 0xc4, 0x95, 0x74, 0x41, 0x73, 0x3e, 0x14, - 0x22, 0x3e, 0x3b, 0x61, 0xae, 0x0f, 0x12, 0xb3, 0x44, 0xe5, 0xdf, 0x37, 0xcb, 0x61, 0xf5, 0x42, 0xf0, 0xb0, 0xd8, - 0xae, 0xaa, 0x1c, 0x28, 0x24, 0x12, 0xd6, 0xa8, 0x13, 0xe6, 0xce, 0x1b, 0xcb, 0xdf, 0x14, 0xc1, 0x9e, 0x27, 0x64, - 0x26, 0x18, 0xa5, 0x57, 0x51, 0xae, 0x54, 0xef, 0x94, 0x39, 0x8c, 0xdc, 0xf0, 0xee, 0xa6, 0xf8, 0xc1, 0x81, 0xbc, - 0x67, 0x53, 0x7a, 0xc4, 0xdb, 0xfd, 0x50, 0x4b, 0x9c, 0x53, 0x24, 0x39, 0x41, 0x29, 0xe8, 0xfe, 0xc3, 0x6b, 0x47, - 0x25, 0x31, 0xfe, 0xd0, 0xa2, 0xf4, 0x5b, 0x8b, 0xa7, 0xb9, 0x96, 0x33, 0x6d, 0xd2, 0xcc, 0x9c, 0x6f, 0x46, 0x15, - 0x9b, 0x2b, 0x63, 0x68, 0x5d, 0x70, 0x20, 0x00, 0x37, 0x83, 0x75, 0x2a, 0xad, 0xcf, 0xf5, 0x07, 0x08, 0x7d, 0xe3, - 0x3e, 0x28, 0xb3, 0x1d, 0x3c, 0x1a, 0x63, 0xc8, 0xeb, 0x67, 0x57, 0x75, 0xd0, 0x65, 0x44, 0x82, 0x00, 0x16, 0x7a, - 0xc8, 0xe1, 0x95, 0xba, 0x9c, 0xd9, 0xca, 0xec, 0xd1, 0xe6, 0xb5, 0x1c, 0x6f, 0x1d, 0x69, 0x38, 0x2e, 0x8e, 0x67, - 0x1f, 0x2c, 0x9d, 0x47, 0xe8, 0x48, 0xca, 0x8d, 0xf7, 0x4a, 0x20, 0xdf, 0x10, 0x19, 0xa8, 0xe7, 0xa2, 0x02, 0xb0, - 0x2b, 0x8b, 0xaa, 0xe4, 0x75, 0x78, 0xe8, 0xf9, 0x38, 0x32, 0x8f, 0x18, 0xe3, 0x10, 0x55, 0x46, 0x1e, 0x9d, 0xdc, - 0x2e, 0x2d, 0x32, 0x6a, 0x2e, 0x98, 0x5a, 0xcd, 0xbb, 0xea, 0x94, 0x07, 0xb2, 0xc9, 0xd7, 0x2b, 0x2d, 0xb4, 0x1e, - 0x89, 0x15, 0xdd, 0xac, 0x8a, 0x7a, 0x58, 0x20, 0x62, 0xbd, 0xff, 0x04, 0x91, 0x47, 0x2c, 0x1f, 0x64, 0xd4, 0x22, - 0x6d, 0xae, 0xc4, 0x4a, 0x29, 0x60, 0x76, 0x81, 0x42, 0x2b, 0xef, 0x10, 0x5c, 0xf9, 0x4d, 0x85, 0x44, 0xda, 0xc5, - 0x5d, 0x07, 0xea, 0x11, 0xbf, 0x35, 0xb2, 0x59, 0x1f, 0xa8, 0xe5, 0x7c, 0x2b, 0x2a, 0x1a, 0x22, 0x23, 0xd2, 0xd1, - 0x6f, 0x38, 0x01, 0xc3, 0x82, 0x0c, 0xe9, 0xf4, 0x3c, 0xf5, 0x58, 0xa0, 0xc5, 0x50, 0xe5, 0x54, 0x8c, 0x65, 0x52, - 0xdd, 0x0a, 0x96, 0x29, 0xb3, 0x50, 0x12, 0x5d, 0x41, 0xcb, 0xec, 0x35, 0xd8, 0x7c, 0xcf, 0x6a, 0x5b, 0x64, 0x44, - 0xc2, 0x35, 0xc2, 0x1f, 0x86, 0x31, 0x00, 0xaf, 0x12, 0xa5, 0xf3, 0xc0, 0x68, 0xc5, 0x24, 0xe6, 0x71, 0x0a, 0xaf, - 0x9b, 0x2a, 0x79, 0x81, 0x5b, 0xf3, 0xd4, 0xd4, 0x58, 0x7e, 0xff, 0xfa, 0xfb, 0x41, 0x53, 0x65, 0xad, 0x40, 0x7e, - 0xb2, 0x6e, 0xfd, 0xfb, 0x2e, 0xf7, 0x20, 0x6f, 0xd3, 0xfb, 0x7e, 0x1c, 0xf2, 0x0d, 0x04, 0x82, 0x51, 0x0a, 0xd3, - 0xc5, 0xfa, 0xb4, 0xc2, 0xe8, 0x7a, 0x49, 0xbb, 0x32, 0x7d, 0x40, 0xc2, 0xfb, 0x7a, 0xfb, 0x19, 0xa1, 0xcc, 0x12, - 0xfb, 0x50, 0x10, 0xc5, 0x4a, 0x94, 0x47, 0xe6, 0x67, 0x73, 0x6f, 0x45, 0x5c, 0x30, 0x53, 0xfd, 0x62, 0xf2, 0x30, - 0x24, 0x1c, 0x98, 0x99, 0x08, 0x07, 0xd6, 0xb4, 0xf0, 0xec, 0x6a, 0xc1, 0x9f, 0x96, 0x12, 0xe0, 0x11, 0xeb, 0x2a, - 0xfd, 0xbd, 0x8c, 0xa5, 0x18, 0xb1, 0xbd, 0x9d, 0xa1, 0xf4, 0x9c, 0x59, 0x07, 0x1d, 0x5e, 0x89, 0x82, 0x2d, 0xce, - 0xf4, 0x03, 0x33, 0x39, 0x8a, 0x0b, 0xaa, 0x25, 0xfc, 0xed, 0xad, 0xe2, 0xbe, 0x54, 0x3c, 0xa7, 0xb0, 0xef, 0x49, - 0xbe, 0xf8, 0x20, 0xed, 0xbd, 0xa8, 0x82, 0x56, 0x38, 0xb5, 0xc1, 0x0d, 0xf1, 0xd1, 0x9d, 0xf2, 0x50, 0x29, 0x42, - 0x08, 0xa3, 0xe8, 0x17, 0xf5, 0x08, 0x79, 0x81, 0xd7, 0xcb, 0xb7, 0x75, 0x7d, 0x48, 0x98, 0x82, 0xb8, 0xbe, 0xdb, - 0xc2, 0x19, 0x7d, 0x6a, 0x46, 0xcd, 0xdd, 0x71, 0xf5, 0x17, 0xea, 0x16, 0xef, 0x40, 0xb4, 0xc3, 0x74, 0x0f, 0x8f, - 0xeb, 0xfa, 0x68, 0x72, 0xd4, 0x85, 0x26, 0x6e, 0x35, 0xdb, 0xaf, 0xb4, 0x64, 0x9e, 0x06, 0x30, 0x68, 0x8c, 0xfa, - 0x7d, 0xc9, 0x1e, 0x8d, 0xef, 0x78, 0x4b, 0x64, 0x43, 0xb8, 0x0d, 0xe8, 0x70, 0x70, 0xa1, 0xa7, 0xfe, 0x46, 0xf6, - 0x6b, 0xb9, 0x04, 0xc7, 0x4b, 0xf1, 0x63, 0xdd, 0xf0, 0x47, 0x99, 0xd0, 0xec, 0x24, 0xa6, 0xc1, 0xfd, 0xb6, 0xb8, - 0xb2, 0xc2, 0x65, 0x12, 0x0a, 0x0b, 0x68, 0x40, 0x60, 0x01, 0x45, 0xd0, 0xe7, 0x30, 0x49, 0x94, 0x3d, 0x9c, 0xb3, - 0xdd, 0xdb, 0x7b, 0x71, 0x4c, 0x24, 0x9d, 0xd2, 0xe4, 0xe8, 0x07, 0x5e, 0x4c, 0x67, 0x51, 0x93, 0x68, 0xa4, 0x5f, - 0x31, 0x27, 0x2f, 0x1b, 0xe9, 0xc8, 0x00, 0x31, 0x67, 0x15, 0xa2, 0x0b, 0x69, 0xdf, 0x3f, 0x23, 0x32, 0xa0, 0x62, - 0x50, 0x37, 0xc3, 0x0e, 0xb1, 0x29, 0xe7, 0xb5, 0xeb, 0x07, 0x46, 0x4b, 0x7c, 0xb1, 0x3c, 0xca, 0xb0, 0xfc, 0x31, - 0x1f, 0xa3, 0xef, 0xbc, 0x95, 0x65, 0xe8, 0xc2, 0x72, 0x7e, 0x17, 0x93, 0xa5, 0x3a, 0x5c, 0x3d, 0x09, 0xe9, 0x7e, - 0x6a, 0xcd, 0x4b, 0xff, 0xb3, 0xe9, 0x82, 0xb4, 0xcf, 0x3c, 0xa4, 0x7e, 0x92, 0xc7, 0x68, 0xf7, 0x55, 0x2f, 0xac, - 0xd3, 0x85, 0x11, 0x66, 0xfa, 0xa8, 0x9a, 0x85, 0x0f, 0x55, 0x66, 0xcb, 0xc6, 0xd3, 0x52, 0xcc, 0x1f, 0x1d, 0xc1, - 0x1a, 0x82, 0x26, 0x24, 0x1b, 0xf7, 0x25, 0xf1, 0x83, 0xea, 0x82, 0xf1, 0x60, 0x87, 0xe5, 0xf5, 0xfc, 0x66, 0xd7, - 0xbe, 0xd3, 0xf2, 0xa9, 0xe0, 0x1f, 0x3c, 0xc4, 0xca, 0x9f, 0x0a, 0x87, 0x25, 0x2e, 0xbe, 0xb0, 0x52, 0x67, 0xbd, - 0x28, 0xa4, 0xad, 0xd5, 0xac, 0xa8, 0x65, 0x37, 0x64, 0xe5, 0x55, 0xde, 0xf2, 0x52, 0x0a, 0x7e, 0x4d, 0x45, 0x4e, - 0x72, 0x9d, 0x72, 0x31, 0x18, 0x78, 0x33, 0x27, 0xfd, 0x7a, 0x42, 0x1b, 0xb9, 0x81, 0x71, 0xfa, 0x3a, 0xb6, 0x2e, - 0x90, 0x04, 0x76, 0xf5, 0x91, 0x0b, 0x4f, 0x20, 0x91, 0xd5, 0xc7, 0x73, 0x36, 0xd6, 0xcd, 0x4e, 0xbe, 0x97, 0x77, - 0x19, 0x47, 0xf0, 0x4c, 0x21, 0xde, 0x9c, 0xd7, 0x06, 0xfc, 0x23, 0xef, 0x70, 0x6e, 0xe5, 0x7d, 0x50, 0x8d, 0xa1, - 0x35, 0x6c, 0x69, 0xe0, 0xab, 0x0b, 0x8c, 0x61, 0x02, 0xd5, 0x24, 0x08, 0x8e, 0xd6, 0x62, 0xbb, 0x20, 0x38, 0x96, - 0x51, 0x78, 0xb1, 0x3a, 0xe5, 0x17, 0x66, 0x53, 0x11, 0x45, 0x26, 0xfc, 0xc2, 0x0e, 0xd5, 0x66, 0x84, 0x43, 0xfc, - 0x58, 0x11, 0xe5, 0x43, 0x8d, 0x07, 0x20, 0x0e, 0x60, 0x7a, 0xd3, 0x88, 0x68, 0x7f, 0x8d, 0x1a, 0x05, 0x35, 0x3c, - 0x73, 0x97, 0xde, 0x59, 0x23, 0x2e, 0xeb, 0x6f, 0x0a, 0xcc, 0x2b, 0xb1, 0x6c, 0xaf, 0xac, 0xcb, 0x92, 0xf3, 0x3d, - 0x68, 0xe2, 0xb8, 0xd3, 0xce, 0x92, 0xb3, 0xe4, 0x00, 0x6d, 0xbb, 0xa7, 0xcd, 0x7c, 0x42, 0x72, 0x71, 0xb5, 0xeb, - 0x14, 0xa4, 0x32, 0xaf, 0x62, 0x23, 0x95, 0xe2, 0xbc, 0x73, 0x09, 0x38, 0xdc, 0x4f, 0xf1, 0xff, 0x55, 0xa2, 0xbe, - 0x9b, 0x5f, 0x10, 0xc6, 0x76, 0x52, 0xbf, 0x1c, 0x36, 0x6d, 0x61, 0x76, 0x70, 0xdd, 0xe2, 0xa6, 0xdd, 0x10, 0x55, - 0xd9, 0x5b, 0x6b, 0xbe, 0x34, 0x0f, 0x79, 0x61, 0x39, 0xb3, 0xd0, 0xea, 0xf3, 0xb8, 0x65, 0x44, 0xf9, 0xd4, 0x85, - 0xc3, 0xb1, 0xd0, 0x90, 0xcb, 0x9b, 0x43, 0x5d, 0xe4, 0xc7, 0xa4, 0xed, 0x01, 0x03, 0x49, 0x3d, 0xd1, 0xc6, 0x87, - 0x6e, 0xe6, 0xb6, 0x3b, 0x03, 0xe9, 0x7a, 0x39, 0x0d, 0x25, 0xb3, 0x18, 0xb8, 0x70, 0x34, 0xe6, 0xa9, 0x43, 0xa7, - 0x5d, 0xb1, 0x11, 0xd6, 0x1d, 0x0c, 0x57, 0x62, 0x54, 0x75, 0x18, 0xbb, 0xa6, 0xd5, 0x39, 0x56, 0xaa, 0xc7, 0xde, - 0x67, 0x1d, 0x91, 0xe0, 0x09, 0x05, 0x47, 0x1e, 0x78, 0x86, 0xcf, 0xea, 0xa0, 0xc3, 0xa3, 0x8e, 0xc0, 0xa1, 0xba, - 0x40, 0x5f, 0x1d, 0xc6, 0x40, 0x39, 0x82, 0x50, 0x44, 0xbe, 0x7b, 0xa0, 0x0e, 0xe1, 0x6b, 0x7e, 0x82, 0x99, 0x52, - 0x7a, 0x3e, 0x66, 0x7b, 0xf0, 0xe5, 0x80, 0xfb, 0xfd, 0x17, 0x9e, 0xd2, 0x35, 0x8c, 0xc3, 0x0f, 0xbf, 0xd5, 0x62, - 0xf9, 0xfd, 0x00, 0xf3, 0xed, 0x20, 0xd5, 0x25, 0x1c, 0xe5, 0x2a, 0xc0, 0x1f, 0x6d, 0x19, 0x77, 0x0d, 0x86, 0xf5, - 0x11, 0x14, 0x11, 0x1e, 0x71, 0x30, 0xdc, 0x2c, 0x05, 0x80, 0xe2, 0x3c, 0xac, 0x80, 0xc8, 0x42, 0x34, 0x3f, 0x2f, - 0x97, 0x58, 0x57, 0x65, 0x68, 0x4b, 0x4b, 0x36, 0x8f, 0x13, 0x31, 0x6c, 0x26, 0x49, 0x25, 0x44, 0xaf, 0x88, 0x18, - 0x11, 0x33, 0x43, 0xeb, 0xa5, 0xfd, 0x9e, 0xba, 0x2a, 0x08, 0xa3, 0xd6, 0x6d, 0xb8, 0xd7, 0xf5, 0x55, 0x2f, 0x6a, - 0xb5, 0x5f, 0x6b, 0x65, 0x00, 0xfb, 0x96, 0x7c, 0x80, 0x22, 0x09, 0x5b, 0xda, 0xf1, 0x6f, 0x07, 0x72, 0xd1, 0x3f, - 0x84, 0xb0, 0x89, 0x4d, 0x90, 0x73, 0x78, 0xa9, 0x75, 0xf6, 0x36, 0x10, 0xc2, 0x24, 0xd6, 0x6a, 0x3d, 0x82, 0x17, - 0x4d, 0x00, 0xa9, 0xd0, 0x3e, 0x63, 0xf9, 0x88, 0x54, 0x9c, 0x3f, 0x1f, 0x5a, 0x36, 0xb7, 0x3f, 0xe5, 0x13, 0x2b, - 0x47, 0x9c, 0xad, 0x5f, 0x2c, 0x49, 0x56, 0xf0, 0x5d, 0x22, 0xc1, 0x37, 0x96, 0xa1, 0xfa, 0xb4, 0x0f, 0xa0, 0x49, - 0x21, 0xd0, 0xc1, 0xe5, 0x5d, 0x8d, 0x0c, 0xb5, 0x58, 0x46, 0x75, 0xb4, 0xc7, 0x22, 0xd3, 0x17, 0x2f, 0xca, 0xea, - 0xb3, 0x08, 0x0d, 0x27, 0x16, 0xc3, 0x28, 0x95, 0x5e, 0x6c, 0xd1, 0xc6, 0x9f, 0xf4, 0x3f, 0xe6, 0x06, 0xa5, 0xea, - 0x78, 0x85, 0x5b, 0x35, 0x54, 0x87, 0xae, 0xd0, 0x1b, 0xd9, 0xca, 0xb1, 0x7f, 0x79, 0x67, 0x51, 0xc7, 0x9a, 0x36, - 0x08, 0x5e, 0x07, 0xfd, 0xcd, 0x14, 0x9c, 0xec, 0xfc, 0x9a, 0xe8, 0x14, 0x06, 0x08, 0x0a, 0x66, 0x08, 0xf6, 0x19, - 0xcd, 0xa6, 0xa5, 0x74, 0x67, 0xcd, 0x89, 0x3a, 0x36, 0xce, 0x8c, 0xb2, 0x76, 0x29, 0x9e, 0xda, 0x78, 0xeb, 0x05, - 0x3d, 0xf8, 0x5a, 0xbc, 0x58, 0x71, 0x52, 0x5b, 0x46, 0xc4, 0x0b, 0x8e, 0x87, 0xeb, 0x98, 0x43, 0xb5, 0x71, 0x6b, - 0xc1, 0x84, 0x09, 0xad, 0x86, 0xcd, 0xce, 0x5a, 0x4e, 0xf9, 0x5a, 0x1e, 0x27, 0x95, 0x4b, 0x7f, 0xac, 0x90, 0x00, - 0x08, 0x1d, 0x2d, 0x22, 0x09, 0x7c, 0x56, 0x18, 0xc6, 0x1c, 0x0f, 0x92, 0x25, 0xf3, 0x63, 0x25, 0x8f, 0x00, 0x33, - 0x31, 0x5c, 0xbc, 0x0d, 0xbd, 0x7a, 0x82, 0x2e, 0xd9, 0xc1, 0x46, 0xdd, 0x20, 0x08, 0x12, 0xec, 0x00, 0x7f, 0xe1, - 0x7d, 0x17, 0x26, 0xe7, 0xfc, 0x66, 0xeb, 0xf0, 0xff, 0x04, 0x4f, 0xe6, 0x61, 0x6d, 0xbb, 0x1f, 0x6c, 0xd4, 0x97, - 0xff, 0x3f, 0xd5, 0x35, 0xb4, 0x0e, 0x7c, 0xf8, 0xc0, 0x85, 0xc7, 0xab, 0x55, 0x3d, 0x5a, 0x6d, 0xed, 0x80, 0x21, - 0x99, 0x38, 0x51, 0x56, 0xec, 0xa8, 0xde, 0xa1, 0xee, 0x1f, 0x1c, 0xee, 0x8f, 0x1c, 0xa0, 0xfe, 0xc1, 0xc4, 0xdb, - 0x48, 0x23, 0xdd, 0xfd, 0x22, 0x64, 0x62, 0x3d, 0xea, 0x20, 0x57, 0x29, 0xfd, 0xfc, 0xdc, 0xb3, 0x75, 0x84, 0xa5, - 0xab, 0x74, 0x70, 0x7f, 0xd1, 0x59, 0x7b, 0xb0, 0xc1, 0xe5, 0x8b, 0xec, 0x56, 0xad, 0x7d, 0x52, 0xba, 0xca, 0x1a, - 0x6f, 0x02, 0x10, 0x60, 0xab, 0xcc, 0x64, 0xe5, 0xe9, 0x9e, 0x52, 0xc2, 0xbb, 0xd6, 0xe6, 0xec, 0xaf, 0xd7, 0xc1, - 0x29, 0x63, 0x6d, 0x77, 0x71, 0x6b, 0xbd, 0x00, 0x41, 0x39, 0xf7, 0x1a, 0xca, 0x29, 0x84, 0x78, 0x49, 0x0d, 0x2e, - 0x87, 0xb1, 0xf1, 0x10, 0x39, 0x72, 0x88, 0x6e, 0x23, 0x82, 0x75, 0x95, 0xb6, 0x2a, 0x8e, 0xbd, 0x96, 0x27, 0x66, - 0x0b, 0xe3, 0x26, 0xa6, 0x14, 0x16, 0x15, 0x18, 0x79, 0x1a, 0x76, 0x38, 0xdb, 0x11, 0x7a, 0x34, 0x6b, 0x5b, 0x90, - 0x26, 0xec, 0x97, 0xfa, 0x7d, 0x58, 0x82, 0xb1, 0xf9, 0xaa, 0x85, 0xd0, 0x4b, 0xe0, 0x34, 0x79, 0x6f, 0xc8, 0xaf, - 0x2e, 0xf4, 0x8c, 0x70, 0x59, 0x24, 0xf7, 0x58, 0x08, 0x42, 0x65, 0x6b, 0xbb, 0x4c, 0xba, 0x99, 0x63, 0x88, 0xe7, - 0x19, 0x63, 0x68, 0xe1, 0x05, 0x81, 0x4c, 0x13, 0x94, 0x32, 0xfc, 0x16, 0xe1, 0x71, 0x86, 0xb3, 0x43, 0x6e, 0xa6, - 0xc3, 0x48, 0xb8, 0xc2, 0xed, 0x04, 0x99, 0xa5, 0xf9, 0x44, 0xe9, 0xc7, 0x54, 0x75, 0xd8, 0x67, 0x26, 0x21, 0x6a, - 0x8f, 0x58, 0x8f, 0xa7, 0x34, 0x6c, 0x67, 0xfc, 0x9c, 0xe7, 0x52, 0x6c, 0x20, 0xee, 0xae, 0xdc, 0x25, 0xd7, 0xc4, - 0x29, 0xb1, 0xb4, 0xca, 0x38, 0xa4, 0xd0, 0x8e, 0x85, 0xb6, 0xf1, 0x90, 0x1e, 0x44, 0xda, 0xae, 0x0f, 0x49, 0x95, - 0x4e, 0x1e, 0xf3, 0x23, 0x62, 0xc8, 0x4c, 0xbf, 0xc0, 0xda, 0xfe, 0x72, 0xf3, 0x21, 0x04, 0x2a, 0x12, 0x3b, 0x77, - 0x04, 0x3e, 0x0d, 0xb0, 0x79, 0x29, 0x2d, 0x85, 0x56, 0x85, 0xce, 0x55, 0x5b, 0xbd, 0x34, 0x94, 0x0d, 0x51, 0x04, - 0x92, 0x59, 0x96, 0xf0, 0x51, 0xd6, 0x30, 0xc8, 0xa9, 0xdf, 0x35, 0x20, 0xdb, 0x1e, 0x06, 0xeb, 0x7b, 0xaa, 0x2c, - 0xf5, 0xfd, 0xd9, 0x4f, 0x9f, 0xf0, 0xb1, 0x0e, 0x61, 0x95, 0x01, 0xd7, 0x6c, 0x5e, 0xe3, 0xa1, 0x77, 0x9f, 0xcc, - 0xa0, 0x7e, 0xc2, 0x91, 0xbe, 0xc1, 0xd7, 0xc8, 0xfd, 0xcc, 0xcb, 0xf2, 0xca, 0x7b, 0x49, 0x9e, 0x6d, 0x53, 0xea, - 0x27, 0x2d, 0x56, 0xbc, 0x81, 0x3f, 0x75, 0x7e, 0x68, 0xc5, 0xf7, 0x65, 0x75, 0x67, 0xdb, 0x99, 0x33, 0x2c, 0x33, - 0xd8, 0x83, 0x19, 0xba, 0xeb, 0xa3, 0x56, 0x2a, 0xa4, 0x5e, 0xe9, 0xcb, 0x07, 0xef, 0x53, 0xef, 0x53, 0x26, 0x4d, - 0x74, 0x13, 0x98, 0xa2, 0xd2, 0xd7, 0x21, 0xca, 0x0e, 0x69, 0x62, 0xda, 0xa1, 0x4a, 0x14, 0x1d, 0x5e, 0x98, 0x65, - 0x29, 0xc0, 0xf0, 0x8d, 0xe5, 0x53, 0x86, 0x6b, 0x25, 0xa9, 0x20, 0xd4, 0x1a, 0xc4, 0x67, 0x93, 0xe9, 0x7d, 0x99, - 0x1b, 0x0a, 0x58, 0xb0, 0xf8, 0x3a, 0x86, 0x5d, 0xa4, 0x7f, 0x38, 0x7e, 0x27, 0xc1, 0x39, 0xe1, 0x70, 0x64, 0x03, - 0x01, 0x94, 0x69, 0xbb, 0xe0, 0xe2, 0x7e, 0x83, 0x3d, 0xcc, 0xd2, 0x7f, 0x42, 0x6a, 0xc1, 0x69, 0xa0, 0x97, 0xe8, - 0xff, 0xba, 0x33, 0x7f, 0x2a, 0x5f, 0x2f, 0x2c, 0xe6, 0x44, 0xb8, 0xc5, 0xd9, 0x57, 0x96, 0x59, 0xe5, 0x8a, 0xfb, - 0x03, 0x23, 0x13, 0xad, 0x5d, 0x9f, 0x1f, 0xac, 0x56, 0xd4, 0x2a, 0xd4, 0xd0, 0x57, 0xee, 0x7f, 0xa6, 0x7b, 0xb9, - 0x67, 0xc6, 0x3c, 0x14, 0x73, 0x87, 0x75, 0xd1, 0xd0, 0xf8, 0x0c, 0xd1, 0x10, 0xa5, 0xc6, 0x6a, 0xc0, 0x66, 0x4c, - 0xea, 0xd7, 0x83, 0x08, 0x4b, 0xe9, 0x9c, 0x18, 0x55, 0x6a, 0x91, 0x41, 0x82, 0xc9, 0xf1, 0x5c, 0xda, 0x1c, 0x0a, - 0x44, 0xd0, 0xcc, 0x6b, 0x68, 0xf4, 0x35, 0x1e, 0x56, 0xb8, 0xd1, 0xcd, 0x1e, 0x31, 0x64, 0x04, 0x41, 0x65, 0xd9, - 0x18, 0xd9, 0x2e, 0x46, 0x51, 0x38, 0xf5, 0xb3, 0x43, 0x41, 0xf1, 0xcb, 0x99, 0x2f, 0x4d, 0x76, 0xdc, 0x3d, 0x1a, - 0x80, 0xa2, 0x58, 0x97, 0x78, 0xd9, 0x66, 0x22, 0x37, 0xb9, 0xc1, 0x94, 0x20, 0x88, 0x39, 0xfc, 0x09, 0xb2, 0xa4, - 0x88, 0xe9, 0x22, 0x6e, 0x2e, 0xcd, 0xc5, 0xa8, 0x4c, 0x76, 0xf5, 0xc0, 0x6d, 0x68, 0x54, 0xab, 0x89, 0x5e, 0x6b, - 0xdd, 0x0f, 0x0a, 0xd1, 0x09, 0x8b, 0x27, 0xf2, 0x8a, 0x85, 0x48, 0x82, 0x81, 0x00, 0x45, 0xdb, 0xc2, 0x28, 0x0a, - 0xbd, 0x16, 0xd3, 0xd9, 0x72, 0x7e, 0x2e, 0xd3, 0x50, 0x34, 0x3a, 0xe3, 0x96, 0x81, 0x55, 0x3f, 0x6d, 0x96, 0x1f, - 0x78, 0xfc, 0x4f, 0x62, 0xc2, 0xdb, 0x1e, 0x7a, 0x06, 0xe2, 0x53, 0x0f, 0x28, 0xd3, 0x5d, 0x02, 0x85, 0xe9, 0xb9, - 0x8b, 0x50, 0x22, 0x29, 0xea, 0xc6, 0x9c, 0x58, 0x72, 0x1f, 0x95, 0xf8, 0xbe, 0x1a, 0x1f, 0xc2, 0x11, 0xd5, 0x87, - 0xc4, 0x15, 0x05, 0x2c, 0xf2, 0xac, 0x64, 0xf3, 0x39, 0xcb, 0xb8, 0x0e, 0x8b, 0x35, 0x73, 0xce, 0x1b, 0x5e, 0x96, - 0xfa, 0xa0, 0x64, 0x04, 0xef, 0x07, 0x73, 0x08, 0x55, 0xae, 0xc8, 0x0c, 0xf9, 0x55, 0x89, 0xda, 0x0a, 0x58, 0xe3, - 0x0a, 0xc2, 0x6c, 0xed, 0x15, 0xaf, 0x6f, 0x8b, 0x95, 0xfe, 0x40, 0xae, 0x11, 0xf7, 0x70, 0x08, 0x00, 0x0c, 0xfb, - 0xdd, 0x09, 0x8a, 0x91, 0x0a, 0x17, 0xe6, 0x62, 0x68, 0x40, 0xc2, 0x36, 0x48, 0x99, 0xed, 0xc7, 0xf9, 0xf0, 0xee, - 0x9f, 0xd6, 0x9c, 0x1d, 0xac, 0x95, 0x70, 0xed, 0xe8, 0x2a, 0x13, 0xe4, 0xe5, 0x43, 0x94, 0x9d, 0xb9, 0x6d, 0xae, - 0x96, 0x05, 0x51, 0x69, 0x31, 0x9e, 0xad, 0xc4, 0xfd, 0x32, 0x85, 0xc7, 0xce, 0x22, 0xd8, 0x99, 0x97, 0xe0, 0x12, - 0x10, 0x7d, 0x90, 0xf1, 0x91, 0x0d, 0x12, 0xbd, 0xf2, 0x6c, 0xfc, 0x59, 0x75, 0xef, 0x51, 0x9b, 0x2a, 0x52, 0xbb, - 0x1e, 0xb8, 0x3b, 0x94, 0xa4, 0x82, 0x69, 0x77, 0x03, 0xd6, 0xcc, 0xeb, 0x89, 0xc9, 0x37, 0x0a, 0xe2, 0x06, 0x38, - 0xfb, 0x6e, 0x1c, 0x68, 0x1a, 0x58, 0x6f, 0x3e, 0xa2, 0x68, 0x10, 0x6a, 0x44, 0x9c, 0x9b, 0xf5, 0xfa, 0xa5, 0xdf, - 0x81, 0x00, 0xa4, 0x60, 0x56, 0x12, 0xbc, 0x77, 0xe5, 0xa4, 0x10, 0x84, 0xd8, 0x02, 0x88, 0xe9, 0x06, 0x12, 0xc7, - 0x11, 0xe5, 0x1a, 0xcf, 0xbe, 0x59, 0x7a, 0xf4, 0xa2, 0x23, 0x76, 0x7f, 0x09, 0xac, 0xe9, 0x65, 0x07, 0xdb, 0xb9, - 0x09, 0x49, 0x85, 0x32, 0xf4, 0xaa, 0x9e, 0xdd, 0xb0, 0xb9, 0x75, 0x2c, 0x0b, 0x3d, 0x7a, 0x08, 0x72, 0xc9, 0xbc, - 0xb7, 0xd5, 0x18, 0x08, 0xa5, 0x9b, 0x5f, 0x08, 0x14, 0x47, 0xeb, 0x5f, 0x68, 0x93, 0x0c, 0x6d, 0x9c, 0xdb, 0xb4, - 0xb3, 0x66, 0xb7, 0x29, 0xac, 0x5c, 0x72, 0x73, 0xbd, 0x38, 0xa6, 0xa8, 0xa7, 0xf2, 0xbd, 0xd6, 0xb2, 0x67, 0x63, - 0xa0, 0x86, 0xf6, 0xc8, 0x27, 0x85, 0xd0, 0x1b, 0xb6, 0x62, 0x69, 0x24, 0x93, 0xe6, 0xce, 0x3b, 0x27, 0xd4, 0xe6, - 0x61, 0x87, 0xc4, 0x09, 0x73, 0xeb, 0xbf, 0xcf, 0x23, 0x29, 0x8b, 0xf7, 0x29, 0x14, 0x6e, 0x86, 0xea, 0x86, 0xb1, - 0xe8, 0x38, 0x01, 0xb7, 0x95, 0xf5, 0xd3, 0x8c, 0x44, 0xac, 0x16, 0x16, 0xc6, 0x33, 0x80, 0xa9, 0x98, 0x22, 0x6e, - 0x55, 0x30, 0xd4, 0x20, 0x39, 0x57, 0x83, 0x60, 0xa6, 0xd7, 0x8c, 0x9d, 0x79, 0x99, 0xb7, 0xd0, 0xd6, 0xc6, 0x2c, - 0x2c, 0xd4, 0x6c, 0x4c, 0xcd, 0x93, 0x49, 0x01, 0x4b, 0x23, 0xe8, 0xf6, 0x98, 0x1e, 0xee, 0xae, 0x91, 0xef, 0x96, - 0x23, 0x67, 0x17, 0x83, 0xf9, 0xd8, 0xcb, 0xec, 0x71, 0xea, 0xc1, 0xcb, 0x04, 0x33, 0x42, 0x85, 0xad, 0xe2, 0x02, - 0xda, 0xb3, 0xa6, 0xff, 0xc0, 0x37, 0xf1, 0x31, 0x07, 0x37, 0x66, 0xec, 0xad, 0x59, 0xba, 0xe2, 0x3d, 0x1d, 0x23, - 0x64, 0x11, 0x23, 0xf2, 0x9c, 0x35, 0xc5, 0xdc, 0x4a, 0x15, 0xe3, 0x1e, 0x14, 0x82, 0xe5, 0x2b, 0x4c, 0x05, 0x10, - 0x0e, 0x66, 0x37, 0x1a, 0x1c, 0x62, 0x7d, 0xdc, 0xba, 0x5b, 0x20, 0x04, 0x06, 0x50, 0x5d, 0x9c, 0x73, 0x34, 0xd1, - 0x01, 0x90, 0xfc, 0x3e, 0x12, 0x00, 0x49, 0x60, 0x86, 0x22, 0x01, 0x46, 0xaf, 0x5a, 0xfa, 0x9a, 0x17, 0x6b, 0x8c, - 0x8c, 0xd8, 0x23, 0x08, 0xb6, 0x72, 0x8f, 0x2c, 0x90, 0x66, 0x73, 0xaf, 0x75, 0xc2, 0xb7, 0x67, 0x45, 0x25, 0x0e, - 0x2e, 0xbf, 0x2a, 0x23, 0xe9, 0x9f, 0x0c, 0x6a, 0xac, 0x63, 0xe5, 0x29, 0xfd, 0x98, 0xa9, 0xa3, 0x47, 0x77, 0x69, - 0x9a, 0x4e, 0xe6, 0xa0, 0xd8, 0x43, 0x36, 0x28, 0xab, 0x64, 0xec, 0xc4, 0x39, 0x74, 0x22, 0xa9, 0x7f, 0x1c, 0xbd, - 0xbc, 0x53, 0x8f, 0xa2, 0x34, 0xb7, 0xeb, 0x09, 0xb5, 0x72, 0xaa, 0xdd, 0x08, 0xbc, 0x49, 0x79, 0x26, 0x75, 0xc6, - 0x96, 0xfa, 0xa5, 0x42, 0x2a, 0x3b, 0x35, 0x26, 0xb1, 0x93, 0xf3, 0x32, 0xe7, 0xe8, 0x29, 0xbf, 0x10, 0xc6, 0x81, - 0xb1, 0x3f, 0x9d, 0xb6, 0xde, 0xaf, 0xd9, 0x19, 0xe2, 0xf1, 0x6a, 0xaa, 0xf6, 0x21, 0x5d, 0xab, 0x26, 0xa6, 0x40, - 0xd3, 0x9e, 0xa6, 0xff, 0xc9, 0x80, 0x3e, 0x0f, 0xc1, 0x9e, 0xe9, 0x27, 0x21, 0xd5, 0x0e, 0xa2, 0xfd, 0x41, 0x0b, - 0xaf, 0xf0, 0x35, 0x4a, 0xa8, 0xfe, 0xce, 0x09, 0xd0, 0xf1, 0xbd, 0x6e, 0x10, 0x5b, 0x92, 0xb8, 0x98, 0x8b, 0x54, - 0x76, 0x8e, 0x19, 0xd5, 0x40, 0x2e, 0x88, 0x14, 0xcf, 0x75, 0x1a, 0x95, 0x85, 0x2c, 0x79, 0x83, 0x1b, 0x3f, 0xfb, - 0x35, 0x53, 0x28, 0xfc, 0x6a, 0x38, 0x08, 0x58, 0x06, 0x90, 0x30, 0xef, 0x5e, 0x69, 0xce, 0x99, 0x9d, 0x8d, 0x18, - 0xb2, 0x00, 0x2e, 0x75, 0xec, 0x23, 0x74, 0x12, 0x00, 0x10, 0x1d, 0x13, 0x63, 0x20, 0xaf, 0x76, 0x54, 0xff, 0x03, - 0x1c, 0x7a, 0x27, 0xbd, 0x5a, 0x73, 0x37, 0x81, 0x28, 0x42, 0x40, 0x80, 0xc4, 0xde, 0x50, 0x10, 0x45, 0xcb, 0x41, - 0x24, 0x55, 0x62, 0x27, 0xb4, 0x15, 0x9a, 0x05, 0x37, 0xb2, 0x11, 0x69, 0x04, 0xd0, 0x2b, 0xb8, 0x10, 0x33, 0x02, - 0x65, 0x1e, 0x47, 0x1a, 0xbf, 0xa0, 0xc4, 0xcc, 0x4b, 0xc5, 0xe8, 0x73, 0x8a, 0x7a, 0xef, 0x41, 0x74, 0xcf, 0xcd, - 0xa3, 0xd6, 0xc7, 0x84, 0x10, 0x3d, 0x01, 0x6b, 0x28, 0xab, 0x9f, 0xa2, 0x0c, 0x30, 0x1a, 0xa0, 0x6c, 0xef, 0x70, - 0x1e, 0xb0, 0x7c, 0xa9, 0x79, 0x52, 0x0f, 0x1d, 0xf3, 0x88, 0x5c, 0x3a, 0x9f, 0xf7, 0xeb, 0xb8, 0x5e, 0xd4, 0x0e, - 0x2a, 0x04, 0x3c, 0x56, 0x2f, 0xd5, 0x8d, 0x20, 0x37, 0x14, 0xff, 0x45, 0xc5, 0xd4, 0x18, 0xf6, 0x95, 0x5f, 0x4c, - 0x27, 0x1d, 0x6a, 0x87, 0xf5, 0x2e, 0x72, 0x75, 0x2a, 0x4a, 0x00, 0x4e, 0xbb, 0x1d, 0xda, 0x39, 0xb3, 0xe3, 0x6f, - 0x77, 0xfd, 0x68, 0x95, 0x95, 0x6a, 0x51, 0xe7, 0x59, 0x43, 0x41, 0x79, 0x39, 0x1d, 0xff, 0x0b, 0x4f, 0xf6, 0xf2, - 0x64, 0x30, 0xa7, 0x16, 0x61, 0x9c, 0xba, 0xf3, 0xec, 0xfd, 0xc1, 0xdb, 0x61, 0x4b, 0x88, 0x9d, 0xea, 0xe6, 0xd7, - 0x7a, 0xe4, 0x8b, 0xa9, 0xdb, 0x7a, 0x82, 0x1b, 0x37, 0xb7, 0xce, 0xd8, 0xab, 0xc7, 0xd0, 0x31, 0x01, 0xe0, 0xad, - 0x25, 0x8a, 0xa2, 0x22, 0xfc, 0xfb, 0xe3, 0xe9, 0xa1, 0xe6, 0xf4, 0xa0, 0x6f, 0xe3, 0x9d, 0x18, 0x34, 0x05, 0x26, - 0x58, 0x07, 0x0c, 0xf3, 0x01, 0xfd, 0xae, 0xa4, 0x1a, 0xdf, 0xf7, 0xa2, 0xc8, 0x41, 0x4c, 0x66, 0xf0, 0xa5, 0xf1, - 0x4d, 0x56, 0x64, 0x7c, 0x51, 0x01, 0xda, 0xbc, 0x4c, 0xb6, 0x0b, 0xc7, 0x30, 0x85, 0x69, 0xb7, 0xee, 0x7b, 0xec, - 0xa0, 0x5c, 0xdc, 0x1a, 0xc6, 0x6f, 0x24, 0x82, 0xec, 0x8d, 0x65, 0xe6, 0x03, 0xc5, 0xaf, 0x9f, 0x3a, 0x26, 0xb9, - 0xe7, 0x0d, 0xf7, 0x87, 0xa8, 0xa9, 0xd0, 0xf9, 0x5c, 0x79, 0xb7, 0x9c, 0xdf, 0x85, 0xd7, 0xaa, 0x50, 0x77, 0x64, - 0xc9, 0x48, 0xd3, 0x69, 0xe8, 0xe9, 0x5a, 0x2d, 0xda, 0x9c, 0xb8, 0x0e, 0xda, 0xb6, 0xd8, 0x04, 0x12, 0x4b, 0xce, - 0x60, 0xa6, 0xe4, 0x4d, 0x2c, 0x08, 0x0e, 0x1d, 0x41, 0xa1, 0xbb, 0x28, 0x0e, 0x91, 0x30, 0x60, 0xb3, 0x43, 0x85, - 0xdd, 0x47, 0xb0, 0xf1, 0xd5, 0xbc, 0x50, 0xe4, 0x19, 0xab, 0xc2, 0x9c, 0xa9, 0x66, 0x56, 0xb5, 0x5f, 0x0c, 0x70, - 0xf6, 0x4f, 0xb8, 0x97, 0x4e, 0x0c, 0xd6, 0x83, 0x4c, 0x49, 0x0d, 0x9d, 0x72, 0x8a, 0x6a, 0x1e, 0xb1, 0x8d, 0x35, - 0x14, 0x50, 0x3b, 0x3c, 0x32, 0x1c, 0x6e, 0x7a, 0x6f, 0x7c, 0x3e, 0xf0, 0x2c, 0x36, 0xf0, 0xce, 0x21, 0xb0, 0x10, - 0xfb, 0x51, 0xbb, 0x38, 0xb4, 0x35, 0x9b, 0xa1, 0xb0, 0x8d, 0x86, 0x42, 0x3a, 0xb3, 0x17, 0x24, 0xd3, 0x41, 0x1a, - 0x48, 0x5b, 0x87, 0x89, 0xc6, 0xde, 0x57, 0x07, 0xba, 0x03, 0x8d, 0x37, 0x3d, 0xa2, 0xd0, 0xc6, 0xae, 0x1a, 0xc0, - 0x2b, 0x3a, 0x97, 0xe8, 0x66, 0xdf, 0x12, 0xd8, 0xaf, 0x36, 0x83, 0x1b, 0xcb, 0x97, 0x00, 0xa6, 0x14, 0x90, 0x6d, - 0xd9, 0xf8, 0xdc, 0x73, 0x3e, 0x90, 0x4d, 0x98, 0x1c, 0x8d, 0xa3, 0x76, 0x11, 0x76, 0x69, 0x8d, 0xb7, 0x1c, 0x4d, - 0xf5, 0xcf, 0xa5, 0x08, 0x0b, 0xac, 0x2e, 0x98, 0xb6, 0xc5, 0x47, 0x23, 0xac, 0x49, 0x51, 0xb7, 0x87, 0x05, 0xd4, - 0xd8, 0x61, 0x21, 0x95, 0xd6, 0x6b, 0x89, 0x8b, 0x95, 0x18, 0x5f, 0xd3, 0x53, 0x28, 0x0e, 0x2c, 0x3b, 0x47, 0x40, - 0xe3, 0xc1, 0x3a, 0xdf, 0x0a, 0x5f, 0x2a, 0xdc, 0x2f, 0xe5, 0xa8, 0xe4, 0x99, 0x26, 0xce, 0xf5, 0x02, 0xce, 0x08, - 0x89, 0xcb, 0x0f, 0xd8, 0x38, 0x96, 0x00, 0x5b, 0xa6, 0xf7, 0xff, 0x50, 0x87, 0x05, 0xdb, 0x21, 0xc0, 0x2b, 0xee, - 0xa2, 0x7d, 0xa0, 0x5c, 0x01, 0xa4, 0xc9, 0x51, 0x86, 0x5c, 0x7e, 0xd6, 0xbd, 0x42, 0xc6, 0xb4, 0x4f, 0xa2, 0x63, - 0xcb, 0x76, 0x76, 0x20, 0x99, 0x23, 0x43, 0xc2, 0xb8, 0xf5, 0x2a, 0x65, 0xe1, 0x6e, 0x80, 0x63, 0x44, 0xb1, 0xb4, - 0x62, 0x7e, 0x99, 0x29, 0xf2, 0x0a, 0xd8, 0x8d, 0x93, 0xa6, 0xbd, 0x36, 0xa6, 0x4b, 0x64, 0x63, 0x30, 0x76, 0xaa, - 0x08, 0x2c, 0x8c, 0x41, 0x55, 0xb2, 0x20, 0xfc, 0x63, 0x4f, 0x38, 0xe0, 0x7f, 0x72, 0x26, 0x28, 0x3f, 0x1e, 0xcb, - 0x79, 0x52, 0x61, 0x1e, 0xc8, 0x14, 0xf6, 0x70, 0xfa, 0xd2, 0xbf, 0xa2, 0x4f, 0xa9, 0xc0, 0x9a, 0x24, 0xc2, 0xb8, - 0x01, 0x06, 0x55, 0x1b, 0x00, 0x74, 0x83, 0x14, 0x6f, 0x12, 0xd0, 0xe4, 0x26, 0xb4, 0x0a, 0xe5, 0x28, 0x1d, 0xb0, - 0x52, 0xe4, 0x66, 0xd4, 0x14, 0xc1, 0x46, 0xda, 0x41, 0x8a, 0x12, 0x0c, 0x3b, 0xa9, 0x06, 0x69, 0x95, 0x7a, 0x38, - 0x08, 0x7f, 0x4d, 0x80, 0x0e, 0x40, 0x1e, 0x96, 0xff, 0x65, 0x32, 0xc2, 0xcb, 0x23, 0x2b, 0xb9, 0x47, 0xcd, 0x51, - 0x63, 0x62, 0x1a, 0x3a, 0xb9, 0xa5, 0x13, 0xde, 0xd5, 0x1c, 0x71, 0x36, 0x0e, 0x6a, 0xab, 0x9a, 0x0f, 0x06, 0x43, - 0xa7, 0x4e, 0x3a, 0x82, 0xc2, 0x47, 0x39, 0x06, 0x13, 0xda, 0xcd, 0x14, 0x3d, 0x0f, 0x7b, 0x99, 0x97, 0x93, 0x6e, - 0x36, 0x53, 0x00, 0xb6, 0x5a, 0x0a, 0x5b, 0x86, 0x81, 0x31, 0x8c, 0x3f, 0x02, 0x72, 0xc3, 0xa7, 0xcf, 0x4b, 0x23, - 0x8f, 0x4a, 0x2f, 0x6f, 0x7e, 0xf8, 0xf8, 0x31, 0x80, 0xc1, 0x50, 0xd1, 0xe0, 0xc3, 0x67, 0x7d, 0x35, 0xbe, 0x93, - 0xc9, 0xc6, 0x1a, 0xe3, 0x1c, 0x44, 0x79, 0x16, 0xda, 0x91, 0x9f, 0x95, 0x75, 0x51, 0x0c, 0xdb, 0xd7, 0x17, 0x95, - 0xd7, 0x97, 0x22, 0xa4, 0x6a, 0x41, 0x2d, 0x6f, 0x71, 0x8a, 0x8d, 0x43, 0x28, 0x34, 0xe6, 0xa0, 0x08, 0x19, 0x8e, - 0x22, 0x6e, 0xee, 0x34, 0x14, 0x03, 0x52, 0x22, 0x12, 0xe6, 0xd4, 0x69, 0xed, 0x6d, 0x4e, 0xb0, 0xd9, 0x3b, 0x53, - 0x4b, 0x0d, 0xaf, 0x31, 0x61, 0x65, 0xe7, 0x48, 0x91, 0xc0, 0xa5, 0x2d, 0xbb, 0xb5, 0xc8, 0x54, 0xdd, 0x8d, 0x86, - 0xcc, 0x99, 0x15, 0x14, 0xab, 0x97, 0xcf, 0x0a, 0x87, 0x56, 0x50, 0xfc, 0xa6, 0xc1, 0x06, 0xc4, 0x38, 0x76, 0x7f, - 0x7c, 0xae, 0x82, 0x7f, 0xf8, 0x39, 0xdc, 0x93, 0x3f, 0xa6, 0x17, 0xac, 0x52, 0xcc, 0x06, 0x35, 0xdf, 0x25, 0xca, - 0xf4, 0xda, 0x9c, 0x09, 0x4c, 0x1c, 0xf3, 0x82, 0x1e, 0x3e, 0x4b, 0x5f, 0x14, 0xa3, 0xcd, 0xa0, 0x22, 0x65, 0x52, - 0x01, 0x44, 0x34, 0xe9, 0x0e, 0xa9, 0xd7, 0x58, 0xa4, 0x2c, 0x9b, 0x62, 0x9b, 0xe6, 0x4a, 0x6d, 0x1f, 0x3b, 0x6a, - 0x6a, 0xdd, 0x90, 0x48, 0x3c, 0xa4, 0xf9, 0x0f, 0xc5, 0xd7, 0x31, 0x16, 0x84, 0x48, 0x21, 0xad, 0x2f, 0xce, 0x74, - 0x7a, 0x7c, 0x36, 0x8a, 0x3b, 0x1a, 0xc7, 0xa3, 0xfc, 0x6b, 0x8d, 0xe9, 0x54, 0xb0, 0x1f, 0xd2, 0x19, 0x12, 0x47, - 0x9e, 0x1b, 0x17, 0xdd, 0xad, 0xcf, 0x3a, 0x7c, 0x10, 0xb5, 0xbc, 0xe4, 0x1d, 0xbb, 0x21, 0x54, 0x32, 0x98, 0xeb, - 0x94, 0x40, 0x6b, 0xea, 0x0f, 0xfc, 0x0d, 0x5f, 0xb8, 0x51, 0x5d, 0x3a, 0x24, 0x5a, 0xd8, 0x90, 0x60, 0x3a, 0x49, - 0x8a, 0xb4, 0xe0, 0x12, 0x26, 0x9b, 0xa0, 0x58, 0xbb, 0xb0, 0xe0, 0xb3, 0xb0, 0x38, 0x64, 0xf3, 0x8b, 0x2e, 0xc4, - 0x78, 0x07, 0xd6, 0x19, 0xaa, 0x3c, 0x73, 0x8e, 0x41, 0x33, 0x78, 0x61, 0x61, 0xaf, 0x0a, 0x72, 0x88, 0x0b, 0xeb, - 0x80, 0xca, 0xc7, 0xa8, 0x91, 0xf1, 0xe8, 0xad, 0x4d, 0x21, 0x3d, 0xd0, 0x7d, 0xff, 0xce, 0x6f, 0xaf, 0x22, 0x67, - 0x60, 0x88, 0x0b, 0x91, 0x17, 0x1e, 0xcd, 0x6c, 0x70, 0x81, 0x71, 0xe1, 0x6d, 0x8e, 0x7a, 0xf1, 0x1c, 0xce, 0xdb, - 0x37, 0x54, 0x6a, 0x78, 0xc5, 0x94, 0x8e, 0x13, 0x14, 0xdc, 0xa4, 0x97, 0x15, 0xc8, 0xbb, 0x93, 0xb4, 0xd8, 0x35, - 0xbb, 0x14, 0xb2, 0x21, 0x45, 0x67, 0xed, 0x6e, 0x70, 0xca, 0xdc, 0x9e, 0x49, 0x87, 0x65, 0x4e, 0xd1, 0xcc, 0x24, - 0x0a, 0x3d, 0x87, 0x28, 0xc6, 0x8c, 0xa1, 0x49, 0xb5, 0x65, 0x5e, 0xd4, 0x92, 0x0d, 0x95, 0xba, 0x46, 0x50, 0xd6, - 0xcd, 0x91, 0xfd, 0x73, 0xe6, 0xe3, 0xdc, 0xb9, 0xc1, 0xd0, 0x03, 0x79, 0x18, 0x90, 0xb1, 0x3a, 0xc7, 0xfd, 0x85, - 0x8f, 0x69, 0xde, 0xed, 0x5e, 0x73, 0xfc, 0x6b, 0x04, 0x6d, 0x99, 0x7c, 0xd3, 0xfa, 0x07, 0xd5, 0xff, 0x65, 0x03, - 0x46, 0xd6, 0x3a, 0x3e, 0x2c, 0x36, 0xad, 0x37, 0x55, 0x4d, 0x76, 0xd0, 0xd8, 0x99, 0x26, 0x6d, 0x2c, 0x1c, 0xd4, - 0xdd, 0xdb, 0xc8, 0x24, 0x38, 0x6c, 0xde, 0x1c, 0xc2, 0x40, 0x56, 0xc6, 0x77, 0x1b, 0xa1, 0x75, 0xeb, 0xb4, 0xa9, - 0xc3, 0x2f, 0x43, 0x13, 0x0f, 0x7b, 0x8d, 0x56, 0x0c, 0x61, 0x9e, 0x4d, 0x19, 0xbb, 0x02, 0xde, 0x14, 0x41, 0x11, - 0x4f, 0xe3, 0x9a, 0x23, 0x98, 0xd2, 0x6a, 0x60, 0xc8, 0xaa, 0x11, 0xcf, 0x51, 0xa5, 0x6b, 0xd4, 0x73, 0xfb, 0xa6, - 0x07, 0x0c, 0xb9, 0x90, 0xb3, 0x5f, 0x4e, 0x6b, 0x42, 0x67, 0xeb, 0x76, 0xf4, 0x18, 0xf0, 0x0a, 0x91, 0xe8, 0xf3, - 0x41, 0x96, 0x01, 0xb1, 0xc5, 0x64, 0xa5, 0x43, 0x21, 0xc1, 0xe3, 0x76, 0xf8, 0x8c, 0xd5, 0xa7, 0xbc, 0xa7, 0x4f, - 0x59, 0xec, 0x86, 0xa6, 0xd6, 0xc1, 0xdf, 0xa9, 0x12, 0x22, 0x05, 0xae, 0xa5, 0xba, 0xcb, 0x90, 0x55, 0xa5, 0x1e, - 0xfd, 0x41, 0x91, 0x96, 0x46, 0xac, 0xc4, 0x52, 0xa9, 0x1a, 0xd3, 0xfa, 0xef, 0xb4, 0x15, 0x9d, 0xa8, 0xff, 0xb4, - 0xb0, 0x5a, 0x7d, 0x45, 0xac, 0xab, 0x84, 0xe3, 0x45, 0xaf, 0xb6, 0xe8, 0xc8, 0x10, 0x41, 0x02, 0x9f, 0x75, 0xad, - 0x37, 0x1b, 0x3a, 0x0d, 0x68, 0xbc, 0xcd, 0xe3, 0xbf, 0xea, 0xf9, 0x8c, 0xec, 0x05, 0x9a, 0xae, 0x52, 0x9b, 0x02, - 0x5d, 0x41, 0xe0, 0x9c, 0x25, 0xd6, 0x5c, 0xa5, 0x81, 0x09, 0xa6, 0x79, 0xb1, 0xe5, 0x0b, 0xa8, 0x4e, 0xb7, 0x40, - 0x1a, 0x08, 0xd2, 0x50, 0x7a, 0xa9, 0x55, 0xea, 0x36, 0xa6, 0xd3, 0x41, 0x79, 0xd6, 0x06, 0xa5, 0xf8, 0x01, 0xe5, - 0xc0, 0x95, 0x5b, 0xbd, 0x44, 0x09, 0xb2, 0xea, 0xd0, 0xbc, 0x95, 0xbd, 0x66, 0x1e, 0x52, 0xb8, 0x61, 0x4d, 0xc6, - 0x05, 0x6f, 0xfb, 0xdb, 0xdd, 0x40, 0xe6, 0x28, 0x5a, 0x47, 0x4d, 0xc9, 0x42, 0xf7, 0x88, 0xab, 0x08, 0xe9, 0xe7, - 0x85, 0xd2, 0x2d, 0xb0, 0xd3, 0xed, 0xef, 0xd0, 0xba, 0x5b, 0xd4, 0xc5, 0xf2, 0xd0, 0xc3, 0xce, 0x91, 0x7f, 0x04, - 0xef, 0x8f, 0x02, 0x16, 0xc3, 0x4f, 0x13, 0x65, 0x07, 0x6d, 0xf6, 0xfa, 0x7e, 0xb0, 0x4d, 0xbf, 0xa9, 0xb1, 0x1b, - 0xbc, 0x1d, 0xf0, 0x57, 0x1d, 0xac, 0xc2, 0x61, 0x0f, 0xcc, 0x27, 0x16, 0xbf, 0x3f, 0xbf, 0xd8, 0xd7, 0xe0, 0xf8, - 0x44, 0xb8, 0xcd, 0x54, 0x3e, 0xc0, 0xdb, 0x24, 0xb7, 0x74, 0xbd, 0x30, 0xf2, 0xea, 0x02, 0x52, 0x56, 0xce, 0x89, - 0xeb, 0x8b, 0x02, 0x57, 0xa0, 0x85, 0x12, 0x8f, 0x6e, 0x0b, 0xde, 0xb3, 0x86, 0xb4, 0x77, 0x1b, 0x63, 0xd3, 0x69, - 0xef, 0x38, 0x15, 0x87, 0x99, 0x2c, 0xcd, 0x26, 0xe4, 0xdf, 0xae, 0xa8, 0x53, 0x35, 0x70, 0x9f, 0x5f, 0xd7, 0x57, - 0xb3, 0x74, 0x37, 0xee, 0xe7, 0x4f, 0xd9, 0x9e, 0xb0, 0x95, 0x0d, 0x63, 0x76, 0xc8, 0xef, 0x0b, 0x0d, 0xe8, 0x7a, - 0x34, 0x8b, 0x90, 0xfd, 0x40, 0x80, 0xc1, 0x57, 0xe7, 0x8c, 0xa5, 0x59, 0xd9, 0x37, 0xfd, 0xc2, 0x43, 0x49, 0x41, - 0x8c, 0xca, 0x5e, 0x03, 0x64, 0xb4, 0x24, 0x5b, 0xa7, 0xc5, 0x7b, 0x61, 0x01, 0xa3, 0xef, 0x53, 0xe8, 0x54, 0x9f, - 0x64, 0x08, 0xab, 0x2e, 0xd1, 0x78, 0x4e, 0x7b, 0xc4, 0xd7, 0x79, 0x3e, 0x7c, 0x1d, 0x8b, 0x3d, 0x57, 0x58, 0x66, - 0xd8, 0x4c, 0x8c, 0x43, 0x03, 0xf1, 0xc4, 0xa1, 0xfb, 0x91, 0xd1, 0x62, 0xdc, 0x5a, 0x50, 0xc3, 0xe3, 0x2f, 0xe7, - 0x89, 0xa5, 0xeb, 0xdb, 0x80, 0x0c, 0x53, 0x44, 0x9c, 0x59, 0xd4, 0xeb, 0x8b, 0x6a, 0xd8, 0xbd, 0x2e, 0x2a, 0x08, - 0x9a, 0xcd, 0xb7, 0xf5, 0xe0, 0x06, 0xdb, 0x45, 0xed, 0x87, 0x2c, 0xd1, 0xda, 0xba, 0xdf, 0xb4, 0x1b, 0x64, 0x9f, - 0x33, 0x0e, 0xda, 0x40, 0xc0, 0xf8, 0xde, 0xbf, 0xb4, 0xd5, 0xd9, 0xde, 0x3a, 0xcd, 0x5f, 0x88, 0x8b, 0xbf, 0x93, - 0xb0, 0xbc, 0x9b, 0xe1, 0xa0, 0x94, 0x90, 0xe1, 0x04, 0x11, 0xa6, 0xc2, 0xba, 0xb8, 0xe4, 0xb3, 0x0b, 0x13, 0x2b, - 0x23, 0xac, 0x88, 0x76, 0xc4, 0x37, 0x7c, 0x9f, 0xb7, 0x05, 0x04, 0xb1, 0xb3, 0x65, 0xcc, 0x78, 0x46, 0x44, 0x89, - 0x8c, 0xec, 0x30, 0xb1, 0x69, 0x36, 0x61, 0xea, 0xd8, 0x6d, 0x32, 0x68, 0xea, 0x60, 0x9c, 0xc2, 0x06, 0xba, 0x1b, - 0xaa, 0xad, 0xb6, 0x92, 0x8c, 0xf9, 0x85, 0xac, 0x9d, 0xed, 0x8f, 0xea, 0x65, 0x5e, 0x6c, 0x3f, 0xdb, 0x86, 0xe6, - 0x55, 0x31, 0x86, 0x76, 0x20, 0xb3, 0x23, 0xdc, 0x65, 0xea, 0x1e, 0xd2, 0x78, 0xa2, 0x50, 0x6d, 0x25, 0x08, 0x07, - 0xa0, 0x90, 0xa6, 0x39, 0xe3, 0x02, 0xb3, 0xe8, 0xa3, 0x2a, 0xbc, 0xd3, 0x41, 0x59, 0x2d, 0x0d, 0xa0, 0x04, 0x88, - 0xe3, 0x4e, 0x3a, 0x8c, 0xe4, 0xc1, 0x5e, 0xa9, 0x3b, 0xb5, 0x6a, 0x9d, 0xb9, 0x58, 0xec, 0x47, 0x97, 0x5a, 0xec, - 0x11, 0xa6, 0x59, 0x52, 0xeb, 0x07, 0x5a, 0x68, 0xcf, 0x37, 0x5b, 0x13, 0xf3, 0x94, 0x71, 0x48, 0x7b, 0x68, 0x3f, - 0x14, 0xd4, 0x7a, 0x09, 0x8f, 0x95, 0x41, 0x89, 0x64, 0xa7, 0xa6, 0x9d, 0x95, 0x69, 0x0a, 0xde, 0x65, 0x99, 0x78, - 0x15, 0xfa, 0x1d, 0xe1, 0xdd, 0x96, 0x59, 0xdb, 0xc8, 0xc4, 0xcd, 0x49, 0xa4, 0x58, 0x0e, 0xce, 0x35, 0xbc, 0x2d, - 0x3a, 0x76, 0xf1, 0xdb, 0x4c, 0xad, 0x5f, 0xb0, 0x8c, 0x38, 0x5a, 0xc2, 0xcd, 0x0f, 0xe9, 0x91, 0x88, 0x02, 0xa3, - 0xfe, 0x4d, 0x5e, 0xc5, 0x89, 0x1b, 0x66, 0xfc, 0x8e, 0x86, 0x38, 0x54, 0x87, 0xba, 0x67, 0x89, 0x15, 0x88, 0x85, - 0xf3, 0xf7, 0xd5, 0x4d, 0x20, 0x97, 0xde, 0x56, 0xab, 0xe6, 0x61, 0xd4, 0x65, 0xdf, 0x83, 0x3f, 0x85, 0x31, 0x35, - 0x9f, 0xbc, 0x2a, 0xdf, 0x70, 0xcf, 0x64, 0x29, 0x97, 0xf0, 0xba, 0xde, 0x52, 0x73, 0x9c, 0xcb, 0x37, 0x5c, 0x56, - 0x59, 0x6a, 0x33, 0x82, 0x49, 0xdf, 0x5a, 0xe1, 0x38, 0x82, 0x35, 0x14, 0x91, 0xb8, 0x39, 0xa8, 0x83, 0xcd, 0xb1, - 0x0e, 0xe5, 0x36, 0x13, 0xac, 0x43, 0x36, 0xd8, 0x03, 0xc2, 0xa9, 0xc5, 0x66, 0x8e, 0x7d, 0x6c, 0x08, 0x07, 0x74, - 0xdc, 0x9a, 0x22, 0x4a, 0x4e, 0xdd, 0x89, 0xa7, 0x96, 0xe5, 0xbb, 0x19, 0xd3, 0x74, 0x7c, 0x87, 0x18, 0x59, 0x12, - 0x8b, 0xdc, 0xcb, 0x10, 0x97, 0x43, 0x8b, 0xf6, 0x2c, 0x55, 0x21, 0x37, 0xe8, 0xd6, 0x2d, 0x37, 0x1c, 0x3e, 0x7d, - 0x38, 0x2e, 0x7a, 0x22, 0xda, 0x4a, 0x7c, 0x39, 0x85, 0x54, 0x4a, 0xf6, 0x93, 0x0a, 0x2f, 0x54, 0x48, 0xa7, 0xcf, - 0x8a, 0x04, 0xdc, 0xdc, 0x99, 0x0b, 0x57, 0x53, 0xae, 0x65, 0x8d, 0x76, 0x93, 0x9c, 0x08, 0x15, 0x96, 0xcc, 0x18, - 0xbd, 0x78, 0x3f, 0x8b, 0x16, 0xdb, 0x39, 0xc5, 0x76, 0xd8, 0xd4, 0xd4, 0x79, 0x87, 0x22, 0xa7, 0xa1, 0xb2, 0x8c, - 0xc4, 0x2c, 0xca, 0x21, 0x3e, 0x3a, 0x75, 0xbe, 0xc7, 0x28, 0x75, 0x05, 0x73, 0xaa, 0x4d, 0xc9, 0x83, 0xd3, 0xc5, - 0xf9, 0x17, 0x6e, 0x37, 0xfd, 0xe3, 0x28, 0xe8, 0xb7, 0x5a, 0x35, 0x51, 0xad, 0x1c, 0x9a, 0x5d, 0xd7, 0x6e, 0x34, - 0x26, 0xc7, 0x0e, 0x70, 0x67, 0x13, 0xc4, 0x18, 0x3e, 0x2e, 0xc3, 0x2c, 0x9a, 0xe7, 0x53, 0x7d, 0xfd, 0xf3, 0x20, - 0xca, 0xb6, 0x4c, 0xdd, 0x0a, 0xa3, 0xc0, 0xa5, 0x81, 0x07, 0xe3, 0xa8, 0x21, 0x86, 0xcd, 0xa3, 0xa3, 0x6d, 0x22, - 0x93, 0x74, 0xcf, 0x6a, 0xae, 0x00, 0x4d, 0xa7, 0x20, 0xf2, 0xef, 0x71, 0x9b, 0x2f, 0x38, 0x8e, 0x4e, 0xe9, 0xf9, - 0x17, 0xa5, 0x1f, 0x69, 0xf0, 0xc6, 0x78, 0x75, 0xc1, 0xaa, 0x27, 0x23, 0xef, 0x88, 0x87, 0x39, 0x0a, 0xe4, 0x07, - 0xf0, 0x4d, 0xe9, 0x82, 0x73, 0x63, 0xf7, 0x3d, 0xc8, 0x96, 0xed, 0x68, 0x55, 0xc4, 0x1a, 0xf9, 0x1a, 0x27, 0x2e, - 0xb5, 0xfe, 0x92, 0xcc, 0x3a, 0x09, 0xf6, 0x35, 0xf2, 0x78, 0x47, 0xbc, 0xcf, 0xf6, 0x76, 0x68, 0xe3, 0x35, 0x92, - 0xb0, 0xef, 0xb6, 0xeb, 0xaa, 0x2b, 0x4e, 0x7f, 0x62, 0x01, 0xe1, 0x02, 0x36, 0x16, 0xe8, 0x9b, 0xdb, 0x86, 0xd2, - 0xb9, 0xee, 0x1a, 0x7c, 0xaf, 0xf1, 0xce, 0x3b, 0x3b, 0xb8, 0x8a, 0x93, 0x44, 0x60, 0x76, 0xa1, 0x34, 0x26, 0xea, - 0x17, 0x86, 0xe7, 0x6a, 0xcf, 0xb7, 0xea, 0x65, 0x54, 0xb3, 0x67, 0x02, 0xfe, 0x3a, 0x1a, 0x1f, 0x95, 0x79, 0x83, - 0x85, 0xdb, 0x3a, 0x85, 0xee, 0xfc, 0x7d, 0x00, 0xaf, 0xbc, 0x6b, 0xb6, 0x2c, 0xe6, 0x3b, 0xce, 0x82, 0xa5, 0xcd, - 0xdb, 0x5d, 0x99, 0xe0, 0x97, 0x1f, 0x70, 0xaf, 0xf8, 0xd2, 0xc1, 0x93, 0x7e, 0xdd, 0x52, 0xc0, 0x5d, 0xc0, 0xe4, - 0xa5, 0x1b, 0x6e, 0x42, 0xdc, 0xac, 0x72, 0xd3, 0xb7, 0x48, 0xf9, 0xe9, 0xe9, 0xac, 0x79, 0xea, 0x10, 0xde, 0x78, - 0x54, 0x87, 0xca, 0x68, 0x6e, 0xdd, 0xc2, 0x95, 0xfe, 0x05, 0xb7, 0x35, 0x77, 0x30, 0xc3, 0x89, 0x2c, 0x89, 0xc7, - 0x93, 0xee, 0x1e, 0xa0, 0xcc, 0x03, 0x2f, 0x22, 0x29, 0xa2, 0x6d, 0x60, 0x07, 0x2d, 0x28, 0x6b, 0x0d, 0xa2, 0xd6, - 0xde, 0x23, 0x66, 0x7b, 0x69, 0xf7, 0x43, 0xf7, 0xe1, 0x03, 0x0f, 0xd6, 0x44, 0xc8, 0x19, 0x4c, 0x28, 0x7e, 0x97, - 0xf6, 0x73, 0xd8, 0x33, 0xc2, 0x95, 0x56, 0x28, 0x78, 0x8e, 0x1b, 0x54, 0x7d, 0x9f, 0x2d, 0x20, 0x5a, 0x24, 0x20, - 0x67, 0xbb, 0x16, 0xd6, 0xa8, 0x18, 0xf9, 0x49, 0x0c, 0x95, 0xd4, 0xb6, 0x7c, 0x83, 0xa5, 0xbf, 0xae, 0x02, 0x49, - 0x20, 0x31, 0x27, 0xf5, 0xbc, 0xb6, 0x2d, 0x16, 0x37, 0x4b, 0x36, 0x0f, 0x15, 0xa5, 0xe7, 0xe5, 0xd8, 0x79, 0x07, - 0xf5, 0x28, 0xb8, 0x2c, 0xdb, 0xeb, 0xdc, 0x2e, 0xed, 0xae, 0x75, 0x18, 0x4e, 0x52, 0x4e, 0x31, 0xe0, 0xa1, 0x6d, - 0x3d, 0x72, 0xb1, 0xf8, 0xf7, 0x40, 0x1a, 0xde, 0x5d, 0x7e, 0x78, 0x73, 0x4b, 0x6b, 0x4c, 0xd4, 0x66, 0x2a, 0x12, - 0xe1, 0xe4, 0x86, 0x15, 0xc9, 0x90, 0x26, 0x12, 0x3d, 0x7c, 0x91, 0x6f, 0xae, 0x35, 0xac, 0x12, 0xd9, 0x90, 0x61, - 0xb3, 0xd9, 0xcd, 0x64, 0xdc, 0xca, 0x76, 0x7f, 0x3a, 0xf4, 0x26, 0xc8, 0xd6, 0x89, 0xd2, 0x3c, 0x77, 0xd8, 0x62, - 0xed, 0x9e, 0xb1, 0xa8, 0x9f, 0x1c, 0x65, 0x8e, 0x78, 0x43, 0x4c, 0xb4, 0x4d, 0xb9, 0xb6, 0x66, 0x1e, 0x21, 0x70, - 0x6f, 0xed, 0x3f, 0x2b, 0xf6, 0xf1, 0x1a, 0xd3, 0xc7, 0xf4, 0x0b, 0xee, 0xf9, 0xc4, 0xc3, 0xcf, 0x34, 0xc9, 0x0d, - 0xb1, 0xdd, 0x45, 0x3c, 0xe4, 0xa3, 0xbb, 0x61, 0xca, 0x84, 0x65, 0xda, 0x5c, 0xb5, 0x9c, 0x1b, 0x83, 0x54, 0xa1, - 0x48, 0xe5, 0x3e, 0xa2, 0xeb, 0xb0, 0x1f, 0xce, 0xf2, 0x3d, 0x48, 0x64, 0xbe, 0x4f, 0x22, 0x10, 0xeb, 0x1f, 0x05, - 0x71, 0x8e, 0x25, 0x2f, 0x8d, 0x22, 0x8d, 0xfe, 0x84, 0x52, 0x96, 0x72, 0x08, 0xf8, 0xe6, 0x80, 0x73, 0x15, 0x5b, - 0xff, 0x7d, 0xf6, 0x7d, 0x98, 0x76, 0x7d, 0xce, 0xee, 0x16, 0x12, 0xde, 0x72, 0xe2, 0x4e, 0xe5, 0xf1, 0xa9, 0x90, - 0x7b, 0x0f, 0x72, 0x2d, 0xbf, 0xed, 0x86, 0x86, 0xce, 0xf1, 0xb2, 0x45, 0x71, 0x55, 0xa7, 0xf2, 0x47, 0x51, 0xab, - 0xaa, 0xa4, 0x2a, 0x7b, 0x1e, 0x39, 0x93, 0x93, 0xb3, 0xdc, 0xab, 0x0a, 0x43, 0x03, 0x8f, 0x38, 0x5b, 0x62, 0x9d, - 0xbd, 0xc7, 0xcc, 0xe2, 0x84, 0x8f, 0x42, 0x03, 0xc1, 0x2a, 0xe9, 0x13, 0x8e, 0xe8, 0x0b, 0xb4, 0xd3, 0xfa, 0xd2, - 0xcd, 0xed, 0x47, 0x96, 0xb2, 0x83, 0xa3, 0xf1, 0xca, 0x8a, 0x7c, 0x9d, 0x02, 0x31, 0x53, 0xac, 0x62, 0xe4, 0xf3, - 0x1b, 0x84, 0x44, 0xf3, 0x8b, 0xe9, 0x82, 0xf6, 0x2e, 0x6b, 0x51, 0x1d, 0x3e, 0x6b, 0xf6, 0xca, 0x0b, 0x40, 0x1e, - 0x57, 0x15, 0x87, 0x48, 0x7b, 0x83, 0x38, 0x4d, 0x88, 0x7a, 0x76, 0xdb, 0xb3, 0x85, 0x38, 0x31, 0xcb, 0xd1, 0x62, - 0xd2, 0xab, 0x12, 0xd9, 0x6f, 0x4f, 0x4f, 0x20, 0x25, 0x3a, 0x63, 0x2c, 0x19, 0x69, 0x4b, 0xf9, 0x86, 0xe6, 0xf4, - 0x1e, 0xd6, 0x31, 0x11, 0xb1, 0x5a, 0x00, 0x52, 0x0d, 0x29, 0xf4, 0x35, 0x6a, 0x47, 0x44, 0xf8, 0xf9, 0xc8, 0x52, - 0x29, 0x32, 0xc6, 0x37, 0xf6, 0x23, 0x6d, 0x31, 0xab, 0x88, 0x53, 0xc4, 0xac, 0x61, 0x18, 0xd9, 0xb8, 0xc4, 0x28, - 0xa8, 0x56, 0x8f, 0x37, 0xf8, 0x04, 0x83, 0x94, 0x62, 0xab, 0xeb, 0x71, 0xdc, 0xc4, 0x4f, 0x47, 0x22, 0xc2, 0x7d, - 0x82, 0x38, 0x29, 0x81, 0x8d, 0xe7, 0x6f, 0xce, 0x54, 0xe7, 0x7c, 0x69, 0xb0, 0xe7, 0x48, 0xf5, 0x38, 0xc3, 0x40, - 0x13, 0x7c, 0xf5, 0xa3, 0xd9, 0x1f, 0xcb, 0x37, 0x32, 0x8b, 0x3b, 0x09, 0xa9, 0x95, 0xd3, 0xad, 0x36, 0x5b, 0x22, - 0x7e, 0x80, 0x0b, 0x67, 0xbc, 0x47, 0x35, 0x77, 0xf9, 0xa5, 0x26, 0x06, 0x56, 0x98, 0x13, 0x72, 0x37, 0x47, 0x92, - 0xbf, 0x00, 0xe3, 0x66, 0x0d, 0x6d, 0x2e, 0xc7, 0x2e, 0x38, 0x09, 0xe4, 0xea, 0xa6, 0xf4, 0x9b, 0xcf, 0x9e, 0x81, - 0xe9, 0x61, 0xf9, 0xd1, 0xef, 0x32, 0xff, 0x65, 0xac, 0x53, 0x13, 0xfc, 0xc8, 0x51, 0x59, 0x9f, 0xcb, 0xe3, 0xdf, - 0x01, 0xb7, 0x67, 0x7d, 0xe3, 0xcf, 0x93, 0x20, 0xce, 0x35, 0x7f, 0x66, 0xc1, 0x44, 0x36, 0xbb, 0xd6, 0xde, 0xcf, - 0x9f, 0xc1, 0x9b, 0xfb, 0x3d, 0x42, 0xe5, 0x6b, 0xe7, 0x14, 0xea, 0xa6, 0x01, 0x16, 0x7a, 0xf5, 0x60, 0x1f, 0x90, - 0x9b, 0x7d, 0x88, 0x0a, 0x14, 0x39, 0xa2, 0xd2, 0x35, 0xe3, 0x1a, 0xb6, 0x55, 0x3b, 0x94, 0x3d, 0xc3, 0x79, 0x4f, - 0x9b, 0x5d, 0xb5, 0xad, 0x57, 0x69, 0x13, 0x52, 0x98, 0xc2, 0x1a, 0xe8, 0x56, 0x37, 0xec, 0xdc, 0x1c, 0x2c, 0xdf, - 0x5a, 0x06, 0x1e, 0x16, 0xae, 0x98, 0xbd, 0x98, 0xf9, 0x8e, 0x0e, 0xfc, 0x59, 0xca, 0x2b, 0x0a, 0xb5, 0xfa, 0x8d, - 0xe3, 0xa8, 0x87, 0x36, 0xe0, 0x0d, 0x7f, 0x9d, 0x50, 0x9d, 0x3d, 0x67, 0x0b, 0x23, 0x17, 0xe2, 0xd7, 0xba, 0xc1, - 0x27, 0x74, 0xa9, 0x0a, 0x26, 0xe0, 0x4b, 0x9b, 0x1d, 0x0f, 0x5e, 0x87, 0x96, 0xa4, 0xf2, 0xb8, 0x79, 0x8c, 0xe5, - 0xdc, 0x4e, 0xb9, 0x54, 0x2b, 0xf3, 0xa3, 0x1b, 0x25, 0xd8, 0x20, 0x90, 0x24, 0x58, 0x84, 0xf0, 0x4f, 0x30, 0x67, - 0x1c, 0x89, 0x21, 0x7b, 0xa3, 0x62, 0x8a, 0x16, 0x14, 0x0c, 0x91, 0x52, 0xb6, 0x58, 0x60, 0xb3, 0xf7, 0x08, 0x7f, - 0x7f, 0xdf, 0x3a, 0xf5, 0xb4, 0xef, 0x9d, 0x02, 0xed, 0xdb, 0x27, 0xa5, 0xb4, 0xef, 0x9f, 0x14, 0x9d, 0xa5, 0x56, - 0x19, 0xfb, 0x6a, 0xc9, 0xbe, 0x1a, 0xd9, 0x63, 0x3b, 0xdb, 0x43, 0x2b, 0x8e, 0x6b, 0xd0, 0x8e, 0xc5, 0x82, 0x6f, - 0xc9, 0x01, 0xc7, 0x11, 0xcb, 0x92, 0xf5, 0x63, 0xa1, 0xb9, 0x77, 0xb5, 0x1e, 0xf9, 0xf6, 0x00, 0x15, 0x85, 0xb7, - 0xc3, 0xda, 0x8d, 0xac, 0x2c, 0xcf, 0x34, 0xb7, 0x4e, 0xe2, 0xd4, 0xd9, 0xde, 0x42, 0xf1, 0x74, 0xea, 0x08, 0x7e, - 0xd6, 0xe4, 0x70, 0x86, 0x4a, 0x13, 0xf8, 0x8f, 0x1c, 0x3f, 0x36, 0x5a, 0x5b, 0xd1, 0x78, 0xf3, 0xbd, 0x27, 0x1a, - 0x9a, 0xbf, 0xb8, 0x8a, 0x58, 0x98, 0x96, 0x93, 0x7b, 0x07, 0x53, 0x1f, 0x4a, 0xd6, 0xe2, 0x76, 0x07, 0x64, 0xb6, - 0x94, 0x97, 0x39, 0x41, 0x08, 0xa7, 0xba, 0x85, 0xff, 0x2c, 0xa0, 0x31, 0x27, 0x2d, 0x17, 0x53, 0xf9, 0xbc, 0xd5, - 0x86, 0xda, 0xce, 0x59, 0x83, 0x78, 0xec, 0xca, 0x74, 0x57, 0x82, 0x29, 0x3c, 0x9f, 0xc5, 0x15, 0x90, 0xfa, 0x85, - 0x35, 0xff, 0x46, 0xd9, 0xc3, 0xe5, 0x4e, 0x4c, 0x83, 0xff, 0xf7, 0x64, 0x3b, 0x08, 0x27, 0x74, 0x35, 0x4e, 0xb9, - 0x24, 0xe2, 0x7e, 0x6e, 0xa5, 0xed, 0x99, 0x37, 0x72, 0x7d, 0xfb, 0x8c, 0x61, 0x7a, 0xae, 0x42, 0x20, 0x53, 0x68, - 0x3f, 0xdd, 0x3f, 0x8f, 0x71, 0x48, 0xb0, 0x62, 0xcf, 0x09, 0x56, 0x19, 0x98, 0x92, 0x77, 0x33, 0x99, 0x9e, 0xbf, - 0xfc, 0x5f, 0xe6, 0xf7, 0x92, 0xf5, 0xa0, 0x37, 0x7b, 0xcb, 0x36, 0xb7, 0x85, 0x75, 0x69, 0x31, 0xb3, 0x7e, 0x34, - 0xd3, 0xfa, 0x5f, 0x71, 0x80, 0xb7, 0xdb, 0xe9, 0x8a, 0x3f, 0xaa, 0x0c, 0xd2, 0x38, 0x64, 0xdd, 0x6f, 0x4b, 0xd6, - 0x03, 0xde, 0xed, 0xed, 0xf3, 0x5b, 0x1c, 0xfe, 0xb1, 0x09, 0x7c, 0x4b, 0x17, 0x00, 0x02, 0xf8, 0x91, 0xbb, 0x1c, - 0xbb, 0x9c, 0xc9, 0x9d, 0xeb, 0x0a, 0x0b, 0xaa, 0x96, 0x43, 0x16, 0x2e, 0x96, 0x6c, 0x41, 0x3e, 0x5e, 0x13, 0xd9, - 0xe8, 0xc0, 0xec, 0x12, 0xb1, 0xf7, 0x44, 0x09, 0xde, 0x6c, 0xf3, 0x29, 0xd1, 0xf3, 0xe7, 0x04, 0xda, 0x66, 0xd7, - 0x89, 0x0d, 0xb9, 0xb6, 0x38, 0x15, 0x27, 0x00, 0xec, 0x7b, 0xe6, 0xc2, 0xe0, 0x6c, 0xa4, 0xf6, 0x41, 0x0b, 0xa7, - 0xb7, 0x04, 0xfa, 0xd9, 0x38, 0x45, 0xde, 0xef, 0x00, 0x95, 0x52, 0x78, 0xe2, 0x10, 0x13, 0x2a, 0x76, 0xf8, 0x9c, - 0x52, 0x9f, 0x43, 0x8e, 0x9c, 0x77, 0xc0, 0x89, 0x5b, 0xda, 0x74, 0x63, 0x79, 0xbb, 0xe1, 0xbb, 0x8a, 0x74, 0x65, - 0xf5, 0xb0, 0x84, 0xb6, 0xcd, 0xd1, 0x19, 0x67, 0x94, 0xa0, 0xc4, 0x08, 0x29, 0xc3, 0xf3, 0x63, 0x30, 0x48, 0x26, - 0xe3, 0x30, 0xd1, 0x6b, 0xce, 0x0a, 0xa3, 0xff, 0x44, 0x08, 0x23, 0x84, 0x9f, 0x5f, 0x67, 0x6c, 0xdf, 0xa3, 0xbb, - 0xb6, 0xe9, 0x5e, 0x6f, 0x15, 0xb1, 0xcf, 0x2b, 0x0d, 0x4a, 0xa5, 0xd2, 0x58, 0xdb, 0x8c, 0x7f, 0x75, 0x52, 0x9d, - 0x46, 0x1d, 0x8e, 0x70, 0x76, 0xa6, 0xcd, 0xf9, 0xbf, 0x9e, 0x99, 0xb9, 0xd7, 0xce, 0xcb, 0x9e, 0x19, 0xeb, 0xc6, - 0x25, 0x91, 0x16, 0xe4, 0x26, 0x0d, 0x25, 0xeb, 0xb1, 0xd1, 0xde, 0x94, 0x4c, 0xfd, 0xc8, 0xa4, 0x80, 0x8d, 0xb1, - 0xda, 0xe1, 0x32, 0x3e, 0xcd, 0xfb, 0xa8, 0x24, 0x0b, 0x2e, 0xc4, 0xf9, 0x70, 0xbb, 0xb1, 0x22, 0x85, 0x1d, 0x68, - 0xf2, 0xeb, 0x4f, 0xc9, 0xae, 0xf0, 0x9d, 0xdf, 0x81, 0x64, 0xd6, 0x17, 0x2b, 0xc5, 0x06, 0x75, 0x05, 0x7a, 0x03, - 0x2e, 0xdf, 0xd1, 0x1b, 0x65, 0x34, 0x7c, 0x5d, 0x4a, 0x54, 0x92, 0x50, 0x63, 0xa5, 0x60, 0xb7, 0x58, 0x97, 0x51, - 0x14, 0x17, 0x6b, 0xa2, 0x5f, 0x15, 0x4c, 0x16, 0xdb, 0x6e, 0xe0, 0xdc, 0x04, 0xea, 0x51, 0x07, 0x27, 0xfa, 0x3b, - 0x44, 0xde, 0x16, 0xc5, 0x86, 0x6c, 0x1b, 0x88, 0xa1, 0x15, 0xd7, 0x75, 0xaa, 0xb1, 0x3e, 0xf2, 0x80, 0x1e, 0xf7, - 0xaf, 0x8e, 0x21, 0x78, 0xf8, 0x69, 0xc0, 0x52, 0x57, 0x9d, 0x3a, 0xbc, 0x7d, 0x74, 0x63, 0x49, 0xea, 0x71, 0x7e, - 0xf3, 0x4f, 0xc5, 0x36, 0x2d, 0x4f, 0xb7, 0xc8, 0x0d, 0x89, 0xe0, 0x5d, 0x41, 0xf6, 0xd3, 0xc1, 0x6d, 0x7b, 0xa6, - 0x28, 0x0a, 0x2f, 0x94, 0xb4, 0xbc, 0x29, 0x3c, 0x8c, 0xe3, 0x46, 0x8a, 0x1a, 0x2a, 0x32, 0xfe, 0x31, 0x36, 0x2c, - 0x92, 0x9d, 0xad, 0x0f, 0x63, 0xe7, 0x95, 0x90, 0x8f, 0x2b, 0xd7, 0xea, 0x46, 0xa6, 0x63, 0x5b, 0x14, 0x39, 0x7a, - 0x9d, 0x07, 0xa0, 0xf2, 0x47, 0x61, 0x12, 0x50, 0x1c, 0x89, 0x00, 0xa2, 0x3a, 0xf1, 0xa2, 0xe8, 0x81, 0xcd, 0xa1, - 0x19, 0x56, 0x83, 0x7c, 0x2a, 0xfa, 0x1b, 0x15, 0x9d, 0xd9, 0xf1, 0x50, 0xd8, 0x8b, 0x4c, 0xac, 0x3e, 0xe6, 0xae, - 0x43, 0x91, 0x36, 0x72, 0xea, 0x3b, 0xd7, 0x98, 0x37, 0xd0, 0xc3, 0x22, 0x14, 0x7f, 0x61, 0x83, 0x98, 0xb2, 0x01, - 0x2b, 0x64, 0xec, 0x79, 0x04, 0x30, 0x4b, 0xf2, 0x45, 0x12, 0x78, 0xd3, 0xaf, 0x40, 0xbf, 0xc1, 0x7d, 0xb5, 0x6f, - 0x26, 0x4d, 0xb3, 0x70, 0x77, 0x63, 0xbf, 0x2f, 0x36, 0x72, 0x4f, 0x08, 0x22, 0x58, 0x92, 0xd9, 0xb2, 0x96, 0xde, - 0x03, 0x7e, 0xa5, 0xe0, 0x19, 0x78, 0xc8, 0x00, 0x8e, 0x98, 0x96, 0xb8, 0xae, 0xd6, 0x93, 0xab, 0xc3, 0x56, 0x6e, - 0x0b, 0x25, 0x38, 0x44, 0xd2, 0xe8, 0x96, 0x4a, 0xb3, 0x94, 0xee, 0x99, 0xad, 0xbb, 0x91, 0x71, 0xfc, 0x65, 0x50, - 0x9e, 0x58, 0x8f, 0x5f, 0x93, 0xc8, 0x96, 0x44, 0x14, 0x97, 0x5f, 0xe7, 0x90, 0xdd, 0x58, 0xa9, 0x98, 0x0c, 0x54, - 0x3d, 0x79, 0xaa, 0x09, 0xde, 0x60, 0x71, 0x17, 0x1c, 0xe9, 0x03, 0xed, 0x09, 0xc5, 0x49, 0xaa, 0x4a, 0xa9, 0x87, - 0x7e, 0xc2, 0x57, 0xd8, 0xe7, 0xa2, 0xb7, 0xd9, 0x31, 0xcd, 0xd8, 0x67, 0x34, 0xc1, 0x80, 0x3a, 0x09, 0x4e, 0xbb, - 0x66, 0x78, 0xe4, 0x48, 0x6c, 0x3a, 0x90, 0x8f, 0xf1, 0x54, 0xe8, 0x36, 0x6e, 0x56, 0x21, 0x8e, 0x86, 0xd0, 0xfd, - 0x30, 0x14, 0x46, 0x3f, 0xa3, 0x74, 0xac, 0x3e, 0xed, 0xd7, 0x5c, 0xd4, 0xce, 0x98, 0xa2, 0x29, 0x2f, 0xbb, 0xa6, - 0x00, 0x6f, 0xa4, 0xbc, 0xc1, 0x0a, 0xf8, 0xfe, 0xb2, 0x59, 0x57, 0x8f, 0x17, 0x36, 0xf9, 0x0f, 0xae, 0x83, 0x8d, - 0x84, 0x09, 0xfc, 0x11, 0x92, 0x99, 0x2d, 0x82, 0x35, 0x8c, 0xf3, 0x92, 0x58, 0x38, 0x7a, 0x9c, 0xef, 0x07, 0xd9, - 0x1f, 0x57, 0x8d, 0x07, 0x61, 0x0b, 0x0f, 0xad, 0xe4, 0x9c, 0xa8, 0xd7, 0xd4, 0xa9, 0x91, 0x0f, 0x22, 0x93, 0xc0, - 0x84, 0xf2, 0x3c, 0xc1, 0x34, 0xab, 0xb3, 0x59, 0x50, 0xdb, 0x44, 0xc5, 0xa0, 0xd0, 0x8d, 0xdb, 0x99, 0x20, 0xc9, - 0x86, 0xe0, 0x94, 0x97, 0x65, 0xc3, 0xed, 0x75, 0x6b, 0xe6, 0x45, 0xf3, 0xd7, 0x64, 0x87, 0xa5, 0xdf, 0x05, 0x1d, - 0x6b, 0xe3, 0xf5, 0x60, 0x7b, 0xd0, 0x79, 0x58, 0xbc, 0x50, 0x3a, 0x8d, 0xaa, 0x9b, 0x7a, 0x11, 0x37, 0xfb, 0x39, - 0x75, 0x35, 0xd1, 0x6c, 0x09, 0x48, 0x67, 0xa3, 0x7c, 0x8f, 0x9d, 0xb8, 0x46, 0x51, 0x5a, 0x4b, 0xab, 0x5b, 0xe6, - 0x1d, 0x8b, 0x91, 0xbb, 0x81, 0x51, 0x62, 0xed, 0x22, 0x86, 0x9a, 0x9f, 0xc3, 0xdc, 0x9e, 0x98, 0x40, 0x45, 0xff, - 0x3a, 0x9f, 0xcc, 0xec, 0x62, 0x9a, 0x4a, 0x32, 0xcc, 0x07, 0xa5, 0x6f, 0x89, 0xe6, 0xee, 0xf1, 0x9c, 0x93, 0xd2, - 0xb6, 0xad, 0x62, 0x1d, 0xba, 0x85, 0x81, 0x0f, 0x5c, 0x4d, 0x03, 0xc1, 0x15, 0xbd, 0xc5, 0x98, 0x67, 0xf0, 0x7c, - 0xc0, 0xec, 0x1b, 0x79, 0x3e, 0x2f, 0x45, 0xde, 0x3e, 0x91, 0x19, 0xbe, 0x50, 0xa0, 0x98, 0xde, 0xe9, 0x7c, 0x6f, - 0x9f, 0x87, 0x1b, 0x77, 0x59, 0xe0, 0xbe, 0x24, 0x0e, 0x19, 0xfe, 0x75, 0x17, 0x5b, 0xc6, 0x1a, 0xcf, 0x9c, 0xfb, - 0x2d, 0x89, 0x09, 0xa5, 0xda, 0xae, 0x1f, 0xa2, 0xbc, 0x16, 0x61, 0x52, 0x85, 0xa5, 0xdb, 0x2a, 0xa4, 0x32, 0xf4, - 0x45, 0xa4, 0x8a, 0xc7, 0x99, 0x9b, 0x9d, 0xa1, 0x34, 0x82, 0x0c, 0x05, 0x13, 0x64, 0xb5, 0x4f, 0xa2, 0x79, 0x56, - 0xf2, 0xa0, 0x4d, 0x13, 0xf9, 0xf0, 0xba, 0x2a, 0x63, 0xe1, 0x71, 0xe6, 0xde, 0x76, 0xc4, 0xdc, 0xba, 0x8e, 0xf3, - 0xea, 0x03, 0x75, 0x2b, 0x47, 0xe6, 0xb9, 0x62, 0x6c, 0xc5, 0xd8, 0x3d, 0xa8, 0x45, 0xa0, 0x0c, 0x45, 0x12, 0x0e, - 0x6c, 0x31, 0x7a, 0x7b, 0xa1, 0xce, 0x06, 0xc2, 0xad, 0xb2, 0x3e, 0xaa, 0xc5, 0x6b, 0xda, 0xb6, 0x52, 0x0a, 0x4e, - 0xa0, 0x10, 0x4e, 0x34, 0xf6, 0x9c, 0x0f, 0xff, 0xfc, 0x5c, 0xa7, 0x1e, 0xff, 0x99, 0x10, 0x9b, 0xfd, 0x97, 0xf7, - 0xaf, 0x63, 0x0a, 0xd0, 0xeb, 0x9e, 0x75, 0x45, 0x7a, 0xad, 0xeb, 0x35, 0xd2, 0xab, 0xaf, 0x57, 0xb5, 0x39, 0xe1, - 0x59, 0x2d, 0xb5, 0x51, 0x1b, 0x77, 0x6e, 0x14, 0xeb, 0x30, 0x94, 0x94, 0xd4, 0x7e, 0x4f, 0x4f, 0x3f, 0x8d, 0x55, - 0x99, 0x6f, 0xcd, 0xa4, 0x98, 0x4d, 0x5f, 0x1c, 0xab, 0xf5, 0xb8, 0x8c, 0x10, 0xbb, 0x17, 0x43, 0x6d, 0xa5, 0x3a, - 0x35, 0x75, 0x9b, 0xcf, 0x2f, 0xc6, 0xc5, 0xe5, 0xcb, 0xbf, 0x22, 0xc4, 0xf3, 0x11, 0xe3, 0xa1, 0x8d, 0x76, 0xde, - 0x37, 0x48, 0x8d, 0xcb, 0xcd, 0x11, 0xe7, 0x68, 0x56, 0x15, 0xc6, 0x88, 0xa7, 0x55, 0xe7, 0x2e, 0xb8, 0xf6, 0x20, - 0xf0, 0x73, 0x71, 0x55, 0xa9, 0x48, 0x52, 0xdf, 0x36, 0xb0, 0x2e, 0x37, 0x4b, 0x93, 0xc3, 0xdf, 0x0b, 0x2c, 0xe8, - 0x63, 0x53, 0x95, 0xeb, 0x07, 0x25, 0xc4, 0xd8, 0x29, 0x62, 0xca, 0xa9, 0xf4, 0x2e, 0xac, 0x7c, 0x51, 0x5f, 0x4f, - 0x99, 0xfa, 0x36, 0x88, 0x31, 0x8b, 0x31, 0x27, 0x4b, 0x31, 0x27, 0x79, 0x60, 0xfb, 0x3c, 0x06, 0xc6, 0xc5, 0x24, - 0x10, 0xf9, 0x70, 0xe3, 0xca, 0x58, 0xbe, 0x08, 0x18, 0xac, 0xa2, 0x0d, 0x04, 0xd3, 0x3b, 0x33, 0xed, 0xe2, 0x1f, - 0xf3, 0xcb, 0x41, 0x64, 0x5c, 0x89, 0x21, 0xac, 0x8e, 0xf8, 0xad, 0xd3, 0x0b, 0x64, 0x62, 0xe5, 0x8c, 0x66, 0x09, - 0x98, 0x75, 0xd3, 0x34, 0x38, 0x56, 0x4d, 0x83, 0x4a, 0x33, 0xaf, 0xb0, 0x0c, 0x24, 0x31, 0x30, 0x95, 0x6a, 0xf8, - 0x95, 0x16, 0x09, 0xce, 0xf9, 0xfb, 0xae, 0xcf, 0x29, 0x90, 0xc6, 0x99, 0x46, 0xa5, 0xc0, 0xe7, 0x0e, 0xf8, 0x05, - 0xfa, 0x15, 0x9f, 0x88, 0xe3, 0x34, 0xe9, 0x91, 0xc9, 0xe8, 0x81, 0xda, 0x81, 0x90, 0x59, 0x4b, 0x46, 0x61, 0x1a, - 0x42, 0x28, 0x05, 0x44, 0x7c, 0x3f, 0xcc, 0x45, 0x95, 0x35, 0xaf, 0xc6, 0x02, 0xb7, 0x10, 0x31, 0x23, 0xaa, 0x06, - 0x22, 0xc9, 0x48, 0xae, 0x1b, 0x32, 0x2c, 0x96, 0x26, 0x2d, 0xc6, 0xe0, 0x04, 0xc9, 0x3c, 0x9d, 0x08, 0xfe, 0x65, - 0x48, 0xc6, 0x05, 0x6f, 0x7a, 0xaf, 0x80, 0xbe, 0xc6, 0xc5, 0x64, 0xe7, 0xcd, 0xbc, 0xe8, 0x89, 0xb4, 0x5c, 0xe5, - 0x43, 0xa2, 0xd0, 0xdf, 0xd7, 0x7d, 0xef, 0x58, 0x3d, 0x48, 0xc1, 0xbc, 0x2d, 0x6a, 0x8b, 0x96, 0xed, 0xb5, 0x95, - 0xc7, 0x72, 0xdc, 0x3d, 0x4a, 0x50, 0x00, 0xdd, 0x66, 0xd1, 0x0a, 0x3c, 0x49, 0xd6, 0xd8, 0xa9, 0x4f, 0x44, 0x74, - 0x74, 0x1b, 0x25, 0xb3, 0x23, 0x5b, 0x17, 0x3f, 0x90, 0x91, 0xe4, 0xcc, 0x5c, 0x89, 0xce, 0xff, 0x59, 0xf2, 0x26, - 0x17, 0x33, 0x5b, 0x75, 0xc8, 0x01, 0x6e, 0x3a, 0x13, 0x61, 0x8a, 0xf6, 0x56, 0x66, 0x23, 0x44, 0x86, 0x93, 0x49, - 0x16, 0x64, 0xea, 0xc5, 0x5f, 0x8c, 0x14, 0xfc, 0x47, 0xc4, 0x86, 0x96, 0x3c, 0xd2, 0xff, 0x70, 0x0d, 0xe1, 0x5b, - 0x39, 0x1c, 0x24, 0xd5, 0x7b, 0x2d, 0xb8, 0x2d, 0x2d, 0x1b, 0x66, 0x83, 0x24, 0x3c, 0x3e, 0xbb, 0x7c, 0xe6, 0xdb, - 0x83, 0xfc, 0x43, 0x44, 0x08, 0x84, 0xfa, 0xdf, 0xf4, 0xaa, 0x76, 0xf1, 0x32, 0x2a, 0x4e, 0x83, 0xf2, 0xf5, 0xf8, - 0x8c, 0xc3, 0x1b, 0x2a, 0x2f, 0xe0, 0xb7, 0x6f, 0xe7, 0x1c, 0x98, 0x81, 0x2f, 0x63, 0xab, 0xb1, 0x80, 0xbd, 0x70, - 0xd8, 0x63, 0x28, 0x59, 0xc4, 0xa1, 0xed, 0x6c, 0x84, 0xdb, 0xd0, 0xf5, 0x36, 0xdb, 0xaf, 0x94, 0x72, 0x75, 0xc6, - 0xf9, 0x3b, 0xdb, 0xaa, 0xe6, 0x66, 0xd7, 0x0d, 0x5b, 0x2a, 0xe9, 0x69, 0x5f, 0x6e, 0x30, 0x75, 0x43, 0xf6, 0x36, - 0xd4, 0x5a, 0xbe, 0x19, 0xd6, 0x95, 0x37, 0x0b, 0x83, 0x42, 0xc0, 0x98, 0x61, 0xcd, 0x15, 0xb9, 0xd6, 0xca, 0x7e, - 0x30, 0xc5, 0xfe, 0x30, 0x08, 0x89, 0xa8, 0x8a, 0x24, 0x67, 0x83, 0x1e, 0xe7, 0x6a, 0xed, 0x59, 0x3d, 0x02, 0x4b, - 0x27, 0x96, 0x63, 0xcd, 0x0a, 0x06, 0x43, 0xa9, 0xaa, 0xd5, 0x52, 0x77, 0xb8, 0x4a, 0x9f, 0x6a, 0x79, 0xc5, 0x0b, - 0x12, 0xf6, 0x0b, 0x88, 0x4e, 0x7c, 0x77, 0xf7, 0x24, 0xf2, 0x9d, 0x59, 0x7d, 0x63, 0x2a, 0x4d, 0x94, 0x67, 0xc5, - 0x0a, 0x4a, 0xd6, 0x3b, 0xc0, 0x50, 0x51, 0x63, 0x6c, 0xe8, 0x0e, 0x0d, 0xd6, 0xe6, 0x38, 0xdc, 0x17, 0xf6, 0xdb, - 0x82, 0xec, 0x47, 0xfd, 0x9c, 0x93, 0xfb, 0x68, 0xb3, 0xa8, 0x97, 0xf5, 0x56, 0x63, 0xe4, 0x08, 0xaf, 0x37, 0x27, - 0x55, 0xb6, 0xa0, 0xc9, 0x5e, 0x83, 0xd3, 0x4b, 0x33, 0x75, 0xa1, 0xec, 0xc4, 0x8c, 0x28, 0xd3, 0x41, 0x24, 0x09, - 0xba, 0xb3, 0x1e, 0x04, 0xd7, 0x2c, 0x0b, 0x6b, 0x93, 0x91, 0x7b, 0x30, 0x9c, 0x23, 0x15, 0xd1, 0x25, 0x14, 0xc5, - 0x39, 0x9b, 0xd7, 0x3f, 0x30, 0xe4, 0x28, 0x8f, 0xc5, 0xb2, 0x64, 0x41, 0xbd, 0x6f, 0x61, 0xa4, 0x26, 0xfb, 0x74, - 0x2c, 0xa5, 0x90, 0x1d, 0xc0, 0xc6, 0x8e, 0xb6, 0x73, 0xc1, 0x9c, 0xda, 0xba, 0x04, 0x3b, 0xd9, 0xa9, 0xb9, 0x5b, - 0x91, 0x01, 0x91, 0x07, 0x42, 0x14, 0x06, 0x7c, 0x7f, 0x5e, 0x11, 0xc0, 0x9a, 0xe3, 0x14, 0x89, 0x3f, 0x08, 0xe3, - 0x97, 0x1f, 0x14, 0x83, 0x84, 0xe5, 0xae, 0xe7, 0x70, 0xfa, 0x3a, 0x80, 0x56, 0xea, 0xc5, 0xe6, 0x7b, 0x26, 0xca, - 0x46, 0xfe, 0x2a, 0xd6, 0x3a, 0x62, 0x88, 0x70, 0xe0, 0xcb, 0x66, 0x43, 0xd2, 0x78, 0xb3, 0x5c, 0x9c, 0x8d, 0x9a, - 0xae, 0xab, 0x23, 0xee, 0x23, 0x15, 0x84, 0xb1, 0xd9, 0xf0, 0xd0, 0x8d, 0xf3, 0x43, 0xd6, 0x6e, 0x06, 0x87, 0x41, - 0x04, 0x7e, 0x6d, 0xba, 0x56, 0x97, 0x70, 0xb5, 0xa6, 0x99, 0x78, 0x2f, 0xce, 0xa6, 0xfb, 0xba, 0xd7, 0x35, 0xc2, - 0xbf, 0x5c, 0xe2, 0x80, 0xf9, 0x37, 0x52, 0xc5, 0x41, 0x8b, 0x39, 0x7a, 0x8d, 0x4b, 0x9a, 0xe9, 0xa9, 0x21, 0x77, - 0x57, 0xca, 0x7b, 0x28, 0x07, 0xaa, 0x63, 0x3c, 0x3d, 0x64, 0x37, 0x87, 0x5b, 0x80, 0xda, 0x8e, 0x10, 0x57, 0x06, - 0xea, 0x09, 0x80, 0x2b, 0x09, 0x84, 0x65, 0x1e, 0xcf, 0x90, 0xbe, 0x67, 0xd2, 0x09, 0x68, 0xe8, 0x40, 0xb9, 0xe9, - 0x49, 0x99, 0x43, 0xea, 0xa1, 0x0e, 0x52, 0x4c, 0x78, 0xd0, 0xcb, 0xae, 0x96, 0xea, 0x3a, 0x1a, 0x21, 0x69, 0x42, - 0x41, 0xfc, 0x82, 0xa0, 0xe8, 0xab, 0x21, 0xf2, 0x97, 0x89, 0xca, 0xba, 0xaa, 0xf0, 0x06, 0xff, 0x12, 0x2d, 0xb2, - 0xfa, 0xce, 0xcc, 0xec, 0x48, 0x5d, 0x56, 0xa2, 0xf6, 0x02, 0xb0, 0x0e, 0x87, 0xe0, 0x40, 0x22, 0x62, 0x9e, 0x44, - 0x13, 0xd9, 0x54, 0x28, 0x7f, 0xe6, 0xd0, 0x28, 0x80, 0xcb, 0x79, 0x24, 0x68, 0x22, 0xf0, 0xb1, 0x13, 0xe0, 0xcc, - 0x0c, 0x3c, 0x9c, 0xad, 0x26, 0x8d, 0xc0, 0x98, 0x6b, 0xe5, 0xa5, 0x66, 0x1f, 0x33, 0xa2, 0x1c, 0x17, 0x73, 0x23, - 0xbb, 0x6b, 0xf2, 0x70, 0x88, 0x79, 0x62, 0x63, 0x0e, 0xdf, 0xd7, 0x9e, 0x19, 0xd3, 0xbf, 0xcc, 0xc0, 0x27, 0x25, - 0xea, 0x4e, 0x0d, 0x8a, 0xd7, 0xed, 0x9d, 0xd7, 0x56, 0xbb, 0x86, 0x5c, 0x16, 0x1d, 0x06, 0xab, 0xb5, 0xff, 0xd7, - 0x7f, 0x8a, 0xe3, 0xbe, 0x72, 0x3e, 0x06, 0x57, 0x3c, 0x04, 0x87, 0x35, 0x43, 0xcd, 0xaf, 0xeb, 0xe2, 0x39, 0x3e, - 0x6d, 0x1f, 0xe6, 0xc6, 0xd3, 0xdd, 0x81, 0x97, 0xb9, 0x90, 0xfa, 0xcc, 0x12, 0xa2, 0x0f, 0x43, 0x8b, 0x67, 0x63, - 0x54, 0x89, 0xc6, 0x97, 0x0e, 0x29, 0x96, 0x2d, 0x9e, 0xee, 0x04, 0xe2, 0xe5, 0x70, 0x77, 0xb6, 0x40, 0xac, 0x28, - 0x11, 0xe6, 0x74, 0x22, 0xd2, 0x38, 0x02, 0xc6, 0x2b, 0xf1, 0xc0, 0x10, 0x18, 0x69, 0x94, 0x59, 0xd3, 0xfe, 0xb0, - 0x11, 0xd9, 0xe7, 0x90, 0x68, 0x32, 0x6c, 0xca, 0x3b, 0x9b, 0x51, 0x7b, 0x25, 0x12, 0x8a, 0x86, 0x75, 0xdf, 0x4d, - 0x33, 0x2a, 0xef, 0xc5, 0x38, 0x24, 0x0e, 0xe1, 0xa4, 0x77, 0x3f, 0x6d, 0x1f, 0x4a, 0x1e, 0x7e, 0x0e, 0xfb, 0xc3, - 0x0f, 0xfe, 0xe1, 0xe7, 0x70, 0x77, 0x7e, 0xf0, 0x9d, 0x9f, 0x43, 0xde, 0xf9, 0x41, 0xbc, 0x54, 0x9a, 0xbe, 0xd2, - 0x9e, 0x07, 0x63, 0xc5, 0x50, 0x2e, 0xcb, 0xc8, 0x56, 0xaa, 0xe0, 0x17, 0x3f, 0x24, 0xdc, 0xe7, 0x12, 0x29, 0x39, - 0x25, 0x2e, 0x58, 0x89, 0x4a, 0x56, 0x86, 0x4e, 0x81, 0x7d, 0x1a, 0xd0, 0xc3, 0xea, 0xed, 0xe7, 0xfc, 0xcb, 0x6d, - 0x50, 0x74, 0x26, 0xe2, 0x01, 0x24, 0x43, 0xb9, 0x33, 0x07, 0x2f, 0x4c, 0x49, 0x18, 0x65, 0x39, 0x62, 0xb4, 0xa2, - 0xd2, 0x8e, 0xb3, 0x44, 0xef, 0xdc, 0x01, 0x16, 0x82, 0xbe, 0x5d, 0xe8, 0xb5, 0x72, 0x58, 0x7f, 0xff, 0x05, 0x28, - 0x55, 0x57, 0x0c, 0x78, 0x60, 0x1f, 0x7b, 0x71, 0x1f, 0x69, 0xe5, 0xd5, 0xa4, 0x8a, 0x1a, 0x5c, 0x93, 0x83, 0x31, - 0x46, 0x48, 0xdc, 0xd3, 0xbf, 0x64, 0x4d, 0x72, 0xe6, 0xe6, 0xad, 0x66, 0xe1, 0x1e, 0xa3, 0xe7, 0x80, 0xe6, 0xc4, - 0xa8, 0x9a, 0x19, 0xb6, 0x88, 0x5a, 0xb3, 0x9a, 0x33, 0x8b, 0x38, 0x59, 0x8a, 0xad, 0xab, 0xb0, 0xe7, 0x3d, 0x7e, - 0xca, 0xef, 0xe0, 0x2a, 0x37, 0x43, 0x1a, 0xec, 0x8b, 0x0c, 0xec, 0x83, 0x2b, 0x6c, 0x6b, 0x0d, 0xa6, 0x27, 0x9c, - 0xad, 0xc5, 0xf5, 0xd5, 0x14, 0xbe, 0x20, 0xad, 0xa1, 0x2d, 0x45, 0x34, 0xba, 0x4b, 0x26, 0x36, 0x52, 0xda, 0xfa, - 0xe1, 0x6b, 0x0b, 0x8d, 0x36, 0x2b, 0x96, 0x60, 0xc9, 0xee, 0x37, 0x2f, 0xb9, 0x0f, 0x4d, 0xe6, 0x2c, 0xc8, 0x44, - 0xd5, 0x4d, 0x90, 0x36, 0x05, 0xbe, 0x38, 0x59, 0x61, 0x3c, 0x02, 0x59, 0xe4, 0x36, 0x17, 0x87, 0x53, 0x47, 0x2d, - 0xa3, 0xaa, 0x84, 0x48, 0x7d, 0x56, 0xae, 0x93, 0x4b, 0xd0, 0xf1, 0xe2, 0x40, 0x04, 0x97, 0xc3, 0x84, 0x54, 0x6a, - 0x3a, 0x6d, 0xd7, 0x68, 0x6f, 0x21, 0xcf, 0xa1, 0x4e, 0x3f, 0x0d, 0x36, 0x84, 0x21, 0xaa, 0x31, 0xf8, 0x32, 0xf3, - 0xf4, 0x9a, 0x2e, 0x4d, 0xdb, 0xc7, 0x01, 0x04, 0x7a, 0xb1, 0x3d, 0x95, 0xce, 0x5d, 0x9f, 0x92, 0x48, 0x20, 0x91, - 0xf8, 0x02, 0xe0, 0x03, 0x80, 0xaf, 0x7a, 0x89, 0xaa, 0x45, 0x26, 0xbd, 0x54, 0x81, 0x9e, 0x29, 0xb8, 0x03, 0x32, - 0x43, 0x2b, 0x40, 0xe5, 0x8f, 0x48, 0xf1, 0xb5, 0x43, 0xb2, 0x98, 0xf0, 0xd2, 0x50, 0xbc, 0x8e, 0x09, 0xed, 0x7c, - 0x98, 0x9a, 0x5e, 0x22, 0x77, 0x81, 0x94, 0x8e, 0xd8, 0xa2, 0x9f, 0xbe, 0x3c, 0xbb, 0x69, 0xe1, 0x24, 0x8f, 0x2c, - 0xbf, 0xd6, 0xfe, 0x2d, 0x6b, 0xdb, 0x55, 0xf5, 0x47, 0xa6, 0xa4, 0x0e, 0xb4, 0x21, 0x94, 0xeb, 0x99, 0xb2, 0xa7, - 0xf4, 0x15, 0xec, 0x2c, 0x86, 0x45, 0xaf, 0x2d, 0xb3, 0xdd, 0x1c, 0x3e, 0x74, 0xd1, 0x03, 0xd1, 0x84, 0xdb, 0xd7, - 0x48, 0xa0, 0xb9, 0x44, 0xb0, 0x18, 0x9e, 0xe1, 0xd2, 0x6e, 0xfc, 0x92, 0x53, 0x14, 0xc4, 0x2a, 0xf0, 0x21, 0x7d, - 0xff, 0x01, 0x43, 0x86, 0xb2, 0xdd, 0x86, 0xc3, 0x55, 0x0d, 0x34, 0x5f, 0xf7, 0x71, 0xd8, 0xab, 0x13, 0xb0, 0xb6, - 0x64, 0xbe, 0xda, 0xb4, 0x51, 0xec, 0x35, 0x97, 0xa7, 0xbd, 0xb6, 0x52, 0xe0, 0xcf, 0xc5, 0x47, 0x7f, 0x7b, 0x5e, - 0xd4, 0x2c, 0xcb, 0x8b, 0xd2, 0x7b, 0x5b, 0xd5, 0xec, 0xb4, 0x06, 0xa3, 0x3f, 0x4e, 0x85, 0x90, 0x58, 0x0e, 0x93, - 0xd2, 0xf3, 0xd1, 0xa8, 0x16, 0xbb, 0xd7, 0x64, 0x1e, 0x1f, 0x26, 0xa1, 0x9a, 0x4d, 0x8d, 0x3c, 0xb8, 0xd7, 0x9b, - 0x0b, 0x7d, 0x8f, 0x02, 0xd5, 0xbd, 0x16, 0x4e, 0xd5, 0x55, 0x29, 0x41, 0x4c, 0x46, 0x46, 0x33, 0xcd, 0xc6, 0xbc, - 0x0c, 0xdc, 0x9a, 0xa9, 0x7e, 0x41, 0x9f, 0x48, 0xc9, 0x61, 0xd8, 0x59, 0x59, 0x94, 0x8a, 0x49, 0x4a, 0x00, 0x8b, - 0xed, 0x67, 0x71, 0x72, 0x60, 0x50, 0xb5, 0xea, 0x3c, 0x60, 0x24, 0x8e, 0xc5, 0xe2, 0x23, 0x50, 0xf1, 0x5b, 0x07, - 0xa8, 0x12, 0x4e, 0x8f, 0x55, 0x71, 0x1e, 0x7e, 0x10, 0xa5, 0x52, 0x4f, 0x40, 0xa0, 0xa6, 0x4e, 0x5e, 0xe6, 0x5e, - 0xb0, 0x7c, 0x33, 0xa7, 0x8d, 0xbd, 0x30, 0x2b, 0x1d, 0x90, 0x6b, 0xd3, 0x48, 0x0c, 0x45, 0xfc, 0x93, 0x63, 0xe3, - 0x36, 0xba, 0xb0, 0xea, 0x85, 0xe5, 0x5e, 0x54, 0x07, 0xa1, 0x41, 0xe8, 0x90, 0xa7, 0xca, 0x6d, 0x19, 0xd6, 0xe7, - 0x81, 0x97, 0x27, 0xfd, 0x0b, 0x4f, 0x0f, 0x36, 0x3d, 0xfc, 0x80, 0x45, 0x2b, 0x89, 0x34, 0x54, 0xb1, 0x49, 0xe1, - 0x8e, 0x48, 0x95, 0xe5, 0xce, 0xd3, 0x1e, 0xdd, 0x6b, 0x33, 0x0f, 0xd2, 0xd5, 0x47, 0x05, 0x45, 0x6b, 0x68, 0x09, - 0xa5, 0x2e, 0x60, 0x0a, 0xa3, 0x2c, 0xde, 0xe9, 0xab, 0xf5, 0x93, 0x5d, 0x4a, 0xc2, 0x01, 0x1f, 0xc3, 0x60, 0x26, - 0xf0, 0xef, 0x87, 0x48, 0x07, 0x37, 0xb5, 0x6e, 0x85, 0x32, 0x86, 0xb4, 0x42, 0x30, 0x1f, 0x49, 0x74, 0x98, 0xe0, - 0xfb, 0xc1, 0xa4, 0xc8, 0x49, 0xc1, 0x46, 0xe3, 0x37, 0xe3, 0x1a, 0x43, 0xc7, 0x99, 0xf1, 0x9d, 0x9f, 0xae, 0xd8, - 0xdb, 0x72, 0x5c, 0x1d, 0x42, 0xc0, 0xe5, 0x58, 0xee, 0x75, 0x5d, 0x90, 0x75, 0x8c, 0x76, 0x14, 0x3e, 0x23, 0x71, - 0xa9, 0x0b, 0x3d, 0xa5, 0xda, 0x91, 0x33, 0x86, 0x25, 0x38, 0x5d, 0xcd, 0x9f, 0xd8, 0xc6, 0x15, 0xb4, 0xed, 0xec, - 0x34, 0x50, 0xb7, 0x57, 0xc0, 0x83, 0x5d, 0x63, 0x4a, 0x94, 0x25, 0x56, 0x05, 0x34, 0x18, 0x01, 0x6d, 0x59, 0x60, - 0x54, 0x13, 0x31, 0xd1, 0x28, 0x8c, 0x12, 0xa9, 0xa5, 0x94, 0x1d, 0xcb, 0x1f, 0x75, 0x92, 0x4c, 0x92, 0x75, 0x28, - 0x4e, 0x7a, 0x62, 0x92, 0xd4, 0x6a, 0x5d, 0xb6, 0x78, 0x79, 0x21, 0xf6, 0x8b, 0x54, 0x7a, 0x62, 0xef, 0xa0, 0x05, - 0x72, 0xb3, 0xef, 0x69, 0x48, 0x0d, 0x8d, 0xce, 0xf6, 0x46, 0xe7, 0xe5, 0xa9, 0x6c, 0xbe, 0xd5, 0x51, 0xcb, 0xf8, - 0xc6, 0x98, 0xa2, 0x0a, 0xa8, 0x3f, 0xd6, 0x82, 0xf4, 0xfd, 0x4b, 0xb1, 0xce, 0x50, 0x34, 0x4c, 0x5d, 0xf6, 0x58, - 0x8c, 0x74, 0x9d, 0xe6, 0x89, 0x90, 0xe0, 0xde, 0x1d, 0x18, 0x78, 0x44, 0x99, 0x3b, 0x19, 0xd3, 0x09, 0xc2, 0x10, - 0x91, 0x75, 0xb2, 0xe6, 0x7d, 0x6e, 0xfd, 0x7c, 0x14, 0x87, 0x61, 0x0c, 0x1b, 0x4c, 0xae, 0xf6, 0x53, 0x7a, 0xef, - 0xc7, 0x62, 0xa4, 0x76, 0x9d, 0xd9, 0xcd, 0x78, 0x61, 0xa9, 0x3d, 0x16, 0xf6, 0x3f, 0x64, 0x3e, 0xf5, 0x58, 0xe9, - 0xbd, 0xb4, 0x86, 0x34, 0x9e, 0x59, 0x63, 0xd5, 0x5f, 0x82, 0x76, 0xe4, 0x12, 0xed, 0xc4, 0x4e, 0xa9, 0x2a, 0x48, - 0x28, 0x48, 0x8c, 0xa9, 0xed, 0x1c, 0x0c, 0x34, 0x63, 0x9d, 0xb9, 0x63, 0x8b, 0xbe, 0x3d, 0xe5, 0xa4, 0x1c, 0xa0, - 0xbc, 0x14, 0xfe, 0xd9, 0x76, 0x50, 0x62, 0x1f, 0xc7, 0x18, 0x5b, 0x81, 0x7d, 0x48, 0x20, 0x55, 0xc1, 0x84, 0x56, - 0x93, 0x07, 0x74, 0x71, 0x4a, 0xc7, 0x9f, 0x19, 0xe6, 0x4f, 0xb0, 0xfa, 0x9a, 0x27, 0xb6, 0xd9, 0x85, 0x63, 0x4c, - 0xa9, 0xd7, 0xd9, 0x11, 0xeb, 0xa7, 0x74, 0x61, 0x8b, 0xb5, 0x31, 0xa4, 0x6c, 0xc9, 0xd6, 0xb5, 0x45, 0xc8, 0x84, - 0x21, 0xeb, 0x3a, 0x52, 0x71, 0x03, 0xe7, 0x37, 0xe4, 0x02, 0x5e, 0xef, 0xe7, 0x5c, 0xa9, 0x67, 0x11, 0xcd, 0x32, - 0x41, 0xbb, 0x04, 0x72, 0xa4, 0xf3, 0xa2, 0xfe, 0xbf, 0x95, 0x10, 0xa2, 0x4b, 0x6b, 0xba, 0x2d, 0xa1, 0x4e, 0xf2, - 0xd9, 0x59, 0xb4, 0x80, 0xc7, 0x6e, 0x94, 0x1b, 0xe7, 0xb1, 0xb4, 0x09, 0x9e, 0x0d, 0x22, 0x81, 0x0d, 0xcb, 0x29, - 0x51, 0x0d, 0xab, 0xad, 0xee, 0x7a, 0x18, 0x9f, 0xdd, 0xde, 0x28, 0xc4, 0x50, 0x61, 0xf6, 0x77, 0xa0, 0xa4, 0xe2, - 0x5e, 0x97, 0xd4, 0x3a, 0x2a, 0xff, 0x1b, 0xa5, 0x0d, 0x41, 0xe1, 0x8b, 0x9b, 0x82, 0x1d, 0xdc, 0xeb, 0x9e, 0x1a, - 0x8a, 0xfd, 0xfd, 0x42, 0x85, 0x69, 0xa7, 0x0f, 0xca, 0x04, 0x4d, 0x78, 0x0b, 0x72, 0x39, 0xf2, 0xfd, 0x6c, 0x2a, - 0xbf, 0xc8, 0x2f, 0x7d, 0x7b, 0x6d, 0x08, 0x5b, 0xd1, 0x4a, 0x2b, 0x56, 0x47, 0xf9, 0x61, 0x78, 0x13, 0xb7, 0x45, - 0x06, 0x45, 0x7d, 0x5e, 0x63, 0xef, 0x90, 0xaa, 0xc4, 0x6e, 0x7b, 0xe2, 0x06, 0x61, 0x39, 0xe9, 0x12, 0x5c, 0x58, - 0x23, 0x11, 0xa3, 0xd4, 0x9c, 0xe1, 0x54, 0x8b, 0xda, 0xc2, 0x72, 0xae, 0xd5, 0x11, 0x15, 0x10, 0xaa, 0xef, 0xa9, - 0x52, 0xd6, 0xc0, 0xb0, 0x77, 0x9e, 0x86, 0xc1, 0xcb, 0xb1, 0xab, 0x6b, 0xe5, 0xe8, 0x34, 0x5d, 0xf7, 0xb4, 0x40, - 0x81, 0x36, 0xa5, 0xb7, 0x76, 0x59, 0x8e, 0xb7, 0xea, 0x02, 0x17, 0x43, 0x0b, 0x9e, 0x3b, 0xef, 0x00, 0xbe, 0x4a, - 0x1e, 0x29, 0x3c, 0x58, 0xba, 0x76, 0x05, 0xb4, 0x30, 0x99, 0x04, 0x1e, 0x9c, 0xc5, 0x5a, 0x25, 0x6b, 0x51, 0xe1, - 0x35, 0x21, 0x0c, 0xc8, 0x59, 0x1f, 0x6c, 0xbb, 0x31, 0x72, 0x89, 0xda, 0xeb, 0x47, 0x1a, 0x5a, 0x64, 0xfd, 0xa0, - 0x49, 0xcf, 0x03, 0x45, 0xe5, 0xa8, 0x7a, 0x77, 0xa7, 0x8c, 0xbe, 0xc4, 0x3c, 0x61, 0xd4, 0x27, 0x06, 0x8d, 0xf4, - 0x85, 0x3a, 0x22, 0xe4, 0xfc, 0xc4, 0x66, 0xcd, 0x57, 0xfb, 0xf0, 0x9e, 0x10, 0xc6, 0x6a, 0xd3, 0x91, 0xcf, 0x13, - 0x68, 0xcf, 0x96, 0xae, 0x5f, 0xd4, 0x90, 0xe1, 0xb5, 0xe9, 0x72, 0x48, 0xc6, 0x82, 0xa7, 0x66, 0x08, 0x83, 0x5a, - 0xc9, 0x38, 0x4d, 0xec, 0x73, 0x16, 0x26, 0xd2, 0x55, 0xb9, 0x86, 0x00, 0xd7, 0x2f, 0x9c, 0x49, 0xb3, 0xd8, 0x72, - 0x8b, 0x92, 0xd1, 0xa5, 0x26, 0xc4, 0x16, 0x4d, 0x44, 0x06, 0x00, 0xbd, 0x1c, 0xf6, 0x11, 0x90, 0xf0, 0x6d, 0xc2, - 0xb9, 0x79, 0x62, 0x4b, 0x1b, 0xd7, 0x5c, 0x50, 0x18, 0xee, 0xe8, 0xc9, 0x5e, 0x6c, 0x2a, 0x62, 0xcf, 0x60, 0x1e, - 0x9a, 0x8d, 0x65, 0x36, 0x7f, 0xe4, 0xa7, 0xe3, 0x50, 0x0c, 0xa4, 0xff, 0xc0, 0x82, 0xf8, 0x9f, 0xa1, 0x42, 0x5c, - 0x71, 0x41, 0xfe, 0x80, 0x2b, 0x69, 0xf8, 0x82, 0x74, 0x3b, 0x9d, 0xf9, 0xd9, 0xf4, 0xa9, 0x5a, 0x40, 0x50, 0x1e, - 0x08, 0x85, 0x34, 0x17, 0x90, 0xc6, 0x0b, 0x1c, 0x58, 0x2f, 0xec, 0x90, 0x04, 0xb6, 0x9e, 0x8e, 0x64, 0xd2, 0x48, - 0xa7, 0x78, 0xe0, 0x53, 0xbd, 0xb6, 0x3f, 0xd5, 0x31, 0xa5, 0x37, 0xe5, 0x69, 0xd3, 0x3c, 0x15, 0x0f, 0x3d, 0x6b, - 0xab, 0x08, 0x13, 0x06, 0x4f, 0x85, 0x13, 0x5e, 0xef, 0xe9, 0x5a, 0xbb, 0x86, 0xaf, 0xe0, 0x8b, 0x9e, 0x0d, 0xe6, - 0xc2, 0xe6, 0x5a, 0x24, 0xe8, 0x20, 0x4c, 0x17, 0x3e, 0x3e, 0xc2, 0xc8, 0x74, 0x29, 0xbd, 0xa2, 0x1f, 0x0d, 0x0a, - 0xc5, 0xdb, 0xf5, 0x87, 0xf6, 0x2e, 0x82, 0x83, 0xb3, 0x05, 0xd9, 0x98, 0x76, 0x07, 0x20, 0x0f, 0x69, 0x51, 0xd5, - 0x18, 0x23, 0xa4, 0x42, 0x1c, 0x43, 0xc4, 0xe9, 0xf6, 0x55, 0x5b, 0x1e, 0xba, 0xe5, 0x97, 0x3c, 0x23, 0xff, 0x5e, - 0xfc, 0x99, 0xf9, 0xae, 0x6f, 0xd0, 0x15, 0xd7, 0x79, 0x0e, 0xf1, 0xbd, 0xdf, 0xb5, 0x46, 0x42, 0x94, 0x84, 0x7f, - 0x0c, 0x1e, 0x20, 0x66, 0x3c, 0x58, 0x03, 0xf6, 0xbc, 0xba, 0x91, 0x93, 0xe0, 0xbe, 0x60, 0xe8, 0x6d, 0xf3, 0xb5, - 0x7e, 0x3c, 0x26, 0xf1, 0x16, 0x6d, 0x11, 0xbb, 0x52, 0x07, 0x33, 0x76, 0xe2, 0x9c, 0x0f, 0x93, 0xd9, 0x7f, 0x8c, - 0xb0, 0xc0, 0x11, 0x0a, 0x6a, 0x2d, 0xfc, 0xb2, 0x15, 0xc0, 0xad, 0xfe, 0x83, 0x91, 0x02, 0x37, 0xd1, 0x13, 0x3f, - 0xdb, 0x3d, 0xc5, 0x26, 0x38, 0x11, 0x7b, 0x45, 0x6c, 0xcf, 0x81, 0x5a, 0xad, 0x6a, 0x0a, 0xd5, 0xad, 0xd3, 0x41, - 0xe8, 0x62, 0x51, 0x98, 0xeb, 0x75, 0x14, 0xf8, 0xac, 0x5a, 0x56, 0x1d, 0x86, 0x6c, 0x57, 0xa1, 0xf6, 0x24, 0x1b, - 0x16, 0x25, 0x2a, 0x72, 0xe3, 0x78, 0x53, 0xac, 0x03, 0xea, 0xb7, 0x7a, 0x6d, 0x82, 0x5b, 0x2f, 0x78, 0x74, 0x2c, - 0xc8, 0xb5, 0x14, 0x31, 0x78, 0x82, 0xc8, 0xe0, 0x55, 0xb9, 0x40, 0x07, 0xbd, 0x74, 0x5f, 0x37, 0x1f, 0x5a, 0xe3, - 0xe9, 0x6e, 0x1a, 0x3e, 0xfb, 0xb9, 0xf7, 0xd6, 0x88, 0xed, 0x9a, 0x31, 0x32, 0x2e, 0x92, 0x16, 0x3d, 0x75, 0x8d, - 0xcb, 0x35, 0x98, 0x3d, 0xb4, 0x3a, 0x66, 0x98, 0xbf, 0x5c, 0x69, 0x31, 0xc6, 0xef, 0x44, 0x31, 0xed, 0x41, 0x37, - 0x2b, 0xc4, 0x3d, 0xbd, 0x60, 0xc0, 0x5a, 0x4b, 0xbc, 0x69, 0xf5, 0x56, 0x5b, 0x9f, 0x2d, 0xcb, 0x20, 0xfa, 0x46, - 0x53, 0xbe, 0x9b, 0x85, 0x2c, 0x97, 0x29, 0xd6, 0x68, 0x13, 0xf6, 0xe5, 0x72, 0x6f, 0x37, 0xb6, 0x95, 0xf1, 0x6f, - 0x51, 0xf5, 0x64, 0x48, 0x24, 0x2d, 0x51, 0x2a, 0x15, 0x38, 0xe9, 0xc2, 0x10, 0x6b, 0x3a, 0x6a, 0xb9, 0x4e, 0x82, - 0xf9, 0xbe, 0x3b, 0x75, 0x58, 0xfe, 0xf8, 0x9c, 0x17, 0x69, 0xe5, 0x93, 0x22, 0xf6, 0xd9, 0xe1, 0x62, 0x42, 0x39, - 0x85, 0x33, 0xb2, 0xfb, 0x6f, 0x78, 0xb5, 0x2b, 0x80, 0x9a, 0x60, 0xf4, 0x72, 0xc9, 0xd5, 0x50, 0x94, 0x7e, 0x3a, - 0x19, 0xa6, 0x20, 0xac, 0xaf, 0xd6, 0xc2, 0x6b, 0xaf, 0x48, 0x74, 0x89, 0xbf, 0x92, 0x5e, 0x1a, 0x82, 0xa4, 0xed, - 0x50, 0x5f, 0xd5, 0x25, 0x08, 0x74, 0x88, 0x57, 0x12, 0xe0, 0x66, 0xde, 0x82, 0x26, 0x13, 0x19, 0x17, 0x6f, 0x5c, - 0x00, 0x17, 0xc6, 0xdb, 0xa7, 0x1b, 0x48, 0xd6, 0x5a, 0x62, 0x27, 0xa1, 0x9b, 0x5e, 0x1a, 0x9c, 0x00, 0x09, 0x76, - 0x3c, 0x81, 0x26, 0xef, 0x84, 0xcf, 0x5c, 0xaf, 0x26, 0xa6, 0x20, 0x88, 0xe8, 0xde, 0x73, 0xb0, 0x9b, 0xeb, 0x59, - 0x56, 0xd8, 0x84, 0xd8, 0xec, 0xa8, 0xfa, 0x7e, 0xaa, 0xc0, 0xeb, 0xa5, 0x49, 0xc5, 0x46, 0xa1, 0xeb, 0xe4, 0x0e, - 0xc7, 0x01, 0xa6, 0xb3, 0xe4, 0x50, 0xc3, 0x95, 0x8f, 0x65, 0x39, 0x49, 0x09, 0x2d, 0x85, 0x03, 0xce, 0x40, 0x72, - 0xf0, 0x3f, 0x96, 0x74, 0x90, 0x75, 0xf8, 0x89, 0x69, 0x0b, 0xfe, 0x4c, 0x5a, 0xd3, 0xb4, 0x88, 0x56, 0x7b, 0x1d, - 0x6b, 0xd0, 0xbc, 0x4a, 0x9e, 0x4f, 0x0c, 0x60, 0xb3, 0x5a, 0xc8, 0xea, 0xc7, 0x5e, 0x5b, 0xfe, 0x48, 0xf9, 0x29, - 0x0b, 0xb5, 0xa7, 0x7a, 0x6c, 0x85, 0x64, 0xa7, 0x69, 0x51, 0x11, 0xc5, 0xf5, 0x64, 0xbb, 0x21, 0x7e, 0xf8, 0x22, - 0x11, 0x94, 0x4b, 0x05, 0xc4, 0x90, 0x00, 0x04, 0x83, 0x19, 0xd4, 0x90, 0xd0, 0x51, 0x5f, 0x6f, 0x9e, 0x8e, 0x7b, - 0x08, 0x34, 0x4f, 0x85, 0x02, 0x62, 0xba, 0x62, 0x76, 0xbe, 0x0b, 0xa8, 0xe2, 0xfd, 0x1b, 0x6c, 0x9b, 0x56, 0xdf, - 0xd6, 0xb4, 0xca, 0x4f, 0xd6, 0x7f, 0xd4, 0xb9, 0x29, 0xb0, 0x21, 0x36, 0xa8, 0x52, 0x24, 0xac, 0x32, 0x06, 0x88, - 0x46, 0xcf, 0xdc, 0x64, 0x9a, 0xc2, 0xfe, 0xee, 0x3c, 0x5d, 0xf5, 0x75, 0x6a, 0xf3, 0x5d, 0xcf, 0xa5, 0xc4, 0x12, - 0x2e, 0xb3, 0xd0, 0xc7, 0x72, 0x00, 0x64, 0xa6, 0x87, 0xa5, 0x83, 0x06, 0x5f, 0x83, 0x57, 0x57, 0x2c, 0x55, 0xd7, - 0xec, 0x7e, 0xc8, 0xf8, 0xeb, 0x9b, 0xf4, 0x8a, 0xde, 0xc9, 0xc8, 0x7c, 0x73, 0xaf, 0x77, 0xd7, 0xea, 0xfa, 0x85, - 0xf5, 0x8c, 0xba, 0x54, 0x2d, 0x4f, 0x7f, 0x6f, 0xf7, 0x7d, 0x71, 0x67, 0xed, 0x4f, 0x41, 0x19, 0xdb, 0x93, 0x7c, - 0xa0, 0x9a, 0x1b, 0xff, 0x02, 0xcd, 0x9b, 0x82, 0x5a, 0x46, 0xa6, 0xbc, 0xad, 0xfd, 0x92, 0x1b, 0xf2, 0xf6, 0x44, - 0xc6, 0x11, 0xe7, 0x8e, 0x21, 0xef, 0x4b, 0xdb, 0xf8, 0xdc, 0xeb, 0x08, 0x14, 0x7e, 0x79, 0x3a, 0xa5, 0x80, 0xd6, - 0x84, 0x4b, 0xc4, 0x11, 0x5a, 0x5e, 0x97, 0x2e, 0x8a, 0x41, 0xe4, 0xe8, 0x03, 0xd8, 0xd2, 0x86, 0xe0, 0xd3, 0x22, - 0xfc, 0x6c, 0x26, 0xd4, 0x93, 0xad, 0x40, 0xad, 0x88, 0x2a, 0x7b, 0x48, 0x56, 0x02, 0xcb, 0x89, 0xe4, 0xa4, 0x27, - 0x75, 0x26, 0x90, 0x60, 0xea, 0x15, 0xef, 0xbb, 0x60, 0xc8, 0x62, 0x97, 0x2b, 0x0c, 0x2c, 0xe2, 0x64, 0xa1, 0x7e, - 0xbd, 0x3c, 0x95, 0x46, 0x0b, 0x0c, 0x01, 0x4c, 0x73, 0x2f, 0x2f, 0x1b, 0x23, 0x9e, 0xfe, 0xee, 0x86, 0x4c, 0x17, - 0x78, 0xf0, 0xcd, 0x8b, 0x9b, 0xd4, 0x52, 0x80, 0x9e, 0x9b, 0xfc, 0x6e, 0xa4, 0x9d, 0xc8, 0x09, 0xa9, 0xcd, 0x19, - 0x0e, 0x01, 0xaa, 0x9a, 0x3d, 0xc4, 0x5c, 0x2a, 0x65, 0x27, 0xae, 0x81, 0x2c, 0xbf, 0x89, 0xc0, 0x97, 0x6f, 0xe7, - 0xd8, 0x3b, 0x15, 0x95, 0xad, 0xd0, 0x0e, 0xa1, 0xa2, 0x36, 0xac, 0xee, 0xe6, 0xe1, 0x31, 0x47, 0xb0, 0xf3, 0x87, - 0x79, 0xdc, 0xd7, 0x0d, 0x8f, 0x10, 0x60, 0x05, 0xc2, 0x27, 0x04, 0x1f, 0x60, 0x88, 0x66, 0xba, 0xb5, 0xef, 0xef, - 0x55, 0x52, 0x55, 0x3c, 0x05, 0x38, 0x3e, 0xc0, 0xf0, 0xce, 0xd4, 0x63, 0xb3, 0x04, 0x9b, 0x79, 0x04, 0x86, 0x90, - 0x9b, 0xe6, 0x54, 0x53, 0x6e, 0x80, 0xf9, 0x2e, 0x62, 0x98, 0xe2, 0x91, 0xee, 0xd1, 0xf0, 0x01, 0xed, 0xc6, 0x9b, - 0x3b, 0x2f, 0xf0, 0xd3, 0x2c, 0x62, 0xd9, 0xf3, 0x64, 0x94, 0xc1, 0x27, 0x22, 0xdf, 0x22, 0x85, 0xcc, 0xfd, 0xc4, - 0x29, 0xac, 0xb6, 0x69, 0x7d, 0x51, 0x88, 0xdc, 0x5c, 0xdd, 0x98, 0x68, 0x0d, 0x5c, 0xa8, 0x4d, 0x54, 0x27, 0xd0, - 0xda, 0x66, 0x7b, 0xb8, 0xea, 0x4c, 0x24, 0x83, 0x27, 0xc2, 0xfc, 0x1b, 0xaf, 0xee, 0x64, 0xeb, 0x90, 0x8b, 0xd3, - 0xa3, 0x30, 0x57, 0x7b, 0x6b, 0xcf, 0x5b, 0xf7, 0x2d, 0x77, 0xd5, 0x9a, 0x3c, 0xa7, 0x45, 0x28, 0xb1, 0x93, 0x0c, - 0xa0, 0x08, 0xee, 0x9b, 0x41, 0xef, 0x3d, 0xd4, 0x89, 0x0c, 0x2e, 0x54, 0x31, 0xe3, 0xcc, 0x38, 0xca, 0xf3, 0x2b, - 0xae, 0x39, 0xb8, 0xfd, 0xbc, 0x71, 0x31, 0x10, 0xa0, 0xd0, 0x01, 0x99, 0xfa, 0x51, 0x99, 0xda, 0x9a, 0x26, 0xc7, - 0x7c, 0x05, 0x0b, 0x44, 0x86, 0x20, 0x00, 0x59, 0x78, 0xda, 0x56, 0xe9, 0x3e, 0x9e, 0x0c, 0x07, 0xca, 0x1b, 0x81, - 0x19, 0x19, 0x74, 0x10, 0xcd, 0x58, 0xdb, 0x99, 0x44, 0x44, 0x98, 0x84, 0x1b, 0x8b, 0x1a, 0xfe, 0xc5, 0x53, 0x52, - 0x3e, 0xe6, 0xa1, 0x87, 0x11, 0xd3, 0x62, 0x5e, 0x51, 0x7c, 0x49, 0x41, 0x3a, 0x97, 0x56, 0xdf, 0xb2, 0x4c, 0xce, - 0xa9, 0x97, 0xa1, 0xd0, 0x45, 0xc2, 0xa8, 0xb0, 0x49, 0x3d, 0x91, 0x01, 0x24, 0x63, 0x95, 0x19, 0xca, 0x15, 0x5e, - 0x8f, 0x2a, 0x79, 0x5c, 0xf2, 0x6f, 0xcc, 0xca, 0xb8, 0x1c, 0x5b, 0xd6, 0x0d, 0xeb, 0x1c, 0x1c, 0xaf, 0x54, 0xcb, - 0xe4, 0x9b, 0xa2, 0x38, 0xf1, 0xe2, 0x23, 0x06, 0xe2, 0xfd, 0xac, 0xde, 0x66, 0x9e, 0x7d, 0x58, 0xee, 0xda, 0xc2, - 0x95, 0x49, 0xc5, 0x20, 0x96, 0x30, 0x11, 0xb4, 0x28, 0x8d, 0xdf, 0x71, 0x30, 0xc5, 0x29, 0x40, 0x1b, 0x0b, 0xdf, - 0x1b, 0x49, 0x55, 0xe5, 0xb0, 0x5c, 0x46, 0x6f, 0xa5, 0xa8, 0xb3, 0x59, 0x5e, 0x46, 0x9b, 0x79, 0x12, 0x10, 0xe0, - 0xea, 0x4c, 0x59, 0xcd, 0x6e, 0x0e, 0x1d, 0x86, 0x33, 0xac, 0x2c, 0xe5, 0x84, 0x29, 0x9a, 0x35, 0x96, 0x12, 0x61, - 0xdc, 0x66, 0xb7, 0x2f, 0x8e, 0xdf, 0xd5, 0x72, 0x67, 0xfa, 0x0d, 0xdc, 0xe5, 0xae, 0x59, 0x40, 0x78, 0xe0, 0x11, - 0x9d, 0x93, 0xcb, 0x80, 0xaf, 0x8c, 0xea, 0x0d, 0x1a, 0xb0, 0x25, 0xeb, 0xa5, 0xf9, 0x58, 0x95, 0x87, 0xbe, 0x8a, - 0x5d, 0xbc, 0xd4, 0x25, 0xb4, 0x3a, 0xd4, 0xfa, 0xb0, 0xb7, 0xff, 0xb4, 0x57, 0xed, 0x34, 0xa0, 0x03, 0x62, 0x5f, - 0xeb, 0xf1, 0x65, 0x97, 0xff, 0xd5, 0x1f, 0xb7, 0x45, 0xa2, 0xed, 0x94, 0xba, 0x81, 0x0a, 0x41, 0xee, 0x40, 0xb0, - 0x95, 0xce, 0x67, 0xe5, 0x38, 0xe8, 0x85, 0x25, 0xa1, 0x16, 0x5e, 0x97, 0x97, 0x4a, 0xf0, 0x60, 0x4a, 0x49, 0xac, - 0x71, 0xaf, 0x37, 0x87, 0x01, 0x7d, 0xb8, 0xc5, 0x5a, 0x4d, 0x4c, 0x7f, 0x42, 0x54, 0x99, 0x48, 0x0f, 0x6c, 0x2f, - 0x9a, 0x98, 0xf0, 0xb0, 0x1f, 0x54, 0xa4, 0x84, 0xea, 0x40, 0xd0, 0x06, 0xca, 0xc4, 0x1c, 0x5f, 0x76, 0x28, 0x79, - 0x2e, 0xb4, 0xc0, 0x27, 0x06, 0xfb, 0x8e, 0xab, 0xb1, 0x50, 0xb1, 0x03, 0xc9, 0x31, 0x65, 0x0e, 0x37, 0xd8, 0x22, - 0xf6, 0x27, 0xd5, 0x40, 0xe9, 0xaf, 0xc6, 0x75, 0xdf, 0x56, 0x01, 0x94, 0xba, 0xe6, 0xc7, 0x7d, 0x8d, 0x42, 0x0f, - 0x16, 0xf1, 0x76, 0x08, 0xcf, 0x64, 0xbb, 0xa6, 0x22, 0xd6, 0x7c, 0x96, 0xec, 0xb9, 0x61, 0xc3, 0xdf, 0x57, 0x04, - 0x32, 0x46, 0x9a, 0x0e, 0x65, 0x6c, 0xc6, 0x2f, 0x65, 0x14, 0x53, 0x84, 0x7d, 0xe1, 0x77, 0x92, 0x10, 0x21, 0x42, - 0xc6, 0x30, 0xcd, 0x11, 0xb4, 0x33, 0x9f, 0x27, 0xb5, 0x40, 0x75, 0x4d, 0x42, 0xdf, 0xd3, 0xc3, 0x8a, 0x78, 0x90, - 0xa3, 0x47, 0x25, 0x00, 0xea, 0xbf, 0xc5, 0xbd, 0x27, 0x59, 0x31, 0x82, 0xb4, 0xe2, 0x44, 0x1a, 0x57, 0xe0, 0x38, - 0xc7, 0x27, 0x2d, 0x24, 0x88, 0x97, 0xea, 0x4e, 0x42, 0x5f, 0xb4, 0x71, 0x6a, 0xf0, 0x02, 0xb9, 0x28, 0x56, 0x2a, - 0x00, 0xb5, 0x5b, 0xf0, 0x66, 0x09, 0x33, 0x66, 0x48, 0x8f, 0xbc, 0x07, 0x6b, 0x1e, 0xf2, 0x52, 0x2e, 0x8f, 0x39, - 0x39, 0x87, 0xa8, 0xb9, 0x28, 0x92, 0x1a, 0x73, 0x05, 0x7d, 0x0d, 0x8a, 0x53, 0xe8, 0x63, 0x4c, 0xac, 0x36, 0x4f, - 0x7d, 0xaa, 0x86, 0xa2, 0xf4, 0x6c, 0x56, 0x17, 0xeb, 0x88, 0x2d, 0xb0, 0x0b, 0xcd, 0x18, 0x82, 0x5f, 0xc9, 0x24, - 0x87, 0x83, 0xb4, 0x4c, 0x04, 0x1d, 0x95, 0x17, 0x43, 0x27, 0x33, 0xda, 0xbb, 0xf4, 0x84, 0x3b, 0x7a, 0x28, 0x39, - 0x7d, 0x81, 0xd2, 0x43, 0x08, 0xd0, 0x5f, 0x8d, 0x68, 0xdc, 0xfe, 0x0a, 0x27, 0xc5, 0x8b, 0x09, 0x1f, 0x24, 0x51, - 0x84, 0x87, 0x70, 0x46, 0x14, 0x32, 0x12, 0xed, 0x43, 0xc1, 0xcc, 0x3b, 0xdb, 0xd6, 0x94, 0xf7, 0x45, 0x9d, 0x3a, - 0xcd, 0xc1, 0xcb, 0xf7, 0xe2, 0xb5, 0x5c, 0x4e, 0x3d, 0x7a, 0xec, 0xcb, 0x96, 0x90, 0x9d, 0x07, 0x00, 0x02, 0xe4, - 0x8b, 0x1d, 0x32, 0x26, 0x68, 0xc3, 0x9a, 0x96, 0x64, 0x4d, 0x3f, 0x5a, 0x84, 0x7e, 0x54, 0x7d, 0x9c, 0x66, 0x99, - 0x90, 0x6a, 0x0b, 0x63, 0x40, 0x84, 0x9e, 0x2a, 0x94, 0x60, 0x45, 0xee, 0x83, 0x97, 0xb8, 0x9a, 0x00, 0xdb, 0xb6, - 0x18, 0x9e, 0xb4, 0x37, 0x43, 0x60, 0x3b, 0x22, 0xa0, 0xd3, 0x0c, 0x89, 0x42, 0x6c, 0xb8, 0x8f, 0xd1, 0x4c, 0x52, - 0xc1, 0x98, 0x26, 0x2a, 0x1f, 0xfc, 0x07, 0xb5, 0x11, 0x37, 0x69, 0xaf, 0xe2, 0x79, 0x84, 0x3d, 0xc7, 0xa1, 0xeb, - 0xc2, 0x65, 0x40, 0x54, 0xd9, 0x72, 0x59, 0x73, 0x3d, 0x5a, 0x9e, 0x91, 0x41, 0x95, 0x48, 0xfd, 0x85, 0x5b, 0x07, - 0x95, 0x06, 0xd4, 0xb3, 0xf8, 0x64, 0xe0, 0xb9, 0x25, 0xb4, 0xdc, 0x9f, 0x23, 0x89, 0x07, 0xe0, 0xd4, 0xa3, 0x39, - 0xc2, 0x4b, 0x77, 0x87, 0x00, 0xf7, 0x56, 0x75, 0xbb, 0x69, 0x09, 0x28, 0x63, 0x27, 0xe1, 0xaa, 0xad, 0x52, 0x92, - 0x5a, 0x83, 0x12, 0xf3, 0xef, 0xf2, 0x4b, 0x3d, 0x76, 0x15, 0x1b, 0x96, 0x21, 0xd0, 0xb5, 0x42, 0xfd, 0xe5, 0x13, - 0xda, 0x49, 0xe1, 0xc6, 0xe1, 0x0d, 0xb2, 0x68, 0xf3, 0x11, 0xb5, 0x60, 0x2e, 0x50, 0x77, 0x5c, 0xd4, 0xbd, 0xf9, - 0x1b, 0xc1, 0x4d, 0x51, 0x53, 0xe8, 0x42, 0xc9, 0x46, 0x8f, 0x37, 0x12, 0x33, 0x40, 0x73, 0xb9, 0xd2, 0x0a, 0xcf, - 0xaa, 0x07, 0x6a, 0xbf, 0x21, 0x71, 0x6b, 0xbd, 0xbe, 0x0d, 0x1b, 0x3d, 0x44, 0xab, 0xc9, 0x82, 0x36, 0x46, 0x92, - 0xc7, 0xcc, 0xa1, 0xb5, 0x22, 0xd3, 0x35, 0x49, 0xb0, 0x2c, 0xa9, 0xf5, 0x6a, 0xd7, 0xf0, 0xf3, 0xb7, 0x3e, 0x40, - 0x58, 0x30, 0xb0, 0x5a, 0x49, 0xef, 0xb0, 0xdd, 0xca, 0xa5, 0x85, 0xab, 0x4d, 0xfe, 0x2c, 0x95, 0x43, 0x40, 0x9b, - 0x2c, 0xbf, 0xc4, 0xa5, 0xa7, 0x28, 0x88, 0xd4, 0x69, 0xab, 0xab, 0x84, 0x84, 0x60, 0xa5, 0x52, 0x3f, 0x1d, 0x98, - 0x90, 0x23, 0x2a, 0x47, 0x64, 0xf7, 0xba, 0x9c, 0xf3, 0x53, 0x03, 0xd2, 0xdd, 0x88, 0x48, 0xc8, 0xe9, 0x8d, 0x01, - 0x5c, 0x16, 0x1a, 0xfb, 0xdb, 0x80, 0x2b, 0x7c, 0x88, 0xe0, 0xb4, 0xef, 0x4a, 0xb9, 0x2e, 0x82, 0xfb, 0xbe, 0x40, - 0x8a, 0xaa, 0x22, 0x82, 0x05, 0xd5, 0x8e, 0x6c, 0xce, 0x8e, 0xfc, 0xc6, 0x8c, 0x02, 0xe7, 0xe6, 0x78, 0xd7, 0x28, - 0x42, 0xe9, 0x62, 0xe7, 0xbe, 0x62, 0x20, 0x4a, 0x12, 0x3e, 0x3b, 0x46, 0x68, 0xad, 0x75, 0x3e, 0xf1, 0x7e, 0xc0, - 0xb3, 0x24, 0x9c, 0x7f, 0x60, 0x93, 0xf7, 0xa5, 0x38, 0x2f, 0xaf, 0x36, 0x75, 0x5b, 0x30, 0x02, 0x50, 0x5f, 0x78, - 0xde, 0x56, 0x1e, 0xdc, 0x60, 0x64, 0x90, 0x27, 0x73, 0x81, 0xf1, 0xcc, 0xd5, 0x60, 0x9e, 0x1f, 0x3b, 0x2a, 0x04, - 0x2c, 0x04, 0xf2, 0x54, 0x53, 0x9b, 0xd6, 0x4a, 0x6c, 0xd1, 0x8e, 0xd9, 0x6f, 0xd9, 0x00, 0x27, 0xc0, 0xe9, 0x70, - 0xbc, 0xb4, 0x0d, 0xde, 0x90, 0x4b, 0x7a, 0x6b, 0x19, 0x05, 0xd9, 0x85, 0x7f, 0x1b, 0xb4, 0x06, 0xe5, 0x15, 0x08, - 0x15, 0x49, 0x1d, 0x1b, 0x25, 0xa5, 0x48, 0x1a, 0xa1, 0x65, 0xb6, 0x05, 0x59, 0x71, 0xb6, 0x47, 0x7c, 0xd5, 0xcc, - 0xe1, 0xa6, 0xc8, 0x6d, 0x91, 0xce, 0x1a, 0xee, 0x8b, 0x40, 0xc5, 0xa6, 0x90, 0x66, 0x5a, 0x23, 0xdb, 0xb8, 0x27, - 0xab, 0xb4, 0x77, 0x1b, 0x51, 0x33, 0x68, 0x44, 0xdf, 0xd2, 0x54, 0xf9, 0x7d, 0x2d, 0xaa, 0x97, 0x62, 0xa0, 0xcc, - 0x21, 0xa6, 0x6b, 0x5a, 0xc1, 0xa4, 0x4a, 0x2d, 0x8e, 0xf3, 0x36, 0x9f, 0x3e, 0x5c, 0x28, 0x87, 0xe4, 0xc0, 0x09, - 0x25, 0x47, 0x0c, 0xd9, 0x0a, 0x43, 0x70, 0x2b, 0x67, 0x13, 0xc9, 0x72, 0x23, 0x72, 0x99, 0x35, 0x46, 0x77, 0xfc, - 0x83, 0x05, 0xa0, 0xd0, 0x17, 0x1b, 0x14, 0xf4, 0x63, 0xad, 0xf5, 0x89, 0x3a, 0x52, 0x6a, 0x52, 0x7c, 0xba, 0x70, - 0x13, 0x95, 0x43, 0xcd, 0xd5, 0xab, 0xa2, 0x01, 0xb5, 0x26, 0x74, 0xc0, 0xf5, 0x08, 0x83, 0x0d, 0x84, 0xd1, 0x1f, - 0x4d, 0x21, 0x2c, 0xf7, 0x55, 0xdc, 0xb4, 0x9b, 0xbc, 0x7b, 0x3a, 0xdb, 0x63, 0xa4, 0x06, 0x15, 0x69, 0x59, 0x71, - 0x0c, 0xa7, 0x07, 0x9c, 0x83, 0xc7, 0x8e, 0x19, 0x36, 0x1b, 0xa7, 0xc7, 0x18, 0x03, 0x2c, 0x59, 0x61, 0xb1, 0x4d, - 0xa5, 0xb5, 0x22, 0x42, 0x6a, 0x9b, 0xd5, 0x4b, 0x9b, 0x3b, 0x45, 0x7e, 0xfb, 0x33, 0x00, 0xcc, 0xab, 0x26, 0xd3, - 0x3a, 0x8a, 0x29, 0x62, 0x94, 0xb4, 0x59, 0x1c, 0x2f, 0xc4, 0xca, 0x8b, 0x8f, 0x05, 0xee, 0x8f, 0x54, 0xb9, 0xb2, - 0xec, 0xb8, 0x3a, 0x93, 0xfb, 0xe1, 0xe6, 0x87, 0xcc, 0x49, 0xc4, 0x03, 0x16, 0xfa, 0x8c, 0xd9, 0x70, 0x75, 0xe2, - 0x1d, 0xa9, 0xc3, 0x2c, 0x26, 0xf7, 0xba, 0x78, 0xcb, 0xc7, 0xb9, 0x0b, 0xa8, 0xec, 0x41, 0xec, 0xb6, 0x2a, 0x63, - 0xbd, 0xce, 0xc8, 0x20, 0xe1, 0x5b, 0x2a, 0xf6, 0x4a, 0xc6, 0x4e, 0x7c, 0x06, 0x99, 0x1e, 0x2c, 0xc3, 0xc2, 0x53, - 0x46, 0x72, 0xfb, 0x4c, 0x15, 0xb5, 0xeb, 0x29, 0x95, 0xeb, 0xa2, 0x3b, 0xaf, 0xb9, 0xb7, 0x15, 0xee, 0xd4, 0xcc, - 0xa4, 0x13, 0xaf, 0x0b, 0x50, 0xe7, 0x83, 0x97, 0x16, 0xe9, 0x9c, 0x37, 0xb0, 0x6a, 0x85, 0xc2, 0x75, 0xa9, 0x46, - 0x9f, 0x5d, 0xee, 0xa3, 0x2d, 0x8e, 0x4d, 0x77, 0x7e, 0x5d, 0xf6, 0x68, 0xf2, 0x59, 0x87, 0x40, 0xec, 0x29, 0x22, - 0x3e, 0xa5, 0xc1, 0xad, 0x75, 0x98, 0x69, 0xab, 0xad, 0x0c, 0x54, 0x9b, 0xa4, 0x16, 0xf8, 0x49, 0x9b, 0xd2, 0xec, - 0x70, 0x6a, 0x79, 0xd7, 0x20, 0x96, 0xf8, 0x05, 0x0e, 0xab, 0x62, 0xf5, 0xec, 0xf1, 0x2d, 0xae, 0xac, 0x0c, 0x73, - 0xbf, 0x1e, 0x55, 0x0e, 0xb3, 0xb9, 0xe2, 0x78, 0x53, 0x1d, 0x91, 0x48, 0x6d, 0x3f, 0xf7, 0xf3, 0x27, 0x43, 0x45, - 0x8f, 0x83, 0x81, 0x38, 0x50, 0x55, 0xe4, 0x4c, 0x89, 0xb0, 0x0a, 0xa7, 0x25, 0x9a, 0x86, 0xc6, 0x3a, 0x14, 0x04, - 0x64, 0xd4, 0xff, 0x81, 0x70, 0x10, 0x99, 0xb7, 0x4e, 0x48, 0xaa, 0x2a, 0x35, 0x2c, 0xd1, 0x5e, 0xec, 0x7b, 0x48, - 0xe1, 0x21, 0x4f, 0xb6, 0x3e, 0x6f, 0xbf, 0xce, 0x91, 0x05, 0x0f, 0x04, 0xa3, 0x4c, 0x12, 0x03, 0x5b, 0x47, 0x97, - 0x7a, 0xd9, 0x8b, 0xbb, 0x4c, 0x40, 0x4f, 0x77, 0x1e, 0x7f, 0x84, 0x43, 0x51, 0xda, 0x9c, 0xbf, 0x6a, 0x49, 0x36, - 0xf3, 0xe8, 0xb6, 0x6a, 0xac, 0x43, 0x24, 0x36, 0x97, 0x1c, 0x2d, 0xe7, 0x45, 0x9e, 0x72, 0x74, 0xf9, 0x00, 0x8c, - 0x85, 0x77, 0xe7, 0x5c, 0x35, 0x17, 0x52, 0x4d, 0x5f, 0x1c, 0x13, 0xb8, 0x0e, 0x8f, 0xd8, 0x4a, 0xdb, 0x06, 0xeb, - 0xc1, 0x72, 0x88, 0xe7, 0xdc, 0x50, 0xae, 0x3f, 0xd4, 0x92, 0x6a, 0x52, 0xcf, 0x60, 0x1a, 0x2b, 0x75, 0x82, 0x26, - 0x65, 0xce, 0x2b, 0x9e, 0x3a, 0x98, 0x3a, 0x74, 0x93, 0x44, 0xf4, 0xd7, 0x91, 0x39, 0x91, 0xa4, 0x49, 0x3f, 0xb6, - 0x8d, 0x0a, 0x08, 0x80, 0x8e, 0x56, 0x08, 0x68, 0xf7, 0xbd, 0x5b, 0x7d, 0x26, 0xc9, 0x87, 0x67, 0x3d, 0x8a, 0xb9, - 0xd6, 0xd1, 0x56, 0xd7, 0xb0, 0x7c, 0x7b, 0x45, 0x18, 0xcd, 0xdb, 0x03, 0xb3, 0xc2, 0xd9, 0x88, 0x14, 0x63, 0xe7, - 0x2d, 0x20, 0x61, 0x1e, 0x22, 0xc7, 0xbb, 0x2e, 0x6a, 0xdc, 0x4a, 0xe7, 0xe8, 0xbc, 0x08, 0x4f, 0x9b, 0x2b, 0x16, - 0x4a, 0xd1, 0x4b, 0xe5, 0xd8, 0x6f, 0xde, 0x99, 0x51, 0x43, 0x5e, 0xf2, 0xb0, 0xf1, 0x7e, 0x94, 0xa7, 0xa7, 0x30, - 0x3a, 0x3f, 0xc4, 0x61, 0xee, 0x48, 0x5f, 0xa9, 0x03, 0xf4, 0x7a, 0x4f, 0x0e, 0xdf, 0xae, 0xef, 0x65, 0x27, 0x38, - 0x5c, 0x18, 0x2e, 0x8a, 0xf3, 0x05, 0xa9, 0x24, 0xe6, 0x28, 0xf5, 0x78, 0x51, 0x4f, 0xf1, 0x81, 0x78, 0xe5, 0x04, - 0xdb, 0x5e, 0xf6, 0xfd, 0xdf, 0x85, 0x33, 0x29, 0xbf, 0xef, 0x2e, 0x81, 0xaf, 0x07, 0x7f, 0xe8, 0xf6, 0x0b, 0x1c, - 0x89, 0xc8, 0x61, 0x1c, 0xee, 0xd8, 0x56, 0x71, 0xbd, 0x6f, 0xc3, 0x16, 0xa9, 0xd7, 0x1f, 0x27, 0x84, 0x5c, 0x37, - 0xe4, 0xa8, 0x3b, 0x28, 0xe2, 0x65, 0x09, 0x4c, 0xdc, 0x14, 0x42, 0x14, 0xe3, 0xbf, 0x5c, 0xcd, 0x53, 0x84, 0x5f, - 0x35, 0xa2, 0xb0, 0x55, 0x53, 0x53, 0x70, 0x57, 0x60, 0x00, 0x56, 0xf2, 0x04, 0x77, 0xa0, 0xe5, 0x43, 0x59, 0x78, - 0x85, 0x8e, 0xd5, 0xa2, 0xac, 0x04, 0x6a, 0x99, 0x21, 0x8f, 0x08, 0x4e, 0xd0, 0x5e, 0x84, 0x59, 0xd7, 0x30, 0x29, - 0xf7, 0x60, 0xf2, 0xb6, 0x6e, 0xe1, 0x75, 0xb7, 0xa9, 0xd3, 0xc3, 0xfb, 0x55, 0x69, 0xb1, 0xab, 0xb2, 0xc7, 0x03, - 0xe4, 0x28, 0x39, 0xbd, 0x03, 0x30, 0xe7, 0x61, 0x12, 0xd8, 0xea, 0xd2, 0x1c, 0xb6, 0x76, 0x97, 0xd0, 0x6f, 0x33, - 0x7c, 0xba, 0x43, 0x66, 0xa3, 0xa4, 0x9d, 0x7d, 0xfe, 0x53, 0x05, 0x8b, 0xa1, 0x37, 0x00, 0x9e, 0xb0, 0xee, 0x64, - 0xb5, 0xb0, 0x81, 0x7b, 0xfc, 0xd3, 0x87, 0xa6, 0x28, 0xa4, 0x25, 0xa6, 0xb1, 0x8b, 0xa3, 0x9a, 0x6c, 0xad, 0xf6, - 0x1a, 0x39, 0xbb, 0x21, 0x71, 0x55, 0x4a, 0x88, 0x2e, 0x47, 0xb4, 0x42, 0xb2, 0x47, 0x14, 0xc1, 0x6a, 0xef, 0x2c, - 0xdd, 0x46, 0x5f, 0xc3, 0x74, 0x05, 0x18, 0x4d, 0xc0, 0xb0, 0x41, 0xa5, 0xbd, 0x13, 0x00, 0x18, 0xa5, 0x55, 0x53, - 0xa7, 0xf4, 0x2e, 0x76, 0xd9, 0xe5, 0xe6, 0x41, 0xa6, 0xd4, 0x13, 0x35, 0x93, 0xdb, 0x03, 0x2a, 0x6b, 0x2d, 0x54, - 0xb2, 0x5f, 0x72, 0xc5, 0xa7, 0x51, 0x89, 0x56, 0xe8, 0x6a, 0x46, 0x07, 0xdd, 0x4c, 0xd1, 0x51, 0x22, 0xb6, 0x4c, - 0x4c, 0xbb, 0xf2, 0x66, 0x98, 0x78, 0xa9, 0xd8, 0x2a, 0x33, 0x22, 0x4d, 0xd9, 0xa2, 0x96, 0x23, 0xe2, 0xfc, 0xa8, - 0xbd, 0x66, 0x55, 0xa7, 0x36, 0xd6, 0x5a, 0x78, 0xba, 0x38, 0xc4, 0xe4, 0xea, 0x43, 0xb4, 0xdd, 0x07, 0x29, 0x38, - 0xd3, 0xa6, 0x8d, 0x2b, 0xb5, 0xcd, 0xbe, 0x88, 0x32, 0x5e, 0x91, 0x71, 0x11, 0xb3, 0xd9, 0xed, 0x93, 0xa5, 0x1d, - 0x26, 0xca, 0xe3, 0x98, 0x4c, 0x46, 0x0e, 0x54, 0xd2, 0x06, 0xaa, 0x25, 0xf7, 0x92, 0x15, 0x17, 0x71, 0xf1, 0xdf, - 0x68, 0xd9, 0xe6, 0xd9, 0xc2, 0xc0, 0x82, 0x16, 0x66, 0x89, 0x02, 0xb3, 0x54, 0x4a, 0x07, 0x25, 0x1c, 0x45, 0x64, - 0x27, 0x09, 0xd3, 0xcb, 0x92, 0x36, 0xf8, 0xa0, 0x91, 0xee, 0x4e, 0x26, 0x0d, 0x09, 0x97, 0x6b, 0x9c, 0xb5, 0x2d, - 0x26, 0x32, 0xe2, 0xa9, 0x6f, 0x4a, 0x26, 0xc2, 0x48, 0x3c, 0xc8, 0x94, 0x98, 0x0b, 0xcf, 0x06, 0x52, 0xe2, 0x8b, - 0x9c, 0x7e, 0xae, 0x17, 0xb3, 0xd1, 0x22, 0x8d, 0xfe, 0xa1, 0xe7, 0x97, 0xc5, 0xce, 0x6e, 0x47, 0x8c, 0x7a, 0x7b, - 0x5c, 0x79, 0x56, 0x53, 0x6b, 0xd7, 0x8c, 0x1c, 0x33, 0x06, 0x34, 0x52, 0x08, 0x14, 0xd2, 0x27, 0x23, 0x9c, 0x16, - 0x97, 0x03, 0x1b, 0x36, 0xbe, 0x53, 0x8e, 0x67, 0x0a, 0xb7, 0x17, 0x43, 0xc3, 0x73, 0x87, 0x44, 0x10, 0xa1, 0xf1, - 0x06, 0x67, 0xce, 0x50, 0xff, 0xe1, 0xe9, 0xbc, 0x35, 0xd7, 0xfd, 0x0f, 0x8a, 0x9e, 0xa5, 0x45, 0x44, 0x80, 0xf3, - 0x45, 0x45, 0x9a, 0xdb, 0x7b, 0x27, 0x8b, 0xac, 0xc7, 0x37, 0xcd, 0xfa, 0x15, 0x01, 0xdc, 0x49, 0x02, 0x42, 0x80, - 0x86, 0xd7, 0xf5, 0x7c, 0x38, 0x4b, 0x58, 0x1e, 0x60, 0xba, 0xab, 0xe0, 0xef, 0xc4, 0x4d, 0xce, 0x4b, 0x13, 0xfa, - 0xb1, 0xa8, 0xe0, 0x83, 0x9d, 0x2c, 0x10, 0x6e, 0x01, 0x96, 0x10, 0x04, 0x82, 0x92, 0x99, 0x29, 0xa6, 0x12, 0xfa, - 0x0b, 0x29, 0x21, 0x43, 0x02, 0x4c, 0x47, 0xe3, 0x82, 0x1b, 0x24, 0xd5, 0x46, 0x3c, 0xad, 0x62, 0x36, 0x9c, 0x34, - 0x0c, 0x88, 0xf5, 0xc7, 0x30, 0xd8, 0x2a, 0x26, 0xc9, 0xb0, 0xbf, 0xb3, 0x37, 0x9e, 0x0c, 0xa7, 0x4b, 0x14, 0x72, - 0xb1, 0xcf, 0x98, 0x3c, 0xa1, 0xe9, 0x17, 0x85, 0xa8, 0x2f, 0xeb, 0x82, 0xd3, 0x7b, 0x76, 0x74, 0x07, 0x4f, 0xae, - 0x32, 0xd2, 0xdb, 0x38, 0xb0, 0xdc, 0x42, 0x22, 0xc0, 0xbc, 0xdf, 0x03, 0xcd, 0x48, 0x32, 0x64, 0x28, 0x03, 0xcc, - 0x35, 0x66, 0x4f, 0x0d, 0x4d, 0x0f, 0x65, 0x47, 0x72, 0x6d, 0x12, 0xac, 0x1e, 0xe6, 0xbe, 0xbc, 0xb2, 0x6e, 0x73, - 0xbd, 0x03, 0xb9, 0x6e, 0x6b, 0x08, 0xd8, 0xe5, 0x88, 0x34, 0xa7, 0x26, 0xb7, 0x09, 0xd5, 0x03, 0x14, 0x48, 0x35, - 0xd5, 0xb4, 0x0e, 0x0e, 0x37, 0x7c, 0xd0, 0x01, 0xe1, 0x26, 0xc4, 0x46, 0xe5, 0x11, 0x7a, 0xad, 0xc6, 0x3e, 0xd1, - 0xd7, 0x92, 0x6b, 0x9a, 0x6f, 0x90, 0x3a, 0x72, 0xa9, 0xea, 0x3c, 0x4e, 0xd4, 0xb5, 0xb6, 0xda, 0x82, 0x2d, 0xc2, - 0x00, 0x8b, 0x55, 0x0c, 0x87, 0xe8, 0x54, 0xa8, 0x68, 0x89, 0x7b, 0x1b, 0x73, 0xd5, 0xcb, 0x9d, 0xb7, 0x55, 0x97, - 0x7a, 0xa7, 0x06, 0x8d, 0xc8, 0xf4, 0x50, 0x01, 0x0e, 0x84, 0x8c, 0xb5, 0x7d, 0xb0, 0x8c, 0xe3, 0x8c, 0x54, 0x65, - 0xd8, 0x08, 0x46, 0xd3, 0x01, 0xca, 0x5a, 0xf5, 0x38, 0x9c, 0x03, 0x62, 0x79, 0x48, 0x6e, 0x9a, 0xcc, 0x10, 0xd9, - 0x22, 0x9b, 0x5f, 0x6a, 0xf2, 0xe4, 0x0a, 0x1d, 0x0d, 0xfb, 0x1e, 0xd0, 0xae, 0xee, 0xc0, 0x40, 0x76, 0xf8, 0xaa, - 0x93, 0xce, 0x72, 0x09, 0xb4, 0x39, 0x86, 0xce, 0x85, 0xc5, 0x29, 0x9f, 0xa7, 0x23, 0x1b, 0xee, 0x1d, 0xe0, 0x45, - 0x47, 0xd7, 0x0b, 0xf0, 0xdb, 0xc1, 0xc5, 0x1d, 0x63, 0x0f, 0x6e, 0xca, 0xa3, 0x2c, 0x3b, 0x95, 0x30, 0x95, 0x47, - 0x13, 0x17, 0xeb, 0x9c, 0x0b, 0x5d, 0xce, 0xe6, 0x75, 0xba, 0xf5, 0x27, 0x2a, 0x86, 0x9b, 0xb5, 0x73, 0x06, 0xcf, - 0x55, 0x4e, 0x87, 0x24, 0x62, 0x49, 0x8b, 0x73, 0xf4, 0x85, 0x44, 0x9e, 0xd6, 0xf9, 0xfd, 0x42, 0x81, 0xce, 0xa9, - 0x83, 0x6a, 0x1d, 0xe3, 0xcc, 0x4e, 0x0f, 0x3a, 0xef, 0x95, 0xc6, 0xa2, 0xb1, 0x4a, 0x59, 0xf1, 0x1f, 0x38, 0xb7, - 0xf4, 0xf6, 0x84, 0xb0, 0x49, 0x2a, 0xa4, 0x50, 0x96, 0x09, 0xb7, 0x3d, 0x0e, 0x34, 0x6d, 0xe7, 0x44, 0x76, 0x5b, - 0xdf, 0xbe, 0x93, 0x24, 0x22, 0x71, 0xdb, 0x0b, 0xa2, 0xf0, 0x0c, 0xd0, 0x18, 0x92, 0xb3, 0xe7, 0x9d, 0x75, 0xf9, - 0xd2, 0xcb, 0x72, 0xbc, 0xc2, 0xde, 0x15, 0x83, 0xb1, 0xb0, 0x42, 0x0b, 0x0b, 0x37, 0x0d, 0xd4, 0xb1, 0x93, 0x24, - 0x76, 0x59, 0x12, 0x3f, 0xb6, 0xfc, 0x33, 0x69, 0x6e, 0x44, 0x9e, 0x8a, 0x8e, 0x75, 0xc8, 0x3e, 0x73, 0xaa, 0x54, - 0xf7, 0x5a, 0xe5, 0x41, 0x39, 0xe6, 0xa9, 0x1a, 0x31, 0x67, 0x6e, 0x33, 0x45, 0x3e, 0x92, 0x3e, 0x6f, 0xae, 0x67, - 0x94, 0x28, 0x10, 0xa9, 0x0b, 0xbd, 0xca, 0x9c, 0xab, 0xd0, 0x91, 0x42, 0x4a, 0xb7, 0x46, 0xb3, 0x89, 0x39, 0x0e, - 0x67, 0x3f, 0x55, 0xd9, 0x13, 0x7c, 0xed, 0x3d, 0x6f, 0xed, 0xc3, 0x66, 0x83, 0xeb, 0x50, 0xf3, 0x21, 0x3d, 0x60, - 0xa6, 0x99, 0x3b, 0x53, 0x20, 0x0b, 0xdb, 0xaf, 0xec, 0x48, 0x94, 0x32, 0xfd, 0x63, 0xa3, 0x75, 0x7d, 0xd9, 0x47, - 0x75, 0x4c, 0xfe, 0xfd, 0x2d, 0x5d, 0xc3, 0x55, 0x07, 0x45, 0x8e, 0xe1, 0x58, 0xd1, 0x6e, 0xa5, 0x3b, 0x00, 0xe1, - 0x35, 0x3b, 0x8c, 0xdc, 0x72, 0x36, 0x45, 0xbd, 0x55, 0x57, 0xc1, 0x02, 0x6a, 0xd4, 0x91, 0x94, 0xbd, 0x51, 0x58, - 0x44, 0xfd, 0x9a, 0x5d, 0x8b, 0x2b, 0x8a, 0x6e, 0x59, 0xe3, 0x7e, 0xc8, 0xec, 0xa8, 0x3f, 0xe2, 0x5a, 0xb9, 0xc3, - 0x0c, 0xd9, 0xe1, 0x1a, 0x53, 0x48, 0xea, 0x8d, 0xc6, 0xcd, 0xb6, 0xd5, 0xf3, 0x4c, 0xc3, 0xb8, 0x6d, 0xcd, 0xd2, - 0x26, 0x76, 0x50, 0x0d, 0xb7, 0x75, 0xc1, 0x54, 0xb5, 0x5d, 0xf8, 0xfa, 0xd5, 0x6e, 0x25, 0xb2, 0x26, 0xb4, 0xe1, - 0x68, 0x6b, 0x60, 0x9a, 0x16, 0xf9, 0x5c, 0xf4, 0xec, 0x6a, 0xb0, 0xc5, 0xbe, 0x0b, 0xd9, 0xbc, 0xfb, 0x6b, 0x95, - 0x84, 0x2a, 0xb9, 0x72, 0x1f, 0x97, 0xe4, 0x27, 0x9d, 0xac, 0xc2, 0x33, 0xb5, 0x8d, 0xfc, 0x0e, 0x27, 0xda, 0x87, - 0x95, 0xe6, 0x69, 0x25, 0xb3, 0x10, 0x10, 0x85, 0xae, 0xf0, 0x2a, 0x04, 0xba, 0x05, 0x0b, 0xff, 0x07, 0x3a, 0x76, - 0x65, 0x5c, 0x0a, 0xe9, 0x8d, 0xca, 0x39, 0x74, 0x43, 0x42, 0x3e, 0xb4, 0xb0, 0x9c, 0x9c, 0x97, 0x1a, 0x74, 0xb5, - 0x35, 0x74, 0x64, 0x79, 0x20, 0x02, 0xfc, 0x44, 0x0e, 0x79, 0xa6, 0x26, 0xd8, 0xfd, 0x24, 0x70, 0x96, 0x66, 0xb3, - 0x08, 0xbf, 0x18, 0x70, 0x86, 0xa4, 0xf6, 0x9e, 0x7e, 0xf9, 0x14, 0x68, 0x0f, 0xbf, 0x5c, 0x68, 0x7d, 0x72, 0x66, - 0xce, 0x51, 0x4b, 0xc7, 0x0d, 0x1c, 0xc2, 0x45, 0x69, 0xc0, 0xf7, 0x48, 0x35, 0x86, 0x29, 0x22, 0x4c, 0x0e, 0xe0, - 0x5c, 0xb9, 0x6d, 0x78, 0x56, 0x6e, 0x02, 0x33, 0x1d, 0x29, 0xa5, 0x15, 0xc7, 0xa8, 0xfb, 0xb6, 0xf7, 0xa3, 0x24, - 0xe9, 0xcd, 0xc7, 0xcb, 0xac, 0x50, 0xfa, 0x9e, 0x99, 0x85, 0xae, 0xe2, 0x77, 0x26, 0xb9, 0xab, 0x4b, 0xe8, 0xa4, - 0x5a, 0xce, 0x80, 0x51, 0xae, 0x56, 0x58, 0xee, 0x84, 0x40, 0x0e, 0x9b, 0xfb, 0xe9, 0x66, 0x90, 0x26, 0x5b, 0x51, - 0x95, 0x18, 0x23, 0x52, 0x68, 0xbf, 0xd9, 0x9d, 0xfb, 0xa3, 0xd5, 0x0c, 0x3a, 0xea, 0x3b, 0x66, 0x5c, 0xcd, 0xb7, - 0x62, 0xbb, 0xd8, 0xb0, 0x83, 0x69, 0x14, 0x75, 0x98, 0xe6, 0x01, 0x42, 0xf7, 0x2c, 0x1d, 0xa8, 0x5f, 0x10, 0x9f, - 0xf2, 0x76, 0x55, 0x6d, 0x1d, 0xe4, 0x62, 0xa6, 0xa2, 0x7c, 0x8a, 0x1a, 0x14, 0xb0, 0x68, 0xdd, 0x2e, 0x4d, 0xc0, - 0x14, 0x59, 0x48, 0xb7, 0x90, 0x82, 0x28, 0x59, 0x08, 0x66, 0x50, 0xf1, 0x99, 0xbf, 0x4c, 0x7c, 0xad, 0x8f, 0x16, - 0x3c, 0xa5, 0x27, 0x6c, 0x15, 0x72, 0x75, 0xc7, 0x68, 0x31, 0xab, 0x4e, 0x3b, 0x4e, 0x13, 0x87, 0x0e, 0x35, 0xea, - 0x88, 0xd8, 0x75, 0x7c, 0xf0, 0x54, 0x32, 0x79, 0x83, 0xec, 0x2f, 0x27, 0x01, 0x3f, 0xd6, 0xb3, 0x5f, 0x32, 0x7b, - 0x88, 0x55, 0x69, 0xc6, 0xe3, 0x85, 0xb2, 0x47, 0xe5, 0xa8, 0xa8, 0x35, 0xf6, 0x73, 0x17, 0xa7, 0xb5, 0x51, 0x49, - 0x21, 0x77, 0x1e, 0x2e, 0xe4, 0x2b, 0xa7, 0x70, 0xee, 0x46, 0x25, 0xa2, 0x3c, 0x80, 0x99, 0xb0, 0x39, 0x71, 0xa3, - 0xe2, 0x16, 0x50, 0x39, 0xd3, 0x93, 0x26, 0x31, 0x9d, 0x95, 0x88, 0x31, 0xa3, 0x4b, 0xb8, 0x1e, 0x87, 0x68, 0x0c, - 0xcd, 0x30, 0xa7, 0xf7, 0x31, 0x7a, 0x82, 0x1c, 0x50, 0x0f, 0xed, 0x5a, 0x43, 0x88, 0x99, 0x54, 0xf8, 0x56, 0xad, - 0x88, 0x2d, 0xb3, 0x4f, 0x04, 0xb5, 0x6d, 0x2e, 0xf3, 0x88, 0x28, 0x6f, 0x29, 0x7c, 0x9f, 0xfb, 0xcb, 0x77, 0x8c, - 0x57, 0x72, 0xe8, 0x9d, 0x8e, 0x92, 0x9f, 0xc3, 0xfc, 0xec, 0x37, 0x0e, 0x60, 0x01, 0x11, 0xe7, 0x92, 0x9c, 0x7a, - 0x4a, 0x96, 0xe6, 0x3a, 0xeb, 0x75, 0x13, 0xc1, 0x2c, 0x99, 0x06, 0x4c, 0xac, 0x65, 0x16, 0x40, 0x07, 0x52, 0x09, - 0x9c, 0x15, 0x95, 0x75, 0x34, 0x93, 0x47, 0x0b, 0xbd, 0x37, 0xf1, 0xf0, 0x45, 0x29, 0xc6, 0x02, 0xfc, 0xb1, 0xa5, - 0xc6, 0xa2, 0x4c, 0xdf, 0xbc, 0x08, 0x54, 0xcd, 0x5a, 0x1e, 0x87, 0x74, 0xe9, 0xf5, 0x8a, 0x9a, 0x55, 0x49, 0x6b, - 0xa9, 0x2e, 0xd0, 0x76, 0x40, 0x8e, 0x51, 0x8b, 0xea, 0x0c, 0xd2, 0x50, 0xb4, 0x07, 0x4a, 0x5f, 0xc3, 0x84, 0x1e, - 0xf0, 0x4b, 0x35, 0x90, 0xd9, 0xe0, 0x9d, 0x4d, 0xb7, 0xb8, 0x98, 0x1c, 0x39, 0xeb, 0x06, 0x10, 0x70, 0xbb, 0xde, - 0x96, 0x9a, 0x08, 0xa9, 0x70, 0x83, 0x71, 0x59, 0x24, 0xea, 0x2f, 0x9a, 0xc3, 0xda, 0x15, 0x92, 0x3a, 0xc4, 0x3a, - 0xb4, 0x30, 0x01, 0xad, 0x19, 0x17, 0x1b, 0x5a, 0x94, 0x9d, 0xc8, 0x81, 0xb5, 0x59, 0x24, 0x19, 0x87, 0x3d, 0x9a, - 0x69, 0x33, 0x91, 0x6b, 0x09, 0x9e, 0x4a, 0x44, 0x6f, 0xd1, 0xf4, 0xeb, 0x07, 0x15, 0x36, 0x37, 0x99, 0x54, 0xca, - 0x4c, 0x8f, 0x86, 0x40, 0xbb, 0x76, 0x07, 0x7c, 0x87, 0x0a, 0xfe, 0x12, 0x3e, 0x18, 0x45, 0xf7, 0xfb, 0xec, 0x69, - 0x07, 0x7c, 0x08, 0xa7, 0x4e, 0xfb, 0x45, 0x80, 0x75, 0x0e, 0x94, 0x62, 0x5d, 0x98, 0xe3, 0x8c, 0xa3, 0x76, 0x35, - 0xa3, 0x8d, 0xfd, 0xc4, 0x18, 0x02, 0x85, 0xc3, 0xb7, 0x3d, 0x5a, 0x79, 0xd5, 0xcc, 0xd6, 0x4c, 0x2f, 0x69, 0x47, - 0x3e, 0xa2, 0x46, 0x30, 0x09, 0x22, 0x69, 0x99, 0x40, 0x68, 0xc6, 0xe8, 0x2d, 0x5c, 0xc1, 0xda, 0x9c, 0x01, 0x2d, - 0x75, 0xbd, 0x50, 0xe8, 0x81, 0xa7, 0xe7, 0x4c, 0x4c, 0x0a, 0xf3, 0x01, 0x2e, 0x69, 0xff, 0xde, 0x1c, 0x66, 0x0d, - 0xd5, 0x6a, 0x6d, 0xb7, 0x65, 0x7d, 0x97, 0x28, 0x10, 0xb6, 0x1f, 0xda, 0x45, 0xf7, 0x23, 0x3f, 0xbb, 0x16, 0xa0, - 0xce, 0x62, 0xdb, 0x35, 0x9e, 0xf4, 0xd7, 0x5e, 0xb7, 0x04, 0x1f, 0xfb, 0x2b, 0x0d, 0x9f, 0x54, 0x0c, 0xcb, 0x92, - 0x09, 0xd3, 0x95, 0xe5, 0x18, 0x67, 0xa5, 0xb8, 0xcf, 0xcb, 0x98, 0x74, 0x77, 0x28, 0x31, 0x89, 0xaf, 0x3b, 0x1b, - 0xd0, 0xb7, 0x8c, 0xe8, 0x65, 0xfd, 0x56, 0xcf, 0xb0, 0xd7, 0x25, 0x80, 0x98, 0x7a, 0x45, 0xc5, 0x78, 0x98, 0xe8, - 0x8b, 0x87, 0xa0, 0x30, 0x7e, 0x94, 0xb9, 0x18, 0x7c, 0x52, 0x6f, 0x5b, 0x48, 0x84, 0x9f, 0xc6, 0xa3, 0xb8, 0x98, - 0xb5, 0x68, 0xd8, 0x76, 0x3d, 0x29, 0x0e, 0x84, 0x84, 0xd6, 0xcc, 0xa7, 0x49, 0x5a, 0x73, 0x29, 0x0c, 0xbf, 0x59, - 0x88, 0x8d, 0x66, 0xe3, 0x28, 0x5a, 0x0b, 0x60, 0x74, 0x55, 0x73, 0xc5, 0x62, 0xe0, 0x61, 0xc1, 0x43, 0xf9, 0xd2, - 0x12, 0x96, 0x3d, 0x7f, 0x9f, 0x4e, 0xe4, 0x9b, 0xbb, 0x9c, 0x6e, 0xbf, 0x77, 0x9d, 0xbd, 0xb9, 0x4b, 0x27, 0xca, - 0xea, 0x17, 0x1d, 0x95, 0xa8, 0xc6, 0xfa, 0xd8, 0xfa, 0x70, 0x97, 0x5b, 0xfd, 0x44, 0x72, 0xda, 0xd9, 0x0e, 0x18, - 0xb7, 0x14, 0xb0, 0x65, 0xda, 0x1e, 0x36, 0xe5, 0x3f, 0xde, 0xba, 0x38, 0xd2, 0x28, 0x48, 0x7c, 0xc2, 0x9c, 0x21, - 0x49, 0xf1, 0xd8, 0x64, 0x00, 0xa3, 0x96, 0x01, 0xf5, 0x08, 0xf6, 0x75, 0x63, 0x47, 0xbe, 0xb9, 0x8c, 0x71, 0xa9, - 0x4e, 0xbb, 0x0e, 0x64, 0xda, 0xe5, 0x21, 0xb0, 0x71, 0x9b, 0xbb, 0x1c, 0x28, 0x12, 0x07, 0x2a, 0x62, 0xa6, 0xfd, - 0x22, 0xf5, 0xa7, 0x1b, 0xc4, 0x46, 0xed, 0xc0, 0xf5, 0x39, 0xd8, 0x14, 0xfb, 0x64, 0xb1, 0xdf, 0xca, 0x3b, 0x3b, - 0xec, 0x8d, 0xf4, 0x47, 0x9c, 0x9b, 0xcf, 0x38, 0x30, 0xa2, 0x4a, 0x73, 0x31, 0x2b, 0x91, 0x2a, 0xb2, 0xa7, 0x95, - 0xef, 0x2f, 0xa4, 0x49, 0xe0, 0xdc, 0xad, 0x0f, 0x3d, 0x9c, 0xcc, 0x9e, 0x08, 0x93, 0x39, 0xe4, 0x3b, 0x78, 0x49, - 0x89, 0xa6, 0x0b, 0x8d, 0xb6, 0x5b, 0x07, 0x04, 0x76, 0x02, 0xe6, 0x69, 0x89, 0xbc, 0x4e, 0xc9, 0x4b, 0x7e, 0xff, - 0xf6, 0xcf, 0xd2, 0xb2, 0x80, 0xe1, 0xc8, 0x53, 0x5c, 0xa5, 0x00, 0x11, 0xc7, 0x71, 0xfe, 0x6a, 0xdd, 0x67, 0x24, - 0xc6, 0xfa, 0xf3, 0xbb, 0x1f, 0xec, 0x3e, 0x41, 0xae, 0xa4, 0xa1, 0xf0, 0xcc, 0xcd, 0x91, 0x9d, 0x83, 0xec, 0xca, - 0xb8, 0x62, 0xb7, 0x41, 0x3f, 0x89, 0x2c, 0x2a, 0xd1, 0x4c, 0xeb, 0xcd, 0x29, 0x16, 0x49, 0x49, 0x2b, 0x2c, 0x6a, - 0xc9, 0x17, 0x0c, 0xe5, 0x30, 0x59, 0x96, 0xb6, 0x9d, 0x39, 0x0e, 0xc5, 0x5a, 0x96, 0x00, 0xd9, 0xc5, 0x12, 0x9c, - 0x2b, 0x8a, 0x5c, 0x86, 0x95, 0x35, 0xb7, 0x02, 0xe3, 0xc0, 0x14, 0x7e, 0xf2, 0x8f, 0x12, 0xed, 0xef, 0x64, 0x58, - 0xb2, 0x8b, 0x3f, 0xa7, 0x2b, 0xf4, 0xda, 0xb9, 0x17, 0xcc, 0x60, 0x32, 0x44, 0xef, 0xb1, 0x84, 0x79, 0xb9, 0x13, - 0xaf, 0x4a, 0x96, 0xa5, 0x71, 0xe0, 0xa0, 0x59, 0x37, 0x6b, 0x75, 0xdf, 0x22, 0x48, 0xcb, 0x86, 0xab, 0x46, 0xac, - 0xb4, 0x12, 0x2a, 0xa1, 0x29, 0xe8, 0x88, 0x92, 0xbc, 0x44, 0x98, 0x19, 0x80, 0xb2, 0x93, 0x88, 0xca, 0x08, 0x82, - 0x63, 0x58, 0xb1, 0x98, 0x69, 0x5c, 0xd9, 0x80, 0xd5, 0xa9, 0xf1, 0x51, 0x1e, 0xba, 0x5e, 0xc0, 0x50, 0x7d, 0xed, - 0x4d, 0xc6, 0x39, 0xc6, 0xbc, 0xd6, 0x4c, 0x73, 0x72, 0xec, 0x7f, 0xdc, 0x55, 0x13, 0x24, 0x2d, 0xbe, 0x1f, 0xed, - 0x67, 0x0c, 0x4d, 0x33, 0x20, 0x96, 0x3d, 0x7c, 0xc2, 0x56, 0x07, 0x6c, 0xd2, 0x75, 0xd8, 0x48, 0x12, 0x25, 0xe8, - 0x4d, 0x9c, 0x66, 0xfb, 0x26, 0x80, 0xa1, 0xba, 0x34, 0xc8, 0x9e, 0x47, 0x46, 0xbc, 0x35, 0x96, 0x03, 0x4b, 0xbe, - 0x02, 0xba, 0xa0, 0x3c, 0xf3, 0x08, 0xce, 0xb6, 0x73, 0x20, 0x0a, 0x63, 0x2d, 0x8a, 0x4b, 0x9c, 0xf0, 0x3b, 0x92, - 0x43, 0x59, 0x32, 0x43, 0x61, 0xca, 0xe7, 0xe0, 0x5c, 0x99, 0x0f, 0x1f, 0xfe, 0x90, 0x7f, 0xfc, 0x4c, 0x57, 0x97, - 0x22, 0xf6, 0xf9, 0x71, 0x8e, 0xcf, 0xbf, 0x4d, 0x7a, 0xca, 0xed, 0x2c, 0xfc, 0x06, 0xde, 0x59, 0x42, 0xce, 0xbb, - 0x1f, 0x7e, 0xd4, 0x2d, 0x0e, 0x8a, 0x85, 0xce, 0x62, 0x8b, 0x5a, 0x70, 0xfe, 0xf9, 0x79, 0x31, 0x17, 0x55, 0x1e, - 0x13, 0x38, 0x53, 0x49, 0x59, 0xfd, 0xa6, 0x48, 0x81, 0xb4, 0x8d, 0x4a, 0xc2, 0xc6, 0xff, 0x18, 0x45, 0xf1, 0xff, - 0xa3, 0x0c, 0x85, 0x86, 0xac, 0xfd, 0xf1, 0x96, 0x45, 0x65, 0x83, 0xcd, 0xff, 0x98, 0x68, 0xad, 0x56, 0x9f, 0x09, - 0x50, 0x49, 0x5b, 0x49, 0xa5, 0x0f, 0x0a, 0x3c, 0xd3, 0xd1, 0xe4, 0x0c, 0x35, 0xc8, 0x88, 0x27, 0x8c, 0x33, 0x60, - 0x68, 0x9b, 0x75, 0xc9, 0xbb, 0x6d, 0x13, 0x7f, 0x8d, 0xc2, 0x9b, 0x32, 0xb5, 0xd1, 0x18, 0x24, 0xa7, 0x0a, 0x90, - 0xe6, 0x38, 0x5b, 0x85, 0xae, 0x68, 0xc3, 0x39, 0x37, 0x6b, 0x2d, 0x38, 0x1b, 0xc6, 0x56, 0xc3, 0x97, 0xbf, 0x20, - 0x41, 0x60, 0xd7, 0xa4, 0x0e, 0xaa, 0xb2, 0xe6, 0xc5, 0x4d, 0xf8, 0x27, 0x6c, 0x2f, 0x31, 0x98, 0xc9, 0x4b, 0x9a, - 0x77, 0xa6, 0x23, 0xa4, 0x79, 0x84, 0x9c, 0xd9, 0xfc, 0x8f, 0x62, 0x26, 0xcb, 0x43, 0x19, 0xcd, 0x7c, 0x98, 0x18, - 0xff, 0xe6, 0x49, 0x02, 0xfb, 0x99, 0xf3, 0x61, 0x14, 0x99, 0x58, 0x1e, 0xdb, 0xc6, 0x0b, 0x72, 0x1f, 0x43, 0x37, - 0x5a, 0xac, 0xb2, 0x2c, 0x63, 0x5f, 0x29, 0xb3, 0xb4, 0xb4, 0xe0, 0xf0, 0x74, 0x03, 0xa2, 0x0a, 0x9d, 0x0d, 0x21, - 0xcf, 0xa5, 0x7f, 0x59, 0xa5, 0xc2, 0xf4, 0xa1, 0xcc, 0x58, 0xeb, 0x2d, 0x10, 0x7b, 0x3d, 0x51, 0x7f, 0x72, 0x87, - 0x44, 0x9b, 0xdc, 0x68, 0xf9, 0xe0, 0x14, 0x56, 0x93, 0x74, 0x1e, 0x99, 0x88, 0x47, 0xf8, 0xce, 0xb6, 0x9f, 0xb7, - 0x4a, 0x7a, 0x7e, 0xf0, 0x11, 0x76, 0xbb, 0x34, 0xf6, 0x5e, 0xf2, 0x3b, 0xf9, 0x39, 0xfa, 0x30, 0xb8, 0x23, 0x27, - 0x25, 0xb5, 0xfd, 0xa1, 0x8f, 0x71, 0x1d, 0x28, 0xbb, 0xff, 0x41, 0xe3, 0x39, 0x64, 0x51, 0xf1, 0x68, 0x92, 0xce, - 0x30, 0x07, 0x4b, 0xfd, 0x30, 0x73, 0xe1, 0x6f, 0xd2, 0x04, 0x67, 0xd1, 0x8d, 0x5e, 0x1e, 0x4c, 0xeb, 0xc9, 0x3f, - 0x22, 0x2b, 0x7f, 0x9a, 0x65, 0x93, 0xc3, 0x69, 0xb8, 0xe0, 0x47, 0x32, 0xfa, 0xed, 0xac, 0x6e, 0x4f, 0x96, 0xad, - 0x5b, 0xed, 0x21, 0x60, 0xfa, 0x91, 0x86, 0x48, 0xde, 0x2c, 0x53, 0x85, 0x81, 0xa8, 0x18, 0x5c, 0xd0, 0x1a, 0x74, - 0x29, 0x35, 0xb5, 0x55, 0xe0, 0x8c, 0x4e, 0x04, 0x1d, 0x54, 0x70, 0xb4, 0x5c, 0xf9, 0xea, 0x07, 0x0d, 0x8b, 0x93, - 0x8a, 0xed, 0xb6, 0x28, 0x92, 0x3d, 0x83, 0xe3, 0x68, 0x11, 0x15, 0x99, 0xf1, 0xef, 0x32, 0x5a, 0x29, 0xa5, 0x54, - 0x82, 0xe0, 0x8e, 0xbe, 0xd0, 0xc5, 0x65, 0x94, 0x86, 0xfc, 0x90, 0x4a, 0xb6, 0xd0, 0x80, 0x4a, 0x29, 0x0e, 0x54, - 0x8d, 0xcb, 0x44, 0xb8, 0x32, 0x36, 0x17, 0x8d, 0x2b, 0x5a, 0x15, 0xaf, 0x62, 0x87, 0xf4, 0xfa, 0x3a, 0x51, 0x85, - 0x21, 0xcb, 0xc0, 0xe1, 0xe1, 0x1c, 0x65, 0xcd, 0x93, 0x6d, 0x28, 0xc9, 0x33, 0x15, 0x73, 0x30, 0xe3, 0x5c, 0x3d, - 0xa9, 0xc6, 0x06, 0x34, 0x54, 0x54, 0x67, 0x31, 0x9d, 0xad, 0x0e, 0xa8, 0x53, 0x02, 0x02, 0xb3, 0xf0, 0x18, 0x8f, - 0xa3, 0x10, 0x77, 0xa5, 0x0c, 0xbb, 0x70, 0x9b, 0x25, 0x58, 0x8f, 0x93, 0xe1, 0x70, 0x47, 0x1b, 0x3b, 0x17, 0x75, - 0xf8, 0x51, 0xb0, 0x4b, 0x31, 0x68, 0x48, 0x23, 0x24, 0xb9, 0xd8, 0x39, 0x75, 0x2c, 0x9a, 0x70, 0x27, 0x0b, 0x02, - 0x20, 0xf2, 0x30, 0xf5, 0xc1, 0xe2, 0xf2, 0xa8, 0xb3, 0x80, 0x89, 0x79, 0xae, 0xec, 0xa2, 0xbc, 0x81, 0xaf, 0xd6, - 0xa1, 0x1c, 0x62, 0x99, 0xc4, 0x58, 0x69, 0x33, 0xfe, 0x77, 0x59, 0x1e, 0xa5, 0x37, 0x96, 0xd5, 0xb4, 0x45, 0xf5, - 0xa0, 0xd1, 0x1d, 0xae, 0x1d, 0x31, 0x36, 0x96, 0x59, 0x27, 0x86, 0x05, 0xf3, 0xdf, 0x67, 0x86, 0xc5, 0x46, 0x55, - 0xcb, 0x37, 0x39, 0xdf, 0xbd, 0xe3, 0x53, 0x08, 0x66, 0x51, 0xc3, 0x03, 0xee, 0x1e, 0x96, 0x31, 0x2c, 0x16, 0x04, - 0xb3, 0xec, 0xc1, 0xc3, 0xba, 0x1a, 0x82, 0x34, 0xe3, 0x51, 0x92, 0x40, 0x37, 0x62, 0x28, 0x46, 0x72, 0x46, 0xc0, - 0x26, 0x29, 0xc4, 0xe0, 0x15, 0xb0, 0x3f, 0xd6, 0x25, 0xa4, 0x82, 0x23, 0x3c, 0x20, 0x1c, 0xaa, 0xb8, 0xfc, 0xb0, - 0x88, 0x61, 0x20, 0x86, 0x54, 0xbc, 0x98, 0x95, 0x4f, 0x0b, 0x80, 0x91, 0x35, 0xaa, 0x78, 0x48, 0x86, 0xc8, 0xc8, - 0x9b, 0x16, 0x19, 0x75, 0xf0, 0xc6, 0xf0, 0x1b, 0x11, 0x03, 0x5e, 0xc9, 0x19, 0xe4, 0x31, 0x27, 0x2b, 0x73, 0xf9, - 0x32, 0x77, 0xe9, 0xb7, 0xe3, 0xa9, 0x1c, 0xb7, 0xcd, 0x3c, 0xb4, 0xe9, 0x65, 0xac, 0x73, 0x51, 0x71, 0x70, 0xbd, - 0xcc, 0x31, 0xa2, 0xa7, 0xf9, 0xc2, 0xb5, 0x45, 0xf6, 0xb4, 0x86, 0xeb, 0xd7, 0x2a, 0xfb, 0xf0, 0x09, 0x19, 0x5b, - 0x40, 0x86, 0x87, 0x9d, 0xba, 0x6d, 0x64, 0x0c, 0x23, 0xf0, 0xdf, 0xc6, 0xf7, 0x13, 0xe7, 0x98, 0x2e, 0x73, 0x41, - 0x72, 0x98, 0x17, 0xf8, 0xb6, 0x30, 0xfe, 0x92, 0x73, 0x1c, 0x8d, 0xc9, 0xba, 0x87, 0x1a, 0xdd, 0xbd, 0xb4, 0xe1, - 0x0b, 0x26, 0xe8, 0xfc, 0x12, 0x1d, 0x5f, 0x93, 0x06, 0xcb, 0x7d, 0xde, 0xd7, 0x33, 0x64, 0x1a, 0x0f, 0x63, 0x4c, - 0xc8, 0x35, 0x9e, 0x33, 0xd1, 0x8d, 0x7a, 0xcf, 0x96, 0xb1, 0x96, 0xd8, 0x2a, 0xa2, 0xcd, 0x36, 0x58, 0x39, 0xaa, - 0xfe, 0xf5, 0x5d, 0x24, 0x82, 0x11, 0x35, 0xed, 0xd3, 0x5a, 0xdf, 0xa8, 0x3c, 0xf3, 0xdb, 0x99, 0x77, 0x1b, 0x56, - 0x86, 0x19, 0x04, 0x33, 0xbe, 0x62, 0xce, 0xd0, 0xcf, 0x23, 0x73, 0x0f, 0xbc, 0xdd, 0x4b, 0xef, 0xc6, 0x9a, 0x35, - 0xfa, 0x61, 0xba, 0x53, 0x92, 0x59, 0x60, 0x3b, 0xfe, 0x4d, 0xd0, 0x53, 0x21, 0xf5, 0xa3, 0x3a, 0xb0, 0xf8, 0x9a, - 0x93, 0x98, 0x90, 0x0c, 0x39, 0x58, 0x90, 0xab, 0xe6, 0xbd, 0xa7, 0xdb, 0xb6, 0x8c, 0x0a, 0x71, 0xe9, 0x74, 0xf5, - 0xe5, 0xf5, 0xda, 0x0b, 0xb4, 0xa3, 0xfa, 0xd1, 0xc6, 0xcb, 0x78, 0xf1, 0x78, 0x03, 0x77, 0x22, 0x7e, 0x43, 0x6e, - 0x68, 0x8c, 0xaf, 0xc2, 0xa3, 0xb5, 0xda, 0x2b, 0xae, 0xbd, 0x69, 0xee, 0xf1, 0x8b, 0xb9, 0x56, 0x67, 0x4e, 0xb5, - 0x57, 0x66, 0x5c, 0x99, 0xb8, 0x58, 0x51, 0x92, 0x0f, 0x5f, 0x10, 0x5c, 0xc7, 0xcf, 0xd6, 0x41, 0xb8, 0xeb, 0xf1, - 0x9d, 0x1c, 0x2c, 0xc5, 0xc0, 0x74, 0x03, 0xd7, 0x81, 0x18, 0xc3, 0xd8, 0x22, 0x01, 0x92, 0xfa, 0xa1, 0x6c, 0xc5, - 0x28, 0x18, 0xbf, 0x3e, 0x5e, 0xb6, 0xea, 0x1d, 0xff, 0x61, 0x09, 0xe0, 0xd8, 0x46, 0x38, 0x02, 0xcd, 0xac, 0x38, - 0xe5, 0x52, 0x5c, 0xe8, 0x23, 0x38, 0xb3, 0x29, 0xfb, 0x80, 0xe3, 0x90, 0x4d, 0x30, 0xed, 0x8f, 0x86, 0xca, 0xef, - 0x9f, 0xc8, 0x8f, 0x6b, 0x77, 0xbf, 0x57, 0xd7, 0x16, 0x9e, 0xfe, 0x73, 0x87, 0xde, 0x49, 0x47, 0x5e, 0x3b, 0x1d, - 0x75, 0xb8, 0x02, 0xd9, 0x71, 0x53, 0x7b, 0x56, 0x57, 0x5f, 0xc9, 0xf1, 0x63, 0x7f, 0x5b, 0x95, 0x6e, 0xe3, 0xfd, - 0xf3, 0xb2, 0xaa, 0xec, 0x6c, 0xaf, 0x5c, 0x17, 0x7f, 0x59, 0x69, 0xf2, 0xda, 0x7f, 0xd1, 0x6f, 0xe7, 0xa4, 0x1f, - 0xe6, 0x9b, 0x45, 0x8b, 0xbc, 0x61, 0x9b, 0x01, 0xfe, 0xf1, 0xa0, 0xf1, 0x50, 0x44, 0xd8, 0xdc, 0x75, 0xbf, 0xb5, - 0xa1, 0x41, 0x31, 0x27, 0xef, 0x04, 0x29, 0x0a, 0x20, 0x71, 0xc7, 0x5a, 0x01, 0x38, 0x06, 0x86, 0xc1, 0x73, 0xef, - 0x53, 0xeb, 0xc6, 0x14, 0x75, 0xb9, 0x7a, 0xa2, 0xb1, 0x9b, 0xad, 0x37, 0xf4, 0x35, 0x6e, 0xf4, 0x1f, 0x91, 0x0b, - 0x11, 0x18, 0x3c, 0x3f, 0x80, 0xfb, 0xc7, 0x29, 0x7b, 0xd1, 0x62, 0x52, 0x79, 0xc3, 0xe7, 0xf6, 0xf5, 0xe1, 0xb5, - 0x7c, 0x9a, 0xcd, 0x05, 0x12, 0xbd, 0x3e, 0x36, 0xb5, 0xd0, 0x14, 0xb9, 0x96, 0x3b, 0xd9, 0xc5, 0xd1, 0x34, 0xc4, - 0x68, 0x01, 0x50, 0x28, 0x03, 0xc6, 0x4f, 0xb0, 0x86, 0x3a, 0xe3, 0x9f, 0xcf, 0xa7, 0x3c, 0xa7, 0xfb, 0xcd, 0x5b, - 0x33, 0xbd, 0xa5, 0x39, 0xe0, 0xdb, 0x90, 0xff, 0xdb, 0x3f, 0xd1, 0xad, 0x63, 0xac, 0xf6, 0x98, 0x1d, 0x5c, 0x9b, - 0x6b, 0x59, 0xf4, 0x6f, 0x6b, 0xe2, 0xca, 0xeb, 0xd1, 0x0f, 0xf8, 0x75, 0xee, 0x0b, 0x81, 0xd1, 0x14, 0x9e, 0xb1, - 0x98, 0xb4, 0x55, 0xae, 0xef, 0x7a, 0xc2, 0x6c, 0x1b, 0x9d, 0x22, 0x35, 0x04, 0xd7, 0xfb, 0x18, 0x57, 0x1b, 0x4f, - 0xa2, 0xb2, 0xda, 0xbe, 0x79, 0x2a, 0xc0, 0x85, 0xc6, 0xf2, 0x4f, 0xd4, 0x79, 0xbb, 0x47, 0x6d, 0x72, 0xda, 0x3f, - 0x69, 0xed, 0x9e, 0x4b, 0x0f, 0x1d, 0xe9, 0xb1, 0xe9, 0x53, 0x6b, 0xde, 0x10, 0xec, 0x5b, 0xd2, 0x62, 0x2f, 0x00, - 0xdc, 0x01, 0x9e, 0xa8, 0x36, 0xd1, 0xb3, 0xaa, 0x7f, 0xec, 0x01, 0x69, 0x8c, 0xef, 0x31, 0x49, 0x95, 0x1b, 0xb9, - 0x50, 0xb3, 0x48, 0x50, 0x74, 0x1c, 0x1f, 0xdf, 0x31, 0xad, 0xd6, 0xc3, 0xf3, 0x62, 0x55, 0x0a, 0x63, 0xcb, 0xdc, - 0x9b, 0x79, 0x90, 0xd3, 0x54, 0x1f, 0xbc, 0x16, 0xee, 0x1b, 0xba, 0x14, 0x3e, 0x16, 0x8f, 0x5a, 0xed, 0x80, 0x9c, - 0x6c, 0x41, 0x08, 0x47, 0x74, 0xfe, 0x52, 0x32, 0x53, 0x80, 0xd7, 0x81, 0xbb, 0xe2, 0x18, 0x3d, 0xb6, 0xe3, 0x6e, - 0x54, 0xdc, 0xc2, 0x9f, 0x1d, 0x44, 0x11, 0xd6, 0x55, 0xbb, 0x35, 0x61, 0xce, 0xcb, 0x14, 0x46, 0xa9, 0x90, 0x80, - 0x70, 0xb8, 0xcc, 0x0d, 0x50, 0x42, 0x49, 0x40, 0x5b, 0x15, 0xd5, 0x1f, 0xca, 0xdc, 0x76, 0xbb, 0x51, 0x73, 0x1e, - 0x89, 0x87, 0x81, 0x8a, 0xf5, 0x98, 0xd6, 0x5a, 0x92, 0x03, 0x0a, 0x51, 0xb3, 0xc9, 0xf3, 0xf2, 0x8f, 0xf5, 0x48, - 0x2e, 0x05, 0x8f, 0x44, 0x2c, 0xde, 0x96, 0xe4, 0x9b, 0xfc, 0xf1, 0x0c, 0x99, 0xbd, 0xe5, 0xe4, 0x87, 0x39, 0x4c, - 0x27, 0x76, 0x19, 0xf0, 0x04, 0x05, 0xac, 0x51, 0x8f, 0xb6, 0xa2, 0xa7, 0x80, 0x74, 0x98, 0x15, 0x0c, 0x08, 0x4e, - 0xa9, 0x5f, 0xe6, 0x47, 0xbe, 0xd9, 0x96, 0x42, 0x55, 0x89, 0x68, 0x29, 0x0b, 0xbe, 0xdc, 0x9e, 0x6f, 0x26, 0x94, - 0xac, 0xb8, 0xa6, 0xb6, 0x99, 0xad, 0xa2, 0x45, 0x2b, 0x08, 0x7f, 0x5c, 0xcd, 0x8c, 0xa8, 0xbf, 0x90, 0x6e, 0xd6, - 0xb4, 0x7d, 0x80, 0xb4, 0x9a, 0x53, 0x3b, 0x3b, 0x47, 0x73, 0x41, 0x03, 0xf5, 0x18, 0xc1, 0xc6, 0xe2, 0x52, 0x93, - 0x72, 0xd6, 0x39, 0xaf, 0xc6, 0x1b, 0x86, 0xdb, 0x4d, 0x52, 0x2f, 0x8a, 0x1b, 0x57, 0x37, 0x3a, 0xe9, 0x4b, 0xd0, - 0xc1, 0xa0, 0x03, 0x86, 0x94, 0x5a, 0x85, 0x8a, 0xec, 0xce, 0x62, 0x5d, 0x38, 0x4d, 0x48, 0x3a, 0x5d, 0xf1, 0x72, - 0x52, 0xbc, 0x67, 0x84, 0x38, 0xfa, 0x01, 0x29, 0x93, 0x47, 0xa8, 0x49, 0x5e, 0xfb, 0x80, 0x21, 0xf3, 0x67, 0xd4, - 0xe2, 0xb0, 0xa1, 0x0d, 0xa2, 0x7f, 0x10, 0x38, 0x1e, 0x47, 0x90, 0x0a, 0xd6, 0x53, 0x32, 0xba, 0x04, 0x48, 0x7a, - 0x09, 0x9f, 0x1e, 0xb1, 0x60, 0x6a, 0xee, 0x94, 0x82, 0xe2, 0xc9, 0x00, 0x43, 0x5b, 0x69, 0x54, 0x96, 0x54, 0x4e, - 0xf4, 0x40, 0x03, 0xef, 0x29, 0x14, 0x10, 0x46, 0x9c, 0x3d, 0xf6, 0x39, 0x2f, 0x62, 0x50, 0xec, 0xad, 0x41, 0xe8, - 0x3e, 0x03, 0xd8, 0xc8, 0x33, 0x0c, 0x16, 0x79, 0x5e, 0x21, 0x47, 0x65, 0x2f, 0xab, 0xb9, 0xff, 0x72, 0x46, 0xd9, - 0xc0, 0xe0, 0x51, 0x3d, 0xe9, 0xe4, 0x5a, 0xbf, 0x0e, 0x27, 0xc8, 0x59, 0xfa, 0x94, 0xd5, 0xa3, 0x76, 0x6e, 0xca, - 0x98, 0xac, 0x2b, 0xf5, 0x67, 0xee, 0x61, 0x24, 0xdf, 0xca, 0x99, 0x51, 0x16, 0xa9, 0x88, 0x17, 0x7e, 0x00, 0xa5, - 0x9f, 0x67, 0x1d, 0x83, 0xc2, 0x13, 0x0b, 0x0d, 0x81, 0x38, 0xc4, 0x35, 0x36, 0xb8, 0x71, 0x20, 0x18, 0x35, 0x68, - 0x4c, 0x6e, 0x51, 0xad, 0x29, 0x72, 0x2d, 0xd4, 0xa7, 0x06, 0x43, 0x6d, 0x9c, 0x79, 0x66, 0x25, 0x98, 0xd0, 0xf0, - 0x92, 0x4f, 0x95, 0xac, 0xa3, 0xb8, 0xc2, 0x2f, 0x57, 0x80, 0xd9, 0xc0, 0x34, 0x77, 0x1d, 0x60, 0xb0, 0xd2, 0x9c, - 0x9a, 0x91, 0x67, 0xe7, 0x0e, 0xa1, 0xd4, 0x8d, 0x5e, 0xc0, 0x04, 0x30, 0x1c, 0x02, 0xda, 0xa0, 0x97, 0x17, 0x3e, - 0x5c, 0x90, 0xaa, 0x1d, 0x19, 0x70, 0xb4, 0xc8, 0x89, 0xb2, 0x75, 0x88, 0xff, 0x99, 0x48, 0x48, 0xda, 0xec, 0x40, - 0xbc, 0x39, 0x76, 0x53, 0xc7, 0xaa, 0xe7, 0x20, 0xbf, 0xba, 0xc1, 0x5e, 0x2b, 0xae, 0x4c, 0x93, 0x1a, 0x7a, 0x35, - 0x1a, 0x87, 0x82, 0xb4, 0xbc, 0x98, 0xdd, 0x78, 0xd2, 0x24, 0xba, 0x2c, 0xdd, 0x34, 0xe8, 0x21, 0xbc, 0x33, 0x0f, - 0xf9, 0x1d, 0xef, 0xeb, 0xc9, 0xfe, 0x81, 0xa2, 0x43, 0x60, 0xb7, 0x21, 0xd7, 0x15, 0x5f, 0x3f, 0x21, 0xc5, 0x32, - 0xda, 0xa7, 0x5d, 0x5b, 0xf3, 0xd4, 0xe2, 0x04, 0x86, 0xbd, 0x9c, 0x8d, 0x0b, 0x6e, 0x55, 0x84, 0x61, 0x6a, 0x28, - 0x25, 0x97, 0x9d, 0x6e, 0x49, 0x4e, 0xae, 0x05, 0x1a, 0x83, 0x40, 0x71, 0x1e, 0xf7, 0x9f, 0xd3, 0x97, 0x12, 0x6c, - 0xc7, 0x0e, 0x46, 0x27, 0xe9, 0x3d, 0xad, 0x93, 0xa3, 0xa2, 0xb0, 0xdd, 0x29, 0xdd, 0x38, 0xf0, 0xc7, 0x89, 0x2a, - 0xb5, 0x25, 0x06, 0x9e, 0xef, 0xae, 0x4d, 0x42, 0x5b, 0x73, 0x0e, 0xb0, 0x12, 0x00, 0x28, 0x2f, 0x06, 0x55, 0xbb, - 0x78, 0xe0, 0xa6, 0xa5, 0x6d, 0x70, 0xd3, 0x40, 0x8d, 0x44, 0x04, 0x51, 0x40, 0xc2, 0xd4, 0x3f, 0x37, 0x25, 0x3b, - 0xf1, 0x8e, 0x79, 0x27, 0x0a, 0x15, 0x92, 0x06, 0xda, 0x79, 0xf5, 0xf0, 0xe8, 0x63, 0x42, 0x58, 0x63, 0x9c, 0x18, - 0xdb, 0x80, 0x7d, 0xd7, 0x5a, 0xd1, 0x5c, 0x17, 0xf4, 0xb6, 0xee, 0x14, 0xd5, 0x1c, 0xa0, 0x93, 0x70, 0x3b, 0x0f, - 0x3e, 0x42, 0x46, 0x95, 0xbc, 0xdf, 0xe5, 0xc8, 0xe3, 0x12, 0x04, 0x39, 0xdf, 0x36, 0x54, 0x1e, 0x83, 0x07, 0x51, - 0xe0, 0x83, 0x2a, 0x06, 0x53, 0xe5, 0xc4, 0x4d, 0x20, 0xd5, 0x08, 0x32, 0xaf, 0x22, 0xc4, 0x2b, 0x3a, 0xf9, 0x7d, - 0x8f, 0x40, 0xac, 0x12, 0x9e, 0x24, 0xf3, 0x2a, 0xf9, 0x34, 0x23, 0xae, 0x6a, 0x83, 0x61, 0x66, 0xd8, 0x6e, 0xc9, - 0x69, 0x8c, 0x88, 0xa7, 0xe3, 0x66, 0x6f, 0xe2, 0xb9, 0x11, 0xd8, 0xc2, 0x51, 0xc4, 0xf2, 0x35, 0xd1, 0x19, 0x98, - 0x21, 0x55, 0x85, 0xd8, 0x5c, 0xf2, 0x19, 0x91, 0x8d, 0xc2, 0xf5, 0xb5, 0xf8, 0xbb, 0x4a, 0x29, 0x11, 0x19, 0xea, - 0x6b, 0xf5, 0x4f, 0xd1, 0x08, 0xdb, 0xa9, 0x62, 0xf4, 0xfa, 0xf1, 0x81, 0x7a, 0x04, 0x3a, 0x53, 0xaa, 0x8d, 0x52, - 0x2d, 0x98, 0xd7, 0x5a, 0xa1, 0x61, 0xa1, 0x75, 0xd4, 0xa7, 0x26, 0xc3, 0xe2, 0xc7, 0xab, 0xdc, 0x2e, 0x06, 0x80, - 0x4e, 0x02, 0x94, 0xec, 0x0f, 0x2d, 0xf5, 0xcd, 0x2a, 0x4b, 0x12, 0x80, 0xcc, 0x01, 0xd8, 0xe3, 0x38, 0xa9, 0x59, - 0x91, 0x1d, 0xfd, 0x42, 0x54, 0x4e, 0xd8, 0xe1, 0x8b, 0xa5, 0x69, 0xfe, 0x00, 0x57, 0x09, 0xcc, 0x08, 0x31, 0x19, - 0xd7, 0x51, 0x67, 0x83, 0xd0, 0x02, 0xa0, 0x5b, 0xda, 0xa9, 0x87, 0xe4, 0xed, 0x7a, 0x0c, 0x52, 0xf6, 0x01, 0xea, - 0xbc, 0xab, 0xe1, 0xfa, 0x08, 0xc3, 0x9c, 0x1d, 0x24, 0xa0, 0x9d, 0xaa, 0xd0, 0x9f, 0x9a, 0xa6, 0x72, 0x44, 0xaf, - 0xa7, 0x4d, 0x07, 0x95, 0xbb, 0x89, 0x2a, 0x13, 0xe0, 0xe0, 0x4d, 0x3c, 0x49, 0xe2, 0xb0, 0xdb, 0x4b, 0x4d, 0x53, - 0x3f, 0x99, 0xb8, 0xb2, 0x5a, 0x4d, 0xf9, 0x76, 0x6e, 0x95, 0xd0, 0xd2, 0xe3, 0x42, 0x88, 0x79, 0xbc, 0xe7, 0x81, - 0xeb, 0x65, 0xdf, 0xc8, 0x1a, 0x2c, 0xee, 0x9b, 0x95, 0x51, 0x95, 0xd3, 0x11, 0x1a, 0x93, 0x62, 0x9e, 0xfc, 0x05, - 0x88, 0xd1, 0xdd, 0x4e, 0xd3, 0xeb, 0x10, 0x42, 0x44, 0x37, 0xfb, 0xf6, 0x9e, 0x1a, 0xeb, 0x88, 0x3d, 0x21, 0x2c, - 0x73, 0xc3, 0x4b, 0x74, 0x0c, 0xdc, 0xf6, 0xae, 0x2c, 0xc9, 0x74, 0xf9, 0xdc, 0x17, 0x20, 0x7c, 0x1d, 0x30, 0x43, - 0x0a, 0x54, 0x4a, 0xec, 0x83, 0xcd, 0xf7, 0x91, 0xd0, 0x3c, 0x3d, 0x17, 0xb6, 0x11, 0x7a, 0xbe, 0xec, 0xb3, 0xf5, - 0x5b, 0x38, 0x62, 0x6b, 0xab, 0x60, 0x0f, 0x7b, 0xb9, 0x6e, 0x91, 0xd1, 0x3c, 0xf8, 0x85, 0xe9, 0x2c, 0x0b, 0x89, - 0x57, 0x1b, 0xf5, 0x0d, 0xeb, 0x1d, 0x5b, 0xfa, 0x4c, 0x66, 0x4d, 0x3c, 0x4c, 0xd6, 0xd3, 0xc8, 0xc3, 0xc9, 0xa9, - 0x3c, 0xc7, 0xe6, 0xa9, 0xb0, 0xc0, 0x1b, 0xba, 0x7a, 0x7a, 0xcb, 0xb8, 0xf7, 0xa6, 0x21, 0x79, 0x89, 0xcf, 0xce, - 0xa2, 0x05, 0xa0, 0x98, 0xa8, 0x9c, 0x5e, 0xbb, 0xc0, 0x09, 0xf6, 0x7a, 0x51, 0x41, 0x83, 0x63, 0xe4, 0xd8, 0x96, - 0xe0, 0xe9, 0x70, 0x26, 0x67, 0x9d, 0x0b, 0x48, 0x5f, 0x33, 0xa9, 0x39, 0x0b, 0x73, 0x4e, 0x4a, 0x11, 0xf8, 0xe8, - 0x51, 0x9c, 0xa3, 0x79, 0xba, 0x01, 0x04, 0x86, 0x8a, 0xf7, 0x5d, 0x60, 0x8f, 0x37, 0x1c, 0xa9, 0x8b, 0x1c, 0xac, - 0xe4, 0x3d, 0x31, 0xcc, 0x0a, 0xfd, 0xeb, 0xe7, 0x07, 0x2b, 0x85, 0x8a, 0x5c, 0x8e, 0x51, 0x88, 0x62, 0xf7, 0x8c, - 0x08, 0xcc, 0x4d, 0xa5, 0x3a, 0x08, 0xd4, 0xf2, 0x0f, 0xb6, 0x5f, 0x08, 0x57, 0x4a, 0x70, 0xeb, 0x41, 0x5d, 0x5a, - 0x42, 0xc6, 0x1e, 0xce, 0xea, 0x2d, 0xd2, 0x58, 0x40, 0xb0, 0xc7, 0x5c, 0x6b, 0x7a, 0x98, 0x03, 0xc9, 0xac, 0x06, - 0x18, 0x6d, 0x89, 0x20, 0xf5, 0x82, 0xc1, 0x2e, 0x15, 0xdd, 0xd7, 0x05, 0x45, 0xba, 0xcb, 0xa8, 0x31, 0x95, 0x56, - 0x72, 0x7c, 0x1e, 0x62, 0x7f, 0xad, 0xa9, 0x5a, 0xea, 0xab, 0xec, 0x6b, 0x72, 0xba, 0xbb, 0x5f, 0x6c, 0xfc, 0x48, - 0xf8, 0x79, 0xae, 0x98, 0x41, 0x95, 0x8c, 0xa3, 0x5d, 0xc2, 0xa4, 0xa1, 0x7a, 0xa5, 0x38, 0x6e, 0x2c, 0x37, 0x9e, - 0x6e, 0x5f, 0x74, 0xc6, 0x56, 0xe9, 0xbf, 0xbb, 0x05, 0x3e, 0x27, 0xdd, 0x6b, 0x32, 0x4f, 0x49, 0x6c, 0xf0, 0x43, - 0xf7, 0x20, 0x9d, 0x28, 0xcf, 0xfd, 0xcb, 0xf3, 0xe3, 0x39, 0x29, 0x62, 0xdb, 0x56, 0xe4, 0x95, 0x15, 0xa0, 0x1c, - 0xd2, 0x6e, 0x02, 0xea, 0x4b, 0x37, 0xea, 0x4d, 0xe4, 0x8d, 0x0d, 0xbc, 0x84, 0xd4, 0x1a, 0x28, 0x76, 0x61, 0xec, - 0xab, 0xd3, 0x51, 0x48, 0x93, 0x33, 0xd9, 0x43, 0x42, 0x31, 0x61, 0x80, 0xfe, 0x69, 0x71, 0x34, 0xa3, 0x82, 0xd6, - 0xfb, 0xbd, 0xa8, 0x8e, 0x65, 0xe7, 0x1a, 0x08, 0x99, 0xd9, 0x68, 0x96, 0xbf, 0xc8, 0xf0, 0xc6, 0x21, 0xf2, 0x55, - 0x66, 0x3a, 0x3a, 0xf0, 0xd7, 0x94, 0x3b, 0xa9, 0xc3, 0xc6, 0x55, 0x76, 0x24, 0x81, 0xff, 0x2e, 0x73, 0x22, 0x14, - 0xbe, 0x99, 0x2d, 0x0f, 0xe4, 0x6b, 0x5d, 0xf9, 0x5f, 0x33, 0xea, 0xb3, 0xc2, 0x1d, 0x6d, 0xcb, 0xd5, 0x8c, 0xc3, - 0xd9, 0x70, 0x20, 0xf3, 0xf1, 0x81, 0x0b, 0x5e, 0x79, 0xaa, 0xca, 0x7e, 0x13, 0x0e, 0xc9, 0x03, 0x7b, 0x36, 0x39, - 0x4a, 0x4b, 0x47, 0xed, 0x7f, 0xe5, 0xb4, 0xe8, 0x50, 0x34, 0x2c, 0x5a, 0x17, 0x05, 0xa2, 0x56, 0x1b, 0xcb, 0xcb, - 0x3c, 0x22, 0x41, 0xed, 0x8b, 0xc5, 0x43, 0x7b, 0xe0, 0xa3, 0x29, 0x46, 0xbe, 0xcf, 0x58, 0x07, 0x12, 0x7d, 0x7f, - 0x44, 0x90, 0x32, 0x50, 0x3a, 0x74, 0x06, 0xa5, 0x89, 0x29, 0x1e, 0x93, 0x3c, 0x67, 0xb1, 0xc2, 0x5e, 0xf2, 0x3a, - 0x2a, 0x07, 0x2b, 0x92, 0x7f, 0x8e, 0x08, 0x70, 0x14, 0x0c, 0x1e, 0x45, 0x9e, 0xfa, 0x25, 0xb8, 0xe5, 0xbe, 0x3f, - 0x60, 0x44, 0x56, 0xd2, 0x58, 0x5b, 0x8c, 0x5e, 0x88, 0x91, 0xf9, 0x08, 0x8e, 0xc7, 0xef, 0x9b, 0xa3, 0x14, 0x94, - 0xbe, 0xb4, 0x2b, 0x50, 0xdc, 0x04, 0xba, 0xb4, 0x9b, 0x1a, 0xa7, 0x81, 0x9c, 0xc8, 0xb4, 0xb5, 0x1d, 0xf7, 0xdd, - 0xd9, 0xb1, 0xa0, 0x2d, 0x41, 0xc6, 0x74, 0x17, 0x9a, 0x39, 0x0a, 0x0c, 0xef, 0xb7, 0x1a, 0x47, 0xc0, 0x80, 0x5d, - 0x63, 0x3d, 0xfc, 0x52, 0x4c, 0xfb, 0x54, 0xe9, 0x87, 0x2b, 0x9c, 0xb3, 0x4b, 0x3a, 0xbd, 0xf9, 0xfd, 0x40, 0x06, - 0xc4, 0xc5, 0x1b, 0x31, 0xf5, 0x05, 0xcc, 0x2f, 0x83, 0x02, 0x10, 0xa6, 0x12, 0x58, 0xfa, 0xbf, 0x98, 0x0b, 0xbc, - 0x13, 0x87, 0x35, 0x83, 0x03, 0x83, 0x88, 0x8f, 0x3b, 0xb8, 0xc5, 0x5f, 0x87, 0xff, 0x68, 0x80, 0xba, 0x72, 0xf7, - 0x19, 0x65, 0xcd, 0xf7, 0x49, 0x29, 0x32, 0x7d, 0xf9, 0xee, 0x65, 0x2b, 0xd4, 0x41, 0x8e, 0x6d, 0x6e, 0x55, 0xf3, - 0xda, 0xe2, 0xf7, 0xd3, 0x58, 0xcd, 0x4d, 0x7e, 0xd3, 0xdb, 0x55, 0x57, 0x4f, 0x8d, 0x1a, 0xf5, 0x84, 0x60, 0xf4, - 0xe6, 0x66, 0xd8, 0xad, 0xf1, 0xcb, 0x59, 0x09, 0x68, 0x64, 0xb3, 0x57, 0xbf, 0x47, 0x41, 0xae, 0xaf, 0xf5, 0xf3, - 0xbc, 0xac, 0x32, 0x2e, 0xbe, 0x09, 0xc0, 0x53, 0xe3, 0x43, 0xa2, 0x4a, 0xb5, 0x2c, 0x0d, 0x51, 0x93, 0x00, 0x82, - 0xc3, 0x1f, 0x74, 0x0b, 0x2e, 0xed, 0x57, 0x72, 0x9b, 0x55, 0x79, 0x6d, 0x45, 0xd0, 0x81, 0x45, 0x9f, 0xae, 0x0c, - 0x76, 0x30, 0xe0, 0xe1, 0x14, 0xfd, 0x43, 0xf1, 0x87, 0x89, 0xed, 0x9d, 0x6d, 0x4a, 0x28, 0x1f, 0x9a, 0xb9, 0x17, - 0x77, 0xf6, 0x4c, 0x11, 0xcd, 0x22, 0xd4, 0xac, 0x82, 0x19, 0x2c, 0x1b, 0x6a, 0xe7, 0x1a, 0x12, 0xf6, 0x08, 0x52, - 0x4c, 0xc1, 0xb8, 0xd1, 0xde, 0x90, 0xee, 0x88, 0x6b, 0x06, 0xe5, 0xb0, 0x50, 0x94, 0xf9, 0xcd, 0x08, 0xc9, 0x38, - 0xa7, 0xe9, 0x0d, 0x0a, 0xfc, 0x30, 0xe0, 0x73, 0x79, 0xb0, 0x20, 0xcf, 0x1f, 0x55, 0xe9, 0x74, 0x14, 0xfb, 0x56, - 0x12, 0x31, 0x63, 0xda, 0x81, 0x2d, 0xaf, 0xf7, 0xca, 0x74, 0x61, 0xb3, 0x4f, 0x3a, 0xd6, 0x1d, 0xe2, 0xed, 0x29, - 0x71, 0x1d, 0xc4, 0x9e, 0xa6, 0x1c, 0x36, 0x79, 0x3d, 0x99, 0xe3, 0x68, 0x51, 0x76, 0x5d, 0xac, 0xa6, 0x33, 0x14, - 0x7a, 0x0b, 0xc9, 0x46, 0x1b, 0x7a, 0xfe, 0xa4, 0x72, 0x8c, 0xf3, 0xc3, 0xe5, 0x24, 0x86, 0xe9, 0x4b, 0xa9, 0x21, - 0x5a, 0xb7, 0x94, 0xee, 0xb1, 0xbe, 0x63, 0x05, 0x5b, 0xb3, 0xf7, 0x8f, 0x44, 0x96, 0x26, 0x96, 0xa9, 0xd4, 0x96, - 0x9d, 0xba, 0x71, 0xef, 0x59, 0x7b, 0xfc, 0xde, 0x62, 0xb1, 0x46, 0xea, 0x6c, 0xb4, 0x31, 0xcd, 0x40, 0x3e, 0x1a, - 0xda, 0x07, 0x5f, 0x30, 0x65, 0x0b, 0xfd, 0x70, 0xde, 0x6d, 0xd0, 0x16, 0xe3, 0x33, 0x86, 0xa6, 0xd9, 0x9d, 0x0f, - 0xbc, 0xfa, 0x2c, 0x8b, 0x2e, 0x17, 0x1d, 0x4f, 0x73, 0x8c, 0x18, 0x75, 0xff, 0x5f, 0x1e, 0xf6, 0x52, 0x86, 0xbb, - 0x3c, 0x21, 0xc3, 0x4e, 0xee, 0xdb, 0x29, 0xab, 0x80, 0x7c, 0x8c, 0xad, 0xf4, 0xbc, 0x72, 0x30, 0xa2, 0xd2, 0x51, - 0x9c, 0xe9, 0x3f, 0x7c, 0xe5, 0xd7, 0x32, 0x8d, 0xda, 0xf4, 0xa3, 0xcb, 0x92, 0xbf, 0xb2, 0x1a, 0x88, 0x36, 0x4f, - 0x88, 0x4c, 0xfe, 0x4f, 0x24, 0x25, 0x47, 0x06, 0xe2, 0xd1, 0x01, 0x14, 0x30, 0x53, 0x27, 0x93, 0xd3, 0x62, 0x70, - 0x02, 0x22, 0x4b, 0x34, 0x87, 0x73, 0x00, 0x93, 0xb4, 0x04, 0x13, 0x1e, 0xd7, 0x6a, 0xdf, 0x63, 0xc6, 0x01, 0x7f, - 0x99, 0x47, 0x73, 0x70, 0xf7, 0x01, 0x2d, 0x9a, 0x80, 0x64, 0x24, 0x61, 0x58, 0x6b, 0xdb, 0x79, 0x38, 0xd9, 0x4e, - 0xf0, 0xac, 0x7a, 0x7d, 0xc0, 0x8f, 0xb5, 0x82, 0xcb, 0x9d, 0x28, 0x45, 0x75, 0x1f, 0x7c, 0xd9, 0xea, 0xcd, 0x21, - 0xd4, 0x59, 0x0f, 0xf5, 0xcc, 0x40, 0x71, 0xdb, 0xce, 0x66, 0x54, 0xf3, 0x05, 0xff, 0xf8, 0xcb, 0xc2, 0x50, 0x2c, - 0x9a, 0x35, 0x64, 0xc0, 0x00, 0xdc, 0xc6, 0x9c, 0xef, 0x75, 0xfc, 0x97, 0x4f, 0x90, 0xb0, 0x17, 0x11, 0xf6, 0x26, - 0xc5, 0x28, 0xe1, 0x97, 0x13, 0x06, 0x04, 0xf1, 0xda, 0x13, 0x25, 0x88, 0xf4, 0xa0, 0x3e, 0x99, 0x66, 0x5c, 0x66, - 0x33, 0x48, 0xd6, 0xb0, 0x08, 0xba, 0xdd, 0x35, 0xeb, 0x32, 0xe3, 0x4f, 0x7e, 0xc8, 0x70, 0x0d, 0xf4, 0x4f, 0x26, - 0x4a, 0x3a, 0x37, 0x24, 0xa8, 0xf8, 0x20, 0x5e, 0xe6, 0x50, 0x79, 0xde, 0x33, 0xe4, 0xe9, 0xf9, 0x47, 0x7f, 0xdf, - 0xcc, 0x1c, 0xca, 0x53, 0xd6, 0xe4, 0xef, 0x9e, 0xea, 0xfe, 0xa7, 0xc8, 0x2b, 0x3a, 0xf3, 0xd5, 0xac, 0xb3, 0xe2, - 0x3a, 0xe3, 0xec, 0x88, 0x54, 0x70, 0x6a, 0x45, 0xeb, 0x1d, 0x0f, 0xb1, 0x69, 0xfc, 0xb5, 0x40, 0xea, 0xec, 0x91, - 0xb9, 0x67, 0x07, 0x15, 0xa3, 0x25, 0x14, 0x58, 0x2f, 0xa2, 0x06, 0xbe, 0x1d, 0xb5, 0x19, 0x33, 0x7d, 0x4e, 0x0a, - 0xb4, 0x68, 0x09, 0x36, 0x6d, 0x17, 0xa3, 0x26, 0x5e, 0x96, 0xcc, 0x15, 0x27, 0xfc, 0xe9, 0x32, 0x53, 0xec, 0x87, - 0x8c, 0xd4, 0xc1, 0x9e, 0x17, 0x2b, 0x96, 0x2c, 0x97, 0x4f, 0xd7, 0x0f, 0xc9, 0x2e, 0xf7, 0x1e, 0x11, 0x33, 0x5e, - 0x3f, 0x5e, 0xb2, 0x4b, 0x09, 0x28, 0x91, 0x91, 0x0d, 0xe3, 0x36, 0x12, 0x6a, 0x14, 0x15, 0xa3, 0x2b, 0x50, 0x72, - 0xac, 0x53, 0x11, 0x00, 0xf0, 0xc7, 0xf4, 0x52, 0xd8, 0xc0, 0x83, 0xd3, 0x89, 0x02, 0x94, 0x91, 0xa7, 0xef, 0x4c, - 0xc6, 0x82, 0xe8, 0xa8, 0x99, 0xc3, 0xef, 0x84, 0xb1, 0x7a, 0xe6, 0xde, 0xeb, 0xa3, 0x48, 0xb0, 0x7b, 0xd9, 0x08, - 0x03, 0x89, 0x65, 0xd9, 0x64, 0x1c, 0xb6, 0x6e, 0x2b, 0xfc, 0xb4, 0x58, 0x81, 0x34, 0x05, 0x68, 0xde, 0xd3, 0x46, - 0xc0, 0x69, 0x18, 0xb3, 0x2f, 0x13, 0x48, 0xa9, 0x82, 0xb1, 0xfc, 0xa4, 0x64, 0xc3, 0xb3, 0x49, 0xde, 0xfd, 0xc4, - 0xd3, 0x5c, 0x20, 0xe4, 0xc5, 0x02, 0xdb, 0x9a, 0xa9, 0x13, 0xbf, 0x19, 0xe5, 0x66, 0x3f, 0x56, 0xcd, 0xa2, 0x0d, - 0x47, 0x1e, 0x95, 0xe5, 0xa6, 0x1b, 0xdd, 0xda, 0x2d, 0x58, 0xb5, 0x10, 0xa9, 0xe6, 0x78, 0x19, 0x80, 0x8d, 0xe8, - 0x97, 0x94, 0x61, 0xf5, 0x83, 0x4e, 0x81, 0x64, 0x61, 0xc8, 0xb6, 0xcd, 0x92, 0x32, 0x18, 0x82, 0xf2, 0xa8, 0x9a, - 0x02, 0xac, 0x91, 0xf2, 0x4d, 0x0a, 0xa3, 0xc9, 0xbf, 0x6a, 0x8b, 0xfe, 0x93, 0xff, 0x29, 0xd6, 0x7b, 0x26, 0x88, - 0x64, 0x7b, 0x38, 0x9f, 0x9d, 0xa6, 0x05, 0x33, 0x68, 0x14, 0x84, 0xf6, 0x60, 0x4a, 0xcd, 0x49, 0x24, 0x06, 0x25, - 0x17, 0x22, 0xfb, 0x93, 0xea, 0x2d, 0xc7, 0x47, 0x1e, 0xb2, 0xaf, 0x6f, 0x92, 0x26, 0x9d, 0x56, 0xa7, 0xca, 0x08, - 0xee, 0x0a, 0x9c, 0xa0, 0x04, 0xb3, 0x01, 0xfd, 0x93, 0x9f, 0x3f, 0x85, 0x24, 0xfa, 0xd0, 0x05, 0x84, 0x52, 0x67, - 0xcf, 0x88, 0xdc, 0x2c, 0x3c, 0xa2, 0x55, 0x88, 0x62, 0x5c, 0x20, 0x07, 0xc8, 0xfc, 0xb7, 0x91, 0x05, 0xbd, 0x86, - 0xfd, 0x42, 0x37, 0xa2, 0x7d, 0x08, 0x8b, 0x11, 0x9b, 0x2b, 0xde, 0xe8, 0x3d, 0x90, 0x67, 0x88, 0x1b, 0xf7, 0x34, - 0x2e, 0x68, 0xe9, 0x2a, 0x9b, 0x95, 0x02, 0xdd, 0xc4, 0xa3, 0x3e, 0x09, 0x1d, 0xb5, 0x5a, 0xde, 0x0c, 0xd1, 0x3b, - 0xd0, 0xf3, 0x7a, 0xff, 0x04, 0xdf, 0x0e, 0x08, 0x10, 0x51, 0xb8, 0xa3, 0x33, 0xf9, 0xc1, 0xe1, 0x77, 0xae, 0x3c, - 0xff, 0xc8, 0x44, 0x3d, 0x52, 0x99, 0xef, 0x96, 0xf8, 0xbb, 0x5b, 0xde, 0xff, 0xa1, 0x29, 0x53, 0x82, 0xf2, 0x83, - 0x60, 0x60, 0x64, 0x85, 0x0f, 0xae, 0x9d, 0x0e, 0xbf, 0x91, 0x79, 0x89, 0xe2, 0x85, 0xe4, 0xb2, 0x85, 0xdb, 0x2b, - 0xc6, 0x55, 0xac, 0xe2, 0xca, 0x0e, 0xda, 0x67, 0xdc, 0x7a, 0xfc, 0x10, 0x35, 0xc6, 0x1a, 0x47, 0xe7, 0x1c, 0x94, - 0x06, 0x84, 0x04, 0xd3, 0xc0, 0x26, 0x3d, 0x5a, 0x60, 0x99, 0x16, 0x48, 0x09, 0x42, 0x48, 0x2a, 0xba, 0x1f, 0x43, - 0x53, 0x89, 0xcd, 0x8c, 0x20, 0xad, 0x2a, 0x76, 0xa8, 0xc4, 0x29, 0x67, 0x1f, 0xa6, 0x58, 0x23, 0x7c, 0xaa, 0xe9, - 0x3b, 0x88, 0x92, 0xc8, 0x7b, 0x4e, 0x2e, 0x2e, 0x1d, 0x68, 0x45, 0xa6, 0x4a, 0x49, 0xdf, 0x79, 0xc1, 0xad, 0xbf, - 0xd6, 0x3e, 0x20, 0xd6, 0x41, 0x15, 0xf4, 0xac, 0x8a, 0xbf, 0xdc, 0x62, 0xae, 0xa4, 0x25, 0x56, 0xb1, 0xa7, 0x2c, - 0xf6, 0x73, 0x5a, 0x71, 0x1e, 0xce, 0x69, 0xe8, 0x2e, 0x39, 0x77, 0xa9, 0xb8, 0x27, 0x9d, 0xf5, 0x12, 0xe1, 0xbe, - 0x65, 0x07, 0xd3, 0x67, 0x25, 0xfc, 0xf8, 0xbb, 0x39, 0x29, 0x29, 0xd7, 0x81, 0x46, 0xcf, 0x61, 0xe0, 0x65, 0xd0, - 0xa2, 0xee, 0xd4, 0xc0, 0x3d, 0x91, 0xe8, 0x5b, 0x7f, 0x60, 0xc6, 0x66, 0xd9, 0x12, 0x19, 0x14, 0xcf, 0xd4, 0xff, - 0x24, 0x48, 0xe2, 0xb1, 0xfc, 0x23, 0x2f, 0x0e, 0x49, 0x22, 0xa9, 0x3e, 0x80, 0x3e, 0x09, 0x9e, 0x58, 0x80, 0x57, - 0x7f, 0xa0, 0x80, 0xa1, 0x28, 0x57, 0x39, 0x32, 0x77, 0xc2, 0x1c, 0xf2, 0x74, 0x57, 0xbd, 0x53, 0x07, 0x38, 0x7d, - 0xb5, 0x9e, 0x4d, 0x40, 0xa7, 0x85, 0x1e, 0xa0, 0xc4, 0x99, 0x11, 0xa5, 0x19, 0x07, 0xa7, 0x86, 0x39, 0xfc, 0xaf, - 0x57, 0x12, 0x61, 0xec, 0xc1, 0xc3, 0x41, 0xe3, 0x41, 0x05, 0xf9, 0xd9, 0x8e, 0xa6, 0x34, 0x0c, 0x48, 0xc2, 0xb9, - 0x16, 0xab, 0x64, 0x19, 0x5e, 0x3c, 0xf2, 0xca, 0x0c, 0xe1, 0x04, 0xd6, 0x9d, 0x3e, 0x95, 0x0e, 0x82, 0x71, 0x09, - 0x17, 0x2a, 0xaf, 0x39, 0x35, 0x1c, 0x69, 0xb9, 0x40, 0xf1, 0x57, 0x9a, 0xa8, 0x6b, 0x11, 0x4f, 0xe6, 0x47, 0x5c, - 0x35, 0x10, 0x61, 0xda, 0x05, 0x01, 0x96, 0x97, 0xc8, 0xad, 0x85, 0x72, 0xed, 0xb7, 0x1e, 0x36, 0x30, 0x06, 0xeb, - 0xe6, 0xd7, 0x4b, 0x7e, 0x7d, 0xd3, 0xb4, 0xf6, 0xe2, 0x2d, 0x2a, 0x34, 0x9d, 0xe8, 0xe9, 0x90, 0x22, 0x3c, 0x1d, - 0x77, 0x11, 0x19, 0x46, 0x03, 0x4c, 0xdf, 0x56, 0xd5, 0x62, 0x26, 0xed, 0x00, 0xfa, 0xb9, 0x20, 0xcd, 0x01, 0xa0, - 0x29, 0x42, 0xd9, 0x01, 0x70, 0x15, 0xaa, 0xf5, 0xba, 0x5f, 0x69, 0x63, 0x63, 0x3c, 0xe0, 0x11, 0x81, 0x59, 0xf1, - 0x94, 0x42, 0xc9, 0x79, 0x02, 0x79, 0xb1, 0x4d, 0x55, 0xba, 0x99, 0x96, 0xcd, 0xfa, 0xdd, 0xfa, 0x47, 0x96, 0x00, - 0xa2, 0x26, 0x79, 0x64, 0x32, 0x81, 0x0d, 0x15, 0xd2, 0x14, 0xc7, 0xa4, 0x56, 0x02, 0xae, 0xf9, 0xb0, 0x8f, 0x6c, - 0x09, 0x38, 0x3b, 0x70, 0x2d, 0x88, 0xc3, 0x59, 0x33, 0x64, 0xb2, 0x3c, 0xa7, 0xad, 0xd1, 0x3f, 0x5b, 0xad, 0xb1, - 0xb5, 0xff, 0x43, 0x4b, 0x71, 0x3f, 0x19, 0x0b, 0x4d, 0x0c, 0x48, 0x6d, 0x8f, 0xbf, 0xbb, 0x95, 0x74, 0xe6, 0x6d, - 0xc1, 0x49, 0xff, 0x37, 0xd3, 0xe6, 0x74, 0x9e, 0x3d, 0x39, 0x8c, 0x7c, 0xc0, 0x98, 0x0a, 0x61, 0x8c, 0x93, 0xf0, - 0x62, 0x3b, 0xbc, 0x68, 0x0c, 0x6a, 0xff, 0xe5, 0x0e, 0x86, 0x9c, 0xea, 0xd8, 0x7b, 0x1f, 0x44, 0xc9, 0xbe, 0x98, - 0x5b, 0x34, 0x56, 0x87, 0xb4, 0x28, 0x6e, 0xfb, 0x00, 0x32, 0xf0, 0xd2, 0xfd, 0xff, 0xb8, 0x75, 0x88, 0x63, 0xb0, - 0x09, 0x79, 0x89, 0x4b, 0x12, 0xb3, 0x4d, 0x1f, 0x05, 0xf5, 0xfa, 0xb4, 0x11, 0x2e, 0xd1, 0x5c, 0xe9, 0xfe, 0x07, - 0x2f, 0x5b, 0x54, 0x77, 0x29, 0x0f, 0xf7, 0x0e, 0x8c, 0x69, 0x7c, 0x73, 0xf3, 0x3d, 0x0d, 0xa6, 0x14, 0xba, 0x19, - 0xef, 0x60, 0x13, 0xbb, 0xde, 0x56, 0x56, 0x6c, 0x17, 0x99, 0xa2, 0xa2, 0xa9, 0xd1, 0x47, 0x33, 0xd8, 0xec, 0xd0, - 0x80, 0xf6, 0x6f, 0x31, 0xc9, 0x60, 0xf1, 0x70, 0x6b, 0x2e, 0x44, 0xcb, 0xeb, 0x9c, 0xed, 0x28, 0x38, 0x27, 0x23, - 0x8e, 0x24, 0x48, 0x93, 0xee, 0x3b, 0x8e, 0x1e, 0xd4, 0x41, 0xd5, 0x88, 0x3b, 0x6d, 0xc9, 0x7e, 0x45, 0x7d, 0x97, - 0x3e, 0xae, 0x0b, 0x79, 0xe5, 0x1c, 0x48, 0xc4, 0x67, 0x85, 0x37, 0x27, 0x44, 0x46, 0x6d, 0x1b, 0xa9, 0x15, 0x59, - 0x91, 0x5f, 0x21, 0x25, 0xea, 0x5f, 0x51, 0x2b, 0xc8, 0x62, 0x0e, 0xc0, 0xc0, 0x36, 0x00, 0xab, 0xdf, 0xac, 0x18, - 0xb2, 0xa5, 0x80, 0xc6, 0x2f, 0x67, 0xdb, 0x7c, 0xe2, 0x96, 0xec, 0xe8, 0x17, 0x44, 0x6d, 0x6b, 0x45, 0x13, 0x9c, - 0x77, 0x2f, 0xac, 0x9e, 0x89, 0xdf, 0x53, 0xcf, 0xb7, 0xc0, 0x36, 0x90, 0x4f, 0xd2, 0xfd, 0xce, 0x99, 0x3e, 0x60, - 0x0f, 0xc6, 0x58, 0xc7, 0x60, 0x57, 0xd8, 0x63, 0xa3, 0x37, 0x55, 0xe5, 0x39, 0x68, 0x57, 0xb7, 0x1c, 0x15, 0xf1, - 0xf8, 0x2d, 0xcb, 0x3a, 0x18, 0x66, 0x18, 0x3d, 0xf3, 0x05, 0x94, 0x2d, 0xda, 0x11, 0x99, 0x93, 0x5c, 0x46, 0xdb, - 0x54, 0x0e, 0x28, 0x81, 0x05, 0x31, 0xa9, 0x71, 0x4a, 0xdd, 0x2d, 0x9b, 0x97, 0xae, 0xa3, 0x09, 0xf1, 0xd6, 0x5f, - 0x67, 0x3e, 0xd7, 0x83, 0xa3, 0xf2, 0x3c, 0x44, 0x60, 0x1a, 0xc8, 0xc3, 0x02, 0x0e, 0x23, 0x79, 0x5e, 0x8a, 0x40, - 0x01, 0xef, 0x06, 0x7d, 0xb6, 0x19, 0x28, 0x72, 0x0a, 0x91, 0x77, 0x9e, 0x83, 0x05, 0xba, 0xc1, 0x53, 0x44, 0x19, - 0x87, 0x87, 0xff, 0x2e, 0x70, 0x19, 0x1e, 0x92, 0x25, 0x8c, 0xef, 0x1d, 0x4e, 0x24, 0x27, 0xa9, 0x8b, 0xa4, 0xf5, - 0x4b, 0x78, 0xa6, 0xb6, 0x71, 0x6b, 0xfe, 0x22, 0xfb, 0x24, 0x76, 0xaf, 0xbc, 0x80, 0xf9, 0x18, 0x35, 0xd9, 0x65, - 0xfe, 0xc2, 0x3c, 0x26, 0x3d, 0x33, 0xaf, 0xd1, 0x6a, 0x0d, 0x78, 0x20, 0x69, 0x45, 0x58, 0xca, 0x2c, 0x99, 0x73, - 0x19, 0x00, 0xe8, 0xda, 0x78, 0xd8, 0x1a, 0x42, 0x7c, 0x22, 0xd7, 0x77, 0x45, 0x42, 0x65, 0xaa, 0x59, 0x96, 0x23, - 0xf7, 0xc9, 0x4d, 0x08, 0x4b, 0xb5, 0xcb, 0x12, 0xb7, 0x99, 0xe6, 0xb6, 0x36, 0x3c, 0xf7, 0xca, 0xf2, 0xbd, 0xc0, - 0x14, 0xf5, 0xa0, 0xbf, 0xb3, 0x8d, 0x38, 0x45, 0x10, 0x22, 0x66, 0x70, 0x87, 0xa3, 0x11, 0x64, 0x53, 0x4e, 0xf4, - 0x67, 0xbb, 0xc4, 0xe6, 0xa7, 0x97, 0xa9, 0xaa, 0x70, 0x39, 0x62, 0x32, 0xb1, 0x39, 0x1b, 0xb0, 0x98, 0x83, 0x57, - 0x7b, 0x72, 0x9b, 0xdb, 0xb2, 0xec, 0x8d, 0x08, 0x56, 0x83, 0x16, 0xce, 0x1d, 0x2c, 0x15, 0xfa, 0x4e, 0x66, 0xbd, - 0xab, 0x83, 0x9b, 0xd9, 0x6f, 0xd2, 0xee, 0x8f, 0x1c, 0x7d, 0x55, 0x69, 0xdc, 0x81, 0x6d, 0x2c, 0x81, 0x0d, 0x8f, - 0x11, 0x29, 0x87, 0x44, 0xf5, 0xa9, 0x4f, 0x6c, 0x1e, 0xd5, 0x98, 0xe4, 0x38, 0xc8, 0x1d, 0x26, 0xae, 0x68, 0x3a, - 0x9b, 0xb4, 0x10, 0xbb, 0x52, 0x21, 0x3d, 0x9d, 0x85, 0xfc, 0x16, 0x73, 0xd3, 0x75, 0x92, 0xc8, 0xd6, 0xb5, 0x0f, - 0xf9, 0xa2, 0x25, 0x75, 0x68, 0x60, 0xa7, 0xc7, 0x1c, 0xfd, 0x78, 0xb5, 0x95, 0xaf, 0xd4, 0xd6, 0x71, 0x4e, 0x92, - 0x8f, 0x71, 0xbc, 0x68, 0xf8, 0xe7, 0xa2, 0xa2, 0xd1, 0xc2, 0x93, 0xd8, 0xfa, 0x61, 0x27, 0xaf, 0x5f, 0xd1, 0x62, - 0x36, 0x1c, 0xb5, 0x5e, 0x96, 0x57, 0x1c, 0xee, 0xdd, 0xb6, 0x14, 0x4b, 0x58, 0x1f, 0xe3, 0x72, 0xc9, 0xd3, 0xa8, - 0x5a, 0x3a, 0xfa, 0xcb, 0x1b, 0xb8, 0x25, 0xef, 0x04, 0xc0, 0x44, 0x52, 0x1f, 0x61, 0x41, 0x7b, 0x19, 0x31, 0x42, - 0xec, 0x05, 0xd9, 0x27, 0x68, 0x7b, 0xb1, 0xaf, 0x76, 0x3d, 0x0c, 0xd9, 0x92, 0x64, 0x77, 0x6f, 0x46, 0xf8, 0x42, - 0xdd, 0x3d, 0xb2, 0x1a, 0x87, 0x6b, 0xf2, 0xe2, 0x32, 0x44, 0xb1, 0x97, 0x70, 0xc3, 0xa8, 0x2d, 0xc5, 0xdc, 0x82, - 0x1b, 0x49, 0x8b, 0x89, 0xad, 0x51, 0x46, 0x0d, 0x9b, 0x43, 0x9b, 0x43, 0x69, 0xef, 0x15, 0xdf, 0xf0, 0xdf, 0x10, - 0xef, 0x7c, 0x69, 0x4b, 0x12, 0x75, 0xef, 0x42, 0x5a, 0xe6, 0x45, 0xba, 0x92, 0xf5, 0xf3, 0x76, 0x62, 0x43, 0x71, - 0x37, 0xc7, 0x80, 0xf5, 0xc4, 0x41, 0x76, 0x69, 0xf2, 0x81, 0xc4, 0x26, 0x4a, 0x56, 0x5a, 0xfd, 0xcf, 0xee, 0xfa, - 0xb0, 0xe0, 0xa1, 0x89, 0x46, 0xc7, 0xb6, 0x43, 0x37, 0x62, 0x1e, 0x7e, 0x8d, 0x67, 0xaa, 0x16, 0x90, 0x1c, 0xe6, - 0x26, 0x51, 0xea, 0x66, 0x84, 0xea, 0xc4, 0x8d, 0x17, 0x88, 0x7a, 0xda, 0xf5, 0x4c, 0xc7, 0xd2, 0xfb, 0xbb, 0x0c, - 0xa1, 0xa9, 0x21, 0x04, 0x0f, 0x21, 0x39, 0x3f, 0x09, 0x6f, 0x46, 0x27, 0xe2, 0x1b, 0xa6, 0xcb, 0x19, 0x72, 0x0f, - 0x5f, 0xa0, 0x75, 0x27, 0xc1, 0xc2, 0xe1, 0x86, 0x90, 0x22, 0x15, 0x04, 0xc8, 0xf6, 0x31, 0x80, 0x85, 0x46, 0xf6, - 0xa2, 0xc9, 0xd4, 0x80, 0xc8, 0x66, 0x6d, 0x4b, 0x98, 0x63, 0x33, 0x35, 0x68, 0xc1, 0xd6, 0xfc, 0x12, 0x28, 0x1b, - 0xda, 0xe2, 0x2d, 0xfd, 0x4f, 0x5e, 0x13, 0x41, 0x8c, 0x69, 0x6a, 0xd3, 0xcc, 0x7a, 0xe5, 0xda, 0xde, 0xf5, 0x29, - 0x16, 0x0b, 0xe4, 0xc0, 0x75, 0x43, 0x69, 0x6c, 0x8d, 0xd5, 0x25, 0x0d, 0x68, 0xb9, 0xa8, 0x2e, 0x08, 0x84, 0xc4, - 0x10, 0xf3, 0xaa, 0xa1, 0x90, 0x92, 0x84, 0x6a, 0x6e, 0xdd, 0x89, 0x6d, 0x82, 0xc2, 0xec, 0xb8, 0x33, 0x79, 0xe8, - 0xe7, 0x70, 0xfe, 0xfe, 0xc6, 0x2c, 0x40, 0x51, 0xb8, 0xe2, 0xa5, 0x8c, 0x06, 0x89, 0x7e, 0xb3, 0x1e, 0x7a, 0xfe, - 0x83, 0x03, 0xda, 0x9d, 0xca, 0x32, 0xa3, 0xd4, 0xa9, 0x9e, 0x09, 0x4e, 0x6f, 0x0d, 0xd0, 0x88, 0x48, 0x80, 0x09, - 0xfc, 0xa8, 0x3f, 0x0a, 0x15, 0x0b, 0x98, 0xb5, 0x95, 0x53, 0xaf, 0xef, 0x31, 0x10, 0x29, 0xec, 0xb0, 0x71, 0xce, - 0xa2, 0x55, 0x8d, 0x78, 0x42, 0x82, 0x3e, 0x48, 0xc8, 0xce, 0x59, 0xf5, 0x8c, 0xaf, 0x93, 0x0b, 0xbe, 0x60, 0x77, - 0xfc, 0xb5, 0x06, 0x50, 0x8e, 0x7f, 0xb1, 0xf7, 0x86, 0xd7, 0xc3, 0x16, 0xd7, 0x23, 0xe6, 0x8b, 0x32, 0x2f, 0x7f, - 0x78, 0xd0, 0x72, 0xfa, 0xf7, 0xe7, 0x69, 0x80, 0x2a, 0x7f, 0xb1, 0x84, 0x01, 0xa9, 0x3c, 0xbc, 0xf5, 0x46, 0xe4, - 0x4a, 0x66, 0x14, 0x8d, 0x59, 0x3b, 0x6e, 0x09, 0x3b, 0xb8, 0x28, 0x8e, 0x20, 0x54, 0xfc, 0xf3, 0x16, 0x40, 0x62, - 0x2b, 0x68, 0x99, 0xd1, 0xa0, 0x11, 0xed, 0x81, 0x3a, 0x2b, 0x6c, 0xcc, 0x0b, 0xb6, 0x2e, 0x5f, 0xde, 0xad, 0xe0, - 0x20, 0x4b, 0x48, 0x82, 0x87, 0xf5, 0xf6, 0xcd, 0x26, 0xd3, 0xa5, 0x87, 0xa9, 0xd7, 0x1d, 0xbf, 0x67, 0x56, 0x20, - 0xa4, 0xd9, 0x43, 0x64, 0x6d, 0x37, 0x12, 0xd3, 0x1b, 0x4f, 0x6d, 0x3b, 0x62, 0x5e, 0xb7, 0x13, 0x91, 0x2b, 0x75, - 0x6c, 0x9b, 0x87, 0xc8, 0x08, 0x2b, 0x8c, 0x24, 0xb8, 0xfc, 0x32, 0x20, 0x36, 0x51, 0xd0, 0xd8, 0xc7, 0xe2, 0x52, - 0x16, 0x93, 0xec, 0xa3, 0xf8, 0x4b, 0x59, 0xeb, 0x5f, 0x22, 0xd5, 0xd9, 0x13, 0xf8, 0x15, 0x43, 0x7b, 0x0f, 0xa1, - 0xb1, 0x4e, 0x83, 0xbb, 0x16, 0x3c, 0xb2, 0x80, 0x72, 0x1f, 0x1a, 0x12, 0x42, 0x71, 0xba, 0x1d, 0x16, 0xd9, 0xae, - 0x25, 0x46, 0x80, 0x8f, 0x92, 0x5e, 0xa9, 0x4d, 0xc6, 0x70, 0x05, 0x05, 0x70, 0x79, 0xae, 0xc7, 0xf3, 0xd1, 0xcd, - 0xf6, 0x4a, 0x23, 0x09, 0x7d, 0x37, 0xac, 0x78, 0xb9, 0xb9, 0xee, 0x2a, 0x8b, 0x36, 0x9f, 0x62, 0x1c, 0xeb, 0x02, - 0x91, 0x19, 0x21, 0x62, 0x6e, 0xd9, 0xa0, 0x20, 0x1d, 0x6c, 0x17, 0x03, 0xf4, 0xb1, 0x81, 0xe1, 0x0c, 0x56, 0xba, - 0xaa, 0xad, 0x9d, 0xa7, 0xc8, 0xf4, 0x6f, 0xb6, 0x98, 0xc0, 0xcf, 0x17, 0x17, 0x24, 0x04, 0x24, 0x2c, 0xf4, 0xcc, - 0x83, 0x59, 0x0f, 0x27, 0x79, 0xf6, 0x12, 0x13, 0x2e, 0x64, 0xa8, 0x70, 0xfc, 0xa0, 0xad, 0xe6, 0x82, 0xe6, 0xf8, - 0xf5, 0x4c, 0x5b, 0x95, 0xaf, 0x95, 0x34, 0xc9, 0x82, 0x43, 0x5e, 0x38, 0x5d, 0xde, 0x32, 0x44, 0xf1, 0xa9, 0x76, - 0xdd, 0x77, 0xb8, 0xf9, 0x4c, 0x8a, 0x9c, 0x54, 0xda, 0x89, 0x40, 0xa5, 0x21, 0x93, 0xb7, 0x7b, 0x01, 0xb0, 0x6d, - 0x88, 0xbe, 0x68, 0x36, 0x32, 0x53, 0x99, 0x8e, 0xae, 0x96, 0x87, 0x70, 0x6c, 0x0f, 0x6f, 0x06, 0xc3, 0x10, 0xf0, - 0xfa, 0xb4, 0x66, 0xff, 0xba, 0x67, 0x25, 0x55, 0x15, 0x4d, 0x8c, 0x8a, 0xb8, 0xb9, 0x60, 0x72, 0x0f, 0x2a, 0xa6, - 0xc1, 0x43, 0x38, 0x69, 0xc0, 0xe9, 0x38, 0x53, 0xd9, 0x20, 0x79, 0x81, 0x49, 0x10, 0x7b, 0x02, 0x2d, 0x4d, 0xc0, - 0xbc, 0xa2, 0xec, 0x38, 0xda, 0x8c, 0xed, 0x88, 0x50, 0xce, 0x9c, 0x44, 0x45, 0xfc, 0x98, 0x7b, 0xd2, 0x0a, 0xb0, - 0xcf, 0x40, 0x77, 0xbd, 0xc6, 0x4f, 0x6a, 0x41, 0xd1, 0xb7, 0xb6, 0xff, 0x5f, 0x86, 0x41, 0xd8, 0x9e, 0xb6, 0x73, - 0x00, 0x0a, 0xb2, 0x84, 0x00, 0xfe, 0xf2, 0x82, 0xbe, 0x04, 0x59, 0x92, 0x0a, 0x3f, 0x90, 0x57, 0x8f, 0xad, 0x3e, - 0x55, 0xce, 0xbe, 0x3a, 0xfb, 0xf5, 0xb7, 0xec, 0x97, 0xf4, 0xc1, 0x25, 0x27, 0x77, 0xfb, 0x54, 0x62, 0x73, 0xbd, - 0x53, 0x2a, 0x1c, 0x9d, 0x63, 0xb9, 0xac, 0xaf, 0xc4, 0x70, 0x39, 0x95, 0x10, 0xfc, 0x87, 0x0f, 0xc4, 0xef, 0xb3, - 0x72, 0xb7, 0x4f, 0x21, 0xbf, 0x9f, 0x4b, 0x2b, 0xf9, 0xc9, 0xe1, 0x46, 0xba, 0x4f, 0xab, 0x28, 0xc7, 0x5c, 0x7f, - 0x5b, 0x4a, 0xe5, 0xac, 0xb3, 0xb3, 0x4b, 0xb9, 0xb9, 0x9c, 0x73, 0x78, 0xaa, 0xcb, 0xbd, 0x5e, 0xb5, 0xd6, 0xff, - 0x57, 0x66, 0x0d, 0x65, 0xb9, 0x19, 0x94, 0xcc, 0x7b, 0x28, 0x08, 0x72, 0x37, 0xb1, 0x4e, 0x2f, 0x72, 0xe7, 0xb8, - 0x43, 0x39, 0x96, 0xb6, 0xbe, 0x2a, 0x53, 0x8f, 0xcc, 0x45, 0x8c, 0xf3, 0x15, 0xf1, 0xb2, 0x9a, 0xbc, 0x6d, 0xd0, - 0x6f, 0x4f, 0xc8, 0xfc, 0xe7, 0xd7, 0x90, 0x64, 0x3f, 0xc6, 0x2f, 0xea, 0xfe, 0x02, 0x5c, 0xc3, 0x9b, 0x72, 0xe4, - 0x05, 0x3b, 0xae, 0xab, 0xa7, 0x6d, 0xb2, 0xae, 0x85, 0x63, 0xdb, 0xe5, 0xc0, 0x6b, 0x8b, 0x38, 0x04, 0x44, 0x69, - 0x65, 0xdc, 0x73, 0x7a, 0xd7, 0xe9, 0x77, 0xa6, 0x3a, 0x86, 0xdd, 0x80, 0x20, 0x11, 0x0c, 0x28, 0x30, 0x1f, 0x83, - 0xba, 0x93, 0x51, 0xe5, 0xc4, 0x9e, 0x35, 0x10, 0x4a, 0x60, 0x45, 0xf3, 0x35, 0x12, 0x80, 0x96, 0x76, 0xe0, 0x65, - 0xad, 0xa2, 0x93, 0x25, 0x6b, 0x10, 0x1c, 0xf4, 0xff, 0x88, 0xc1, 0x11, 0x07, 0xdf, 0x24, 0x21, 0xce, 0x0a, 0x45, - 0x62, 0x4e, 0xb3, 0x47, 0x1f, 0xb3, 0x8f, 0x72, 0x09, 0xd2, 0xec, 0x47, 0x60, 0x80, 0x60, 0x19, 0x8e, 0x63, 0x91, - 0xa0, 0x64, 0xbe, 0x2a, 0xc8, 0x92, 0x9a, 0xf7, 0x9f, 0x60, 0x6c, 0xff, 0x46, 0xb7, 0x8d, 0xec, 0xef, 0x9a, 0x4a, - 0x6e, 0x7f, 0xe5, 0xdd, 0xf2, 0xeb, 0xfa, 0x7a, 0x79, 0xa1, 0xfe, 0xfc, 0xba, 0x69, 0x81, 0x77, 0x72, 0xf7, 0x52, - 0x0e, 0x35, 0x3f, 0x5f, 0x67, 0xc4, 0x58, 0x30, 0x40, 0xec, 0x53, 0xc7, 0x87, 0x92, 0xee, 0xb7, 0x9e, 0x0d, 0xac, - 0x89, 0xfd, 0x1a, 0xb7, 0xa8, 0x5e, 0xce, 0x0b, 0x6c, 0x56, 0xe3, 0x1a, 0xba, 0xe7, 0x85, 0xd6, 0x3c, 0x17, 0x66, - 0xa9, 0xa0, 0x14, 0x5b, 0x53, 0xc0, 0x27, 0xb8, 0xeb, 0xca, 0x4d, 0x6a, 0xa2, 0xea, 0x4d, 0x78, 0x92, 0xa0, 0xa2, - 0x03, 0x17, 0x4d, 0x5f, 0x3d, 0xb5, 0x2d, 0x36, 0x86, 0x3f, 0x13, 0x74, 0x35, 0x86, 0xac, 0x46, 0x39, 0x66, 0x2d, - 0x56, 0x7a, 0xa1, 0xb5, 0xbc, 0x5a, 0xea, 0x6e, 0x5f, 0x03, 0xbd, 0xf2, 0x82, 0x32, 0xe0, 0x1e, 0x80, 0xac, 0x57, - 0xf4, 0x94, 0x56, 0x91, 0x2d, 0xd9, 0x27, 0x14, 0xdc, 0x3c, 0x9e, 0xe0, 0xb0, 0xf4, 0x51, 0xdd, 0x23, 0x4d, 0x62, - 0x2b, 0x5c, 0xc3, 0xde, 0x64, 0x55, 0xe9, 0x65, 0xf3, 0x84, 0x07, 0x98, 0xd3, 0x82, 0xfd, 0x1b, 0xdb, 0x62, 0xf9, - 0x71, 0x12, 0x68, 0xbb, 0x68, 0x14, 0x37, 0xca, 0x00, 0x88, 0xd2, 0x3d, 0xbd, 0x01, 0x07, 0xa2, 0x5d, 0xd7, 0x42, - 0x7d, 0x9b, 0xd8, 0x0e, 0xe7, 0x26, 0x13, 0x6a, 0xe1, 0xc2, 0x1a, 0xcd, 0xa6, 0x0b, 0x27, 0x6a, 0xef, 0xd2, 0x9e, - 0x27, 0x83, 0x8c, 0xff, 0x2a, 0x83, 0x98, 0xf4, 0xbd, 0xc0, 0xa3, 0x83, 0x70, 0x0f, 0xa1, 0x27, 0x61, 0x91, 0x8a, - 0xd6, 0x14, 0x6c, 0x83, 0x15, 0xa5, 0x71, 0x00, 0xa0, 0xbd, 0x8b, 0xb8, 0x01, 0x07, 0x37, 0x6c, 0x0c, 0x1d, 0x1b, - 0xb7, 0xe4, 0x95, 0x64, 0x82, 0xa0, 0xf2, 0x66, 0x89, 0xcd, 0x78, 0xb2, 0x13, 0x95, 0x6f, 0x70, 0xb3, 0x73, 0x27, - 0x14, 0xf6, 0x3b, 0x9d, 0x11, 0x4c, 0x59, 0x59, 0xed, 0xd0, 0x37, 0x23, 0x5e, 0x70, 0x98, 0x43, 0xb2, 0x20, 0x22, - 0x19, 0xb1, 0xaa, 0x1b, 0xbf, 0xf3, 0x7e, 0x94, 0x9b, 0x89, 0x6d, 0xb1, 0x5e, 0xf1, 0x8c, 0x60, 0xbd, 0x83, 0xa3, - 0x73, 0xf2, 0xdc, 0xcd, 0xc8, 0x5c, 0xe1, 0x3f, 0x86, 0xc9, 0xed, 0x66, 0x7e, 0x30, 0x8c, 0xa8, 0x2f, 0xff, 0x93, - 0x8c, 0x59, 0x55, 0x4e, 0xa3, 0x31, 0x24, 0x44, 0x32, 0xbc, 0x09, 0x40, 0x3c, 0xcf, 0x9a, 0x8c, 0xd1, 0x4c, 0xac, - 0xb6, 0xad, 0xd3, 0x34, 0xfb, 0xf6, 0x92, 0xd3, 0xef, 0x45, 0x85, 0x17, 0x78, 0x5c, 0x75, 0x6e, 0x64, 0xd7, 0x0f, - 0x74, 0x31, 0x87, 0xbe, 0x55, 0xe9, 0xaa, 0xbe, 0x91, 0x1f, 0x6a, 0xf8, 0x4a, 0x0c, 0xea, 0x6e, 0x50, 0xf2, 0x00, - 0x00, 0xfd, 0x71, 0x5e, 0x5e, 0xfd, 0x5f, 0xa3, 0xb9, 0x93, 0x4e, 0xb0, 0xb1, 0x62, 0x69, 0x8e, 0xe3, 0xe5, 0xd0, - 0x5f, 0xa8, 0xe8, 0x39, 0xa1, 0xdf, 0x8d, 0x48, 0xba, 0x44, 0x67, 0x18, 0x4f, 0xcc, 0xd2, 0xe0, 0xb0, 0x86, 0x12, - 0xfa, 0x9b, 0xd1, 0x6f, 0xd7, 0xde, 0x37, 0x90, 0xe2, 0xdf, 0xb8, 0xad, 0x8e, 0x67, 0x47, 0x95, 0x99, 0xd4, 0x32, - 0x0f, 0xdc, 0x16, 0x57, 0x75, 0xd5, 0xcc, 0xa7, 0xed, 0x92, 0x69, 0xda, 0x79, 0xcc, 0x2e, 0xe3, 0x57, 0x38, 0x91, - 0x44, 0x7d, 0xb7, 0x0e, 0x03, 0x34, 0x30, 0xd0, 0x5e, 0x12, 0xa7, 0x17, 0x99, 0xae, 0xde, 0x6a, 0x06, 0x43, 0x73, - 0xa5, 0x4e, 0x3f, 0xb0, 0x7a, 0x41, 0xcb, 0xb0, 0xb3, 0x66, 0xf2, 0xc8, 0x09, 0xb1, 0x8b, 0x9c, 0x9f, 0x98, 0x0f, - 0x39, 0xa1, 0xa6, 0x01, 0xbd, 0x9d, 0x97, 0x57, 0xae, 0x54, 0x91, 0x81, 0x9a, 0x09, 0x29, 0x20, 0xbb, 0xa1, 0xf5, - 0xb1, 0x26, 0xc6, 0x1e, 0xa4, 0x0b, 0xb3, 0xd6, 0x3c, 0x0d, 0x42, 0x53, 0x28, 0x0b, 0x57, 0x66, 0x64, 0xa3, 0xf0, - 0x3d, 0x39, 0x85, 0x86, 0x0b, 0x5a, 0x42, 0x7b, 0xf7, 0x3e, 0x24, 0x74, 0xf7, 0x98, 0x44, 0xd5, 0x74, 0x96, 0x16, - 0xca, 0xcd, 0x42, 0x79, 0x8e, 0xc0, 0x0b, 0x16, 0xb9, 0xe7, 0x55, 0x39, 0x12, 0xb7, 0xee, 0xd6, 0xe9, 0xeb, 0x6e, - 0xb5, 0x86, 0x7d, 0x4c, 0x79, 0x24, 0xbc, 0xa3, 0x85, 0xf9, 0x57, 0xb2, 0xe4, 0x48, 0x87, 0x8d, 0x9a, 0x66, 0xf2, - 0x15, 0x3e, 0xff, 0x47, 0x75, 0x6f, 0xe2, 0x7d, 0xe2, 0x59, 0x81, 0x70, 0x57, 0x14, 0x3a, 0xe3, 0x8e, 0x59, 0x47, - 0xeb, 0x70, 0x4e, 0x9d, 0x98, 0xf1, 0xf0, 0xb8, 0x40, 0x31, 0xfc, 0xf6, 0x8c, 0x06, 0xdc, 0xfd, 0xe9, 0x98, 0xd8, - 0xbd, 0x0e, 0x52, 0xec, 0x32, 0x8b, 0x74, 0x7f, 0xd5, 0x68, 0xaa, 0x0b, 0xb1, 0x6e, 0x95, 0xb9, 0x27, 0xa6, 0xec, - 0x30, 0x9c, 0x51, 0xed, 0xb3, 0x85, 0x9b, 0xbd, 0x91, 0xbb, 0x51, 0xd5, 0x53, 0x6c, 0xe9, 0x92, 0xc3, 0x13, 0x38, - 0x64, 0xd3, 0xa8, 0xdc, 0xfd, 0x5a, 0xcb, 0x57, 0xfb, 0x6a, 0xd1, 0x97, 0x58, 0xc4, 0xf7, 0xf3, 0x21, 0x85, 0x2d, - 0x4f, 0x44, 0xb6, 0x3a, 0x8c, 0x75, 0x80, 0xf1, 0x50, 0xeb, 0xdb, 0xcd, 0x4e, 0x6a, 0x3f, 0xa0, 0xbb, 0x75, 0x96, - 0x96, 0x6f, 0x16, 0xbf, 0xad, 0xff, 0x3c, 0x70, 0x2f, 0x39, 0x14, 0xbf, 0x56, 0x5f, 0x45, 0xa2, 0xc1, 0xfd, 0xb2, - 0x5c, 0x93, 0x49, 0x71, 0xfc, 0x04, 0xc7, 0x54, 0xa6, 0x28, 0x47, 0xd5, 0x6d, 0x37, 0x84, 0x8a, 0x8d, 0x8e, 0xcd, - 0x79, 0xb2, 0x33, 0x75, 0xf5, 0x00, 0x8f, 0x0c, 0x51, 0xfb, 0x9f, 0xca, 0x8b, 0xd3, 0x1e, 0xd9, 0x07, 0xfb, 0xb7, - 0xcc, 0x21, 0xb6, 0x4e, 0xbb, 0x4e, 0xad, 0x9a, 0x70, 0xe0, 0xc3, 0xea, 0x1a, 0xff, 0x17, 0x2f, 0xb8, 0xd1, 0x44, - 0xf4, 0x56, 0xb5, 0x65, 0xa5, 0x04, 0xb6, 0xab, 0x5d, 0x2a, 0x35, 0xbd, 0xd5, 0x4d, 0x8c, 0xcb, 0x9c, 0xd7, 0xd5, - 0xde, 0x90, 0xf5, 0x93, 0xa0, 0x0d, 0xb9, 0x7f, 0xfa, 0x30, 0xe2, 0x10, 0x23, 0x29, 0x6b, 0x17, 0x63, 0xae, 0x0d, - 0xa1, 0x93, 0x2d, 0xca, 0x98, 0xdc, 0xf5, 0x4f, 0x24, 0xaa, 0x2e, 0x9a, 0x20, 0x10, 0xe7, 0xed, 0xb1, 0xf5, 0xea, - 0x6e, 0x71, 0xc9, 0xcd, 0x70, 0x65, 0xaa, 0x12, 0xf0, 0x79, 0x62, 0xf0, 0xc5, 0x82, 0x44, 0x09, 0x3c, 0x0b, 0xd5, - 0x64, 0xdc, 0x35, 0x44, 0x1f, 0x6c, 0xbc, 0xfc, 0xc3, 0xc8, 0x31, 0x3f, 0xf3, 0x4f, 0xca, 0x1b, 0x44, 0x27, 0xc0, - 0x99, 0x00, 0x3c, 0x9e, 0xa5, 0x25, 0xd5, 0x37, 0xa7, 0x7f, 0x6d, 0x92, 0xff, 0x77, 0x6c, 0xf8, 0x56, 0xfb, 0x15, - 0xd0, 0x68, 0x61, 0xd8, 0x21, 0xd0, 0x1a, 0xd4, 0x39, 0x85, 0x71, 0x0f, 0x01, 0xb5, 0xe2, 0x1a, 0xd7, 0x77, 0x69, - 0x84, 0x30, 0x08, 0x49, 0x50, 0xd9, 0x1d, 0x76, 0xb8, 0xb7, 0xbe, 0x2f, 0x90, 0x01, 0xc2, 0x43, 0x19, 0x41, 0x8b, - 0x8c, 0x07, 0xf7, 0x06, 0x7b, 0x10, 0xd6, 0xb9, 0x94, 0x53, 0xae, 0x92, 0xae, 0x43, 0xf6, 0x71, 0xd3, 0xf4, 0x1a, - 0x27, 0xe4, 0x08, 0x52, 0xa9, 0x67, 0x40, 0xd3, 0x74, 0x91, 0x5e, 0xae, 0xb7, 0x74, 0xca, 0xb7, 0x06, 0x62, 0xeb, - 0x5a, 0x58, 0x74, 0x9f, 0x5d, 0xca, 0x43, 0x0f, 0x52, 0x08, 0x0e, 0x89, 0xe5, 0x14, 0xd4, 0x0f, 0x60, 0x52, 0x2e, - 0xff, 0xc3, 0x24, 0x5e, 0xe5, 0xee, 0xfe, 0xd7, 0x6a, 0xb1, 0xaa, 0x1e, 0xcc, 0x6c, 0xfc, 0x40, 0xf7, 0x97, 0xf0, - 0x51, 0xad, 0x3d, 0x5f, 0x39, 0x66, 0x05, 0xa6, 0x0c, 0xfe, 0x93, 0x7f, 0xd0, 0x86, 0x3a, 0x97, 0xf9, 0x6f, 0x71, - 0x25, 0xae, 0x81, 0x14, 0xe7, 0x3d, 0xd4, 0x88, 0x26, 0x69, 0xbc, 0x4c, 0x59, 0x6d, 0x1a, 0x9f, 0x66, 0x8a, 0x40, - 0x88, 0x3a, 0x7a, 0x1d, 0x2e, 0x39, 0x70, 0x91, 0xc3, 0xaa, 0x05, 0xf8, 0x67, 0xc1, 0x0a, 0xe8, 0xf6, 0xb7, 0xe4, - 0x68, 0xcd, 0xfc, 0xed, 0x8e, 0xc6, 0x95, 0x0b, 0x39, 0x34, 0xb1, 0xf6, 0xd5, 0x76, 0x4c, 0xce, 0xd4, 0x9d, 0xa7, - 0x15, 0x8a, 0xae, 0xab, 0x9b, 0x89, 0x2b, 0x02, 0x8e, 0x53, 0xcf, 0x0f, 0x02, 0x0c, 0x66, 0x85, 0x2f, 0xfb, 0x42, - 0x4d, 0xbf, 0xc6, 0x60, 0x0a, 0x32, 0x96, 0x3d, 0x8b, 0x62, 0x78, 0x17, 0xf2, 0x2a, 0x62, 0x2c, 0x97, 0x22, 0x56, - 0x08, 0x65, 0x01, 0x5b, 0x56, 0xae, 0x47, 0xa1, 0x78, 0x78, 0x9c, 0xe2, 0xdd, 0xcc, 0x39, 0x52, 0xee, 0x12, 0xcc, - 0xee, 0x90, 0xf9, 0x49, 0x22, 0xf5, 0xda, 0xb5, 0x60, 0x93, 0x62, 0x8a, 0x5d, 0x51, 0xe4, 0x06, 0x87, 0x30, 0xe1, - 0xa8, 0x7b, 0x7b, 0xa3, 0x69, 0x22, 0xa1, 0x91, 0x28, 0x30, 0x23, 0xa4, 0xbb, 0xfe, 0xe3, 0xee, 0x4d, 0x3f, 0x99, - 0x32, 0x06, 0x11, 0xd0, 0x28, 0x7a, 0x06, 0x10, 0x7a, 0xbe, 0x4a, 0xb9, 0x64, 0x3a, 0xae, 0x60, 0xc4, 0x7d, 0x05, - 0x24, 0x5c, 0x34, 0x6e, 0xcd, 0x2f, 0xd1, 0x49, 0xa6, 0x78, 0x9a, 0x00, 0x45, 0xa3, 0xad, 0xf2, 0x6c, 0x28, 0x1f, - 0x79, 0x16, 0xac, 0x44, 0x3d, 0x69, 0x70, 0x14, 0x0c, 0xba, 0xd9, 0x48, 0xc2, 0x21, 0x35, 0x19, 0xc6, 0xc8, 0x30, - 0x38, 0xfa, 0x97, 0xb6, 0xca, 0x43, 0x6a, 0x5d, 0x2d, 0x14, 0x32, 0xa3, 0x07, 0x33, 0x3f, 0x98, 0xa8, 0x61, 0x55, - 0x0b, 0xf3, 0x41, 0xba, 0x76, 0x5a, 0x65, 0x94, 0x25, 0xc6, 0x69, 0xb0, 0x30, 0x86, 0x1c, 0x6a, 0x1c, 0xb0, 0xd9, - 0x40, 0xee, 0x6a, 0xce, 0xe6, 0x51, 0x33, 0x6e, 0xaf, 0x6b, 0x46, 0x9f, 0xfa, 0xe2, 0x56, 0x7f, 0x2e, 0xd3, 0x0d, - 0x3b, 0x56, 0xf9, 0x4b, 0xbf, 0xa8, 0xa6, 0x0f, 0x3d, 0xe6, 0x4d, 0x39, 0x18, 0x66, 0x78, 0xf5, 0x59, 0x58, 0x3c, - 0x48, 0x1a, 0x94, 0xf9, 0x52, 0xad, 0x1d, 0x6e, 0x7f, 0x3f, 0x30, 0xf4, 0x66, 0x37, 0x31, 0x49, 0x1a, 0x02, 0xe5, - 0x08, 0x89, 0x08, 0x8e, 0x59, 0xf1, 0x1f, 0x57, 0x95, 0xff, 0xbd, 0x53, 0x5f, 0xd0, 0x83, 0xf0, 0xd1, 0x5e, 0xf7, - 0x34, 0x0a, 0x98, 0xb3, 0x96, 0xed, 0xea, 0xd3, 0x84, 0x1a, 0xd2, 0x5f, 0x11, 0x32, 0x6e, 0x1c, 0xab, 0x7f, 0x74, - 0x53, 0xf2, 0x3b, 0x5d, 0x25, 0xf6, 0xd1, 0x5c, 0x9f, 0xd8, 0xa2, 0x4a, 0x3a, 0x3a, 0x36, 0xa7, 0x2d, 0x29, 0xcd, - 0x49, 0xf9, 0x56, 0x7b, 0x78, 0xda, 0x4a, 0x71, 0xc9, 0xe6, 0x3d, 0xb9, 0x9a, 0x27, 0x59, 0x6d, 0xcb, 0x71, 0x84, - 0x3b, 0xc8, 0xd7, 0xe7, 0x8c, 0xd2, 0xd1, 0x07, 0xab, 0x1f, 0xf7, 0x26, 0x81, 0xcc, 0xd3, 0x13, 0x70, 0xa3, 0x6b, - 0x57, 0x7a, 0x7c, 0x2b, 0x4e, 0xcc, 0x93, 0xf7, 0x43, 0xf6, 0x6b, 0x5c, 0xc9, 0x82, 0x8e, 0x7b, 0x5f, 0x35, 0x4c, - 0xb7, 0x19, 0xd3, 0x7e, 0xa4, 0x18, 0x8c, 0xe6, 0xab, 0x2c, 0x89, 0x0a, 0x62, 0xc1, 0x6b, 0xe2, 0x83, 0xd8, 0x00, - 0x40, 0xce, 0x68, 0x8b, 0x5a, 0x7a, 0x8c, 0x25, 0x51, 0xbc, 0xad, 0x40, 0xcd, 0x79, 0x76, 0x96, 0xd1, 0xaa, 0x3a, - 0xd1, 0xab, 0x53, 0xae, 0xd2, 0xec, 0x22, 0x74, 0x3d, 0x7c, 0x65, 0x29, 0x2a, 0x59, 0x56, 0xbd, 0x0b, 0xd3, 0x57, - 0xec, 0x95, 0x17, 0x48, 0x79, 0x57, 0x4a, 0x4d, 0x21, 0x23, 0x1b, 0x83, 0xc6, 0xd6, 0xd9, 0x4b, 0x2c, 0x6e, 0xb2, - 0x3c, 0x4a, 0x28, 0x7c, 0x31, 0xf7, 0x71, 0x7b, 0x2c, 0x55, 0xc5, 0x9c, 0x43, 0x98, 0x93, 0x2a, 0x9d, 0x74, 0x95, - 0x03, 0xf8, 0xd5, 0x65, 0x10, 0xd6, 0x48, 0xa1, 0x3a, 0xc7, 0x3d, 0x6c, 0x49, 0xa6, 0x63, 0x06, 0x19, 0x8b, 0xee, - 0xfa, 0x3b, 0x2a, 0x9d, 0xc7, 0x41, 0x74, 0x1f, 0xba, 0x5a, 0x21, 0xc2, 0x60, 0x7b, 0xd6, 0x92, 0x2b, 0x9e, 0x2b, - 0x8e, 0xb2, 0x2b, 0x31, 0xb5, 0x3c, 0x1b, 0xb2, 0x6d, 0xd1, 0x15, 0x4b, 0x65, 0x4d, 0x77, 0x57, 0x13, 0xa9, 0xe0, - 0xb1, 0x1f, 0x7f, 0xe0, 0xcb, 0x92, 0x91, 0x53, 0x99, 0xc4, 0xb2, 0x0c, 0x61, 0x6e, 0xdc, 0x10, 0x3c, 0xc1, 0x68, - 0xde, 0x92, 0x79, 0xca, 0x29, 0x85, 0xd2, 0xfb, 0x9f, 0x1b, 0x8f, 0x50, 0x36, 0xdb, 0x30, 0xbd, 0x65, 0xea, 0xbb, - 0xc4, 0xf5, 0xfc, 0x87, 0xe8, 0x94, 0x44, 0x0b, 0xde, 0x9f, 0x27, 0x32, 0xda, 0x54, 0xa8, 0xb0, 0x6e, 0x66, 0xbb, - 0xcf, 0xc1, 0xc6, 0x7f, 0x51, 0x49, 0x86, 0x1c, 0x54, 0x98, 0x5e, 0xb5, 0x63, 0xa1, 0x73, 0xc8, 0x5d, 0x6f, 0x28, - 0x88, 0x8d, 0xc0, 0x6e, 0x68, 0x05, 0x89, 0x34, 0x59, 0x88, 0x7d, 0x36, 0xaa, 0xba, 0x9b, 0x55, 0xa1, 0x06, 0xfc, - 0xf2, 0x17, 0xb1, 0x38, 0xbf, 0x40, 0x52, 0x7d, 0xc1, 0x21, 0x21, 0xf4, 0xc9, 0x6f, 0xc4, 0x5e, 0x0d, 0xbe, 0x88, - 0x95, 0x66, 0xdb, 0x31, 0xfa, 0x99, 0x9f, 0x8f, 0xbb, 0xee, 0x0c, 0x53, 0x74, 0xb8, 0x01, 0x8b, 0x11, 0x43, 0x4e, - 0x52, 0x37, 0xf9, 0x4b, 0x4a, 0x7e, 0x32, 0xbd, 0xf9, 0x06, 0xdf, 0x69, 0x6d, 0x6f, 0xa0, 0x50, 0x88, 0x59, 0x67, - 0x68, 0x70, 0xc3, 0x1e, 0x9e, 0xea, 0x98, 0x59, 0x98, 0xe3, 0x90, 0x24, 0xa2, 0x45, 0x0e, 0x67, 0x88, 0xdf, 0x00, - 0x98, 0x40, 0x93, 0x95, 0x08, 0x19, 0x25, 0xb0, 0x47, 0xf0, 0x82, 0x9b, 0x6d, 0xde, 0xef, 0x79, 0x1e, 0x2e, 0xa4, - 0x56, 0xae, 0xe0, 0x0a, 0x30, 0xd5, 0xb3, 0x6b, 0x49, 0xf1, 0xe1, 0x51, 0xb4, 0x86, 0x78, 0xae, 0x25, 0x94, 0xc5, - 0xce, 0x83, 0x60, 0x55, 0x65, 0x57, 0xd9, 0x19, 0xcc, 0x52, 0x0f, 0x0e, 0x54, 0x71, 0x81, 0x24, 0xdd, 0x18, 0x6f, - 0x53, 0xcc, 0xb2, 0x16, 0x7e, 0xaa, 0x62, 0xde, 0xb0, 0xa9, 0xe0, 0xf0, 0x5a, 0x9d, 0x7f, 0x31, 0xd6, 0x30, 0xa1, - 0x4d, 0x0d, 0xac, 0x04, 0x31, 0x69, 0x58, 0xc2, 0xc6, 0xc1, 0x67, 0xd0, 0x9f, 0x07, 0x4c, 0x33, 0x9c, 0xde, 0x8f, - 0x51, 0xbd, 0x65, 0xf5, 0xc9, 0xf7, 0xd2, 0xf3, 0x46, 0x1d, 0x3d, 0xa8, 0xc4, 0xaa, 0xe5, 0xeb, 0x0c, 0x11, 0xdd, - 0xde, 0xfa, 0x8c, 0xe7, 0xd4, 0x32, 0x04, 0x40, 0xe2, 0x49, 0x9d, 0x19, 0x7b, 0x7c, 0xdc, 0x30, 0x46, 0x52, 0xa5, - 0xb7, 0x2c, 0x42, 0xa6, 0x9f, 0x94, 0x55, 0x0d, 0x87, 0x27, 0x9b, 0x76, 0x25, 0x54, 0x0c, 0xd7, 0x6f, 0x96, 0x17, - 0x50, 0x85, 0xc5, 0x0c, 0xc5, 0x1c, 0x9b, 0xca, 0xd9, 0x78, 0x83, 0x79, 0x06, 0xe3, 0x3c, 0xa5, 0x31, 0x37, 0x54, - 0xa0, 0x5f, 0x2a, 0x47, 0x53, 0x67, 0x62, 0xce, 0x58, 0x9e, 0xfb, 0xb0, 0xe3, 0x73, 0xc7, 0x98, 0x5d, 0x78, 0xee, - 0xee, 0xa9, 0xc3, 0xf6, 0x59, 0x74, 0x19, 0xee, 0x6e, 0x61, 0xc9, 0x9e, 0x92, 0x49, 0x8c, 0x03, 0x58, 0xe7, 0xd1, - 0x95, 0xad, 0xf1, 0x52, 0x06, 0xbb, 0x3f, 0x41, 0x0c, 0xe0, 0x68, 0xc1, 0x60, 0x04, 0xec, 0x5a, 0x7e, 0xed, 0x6a, - 0xac, 0x74, 0xf3, 0x71, 0x60, 0x85, 0x17, 0x99, 0xc0, 0xe5, 0x23, 0x26, 0xd2, 0xe0, 0x7f, 0xde, 0xc7, 0xc9, 0x57, - 0x9b, 0x8e, 0x26, 0xb2, 0xd7, 0x42, 0xbe, 0xf0, 0xaf, 0xe1, 0x6e, 0x1e, 0x98, 0xf2, 0xc5, 0x1e, 0x4f, 0x11, 0x05, - 0x4d, 0x62, 0x6c, 0xf5, 0x8c, 0x8b, 0x3d, 0x73, 0xb5, 0xe1, 0x17, 0x22, 0x8a, 0xbb, 0xbb, 0xb8, 0x2c, 0x04, 0x2c, - 0x99, 0xf0, 0x53, 0xce, 0xd4, 0xc8, 0x94, 0x3d, 0xc4, 0xb7, 0xcb, 0xf0, 0xe1, 0x71, 0x23, 0xf6, 0xc9, 0x5d, 0x11, - 0x9e, 0x48, 0xaa, 0xb0, 0xcf, 0xfc, 0xef, 0x90, 0x31, 0x27, 0xa2, 0xe8, 0xb1, 0x4c, 0x37, 0x06, 0xcf, 0x7f, 0x56, - 0xf1, 0x64, 0x55, 0x00, 0xb6, 0x46, 0x05, 0xe9, 0x97, 0x80, 0x73, 0x0a, 0x40, 0x3d, 0x0c, 0x63, 0x20, 0xc5, 0x9c, - 0x42, 0xe0, 0xe8, 0x92, 0x76, 0xc0, 0xf0, 0xb3, 0x59, 0xd0, 0x63, 0xf1, 0x5b, 0xba, 0xdc, 0x6d, 0xce, 0xcf, 0xd6, - 0x28, 0x6f, 0x2a, 0x77, 0xd5, 0xb0, 0xca, 0x4c, 0x4d, 0x61, 0xb2, 0xd8, 0x9b, 0xb9, 0x0e, 0xdf, 0xf9, 0x63, 0x5f, - 0xbb, 0xc4, 0xc1, 0xc8, 0x8d, 0xcc, 0x15, 0x2e, 0x3c, 0x98, 0x76, 0xf2, 0x0a, 0x88, 0x9a, 0xad, 0x04, 0x57, 0x02, - 0xad, 0x07, 0xa7, 0x0e, 0x77, 0x0a, 0x58, 0x41, 0x20, 0xf3, 0xfa, 0xab, 0x3e, 0x39, 0x90, 0xd1, 0xe6, 0x0a, 0x19, - 0xf4, 0xdc, 0xea, 0x05, 0x5a, 0xe5, 0x7d, 0xab, 0xfb, 0x39, 0x79, 0x63, 0xde, 0x75, 0x1f, 0x81, 0xc9, 0xf7, 0x8c, - 0xc4, 0x86, 0x2c, 0xaf, 0x85, 0xc2, 0x24, 0x01, 0x3d, 0x0e, 0xaa, 0x0a, 0x89, 0xd4, 0xa1, 0x6c, 0xd4, 0x0c, 0x15, - 0xc2, 0xf4, 0xfa, 0x07, 0x80, 0x80, 0xa3, 0x94, 0x42, 0x79, 0x22, 0xaa, 0x32, 0x02, 0x08, 0x8c, 0x0d, 0xd0, 0xb0, - 0x0c, 0x4c, 0x61, 0x9b, 0x51, 0xb4, 0xe5, 0x74, 0xe9, 0x6e, 0xbc, 0x2f, 0x47, 0xe6, 0xbc, 0x1b, 0x3c, 0x4b, 0xd0, - 0x6e, 0xec, 0xeb, 0x38, 0x86, 0x7e, 0x2a, 0xfa, 0x47, 0xb0, 0x83, 0x73, 0x58, 0x82, 0x82, 0x53, 0x42, 0x9f, 0x33, - 0xff, 0x83, 0xaf, 0xc4, 0xeb, 0x9e, 0xb6, 0xb8, 0xb7, 0x63, 0xc7, 0xcc, 0xca, 0x8f, 0x4d, 0x96, 0x5c, 0xcb, 0x90, - 0x44, 0x79, 0xcd, 0xa5, 0x63, 0xd0, 0x94, 0xc8, 0xcd, 0x07, 0x81, 0xa4, 0xbd, 0x41, 0xe5, 0x47, 0x9b, 0xd3, 0xfe, - 0x48, 0x08, 0xb1, 0x5a, 0x6a, 0xe6, 0xe2, 0x4b, 0x8a, 0x05, 0x50, 0x70, 0x1f, 0xc0, 0xf9, 0x3b, 0x71, 0xfd, 0xbb, - 0xe8, 0xc0, 0xa1, 0x8f, 0x00, 0x06, 0xe0, 0xad, 0x54, 0x5b, 0x79, 0x13, 0x50, 0x5a, 0x01, 0x90, 0x6b, 0x53, 0x19, - 0xe0, 0x0d, 0xf9, 0x0b, 0x1e, 0xd9, 0x97, 0x29, 0x50, 0x8c, 0xe2, 0xc6, 0xbb, 0x4c, 0xe5, 0xe5, 0xdd, 0x31, 0xe7, - 0x82, 0xdd, 0xdc, 0x30, 0xaf, 0xda, 0x44, 0x99, 0xb4, 0x5d, 0x0c, 0x62, 0x9c, 0x12, 0xa4, 0x28, 0x01, 0xd2, 0xf7, - 0x0f, 0x66, 0x21, 0x7a, 0xfe, 0xee, 0xd1, 0x5d, 0x65, 0xae, 0xc3, 0x30, 0x9a, 0xec, 0x1d, 0x51, 0x0b, 0x72, 0xba, - 0x3a, 0x26, 0xdf, 0x27, 0x07, 0xe1, 0x5f, 0x12, 0xf5, 0x33, 0x55, 0x82, 0xa6, 0xfa, 0xa6, 0x8a, 0x38, 0xa8, 0xf1, - 0x09, 0x88, 0xda, 0x32, 0xa9, 0x09, 0x73, 0x25, 0xea, 0x93, 0xeb, 0x44, 0x60, 0x5a, 0xfd, 0xb3, 0x1f, 0x5f, 0xfd, - 0xc0, 0x60, 0x7b, 0xbf, 0x77, 0xf1, 0x75, 0xa6, 0x0f, 0xe7, 0xef, 0xd8, 0xbd, 0xfb, 0x3c, 0xb8, 0x71, 0x5c, 0x5d, - 0xd6, 0x23, 0x49, 0x23, 0x33, 0xc0, 0x7b, 0xca, 0xa0, 0x61, 0x2f, 0xca, 0xe6, 0xcc, 0x35, 0xbb, 0xcf, 0xba, 0xca, - 0x5d, 0x40, 0x3f, 0x22, 0x69, 0x35, 0xa1, 0xb8, 0x00, 0x6e, 0xe7, 0x31, 0xcd, 0x0a, 0xff, 0x83, 0x42, 0xcd, 0x04, - 0x7b, 0x19, 0x19, 0xa5, 0x2f, 0x23, 0x98, 0xaf, 0x16, 0x41, 0xb6, 0xac, 0x42, 0xbb, 0x8f, 0x1a, 0x6c, 0xd6, 0xae, - 0xcd, 0xb3, 0x7b, 0xcb, 0x01, 0x39, 0x13, 0x44, 0xe3, 0x45, 0xad, 0xe4, 0x59, 0x4f, 0xf5, 0xaa, 0xe7, 0x88, 0x9b, - 0xae, 0xdc, 0xab, 0x47, 0xa6, 0xf9, 0x78, 0x85, 0x77, 0x3f, 0xea, 0x22, 0xda, 0x95, 0xb5, 0x00, 0x56, 0x16, 0x43, - 0xf2, 0x3d, 0xeb, 0xef, 0x99, 0x74, 0x09, 0x16, 0x90, 0xfa, 0xc4, 0xeb, 0xda, 0x75, 0xd0, 0x91, 0x93, 0xba, 0xfd, - 0x28, 0x0a, 0x66, 0xa5, 0x45, 0x26, 0xe5, 0x89, 0xa4, 0x2b, 0xc9, 0x47, 0xae, 0x62, 0x66, 0x98, 0xe6, 0xd2, 0x19, - 0xf6, 0xa4, 0xbf, 0xaa, 0xda, 0xab, 0xed, 0x39, 0x04, 0xc0, 0x35, 0x4f, 0x21, 0x56, 0xef, 0x56, 0xd1, 0xc2, 0x9d, - 0xf1, 0x6e, 0xff, 0xa2, 0xb5, 0xe3, 0x61, 0xec, 0xd6, 0x30, 0x0e, 0x32, 0x67, 0x05, 0xf3, 0x9b, 0x1c, 0xd3, 0x70, - 0xcd, 0x68, 0xa3, 0x0b, 0x3e, 0xc5, 0xbe, 0x5c, 0xbd, 0x8f, 0xba, 0x87, 0x0a, 0x91, 0xde, 0x83, 0x67, 0x84, 0xaf, - 0x37, 0x7f, 0x6d, 0xd4, 0x6b, 0xdf, 0xd1, 0x58, 0xde, 0x58, 0x3a, 0x82, 0xc6, 0x3f, 0x26, 0x38, 0xa3, 0x30, 0xdf, - 0xc0, 0x9b, 0x26, 0x93, 0xb5, 0x09, 0xc7, 0x8e, 0xdc, 0x26, 0xc5, 0xa6, 0xa5, 0x2a, 0xdf, 0x25, 0xb0, 0x92, 0x5b, - 0xa6, 0xfd, 0xe2, 0x9e, 0x52, 0x8f, 0x0d, 0xc1, 0x42, 0xc5, 0x82, 0x00, 0x35, 0xe6, 0xaa, 0xbd, 0x99, 0x48, 0x06, - 0xa7, 0xb4, 0xfd, 0x6c, 0xfb, 0x0c, 0xb3, 0xb3, 0x26, 0x12, 0x82, 0xb3, 0x42, 0xcb, 0xa6, 0x9b, 0xb4, 0x91, 0x00, - 0x8d, 0x54, 0xa3, 0xd0, 0x64, 0x82, 0xc6, 0xce, 0x7b, 0x41, 0xee, 0x86, 0x0e, 0xa9, 0xeb, 0x0c, 0x4a, 0xf0, 0x85, - 0x23, 0x31, 0x4b, 0x77, 0x41, 0x69, 0x0d, 0xdd, 0x63, 0xa8, 0x5e, 0x6f, 0xbf, 0x0b, 0x9e, 0x91, 0xf1, 0xa6, 0x11, - 0x68, 0x8e, 0x41, 0x20, 0xdf, 0x04, 0x63, 0x02, 0x0e, 0xbc, 0xf3, 0xf2, 0xfb, 0x48, 0x44, 0xca, 0x0f, 0xb0, 0x01, - 0xbd, 0xcc, 0x37, 0x5e, 0x04, 0x2b, 0x52, 0xa5, 0x19, 0x16, 0x66, 0x8f, 0xc1, 0xbc, 0xed, 0x8e, 0x7e, 0x32, 0xfc, - 0xcc, 0x04, 0x2f, 0xf9, 0x93, 0xe7, 0xf4, 0xce, 0x10, 0x1e, 0x62, 0xf0, 0xc1, 0x58, 0xf5, 0xc0, 0xe3, 0x94, 0xc6, - 0x0d, 0x01, 0x2e, 0x9f, 0xfd, 0xc8, 0x7c, 0x10, 0xbb, 0x11, 0x80, 0x67, 0x17, 0x57, 0x19, 0x78, 0x9b, 0xa9, 0xab, - 0x93, 0x96, 0x4e, 0xee, 0x17, 0x62, 0xc4, 0x0c, 0x52, 0x31, 0x9f, 0xde, 0xc8, 0x28, 0x0d, 0xe2, 0xa2, 0x64, 0xc6, - 0x25, 0xc5, 0xae, 0x39, 0x6f, 0xe8, 0x96, 0x9f, 0x33, 0x45, 0xed, 0x9d, 0x1d, 0xeb, 0x78, 0xff, 0x8f, 0xf3, 0x27, - 0x5d, 0x30, 0xba, 0xbc, 0xb5, 0xae, 0x66, 0xdb, 0xf3, 0x4d, 0x4d, 0xa9, 0x81, 0xe1, 0x37, 0x26, 0xfc, 0xd1, 0xc7, - 0x4b, 0x4d, 0x41, 0x0d, 0x8d, 0x5d, 0x64, 0x53, 0x8a, 0xac, 0xf0, 0xc6, 0x31, 0x65, 0xbe, 0x04, 0x52, 0x2c, 0xc6, - 0x4f, 0x3e, 0x37, 0x1a, 0x5c, 0x33, 0x52, 0x1c, 0x0e, 0x09, 0xea, 0x45, 0x91, 0xb7, 0x9f, 0x3a, 0xf9, 0x1c, 0x57, - 0x6f, 0xe6, 0x37, 0x43, 0x60, 0xa6, 0xdb, 0x56, 0x5a, 0xac, 0x9b, 0xb6, 0xe2, 0xab, 0xb5, 0x4a, 0xe3, 0x29, 0x5a, - 0xe3, 0xbb, 0x1a, 0x87, 0xe9, 0x95, 0xea, 0xab, 0xe1, 0xd7, 0xbd, 0x8d, 0xcd, 0x8c, 0x66, 0x2e, 0x74, 0x90, 0x38, - 0x24, 0xbd, 0x54, 0x4d, 0xab, 0xa8, 0xc6, 0x9e, 0x1e, 0xab, 0x1a, 0x94, 0x88, 0x77, 0xe8, 0x24, 0xd6, 0x30, 0x5b, - 0x8f, 0x72, 0xfa, 0x61, 0xb5, 0x85, 0x5a, 0xb1, 0x33, 0x31, 0xa9, 0xa9, 0x37, 0x96, 0xe5, 0xd5, 0x56, 0xd5, 0xe7, - 0x74, 0xa0, 0xc3, 0x9f, 0xc3, 0x1d, 0x84, 0xef, 0x6e, 0x05, 0x9a, 0x10, 0x64, 0x2e, 0xb6, 0xf2, 0x75, 0x88, 0xef, - 0xd2, 0x1e, 0x8f, 0x25, 0x56, 0x2b, 0x12, 0x8d, 0x6f, 0xaa, 0x2a, 0x0c, 0xc8, 0x65, 0x46, 0x15, 0x4b, 0x7b, 0x65, - 0x54, 0xc8, 0x8a, 0xec, 0x8d, 0x04, 0x69, 0x55, 0xf0, 0x42, 0x90, 0xd2, 0x2c, 0x9a, 0x98, 0xcc, 0x5f, 0x0d, 0xef, - 0x92, 0x92, 0xcc, 0x26, 0xf8, 0xd3, 0x26, 0xce, 0x66, 0x14, 0x70, 0xb7, 0x52, 0x37, 0xb8, 0xd8, 0x9a, 0x71, 0x55, - 0xae, 0x20, 0xb7, 0x05, 0x07, 0x21, 0xab, 0xa2, 0x68, 0x61, 0x9f, 0xdf, 0xf9, 0xa7, 0x48, 0x53, 0x00, 0x40, 0xe4, - 0x35, 0xa1, 0x29, 0xbb, 0x69, 0x41, 0x66, 0xe9, 0x60, 0x19, 0xc0, 0x07, 0x2c, 0x85, 0xf2, 0xd0, 0x79, 0x1e, 0xd7, - 0xdd, 0xcb, 0x4c, 0x2d, 0x2a, 0x2d, 0x1b, 0x74, 0x4f, 0x07, 0x87, 0x5c, 0xaf, 0x74, 0xfa, 0xef, 0x8f, 0xd4, 0x56, - 0x5e, 0xa0, 0x0e, 0x2a, 0x7c, 0xf7, 0x51, 0xb5, 0x10, 0xe3, 0x50, 0xab, 0xff, 0xad, 0x28, 0xd1, 0x39, 0x05, 0x4f, - 0xa2, 0xd7, 0x3f, 0x56, 0xac, 0xd3, 0x2b, 0x26, 0x91, 0xf8, 0x72, 0x22, 0xa8, 0xdb, 0x66, 0x57, 0x5d, 0x72, 0xda, - 0x5c, 0x65, 0xf6, 0x9f, 0x0e, 0x78, 0xf6, 0xad, 0x77, 0x7e, 0xa7, 0x17, 0x77, 0x90, 0x44, 0xa1, 0xd0, 0xf3, 0x21, - 0x3e, 0xa0, 0xa3, 0xb2, 0xfa, 0x13, 0x91, 0x14, 0xab, 0x96, 0xcb, 0xd0, 0x80, 0x22, 0xc5, 0x4d, 0x19, 0xd5, 0x78, - 0xd0, 0xeb, 0x19, 0xbb, 0x04, 0x69, 0x74, 0xb3, 0x57, 0x2a, 0xc1, 0x49, 0x2b, 0xad, 0x3e, 0x2f, 0x41, 0x90, 0x6d, - 0xbc, 0x93, 0x5c, 0xa5, 0x58, 0x61, 0xf6, 0x58, 0x96, 0x95, 0x51, 0xd7, 0x50, 0x8c, 0xce, 0xb2, 0x8a, 0x5a, 0xe7, - 0xda, 0x6a, 0xa7, 0x08, 0xc5, 0x3a, 0x16, 0x00, 0x9b, 0x16, 0x4e, 0x07, 0x69, 0x61, 0x0b, 0x7a, 0x3a, 0xa9, 0xc6, - 0x25, 0xcd, 0x8e, 0xb1, 0xc8, 0xbb, 0x85, 0x41, 0xd0, 0x44, 0x5f, 0x77, 0x70, 0x53, 0x1e, 0x2b, 0x8a, 0x3a, 0xb8, - 0xde, 0xb1, 0x0c, 0xaf, 0x0e, 0xcd, 0x78, 0x81, 0xae, 0xa4, 0xec, 0x08, 0x95, 0x2b, 0x61, 0x27, 0x6d, 0x4a, 0x07, - 0x6d, 0x15, 0x7e, 0x68, 0x9e, 0x94, 0x8e, 0x05, 0xaf, 0x58, 0xa1, 0xc4, 0x35, 0xdd, 0x79, 0x0f, 0xeb, 0xa5, 0x9b, - 0x18, 0x23, 0xe4, 0x2b, 0xe2, 0xaf, 0x91, 0x22, 0xcc, 0x0d, 0x64, 0x0d, 0x2c, 0x64, 0x34, 0xc5, 0x24, 0x4c, 0x90, - 0xb1, 0xc7, 0x98, 0x78, 0xd1, 0x2d, 0x2e, 0xfd, 0x19, 0xb4, 0x41, 0x9b, 0x4d, 0x2b, 0xe9, 0x3e, 0x70, 0x85, 0xf6, - 0x7b, 0x3c, 0xe9, 0x90, 0x8f, 0x2d, 0x44, 0xcf, 0x14, 0x5c, 0xbe, 0x2e, 0xa1, 0x51, 0x7f, 0x00, 0x65, 0xed, 0x58, - 0x8a, 0xcd, 0x9a, 0x84, 0x1d, 0x98, 0x4e, 0x94, 0xf6, 0x7d, 0xf0, 0xa9, 0xc7, 0x5f, 0xbe, 0xde, 0x23, 0xf8, 0x0c, - 0x65, 0x4f, 0x14, 0x9b, 0x29, 0x86, 0x8a, 0xa6, 0xa6, 0xa0, 0x99, 0x0f, 0xce, 0xc2, 0x6d, 0xf1, 0x7a, 0x42, 0x2f, - 0x57, 0xbb, 0x75, 0x4f, 0xe5, 0xe3, 0x8a, 0x34, 0x40, 0xab, 0xcf, 0x1a, 0x95, 0x0f, 0xe6, 0xc9, 0x3d, 0xaf, 0x74, - 0xcf, 0x0f, 0xe8, 0xa1, 0xd1, 0xee, 0xe2, 0x4d, 0xd7, 0x32, 0x3e, 0xb4, 0x9b, 0xac, 0xcf, 0x60, 0x11, 0x7a, 0xa8, - 0xa1, 0x14, 0xcd, 0xe1, 0x26, 0xab, 0xd9, 0xf0, 0xee, 0x8d, 0x66, 0x6d, 0x29, 0x25, 0x7f, 0x6f, 0xe7, 0xc5, 0x36, - 0x9b, 0x2a, 0x9c, 0xde, 0x0f, 0xa4, 0x77, 0x09, 0x14, 0xcd, 0x91, 0xef, 0x4e, 0xa7, 0xa9, 0x7c, 0xd0, 0x4f, 0x80, - 0xa1, 0x82, 0xef, 0x1e, 0x69, 0x74, 0x67, 0x4d, 0x71, 0xbe, 0x82, 0x4b, 0x0f, 0xa3, 0xc6, 0xb6, 0x4b, 0x4f, 0x04, - 0xe1, 0xd4, 0xe2, 0x1e, 0xe9, 0x25, 0x45, 0x4b, 0x1d, 0x29, 0xe1, 0x9f, 0x22, 0x9c, 0x53, 0x8d, 0x1d, 0xed, 0x6c, - 0x1a, 0x6f, 0xa8, 0x20, 0x3d, 0x6a, 0x72, 0x4d, 0xfb, 0x12, 0x0a, 0xe0, 0xbf, 0x9d, 0xba, 0x12, 0xe1, 0x32, 0x21, - 0x37, 0xab, 0x8a, 0x4a, 0x83, 0x32, 0x00, 0x28, 0xbf, 0x5d, 0xcb, 0xe8, 0x1a, 0x3f, 0x5a, 0xa8, 0xcb, 0x12, 0x73, - 0xa0, 0x83, 0x16, 0x67, 0x77, 0x03, 0x2d, 0x92, 0x6d, 0x73, 0xea, 0xae, 0x51, 0xb5, 0xc5, 0x93, 0xc0, 0x4b, 0x44, - 0x63, 0x39, 0xeb, 0xe7, 0xf0, 0x6d, 0xda, 0x5e, 0x0f, 0xce, 0x3a, 0x40, 0xb7, 0xb0, 0xa0, 0xda, 0x9a, 0xb5, 0xfc, - 0x5e, 0x81, 0x0f, 0xf8, 0x51, 0x6c, 0xec, 0x3c, 0x76, 0x42, 0xcd, 0xc9, 0x0e, 0xbd, 0xb9, 0x49, 0x39, 0x27, 0xca, - 0x9c, 0x4e, 0x62, 0x1c, 0x85, 0x26, 0xea, 0x8c, 0x30, 0xf9, 0xd4, 0x6b, 0x32, 0x03, 0x1e, 0x7d, 0xe3, 0x22, 0x61, - 0x1f, 0x9c, 0x53, 0xc9, 0x96, 0xb5, 0x66, 0x93, 0x9b, 0x9f, 0x49, 0xb1, 0xc9, 0x98, 0x60, 0xbd, 0xa0, 0xf7, 0x37, - 0x44, 0x42, 0x18, 0x37, 0x84, 0x62, 0x68, 0x6a, 0x52, 0xe3, 0x69, 0x73, 0xa5, 0x64, 0x46, 0x97, 0x54, 0xbf, 0xac, - 0x2e, 0x28, 0x24, 0x5a, 0x51, 0xf0, 0xcb, 0x16, 0x8e, 0xbd, 0x46, 0x10, 0x7b, 0x06, 0xa0, 0x0f, 0x1d, 0xa0, 0x89, - 0x91, 0xdc, 0x6a, 0x6a, 0x4f, 0xb6, 0x04, 0x5e, 0x79, 0x19, 0xe2, 0x0d, 0xfb, 0x4d, 0x50, 0x09, 0x3c, 0xc7, 0xbe, - 0x59, 0x0e, 0x79, 0x0e, 0x97, 0xd5, 0x5d, 0x7d, 0x8a, 0xe8, 0xdc, 0xf9, 0xc2, 0xc3, 0x14, 0xbd, 0x43, 0xb5, 0x8f, - 0xd0, 0xd5, 0x2b, 0x79, 0x15, 0x73, 0x90, 0x83, 0x45, 0xdd, 0x98, 0x6c, 0x62, 0x57, 0xa7, 0x9e, 0x6b, 0x40, 0xb7, - 0xc7, 0x6e, 0x06, 0x62, 0x0f, 0x23, 0x26, 0xeb, 0x78, 0x4a, 0x6f, 0x11, 0x31, 0xda, 0x62, 0x22, 0xa9, 0x99, 0x34, - 0x6b, 0x52, 0x03, 0x39, 0x2d, 0xc2, 0x1c, 0xd4, 0x4f, 0x74, 0x8e, 0x3d, 0x12, 0xba, 0xb3, 0x6c, 0xbb, 0x46, 0x25, - 0x93, 0xdc, 0x61, 0xae, 0x50, 0x49, 0x08, 0xa9, 0x28, 0xbb, 0x92, 0x29, 0x30, 0xa5, 0x31, 0xe1, 0x1a, 0x57, 0x83, - 0x22, 0x43, 0x97, 0x0a, 0x6a, 0x6f, 0x9d, 0xa4, 0xd4, 0x39, 0x67, 0x0e, 0xf0, 0x29, 0x94, 0x3f, 0xab, 0x9a, 0x87, - 0x4d, 0xad, 0x40, 0xc5, 0xbd, 0x7a, 0xb9, 0x58, 0xf0, 0x66, 0xfe, 0x04, 0x85, 0x41, 0xa1, 0xaf, 0xa8, 0x29, 0xcf, - 0xe5, 0xa1, 0xac, 0x44, 0xdf, 0x8c, 0xbf, 0xa7, 0xf2, 0x8a, 0xc0, 0x65, 0xbe, 0x42, 0x24, 0xa6, 0xdf, 0x78, 0xa8, - 0x3f, 0x2d, 0x0b, 0x9b, 0x2a, 0x62, 0xfa, 0xf7, 0x93, 0xbf, 0x36, 0x90, 0xe0, 0x87, 0x9e, 0xd8, 0x2f, 0x5a, 0x08, - 0x3a, 0x16, 0x19, 0x54, 0x98, 0x23, 0x72, 0xa2, 0x00, 0x4e, 0xb2, 0x47, 0xd7, 0xcb, 0x4b, 0x6a, 0xe8, 0x08, 0x6e, - 0x81, 0x9c, 0xb0, 0xa2, 0x6a, 0x24, 0xcf, 0xfe, 0xde, 0x76, 0x4b, 0xaa, 0x53, 0x0e, 0x03, 0x36, 0x7f, 0xed, 0x69, - 0x19, 0xba, 0x7b, 0x1b, 0x04, 0xb0, 0x05, 0xbd, 0x1e, 0x87, 0x1f, 0x01, 0x34, 0x82, 0xc9, 0x0c, 0x19, 0xcb, 0xa7, - 0x86, 0xea, 0x55, 0x5f, 0x9e, 0x1c, 0x85, 0xb8, 0x75, 0x8a, 0x8c, 0x28, 0x5b, 0x41, 0x2e, 0x63, 0xcb, 0x6e, 0xbf, - 0x95, 0xfe, 0xc0, 0x0a, 0xaa, 0x6b, 0x15, 0x82, 0x1c, 0x8c, 0x49, 0x78, 0x23, 0x51, 0x61, 0xcd, 0xdf, 0x74, 0x06, - 0x78, 0xcb, 0x53, 0x38, 0x84, 0x7b, 0xbb, 0x03, 0x6a, 0x7d, 0x0d, 0x41, 0xa1, 0xa9, 0x1c, 0x38, 0x9b, 0xa2, 0x35, - 0x47, 0x1c, 0x9c, 0xcf, 0x61, 0xc3, 0xf2, 0x1e, 0x77, 0x33, 0xc4, 0x32, 0x88, 0x14, 0xd1, 0xe1, 0x90, 0xa5, 0xc5, - 0xa2, 0xc6, 0x16, 0xab, 0x90, 0xf6, 0x29, 0xce, 0x08, 0x37, 0x31, 0xa5, 0x38, 0xd1, 0x87, 0x97, 0x60, 0x78, 0x06, - 0xce, 0x30, 0x6b, 0xd7, 0x1e, 0x88, 0xdb, 0x1b, 0x3a, 0x5f, 0x7e, 0xc9, 0x8e, 0x32, 0x7f, 0xae, 0x3a, 0x03, 0x96, - 0xcf, 0x7e, 0xde, 0x13, 0xa0, 0xa7, 0x9f, 0x38, 0x6c, 0xab, 0x9f, 0x4a, 0xcf, 0x46, 0x6a, 0xac, 0xee, 0x99, 0x4e, - 0xce, 0xdc, 0xd6, 0x1b, 0x1e, 0xe8, 0xcc, 0x82, 0x84, 0x0f, 0x5e, 0x63, 0x1f, 0xb4, 0x91, 0x4a, 0xd2, 0x57, 0x3c, - 0x51, 0xde, 0x5b, 0xe0, 0x57, 0x0f, 0x64, 0xe5, 0x91, 0x0b, 0x12, 0xf4, 0x12, 0xda, 0xf3, 0x79, 0x74, 0xc6, 0xc1, - 0xb0, 0x37, 0xca, 0x27, 0xc6, 0x4b, 0x4c, 0xfa, 0xe6, 0xdb, 0x61, 0x88, 0x48, 0xdf, 0x47, 0x54, 0xe0, 0x84, 0x2c, - 0xa9, 0xf2, 0xf0, 0x46, 0xc2, 0x21, 0x9e, 0x4a, 0x74, 0xa6, 0x4e, 0xcd, 0x59, 0xb5, 0x58, 0x6c, 0x59, 0x79, 0xf5, - 0xda, 0x51, 0xe4, 0x77, 0x6c, 0xd5, 0x92, 0x76, 0xb2, 0xaf, 0x94, 0xa1, 0x55, 0x89, 0x33, 0x18, 0x19, 0x5d, 0xb0, - 0xd5, 0x8b, 0x24, 0x1f, 0xb2, 0x26, 0x2a, 0x1a, 0xd6, 0x95, 0xc9, 0xcc, 0x28, 0xb9, 0x95, 0xf5, 0x45, 0xda, 0xb1, - 0x5d, 0x78, 0xad, 0x42, 0x25, 0xe9, 0xa0, 0x9e, 0x93, 0xe4, 0xfc, 0x40, 0xda, 0x3a, 0xca, 0xdf, 0xb6, 0xaa, 0x93, - 0xeb, 0xa1, 0xa7, 0xec, 0x2a, 0x1d, 0x08, 0xf3, 0x55, 0xb2, 0x06, 0x81, 0x81, 0x7c, 0x77, 0x60, 0x3b, 0x06, 0x9d, - 0xbe, 0xdd, 0x1e, 0x99, 0x74, 0x3c, 0x05, 0x3a, 0x65, 0x14, 0x30, 0x4d, 0x14, 0x46, 0x57, 0x6b, 0xcc, 0xfe, 0xee, - 0x78, 0xe9, 0xbb, 0x82, 0x03, 0x55, 0xdc, 0xe9, 0x41, 0xf8, 0xe1, 0xde, 0xd5, 0xc6, 0xe0, 0x87, 0x53, 0x47, 0x4f, - 0xcc, 0xcc, 0x52, 0xcb, 0xbf, 0xaf, 0x77, 0x87, 0xdf, 0xc7, 0x29, 0xc4, 0xf0, 0x81, 0x5e, 0xc7, 0xee, 0x8b, 0xfa, - 0x57, 0xf1, 0x32, 0x97, 0xf8, 0xc2, 0x0f, 0x5d, 0xd1, 0xec, 0xa6, 0xde, 0x95, 0x35, 0xcc, 0xa1, 0x6f, 0xc5, 0xda, - 0x48, 0xe5, 0x16, 0x58, 0xf7, 0x66, 0xb9, 0x13, 0x07, 0x0c, 0x38, 0xd7, 0x73, 0xc4, 0xc4, 0x67, 0xa6, 0x5a, 0xcf, - 0x24, 0x9d, 0x9a, 0xe9, 0x48, 0x86, 0x51, 0x0b, 0x58, 0x89, 0x89, 0x50, 0xdc, 0xe1, 0x01, 0x51, 0xd9, 0x21, 0x8f, - 0x7e, 0x2c, 0xf4, 0x6d, 0x4a, 0x28, 0x1b, 0xbe, 0x82, 0x97, 0x27, 0x97, 0x3f, 0x18, 0xd9, 0x78, 0xdc, 0xfc, 0xb1, - 0xd2, 0x8b, 0x97, 0x33, 0x4f, 0xde, 0xa0, 0x88, 0xd5, 0x79, 0x82, 0xe5, 0x01, 0x12, 0x9b, 0x33, 0x37, 0xd0, 0x49, - 0x30, 0xf5, 0x46, 0x37, 0xbf, 0x14, 0xc1, 0xad, 0xb6, 0x0d, 0x4e, 0xf9, 0x99, 0xe9, 0x1e, 0x85, 0xe2, 0x27, 0x3f, - 0xc3, 0x92, 0x59, 0x36, 0x4e, 0xc0, 0xc9, 0xb2, 0x2b, 0x0f, 0x3e, 0x6d, 0x7d, 0xf1, 0x61, 0x7d, 0x61, 0xfd, 0x79, - 0x8a, 0xb1, 0xfe, 0xfc, 0x92, 0xde, 0xdc, 0x3b, 0xac, 0x83, 0xd8, 0x7a, 0x74, 0x83, 0xfe, 0x29, 0x35, 0xec, 0xfc, - 0xb1, 0x33, 0x84, 0x0a, 0x11, 0x6a, 0xbc, 0x41, 0x58, 0x70, 0xee, 0x8e, 0x94, 0xac, 0x9a, 0x31, 0x4e, 0x2b, 0x49, - 0x6b, 0xc0, 0xbe, 0xd2, 0xa4, 0xb4, 0xca, 0xed, 0x6a, 0x45, 0xcc, 0x2e, 0x4d, 0xed, 0x50, 0x60, 0xd1, 0x77, 0x52, - 0x92, 0x24, 0xe7, 0xa5, 0x67, 0xf7, 0x48, 0x6d, 0x47, 0x40, 0xe5, 0xea, 0xbe, 0x40, 0x30, 0x6e, 0x6f, 0x77, 0x07, - 0xaa, 0xa6, 0xbe, 0x83, 0x41, 0x3d, 0xf2, 0x52, 0xff, 0x1f, 0x6c, 0x44, 0x6f, 0x5e, 0xfa, 0x0e, 0x50, 0x36, 0xa2, - 0xd9, 0xbb, 0x20, 0xd7, 0x01, 0xf2, 0x8e, 0x19, 0x4e, 0x55, 0xe5, 0x30, 0xca, 0xe8, 0xaf, 0x3e, 0x4b, 0xe1, 0xd2, - 0x7b, 0x87, 0x79, 0xb2, 0x49, 0x07, 0x9e, 0x05, 0x5b, 0xba, 0xf7, 0x12, 0x49, 0x60, 0xd9, 0x21, 0x21, 0x57, 0x69, - 0xdf, 0xa0, 0xcb, 0xaa, 0x53, 0x6d, 0x52, 0xf4, 0xd3, 0x8e, 0x1b, 0x3c, 0x62, 0x5a, 0x70, 0x8b, 0x74, 0x84, 0x5a, - 0x25, 0x84, 0x31, 0xe3, 0x29, 0x92, 0xd5, 0x80, 0xf0, 0x5a, 0x4d, 0x3c, 0x25, 0x8d, 0x28, 0x0f, 0x0c, 0x13, 0xdb, - 0x7b, 0xb1, 0xf3, 0x63, 0x17, 0xc4, 0x98, 0xdd, 0xba, 0xfb, 0x55, 0xf1, 0xa1, 0x3f, 0xc3, 0xb3, 0xb6, 0x3b, 0x99, - 0x4b, 0x48, 0x90, 0x38, 0xf4, 0x2f, 0x54, 0xfc, 0x5a, 0x23, 0x3c, 0x01, 0x0d, 0x56, 0x09, 0x86, 0xa9, 0x05, 0xbe, - 0x9d, 0xde, 0x67, 0xbd, 0x5e, 0x3d, 0x61, 0xe2, 0xe8, 0x16, 0xe0, 0xda, 0x23, 0x15, 0x97, 0xd3, 0x87, 0x03, 0xbb, - 0xaf, 0x96, 0x1a, 0xe8, 0xaa, 0x64, 0xb2, 0xf8, 0x4b, 0x6f, 0x9c, 0xff, 0x4d, 0x43, 0xd1, 0x81, 0x25, 0xfa, 0x39, - 0xbd, 0x16, 0x3b, 0xf7, 0xc9, 0x27, 0x12, 0x30, 0x1a, 0xc3, 0x93, 0x9c, 0x1e, 0x58, 0x15, 0x6d, 0x26, 0xdc, 0x9f, - 0x17, 0xa7, 0x09, 0x34, 0x76, 0xa0, 0x17, 0x37, 0x99, 0x6f, 0xe3, 0xdd, 0x95, 0xad, 0xee, 0xfd, 0x61, 0x59, 0xd5, - 0xbc, 0x4d, 0xea, 0xa8, 0xbb, 0x48, 0x10, 0xe2, 0x52, 0x47, 0xa8, 0x49, 0x57, 0x6c, 0xad, 0x39, 0x73, 0x71, 0x9a, - 0xc7, 0x56, 0x7e, 0x96, 0x6d, 0x79, 0x20, 0xeb, 0x85, 0x58, 0xd5, 0x6c, 0xd4, 0x04, 0x29, 0x43, 0x5d, 0x88, 0xee, - 0x32, 0xa2, 0x82, 0x16, 0xab, 0x5f, 0xd7, 0xb1, 0x32, 0xdd, 0xa7, 0xe9, 0x68, 0x42, 0xe0, 0xf7, 0x71, 0x00, 0x46, - 0xb4, 0xfe, 0x15, 0x83, 0xa6, 0xd6, 0x28, 0x39, 0x8d, 0x4e, 0x84, 0x88, 0x43, 0xdb, 0x34, 0x06, 0xbc, 0x74, 0xf9, - 0x99, 0x1c, 0x62, 0xaa, 0x05, 0xc6, 0x1b, 0x18, 0xaf, 0x87, 0xb6, 0x98, 0x72, 0xe7, 0xe9, 0x55, 0x65, 0x94, 0xf1, - 0xc9, 0xfd, 0xcc, 0xeb, 0x9e, 0x7d, 0x40, 0xc9, 0x00, 0x84, 0x0a, 0xaf, 0x55, 0x18, 0x32, 0x58, 0x25, 0xf8, 0xf3, - 0x16, 0x63, 0xf0, 0x73, 0xdb, 0x9c, 0x87, 0xf9, 0x10, 0x2b, 0xec, 0x30, 0x0b, 0xf4, 0xe9, 0x79, 0x01, 0xd3, 0x03, - 0xd9, 0xd9, 0xb3, 0xd2, 0x96, 0x51, 0x5a, 0x22, 0x60, 0x57, 0xb8, 0x4e, 0x01, 0x2c, 0x2a, 0x81, 0xc7, 0x41, 0x94, - 0x40, 0x1b, 0xfb, 0x81, 0x68, 0xca, 0x12, 0x52, 0x00, 0xab, 0xd5, 0x9f, 0x01, 0xec, 0xa0, 0x24, 0xdb, 0xce, 0xae, - 0x09, 0x91, 0xd9, 0x7a, 0xa4, 0xbd, 0xea, 0x30, 0x2d, 0xfa, 0xe7, 0xc4, 0x59, 0x9a, 0x18, 0xfb, 0x28, 0x89, 0x8f, - 0x1a, 0x37, 0x30, 0x25, 0x12, 0x74, 0x23, 0x0f, 0xc0, 0xcd, 0x2d, 0xf7, 0xe4, 0xbc, 0xd3, 0x9b, 0x03, 0x18, 0xe4, - 0xf4, 0x4c, 0x7f, 0xed, 0xc0, 0x82, 0xfb, 0xcf, 0x25, 0xea, 0xe8, 0x69, 0x09, 0xc9, 0x04, 0x2e, 0x7e, 0x34, 0xee, - 0x99, 0x06, 0x02, 0x62, 0xcf, 0x85, 0x51, 0x0b, 0x18, 0xfc, 0xd4, 0x11, 0xaf, 0x69, 0xcf, 0x92, 0x0e, 0xa3, 0xd2, - 0xf7, 0x44, 0x3a, 0xd5, 0xb6, 0x47, 0xa6, 0x7b, 0x9d, 0xc6, 0xd4, 0x87, 0xc8, 0x07, 0x53, 0xb8, 0x52, 0x04, 0xc0, - 0xf9, 0x1f, 0x0f, 0x58, 0xee, 0xdf, 0xc4, 0x8a, 0xae, 0x90, 0xe7, 0xda, 0x98, 0x72, 0x58, 0x25, 0x03, 0x5b, 0xc6, - 0x65, 0x47, 0x4c, 0x98, 0x8d, 0x99, 0x56, 0x46, 0x6f, 0xe6, 0xc8, 0x19, 0xa4, 0xb2, 0x31, 0x5c, 0x44, 0x39, 0xb5, - 0x25, 0x20, 0x21, 0xaa, 0x02, 0x18, 0x3c, 0xbd, 0x85, 0x8d, 0x95, 0xd4, 0xa6, 0x74, 0x26, 0x18, 0xaa, 0x21, 0xca, - 0x57, 0x39, 0xb1, 0x9d, 0xca, 0xd1, 0x7c, 0xa0, 0xc9, 0xea, 0x6f, 0x9f, 0x16, 0xee, 0x63, 0x87, 0xe7, 0xbd, 0x4e, - 0x9e, 0x99, 0x14, 0xe8, 0xd3, 0x96, 0xb9, 0x73, 0xe9, 0xc4, 0x65, 0xf1, 0xd2, 0x74, 0xb1, 0x5f, 0x9c, 0xf5, 0x4d, - 0x0a, 0xb2, 0x6c, 0xed, 0xd7, 0x83, 0xb9, 0xc3, 0xb6, 0x98, 0x3a, 0x8f, 0x45, 0x80, 0xcb, 0x12, 0x51, 0xba, 0x96, - 0x09, 0x81, 0x4d, 0xcb, 0xbc, 0x30, 0x9b, 0xd1, 0xe6, 0x0a, 0x2f, 0xcf, 0x47, 0x35, 0xad, 0xc9, 0x15, 0x7a, 0xdd, - 0xa7, 0xd3, 0x77, 0x42, 0xfe, 0x79, 0x39, 0xea, 0x9e, 0x59, 0xca, 0x40, 0x54, 0xed, 0x94, 0x0e, 0x3c, 0xe9, 0xc0, - 0xce, 0xb6, 0xa6, 0x6f, 0xdf, 0x2f, 0xfe, 0xd1, 0x3e, 0x99, 0x3a, 0xb7, 0xa1, 0xb5, 0xe8, 0xf8, 0xfd, 0x1e, 0x51, - 0xbb, 0x64, 0x85, 0x23, 0x04, 0x2a, 0xcf, 0x18, 0xd8, 0xa4, 0xde, 0xcc, 0x59, 0xdc, 0xf8, 0x48, 0xb5, 0xe8, 0x16, - 0x0e, 0xf0, 0x58, 0xdf, 0xfd, 0xea, 0x4f, 0xaa, 0x6e, 0xcf, 0xff, 0xda, 0x9e, 0x84, 0x76, 0x93, 0x7f, 0x6d, 0x6c, - 0xfd, 0xc7, 0xce, 0xc8, 0x72, 0x05, 0xd1, 0xf3, 0xda, 0x7a, 0xb5, 0x24, 0x1c, 0xbc, 0xc3, 0xdc, 0x3d, 0x01, 0xdf, - 0x8e, 0xbf, 0x35, 0xcd, 0x80, 0xa4, 0x65, 0x16, 0xad, 0x8d, 0x5c, 0xe3, 0x25, 0x0c, 0x28, 0x43, 0xd9, 0x15, 0xce, - 0x54, 0x1b, 0x43, 0xf3, 0xeb, 0x1d, 0xb7, 0x6f, 0x1b, 0x6c, 0xdc, 0xa2, 0x5b, 0x45, 0x37, 0x71, 0x61, 0x75, 0x78, - 0xa6, 0xb8, 0xca, 0xe6, 0x54, 0xb9, 0xca, 0xf8, 0xbb, 0x60, 0xa8, 0x0e, 0x03, 0x6e, 0x33, 0xec, 0xc2, 0x75, 0xe7, - 0xa1, 0x0b, 0x15, 0x45, 0x30, 0x2c, 0x48, 0xa5, 0xe5, 0x04, 0x8a, 0x32, 0xb6, 0xc5, 0x86, 0xa2, 0x04, 0xff, 0xfa, - 0xf7, 0x8c, 0x97, 0x51, 0xc0, 0x37, 0x36, 0xb4, 0xc8, 0x6c, 0x85, 0xa6, 0x29, 0xe1, 0x67, 0x98, 0x92, 0xe3, 0xca, - 0x27, 0x8d, 0xdb, 0xe1, 0x7f, 0x15, 0x4d, 0x04, 0x0a, 0xa8, 0x13, 0x0b, 0x09, 0x99, 0x69, 0x33, 0x45, 0xaf, 0x30, - 0x84, 0xae, 0x48, 0xca, 0x07, 0x97, 0x39, 0xf8, 0xae, 0xcd, 0x3d, 0x77, 0x2d, 0x6f, 0x1e, 0x04, 0x5b, 0x92, 0x4d, - 0x90, 0x96, 0x14, 0x32, 0xc9, 0xc2, 0xda, 0x91, 0x81, 0xeb, 0x6b, 0x7b, 0xa1, 0xa0, 0x24, 0x59, 0x10, 0x89, 0xe7, - 0x6b, 0x6d, 0x91, 0x3a, 0x16, 0x7f, 0x61, 0xba, 0x9f, 0xe2, 0xd3, 0x5e, 0xf8, 0x81, 0xfa, 0x3a, 0xdc, 0x75, 0x5f, - 0x45, 0xb3, 0x21, 0x6e, 0xd5, 0x8f, 0x19, 0x15, 0xd7, 0x23, 0xa5, 0xfd, 0x58, 0xfe, 0xf9, 0xfb, 0x0d, 0x8b, 0xc9, - 0x00, 0xc7, 0xc3, 0x9b, 0x9b, 0xa9, 0xc3, 0x8c, 0xbd, 0x86, 0x54, 0x6d, 0xac, 0xbe, 0x01, 0xb9, 0x45, 0x0e, 0xd5, - 0xae, 0x89, 0xa2, 0x0e, 0xb8, 0x99, 0x08, 0x0e, 0xc4, 0x7d, 0x64, 0xce, 0xa8, 0xd7, 0x9e, 0x54, 0x73, 0xba, 0x94, - 0x69, 0xd1, 0x52, 0xcd, 0x18, 0x66, 0xca, 0x74, 0x54, 0x31, 0xa0, 0xae, 0xdc, 0xd9, 0x95, 0xe7, 0x7b, 0x7e, 0x2a, - 0xcb, 0x8b, 0x0d, 0x9b, 0xa1, 0x4b, 0x2d, 0xcc, 0x51, 0xbe, 0xea, 0xf3, 0xb8, 0xbf, 0xf1, 0x8a, 0xf7, 0x7a, 0x87, - 0xa1, 0x43, 0x2d, 0x3d, 0x66, 0x6f, 0xd6, 0xfa, 0x7a, 0x3e, 0xe3, 0x20, 0x2d, 0x6a, 0xa7, 0x82, 0x71, 0xce, 0xa2, - 0x80, 0x05, 0x78, 0x85, 0xdd, 0x11, 0x8c, 0x8f, 0x67, 0xf1, 0x88, 0x7e, 0x64, 0xec, 0xe6, 0x6d, 0xf3, 0x1a, 0x10, - 0xa4, 0xca, 0x8e, 0x7b, 0x4b, 0x17, 0x2a, 0x16, 0xd1, 0xd2, 0x3b, 0xd3, 0x29, 0x94, 0x8f, 0x15, 0x00, 0xa4, 0xee, - 0xf4, 0x66, 0x30, 0x1e, 0xca, 0xcd, 0x01, 0xc2, 0x8d, 0x9c, 0x19, 0x37, 0x26, 0x0a, 0x47, 0x37, 0x06, 0x84, 0x08, - 0x71, 0x35, 0xf0, 0x95, 0x97, 0xb4, 0x4f, 0x54, 0x2c, 0x0d, 0xf1, 0xbd, 0xf2, 0xe0, 0x3e, 0xdc, 0xda, 0x6f, 0x2f, - 0x4e, 0x55, 0xc9, 0xc1, 0x22, 0x14, 0x1b, 0xc5, 0x7b, 0xe3, 0x77, 0x2f, 0xec, 0x7e, 0x62, 0x31, 0xd7, 0x12, 0x01, - 0xe5, 0x96, 0xef, 0x97, 0x56, 0x4d, 0x9c, 0x3f, 0xfd, 0x87, 0x0f, 0xe5, 0x12, 0x72, 0xe4, 0xab, 0x58, 0x76, 0x40, - 0x66, 0xbe, 0xa2, 0x9f, 0x45, 0x59, 0x4d, 0xe1, 0x53, 0xde, 0xc2, 0xdd, 0x75, 0xc7, 0xb5, 0x0e, 0x00, 0x59, 0x38, - 0x44, 0xb3, 0xd1, 0xd3, 0x2e, 0x89, 0xd0, 0x36, 0xda, 0xf8, 0x96, 0x47, 0x9a, 0x51, 0x51, 0x51, 0x34, 0x2e, 0xcd, - 0x46, 0x3d, 0xa4, 0x20, 0x9e, 0xa0, 0x17, 0xdf, 0x86, 0x80, 0x7c, 0x74, 0xed, 0x9d, 0x12, 0xb3, 0x74, 0x6b, 0xe1, - 0xfb, 0xfe, 0x46, 0xa2, 0x9e, 0x02, 0xfd, 0x28, 0xc2, 0x64, 0x41, 0x33, 0xf2, 0x95, 0xff, 0x2e, 0x00, 0x6f, 0x62, - 0xd1, 0x3f, 0xb1, 0xf0, 0x67, 0xb2, 0xee, 0xe1, 0xab, 0x9d, 0xb8, 0xde, 0x2e, 0x9a, 0xd3, 0x41, 0xfb, 0x10, 0x94, - 0xaa, 0xbe, 0xc7, 0xe1, 0x4d, 0xa1, 0xf5, 0xcb, 0x8e, 0x5d, 0xb0, 0x15, 0x05, 0x03, 0x9e, 0x75, 0x4a, 0x34, 0x31, - 0x5d, 0x97, 0x15, 0x01, 0xc6, 0x12, 0x27, 0x90, 0x5b, 0x9d, 0xb3, 0x74, 0x94, 0x9b, 0xb3, 0x9b, 0x3c, 0x6b, 0x27, - 0xd7, 0xd1, 0xbe, 0x9d, 0xcc, 0x4a, 0x59, 0xe5, 0xba, 0x21, 0x34, 0x7b, 0xf9, 0xe4, 0x2c, 0x94, 0x8b, 0x42, 0x1d, - 0x15, 0x37, 0xb4, 0x6a, 0x4d, 0x56, 0xc0, 0xca, 0xc9, 0x45, 0xab, 0xf2, 0xe6, 0xe9, 0xd0, 0xb8, 0xc9, 0x36, 0xfd, - 0x58, 0xd1, 0x76, 0x07, 0x0a, 0xaf, 0x14, 0xd6, 0xf6, 0x1c, 0x6c, 0xc3, 0x89, 0x06, 0xc7, 0x7d, 0xbb, 0x6d, 0x40, - 0x54, 0x20, 0xbb, 0x98, 0x50, 0x62, 0x6b, 0xf9, 0x2f, 0x0e, 0x28, 0xbe, 0xbd, 0x9a, 0x5e, 0x47, 0x31, 0x32, 0x7c, - 0x53, 0xff, 0x1e, 0x08, 0xf0, 0x2f, 0x7c, 0x60, 0x6a, 0x67, 0x54, 0x45, 0x28, 0x6b, 0x37, 0xab, 0xd4, 0x1f, 0x15, - 0xb9, 0xa5, 0x49, 0x6a, 0xb6, 0x79, 0x7a, 0x82, 0x29, 0x2b, 0xda, 0x9b, 0xc3, 0x06, 0x7b, 0x74, 0x6d, 0x44, 0xd8, - 0x60, 0x42, 0x1c, 0xff, 0x83, 0x9d, 0x00, 0x9d, 0x48, 0xf1, 0x82, 0xcb, 0x71, 0x65, 0x29, 0x9a, 0x12, 0xcd, 0x4b, - 0x51, 0xfb, 0x14, 0xe6, 0x3d, 0xaf, 0x02, 0xae, 0x9b, 0x93, 0xa3, 0x6e, 0x87, 0x3e, 0x71, 0x8a, 0x38, 0xcf, 0x81, - 0x08, 0x7b, 0x2a, 0xa7, 0x5d, 0xbd, 0x59, 0x5b, 0x86, 0xb8, 0x6e, 0x56, 0x88, 0x90, 0x7c, 0x18, 0xa7, 0x62, 0x88, - 0xb1, 0x7d, 0x23, 0x73, 0xde, 0xe3, 0xab, 0x85, 0x28, 0xac, 0x70, 0x11, 0x8f, 0x91, 0xa5, 0x3f, 0x79, 0x05, 0xd1, - 0x9d, 0x51, 0x93, 0x60, 0xd6, 0xea, 0x64, 0xa4, 0x38, 0x53, 0xff, 0x02, 0x02, 0x43, 0x6d, 0x90, 0xd1, 0x41, 0x4d, - 0x95, 0xf9, 0xd1, 0xd3, 0x88, 0x1b, 0x1f, 0x38, 0xaa, 0x46, 0x9b, 0x90, 0x71, 0xa6, 0xd8, 0x16, 0xbf, 0x35, 0x77, - 0x4b, 0x00, 0x66, 0x77, 0x1d, 0xa8, 0x15, 0x71, 0x24, 0xa1, 0x55, 0x7f, 0xae, 0xfe, 0x7a, 0x89, 0x44, 0x71, 0x4e, - 0xe8, 0x63, 0xa0, 0xc5, 0x47, 0x98, 0xae, 0xe6, 0x62, 0x1b, 0x87, 0x1d, 0x47, 0xa2, 0x8a, 0xd3, 0xbb, 0xe8, 0x72, - 0x3f, 0x93, 0x60, 0xf7, 0x13, 0x22, 0x9e, 0xef, 0xad, 0x0b, 0x35, 0x0b, 0x47, 0x1f, 0xb6, 0x3f, 0x09, 0x12, 0x32, - 0xd4, 0x5f, 0x0b, 0x37, 0x47, 0xed, 0xd5, 0x4b, 0x2d, 0xa3, 0x0d, 0x3f, 0x2d, 0xa2, 0xc5, 0xa9, 0x00, 0x94, 0x0c, - 0xa3, 0xf3, 0xcf, 0x5f, 0xec, 0xb0, 0x9f, 0x83, 0x73, 0x0c, 0x26, 0x0f, 0x78, 0xc0, 0xcd, 0xdd, 0x4f, 0xe8, 0xda, - 0x52, 0xce, 0x08, 0x67, 0xac, 0x0d, 0x09, 0x56, 0xc6, 0xb9, 0x66, 0x6b, 0xe3, 0x45, 0xc3, 0x09, 0xe1, 0x08, 0x3a, - 0x68, 0x8c, 0x7a, 0x9e, 0x33, 0x9a, 0xa7, 0x58, 0xfd, 0xca, 0x19, 0x6f, 0xf9, 0x81, 0x9d, 0xcb, 0x15, 0x04, 0x55, - 0x17, 0x55, 0x62, 0x4d, 0x27, 0xda, 0x81, 0xcb, 0xfd, 0x25, 0x7b, 0xca, 0x22, 0x7f, 0xbb, 0xc0, 0xa4, 0xa6, 0x42, - 0x21, 0x67, 0x85, 0x1b, 0xda, 0x15, 0x82, 0xd5, 0x6a, 0xdc, 0xf0, 0x3b, 0x6f, 0x59, 0x96, 0xaa, 0x4e, 0xed, 0x46, - 0x35, 0x34, 0xc3, 0x84, 0x29, 0x6e, 0x68, 0x19, 0xdf, 0x91, 0x94, 0xd8, 0x59, 0xd7, 0x06, 0x73, 0xfa, 0x1f, 0x52, - 0x9c, 0x0a, 0x2d, 0x44, 0xa9, 0xfa, 0x1c, 0x34, 0x3a, 0x31, 0x4d, 0xd5, 0x79, 0x23, 0x77, 0x26, 0x07, 0x83, 0x9a, - 0xb2, 0xb1, 0x53, 0xf3, 0x8e, 0xe9, 0xc8, 0x0c, 0xfe, 0x8e, 0xfc, 0xe4, 0x21, 0xab, 0x65, 0x72, 0x99, 0xe8, 0x67, - 0xbd, 0xf1, 0xaa, 0x00, 0x14, 0xd6, 0x31, 0xa8, 0xd0, 0x3c, 0x6b, 0x2a, 0x6b, 0x55, 0x65, 0xba, 0x09, 0x5f, 0x75, - 0x4b, 0x03, 0x05, 0xcf, 0x94, 0xa7, 0x10, 0x51, 0x53, 0xe2, 0xa4, 0xd5, 0x43, 0xa8, 0x01, 0xe5, 0xe8, 0xbc, 0x88, - 0xb9, 0x8e, 0x95, 0x2a, 0x1b, 0xff, 0x72, 0xef, 0xa3, 0x75, 0xeb, 0x20, 0xef, 0x67, 0x36, 0xea, 0xfd, 0x22, 0x55, - 0x4e, 0xa1, 0xcf, 0x8f, 0x34, 0x8d, 0x35, 0x09, 0xb6, 0x71, 0x32, 0x90, 0x0a, 0x3a, 0xa9, 0xc0, 0xff, 0xcb, 0x94, - 0x33, 0x56, 0x4c, 0x2a, 0x40, 0xc5, 0x62, 0xed, 0x9a, 0x7f, 0xdd, 0x87, 0x05, 0x93, 0xa0, 0xee, 0x1f, 0x80, 0x5e, - 0x0b, 0xb9, 0x90, 0x5f, 0xad, 0xb7, 0xa1, 0xec, 0x7c, 0xc3, 0x49, 0xeb, 0x73, 0xf5, 0x93, 0x23, 0x17, 0xb1, 0x9a, - 0xa2, 0xcd, 0xb4, 0x9e, 0x3a, 0x4b, 0x98, 0xf0, 0xd3, 0x72, 0x6e, 0xba, 0x41, 0xe6, 0x1a, 0x47, 0xca, 0xcb, 0xec, - 0xe3, 0xa8, 0x55, 0x66, 0xe9, 0xd8, 0x86, 0x2a, 0x6a, 0x73, 0x3c, 0x73, 0xc6, 0xf8, 0x62, 0x4f, 0x9a, 0x6a, 0x57, - 0x56, 0xf2, 0xcd, 0xb5, 0x98, 0x37, 0x87, 0x32, 0x45, 0x3d, 0x32, 0xad, 0x93, 0x8b, 0x13, 0x2a, 0xb3, 0x02, 0xc0, - 0xdb, 0x90, 0x6c, 0x84, 0xc0, 0x2e, 0xd8, 0x8f, 0xb5, 0x78, 0xe9, 0x2e, 0xa5, 0x51, 0x82, 0x97, 0x10, 0x2b, 0xfa, - 0x87, 0xd2, 0x42, 0x83, 0x54, 0x57, 0x94, 0x2c, 0x0d, 0xf5, 0xdf, 0x4a, 0x0f, 0x27, 0x39, 0x6a, 0x70, 0x0e, 0xb5, - 0xc7, 0xc2, 0xa8, 0xf7, 0x63, 0xd2, 0xa3, 0x3c, 0xd6, 0x4b, 0x81, 0x4d, 0x96, 0xc0, 0xca, 0x09, 0x76, 0x17, 0x20, - 0xe5, 0xb5, 0x87, 0xbe, 0x56, 0x64, 0xc2, 0xe3, 0xf3, 0xe4, 0xd6, 0xa5, 0x65, 0xe0, 0x15, 0xf4, 0xae, 0xbd, 0xa1, - 0xd2, 0x02, 0x77, 0xbf, 0xc8, 0x95, 0xff, 0xe8, 0x50, 0x24, 0x1d, 0xf1, 0x54, 0x12, 0x78, 0x2b, 0xa9, 0xc1, 0xc0, - 0xad, 0x65, 0xc3, 0xb5, 0x69, 0x1b, 0x7d, 0xa8, 0x8f, 0xe3, 0x3b, 0x46, 0xab, 0xe0, 0x3f, 0x9f, 0x7e, 0xc3, 0x38, - 0xb4, 0xe0, 0xd9, 0xaa, 0x54, 0x59, 0xd7, 0x53, 0x47, 0xb2, 0xfd, 0xd5, 0xce, 0x5b, 0x04, 0xb3, 0x70, 0x25, 0x0b, - 0x4d, 0x02, 0x3a, 0xb6, 0x49, 0x16, 0xb8, 0x4d, 0x81, 0x99, 0x47, 0x3f, 0x45, 0x6f, 0x23, 0xd5, 0x38, 0x52, 0xb5, - 0x68, 0x12, 0xe3, 0x70, 0x41, 0x34, 0x79, 0x73, 0xb7, 0x2a, 0x02, 0x19, 0x1c, 0xc0, 0x2d, 0xbf, 0x33, 0xce, 0x3d, - 0xf5, 0x91, 0xd6, 0x3a, 0xf0, 0xbb, 0x6e, 0xb2, 0x5d, 0xda, 0xa1, 0x51, 0x4b, 0xf4, 0xb6, 0x1d, 0x35, 0x1a, 0x64, - 0xd8, 0x23, 0xc5, 0xd8, 0xbd, 0x8f, 0xcf, 0xea, 0x31, 0x83, 0x2c, 0xd1, 0x01, 0x5f, 0x77, 0x0d, 0x54, 0x2c, 0x32, - 0x90, 0xbb, 0x0b, 0x21, 0x51, 0x87, 0x6d, 0xb4, 0x00, 0x50, 0xfa, 0x04, 0xab, 0xef, 0xc4, 0x2d, 0xf5, 0x06, 0x94, - 0xf9, 0x3e, 0xa4, 0x94, 0x42, 0x7d, 0x51, 0x91, 0x29, 0x67, 0x8b, 0xc5, 0x8c, 0x22, 0x8c, 0x3c, 0x11, 0x19, 0x6a, - 0x13, 0xc4, 0x08, 0x9c, 0xde, 0x32, 0xaa, 0x7e, 0x6c, 0x2f, 0x03, 0x2d, 0xed, 0xb5, 0x88, 0xa9, 0xca, 0x19, 0xcf, - 0x01, 0x94, 0x80, 0xc1, 0x55, 0x00, 0x67, 0xa6, 0x7a, 0x57, 0xc6, 0x9c, 0x58, 0x66, 0x05, 0x0f, 0x94, 0x4e, 0x2e, - 0xc6, 0xd7, 0xc0, 0xf9, 0x8f, 0xad, 0x89, 0xab, 0xf8, 0xeb, 0xd7, 0x2d, 0x9f, 0x67, 0xff, 0x97, 0x89, 0x76, 0x75, - 0x06, 0xac, 0x9c, 0xb0, 0xcf, 0x13, 0xc4, 0xeb, 0x06, 0xdb, 0xcb, 0xd6, 0x62, 0xc5, 0x93, 0x5e, 0x7f, 0x6c, 0xb5, - 0xa4, 0x2c, 0xab, 0xe4, 0x57, 0x1b, 0x08, 0xa4, 0xf1, 0x9d, 0x49, 0x64, 0x90, 0x0a, 0x92, 0x62, 0xba, 0x11, 0xfc, - 0xee, 0x5b, 0xef, 0x61, 0x47, 0x1a, 0x78, 0xd9, 0xea, 0xc2, 0xf0, 0x99, 0xba, 0x5d, 0xd3, 0x49, 0xce, 0xe0, 0xcc, - 0x9b, 0x09, 0x47, 0x5b, 0xef, 0xf2, 0xe5, 0x0a, 0x2d, 0xfa, 0x3c, 0xf4, 0x2b, 0xba, 0x4d, 0x5a, 0x96, 0xc7, 0x3d, - 0xcc, 0xa0, 0xfe, 0xaf, 0x62, 0xcd, 0x69, 0xf4, 0x55, 0x51, 0x5f, 0x7a, 0x41, 0x2b, 0xcd, 0x6d, 0xad, 0x2d, 0xe4, - 0x74, 0x6e, 0x91, 0x7b, 0x30, 0x34, 0xed, 0xfb, 0x8f, 0x2a, 0xc2, 0x92, 0x3d, 0xa5, 0xad, 0xf7, 0xc9, 0x45, 0x2f, - 0xd5, 0xb9, 0x11, 0xff, 0x96, 0x53, 0x79, 0xd3, 0x3a, 0x6a, 0x64, 0x27, 0xfe, 0x0f, 0xd6, 0x4d, 0x94, 0x71, 0xb9, - 0x4e, 0xee, 0xb4, 0x83, 0xe2, 0xa8, 0x4b, 0x8e, 0x87, 0x38, 0xd7, 0x8c, 0x46, 0x7a, 0x25, 0xcc, 0x33, 0xa7, 0x55, - 0x85, 0x1e, 0x8b, 0x06, 0xc9, 0x1a, 0x1a, 0x90, 0x04, 0xa8, 0xc9, 0x09, 0x71, 0xea, 0x4e, 0x70, 0x6b, 0x40, 0x72, - 0x72, 0x89, 0x90, 0x9c, 0x16, 0xde, 0xe5, 0xe7, 0x0d, 0x19, 0xa2, 0x9c, 0xd7, 0x37, 0xb1, 0x23, 0x2a, 0x3e, 0x8b, - 0x6e, 0xb9, 0x6f, 0x11, 0x1a, 0x6d, 0x1f, 0x34, 0x9a, 0x8e, 0x39, 0xb0, 0xcb, 0x9b, 0x35, 0x68, 0x39, 0x33, 0x08, - 0xf9, 0xe9, 0x19, 0x34, 0x61, 0xc0, 0x6c, 0x85, 0x10, 0x73, 0x94, 0x6c, 0x95, 0x9a, 0x34, 0x06, 0xf5, 0xc4, 0x4e, - 0x1c, 0xa8, 0xcf, 0xcf, 0xba, 0x59, 0x29, 0xd9, 0x9c, 0x9a, 0xda, 0xf4, 0x03, 0xd8, 0xe2, 0x89, 0x36, 0x1f, 0x28, - 0xc3, 0x20, 0x0d, 0x57, 0x25, 0xc2, 0xdf, 0xa8, 0xa8, 0xf3, 0x65, 0x3e, 0x6f, 0xd8, 0x46, 0xd0, 0x88, 0x21, 0x03, - 0xb3, 0x13, 0xac, 0x81, 0x20, 0x58, 0x16, 0x67, 0x72, 0x96, 0xd2, 0xd9, 0x38, 0x96, 0x58, 0x0b, 0x05, 0xb4, 0xbc, - 0x4d, 0xce, 0x1d, 0x04, 0x50, 0x46, 0xa2, 0xc4, 0xb2, 0x8d, 0x88, 0x3e, 0x30, 0x09, 0xde, 0x10, 0x2b, 0xf8, 0x05, - 0xdf, 0x50, 0x3a, 0xe9, 0x40, 0x6d, 0x92, 0x3b, 0x85, 0xaa, 0x0c, 0x0e, 0xc6, 0xe1, 0x15, 0xff, 0xed, 0xca, 0x0f, - 0x0e, 0x6d, 0x22, 0xee, 0x2a, 0xe0, 0x92, 0xd1, 0x73, 0x08, 0xea, 0xe4, 0xda, 0xb2, 0x89, 0xef, 0x34, 0xda, 0xde, - 0x55, 0xb5, 0x2b, 0x8e, 0xf8, 0xfc, 0x51, 0x60, 0x14, 0xa4, 0x5c, 0xce, 0xfe, 0x8d, 0x27, 0x69, 0xa2, 0x39, 0xb7, - 0xef, 0x1d, 0x2e, 0x16, 0x69, 0xa6, 0x3a, 0x35, 0xbd, 0xb9, 0x58, 0xb5, 0x0f, 0x46, 0xae, 0xb5, 0x67, 0x67, 0x1c, - 0x47, 0x20, 0x05, 0xe5, 0x03, 0xfe, 0x0b, 0xa9, 0x1a, 0xf2, 0xf9, 0xd0, 0xcf, 0x01, 0xd5, 0x4c, 0xf1, 0x69, 0xd5, - 0xd6, 0xbe, 0x49, 0xb5, 0xe0, 0x7f, 0x73, 0x58, 0xa4, 0x75, 0xfd, 0xfd, 0xf9, 0x8b, 0xde, 0x36, 0xd2, 0xf1, 0x23, - 0xb1, 0x92, 0xfa, 0x13, 0xa0, 0xac, 0xbd, 0x19, 0xb3, 0x76, 0x10, 0x4e, 0x63, 0x4a, 0x21, 0xfa, 0x4f, 0xe2, 0x53, - 0x4f, 0x65, 0xf0, 0x0d, 0xd4, 0x0f, 0xde, 0xc4, 0x68, 0xe9, 0x67, 0x6d, 0x6a, 0x21, 0xfc, 0x2d, 0xe6, 0x8b, 0x5a, - 0x3d, 0xe8, 0x38, 0x0f, 0xb9, 0x78, 0xc5, 0xea, 0x3f, 0x7d, 0xf1, 0xe5, 0x6c, 0x61, 0xb0, 0x96, 0xb5, 0x07, 0xff, - 0x8f, 0xf3, 0x00, 0x20, 0x5b, 0x61, 0x58, 0x4b, 0x34, 0xd3, 0x2f, 0xad, 0xa7, 0x00, 0xdf, 0x9d, 0xa7, 0x52, 0x6c, - 0x4d, 0x8b, 0x95, 0xa9, 0x67, 0x3a, 0xa8, 0x57, 0x6a, 0x6b, 0xdc, 0x37, 0xd6, 0x87, 0xd1, 0xd0, 0x77, 0x30, 0x57, - 0xbc, 0x7e, 0x8c, 0xe9, 0xee, 0x9f, 0x26, 0x26, 0x86, 0xfd, 0x4e, 0x75, 0x4a, 0x9a, 0xa5, 0xbe, 0x6d, 0xc8, 0x99, - 0xdd, 0x26, 0x70, 0xdf, 0x64, 0x8a, 0x90, 0xe3, 0x7d, 0x72, 0x94, 0xa2, 0xa6, 0x7d, 0x4b, 0x25, 0xab, 0x5b, 0x0f, - 0x69, 0xc4, 0x2c, 0x35, 0xd0, 0xfb, 0xe2, 0x55, 0x81, 0x81, 0x87, 0xea, 0xbc, 0x7e, 0x8b, 0x02, 0x9e, 0xc1, 0x47, - 0xcb, 0xac, 0xda, 0xba, 0x04, 0x8e, 0x51, 0xeb, 0xc0, 0xfd, 0xf2, 0xc0, 0x1f, 0x29, 0xfa, 0xe2, 0x6d, 0xe4, 0x60, - 0x83, 0xd7, 0x53, 0x83, 0x53, 0x1e, 0x9e, 0x8d, 0xf5, 0x31, 0xe3, 0xa7, 0x95, 0xa3, 0xb0, 0x67, 0x5c, 0x3c, 0x99, - 0x5d, 0x8c, 0xc3, 0xa6, 0xdb, 0x2a, 0x27, 0x4a, 0xa6, 0x4c, 0xd7, 0x64, 0x7e, 0xc6, 0x85, 0x9e, 0x37, 0x6b, 0xb5, - 0x84, 0xcd, 0x0f, 0xfe, 0x70, 0x53, 0x5c, 0x19, 0x27, 0xa3, 0xd0, 0xfe, 0x1f, 0xd9, 0x99, 0xa6, 0x77, 0xa1, 0x46, - 0xe0, 0x52, 0x70, 0xb5, 0x54, 0x96, 0x46, 0xda, 0xcf, 0xf6, 0xe9, 0xfb, 0x24, 0x5f, 0x41, 0x9e, 0xfe, 0x92, 0x15, - 0x1b, 0x73, 0x92, 0x64, 0xff, 0xa8, 0x14, 0x32, 0x87, 0xaa, 0x45, 0x3b, 0x46, 0x5b, 0xf9, 0x09, 0x41, 0x7d, 0xd9, - 0x21, 0xea, 0x00, 0xcc, 0xb6, 0x4a, 0x79, 0xbf, 0x18, 0x68, 0x46, 0x51, 0xb6, 0x1c, 0xf4, 0xb5, 0x61, 0x06, 0x07, - 0xaf, 0x1a, 0xd6, 0xef, 0xbd, 0xac, 0x55, 0x32, 0x52, 0x69, 0xb3, 0xcc, 0x51, 0x6a, 0xf2, 0x74, 0xbf, 0xd4, 0xb9, - 0xe8, 0x9a, 0x38, 0xf8, 0xd9, 0xda, 0xf7, 0x60, 0xd7, 0x4e, 0xcb, 0xae, 0x14, 0xe6, 0x06, 0xc7, 0x79, 0xcc, 0x71, - 0x65, 0x03, 0x11, 0x6b, 0x16, 0x5a, 0xde, 0x14, 0x2d, 0x52, 0x77, 0xea, 0xbb, 0xb3, 0xec, 0x26, 0x80, 0xad, 0x62, - 0xef, 0xa1, 0xe5, 0xdb, 0x67, 0xe9, 0x8d, 0x0e, 0x6c, 0x6b, 0xe3, 0x5e, 0xc7, 0x37, 0x16, 0x84, 0x9e, 0x2c, 0xaf, - 0xce, 0xa8, 0x8e, 0x3b, 0xa7, 0xf9, 0xfc, 0x50, 0x31, 0x96, 0x6e, 0x93, 0xe8, 0x9c, 0x8f, 0xe4, 0x09, 0xb2, 0x0c, - 0x15, 0xcb, 0x69, 0x60, 0x2d, 0x23, 0x68, 0xec, 0x24, 0x7d, 0xe5, 0x91, 0xac, 0xc6, 0x8a, 0xf9, 0x47, 0xa0, 0x76, - 0xae, 0xec, 0xb8, 0x6d, 0x86, 0xa4, 0x5a, 0xae, 0xb4, 0x46, 0x30, 0x0c, 0x8d, 0x7f, 0x2d, 0x44, 0xa2, 0xda, 0x4a, - 0x40, 0x02, 0x0e, 0x67, 0x29, 0xa8, 0xdd, 0x6d, 0x79, 0xf3, 0x6e, 0x94, 0x1e, 0x51, 0xa4, 0xa2, 0x56, 0x54, 0x4e, - 0xf1, 0x86, 0xb2, 0xf5, 0x4c, 0x34, 0x01, 0x13, 0x8d, 0x62, 0x23, 0x33, 0x28, 0x6f, 0xb7, 0x2a, 0xe4, 0x5e, 0xae, - 0xfb, 0xb7, 0x57, 0xef, 0x28, 0x0d, 0x9b, 0xbe, 0x12, 0x92, 0x06, 0xad, 0x50, 0x44, 0x7c, 0xc0, 0x8e, 0x31, 0x8e, - 0xae, 0xc9, 0xf4, 0x99, 0x3a, 0x30, 0x46, 0x75, 0x89, 0x94, 0x2f, 0xcd, 0x9f, 0xbd, 0xf1, 0xea, 0x25, 0xb0, 0xf5, - 0x3b, 0x5d, 0x6b, 0x4d, 0x66, 0xde, 0x96, 0x52, 0x2b, 0x91, 0x6e, 0x32, 0x22, 0x8d, 0xff, 0x4c, 0xb3, 0x6f, 0x26, - 0xf2, 0x87, 0x1d, 0xed, 0xc0, 0x40, 0x86, 0xf4, 0x66, 0xb3, 0x39, 0xa7, 0x6a, 0x16, 0x00, 0x0a, 0xff, 0xd5, 0xba, - 0x0f, 0x66, 0x6b, 0xa6, 0xa9, 0x88, 0xe0, 0xb3, 0x30, 0x34, 0x6f, 0xe1, 0x90, 0xd5, 0x69, 0x04, 0xe0, 0x20, 0x09, - 0x81, 0xcc, 0xd9, 0x5c, 0x6f, 0x08, 0xaa, 0xd8, 0xdb, 0xb0, 0x46, 0x9f, 0x42, 0xe8, 0x7f, 0xe4, 0xd3, 0xcf, 0xf9, - 0x5e, 0x45, 0x51, 0x0c, 0x5d, 0x1d, 0x0a, 0x87, 0xd6, 0xdf, 0x64, 0xd2, 0x78, 0x97, 0x2c, 0x14, 0x83, 0xfa, 0x8b, - 0xbd, 0x43, 0xcb, 0xdc, 0x74, 0x67, 0x03, 0x0b, 0x97, 0x0a, 0x06, 0x52, 0x2c, 0x42, 0x48, 0x73, 0x83, 0xb3, 0x7e, - 0xeb, 0xb1, 0x7c, 0xe9, 0x02, 0x4d, 0xdf, 0xca, 0xe3, 0x31, 0x3e, 0xfb, 0x76, 0xbc, 0xe3, 0x13, 0x66, 0x5a, 0x66, - 0x89, 0x4a, 0x0a, 0xe9, 0x93, 0xff, 0x0e, 0xa3, 0x96, 0xc7, 0x84, 0x05, 0xd3, 0xea, 0xee, 0xa9, 0x14, 0xc5, 0xce, - 0x73, 0x58, 0x53, 0x2f, 0xa0, 0x0e, 0x85, 0x9b, 0xea, 0x03, 0xbb, 0x12, 0x41, 0x6a, 0x53, 0x00, 0x30, 0xfe, 0x08, - 0x80, 0x88, 0x07, 0x99, 0x57, 0xaa, 0x25, 0x64, 0xb8, 0x59, 0x4e, 0xa4, 0xbb, 0x8b, 0x51, 0xe2, 0x9b, 0x23, 0x02, - 0xb4, 0xa5, 0x66, 0x18, 0x9e, 0xc9, 0x6f, 0x73, 0x79, 0x13, 0x2e, 0x81, 0xed, 0x1a, 0xc1, 0x1b, 0x21, 0x6d, 0xd6, - 0x7e, 0x38, 0x02, 0xaa, 0xb6, 0x01, 0x51, 0xfa, 0x4d, 0x79, 0x63, 0xde, 0x88, 0x14, 0xaa, 0xd5, 0xce, 0xee, 0x4d, - 0x5a, 0xa7, 0x0d, 0xab, 0xe1, 0x29, 0xdc, 0x54, 0xa9, 0x6d, 0x23, 0xd7, 0xf6, 0x7f, 0x92, 0x82, 0x9c, 0x4d, 0xdd, - 0xd5, 0x6d, 0xf7, 0xfb, 0xa7, 0x09, 0x38, 0xfc, 0x24, 0x31, 0xbe, 0xfb, 0xd5, 0x32, 0xfb, 0x3f, 0xb6, 0xf2, 0xa0, - 0x04, 0x0f, 0xa7, 0x20, 0x9f, 0x62, 0x0d, 0xd7, 0x90, 0x7a, 0xf2, 0xae, 0xaf, 0xbb, 0x80, 0xc0, 0xfa, 0x2d, 0xb9, - 0x13, 0xef, 0x32, 0x82, 0x53, 0x00, 0xdb, 0xd6, 0x11, 0x58, 0xeb, 0xe6, 0x3b, 0x90, 0x82, 0x18, 0xf9, 0x2d, 0x92, - 0xff, 0xb3, 0x32, 0x37, 0xfc, 0x48, 0x51, 0xdc, 0x9c, 0x4b, 0x17, 0xd1, 0x93, 0x55, 0xd8, 0x0e, 0x1b, 0x55, 0x80, - 0x23, 0xb0, 0xf0, 0x7e, 0x6e, 0x26, 0xff, 0x0c, 0xa1, 0x9d, 0xab, 0x33, 0xc5, 0xa1, 0x18, 0xd5, 0x4f, 0x75, 0x01, - 0xca, 0xc3, 0x64, 0xc4, 0xa6, 0x26, 0xb4, 0x18, 0x0b, 0x4b, 0x97, 0x24, 0x80, 0x40, 0x7b, 0xa8, 0x25, 0x32, 0x97, - 0x6b, 0x91, 0x5d, 0x32, 0xee, 0xd9, 0x56, 0x2c, 0x5d, 0xfb, 0x98, 0xd7, 0xd9, 0x33, 0x70, 0xe3, 0x3c, 0x06, 0x5f, - 0xdc, 0xd9, 0x52, 0x58, 0xe9, 0x19, 0xb2, 0x3a, 0x3b, 0x57, 0xe2, 0xb0, 0x4d, 0xb6, 0x1f, 0x15, 0xec, 0xee, 0xdb, - 0x5b, 0x22, 0x0b, 0xc4, 0xe0, 0x3f, 0xad, 0x35, 0x59, 0xeb, 0x6f, 0xe4, 0x00, 0xbe, 0x85, 0x95, 0x7c, 0x41, 0x33, - 0xe0, 0x72, 0x77, 0x73, 0x40, 0xea, 0x81, 0x4f, 0x26, 0xac, 0xaa, 0x72, 0xcd, 0xcd, 0x46, 0xa6, 0x09, 0x9a, 0x10, - 0xff, 0xbf, 0xb2, 0xd5, 0x10, 0x1b, 0x80, 0x27, 0x63, 0xdf, 0x7c, 0xd9, 0x85, 0xc1, 0x66, 0xa1, 0xc5, 0x16, 0xf6, - 0xe1, 0x2d, 0xa7, 0xe2, 0x75, 0x73, 0x03, 0x35, 0xfc, 0x20, 0x81, 0x95, 0xef, 0x12, 0xaa, 0xf9, 0x9e, 0x38, 0xf6, - 0xbd, 0x57, 0xbe, 0x7a, 0x4e, 0x8f, 0x40, 0xd3, 0xe8, 0xac, 0x99, 0xf4, 0xe4, 0x70, 0x6e, 0x0c, 0x55, 0x23, 0xaf, - 0x95, 0xb7, 0x07, 0x57, 0xab, 0xbf, 0x3e, 0x9b, 0xf3, 0x36, 0x3f, 0xa2, 0x1f, 0x5d, 0x63, 0x23, 0x66, 0x71, 0xc2, - 0x57, 0xd7, 0x47, 0x91, 0x50, 0x51, 0xc4, 0xc5, 0x87, 0x75, 0x9f, 0x36, 0xae, 0xb7, 0x8e, 0x6e, 0xf1, 0x2e, 0xc0, - 0x9c, 0x92, 0x54, 0x9d, 0x6d, 0x67, 0xe8, 0x0a, 0xbe, 0x97, 0xb5, 0xc5, 0xf1, 0xa5, 0xb5, 0x6e, 0xcb, 0xcb, 0xae, - 0xbc, 0x37, 0x46, 0x5d, 0xb4, 0x60, 0xd7, 0x77, 0x9c, 0xbc, 0xd5, 0xc8, 0xfd, 0xea, 0xa9, 0x2d, 0x96, 0x50, 0x40, - 0x1b, 0x5a, 0xbe, 0x20, 0x3b, 0xc6, 0x9e, 0x8d, 0x4e, 0xa5, 0xc9, 0x53, 0xf4, 0xba, 0xfb, 0xcc, 0x23, 0x1e, 0xd6, - 0x81, 0xae, 0x9c, 0x06, 0x1d, 0xff, 0xc2, 0x7f, 0x79, 0x59, 0xaa, 0xb7, 0x2a, 0xae, 0xbd, 0x12, 0x00, 0x93, 0x2a, - 0x9f, 0xf4, 0xf2, 0xf7, 0x41, 0x10, 0x19, 0xd9, 0x08, 0xf1, 0x4c, 0x54, 0x96, 0x00, 0x3a, 0xae, 0x72, 0xf1, 0xce, - 0x74, 0xd0, 0x2f, 0x67, 0x22, 0x11, 0x39, 0x03, 0x6d, 0x1b, 0x14, 0x0a, 0x91, 0x7a, 0xbb, 0x08, 0xe2, 0x1e, 0x45, - 0x4c, 0x34, 0xd7, 0x5d, 0xdf, 0xaf, 0xd1, 0x71, 0x34, 0x36, 0xa3, 0x76, 0xfb, 0x5b, 0xc1, 0x14, 0x48, 0x89, 0x83, - 0x81, 0xba, 0xa2, 0x22, 0x1e, 0xff, 0xf1, 0x40, 0xfb, 0x25, 0x35, 0x9c, 0xb2, 0xc3, 0x78, 0x15, 0x5f, 0x59, 0x55, - 0xb5, 0xe2, 0x97, 0x88, 0x99, 0x21, 0x88, 0x37, 0x1a, 0xe9, 0x95, 0xcd, 0x5e, 0xcd, 0x64, 0xa2, 0x38, 0x29, 0x2c, - 0x8f, 0x6b, 0xd7, 0x84, 0x75, 0x00, 0x6b, 0xf5, 0xd1, 0xa1, 0xa5, 0xf8, 0xfb, 0xec, 0x8f, 0x4b, 0x8e, 0x99, 0xe7, - 0xcf, 0xf0, 0xbf, 0xcd, 0x2e, 0x97, 0xfc, 0xd1, 0x3d, 0xc9, 0xf6, 0x3d, 0x76, 0x00, 0xcd, 0x32, 0xa5, 0x8e, 0x32, - 0x86, 0x00, 0xc0, 0x41, 0xe2, 0x7b, 0x8b, 0xdb, 0xff, 0xee, 0x18, 0x44, 0xce, 0xf2, 0xa6, 0xc5, 0x83, 0xff, 0x18, - 0x51, 0x5a, 0x1a, 0x6b, 0xe1, 0x08, 0x82, 0x71, 0x6d, 0xac, 0x1b, 0xc9, 0x3c, 0xd0, 0x75, 0x04, 0xb2, 0x96, 0x9c, - 0x60, 0xa2, 0x44, 0xee, 0x55, 0xcd, 0xeb, 0x10, 0x6a, 0x25, 0x96, 0xa9, 0xcd, 0x23, 0xea, 0xa8, 0xb1, 0xef, 0x40, - 0xf0, 0x32, 0x3b, 0x44, 0x6d, 0xfe, 0x63, 0x4b, 0x81, 0x5f, 0x4a, 0x79, 0x32, 0x70, 0x78, 0x23, 0x14, 0x15, 0x1f, - 0x05, 0x30, 0x9c, 0x11, 0xbc, 0xa8, 0xd5, 0x57, 0x8e, 0x63, 0xa0, 0x1f, 0x4a, 0x2a, 0x5e, 0xec, 0x3e, 0x6f, 0xbc, - 0x01, 0x77, 0xa1, 0xfc, 0x03, 0xe5, 0x3a, 0x52, 0x2d, 0x7b, 0xf9, 0xc8, 0x4e, 0x6d, 0xc7, 0xd9, 0x50, 0x15, 0x54, - 0x45, 0xef, 0xd0, 0x2f, 0x85, 0x70, 0x60, 0x79, 0xb2, 0xda, 0x1b, 0xee, 0x0c, 0x7c, 0x6c, 0xc4, 0x47, 0x7d, 0x25, - 0x7b, 0x43, 0xa2, 0x8c, 0x85, 0xe4, 0x38, 0x2a, 0x40, 0xf4, 0xe4, 0xd3, 0x75, 0x36, 0x0d, 0x7b, 0x75, 0xb6, 0x14, - 0x48, 0x23, 0x46, 0x3a, 0x97, 0x4a, 0x67, 0xf6, 0xf4, 0x48, 0x19, 0x3f, 0xef, 0xfc, 0x6a, 0xd9, 0xa0, 0xcc, 0x36, - 0xa4, 0xf2, 0xa7, 0xbc, 0x2f, 0x25, 0x65, 0xb2, 0xad, 0xd8, 0xf4, 0xc6, 0xe6, 0x14, 0xc0, 0x64, 0x05, 0x61, 0xee, - 0xbe, 0x41, 0x39, 0x18, 0x63, 0x5d, 0xa9, 0x22, 0xdf, 0xf8, 0x3c, 0x76, 0x7a, 0x7a, 0xc1, 0x33, 0x8a, 0x2c, 0xfa, - 0x53, 0x04, 0x36, 0xcb, 0x6b, 0x85, 0x09, 0xdf, 0xe7, 0xb8, 0x46, 0xbf, 0xd0, 0x14, 0x4d, 0x42, 0xf4, 0xe3, 0x8d, - 0x48, 0x35, 0x2b, 0xe0, 0xcd, 0xfb, 0xa6, 0x1b, 0xc1, 0xb3, 0x32, 0xda, 0x48, 0x24, 0xda, 0xba, 0x29, 0xf0, 0xef, - 0x11, 0x7d, 0x23, 0x66, 0xfa, 0x83, 0x34, 0x5f, 0xfd, 0x20, 0xcc, 0x37, 0xdb, 0x03, 0xaa, 0xda, 0x87, 0xdc, 0xf8, - 0xe4, 0x42, 0x01, 0x16, 0x10, 0x46, 0x2f, 0x95, 0x36, 0xd6, 0x04, 0xa5, 0x84, 0x4b, 0x51, 0x93, 0x51, 0x5e, 0x4f, - 0xf5, 0x09, 0xad, 0xeb, 0x25, 0x19, 0x60, 0x12, 0xba, 0xb1, 0x8d, 0xbe, 0x8d, 0xb9, 0x4d, 0x97, 0xfd, 0x87, 0x0a, - 0xed, 0x81, 0x2b, 0x1b, 0x2c, 0xe0, 0x73, 0xb5, 0xe7, 0xce, 0x45, 0x04, 0x5a, 0x83, 0xf8, 0x8f, 0xe3, 0x7a, 0xb1, - 0x77, 0x4b, 0x25, 0x25, 0x56, 0x59, 0x08, 0x19, 0x2a, 0x17, 0x76, 0x73, 0xc3, 0x3c, 0xeb, 0x71, 0xf0, 0x8c, 0x04, - 0x01, 0xc1, 0xa9, 0x82, 0x49, 0x5c, 0x4d, 0x69, 0x58, 0xd9, 0x73, 0x74, 0xc3, 0x69, 0xf9, 0x35, 0x53, 0x65, 0xbb, - 0x40, 0xa7, 0x6f, 0x5c, 0x31, 0x98, 0x9f, 0xd8, 0x17, 0x8e, 0x1e, 0x5a, 0x46, 0xd7, 0x67, 0x07, 0x46, 0x80, 0x1c, - 0x56, 0x96, 0x81, 0x84, 0x2d, 0x49, 0xab, 0x37, 0x79, 0x78, 0xcf, 0x14, 0x22, 0xc9, 0x02, 0x55, 0x8e, 0x5f, 0x60, - 0x6b, 0x69, 0x49, 0x39, 0x2b, 0xd1, 0x5a, 0x85, 0x32, 0x44, 0x6b, 0xbd, 0x6f, 0x57, 0x9d, 0xde, 0x7b, 0x5f, 0xd0, - 0x79, 0x69, 0x24, 0x87, 0x18, 0x02, 0x43, 0x2c, 0x8d, 0xef, 0x14, 0x36, 0x5a, 0x6f, 0x96, 0xd9, 0x7d, 0x35, 0xb6, - 0x5f, 0xc3, 0x75, 0x3d, 0xf1, 0xa6, 0xfc, 0xb6, 0xce, 0x1e, 0xe6, 0xbc, 0x72, 0xa2, 0x1b, 0xba, 0x86, 0xcd, 0xda, - 0x4e, 0x7f, 0x55, 0xdf, 0x32, 0x19, 0x16, 0x1f, 0x7b, 0x08, 0x21, 0x17, 0xaa, 0x54, 0x88, 0xf4, 0x76, 0x27, 0x90, - 0x2a, 0xf7, 0x94, 0x2b, 0x9d, 0xe3, 0x44, 0xd6, 0xb1, 0x9d, 0x1c, 0x2e, 0x4d, 0x2a, 0x88, 0x63, 0x7b, 0xf7, 0x9d, - 0x58, 0xf0, 0xc9, 0x17, 0xd2, 0x9c, 0xa7, 0xeb, 0x97, 0x7e, 0x78, 0x65, 0xac, 0x94, 0x9c, 0x6e, 0x66, 0x51, 0xd3, - 0xdd, 0x2c, 0xb2, 0xf3, 0xaf, 0x71, 0xeb, 0x92, 0xf0, 0x3a, 0x69, 0xff, 0x6a, 0x84, 0x97, 0x5c, 0xeb, 0x52, 0x44, - 0x53, 0x94, 0xba, 0x7f, 0x9d, 0xa0, 0x20, 0x12, 0xfc, 0xb9, 0x68, 0x18, 0x6b, 0x9f, 0x56, 0xcd, 0x47, 0x63, 0xc5, - 0xd6, 0xde, 0xb7, 0x92, 0x1a, 0x17, 0x05, 0xd7, 0x8c, 0x5c, 0x69, 0xa5, 0xc4, 0xe0, 0x38, 0xd0, 0x94, 0x3f, 0x50, - 0xe5, 0x0f, 0x53, 0xd2, 0x79, 0x8b, 0xd9, 0xea, 0xfb, 0xd4, 0x6e, 0x1d, 0x53, 0x45, 0x23, 0x9d, 0x19, 0xb3, 0x51, - 0x2b, 0x38, 0xda, 0xe3, 0x7a, 0x59, 0x48, 0xe7, 0xb4, 0xcd, 0xe0, 0x93, 0xf4, 0xf1, 0xad, 0x7c, 0xb6, 0xca, 0x5f, - 0xea, 0xfd, 0x5e, 0xda, 0xdb, 0xe4, 0xc5, 0x06, 0xde, 0x0a, 0x13, 0x60, 0x20, 0xa2, 0x52, 0x05, 0xb5, 0x84, 0x24, - 0xec, 0xb4, 0xd3, 0x39, 0x43, 0x55, 0x5a, 0x4c, 0x81, 0x1f, 0x97, 0xf5, 0xf1, 0xf8, 0x5a, 0x34, 0xa6, 0xd6, 0x51, - 0x23, 0x3e, 0x2e, 0xe7, 0x19, 0x20, 0x2f, 0x54, 0x3c, 0x53, 0x11, 0x7d, 0x46, 0xce, 0xf0, 0xa0, 0xcc, 0x82, 0x91, - 0x76, 0x18, 0x8a, 0x2d, 0x37, 0xa6, 0x3a, 0x03, 0xba, 0xf0, 0x67, 0x8d, 0x94, 0x69, 0x84, 0x52, 0xc8, 0xb5, 0x49, - 0xbb, 0xcc, 0x37, 0x08, 0xd3, 0x0b, 0x1a, 0x7f, 0x3d, 0xf9, 0x5e, 0x0a, 0x99, 0x02, 0xee, 0x23, 0x85, 0xd7, 0xf4, - 0x42, 0x66, 0xc0, 0x9b, 0x1a, 0x20, 0x09, 0x40, 0x9a, 0x55, 0x27, 0xbc, 0x0d, 0x0f, 0x49, 0xb3, 0xdf, 0xca, 0x52, - 0xb9, 0x27, 0x57, 0x5a, 0xf2, 0xad, 0x6e, 0x2b, 0xe6, 0x4b, 0xd6, 0x36, 0xad, 0x9d, 0x9d, 0xd0, 0xeb, 0x34, 0x4d, - 0xba, 0x44, 0x38, 0xa8, 0x24, 0xbd, 0xdf, 0x01, 0x06, 0x53, 0x5f, 0xbe, 0x45, 0xcd, 0xfc, 0x5e, 0x82, 0x9d, 0x0c, - 0xd8, 0x50, 0x65, 0xe5, 0x32, 0x0b, 0x00, 0x01, 0xba, 0x6d, 0xa3, 0x9b, 0x26, 0x8b, 0x37, 0x22, 0xf7, 0x80, 0xce, - 0x05, 0x77, 0x64, 0x6f, 0x29, 0xdd, 0x99, 0x8e, 0x95, 0x6c, 0xbc, 0x2b, 0x6b, 0xb2, 0x0b, 0x95, 0xf8, 0x26, 0x06, - 0x66, 0x3b, 0x2b, 0x09, 0x80, 0xeb, 0xc6, 0x2e, 0xf3, 0x42, 0x9d, 0xc9, 0x6c, 0xcd, 0xaa, 0x3c, 0x55, 0xc3, 0x54, - 0x3a, 0x74, 0xd5, 0x44, 0x0d, 0x41, 0x36, 0x20, 0x6c, 0x5e, 0xdb, 0x5c, 0xc7, 0x67, 0x01, 0x20, 0xe8, 0x41, 0x29, - 0x4b, 0xc6, 0x8e, 0x1b, 0x69, 0x77, 0xbd, 0xac, 0x00, 0x61, 0xbc, 0xb3, 0x26, 0x39, 0x39, 0x2d, 0xfd, 0xc9, 0x78, - 0xdb, 0x6a, 0xa6, 0xdd, 0xf1, 0x43, 0x42, 0xdb, 0xe2, 0xd0, 0x82, 0x1f, 0xa9, 0xdd, 0xb9, 0x5a, 0xc4, 0xaa, 0xbd, - 0x2c, 0x60, 0xb0, 0x8d, 0xd6, 0xba, 0x6d, 0xee, 0xe6, 0x98, 0x08, 0x27, 0xcb, 0xc6, 0x74, 0x27, 0x96, 0x17, 0x89, - 0x35, 0x06, 0x6a, 0x6b, 0xde, 0xf8, 0xa5, 0xc0, 0xd4, 0x04, 0xdf, 0xa8, 0x5c, 0x2c, 0x8d, 0xfe, 0xf4, 0x03, 0x11, - 0xa1, 0x59, 0x6c, 0xae, 0xd6, 0x4d, 0x68, 0xbc, 0xc6, 0xf5, 0x06, 0xdc, 0x0d, 0x2c, 0x1a, 0x4e, 0xf4, 0x60, 0xce, - 0xee, 0x48, 0xcd, 0x8a, 0x65, 0xf8, 0xd1, 0xa3, 0xa3, 0x02, 0xbb, 0xb3, 0x39, 0x96, 0x14, 0x48, 0x30, 0xe2, 0xd7, - 0xd7, 0x58, 0x2c, 0x6a, 0x97, 0x46, 0x07, 0x63, 0x2e, 0xf9, 0x0f, 0xaa, 0x9b, 0x69, 0x5f, 0x01, 0x9b, 0x7b, 0x86, - 0x23, 0x49, 0x99, 0x19, 0xbd, 0xbc, 0x36, 0x0d, 0xec, 0x55, 0x1e, 0x75, 0x1c, 0x46, 0x4f, 0x4a, 0xc2, 0xde, 0x6c, - 0x4d, 0xa9, 0x5c, 0x8a, 0x51, 0xe8, 0x79, 0x83, 0x58, 0xf4, 0xb8, 0x07, 0x38, 0xc9, 0x98, 0x22, 0x7d, 0xb5, 0x51, - 0x90, 0xb7, 0xda, 0x5f, 0xba, 0xec, 0xa0, 0x49, 0x87, 0xce, 0x02, 0x9c, 0x8c, 0x92, 0x82, 0x10, 0xa0, 0x0d, 0xa1, - 0xd7, 0x06, 0x2f, 0xa5, 0x08, 0x4d, 0x4d, 0x66, 0xd4, 0x85, 0xf9, 0x9c, 0x71, 0x46, 0xa1, 0xa0, 0xa7, 0x5d, 0x9a, - 0x77, 0xab, 0xdb, 0x5c, 0x38, 0xde, 0x5d, 0x54, 0x2b, 0x02, 0x29, 0x5a, 0xf1, 0xd3, 0x43, 0xe1, 0x22, 0xb7, 0x20, - 0xa2, 0xd6, 0x1c, 0xde, 0x1a, 0x9c, 0x5c, 0x4c, 0x68, 0x95, 0xea, 0xae, 0xf7, 0xf0, 0x85, 0x88, 0xef, 0xda, 0x3c, - 0x21, 0x8e, 0x5a, 0x6f, 0xe8, 0xe6, 0x2c, 0xcd, 0x53, 0x09, 0xf5, 0xcc, 0x16, 0x02, 0x97, 0x8d, 0x8c, 0x2a, 0x7c, - 0x33, 0x3e, 0xc7, 0xc8, 0x92, 0x80, 0x32, 0x38, 0x9d, 0xc5, 0x08, 0x2c, 0x32, 0xe6, 0xe3, 0xd8, 0x1f, 0xcf, 0x6c, - 0x82, 0x7c, 0xd7, 0x98, 0x11, 0x89, 0xb7, 0xbd, 0x37, 0xd4, 0x28, 0x94, 0x8c, 0x44, 0x5c, 0x1e, 0x39, 0xb4, 0x7b, - 0x50, 0x7d, 0x37, 0x20, 0x36, 0x8c, 0x29, 0xd3, 0x09, 0xa1, 0x4f, 0x1e, 0xc4, 0x9a, 0x5c, 0x98, 0xb0, 0xd2, 0x49, - 0x0c, 0xc4, 0xe8, 0x6c, 0x40, 0xf5, 0x8d, 0xd0, 0x22, 0x91, 0x05, 0x25, 0x12, 0xf9, 0x6c, 0x4e, 0x88, 0xc3, 0x56, - 0x64, 0xfc, 0x60, 0xb5, 0x77, 0x11, 0x95, 0x3e, 0xe3, 0xa4, 0xb0, 0x2e, 0x0b, 0xa3, 0x3f, 0x46, 0x09, 0x61, 0xc0, - 0xd9, 0xed, 0x49, 0x51, 0xde, 0x0d, 0x8b, 0x47, 0x17, 0xa8, 0xca, 0xb7, 0x5c, 0x01, 0xec, 0xd1, 0x42, 0x0d, 0x54, - 0x59, 0xb2, 0x9c, 0xeb, 0x47, 0x21, 0xc2, 0x53, 0x66, 0x8e, 0xaa, 0x10, 0x06, 0x84, 0x88, 0x4a, 0xed, 0xc2, 0xae, - 0x95, 0x02, 0x74, 0x30, 0xa6, 0x8d, 0x46, 0x88, 0x0b, 0x78, 0x9e, 0xb7, 0x3f, 0x0e, 0x98, 0xe6, 0x89, 0x3f, 0xa8, - 0x06, 0xfd, 0x77, 0x24, 0x9b, 0xac, 0x9f, 0xdc, 0xf7, 0xc3, 0x27, 0x7d, 0x87, 0xce, 0xde, 0xef, 0xab, 0xbf, 0x7f, - 0xec, 0xd1, 0x40, 0x16, 0xf2, 0x0b, 0xdd, 0x84, 0x56, 0xcf, 0xde, 0x18, 0xee, 0x88, 0x56, 0xcf, 0x4e, 0x2f, 0x0a, - 0xd4, 0x3b, 0xd7, 0x4e, 0x6d, 0x1b, 0x36, 0x32, 0x89, 0xc7, 0x9a, 0x27, 0x63, 0xb0, 0x22, 0x83, 0x6a, 0x05, 0x2b, - 0x9b, 0x2c, 0xd1, 0x5d, 0x9f, 0x99, 0x83, 0x7b, 0xe2, 0x46, 0xbe, 0x93, 0x67, 0x1f, 0x80, 0x9b, 0x10, 0xf9, 0x4b, - 0x0e, 0xab, 0xfa, 0x1d, 0xd5, 0xa6, 0x3b, 0x28, 0x18, 0x4a, 0x2d, 0x31, 0x5b, 0x15, 0x8d, 0x25, 0xd8, 0x1b, 0x04, - 0x5a, 0x53, 0xab, 0x0f, 0xeb, 0x70, 0xc8, 0x1f, 0x5b, 0xfb, 0x07, 0x95, 0x89, 0xba, 0x68, 0x40, 0x9e, 0x86, 0x5f, - 0xba, 0x44, 0xb8, 0x6c, 0x53, 0xff, 0xaf, 0x6e, 0x2f, 0x76, 0x46, 0xc1, 0x24, 0xe4, 0x6d, 0xc8, 0xc3, 0xdd, 0xc1, - 0x00, 0x05, 0x4a, 0xe7, 0x1b, 0x6d, 0x78, 0x12, 0x3d, 0xc9, 0xc3, 0xf6, 0x79, 0x69, 0xaf, 0x46, 0x7d, 0xae, 0x63, - 0x9b, 0xda, 0xb6, 0x49, 0x4d, 0x49, 0x73, 0x70, 0x05, 0x96, 0x18, 0x17, 0x34, 0xad, 0xe4, 0x11, 0x4b, 0x2c, 0xc7, - 0xa4, 0xca, 0xad, 0xe4, 0x29, 0xa7, 0x8c, 0xff, 0x10, 0xb4, 0x97, 0x59, 0x1e, 0x0d, 0x97, 0xe5, 0xd1, 0x65, 0xb0, - 0x36, 0xa1, 0xba, 0xb7, 0xa1, 0xfa, 0x62, 0xd6, 0xb4, 0xd4, 0x6a, 0x93, 0x24, 0x91, 0xc6, 0x7b, 0xba, 0x58, 0xd7, - 0x03, 0xe8, 0xce, 0xd4, 0x2e, 0x65, 0x12, 0xc7, 0x38, 0xd9, 0x86, 0xb9, 0xfa, 0xd8, 0x2a, 0xad, 0xcf, 0x5f, 0xd0, - 0xf8, 0xdc, 0x7d, 0x2b, 0x8f, 0x18, 0xb5, 0x18, 0x78, 0x7f, 0x78, 0x2a, 0xc1, 0xc5, 0xa1, 0xb1, 0xb3, 0x3d, 0x4c, - 0x1c, 0x76, 0xec, 0xec, 0xd7, 0x14, 0x4c, 0xcf, 0x81, 0x36, 0xf4, 0xd5, 0xe0, 0xf8, 0xda, 0x3d, 0x77, 0xf0, 0x62, - 0x40, 0x4b, 0xa4, 0xbc, 0x53, 0xe4, 0x88, 0x01, 0x26, 0x5a, 0xf9, 0x9b, 0x5f, 0xe7, 0xf5, 0x87, 0xf8, 0x7a, 0x3c, - 0x10, 0x3b, 0x51, 0x1e, 0x3d, 0x2b, 0x14, 0xa5, 0x44, 0x45, 0x4f, 0xe1, 0x2f, 0x6e, 0xa1, 0x0c, 0xa7, 0x89, 0x4e, - 0x47, 0x45, 0xb7, 0x77, 0x4f, 0x7c, 0x67, 0xff, 0xa6, 0x3a, 0x97, 0xf3, 0x0a, 0x03, 0x5d, 0x08, 0x6c, 0xa0, 0x8c, - 0x8c, 0x05, 0x4a, 0xf1, 0x63, 0xcc, 0x2e, 0x43, 0x94, 0xdc, 0xea, 0x13, 0x3e, 0x70, 0x11, 0x98, 0x3b, 0xa4, 0x49, - 0xc2, 0xe8, 0x51, 0x7b, 0x6e, 0x5a, 0x9e, 0x84, 0x99, 0x9d, 0x27, 0x99, 0x9d, 0x53, 0xc5, 0x85, 0x09, 0x53, 0x35, - 0x28, 0x16, 0x8f, 0xe5, 0xa4, 0xb6, 0x5a, 0x4d, 0x33, 0x27, 0x9a, 0xe9, 0x91, 0x3b, 0x0c, 0x81, 0x6e, 0xd2, 0x0d, - 0x35, 0xfa, 0x4d, 0x54, 0xf1, 0xd1, 0x7a, 0x11, 0x0c, 0xd1, 0xfa, 0x74, 0xd6, 0x46, 0xb9, 0x63, 0x14, 0x25, 0xdf, - 0x17, 0x80, 0xb8, 0xb7, 0xae, 0x28, 0x5d, 0x7d, 0xf2, 0xc7, 0x3f, 0x4c, 0xb5, 0x9e, 0x07, 0x10, 0x23, 0xbe, 0x66, - 0x93, 0x33, 0xa3, 0xf2, 0x48, 0xfc, 0x43, 0x98, 0xb4, 0x80, 0x3b, 0x42, 0x58, 0xb5, 0x71, 0x30, 0x49, 0x4e, 0xe7, - 0x62, 0xa8, 0xef, 0xa2, 0x91, 0x24, 0x94, 0x49, 0x7d, 0x0a, 0x9e, 0x4d, 0x4e, 0xad, 0x8f, 0x0e, 0x09, 0x77, 0xeb, - 0x20, 0x14, 0x62, 0xa6, 0x5a, 0x03, 0xdc, 0x3d, 0xa5, 0xfb, 0x7a, 0xed, 0x8d, 0xd7, 0x2a, 0xe2, 0xfe, 0xfb, 0xc5, - 0xc1, 0xfb, 0xef, 0x78, 0xa9, 0xa9, 0xdf, 0x6f, 0x9c, 0x0d, 0xdb, 0xb7, 0x3c, 0x00, 0x2f, 0x06, 0x78, 0x08, 0x70, - 0x11, 0xf5, 0x56, 0xa7, 0xfd, 0x61, 0x74, 0xe3, 0xeb, 0xca, 0xec, 0x59, 0xd1, 0xf5, 0x3b, 0x3f, 0x78, 0xb7, 0x6f, - 0x21, 0x60, 0x17, 0xdd, 0xff, 0x1f, 0x81, 0x0a, 0x08, 0x86, 0x82, 0xbf, 0x3f, 0x6e, 0x87, 0xb3, 0x23, 0x78, 0x0e, - 0xbd, 0x3e, 0x8e, 0x62, 0xa5, 0x7b, 0x27, 0x4d, 0xb1, 0x57, 0x11, 0x54, 0x99, 0x57, 0xc4, 0xa6, 0x8c, 0xcd, 0x2e, - 0xeb, 0x52, 0xaf, 0xcd, 0x37, 0x18, 0xd0, 0x97, 0x00, 0xc8, 0x48, 0xf5, 0xa6, 0x0c, 0x20, 0xfc, 0xfa, 0x52, 0x2c, - 0x46, 0xf3, 0x7c, 0xa7, 0xb5, 0x6b, 0xf7, 0x29, 0xf4, 0xc3, 0x76, 0x1d, 0x1e, 0x0c, 0xed, 0x09, 0x79, 0x9e, 0x37, - 0xbc, 0xcb, 0xf0, 0x6d, 0x5e, 0x14, 0x9c, 0x06, 0x2f, 0xa3, 0x5a, 0x1a, 0xf2, 0x49, 0x34, 0x06, 0xfa, 0xb4, 0x6f, - 0x29, 0x01, 0xb7, 0x21, 0x31, 0xd8, 0x41, 0x56, 0x7a, 0x7d, 0x24, 0xed, 0x9d, 0xeb, 0x31, 0xbc, 0xd9, 0x6e, 0x71, - 0x91, 0x32, 0x22, 0xb1, 0x63, 0xa0, 0xc9, 0x8d, 0x50, 0xed, 0xed, 0xce, 0x9e, 0x0f, 0xdf, 0xdc, 0x5c, 0xde, 0xdc, - 0xae, 0x8f, 0x43, 0xaa, 0xb1, 0x4e, 0xa7, 0xd6, 0x6a, 0x6c, 0x27, 0x6d, 0x91, 0xef, 0x2d, 0x0b, 0x9b, 0x84, 0x16, - 0xe9, 0x06, 0x96, 0x96, 0x0f, 0x93, 0xaa, 0x55, 0x06, 0x38, 0x91, 0x9a, 0xba, 0x9f, 0x9e, 0x9e, 0x33, 0x25, 0xcb, - 0x03, 0x7a, 0x71, 0xd0, 0x55, 0x21, 0xb6, 0x4b, 0xd7, 0x6f, 0x2f, 0x97, 0x9e, 0xeb, 0x06, 0x60, 0x13, 0x39, 0x30, - 0x90, 0xf2, 0x7f, 0xc7, 0xa2, 0x5e, 0x0e, 0xcb, 0x93, 0x25, 0x82, 0xc2, 0x25, 0xde, 0x75, 0x49, 0x4a, 0xb4, 0x29, - 0x45, 0x68, 0x2e, 0x5e, 0x1f, 0x15, 0xe3, 0x49, 0xdd, 0x59, 0xf3, 0xec, 0x20, 0x12, 0x19, 0x5b, 0x19, 0x1b, 0xcc, - 0x4d, 0x5a, 0x86, 0x00, 0x07, 0x85, 0x64, 0xcb, 0xf5, 0xa6, 0x0b, 0xb0, 0x5d, 0xf2, 0x57, 0xa3, 0x71, 0x9e, 0x2c, - 0xd1, 0x1d, 0x1a, 0xf6, 0xe5, 0x40, 0xc1, 0xe4, 0xe6, 0xca, 0xe9, 0x91, 0x1f, 0xc5, 0x6c, 0xb1, 0x46, 0x86, 0xc1, - 0x82, 0xe9, 0x04, 0x1c, 0x08, 0xb9, 0x57, 0x0e, 0x10, 0x5b, 0x16, 0xf8, 0x30, 0x98, 0x5b, 0x22, 0x9b, 0x3c, 0xda, - 0xd9, 0x3d, 0x55, 0x28, 0xf8, 0xe4, 0xd6, 0x6d, 0x59, 0xf2, 0xca, 0x0f, 0x82, 0x5e, 0xc5, 0xe5, 0x69, 0xbb, 0x68, - 0x8e, 0xc9, 0xd1, 0x77, 0xd9, 0x94, 0xfd, 0x30, 0x8d, 0xc8, 0xc3, 0x43, 0x92, 0xe1, 0x30, 0x0b, 0x82, 0xc5, 0x4e, - 0x78, 0x29, 0x6c, 0x60, 0xac, 0x6d, 0xc2, 0x8e, 0xd4, 0x10, 0xde, 0x21, 0x26, 0xac, 0x99, 0xb3, 0x16, 0x2c, 0x10, - 0x71, 0x39, 0xe8, 0x3e, 0x72, 0xa0, 0x5f, 0xd9, 0x0a, 0x1d, 0xed, 0xe2, 0x6e, 0xf6, 0x23, 0x16, 0xc8, 0xd8, 0x92, - 0x39, 0xe9, 0x1a, 0x7e, 0xcb, 0x50, 0xad, 0xad, 0x67, 0xa3, 0xb3, 0x7b, 0xc3, 0x34, 0xd1, 0x96, 0x25, 0x3b, 0xa2, - 0x64, 0xfd, 0x42, 0x02, 0xb7, 0x50, 0xe5, 0x46, 0xee, 0xad, 0x44, 0x11, 0xc4, 0x14, 0xba, 0x78, 0xdd, 0x2d, 0x8c, - 0x88, 0x37, 0xd3, 0xa9, 0x39, 0x2a, 0x7c, 0x22, 0x63, 0x50, 0x52, 0x92, 0x82, 0xff, 0x67, 0xbd, 0x5f, 0x80, 0x82, - 0xf8, 0xc4, 0xaf, 0x7f, 0x17, 0x44, 0x38, 0xb0, 0xdb, 0x5e, 0xb6, 0xaa, 0x1d, 0x4b, 0x50, 0x1e, 0x15, 0xe6, 0xdc, - 0x40, 0x6a, 0xfd, 0x7b, 0x6e, 0xe3, 0xcd, 0x9f, 0xbf, 0xcb, 0x5c, 0xad, 0xdb, 0xe5, 0xe6, 0xb5, 0x3b, 0xe4, 0x9a, - 0xb1, 0x03, 0xf6, 0xe5, 0xe0, 0xc3, 0x6a, 0x26, 0xdd, 0x02, 0x92, 0x86, 0x4c, 0x2f, 0xdc, 0xae, 0xe8, 0x86, 0x13, - 0x72, 0x07, 0xe4, 0x10, 0x20, 0xd0, 0x66, 0x50, 0xd6, 0xe8, 0x58, 0xef, 0xc3, 0x79, 0x7b, 0x7d, 0xf9, 0xf7, 0xba, - 0x5e, 0xa2, 0x43, 0x9a, 0x9d, 0xc5, 0xa0, 0xff, 0x7e, 0x2b, 0x19, 0xc9, 0xf6, 0xcd, 0xf6, 0xfe, 0x5d, 0x0b, 0x8a, - 0x6b, 0x9a, 0xf6, 0x0f, 0x7e, 0xf9, 0xa2, 0xb7, 0xf0, 0x7a, 0xe7, 0x23, 0xa9, 0x49, 0x53, 0x6e, 0xf8, 0x71, 0xb5, - 0x95, 0xef, 0x4a, 0x66, 0x7c, 0x40, 0x60, 0xc4, 0xc9, 0xea, 0xe2, 0xe9, 0x61, 0xc4, 0x64, 0x3d, 0x6a, 0x18, 0x4e, - 0x6e, 0x6d, 0xc6, 0xb4, 0x6a, 0x21, 0x32, 0xc0, 0x25, 0x1a, 0x95, 0x28, 0x12, 0x25, 0x31, 0x40, 0x70, 0x6f, 0x7d, - 0x9e, 0xa0, 0x2d, 0x6a, 0xd6, 0x0e, 0xd4, 0x76, 0x56, 0x36, 0x27, 0x01, 0xa3, 0xcd, 0x1c, 0xd3, 0x6a, 0x2e, 0x42, - 0xe7, 0xee, 0x34, 0x88, 0x0e, 0xbd, 0x25, 0xba, 0x94, 0xb9, 0x62, 0xdf, 0xb4, 0xac, 0x2d, 0x03, 0xf2, 0x49, 0xd4, - 0x46, 0x1d, 0x24, 0x58, 0xe5, 0x54, 0x6c, 0x26, 0xf6, 0x8d, 0xa1, 0x2d, 0xdc, 0x81, 0xbe, 0x81, 0x1e, 0xac, 0xf1, - 0x92, 0xdd, 0xe4, 0xed, 0x53, 0xca, 0x0b, 0x8b, 0x49, 0xf7, 0x3b, 0xa9, 0x1e, 0xdb, 0x5b, 0x03, 0xa2, 0x50, 0x8c, - 0x77, 0x0f, 0x09, 0x56, 0x1e, 0xbd, 0x0d, 0x38, 0xb6, 0x4a, 0xaf, 0x71, 0x55, 0x3d, 0x31, 0x26, 0x78, 0x58, 0xca, - 0x27, 0xdf, 0x3f, 0x79, 0x35, 0xee, 0x1a, 0xc6, 0x4b, 0x8b, 0x5b, 0x10, 0x54, 0x30, 0x7b, 0x8b, 0x59, 0xfc, 0xd2, - 0xfc, 0xbe, 0x7b, 0xe0, 0xc6, 0xce, 0x21, 0x37, 0x6f, 0x70, 0xf7, 0x5a, 0xdc, 0xa7, 0xce, 0x67, 0xf5, 0xec, 0xd3, - 0xe9, 0x6a, 0x6b, 0x14, 0x7d, 0x3b, 0x03, 0xed, 0x11, 0xe9, 0xac, 0x01, 0x98, 0x04, 0x28, 0x4b, 0x32, 0xa0, 0x86, - 0x05, 0x5e, 0x2e, 0xad, 0xba, 0x13, 0xd4, 0x54, 0x7b, 0xb6, 0x29, 0x9f, 0x0b, 0x6b, 0x2c, 0xbe, 0x58, 0xba, 0x4e, - 0x53, 0xc3, 0x14, 0xb5, 0xae, 0x5d, 0xf3, 0xf7, 0x6f, 0x65, 0x09, 0x34, 0x4c, 0xe5, 0x8a, 0xfd, 0x1a, 0x55, 0x43, - 0xf0, 0x29, 0x2c, 0xa2, 0x84, 0x00, 0xcf, 0x62, 0x12, 0xa8, 0x5a, 0x3f, 0xb4, 0xbd, 0xdf, 0xbb, 0x63, 0xeb, 0x64, - 0x3a, 0xb8, 0x6b, 0x40, 0x96, 0x99, 0xf3, 0xce, 0x99, 0x96, 0xa1, 0x9b, 0xc6, 0x45, 0x48, 0xd9, 0x4f, 0x5f, 0xa0, - 0x4e, 0x96, 0xdb, 0xec, 0x51, 0xd0, 0x58, 0x0e, 0x91, 0x14, 0xb9, 0x20, 0xc5, 0xbf, 0x0b, 0x47, 0x3c, 0x46, 0x6a, - 0x9d, 0xa9, 0x65, 0x8c, 0xa6, 0xff, 0x16, 0xd6, 0x82, 0xa5, 0xdd, 0x7b, 0x96, 0xc1, 0x8f, 0x93, 0x01, 0xd5, 0x3a, - 0x77, 0x52, 0x26, 0x9b, 0x25, 0x8c, 0x0c, 0xed, 0x8e, 0x5a, 0xfd, 0xf4, 0x6b, 0xbd, 0x5d, 0x9a, 0xbd, 0x34, 0xcd, - 0x2f, 0xa2, 0x85, 0x81, 0x2c, 0x01, 0x17, 0x0b, 0x4a, 0x3b, 0x27, 0xd5, 0xbf, 0xf7, 0xcd, 0xf7, 0xc4, 0xf7, 0xc2, - 0x5f, 0x66, 0x3e, 0x8f, 0x7c, 0xca, 0x2b, 0x3f, 0x40, 0x9e, 0x4f, 0xee, 0xad, 0x16, 0x0c, 0x23, 0x98, 0x88, 0xac, - 0x5c, 0x81, 0x80, 0x45, 0x91, 0x3c, 0x50, 0x01, 0x89, 0x88, 0x2b, 0xdb, 0x21, 0xad, 0x66, 0xbd, 0x9b, 0x01, 0x85, - 0x01, 0xd7, 0xfe, 0x42, 0xe3, 0x9c, 0x2e, 0xf6, 0xd6, 0x51, 0x51, 0xe9, 0x58, 0x1a, 0xfd, 0x11, 0x98, 0x18, 0x51, - 0xc9, 0xe9, 0xa8, 0x38, 0xb3, 0x18, 0xed, 0x2b, 0x3a, 0x8b, 0x19, 0xc8, 0x58, 0xa9, 0x29, 0x5b, 0xf9, 0x0d, 0x30, - 0xbb, 0x3d, 0x97, 0x34, 0xf5, 0x18, 0x0e, 0xe4, 0x05, 0x44, 0x0d, 0xac, 0x68, 0x03, 0x9d, 0xda, 0x6f, 0x08, 0xcf, - 0x1b, 0x96, 0x47, 0x80, 0x20, 0x28, 0xdf, 0x41, 0xd8, 0x9f, 0xd8, 0xbe, 0x72, 0x35, 0xc3, 0x29, 0xc3, 0xf4, 0x19, - 0x87, 0x86, 0xfa, 0x14, 0xfc, 0x04, 0x6c, 0xa2, 0xab, 0x11, 0x20, 0xdf, 0x24, 0x84, 0x1e, 0x04, 0xfd, 0x2b, 0x8f, - 0x48, 0x7f, 0xdd, 0xd4, 0xea, 0x2b, 0x98, 0xe2, 0xa8, 0x4c, 0xd6, 0x6d, 0x6a, 0x5b, 0xbd, 0xb2, 0x65, 0x5c, 0xd7, - 0x80, 0x3a, 0x2d, 0x9d, 0xe3, 0x0c, 0x27, 0x0d, 0xf1, 0xbf, 0x06, 0x86, 0x3f, 0xa8, 0xdd, 0x0e, 0xa3, 0x0f, 0xfd, - 0xc6, 0x8c, 0x79, 0x87, 0x70, 0x78, 0x3c, 0x31, 0x8d, 0xdc, 0x9f, 0x0b, 0x4c, 0x87, 0x96, 0xf8, 0x23, 0x8d, 0x38, - 0xe9, 0x83, 0xd2, 0x8b, 0xd5, 0xa1, 0x32, 0xfe, 0xdb, 0xb8, 0x1f, 0xbe, 0x6d, 0xb3, 0x8a, 0xe1, 0xc9, 0x88, 0x02, - 0xb6, 0x1a, 0xb3, 0x8e, 0x4f, 0x8e, 0xd6, 0xe3, 0x98, 0xdb, 0x80, 0xa8, 0x71, 0xbd, 0xa9, 0xda, 0x2c, 0x52, 0xb1, - 0xe5, 0x96, 0x3d, 0x1f, 0xcc, 0xa8, 0x7c, 0xfc, 0xf3, 0x32, 0x15, 0x82, 0x00, 0x55, 0xe2, 0x43, 0x34, 0xd0, 0xc5, - 0x6e, 0x27, 0x68, 0xe1, 0xb7, 0x96, 0xd2, 0x4a, 0xe6, 0xc1, 0x6a, 0xee, 0x90, 0x80, 0x8e, 0xaa, 0x01, 0xc3, 0xa7, - 0x68, 0xb2, 0xab, 0xc9, 0x31, 0x42, 0x01, 0x4d, 0xce, 0x92, 0x86, 0x93, 0x61, 0xbf, 0x2d, 0x4e, 0x7f, 0x9d, 0xf3, - 0x51, 0xb3, 0x21, 0x52, 0xdf, 0x8e, 0x89, 0x98, 0x7e, 0xc7, 0x57, 0x59, 0x19, 0x1b, 0xa1, 0x78, 0x33, 0x88, 0x8d, - 0x21, 0xc9, 0x1b, 0x05, 0x25, 0x42, 0x24, 0xbb, 0x38, 0x11, 0x66, 0xf3, 0x7e, 0xa5, 0xf0, 0xf4, 0x15, 0xa1, 0xd4, - 0x1c, 0x23, 0x8d, 0x0e, 0xb6, 0x74, 0xc2, 0xda, 0xb4, 0x7d, 0x5c, 0x7d, 0x81, 0x41, 0x87, 0xcf, 0x1c, 0xf0, 0x02, - 0xe0, 0xc6, 0xb0, 0x0a, 0x60, 0xad, 0x31, 0x77, 0x0c, 0xb7, 0x65, 0x7c, 0x62, 0x2d, 0x73, 0x40, 0xff, 0x98, 0xc8, - 0x72, 0x43, 0x7b, 0x0e, 0x41, 0xc1, 0xb4, 0x1d, 0x58, 0xa2, 0xf2, 0xef, 0xb4, 0x29, 0x76, 0x55, 0x31, 0x31, 0x0f, - 0x84, 0xcb, 0x12, 0x09, 0x95, 0xaf, 0x7b, 0xd7, 0x63, 0x06, 0xf8, 0x88, 0xa8, 0x19, 0x54, 0xbc, 0xce, 0x4d, 0x7e, - 0x55, 0x3f, 0xbf, 0x04, 0xec, 0x75, 0xf6, 0xba, 0xfe, 0xf0, 0xba, 0x7a, 0xfa, 0x93, 0x52, 0x00, 0xf4, 0x5c, 0xd8, - 0x95, 0x61, 0x26, 0x0b, 0x9b, 0xc8, 0xf0, 0x73, 0xbd, 0x84, 0xf2, 0xb4, 0x99, 0x03, 0x42, 0x38, 0xc7, 0xf9, 0xe4, - 0xfa, 0x74, 0x95, 0xb9, 0x09, 0xa4, 0x08, 0xb8, 0x09, 0x20, 0xf3, 0xfe, 0x08, 0x67, 0xce, 0x07, 0x04, 0xe2, 0x5d, - 0x5c, 0x9b, 0x1c, 0x3d, 0x0e, 0x92, 0x98, 0xdd, 0x4f, 0x3d, 0x2a, 0x88, 0xcb, 0x68, 0x01, 0x0d, 0x5b, 0x53, 0x76, - 0x2d, 0x58, 0xee, 0x08, 0x1d, 0x36, 0x84, 0x99, 0x42, 0x57, 0x89, 0xfc, 0x87, 0x47, 0x4b, 0xaa, 0xe8, 0xb1, 0x3b, - 0x7a, 0xb6, 0x22, 0xca, 0x70, 0x52, 0x47, 0x42, 0x82, 0xf0, 0x85, 0xa8, 0x81, 0x7e, 0xc0, 0xc6, 0xa8, 0x52, 0xe2, - 0x12, 0xdb, 0x12, 0xe8, 0x3b, 0x09, 0xc2, 0xb2, 0x53, 0x1a, 0x86, 0xe6, 0x90, 0xc3, 0x48, 0x14, 0x41, 0x29, 0xfc, - 0x02, 0x25, 0xcf, 0x34, 0x94, 0x80, 0x32, 0x75, 0x60, 0x47, 0x0d, 0x55, 0x89, 0x09, 0x75, 0x7a, 0x7a, 0x10, 0xdd, - 0xbb, 0x0c, 0x34, 0x4d, 0x07, 0xa7, 0x1d, 0x2a, 0xc6, 0xd2, 0x98, 0xea, 0x60, 0x3b, 0x2a, 0x04, 0x47, 0x3a, 0x1e, - 0x32, 0x0a, 0x4e, 0x6e, 0xdf, 0xe1, 0xb2, 0xe1, 0xd3, 0xed, 0xa7, 0x4a, 0x8c, 0x8e, 0x9e, 0xac, 0xce, 0xa5, 0xd5, - 0xf3, 0x6c, 0xcc, 0x24, 0x48, 0x9f, 0xc0, 0xa1, 0x52, 0xf8, 0x32, 0x03, 0xd3, 0x22, 0x8f, 0xb7, 0x65, 0xb4, 0x38, - 0x85, 0x92, 0xab, 0x6e, 0x1f, 0xe9, 0x36, 0xdf, 0xce, 0xa4, 0xdb, 0x6f, 0xa7, 0xc1, 0x51, 0xd6, 0xcc, 0xfa, 0x42, - 0xf9, 0xbc, 0x52, 0xaa, 0xed, 0x5b, 0xf9, 0x49, 0xa2, 0x83, 0x63, 0x0d, 0xd5, 0x2a, 0x2c, 0xf1, 0x93, 0x81, 0xd5, - 0x6b, 0x48, 0xb5, 0x91, 0x8a, 0x61, 0x07, 0x9e, 0x8f, 0x3c, 0x9e, 0xbb, 0xae, 0x34, 0xe3, 0xca, 0x30, 0xb3, 0x49, - 0x25, 0xc6, 0xf7, 0xc3, 0x63, 0x0f, 0xed, 0x99, 0xf6, 0xf9, 0x74, 0xf8, 0x12, 0xe8, 0x74, 0x20, 0x9a, 0x80, 0x81, - 0x39, 0x84, 0x32, 0x16, 0x68, 0x6c, 0x2c, 0x66, 0x51, 0x1e, 0x95, 0x29, 0x4d, 0x95, 0xc6, 0x30, 0x86, 0xda, 0x00, - 0xae, 0x6e, 0xd7, 0x4c, 0x4a, 0x46, 0x49, 0x77, 0x29, 0x0d, 0x14, 0xd3, 0x31, 0x8c, 0x15, 0x9e, 0x29, 0x19, 0x2e, - 0x0a, 0x71, 0x1a, 0xe0, 0xcb, 0x8b, 0xff, 0xf7, 0xaf, 0xc0, 0xa8, 0xb9, 0xed, 0x91, 0xac, 0xd9, 0xec, 0x68, 0x4b, - 0x2b, 0x3c, 0x4f, 0xe7, 0xcb, 0x17, 0x29, 0xeb, 0x52, 0x2d, 0x8a, 0xd3, 0xe8, 0x28, 0x23, 0x4a, 0xfb, 0x76, 0xf7, - 0x97, 0xba, 0x33, 0x8c, 0x98, 0x2b, 0xdf, 0xf8, 0x3d, 0xe5, 0x5a, 0xf2, 0x6e, 0xb7, 0x8c, 0xac, 0x4a, 0x31, 0xe1, - 0x43, 0xe5, 0x1a, 0x5e, 0x69, 0xfd, 0x07, 0xf9, 0x4f, 0xb9, 0xaa, 0x6d, 0x7f, 0x0c, 0xeb, 0x95, 0x6c, 0x4e, 0xb4, - 0xde, 0x3c, 0xe3, 0x88, 0xb7, 0x3d, 0xc6, 0xfd, 0x25, 0x85, 0x63, 0x69, 0xfc, 0xae, 0xea, 0x64, 0x37, 0x3f, 0xb9, - 0x5c, 0x90, 0xb4, 0x98, 0x74, 0xeb, 0xad, 0xca, 0x7e, 0xe6, 0xab, 0xf7, 0xfb, 0xb3, 0x87, 0x3b, 0x26, 0x41, 0xc2, - 0x6d, 0x43, 0x3e, 0x0d, 0x22, 0xbd, 0x6d, 0x46, 0x47, 0x69, 0xf2, 0xca, 0x99, 0x4d, 0x08, 0x84, 0xe3, 0x8d, 0xe9, - 0x01, 0x26, 0x3b, 0x93, 0xd2, 0xcb, 0xfe, 0x67, 0x76, 0xe5, 0xda, 0xd4, 0xc5, 0x5d, 0xb1, 0xc5, 0x83, 0xe4, 0xd7, - 0x43, 0x7c, 0x38, 0x86, 0x37, 0x9f, 0xe3, 0x77, 0xc8, 0x3f, 0xea, 0xb8, 0x0c, 0x0c, 0x4c, 0xac, 0x1c, 0xfb, 0x4e, - 0x78, 0xd9, 0xdf, 0x12, 0x6b, 0x50, 0x56, 0x69, 0x8a, 0x21, 0x18, 0xc4, 0x79, 0x1d, 0x00, 0xc8, 0x95, 0x0d, 0x62, - 0x9b, 0x27, 0xb2, 0xe5, 0xab, 0x60, 0xf1, 0xce, 0xf1, 0xd1, 0x0b, 0x6e, 0x4a, 0x7c, 0xaa, 0xbc, 0x3d, 0x63, 0x0c, - 0x70, 0x0b, 0xca, 0xd3, 0xb1, 0x83, 0x19, 0x31, 0x47, 0x42, 0xed, 0x8a, 0x4a, 0x2c, 0x49, 0x1d, 0x2a, 0x14, 0xcd, - 0xea, 0x82, 0x91, 0x89, 0xe4, 0xb3, 0x35, 0x55, 0x82, 0x81, 0xd4, 0x41, 0x7b, 0xf6, 0x2c, 0x4a, 0x9a, 0x7d, 0x1e, - 0x9a, 0x6c, 0x92, 0x3b, 0x7e, 0x09, 0xa6, 0x3f, 0xf8, 0x59, 0x28, 0xe9, 0x73, 0x6f, 0x62, 0x21, 0x7f, 0xb7, 0x95, - 0xf5, 0x27, 0xec, 0x1d, 0xfe, 0x26, 0x21, 0x7c, 0x39, 0x85, 0xd5, 0x24, 0x61, 0x59, 0xb8, 0xf0, 0x76, 0x49, 0x80, - 0x3c, 0x65, 0x69, 0x57, 0x83, 0x03, 0x85, 0x3e, 0x14, 0x94, 0x2c, 0x96, 0xb1, 0x12, 0x33, 0xc3, 0x22, 0xa6, 0xe4, - 0x5e, 0xf4, 0x35, 0xf3, 0xbe, 0xf9, 0x3a, 0x85, 0x47, 0x06, 0x4f, 0xe5, 0xa6, 0x6d, 0x5b, 0x88, 0x0e, 0x18, 0x9a, - 0xe9, 0x4f, 0x70, 0x40, 0xbb, 0x7f, 0xdd, 0xa5, 0xa7, 0x1c, 0xf8, 0xec, 0x39, 0x0e, 0xd6, 0x56, 0x9e, 0xa5, 0x9c, - 0x35, 0x54, 0xf7, 0x39, 0x05, 0x3f, 0x17, 0xef, 0x10, 0x57, 0x26, 0xc1, 0xd3, 0x5d, 0x4c, 0x12, 0x54, 0x9f, 0x82, - 0x21, 0xe9, 0x04, 0x74, 0xb1, 0xc2, 0xea, 0x5a, 0xb3, 0xe5, 0x09, 0xba, 0x98, 0x60, 0x05, 0x63, 0x38, 0x14, 0xf4, - 0xf2, 0x30, 0xb3, 0x1e, 0x56, 0xd3, 0xd3, 0x22, 0x48, 0x22, 0x9d, 0xec, 0xf6, 0x53, 0x92, 0xbd, 0x26, 0x12, 0x40, - 0x3f, 0x37, 0x2b, 0x69, 0x03, 0xe0, 0x41, 0xad, 0x10, 0xb1, 0xef, 0x45, 0xcc, 0x49, 0x2a, 0x55, 0x73, 0x46, 0xb7, - 0x15, 0x02, 0x62, 0x5d, 0xf8, 0x5b, 0x5e, 0xdd, 0x94, 0xfa, 0x53, 0xb0, 0x80, 0xbe, 0xe1, 0x42, 0x02, 0xaf, 0x8d, - 0x8d, 0xf7, 0x8a, 0xc6, 0x1a, 0x5f, 0x02, 0x58, 0x1c, 0x0c, 0xf0, 0xa4, 0xc6, 0x32, 0x2c, 0x01, 0x69, 0x15, 0x0f, - 0x9d, 0x98, 0xb0, 0xf2, 0xb4, 0xe0, 0x98, 0xe5, 0xbb, 0x7f, 0x98, 0xdf, 0xe9, 0xb4, 0x4e, 0x20, 0x31, 0xd3, 0xa9, - 0x76, 0x4b, 0x2f, 0x1f, 0x58, 0xbf, 0xd6, 0x98, 0x25, 0xe2, 0x9e, 0xe4, 0x65, 0xb7, 0x63, 0x15, 0xda, 0x58, 0xc4, - 0x32, 0x9e, 0x29, 0x87, 0x57, 0x53, 0x6f, 0xf3, 0xf0, 0x00, 0x0e, 0xcf, 0xa7, 0x96, 0xfb, 0xeb, 0x00, 0x13, 0x87, - 0x9b, 0x52, 0x28, 0x15, 0xf1, 0x7a, 0x10, 0x20, 0x12, 0xc4, 0x44, 0xbb, 0xc8, 0x50, 0x7a, 0xca, 0x0d, 0x62, 0xb3, - 0x01, 0x25, 0x62, 0x87, 0xb6, 0x8e, 0xd2, 0x1f, 0xc2, 0x57, 0x47, 0xf9, 0x54, 0x99, 0xea, 0xa4, 0xb7, 0x30, 0xcb, - 0xe5, 0x48, 0x35, 0x34, 0x60, 0xd9, 0x71, 0xfb, 0xc9, 0x63, 0x5b, 0x61, 0x78, 0x6e, 0xab, 0xfe, 0x6e, 0x1b, 0xfe, - 0xfe, 0x02, 0x5e, 0x3c, 0xfd, 0xbe, 0xee, 0x6b, 0x6e, 0xd9, 0x90, 0x43, 0x5d, 0xda, 0x8d, 0x88, 0xb8, 0x17, 0x2f, - 0xaf, 0x52, 0x48, 0x01, 0xd2, 0xfc, 0x01, 0x3c, 0x3b, 0xbe, 0x3d, 0xd2, 0x7d, 0x2a, 0x32, 0x41, 0x24, 0xe4, 0xed, - 0x82, 0xb0, 0xe2, 0xb1, 0xa7, 0xb0, 0x69, 0x64, 0x41, 0x9f, 0x4a, 0xe8, 0x12, 0x7e, 0x8a, 0x7c, 0x79, 0x39, 0x17, - 0xfc, 0x18, 0xd2, 0x09, 0x68, 0xb0, 0x3b, 0xeb, 0x45, 0x50, 0x06, 0x39, 0xed, 0x2d, 0xa5, 0x79, 0x27, 0x97, 0x8d, - 0x02, 0xd3, 0x96, 0x85, 0xf6, 0x4b, 0xa3, 0x6e, 0xba, 0x78, 0x6a, 0xa2, 0x10, 0xf0, 0xf0, 0xb0, 0xd9, 0xed, 0xa4, - 0xa1, 0x9c, 0x55, 0x73, 0xef, 0xab, 0x55, 0xe3, 0x8a, 0xe4, 0xe3, 0x61, 0x86, 0x20, 0xa4, 0xdd, 0x8e, 0x9c, 0x1a, - 0xc3, 0x51, 0xd1, 0xbe, 0x48, 0xd6, 0x79, 0xe2, 0x70, 0xdc, 0xcb, 0x27, 0x71, 0xb2, 0x71, 0xac, 0x8b, 0x93, 0x48, - 0x05, 0xbe, 0x58, 0x7d, 0xd5, 0x10, 0x6d, 0xa6, 0xc5, 0xe9, 0x5d, 0x55, 0xa5, 0x6a, 0x0a, 0xb4, 0x93, 0x22, 0x47, - 0x76, 0x33, 0xbb, 0x2b, 0xb6, 0xa1, 0xd0, 0x0c, 0x38, 0x7f, 0xd6, 0x5e, 0xac, 0x47, 0x78, 0xa8, 0xbc, 0xf8, 0x47, - 0xd1, 0x3f, 0x56, 0x3d, 0x91, 0x65, 0x2b, 0xfc, 0xd5, 0x78, 0xbd, 0xb4, 0xf8, 0x37, 0x0f, 0xdc, 0x67, 0xd7, 0xd9, - 0x91, 0xb7, 0xde, 0x9c, 0x8f, 0x57, 0x15, 0x4f, 0x17, 0x89, 0x6f, 0x18, 0x06, 0x70, 0x39, 0xa4, 0x79, 0xb9, 0xdb, - 0x7b, 0x0c, 0x9f, 0x86, 0x80, 0x90, 0x6c, 0xe7, 0xdc, 0x3e, 0x9f, 0x3f, 0x1c, 0x69, 0x33, 0x9c, 0xc9, 0x4b, 0x21, - 0xd9, 0x57, 0x08, 0x00, 0x64, 0xd5, 0x66, 0xa4, 0x63, 0x5d, 0x4d, 0x02, 0x69, 0x32, 0x49, 0xdd, 0x6e, 0x03, 0x5c, - 0x80, 0x54, 0x94, 0x2f, 0xd7, 0x83, 0x15, 0x35, 0xf5, 0xc2, 0x14, 0x5f, 0xee, 0xe5, 0x0b, 0x34, 0xad, 0x69, 0xda, - 0xcb, 0xb9, 0x0c, 0x05, 0xd6, 0xcb, 0x0e, 0x11, 0x1e, 0x64, 0x2b, 0xc6, 0xe3, 0x71, 0xe4, 0xbb, 0xc9, 0x07, 0x94, - 0x1b, 0x2c, 0x2e, 0xf7, 0xea, 0xcb, 0xa9, 0xdd, 0x14, 0xb6, 0x42, 0x9f, 0x61, 0x15, 0x05, 0x73, 0xc0, 0x9b, 0x6b, - 0x7a, 0x3b, 0x9b, 0x0b, 0xb2, 0xd9, 0xc5, 0x67, 0x0b, 0xdb, 0x20, 0x81, 0x78, 0x1c, 0x06, 0x6b, 0x72, 0x88, 0x94, - 0x78, 0x74, 0x4a, 0x53, 0x42, 0x01, 0xc8, 0x00, 0x5e, 0x4c, 0xe2, 0x2d, 0x24, 0xfd, 0xf7, 0xe0, 0x13, 0xec, 0xad, - 0x71, 0xc5, 0xc8, 0x79, 0xfe, 0xe1, 0x74, 0xc0, 0xe9, 0xcf, 0xed, 0x9d, 0xcf, 0x3d, 0x23, 0xa0, 0x46, 0xa9, 0x0f, - 0xe5, 0xc1, 0x7f, 0xd2, 0x15, 0x9d, 0xd6, 0x62, 0xbe, 0x13, 0xb1, 0x4a, 0x85, 0x2d, 0xf7, 0x32, 0xd8, 0xdf, 0xef, - 0x87, 0xe9, 0xff, 0xab, 0x6b, 0x43, 0x55, 0x7e, 0xfe, 0xb7, 0x35, 0xfc, 0x27, 0xbd, 0x0e, 0x4b, 0xcd, 0xfd, 0x6f, - 0x0d, 0x36, 0xfd, 0xf6, 0x1a, 0xea, 0xa1, 0x6d, 0xff, 0xd6, 0x03, 0x88, 0x3a, 0x28, 0x72, 0xb3, 0x27, 0xb2, 0xd2, - 0xaa, 0x73, 0x0f, 0x06, 0xda, 0xc2, 0xff, 0x9f, 0xe5, 0x3d, 0xcb, 0x9e, 0xad, 0x30, 0xb5, 0xf0, 0xf1, 0xfd, 0x8c, - 0x49, 0x00, 0xcb, 0x49, 0x84, 0x36, 0x0e, 0x39, 0xad, 0xfc, 0xb4, 0x46, 0xae, 0x43, 0x5a, 0xb1, 0x56, 0x01, 0xfd, - 0xb2, 0xa4, 0x4f, 0x10, 0xcf, 0x3d, 0x8c, 0xbd, 0x86, 0x92, 0xe0, 0x81, 0x7a, 0xbe, 0x75, 0x94, 0x1f, 0x49, 0xd3, - 0x62, 0x57, 0x4a, 0x7e, 0xe9, 0x9f, 0x3f, 0x66, 0xd9, 0x57, 0x96, 0x1f, 0x88, 0xa1, 0x26, 0xb7, 0xff, 0xc1, 0x42, - 0xda, 0x17, 0x24, 0x31, 0x16, 0xa6, 0x6e, 0x5d, 0x38, 0x9e, 0x38, 0xbd, 0x63, 0x5b, 0xb5, 0x19, 0x84, 0x17, 0x55, - 0x2d, 0x14, 0x67, 0xd7, 0x82, 0x32, 0xa6, 0xf7, 0xe9, 0x4c, 0x13, 0x0c, 0xa8, 0xa5, 0xe4, 0x9d, 0xdf, 0xf0, 0xef, - 0x6c, 0x85, 0x79, 0x57, 0x63, 0xee, 0xde, 0xc0, 0x3e, 0x1a, 0x39, 0x8c, 0xe3, 0x3e, 0x42, 0xa1, 0x6e, 0x70, 0x83, - 0x2f, 0x35, 0x12, 0xdd, 0xb3, 0x65, 0x1a, 0x46, 0x54, 0xf6, 0xbc, 0x05, 0x47, 0xe2, 0x9c, 0x71, 0x09, 0x32, 0xf4, - 0x08, 0x0d, 0xcb, 0x69, 0x78, 0x8b, 0x29, 0x6c, 0x2f, 0xef, 0x18, 0x77, 0x96, 0xad, 0xed, 0x55, 0x9a, 0x21, 0x90, - 0xce, 0x8b, 0xe0, 0xad, 0xe2, 0x49, 0xb8, 0x31, 0x6d, 0xcf, 0xd4, 0x83, 0x5d, 0x7b, 0x49, 0x2f, 0x6a, 0xf3, 0x37, - 0xb2, 0xdb, 0x7b, 0xe9, 0x98, 0x29, 0xcd, 0xeb, 0x9a, 0x2d, 0x5e, 0xbc, 0x20, 0x13, 0x7e, 0x1c, 0x5c, 0x1b, 0xb3, - 0x6e, 0xb7, 0x12, 0x80, 0xcc, 0x89, 0xc6, 0xd5, 0x5c, 0xec, 0x7f, 0xda, 0x1f, 0xa4, 0xf5, 0x60, 0xde, 0x3d, 0xb8, - 0x92, 0x11, 0x9b, 0xbf, 0x33, 0x37, 0x92, 0x7d, 0x93, 0x49, 0x0e, 0xb5, 0xa8, 0xaa, 0xe2, 0xc1, 0xbb, 0x17, 0xc9, - 0xdd, 0xd5, 0xa5, 0x25, 0xa3, 0xde, 0x20, 0x9f, 0xef, 0xd0, 0xcd, 0x3e, 0xac, 0xdb, 0x5a, 0xe3, 0xd4, 0xe2, 0x24, - 0x36, 0x4d, 0xac, 0xc2, 0xac, 0xa6, 0x13, 0xc1, 0xf6, 0xbf, 0xd6, 0xe0, 0x9a, 0x89, 0x3a, 0x14, 0xd6, 0x56, 0x28, - 0x94, 0x82, 0x1f, 0x25, 0x20, 0x61, 0xc6, 0x98, 0x13, 0x70, 0x82, 0x64, 0x4c, 0x27, 0x53, 0xa2, 0x69, 0x28, 0x37, - 0x3f, 0x88, 0x19, 0xbe, 0xcd, 0x28, 0x46, 0x40, 0x72, 0x3f, 0x32, 0x72, 0xc3, 0xc9, 0x92, 0x50, 0x23, 0xee, 0xf6, - 0xc9, 0x2f, 0x70, 0xcc, 0x78, 0x8e, 0xa5, 0xd4, 0xf8, 0x69, 0x7d, 0x7e, 0xcc, 0x7a, 0x3f, 0x5d, 0xff, 0xb0, 0xba, - 0xe7, 0xce, 0x3f, 0x28, 0xe9, 0xd4, 0x5c, 0x43, 0x66, 0x55, 0x00, 0xc8, 0x9b, 0xf2, 0xce, 0xb8, 0x8e, 0xd3, 0x7b, - 0xab, 0x44, 0x04, 0x2e, 0x55, 0xb4, 0xaa, 0x31, 0x82, 0xf9, 0x5e, 0x88, 0x18, 0x27, 0x2b, 0x07, 0xbe, 0xf7, 0x2b, - 0x54, 0x24, 0xe7, 0xe1, 0x73, 0xf6, 0x46, 0x9a, 0x3e, 0x16, 0x4d, 0x26, 0xcf, 0x1d, 0xf1, 0x55, 0x7c, 0x7e, 0x37, - 0x4b, 0x17, 0x9b, 0x6c, 0x0e, 0x52, 0xc1, 0x92, 0x86, 0xba, 0x80, 0xda, 0xd6, 0x62, 0x28, 0xd1, 0x8e, 0xd4, 0xea, - 0x84, 0x2f, 0xa5, 0x80, 0xa5, 0x32, 0x22, 0x67, 0xa8, 0xad, 0xc1, 0xa9, 0xa3, 0x34, 0x71, 0xdd, 0xab, 0x0a, 0xbe, - 0x28, 0xf3, 0xc8, 0x9d, 0x31, 0xfc, 0xd2, 0xc7, 0xeb, 0x90, 0x8c, 0x91, 0x69, 0x36, 0x70, 0x7e, 0x9a, 0x15, 0xeb, - 0x1d, 0x7c, 0x21, 0x74, 0xea, 0xd4, 0x4c, 0xbe, 0x40, 0xdd, 0x0a, 0x4a, 0x32, 0x1c, 0x7c, 0xad, 0x8a, 0x5b, 0xb4, - 0x12, 0xf7, 0x1f, 0x90, 0xf5, 0x49, 0x2b, 0x69, 0xd1, 0x9e, 0x56, 0x56, 0x04, 0xa5, 0x65, 0x52, 0xb5, 0x29, 0x4c, - 0xbf, 0x14, 0x1d, 0xd5, 0xd3, 0xba, 0x7b, 0x3f, 0xe4, 0x76, 0xc9, 0x25, 0xdb, 0x7d, 0x8b, 0x34, 0x34, 0xba, 0xda, - 0x15, 0x80, 0xb4, 0xeb, 0x4d, 0x5f, 0x85, 0xcc, 0x53, 0xd2, 0x94, 0x92, 0x1e, 0x1c, 0xb2, 0x23, 0x34, 0xbf, 0xef, - 0xc6, 0x56, 0x1d, 0xe9, 0x4e, 0x05, 0xfb, 0xce, 0x2f, 0x73, 0xbb, 0x19, 0x9c, 0xc4, 0xe7, 0x36, 0x7e, 0xed, 0x11, - 0x40, 0xb6, 0xa8, 0x84, 0xaf, 0x4d, 0x39, 0x68, 0x97, 0x5f, 0xe2, 0x99, 0x9a, 0x1d, 0x0a, 0xef, 0xf3, 0xd6, 0x69, - 0xba, 0x75, 0x6c, 0x94, 0x4a, 0x1e, 0x7e, 0xa3, 0x42, 0xb6, 0x62, 0x77, 0x56, 0xb8, 0x00, 0x73, 0xfe, 0xaa, 0x20, - 0xea, 0x4a, 0x56, 0xdb, 0x45, 0x8d, 0xc1, 0x06, 0xda, 0x38, 0xd4, 0x2b, 0x44, 0xcc, 0x3b, 0x46, 0x39, 0x42, 0x87, - 0xa4, 0x43, 0x49, 0x27, 0xd3, 0x40, 0x4e, 0xac, 0x3a, 0x24, 0xd8, 0x9f, 0x8e, 0x94, 0x03, 0xf8, 0x9f, 0x4c, 0x91, - 0xe5, 0x9f, 0xea, 0x55, 0xce, 0xd4, 0x29, 0xfe, 0x5c, 0xb2, 0x6b, 0x76, 0x94, 0x5a, 0x4d, 0x35, 0xee, 0x17, 0x4d, - 0x01, 0xa3, 0x52, 0x5e, 0xcb, 0x8e, 0xdc, 0xcc, 0x91, 0x14, 0xff, 0x60, 0xb2, 0xf4, 0xa4, 0x7f, 0x7c, 0xc8, 0xa5, - 0xaf, 0x9c, 0x7b, 0xf5, 0xce, 0x22, 0xa7, 0x2a, 0xdd, 0xfd, 0x34, 0x77, 0x9e, 0xfe, 0xfe, 0x92, 0x9d, 0x1f, 0xfd, - 0xc5, 0x43, 0x74, 0x86, 0xbf, 0x60, 0x43, 0xec, 0xc1, 0xda, 0x65, 0xe1, 0xc9, 0xeb, 0xf3, 0x43, 0xa3, 0x4f, 0x19, - 0x58, 0xf2, 0xee, 0x82, 0x96, 0x40, 0x99, 0xd7, 0x94, 0xa5, 0x5a, 0xdf, 0x17, 0xd3, 0xa7, 0x2b, 0x76, 0xbe, 0x98, - 0x55, 0x5b, 0x6d, 0xdf, 0x97, 0xd5, 0x6d, 0x75, 0xff, 0x72, 0xf6, 0xe1, 0xaf, 0xdb, 0x7b, 0x3e, 0x31, 0x01, 0x08, - 0xec, 0xf4, 0x50, 0xf5, 0x8b, 0x9f, 0xab, 0xb2, 0x98, 0xaa, 0xba, 0x38, 0xab, 0xc6, 0xc5, 0x79, 0x35, 0x3d, 0xfc, - 0x74, 0xc4, 0x0f, 0x3c, 0x12, 0x86, 0xd5, 0x89, 0x06, 0x59, 0x5b, 0xfc, 0xd2, 0xd4, 0x32, 0xcb, 0x27, 0x8a, 0xdd, - 0x4a, 0xad, 0x3f, 0xed, 0xd2, 0xf8, 0xd3, 0x64, 0x79, 0x23, 0x05, 0xbd, 0x52, 0xd1, 0x2e, 0x27, 0xb6, 0xd3, 0x4c, - 0x2c, 0x48, 0x2c, 0x65, 0xa7, 0xbd, 0xb5, 0x0e, 0x39, 0x83, 0x41, 0x6f, 0xbf, 0xe4, 0x1a, 0xcf, 0x22, 0x8c, 0x99, - 0xbc, 0xa1, 0xb7, 0x4c, 0x05, 0x5f, 0xa1, 0x1a, 0x33, 0xeb, 0x3b, 0x51, 0x47, 0x12, 0x0b, 0x82, 0x18, 0xba, 0xd4, - 0x49, 0xed, 0xed, 0xd2, 0xd5, 0xad, 0xab, 0xbe, 0x04, 0x70, 0x2d, 0xd6, 0x94, 0x9e, 0xfa, 0xa2, 0x46, 0x31, 0x3a, - 0x2a, 0x4b, 0x66, 0xaa, 0x84, 0x8a, 0x1e, 0x62, 0x7d, 0xcb, 0xbc, 0xce, 0xca, 0x73, 0x33, 0x4c, 0xd3, 0x2d, 0xcd, - 0x00, 0x5f, 0xd1, 0x85, 0xac, 0xcc, 0x05, 0x6f, 0x29, 0x99, 0xd6, 0x23, 0xe3, 0x54, 0xd3, 0xba, 0x7a, 0x44, 0xf6, - 0xf2, 0x97, 0xb7, 0x40, 0x64, 0x1f, 0xfa, 0xa2, 0xf6, 0x59, 0x94, 0xad, 0x30, 0x89, 0x41, 0xa6, 0x21, 0xe4, 0x28, - 0x0d, 0xd1, 0x88, 0xb3, 0x78, 0xb4, 0xab, 0x20, 0xb1, 0xf1, 0x59, 0x7e, 0xcd, 0x8c, 0xbd, 0x0e, 0x20, 0x16, 0xa8, - 0xb8, 0x2c, 0xbd, 0xe0, 0xff, 0x41, 0x0d, 0xe5, 0xbe, 0xe9, 0x7f, 0xa0, 0x98, 0x14, 0xca, 0xcd, 0xd0, 0x8f, 0x4b, - 0xae, 0x60, 0x13, 0x62, 0xd0, 0x83, 0x15, 0x51, 0x9d, 0xc5, 0xbe, 0x45, 0x9d, 0x40, 0x0a, 0x38, 0x50, 0x9c, 0x41, - 0xe3, 0x44, 0x01, 0x8e, 0x06, 0xad, 0xb5, 0x48, 0x85, 0x50, 0x78, 0x3f, 0xea, 0xaa, 0x75, 0x39, 0xd2, 0xd0, 0x4d, - 0xa4, 0xdf, 0xea, 0xd7, 0x56, 0x94, 0xc1, 0x9c, 0x5f, 0xae, 0xbc, 0xf9, 0xa0, 0xe4, 0xef, 0xdb, 0x3f, 0xa9, 0x0b, - 0x54, 0xf4, 0x0e, 0x1c, 0x46, 0xb4, 0x39, 0x62, 0x6c, 0x61, 0x71, 0x18, 0x5b, 0xea, 0x09, 0xb1, 0xfe, 0x0e, 0x3d, - 0xc2, 0xd9, 0x37, 0x49, 0xad, 0x79, 0x39, 0x99, 0xe5, 0x76, 0x3b, 0xba, 0xdd, 0xf9, 0x99, 0x29, 0xfc, 0xa4, 0xe6, - 0x60, 0x51, 0xef, 0x49, 0xa4, 0x01, 0xba, 0x5e, 0x38, 0x8f, 0xc0, 0xf5, 0x28, 0x49, 0xc1, 0x64, 0x40, 0x13, 0x1a, - 0x3b, 0x62, 0x65, 0xc5, 0x59, 0x1a, 0x8d, 0xce, 0x85, 0xab, 0xa2, 0xfa, 0xfb, 0xcb, 0x62, 0x2e, 0x00, 0x8c, 0x20, - 0xf4, 0xc1, 0x1b, 0xbb, 0x9d, 0x36, 0xbd, 0xda, 0x96, 0x34, 0xc4, 0x11, 0x44, 0x65, 0x41, 0xc5, 0x2e, 0xa8, 0x3a, - 0xda, 0x2f, 0xa8, 0x1c, 0x27, 0xd5, 0x90, 0x9f, 0x7a, 0x65, 0xb9, 0x0b, 0xfe, 0xdc, 0xa3, 0x5a, 0xfd, 0xf3, 0x43, - 0xc3, 0x53, 0xfd, 0x43, 0x98, 0xf7, 0x95, 0xf2, 0x3c, 0x97, 0x7c, 0x6c, 0x12, 0xc9, 0xd5, 0x56, 0x05, 0x1f, 0x1e, - 0x4a, 0x7a, 0x2b, 0x6a, 0x16, 0x58, 0x6f, 0x0f, 0xcf, 0x6b, 0xcf, 0x61, 0xc6, 0x8e, 0xfa, 0x25, 0x51, 0x37, 0x67, - 0xff, 0x0d, 0x06, 0xf6, 0x9b, 0x56, 0x72, 0xae, 0x9b, 0xf5, 0x9e, 0x27, 0xc5, 0x7a, 0x3d, 0xbf, 0xa2, 0x81, 0x8d, - 0x7d, 0xf6, 0x99, 0x3f, 0xa0, 0x61, 0x90, 0x3d, 0x5d, 0x37, 0xe7, 0xb4, 0xce, 0xce, 0xb9, 0x72, 0xd8, 0x69, 0x33, - 0x7e, 0xd2, 0xbd, 0xe5, 0xa0, 0xda, 0x02, 0xf9, 0x9d, 0xfd, 0x84, 0x38, 0x69, 0xf9, 0xf9, 0x69, 0xb4, 0x33, 0x0b, - 0x21, 0x0f, 0xce, 0x76, 0x2b, 0x20, 0xe5, 0x65, 0x76, 0x01, 0x49, 0x73, 0xa1, 0xe7, 0x38, 0x2a, 0x45, 0x82, 0x2f, - 0x03, 0x66, 0xdd, 0x35, 0x02, 0xd3, 0xf5, 0x6e, 0x65, 0xde, 0xc5, 0xaa, 0x06, 0x9d, 0xd7, 0x36, 0x6d, 0xdf, 0x7c, - 0xa5, 0x3b, 0x9e, 0xbe, 0x28, 0x16, 0x3b, 0xac, 0xdc, 0xe5, 0x20, 0x7f, 0xaf, 0x04, 0x1e, 0x05, 0xf0, 0x5e, 0x4c, - 0xd2, 0x4f, 0xf0, 0x74, 0x27, 0x13, 0x98, 0xa8, 0x86, 0xa4, 0x6c, 0x75, 0x77, 0x23, 0x9b, 0x51, 0x35, 0xd0, 0x29, - 0x47, 0x8e, 0x78, 0xf5, 0xb3, 0xf6, 0x98, 0x07, 0x3b, 0xf7, 0xad, 0x17, 0x7e, 0x94, 0x0d, 0x15, 0x96, 0x67, 0x0c, - 0x0d, 0x38, 0x65, 0x58, 0x5c, 0xc6, 0x60, 0x40, 0x6e, 0xae, 0xe3, 0x46, 0x0a, 0xcd, 0x3f, 0x47, 0x3f, 0xa6, 0xa0, - 0x06, 0xea, 0x8d, 0xeb, 0xf1, 0xa1, 0x19, 0xec, 0x97, 0xbf, 0x01, 0x8f, 0x0f, 0x32, 0xa0, 0x9a, 0x85, 0xce, 0x68, - 0xe3, 0x69, 0x9e, 0x7f, 0xd2, 0xb7, 0xb9, 0xe4, 0xfc, 0x47, 0xff, 0x34, 0x1b, 0xa7, 0xce, 0xc9, 0x99, 0x26, 0xc1, - 0x79, 0x0a, 0x5d, 0x9d, 0xfd, 0x7f, 0x97, 0x6c, 0x64, 0x15, 0x2f, 0x9a, 0x47, 0x71, 0x75, 0x81, 0x28, 0xaa, 0xf5, - 0x91, 0x67, 0xed, 0xce, 0x5e, 0xec, 0x7b, 0x38, 0x0c, 0x7a, 0x83, 0x0f, 0x7e, 0xaa, 0xf2, 0x24, 0x66, 0xfd, 0xca, - 0x44, 0xca, 0x25, 0x7e, 0x4a, 0x5d, 0xd9, 0xd7, 0x49, 0xb3, 0x0f, 0x97, 0xa6, 0x34, 0x1c, 0xd8, 0x94, 0x62, 0x8d, - 0x0a, 0xb0, 0x5f, 0x89, 0xd2, 0xb7, 0x76, 0xce, 0xd0, 0x07, 0xff, 0xac, 0x0a, 0x2c, 0x4e, 0xeb, 0x32, 0x40, 0x52, - 0xd7, 0xe3, 0xca, 0x7e, 0x3d, 0x09, 0x88, 0x8b, 0x7c, 0x85, 0x36, 0x47, 0x8c, 0x51, 0x91, 0x0b, 0xd1, 0x41, 0xe6, - 0xaa, 0x62, 0xa2, 0xd6, 0xa7, 0x17, 0xb4, 0xfb, 0x6e, 0x22, 0x2e, 0xd4, 0xd0, 0xf9, 0x57, 0x27, 0x16, 0x94, 0x36, - 0xc7, 0xf6, 0x8e, 0xd0, 0x23, 0x97, 0xf1, 0x11, 0x41, 0x12, 0x5f, 0x4f, 0x61, 0xde, 0x7e, 0xc7, 0x8f, 0xab, 0x08, - 0x20, 0x81, 0x77, 0x8b, 0xb8, 0x19, 0x18, 0x4a, 0x12, 0xa8, 0x9a, 0x5a, 0xeb, 0x01, 0x13, 0xf3, 0x4e, 0x47, 0xe1, - 0x56, 0x54, 0x20, 0xf0, 0x10, 0x99, 0xd8, 0x83, 0x44, 0x56, 0x8f, 0xa2, 0x87, 0x3b, 0xda, 0xe9, 0x4a, 0xa6, 0x68, - 0x04, 0x25, 0xda, 0xf4, 0x90, 0xa4, 0x87, 0x2f, 0x9b, 0x89, 0xde, 0x89, 0x73, 0xd3, 0x1f, 0xf5, 0x5e, 0xcb, 0xfe, - 0x77, 0x5d, 0x47, 0xf6, 0x2e, 0x63, 0x44, 0xcc, 0xe1, 0x51, 0xb6, 0x9e, 0xac, 0x8e, 0xdb, 0x3e, 0xe4, 0xdc, 0x0b, - 0x8a, 0x01, 0x68, 0x6f, 0x0e, 0xdd, 0x77, 0xa5, 0x44, 0xad, 0xeb, 0xd6, 0x43, 0xca, 0x35, 0x12, 0xfd, 0xc5, 0xf7, - 0xe7, 0x77, 0xb5, 0xc9, 0xc9, 0x26, 0x0a, 0x15, 0x4d, 0xf2, 0x18, 0x44, 0x87, 0x97, 0xc6, 0x30, 0xea, 0xc5, 0xc5, - 0x18, 0xb1, 0xa7, 0xd3, 0x28, 0x6e, 0x61, 0x31, 0x5a, 0x65, 0x6f, 0x11, 0x62, 0x5d, 0x3a, 0x35, 0x4c, 0x51, 0xf5, - 0xdf, 0x9f, 0x46, 0xb5, 0x3b, 0x05, 0x11, 0xf8, 0x7a, 0xee, 0x58, 0xb2, 0x0b, 0xa8, 0x97, 0xf3, 0x77, 0xac, 0x68, - 0xd3, 0x69, 0x1f, 0x84, 0x71, 0x8c, 0xcc, 0x7b, 0xf9, 0xb6, 0x08, 0x31, 0x94, 0x12, 0xa4, 0xe0, 0x6b, 0xc7, 0x30, - 0x08, 0x0e, 0xf3, 0xf2, 0x31, 0xb4, 0xff, 0x10, 0xee, 0xc8, 0x8c, 0x31, 0x99, 0xe2, 0xde, 0x00, 0xeb, 0x0d, 0x77, - 0xd8, 0x47, 0x47, 0xbd, 0xd2, 0xe4, 0x4e, 0x12, 0x7b, 0x9a, 0x49, 0x8e, 0xde, 0xed, 0xd2, 0x28, 0x53, 0x3a, 0x7c, - 0x33, 0x89, 0xf8, 0x56, 0x9c, 0x10, 0xa9, 0xba, 0xac, 0xad, 0xae, 0xfd, 0xbe, 0x74, 0x1c, 0xdd, 0xb3, 0x6b, 0xbd, - 0x8f, 0x62, 0x6c, 0xd5, 0x9b, 0x9a, 0x6d, 0xea, 0xa7, 0xa1, 0x40, 0x8e, 0x0e, 0x77, 0xba, 0x95, 0x4c, 0xc7, 0xea, - 0xf2, 0x17, 0x6d, 0x5b, 0xe4, 0x0b, 0x03, 0x98, 0x9e, 0xba, 0xb7, 0x59, 0xed, 0x27, 0x44, 0x89, 0xf4, 0x81, 0x98, - 0x25, 0x3e, 0x4a, 0x01, 0xe3, 0x2b, 0xa7, 0x89, 0x6c, 0xf0, 0xb3, 0xfc, 0x5c, 0xc4, 0xed, 0xae, 0xf1, 0x9c, 0x4f, - 0x00, 0xbd, 0x1f, 0x8f, 0xb3, 0x33, 0x68, 0xe7, 0xdb, 0x74, 0xa6, 0x53, 0x79, 0x31, 0xfd, 0xb3, 0xff, 0xcf, 0xf4, - 0x40, 0xfd, 0x01, 0x24, 0x1a, 0xff, 0xf7, 0x22, 0x93, 0xd7, 0x6a, 0x24, 0x26, 0x07, 0x31, 0xea, 0x1e, 0x14, 0x8b, - 0x68, 0x08, 0xe0, 0x2b, 0x2f, 0x88, 0x1b, 0x1c, 0x1e, 0x15, 0x3e, 0x4d, 0xef, 0x0e, 0xe4, 0x70, 0xa7, 0xe3, 0x49, - 0x5b, 0xdc, 0x57, 0xc9, 0xcd, 0x8c, 0xfd, 0x3e, 0x83, 0x68, 0x18, 0x14, 0x7d, 0x81, 0x41, 0x29, 0xe4, 0xe7, 0x4b, - 0xf1, 0xa5, 0x99, 0xab, 0x2b, 0xa3, 0xa4, 0xb5, 0x82, 0xf5, 0x2a, 0xa4, 0x06, 0x12, 0xef, 0xa5, 0xf0, 0x19, 0xf4, - 0x14, 0x8a, 0xfd, 0xfe, 0xd4, 0x29, 0x27, 0x68, 0x2f, 0xab, 0xd2, 0xa4, 0x57, 0x92, 0xdb, 0x7b, 0x67, 0x1d, 0xfd, - 0x04, 0x28, 0xc7, 0x0f, 0xa2, 0xc5, 0xd7, 0x0e, 0x8b, 0x72, 0xbb, 0x54, 0x75, 0x1c, 0x43, 0xf0, 0xfc, 0xc9, 0xb3, - 0xb0, 0x5d, 0x91, 0x9e, 0xfe, 0x6d, 0xb1, 0xe9, 0xbb, 0x73, 0xab, 0xe1, 0xff, 0xe4, 0xb3, 0x3f, 0xf0, 0x36, 0x3d, - 0xeb, 0xcf, 0xd8, 0x48, 0xe5, 0x5d, 0xc2, 0xe5, 0x36, 0xb1, 0xf9, 0x02, 0x86, 0xe1, 0x71, 0x7b, 0x9e, 0x08, 0x89, - 0xfd, 0xa6, 0x30, 0xb3, 0xc7, 0xb1, 0x68, 0x25, 0xc2, 0xdf, 0xee, 0x46, 0xde, 0xf9, 0x4f, 0x87, 0x25, 0x08, 0xc3, - 0xb9, 0x71, 0xa6, 0xdf, 0x33, 0xda, 0x7f, 0x9a, 0xa7, 0x4f, 0x7f, 0x77, 0xc9, 0xe9, 0x8f, 0xfe, 0x69, 0xf6, 0xbd, - 0x7d, 0x55, 0xa2, 0x77, 0xc0, 0x66, 0xdf, 0x44, 0x8c, 0x9a, 0xbc, 0x9e, 0x53, 0x0e, 0x7a, 0x44, 0x57, 0x33, 0xe1, - 0xe5, 0x09, 0x5c, 0xa0, 0x61, 0x54, 0xe7, 0x3d, 0xcf, 0xc1, 0x0b, 0x65, 0xbb, 0xa3, 0x58, 0x92, 0x68, 0xb3, 0x90, - 0x3b, 0xf4, 0x53, 0x83, 0x28, 0xc1, 0xac, 0xfb, 0x49, 0xb2, 0x47, 0x6d, 0x35, 0x4c, 0xac, 0x52, 0x5d, 0x7c, 0xe7, - 0x5a, 0x26, 0x29, 0xe5, 0x55, 0xbc, 0x53, 0x89, 0xbc, 0xf9, 0x21, 0xcc, 0x98, 0x0d, 0x46, 0x2f, 0x84, 0xb0, 0xdf, - 0x29, 0x02, 0x23, 0x47, 0x15, 0x2c, 0x24, 0x7e, 0xbb, 0x03, 0x24, 0xde, 0xbe, 0x0b, 0xd2, 0x57, 0x12, 0x20, 0x5f, - 0xcb, 0x96, 0x53, 0x9b, 0x9d, 0x1b, 0xe1, 0xb0, 0x47, 0xe9, 0x1b, 0xef, 0x91, 0x6f, 0x64, 0xd2, 0x56, 0xa9, 0x1f, - 0x03, 0xcc, 0xce, 0xd6, 0x61, 0x64, 0xc4, 0x0e, 0xe4, 0x10, 0x53, 0xb1, 0x03, 0x04, 0xb3, 0x0e, 0xfd, 0x1c, 0xf8, - 0x63, 0xd7, 0x0d, 0x40, 0x34, 0x6b, 0x2e, 0x7d, 0x92, 0xb1, 0x9d, 0x1c, 0x8e, 0x4d, 0x04, 0xe3, 0x7d, 0xa9, 0xfb, - 0xac, 0x79, 0x8a, 0x94, 0x6a, 0x89, 0x14, 0x34, 0x20, 0xbd, 0x8a, 0x3b, 0xf7, 0x6c, 0x0e, 0x46, 0x9c, 0xec, 0xef, - 0x4a, 0xa9, 0x3e, 0xdc, 0xb8, 0xcb, 0xa1, 0x71, 0x5e, 0x1e, 0xb0, 0x8b, 0xcd, 0xa0, 0x04, 0xda, 0xe9, 0x34, 0x4f, - 0xd6, 0x1a, 0xcc, 0xb9, 0x26, 0x25, 0x29, 0x0b, 0x9f, 0x90, 0x19, 0xb9, 0xf9, 0xbe, 0xbc, 0xbe, 0xe5, 0xc3, 0x68, - 0x4e, 0x29, 0xd8, 0x2b, 0x7d, 0xd3, 0xa7, 0xfb, 0xba, 0xfc, 0xdc, 0x05, 0xdd, 0xda, 0x41, 0x2b, 0x17, 0x0f, 0xfb, - 0x93, 0x47, 0x02, 0xc8, 0x04, 0xf1, 0xc3, 0x0d, 0xcb, 0xee, 0xbe, 0x4f, 0x60, 0xf6, 0x8d, 0x5f, 0xec, 0xa7, 0x0c, - 0x83, 0x6f, 0xec, 0x66, 0x95, 0x60, 0x39, 0xfc, 0x3f, 0xf7, 0xcf, 0xb6, 0x5e, 0xec, 0x26, 0x87, 0xab, 0xfd, 0xba, - 0x7d, 0x06, 0x18, 0x7b, 0xbf, 0x5c, 0x27, 0x54, 0xc2, 0x48, 0x6d, 0xd1, 0xe4, 0xab, 0xc2, 0x99, 0x3d, 0x9c, 0x4c, - 0xd9, 0x4e, 0xa1, 0x16, 0x69, 0x1c, 0xd7, 0x39, 0x47, 0x5a, 0xa0, 0x8d, 0x65, 0xb1, 0x68, 0x14, 0x09, 0x9d, 0x60, - 0x8b, 0x8d, 0x1c, 0xf7, 0xc3, 0xfa, 0x6c, 0x98, 0xf1, 0x96, 0x28, 0xb4, 0xe0, 0x6c, 0xc4, 0x44, 0x90, 0x51, 0x35, - 0x06, 0xa1, 0x1d, 0x72, 0xb0, 0x00, 0xd5, 0xd0, 0x29, 0x82, 0xe7, 0xc6, 0x9f, 0x16, 0x3f, 0x2e, 0x0c, 0x5e, 0x42, - 0x32, 0x0c, 0x12, 0x40, 0x8a, 0xc9, 0x4a, 0xba, 0x71, 0x6f, 0xb7, 0x70, 0xbc, 0x2f, 0x98, 0x6a, 0xec, 0xa7, 0xdd, - 0xa3, 0x9b, 0x0e, 0xd4, 0x8b, 0x8f, 0x06, 0x86, 0xed, 0x8e, 0x21, 0xf3, 0xca, 0x88, 0xce, 0x44, 0xcf, 0xfb, 0x38, - 0xe9, 0xb1, 0x55, 0x98, 0x23, 0xcc, 0x08, 0xbe, 0x31, 0x99, 0x8d, 0x3c, 0xc2, 0xdd, 0x6e, 0x3f, 0x9a, 0xe3, 0xd8, - 0x1a, 0x7b, 0x85, 0x50, 0xa8, 0x78, 0xcb, 0x74, 0x37, 0xa1, 0x59, 0x87, 0xcd, 0x3d, 0xd4, 0xd9, 0x55, 0x06, 0xfa, - 0x2c, 0xab, 0x04, 0x27, 0xf2, 0xf6, 0xdb, 0xe8, 0x42, 0x03, 0x27, 0x68, 0x6b, 0xa3, 0x87, 0x7f, 0x88, 0xd0, 0xb7, - 0xa0, 0x4e, 0x38, 0x29, 0xdf, 0x19, 0x8f, 0x89, 0x41, 0xd4, 0x38, 0x4e, 0x95, 0x59, 0x4e, 0x4f, 0x76, 0x23, 0x57, - 0x4a, 0xae, 0xb0, 0x9c, 0x59, 0x5a, 0x36, 0x4b, 0x05, 0x78, 0xff, 0x51, 0x17, 0xc7, 0x84, 0x94, 0xab, 0x46, 0x6d, - 0xea, 0x81, 0x86, 0x4f, 0xa3, 0x95, 0x54, 0x56, 0x36, 0xf1, 0x87, 0x1e, 0xee, 0xf4, 0x07, 0xd1, 0xdd, 0x8a, 0x6a, - 0x93, 0xdb, 0xd0, 0x78, 0x42, 0x8f, 0x29, 0xec, 0x83, 0x45, 0xa0, 0xce, 0xa3, 0xf0, 0xf0, 0xf8, 0x3b, 0x26, 0x6f, - 0x24, 0xd1, 0xad, 0xc0, 0xcd, 0xe2, 0x07, 0x2e, 0x58, 0x24, 0x39, 0x5a, 0xc5, 0xd2, 0xbb, 0xd3, 0xb2, 0x35, 0xa9, - 0xfc, 0x84, 0xb6, 0xaf, 0xaf, 0xe5, 0x55, 0x0b, 0xac, 0xc4, 0xec, 0x55, 0x23, 0xf9, 0x45, 0x29, 0x0e, 0xec, 0x80, - 0x69, 0x91, 0x6b, 0x34, 0xcc, 0xd4, 0xb2, 0x79, 0x30, 0xee, 0xe9, 0x36, 0x1c, 0x4a, 0x67, 0x77, 0x7f, 0xa1, 0x09, - 0x0e, 0xa1, 0x29, 0xa9, 0x09, 0x93, 0x7c, 0x3c, 0xb5, 0x71, 0x62, 0x15, 0xb5, 0x60, 0xb2, 0xe5, 0xb8, 0xe5, 0xb5, - 0x3a, 0xa6, 0xea, 0xa5, 0xf7, 0x31, 0x90, 0x24, 0xd3, 0x38, 0xa1, 0x72, 0x70, 0x43, 0xbc, 0x42, 0xc1, 0x69, 0x7b, - 0x1a, 0x27, 0x76, 0x28, 0x6f, 0xff, 0x2a, 0xde, 0x56, 0x68, 0xfe, 0x15, 0x4e, 0xde, 0xcb, 0xf5, 0xbb, 0x6e, 0xb8, - 0x99, 0xd8, 0x0d, 0xbb, 0xfd, 0xab, 0x69, 0xab, 0x54, 0xec, 0xe9, 0xa4, 0xe7, 0x23, 0x1f, 0x00, 0xf8, 0xf3, 0xca, - 0x04, 0xf9, 0x64, 0x98, 0x11, 0xb5, 0x09, 0xc2, 0x4c, 0x65, 0xc4, 0xf8, 0xa6, 0x2a, 0x37, 0xb5, 0x68, 0x45, 0x62, - 0x49, 0x69, 0x1a, 0x67, 0xe7, 0x8e, 0x34, 0x3b, 0xee, 0x8e, 0xd8, 0x6d, 0x89, 0xb9, 0x7e, 0x9a, 0xf4, 0x34, 0x58, - 0x85, 0x22, 0x54, 0x9e, 0x50, 0xae, 0x29, 0x47, 0x7b, 0xd0, 0x8d, 0xba, 0x86, 0x0c, 0x86, 0x54, 0xa1, 0x8c, 0x5e, - 0xec, 0x3c, 0x22, 0x70, 0x54, 0xa1, 0x87, 0x0c, 0xa4, 0xa8, 0x88, 0x66, 0x33, 0x7e, 0x7c, 0xfe, 0x95, 0xa2, 0x2d, - 0xea, 0x06, 0xe1, 0x10, 0x80, 0xac, 0x77, 0x87, 0x43, 0x08, 0x5c, 0xff, 0x0e, 0xcb, 0xd6, 0xa8, 0x51, 0x46, 0x06, - 0x36, 0x64, 0x3d, 0x45, 0xfa, 0x8f, 0x51, 0x5d, 0x91, 0x49, 0xdd, 0xac, 0x50, 0x46, 0x90, 0x41, 0xcc, 0x3b, 0x4a, - 0x9b, 0x6f, 0x86, 0xd1, 0x91, 0x35, 0x8a, 0x30, 0x15, 0xbb, 0x41, 0xe1, 0xaa, 0x3f, 0x48, 0x91, 0x5d, 0x88, 0x38, - 0x05, 0x78, 0x77, 0x6a, 0x48, 0xd4, 0xac, 0xa9, 0x68, 0xf8, 0x18, 0x7a, 0xee, 0xcc, 0xbb, 0x0d, 0x07, 0x12, 0xc2, - 0x22, 0x35, 0xd8, 0x81, 0x68, 0x0b, 0x32, 0x16, 0xe1, 0x8d, 0x48, 0x34, 0xd4, 0x7b, 0x02, 0xf0, 0x6e, 0xdd, 0xa7, - 0xbc, 0x03, 0x80, 0x3e, 0x59, 0x39, 0x91, 0xee, 0x8f, 0x07, 0x72, 0x88, 0xb9, 0xd9, 0x91, 0xba, 0x43, 0x5c, 0x8a, - 0xf3, 0x89, 0x62, 0xbd, 0x20, 0x07, 0x91, 0xa0, 0x15, 0xaf, 0xc9, 0x45, 0x99, 0xb4, 0xf3, 0xae, 0x33, 0xd7, 0xb9, - 0x26, 0x9e, 0xe4, 0xa8, 0x33, 0x51, 0x4c, 0xee, 0x99, 0x7c, 0xad, 0xdb, 0xb0, 0xda, 0x41, 0x9f, 0x10, 0xe3, 0xc9, - 0x58, 0xa6, 0x1e, 0xd9, 0xd9, 0x78, 0x36, 0xe2, 0x50, 0x01, 0x2d, 0x1d, 0xdc, 0x72, 0xd9, 0xac, 0xf9, 0x19, 0x77, - 0xfc, 0xb0, 0x09, 0x1f, 0xad, 0xe2, 0xda, 0xf4, 0xe9, 0x65, 0x90, 0x06, 0xf3, 0xa1, 0xa4, 0xe0, 0x4a, 0xaa, 0xb1, - 0xef, 0x4d, 0x25, 0xb5, 0x7f, 0xb7, 0x99, 0x9a, 0xb5, 0x58, 0xf1, 0x64, 0x5c, 0x04, 0x91, 0xf9, 0xfa, 0xdd, 0xd4, - 0x8c, 0xa3, 0xdd, 0xb4, 0x20, 0x42, 0x5f, 0xe5, 0x62, 0x64, 0x39, 0xfd, 0xa6, 0x89, 0x37, 0x37, 0x84, 0x3e, 0x62, - 0xfa, 0xb3, 0x8d, 0x39, 0x3e, 0x3b, 0xbc, 0x50, 0x43, 0x0f, 0xda, 0x20, 0x22, 0x35, 0x4e, 0x77, 0xb0, 0x48, 0x64, - 0x4b, 0x78, 0x45, 0xd1, 0x8a, 0xb9, 0xfa, 0xe1, 0x90, 0xb1, 0x44, 0x26, 0x88, 0x34, 0xfa, 0xf1, 0xc3, 0x2e, 0x1d, - 0xb6, 0x1e, 0x86, 0xb1, 0x02, 0x5c, 0xe6, 0x25, 0x25, 0x6f, 0xac, 0xe0, 0xb7, 0x9f, 0x03, 0xd3, 0xbc, 0xdf, 0xde, - 0x35, 0xbd, 0x11, 0x2f, 0xd5, 0x8d, 0xd3, 0x3b, 0x14, 0x4a, 0x42, 0x94, 0xd3, 0xc6, 0xc5, 0xc5, 0x9c, 0x3d, 0x0d, - 0x2c, 0xf2, 0x72, 0xc5, 0xd2, 0x2e, 0x7e, 0x0d, 0xa2, 0x61, 0xc5, 0x3b, 0x08, 0xe9, 0x22, 0xbb, 0xce, 0xf0, 0x00, - 0x8d, 0xea, 0xe1, 0x1e, 0x6d, 0xd1, 0x05, 0x04, 0x99, 0x63, 0xf4, 0x68, 0xa0, 0x04, 0x14, 0x7c, 0xc5, 0x09, 0x74, - 0x95, 0xd6, 0xcc, 0xb3, 0x35, 0x32, 0x63, 0x02, 0x84, 0xd3, 0xfa, 0x93, 0x08, 0x2e, 0x21, 0x73, 0xb8, 0x54, 0xd8, - 0x82, 0x8c, 0x5a, 0x29, 0x4e, 0x46, 0x01, 0x4d, 0x9f, 0x88, 0xe3, 0x17, 0xbd, 0x4b, 0x01, 0x38, 0x7a, 0x2c, 0xac, - 0x24, 0xf0, 0x99, 0xc6, 0x15, 0xb3, 0xcb, 0xa0, 0x39, 0xd0, 0xb8, 0xf6, 0xb5, 0xd5, 0x18, 0x8b, 0x8d, 0xd7, 0xdf, - 0x43, 0x84, 0x0d, 0xf6, 0x94, 0x42, 0xac, 0x48, 0x74, 0x80, 0xac, 0x5c, 0x43, 0x27, 0xef, 0xd9, 0xd3, 0xb1, 0xb5, - 0x5c, 0x41, 0x17, 0x3a, 0x92, 0x70, 0xad, 0xc1, 0x66, 0xff, 0x11, 0xe0, 0x4c, 0x43, 0x5a, 0xcf, 0x0c, 0x2b, 0x72, - 0x99, 0x82, 0x1a, 0xf1, 0xaf, 0x53, 0x07, 0x8b, 0x7a, 0x48, 0x17, 0x71, 0x2a, 0xea, 0x99, 0x56, 0x16, 0xe8, 0x84, - 0x3a, 0x52, 0x43, 0x6c, 0x00, 0x05, 0x6f, 0x94, 0x9e, 0x70, 0xfa, 0xdd, 0xa5, 0xe7, 0xa8, 0x2c, 0xb8, 0x0e, 0xcd, - 0xe2, 0x0f, 0x51, 0x6d, 0x3c, 0xfd, 0xf8, 0x60, 0x06, 0x0f, 0xe2, 0xed, 0x59, 0xc0, 0x87, 0x89, 0xb7, 0x63, 0xe7, - 0x79, 0x67, 0x37, 0x01, 0xc1, 0xac, 0x34, 0x11, 0x92, 0x11, 0xe6, 0xce, 0xbd, 0xc3, 0xd6, 0xf8, 0x2b, 0x76, 0x7f, - 0x29, 0x14, 0x06, 0xdb, 0x91, 0x08, 0xf3, 0xb1, 0x18, 0x45, 0xa8, 0xed, 0xe5, 0xd7, 0x2c, 0x19, 0xc9, 0xef, 0xce, - 0x9b, 0x8b, 0xb8, 0x1d, 0xd8, 0xaa, 0x54, 0xa9, 0x1f, 0x10, 0x55, 0xed, 0xf7, 0xb2, 0x61, 0x9b, 0x85, 0x8f, 0x17, - 0x3d, 0x3b, 0xf1, 0xc1, 0x72, 0x3d, 0xc7, 0x92, 0xdf, 0x3f, 0x43, 0x40, 0xcd, 0x66, 0xfb, 0xd5, 0xe2, 0xa0, 0xcf, - 0xb5, 0xf5, 0x1b, 0xb5, 0x81, 0x7e, 0x42, 0x58, 0xe0, 0xfb, 0x79, 0x8d, 0x5c, 0x3c, 0xca, 0xe6, 0xfa, 0x81, 0xdf, - 0x78, 0xb5, 0xc0, 0x3e, 0xbb, 0x33, 0x37, 0x9c, 0x1b, 0xc2, 0xd0, 0xf6, 0x44, 0xe3, 0xfe, 0x89, 0x49, 0x08, 0xaf, - 0xb3, 0x8a, 0x29, 0x9d, 0xc8, 0xac, 0xf2, 0x4f, 0xfa, 0x9d, 0xbb, 0x9b, 0xf9, 0x08, 0x25, 0xda, 0xdf, 0x80, 0xf3, - 0x72, 0xd5, 0x7e, 0x4d, 0xf2, 0x8c, 0x96, 0x1e, 0xb0, 0xa9, 0xa5, 0x9f, 0xeb, 0x95, 0xea, 0x40, 0xe9, 0xbe, 0x03, - 0x09, 0x30, 0x50, 0x87, 0x19, 0xbf, 0x8f, 0xcd, 0x10, 0x6e, 0x4a, 0x30, 0x06, 0x9e, 0xe9, 0x3f, 0x7c, 0x81, 0x83, - 0xb3, 0x92, 0x81, 0x39, 0xa2, 0xe6, 0x15, 0x41, 0xc0, 0xe7, 0x12, 0x54, 0xc8, 0x6e, 0x05, 0xf2, 0xf3, 0xbc, 0x72, - 0xe4, 0x06, 0x90, 0x5b, 0x21, 0xa8, 0xb8, 0x27, 0xcf, 0x5c, 0x1a, 0xd0, 0x03, 0x50, 0xfe, 0xe1, 0x9c, 0x93, 0x84, - 0xfe, 0x26, 0xa0, 0xa8, 0xd1, 0x49, 0x7f, 0xfe, 0xb5, 0x66, 0x64, 0xf2, 0xe7, 0xb1, 0x5f, 0x79, 0xbc, 0xec, 0xe6, - 0x2d, 0xc8, 0x48, 0x1b, 0xdf, 0x86, 0x19, 0x99, 0x81, 0x8e, 0x55, 0x50, 0x5b, 0xf8, 0x42, 0xaa, 0x55, 0x40, 0xae, - 0x2e, 0x42, 0x8b, 0x14, 0xb7, 0x90, 0xd3, 0x9f, 0xb6, 0xb3, 0x90, 0x7f, 0x9a, 0x01, 0x8e, 0x59, 0xf9, 0xcf, 0xc6, - 0x15, 0x45, 0xf6, 0x10, 0x18, 0xcd, 0x8f, 0x2e, 0x15, 0xd4, 0xb4, 0x72, 0x12, 0x7f, 0x02, 0x72, 0x09, 0x12, 0x30, - 0x3e, 0xbf, 0x51, 0x7b, 0xff, 0x9d, 0xce, 0x52, 0x8b, 0xaa, 0x63, 0xa4, 0x9f, 0xfc, 0x1a, 0xf2, 0x1f, 0xe0, 0x47, - 0x1f, 0x91, 0xd2, 0xd9, 0x3c, 0x5b, 0xfd, 0x89, 0xab, 0xd8, 0x65, 0x41, 0x75, 0x02, 0x2a, 0x48, 0x58, 0x05, 0xb5, - 0x06, 0x23, 0xfb, 0x1f, 0x16, 0xae, 0x46, 0x4c, 0xf3, 0xa7, 0x5b, 0xb4, 0x1a, 0xba, 0x57, 0xa0, 0xea, 0x70, 0x03, - 0x44, 0x0e, 0xdd, 0xa3, 0xea, 0x62, 0xc7, 0x99, 0xfe, 0x5b, 0x09, 0xd8, 0x38, 0x73, 0x82, 0xd3, 0xfd, 0x87, 0x97, - 0x2f, 0xd6, 0xf6, 0xa4, 0x5f, 0x32, 0xc3, 0xf8, 0x92, 0xba, 0x78, 0x70, 0x5f, 0xd3, 0xe2, 0x5b, 0xc2, 0xe4, 0xd3, - 0xfc, 0xf3, 0x49, 0xff, 0xea, 0x4b, 0xfe, 0xfc, 0xe8, 0x17, 0xbe, 0x95, 0xaf, 0x79, 0xf6, 0x4d, 0x5a, 0xa3, 0x1d, - 0xf6, 0x7a, 0x88, 0xbb, 0x37, 0xfd, 0xa1, 0x0e, 0xf9, 0x5a, 0xc5, 0xf8, 0xaf, 0x9e, 0xe9, 0xd3, 0x1f, 0x1e, 0x1f, - 0xdc, 0xa4, 0x77, 0x09, 0x39, 0xcd, 0x94, 0x57, 0xe7, 0xd6, 0xbe, 0xc1, 0x12, 0xb6, 0xf5, 0x26, 0xc1, 0xde, 0xa0, - 0x20, 0xd2, 0x48, 0xbb, 0x13, 0x21, 0x02, 0x95, 0x41, 0xae, 0x60, 0xc8, 0xcd, 0x71, 0xd4, 0xf0, 0x3f, 0x71, 0xc0, - 0x28, 0x97, 0x11, 0x55, 0xa5, 0x8a, 0xd3, 0xd1, 0xc1, 0x4c, 0xc0, 0x29, 0x44, 0x18, 0x21, 0xf9, 0x5e, 0xcd, 0x62, - 0x81, 0xce, 0x24, 0x0d, 0x3e, 0x7e, 0x27, 0x1d, 0x4b, 0x56, 0x5c, 0x5b, 0xe6, 0xeb, 0xfd, 0x27, 0xd9, 0x58, 0xf9, - 0x28, 0x90, 0x59, 0x79, 0x87, 0x02, 0xd5, 0x21, 0x05, 0x93, 0x8b, 0xd4, 0xf9, 0x88, 0x99, 0xf3, 0x91, 0x4a, 0x2f, - 0xd8, 0xaf, 0xe6, 0x06, 0xda, 0x8d, 0x3d, 0x1c, 0xec, 0x5b, 0x65, 0x6c, 0xc2, 0x90, 0xe4, 0x26, 0xbf, 0x46, 0x06, - 0xe5, 0xe4, 0xa6, 0x0d, 0x5b, 0xe0, 0x9b, 0x5f, 0x9f, 0xa1, 0x49, 0x0a, 0x9d, 0x8d, 0x7c, 0xcf, 0xc8, 0x83, 0xeb, - 0xfb, 0xb3, 0xd7, 0xfe, 0xd1, 0x94, 0x45, 0x13, 0xd6, 0x6e, 0xa9, 0x7d, 0x42, 0x28, 0x05, 0x2a, 0x08, 0x10, 0xa6, - 0xc2, 0x1a, 0x58, 0xd6, 0x21, 0x35, 0x87, 0x9a, 0xae, 0x3f, 0x67, 0x90, 0x23, 0xb5, 0xc3, 0xc4, 0xbe, 0x0d, 0x03, - 0x5f, 0x2b, 0xa5, 0xb7, 0x37, 0x50, 0xa5, 0x16, 0xf6, 0x59, 0x64, 0xa8, 0x33, 0x39, 0x57, 0x1c, 0x81, 0xd7, 0x2d, - 0x35, 0x33, 0x51, 0xe8, 0x2c, 0x1b, 0x69, 0x7e, 0x4a, 0x78, 0x45, 0x7f, 0x55, 0x04, 0x4c, 0x74, 0xd0, 0x99, 0xdc, - 0x9a, 0x8a, 0x02, 0x93, 0x90, 0xaa, 0xba, 0x62, 0xeb, 0x78, 0x0a, 0x84, 0x9f, 0xa7, 0x88, 0xed, 0x1a, 0x9f, 0x87, - 0xa2, 0x3c, 0xc9, 0xfb, 0x34, 0x77, 0x7d, 0xe8, 0x9c, 0x6b, 0x03, 0x91, 0x6c, 0x46, 0x74, 0xe1, 0x87, 0xd7, 0x54, - 0xa7, 0xc5, 0x6d, 0x4b, 0xf7, 0x69, 0x5e, 0x7c, 0xd2, 0xac, 0x4b, 0x2e, 0x7e, 0xf4, 0x97, 0x6c, 0x9b, 0x65, 0x08, - 0x45, 0x2a, 0x53, 0xf0, 0x6a, 0x9f, 0x2f, 0x8a, 0xc9, 0xf6, 0x7b, 0x58, 0xf2, 0xc4, 0x97, 0x41, 0x83, 0x89, 0x7e, - 0x71, 0xe7, 0x11, 0x1c, 0xaf, 0xba, 0xc8, 0x6a, 0x0e, 0x9c, 0xeb, 0x7a, 0x36, 0xe6, 0xb2, 0x35, 0x2e, 0x34, 0x42, - 0xa2, 0xae, 0x1a, 0x79, 0xd9, 0xbb, 0x80, 0x0c, 0x23, 0x29, 0x7b, 0x20, 0xc0, 0x9c, 0x5f, 0x5b, 0x46, 0xc3, 0xb3, - 0x90, 0x7c, 0xdd, 0x74, 0xba, 0xa0, 0x21, 0x54, 0x40, 0x83, 0x9f, 0xbf, 0x97, 0xd0, 0x9e, 0x0a, 0x7b, 0x7d, 0xfa, - 0x0b, 0xcf, 0x4c, 0x5a, 0x51, 0xc6, 0x33, 0x7d, 0x16, 0x4b, 0x9a, 0x27, 0x9d, 0xb1, 0x25, 0xcf, 0xfb, 0x58, 0xbe, - 0x4f, 0xe5, 0x58, 0xee, 0xee, 0x69, 0xba, 0xe4, 0x24, 0x35, 0xc7, 0x4a, 0x67, 0x42, 0x6d, 0x7c, 0x99, 0x4f, 0x22, - 0x12, 0x37, 0x78, 0x8a, 0x81, 0x58, 0xcf, 0x7d, 0x3a, 0x18, 0x4e, 0x15, 0xcd, 0xb7, 0xa7, 0xbb, 0x55, 0xe9, 0x9b, - 0x4d, 0xb5, 0x08, 0x71, 0x79, 0xc8, 0x62, 0xe2, 0xc3, 0x40, 0xd9, 0xd9, 0xa6, 0x8d, 0x9b, 0x04, 0x0f, 0xa4, 0xce, - 0xe5, 0xf4, 0x60, 0xb8, 0x88, 0xbd, 0xce, 0x3c, 0xa4, 0x57, 0x5c, 0xdc, 0x05, 0xe2, 0xbc, 0x42, 0x38, 0xa8, 0x57, - 0x8c, 0x6b, 0xf9, 0xa6, 0xd9, 0xbf, 0x9c, 0x4a, 0xe2, 0x92, 0x87, 0x6b, 0xd0, 0x4a, 0x35, 0x6b, 0x9d, 0x62, 0xab, - 0xa3, 0xf5, 0xf0, 0xdf, 0x37, 0x88, 0xac, 0xd8, 0x7c, 0xe1, 0x5b, 0xf9, 0xca, 0x76, 0x41, 0xc8, 0xec, 0x2f, 0xc7, - 0x17, 0x68, 0x3f, 0xcb, 0xd6, 0xda, 0x8b, 0xd3, 0xee, 0x74, 0xe3, 0x2e, 0xaf, 0x0f, 0xdb, 0x60, 0x7c, 0x85, 0x0e, - 0xdb, 0x05, 0x99, 0x7e, 0x62, 0xbd, 0xbe, 0xa7, 0x12, 0xfe, 0xe1, 0xfa, 0x87, 0xdf, 0xf4, 0xb9, 0x3f, 0xe6, 0x2a, - 0xe2, 0x00, 0x99, 0x97, 0xd4, 0x86, 0x71, 0xcd, 0x62, 0x2f, 0xe8, 0x56, 0x42, 0x7d, 0x6e, 0x9f, 0x01, 0x07, 0x37, - 0x37, 0xbd, 0xa7, 0x56, 0x03, 0x80, 0x45, 0x1c, 0x5d, 0xc3, 0x8e, 0x27, 0xe0, 0x13, 0x4a, 0x05, 0x61, 0x8f, 0x63, - 0x54, 0x29, 0x5d, 0xaa, 0x47, 0x1d, 0x3f, 0x0f, 0xa3, 0x3a, 0x10, 0x20, 0xe0, 0xf1, 0x98, 0xc7, 0x82, 0x44, 0x0d, - 0xea, 0x3c, 0x9a, 0xf2, 0x0a, 0x3e, 0x44, 0x02, 0xf6, 0x5d, 0xaf, 0xef, 0xc6, 0x37, 0xc3, 0x2b, 0x02, 0x5b, 0xf8, - 0x25, 0x8d, 0x6c, 0x23, 0x34, 0x8a, 0x47, 0xb9, 0x75, 0x4d, 0xf4, 0x45, 0x6d, 0xc7, 0xcc, 0x0b, 0x41, 0x56, 0x4f, - 0x78, 0x06, 0x0b, 0xe5, 0x82, 0xe0, 0x0b, 0xab, 0x80, 0xfb, 0x73, 0xa2, 0x1f, 0x83, 0x94, 0x1e, 0x8a, 0xe8, 0x88, - 0xd6, 0x91, 0xa9, 0xc1, 0x71, 0x8f, 0x65, 0x89, 0xe1, 0x3c, 0x42, 0xb0, 0xdb, 0x96, 0x35, 0x22, 0xab, 0xd5, 0x08, - 0x7e, 0xf3, 0x52, 0xd1, 0x3a, 0xa4, 0x24, 0x85, 0x0a, 0xd6, 0xd4, 0xf4, 0x5a, 0x10, 0xa9, 0x45, 0xe7, 0x7f, 0x02, - 0xc4, 0x69, 0x4f, 0x34, 0xad, 0xf6, 0x9c, 0x5a, 0x54, 0x1c, 0xda, 0x46, 0xc2, 0xdc, 0xa5, 0xc0, 0x95, 0x38, 0x70, - 0x00, 0xb1, 0xf4, 0xae, 0x48, 0xe4, 0x3d, 0xb4, 0x3f, 0xb8, 0x42, 0x9a, 0x4e, 0x8d, 0x77, 0x72, 0xca, 0x0d, 0x52, - 0x75, 0x61, 0xe4, 0x34, 0x12, 0x93, 0x2a, 0x27, 0x8c, 0x50, 0xc5, 0xed, 0x5a, 0x2d, 0xe1, 0xd4, 0x1b, 0xb7, 0x03, - 0x4f, 0x01, 0xef, 0x92, 0x21, 0x6c, 0xaf, 0x35, 0xe2, 0xcc, 0x18, 0xba, 0x7c, 0xf3, 0x9f, 0xba, 0x9d, 0x53, 0xfb, - 0x65, 0x70, 0x45, 0x87, 0x81, 0xaf, 0xc6, 0xab, 0x30, 0x79, 0x4a, 0x61, 0x5a, 0xfd, 0xa5, 0xeb, 0x33, 0x18, 0xf2, - 0x27, 0xf9, 0x4c, 0x43, 0x22, 0x48, 0xf1, 0x36, 0x7c, 0x78, 0x3f, 0xda, 0x06, 0xe4, 0x21, 0x70, 0x98, 0x8f, 0xc1, - 0xef, 0x44, 0xf6, 0x41, 0x6b, 0x44, 0x77, 0x8a, 0xb0, 0x20, 0x35, 0x77, 0xf8, 0xe8, 0x90, 0x6f, 0x1e, 0xea, 0x91, - 0x5c, 0x5e, 0x83, 0x00, 0x0a, 0x56, 0xd3, 0xc2, 0x9e, 0x3e, 0xb7, 0x79, 0xc6, 0x7b, 0xd0, 0x44, 0x47, 0xe1, 0x10, - 0x13, 0x3c, 0xe7, 0x0c, 0xed, 0x68, 0x27, 0x87, 0xe1, 0x31, 0xf4, 0x4a, 0x61, 0xee, 0x3f, 0x23, 0x72, 0xc3, 0xf9, - 0xb9, 0x9e, 0x31, 0x8d, 0x72, 0x9e, 0xb2, 0xaf, 0x57, 0x8d, 0x1e, 0xff, 0xb1, 0x03, 0x70, 0xff, 0xf4, 0xd7, 0x84, - 0xe4, 0x4f, 0x75, 0x0a, 0xdf, 0x57, 0x96, 0x84, 0xb7, 0x02, 0xff, 0x06, 0xaf, 0x59, 0x62, 0x70, 0x98, 0x82, 0x42, - 0xf9, 0x6b, 0x0b, 0x42, 0x6e, 0x73, 0x72, 0x6d, 0x0e, 0x97, 0xcf, 0x99, 0xe4, 0x0b, 0xb6, 0x09, 0xb5, 0x3e, 0x2b, - 0x70, 0xf0, 0xa6, 0xc9, 0x72, 0x3a, 0x8e, 0x9c, 0xf9, 0xad, 0xd8, 0x5c, 0x37, 0x26, 0x79, 0x14, 0x29, 0xfa, 0xcd, - 0xf4, 0x46, 0xde, 0x78, 0xb3, 0x10, 0x6d, 0x87, 0x5e, 0x9a, 0xd6, 0x8f, 0x2f, 0x08, 0x3f, 0x0d, 0xcb, 0x89, 0xd9, - 0x1f, 0x7c, 0x2f, 0xb0, 0xba, 0xc4, 0xc5, 0x80, 0x0c, 0xc3, 0xee, 0x58, 0xb0, 0x0e, 0x57, 0xd7, 0x68, 0xca, 0xb8, - 0x1c, 0xa4, 0x8a, 0x96, 0xee, 0x08, 0xa1, 0x9b, 0xb8, 0x28, 0xed, 0x4c, 0xd9, 0x7b, 0xf9, 0x3b, 0xb4, 0xfa, 0xb5, - 0x2a, 0xde, 0x5d, 0x12, 0x3e, 0xf8, 0xee, 0x5d, 0xd0, 0xdf, 0x74, 0xc8, 0xc6, 0xba, 0x5f, 0x3e, 0xbe, 0x54, 0x4d, - 0x16, 0x46, 0x83, 0x99, 0x4f, 0x79, 0x73, 0x76, 0x57, 0x65, 0x94, 0xd4, 0x35, 0x14, 0x46, 0x62, 0x8f, 0x1c, 0xe7, - 0xbd, 0x33, 0x59, 0xd7, 0xbb, 0x8e, 0x55, 0xe9, 0xf2, 0xb3, 0x04, 0x8b, 0xd6, 0x72, 0xef, 0xfe, 0x2c, 0xd5, 0xa7, - 0x50, 0x03, 0x69, 0xb3, 0x81, 0x0e, 0xdd, 0x46, 0x9b, 0x68, 0x9c, 0x49, 0xa0, 0xb4, 0x87, 0x2b, 0x2f, 0x6a, 0xfa, - 0x2c, 0x26, 0xd0, 0xba, 0x9d, 0x2d, 0x74, 0xb6, 0x0b, 0x4a, 0x83, 0xdb, 0x3f, 0xee, 0x76, 0xe9, 0xcc, 0xe0, 0xe3, - 0xfd, 0x83, 0x0c, 0xcb, 0xff, 0x1b, 0x55, 0xec, 0x9e, 0x1c, 0x80, 0x86, 0x35, 0x6f, 0x9b, 0x44, 0x44, 0x48, 0x58, - 0xdc, 0x7c, 0x72, 0xec, 0xfb, 0xc6, 0x97, 0xe8, 0xb9, 0xa1, 0x27, 0xe3, 0xc4, 0xf5, 0x52, 0x9d, 0xb2, 0x1e, 0x89, - 0x01, 0x7f, 0xd2, 0x39, 0x90, 0x68, 0x6b, 0x9a, 0xdd, 0x0e, 0xca, 0x81, 0xdd, 0x9b, 0x03, 0xeb, 0x8f, 0xf9, 0x06, - 0x23, 0x07, 0x2b, 0x9b, 0x3f, 0xb5, 0xb9, 0xed, 0xb4, 0x0e, 0x9f, 0x4d, 0xc6, 0xd2, 0xe3, 0xe1, 0x2b, 0xab, 0x23, - 0xb4, 0x35, 0x92, 0x15, 0x83, 0x6a, 0x6f, 0xf7, 0x63, 0x0f, 0x22, 0x7e, 0xa6, 0xee, 0xde, 0x45, 0xdd, 0xa1, 0xa5, - 0x67, 0xf6, 0xf6, 0xe0, 0xb1, 0x7f, 0x60, 0x8d, 0x43, 0xdd, 0xcb, 0x05, 0x08, 0x4b, 0xdc, 0x51, 0xd6, 0x56, 0x71, - 0x71, 0xfb, 0xe7, 0xd7, 0x0f, 0x9a, 0x83, 0x40, 0xe5, 0x70, 0x30, 0xd1, 0x8b, 0x11, 0xeb, 0xc8, 0xb1, 0x63, 0x18, - 0x23, 0x76, 0x73, 0x80, 0x94, 0x11, 0x23, 0xcd, 0x29, 0xdf, 0x07, 0x63, 0x5c, 0xf4, 0x46, 0xed, 0xc2, 0x86, 0x79, - 0x80, 0x15, 0xee, 0xa4, 0xaa, 0xc3, 0xc2, 0xc4, 0xfc, 0xba, 0xb5, 0x49, 0x72, 0xde, 0x91, 0xf5, 0xa9, 0xd9, 0xbb, - 0x12, 0x84, 0x3e, 0x1f, 0xfe, 0x8d, 0x8a, 0x78, 0xae, 0xb3, 0x87, 0x60, 0x02, 0x7e, 0xac, 0x3a, 0xec, 0x6f, 0xc1, - 0xa7, 0x0d, 0x27, 0xea, 0xe8, 0x93, 0xd1, 0x59, 0xe1, 0x80, 0x5d, 0x6b, 0xfa, 0x50, 0xc6, 0x43, 0x8f, 0x59, 0x18, - 0x2b, 0xd3, 0x5b, 0x15, 0x94, 0x0d, 0x9b, 0xa9, 0x2e, 0xa9, 0x06, 0xaa, 0x4c, 0x26, 0x99, 0x4c, 0xd9, 0x42, 0xce, - 0x00, 0xb6, 0xf7, 0x41, 0x72, 0x85, 0x88, 0x7a, 0x5f, 0x5a, 0x8f, 0xcc, 0x22, 0xae, 0x91, 0x23, 0xda, 0x63, 0x50, - 0x8b, 0x88, 0x77, 0x6a, 0x75, 0x94, 0xe4, 0xa3, 0x2f, 0x1f, 0x82, 0xd0, 0xb5, 0xa4, 0x3f, 0x9b, 0xa1, 0x84, 0x65, - 0x46, 0x2e, 0xdb, 0x4f, 0xdc, 0xbd, 0x3f, 0x8d, 0x7f, 0x9a, 0x08, 0x6d, 0x97, 0x67, 0xeb, 0xc1, 0xc8, 0xb5, 0x34, - 0x95, 0xd7, 0xb8, 0xa5, 0xc6, 0xb8, 0xe0, 0xa7, 0x38, 0xd2, 0xe6, 0x6b, 0xcd, 0xd3, 0x43, 0xdd, 0x7a, 0x1e, 0xb5, - 0x0f, 0xb2, 0xb6, 0x0e, 0xec, 0xc5, 0x42, 0x7b, 0x0a, 0x7b, 0xe7, 0xf8, 0xd0, 0xfd, 0xc5, 0xad, 0xcb, 0x4d, 0x95, - 0x8f, 0xce, 0x5c, 0x48, 0x64, 0x8e, 0x8a, 0xb7, 0x38, 0xc8, 0x07, 0xa0, 0x22, 0x92, 0xe1, 0xbd, 0x5b, 0x1e, 0x36, - 0xcf, 0xba, 0x47, 0x3d, 0xf6, 0xa0, 0x8c, 0x84, 0x8f, 0x77, 0x08, 0x89, 0x52, 0x21, 0xf6, 0xfc, 0x67, 0x92, 0x72, - 0x16, 0x0d, 0x95, 0xb7, 0x65, 0xe5, 0xf4, 0xf5, 0x3c, 0x92, 0x6a, 0x19, 0x0f, 0x78, 0x4f, 0x6e, 0xb6, 0x96, 0x13, - 0xc5, 0xad, 0xbe, 0xda, 0x5c, 0x82, 0xa0, 0x6c, 0xf4, 0x86, 0xdb, 0xb7, 0x11, 0x3b, 0x4e, 0xa0, 0x6d, 0xdb, 0x9f, - 0x5c, 0x2c, 0x45, 0xa9, 0x70, 0xc2, 0x58, 0x37, 0x39, 0x8a, 0xe6, 0x10, 0x86, 0x37, 0x6b, 0xab, 0x09, 0x1f, 0x70, - 0xc3, 0x31, 0x6f, 0x6f, 0x29, 0x87, 0x55, 0x2d, 0x9c, 0xa3, 0x48, 0xc6, 0xc4, 0xde, 0x2e, 0xa3, 0xdb, 0x5b, 0x85, - 0xfe, 0x13, 0xb2, 0xeb, 0xac, 0x56, 0xde, 0x04, 0x5f, 0x29, 0x88, 0x6c, 0xee, 0xc7, 0x67, 0xc6, 0x01, 0xd2, 0x0d, - 0xf0, 0xd7, 0x0a, 0x92, 0x55, 0x9e, 0xa8, 0xbc, 0x0a, 0x4c, 0xd3, 0x90, 0x82, 0xe1, 0x53, 0x7a, 0x0f, 0x96, 0xbc, - 0xe6, 0xcb, 0x66, 0xd7, 0x37, 0x17, 0x3f, 0xac, 0xf5, 0x10, 0x2f, 0x3b, 0xbd, 0xb5, 0x2a, 0x9c, 0xe0, 0x31, 0x49, - 0xfc, 0xba, 0xf4, 0xb3, 0xfd, 0x60, 0xe3, 0x96, 0x42, 0xed, 0x07, 0x9c, 0xd9, 0xba, 0xe7, 0x30, 0xb3, 0x49, 0x9f, - 0x01, 0x12, 0x16, 0x68, 0xdd, 0xc7, 0x22, 0x53, 0x60, 0xab, 0x01, 0x6e, 0x00, 0x23, 0xb6, 0x7d, 0xc8, 0x1e, 0xbd, - 0x29, 0x92, 0x2d, 0xe4, 0x7b, 0x3a, 0x72, 0xfb, 0x53, 0x4c, 0xef, 0x17, 0x75, 0x20, 0x9a, 0xaf, 0x03, 0x6e, 0xeb, - 0x81, 0x77, 0x1c, 0xa4, 0x48, 0x5c, 0x21, 0xa6, 0x49, 0xf7, 0x15, 0x5a, 0xb5, 0xba, 0x9b, 0x5c, 0xf6, 0xe7, 0x8e, - 0x93, 0xb5, 0xde, 0x86, 0xbb, 0xd8, 0xcf, 0xaa, 0x1d, 0xd2, 0x51, 0x03, 0xf8, 0xd2, 0xaf, 0x0c, 0x74, 0x7a, 0x9a, - 0xc2, 0x77, 0x25, 0x96, 0x4d, 0x08, 0x98, 0x3b, 0x28, 0xec, 0x2c, 0x90, 0x04, 0x2b, 0x9c, 0x38, 0x96, 0x77, 0x58, - 0x93, 0x17, 0xfa, 0x7a, 0x1c, 0x19, 0x18, 0x98, 0xb2, 0x27, 0x11, 0x61, 0xef, 0x2c, 0x52, 0x34, 0x6b, 0x19, 0xde, - 0x32, 0xd1, 0x93, 0x0f}; + 0x5b, 0x7b, 0x7b, 0x53, 0xc1, 0x6e, 0x19, 0x03, 0xf5, 0x04, 0xe0, 0xf8, 0xbb, 0x25, 0x3d, 0x34, 0x51, 0x94, 0xb1, + 0xe6, 0x22, 0x2f, 0x61, 0xbb, 0xc2, 0x70, 0x9e, 0x80, 0x65, 0x6b, 0x78, 0xad, 0x47, 0x01, 0x40, 0x55, 0x13, 0x0e, + 0xd8, 0x18, 0x92, 0x41, 0xc7, 0x81, 0x6a, 0xd1, 0xee, 0xdb, 0xf0, 0xa6, 0x22, 0xc8, 0x31, 0x97, 0xd9, 0x3c, 0xcc, + 0xe5, 0xa4, 0xd5, 0x13, 0x8c, 0x3a, 0x44, 0xf9, 0x1a, 0x8c, 0x4b, 0x4c, 0x51, 0x7e, 0x10, 0x4b, 0xea, 0xba, 0xe6, + 0x24, 0x45, 0x6e, 0xf3, 0x72, 0x99, 0x36, 0xac, 0xdb, 0x79, 0x37, 0x0a, 0x4f, 0x92, 0xc8, 0x21, 0xb3, 0xaa, 0x10, + 0x8e, 0x77, 0x8e, 0xb5, 0x29, 0xe5, 0xbf, 0x10, 0x83, 0x6d, 0xc3, 0xba, 0x2d, 0x5d, 0x36, 0x49, 0x12, 0x9a, 0x5b, + 0x46, 0xa5, 0x0a, 0x42, 0x62, 0xf0, 0x33, 0xd7, 0x94, 0x37, 0x3f, 0x57, 0x6b, 0x9c, 0x30, 0x61, 0xbd, 0xf1, 0x5b, + 0x28, 0xf2, 0xda, 0x5a, 0xe3, 0x0b, 0x44, 0xe0, 0x2e, 0xa4, 0xbe, 0x68, 0xed, 0xa7, 0x5b, 0x5f, 0x57, 0x34, 0xde, + 0x43, 0xba, 0xff, 0x8d, 0xbf, 0x8b, 0x43, 0x72, 0x99, 0xdb, 0x59, 0x36, 0xac, 0xdb, 0xf2, 0xa6, 0xee, 0x77, 0xca, + 0x1c, 0x5e, 0x9c, 0x1b, 0x62, 0x68, 0x1d, 0x10, 0x8f, 0xcd, 0xa1, 0x4a, 0x44, 0x8b, 0x11, 0x2b, 0xf6, 0xda, 0x13, + 0xf3, 0x5f, 0x65, 0xcd, 0x7a, 0x7d, 0x47, 0xb5, 0x8c, 0x9c, 0x1d, 0x4d, 0x37, 0x55, 0x02, 0x7c, 0x97, 0xc6, 0xd7, + 0x09, 0x1e, 0xf0, 0xb1, 0x63, 0x19, 0x43, 0x51, 0x9d, 0xdd, 0x4a, 0x10, 0x59, 0x4d, 0x65, 0x8a, 0x4b, 0xf2, 0xff, + 0xa6, 0xaa, 0xe7, 0xf8, 0x72, 0xa2, 0xbf, 0xfa, 0x1c, 0xb2, 0x16, 0x10, 0x7c, 0x80, 0xe0, 0x90, 0xca, 0x4c, 0xc9, + 0x59, 0xb2, 0xbb, 0x24, 0x27, 0x5b, 0x06, 0x49, 0x70, 0xa4, 0x1c, 0x29, 0x01, 0x6a, 0x44, 0xce, 0xfd, 0x92, 0x7d, + 0x55, 0xed, 0xa7, 0x3d, 0x4f, 0xd7, 0xa6, 0xdf, 0xb6, 0xdf, 0xdd, 0x7b, 0xe2, 0x85, 0x47, 0x51, 0x90, 0x08, 0x99, + 0x06, 0x14, 0x02, 0x6a, 0xf6, 0xb5, 0xb7, 0xb4, 0xff, 0xaf, 0xdf, 0x91, 0x10, 0xe0, 0x4a, 0x70, 0x16, 0x75, 0xe6, + 0x2d, 0x5b, 0xcf, 0x85, 0xb3, 0x2c, 0x5b, 0x5f, 0xc3, 0x61, 0x58, 0x47, 0x5d, 0x35, 0xa1, 0xc9, 0x7e, 0x24, 0x15, + 0xbb, 0x23, 0xd8, 0xcf, 0x7d, 0x96, 0x7d, 0x7d, 0x2f, 0x5a, 0x3f, 0xd7, 0x0a, 0xcf, 0x78, 0x8a, 0x0b, 0xb1, 0xfe, + 0x2e, 0xa4, 0x84, 0xe9, 0x5a, 0x35, 0xa7, 0x16, 0xc8, 0xc0, 0x38, 0x3e, 0xfb, 0x6c, 0x5f, 0x55, 0xa7, 0x6b, 0x47, + 0x9a, 0x25, 0x26, 0x47, 0xae, 0x17, 0x3d, 0x73, 0x14, 0x38, 0x9a, 0xfd, 0x5e, 0x2e, 0x1a, 0x1d, 0xe8, 0x0c, 0xe5, + 0x04, 0xb6, 0x31, 0x50, 0x0b, 0x44, 0x55, 0xb7, 0x19, 0xb2, 0xea, 0x7d, 0xf5, 0xfd, 0xaf, 0x5f, 0x72, 0xa3, 0x40, + 0x3d, 0xc6, 0x2c, 0x69, 0x29, 0xef, 0x81, 0x47, 0x88, 0x2c, 0x47, 0xc7, 0x8a, 0x1f, 0xf2, 0x95, 0x74, 0x1e, 0x2e, + 0x86, 0x45, 0x43, 0xc4, 0x92, 0x42, 0x0e, 0xb4, 0xe0, 0xc5, 0x2e, 0x2d, 0xd0, 0xc4, 0x06, 0xfe, 0xbf, 0xaa, 0x59, + 0xfd, 0xbe, 0x37, 0x2b, 0x09, 0x4b, 0x21, 0x2e, 0x71, 0x82, 0x40, 0xdd, 0xf3, 0x7b, 0x38, 0xde, 0xf3, 0x9f, 0x87, + 0xec, 0x30, 0x05, 0xad, 0xa2, 0x32, 0xc9, 0x41, 0xa4, 0x01, 0x35, 0x7a, 0x5c, 0x4b, 0xd3, 0xca, 0xd7, 0x57, 0x10, + 0xb0, 0x03, 0x97, 0xa6, 0xd8, 0xd3, 0x0c, 0x4d, 0x51, 0x24, 0x6c, 0x3a, 0xa5, 0xb7, 0xb3, 0x2e, 0x6b, 0x2f, 0x27, + 0xf3, 0xd0, 0xa9, 0x4d, 0xbb, 0x87, 0x49, 0x14, 0xd1, 0x36, 0x97, 0xb4, 0x86, 0x49, 0xc1, 0xdb, 0x49, 0x77, 0xc2, + 0x8a, 0x51, 0x79, 0x24, 0x4c, 0xf8, 0x87, 0x87, 0xe4, 0x03, 0x6a, 0xf5, 0x0d, 0xff, 0x69, 0x6a, 0xf6, 0xfa, 0xc6, + 0x0b, 0x3d, 0xe4, 0x54, 0x2e, 0xf2, 0x76, 0x80, 0xac, 0xee, 0xba, 0xb5, 0x69, 0x4e, 0x72, 0x9d, 0x98, 0x61, 0x93, + 0x02, 0xb6, 0x1b, 0x0e, 0x25, 0xd2, 0x46, 0x2c, 0x2d, 0xd5, 0xd7, 0x3b, 0x79, 0x1d, 0x25, 0x4a, 0x86, 0xf2, 0x0a, + 0x16, 0xd9, 0xb4, 0x5f, 0x29, 0x6d, 0xe0, 0xdb, 0xf8, 0xc6, 0x85, 0x03, 0x50, 0x4b, 0xf7, 0x84, 0x48, 0xea, 0xa0, + 0x10, 0x15, 0x28, 0x6c, 0xb0, 0xfc, 0xff, 0xbd, 0x95, 0x96, 0xdb, 0x1f, 0x91, 0xae, 0x08, 0x91, 0x3d, 0x00, 0x39, + 0xce, 0x70, 0x64, 0xfd, 0xbe, 0x33, 0xb3, 0x0a, 0x9c, 0x06, 0x48, 0xf6, 0x38, 0xb3, 0x92, 0xd6, 0x5a, 0x6c, 0x2a, + 0xee, 0xbd, 0xef, 0x5d, 0xe6, 0x77, 0xd1, 0x19, 0x3f, 0x0c, 0x2b, 0x4c, 0x66, 0x23, 0xed, 0xb0, 0x6c, 0x57, 0x96, + 0xd3, 0x00, 0x20, 0x78, 0xdf, 0x7b, 0x3f, 0x0a, 0xff, 0xff, 0xc8, 0xe2, 0xfc, 0x88, 0x2c, 0x50, 0x91, 0x59, 0xc5, + 0x39, 0x99, 0x05, 0xcc, 0xa8, 0x0a, 0xe0, 0x8c, 0x0a, 0x60, 0x1f, 0x1d, 0x90, 0x63, 0x41, 0xd0, 0x68, 0x9a, 0x6c, + 0xf6, 0xd1, 0x90, 0x2d, 0xef, 0x57, 0x5a, 0xac, 0x20, 0xca, 0xf5, 0x8c, 0x6c, 0x4b, 0x7e, 0xb7, 0x03, 0x65, 0xcd, + 0x52, 0x5a, 0xe9, 0xe8, 0xff, 0xe6, 0xd4, 0x66, 0x40, 0x8e, 0x40, 0x75, 0x53, 0x57, 0x21, 0xdc, 0xf2, 0xff, 0x5d, + 0xf2, 0x7a, 0x97, 0x52, 0xd2, 0xd1, 0xa5, 0x78, 0x19, 0x26, 0x1d, 0x95, 0x60, 0xe0, 0x2a, 0x27, 0xa7, 0xe7, 0x66, + 0x2c, 0xb2, 0x71, 0x53, 0x7f, 0x0c, 0xbf, 0x87, 0xd2, 0xc2, 0x60, 0xea, 0x4c, 0xd3, 0xf4, 0xdf, 0x6d, 0xa2, 0xab, + 0xae, 0x2c, 0x2c, 0xbf, 0xed, 0x66, 0x12, 0x57, 0x59, 0x96, 0x25, 0xb9, 0x24, 0x31, 0x01, 0xfe, 0x21, 0xba, 0xef, + 0x6f, 0xa8, 0xcf, 0x1b, 0x4b, 0xdb, 0x0c, 0x42, 0x26, 0x04, 0x48, 0xdb, 0xff, 0x37, 0x99, 0xeb, 0x9c, 0xb8, 0x78, + 0x7c, 0x49, 0xd3, 0x34, 0xb3, 0x6c, 0x7f, 0xae, 0xa3, 0xb4, 0x94, 0x6e, 0x9b, 0xed, 0x36, 0x7d, 0xa4, 0x0e, 0xdf, + 0xc0, 0x6f, 0x0c, 0x18, 0xc3, 0xe4, 0x6e, 0x15, 0x53, 0x43, 0x76, 0x69, 0xb6, 0xa5, 0xcd, 0x02, 0xd4, 0xb2, 0xfe, + 0x87, 0xa4, 0xdc, 0x5b, 0x42, 0x27, 0xf6, 0x34, 0xac, 0x62, 0x92, 0x7c, 0x83, 0x6f, 0x4c, 0xdf, 0x5a, 0x48, 0x2c, + 0x43, 0xd3, 0xda, 0xfe, 0x65, 0xb6, 0xf9, 0x75, 0x18, 0xb3, 0xcc, 0x14, 0x5b, 0x92, 0x63, 0x5b, 0x09, 0x76, 0xef, + 0x8a, 0x4c, 0xac, 0x3d, 0x0e, 0x00, 0xa7, 0xe5, 0x88, 0xb6, 0xe0, 0x33, 0x10, 0x8e, 0x73, 0x17, 0xbf, 0xfc, 0x55, + 0x09, 0xa6, 0xa3, 0x3d, 0x14, 0x7c, 0x75, 0x8c, 0x42, 0x2c, 0x41, 0x14, 0x79, 0xee, 0xe2, 0xde, 0x07, 0x26, 0xdf, + 0x0e, 0xaa, 0xe8, 0x1f, 0xba, 0xa6, 0x27, 0x9c, 0x21, 0x50, 0x8f, 0x5e, 0xf2, 0x0b, 0x07, 0xde, 0xfd, 0x7b, 0x00, + 0xa2, 0x12, 0x51, 0xea, 0xdb, 0x6f, 0xb8, 0x4e, 0x30, 0x7d, 0xdf, 0x4d, 0xdb, 0x03, 0xee, 0x0e, 0x1e, 0x12, 0x78, + 0x52, 0x0a, 0xcb, 0xfd, 0x97, 0xaa, 0x2b, 0x6e, 0x96, 0xa1, 0xd7, 0x31, 0x9d, 0xef, 0x26, 0xd8, 0x14, 0x2d, 0x6b, + 0x29, 0x18, 0x7a, 0xe6, 0xf1, 0xd6, 0x58, 0xfd, 0x0c, 0x56, 0xc9, 0xc0, 0x2d, 0x2d, 0xcc, 0xe4, 0xd4, 0xcf, 0xd7, + 0x54, 0xf5, 0xc1, 0x48, 0x12, 0x01, 0x90, 0xbc, 0xf9, 0x10, 0x27, 0x44, 0xe2, 0xfa, 0x7a, 0x3e, 0x5f, 0x95, 0x97, + 0xd9, 0x7e, 0x98, 0x60, 0x20, 0xd9, 0x20, 0x03, 0x98, 0xed, 0x3d, 0x5c, 0x7d, 0xb8, 0x57, 0xf3, 0x32, 0x6a, 0xfa, + 0xd7, 0x79, 0xb4, 0xa1, 0x33, 0x6d, 0x40, 0x1e, 0xb7, 0x69, 0x59, 0x9a, 0x92, 0x82, 0xc4, 0x86, 0x43, 0x06, 0x77, + 0x83, 0x39, 0xad, 0xc7, 0xa4, 0xe6, 0x9c, 0xac, 0xc9, 0x15, 0x97, 0x06, 0x37, 0xeb, 0xa3, 0x0f, 0xf7, 0xbe, 0xa4, + 0xc3, 0x2d, 0x3e, 0x6c, 0xfa, 0x24, 0x93, 0x7b, 0xde, 0x84, 0xcf, 0x4d, 0xb9, 0xbe, 0x1c, 0x02, 0x7b, 0xf3, 0x13, + 0x76, 0x85, 0xa0, 0x59, 0xdf, 0xea, 0xc8, 0x37, 0xde, 0xb5, 0xeb, 0xa1, 0x44, 0x32, 0x1a, 0x7d, 0xef, 0x41, 0xf3, + 0xa2, 0xdc, 0x88, 0x47, 0xd8, 0x2b, 0xd4, 0xb7, 0x3f, 0xb1, 0xc2, 0xb2, 0xbb, 0x99, 0x3f, 0x6c, 0x74, 0x7b, 0xf6, + 0xdd, 0xcb, 0xc1, 0xa3, 0x2f, 0xc2, 0x5c, 0x7d, 0xb8, 0xbf, 0xdd, 0x3a, 0xc1, 0x63, 0x42, 0x29, 0x76, 0x43, 0xc2, + 0xa1, 0xe6, 0xf7, 0x6e, 0xf6, 0x6e, 0xf2, 0x73, 0x59, 0x8b, 0x59, 0x4d, 0xfe, 0x93, 0xdf, 0xfe, 0xea, 0x77, 0x6a, + 0x9b, 0x8f, 0xf0, 0xed, 0x09, 0xc2, 0xd3, 0xbb, 0xa3, 0x8c, 0xb0, 0xe6, 0x30, 0xfe, 0xac, 0xa7, 0xca, 0x3f, 0xdb, + 0x2c, 0x6c, 0x73, 0x98, 0xaf, 0x4b, 0xda, 0x9e, 0xc3, 0xa4, 0x75, 0x57, 0xa2, 0xde, 0x4d, 0x94, 0xf2, 0xa0, 0x09, + 0xf2, 0xf2, 0xb9, 0x03, 0x7d, 0xe3, 0x7c, 0xcd, 0xa0, 0xc8, 0x6e, 0xa9, 0xe5, 0xd2, 0x9a, 0xc7, 0x9b, 0xf9, 0x60, + 0x59, 0xa2, 0x40, 0xbf, 0x4a, 0xbd, 0x77, 0xad, 0xbb, 0x7e, 0x21, 0xaa, 0x1f, 0x6d, 0xe6, 0x6a, 0x04, 0x42, 0xa4, + 0x5c, 0x37, 0x01, 0x22, 0x4b, 0xa4, 0xc8, 0x33, 0xf1, 0x5c, 0xa7, 0x4d, 0x86, 0x1e, 0xb9, 0xbf, 0xf2, 0x6b, 0xa4, + 0xe1, 0xf9, 0x84, 0x76, 0xf8, 0xd1, 0x66, 0x25, 0xd4, 0x2b, 0x54, 0xc8, 0x1b, 0x67, 0xc5, 0x7f, 0xee, 0x43, 0xa9, + 0xd6, 0x44, 0x0c, 0xcf, 0xcd, 0x64, 0x90, 0xf7, 0x2c, 0xbb, 0x92, 0xea, 0x58, 0x5b, 0x5b, 0x55, 0xd7, 0xb7, 0x50, + 0xde, 0xcc, 0x50, 0xee, 0x45, 0x95, 0x22, 0xf9, 0x60, 0x18, 0xd2, 0x73, 0xfc, 0xdb, 0xd6, 0x37, 0x3f, 0x15, 0x88, + 0x73, 0x91, 0x37, 0x28, 0x75, 0x43, 0x4d, 0x96, 0x12, 0xfb, 0x59, 0x9d, 0xb2, 0xdd, 0x23, 0xed, 0xa0, 0x23, 0x57, + 0x03, 0x98, 0xc2, 0x54, 0xb0, 0xe7, 0xd5, 0x4b, 0x56, 0x9d, 0xe7, 0x05, 0xf9, 0xb6, 0xe2, 0x47, 0x04, 0x40, 0xa3, + 0x78, 0x43, 0x34, 0x2b, 0xa0, 0x2a, 0x91, 0x26, 0x0b, 0xc7, 0x4e, 0xf3, 0x4f, 0x68, 0x43, 0xcd, 0x7e, 0xbf, 0xed, + 0x64, 0x50, 0xc2, 0xc5, 0x37, 0x9f, 0x7d, 0xa0, 0x09, 0x7e, 0xfb, 0x99, 0x8c, 0xac, 0x95, 0xa0, 0xa3, 0x9c, 0xbc, + 0xee, 0x40, 0xca, 0x2c, 0x53, 0x61, 0xa1, 0x8b, 0xa4, 0x84, 0x6e, 0x98, 0x9c, 0x2f, 0x78, 0x7b, 0x83, 0xf3, 0xb5, + 0xac, 0x56, 0x5a, 0xbe, 0x99, 0xaa, 0x85, 0x79, 0x07, 0x54, 0x7d, 0xec, 0x04, 0x9e, 0xd0, 0x6d, 0x32, 0xef, 0x96, + 0xd2, 0x23, 0x5a, 0xf9, 0xde, 0x4b, 0x91, 0x66, 0xb7, 0xfe, 0x84, 0xe8, 0xd5, 0x11, 0x81, 0xfb, 0x22, 0xd9, 0x8a, + 0xbe, 0x37, 0x8c, 0x88, 0xe2, 0xfe, 0x1e, 0xfd, 0x12, 0x3f, 0xcb, 0xaf, 0x5d, 0x21, 0x34, 0x56, 0xc0, 0x23, 0x69, + 0x7d, 0xef, 0x6a, 0x8f, 0x21, 0x80, 0x4e, 0xaf, 0x42, 0x31, 0xec, 0xb6, 0x22, 0x66, 0xc7, 0x99, 0x38, 0xee, 0xf3, + 0x19, 0x96, 0xf9, 0xda, 0x34, 0xa1, 0x1b, 0xaa, 0x4f, 0x71, 0x21, 0x65, 0x92, 0x36, 0x45, 0x55, 0x17, 0x8d, 0xbf, + 0x35, 0xc8, 0x98, 0x62, 0xde, 0x7a, 0x34, 0xe8, 0x2f, 0xf6, 0x05, 0xf1, 0xe0, 0x48, 0xd0, 0xcb, 0x74, 0xf4, 0xc6, + 0xb1, 0x6a, 0x2c, 0x6f, 0x2c, 0x3b, 0x30, 0x13, 0x36, 0x09, 0xd1, 0xd8, 0x60, 0xeb, 0xc8, 0x82, 0x55, 0xcf, 0x18, + 0x9b, 0x77, 0xe9, 0x2d, 0x12, 0xde, 0x95, 0x2d, 0x1c, 0xa6, 0xfa, 0x42, 0xc6, 0x59, 0x2f, 0xd7, 0xf2, 0xe9, 0x3a, + 0x01, 0x0e, 0x12, 0x86, 0x17, 0xc4, 0x18, 0xfe, 0xe2, 0xbc, 0x49, 0x55, 0xb0, 0x28, 0xb4, 0x6d, 0x7c, 0x51, 0x7b, + 0x10, 0xcf, 0x4a, 0x10, 0xdf, 0xca, 0xb8, 0xea, 0xa0, 0x1b, 0x8e, 0x30, 0x57, 0xc3, 0x26, 0x84, 0x56, 0x10, 0x81, + 0x9a, 0xfa, 0x33, 0x0d, 0xd5, 0xb5, 0xae, 0xf2, 0x42, 0xa2, 0xe4, 0xb3, 0x68, 0x1c, 0x41, 0x21, 0x07, 0x83, 0xc2, + 0x09, 0x3d, 0xd8, 0x3d, 0xf8, 0x8d, 0x83, 0x71, 0xc1, 0x71, 0x43, 0xfe, 0xda, 0x2d, 0x6b, 0xdc, 0x33, 0x30, 0x95, + 0x97, 0x2b, 0xcd, 0xe6, 0x00, 0x2a, 0x83, 0x5d, 0x6c, 0x48, 0x3e, 0x5b, 0xf4, 0x84, 0xbe, 0xbb, 0xa1, 0x01, 0x0c, + 0x0f, 0x8f, 0xbc, 0x99, 0x7f, 0x23, 0x01, 0x0f, 0x0e, 0x66, 0xe5, 0x97, 0x0b, 0xa1, 0x58, 0x7d, 0x11, 0x20, 0x40, + 0x7c, 0x8d, 0xee, 0x07, 0x41, 0x74, 0x84, 0x60, 0x45, 0x1d, 0x0b, 0xe0, 0x44, 0xc5, 0x29, 0x39, 0x22, 0xc0, 0x38, + 0x41, 0x7d, 0x13, 0x34, 0xf3, 0x7b, 0xa3, 0xfc, 0x0b, 0xb7, 0x9b, 0x79, 0xe2, 0x59, 0x3f, 0x9b, 0xd7, 0x8b, 0x24, + 0x4f, 0xe0, 0x51, 0xd3, 0x81, 0x12, 0x85, 0xd2, 0x0d, 0xee, 0xa6, 0x14, 0x71, 0x9a, 0x88, 0xd5, 0x42, 0x00, 0xb6, + 0xb5, 0xb2, 0x96, 0x7e, 0xa3, 0x74, 0x8e, 0x3a, 0x87, 0x3d, 0x0b, 0xbe, 0x50, 0x7e, 0x6f, 0x09, 0x5d, 0xd5, 0x68, + 0x3b, 0x97, 0x9a, 0x1f, 0xae, 0x36, 0xb2, 0xa1, 0x75, 0xcd, 0xde, 0x42, 0x50, 0x53, 0x54, 0x86, 0x9a, 0xf2, 0x22, + 0x19, 0xdb, 0x9d, 0x98, 0xfd, 0xd0, 0x48, 0xf2, 0x1a, 0x79, 0x65, 0x7f, 0x43, 0x3b, 0xd3, 0x26, 0x1e, 0xbb, 0x12, + 0x0c, 0xbf, 0x68, 0x29, 0x7d, 0x2d, 0x9c, 0x5d, 0xcb, 0xcf, 0x97, 0xb0, 0x36, 0xa6, 0x00, 0x82, 0x90, 0x7e, 0x36, + 0xda, 0xaa, 0x31, 0xba, 0xd5, 0x63, 0xca, 0x3e, 0xea, 0x31, 0xdf, 0xfd, 0x1e, 0xa9, 0x92, 0x85, 0x20, 0x39, 0x34, + 0xf4, 0xd7, 0x63, 0x64, 0x18, 0xa0, 0x48, 0x22, 0xe4, 0x5b, 0x29, 0x03, 0xf7, 0xef, 0x57, 0x8c, 0x0e, 0xb6, 0xd4, + 0x9c, 0x49, 0xb3, 0xab, 0x67, 0x34, 0x20, 0x6c, 0xb4, 0x1e, 0x26, 0xce, 0x08, 0xe1, 0xa4, 0xb1, 0x7d, 0xaa, 0x22, + 0x12, 0xe9, 0xbd, 0x14, 0x31, 0xd8, 0xb8, 0x52, 0xba, 0xc4, 0x08, 0x6b, 0x66, 0x2c, 0xc7, 0x06, 0x50, 0x39, 0x73, + 0x5b, 0x94, 0xc6, 0x37, 0xad, 0xa0, 0x04, 0xb8, 0x47, 0x0c, 0xf6, 0x41, 0x23, 0x40, 0xae, 0x0b, 0x2a, 0x48, 0x68, + 0x9f, 0x0b, 0xc8, 0x84, 0x06, 0x19, 0x19, 0x13, 0xeb, 0x46, 0x20, 0xb9, 0x7b, 0x7a, 0xd3, 0x2e, 0x01, 0xa6, 0x72, + 0xb2, 0x9a, 0x21, 0x62, 0xe2, 0x78, 0x5d, 0x2d, 0x9c, 0xc0, 0x58, 0x0a, 0xd8, 0x31, 0x76, 0x54, 0x72, 0x2e, 0x76, + 0x68, 0xb4, 0x69, 0xe6, 0x17, 0xba, 0x3e, 0x43, 0xe1, 0x87, 0xb5, 0x0b, 0xc8, 0xc8, 0xa9, 0xdb, 0x4b, 0x0f, 0x46, + 0x06, 0x12, 0x57, 0xeb, 0x4e, 0x8b, 0xa4, 0x15, 0x91, 0xcf, 0x8a, 0x7e, 0x75, 0x6c, 0x72, 0x25, 0x2e, 0xd6, 0x8a, + 0x1a, 0x43, 0x91, 0x07, 0xb7, 0xc1, 0x3f, 0x76, 0xf4, 0xb8, 0x75, 0xc2, 0x02, 0x80, 0xf5, 0x58, 0x4e, 0x06, 0x9c, + 0xab, 0xee, 0xe0, 0xd7, 0x40, 0x95, 0xc0, 0x2b, 0x47, 0x9d, 0x45, 0x1c, 0x5f, 0x58, 0xa0, 0x18, 0xfc, 0xeb, 0x14, + 0x79, 0x0c, 0x76, 0x83, 0x2c, 0xe9, 0xa6, 0x59, 0x04, 0x7b, 0x4a, 0x79, 0x26, 0x62, 0xfe, 0xaa, 0x91, 0x34, 0x2a, + 0xac, 0x78, 0x9a, 0x6a, 0xa9, 0x13, 0x3e, 0x55, 0x09, 0x05, 0xc2, 0x1e, 0x82, 0xa6, 0x00, 0xde, 0x9b, 0x12, 0xf3, + 0xf8, 0xa6, 0x85, 0xc4, 0xf9, 0xc9, 0x3a, 0x9b, 0x35, 0x63, 0x06, 0xba, 0x92, 0x80, 0x6e, 0x4e, 0x35, 0x0d, 0xb7, + 0xe8, 0xba, 0x2c, 0x85, 0xa5, 0x64, 0x85, 0x5a, 0x82, 0x89, 0x30, 0x19, 0xde, 0x06, 0x17, 0x90, 0xbc, 0x37, 0x69, + 0x66, 0xdc, 0x3c, 0xbd, 0xaa, 0xf2, 0x04, 0x9a, 0xc7, 0x7d, 0x99, 0x2f, 0x34, 0xa5, 0xb9, 0xc2, 0x01, 0x48, 0x7b, + 0xc1, 0x3c, 0x16, 0x1a, 0x67, 0x52, 0x32, 0xfd, 0x8e, 0x8b, 0x99, 0xd4, 0x54, 0x71, 0x17, 0xd6, 0x09, 0x2b, 0x40, + 0x22, 0x59, 0x32, 0x18, 0x3c, 0x03, 0x8a, 0xf7, 0x05, 0xe0, 0x88, 0x68, 0x14, 0xbe, 0xb3, 0xa3, 0x1c, 0xad, 0x4a, + 0x42, 0x88, 0xcc, 0x56, 0xec, 0xbc, 0x78, 0xa3, 0x1c, 0x45, 0xce, 0x38, 0xda, 0x01, 0x6c, 0x5e, 0x7f, 0xc8, 0x7c, + 0x06, 0x81, 0xac, 0x1f, 0x27, 0x3a, 0x9b, 0x9b, 0xa6, 0x4b, 0x91, 0xce, 0x46, 0x73, 0x96, 0x17, 0x78, 0xc6, 0x29, + 0x13, 0x3c, 0x96, 0x8d, 0xe2, 0x86, 0xa8, 0xf3, 0x4f, 0xd4, 0x01, 0xa7, 0xda, 0x66, 0x7b, 0x33, 0x58, 0x3d, 0x2d, + 0x4e, 0x0e, 0x18, 0x95, 0x9c, 0xcd, 0xa3, 0xd5, 0xeb, 0xfd, 0x5f, 0x4e, 0xbe, 0x2a, 0x63, 0x81, 0xc6, 0xab, 0x9c, + 0xaa, 0xc8, 0xc8, 0x74, 0xc0, 0x89, 0x97, 0x9a, 0xcf, 0xc5, 0x00, 0x2d, 0x32, 0xaf, 0x4a, 0x32, 0x14, 0x92, 0xd5, + 0xb0, 0xf2, 0x06, 0x1a, 0x64, 0xd3, 0xd5, 0x50, 0xa3, 0xe0, 0x08, 0x59, 0xd2, 0x62, 0x63, 0xb6, 0x58, 0xac, 0x79, + 0xad, 0x99, 0x36, 0xc7, 0x08, 0x22, 0xb0, 0x38, 0x20, 0xae, 0x3f, 0xab, 0x35, 0x36, 0x30, 0x89, 0x57, 0xbb, 0x11, + 0x06, 0xdd, 0x0d, 0x2d, 0xa9, 0x4e, 0x8c, 0xa5, 0x12, 0x44, 0x4e, 0x1d, 0x69, 0xec, 0x47, 0x9e, 0xaf, 0xf9, 0xe3, + 0x9e, 0x05, 0xc6, 0xb2, 0x3c, 0x18, 0x19, 0xaa, 0x18, 0x52, 0xda, 0x0d, 0x66, 0x1e, 0xa2, 0x7e, 0x7a, 0xa4, 0x56, + 0xe5, 0x4c, 0xed, 0x31, 0x3c, 0x16, 0x9d, 0x4b, 0xf2, 0xe1, 0x51, 0x37, 0x04, 0x64, 0x5b, 0x81, 0xd5, 0xa7, 0x0e, + 0x22, 0x8a, 0x40, 0xd8, 0xcf, 0xe9, 0x1f, 0xbb, 0x91, 0xff, 0x14, 0xb0, 0x54, 0xaf, 0x87, 0xba, 0xa5, 0xcc, 0x31, + 0x25, 0x6b, 0x79, 0xcb, 0x29, 0xa8, 0xb8, 0x74, 0x55, 0x3a, 0x79, 0x20, 0xb6, 0x10, 0x29, 0x58, 0xcc, 0x3e, 0x9f, + 0x2e, 0x1c, 0xa8, 0xa4, 0x50, 0x7d, 0xdf, 0x05, 0x79, 0x7e, 0xb8, 0x71, 0x50, 0x8b, 0x31, 0xc6, 0x43, 0xaa, 0xad, + 0xaf, 0x1d, 0xdc, 0x8a, 0xbd, 0x0d, 0x3c, 0x3f, 0xb1, 0xdf, 0xef, 0xb7, 0x6c, 0x94, 0x91, 0x95, 0xc2, 0x8a, 0xdc, + 0xd4, 0xa2, 0xf3, 0xc3, 0x49, 0x38, 0x79, 0x42, 0x19, 0x90, 0x97, 0x33, 0xd8, 0x02, 0x59, 0x74, 0x53, 0xf4, 0xc2, + 0x68, 0xb3, 0xbe, 0xe5, 0xe2, 0x5b, 0xbf, 0xc3, 0x21, 0x93, 0x94, 0x2e, 0x69, 0x3a, 0xdf, 0xeb, 0x52, 0x7d, 0x17, + 0x59, 0xb4, 0x48, 0x67, 0xd5, 0xb6, 0xfb, 0x45, 0xaa, 0xb5, 0x17, 0x22, 0x59, 0x32, 0x1c, 0xc5, 0xde, 0xb9, 0x20, + 0xb5, 0x74, 0x06, 0xd5, 0xf4, 0x63, 0x32, 0x8e, 0x5d, 0x02, 0xad, 0x15, 0x4c, 0x6f, 0xe1, 0xc5, 0x20, 0xdb, 0x48, + 0x61, 0x91, 0x16, 0x82, 0x35, 0x9a, 0x39, 0xd2, 0x7e, 0x90, 0x28, 0x2c, 0x03, 0x74, 0x96, 0xf4, 0x39, 0xb3, 0x87, + 0xa3, 0x78, 0x84, 0x2a, 0xa2, 0xd4, 0xfd, 0x61, 0x42, 0x85, 0x54, 0xa7, 0x79, 0x82, 0xa2, 0x3d, 0x1f, 0xb9, 0x63, + 0x03, 0xe6, 0xa7, 0x33, 0xd1, 0xae, 0xbf, 0x5a, 0x02, 0x16, 0x5e, 0x7e, 0x48, 0x71, 0x9b, 0xd2, 0xdb, 0xf9, 0x1f, + 0xf3, 0x39, 0xa5, 0x3c, 0x33, 0x74, 0x4a, 0xa9, 0xd0, 0xcc, 0xe6, 0xc2, 0x0a, 0x49, 0xa5, 0xf9, 0x70, 0x67, 0x0d, + 0xba, 0x19, 0x82, 0x12, 0x09, 0xc5, 0x8d, 0x60, 0x16, 0xa3, 0x18, 0x6b, 0xa0, 0x72, 0x37, 0x6f, 0xd5, 0x49, 0xa5, + 0xb9, 0x53, 0x95, 0x5c, 0xf1, 0xdd, 0xcf, 0x8d, 0x82, 0x61, 0x08, 0xb1, 0xe9, 0xf8, 0x22, 0x25, 0xcb, 0x4b, 0x39, + 0xac, 0xc6, 0x95, 0x21, 0x82, 0x96, 0x41, 0x9c, 0x10, 0xac, 0xe4, 0x12, 0xd4, 0x56, 0x98, 0xee, 0x54, 0x89, 0x52, + 0x41, 0x1f, 0x28, 0xbd, 0xba, 0x83, 0xe6, 0xc4, 0x36, 0x84, 0xb7, 0xa6, 0xa1, 0x80, 0x98, 0xf5, 0xdf, 0x07, 0x19, + 0x1d, 0x3a, 0x7e, 0x2b, 0x19, 0x53, 0x21, 0x50, 0x33, 0x47, 0xcb, 0xcb, 0x80, 0x4d, 0x0a, 0x71, 0xa5, 0x28, 0x4e, + 0x04, 0x71, 0xd8, 0xc7, 0xa6, 0xe6, 0xd3, 0xc7, 0x41, 0x62, 0x1a, 0xd6, 0x65, 0x03, 0xa4, 0xd6, 0x0b, 0x91, 0xf8, + 0x35, 0xf5, 0xe6, 0xa0, 0x09, 0xe3, 0x75, 0xa8, 0x20, 0x66, 0xc5, 0x69, 0x23, 0x05, 0x63, 0x95, 0x86, 0x43, 0x50, + 0x8e, 0x0c, 0xcb, 0xc4, 0xfa, 0xd2, 0x2c, 0xd1, 0x1b, 0x26, 0x06, 0xb6, 0x63, 0xa5, 0x84, 0x00, 0x38, 0x33, 0x66, + 0xdd, 0x8e, 0x49, 0xab, 0x0a, 0xea, 0x81, 0xea, 0xb3, 0xbe, 0xe8, 0x78, 0x86, 0x98, 0xf0, 0x21, 0x01, 0x47, 0xf3, + 0x45, 0xd2, 0x73, 0x6b, 0xa2, 0x25, 0x6d, 0x6a, 0x88, 0x3f, 0x31, 0xd2, 0x5a, 0xb4, 0xd2, 0x81, 0xaf, 0x00, 0x54, + 0x90, 0xa9, 0xe0, 0x12, 0x55, 0x52, 0x36, 0x15, 0x95, 0x07, 0x93, 0x72, 0x6d, 0x99, 0x95, 0x55, 0xee, 0x5d, 0x1f, + 0xe1, 0xcf, 0xb4, 0x50, 0xd2, 0xba, 0x43, 0x7c, 0xa9, 0xe0, 0xaf, 0x51, 0x48, 0x11, 0xf5, 0x99, 0x91, 0x5d, 0x1d, + 0xf3, 0xec, 0x91, 0x95, 0xff, 0xda, 0xc6, 0xaf, 0x5d, 0x28, 0x31, 0xca, 0xdd, 0x7b, 0x64, 0x32, 0xb2, 0x85, 0x80, + 0xa8, 0xdb, 0xd8, 0x0f, 0x47, 0xea, 0xf8, 0xe3, 0x90, 0xe2, 0x3f, 0x5d, 0x05, 0x51, 0x7b, 0xd2, 0x42, 0xaa, 0x83, + 0x9e, 0x03, 0x6b, 0xd0, 0x9a, 0x34, 0x7a, 0xd0, 0xbd, 0x07, 0x2a, 0x57, 0x04, 0xe7, 0x8f, 0x6e, 0xc2, 0x44, 0x05, + 0x9e, 0x02, 0xfe, 0xc2, 0x14, 0x84, 0x59, 0x23, 0x50, 0xdd, 0x2e, 0xda, 0x3e, 0x6f, 0x33, 0x66, 0x90, 0xf7, 0x6e, + 0xdf, 0x08, 0x3f, 0xa2, 0x1e, 0x36, 0x5b, 0xfd, 0x9b, 0x6f, 0x79, 0x94, 0xa8, 0x2c, 0x84, 0xa9, 0x91, 0x50, 0x53, + 0x67, 0x49, 0xe0, 0x47, 0x37, 0xb1, 0x86, 0xd9, 0x7e, 0xb2, 0x56, 0xb8, 0x54, 0x08, 0x99, 0x22, 0x10, 0x9d, 0x21, + 0xcc, 0xa8, 0xf3, 0x44, 0x01, 0xbc, 0xad, 0x00, 0xb4, 0x04, 0xfd, 0x18, 0x6c, 0x73, 0xfb, 0x84, 0xd0, 0x5c, 0xcc, + 0xf3, 0x47, 0x4c, 0x42, 0x41, 0x8a, 0x9f, 0xe5, 0xd3, 0xac, 0x79, 0xa1, 0x12, 0x15, 0xd7, 0x50, 0xb4, 0x15, 0xd7, + 0xc1, 0x03, 0x63, 0xd6, 0x47, 0xd1, 0xa9, 0x8d, 0x29, 0xcd, 0xe2, 0x66, 0x97, 0x68, 0x47, 0xea, 0x6e, 0x3c, 0x9f, + 0x34, 0xdc, 0x89, 0x24, 0xa1, 0x2c, 0x43, 0xab, 0xdb, 0xa6, 0x4b, 0x71, 0x0a, 0xe7, 0x74, 0x5e, 0x7e, 0xcb, 0x10, + 0xef, 0xbf, 0xe6, 0xf8, 0xf4, 0x39, 0xab, 0x66, 0x9e, 0x1f, 0x3d, 0x12, 0x5a, 0x60, 0x66, 0x2d, 0x76, 0xf3, 0x28, + 0x8b, 0x35, 0x24, 0xb0, 0xc3, 0x86, 0xa1, 0x13, 0x5e, 0x6f, 0x59, 0x3c, 0x54, 0x5b, 0x0f, 0x36, 0xde, 0x53, 0x18, + 0xba, 0x5b, 0xd2, 0x46, 0xd6, 0x35, 0x51, 0xd1, 0xcf, 0x6f, 0x91, 0xcd, 0xdd, 0xfe, 0xb8, 0x4c, 0x9a, 0x14, 0x15, + 0xa7, 0xa3, 0xdd, 0xe9, 0xc5, 0xdf, 0x18, 0x33, 0xf3, 0x18, 0x39, 0x65, 0x32, 0xd6, 0xd6, 0x19, 0x6d, 0xb9, 0xb5, + 0x73, 0x95, 0xf5, 0x64, 0xe0, 0x77, 0x82, 0xe4, 0xe7, 0x47, 0x39, 0x68, 0x44, 0x20, 0xa8, 0xdd, 0xae, 0x51, 0xc8, + 0x86, 0x03, 0x33, 0xc7, 0x7f, 0x67, 0x6d, 0xfb, 0x3e, 0x79, 0x9b, 0xf5, 0x62, 0x76, 0xc0, 0xf5, 0xc6, 0x46, 0x73, + 0x64, 0x6e, 0x57, 0x23, 0x1b, 0x3a, 0xdc, 0x91, 0x50, 0xdf, 0x1c, 0x99, 0x67, 0xc7, 0x7c, 0x69, 0x44, 0x70, 0x36, + 0x3a, 0x02, 0xc3, 0x41, 0x9b, 0xeb, 0xbf, 0x49, 0xf2, 0x3d, 0x93, 0x18, 0xe0, 0x80, 0x0d, 0xb2, 0x7a, 0x27, 0x0b, + 0x42, 0xc5, 0x2d, 0x1d, 0xf0, 0x72, 0x9f, 0x07, 0x14, 0x6f, 0xa2, 0x52, 0x72, 0xbd, 0x39, 0x13, 0x15, 0x9a, 0xbb, + 0x7b, 0xe3, 0x6d, 0xab, 0xf1, 0x42, 0xfd, 0xfb, 0x7a, 0x97, 0xda, 0xdf, 0xfc, 0xb1, 0xe9, 0xbb, 0x2c, 0x3f, 0x6b, + 0x51, 0xa7, 0xe7, 0x22, 0xe3, 0x79, 0xd3, 0x2e, 0xd1, 0x1e, 0x46, 0xaa, 0xc9, 0x6a, 0x09, 0xcd, 0x8d, 0xd8, 0xe6, + 0x1d, 0xb7, 0x3a, 0xe0, 0x55, 0x98, 0x55, 0xcd, 0x89, 0x78, 0xef, 0x49, 0xe0, 0x7a, 0xea, 0xc9, 0xda, 0x84, 0xdb, + 0xa1, 0x57, 0x76, 0xb3, 0x33, 0x86, 0xce, 0xa7, 0xd5, 0x3f, 0xd9, 0x47, 0xf0, 0x9b, 0x93, 0xcd, 0xbf, 0x37, 0x35, + 0xa5, 0xc9, 0xdb, 0x93, 0x69, 0xd6, 0x93, 0xa7, 0x1b, 0xc3, 0xd9, 0x96, 0xf6, 0xd3, 0xe6, 0xc3, 0x6c, 0xda, 0x7f, + 0x29, 0xdf, 0x54, 0x85, 0x49, 0xa9, 0xff, 0x04, 0x96, 0x7a, 0x64, 0xa1, 0xf7, 0x1a, 0x43, 0xfc, 0xaa, 0x0a, 0x07, + 0x17, 0x35, 0xdd, 0x0f, 0xe3, 0xdd, 0x68, 0xe2, 0xd6, 0xe5, 0x65, 0x69, 0xce, 0x6a, 0xc4, 0x49, 0xee, 0x49, 0xab, + 0xeb, 0x5d, 0xe6, 0x39, 0xb4, 0xcb, 0xbf, 0x17, 0x08, 0xb7, 0x26, 0x28, 0x68, 0x5d, 0x6a, 0x9b, 0x75, 0x7e, 0x16, + 0x58, 0xfe, 0x1b, 0x59, 0x4f, 0xd7, 0x57, 0xb1, 0xeb, 0x97, 0x2a, 0x3f, 0xff, 0x14, 0xfe, 0x5e, 0xf4, 0xa4, 0xf9, + 0xeb, 0xc1, 0xd9, 0xe7, 0xf9, 0x9f, 0xa3, 0x4c, 0x9b, 0x5a, 0x75, 0xeb, 0x29, 0xfc, 0xf3, 0x78, 0x28, 0x66, 0xb3, + 0xf1, 0xd7, 0x56, 0xf3, 0x3b, 0x84, 0x57, 0xff, 0xf1, 0xe2, 0xe7, 0x2f, 0xcd, 0xc0, 0x7c, 0xe8, 0x9f, 0xe6, 0x6c, + 0xea, 0x62, 0xfd, 0x17, 0x29, 0xeb, 0xeb, 0x3b, 0x6f, 0x4c, 0x34, 0xac, 0xf8, 0xb6, 0xe9, 0xd6, 0x6c, 0x56, 0x47, + 0x95, 0x7f, 0xe1, 0xda, 0xbf, 0x3d, 0xf5, 0x19, 0x04, 0xf9, 0xbc, 0x93, 0x7a, 0xde, 0x18, 0xee, 0x96, 0x14, 0xf6, + 0xd9, 0x72, 0xef, 0xd7, 0x0b, 0x3f, 0x1e, 0x2f, 0xca, 0xd6, 0x51, 0x37, 0x59, 0x59, 0x35, 0xd7, 0x7e, 0xb1, 0x26, + 0x39, 0xdb, 0x15, 0x38, 0xff, 0xb4, 0x7c, 0x3c, 0xfe, 0xe7, 0xe4, 0x69, 0x5d, 0x8e, 0x66, 0x30, 0xe3, 0x3d, 0x9a, + 0x27, 0x9a, 0x37, 0x26, 0xd3, 0x66, 0xbf, 0xfd, 0x10, 0xdf, 0x9a, 0x6e, 0xdd, 0x9b, 0xaf, 0xf8, 0x01, 0x57, 0xcc, + 0xa9, 0xef, 0x5a, 0xf9, 0x5d, 0x4f, 0x0d, 0x71, 0xc1, 0xd8, 0x04, 0x12, 0x8f, 0xfd, 0xdf, 0xc1, 0xd8, 0x0f, 0xbd, + 0x97, 0xde, 0x6c, 0x11, 0xdf, 0x09, 0x61, 0xd7, 0xac, 0x94, 0x73, 0x91, 0x8e, 0xd8, 0xc6, 0x70, 0x9c, 0xf9, 0x40, + 0x45, 0xdb, 0x27, 0xef, 0x37, 0x3e, 0xea, 0x77, 0xa1, 0xf6, 0xe0, 0xfa, 0xe1, 0xf2, 0xb5, 0x78, 0xef, 0x9f, 0x09, + 0xe1, 0x65, 0x03, 0x62, 0x5e, 0xf1, 0xee, 0xf6, 0x3f, 0x46, 0xc5, 0x29, 0x14, 0x75, 0xa2, 0xb2, 0x66, 0xdb, 0x66, + 0xf6, 0x7d, 0xb4, 0x78, 0xe8, 0x40, 0xcd, 0xc7, 0xfb, 0x84, 0xc8, 0xa3, 0x8b, 0x74, 0x97, 0xef, 0xa7, 0x12, 0x08, + 0xec, 0x11, 0x05, 0x76, 0x0d, 0xf2, 0x69, 0xd7, 0x83, 0xbf, 0xc0, 0x9b, 0xb0, 0xb1, 0xb9, 0x7a, 0xb7, 0x73, 0xc8, + 0x1e, 0xae, 0xe6, 0xc5, 0xfa, 0x14, 0x40, 0x12, 0x9b, 0x84, 0x80, 0xf9, 0x3f, 0xb9, 0xa0, 0x86, 0xad, 0xd7, 0xe9, + 0xe7, 0x17, 0xa3, 0xee, 0xd4, 0xa4, 0x59, 0x47, 0x18, 0xe9, 0x5e, 0x78, 0xb5, 0xa1, 0x13, 0x1e, 0x72, 0x8c, 0xf5, + 0x8a, 0xd2, 0x4a, 0x0b, 0xee, 0xd4, 0x47, 0x9d, 0x95, 0x9f, 0x1b, 0x90, 0x88, 0x6c, 0x95, 0x0d, 0xee, 0x32, 0xb3, + 0xf7, 0x0a, 0x6e, 0x58, 0xa5, 0x36, 0x2f, 0xa1, 0xce, 0xf8, 0x3d, 0x57, 0x53, 0x6a, 0xeb, 0xab, 0x6e, 0xde, 0xc4, + 0xcf, 0xed, 0xc5, 0x42, 0x7a, 0xc3, 0x2e, 0xad, 0x0d, 0x48, 0xe0, 0xea, 0x1b, 0xda, 0xed, 0xd4, 0x68, 0xc4, 0x40, + 0x3e, 0x4e, 0x82, 0xf3, 0x5c, 0x33, 0x53, 0x18, 0xef, 0x1a, 0x6a, 0x65, 0xd1, 0x2d, 0xc7, 0x45, 0x93, 0xb7, 0xed, + 0xff, 0x5d, 0x46, 0x8e, 0xeb, 0xe1, 0x0c, 0xe0, 0x36, 0x0f, 0xa0, 0x9f, 0x55, 0x17, 0x56, 0xb9, 0x99, 0x3f, 0xdd, + 0x1a, 0x0c, 0x2a, 0x1f, 0x7a, 0x98, 0x72, 0xfc, 0x46, 0x4e, 0xff, 0x11, 0xb0, 0x6b, 0xeb, 0xb9, 0x96, 0x7b, 0xde, + 0xec, 0xc5, 0x7b, 0x33, 0x9d, 0xcd, 0x4c, 0x99, 0xf5, 0x58, 0xc5, 0x94, 0x13, 0x5f, 0xdb, 0xd5, 0xb7, 0x8b, 0xef, + 0xf6, 0xe1, 0x93, 0x0d, 0x4e, 0x01, 0x2b, 0x68, 0x28, 0x88, 0x83, 0xca, 0xcf, 0xf7, 0x6f, 0xee, 0x98, 0xdc, 0x06, + 0xc9, 0xf4, 0x6a, 0xe1, 0x3a, 0x9e, 0xf4, 0x17, 0x16, 0xfd, 0x59, 0x2b, 0xa1, 0xbf, 0x00, 0xc3, 0x07, 0x6e, 0xcf, + 0x99, 0xe9, 0xdc, 0xc5, 0xa2, 0x7c, 0x9a, 0x71, 0x2b, 0xb9, 0x9b, 0x33, 0x6a, 0x4d, 0x73, 0x00, 0x90, 0x16, 0x4a, + 0x8d, 0x3f, 0x51, 0xd5, 0xa1, 0xa4, 0xc6, 0xb7, 0x91, 0x2a, 0x74, 0x74, 0x59, 0xe4, 0xa7, 0x8b, 0x2b, 0x62, 0xe1, + 0x75, 0x70, 0x7b, 0x8a, 0xe1, 0x8b, 0xef, 0x99, 0xd3, 0xf2, 0x83, 0xca, 0x37, 0x85, 0xe2, 0x6c, 0xd8, 0x18, 0x79, + 0xeb, 0xfe, 0xd4, 0x12, 0x34, 0x7c, 0x8b, 0xde, 0x9a, 0x57, 0xff, 0xed, 0x69, 0x15, 0xa0, 0x5b, 0x1c, 0xe1, 0xe9, + 0x0e, 0x8a, 0x66, 0xee, 0xa9, 0x78, 0x51, 0x06, 0xca, 0xe4, 0x75, 0x3f, 0xe3, 0x45, 0x70, 0x52, 0x68, 0x9f, 0xd7, + 0xcf, 0xeb, 0x05, 0x55, 0x33, 0xc9, 0xe9, 0xed, 0xc2, 0xbc, 0xe1, 0xfb, 0xeb, 0x16, 0x5f, 0x67, 0x29, 0x53, 0xb1, + 0x1d, 0x94, 0xd1, 0x6f, 0x0b, 0x60, 0xbe, 0xfa, 0x92, 0x89, 0x05, 0x9d, 0xac, 0xb9, 0x59, 0xcd, 0xf7, 0xb6, 0x67, + 0x7e, 0x8f, 0xe0, 0xe5, 0x5b, 0xa5, 0x68, 0xeb, 0x67, 0x95, 0x67, 0x2d, 0x30, 0x77, 0x08, 0x31, 0x37, 0x91, 0x06, + 0x8b, 0x02, 0xf2, 0xdd, 0xcd, 0x4b, 0xcb, 0x28, 0xf3, 0x68, 0xde, 0xfc, 0xb3, 0x5e, 0x50, 0x07, 0xa4, 0x17, 0xdf, + 0xb9, 0xdc, 0x40, 0x42, 0x8b, 0x7b, 0xa1, 0xc6, 0xdb, 0xe8, 0x71, 0xca, 0xac, 0x3c, 0x40, 0xb2, 0x86, 0x0e, 0x5a, + 0xdd, 0x87, 0x74, 0x5c, 0x1c, 0x5f, 0xa3, 0xe9, 0xfb, 0x26, 0xde, 0x4e, 0x74, 0x35, 0x79, 0x4f, 0xd9, 0x6d, 0x96, + 0x81, 0x12, 0xcb, 0xcb, 0x0b, 0x79, 0x27, 0xac, 0xa5, 0xa4, 0xb9, 0x0e, 0x13, 0x67, 0x83, 0xfa, 0xeb, 0x99, 0x97, + 0x5e, 0xd6, 0x3e, 0xe0, 0x1b, 0x85, 0x0a, 0xee, 0xe7, 0x09, 0x35, 0x82, 0xfd, 0x20, 0x45, 0xea, 0x41, 0x9d, 0x30, + 0xa3, 0x06, 0x23, 0x69, 0xba, 0xb4, 0x14, 0x67, 0x4e, 0xfa, 0x8b, 0x8a, 0xd2, 0x85, 0x5d, 0xbe, 0xad, 0x62, 0xa9, + 0x4e, 0x6f, 0x63, 0xa4, 0x3c, 0x6a, 0xde, 0x9b, 0xb7, 0x45, 0xde, 0x4e, 0x1b, 0x12, 0x22, 0xe9, 0x05, 0x72, 0xd9, + 0x3a, 0x3f, 0x84, 0xee, 0xe7, 0x2c, 0x1e, 0xc1, 0xa6, 0x84, 0x51, 0x90, 0xeb, 0x32, 0xd7, 0x7b, 0x43, 0x03, 0x13, + 0xf2, 0x63, 0x7e, 0x96, 0x80, 0xa5, 0x6d, 0xdd, 0x7a, 0x67, 0x7c, 0x68, 0x99, 0x43, 0xef, 0x96, 0x00, 0x32, 0xb7, + 0x6b, 0xf6, 0xae, 0xd6, 0x39, 0x99, 0x38, 0x24, 0x35, 0xa0, 0xef, 0x19, 0x75, 0xfa, 0xc6, 0x32, 0xb1, 0x48, 0xa4, + 0xa6, 0x37, 0x89, 0x95, 0xe6, 0xb1, 0xa7, 0xaf, 0x4e, 0x3d, 0x03, 0xbe, 0x36, 0xf7, 0x9a, 0xdd, 0xc7, 0x06, 0xec, + 0xb0, 0xd0, 0xc6, 0xee, 0x02, 0xe6, 0xf2, 0xa6, 0xdd, 0x4e, 0xa8, 0x3c, 0xba, 0x71, 0xcc, 0x0d, 0xc1, 0x40, 0x7a, + 0x11, 0x8d, 0xa2, 0xfc, 0xbe, 0xea, 0x49, 0xec, 0x75, 0x07, 0x76, 0xb7, 0xfb, 0xb3, 0x23, 0x55, 0xb8, 0xbd, 0x4c, + 0x06, 0x7e, 0xb2, 0x3d, 0x23, 0x79, 0x68, 0x2a, 0xf6, 0x80, 0x26, 0x1b, 0xde, 0x05, 0xe2, 0x86, 0xf1, 0xbe, 0x0f, + 0xfb, 0xfb, 0x92, 0xaf, 0x09, 0xa8, 0x71, 0x20, 0x41, 0xb5, 0x64, 0xcf, 0x5f, 0xae, 0xcc, 0xe6, 0x32, 0xcc, 0x26, + 0x5e, 0xb9, 0xa8, 0xf3, 0xfe, 0xe9, 0xb5, 0x83, 0xfb, 0x2d, 0x35, 0x94, 0x9b, 0xf1, 0xcc, 0xff, 0x47, 0x4d, 0x61, + 0x43, 0xe0, 0x01, 0x59, 0x69, 0x21, 0xb9, 0xb2, 0xc0, 0xa7, 0x6f, 0x0e, 0x75, 0x3e, 0x8c, 0xe7, 0x2d, 0x66, 0x65, + 0x46, 0xe4, 0x62, 0x7c, 0x80, 0x48, 0x36, 0x50, 0x0c, 0x13, 0x2e, 0x60, 0xf4, 0xd1, 0x65, 0xda, 0xa2, 0x79, 0x20, + 0xed, 0xca, 0xd6, 0x1f, 0xcf, 0x0c, 0xbc, 0x92, 0xff, 0xc6, 0x79, 0x5c, 0x86, 0x39, 0xbe, 0xd2, 0xd8, 0x9e, 0x92, + 0xe7, 0xc2, 0x15, 0x19, 0xe5, 0xa1, 0xaa, 0x3c, 0xe9, 0x9c, 0xbb, 0xbb, 0x7a, 0x32, 0x1d, 0xd9, 0x0c, 0x60, 0x6e, + 0x69, 0xda, 0xd8, 0xb1, 0x52, 0x5d, 0xf2, 0x10, 0x6f, 0x30, 0x18, 0xec, 0xcb, 0xd6, 0xad, 0x3f, 0xdb, 0x28, 0x68, + 0xb8, 0x42, 0x10, 0x58, 0x82, 0x81, 0xab, 0x92, 0x04, 0xe9, 0x0f, 0x45, 0xde, 0xb9, 0x29, 0x79, 0x4f, 0x3d, 0xb9, + 0x78, 0x25, 0x79, 0x70, 0x68, 0x09, 0x70, 0xd1, 0x7f, 0xd6, 0x5a, 0xc9, 0xda, 0x52, 0xbe, 0x3b, 0xce, 0x3e, 0x76, + 0xba, 0x99, 0x05, 0xd9, 0xd2, 0x87, 0x51, 0x6c, 0xcf, 0xbd, 0x1e, 0xe6, 0xa1, 0x25, 0xb0, 0x90, 0xb9, 0x59, 0xda, + 0x01, 0xf1, 0x2d, 0x9a, 0xd4, 0x66, 0xc9, 0xff, 0xc4, 0x2d, 0x6f, 0x20, 0x44, 0xd4, 0xb6, 0xbe, 0x6b, 0x68, 0x74, + 0x12, 0x27, 0xb9, 0x41, 0xde, 0x7f, 0x53, 0x0a, 0x28, 0x50, 0xb6, 0x54, 0x76, 0x92, 0xdf, 0x7f, 0xe2, 0x21, 0x84, + 0x66, 0x36, 0x5e, 0x5a, 0xb5, 0x6e, 0x33, 0x6b, 0x09, 0xa7, 0x91, 0x30, 0xb3, 0x9b, 0x83, 0xae, 0x2a, 0x12, 0x8e, + 0x92, 0x34, 0xa6, 0x48, 0x47, 0x38, 0xdc, 0x69, 0xbe, 0xbb, 0x93, 0xba, 0x63, 0x01, 0x6b, 0x9b, 0x39, 0x6e, 0x01, + 0x02, 0x8c, 0xfa, 0x5d, 0x03, 0xd1, 0x44, 0x93, 0x53, 0xa8, 0xe5, 0x8d, 0xdc, 0xd5, 0xa3, 0x5b, 0xf3, 0x58, 0x83, + 0xf6, 0x59, 0xfd, 0x29, 0x21, 0xe0, 0xb6, 0xa2, 0xde, 0x93, 0x81, 0x15, 0xa9, 0x0b, 0xc1, 0x35, 0x10, 0x58, 0xef, + 0x8c, 0xd6, 0x3e, 0x35, 0x26, 0xd2, 0xfe, 0xa2, 0xc1, 0x05, 0x24, 0x04, 0x02, 0x98, 0x97, 0x65, 0xb3, 0x84, 0x4f, + 0x22, 0x39, 0x80, 0xaa, 0xc7, 0xa5, 0xb7, 0x5a, 0x4a, 0x44, 0xc3, 0xa3, 0x1a, 0x01, 0xd7, 0xed, 0x02, 0xe5, 0x03, + 0x46, 0x58, 0x39, 0x85, 0x79, 0x26, 0xa4, 0x6a, 0x52, 0x8c, 0xba, 0x99, 0x4d, 0xa4, 0x3c, 0x33, 0xce, 0x53, 0x49, + 0xd4, 0x69, 0xfd, 0x6b, 0xe5, 0x4b, 0x1b, 0x44, 0xdb, 0xf0, 0xd9, 0x70, 0x7d, 0xac, 0xb9, 0x1e, 0x6d, 0x06, 0xa6, + 0xb5, 0xab, 0x59, 0x04, 0x88, 0x7a, 0x2a, 0xbb, 0xab, 0xcf, 0x5c, 0x90, 0x87, 0x1a, 0x3f, 0xf2, 0xe2, 0x14, 0xec, + 0x4a, 0x3f, 0xbf, 0x69, 0x28, 0x40, 0x18, 0x2f, 0x1d, 0xf1, 0x92, 0x55, 0x5e, 0x6c, 0x8a, 0x36, 0xee, 0x30, 0xf6, + 0x7a, 0xb4, 0x02, 0x52, 0x8f, 0x4d, 0xdd, 0x49, 0x96, 0xac, 0x8b, 0x73, 0xca, 0xab, 0xb8, 0x67, 0xba, 0x34, 0x7d, + 0x4c, 0xfd, 0x87, 0x4a, 0xe7, 0xc4, 0x0a, 0xe1, 0x7f, 0x4b, 0xca, 0xce, 0x2a, 0x65, 0x5a, 0x90, 0x88, 0xb5, 0x20, + 0x0a, 0x9c, 0xef, 0x04, 0xc9, 0xc2, 0xb2, 0x88, 0x24, 0x4f, 0x63, 0x79, 0xad, 0x4b, 0xf0, 0x24, 0x7b, 0xa0, 0xc8, + 0x87, 0x5d, 0xd9, 0x25, 0xc1, 0xdc, 0xf3, 0x83, 0xb4, 0x61, 0xa2, 0xb0, 0x0f, 0x5a, 0xf2, 0xb8, 0x66, 0x01, 0x38, + 0x3d, 0xf4, 0x6b, 0xef, 0xf9, 0xd8, 0x36, 0x7e, 0x8b, 0xe0, 0x5d, 0x4e, 0x84, 0xfb, 0x39, 0x97, 0x04, 0xcb, 0xaf, + 0xae, 0x53, 0x66, 0xb1, 0x5a, 0x83, 0x8a, 0x97, 0x3b, 0xbc, 0x6d, 0xdd, 0x5f, 0x96, 0xf0, 0xbe, 0x93, 0xcd, 0x70, + 0x37, 0x1d, 0x91, 0xcd, 0xc4, 0x39, 0x92, 0x8a, 0x44, 0x5c, 0x75, 0x32, 0x8d, 0xc5, 0x87, 0x39, 0x01, 0x04, 0x93, + 0xfa, 0x37, 0x2a, 0x84, 0x36, 0x24, 0x74, 0x7c, 0xec, 0xf2, 0xb5, 0x61, 0xed, 0xd6, 0xd7, 0xca, 0xd6, 0xbe, 0x75, + 0x23, 0x8a, 0x0a, 0xed, 0x58, 0x2c, 0x86, 0x64, 0x8c, 0x5e, 0xe9, 0x37, 0xd6, 0x34, 0xc9, 0xe2, 0xe1, 0xab, 0xdb, + 0x68, 0x31, 0x0e, 0x62, 0x17, 0x78, 0xfb, 0xd1, 0xec, 0x6d, 0x2d, 0x29, 0x7e, 0xff, 0xea, 0x8c, 0xa2, 0x56, 0xfc, + 0x43, 0xe9, 0xcf, 0xba, 0xc0, 0x25, 0x2a, 0x03, 0x2d, 0x66, 0xf8, 0x83, 0x48, 0xab, 0x57, 0xc8, 0xb9, 0xcf, 0xb9, + 0x3e, 0x24, 0xff, 0xc5, 0x03, 0x6f, 0x28, 0x8b, 0x42, 0xa8, 0xeb, 0x11, 0x37, 0x52, 0xc4, 0x62, 0xdd, 0x7d, 0x79, + 0xd0, 0x16, 0x39, 0x0b, 0x66, 0xcd, 0x6e, 0xca, 0x34, 0xdc, 0x85, 0x4b, 0x8b, 0x6e, 0xd3, 0x6c, 0x13, 0xbc, 0x0c, + 0x3b, 0xe9, 0x38, 0x7a, 0x67, 0x03, 0xa1, 0x28, 0x08, 0x10, 0x4a, 0x1a, 0xfa, 0x67, 0x28, 0x6d, 0xa5, 0x98, 0x87, + 0x96, 0x72, 0xca, 0x65, 0x21, 0xe6, 0x7e, 0x42, 0x86, 0x81, 0xfb, 0xc5, 0x8d, 0xdc, 0xb4, 0x16, 0x48, 0x16, 0x89, + 0x1e, 0xf5, 0xbc, 0x7b, 0x72, 0x95, 0xc5, 0xa0, 0x07, 0x44, 0x0e, 0x70, 0xbd, 0x9b, 0xaa, 0x67, 0x25, 0xc1, 0xc0, + 0xd1, 0x7d, 0xc0, 0x5a, 0x5f, 0x5b, 0xc3, 0x44, 0x2b, 0x04, 0x5e, 0x42, 0x8d, 0x19, 0x12, 0xed, 0x03, 0xf5, 0x90, + 0x98, 0x00, 0x34, 0x05, 0xaf, 0xb1, 0x25, 0xd0, 0xb6, 0x6b, 0x4c, 0x09, 0x14, 0xb0, 0x32, 0xd5, 0x88, 0xc6, 0xcc, + 0x43, 0x47, 0x8c, 0xc4, 0x71, 0xee, 0x47, 0xe4, 0xc1, 0x86, 0xd4, 0x21, 0xda, 0xfe, 0xa6, 0x7e, 0xb0, 0xc6, 0x99, + 0x31, 0x8d, 0x5c, 0x20, 0x1c, 0xaf, 0x41, 0xe1, 0x86, 0xb1, 0x61, 0xfb, 0xaa, 0x26, 0xab, 0x3a, 0x23, 0x32, 0xab, + 0x9e, 0x39, 0xec, 0x57, 0xf1, 0x47, 0x97, 0x58, 0x49, 0xb3, 0xe1, 0x9b, 0xa4, 0xd4, 0xb3, 0xe5, 0xd5, 0x37, 0x46, + 0x22, 0x3d, 0xdd, 0x07, 0x5c, 0x70, 0x0d, 0xa2, 0x9b, 0x92, 0x9f, 0x7d, 0x32, 0x6a, 0x00, 0x8f, 0xda, 0xb4, 0x43, + 0x15, 0x14, 0x83, 0x81, 0x91, 0xa6, 0xd3, 0xd2, 0x98, 0x2e, 0xd1, 0x6c, 0xa0, 0x99, 0xc7, 0x78, 0x22, 0xd2, 0x89, + 0xed, 0x1d, 0xcf, 0x57, 0x2d, 0x1a, 0x59, 0xad, 0xda, 0x20, 0xcb, 0x6f, 0xd3, 0x7a, 0xad, 0x32, 0x32, 0xde, 0x96, + 0x01, 0xf1, 0x47, 0x28, 0x0b, 0x86, 0x8a, 0x8a, 0x24, 0xc5, 0x14, 0x15, 0x97, 0xc6, 0x47, 0xae, 0x02, 0x74, 0x19, + 0x56, 0xad, 0xcd, 0xab, 0xf0, 0xf6, 0x49, 0x0c, 0xf7, 0x41, 0xa9, 0xc2, 0xe9, 0xe5, 0x62, 0xb6, 0x3c, 0x56, 0xe1, + 0x8f, 0x5d, 0x75, 0x12, 0x3c, 0x6d, 0xcf, 0xde, 0x39, 0xd5, 0xe8, 0x54, 0x5f, 0x1c, 0xb2, 0x63, 0x2f, 0xce, 0x18, + 0x88, 0x90, 0x93, 0xd9, 0x6a, 0x17, 0x7d, 0x92, 0xee, 0x35, 0x02, 0x7d, 0x39, 0xc2, 0x55, 0xcf, 0x9b, 0x13, 0xca, + 0x6c, 0x35, 0xd2, 0x51, 0x50, 0x9a, 0x21, 0x8a, 0xe1, 0x29, 0x12, 0x07, 0x9e, 0xe6, 0xc4, 0x61, 0xc2, 0x00, 0x25, + 0x6c, 0x73, 0xa2, 0x8b, 0xf6, 0x9f, 0x61, 0x96, 0xef, 0x59, 0xc6, 0x96, 0xe6, 0xd1, 0x80, 0x14, 0x01, 0x26, 0x95, + 0x62, 0x15, 0xff, 0x60, 0x2e, 0x1c, 0x0f, 0x13, 0x83, 0xc9, 0xcf, 0xb0, 0x0f, 0xe5, 0x4d, 0x0f, 0x2f, 0x8f, 0xca, + 0x81, 0x34, 0xb1, 0x4a, 0x3d, 0x45, 0x6b, 0xa4, 0x76, 0xdb, 0x0d, 0x6c, 0xb9, 0xd2, 0x0d, 0xd5, 0xf8, 0xa2, 0x08, + 0x46, 0xff, 0x52, 0x03, 0xe1, 0xe3, 0x93, 0x18, 0x63, 0x30, 0x29, 0x7a, 0x53, 0x3b, 0x30, 0xed, 0x9b, 0x52, 0x75, + 0x2d, 0x80, 0x8f, 0x4d, 0x15, 0xf8, 0xcf, 0xc1, 0x29, 0x22, 0xe6, 0xce, 0x58, 0x4c, 0x56, 0x67, 0x50, 0x97, 0xfb, + 0xdf, 0x0f, 0x1d, 0x41, 0xd8, 0xbf, 0x4e, 0xe7, 0xe8, 0x2c, 0x40, 0x26, 0x7b, 0xe0, 0x82, 0x58, 0x2a, 0xc6, 0x31, + 0x8f, 0x46, 0x84, 0xa5, 0x22, 0x6b, 0xbc, 0x8f, 0x4b, 0x49, 0xf3, 0xb5, 0x0e, 0x1c, 0x10, 0x85, 0x83, 0xf9, 0xad, + 0x41, 0xdf, 0x42, 0xc8, 0xbc, 0xaa, 0x72, 0x00, 0xa8, 0x8b, 0x71, 0x31, 0xae, 0x25, 0x24, 0x23, 0x3f, 0xee, 0xa8, + 0x1d, 0xa3, 0xa1, 0xc9, 0xc7, 0xa7, 0xeb, 0x54, 0xd3, 0xbd, 0xfa, 0x87, 0x1a, 0x8a, 0xf9, 0x7b, 0x99, 0x18, 0x24, + 0x6a, 0x96, 0xec, 0xbd, 0xf8, 0xe9, 0x3c, 0x72, 0x9e, 0x9a, 0x9e, 0x1a, 0xc6, 0xac, 0x56, 0x37, 0x26, 0x5b, 0xa6, + 0x76, 0xe4, 0x0e, 0xb4, 0x3a, 0xe3, 0xeb, 0xf4, 0x06, 0xe2, 0x78, 0x2f, 0x24, 0x6e, 0x45, 0x47, 0x8a, 0xd2, 0x8f, + 0x2b, 0x23, 0xa0, 0x46, 0xd1, 0xa1, 0x2a, 0x99, 0xe6, 0x6f, 0x86, 0x5c, 0x55, 0x41, 0x87, 0x55, 0x50, 0x4d, 0x31, + 0x33, 0xcd, 0xca, 0xa1, 0x91, 0x06, 0x14, 0x4a, 0x69, 0x0c, 0x8a, 0x5a, 0xaa, 0x90, 0xec, 0x79, 0x89, 0xa5, 0xe7, + 0x38, 0x09, 0x1d, 0xca, 0xa6, 0x83, 0xe7, 0x51, 0xb8, 0x24, 0xec, 0x79, 0xcd, 0x0c, 0xd3, 0x64, 0x2b, 0x2d, 0xab, + 0x5a, 0x54, 0x42, 0x21, 0xd7, 0xe7, 0xa5, 0x52, 0x9e, 0x46, 0xb8, 0x8d, 0xa7, 0x34, 0x5a, 0x45, 0xf9, 0x0a, 0xfb, + 0x38, 0xf9, 0x14, 0xf9, 0x77, 0xa0, 0xac, 0xbe, 0x14, 0x40, 0x06, 0x22, 0x09, 0x56, 0x02, 0xf9, 0x7e, 0xf1, 0x82, + 0x8b, 0xf0, 0x8b, 0x00, 0x5e, 0x45, 0xbc, 0xce, 0x74, 0x43, 0x9e, 0xaf, 0x7f, 0xfd, 0x9f, 0xea, 0xf5, 0x9f, 0x29, + 0x1c, 0x6e, 0x80, 0xf4, 0x06, 0xd2, 0x2c, 0xe8, 0x1f, 0xad, 0x57, 0x5f, 0xa9, 0x4b, 0x99, 0xbd, 0x8e, 0xc2, 0x77, + 0xb7, 0x74, 0x6d, 0xf4, 0x6c, 0x24, 0x42, 0xb3, 0x52, 0xfa, 0x5e, 0x48, 0x5a, 0x06, 0x6a, 0xe4, 0x8b, 0xbd, 0xd9, + 0x80, 0x69, 0x6b, 0x9c, 0xc2, 0xed, 0xbd, 0xa4, 0xc6, 0x5b, 0x8b, 0x13, 0xa0, 0xca, 0x62, 0x8a, 0xef, 0xd8, 0x79, + 0x20, 0xf7, 0xc1, 0xa3, 0x36, 0x7e, 0xbb, 0x73, 0x7b, 0x3e, 0x0d, 0xec, 0x12, 0x51, 0x0e, 0xa2, 0x6d, 0x58, 0x65, + 0xec, 0xf5, 0x45, 0x84, 0xcd, 0x65, 0x49, 0x83, 0x92, 0x0a, 0xbc, 0xf1, 0xca, 0x5d, 0xb8, 0xb9, 0x3d, 0x82, 0x00, + 0xfa, 0x4d, 0x13, 0xe6, 0x76, 0x88, 0x54, 0x18, 0x77, 0xe9, 0x71, 0x52, 0xe6, 0xf9, 0x77, 0x7a, 0x1c, 0x33, 0xc6, + 0xce, 0xcc, 0x33, 0xab, 0xd0, 0xd0, 0xb2, 0xa1, 0xf1, 0x53, 0xb0, 0x5b, 0x64, 0x14, 0x6b, 0x45, 0x01, 0xfb, 0xa0, + 0x14, 0x68, 0x79, 0x10, 0x8a, 0xea, 0x22, 0x3e, 0xc1, 0xf1, 0xe1, 0x8f, 0x86, 0x03, 0x25, 0x86, 0x16, 0x09, 0xb6, + 0xd8, 0x23, 0x1d, 0x36, 0xe5, 0xa6, 0xde, 0xa9, 0xb3, 0x0a, 0xe7, 0x4d, 0x63, 0x59, 0x07, 0xa5, 0xdf, 0xd5, 0xe3, + 0x75, 0xfd, 0x84, 0x37, 0xf8, 0x5b, 0x29, 0xd5, 0xe3, 0x17, 0xf5, 0x7e, 0x8d, 0x5d, 0xa5, 0x3a, 0x8c, 0xd1, 0xe2, + 0x4f, 0x26, 0xa4, 0x31, 0x2e, 0xec, 0xa1, 0x7e, 0x25, 0x1d, 0x7c, 0x41, 0xd9, 0xf5, 0xc8, 0xc6, 0x64, 0x3d, 0x28, + 0x80, 0xfb, 0xbc, 0x7f, 0xfb, 0xa8, 0x9f, 0x05, 0x39, 0x34, 0x22, 0x45, 0x4d, 0xfc, 0x6e, 0xc8, 0x4d, 0xaa, 0x51, + 0x10, 0xbb, 0x36, 0xa5, 0x76, 0x78, 0x0f, 0xb5, 0xf7, 0x6f, 0x32, 0xa8, 0x00, 0x6a, 0x7b, 0xd3, 0x8f, 0x65, 0x70, + 0x5a, 0x3d, 0x4d, 0x4e, 0x18, 0xa9, 0x01, 0x52, 0x53, 0xc4, 0x66, 0xc2, 0xca, 0xc5, 0xe7, 0xc0, 0x6c, 0xd6, 0xa4, + 0xb3, 0xf6, 0x16, 0x5c, 0x5a, 0x46, 0xdd, 0xef, 0x59, 0xb8, 0xfb, 0x58, 0x06, 0x9f, 0x17, 0x6e, 0xa9, 0x3b, 0x68, + 0x85, 0x2c, 0x46, 0xad, 0xdc, 0x84, 0x43, 0x7b, 0x55, 0x25, 0x30, 0xd6, 0x6f, 0xd3, 0xc6, 0x59, 0x2f, 0x70, 0x60, + 0xe8, 0xbd, 0x1f, 0xb8, 0xac, 0xfc, 0x14, 0x88, 0x61, 0x78, 0xd5, 0xbc, 0x39, 0xe6, 0x8c, 0x17, 0xef, 0x79, 0x7b, + 0x86, 0x73, 0xfb, 0x5c, 0xf1, 0x47, 0xcf, 0x37, 0x65, 0xa3, 0x7a, 0x92, 0x38, 0x33, 0xeb, 0x58, 0x52, 0xf5, 0xc8, + 0x50, 0x2e, 0xee, 0x01, 0xa0, 0x42, 0x32, 0x2a, 0x82, 0x48, 0x23, 0x8d, 0xf2, 0x53, 0xe5, 0x95, 0xea, 0x7d, 0xc2, + 0x44, 0x89, 0x80, 0x19, 0x7c, 0xff, 0xa4, 0xd2, 0x15, 0xbb, 0x1e, 0xe0, 0x1f, 0x11, 0x2b, 0x88, 0x68, 0x16, 0x49, + 0x28, 0x0a, 0x48, 0xc6, 0xef, 0x8e, 0xe5, 0x91, 0x9d, 0x49, 0x88, 0xe0, 0xa0, 0xee, 0x06, 0x08, 0x10, 0xf3, 0x35, + 0x42, 0xbb, 0xfc, 0x2b, 0x3d, 0xae, 0xd7, 0xac, 0x50, 0x87, 0x59, 0x76, 0xa1, 0x01, 0x6f, 0xb3, 0xe8, 0x97, 0xca, + 0x85, 0xef, 0xb5, 0x76, 0xb2, 0xbe, 0xbc, 0xfd, 0xb8, 0x5c, 0x93, 0xd2, 0xc1, 0xd2, 0x02, 0x50, 0xb2, 0xb1, 0xcc, + 0xc6, 0xa9, 0x5c, 0xb5, 0x5e, 0x59, 0x8a, 0xd2, 0x09, 0xc3, 0x76, 0x08, 0x29, 0x1e, 0x8c, 0x6a, 0xc4, 0xcc, 0xb1, + 0xa6, 0xc7, 0xbd, 0xf4, 0x60, 0x8f, 0x7b, 0x3f, 0x84, 0xce, 0x05, 0x3d, 0x62, 0x1e, 0x01, 0xe7, 0x65, 0xe5, 0xa9, + 0x90, 0x69, 0x42, 0x85, 0x38, 0x08, 0x20, 0x33, 0xae, 0x7b, 0x60, 0x4c, 0x99, 0x16, 0x3b, 0x2c, 0x26, 0xb3, 0x81, + 0x82, 0x90, 0x1b, 0x9b, 0x44, 0x0a, 0x39, 0x32, 0x89, 0xa5, 0x07, 0xf6, 0x33, 0x20, 0x6b, 0x3d, 0x8a, 0xd3, 0x9a, + 0x56, 0x44, 0x97, 0x22, 0x70, 0xb9, 0x91, 0xf2, 0x4d, 0x9f, 0xd0, 0x2b, 0x33, 0x47, 0xc3, 0xf7, 0xdb, 0x59, 0x09, + 0xc3, 0x72, 0x7c, 0xec, 0xec, 0x65, 0xfd, 0xe3, 0x39, 0x85, 0x6a, 0x6e, 0x67, 0x2e, 0x5f, 0x32, 0xf9, 0xef, 0x75, + 0x18, 0x48, 0x5e, 0x28, 0x7c, 0x56, 0x13, 0x88, 0xb4, 0x24, 0xa5, 0xe0, 0xad, 0xe1, 0xef, 0x45, 0x15, 0xc6, 0xfd, + 0x87, 0xef, 0xc2, 0xc5, 0x8d, 0xef, 0xaf, 0xea, 0xbe, 0x8a, 0xae, 0xbd, 0x11, 0x90, 0x74, 0xce, 0x96, 0x3b, 0x6c, + 0xa0, 0xd6, 0x5b, 0x84, 0xb2, 0xce, 0xeb, 0x8b, 0xfb, 0x9a, 0x3c, 0xbb, 0x6e, 0x3f, 0xee, 0x02, 0x8f, 0x98, 0xac, + 0xcd, 0xda, 0x42, 0xf3, 0xc8, 0x1a, 0xdc, 0xfd, 0x0c, 0xc3, 0x3d, 0x80, 0x1d, 0x4d, 0xdd, 0xe2, 0x17, 0xde, 0x8b, + 0xf4, 0x3e, 0x65, 0xab, 0xb7, 0xfa, 0xa7, 0xcd, 0x2f, 0x7f, 0x6e, 0x1c, 0x53, 0xa8, 0x61, 0xed, 0xa6, 0xba, 0x27, + 0x33, 0x7b, 0x30, 0x2d, 0x83, 0x14, 0xae, 0x74, 0xf5, 0x55, 0xc0, 0x51, 0xd0, 0x73, 0x42, 0x07, 0x9b, 0x28, 0x34, + 0x8f, 0x5f, 0x10, 0xaa, 0x64, 0xfe, 0xf1, 0x72, 0x65, 0x0c, 0x82, 0xf0, 0xb7, 0x23, 0xd6, 0x8a, 0xa8, 0xb3, 0x63, + 0x7f, 0xcc, 0xd5, 0x04, 0xbf, 0xa4, 0x1e, 0x8e, 0x16, 0xe1, 0x5f, 0xea, 0xb0, 0xdd, 0x61, 0x96, 0x1e, 0x68, 0xdc, + 0xec, 0x37, 0xf0, 0x8d, 0xe8, 0xcc, 0xc2, 0x8e, 0x2f, 0x4b, 0xb5, 0x43, 0x87, 0x43, 0xcd, 0xb0, 0x04, 0x7a, 0x1e, + 0x06, 0xe8, 0xa1, 0x1b, 0x7b, 0xbb, 0x54, 0x07, 0xe5, 0x20, 0x11, 0xbd, 0x87, 0x42, 0xe8, 0xd1, 0x5c, 0x9d, 0xf6, + 0xa8, 0x07, 0x6e, 0x2b, 0x0c, 0xc8, 0x83, 0xfe, 0xe0, 0x63, 0x56, 0x48, 0xd5, 0x45, 0x75, 0x1d, 0x35, 0xad, 0x31, + 0x23, 0x1f, 0xd3, 0x77, 0xbf, 0xbc, 0x21, 0xa2, 0x1d, 0x59, 0xaf, 0x31, 0xce, 0xb0, 0xf2, 0xa1, 0x4c, 0x85, 0x29, + 0xd5, 0x05, 0xdb, 0x63, 0x43, 0x7f, 0xd6, 0x76, 0x19, 0x59, 0xa1, 0x88, 0x8e, 0x60, 0xe1, 0x7f, 0x7c, 0x59, 0xdc, + 0x0a, 0x32, 0xb2, 0xe6, 0xb7, 0x25, 0x39, 0x63, 0x1c, 0xfa, 0xba, 0x5c, 0xce, 0xbb, 0x58, 0x3d, 0xfa, 0xf0, 0x24, + 0xa4, 0x48, 0xd6, 0x3e, 0x86, 0x56, 0x03, 0x43, 0x04, 0x21, 0xf9, 0x66, 0xad, 0xf5, 0x1c, 0x70, 0x12, 0xf3, 0xbb, + 0x0e, 0xec, 0xb7, 0xf3, 0x3c, 0xef, 0x10, 0x10, 0x20, 0xff, 0x1a, 0x62, 0x9c, 0x55, 0xd4, 0x3b, 0xd3, 0xa2, 0xaa, + 0x97, 0x8b, 0x59, 0x61, 0x4d, 0xc7, 0x98, 0x34, 0x54, 0x5e, 0xca, 0xa6, 0x52, 0x17, 0x32, 0x9a, 0xc7, 0x82, 0x7e, + 0x74, 0x79, 0x9d, 0xe1, 0xac, 0xa1, 0x3d, 0x4d, 0xbf, 0x19, 0x00, 0x23, 0x6d, 0x17, 0x61, 0xa2, 0x72, 0x58, 0x95, + 0x23, 0x23, 0x57, 0x59, 0x81, 0x8f, 0x32, 0x3e, 0x6f, 0xa0, 0x05, 0x2e, 0xac, 0x2e, 0x39, 0x92, 0x15, 0xa2, 0xa3, + 0xb8, 0xf1, 0x7e, 0x42, 0x0c, 0x1f, 0xc5, 0x4c, 0x74, 0xd2, 0x8c, 0x63, 0xde, 0xfd, 0x39, 0x08, 0xad, 0x39, 0xa2, + 0xc1, 0xc2, 0x5b, 0x1a, 0x72, 0x98, 0x25, 0xaf, 0xac, 0x48, 0x25, 0xc3, 0x6f, 0x05, 0x2a, 0xd3, 0x29, 0x44, 0x6b, + 0x5c, 0x02, 0xa7, 0xed, 0x27, 0xf3, 0x2e, 0x78, 0x66, 0x0a, 0xe7, 0xc2, 0xf1, 0x62, 0xc6, 0x9a, 0x12, 0x43, 0xb1, + 0x1c, 0x95, 0x0e, 0x79, 0xaa, 0x50, 0x77, 0xab, 0x88, 0x5a, 0x5b, 0xf7, 0x93, 0x7e, 0x52, 0x10, 0x4f, 0x5b, 0x82, + 0x8c, 0x9a, 0x1c, 0xef, 0x7a, 0x34, 0x7a, 0x62, 0x51, 0x6a, 0xa4, 0xb8, 0xf9, 0xee, 0x13, 0x96, 0x31, 0x02, 0xcf, + 0x55, 0x4a, 0x8e, 0x0d, 0x55, 0x99, 0xfd, 0x81, 0xfa, 0x66, 0x82, 0x83, 0xbd, 0x84, 0x22, 0xb5, 0x55, 0x72, 0x82, + 0xe9, 0x83, 0x2e, 0xe5, 0x78, 0x14, 0xf6, 0x8d, 0xda, 0xfc, 0x25, 0x82, 0x0b, 0x2c, 0xb9, 0xcb, 0xa7, 0x33, 0xb5, + 0x45, 0x79, 0x26, 0xb7, 0x88, 0xb8, 0x58, 0x87, 0xda, 0xa3, 0x86, 0x0c, 0xe2, 0x4d, 0xd7, 0x56, 0x0c, 0xc3, 0x27, + 0x29, 0x45, 0x38, 0xef, 0x8a, 0xc1, 0x7d, 0xdb, 0x35, 0xe6, 0x12, 0x8a, 0xc9, 0xdf, 0xdb, 0xfd, 0x2c, 0x2d, 0x15, + 0x5f, 0xb5, 0xdd, 0x1c, 0xe5, 0xf9, 0x23, 0x81, 0xee, 0x71, 0x2c, 0xb7, 0x37, 0x69, 0xe2, 0xb3, 0x3c, 0x7d, 0x9b, + 0x8d, 0xc1, 0x42, 0xfe, 0x7f, 0xb3, 0x14, 0x2f, 0xb0, 0x7a, 0x60, 0x52, 0x90, 0x3b, 0x1a, 0x53, 0xb9, 0x76, 0x6c, + 0x6c, 0x2b, 0xdf, 0x5d, 0x8c, 0x75, 0x32, 0xb5, 0xf2, 0x6d, 0xec, 0xd8, 0xf0, 0xab, 0x68, 0xbe, 0xbb, 0xd8, 0xac, + 0x2b, 0x5e, 0xdb, 0xea, 0x17, 0xdc, 0xf1, 0x9f, 0xc3, 0x71, 0xeb, 0x3c, 0x6f, 0x1e, 0x47, 0x1f, 0xf7, 0x6c, 0xdf, + 0xa5, 0x45, 0x88, 0xf5, 0x97, 0x8c, 0x3d, 0x52, 0xe7, 0xc7, 0xc4, 0xdb, 0xf1, 0xb5, 0xdf, 0xae, 0xe3, 0x88, 0x3a, + 0x55, 0xfe, 0x87, 0x85, 0xe9, 0x53, 0xb3, 0x1e, 0xed, 0xf9, 0x34, 0x4d, 0xdf, 0x09, 0xd2, 0x6d, 0x9a, 0xa6, 0xbf, + 0x15, 0x1d, 0x6d, 0xbc, 0x58, 0xd7, 0x80, 0xd9, 0x3b, 0xa0, 0x6e, 0xf6, 0x41, 0xac, 0xe4, 0x58, 0x62, 0x31, 0xac, + 0xf5, 0x38, 0x6c, 0x44, 0xde, 0x34, 0xfa, 0xe0, 0x62, 0x61, 0x62, 0x07, 0x8c, 0xfc, 0x18, 0x16, 0x86, 0x0e, 0x49, + 0x55, 0xdb, 0x35, 0x7e, 0x38, 0xa9, 0x8f, 0xb0, 0x30, 0x56, 0x13, 0xd9, 0xff, 0x2c, 0xc8, 0x7b, 0x50, 0x60, 0x8b, + 0xeb, 0x4e, 0xe3, 0x52, 0x3a, 0xf0, 0xe5, 0x2b, 0x41, 0x33, 0x39, 0xa0, 0x49, 0x6f, 0x31, 0xb6, 0x73, 0x9e, 0x44, + 0x2f, 0x0e, 0x29, 0x4d, 0xa1, 0x88, 0xae, 0xaa, 0xa4, 0xa9, 0x2d, 0xfb, 0x38, 0x1a, 0xac, 0x7d, 0xe9, 0x70, 0xf4, + 0x58, 0x01, 0xc3, 0xca, 0x7f, 0xa7, 0x29, 0x07, 0xea, 0x2e, 0xd8, 0x7c, 0xf4, 0x15, 0x0e, 0x13, 0x7c, 0x1d, 0x34, + 0x59, 0x59, 0xa2, 0x9b, 0xda, 0x50, 0x78, 0x4c, 0xfb, 0x6d, 0x0c, 0x38, 0x54, 0xe1, 0x25, 0x37, 0x61, 0xd5, 0x2d, + 0xc7, 0xfd, 0xad, 0x4c, 0x78, 0xb9, 0x1d, 0x26, 0x5b, 0xc3, 0xd6, 0x40, 0x3c, 0x63, 0x18, 0x0c, 0xa2, 0xa1, 0xc5, + 0x25, 0x89, 0x57, 0x30, 0x6b, 0x64, 0xcf, 0x45, 0xa3, 0x64, 0x58, 0x63, 0xdc, 0x98, 0x50, 0xf1, 0x7a, 0x21, 0x86, + 0xf3, 0x69, 0x9a, 0xa6, 0x28, 0x1f, 0x58, 0x70, 0x83, 0x05, 0xad, 0x0a, 0x87, 0x03, 0x9a, 0x6d, 0x8b, 0x46, 0x8b, + 0xd2, 0xa4, 0x4d, 0x25, 0x9d, 0xc4, 0x57, 0xfa, 0xb9, 0x8c, 0x75, 0xb6, 0xaa, 0x26, 0x8c, 0x38, 0xda, 0x0f, 0x8d, + 0x52, 0x75, 0x10, 0xa1, 0x3a, 0x00, 0xce, 0x26, 0x18, 0xf0, 0xd0, 0x46, 0xf7, 0x03, 0x54, 0x17, 0x32, 0xb4, 0x6b, + 0x58, 0xe4, 0xba, 0x99, 0x38, 0xe2, 0x95, 0x7e, 0xa6, 0x6f, 0xe7, 0x68, 0x68, 0x23, 0x49, 0x9b, 0x20, 0x46, 0x1c, + 0xcd, 0x98, 0xfe, 0x60, 0xd3, 0x46, 0xfb, 0xd8, 0xc4, 0x83, 0x1d, 0xf4, 0x72, 0x5c, 0x90, 0x46, 0x9f, 0x55, 0x72, + 0x50, 0xb8, 0x0c, 0xac, 0x79, 0x25, 0xe5, 0xde, 0x2f, 0xf6, 0x65, 0xac, 0xf1, 0xad, 0x5a, 0x99, 0xad, 0x9e, 0x31, + 0x12, 0x23, 0x7b, 0x21, 0x0c, 0x7e, 0x25, 0x7b, 0x3d, 0x6f, 0x79, 0x4d, 0x71, 0xdf, 0xcf, 0x21, 0x3b, 0x26, 0x0c, + 0x18, 0xe8, 0xa2, 0x4c, 0x4e, 0xbb, 0xfa, 0xe8, 0xd5, 0xe7, 0x77, 0xc3, 0xe5, 0x05, 0xe9, 0xf2, 0xc9, 0x5e, 0x47, + 0xae, 0xfb, 0xe1, 0xcf, 0xbc, 0x22, 0xd8, 0x8f, 0x95, 0xff, 0x1c, 0xa2, 0x88, 0x00, 0x60, 0x05, 0x89, 0x8d, 0x66, + 0x73, 0xb0, 0xaf, 0x8b, 0x8e, 0x76, 0x9d, 0xc8, 0x14, 0x95, 0xe1, 0x25, 0x7b, 0x11, 0x61, 0x17, 0xd1, 0x70, 0xb0, + 0x21, 0x6c, 0x62, 0x8b, 0x69, 0xe8, 0x62, 0xf9, 0x66, 0x7e, 0x5a, 0xe3, 0x76, 0xcc, 0xad, 0x43, 0xa1, 0x93, 0xd4, + 0xe8, 0x36, 0x03, 0x9f, 0xe3, 0x4f, 0xe1, 0x84, 0x63, 0x57, 0x69, 0x83, 0x0a, 0xcb, 0xb1, 0x59, 0xcd, 0xa3, 0x28, + 0x78, 0x3e, 0x5b, 0xe7, 0x50, 0xcc, 0x6d, 0x59, 0x2d, 0x58, 0x91, 0x23, 0xde, 0x71, 0xbd, 0x6e, 0xdb, 0x66, 0x17, + 0x9a, 0x1c, 0x51, 0x45, 0x0e, 0xcc, 0xb2, 0xa5, 0x02, 0x6a, 0xb1, 0xf0, 0xa4, 0x1d, 0x06, 0x13, 0xca, 0x22, 0x9e, + 0x5e, 0x74, 0x99, 0x2f, 0x4a, 0x93, 0xb2, 0x30, 0x17, 0x5b, 0x61, 0x0e, 0x6c, 0xed, 0x23, 0x6b, 0x18, 0x2c, 0x25, + 0x20, 0x8d, 0x7d, 0x58, 0xde, 0xa2, 0x4d, 0xb9, 0x63, 0xb4, 0xff, 0x99, 0xa5, 0xf6, 0xb1, 0x4b, 0x9b, 0x94, 0xbe, + 0xea, 0x0f, 0x2b, 0x13, 0x3e, 0x74, 0xfd, 0xaa, 0xdf, 0x6c, 0x8e, 0x4d, 0x50, 0x3f, 0x84, 0x2f, 0x49, 0x26, 0x35, + 0x38, 0x58, 0x18, 0xe8, 0xad, 0x0a, 0x1b, 0x83, 0x35, 0x01, 0x8f, 0xd2, 0x25, 0xd2, 0x44, 0x5c, 0x1b, 0x15, 0x55, + 0xf9, 0x22, 0x6b, 0xaf, 0xf4, 0x92, 0x7d, 0x40, 0xf0, 0xc6, 0x77, 0xb7, 0xd5, 0x68, 0xf8, 0x8e, 0x35, 0x89, 0x72, + 0x10, 0x1f, 0x56, 0xc3, 0x93, 0x66, 0x70, 0xf9, 0x8b, 0x76, 0xe2, 0x53, 0xb2, 0x5b, 0x5c, 0xa0, 0x81, 0xb3, 0xe0, + 0xe8, 0x9f, 0x11, 0xac, 0xaa, 0xab, 0xc8, 0x6a, 0xb3, 0x21, 0x41, 0x34, 0x0d, 0x96, 0x31, 0xb3, 0x36, 0x47, 0xd5, + 0x26, 0xb1, 0xc6, 0x38, 0x1a, 0xaf, 0xff, 0xce, 0x26, 0xf0, 0xf2, 0xac, 0x41, 0x7b, 0xe2, 0xba, 0xed, 0x52, 0x8b, + 0xc7, 0xe3, 0x3f, 0x1f, 0x79, 0x4c, 0xe0, 0xa0, 0xc5, 0x50, 0xcc, 0x0e, 0xc7, 0x7a, 0xd5, 0xe9, 0x55, 0x7c, 0x15, + 0x7a, 0x68, 0x7d, 0xbd, 0x99, 0xa0, 0xc8, 0xd1, 0x16, 0x66, 0xd9, 0xcc, 0x8d, 0x64, 0x6b, 0x8e, 0xbe, 0x59, 0x5b, + 0x24, 0x7b, 0x07, 0x0d, 0x96, 0x33, 0xf1, 0xc5, 0xa7, 0xd8, 0xbc, 0xd3, 0xd6, 0x31, 0x79, 0xc2, 0xb0, 0x23, 0xf8, + 0xa2, 0xa3, 0x2d, 0xc6, 0xe0, 0x7a, 0x8b, 0x75, 0xec, 0x61, 0x82, 0x94, 0x60, 0xb6, 0x00, 0x17, 0x1d, 0x3b, 0x9f, + 0x0c, 0x5f, 0xc5, 0xde, 0x19, 0xb0, 0x0d, 0xb4, 0x90, 0x3d, 0xf9, 0x45, 0x29, 0x4d, 0xe3, 0xe5, 0x53, 0x8b, 0xe0, + 0xc7, 0x0d, 0x75, 0x4e, 0xe5, 0xff, 0x8c, 0x46, 0x29, 0xe5, 0x49, 0x3a, 0xa1, 0x86, 0xc7, 0x56, 0xc0, 0x00, 0xb5, + 0xec, 0x59, 0xa5, 0xcf, 0x3e, 0xa4, 0x09, 0x5b, 0x88, 0x2d, 0xa9, 0xba, 0x79, 0x82, 0xf8, 0x3b, 0xbc, 0xe2, 0x22, + 0x06, 0x18, 0x39, 0xac, 0x1b, 0xfd, 0xe3, 0x16, 0xe9, 0x3c, 0x9e, 0xae, 0x0f, 0xe6, 0x84, 0xa3, 0xe1, 0xd7, 0x23, + 0x55, 0x26, 0x36, 0x1f, 0xaf, 0xdb, 0xd7, 0xa4, 0xb0, 0x83, 0x97, 0x4a, 0xb6, 0x7f, 0x1d, 0xbd, 0xf5, 0x66, 0x2b, + 0x23, 0x56, 0x24, 0xaf, 0x9c, 0xa2, 0x0a, 0xfa, 0xd5, 0xa7, 0xac, 0x92, 0x41, 0x0d, 0x18, 0xd6, 0x90, 0x51, 0x8d, + 0x18, 0xd7, 0x98, 0xcf, 0x04, 0x95, 0xcf, 0x0d, 0x3d, 0x5f, 0x18, 0x06, 0x2e, 0x0c, 0x23, 0x97, 0x82, 0x89, 0x57, + 0x86, 0x46, 0x85, 0x51, 0x4d, 0xab, 0x59, 0x35, 0xaf, 0xea, 0x4a, 0xd1, 0x1f, 0xf4, 0x6f, 0x81, 0xf1, 0x2f, 0x08, + 0x30, 0x1f, 0x62, 0xb2, 0xbc, 0x96, 0xed, 0x9b, 0xe7, 0x2f, 0x16, 0xcb, 0x2b, 0x18, 0x39, 0x42, 0x49, 0xeb, 0xb3, + 0x5f, 0xd4, 0x19, 0xda, 0xce, 0x01, 0xf4, 0x2d, 0x5d, 0xca, 0x78, 0x52, 0xec, 0xf7, 0x3f, 0xdf, 0xbb, 0xfd, 0x13, + 0xcf, 0x43, 0x5c, 0x36, 0xbe, 0x2c, 0x89, 0x7b, 0xec, 0x83, 0xdd, 0x06, 0x2d, 0x5e, 0x5c, 0x98, 0x51, 0x59, 0x5e, + 0xf4, 0xcc, 0xbb, 0x79, 0xcc, 0x82, 0xa1, 0x4e, 0xed, 0xa1, 0xe6, 0x5a, 0xf1, 0xf6, 0x07, 0x1d, 0xd6, 0x53, 0x71, + 0x6a, 0x25, 0xfb, 0xe6, 0x04, 0x56, 0xa2, 0x69, 0x26, 0xfe, 0x8c, 0xaa, 0x7f, 0xb0, 0xb2, 0x6b, 0x1d, 0xb2, 0x71, + 0xb3, 0xd3, 0xdb, 0x1f, 0xf5, 0x02, 0xf7, 0x71, 0x8d, 0x2b, 0x4b, 0xe0, 0x2c, 0x5f, 0x48, 0x67, 0x45, 0x53, 0x09, + 0x05, 0x68, 0x67, 0x7c, 0xc0, 0x4a, 0x46, 0xd0, 0x9f, 0x0d, 0xfd, 0x78, 0xed, 0x2e, 0xec, 0x14, 0xf9, 0xed, 0xdd, + 0xd3, 0x9d, 0xff, 0x09, 0x27, 0x94, 0x09, 0x8b, 0x44, 0xc5, 0x9f, 0x49, 0x17, 0x49, 0x2f, 0x50, 0xc5, 0xcd, 0xc4, + 0x99, 0x30, 0xd9, 0x8b, 0xb0, 0xd8, 0xed, 0x63, 0x53, 0x02, 0x2e, 0x50, 0x7f, 0xcc, 0x4f, 0x59, 0x3d, 0x8d, 0xa7, + 0xdf, 0xbd, 0x6c, 0x2a, 0x7a, 0xa3, 0xa7, 0xc5, 0x27, 0xff, 0xaa, 0xff, 0x96, 0x7a, 0x3b, 0xcb, 0xcd, 0x7c, 0xbf, + 0x26, 0x85, 0x3f, 0x98, 0x5c, 0xf5, 0x8e, 0x2f, 0x67, 0x9d, 0x87, 0x8d, 0xf3, 0x59, 0xe5, 0x6d, 0xea, 0xe6, 0x52, + 0xaa, 0xd4, 0xc6, 0x06, 0x9b, 0xdc, 0xfc, 0x53, 0xd5, 0x1e, 0x6d, 0x55, 0xb2, 0xfd, 0xf5, 0x38, 0xee, 0xc7, 0x77, + 0xfa, 0x0b, 0xfc, 0x92, 0x5e, 0x9a, 0xd9, 0x74, 0x7e, 0xfc, 0x73, 0x2b, 0xdd, 0x64, 0xf5, 0xcf, 0xe3, 0x52, 0xb7, + 0x54, 0x9b, 0xd4, 0x34, 0x8f, 0xba, 0x66, 0xc4, 0x03, 0xb4, 0xa6, 0xb7, 0x77, 0x3f, 0x65, 0xf5, 0x37, 0xea, 0xa4, + 0xda, 0xc3, 0xfd, 0x5f, 0x93, 0x37, 0x5b, 0x73, 0x31, 0x22, 0x85, 0xb1, 0x78, 0x3b, 0xa0, 0x7a, 0xbf, 0x7b, 0x0e, + 0xe9, 0xdc, 0xf8, 0x4f, 0x4f, 0x08, 0x12, 0xb3, 0x20, 0xf9, 0x7a, 0x7f, 0x43, 0xf1, 0xe0, 0x03, 0x4a, 0x7d, 0x0c, + 0xad, 0x0f, 0xfc, 0x6f, 0x9e, 0xc3, 0x1b, 0x8c, 0x5d, 0xa6, 0x03, 0xb7, 0xdc, 0x5c, 0xe8, 0xe7, 0x2f, 0xc4, 0x59, + 0x10, 0xee, 0xe1, 0x8b, 0xa9, 0x1d, 0x8c, 0x41, 0x39, 0x71, 0x04, 0x0e, 0xbe, 0x1d, 0x08, 0x93, 0x40, 0x7c, 0xbd, + 0xbf, 0xad, 0x78, 0xc8, 0x85, 0xdd, 0xcb, 0xfb, 0xd5, 0x9c, 0x4f, 0xdc, 0x71, 0x69, 0x57, 0x9f, 0x8e, 0x4f, 0xb2, + 0xdb, 0x3d, 0x0b, 0xaa, 0xdb, 0x39, 0xb7, 0x5b, 0x3e, 0x41, 0xd9, 0x2f, 0x3a, 0x52, 0xc3, 0x66, 0x35, 0xb4, 0x8c, + 0x7a, 0xd3, 0xfb, 0xf4, 0xb4, 0x70, 0xad, 0xe1, 0x2e, 0x80, 0x7f, 0x66, 0x40, 0xf6, 0x26, 0xc4, 0xde, 0x04, 0x84, + 0x6c, 0x33, 0xe3, 0x76, 0x33, 0x3e, 0x4e, 0x5e, 0xb3, 0x94, 0xb5, 0x77, 0x4e, 0x83, 0xf3, 0xb8, 0xde, 0x79, 0x5d, + 0xf9, 0x58, 0x94, 0x5c, 0xdd, 0xf1, 0x3a, 0x7d, 0xda, 0x43, 0xbe, 0x6f, 0x79, 0x2f, 0x49, 0x34, 0x38, 0x06, 0xf6, + 0xa2, 0x23, 0xa6, 0xb7, 0x2b, 0x43, 0x64, 0xda, 0x87, 0x31, 0xd4, 0x3d, 0xa9, 0xba, 0x14, 0x56, 0x5f, 0xf6, 0x4d, + 0x8d, 0x79, 0x2d, 0x8b, 0xad, 0x83, 0xae, 0xa6, 0x7b, 0x32, 0x63, 0x77, 0xcc, 0xb8, 0x8a, 0x99, 0xc1, 0x4e, 0x2f, + 0x9d, 0x11, 0x07, 0x2d, 0x1c, 0xfa, 0x23, 0x8b, 0xf7, 0xc9, 0xa8, 0x3b, 0x03, 0x43, 0xb5, 0x98, 0xbe, 0xcd, 0x56, + 0x0e, 0x98, 0x2d, 0x11, 0xe4, 0x35, 0x34, 0xbf, 0xd7, 0x14, 0x06, 0x3b, 0x85, 0x69, 0x63, 0xbd, 0xbd, 0x4b, 0xe5, + 0x52, 0x18, 0x88, 0x7a, 0xcf, 0x7d, 0x11, 0xd6, 0x3e, 0x28, 0x6e, 0xb0, 0x65, 0x82, 0xfd, 0xa2, 0xc4, 0xfe, 0x6d, + 0x3d, 0xcf, 0x0d, 0xec, 0xe2, 0x75, 0x61, 0x73, 0xd1, 0x52, 0x99, 0x22, 0x56, 0xa5, 0xe8, 0xb3, 0xfd, 0xbd, 0x72, + 0x36, 0x2a, 0x39, 0x5d, 0x4f, 0xe0, 0x2c, 0xa8, 0xba, 0xeb, 0xaa, 0x5d, 0xd1, 0x5c, 0xb6, 0x40, 0xef, 0x16, 0x38, + 0xbd, 0x3c, 0x49, 0xcb, 0xb3, 0x4d, 0x91, 0xc4, 0x52, 0x7a, 0xff, 0x89, 0xaf, 0x12, 0xf5, 0xe3, 0xec, 0xf1, 0xec, + 0x1b, 0xe1, 0x74, 0x83, 0xd3, 0xbc, 0x2c, 0x7f, 0xa6, 0x39, 0x7f, 0x57, 0xd1, 0x67, 0x96, 0xf5, 0xfc, 0xf6, 0xd1, + 0x23, 0x10, 0x4b, 0x13, 0xd8, 0x6b, 0x4b, 0xfd, 0x67, 0x6c, 0xfb, 0x10, 0xd3, 0x46, 0xd8, 0x68, 0x3c, 0xda, 0x04, + 0x9c, 0xb7, 0xf7, 0x6e, 0xe4, 0xdd, 0x75, 0x4b, 0x02, 0xae, 0xf1, 0x9e, 0xaf, 0xf9, 0xfe, 0x5e, 0xdb, 0x8a, 0x69, + 0xca, 0xb6, 0x62, 0x3e, 0xfb, 0xea, 0x5a, 0xc4, 0xdc, 0xb8, 0xdc, 0xc0, 0x5e, 0x55, 0x6b, 0xb5, 0x20, 0xd9, 0xde, + 0x87, 0x79, 0xfe, 0xd0, 0xcd, 0xec, 0xf0, 0x0c, 0x1e, 0xb5, 0x81, 0xc4, 0xcf, 0xfd, 0xf4, 0xeb, 0xd1, 0x54, 0xd6, + 0x17, 0x40, 0x98, 0x98, 0x11, 0x89, 0x4f, 0x1c, 0xdf, 0x17, 0x9e, 0x6f, 0xab, 0xf6, 0xdb, 0xe1, 0x3c, 0x6b, 0x8e, + 0x6c, 0x93, 0xee, 0x3e, 0x72, 0xb3, 0xf2, 0x03, 0x7a, 0xd5, 0x34, 0x45, 0x5c, 0xab, 0xfe, 0x89, 0x05, 0xd4, 0x52, + 0xe0, 0x40, 0x9e, 0xba, 0x9a, 0x28, 0x04, 0x3e, 0xe1, 0xf5, 0xf9, 0x4e, 0x01, 0xe8, 0xee, 0x45, 0xd0, 0x8c, 0x04, + 0xaf, 0x05, 0x15, 0x57, 0x75, 0x15, 0xcc, 0x56, 0xae, 0x12, 0x8c, 0xf5, 0x07, 0x0a, 0x9a, 0x27, 0xa5, 0x4c, 0x2a, + 0xa0, 0x07, 0xe4, 0x27, 0x1f, 0x55, 0xf1, 0x01, 0xcf, 0x35, 0x89, 0x5e, 0xaf, 0xe2, 0x9a, 0x38, 0x31, 0xa8, 0xc1, + 0xfd, 0x93, 0xaa, 0xf5, 0xa7, 0x62, 0x63, 0xc0, 0xc6, 0x1f, 0xa8, 0xcb, 0xed, 0xe1, 0x34, 0x2b, 0x49, 0x3a, 0x87, + 0x80, 0x1b, 0xd6, 0xf4, 0x18, 0xd5, 0x75, 0x1c, 0x60, 0xfa, 0xa3, 0xf4, 0x3d, 0xa2, 0xc3, 0x4d, 0x34, 0xdf, 0x0e, + 0xe4, 0x26, 0xdf, 0x0c, 0xbe, 0xd1, 0xc6, 0x7f, 0x1c, 0x7c, 0x35, 0xe8, 0xab, 0xe1, 0x8b, 0xc1, 0xe3, 0x6b, 0x6f, + 0xf8, 0x6c, 0xb0, 0xdd, 0x0d, 0x9f, 0x0c, 0xfe, 0x43, 0x7d, 0xaf, 0xfe, 0x68, 0x10, 0x5e, 0x55, 0xfc, 0x7a, 0xa7, + 0x2b, 0x0e, 0x7a, 0x1f, 0x4e, 0x75, 0xaf, 0xca, 0x7b, 0x6f, 0xf7, 0xee, 0x9d, 0xea, 0x67, 0xef, 0x3e, 0xdc, 0x3f, + 0xb5, 0x35, 0xef, 0x01, 0x50, 0xff, 0x54, 0x30, 0xef, 0xdd, 0xa9, 0x66, 0xf5, 0xde, 0x5e, 0x1d, 0xdf, 0xfd, 0xff, + 0xef, 0xbb, 0x86, 0xf7, 0x1e, 0x9e, 0x7a, 0x5a, 0x56, 0xde, 0x54, 0x7a, 0x9b, 0xf7, 0xfa, 0x5f, 0xcb, 0xea, 0x65, + 0x78, 0x65, 0xd0, 0x56, 0xc3, 0x4b, 0x83, 0xba, 0x1a, 0x5e, 0x18, 0x1c, 0x6a, 0xdb, 0x76, 0x86, 0x47, 0x06, 0x6e, + 0x35, 0x3c, 0x37, 0x58, 0x3f, 0x0d, 0x8f, 0x0d, 0xaa, 0xfd, 0xf7, 0x60, 0x78, 0x62, 0xe0, 0x66, 0xc3, 0xd3, 0xc2, + 0xbc, 0xad, 0xf7, 0xec, 0x9f, 0x89, 0x97, 0xa7, 0x31, 0x76, 0xe8, 0xb0, 0xcf, 0xb5, 0xfb, 0x2d, 0xc4, 0xbc, 0x5d, + 0x8e, 0x5d, 0x75, 0x6a, 0x03, 0x36, 0xea, 0x7f, 0x33, 0x2d, 0x37, 0x9c, 0xf0, 0x1b, 0x81, 0x04, 0x96, 0x67, 0xe7, + 0x0a, 0x30, 0xb5, 0x1f, 0x7a, 0x3c, 0x67, 0x60, 0x6a, 0x25, 0x2b, 0x46, 0xae, 0x62, 0xde, 0x9e, 0xfa, 0x3f, 0xf7, + 0x6d, 0x16, 0x6b, 0x94, 0x20, 0x3d, 0xe4, 0x0f, 0xf1, 0xe3, 0x23, 0x37, 0x84, 0x0e, 0xa3, 0x9f, 0x36, 0x29, 0xef, + 0x02, 0xfc, 0xad, 0x25, 0x39, 0xd0, 0xd7, 0x6e, 0x9f, 0x08, 0xdf, 0x82, 0xb3, 0x88, 0x33, 0x2e, 0x24, 0x22, 0x43, + 0x5c, 0xbd, 0xfe, 0x97, 0xab, 0xee, 0x28, 0x36, 0x9e, 0x69, 0x51, 0xfa, 0xc4, 0xfb, 0x62, 0x9b, 0xe4, 0x98, 0x69, + 0x6e, 0xc4, 0x3c, 0x8d, 0xeb, 0xe2, 0x1c, 0x36, 0x43, 0xa2, 0x72, 0x7b, 0x12, 0x5e, 0x22, 0x85, 0x8f, 0xe4, 0xea, + 0x05, 0xf6, 0x9e, 0x60, 0x8a, 0xfd, 0x1c, 0x98, 0xe5, 0x0c, 0xaa, 0x9c, 0xec, 0x40, 0x38, 0x62, 0x52, 0x8d, 0xbf, + 0x52, 0x1e, 0xdf, 0x8e, 0xaa, 0x3c, 0x0e, 0x80, 0xa8, 0xfd, 0x06, 0xde, 0x81, 0x50, 0x99, 0x72, 0xc8, 0x62, 0x82, + 0x17, 0xf4, 0x78, 0xd1, 0x00, 0xaf, 0x64, 0xc2, 0x6f, 0x6b, 0xe5, 0x96, 0xe0, 0x6c, 0x33, 0x32, 0x61, 0x02, 0x66, + 0x57, 0xb0, 0x0a, 0xe2, 0x7f, 0xd9, 0x93, 0x5e, 0x01, 0xa4, 0x40, 0x8b, 0x4d, 0xc3, 0xd0, 0xbd, 0xc4, 0x77, 0x6c, + 0x4c, 0xba, 0xc2, 0xb5, 0xf4, 0x1b, 0xd6, 0x26, 0xeb, 0x67, 0x40, 0xb1, 0xfb, 0xdb, 0x42, 0x1d, 0x80, 0xfe, 0x0b, + 0xa9, 0xfb, 0x97, 0x33, 0x5c, 0x74, 0xed, 0x22, 0x8a, 0x52, 0x4b, 0x0c, 0x0c, 0xb7, 0x11, 0x68, 0x3b, 0x0c, 0x1a, + 0xaf, 0xd3, 0x57, 0x22, 0xe1, 0x8b, 0x68, 0xa5, 0x5c, 0x78, 0x47, 0xb0, 0x83, 0x1a, 0x9d, 0xaa, 0x89, 0xe6, 0x8f, + 0xf2, 0x46, 0x5b, 0x58, 0x04, 0x61, 0xcb, 0x54, 0x8f, 0x14, 0x30, 0x9d, 0x07, 0xfd, 0x6f, 0x34, 0x7b, 0x49, 0xb5, + 0x84, 0x89, 0x7b, 0x7a, 0xcb, 0x7e, 0x42, 0x56, 0xfc, 0x53, 0x24, 0x8f, 0x9d, 0xa6, 0x3c, 0xf1, 0xc9, 0x79, 0x80, + 0x97, 0x5f, 0x8e, 0x80, 0xec, 0x9a, 0xa0, 0xc8, 0x87, 0xbc, 0xd0, 0x84, 0x89, 0x33, 0xe3, 0x11, 0xc1, 0x00, 0x93, + 0x05, 0xb8, 0xcd, 0xbe, 0xd5, 0x62, 0x3a, 0xe1, 0x80, 0xc9, 0x70, 0x59, 0xc9, 0x8b, 0x52, 0x9c, 0x8b, 0xda, 0xdc, + 0x6c, 0x8d, 0x67, 0x84, 0x21, 0x79, 0x73, 0x97, 0x76, 0x38, 0x18, 0x46, 0xfd, 0xad, 0x21, 0x57, 0x89, 0xd2, 0xbd, + 0x98, 0xb4, 0x2b, 0xd9, 0x95, 0x3e, 0xe9, 0x6c, 0x66, 0x3c, 0xbe, 0xf9, 0x6d, 0x48, 0x29, 0x50, 0xec, 0x0d, 0xe5, + 0xfa, 0x10, 0xbf, 0xb7, 0xda, 0x20, 0x7a, 0xe1, 0xf9, 0xf3, 0x53, 0xd0, 0x70, 0x16, 0x8c, 0x52, 0xe9, 0x00, 0x6d, + 0x10, 0x47, 0x67, 0x4d, 0x78, 0xd6, 0xc9, 0xed, 0xb3, 0x0b, 0xf1, 0x60, 0x55, 0x21, 0xe1, 0x0c, 0x9d, 0x7b, 0xda, + 0x58, 0xea, 0xcc, 0x30, 0x25, 0x31, 0x00, 0x1c, 0x01, 0x8f, 0xb6, 0xc3, 0x73, 0xea, 0x69, 0x2f, 0x05, 0xd0, 0x9b, + 0x3c, 0xef, 0xa7, 0x8f, 0x4a, 0xa5, 0x07, 0x3a, 0x8f, 0x5a, 0x7d, 0x76, 0x96, 0xd6, 0x97, 0x25, 0x64, 0x10, 0x18, + 0x8d, 0x1a, 0x9a, 0x2f, 0xa2, 0x72, 0xbf, 0x45, 0x0d, 0xd6, 0x52, 0x65, 0x8d, 0xbc, 0x79, 0xef, 0xfd, 0xc1, 0x35, + 0x6c, 0x76, 0x0d, 0x95, 0xbe, 0x1e, 0xd7, 0x1c, 0x94, 0x02, 0x73, 0x12, 0xe2, 0xd8, 0x16, 0xde, 0x9f, 0x8c, 0x70, + 0xbd, 0x7d, 0xa1, 0x66, 0x8b, 0x2d, 0x0e, 0x60, 0xee, 0x4f, 0x38, 0xe7, 0x92, 0xec, 0x78, 0xe7, 0x2c, 0x96, 0x5f, + 0xd7, 0x5a, 0x79, 0x49, 0xfe, 0xd5, 0x38, 0x3b, 0xb6, 0xac, 0x72, 0x56, 0x81, 0x10, 0x88, 0xfc, 0x74, 0x3a, 0x91, + 0x88, 0xd5, 0x16, 0x04, 0x8d, 0x14, 0x26, 0x84, 0x75, 0xf2, 0xee, 0xe8, 0x46, 0xbc, 0x75, 0x6a, 0x41, 0x66, 0xe2, + 0x40, 0x01, 0xa6, 0xe2, 0xd4, 0xda, 0x93, 0x3d, 0x03, 0x12, 0xec, 0xcb, 0x02, 0x96, 0x6a, 0x80, 0x5c, 0x48, 0x67, + 0xd2, 0x17, 0x44, 0xd1, 0x49, 0x63, 0x2e, 0xb9, 0x78, 0x0a, 0xd8, 0xd0, 0x29, 0x40, 0xa8, 0x34, 0x61, 0xd4, 0x73, + 0x7c, 0xb7, 0x26, 0xb5, 0xa3, 0x4e, 0x49, 0x98, 0xb9, 0x47, 0x0c, 0x9e, 0xcc, 0x9b, 0x0a, 0x71, 0xeb, 0xb3, 0x84, + 0x15, 0xeb, 0x7c, 0x08, 0xf8, 0x04, 0xc6, 0xdb, 0x88, 0xbd, 0x51, 0x1b, 0xf2, 0x06, 0xd6, 0x8f, 0x85, 0x11, 0x84, + 0x8d, 0x19, 0x26, 0xc7, 0x76, 0x83, 0x27, 0x81, 0x06, 0x58, 0xd8, 0x99, 0x9e, 0x13, 0x18, 0x78, 0x77, 0xd6, 0xda, + 0xd8, 0xf4, 0x7e, 0xd5, 0x89, 0x4a, 0xb5, 0x91, 0x59, 0xfe, 0x75, 0x01, 0x55, 0x5a, 0x5f, 0x01, 0xa8, 0x0a, 0xb8, + 0x88, 0xfc, 0xf1, 0x97, 0x9f, 0x27, 0xff, 0xda, 0x04, 0x19, 0x8c, 0xd8, 0x7c, 0x09, 0xb9, 0x41, 0x2d, 0xd8, 0xc8, + 0x77, 0x8c, 0xb9, 0x12, 0xab, 0xc2, 0x97, 0x30, 0x3c, 0x3f, 0xb5, 0xc3, 0x55, 0x1e, 0xd4, 0xa4, 0xc5, 0x47, 0x44, + 0x16, 0x26, 0x69, 0x79, 0x62, 0xa0, 0xa1, 0xaf, 0x84, 0xca, 0x2f, 0x2e, 0xae, 0xd1, 0xf8, 0x56, 0xf1, 0x18, 0x2c, + 0x3c, 0xbe, 0xe5, 0xda, 0x36, 0xd3, 0x46, 0xd9, 0x83, 0xa9, 0x91, 0xb9, 0xd2, 0x5b, 0xb5, 0xd1, 0x21, 0xae, 0xef, + 0xa1, 0x4d, 0x6e, 0xc2, 0x5e, 0xfc, 0x31, 0xa3, 0xac, 0xf6, 0x38, 0x5a, 0xbc, 0xc6, 0xc2, 0x15, 0x7e, 0x5d, 0x40, + 0xc1, 0xdb, 0xe9, 0x63, 0x87, 0x7e, 0x5c, 0xfa, 0x3a, 0x1c, 0x41, 0xa6, 0x4a, 0x54, 0x5c, 0x45, 0x50, 0x09, 0x51, + 0x0f, 0xd7, 0x00, 0x21, 0x4f, 0xe3, 0x4e, 0x34, 0x5a, 0xd5, 0xa6, 0xf4, 0x6a, 0xa4, 0x51, 0xe0, 0xec, 0x2e, 0xfa, + 0xb0, 0x12, 0x79, 0x4b, 0x95, 0x44, 0x0c, 0x94, 0x30, 0x45, 0xd6, 0xbf, 0x99, 0x38, 0x2b, 0x5b, 0xa2, 0x2a, 0x01, + 0x4c, 0x9d, 0x68, 0xc3, 0x4f, 0xbc, 0x11, 0x06, 0xaa, 0x48, 0xa6, 0x12, 0x09, 0x3a, 0x53, 0x65, 0x00, 0x25, 0x4d, + 0x40, 0x1d, 0xd3, 0xee, 0xc1, 0xc3, 0x0a, 0xcb, 0x4d, 0x96, 0x6b, 0x4c, 0x61, 0xb9, 0xbf, 0x7f, 0xca, 0xb3, 0x52, + 0x97, 0x71, 0x10, 0xb5, 0xf2, 0x34, 0xcd, 0x76, 0xaa, 0xaa, 0x84, 0x6e, 0xe3, 0x8a, 0xf3, 0x92, 0xb5, 0xc8, 0xfb, + 0x71, 0x36, 0x6d, 0x7c, 0x10, 0x34, 0x2c, 0x7a, 0xb7, 0xbc, 0x4c, 0xae, 0x24, 0xd6, 0x27, 0x98, 0x1d, 0x41, 0x66, + 0xd0, 0x49, 0x55, 0x2f, 0x48, 0x4a, 0x48, 0x50, 0xaa, 0x44, 0xfe, 0x47, 0xa5, 0xa4, 0x4e, 0xe2, 0xbe, 0x87, 0xf5, + 0x57, 0x95, 0xc5, 0x2b, 0x56, 0x68, 0xdc, 0xf7, 0xf5, 0xed, 0x24, 0xbf, 0x86, 0x11, 0x8a, 0x01, 0x10, 0x5f, 0x07, + 0x70, 0x84, 0x57, 0x2e, 0x9f, 0x8c, 0x60, 0x18, 0x85, 0x8a, 0x23, 0xd6, 0xb4, 0xc5, 0x95, 0xb8, 0x3c, 0x73, 0x05, + 0x23, 0x3c, 0xfc, 0xad, 0x8a, 0x1b, 0x88, 0x87, 0xaf, 0xdb, 0x80, 0x3e, 0x3e, 0xce, 0x97, 0xde, 0x0b, 0xfa, 0xd6, + 0x42, 0x93, 0x4c, 0x10, 0x67, 0xf3, 0x37, 0x8f, 0x97, 0xcd, 0x9e, 0x2f, 0xbf, 0x68, 0x1a, 0x25, 0x81, 0xbe, 0xe7, + 0x6a, 0xf2, 0xf8, 0x67, 0x91, 0x25, 0xc1, 0x21, 0x68, 0xf1, 0x66, 0x42, 0xe0, 0x8b, 0x5e, 0xb0, 0x6a, 0x56, 0x03, + 0xd3, 0x49, 0x71, 0x30, 0xba, 0xb6, 0x89, 0x3a, 0xc5, 0xea, 0x58, 0x9d, 0xd9, 0x11, 0x06, 0x95, 0x7a, 0x08, 0xd5, + 0x53, 0x3a, 0xd2, 0x9b, 0xaf, 0xe8, 0x47, 0xe1, 0xa6, 0xc4, 0xd7, 0xec, 0x52, 0x55, 0x0a, 0xab, 0xc0, 0x19, 0x88, + 0xae, 0x16, 0x1c, 0x27, 0x36, 0x74, 0xf5, 0x10, 0x2c, 0x1b, 0xc6, 0x06, 0x27, 0x6a, 0xa9, 0x42, 0xd1, 0x36, 0x1f, + 0xef, 0xf9, 0x5e, 0xe0, 0x43, 0xc2, 0xac, 0xf3, 0xe1, 0x81, 0x90, 0xed, 0x60, 0xdc, 0x65, 0xf4, 0x03, 0xaa, 0x3b, + 0x23, 0xe8, 0x35, 0xa6, 0xc7, 0xd4, 0x95, 0x44, 0x86, 0xf9, 0xf9, 0xa5, 0xc3, 0x5a, 0x67, 0xe0, 0xea, 0xa1, 0xfb, + 0x21, 0x35, 0x06, 0x35, 0xfc, 0xc1, 0xe8, 0x2a, 0x5c, 0xed, 0xee, 0x9b, 0xe9, 0x20, 0xb0, 0x55, 0x13, 0xa6, 0x66, + 0xc0, 0x34, 0x49, 0x91, 0x98, 0xac, 0x67, 0xd9, 0xd6, 0x8d, 0x7a, 0x5c, 0x50, 0x3e, 0xfb, 0x38, 0x69, 0xfb, 0xba, + 0xb2, 0x82, 0x34, 0x73, 0x21, 0x28, 0x63, 0xe8, 0xa8, 0x4f, 0xac, 0xb3, 0x1a, 0x41, 0x8e, 0x14, 0x96, 0xb6, 0x90, + 0x89, 0x62, 0xcd, 0x69, 0x57, 0x69, 0x5a, 0x59, 0xe2, 0x8f, 0xe9, 0x58, 0xe4, 0xc2, 0x26, 0x83, 0x96, 0x43, 0x29, + 0x4d, 0x9a, 0xf6, 0x4f, 0xf9, 0x44, 0xf8, 0xad, 0x44, 0xd6, 0xaf, 0x6f, 0xf0, 0xec, 0xd9, 0xed, 0x68, 0x03, 0x8c, + 0x97, 0xae, 0x91, 0x4e, 0xb1, 0x1e, 0x63, 0xb7, 0x7c, 0x8f, 0x91, 0xf0, 0x3d, 0x34, 0xd5, 0x57, 0xf9, 0x14, 0xe7, + 0x8e, 0xe8, 0x69, 0x63, 0xf9, 0x77, 0xcf, 0x6e, 0x41, 0xf9, 0x9a, 0xef, 0xb1, 0x20, 0x6d, 0xef, 0x73, 0x26, 0x95, + 0x2b, 0x4a, 0x0c, 0x39, 0x2a, 0xa9, 0xe0, 0x41, 0x03, 0x80, 0x59, 0x9d, 0x55, 0x8d, 0x06, 0x60, 0x17, 0xf9, 0x9d, + 0x52, 0x41, 0x86, 0x4b, 0x64, 0x81, 0x1b, 0x60, 0x7d, 0x00, 0x87, 0x32, 0x53, 0x32, 0x3c, 0x98, 0x5f, 0x61, 0x32, + 0x31, 0xd2, 0xef, 0x50, 0x1c, 0x8f, 0x3b, 0xde, 0xba, 0xe7, 0xa7, 0xa4, 0xd9, 0x69, 0x0f, 0x30, 0x37, 0x91, 0x3c, + 0x0b, 0x0b, 0xfb, 0x20, 0x67, 0xbf, 0x33, 0x0f, 0x84, 0xd1, 0x3a, 0x7f, 0xba, 0xd9, 0x4f, 0x4a, 0x24, 0x78, 0x48, + 0xa9, 0xed, 0xcd, 0x88, 0x72, 0x22, 0x73, 0x29, 0xf5, 0x8d, 0x6d, 0xab, 0x06, 0x53, 0xa4, 0x84, 0x41, 0xa7, 0x11, + 0xbd, 0xb6, 0xb1, 0xbb, 0xa3, 0xd1, 0xf9, 0x27, 0xaa, 0x05, 0x03, 0x99, 0xe1, 0x88, 0x03, 0x58, 0x13, 0xe1, 0x64, + 0x66, 0x67, 0x46, 0x16, 0x64, 0xde, 0x66, 0xee, 0xcf, 0xa4, 0xb9, 0x44, 0x74, 0x5b, 0x6d, 0xae, 0xc8, 0x0c, 0xd3, + 0x53, 0xdc, 0xbd, 0xad, 0xe4, 0xe8, 0xae, 0x77, 0x00, 0x5a, 0xe9, 0xc3, 0xf9, 0x5f, 0x8f, 0xe7, 0xc8, 0x68, 0xc0, + 0xeb, 0x39, 0x57, 0x41, 0xf3, 0x17, 0x38, 0x4f, 0x73, 0x6b, 0x6b, 0x62, 0xa4, 0x26, 0x73, 0x5a, 0xe5, 0xf9, 0x5e, + 0x46, 0x3f, 0x57, 0x8d, 0x3e, 0x6a, 0xe9, 0xd4, 0x6b, 0x90, 0x08, 0x95, 0x19, 0xf1, 0xe7, 0x92, 0xb7, 0x17, 0x10, + 0xdd, 0xa5, 0x12, 0xc6, 0xda, 0x09, 0x98, 0xb9, 0x17, 0xeb, 0x7c, 0x9e, 0x5e, 0x7f, 0x32, 0x69, 0x32, 0x5f, 0xee, + 0xde, 0x05, 0xf2, 0x8e, 0x13, 0x0c, 0x9f, 0x7d, 0x86, 0x21, 0xb2, 0xb8, 0xf8, 0xc5, 0xeb, 0xe9, 0xbd, 0x48, 0x40, + 0xef, 0x13, 0x66, 0x79, 0x4b, 0xc5, 0x2d, 0x98, 0x87, 0x5a, 0x1a, 0xcb, 0xcf, 0xe4, 0xf6, 0x8b, 0xde, 0x11, 0xec, + 0xbd, 0x17, 0x37, 0xbe, 0xfa, 0xbf, 0xb1, 0x67, 0x48, 0xec, 0x7f, 0x2e, 0x91, 0x8a, 0xab, 0xca, 0xdc, 0x8f, 0x25, + 0xa9, 0x82, 0xd5, 0x74, 0x9e, 0x22, 0x19, 0xec, 0xdd, 0x54, 0x83, 0x80, 0x4d, 0x91, 0x31, 0xed, 0x79, 0x80, 0xde, + 0xa0, 0xef, 0x2c, 0xc2, 0x46, 0x45, 0x11, 0xd3, 0x4f, 0x6a, 0x56, 0xe6, 0xe8, 0x74, 0x2c, 0x59, 0x39, 0xb0, 0xd3, + 0xef, 0x5e, 0x7c, 0xfb, 0x35, 0x52, 0xe5, 0xbd, 0xed, 0xdb, 0x59, 0x2b, 0x42, 0xd0, 0xf0, 0x21, 0xd3, 0xdb, 0xf3, + 0x3c, 0xcf, 0x55, 0x16, 0xf7, 0xf1, 0x77, 0x89, 0xc3, 0xc4, 0x28, 0x5b, 0xa3, 0x84, 0x27, 0x5a, 0xd0, 0xcb, 0x5f, + 0x34, 0x45, 0x83, 0xaf, 0x52, 0x14, 0x16, 0xe8, 0x55, 0x43, 0x8e, 0x96, 0xe5, 0xbb, 0x92, 0x06, 0xaa, 0x82, 0xeb, + 0x96, 0xc1, 0xc2, 0xdd, 0xa9, 0x90, 0xd6, 0xa9, 0xb9, 0x50, 0xb6, 0x4f, 0x25, 0xf8, 0x0f, 0xa9, 0xdd, 0x98, 0xa5, + 0x0a, 0xa9, 0x80, 0xea, 0x78, 0xc0, 0xdb, 0x1e, 0x03, 0x2d, 0x4f, 0x30, 0x7b, 0xaf, 0x95, 0x14, 0x83, 0x0a, 0x72, + 0x1b, 0x00, 0x5b, 0x6e, 0x08, 0xd7, 0xe0, 0xe9, 0x18, 0x44, 0xc2, 0x9d, 0x2f, 0x8b, 0xfe, 0xb7, 0x37, 0xf5, 0xac, + 0xfa, 0x4b, 0x86, 0x45, 0xf1, 0xde, 0xf4, 0x1f, 0xb5, 0x69, 0x08, 0x82, 0x6f, 0xa3, 0x44, 0xc4, 0x9f, 0xf9, 0x40, + 0xd5, 0x1a, 0x18, 0xeb, 0x3a, 0x0c, 0x1e, 0x48, 0x61, 0xb2, 0x65, 0x5a, 0x36, 0xa5, 0x4e, 0xdd, 0xc2, 0xee, 0x13, + 0x94, 0xb7, 0x41, 0xf5, 0x5e, 0x2a, 0x2b, 0x1f, 0x50, 0x04, 0x64, 0x45, 0x19, 0x94, 0x8a, 0x7b, 0xba, 0x9e, 0x55, + 0x6c, 0xc2, 0x4f, 0x2f, 0x2b, 0x67, 0xac, 0x83, 0x78, 0x29, 0xff, 0xeb, 0x51, 0xf9, 0x3d, 0xda, 0x1a, 0xea, 0x6b, + 0x51, 0x48, 0x98, 0xe3, 0x16, 0xe3, 0x07, 0x3b, 0x43, 0x27, 0x50, 0x4b, 0x29, 0x9f, 0x10, 0x5f, 0x1c, 0xa2, 0xb0, + 0x73, 0xa8, 0x50, 0x9b, 0x49, 0x08, 0x0b, 0xaf, 0x7e, 0x21, 0xbd, 0xec, 0x87, 0xe0, 0x5e, 0x71, 0x44, 0xaa, 0x4c, + 0xee, 0x58, 0xa7, 0xca, 0x6f, 0x10, 0x0b, 0xb3, 0xb7, 0xef, 0xfb, 0x7d, 0x1d, 0xfc, 0x9d, 0xfe, 0xc7, 0x4f, 0xf8, + 0x68, 0x4f, 0xfb, 0xd1, 0xce, 0xe7, 0x65, 0x40, 0xfd, 0xf1, 0xd4, 0xb4, 0x6d, 0x58, 0xd3, 0x6e, 0xb0, 0x48, 0x5f, + 0x93, 0x85, 0x99, 0x78, 0x68, 0xc6, 0xbf, 0x2d, 0xca, 0xfb, 0x94, 0xce, 0x56, 0x35, 0x83, 0xaa, 0x25, 0xff, 0xfa, + 0x57, 0x85, 0x0d, 0xc2, 0x34, 0x60, 0x27, 0x80, 0xd0, 0x17, 0x79, 0x3f, 0x73, 0x3d, 0x44, 0x08, 0xbe, 0x60, 0x00, + 0x77, 0x0e, 0x7d, 0x81, 0x3a, 0x87, 0xa1, 0x6a, 0xbd, 0x9c, 0xeb, 0xc8, 0x46, 0xcd, 0xf1, 0x6a, 0xd7, 0x47, 0x7f, + 0xa0, 0xef, 0xfd, 0x34, 0xf2, 0x67, 0x4b, 0x2d, 0xb8, 0x19, 0x37, 0xeb, 0x16, 0x70, 0x06, 0x67, 0xf1, 0x1c, 0x28, + 0xd3, 0x57, 0x83, 0x17, 0xe7, 0x32, 0x5a, 0x1b, 0x98, 0x82, 0x69, 0xe5, 0x86, 0x8b, 0xa2, 0x74, 0xec, 0xa8, 0x17, + 0xbb, 0xb6, 0x8a, 0x2e, 0xdd, 0x46, 0x8e, 0x72, 0xbe, 0x65, 0xef, 0x50, 0x95, 0xb0, 0xbe, 0x64, 0x13, 0x79, 0x17, + 0xd3, 0xcb, 0xab, 0xf3, 0x8a, 0x66, 0xbc, 0x6a, 0xcb, 0xda, 0x03, 0x11, 0x67, 0x42, 0xbe, 0xe8, 0x9e, 0xa2, 0x51, + 0xe0, 0xd0, 0x54, 0xed, 0xe2, 0xdf, 0x8f, 0xb8, 0xaa, 0x77, 0xbd, 0xf8, 0x37, 0xbb, 0x66, 0x5d, 0xcf, 0xc4, 0x80, + 0x51, 0x4e, 0xbe, 0x60, 0xe5, 0x30, 0xbc, 0xe2, 0x9e, 0xfa, 0xbe, 0x48, 0xcf, 0x33, 0xea, 0x55, 0x34, 0xb7, 0xef, + 0xd4, 0x9f, 0xe3, 0x59, 0xcd, 0xf5, 0x67, 0xdb, 0xb0, 0x87, 0x25, 0xef, 0xcb, 0xed, 0x93, 0x73, 0xd2, 0xaa, 0x53, + 0x4e, 0xa9, 0x5d, 0x78, 0x09, 0x8f, 0x6c, 0x6f, 0x68, 0x50, 0xe6, 0xce, 0xfa, 0xb4, 0x3b, 0xdc, 0x4f, 0x8e, 0x8a, + 0x32, 0x76, 0xc5, 0x61, 0x9f, 0x51, 0xd2, 0xfb, 0x8a, 0x9b, 0xc3, 0x10, 0x83, 0x53, 0x27, 0x50, 0x94, 0xf5, 0x08, + 0x2b, 0xcf, 0x03, 0xfb, 0xed, 0x8a, 0x9f, 0x81, 0x73, 0x98, 0xda, 0x6d, 0x4c, 0xee, 0xfa, 0x94, 0x4a, 0xee, 0xab, + 0x8a, 0xee, 0x23, 0xe3, 0x82, 0xbd, 0xc3, 0xfa, 0x83, 0x83, 0x3e, 0xe2, 0xb2, 0xc5, 0xc7, 0x8f, 0x59, 0x80, 0xbf, + 0xaa, 0xce, 0xfb, 0x86, 0x21, 0x14, 0x60, 0xb2, 0x4a, 0x4d, 0x1b, 0xc5, 0x4b, 0x86, 0xcd, 0xbd, 0x93, 0x8f, 0x4b, + 0xd4, 0x09, 0xee, 0xaf, 0xd1, 0xb2, 0xda, 0x0d, 0xf0, 0x79, 0x12, 0x4b, 0xcc, 0x89, 0xf6, 0xd8, 0x3f, 0xde, 0xac, + 0x66, 0xf2, 0x27, 0x66, 0xe8, 0x33, 0x54, 0x0b, 0xeb, 0x58, 0xfe, 0x20, 0xce, 0x4f, 0x7d, 0x7e, 0xbb, 0x24, 0xf9, + 0x9b, 0xa1, 0xc2, 0xc2, 0xa6, 0xb0, 0x82, 0xb0, 0x95, 0xaf, 0x2f, 0xec, 0x00, 0xea, 0xbd, 0xc9, 0xec, 0xfe, 0x0d, + 0xe3, 0xcb, 0x2e, 0xe1, 0xcb, 0xed, 0x12, 0xc5, 0xb2, 0x8b, 0xc3, 0x45, 0x2e, 0x23, 0x0a, 0x27, 0x1e, 0x8c, 0x80, + 0x17, 0x95, 0x75, 0xe0, 0x87, 0x75, 0xc4, 0xc7, 0xe7, 0x71, 0xb9, 0x20, 0x5a, 0x94, 0xe6, 0xcf, 0x83, 0x96, 0x25, + 0x1d, 0xd7, 0xf4, 0x4d, 0x74, 0x98, 0xd2, 0x04, 0x84, 0xec, 0xb1, 0x29, 0xf4, 0x63, 0x95, 0xa2, 0xba, 0x59, 0x3a, + 0x70, 0xe7, 0xc6, 0x76, 0xd5, 0x48, 0xf9, 0x5d, 0xbf, 0x4e, 0x77, 0xb2, 0x6b, 0xd9, 0x3f, 0x65, 0xc8, 0x7c, 0xd4, + 0x05, 0xf3, 0xc7, 0x99, 0x2a, 0x1d, 0x72, 0xed, 0xf5, 0x69, 0x57, 0x45, 0xd0, 0x14, 0xfb, 0x9f, 0x76, 0xf5, 0x92, + 0xee, 0x8b, 0x1f, 0x15, 0xd0, 0xea, 0xa2, 0x43, 0x8a, 0x1c, 0x18, 0xc3, 0x21, 0x61, 0xb8, 0x11, 0xb1, 0x6d, 0x48, + 0x82, 0xc7, 0xca, 0x29, 0xbc, 0x10, 0xf7, 0xc7, 0x91, 0x8a, 0x51, 0x15, 0xdd, 0xd8, 0xda, 0xd8, 0xc6, 0x66, 0x62, + 0x1e, 0xd7, 0x43, 0xf9, 0xab, 0x28, 0x93, 0x26, 0xb8, 0x1b, 0x0c, 0xea, 0xec, 0x79, 0xa2, 0x14, 0xb4, 0x99, 0xe9, + 0xb1, 0x15, 0x4e, 0x93, 0x5b, 0xee, 0x76, 0x91, 0x44, 0x97, 0x85, 0xa1, 0xd9, 0x1a, 0x4c, 0x1c, 0x23, 0xf5, 0x16, + 0x24, 0xb2, 0x2d, 0x85, 0xcb, 0x2e, 0x7e, 0xa3, 0x28, 0x61, 0xd0, 0xf9, 0x4c, 0x30, 0xde, 0x44, 0xc0, 0x94, 0x23, + 0x4f, 0x13, 0xda, 0x4a, 0x1e, 0x8d, 0x91, 0x57, 0x32, 0x4d, 0x65, 0x7b, 0x2c, 0x7f, 0x24, 0xc9, 0x94, 0x9b, 0xe9, + 0x62, 0xa1, 0x17, 0x13, 0x04, 0xaa, 0xb0, 0xea, 0xad, 0x58, 0x49, 0x04, 0x60, 0xb9, 0x82, 0xb2, 0xec, 0xd2, 0xfd, + 0xbc, 0x02, 0x47, 0x1e, 0xa6, 0x53, 0xc4, 0x86, 0x27, 0x8d, 0x8c, 0xc4, 0x89, 0xaf, 0x2f, 0xc9, 0x96, 0x53, 0x33, + 0x38, 0x8b, 0x78, 0x60, 0xaa, 0xdb, 0xdc, 0x78, 0x79, 0xa4, 0xd8, 0xba, 0x97, 0xde, 0x89, 0xb8, 0x74, 0x9d, 0x95, + 0xa2, 0x1c, 0x55, 0x52, 0xa8, 0xe7, 0x4c, 0xa3, 0xa9, 0xbc, 0xb5, 0x85, 0x12, 0x59, 0x05, 0xad, 0x92, 0xd3, 0xff, + 0xef, 0x88, 0x24, 0x24, 0x5c, 0x08, 0x2c, 0xfe, 0x32, 0x15, 0xd2, 0xec, 0xad, 0xb6, 0x63, 0x18, 0x44, 0xba, 0xce, + 0x0b, 0x6e, 0x19, 0xbf, 0xfa, 0x05, 0x00, 0x7a, 0x2b, 0xda, 0x06, 0xa6, 0x8b, 0x05, 0x9c, 0xd9, 0xd9, 0x8c, 0xde, + 0xe6, 0xc2, 0xac, 0x8e, 0x2b, 0xfa, 0x89, 0xd5, 0xbf, 0x86, 0x85, 0xdd, 0xb3, 0xfd, 0x78, 0xb0, 0x63, 0x46, 0x53, + 0x57, 0x09, 0x61, 0x98, 0x20, 0x8b, 0x5e, 0x06, 0x77, 0xc8, 0x22, 0x8c, 0xc0, 0xae, 0x1c, 0xda, 0xc8, 0x84, 0xf3, + 0x15, 0x84, 0x7f, 0x8e, 0xf9, 0x7a, 0x0a, 0x2c, 0xcb, 0xfd, 0xc9, 0x50, 0x0f, 0x03, 0xc2, 0x44, 0x46, 0x38, 0x82, + 0x24, 0x64, 0x53, 0x21, 0x98, 0x78, 0x0a, 0xea, 0x26, 0x38, 0xb0, 0xc5, 0xd1, 0x8d, 0x8d, 0x52, 0x98, 0x11, 0x7f, + 0xc5, 0x82, 0x91, 0xdb, 0xc7, 0xf8, 0xf6, 0x80, 0xc2, 0x2b, 0xd8, 0x29, 0x84, 0xea, 0xe5, 0xa5, 0x36, 0xbd, 0xd8, + 0x8f, 0x7c, 0x07, 0x7d, 0x3c, 0x9b, 0xe9, 0xc8, 0x0b, 0x32, 0x4c, 0xa7, 0x21, 0x0d, 0x40, 0x42, 0x78, 0xe1, 0xa6, + 0x6e, 0x7f, 0x72, 0x68, 0x9d, 0x4c, 0x15, 0x58, 0xde, 0xe5, 0x4d, 0x27, 0x23, 0x20, 0x2f, 0xec, 0xb2, 0x52, 0xcc, + 0xa7, 0xff, 0x54, 0x8d, 0xed, 0x30, 0x9d, 0x76, 0x38, 0xbb, 0x98, 0xbb, 0x42, 0x63, 0x26, 0x22, 0x2f, 0xca, 0x15, + 0xb6, 0x5e, 0x9c, 0xe6, 0x70, 0x80, 0xf7, 0xb8, 0x7c, 0x43, 0x42, 0xc8, 0x07, 0x2f, 0x48, 0x87, 0xe8, 0x59, 0x9a, + 0x8f, 0x19, 0xf5, 0xc2, 0x5b, 0x5f, 0x64, 0x0a, 0x02, 0xfe, 0x74, 0xeb, 0x23, 0x51, 0x8d, 0xf4, 0x14, 0x2d, 0x4e, + 0xa8, 0x2c, 0xd9, 0x16, 0xc8, 0xe9, 0xbf, 0x20, 0x3a, 0x18, 0x63, 0xf9, 0x36, 0xe1, 0xcd, 0xcb, 0x2d, 0x6b, 0xbc, + 0xfd, 0xc8, 0x76, 0x86, 0x52, 0xfe, 0xc6, 0x71, 0x88, 0xe9, 0x4c, 0x26, 0x76, 0x66, 0x02, 0x46, 0x0f, 0x0b, 0x68, + 0x1d, 0xb8, 0x19, 0x79, 0xfc, 0xe4, 0xd5, 0x9b, 0x90, 0x9b, 0xcf, 0xd5, 0xff, 0xfc, 0xb2, 0x75, 0x16, 0xf7, 0x6e, + 0x2f, 0x25, 0x0e, 0x9d, 0x99, 0xcd, 0x32, 0x18, 0xaf, 0x68, 0x80, 0xe0, 0xe4, 0x1a, 0x30, 0x0c, 0xca, 0xd2, 0x0f, + 0x04, 0x8c, 0x5d, 0x1e, 0xa9, 0xba, 0x19, 0x3f, 0x42, 0xcc, 0x76, 0x59, 0x3e, 0x44, 0x5a, 0x18, 0xed, 0x5b, 0xa0, + 0xb0, 0x03, 0x66, 0x2e, 0x8e, 0x40, 0xde, 0x73, 0x99, 0x79, 0x0d, 0x44, 0xeb, 0xf3, 0xcd, 0x79, 0x7c, 0x9d, 0x94, + 0xff, 0x28, 0x9a, 0x43, 0x5a, 0xd1, 0x8c, 0xfc, 0x3e, 0x1a, 0x3d, 0xd6, 0xdb, 0xbc, 0xd9, 0x8e, 0xab, 0x4c, 0xd9, + 0x12, 0x8c, 0x28, 0xb9, 0xb1, 0xc3, 0x7c, 0x50, 0x71, 0x15, 0xd8, 0x92, 0xaf, 0xd1, 0xad, 0x1d, 0xe2, 0x70, 0xee, + 0x37, 0x2d, 0xf2, 0xb6, 0xe5, 0xe8, 0xa2, 0xb0, 0x5b, 0x81, 0xf3, 0xab, 0x86, 0xb6, 0x12, 0xdf, 0xc8, 0x9f, 0x8c, + 0x89, 0x2a, 0x24, 0x88, 0x09, 0x7a, 0x34, 0x9c, 0x7f, 0x10, 0xa2, 0xa1, 0xcb, 0x64, 0xb7, 0x6c, 0xd2, 0x97, 0xda, + 0xc2, 0x55, 0x60, 0x16, 0xd8, 0x6d, 0xec, 0x77, 0x7d, 0x3c, 0x6f, 0xc7, 0x65, 0x66, 0xcd, 0x87, 0x5a, 0xf1, 0x15, + 0xce, 0x05, 0x41, 0xa5, 0x35, 0xdc, 0x92, 0xfc, 0xdf, 0xcf, 0xfb, 0x67, 0xdc, 0x7a, 0x5a, 0xf6, 0xea, 0x7b, 0xe8, + 0xf7, 0xf5, 0x5e, 0x2d, 0x17, 0xbd, 0x48, 0x2d, 0xfa, 0x6a, 0x34, 0x6d, 0x3c, 0xbf, 0x7f, 0x7d, 0x7d, 0x21, 0x9d, + 0xde, 0xf1, 0x2b, 0xbf, 0x85, 0xee, 0x1d, 0xb8, 0xa2, 0xdc, 0xe0, 0xe7, 0x2a, 0x1e, 0xce, 0xfe, 0x2b, 0x77, 0x58, + 0x1d, 0xd7, 0xaf, 0xaa, 0xcb, 0x36, 0xc7, 0x33, 0xd8, 0x1b, 0xfd, 0xb6, 0x3d, 0x03, 0xfe, 0xbf, 0x05, 0x48, 0x7c, + 0x91, 0x92, 0x49, 0x05, 0x0a, 0x40, 0xa0, 0xbb, 0x1e, 0xfc, 0x11, 0x84, 0x51, 0x4a, 0x3b, 0x7c, 0xfc, 0x98, 0x4c, + 0x54, 0x70, 0x78, 0x75, 0x6e, 0xa1, 0x59, 0x8f, 0xf4, 0xfb, 0x3c, 0xdd, 0xf5, 0xf8, 0x53, 0x1b, 0x55, 0x27, 0x02, + 0x99, 0xd9, 0x38, 0xd3, 0x4e, 0xb9, 0xfe, 0x6d, 0xa3, 0x3f, 0xab, 0xf0, 0xad, 0x42, 0x45, 0x77, 0x5f, 0xfc, 0xe3, + 0xaa, 0xd1, 0xbb, 0xee, 0x2a, 0xfc, 0x70, 0xd5, 0xab, 0xb7, 0xdd, 0xed, 0xbb, 0x15, 0x15, 0x6b, 0x58, 0x9e, 0x31, + 0xc3, 0xa0, 0x39, 0x22, 0x9a, 0x9d, 0xf2, 0xff, 0x7d, 0x64, 0xeb, 0x45, 0xc4, 0x92, 0xad, 0xb8, 0x00, 0x79, 0xb1, + 0x8d, 0xd3, 0x67, 0xf1, 0x46, 0x35, 0x17, 0xae, 0x3c, 0xea, 0xdd, 0x49, 0xba, 0x37, 0x18, 0xaa, 0xf9, 0xfd, 0x80, + 0xd7, 0x05, 0x5d, 0x39, 0xf1, 0xd1, 0xf1, 0x4e, 0xd9, 0xfa, 0x68, 0x6c, 0xff, 0x2b, 0x5f, 0x43, 0xc7, 0xe6, 0xc5, + 0xb6, 0x03, 0xbb, 0xe1, 0xc7, 0x6c, 0xe2, 0xcd, 0xa7, 0xf5, 0xf8, 0x8c, 0xcf, 0xd3, 0xb8, 0xc7, 0x18, 0xde, 0x19, + 0xb7, 0xe6, 0x01, 0x9f, 0x19, 0x65, 0x06, 0x72, 0x19, 0xb2, 0xf7, 0x1e, 0xd6, 0xe8, 0xa9, 0x03, 0xfa, 0x35, 0x15, + 0x0a, 0x80, 0x45, 0xb9, 0x98, 0x21, 0xad, 0x99, 0xd1, 0xbf, 0x81, 0x46, 0x94, 0x8c, 0xf2, 0xf9, 0xdc, 0x59, 0x74, + 0x43, 0xa7, 0x4f, 0x40, 0x06, 0xd6, 0xd6, 0x01, 0x6b, 0x89, 0x45, 0x85, 0x68, 0x13, 0x9a, 0x4c, 0x00, 0xee, 0x93, + 0x60, 0x43, 0xe1, 0xd7, 0x5a, 0x4e, 0x82, 0x9f, 0xbb, 0x57, 0x82, 0xa4, 0x97, 0xe2, 0x28, 0x9d, 0x4c, 0x18, 0xb4, + 0x7b, 0xcd, 0xcb, 0x97, 0xbd, 0xcf, 0xed, 0xfa, 0x90, 0xf9, 0xc8, 0x9e, 0xb5, 0xe6, 0x64, 0xe4, 0x6b, 0xcd, 0x51, + 0x77, 0xf2, 0x06, 0x52, 0x36, 0xfb, 0x85, 0x61, 0x81, 0xc5, 0x6f, 0x35, 0x4c, 0x6e, 0xbd, 0x39, 0xa5, 0xf6, 0x11, + 0x4f, 0x12, 0x38, 0x1b, 0x5e, 0x37, 0xd4, 0x5a, 0x68, 0xaf, 0x57, 0x38, 0xaa, 0xf4, 0xe9, 0x4e, 0x29, 0x37, 0xd7, + 0x63, 0xef, 0xbe, 0xf5, 0xad, 0xf4, 0x84, 0xbc, 0xf3, 0x02, 0x9c, 0x95, 0x3f, 0x5f, 0xfb, 0x8f, 0x05, 0xa4, 0xae, + 0x1a, 0x67, 0x73, 0x5b, 0xf6, 0xc6, 0x77, 0x4b, 0xde, 0xbe, 0x17, 0xd6, 0xb0, 0x6e, 0x5b, 0x27, 0x89, 0xd7, 0x6e, + 0x31, 0x2b, 0x2d, 0xe4, 0x33, 0x72, 0xc9, 0x4c, 0x22, 0xe4, 0x1a, 0xa1, 0xe1, 0x5a, 0xaf, 0xd0, 0x6d, 0xd7, 0x10, + 0xe6, 0x2a, 0x4c, 0x8f, 0x2d, 0x11, 0x1c, 0x54, 0xcd, 0xb7, 0xf5, 0xbf, 0x81, 0x1e, 0xfe, 0xd8, 0xec, 0x95, 0x05, + 0x53, 0x3c, 0xe9, 0xdc, 0xd7, 0xfa, 0xbb, 0x46, 0x3c, 0x4a, 0x4f, 0x1a, 0xa2, 0xe8, 0x11, 0x09, 0xf8, 0x5a, 0xc5, + 0xa0, 0x97, 0x15, 0xf7, 0x50, 0xa1, 0x4f, 0x5b, 0x98, 0xa3, 0xc2, 0x55, 0xaf, 0xc8, 0x93, 0x11, 0xfa, 0x4c, 0xad, + 0x0f, 0x84, 0x5c, 0x14, 0xef, 0x7d, 0xd2, 0x7a, 0xbb, 0x3e, 0x5f, 0xe4, 0x0e, 0xe9, 0xdd, 0xdb, 0x84, 0xe9, 0xa5, + 0x43, 0x37, 0xb6, 0xf1, 0x4f, 0xc4, 0xb3, 0x8d, 0xe1, 0x42, 0x95, 0xa5, 0x78, 0x5a, 0x8e, 0x52, 0xdd, 0xd1, 0x98, + 0x24, 0x15, 0xc8, 0xde, 0xd9, 0x76, 0x58, 0x73, 0xe1, 0xab, 0xec, 0xea, 0xd8, 0x03, 0x95, 0xb8, 0x87, 0xe4, 0x0e, + 0xfb, 0xb6, 0xbf, 0xcc, 0x54, 0xa6, 0x21, 0xfe, 0xc7, 0xf7, 0xdc, 0x81, 0x46, 0x7f, 0x3b, 0x8e, 0xe8, 0x58, 0x72, + 0x8b, 0x65, 0xca, 0x70, 0xe4, 0x04, 0x8b, 0xed, 0xde, 0x70, 0xca, 0xb9, 0xec, 0xb4, 0x45, 0x31, 0x4c, 0x72, 0x0f, + 0x8c, 0x6c, 0x45, 0xfb, 0x27, 0xf6, 0x44, 0xc3, 0x9c, 0x9e, 0x9a, 0x77, 0x96, 0xf8, 0x36, 0xed, 0x9f, 0xa8, 0x5d, + 0x42, 0x15, 0xa5, 0xc8, 0x4a, 0xdc, 0xe5, 0x97, 0x76, 0x9b, 0x08, 0xdb, 0x45, 0x98, 0xd6, 0x5e, 0x4f, 0x52, 0x39, + 0xd2, 0x28, 0x75, 0xec, 0xf0, 0xb6, 0x93, 0xa6, 0x02, 0x22, 0x54, 0x54, 0x4f, 0x4a, 0x5a, 0x4a, 0x5f, 0x88, 0x5a, + 0x77, 0x3e, 0xda, 0x8a, 0xf6, 0x04, 0x1c, 0xc0, 0xa6, 0xd5, 0x16, 0x95, 0xca, 0xc3, 0x0d, 0x3b, 0x04, 0xed, 0x2b, + 0x78, 0xf9, 0x00, 0x47, 0x55, 0x9e, 0xde, 0x17, 0xa4, 0xe2, 0xc7, 0x29, 0x36, 0x1e, 0x66, 0x93, 0xa1, 0x12, 0xb8, + 0x31, 0x4a, 0x87, 0xcf, 0xd7, 0xef, 0x74, 0x98, 0xbc, 0xfa, 0xb8, 0xa7, 0x17, 0xd3, 0x2b, 0x20, 0x5e, 0xb8, 0x79, + 0x7f, 0x1c, 0x26, 0xd7, 0x70, 0x82, 0xf4, 0x49, 0xaa, 0xb7, 0x6d, 0x19, 0x03, 0x0a, 0xcb, 0xbe, 0x9c, 0xc6, 0x5e, + 0x4c, 0x7c, 0x9e, 0xbf, 0x4b, 0x1b, 0xd3, 0xb2, 0xc2, 0x58, 0x7b, 0x75, 0xdb, 0x21, 0x5c, 0xe6, 0x0e, 0x7e, 0xf9, + 0x3f, 0x7c, 0xd4, 0x76, 0x73, 0xbf, 0x6e, 0xce, 0x8c, 0x00, 0xcf, 0x48, 0x88, 0xbe, 0x3c, 0x90, 0x2b, 0xd7, 0xaf, + 0xfe, 0x37, 0x50, 0xfc, 0xa4, 0x2b, 0xcd, 0xbf, 0xe6, 0xfa, 0xb0, 0x18, 0x9b, 0x82, 0x6c, 0x1f, 0x49, 0x61, 0x74, + 0x8d, 0x68, 0xbc, 0xdf, 0xb7, 0x61, 0x5d, 0x0d, 0x32, 0x72, 0x8b, 0x90, 0xd7, 0x87, 0x58, 0x60, 0xf4, 0xfd, 0x65, + 0xdb, 0xe2, 0x9b, 0x56, 0x24, 0xde, 0x30, 0xab, 0xb4, 0xfe, 0x17, 0x59, 0xb8, 0xbe, 0xfb, 0xd2, 0x80, 0x80, 0xd6, + 0xda, 0x57, 0xc2, 0x72, 0xe7, 0x08, 0x02, 0x18, 0x94, 0x30, 0x16, 0x4f, 0x22, 0xfa, 0x97, 0xcc, 0x88, 0xd4, 0x53, + 0xc5, 0x74, 0xe2, 0x84, 0xe1, 0xac, 0x04, 0x35, 0x56, 0x7a, 0x80, 0xcd, 0x5c, 0x94, 0xab, 0x61, 0x2b, 0xc6, 0x43, + 0x8a, 0xb8, 0x63, 0xd6, 0xc8, 0x7b, 0x42, 0x25, 0x0d, 0xaa, 0x88, 0x0a, 0x29, 0x8f, 0x42, 0x1c, 0x86, 0x67, 0x10, + 0x02, 0xa4, 0x52, 0xc4, 0x3a, 0x73, 0x49, 0x86, 0x71, 0xe0, 0xb5, 0x53, 0xc9, 0xab, 0xd1, 0xdd, 0x2a, 0x74, 0x0a, + 0x22, 0x3a, 0x30, 0xbb, 0x05, 0x5d, 0x6f, 0x16, 0xb0, 0x5b, 0x31, 0xb5, 0x52, 0xdc, 0xcd, 0x98, 0xad, 0x58, 0x6c, + 0x61, 0x40, 0x24, 0xb4, 0x65, 0xfe, 0x1a, 0x1d, 0xf0, 0xa2, 0x8b, 0xa2, 0x27, 0xa5, 0xf1, 0xdf, 0x94, 0x7a, 0x5f, + 0x50, 0xc3, 0xc8, 0x82, 0x82, 0xeb, 0x6c, 0xdc, 0x4a, 0xfc, 0xf0, 0x96, 0x3a, 0xdc, 0x42, 0xf0, 0x55, 0x48, 0x37, + 0xd5, 0xc2, 0x5c, 0x61, 0x0f, 0xb2, 0xe5, 0xda, 0x72, 0xa3, 0xe3, 0xbb, 0x5e, 0xbb, 0xf0, 0xfc, 0xa5, 0x36, 0xcf, + 0x95, 0x53, 0x3c, 0x96, 0x82, 0x5c, 0xe2, 0xa9, 0x95, 0x75, 0x27, 0xf5, 0x61, 0x58, 0xd3, 0x51, 0x8d, 0x3b, 0xe3, + 0xc9, 0x13, 0x32, 0xc9, 0x97, 0x56, 0xea, 0x9c, 0x50, 0x47, 0xa0, 0xb6, 0x1e, 0x94, 0xa9, 0x5f, 0x8a, 0x2d, 0x60, + 0x1e, 0x1e, 0xf8, 0x8f, 0x61, 0x91, 0x3c, 0x99, 0x44, 0x4e, 0x13, 0x4f, 0xe5, 0xf8, 0x15, 0x9f, 0x33, 0x9e, 0x0c, + 0x27, 0x7b, 0x2c, 0x49, 0x7a, 0xb6, 0x8c, 0xf9, 0x61, 0x00, 0x88, 0x13, 0x61, 0xcc, 0x45, 0x1e, 0x51, 0x28, 0x5a, + 0x9c, 0x5c, 0x57, 0x40, 0x6a, 0xaa, 0x6d, 0xbf, 0xa6, 0xe8, 0x08, 0xcc, 0xd2, 0x65, 0x1a, 0xd5, 0x2c, 0x55, 0x26, + 0x08, 0xe1, 0x73, 0x6e, 0xad, 0x1d, 0x17, 0x30, 0xd3, 0x8e, 0x9e, 0xdb, 0xe4, 0x75, 0xf6, 0x47, 0x46, 0x66, 0xea, + 0xce, 0xaa, 0xc6, 0x04, 0x63, 0x57, 0xed, 0xd2, 0x50, 0x79, 0xe3, 0x64, 0xf7, 0xd5, 0xa9, 0xdd, 0x86, 0x32, 0xb8, + 0x88, 0x89, 0x87, 0x6c, 0x04, 0x20, 0xba, 0x96, 0xab, 0x95, 0x27, 0xc7, 0xc6, 0x10, 0xe6, 0xa6, 0x38, 0xcf, 0x81, + 0xb6, 0x7f, 0xdc, 0xb5, 0x50, 0x2b, 0x44, 0x56, 0x36, 0xfb, 0x67, 0x13, 0x78, 0xbd, 0x58, 0xbc, 0x08, 0x2f, 0xe6, + 0x41, 0x2a, 0x2f, 0x16, 0xbf, 0xb2, 0x94, 0x86, 0x14, 0x61, 0x2d, 0xb0, 0xb9, 0xb4, 0x92, 0x67, 0xcb, 0xe9, 0x85, + 0xeb, 0x99, 0xcc, 0xbc, 0x10, 0x30, 0x66, 0xe9, 0x57, 0x5e, 0xa2, 0xb3, 0x03, 0xfb, 0x9f, 0xfd, 0x86, 0x3a, 0x22, + 0x53, 0xb0, 0xe9, 0x36, 0x46, 0x6a, 0x91, 0xac, 0x24, 0xea, 0x47, 0x56, 0x3e, 0x7b, 0xd7, 0xea, 0xb7, 0xda, 0xb9, + 0x21, 0x50, 0xf8, 0xde, 0x88, 0x09, 0x0d, 0x2a, 0xb1, 0xa4, 0x6e, 0xdc, 0x07, 0xe7, 0x41, 0x59, 0xd3, 0xaf, 0x04, + 0x82, 0xff, 0xc4, 0x6e, 0xda, 0x25, 0x57, 0x90, 0x2e, 0x06, 0x77, 0x2a, 0x54, 0x37, 0x44, 0x78, 0x7d, 0x76, 0x2f, + 0xd1, 0xc4, 0x61, 0xb6, 0x22, 0x0b, 0x3d, 0xbc, 0xf6, 0xe0, 0xf6, 0x79, 0x66, 0x2d, 0xee, 0x54, 0x82, 0xf6, 0xb5, + 0xd9, 0xab, 0x7e, 0xf2, 0x78, 0xf0, 0xab, 0xc1, 0x73, 0x41, 0x06, 0x37, 0xbb, 0x41, 0xd4, 0x0f, 0xa1, 0xf3, 0x2c, + 0xf8, 0x1e, 0xc1, 0x94, 0xfe, 0x95, 0x17, 0xe2, 0x57, 0x83, 0x8f, 0x32, 0x33, 0xa8, 0x1e, 0xab, 0x08, 0x52, 0x7e, + 0x92, 0x61, 0x84, 0x91, 0x61, 0xe8, 0xba, 0x0a, 0x51, 0xc2, 0x1b, 0x2c, 0x36, 0xb3, 0x7b, 0x53, 0xf3, 0x7f, 0x81, + 0xd4, 0x21, 0xfc, 0x90, 0xd8, 0x13, 0xf3, 0x10, 0xf6, 0x6a, 0xe6, 0x71, 0xb6, 0xaf, 0xa2, 0x8e, 0xf5, 0x66, 0x8b, + 0x27, 0x16, 0x54, 0x1f, 0xc2, 0xda, 0x54, 0x81, 0x4b, 0xc4, 0xdc, 0xae, 0xfd, 0x7f, 0xfc, 0x75, 0xda, 0xb1, 0x8d, + 0x98, 0x99, 0x1e, 0x8e, 0xfb, 0xc6, 0x15, 0x51, 0x17, 0xa0, 0x60, 0x0e, 0x5a, 0x57, 0xb0, 0x12, 0x8f, 0xda, 0xd3, + 0xdb, 0xae, 0xbf, 0x1f, 0x20, 0xc4, 0x0f, 0xcd, 0xf2, 0xbe, 0x42, 0x6c, 0x34, 0x69, 0xbb, 0xb1, 0x73, 0x6c, 0xab, + 0x0e, 0x2b, 0x0a, 0x25, 0x74, 0x43, 0x03, 0xe7, 0x6e, 0x20, 0xc0, 0xfa, 0x29, 0xce, 0xa2, 0x5d, 0xd8, 0x43, 0xd7, + 0x6e, 0x6b, 0x3c, 0x35, 0x7a, 0x62, 0xa4, 0x95, 0x80, 0x2d, 0x53, 0xdf, 0x79, 0x45, 0x77, 0x9b, 0x1b, 0x76, 0xae, + 0xcf, 0x6d, 0xa9, 0xf6, 0xe3, 0x78, 0x6c, 0x1b, 0x66, 0x99, 0xda, 0xbd, 0xbb, 0x66, 0xae, 0x7e, 0xb9, 0xce, 0x54, + 0x84, 0x6c, 0x38, 0x85, 0xe4, 0x84, 0xe4, 0xb6, 0xd7, 0x92, 0x18, 0xc5, 0x7a, 0xc7, 0x06, 0x8e, 0x90, 0x73, 0xb6, + 0x62, 0x06, 0x6b, 0xb3, 0xdd, 0xc7, 0xc2, 0x64, 0xc3, 0x69, 0xed, 0x1e, 0x5a, 0x68, 0x04, 0x97, 0x8c, 0xe7, 0x2a, + 0x93, 0xc5, 0xe3, 0x0e, 0xf3, 0xcb, 0xf6, 0x19, 0x8d, 0x17, 0x0d, 0xa7, 0x1a, 0x7b, 0x53, 0x52, 0x46, 0xb3, 0xef, + 0xdc, 0xd2, 0x5a, 0x24, 0xde, 0xbc, 0xa7, 0x77, 0x82, 0xa1, 0xb5, 0xf7, 0xaa, 0x2d, 0x80, 0xfa, 0x9f, 0xed, 0xac, + 0x58, 0xd0, 0x38, 0xec, 0x0c, 0x70, 0xe3, 0xe2, 0x79, 0x87, 0xe2, 0x31, 0x99, 0xe9, 0xbd, 0x15, 0x59, 0xef, 0xf2, + 0xbf, 0xda, 0xae, 0x13, 0x9f, 0x3d, 0xba, 0xdb, 0xea, 0xa0, 0xb5, 0xae, 0x8b, 0x94, 0xf8, 0x38, 0xad, 0x5d, 0x4c, + 0xdc, 0x92, 0x85, 0x97, 0x39, 0x9a, 0xff, 0x15, 0x8b, 0x1c, 0x36, 0x68, 0x9c, 0x9b, 0xf8, 0xd6, 0x52, 0x1a, 0x7d, + 0x6a, 0x50, 0x17, 0x26, 0x51, 0x89, 0x20, 0xb4, 0xd2, 0xbf, 0x62, 0xef, 0x6b, 0x6f, 0x33, 0x15, 0xd7, 0x29, 0xce, + 0x60, 0xf2, 0xa8, 0xe7, 0x1c, 0x49, 0xc7, 0x2c, 0x6b, 0x7c, 0x03, 0x4d, 0xdb, 0x4a, 0xd3, 0x64, 0x54, 0xc3, 0x46, + 0xac, 0x33, 0x1b, 0xf1, 0xc2, 0x48, 0xd3, 0xb6, 0x2b, 0xa1, 0xd3, 0xa9, 0xfa, 0xc5, 0x13, 0xe7, 0xd6, 0xc2, 0x7f, + 0xcb, 0x8b, 0x03, 0xc4, 0xb9, 0xae, 0x46, 0x1a, 0x19, 0x74, 0xe1, 0x2e, 0x3e, 0xe5, 0x8e, 0x5b, 0x39, 0x86, 0x60, + 0xd5, 0x6a, 0xe3, 0xe2, 0x50, 0xd6, 0xd7, 0x20, 0xf5, 0x3e, 0x18, 0x69, 0x32, 0x66, 0x57, 0xce, 0x9f, 0xe6, 0xe9, + 0xa1, 0x44, 0x99, 0x1a, 0x99, 0x36, 0x7c, 0xcf, 0xaf, 0xe6, 0x24, 0x76, 0x6d, 0x3c, 0x1f, 0x9c, 0x98, 0x7a, 0x2b, + 0x67, 0x25, 0x45, 0x01, 0xd0, 0x86, 0xb9, 0xb6, 0x64, 0x23, 0x65, 0xda, 0xb3, 0xce, 0xfb, 0x76, 0xe7, 0x8a, 0x93, + 0xd9, 0x69, 0x02, 0x5d, 0xa1, 0xa9, 0xea, 0xd4, 0x0c, 0x8d, 0x10, 0x98, 0xf1, 0x61, 0x0a, 0xfd, 0xa2, 0x48, 0x30, + 0x74, 0xd3, 0x0b, 0x8a, 0x15, 0x27, 0x9a, 0xe7, 0x4b, 0x5d, 0x25, 0xe1, 0xa6, 0xf6, 0x7e, 0xed, 0xfe, 0x97, 0x9e, + 0xdc, 0x45, 0x9d, 0x09, 0x41, 0x29, 0x60, 0xd2, 0x71, 0xf0, 0x61, 0x28, 0xc3, 0x1f, 0x57, 0x30, 0x7a, 0x91, 0x59, + 0x7f, 0x20, 0x92, 0x43, 0xc5, 0x77, 0x96, 0x5f, 0x5a, 0xa1, 0xf8, 0x89, 0xc8, 0x0e, 0x8a, 0xaf, 0x41, 0xc0, 0x23, + 0xa8, 0xd9, 0x4e, 0x57, 0x82, 0x27, 0x78, 0xc7, 0x8b, 0x7c, 0xc5, 0xc8, 0xeb, 0x69, 0xb5, 0xa4, 0x61, 0x68, 0x8e, + 0x25, 0x41, 0x63, 0x53, 0xc7, 0x12, 0x82, 0x79, 0x5f, 0x1f, 0xeb, 0xb9, 0xd5, 0x8e, 0x02, 0x27, 0x58, 0xfb, 0x81, + 0xb4, 0x8e, 0x74, 0x3c, 0xb5, 0x68, 0xd6, 0x36, 0x32, 0xd1, 0xd9, 0xc4, 0x40, 0x3a, 0x0b, 0x0e, 0x36, 0xe6, 0xd3, + 0x68, 0xae, 0xbc, 0x61, 0x04, 0xff, 0xbd, 0x0a, 0xcb, 0x59, 0x7a, 0xb5, 0xe5, 0x62, 0x1c, 0x55, 0xf8, 0x3f, 0x0d, + 0x13, 0xbe, 0xc9, 0xf9, 0xb8, 0x5c, 0x24, 0x44, 0xa8, 0x80, 0x07, 0x3a, 0x26, 0x7c, 0x1d, 0xad, 0x86, 0x11, 0x5a, + 0x75, 0x2b, 0xc8, 0x11, 0xd2, 0x7e, 0xdf, 0x54, 0x5b, 0xdf, 0x34, 0x67, 0x6f, 0xcf, 0x0d, 0x9b, 0x06, 0xf3, 0xe3, + 0x73, 0x8f, 0x4d, 0x37, 0x12, 0x55, 0x2c, 0xbf, 0x83, 0x8f, 0xda, 0x98, 0xe1, 0x83, 0xfe, 0xf0, 0xa6, 0x71, 0xcc, + 0x78, 0x95, 0x4d, 0x9a, 0xf4, 0xc3, 0x99, 0x6b, 0x81, 0xda, 0xa7, 0xa6, 0xee, 0x48, 0xd1, 0x81, 0xa3, 0xab, 0xf9, + 0x16, 0x5f, 0x89, 0xf0, 0xf0, 0x6b, 0x12, 0x95, 0x35, 0xcd, 0xa0, 0x4e, 0xa5, 0x34, 0x51, 0x75, 0xdb, 0x54, 0x00, + 0x7b, 0xcf, 0xb0, 0x32, 0x50, 0xa3, 0x27, 0xba, 0x13, 0x34, 0x42, 0x1a, 0xc7, 0x9f, 0x42, 0xfb, 0x91, 0xc6, 0x6f, + 0xc5, 0x94, 0x63, 0x3b, 0x86, 0x79, 0xd5, 0x00, 0x55, 0x0b, 0x7d, 0xfc, 0xeb, 0x9b, 0xad, 0xdb, 0xb5, 0xed, 0x76, + 0x87, 0xb0, 0x54, 0x2f, 0x8f, 0x5a, 0x34, 0x93, 0x98, 0xa6, 0x14, 0x16, 0x5d, 0xb4, 0x8e, 0x97, 0xd3, 0xc6, 0x41, + 0xad, 0x30, 0xd8, 0x16, 0xaa, 0x74, 0x19, 0x31, 0xdc, 0x4e, 0x61, 0x84, 0x4c, 0xa1, 0x42, 0x1f, 0xb1, 0x66, 0xba, + 0x75, 0xf7, 0x50, 0x5a, 0xcb, 0xf2, 0xad, 0x17, 0x6b, 0xd4, 0xb7, 0xde, 0x66, 0x35, 0x8a, 0x5a, 0x4c, 0xbc, 0x12, + 0x8c, 0xae, 0x2f, 0x13, 0x5a, 0xb9, 0x45, 0x5b, 0xa5, 0x20, 0x48, 0xec, 0xd6, 0xe2, 0x2b, 0xd1, 0x8e, 0xcd, 0x1c, + 0x89, 0xc9, 0xfc, 0xf4, 0xda, 0x54, 0x86, 0xca, 0x87, 0x0f, 0x3e, 0x67, 0x68, 0x8a, 0x27, 0xef, 0xc0, 0x4f, 0xba, + 0xfc, 0x49, 0xea, 0x03, 0xef, 0xb6, 0x0c, 0x4e, 0x51, 0x3b, 0xb7, 0x74, 0x18, 0xc0, 0x75, 0x52, 0xf0, 0x82, 0x2b, + 0x4c, 0x92, 0x46, 0x3e, 0x3a, 0x41, 0x4c, 0x8a, 0xce, 0x94, 0x35, 0x18, 0x94, 0xb5, 0x0c, 0x80, 0x35, 0xda, 0x84, + 0xe1, 0x23, 0x90, 0x19, 0x63, 0x06, 0x69, 0x1b, 0xe6, 0x94, 0xcf, 0xba, 0x3f, 0xbe, 0x10, 0xba, 0x3d, 0xd8, 0x13, + 0x51, 0x96, 0x0f, 0xc8, 0x07, 0x1d, 0xd2, 0xbf, 0x22, 0x31, 0xca, 0xe1, 0xb9, 0xdc, 0x7f, 0x12, 0x58, 0x38, 0x80, + 0x9b, 0xb5, 0x77, 0xec, 0x80, 0x64, 0xde, 0x2a, 0x2c, 0xbf, 0x1f, 0x02, 0x0c, 0x5b, 0x3b, 0xb1, 0x9c, 0x15, 0xa3, + 0x65, 0x39, 0x59, 0x41, 0xc3, 0xf2, 0x37, 0x80, 0xaf, 0x03, 0x56, 0xbd, 0x5f, 0xe2, 0x32, 0x53, 0x14, 0xf8, 0x67, + 0xe3, 0xb4, 0x4a, 0x5b, 0x10, 0x1f, 0x04, 0x22, 0x0f, 0xb0, 0x07, 0x57, 0x8f, 0x85, 0xb7, 0x53, 0xbe, 0x8b, 0xca, + 0xd2, 0x35, 0x1a, 0x39, 0xa5, 0x7a, 0xbf, 0xc5, 0x76, 0x83, 0x3d, 0x08, 0xa9, 0x2d, 0x94, 0x7f, 0x85, 0xaa, 0x4a, + 0x51, 0xeb, 0xcd, 0x08, 0x83, 0x16, 0x9c, 0x9b, 0x23, 0x50, 0x43, 0x60, 0xd4, 0xda, 0x5c, 0x4b, 0xa0, 0x35, 0x3f, + 0x80, 0x5d, 0xe7, 0xe3, 0x97, 0x51, 0x4c, 0x78, 0xbc, 0x6f, 0x1a, 0x93, 0x93, 0x1f, 0x3d, 0xee, 0xfa, 0x66, 0xdd, + 0x64, 0x88, 0x59, 0x24, 0xf5, 0x3c, 0xc2, 0x6c, 0xe7, 0xb5, 0x70, 0xb1, 0x3a, 0x41, 0xcf, 0xe5, 0x8a, 0x14, 0xf7, + 0xa8, 0xbb, 0x65, 0xf7, 0x7c, 0xaa, 0x9e, 0xc4, 0x58, 0x4b, 0x11, 0x3f, 0xc5, 0xb5, 0x99, 0x50, 0xa5, 0xc8, 0xcd, + 0x26, 0xb0, 0x95, 0x23, 0xed, 0xf1, 0x48, 0x96, 0x13, 0x75, 0xac, 0x41, 0xd4, 0x3c, 0xbe, 0xb3, 0x72, 0xe8, 0x46, + 0x77, 0xd8, 0x37, 0xff, 0x1f, 0xbb, 0xe9, 0xe9, 0x38, 0x93, 0x65, 0xf0, 0x32, 0x06, 0x67, 0xbc, 0xf3, 0xc2, 0xb4, + 0x4a, 0x45, 0x8c, 0x46, 0x3f, 0x16, 0x7d, 0x7f, 0xaa, 0x77, 0x5d, 0x82, 0x20, 0xd5, 0xe5, 0xbf, 0x81, 0xa3, 0xba, + 0x3a, 0x5c, 0x7a, 0x7a, 0xe6, 0x96, 0x46, 0x97, 0xef, 0x98, 0xc1, 0x5d, 0x05, 0x13, 0x60, 0x0d, 0xbc, 0x45, 0xef, + 0xdc, 0x12, 0xc2, 0x65, 0xd4, 0xbb, 0xee, 0x95, 0x53, 0x28, 0x3a, 0x47, 0x77, 0x83, 0x84, 0x1a, 0xae, 0xf3, 0xdc, + 0x3e, 0x5a, 0x29, 0x2a, 0x1f, 0xe7, 0xc3, 0x85, 0xb3, 0x44, 0x12, 0x05, 0xc7, 0x4b, 0x08, 0xd7, 0x7d, 0x3b, 0x66, + 0x84, 0x91, 0x6d, 0x4b, 0xa5, 0xba, 0xe1, 0x5d, 0xe8, 0x51, 0xcc, 0x5a, 0x36, 0xe0, 0xfc, 0x7f, 0xe9, 0xf5, 0x48, + 0xba, 0xb7, 0x29, 0xf1, 0xb8, 0xf0, 0xef, 0xe2, 0xc8, 0x29, 0x28, 0x89, 0x4a, 0xb4, 0x7d, 0x57, 0x76, 0xe0, 0x78, + 0x68, 0x0f, 0xe9, 0xb4, 0x29, 0xcb, 0x2a, 0x00, 0xad, 0x7d, 0xe6, 0x65, 0xe4, 0x64, 0xf4, 0xa4, 0xbd, 0x43, 0xd1, + 0x1b, 0x54, 0x26, 0x21, 0x87, 0x41, 0x22, 0xe6, 0x3a, 0xe0, 0xee, 0xaa, 0xdb, 0x5d, 0x73, 0x15, 0xba, 0x6b, 0x76, + 0xe5, 0x80, 0x8e, 0xe4, 0x90, 0x64, 0xe6, 0xac, 0xf6, 0x41, 0x11, 0x45, 0xde, 0x23, 0xf6, 0xc5, 0x9d, 0x4a, 0xba, + 0x99, 0x77, 0x51, 0x48, 0x14, 0x10, 0xc6, 0x29, 0x88, 0xf7, 0x04, 0x08, 0xa5, 0x75, 0x77, 0xd4, 0x26, 0x5c, 0xf5, + 0x4c, 0x5b, 0x19, 0xc3, 0x9d, 0xce, 0x9d, 0x91, 0x5d, 0xe0, 0x52, 0xf7, 0x62, 0x08, 0xa2, 0x40, 0x4e, 0x41, 0x0c, + 0x27, 0x41, 0xf1, 0xa1, 0x38, 0x90, 0x80, 0x43, 0xe4, 0x41, 0xa9, 0x71, 0xc9, 0xdc, 0x78, 0xa3, 0x10, 0x62, 0x31, + 0x12, 0x31, 0x21, 0xd9, 0x30, 0x70, 0x4c, 0x05, 0xda, 0xfd, 0x72, 0xdf, 0x7b, 0xe1, 0xf7, 0x43, 0x4d, 0x2d, 0xe6, + 0x42, 0x16, 0x46, 0xab, 0x93, 0x7b, 0x81, 0x63, 0xbe, 0x57, 0x2f, 0xb7, 0x91, 0xbd, 0xf0, 0x8d, 0x4b, 0x72, 0x95, + 0x12, 0x10, 0xf6, 0x1f, 0x8c, 0x03, 0x01, 0x30, 0x97, 0x56, 0xb5, 0x96, 0xc8, 0xc3, 0x1b, 0x69, 0xd6, 0xb4, 0x14, + 0xeb, 0x66, 0x1e, 0x2a, 0xc0, 0x92, 0x5a, 0xdc, 0x30, 0x97, 0x15, 0xce, 0x68, 0x0e, 0x4a, 0x78, 0xd3, 0x42, 0xd7, + 0xe6, 0x73, 0x78, 0x92, 0xe6, 0xe8, 0xf7, 0xf0, 0x56, 0x75, 0xcb, 0x92, 0xea, 0x4c, 0x32, 0x98, 0xc8, 0x54, 0xea, + 0x69, 0x38, 0xee, 0xa4, 0xef, 0x04, 0x63, 0xb2, 0xd0, 0x78, 0x27, 0xeb, 0x6c, 0xec, 0x0c, 0x7d, 0x60, 0x7f, 0xc0, + 0x05, 0xc5, 0x77, 0x49, 0xc7, 0xb7, 0x49, 0x84, 0x45, 0x56, 0x76, 0xed, 0xf2, 0xd2, 0xf7, 0x5d, 0x6f, 0xe6, 0xa5, + 0xfb, 0xec, 0xbb, 0xdf, 0xbd, 0x25, 0x6b, 0x45, 0xc9, 0x49, 0xf2, 0x84, 0xe5, 0x6d, 0xda, 0x1e, 0xf2, 0x74, 0x60, + 0xc8, 0xdc, 0x38, 0xae, 0x7f, 0x51, 0x8c, 0x34, 0x75, 0xd8, 0x51, 0x7a, 0x53, 0x81, 0xa7, 0xf6, 0x39, 0x8b, 0x0e, + 0x14, 0xcf, 0x30, 0x5d, 0x13, 0xe1, 0xcd, 0xfe, 0xc5, 0xfc, 0xdf, 0x03, 0xa2, 0xe3, 0xc3, 0x98, 0x36, 0xe4, 0xc3, + 0x2a, 0xbc, 0x14, 0xc7, 0xe2, 0x07, 0x8b, 0x49, 0xe4, 0x49, 0x1c, 0xe0, 0x7d, 0x60, 0x91, 0x0a, 0x23, 0x83, 0x3a, + 0x56, 0x76, 0xc7, 0xf1, 0x02, 0x30, 0xe2, 0x21, 0xe3, 0xfc, 0xe2, 0x33, 0x10, 0x38, 0x5e, 0xa8, 0x66, 0x3b, 0x87, + 0x15, 0x08, 0x80, 0x8c, 0x59, 0xa9, 0xb8, 0x18, 0xcd, 0xa2, 0x14, 0x83, 0x67, 0x7c, 0x68, 0x57, 0x0d, 0xb1, 0xcc, + 0xfc, 0x60, 0x50, 0xce, 0xad, 0x85, 0x14, 0xdc, 0xae, 0x2f, 0x8c, 0x09, 0x6e, 0xdb, 0x48, 0xb0, 0x45, 0xfd, 0x18, + 0x10, 0x8b, 0x0b, 0xea, 0x1a, 0xbf, 0xd7, 0x99, 0x3b, 0x69, 0x9f, 0xb8, 0x8e, 0xd2, 0xb2, 0x94, 0xc4, 0x75, 0x1e, + 0x46, 0x02, 0xc1, 0xf4, 0x9a, 0x10, 0x95, 0x18, 0x62, 0x1f, 0xcb, 0xbd, 0x01, 0xf0, 0x18, 0xa2, 0x23, 0xc7, 0xec, + 0xbc, 0x43, 0x78, 0xba, 0x81, 0x5f, 0x16, 0xbf, 0x95, 0xf1, 0xeb, 0xe3, 0x51, 0x76, 0x44, 0x3e, 0xbc, 0x91, 0xb8, + 0x53, 0x31, 0x07, 0xd2, 0xc8, 0x15, 0xb0, 0xb4, 0x05, 0x72, 0x91, 0x71, 0x0c, 0x5b, 0x3f, 0xb5, 0x3e, 0x06, 0x3f, + 0xf6, 0xb1, 0xe8, 0xf8, 0x75, 0xa0, 0xaf, 0x52, 0x22, 0x7f, 0x2b, 0xa5, 0x38, 0x7b, 0x6f, 0x46, 0xbb, 0x3b, 0x71, + 0x53, 0xaf, 0xec, 0x6d, 0x43, 0x7d, 0x93, 0xb8, 0x7d, 0x6b, 0x1e, 0x03, 0xee, 0xeb, 0xc4, 0x8d, 0xa1, 0xd0, 0x27, + 0xcb, 0xe3, 0x46, 0x53, 0x13, 0x43, 0x77, 0x1e, 0xe1, 0x57, 0xa7, 0x3d, 0x9c, 0xdd, 0x97, 0x26, 0xdd, 0x08, 0xb3, + 0xb8, 0xd8, 0x25, 0x19, 0x1c, 0x06, 0x2c, 0x0e, 0x45, 0x8a, 0x16, 0xb9, 0x6c, 0x0c, 0x91, 0xc3, 0x0e, 0xee, 0x26, + 0x8d, 0x53, 0xde, 0x31, 0x78, 0x69, 0x52, 0x9f, 0xb7, 0xd5, 0x62, 0x42, 0x4d, 0x98, 0x6a, 0xf0, 0xd6, 0xb6, 0x7c, + 0x2c, 0x94, 0x72, 0x12, 0x48, 0xa7, 0x2c, 0x54, 0x0a, 0x7e, 0xe2, 0x0f, 0xf7, 0x7f, 0x50, 0x94, 0x3b, 0x02, 0x6e, + 0x05, 0x1d, 0xfe, 0x7c, 0x10, 0x2f, 0x63, 0x88, 0x47, 0x46, 0xc6, 0xf4, 0x2f, 0x29, 0xab, 0x7e, 0x05, 0x99, 0x98, + 0xaf, 0xb3, 0x07, 0xb9, 0x1a, 0xdf, 0xa9, 0xb5, 0x30, 0xae, 0x23, 0x0d, 0x4d, 0xcc, 0x4f, 0xa1, 0xb0, 0xe9, 0x2a, + 0x03, 0x0b, 0xa5, 0x0c, 0xf9, 0xbe, 0xd4, 0xed, 0xa3, 0xe1, 0x27, 0xa1, 0xe7, 0xd3, 0x0c, 0x93, 0x90, 0x16, 0x40, + 0xf5, 0xe1, 0x68, 0xd2, 0x0d, 0x76, 0xf3, 0x51, 0x07, 0x2a, 0x9d, 0x1d, 0x73, 0x4a, 0x90, 0xf3, 0xfc, 0x64, 0x1b, + 0x7b, 0xc7, 0x5f, 0x1b, 0x7f, 0x83, 0xc0, 0x67, 0xfe, 0xa3, 0x37, 0x55, 0x1b, 0x88, 0xf5, 0x72, 0x46, 0xd0, 0xb6, + 0x0c, 0xb8, 0xa5, 0xca, 0xa1, 0xd9, 0x52, 0x31, 0x2c, 0xcc, 0xd4, 0xc2, 0x14, 0x2f, 0x3a, 0x41, 0xee, 0x0f, 0x21, + 0x16, 0x28, 0x37, 0x20, 0x65, 0xc9, 0x31, 0x1d, 0x44, 0x8a, 0xde, 0x06, 0x0a, 0x22, 0x94, 0x5f, 0xbf, 0xd4, 0xff, + 0x45, 0x04, 0x58, 0x8e, 0xb4, 0xca, 0x40, 0x32, 0xb5, 0xb1, 0x9c, 0xd4, 0xe2, 0x54, 0x9c, 0x55, 0xca, 0x30, 0xf9, + 0xdd, 0x78, 0xd9, 0x9a, 0xa0, 0x66, 0x08, 0x4f, 0xc9, 0xc1, 0x1a, 0x4d, 0x4c, 0x4f, 0x99, 0xfd, 0x05, 0x17, 0xa2, + 0x41, 0x7e, 0x23, 0xb8, 0x75, 0x2c, 0x6d, 0x14, 0x78, 0xd4, 0xbe, 0x89, 0x15, 0x95, 0x56, 0xe1, 0x9c, 0xa8, 0x99, + 0x6c, 0xcb, 0x5e, 0xee, 0xca, 0x3d, 0x06, 0x2e, 0x33, 0x23, 0xd0, 0x4b, 0xeb, 0x7b, 0xef, 0x00, 0xff, 0xd1, 0xa2, + 0xc8, 0x0d, 0xdb, 0x22, 0x85, 0x8c, 0x6d, 0xbd, 0xf1, 0x5b, 0x7d, 0x8a, 0x83, 0x3c, 0xf6, 0x42, 0x2b, 0x3b, 0xe1, + 0x9d, 0xef, 0x4e, 0x19, 0xe6, 0x45, 0x1c, 0xe7, 0x59, 0x54, 0xe8, 0xc3, 0xa2, 0xaa, 0x44, 0xff, 0x09, 0x00, 0x46, + 0xee, 0x72, 0x2a, 0xfc, 0x5b, 0xc2, 0x6d, 0x7c, 0xd0, 0x4e, 0x0d, 0xe7, 0x73, 0xaa, 0xcf, 0xbb, 0xee, 0x3b, 0xfc, + 0x30, 0x7c, 0xad, 0x71, 0x44, 0x05, 0xa6, 0x69, 0x9e, 0x98, 0xad, 0xe1, 0x77, 0x0a, 0xf8, 0xfe, 0xa1, 0x14, 0xdb, + 0xb0, 0x99, 0x56, 0xed, 0xcd, 0xbc, 0xde, 0xc1, 0x67, 0xce, 0x6a, 0x96, 0xaf, 0x3f, 0xf8, 0x3e, 0xa1, 0x2c, 0xc2, + 0x6f, 0xcb, 0x44, 0x3d, 0xe2, 0x2c, 0x1d, 0x5c, 0xc0, 0xe3, 0x1e, 0xc9, 0xd0, 0xf3, 0x75, 0x36, 0x22, 0x7f, 0xb4, + 0x71, 0x01, 0x69, 0xab, 0x09, 0x25, 0xea, 0x44, 0x8f, 0x48, 0xca, 0x58, 0x58, 0x68, 0x5b, 0x1d, 0x90, 0x45, 0xc1, + 0x72, 0x1b, 0x38, 0x4f, 0x4c, 0x11, 0x0e, 0xdf, 0xb5, 0xa7, 0x8b, 0xa8, 0xff, 0x31, 0x03, 0xf8, 0x0f, 0x98, 0x18, + 0x15, 0xca, 0xff, 0x0e, 0xc3, 0x1f, 0x84, 0x11, 0x71, 0x3a, 0x31, 0x3b, 0x30, 0x60, 0xe4, 0x45, 0x65, 0x46, 0x52, + 0x62, 0xad, 0x95, 0x3c, 0xf8, 0x3e, 0x14, 0x8d, 0xeb, 0x1a, 0x84, 0x60, 0x83, 0x69, 0x05, 0xf1, 0x70, 0x1a, 0x51, + 0xd6, 0x78, 0x34, 0x7e, 0x4f, 0xa5, 0x26, 0xf4, 0xf8, 0x36, 0x4a, 0x16, 0x8f, 0xaa, 0x27, 0xca, 0x47, 0x12, 0x43, + 0xda, 0xc8, 0x49, 0xf1, 0x26, 0xe3, 0xfd, 0xb4, 0x31, 0x22, 0x39, 0x39, 0x9d, 0x1d, 0x91, 0xf2, 0x0b, 0x19, 0x66, + 0xd7, 0x7f, 0xf1, 0xf2, 0x8b, 0x2f, 0xbe, 0x96, 0x4a, 0x54, 0xd7, 0x22, 0x86, 0x6e, 0xd7, 0xd1, 0xfb, 0xae, 0x84, + 0x21, 0x1d, 0x52, 0x1e, 0x14, 0x12, 0x53, 0x59, 0x20, 0x0d, 0xf9, 0x49, 0x54, 0xfe, 0x1e, 0xe6, 0xb3, 0x77, 0xaf, + 0x52, 0x97, 0xa4, 0xac, 0x24, 0x2e, 0x0f, 0x58, 0x9a, 0x4c, 0xbc, 0x39, 0x0f, 0xbb, 0x3f, 0x27, 0x6f, 0xfe, 0xaf, + 0x28, 0x63, 0xaa, 0x29, 0x47, 0x16, 0xea, 0xa0, 0x94, 0xd5, 0x70, 0xda, 0xe2, 0x8b, 0x20, 0xda, 0x2a, 0x74, 0xa9, + 0x79, 0xe0, 0xb2, 0xb0, 0x26, 0x82, 0x2d, 0xe8, 0xe9, 0x30, 0xb2, 0x25, 0xb5, 0x89, 0x4d, 0xaf, 0x23, 0xcf, 0xf2, + 0xa9, 0xda, 0x5d, 0xea, 0x63, 0xef, 0xa0, 0x1e, 0x8b, 0xab, 0xfd, 0xd4, 0x64, 0x1a, 0x70, 0x81, 0xa0, 0x7e, 0x05, + 0xb9, 0x55, 0x8c, 0xb8, 0xd1, 0xcd, 0xfd, 0x63, 0xb5, 0x75, 0x2b, 0xff, 0xb4, 0x0b, 0x22, 0x23, 0x81, 0x81, 0x66, + 0xd1, 0x6a, 0x42, 0x3f, 0x36, 0x2c, 0x85, 0x21, 0x67, 0x4b, 0x66, 0x39, 0xaf, 0x0a, 0xda, 0x95, 0xb6, 0x82, 0x03, + 0x12, 0x46, 0xeb, 0x18, 0x33, 0x83, 0xcf, 0xa1, 0x20, 0x5f, 0xb5, 0xc9, 0x05, 0xfb, 0xe2, 0x9e, 0x26, 0x98, 0x0a, + 0xc2, 0xbc, 0x52, 0x30, 0x9d, 0xf5, 0xcd, 0xc2, 0x1c, 0x0b, 0x85, 0xfc, 0xf8, 0x0b, 0x8a, 0x83, 0xa9, 0x40, 0x17, + 0xf9, 0x2b, 0x0d, 0xdb, 0xce, 0x2c, 0xfa, 0xee, 0x83, 0x02, 0xbc, 0x51, 0x47, 0xe6, 0x25, 0x8b, 0xbf, 0x7a, 0xe7, + 0xe3, 0xe4, 0x1b, 0x2d, 0xb2, 0x8b, 0x89, 0xfe, 0x52, 0x49, 0x33, 0xbf, 0x2e, 0xf5, 0x50, 0xb6, 0xa7, 0x3c, 0xae, + 0x98, 0xe6, 0x3d, 0x4a, 0x7f, 0x1a, 0xf3, 0x84, 0x4c, 0x68, 0x2f, 0xa7, 0xbf, 0x25, 0x6a, 0x76, 0x9f, 0x59, 0xaa, + 0xfe, 0x0d, 0x2f, 0x95, 0x26, 0xe5, 0x58, 0xc6, 0xb4, 0x9e, 0x12, 0xeb, 0x96, 0x05, 0x0c, 0xb2, 0x28, 0x4e, 0x6c, + 0xb4, 0xd9, 0x3b, 0xa2, 0xf9, 0x4e, 0xed, 0x65, 0x72, 0xc2, 0xc2, 0x5c, 0x5d, 0xc9, 0x76, 0x1a, 0x61, 0xb7, 0xde, + 0x13, 0xa9, 0x21, 0x68, 0x46, 0xc9, 0xae, 0x76, 0x7b, 0x41, 0xc3, 0xc4, 0x9a, 0x49, 0x91, 0x2d, 0x9a, 0xe5, 0x4e, + 0xd0, 0x43, 0x3e, 0x95, 0xfc, 0xea, 0x3f, 0x5b, 0x88, 0x9b, 0xcd, 0xf9, 0x3d, 0x23, 0x32, 0x08, 0x83, 0xdc, 0xad, + 0x22, 0x5e, 0xce, 0x04, 0x0a, 0x63, 0x67, 0x82, 0xcd, 0xbb, 0x58, 0x47, 0x58, 0x24, 0xaa, 0x23, 0x69, 0x48, 0x57, + 0x79, 0x08, 0x54, 0xb1, 0xef, 0xc9, 0xd3, 0xca, 0x28, 0x5a, 0xbf, 0x3a, 0xf6, 0x19, 0x10, 0x52, 0x25, 0xcb, 0x8a, + 0xb4, 0x72, 0x85, 0x99, 0x81, 0x91, 0x84, 0x83, 0x23, 0xd0, 0x4d, 0x13, 0xc2, 0xcb, 0x43, 0x7a, 0x69, 0x2d, 0x35, + 0xaa, 0xc5, 0x35, 0x78, 0x25, 0x80, 0xd8, 0x64, 0x8c, 0x5f, 0xef, 0xf6, 0xf4, 0xb0, 0xbe, 0x68, 0xb1, 0xfe, 0x88, + 0x80, 0x63, 0xa4, 0xfb, 0xa2, 0x1c, 0x7a, 0x03, 0x96, 0xb5, 0xc4, 0xb7, 0x8f, 0x61, 0xa8, 0x74, 0xa0, 0x5e, 0x8e, + 0xdc, 0x22, 0xaa, 0x37, 0xc0, 0xb5, 0xdb, 0x15, 0x11, 0xbe, 0x9d, 0x1f, 0xd3, 0xa4, 0x96, 0x10, 0xc4, 0xba, 0x8f, + 0x68, 0x96, 0x89, 0xb0, 0xd9, 0xb8, 0xeb, 0x70, 0x71, 0x0c, 0x45, 0x1f, 0x9e, 0xe2, 0x22, 0x96, 0x9c, 0x2d, 0xbd, + 0xb4, 0x31, 0x4f, 0x87, 0xf4, 0x53, 0xdb, 0x51, 0xe1, 0xd1, 0x0b, 0xcb, 0x85, 0xc6, 0x9d, 0xa4, 0xe0, 0xea, 0x3d, + 0x10, 0x26, 0xe9, 0x73, 0xf7, 0x98, 0xc7, 0xd5, 0xe8, 0x2d, 0x38, 0x7d, 0x0b, 0x68, 0x6f, 0x8a, 0xe0, 0x72, 0xd5, + 0x5e, 0x9a, 0x30, 0xa3, 0x3d, 0xcf, 0x74, 0xb6, 0x24, 0x55, 0x23, 0xde, 0x8b, 0x16, 0xbc, 0x86, 0x72, 0x4f, 0x2c, + 0x61, 0xcc, 0xe0, 0xb6, 0x4b, 0x48, 0xb2, 0xaf, 0xa5, 0x82, 0x95, 0xa0, 0x07, 0xf2, 0xa8, 0x48, 0x46, 0x49, 0xa6, + 0xdb, 0xfe, 0x6c, 0xe6, 0xb6, 0x37, 0x95, 0xdf, 0xb6, 0xce, 0x44, 0x95, 0xa4, 0xaf, 0x57, 0x7d, 0xda, 0x3d, 0xa3, + 0x2b, 0x0f, 0x02, 0xfa, 0x96, 0xd1, 0x5b, 0x2e, 0xb0, 0x6e, 0xc9, 0x0d, 0xa9, 0x20, 0xf6, 0x2e, 0x2b, 0x70, 0xe1, + 0xad, 0x3d, 0x98, 0xb0, 0x06, 0xef, 0x33, 0x3d, 0x69, 0xad, 0xbe, 0x7d, 0xa9, 0xeb, 0xf8, 0xec, 0xbb, 0xed, 0x86, + 0x68, 0xf0, 0x5b, 0x2e, 0xbe, 0x17, 0x9f, 0x99, 0x69, 0x15, 0x0e, 0x66, 0x51, 0xfa, 0x2a, 0xfd, 0x8b, 0x93, 0xd6, + 0x91, 0x0b, 0x70, 0x00, 0xf2, 0x6e, 0xb8, 0x2e, 0xc6, 0x61, 0xbc, 0xe6, 0x84, 0xf3, 0xd4, 0x7b, 0xb0, 0x6b, 0xa7, + 0x14, 0xfc, 0x73, 0x86, 0x8d, 0x1c, 0x32, 0x3b, 0x5e, 0x84, 0x6f, 0x6a, 0x1b, 0x7e, 0x4e, 0xfc, 0x80, 0xbf, 0xce, + 0x0c, 0xef, 0x67, 0x71, 0xf6, 0xb6, 0xc0, 0x1f, 0xa6, 0x78, 0xe1, 0xcf, 0x95, 0x30, 0xe3, 0x2b, 0xfe, 0x95, 0xf8, + 0x6f, 0x04, 0x6f, 0x98, 0x70, 0x99, 0xad, 0x35, 0x5a, 0x64, 0xf3, 0x9b, 0x7c, 0x7a, 0x77, 0xf7, 0x70, 0x76, 0xb3, + 0x5a, 0x56, 0xb4, 0x61, 0x25, 0x7b, 0x8e, 0xea, 0x3a, 0xae, 0xca, 0xfe, 0x99, 0xc2, 0x6a, 0x69, 0xdf, 0x51, 0x24, + 0xf7, 0x26, 0xe9, 0xb3, 0xb7, 0x9b, 0x53, 0x93, 0x87, 0xa2, 0x09, 0x1d, 0xe9, 0xcb, 0xa3, 0xb5, 0x04, 0x9e, 0x96, + 0x5d, 0xa9, 0x8b, 0x60, 0xf2, 0xc3, 0xcc, 0xcb, 0x5e, 0xa4, 0x34, 0x55, 0x87, 0xdd, 0xb5, 0x8a, 0x24, 0x54, 0x69, + 0x47, 0xee, 0x94, 0x62, 0xd2, 0xaa, 0x03, 0x67, 0xa0, 0x20, 0xfb, 0x4a, 0x24, 0x4e, 0xf8, 0x21, 0x7a, 0xf0, 0x81, + 0xf1, 0xa4, 0x88, 0xe6, 0xc1, 0x14, 0xe1, 0xff, 0x9f, 0xae, 0x67, 0xd5, 0x33, 0x1b, 0xa0, 0xd6, 0xff, 0x05, 0x62, + 0x0e, 0x4d, 0x55, 0x42, 0xf2, 0xc0, 0x84, 0x3b, 0x7f, 0x7a, 0xd6, 0x0c, 0x16, 0x96, 0x1f, 0xd5, 0x61, 0x90, 0xa7, + 0xd9, 0x39, 0x99, 0xc8, 0x38, 0x4e, 0xce, 0x94, 0x42, 0x3d, 0xe3, 0xea, 0xcb, 0x35, 0x88, 0xde, 0x6b, 0xaa, 0xd5, + 0xa1, 0x93, 0x74, 0x92, 0x77, 0xc6, 0x52, 0x2c, 0xa2, 0x65, 0xbb, 0x6a, 0xe3, 0x62, 0x3b, 0x82, 0x33, 0x28, 0x38, + 0xcb, 0x1c, 0x7a, 0x0f, 0x16, 0xda, 0xee, 0xdc, 0xd8, 0x61, 0xba, 0x37, 0x37, 0x0e, 0x47, 0x04, 0x8d, 0xdd, 0x36, + 0xdd, 0xb6, 0x22, 0x2a, 0xb1, 0xd9, 0xa2, 0x8b, 0x78, 0x63, 0x40, 0x40, 0x2f, 0x3e, 0x4e, 0xf5, 0xa9, 0xcf, 0xdb, + 0x4e, 0xbe, 0xd2, 0x09, 0xcb, 0x5a, 0xce, 0xbd, 0xc3, 0x78, 0xd5, 0x8d, 0x82, 0xd0, 0x2c, 0x84, 0x54, 0xf6, 0x42, + 0xe7, 0x09, 0xd8, 0xc4, 0x88, 0x3f, 0x62, 0x2b, 0xa1, 0x4c, 0x0e, 0xac, 0x0a, 0x4a, 0xc7, 0x87, 0x9a, 0x1d, 0x08, + 0x42, 0x57, 0xfb, 0xc8, 0x06, 0x72, 0x2c, 0xb4, 0x13, 0x19, 0x88, 0x26, 0x0e, 0x81, 0x6b, 0xac, 0x11, 0xd6, 0x47, + 0x32, 0x5e, 0x0e, 0x6d, 0xbd, 0x59, 0x03, 0x9b, 0xa8, 0xcd, 0x41, 0xef, 0xfe, 0x53, 0xde, 0xa1, 0x8b, 0x22, 0x6b, + 0x3d, 0x67, 0x4c, 0xcf, 0x22, 0xe6, 0x41, 0xb1, 0x2c, 0x81, 0x46, 0x11, 0x8a, 0xd0, 0xbf, 0x27, 0xf6, 0x50, 0x4f, + 0x2a, 0xa3, 0x3c, 0x61, 0x3e, 0xac, 0x50, 0x4d, 0xab, 0xa5, 0xa4, 0x53, 0x16, 0x1e, 0xc3, 0xdd, 0xc1, 0x8f, 0xaf, + 0xfd, 0xd1, 0xb8, 0xfe, 0x6a, 0xf4, 0xb1, 0xed, 0xf9, 0x9a, 0x96, 0xa9, 0x1f, 0x67, 0x4d, 0x7e, 0x1d, 0x9c, 0x37, + 0x16, 0x9b, 0xed, 0x95, 0x1e, 0xb8, 0x64, 0x22, 0x50, 0xaa, 0x5f, 0x66, 0xfb, 0x01, 0x9d, 0x2d, 0x14, 0x9f, 0x1a, + 0x15, 0xe5, 0xde, 0x5a, 0x8d, 0x65, 0x80, 0x70, 0x0c, 0x69, 0xa4, 0x5d, 0x62, 0x0a, 0x22, 0xad, 0xcf, 0xd2, 0x53, + 0xc0, 0x6b, 0x6b, 0x34, 0x8f, 0xe0, 0x90, 0x21, 0xd3, 0x24, 0xb1, 0x22, 0xfd, 0x0c, 0x10, 0xb7, 0x11, 0xd2, 0x8b, + 0xf4, 0x0f, 0x28, 0x01, 0x78, 0x15, 0xd1, 0xde, 0xa8, 0x8c, 0x45, 0xb2, 0x2a, 0xab, 0x95, 0xc2, 0x72, 0x7c, 0xc0, + 0xbf, 0xf4, 0x0f, 0x0b, 0x16, 0xa8, 0x1a, 0xe9, 0xd8, 0xc2, 0x4d, 0x04, 0x6a, 0x85, 0xed, 0x46, 0x88, 0xe2, 0xfb, + 0x75, 0x7d, 0xa4, 0x6c, 0x2e, 0x16, 0xc7, 0x18, 0x5b, 0xed, 0xcd, 0xd7, 0x5c, 0x6e, 0x3b, 0x43, 0xb7, 0x6d, 0xee, + 0xc0, 0xde, 0xa4, 0x7b, 0x1a, 0xbf, 0x7d, 0xab, 0xe1, 0x29, 0x7e, 0xf3, 0x6a, 0xa4, 0xf4, 0xf2, 0x78, 0x25, 0x74, + 0xef, 0xc9, 0x82, 0xe6, 0xc6, 0x65, 0x5c, 0xfa, 0xdd, 0x7b, 0x8a, 0xf0, 0x57, 0xfd, 0x57, 0x6a, 0x3c, 0xa9, 0xca, + 0xca, 0xdf, 0xac, 0x14, 0xf6, 0x07, 0x86, 0xaa, 0xca, 0x90, 0x21, 0x90, 0xa7, 0x4a, 0x0f, 0xb6, 0x27, 0x51, 0x6e, + 0xbf, 0x29, 0x69, 0x6c, 0x01, 0x17, 0x8a, 0x4e, 0x8d, 0xac, 0x72, 0xc2, 0xb3, 0x9e, 0xad, 0xf7, 0x90, 0x44, 0x5c, + 0xb9, 0xce, 0xc4, 0x11, 0x77, 0x3f, 0xe7, 0xf3, 0x8f, 0x2a, 0x02, 0x3a, 0x64, 0xf1, 0x51, 0x77, 0x19, 0x05, 0x41, + 0xc3, 0x0b, 0x69, 0x98, 0x20, 0x33, 0xa1, 0xac, 0xa9, 0x76, 0x5f, 0xa6, 0x69, 0xbd, 0x3e, 0x7f, 0x2e, 0x8e, 0xbd, + 0x1d, 0x90, 0xcc, 0xc6, 0x9c, 0x69, 0x56, 0x12, 0x37, 0xf2, 0xe0, 0x54, 0xd1, 0xe6, 0x2c, 0xc8, 0xd6, 0xea, 0xad, + 0x9e, 0x93, 0x39, 0xe0, 0x24, 0xd2, 0x32, 0xcc, 0x8f, 0x3c, 0xe7, 0xcf, 0x15, 0x27, 0xd3, 0xe8, 0xbe, 0x57, 0x63, + 0xdb, 0x66, 0x98, 0xf4, 0x24, 0x03, 0x40, 0x9d, 0x08, 0xe0, 0x9b, 0x12, 0xd4, 0x01, 0x8a, 0xaf, 0x2d, 0x8b, 0xc9, + 0x4c, 0x0c, 0x08, 0x26, 0x93, 0xfd, 0xda, 0x93, 0x03, 0xd3, 0x99, 0x08, 0x6c, 0x18, 0xf2, 0xcf, 0x40, 0x54, 0xe3, + 0xdb, 0x14, 0x24, 0xa1, 0x68, 0x4d, 0xa6, 0xb8, 0xf9, 0x04, 0x05, 0x9e, 0x7d, 0x11, 0xb2, 0xa5, 0x7e, 0x53, 0xc6, + 0x29, 0x84, 0xcd, 0x69, 0x3d, 0x98, 0xb2, 0x32, 0x9e, 0x49, 0x78, 0x8d, 0xe3, 0x9f, 0x2d, 0x82, 0xc7, 0xa8, 0xbc, + 0x8c, 0xc1, 0x1a, 0x8d, 0x5f, 0xc0, 0xe0, 0xb2, 0xb7, 0xa2, 0xc3, 0x28, 0xae, 0x3f, 0x6c, 0x8d, 0x71, 0x92, 0xd5, + 0x7e, 0x71, 0xf6, 0x37, 0xdb, 0x6f, 0x3d, 0x72, 0xf3, 0xd5, 0xcd, 0xce, 0x0e, 0x4d, 0x6b, 0x05, 0x83, 0x1e, 0xc6, + 0x60, 0x03, 0x70, 0xc5, 0x38, 0xb3, 0x91, 0x62, 0x47, 0xcd, 0x33, 0xe0, 0xa8, 0x57, 0x37, 0x3f, 0xd2, 0xee, 0xf8, + 0x79, 0x86, 0xf8, 0x44, 0x22, 0x4c, 0xde, 0x89, 0x60, 0x83, 0x5a, 0xba, 0xa3, 0x3f, 0xf2, 0x54, 0x86, 0x16, 0x15, + 0x6f, 0x26, 0x79, 0x4e, 0x31, 0xd1, 0xb2, 0xdc, 0x1e, 0x3d, 0xf1, 0x16, 0xd3, 0x52, 0x87, 0xda, 0x65, 0xed, 0x34, + 0x8d, 0x1d, 0x53, 0xa8, 0x27, 0xec, 0xf8, 0x3b, 0x8c, 0xf2, 0xaf, 0x85, 0x97, 0x7f, 0x3e, 0xfe, 0xd7, 0x1c, 0xff, + 0x22, 0xdc, 0xd5, 0xc9, 0xea, 0xf0, 0xe7, 0xe8, 0xff, 0x1e, 0x1f, 0x50, 0x9d, 0xbc, 0xd9, 0xda, 0x7c, 0xa3, 0x5a, + 0x6f, 0x92, 0x47, 0x6b, 0x3d, 0x4e, 0xd0, 0x57, 0x1f, 0x77, 0x4e, 0x27, 0x18, 0xe7, 0x9c, 0x3a, 0x3f, 0x8a, 0xfd, + 0xd1, 0x12, 0x17, 0x7e, 0x31, 0xfe, 0xb0, 0xb5, 0x99, 0xdc, 0xa3, 0xfd, 0x37, 0xa4, 0x72, 0x9f, 0x9f, 0x50, 0x6d, + 0x2b, 0x1a, 0x24, 0x7f, 0x7f, 0xe8, 0xdc, 0x11, 0x81, 0x0d, 0xe7, 0xfe, 0xeb, 0x35, 0x8d, 0x3f, 0x19, 0x5c, 0x44, + 0x8a, 0xd6, 0x0a, 0x8b, 0xcf, 0xf4, 0x99, 0x56, 0x56, 0xdf, 0xb8, 0x55, 0x65, 0x1b, 0x3a, 0xa1, 0x36, 0xdd, 0x00, + 0xc4, 0xa4, 0x82, 0x06, 0x65, 0xed, 0x7e, 0xf9, 0xd3, 0x44, 0x1b, 0x12, 0x8a, 0x7e, 0xf6, 0x83, 0x91, 0x76, 0x37, + 0xa7, 0x0a, 0x20, 0xb1, 0x61, 0x7a, 0xfc, 0x52, 0x30, 0x19, 0xd0, 0xf0, 0x50, 0x47, 0x17, 0xad, 0x55, 0x9f, 0x45, + 0x7a, 0x63, 0xbd, 0x26, 0xc5, 0xf5, 0x15, 0x90, 0x20, 0xa4, 0x69, 0xb8, 0xfc, 0x3f, 0xfe, 0x44, 0x24, 0x4d, 0xaf, + 0xd1, 0x50, 0x39, 0x0d, 0xfd, 0xf5, 0x3b, 0xb4, 0xec, 0x85, 0x96, 0x33, 0x7b, 0x19, 0x15, 0x03, 0xcf, 0x82, 0xa0, + 0xba, 0x22, 0xc7, 0x26, 0x57, 0xe3, 0x39, 0x29, 0xc7, 0xfc, 0x1f, 0x67, 0x79, 0xbd, 0x86, 0x39, 0xc7, 0x88, 0xef, + 0xec, 0x02, 0x61, 0x49, 0x56, 0xc3, 0xc6, 0xec, 0x41, 0x7f, 0xf8, 0xe8, 0x0d, 0x34, 0xe8, 0x87, 0x8f, 0xbf, 0x20, + 0x01, 0x5f, 0xf8, 0x61, 0x74, 0x35, 0x2a, 0x1e, 0x9b, 0xd3, 0xda, 0xf5, 0x71, 0x63, 0x60, 0xe8, 0x23, 0x11, 0x1c, + 0x72, 0x0a, 0x10, 0x5f, 0x24, 0x9d, 0x1d, 0x4d, 0x1c, 0x73, 0x39, 0x24, 0x07, 0xed, 0x38, 0x97, 0x2a, 0x53, 0x0e, + 0x35, 0xa7, 0x8e, 0x72, 0x40, 0x8e, 0xf3, 0xe8, 0x40, 0xb3, 0x6e, 0x2f, 0x27, 0xe3, 0xcb, 0x9c, 0xb4, 0xcb, 0x66, + 0xb3, 0xd7, 0xd4, 0x30, 0x93, 0x88, 0x91, 0x0a, 0xde, 0xe4, 0xac, 0x8a, 0xa0, 0x5f, 0x74, 0x8a, 0xa9, 0x8d, 0x78, + 0xb8, 0xb7, 0x9e, 0x9d, 0x3a, 0x0f, 0x34, 0xb8, 0x32, 0x20, 0xaf, 0x78, 0x0d, 0xb8, 0x51, 0xc8, 0x84, 0x59, 0xc2, + 0x02, 0x2e, 0xcc, 0xdf, 0x7d, 0xe2, 0x3e, 0xd8, 0x2f, 0xa2, 0xe0, 0x3c, 0x7b, 0x6f, 0x06, 0xcb, 0x12, 0x76, 0x41, + 0xf5, 0xc6, 0x7d, 0xee, 0x3d, 0xfe, 0x71, 0xc3, 0x14, 0x14, 0x58, 0x06, 0xf9, 0x74, 0xe7, 0x0b, 0x22, 0xf0, 0x03, + 0xfb, 0xc3, 0x3c, 0xe6, 0xec, 0x1f, 0x9a, 0x53, 0x73, 0x4b, 0x28, 0x1b, 0x48, 0x75, 0x69, 0xcb, 0x82, 0xb3, 0xd3, + 0x61, 0x8b, 0xf3, 0x9e, 0xa3, 0x46, 0xa9, 0xee, 0xa9, 0x83, 0x32, 0x21, 0x5a, 0xe6, 0x14, 0xd8, 0x22, 0x80, 0x96, + 0xad, 0x08, 0xaf, 0x03, 0xe5, 0xa5, 0x66, 0x46, 0x43, 0x7f, 0x88, 0xb3, 0x49, 0xf8, 0x06, 0x74, 0x72, 0xd1, 0xe1, + 0xa2, 0xcb, 0xa5, 0x53, 0x7a, 0x7c, 0x3c, 0x40, 0x34, 0x76, 0xce, 0xc2, 0x60, 0x5e, 0x4f, 0x52, 0xbe, 0xf4, 0xec, + 0xd7, 0xe3, 0xa2, 0xbd, 0x36, 0xfa, 0x70, 0x32, 0x4d, 0x98, 0xd8, 0x80, 0x9a, 0x56, 0xc7, 0x21, 0x1e, 0x3c, 0xa4, + 0x80, 0x1e, 0x94, 0x66, 0x79, 0xdf, 0x04, 0x52, 0x48, 0x45, 0xc8, 0x44, 0x5e, 0x16, 0x7a, 0xb6, 0x0e, 0x06, 0x82, + 0x9a, 0xed, 0x8c, 0x4f, 0x75, 0xd2, 0x68, 0xa9, 0x78, 0x81, 0x98, 0x12, 0x46, 0x48, 0xd3, 0xfa, 0x27, 0xa0, 0x1b, + 0xbe, 0x06, 0x28, 0x7f, 0x52, 0x2e, 0x3b, 0x9e, 0x59, 0xc6, 0x0e, 0xe1, 0x80, 0x9f, 0xaa, 0x02, 0x77, 0x17, 0x15, + 0xfa, 0xc7, 0xf3, 0xd1, 0x90, 0x1c, 0x22, 0x34, 0x0c, 0x95, 0x70, 0x01, 0x91, 0x51, 0xea, 0x63, 0x87, 0xd0, 0xeb, + 0x7e, 0x40, 0xbe, 0xf8, 0x23, 0x9a, 0xf0, 0x88, 0x3b, 0xe5, 0xad, 0xae, 0x5a, 0x68, 0xe2, 0x23, 0xee, 0x82, 0x06, + 0xdf, 0x7c, 0x70, 0x9a, 0xee, 0x1e, 0x55, 0x96, 0x56, 0xe8, 0x13, 0x0d, 0x64, 0x4a, 0xf5, 0xf4, 0x7a, 0xa6, 0x9a, + 0xde, 0x2c, 0xa1, 0x95, 0x40, 0xd9, 0xc6, 0x74, 0x9e, 0xc6, 0x96, 0xed, 0xb5, 0x8b, 0x14, 0xf9, 0xf3, 0x34, 0x62, + 0x0d, 0x5b, 0x02, 0x76, 0xe3, 0x8e, 0xbe, 0xed, 0x64, 0xc7, 0xd0, 0x10, 0x25, 0xbd, 0xa8, 0x38, 0x1d, 0x63, 0xe4, + 0xe6, 0x75, 0x0f, 0xd8, 0x2e, 0xa3, 0xb7, 0xd5, 0xc0, 0x70, 0xee, 0x9b, 0xd4, 0x9c, 0x14, 0x9c, 0xf3, 0xd6, 0xfd, + 0x75, 0x82, 0x34, 0x9e, 0xe7, 0xad, 0x8b, 0xf7, 0x22, 0x9e, 0x69, 0xf3, 0xaf, 0x17, 0xe5, 0xf9, 0xaa, 0xc6, 0x65, + 0xeb, 0xaf, 0x49, 0xb0, 0x85, 0xec, 0x67, 0x15, 0x52, 0xfd, 0x47, 0xc5, 0x8e, 0x78, 0x7b, 0x3e, 0xa7, 0x02, 0x67, + 0xae, 0x3a, 0x3e, 0x2a, 0xbe, 0x41, 0x2f, 0x0e, 0x07, 0x38, 0x07, 0x01, 0xf2, 0xc0, 0x49, 0xa8, 0xc9, 0x3c, 0x60, + 0xcc, 0xa9, 0x56, 0xf3, 0x15, 0xeb, 0x31, 0xeb, 0x0d, 0x33, 0x3c, 0x57, 0xff, 0x03, 0xd4, 0x80, 0x0b, 0xe8, 0x0f, + 0x3b, 0xbc, 0xaf, 0x31, 0x84, 0x46, 0xdc, 0x8d, 0x7c, 0x62, 0xf0, 0xbb, 0xfc, 0x37, 0x83, 0x99, 0x6c, 0x24, 0xc8, + 0xcc, 0x3a, 0xd5, 0x3e, 0x31, 0x59, 0x19, 0x82, 0x7a, 0x2d, 0xed, 0xa6, 0xf4, 0x10, 0x19, 0x8a, 0x70, 0x02, 0x0c, + 0x14, 0xb4, 0x31, 0x81, 0x57, 0x57, 0x68, 0xa6, 0x1b, 0xcc, 0xd5, 0x47, 0x4d, 0x9d, 0x43, 0xdc, 0x2b, 0x2d, 0x95, + 0xc1, 0xa0, 0x36, 0x08, 0xbc, 0x6b, 0xbf, 0xfc, 0xc3, 0x32, 0x9e, 0x27, 0x87, 0xaa, 0x9f, 0x0e, 0x1b, 0xc3, 0x35, + 0x75, 0xac, 0x7a, 0xfd, 0xcf, 0xd4, 0x24, 0xc6, 0xa7, 0x46, 0x82, 0xc1, 0xba, 0x8a, 0x13, 0x2d, 0x88, 0xd3, 0x46, + 0x69, 0x17, 0x8a, 0x3a, 0xd4, 0x02, 0x2e, 0x0d, 0xa9, 0x71, 0xc0, 0x2a, 0x37, 0x2f, 0xcf, 0x0d, 0x74, 0xe2, 0x39, + 0x7f, 0x9d, 0x99, 0xf0, 0xa1, 0x9e, 0xe6, 0x50, 0xd7, 0x26, 0xcf, 0xe5, 0xfd, 0xf8, 0xc5, 0xca, 0x43, 0x22, 0x27, + 0xb1, 0xd0, 0x26, 0x9b, 0xeb, 0x7c, 0xbe, 0xc0, 0x62, 0x23, 0x88, 0xfa, 0x7c, 0x85, 0x0a, 0xa2, 0xc3, 0x61, 0x53, + 0x4c, 0x75, 0xc4, 0x33, 0xc6, 0x44, 0xa5, 0xed, 0x62, 0x33, 0x1c, 0xc0, 0x00, 0x9c, 0x8b, 0xb2, 0x96, 0x8f, 0xdf, + 0xa6, 0xd1, 0x9f, 0xe4, 0xec, 0x4c, 0x4a, 0x19, 0xbf, 0x21, 0xfb, 0x33, 0xbe, 0x3f, 0x62, 0x74, 0xef, 0xdf, 0xc9, + 0x3e, 0xed, 0x5f, 0x33, 0xb6, 0x31, 0xb6, 0x24, 0x6f, 0xcc, 0xec, 0xab, 0xcd, 0xcb, 0xb8, 0x24, 0x0a, 0xc8, 0xfe, + 0x46, 0xe3, 0x61, 0x9a, 0x87, 0x38, 0x3c, 0xac, 0x1a, 0x45, 0x7e, 0x47, 0x41, 0x96, 0x18, 0xe0, 0x6d, 0xa6, 0x45, + 0xba, 0x99, 0xc0, 0xdb, 0xa0, 0x94, 0x74, 0x68, 0x77, 0xa6, 0x2c, 0x31, 0xa8, 0xc2, 0xc0, 0x20, 0x22, 0x77, 0xba, + 0x04, 0xa2, 0xdd, 0x4a, 0x66, 0x4f, 0xf0, 0x3e, 0xa6, 0xa1, 0x13, 0xb7, 0x6c, 0x79, 0x8b, 0x6d, 0x4d, 0xcd, 0xec, + 0xe8, 0x85, 0x9a, 0xa1, 0x30, 0x32, 0x3a, 0x7d, 0xa1, 0xd6, 0x8f, 0x26, 0x64, 0xa9, 0x10, 0xbf, 0x2a, 0xf1, 0x55, + 0xeb, 0x6b, 0xa9, 0x10, 0x57, 0x67, 0x17, 0x39, 0x86, 0x9f, 0x65, 0x88, 0xc7, 0xd8, 0x8e, 0x7b, 0xeb, 0x6b, 0x0f, + 0x27, 0x80, 0x8a, 0xa4, 0x65, 0x48, 0x6e, 0xe5, 0xd8, 0x90, 0x86, 0x96, 0xfe, 0xf0, 0x74, 0x86, 0x99, 0x22, 0x40, + 0x67, 0xcd, 0x13, 0x4f, 0x5d, 0x4c, 0xd5, 0x7f, 0xa7, 0xa0, 0x62, 0xfb, 0x83, 0xca, 0x00, 0x38, 0x49, 0x1d, 0x44, + 0x23, 0xb3, 0xcf, 0x3a, 0x8d, 0x3e, 0xe4, 0xe2, 0x29, 0x38, 0x02, 0x96, 0x53, 0xe4, 0x9a, 0x33, 0x5a, 0xd7, 0x32, + 0xa4, 0x49, 0xb6, 0x6f, 0x97, 0xe3, 0xde, 0x05, 0x77, 0x68, 0xd2, 0x48, 0x68, 0xa9, 0xba, 0x42, 0xae, 0x94, 0xa5, + 0xa3, 0xee, 0xb4, 0x1b, 0x53, 0x6e, 0xac, 0x70, 0x2b, 0x73, 0xd1, 0xb1, 0x8c, 0x55, 0x39, 0xc2, 0x22, 0x5d, 0x1c, + 0x05, 0x96, 0x05, 0xf8, 0x1e, 0x18, 0x44, 0xa5, 0x2a, 0xcb, 0x44, 0x11, 0x92, 0xea, 0x84, 0x05, 0xc6, 0xb2, 0xf9, + 0x7e, 0x13, 0x09, 0x1e, 0x7c, 0xfd, 0x37, 0x8c, 0x24, 0xb1, 0x11, 0x10, 0x40, 0x83, 0x86, 0x16, 0x50, 0xcd, 0xfc, + 0x5e, 0xd9, 0x2d, 0x84, 0xce, 0x93, 0xf8, 0xa0, 0x92, 0x64, 0xd0, 0x9f, 0xff, 0xc7, 0x04, 0x31, 0x68, 0x1d, 0x52, + 0xce, 0x82, 0x03, 0x6e, 0x98, 0x9b, 0x4e, 0xa2, 0xba, 0x6c, 0x51, 0x2c, 0xb6, 0xd8, 0xf3, 0xb9, 0x0d, 0x6a, 0x05, + 0x2b, 0x2f, 0x21, 0xa5, 0x1d, 0xcd, 0x57, 0x5e, 0x87, 0x2a, 0x6f, 0x79, 0x8d, 0x3b, 0x4c, 0xf4, 0x0b, 0x27, 0xba, + 0x26, 0xab, 0xd1, 0xad, 0x23, 0x00, 0x99, 0x8d, 0x03, 0xd5, 0x1b, 0x84, 0x4b, 0x48, 0xd9, 0xe8, 0x2d, 0x73, 0x6e, + 0xf0, 0xdb, 0xf9, 0x9c, 0x90, 0xc4, 0xc8, 0x85, 0x26, 0x80, 0x93, 0x38, 0x25, 0xb4, 0xa9, 0x8b, 0x9c, 0xa9, 0xd3, + 0x13, 0xde, 0x3a, 0x68, 0x6e, 0x6d, 0x36, 0x42, 0xb1, 0x97, 0xf5, 0x49, 0x11, 0x25, 0x55, 0x97, 0x83, 0x72, 0x53, + 0x82, 0x5d, 0xfb, 0x31, 0xde, 0xca, 0x30, 0x64, 0x37, 0x2b, 0x60, 0x24, 0x66, 0x42, 0x72, 0x26, 0x48, 0x92, 0x65, + 0xd2, 0x65, 0x2d, 0xcd, 0xea, 0xda, 0x7f, 0xb4, 0x10, 0x1e, 0x91, 0x8c, 0xf3, 0xb3, 0x3c, 0x94, 0x1d, 0x57, 0xd6, + 0x29, 0xb2, 0x3c, 0x3d, 0x11, 0xae, 0xbb, 0x55, 0x35, 0x35, 0xbc, 0x07, 0x44, 0x64, 0x72, 0xcb, 0x56, 0xf5, 0xb1, + 0x33, 0xc1, 0xcf, 0x5c, 0x1e, 0x88, 0x8b, 0x07, 0x15, 0x49, 0xe8, 0xe7, 0xdb, 0x3c, 0x4f, 0x14, 0x1a, 0xbd, 0x43, + 0xce, 0xad, 0xe4, 0xe2, 0x5c, 0x0b, 0x94, 0x58, 0xf0, 0xe5, 0xf6, 0xa4, 0x3a, 0x47, 0x1e, 0xf8, 0x4e, 0x9c, 0x09, + 0x5d, 0x64, 0x5e, 0xe9, 0x1a, 0x79, 0x2b, 0xbd, 0x57, 0xd5, 0xc8, 0x1f, 0xfc, 0xea, 0x7f, 0x59, 0xe9, 0x35, 0x7a, + 0x11, 0x89, 0x33, 0x5f, 0xe2, 0x12, 0xed, 0x0c, 0xec, 0x30, 0x4e, 0xea, 0x9a, 0xbb, 0x2f, 0x80, 0x56, 0x17, 0xde, + 0x74, 0xb4, 0x16, 0x09, 0x3c, 0xd7, 0xdd, 0x25, 0xae, 0x84, 0x1d, 0x6e, 0xa0, 0xd8, 0xc3, 0x0c, 0x06, 0x42, 0xa3, + 0xc8, 0x86, 0x03, 0xc0, 0xcf, 0x21, 0xfe, 0x1a, 0xf3, 0xa3, 0x6e, 0xd9, 0x46, 0x0b, 0x9c, 0x53, 0x64, 0x06, 0xd9, + 0x8b, 0xc8, 0x80, 0x1c, 0xea, 0x84, 0x2c, 0xc8, 0x35, 0x6a, 0xec, 0x80, 0xb5, 0xc2, 0x0a, 0x65, 0x35, 0xc0, 0xb1, + 0xc1, 0x66, 0xed, 0xa5, 0xb9, 0xa9, 0xc0, 0xa7, 0x4b, 0x44, 0xae, 0xe9, 0x91, 0x50, 0xbe, 0x82, 0x14, 0x54, 0xa4, + 0x9f, 0x57, 0xff, 0x0a, 0x4c, 0x7a, 0x3b, 0x27, 0x68, 0x17, 0x91, 0x71, 0xbf, 0xd0, 0x11, 0x28, 0x2d, 0x62, 0xfb, + 0x87, 0xc9, 0xf1, 0x75, 0x30, 0xa6, 0x6b, 0xe4, 0x73, 0x6b, 0xcd, 0x3f, 0x41, 0xf5, 0x3c, 0x19, 0x0f, 0x14, 0xa9, + 0x30, 0x00, 0xfc, 0xde, 0x08, 0x1a, 0xef, 0xfd, 0xdf, 0x33, 0x1c, 0x67, 0x74, 0x4b, 0x28, 0x3c, 0x02, 0xf2, 0x4d, + 0xfe, 0x17, 0xc3, 0x78, 0x54, 0x00, 0x3b, 0x2b, 0xf2, 0xde, 0xd0, 0xde, 0xad, 0x43, 0xc0, 0xd0, 0x37, 0x60, 0xcc, + 0xfc, 0x0d, 0x47, 0xd9, 0x40, 0x6e, 0xdb, 0x19, 0xae, 0xab, 0x92, 0x66, 0x26, 0x19, 0x1e, 0x49, 0x0c, 0x52, 0x69, + 0xe4, 0x47, 0x5d, 0x59, 0x9c, 0x66, 0xee, 0x2a, 0x38, 0xf2, 0xb3, 0xc7, 0x33, 0x6c, 0xde, 0xd8, 0x88, 0x3b, 0x5e, + 0x80, 0x34, 0x37, 0x34, 0x00, 0xe0, 0x85, 0x4b, 0x45, 0x87, 0x3b, 0xe6, 0x2a, 0x5b, 0x81, 0xfa, 0x69, 0xa2, 0x39, + 0x38, 0xce, 0x46, 0x15, 0xf2, 0x09, 0xb7, 0x1b, 0xf1, 0x79, 0x0e, 0x10, 0x8f, 0x63, 0xa5, 0x32, 0x18, 0x12, 0x05, + 0x3f, 0x11, 0x61, 0x47, 0xd3, 0x89, 0xb3, 0xe4, 0xae, 0x52, 0x7b, 0x0c, 0x50, 0x0d, 0x09, 0x58, 0x65, 0x6c, 0xc3, + 0xfa, 0x45, 0x90, 0xb8, 0xac, 0xef, 0x18, 0x2d, 0xeb, 0xb0, 0x50, 0x0b, 0x1f, 0x39, 0xa7, 0x1f, 0xe2, 0xa0, 0x10, + 0x67, 0x23, 0x9c, 0x67, 0x20, 0x79, 0xda, 0x40, 0x66, 0xe4, 0xc5, 0xf8, 0xbd, 0x74, 0x67, 0xbb, 0x61, 0x65, 0x48, + 0xba, 0xc5, 0x5b, 0x6d, 0x3d, 0x93, 0xfc, 0x88, 0x1c, 0x38, 0x29, 0x02, 0xc9, 0x24, 0x52, 0x41, 0x95, 0xd2, 0x60, + 0xe5, 0xaf, 0x00, 0x28, 0x98, 0x6b, 0x5e, 0xd3, 0x54, 0x4f, 0xcb, 0x84, 0xdd, 0xe6, 0x68, 0xb0, 0x4e, 0x1c, 0xaa, + 0x1f, 0x0c, 0x3a, 0x85, 0x38, 0x43, 0xbb, 0xc0, 0x03, 0x8d, 0x4c, 0xec, 0xf1, 0xe7, 0xf9, 0x49, 0xc1, 0x3b, 0xab, + 0x34, 0x4b, 0xc1, 0x33, 0x95, 0x32, 0x78, 0x0c, 0x56, 0xe7, 0xdf, 0xee, 0x6b, 0xa2, 0xd2, 0x80, 0x00, 0xd0, 0x51, + 0xcc, 0xe1, 0xbc, 0x9b, 0xa2, 0x49, 0x77, 0x6a, 0xb2, 0xff, 0xd6, 0xab, 0xdb, 0x9b, 0x71, 0x94, 0x17, 0xdd, 0x61, + 0x35, 0xf1, 0x71, 0xd2, 0x84, 0xed, 0x8c, 0xad, 0xd4, 0xf5, 0x0b, 0xb0, 0x00, 0x76, 0x99, 0xf1, 0x6c, 0x0c, 0xaf, + 0xeb, 0xc8, 0x4e, 0x17, 0xe4, 0xea, 0xe1, 0xa3, 0x9a, 0xc3, 0x47, 0xdc, 0x72, 0x72, 0xca, 0x11, 0x9c, 0x59, 0x04, + 0xcd, 0x0c, 0xa0, 0x02, 0xf2, 0x12, 0x9a, 0x92, 0x2e, 0x08, 0x7e, 0x6d, 0x90, 0x34, 0x1f, 0x30, 0x06, 0xe0, 0xa3, + 0xbe, 0xd3, 0x9c, 0xbf, 0x19, 0x9c, 0xee, 0x44, 0xbc, 0xb7, 0xa8, 0xe2, 0x97, 0x56, 0xca, 0x90, 0x29, 0x4f, 0x2e, + 0xd9, 0x2a, 0xac, 0x42, 0xd5, 0xda, 0xae, 0x43, 0x09, 0xf1, 0x19, 0xed, 0x0f, 0x2e, 0x28, 0xde, 0xc1, 0x40, 0x7d, + 0xe1, 0x47, 0xde, 0x69, 0xbd, 0x8a, 0x66, 0x2d, 0x6c, 0xbd, 0xf8, 0xbe, 0x6a, 0x5a, 0x03, 0x47, 0x76, 0xb6, 0x57, + 0xfa, 0x67, 0x75, 0x18, 0xad, 0x43, 0x54, 0xfe, 0xac, 0xfe, 0x4a, 0x37, 0x75, 0xcb, 0x9a, 0xc6, 0xaf, 0x23, 0xf1, + 0x9b, 0x24, 0x4c, 0xea, 0xb5, 0x5b, 0xd3, 0xe3, 0xf4, 0x38, 0xd1, 0x38, 0x75, 0x72, 0xf7, 0xfc, 0xd7, 0x68, 0x75, + 0xd4, 0xa0, 0xed, 0x64, 0xda, 0xa6, 0xdf, 0x35, 0x96, 0x28, 0x4d, 0xaa, 0xa7, 0xb1, 0x73, 0x6d, 0x17, 0x2f, 0x16, + 0x1d, 0x12, 0xdd, 0x9f, 0x75, 0x5f, 0x91, 0xb9, 0x16, 0x26, 0x7e, 0x66, 0x52, 0x43, 0x5c, 0x6b, 0x35, 0xf1, 0xce, + 0x5e, 0x6c, 0x4b, 0x8e, 0xdd, 0x74, 0x95, 0x64, 0x30, 0xa8, 0x8e, 0x4c, 0x0d, 0x89, 0x64, 0x88, 0xa8, 0x5f, 0x3e, + 0x08, 0x98, 0x75, 0x8d, 0x77, 0xcf, 0xd7, 0xa4, 0x71, 0xfa, 0xd6, 0x63, 0xae, 0x3f, 0x2f, 0xc3, 0xed, 0x7b, 0x04, + 0xce, 0xb6, 0x29, 0xfd, 0xe8, 0x8d, 0x52, 0xa7, 0x8d, 0x92, 0x58, 0x4e, 0xd3, 0x13, 0x28, 0xff, 0x80, 0x48, 0x22, + 0xfc, 0xa4, 0x29, 0x3b, 0x49, 0x25, 0xd3, 0x6f, 0xd4, 0xdd, 0x7e, 0xaf, 0x84, 0x40, 0x7a, 0xfb, 0x47, 0x1d, 0x55, + 0xd3, 0xcb, 0x44, 0x12, 0xab, 0x0e, 0xc4, 0x6b, 0x0a, 0x43, 0xee, 0xf3, 0x2f, 0xb6, 0x77, 0xca, 0x28, 0x14, 0x51, + 0xd6, 0x92, 0xde, 0x01, 0x4c, 0x43, 0x0d, 0x23, 0xa3, 0x68, 0xd8, 0x26, 0xe5, 0xef, 0xf1, 0xc7, 0xd9, 0x30, 0xa0, + 0x4d, 0x47, 0xa5, 0x0d, 0x5d, 0xb0, 0xaa, 0xde, 0xc2, 0xef, 0xd3, 0x53, 0x5f, 0xb0, 0xe6, 0x15, 0xf6, 0x4e, 0xdf, + 0xde, 0xe6, 0xcf, 0xe7, 0xfc, 0xfc, 0xf9, 0xac, 0x37, 0xbc, 0x61, 0x66, 0x65, 0xdc, 0xab, 0xe0, 0xe5, 0x82, 0xae, + 0x71, 0x28, 0xc1, 0x53, 0x5b, 0xfe, 0xa3, 0x13, 0x30, 0xe5, 0x01, 0xce, 0x68, 0x03, 0x7d, 0x2a, 0x03, 0xa7, 0x9b, + 0x1b, 0x66, 0x34, 0x5d, 0x99, 0x19, 0x69, 0x66, 0x3c, 0x29, 0xa2, 0xcf, 0x49, 0xcc, 0xc1, 0x1e, 0xc9, 0x59, 0xfa, + 0x58, 0xcc, 0xf8, 0x51, 0x69, 0x0b, 0xda, 0x0e, 0x85, 0x9f, 0x82, 0x4c, 0x05, 0xe8, 0x45, 0xe7, 0xdb, 0x38, 0x8d, + 0xb3, 0xf4, 0x77, 0x0e, 0xe9, 0x48, 0x4f, 0x4f, 0x44, 0xf6, 0xa0, 0xbb, 0xee, 0xbd, 0x17, 0xf0, 0x4b, 0x42, 0x53, + 0x32, 0x7e, 0x27, 0x06, 0xed, 0x8b, 0xf4, 0x51, 0x8d, 0xc0, 0xa9, 0x00, 0x79, 0x35, 0xc2, 0x38, 0x90, 0x37, 0xb4, + 0xd7, 0xc8, 0x0f, 0x4a, 0x95, 0xee, 0xb9, 0xa7, 0x25, 0xad, 0xc8, 0x42, 0xa6, 0x9f, 0x8c, 0x31, 0x66, 0x55, 0xe4, + 0xd8, 0xd2, 0xbc, 0x6f, 0x90, 0x49, 0xbe, 0x70, 0x91, 0xd1, 0x62, 0x4e, 0x8d, 0x05, 0xba, 0x55, 0xa8, 0xb5, 0x0b, + 0xaf, 0x7f, 0xa1, 0x72, 0xa0, 0xa9, 0x28, 0xfb, 0x7e, 0x88, 0x2d, 0xe2, 0x03, 0xfd, 0x8a, 0x8f, 0x90, 0x71, 0xdb, + 0x73, 0x9c, 0x10, 0x52, 0xf5, 0xae, 0x28, 0xee, 0x6d, 0x93, 0x0a, 0xc9, 0x0d, 0x55, 0x0c, 0x65, 0xd4, 0xc2, 0xf9, + 0x19, 0x9c, 0x2f, 0x9c, 0x9f, 0xe6, 0xdc, 0xa0, 0x2d, 0x99, 0xaa, 0x67, 0x24, 0x96, 0xae, 0xb0, 0xa3, 0x96, 0xdf, + 0xe4, 0x27, 0xec, 0x42, 0x06, 0x68, 0x6a, 0xa5, 0x57, 0x45, 0x82, 0x2e, 0x83, 0x0d, 0xa8, 0x51, 0x1d, 0x88, 0xbc, + 0xc4, 0x37, 0x13, 0x10, 0x80, 0xd1, 0x83, 0x4f, 0xaa, 0x29, 0x9d, 0x36, 0x7c, 0xb7, 0xcb, 0x31, 0x81, 0xa2, 0x6b, + 0x36, 0x98, 0x84, 0xbc, 0x29, 0xb8, 0xa6, 0x9a, 0x3d, 0x15, 0xc2, 0x18, 0xbc, 0x3c, 0x35, 0xb6, 0x58, 0xbd, 0x7f, + 0x2b, 0xd6, 0x57, 0x86, 0x90, 0xd8, 0x72, 0xc8, 0xbe, 0xd0, 0xbc, 0xd2, 0x83, 0x68, 0x9a, 0xe6, 0xe4, 0xd2, 0x43, + 0x5f, 0xc8, 0xeb, 0xd1, 0xd9, 0x27, 0xc8, 0xeb, 0xdb, 0x6c, 0x5b, 0x73, 0x13, 0x36, 0xf1, 0x25, 0x7d, 0xa6, 0xfb, + 0xe7, 0x6a, 0x21, 0x7b, 0x56, 0xea, 0xbc, 0x73, 0x25, 0x76, 0x4d, 0xa7, 0x88, 0x1a, 0x83, 0x4e, 0xc1, 0xdb, 0x0e, + 0x11, 0xb4, 0x05, 0x27, 0x49, 0x86, 0x48, 0x54, 0x06, 0xea, 0xb3, 0xa9, 0x48, 0x82, 0xd9, 0x00, 0x4b, 0x25, 0xaf, + 0xb9, 0xd8, 0x35, 0xbf, 0x64, 0x4d, 0x32, 0xab, 0x80, 0x8b, 0xe4, 0x99, 0x4e, 0x4e, 0xd7, 0x91, 0xd5, 0x1e, 0xa6, + 0xc6, 0x5d, 0x2c, 0x5e, 0x25, 0x5c, 0xce, 0xca, 0x4d, 0xac, 0xc4, 0x9b, 0x40, 0xcd, 0x78, 0x4f, 0x2a, 0x7f, 0x6c, + 0xb2, 0xa3, 0x36, 0x52, 0x02, 0x6d, 0x0f, 0xa9, 0xb6, 0x36, 0x8d, 0x70, 0x1b, 0xd2, 0x6f, 0x57, 0xb7, 0x2d, 0x50, + 0xe9, 0xb7, 0xb4, 0x30, 0xa4, 0xff, 0x1b, 0x15, 0xaa, 0x46, 0x85, 0x11, 0xc2, 0xfd, 0x24, 0x40, 0xb8, 0x2f, 0x9c, + 0xbc, 0x20, 0x16, 0xd5, 0x79, 0x14, 0xf6, 0x5e, 0x67, 0xcd, 0xd5, 0xb8, 0xf8, 0xfb, 0xa0, 0xfe, 0x3e, 0x0a, 0x8d, + 0x63, 0xbd, 0xc6, 0xef, 0x8c, 0x1f, 0x7f, 0x64, 0xdf, 0xd0, 0xc0, 0x08, 0x37, 0x11, 0xb4, 0x12, 0x34, 0xdb, 0x12, + 0xd6, 0xb6, 0x2a, 0xa0, 0x08, 0x61, 0x36, 0x52, 0xd5, 0x82, 0x09, 0x6d, 0xa5, 0x27, 0x58, 0xbc, 0xeb, 0x38, 0xfd, + 0x6f, 0x68, 0xbd, 0x4e, 0x08, 0x29, 0x58, 0x93, 0x23, 0x4f, 0x9e, 0x44, 0xab, 0x7d, 0xe6, 0xdf, 0x18, 0xb7, 0xbe, + 0xfa, 0x8c, 0x57, 0x23, 0x75, 0xa4, 0x98, 0x41, 0xe1, 0xb5, 0x9b, 0xd3, 0x9b, 0xf1, 0x39, 0xc9, 0x7b, 0xd1, 0x3c, + 0xda, 0xa9, 0xa0, 0x54, 0x53, 0xd7, 0xac, 0xce, 0xb5, 0x79, 0x9d, 0xd1, 0xb9, 0xc6, 0xde, 0x58, 0xd6, 0xd3, 0x35, + 0xce, 0xf8, 0x8d, 0xb6, 0x62, 0xa0, 0xd4, 0xf1, 0xb0, 0xd1, 0x73, 0xac, 0x40, 0x06, 0xe8, 0x85, 0xe3, 0x26, 0x82, + 0xf4, 0x97, 0xc0, 0xa1, 0x53, 0x1b, 0x2e, 0xb0, 0xd6, 0x72, 0xc4, 0x90, 0x67, 0x58, 0x62, 0x4a, 0xbf, 0x71, 0x1d, + 0x48, 0xbb, 0xf5, 0x9b, 0x05, 0x8f, 0x82, 0xaf, 0xec, 0xe9, 0x30, 0x8f, 0x68, 0x9c, 0x5b, 0x04, 0x2f, 0x12, 0xe5, + 0x61, 0xbb, 0xf0, 0x9c, 0x5f, 0x89, 0x74, 0x50, 0x90, 0x65, 0x3c, 0x9f, 0x79, 0x01, 0x42, 0x48, 0x77, 0x2d, 0xa1, + 0xed, 0x73, 0xc1, 0x9e, 0x18, 0xd7, 0x8e, 0x49, 0x52, 0x53, 0x82, 0xfd, 0xdf, 0x36, 0x5d, 0x96, 0x56, 0xe7, 0x2f, + 0xef, 0x2b, 0x66, 0x62, 0x3b, 0xae, 0xce, 0x52, 0x21, 0x7b, 0xef, 0x57, 0x91, 0x78, 0x8c, 0xcc, 0x1f, 0xdb, 0x20, + 0x7e, 0xef, 0x9c, 0x72, 0xfc, 0x5f, 0xd8, 0x6f, 0x7a, 0xf4, 0xca, 0xc9, 0x6c, 0x23, 0x01, 0x93, 0x23, 0xf7, 0xaa, + 0xbe, 0x1f, 0x01, 0x7b, 0xc3, 0x03, 0x81, 0xb2, 0x8a, 0xfe, 0x83, 0x7a, 0xd3, 0x00, 0x60, 0x0a, 0xc3, 0x6d, 0xb8, + 0xe7, 0x8f, 0xc6, 0x6f, 0x75, 0xc0, 0xe5, 0x8a, 0xe5, 0xbf, 0x81, 0xc1, 0xf5, 0x3a, 0x22, 0xd8, 0x6f, 0x9d, 0xf5, + 0x40, 0xd0, 0x9d, 0xc7, 0x9c, 0x62, 0x10, 0xd7, 0x92, 0x2f, 0x58, 0xaf, 0x22, 0xf3, 0x18, 0xc5, 0xe6, 0x17, 0x6b, + 0x2b, 0xf8, 0x2a, 0x93, 0xfa, 0x45, 0x1e, 0xfc, 0x17, 0xa4, 0x76, 0x08, 0x87, 0xe7, 0x89, 0x45, 0xfe, 0x4d, 0xe2, + 0x70, 0x84, 0x05, 0xb6, 0x62, 0xa5, 0xa1, 0x39, 0x33, 0x7e, 0x4c, 0xc9, 0xa1, 0x4d, 0x30, 0x0e, 0x45, 0xce, 0xd6, + 0x1c, 0x2c, 0x47, 0xa9, 0x66, 0x9e, 0x7f, 0x6f, 0xf0, 0x41, 0x98, 0xb4, 0xb4, 0xf2, 0x7c, 0x80, 0xf6, 0x31, 0xfa, + 0xf3, 0x7f, 0x16, 0x87, 0x0d, 0xc3, 0xb2, 0xf7, 0x6e, 0xe2, 0x27, 0x1b, 0x38, 0xaa, 0x79, 0x52, 0xc2, 0xd5, 0x5b, + 0xab, 0xaf, 0xda, 0x96, 0x1e, 0x3f, 0x09, 0x85, 0xc6, 0x30, 0x46, 0x0b, 0x83, 0x81, 0x3b, 0x17, 0xfb, 0x39, 0x98, + 0xb9, 0x61, 0x1b, 0x7d, 0x23, 0xe1, 0x4b, 0x3e, 0x7f, 0x07, 0xea, 0x10, 0xa3, 0xa6, 0x4b, 0x23, 0x2a, 0xfd, 0x0e, + 0x45, 0xb7, 0x06, 0x14, 0x68, 0x9e, 0xf9, 0x1c, 0x0a, 0xa7, 0xa3, 0x48, 0x24, 0x39, 0xc0, 0xda, 0x99, 0x7e, 0xd6, + 0xb2, 0xc7, 0xef, 0xb3, 0xa5, 0xc3, 0xf0, 0xba, 0xb6, 0x3d, 0x1e, 0x73, 0xe5, 0x56, 0x56, 0x1d, 0x17, 0x50, 0x5f, + 0x96, 0x6d, 0x36, 0xf6, 0x8e, 0x50, 0x67, 0xab, 0x87, 0x22, 0x72, 0x86, 0x78, 0x90, 0x58, 0xdd, 0xa0, 0x8f, 0x54, + 0xb0, 0xce, 0x67, 0x1b, 0x34, 0xf9, 0x56, 0xd1, 0x8b, 0xab, 0x85, 0xcd, 0x69, 0x48, 0x88, 0x69, 0xc4, 0x70, 0xf0, + 0x49, 0x84, 0xce, 0xa4, 0x7d, 0xdc, 0x50, 0x9d, 0x38, 0x43, 0xd2, 0x70, 0x1d, 0x71, 0x5a, 0x55, 0xc2, 0xac, 0xb2, + 0x85, 0xc5, 0x53, 0xda, 0xe1, 0xea, 0xae, 0x70, 0x3b, 0x67, 0xc2, 0x51, 0xcb, 0x35, 0xb4, 0x4d, 0x44, 0x0a, 0xd9, + 0x61, 0xcb, 0x35, 0xfa, 0xea, 0xb0, 0x62, 0x85, 0x8c, 0xb7, 0xf3, 0xe2, 0x55, 0xcc, 0x38, 0x6c, 0x09, 0x4b, 0x71, + 0x80, 0x81, 0x0f, 0x6d, 0xe5, 0x7d, 0xd5, 0xc9, 0xa9, 0x70, 0x4e, 0x79, 0x97, 0x52, 0x82, 0x2d, 0x63, 0xff, 0xdc, + 0xd5, 0xab, 0xf3, 0xcb, 0xb9, 0xab, 0xce, 0x78, 0x73, 0x61, 0xea, 0xb4, 0xbe, 0x84, 0xae, 0xed, 0x10, 0x51, 0xe5, + 0x3e, 0x57, 0xd3, 0x71, 0x6f, 0xb1, 0x86, 0x9e, 0x74, 0x8e, 0x89, 0xfe, 0xbf, 0x42, 0x94, 0x8f, 0x08, 0x9d, 0xdc, + 0xdd, 0x29, 0x5f, 0x95, 0x3c, 0x55, 0x49, 0xec, 0x63, 0xb5, 0x0d, 0x23, 0x83, 0x56, 0xda, 0x89, 0x6a, 0xdf, 0x5e, + 0xee, 0x09, 0x62, 0xc8, 0x5b, 0x62, 0x59, 0xb8, 0x5d, 0x5e, 0x96, 0xdc, 0x21, 0xce, 0xed, 0x64, 0x68, 0xa7, 0x63, + 0x34, 0x42, 0x3f, 0xb4, 0xa5, 0x98, 0x04, 0x44, 0x52, 0xfb, 0x09, 0xe9, 0x1c, 0xfe, 0x2e, 0x7b, 0x7f, 0x16, 0xef, + 0x09, 0x61, 0x3e, 0x7a, 0xd1, 0x31, 0xa8, 0x4b, 0xa8, 0x73, 0xbc, 0xce, 0xab, 0x06, 0x4c, 0x12, 0x4d, 0xaf, 0xad, + 0x38, 0xd5, 0x39, 0xf5, 0xb6, 0x08, 0xc5, 0x2e, 0xfd, 0xa2, 0x25, 0xb9, 0xd9, 0x2c, 0x33, 0x66, 0x0c, 0x02, 0x75, + 0xa8, 0xe8, 0x66, 0x80, 0x62, 0x4c, 0x89, 0xb0, 0xd3, 0xf9, 0x87, 0x4c, 0xaa, 0x29, 0x2d, 0xaa, 0x76, 0xf4, 0xfb, + 0xc6, 0x60, 0x87, 0x47, 0xd3, 0x97, 0x3f, 0xbf, 0x3d, 0xd2, 0x83, 0x2a, 0xe8, 0x10, 0x3e, 0xee, 0xee, 0x8e, 0xa1, + 0x50, 0x80, 0xac, 0x6c, 0x5f, 0xcc, 0x00, 0x6a, 0x4c, 0x45, 0x48, 0x77, 0x6d, 0xdd, 0x5f, 0x4a, 0x72, 0x5b, 0x53, + 0xe5, 0xfb, 0x40, 0x83, 0xef, 0x0d, 0xb5, 0xd3, 0x1d, 0x3e, 0x87, 0xd9, 0x88, 0xa7, 0x40, 0xc7, 0xc2, 0xe0, 0x6f, + 0x48, 0x71, 0x13, 0x06, 0x19, 0xaa, 0x64, 0x9a, 0x3d, 0xa5, 0x2d, 0xab, 0xe6, 0x5a, 0x4a, 0x3a, 0xc7, 0x84, 0xbd, + 0x2a, 0xfc, 0x91, 0xf7, 0x24, 0xb5, 0xa5, 0x1a, 0x0c, 0x70, 0x82, 0xd2, 0x86, 0xe5, 0x58, 0xc5, 0x8d, 0x7c, 0xa7, + 0xf0, 0x22, 0x02, 0x3d, 0x1d, 0xdc, 0xdb, 0xf9, 0xfd, 0xde, 0x18, 0x21, 0x48, 0x05, 0xdf, 0x4a, 0xa9, 0xc9, 0x1a, + 0x9e, 0xfb, 0x47, 0xaf, 0x6c, 0x87, 0x47, 0xba, 0x9b, 0x24, 0x6a, 0x8b, 0x4e, 0x54, 0x80, 0x15, 0x88, 0xa6, 0x80, + 0x0b, 0xd5, 0x31, 0xa6, 0x71, 0xe7, 0x77, 0x3f, 0xb1, 0xd6, 0xdd, 0xea, 0xf5, 0xac, 0x97, 0x4e, 0x1e, 0x93, 0x05, + 0x6a, 0x3c, 0x8a, 0x7d, 0x79, 0x15, 0xbe, 0x5b, 0xf6, 0x9b, 0x95, 0x2d, 0xc8, 0x0c, 0x02, 0xf4, 0x9b, 0xb5, 0x39, + 0x13, 0xbd, 0x46, 0xb8, 0x93, 0x4a, 0xf3, 0xbc, 0x92, 0x33, 0x95, 0x5f, 0x5f, 0x39, 0x8b, 0x21, 0x59, 0xed, 0xac, + 0xdd, 0xa8, 0x48, 0x8f, 0xad, 0x41, 0xd6, 0xaf, 0x99, 0x64, 0xa9, 0xff, 0x35, 0x7c, 0xd4, 0x37, 0xaf, 0xd7, 0x60, + 0xda, 0x76, 0xb5, 0xd3, 0xcb, 0x53, 0x8e, 0x8a, 0x39, 0x2f, 0x7e, 0x61, 0x8d, 0x2d, 0x3c, 0x1e, 0x6c, 0xf4, 0x84, + 0xc9, 0x54, 0xb2, 0x7a, 0x56, 0xc9, 0xca, 0x59, 0xe2, 0x72, 0xb3, 0x17, 0x5d, 0x40, 0xc7, 0x1f, 0x0e, 0x5a, 0x95, + 0x3f, 0x6c, 0xcc, 0xaa, 0x7c, 0xd8, 0x49, 0xd5, 0xfa, 0x24, 0x91, 0xd9, 0x33, 0x6b, 0xe4, 0x61, 0x61, 0xad, 0x98, + 0x4c, 0xf2, 0x7d, 0x42, 0xae, 0xd0, 0x0c, 0xab, 0x6a, 0xd5, 0xe1, 0xc9, 0x0d, 0x37, 0xb8, 0x58, 0xf8, 0xb9, 0x19, + 0xd7, 0x7f, 0x46, 0xdc, 0x59, 0x0e, 0x3a, 0x0b, 0xad, 0xbf, 0xbd, 0x0e, 0x75, 0x3f, 0x82, 0x2f, 0x4d, 0x70, 0x65, + 0xfa, 0x16, 0x5c, 0xfd, 0x4a, 0x92, 0xd9, 0x16, 0x78, 0xad, 0x00, 0xb9, 0xd8, 0x1b, 0x1b, 0xb1, 0xd6, 0x92, 0x44, + 0x63, 0x43, 0x90, 0x3a, 0x8b, 0xb4, 0x1b, 0x52, 0x3b, 0x9a, 0xed, 0xb4, 0x8e, 0xe6, 0x27, 0xfc, 0x8d, 0x3f, 0x55, + 0x43, 0x15, 0xe6, 0x5b, 0x85, 0xea, 0x15, 0x0f, 0x4e, 0x5b, 0x6f, 0x35, 0x8b, 0xf3, 0x4d, 0xb0, 0xd2, 0x8a, 0xa8, + 0x08, 0x8d, 0xc1, 0x17, 0x19, 0x1c, 0xc4, 0xfd, 0x8a, 0xb5, 0x82, 0x74, 0x53, 0xd6, 0xed, 0x7f, 0x0d, 0xb5, 0xd2, + 0xee, 0x40, 0xec, 0x1b, 0x74, 0x81, 0x95, 0xb5, 0x02, 0xb9, 0x87, 0xf5, 0xfe, 0x82, 0xd2, 0x0a, 0x71, 0xe1, 0xcc, + 0x11, 0x35, 0x61, 0xad, 0xf7, 0x88, 0xb7, 0xc8, 0xfa, 0xcb, 0x3f, 0xd3, 0x8b, 0x26, 0xce, 0xe2, 0x61, 0x19, 0xe7, + 0x0e, 0xd9, 0x91, 0xcb, 0x2c, 0x9f, 0xae, 0xbc, 0xd5, 0x22, 0x82, 0x86, 0x3c, 0x99, 0xf6, 0xf8, 0x14, 0x4e, 0x9b, + 0x35, 0x9c, 0x9e, 0xc8, 0xa7, 0xd6, 0x5a, 0xd3, 0xc9, 0xaa, 0xe1, 0x1f, 0x70, 0xc1, 0x05, 0x86, 0x1d, 0x0c, 0x4e, + 0xaf, 0x9c, 0xaf, 0xba, 0xa0, 0x49, 0x4f, 0x58, 0x70, 0x06, 0xcd, 0x6d, 0xc0, 0x93, 0x0f, 0xe9, 0x29, 0x75, 0x77, + 0x76, 0x9b, 0xd7, 0x40, 0x6e, 0x13, 0x7d, 0x6a, 0x31, 0xcf, 0x0a, 0x5b, 0x70, 0xa6, 0xce, 0x6e, 0x63, 0x7a, 0xae, + 0xae, 0xdb, 0x56, 0x82, 0xa4, 0x4d, 0x9e, 0xcf, 0x06, 0xd7, 0x8c, 0x14, 0x86, 0xc1, 0xff, 0x97, 0x90, 0x92, 0xb7, + 0xa2, 0x20, 0x98, 0x3a, 0x27, 0x7d, 0xad, 0x17, 0x57, 0xb8, 0x11, 0xb1, 0xcc, 0xaa, 0x23, 0xa8, 0x52, 0xf6, 0x04, + 0x5d, 0xfa, 0xdc, 0xc1, 0x25, 0x27, 0x62, 0xbb, 0x67, 0xa5, 0x33, 0x29, 0xa1, 0xfd, 0x79, 0xc1, 0xbb, 0x6b, 0xbc, + 0x72, 0x47, 0xf6, 0xc7, 0xca, 0x3d, 0xe3, 0x1d, 0xb8, 0x7a, 0xf6, 0xe7, 0x38, 0x6b, 0xe1, 0xa0, 0xcb, 0x30, 0x8f, + 0x27, 0x3d, 0x3c, 0xcb, 0x3f, 0xe1, 0x59, 0x39, 0xcf, 0x18, 0x82, 0xd6, 0x61, 0x85, 0x6f, 0xbe, 0x06, 0x28, 0xef, + 0x64, 0xf8, 0xf8, 0x58, 0xfc, 0xd6, 0xd8, 0x8b, 0x4e, 0xca, 0x21, 0x9a, 0xa9, 0x1d, 0x34, 0xcf, 0x5b, 0x30, 0xe4, + 0xa9, 0xdd, 0x20, 0x90, 0x46, 0xeb, 0x3c, 0x57, 0x3f, 0xc5, 0x41, 0x35, 0x7f, 0x9b, 0x79, 0x09, 0x73, 0x5b, 0xa1, + 0x88, 0xfc, 0x33, 0x21, 0x9a, 0xfd, 0x48, 0xa5, 0x81, 0x3a, 0xf9, 0x55, 0x4b, 0xf2, 0x95, 0xb7, 0x23, 0x06, 0x9d, + 0xb9, 0x09, 0xbb, 0xd8, 0x08, 0xf3, 0xd3, 0x98, 0x7c, 0xa6, 0x3a, 0x9b, 0xc9, 0x32, 0xcb, 0x6a, 0x1f, 0x13, 0x0f, + 0x8f, 0xd6, 0x4b, 0xaa, 0x5b, 0x14, 0x6a, 0xb3, 0x3c, 0x5f, 0x94, 0x59, 0xa9, 0x7d, 0x4e, 0xbd, 0x10, 0x47, 0x93, + 0xf5, 0xc2, 0xe3, 0x5e, 0x62, 0x46, 0x26, 0xd5, 0xbc, 0xcc, 0x1c, 0x22, 0x0f, 0xcf, 0x1f, 0x7c, 0xcb, 0x2e, 0x79, + 0xa2, 0xa0, 0xb4, 0x1d, 0x32, 0x0f, 0xdc, 0x37, 0x98, 0xae, 0x9c, 0x7a, 0xcc, 0xd3, 0x15, 0x70, 0x6b, 0xc0, 0x6c, + 0x69, 0x14, 0x47, 0x56, 0x59, 0x85, 0xac, 0xeb, 0xf5, 0xba, 0xf2, 0xb9, 0x65, 0x9a, 0x09, 0x37, 0xf6, 0x14, 0x64, + 0x9a, 0xae, 0x4a, 0xd7, 0xd2, 0x67, 0xfe, 0xcd, 0x9c, 0x67, 0x1f, 0xf0, 0xd3, 0x4f, 0xc1, 0x2d, 0xfa, 0xcb, 0xa9, + 0x6b, 0x5c, 0xf9, 0x36, 0xa3, 0x51, 0xe3, 0x14, 0x8d, 0x37, 0x48, 0x4c, 0x54, 0x54, 0x85, 0xd5, 0x98, 0xf2, 0x73, + 0xec, 0xdd, 0x48, 0x4e, 0xa6, 0x43, 0x3e, 0xd7, 0x76, 0x3f, 0xb3, 0x66, 0xf5, 0x19, 0x75, 0x68, 0x95, 0xd5, 0x71, + 0xc4, 0x97, 0xce, 0x6e, 0x57, 0x06, 0xa1, 0x00, 0x04, 0xd8, 0xc3, 0xe4, 0x73, 0xca, 0x5a, 0x4d, 0xfe, 0xfc, 0xfb, + 0xfb, 0x47, 0x15, 0x9c, 0x62, 0x95, 0xf7, 0xdd, 0xd8, 0x04, 0x8b, 0x64, 0x46, 0x18, 0x59, 0x23, 0xbb, 0x39, 0x46, + 0x92, 0x22, 0x44, 0xe3, 0x1e, 0x4b, 0x11, 0x7a, 0xab, 0xfb, 0x01, 0xe0, 0x1c, 0x79, 0x52, 0x9c, 0x26, 0x47, 0xa7, + 0xc8, 0xa6, 0xd9, 0x56, 0x6c, 0x91, 0x85, 0x03, 0x7c, 0x2d, 0x6a, 0x25, 0xdb, 0xc6, 0x58, 0x41, 0x83, 0x62, 0x0e, + 0x64, 0x3a, 0xf3, 0x01, 0x5f, 0x31, 0xe2, 0x9c, 0x3f, 0x4c, 0x1b, 0x93, 0x27, 0xd3, 0x5e, 0x5f, 0x25, 0xcc, 0x6c, + 0xb7, 0x5e, 0x30, 0x9c, 0xd3, 0x0c, 0x0c, 0xc8, 0xc7, 0x15, 0xaa, 0xf9, 0x13, 0x2c, 0x51, 0xf0, 0xb7, 0x36, 0xb2, + 0xf3, 0xe7, 0xa4, 0x36, 0x62, 0xc8, 0x98, 0x68, 0x6c, 0x2f, 0x8c, 0x94, 0x82, 0x17, 0x35, 0x74, 0x46, 0x58, 0x04, + 0x1f, 0xec, 0x9e, 0xc2, 0xf5, 0x59, 0xd9, 0xeb, 0x74, 0x12, 0x3d, 0x30, 0x4f, 0x94, 0xe0, 0xd2, 0x7c, 0x5f, 0xdb, + 0x20, 0xa0, 0x3e, 0x6f, 0x79, 0x26, 0x07, 0x24, 0x25, 0x26, 0xb0, 0xf0, 0xb8, 0x29, 0x5f, 0xe3, 0xd4, 0x5b, 0xef, + 0xb2, 0x1a, 0x75, 0xc5, 0x25, 0x8d, 0x36, 0xce, 0x18, 0x34, 0x18, 0x1d, 0x11, 0x89, 0xe7, 0x42, 0x30, 0x46, 0xc3, + 0xdf, 0x7a, 0x24, 0x69, 0x08, 0xce, 0x63, 0x4f, 0x10, 0x37, 0x39, 0x99, 0xde, 0x40, 0x88, 0xb2, 0x6d, 0xb9, 0xf9, + 0x79, 0x5f, 0xa0, 0xd1, 0x9c, 0x8f, 0x4d, 0xcc, 0x9c, 0xf7, 0x00, 0x65, 0x26, 0x5a, 0x04, 0xe4, 0xd0, 0xe3, 0x1e, + 0xe2, 0x2a, 0x3d, 0x58, 0xec, 0x25, 0x2e, 0xd3, 0x31, 0x10, 0x5f, 0xaf, 0x95, 0x82, 0x34, 0x3b, 0x8b, 0x14, 0x78, + 0x31, 0xdf, 0xfc, 0xc9, 0x95, 0x62, 0x95, 0x7c, 0xd3, 0x60, 0x72, 0xfe, 0xe4, 0xc7, 0xe6, 0x97, 0xe0, 0xe5, 0x5b, + 0x2d, 0xb5, 0xc8, 0x7d, 0xe0, 0x9d, 0xaf, 0x49, 0x41, 0xbb, 0xff, 0xd9, 0x92, 0x91, 0xf7, 0x31, 0xad, 0x96, 0xc5, + 0x5b, 0xed, 0xa2, 0x5b, 0x14, 0xf2, 0x26, 0x0f, 0xf7, 0xb0, 0x08, 0xa9, 0xb5, 0x96, 0x61, 0x56, 0xdb, 0xa3, 0xdc, + 0xd8, 0x7b, 0xbd, 0x16, 0xa4, 0x45, 0xcc, 0x2e, 0x51, 0xe5, 0xc6, 0x0b, 0x4c, 0xd6, 0x9f, 0x5c, 0x08, 0x96, 0xf9, + 0x05, 0x55, 0x69, 0xef, 0xb2, 0x8e, 0xa7, 0x6c, 0x66, 0xad, 0x8b, 0x9a, 0x4d, 0x01, 0xa7, 0x28, 0x2b, 0x55, 0xdc, + 0xc8, 0xe0, 0xbb, 0x46, 0xa0, 0x35, 0xf0, 0x13, 0x18, 0xa5, 0xc8, 0x6a, 0xaa, 0x8d, 0xa4, 0xff, 0xce, 0xe4, 0xdf, + 0x39, 0xe6, 0xbf, 0x41, 0xe6, 0xdf, 0x87, 0x56, 0x7e, 0xdf, 0x18, 0x6b, 0x02, 0x5c, 0xe1, 0xa4, 0x10, 0x5f, 0xa9, + 0x9c, 0x25, 0x80, 0x1a, 0x4d, 0x99, 0xec, 0xc6, 0x0b, 0x81, 0x15, 0x91, 0xe7, 0x36, 0x4e, 0xb3, 0xb4, 0x47, 0xb6, + 0xe8, 0xfe, 0xce, 0x0b, 0x70, 0x42, 0x2e, 0x0a, 0xee, 0x88, 0xed, 0xab, 0x31, 0xe7, 0x50, 0xc4, 0xd9, 0xe4, 0xa2, + 0x00, 0x31, 0x82, 0x01, 0x21, 0x1b, 0x49, 0xa0, 0xa3, 0xa4, 0x99, 0x68, 0xc4, 0x14, 0x80, 0x06, 0xd8, 0xdd, 0x03, + 0x04, 0x16, 0xc1, 0x0c, 0x13, 0x04, 0x23, 0x79, 0x25, 0xc0, 0x72, 0x4c, 0xf6, 0x8e, 0x55, 0xb0, 0xb0, 0x52, 0x07, + 0x3b, 0xd0, 0x20, 0x4e, 0x60, 0x8a, 0x66, 0x79, 0x24, 0x28, 0xaa, 0x60, 0x11, 0x25, 0xcb, 0x36, 0x17, 0x2f, 0x32, + 0xb7, 0xf5, 0x2a, 0x49, 0xa1, 0x8b, 0xa7, 0x4f, 0x33, 0x4b, 0x28, 0xfd, 0x03, 0xf0, 0xaf, 0x41, 0x1d, 0xd8, 0xb3, + 0x0e, 0xa0, 0x63, 0x2b, 0x4e, 0x4e, 0xa5, 0xca, 0x9f, 0x5d, 0x03, 0x40, 0x49, 0x4f, 0x1b, 0xc4, 0x5c, 0xa0, 0x75, + 0x0d, 0x71, 0x0d, 0x2a, 0x80, 0x61, 0x93, 0xf1, 0x52, 0x53, 0xdb, 0x7a, 0x66, 0xf1, 0x52, 0xef, 0x91, 0x99, 0xa3, + 0x43, 0x12, 0x2f, 0xa2, 0xc4, 0x5d, 0x14, 0x96, 0x23, 0xa5, 0xd6, 0xdc, 0x28, 0xd6, 0x98, 0xf2, 0xd2, 0x6e, 0x0e, + 0xf1, 0x1d, 0xa2, 0xd3, 0x45, 0x50, 0xf5, 0x79, 0x8b, 0xa7, 0xb5, 0x11, 0xf8, 0x91, 0xd3, 0xa2, 0x40, 0x79, 0xbb, + 0xe2, 0xa4, 0xa6, 0x27, 0x3b, 0x56, 0xd8, 0x34, 0x2d, 0xbd, 0x83, 0x5b, 0x4f, 0xdf, 0x96, 0x64, 0x90, 0x71, 0x20, + 0xb0, 0x23, 0x20, 0x6c, 0x8a, 0x3b, 0x33, 0xd1, 0x16, 0x47, 0x70, 0x82, 0x50, 0x46, 0x66, 0x87, 0x6f, 0x05, 0xcf, + 0x2a, 0x02, 0x9f, 0xf7, 0xa3, 0xf7, 0x9c, 0xeb, 0x6a, 0x28, 0xad, 0x8e, 0x3d, 0x6a, 0x24, 0x38, 0xca, 0xb3, 0xa6, + 0x6f, 0x38, 0xa7, 0x16, 0x21, 0x55, 0x71, 0xbf, 0x00, 0x2b, 0xb7, 0xf7, 0x49, 0x83, 0x15, 0x9f, 0xb1, 0x6c, 0x0f, + 0xb2, 0x95, 0x32, 0xa2, 0x91, 0xf2, 0xba, 0xc7, 0xcc, 0x68, 0x7b, 0xc1, 0xc8, 0x8d, 0xb9, 0xe1, 0xfd, 0xec, 0x31, + 0x8a, 0xea, 0x15, 0x46, 0xac, 0x16, 0xdb, 0x09, 0x30, 0xf7, 0xc6, 0xbd, 0x55, 0x33, 0x67, 0x3e, 0xe5, 0x42, 0x4a, + 0xa9, 0x60, 0xbe, 0x53, 0x79, 0x06, 0x27, 0x9f, 0x42, 0x30, 0xe4, 0x87, 0xef, 0x33, 0xbf, 0x5e, 0x73, 0x6b, 0x96, + 0xf1, 0xa2, 0xbe, 0xa7, 0x7d, 0x36, 0x43, 0x6d, 0x78, 0xb5, 0x94, 0x10, 0x57, 0x67, 0xd9, 0xb9, 0x78, 0x0d, 0xac, + 0xa9, 0x0c, 0xf0, 0x15, 0xab, 0xa2, 0x2e, 0xc1, 0x57, 0xc4, 0xbc, 0x91, 0x30, 0x7f, 0xc3, 0x2a, 0x06, 0xf3, 0xa6, + 0x4a, 0xca, 0x27, 0xee, 0x8f, 0xd8, 0x94, 0x71, 0x89, 0xb2, 0xa5, 0x0f, 0xe9, 0x77, 0xb0, 0x37, 0xaa, 0x78, 0xb3, + 0x12, 0xbe, 0x96, 0xec, 0xb7, 0x7d, 0x6c, 0x4d, 0xc2, 0x14, 0x00, 0x2d, 0x32, 0x16, 0x01, 0xdd, 0x7a, 0xf5, 0xb6, + 0x90, 0xad, 0x09, 0x8d, 0x34, 0x34, 0x84, 0xa2, 0xee, 0xbd, 0x60, 0x62, 0x52, 0xdc, 0x1d, 0x28, 0x31, 0x31, 0x9e, + 0x35, 0x96, 0x5f, 0x90, 0x9f, 0x57, 0x75, 0xda, 0x1a, 0x73, 0xa1, 0x63, 0x46, 0x30, 0xa9, 0x41, 0x33, 0x01, 0x92, + 0x00, 0x5e, 0x2e, 0xa3, 0xc1, 0x38, 0x4f, 0x38, 0x36, 0xf7, 0x3a, 0x4b, 0xc8, 0x00, 0x81, 0x4e, 0x31, 0xa5, 0x52, + 0xbc, 0x5a, 0x1f, 0xa4, 0x94, 0x17, 0x80, 0xb2, 0x63, 0x36, 0x58, 0x52, 0x50, 0x1f, 0x6d, 0xda, 0x4c, 0xae, 0x6d, + 0x0d, 0x7b, 0xca, 0x64, 0xd6, 0x42, 0x99, 0xe6, 0x0f, 0x97, 0xf9, 0x45, 0xc4, 0xb8, 0xa8, 0xf9, 0x84, 0x7d, 0xd5, + 0x61, 0x04, 0x5a, 0x8f, 0x41, 0x5e, 0x0f, 0x27, 0xbc, 0x9f, 0xd7, 0xfb, 0xe6, 0xd6, 0xc4, 0x93, 0x17, 0x05, 0x4e, + 0x7d, 0xa9, 0xfc, 0x4b, 0xfb, 0x13, 0xd8, 0xc4, 0x03, 0x99, 0xf8, 0x54, 0xb2, 0x95, 0x89, 0xa2, 0x04, 0xa2, 0x5a, + 0x84, 0x67, 0x92, 0x0b, 0x82, 0x94, 0x8c, 0x97, 0x81, 0x50, 0xdb, 0x8c, 0x06, 0x24, 0xef, 0x6b, 0x4b, 0x78, 0x2d, + 0xf9, 0x74, 0x11, 0xf2, 0x66, 0x33, 0xac, 0xed, 0xf9, 0xb4, 0xdb, 0xde, 0x4a, 0xa1, 0x6a, 0x80, 0x92, 0xc9, 0x70, + 0x19, 0xf4, 0x0d, 0xcd, 0x0e, 0xe5, 0x09, 0xed, 0xf6, 0x6d, 0x56, 0xca, 0x24, 0xcc, 0x4e, 0xd7, 0xe4, 0xa8, 0xf8, + 0x85, 0xd2, 0xee, 0x6c, 0x74, 0x05, 0xaf, 0x75, 0x07, 0xe3, 0xa2, 0x50, 0x0e, 0x30, 0xa6, 0x46, 0xe6, 0x0f, 0xdc, + 0xc8, 0x91, 0xa5, 0x0f, 0xcb, 0xe4, 0xa2, 0x56, 0x54, 0x26, 0x43, 0xda, 0xb4, 0xb6, 0xea, 0x36, 0x1b, 0x25, 0xe9, + 0xb2, 0x44, 0xce, 0xb7, 0x56, 0xf1, 0xb2, 0xea, 0xe1, 0x5d, 0x28, 0xa5, 0xef, 0x4b, 0x5c, 0xbc, 0x74, 0xa0, 0xee, + 0x6d, 0x25, 0x96, 0xf0, 0xa9, 0x69, 0xe2, 0x14, 0xdc, 0x01, 0x63, 0x95, 0xad, 0x88, 0x5a, 0x20, 0xa9, 0xff, 0xc2, + 0x8b, 0xfb, 0x42, 0x84, 0x78, 0xe7, 0xaa, 0x57, 0x33, 0x24, 0x66, 0x92, 0xc7, 0x68, 0xf5, 0x3b, 0x88, 0x82, 0x6e, + 0x39, 0x8d, 0x03, 0x02, 0x4f, 0x4d, 0x7a, 0xf9, 0xed, 0x48, 0xe2, 0xec, 0x36, 0x2b, 0x34, 0xd0, 0xe3, 0x59, 0x76, + 0xb0, 0xc6, 0xb6, 0x6a, 0x8f, 0x67, 0xa6, 0x2f, 0x2e, 0xb4, 0x4c, 0xc2, 0x98, 0xdf, 0x36, 0xf4, 0x03, 0xd8, 0xa5, + 0xe9, 0xc6, 0x41, 0x63, 0x76, 0x57, 0xab, 0x2f, 0xf1, 0xbc, 0xa8, 0x82, 0x24, 0x2e, 0xb1, 0x31, 0x0a, 0xeb, 0xb7, + 0x2a, 0x1f, 0x15, 0x05, 0xcb, 0xb9, 0xe5, 0xaa, 0xca, 0x6b, 0xd7, 0x91, 0x17, 0xaf, 0x45, 0x4e, 0x82, 0xca, 0x3d, + 0x32, 0xe3, 0x18, 0x5c, 0x44, 0x0b, 0xfd, 0x9c, 0x5e, 0x54, 0x15, 0x1d, 0xaf, 0x2c, 0x6b, 0x88, 0x20, 0x70, 0xab, + 0xea, 0x15, 0x52, 0x62, 0x91, 0x98, 0x67, 0x11, 0xb2, 0xbd, 0x0e, 0x72, 0x9b, 0xb3, 0x81, 0x70, 0x93, 0x4e, 0x09, + 0x9c, 0x92, 0xf0, 0x0f, 0xe5, 0xd9, 0x86, 0x11, 0xf5, 0x4c, 0x6b, 0xa4, 0x8b, 0xaa, 0x35, 0xe7, 0xb5, 0x28, 0xd4, + 0x0e, 0x94, 0xb8, 0x5a, 0xaf, 0x6e, 0x84, 0x42, 0x80, 0x70, 0x61, 0xfe, 0x1c, 0xc0, 0xfd, 0x6d, 0xcd, 0x8a, 0x07, + 0x9b, 0xca, 0xa1, 0x5a, 0x35, 0x6d, 0x1c, 0x80, 0x03, 0xf2, 0x16, 0x2b, 0x83, 0x0b, 0x24, 0xc3, 0x0c, 0xf5, 0x32, + 0xd1, 0x06, 0x43, 0xc5, 0x38, 0xb5, 0xf8, 0x5c, 0xea, 0x5c, 0xa7, 0x4f, 0xc3, 0x8a, 0x99, 0xc5, 0x1d, 0xfa, 0x6c, + 0x95, 0x39, 0xf8, 0xda, 0x11, 0xec, 0xf2, 0x93, 0x69, 0xdb, 0x07, 0x25, 0xbf, 0x0d, 0x65, 0x1a, 0xde, 0xc4, 0xb9, + 0x4d, 0xd9, 0xe9, 0x63, 0x65, 0xe1, 0xab, 0xf7, 0x9d, 0x5b, 0xf2, 0xc1, 0xcc, 0x16, 0x91, 0x7e, 0x05, 0x18, 0xf2, + 0xc7, 0xf8, 0x79, 0x32, 0x88, 0xb6, 0x9d, 0xae, 0x73, 0xcd, 0x3b, 0x54, 0x49, 0x45, 0x45, 0xae, 0x84, 0x21, 0x72, + 0x28, 0xe4, 0x32, 0x52, 0xfa, 0x5a, 0x22, 0x6b, 0x33, 0x72, 0x27, 0xd3, 0x8f, 0x96, 0xd3, 0x29, 0x0e, 0x79, 0x69, + 0xad, 0x0b, 0xeb, 0xf2, 0x37, 0xba, 0xb2, 0x4d, 0xfa, 0x4b, 0x3d, 0x91, 0x8b, 0x86, 0xf0, 0xf3, 0xb5, 0xcd, 0x01, + 0x4a, 0xfd, 0xaf, 0xd6, 0x2f, 0xe2, 0xa8, 0xa0, 0x0b, 0x5d, 0x19, 0x88, 0x0f, 0x8a, 0x52, 0x82, 0xed, 0x73, 0x96, + 0x50, 0xd7, 0x3d, 0x30, 0x4e, 0xba, 0xe2, 0xa4, 0xe8, 0x17, 0xef, 0x45, 0x78, 0x6f, 0x9f, 0x1c, 0x56, 0xee, 0x10, + 0xa7, 0xa7, 0x5a, 0xf5, 0x31, 0x32, 0x59, 0x49, 0x4c, 0x34, 0x61, 0x95, 0x37, 0x34, 0x87, 0xad, 0x32, 0x9a, 0xd5, + 0x74, 0x9d, 0x7c, 0x7f, 0xa0, 0x30, 0x12, 0x19, 0xfe, 0x6e, 0x6e, 0x22, 0x03, 0x0d, 0x1c, 0xd5, 0x19, 0xa8, 0xe4, + 0xb8, 0x9f, 0x6b, 0xd6, 0x87, 0xca, 0x4b, 0x00, 0x64, 0xf6, 0x78, 0xa3, 0xac, 0x5b, 0x7e, 0x37, 0xaf, 0x41, 0x40, + 0xaf, 0xff, 0x15, 0x6d, 0xb2, 0x80, 0x68, 0x33, 0xb8, 0x56, 0x53, 0x50, 0x3e, 0x65, 0xa2, 0x3f, 0xda, 0xa0, 0x67, + 0xbf, 0xdb, 0xe6, 0x0c, 0xd5, 0x85, 0xa5, 0xc4, 0xee, 0x5b, 0x94, 0x15, 0x0b, 0xd8, 0xcf, 0x6a, 0x84, 0xee, 0x94, + 0xf1, 0xf3, 0x47, 0xdd, 0xcc, 0x66, 0x61, 0xab, 0x08, 0xe8, 0xd1, 0x57, 0x57, 0x1c, 0x00, 0x0b, 0xe8, 0x12, 0x16, + 0x46, 0xec, 0x58, 0xca, 0x33, 0xcb, 0x54, 0xf6, 0x99, 0x47, 0x74, 0x7d, 0x33, 0xe4, 0x1e, 0x3e, 0xdd, 0x7e, 0x8b, + 0x55, 0x31, 0x8e, 0x27, 0xd6, 0xd5, 0x45, 0x67, 0x50, 0x34, 0x21, 0xe9, 0xf4, 0xcb, 0x19, 0x90, 0xaa, 0x95, 0x9d, + 0x98, 0xab, 0x36, 0x01, 0xf4, 0xf6, 0x5d, 0x49, 0xe0, 0x31, 0x39, 0xbc, 0x1b, 0xcc, 0x2c, 0x30, 0x45, 0xcb, 0x52, + 0x08, 0x7d, 0xb7, 0x14, 0xe5, 0xbc, 0x15, 0x0a, 0x06, 0xb4, 0x0b, 0xc2, 0xdf, 0x38, 0x2e, 0xb1, 0x05, 0x2d, 0xa3, + 0xf5, 0x22, 0x88, 0x8e, 0x40, 0x24, 0x37, 0x46, 0x8e, 0x0f, 0x67, 0xeb, 0x1a, 0x14, 0x43, 0x96, 0xba, 0xc0, 0xa1, + 0x9b, 0x17, 0x6c, 0x97, 0x0a, 0xc9, 0x44, 0xbe, 0x43, 0x43, 0x60, 0x79, 0xee, 0xc4, 0xe9, 0x80, 0xe8, 0xde, 0xdf, + 0x27, 0x4b, 0x56, 0x54, 0xfc, 0x50, 0x86, 0xdb, 0x17, 0x66, 0x70, 0xa8, 0x27, 0xde, 0x0c, 0x3a, 0xe0, 0x4a, 0xef, + 0x53, 0x25, 0x46, 0x32, 0xeb, 0x1d, 0x20, 0x8a, 0x88, 0x32, 0xf3, 0x4c, 0x76, 0x8b, 0xdb, 0xc3, 0x29, 0x60, 0x20, + 0x63, 0xda, 0xa4, 0x27, 0xc3, 0x44, 0x60, 0x88, 0xf9, 0x6a, 0x7c, 0xde, 0x83, 0x1f, 0xdb, 0x7d, 0x44, 0xce, 0x45, + 0xb9, 0x86, 0xc2, 0x36, 0x66, 0x33, 0x5b, 0xf4, 0x04, 0xdf, 0x48, 0xa4, 0xa3, 0x97, 0x31, 0x94, 0x0b, 0x84, 0x83, + 0x95, 0xce, 0x89, 0xe9, 0xc1, 0x8a, 0x2a, 0x40, 0x5c, 0xb9, 0x71, 0xca, 0xa8, 0x01, 0xb3, 0xe4, 0x06, 0x57, 0xd0, + 0x64, 0xd4, 0xe1, 0x57, 0x77, 0xf4, 0xec, 0x63, 0x16, 0xdc, 0x93, 0x97, 0xc1, 0xa1, 0x6e, 0xad, 0xa7, 0x75, 0xf7, + 0x06, 0x12, 0x62, 0x41, 0x59, 0x60, 0xce, 0x4e, 0x87, 0x85, 0x15, 0x6c, 0x6b, 0x6a, 0x85, 0x57, 0xeb, 0x87, 0x16, + 0x56, 0x92, 0xe1, 0x34, 0x88, 0x24, 0xce, 0xc0, 0x34, 0x0a, 0xf1, 0x87, 0xfa, 0x8b, 0x45, 0x5f, 0x9e, 0xf8, 0xad, + 0xfb, 0x6b, 0xa9, 0xb4, 0xfa, 0xfc, 0xb3, 0x58, 0xb8, 0x20, 0x13, 0xfb, 0x8d, 0x5e, 0x58, 0x98, 0x14, 0x56, 0xe0, + 0xaa, 0x7a, 0xc1, 0xb3, 0x64, 0xa5, 0xf0, 0xe4, 0xbb, 0x11, 0x5a, 0x7a, 0xc2, 0xcf, 0xa3, 0xac, 0x1a, 0x7b, 0x33, + 0xa2, 0x51, 0x2d, 0x9f, 0x82, 0xda, 0x1d, 0x1d, 0x08, 0x97, 0xc9, 0xc0, 0xaa, 0xb2, 0x00, 0xf5, 0xe7, 0x97, 0xb9, + 0x47, 0xc2, 0xba, 0x54, 0x4c, 0xd9, 0x07, 0xcf, 0x89, 0xa0, 0xb7, 0x10, 0x85, 0x18, 0x1e, 0x49, 0xdf, 0xa0, 0xfc, + 0xea, 0x8f, 0xfc, 0xbe, 0xd7, 0x93, 0xbf, 0x33, 0x76, 0xbe, 0x69, 0x96, 0x3b, 0xb3, 0xd7, 0xe8, 0xf5, 0xcf, 0x21, + 0x6b, 0x11, 0x06, 0x39, 0x4d, 0x17, 0x82, 0x26, 0x28, 0x5e, 0x18, 0x0d, 0xac, 0xe7, 0x74, 0xad, 0x37, 0x41, 0xee, + 0x85, 0xc4, 0xf8, 0x7f, 0x91, 0xf0, 0x32, 0xa0, 0x72, 0x32, 0x8a, 0x5a, 0xf0, 0x00, 0x5c, 0x55, 0x43, 0x2d, 0x50, + 0x26, 0x0f, 0x4f, 0xa0, 0x25, 0x63, 0x11, 0x9e, 0x65, 0x1f, 0xeb, 0xd4, 0xc1, 0x78, 0x24, 0xf3, 0xb0, 0xa6, 0xc2, + 0xd5, 0x72, 0x36, 0x39, 0x66, 0x76, 0xcc, 0xea, 0x6a, 0x1f, 0xbb, 0x13, 0x26, 0xf1, 0xcc, 0x79, 0xc8, 0x67, 0xdb, + 0xe3, 0x40, 0x53, 0x6f, 0x1e, 0x38, 0xac, 0x69, 0x36, 0x11, 0xe4, 0x9a, 0x06, 0xb6, 0x00, 0x83, 0x9d, 0xac, 0x55, + 0xa3, 0x84, 0x64, 0xcd, 0x0d, 0x80, 0x38, 0x92, 0x51, 0x08, 0xa9, 0x6c, 0xf8, 0x81, 0xb5, 0x54, 0x5f, 0x81, 0x1e, + 0xab, 0x2f, 0x35, 0x0c, 0x84, 0xa8, 0x6d, 0x84, 0x2a, 0x60, 0x0c, 0x5c, 0x99, 0x7f, 0x29, 0x10, 0x5c, 0xd0, 0x5f, + 0xf6, 0x1a, 0xbe, 0xdc, 0xac, 0xdb, 0x8e, 0x21, 0xea, 0x3a, 0x58, 0x8b, 0xc8, 0x78, 0xd5, 0x15, 0xfe, 0x1b, 0x6e, + 0x22, 0x45, 0x0a, 0xc5, 0x12, 0x91, 0xfc, 0x88, 0xf2, 0x1e, 0xe3, 0x1e, 0xea, 0xbd, 0x1d, 0xbc, 0x8e, 0x84, 0x41, + 0x73, 0xa8, 0xd1, 0x4a, 0x52, 0xbc, 0xc7, 0x56, 0x3d, 0xf6, 0x28, 0xb8, 0x9f, 0x2c, 0x35, 0x7c, 0x87, 0x28, 0x5d, + 0xfd, 0x14, 0x50, 0x4f, 0xfe, 0xa3, 0x67, 0x9b, 0xa7, 0x66, 0x1f, 0x11, 0x7d, 0x93, 0xd1, 0x38, 0xb2, 0x50, 0x51, + 0x14, 0x5e, 0x08, 0x81, 0xe7, 0x1c, 0xf1, 0x54, 0x1f, 0x20, 0xe6, 0x21, 0xd3, 0x64, 0xe4, 0x7a, 0x40, 0x0f, 0x34, + 0x39, 0x7a, 0x76, 0x39, 0xa6, 0x8b, 0xf6, 0x61, 0x74, 0x6c, 0x47, 0x88, 0x4b, 0xb5, 0x89, 0x68, 0x4e, 0xab, 0x2e, + 0x5b, 0x48, 0x62, 0x9d, 0xa7, 0x7c, 0xa4, 0x20, 0x07, 0x6e, 0xc2, 0xea, 0x77, 0x8e, 0x43, 0xbb, 0x28, 0xb8, 0x7d, + 0x4d, 0x25, 0x9c, 0x8d, 0x2a, 0xba, 0x2f, 0x83, 0x4f, 0xa2, 0x59, 0x34, 0x80, 0x6c, 0xc0, 0xd7, 0xfb, 0xdb, 0x09, + 0x96, 0x25, 0xd8, 0x45, 0x6d, 0xa6, 0x6c, 0x5e, 0x9e, 0xc3, 0x6c, 0x6b, 0xb8, 0x2f, 0xd0, 0xfa, 0x12, 0xea, 0x5d, + 0xea, 0x33, 0xc2, 0xb7, 0xf2, 0x60, 0x88, 0xc9, 0xca, 0xcd, 0x46, 0x16, 0x83, 0x75, 0x98, 0x75, 0x8f, 0x91, 0x39, + 0x89, 0x7f, 0xa1, 0xce, 0x5c, 0x10, 0x9e, 0x59, 0xc9, 0x82, 0x4f, 0xe8, 0x66, 0xb0, 0x61, 0x3c, 0xc6, 0xcf, 0x51, + 0xf6, 0xe0, 0xfd, 0x4e, 0x92, 0x56, 0x30, 0x1b, 0x92, 0xda, 0x71, 0xb5, 0xd6, 0xf1, 0x8b, 0x0b, 0xf4, 0x20, 0x35, + 0xf1, 0x54, 0x54, 0x76, 0xc4, 0x2c, 0x90, 0xea, 0x25, 0xf6, 0xbe, 0xf9, 0x49, 0x7c, 0xa4, 0x0d, 0x9e, 0xcb, 0x10, + 0x06, 0xf4, 0x46, 0x62, 0x7d, 0xaf, 0x94, 0xa6, 0x47, 0x65, 0x63, 0xd0, 0xda, 0x98, 0xc9, 0x1c, 0x26, 0xd6, 0x5d, + 0xa2, 0x5e, 0x2c, 0x4f, 0xf2, 0x6b, 0x5b, 0xd3, 0x8a, 0xe3, 0x91, 0xf4, 0x55, 0x95, 0x62, 0xfe, 0x18, 0xd0, 0xf8, + 0xd7, 0x14, 0xc9, 0x23, 0x03, 0x0d, 0x06, 0xa9, 0xb1, 0x62, 0x19, 0x80, 0x43, 0x0c, 0x4d, 0x44, 0x6d, 0xa0, 0x1d, + 0xc3, 0x1d, 0x8d, 0x0c, 0xa9, 0x8f, 0x68, 0x86, 0x24, 0xc0, 0x23, 0x9b, 0x98, 0xac, 0x8c, 0x5d, 0x80, 0x2b, 0x70, + 0xfb, 0x78, 0x06, 0x8d, 0xdf, 0x6e, 0xdd, 0x20, 0xa5, 0xa6, 0x9c, 0x2e, 0x02, 0xd6, 0x98, 0x00, 0x9e, 0x52, 0x4d, + 0xb4, 0x6c, 0x48, 0xf5, 0x53, 0x27, 0x60, 0xbf, 0x38, 0xa8, 0x8f, 0xad, 0x69, 0x4a, 0x59, 0x36, 0x0d, 0xbc, 0x94, + 0x34, 0x42, 0x8c, 0xd0, 0x57, 0x38, 0xe5, 0x08, 0xc4, 0x3b, 0xfc, 0xfa, 0xf4, 0x7a, 0x92, 0xde, 0x26, 0xda, 0xd8, + 0x64, 0x80, 0x61, 0xf8, 0x18, 0xe1, 0x17, 0x3d, 0xec, 0x6c, 0xcd, 0xf8, 0x6b, 0x82, 0x64, 0x3c, 0x29, 0x7c, 0x56, + 0x78, 0x36, 0xb5, 0x45, 0x93, 0x10, 0xff, 0x40, 0x74, 0x28, 0x30, 0x3a, 0x15, 0x94, 0xd9, 0x97, 0x8b, 0xea, 0x45, + 0x4e, 0x41, 0xa3, 0x7d, 0x66, 0xb9, 0xb2, 0x2c, 0x5f, 0x5f, 0xfe, 0xe3, 0x5c, 0x77, 0x5c, 0x62, 0xcf, 0x9d, 0x94, + 0xb8, 0x68, 0x65, 0xcd, 0x1f, 0x5a, 0x5b, 0x6f, 0xc9, 0x61, 0x23, 0x17, 0x9d, 0x42, 0x09, 0xff, 0xc4, 0x5f, 0x0a, + 0x82, 0x95, 0x7b, 0xb0, 0x64, 0x2a, 0xe5, 0x82, 0x8b, 0x19, 0xdd, 0x76, 0xfa, 0x5e, 0xb0, 0xd0, 0xd9, 0xd9, 0xc5, + 0x71, 0x82, 0x24, 0xe5, 0x87, 0xfc, 0x33, 0xef, 0xe2, 0x6c, 0x3b, 0xab, 0xe9, 0x68, 0x45, 0xef, 0xd8, 0xbb, 0x1c, + 0x4e, 0x6c, 0x11, 0xa5, 0xd3, 0x07, 0xe7, 0x67, 0x33, 0xf8, 0xe0, 0x28, 0x6a, 0xe9, 0x4c, 0xcd, 0x58, 0xc0, 0xb9, + 0xb9, 0x7b, 0x88, 0xa0, 0xa7, 0x90, 0x88, 0xd1, 0xf7, 0x2e, 0xa8, 0xf7, 0x8a, 0x6d, 0xce, 0x37, 0x89, 0xa0, 0xcd, + 0x0a, 0x9a, 0x45, 0xf4, 0x62, 0x78, 0x2a, 0xbc, 0x76, 0xe7, 0x5a, 0xae, 0x78, 0x5e, 0x42, 0xa3, 0x21, 0x6b, 0x90, + 0x6c, 0xbf, 0xd3, 0xc4, 0x0f, 0xfa, 0xb9, 0xd5, 0x42, 0x6d, 0x65, 0x4a, 0xfd, 0x98, 0x31, 0x4b, 0x9d, 0xb3, 0x92, + 0xfe, 0x9c, 0xfa, 0x0c, 0x6a, 0x9e, 0x6c, 0x75, 0xfa, 0x35, 0x9f, 0x5f, 0x0e, 0xd5, 0xb3, 0x99, 0xf2, 0x0e, 0x61, + 0x09, 0xf3, 0x7d, 0xa2, 0x54, 0x8f, 0xac, 0xbb, 0x25, 0xce, 0x52, 0x54, 0xc7, 0x22, 0x89, 0x22, 0x63, 0x3b, 0xc3, + 0x11, 0x7a, 0x21, 0xf1, 0x6c, 0x56, 0x67, 0xc2, 0xe4, 0x6a, 0x16, 0x6f, 0x07, 0x73, 0x25, 0x9c, 0xc4, 0x22, 0x89, + 0x50, 0xa4, 0x7d, 0x23, 0x5d, 0x4c, 0xf9, 0xa9, 0xce, 0xed, 0x48, 0xa8, 0xf4, 0x16, 0xff, 0x34, 0xb8, 0xc4, 0x44, + 0x2a, 0x50, 0x89, 0xcf, 0xef, 0x96, 0x58, 0x22, 0x49, 0x15, 0x39, 0x14, 0xd4, 0xca, 0xe4, 0x0f, 0x9b, 0xe7, 0x52, + 0x5a, 0x77, 0x47, 0xe0, 0xfa, 0x32, 0x56, 0x12, 0x77, 0xff, 0x32, 0x99, 0x47, 0x01, 0xd8, 0x2f, 0xcb, 0x75, 0x3e, + 0xc4, 0x80, 0xcb, 0xa3, 0x53, 0x8d, 0x20, 0xd8, 0xf1, 0x06, 0xde, 0x0c, 0x24, 0x08, 0x4e, 0x33, 0x12, 0x11, 0x0b, + 0xce, 0x90, 0xc5, 0x93, 0x37, 0x00, 0x24, 0xe7, 0x0f, 0xf1, 0xf3, 0x82, 0x94, 0x1d, 0xa0, 0x0a, 0x47, 0x05, 0x20, + 0x76, 0x48, 0xd0, 0xe8, 0xc2, 0xbb, 0xd9, 0x67, 0xad, 0xd9, 0xf2, 0x7a, 0x55, 0x3c, 0x07, 0x55, 0x43, 0x72, 0x52, + 0x12, 0x46, 0x9c, 0x61, 0xf6, 0x83, 0xa0, 0x44, 0xf9, 0xf6, 0x30, 0x21, 0x8c, 0xcc, 0x96, 0x78, 0xa1, 0xd1, 0x20, + 0xc0, 0xed, 0x23, 0xc4, 0x4c, 0xb6, 0x4d, 0x39, 0x26, 0x5f, 0x73, 0xc6, 0x39, 0x63, 0xce, 0x10, 0x8a, 0x06, 0x66, + 0x6b, 0x09, 0xc4, 0x3a, 0x8b, 0x32, 0x1a, 0x4a, 0x53, 0xfc, 0x4e, 0x8e, 0xa0, 0xd6, 0x91, 0xb7, 0x26, 0x43, 0xbb, + 0x0d, 0xee, 0x44, 0x80, 0x43, 0x0a, 0xf7, 0x4b, 0x60, 0x41, 0x79, 0xe5, 0xb6, 0x64, 0x96, 0xda, 0x7e, 0x48, 0xb6, + 0x92, 0xde, 0x9b, 0x81, 0xc1, 0xbb, 0x58, 0xc3, 0xc5, 0x2c, 0x1d, 0x25, 0x64, 0x15, 0x6c, 0x16, 0xeb, 0xfe, 0xe5, + 0xd7, 0x5d, 0x37, 0x19, 0xb9, 0xad, 0x92, 0xb1, 0xa2, 0x1c, 0x8f, 0xab, 0x39, 0x1b, 0x70, 0x7d, 0x19, 0xa4, 0xe1, + 0x52, 0x21, 0x74, 0xa6, 0x7d, 0xb7, 0xbf, 0x8b, 0x6b, 0xb7, 0x5c, 0x1e, 0x2d, 0xc0, 0xa0, 0x8d, 0x3d, 0x70, 0x8a, + 0x0a, 0x2c, 0x89, 0x0a, 0x49, 0xd8, 0x7c, 0x00, 0x4c, 0xb5, 0x7e, 0x10, 0xe5, 0xf8, 0x77, 0x49, 0x5f, 0x0b, 0x32, + 0x3d, 0xd7, 0x79, 0x7e, 0x96, 0xfa, 0x83, 0x69, 0xf7, 0x71, 0x8c, 0xe1, 0x8c, 0xc3, 0x1c, 0x21, 0x2a, 0x73, 0xf4, + 0xeb, 0xcf, 0xf0, 0xd8, 0xdb, 0x4a, 0xf5, 0x9f, 0x50, 0x9c, 0xdf, 0x2b, 0xa3, 0x79, 0xb6, 0x4c, 0xfa, 0x6c, 0x41, + 0xbf, 0xcf, 0x24, 0x2d, 0xdd, 0x76, 0xf9, 0xc4, 0xff, 0xa6, 0x3a, 0x3c, 0xdd, 0xed, 0x11, 0xe3, 0x22, 0x92, 0x04, + 0x9f, 0x98, 0x13, 0x9e, 0xee, 0x9a, 0x89, 0xba, 0x3c, 0x43, 0x6a, 0xf7, 0xc6, 0x68, 0x9b, 0x4a, 0xf5, 0xb6, 0xac, + 0xd8, 0xf4, 0xa2, 0x22, 0xd8, 0xd5, 0x85, 0x75, 0x79, 0xf7, 0xbb, 0x4f, 0xa9, 0x77, 0x73, 0x10, 0x6e, 0x5c, 0x6d, + 0x57, 0x35, 0x5a, 0xcc, 0x69, 0x01, 0xa5, 0x24, 0x52, 0x12, 0xcd, 0xa6, 0x71, 0xa4, 0x54, 0xf8, 0x79, 0x8e, 0x92, + 0x5b, 0x49, 0x9b, 0x5f, 0x5b, 0xc3, 0x89, 0x2a, 0xa9, 0x8e, 0xd4, 0xd4, 0x61, 0x4d, 0x7a, 0x0a, 0xcc, 0xff, 0xd9, + 0x31, 0x12, 0x82, 0xc2, 0x85, 0x33, 0x0f, 0x28, 0xf5, 0x57, 0x43, 0xb5, 0x93, 0x3e, 0x1e, 0x79, 0x7d, 0x6f, 0x1d, + 0xe7, 0x3a, 0x17, 0xce, 0x38, 0x74, 0xd3, 0xcd, 0x03, 0x3d, 0xfd, 0xae, 0xc7, 0x57, 0xf1, 0xd7, 0x86, 0x64, 0x49, + 0x22, 0x35, 0x73, 0x67, 0x7b, 0x65, 0x4b, 0xfb, 0xea, 0xa1, 0x42, 0x8b, 0xe3, 0xd2, 0x58, 0xed, 0x2b, 0xcc, 0xd3, + 0x1b, 0x35, 0x58, 0x44, 0x94, 0xa6, 0x7e, 0x38, 0x1e, 0xd2, 0x79, 0x0e, 0xd4, 0xd4, 0xe2, 0xe6, 0x29, 0xa7, 0xf5, + 0x13, 0xc6, 0xa9, 0x00, 0x3b, 0x13, 0x45, 0x2e, 0x5e, 0xab, 0xbf, 0x29, 0xfd, 0x0a, 0xf6, 0xd7, 0x2b, 0xa9, 0xfa, + 0x99, 0xc5, 0x2a, 0x9d, 0x19, 0x56, 0xe5, 0xcc, 0x9a, 0xe9, 0x0a, 0xfb, 0x39, 0x17, 0xbb, 0x1c, 0x58, 0x94, 0x24, + 0x79, 0x3a, 0xae, 0xcc, 0x22, 0x9c, 0xdb, 0x4b, 0xe7, 0x91, 0x4e, 0x9d, 0x6c, 0x30, 0x29, 0x13, 0x5a, 0x3d, 0x32, + 0x2d, 0x31, 0x32, 0x4d, 0x20, 0xd8, 0xa5, 0xb7, 0xc8, 0xd2, 0xf6, 0x8b, 0x3b, 0x16, 0x85, 0xda, 0x5c, 0x6d, 0x7a, + 0x1c, 0x85, 0x8c, 0xf9, 0xa5, 0xb5, 0xa7, 0xc4, 0xa5, 0xf3, 0x63, 0x11, 0xed, 0xa7, 0x4b, 0x75, 0xac, 0xd9, 0x89, + 0x40, 0x95, 0x6b, 0x03, 0xf9, 0x79, 0x9b, 0x1e, 0xd2, 0xe7, 0x2d, 0x9c, 0x95, 0x3f, 0x94, 0x61, 0x7d, 0x40, 0x08, + 0x13, 0x81, 0x91, 0xb1, 0x50, 0x5a, 0x49, 0x60, 0x15, 0x78, 0xc5, 0xa8, 0xd9, 0x6c, 0x57, 0x7c, 0x1f, 0x40, 0x3a, + 0xc7, 0x4d, 0x08, 0x07, 0x80, 0xbc, 0x9e, 0x42, 0x75, 0x16, 0xa2, 0x40, 0x33, 0x05, 0x48, 0xf8, 0x21, 0x3d, 0x7f, + 0x01, 0xf3, 0xc7, 0x74, 0xf4, 0x56, 0xad, 0xdc, 0x46, 0x3b, 0x1c, 0xcb, 0x53, 0xe5, 0xa6, 0x1a, 0x87, 0x8b, 0x92, + 0xa8, 0x24, 0x16, 0x35, 0xbc, 0x72, 0x45, 0x9b, 0x33, 0x1f, 0xf9, 0x0d, 0xdb, 0xc4, 0xe3, 0x5f, 0x57, 0x63, 0x5c, + 0x81, 0xaa, 0x51, 0x05, 0x5b, 0xf2, 0x05, 0x98, 0xea, 0x2e, 0x12, 0xd8, 0x62, 0xd3, 0xd8, 0x9c, 0x81, 0x0e, 0xed, + 0xa3, 0xec, 0x49, 0xa9, 0x4a, 0x16, 0xa8, 0xe4, 0x6a, 0x29, 0xac, 0xb6, 0xa6, 0x51, 0x9b, 0x90, 0xf7, 0xbf, 0xa1, + 0x79, 0xeb, 0x4b, 0x3e, 0x61, 0x7b, 0x88, 0xe8, 0x33, 0x7c, 0xee, 0xa3, 0x5a, 0x7c, 0x0f, 0x28, 0x9c, 0x2d, 0x05, + 0x23, 0x53, 0x1c, 0xda, 0xe3, 0x05, 0x4a, 0x93, 0x79, 0x78, 0xa8, 0xa3, 0x0a, 0x1b, 0xf2, 0x11, 0x0e, 0xd8, 0x7e, + 0x4c, 0x61, 0x89, 0x0a, 0x25, 0xfa, 0x2e, 0xda, 0xcd, 0xc1, 0x77, 0xa5, 0x03, 0xde, 0x96, 0x21, 0x2e, 0xa6, 0x9b, + 0x9d, 0x78, 0x8b, 0x96, 0xe5, 0xab, 0x38, 0xd8, 0x66, 0x84, 0xa1, 0x6c, 0x0a, 0x70, 0xe7, 0xbd, 0xaa, 0x50, 0xe4, + 0xf8, 0xd6, 0x0c, 0x8e, 0xea, 0x0d, 0xd2, 0x45, 0x13, 0xa0, 0x0e, 0x46, 0x3d, 0xf0, 0x13, 0x82, 0x1c, 0x50, 0x19, + 0xbd, 0xdb, 0xa2, 0x2d, 0xae, 0x05, 0xcf, 0x84, 0x80, 0x34, 0xad, 0x48, 0xb5, 0x1b, 0xa5, 0x51, 0x1f, 0x0d, 0xcd, + 0xbe, 0x89, 0x45, 0x02, 0x90, 0xcc, 0xe2, 0x55, 0x49, 0xa4, 0x02, 0xd8, 0x02, 0x3b, 0x36, 0x8b, 0x6e, 0xf8, 0x66, + 0x7d, 0x32, 0x60, 0x68, 0xe9, 0xb5, 0xef, 0xc9, 0xea, 0xa3, 0xf6, 0xb9, 0x86, 0x78, 0xc5, 0x71, 0x8e, 0x34, 0x99, + 0x2a, 0xea, 0x7c, 0xb2, 0x8e, 0xf2, 0x58, 0x9b, 0xcb, 0xe5, 0x8d, 0x0d, 0x65, 0xd0, 0x63, 0x83, 0x45, 0x4a, 0x5c, + 0x3b, 0x66, 0xbf, 0xbe, 0xb8, 0xc8, 0xa0, 0xe3, 0x9c, 0x3e, 0x90, 0x30, 0x4d, 0x27, 0x11, 0xea, 0x8e, 0x95, 0xaf, + 0xab, 0xd0, 0x2c, 0x08, 0xfb, 0xfe, 0x22, 0x19, 0x6b, 0xd8, 0x78, 0x37, 0x64, 0x73, 0x7d, 0xd5, 0xde, 0x0f, 0x50, + 0x07, 0xe2, 0x62, 0xc0, 0xc5, 0x5b, 0x50, 0xc6, 0xcc, 0xbf, 0xa3, 0x5e, 0x2b, 0xa5, 0x34, 0x6a, 0x79, 0x18, 0x6a, + 0x78, 0xab, 0xbd, 0xcc, 0x7f, 0x3c, 0xfb, 0x90, 0x0f, 0x05, 0x2a, 0x54, 0x21, 0x35, 0x4d, 0xa2, 0x6e, 0xd7, 0x41, + 0x6c, 0x6b, 0x27, 0x99, 0x5a, 0xb1, 0x88, 0x94, 0x47, 0x80, 0xbb, 0x70, 0x78, 0xb7, 0xfa, 0x85, 0x11, 0xdf, 0xec, + 0x73, 0x2d, 0xb4, 0x25, 0x9a, 0xb3, 0x23, 0xde, 0x45, 0x2b, 0x3b, 0x9c, 0x5a, 0x20, 0x1d, 0x3b, 0x15, 0xdb, 0x25, + 0x8a, 0xde, 0x63, 0x81, 0xad, 0x66, 0x6b, 0xeb, 0xb7, 0x56, 0xf4, 0x21, 0xac, 0x16, 0xb4, 0xb6, 0xe7, 0x32, 0x8d, + 0xcd, 0xc4, 0x09, 0x62, 0x01, 0x34, 0x7b, 0xfb, 0xaa, 0x24, 0xef, 0x33, 0x0b, 0x2e, 0x4b, 0xb1, 0x44, 0x8a, 0xb0, + 0x03, 0x3a, 0x89, 0x06, 0x4c, 0x54, 0x05, 0xc7, 0x46, 0xec, 0xf9, 0xa2, 0xde, 0x37, 0xae, 0x4a, 0x32, 0x28, 0x93, + 0xd6, 0x6d, 0xd5, 0x8b, 0xc9, 0xf7, 0x7e, 0x16, 0x48, 0x3e, 0x14, 0x0e, 0x60, 0xc7, 0x25, 0x5c, 0x7c, 0x16, 0x8c, + 0xdc, 0x2a, 0x65, 0x2d, 0xc0, 0x9c, 0xce, 0x99, 0xbf, 0x5a, 0x7a, 0x34, 0x2d, 0x29, 0x27, 0x0e, 0xd3, 0xf7, 0xe7, + 0x10, 0xc9, 0x15, 0x48, 0x3f, 0xef, 0x3d, 0xef, 0x15, 0x7d, 0xe3, 0x8f, 0x57, 0xfb, 0x94, 0x19, 0xcd, 0xa6, 0x2c, + 0xf5, 0x64, 0xc9, 0xd3, 0x2d, 0x15, 0x1c, 0xa3, 0x8b, 0x56, 0x37, 0x6c, 0xcd, 0x8a, 0x35, 0x23, 0xcb, 0xf0, 0x8f, + 0x60, 0x85, 0x6f, 0x60, 0x5d, 0x2c, 0x01, 0xcd, 0xdf, 0x18, 0x1f, 0x85, 0x3c, 0x2e, 0x3e, 0xd0, 0xf9, 0x19, 0x21, + 0xae, 0xc2, 0x54, 0x91, 0x70, 0xbe, 0x55, 0x6a, 0xa5, 0x04, 0x15, 0xd3, 0xf2, 0x99, 0x16, 0xdf, 0xa8, 0x6d, 0x95, + 0xd9, 0x5b, 0x7e, 0x99, 0xe4, 0xca, 0x74, 0x7e, 0x9e, 0x9c, 0x49, 0xf1, 0xf2, 0xc3, 0x12, 0x55, 0xe6, 0x9f, 0x46, + 0x68, 0xa3, 0xef, 0xe1, 0xc7, 0x0e, 0x3f, 0xc8, 0xbc, 0x40, 0x24, 0xd5, 0xb8, 0xc0, 0x38, 0x2a, 0x3f, 0x4d, 0xab, + 0x11, 0x33, 0x45, 0xf8, 0xc6, 0xa9, 0x03, 0xcb, 0xf7, 0xb9, 0x54, 0x73, 0x2e, 0x42, 0x05, 0x10, 0x7b, 0x1a, 0x3b, + 0xef, 0xc2, 0x9c, 0x31, 0x15, 0x09, 0x84, 0x71, 0x85, 0x76, 0x49, 0x30, 0x76, 0x4b, 0xa9, 0xb6, 0xd5, 0xbb, 0x05, + 0xf3, 0x9a, 0x8a, 0x08, 0x98, 0xc2, 0x3b, 0xd0, 0xbc, 0x99, 0x2d, 0x6d, 0xd0, 0x39, 0xb1, 0xa3, 0x02, 0xfb, 0x31, + 0xa6, 0xbc, 0xc3, 0xde, 0x6f, 0xa6, 0xcf, 0x19, 0xe7, 0xd0, 0x3d, 0x0f, 0xf5, 0xa6, 0x33, 0x5c, 0xf9, 0x86, 0x3e, + 0x9b, 0x11, 0x67, 0x0b, 0x24, 0x5f, 0x23, 0x5b, 0xb1, 0xae, 0x5a, 0x82, 0xba, 0x07, 0x92, 0xbd, 0x7d, 0x75, 0xdd, + 0x5b, 0x7d, 0x2e, 0x08, 0x1a, 0xdd, 0xad, 0x00, 0xbb, 0x83, 0x05, 0xef, 0x56, 0x67, 0xe2, 0x89, 0x03, 0x80, 0xec, + 0xd2, 0x7f, 0x12, 0x36, 0xd0, 0x9d, 0x76, 0x7f, 0xed, 0x84, 0xb2, 0xa0, 0x75, 0x36, 0xe5, 0x31, 0xb4, 0x65, 0x17, + 0x11, 0x43, 0x76, 0x1d, 0xf6, 0xac, 0x9b, 0xfb, 0x42, 0x58, 0x81, 0xc7, 0x3d, 0xb0, 0xbe, 0x08, 0x7c, 0x4a, 0x04, + 0x24, 0xe4, 0x5c, 0x88, 0xbf, 0x75, 0xa1, 0x66, 0x19, 0x77, 0x9b, 0x0e, 0xb1, 0x9b, 0x24, 0xf4, 0x07, 0x55, 0xe1, + 0xad, 0xa5, 0x95, 0xcf, 0x02, 0xca, 0x7c, 0x24, 0x23, 0x03, 0xe7, 0xdc, 0xd8, 0x9e, 0x76, 0x5e, 0x9a, 0x31, 0x2f, + 0x15, 0x5a, 0x66, 0xf2, 0x6e, 0xd5, 0xc0, 0xb3, 0xf6, 0xbf, 0x9b, 0xe3, 0xc4, 0x86, 0xe6, 0xb1, 0x1d, 0x73, 0xb4, + 0xbd, 0x18, 0xf7, 0x2d, 0xfb, 0xea, 0xe5, 0x32, 0x2e, 0x9b, 0x67, 0xbd, 0x5b, 0xbb, 0x55, 0xec, 0xa7, 0x88, 0x0a, + 0x9b, 0xc2, 0x64, 0xaa, 0x49, 0x0c, 0x83, 0xc0, 0x68, 0x01, 0xec, 0x4d, 0x34, 0xc3, 0x2e, 0xe6, 0xa0, 0xb9, 0x34, + 0xeb, 0x6e, 0xf6, 0x38, 0x7d, 0x9b, 0xf9, 0x4a, 0xd5, 0x5e, 0x55, 0xa3, 0x44, 0xce, 0xe9, 0xb0, 0x7f, 0x29, 0xed, + 0x3f, 0x8a, 0xbc, 0xa9, 0x61, 0x2c, 0x0e, 0x44, 0x63, 0x01, 0xc1, 0x65, 0x7a, 0xab, 0xcd, 0xb2, 0x08, 0xc9, 0xa9, + 0x15, 0xe5, 0x1f, 0x34, 0x80, 0x54, 0x5c, 0xad, 0x16, 0x37, 0xe3, 0x58, 0x70, 0x8c, 0x4a, 0x6d, 0x0c, 0x4f, 0xff, + 0x24, 0x1e, 0x52, 0xd1, 0x56, 0x97, 0x13, 0xcd, 0x4b, 0xb5, 0xe5, 0x10, 0x40, 0x20, 0x57, 0x1b, 0xd6, 0x38, 0xf4, + 0x57, 0x27, 0x73, 0x23, 0xd3, 0x61, 0x66, 0xaa, 0xc0, 0xf8, 0x5b, 0x45, 0x53, 0x30, 0x39, 0x17, 0x49, 0xcc, 0xdc, + 0xce, 0xc0, 0xb2, 0x06, 0xe8, 0x20, 0x7a, 0xc3, 0xb7, 0x93, 0x1f, 0xea, 0x4f, 0x2b, 0x8b, 0x22, 0x4e, 0x1d, 0x93, + 0xd3, 0xd7, 0x76, 0x50, 0x50, 0xab, 0xed, 0x5c, 0xc4, 0x6b, 0x9e, 0x13, 0x68, 0x5f, 0xf9, 0xd5, 0xec, 0xf4, 0xfa, + 0x85, 0xd3, 0xef, 0x90, 0x15, 0x48, 0x9d, 0xe2, 0x5f, 0xba, 0x32, 0xca, 0xd5, 0xce, 0x79, 0x36, 0xfd, 0xf2, 0x98, + 0x24, 0xdb, 0xc6, 0xbf, 0x46, 0x2e, 0x39, 0x20, 0xf9, 0x13, 0xe7, 0xc0, 0xc8, 0x16, 0xd3, 0x24, 0x61, 0xaa, 0xd7, + 0x24, 0xcd, 0x59, 0x58, 0xc7, 0x6e, 0x3a, 0xfe, 0x73, 0xec, 0xa2, 0x27, 0x91, 0x90, 0x5a, 0x6f, 0x69, 0xa4, 0x85, + 0x75, 0xef, 0x8c, 0x5c, 0xc8, 0xe6, 0xa1, 0x4c, 0x01, 0x19, 0xd3, 0xcd, 0xba, 0x4b, 0x25, 0x12, 0xb5, 0x60, 0x69, + 0x68, 0xb7, 0x93, 0xe1, 0x10, 0xb5, 0xf6, 0x91, 0xec, 0x54, 0xf4, 0x2e, 0x54, 0x85, 0xa1, 0x8e, 0xe4, 0x4b, 0x61, + 0x25, 0x16, 0x58, 0x7b, 0x29, 0xd7, 0x92, 0x05, 0x5d, 0x79, 0x79, 0x24, 0x14, 0xeb, 0x00, 0xb6, 0xd6, 0xa5, 0xd1, + 0x0d, 0xa0, 0x13, 0xc5, 0xc0, 0x75, 0xc8, 0x00, 0x94, 0x31, 0x85, 0xca, 0x2d, 0x2d, 0x2e, 0xb9, 0x16, 0xa5, 0x98, + 0x03, 0x52, 0xbf, 0xc6, 0xe0, 0x8c, 0xf9, 0xbd, 0x8f, 0x29, 0xc4, 0x91, 0x31, 0xbc, 0x6a, 0x49, 0xda, 0x32, 0xd7, + 0xd6, 0x8a, 0x69, 0x9d, 0x30, 0x75, 0x96, 0xfd, 0x34, 0xf8, 0xce, 0xbf, 0xa3, 0x8e, 0xb4, 0xbc, 0xc5, 0x91, 0x8a, + 0x70, 0x68, 0x7b, 0x62, 0x2e, 0x4c, 0x29, 0x3c, 0x66, 0xb7, 0x77, 0x84, 0x6e, 0x7a, 0x29, 0xe0, 0xb1, 0x70, 0x63, + 0x2a, 0x30, 0x8e, 0x1e, 0x3f, 0x14, 0x4e, 0x84, 0xe1, 0xd0, 0x54, 0x9d, 0xf0, 0x6e, 0x9a, 0x32, 0x0b, 0x72, 0x6a, + 0x24, 0x6c, 0x78, 0xb0, 0xee, 0x07, 0x50, 0x14, 0x09, 0x69, 0x16, 0x57, 0x8d, 0x26, 0x8a, 0xeb, 0x8a, 0x0b, 0xbb, + 0x2f, 0xc7, 0xf9, 0x45, 0x25, 0x0e, 0xdd, 0xb3, 0xaa, 0x63, 0x8b, 0xc4, 0x67, 0x53, 0x55, 0x46, 0x44, 0xd5, 0x7b, + 0x09, 0x81, 0xb9, 0xad, 0xa5, 0x1b, 0x7f, 0xec, 0x0a, 0x57, 0x06, 0x0f, 0x0c, 0x21, 0xd2, 0xf4, 0x6a, 0x5d, 0xa2, + 0xe4, 0xed, 0xea, 0x0f, 0xfb, 0x61, 0xfd, 0xc1, 0xd8, 0x64, 0x07, 0xb7, 0x0a, 0xa4, 0xcd, 0x39, 0xbf, 0x66, 0xa6, + 0xb5, 0x6c, 0xb5, 0x0f, 0x6a, 0x94, 0x07, 0x9b, 0xcb, 0x34, 0x14, 0xf3, 0x4f, 0xef, 0x0c, 0x1f, 0x9c, 0x70, 0x91, + 0xf8, 0x02, 0x12, 0x71, 0xd8, 0x9e, 0x3e, 0x3e, 0x52, 0xf9, 0x5b, 0x27, 0x54, 0xd8, 0x8d, 0x52, 0xb6, 0x83, 0xf2, + 0xbe, 0x3a, 0xdc, 0x13, 0x13, 0x35, 0xd8, 0x67, 0x97, 0xa5, 0xa3, 0x01, 0x92, 0x94, 0x26, 0xf6, 0x25, 0x8e, 0xf7, + 0xc5, 0x0c, 0xeb, 0x05, 0x22, 0x5e, 0x75, 0xb2, 0x14, 0x4a, 0xa6, 0xec, 0xf9, 0xec, 0x78, 0x1d, 0x64, 0xf2, 0x11, + 0x55, 0x1d, 0xd2, 0xdc, 0xd4, 0x72, 0x97, 0x13, 0x03, 0xdd, 0x6b, 0xd3, 0x9f, 0xdf, 0x37, 0x86, 0x6c, 0x2b, 0x91, + 0x6f, 0x7c, 0x7b, 0xd4, 0x3f, 0xbd, 0x7e, 0xa1, 0x21, 0xd9, 0x9b, 0x65, 0xec, 0x6e, 0x7f, 0xb8, 0x2c, 0xea, 0xa8, + 0xea, 0x07, 0x55, 0x30, 0x4b, 0xea, 0xa9, 0xe9, 0x2c, 0xa4, 0x04, 0x13, 0x0e, 0x04, 0x9c, 0xb5, 0x1e, 0x84, 0xaa, + 0xcb, 0xbf, 0xb6, 0x57, 0x57, 0xbb, 0xf1, 0x62, 0xe1, 0x69, 0x64, 0x23, 0x31, 0xd4, 0x61, 0xe9, 0x3b, 0xb3, 0x85, + 0xf0, 0x0c, 0xbf, 0xef, 0x6a, 0x24, 0x2e, 0x35, 0x00, 0x5f, 0x2f, 0xdf, 0x9d, 0xfb, 0xe1, 0xf0, 0x21, 0xb0, 0x17, + 0xcc, 0x8c, 0xf7, 0x59, 0x69, 0x8a, 0x25, 0x0d, 0x3f, 0x46, 0x36, 0xb3, 0xae, 0x7d, 0x12, 0x82, 0x08, 0xac, 0x21, + 0x42, 0x95, 0x87, 0x66, 0x0e, 0x65, 0xac, 0x1c, 0xab, 0x68, 0xed, 0xd9, 0x6f, 0x30, 0x25, 0xb2, 0xd9, 0x22, 0xa0, + 0x23, 0xfb, 0x7e, 0x79, 0x51, 0xcb, 0xf0, 0xba, 0x7f, 0x79, 0xf8, 0x22, 0x17, 0xb5, 0x59, 0x03, 0xf8, 0x3b, 0x92, + 0xd5, 0xb2, 0x37, 0x96, 0x5f, 0xe8, 0x14, 0x6c, 0xb5, 0x39, 0x30, 0x22, 0x92, 0x36, 0x8c, 0xb8, 0x20, 0x99, 0x33, + 0x31, 0x15, 0x42, 0x96, 0x1e, 0xf7, 0xf1, 0x32, 0x05, 0xc0, 0xe9, 0x72, 0x65, 0xc4, 0x05, 0x81, 0x90, 0x8e, 0xc3, + 0x98, 0x16, 0xd2, 0xb2, 0x9e, 0xed, 0x42, 0xb3, 0x51, 0xa3, 0xd0, 0x35, 0x87, 0x44, 0x8d, 0x99, 0x75, 0x8f, 0x43, + 0x5c, 0x6a, 0x3b, 0x21, 0x2b, 0xbf, 0xb9, 0x9a, 0x01, 0xd0, 0x98, 0x48, 0x2e, 0x97, 0xc3, 0x44, 0x96, 0x98, 0xcf, + 0x98, 0xb4, 0xe9, 0xeb, 0xc3, 0x37, 0x31, 0x3d, 0x43, 0xec, 0x1a, 0xeb, 0x0f, 0xd1, 0xf2, 0xdc, 0x8b, 0x10, 0xd4, + 0xba, 0x6c, 0xd9, 0xa3, 0x68, 0x2b, 0x64, 0xa2, 0x6d, 0x49, 0xd8, 0x02, 0x0d, 0xec, 0x33, 0x9e, 0x0d, 0x97, 0x83, + 0x28, 0x4b, 0x40, 0x6a, 0x29, 0x87, 0xfc, 0x1a, 0xed, 0x11, 0x62, 0x0c, 0x16, 0xac, 0x81, 0xe5, 0xbe, 0xe1, 0x30, + 0x0a, 0x12, 0xec, 0x81, 0xff, 0xbf, 0x20, 0x96, 0xab, 0x6f, 0x27, 0x7b, 0x5e, 0x57, 0x25, 0xda, 0x06, 0x03, 0xe0, + 0xa0, 0xe3, 0x11, 0x06, 0x8d, 0x6b, 0x1a, 0xa8, 0xae, 0x27, 0x97, 0x0b, 0x33, 0x36, 0x55, 0x90, 0x7a, 0x06, 0xdc, + 0x12, 0x6e, 0xfb, 0x59, 0xc6, 0x1c, 0x0c, 0x6c, 0x9c, 0xdd, 0x8d, 0xed, 0x1a, 0x43, 0xf0, 0xe8, 0x04, 0xed, 0x74, + 0xa7, 0x84, 0x3c, 0xaf, 0x1f, 0xad, 0xd5, 0xb0, 0xc3, 0xe7, 0xad, 0x69, 0xcf, 0x23, 0xcc, 0x88, 0xb8, 0x69, 0xba, + 0x60, 0x63, 0x29, 0xc1, 0x52, 0xa4, 0x88, 0x01, 0x6c, 0x47, 0xd9, 0x0d, 0x80, 0x16, 0xd8, 0x1f, 0xca, 0x6b, 0x8d, + 0x1e, 0x3d, 0x1b, 0x3e, 0xc7, 0xa8, 0xea, 0x32, 0x87, 0x91, 0x7a, 0xee, 0x50, 0x37, 0x1e, 0x78, 0x7e, 0xaa, 0xd6, + 0x28, 0x14, 0x8a, 0x25, 0x70, 0xf4, 0xf3, 0x7d, 0x1a, 0x89, 0x67, 0x99, 0x21, 0xec, 0xe4, 0x66, 0xf3, 0x04, 0xc4, + 0x3e, 0x34, 0x32, 0x21, 0x80, 0x10, 0x2c, 0x84, 0xd5, 0x1e, 0x50, 0xce, 0xdf, 0x13, 0xf6, 0x7d, 0x44, 0xc7, 0x4d, + 0x80, 0x07, 0x53, 0x50, 0x9c, 0xac, 0x7d, 0x2a, 0x22, 0x52, 0xf9, 0x49, 0x92, 0x6c, 0xc6, 0x49, 0x9d, 0x04, 0x66, + 0x47, 0x9c, 0x92, 0xa5, 0x58, 0x38, 0x2f, 0x9e, 0x70, 0x60, 0xd3, 0x35, 0x05, 0x4c, 0x27, 0xbe, 0xc8, 0x49, 0xd9, + 0x0c, 0x5a, 0x38, 0x1f, 0xe7, 0xb6, 0x8d, 0x05, 0x47, 0x65, 0x19, 0x3b, 0x7b, 0xab, 0xc6, 0x08, 0x1d, 0xf6, 0x4d, + 0x82, 0x7a, 0x3f, 0xa6, 0xb0, 0x76, 0xda, 0xe3, 0x23, 0x26, 0xc1, 0xa1, 0x42, 0xe8, 0x26, 0xa8, 0x59, 0xa5, 0x3f, + 0xea, 0x8e, 0x39, 0x35, 0x92, 0xa4, 0x3c, 0x2e, 0x37, 0x24, 0xa9, 0x93, 0x7d, 0xf6, 0x68, 0x4f, 0x1e, 0x28, 0x9c, + 0x26, 0x3c, 0xd1, 0x95, 0x02, 0x06, 0xc1, 0x8b, 0x04, 0xbb, 0xba, 0x2c, 0x14, 0xc9, 0x40, 0x16, 0x43, 0xbb, 0x01, + 0x67, 0x57, 0xe6, 0x94, 0x84, 0x7c, 0xe6, 0x0b, 0x9e, 0xd9, 0x6e, 0x86, 0xe8, 0x26, 0x5b, 0xd4, 0x90, 0x51, 0x30, + 0xb4, 0x5b, 0x28, 0x22, 0x74, 0xeb, 0xc2, 0xdf, 0xe1, 0x0f, 0xcf, 0x52, 0xd9, 0x5c, 0x70, 0x9d, 0x2e, 0xbc, 0xc6, + 0x5f, 0x7a, 0xd6, 0x8a, 0x9d, 0x6f, 0xad, 0x9d, 0x4b, 0x96, 0x8b, 0x5e, 0xf3, 0x1f, 0xb9, 0xc7, 0x05, 0x3a, 0xb1, + 0x05, 0xd1, 0x86, 0x26, 0xa8, 0x0c, 0xa7, 0x81, 0x0b, 0x0f, 0x14, 0x52, 0x7b, 0x1c, 0x96, 0xb2, 0x45, 0xf4, 0x93, + 0x79, 0xae, 0xae, 0xc1, 0x22, 0x31, 0x6b, 0xa5, 0xe8, 0x45, 0x53, 0xa1, 0x88, 0x8c, 0xae, 0x06, 0xa2, 0x54, 0x97, + 0x43, 0x9a, 0x02, 0x91, 0x53, 0x92, 0x78, 0x25, 0x73, 0x06, 0x45, 0x3e, 0xe8, 0x45, 0xff, 0x8b, 0x13, 0x51, 0x0f, + 0xf9, 0xfc, 0x27, 0x55, 0x3e, 0xcb, 0xa2, 0x7e, 0x14, 0x76, 0x7d, 0x19, 0x9b, 0x6c, 0x18, 0x03, 0x18, 0x34, 0xcc, + 0x21, 0xbb, 0x18, 0xd9, 0xaa, 0x76, 0xdd, 0x0c, 0x92, 0x73, 0x43, 0x7e, 0x36, 0x73, 0xc0, 0xfc, 0xfe, 0x5b, 0x28, + 0x1b, 0xbc, 0xc4, 0x8c, 0xc3, 0x7d, 0xe4, 0x27, 0x6f, 0x22, 0x0b, 0xfe, 0x70, 0x1a, 0x3a, 0x40, 0xd3, 0x21, 0xd4, + 0xe6, 0x8a, 0x09, 0x33, 0x03, 0x9b, 0xb2, 0x20, 0xa6, 0x45, 0x4f, 0x89, 0x1a, 0xff, 0xbd, 0x7f, 0xd6, 0x00, 0x34, + 0x7b, 0xe4, 0xcf, 0xd6, 0xe8, 0x40, 0xb7, 0xea, 0xd2, 0x47, 0xf7, 0x26, 0x99, 0x06, 0x00, 0x97, 0xdb, 0xeb, 0xb5, + 0xd8, 0x6e, 0xa7, 0x55, 0xc8, 0x3e, 0x98, 0xe1, 0xc6, 0xf1, 0x94, 0x9c, 0xb7, 0x29, 0x1b, 0x0b, 0x84, 0xa7, 0xcc, + 0x0a, 0x12, 0xbb, 0x6f, 0xdd, 0xb3, 0xb2, 0x7f, 0x8c, 0xff, 0xa5, 0xf1, 0xcb, 0x22, 0x3f, 0xdf, 0x6e, 0xa5, 0x12, + 0x78, 0xa5, 0x9f, 0xd1, 0x7b, 0x17, 0xc0, 0x72, 0x07, 0x91, 0x8c, 0x96, 0xf7, 0xd4, 0xa2, 0xea, 0xa9, 0x5f, 0x64, + 0xab, 0x71, 0xe3, 0xc4, 0x8e, 0xf2, 0xe6, 0xf3, 0x82, 0x8d, 0x40, 0xc5, 0xc3, 0x6b, 0x46, 0x98, 0xfe, 0x7d, 0x32, + 0x71, 0xea, 0x1d, 0x3b, 0x7b, 0x8f, 0x20, 0xeb, 0x89, 0xed, 0xdb, 0xb3, 0x2c, 0xfe, 0x1f, 0x8b, 0x93, 0x75, 0x02, + 0x4f, 0x0d, 0x82, 0xac, 0xfb, 0xcc, 0x0b, 0x2b, 0x40, 0x65, 0xf7, 0x28, 0xe3, 0xcb, 0xc3, 0xd0, 0x7f, 0xfd, 0xcc, + 0x19, 0x35, 0xba, 0x70, 0x8a, 0xe1, 0x9c, 0xa2, 0x31, 0x84, 0xe3, 0x8f, 0x4f, 0x27, 0xbd, 0xb8, 0x67, 0xfc, 0xa7, + 0x49, 0x2f, 0xac, 0xea, 0x35, 0x6d, 0x48, 0x1c, 0xff, 0xb0, 0xf9, 0x9b, 0x45, 0x1e, 0xec, 0x7c, 0xb5, 0x42, 0x8a, + 0xac, 0x0b, 0xa9, 0x4e, 0xab, 0x56, 0x55, 0x17, 0x03, 0xce, 0xd9, 0x1f, 0x8b, 0x97, 0x3a, 0xbb, 0x5f, 0xf4, 0x3f, + 0x9a, 0x79, 0x4d, 0xeb, 0xa3, 0x0f, 0xee, 0xa6, 0x50, 0x35, 0xfb, 0x19, 0xdd, 0x3b, 0xbd, 0xa3, 0x9c, 0xb2, 0x99, + 0x4b, 0x7c, 0xee, 0xab, 0xa5, 0xe7, 0x09, 0xb7, 0x16, 0x1a, 0x99, 0xa1, 0x3b, 0x75, 0x8f, 0xe0, 0x52, 0x24, 0x4d, + 0xcb, 0xde, 0xc2, 0x35, 0x13, 0xe9, 0x4c, 0x7f, 0x76, 0x92, 0xd2, 0x9b, 0xce, 0x67, 0x35, 0x45, 0xcc, 0xaf, 0x88, + 0x99, 0x71, 0x96, 0x04, 0x4f, 0x21, 0x22, 0xd0, 0xda, 0x8a, 0xf2, 0xa9, 0xa2, 0xba, 0xe2, 0x57, 0xbf, 0x9e, 0x65, + 0x81, 0x9f, 0x99, 0x4d, 0x75, 0x2b, 0x57, 0xf4, 0xd1, 0x69, 0x9e, 0xe5, 0x3a, 0x76, 0x20, 0x67, 0x1b, 0xe0, 0xc0, + 0xfe, 0x4d, 0x47, 0x30, 0xac, 0xad, 0xb9, 0x3f, 0x12, 0xbd, 0x31, 0x0a, 0xfe, 0x42, 0x00, 0x46, 0xa4, 0x68, 0xc3, + 0x3e, 0xda, 0x42, 0x17, 0x32, 0xaa, 0xf7, 0x27, 0x6e, 0xff, 0xbc, 0x71, 0xbd, 0xf3, 0x6b, 0xa7, 0x35, 0xa7, 0x54, + 0xe6, 0xe9, 0x74, 0xb4, 0x91, 0xdd, 0xf5, 0xb0, 0x0c, 0xf2, 0x5b, 0xbe, 0xd0, 0xe8, 0xc5, 0x2f, 0x1d, 0x6c, 0x69, + 0xf9, 0x11, 0xa9, 0x7a, 0x92, 0x08, 0xe4, 0x58, 0xcb, 0xc3, 0xab, 0xb9, 0x23, 0x95, 0x0a, 0x1c, 0xd5, 0x3d, 0x19, + 0xf9, 0x66, 0x4e, 0xd9, 0xb5, 0xa4, 0x1d, 0xc1, 0xc6, 0xb0, 0x6c, 0xbe, 0xe6, 0xd2, 0x2c, 0xb5, 0x5e, 0xd9, 0xb3, + 0x13, 0xe1, 0x05, 0x8b, 0x57, 0x62, 0x9b, 0x82, 0xcb, 0xaf, 0xc6, 0x92, 0xb9, 0x79, 0x3d, 0x91, 0x80, 0x59, 0xe6, + 0xd2, 0x6e, 0xf2, 0x19, 0xe9, 0x4a, 0xfd, 0x39, 0x2c, 0x4c, 0x9f, 0x7c, 0x63, 0x31, 0x41, 0xdb, 0xaa, 0x55, 0xb9, + 0xf2, 0x1c, 0xdf, 0xd0, 0xa4, 0xd8, 0x3b, 0xda, 0x33, 0xe9, 0x21, 0x1c, 0x89, 0xc1, 0xcd, 0xbc, 0xa5, 0x92, 0x32, + 0x8d, 0x63, 0x27, 0x49, 0xff, 0x55, 0x5f, 0x86, 0x49, 0x82, 0x83, 0x58, 0xfd, 0x07, 0xd5, 0x98, 0x01, 0x87, 0xd4, + 0x47, 0x27, 0x2a, 0x82, 0xd1, 0x4c, 0x21, 0xba, 0x41, 0xfd, 0x4a, 0x9d, 0x88, 0x67, 0x2f, 0x56, 0x38, 0xe9, 0xcb, + 0x1c, 0x69, 0x5e, 0xf8, 0x8e, 0xdd, 0x3e, 0x32, 0x80, 0x46, 0x61, 0x6e, 0x8c, 0x81, 0x5d, 0xd6, 0xa4, 0x2d, 0x05, + 0x37, 0x7a, 0x03, 0x4d, 0xe0, 0xe6, 0x3d, 0x9d, 0x85, 0x3e, 0x17, 0xe9, 0xc4, 0xe2, 0x8e, 0x76, 0x31, 0xb9, 0xd6, + 0x7c, 0x5d, 0xb0, 0x0b, 0xf9, 0xbb, 0xb9, 0x56, 0xde, 0xb6, 0x69, 0x2e, 0x54, 0x20, 0xc8, 0x51, 0xe0, 0x94, 0xcb, + 0x7b, 0xa2, 0x46, 0xc7, 0xc1, 0xeb, 0xd4, 0x86, 0xd2, 0x1f, 0xf8, 0x75, 0x10, 0x88, 0xce, 0x7e, 0xd0, 0xa6, 0xdf, + 0xb7, 0x54, 0x85, 0x59, 0xd4, 0x43, 0x2c, 0x89, 0x49, 0x77, 0x77, 0xeb, 0xa3, 0x8e, 0xcf, 0xea, 0x1a, 0xb7, 0xf0, + 0x12, 0x83, 0x2b, 0x38, 0x42, 0xab, 0x58, 0x48, 0x9e, 0x81, 0x4f, 0xb7, 0xb0, 0xf1, 0x63, 0xe6, 0x6e, 0x47, 0xe4, + 0xfe, 0xea, 0x7d, 0xc5, 0x91, 0xdd, 0x62, 0xac, 0x9e, 0x3c, 0x45, 0xec, 0x1d, 0xad, 0x32, 0xc3, 0x95, 0x6b, 0xde, + 0x2b, 0xdc, 0xf6, 0x9e, 0x4f, 0xf1, 0xc0, 0x0c, 0x02, 0x7b, 0x46, 0xcc, 0x8e, 0xb1, 0x7e, 0x6d, 0xd8, 0xdb, 0xbe, + 0x73, 0x5d, 0x0a, 0x18, 0xb5, 0x2e, 0xe8, 0x83, 0x20, 0xbe, 0xcf, 0x0c, 0x58, 0x7b, 0x0e, 0xcc, 0xde, 0xe8, 0x8e, + 0xdb, 0x24, 0xec, 0x4a, 0x7d, 0x3c, 0x3e, 0x64, 0xbd, 0x2b, 0x3d, 0x2a, 0x45, 0x1f, 0x05, 0x2e, 0x9a, 0x00, 0x31, + 0x07, 0x47, 0xb2, 0x17, 0x7b, 0xf2, 0x89, 0x98, 0x0b, 0x91, 0x8b, 0x66, 0xb8, 0x07, 0x04, 0x23, 0x87, 0x15, 0xb6, + 0xff, 0x88, 0xd2, 0x86, 0x87, 0x5b, 0x2c, 0x64, 0x98, 0xf3, 0x1a, 0xd7, 0xdd, 0xfd, 0x3b, 0x60, 0xce, 0x5d, 0xbd, + 0x45, 0xdf, 0xe9, 0x31, 0x28, 0xbd, 0x4f, 0x83, 0xa8, 0x55, 0xe4, 0x1e, 0x5e, 0x84, 0xf0, 0xba, 0xc8, 0x8b, 0x46, + 0x20, 0xdd, 0x1d, 0x86, 0xe1, 0x57, 0x10, 0x31, 0x7d, 0x2d, 0x01, 0x7f, 0xa2, 0x30, 0x10, 0x0b, 0x5e, 0x6e, 0xaa, + 0x4a, 0x5d, 0xd9, 0x7a, 0x0c, 0xb5, 0xf0, 0x0c, 0xac, 0xaa, 0x93, 0x8c, 0xe0, 0x6e, 0x73, 0x96, 0x32, 0xbf, 0xad, + 0xc8, 0x8f, 0x65, 0x5d, 0x1c, 0xd2, 0xa6, 0xbd, 0x8a, 0xdf, 0x32, 0xec, 0x05, 0x10, 0xa3, 0x2a, 0x33, 0x53, 0x25, + 0x22, 0x5f, 0x17, 0xa4, 0x8a, 0x94, 0x3d, 0x4b, 0xb6, 0x57, 0xf4, 0x57, 0xaf, 0xd8, 0x12, 0x67, 0xb6, 0x25, 0x27, + 0xfc, 0x54, 0x4d, 0xe2, 0xf9, 0xaf, 0xf2, 0xce, 0xfd, 0x6d, 0xfa, 0xfe, 0x7c, 0x98, 0xc4, 0x59, 0x2e, 0xe9, 0xba, + 0xb5, 0xb8, 0xf8, 0xa4, 0xf5, 0xb7, 0xab, 0x3d, 0x6a, 0xdf, 0xad, 0xe5, 0xf4, 0x76, 0xe4, 0x9a, 0xf9, 0x12, 0xd2, + 0xac, 0xf5, 0xe1, 0x24, 0x7f, 0x95, 0x61, 0x97, 0x37, 0x7a, 0xd0, 0xb4, 0x64, 0xfa, 0xe2, 0xe7, 0x8a, 0x6d, 0x19, + 0xba, 0x12, 0xbd, 0xf3, 0xd3, 0x17, 0xe3, 0xae, 0x11, 0xb3, 0x35, 0x90, 0x3c, 0x61, 0x5e, 0x44, 0x63, 0xcf, 0x8d, + 0x05, 0x02, 0xbd, 0x4f, 0xfb, 0x16, 0xcc, 0xd2, 0x6f, 0x9c, 0x28, 0xb9, 0x4f, 0xb0, 0x3f, 0xd2, 0x22, 0x18, 0xb8, + 0x73, 0x57, 0xbd, 0xe0, 0x38, 0x0b, 0x7d, 0xd4, 0xb5, 0xdc, 0x17, 0x31, 0x72, 0x9b, 0xe3, 0xf4, 0x6e, 0x29, 0x99, + 0x08, 0xfb, 0xc5, 0x53, 0xce, 0xac, 0xef, 0x7e, 0x99, 0x25, 0xad, 0xd5, 0x02, 0xfd, 0x8a, 0xab, 0xe7, 0x6e, 0xfd, + 0x27, 0x10, 0xbd, 0x9f, 0x76, 0x58, 0x2c, 0xad, 0xd4, 0x9d, 0xaa, 0xd2, 0x37, 0x78, 0x52, 0x06, 0xc8, 0x59, 0x40, + 0x67, 0xda, 0x5a, 0xee, 0x16, 0x46, 0xfd, 0xa5, 0xc7, 0xb9, 0xfe, 0xde, 0xca, 0x18, 0x1c, 0x42, 0xb4, 0xfd, 0x0a, + 0xe7, 0x71, 0x7b, 0x25, 0x5e, 0x0b, 0xaf, 0x28, 0x34, 0x5b, 0x1e, 0xbf, 0x54, 0x30, 0x89, 0x7e, 0x12, 0x91, 0x3b, + 0x3f, 0x5b, 0xb3, 0x30, 0x31, 0x9f, 0xce, 0x2d, 0xbf, 0x47, 0xa7, 0xe6, 0x02, 0x5a, 0xee, 0xf9, 0x81, 0x8b, 0xf9, + 0x3f, 0xcb, 0x2c, 0x4b, 0x6a, 0x85, 0x66, 0xd9, 0x36, 0xc0, 0xd1, 0x0d, 0x4f, 0x71, 0xe3, 0x39, 0x0e, 0x28, 0xb4, + 0x83, 0x52, 0x6f, 0xb5, 0x40, 0x8d, 0x14, 0x61, 0xa1, 0xa0, 0x90, 0x7e, 0x44, 0xf3, 0x28, 0x3b, 0x62, 0xc0, 0x48, + 0xb7, 0xfa, 0x9b, 0x5c, 0x5b, 0x64, 0x45, 0xab, 0xfd, 0xb2, 0x7c, 0xbf, 0x2f, 0x82, 0xe8, 0xbf, 0x5d, 0x80, 0x22, + 0xd6, 0x86, 0xec, 0x4d, 0xc0, 0x34, 0xa2, 0x98, 0xa2, 0xe0, 0xdb, 0x80, 0xa4, 0x50, 0x29, 0x7b, 0x17, 0xb6, 0x08, + 0x33, 0x97, 0x5a, 0x52, 0xc6, 0x98, 0x78, 0xde, 0x00, 0x74, 0xa4, 0xff, 0xda, 0xf8, 0x2e, 0x3b, 0x33, 0x1e, 0x26, + 0xe5, 0x1e, 0x11, 0x91, 0xa0, 0x9e, 0xca, 0x4a, 0xc0, 0x7e, 0xb3, 0x29, 0xbe, 0x15, 0x94, 0xa4, 0x49, 0xed, 0x45, + 0xb0, 0xdb, 0x86, 0x0c, 0x2e, 0xa3, 0xb5, 0x86, 0x82, 0x86, 0xef, 0x0d, 0xe3, 0x01, 0xab, 0x5c, 0xf4, 0x12, 0x9b, + 0xfc, 0x08, 0x9e, 0xa9, 0xe8, 0x2e, 0xdf, 0xa2, 0x8f, 0x77, 0x54, 0xe6, 0x65, 0xa7, 0x75, 0xed, 0xdd, 0x81, 0x41, + 0x18, 0x36, 0x3e, 0x35, 0xd0, 0x91, 0xbe, 0x1e, 0xb0, 0x41, 0xf3, 0x78, 0x86, 0x0d, 0x38, 0xa5, 0x2b, 0x32, 0x5a, + 0xe7, 0x23, 0xcb, 0x17, 0x7b, 0xfc, 0x3e, 0x1a, 0x21, 0x63, 0xe2, 0x08, 0xec, 0xa8, 0x01, 0x1e, 0x12, 0x66, 0x08, + 0x3f, 0xf6, 0x0e, 0xf6, 0xb5, 0x81, 0xff, 0x4a, 0x13, 0x50, 0x40, 0x8e, 0xf6, 0xb8, 0x90, 0x54, 0x3c, 0x86, 0x19, + 0x83, 0xc2, 0x87, 0x64, 0x28, 0x73, 0xfc, 0xef, 0xbb, 0x92, 0x62, 0xcd, 0x70, 0x57, 0x8c, 0x4c, 0x1b, 0xee, 0xbe, + 0x6b, 0xcc, 0x6f, 0xe9, 0xde, 0x51, 0x14, 0x3d, 0x1d, 0x03, 0x0f, 0xa1, 0x14, 0xa1, 0xec, 0xcc, 0x84, 0x2a, 0x00, + 0xfd, 0xa2, 0x19, 0x6d, 0x40, 0xeb, 0xc7, 0xc8, 0x1d, 0xdf, 0x5e, 0xc1, 0xc9, 0x45, 0xa2, 0xc0, 0xba, 0xf8, 0xfa, + 0x97, 0x4a, 0x7a, 0xef, 0xde, 0x25, 0x5b, 0xe5, 0xca, 0x9c, 0xda, 0xe2, 0xa1, 0x0b, 0xbe, 0x4c, 0xd7, 0xc7, 0xde, + 0xcb, 0x13, 0xa4, 0xa6, 0x61, 0xb5, 0x8e, 0x6d, 0xc2, 0x93, 0x16, 0xbb, 0xe4, 0xed, 0xfc, 0xe5, 0x49, 0x36, 0xf1, + 0x8a, 0xa5, 0x40, 0xa7, 0x67, 0x56, 0xc5, 0x36, 0xd2, 0xd3, 0x65, 0xc3, 0x67, 0x06, 0xf8, 0x3c, 0x1b, 0xc8, 0x3d, + 0xcf, 0xf5, 0xe7, 0xfa, 0xed, 0x92, 0x87, 0x84, 0x92, 0xdd, 0xd6, 0x38, 0xbd, 0x6b, 0x6c, 0x33, 0x1f, 0xcd, 0xdc, + 0x3e, 0xb6, 0x3e, 0xf3, 0x91, 0xc9, 0xd2, 0x05, 0x25, 0x61, 0x7b, 0x3c, 0x24, 0x9d, 0x6c, 0xb2, 0xe0, 0xcc, 0xa9, + 0x2f, 0x91, 0xcb, 0xe2, 0xbc, 0xae, 0x34, 0x17, 0x36, 0x2b, 0xe8, 0x32, 0x80, 0x53, 0x9d, 0x3a, 0x09, 0xae, 0x2a, + 0x02, 0xa7, 0xa6, 0x66, 0xaa, 0x28, 0x9e, 0xb2, 0x66, 0xbb, 0x39, 0x51, 0xfd, 0x14, 0x2d, 0x2e, 0x75, 0x2a, 0x4a, + 0xd4, 0x4c, 0xb6, 0xcc, 0x14, 0xc8, 0x64, 0x51, 0xa4, 0x39, 0x89, 0x15, 0x0e, 0xfa, 0x9e, 0x53, 0x24, 0x7b, 0xd1, + 0x6e, 0x3e, 0x5e, 0xd9, 0x5a, 0xb2, 0xc2, 0x68, 0x66, 0xab, 0x79, 0x76, 0x22, 0x15, 0xdb, 0x07, 0xca, 0xa1, 0x70, + 0xdf, 0x26, 0xb0, 0x52, 0x23, 0xe5, 0xa5, 0xa8, 0x23, 0x35, 0x3c, 0xc5, 0x5f, 0x9b, 0x6e, 0x88, 0xd1, 0x6c, 0xd8, + 0xd1, 0x46, 0xb3, 0xd9, 0x0c, 0x8a, 0x4d, 0x8d, 0x43, 0xab, 0xd4, 0x74, 0x1b, 0x91, 0xaf, 0x50, 0x35, 0xb2, 0x6f, + 0xac, 0x2c, 0x88, 0x25, 0x73, 0x88, 0xd7, 0x50, 0x98, 0x24, 0xf7, 0x28, 0xb6, 0xe8, 0xf5, 0xa2, 0xcd, 0xcd, 0x91, + 0x63, 0x43, 0x76, 0xae, 0xe2, 0x5c, 0xa6, 0x2b, 0x91, 0x47, 0x81, 0x50, 0x58, 0x89, 0xa4, 0x04, 0x93, 0x31, 0x4f, + 0xdf, 0xf8, 0x29, 0xe9, 0xb9, 0x47, 0x40, 0x34, 0xfb, 0x82, 0x6a, 0x45, 0x7d, 0x11, 0x23, 0x3e, 0x92, 0x90, 0x63, + 0xf8, 0x8a, 0x61, 0xf8, 0xde, 0xa6, 0xa2, 0xff, 0x6a, 0xe7, 0x53, 0x13, 0x65, 0x72, 0x54, 0xed, 0x10, 0x69, 0x03, + 0xb1, 0x35, 0x40, 0x3c, 0x4d, 0xc7, 0x12, 0x94, 0x46, 0x8f, 0xc1, 0xce, 0xe7, 0xe5, 0x69, 0x27, 0xd4, 0xe2, 0x48, + 0x77, 0x99, 0x9b, 0x00, 0x67, 0xfd, 0x30, 0xbd, 0x4d, 0xcc, 0xee, 0xfe, 0xcc, 0x01, 0xdd, 0x89, 0x71, 0x84, 0x8f, + 0x66, 0x97, 0x55, 0x08, 0x4f, 0xfc, 0x3b, 0xaf, 0xda, 0x94, 0x84, 0x13, 0xe2, 0x8d, 0x63, 0x03, 0x98, 0xce, 0xb4, + 0xa7, 0x6a, 0x39, 0x10, 0x29, 0x7e, 0x0d, 0xbe, 0xc1, 0x95, 0xd0, 0xa0, 0x20, 0x51, 0x3f, 0x8f, 0x5c, 0x13, 0x53, + 0x3d, 0xce, 0x7f, 0x44, 0x28, 0x03, 0x83, 0x04, 0x32, 0x2a, 0xd8, 0x3d, 0x6f, 0x8d, 0x28, 0xd6, 0x7a, 0xd2, 0xb2, + 0xcb, 0x99, 0xeb, 0x36, 0xb5, 0x33, 0x7b, 0xdf, 0x0a, 0x0e, 0x04, 0xfd, 0xe5, 0x56, 0xa6, 0x1f, 0x01, 0x06, 0xc3, + 0xac, 0x30, 0xff, 0x89, 0x0c, 0x9a, 0x2b, 0x64, 0xd4, 0x5d, 0x77, 0xd5, 0x3b, 0xc1, 0xd8, 0x99, 0x8c, 0x23, 0x9f, + 0xfc, 0x3c, 0x70, 0xf7, 0xad, 0x48, 0x35, 0x9e, 0xb9, 0x8d, 0x91, 0x4f, 0x26, 0x81, 0xd9, 0xb6, 0x6e, 0x54, 0x53, + 0x26, 0x38, 0x12, 0x31, 0x95, 0x7e, 0x73, 0x1f, 0xb7, 0xe1, 0x59, 0x7e, 0xf0, 0xdf, 0x6f, 0xd3, 0xc4, 0xb9, 0x17, + 0x76, 0x61, 0xba, 0x89, 0x37, 0x0e, 0xba, 0xdf, 0xb5, 0x8f, 0xe6, 0x1a, 0x0f, 0x53, 0x91, 0xd4, 0x76, 0xa2, 0xce, + 0x47, 0xea, 0xe1, 0x35, 0x9d, 0x9f, 0x49, 0xb3, 0xce, 0xf5, 0x9f, 0xaa, 0x0e, 0x06, 0xfd, 0x15, 0x73, 0xb6, 0x45, + 0xbc, 0xd7, 0x9e, 0x6b, 0x29, 0xbc, 0x83, 0xaf, 0xcc, 0xb9, 0x15, 0xf4, 0x2b, 0x17, 0x95, 0x67, 0xaf, 0x49, 0xd7, + 0x78, 0x52, 0x56, 0x13, 0x36, 0xf5, 0x20, 0x4e, 0xf9, 0xab, 0xe0, 0x18, 0xa0, 0x37, 0x54, 0x8d, 0x91, 0xb2, 0x8b, + 0xf7, 0xd5, 0xc0, 0x99, 0x0a, 0xf1, 0x8f, 0x82, 0xa1, 0x51, 0xda, 0x96, 0xea, 0x18, 0x5b, 0xef, 0x31, 0x8f, 0x47, + 0x95, 0xcb, 0xea, 0x09, 0x0b, 0x4e, 0x9d, 0x9d, 0xdf, 0xfd, 0x88, 0x6b, 0x1e, 0x60, 0x9d, 0xd5, 0xfe, 0x0a, 0x9c, + 0xd7, 0xfe, 0x33, 0xdd, 0x7c, 0x28, 0xba, 0x27, 0x5a, 0x6f, 0xe6, 0xde, 0xf3, 0x6c, 0xd6, 0x9f, 0xef, 0x45, 0x68, + 0x35, 0x5c, 0x67, 0x7c, 0x7a, 0xcb, 0xef, 0x40, 0x67, 0x3b, 0xe8, 0x1a, 0xef, 0x2b, 0xcd, 0x7b, 0x3b, 0x0b, 0x56, + 0xaa, 0xa8, 0x75, 0x8e, 0x1d, 0xba, 0xd6, 0x78, 0x3c, 0xb8, 0xc8, 0xa4, 0xb1, 0x3a, 0x59, 0x79, 0x68, 0x85, 0xca, + 0xd7, 0x8b, 0xb8, 0x63, 0x27, 0xd1, 0xcd, 0xb2, 0x11, 0x25, 0x12, 0xe4, 0x6f, 0x83, 0x42, 0x31, 0x1c, 0x32, 0xe1, + 0x61, 0xdc, 0x9b, 0x08, 0x61, 0x5e, 0x4b, 0xb9, 0x10, 0xab, 0x1d, 0x5e, 0xaf, 0xd0, 0x23, 0xe0, 0x60, 0x49, 0x95, + 0xb4, 0x91, 0x88, 0xba, 0x94, 0x7d, 0x58, 0xdd, 0xfe, 0x50, 0x2f, 0xee, 0xca, 0x5f, 0xd5, 0xb6, 0x66, 0xd1, 0xfc, + 0x8b, 0x12, 0x8e, 0x95, 0x08, 0x9b, 0x29, 0xb6, 0x75, 0xf4, 0x7f, 0x44, 0x85, 0x0e, 0x9d, 0x0b, 0x80, 0xda, 0x0f, + 0x95, 0x05, 0x8a, 0x62, 0x04, 0x68, 0x3f, 0xa9, 0xb2, 0x90, 0x7a, 0xc7, 0x1f, 0xcc, 0xae, 0x5b, 0x86, 0x2c, 0x17, + 0xc1, 0x58, 0x9d, 0x6d, 0x00, 0x08, 0xab, 0x4e, 0x60, 0x02, 0x51, 0x34, 0x8a, 0xb2, 0x29, 0x37, 0xd8, 0x2d, 0x5e, + 0x41, 0xb4, 0xfa, 0xfa, 0x4c, 0xf4, 0x8c, 0xac, 0xa4, 0x2a, 0x59, 0xe6, 0xfb, 0x57, 0x16, 0xcc, 0x95, 0x34, 0x7c, + 0x6b, 0xcf, 0xed, 0x6c, 0xd1, 0x79, 0x7f, 0x57, 0xd3, 0xbf, 0xb0, 0x9b, 0xe1, 0x6f, 0xba, 0x01, 0x33, 0xcc, 0x27, + 0xb7, 0xdf, 0x4f, 0xb1, 0x26, 0x1c, 0xff, 0xc8, 0x2a, 0x86, 0x85, 0x2b, 0x08, 0x16, 0x35, 0x46, 0x9c, 0x92, 0x7f, + 0xec, 0x03, 0x05, 0xda, 0xc3, 0x86, 0x02, 0x83, 0x51, 0xe5, 0xa1, 0x12, 0xe9, 0x53, 0xf1, 0xcb, 0x36, 0x90, 0x41, + 0x27, 0x1c, 0x4a, 0x06, 0x76, 0x6a, 0xd7, 0x2a, 0x31, 0x5b, 0x73, 0xeb, 0x3f, 0x66, 0x05, 0x9b, 0x61, 0xc0, 0x12, + 0xf5, 0x90, 0x46, 0x7a, 0x59, 0xb5, 0x08, 0xef, 0x0d, 0x4d, 0xdd, 0x43, 0x90, 0x5a, 0x16, 0x09, 0x7f, 0x60, 0x1e, + 0xa0, 0x46, 0x30, 0x66, 0x9a, 0x67, 0xa5, 0x1c, 0x42, 0x2e, 0xd3, 0xe3, 0x54, 0x14, 0xa3, 0x96, 0xe5, 0x3a, 0x63, + 0x15, 0x47, 0x5e, 0xb3, 0x38, 0x6f, 0x66, 0x51, 0xae, 0x51, 0x36, 0x2c, 0xb8, 0xfe, 0x0c, 0x89, 0x46, 0xb1, 0x41, + 0x43, 0xec, 0x8e, 0x73, 0x52, 0xa6, 0x39, 0x47, 0x1d, 0x92, 0x5b, 0x72, 0x8f, 0x58, 0xcd, 0x6c, 0x25, 0x4c, 0x8e, + 0x56, 0x6d, 0x46, 0xd8, 0xee, 0x68, 0x1c, 0x33, 0x4d, 0x1c, 0x4f, 0x21, 0xf4, 0x40, 0x9b, 0x3d, 0x2d, 0xd9, 0x71, + 0xf1, 0x7f, 0x90, 0x02, 0xba, 0x79, 0xb4, 0x42, 0x30, 0x17, 0xfb, 0x18, 0xa5, 0x86, 0x9b, 0x63, 0x17, 0xd8, 0xb0, + 0xfd, 0xe7, 0x26, 0xba, 0xa2, 0xe3, 0xb9, 0x5e, 0xa9, 0x91, 0x83, 0x38, 0xb1, 0x3e, 0xdb, 0x83, 0xd0, 0x7a, 0x44, + 0xc2, 0x81, 0xb2, 0xce, 0x7a, 0x65, 0x1e, 0xeb, 0xd2, 0x7f, 0xfd, 0x4b, 0x6d, 0x09, 0x41, 0x60, 0x58, 0x3d, 0xd8, + 0xfe, 0x04, 0x56, 0x5c, 0xc8, 0x12, 0x99, 0xf1, 0xc2, 0xbf, 0x62, 0x87, 0xaf, 0x69, 0x56, 0x56, 0x3a, 0xc7, 0xe5, + 0xcc, 0x42, 0xa7, 0xa1, 0x6a, 0x8e, 0x79, 0x1e, 0x32, 0x16, 0xd3, 0x0b, 0x83, 0x9c, 0x0b, 0x02, 0x1a, 0x9a, 0x73, + 0xee, 0xca, 0x7a, 0x93, 0xe0, 0x36, 0x82, 0x62, 0x29, 0x40, 0x57, 0xe8, 0x32, 0xbd, 0xf3, 0xcd, 0x30, 0x0e, 0x86, + 0xdc, 0xcc, 0x00, 0x84, 0x2d, 0x11, 0x54, 0x32, 0xf0, 0xac, 0xd8, 0xb3, 0x92, 0x73, 0x30, 0xe7, 0x15, 0xea, 0xbd, + 0x46, 0xfa, 0x1b, 0x24, 0x5c, 0xa0, 0x5a, 0x29, 0x70, 0x32, 0xa0, 0xcb, 0x52, 0x2b, 0x34, 0x2f, 0x11, 0x62, 0xac, + 0x01, 0x49, 0x6d, 0xe2, 0x97, 0xf3, 0x02, 0xf7, 0xbc, 0x9f, 0x0d, 0x67, 0x5d, 0x97, 0x00, 0xf2, 0x30, 0x2f, 0xbf, + 0xbd, 0xcc, 0x70, 0x90, 0x13, 0x90, 0xb8, 0x18, 0x98, 0x39, 0xa1, 0x9d, 0x5d, 0xc1, 0x96, 0xba, 0x18, 0x55, 0xb8, + 0xad, 0x61, 0xb2, 0x14, 0x95, 0x6d, 0xb8, 0x3e, 0x86, 0xce, 0x48, 0xfa, 0xce, 0x4f, 0x33, 0x09, 0x33, 0x74, 0xcd, + 0xc9, 0x54, 0xee, 0x04, 0x9b, 0x4f, 0x9a, 0x81, 0xbe, 0xd8, 0xfa, 0x73, 0xe8, 0x7f, 0xda, 0xd8, 0x04, 0xd3, 0xf7, + 0x8c, 0x64, 0xc4, 0x54, 0xa2, 0xcf, 0x1b, 0xcc, 0x3e, 0xed, 0xf7, 0xf9, 0x0e, 0x16, 0xeb, 0xcb, 0xd8, 0xcb, 0x8a, + 0x8d, 0xfa, 0xd8, 0x5a, 0xc6, 0x24, 0x71, 0x2c, 0xb9, 0x3d, 0x28, 0x29, 0xa8, 0xcc, 0x9b, 0xa8, 0x21, 0x23, 0xa6, + 0x35, 0x27, 0x3b, 0xf1, 0xbf, 0x73, 0xc5, 0xcc, 0xc4, 0xc0, 0x8f, 0xb1, 0xc7, 0x3e, 0xbe, 0x7a, 0xe2, 0xad, 0xf6, + 0x23, 0x67, 0xe8, 0x98, 0x3c, 0x40, 0x20, 0x17, 0x98, 0x97, 0x2e, 0x30, 0xe7, 0xd6, 0x8a, 0x35, 0x6b, 0x6a, 0xe5, + 0x3f, 0xbb, 0x2b, 0x7d, 0x60, 0xec, 0x13, 0x41, 0x7f, 0x36, 0xed, 0x66, 0xec, 0x1b, 0xb3, 0x57, 0x03, 0x4e, 0x1d, + 0xcc, 0x6c, 0xbc, 0xa9, 0xf4, 0x1f, 0x6a, 0x73, 0xc5, 0x02, 0x14, 0x39, 0x1b, 0xf9, 0xa4, 0xa9, 0x08, 0xfe, 0xb8, + 0x3a, 0x7b, 0xb1, 0xdd, 0xa2, 0x50, 0x70, 0x65, 0x34, 0xe1, 0x5d, 0x46, 0x3e, 0xd1, 0xd0, 0x06, 0x6f, 0xe4, 0x8d, + 0x6d, 0x5c, 0x46, 0xfb, 0x68, 0x3f, 0x07, 0xb1, 0x0b, 0x82, 0xb6, 0x26, 0x16, 0x04, 0x59, 0x53, 0xe7, 0x0d, 0x23, + 0x12, 0xfc, 0xd6, 0x5a, 0xe9, 0xbc, 0x8e, 0xbd, 0xd2, 0x1d, 0xe7, 0x43, 0x22, 0x46, 0xe0, 0xb6, 0xe8, 0x7a, 0x4b, + 0x42, 0x19, 0x97, 0x8e, 0x4e, 0x26, 0x78, 0xd4, 0x26, 0x4e, 0xaa, 0x6d, 0xaf, 0x47, 0x1d, 0x1e, 0xf5, 0xdd, 0xbc, + 0x18, 0x94, 0xb6, 0x3b, 0xfa, 0x6f, 0xe1, 0xad, 0xcc, 0x91, 0xc7, 0xb5, 0xbe, 0xd3, 0xdc, 0x02, 0xbd, 0x89, 0xe8, + 0x44, 0x51, 0x27, 0x9c, 0xbc, 0x52, 0x8e, 0xff, 0x0b, 0x85, 0x15, 0x0c, 0x81, 0xc9, 0x4c, 0x24, 0xaa, 0x2d, 0x48, + 0x67, 0xa1, 0xbf, 0xf5, 0xf1, 0xb5, 0x42, 0x16, 0xd8, 0x62, 0x06, 0x71, 0xa8, 0x07, 0x8d, 0xe0, 0x25, 0x14, 0x88, + 0xe2, 0xde, 0x19, 0x1a, 0x83, 0x1e, 0x94, 0x3b, 0xa4, 0x81, 0x62, 0xd0, 0xb2, 0x14, 0x1a, 0xda, 0x84, 0x54, 0xbb, + 0xdf, 0x1b, 0xca, 0xfa, 0x25, 0x37, 0xd4, 0x28, 0xa2, 0x51, 0x6f, 0x1d, 0x24, 0x20, 0xe8, 0x15, 0x07, 0x69, 0xa0, + 0xbc, 0x5e, 0x12, 0x23, 0x96, 0xf1, 0x38, 0xc8, 0xd5, 0xc2, 0xe3, 0x95, 0x90, 0x53, 0xb3, 0x42, 0xc8, 0x31, 0x80, + 0x61, 0xec, 0x81, 0x7b, 0x39, 0xec, 0x60, 0x11, 0xf0, 0xbc, 0x5c, 0x51, 0xcf, 0x46, 0xb1, 0xb0, 0xfd, 0xbb, 0xbc, + 0x98, 0x5f, 0xd2, 0xde, 0x26, 0x29, 0x8f, 0x55, 0x9a, 0x4a, 0xf0, 0xdd, 0x9f, 0xde, 0xc5, 0x7c, 0x2c, 0x59, 0xb3, + 0xa5, 0x32, 0x07, 0x13, 0xa2, 0xeb, 0x90, 0x91, 0x3e, 0x55, 0xc5, 0xb1, 0x49, 0x01, 0x35, 0x1c, 0x87, 0x9d, 0x0b, + 0xc2, 0xe3, 0x84, 0x35, 0x9c, 0x4b, 0xcc, 0x61, 0x87, 0x0a, 0x36, 0xc2, 0xe8, 0x86, 0x12, 0x62, 0x49, 0x6d, 0xc4, + 0xb7, 0x03, 0x5c, 0x82, 0xef, 0x17, 0x5a, 0x79, 0x1f, 0x20, 0xfe, 0xd8, 0xa4, 0x33, 0x40, 0x2e, 0xb1, 0xb2, 0x98, + 0xb0, 0xed, 0xdf, 0x2a, 0x6d, 0x2b, 0x0f, 0xd3, 0xcd, 0xbd, 0x39, 0xbb, 0x03, 0x85, 0x33, 0x27, 0x19, 0xf9, 0x31, + 0xe9, 0x51, 0x39, 0x93, 0xff, 0xdc, 0x30, 0x06, 0x64, 0xe6, 0x0e, 0xf6, 0x95, 0xc0, 0x98, 0xbe, 0xd2, 0xd1, 0x84, + 0x7f, 0x89, 0x94, 0x9f, 0x8d, 0x46, 0x4c, 0x5e, 0x61, 0xc8, 0x55, 0xfa, 0x4a, 0xbf, 0xcf, 0x5c, 0xf4, 0x52, 0xde, + 0x38, 0xc6, 0xa8, 0xb8, 0xc9, 0xf8, 0xc5, 0xc8, 0x16, 0x22, 0xf5, 0x66, 0xcc, 0xb6, 0x3f, 0x5b, 0xa2, 0x7b, 0x86, + 0x07, 0x92, 0xa0, 0x71, 0xa3, 0x40, 0x01, 0x76, 0x31, 0xc1, 0x90, 0xdc, 0x01, 0x93, 0xa6, 0x69, 0x9e, 0xa7, 0x50, + 0xd7, 0x6a, 0x38, 0xa9, 0x6c, 0xab, 0xbb, 0xac, 0x4c, 0x65, 0xdb, 0xc1, 0x70, 0x8d, 0x82, 0xc4, 0x51, 0xe3, 0x14, + 0x15, 0xb3, 0xea, 0x69, 0x52, 0x86, 0x05, 0x44, 0x5a, 0x71, 0x8e, 0xdf, 0x5c, 0x9a, 0x4c, 0x67, 0xa7, 0xd8, 0x2b, + 0x3c, 0x4f, 0x85, 0x08, 0x76, 0x67, 0x15, 0x09, 0xbb, 0xb6, 0x65, 0x1d, 0x2d, 0x64, 0xee, 0x5b, 0x17, 0xe8, 0x12, + 0xe2, 0x07, 0x6f, 0xf5, 0xdb, 0xfd, 0x04, 0xec, 0x20, 0x8c, 0xf5, 0x11, 0x5d, 0x7c, 0xd4, 0x0b, 0x4a, 0x2b, 0x3f, + 0x09, 0xce, 0xd9, 0x66, 0xe9, 0xfd, 0x2f, 0x58, 0xdf, 0x94, 0x17, 0x0b, 0x0a, 0x85, 0x15, 0xcb, 0x52, 0x5c, 0xb5, + 0x8c, 0xcf, 0x51, 0x85, 0x55, 0xc8, 0xb1, 0x87, 0x1e, 0x37, 0x10, 0xa9, 0x65, 0x91, 0x34, 0x69, 0xee, 0xac, 0x44, + 0xa6, 0x6b, 0xb0, 0xf3, 0x4a, 0x00, 0x76, 0x6c, 0x52, 0xd5, 0x8b, 0x85, 0xa7, 0x24, 0xc1, 0xd1, 0xad, 0x90, 0xbb, + 0x50, 0x65, 0x0f, 0x14, 0x62, 0x58, 0x07, 0x58, 0x38, 0x2b, 0x58, 0x12, 0xb6, 0x0f, 0xab, 0xf1, 0x63, 0x54, 0x5b, + 0xc0, 0xf8, 0x10, 0x42, 0x7d, 0xb7, 0x83, 0x8e, 0xa2, 0xa3, 0x35, 0x9a, 0xdc, 0xe3, 0x00, 0x19, 0xf4, 0x73, 0x3f, + 0x15, 0x5c, 0xf2, 0x80, 0xbc, 0x18, 0x39, 0x89, 0xab, 0xf1, 0xa6, 0x65, 0xea, 0x5c, 0xf9, 0xee, 0x4b, 0x1b, 0x61, + 0x5d, 0x20, 0x2e, 0xe4, 0x7d, 0xec, 0x90, 0x7d, 0x77, 0x18, 0xad, 0xae, 0x9b, 0x27, 0x8b, 0xfc, 0x59, 0x56, 0x4d, + 0x45, 0xf8, 0xd3, 0xf7, 0x1b, 0x6a, 0x73, 0x16, 0x50, 0xee, 0xbd, 0x5e, 0x70, 0x8a, 0x7a, 0x47, 0x05, 0x22, 0x98, + 0x64, 0xf8, 0xed, 0x23, 0xd2, 0x16, 0x24, 0x62, 0xcd, 0x87, 0x4b, 0xaf, 0x59, 0x7f, 0x0b, 0x82, 0x55, 0x13, 0xe1, + 0xec, 0x57, 0x1a, 0xc4, 0xc1, 0x4b, 0x11, 0x92, 0xae, 0x08, 0x06, 0x3a, 0x2a, 0x88, 0xad, 0xd8, 0xca, 0x5e, 0x56, + 0x6b, 0x08, 0x44, 0x9c, 0x83, 0xcd, 0x67, 0x96, 0xe1, 0x39, 0xf1, 0xea, 0x97, 0x07, 0x29, 0x5c, 0x8c, 0x41, 0xff, + 0xab, 0x65, 0xe1, 0x07, 0x07, 0x07, 0x56, 0x46, 0x56, 0x8e, 0x7a, 0xd7, 0x4b, 0xe5, 0xb6, 0xac, 0xe3, 0xd6, 0xaa, + 0xf7, 0xe4, 0x05, 0x28, 0x8d, 0x36, 0x83, 0x64, 0xb7, 0x8e, 0x99, 0x1a, 0xc3, 0x43, 0x56, 0x8b, 0xfa, 0x98, 0x70, + 0x87, 0xbd, 0x91, 0x86, 0xbd, 0x83, 0x89, 0x68, 0xbc, 0x6f, 0xff, 0xc4, 0x48, 0x43, 0xc2, 0x74, 0xcc, 0x21, 0x77, + 0x50, 0x66, 0x4c, 0x4f, 0x05, 0x6d, 0xc7, 0x11, 0xcf, 0x45, 0x92, 0xce, 0xfd, 0x2b, 0xc3, 0xfb, 0x0b, 0x19, 0x5b, + 0x42, 0x46, 0x77, 0x24, 0xa5, 0x08, 0xd7, 0xd2, 0x60, 0x60, 0x8c, 0x60, 0x3e, 0x25, 0x9a, 0x88, 0x65, 0xb7, 0xb9, + 0x20, 0xb1, 0xcf, 0xd5, 0x92, 0xbd, 0x55, 0x2c, 0xa6, 0x04, 0x2d, 0x8a, 0x5e, 0xbc, 0x5c, 0x99, 0x31, 0xe1, 0xd1, + 0xb5, 0x71, 0x13, 0x23, 0x76, 0x67, 0x56, 0x7b, 0x1b, 0x3c, 0x68, 0x9f, 0x7f, 0xbd, 0x51, 0xbc, 0xb8, 0x5d, 0xbe, + 0x84, 0xe0, 0x07, 0x4f, 0x93, 0xc5, 0x50, 0x06, 0xb9, 0xd8, 0x70, 0xc1, 0x03, 0x59, 0x44, 0x6d, 0xb7, 0x1e, 0x23, + 0x36, 0xcf, 0x27, 0x9f, 0xb6, 0x30, 0x3c, 0x93, 0x93, 0xc1, 0xfe, 0x45, 0x07, 0xbf, 0x01, 0x5a, 0x37, 0x29, 0xf2, + 0xef, 0x4a, 0xd5, 0x41, 0x46, 0xf0, 0xf1, 0xcb, 0xed, 0x2f, 0xca, 0x50, 0xd3, 0x33, 0x9a, 0x86, 0xdd, 0xf2, 0xf7, + 0xc9, 0x29, 0xd8, 0x77, 0x65, 0x00, 0xa8, 0xd3, 0xa5, 0x8c, 0xf8, 0x9c, 0x7c, 0x83, 0x30, 0x00, 0x22, 0xbf, 0xf9, + 0x55, 0x3b, 0x3e, 0x36, 0xc7, 0xe5, 0x0f, 0x6d, 0x7b, 0x96, 0x88, 0xfe, 0xae, 0x0d, 0xb3, 0x1d, 0xfb, 0x80, 0x15, + 0x0f, 0xa3, 0x44, 0xb4, 0xac, 0xf9, 0x90, 0xb9, 0x4f, 0xf1, 0xb0, 0x79, 0xb4, 0x6a, 0x23, 0x8a, 0x6c, 0xb0, 0x5d, + 0xb2, 0xbf, 0xd0, 0xd2, 0xf9, 0x66, 0x87, 0x66, 0x50, 0xb7, 0x47, 0xc8, 0xab, 0x08, 0x20, 0x1e, 0x83, 0xc1, 0x7f, + 0x6d, 0xe6, 0x3d, 0x5b, 0xac, 0x00, 0x3f, 0x3b, 0x76, 0xfe, 0xf2, 0x7c, 0x6a, 0x11, 0x04, 0x7d, 0xd6, 0x3c, 0xaa, + 0x47, 0x44, 0xd2, 0x4f, 0x67, 0x5b, 0xbd, 0x4f, 0x87, 0x51, 0x89, 0x47, 0x6c, 0xda, 0xfe, 0x1d, 0x8b, 0xba, 0xd8, + 0xde, 0xb3, 0xe9, 0xfc, 0xb9, 0x29, 0x74, 0x06, 0x91, 0xda, 0xc6, 0x99, 0x8c, 0x64, 0x47, 0xa6, 0x01, 0x0d, 0xd1, + 0x5e, 0x28, 0xaf, 0x1f, 0x50, 0x32, 0x0a, 0xe4, 0x18, 0x41, 0xae, 0x8d, 0x8c, 0x2d, 0x27, 0x4b, 0x10, 0x86, 0x25, + 0xce, 0xef, 0xa9, 0x43, 0xd0, 0x4b, 0x85, 0xe4, 0xec, 0x22, 0x5c, 0x6f, 0x87, 0x34, 0x1a, 0x00, 0x4a, 0x8d, 0xb7, + 0x09, 0x1e, 0xb6, 0x20, 0x46, 0x2a, 0xb2, 0x22, 0xf1, 0xa7, 0xc4, 0x45, 0xe5, 0x18, 0x8f, 0x00, 0x24, 0xc6, 0xf1, + 0x50, 0xea, 0x3c, 0xa8, 0x43, 0xf2, 0x8a, 0x89, 0x39, 0xd2, 0xb3, 0x0a, 0x3d, 0x98, 0x69, 0x68, 0x73, 0x35, 0x9a, + 0x2a, 0x68, 0x0e, 0x4a, 0xff, 0x81, 0xea, 0x2a, 0x1f, 0x92, 0x47, 0x06, 0x41, 0x18, 0xae, 0xd6, 0x5b, 0xea, 0xf7, + 0x15, 0x42, 0x8b, 0x03, 0x33, 0xc9, 0x20, 0xce, 0x8d, 0x0f, 0x5b, 0x5d, 0xe3, 0x8b, 0x7a, 0x02, 0x34, 0x27, 0xae, + 0x7c, 0xf8, 0x78, 0x32, 0x50, 0x38, 0x41, 0xc9, 0xe8, 0x4f, 0x50, 0x53, 0x2d, 0xe9, 0x76, 0x1e, 0x37, 0xdd, 0x94, + 0xaf, 0x92, 0x5b, 0x6a, 0x66, 0x29, 0x7a, 0x2d, 0xe5, 0x81, 0x66, 0xbb, 0x95, 0xf5, 0xd7, 0x7f, 0x6a, 0xf8, 0x04, + 0xd0, 0x45, 0xc2, 0xca, 0xc4, 0xb7, 0xa8, 0xc1, 0x2f, 0x3e, 0x1c, 0x9c, 0x8c, 0x61, 0x7b, 0xa8, 0xc5, 0xdc, 0xe1, + 0x38, 0xc7, 0xfe, 0x3d, 0x90, 0x1b, 0xdc, 0x4a, 0xa0, 0xe4, 0x6b, 0x59, 0x84, 0x99, 0xcc, 0x62, 0xa0, 0x72, 0x35, + 0xe8, 0x3a, 0xb0, 0x90, 0x35, 0xb5, 0xe6, 0x87, 0xfe, 0xa7, 0x0a, 0x32, 0xf7, 0x6c, 0x95, 0x02, 0x24, 0xc8, 0xb7, + 0xd2, 0x36, 0xed, 0x7d, 0x8b, 0xdc, 0x41, 0xf7, 0x08, 0x16, 0xb5, 0xdd, 0x61, 0x02, 0x68, 0xa1, 0x83, 0x10, 0x52, + 0xe7, 0x53, 0x28, 0x7a, 0xb9, 0x49, 0x26, 0x74, 0xae, 0x05, 0x9e, 0x2f, 0x1d, 0x1c, 0xfd, 0xfb, 0xe3, 0x81, 0x72, + 0x45, 0x02, 0x97, 0x13, 0x7c, 0x0a, 0x9b, 0xda, 0x9c, 0x01, 0x65, 0xa4, 0x7d, 0x75, 0xb8, 0x62, 0x1f, 0x05, 0xac, + 0x0b, 0x9d, 0x59, 0x08, 0x15, 0x99, 0xec, 0x48, 0xd8, 0x17, 0x45, 0x33, 0xc4, 0x79, 0xc1, 0x55, 0x6c, 0x03, 0x9f, + 0xdb, 0xa4, 0x83, 0x98, 0x8b, 0xb6, 0x05, 0x1f, 0x0b, 0xaa, 0xcc, 0x09, 0x8b, 0x6e, 0x80, 0xd1, 0x5e, 0x7b, 0xa9, + 0xf5, 0x83, 0x76, 0x42, 0x67, 0xc5, 0xbd, 0xeb, 0x2a, 0xc2, 0xc0, 0x27, 0xd8, 0xa9, 0xfd, 0x2b, 0x8a, 0xe3, 0x6f, + 0x9b, 0x71, 0xb4, 0xe0, 0x53, 0x04, 0x06, 0x90, 0x90, 0x6e, 0x98, 0x6d, 0xcd, 0x08, 0x3a, 0x7e, 0x08, 0x35, 0x4a, + 0x01, 0x29, 0x8d, 0x30, 0x38, 0xca, 0xe4, 0x37, 0x41, 0x86, 0xe4, 0xbc, 0x9c, 0xa3, 0x87, 0x21, 0x46, 0x0e, 0x48, + 0x65, 0xae, 0x6c, 0xc7, 0x5e, 0x55, 0x4f, 0x85, 0x3c, 0x71, 0x0e, 0x62, 0x31, 0xf4, 0xc8, 0x88, 0x3f, 0xc8, 0x54, + 0x67, 0xa0, 0x89, 0x01, 0x33, 0x82, 0x03, 0xb1, 0x29, 0x68, 0x84, 0xc0, 0x09, 0x59, 0xb6, 0x7c, 0x29, 0x56, 0x01, + 0x89, 0x50, 0xc4, 0xa2, 0x25, 0x92, 0x1f, 0x31, 0x32, 0x30, 0x43, 0x12, 0xe8, 0x31, 0x7b, 0x4d, 0x07, 0xc6, 0x05, + 0x18, 0x53, 0xa9, 0x1e, 0x40, 0x3e, 0x05, 0xa3, 0xb0, 0x88, 0x50, 0xcb, 0x5d, 0x79, 0x91, 0x34, 0x34, 0x58, 0xc3, + 0xb1, 0x68, 0x2e, 0xe6, 0x28, 0xbd, 0x67, 0xca, 0x10, 0x24, 0x57, 0xad, 0x8c, 0xb0, 0xd3, 0x9f, 0xc7, 0x21, 0xe4, + 0xab, 0x0e, 0x42, 0x9b, 0x1b, 0x67, 0x11, 0x20, 0xf4, 0x48, 0x6c, 0x63, 0x8c, 0x80, 0xa4, 0xa1, 0x03, 0xa9, 0x0b, + 0x10, 0x21, 0x21, 0x44, 0x92, 0x80, 0xe6, 0x7c, 0x8b, 0x44, 0x7c, 0x06, 0x61, 0xae, 0x0b, 0xd2, 0x64, 0x89, 0x4a, + 0xbf, 0x6f, 0x96, 0x61, 0xb9, 0xc3, 0xc9, 0x2c, 0xc8, 0x55, 0x95, 0xb3, 0x00, 0x89, 0x84, 0xd9, 0xea, 0x84, 0xa1, + 0xf3, 0x46, 0xfb, 0x49, 0xc0, 0xd9, 0xc2, 0x84, 0x0c, 0x04, 0xa3, 0x58, 0x14, 0x85, 0x4a, 0xf5, 0x49, 0x81, 0xc3, + 0x08, 0x0d, 0xef, 0x2e, 0x0a, 0x37, 0xf3, 0x64, 0x2d, 0xab, 0xe2, 0x11, 0x93, 0xfb, 0xa1, 0x96, 0x38, 0xa7, 0x40, + 0x72, 0x82, 0xa2, 0xd1, 0xfd, 0xd7, 0xcf, 0x1d, 0x95, 0x44, 0x78, 0xd1, 0xa2, 0xf4, 0x6b, 0x8b, 0xdb, 0x5c, 0xcd, + 0x09, 0x34, 0x69, 0x66, 0xc8, 0x37, 0x9d, 0x8a, 0xf9, 0x95, 0xc1, 0xe5, 0x2e, 0xd8, 0x10, 0x40, 0x9b, 0x41, 0xef, + 0x4b, 0xeb, 0x53, 0xfa, 0x01, 0x46, 0xdf, 0xb8, 0xf3, 0xc2, 0x68, 0x27, 0xeb, 0xbd, 0xa1, 0x0b, 0xeb, 0x67, 0x57, + 0xb5, 0xd3, 0x71, 0x44, 0x02, 0x67, 0x2d, 0x74, 0xc8, 0xe6, 0x95, 0xb0, 0x9c, 0xd9, 0xe2, 0xec, 0xd1, 0xaa, 0xb5, + 0x1c, 0x91, 0x8e, 0x34, 0x1c, 0x90, 0xe3, 0xd9, 0x07, 0xa8, 0xf3, 0x08, 0x18, 0x49, 0x39, 0xf3, 0x5e, 0x71, 0x9c, + 0x37, 0x44, 0x1a, 0xea, 0x39, 0x2f, 0x00, 0xec, 0xca, 0x22, 0x29, 0x79, 0x1d, 0x72, 0x2d, 0xfd, 0xe9, 0x98, 0x47, + 0x8c, 0xb1, 0x73, 0x2a, 0x23, 0x8c, 0x4e, 0xae, 0x6b, 0x8e, 0x8c, 0xb2, 0x0b, 0x26, 0x54, 0xf3, 0xae, 0x34, 0xe5, + 0x81, 0x2c, 0xb2, 0xe9, 0x4a, 0x0b, 0x4e, 0x47, 0x62, 0xae, 0x6e, 0x56, 0x51, 0x3d, 0x4c, 0x10, 0xb1, 0xde, 0xbe, + 0xc1, 0xe4, 0x11, 0xcf, 0x27, 0x82, 0x54, 0xa4, 0xcd, 0xe9, 0x59, 0xc9, 0x07, 0xcc, 0x16, 0x68, 0xb4, 0xf2, 0x5e, + 0x00, 0x94, 0xdf, 0x94, 0xa8, 0x48, 0xb9, 0x6c, 0xd1, 0x41, 0x34, 0xe2, 0xd7, 0x41, 0x36, 0xeb, 0x3d, 0x39, 0x9e, + 0x6f, 0x8d, 0xac, 0x86, 0xc8, 0xd0, 0xea, 0xe8, 0x37, 0x74, 0xe8, 0x2b, 0xc2, 0xa4, 0xd3, 0xf3, 0xd8, 0xd6, 0x02, + 0x2d, 0x86, 0x8a, 0xa7, 0x62, 0x8c, 0x93, 0xea, 0x1a, 0xb1, 0x4c, 0xa9, 0x6f, 0x31, 0xd1, 0x15, 0xf4, 0x93, 0x2d, + 0x05, 0x9b, 0x6f, 0x59, 0xc9, 0x8b, 0x8c, 0x08, 0x7b, 0x8d, 0xf0, 0x62, 0x18, 0x03, 0xf4, 0xaa, 0xa5, 0x74, 0x1e, + 0xe8, 0xad, 0xe8, 0x8a, 0x79, 0xec, 0xc3, 0xeb, 0x2e, 0x49, 0x5e, 0xe0, 0xd6, 0x3c, 0x66, 0x35, 0x96, 0xdf, 0xbc, + 0xfe, 0xc6, 0x54, 0x25, 0xd6, 0xca, 0xca, 0x4f, 0xba, 0x6c, 0xdf, 0x0f, 0x49, 0x83, 0xbc, 0x4d, 0x6b, 0xfb, 0xbd, + 0xc9, 0x37, 0x10, 0x1b, 0x8c, 0xa2, 0x99, 0x2e, 0x16, 0x87, 0x05, 0xd2, 0xaf, 0x97, 0xa0, 0x2b, 0xd3, 0x0c, 0xd2, + 0xbe, 0xaf, 0x2f, 0x7f, 0x03, 0x98, 0x11, 0x63, 0x1f, 0x72, 0x22, 0x5a, 0x89, 0x66, 0xcb, 0xfc, 0xec, 0xec, 0x2d, + 0x08, 0x01, 0x33, 0xd9, 0xcf, 0x0f, 0x33, 0x43, 0xc2, 0x5e, 0x33, 0x13, 0xa1, 0xc0, 0x9a, 0x66, 0x9e, 0x5d, 0xcd, + 0xed, 0xd3, 0x52, 0xb4, 0x78, 0xac, 0x75, 0x95, 0xfa, 0x5e, 0xc6, 0x93, 0x8b, 0xd8, 0x9e, 0x67, 0x68, 0x3d, 0x63, + 0xa4, 0x41, 0x87, 0x17, 0x22, 0x62, 0x8b, 0x67, 0xff, 0x81, 0x99, 0x19, 0x85, 0x80, 0x6a, 0x0a, 0x7d, 0x7b, 0x8b, + 0x78, 0x2c, 0x4d, 0x9e, 0x91, 0xd9, 0xf7, 0x24, 0xdf, 0xac, 0x93, 0xf7, 0x5e, 0xaf, 0x5c, 0xad, 0x70, 0x6a, 0x85, + 0x1b, 0xe8, 0x51, 0xbf, 0xd5, 0x90, 0x28, 0x42, 0x0e, 0xe3, 0xd2, 0x2f, 0xea, 0x08, 0xe7, 0x02, 0xaf, 0xa7, 0x6e, + 0xeb, 0x7a, 0x48, 0x35, 0x05, 0x71, 0xee, 0xb6, 0x70, 0x46, 0x6f, 0xcd, 0x91, 0xa1, 0x3b, 0xce, 0xf2, 0x42, 0x5d, + 0xdd, 0x1d, 0x98, 0x76, 0x68, 0x68, 0x78, 0x5c, 0xd7, 0xa3, 0xc9, 0x23, 0x11, 0x4d, 0xdc, 0x5a, 0xac, 0xbf, 0x23, + 0xca, 0x3c, 0x0d, 0x60, 0xa7, 0x31, 0xea, 0xbf, 0x4b, 0xf6, 0x68, 0x74, 0xc7, 0x24, 0x91, 0x0d, 0x99, 0x6d, 0x40, + 0x9b, 0x83, 0x23, 0x3d, 0xf5, 0x15, 0x95, 0xdf, 0x4b, 0x14, 0x1c, 0x2f, 0xc5, 0x2d, 0x97, 0xf8, 0xab, 0x78, 0xe8, + 0xe9, 0x24, 0xa6, 0xc1, 0x0d, 0x59, 0x5c, 0x19, 0xe0, 0x32, 0x69, 0x0b, 0x0b, 0x68, 0xd8, 0xc0, 0x02, 0x0a, 0xa3, + 0xcf, 0x61, 0x92, 0x88, 0x7b, 0x38, 0x64, 0xbb, 0xc9, 0x7b, 0x71, 0x4c, 0x14, 0xcf, 0xd5, 0xe4, 0xe8, 0x82, 0x17, + 0xd3, 0x41, 0xd4, 0xec, 0x34, 0xd2, 0xcf, 0x30, 0xbd, 0x97, 0xad, 0xeb, 0xc8, 0x00, 0x61, 0x06, 0x15, 0xea, 0x17, + 0xd2, 0x3e, 0x7b, 0x39, 0x64, 0x40, 0xd1, 0xa0, 0xce, 0x86, 0x1d, 0x62, 0x51, 0xc8, 0x6b, 0x17, 0x4f, 0xb8, 0x96, + 0x78, 0x8f, 0x1e, 0x65, 0x58, 0x5c, 0xe6, 0x63, 0xb4, 0xf3, 0x56, 0x96, 0xa6, 0x0b, 0xcb, 0xf9, 0x5d, 0x8c, 0x16, + 0xe8, 0x70, 0xf5, 0xb8, 0x48, 0xf7, 0x53, 0x7b, 0x5e, 0xf8, 0x9f, 0x43, 0x17, 0x5d, 0xfb, 0x4c, 0x26, 0x75, 0x25, + 0x8f, 0x11, 0xf5, 0x55, 0x2f, 0xac, 0xe2, 0xde, 0x6b, 0xcd, 0xf4, 0x51, 0x8e, 0x32, 0x0f, 0x55, 0x66, 0x0d, 0xc6, + 0xd3, 0x92, 0x0c, 0x1f, 0x1d, 0x01, 0x0e, 0x41, 0x13, 0x82, 0x99, 0xfb, 0x92, 0x18, 0xa3, 0x12, 0x30, 0xee, 0x2c, + 0xb0, 0xbc, 0x9e, 0xdd, 0xd3, 0xd0, 0x16, 0x5a, 0x3e, 0xe5, 0xfc, 0x83, 0x2d, 0x96, 0xf9, 0xa9, 0xb0, 0x59, 0xe2, + 0xe2, 0x8e, 0x85, 0x3c, 0xea, 0x45, 0x55, 0xda, 0x5a, 0xf9, 0x8a, 0x54, 0x76, 0x43, 0x16, 0x5e, 0xd6, 0x2d, 0x2f, + 0x45, 0xe7, 0x55, 0x8c, 0x72, 0x92, 0x63, 0x0c, 0xc5, 0x10, 0xe0, 0xcd, 0x1c, 0x74, 0xf7, 0x02, 0x67, 0x72, 0x03, + 0x99, 0xe9, 0xeb, 0xd8, 0x52, 0x41, 0x1e, 0xec, 0xea, 0x99, 0x85, 0x07, 0x90, 0xc8, 0xf2, 0xf1, 0x9c, 0x8c, 0x2d, + 0xcb, 0x93, 0xef, 0xe5, 0x93, 0x60, 0x06, 0xaf, 0x02, 0x64, 0xd9, 0x79, 0xcd, 0xc1, 0x9f, 0x75, 0x87, 0x73, 0x4b, + 0x6b, 0x83, 0x6a, 0x1f, 0x7a, 0xce, 0x96, 0x0c, 0xbe, 0x12, 0x60, 0x34, 0x13, 0xa8, 0x2c, 0x41, 0x30, 0x4b, 0x8b, + 0xf9, 0x82, 0x60, 0x8e, 0xa3, 0x50, 0xb0, 0x3a, 0xe5, 0xe7, 0x61, 0x53, 0x14, 0x45, 0x3c, 0xfc, 0x3c, 0x0e, 0x95, + 0x67, 0x84, 0x55, 0x7c, 0xad, 0x88, 0xf2, 0xa1, 0xc6, 0x93, 0x81, 0x14, 0x40, 0xff, 0xa6, 0x2b, 0xa2, 0xfd, 0x15, + 0x69, 0x14, 0x14, 0xf6, 0x99, 0xbb, 0xd0, 0xce, 0x1a, 0x71, 0x91, 0x7e, 0x93, 0x61, 0x5e, 0x89, 0x67, 0x7e, 0x65, + 0x5d, 0xd6, 0x3a, 0xdf, 0x83, 0x6a, 0x3f, 0x52, 0xda, 0x59, 0xce, 0x2c, 0x39, 0x40, 0xbb, 0xa6, 0x69, 0x33, 0x9f, + 0x90, 0xb3, 0xb8, 0xda, 0x61, 0x0a, 0x52, 0x81, 0x57, 0x4d, 0x23, 0x95, 0xe2, 0xbc, 0x13, 0x05, 0x1c, 0x2e, 0xa7, + 0xf8, 0xbf, 0x39, 0x51, 0xbb, 0xf9, 0x05, 0x79, 0x6c, 0xef, 0xea, 0x97, 0x83, 0xac, 0x2d, 0x1c, 0x1d, 0x5c, 0xe7, + 0xb8, 0x89, 0x1a, 0xa2, 0x2a, 0x78, 0x6b, 0xc8, 0x97, 0xe6, 0x21, 0x05, 0x96, 0x23, 0x2d, 0x5a, 0x7d, 0x1e, 0xf7, + 0x89, 0x68, 0x9f, 0xba, 0x70, 0x3a, 0x2e, 0x33, 0x36, 0x87, 0xba, 0xc8, 0x8f, 0x49, 0xdb, 0x03, 0x06, 0x96, 0x7a, + 0xa2, 0x8d, 0x0f, 0x5d, 0xc4, 0x6d, 0x77, 0x06, 0xd2, 0xf5, 0x72, 0x1a, 0x4a, 0x66, 0x31, 0x70, 0xe1, 0x68, 0xcc, + 0xe3, 0x06, 0x9d, 0x76, 0xc5, 0x46, 0x64, 0x77, 0x30, 0x5c, 0x89, 0x51, 0xd5, 0x61, 0xec, 0x2e, 0x6a, 0x4e, 0xb0, + 0x52, 0x3d, 0xf6, 0x59, 0x74, 0x40, 0x82, 0x27, 0x14, 0x1c, 0x79, 0xe0, 0x11, 0x3e, 0xab, 0x83, 0x0e, 0x8f, 0x3a, + 0x03, 0xab, 0xea, 0x06, 0xdb, 0xea, 0x30, 0x06, 0xca, 0x11, 0x84, 0x22, 0xf2, 0xdd, 0x82, 0x3a, 0x85, 0xc7, 0xfc, + 0x86, 0x30, 0xa5, 0xf4, 0x7c, 0xce, 0xf6, 0xe2, 0xdb, 0x01, 0xfb, 0xdd, 0x27, 0x5e, 0xd2, 0x35, 0x8c, 0xc3, 0x0f, + 0xff, 0xaa, 0xc5, 0xf2, 0xeb, 0x01, 0xe6, 0xf7, 0x41, 0xaa, 0x4b, 0x58, 0xcb, 0x19, 0xc0, 0x1f, 0x6d, 0x19, 0x77, + 0x0d, 0x86, 0xf5, 0x11, 0x2a, 0x22, 0x3c, 0xe2, 0xa0, 0x7f, 0xaa, 0x05, 0x80, 0xe2, 0x38, 0xad, 0x80, 0xc8, 0x42, + 0x34, 0x3f, 0x2f, 0x67, 0x5f, 0x96, 0x65, 0x68, 0x4b, 0x4b, 0x56, 0x8f, 0x13, 0x69, 0xd8, 0x4c, 0x82, 0x4a, 0x88, + 0x5e, 0x11, 0x31, 0x22, 0x66, 0x86, 0xd6, 0x4b, 0xfb, 0x3d, 0x75, 0x57, 0x10, 0x46, 0xad, 0xdb, 0x70, 0xaf, 0xeb, + 0x51, 0x6f, 0xa4, 0xd9, 0xaf, 0xb5, 0x32, 0x80, 0x7d, 0x4b, 0xbe, 0xc0, 0x91, 0x84, 0x2d, 0xed, 0xf8, 0xef, 0x03, + 0xb1, 0xe8, 0x1f, 0x42, 0xd8, 0xc4, 0x26, 0xc8, 0x19, 0xbc, 0xd4, 0x3a, 0x7b, 0x1b, 0x24, 0xc2, 0x24, 0xd6, 0x6a, + 0x3d, 0x85, 0x24, 0x9a, 0x00, 0x52, 0xa1, 0x7d, 0xc6, 0xf4, 0x8a, 0x54, 0x9c, 0x3f, 0xdf, 0xb5, 0x6c, 0xae, 0x9a, + 0xf2, 0x89, 0x95, 0x23, 0xce, 0xd6, 0x4f, 0x96, 0x24, 0x9b, 0xf0, 0x5d, 0x22, 0xc1, 0x37, 0x16, 0xbb, 0xca, 0xab, + 0x7c, 0x0d, 0x9a, 0x14, 0x02, 0x1d, 0x5c, 0xee, 0x1c, 0x32, 0xd4, 0x62, 0x19, 0xd5, 0xd1, 0x16, 0x8b, 0x4c, 0xef, + 0x77, 0xca, 0xea, 0xb3, 0x08, 0x0d, 0x27, 0x16, 0xc3, 0x28, 0x95, 0x5e, 0x6c, 0xd1, 0xca, 0x9f, 0xf4, 0x7f, 0xc8, + 0x02, 0xa5, 0xea, 0x78, 0x89, 0x5b, 0x35, 0x74, 0x87, 0xae, 0xa8, 0x37, 0xa2, 0xb5, 0x63, 0xff, 0xf2, 0xc6, 0xa4, + 0x8e, 0x35, 0x6d, 0x10, 0xbc, 0x0e, 0xfa, 0x99, 0x29, 0x38, 0xd9, 0x78, 0x15, 0xe9, 0x14, 0x06, 0x04, 0x0a, 0x61, + 0x08, 0xf6, 0x19, 0xc9, 0xa6, 0xa5, 0x74, 0x67, 0x17, 0x27, 0xea, 0xd8, 0x38, 0x33, 0xca, 0xda, 0x45, 0xbc, 0xb4, + 0xf1, 0xd6, 0x13, 0x7a, 0xf1, 0xbd, 0x78, 0xb6, 0xe2, 0xa4, 0xb6, 0x8c, 0x88, 0x17, 0x1c, 0x0f, 0x97, 0x31, 0x87, + 0x6a, 0xe3, 0xd6, 0x82, 0x1e, 0x13, 0x5a, 0x0d, 0x9b, 0x9d, 0xb5, 0x9c, 0xf2, 0xb5, 0x18, 0x17, 0xe5, 0x8b, 0x37, + 0x0b, 0x28, 0x03, 0x42, 0x47, 0x8b, 0x48, 0x02, 0x9f, 0x15, 0x76, 0x63, 0x8e, 0x27, 0xc9, 0x92, 0xf9, 0xb5, 0x92, + 0x47, 0x80, 0x99, 0x18, 0x2e, 0xde, 0x86, 0xac, 0x9e, 0xa0, 0x4b, 0x76, 0xb0, 0x52, 0x37, 0x08, 0xb2, 0x04, 0x3b, + 0xc0, 0x5f, 0x78, 0x3f, 0xc6, 0xde, 0x39, 0xbf, 0xd9, 0x3a, 0xfc, 0x3f, 0xc1, 0x83, 0x79, 0x58, 0xdb, 0xee, 0x17, + 0x1b, 0xf5, 0xe5, 0xff, 0x4f, 0x75, 0x0d, 0xad, 0x03, 0x1f, 0x3e, 0x80, 0xf0, 0x78, 0x79, 0xa8, 0x45, 0xab, 0xad, + 0xbd, 0xc3, 0x90, 0x4c, 0x9c, 0x28, 0x2b, 0x76, 0x54, 0xef, 0x50, 0xb4, 0x9b, 0xf9, 0xb3, 0x23, 0x03, 0xd4, 0x3f, + 0x98, 0x78, 0x1f, 0x34, 0xd2, 0xdd, 0x2f, 0x20, 0x13, 0xeb, 0x51, 0x87, 0x5c, 0xa5, 0xf4, 0xf3, 0x73, 0xf7, 0xd6, + 0x7d, 0x94, 0xae, 0xd2, 0xc1, 0xfd, 0x45, 0x57, 0xed, 0xc1, 0x06, 0x17, 0x3b, 0xc5, 0xad, 0x5a, 0xfb, 0xa4, 0x74, + 0x95, 0x25, 0x3e, 0x04, 0x20, 0xc0, 0x56, 0x99, 0xc9, 0xca, 0x53, 0xbe, 0x85, 0x84, 0x77, 0xad, 0x4f, 0x67, 0x7f, + 0xbd, 0x0e, 0x6f, 0x14, 0x6b, 0xbb, 0x8b, 0x47, 0x6b, 0x07, 0x04, 0xe5, 0xdc, 0x6b, 0x28, 0x27, 0x10, 0xe2, 0x25, + 0x62, 0xae, 0x00, 0x97, 0xc3, 0xc8, 0x78, 0x8a, 0x1c, 0x39, 0x44, 0xb7, 0x11, 0xc1, 0xba, 0x4a, 0x5b, 0x15, 0xc7, + 0x5e, 0xcb, 0x23, 0xb3, 0x85, 0x71, 0x13, 0x11, 0x87, 0x45, 0x05, 0x46, 0x9e, 0x86, 0x1d, 0xce, 0x76, 0x86, 0x5e, + 0xcd, 0x42, 0x16, 0xa4, 0x09, 0xdb, 0xa5, 0x7e, 0x1f, 0x4e, 0x4e, 0x58, 0x7d, 0xd5, 0x42, 0xec, 0x05, 0x70, 0x9a, + 0xbc, 0x35, 0xe4, 0x57, 0x67, 0x7a, 0x46, 0xb8, 0x2c, 0x92, 0x7b, 0x2c, 0x04, 0xa1, 0xb2, 0xb5, 0x5d, 0x26, 0xcb, + 0xd2, 0x31, 0xc4, 0xfb, 0x8c, 0x21, 0xcc, 0xf0, 0x82, 0x40, 0xa6, 0x09, 0x4a, 0x19, 0x7e, 0x0b, 0xf7, 0x5c, 0x60, + 0x6c, 0x90, 0x9b, 0xe9, 0x30, 0x12, 0xae, 0xe8, 0x76, 0x80, 0xc8, 0xd2, 0x7c, 0xa2, 0x58, 0x4d, 0x55, 0x87, 0x7d, + 0x67, 0x12, 0xa2, 0xf6, 0x88, 0xf5, 0x78, 0x4a, 0xb7, 0xdb, 0x49, 0xbe, 0xca, 0x5c, 0x8a, 0x21, 0xa2, 0x4a, 0x47, + 0xee, 0x92, 0x6b, 0xe2, 0x94, 0x58, 0x5a, 0x65, 0x1c, 0x24, 0xb4, 0x63, 0xa1, 0x6d, 0x3c, 0xa5, 0x07, 0x91, 0xb6, + 0x8b, 0x5d, 0x52, 0xa5, 0x93, 0xc7, 0xfc, 0x88, 0x18, 0x32, 0xd3, 0x2f, 0xb0, 0xb6, 0xbf, 0xdc, 0x7c, 0x0a, 0x47, + 0x45, 0x62, 0xe7, 0x8e, 0xc0, 0x1f, 0x03, 0x6c, 0x5e, 0x4a, 0x4b, 0x61, 0x54, 0xa1, 0x73, 0xd5, 0x56, 0x2f, 0x0c, + 0x65, 0x43, 0x88, 0x40, 0x32, 0xcb, 0x12, 0x3e, 0xca, 0x1a, 0x06, 0x39, 0xf5, 0xbd, 0x06, 0x64, 0xdb, 0x83, 0x60, + 0xf9, 0x48, 0x95, 0xa5, 0xbe, 0xbf, 0x7c, 0x36, 0x09, 0x1f, 0xeb, 0x10, 0x66, 0x19, 0x70, 0xcd, 0x7a, 0xef, 0x86, + 0xc6, 0xfd, 0x61, 0x06, 0xf5, 0x2f, 0x5c, 0xe9, 0x1b, 0x7c, 0x8d, 0x3c, 0x16, 0x2e, 0xf5, 0xc8, 0x7b, 0x4b, 0x9e, + 0x6d, 0x53, 0xf2, 0x99, 0x16, 0x2b, 0xde, 0xc0, 0x67, 0x11, 0xef, 0x5a, 0xf1, 0x7d, 0x59, 0xdd, 0xd9, 0x76, 0xe6, + 0x04, 0xd3, 0x0c, 0xf6, 0x60, 0x86, 0xee, 0xfa, 0xa0, 0x95, 0x4a, 0x53, 0x47, 0xfa, 0xf6, 0xc1, 0xc7, 0xad, 0xf7, + 0x7f, 0x21, 0x4d, 0x74, 0x03, 0x84, 0xa2, 0xd2, 0xd7, 0x21, 0xca, 0x0e, 0x69, 0x62, 0xda, 0xa1, 0x4a, 0x14, 0x1d, + 0x3a, 0x65, 0x96, 0xa5, 0x00, 0xc3, 0x37, 0x96, 0x1f, 0x29, 0x5c, 0x2b, 0xc9, 0x0d, 0x84, 0x5a, 0x83, 0xf8, 0x6c, + 0x32, 0xbd, 0x2f, 0xd3, 0x82, 0x02, 0x16, 0x4c, 0xbe, 0x8e, 0x61, 0x17, 0xe9, 0xef, 0xe6, 0x0d, 0x09, 0xce, 0x09, + 0x87, 0x23, 0x1b, 0x08, 0xa0, 0x4c, 0xdb, 0x05, 0x17, 0xf7, 0x1b, 0xca, 0x9f, 0x5b, 0x69, 0xcf, 0x90, 0x5a, 0x70, + 0x18, 0xe8, 0x25, 0xfa, 0xbf, 0xee, 0x0c, 0x1f, 0xca, 0xe3, 0x85, 0x83, 0x39, 0x11, 0x6e, 0x71, 0xf6, 0x95, 0x65, + 0x56, 0xb9, 0xe2, 0xfe, 0xc0, 0xc8, 0x44, 0x6b, 0xd7, 0xd7, 0x07, 0xab, 0x15, 0xb5, 0x0a, 0x35, 0xf4, 0x95, 0xfb, + 0x9f, 0xe9, 0x5e, 0xee, 0x99, 0x31, 0x0f, 0xc5, 0xdc, 0x61, 0x5e, 0x34, 0x34, 0x3e, 0x43, 0x34, 0x44, 0xa9, 0xb1, + 0x1a, 0x70, 0x32, 0x26, 0xf5, 0xf1, 0xa0, 0xc3, 0x52, 0x3a, 0x27, 0x46, 0x95, 0x5a, 0x64, 0x90, 0x60, 0x72, 0x3c, + 0x97, 0x36, 0x87, 0x02, 0x11, 0x34, 0xf3, 0x1a, 0x1a, 0xfd, 0x28, 0x87, 0x15, 0x6e, 0x2c, 0xcb, 0x25, 0x86, 0x8c, + 0x20, 0xa8, 0x2c, 0x1b, 0x37, 0x75, 0x93, 0xa0, 0x28, 0x9c, 0xfa, 0xb1, 0x41, 0x41, 0xf1, 0xdb, 0x99, 0x2f, 0x4d, + 0x76, 0xdc, 0x3d, 0x1a, 0xc0, 0xa2, 0x58, 0x97, 0x78, 0xd9, 0xc5, 0x44, 0x6e, 0x72, 0x83, 0x55, 0x46, 0x20, 0xe6, + 0xf0, 0x27, 0xa8, 0x92, 0x22, 0xa6, 0x8b, 0xb8, 0xb9, 0x34, 0x17, 0x47, 0x32, 0xb5, 0xab, 0x07, 0x6e, 0x43, 0xa3, + 0x5a, 0x4d, 0xf4, 0xda, 0x32, 0x3f, 0x91, 0x88, 0x4e, 0x58, 0x3c, 0x91, 0x57, 0x4c, 0x44, 0x12, 0x0c, 0x0c, 0x28, + 0xda, 0x16, 0x42, 0x51, 0xe8, 0x35, 0x9f, 0xae, 0x96, 0xf3, 0x73, 0xb9, 0x05, 0x49, 0xa1, 0xd1, 0xef, 0x13, 0x48, + 0xf5, 0xd3, 0xa6, 0x3f, 0x61, 0xf1, 0x3f, 0x89, 0x09, 0xb7, 0x3d, 0xf4, 0x0c, 0xc4, 0xa7, 0x1e, 0xe0, 0xd3, 0x53, + 0x07, 0x0a, 0xd3, 0xcb, 0x17, 0xc1, 0x83, 0x22, 0xea, 0xc6, 0x9c, 0x58, 0xf2, 0x18, 0x4a, 0x7c, 0x5f, 0x95, 0x4f, + 0x31, 0xa3, 0xda, 0x4a, 0xe1, 0x9e, 0x04, 0x8a, 0x26, 0xae, 0x64, 0xf3, 0x39, 0x65, 0x5c, 0x86, 0xe2, 0xe3, 0x84, + 0xf3, 0x86, 0xe5, 0x52, 0x16, 0x4a, 0x5e, 0xe1, 0xfd, 0x60, 0x0e, 0x21, 0xcb, 0x15, 0xa9, 0x21, 0xbf, 0x2a, 0x61, + 0x7f, 0x0f, 0xa4, 0x71, 0x05, 0x63, 0xb6, 0xf6, 0x0a, 0xeb, 0xc7, 0x62, 0xa5, 0x1f, 0x90, 0x6b, 0xc4, 0x3d, 0x1c, + 0x32, 0x00, 0xc3, 0x7e, 0x77, 0x44, 0xcd, 0x48, 0x85, 0x0b, 0x73, 0xf7, 0x92, 0x40, 0xc2, 0x36, 0x08, 0x9b, 0xed, + 0x8b, 0x79, 0xf8, 0xf8, 0x57, 0x6b, 0xce, 0x0e, 0xd6, 0x4a, 0xb8, 0x74, 0x74, 0x95, 0x09, 0xf2, 0xf2, 0x31, 0x12, + 0x67, 0x6e, 0xa7, 0xa9, 0x65, 0x41, 0x54, 0x5a, 0x8c, 0x67, 0x2b, 0x71, 0xb3, 0x4c, 0xe1, 0xb1, 0xc7, 0x04, 0xed, + 0xcc, 0x4b, 0x70, 0x09, 0x88, 0x3e, 0xc8, 0xf8, 0xca, 0x3a, 0x89, 0x5e, 0x79, 0x36, 0xfe, 0x2c, 0xbb, 0xf7, 0xa8, + 0xff, 0xaa, 0x48, 0xed, 0x7a, 0xd6, 0xdd, 0xa1, 0x24, 0x15, 0x4c, 0xbb, 0x1b, 0xf0, 0x71, 0xd2, 0x4f, 0x4c, 0xbe, + 0x51, 0x10, 0x37, 0xc0, 0xd9, 0x77, 0xe3, 0x40, 0xb7, 0x80, 0xf5, 0xe6, 0x83, 0x44, 0x03, 0x57, 0x23, 0xd2, 0xb9, + 0x59, 0xaf, 0xaf, 0x4d, 0x0b, 0x05, 0x20, 0x05, 0xb3, 0x92, 0x90, 0xbc, 0x2b, 0x17, 0x6d, 0x7d, 0x22, 0xb6, 0x00, + 0x62, 0xba, 0x81, 0xc4, 0x71, 0x44, 0xb9, 0xc6, 0xa3, 0x6f, 0x96, 0x1e, 0x3d, 0xeb, 0x88, 0xdd, 0x3f, 0x85, 0xd6, + 0xf4, 0xb2, 0x83, 0xed, 0x9c, 0x22, 0xa8, 0x50, 0x86, 0x8e, 0xea, 0xd9, 0x0d, 0x9b, 0x5b, 0xc7, 0xb2, 0xd0, 0xa3, + 0x87, 0x20, 0x96, 0xcc, 0x7b, 0xdb, 0x08, 0x8d, 0x10, 0xdf, 0xfd, 0x42, 0xc0, 0x38, 0x5a, 0xff, 0x42, 0xab, 0x6c, + 0xa8, 0xe3, 0xd4, 0xc6, 0x83, 0x8f, 0x9b, 0x55, 0x61, 0xe5, 0x92, 0xf9, 0xdc, 0xbb, 0x63, 0x8a, 0x7a, 0x2a, 0xdf, + 0x7a, 0x2d, 0x7b, 0x32, 0x3a, 0x6a, 0x68, 0x8f, 0x7c, 0xd2, 0xd6, 0xb7, 0x86, 0xad, 0x48, 0x1a, 0xc9, 0xa4, 0xb9, + 0xf3, 0xc1, 0x09, 0xb5, 0x79, 0xd8, 0x21, 0x71, 0xc2, 0xdc, 0xfa, 0xdd, 0x3c, 0x92, 0xb2, 0x78, 0x04, 0x5b, 0xf8, + 0x66, 0x68, 0xd3, 0x30, 0x26, 0x1d, 0x27, 0xe0, 0xba, 0xd2, 0x3f, 0xcd, 0xa0, 0xc4, 0x6a, 0x61, 0x61, 0x3c, 0x03, + 0x98, 0x8a, 0x29, 0xe2, 0xa5, 0x0a, 0x86, 0x1a, 0x24, 0xe7, 0x6a, 0x10, 0xcc, 0x74, 0xcc, 0xd8, 0x99, 0x97, 0x79, + 0x0f, 0x6d, 0x6d, 0xcc, 0xc2, 0x42, 0xcf, 0xc6, 0xd4, 0x3c, 0xaa, 0x14, 0x30, 0x35, 0x82, 0x6e, 0x87, 0x71, 0x71, + 0xb7, 0x47, 0x7e, 0x5a, 0x8e, 0x9c, 0x5d, 0x0c, 0x8e, 0xc7, 0x5e, 0x66, 0x8b, 0x53, 0x0f, 0x9e, 0x07, 0x98, 0x11, + 0x2a, 0x6c, 0x15, 0x2f, 0xd0, 0x9e, 0x35, 0xfd, 0x07, 0xbe, 0x89, 0x8d, 0x31, 0x98, 0x37, 0xc6, 0xd1, 0x9a, 0xa5, + 0x2b, 0xde, 0xd3, 0x30, 0x42, 0x16, 0x31, 0x22, 0xcb, 0x59, 0x53, 0xcc, 0xad, 0x54, 0x31, 0x9e, 0x41, 0x22, 0x58, + 0xbe, 0xc2, 0x54, 0x00, 0xe1, 0x60, 0x76, 0xa3, 0xc1, 0x6e, 0xd6, 0xc7, 0xb5, 0x7e, 0x04, 0x44, 0x60, 0x00, 0xd5, + 0xc5, 0x39, 0xd7, 0x26, 0x3a, 0x00, 0x96, 0xdf, 0x47, 0x00, 0x20, 0x09, 0xcc, 0x50, 0x24, 0xa0, 0xe8, 0x55, 0x4b, + 0x5f, 0xf3, 0x62, 0x0e, 0x9d, 0x1e, 0x0a, 0x82, 0x60, 0x2b, 0xf7, 0xe8, 0x34, 0x48, 0xb3, 0xb9, 0x41, 0x1f, 0xf1, + 0xed, 0x59, 0x51, 0x89, 0x83, 0xcb, 0xaf, 0x8a, 0xa0, 0xf8, 0x27, 0x43, 0xf6, 0x26, 0x63, 0xa6, 0x23, 0xde, 0xea, + 0xc8, 0xa3, 0x85, 0x7c, 0x31, 0x4e, 0x17, 0x9f, 0xa1, 0xd8, 0x43, 0x36, 0x28, 0xab, 0x64, 0xec, 0xc4, 0x93, 0xa1, + 0x11, 0x49, 0xfd, 0xe3, 0x30, 0xf7, 0x45, 0x3d, 0x8a, 0xd2, 0x3c, 0xad, 0x27, 0xd4, 0x8a, 0xa9, 0x76, 0x23, 0xb0, + 0x26, 0xe5, 0x99, 0xd0, 0x19, 0x5b, 0xea, 0x97, 0x0a, 0x52, 0x76, 0x6a, 0x4c, 0xc5, 0x4e, 0xce, 0x8b, 0x9c, 0xa3, + 0xa7, 0x3c, 0x08, 0xe3, 0xc0, 0xd8, 0x9f, 0x4e, 0x97, 0xd5, 0xee, 0xd9, 0x09, 0xe2, 0xf1, 0x6a, 0xa8, 0xf6, 0x21, + 0x5d, 0xab, 0x26, 0xa6, 0x40, 0xd3, 0x9e, 0xa6, 0xff, 0x25, 0x81, 0x3e, 0x0f, 0xc1, 0x9e, 0xe9, 0xb3, 0x91, 0x6a, + 0x07, 0xd1, 0xfe, 0xa0, 0x85, 0x77, 0xf8, 0x1a, 0x25, 0x54, 0xbf, 0xe7, 0x04, 0xe8, 0xf8, 0x06, 0x6b, 0xc4, 0x96, + 0x24, 0xce, 0xe7, 0x22, 0x95, 0x9d, 0x63, 0x46, 0x2d, 0x20, 0x17, 0x44, 0x81, 0xe7, 0x3a, 0x8d, 0xca, 0x42, 0x96, + 0xbc, 0xc1, 0x8d, 0x9f, 0xfd, 0x9a, 0x29, 0x14, 0xfe, 0x69, 0x38, 0x08, 0x58, 0x06, 0xb0, 0x30, 0x9f, 0x5e, 0x61, + 0xce, 0x99, 0x9d, 0x25, 0x0c, 0x59, 0x80, 0x96, 0x3a, 0x7a, 0x0b, 0x9d, 0x04, 0x00, 0x44, 0x47, 0xc5, 0x18, 0xc8, + 0xab, 0x1d, 0x55, 0x9f, 0xc0, 0xa1, 0x77, 0xd2, 0x73, 0x69, 0xee, 0x26, 0x10, 0x45, 0x08, 0x08, 0x90, 0xd8, 0x1a, + 0x0a, 0x22, 0x6f, 0x39, 0x88, 0xa8, 0x4a, 0xec, 0x04, 0xb7, 0x42, 0xb3, 0xe0, 0x46, 0x32, 0x22, 0x8d, 0x00, 0x7a, + 0x05, 0x08, 0x31, 0x23, 0x50, 0xe6, 0x3c, 0xd2, 0xf8, 0x05, 0x1e, 0x26, 0x2f, 0x44, 0xc1, 0xe7, 0x14, 0xb5, 0xde, + 0x83, 0xe8, 0x9e, 0x9b, 0xb3, 0xf6, 0xc7, 0x84, 0x10, 0x3d, 0x02, 0x6b, 0x28, 0xab, 0x7f, 0x45, 0x29, 0x60, 0x34, + 0xc0, 0xd9, 0xde, 0xe1, 0xdc, 0x63, 0xfe, 0x51, 0xf2, 0xa0, 0x0a, 0x1d, 0xf3, 0x88, 0x5c, 0x3a, 0x9f, 0x74, 0xab, + 0xb0, 0x5e, 0xd4, 0x0e, 0x6c, 0xb7, 0x1e, 0x8f, 0xd5, 0x4b, 0x75, 0xad, 0x41, 0x1a, 0x8a, 0xff, 0xa2, 0xfc, 0x68, + 0x0c, 0x95, 0xf3, 0x8b, 0xf1, 0xa0, 0x7b, 0xd1, 0x61, 0xbd, 0x8b, 0x5c, 0x40, 0x45, 0x09, 0x00, 0xb4, 0xdb, 0xa1, + 0x9d, 0x33, 0x9b, 0x7f, 0xbb, 0xfd, 0x85, 0xaf, 0x2c, 0x55, 0x8b, 0x3a, 0xcf, 0x1a, 0x0a, 0xce, 0xcb, 0x71, 0xfe, + 0x2f, 0x3c, 0xd8, 0xcb, 0x93, 0xce, 0x98, 0x2a, 0x42, 0x9c, 0xba, 0x33, 0xfb, 0x26, 0x1f, 0x87, 0x2d, 0x21, 0x76, + 0xaa, 0x9b, 0xbf, 0xd9, 0xcc, 0x83, 0xa9, 0xaf, 0x76, 0x80, 0x1b, 0x37, 0xb7, 0xcc, 0xd8, 0xab, 0xc7, 0xd0, 0x31, + 0x01, 0xe0, 0xad, 0x25, 0x8a, 0x22, 0xe2, 0x25, 0xe1, 0xdf, 0x1f, 0x8f, 0x0f, 0x55, 0xc3, 0x07, 0x7d, 0x1b, 0xef, + 0x44, 0xa1, 0x29, 0x30, 0xc1, 0x3a, 0x60, 0x98, 0x0f, 0xe8, 0x7b, 0x85, 0xcd, 0x8c, 0x1a, 0xdf, 0x76, 0xba, 0x28, + 0x40, 0x4c, 0x61, 0x70, 0xa5, 0xf1, 0x49, 0x5e, 0x64, 0x3c, 0xa8, 0x02, 0x6d, 0xde, 0x26, 0xfb, 0xaa, 0x30, 0x34, + 0x3c, 0xed, 0xd6, 0x43, 0x8f, 0x1d, 0x34, 0x8b, 0x5b, 0xc3, 0xf8, 0x85, 0x74, 0x90, 0xbf, 0xb1, 0xc9, 0x2c, 0x51, + 0xfc, 0xfe, 0x47, 0xe7, 0x24, 0xf7, 0x7c, 0xd0, 0x4e, 0x8a, 0x9a, 0x0a, 0x9d, 0x3f, 0x2b, 0x1f, 0x97, 0xf3, 0xb3, + 0xf0, 0xee, 0x2c, 0xd4, 0x1d, 0x59, 0x0a, 0x12, 0x39, 0x0d, 0x4d, 0xae, 0xd5, 0x62, 0xcd, 0x89, 0x8b, 0xb7, 0xb6, + 0xc5, 0x27, 0x70, 0xb3, 0xe4, 0x0c, 0x61, 0x2a, 0xde, 0xc4, 0x84, 0xe0, 0x30, 0x10, 0x14, 0x86, 0x8b, 0xe2, 0x10, + 0x09, 0x83, 0x37, 0x3b, 0x3c, 0xb1, 0x5b, 0x06, 0x1b, 0x5f, 0xcd, 0x1b, 0x65, 0x9e, 0xb1, 0x9e, 0x98, 0x81, 0x6a, + 0x16, 0x55, 0xd7, 0x8b, 0x01, 0x56, 0xff, 0x84, 0xd7, 0xd2, 0x89, 0xd9, 0x7a, 0x90, 0x25, 0xa9, 0x61, 0x53, 0x2e, + 0x51, 0x4d, 0x19, 0xdb, 0x58, 0x43, 0xc1, 0xb5, 0xc3, 0x23, 0xfd, 0xe1, 0xfa, 0x4f, 0xce, 0x67, 0x89, 0x67, 0xa1, + 0xe7, 0x2b, 0x87, 0xc0, 0x5a, 0xec, 0xb2, 0x76, 0x7d, 0xe8, 0x6b, 0x36, 0x47, 0x61, 0x1b, 0x0d, 0xa5, 0x74, 0x16, + 0x2f, 0x88, 0xae, 0x83, 0x32, 0x90, 0x2e, 0x1d, 0x26, 0x3a, 0x7b, 0x5f, 0x35, 0xeb, 0x0e, 0x34, 0xde, 0xf4, 0x88, + 0x44, 0x1b, 0xbb, 0x6a, 0x30, 0xaf, 0xe8, 0x9c, 0xa2, 0x9b, 0x63, 0x4b, 0xa0, 0xbf, 0xda, 0x1c, 0x6e, 0x4c, 0x5f, + 0x02, 0x31, 0xa5, 0x80, 0x7c, 0xcb, 0xa6, 0xe6, 0x9e, 0xf3, 0x40, 0x3e, 0x61, 0x2a, 0x34, 0x64, 0xed, 0x3a, 0xec, + 0xc6, 0x1a, 0x2f, 0x39, 0x22, 0xf5, 0xcf, 0xb5, 0x08, 0x0b, 0xaf, 0x2e, 0x58, 0xb6, 0xc5, 0x47, 0x27, 0xac, 0x49, + 0xd2, 0xb6, 0x87, 0x05, 0xb4, 0xd8, 0x61, 0x51, 0x9e, 0x5a, 0xcf, 0x25, 0x2e, 0x66, 0x62, 0x7c, 0x4d, 0x97, 0x2e, + 0x39, 0xb0, 0xec, 0x1c, 0x01, 0x8d, 0x07, 0x2b, 0xbd, 0x15, 0xbe, 0x55, 0x74, 0xbf, 0x6a, 0x46, 0x25, 0xce, 0x34, + 0x90, 0xd6, 0x0b, 0x58, 0x23, 0xd4, 0xb5, 0xfc, 0xc0, 0x19, 0xc7, 0x02, 0x6c, 0xcb, 0xf4, 0xfe, 0x76, 0x29, 0x2d, + 0xc4, 0x0e, 0x01, 0x9e, 0x71, 0x17, 0xfd, 0x03, 0xcd, 0x0a, 0x60, 0x4c, 0x4e, 0x4d, 0xc8, 0xc5, 0x7b, 0xdd, 0x10, + 0x32, 0xa6, 0x7f, 0xd2, 0x3e, 0xb6, 0x6c, 0x47, 0x87, 0x04, 0x1c, 0x19, 0x06, 0xc6, 0xad, 0x57, 0x29, 0x6b, 0x77, + 0x33, 0x1c, 0x23, 0xaa, 0xa5, 0x15, 0xf7, 0xcb, 0x44, 0x81, 0x67, 0xc0, 0x6e, 0x5c, 0x34, 0xed, 0xb5, 0x41, 0x2e, + 0x91, 0x9d, 0xc1, 0xab, 0x53, 0x45, 0x66, 0x61, 0x8c, 0x5d, 0x25, 0x0b, 0x3c, 0x3e, 0xf6, 0x84, 0x31, 0xfe, 0x27, + 0x29, 0x41, 0xf9, 0xfe, 0xbb, 0xa4, 0x93, 0x0a, 0x95, 0xc2, 0x1e, 0x4e, 0xaf, 0xe3, 0x2b, 0xfa, 0x2a, 0x11, 0x58, + 0xf3, 0xa8, 0x7e, 0xdc, 0x00, 0x83, 0xaa, 0x0d, 0x78, 0x74, 0x43, 0x29, 0xde, 0x54, 0xf8, 0x26, 0x77, 0xa1, 0x55, + 0x51, 0x8e, 0xca, 0x01, 0x6b, 0x8e, 0xdc, 0x1c, 0x59, 0x22, 0xd8, 0xb2, 0x76, 0x90, 0xa2, 0x02, 0xc3, 0x9e, 0x55, + 0x83, 0xb4, 0x2a, 0x3d, 0x1c, 0x19, 0x7f, 0x4d, 0x80, 0x16, 0x40, 0x18, 0x96, 0x3f, 0x33, 0x93, 0x8c, 0x97, 0x29, + 0x2b, 0xb9, 0xa9, 0xe6, 0x28, 0x9a, 0x98, 0x86, 0x4e, 0xee, 0xe9, 0x84, 0x1f, 0x6a, 0x8e, 0x38, 0x1b, 0x04, 0xb5, + 0x55, 0xd5, 0x3a, 0x83, 0x61, 0x50, 0x27, 0x1d, 0x01, 0xf2, 0x51, 0xd2, 0x60, 0xc2, 0x73, 0x73, 0x8e, 0x9e, 0xc7, + 0x79, 0x19, 0x96, 0x93, 0x76, 0x36, 0x4b, 0x00, 0x3e, 0xb5, 0x14, 0xb6, 0x90, 0x81, 0x31, 0x8c, 0x3f, 0x02, 0x72, + 0xc7, 0xa7, 0xcf, 0x4b, 0xcb, 0x1e, 0x95, 0x5e, 0xde, 0xfc, 0xf0, 0xf1, 0x07, 0x83, 0x37, 0x18, 0x2a, 0x1a, 0xbc, + 0x7b, 0xaf, 0x2f, 0xe9, 0x3b, 0x99, 0x60, 0xac, 0x41, 0xe7, 0x20, 0x8a, 0x55, 0x68, 0x47, 0xb6, 0x2a, 0xeb, 0x22, + 0x27, 0xdb, 0xd7, 0x27, 0xe5, 0xe7, 0x97, 0x22, 0x94, 0x6a, 0x41, 0x21, 0x6f, 0xb1, 0x8a, 0x0d, 0x42, 0x28, 0x54, + 0xe0, 0xa0, 0x08, 0x01, 0x8e, 0x22, 0xee, 0xee, 0x34, 0x14, 0x00, 0x52, 0x52, 0x14, 0xcc, 0xa9, 0xcb, 0xda, 0xdb, + 0x5c, 0x60, 0xb3, 0x73, 0xa6, 0xee, 0x23, 0x3e, 0xc7, 0x84, 0xd5, 0x39, 0x47, 0x8a, 0x04, 0xb2, 0xb6, 0xec, 0xd6, + 0x22, 0x4b, 0x75, 0x77, 0x34, 0x64, 0xc8, 0xac, 0x20, 0xe7, 0x5e, 0x3e, 0x2b, 0x10, 0x5a, 0x41, 0xfe, 0x93, 0x26, + 0x36, 0x60, 0x8c, 0x63, 0xfb, 0xc7, 0xef, 0x54, 0xf0, 0x37, 0x5f, 0xc3, 0x3d, 0xf9, 0x6d, 0x3a, 0xc1, 0x2a, 0xc5, + 0x60, 0x50, 0xf3, 0x2b, 0xe7, 0x4c, 0xaf, 0xcd, 0x18, 0x88, 0x89, 0x63, 0x56, 0xbe, 0x87, 0x57, 0xe9, 0x8b, 0x52, + 0xb4, 0x19, 0x54, 0xa4, 0x4c, 0x2a, 0x80, 0x84, 0x26, 0xed, 0x21, 0xf5, 0x1a, 0x4c, 0xca, 0xb2, 0x29, 0xb6, 0x69, + 0xae, 0xd4, 0xf6, 0xb1, 0xa3, 0xa6, 0xd6, 0x83, 0x32, 0x89, 0x87, 0x38, 0x7d, 0x16, 0x78, 0x1c, 0x63, 0x42, 0x88, + 0x14, 0x12, 0x7f, 0x71, 0xa6, 0xd5, 0xe3, 0x2b, 0x2a, 0xee, 0xb9, 0x8f, 0xa0, 0x63, 0x0c, 0x8d, 0xe9, 0x54, 0xb0, + 0x1b, 0xd2, 0x19, 0x12, 0x7b, 0x9d, 0x1b, 0x99, 0xee, 0xd6, 0xab, 0x0e, 0x1f, 0x8c, 0xcc, 0x4f, 0x79, 0xc7, 0xae, + 0xf7, 0x46, 0x06, 0x6b, 0x9d, 0xd2, 0xd3, 0x9a, 0xf2, 0xf4, 0x7f, 0xc3, 0x15, 0xee, 0xa8, 0x2e, 0x2d, 0x12, 0x5d, + 0x9e, 0x21, 0xc1, 0xb8, 0x48, 0x8a, 0xb4, 0xde, 0x25, 0x4c, 0x36, 0xbd, 0x62, 0xed, 0x9a, 0xd1, 0x65, 0x61, 0x7e, + 0xc8, 0xe6, 0x17, 0x5d, 0x8b, 0xf1, 0x0e, 0xac, 0xb3, 0xaf, 0xf2, 0xcc, 0x39, 0x46, 0x9e, 0xc1, 0x8c, 0x85, 0xbd, + 0x2a, 0xa8, 0x43, 0x5a, 0x58, 0x07, 0xa8, 0x1e, 0xa3, 0x28, 0xe3, 0xd1, 0x4b, 0x9b, 0x42, 0x7a, 0xa0, 0xdb, 0xee, + 0x95, 0x5f, 0x5e, 0x45, 0x85, 0x02, 0x20, 0x2e, 0x44, 0x58, 0x78, 0x34, 0x83, 0xc1, 0x05, 0x0a, 0x85, 0xb7, 0x39, + 0xe8, 0xc5, 0x35, 0x9c, 0xb7, 0x1f, 0xa4, 0xd4, 0x70, 0x8a, 0x29, 0x1d, 0x27, 0x5f, 0x70, 0x67, 0xbd, 0xac, 0x40, + 0x7e, 0x38, 0xb3, 0x16, 0xbb, 0x66, 0x97, 0x42, 0x36, 0xa4, 0xe8, 0xaa, 0xdd, 0xed, 0x9d, 0xb2, 0xb6, 0x67, 0xe6, + 0xc3, 0xb2, 0xa6, 0x68, 0x56, 0x12, 0x85, 0x9e, 0x43, 0x14, 0x43, 0xc5, 0xd0, 0xcc, 0xb5, 0x65, 0x5d, 0xd4, 0x52, + 0x0d, 0x95, 0xba, 0x46, 0x50, 0xd5, 0xcd, 0x51, 0xfd, 0x73, 0xd6, 0xe3, 0xdc, 0xb5, 0xc1, 0xd0, 0x7a, 0xf2, 0x30, + 0x5e, 0xc6, 0xea, 0x1c, 0x1f, 0x2f, 0x7c, 0x8e, 0x73, 0xdb, 0xbe, 0x57, 0xf7, 0x3b, 0x05, 0x6d, 0x59, 0x7c, 0x13, + 0xff, 0x83, 0xea, 0xff, 0xb2, 0x01, 0x23, 0x93, 0x8f, 0x0f, 0xcb, 0x99, 0xd6, 0x17, 0x59, 0x4c, 0x76, 0xe4, 0xb1, + 0x33, 0x4d, 0x9e, 0xb1, 0xb0, 0x57, 0x77, 0x6f, 0x23, 0x67, 0xc1, 0x61, 0x73, 0xe6, 0x10, 0x06, 0xb2, 0x32, 0xfe, + 0xb0, 0x65, 0xb4, 0x6e, 0x9d, 0x36, 0x75, 0xf8, 0x30, 0x34, 0x31, 0xd9, 0x6b, 0x3c, 0xc5, 0x10, 0xe6, 0xd9, 0x94, + 0xb1, 0x2d, 0xe0, 0x45, 0x65, 0x28, 0xe2, 0x32, 0xae, 0x39, 0x82, 0x29, 0xad, 0x06, 0xf6, 0x59, 0x45, 0xf1, 0x1c, + 0x55, 0xba, 0xa8, 0x9e, 0xdb, 0x37, 0x3d, 0x60, 0x48, 0x46, 0xce, 0x7e, 0xb9, 0xfa, 0x18, 0x1a, 0x58, 0xb7, 0xa3, + 0xaf, 0x06, 0x3c, 0x43, 0x24, 0xfa, 0xbc, 0x33, 0x36, 0x20, 0xb6, 0x58, 0x99, 0xe5, 0x50, 0x48, 0xfe, 0x71, 0x3b, + 0x5c, 0xc6, 0xea, 0x53, 0x7e, 0xa4, 0x2f, 0x59, 0xec, 0x86, 0xa6, 0xd6, 0xc1, 0x5f, 0xa9, 0x0a, 0x22, 0xe5, 0x5d, + 0x4b, 0x75, 0x97, 0x21, 0x6d, 0x4a, 0x3d, 0xfa, 0x7b, 0xa0, 0x2c, 0x8d, 0x58, 0x89, 0xa5, 0x51, 0x35, 0x26, 0xfe, + 0xef, 0xf4, 0x29, 0x3a, 0x23, 0x3f, 0xb5, 0xb0, 0xe2, 0xbe, 0x22, 0x16, 0x2e, 0xe1, 0x98, 0xe9, 0xd5, 0x16, 0x1d, + 0x15, 0x22, 0x28, 0xe0, 0xb3, 0x45, 0xef, 0xcd, 0x86, 0x4c, 0x04, 0x8d, 0xb7, 0x79, 0x7a, 0x1d, 0x4f, 0xf7, 0xf3, + 0x19, 0xd9, 0x11, 0x9a, 0x2e, 0xac, 0x4d, 0x41, 0xe1, 0x20, 0x70, 0x6e, 0x21, 0xd0, 0x5c, 0x95, 0x81, 0x09, 0x8e, + 0xf3, 0x62, 0xcb, 0x27, 0x50, 0x9d, 0xee, 0x81, 0x34, 0xa8, 0x5a, 0x9e, 0x6a, 0x95, 0xba, 0x8f, 0xe9, 0xb4, 0xd5, + 0x3a, 0x6b, 0x83, 0x52, 0xfc, 0x00, 0xbb, 0xa0, 0x80, 0x56, 0x2f, 0x51, 0x82, 0xb8, 0x39, 0x34, 0x5f, 0xca, 0x5e, + 0x33, 0xe7, 0x68, 0xef, 0xd0, 0x92, 0x71, 0x41, 0xfb, 0xfb, 0xfb, 0x03, 0x21, 0x73, 0x14, 0xad, 0x83, 0xa6, 0x64, + 0x2e, 0xf7, 0x88, 0xab, 0x48, 0xe5, 0x9f, 0x17, 0x6c, 0xa8, 0xe0, 0xe5, 0xf6, 0x77, 0xa8, 0x1f, 0x16, 0x75, 0xd1, + 0x7e, 0x0b, 0xf1, 0x1a, 0xf9, 0x47, 0xf0, 0xfe, 0x28, 0x20, 0x1a, 0x7e, 0x9a, 0xf0, 0x3b, 0x68, 0xb3, 0x57, 0xf7, + 0x0b, 0xdf, 0xf7, 0x7d, 0x8b, 0xdd, 0xe0, 0xad, 0xef, 0x9f, 0x3a, 0x58, 0x85, 0xc3, 0x1e, 0xb8, 0x9e, 0x18, 0xdd, + 0xfe, 0xfc, 0xfc, 0xbe, 0x86, 0x8a, 0x2f, 0xce, 0xb0, 0x9b, 0xa9, 0x7c, 0xa0, 0xee, 0x9d, 0xdc, 0xd2, 0x7e, 0xa1, + 0xe6, 0x35, 0x04, 0xa4, 0x5c, 0x38, 0x27, 0xae, 0x4f, 0x0a, 0x5c, 0x81, 0x16, 0x52, 0x3a, 0xba, 0x2d, 0xf1, 0x9e, + 0x35, 0xa4, 0xfd, 0xb0, 0x01, 0x36, 0x9d, 0xf6, 0x1d, 0x52, 0x71, 0x98, 0xc9, 0xd2, 0x6c, 0x42, 0xfe, 0x6b, 0x8e, + 0x3a, 0x55, 0x07, 0xf7, 0x79, 0xb1, 0x2e, 0x0c, 0xeb, 0x6e, 0x3c, 0xce, 0x9f, 0xaa, 0x3d, 0x61, 0xc4, 0x0d, 0x63, + 0x75, 0xc8, 0x6f, 0x90, 0x06, 0xf4, 0x76, 0x34, 0x93, 0x22, 0xfb, 0x81, 0x00, 0x80, 0xaf, 0xd6, 0x8c, 0xa5, 0x41, + 0xd9, 0x37, 0xfd, 0x1c, 0x2a, 0x34, 0x41, 0x8c, 0xca, 0x5e, 0x03, 0x24, 0xe0, 0x22, 0x5b, 0x97, 0xc5, 0x7b, 0xa1, + 0x22, 0xa1, 0x5b, 0x97, 0xd0, 0xa9, 0xde, 0xc9, 0x10, 0x56, 0x5d, 0x22, 0xc2, 0x9c, 0xf6, 0x84, 0xaf, 0xeb, 0x7c, + 0xf8, 0x3c, 0x16, 0x7b, 0xce, 0xd3, 0xcf, 0xb0, 0xb9, 0x30, 0x0d, 0x0d, 0x44, 0x33, 0x0e, 0xdd, 0x8f, 0xd4, 0x96, + 0xe2, 0xd6, 0xac, 0x62, 0x3c, 0xfe, 0x72, 0x5e, 0x55, 0x64, 0xfd, 0xe5, 0x22, 0xc3, 0x14, 0xe1, 0x66, 0x16, 0xf5, + 0xf2, 0xa2, 0x10, 0x66, 0xa7, 0x8b, 0x06, 0x82, 0x66, 0xb4, 0x6d, 0x3d, 0xb8, 0xa1, 0xb4, 0x11, 0xfa, 0x45, 0x95, + 0x68, 0x6d, 0xd5, 0xf7, 0xfd, 0x06, 0xd9, 0xe5, 0x1c, 0x07, 0x6d, 0x5e, 0xc0, 0xf1, 0xbd, 0x7f, 0xea, 0x97, 0xab, + 0xbd, 0x75, 0x9a, 0xbf, 0xe0, 0x16, 0x5f, 0x90, 0xb0, 0xfc, 0x30, 0xc3, 0x41, 0x29, 0x21, 0xc3, 0xc9, 0x47, 0x38, + 0x17, 0xd6, 0xe8, 0x92, 0xcf, 0xf6, 0x5c, 0x18, 0xe8, 0x60, 0x45, 0xb4, 0x23, 0xbe, 0xe1, 0xa7, 0xba, 0x2d, 0x44, + 0x10, 0x3b, 0x58, 0xc6, 0x80, 0x67, 0x64, 0x72, 0x22, 0xa3, 0x3a, 0x4c, 0x60, 0x9a, 0x4d, 0x98, 0x06, 0x76, 0x9b, + 0x00, 0x9a, 0x3a, 0x18, 0xa7, 0x38, 0x03, 0x7d, 0x18, 0xaa, 0xad, 0x67, 0x25, 0x19, 0xf3, 0x81, 0xa0, 0x9d, 0xed, + 0x8f, 0x1a, 0x65, 0x5e, 0x6c, 0x37, 0xdb, 0x48, 0xf3, 0xaa, 0x14, 0x43, 0x3b, 0x90, 0xd9, 0x91, 0x34, 0x64, 0xea, + 0x1e, 0xd4, 0xb8, 0x50, 0xa8, 0x36, 0x0c, 0xc2, 0x01, 0x4a, 0x91, 0xa6, 0x39, 0xf5, 0x08, 0xb3, 0xe8, 0xd6, 0x14, + 0xde, 0x59, 0x66, 0xb8, 0x5a, 0x22, 0xa0, 0x04, 0x11, 0xc7, 0x5d, 0x74, 0x18, 0xc5, 0x83, 0xbd, 0x51, 0x77, 0x4a, + 0xa8, 0xaf, 0x5c, 0x2c, 0xd6, 0xa3, 0xad, 0x16, 0x7b, 0x82, 0x69, 0x5a, 0xd7, 0xfb, 0x81, 0x18, 0xed, 0xf9, 0x66, + 0x22, 0x55, 0xea, 0x12, 0x54, 0x95, 0xde, 0xb7, 0x1f, 0xb2, 0x8a, 0x3d, 0x86, 0xc7, 0x4a, 0xa5, 0x44, 0xb1, 0x53, + 0xd3, 0xce, 0xe2, 0x34, 0x45, 0xda, 0x65, 0x99, 0x78, 0x13, 0xfa, 0x1d, 0x49, 0xbb, 0x2d, 0xb3, 0xb6, 0x17, 0x8b, + 0x9b, 0x93, 0x48, 0xb1, 0x1c, 0xac, 0x35, 0xbc, 0x2d, 0x73, 0xec, 0x82, 0xb7, 0x39, 0xb7, 0x7e, 0xc1, 0x58, 0x43, + 0xeb, 0x33, 0xd6, 0xdf, 0xa4, 0x47, 0x46, 0x14, 0xa0, 0xfa, 0x37, 0x59, 0x08, 0x12, 0x37, 0xcc, 0xf8, 0x1d, 0xb5, + 0x61, 0x51, 0x5d, 0xd4, 0x3d, 0x4b, 0xac, 0x88, 0x58, 0x38, 0x7f, 0x5f, 0x9d, 0x05, 0x72, 0xe9, 0x6c, 0xc5, 0x35, + 0x0f, 0x47, 0x5d, 0x76, 0x3d, 0xb8, 0x53, 0x18, 0x53, 0xf3, 0xc9, 0x42, 0xf5, 0x86, 0x7b, 0x2e, 0x3e, 0xd7, 0x12, + 0x5e, 0x57, 0xfb, 0xdc, 0x9c, 0xe6, 0xf2, 0x2d, 0x2e, 0xab, 0x2a, 0xb5, 0x99, 0xc0, 0xa4, 0x6b, 0xad, 0xfe, 0x38, + 0x82, 0x35, 0x14, 0x91, 0xb8, 0x49, 0xd4, 0xc1, 0x66, 0x59, 0x87, 0x72, 0x9b, 0x09, 0x56, 0x92, 0x0d, 0xf6, 0x80, + 0x70, 0x6a, 0xb1, 0x99, 0x63, 0xa7, 0x0d, 0xe1, 0xf0, 0x1d, 0xb7, 0xa6, 0x88, 0x8a, 0x53, 0x77, 0xe1, 0xa9, 0x65, + 0xf9, 0xc3, 0xec, 0x6a, 0x4d, 0xd3, 0xf5, 0x1d, 0x6a, 0x64, 0x49, 0xb8, 0x72, 0x2f, 0x63, 0x98, 0x0f, 0x2d, 0xe4, + 0x59, 0xaa, 0x8e, 0x60, 0xd0, 0xd2, 0x2d, 0x37, 0xfc, 0x7d, 0xf8, 0x74, 0x5c, 0x6b, 0x22, 0xda, 0x38, 0xbe, 0xdc, + 0x43, 0x2a, 0x27, 0xfb, 0x49, 0xcc, 0x0b, 0x95, 0xd3, 0xe9, 0x49, 0x91, 0x80, 0x87, 0x9b, 0xb8, 0x70, 0x89, 0x72, + 0x2d, 0xcb, 0x74, 0x35, 0xc9, 0xa9, 0xa1, 0x42, 0xce, 0x8c, 0xa1, 0xc5, 0xfb, 0x59, 0xc4, 0x30, 0x63, 0x13, 0x66, + 0x66, 0x53, 0x53, 0xd3, 0x0e, 0x45, 0xee, 0x43, 0x25, 0x8f, 0xc4, 0x64, 0xe5, 0xd0, 0x38, 0x3a, 0x35, 0xdd, 0x63, + 0x70, 0x5d, 0x21, 0x9c, 0x6a, 0x54, 0xfb, 0x01, 0x74, 0x71, 0xfe, 0x85, 0xdb, 0x51, 0xbf, 0x1c, 0x8c, 0x7e, 0x6b, + 0x54, 0x13, 0x95, 0xf9, 0xd0, 0x0c, 0x5d, 0x3b, 0x32, 0x98, 0x1c, 0x03, 0xe0, 0x26, 0x13, 0x84, 0x0d, 0x1f, 0x57, + 0x60, 0x16, 0x7b, 0xaa, 0xaf, 0x7f, 0x0e, 0x52, 0x38, 0x97, 0xa9, 0x67, 0x61, 0xd4, 0x72, 0x80, 0x4b, 0x03, 0x0b, + 0xe3, 0x4a, 0x43, 0x0c, 0x9b, 0xdf, 0x8f, 0xb6, 0x89, 0x4c, 0xd2, 0x3d, 0xab, 0x29, 0x00, 0x9a, 0x4e, 0x41, 0xe4, + 0xdf, 0xa3, 0xe4, 0x05, 0xc7, 0xd1, 0x29, 0x3d, 0xfd, 0xa2, 0xd4, 0xa3, 0x19, 0xb4, 0xf7, 0x78, 0x75, 0xc1, 0xac, + 0x27, 0x23, 0xed, 0x88, 0x87, 0xd9, 0x09, 0xe4, 0x07, 0x48, 0x4d, 0xe9, 0x5a, 0x73, 0x63, 0xf7, 0x35, 0xc8, 0x96, + 0xed, 0x68, 0x90, 0xc3, 0x1a, 0xf9, 0x1a, 0x54, 0xca, 0xa1, 0x7c, 0x93, 0xcc, 0xe3, 0x24, 0xd8, 0xd7, 0xc8, 0xed, + 0x3b, 0xee, 0x6b, 0xb6, 0xb7, 0x43, 0x52, 0x1d, 0x92, 0xb0, 0xef, 0xb6, 0x69, 0x92, 0xe0, 0x70, 0x83, 0x0c, 0xc2, + 0x05, 0x6c, 0x64, 0xe8, 0xdb, 0xeb, 0x46, 0x21, 0x9a, 0xef, 0x1a, 0x7c, 0xaf, 0xee, 0x8b, 0x37, 0x66, 0x30, 0x49, + 0x92, 0x44, 0x60, 0x36, 0x53, 0x1a, 0x13, 0xe5, 0x1b, 0xc3, 0x73, 0xb5, 0xe7, 0x07, 0xe5, 0x5c, 0x4b, 0xd8, 0x33, + 0x1d, 0xbf, 0x1d, 0x8d, 0x57, 0xa5, 0xdf, 0xe0, 0x55, 0x52, 0x12, 0xdd, 0xf9, 0xfb, 0x00, 0x8e, 0xbc, 0x29, 0xeb, + 0x17, 0xf3, 0x1d, 0xa7, 0xc7, 0xd2, 0xe6, 0xed, 0x26, 0x2e, 0xf0, 0x37, 0x4f, 0xa4, 0x5e, 0xf1, 0xa5, 0xa6, 0x49, + 0xbf, 0x6e, 0xf1, 0x60, 0x17, 0x30, 0x79, 0xcb, 0x0d, 0xb3, 0x06, 0x7d, 0xb3, 0xca, 0x4d, 0xdf, 0x42, 0x79, 0x58, + 0xce, 0x63, 0x9e, 0x3a, 0x84, 0x5f, 0x3c, 0xaa, 0x43, 0x65, 0x34, 0xb7, 0x66, 0x27, 0xf4, 0x37, 0x98, 0xd7, 0xdc, + 0xc1, 0x0c, 0x27, 0xb2, 0x24, 0x0d, 0x6f, 0x7a, 0x7a, 0x3b, 0xca, 0x3c, 0x08, 0x42, 0x92, 0x22, 0xda, 0x06, 0x76, + 0xd0, 0x82, 0x0a, 0xb8, 0x41, 0xd4, 0xec, 0x3d, 0x62, 0xb6, 0x97, 0x76, 0x1f, 0xe7, 0xbd, 0x77, 0x3c, 0x59, 0x13, + 0x21, 0x67, 0x08, 0xa1, 0xf8, 0x7b, 0xda, 0xcf, 0x61, 0xcf, 0x08, 0x57, 0x5a, 0xa1, 0x60, 0xc4, 0x0d, 0xaa, 0x7e, + 0xcc, 0x16, 0x10, 0x2d, 0x12, 0x90, 0xb3, 0x5d, 0x0b, 0x6b, 0x26, 0x33, 0xf9, 0x49, 0x0c, 0x95, 0xd4, 0xb6, 0x7c, + 0xc3, 0x7f, 0xae, 0x0a, 0x49, 0x60, 0x31, 0x27, 0x75, 0xdf, 0x47, 0x12, 0x8b, 0x9b, 0x35, 0x9b, 0x87, 0x72, 0xed, + 0xf3, 0x72, 0xac, 0xbd, 0x83, 0xbe, 0x50, 0x71, 0x59, 0x2e, 0xaf, 0x4a, 0xbb, 0x44, 0x5d, 0xeb, 0x30, 0xb4, 0xa4, + 0xb4, 0x62, 0xd8, 0x87, 0x56, 0xf5, 0xc8, 0x91, 0xc3, 0xdf, 0x03, 0x69, 0xb8, 0xbb, 0xcc, 0xf0, 0xe6, 0xa5, 0xeb, + 0x5d, 0x34, 0x6d, 0xa5, 0x22, 0xe1, 0x4e, 0x6e, 0xbb, 0xa2, 0x33, 0x24, 0x88, 0x58, 0x0f, 0x1f, 0xe5, 0x87, 0x0b, + 0x86, 0x55, 0x8a, 0x36, 0xa4, 0xdb, 0x6c, 0x2e, 0x33, 0x37, 0x92, 0xb2, 0xdd, 0x9f, 0x56, 0xbd, 0x09, 0xaa, 0x75, + 0xa2, 0x36, 0xcf, 0xed, 0xb6, 0xd8, 0xba, 0x67, 0x00, 0xf5, 0x93, 0x33, 0x85, 0x23, 0x26, 0x88, 0x89, 0x56, 0x29, + 0x17, 0x61, 0xe6, 0x11, 0x0c, 0xf7, 0xd6, 0xfc, 0x84, 0xd8, 0xc7, 0x8b, 0x1c, 0x3f, 0xa6, 0x07, 0xb8, 0xe7, 0x13, + 0xb7, 0xcf, 0x69, 0x92, 0x83, 0xec, 0x88, 0xed, 0x46, 0xf1, 0x90, 0x8b, 0xee, 0x86, 0x4d, 0x25, 0x2c, 0x13, 0xe7, + 0xaa, 0xe5, 0xda, 0x18, 0x94, 0x0a, 0x45, 0x45, 0xee, 0x23, 0x65, 0xf1, 0xfb, 0x49, 0x55, 0xbe, 0x07, 0x91, 0xd8, + 0xf6, 0x49, 0x04, 0x52, 0xfd, 0xa3, 0xa0, 0x94, 0x12, 0xe6, 0xa5, 0x91, 0x67, 0xea, 0x4f, 0x28, 0x65, 0xc1, 0x43, + 0xc0, 0x17, 0x07, 0x9c, 0x0b, 0x6d, 0xfd, 0xf7, 0xb9, 0xee, 0x79, 0x3a, 0xf4, 0x92, 0xc2, 0x9d, 0xa3, 0xba, 0x4b, + 0xe4, 0x4e, 0xc9, 0xf8, 0x14, 0xa7, 0xe8, 0x41, 0xae, 0xd5, 0xb7, 0xdd, 0xbe, 0xa1, 0x6b, 0xbc, 0x7c, 0xa2, 0xf8, + 0xd6, 0xa6, 0xf2, 0x47, 0x51, 0xa7, 0xd3, 0x18, 0x9b, 0xec, 0x99, 0x72, 0x26, 0x17, 0x67, 0xb9, 0x9f, 0x1a, 0x0c, + 0x8d, 0x78, 0xc4, 0xd5, 0x12, 0xeb, 0xec, 0x3d, 0x66, 0x15, 0x27, 0xbc, 0x21, 0x0d, 0x04, 0xa8, 0xa4, 0x17, 0x1c, + 0xd1, 0x17, 0x68, 0xcb, 0xfa, 0xd2, 0xdd, 0xed, 0x47, 0x7a, 0xdc, 0xc1, 0xd1, 0x68, 0x55, 0x45, 0xbe, 0x4e, 0x0e, + 0x2a, 0xb9, 0x10, 0xa2, 0xd6, 0xf3, 0x1b, 0xd8, 0x42, 0xf3, 0x8b, 0xc9, 0x82, 0xfe, 0x2e, 0x6b, 0x4e, 0xd9, 0x7f, + 0xd6, 0xca, 0xb5, 0x21, 0x40, 0x1e, 0x17, 0xe4, 0xee, 0x15, 0xb8, 0x4c, 0x88, 0xfa, 0xc3, 0x7d, 0xcf, 0x76, 0x22, + 0xf2, 0xa1, 0x46, 0x8b, 0x45, 0xaf, 0x2a, 0x64, 0xbf, 0x3d, 0x1b, 0x77, 0xce, 0x1c, 0xf8, 0x3d, 0x2f, 0xbc, 0x92, + 0x4f, 0xfc, 0x86, 0x86, 0xf4, 0x1e, 0xd6, 0xb3, 0xa2, 0x6b, 0x16, 0x80, 0x52, 0x43, 0x0a, 0x7d, 0x0d, 0xdb, 0x73, + 0x50, 0x69, 0x9f, 0x79, 0x51, 0x8a, 0x80, 0xf1, 0x8d, 0xdd, 0x33, 0xf9, 0x54, 0x56, 0xc4, 0x25, 0x62, 0x96, 0x0e, + 0x18, 0x60, 0x64, 0x8e, 0x91, 0x51, 0xad, 0x1e, 0xaf, 0x70, 0x07, 0x8e, 0x94, 0x60, 0xab, 0xfd, 0xf3, 0xb8, 0x49, + 0xe6, 0xcf, 0x1c, 0x94, 0xfa, 0x84, 0xbc, 0xe7, 0x12, 0x82, 0xf1, 0xfc, 0xe4, 0x40, 0x75, 0xae, 0xc5, 0x06, 0x7b, + 0x3d, 0x67, 0x39, 0xce, 0xbc, 0x07, 0x46, 0xb0, 0xf5, 0xaf, 0xe2, 0x1f, 0xcb, 0x13, 0x77, 0x8b, 0x07, 0x31, 0xa9, + 0x95, 0xd3, 0xb5, 0x36, 0x5b, 0xe8, 0x6e, 0x20, 0xc3, 0x99, 0xe6, 0xcd, 0x9a, 0xba, 0xdc, 0x54, 0xc3, 0xc0, 0x6a, + 0xe6, 0x84, 0x5c, 0xcc, 0x91, 0xf8, 0x2f, 0x18, 0xe7, 0x66, 0x0d, 0x65, 0x2e, 0xc7, 0x66, 0x72, 0x09, 0xe4, 0xea, + 0x14, 0xfb, 0xcd, 0x3f, 0xfc, 0x06, 0x54, 0xc2, 0xf2, 0xa3, 0x7f, 0x88, 0xf2, 0x03, 0xcb, 0xd4, 0x1c, 0x7e, 0xe4, + 0xa8, 0x87, 0x32, 0x97, 0xc7, 0xff, 0x80, 0xac, 0xcf, 0xfa, 0xca, 0xf7, 0x93, 0xbb, 0xef, 0x9b, 0xe4, 0xcf, 0x6c, + 0x35, 0x27, 0x9b, 0x5d, 0x6b, 0xef, 0xe7, 0x7b, 0xf0, 0xbd, 0xf9, 0x3d, 0x32, 0xab, 0x85, 0xde, 0x28, 0xd4, 0x55, + 0x0f, 0x58, 0xe8, 0xd5, 0x4f, 0x7d, 0x8b, 0xd0, 0xec, 0x43, 0xac, 0xc1, 0x39, 0x44, 0x54, 0xba, 0xa7, 0x5e, 0xc3, + 0xb6, 0xbe, 0x77, 0x27, 0x06, 0xba, 0x66, 0x38, 0xef, 0x69, 0x93, 0xc8, 0x6d, 0xd4, 0xc3, 0xe6, 0xfd, 0xd9, 0x15, + 0xd6, 0x44, 0xb7, 0xba, 0x61, 0xe7, 0x52, 0xb2, 0x7c, 0x6b, 0x0f, 0xe0, 0xb1, 0x7a, 0x10, 0xf6, 0xae, 0x99, 0x3f, + 0x96, 0x03, 0x7f, 0x96, 0xf2, 0x4e, 0xb5, 0xb4, 0xfa, 0x8d, 0x6f, 0x55, 0x1f, 0xfb, 0x80, 0x37, 0xc2, 0x53, 0x41, + 0x75, 0xf6, 0x9c, 0x3d, 0x79, 0x71, 0x21, 0xbe, 0xd1, 0x0d, 0x2e, 0xa1, 0x5b, 0x15, 0x79, 0x03, 0x5f, 0xda, 0xbc, + 0xaa, 0xe0, 0x79, 0x68, 0xc9, 0x28, 0x4f, 0x9a, 0x72, 0x6c, 0xe6, 0x76, 0x31, 0x49, 0xb7, 0x32, 0x3f, 0xba, 0x51, + 0x81, 0x0b, 0x04, 0x92, 0x74, 0x65, 0x08, 0xff, 0x04, 0x27, 0x5e, 0x2b, 0xe1, 0xd3, 0x8d, 0x66, 0xbd, 0xd7, 0x55, + 0x3d, 0xee, 0x1a, 0xf4, 0x22, 0x3e, 0xb5, 0xd3, 0x5e, 0x7b, 0x84, 0xbf, 0xbf, 0x7f, 0x9e, 0x69, 0xe4, 0xbf, 0xce, + 0xec, 0xe4, 0x3f, 0xcf, 0xcd, 0xe4, 0xbf, 0xce, 0x0d, 0x9c, 0x5a, 0x7d, 0xcf, 0xbe, 0x7a, 0x61, 0x5f, 0xbd, 0xb2, + 0xc7, 0x4c, 0xed, 0xa1, 0x75, 0xad, 0x73, 0xd0, 0x8e, 0x5d, 0xcf, 0xf5, 0x96, 0x1c, 0xf0, 0xad, 0xae, 0xb2, 0x64, + 0xfd, 0xdb, 0xc9, 0xee, 0xde, 0x15, 0x53, 0xf9, 0xfe, 0x00, 0xc1, 0x93, 0xef, 0x87, 0x65, 0xad, 0xa2, 0x6c, 0xce, + 0xb4, 0x8c, 0xad, 0x74, 0xb6, 0xf7, 0x50, 0x3c, 0x9d, 0x3e, 0x42, 0xb2, 0xad, 0xe1, 0x0c, 0x55, 0x26, 0xf0, 0x1f, + 0x49, 0x3f, 0x36, 0x2a, 0xbd, 0x68, 0xbc, 0x74, 0xef, 0x48, 0xca, 0xf3, 0x17, 0x43, 0xc4, 0xc8, 0xb4, 0x9c, 0xda, + 0x3b, 0x98, 0xba, 0xc7, 0xac, 0xc5, 0xcb, 0x0e, 0xc8, 0x6c, 0xe9, 0x56, 0x52, 0x81, 0x10, 0xc6, 0xb6, 0x85, 0xff, + 0x2c, 0xc0, 0xaa, 0xfa, 0x96, 0x59, 0x3a, 0xcd, 0x9e, 0xa2, 0xa5, 0xd3, 0x0b, 0xd0, 0x20, 0x0e, 0x43, 0x99, 0xee, + 0x0a, 0x99, 0xc3, 0xf3, 0x2a, 0xae, 0x20, 0xab, 0x5f, 0x28, 0xf9, 0xef, 0x73, 0xf6, 0x70, 0xfd, 0x41, 0x40, 0x83, + 0xff, 0xdb, 0x64, 0x3b, 0xe8, 0x4f, 0x68, 0x6b, 0x9c, 0x72, 0x49, 0xa4, 0xfd, 0x5c, 0xc9, 0xdb, 0x33, 0xdf, 0x67, + 0xd7, 0xb7, 0xcf, 0x18, 0xce, 0xcf, 0x55, 0x08, 0x64, 0xce, 0xda, 0x4f, 0xf7, 0xf5, 0x31, 0x15, 0xb9, 0xeb, 0xbc, + 0xe7, 0x04, 0xab, 0xdc, 0x99, 0x52, 0x6b, 0x66, 0x72, 0x7e, 0xfe, 0xf2, 0x3f, 0xcc, 0xaf, 0x25, 0xe5, 0xa0, 0xef, + 0xf5, 0x92, 0xdd, 0xdc, 0x17, 0xca, 0xd2, 0xf3, 0x4c, 0xf9, 0xe8, 0x83, 0x4a, 0x3e, 0x1f, 0xd0, 0x74, 0x3f, 0xdd, + 0xf9, 0x8f, 0xea, 0x01, 0xdd, 0xa6, 0xf9, 0xac, 0xfb, 0x65, 0x49, 0x39, 0xe0, 0x07, 0xbd, 0x7c, 0x7e, 0x7b, 0x8b, + 0x7f, 0x6c, 0x3a, 0xdf, 0xd3, 0x05, 0x80, 0xf0, 0xfc, 0x28, 0xd9, 0x1c, 0x87, 0x9c, 0xc9, 0x9d, 0xeb, 0x0a, 0xcf, + 0xa8, 0x5a, 0x0e, 0x85, 0x5c, 0x2c, 0xf1, 0x19, 0xf9, 0x98, 0x27, 0xb2, 0xd1, 0x27, 0xb0, 0x4b, 0x99, 0xbd, 0x87, + 0x25, 0x64, 0xb7, 0xcd, 0xa7, 0x70, 0x94, 0xcf, 0x3d, 0xa2, 0x6d, 0x76, 0x1d, 0x16, 0x26, 0x6d, 0x69, 0x2a, 0x2e, + 0x3c, 0x60, 0xdf, 0x09, 0x0a, 0x83, 0xd5, 0x48, 0xed, 0x63, 0x46, 0x4e, 0x6f, 0x21, 0xba, 0xce, 0x38, 0x95, 0xbd, + 0xdf, 0xc1, 0x80, 0xa5, 0xf0, 0xf0, 0xd0, 0x7b, 0x0d, 0x68, 0x87, 0xcf, 0xb9, 0xe8, 0xa3, 0x9b, 0x50, 0xaf, 0x06, + 0xe0, 0xc4, 0x59, 0x36, 0xdd, 0x78, 0xb9, 0x9f, 0xf3, 0x87, 0xce, 0xe5, 0xca, 0xea, 0x63, 0x0d, 0x6d, 0x9b, 0xa3, + 0x33, 0xce, 0x57, 0x09, 0x2a, 0x8c, 0x30, 0x67, 0x78, 0xfe, 0xf5, 0xd4, 0x7d, 0xa0, 0x04, 0x7d, 0xa2, 0xd7, 0x9c, + 0x90, 0xd1, 0x7f, 0x22, 0x50, 0xa7, 0x93, 0xb4, 0x67, 0xf5, 0x47, 0xff, 0x1e, 0x3d, 0xb4, 0x4d, 0x8f, 0x7a, 0xab, + 0xe0, 0x3e, 0x85, 0x06, 0xa5, 0x52, 0x69, 0xac, 0x6d, 0x8e, 0x7f, 0x75, 0x72, 0x9d, 0x46, 0x6d, 0x8f, 0x70, 0x76, + 0xa6, 0xcd, 0x79, 0xdc, 0xde, 0xcc, 0xdc, 0xab, 0x17, 0x0f, 0xfd, 0x17, 0xff, 0x65, 0x18, 0x97, 0x8c, 0xb4, 0x20, + 0x37, 0xa9, 0x3d, 0xab, 0x1e, 0x1b, 0xf3, 0xaa, 0x7f, 0xab, 0x7e, 0x64, 0x54, 0xc0, 0xc6, 0x58, 0xcf, 0xe1, 0x32, + 0x3e, 0xcd, 0xeb, 0xa8, 0x28, 0x0b, 0x36, 0xc4, 0xf9, 0x70, 0xbb, 0xd7, 0xde, 0x23, 0x3b, 0xd0, 0xe4, 0xd7, 0x5f, + 0x66, 0xd3, 0x8f, 0xb6, 0xf3, 0x3b, 0x50, 0xcc, 0xfa, 0xfe, 0x94, 0x62, 0x83, 0xba, 0x02, 0xb7, 0x01, 0x97, 0xef, + 0xd8, 0x34, 0xf3, 0xaa, 0xf1, 0xbe, 0x7f, 0xc0, 0x5a, 0x12, 0x8a, 0x56, 0x0a, 0x0e, 0x8b, 0x75, 0x19, 0x45, 0x69, + 0xb1, 0x26, 0xfa, 0x55, 0xa7, 0xaa, 0xd3, 0xb6, 0x1b, 0x38, 0x37, 0x11, 0xa6, 0xaa, 0xb7, 0xa2, 0x1f, 0x22, 0xf2, + 0x36, 0x9e, 0xea, 0xab, 0x6d, 0x20, 0x86, 0xa7, 0xb8, 0x6e, 0xad, 0x7a, 0x0d, 0x67, 0x30, 0xa0, 0x27, 0x7d, 0x71, + 0x0c, 0xc1, 0xc3, 0x97, 0x01, 0x4b, 0xbd, 0xe9, 0xd2, 0xe1, 0xed, 0x63, 0xad, 0xd6, 0x9b, 0x3a, 0xaf, 0x3e, 0x55, + 0x6a, 0xd3, 0xf2, 0x74, 0x8f, 0x92, 0x21, 0xd1, 0xfe, 0xaa, 0x7c, 0xf6, 0xd3, 0x21, 0x63, 0x7b, 0x26, 0x9e, 0x2a, + 0x5e, 0x28, 0x69, 0x79, 0x57, 0xf1, 0x30, 0x8e, 0x3b, 0x29, 0x6a, 0x08, 0x56, 0xfc, 0x63, 0x18, 0x16, 0xe9, 0x9c, + 0xad, 0x0f, 0x75, 0xf0, 0x4a, 0x28, 0xe9, 0xca, 0xb5, 0xd6, 0x0a, 0x74, 0x6c, 0xe3, 0x99, 0x9f, 0x39, 0x9d, 0x09, + 0x50, 0xf9, 0x55, 0x98, 0x04, 0x14, 0x44, 0x22, 0x3c, 0x51, 0x2d, 0xbc, 0x28, 0xfa, 0x0c, 0xe6, 0xd0, 0x0c, 0xab, + 0xc1, 0x34, 0x15, 0xfd, 0x8d, 0x32, 0x30, 0xd7, 0x21, 0x82, 0x17, 0x99, 0x6b, 0xf3, 0x31, 0x0f, 0x1d, 0x8a, 0x9c, + 0x91, 0x53, 0x7f, 0xb0, 0xa4, 0xbc, 0x81, 0x3c, 0x56, 0xa1, 0xf8, 0x57, 0x30, 0x88, 0x73, 0x36, 0x00, 0x85, 0x8c, + 0x3d, 0x8f, 0x00, 0x60, 0x49, 0x3e, 0x49, 0x02, 0x6f, 0xfa, 0xbb, 0xb3, 0xf1, 0x59, 0x51, 0xb0, 0x5f, 0xed, 0x9b, + 0x49, 0xd3, 0x2c, 0xdc, 0xdd, 0xb3, 0x65, 0xf7, 0x14, 0x41, 0x04, 0x48, 0x32, 0x9b, 0x56, 0xec, 0x3d, 0xc4, 0xaf, + 0x14, 0x30, 0x03, 0x93, 0x0c, 0xe0, 0x84, 0x69, 0x49, 0xeb, 0x8a, 0x9f, 0x5c, 0x1d, 0xb6, 0x72, 0x5b, 0x28, 0xc1, + 0x22, 0x32, 0x8f, 0x6e, 0x89, 0x34, 0x4b, 0xe9, 0x9e, 0x5b, 0xeb, 0x3b, 0x19, 0xc7, 0x0f, 0x23, 0xe7, 0x89, 0xe3, + 0xf8, 0x35, 0x89, 0x68, 0x45, 0x44, 0x71, 0xba, 0x75, 0x0e, 0xd9, 0x15, 0x94, 0x8a, 0x15, 0x80, 0xaa, 0x07, 0x4c, + 0x35, 0xc1, 0x9a, 0x5f, 0xdc, 0x05, 0x7b, 0xf9, 0x40, 0x7b, 0x42, 0x71, 0x92, 0xac, 0x8c, 0xf5, 0xd0, 0x17, 0x7c, + 0x85, 0x5d, 0x2e, 0x46, 0x9b, 0x1d, 0x93, 0x24, 0xb5, 0xa2, 0x09, 0x06, 0xd4, 0x35, 0xc3, 0x69, 0xd7, 0xce, 0x3f, + 0x72, 0x9a, 0xd9, 0x74, 0x40, 0x8e, 0x71, 0x29, 0x74, 0x1b, 0xf7, 0xa4, 0x10, 0x47, 0x43, 0xe8, 0xe3, 0x30, 0x14, + 0x46, 0x3f, 0xc3, 0x66, 0x56, 0x9f, 0xf6, 0x31, 0x17, 0xb4, 0x35, 0xa6, 0xa8, 0xaa, 0xcb, 0xae, 0x29, 0x00, 0x1b, + 0x29, 0x67, 0xb0, 0x02, 0xfe, 0x78, 0xd9, 0x4e, 0x57, 0x0f, 0x37, 0x36, 0xf9, 0x0f, 0x6e, 0xf6, 0x1b, 0xe9, 0x27, + 0xf0, 0x47, 0x48, 0x66, 0xd6, 0x04, 0xd6, 0x10, 0xce, 0x4b, 0x62, 0x81, 0xe8, 0x71, 0xbe, 0x1f, 0x04, 0x7f, 0x5c, + 0x2d, 0x1e, 0x14, 0x5b, 0x98, 0xb4, 0x92, 0x73, 0xa2, 0x5e, 0x53, 0xa7, 0x8e, 0x7c, 0x90, 0x98, 0x44, 0x4c, 0x28, + 0xcf, 0xa3, 0x9f, 0x66, 0xb5, 0x9a, 0x05, 0xb5, 0x4d, 0x54, 0xec, 0x15, 0xba, 0x73, 0x3b, 0x67, 0x48, 0xb2, 0x23, + 0x38, 0xd5, 0x65, 0xd9, 0x70, 0x7b, 0xdb, 0x9a, 0x79, 0xd3, 0xf0, 0x35, 0x9d, 0xc3, 0x32, 0xee, 0x82, 0x8e, 0xb5, + 0xf1, 0x9a, 0xd8, 0x1e, 0x0c, 0x1e, 0x16, 0x4f, 0x94, 0x4e, 0xa3, 0xe9, 0xa6, 0x9e, 0x99, 0x9b, 0x7d, 0x4d, 0x5d, + 0x4d, 0xb4, 0xb3, 0x04, 0x9a, 0xcf, 0x46, 0xf1, 0x1a, 0x5b, 0xe6, 0x1a, 0x39, 0xb6, 0x96, 0xb8, 0x5b, 0xe6, 0x1d, + 0x8b, 0x91, 0xbb, 0x81, 0x51, 0x62, 0xee, 0x22, 0x86, 0x9a, 0x9f, 0xc3, 0xdc, 0x9e, 0x98, 0x40, 0xa8, 0x7f, 0x5d, + 0x4f, 0x66, 0x70, 0x31, 0x4d, 0x23, 0x19, 0xd6, 0x83, 0xd2, 0xf7, 0x44, 0x73, 0x8f, 0x78, 0xce, 0x09, 0xb6, 0x6d, + 0x2b, 0x5f, 0x7c, 0xcd, 0x18, 0xf8, 0xc0, 0x54, 0x77, 0x10, 0x5c, 0xd1, 0x5b, 0xd0, 0x3c, 0x83, 0xeb, 0x01, 0xb3, + 0x6f, 0x84, 0xf9, 0xbc, 0x10, 0x75, 0xfb, 0x44, 0x26, 0xff, 0x05, 0x84, 0x62, 0x7a, 0xab, 0xf3, 0x47, 0xfb, 0x1c, + 0xee, 0x3c, 0x64, 0x81, 0xc7, 0x92, 0x38, 0x64, 0xf8, 0xc7, 0x8d, 0xb6, 0x8c, 0x45, 0xcf, 0x9c, 0xc7, 0x2d, 0x89, + 0x09, 0xa5, 0xda, 0x5d, 0x4b, 0xa2, 0xbc, 0x16, 0x61, 0x51, 0x85, 0xd8, 0x6d, 0x15, 0x52, 0x19, 0x75, 0x45, 0xa4, + 0x8a, 0xc7, 0x59, 0x37, 0x3b, 0x43, 0x69, 0x04, 0x19, 0x0a, 0x26, 0xa8, 0x6a, 0x9f, 0x44, 0xb5, 0x14, 0xf3, 0xa0, + 0x4d, 0x13, 0xf5, 0xf0, 0xba, 0x2a, 0x63, 0xe1, 0x71, 0xd6, 0xbd, 0xed, 0x88, 0x75, 0xeb, 0x3a, 0xce, 0xb3, 0x75, + 0xe4, 0xad, 0x1c, 0x99, 0xd7, 0x15, 0x61, 0x2b, 0xc2, 0xf6, 0x41, 0x2d, 0x22, 0xca, 0x50, 0x22, 0xe1, 0xc0, 0x16, + 0xd4, 0xdb, 0x0b, 0x65, 0x36, 0x10, 0xee, 0x95, 0xf5, 0x51, 0xc9, 0x56, 0xd2, 0xb6, 0x95, 0x52, 0xb0, 0x80, 0x42, + 0x58, 0x68, 0xec, 0x39, 0xeb, 0xfe, 0xf6, 0xb9, 0x8e, 0xad, 0xff, 0xdb, 0x40, 0x6c, 0xf6, 0xef, 0xde, 0xdf, 0x8f, + 0x31, 0xc0, 0xa8, 0x7b, 0xd6, 0x15, 0xe9, 0x5b, 0x5d, 0xdf, 0x22, 0x7d, 0xf3, 0xf5, 0x4d, 0x6d, 0x4e, 0x78, 0x96, + 0xb1, 0x36, 0x6a, 0xe3, 0xce, 0x0d, 0xb4, 0x0e, 0xfb, 0x92, 0x92, 0xda, 0xef, 0xdb, 0xe5, 0xa7, 0xb1, 0x2a, 0xf3, + 0xa5, 0x99, 0x94, 0xb2, 0xe9, 0xc1, 0xa9, 0x5a, 0xd3, 0x65, 0x84, 0xd4, 0xbd, 0x18, 0x6a, 0x2b, 0xd5, 0xa9, 0xab, + 0xdb, 0x7c, 0x7c, 0x31, 0x26, 0xc6, 0x2f, 0xff, 0x0a, 0x17, 0xcf, 0x77, 0x4c, 0x87, 0xb6, 0xbc, 0xf3, 0xbe, 0xad, + 0xc4, 0xb8, 0xdc, 0x94, 0x70, 0x8e, 0x66, 0x16, 0x32, 0x46, 0x5c, 0x56, 0x9d, 0xbb, 0xe0, 0x32, 0x82, 0xc0, 0x17, + 0x74, 0x55, 0x29, 0x99, 0xa5, 0xbe, 0xad, 0xa3, 0xcf, 0xf7, 0x44, 0x95, 0xc3, 0x9f, 0x0b, 0x4c, 0xe8, 0x42, 0x57, + 0x95, 0xeb, 0x7b, 0x45, 0xc4, 0x50, 0x14, 0x71, 0xce, 0xa9, 0xf4, 0x2e, 0x2c, 0x7c, 0x53, 0x8f, 0xa7, 0x44, 0x6d, + 0x1b, 0xa4, 0x98, 0xc5, 0x98, 0x4b, 0x4b, 0x31, 0x97, 0xf2, 0x88, 0xed, 0xf3, 0x18, 0x08, 0x8b, 0x49, 0x20, 0xf2, + 0xe1, 0xca, 0x85, 0x63, 0xf9, 0x22, 0x60, 0xb0, 0x8a, 0x3e, 0x10, 0x9c, 0xdf, 0x99, 0x65, 0x17, 0x7f, 0x9b, 0x0f, + 0x47, 0x26, 0xe3, 0x2a, 0x0c, 0x81, 0x3b, 0xe2, 0xb7, 0x4e, 0x3b, 0x94, 0x01, 0xce, 0x19, 0x4d, 0x0c, 0x98, 0x75, + 0xd3, 0x34, 0x38, 0x55, 0x4d, 0x5b, 0xe5, 0x6e, 0x5e, 0x61, 0x26, 0x24, 0x31, 0x10, 0xe5, 0x66, 0xf8, 0x95, 0x1a, + 0x09, 0xc8, 0xf9, 0xfb, 0x2e, 0xce, 0xc9, 0x29, 0x85, 0x13, 0x95, 0x4c, 0x82, 0xaf, 0x1d, 0x78, 0x87, 0xba, 0x15, + 0x2f, 0xc4, 0x71, 0x9a, 0xf2, 0xc8, 0x04, 0xf4, 0x40, 0xed, 0x40, 0x94, 0x55, 0x4b, 0x8e, 0xc2, 0x44, 0x42, 0x28, + 0x85, 0x8f, 0xf8, 0x4c, 0xe6, 0xa2, 0xaa, 0x35, 0xaf, 0xfa, 0x82, 0x6e, 0x41, 0x62, 0x40, 0x54, 0x11, 0x22, 0xc9, + 0xa4, 0x5a, 0x37, 0x54, 0x58, 0x2c, 0x5d, 0x5a, 0x0c, 0xe2, 0x04, 0xc9, 0x3c, 0x2e, 0x04, 0xff, 0x32, 0xb0, 0xb7, + 0x1c, 0x6f, 0x7a, 0xef, 0x06, 0x75, 0x35, 0x32, 0x93, 0x9d, 0xf7, 0xe6, 0x45, 0xaf, 0xa4, 0x25, 0x97, 0x0f, 0x89, + 0x42, 0x7f, 0x5f, 0xb7, 0x9d, 0x65, 0x35, 0x91, 0x82, 0x79, 0x59, 0x54, 0x17, 0x95, 0xed, 0xa5, 0x95, 0x0b, 0x3c, + 0xee, 0x1e, 0x26, 0x48, 0xf0, 0xdd, 0x66, 0xf2, 0x14, 0xb8, 0x48, 0xd6, 0xd8, 0x72, 0x9f, 0x48, 0xa3, 0xa3, 0xdb, + 0x28, 0x59, 0x1d, 0xd9, 0xda, 0x3f, 0x41, 0x94, 0xe4, 0xcc, 0x5a, 0x89, 0xae, 0xff, 0x59, 0xea, 0x26, 0x17, 0x85, + 0xb5, 0x38, 0xe4, 0x20, 0x6e, 0x3a, 0x0b, 0x61, 0x4a, 0xf6, 0x56, 0x60, 0x23, 0x44, 0x86, 0x8b, 0x49, 0x16, 0xe4, + 0xdc, 0x8b, 0x1f, 0x1c, 0x29, 0xf8, 0x8f, 0x48, 0x0d, 0x2d, 0x99, 0xd2, 0xff, 0x70, 0x1d, 0xe1, 0x5b, 0x19, 0x0e, + 0x92, 0xd9, 0x8b, 0x17, 0xdc, 0x96, 0x9e, 0x77, 0xcc, 0x06, 0x49, 0xf8, 0xfd, 0xec, 0xf2, 0x59, 0x6f, 0x0f, 0xe2, + 0x0f, 0x65, 0x42, 0xf0, 0x45, 0x47, 0xb5, 0x8b, 0xa7, 0x51, 0x71, 0x3a, 0x94, 0x5f, 0x8f, 0x4f, 0xcd, 0xef, 0xed, + 0xf2, 0x02, 0x7e, 0xfa, 0xe5, 0x9c, 0x03, 0x33, 0xf0, 0x85, 0xb6, 0x1a, 0x6b, 0xd8, 0x0b, 0x83, 0x3d, 0x86, 0x92, + 0x45, 0x3a, 0xb4, 0x9f, 0x8d, 0x30, 0x1f, 0xba, 0xde, 0x66, 0xfd, 0x1d, 0xc3, 0xac, 0xce, 0x30, 0xbe, 0xb1, 0xaf, + 0x6a, 0x65, 0x76, 0xdb, 0xb0, 0xa7, 0x92, 0x9d, 0xf6, 0xe5, 0x06, 0x53, 0x37, 0x67, 0x6f, 0x43, 0xcd, 0xe5, 0x9b, + 0x51, 0x5c, 0x79, 0x33, 0x0f, 0x4b, 0x08, 0x18, 0x33, 0xcc, 0xb9, 0x22, 0xe7, 0x5a, 0xd9, 0x0f, 0x96, 0xd8, 0x1f, + 0xb6, 0x42, 0xda, 0x54, 0x45, 0x32, 0xb3, 0x81, 0x8f, 0xb5, 0x5a, 0x7b, 0x5a, 0x0f, 0xcc, 0xd2, 0x89, 0xe9, 0x58, + 0xb3, 0xb4, 0x82, 0xa1, 0x54, 0x68, 0xb5, 0xd4, 0x1d, 0xae, 0xd2, 0x97, 0x5a, 0x5e, 0xf2, 0x84, 0x84, 0xfd, 0x04, + 0xb2, 0x13, 0xdf, 0xc3, 0x3d, 0x69, 0xfb, 0xce, 0xac, 0xb1, 0x31, 0x95, 0x25, 0xca, 0x93, 0x72, 0x05, 0x65, 0xea, + 0x1d, 0x60, 0xa8, 0xa8, 0x31, 0x36, 0x74, 0x87, 0x06, 0x6d, 0x34, 0x0e, 0xf7, 0x85, 0xeb, 0x6d, 0x41, 0xfe, 0xa3, + 0xbe, 0xcf, 0xc9, 0x57, 0x67, 0xb3, 0xa8, 0xa7, 0xf5, 0x56, 0x63, 0xe4, 0xc8, 0x78, 0x80, 0xd7, 0x9b, 0x93, 0x2a, + 0x5b, 0x30, 0x64, 0xaf, 0xa1, 0xfe, 0xa9, 0x99, 0xba, 0x90, 0x76, 0x62, 0x46, 0x94, 0xf1, 0x20, 0x92, 0x04, 0x3d, + 0x59, 0x0f, 0x82, 0x6b, 0x96, 0x85, 0xb5, 0xc9, 0xc8, 0x3d, 0x18, 0xce, 0x91, 0x8a, 0xe8, 0x12, 0x8a, 0xe2, 0x9c, + 0xcd, 0xe3, 0x13, 0x86, 0x1c, 0xe5, 0xb1, 0x58, 0x96, 0x2c, 0xa8, 0xf7, 0x2d, 0x8c, 0xd4, 0x64, 0x9b, 0x8e, 0xa5, + 0xe4, 0xb2, 0x03, 0x38, 0xb1, 0xa3, 0xed, 0x3c, 0x61, 0x4e, 0x6d, 0x5d, 0x82, 0x9d, 0xec, 0xd4, 0xdc, 0xad, 0xc8, + 0x00, 0xc9, 0x03, 0x21, 0x0a, 0x03, 0x3e, 0xdf, 0xaf, 0x08, 0x50, 0xcd, 0x71, 0x8a, 0xc4, 0x1f, 0x84, 0xf2, 0xc7, + 0x13, 0x49, 0xa7, 0xc2, 0x72, 0xd7, 0x33, 0xbc, 0x39, 0x0e, 0xa0, 0x95, 0x7a, 0xb2, 0xf9, 0x41, 0x89, 0xb2, 0x91, + 0xbf, 0x8a, 0xb5, 0x8e, 0x18, 0x22, 0x1c, 0xf8, 0xcd, 0x6a, 0x43, 0xd2, 0x78, 0xb3, 0xba, 0x38, 0x1a, 0x85, 0x42, + 0x57, 0x07, 0xdc, 0x47, 0x2a, 0x00, 0xfb, 0x66, 0xc3, 0x53, 0x37, 0x4e, 0x77, 0x51, 0x96, 0x25, 0x9c, 0x06, 0x13, + 0xf8, 0x67, 0xd3, 0xb5, 0xba, 0x85, 0x8b, 0x35, 0xcd, 0xc4, 0x47, 0x71, 0x3a, 0xdd, 0xd7, 0xbd, 0x0e, 0x01, 0xff, + 0x72, 0x89, 0x1d, 0xd2, 0x27, 0xa4, 0x8a, 0x83, 0x11, 0x73, 0x74, 0x8c, 0x4b, 0x9a, 0xe9, 0xa9, 0x21, 0x77, 0x97, + 0xca, 0x47, 0x28, 0x07, 0xaa, 0x73, 0x3c, 0x3d, 0x64, 0x37, 0xc3, 0x31, 0x42, 0x6d, 0x67, 0x88, 0x2b, 0x03, 0xf5, + 0x04, 0xc8, 0x95, 0x04, 0xc2, 0x32, 0xcf, 0x67, 0x48, 0xdf, 0x33, 0x66, 0x02, 0x1a, 0x3a, 0x50, 0x6e, 0x7a, 0x52, + 0xe6, 0x90, 0x7a, 0xa8, 0x83, 0x10, 0x13, 0x1e, 0xf4, 0xb2, 0xa9, 0x69, 0x65, 0x1d, 0x8d, 0x50, 0x69, 0x42, 0x41, + 0xfc, 0x02, 0xa7, 0xe8, 0xab, 0x21, 0xf2, 0x97, 0x91, 0xf2, 0x3a, 0x2b, 0xf3, 0x86, 0xf4, 0x12, 0x2d, 0xb2, 0xfa, + 0xc6, 0xc8, 0xec, 0x48, 0x5d, 0x56, 0x7a, 0xed, 0x05, 0x60, 0x1e, 0x0e, 0xc1, 0x89, 0x44, 0xc4, 0x3c, 0x89, 0x26, + 0xb2, 0xa9, 0x50, 0xfe, 0xcc, 0xee, 0x49, 0x01, 0x5c, 0xce, 0x23, 0x41, 0x13, 0x81, 0x8f, 0x1d, 0x00, 0x67, 0x66, + 0x10, 0xe0, 0x6c, 0x35, 0x69, 0x04, 0xc6, 0x5c, 0x2b, 0x6f, 0x35, 0xfb, 0x98, 0x11, 0xe5, 0xb8, 0x98, 0x1b, 0xd9, + 0x5d, 0x93, 0xfb, 0x53, 0xcc, 0x13, 0x1b, 0x73, 0xf8, 0xb9, 0xf6, 0x2a, 0x99, 0xfe, 0x65, 0x06, 0x3e, 0x29, 0x51, + 0x7d, 0x69, 0x50, 0xbc, 0x6e, 0xe3, 0x82, 0x36, 0xda, 0x35, 0xe4, 0xb2, 0xe8, 0x30, 0x58, 0xae, 0xfd, 0xbf, 0x7e, + 0x7b, 0x3e, 0xef, 0x2b, 0xe7, 0x63, 0x76, 0xc5, 0x7d, 0x70, 0x58, 0x33, 0xe4, 0xfc, 0xba, 0x2e, 0x9e, 0xe3, 0xfb, + 0xf5, 0xb7, 0xb9, 0xf1, 0x74, 0x77, 0x10, 0x64, 0x2e, 0xa4, 0x3e, 0xb3, 0x84, 0xe8, 0xc3, 0xd0, 0xe2, 0xd9, 0x18, + 0x55, 0xa2, 0xf1, 0xa5, 0x43, 0x8a, 0x65, 0x8b, 0xa7, 0x27, 0x81, 0x78, 0x39, 0xdc, 0x93, 0x2d, 0x10, 0x2b, 0x4a, + 0x84, 0x39, 0x9d, 0x88, 0x34, 0x8e, 0x80, 0xf1, 0x4a, 0xdc, 0x33, 0x04, 0x46, 0x1a, 0x65, 0xd6, 0xb4, 0xff, 0xd8, + 0x88, 0xec, 0x73, 0x48, 0x34, 0x19, 0x36, 0xe5, 0x93, 0xcd, 0xa8, 0xbd, 0x12, 0x09, 0x45, 0xc3, 0xba, 0x9f, 0xa6, + 0x19, 0x95, 0xf7, 0x62, 0x1c, 0x12, 0x87, 0x70, 0xd2, 0xbb, 0xdf, 0xaf, 0xbf, 0x95, 0x3c, 0xfc, 0x1e, 0xf6, 0x1f, + 0xbf, 0xf8, 0x1f, 0xbf, 0x87, 0x7b, 0xf2, 0x8b, 0x9f, 0xfc, 0x1e, 0xf2, 0xc9, 0x2f, 0xe2, 0xa5, 0xd2, 0xf4, 0x95, + 0xdd, 0x79, 0x30, 0x16, 0x0c, 0xe5, 0xb2, 0x8c, 0x6c, 0xa5, 0x0a, 0x7e, 0xf1, 0x21, 0xe1, 0x3e, 0x17, 0x48, 0xc9, + 0xa9, 0x64, 0x82, 0x95, 0xa8, 0x64, 0x65, 0xe8, 0x14, 0xd4, 0xa7, 0x01, 0x3e, 0x4a, 0xbd, 0xfd, 0x9c, 0x7f, 0xba, + 0x35, 0x92, 0xc6, 0x40, 0x3c, 0x19, 0x82, 0xae, 0xdc, 0x99, 0x5b, 0xcf, 0x4d, 0x49, 0x18, 0x65, 0x39, 0x62, 0xb4, + 0xa2, 0xd2, 0x8e, 0xb3, 0x44, 0xef, 0x3c, 0x18, 0x34, 0x13, 0xf4, 0xed, 0x7b, 0xe8, 0xa4, 0xb0, 0x3b, 0x43, 0x01, + 0x72, 0x96, 0x95, 0x02, 0x1e, 0xd8, 0xc7, 0x5e, 0x3c, 0x47, 0x5a, 0x79, 0x35, 0xa9, 0xa2, 0x06, 0xd7, 0xe4, 0x60, + 0x8c, 0x11, 0x12, 0xf7, 0xf4, 0x2f, 0xf9, 0x98, 0x9c, 0xb9, 0x79, 0xab, 0x59, 0xb8, 0xc7, 0xd4, 0x72, 0x40, 0x73, + 0x62, 0x54, 0xcd, 0x0c, 0x5b, 0x44, 0xad, 0x59, 0xcd, 0x99, 0x45, 0x9c, 0x2c, 0xc5, 0xd6, 0x55, 0xd8, 0xf3, 0x1e, + 0x3f, 0xe5, 0x1f, 0xe6, 0x34, 0x57, 0x8f, 0x34, 0xd8, 0x17, 0x19, 0xbb, 0x0f, 0xae, 0x70, 0x5a, 0x6b, 0x30, 0x3d, + 0xe1, 0x6c, 0x2d, 0xae, 0xaf, 0xa6, 0xf0, 0x05, 0x69, 0x75, 0xcf, 0xa5, 0x88, 0x46, 0x37, 0xc9, 0xc4, 0x86, 0xa1, + 0xb5, 0xd9, 0x7d, 0x6d, 0xa1, 0xd1, 0x66, 0x05, 0xad, 0x59, 0xd9, 0xfd, 0xe6, 0x8d, 0x36, 0xb1, 0xc9, 0x9c, 0x05, + 0x99, 0xa8, 0xba, 0x09, 0xd2, 0xa6, 0xc0, 0x27, 0x27, 0x2b, 0x8c, 0x47, 0x20, 0x8b, 0xdc, 0xe6, 0x64, 0x7f, 0xe9, + 0xa8, 0x65, 0x54, 0x95, 0x10, 0x89, 0xcf, 0xca, 0x2d, 0xe4, 0x12, 0x74, 0xbc, 0x38, 0x10, 0xc1, 0xe5, 0x30, 0x2e, + 0x95, 0x9a, 0x46, 0xdb, 0x35, 0xda, 0x5b, 0xc8, 0x73, 0xa8, 0xcb, 0x4f, 0x83, 0x0d, 0x61, 0x88, 0x6a, 0xf4, 0xa1, + 0xcd, 0x3c, 0xbd, 0xa6, 0x4b, 0xfb, 0xf5, 0xf7, 0x01, 0x38, 0x7a, 0xb1, 0xbd, 0x90, 0xcc, 0x5d, 0x9f, 0x92, 0x48, + 0x20, 0x51, 0xf2, 0x05, 0xa0, 0x07, 0x80, 0x5e, 0xf5, 0x12, 0x56, 0x03, 0x06, 0xad, 0x54, 0x81, 0x9e, 0x29, 0x78, + 0x00, 0x32, 0x43, 0xcb, 0x41, 0xe5, 0x8f, 0x48, 0xf0, 0xb5, 0x43, 0xb2, 0x98, 0xf0, 0xd2, 0x50, 0xbc, 0x8e, 0x09, + 0xed, 0x7c, 0x98, 0x9a, 0x5e, 0x22, 0xf7, 0x14, 0x29, 0x1d, 0xb1, 0x45, 0x3f, 0xfd, 0xf4, 0xaa, 0xa7, 0x85, 0x93, + 0x3c, 0xb2, 0x7c, 0xac, 0xfd, 0x5b, 0xd6, 0xb6, 0xab, 0xea, 0x8f, 0x4c, 0x49, 0x1d, 0x68, 0x43, 0x28, 0xd7, 0x33, + 0x65, 0x4f, 0xe9, 0x2b, 0xd8, 0x59, 0x0c, 0x8b, 0x5e, 0xbb, 0xcf, 0x6a, 0x73, 0xf8, 0xd0, 0x45, 0x0f, 0x44, 0x13, + 0x6e, 0x5f, 0x23, 0x81, 0xe6, 0x12, 0xc1, 0x62, 0x78, 0x46, 0x97, 0x76, 0xe3, 0x43, 0x4e, 0x51, 0x10, 0xab, 0xc0, + 0x87, 0x74, 0xfd, 0x84, 0x86, 0x0c, 0x65, 0xbb, 0x8d, 0x02, 0x67, 0x35, 0xd0, 0x7c, 0x5f, 0xe3, 0xb0, 0x57, 0x27, + 0x60, 0x6d, 0xc9, 0x7c, 0xb5, 0x69, 0xa3, 0xd8, 0x6b, 0x2e, 0xaf, 0xf6, 0xda, 0x0a, 0x81, 0x3f, 0x17, 0x9f, 0xfd, + 0xed, 0x79, 0x52, 0x7d, 0x9f, 0x9f, 0x94, 0xde, 0xdb, 0xac, 0xfa, 0xa0, 0x35, 0xd8, 0xfb, 0xe3, 0x94, 0xf7, 0x91, + 0xe5, 0x30, 0x29, 0x3d, 0x1f, 0x8d, 0x6a, 0xb1, 0x7b, 0x4d, 0xe6, 0xf1, 0x61, 0x25, 0x54, 0xb3, 0xa9, 0x91, 0x07, + 0xf7, 0x5a, 0x73, 0xa1, 0xef, 0x51, 0xa0, 0xba, 0xd7, 0xc2, 0xa9, 0xba, 0x2a, 0x25, 0x88, 0xc9, 0xc8, 0x68, 0xa6, + 0xd9, 0x58, 0x6f, 0x03, 0xf3, 0x71, 0xaa, 0x5f, 0xf0, 0x27, 0x52, 0x72, 0xd8, 0xed, 0xac, 0x2c, 0x4a, 0xc5, 0x24, + 0x25, 0xa0, 0xc5, 0xf6, 0x6f, 0x71, 0x70, 0x60, 0x50, 0xb5, 0xea, 0x3c, 0x60, 0x24, 0xf6, 0xc5, 0xe2, 0x23, 0x50, + 0xf1, 0x5b, 0x3b, 0xc8, 0xec, 0x86, 0x8f, 0x65, 0x29, 0x2c, 0xfc, 0x20, 0x4a, 0xa5, 0x9e, 0x80, 0x40, 0x4d, 0x9d, + 0xbc, 0x29, 0x41, 0xb0, 0x7c, 0x33, 0xa7, 0x8d, 0xbd, 0x30, 0x5d, 0x1d, 0xc8, 0xb5, 0x69, 0x24, 0x86, 0x22, 0xfe, + 0xc9, 0xb1, 0xe1, 0x3a, 0x9a, 0xb0, 0xea, 0x89, 0xe5, 0x5e, 0x94, 0x07, 0xa1, 0x41, 0xe8, 0x90, 0xa7, 0xca, 0x6d, + 0x19, 0xd6, 0xe7, 0x2d, 0x2f, 0x4f, 0xfa, 0x17, 0x1e, 0x1f, 0x2c, 0x3a, 0x7f, 0x42, 0x33, 0x17, 0x02, 0x29, 0xa8, + 0x62, 0x93, 0xc2, 0x1d, 0xa1, 0x2a, 0xcb, 0x9d, 0x97, 0x15, 0xcd, 0x6b, 0x33, 0x0f, 0xd2, 0xd5, 0x47, 0x05, 0x99, + 0x4b, 0x28, 0x09, 0xa5, 0x2e, 0x60, 0x0a, 0xa3, 0x2c, 0xde, 0xe8, 0xbb, 0xf5, 0x0f, 0xbb, 0x94, 0x84, 0x03, 0x3e, + 0x86, 0xc1, 0x4c, 0xe0, 0xdf, 0x0f, 0x29, 0x0d, 0xdc, 0xd4, 0xba, 0x16, 0xca, 0x18, 0xd2, 0x0a, 0xc1, 0x7c, 0x24, + 0xd1, 0x60, 0x82, 0xef, 0x3b, 0x83, 0x22, 0x27, 0x05, 0x2b, 0x8d, 0xdf, 0x8c, 0x7b, 0x0c, 0x1d, 0x67, 0xc6, 0x3b, + 0x3b, 0x5d, 0xb1, 0xb7, 0xe6, 0xb8, 0x3a, 0x84, 0x80, 0xcb, 0xb1, 0xdc, 0xca, 0xba, 0x20, 0xeb, 0x18, 0xf2, 0x2c, + 0xdc, 0x22, 0x71, 0xc9, 0x08, 0x3d, 0xa5, 0x43, 0x23, 0x95, 0x61, 0x09, 0x4e, 0x9b, 0xe1, 0x03, 0xdb, 0xb8, 0x82, + 0xba, 0x9d, 0x9d, 0x06, 0xea, 0xf6, 0x0a, 0x78, 0xb0, 0x6b, 0x42, 0x89, 0xd2, 0xc8, 0xaa, 0x80, 0x06, 0x23, 0xa0, + 0x2d, 0x0b, 0x94, 0x6a, 0x22, 0x26, 0x1a, 0x85, 0x51, 0x22, 0xb5, 0x94, 0xb2, 0xa3, 0xe9, 0x77, 0x5d, 0x24, 0x93, + 0x64, 0x1d, 0x8a, 0x83, 0x9e, 0x98, 0x24, 0xb5, 0x5a, 0x97, 0x2d, 0x3e, 0x1c, 0x88, 0xfd, 0x22, 0x95, 0x9e, 0xd8, + 0xdb, 0x69, 0x81, 0xdc, 0xec, 0x7b, 0x1a, 0x52, 0x43, 0xa3, 0xb3, 0xad, 0xd1, 0x79, 0x79, 0x2a, 0x9b, 0x1f, 0x74, + 0xd4, 0x72, 0xeb, 0xc6, 0x98, 0xa2, 0x0a, 0xa8, 0x3f, 0xd6, 0x82, 0xf4, 0xfd, 0x4b, 0xa1, 0x4e, 0x50, 0x34, 0x4c, + 0xed, 0x7b, 0x2c, 0x46, 0xba, 0x4e, 0xf3, 0x48, 0x48, 0x70, 0xef, 0x09, 0x02, 0x3c, 0x22, 0x4f, 0x23, 0x19, 0xd3, + 0x09, 0xc2, 0x10, 0x91, 0x75, 0xb2, 0xe6, 0x7d, 0x6e, 0xfd, 0xfe, 0x92, 0xbc, 0xef, 0xe2, 0x06, 0x93, 0xab, 0xfd, + 0x94, 0xde, 0xfb, 0xed, 0x76, 0x68, 0xed, 0x71, 0x12, 0x37, 0xe3, 0x85, 0xa5, 0xf6, 0x58, 0xd8, 0xff, 0x66, 0xf3, + 0xa9, 0x53, 0xa5, 0xb7, 0x6b, 0x0d, 0x69, 0x3c, 0xb3, 0xc6, 0x66, 0x3f, 0x09, 0xda, 0x91, 0x0b, 0xb4, 0x13, 0x3b, + 0x39, 0xab, 0x20, 0xa1, 0x21, 0x31, 0xa6, 0xb6, 0x73, 0x08, 0xd0, 0x8c, 0x75, 0xe6, 0xf6, 0xad, 0xf6, 0xed, 0x29, + 0x27, 0x65, 0x80, 0xf2, 0x52, 0xf8, 0x67, 0xdb, 0x49, 0x89, 0x7d, 0x1c, 0x63, 0x6c, 0x05, 0xf1, 0x21, 0x81, 0x54, + 0x05, 0x13, 0x5a, 0x4d, 0x1e, 0xd0, 0xc5, 0x29, 0x1d, 0x7f, 0xa6, 0x1f, 0x3e, 0xc0, 0xea, 0x6b, 0x1e, 0xd9, 0x66, + 0x0f, 0x1c, 0x63, 0x4a, 0xbd, 0xce, 0x0e, 0x58, 0x3f, 0xa5, 0xf7, 0xba, 0x58, 0x1b, 0x43, 0xca, 0x96, 0x5c, 0xbb, + 0xb6, 0x08, 0x99, 0x30, 0x64, 0x5d, 0x47, 0x28, 0xac, 0xe0, 0xfc, 0x86, 0x9c, 0xc0, 0xea, 0xfd, 0x9c, 0x2b, 0xf5, + 0x2c, 0x52, 0xb3, 0x4c, 0xd0, 0xce, 0x8e, 0x1c, 0xe9, 0x3c, 0xa9, 0xff, 0x6f, 0x25, 0x84, 0xe0, 0xd2, 0x9a, 0x6e, + 0x4b, 0xa8, 0x93, 0xfc, 0xe4, 0x2a, 0x5a, 0xc0, 0x73, 0x37, 0xca, 0x1f, 0xc9, 0xea, 0x6d, 0x82, 0x67, 0x83, 0x48, + 0x60, 0xc3, 0x72, 0x4a, 0x54, 0xc3, 0x6a, 0xab, 0x5b, 0xf8, 0xee, 0xd1, 0xed, 0x8d, 0x62, 0x0c, 0x15, 0x4e, 0x7e, + 0x0e, 0x94, 0x54, 0xdc, 0xeb, 0x92, 0x5a, 0x47, 0xe5, 0x7f, 0xa3, 0xb8, 0xc2, 0x49, 0x7c, 0x73, 0x93, 0xb3, 0x81, + 0x47, 0xdd, 0x53, 0x43, 0xb2, 0xbf, 0x5f, 0xa8, 0x10, 0x6d, 0xb4, 0x8e, 0x19, 0xa0, 0x0a, 0x1f, 0x41, 0x2e, 0x47, + 0xbe, 0x9f, 0x75, 0xe5, 0x17, 0xf9, 0xa5, 0x6f, 0xcf, 0x0d, 0x62, 0xcd, 0x5c, 0xa8, 0x59, 0xca, 0x28, 0xbf, 0x0c, + 0x6f, 0xe2, 0xb6, 0xc8, 0x20, 0xab, 0xcf, 0x6b, 0xec, 0x1d, 0x62, 0xe5, 0xd8, 0x6d, 0x4f, 0x58, 0x41, 0x4c, 0x90, + 0x2e, 0xc1, 0x53, 0x5d, 0x50, 0xc4, 0x28, 0x35, 0x67, 0x38, 0xd5, 0xa2, 0xba, 0x50, 0xce, 0xd5, 0x7a, 0x49, 0x05, + 0x84, 0xea, 0x7b, 0x2a, 0xe7, 0x25, 0x30, 0xec, 0x9d, 0xc7, 0x7e, 0xb0, 0x3c, 0x6f, 0xea, 0x5a, 0x99, 0x9d, 0xa6, + 0xeb, 0x1e, 0x2a, 0x1c, 0x68, 0x53, 0x7a, 0x4b, 0x57, 0xf3, 0x7c, 0xad, 0x16, 0xf8, 0x6d, 0x68, 0xc1, 0x33, 0xe7, + 0x13, 0xd0, 0x57, 0xc9, 0x23, 0x89, 0x3b, 0x4b, 0xd7, 0xae, 0x80, 0x16, 0x26, 0x93, 0xc0, 0x83, 0xd3, 0x7d, 0xad, + 0x92, 0xb5, 0x91, 0x70, 0x4c, 0x08, 0x03, 0x72, 0xd6, 0x07, 0xdb, 0x6e, 0x8c, 0x5c, 0xa2, 0xf6, 0xfa, 0x91, 0x86, + 0x16, 0x59, 0x3f, 0x68, 0xd2, 0xf3, 0x40, 0x51, 0x39, 0xaa, 0xde, 0xdc, 0x29, 0xa3, 0x87, 0x98, 0x27, 0x8c, 0xda, + 0xc4, 0xa0, 0x91, 0x1e, 0xa8, 0x33, 0x42, 0xce, 0x4f, 0x6c, 0x52, 0x7d, 0x8d, 0x0f, 0x9f, 0x09, 0x61, 0xac, 0x36, + 0x0d, 0xf9, 0x3c, 0x81, 0xf6, 0x6c, 0xe9, 0xb8, 0x53, 0x43, 0x86, 0xd7, 0xa6, 0xcb, 0x21, 0x19, 0x0b, 0x2e, 0x9b, + 0x21, 0x0c, 0x6a, 0x25, 0xe3, 0x34, 0xb1, 0xcf, 0xa9, 0x1b, 0x49, 0x57, 0xe5, 0x1a, 0x02, 0x1c, 0x77, 0x9c, 0x49, + 0xb3, 0xd8, 0x72, 0x8b, 0x92, 0xab, 0x4b, 0x4d, 0x88, 0x2d, 0x9a, 0x88, 0x12, 0x00, 0x7a, 0x39, 0xec, 0x23, 0x20, + 0xe1, 0xdb, 0x0a, 0xe7, 0xe6, 0x89, 0x2d, 0xad, 0x5c, 0x73, 0x41, 0x61, 0xb8, 0xa3, 0xaf, 0xf7, 0x62, 0x53, 0x11, + 0x7b, 0x06, 0xf3, 0xd0, 0x6c, 0x2c, 0xb3, 0xf9, 0x23, 0xdf, 0x9f, 0x87, 0x66, 0x20, 0xfd, 0x03, 0x16, 0xc4, 0x7f, + 0x0d, 0x15, 0xe2, 0x19, 0x17, 0xe4, 0x0f, 0xb4, 0x92, 0x86, 0x2f, 0x58, 0xb7, 0xd3, 0x95, 0x9f, 0x4d, 0x9f, 0xaa, + 0x05, 0x04, 0xe5, 0x81, 0x5c, 0x48, 0x73, 0x03, 0x6b, 0xbc, 0xc1, 0x8a, 0xf5, 0xc6, 0x0e, 0x49, 0x60, 0xeb, 0xe9, + 0x48, 0x26, 0x8d, 0x74, 0x8a, 0x07, 0xbe, 0xd5, 0xb1, 0xfd, 0xad, 0xce, 0x29, 0xbd, 0x29, 0x4f, 0x9b, 0xe6, 0xad, + 0x78, 0xe8, 0x59, 0x5b, 0x45, 0x98, 0x30, 0x78, 0x2a, 0x9c, 0xf0, 0x7a, 0x2f, 0x57, 0xd9, 0x35, 0x7c, 0x06, 0x3f, + 0xf4, 0x6c, 0x30, 0x17, 0x36, 0xd7, 0x22, 0x41, 0x07, 0x61, 0xbc, 0xf1, 0xf9, 0x11, 0x46, 0xa6, 0x4b, 0xe9, 0x15, + 0xfd, 0x68, 0x90, 0x28, 0xde, 0xae, 0xbf, 0xdd, 0x7d, 0x8f, 0xe0, 0xe0, 0xde, 0x82, 0x6c, 0x4c, 0x9b, 0xbd, 0x61, + 0x0f, 0x69, 0x51, 0xd5, 0x18, 0x23, 0xa4, 0x42, 0x1c, 0x43, 0xc4, 0xe5, 0xf6, 0x55, 0x5b, 0x1e, 0xdc, 0xf2, 0x4b, + 0x9e, 0x51, 0xf8, 0x28, 0xfe, 0xce, 0x7c, 0xd7, 0x47, 0xe8, 0x8a, 0xeb, 0x3c, 0x87, 0xf8, 0xda, 0x6f, 0xaf, 0x91, + 0x10, 0x25, 0xe1, 0x7f, 0x06, 0x0f, 0x30, 0x33, 0x5e, 0xac, 0x01, 0x7b, 0x5e, 0xdd, 0xc8, 0x49, 0x70, 0x5f, 0x30, + 0xf4, 0xb6, 0xf9, 0x42, 0x3f, 0x9e, 0x92, 0x78, 0x8b, 0xb6, 0x88, 0x5d, 0xa9, 0x83, 0x19, 0x3b, 0x71, 0xcd, 0x87, + 0xc9, 0xec, 0x3f, 0x46, 0x58, 0x00, 0x84, 0x82, 0x5a, 0x0b, 0x3f, 0x6d, 0x05, 0x70, 0xab, 0xff, 0x60, 0xa4, 0xc0, + 0x4d, 0xf4, 0xc4, 0xcf, 0x76, 0x4f, 0xb0, 0x09, 0x4e, 0xc4, 0x5e, 0x91, 0xb6, 0xe7, 0x40, 0xaf, 0x56, 0x35, 0x84, + 0xea, 0xd6, 0xe9, 0x20, 0x74, 0xb1, 0x28, 0x8c, 0xf5, 0x3a, 0x0a, 0x6c, 0x56, 0x2d, 0xab, 0x0e, 0x43, 0x6d, 0x57, + 0xa1, 0xf6, 0x24, 0x1b, 0x16, 0x25, 0x2a, 0x72, 0xe3, 0x78, 0x53, 0xac, 0x03, 0xea, 0xd7, 0x7e, 0x6d, 0x82, 0x5b, + 0x2f, 0x78, 0x74, 0x2c, 0xc8, 0xd5, 0x14, 0x31, 0x78, 0x81, 0xc8, 0xe0, 0x55, 0x59, 0xa0, 0x93, 0x5e, 0xb8, 0xef, + 0x9b, 0x4f, 0x75, 0x61, 0xe9, 0x6e, 0x1a, 0x3e, 0xfb, 0x79, 0xf4, 0xab, 0xe1, 0xeb, 0x25, 0x63, 0x64, 0x5c, 0x24, + 0x2d, 0x7a, 0xea, 0x1c, 0x97, 0x6b, 0x30, 0x7b, 0x68, 0x75, 0xcc, 0xb0, 0xfb, 0x74, 0xa5, 0xc5, 0x18, 0xbf, 0x13, + 0xc5, 0xb4, 0x07, 0xcb, 0x32, 0x13, 0xf7, 0xf4, 0x82, 0x00, 0x69, 0x2d, 0xf1, 0xa6, 0xd5, 0x5b, 0x6d, 0x7d, 0x36, + 0x2d, 0x83, 0xe8, 0x1b, 0x8b, 0x4c, 0xdd, 0x2c, 0x64, 0xb9, 0x4c, 0xb1, 0x46, 0xab, 0xb0, 0x2f, 0x97, 0x47, 0x37, + 0x7d, 0x5d, 0x1a, 0xff, 0x16, 0x55, 0x4f, 0x86, 0x44, 0xd2, 0x12, 0xa5, 0x52, 0x81, 0x93, 0x2e, 0xec, 0x62, 0x4d, + 0x47, 0x2d, 0xd7, 0x89, 0x33, 0xde, 0x8f, 0x97, 0x0e, 0xcb, 0x1f, 0x9f, 0x0b, 0x42, 0xad, 0xfc, 0x3f, 0x10, 0xfb, + 0xec, 0x70, 0x32, 0xa0, 0x9c, 0xc2, 0x19, 0xd9, 0xfd, 0x0f, 0xba, 0xda, 0x15, 0x40, 0xcd, 0x30, 0x7a, 0xb9, 0x54, + 0x38, 0x54, 0x94, 0x7e, 0x3a, 0xe9, 0xc6, 0x50, 0x58, 0x5f, 0xad, 0x85, 0xd7, 0x5e, 0x52, 0xd1, 0x25, 0xfe, 0x4a, + 0xfa, 0x98, 0x70, 0x2a, 0x65, 0x87, 0xfa, 0xaa, 0x21, 0x01, 0xa0, 0x43, 0xbc, 0x12, 0x01, 0x37, 0xf3, 0x16, 0x34, + 0x99, 0xc8, 0xb8, 0xf8, 0xe0, 0x02, 0xb8, 0x30, 0xde, 0x3e, 0xcd, 0x40, 0xb2, 0xd6, 0x12, 0x3b, 0x09, 0xdd, 0xf4, + 0x31, 0x61, 0x04, 0x48, 0xb0, 0xe3, 0x01, 0x34, 0x79, 0x27, 0xbc, 0xc7, 0x7a, 0x35, 0x31, 0x05, 0x41, 0x44, 0xf7, + 0x9e, 0x83, 0xdd, 0x5c, 0xcb, 0x6a, 0x85, 0x4d, 0x88, 0xcd, 0x8e, 0xaa, 0xef, 0xa7, 0x0a, 0xbc, 0x5e, 0x98, 0x54, + 0x6c, 0x14, 0xba, 0x4e, 0x1e, 0x68, 0x1c, 0x60, 0x3a, 0x4b, 0x0e, 0x35, 0x5c, 0xf9, 0x50, 0x96, 0x93, 0x94, 0xd0, + 0x52, 0x38, 0xe0, 0x0c, 0x24, 0x07, 0xff, 0x63, 0x41, 0x03, 0x59, 0x87, 0x9f, 0x18, 0xd7, 0xe0, 0x5f, 0x48, 0x6b, + 0x9a, 0x16, 0xd1, 0x6a, 0xaf, 0x61, 0x0d, 0x9a, 0x97, 0xc9, 0x97, 0x13, 0x03, 0xd8, 0xac, 0x16, 0xb2, 0xfa, 0xb1, + 0xe7, 0x9a, 0x3f, 0x52, 0x7e, 0xca, 0x42, 0xed, 0xa9, 0x9e, 0xb6, 0x42, 0xb2, 0xd3, 0xb4, 0xa8, 0x88, 0xe2, 0x7a, + 0xb2, 0x5d, 0x17, 0x2f, 0xbe, 0x88, 0x04, 0x7e, 0x31, 0x81, 0x18, 0x12, 0x40, 0x60, 0x70, 0x04, 0x35, 0x24, 0x74, + 0xd4, 0xd7, 0x9b, 0xc7, 0x57, 0x15, 0x04, 0xcd, 0x63, 0xa6, 0x80, 0x98, 0xae, 0x98, 0x9d, 0xbf, 0x04, 0x5a, 0xf1, + 0xfe, 0x0d, 0xd6, 0x55, 0xcd, 0x9f, 0x37, 0x69, 0xe3, 0x17, 0xd6, 0x7f, 0xd4, 0xb1, 0x2a, 0xb0, 0x21, 0x36, 0xa8, + 0x52, 0x24, 0xac, 0x32, 0x06, 0x88, 0x46, 0xcf, 0x5c, 0x45, 0x9a, 0xc2, 0xfe, 0xee, 0x3c, 0x1e, 0xd4, 0x3a, 0xb5, + 0xf9, 0xa6, 0xe7, 0x52, 0x62, 0x09, 0x97, 0x99, 0xe9, 0x73, 0x39, 0x00, 0x32, 0xd3, 0x83, 0xdc, 0x40, 0x83, 0xaf, + 0xc1, 0xab, 0x2b, 0xe6, 0x2c, 0x3d, 0xbb, 0x1f, 0x36, 0x7e, 0x7f, 0x95, 0x5e, 0xd1, 0x3b, 0x18, 0x99, 0x6f, 0xee, + 0xf5, 0xee, 0x5a, 0x5d, 0xbf, 0xb0, 0x98, 0x51, 0x97, 0xaa, 0xe5, 0xe9, 0xe7, 0xed, 0xbe, 0x2f, 0x1e, 0xac, 0xfd, + 0x29, 0x28, 0x63, 0x7b, 0x92, 0x77, 0xad, 0xe4, 0xc6, 0xbf, 0x40, 0xd3, 0xaa, 0xa0, 0x96, 0x91, 0x29, 0x6f, 0x6b, + 0xbf, 0xe5, 0xba, 0xbc, 0x3d, 0x91, 0x71, 0xc4, 0xb9, 0x63, 0xc8, 0xfb, 0xd2, 0x36, 0x3e, 0xf7, 0x1a, 0x02, 0x85, + 0x5f, 0x9e, 0x4e, 0x29, 0x68, 0x6b, 0xc2, 0x25, 0xe2, 0x0c, 0x2d, 0xaf, 0x4b, 0x37, 0xc5, 0x20, 0x72, 0xf4, 0x81, + 0xdd, 0xd2, 0x86, 0xe0, 0xdb, 0x22, 0xfc, 0x6c, 0x26, 0xd4, 0x93, 0xad, 0x40, 0xad, 0x88, 0x2a, 0x7b, 0x88, 0x16, + 0x02, 0xcb, 0x89, 0xe4, 0xa4, 0x37, 0x75, 0x26, 0x90, 0x60, 0xea, 0x15, 0x6f, 0xbb, 0x60, 0xc8, 0x62, 0x97, 0x2b, + 0x0c, 0x2c, 0xa2, 0x64, 0x2a, 0x7e, 0xbd, 0x3c, 0x95, 0x46, 0x0b, 0x0c, 0x01, 0x4c, 0x73, 0x2f, 0x2f, 0x1a, 0x03, + 0xee, 0xfe, 0xee, 0x46, 0x9a, 0x6e, 0x48, 0xe0, 0x9b, 0x67, 0xf3, 0x5e, 0x4a, 0x06, 0x7a, 0x6e, 0xf2, 0xeb, 0x49, + 0xda, 0x89, 0x9c, 0x93, 0xda, 0x9c, 0xe1, 0x10, 0xa0, 0xaa, 0xd9, 0x43, 0x9a, 0x56, 0xa5, 0xec, 0xc4, 0x25, 0x90, + 0xe5, 0x37, 0x11, 0xf8, 0xf2, 0xcb, 0x63, 0xec, 0x9d, 0x8a, 0xcc, 0x14, 0x61, 0x4f, 0x94, 0x4f, 0x1b, 0x56, 0x77, + 0xf3, 0xf0, 0x34, 0x47, 0xb0, 0xf3, 0x87, 0x69, 0xdc, 0xd7, 0x0d, 0xcf, 0x00, 0x30, 0x03, 0xe1, 0x13, 0x82, 0x4f, + 0x30, 0x44, 0x33, 0xdd, 0xdc, 0x76, 0x1f, 0x55, 0xa5, 0xaa, 0x78, 0x0a, 0x70, 0x7c, 0x82, 0xe1, 0x9d, 0xa9, 0xc7, + 0x66, 0x09, 0x36, 0xcf, 0x23, 0x30, 0x84, 0xdc, 0x34, 0xa7, 0x9a, 0x72, 0x03, 0xe4, 0xbb, 0x88, 0x61, 0x8a, 0x67, + 0xb1, 0x47, 0xc3, 0x07, 0xd4, 0x2b, 0x6f, 0xee, 0xbc, 0xc0, 0x6f, 0xb3, 0x88, 0x65, 0xcf, 0x93, 0x51, 0x06, 0x9f, + 0x88, 0x7c, 0x8b, 0x14, 0x32, 0xf7, 0x83, 0xa6, 0xb0, 0xda, 0xa6, 0xf5, 0x33, 0x20, 0x72, 0x73, 0x75, 0x63, 0xa2, + 0x35, 0x70, 0xa1, 0x37, 0x51, 0x5d, 0x40, 0x6b, 0x9b, 0xf5, 0xe1, 0x66, 0x57, 0x22, 0x19, 0x3c, 0x10, 0xe6, 0xdf, + 0x78, 0xf1, 0x60, 0xf2, 0x2d, 0xe4, 0xc9, 0xf0, 0x91, 0x87, 0xd3, 0xbd, 0xb5, 0xe7, 0xad, 0xfb, 0x96, 0xbb, 0x6a, + 0x4d, 0x9e, 0xd3, 0x22, 0x94, 0xd8, 0x49, 0x06, 0x70, 0x04, 0x1f, 0x9b, 0xb1, 0xee, 0x03, 0xd4, 0x89, 0x0c, 0x2e, + 0x54, 0x31, 0xe3, 0xcc, 0x38, 0xca, 0xf2, 0x2b, 0xae, 0x39, 0xb8, 0xfd, 0xbc, 0x72, 0x31, 0x10, 0xb0, 0xd0, 0x81, + 0x32, 0xf5, 0x47, 0x32, 0xb5, 0x35, 0x4d, 0x8e, 0xf9, 0x19, 0x2c, 0x10, 0x19, 0x05, 0x01, 0xc8, 0xc2, 0xd3, 0xb6, + 0x4a, 0xf7, 0xf1, 0xa0, 0x1b, 0x50, 0xde, 0x08, 0xcc, 0xc8, 0xa0, 0x43, 0x30, 0x63, 0x6d, 0x67, 0x22, 0x11, 0x61, + 0x12, 0xae, 0x2c, 0x6a, 0xf8, 0x17, 0x4f, 0x49, 0xf9, 0x98, 0x87, 0xbe, 0x20, 0x8c, 0x8b, 0x79, 0x45, 0xe1, 0x90, + 0x82, 0x74, 0x2e, 0xae, 0xbe, 0x65, 0x99, 0x9c, 0x53, 0x2f, 0x43, 0xa1, 0x8b, 0x84, 0x51, 0x66, 0x93, 0x7a, 0x22, + 0x03, 0x48, 0xc6, 0x2a, 0x33, 0x94, 0x2b, 0xbc, 0x1e, 0x55, 0x72, 0x51, 0xf3, 0x6f, 0xcc, 0xca, 0xb8, 0x1c, 0x5b, + 0xd6, 0x0d, 0xeb, 0x0c, 0x8e, 0x57, 0xaa, 0x65, 0xf2, 0x4d, 0x51, 0x9c, 0x78, 0xf1, 0x19, 0x03, 0xf1, 0x7e, 0x56, + 0x6f, 0xb3, 0x9b, 0x43, 0x5c, 0xee, 0xda, 0xc2, 0x95, 0x49, 0xc5, 0x20, 0x96, 0x30, 0x11, 0xb4, 0x28, 0x8d, 0x3f, + 0x72, 0x30, 0xc5, 0x29, 0x40, 0x1b, 0x0b, 0x3f, 0x19, 0x49, 0x55, 0xe5, 0xb0, 0x5c, 0x46, 0x6f, 0xa5, 0xa8, 0xb1, + 0x59, 0x5e, 0x46, 0x9b, 0x79, 0x12, 0x10, 0xe0, 0xea, 0x4a, 0x59, 0xcd, 0xae, 0x4f, 0x1d, 0xb6, 0x67, 0x5c, 0x59, + 0xca, 0x09, 0x53, 0x34, 0x6b, 0x2c, 0x25, 0xc2, 0xb8, 0xcd, 0xc5, 0xb6, 0x38, 0x7e, 0x57, 0xf3, 0x97, 0xd2, 0x6f, + 0xe0, 0x2e, 0x77, 0x4d, 0x01, 0x6e, 0x91, 0x47, 0xf4, 0x8e, 0x5c, 0x06, 0x7c, 0x67, 0x54, 0x6f, 0xd0, 0x80, 0x2d, + 0x5a, 0x6e, 0xcd, 0xc7, 0xb2, 0x3c, 0xf4, 0x55, 0x74, 0xe1, 0x62, 0x11, 0xd1, 0xea, 0x50, 0xeb, 0xfd, 0xde, 0xfe, + 0xd3, 0x5e, 0xb5, 0xd3, 0x80, 0x0e, 0x28, 0x7d, 0xad, 0xd3, 0xdb, 0x2e, 0xff, 0xab, 0x1f, 0x6e, 0x8b, 0x44, 0x9f, + 0x97, 0xd4, 0x0d, 0x74, 0x08, 0x72, 0x07, 0x82, 0xad, 0x74, 0x3d, 0x67, 0x8e, 0x83, 0x5e, 0x58, 0x12, 0x6a, 0xe1, + 0x75, 0x79, 0x1b, 0x04, 0x0f, 0xa6, 0x94, 0xc4, 0x1a, 0x8f, 0xaa, 0x39, 0x0c, 0xe8, 0xc3, 0x2d, 0xd6, 0x6a, 0x62, + 0xfa, 0x13, 0xa2, 0xca, 0x44, 0x7a, 0x60, 0x7b, 0xd1, 0xc4, 0x84, 0x87, 0xfd, 0xa0, 0x24, 0x25, 0x54, 0x07, 0x82, + 0x36, 0x50, 0x26, 0xd6, 0xf1, 0x65, 0x87, 0x82, 0xe7, 0x42, 0x0b, 0x6c, 0x62, 0xb0, 0xef, 0xb8, 0x18, 0x12, 0x15, + 0x3b, 0xa4, 0xd4, 0x63, 0xa4, 0x76, 0x87, 0x2d, 0x62, 0x7f, 0x52, 0x0d, 0x94, 0xfe, 0x6e, 0xdc, 0xf7, 0xad, 0x15, + 0x40, 0xa9, 0x6b, 0x7e, 0xdc, 0xf7, 0x28, 0xf6, 0x60, 0x11, 0xbf, 0x0e, 0xc1, 0x99, 0x6c, 0xd7, 0x54, 0xc4, 0x9a, + 0xcf, 0x92, 0x3d, 0x37, 0x6c, 0xf8, 0xfb, 0x8a, 0x40, 0xc6, 0x48, 0xd3, 0xa1, 0x8c, 0xcd, 0xf8, 0x59, 0x46, 0x31, + 0x45, 0xd8, 0x17, 0x7e, 0x27, 0x09, 0x11, 0x22, 0x64, 0x0c, 0xd3, 0x1c, 0x41, 0x3b, 0xf3, 0x79, 0x52, 0x0b, 0x54, + 0xd7, 0x24, 0xf4, 0x3d, 0xdd, 0x1d, 0x88, 0x07, 0x39, 0x7a, 0x54, 0x02, 0xa0, 0xff, 0x5b, 0x3c, 0x7b, 0x72, 0xce, + 0x18, 0xc1, 0x5a, 0x71, 0x22, 0x8d, 0x2b, 0x70, 0x9c, 0xe3, 0x93, 0x16, 0x12, 0xc4, 0x4b, 0x75, 0x27, 0xa1, 0x4f, + 0xda, 0x38, 0x35, 0x78, 0x82, 0x5c, 0x14, 0x2b, 0x15, 0x80, 0xda, 0x2d, 0x78, 0xb3, 0x84, 0x19, 0x33, 0xa4, 0x47, + 0xde, 0x83, 0x35, 0x0f, 0x75, 0x29, 0x97, 0xc7, 0x9c, 0x9c, 0x21, 0x6a, 0x2e, 0xf2, 0xa4, 0xc6, 0x5c, 0x41, 0x5f, + 0x83, 0xe2, 0x14, 0xda, 0x18, 0x13, 0xab, 0xcd, 0x53, 0x9f, 0xaa, 0xa1, 0x28, 0x3d, 0x9b, 0xe5, 0xc5, 0x3a, 0xe2, + 0x12, 0xd8, 0x85, 0x66, 0xf4, 0xc1, 0xaf, 0x64, 0x92, 0xc3, 0x41, 0x9a, 0x27, 0x82, 0x8e, 0xf2, 0xc1, 0xd0, 0xc9, + 0x8c, 0xf6, 0x2e, 0x3d, 0x62, 0x47, 0x0f, 0x25, 0xa7, 0x2f, 0x50, 0x7a, 0x08, 0x01, 0xfa, 0xab, 0xe1, 0x4d, 0xdb, + 0x5f, 0xd1, 0x49, 0xf1, 0x62, 0xc2, 0x3b, 0x49, 0x14, 0xe1, 0x21, 0x9c, 0x11, 0x85, 0x8c, 0x44, 0xfb, 0x60, 0x30, + 0xf3, 0xce, 0xb6, 0x35, 0xe5, 0x7d, 0x51, 0xa7, 0x4e, 0x73, 0xf0, 0xf4, 0xbd, 0x78, 0x2d, 0x37, 0x0f, 0x02, 0x7a, + 0xec, 0xcb, 0x96, 0x90, 0x9d, 0x27, 0x03, 0x08, 0x90, 0x2f, 0x76, 0xc8, 0x98, 0x20, 0x0d, 0x6b, 0x5a, 0x92, 0x35, + 0xfd, 0x68, 0x11, 0xfa, 0xa7, 0xea, 0xe3, 0x34, 0xcb, 0x84, 0x50, 0x5b, 0x18, 0x03, 0x22, 0xf4, 0x94, 0x93, 0x82, + 0x15, 0xb9, 0x0f, 0x5e, 0x52, 0x38, 0x1c, 0xac, 0xd7, 0xc5, 0xf0, 0xa4, 0x39, 0x1b, 0x02, 0xdb, 0x31, 0x01, 0x9d, + 0x66, 0x48, 0x14, 0x62, 0xc3, 0x7d, 0x8c, 0x66, 0x92, 0x0a, 0xc6, 0x34, 0x51, 0xf9, 0xd0, 0x3f, 0xa8, 0x8d, 0xb8, + 0x49, 0x3d, 0x8a, 0x87, 0x11, 0xf6, 0x1c, 0x87, 0xae, 0x13, 0xcb, 0x80, 0xa8, 0xb2, 0xa4, 0xb2, 0xe6, 0x7a, 0xd4, + 0x34, 0x23, 0x83, 0x2a, 0x91, 0xfa, 0x45, 0x5b, 0x07, 0x97, 0x06, 0xd4, 0xb3, 0xf8, 0x66, 0xe0, 0xb9, 0x25, 0xb4, + 0xdc, 0x9f, 0x23, 0x89, 0x27, 0x83, 0x51, 0x8f, 0xe6, 0x08, 0x2f, 0xdd, 0x1d, 0x02, 0xe0, 0xad, 0xf2, 0x76, 0xd5, + 0xf3, 0xef, 0x28, 0x63, 0x27, 0x6e, 0xaa, 0xad, 0x52, 0x92, 0x5a, 0x83, 0x12, 0xf3, 0xef, 0xf2, 0xc7, 0x38, 0x77, + 0x15, 0x0b, 0xee, 0xbd, 0xa7, 0x6b, 0x85, 0xfa, 0xd3, 0x27, 0xb2, 0x93, 0xc2, 0x8d, 0xd3, 0x1b, 0x44, 0xe6, 0xe1, + 0x23, 0x6a, 0xc1, 0x5c, 0xe0, 0xee, 0xb8, 0xa8, 0x7b, 0xf3, 0x37, 0x84, 0x9b, 0xa2, 0xa6, 0xd0, 0x85, 0x92, 0x8d, + 0x16, 0x5f, 0xc9, 0xcc, 0x00, 0xcd, 0xe5, 0x4a, 0x2d, 0x3c, 0x67, 0x3d, 0x50, 0xfb, 0x15, 0x89, 0x5b, 0xeb, 0xf5, + 0xb5, 0x5b, 0xdb, 0x43, 0xb8, 0x9a, 0x2c, 0xa8, 0x63, 0x24, 0x79, 0xcc, 0x1c, 0x5a, 0x2b, 0x32, 0x5d, 0x93, 0x84, + 0xe6, 0x92, 0x5a, 0xaf, 0x2e, 0x1a, 0x7e, 0xfe, 0xda, 0x44, 0x10, 0x13, 0x46, 0x56, 0x2b, 0xe8, 0x1d, 0xb6, 0x9b, + 0x5f, 0x2c, 0x5c, 0x6d, 0x52, 0xa6, 0xc2, 0x21, 0x50, 0x9b, 0x2c, 0x3f, 0xc7, 0xd2, 0x53, 0x14, 0x44, 0xea, 0xb4, + 0xd5, 0x55, 0x42, 0x42, 0xb0, 0x52, 0xa9, 0x7f, 0x1d, 0x98, 0x90, 0x23, 0x2a, 0x47, 0x64, 0xf7, 0xba, 0x9c, 0xf3, + 0x53, 0x03, 0xd2, 0xdd, 0x88, 0x48, 0xc8, 0xe9, 0x8d, 0x01, 0x5d, 0x16, 0x1a, 0xfb, 0xdb, 0x80, 0x2b, 0x7c, 0x88, + 0xd0, 0xe9, 0xd8, 0x95, 0x72, 0x5d, 0x84, 0xfb, 0xbe, 0x40, 0x8a, 0xaa, 0x22, 0x82, 0x05, 0xd5, 0x8e, 0x6c, 0xce, + 0x8e, 0xfc, 0xc6, 0x1a, 0x1c, 0xce, 0xcd, 0xf1, 0xae, 0x51, 0x84, 0xd2, 0xc5, 0xce, 0xe3, 0x40, 0x4f, 0x94, 0x24, + 0x7c, 0x77, 0x8c, 0xd0, 0x5a, 0xeb, 0xfc, 0xac, 0xfb, 0x01, 0xcf, 0x92, 0x70, 0xfe, 0x81, 0x4d, 0xde, 0x97, 0xe4, + 0xbc, 0xbc, 0xda, 0xd4, 0x6d, 0xc1, 0x08, 0x40, 0x7d, 0xe3, 0x79, 0x5b, 0x79, 0x70, 0x83, 0x91, 0x41, 0x9e, 0xcc, + 0x09, 0xc6, 0x33, 0x57, 0x83, 0x79, 0x76, 0xec, 0x2c, 0xef, 0xb1, 0x10, 0xc8, 0x53, 0x4d, 0x6d, 0x5a, 0x2b, 0xb1, + 0x45, 0x3b, 0x66, 0xbf, 0x65, 0x03, 0x9c, 0x00, 0xa7, 0xc3, 0xf1, 0xd2, 0x36, 0xf8, 0x40, 0x2e, 0xe9, 0xad, 0x65, + 0x14, 0x64, 0x17, 0xfe, 0x6d, 0xa8, 0x8f, 0x28, 0xaf, 0x40, 0xa8, 0x48, 0xea, 0xd8, 0x28, 0x29, 0x45, 0xa9, 0x11, + 0x5a, 0x66, 0x5b, 0x90, 0x15, 0x67, 0x7b, 0xc4, 0xa3, 0x66, 0x86, 0x87, 0x22, 0xb7, 0x45, 0x3a, 0x6b, 0xb8, 0x2f, + 0x05, 0x2a, 0x36, 0x85, 0x34, 0xd3, 0x1a, 0xd8, 0xc6, 0x3d, 0x59, 0x53, 0x7b, 0xb7, 0x11, 0x35, 0x83, 0x47, 0xf4, + 0x2d, 0x4d, 0x4d, 0xdf, 0xaf, 0x8d, 0xb4, 0x52, 0x0c, 0x94, 0x39, 0xc4, 0x74, 0x4d, 0x8d, 0x99, 0x54, 0xa9, 0xc5, + 0x7e, 0xdd, 0xe6, 0xd3, 0x6f, 0x17, 0xca, 0x21, 0x39, 0x70, 0x42, 0xc9, 0x11, 0x43, 0x76, 0x86, 0x21, 0xb8, 0x95, + 0xb3, 0x89, 0x64, 0xb9, 0x11, 0xb9, 0xcc, 0x3a, 0xa3, 0x3b, 0xfe, 0xc1, 0x04, 0x50, 0xe8, 0x8b, 0x05, 0x0a, 0xfa, + 0xb1, 0xda, 0xfa, 0x44, 0x1d, 0x49, 0x25, 0x29, 0x3e, 0x5d, 0xb8, 0x8a, 0xca, 0xa1, 0xe6, 0xea, 0x55, 0x51, 0x81, + 0x5a, 0x13, 0x3a, 0x70, 0x3d, 0x42, 0x60, 0x03, 0x61, 0xf4, 0x47, 0x53, 0x08, 0xcb, 0x7d, 0x15, 0x37, 0xed, 0x26, + 0xef, 0x9e, 0xce, 0xf6, 0x18, 0xa9, 0x41, 0x16, 0x5a, 0x56, 0x1c, 0xc3, 0xe9, 0x01, 0x4f, 0x06, 0x8f, 0x1d, 0x33, + 0x6c, 0x36, 0x4e, 0x8f, 0x31, 0x06, 0x58, 0xb2, 0xc2, 0x62, 0x9b, 0x4a, 0x6b, 0x45, 0x84, 0xd4, 0x36, 0xab, 0x97, + 0x36, 0x77, 0x8a, 0xfc, 0xf6, 0x67, 0x00, 0x98, 0x57, 0x4d, 0xa6, 0x75, 0x14, 0x53, 0xc4, 0x28, 0x69, 0xb3, 0x38, + 0x5e, 0x88, 0x95, 0x17, 0x1f, 0x0b, 0xdc, 0x1f, 0xa1, 0x72, 0x65, 0xb9, 0xe0, 0xea, 0x4c, 0xee, 0x87, 0x9b, 0xef, + 0x33, 0x27, 0x11, 0x2f, 0x98, 0xe8, 0x33, 0x66, 0xc3, 0xd5, 0x85, 0x77, 0xa4, 0x4e, 0xb3, 0x98, 0xdc, 0xfb, 0xe2, + 0x2d, 0x9f, 0xe7, 0x2e, 0xa0, 0xb2, 0x07, 0xb1, 0xdb, 0xaa, 0x8c, 0xf5, 0x3a, 0x23, 0x83, 0x84, 0x6f, 0x29, 0xd9, + 0x2b, 0x19, 0x3b, 0xf1, 0x19, 0x64, 0x7a, 0xb0, 0x0c, 0x0b, 0x4f, 0x19, 0xc9, 0xed, 0x33, 0x55, 0xd4, 0xae, 0xa7, + 0x54, 0xae, 0x8b, 0xee, 0xbc, 0xe6, 0xde, 0x56, 0xb8, 0x53, 0x33, 0x93, 0x4e, 0xbc, 0x2e, 0x40, 0x9d, 0x0f, 0x2e, + 0x2d, 0xd2, 0x39, 0x2f, 0x60, 0xd1, 0x0c, 0x85, 0xeb, 0xa9, 0x1a, 0x7d, 0xb6, 0xdc, 0x47, 0x16, 0xc3, 0xa6, 0x3b, + 0xbf, 0x2c, 0x7b, 0x34, 0xf9, 0x64, 0x81, 0x40, 0xec, 0x29, 0x3c, 0xbe, 0xa4, 0xc1, 0xad, 0xc5, 0xcf, 0xb4, 0xd5, + 0x56, 0x06, 0xaa, 0x4d, 0x52, 0x0b, 0xfc, 0x64, 0x39, 0xe2, 0xe4, 0x70, 0x6a, 0x79, 0xd7, 0xc0, 0x97, 0xf8, 0x05, + 0xf4, 0x87, 0xb0, 0x2a, 0x52, 0x97, 0x88, 0x6f, 0x09, 0x65, 0xe5, 0x98, 0xfb, 0x0d, 0xc8, 0x7a, 0x98, 0x2d, 0x14, + 0xc7, 0x9b, 0x70, 0x44, 0xa2, 0xb4, 0xfd, 0xdc, 0x1f, 0x1f, 0xf4, 0x2b, 0x7a, 0x0c, 0x86, 0xe3, 0x40, 0x85, 0xc8, + 0x99, 0x12, 0x22, 0x0a, 0xa7, 0x25, 0x5c, 0x86, 0xc6, 0x3c, 0x14, 0x04, 0x64, 0xd4, 0xff, 0x81, 0x70, 0x70, 0x31, + 0x6f, 0x9d, 0xa0, 0x52, 0x55, 0x5a, 0x58, 0x2e, 0x7b, 0xb1, 0x1f, 0x40, 0x95, 0x87, 0x3c, 0x60, 0x7d, 0xde, 0x71, + 0x9d, 0x33, 0x0b, 0x1e, 0x08, 0x46, 0x40, 0x12, 0x33, 0x5b, 0x47, 0xb7, 0x7a, 0xfa, 0x8b, 0xbb, 0x4e, 0x40, 0x3f, + 0x6e, 0x18, 0x7f, 0x84, 0x53, 0x51, 0x5a, 0xc8, 0x5f, 0xb5, 0x24, 0x9b, 0x30, 0xba, 0x0d, 0x8d, 0x75, 0x88, 0xc4, + 0xc5, 0x25, 0x47, 0xcf, 0x79, 0x51, 0xa0, 0x1c, 0xba, 0xee, 0x00, 0x8f, 0x85, 0x77, 0x57, 0x14, 0x68, 0x2e, 0xdc, + 0x35, 0x7d, 0x21, 0x27, 0xd6, 0x3a, 0x3c, 0x62, 0xad, 0x6d, 0x1b, 0xa2, 0x07, 0xcb, 0x29, 0x9e, 0xa1, 0xa1, 0x5c, + 0x2b, 0xd5, 0x92, 0x6c, 0x52, 0xcf, 0x80, 0x8c, 0x95, 0x7a, 0x82, 0x26, 0x65, 0xde, 0x21, 0x9e, 0x3a, 0x18, 0x3b, + 0x74, 0x93, 0x41, 0xf4, 0x5f, 0x47, 0xe6, 0x44, 0xe5, 0x9e, 0xf4, 0x63, 0xdb, 0xa8, 0xe0, 0x00, 0xe8, 0x68, 0x79, + 0xbf, 0xec, 0xbe, 0x77, 0xab, 0xb3, 0x14, 0x6d, 0x78, 0x55, 0x91, 0x84, 0x5a, 0x47, 0xfb, 0xbc, 0x86, 0xe7, 0xdb, + 0x11, 0x61, 0x44, 0xb7, 0x07, 0x66, 0x85, 0xb3, 0x6d, 0x52, 0x8c, 0x5d, 0xb5, 0xe0, 0x84, 0x79, 0x08, 0x88, 0x77, + 0x3d, 0xa9, 0x0e, 0x2b, 0x0d, 0xd1, 0x79, 0x1e, 0x5e, 0x2e, 0xae, 0x58, 0x98, 0xaa, 0x5e, 0x0a, 0x62, 0xbf, 0xf9, + 0xe0, 0x03, 0xf7, 0x79, 0x86, 0x61, 0xe3, 0xcd, 0x28, 0x4f, 0x8f, 0x31, 0x3b, 0x3f, 0xc4, 0x6e, 0xea, 0x48, 0x5f, + 0x71, 0x01, 0x7a, 0xbd, 0x27, 0xa7, 0xef, 0xd0, 0xf7, 0xa2, 0xe3, 0x8c, 0x2f, 0x0c, 0xd7, 0x8e, 0xf3, 0x05, 0x71, + 0x4a, 0x84, 0x28, 0xf5, 0x78, 0x51, 0x8f, 0x59, 0x22, 0x5e, 0x05, 0xc1, 0xb6, 0xe5, 0xcd, 0xf8, 0xef, 0xc2, 0x49, + 0xca, 0x77, 0xc7, 0x90, 0xc0, 0xe3, 0xc1, 0x9f, 0xa3, 0xe3, 0x02, 0x67, 0x22, 0x72, 0x18, 0x87, 0x3b, 0xb7, 0x55, + 0x4a, 0xef, 0xdb, 0xb0, 0x66, 0xea, 0xf5, 0xe7, 0x05, 0x21, 0xe3, 0x86, 0x1c, 0xb8, 0x83, 0x22, 0x9e, 0x96, 0xc0, + 0x5c, 0x9b, 0x42, 0x88, 0x7a, 0xfc, 0x37, 0xdc, 0x3c, 0x45, 0xf8, 0xa8, 0x11, 0x85, 0x89, 0xa6, 0xa6, 0xe4, 0xae, + 0xd8, 0x00, 0xac, 0xc4, 0x09, 0xed, 0x20, 0xf5, 0x43, 0x59, 0x79, 0x85, 0x81, 0xd5, 0xa2, 0xae, 0x04, 0x6a, 0x59, + 0x20, 0x8f, 0x0c, 0x4e, 0xec, 0xbd, 0x08, 0x8b, 0xae, 0x61, 0x14, 0xf4, 0x60, 0xaa, 0xb6, 0x5e, 0xc2, 0xeb, 0x6e, + 0x0b, 0xcb, 0x0f, 0xef, 0x57, 0x53, 0xcb, 0x5d, 0x95, 0x3f, 0x1e, 0x20, 0x67, 0xc9, 0xe9, 0x03, 0x80, 0x15, 0x0f, + 0x53, 0xc0, 0x56, 0xef, 0xcd, 0x61, 0x6b, 0x77, 0x89, 0xc6, 0x6d, 0xe6, 0x4f, 0x77, 0x48, 0x30, 0x4a, 0xfa, 0xd9, + 0xe7, 0x3f, 0xcf, 0x60, 0x71, 0xf4, 0x06, 0xc0, 0x43, 0xac, 0x3b, 0x59, 0xd5, 0xad, 0xec, 0x1e, 0xff, 0xf4, 0xa1, + 0x29, 0x12, 0xe9, 0x89, 0x69, 0xfc, 0xe2, 0xa8, 0x26, 0x7b, 0xab, 0x1d, 0x23, 0x67, 0x77, 0x24, 0xce, 0x4a, 0x09, + 0xc9, 0xe5, 0x88, 0x4a, 0x74, 0xdb, 0x23, 0x8a, 0xe0, 0xb5, 0x77, 0x96, 0x61, 0xa3, 0x5f, 0xc3, 0x08, 0x05, 0xa0, + 0x26, 0x60, 0xf8, 0xa0, 0xb2, 0xde, 0x09, 0x00, 0x8c, 0xd2, 0xaa, 0xa9, 0x53, 0x46, 0x17, 0xbb, 0xe9, 0xf2, 0xe2, + 0x41, 0xa6, 0xb4, 0x13, 0x35, 0x93, 0xdb, 0x13, 0x2a, 0x5b, 0x2d, 0x8c, 0x6d, 0xbf, 0x64, 0xc4, 0xa7, 0x81, 0x44, + 0x2b, 0x2c, 0x30, 0xa3, 0x83, 0x65, 0x29, 0xcb, 0x51, 0x22, 0xb1, 0x4c, 0x90, 0x5d, 0x79, 0x33, 0x8c, 0xbc, 0x0d, + 0xac, 0xc8, 0x8c, 0x48, 0x24, 0x5b, 0xd4, 0x74, 0x44, 0x0c, 0x8f, 0xda, 0x31, 0xab, 0xba, 0xb4, 0xb1, 0x62, 0xe1, + 0xe9, 0xe6, 0xd0, 0x93, 0x2b, 0xa4, 0xe8, 0x72, 0x1f, 0xa4, 0x50, 0x4c, 0x17, 0x6d, 0x5c, 0x9d, 0xdb, 0xec, 0x8b, + 0x28, 0xf3, 0x15, 0x99, 0x17, 0xb1, 0x98, 0xdd, 0x3f, 0xd9, 0xd8, 0x61, 0xb2, 0x3c, 0xce, 0xc9, 0x64, 0xe6, 0x40, + 0x35, 0x6d, 0xc8, 0xb5, 0xe4, 0xb5, 0x64, 0xc5, 0x49, 0x5c, 0xfc, 0xbb, 0xbc, 0x6c, 0xf3, 0x64, 0xaa, 0x10, 0x41, + 0x0f, 0xb3, 0x64, 0x81, 0x59, 0xaa, 0xa5, 0x83, 0x12, 0xce, 0x22, 0xb2, 0xa3, 0x81, 0xe9, 0x4d, 0x49, 0x9b, 0x7c, + 0xd0, 0x49, 0x77, 0x27, 0x6f, 0x0d, 0x09, 0xd7, 0x6b, 0x9c, 0xd8, 0x16, 0x73, 0x31, 0xe2, 0xa9, 0xef, 0xca, 0x24, + 0x5a, 0x91, 0x78, 0x90, 0x25, 0x31, 0x57, 0x9e, 0x8d, 0x45, 0x89, 0x2f, 0x72, 0x7a, 0x5a, 0x2f, 0x66, 0xa3, 0x45, + 0x1a, 0xfb, 0xc3, 0xc8, 0x2f, 0x8b, 0x9f, 0xdd, 0x8e, 0x1c, 0xf5, 0xf6, 0x84, 0xf2, 0xac, 0xa6, 0xb6, 0xae, 0x99, + 0x39, 0x66, 0x94, 0x69, 0xa4, 0x10, 0x4b, 0x48, 0x9f, 0x8c, 0x08, 0x5a, 0x9c, 0x0e, 0x6c, 0xd8, 0xfc, 0x4e, 0x05, + 0x9e, 0xa9, 0xdd, 0x5e, 0x0d, 0x0d, 0xcf, 0x2b, 0x24, 0x82, 0x0b, 0x1a, 0x6f, 0x70, 0xd4, 0x0c, 0xf5, 0x7f, 0x78, + 0x3a, 0x6f, 0xcd, 0x74, 0xf6, 0x44, 0x32, 0xb2, 0xb4, 0xf0, 0x0c, 0x70, 0x3e, 0xa9, 0x4a, 0x73, 0x7b, 0x3f, 0xc8, + 0x23, 0xeb, 0xfe, 0x49, 0x54, 0xbf, 0x22, 0xb0, 0x3b, 0x49, 0x4c, 0x08, 0xd0, 0xf0, 0xba, 0x9e, 0x0d, 0x13, 0x09, + 0xad, 0x04, 0xef, 0xbb, 0x0a, 0xfe, 0x4e, 0xca, 0x24, 0x5d, 0x9a, 0xd0, 0xe4, 0xa2, 0x5c, 0x0d, 0x76, 0xb2, 0x40, + 0xbe, 0x05, 0xd8, 0x40, 0x10, 0x08, 0xac, 0x30, 0xef, 0x98, 0x4a, 0x68, 0x07, 0xd2, 0x40, 0xe6, 0x04, 0x98, 0x64, + 0xe3, 0x5c, 0x19, 0x14, 0xd5, 0x46, 0x3e, 0xad, 0x72, 0x36, 0x24, 0x1a, 0x06, 0x99, 0xf5, 0xc7, 0xd0, 0xd9, 0x2b, + 0x26, 0xc9, 0xbc, 0xbf, 0x73, 0x34, 0x9e, 0x6c, 0xcf, 0x90, 0x28, 0xe4, 0x6a, 0x9f, 0x41, 0x3c, 0xa1, 0x19, 0x2e, + 0x2b, 0x51, 0x5f, 0xd6, 0xb5, 0xfa, 0x4f, 0xaa, 0xf7, 0x1d, 0x3c, 0x39, 0x90, 0x45, 0x6f, 0xe3, 0xc0, 0x72, 0xcb, + 0x16, 0x01, 0xe6, 0xf9, 0x1a, 0x68, 0x46, 0x09, 0x20, 0x43, 0x13, 0x60, 0xae, 0x31, 0x7b, 0x69, 0x68, 0x46, 0x28, + 0xfb, 0x22, 0xd7, 0x26, 0xa1, 0xe8, 0x61, 0xee, 0xcb, 0x2b, 0x71, 0x9b, 0xeb, 0x1d, 0xd2, 0xeb, 0xb6, 0x7a, 0x8f, + 0x5d, 0x8e, 0xc8, 0x72, 0x8a, 0xb8, 0x4d, 0xa8, 0x1e, 0xa0, 0x90, 0x55, 0x13, 0xa6, 0x75, 0xb0, 0x3b, 0xe3, 0x2f, + 0x49, 0x88, 0x30, 0x21, 0x31, 0xaa, 0x8f, 0xd0, 0xb1, 0x1a, 0xfb, 0x44, 0x8f, 0x25, 0x47, 0xa2, 0x37, 0x48, 0x1d, + 0xb9, 0x14, 0x3a, 0x8f, 0x0b, 0x75, 0x6d, 0xad, 0xb6, 0xe0, 0x12, 0x61, 0xc0, 0x89, 0x55, 0x0e, 0x87, 0xcb, 0xa9, + 0x50, 0xd9, 0x12, 0xf7, 0x36, 0x66, 0xd4, 0xcb, 0x9d, 0xb7, 0x59, 0x97, 0x7a, 0x6f, 0x12, 0x16, 0x91, 0xe5, 0xa1, + 0x62, 0x1c, 0x08, 0x05, 0x6b, 0xfb, 0x60, 0x79, 0x8d, 0x33, 0xf2, 0x2c, 0xc3, 0x66, 0x30, 0x7a, 0x1f, 0xa0, 0xac, + 0xa8, 0xc7, 0xe1, 0x02, 0x10, 0xeb, 0x43, 0xf2, 0xa2, 0xc9, 0x0c, 0x01, 0x16, 0xd9, 0xe2, 0x52, 0x93, 0x2c, 0x14, + 0x3a, 0xea, 0xaf, 0x7b, 0x40, 0xbb, 0x16, 0x12, 0x03, 0xe9, 0xf0, 0xa8, 0x93, 0xae, 0x66, 0x89, 0x65, 0x73, 0x0c, + 0x0d, 0x85, 0xc5, 0x69, 0x9e, 0xa7, 0x23, 0xdb, 0xda, 0x3b, 0xc0, 0x89, 0x8e, 0xae, 0x17, 0xe0, 0xb6, 0x83, 0x4b, + 0x21, 0xc7, 0x11, 0xdc, 0x34, 0x47, 0x79, 0x76, 0x2a, 0x6d, 0x0a, 0x46, 0x13, 0x37, 0x2b, 0xcd, 0x85, 0x2e, 0xa7, + 0xf0, 0x3c, 0xdd, 0xfa, 0x13, 0x15, 0xfd, 0xd3, 0x52, 0x3b, 0x83, 0x41, 0x95, 0xd3, 0xae, 0x94, 0xb1, 0xa4, 0x5d, + 0x73, 0xf4, 0x85, 0x40, 0x1e, 0x16, 0xfa, 0x7e, 0xa1, 0x71, 0xe7, 0xd4, 0x41, 0xf1, 0x8e, 0x71, 0x66, 0xa7, 0x07, + 0x0d, 0x7b, 0xa5, 0xf1, 0x68, 0x44, 0x29, 0x2b, 0xf5, 0x03, 0xe3, 0x5a, 0xde, 0x9e, 0x10, 0x6d, 0x32, 0x0a, 0x77, + 0x28, 0xcb, 0xe4, 0xdb, 0x1e, 0x07, 0x9a, 0xb6, 0x67, 0xdc, 0x76, 0x5b, 0xdf, 0xae, 0x93, 0x5b, 0x44, 0xe2, 0xf6, + 0x17, 0x5c, 0xc2, 0x33, 0xf8, 0xc6, 0x90, 0x8a, 0x3d, 0xeb, 0xc4, 0xe5, 0xcb, 0x28, 0xcb, 0xf9, 0x0a, 0x47, 0x57, + 0x4c, 0xc6, 0xc2, 0x0b, 0x2d, 0x22, 0xdc, 0x34, 0x50, 0xc7, 0x95, 0x24, 0xb1, 0x9b, 0x92, 0xf8, 0xb9, 0xe5, 0x9f, + 0xb7, 0xe6, 0x46, 0xc0, 0x54, 0x24, 0xd7, 0x21, 0xfa, 0xcc, 0xa9, 0x5a, 0xdd, 0x6b, 0x95, 0x05, 0xf5, 0x98, 0xa7, + 0x72, 0xc4, 0x9c, 0xba, 0xdd, 0x14, 0x59, 0x26, 0x3d, 0x6c, 0xae, 0x29, 0x4a, 0x14, 0x68, 0xab, 0x0b, 0xbd, 0xcc, + 0x9c, 0xb3, 0xd0, 0xd1, 0x89, 0x94, 0x6d, 0x8d, 0x66, 0x13, 0x73, 0x1c, 0xce, 0x7e, 0x12, 0xd9, 0x13, 0x5c, 0xf5, + 0x9e, 0xb7, 0xf6, 0x61, 0xb3, 0xf1, 0x75, 0xa8, 0xd5, 0x90, 0x1d, 0x10, 0x68, 0xe6, 0xce, 0x14, 0x28, 0xc2, 0xfe, + 0x2b, 0x3b, 0x12, 0xa5, 0x2c, 0xff, 0xd8, 0x69, 0x5d, 0xdf, 0x36, 0xaa, 0x8e, 0xc9, 0x5f, 0xd3, 0xbe, 0x86, 0xab, + 0x0e, 0x8a, 0x9c, 0xc3, 0xf1, 0x49, 0xbb, 0x33, 0xdd, 0x3c, 0x10, 0x9e, 0xb3, 0xc3, 0xa8, 0x2c, 0x67, 0x57, 0xd4, + 0x1b, 0xba, 0x0a, 0x18, 0xa8, 0x51, 0x32, 0x29, 0x7b, 0xa3, 0xb0, 0x8e, 0xfa, 0x9d, 0xb8, 0xd6, 0x57, 0x14, 0xdd, + 0xb2, 0xc6, 0xad, 0x4d, 0x76, 0xe0, 0x8f, 0x18, 0x2b, 0x77, 0x98, 0x21, 0x3f, 0x5c, 0x63, 0xd5, 0x22, 0xf5, 0x46, + 0xe3, 0x62, 0xdb, 0x6a, 0x3a, 0xd3, 0x40, 0xb7, 0xad, 0x99, 0x1b, 0x61, 0x07, 0xd5, 0x70, 0x5b, 0xb7, 0x95, 0xaa, + 0xb6, 0x9d, 0xc7, 0xaf, 0xf6, 0xd5, 0x89, 0x98, 0xd0, 0x86, 0xa1, 0xaf, 0x81, 0xe9, 0x5a, 0x54, 0x73, 0x31, 0xb0, + 0xa9, 0x5e, 0x2d, 0xf6, 0x5d, 0xc8, 0xee, 0xdd, 0x5f, 0x43, 0x12, 0xaa, 0xe2, 0xca, 0x2d, 0x2f, 0xb7, 0x9f, 0x74, + 0xb2, 0x4a, 0x65, 0x6a, 0x1f, 0xf9, 0x1d, 0x66, 0xca, 0x87, 0x99, 0xe2, 0x71, 0xa5, 0x63, 0x2d, 0x20, 0x0a, 0x43, + 0xe1, 0x55, 0x0a, 0x74, 0x6b, 0x16, 0xf1, 0x0f, 0x74, 0xec, 0xca, 0x98, 0x11, 0x32, 0x1a, 0x95, 0x33, 0x74, 0x43, + 0x42, 0x35, 0x34, 0xb1, 0x9c, 0xa4, 0x4b, 0x0d, 0xba, 0xda, 0xe1, 0x3a, 0xb2, 0x3c, 0x10, 0x02, 0x71, 0x22, 0x87, + 0x39, 0x53, 0x23, 0xda, 0xfd, 0x24, 0x30, 0x91, 0x66, 0x5d, 0xb5, 0x5f, 0x74, 0x38, 0xdd, 0x50, 0x7b, 0x4f, 0xbf, + 0x7c, 0x68, 0xb4, 0xa7, 0x5f, 0xae, 0xb4, 0x3e, 0x39, 0x31, 0xe5, 0xd4, 0x4a, 0xc7, 0x0d, 0x8c, 0xc3, 0x45, 0xe9, + 0xc0, 0xf7, 0x48, 0x35, 0xb8, 0x31, 0xdc, 0x8d, 0x4e, 0xe0, 0x8c, 0xdc, 0x36, 0x22, 0x2b, 0x37, 0x81, 0x99, 0x81, + 0x94, 0xd2, 0x8b, 0x63, 0xe0, 0xbe, 0xed, 0xfd, 0x28, 0xc9, 0x78, 0xd3, 0x64, 0xfc, 0x7a, 0x99, 0x15, 0x4a, 0xdf, + 0x33, 0xb3, 0xd0, 0x55, 0xfc, 0xce, 0x24, 0x77, 0xb5, 0xc6, 0x4e, 0xaa, 0xe5, 0x0c, 0x18, 0xe5, 0x6a, 0x85, 0xe5, + 0x8e, 0xf7, 0xe4, 0xb0, 0xb9, 0x9f, 0x65, 0x09, 0x69, 0xb2, 0x15, 0x55, 0x89, 0x31, 0x22, 0x85, 0xf6, 0x17, 0x67, + 0xe7, 0xfe, 0x68, 0xf1, 0x01, 0x1d, 0xf5, 0x1d, 0x33, 0xae, 0xc6, 0xad, 0xd8, 0x2e, 0x56, 0xec, 0x60, 0x1a, 0xae, + 0x0d, 0xa6, 0x79, 0x80, 0xd0, 0x3d, 0x73, 0x07, 0xf5, 0x0b, 0xfc, 0x8f, 0x7c, 0x5c, 0x55, 0x48, 0x87, 0x2e, 0x9b, + 0xa9, 0x28, 0x5f, 0xa2, 0x06, 0x05, 0x2c, 0x5a, 0xb7, 0x4b, 0x13, 0x30, 0x45, 0x16, 0xd2, 0x2d, 0xa4, 0x20, 0x4a, + 0x16, 0x82, 0x19, 0x54, 0x7c, 0xe5, 0x2f, 0x13, 0x5f, 0xeb, 0xab, 0x85, 0x5e, 0xd2, 0x13, 0xb6, 0x0a, 0xb9, 0xba, + 0x61, 0xb4, 0x98, 0x55, 0xa7, 0x1d, 0xa7, 0x89, 0x43, 0x83, 0x1a, 0x75, 0x44, 0xe8, 0x3a, 0x3e, 0xf8, 0x6c, 0x13, + 0x79, 0x83, 0xc9, 0x4f, 0x4e, 0x02, 0xfe, 0x5e, 0x9f, 0xbc, 0xc5, 0xd9, 0x43, 0xac, 0x4a, 0x33, 0x1e, 0x2f, 0x94, + 0x3d, 0x2a, 0x7b, 0x41, 0xad, 0xb1, 0x9f, 0x5d, 0x98, 0xd6, 0x46, 0x25, 0x85, 0xdc, 0x79, 0xb8, 0x90, 0xef, 0x9c, + 0xc2, 0xb9, 0x1b, 0x95, 0x88, 0xf2, 0x00, 0x66, 0xc2, 0xe6, 0xc4, 0x8d, 0x8a, 0x5b, 0x40, 0xe5, 0x4c, 0x4f, 0x9a, + 0xc4, 0x74, 0x56, 0x22, 0xc6, 0x8c, 0x4e, 0xe1, 0x7a, 0x1c, 0xa2, 0x31, 0x34, 0xc3, 0x9c, 0xde, 0xc7, 0xe8, 0x09, + 0x72, 0x80, 0xb3, 0x76, 0xad, 0x21, 0xc4, 0x4c, 0x2a, 0x7c, 0xef, 0x56, 0xc4, 0x96, 0xd9, 0x17, 0x82, 0xda, 0x36, + 0xef, 0xbb, 0x11, 0x51, 0x5e, 0x29, 0x7c, 0x9f, 0xfb, 0xcb, 0x2f, 0x18, 0xaf, 0x64, 0x68, 0x0d, 0xcf, 0x92, 0x9f, + 0xc3, 0xfc, 0xec, 0x37, 0x76, 0x60, 0x02, 0x12, 0xa7, 0x15, 0x8d, 0x7a, 0x4a, 0x96, 0xe6, 0x3a, 0xeb, 0x7d, 0x13, + 0xce, 0x28, 0x99, 0x06, 0x4c, 0xac, 0x65, 0x16, 0x40, 0x27, 0x52, 0x09, 0x9c, 0x25, 0x95, 0x75, 0x34, 0x93, 0x47, + 0x0b, 0xbd, 0x37, 0xf1, 0xf4, 0x45, 0x49, 0x7a, 0x05, 0xfe, 0xd8, 0x52, 0x63, 0x51, 0xa6, 0x6d, 0x5e, 0x04, 0xaa, + 0x66, 0x2d, 0x8f, 0x83, 0x5c, 0x7a, 0xbd, 0xac, 0x7a, 0xe5, 0x69, 0x2d, 0xd5, 0x05, 0xda, 0x4e, 0xc8, 0x31, 0x6a, + 0x51, 0x5e, 0x41, 0x1a, 0x8a, 0xf6, 0x40, 0xe9, 0x6b, 0x98, 0xd0, 0x03, 0x7e, 0xa9, 0x06, 0x65, 0x34, 0x78, 0x67, + 0xcd, 0x16, 0x17, 0x93, 0x23, 0x67, 0xcd, 0x00, 0x02, 0x6e, 0xd7, 0xdb, 0x52, 0x13, 0x21, 0x15, 0x6e, 0x30, 0x4c, + 0x8b, 0x44, 0xfd, 0x44, 0x73, 0x58, 0xbb, 0x42, 0x52, 0x87, 0x58, 0x87, 0x16, 0x26, 0xa0, 0x35, 0xe3, 0x62, 0x43, + 0x8b, 0xb2, 0x13, 0x39, 0xb0, 0x36, 0x8b, 0x24, 0xe3, 0xb0, 0x47, 0x33, 0x6d, 0x06, 0x72, 0x2d, 0xc1, 0x65, 0x89, + 0xe8, 0x2d, 0x8a, 0xee, 0x9e, 0xc8, 0xb0, 0xb9, 0xc9, 0x4a, 0xa6, 0xcc, 0xf4, 0x68, 0x08, 0xb4, 0x6b, 0x0f, 0x06, + 0xdb, 0xa1, 0x82, 0xbf, 0x84, 0x77, 0x49, 0xd2, 0xfd, 0x3e, 0x7b, 0xdc, 0x81, 0x0f, 0xe1, 0xd4, 0x69, 0xbf, 0x09, + 0xb0, 0xce, 0x81, 0x53, 0xac, 0x13, 0x63, 0x9c, 0x71, 0x54, 0xef, 0x66, 0xb4, 0xb1, 0x9f, 0x10, 0x43, 0xa0, 0x70, + 0xf8, 0xb6, 0x47, 0x2b, 0xaf, 0xda, 0xb1, 0x36, 0xd3, 0x4b, 0xda, 0x91, 0x8f, 0xc8, 0x11, 0x4c, 0x82, 0x48, 0x5a, + 0x26, 0x10, 0x9a, 0x31, 0x78, 0x0b, 0x57, 0xb0, 0x36, 0x67, 0x40, 0x4b, 0x5d, 0x2f, 0x14, 0x5a, 0xe0, 0xe9, 0x19, + 0x03, 0x93, 0xc2, 0xbc, 0x83, 0x4b, 0xda, 0x7f, 0x34, 0xc2, 0xac, 0xa1, 0x5a, 0xad, 0xed, 0x36, 0x2d, 0x1f, 0x12, + 0x05, 0xc2, 0xf6, 0x53, 0xbd, 0xe9, 0x7e, 0xe4, 0x67, 0xd7, 0x02, 0xd4, 0x55, 0x6c, 0xbb, 0xc6, 0x8b, 0x7a, 0xef, + 0x6d, 0x6b, 0xf4, 0xb1, 0xbf, 0xd2, 0xf0, 0x2d, 0xc4, 0xb0, 0x2c, 0x99, 0x30, 0x5d, 0x99, 0x0f, 0x7e, 0xce, 0x14, + 0xf7, 0x79, 0x1a, 0x93, 0xee, 0x0e, 0x25, 0x26, 0xf1, 0x75, 0x67, 0x77, 0xd8, 0xb6, 0x8c, 0xe8, 0x65, 0xfd, 0x56, + 0xaf, 0xb0, 0xd3, 0xe7, 0xdf, 0x41, 0x4c, 0xbd, 0xa2, 0x64, 0x3c, 0x4c, 0xb4, 0xc5, 0x43, 0x50, 0x18, 0xbf, 0xca, + 0x9c, 0x0c, 0x3e, 0xb9, 0xb7, 0x2d, 0x24, 0xc2, 0x6f, 0xe3, 0x55, 0x9c, 0xcc, 0x5a, 0x34, 0x9c, 0x76, 0x3d, 0x29, + 0x0e, 0x8c, 0x84, 0xd6, 0xcc, 0xb7, 0x49, 0x5a, 0x73, 0x29, 0x0c, 0xbf, 0x58, 0x88, 0x8d, 0x66, 0xe3, 0x28, 0x5a, + 0x0a, 0xa0, 0xa5, 0x3d, 0x72, 0xc9, 0x62, 0xe0, 0x61, 0xc1, 0x43, 0xf9, 0xd2, 0x12, 0x96, 0x3d, 0x7f, 0x9d, 0x4e, + 0xe4, 0x9b, 0x9b, 0x9c, 0x6e, 0xb7, 0x73, 0x75, 0xf9, 0xfc, 0x4b, 0x1a, 0x51, 0x56, 0xbf, 0xe8, 0x91, 0x44, 0x35, + 0xd6, 0xc7, 0xd6, 0xf3, 0x2f, 0xb9, 0x57, 0x27, 0x92, 0xd3, 0xce, 0x76, 0xc0, 0x70, 0x4d, 0x01, 0x5b, 0xa6, 0xed, + 0x61, 0x53, 0xf6, 0xf7, 0x5b, 0x17, 0x07, 0x75, 0x41, 0xe2, 0x13, 0xe6, 0x14, 0x49, 0x8a, 0xc7, 0x06, 0x1d, 0x08, + 0xb5, 0x0c, 0xa8, 0x47, 0xb0, 0x2f, 0x27, 0x76, 0xe4, 0x9b, 0xa7, 0xd1, 0x2f, 0xca, 0x74, 0xe8, 0x90, 0xa6, 0x43, + 0x1e, 0x02, 0x1b, 0xb7, 0xb9, 0xcb, 0x81, 0x22, 0x71, 0xa0, 0x22, 0x66, 0xda, 0x2f, 0x52, 0x7b, 0x39, 0x2f, 0xc2, + 0x9c, 0xa3, 0xea, 0xca, 0xe9, 0x53, 0x62, 0xdf, 0x85, 0x18, 0x7d, 0x88, 0x5b, 0x79, 0x67, 0x87, 0xbd, 0x91, 0x7e, + 0x88, 0x73, 0xf3, 0x25, 0x0e, 0x8c, 0xa8, 0xd2, 0x1c, 0xcd, 0x42, 0xa4, 0x14, 0xb9, 0xa6, 0x95, 0x7d, 0x47, 0x91, + 0xe9, 0x7a, 0x16, 0x7d, 0x79, 0x96, 0xc8, 0xec, 0x89, 0x30, 0x99, 0x43, 0xbd, 0x83, 0x97, 0x94, 0x68, 0xd6, 0xb6, + 0x5b, 0x07, 0x04, 0x76, 0x02, 0xe6, 0x69, 0x89, 0xbc, 0x4e, 0xc9, 0xc9, 0x7f, 0x7c, 0xfb, 0x2f, 0x2a, 0x79, 0x04, + 0x0f, 0x35, 0x75, 0x61, 0x19, 0x2d, 0x44, 0x1c, 0xc7, 0xf9, 0xdd, 0xba, 0x4e, 0x40, 0x8c, 0xf5, 0xe7, 0x67, 0x6b, + 0xcc, 0xd6, 0x41, 0xad, 0xa4, 0xa1, 0x48, 0xcc, 0xcd, 0x8e, 0x99, 0x95, 0xc9, 0x95, 0x71, 0xc5, 0x6e, 0x83, 0x7e, + 0x12, 0x59, 0x28, 0xd1, 0x8c, 0xe2, 0xe1, 0x14, 0x8b, 0xa4, 0xa4, 0x15, 0x16, 0xb5, 0xe4, 0x33, 0x43, 0x39, 0x4c, + 0x96, 0xa5, 0x6d, 0x67, 0x2e, 0x85, 0x64, 0x2d, 0x4b, 0x80, 0xec, 0x62, 0x89, 0x9a, 0xf3, 0x8a, 0x5c, 0x86, 0x15, + 0x91, 0x13, 0xc0, 0x38, 0x30, 0x85, 0x9f, 0xfc, 0x49, 0x68, 0x7f, 0x27, 0x0f, 0x3e, 0x85, 0xf0, 0x32, 0x4e, 0xd0, + 0x83, 0x71, 0x2b, 0x98, 0xc1, 0xc1, 0x10, 0xbd, 0x50, 0xc2, 0xba, 0xdc, 0x89, 0x17, 0x24, 0xcb, 0x52, 0x37, 0x40, + 0x68, 0xd6, 0xcd, 0x5a, 0xdd, 0xb7, 0xb0, 0x2a, 0x59, 0x42, 0x68, 0xc4, 0x4a, 0x2b, 0xb6, 0x62, 0x9b, 0x82, 0x8e, + 0x28, 0xc9, 0x09, 0x60, 0x66, 0x00, 0xce, 0x4e, 0x22, 0x2a, 0x35, 0xb0, 0x8e, 0x61, 0xc5, 0x62, 0xa6, 0x31, 0x29, + 0x80, 0xd5, 0xae, 0xf1, 0x51, 0x36, 0x4d, 0x17, 0x28, 0x54, 0x5f, 0x3b, 0x27, 0xe8, 0xa3, 0x4b, 0x2b, 0xf5, 0xd8, + 0x27, 0x60, 0xff, 0xe3, 0x0e, 0xea, 0x60, 0xd1, 0xa8, 0xfb, 0xd6, 0xbf, 0xc4, 0x90, 0xe7, 0x35, 0x62, 0xdc, 0xdc, + 0x1f, 0x38, 0xd5, 0x01, 0x9b, 0x64, 0x35, 0x1b, 0x49, 0x9c, 0x04, 0x3d, 0x87, 0xea, 0x4d, 0x28, 0xc1, 0x50, 0x5d, + 0xba, 0xca, 0x9e, 0x47, 0x46, 0xbc, 0x35, 0x96, 0x95, 0x2c, 0xf9, 0x19, 0xd0, 0x05, 0xe5, 0x29, 0x21, 0x38, 0xdb, + 0xce, 0x4a, 0xa2, 0x30, 0xd6, 0xa2, 0x38, 0xc6, 0x09, 0xbf, 0x23, 0x59, 0x19, 0x97, 0x4c, 0x51, 0x98, 0xf2, 0x39, + 0x38, 0x57, 0xe6, 0xc3, 0xdf, 0x9e, 0xfc, 0xf2, 0x9c, 0xae, 0x2e, 0x45, 0xec, 0xf3, 0xe3, 0x9c, 0x5e, 0x7f, 0x9b, + 0xfe, 0x25, 0xf3, 0x59, 0xf8, 0x27, 0xbc, 0xb3, 0x84, 0x9c, 0x77, 0x3f, 0x3e, 0x15, 0x2d, 0x0e, 0x8a, 0x85, 0xae, + 0x62, 0x8b, 0x5a, 0x70, 0xfe, 0xfc, 0xca, 0x66, 0xaa, 0x3c, 0x26, 0x68, 0xa6, 0x92, 0xb2, 0xfa, 0x4d, 0x91, 0x02, + 0x69, 0x1b, 0x95, 0x84, 0x8d, 0xff, 0x31, 0x05, 0xc5, 0xff, 0x47, 0x19, 0x0a, 0x0d, 0x59, 0xfb, 0xeb, 0x2d, 0x93, + 0xfc, 0x0a, 0x9e, 0xff, 0x31, 0x29, 0x50, 0xab, 0x9f, 0x08, 0x50, 0x49, 0x5b, 0x49, 0xa5, 0x0f, 0x0e, 0x3c, 0xd6, + 0xd1, 0xe4, 0x8c, 0x69, 0x18, 0xcf, 0x3c, 0x61, 0x3f, 0x03, 0x86, 0xb6, 0x59, 0x97, 0xbc, 0xdb, 0x36, 0xf1, 0x1f, + 0x28, 0xbc, 0x29, 0x53, 0x1b, 0x8d, 0x41, 0x72, 0xaa, 0x00, 0x69, 0x8e, 0xb3, 0x55, 0xe8, 0x8a, 0x36, 0x9c, 0x73, + 0xb3, 0xa5, 0x05, 0x67, 0xc3, 0xd8, 0x6a, 0xf8, 0xf2, 0x17, 0xc4, 0x56, 0xd8, 0x35, 0xa9, 0x83, 0xaa, 0xac, 0x79, + 0x71, 0x13, 0xfe, 0x09, 0xdb, 0x4b, 0x0c, 0x66, 0xf2, 0x92, 0xe6, 0x93, 0xe9, 0x08, 0x69, 0x9e, 0x21, 0x67, 0x36, + 0xff, 0xa3, 0x98, 0xc9, 0xf2, 0x52, 0x46, 0x33, 0x5f, 0x26, 0xc6, 0xbf, 0xf9, 0x33, 0x09, 0xec, 0x57, 0xce, 0x87, + 0x51, 0x64, 0x62, 0x79, 0x6c, 0x1b, 0x2f, 0xc8, 0x7d, 0x0c, 0xdd, 0x68, 0xb1, 0xca, 0xb2, 0x8c, 0x7d, 0xa5, 0xcc, + 0xd2, 0x18, 0x83, 0xc3, 0xd3, 0xf5, 0x88, 0x2a, 0x74, 0xd6, 0x87, 0x3c, 0x97, 0xfe, 0x65, 0x95, 0x0a, 0xd3, 0x87, + 0x32, 0x53, 0x5a, 0x6f, 0x81, 0xd8, 0xeb, 0x89, 0xe2, 0xc3, 0x57, 0x12, 0x6d, 0x72, 0x24, 0xe7, 0x83, 0x53, 0x58, + 0x4d, 0xf2, 0xda, 0x23, 0x13, 0xf1, 0x0c, 0x3f, 0xd9, 0xf6, 0xf3, 0x5c, 0x49, 0xcf, 0x2f, 0x3e, 0xc3, 0x6e, 0x97, + 0xc6, 0xde, 0x4b, 0x7e, 0x27, 0x3f, 0x47, 0x1f, 0x06, 0x77, 0xe4, 0xa4, 0xa4, 0xb6, 0xbf, 0xf4, 0x39, 0xae, 0x03, + 0x65, 0xf7, 0x3f, 0xa8, 0xbe, 0x86, 0x2c, 0x2a, 0x1e, 0x4d, 0xd2, 0x15, 0xe6, 0x60, 0xa9, 0x1f, 0x66, 0x2e, 0xfc, + 0x45, 0x9a, 0xe0, 0x2c, 0xba, 0xd1, 0xcb, 0x83, 0x69, 0x3d, 0xf9, 0x47, 0x64, 0xe9, 0x4f, 0xb3, 0x6c, 0x72, 0x38, + 0x0d, 0x17, 0xfc, 0x48, 0x46, 0x3f, 0xde, 0xab, 0xdb, 0x93, 0x7a, 0xad, 0x97, 0x7b, 0x08, 0x98, 0x7e, 0xa4, 0x21, + 0x92, 0x37, 0xcb, 0x54, 0x61, 0x40, 0xf2, 0x06, 0x17, 0xb4, 0x06, 0x5d, 0x6a, 0x9a, 0xa5, 0x55, 0xe0, 0x8c, 0xee, + 0x09, 0x3a, 0xa8, 0xe0, 0x68, 0xb9, 0xf2, 0xf5, 0x59, 0xc4, 0xe2, 0xa4, 0x62, 0xbb, 0x2d, 0x8a, 0x68, 0xcf, 0xe0, + 0x38, 0x5a, 0x44, 0x45, 0x66, 0xf4, 0xbb, 0xd4, 0x56, 0x28, 0xfb, 0x82, 0x15, 0xdc, 0xd1, 0x17, 0xb2, 0x52, 0xae, + 0xa5, 0x21, 0xdf, 0x4a, 0xc9, 0x16, 0x1a, 0x50, 0x29, 0xc5, 0x96, 0xaa, 0x71, 0x19, 0x07, 0x57, 0xc6, 0xe6, 0x58, + 0xc2, 0x92, 0x56, 0xc5, 0xab, 0xc8, 0x90, 0x8e, 0xaf, 0x13, 0x41, 0xca, 0x65, 0x19, 0x38, 0x3c, 0x9c, 0xa3, 0x0c, + 0x79, 0xb2, 0x0d, 0x25, 0x79, 0x26, 0x60, 0x0e, 0x66, 0x5c, 0xab, 0x27, 0xd5, 0xaa, 0x01, 0x8d, 0x14, 0xd5, 0x55, + 0x4c, 0x67, 0xab, 0x03, 0xea, 0xf8, 0x15, 0x81, 0x59, 0x58, 0xc6, 0xf3, 0x28, 0xc4, 0x5d, 0x29, 0xc3, 0x2e, 0xdc, + 0x4e, 0x12, 0xac, 0xc7, 0xc9, 0x70, 0xb8, 0xa3, 0x8d, 0x9d, 0x8b, 0x5e, 0xe3, 0x47, 0x21, 0x5c, 0x4a, 0xf7, 0x18, + 0x19, 0x81, 0xc9, 0xc5, 0xce, 0xa5, 0xf3, 0x49, 0x13, 0xee, 0x64, 0x41, 0x00, 0x44, 0x1e, 0xf6, 0x7d, 0xb0, 0xb8, + 0x3c, 0xea, 0x2c, 0x60, 0x62, 0x9e, 0x2b, 0x3b, 0x2a, 0x6f, 0xe0, 0xab, 0x75, 0x28, 0x2b, 0x7b, 0x47, 0x5f, 0x26, + 0x31, 0x56, 0xda, 0x8c, 0xdf, 0x96, 0xe5, 0x51, 0x7a, 0x63, 0x59, 0x4d, 0x5b, 0x54, 0x0f, 0x1e, 0xdd, 0xe1, 0xda, + 0x11, 0x63, 0x63, 0x99, 0x75, 0x62, 0x11, 0x98, 0xff, 0x3e, 0xb3, 0x08, 0x1b, 0x55, 0x2d, 0xdf, 0x04, 0xd2, 0x11, + 0xa3, 0x59, 0xd4, 0xf0, 0x80, 0x4f, 0x47, 0xcb, 0x18, 0x16, 0x33, 0x82, 0x59, 0xf6, 0xa0, 0xe5, 0x6a, 0x08, 0xd2, + 0x8c, 0x47, 0x89, 0x20, 0xdd, 0x88, 0xa1, 0x19, 0xc9, 0x19, 0x01, 0x9b, 0xa4, 0x10, 0x83, 0x67, 0xc0, 0xfe, 0xd8, + 0x39, 0x22, 0x15, 0x1c, 0xd1, 0x03, 0xc2, 0xaa, 0x8a, 0xcb, 0x0f, 0x0b, 0x1b, 0x06, 0x62, 0x48, 0xc5, 0x8b, 0x59, + 0xf9, 0xb4, 0x00, 0x18, 0x59, 0xa3, 0x8a, 0x87, 0x64, 0x88, 0x8c, 0xbc, 0x69, 0x91, 0x51, 0x87, 0x64, 0x0c, 0xbf, + 0x11, 0x31, 0x90, 0x94, 0x9c, 0x41, 0x1e, 0x73, 0xb2, 0x55, 0x2e, 0x5f, 0xe6, 0x2e, 0xfd, 0xd3, 0xfe, 0x54, 0x8e, + 0xf7, 0xa9, 0xd4, 0xd0, 0xa6, 0x97, 0x71, 0x39, 0x17, 0x15, 0x07, 0xd7, 0xcb, 0x76, 0xd3, 0xd3, 0x8e, 0xe6, 0x0b, + 0xd7, 0xe6, 0x66, 0xbb, 0x30, 0xde, 0x1d, 0xab, 0xec, 0xc3, 0x27, 0x94, 0x71, 0x41, 0x33, 0x3c, 0xec, 0xd4, 0x6d, + 0x23, 0x63, 0x18, 0x41, 0xff, 0x36, 0xbe, 0x9e, 0xc8, 0x2e, 0x5d, 0xe6, 0x82, 0xe4, 0x30, 0x6f, 0xf0, 0x6d, 0x61, + 0xfc, 0x25, 0xd9, 0x8d, 0xd6, 0xc9, 0xba, 0xa7, 0x35, 0xba, 0x7b, 0x69, 0xc3, 0x17, 0x1c, 0xa0, 0xf3, 0x4b, 0x1c, + 0xea, 0xd1, 0x14, 0x58, 0xee, 0xf3, 0xa6, 0x3e, 0x41, 0xa6, 0xf1, 0xb0, 0xb6, 0x03, 0x72, 0x8d, 0xe7, 0xba, 0x8d, + 0x1a, 0xf5, 0x1d, 0x5b, 0xa6, 0xb7, 0xc4, 0x56, 0xde, 0xdb, 0x6c, 0x83, 0x39, 0x50, 0xf5, 0xdf, 0x3e, 0x44, 0x22, + 0x18, 0x49, 0xd3, 0x3e, 0x47, 0xeb, 0x77, 0x2e, 0xcf, 0xfc, 0xeb, 0xcc, 0xd1, 0x86, 0x95, 0x61, 0x46, 0x83, 0x19, + 0x5f, 0xe9, 0xce, 0xd0, 0xcc, 0x6b, 0xe6, 0x1e, 0xb8, 0xdd, 0x4b, 0xef, 0xc6, 0x9a, 0x35, 0xfa, 0x61, 0xba, 0x53, + 0x92, 0x59, 0xe0, 0x74, 0xfc, 0x9b, 0xa0, 0xa7, 0x82, 0xf4, 0xa3, 0x3a, 0xb0, 0xf8, 0x8e, 0x93, 0x98, 0x90, 0x0c, + 0x39, 0x58, 0x90, 0xab, 0xe6, 0xbd, 0xa7, 0xdb, 0x5e, 0x9b, 0xb2, 0x46, 0x5c, 0x3a, 0x5d, 0x7d, 0x79, 0xbd, 0xf0, + 0x02, 0xed, 0xf1, 0xde, 0x8f, 0x36, 0xde, 0xd0, 0xc9, 0xe3, 0x0d, 0x54, 0x44, 0xfc, 0x86, 0xdc, 0xd0, 0x18, 0x5f, + 0x85, 0x29, 0x03, 0xc7, 0x7c, 0xef, 0xae, 0xbd, 0x69, 0xee, 0xf1, 0x8b, 0xb9, 0x56, 0x67, 0x4e, 0xb4, 0x57, 0x66, + 0xbd, 0x32, 0x71, 0xb1, 0xa0, 0x24, 0x1f, 0x1e, 0x10, 0x5c, 0xc7, 0x3f, 0xad, 0x56, 0xe1, 0xae, 0xc7, 0x0f, 0x72, + 0xb0, 0x14, 0x03, 0xd3, 0x0d, 0x5c, 0x07, 0x62, 0x1d, 0xc6, 0x16, 0x69, 0x60, 0xa9, 0x1f, 0xca, 0x88, 0x51, 0x30, + 0x7e, 0x7e, 0xbc, 0x8c, 0x7a, 0xc7, 0x7f, 0x58, 0x02, 0x58, 0xb7, 0x11, 0x8e, 0x40, 0x33, 0x2b, 0x4e, 0x39, 0x1f, + 0x17, 0xfa, 0x08, 0xae, 0x6c, 0xca, 0xbe, 0x61, 0xe0, 0x90, 0x15, 0x98, 0xf6, 0x47, 0x43, 0xe5, 0xf7, 0x4f, 0xe4, + 0xc7, 0xb5, 0xbb, 0xdf, 0x6b, 0xd3, 0xc6, 0x0c, 0x47, 0x8f, 0x90, 0x89, 0x0e, 0xe6, 0x40, 0x87, 0x47, 0xc3, 0x62, + 0xca, 0x8e, 0x9b, 0xda, 0xb3, 0x1a, 0x6f, 0xc9, 0xf1, 0x18, 0x7e, 0xad, 0xa2, 0xd9, 0x78, 0x90, 0x6e, 0xab, 0x5c, + 0xcf, 0x76, 0x94, 0x6f, 0x7e, 0xe8, 0x34, 0xd9, 0xc2, 0x37, 0xfa, 0xd7, 0x39, 0xb4, 0x68, 0xbe, 0x46, 0xb4, 0xc8, + 0x1a, 0xea, 0x03, 0xf0, 0xe3, 0x42, 0x63, 0xcd, 0x63, 0x28, 0x08, 0x9b, 0x9b, 0xd6, 0xb5, 0x0d, 0x0d, 0x9a, 0x39, + 0x79, 0x27, 0x48, 0x51, 0x00, 0x89, 0x3b, 0x56, 0xa1, 0xa7, 0x73, 0x10, 0x18, 0x3c, 0xf6, 0x3e, 0xb5, 0x6e, 0x4c, + 0x51, 0x97, 0x7b, 0x4c, 0x34, 0x76, 0xb3, 0x6f, 0x8b, 0xf6, 0xe9, 0x57, 0xfa, 0x8f, 0xc8, 0x85, 0x08, 0x0c, 0x9e, + 0x1f, 0x00, 0xfb, 0x38, 0xb0, 0x15, 0xcd, 0x26, 0x95, 0x37, 0x7c, 0x6e, 0x5f, 0x7f, 0xee, 0xcb, 0xa7, 0xd9, 0x5c, + 0x20, 0xd1, 0xf7, 0xe7, 0xa6, 0x4e, 0xa6, 0x2a, 0xd7, 0x72, 0x07, 0xbb, 0x38, 0x9a, 0x86, 0x18, 0x2d, 0x00, 0x1a, + 0x65, 0x20, 0xf8, 0x09, 0x3e, 0x52, 0x67, 0xfc, 0xf3, 0x79, 0x97, 0xe7, 0x74, 0xff, 0xe1, 0x2d, 0x99, 0xde, 0xd2, + 0x1c, 0xf0, 0x6d, 0xc8, 0xff, 0xed, 0xbf, 0xd1, 0xad, 0x63, 0xac, 0x08, 0xcc, 0x0e, 0xae, 0xcd, 0xa2, 0x5c, 0x7a, + 0x5b, 0x9b, 0xb8, 0xf2, 0x71, 0xf6, 0x03, 0xdc, 0xe6, 0xbe, 0x11, 0x18, 0x4d, 0xe1, 0x63, 0x16, 0x93, 0xb6, 0xca, + 0x75, 0xd3, 0x13, 0x66, 0xdb, 0xe8, 0x12, 0xa9, 0x21, 0xb8, 0xde, 0xc7, 0xb2, 0xd8, 0x78, 0x32, 0x92, 0xd5, 0xf6, + 0xc5, 0x53, 0x01, 0x2e, 0x34, 0x96, 0x7f, 0xa2, 0xce, 0xdb, 0x3d, 0x6a, 0x93, 0xd3, 0xfe, 0x87, 0xd6, 0xee, 0xb9, + 0x54, 0x74, 0x6d, 0x8f, 0x4d, 0x9f, 0x5a, 0x0b, 0x86, 0x60, 0xdf, 0x92, 0x15, 0x7b, 0x01, 0xd0, 0x0e, 0xf0, 0x42, + 0xb5, 0x89, 0x6e, 0xab, 0xfe, 0xb1, 0x07, 0xa4, 0x31, 0xbe, 0xc7, 0x24, 0x55, 0x6e, 0x64, 0x42, 0xcd, 0x22, 0x41, + 0xd1, 0x71, 0x7c, 0x7c, 0x47, 0x5b, 0xad, 0x87, 0x17, 0x62, 0x55, 0x0a, 0x63, 0xcb, 0xdc, 0x9b, 0x32, 0xc8, 0x69, + 0xaa, 0x0f, 0x49, 0x0b, 0xb7, 0x0d, 0x5d, 0x0a, 0x1f, 0x8b, 0x47, 0xad, 0x76, 0x20, 0x27, 0x1b, 0x08, 0xe1, 0x88, + 0xce, 0x5f, 0x4a, 0x9d, 0x02, 0xbc, 0x0e, 0xdc, 0x15, 0xc7, 0xb0, 0x6c, 0xc7, 0xdd, 0xa8, 0xd5, 0x16, 0xfe, 0xec, + 0x00, 0xd4, 0xb0, 0xae, 0xda, 0xed, 0x1d, 0xf5, 0xba, 0x4c, 0x61, 0x94, 0x0a, 0x09, 0x08, 0x87, 0xcb, 0xd9, 0xa4, + 0x20, 0x94, 0x04, 0x8c, 0x55, 0x51, 0xfd, 0xa1, 0xcc, 0x6d, 0xb7, 0x1b, 0x35, 0xe7, 0x91, 0x78, 0x18, 0xa8, 0x58, + 0x8f, 0x69, 0x6d, 0xe6, 0xe0, 0x80, 0x42, 0xd4, 0x6c, 0x7a, 0x2c, 0x7f, 0x58, 0x8f, 0xe4, 0x52, 0xf0, 0x48, 0xc4, + 0xe2, 0x6d, 0x8f, 0xd1, 0xe4, 0x8f, 0x67, 0xc8, 0xec, 0x2d, 0x17, 0x3f, 0xcc, 0xe1, 0x76, 0x62, 0x97, 0x01, 0x4f, + 0x30, 0x31, 0x35, 0xea, 0xc9, 0x56, 0xf4, 0x14, 0x90, 0x0e, 0xb3, 0x82, 0x01, 0xc2, 0x29, 0xf5, 0xcb, 0x68, 0xcc, + 0x9b, 0xcb, 0x95, 0x5b, 0x89, 0x46, 0xb4, 0x94, 0x85, 0xb6, 0xdc, 0x96, 0x1f, 0x26, 0x94, 0xac, 0xb8, 0xa6, 0xb6, + 0x99, 0xad, 0xa2, 0x45, 0x2b, 0x08, 0x7f, 0x5c, 0xcd, 0x8c, 0xa8, 0xbf, 0x90, 0x6e, 0xd6, 0x74, 0x77, 0x06, 0x69, + 0x35, 0xa7, 0x76, 0x76, 0x8e, 0xe6, 0x82, 0x06, 0xea, 0x35, 0x82, 0x8c, 0xc5, 0xa5, 0x26, 0xe5, 0xac, 0x73, 0xa1, + 0xc6, 0x1b, 0x86, 0xaf, 0x9b, 0xa4, 0x5e, 0x94, 0x36, 0xae, 0x6e, 0x74, 0xea, 0x4b, 0xd0, 0xc1, 0xa0, 0x83, 0x84, + 0x94, 0x5a, 0x85, 0x8a, 0xec, 0xd3, 0xc5, 0xba, 0x70, 0x9a, 0x90, 0x74, 0xba, 0xe2, 0xe5, 0xa4, 0x78, 0xcf, 0x08, + 0x71, 0xf4, 0x03, 0x52, 0x26, 0x8f, 0x50, 0x93, 0xbc, 0xf6, 0x01, 0x65, 0xf2, 0x34, 0x6a, 0x71, 0xd8, 0xd0, 0x06, + 0x11, 0x0f, 0x06, 0xc7, 0xe3, 0x08, 0x52, 0xc1, 0x7a, 0x4a, 0x46, 0x97, 0x00, 0x49, 0x2f, 0xc9, 0xd3, 0x03, 0x0b, + 0xa6, 0xe6, 0x4e, 0x29, 0x28, 0x9e, 0x0c, 0x30, 0xb4, 0x95, 0x46, 0x65, 0xc9, 0x0c, 0x45, 0x0f, 0x74, 0xeb, 0xf7, + 0x14, 0x0a, 0x18, 0x23, 0xce, 0x1e, 0xfb, 0xdc, 0x04, 0x10, 0x14, 0x87, 0x35, 0x08, 0xdd, 0x67, 0x04, 0x1b, 0x79, + 0x46, 0xc1, 0x22, 0xcf, 0x07, 0xe4, 0xa8, 0xec, 0x65, 0x35, 0xf7, 0x5f, 0xce, 0x90, 0x0d, 0x0c, 0x1e, 0xd5, 0x93, + 0x4e, 0xae, 0xf5, 0xeb, 0x70, 0x82, 0x9c, 0xd1, 0xa7, 0xac, 0x9e, 0xb4, 0x73, 0x53, 0x4f, 0xd1, 0xac, 0x50, 0x7f, + 0xe6, 0x1e, 0x5e, 0xe1, 0x5b, 0x39, 0x33, 0xca, 0x22, 0x15, 0xf1, 0xc2, 0x0f, 0x60, 0xe3, 0xe7, 0x59, 0xc7, 0xe0, + 0xf0, 0xc4, 0xd9, 0xea, 0x84, 0x38, 0xc4, 0x35, 0x39, 0xf8, 0xb8, 0x45, 0x8c, 0x1a, 0x34, 0x26, 0xb7, 0xa8, 0xd6, + 0x94, 0x78, 0x0b, 0xf5, 0xa9, 0xc1, 0x50, 0x1b, 0x27, 0x5d, 0x59, 0x09, 0x26, 0x34, 0xbc, 0xe4, 0x53, 0x25, 0xeb, + 0x28, 0x56, 0xf8, 0xe5, 0x0a, 0x30, 0x1b, 0x98, 0xe6, 0xae, 0x13, 0x0c, 0x56, 0x9a, 0x53, 0x33, 0xf2, 0xea, 0xdc, + 0x21, 0x94, 0xba, 0xd1, 0x0b, 0x98, 0x00, 0x86, 0x43, 0x46, 0x1b, 0xf4, 0xf2, 0xc2, 0x97, 0x0b, 0x52, 0xb5, 0x23, + 0x87, 0x0c, 0x16, 0x39, 0x91, 0x06, 0x87, 0xf8, 0x9f, 0x09, 0x41, 0xd2, 0x66, 0x07, 0xe2, 0xcd, 0xb1, 0x9b, 0x3a, + 0x56, 0x3d, 0x07, 0xf9, 0xdd, 0x0d, 0xf6, 0x5a, 0xf1, 0xda, 0x34, 0xa9, 0xa1, 0x57, 0xa3, 0x71, 0x28, 0x48, 0xcb, + 0x8b, 0xd9, 0x95, 0x27, 0x4d, 0xa2, 0xdb, 0xd2, 0x55, 0x83, 0x1e, 0xc2, 0x3b, 0xf3, 0x90, 0xdf, 0xf0, 0xbe, 0x9e, + 0xcc, 0x05, 0x45, 0x87, 0x70, 0x0d, 0xb9, 0x89, 0x44, 0xfd, 0x44, 0x57, 0x6c, 0x41, 0x59, 0xec, 0x67, 0xa8, 0x03, + 0xbc, 0xb4, 0x38, 0x41, 0x61, 0x8f, 0xd4, 0xb8, 0xe0, 0xb6, 0x27, 0x0c, 0x53, 0xeb, 0xb2, 0x70, 0xd9, 0xe9, 0xb6, + 0x68, 0x72, 0x2d, 0x50, 0x0c, 0x02, 0xcd, 0x79, 0xfe, 0x7a, 0x7b, 0xea, 0x1a, 0xcf, 0xe0, 0x74, 0xec, 0x60, 0x74, + 0x32, 0xe3, 0x2a, 0x61, 0x83, 0xa8, 0xc3, 0x5d, 0xba, 0x69, 0x20, 0x97, 0x3d, 0xa8, 0x6e, 0x9e, 0xf7, 0xa7, 0xb3, + 0x6b, 0xe3, 0xad, 0x06, 0xd0, 0x1e, 0x00, 0xca, 0x8b, 0x5d, 0xfa, 0xc0, 0x89, 0x9b, 0x76, 0xf7, 0x25, 0xd6, 0x1b, + 0xa8, 0x91, 0x88, 0x20, 0x0a, 0x48, 0x98, 0xfa, 0xe7, 0x4e, 0xd9, 0xf4, 0xf1, 0x1d, 0xaf, 0x3a, 0x51, 0xa8, 0x90, + 0x34, 0x70, 0x8d, 0xa3, 0x87, 0x43, 0x1b, 0x73, 0xc0, 0x1a, 0xe3, 0x44, 0xb8, 0xdf, 0x62, 0xdf, 0xb5, 0x56, 0x1c, + 0xd7, 0x65, 0xb8, 0xe8, 0x3b, 0x45, 0x35, 0x07, 0xc3, 0xab, 0xc3, 0xe3, 0x3c, 0xf8, 0x15, 0xaa, 0xa8, 0xe4, 0xdb, + 0x2e, 0x47, 0x1e, 0x57, 0xa0, 0xcb, 0xf9, 0xb6, 0xbd, 0xbf, 0xc1, 0x30, 0x80, 0x28, 0xf0, 0x41, 0x15, 0xbb, 0x54, + 0x39, 0xb1, 0x3e, 0x70, 0xd6, 0x08, 0x32, 0xaf, 0x22, 0xc4, 0x2b, 0x2e, 0xf9, 0x7d, 0x07, 0x80, 0x5d, 0xb9, 0xca, + 0xb2, 0xae, 0x2b, 0xff, 0x6f, 0x86, 0x11, 0x42, 0xc6, 0xd0, 0xb1, 0x6f, 0xb7, 0xe4, 0x34, 0x06, 0xf5, 0x74, 0xdc, + 0xec, 0x4d, 0x3c, 0x37, 0x0e, 0x5c, 0x00, 0x14, 0xb1, 0x7c, 0xcd, 0x13, 0xde, 0x45, 0x9c, 0x05, 0x88, 0x0d, 0x92, + 0xcf, 0x60, 0xca, 0x71, 0xbf, 0xbe, 0x96, 0x2c, 0xab, 0x38, 0x73, 0x50, 0x1f, 0x9c, 0xfb, 0xa7, 0xe6, 0xf0, 0xb2, + 0x4d, 0x31, 0x0e, 0xc7, 0x8f, 0x3f, 0xd0, 0x55, 0x0c, 0xac, 0x54, 0x7b, 0x20, 0x2d, 0x98, 0xf7, 0x5a, 0xa1, 0x61, + 0xa1, 0xf5, 0xa1, 0x4f, 0x4d, 0xe6, 0x7d, 0xfc, 0x78, 0x55, 0x3d, 0xd0, 0x01, 0x3a, 0xb9, 0x43, 0x69, 0x7f, 0x68, + 0xa9, 0x6f, 0x56, 0xbf, 0x44, 0x05, 0x76, 0x99, 0x83, 0xdd, 0x1e, 0xc7, 0x39, 0x9b, 0x15, 0xd9, 0xd1, 0x2f, 0x44, + 0x97, 0x09, 0x3b, 0x7c, 0x9c, 0x9a, 0xe6, 0x0f, 0xb0, 0x2b, 0x5f, 0x6e, 0xfe, 0x44, 0x09, 0x4c, 0xd4, 0xd9, 0x60, + 0x1f, 0x01, 0xd0, 0x7d, 0xf0, 0x79, 0x82, 0xe4, 0xe3, 0xfa, 0x71, 0xf7, 0x5f, 0xfb, 0x03, 0xd4, 0x79, 0x57, 0x62, + 0xd9, 0x40, 0x9c, 0xb8, 0x42, 0x02, 0xda, 0x14, 0x42, 0x7f, 0x2a, 0xe5, 0x65, 0x1c, 0x8a, 0x67, 0x4d, 0x07, 0x95, + 0xbb, 0xb9, 0x4a, 0x26, 0xa0, 0xc1, 0x9b, 0x64, 0x96, 0xfd, 0x98, 0x0e, 0x7b, 0xa9, 0x69, 0xea, 0x27, 0x73, 0x5d, + 0x59, 0xad, 0xa6, 0x7c, 0xbb, 0x7d, 0x57, 0x7e, 0xba, 0xe9, 0x09, 0xd2, 0x78, 0xcf, 0x03, 0xb7, 0x75, 0xdf, 0xc8, + 0x1a, 0x0c, 0xf0, 0xcd, 0xc2, 0xa8, 0xca, 0xe9, 0x08, 0x85, 0xa8, 0x98, 0x07, 0x7f, 0x01, 0x62, 0x3c, 0xac, 0xc6, + 0xf1, 0x93, 0x4e, 0x27, 0xc0, 0x32, 0xfb, 0xf2, 0x66, 0x63, 0x1d, 0xb1, 0x27, 0x30, 0xbc, 0xa8, 0xcc, 0x15, 0x2f, + 0xd1, 0x31, 0x70, 0xdb, 0xbb, 0xb2, 0x4a, 0xa6, 0xcb, 0xe7, 0xbe, 0x0d, 0x0a, 0x5f, 0x1f, 0x90, 0x20, 0x05, 0x2a, + 0x05, 0xf6, 0xc1, 0xe6, 0xfb, 0x08, 0x68, 0x1e, 0xe7, 0xaa, 0x9e, 0xae, 0xdb, 0xab, 0x2d, 0xda, 0x6f, 0xe1, 0x88, + 0xad, 0xad, 0x82, 0x3d, 0xec, 0xe5, 0xbc, 0x77, 0x7a, 0xf3, 0xe0, 0x17, 0xa6, 0x61, 0x16, 0x12, 0xef, 0x36, 0xea, + 0x1b, 0xd6, 0x6b, 0xb6, 0xf4, 0x99, 0xcc, 0x9a, 0x78, 0x98, 0xac, 0xa7, 0x91, 0x87, 0x93, 0x53, 0x79, 0x8e, 0xcd, + 0x63, 0x61, 0x81, 0x37, 0x74, 0xf5, 0xf4, 0x9a, 0x29, 0x3e, 0x9a, 0x8a, 0xe4, 0x25, 0x3e, 0xb9, 0x8a, 0x16, 0x80, + 0x63, 0xa2, 0x72, 0x7a, 0xed, 0x02, 0x27, 0xd8, 0xeb, 0x45, 0x09, 0x0d, 0x8e, 0x91, 0x63, 0x5b, 0x82, 0xa7, 0xa3, + 0x33, 0x31, 0x6b, 0x5c, 0x40, 0xfa, 0x9a, 0xac, 0xbf, 0xae, 0x42, 0x9a, 0x91, 0x49, 0x06, 0x1f, 0x3d, 0x4b, 0x53, + 0x37, 0x2f, 0x37, 0x80, 0xc0, 0x51, 0xf1, 0xbe, 0x0b, 0x64, 0x79, 0xc3, 0x90, 0x3c, 0xc9, 0xc1, 0x4a, 0xb7, 0x27, + 0xb8, 0x09, 0xc1, 0xff, 0xf9, 0xdd, 0xc2, 0x4a, 0xa6, 0x22, 0x97, 0x63, 0x14, 0xa2, 0xd8, 0x3d, 0xe7, 0x06, 0x73, + 0x53, 0xc9, 0x55, 0x02, 0xb5, 0xfc, 0x83, 0xed, 0xcf, 0x6a, 0x48, 0x72, 0xe6, 0x0b, 0xc8, 0x8b, 0xd9, 0x45, 0x28, + 0x70, 0x56, 0x6f, 0x51, 0xc4, 0x06, 0x82, 0x3d, 0xe6, 0x5a, 0xd3, 0xc3, 0x1c, 0x48, 0x66, 0x35, 0xc0, 0x68, 0x4b, + 0x04, 0xa9, 0x17, 0xec, 0xec, 0x52, 0xd1, 0x7d, 0x5d, 0x50, 0xa4, 0xbb, 0x2c, 0x11, 0x53, 0x69, 0x25, 0xc7, 0xe7, + 0x2d, 0xf6, 0xd7, 0x9a, 0xaa, 0xa5, 0xbe, 0xca, 0xce, 0x31, 0xa6, 0xa7, 0xe3, 0x4f, 0x1b, 0x3f, 0x12, 0x7e, 0x9f, + 0x2b, 0x66, 0x30, 0x1b, 0x86, 0xd1, 0x2e, 0x61, 0xd2, 0x50, 0x7d, 0xa6, 0x38, 0x6e, 0x2c, 0x37, 0x5e, 0x6e, 0x5f, + 0x74, 0xc5, 0x56, 0xe9, 0x9f, 0xbb, 0x05, 0xbe, 0x26, 0xdd, 0x6b, 0x32, 0x2f, 0x48, 0x6c, 0xf0, 0x44, 0xf7, 0x60, + 0x9d, 0xa8, 0xae, 0xfd, 0xcb, 0xf3, 0xd3, 0x84, 0x10, 0xb3, 0x6d, 0x2b, 0xf2, 0xca, 0x0a, 0x50, 0x0e, 0x69, 0x37, + 0x01, 0xf5, 0xa5, 0x1b, 0xce, 0x83, 0xba, 0xb1, 0x81, 0x97, 0x90, 0x5a, 0x03, 0xc5, 0x2e, 0x8c, 0x7d, 0x75, 0x3a, + 0x0a, 0x69, 0x72, 0x26, 0x7b, 0x48, 0x28, 0x26, 0x0c, 0xd0, 0x3f, 0x2d, 0x8e, 0x66, 0x54, 0xd0, 0x7a, 0x77, 0x45, + 0x75, 0x2c, 0x3b, 0xd7, 0x40, 0x94, 0x99, 0x8d, 0x66, 0xda, 0x41, 0x86, 0x37, 0x0e, 0x91, 0xef, 0x32, 0xd3, 0xd1, + 0x81, 0x1d, 0x53, 0xee, 0xa4, 0x0e, 0x1b, 0x57, 0xd9, 0x91, 0x04, 0xf6, 0xbd, 0xcc, 0x89, 0x50, 0xf8, 0x66, 0xb6, + 0x3c, 0x90, 0xaf, 0x75, 0xe5, 0x7f, 0xcd, 0xa8, 0xcf, 0x0a, 0x77, 0xb4, 0x2d, 0x57, 0x33, 0x0e, 0x63, 0xc3, 0x81, + 0xcc, 0xc7, 0x07, 0x26, 0x78, 0xe5, 0xa9, 0x2a, 0xfb, 0x4d, 0xd8, 0x65, 0x0f, 0xec, 0xd9, 0xe4, 0x28, 0x2d, 0x1d, + 0xb5, 0xff, 0xb5, 0xcb, 0xa2, 0x43, 0xd1, 0xb0, 0x68, 0x5d, 0x24, 0x88, 0x5a, 0x6d, 0xf1, 0xc3, 0x3c, 0x22, 0x41, + 0xed, 0x8b, 0xc5, 0x4b, 0x7b, 0xe0, 0xa3, 0x29, 0x06, 0xbe, 0xcf, 0x58, 0x3c, 0x89, 0xbe, 0x3f, 0xc2, 0x49, 0x19, + 0x28, 0x1d, 0x3a, 0x03, 0xd2, 0xc4, 0x2a, 0x1e, 0x93, 0x3c, 0x67, 0xb1, 0xc2, 0x5e, 0xf2, 0x3a, 0x2a, 0x83, 0x16, + 0xc9, 0x3f, 0x47, 0x7c, 0xd0, 0xe0, 0x18, 0x3c, 0x8a, 0xbc, 0xf4, 0x4b, 0x70, 0xcb, 0x7d, 0x7f, 0xc0, 0x08, 0x26, + 0x54, 0x6f, 0xd2, 0x62, 0xf4, 0x42, 0x44, 0xe6, 0x23, 0x34, 0x1e, 0xbf, 0x6f, 0x0d, 0x5e, 0x50, 0xfa, 0xd2, 0xce, + 0x40, 0x72, 0x13, 0xe8, 0xd2, 0x6e, 0x6a, 0x9c, 0x06, 0x72, 0x22, 0x53, 0xd7, 0x76, 0xdc, 0x77, 0xc3, 0x63, 0x41, + 0x5b, 0x82, 0x8c, 0xe9, 0x2e, 0x34, 0x73, 0x14, 0x18, 0xfe, 0xbd, 0xd5, 0x38, 0x02, 0x06, 0xec, 0x1a, 0xeb, 0xe1, + 0x97, 0x62, 0xdc, 0xa4, 0x4a, 0x3f, 0x5c, 0xe1, 0x9c, 0x5d, 0xd2, 0xe9, 0xcd, 0xef, 0x07, 0x4a, 0x20, 0x2e, 0xde, + 0x88, 0x55, 0xdf, 0x06, 0xf3, 0xcb, 0xa0, 0x00, 0x8c, 0xa9, 0x34, 0x64, 0xfa, 0xbf, 0x58, 0x17, 0xf4, 0x4e, 0x0c, + 0xd6, 0x0c, 0x0e, 0x0c, 0x22, 0x3e, 0xee, 0xe0, 0x1e, 0x7f, 0x1d, 0xfe, 0x37, 0x25, 0xa8, 0x2b, 0x77, 0x3f, 0x51, + 0xd6, 0x7c, 0x9f, 0x94, 0x22, 0xd3, 0x97, 0xef, 0x5e, 0xb6, 0x42, 0x1d, 0xd4, 0xd8, 0xe6, 0x16, 0x35, 0xaf, 0x2d, + 0x7e, 0x3d, 0x8d, 0xc5, 0xdc, 0xe4, 0x37, 0xbd, 0x5d, 0x75, 0xf5, 0xd4, 0xa8, 0x51, 0x4f, 0x08, 0x46, 0x6f, 0x6e, + 0x86, 0xdd, 0x1a, 0x3f, 0xcf, 0x4a, 0x40, 0x23, 0x9b, 0xbd, 0x7a, 0x03, 0x05, 0xb9, 0xae, 0xd6, 0xcf, 0x63, 0x59, + 0x65, 0x5c, 0x7c, 0x47, 0x00, 0x5e, 0x1a, 0x1f, 0x12, 0x55, 0xaa, 0x65, 0x65, 0x88, 0x9a, 0x04, 0x10, 0x1c, 0xfe, + 0xa0, 0x7b, 0x73, 0x69, 0x3f, 0xc5, 0x6d, 0x56, 0xe4, 0xb5, 0x15, 0x41, 0x07, 0x19, 0x6a, 0xba, 0x32, 0xb8, 0x81, + 0x0e, 0x0f, 0xa7, 0xe8, 0x7f, 0x15, 0x7f, 0x58, 0xb1, 0x7f, 0xd2, 0x4d, 0x09, 0xe5, 0x53, 0x33, 0x3b, 0xf1, 0x64, + 0xcf, 0x14, 0xa9, 0x59, 0x84, 0x9a, 0x55, 0x6b, 0x06, 0xcb, 0x86, 0xda, 0x7d, 0x0d, 0x09, 0x5b, 0x04, 0x29, 0xa6, + 0x60, 0xdc, 0xd8, 0x9d, 0x11, 0x70, 0xc4, 0x39, 0x83, 0x72, 0xe8, 0x14, 0x65, 0x7e, 0x33, 0x5c, 0x36, 0x4e, 0xdd, + 0xf4, 0x06, 0x05, 0x7e, 0x18, 0xf0, 0xb9, 0xbc, 0xb5, 0x20, 0xcf, 0x1e, 0x65, 0xc5, 0x74, 0x16, 0xfb, 0x56, 0x02, + 0x31, 0x51, 0xb4, 0x03, 0x5b, 0x5e, 0xf1, 0xf2, 0x74, 0x66, 0xb5, 0x4f, 0x3a, 0xd7, 0x1d, 0xc2, 0xfd, 0x21, 0x71, + 0x1d, 0x84, 0x5e, 0xa7, 0x1c, 0x36, 0x79, 0x3d, 0x29, 0x61, 0xb7, 0x28, 0xbb, 0x2e, 0x16, 0xd3, 0x19, 0x0a, 0xbd, + 0x05, 0xf6, 0xbb, 0xdf, 0x7a, 0xfe, 0xa4, 0x72, 0x8c, 0xeb, 0xc3, 0xe5, 0x24, 0x86, 0xf1, 0xb5, 0xd4, 0x10, 0x2d, + 0x5b, 0x4a, 0xf7, 0x58, 0xdb, 0xb0, 0x80, 0xad, 0xd9, 0xfb, 0x47, 0x22, 0xa5, 0x89, 0x32, 0x15, 0xa7, 0x7d, 0xa1, + 0x32, 0x6e, 0xac, 0x3b, 0xab, 0x77, 0xb5, 0x16, 0x1f, 0xad, 0xce, 0x46, 0x1b, 0xa7, 0x12, 0xec, 0xbd, 0xa1, 0xbb, + 0xe8, 0x0b, 0xa6, 0x6c, 0xa1, 0xef, 0xe0, 0xdd, 0x06, 0x6d, 0x31, 0x3e, 0x63, 0x68, 0x9a, 0xdd, 0x79, 0xe0, 0xc5, + 0x67, 0x59, 0x74, 0xb9, 0x68, 0x3e, 0xcd, 0x1c, 0x69, 0xd4, 0xfd, 0x7f, 0x79, 0x6b, 0xa5, 0x0c, 0x77, 0x79, 0x42, + 0x86, 0x9d, 0xdc, 0xaf, 0x4b, 0x56, 0x01, 0xf9, 0x18, 0x5b, 0xe9, 0x79, 0x65, 0x97, 0x44, 0xa1, 0xa3, 0x38, 0xd3, + 0x7f, 0xf8, 0xca, 0x5d, 0xed, 0x3b, 0x6d, 0xfa, 0xd1, 0x65, 0xc9, 0x5f, 0x59, 0x4e, 0x8a, 0x36, 0x4f, 0x88, 0x4c, + 0xfe, 0x4f, 0x24, 0x25, 0x47, 0x06, 0xe2, 0xd1, 0x01, 0x14, 0x30, 0x53, 0x27, 0x93, 0xd3, 0x62, 0x70, 0x02, 0x22, + 0x4b, 0x34, 0x87, 0x33, 0x80, 0x49, 0x5a, 0x80, 0x09, 0xcf, 0x6b, 0xb5, 0xef, 0x31, 0x35, 0x8f, 0xbf, 0xcc, 0xa3, + 0x19, 0x8a, 0x33, 0x87, 0x16, 0x4d, 0x40, 0x32, 0x92, 0x30, 0xac, 0xb5, 0xed, 0x9c, 0x9f, 0x6c, 0x27, 0x78, 0x42, + 0xbd, 0x3f, 0xe0, 0x96, 0x43, 0x70, 0xb9, 0x13, 0xa5, 0xa8, 0xee, 0x93, 0x2f, 0x5b, 0xbd, 0x39, 0xe4, 0x3a, 0xeb, + 0xa1, 0x1e, 0x19, 0x28, 0x6e, 0xdb, 0xd9, 0x24, 0xfd, 0xf5, 0x8a, 0x7f, 0xfc, 0x65, 0xa2, 0x8b, 0x8a, 0x66, 0x0d, + 0x1a, 0x28, 0x00, 0xb7, 0x31, 0xe7, 0x7b, 0x1d, 0xb7, 0xb6, 0x83, 0xb9, 0x0d, 0x70, 0xb7, 0x51, 0x28, 0x06, 0x73, + 0x3f, 0x4f, 0x18, 0x10, 0xcc, 0x6b, 0x4f, 0x14, 0x20, 0xd2, 0x83, 0xfb, 0xe4, 0x54, 0x72, 0x99, 0x8d, 0x20, 0x58, + 0xc3, 0x2c, 0xe8, 0x76, 0xd7, 0xac, 0xcb, 0x8c, 0x3f, 0xf9, 0x21, 0xc3, 0x35, 0xd0, 0x3f, 0x99, 0x28, 0xe9, 0xdc, + 0x90, 0x50, 0xd1, 0x83, 0x78, 0x99, 0x43, 0xe5, 0x79, 0xcf, 0x50, 0x4f, 0xaf, 0x3f, 0xfa, 0xfb, 0xd6, 0xcc, 0xa1, + 0xbc, 0x64, 0x4d, 0xfe, 0xee, 0x31, 0xaf, 0x67, 0x79, 0x45, 0x67, 0xbe, 0x9a, 0x75, 0x56, 0x5c, 0x64, 0x9c, 0x1d, + 0x91, 0x0a, 0x4e, 0xad, 0x68, 0x7d, 0xe2, 0x29, 0x36, 0x8d, 0xdf, 0x1b, 0xa4, 0xce, 0x1e, 0x99, 0x7b, 0x76, 0x50, + 0x51, 0x5a, 0x42, 0x81, 0xf5, 0x22, 0x6a, 0xe0, 0xdb, 0x23, 0x9b, 0x31, 0xd3, 0xe7, 0xa4, 0xc0, 0x8b, 0x96, 0x60, + 0xb3, 0xbc, 0xd4, 0x41, 0x13, 0x2f, 0x4b, 0xe6, 0x8a, 0x13, 0xfe, 0x74, 0x99, 0x29, 0xf6, 0x43, 0x46, 0xea, 0x60, + 0xcf, 0x8b, 0x15, 0x7b, 0x96, 0xcb, 0xa7, 0xcb, 0x87, 0x68, 0x93, 0x7b, 0x8f, 0x88, 0x19, 0xaf, 0x1f, 0x2f, 0xda, + 0xa4, 0x04, 0x94, 0xc8, 0xc8, 0x86, 0x71, 0x1b, 0x09, 0x35, 0x8a, 0xf2, 0xd1, 0x15, 0x28, 0x39, 0xd6, 0xa9, 0x08, + 0x00, 0xf8, 0x63, 0x3a, 0x14, 0x36, 0xf0, 0x60, 0x3e, 0x91, 0x80, 0x32, 0xf2, 0xf4, 0x9d, 0xc9, 0x90, 0x10, 0x1d, + 0x35, 0x33, 0x7c, 0x4f, 0x18, 0xab, 0x67, 0x1e, 0x1d, 0x1f, 0x45, 0x1d, 0x6e, 0x84, 0x81, 0xc4, 0xb2, 0x6c, 0xb2, + 0x9b, 0xb7, 0x6e, 0x2b, 0x7c, 0x57, 0xac, 0x40, 0x9a, 0x02, 0x34, 0x2f, 0xe3, 0x46, 0xc0, 0x69, 0x18, 0xb3, 0x2f, + 0x03, 0xd4, 0x58, 0xc1, 0x58, 0x7e, 0xb5, 0xb2, 0xe1, 0xd9, 0x24, 0xef, 0x7e, 0x74, 0x99, 0x0b, 0x84, 0xbc, 0x58, + 0x60, 0x5b, 0x12, 0x75, 0xe2, 0x37, 0x83, 0xdf, 0xd3, 0xef, 0xd5, 0xf4, 0xd1, 0xc6, 0x88, 0x36, 0x3a, 0xcb, 0x4d, + 0x0f, 0x7a, 0xb4, 0x5b, 0xb0, 0x6a, 0x21, 0x52, 0xcd, 0xf1, 0x30, 0x03, 0x1b, 0xd1, 0x97, 0xd8, 0x60, 0xf5, 0x83, + 0x8d, 0x02, 0xc9, 0xc2, 0x90, 0x6d, 0x9b, 0x3d, 0x36, 0x30, 0x04, 0xe5, 0x59, 0x35, 0x05, 0x58, 0x23, 0xb6, 0xab, + 0x14, 0x46, 0x93, 0x7f, 0xd5, 0x16, 0xfd, 0x27, 0xff, 0x53, 0xac, 0xf7, 0x4c, 0x80, 0x64, 0x7b, 0x38, 0x9f, 0x9d, + 0xa6, 0x05, 0x33, 0x78, 0x14, 0x84, 0xf6, 0x60, 0x4a, 0xcd, 0x49, 0x24, 0x06, 0x25, 0x17, 0x22, 0xfb, 0x93, 0xea, + 0x2d, 0xc7, 0x67, 0x1e, 0x2a, 0xbf, 0xb9, 0x93, 0xe2, 0xa4, 0xd3, 0xea, 0x52, 0x19, 0xc1, 0x5d, 0x81, 0x13, 0x94, + 0x60, 0x36, 0xa0, 0x7f, 0xf2, 0xdb, 0x4d, 0x48, 0xa2, 0x4f, 0x5d, 0x60, 0x28, 0x63, 0xf6, 0x8c, 0xc8, 0xcc, 0xc2, + 0x23, 0x5a, 0x85, 0x28, 0xc6, 0x05, 0x72, 0xc0, 0x6c, 0x3f, 0x1b, 0x59, 0xb0, 0xd5, 0xb0, 0x9f, 0xfb, 0x46, 0xb4, + 0x0f, 0x61, 0x32, 0x62, 0x73, 0xe2, 0x2d, 0xc9, 0x03, 0x68, 0x88, 0x1e, 0xe6, 0x42, 0xe3, 0x82, 0x97, 0xae, 0x52, + 0xa3, 0x14, 0xe8, 0x26, 0x1e, 0xf5, 0x76, 0x68, 0xd4, 0x6a, 0x79, 0x33, 0x46, 0x17, 0xc0, 0x21, 0xaf, 0xf7, 0x4f, + 0xf0, 0xd4, 0x63, 0x86, 0xd8, 0x8b, 0x37, 0x1c, 0x58, 0xad, 0x71, 0xb1, 0x9d, 0x13, 0x37, 0x45, 0xc1, 0xc5, 0x99, + 0x4a, 0x7f, 0xb7, 0x85, 0xff, 0xad, 0xbc, 0xbb, 0x2a, 0xb2, 0x26, 0x28, 0x3f, 0x08, 0xce, 0xdc, 0xf3, 0x02, 0x3e, + 0x59, 0xe9, 0x74, 0xf8, 0x8d, 0xd2, 0x7c, 0x70, 0xf3, 0x84, 0xd1, 0x16, 0x6e, 0xaf, 0x30, 0x57, 0xe1, 0x0a, 0x96, + 0x11, 0xda, 0x67, 0xdc, 0x7a, 0xfc, 0xb9, 0x68, 0x8c, 0x29, 0x47, 0xe7, 0x1c, 0xe4, 0x67, 0x84, 0x04, 0xd3, 0xc0, + 0x26, 0x3d, 0xda, 0x61, 0x99, 0x16, 0x48, 0x09, 0x42, 0x4e, 0x2a, 0xba, 0x1f, 0xc3, 0x50, 0x89, 0xcd, 0x24, 0x24, + 0xad, 0x2a, 0x76, 0xe8, 0xc4, 0x29, 0x37, 0x1b, 0xa6, 0x58, 0x23, 0x7c, 0xba, 0xe9, 0x67, 0x88, 0x92, 0xc8, 0x7b, + 0x2e, 0x6e, 0x46, 0x1d, 0xbc, 0x22, 0x53, 0xc5, 0xd2, 0x57, 0x9e, 0x70, 0xeb, 0xaf, 0xb5, 0x0f, 0x90, 0xef, 0x10, + 0x0a, 0x7a, 0x5c, 0xe5, 0x5f, 0xce, 0x61, 0x56, 0xf2, 0x12, 0xae, 0xf0, 0x53, 0x1c, 0xca, 0x5c, 0x54, 0xd0, 0xe3, + 0xb9, 0x08, 0xf1, 0x96, 0xc3, 0x5b, 0x05, 0x9f, 0x44, 0x5f, 0x24, 0xc2, 0x7d, 0xcb, 0xce, 0xa6, 0xcf, 0x4a, 0x78, + 0xfd, 0xb9, 0x39, 0x29, 0x05, 0xd7, 0x81, 0x46, 0xcf, 0x61, 0xe0, 0x65, 0xd0, 0x62, 0xec, 0xd4, 0xc0, 0x3d, 0x91, + 0xec, 0x5b, 0x7f, 0x60, 0x49, 0xf5, 0xd3, 0x0f, 0x1a, 0x10, 0xcf, 0xd4, 0x7f, 0x3b, 0x30, 0xf1, 0x58, 0xfe, 0x91, + 0xe7, 0x3f, 0x93, 0x44, 0xd5, 0xc5, 0x03, 0x6c, 0x9d, 0x64, 0x0b, 0x05, 0x14, 0x1d, 0x1e, 0x10, 0xb0, 0x68, 0x6f, + 0x57, 0x69, 0x99, 0x9d, 0x30, 0x87, 0x3c, 0xdd, 0x55, 0xaf, 0xb3, 0x04, 0xa7, 0xaf, 0xd6, 0xb3, 0x15, 0xe8, 0xb4, + 0xb0, 0x00, 0x94, 0x38, 0xb3, 0x44, 0x75, 0xc6, 0xc1, 0xa9, 0xc5, 0x67, 0xfc, 0xaf, 0x57, 0x2a, 0x61, 0xec, 0xc1, + 0xc3, 0x41, 0x75, 0xa1, 0x82, 0xfc, 0xec, 0x85, 0xa6, 0x34, 0x0c, 0x20, 0xe1, 0x9c, 0xc6, 0x21, 0x59, 0xc6, 0x16, + 0x8f, 0xbc, 0x32, 0x15, 0x3a, 0x81, 0x75, 0xa7, 0x4f, 0xa7, 0x83, 0x60, 0x5c, 0x62, 0x85, 0xc1, 0x6b, 0x2e, 0x0c, + 0x47, 0x5a, 0x2e, 0xa7, 0xf8, 0x2b, 0x4d, 0xd4, 0xb5, 0xc8, 0x26, 0xf3, 0x1a, 0x57, 0x0d, 0xc4, 0x99, 0x76, 0x41, + 0x86, 0xe5, 0x53, 0xe4, 0xd6, 0x62, 0xb9, 0xf6, 0x5b, 0x9f, 0x57, 0x18, 0x86, 0xca, 0xcd, 0xaf, 0xf7, 0xf4, 0xcd, + 0x1d, 0x89, 0x53, 0x2f, 0xde, 0xa2, 0x40, 0xd3, 0x89, 0x5e, 0x0c, 0x35, 0xc2, 0xd3, 0x71, 0x17, 0x91, 0x61, 0x34, + 0xe0, 0xf4, 0x6d, 0x55, 0x33, 0x66, 0xd2, 0x0e, 0xa0, 0x9f, 0x0b, 0xea, 0x1c, 0x00, 0x9a, 0x22, 0x94, 0x1d, 0x08, + 0x57, 0xa1, 0x5a, 0xaf, 0x97, 0x95, 0x36, 0x36, 0x96, 0x07, 0x0a, 0x21, 0x30, 0x2b, 0x5e, 0x52, 0x28, 0xb9, 0x42, + 0x20, 0x2f, 0xb6, 0xa9, 0x4a, 0x65, 0xa6, 0x65, 0xb3, 0x76, 0xd7, 0x15, 0xed, 0x00, 0xa2, 0x26, 0x6d, 0x64, 0x32, + 0x81, 0x0d, 0x15, 0xd2, 0x14, 0x17, 0x49, 0xad, 0x04, 0x5c, 0xf3, 0x61, 0x0a, 0xa6, 0x11, 0x38, 0x3b, 0x80, 0x16, + 0xcc, 0xe1, 0x5e, 0x33, 0x64, 0x9a, 0x3c, 0xa7, 0x7d, 0x46, 0x8f, 0xb6, 0x5a, 0x63, 0xab, 0x5a, 0xb5, 0x8b, 0xfb, + 0xc9, 0x3a, 0x60, 0x62, 0xc0, 0x6a, 0x7b, 0xfc, 0x6f, 0x85, 0x74, 0xe6, 0x63, 0x21, 0x95, 0xfe, 0x6f, 0x46, 0xe7, + 0x62, 0xde, 0x3c, 0x3f, 0x8c, 0x5c, 0x61, 0x4c, 0x85, 0x3c, 0xc6, 0x49, 0x78, 0xb1, 0x1d, 0x5e, 0x34, 0x06, 0xb5, + 0x1f, 0x30, 0x18, 0x72, 0xaa, 0x63, 0xef, 0x7d, 0x10, 0x92, 0x7d, 0x31, 0xb7, 0x68, 0xac, 0x4e, 0x69, 0x51, 0xac, + 0xfb, 0x00, 0x32, 0x28, 0x8a, 0xfd, 0xff, 0xb8, 0x75, 0x91, 0x85, 0xe6, 0x0f, 0xe4, 0x25, 0x2e, 0x79, 0x98, 0xfe, + 0xf8, 0x5d, 0x50, 0xac, 0x4f, 0x1b, 0xf1, 0x12, 0xcd, 0x95, 0x83, 0x7f, 0xd3, 0x65, 0x8b, 0xea, 0x2e, 0xe5, 0xe1, + 0xde, 0x81, 0x31, 0x8d, 0x6f, 0x6e, 0xbe, 0x8c, 0x0b, 0x6b, 0x9c, 0xbb, 0x19, 0xef, 0x70, 0x13, 0xbb, 0xde, 0x56, + 0x56, 0x6c, 0x17, 0x99, 0xa2, 0xa2, 0xa9, 0xd1, 0x47, 0x33, 0x30, 0x76, 0x68, 0x40, 0xfb, 0xb7, 0x18, 0x32, 0x58, + 0x3c, 0xac, 0xcd, 0x85, 0x68, 0x79, 0x9d, 0xcb, 0x1d, 0x05, 0xe7, 0x64, 0xc4, 0x91, 0x04, 0x69, 0xd2, 0x7d, 0xc7, + 0xc9, 0x83, 0x3a, 0xa8, 0x1a, 0x71, 0xa7, 0x9a, 0xec, 0x57, 0xc2, 0xff, 0x21, 0x1f, 0xd7, 0x9d, 0xb6, 0x72, 0x0e, + 0x08, 0xf1, 0x59, 0xe7, 0xcd, 0x09, 0x91, 0x51, 0xdb, 0x46, 0x6d, 0x25, 0xcd, 0xc8, 0xaf, 0x10, 0x89, 0xfa, 0x57, + 0x8c, 0x02, 0x53, 0x7c, 0x06, 0x30, 0xb0, 0x4d, 0x82, 0xd5, 0x6f, 0xd6, 0x0d, 0xd9, 0x52, 0x40, 0xe3, 0x97, 0xb3, + 0x6d, 0x3e, 0xb1, 0x71, 0x3b, 0xfa, 0x05, 0x51, 0xdb, 0x5a, 0xd1, 0x04, 0xd7, 0xdd, 0x0b, 0xab, 0x37, 0xe2, 0xf7, + 0xd4, 0xdb, 0x23, 0xc8, 0x0d, 0xe4, 0x93, 0x74, 0xbf, 0x73, 0xa6, 0x0f, 0xd8, 0x83, 0x31, 0x8e, 0x31, 0xd8, 0x15, + 0xf3, 0xcc, 0xe8, 0x4d, 0x55, 0xd9, 0x04, 0x7a, 0x77, 0xcb, 0x51, 0x71, 0x8f, 0xdf, 0xd2, 0x2f, 0xde, 0x30, 0xc3, + 0xe8, 0x3e, 0x5f, 0x40, 0xd9, 0xa2, 0x1d, 0x57, 0x1a, 0xc9, 0x65, 0xb4, 0x4d, 0xe5, 0x88, 0x12, 0x58, 0x50, 0x92, + 0x1a, 0x5d, 0xde, 0xdc, 0xb2, 0x79, 0x71, 0x1d, 0x4d, 0x28, 0xb7, 0xfe, 0x74, 0xe4, 0x73, 0x3d, 0x38, 0x2a, 0x6f, + 0x43, 0x04, 0xa6, 0x89, 0x36, 0x2c, 0xe0, 0x30, 0xd3, 0xe6, 0xa5, 0x08, 0x02, 0xf0, 0x6e, 0xf0, 0x67, 0x9b, 0x81, + 0x22, 0x17, 0x10, 0x79, 0xe7, 0x2d, 0x58, 0xa0, 0x1b, 0x3c, 0x05, 0xfa, 0x38, 0x36, 0xfc, 0x77, 0xc1, 0xca, 0xd8, + 0x90, 0x2c, 0x61, 0x7c, 0xaf, 0x73, 0x22, 0x39, 0x49, 0x5d, 0x24, 0xad, 0x9f, 0xc2, 0x33, 0xb5, 0x8d, 0x5b, 0xf3, + 0x17, 0xe9, 0x27, 0xd1, 0x50, 0x79, 0x01, 0xf3, 0x35, 0xaa, 0xb3, 0xcb, 0xfc, 0x85, 0x79, 0x4e, 0x7a, 0x66, 0x5e, + 0xa3, 0xd5, 0x1a, 0xf0, 0xc0, 0xd2, 0x8a, 0xb0, 0x94, 0x59, 0x32, 0xe7, 0x32, 0x00, 0xf0, 0xb5, 0xf1, 0x79, 0x6d, + 0x08, 0xf1, 0x89, 0x5d, 0xdf, 0x15, 0x84, 0xca, 0x54, 0xd3, 0xae, 0x33, 0xf7, 0xc9, 0x2a, 0x84, 0xa5, 0xda, 0x76, + 0xc5, 0x6d, 0xa6, 0xb9, 0xad, 0x0d, 0xcf, 0x3d, 0x5f, 0x37, 0x05, 0xa6, 0xe8, 0x0c, 0xfa, 0x3b, 0xdb, 0x88, 0x53, + 0x04, 0x21, 0x62, 0x06, 0x1f, 0xb0, 0x36, 0x82, 0x6c, 0xca, 0x89, 0xfe, 0x6c, 0x17, 0xd4, 0x34, 0xbd, 0x4c, 0x55, + 0x85, 0xcb, 0x39, 0x26, 0x13, 0x9b, 0xb3, 0x01, 0x8b, 0x39, 0x78, 0xf0, 0xf0, 0x36, 0xb7, 0x65, 0xd9, 0x1b, 0x11, + 0xac, 0x06, 0x2d, 0x9c, 0x3b, 0x58, 0x2a, 0xf4, 0x9d, 0xcc, 0x7a, 0x57, 0x07, 0x37, 0xb3, 0xdf, 0xa4, 0xdd, 0x1f, + 0x39, 0xfa, 0xaa, 0xd2, 0xb8, 0x03, 0xdb, 0x58, 0x02, 0x1b, 0x1e, 0x23, 0x52, 0x0e, 0x89, 0xea, 0x53, 0x1f, 0x54, + 0x8f, 0x6a, 0x4c, 0x72, 0x1c, 0x48, 0x87, 0x89, 0x2b, 0x12, 0x7b, 0x93, 0x16, 0x62, 0x57, 0x2a, 0xa4, 0xa7, 0xb3, + 0x90, 0xaf, 0x25, 0x37, 0x5d, 0x27, 0x89, 0x6c, 0x51, 0xfb, 0x90, 0x57, 0x2d, 0xa9, 0x53, 0x83, 0xf2, 0x78, 0xcc, + 0xd1, 0x8f, 0x77, 0x5b, 0xf9, 0x4a, 0x6d, 0x1d, 0xe7, 0x24, 0xf8, 0x1c, 0xc7, 0x8b, 0x86, 0x7f, 0x2e, 0xca, 0x1b, + 0x2d, 0x3c, 0x8f, 0x2b, 0x3f, 0xec, 0xe4, 0xf5, 0x2b, 0x34, 0x4c, 0xc3, 0x51, 0xeb, 0xb6, 0xbc, 0xe2, 0x70, 0xef, + 0x76, 0x62, 0xb1, 0x84, 0xf5, 0x31, 0x2e, 0x97, 0x3c, 0x8d, 0xaa, 0xa5, 0xa3, 0x3f, 0xdd, 0x01, 0xb7, 0xe4, 0x9d, + 0x00, 0x98, 0xe8, 0xd0, 0x47, 0x58, 0xd0, 0x5e, 0x46, 0x8c, 0x10, 0x7b, 0xc1, 0xe4, 0x30, 0x64, 0xef, 0xfe, 0x0f, + 0xbb, 0x1e, 0x86, 0x6c, 0x49, 0xb2, 0xbb, 0x37, 0x23, 0x7c, 0xa1, 0x9e, 0x1e, 0x58, 0x8d, 0xc3, 0x35, 0x79, 0xb1, + 0x0d, 0x51, 0xec, 0x25, 0xdc, 0x30, 0x6a, 0x4b, 0x31, 0xb7, 0x60, 0x8d, 0x71, 0x48, 0xb1, 0x35, 0xca, 0xa8, 0x61, + 0x73, 0x68, 0x73, 0x28, 0xed, 0xbd, 0xe2, 0xbb, 0xfc, 0x1d, 0xe2, 0x83, 0x6f, 0x6d, 0x8f, 0xa2, 0xee, 0x9d, 0x7b, + 0xcb, 0xbc, 0x48, 0x57, 0xb2, 0xfe, 0xb9, 0x9d, 0xd8, 0x50, 0xdc, 0x4d, 0x37, 0x63, 0x3d, 0x71, 0x90, 0x5d, 0x9a, + 0x7c, 0x20, 0xa8, 0xa2, 0x64, 0xa5, 0xd5, 0xff, 0xec, 0xf6, 0xdf, 0x72, 0x1e, 0x9a, 0x68, 0x74, 0x6c, 0x3b, 0xb4, + 0x46, 0xef, 0xe1, 0xd7, 0xf8, 0x18, 0xab, 0x05, 0x24, 0x87, 0xb9, 0x4e, 0x94, 0xba, 0x19, 0x11, 0x3a, 0x71, 0xe3, + 0x05, 0xa2, 0xde, 0x76, 0x3d, 0xd3, 0xb9, 0xf4, 0xfe, 0x2e, 0x03, 0x34, 0x35, 0x84, 0xe0, 0x21, 0x24, 0xe7, 0x37, + 0xe1, 0xcd, 0xe8, 0x44, 0x7c, 0xc3, 0x74, 0x39, 0x43, 0xee, 0xe1, 0x0b, 0xb4, 0xee, 0x24, 0x58, 0x38, 0xdc, 0x10, + 0x52, 0xa4, 0x82, 0x00, 0xd9, 0x3e, 0x06, 0xb0, 0x30, 0xc9, 0x5e, 0x34, 0x19, 0x0d, 0x88, 0x6c, 0xd6, 0xb6, 0x84, + 0x39, 0x36, 0x53, 0x80, 0x16, 0x6c, 0xcd, 0x2f, 0x81, 0xb3, 0xa1, 0x2d, 0xde, 0xd2, 0xff, 0xe4, 0x35, 0x11, 0x60, + 0x4c, 0x53, 0x9b, 0x66, 0xd6, 0x2b, 0xab, 0x85, 0xa3, 0x28, 0x59, 0x2c, 0x90, 0x03, 0xd7, 0x0d, 0xa5, 0xb1, 0x35, + 0x56, 0x97, 0x34, 0xa0, 0xe5, 0xa2, 0xba, 0x20, 0x10, 0x12, 0x43, 0xcc, 0xab, 0x86, 0x42, 0x4a, 0x12, 0xaa, 0xb9, + 0x75, 0x27, 0xb6, 0x09, 0x0a, 0xb3, 0xe3, 0xce, 0xe4, 0xa1, 0x9f, 0xe1, 0xf8, 0xe3, 0x8d, 0xd9, 0x41, 0xa0, 0x70, + 0xc5, 0x4b, 0x19, 0x0d, 0x2a, 0xcb, 0x66, 0x3d, 0xf4, 0xca, 0xcd, 0x02, 0xda, 0x9d, 0xca, 0x32, 0xa3, 0xda, 0xa9, + 0x9e, 0x09, 0x4e, 0x6f, 0x0d, 0xd0, 0x88, 0x48, 0x80, 0x09, 0xfc, 0xa8, 0xbf, 0x34, 0x2a, 0x16, 0x18, 0x6b, 0x2b, + 0x8f, 0x7a, 0x7d, 0x8f, 0x33, 0x99, 0xce, 0x03, 0x6c, 0x9c, 0xb3, 0x68, 0x55, 0x23, 0x9e, 0x90, 0xa0, 0x4f, 0x72, + 0xb2, 0x73, 0x56, 0x2d, 0xe3, 0xeb, 0xe4, 0x82, 0x2f, 0xd8, 0x1d, 0x7f, 0xad, 0x11, 0x94, 0xe3, 0x5f, 0x5c, 0xbc, + 0xc5, 0x6b, 0xe1, 0x14, 0xd7, 0x23, 0xe6, 0x8b, 0x32, 0x2f, 0x7f, 0x78, 0x61, 0xe6, 0xf4, 0xef, 0xaf, 0x30, 0x01, + 0x55, 0xfe, 0x62, 0x89, 0x04, 0x52, 0x79, 0x78, 0xeb, 0x8d, 0xe0, 0x4a, 0x66, 0x14, 0x8d, 0x59, 0x3b, 0x6e, 0x09, + 0x3b, 0x58, 0x14, 0x47, 0x10, 0x2a, 0xfe, 0xf9, 0x0c, 0x20, 0x71, 0x16, 0xb4, 0xcc, 0x68, 0xd0, 0x88, 0xf6, 0xc0, + 0x9d, 0x15, 0x36, 0xe6, 0x85, 0x5c, 0x97, 0x6f, 0x1f, 0x56, 0x70, 0x90, 0x25, 0x24, 0xc1, 0xc3, 0x7a, 0xfb, 0xa6, + 0xca, 0x74, 0xe9, 0x61, 0xea, 0x75, 0xc7, 0xef, 0x99, 0x09, 0x08, 0x69, 0xf6, 0x10, 0xd9, 0xdc, 0x8d, 0xc4, 0xf4, + 0xc6, 0x53, 0xdb, 0x8e, 0x98, 0x8f, 0xed, 0x44, 0xe4, 0x4a, 0x1d, 0xdb, 0xe6, 0x21, 0x32, 0xc2, 0x0a, 0x23, 0x09, + 0x2e, 0xbf, 0x8c, 0xc8, 0x4d, 0x16, 0x34, 0xf6, 0x31, 0xba, 0x94, 0xc5, 0x24, 0xfb, 0x08, 0xfe, 0x52, 0xd6, 0xfa, + 0x97, 0xa8, 0x75, 0xf6, 0x04, 0x7e, 0xc5, 0xd0, 0xde, 0x43, 0x68, 0xac, 0xb3, 0xe0, 0x5d, 0x0b, 0x1e, 0x29, 0xa0, + 0xdc, 0x87, 0x89, 0x84, 0x50, 0x5c, 0x1f, 0x87, 0x5d, 0xb9, 0x6b, 0x89, 0x11, 0xe1, 0xa3, 0xa4, 0x57, 0x6a, 0x93, + 0x31, 0x5c, 0x81, 0x00, 0x2e, 0xcf, 0xf5, 0x78, 0x3e, 0xc3, 0x6c, 0xaf, 0x34, 0x92, 0xd0, 0x77, 0xc3, 0x8c, 0x97, + 0x9b, 0x6e, 0x51, 0x59, 0xb4, 0x79, 0x2b, 0x85, 0xbd, 0x2e, 0x10, 0x99, 0x11, 0x22, 0xe6, 0x96, 0xdf, 0x14, 0xa4, + 0x93, 0xed, 0x7c, 0x83, 0x3e, 0x36, 0x30, 0x9c, 0xc1, 0x4a, 0x57, 0xb5, 0xb5, 0x73, 0x2b, 0xb1, 0xfe, 0x9d, 0x15, + 0x13, 0xf8, 0xf9, 0x62, 0x41, 0x42, 0x40, 0xc2, 0x42, 0xcf, 0x3c, 0x98, 0xf5, 0x70, 0x92, 0x4e, 0x79, 0xf6, 0x12, + 0x13, 0x2e, 0x64, 0xe8, 0x70, 0xfc, 0xa0, 0xa5, 0xb9, 0xa0, 0x39, 0x7e, 0x3e, 0xd3, 0x52, 0xf9, 0x5a, 0x49, 0x93, + 0x2c, 0x58, 0xe5, 0x85, 0xd3, 0xe5, 0x23, 0x43, 0x14, 0x9f, 0x6a, 0xd7, 0x7d, 0x87, 0x9b, 0xcf, 0xa4, 0x68, 0x24, + 0x95, 0x76, 0x22, 0x50, 0x69, 0xc8, 0xe4, 0xed, 0x5e, 0x00, 0x62, 0x1b, 0xa2, 0x2f, 0x9a, 0x8d, 0xcc, 0x54, 0xa6, + 0xa3, 0xab, 0xe5, 0x21, 0x1c, 0xdb, 0xc3, 0x9b, 0xa1, 0x61, 0x08, 0x78, 0x7d, 0x5a, 0xb3, 0x7f, 0x1d, 0x75, 0xa8, + 0x68, 0x62, 0x54, 0xc4, 0xcd, 0x05, 0x93, 0x25, 0x2b, 0xa6, 0x21, 0x41, 0x38, 0x69, 0xc0, 0xe9, 0x6c, 0xc6, 0xd8, + 0x20, 0x79, 0x81, 0x49, 0x26, 0xf6, 0x04, 0x5a, 0x9a, 0x80, 0x79, 0x45, 0xd9, 0x79, 0xb4, 0x19, 0xdb, 0x19, 0xa1, + 0x9c, 0x39, 0x89, 0x8a, 0xf8, 0x67, 0xee, 0x49, 0x2b, 0xe0, 0x3e, 0x63, 0xba, 0xeb, 0x35, 0x9e, 0x71, 0x04, 0x45, + 0xbf, 0x6d, 0x9b, 0xff, 0x65, 0x18, 0x84, 0xa7, 0xcb, 0x76, 0x0e, 0x50, 0x41, 0x96, 0x10, 0xf0, 0x27, 0x2f, 0xe8, + 0x4b, 0xc0, 0x43, 0x0c, 0xf8, 0x81, 0xbd, 0x7a, 0x6d, 0x05, 0x3a, 0xb8, 0xfa, 0xea, 0xec, 0xf7, 0xdf, 0x32, 0x38, + 0xfc, 0x07, 0x57, 0xda, 0xf7, 0x8f, 0x4f, 0x09, 0x9b, 0x93, 0xa7, 0x78, 0x3a, 0x3a, 0xc7, 0xe1, 0xb6, 0x1e, 0xe5, + 0x74, 0x3b, 0x25, 0x14, 0x5e, 0xf8, 0x40, 0xfc, 0x31, 0x8b, 0xbb, 0x81, 0xa6, 0xf2, 0x71, 0xce, 0x1f, 0xe4, 0x27, + 0xc7, 0x27, 0xe9, 0x3e, 0xcd, 0x73, 0x38, 0xe6, 0x82, 0x6d, 0x0c, 0x83, 0xab, 0xce, 0xce, 0x2e, 0xe5, 0x66, 0x58, + 0x4a, 0x7a, 0xab, 0xdb, 0xbd, 0x8e, 0x51, 0xe9, 0xff, 0x2b, 0x7b, 0x4b, 0x47, 0x38, 0x8c, 0x7f, 0xf8, 0x0c, 0x05, + 0x41, 0xee, 0x14, 0xeb, 0xf4, 0xa2, 0x70, 0x8d, 0x3b, 0x94, 0x6f, 0xad, 0xb6, 0xbe, 0xaa, 0x52, 0x8f, 0xcc, 0x45, + 0x8c, 0xf3, 0x15, 0xf1, 0xb2, 0x9a, 0xbc, 0x6e, 0xd0, 0x6f, 0x4f, 0x94, 0xf9, 0xcf, 0xaf, 0x21, 0xc1, 0x76, 0x74, + 0xbf, 0x86, 0xfb, 0x1d, 0x71, 0x0d, 0x6b, 0xce, 0x91, 0x17, 0x9c, 0x71, 0x5d, 0x3d, 0x6d, 0x93, 0x75, 0x2d, 0x1c, + 0xdb, 0x2e, 0x07, 0x5e, 0xeb, 0x52, 0xe7, 0x10, 0xa5, 0x95, 0x71, 0xcf, 0xe9, 0x5d, 0x97, 0xdf, 0x99, 0xea, 0x18, + 0x76, 0x03, 0x9c, 0x8a, 0x60, 0x40, 0x81, 0x79, 0x1f, 0xd4, 0x9d, 0x0c, 0x21, 0x27, 0xf6, 0xac, 0x81, 0x5c, 0x82, + 0x28, 0x9a, 0x2f, 0x41, 0x00, 0x5a, 0xda, 0x81, 0x97, 0xb5, 0x8a, 0x46, 0x96, 0xac, 0x81, 0xb3, 0xd7, 0xff, 0x23, + 0x06, 0x43, 0x9c, 0x7c, 0x93, 0x80, 0x38, 0xc9, 0x14, 0x89, 0x39, 0x8d, 0x45, 0x9f, 0xb3, 0x8f, 0x72, 0x09, 0xd2, + 0xec, 0x67, 0x60, 0x80, 0x60, 0x1a, 0x8e, 0x63, 0x41, 0xa1, 0x64, 0xbe, 0x2a, 0xfa, 0x69, 0xb3, 0xf8, 0xfc, 0x09, + 0xc6, 0xf6, 0x6f, 0x74, 0xdb, 0xa8, 0xfc, 0x5e, 0x53, 0xc9, 0xed, 0xaf, 0x3c, 0x9f, 0xfe, 0xb6, 0x3a, 0x3c, 0xfd, + 0x44, 0xfd, 0xf8, 0x75, 0xd3, 0x02, 0xef, 0xe4, 0xee, 0xa5, 0x0c, 0x35, 0x3f, 0x5f, 0x67, 0x40, 0x58, 0x18, 0x80, + 0xfa, 0xd1, 0xf1, 0xa1, 0xa4, 0xdd, 0xd6, 0xb3, 0x41, 0x34, 0xb1, 0x8f, 0x71, 0x8b, 0xea, 0xe5, 0xbc, 0xc0, 0x66, + 0x35, 0xae, 0xa1, 0x7b, 0x5e, 0x68, 0xcd, 0x33, 0x61, 0x96, 0x0a, 0x4a, 0xe1, 0x64, 0x0a, 0xb8, 0x01, 0x5c, 0x57, + 0x4e, 0x9b, 0x85, 0x17, 0xbd, 0x09, 0x4f, 0x12, 0xcc, 0xe8, 0xc0, 0x45, 0xd3, 0x57, 0x4f, 0xed, 0x8b, 0x8e, 0xe1, + 0xcf, 0x44, 0x5d, 0x8d, 0x21, 0xa9, 0x51, 0x8e, 0x49, 0x8b, 0x95, 0x56, 0x68, 0x2d, 0xaf, 0x96, 0xba, 0xdb, 0x39, + 0x42, 0xaf, 0xbc, 0xa0, 0x0c, 0xc0, 0x03, 0x98, 0xf5, 0x92, 0xde, 0xd2, 0x2a, 0xb2, 0x29, 0xfb, 0x84, 0x5c, 0x9b, + 0xc7, 0x13, 0x9c, 0x96, 0x3e, 0xaa, 0x5b, 0xa4, 0x49, 0x6c, 0x85, 0x6b, 0x38, 0x37, 0x59, 0x55, 0xf5, 0xa2, 0xf9, + 0xda, 0x0f, 0x30, 0xa7, 0x05, 0xfb, 0x37, 0xf6, 0x45, 0xd3, 0x72, 0x12, 0x68, 0xbb, 0x68, 0x64, 0x0b, 0xca, 0x00, + 0x88, 0xd2, 0x3d, 0xbd, 0x01, 0x07, 0xa2, 0x5d, 0xd3, 0x89, 0xf8, 0x36, 0xb1, 0x1d, 0xce, 0x4d, 0x56, 0xa8, 0x85, + 0x0b, 0x73, 0x34, 0x9b, 0x2e, 0x9c, 0xa8, 0xbd, 0x4b, 0x7b, 0x9e, 0x0d, 0x34, 0x6e, 0xf3, 0x40, 0x21, 0x7d, 0x2f, + 0xf0, 0xa8, 0x41, 0xdc, 0x50, 0xa1, 0x17, 0x21, 0x53, 0x81, 0x6b, 0x0a, 0xb6, 0x21, 0x33, 0xd3, 0x38, 0x00, 0xc8, + 0xde, 0x45, 0xdc, 0x80, 0x83, 0x2b, 0x35, 0x86, 0x8e, 0xad, 0xd7, 0xe4, 0x95, 0x64, 0x82, 0xa0, 0xf2, 0x66, 0x89, + 0xcd, 0x58, 0x72, 0x10, 0x95, 0x6f, 0x70, 0xb3, 0x73, 0x27, 0x64, 0xf6, 0x3b, 0x9d, 0x21, 0x4c, 0x59, 0x59, 0xed, + 0x90, 0x9b, 0x11, 0x2f, 0x14, 0x98, 0x5a, 0xb4, 0x20, 0x22, 0x19, 0xb1, 0xaa, 0x1b, 0xbf, 0xf3, 0x76, 0x94, 0x9b, + 0x89, 0x6d, 0xb1, 0x5e, 0xf1, 0x8c, 0x60, 0xbd, 0x83, 0xb5, 0x73, 0xf4, 0x6a, 0x67, 0x64, 0xae, 0xf0, 0x62, 0x98, + 0xdc, 0xae, 0xe7, 0x83, 0x61, 0x44, 0x7d, 0xf9, 0x3f, 0xdb, 0x98, 0x55, 0xe5, 0x34, 0x1a, 0x43, 0x42, 0x24, 0xc3, + 0x9b, 0x00, 0xc4, 0xf3, 0xac, 0xc9, 0x18, 0xcd, 0xc4, 0x6a, 0xdb, 0x3a, 0x4d, 0xb3, 0x9f, 0x4f, 0x39, 0xfd, 0xde, + 0x48, 0x38, 0xc0, 0xf3, 0xaa, 0x73, 0x23, 0xbb, 0x7e, 0xa0, 0x8b, 0x39, 0xf4, 0x65, 0x26, 0x57, 0xf5, 0x8d, 0xec, + 0x54, 0x23, 0xcc, 0xcc, 0xa0, 0xef, 0x06, 0x25, 0x0f, 0x00, 0xd0, 0x1f, 0xe7, 0xe5, 0xd5, 0xff, 0x35, 0x9a, 0x3b, + 0x61, 0x04, 0x1b, 0x2b, 0x96, 0xe6, 0x38, 0x5e, 0x0e, 0xed, 0x40, 0x45, 0xcf, 0x89, 0xda, 0xd3, 0x88, 0xa4, 0x4b, + 0x6a, 0x0c, 0xe3, 0x89, 0x59, 0x1a, 0x1c, 0xd6, 0x50, 0x82, 0xfd, 0x32, 0xfa, 0xed, 0xda, 0xfb, 0x06, 0x52, 0xfc, + 0x1b, 0xd7, 0xd5, 0xf1, 0xec, 0xa8, 0x32, 0x93, 0x5a, 0xe6, 0x89, 0xdb, 0xe2, 0xaa, 0xae, 0x9a, 0xf9, 0xb4, 0x5d, + 0x32, 0x4d, 0x3b, 0x8f, 0xd9, 0x65, 0xfc, 0x19, 0x4d, 0x24, 0x23, 0x3f, 0xac, 0xc3, 0x00, 0x0d, 0x0c, 0xb4, 0x97, + 0xf8, 0xe9, 0x49, 0xa6, 0xab, 0xb7, 0xba, 0x49, 0xd0, 0xba, 0x5c, 0xa7, 0x1f, 0x48, 0xbd, 0xa0, 0x65, 0xd8, 0x59, + 0x33, 0x78, 0xe6, 0x84, 0xe8, 0x02, 0xe7, 0x27, 0xe6, 0x21, 0x67, 0xd4, 0x34, 0xa0, 0x5f, 0xe7, 0xe5, 0x55, 0x97, + 0xbb, 0xc8, 0xc0, 0xcd, 0x04, 0x76, 0xc8, 0x6e, 0x68, 0x7d, 0xac, 0x89, 0xa1, 0x07, 0xe9, 0xc2, 0xb4, 0x35, 0x8f, + 0x83, 0xd0, 0x14, 0xca, 0xc2, 0x95, 0x29, 0xd9, 0x28, 0x7c, 0x4f, 0x8e, 0xae, 0xe1, 0x82, 0x96, 0xd0, 0xde, 0xfd, + 0xdb, 0x05, 0x74, 0xf7, 0x98, 0x40, 0x95, 0x78, 0x92, 0x16, 0xca, 0xcd, 0x42, 0x79, 0x4e, 0x81, 0x15, 0x2c, 0x32, + 0xcf, 0xaa, 0xe9, 0x48, 0xb3, 0xd6, 0x8f, 0x4e, 0xe7, 0xba, 0xd5, 0x1a, 0xf6, 0x31, 0x65, 0x41, 0xf1, 0x8e, 0x16, + 0xe6, 0x5f, 0x89, 0x92, 0x23, 0x0d, 0xfe, 0x2f, 0x12, 0xab, 0xa6, 0x19, 0x7c, 0x85, 0xf9, 0x7f, 0x54, 0xb7, 0x26, + 0xde, 0x27, 0x70, 0x05, 0xc2, 0x5d, 0xa9, 0xb6, 0x33, 0xee, 0x18, 0x75, 0xb4, 0x0e, 0x3c, 0x75, 0x62, 0xc6, 0xc3, + 0xe3, 0x62, 0x8b, 0xe1, 0xb7, 0xa7, 0x37, 0xe0, 0xee, 0xb3, 0x63, 0xdd, 0xdd, 0xeb, 0x20, 0xa4, 0x57, 0x66, 0x91, + 0xee, 0xaf, 0x5a, 0x4d, 0x35, 0x21, 0xd6, 0xb5, 0x32, 0xf7, 0xc4, 0x98, 0x0d, 0x86, 0x33, 0x62, 0x7c, 0x76, 0x70, + 0xb3, 0x35, 0x72, 0x77, 0xa4, 0x24, 0x8a, 0x1d, 0x5d, 0x4a, 0x78, 0x02, 0x43, 0x36, 0xac, 0xca, 0xcd, 0xaf, 0xb5, + 0x7a, 0xb5, 0xaf, 0x3e, 0xfb, 0x12, 0x93, 0xf4, 0x8b, 0x1f, 0x52, 0xd8, 0xf1, 0x44, 0x64, 0xab, 0xc3, 0x58, 0x07, + 0x74, 0x1f, 0x6a, 0xfd, 0xf2, 0xba, 0xa1, 0xda, 0x0f, 0xf8, 0x6e, 0x9d, 0x95, 0xe5, 0x57, 0x8b, 0xdf, 0xd6, 0xb7, + 0x07, 0xee, 0x25, 0x83, 0xe2, 0x17, 0xf8, 0x2a, 0x22, 0x03, 0xee, 0x97, 0xd5, 0x9a, 0x4c, 0x8a, 0xe3, 0x27, 0x74, + 0x8c, 0x65, 0x8a, 0xf2, 0x48, 0xd3, 0x76, 0xb7, 0xde, 0xa8, 0xd1, 0xb1, 0xe1, 0x93, 0x9d, 0xa9, 0xcb, 0x07, 0x64, + 0x64, 0x08, 0xdb, 0xff, 0x54, 0x5e, 0x9c, 0x0e, 0x88, 0x36, 0xd8, 0xbf, 0x65, 0x86, 0xd0, 0x3a, 0x6c, 0x26, 0xb5, + 0x1a, 0xc2, 0xb1, 0x1f, 0x56, 0x57, 0xff, 0xbf, 0xf8, 0x52, 0x1a, 0x0d, 0x44, 0x6f, 0xd5, 0x5b, 0x02, 0x25, 0xb0, + 0x5e, 0xed, 0x52, 0xea, 0xaf, 0x4e, 0x61, 0x13, 0xe3, 0xb2, 0xe4, 0x75, 0xed, 0xce, 0xd0, 0xfa, 0x49, 0xab, 0x0d, + 0xb9, 0x7f, 0xda, 0xd0, 0xe3, 0x10, 0x23, 0x29, 0x6b, 0x13, 0x63, 0x86, 0x86, 0x10, 0xb3, 0x45, 0x19, 0x83, 0xbb, + 0xfe, 0x89, 0x44, 0x6d, 0x9c, 0x44, 0x68, 0x38, 0xcf, 0xdb, 0x60, 0xed, 0xd5, 0xdd, 0xd2, 0x14, 0x37, 0xc3, 0x95, + 0xa9, 0x4b, 0xc0, 0x7c, 0x62, 0xf0, 0xc5, 0x0e, 0x16, 0x14, 0xf0, 0x12, 0x74, 0x93, 0x71, 0xd3, 0x10, 0x7d, 0xb0, + 0xf1, 0xe6, 0xcf, 0x3d, 0xc7, 0xfc, 0xcc, 0xb7, 0x83, 0x35, 0xa8, 0x9d, 0x00, 0x27, 0x3a, 0xd0, 0xf5, 0x59, 0xb5, + 0xa4, 0xfa, 0xe6, 0xf0, 0xaf, 0x4d, 0xe5, 0x77, 0xc7, 0x86, 0x6f, 0xb5, 0xb9, 0x00, 0xbc, 0x9e, 0x19, 0x76, 0x08, + 0xb4, 0x06, 0x75, 0x4e, 0x61, 0xdc, 0x5d, 0x40, 0xad, 0x7b, 0x8d, 0xeb, 0x9b, 0x22, 0x42, 0x18, 0xb8, 0x2c, 0xa8, + 0xec, 0xf6, 0x1b, 0xcc, 0x5b, 0xdf, 0x17, 0xa8, 0x01, 0xc2, 0x43, 0x19, 0xda, 0x16, 0x19, 0x77, 0xee, 0x0d, 0x36, + 0x4b, 0x58, 0xe7, 0x52, 0x4e, 0xb9, 0xa6, 0x74, 0x1d, 0xaa, 0x8f, 0x9b, 0xa2, 0x97, 0x18, 0x90, 0x23, 0x88, 0xa5, + 0x9e, 0x01, 0xab, 0x86, 0x8b, 0xf4, 0x32, 0x4d, 0xd2, 0x29, 0x5f, 0x06, 0x88, 0xad, 0xeb, 0x44, 0xa3, 0xfb, 0x6c, + 0x29, 0x0f, 0x3d, 0x88, 0x21, 0x24, 0x24, 0x92, 0x52, 0x50, 0x3f, 0x90, 0x49, 0xb9, 0xfc, 0x0f, 0x2b, 0xf1, 0x2a, + 0x4f, 0xc7, 0x5f, 0x9e, 0x4e, 0x56, 0xd5, 0x83, 0x0f, 0x84, 0x1f, 0xe8, 0xbe, 0x75, 0xbc, 0x56, 0x6b, 0xcf, 0x57, + 0x75, 0x93, 0x1c, 0xfd, 0xc4, 0xbe, 0xe4, 0x1f, 0xb4, 0xa5, 0xce, 0x4d, 0x78, 0x16, 0x57, 0xc2, 0x9a, 0xe9, 0xf2, + 0xe5, 0x3d, 0x54, 0x79, 0x24, 0x69, 0x3c, 0x4d, 0x59, 0x6d, 0x1a, 0xef, 0x66, 0x8a, 0x40, 0x1b, 0x75, 0xf4, 0x0a, + 0x4e, 0x39, 0x70, 0x51, 0x87, 0x45, 0x27, 0xcb, 0x3f, 0x0b, 0x96, 0x85, 0x6e, 0x7f, 0x4b, 0x66, 0x1f, 0x27, 0x5f, + 0x6f, 0xa8, 0x5c, 0x38, 0x91, 0x43, 0x13, 0x4b, 0x5b, 0x6d, 0xc7, 0xe0, 0x4c, 0xdd, 0x79, 0x5c, 0x92, 0xe8, 0x3a, + 0x96, 0xe5, 0x79, 0x45, 0xac, 0xe3, 0xd4, 0x7b, 0x3d, 0x88, 0x90, 0x35, 0x2b, 0x7c, 0xd9, 0x7b, 0xfd, 0xd5, 0xad, + 0xd0, 0x99, 0x82, 0xac, 0x65, 0xcf, 0xa2, 0x18, 0xde, 0x85, 0xbc, 0x8a, 0xe8, 0xcb, 0xa5, 0x90, 0x15, 0x42, 0x59, + 0xc0, 0x56, 0xe9, 0x8f, 0xa3, 0x90, 0x3c, 0x3c, 0x4e, 0xf1, 0x62, 0xe6, 0x1c, 0x29, 0x77, 0x09, 0x61, 0x77, 0xc8, + 0xf2, 0x24, 0x92, 0x7a, 0xed, 0x46, 0xb0, 0x29, 0x31, 0xc5, 0xa6, 0x28, 0x72, 0x83, 0x5d, 0x10, 0x1c, 0x75, 0xab, + 0x6f, 0x34, 0x6d, 0x24, 0x0c, 0x12, 0xf9, 0xce, 0x08, 0xe9, 0x53, 0xdf, 0xdc, 0xbd, 0xe9, 0x07, 0x53, 0xc6, 0x20, + 0x02, 0x1e, 0x45, 0xcb, 0x00, 0xda, 0x9e, 0xaf, 0xd2, 0x2e, 0x19, 0x0f, 0x33, 0x18, 0x71, 0x5b, 0x01, 0xb9, 0x2e, + 0x1a, 0xb7, 0xe1, 0x97, 0xf0, 0x24, 0x51, 0x3c, 0x4d, 0x0b, 0x45, 0x23, 0x52, 0x79, 0x36, 0x24, 0x6b, 0x9e, 0x04, + 0x0b, 0x52, 0x4f, 0x1a, 0xcc, 0x86, 0xc1, 0x62, 0x34, 0x92, 0xb0, 0x4f, 0x4d, 0x86, 0xb1, 0x32, 0xec, 0x1c, 0xfd, + 0x4b, 0x9b, 0xd3, 0x16, 0x6b, 0x53, 0x0b, 0xb5, 0x99, 0xd1, 0x83, 0x19, 0x6f, 0x8c, 0xd4, 0xb0, 0x6a, 0x86, 0xf1, + 0x45, 0xa6, 0x76, 0x3a, 0x65, 0x14, 0x25, 0xc6, 0x69, 0x30, 0x77, 0x0c, 0x39, 0x54, 0x3f, 0x60, 0xb3, 0x82, 0xdc, + 0x55, 0x9d, 0xcd, 0xbd, 0x66, 0xdc, 0x5e, 0xd7, 0x8c, 0x3e, 0xf5, 0x4f, 0xb7, 0xfe, 0x73, 0x99, 0xae, 0xdb, 0xb1, + 0xca, 0x5f, 0xfa, 0x79, 0x37, 0x7d, 0x68, 0x31, 0x6f, 0xca, 0xce, 0x30, 0xc3, 0xeb, 0xcf, 0xa7, 0xc5, 0x83, 0xa2, + 0x81, 0xcd, 0x97, 0x6a, 0xe3, 0x70, 0xfd, 0xfb, 0x81, 0xad, 0xb7, 0xbb, 0xb9, 0x93, 0xa4, 0x21, 0xb6, 0x1c, 0x21, + 0x37, 0x82, 0x63, 0x02, 0xfe, 0xe3, 0x04, 0xf9, 0xdf, 0x3b, 0xf4, 0x6d, 0x7b, 0x10, 0x3e, 0xc6, 0xeb, 0x1e, 0x46, + 0x01, 0x73, 0xd6, 0xb2, 0x5e, 0x7d, 0x1a, 0x57, 0x45, 0xfa, 0x2b, 0x82, 0xfa, 0x8d, 0x23, 0xf8, 0x47, 0x57, 0x25, + 0xbf, 0xd3, 0x65, 0xd4, 0xbe, 0xfb, 0xdc, 0x0f, 0xd6, 0xa8, 0x32, 0x8e, 0xee, 0xcd, 0x69, 0x4b, 0x4a, 0x7b, 0x52, + 0xbe, 0xd5, 0x1e, 0x9e, 0xb6, 0x42, 0x9a, 0xb3, 0x79, 0x4f, 0x2e, 0xe7, 0x51, 0x82, 0x6d, 0x39, 0x8e, 0x70, 0x07, + 0xf9, 0xfa, 0x94, 0x51, 0x3a, 0x7a, 0x97, 0xe5, 0xed, 0xde, 0x04, 0x36, 0xf3, 0xf4, 0x04, 0xcc, 0x68, 0xda, 0x95, + 0x7e, 0xbf, 0x15, 0x27, 0xe6, 0xc3, 0xf6, 0x2e, 0xfb, 0x35, 0xae, 0xb4, 0x00, 0x8f, 0x7b, 0x5f, 0xb5, 0xfd, 0x6b, + 0xdb, 0x43, 0xdc, 0x8c, 0x14, 0x83, 0xb7, 0xf9, 0x2a, 0x4b, 0xa2, 0x02, 0x59, 0xf0, 0x1a, 0xf9, 0x20, 0xb6, 0x05, + 0x20, 0x67, 0xb4, 0x46, 0x2d, 0xfd, 0x8e, 0x25, 0xf1, 0x7c, 0x5b, 0x81, 0x9a, 0xf3, 0xec, 0xac, 0xa2, 0x55, 0x77, + 0xc2, 0x57, 0xa7, 0x9c, 0xa5, 0xd9, 0x85, 0xe8, 0x7a, 0xf8, 0xcc, 0x52, 0x54, 0xb2, 0x6c, 0x78, 0x37, 0xc6, 0xaf, + 0xd8, 0x2b, 0xcf, 0x50, 0xf2, 0xae, 0x94, 0x86, 0x42, 0x41, 0xb6, 0x06, 0xf5, 0xad, 0xb3, 0x97, 0x58, 0xdc, 0x68, + 0x79, 0x94, 0xab, 0xf0, 0xc5, 0xdc, 0xc7, 0xed, 0x71, 0x54, 0x15, 0x73, 0x0e, 0x61, 0x4f, 0x02, 0x3a, 0x69, 0x90, + 0x03, 0xa4, 0xd5, 0x65, 0x11, 0x36, 0x48, 0xa1, 0x5e, 0x8e, 0x7b, 0x94, 0x2b, 0xda, 0x8e, 0x05, 0x64, 0x2c, 0xba, + 0xcb, 0x8c, 0x4c, 0xe7, 0xb1, 0x13, 0xdd, 0x87, 0x2e, 0x17, 0x28, 0x30, 0x58, 0x9f, 0xb5, 0xe4, 0x92, 0xc7, 0x8a, + 0xa3, 0xec, 0x4a, 0x0c, 0x94, 0x67, 0x43, 0xd6, 0x6b, 0x7c, 0xc5, 0x02, 0xac, 0xe9, 0x76, 0x8e, 0x85, 0x0a, 0x96, + 0x7d, 0xff, 0x0b, 0x9f, 0x96, 0x8c, 0x9c, 0xca, 0x24, 0x96, 0xa5, 0x0f, 0x73, 0xe3, 0x86, 0xe0, 0x09, 0x41, 0x33, + 0x49, 0xe6, 0x29, 0xa7, 0x14, 0x4a, 0xeb, 0x7f, 0xae, 0x3c, 0x42, 0xd5, 0x6c, 0xdd, 0xf4, 0x96, 0x71, 0x77, 0x09, + 0x8d, 0xff, 0x21, 0x3a, 0x56, 0x71, 0xc1, 0xfb, 0xf3, 0x44, 0x92, 0x9c, 0x0a, 0x65, 0x2d, 0x9b, 0x17, 0x5b, 0xc8, + 0xa0, 0xe3, 0x96, 0x72, 0x08, 0xe4, 0x00, 0x60, 0x7a, 0xd5, 0x86, 0xba, 0xc6, 0x3e, 0x77, 0xbd, 0x21, 0x21, 0x56, + 0x04, 0xbb, 0xa1, 0x13, 0x24, 0xd4, 0x54, 0x21, 0xf1, 0x59, 0xaf, 0xf2, 0x6e, 0x14, 0x85, 0x1e, 0xf0, 0x8f, 0x7f, + 0x93, 0x88, 0xf3, 0x37, 0x58, 0xaa, 0xdf, 0xb0, 0x4a, 0x1b, 0xfa, 0xe4, 0x5f, 0x24, 0x5e, 0x75, 0xfe, 0x29, 0x66, + 0x9a, 0x6d, 0x87, 0xee, 0x67, 0x7e, 0x3e, 0xe1, 0x51, 0xf6, 0xc2, 0x21, 0x63, 0x0d, 0x19, 0x3a, 0x86, 0x2e, 0x12, + 0x6c, 0xf2, 0x97, 0x14, 0xfa, 0x64, 0x5a, 0xfa, 0x8a, 0xdf, 0x69, 0xdd, 0x9d, 0xad, 0x42, 0x21, 0x16, 0xcc, 0x50, + 0x4a, 0xa3, 0xee, 0x98, 0xea, 0x98, 0x59, 0x98, 0xe3, 0x90, 0x24, 0xa2, 0x45, 0x0e, 0x67, 0xb8, 0xbf, 0x01, 0x08, + 0x81, 0x06, 0x2b, 0x11, 0x2a, 0xca, 0xc5, 0x1e, 0xc1, 0x13, 0x6e, 0xb6, 0xb9, 0xdf, 0xc9, 0x3c, 0x9c, 0x48, 0xa3, + 0x5c, 0xc1, 0x02, 0x30, 0xd5, 0xb3, 0x1b, 0x49, 0xc9, 0xe1, 0x5e, 0xb4, 0xc6, 0xf9, 0x0c, 0x25, 0x94, 0xc5, 0xce, + 0x83, 0x60, 0x5d, 0x65, 0x53, 0xd9, 0x19, 0xcc, 0xaa, 0xee, 0x1c, 0xa8, 0xe2, 0x02, 0x89, 0xba, 0x31, 0x26, 0x53, + 0xcc, 0xb2, 0x19, 0x7e, 0x02, 0x31, 0x6f, 0xc8, 0x54, 0x70, 0xf7, 0x5a, 0x9d, 0x2d, 0xef, 0x1a, 0x26, 0x94, 0xa1, + 0x81, 0xd5, 0x49, 0x8c, 0x1a, 0x96, 0x70, 0x71, 0xc1, 0x67, 0xd0, 0x9f, 0x06, 0x42, 0x33, 0x3a, 0xbd, 0x19, 0xa3, + 0x7e, 0xcb, 0xc6, 0x93, 0xef, 0x15, 0xe7, 0xbd, 0xee, 0xf0, 0x4a, 0x25, 0x54, 0x25, 0x5f, 0x46, 0x88, 0xe8, 0x56, + 0x5f, 0x2a, 0x9e, 0x53, 0xf7, 0xde, 0x2f, 0x24, 0x9e, 0xf4, 0x99, 0x91, 0xc7, 0xfb, 0x5d, 0x28, 0x28, 0x80, 0xde, + 0xb2, 0x08, 0x99, 0x7e, 0x50, 0x56, 0xd5, 0x1d, 0x9e, 0x5c, 0xda, 0x95, 0x50, 0xf1, 0xba, 0x7e, 0xb3, 0x3c, 0x81, + 0x2a, 0x4c, 0x66, 0x28, 0xe6, 0xd8, 0x54, 0x8e, 0xc6, 0x1b, 0x4c, 0x23, 0x18, 0xe7, 0x39, 0xa1, 0x02, 0xfd, 0x50, + 0x25, 0x9a, 0x3a, 0x33, 0x73, 0xc6, 0xf2, 0xba, 0x0f, 0x7b, 0x3e, 0x77, 0x8a, 0xd9, 0x85, 0x57, 0xfb, 0x96, 0x3a, + 0x6e, 0x9f, 0x05, 0x97, 0xe5, 0xee, 0x16, 0x85, 0xec, 0x29, 0x95, 0xc4, 0x38, 0x80, 0x75, 0x1e, 0x5d, 0xd9, 0x9a, + 0x2e, 0x65, 0xb0, 0xfb, 0x13, 0xa4, 0x00, 0x8e, 0x96, 0x0c, 0x24, 0x60, 0x37, 0xf2, 0x6b, 0xd7, 0x64, 0xe6, 0x9b, + 0x8f, 0x03, 0x0b, 0x82, 0xc8, 0x04, 0xce, 0x10, 0x31, 0x91, 0x86, 0xf0, 0xf3, 0x3e, 0xce, 0xbe, 0xda, 0x4c, 0x34, + 0x51, 0x7b, 0x23, 0xe4, 0xf3, 0xf0, 0x1a, 0x76, 0xf3, 0xc0, 0x94, 0xf7, 0x5b, 0x3a, 0x45, 0x1c, 0x34, 0x89, 0xa9, + 0xd5, 0x33, 0xf6, 0x5b, 0xe6, 0x72, 0xc3, 0x2f, 0xc4, 0x14, 0x77, 0x77, 0x71, 0x2a, 0x0c, 0x2c, 0x99, 0xf0, 0xcb, + 0x83, 0xa9, 0x89, 0x29, 0x7b, 0x88, 0xef, 0xfb, 0xf0, 0xe1, 0x71, 0x63, 0xf6, 0xc9, 0x5d, 0x71, 0x9d, 0x58, 0xaa, + 0xb0, 0xaf, 0xe9, 0xeb, 0x21, 0x63, 0x4e, 0x44, 0xd2, 0x52, 0x99, 0xae, 0x0f, 0x36, 0xfe, 0xac, 0x62, 0xc9, 0xca, + 0x11, 0xb6, 0x46, 0x80, 0xf4, 0x4b, 0x83, 0xa6, 0xe1, 0x90, 0x7a, 0x18, 0xfa, 0x40, 0x8a, 0x39, 0xc1, 0xc0, 0xd1, + 0x25, 0x71, 0x6d, 0xeb, 0x70, 0x58, 0x24, 0x3d, 0x96, 0x68, 0xe9, 0xe7, 0x6e, 0x73, 0x7e, 0xb6, 0x07, 0xc7, 0xc2, + 0x65, 0xe5, 0x65, 0x65, 0x5e, 0x78, 0xc6, 0xc9, 0x62, 0xaf, 0x5a, 0x35, 0x7e, 0xe7, 0xf7, 0x7d, 0x2d, 0x99, 0x83, + 0x91, 0x1b, 0x99, 0x2b, 0x5a, 0x78, 0x30, 0xef, 0xe4, 0x15, 0x34, 0x6e, 0xb6, 0x12, 0x87, 0x12, 0xda, 0x7a, 0x70, + 0xea, 0xfd, 0x99, 0x02, 0x57, 0x10, 0x28, 0xbc, 0x7e, 0x3f, 0x1e, 0x6f, 0xc8, 0x68, 0x73, 0x85, 0x0c, 0x7a, 0x6e, + 0xf5, 0x02, 0xd5, 0x79, 0xdf, 0x7c, 0x3e, 0x67, 0x6f, 0xcc, 0xb3, 0xee, 0x63, 0x48, 0x7d, 0x64, 0x88, 0x1a, 0xb2, + 0xbc, 0x16, 0x0a, 0x93, 0x05, 0xf4, 0x38, 0xaa, 0x2a, 0x44, 0x56, 0x87, 0xb2, 0x71, 0x33, 0x54, 0xd8, 0x4f, 0xaf, + 0x7f, 0x80, 0x11, 0x72, 0x94, 0x52, 0x68, 0x4f, 0x4c, 0x55, 0x46, 0x08, 0x81, 0xb1, 0x21, 0x1a, 0x96, 0x91, 0x29, + 0x6c, 0xb3, 0x8a, 0x76, 0x9c, 0xae, 0xec, 0xd6, 0x37, 0xab, 0x14, 0x73, 0xde, 0x0d, 0x9e, 0x25, 0x68, 0xf7, 0xf6, + 0xb6, 0xc7, 0x31, 0xf4, 0x53, 0xf1, 0x3f, 0x82, 0x1d, 0x9d, 0xc3, 0x12, 0x15, 0x9c, 0x12, 0xfb, 0x9c, 0xf9, 0xab, + 0x63, 0x25, 0x8e, 0x7b, 0xda, 0xe2, 0xde, 0x8e, 0x1d, 0x33, 0x2b, 0x3f, 0x36, 0x59, 0x72, 0x2d, 0x43, 0x12, 0xd5, + 0x35, 0x97, 0x8e, 0x41, 0x53, 0x22, 0x37, 0x6f, 0x66, 0x96, 0xf6, 0x06, 0xcc, 0x8f, 0xf6, 0x41, 0xfb, 0x25, 0x21, + 0xc2, 0x6a, 0xa9, 0x99, 0x8b, 0x2f, 0x71, 0xca, 0x38, 0xc9, 0x7d, 0x03, 0xe6, 0xef, 0xc4, 0xf5, 0xef, 0xa2, 0x07, + 0x87, 0x39, 0x02, 0x18, 0x88, 0xb7, 0x52, 0x6d, 0xe5, 0x4d, 0x44, 0x69, 0x05, 0x86, 0x5d, 0x9b, 0xca, 0x86, 0xa3, + 0x21, 0x7f, 0xc3, 0x23, 0xfb, 0x32, 0x5f, 0x6f, 0x6c, 0xa1, 0x38, 0xf1, 0x2e, 0xff, 0xfc, 0xd3, 0x87, 0xe7, 0xc7, + 0x9c, 0x0b, 0x76, 0x73, 0xe3, 0xbc, 0x6a, 0x13, 0xc8, 0xb6, 0x5d, 0x0c, 0x12, 0x9c, 0x42, 0x23, 0x27, 0x40, 0xfa, + 0xe1, 0xc2, 0x22, 0xc4, 0xcf, 0xdf, 0x3d, 0xd9, 0xf5, 0xf6, 0x3a, 0x6c, 0x46, 0xb3, 0xbd, 0x23, 0x1a, 0x41, 0x4e, + 0x57, 0xc7, 0xec, 0xfb, 0xe4, 0x60, 0xfc, 0x4b, 0xe2, 0x7e, 0xa6, 0xca, 0xcf, 0x35, 0xd7, 0x37, 0x55, 0x7e, 0xea, + 0xe0, 0xc6, 0x27, 0xb0, 0x6a, 0x53, 0x72, 0x13, 0xe6, 0xca, 0xad, 0x3e, 0x79, 0x4c, 0x0c, 0xa6, 0xd5, 0x3f, 0x7d, + 0x7f, 0x1a, 0x06, 0x06, 0x17, 0xbb, 0x3b, 0x4f, 0xbe, 0xce, 0xf4, 0xc7, 0x79, 0xdf, 0xb1, 0xfb, 0x3a, 0xf8, 0x71, + 0x5c, 0x5d, 0xd6, 0x23, 0x49, 0x23, 0x07, 0xc4, 0x7b, 0xca, 0xa8, 0x61, 0x2f, 0x77, 0x95, 0x87, 0x55, 0xfd, 0x7d, + 0xd6, 0xbb, 0x44, 0xef, 0x8e, 0x9d, 0x65, 0xad, 0x26, 0x94, 0x17, 0x98, 0xd3, 0x79, 0x4c, 0xb3, 0x42, 0x47, 0x85, + 0x9a, 0x89, 0xf6, 0x32, 0xb2, 0x4a, 0x7f, 0xf3, 0x4b, 0xda, 0xaf, 0x16, 0xc1, 0xb0, 0xac, 0xc2, 0xe5, 0x3c, 0x6a, + 0xb0, 0x59, 0xbb, 0x36, 0x7f, 0xfd, 0xef, 0x69, 0xc3, 0xce, 0x04, 0x51, 0x7d, 0x52, 0x2b, 0x79, 0xd6, 0x77, 0xb8, + 0xea, 0xf6, 0x7c, 0xbe, 0x91, 0x79, 0xaf, 0x9e, 0x2f, 0x9a, 0x8f, 0xb7, 0x5f, 0xbb, 0x07, 0xe0, 0x97, 0x5d, 0x59, + 0xab, 0x37, 0x2b, 0x8b, 0x21, 0xf5, 0x9e, 0xf5, 0x7e, 0x21, 0x53, 0x02, 0x03, 0x52, 0x5f, 0xf8, 0xbc, 0x76, 0x1d, + 0xf4, 0x3a, 0x2a, 0xdd, 0x7e, 0x59, 0xb4, 0x16, 0x85, 0x94, 0x27, 0x92, 0x52, 0x92, 0x4d, 0x5c, 0xc5, 0xcc, 0x30, + 0xcd, 0x3b, 0xbf, 0xa9, 0x27, 0xfd, 0x55, 0x6d, 0xf6, 0xf5, 0xd6, 0x66, 0x6f, 0x08, 0xaf, 0x79, 0x8a, 0xb0, 0x7a, + 0xb7, 0x4e, 0x39, 0x5e, 0xbd, 0xed, 0xf4, 0x2f, 0x5a, 0xfb, 0xf4, 0xbd, 0x5b, 0xc3, 0xd8, 0xa8, 0x9c, 0x15, 0xca, + 0x6f, 0x72, 0x4a, 0xc3, 0x35, 0xa3, 0x0d, 0x1b, 0x61, 0x8a, 0x7d, 0xb9, 0x7a, 0xb7, 0x3a, 0x61, 0x85, 0x48, 0xef, + 0xc1, 0x33, 0xc2, 0xe3, 0xcd, 0x1f, 0x24, 0x54, 0xfd, 0x82, 0x8c, 0xe5, 0x8d, 0xba, 0xb5, 0x68, 0x3c, 0xda, 0x46, + 0xce, 0x24, 0xcc, 0x37, 0xe8, 0xa6, 0xc9, 0x6c, 0x6d, 0xc2, 0xa9, 0x23, 0xb7, 0x49, 0xb1, 0x19, 0xa9, 0x6a, 0xef, + 0x32, 0x98, 0xd2, 0x7d, 0xd2, 0x3e, 0xb1, 0xa7, 0xd4, 0x63, 0xd9, 0x19, 0x62, 0x5a, 0x10, 0xa0, 0xa6, 0x5c, 0xb5, + 0x57, 0x88, 0x65, 0x70, 0x4a, 0x5b, 0x4f, 0xb6, 0xcf, 0x30, 0x5b, 0x34, 0x93, 0x10, 0x9c, 0x15, 0x5a, 0x36, 0xdd, + 0xa4, 0xad, 0x04, 0x2f, 0x23, 0xd5, 0x68, 0xb4, 0x99, 0xe0, 0xb1, 0xf3, 0x5e, 0x34, 0xf3, 0x43, 0x87, 0xb4, 0xb0, + 0x16, 0x25, 0xfc, 0xc2, 0x91, 0x9c, 0xa5, 0x8d, 0xe0, 0xb4, 0x86, 0xee, 0xde, 0x35, 0xaf, 0xb7, 0xf7, 0x23, 0x1f, + 0xd8, 0x78, 0xd3, 0x88, 0x34, 0xc7, 0x7a, 0xc3, 0xbe, 0x09, 0xc6, 0x04, 0x1c, 0x78, 0xe6, 0xe5, 0x2f, 0x9e, 0x00, + 0xe7, 0x07, 0xd8, 0x90, 0x5e, 0xe6, 0xab, 0x8a, 0x60, 0x25, 0xaa, 0x34, 0xe3, 0xc2, 0xec, 0x31, 0xe8, 0xbb, 0x6d, + 0xe9, 0x37, 0xe3, 0xcf, 0x4c, 0x1c, 0xa5, 0x70, 0xf2, 0x9c, 0x6e, 0x2c, 0xdc, 0x43, 0x02, 0xbe, 0x21, 0xab, 0x9e, + 0x78, 0x93, 0xd3, 0xb8, 0xc1, 0xf5, 0x9b, 0x57, 0xb3, 0x13, 0x3e, 0x28, 0xcd, 0x0a, 0x10, 0xb2, 0xeb, 0x50, 0xd9, + 0xf0, 0x32, 0x53, 0x55, 0x7b, 0xad, 0x9c, 0xdc, 0x2f, 0xc4, 0x88, 0x82, 0x52, 0x31, 0x1f, 0x1f, 0xc8, 0x28, 0x8d, + 0xe2, 0xa2, 0xe4, 0xde, 0x43, 0x8a, 0x5d, 0x73, 0xde, 0xd0, 0x29, 0x3f, 0xa7, 0x81, 0xb6, 0x7e, 0x37, 0x14, 0x5e, + 0xfd, 0xee, 0x1e, 0x8d, 0x1d, 0xdc, 0x7a, 0x7a, 0xeb, 0x64, 0xbd, 0xb1, 0xb5, 0xf9, 0x08, 0x39, 0x35, 0x20, 0x7e, + 0x63, 0xc2, 0x1f, 0x7d, 0x7b, 0xa9, 0x29, 0xac, 0xa1, 0xb1, 0x8f, 0x6c, 0x6e, 0xc4, 0x56, 0x78, 0xe3, 0xd4, 0x0a, + 0x5f, 0x82, 0x28, 0x16, 0xe3, 0x17, 0x3f, 0x6b, 0x34, 0xb8, 0xa6, 0x12, 0x1a, 0x0e, 0x09, 0xee, 0x45, 0x91, 0xa7, + 0x9f, 0xba, 0xf8, 0x59, 0x5c, 0xbc, 0x98, 0xaf, 0x86, 0xc4, 0xcc, 0xd3, 0xb6, 0xd2, 0x62, 0xd9, 0xb4, 0x15, 0x3f, + 0x5b, 0x13, 0x0d, 0x77, 0xd1, 0x1a, 0x9f, 0xd5, 0xd8, 0x56, 0x55, 0xaa, 0x1f, 0xca, 0xef, 0x7b, 0x1b, 0x9b, 0x4c, + 0x9d, 0x81, 0x0e, 0x92, 0x86, 0xa4, 0x97, 0x8a, 0x6e, 0x81, 0x8c, 0x3d, 0x3d, 0x26, 0x0d, 0x4b, 0xc4, 0x58, 0x05, + 0xa1, 0x9c, 0x61, 0xd6, 0x8e, 0x72, 0xf3, 0xb0, 0xde, 0x42, 0xaf, 0xd8, 0x6d, 0x4c, 0x7a, 0xea, 0x8d, 0x65, 0x79, + 0xd6, 0xaa, 0xfb, 0x1c, 0x05, 0x14, 0xff, 0x1c, 0xee, 0xc0, 0x1f, 0x6e, 0x0d, 0x9a, 0xbd, 0x51, 0xb9, 0xd8, 0xd4, + 0xeb, 0x10, 0x6f, 0xd2, 0x1d, 0x8f, 0x25, 0x64, 0x21, 0xa2, 0xf1, 0x4d, 0x37, 0x05, 0x0c, 0xcd, 0x54, 0x46, 0x1d, + 0x4b, 0xe3, 0x28, 0xa8, 0x88, 0x15, 0xd9, 0x3b, 0x47, 0xe4, 0x55, 0x41, 0x85, 0x20, 0xad, 0x59, 0x36, 0x09, 0x99, + 0x7f, 0x1a, 0x64, 0x40, 0x49, 0x61, 0x13, 0xfd, 0x69, 0x13, 0x27, 0x85, 0x04, 0xdc, 0xad, 0xec, 0xa2, 0x8b, 0xad, + 0xa9, 0x15, 0xfa, 0x0c, 0x46, 0x5b, 0x70, 0x14, 0xb2, 0x2a, 0x44, 0x0b, 0xcd, 0x7c, 0xc3, 0xbf, 0x45, 0x9e, 0x02, + 0x12, 0x44, 0x41, 0x13, 0x0e, 0x65, 0x37, 0xdd, 0x41, 0x8a, 0x74, 0xf4, 0x10, 0xc1, 0x07, 0xa4, 0x84, 0x0a, 0xd0, + 0x79, 0x1e, 0xd7, 0xdd, 0x4b, 0x4d, 0x23, 0x2a, 0x23, 0x1b, 0x7c, 0x4f, 0x07, 0x45, 0xae, 0x57, 0xac, 0xf4, 0xff, + 0x1f, 0x79, 0x2c, 0xe5, 0x05, 0xec, 0x50, 0xc0, 0x9b, 0x0f, 0xd8, 0x42, 0x8a, 0x43, 0xad, 0x9e, 0x2f, 0x28, 0x51, + 0x1c, 0x49, 0x34, 0xbd, 0xaf, 0x68, 0xa7, 0x47, 0x8c, 0x32, 0xf1, 0xe1, 0x24, 0x50, 0xb7, 0xcd, 0xad, 0xba, 0x64, + 0xb8, 0xba, 0xca, 0xda, 0x7f, 0x3a, 0xec, 0xab, 0xa9, 0x82, 0x0b, 0x3b, 0xbd, 0xb8, 0x83, 0x60, 0x0a, 0x85, 0x1e, + 0x3b, 0x7f, 0x87, 0x89, 0xca, 0xea, 0x2f, 0x24, 0x52, 0xac, 0x5a, 0x2e, 0x43, 0x03, 0x1c, 0xc4, 0x4d, 0x81, 0xda, + 0x11, 0x0c, 0x7a, 0xc6, 0x2e, 0x41, 0x1a, 0xcb, 0x72, 0x49, 0x65, 0x38, 0x69, 0xa5, 0xd5, 0xe7, 0x93, 0x23, 0xe4, + 0x31, 0xde, 0x49, 0xad, 0x12, 0x15, 0x9c, 0x3d, 0x96, 0x65, 0x6d, 0xd4, 0x73, 0xd8, 0x8c, 0xce, 0xb2, 0x8a, 0x5a, + 0xe7, 0xda, 0x6a, 0xa7, 0x14, 0x4a, 0x75, 0x2c, 0x08, 0x36, 0x2d, 0x1c, 0x0f, 0xd2, 0xc2, 0x0e, 0xf4, 0x74, 0x42, + 0x8d, 0x4b, 0x9a, 0x1d, 0x52, 0x91, 0x77, 0x8b, 0x8e, 0xd0, 0x4c, 0xa7, 0x1b, 0x74, 0x53, 0x1e, 0x2b, 0x82, 0x3a, + 0x98, 0xd9, 0x11, 0x86, 0x57, 0x87, 0x61, 0x3c, 0x47, 0x5f, 0x52, 0x36, 0xc4, 0xca, 0x15, 0xb7, 0xb3, 0x36, 0xa5, + 0x83, 0xb7, 0x0a, 0x3f, 0x34, 0x8f, 0xca, 0xc4, 0xe2, 0xa8, 0x58, 0xa1, 0xc4, 0x35, 0xcd, 0x68, 0x8b, 0xab, 0xa5, + 0x9b, 0x18, 0x23, 0xe4, 0x2b, 0xe6, 0xaf, 0x91, 0x32, 0xcc, 0x0d, 0x64, 0x0d, 0xd2, 0x45, 0x32, 0xc5, 0x2c, 0x4c, + 0x90, 0x71, 0xc0, 0x98, 0xf8, 0x3e, 0x5b, 0x5c, 0xfa, 0x33, 0x70, 0x85, 0x63, 0x36, 0xad, 0xa4, 0xfb, 0x10, 0x14, + 0xba, 0xef, 0xf1, 0xa0, 0x41, 0x3d, 0x76, 0x10, 0x3d, 0x53, 0x70, 0xf9, 0x5c, 0x62, 0xa3, 0x7e, 0x6f, 0xd8, 0xd9, + 0x11, 0x8a, 0xcd, 0x86, 0x04, 0x03, 0xcc, 0x27, 0x4a, 0xf7, 0x3e, 0xf8, 0xd0, 0xd2, 0x2f, 0x5f, 0xdf, 0x22, 0x84, + 0x0c, 0xe5, 0x4e, 0x94, 0x9a, 0x29, 0x81, 0x8a, 0xa6, 0xe6, 0xa0, 0x99, 0x0f, 0x4e, 0xdc, 0xed, 0xf0, 0x7a, 0x42, + 0x2f, 0x57, 0xbb, 0x75, 0x4f, 0xe5, 0xe5, 0x8a, 0x34, 0x42, 0xab, 0x4f, 0x1b, 0x95, 0x0f, 0xe6, 0xb1, 0xb8, 0x5e, + 0xe9, 0xae, 0x1f, 0xb8, 0x43, 0xab, 0xdd, 0xc5, 0x87, 0xd2, 0x32, 0x3e, 0xd4, 0x93, 0xac, 0xd7, 0x60, 0x11, 0x78, + 0xa8, 0xb1, 0x14, 0xcd, 0xf1, 0x26, 0xeb, 0xd9, 0x60, 0x53, 0xa3, 0xd9, 0x58, 0x4a, 0xcb, 0xdf, 0xdb, 0xb8, 0x5f, + 0x67, 0x53, 0x85, 0xd2, 0x87, 0x81, 0xf4, 0x3e, 0x81, 0x92, 0x39, 0x0a, 0xdd, 0xe9, 0x0c, 0x95, 0x8f, 0xe6, 0x09, + 0x30, 0x56, 0xf0, 0x8b, 0x4b, 0x1a, 0xd3, 0x59, 0x73, 0x9c, 0xaf, 0xe0, 0xd4, 0xe2, 0xa8, 0xb1, 0x75, 0xe9, 0x89, + 0x20, 0xbc, 0x5a, 0xdc, 0x12, 0xbd, 0x24, 0xe9, 0xa8, 0x23, 0x25, 0xfe, 0x53, 0x8c, 0x73, 0xaa, 0xa9, 0xa3, 0x9d, + 0x4d, 0xe3, 0x0d, 0x15, 0x9c, 0x01, 0x35, 0x39, 0x6c, 0xfb, 0x12, 0x0a, 0xe0, 0xff, 0x9d, 0xa6, 0x12, 0xf1, 0x32, + 0x11, 0x37, 0xab, 0x0a, 0x65, 0x40, 0x19, 0x01, 0x94, 0x5f, 0xab, 0x91, 0xd1, 0x37, 0x7e, 0x34, 0x51, 0x9f, 0xc7, + 0x98, 0x03, 0x1d, 0xb4, 0x34, 0xf9, 0x1b, 0x38, 0x22, 0xd9, 0x36, 0xc7, 0x66, 0x06, 0x55, 0x5b, 0x3c, 0x09, 0xbc, + 0x44, 0x34, 0x56, 0xb3, 0x7e, 0x8c, 0xdf, 0xa6, 0x73, 0x3f, 0x78, 0xeb, 0x00, 0xfc, 0xc2, 0x82, 0x6a, 0x67, 0xd6, + 0xea, 0x7b, 0x09, 0x1a, 0xf0, 0xa3, 0xd8, 0xe8, 0x2c, 0x75, 0x42, 0xcd, 0xc9, 0x0e, 0xbd, 0xa9, 0xc9, 0x39, 0x27, + 0xca, 0x9e, 0x4e, 0x66, 0x1c, 0x85, 0x43, 0xd4, 0x19, 0x71, 0xf2, 0xa9, 0xf7, 0xcd, 0x8c, 0x78, 0xf4, 0x89, 0x8b, + 0x84, 0x43, 0x70, 0x4e, 0x15, 0x5b, 0x36, 0x9b, 0x8b, 0xd8, 0xfc, 0x4c, 0x8a, 0x4d, 0xc6, 0x04, 0xab, 0x05, 0xbd, + 0xbf, 0x21, 0x12, 0xc2, 0xb4, 0x21, 0x24, 0x4b, 0x53, 0x93, 0x1a, 0x8f, 0x9b, 0xab, 0x60, 0x33, 0xba, 0xc4, 0xfc, + 0x73, 0x75, 0x41, 0xa1, 0xf0, 0x8a, 0x82, 0x9f, 0xd7, 0x70, 0xec, 0x35, 0xc2, 0xd8, 0x33, 0x24, 0xfa, 0xd0, 0x0e, + 0x9a, 0x19, 0xc9, 0x2d, 0xa6, 0xf6, 0x64, 0x47, 0xe0, 0x95, 0x97, 0x21, 0xdd, 0xb0, 0xdf, 0x04, 0x94, 0xc0, 0x6b, + 0xea, 0x9b, 0xe5, 0x50, 0xe6, 0x70, 0x39, 0xdd, 0xd5, 0x6f, 0x80, 0xc6, 0xce, 0x16, 0x1e, 0xa6, 0x68, 0x1d, 0xaa, + 0x7d, 0x48, 0x5d, 0x3d, 0xb3, 0x57, 0x31, 0x47, 0x39, 0x08, 0xea, 0xc6, 0x6c, 0x13, 0xfb, 0x3a, 0x75, 0x5d, 0x03, + 0xf5, 0x7b, 0xec, 0x67, 0xa0, 0xf5, 0x30, 0xd2, 0x6c, 0x1d, 0x4f, 0xe9, 0x2d, 0x12, 0x46, 0x5b, 0x4a, 0x24, 0x0d, + 0x93, 0x66, 0x4d, 0x6a, 0x00, 0xd3, 0x22, 0xcc, 0x41, 0xfd, 0x46, 0xef, 0xd8, 0x53, 0xc2, 0x74, 0x96, 0x6d, 0xd7, + 0xa8, 0x6c, 0x92, 0x3b, 0xce, 0x15, 0x3a, 0x09, 0x29, 0x15, 0x65, 0x5f, 0x32, 0x05, 0xa9, 0x3c, 0x26, 0x9c, 0xe3, + 0x6a, 0x40, 0x32, 0x4c, 0xa9, 0xa0, 0xf6, 0xd6, 0x59, 0x4a, 0x5d, 0x72, 0xe6, 0x08, 0x9f, 0x62, 0xf9, 0xb3, 0xaa, + 0x79, 0xd8, 0x54, 0x63, 0x38, 0xed, 0xd5, 0xcb, 0xc5, 0x82, 0x07, 0xf3, 0x27, 0x70, 0x01, 0x45, 0xbe, 0xa2, 0xa6, + 0x3c, 0x97, 0x87, 0x72, 0x12, 0x7d, 0x32, 0xfe, 0x3d, 0xd5, 0x2d, 0x88, 0x5c, 0xe6, 0x33, 0x44, 0x66, 0xfa, 0x8d, + 0xfb, 0xea, 0xc3, 0x72, 0xb0, 0xa9, 0x24, 0xa6, 0x7f, 0x3f, 0x79, 0xb7, 0x42, 0x19, 0x7e, 0xe8, 0x89, 0x6d, 0xd1, + 0x52, 0xd0, 0xb3, 0xc8, 0xa8, 0xc2, 0x1c, 0x91, 0x13, 0x25, 0x70, 0x96, 0x3d, 0x4a, 0xf0, 0x92, 0x1a, 0x3a, 0x42, + 0x5b, 0x10, 0x27, 0xac, 0xa8, 0x1c, 0xc9, 0xb3, 0xbf, 0x55, 0xf5, 0x92, 0xea, 0x94, 0xc7, 0x80, 0xd5, 0x5f, 0x7b, + 0xa8, 0xbe, 0xbe, 0xb7, 0x41, 0x04, 0x5b, 0xd0, 0xea, 0x71, 0xf8, 0x12, 0xc0, 0x41, 0x30, 0x59, 0x20, 0x53, 0x1e, + 0x53, 0x43, 0xf5, 0xaa, 0x6f, 0x4f, 0x8e, 0x42, 0xde, 0x80, 0x22, 0xdc, 0x12, 0x4f, 0xa7, 0xa7, 0x71, 0x29, 0x6e, + 0x3f, 0x95, 0xfe, 0x81, 0x2f, 0xc0, 0xae, 0x55, 0x0a, 0x1e, 0x60, 0x4a, 0xc2, 0x1b, 0x99, 0x0a, 0x6b, 0xf9, 0xa6, + 0xd3, 0x9b, 0x97, 0x3c, 0xc6, 0x43, 0xb8, 0xb7, 0x3b, 0x70, 0xd6, 0x57, 0xef, 0x35, 0x9a, 0xca, 0xc0, 0xc5, 0x14, + 0x6d, 0x38, 0x3a, 0xc0, 0xe5, 0x1c, 0x61, 0x59, 0xdd, 0x7d, 0x38, 0xa3, 0x54, 0x06, 0x91, 0x32, 0x3a, 0xec, 0xaa, + 0xb4, 0x98, 0xf4, 0xd8, 0x62, 0x16, 0xf2, 0x3e, 0xc5, 0x19, 0x61, 0x13, 0x51, 0x4c, 0x13, 0x7d, 0x58, 0x04, 0xe2, + 0x19, 0x18, 0xdd, 0xae, 0x5d, 0x3f, 0x90, 0xec, 0x0d, 0x43, 0x28, 0xbf, 0x54, 0xee, 0x52, 0xbb, 0xae, 0xba, 0x00, + 0x96, 0xef, 0xc2, 0x7c, 0x42, 0x90, 0xa7, 0xaf, 0x38, 0xac, 0xab, 0xdf, 0xca, 0xcc, 0x46, 0x6e, 0xac, 0x6e, 0x85, + 0x4e, 0x2e, 0xdc, 0xd6, 0x2b, 0x1d, 0xe8, 0x4c, 0x40, 0xc2, 0x07, 0xcf, 0xbe, 0x8f, 0xb6, 0x91, 0x4a, 0x32, 0x57, + 0xdc, 0x73, 0xde, 0x5b, 0x10, 0x56, 0x0f, 0x64, 0x3d, 0x22, 0x17, 0x24, 0xe8, 0x05, 0x1c, 0xcf, 0xe7, 0xd1, 0xa9, + 0x79, 0xc5, 0xde, 0x28, 0x9f, 0x18, 0x96, 0xb8, 0x99, 0x9b, 0x4f, 0x87, 0xa7, 0x88, 0xf4, 0x7d, 0x8c, 0x84, 0x83, + 0x30, 0x24, 0x55, 0xde, 0xbc, 0x91, 0x50, 0xc4, 0x53, 0xc9, 0xce, 0xb8, 0xdc, 0x9c, 0xd5, 0x88, 0xc5, 0xa5, 0x28, + 0xaf, 0x5e, 0x3b, 0x8a, 0xf2, 0x8e, 0xad, 0x5a, 0xd2, 0xce, 0xf6, 0x95, 0x32, 0xb6, 0x2a, 0x71, 0x44, 0x23, 0xa3, + 0x13, 0xb7, 0x7a, 0x51, 0x2a, 0x87, 0xac, 0x91, 0xb2, 0x81, 0x75, 0x65, 0x32, 0x0b, 0xca, 0xc3, 0xca, 0xf9, 0x22, + 0xee, 0xd8, 0x2e, 0x82, 0x56, 0xa1, 0xb0, 0x74, 0x50, 0x2f, 0x28, 0x7a, 0x3f, 0x00, 0x5b, 0x27, 0xf9, 0xdb, 0x52, + 0x74, 0xf1, 0x3d, 0x74, 0x95, 0x5d, 0xd0, 0x81, 0x30, 0x1e, 0x25, 0x69, 0x18, 0x18, 0xc8, 0x37, 0x0f, 0xb6, 0x23, + 0xd0, 0xe5, 0xf5, 0xf6, 0xa8, 0xa4, 0xd3, 0x29, 0xc8, 0x29, 0x03, 0xc0, 0x34, 0x51, 0x58, 0x5d, 0xad, 0x31, 0xfb, + 0xbb, 0xe3, 0xe2, 0x57, 0xc7, 0xae, 0x15, 0xcc, 0xf0, 0x41, 0xd8, 0xcd, 0xbd, 0xab, 0xad, 0xc1, 0x17, 0xa7, 0x8e, + 0x99, 0x58, 0x98, 0x95, 0x96, 0x3f, 0xaf, 0x37, 0xb3, 0x7f, 0x74, 0x7d, 0x4c, 0xe1, 0x03, 0x3d, 0x8e, 0x5d, 0x8b, + 0xda, 0x47, 0xf1, 0xbc, 0x94, 0xf8, 0xc2, 0xef, 0x25, 0x8f, 0x9b, 0xa1, 0xaf, 0xbe, 0x86, 0x39, 0x0c, 0xad, 0x58, + 0x1b, 0xa9, 0xfc, 0x02, 0x9b, 0xde, 0x84, 0x3b, 0x71, 0xc4, 0x80, 0x73, 0x3d, 0x47, 0x9a, 0xf9, 0xcc, 0x64, 0xeb, + 0x99, 0xa4, 0xd1, 0x30, 0x1d, 0x89, 0x38, 0x6a, 0x01, 0x2a, 0x3e, 0x71, 0x70, 0x7f, 0x78, 0xc0, 0x48, 0x71, 0x18, + 0xa3, 0x1f, 0x8b, 0x7c, 0x9b, 0x12, 0xcb, 0x86, 0xaf, 0xe0, 0xb9, 0x66, 0x97, 0x3f, 0xd8, 0x6f, 0x8d, 0x8b, 0x55, + 0x8f, 0x95, 0x41, 0xbc, 0x9c, 0xae, 0xd9, 0x1b, 0x94, 0xb1, 0x3a, 0x8f, 0xb0, 0xdc, 0x9b, 0xcc, 0xe6, 0xcc, 0x05, + 0x72, 0x12, 0xa8, 0xde, 0xe8, 0xe6, 0x97, 0x22, 0xba, 0xd5, 0xae, 0xc1, 0x39, 0x3f, 0x33, 0xdf, 0xa3, 0x48, 0xfc, + 0xe4, 0x47, 0x5d, 0x32, 0xcb, 0xd6, 0x09, 0x74, 0xb2, 0xec, 0xca, 0x8d, 0xef, 0xb6, 0xbe, 0x78, 0xb7, 0xbe, 0xb0, + 0xfe, 0x44, 0x86, 0xf3, 0x4b, 0x72, 0xf7, 0xf8, 0x27, 0xd6, 0x41, 0x6c, 0xfd, 0xe7, 0x2f, 0x94, 0xfc, 0x4f, 0xa9, + 0x71, 0xe7, 0x8f, 0x9d, 0x21, 0x54, 0x8c, 0x50, 0xe3, 0x0d, 0xc6, 0x82, 0x73, 0x77, 0xa4, 0x64, 0xdd, 0x8c, 0x71, + 0x5a, 0x49, 0x59, 0x03, 0xf7, 0x95, 0x26, 0xa7, 0x55, 0x6e, 0xaf, 0x05, 0x31, 0xbb, 0x34, 0xb5, 0x43, 0x81, 0x4f, + 0x7d, 0x26, 0x65, 0x49, 0x72, 0x9d, 0xf2, 0xec, 0x1f, 0xa9, 0xed, 0x08, 0x60, 0xae, 0x7e, 0x05, 0x10, 0x8c, 0xf3, + 0xe5, 0xee, 0x5a, 0xe9, 0xa9, 0xcf, 0x60, 0x54, 0x8f, 0xbc, 0xf4, 0x2a, 0x5f, 0x16, 0x71, 0xa5, 0x1b, 0xe5, 0x3b, + 0x5c, 0xc2, 0x46, 0xb4, 0x78, 0xf7, 0xd2, 0x6b, 0x85, 0xbc, 0xa3, 0x7c, 0x50, 0x55, 0x39, 0x8c, 0x8a, 0xe6, 0xab, + 0x9b, 0x12, 0x2e, 0xb3, 0x77, 0xb0, 0xc9, 0x26, 0x1d, 0x74, 0x16, 0x6c, 0xc9, 0x04, 0x89, 0xc4, 0xb0, 0xec, 0x90, + 0x90, 0xab, 0xb4, 0x6f, 0xf0, 0x32, 0x54, 0xa7, 0x3a, 0xa7, 0xe8, 0xa7, 0x1d, 0x2f, 0xe9, 0x88, 0x69, 0xa1, 0x2d, + 0xd2, 0x11, 0xa5, 0x03, 0x63, 0xcc, 0x78, 0x85, 0x54, 0x35, 0x30, 0xbc, 0x56, 0x13, 0x4f, 0xb1, 0xbc, 0xf6, 0xe0, + 0x31, 0x91, 0x09, 0x62, 0xdf, 0xc2, 0xd5, 0x46, 0x1c, 0xdc, 0xc8, 0xf2, 0x5a, 0xb9, 0x0a, 0xaf, 0xee, 0xe1, 0x59, + 0xdb, 0x99, 0x7c, 0x48, 0x28, 0x90, 0x58, 0xe6, 0x17, 0x3a, 0x7e, 0xad, 0xa7, 0xbb, 0xe2, 0x25, 0xda, 0x65, 0x07, + 0x48, 0x53, 0x4b, 0xbc, 0x9c, 0xbe, 0xcb, 0x5e, 0x99, 0xd5, 0x4b, 0x4d, 0x1a, 0xdd, 0x42, 0xba, 0xf6, 0x08, 0xf1, + 0x30, 0x1f, 0x0a, 0x76, 0x5f, 0x92, 0x06, 0xe8, 0x0a, 0xb3, 0x5b, 0xfc, 0xa5, 0x8d, 0x0b, 0xf7, 0xe1, 0xa3, 0x88, + 0x48, 0xf4, 0x25, 0xbf, 0x16, 0x2f, 0xfe, 0x93, 0xef, 0x48, 0xc0, 0xe8, 0x22, 0x3e, 0xc9, 0xed, 0x07, 0x56, 0x45, + 0x97, 0x09, 0xcd, 0xfd, 0xe2, 0x34, 0x81, 0xd9, 0x0d, 0xf4, 0xe2, 0x26, 0xf3, 0x35, 0xdd, 0x5d, 0x69, 0xea, 0xde, + 0x1f, 0x8e, 0x55, 0x1d, 0xb5, 0xf9, 0xd2, 0x53, 0x77, 0x93, 0xed, 0x75, 0x8d, 0x4b, 0xdd, 0x42, 0x4d, 0xba, 0x62, + 0x6b, 0x6d, 0x51, 0x9f, 0xa6, 0x79, 0x6f, 0x56, 0xfe, 0x55, 0xb6, 0x75, 0x03, 0xb8, 0x5e, 0x88, 0x75, 0xcd, 0x46, + 0x4d, 0x90, 0xb2, 0xd4, 0x85, 0x68, 0xdf, 0xb8, 0x01, 0x68, 0xb1, 0x86, 0x75, 0x9d, 0x2a, 0xd3, 0x79, 0x9a, 0x8f, + 0x26, 0x04, 0x7d, 0x1f, 0x07, 0x97, 0xf2, 0x46, 0xff, 0x8a, 0x46, 0xad, 0xea, 0xf3, 0xcd, 0x73, 0x1e, 0x9d, 0x08, + 0x6f, 0x1c, 0xd0, 0x2e, 0x8d, 0x11, 0x2f, 0x3d, 0xfd, 0x4c, 0xf9, 0xd2, 0x8d, 0x51, 0x60, 0xbc, 0x00, 0x79, 0x3d, + 0xf4, 0xcb, 0x8d, 0x74, 0x81, 0x5e, 0x55, 0x46, 0x19, 0x5f, 0xdc, 0xcf, 0xbc, 0x7e, 0x25, 0x3e, 0xa0, 0x7c, 0x83, + 0x20, 0x54, 0x58, 0x56, 0x71, 0xc8, 0x20, 0x03, 0x7c, 0xdd, 0xa2, 0x5f, 0x46, 0xbf, 0x68, 0x73, 0x1e, 0xe6, 0x45, + 0xac, 0xbc, 0x39, 0xfc, 0x66, 0x78, 0x79, 0xf5, 0xbc, 0x1e, 0xf3, 0x03, 0xd9, 0xdb, 0xb5, 0xd2, 0x8e, 0x51, 0x3a, + 0x38, 0xc4, 0xae, 0x70, 0x9d, 0x02, 0x30, 0x2a, 0x41, 0xc7, 0x41, 0x54, 0x40, 0x5b, 0xfb, 0x81, 0xd4, 0x8a, 0x32, + 0x52, 0x90, 0x56, 0x6b, 0x38, 0x83, 0xb4, 0x03, 0x4a, 0xb6, 0x4d, 0xb3, 0x18, 0x99, 0x9d, 0x47, 0xba, 0xab, 0x8e, + 0xd3, 0xa2, 0xc1, 0x8b, 0xb3, 0x32, 0x31, 0xee, 0x51, 0x92, 0xab, 0xa4, 0x71, 0x63, 0x78, 0x79, 0xf3, 0x46, 0xa4, + 0x1b, 0xf7, 0x87, 0x4b, 0xae, 0x6e, 0xd9, 0xb9, 0x04, 0xa7, 0x37, 0xbf, 0x04, 0xe2, 0xe3, 0xa6, 0xf9, 0xda, 0x47, + 0x02, 0xee, 0x3f, 0x97, 0x80, 0xbb, 0x62, 0xf2, 0x32, 0x9b, 0xc0, 0xe0, 0x47, 0xe3, 0x9c, 0x69, 0xf0, 0x03, 0x63, + 0xcf, 0x85, 0x5e, 0x0b, 0x18, 0xfc, 0xa8, 0x23, 0x5e, 0xa3, 0x96, 0x90, 0x0e, 0xa3, 0xd2, 0xf7, 0xc4, 0xd8, 0xa0, + 0x3d, 0x32, 0xdd, 0xeb, 0xe0, 0xc6, 0x10, 0x22, 0x2f, 0x4c, 0xa1, 0x70, 0x04, 0xc0, 0xf9, 0xff, 0x0a, 0x92, 0xe6, + 0x9b, 0x43, 0xe1, 0x02, 0xe4, 0xb9, 0xd6, 0x8d, 0x48, 0x87, 0x4e, 0x2c, 0x63, 0xd8, 0x11, 0x33, 0x66, 0x63, 0xa6, + 0xaa, 0xe8, 0x31, 0x27, 0xce, 0x00, 0xca, 0x86, 0xaf, 0xc1, 0xd7, 0x36, 0x03, 0x13, 0xa2, 0x2a, 0x82, 0xc1, 0xbb, + 0xb7, 0xb0, 0x11, 0x74, 0x36, 0x25, 0x46, 0x34, 0x54, 0x43, 0x93, 0xaf, 0xf2, 0x62, 0x3b, 0xa1, 0xb1, 0x3f, 0x06, + 0x99, 0xac, 0x7e, 0xfa, 0xcc, 0x70, 0x1f, 0xeb, 0xf7, 0x3b, 0xd1, 0x56, 0x98, 0x14, 0xe4, 0xd3, 0x96, 0xa5, 0x73, + 0xe9, 0xc5, 0x25, 0x78, 0x69, 0xfa, 0xa6, 0xe6, 0x60, 0x7d, 0x99, 0x83, 0x2c, 0xa7, 0xfe, 0x3c, 0x98, 0x3b, 0x48, + 0x30, 0x75, 0x9e, 0x16, 0x01, 0x2e, 0x21, 0xa2, 0xf4, 0x5c, 0x66, 0x04, 0x36, 0x93, 0x87, 0x99, 0x6c, 0xae, 0xb0, + 0x78, 0x7e, 0xa4, 0x99, 0x9b, 0x51, 0xa1, 0xd7, 0xfd, 0xfc, 0xee, 0xa3, 0x34, 0xfb, 0xba, 0x3c, 0x8e, 0xbb, 0x5b, + 0xcd, 0x19, 0x88, 0xaa, 0x9d, 0xd2, 0x83, 0x5f, 0x64, 0x1d, 0xd8, 0xdb, 0xd6, 0xf4, 0xed, 0xe3, 0x9f, 0x7e, 0xe9, + 0x90, 0x4c, 0x9d, 0xdb, 0xd0, 0x59, 0x74, 0xfa, 0x7e, 0x8f, 0x91, 0x36, 0x5b, 0xe1, 0x88, 0x81, 0xca, 0x53, 0x43, + 0x36, 0xa9, 0x37, 0x71, 0x82, 0x1b, 0x1f, 0x91, 0xaa, 0x4d, 0x7f, 0x03, 0x8f, 0xf5, 0xc3, 0x8f, 0xe6, 0x4e, 0xd5, + 0xed, 0x85, 0xef, 0xdb, 0x3b, 0xa1, 0xdd, 0x3c, 0xbe, 0x56, 0xaf, 0xcd, 0xfb, 0xce, 0x48, 0x5d, 0x50, 0xf4, 0xbc, + 0xf6, 0xbf, 0x52, 0x33, 0x0e, 0xde, 0x36, 0xf7, 0x89, 0x81, 0x6f, 0xc7, 0xe7, 0x31, 0xcf, 0x80, 0xac, 0x65, 0x16, + 0x2d, 0x8d, 0x5c, 0xe3, 0x1a, 0x07, 0x94, 0x15, 0xe2, 0x8a, 0x66, 0xaa, 0x8d, 0x87, 0xa8, 0xeb, 0x1d, 0x2f, 0x67, + 0x2b, 0x5c, 0xdc, 0x62, 0x5a, 0xc5, 0x37, 0x71, 0xe1, 0xec, 0xe6, 0x99, 0xe2, 0x2a, 0x9b, 0x53, 0x75, 0x91, 0xe9, + 0x77, 0x41, 0x57, 0x1d, 0x06, 0xc1, 0x66, 0xd2, 0x87, 0xeb, 0xce, 0x43, 0x17, 0x6e, 0x5c, 0x0c, 0x0f, 0x01, 0xa9, + 0xb4, 0x9c, 0x40, 0x01, 0x63, 0x5b, 0xdc, 0x50, 0x96, 0x38, 0xbe, 0xfe, 0xf9, 0xc0, 0xc3, 0x00, 0xf0, 0x8d, 0x3d, + 0xc4, 0xc4, 0x6c, 0x65, 0x33, 0xcd, 0x09, 0x3f, 0xc3, 0x40, 0x8e, 0x2b, 0xef, 0x34, 0x6e, 0x87, 0xff, 0x33, 0x36, + 0x11, 0x29, 0xa0, 0x49, 0x2c, 0x2c, 0x64, 0xa6, 0xed, 0x14, 0x7d, 0xa2, 0x10, 0xba, 0x62, 0x29, 0x1f, 0x5c, 0xe6, + 0xe0, 0xbb, 0xd6, 0x7b, 0x5f, 0x57, 0x7e, 0x7d, 0x10, 0xb4, 0x54, 0x4d, 0xb0, 0x96, 0x14, 0x0a, 0x49, 0x60, 0xed, + 0x48, 0xa7, 0xf5, 0xb5, 0x1d, 0x28, 0x28, 0x59, 0x16, 0x44, 0xd2, 0xf9, 0x5a, 0x3b, 0xa4, 0x4e, 0xc5, 0x5f, 0xf8, + 0x6f, 0x3f, 0x4d, 0xe0, 0xd7, 0x56, 0xc4, 0x40, 0x7d, 0x1d, 0x5f, 0x77, 0x5f, 0x45, 0xbb, 0x21, 0x6d, 0xd5, 0x8f, + 0xa9, 0xb2, 0x99, 0x91, 0xf2, 0x7e, 0xac, 0xfe, 0xfc, 0xd9, 0x86, 0xa1, 0x69, 0xe2, 0x78, 0x78, 0x73, 0x33, 0x77, + 0x98, 0x29, 0x9f, 0x43, 0xaf, 0x36, 0x56, 0xdf, 0x00, 0x6c, 0x91, 0x93, 0xda, 0x35, 0x51, 0x30, 0xc1, 0x34, 0xd9, + 0x44, 0xdf, 0x3d, 0x52, 0x92, 0x98, 0xb5, 0x47, 0xa1, 0x77, 0x97, 0x32, 0x2d, 0x5a, 0xaa, 0xb9, 0x1a, 0x0b, 0x65, + 0x3a, 0xa9, 0x18, 0x6c, 0x6a, 0xfc, 0xd9, 0x95, 0xf3, 0x99, 0x53, 0x10, 0x79, 0xb1, 0xe1, 0x91, 0xeb, 0x73, 0xc8, + 0xc3, 0xad, 0x7c, 0xd5, 0xe7, 0xe7, 0xf6, 0xc2, 0x2b, 0xde, 0xeb, 0xbd, 0x72, 0x1d, 0x6a, 0xe9, 0x31, 0xcf, 0x8b, + 0xba, 0x5f, 0x96, 0x6b, 0x1c, 0x18, 0x50, 0x3b, 0x01, 0xc6, 0xb9, 0x88, 0x02, 0x0c, 0xf0, 0x4a, 0xba, 0x67, 0x24, + 0x3d, 0x9e, 0xc5, 0x25, 0xfa, 0x91, 0xa1, 0x9a, 0xa7, 0xcd, 0x4b, 0x40, 0x94, 0x2a, 0x3b, 0xce, 0x2d, 0x9d, 0x4c, + 0xb3, 0xa8, 0x2d, 0xbd, 0x33, 0x9d, 0x46, 0xf9, 0xbe, 0x02, 0x80, 0xf4, 0x9d, 0x7e, 0xe4, 0x4c, 0x87, 0x72, 0x73, + 0x80, 0x70, 0xa3, 0x64, 0xc6, 0x8d, 0x89, 0xc2, 0xf3, 0x13, 0x03, 0x22, 0x84, 0xb8, 0x1a, 0xf8, 0xca, 0x4b, 0xda, + 0x27, 0x2a, 0x42, 0x43, 0xfc, 0x80, 0x1e, 0xdc, 0x87, 0x5b, 0xfb, 0xf7, 0x7e, 0x50, 0x55, 0x72, 0xb0, 0x0c, 0x25, + 0x46, 0xe9, 0xde, 0xf8, 0x55, 0x81, 0xdd, 0x4f, 0xcc, 0x4a, 0x2d, 0x11, 0x50, 0x69, 0xf9, 0x7e, 0x71, 0x51, 0xe6, + 0xfc, 0xe9, 0x0f, 0xd7, 0x71, 0x48, 0xa8, 0x91, 0x2f, 0x53, 0xd9, 0x01, 0xf9, 0xf0, 0x1d, 0xfd, 0x2c, 0xca, 0x6a, + 0x0a, 0xbf, 0x8d, 0x2d, 0xdc, 0x5d, 0x16, 0x59, 0xea, 0x00, 0x50, 0x84, 0x63, 0x34, 0x1b, 0x3f, 0xed, 0x92, 0x0c, + 0xed, 0xa2, 0x8d, 0xdf, 0x69, 0x49, 0x33, 0x2a, 0x2a, 0x8a, 0x86, 0xd0, 0x6c, 0x34, 0x43, 0x0a, 0xe6, 0x09, 0x7a, + 0xf1, 0x31, 0x3b, 0xf0, 0xe7, 0x46, 0x49, 0x59, 0xba, 0x35, 0x7f, 0xbd, 0xbd, 0x90, 0xac, 0xa7, 0xac, 0x6e, 0x8a, + 0x30, 0x59, 0xd0, 0x0c, 0x7d, 0xe5, 0xff, 0x30, 0x80, 0xa7, 0x90, 0x97, 0x2b, 0x16, 0xfe, 0x5e, 0xd5, 0x3d, 0x7c, + 0xb9, 0x11, 0xc7, 0xf5, 0xa2, 0x29, 0x1f, 0xb4, 0x0f, 0x21, 0xa9, 0xea, 0x7b, 0x1c, 0xf6, 0x9c, 0xfa, 0x8f, 0x85, + 0x4d, 0xb8, 0x15, 0x05, 0x02, 0xcf, 0x66, 0x2d, 0x9a, 0x88, 0xa9, 0xcb, 0x8c, 0x08, 0x63, 0x49, 0x10, 0xc4, 0xad, + 0xce, 0x79, 0x3e, 0xca, 0xcd, 0xc9, 0x49, 0x9e, 0xb7, 0xb3, 0xeb, 0x68, 0xdf, 0x9b, 0x5b, 0x29, 0xab, 0x5c, 0x37, + 0x84, 0x16, 0x2f, 0x5d, 0x5c, 0xa5, 0x32, 0x4c, 0xcb, 0x55, 0x71, 0x43, 0xab, 0xd6, 0xb4, 0x6a, 0xc0, 0x07, 0x19, + 0xb4, 0x2a, 0x4f, 0x9e, 0x76, 0x95, 0x9b, 0x6c, 0xd3, 0x97, 0x15, 0x5d, 0x77, 0xc0, 0xf0, 0x4a, 0x61, 0x6d, 0xd7, + 0xc1, 0x36, 0x9c, 0x68, 0x70, 0xde, 0xb7, 0xdb, 0x06, 0x90, 0xbc, 0xdd, 0xc5, 0x0a, 0x1e, 0x4e, 0x8e, 0xff, 0x62, + 0x87, 0xe2, 0xf7, 0xbe, 0x68, 0x65, 0x14, 0x23, 0x23, 0x34, 0xf5, 0xaf, 0x8e, 0x08, 0xff, 0xc2, 0x77, 0xa5, 0xf6, + 0x98, 0xab, 0x08, 0x65, 0xed, 0x66, 0x15, 0xfb, 0x83, 0x24, 0xbf, 0x34, 0x49, 0xf5, 0x36, 0x4f, 0x4f, 0xb0, 0x4a, + 0x41, 0x7b, 0x73, 0xd8, 0x60, 0x6b, 0xae, 0x8d, 0x14, 0x37, 0x98, 0xd0, 0xc6, 0xff, 0x60, 0x23, 0xc0, 0x27, 0x52, + 0xbc, 0xe0, 0x72, 0x5c, 0x59, 0x8a, 0xe6, 0x44, 0xf3, 0xd2, 0xc8, 0x3e, 0x85, 0x79, 0x3e, 0xaa, 0x90, 0xeb, 0xe6, + 0x3c, 0x50, 0x2f, 0x87, 0x3e, 0x71, 0xca, 0x38, 0xcf, 0x8e, 0x70, 0x3e, 0x95, 0xd3, 0xae, 0xde, 0xac, 0x2d, 0x43, + 0x5c, 0x27, 0x2b, 0x42, 0x48, 0x3e, 0x8c, 0x53, 0x51, 0xa4, 0xd8, 0xbe, 0xda, 0x39, 0xcf, 0xf1, 0x95, 0x21, 0x0a, + 0x27, 0x5c, 0x44, 0x63, 0x4a, 0xe8, 0x4f, 0x5e, 0x50, 0x74, 0x67, 0xd4, 0x24, 0x98, 0xb5, 0x3a, 0x99, 0x04, 0xce, + 0xd4, 0x7f, 0xc0, 0xc2, 0xd0, 0x1b, 0x20, 0x3a, 0xa8, 0xa9, 0x32, 0x3f, 0xba, 0x5b, 0x71, 0xe3, 0x93, 0x8e, 0xcc, + 0x68, 0x13, 0x33, 0xce, 0x94, 0xda, 0xe2, 0x6b, 0xb3, 0x7b, 0x8e, 0xc0, 0xec, 0x6e, 0x01, 0xc1, 0x22, 0x8e, 0x54, + 0x68, 0xd5, 0x9f, 0xab, 0x77, 0xbb, 0x48, 0x80, 0x73, 0x42, 0x1b, 0x03, 0x2d, 0x3e, 0xe3, 0x74, 0x35, 0xe7, 0xdb, + 0x38, 0xec, 0x18, 0x32, 0x55, 0x9c, 0xdf, 0x45, 0x9f, 0xfb, 0x99, 0x00, 0xdd, 0x2d, 0x44, 0x3a, 0xdf, 0x5b, 0x17, + 0x6a, 0x16, 0x0e, 0x21, 0x6c, 0x7f, 0x12, 0x25, 0x64, 0xa8, 0xbf, 0x16, 0x7e, 0x8e, 0xda, 0xab, 0x97, 0x5a, 0x26, + 0x1b, 0x7e, 0x30, 0xa2, 0xc5, 0xa3, 0x00, 0x92, 0x0c, 0xa3, 0xf7, 0xcf, 0xdf, 0xdc, 0xb0, 0x9f, 0xa1, 0xf0, 0x0c, + 0xe6, 0x11, 0x50, 0xc0, 0xcd, 0xdd, 0x4f, 0xe8, 0xda, 0x52, 0x2e, 0x08, 0x67, 0xb2, 0x0d, 0x09, 0x56, 0xc6, 0xb9, + 0x66, 0x6b, 0xe3, 0x45, 0xc3, 0x09, 0xe9, 0x88, 0x3a, 0x68, 0x4c, 0x7a, 0x9e, 0x33, 0x9a, 0xc7, 0x58, 0xfd, 0xc9, + 0x99, 0x60, 0xf9, 0x81, 0x8d, 0xc9, 0x15, 0x04, 0x55, 0x8b, 0x82, 0x58, 0xd3, 0x1d, 0xed, 0xc0, 0x70, 0x7f, 0x29, + 0x9e, 0x12, 0xe4, 0x6f, 0x97, 0x98, 0x38, 0x2a, 0x14, 0x72, 0xd6, 0xb8, 0xa1, 0x6f, 0x44, 0xb0, 0x5e, 0x8d, 0x07, + 0xbd, 0xe7, 0x4b, 0x91, 0xa5, 0xaa, 0x73, 0xbb, 0x51, 0x0e, 0xcd, 0x30, 0x61, 0x8c, 0x13, 0x5a, 0xca, 0x37, 0x64, + 0x25, 0x76, 0x36, 0xb5, 0x14, 0x4e, 0xff, 0x69, 0xc8, 0x53, 0xb1, 0x85, 0x80, 0xaa, 0xcf, 0x41, 0x93, 0x13, 0xd3, + 0xd4, 0x9d, 0x37, 0x72, 0x67, 0x1e, 0x60, 0x54, 0x53, 0x36, 0x3a, 0xa1, 0x77, 0xcc, 0x47, 0x66, 0xf0, 0x33, 0xb2, + 0x3b, 0x0f, 0x59, 0x2d, 0x93, 0xcb, 0x24, 0x3f, 0xeb, 0x8d, 0xef, 0x1c, 0x20, 0xb1, 0x8e, 0x41, 0xc5, 0xe6, 0x59, + 0x57, 0x59, 0xab, 0x2a, 0xd3, 0x4d, 0xfc, 0xaa, 0x5b, 0x1a, 0x28, 0x78, 0xa2, 0x02, 0x85, 0x48, 0x9a, 0x92, 0xa0, + 0x56, 0x0f, 0x21, 0x47, 0x94, 0xa3, 0xbb, 0x45, 0xcc, 0x75, 0xbc, 0xaa, 0x6c, 0xfc, 0x1b, 0xd3, 0x47, 0x8b, 0xda, + 0xa1, 0xdb, 0xcf, 0x6c, 0x54, 0xc3, 0x22, 0x55, 0x4e, 0x61, 0xc8, 0x8f, 0x38, 0x8f, 0x35, 0x09, 0xb2, 0x71, 0x32, + 0x00, 0x05, 0xbd, 0x54, 0xe0, 0x7f, 0x33, 0xe7, 0x8c, 0x15, 0x2b, 0x17, 0xa0, 0x22, 0x58, 0xbb, 0xe6, 0x5f, 0xf7, + 0x69, 0xc4, 0x28, 0x54, 0x67, 0x0f, 0xc0, 0xac, 0x85, 0x0c, 0xe4, 0x57, 0xeb, 0x6d, 0x28, 0x17, 0xb6, 0xe1, 0xa4, + 0xf5, 0xba, 0xfa, 0x2c, 0xe4, 0x22, 0xad, 0xa6, 0x68, 0xb3, 0x3a, 0x4f, 0x9d, 0x15, 0x4c, 0xf8, 0x25, 0x9c, 0x9b, + 0x4e, 0x90, 0xa5, 0xc6, 0x91, 0xf2, 0x30, 0xfb, 0x38, 0x6a, 0x9d, 0x59, 0x39, 0x76, 0xa1, 0x0a, 0xdb, 0x3c, 0xcf, + 0x9c, 0x30, 0xbd, 0xd8, 0x93, 0xaa, 0xda, 0x95, 0x95, 0xee, 0xe6, 0x5a, 0xcc, 0x9b, 0x5d, 0x1d, 0x49, 0x2d, 0x31, + 0xad, 0x93, 0xfd, 0x89, 0x95, 0x59, 0x81, 0xe0, 0x6d, 0xe8, 0x36, 0x42, 0x64, 0x17, 0xec, 0x47, 0x5a, 0xbc, 0x74, + 0x4b, 0xae, 0x8e, 0x60, 0x11, 0x5a, 0x45, 0xff, 0x50, 0x5a, 0x18, 0x90, 0xea, 0x8a, 0x92, 0xd2, 0x48, 0xff, 0xad, + 0xcc, 0x70, 0x92, 0x59, 0xbd, 0x77, 0xa8, 0x3d, 0x16, 0x41, 0xbd, 0x1f, 0x93, 0x1e, 0xe5, 0x5c, 0x2f, 0x05, 0x9c, + 0x2c, 0x81, 0xd9, 0x0b, 0x76, 0x0b, 0x00, 0x79, 0xed, 0x6d, 0x2d, 0x15, 0x99, 0x70, 0xf9, 0x3c, 0x99, 0x73, 0x69, + 0x15, 0x78, 0x05, 0xbd, 0x6b, 0x6f, 0xb0, 0xb2, 0x10, 0xdc, 0x2f, 0x72, 0xa6, 0xcf, 0x0a, 0x92, 0x4a, 0x43, 0xbc, + 0xb4, 0x04, 0xde, 0x4a, 0xaa, 0x29, 0x70, 0x6b, 0xd9, 0x70, 0x6d, 0xda, 0x46, 0x1f, 0xea, 0xfd, 0x78, 0xc7, 0x68, + 0x15, 0xfc, 0xe7, 0xd3, 0xdf, 0x2a, 0x76, 0x47, 0xf0, 0x6c, 0x15, 0xaa, 0xac, 0xeb, 0x61, 0x22, 0xd9, 0xfe, 0x6a, + 0xe7, 0x0b, 0xa0, 0x45, 0xb8, 0x52, 0xba, 0x26, 0x01, 0x9d, 0xd4, 0x14, 0x0b, 0xdc, 0xa6, 0xc0, 0x2c, 0xa3, 0x9f, + 0xc2, 0xb7, 0x91, 0x6b, 0x1c, 0xa9, 0x46, 0x34, 0x99, 0x71, 0xb8, 0x20, 0x9a, 0xbc, 0xb9, 0x5b, 0x15, 0x01, 0x04, + 0x07, 0x68, 0x2b, 0xef, 0x8c, 0xd3, 0x3b, 0xf7, 0x91, 0xd6, 0x39, 0xf0, 0x43, 0x37, 0xd9, 0x2e, 0x75, 0x68, 0xd5, + 0x12, 0xbd, 0x5d, 0x47, 0x8d, 0x06, 0x19, 0xb6, 0x44, 0x31, 0xb6, 0xe0, 0xe3, 0x13, 0x3e, 0x66, 0x90, 0x55, 0x72, + 0xc0, 0xd7, 0x8b, 0x06, 0x2a, 0x16, 0x15, 0xc8, 0xdf, 0x85, 0x50, 0xa8, 0xa3, 0x6d, 0xb4, 0x00, 0x40, 0x7d, 0x82, + 0x12, 0x3a, 0x71, 0x4b, 0xbd, 0x01, 0x55, 0xbe, 0x0f, 0x29, 0x95, 0x50, 0xdf, 0x54, 0x64, 0xca, 0xd1, 0x52, 0x31, + 0x03, 0x84, 0x91, 0x47, 0x26, 0x43, 0x6d, 0xe2, 0x2c, 0x62, 0xee, 0xde, 0x32, 0xaa, 0x7e, 0x6c, 0xcf, 0x3b, 0x59, + 0xda, 0x6b, 0x11, 0x73, 0x95, 0x33, 0xde, 0x07, 0x50, 0x02, 0x07, 0x57, 0x81, 0xb9, 0x67, 0xaa, 0x77, 0x55, 0xbc, + 0xcf, 0x2c, 0xb3, 0x86, 0x07, 0x4a, 0xcf, 0x2e, 0xc6, 0xd7, 0x98, 0xeb, 0xcf, 0xad, 0x89, 0x67, 0xf1, 0x5f, 0x1f, + 0xb7, 0x7c, 0x9e, 0xc3, 0xef, 0x26, 0xda, 0xd5, 0x19, 0xb8, 0x72, 0xc2, 0x3e, 0x4f, 0xd0, 0xae, 0x1b, 0xbc, 0x5b, + 0xb6, 0x16, 0x6b, 0x9e, 0xbc, 0x09, 0xef, 0x5b, 0x33, 0x87, 0xaa, 0xaa, 0x3c, 0xae, 0x36, 0x10, 0x48, 0xe3, 0x3b, + 0x93, 0xcc, 0xa0, 0x6b, 0x48, 0x9a, 0xe9, 0x46, 0xf0, 0xbb, 0x6f, 0xdd, 0x82, 0x8e, 0x34, 0xb0, 0xd8, 0xda, 0x3b, + 0x81, 0xcf, 0x4c, 0x86, 0x15, 0xb3, 0xe4, 0x0c, 0x7e, 0x7b, 0x1b, 0xc2, 0xd3, 0xd6, 0x9b, 0x72, 0xb9, 0x22, 0x8b, + 0x3e, 0x0f, 0xfd, 0x8a, 0x7e, 0x93, 0x96, 0xe5, 0x71, 0x0f, 0x55, 0x72, 0xff, 0x57, 0xb1, 0xe6, 0x34, 0xfa, 0x2a, + 0xa8, 0x5f, 0xbd, 0x63, 0xc0, 0xe6, 0xb6, 0xf6, 0x16, 0x72, 0xba, 0xb4, 0xc8, 0x3d, 0x18, 0x9a, 0xe9, 0xfd, 0x8f, + 0x02, 0x61, 0xc9, 0x9e, 0xd2, 0xd6, 0xf3, 0xe4, 0xa2, 0x97, 0xea, 0xdc, 0x88, 0x7f, 0xcb, 0x95, 0xdf, 0xbc, 0x8e, + 0x1a, 0xa5, 0x89, 0xff, 0x83, 0xff, 0xb5, 0x51, 0x26, 0x97, 0x3a, 0xb9, 0xd3, 0x0e, 0xca, 0xa3, 0x2e, 0x39, 0x1e, + 0xc5, 0x52, 0x33, 0x1a, 0xc5, 0x33, 0x61, 0x9f, 0xb9, 0xa0, 0x2a, 0xf4, 0x58, 0x36, 0x00, 0x6b, 0x18, 0x40, 0x32, + 0xa0, 0x26, 0x67, 0xc4, 0xa9, 0x3b, 0xc1, 0xad, 0x86, 0xd2, 0x55, 0x64, 0x46, 0x72, 0x5a, 0x78, 0x97, 0xf7, 0x2b, + 0x31, 0x44, 0xb9, 0xac, 0x6f, 0x52, 0x47, 0x54, 0x7c, 0x15, 0x5d, 0x4a, 0xdf, 0x22, 0x36, 0xda, 0x7e, 0xd8, 0xd0, + 0x8e, 0x39, 0x60, 0xe4, 0xbd, 0xd1, 0xa8, 0xe5, 0xcc, 0x20, 0xe6, 0xa7, 0x67, 0xd0, 0xc4, 0x01, 0xb3, 0x15, 0x43, + 0xcc, 0x51, 0x72, 0x55, 0x6a, 0xd2, 0x18, 0x14, 0x13, 0x3b, 0x71, 0xa4, 0x3e, 0xbf, 0xee, 0x4e, 0x0a, 0x3f, 0xcc, + 0xa9, 0xa9, 0x75, 0x3f, 0x80, 0x2d, 0x3e, 0xd5, 0xfa, 0x1d, 0x55, 0x18, 0x98, 0xed, 0x1a, 0x22, 0xfc, 0x8d, 0x8a, + 0x8b, 0xf4, 0x24, 0xfd, 0x3b, 0xf5, 0x55, 0x75, 0x1b, 0x31, 0x64, 0xcc, 0xec, 0x04, 0x6b, 0x26, 0x07, 0xb4, 0x2c, + 0xce, 0xcc, 0x2c, 0xe5, 0xb3, 0x71, 0x2c, 0xb1, 0x16, 0x58, 0x6c, 0x79, 0x9b, 0x07, 0x77, 0x68, 0x41, 0xa8, 0x48, + 0x9c, 0x58, 0xb6, 0x31, 0x73, 0x13, 0xda, 0xe0, 0x09, 0xb1, 0xa2, 0x5f, 0xf0, 0x8d, 0x10, 0x3f, 0x3a, 0xe8, 0x4d, + 0x6a, 0xa7, 0xd1, 0x95, 0xd1, 0xc1, 0x38, 0xbc, 0xe6, 0xbf, 0x5d, 0x37, 0x11, 0x74, 0x89, 0xb8, 0xa9, 0x80, 0x4b, + 0x8e, 0x9f, 0x62, 0x50, 0x27, 0x37, 0x83, 0x4d, 0x7c, 0xa7, 0xe3, 0xad, 0x1d, 0xac, 0x77, 0xc0, 0xb9, 0x3f, 0xfe, + 0x3b, 0x71, 0x1b, 0xa5, 0x5c, 0x9e, 0xfc, 0x16, 0x3b, 0x19, 0xa2, 0x39, 0x4f, 0x6f, 0x1d, 0x5e, 0x2d, 0xd2, 0x4c, + 0x75, 0x6a, 0x7a, 0x73, 0x3c, 0xd2, 0x09, 0xfc, 0x95, 0xf1, 0xec, 0x82, 0xe3, 0xb4, 0x60, 0x05, 0xe5, 0x03, 0x7e, + 0x0f, 0xa5, 0x1a, 0xae, 0x5c, 0xf4, 0x75, 0x40, 0x3d, 0x53, 0x7c, 0x59, 0x8d, 0xb5, 0x6f, 0xd2, 0x2d, 0xf8, 0xc3, + 0x1e, 0x16, 0x65, 0x5d, 0x3f, 0x3f, 0x7f, 0xb3, 0x97, 0x8d, 0xf4, 0xfc, 0x77, 0x60, 0x49, 0xfd, 0x53, 0x09, 0xaa, + 0xf6, 0xa6, 0xe6, 0x8d, 0x83, 0x78, 0x1a, 0x53, 0x1a, 0xd1, 0xff, 0xd2, 0x31, 0x75, 0x55, 0x06, 0x57, 0xc0, 0x3c, + 0x78, 0x12, 0x93, 0xa5, 0x9f, 0x8d, 0xa9, 0xa5, 0xf0, 0x6b, 0xcc, 0x4f, 0x6a, 0xf5, 0x90, 0xe3, 0x3c, 0xe4, 0xe2, + 0x95, 0xa4, 0x7b, 0x6f, 0x56, 0xdf, 0xce, 0x16, 0x06, 0xa7, 0xf9, 0x2a, 0x80, 0xff, 0xc7, 0x39, 0x01, 0x74, 0xf7, + 0xcc, 0xc5, 0x63, 0x9e, 0x7c, 0x78, 0xb3, 0xb5, 0x9a, 0x16, 0xe4, 0xdd, 0x79, 0x2a, 0xcd, 0xd6, 0x82, 0x58, 0x9b, + 0x7a, 0x34, 0x41, 0xbd, 0xd3, 0x5b, 0xd3, 0xbe, 0xb1, 0x3e, 0x8c, 0x86, 0xbe, 0x23, 0x0b, 0x85, 0xe7, 0x8f, 0x09, + 0x67, 0xc7, 0xb3, 0x89, 0x89, 0x61, 0xbf, 0x53, 0xed, 0x62, 0x60, 0xab, 0xab, 0x15, 0x0b, 0xc6, 0xfb, 0x81, 0xee, + 0x9b, 0x4c, 0x96, 0x72, 0x3c, 0xc6, 0x4c, 0x25, 0x6a, 0xda, 0xb7, 0xd4, 0xb2, 0xbb, 0x17, 0x28, 0x23, 0x66, 0xa9, + 0x81, 0xd9, 0x17, 0xaf, 0x0a, 0x0c, 0x14, 0xaa, 0xf3, 0xe1, 0x8d, 0x15, 0x94, 0xc1, 0x47, 0xf3, 0xba, 0x94, 0x15, + 0x04, 0x8e, 0x49, 0xeb, 0xc0, 0xfd, 0xf2, 0x40, 0x8f, 0x14, 0x7d, 0xf1, 0x36, 0x0a, 0x58, 0x5e, 0xd7, 0x53, 0x83, + 0xb7, 0x1a, 0xae, 0x8d, 0xf5, 0x32, 0xe3, 0x97, 0xf5, 0x40, 0x61, 0x14, 0x5c, 0xdc, 0x99, 0x5d, 0x8c, 0xc3, 0xbe, + 0xdb, 0x2a, 0x67, 0x4a, 0xa6, 0x5c, 0xaf, 0x6c, 0x7e, 0xc6, 0x40, 0xcf, 0x9b, 0xb5, 0xac, 0x71, 0xfd, 0xc4, 0xef, + 0x6e, 0x8e, 0x2b, 0xe3, 0x6c, 0x14, 0xba, 0xff, 0x23, 0x1b, 0x6a, 0x7c, 0x03, 0x35, 0x82, 0x90, 0x83, 0xab, 0xa5, + 0xb2, 0x34, 0xd2, 0x7e, 0xb6, 0x9f, 0xbe, 0x4f, 0x1e, 0x2b, 0xc8, 0xf2, 0x5f, 0xb2, 0x62, 0x63, 0x0e, 0x93, 0xc9, + 0xaf, 0x3a, 0x85, 0x74, 0x40, 0xd5, 0xa2, 0x1d, 0xa3, 0x57, 0xd9, 0x09, 0x41, 0x7d, 0x31, 0x10, 0x75, 0x00, 0x66, + 0x5b, 0xa5, 0xbc, 0x2c, 0x06, 0x9a, 0x49, 0x94, 0x2d, 0x07, 0x7d, 0x6d, 0xf8, 0xf0, 0x1a, 0xbc, 0x6a, 0x94, 0xd5, + 0xf4, 0xb2, 0x9a, 0x42, 0xa5, 0xd3, 0xa6, 0x95, 0xe0, 0x35, 0x79, 0xba, 0x5f, 0xea, 0x5c, 0x77, 0x4d, 0x1c, 0xfc, + 0x6c, 0xf5, 0x7b, 0xb0, 0xa3, 0xc9, 0xb1, 0x2b, 0xb9, 0xb9, 0xc1, 0x71, 0x1e, 0x73, 0x5c, 0xb9, 0x40, 0x44, 0xcd, + 0x42, 0x2b, 0x18, 0xd0, 0x22, 0x75, 0xa7, 0xbe, 0xbb, 0xc4, 0x6e, 0x02, 0xd8, 0x2a, 0xf6, 0x1e, 0x24, 0xdb, 0x3e, + 0x4b, 0x6f, 0x74, 0x60, 0x3b, 0x78, 0x8b, 0x26, 0xbe, 0x31, 0x57, 0xaa, 0xa9, 0xc8, 0xea, 0x8c, 0xea, 0xb0, 0x73, + 0x9a, 0xcf, 0x0f, 0x9a, 0xb1, 0x72, 0x9b, 0x84, 0xdb, 0x31, 0x52, 0x27, 0x88, 0x05, 0x2a, 0x56, 0xd3, 0xa0, 0x5a, + 0x46, 0x50, 0xb9, 0x49, 0xfa, 0xca, 0x23, 0x59, 0x8d, 0x15, 0xeb, 0x67, 0xa0, 0x6e, 0xae, 0xdc, 0xb8, 0x6d, 0x86, + 0xac, 0x5a, 0xae, 0x70, 0x46, 0x20, 0x86, 0xc6, 0x67, 0xd6, 0x48, 0x54, 0x5b, 0x09, 0xe8, 0xc0, 0xe1, 0x22, 0x05, + 0xb5, 0xbb, 0x2d, 0xaf, 0xdf, 0x8d, 0xd2, 0x23, 0x4a, 0x54, 0xd4, 0x8a, 0xca, 0x29, 0xdd, 0x50, 0xae, 0x9e, 0x89, + 0x26, 0x60, 0xa2, 0x51, 0x6c, 0xa4, 0x16, 0xe5, 0xed, 0x56, 0x85, 0xec, 0xe5, 0xba, 0x7f, 0x79, 0xff, 0x91, 0xd3, + 0xb0, 0xe9, 0x3b, 0x21, 0x69, 0x30, 0x48, 0x45, 0xc2, 0x07, 0xec, 0xa8, 0xb7, 0xe4, 0x9b, 0xcc, 0x90, 0xa9, 0x23, + 0x63, 0xd4, 0x97, 0x58, 0xf9, 0xd2, 0xfc, 0xdd, 0xab, 0x7b, 0xa3, 0x80, 0xad, 0xdf, 0xe9, 0xda, 0xdc, 0x94, 0xc2, + 0xdb, 0x0e, 0x61, 0x0a, 0xe9, 0x26, 0x23, 0xd2, 0xfa, 0xcf, 0x54, 0xfd, 0x66, 0xe2, 0x77, 0x35, 0xb6, 0x6b, 0x82, + 0x3c, 0xd1, 0x9b, 0xcd, 0xe6, 0x9c, 0xaa, 0x59, 0x00, 0x20, 0xfe, 0xab, 0xcd, 0x37, 0xf3, 0x95, 0x2a, 0x1a, 0x88, + 0xe0, 0xb3, 0xd0, 0xf5, 0x6f, 0x64, 0x54, 0x7d, 0x1a, 0xd1, 0xbf, 0x06, 0x49, 0x08, 0x65, 0xce, 0xe6, 0x7a, 0x43, + 0x50, 0xc7, 0x9e, 0x67, 0x6f, 0xf5, 0x29, 0x4c, 0xfc, 0x8f, 0xbc, 0xfa, 0x39, 0xee, 0x55, 0x14, 0xa5, 0xd8, 0xd5, + 0xa1, 0x71, 0x98, 0xc2, 0x4d, 0xa6, 0x5b, 0xef, 0x92, 0x21, 0xe0, 0xf4, 0x5f, 0x1c, 0x0e, 0x23, 0x73, 0xd3, 0x9d, + 0x0d, 0x0c, 0x06, 0x05, 0x23, 0x29, 0x96, 0x21, 0x94, 0xb9, 0xc1, 0x5c, 0xbc, 0x75, 0x80, 0x2f, 0x5d, 0x90, 0xe5, + 0x9b, 0x85, 0x8e, 0xf1, 0xd9, 0xb7, 0xe7, 0x1d, 0x1f, 0xa9, 0xd0, 0x32, 0x4b, 0x04, 0x29, 0xa4, 0x2f, 0xfe, 0x19, + 0x46, 0x2d, 0x8f, 0x89, 0x0b, 0xa6, 0xd5, 0xc3, 0x4b, 0x29, 0xc0, 0xce, 0x73, 0x50, 0x53, 0x2f, 0xa0, 0x8e, 0x85, + 0x9b, 0xca, 0x03, 0xbb, 0x12, 0x43, 0x6a, 0x53, 0x04, 0x30, 0x7e, 0xeb, 0x08, 0x11, 0x0f, 0xd2, 0xa0, 0x54, 0x4b, + 0xc8, 0x78, 0xb3, 0x9c, 0x58, 0x77, 0x17, 0x03, 0xe2, 0x9b, 0x23, 0x06, 0xb4, 0xa5, 0x66, 0x18, 0x1e, 0xe7, 0x5f, + 0x4b, 0x79, 0x13, 0x32, 0x88, 0x5d, 0x03, 0x5d, 0x49, 0xb9, 0x59, 0xfb, 0xe1, 0x18, 0xa8, 0xda, 0x86, 0x44, 0xe9, + 0x37, 0xd5, 0x95, 0x75, 0x25, 0x56, 0xa8, 0x56, 0x3b, 0xbb, 0x37, 0x79, 0x9d, 0x36, 0x34, 0xc3, 0x53, 0xb8, 0xb9, + 0x52, 0xdb, 0xc6, 0xae, 0xed, 0xff, 0x24, 0x73, 0xd0, 0x14, 0xac, 0x95, 0x1f, 0xec, 0x78, 0x36, 0xd1, 0xbf, 0x9e, + 0xd5, 0x99, 0x74, 0xfd, 0x51, 0x79, 0x96, 0x9f, 0x5b, 0x75, 0x50, 0x81, 0x87, 0xd3, 0x22, 0xff, 0xd1, 0xd7, 0x70, + 0x0d, 0xbd, 0x27, 0xef, 0x7a, 0xbb, 0xc1, 0x18, 0xbe, 0x78, 0x13, 0x4f, 0xfb, 0x9b, 0x4c, 0xe0, 0x14, 0xc2, 0xb6, + 0x75, 0x02, 0xd6, 0x3a, 0x7d, 0x47, 0x52, 0xd0, 0x22, 0xbf, 0x45, 0xb3, 0x5f, 0x2b, 0x73, 0xc3, 0x2f, 0x1c, 0xc5, + 0xcd, 0xa5, 0x74, 0x91, 0x3c, 0x59, 0xa5, 0xed, 0x30, 0xcb, 0x20, 0x8e, 0xc0, 0x72, 0xf4, 0x73, 0x27, 0x72, 0xeb, + 0x63, 0x35, 0xcc, 0xee, 0x38, 0x0e, 0xc5, 0xa8, 0x7e, 0xaa, 0x23, 0x52, 0x1e, 0x26, 0x03, 0x36, 0x35, 0xa1, 0xc5, + 0x58, 0x58, 0xba, 0x24, 0x41, 0x0a, 0x74, 0x80, 0x5a, 0x22, 0x73, 0x52, 0x8b, 0xec, 0x8a, 0x71, 0xcf, 0xb6, 0x62, + 0xe9, 0xda, 0xc7, 0x47, 0x9d, 0x3d, 0x03, 0x37, 0x8e, 0x93, 0x93, 0xcd, 0x9d, 0x2d, 0xc0, 0x4a, 0x8f, 0xc9, 0xe9, + 0xec, 0x87, 0x12, 0xcb, 0x35, 0xd9, 0x7d, 0x54, 0xb4, 0xbb, 0xef, 0xe0, 0x88, 0x2c, 0x11, 0xa3, 0xff, 0xb4, 0xce, + 0x64, 0xad, 0xbf, 0x91, 0x03, 0xf8, 0x16, 0x1a, 0xf5, 0x82, 0xc5, 0x80, 0xcb, 0xdd, 0xe5, 0x5d, 0x8d, 0x0f, 0xbc, + 0x32, 0xe1, 0xac, 0x2a, 0xd7, 0xdc, 0x6c, 0x64, 0x9a, 0xa8, 0x09, 0xe9, 0xff, 0x2b, 0x5b, 0x0d, 0xb1, 0x05, 0x78, + 0x32, 0xf6, 0xcd, 0x9b, 0x0d, 0x4c, 0xcd, 0x42, 0x8b, 0x2b, 0xec, 0x43, 0x1c, 0xa7, 0x22, 0xba, 0xb9, 0x81, 0x1a, + 0x7e, 0x90, 0xd0, 0xca, 0x77, 0x09, 0x55, 0xff, 0x41, 0x34, 0xf6, 0xbd, 0x57, 0x59, 0xc2, 0x41, 0xcf, 0x41, 0xa6, + 0xd1, 0xbd, 0x66, 0xd2, 0x93, 0xbd, 0xb9, 0x31, 0x54, 0x8d, 0xbc, 0x56, 0xee, 0x1e, 0xdc, 0x2d, 0xe1, 0xf9, 0xd9, + 0x9c, 0xf7, 0xe6, 0x23, 0xe1, 0x51, 0x37, 0x5e, 0xf5, 0x0f, 0x71, 0x87, 0xaf, 0xae, 0x1f, 0x27, 0x62, 0x45, 0x11, + 0x17, 0x1f, 0xd6, 0xbb, 0x5a, 0x79, 0xdc, 0x3a, 0x3c, 0xc5, 0xfb, 0x06, 0x74, 0x4a, 0x4a, 0x75, 0xde, 0x35, 0x81, + 0xae, 0xe0, 0xfb, 0x73, 0xed, 0xf2, 0xfd, 0x8d, 0xb3, 0x6e, 0xcb, 0xcd, 0xc6, 0xc1, 0x1b, 0x93, 0x2e, 0x5a, 0xb0, + 0xeb, 0x3b, 0x9e, 0xbe, 0xf9, 0x38, 0xfc, 0x68, 0x64, 0x58, 0xd5, 0x58, 0x40, 0x1b, 0x5a, 0xbe, 0x20, 0xef, 0xc9, + 0x22, 0x46, 0x77, 0xa5, 0xc9, 0x53, 0x72, 0xbb, 0xf9, 0x3e, 0x44, 0xbc, 0x59, 0x07, 0xba, 0x72, 0xd0, 0xdd, 0xf8, + 0xd7, 0xfa, 0xe5, 0x65, 0xe9, 0xde, 0xbc, 0x7a, 0xee, 0xb5, 0x90, 0x30, 0xa9, 0xf3, 0xc9, 0x20, 0x97, 0x0f, 0x86, + 0xc8, 0xc8, 0xe6, 0x18, 0xcf, 0x24, 0x65, 0x09, 0xbc, 0x1c, 0x57, 0x19, 0xbc, 0x33, 0x6d, 0xe4, 0x1f, 0xf7, 0x44, + 0x22, 0x1e, 0x0c, 0xb4, 0x6d, 0x50, 0x28, 0x4c, 0xea, 0xed, 0x62, 0x88, 0x7b, 0x94, 0x31, 0xd1, 0x3c, 0x76, 0x7d, + 0xbf, 0x46, 0x27, 0x47, 0x6f, 0x66, 0xd4, 0x6e, 0xff, 0x61, 0x35, 0x05, 0x7a, 0xe2, 0xe0, 0x89, 0xba, 0xa2, 0x12, + 0x1e, 0xff, 0xf4, 0x89, 0xf6, 0x4b, 0x7a, 0x38, 0x55, 0x87, 0xe7, 0xab, 0xf8, 0xca, 0x45, 0x55, 0x2b, 0x7e, 0x09, + 0xfa, 0x70, 0xb1, 0xc8, 0xc9, 0xf3, 0x48, 0xaf, 0x6c, 0xf6, 0x6a, 0x66, 0x13, 0xc5, 0x9d, 0xc2, 0xf2, 0xb8, 0xf9, + 0x8a, 0xe6, 0xd4, 0x90, 0x68, 0xf5, 0xef, 0x43, 0x7f, 0x0c, 0xf6, 0x36, 0xfb, 0xbf, 0x25, 0x71, 0xe6, 0xe9, 0x33, + 0xe2, 0x77, 0xb3, 0xf5, 0x92, 0x1f, 0xba, 0xbf, 0xc4, 0xbf, 0x8f, 0x4d, 0xa0, 0x59, 0xa6, 0x34, 0x51, 0xc6, 0x30, + 0x00, 0x38, 0x00, 0x7e, 0x6d, 0xfe, 0xe2, 0xdf, 0x2d, 0x9b, 0xdc, 0xcc, 0xe2, 0xa4, 0xc5, 0x9d, 0x7f, 0xfa, 0x42, + 0x69, 0x69, 0x9c, 0xe6, 0x01, 0x41, 0x35, 0xae, 0x4d, 0x8f, 0x8d, 0x64, 0x1e, 0xc8, 0x3a, 0x18, 0xb6, 0x96, 0x9c, + 0x60, 0x02, 0x22, 0xf7, 0xaa, 0xe6, 0x4b, 0x97, 0x6a, 0x65, 0x96, 0xa9, 0xcd, 0xd7, 0xd2, 0xc1, 0x60, 0xdf, 0x41, + 0xcc, 0xf7, 0xb9, 0xc7, 0x6c, 0x26, 0x3f, 0xb7, 0xb4, 0xe0, 0x6f, 0xa5, 0x3c, 0x19, 0x73, 0xf3, 0x46, 0x28, 0x2e, + 0x3e, 0x0a, 0xcc, 0x70, 0x46, 0xb0, 0x50, 0xab, 0xaf, 0xbc, 0x89, 0x0d, 0xff, 0x50, 0x12, 0x78, 0xb1, 0x7b, 0xb9, + 0xf2, 0x0a, 0xbc, 0x09, 0xed, 0x1f, 0x28, 0xff, 0xef, 0xa9, 0x96, 0xbd, 0xbc, 0x57, 0xa7, 0xb6, 0xe3, 0x5a, 0x50, + 0x91, 0x54, 0x05, 0x6f, 0xd7, 0xbf, 0x65, 0xa2, 0x81, 0xe5, 0xc9, 0x52, 0xf6, 0xb5, 0x33, 0xf0, 0xb1, 0x81, 0x2e, + 0xf5, 0x95, 0x54, 0xbd, 0x10, 0x67, 0x2c, 0x24, 0xcd, 0x0c, 0x80, 0xe8, 0x75, 0x9f, 0x9e, 0x54, 0xd3, 0xb0, 0x57, + 0x67, 0x2b, 0x7a, 0xd6, 0x88, 0x91, 0xde, 0xa5, 0xd2, 0x98, 0x3d, 0x3d, 0x52, 0xa6, 0xcf, 0x3b, 0x3f, 0x2a, 0x6f, + 0x48, 0x66, 0x1b, 0x12, 0xfc, 0x29, 0x2f, 0x50, 0x52, 0x66, 0xdb, 0x8a, 0x4d, 0xf1, 0x66, 0xee, 0x02, 0x98, 0xac, + 0x27, 0x98, 0xbb, 0x6f, 0x5e, 0x72, 0x30, 0xc6, 0xba, 0x52, 0x45, 0xb9, 0xf1, 0x79, 0x9c, 0x75, 0xb9, 0x43, 0xd8, + 0x44, 0x16, 0x3d, 0x07, 0x81, 0xcd, 0xea, 0x5a, 0x1e, 0xcc, 0xc7, 0x9c, 0x64, 0x97, 0x35, 0xfa, 0x85, 0x49, 0x90, + 0x6e, 0xde, 0xf0, 0x5c, 0xb3, 0x42, 0xde, 0xbc, 0x2f, 0xb9, 0x11, 0xcc, 0x60, 0xb4, 0x11, 0x29, 0xb4, 0x75, 0xca, + 0xb0, 0x8f, 0x88, 0x5e, 0x49, 0x98, 0xfe, 0x41, 0x9e, 0xaf, 0x7e, 0x10, 0xa6, 0xe7, 0xeb, 0x05, 0xaa, 0xfa, 0x87, + 0x02, 0x5e, 0x4c, 0x38, 0xc0, 0x02, 0xea, 0xe8, 0xa5, 0x5c, 0xc7, 0x9a, 0xa0, 0x9c, 0x70, 0xa9, 0xaf, 0xd9, 0x28, + 0xaf, 0xa5, 0xfa, 0x84, 0xd6, 0xb1, 0x66, 0x03, 0x4c, 0x46, 0x37, 0xb6, 0xf1, 0xb7, 0x31, 0xb7, 0xe9, 0xb2, 0x7f, + 0xaa, 0xd8, 0x1e, 0x82, 0xb2, 0xe1, 0x02, 0x3e, 0xf7, 0x08, 0xdc, 0xb9, 0x9e, 0x80, 0xd6, 0x10, 0xff, 0xe3, 0x38, + 0xd6, 0xf2, 0x65, 0x9d, 0x29, 0x89, 0x55, 0x16, 0x42, 0x85, 0xca, 0x89, 0xfd, 0xdc, 0x30, 0xd7, 0x7a, 0x1c, 0x5c, + 0x23, 0xc1, 0x40, 0x70, 0x0a, 0x30, 0x89, 0xab, 0x29, 0x0d, 0x8d, 0x3b, 0x47, 0x7f, 0x78, 0x2d, 0xbf, 0xf0, 0xaa, + 0x5c, 0x17, 0xdc, 0xf4, 0xbd, 0x19, 0x01, 0xf3, 0x0b, 0xfb, 0xc2, 0xd1, 0x45, 0xcb, 0xe8, 0xfa, 0xec, 0x80, 0x04, + 0xc8, 0x63, 0x65, 0x19, 0x49, 0xd8, 0x92, 0xb5, 0x7a, 0x93, 0x9f, 0xef, 0x99, 0x42, 0x24, 0x5b, 0xa0, 0xca, 0xf1, + 0x0b, 0x6c, 0x2d, 0x2d, 0xa9, 0x64, 0x25, 0x5a, 0xab, 0x50, 0x81, 0x68, 0xad, 0x09, 0xd5, 0xaa, 0xd3, 0x7b, 0xdf, + 0x22, 0x3a, 0x2f, 0x8d, 0xd4, 0x21, 0x86, 0x80, 0x88, 0xa5, 0xf5, 0x9d, 0xd2, 0x46, 0xeb, 0xc9, 0xb2, 0xb8, 0xaf, + 0xc6, 0xf6, 0x6b, 0xb8, 0x7a, 0x26, 0xde, 0x54, 0xde, 0xd6, 0xc5, 0xc3, 0x9c, 0x55, 0x4e, 0x74, 0x5d, 0x87, 0x69, + 0xb3, 0xb6, 0xd3, 0x5f, 0xd5, 0x55, 0x26, 0x43, 0xf0, 0xb1, 0x87, 0x50, 0x73, 0xa1, 0x4a, 0x85, 0x48, 0x2f, 0x77, + 0x62, 0x73, 0xe5, 0x1e, 0x73, 0xa5, 0x73, 0x1c, 0xd9, 0x3a, 0xb6, 0x93, 0xe1, 0xa9, 0xc9, 0x05, 0x71, 0xec, 0xee, + 0x7e, 0x88, 0x0b, 0xfe, 0xcf, 0x17, 0xd2, 0x9c, 0xc7, 0xe7, 0x2f, 0xfd, 0xf4, 0x93, 0xb1, 0x92, 0xd2, 0x38, 0x99, + 0x65, 0x4d, 0x2f, 0xcb, 0x20, 0xce, 0x7f, 0xc6, 0xcb, 0x9c, 0x85, 0xd7, 0x59, 0xfb, 0x57, 0xc3, 0xad, 0x38, 0xb4, + 0x2e, 0x45, 0x32, 0x45, 0xb9, 0xfb, 0xd7, 0x71, 0x12, 0x22, 0xc3, 0x9f, 0xf3, 0x86, 0xb1, 0xf6, 0x69, 0xd5, 0x7c, + 0x24, 0x2b, 0x76, 0xf6, 0x7e, 0xe9, 0xb1, 0x71, 0x51, 0x70, 0x27, 0xc8, 0x95, 0x56, 0x4a, 0x0e, 0x8e, 0x03, 0x4d, + 0xe5, 0x03, 0x05, 0x7f, 0x98, 0x92, 0xc6, 0x53, 0xcc, 0x56, 0xdf, 0xa7, 0x36, 0xcb, 0x98, 0x0c, 0x8f, 0x74, 0x66, + 0xcc, 0x46, 0xad, 0xa0, 0xb4, 0xc7, 0xf9, 0xb0, 0xb0, 0xce, 0x69, 0x9b, 0x71, 0x4c, 0xf2, 0xc7, 0xb7, 0x0a, 0xd9, + 0xaa, 0x7c, 0xa9, 0xf7, 0x7b, 0x69, 0x6f, 0x93, 0x17, 0x2b, 0x7a, 0x2b, 0x4c, 0x84, 0x81, 0x88, 0x4a, 0x15, 0x34, + 0x12, 0xb2, 0xb0, 0xd3, 0x4e, 0xed, 0x0c, 0x55, 0x69, 0x31, 0x00, 0x3f, 0x86, 0xf5, 0xf1, 0xf8, 0x5a, 0x34, 0xa6, + 0xd6, 0x51, 0x23, 0x36, 0x2e, 0xe7, 0x19, 0x00, 0x2f, 0x54, 0x3c, 0xb3, 0x62, 0xfa, 0x8c, 0x9c, 0x39, 0x82, 0x2a, + 0x0b, 0x41, 0xda, 0x61, 0x28, 0xb6, 0xdc, 0x98, 0xaa, 0x0d, 0xe4, 0xc2, 0x9f, 0x75, 0x52, 0xa5, 0x11, 0xca, 0x21, + 0xd7, 0x26, 0xef, 0x32, 0xdf, 0x20, 0x44, 0x1f, 0xda, 0xf8, 0xeb, 0xc9, 0x8d, 0x04, 0x64, 0x0a, 0x38, 0x8f, 0x34, + 0x5e, 0xd3, 0xf7, 0x3c, 0x03, 0xde, 0x54, 0x6f, 0x92, 0x04, 0xe4, 0x59, 0x75, 0xa2, 0xdb, 0xf0, 0x90, 0x3c, 0xfb, + 0xad, 0x1c, 0x95, 0x7b, 0x72, 0xa5, 0x65, 0xdf, 0xea, 0x36, 0x63, 0xbe, 0x64, 0xed, 0xd2, 0xda, 0xdb, 0x09, 0xb3, + 0x4e, 0x53, 0x65, 0x4a, 0xc4, 0x83, 0x4a, 0xd2, 0xda, 0x19, 0x40, 0x98, 0xfa, 0xe9, 0x5b, 0xd4, 0x8e, 0x37, 0x92, + 0x73, 0x93, 0x01, 0x0b, 0xaa, 0xac, 0x5c, 0x76, 0x81, 0x44, 0x40, 0x6e, 0xdb, 0xf8, 0xa6, 0xc9, 0x12, 0x8c, 0xc8, + 0x3f, 0xa0, 0x77, 0xc1, 0x1d, 0xd9, 0x5b, 0xa0, 0x3b, 0xd3, 0xc7, 0x9e, 0x1a, 0xef, 0xca, 0x9a, 0xec, 0x42, 0x66, + 0xbe, 0x89, 0x81, 0x6b, 0x57, 0x2d, 0x21, 0xe1, 0xba, 0xb1, 0xcb, 0xbc, 0xa8, 0x33, 0x99, 0xad, 0x59, 0x95, 0xc7, + 0x6a, 0x98, 0x4a, 0x87, 0xa9, 0x9a, 0xb0, 0x25, 0xc8, 0x05, 0x84, 0xcb, 0x6b, 0x97, 0xeb, 0xf8, 0x2a, 0x01, 0x22, + 0x3d, 0x88, 0x93, 0x62, 0xec, 0xb9, 0x91, 0x77, 0xd7, 0xcb, 0x0a, 0x14, 0xc6, 0x3b, 0x6b, 0x92, 0x93, 0x4b, 0xed, + 0x4f, 0xc6, 0xdb, 0x56, 0x33, 0xdd, 0x8e, 0x2f, 0x12, 0xba, 0x16, 0xc7, 0x16, 0x7c, 0x49, 0xed, 0xde, 0xd5, 0x22, + 0x57, 0xed, 0x65, 0x01, 0xa3, 0x6d, 0x74, 0xd6, 0x6d, 0xb1, 0x30, 0xa7, 0x44, 0x38, 0x59, 0x36, 0xe6, 0x3b, 0x11, + 0x5e, 0x24, 0xd6, 0x18, 0xa8, 0x9d, 0x79, 0xe3, 0x4f, 0x0c, 0xc1, 0x09, 0xbe, 0x10, 0x5c, 0x2c, 0x8d, 0xf9, 0xf4, + 0x05, 0x11, 0xb1, 0x59, 0x1c, 0x9e, 0xad, 0x9b, 0xe0, 0x74, 0x8d, 0xeb, 0x0d, 0xb8, 0x1b, 0x58, 0xd4, 0xdf, 0xd1, + 0x83, 0x79, 0xfb, 0xa3, 0xb0, 0x69, 0x20, 0xc3, 0xe8, 0xd1, 0x23, 0x41, 0xdc, 0xd9, 0x1c, 0x4b, 0x4a, 0x24, 0x1c, + 0xf1, 0xeb, 0xe7, 0x08, 0x16, 0xb5, 0x2b, 0xa3, 0xa3, 0x31, 0x97, 0xfa, 0x07, 0xb9, 0xb4, 0xed, 0x2b, 0x60, 0xf1, + 0xcf, 0x50, 0x92, 0x94, 0x9d, 0x31, 0xc8, 0x6b, 0xdb, 0x80, 0xa9, 0x0a, 0xa8, 0xe3, 0x10, 0x7e, 0x52, 0x12, 0xee, + 0x66, 0x6b, 0x4a, 0xe5, 0xd2, 0x8c, 0x62, 0xcf, 0x1b, 0x44, 0xd1, 0xc5, 0x16, 0xe1, 0x24, 0x03, 0x27, 0xfa, 0x6a, + 0xa3, 0x20, 0x6f, 0xb5, 0xbd, 0xf8, 0x3c, 0x03, 0x67, 0x1d, 0x3a, 0x05, 0x34, 0x19, 0x25, 0x0d, 0xa1, 0x42, 0x1b, + 0xc2, 0xac, 0x0d, 0x2e, 0x5b, 0x11, 0x9a, 0x86, 0xcc, 0xb0, 0x0f, 0xf3, 0x79, 0xe0, 0x8c, 0x22, 0x41, 0x4f, 0xbb, + 0xd4, 0x6f, 0x56, 0xbf, 0xb9, 0x30, 0xdf, 0xdd, 0x48, 0x27, 0x02, 0x10, 0xad, 0xf4, 0xe9, 0xa1, 0x78, 0x91, 0x5b, + 0x10, 0x51, 0x6b, 0x0e, 0x6f, 0x09, 0x0e, 0x3e, 0x26, 0x2c, 0xb5, 0xea, 0xae, 0xb6, 0xf8, 0x17, 0x09, 0xdf, 0xb5, + 0x79, 0x40, 0xcc, 0x46, 0x6f, 0xe8, 0xfa, 0x5e, 0x9a, 0xa7, 0x92, 0xea, 0x89, 0x2d, 0x06, 0x2e, 0x0b, 0x05, 0x55, + 0xfc, 0x66, 0x7c, 0x8d, 0x91, 0x15, 0x01, 0x34, 0x38, 0xbd, 0xc5, 0x08, 0x1c, 0x32, 0xe6, 0xe5, 0xd8, 0x1f, 0xd7, + 0x6c, 0x82, 0x7c, 0xd6, 0x98, 0x90, 0x88, 0xb7, 0xbd, 0x37, 0xd8, 0x2a, 0x94, 0x8d, 0x44, 0x5a, 0x1e, 0x39, 0x8c, + 0x7b, 0x50, 0xf1, 0x30, 0x22, 0x36, 0xac, 0x29, 0xf3, 0x09, 0xa1, 0xcd, 0x1e, 0xc4, 0x9c, 0x5d, 0x98, 0xb0, 0xd0, + 0x4b, 0x0c, 0x44, 0xe8, 0x6d, 0x00, 0xfb, 0x46, 0x6c, 0x91, 0x48, 0x21, 0x89, 0x44, 0x3e, 0x9a, 0x13, 0xe2, 0xb0, + 0x15, 0x19, 0x1e, 0xac, 0xf6, 0x2e, 0x46, 0xf2, 0x67, 0x9c, 0x94, 0xd6, 0x65, 0x62, 0xf3, 0xc7, 0x28, 0x61, 0x0c, + 0x38, 0xbb, 0x3b, 0x29, 0xce, 0xbb, 0x61, 0xf9, 0xe8, 0x03, 0x15, 0x7c, 0xcb, 0x15, 0xc1, 0x1e, 0x4d, 0xe4, 0x48, + 0x95, 0x15, 0xcb, 0xb9, 0x7e, 0x14, 0x1a, 0x3c, 0x65, 0xe1, 0xa8, 0x6a, 0xc3, 0x48, 0x10, 0x51, 0x69, 0x5c, 0x30, + 0x5a, 0xc9, 0x40, 0x47, 0x63, 0xda, 0x6a, 0x44, 0xb8, 0x80, 0xe7, 0x59, 0xfb, 0xa7, 0x05, 0xe3, 0x3c, 0x5e, 0x86, + 0xe3, 0x0f, 0x9a, 0x41, 0xff, 0x1d, 0x99, 0x8c, 0x96, 0x4f, 0xee, 0x46, 0xff, 0x49, 0x3f, 0x68, 0x67, 0xef, 0xf7, + 0xd5, 0xe9, 0xc7, 0xbe, 0x5c, 0x48, 0x43, 0x7e, 0xa1, 0x2b, 0x57, 0x73, 0xbb, 0x35, 0x3c, 0x30, 0x35, 0xb7, 0xd3, + 0xeb, 0x04, 0xf5, 0xce, 0xb9, 0x41, 0xdb, 0x86, 0x0d, 0x4c, 0xe2, 0x31, 0xe7, 0xc9, 0x68, 0xac, 0xc8, 0x80, 0x5a, + 0xc1, 0xca, 0x3c, 0x4b, 0x70, 0xd7, 0x67, 0xc6, 0xe0, 0x9e, 0xb8, 0x28, 0xb3, 0xe4, 0xde, 0x07, 0xe0, 0x24, 0x68, + 0xfe, 0x92, 0xdd, 0xa2, 0x7e, 0xa2, 0x5a, 0x74, 0x07, 0x29, 0x43, 0xad, 0x25, 0xde, 0x57, 0xb5, 0xc6, 0x10, 0xec, + 0x0d, 0x00, 0xad, 0xa9, 0xd5, 0x87, 0x89, 0x1c, 0xf2, 0xc7, 0x56, 0xf5, 0x41, 0x69, 0xa2, 0x2e, 0x18, 0x90, 0xa7, + 0xe6, 0x97, 0x2e, 0x11, 0x26, 0x9d, 0xd4, 0xff, 0xab, 0x97, 0xff, 0x6d, 0x0c, 0x94, 0x89, 0xca, 0xdb, 0x90, 0x87, + 0x93, 0xc7, 0xbd, 0x29, 0xde, 0xd2, 0xf9, 0x46, 0x1b, 0xee, 0x04, 0x4f, 0xf2, 0xf0, 0xfa, 0xbc, 0xb5, 0x37, 0x43, + 0xdc, 0xd7, 0xd1, 0xa6, 0xb2, 0x6d, 0x52, 0x52, 0x52, 0x1d, 0x9c, 0x81, 0x25, 0xda, 0x05, 0x4d, 0xcb, 0x79, 0xa4, + 0x1c, 0xcb, 0x36, 0xa9, 0x72, 0x0b, 0x78, 0xca, 0x29, 0xe5, 0x3f, 0x04, 0x1d, 0xa5, 0x9a, 0x47, 0xcd, 0x65, 0x79, + 0xea, 0x52, 0x58, 0x5b, 0x21, 0xba, 0x37, 0xa7, 0xfc, 0x62, 0x96, 0xb4, 0x94, 0x6a, 0x93, 0x00, 0x91, 0xc6, 0x7b, + 0x9a, 0x58, 0xd6, 0x03, 0xe8, 0x44, 0xd5, 0x2e, 0x61, 0x12, 0x43, 0x3b, 0xd9, 0x86, 0xba, 0xfa, 0x68, 0x15, 0xd6, + 0xe7, 0x2f, 0x68, 0x78, 0xb5, 0xdf, 0xd2, 0x23, 0x46, 0xcd, 0x1a, 0xde, 0x1f, 0x1e, 0x4a, 0x70, 0xb1, 0x69, 0xec, + 0x6c, 0xb3, 0x26, 0x0e, 0x3b, 0x7e, 0x0e, 0x2b, 0x08, 0xa6, 0x67, 0x47, 0x1b, 0xc6, 0x6a, 0x70, 0x7c, 0x95, 0x5f, + 0xed, 0x7a, 0x31, 0xa0, 0x26, 0x52, 0xdc, 0x29, 0x72, 0xc0, 0x00, 0x13, 0x2d, 0xe4, 0xcd, 0xd3, 0x79, 0xfc, 0x21, + 0xbe, 0x1e, 0x0f, 0xb4, 0x9f, 0x20, 0x8f, 0x9e, 0x05, 0x8a, 0x0c, 0x50, 0xd1, 0x93, 0xfb, 0x8b, 0x53, 0x28, 0xc3, + 0x6e, 0xa2, 0xd3, 0x41, 0xd1, 0xed, 0xdd, 0x23, 0x6f, 0x7c, 0xbc, 0xa9, 0xca, 0xe5, 0x3c, 0xc2, 0x40, 0xd7, 0x1b, + 0xd8, 0x40, 0x11, 0x19, 0xcb, 0x2a, 0xc5, 0x8f, 0x31, 0xaa, 0x0c, 0x51, 0x70, 0xab, 0x4f, 0x58, 0xc3, 0x45, 0x60, + 0xef, 0x10, 0x26, 0x09, 0xa3, 0x47, 0xee, 0xb9, 0xa9, 0x79, 0x72, 0xcd, 0xec, 0x3c, 0xca, 0x1c, 0xac, 0x2a, 0x0e, + 0x4c, 0x98, 0xb2, 0x41, 0x31, 0x79, 0x2c, 0x97, 0x72, 0xab, 0x55, 0x37, 0x73, 0xa2, 0x98, 0x1e, 0xd9, 0xc3, 0xd0, + 0xc2, 0x4d, 0xba, 0x21, 0x46, 0x7f, 0xe1, 0x85, 0x7e, 0xb4, 0x1a, 0x04, 0x43, 0xb4, 0xc2, 0xce, 0xda, 0x28, 0x67, + 0x8c, 0xa2, 0xf8, 0xfb, 0x02, 0x10, 0x6c, 0xeb, 0xfa, 0x96, 0xae, 0x3e, 0x79, 0x6b, 0x77, 0xab, 0x4a, 0xcf, 0x83, + 0x12, 0x23, 0x7e, 0xcd, 0x2a, 0xe7, 0x9d, 0xea, 0x40, 0xe2, 0x87, 0x50, 0x69, 0x01, 0x57, 0x84, 0xb0, 0x4a, 0xe3, + 0x60, 0x02, 0x9c, 0xce, 0x45, 0x53, 0xdf, 0x45, 0x03, 0x48, 0x28, 0x93, 0xf8, 0xe4, 0x3c, 0x9b, 0x84, 0x5a, 0x1e, + 0x1d, 0xd2, 0x7b, 0xb7, 0x0e, 0x42, 0xe1, 0x3b, 0x53, 0xad, 0x17, 0xdc, 0x3d, 0xa5, 0xfd, 0x7a, 0xed, 0x0b, 0x2b, + 0x95, 0xc6, 0xfd, 0x77, 0xd3, 0xc7, 0xb7, 0xdf, 0xf1, 0xe2, 0xa8, 0xef, 0x26, 0xce, 0x86, 0xe5, 0x5b, 0x1e, 0x80, + 0x37, 0x0b, 0x0e, 0x08, 0xf0, 0x11, 0xf5, 0x54, 0xa7, 0xfd, 0x1e, 0xba, 0xf1, 0x75, 0x66, 0xf6, 0x2c, 0xe9, 0xfc, + 0x9d, 0x1f, 0x7c, 0xd8, 0xb6, 0x20, 0xd0, 0x05, 0xe3, 0xff, 0xa3, 0xa5, 0x02, 0x02, 0x50, 0xf0, 0xf7, 0xe1, 0x75, + 0x38, 0x45, 0xc1, 0x73, 0x18, 0xf5, 0x71, 0x44, 0x99, 0xee, 0x9d, 0x34, 0xf9, 0x5e, 0x45, 0x36, 0xcb, 0xbc, 0x42, + 0x36, 0x61, 0x6c, 0x7a, 0x59, 0xa7, 0x7c, 0x6d, 0x66, 0x60, 0xac, 0xbe, 0x04, 0xa8, 0x8c, 0x44, 0x6f, 0x4a, 0xbf, + 0x84, 0x5f, 0x5f, 0x8a, 0xc5, 0x90, 0x07, 0xdf, 0x69, 0xf5, 0xda, 0xad, 0x8f, 0x8d, 0xdf, 0xae, 0xdc, 0x83, 0xa1, + 0x0f, 0x42, 0xee, 0xe7, 0x0d, 0x59, 0x19, 0x47, 0x9b, 0xe7, 0x05, 0x97, 0xc6, 0xcb, 0x28, 0x97, 0x86, 0x8e, 0x24, + 0x6a, 0x03, 0x7d, 0x5a, 0x5a, 0x72, 0xc0, 0x65, 0x48, 0x8c, 0xfd, 0x20, 0x2b, 0x3d, 0x3e, 0x92, 0xf6, 0xc1, 0xe4, + 0x18, 0x3e, 0x9f, 0x6e, 0x71, 0x11, 0xef, 0x44, 0x60, 0xc7, 0x40, 0x95, 0x1b, 0xae, 0xda, 0xdb, 0xbd, 0xbd, 0xfd, + 0xc3, 0xf6, 0xe1, 0x66, 0xfd, 0x75, 0x85, 0x0e, 0xa9, 0xc6, 0x38, 0x9d, 0x5a, 0xab, 0xb5, 0x9c, 0xb4, 0x85, 0xbf, + 0xb7, 0x2c, 0xda, 0x24, 0xa4, 0x48, 0x0c, 0x98, 0x5b, 0x46, 0x26, 0x55, 0x2b, 0x0f, 0x30, 0x91, 0x9a, 0xba, 0x4d, + 0x4f, 0xf7, 0x99, 0x92, 0xa5, 0x06, 0xbd, 0xd8, 0xe9, 0xaa, 0x10, 0xeb, 0xa5, 0xeb, 0xc7, 0x8b, 0xa5, 0xd7, 0xba, + 0x2e, 0xb0, 0x89, 0x6c, 0x18, 0x48, 0x1d, 0x7f, 0xc7, 0x46, 0xee, 0xd7, 0xc3, 0x93, 0x25, 0x80, 0xc2, 0x25, 0xd2, + 0x75, 0x09, 0x72, 0xb4, 0x29, 0x49, 0x48, 0x2e, 0x5e, 0xa1, 0x8a, 0xf1, 0xa4, 0x66, 0x7b, 0xf3, 0x6c, 0x21, 0x12, + 0x19, 0x4a, 0x19, 0x1b, 0xbb, 0x9b, 0x74, 0xef, 0x02, 0x1c, 0xd4, 0xa2, 0x2e, 0xd7, 0x17, 0x55, 0x80, 0xed, 0x9c, + 0xbf, 0x1a, 0x8d, 0xf3, 0xa8, 0x89, 0x6e, 0xd7, 0xb0, 0x2f, 0xbb, 0xe6, 0x4c, 0x6e, 0x2e, 0x9d, 0xe6, 0xf9, 0x91, + 0xcf, 0x16, 0xab, 0x67, 0x18, 0x5c, 0xee, 0x3a, 0x01, 0x03, 0x54, 0xee, 0x95, 0x01, 0x7c, 0xcb, 0x02, 0xeb, 0x06, + 0x73, 0x49, 0x64, 0x93, 0x44, 0x5b, 0xbb, 0xa7, 0x9c, 0x84, 0x26, 0xb7, 0xee, 0x59, 0xe2, 0xca, 0x0f, 0x82, 0xaa, + 0x6c, 0xf3, 0xb4, 0x5e, 0x34, 0xf7, 0x68, 0xe9, 0x7f, 0x7a, 0x58, 0x04, 0x45, 0x81, 0xe6, 0xe1, 0x2d, 0x52, 0x73, + 0x98, 0x05, 0x51, 0x63, 0x27, 0xbc, 0xa1, 0x7d, 0x60, 0xad, 0x6d, 0xd4, 0x8e, 0x54, 0xef, 0x6f, 0x90, 0x12, 0xd6, + 0xec, 0x92, 0x14, 0x2c, 0x2b, 0xe2, 0x72, 0xd0, 0x8e, 0x08, 0xf0, 0x58, 0xd9, 0x0a, 0x1e, 0xe5, 0xc5, 0xdd, 0x6c, + 0xec, 0x0b, 0x64, 0xac, 0xc9, 0x1c, 0x74, 0x0d, 0xbf, 0x45, 0xa8, 0xd6, 0x56, 0xb7, 0x83, 0xb5, 0x7b, 0xc3, 0x34, + 0xd1, 0x3a, 0x09, 0x76, 0x44, 0x49, 0xfb, 0x05, 0x07, 0x6e, 0xaa, 0xca, 0x8e, 0xdc, 0x5b, 0x89, 0x34, 0x68, 0x57, + 0xe8, 0xfc, 0x75, 0x37, 0x35, 0x02, 0xde, 0x4c, 0xa7, 0xe4, 0x28, 0xf1, 0x89, 0x94, 0x41, 0x41, 0x49, 0x72, 0xfe, + 0x9f, 0xf5, 0xb1, 0x03, 0x05, 0xf1, 0x8d, 0x9f, 0x7f, 0x17, 0x04, 0x38, 0xb0, 0xdb, 0x41, 0xd6, 0xbe, 0x1c, 0x4b, + 0x60, 0x51, 0x85, 0x39, 0xd7, 0x83, 0x5a, 0xff, 0x9e, 0x17, 0xe1, 0xf9, 0xaf, 0x17, 0x5b, 0xaa, 0x75, 0xdb, 0x5e, + 0xf7, 0x16, 0xc9, 0x35, 0x63, 0x3b, 0xec, 0xcb, 0xc1, 0x87, 0xd3, 0x4c, 0xb2, 0x05, 0x24, 0x0d, 0x99, 0xbe, 0x94, + 0x36, 0xe9, 0x86, 0x03, 0x72, 0x07, 0x64, 0x70, 0x10, 0x68, 0x32, 0x28, 0x6b, 0x78, 0xac, 0xe6, 0xe1, 0xbc, 0xbd, + 0x7a, 0xf2, 0xd7, 0x2a, 0x5f, 0xa2, 0x43, 0xea, 0x9d, 0xc5, 0x80, 0xff, 0x7e, 0x2b, 0x18, 0xc9, 0xf6, 0xcd, 0x7e, + 0x77, 0xd3, 0x94, 0xe2, 0x0a, 0xa6, 0xfd, 0x83, 0xff, 0x3f, 0xf4, 0x16, 0x5e, 0xef, 0x64, 0x68, 0xaa, 0xc3, 0x94, + 0x1b, 0xd6, 0x8b, 0x0b, 0xf9, 0xae, 0x4c, 0x8c, 0x11, 0x04, 0x46, 0x60, 0x56, 0x97, 0xe8, 0x1e, 0x86, 0x3b, 0xeb, + 0x51, 0xcd, 0x70, 0x72, 0x69, 0x33, 0x86, 0x55, 0x0b, 0x11, 0x01, 0x2e, 0x51, 0xa0, 0x44, 0x91, 0x20, 0x89, 0x01, + 0xa2, 0x7b, 0xeb, 0xf3, 0x08, 0x65, 0x51, 0xb3, 0xbe, 0xa1, 0xb6, 0xb3, 0xb2, 0x39, 0x09, 0x68, 0x6d, 0xe6, 0x98, + 0x56, 0xa3, 0x00, 0x9d, 0xbb, 0xd3, 0x00, 0x3a, 0xf4, 0x16, 0xe9, 0xa5, 0x8c, 0x15, 0xfb, 0xae, 0x67, 0x6d, 0xe9, + 0x90, 0x4f, 0xa2, 0xd6, 0xea, 0x20, 0xad, 0x55, 0x4e, 0x45, 0x66, 0x42, 0x5f, 0xe8, 0xd2, 0xc2, 0x19, 0xe8, 0x1b, + 0x6f, 0x0f, 0xd6, 0x78, 0x4a, 0x6f, 0xf2, 0xa5, 0x29, 0xe5, 0x65, 0x8f, 0x09, 0xf7, 0x3b, 0xa9, 0x8c, 0xed, 0xad, + 0x01, 0x91, 0x4b, 0xfa, 0xbb, 0x87, 0x84, 0x66, 0x1e, 0xbd, 0x0d, 0x38, 0xec, 0x82, 0x56, 0xfc, 0xaa, 0x7a, 0xbc, + 0x63, 0x82, 0x87, 0xa5, 0x34, 0xf9, 0xfe, 0xc5, 0x9b, 0x61, 0xd6, 0x30, 0x5e, 0x58, 0xec, 0x82, 0x80, 0x82, 0xd9, + 0x5b, 0xcc, 0xdd, 0xff, 0xe5, 0x8f, 0xd6, 0xc0, 0x8d, 0x99, 0x43, 0x6e, 0x3e, 0xe0, 0xf1, 0x3d, 0xbd, 0x4f, 0xbd, + 0x9b, 0xd5, 0xab, 0x4f, 0xa7, 0xc5, 0x85, 0x91, 0xf7, 0xed, 0x74, 0xb4, 0x47, 0x24, 0x5c, 0x03, 0x30, 0x01, 0x50, + 0x96, 0x78, 0x40, 0x09, 0x8b, 0xf7, 0xe5, 0xd2, 0x2a, 0x3b, 0x01, 0x4d, 0xb5, 0x67, 0x9b, 0x3a, 0x72, 0xe1, 0x19, + 0xdb, 0x51, 0x2c, 0x6d, 0xa7, 0x29, 0x61, 0xf2, 0x5a, 0xd7, 0xee, 0xf4, 0xf2, 0xa3, 0x34, 0x81, 0x9a, 0xa9, 0x5c, + 0x29, 0xbf, 0x46, 0xd6, 0x10, 0x7c, 0x0a, 0x8b, 0x28, 0x2a, 0xc0, 0xb3, 0xe8, 0x04, 0xaa, 0xd6, 0x0f, 0xed, 0x77, + 0x77, 0x58, 0x6c, 0x5d, 0x4c, 0x8f, 0x1f, 0x2a, 0x90, 0x79, 0xe6, 0xb8, 0x73, 0xa6, 0xd9, 0xd1, 0x4d, 0xe3, 0x5d, + 0x4c, 0xd9, 0x4f, 0x5f, 0xa0, 0x4f, 0x16, 0x66, 0x76, 0x2f, 0x68, 0x2c, 0x83, 0x27, 0x45, 0x36, 0x48, 0x91, 0xef, + 0xc2, 0x10, 0xc6, 0x48, 0xa5, 0x33, 0x35, 0x8f, 0xd1, 0xf4, 0xb7, 0xd0, 0x16, 0x4c, 0xed, 0xde, 0x53, 0x7d, 0xe8, + 0x7a, 0xa3, 0x54, 0x6b, 0xdf, 0x49, 0x99, 0x49, 0x2f, 0x61, 0xa4, 0x68, 0xb7, 0xd7, 0xea, 0xa7, 0x5f, 0x2b, 0x73, + 0xa9, 0xf6, 0xd2, 0x34, 0x79, 0x11, 0xdd, 0x29, 0xc8, 0xe2, 0x70, 0x31, 0xa5, 0xb4, 0x7d, 0x52, 0xfd, 0x7b, 0xbf, + 0xb8, 0x41, 0xfc, 0x6c, 0xfc, 0x63, 0xe6, 0xf3, 0xc0, 0x97, 0xba, 0xb4, 0x01, 0x72, 0x7f, 0x72, 0x6f, 0x95, 0x18, + 0x86, 0x21, 0x05, 0x64, 0xe5, 0x6a, 0x09, 0x58, 0x14, 0xc8, 0x03, 0x15, 0x10, 0x8d, 0x38, 0xa3, 0x1d, 0x52, 0x6b, + 0xd6, 0x97, 0x25, 0x40, 0x18, 0x70, 0xed, 0x2f, 0x34, 0xce, 0x7e, 0xb1, 0xb7, 0x20, 0xa8, 0x65, 0xc3, 0x4b, 0x9e, + 0x3f, 0x02, 0x23, 0x03, 0x84, 0x9c, 0x1e, 0x89, 0x3d, 0x8b, 0xd1, 0xbc, 0xa2, 0xb3, 0xe8, 0x81, 0x8c, 0x85, 0x9a, + 0x2a, 0x6f, 0xec, 0x04, 0x98, 0xdd, 0x07, 0x97, 0x54, 0xf5, 0x18, 0x0c, 0xe0, 0x05, 0x44, 0x05, 0xac, 0x68, 0x02, + 0x9d, 0xfa, 0xd8, 0x10, 0x07, 0x6f, 0x68, 0x51, 0x80, 0x20, 0xb0, 0x37, 0x10, 0xf6, 0x27, 0xd6, 0x1f, 0x5c, 0xcd, + 0xb0, 0xcb, 0x30, 0x8d, 0xe3, 0xd0, 0xd0, 0x9e, 0x82, 0x9f, 0x0a, 0x9b, 0x68, 0xaa, 0x04, 0x28, 0x37, 0x09, 0xb1, + 0x07, 0x01, 0xff, 0xca, 0x23, 0xf2, 0xb8, 0x6e, 0x6a, 0xff, 0x09, 0xa6, 0x38, 0x2a, 0x83, 0x75, 0x9b, 0xba, 0xeb, + 0xef, 0x75, 0x19, 0xc7, 0x35, 0xa0, 0xb0, 0xa5, 0x73, 0x9c, 0x1e, 0xd3, 0x10, 0xff, 0x6b, 0xa0, 0x7f, 0xd7, 0xaa, + 0xad, 0xef, 0x42, 0x6c, 0xd6, 0x66, 0xcc, 0x07, 0x0d, 0xbb, 0x8b, 0x13, 0xe3, 0xc8, 0xe3, 0xbe, 0xc0, 0xb4, 0x6b, + 0x89, 0x8f, 0x34, 0xf4, 0xe4, 0x11, 0x94, 0x9e, 0xae, 0x76, 0x95, 0xf1, 0xab, 0xf1, 0x78, 0x7b, 0xb3, 0xf5, 0x2a, + 0x86, 0x98, 0x11, 0x05, 0x6c, 0xf5, 0x3b, 0xeb, 0xf8, 0xe4, 0x60, 0x39, 0x8e, 0xb9, 0xf5, 0x12, 0x35, 0xae, 0x2f, + 0xb2, 0x14, 0x8b, 0x54, 0xfb, 0x72, 0xf7, 0x35, 0x1f, 0x4c, 0xaf, 0x7c, 0xfc, 0xfb, 0xf3, 0x50, 0x08, 0x2e, 0xa8, + 0x12, 0x23, 0xd1, 0x40, 0x77, 0x6e, 0x5b, 0x41, 0x0b, 0xbf, 0x95, 0x94, 0x56, 0x3c, 0x0f, 0x56, 0xa3, 0x5d, 0x02, + 0x42, 0x55, 0x03, 0x5e, 0x9f, 0xa2, 0xc9, 0x85, 0x03, 0xc7, 0x08, 0xb5, 0x68, 0x72, 0x96, 0x30, 0x9c, 0x74, 0xfb, + 0x6d, 0x7e, 0xfa, 0xeb, 0x9c, 0x0c, 0x91, 0x02, 0x90, 0xfa, 0x76, 0x4c, 0xf8, 0xf4, 0x3b, 0x5e, 0x4c, 0xfe, 0xf3, + 0x8d, 0x90, 0xbe, 0xe9, 0xc4, 0xc6, 0x43, 0x90, 0x37, 0x8a, 0x42, 0x84, 0x08, 0x76, 0x71, 0x20, 0xcc, 0x76, 0xf8, + 0x95, 0xdc, 0xc2, 0x57, 0xf4, 0x96, 0x9a, 0xa3, 0xa7, 0xd1, 0x41, 0x0b, 0x27, 0xac, 0x4d, 0x7f, 0x9e, 0x47, 0x5f, + 0x60, 0xc0, 0xe1, 0x33, 0x2b, 0xc0, 0x8d, 0x61, 0x15, 0xc0, 0x5a, 0x63, 0xee, 0x18, 0xbe, 0x96, 0xe9, 0x89, 0xb5, + 0xcc, 0x01, 0xf8, 0xb8, 0x92, 0xe3, 0x86, 0xee, 0x1c, 0x2a, 0x05, 0xf3, 0x76, 0x60, 0x8b, 0xfc, 0x9f, 0x69, 0x47, + 0x59, 0x55, 0x4c, 0x2c, 0x03, 0xe1, 0x72, 0x44, 0x42, 0xe6, 0xeb, 0xde, 0xc5, 0x20, 0x0a, 0x3e, 0x62, 0x64, 0xa7, + 0x54, 0x5c, 0xe7, 0x26, 0xbf, 0xea, 0x9f, 0x5f, 0x22, 0xf6, 0xba, 0x78, 0x5d, 0xbf, 0x7f, 0xe8, 0xef, 0xfe, 0xa4, + 0x15, 0xa0, 0x7a, 0xae, 0xec, 0xca, 0x6a, 0x26, 0x07, 0x9b, 0xc8, 0xf0, 0x73, 0xbd, 0x84, 0xca, 0xb4, 0x99, 0x00, + 0x21, 0x9c, 0xe3, 0x72, 0x72, 0x3d, 0x5a, 0x4c, 0xfc, 0x04, 0xd2, 0x18, 0x7a, 0x09, 0x4a, 0xe6, 0xfd, 0x11, 0x1e, + 0x5c, 0x0e, 0x08, 0xc4, 0xbb, 0xb8, 0x0a, 0x39, 0x5a, 0x1a, 0x24, 0x31, 0xbb, 0x9f, 0x62, 0x08, 0x25, 0x2e, 0x23, + 0x05, 0x6a, 0xd9, 0x9a, 0xb2, 0x6f, 0xc1, 0x72, 0x47, 0xd5, 0x61, 0x47, 0x98, 0x29, 0x4c, 0x95, 0xc8, 0x7f, 0x78, + 0x8c, 0xa4, 0x0a, 0x4f, 0xdd, 0xc9, 0xb3, 0x15, 0x52, 0x96, 0x93, 0x06, 0x12, 0x12, 0x78, 0x28, 0x44, 0x01, 0xfa, + 0x01, 0x5b, 0xa3, 0x8a, 0xc7, 0xff, 0x61, 0x5b, 0x02, 0xdd, 0x12, 0x9f, 0x58, 0x76, 0xbc, 0x61, 0x68, 0x0e, 0x79, + 0x8c, 0x44, 0x11, 0xb4, 0xc2, 0xcf, 0xaa, 0xe4, 0x07, 0x81, 0x12, 0x50, 0xc6, 0x45, 0x76, 0x14, 0xa8, 0x4a, 0x4c, + 0x70, 0x35, 0xd0, 0x83, 0xe8, 0xde, 0x65, 0xa0, 0x69, 0x3a, 0x78, 0xed, 0xd0, 0x30, 0x96, 0xc6, 0x54, 0x07, 0xdb, + 0x51, 0x21, 0x38, 0xd2, 0xe9, 0x90, 0x51, 0x70, 0x72, 0xfb, 0x0e, 0x97, 0x0d, 0x39, 0xdd, 0xee, 0x5a, 0xa1, 0xe8, + 0x19, 0xc8, 0xea, 0x5c, 0x6c, 0x9e, 0x67, 0x63, 0x22, 0x40, 0xfa, 0xc4, 0x3c, 0x54, 0x9c, 0x97, 0x19, 0x98, 0x36, + 0x79, 0xbc, 0x2d, 0x13, 0xc5, 0x1c, 0x4b, 0xae, 0x86, 0x7d, 0xc4, 0xd3, 0x7c, 0x3d, 0x93, 0xb2, 0xdf, 0x85, 0x01, + 0x47, 0x62, 0x98, 0xf5, 0x3b, 0xed, 0xf3, 0xda, 0x68, 0x73, 0x09, 0xf5, 0xa6, 0xc2, 0x24, 0xd1, 0xec, 0x58, 0x43, + 0xbd, 0x0a, 0x2d, 0x7e, 0x32, 0xb0, 0x7e, 0x0d, 0xa9, 0x37, 0x52, 0x33, 0xec, 0x8a, 0xe7, 0x23, 0x8f, 0x1f, 0xdd, + 0xa6, 0x56, 0xd6, 0x95, 0xd5, 0xcc, 0x36, 0x95, 0x18, 0xdf, 0x0f, 0xbb, 0xad, 0x6a, 0xcf, 0xb4, 0xca, 0xc7, 0xc3, + 0x97, 0x94, 0x4e, 0x07, 0xa2, 0xa9, 0x30, 0xb0, 0x87, 0x50, 0xc7, 0x02, 0xad, 0x8d, 0xc5, 0x2e, 0xca, 0xa3, 0x32, + 0xa5, 0xad, 0xd2, 0x18, 0xc6, 0x50, 0x1b, 0xc0, 0xd5, 0xed, 0x7a, 0x90, 0x96, 0x51, 0xd6, 0x5d, 0x4a, 0x0b, 0xc5, + 0x74, 0x0c, 0x6b, 0x85, 0x33, 0x25, 0xc3, 0x4d, 0x21, 0x4e, 0x03, 0x7c, 0x79, 0xe1, 0xff, 0xfe, 0x00, 0x56, 0xcd, + 0xed, 0x8e, 0x64, 0x1b, 0x97, 0x1d, 0x5d, 0x69, 0x85, 0xe7, 0xe9, 0xbc, 0x7c, 0x91, 0xb2, 0x2d, 0xd5, 0xa2, 0x61, + 0x1a, 0x1d, 0x65, 0x0c, 0xb5, 0x7d, 0xbb, 0x98, 0x31, 0x9c, 0x61, 0xc4, 0x5c, 0xf9, 0x06, 0x67, 0xbd, 0x96, 0xbc, + 0xfb, 0x2d, 0x23, 0xa7, 0x52, 0x5c, 0xf1, 0xa2, 0x4a, 0x0d, 0xaf, 0x7c, 0xf2, 0x1f, 0xe4, 0x6d, 0x52, 0xfc, 0x6a, + 0xd5, 0x18, 0x4a, 0x92, 0xcb, 0x89, 0xce, 0x9b, 0xd7, 0x70, 0xc2, 0xdb, 0x9e, 0xa6, 0x62, 0x86, 0xe2, 0xb1, 0x04, + 0xbf, 0xab, 0x3a, 0xdb, 0xcd, 0x1f, 0x7c, 0x2e, 0xc8, 0x5a, 0x4c, 0x96, 0xe5, 0xab, 0x0a, 0xce, 0xa4, 0x1e, 0x3f, + 0x7b, 0xb8, 0x53, 0x12, 0xa4, 0xba, 0x6d, 0xc8, 0xa7, 0x41, 0xa4, 0xb7, 0xcd, 0xea, 0x28, 0x43, 0x5e, 0x99, 0xd8, + 0x84, 0xa9, 0x53, 0xc7, 0x1b, 0x77, 0x5b, 0x2a, 0x26, 0x3b, 0x13, 0xe7, 0xe1, 0xff, 0xcc, 0x16, 0xbe, 0x4d, 0x3d, + 0xf9, 0x2b, 0xb6, 0x74, 0x90, 0xfc, 0x0a, 0xc4, 0x87, 0x63, 0x04, 0xf3, 0x39, 0x7d, 0x87, 0xc2, 0xa3, 0x8e, 0x65, + 0x60, 0x60, 0x62, 0xe5, 0xd9, 0x77, 0xfc, 0xdb, 0xf1, 0x96, 0x58, 0xa3, 0xb2, 0xca, 0x50, 0x0c, 0xc1, 0x20, 0xcd, + 0xeb, 0x00, 0x40, 0xae, 0x6c, 0x2a, 0xb6, 0x05, 0x22, 0x5b, 0x5e, 0x44, 0x8b, 0x77, 0xda, 0xb9, 0x11, 0xdc, 0x94, + 0xf8, 0x94, 0xbd, 0x3d, 0x65, 0x0c, 0x70, 0x0b, 0xec, 0x74, 0xec, 0xe0, 0x81, 0x98, 0x23, 0xa1, 0x76, 0x45, 0x16, + 0x4b, 0x52, 0x87, 0x8a, 0x45, 0xb3, 0xbe, 0x50, 0x62, 0x22, 0x86, 0x6c, 0x4d, 0x9d, 0x60, 0x45, 0xea, 0xa8, 0x3d, + 0x07, 0x16, 0x25, 0xcd, 0x3e, 0x43, 0x5e, 0x4c, 0x72, 0xc7, 0x44, 0x34, 0xe3, 0xc1, 0xcf, 0x42, 0x49, 0xcf, 0xbd, + 0x89, 0x85, 0xfc, 0xdd, 0x66, 0xf9, 0x0c, 0x7b, 0x87, 0x3f, 0x49, 0x15, 0xbe, 0x9c, 0xc2, 0x6a, 0x92, 0xd0, 0x56, + 0x2e, 0xbc, 0x5d, 0x12, 0xa0, 0x40, 0x59, 0xda, 0xa7, 0xc1, 0x81, 0x42, 0x1f, 0x0a, 0xca, 0x16, 0xcb, 0x94, 0x12, + 0x33, 0xe3, 0x22, 0xa6, 0xe4, 0x5e, 0xf4, 0x79, 0x3c, 0x5f, 0xd3, 0x77, 0x40, 0xa0, 0x72, 0xb3, 0xdf, 0x6c, 0x4c, + 0x72, 0xc0, 0xd0, 0x4c, 0x7f, 0xc2, 0x27, 0xb4, 0x7b, 0xbd, 0x64, 0x3f, 0x72, 0xe0, 0xfb, 0xc0, 0x71, 0x30, 0x7b, + 0xf2, 0x43, 0xca, 0x59, 0xab, 0xea, 0x3e, 0x0b, 0xf8, 0xfb, 0xe2, 0x05, 0xe2, 0xca, 0x24, 0x04, 0xba, 0x8b, 0x49, + 0x82, 0xd5, 0xa7, 0x60, 0x48, 0x3a, 0x01, 0x5d, 0xac, 0xb0, 0xb9, 0xd6, 0x6c, 0x39, 0x41, 0x17, 0x53, 0x59, 0xc1, + 0x9d, 0x3a, 0x94, 0xea, 0xe5, 0x61, 0x66, 0x3d, 0xac, 0xa6, 0xa7, 0x29, 0x48, 0x22, 0x9d, 0xec, 0xf6, 0x53, 0x92, + 0xbd, 0x26, 0x61, 0x64, 0xdf, 0x37, 0x33, 0x22, 0x00, 0xbe, 0xe8, 0x15, 0x22, 0xf6, 0xbd, 0x48, 0x39, 0x49, 0xa5, + 0x6b, 0xce, 0xe4, 0xb6, 0x42, 0x83, 0x58, 0x17, 0xfe, 0x55, 0x50, 0x37, 0xa5, 0xf9, 0x14, 0xdc, 0xa9, 0xbe, 0x81, + 0x5d, 0x02, 0xaf, 0xcd, 0xbb, 0x10, 0x34, 0x8d, 0x0d, 0xbe, 0x04, 0xb0, 0xb8, 0x0b, 0x03, 0x4f, 0xe0, 0x17, 0x5e, + 0x07, 0x70, 0xb3, 0x59, 0xa1, 0x56, 0x31, 0xd1, 0x9b, 0xf9, 0xa3, 0x5e, 0xd9, 0x78, 0xde, 0x9d, 0x04, 0x0b, 0xcb, + 0x49, 0x90, 0x7d, 0x86, 0x01, 0x2d, 0x5d, 0xbf, 0xe3, 0x62, 0x55, 0x0a, 0x2a, 0x21, 0xa4, 0xf4, 0xdd, 0xbf, 0x99, + 0xef, 0xe9, 0xb4, 0x5e, 0x8c, 0xf4, 0xcc, 0x00, 0x82, 0x5b, 0x92, 0x79, 0xd7, 0xd1, 0xb7, 0xa6, 0x67, 0x11, 0xf7, + 0x24, 0x2f, 0xbb, 0xcb, 0xae, 0x90, 0xd5, 0x22, 0xa6, 0xd4, 0xad, 0x9c, 0x5e, 0xc3, 0xbd, 0xcd, 0x3b, 0x09, 0xdc, + 0xcc, 0xe2, 0x96, 0x47, 0x09, 0x01, 0x57, 0x8e, 0xad, 0xa5, 0xd0, 0x30, 0xe2, 0xf5, 0x20, 0x83, 0x48, 0x90, 0xfe, + 0xed, 0x22, 0x43, 0xe9, 0x29, 0x9f, 0x8f, 0x6d, 0x24, 0xd4, 0xc3, 0x4d, 0xed, 0x08, 0x0e, 0xef, 0xde, 0x5c, 0x7d, + 0xc4, 0x1f, 0xa5, 0xd7, 0xf1, 0xa1, 0x37, 0x4e, 0xcb, 0xe5, 0x35, 0x36, 0x12, 0xc0, 0xed, 0xe3, 0xf6, 0x72, 0xe1, + 0x16, 0x0d, 0xcf, 0x6d, 0x35, 0xde, 0xed, 0xe8, 0x6f, 0x5f, 0xc0, 0xcd, 0xe7, 0xdb, 0x75, 0xe7, 0x7e, 0xf3, 0x33, + 0xe5, 0xe2, 0xa5, 0x8b, 0x8c, 0xe8, 0x82, 0xf1, 0xf2, 0x6a, 0x85, 0x14, 0x20, 0xcd, 0x0f, 0x60, 0xf7, 0xf1, 0xed, + 0x91, 0xee, 0x53, 0xd9, 0x2b, 0x24, 0x7d, 0xde, 0x2e, 0x15, 0x56, 0x22, 0x8e, 0x4f, 0x36, 0x8d, 0x2c, 0xe8, 0xb3, + 0x10, 0x5d, 0xaa, 0x9f, 0x92, 0x7c, 0x5e, 0xce, 0x0d, 0x3f, 0xfc, 0x74, 0x02, 0xba, 0x09, 0xcf, 0x06, 0x11, 0x94, + 0x45, 0x4e, 0x7b, 0x4a, 0x69, 0xdf, 0xc9, 0x3f, 0xa5, 0x28, 0xbc, 0x65, 0xa3, 0xfd, 0xd2, 0xaa, 0x9b, 0xfe, 0xac, + 0xba, 0x52, 0xbc, 0x7b, 0x78, 0xb5, 0xd9, 0x5d, 0xa6, 0xa1, 0x3c, 0x73, 0x73, 0xef, 0xab, 0x05, 0xfa, 0x15, 0xc9, + 0xc7, 0xc3, 0x00, 0x11, 0x57, 0xbb, 0xcb, 0x3c, 0x55, 0xbb, 0x67, 0x4d, 0xfb, 0x22, 0x6d, 0x0c, 0x57, 0x8e, 0x3d, + 0xbe, 0x7c, 0x12, 0x27, 0x17, 0xc7, 0xba, 0x39, 0x89, 0x54, 0x94, 0x8f, 0xf5, 0x57, 0x01, 0x86, 0x33, 0x6d, 0xce, + 0x40, 0xb2, 0xaa, 0xcb, 0x53, 0xa0, 0x1e, 0x99, 0x84, 0x27, 0x67, 0xf6, 0xcd, 0x6c, 0x00, 0xd8, 0x0c, 0x98, 0x86, + 0xd6, 0xbe, 0x9b, 0x27, 0xb3, 0xa8, 0x1a, 0x00, 0x47, 0xc9, 0x2f, 0x4e, 0x3d, 0x91, 0x65, 0x57, 0x58, 0xb3, 0xf1, + 0x7a, 0xe9, 0xee, 0xd7, 0x29, 0x49, 0x21, 0x3b, 0x67, 0x47, 0x91, 0x09, 0xf3, 0x71, 0x7c, 0xd5, 0xe8, 0x65, 0x69, + 0xfa, 0x86, 0x61, 0x00, 0x8b, 0x30, 0xcd, 0xdb, 0xdd, 0x76, 0xab, 0x3e, 0xad, 0x02, 0x42, 0xed, 0x9d, 0x73, 0x2b, + 0xed, 0x3f, 0x9c, 0x54, 0x34, 0x9c, 0xcd, 0x4b, 0x21, 0xd9, 0x57, 0x68, 0x50, 0x90, 0xd5, 0x98, 0x91, 0x8e, 0xf5, + 0x29, 0x09, 0x4c, 0x9b, 0x49, 0xfa, 0x76, 0x1b, 0xd4, 0x05, 0xa8, 0x4c, 0xf9, 0x72, 0x5d, 0x58, 0x53, 0x53, 0x6f, + 0x4c, 0xf1, 0xe5, 0xde, 0xbe, 0x40, 0xd3, 0xcc, 0xd0, 0x5e, 0xce, 0x6d, 0x28, 0x65, 0xbd, 0xec, 0x2a, 0xc2, 0x83, + 0x6c, 0xa5, 0xf3, 0xf8, 0x2e, 0xc9, 0xdf, 0xe4, 0x03, 0x6a, 0x2b, 0x16, 0x97, 0x7b, 0xf5, 0x22, 0x6e, 0x37, 0x19, + 0x9a, 0x11, 0x1a, 0x56, 0x53, 0xb0, 0xdc, 0xbd, 0xf9, 0x4c, 0xef, 0x66, 0x73, 0xf5, 0x39, 0xbb, 0xf8, 0xec, 0x60, + 0x1b, 0x24, 0x90, 0x7a, 0xc4, 0xca, 0x9a, 0xec, 0x21, 0x25, 0x86, 0x89, 0x69, 0xca, 0x9e, 0x00, 0x19, 0xc0, 0x1f, + 0x93, 0xf8, 0x7f, 0xfc, 0xfd, 0xef, 0xc1, 0x1d, 0xda, 0xef, 0xce, 0x17, 0x23, 0xef, 0xf9, 0x87, 0xd3, 0x03, 0xa7, + 0x9f, 0xdb, 0xfb, 0x38, 0xb7, 0x47, 0x44, 0x8d, 0x2a, 0x2e, 0x2a, 0x5a, 0xf1, 0xe4, 0x50, 0x55, 0x5a, 0x87, 0xf9, + 0x4e, 0xdc, 0x29, 0x15, 0xae, 0xdc, 0xcb, 0xe0, 0x7e, 0xbf, 0x1f, 0xae, 0xff, 0x5f, 0x9d, 0x2d, 0x59, 0x7f, 0xff, + 0x6f, 0x6b, 0xfa, 0x7f, 0xe9, 0x4d, 0x58, 0x1a, 0xee, 0x7f, 0x6b, 0x70, 0xe9, 0xb7, 0x67, 0x5a, 0x5f, 0xbb, 0xf6, + 0x6f, 0x1d, 0x20, 0x28, 0x64, 0x3f, 0xd9, 0xb3, 0x76, 0xe9, 0xa9, 0xcb, 0x2c, 0x06, 0xca, 0xc1, 0xff, 0x9f, 0x65, + 0x77, 0xec, 0xd9, 0x09, 0x53, 0x1b, 0x1f, 0xdf, 0xcf, 0x30, 0x0e, 0xb8, 0x55, 0x22, 0x8c, 0x71, 0xc8, 0xeb, 0xca, + 0xef, 0x6a, 0xe4, 0x73, 0x48, 0x27, 0xd6, 0x2a, 0xa0, 0x5f, 0xd6, 0x2f, 0x0a, 0xe2, 0xbe, 0x87, 0x3b, 0x13, 0xb1, + 0x24, 0x78, 0xa0, 0x6e, 0x9c, 0x0a, 0xca, 0x8f, 0xa4, 0x69, 0x7a, 0x8e, 0x92, 0x5f, 0xda, 0xff, 0x31, 0x5b, 0xc3, + 0xaa, 0xf7, 0x17, 0xc4, 0x8b, 0x93, 0xdb, 0x7f, 0x61, 0x21, 0xed, 0x1b, 0x92, 0x18, 0x1b, 0x53, 0xb7, 0x6e, 0x9c, + 0x3a, 0x9d, 0xde, 0xb3, 0xad, 0xea, 0x0c, 0xc2, 0x1f, 0x55, 0x29, 0x4c, 0xde, 0xae, 0x05, 0x51, 0x4d, 0xef, 0xb3, + 0x77, 0x75, 0x34, 0xa0, 0x96, 0x92, 0x67, 0x7e, 0x9b, 0xc1, 0xb3, 0x2b, 0x7c, 0xbf, 0x1a, 0xeb, 0xa7, 0xe0, 0x84, + 0x34, 0x72, 0x99, 0xb2, 0x7e, 0x04, 0x6b, 0xed, 0xe6, 0x83, 0x17, 0x38, 0x89, 0xce, 0xd9, 0x2a, 0xe7, 0x24, 0xaa, + 0xc6, 0xfb, 0x82, 0xf0, 0x3f, 0x67, 0x2c, 0x7c, 0x86, 0x86, 0x0b, 0xb1, 0x9c, 0x80, 0x6a, 0x4c, 0xe1, 0x98, 0x79, + 0xc7, 0xf5, 0x73, 0x7b, 0x6d, 0xbf, 0xf2, 0x8b, 0x21, 0xd2, 0x6c, 0x0c, 0xde, 0xaa, 0x7e, 0xc1, 0x50, 0xb2, 0x1f, + 0x0f, 0x7b, 0x70, 0xe8, 0xa5, 0xe9, 0x45, 0xd6, 0xfe, 0x29, 0x7c, 0x91, 0xaf, 0x7c, 0x48, 0x2d, 0xcd, 0x6b, 0xa5, + 0x18, 0x2f, 0x6e, 0xd8, 0xc5, 0xbf, 0x83, 0xf4, 0xc6, 0xec, 0xb0, 0xdb, 0xb8, 0x81, 0x22, 0x91, 0xc6, 0x1a, 0x32, + 0xf6, 0x3f, 0xad, 0x93, 0x1c, 0x26, 0x2c, 0x31, 0x08, 0xeb, 0x27, 0xb1, 0x79, 0xd5, 0xe7, 0x4e, 0xb2, 0x6f, 0x92, + 0x66, 0x57, 0xa1, 0x69, 0x00, 0x08, 0xcf, 0x1e, 0x91, 0xbb, 0xab, 0x8f, 0x96, 0x6c, 0x7b, 0xc9, 0xe5, 0x6f, 0xc3, + 0xc8, 0xd9, 0x87, 0x4d, 0x5b, 0x1b, 0x9c, 0xda, 0x9c, 0xc4, 0xa6, 0x8d, 0x55, 0xf8, 0xdc, 0x74, 0xc2, 0x7d, 0x7f, + 0xed, 0x59, 0x5c, 0x33, 0x2b, 0x89, 0xe2, 0xda, 0x0a, 0x71, 0x53, 0xf0, 0x03, 0x0c, 0x24, 0xcc, 0x18, 0x73, 0xb6, + 0x51, 0x20, 0x20, 0x49, 0x99, 0xb2, 0x6a, 0x43, 0x7c, 0xf9, 0x41, 0x0c, 0x70, 0x33, 0x13, 0x36, 0x01, 0xb5, 0xfe, + 0xc8, 0xca, 0x0d, 0x27, 0x4b, 0x42, 0xc8, 0xb8, 0xdb, 0x27, 0xbf, 0x60, 0x60, 0xc6, 0x8f, 0x18, 0xa5, 0xc6, 0x77, + 0xeb, 0xfd, 0x63, 0x26, 0x7f, 0xba, 0xfe, 0x93, 0x6d, 0xe3, 0xb7, 0xe1, 0x42, 0x19, 0xb6, 0xe6, 0x33, 0xb4, 0xac, + 0x0a, 0x0c, 0xca, 0xa8, 0xbc, 0xb3, 0x9e, 0xb9, 0xed, 0x93, 0x58, 0x55, 0x49, 0x7c, 0x43, 0xab, 0x32, 0x47, 0xf0, + 0xb8, 0x17, 0xa5, 0x34, 0x25, 0x58, 0x82, 0xdb, 0xf7, 0x2b, 0xe4, 0x2a, 0xe7, 0xe1, 0xcb, 0x13, 0x47, 0x92, 0x2b, + 0x17, 0xa5, 0x57, 0x6f, 0x38, 0xe2, 0xd4, 0xa5, 0x94, 0x9d, 0x65, 0x60, 0x4f, 0x36, 0x0f, 0xa9, 0x20, 0xa5, 0xa1, + 0x96, 0x6d, 0xdb, 0x5a, 0xf9, 0x25, 0x7a, 0x2d, 0xb5, 0xba, 0x60, 0x69, 0x29, 0xe0, 0xc6, 0x8c, 0x28, 0x8f, 0x6a, + 0xeb, 0xe6, 0xea, 0x28, 0xa5, 0x79, 0x50, 0x57, 0xc1, 0x43, 0x6d, 0x1e, 0xb9, 0xb0, 0x86, 0x5f, 0xfa, 0xf8, 0xe8, + 0x91, 0x31, 0x32, 0xed, 0x06, 0x3e, 0x9e, 0x66, 0xc3, 0x66, 0x07, 0x5f, 0xa8, 0x3a, 0x35, 0x21, 0x94, 0x2f, 0xd0, + 0x79, 0xa3, 0x4a, 0xb2, 0x1c, 0xbc, 0x42, 0xc6, 0x2d, 0x4e, 0x12, 0xf7, 0x6f, 0xc8, 0xfa, 0xa2, 0x58, 0x5a, 0xb4, + 0xa7, 0x95, 0x55, 0x41, 0x69, 0x9b, 0xd4, 0xfc, 0xd7, 0x98, 0x7e, 0xe5, 0x21, 0xa9, 0xa7, 0x35, 0xde, 0x1f, 0x72, + 0xbb, 0xe4, 0x1e, 0x77, 0xdf, 0x82, 0x33, 0xa3, 0x76, 0xbb, 0x02, 0x90, 0x76, 0x7d, 0x1c, 0x21, 0x91, 0x39, 0x11, + 0x4e, 0x29, 0xe9, 0xc1, 0x8d, 0x1c, 0xa1, 0xf9, 0xdd, 0x3e, 0xb6, 0x9a, 0x48, 0xb7, 0x70, 0x1c, 0xb1, 0xbf, 0x2c, + 0x63, 0x67, 0x70, 0x12, 0xaf, 0x5d, 0xfc, 0xda, 0x23, 0x14, 0xd9, 0x92, 0x4a, 0x7d, 0x6d, 0xc9, 0x95, 0x76, 0xf9, + 0x4e, 0xed, 0x65, 0xdc, 0xa1, 0xb0, 0x4d, 0x6f, 0x5d, 0x8a, 0xff, 0xc3, 0x29, 0xa5, 0xfa, 0x8e, 0xdf, 0xa8, 0xf4, + 0xb7, 0xdd, 0xfd, 0x5e, 0x6d, 0x05, 0xcb, 0xf9, 0xab, 0x1a, 0xd1, 0x36, 0xed, 0xda, 0x2e, 0x5a, 0xbc, 0x39, 0xd0, + 0xd6, 0xa1, 0xbe, 0x42, 0xff, 0xbc, 0x63, 0x54, 0x05, 0x3a, 0x24, 0x1d, 0xca, 0xb0, 0x99, 0x36, 0xe4, 0xc4, 0x6a, + 0x18, 0x84, 0xfd, 0xa2, 0x50, 0xfb, 0xe0, 0x7f, 0x32, 0x65, 0x45, 0x03, 0x6a, 0xcd, 0x39, 0xd3, 0x96, 0x33, 0xe0, + 0xfa, 0x64, 0xb3, 0xdb, 0xd4, 0x7a, 0xaa, 0x31, 0xce, 0x68, 0xca, 0xb0, 0xad, 0x5b, 0xb6, 0xec, 0xd6, 0xcd, 0x1c, + 0x49, 0xf1, 0x07, 0x33, 0xc3, 0x27, 0xfd, 0xe7, 0xd7, 0xba, 0x01, 0xca, 0xbb, 0x57, 0xef, 0x67, 0x72, 0xaa, 0x3a, + 0xe5, 0x4f, 0xf3, 0xf5, 0xd3, 0x5f, 0x2d, 0x79, 0xfd, 0xa3, 0xbf, 0x78, 0x89, 0xde, 0xf0, 0x17, 0x6c, 0x19, 0xe3, + 0x66, 0xbb, 0x4c, 0x7a, 0x09, 0x3a, 0x2b, 0x35, 0xfa, 0x6c, 0x83, 0xa5, 0xe0, 0x2e, 0x18, 0x09, 0xd4, 0xb4, 0x4d, + 0x59, 0x97, 0xf6, 0x7d, 0x71, 0xfd, 0x74, 0xa3, 0xad, 0x2f, 0xb6, 0xaa, 0x87, 0xb8, 0xef, 0xab, 0xd7, 0xc1, 0x7c, + 0x3e, 0xec, 0xbe, 0xfd, 0x84, 0x4d, 0xf8, 0xa7, 0x10, 0xa0, 0x0d, 0x3b, 0x3d, 0x56, 0x8d, 0x8b, 0xf7, 0xd5, 0xb0, + 0xb8, 0xae, 0xda, 0xe2, 0xac, 0x9a, 0x17, 0xe7, 0xd5, 0xf5, 0xe1, 0xdd, 0x5d, 0xbf, 0x65, 0xf8, 0x1b, 0x56, 0xd3, + 0x1b, 0xb2, 0xf6, 0x33, 0xa6, 0xa9, 0x65, 0xc2, 0xe9, 0x69, 0xb7, 0x7c, 0x84, 0xd3, 0x2e, 0xdd, 0x9d, 0xdd, 0x79, + 0xbc, 0x7d, 0x83, 0x5e, 0xa5, 0x68, 0x97, 0x05, 0x86, 0xea, 0xc4, 0x82, 0xc4, 0xbc, 0xc6, 0xb6, 0x37, 0xeb, 0x90, + 0x33, 0x18, 0xc8, 0x73, 0xc5, 0x35, 0xce, 0x5d, 0x8c, 0x99, 0xbc, 0xa1, 0x00, 0x85, 0x63, 0x49, 0x54, 0xc3, 0xaa, + 0x95, 0x15, 0x75, 0x24, 0xb1, 0x20, 0x88, 0x17, 0x4c, 0x9d, 0x54, 0xc1, 0x2e, 0xdd, 0xc8, 0xbb, 0x1a, 0xc1, 0x00, + 0xb7, 0x9d, 0x4d, 0xb9, 0xb8, 0x2f, 0x1a, 0xd9, 0x62, 0x2b, 0x55, 0x2d, 0xc2, 0x95, 0x48, 0x39, 0x2e, 0xad, 0x6f, + 0x99, 0xdb, 0xf7, 0xba, 0x5f, 0x9c, 0x97, 0xe2, 0x7f, 0xda, 0x01, 0x5e, 0x47, 0x86, 0xac, 0xec, 0x05, 0xbf, 0x52, + 0x32, 0xad, 0x13, 0xeb, 0x54, 0xd3, 0xba, 0xc6, 0x61, 0xf6, 0xf2, 0xd7, 0xf2, 0x40, 0x14, 0x23, 0xfa, 0xa2, 0x56, + 0x2a, 0x6b, 0x74, 0x98, 0xc4, 0x20, 0xd3, 0xd0, 0x94, 0x63, 0x0d, 0xad, 0x15, 0x67, 0xf1, 0x68, 0x57, 0x41, 0x62, + 0xe3, 0x5b, 0xf9, 0x35, 0x27, 0x36, 0xe8, 0x00, 0x62, 0x81, 0x8e, 0xcb, 0x3a, 0x13, 0xfe, 0x3f, 0xea, 0xa1, 0xdc, + 0x37, 0xfd, 0x9f, 0x28, 0xaf, 0x0a, 0xd1, 0x67, 0xe8, 0xdb, 0x25, 0x57, 0x70, 0x09, 0x31, 0xea, 0xc1, 0x9a, 0xa8, + 0xe6, 0xce, 0x6f, 0xd1, 0x27, 0x90, 0x02, 0x82, 0xa7, 0x33, 0x18, 0x9c, 0xa8, 0x36, 0xd2, 0xa0, 0x99, 0x11, 0xa9, + 0x18, 0x0a, 0xef, 0x47, 0x53, 0xb5, 0x6e, 0x47, 0x32, 0xb6, 0x57, 0x32, 0x6f, 0xf5, 0x6b, 0xab, 0x40, 0x61, 0x3e, + 0x5e, 0xae, 0x1a, 0x01, 0xa0, 0xe5, 0xef, 0xdb, 0x9f, 0xd4, 0xd5, 0x38, 0x7a, 0xd7, 0x6d, 0x0a, 0x47, 0xe7, 0x88, + 0x27, 0x86, 0xc5, 0x16, 0xa2, 0xd5, 0x13, 0xa8, 0xf9, 0x0e, 0x0d, 0x57, 0xed, 0x9b, 0xcc, 0x60, 0x5e, 0x4e, 0x4e, + 0x72, 0x7e, 0x87, 0xa9, 0x77, 0xbe, 0x67, 0x8a, 0x30, 0xa9, 0x09, 0xa2, 0xea, 0x3d, 0x14, 0x04, 0x0b, 0xf6, 0x42, + 0x0b, 0xf7, 0xeb, 0x51, 0x92, 0x82, 0xc9, 0x80, 0xae, 0x68, 0xed, 0x88, 0x95, 0x15, 0x53, 0x6a, 0x34, 0x12, 0x19, + 0xae, 0x72, 0xd3, 0xdf, 0xc7, 0x84, 0x4a, 0x01, 0x68, 0xb7, 0x7f, 0x21, 0x63, 0xe4, 0xe0, 0x82, 0x35, 0xd1, 0x6e, + 0x48, 0x43, 0x0b, 0xb7, 0x54, 0x16, 0x04, 0xf0, 0x82, 0x06, 0xab, 0xfd, 0x82, 0xca, 0x71, 0xe1, 0x13, 0x0b, 0x53, + 0xaf, 0x84, 0x5d, 0xf0, 0xe7, 0x86, 0xa5, 0xf5, 0xcf, 0x0f, 0x03, 0x8a, 0xf5, 0x0f, 0x61, 0xd8, 0x97, 0xcf, 0xf3, + 0x9c, 0xf8, 0xd8, 0x08, 0xc9, 0xd5, 0x56, 0x83, 0x10, 0x2f, 0x4a, 0x7a, 0x2b, 0x66, 0x16, 0xb5, 0xde, 0x1e, 0x9e, + 0xd7, 0xbe, 0x74, 0x07, 0xb1, 0xea, 0x97, 0xd8, 0xd8, 0xec, 0x6e, 0x40, 0x90, 0xfd, 0xa6, 0xa8, 0x94, 0xb1, 0xc9, + 0xf7, 0x3c, 0xc9, 0xee, 0xe5, 0xf3, 0x19, 0x81, 0x53, 0xf6, 0xd9, 0x67, 0xbe, 0x26, 0xe0, 0xcb, 0x9e, 0x1e, 0x9b, + 0x3d, 0xad, 0xb3, 0x73, 0x4e, 0x1f, 0x1e, 0xa2, 0x86, 0xda, 0x74, 0x2f, 0x0c, 0x86, 0x2b, 0x90, 0x5f, 0xb8, 0x4f, + 0x88, 0x09, 0x97, 0x9f, 0x9f, 0x46, 0x3b, 0x73, 0x27, 0xe4, 0xc1, 0xd9, 0xe1, 0x13, 0x50, 0x01, 0x33, 0x7b, 0xa7, + 0x92, 0xe6, 0x6d, 0xf5, 0x88, 0x8f, 0x5a, 0x91, 0xd8, 0x03, 0x98, 0xae, 0xbb, 0xe0, 0x3e, 0x5d, 0xef, 0x56, 0xf6, + 0x5d, 0x3c, 0x15, 0xa8, 0x7b, 0x6d, 0xab, 0x6d, 0xea, 0xaf, 0x74, 0xc7, 0xd3, 0x17, 0x85, 0x01, 0xc0, 0xec, 0x2e, + 0x41, 0x0b, 0xbe, 0x92, 0x18, 0xf6, 0xe0, 0xbd, 0x9c, 0xa4, 0xdf, 0x62, 0x07, 0x4f, 0xc6, 0xb5, 0x51, 0x0d, 0xd4, + 0xc2, 0x7c, 0x77, 0x43, 0xcd, 0xaa, 0x1a, 0x48, 0x9c, 0x23, 0xe1, 0x6c, 0xfd, 0xac, 0x3d, 0xe6, 0x8b, 0x9d, 0x2b, + 0x8e, 0xfd, 0x8f, 0x0a, 0xbf, 0xc2, 0xf6, 0x8c, 0xa5, 0x03, 0xaf, 0x0c, 0x2b, 0xe9, 0x18, 0x0c, 0xc8, 0xcf, 0x75, + 0x9c, 0x48, 0xa3, 0xf9, 0xfb, 0xe8, 0x8b, 0x04, 0x35, 0xd0, 0x6f, 0x7c, 0x1e, 0x5f, 0xba, 0xe4, 0x53, 0xad, 0x1f, + 0x08, 0xf8, 0x20, 0x03, 0x6a, 0xcf, 0xe8, 0x8c, 0x16, 0x4f, 0x73, 0xfd, 0x49, 0x7f, 0xcc, 0x25, 0xeb, 0x1f, 0xfd, + 0xd3, 0x2c, 0x4e, 0xad, 0xc5, 0x45, 0x35, 0xc1, 0x7b, 0x0a, 0xfb, 0x9e, 0x02, 0xfe, 0x2e, 0x59, 0x64, 0xc3, 0x32, + 0x9a, 0x47, 0xb1, 0xa6, 0x41, 0x94, 0xd4, 0xfa, 0xc8, 0xad, 0x4d, 0x3e, 0xf6, 0x7d, 0x0f, 0xab, 0x42, 0x5f, 0xe9, + 0xc2, 0x77, 0x55, 0x8b, 0xc5, 0x6c, 0xd5, 0x99, 0x48, 0xb9, 0x9e, 0x51, 0xa9, 0xc0, 0x11, 0x56, 0x9a, 0x23, 0xc7, + 0x34, 0xa5, 0xe1, 0xc0, 0xe1, 0x14, 0x6b, 0x52, 0x80, 0x7d, 0xfd, 0x4b, 0xdf, 0xda, 0x5a, 0x9e, 0x4f, 0xe1, 0xb6, + 0xe1, 0x2d, 0xce, 0xeb, 0x32, 0x94, 0xa4, 0x56, 0x01, 0xcb, 0xbe, 0x8a, 0x05, 0xc4, 0x45, 0xbe, 0xaa, 0x36, 0x27, + 0x8c, 0x51, 0x93, 0x0b, 0xb5, 0x87, 0xcc, 0x0d, 0xd4, 0x44, 0xa7, 0x90, 0x5e, 0x70, 0xda, 0x77, 0x93, 0xd8, 0x5a, + 0xd7, 0x32, 0xeb, 0xeb, 0xc4, 0x52, 0xa5, 0xcd, 0xb3, 0xbd, 0x23, 0x1d, 0x90, 0xcb, 0x98, 0x84, 0x20, 0x89, 0x25, + 0xa8, 0xf0, 0xd8, 0xfe, 0xaa, 0x9f, 0x8b, 0x04, 0x20, 0x81, 0xed, 0x8b, 0xf8, 0x32, 0x70, 0x94, 0xa4, 0xa2, 0x6a, + 0x6a, 0x6d, 0x06, 0x4c, 0xcc, 0x3b, 0x1d, 0x55, 0x6a, 0x51, 0x83, 0x20, 0x40, 0x64, 0xe2, 0x2c, 0x12, 0x39, 0x3d, + 0x8a, 0x1e, 0xee, 0x68, 0xa7, 0x85, 0x4c, 0xd1, 0x0a, 0x4a, 0x64, 0xed, 0x21, 0x49, 0x0f, 0x5f, 0x23, 0x14, 0x83, + 0x13, 0xe7, 0xcc, 0x05, 0xbf, 0xd7, 0x26, 0xbf, 0x9f, 0x5a, 0xe6, 0xde, 0xb5, 0xd8, 0x59, 0x7c, 0xe5, 0x51, 0xae, + 0x9e, 0x6c, 0x04, 0xdc, 0x0e, 0xe8, 0xee, 0x05, 0x05, 0xd8, 0xdb, 0x9b, 0x00, 0x03, 0xaf, 0xb4, 0xa8, 0xb5, 0x6c, + 0xe3, 0xb2, 0x5c, 0x13, 0xd6, 0x96, 0xfc, 0x9f, 0xdf, 0x4b, 0x27, 0x27, 0x9b, 0x28, 0x74, 0x34, 0xc9, 0xa9, 0x12, + 0x1d, 0x41, 0x1a, 0xc3, 0xaa, 0x17, 0x17, 0x90, 0x69, 0x4f, 0x93, 0x37, 0x6e, 0xd9, 0x12, 0x46, 0x66, 0x6f, 0x01, + 0xbb, 0xa7, 0xb7, 0x0c, 0x1c, 0xa9, 0xfa, 0xbf, 0x9f, 0xa6, 0x12, 0x3b, 0x05, 0x11, 0x84, 0x7a, 0xee, 0x58, 0xb2, + 0x0b, 0x64, 0x6c, 0xf5, 0x77, 0xcc, 0xb4, 0x69, 0xb2, 0x09, 0xe1, 0x11, 0x32, 0xe7, 0xbd, 0x72, 0x5b, 0x84, 0x18, + 0x4a, 0x0b, 0x52, 0xf0, 0xb5, 0xd3, 0x29, 0x82, 0xc3, 0x3c, 0x5d, 0x86, 0x0e, 0x1f, 0xc2, 0x19, 0x99, 0x31, 0xfe, + 0x54, 0xdc, 0x1b, 0x60, 0xde, 0x5d, 0x88, 0x1d, 0x26, 0xeb, 0x95, 0x21, 0x77, 0x44, 0x1e, 0xdf, 0x26, 0x79, 0x7a, + 0xb7, 0xcb, 0xa0, 0x4c, 0xe9, 0xf0, 0xc9, 0x24, 0xe2, 0x53, 0x71, 0xaa, 0x48, 0xb5, 0xa0, 0x6d, 0xf5, 0xed, 0xf7, + 0x65, 0xd0, 0x7b, 0xcf, 0xbe, 0xf5, 0x3e, 0x0a, 0x88, 0xae, 0x37, 0x0d, 0xdb, 0x34, 0x4f, 0x43, 0x83, 0x1c, 0xc3, + 0xfc, 0x74, 0x6b, 0x99, 0x4e, 0xd5, 0xe5, 0x2f, 0x7a, 0x6d, 0x91, 0x2f, 0x80, 0x4d, 0x3d, 0x0d, 0xaa, 0xb3, 0xda, + 0x26, 0x10, 0x21, 0x7d, 0x20, 0x66, 0x89, 0x8f, 0x62, 0xc5, 0xf8, 0xec, 0x35, 0x91, 0x0b, 0x7e, 0x96, 0x9f, 0x43, + 0xee, 0xed, 0x8d, 0x1f, 0xf9, 0xa4, 0xa0, 0xf7, 0xe3, 0x71, 0x76, 0x06, 0xf1, 0x7c, 0x9c, 0xce, 0x76, 0xaa, 0x20, + 0xa6, 0xbf, 0xfb, 0xff, 0x4c, 0x53, 0xd4, 0x1f, 0x20, 0x6c, 0x12, 0x2f, 0x0e, 0x13, 0xbc, 0x56, 0x09, 0x37, 0x09, + 0x3a, 0xa9, 0x7b, 0x28, 0x07, 0x6c, 0x22, 0x80, 0xaf, 0x3c, 0x23, 0x6e, 0x60, 0xba, 0x54, 0xf0, 0x34, 0xf2, 0x0e, + 0xc2, 0xe1, 0x4e, 0xc7, 0x93, 0x76, 0xb8, 0xaf, 0xa2, 0x8d, 0xc5, 0xe3, 0x63, 0x06, 0x91, 0x3f, 0x28, 0xfa, 0x9f, + 0x1a, 0x94, 0x46, 0x7e, 0xbe, 0x98, 0x2f, 0xcd, 0x5c, 0xad, 0xc7, 0x92, 0x36, 0x0a, 0x36, 0xab, 0x50, 0xba, 0x65, + 0xbc, 0x17, 0x17, 0xb6, 0xe8, 0x29, 0x34, 0xfb, 0xfd, 0x69, 0x52, 0x4e, 0xa5, 0xbd, 0xac, 0x5a, 0x93, 0x5e, 0x4b, + 0x6e, 0xef, 0x99, 0x4d, 0xf4, 0x13, 0x60, 0x25, 0x7e, 0x2b, 0x5a, 0xbc, 0xf4, 0x58, 0x94, 0xdf, 0xa5, 0x1a, 0x01, + 0x19, 0x82, 0xe7, 0x4f, 0x1e, 0x03, 0xbb, 0x15, 0xe9, 0xe9, 0xdf, 0x16, 0x97, 0xbe, 0x3b, 0x89, 0xd3, 0xff, 0x53, + 0xc8, 0xfe, 0xc0, 0x8f, 0x19, 0x58, 0x7f, 0xc6, 0x22, 0x55, 0x70, 0x09, 0xb7, 0xdb, 0xc4, 0xe6, 0x0b, 0xa8, 0x8a, + 0xcb, 0xed, 0xb9, 0xa3, 0x4a, 0xec, 0x27, 0x85, 0x0f, 0x3e, 0x8e, 0x4d, 0x6b, 0x11, 0xfe, 0x76, 0x17, 0x99, 0xfc, + 0xab, 0xe3, 0x12, 0x84, 0x57, 0xdd, 0xf8, 0xa0, 0xdf, 0x33, 0x5a, 0x3d, 0xcd, 0x7f, 0x9e, 0xfe, 0x9b, 0x25, 0xff, + 0xfc, 0xe8, 0x9f, 0x66, 0xe5, 0xad, 0x54, 0x3d, 0xe2, 0x01, 0x57, 0xe3, 0x25, 0xe2, 0xf1, 0xe4, 0xf5, 0xfc, 0xa3, + 0x64, 0x57, 0x75, 0x0d, 0x15, 0x5e, 0x9e, 0xc8, 0x05, 0x5a, 0x46, 0x35, 0xdb, 0x7a, 0x8e, 0x5e, 0x28, 0xd7, 0x1d, + 0xc5, 0x92, 0x44, 0x9b, 0x5e, 0x7e, 0x8b, 0xf4, 0x6a, 0x90, 0x24, 0x98, 0xed, 0xbf, 0x93, 0x35, 0x20, 0xd4, 0x1a, + 0x66, 0x56, 0xa9, 0x81, 0xc5, 0x73, 0xdb, 0x96, 0x94, 0xf3, 0x2a, 0xde, 0x1f, 0x45, 0x7e, 0xf9, 0x21, 0x0c, 0x58, + 0x0c, 0x46, 0x6f, 0x84, 0x26, 0xe0, 0x29, 0x22, 0x23, 0x47, 0x55, 0x5d, 0x48, 0xfc, 0x76, 0x47, 0x48, 0xbc, 0x95, + 0x4b, 0xa5, 0xaf, 0x04, 0x90, 0xaf, 0x65, 0xf5, 0xa9, 0xab, 0xc1, 0x5d, 0x7f, 0xd8, 0x93, 0xf4, 0x8d, 0x77, 0xe6, + 0x37, 0xea, 0xf2, 0x56, 0x69, 0xf4, 0x04, 0xcc, 0xce, 0x36, 0x4c, 0x65, 0xc4, 0x49, 0xe4, 0xd0, 0xe6, 0x62, 0x07, + 0x56, 0x99, 0x75, 0x33, 0xba, 0x82, 0x3f, 0x76, 0xe7, 0x2e, 0x24, 0x65, 0xcd, 0xb5, 0x4f, 0x32, 0xfd, 0xd0, 0x8a, + 0xe3, 0x2e, 0x81, 0xf1, 0xbe, 0xb4, 0xbc, 0x30, 0x3c, 0x45, 0x4a, 0x6d, 0x53, 0x0a, 0x1a, 0x90, 0x5f, 0xc5, 0x43, + 0x8a, 0x36, 0x41, 0x20, 0x27, 0x7b, 0xa5, 0x95, 0xea, 0x23, 0x95, 0xbb, 0xec, 0x19, 0xd3, 0xe6, 0x01, 0xa7, 0xd9, + 0x0c, 0x4a, 0x60, 0x9c, 0x4e, 0xfb, 0x64, 0x6d, 0x37, 0x9d, 0xdb, 0x6f, 0x92, 0xb2, 0xf0, 0x6b, 0x14, 0x4a, 0x6e, + 0xfe, 0x36, 0xfd, 0xfb, 0x96, 0xaf, 0x9e, 0xf9, 0x47, 0x82, 0xbd, 0xd2, 0x9f, 0xfd, 0xf5, 0xbe, 0xb2, 0x8b, 0x73, + 0xa5, 0x5b, 0x87, 0x85, 0xe5, 0xe2, 0x61, 0x7f, 0x74, 0x24, 0x80, 0x4c, 0x10, 0x2b, 0xdd, 0xb0, 0xc6, 0xf0, 0xfb, + 0x44, 0xcd, 0x3e, 0xf3, 0x8b, 0xa3, 0xa3, 0x61, 0xe5, 0x1b, 0xbb, 0x59, 0x27, 0x58, 0x0e, 0xff, 0xcf, 0xfd, 0x97, + 0xcd, 0x37, 0xbb, 0xcd, 0xe1, 0xc6, 0xc6, 0x6e, 0x9f, 0x05, 0xc6, 0x31, 0x37, 0xd7, 0x6b, 0x04, 0xc6, 0x48, 0xed, + 0xd0, 0xe4, 0x87, 0xc6, 0x99, 0xe3, 0xaa, 0x4c, 0xd9, 0x3b, 0xa2, 0x16, 0x69, 0x5c, 0xcf, 0x4a, 0x8e, 0xb4, 0xd0, + 0x2e, 0x96, 0xc5, 0xa1, 0x51, 0x24, 0x34, 0xad, 0x17, 0x1b, 0x39, 0xee, 0x87, 0xe7, 0xb3, 0x61, 0xc0, 0x53, 0xc2, + 0xda, 0x81, 0xb3, 0x11, 0x13, 0x41, 0x86, 0xdb, 0x29, 0x42, 0x37, 0xe4, 0x60, 0x80, 0x6e, 0xe8, 0x1c, 0xc1, 0x73, + 0x27, 0x67, 0xce, 0x8f, 0x0b, 0x6f, 0x98, 0x90, 0x0c, 0xa3, 0x04, 0x90, 0x63, 0xb2, 0x92, 0x6e, 0xdc, 0xdb, 0xbd, + 0x69, 0x77, 0x5e, 0x50, 0xd5, 0xc5, 0x50, 0x5b, 0xea, 0x49, 0x47, 0xea, 0xc5, 0x07, 0x12, 0xc3, 0xb6, 0xd3, 0xc9, + 0xf3, 0xca, 0xe8, 0xd5, 0x44, 0xf7, 0xfb, 0x98, 0xe6, 0xba, 0x2f, 0x9a, 0x23, 0xba, 0x02, 0x96, 0x33, 0x99, 0x5d, + 0x4b, 0xc2, 0xd9, 0xee, 0x3e, 0x9a, 0xd0, 0x73, 0x8d, 0x63, 0x51, 0x28, 0x14, 0x6c, 0x69, 0xba, 0x1b, 0xcf, 0xac, + 0xc3, 0xc5, 0x3f, 0xd4, 0xc5, 0x55, 0x06, 0x8a, 0xb3, 0xa6, 0x77, 0x22, 0x71, 0xdf, 0x46, 0x17, 0x06, 0x38, 0x41, + 0x93, 0x8b, 0x1e, 0xf6, 0x44, 0x18, 0x5a, 0x50, 0xd3, 0x5c, 0xca, 0x9f, 0x5b, 0x8f, 0x89, 0x6e, 0x30, 0x38, 0xce, + 0x95, 0x59, 0x4e, 0x4d, 0x1e, 0x0a, 0x57, 0x4a, 0xae, 0xb0, 0x9d, 0x59, 0x5c, 0x36, 0x4b, 0xa5, 0xf0, 0xfe, 0x7f, + 0x71, 0xf0, 0x4c, 0x48, 0xbb, 0x6a, 0xd4, 0xa6, 0xfa, 0x04, 0x3e, 0x03, 0x57, 0x52, 0x39, 0xd9, 0xc4, 0x1f, 0x06, + 0xb8, 0xd3, 0x1f, 0x44, 0x77, 0xcb, 0x86, 0x4b, 0x6e, 0x43, 0x1e, 0x0a, 0x0d, 0xc9, 0xd8, 0x07, 0xc3, 0xd5, 0xe7, + 0x51, 0xf6, 0xf0, 0xf8, 0x3b, 0x46, 0x6b, 0x54, 0xbd, 0xb8, 0x6e, 0x16, 0x3f, 0x70, 0x61, 0xdd, 0xa9, 0xab, 0x5f, + 0x51, 0xde, 0xfc, 0x69, 0xd9, 0x87, 0x55, 0x7e, 0x42, 0x16, 0xd8, 0xd7, 0xf2, 0xe6, 0x04, 0xac, 0xc5, 0x1c, 0x54, + 0x23, 0xf9, 0x45, 0x29, 0x0d, 0xec, 0x80, 0x69, 0xca, 0x35, 0x5a, 0x66, 0xea, 0x4f, 0x3d, 0x38, 0x19, 0x5f, 0x37, + 0x1c, 0x4a, 0x67, 0x77, 0xff, 0xd2, 0x71, 0x0f, 0xa1, 0x29, 0xd2, 0x84, 0xbf, 0x3e, 0x9e, 0xd8, 0x38, 0xb1, 0x8a, + 0x5a, 0x60, 0x5c, 0x39, 0xee, 0xef, 0xad, 0xae, 0x73, 0xf5, 0xd2, 0x87, 0x18, 0x48, 0x92, 0x69, 0xbc, 0x50, 0x09, + 0x52, 0x11, 0xaf, 0x50, 0x70, 0xda, 0xde, 0xef, 0xae, 0xec, 0x51, 0xde, 0xfe, 0xa7, 0x78, 0x33, 0xa3, 0xf9, 0x57, + 0x78, 0x79, 0x2f, 0xd7, 0xef, 0xba, 0xf3, 0xf5, 0x95, 0xfd, 0xb0, 0xdb, 0xff, 0x34, 0x03, 0xc9, 0x55, 0x2a, 0xfd, + 0xe9, 0x52, 0xcf, 0x67, 0x9f, 0x00, 0xf0, 0xeb, 0x95, 0xa1, 0x86, 0x64, 0x58, 0x13, 0xcd, 0x44, 0xc2, 0x5a, 0x25, + 0x62, 0x7c, 0xb3, 0x84, 0xaf, 0x69, 0x77, 0x45, 0x78, 0xa4, 0x8c, 0x8d, 0xb3, 0xb6, 0x43, 0xd6, 0xc7, 0x4c, 0x90, + 0xdd, 0x16, 0xcc, 0xf5, 0xd3, 0xac, 0x9f, 0x86, 0x55, 0xb5, 0x08, 0xd5, 0x27, 0x94, 0xe9, 0xf3, 0x68, 0x00, 0xdd, + 0xa0, 0x70, 0x64, 0x68, 0x24, 0x32, 0x36, 0xfa, 0x61, 0x37, 0x11, 0x1d, 0x47, 0x64, 0x44, 0x64, 0x25, 0x45, 0x21, + 0x9a, 0x4d, 0xfc, 0xf8, 0xfc, 0x27, 0xa5, 0x5a, 0x50, 0x24, 0xe1, 0x1a, 0x80, 0xa4, 0xf6, 0xc3, 0x35, 0x04, 0xa6, + 0xfa, 0xc3, 0xb6, 0x35, 0xe8, 0xd8, 0xc8, 0xca, 0x86, 0xa4, 0x8e, 0xa4, 0xbf, 0x0d, 0x22, 0x49, 0xa6, 0x72, 0x93, + 0x8c, 0x8d, 0x90, 0x03, 0xcc, 0x3b, 0x5a, 0x9b, 0x6f, 0x46, 0xd4, 0x91, 0x74, 0x4c, 0x58, 0x89, 0x3d, 0xa2, 0x30, + 0xc1, 0x11, 0xc2, 0xfd, 0x9a, 0xec, 0x42, 0xff, 0x29, 0xc0, 0xf6, 0x53, 0x43, 0xa2, 0x66, 0xfb, 0x48, 0xc3, 0xa7, + 0xd0, 0xf3, 0x10, 0xe2, 0x6d, 0x98, 0x97, 0x10, 0x16, 0xb9, 0xc1, 0x0e, 0xf4, 0x5e, 0x90, 0xa9, 0x08, 0x6f, 0x24, + 0x6c, 0x62, 0x2d, 0x10, 0x80, 0x67, 0xeb, 0x3e, 0x15, 0x1c, 0x00, 0xa4, 0xcd, 0xca, 0xb1, 0x7c, 0x7f, 0x3c, 0x90, + 0x43, 0x5b, 0x9a, 0x1d, 0xa9, 0x3b, 0xc4, 0xa5, 0x34, 0x9f, 0xe8, 0xd8, 0x1a, 0xc9, 0x41, 0xc2, 0x68, 0xc5, 0x33, + 0xb9, 0x28, 0x9b, 0x76, 0x7e, 0x18, 0xde, 0x57, 0xa5, 0x26, 0x9e, 0xb4, 0xbd, 0xca, 0x1c, 0xc5, 0xe4, 0xf1, 0xd0, + 0xd7, 0xba, 0x0d, 0x97, 0x1e, 0xf4, 0x34, 0x1c, 0x4f, 0x52, 0xfe, 0x7a, 0x8e, 0x62, 0x6d, 0xfc, 0x30, 0xd2, 0x50, + 0x01, 0x19, 0x1e, 0xdc, 0x72, 0xd9, 0x6c, 0xf5, 0xc3, 0xee, 0xf8, 0x61, 0x13, 0x3e, 0xda, 0x8b, 0x6b, 0x33, 0xa7, + 0x97, 0x41, 0x1a, 0xcc, 0x87, 0x92, 0x82, 0x2b, 0xab, 0xc6, 0xbe, 0x37, 0x95, 0xd4, 0xfe, 0xdd, 0xa6, 0x60, 0xdb, + 0xda, 0x46, 0x2f, 0xae, 0x3f, 0x2a, 0x91, 0xf9, 0xfa, 0xdd, 0x34, 0xee, 0x76, 0x76, 0xdb, 0x82, 0x68, 0x84, 0x95, + 0x3b, 0x26, 0x96, 0xd3, 0x6f, 0x9a, 0x74, 0x73, 0x43, 0xe8, 0x23, 0x8a, 0x7f, 0x9b, 0x94, 0xe3, 0xb3, 0xc3, 0xf3, + 0x6b, 0xe8, 0x41, 0x13, 0xa6, 0xaa, 0xc7, 0xe9, 0x0e, 0x16, 0x89, 0xe2, 0x09, 0xaf, 0x88, 0x44, 0xf6, 0xea, 0x87, + 0x43, 0xc6, 0x12, 0x85, 0x21, 0xd2, 0x98, 0xc7, 0x0f, 0xbb, 0x74, 0xd8, 0x79, 0x18, 0xc6, 0x09, 0x70, 0xd9, 0x97, + 0x94, 0xbc, 0xb1, 0x86, 0xdf, 0x7e, 0x0e, 0x4c, 0xfb, 0x7e, 0x7b, 0x9f, 0xe9, 0xad, 0x78, 0x69, 0x6c, 0xbc, 0xde, + 0xa1, 0x10, 0x21, 0xa2, 0x9c, 0x36, 0x3e, 0xae, 0x7f, 0xa4, 0xd8, 0xb0, 0x65, 0x59, 0xae, 0x18, 0xdd, 0xe2, 0xd7, + 0xc0, 0x26, 0x34, 0x6c, 0x87, 0x90, 0x3e, 0xb2, 0x6b, 0x5e, 0x09, 0x68, 0x55, 0x0f, 0x4b, 0xbd, 0xa2, 0x0b, 0x68, + 0x39, 0xc7, 0x48, 0xd9, 0x40, 0x19, 0x28, 0xf8, 0x17, 0x67, 0xd0, 0x55, 0x36, 0xb3, 0xcc, 0xd6, 0xc8, 0x82, 0x7f, + 0x10, 0x4e, 0xe7, 0x4f, 0xa2, 0xd5, 0x84, 0x2c, 0xe1, 0x52, 0xf1, 0x16, 0x14, 0xd8, 0x4a, 0x31, 0x05, 0x06, 0xb4, + 0x7d, 0x22, 0x8d, 0x5f, 0x8c, 0x69, 0x05, 0xd4, 0xd1, 0xe3, 0x32, 0xca, 0xe0, 0x33, 0xad, 0x2b, 0x16, 0x97, 0x41, + 0x7b, 0xa0, 0x31, 0xfc, 0x6b, 0x6b, 0xec, 0x5b, 0xdb, 0x65, 0xfe, 0x3d, 0xe0, 0x35, 0xb5, 0xa7, 0x14, 0x62, 0x05, + 0xd1, 0x01, 0xb2, 0x76, 0x0d, 0x9d, 0xbd, 0x67, 0xcf, 0xc7, 0xd6, 0x72, 0x05, 0x53, 0xe8, 0xa0, 0x62, 0x78, 0x83, + 0xcd, 0xfd, 0x23, 0x85, 0x33, 0x0d, 0xe9, 0x3c, 0xb3, 0x5a, 0x91, 0xcb, 0x14, 0xd4, 0x88, 0x7f, 0x9d, 0x3b, 0x58, + 0x24, 0x51, 0x3d, 0xe2, 0x14, 0x91, 0xa6, 0x93, 0x05, 0x26, 0xa1, 0x8e, 0xd4, 0xd0, 0x76, 0xbb, 0x82, 0x27, 0xca, + 0x4f, 0x38, 0xfd, 0x9b, 0xa5, 0x6b, 0xd4, 0x16, 0x7c, 0x0e, 0xcd, 0xe2, 0x0f, 0x51, 0x4b, 0x7f, 0xfd, 0xf1, 0xc1, + 0x00, 0x01, 0xc4, 0xdb, 0xb3, 0x41, 0x08, 0x13, 0x4f, 0xc7, 0xd6, 0x99, 0x7c, 0xc8, 0x40, 0x30, 0x9b, 0x6a, 0x84, + 0x6c, 0x84, 0xb9, 0xb5, 0x77, 0xd3, 0x3a, 0xf9, 0x03, 0xa7, 0xc0, 0x14, 0xe2, 0x84, 0xed, 0xa0, 0xc0, 0xfc, 0x61, + 0x1c, 0x45, 0x08, 0xf5, 0xe5, 0xd7, 0x22, 0x19, 0xc9, 0xf9, 0x36, 0x98, 0x8b, 0x18, 0x25, 0xd8, 0x5a, 0xf1, 0x5a, + 0x3f, 0x20, 0xaa, 0xda, 0xef, 0x4d, 0x86, 0xed, 0x8c, 0x3e, 0xbe, 0x1b, 0x4f, 0x8a, 0x6f, 0x1c, 0xd7, 0x73, 0x18, + 0xca, 0xfb, 0x67, 0x48, 0xa2, 0x65, 0xde, 0xff, 0xc4, 0xb9, 0xdb, 0xd5, 0xb1, 0x09, 0x2f, 0x6a, 0x03, 0xc3, 0x84, + 0xb0, 0xc1, 0xed, 0x79, 0x9b, 0xec, 0x34, 0x58, 0x9c, 0x4e, 0x17, 0xbc, 0xe1, 0x1a, 0x85, 0x7d, 0xb6, 0x33, 0x29, + 0xee, 0x5d, 0xfb, 0xd7, 0x71, 0x23, 0x1a, 0xf7, 0x19, 0x93, 0x90, 0x7f, 0x67, 0x39, 0x53, 0x9a, 0x3e, 0xad, 0x0a, + 0x4f, 0xfa, 0xce, 0xd9, 0xcd, 0x7c, 0x04, 0x17, 0xed, 0x6f, 0x80, 0xe5, 0x4e, 0xb6, 0x39, 0x27, 0x79, 0x46, 0xf3, + 0x0b, 0xbc, 0xd4, 0xd2, 0xcf, 0xed, 0xb4, 0xea, 0x40, 0x74, 0xb7, 0x00, 0x15, 0x0c, 0xd4, 0xe1, 0x81, 0xb7, 0x63, + 0x3b, 0xc4, 0xa7, 0x1a, 0x8c, 0x41, 0x60, 0xfa, 0x0f, 0xdf, 0xcd, 0xf5, 0x2e, 0x14, 0xa2, 0xcf, 0xa2, 0xe5, 0x2e, + 0xa3, 0x2f, 0xec, 0x84, 0x28, 0x23, 0x17, 0x31, 0xfa, 0x39, 0xba, 0x4b, 0xc8, 0x0d, 0x32, 0x17, 0x11, 0x54, 0xdc, + 0x93, 0xef, 0x88, 0x1f, 0xb0, 0x0b, 0x20, 0x1a, 0xc4, 0x39, 0x87, 0x8a, 0xfe, 0x26, 0x94, 0xa2, 0xd9, 0x61, 0x3c, + 0xff, 0xbb, 0x2c, 0x42, 0xe4, 0xcf, 0xa3, 0x78, 0x57, 0xc8, 0xf7, 0xee, 0xb1, 0xc5, 0x48, 0xf0, 0xc5, 0xb7, 0x41, + 0x2f, 0xe4, 0xc9, 0x9e, 0xc8, 0x20, 0xba, 0xf1, 0x0b, 0xa9, 0x56, 0x89, 0x5c, 0x5d, 0x64, 0x2c, 0x98, 0x5f, 0x21, + 0xa7, 0x3f, 0x6d, 0xef, 0xfc, 0xf2, 0x0f, 0x0c, 0xea, 0x98, 0xc5, 0x7f, 0x36, 0xee, 0x9b, 0x50, 0xa4, 0xef, 0xc5, + 0xe3, 0x03, 0xe2, 0x07, 0xd1, 0xf5, 0x2e, 0x41, 0xb1, 0x95, 0xcc, 0x09, 0x41, 0xa2, 0x70, 0x7c, 0x51, 0x7b, 0xff, + 0x9d, 0xde, 0x85, 0x9b, 0xa8, 0x3d, 0x08, 0xe8, 0x27, 0xff, 0xf0, 0xcb, 0x1f, 0x10, 0x1f, 0x88, 0x2c, 0xb8, 0xbe, + 0x9b, 0x67, 0xab, 0x3f, 0x71, 0x9e, 0xbb, 0x18, 0x44, 0x9f, 0x80, 0x0a, 0x12, 0x56, 0xa9, 0x9e, 0xc1, 0x03, 0xf6, + 0x3f, 0x2c, 0x5c, 0x8d, 0x78, 0xfd, 0xf8, 0xf4, 0x26, 0x5e, 0x43, 0xe7, 0x0a, 0xab, 0x0e, 0x5f, 0x80, 0xc8, 0x21, + 0xb9, 0x54, 0x5d, 0xec, 0x38, 0xd3, 0xff, 0x55, 0x02, 0x36, 0xde, 0x11, 0xc1, 0xe9, 0xfc, 0xc3, 0xcb, 0x17, 0x1b, + 0x7b, 0xb2, 0x9b, 0xdb, 0x61, 0xfc, 0x93, 0x06, 0x96, 0x70, 0x5f, 0xd3, 0xf4, 0x47, 0xc6, 0xe4, 0xd3, 0xfc, 0xf6, + 0x49, 0x3f, 0x1f, 0x4b, 0xbe, 0xfd, 0xe8, 0x17, 0xfe, 0xa8, 0x5f, 0xf3, 0xec, 0x57, 0xb2, 0x26, 0x3b, 0xec, 0x35, + 0xc0, 0xa7, 0xbd, 0xf1, 0xa5, 0xe5, 0xfa, 0x5a, 0xc5, 0xf8, 0x8b, 0x51, 0xe8, 0xd3, 0xef, 0x2e, 0x1f, 0xbc, 0x92, + 0x77, 0x0b, 0x25, 0xcd, 0x54, 0x50, 0xe7, 0xd6, 0xa6, 0xb6, 0x15, 0xda, 0x4d, 0x30, 0x09, 0xf6, 0x06, 0x05, 0x91, + 0x46, 0x15, 0x9e, 0xc8, 0xa2, 0x6d, 0x19, 0x94, 0x0a, 0x86, 0xd2, 0x1c, 0x47, 0x5d, 0x0f, 0x89, 0x03, 0x46, 0xf4, + 0x8c, 0x68, 0x55, 0xab, 0x38, 0x1d, 0x1d, 0x2c, 0x04, 0x9c, 0x42, 0x84, 0x11, 0xc8, 0xf7, 0xea, 0x84, 0x0a, 0x74, + 0x21, 0x69, 0x08, 0xf1, 0x3b, 0xe9, 0x58, 0x8a, 0xe2, 0xda, 0x0a, 0x5f, 0xef, 0x3f, 0xc9, 0xc6, 0xca, 0x47, 0x01, + 0x16, 0xe5, 0x1d, 0x4a, 0xa9, 0x0e, 0x29, 0x98, 0x5c, 0xa4, 0x2e, 0x47, 0xcc, 0x9c, 0x8f, 0x64, 0xb3, 0xe0, 0xb0, + 0x9a, 0x5b, 0xd1, 0x6e, 0x9c, 0xe5, 0xe0, 0xd0, 0x2a, 0xe3, 0x30, 0x86, 0x24, 0x37, 0xf9, 0x35, 0x0a, 0x28, 0x27, + 0xeb, 0x53, 0xdc, 0x02, 0xdf, 0x72, 0xfb, 0x8c, 0x5c, 0xa5, 0xd0, 0xd9, 0x23, 0xdf, 0x33, 0xfc, 0xc1, 0xe3, 0xfd, + 0xee, 0x73, 0x78, 0x34, 0x65, 0xd5, 0x84, 0xb5, 0x7f, 0xb4, 0x21, 0x21, 0x94, 0x02, 0x55, 0x04, 0x08, 0x53, 0x65, + 0x0d, 0xac, 0xeb, 0x90, 0x9a, 0x43, 0x4d, 0xd7, 0x9f, 0x58, 0xe4, 0x88, 0x77, 0x98, 0x38, 0xbf, 0x61, 0x60, 0x89, + 0xa5, 0x0c, 0xf6, 0x06, 0xbc, 0xd6, 0xc2, 0x3e, 0x8b, 0x02, 0x75, 0x26, 0xe7, 0x8a, 0x23, 0x08, 0xba, 0xa5, 0x66, + 0x26, 0x2a, 0x9d, 0x65, 0x8f, 0x34, 0x3f, 0xc5, 0xbc, 0x62, 0xbf, 0x2a, 0x93, 0x86, 0x74, 0xd0, 0x99, 0xdc, 0x9a, + 0x9a, 0x02, 0x57, 0x21, 0x55, 0xb5, 0x4e, 0xec, 0x38, 0xf1, 0xc2, 0xcf, 0xd3, 0x11, 0xc7, 0x36, 0x3e, 0x0f, 0x45, + 0x7d, 0x92, 0xf7, 0x69, 0xe9, 0xfa, 0xd0, 0x25, 0xd7, 0x06, 0x69, 0x7a, 0x3b, 0xa2, 0x2b, 0x3f, 0xbc, 0xa6, 0x31, + 0x4d, 0x5f, 0x39, 0xba, 0x4f, 0x73, 0xf3, 0x49, 0xdb, 0x58, 0xb2, 0xf9, 0xd1, 0x5f, 0xf2, 0xca, 0xac, 0x43, 0x28, + 0x72, 0x99, 0x82, 0xfb, 0x7d, 0x3e, 0xaa, 0xc9, 0xf6, 0x3b, 0x78, 0x14, 0x88, 0x2f, 0x1b, 0x81, 0x30, 0xfd, 0xe2, + 0xc1, 0x70, 0x23, 0xaf, 0x06, 0xe6, 0x6a, 0x82, 0xeb, 0x75, 0x7d, 0x02, 0xe9, 0xd9, 0x1a, 0x03, 0x1b, 0x21, 0x53, + 0x57, 0xc1, 0x7b, 0xf6, 0x2e, 0x68, 0x6f, 0xe0, 0x37, 0x7b, 0xc0, 0x08, 0xb3, 0x7a, 0xcb, 0x08, 0x28, 0x0c, 0x29, + 0xd4, 0x4d, 0x93, 0x14, 0x0d, 0xa1, 0x02, 0x06, 0xfc, 0xfc, 0x45, 0xe8, 0xc2, 0xd3, 0x12, 0xe8, 0x7f, 0x70, 0x3e, + 0xd4, 0x8a, 0x32, 0xce, 0x2f, 0x5a, 0x6c, 0x69, 0xee, 0x34, 0x4f, 0x4c, 0x7e, 0xec, 0x23, 0x3c, 0x4f, 0xe5, 0x38, + 0x9c, 0xdd, 0xd7, 0xe9, 0x4a, 0x7b, 0xa9, 0x39, 0x9e, 0x34, 0xff, 0x6a, 0xe3, 0xcb, 0xbc, 0x13, 0x91, 0x38, 0xc1, + 0x5d, 0x80, 0x7f, 0x3d, 0x8f, 0x24, 0x61, 0x38, 0x5d, 0x34, 0xdf, 0x14, 0xef, 0x56, 0x13, 0x6f, 0x71, 0xd5, 0x22, + 0x8d, 0xdb, 0x43, 0xdc, 0xf7, 0x7e, 0x0d, 0x9e, 0x3b, 0xdb, 0xf5, 0x7c, 0xd8, 0x0a, 0x1e, 0x90, 0xc9, 0xe5, 0x14, + 0x08, 0x5e, 0xc4, 0x62, 0x32, 0x0f, 0xa9, 0x57, 0xb6, 0x0e, 0xcb, 0xd2, 0x79, 0xa5, 0xb6, 0x89, 0x7a, 0xc5, 0xb8, + 0x96, 0x6f, 0x76, 0xeb, 0x45, 0x5c, 0x12, 0x0b, 0x2d, 0xae, 0x95, 0x56, 0xaa, 0x59, 0x9f, 0x18, 0x5b, 0x8e, 0xda, + 0x76, 0xff, 0x7e, 0x83, 0xc8, 0x6a, 0x9f, 0x5f, 0x3e, 0x2e, 0x88, 0x81, 0x0f, 0x99, 0xa3, 0xf4, 0xf8, 0x02, 0xad, + 0xb2, 0x6e, 0xad, 0xbd, 0x3a, 0xed, 0x4e, 0xa6, 0xdf, 0x96, 0xf5, 0x61, 0x17, 0x8c, 0xd7, 0x05, 0xb1, 0xed, 0x51, + 0xe8, 0x27, 0xd6, 0xe7, 0x7b, 0xaa, 0x10, 0xfe, 0x6d, 0x7a, 0xff, 0xb3, 0xb7, 0xcd, 0x73, 0xa9, 0x22, 0x8e, 0x90, + 0x79, 0xa2, 0x36, 0x8c, 0x95, 0x92, 0xbd, 0xa0, 0x43, 0x8a, 0xd5, 0x8c, 0x42, 0x03, 0x81, 0x54, 0x37, 0xbd, 0x93, + 0x57, 0x03, 0x80, 0x29, 0x66, 0xb0, 0xe1, 0x70, 0x17, 0xf0, 0x0e, 0xb5, 0x82, 0x70, 0x9c, 0x33, 0xaa, 0x96, 0x2e, + 0xb5, 0xde, 0x8e, 0x9f, 0xc2, 0x51, 0x1d, 0x70, 0xd1, 0xfe, 0x78, 0x2c, 0xd8, 0x8a, 0x44, 0x0d, 0x71, 0x2e, 0x4d, + 0x5a, 0xa8, 0x0f, 0x51, 0x8f, 0x7d, 0x37, 0x68, 0x33, 0xbc, 0x05, 0x5f, 0x11, 0xb8, 0xc2, 0x2f, 0x71, 0x70, 0xcb, + 0x74, 0xb8, 0x87, 0xad, 0xeb, 0x9a, 0xe8, 0x8b, 0xfa, 0x33, 0x66, 0x59, 0x08, 0x72, 0x7a, 0xc2, 0x3f, 0xa8, 0x85, + 0x4a, 0x41, 0xf0, 0x72, 0x2e, 0xe0, 0xfe, 0x1c, 0x46, 0x4f, 0x48, 0xf9, 0xa1, 0x88, 0x04, 0x69, 0x1d, 0x99, 0x1a, + 0x1c, 0xf7, 0x58, 0x97, 0x18, 0x66, 0x2f, 0x82, 0x83, 0xc5, 0xac, 0x11, 0x59, 0xd5, 0x23, 0xf8, 0xcd, 0x93, 0xa6, + 0x75, 0x88, 0x25, 0x85, 0x1a, 0xd6, 0x54, 0xfa, 0x5b, 0x10, 0xa9, 0x4d, 0x97, 0x7f, 0x02, 0x74, 0x6d, 0x4f, 0x94, + 0x9e, 0xf6, 0x92, 0x5a, 0x54, 0x1d, 0xda, 0x46, 0xc2, 0xdc, 0xa5, 0xc0, 0xd0, 0x38, 0xf0, 0x00, 0xb1, 0xf6, 0xae, + 0xc8, 0xe4, 0x3d, 0x74, 0x99, 0x3c, 0x44, 0xd5, 0x4e, 0x8d, 0xed, 0x72, 0xca, 0x0f, 0x52, 0x6d, 0x61, 0xe4, 0x14, + 0x75, 0x4a, 0x95, 0x17, 0x46, 0x08, 0xea, 0xd6, 0x57, 0xc7, 0xba, 0x08, 0xcd, 0xc3, 0x6a, 0xed, 0x44, 0x2f, 0xb1, + 0xcc, 0xfe, 0xd1, 0x20, 0xce, 0xcc, 0xc2, 0x40, 0x73, 0xfe, 0x53, 0xb7, 0x18, 0xa2, 0xfd, 0x5f, 0xf2, 0xb0, 0x5e, + 0x77, 0xfe, 0x74, 0x5c, 0x78, 0x69, 0xb7, 0x4b, 0x77, 0x1b, 0xbd, 0x37, 0xe0, 0x1a, 0x8c, 0xf9, 0x93, 0x7c, 0xa6, + 0x8d, 0x08, 0xa8, 0xf8, 0x36, 0x7c, 0x7c, 0x3f, 0xda, 0x47, 0xe4, 0x21, 0x72, 0x98, 0x3f, 0x46, 0xbf, 0x13, 0x6c, + 0x51, 0x6b, 0x44, 0xb2, 0x8a, 0xb0, 0x20, 0x35, 0x77, 0xf8, 0xd6, 0x23, 0xdf, 0x5c, 0x57, 0x3b, 0xf1, 0x79, 0x0d, + 0x02, 0xa8, 0x58, 0x4d, 0x1b, 0x07, 0xfa, 0xdc, 0xf6, 0x19, 0xcf, 0x41, 0x13, 0x1d, 0x85, 0x43, 0xfc, 0xf3, 0x9c, + 0x73, 0xb4, 0xa3, 0x9d, 0x1c, 0x87, 0xc7, 0xd8, 0x2b, 0xc5, 0xb9, 0xff, 0x8c, 0x42, 0x13, 0x96, 0x9f, 0xe5, 0x3b, + 0xd4, 0x07, 0xfc, 0x3c, 0xe5, 0x7f, 0x5c, 0xf5, 0x40, 0x88, 0x3d, 0x21, 0x00, 0xce, 0x9f, 0xfe, 0x23, 0x14, 0xf2, + 0xa7, 0x12, 0x2e, 0xfc, 0x07, 0x86, 0x84, 0x17, 0xc1, 0x3f, 0xc1, 0xef, 0x2c, 0x31, 0x3a, 0x4c, 0x51, 0xa1, 0xfc, + 0xa3, 0x03, 0x21, 0x5f, 0x73, 0x76, 0x6d, 0x0e, 0x9f, 0xcf, 0x99, 0xe5, 0x0b, 0xae, 0x09, 0xf5, 0x79, 0x2b, 0x30, + 0xff, 0xa6, 0xc9, 0x3e, 0x09, 0x48, 0x2e, 0xfc, 0x56, 0xdc, 0xad, 0x56, 0x93, 0x3c, 0x8a, 0x14, 0xfd, 0x66, 0x1a, + 0x2b, 0x6f, 0xbc, 0x45, 0x89, 0xb6, 0x43, 0x2f, 0x4d, 0x9f, 0xcb, 0x17, 0x84, 0xd9, 0x56, 0xc7, 0x89, 0xd9, 0x1f, + 0xdc, 0x5d, 0xa7, 0x4b, 0x2c, 0x41, 0x64, 0x18, 0x77, 0xc7, 0x60, 0x1d, 0xbe, 0x5a, 0x19, 0x2a, 0x63, 0x11, 0x4a, + 0x15, 0x2d, 0x3d, 0xfc, 0x42, 0x37, 0x71, 0x51, 0xba, 0x99, 0x72, 0xcc, 0xf4, 0x77, 0x68, 0xfd, 0x6b, 0x35, 0x3a, + 0xbb, 0x24, 0x7c, 0xf0, 0x78, 0x2f, 0xe8, 0x6f, 0x3a, 0x64, 0x17, 0xe1, 0x2f, 0x1f, 0x5f, 0xaa, 0x25, 0x0b, 0xa3, + 0x9b, 0xce, 0xa7, 0x34, 0x7b, 0xbb, 0xaf, 0x32, 0x0a, 0x4d, 0x0d, 0x85, 0x91, 0x38, 0x2b, 0xc7, 0x65, 0xef, 0x4c, + 0xd6, 0xf5, 0x73, 0xcf, 0xaa, 0x94, 0x5d, 0x48, 0xb0, 0xa8, 0x97, 0x7b, 0xf7, 0x0d, 0x5a, 0x48, 0xa1, 0x06, 0xd2, + 0x16, 0x03, 0x1d, 0xba, 0x67, 0x38, 0xd1, 0x25, 0x94, 0x40, 0xa4, 0x0f, 0x57, 0x59, 0xd4, 0xf4, 0x45, 0x4c, 0xa0, + 0x4f, 0x3d, 0x5b, 0xec, 0x6c, 0xd7, 0x28, 0x3b, 0x8c, 0x02, 0x72, 0xf7, 0x86, 0x67, 0x46, 0x1f, 0xef, 0xdf, 0xc8, + 0x6a, 0xf9, 0x7f, 0xa3, 0x46, 0xdb, 0x3b, 0x47, 0xa0, 0xe1, 0x99, 0xb7, 0x4b, 0x22, 0x12, 0x24, 0x2c, 0x7e, 0x3e, + 0x79, 0xf6, 0x7d, 0x17, 0x4a, 0xa4, 0xe0, 0xd0, 0x57, 0x63, 0xba, 0x7c, 0xa9, 0x26, 0xca, 0x47, 0x62, 0xc0, 0x4f, + 0x3a, 0x0f, 0x12, 0x5d, 0x4d, 0x73, 0xb0, 0x43, 0x39, 0x70, 0x7b, 0x73, 0xc6, 0xf9, 0x63, 0xbe, 0xc1, 0xca, 0xc1, + 0x93, 0xed, 0x9f, 0x7a, 0xb9, 0x8d, 0x51, 0xc5, 0x4f, 0x44, 0x63, 0x19, 0xf0, 0xf0, 0xd9, 0xe9, 0x08, 0xed, 0x8c, + 0x64, 0x01, 0xca, 0x7b, 0xbb, 0x3f, 0x86, 0x4b, 0xf8, 0x99, 0x1a, 0xef, 0x59, 0xdb, 0xa1, 0xa5, 0xdb, 0xf8, 0xa6, + 0xe4, 0x71, 0x78, 0x60, 0x2d, 0xc5, 0x6a, 0x6c, 0x0d, 0x10, 0x97, 0xb8, 0xa3, 0x6c, 0xad, 0xe2, 0xe2, 0xfe, 0x5f, + 0x1e, 0x9e, 0x39, 0x07, 0x81, 0x2a, 0xe1, 0x60, 0x22, 0x35, 0x23, 0xb6, 0x91, 0x63, 0xc7, 0x6b, 0x46, 0x1c, 0x5c, + 0x01, 0x69, 0x23, 0x26, 0x9a, 0x53, 0xb9, 0x0f, 0xc6, 0xf3, 0xe8, 0x8d, 0xaa, 0x8f, 0x73, 0xe6, 0x81, 0x6d, 0x70, + 0x27, 0x55, 0x1b, 0x16, 0x26, 0xbe, 0xd9, 0xad, 0xa5, 0xe9, 0xcb, 0x8e, 0xac, 0x17, 0x6c, 0xcf, 0x4a, 0x10, 0xfa, + 0x54, 0xfa, 0x37, 0x1a, 0xe2, 0xb9, 0xae, 0x5f, 0x47, 0x17, 0xed, 0x87, 0xb9, 0xc3, 0xfe, 0xee, 0xf8, 0xb4, 0x61, + 0x62, 0x1d, 0x7d, 0x1e, 0x3b, 0x2b, 0xcc, 0xb3, 0x6b, 0x4d, 0x3f, 0xb5, 0xf1, 0xd0, 0xc7, 0xbe, 0xb4, 0x56, 0x66, + 0xb0, 0x2a, 0x28, 0xbb, 0x53, 0x53, 0x03, 0x61, 0x0d, 0xea, 0x3a, 0x99, 0x64, 0x33, 0x65, 0xbf, 0x3c, 0x03, 0xb3, + 0xdf, 0x45, 0xc9, 0x15, 0xfa, 0xeb, 0x7d, 0x69, 0x52, 0xe7, 0x3b, 0xda, 0x22, 0x47, 0xb4, 0xc5, 0xa0, 0x16, 0x11, + 0xef, 0xd4, 0xd7, 0x29, 0xc9, 0x47, 0x2f, 0x5a, 0x82, 0x30, 0xb5, 0xa4, 0xdd, 0x15, 0x28, 0x61, 0x99, 0x91, 0xcf, + 0xf6, 0x13, 0xe3, 0xfd, 0xd3, 0xf8, 0xa5, 0x63, 0xd2, 0x76, 0xb5, 0x6b, 0x07, 0x23, 0xd7, 0xd0, 0x54, 0x41, 0xe3, + 0x16, 0xdf, 0x61, 0xa0, 0x9f, 0xe2, 0x48, 0xdb, 0xaf, 0x35, 0x4f, 0x5f, 0xda, 0xd6, 0xf3, 0xea, 0xf6, 0x89, 0x5a, + 0xeb, 0xc0, 0xb1, 0x33, 0xb4, 0x27, 0x6f, 0x4c, 0x90, 0x0f, 0x7d, 0x3e, 0x3c, 0x0d, 0xa7, 0x26, 0x1f, 0x9d, 0xa5, + 0x90, 0xc8, 0x1e, 0x15, 0x5f, 0x60, 0x3e, 0x1f, 0x28, 0x15, 0x51, 0x1b, 0xef, 0xdd, 0xd6, 0x6e, 0xbe, 0x8f, 0x47, + 0xab, 0x76, 0x8d, 0xd1, 0x46, 0xc2, 0x02, 0x3c, 0x54, 0x89, 0xd2, 0x21, 0x0e, 0xfc, 0x67, 0x92, 0x76, 0x16, 0x75, + 0x8d, 0xb7, 0x65, 0xc3, 0xa4, 0xf9, 0x3c, 0x95, 0x7a, 0x19, 0x77, 0xd8, 0x56, 0x6e, 0xf6, 0xd1, 0x13, 0xd1, 0xbe, + 0x66, 0x6d, 0x3e, 0x41, 0x50, 0x76, 0xb5, 0xc3, 0xbd, 0xea, 0x88, 0x1d, 0x27, 0x6c, 0xbf, 0xd9, 0x7c, 0xe7, 0xa8, + 0x14, 0xa5, 0xc6, 0x09, 0x6b, 0xdd, 0xd4, 0x4e, 0x34, 0x87, 0x30, 0xfc, 0xd2, 0x37, 0xf1, 0x12, 0x52, 0x37, 0x1c, + 0xf3, 0xf6, 0xfe, 0x79, 0x58, 0xd7, 0xc2, 0x09, 0x45, 0xb2, 0x26, 0xf6, 0xde, 0x20, 0xdd, 0xc1, 0x2a, 0x0c, 0x9f, + 0x90, 0x5b, 0x67, 0x75, 0xf2, 0x26, 0x78, 0xa1, 0x21, 0xb2, 0x93, 0x21, 0xdf, 0x32, 0x0e, 0x2c, 0xdd, 0xc0, 0xfe, + 0x5a, 0x95, 0x64, 0x95, 0x27, 0x6a, 0xaf, 0x52, 0xa6, 0x69, 0x49, 0xc1, 0xf2, 0x29, 0xb3, 0x07, 0x47, 0x5e, 0xf3, + 0x65, 0x73, 0xeb, 0x9b, 0x77, 0x4f, 0x9d, 0xf5, 0xd0, 0x2e, 0x76, 0xbd, 0xb5, 0x29, 0x9c, 0xe0, 0x23, 0x49, 0xfc, + 0x50, 0xfb, 0xd9, 0x7e, 0xb0, 0x71, 0xff, 0xa4, 0xf6, 0x03, 0xce, 0xec, 0x53, 0x74, 0x98, 0x87, 0x49, 0x9f, 0x15, + 0x24, 0x1c, 0xd0, 0xba, 0x8f, 0x45, 0xa6, 0xc0, 0x4e, 0x03, 0x9c, 0x40, 0x8d, 0xd8, 0xe3, 0x22, 0x07, 0xf4, 0xa6, + 0x6a, 0x6a, 0x31, 0xdf, 0xd3, 0x91, 0x3b, 0x9c, 0x62, 0x06, 0xbf, 0x68, 0xd8, 0xd2, 0xbc, 0xfa, 0xb8, 0xad, 0x1b, + 0xf4, 0x1c, 0xa4, 0x48, 0xdc, 0x20, 0xa6, 0x49, 0xf7, 0x15, 0x7a, 0xea, 0xeb, 0x37, 0xb9, 0x1d, 0xf7, 0x1d, 0xa7, + 0x8d, 0x76, 0x1b, 0xee, 0x62, 0x95, 0x4d, 0x3b, 0xa4, 0xa3, 0x06, 0xea, 0x4b, 0xff, 0x64, 0x45, 0xa7, 0xa7, 0x29, + 0x42, 0x57, 0x62, 0xdb, 0x04, 0x60, 0x72, 0x50, 0xd8, 0x59, 0x20, 0x09, 0x36, 0x38, 0x71, 0x2c, 0x13, 0x8d, 0xec, + 0x85, 0xbe, 0xda, 0xed, 0x18, 0x18, 0xf8, 0xb9, 0x27, 0xd1, 0x6f, 0xef, 0x2c, 0x52, 0x34, 0x6b, 0x19, 0x7e, 0x65, + 0x22, 0x45, 0x1f}; // Backwards compatibility alias #define INDEX_GZ INDEX_BR diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1e6c4e8c62..3fe3979a8b 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -558,26 +558,19 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, J size_t device_len = device_name ? strlen(device_name) : 0; #endif - // Single stack buffer for both id formats - ArduinoJson copies the string before we overwrite + // Stack buffer for the id - ArduinoJson copies the string before it goes out of scope // Buffer sizes use constants from entity_base.h validated in core/config.py // Note: Device name uses ESPHOME_FRIENDLY_NAME_MAX_LEN (sub-device max 120), not ESPHOME_DEVICE_NAME_MAX_LEN // (hostname) - // Without USE_DEVICES: legacy id ({prefix}-{object_id}) is the largest format - // With USE_DEVICES: name_id ({prefix}/{device}/{name}) is the largest format - static constexpr size_t LEGACY_ID_SIZE = ESPHOME_DOMAIN_MAX_LEN + 1 + OBJECT_ID_MAX_LEN; #ifdef USE_DEVICES static constexpr size_t ID_BUF_SIZE = - std::max(ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1, - LEGACY_ID_SIZE); + ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1; #else - static constexpr size_t ID_BUF_SIZE = - std::max(ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1, LEGACY_ID_SIZE); + static constexpr size_t ID_BUF_SIZE = ESPHOME_DOMAIN_MAX_LEN + 1 + ESPHOME_FRIENDLY_NAME_MAX_LEN + 1; #endif char id_buf[ID_BUF_SIZE]; memcpy(id_buf, prefix, prefix_len); // NOLINT(bugprone-not-null-terminated-result) - // name_id: new format {prefix}/{device?}/{name} - frontend should prefer this - // Remove in 2026.8.0 when id switches to new format permanently char *p = id_buf + prefix_len; *p++ = '/'; #ifdef USE_DEVICES @@ -589,12 +582,6 @@ static void set_json_id(JsonObject &root, EntityBase *obj, const char *prefix, J #endif memcpy(p, name.c_str(), name_len); p[name_len] = '\0'; - root[ESPHOME_F("name_id")] = id_buf; - - // id: old format {prefix}-{object_id} for backward compatibility - // Will switch to new format in 2026.8.0 - reuses prefix already in id_buf - id_buf[prefix_len] = '-'; - obj->write_object_id_to(id_buf + prefix_len + 1, ID_BUF_SIZE - prefix_len - 1); root[ESPHOME_F("id")] = id_buf; if (start_config == DETAIL_ALL) { @@ -1128,6 +1115,7 @@ json::SerializationBuffer<> WebServer::cover_json_(cover::Cover *obj, JsonDetail if (obj->get_traits().get_supports_tilt()) root[ESPHOME_F("tilt")] = obj->tilt; if (start_config == DETAIL_ALL) { + root[ESPHOME_F("assumed_state")] = obj->get_traits().get_is_assumed_state(); this->add_sorting_info_(root, obj); } @@ -1907,7 +1895,7 @@ json::SerializationBuffer<> WebServer::alarm_control_panel_json_(alarm_control_p json::JsonBuilder builder; JsonObject root = builder.root(); - set_json_icon_state_value(root, obj, "alarm-control-panel", + set_json_icon_state_value(root, obj, "alarm_control_panel", json_state_str(alarm_control_panel_state_to_string(value)), value, start_config); if (start_config == DETAIL_ALL) { this->add_sorting_info_(root, obj); diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index bf5a8666dc..993fb6c035 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -381,16 +381,6 @@ void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code #ifdef USE_WEBSERVER_AUTH_DIGEST namespace { -// Hex-encode `len` bytes into `out`, which must hold at least 2 * len + 1 bytes. Null-terminated. -void bytes_to_hex(const uint8_t *data, size_t len, char *out) { - static const char HEX[] = "0123456789abcdef"; - for (size_t i = 0; i < len; i++) { - out[i * 2] = HEX[data[i] >> 4]; - out[i * 2 + 1] = HEX[data[i] & 0x0f]; - } - out[len * 2] = '\0'; -} - // Extract the value of a Digest auth parameter (e.g. "nonce") from the comma-separated // parameter list. Values may be quoted or bare. Returns an empty ref when the key is absent. // Only whole parameter names match, so "nc" does not match inside "cnonce". @@ -468,7 +458,7 @@ bool check_digest_auth(const char *username, const char *password, const std::st esp_rom_md5_update(&ctx, ":", 1); esp_rom_md5_update(&ctx, password, strlen(password)); esp_rom_md5_final(digest, &ctx); - bytes_to_hex(digest, sizeof(digest), ha1); + format_hex_to(ha1, digest, sizeof(digest)); // HA2 = MD5(method:uri) -- uses the uri the client echoed back. char ha2[33]; @@ -477,7 +467,7 @@ bool check_digest_auth(const char *username, const char *password, const std::st esp_rom_md5_update(&ctx, ":", 1); esp_rom_md5_update(&ctx, uri.c_str(), uri.size()); esp_rom_md5_final(digest, &ctx); - bytes_to_hex(digest, sizeof(digest), ha2); + format_hex_to(ha2, digest, sizeof(digest)); // expected = MD5(HA1:nonce:nc:cnonce:qop:HA2) char expected[33]; @@ -494,7 +484,7 @@ bool check_digest_auth(const char *username, const char *password, const std::st esp_rom_md5_update(&ctx, ":", 1); esp_rom_md5_update(&ctx, ha2, 32); esp_rom_md5_final(digest, &ctx); - bytes_to_hex(digest, sizeof(digest), expected); + format_hex_to(expected, digest, sizeof(digest)); // Constant-time comparison of the two 32-char hex digests. uint8_t result = 0; @@ -592,9 +582,9 @@ void AsyncWebServerRequest::requestAuthentication() const { char opaque[33]; char header[160]; esp_fill_random(random_bytes, sizeof(random_bytes)); - bytes_to_hex(random_bytes, sizeof(random_bytes), nonce); + format_hex_to(nonce, random_bytes, sizeof(random_bytes)); esp_fill_random(random_bytes, sizeof(random_bytes)); - bytes_to_hex(random_bytes, sizeof(random_bytes), opaque); + format_hex_to(opaque, random_bytes, sizeof(random_bytes)); snprintf(header, sizeof(header), R"(Digest realm="Login Required", qop="auth", nonce="%s", opaque="%s")", nonce, opaque); httpd_resp_set_hdr(*this, "WWW-Authenticate", header); diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 137304c807..4bb6629da1 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -15,6 +15,7 @@ from esphome.components.esp32 import ( ) from esphome.components.network import ( add_use_address, + get_network_priority, has_high_performance_networking, ip_address_literal, ) @@ -602,6 +603,10 @@ def wifi_network(config, ap, static_ip): @coroutine_with_priority(CoroPriority.COMMUNICATION) async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + + prio = get_network_priority("wifi") + if prio is not None: + cg.set_setup_priority(var, prio) add_use_address(var, config[CONF_USE_ADDRESS]) # Track if any network uses Enterprise authentication @@ -818,6 +823,7 @@ IP_STATE_LISTENERS_KEY = "wifi_ip_state_listeners" SCAN_RESULTS_LISTENERS_KEY = "wifi_scan_results_listeners" CONNECT_STATE_LISTENERS_KEY = "wifi_connect_state_listeners" POWER_SAVE_LISTENERS_KEY = "wifi_power_save_listeners" +SCAN_RESULTS_LOCK_KEY = "wifi_scan_results_lock" def request_wifi_scan_results(): @@ -830,6 +836,19 @@ def request_wifi_scan_results(): CORE.data[KEEP_SCAN_RESULTS_KEY] = True +def request_wifi_scan_results_lock() -> None: + """Request that scan results be guarded by a lock for cross-task readers. + + Components that read WiFi scan results from a task other than the main loop + (for example a web server handler) must call this function during their code + generation, and their C++ code must hold a wifi::ScanResultsLock while + iterating get_scan_result(). On multi-threaded platforms this compiles in a + lock that scan result writers hold; on single-threaded platforms it compiles + to nothing. + """ + CORE.data[SCAN_RESULTS_LOCK_KEY] = True + + def enable_runtime_power_save_control(): """Enable runtime WiFi power save control. @@ -891,6 +910,8 @@ async def final_step(): cg.add_define("USE_WIFI_RUNTIME_POWER_SAVE") if CORE.data.get(RUNTIME_ROAMING_SUPPRESSION_KEY, False): cg.add_define("USE_WIFI_RUNTIME_ROAMING_SUPPRESSION") + if CORE.data.get(SCAN_RESULTS_LOCK_KEY): + cg.add_define("USE_WIFI_SCAN_RESULTS_LOCK") # Generate listener defines - each listener type has its own #ifdef ip_state_count = CORE.data.get(IP_STATE_LISTENERS_KEY, 0) diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 44e3cb6af9..650b06cae1 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1483,23 +1483,26 @@ void WiFiComponent::check_scanning_finished() { } ESP_LOGD(TAG, "Found networks:"); - for (auto &res : this->scan_result_) { - for (auto &ap : this->sta_) { - if (res.matches(ap)) { - res.set_matches(true); - // Cache priority lookup - do single search instead of 2 separate searches - const bssid_t &bssid = res.get_bssid(); - if (!this->has_sta_priority(bssid)) { - this->set_sta_priority(bssid, ap.get_priority()); + { + ScanResultsLock lock(this); + for (auto &res : this->scan_result_) { + for (auto &ap : this->sta_) { + if (res.matches(ap)) { + res.set_matches(true); + // Cache priority lookup - do single search instead of 2 separate searches + const bssid_t &bssid = res.get_bssid(); + if (!this->has_sta_priority(bssid)) { + this->set_sta_priority(bssid, ap.get_priority()); + } + res.set_priority(this->get_sta_priority(bssid)); + break; } - res.set_priority(this->get_sta_priority(bssid)); - break; } } - } - // Sort scan results using insertion sort for better memory efficiency - insertion_sort_scan_results(this->scan_result_); + // Sort scan results using insertion sort for better memory efficiency + insertion_sort_scan_results(this->scan_result_); + } // Log matching networks (non-matching already logged at VERBOSE in scan callback) for (auto &res : this->scan_result_) { @@ -1885,11 +1888,13 @@ bool WiFiComponent::transition_to_phase_(WiFiRetryPhase new_phase) { // Phase-specific setup switch (new_phase) { #ifdef USE_WIFI_FAST_CONNECT - case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: + case WiFiRetryPhase::FAST_CONNECT_CYCLING_APS: { // Move to next configured AP - clear old scan data so new AP is tried with config only this->selected_sta_index_++; + ScanResultsLock lock(this); this->scan_result_.clear(); break; + } #endif case WiFiRetryPhase::EXPLICIT_HIDDEN: @@ -2404,6 +2409,7 @@ void WiFiComponent::clear_roaming_state_() { void WiFiComponent::release_scan_results_() { if (!this->keep_scan_results_) { + ScanResultsLock lock(this); #if defined(USE_RP2) || defined(USE_ESP32) // std::vector - use swap trick since shrink_to_fit is non-binding decltype(this->scan_result_)().swap(this->scan_result_); diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 23b7558564..43e44a135f 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -187,6 +187,13 @@ template using wifi_scan_vector_t = std::vector; template using wifi_scan_vector_t = FixedVector; #endif +// A consumer component (e.g. the captive portal) reads scan results from another +// task; guard them with a real lock only on platforms that actually run multiple +// threads. See ScanResultsLock below the WiFiComponent class. +#if defined(USE_WIFI_SCAN_RESULTS_LOCK) && !defined(ESPHOME_THREAD_SINGLE) +#define WIFI_SCAN_RESULTS_LOCK_ENABLED +#endif + /// 20-byte string: 18 chars inline + null, heap for longer. Always null-terminated. /// Used internally for WiFi SSID/password storage to reduce heap fragmentation. class CompactString { @@ -506,6 +513,9 @@ class WiFiComponent final : public Component { const char *get_use_address() const { return this->use_address_; } void set_use_address(const char *use_address) { this->use_address_ = use_address; } + /// Main-loop callers may read this directly. Callers on any other task must + /// hold a ScanResultsLock for the whole iteration and must call + /// wifi.request_wifi_scan_results_lock() from their code generation. const wifi_scan_vector_t &get_scan_result() const { return scan_result_; } network::IPAddress wifi_soft_ap_ip(); @@ -817,9 +827,11 @@ class WiFiComponent final : public Component { friend void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); #endif + friend class ScanResultsLock; + #ifdef USE_RP2 static int s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result); - void wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result); + void wifi_scan_result_(void *env, const cyw43_ev_scan_result_t *result); #endif #ifdef USE_LIBRETINY @@ -831,7 +843,11 @@ class WiFiComponent final : public Component { // Large/pointer-aligned members first FixedVector sta_; std::vector sta_priorities_; + // Guarded by ScanResultsLock (see below this class) wifi_scan_vector_t scan_result_; +#ifdef WIFI_SCAN_RESULTS_LOCK_ENABLED + Mutex scan_result_lock_; +#endif #ifdef USE_WIFI_AP WiFiAP ap_; #endif @@ -1003,5 +1019,25 @@ class WiFiComponent final : public Component { extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +/// Guards WiFiComponent::scan_result_. Invariant: every mutation and every read +/// from outside the main loop holds this lock, and holders only do bounded work +/// (never unbounded waits or network sends). On every platform where the lock is +/// enabled (ESP32, LibreTiny) scan-done events are drained from the event queue +/// on the main loop, so all writers are main-loop there and main-loop reads take +/// no lock. Single-threaded platforms write from driver context and the lock is +/// a no-op. Compiles to nothing unless a cross-task reader is in the build and +/// the platform is multi-threaded (WIFI_SCAN_RESULTS_LOCK_ENABLED). +class ScanResultsLock { + public: +#ifdef WIFI_SCAN_RESULTS_LOCK_ENABLED + ScanResultsLock(WiFiComponent *parent) : guard_(parent->scan_result_lock_) {} + + private: + LockGuard guard_; +#else + ScanResultsLock(WiFiComponent *) {} +#endif +}; + } // namespace esphome::wifi #endif diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index 84b864c0c5..e082b2c8c1 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -733,6 +733,8 @@ void WiFiComponent::s_wifi_scan_done_callback(void *arg, STATUS status) { } void WiFiComponent::wifi_scan_done_callback_(void *arg, STATUS status) { + // Compiles to nothing here; kept so every scan_result_ mutation holds the lock + ScanResultsLock lock(this); this->scan_result_.clear(); if (status != OK) { diff --git a/esphome/components/wifi/wifi_component_esp_idf.cpp b/esphome/components/wifi/wifi_component_esp_idf.cpp index 2ade015a25..d78cd21380 100644 --- a/esphome/components/wifi/wifi_component_esp_idf.cpp +++ b/esphome/components/wifi/wifi_component_esp_idf.cpp @@ -891,65 +891,65 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { const auto &it = data->data.sta_scan_done; ESP_LOGV(TAG, "Scan done: status=%" PRIu32 " number=%u scan_id=%u", it.status, it.number, it.scan_id); - scan_result_.clear(); - this->scan_done_ = true; - if (it.status != 0) { - // scan error - return; - } - - if (it.number == 0) { - // no results - return; - } - uint16_t number = it.number; bool needs_full = this->needs_full_scan_results_(); + { + // Mutate in place under the lock; blocking a portal request is fine and + // avoids scratch buffers + ScanResultsLock lock(this); + this->scan_result_.clear(); + this->scan_done_ = true; + if (it.status != 0) { + // scan error + return; + } - // Smart reserve: full capacity if needed, small reserve otherwise - if (needs_full) { - this->scan_result_.reserve(number); - } else { - this->scan_result_.reserve(WIFI_SCAN_RESULT_FILTERED_RESERVE); - } + if (number == 0) { + // no results + return; + } + + // Smart reserve: full capacity if needed, small reserve otherwise + this->scan_result_.reserve(needs_full ? number : WIFI_SCAN_RESULT_FILTERED_RESERVE); #ifdef USE_ESP32_HOSTED - // getting records one at a time fails on P4 with hosted esp32 WiFi coprocessor - // Presumably an upstream bug, work-around by getting all records at once - // Use stack buffer (3904 bytes / ~80 bytes per record = ~48 records) with heap fallback - static constexpr size_t SCAN_RECORD_STACK_COUNT = 3904 / sizeof(wifi_ap_record_t); - SmallBufferWithHeapFallback records(number); - err = esp_wifi_scan_get_ap_records(&number, records.get()); - if (err != ESP_OK) { - esp_wifi_clear_ap_list(); - ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err)); - return; - } - for (uint16_t i = 0; i < number; i++) { - wifi_ap_record_t &record = records.get()[i]; -#else - // Process one record at a time to avoid large buffer allocation - for (uint16_t i = 0; i < number; i++) { - wifi_ap_record_t record; - err = esp_wifi_scan_get_ap_record(&record); + // getting records one at a time fails on P4 with hosted esp32 WiFi coprocessor + // Presumably an upstream bug, work-around by getting all records at once + // Use stack buffer (3904 bytes / ~80 bytes per record = ~48 records) with heap fallback + static constexpr size_t SCAN_RECORD_STACK_COUNT = 3904 / sizeof(wifi_ap_record_t); + SmallBufferWithHeapFallback records(number); + err = esp_wifi_scan_get_ap_records(&number, records.get()); if (err != ESP_OK) { - ESP_LOGW(TAG, "esp_wifi_scan_get_ap_record failed: %s", esp_err_to_name(err)); - esp_wifi_clear_ap_list(); // Free remaining records not yet retrieved - break; + esp_wifi_clear_ap_list(); + ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err)); + return; } + for (uint16_t i = 0; i < number; i++) { + wifi_ap_record_t &record = records.get()[i]; +#else + // Process one record at a time to avoid large buffer allocation + for (uint16_t i = 0; i < number; i++) { + wifi_ap_record_t record; + err = esp_wifi_scan_get_ap_record(&record); + if (err != ESP_OK) { + ESP_LOGW(TAG, "esp_wifi_scan_get_ap_record failed: %s", esp_err_to_name(err)); + esp_wifi_clear_ap_list(); // Free remaining records not yet retrieved + break; + } #endif // USE_ESP32_HOSTED - // Check C string first - avoid std::string construction for non-matching networks - const char *ssid_cstr = reinterpret_cast(record.ssid); + // Check C string first - avoid std::string construction for non-matching networks + const char *ssid_cstr = reinterpret_cast(record.ssid); - // Only construct std::string and store if needed - if (needs_full || this->matches_configured_network_(ssid_cstr, record.bssid)) { - bssid_t bssid; - std::copy(record.bssid, record.bssid + 6, bssid.begin()); - this->scan_result_.emplace_back(bssid, ssid_cstr, strlen(ssid_cstr), record.primary, record.rssi, - record.authmode != WIFI_AUTH_OPEN, ssid_cstr[0] == '\0'); - } else { - this->log_discarded_scan_result_(ssid_cstr, record.bssid, record.rssi, record.primary); + // Only construct std::string and store if needed + if (needs_full || this->matches_configured_network_(ssid_cstr, record.bssid)) { + bssid_t bssid; + std::copy(record.bssid, record.bssid + 6, bssid.begin()); + this->scan_result_.emplace_back(bssid, ssid_cstr, strlen(ssid_cstr), record.primary, record.rssi, + record.authmode != WIFI_AUTH_OPEN, ssid_cstr[0] == '\0'); + } else { + this->log_discarded_scan_result_(ssid_cstr, record.bssid, record.rssi, record.primary); + } } } ESP_LOGV(TAG, "Scan complete: %u found, %zu stored%s", number, this->scan_result_.size(), diff --git a/esphome/components/wifi/wifi_component_libretiny.cpp b/esphome/components/wifi/wifi_component_libretiny.cpp index 59efa4f842..ce9c4eb6ce 100644 --- a/esphome/components/wifi/wifi_component_libretiny.cpp +++ b/esphome/components/wifi/wifi_component_libretiny.cpp @@ -657,44 +657,48 @@ bool WiFiComponent::wifi_scan_start_(bool passive) { return true; } void WiFiComponent::wifi_scan_done_callback_() { - this->scan_result_.clear(); - this->scan_done_ = true; - int16_t num = WiFi.scanComplete(); - if (num < 0) - return; - bool needs_full = this->needs_full_scan_results_(); + { + // Mutate in place under the lock; blocking a portal request is fine and + // avoids scratch buffers + ScanResultsLock lock(this); + this->scan_result_.clear(); + this->scan_done_ = true; - // Access scan results directly via WiFi.scan struct to avoid Arduino String allocations - // WiFi.scan is public in LibreTiny for WiFiEvents & WiFiScan static handlers - auto *scan = WiFi.scan; + if (num < 0) + return; - // First pass: count matching networks - size_t count = 0; - for (int i = 0; i < num; i++) { - const char *ssid_cstr = scan->ap[i].ssid; - if (needs_full || this->matches_configured_network_(ssid_cstr, scan->ap[i].bssid.addr)) { - count++; + // Access scan results directly via WiFi.scan struct to avoid Arduino String allocations + // WiFi.scan is public in LibreTiny for WiFiEvents & WiFiScan static handlers + auto *scan = WiFi.scan; + + // First pass: count matching networks + size_t count = 0; + for (int i = 0; i < num; i++) { + const char *ssid_cstr = scan->ap[i].ssid; + if (needs_full || this->matches_configured_network_(ssid_cstr, scan->ap[i].bssid.addr)) { + count++; + } + } + + this->scan_result_.init(count); // Exact allocation + + // Second pass: store matching networks + for (int i = 0; i < num; i++) { + const char *ssid_cstr = scan->ap[i].ssid; + auto &ap = scan->ap[i]; + if (needs_full || this->matches_configured_network_(ssid_cstr, ap.bssid.addr)) { + this->scan_result_.emplace_back(bssid_t{ap.bssid.addr[0], ap.bssid.addr[1], ap.bssid.addr[2], ap.bssid.addr[3], + ap.bssid.addr[4], ap.bssid.addr[5]}, + ssid_cstr, strlen(ssid_cstr), ap.channel, ap.rssi, ap.auth != WIFI_AUTH_OPEN, + ssid_cstr[0] == '\0'); + } else { + this->log_discarded_scan_result_(ssid_cstr, ap.bssid.addr, ap.rssi, ap.channel); + } } } - this->scan_result_.init(count); // Exact allocation - - // Second pass: store matching networks - for (int i = 0; i < num; i++) { - const char *ssid_cstr = scan->ap[i].ssid; - if (needs_full || this->matches_configured_network_(ssid_cstr, scan->ap[i].bssid.addr)) { - auto &ap = scan->ap[i]; - this->scan_result_.emplace_back(bssid_t{ap.bssid.addr[0], ap.bssid.addr[1], ap.bssid.addr[2], ap.bssid.addr[3], - ap.bssid.addr[4], ap.bssid.addr[5]}, - ssid_cstr, strlen(ssid_cstr), ap.channel, ap.rssi, ap.auth != WIFI_AUTH_OPEN, - ssid_cstr[0] == '\0'); - } else { - auto &ap = scan->ap[i]; - this->log_discarded_scan_result_(ssid_cstr, ap.bssid.addr, ap.rssi, ap.channel); - } - } ESP_LOGV(TAG, "Scan complete: %d found, %zu stored%s", num, this->scan_result_.size(), needs_full ? "" : " (filtered)"); WiFi.scanDelete(); diff --git a/esphome/components/wifi/wifi_component_pico_w.cpp b/esphome/components/wifi/wifi_component_pico_w.cpp index 1a70f81a2b..69af9e9a4e 100644 --- a/esphome/components/wifi/wifi_component_pico_w.cpp +++ b/esphome/components/wifi/wifi_component_pico_w.cpp @@ -109,10 +109,7 @@ bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // setup depends on begin() succeeding. beginNoBlock() skips the outer wait loop, saving // up to 20 additional seconds of blocking per attempt. auto ret = WiFi.beginNoBlock(ap.ssid_.c_str(), ap.password_.c_str()); - if (ret == WL_IDLE_STATUS) - return false; - - return true; + return ret != WL_IDLE_STATUS; } bool WiFiComponent::wifi_sta_pre_setup_() { return this->wifi_mode_(true, {}); } @@ -169,11 +166,11 @@ WiFiSTAConnectStatus WiFiComponent::wifi_sta_connect_status_() const { } int WiFiComponent::s_wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result) { - global_wifi_component->wifi_scan_result(env, result); + global_wifi_component->wifi_scan_result_(env, result); return 0; } -void WiFiComponent::wifi_scan_result(void *env, const cyw43_ev_scan_result_t *result) { +void WiFiComponent::wifi_scan_result_(void *env, const cyw43_ev_scan_result_t *result) { s_scan_result_count++; // CYW43 scan results have ssid as a 32-byte buffer that is NOT null-terminated. @@ -193,12 +190,16 @@ void WiFiComponent::wifi_scan_result(void *env, const cyw43_ev_scan_result_t *re std::copy(result->bssid, result->bssid + 6, bssid.begin()); WiFiScanResult res(bssid, ssid_buf, len, result->channel, result->rssi, result->auth_mode != CYW43_AUTH_OPEN, len == 0); + // Compiles to nothing here; kept so every scan_result_ mutation holds the lock + ScanResultsLock lock(this); if (std::find(this->scan_result_.begin(), this->scan_result_.end(), res) == this->scan_result_.end()) { this->scan_result_.push_back(res); } } bool WiFiComponent::wifi_scan_start_(bool passive) { + // Compiles to nothing here; kept so every scan_result_ mutation holds the lock + ScanResultsLock lock(this); this->scan_result_.clear(); this->scan_done_ = false; s_scan_result_count = 0; @@ -282,7 +283,7 @@ network::IPAddresses WiFiComponent::wifi_sta_ip_addresses() { // Filter out AP interface addresses — addrList includes all lwIP netifs. // The AP netif IP lingers even after the AP radio is disabled. IPAddress ap_ip = WiFi.softAPIP(); - for (auto addr : addrList) { + for (const auto &addr : addrList) { IPAddress ip(addr.ipFromNetifNum()); if (ip == ap_ip) { continue; @@ -351,12 +352,11 @@ bool WiFiComponent::wifi_loop_() { // Detect IP address changes (only when connected) if (is_connected) { - bool has_ip = false; - // Check for any IP address (IPv4 or IPv6) - for (auto addr : addrList) { - has_ip = true; - break; - } + // Check for any IP address (IPv4 or IPv6). The iterator comparison + // operators take non-const references, so the temporaries need names. + auto addr_it = addrList.begin(); + auto addr_end = addrList.end(); + bool has_ip = addr_it != addr_end; if (has_ip && !s_sta_had_ip) { // Just got IP address diff --git a/esphome/components/wifi/wpa2_eap.py b/esphome/components/wifi/wpa2_eap.py index 51971a1220..089a4fa99a 100644 --- a/esphome/components/wifi/wpa2_eap.py +++ b/esphome/components/wifi/wpa2_eap.py @@ -58,7 +58,7 @@ def wrapped_load_pem_private_key(value, password): def read_relative_config_path(value): # pylint: disable=unspecified-encoding - return Path(CORE.relative_config_path(value)).read_text() + return Path(CORE.relative_config_path(value)).read_text(encoding="utf-8") def _validate_load_certificate(value): diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index e128b8476d..ea9e5a3b0c 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -6,7 +6,17 @@ import esphome.codegen as cg from esphome.components import time from esphome.components.esp32 import CORE, add_idf_sdkconfig_option import esphome.config_validation as cv -from esphome.const import CONF_ADDRESS, CONF_ID, CONF_REBOOT_TIMEOUT, CONF_TIME_ID +from esphome.const import ( + CONF_ADDRESS, + CONF_ID, + CONF_REBOOT_TIMEOUT, + CONF_TIME_ID, + PLATFORM_BK72XX, + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_LN882X, + PLATFORM_RTL87XX, +) from esphome.core import TimePeriod CONF_NETMASK = "netmask" @@ -57,30 +67,41 @@ def _cidr_network(value): return value -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(Wireguard), - cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), - cv.Required(CONF_ADDRESS): cv.ipv4address, - cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4address, - cv.Required(CONF_PRIVATE_KEY): _wireguard_key, - cv.Required(CONF_PEER_ENDPOINT): cv.string, - cv.Required(CONF_PEER_PUBLIC_KEY): _wireguard_key, - cv.Optional(CONF_PEER_PORT, default=51820): cv.port, - cv.Optional(CONF_PEER_PRESHARED_KEY): _wireguard_key, - cv.Optional(CONF_PEER_ALLOWED_IPS, default=["0.0.0.0/0"]): cv.ensure_list( - _cidr_network - ), - cv.Optional(CONF_PEER_PERSISTENT_KEEPALIVE, default="0s"): cv.All( - cv.positive_time_period_seconds, - cv.Range(max=TimePeriod(seconds=65535)), - ), - cv.Optional( - CONF_REBOOT_TIMEOUT, default="15min" - ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_REQUIRE_CONNECTION_TO_PROCEED, default=False): cv.boolean, - } -).extend(cv.polling_component_schema("10s")) +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(Wireguard), + cv.GenerateID(CONF_TIME_ID): cv.use_id(time.RealTimeClock), + cv.Required(CONF_ADDRESS): cv.ipv4address, + cv.Optional(CONF_NETMASK, default="255.255.255.255"): cv.ipv4address, + cv.Required(CONF_PRIVATE_KEY): cv.sensitive(_wireguard_key), + cv.Required(CONF_PEER_ENDPOINT): cv.string, + cv.Required(CONF_PEER_PUBLIC_KEY): _wireguard_key, + cv.Optional(CONF_PEER_PORT, default=51820): cv.port, + cv.Optional(CONF_PEER_PRESHARED_KEY): cv.sensitive(_wireguard_key), + cv.Optional(CONF_PEER_ALLOWED_IPS, default=["0.0.0.0/0"]): cv.ensure_list( + _cidr_network + ), + cv.Optional(CONF_PEER_PERSISTENT_KEEPALIVE, default="0s"): cv.All( + cv.positive_time_period_seconds, + cv.Range(max=TimePeriod(seconds=65535)), + ), + cv.Optional( + CONF_REBOOT_TIMEOUT, default="15min" + ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_REQUIRE_CONNECTION_TO_PROCEED, default=False): cv.boolean, + } + ).extend(cv.polling_component_schema("10s")), + cv.only_on( + [ + PLATFORM_ESP32, + PLATFORM_ESP8266, + PLATFORM_BK72XX, + PLATFORM_RTL87XX, + PLATFORM_LN882X, + ] + ), +) async def to_code(config): diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index e0724aa94a..5150cda2a5 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -13,7 +13,7 @@ #include #endif -#ifdef USE_BK72XX +#ifdef USE_LIBRETINY #include #endif diff --git a/esphome/components/wled/wled_light_effect.h b/esphome/components/wled/wled_light_effect.h index bed897f5a6..07abb7c674 100644 --- a/esphome/components/wled/wled_light_effect.h +++ b/esphome/components/wled/wled_light_effect.h @@ -8,7 +8,14 @@ #include #include +#if defined(USE_RP2) || defined(USE_LIBRETINY) +namespace arduino { class UDP; +} // namespace arduino +using arduino::UDP; // NOLINT(google-global-names-in-headers) +#else +class UDP; +#endif namespace esphome::wled { diff --git a/esphome/components/zephyr/__init__.py b/esphome/components/zephyr/__init__.py index b98f94d37a..524dc55a13 100644 --- a/esphome/components/zephyr/__init__.py +++ b/esphome/components/zephyr/__init__.py @@ -26,7 +26,26 @@ from .const import ( CODEOWNERS = ["@tomaszduda23"] -PrjConfValueType = bool | str | int + +class HexValue: + """Wrap an integer so it is written as 0x... in prj.conf (required for hex Kconfig types).""" + + def __init__(self, value: int) -> None: + self.value = value + + def __eq__(self, other: object) -> bool: + if isinstance(other, HexValue): + return self.value == other.value + return NotImplemented + + def __repr__(self) -> str: + return f"HexValue(0x{self.value:X})" + + def __str__(self) -> str: + return f"0x{self.value:X}" + + +PrjConfValueType = bool | str | int | HexValue class Section: @@ -164,6 +183,8 @@ def zephyr_setup_preferences(): def _format_prj_conf_val(value: PrjConfValueType) -> str: if isinstance(value, bool): return "y" if value else "n" + if isinstance(value, HexValue): + return hex(value.value) if isinstance(value, int): return str(value) if isinstance(value, str): @@ -249,7 +270,7 @@ def copy_files() -> None: ) if image: - path = CORE.relative_build_path(f"sysbuild/{image}.conf") + path = CORE.relative_build_path(f"zephyr/sysbuild/{image}.conf") else: path = CORE.relative_build_path("zephyr/prj.conf") @@ -257,7 +278,7 @@ def copy_files() -> None: for image, content in zephyr_data()[KEY_OVERLAY].items(): if image: - path = CORE.relative_build_path(f"sysbuild/{image}.overlay") + path = CORE.relative_build_path(f"zephyr/sysbuild/{image}.overlay") else: path = CORE.relative_build_path("zephyr/app.overlay") changed |= write_file_if_changed(path, content) diff --git a/esphome/components/zephyr/gpio.cpp b/esphome/components/zephyr/gpio.cpp index 1e4201d8f5..23da2cafac 100644 --- a/esphome/components/zephyr/gpio.cpp +++ b/esphome/components/zephyr/gpio.cpp @@ -173,6 +173,14 @@ bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { return bool(gpio_pin_get(arg->gpio, arg->pin % arg->gpio_size) != arg->inverted); } +void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) { + auto *arg = (zephyr::ISRPinArg *) this->arg_; + if (arg == nullptr || arg->gpio == nullptr) { + return; + } + gpio_pin_set(arg->gpio, arg->pin % arg->gpio_size, value != arg->inverted ? 1 : 0); +} + } // namespace esphome #endif diff --git a/esphome/components/zephyr_mcumgr/ota/__init__.py b/esphome/components/zephyr_mcumgr/ota/__init__.py index 0ff1825bd1..1503c94274 100644 --- a/esphome/components/zephyr_mcumgr/ota/__init__.py +++ b/esphome/components/zephyr_mcumgr/ota/__init__.py @@ -1,6 +1,8 @@ import esphome.codegen as cg +from esphome.components.nrf52.boards import BOOTLOADER_CONFIG from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code from esphome.components.zephyr import ( + HexValue, zephyr_add_cdc_acm, zephyr_add_overlay, zephyr_add_prj_conf, @@ -72,12 +74,6 @@ CONFIG_SCHEMA = cv.All( ) -def _validate_mcumgr_bootloader(config: ConfigType) -> None: - bootloader = zephyr_data()[KEY_BOOTLOADER] - if bootloader != BOOTLOADER_MCUBOOT: - raise cv.Invalid(f"'{bootloader}' bootloader does not support OTA") - - KEY_ZEPHYR_BLE_SERVER = "zephyr_ble_server" @@ -89,9 +85,22 @@ def _validate_ble_server(config: ConfigType) -> None: raise cv.Invalid(f"'{KEY_ZEPHYR_BLE_SERVER}' component is required for BLE OTA") +def _validate_bootloader(config: ConfigType) -> None: + bootloader = zephyr_data()[KEY_BOOTLOADER] + if bootloader == BOOTLOADER_MCUBOOT: + return + if bootloader not in BOOTLOADER_CONFIG: + raise cv.Invalid(f"{bootloader} does not support OTA") + framework_ver: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] + if framework_ver < cv.Version(2, 9, 2): + raise cv.Invalid( + "OTA with Adafruit_nRF52_Bootloader requires at least SDK 2.9.2" + ) + + def _final_validate(config: ConfigType) -> None: - _validate_mcumgr_bootloader(config) _validate_ble_server(config) + _validate_bootloader(config) FINAL_VALIDATE_SCHEMA = _final_validate @@ -152,3 +161,70 @@ async def to_code(config: ConfigType) -> None: framework_ver = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] if framework_ver >= cv.Version(2, 9, 2): zephyr_data()[KEY_SYSBUILD] = True + + bootloader = zephyr_data()[KEY_BOOTLOADER] + if bootloader != BOOTLOADER_MCUBOOT: + sections = BOOTLOADER_CONFIG[bootloader] + # Derive partition addresses from the SoftDevice and bootloader sections so + # that the DTS flash map matches what the Partition Manager produces: + # MCUboot sits immediately after the SoftDevice, then slot0, then slot1. + mcuboot_size = 0x9000 + sd_end = next(s.address + s.size for s in sections if "SoftDevice" in s.name) + bl_start = next(s.address for s in sections if "Adafruit" in s.name) + slot0_start = sd_end + mcuboot_size + # Align slot size down to a 4 KB sector boundary + slot_size = ((bl_start - slot0_start) // 2 // 0x1000) * 0x1000 + slot1_start = slot0_start + slot_size + + def _mcuboot_partition_overlay() -> str: + def part(name, start, size): + return f""" + {name}: partition@{start:x} {{ + reg = <0x{start:x} 0x{size:x}>; + }};""" + + return f""" + /delete-node/ &boot_partition; + /delete-node/ &storage_partition; + /delete-node/ &code_partition; + /delete-node/ &reserved_partition_0; + + &flash0 {{ + partitions {{ + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + {part("slot0_partition", slot0_start, slot_size)} + {part("slot1_partition", slot1_start, slot_size)} + }}; + }}; + """ + + def _code_partition_overlay() -> str: + return """ + / { + chosen { + zephyr,code-partition = &slot0_partition; + }; + }; + """ + + zephyr_add_overlay(_mcuboot_partition_overlay()) + zephyr_add_overlay(_mcuboot_partition_overlay(), "mcuboot") + zephyr_add_overlay(_code_partition_overlay()) + zephyr_add_overlay(_code_partition_overlay(), "mcuboot") + # mcuboot is second bootloader. It's only task is to swap partitions. + # recovery can be done by first bootloader. Keep it small. + zephyr_add_overlay( + """ + &zephyr_udc0 { + status = "disabled"; + }; + """, + "mcuboot", + ) + zephyr_add_prj_conf("USB_DEVICE_STACK", False, image="mcuboot") + zephyr_add_prj_conf("CONSOLE", False, image="mcuboot") + zephyr_add_prj_conf( + "PM_PARTITION_SIZE_MCUBOOT", HexValue(mcuboot_size), image="mcuboot" + ) diff --git a/esphome/components/zigbee/zigbee_zephyr.cpp b/esphome/components/zigbee/zigbee_zephyr.cpp index 81aad7dcb1..fedcb4a9c2 100644 --- a/esphome/components/zigbee/zigbee_zephyr.cpp +++ b/esphome/components/zigbee/zigbee_zephyr.cpp @@ -229,6 +229,7 @@ void ZigbeeComponent::dump_config() { " Wipe on boot: %s\n" " Device is joined to the network: %s\n" " Sleep time: %us\n" + " Radio sleep time: %us\n" " RX ON when idle: %s\n" " Current channel: %d\n" " Current page: %d\n" @@ -238,9 +239,10 @@ void ZigbeeComponent::dump_config() { " Short addr: 0x%04X\n" " Long pan id: 0x%s\n" " Short pan id: 0x%04X", - get_wipe_on_boot(), YESNO(zb_zdo_joined()), this->sleep_time_, YESNO(zb_get_rx_on_when_idle()), - zb_get_current_channel(), zb_get_current_page(), zb_get_sleep_threshold(), role(), ieee_addr_buf, - zb_get_short_address(), extended_pan_id_buf, zb_get_pan_id()); + get_wipe_on_boot(), YESNO(zb_zdo_joined()), this->sleep_time_, this->radio_sleep_time_, + YESNO(zb_get_rx_on_when_idle()), zb_get_current_channel(), zb_get_current_page(), + zb_get_sleep_threshold(), role(), ieee_addr_buf, zb_get_short_address(), extended_pan_id_buf, + zb_get_pan_id()); dump_reporting_(); } @@ -251,6 +253,13 @@ static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) { void ZigbeeComponent::force_report() { this->force_report_ = true; } +void ZigbeeComponent::add_radio_sleep_time_ms(uint32_t ms) { + this->radio_sleep_remainder_ += ms; + uint32_t seconds = this->radio_sleep_remainder_ / 1000; + this->radio_sleep_remainder_ -= seconds * 1000; + this->radio_sleep_time_ += seconds; +} + void ZigbeeComponent::loop() { if (this->force_report_) { this->force_report_ = false; @@ -327,6 +336,36 @@ zb_ret_t __wrap_zb_zcl_put_reporting_info_from_req(zb_zcl_configure_reporting_re esphome::zigbee::global_zigbee->after_reporting_info(config_rep_req, attr_addr_info); return ret; } + +extern void __real_zb_trans_enter_sleep(void); +extern void __real_zb_trans_enter_receive(void); +extern zb_bool_t __real_zb_trans_transmit(zb_uint8_t wait_type, zb_time_t tx_at, zb_uint8_t *tx_buf, + zb_uint8_t current_channel); + +static uint32_t radio_sleep_start_ms = 0; + +static void stop_radio_sleep_timer() { + if (radio_sleep_start_ms) { + esphome::zigbee::global_zigbee->add_radio_sleep_time_ms(esphome::millis() - radio_sleep_start_ms); + } + radio_sleep_start_ms = 0; +} + +void __wrap_zb_trans_enter_sleep(void) { + __real_zb_trans_enter_sleep(); + radio_sleep_start_ms = esphome::millis(); +} + +void __wrap_zb_trans_enter_receive(void) { + stop_radio_sleep_timer(); + __real_zb_trans_enter_receive(); +} + +zb_bool_t __wrap_zb_trans_transmit(zb_uint8_t wait_type, zb_time_t tx_at, zb_uint8_t *tx_buf, + zb_uint8_t current_channel) { + stop_radio_sleep_timer(); + return __real_zb_trans_transmit(wait_type, tx_at, tx_buf, current_channel); +} // NOLINTEND(readability-identifier-naming,bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) } #endif diff --git a/esphome/components/zigbee/zigbee_zephyr.h b/esphome/components/zigbee/zigbee_zephyr.h index 3b4a465361..8528aebff8 100644 --- a/esphome/components/zigbee/zigbee_zephyr.h +++ b/esphome/components/zigbee/zigbee_zephyr.h @@ -81,6 +81,7 @@ class ZigbeeComponent final : public Component { void force_report(); void loop() override; void set_sleepy(bool sleepy) { this->sleepy_ = sleepy; } + void add_radio_sleep_time_ms(uint32_t ms); protected: static void zcl_device_cb(zb_bufid_t bufid); @@ -94,6 +95,8 @@ class ZigbeeComponent final : public Component { bool force_report_{false}; uint32_t sleep_time_{}; uint32_t sleep_remainder_{}; + uint32_t radio_sleep_time_{}; + uint32_t radio_sleep_remainder_{}; bool sleepy_{}; }; diff --git a/esphome/components/zigbee/zigbee_zephyr.py b/esphome/components/zigbee/zigbee_zephyr.py index 1647fb28ae..f47cf6bd40 100644 --- a/esphome/components/zigbee/zigbee_zephyr.py +++ b/esphome/components/zigbee/zigbee_zephyr.py @@ -117,6 +117,14 @@ async def zephyr_to_code(config: ConfigType) -> "MockObj": cg.add_build_flag("-Wl,--wrap=zb_zcl_put_reporting_info_from_req") + # Wrap the transceiver sleep/receive/transmit calls to measure how long the + # radio is powered down. The span between a zb_trans_enter_sleep() and the + # following zb_trans_enter_receive() or zb_trans_transmit() is time the + # radio spent asleep. + cg.add_build_flag("-Wl,--wrap=zb_trans_enter_sleep") + cg.add_build_flag("-Wl,--wrap=zb_trans_enter_receive") + cg.add_build_flag("-Wl,--wrap=zb_trans_transmit") + if CONF_IEEE802154_VENDOR_OUI in config: zephyr_add_prj_conf("IEEE802154_VENDOR_OUI_ENABLE", True) random_number = config[CONF_IEEE802154_VENDOR_OUI] diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 16f0a63aa0..ff9170813c 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -422,12 +422,14 @@ class Version: @classmethod def parse(cls, value: str) -> Version: - match = re.match(r"^(\d+).(\d+).(\d+)[-.]?(\w*)$", value) + # The patch component is optional and defaults to 0, so "6.0" and + # "6.0-rc1" parse as 6.0.0 and 6.0.0-rc1. + match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?[-.]?(\w*)$", value) if match is None: raise ValueError(f"Not a valid version number {value}") major = int(match[1]) minor = int(match[2]) - patch = int(match[3]) + patch = int(match[3] or 0) extra = match[4] or "" return Version(major=major, minor=minor, patch=patch, extra=extra) @@ -1813,6 +1815,8 @@ def one_of(*values, **kwargs): - *int* (``bool``, default=False): Whether to convert the incoming values to integers. - *float* (``bool``, default=False): Whether to convert the incoming values to floats. - *space* (``str``, default=' '): What to convert spaces in the input string to. + - *underscore* (``str``, default='_'): What to convert underscores in the input string to. + - *hyphen* (``str``, default='-'): What to convert hyphens in the input string to. """ options = ", ".join(f"'{x}'" for x in values) lower = kwargs.pop("lower", False) @@ -1821,8 +1825,11 @@ def one_of(*values, **kwargs): to_int = kwargs.pop("int", False) to_float = kwargs.pop("float", False) space = kwargs.pop("space", " ") + underscore = kwargs.pop("underscore", "_") + hyphen = kwargs.pop("hyphen", "-") if kwargs: raise ValueError + separators = str.maketrans({" ": space, "_": underscore, "-": hyphen}) @schema_extractor("one_of") def validator(value): @@ -1831,7 +1838,7 @@ def one_of(*values, **kwargs): if string_: value = string(value) - value = value.replace(" ", space) + value = value.translate(separators) if to_int: value = int_(value) if to_float: @@ -1936,14 +1943,29 @@ def dimensions(value): return dimensions([match.group(1), match.group(2)]) +def _remap_bundle_path(value: str) -> Path | None: + """Resolve a path from the machine an extracted bundle was created on. + + An absolute path in a config compiled from an extracted bundle may point + at the machine the bundle was created on; the bundle ships the file at + its config-relative location instead. + """ + from esphome.bundle import remap_bundle_path + + return remap_bundle_path(value) + + def directory(value: object) -> Path: value = string(value) path = CORE.relative_config_path(value) if not path.exists(): - raise Invalid( - f"Could not find directory '{path}'. Please make sure it exists (full path: {path.resolve()})." - ) + remapped = _remap_bundle_path(value) + if remapped is None: + raise Invalid( + f"Could not find directory '{path}'. Please make sure it exists (full path: {path.resolve()})." + ) + path = remapped if not path.is_dir(): raise Invalid( f"Path '{path}' is not a directory (full path: {path.resolve()})." @@ -1956,9 +1978,12 @@ def file_(value: object) -> Path: path = CORE.relative_config_path(value) if not path.exists(): - raise Invalid( - f"Could not find file '{path}'. Please make sure it exists (full path: {path.resolve()})." - ) + remapped = _remap_bundle_path(value) + if remapped is None: + raise Invalid( + f"Could not find file '{path}'. Please make sure it exists (full path: {path.resolve()})." + ) + path = remapped if not path.is_file(): raise Invalid(f"Path '{path}' is not a file (full path: {path.resolve()}).") return path @@ -2490,11 +2515,16 @@ def git_ref(value): return value +# What `refresh: never` validates to; also used to recognize a disabled +# refresh when logging (see esphome/git.py) +SOURCE_REFRESH_NEVER = "365250d" + + def source_refresh(value: str): if value.lower() == "always": return source_refresh("0s") if value.lower() == "never": - return source_refresh("365250d") + return source_refresh(SOURCE_REFRESH_NEVER) return positive_time_period_seconds(value) @@ -2698,10 +2728,32 @@ SOURCE_SCHEMA = Any( ) -def rename_key(old_key, new_key): +def rename_key( + old_key, new_key, *, removed_in: str | None = None, component: str | None = None +): + """Rename a config key from ``old_key`` to ``new_key``. + + Specifying both keys is an error; otherwise only one of the two would + survive the rename and the other would be dropped silently. + + When ``removed_in`` is set, a deprecation warning is logged if the old key is + present. Pass ``component`` (the platform/component name) alongside + ``removed_in`` so the warning identifies where it originates. + """ + def validator(config: dict) -> dict: config = config.copy() if old_key in config: + has_at_most_one_key(old_key, new_key)(config) + if removed_in is not None: + prefix = f"[{component}] " if component else "" + _LOGGER.warning( + "%s'%s' is deprecated, use '%s'. Will be removed in %s", + prefix, + old_key, + new_key, + removed_in, + ) config[new_key] = config.pop(old_key) return config diff --git a/esphome/core/application.h b/esphome/core/application.h index 76af514511..a12cdc4ac8 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -612,6 +612,8 @@ class LoopBlockingGuard { uint32_t blocking_time = curr_time - App.get_loop_component_start_time(); if (blocking_time > WARN_IF_BLOCKING_OVER_MS) [[unlikely]] { warn_blocking(blocking_time); + // Exclude synchronous warning-log time from the next operation. + curr_time = MillisInternal::get(); } #endif return curr_time; diff --git a/esphome/core/base_automation.h b/esphome/core/base_automation.h index cf8b05a300..276b8aa972 100644 --- a/esphome/core/base_automation.h +++ b/esphome/core/base_automation.h @@ -201,7 +201,7 @@ template class DelayAction : public Action { /* component= */ nullptr, Scheduler::SchedulerItem::TIMEOUT, Scheduler::NameType::SELF_POINTER, /* static_name= */ reinterpret_cast(this), /* hash_or_id= */ 0, this->delay_.value(), [this]() { this->play_next_(); }, - /* is_retry= */ false, /* skip_cancel= */ this->num_running_ > 1, + /* skip_cancel= */ this->num_running_ > 1, // Record the owning script (if any) so the blocking warning can name it; propagates across // chained delays via the scheduler. /* source= */ App.get_current_source()); @@ -215,7 +215,7 @@ template class DelayAction : public Action { /* component= */ nullptr, Scheduler::SchedulerItem::TIMEOUT, Scheduler::NameType::SELF_POINTER, /* static_name= */ reinterpret_cast(this), /* hash_or_id= */ 0, this->delay_.value(x...), std::move(f), - /* is_retry= */ false, /* skip_cancel= */ this->num_running_ > 1, + /* skip_cancel= */ this->num_running_ > 1, // See the no-argument branch above: record the owning script for log attribution. /* source= */ App.get_current_source()); } @@ -502,6 +502,9 @@ template class WaitUntilAction : public Action, public Co void stop() override { this->var_queue_.clear(); + // Tell any process_queue_() call further down the stack that the items it is + // still holding were cancelled + this->stop_generation_++; this->disable_loop(); } @@ -511,33 +514,57 @@ template class WaitUntilAction : public Action, public Co } protected: + using QueueItem = std::tuple, std::tuple>; + // Helper: Process queue, triggering completed items and removing them // Returns true if queue still has pending items bool process_queue_(uint32_t now) { - // Process each queued wait_until and remove completed ones - this->var_queue_.remove_if([&](auto &queued) { - auto start = std::get(queued); - auto timeout = std::get>(queued); - auto &var = std::get>(queued); + // Completed items run the rest of the action chain synchronously, and that chain + // can re-enter this same action (e.g. a script with mode: restart that executes + // itself) and add to or clear var_queue_. Iterating the member list directly would + // then corrupt it, so move it aside and iterate a local list instead. + std::list queue; + queue.swap(this->var_queue_); + std::list pending; + while (!queue.empty()) { + auto it = queue.begin(); + auto start = std::get(*it); + auto timeout = std::get>(*it); // Check if timeout has expired auto expired = timeout && (now - start) >= *timeout; // Keep waiting if not expired and condition not met - if (!expired && !this->condition_->check_tuple(var)) { - return false; + if (!expired && !this->condition_->check_tuple(std::get>(*it))) { + pending.splice(pending.end(), queue, it); + continue; } - // Condition met or timed out - trigger next action - this->play_next_tuple_(var); - return true; - }); + // Condition met or timed out - trigger the next action. Keep the item in a local + // holder so its arguments stay valid while the chain runs, without any nested + // process_queue_() call being able to see (and fire) it again. + std::list completed; + completed.splice(completed.begin(), queue, it); + uint8_t generation = this->stop_generation_; + this->play_next_tuple_(std::get>(completed.front())); + if (generation != this->stop_generation_) { + // stop() ran inside the chain - the items still held locally were cancelled + pending.clear(); + break; + } + } + + // Re-entrant continuations may have enqueued new waits into var_queue_; put the + // older still-waiting items back in front of them to keep FIFO firing order + this->var_queue_.splice(this->var_queue_.begin(), pending); return !this->var_queue_.empty(); } Condition *condition_; - std::list, std::tuple>> var_queue_{}; + std::list var_queue_{}; + // Bumped by stop() so process_queue_() can detect a stop from inside play_next_tuple_() + uint8_t stop_generation_{0}; }; template class UpdateComponentAction : public Action { diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index 281d7aaecd..e5fbb8ba07 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -93,36 +93,6 @@ bool Component::cancel_interval(const char *name) { // NOLINT return App.scheduler.cancel_interval(this, name); } -void Component::set_retry(const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, - std::function &&f, float backoff_increase_factor) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); -#pragma GCC diagnostic pop -} - -void Component::set_retry(const char *name, uint32_t initial_wait_time, uint8_t max_attempts, - std::function &&f, float backoff_increase_factor) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - App.scheduler.set_retry(this, name, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); -#pragma GCC diagnostic pop -} - -bool Component::cancel_retry(const std::string &name) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return App.scheduler.cancel_retry(this, name); -#pragma GCC diagnostic pop -} - -bool Component::cancel_retry(const char *name) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return App.scheduler.cancel_retry(this, name); -#pragma GCC diagnostic pop -} - void Component::set_timeout(const char *name, uint32_t timeout, std::function &&f) { // NOLINT App.scheduler.set_timeout(this, name, timeout, std::move(f)); } @@ -156,21 +126,6 @@ void Component::set_interval(InternalSchedulerID id, uint32_t interval, std::fun bool Component::cancel_interval(InternalSchedulerID id) { return App.scheduler.cancel_interval(this, id); } -void Component::set_retry(uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, - std::function &&f, float backoff_increase_factor) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - App.scheduler.set_retry(this, id, initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); -#pragma GCC diagnostic pop -} - -bool Component::cancel_retry(uint32_t id) { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - return App.scheduler.cancel_retry(this, id); -#pragma GCC diagnostic pop -} - void Component::call_setup() { this->setup(); } void Component::call_dump_config_() { this->dump_config(); @@ -307,13 +262,6 @@ void Component::set_timeout(uint32_t timeout, std::function &&f) { // N void Component::set_interval(uint32_t interval, std::function &&f) { // NOLINT App.scheduler.set_interval(this, static_cast(nullptr), interval, std::move(f)); } -void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, - float backoff_increase_factor) { // NOLINT -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - App.scheduler.set_retry(this, "", initial_wait_time, max_attempts, std::move(f), backoff_increase_factor); -#pragma GCC diagnostic pop -} bool Component::is_ready() const { // Bitmask check: valid states are SETUP(1), LOOP(2), LOOP_DONE(4) // (1 << state) & 0b10110 checks membership in one instruction diff --git a/esphome/core/component.h b/esphome/core/component.h index 70a051ca0b..ecaf863ecf 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -96,8 +96,6 @@ inline constexpr uint8_t COMPONENT_HAS_LOOP = 0x20; // decide whether to propagate clears to App.app_state_. Never set on a // Component's component_state_. inline constexpr uint8_t APP_STATE_SETUP_COMPLETE = 0x40; -// Remove before 2026.8.0 -enum class RetryResult { DONE, RETRY }; inline constexpr uint8_t WARN_IF_BLOCKING_OVER_CS = 5U; // 50ms in centiseconds (1cs = 10ms) @@ -410,41 +408,6 @@ class Component { bool cancel_interval(uint32_t id); // NOLINT bool cancel_interval(InternalSchedulerID id); // NOLINT - /// @deprecated set_retry is deprecated. Use set_timeout or set_interval instead. Removed in 2026.8.0. - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT - std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT - - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(const char *name, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT - std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT - - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, // NOLINT - std::function &&f, float backoff_increase_factor = 1.0f); // NOLINT - - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std::function &&f, // NOLINT - float backoff_increase_factor = 1.0f); // NOLINT - - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(const std::string &name); // NOLINT - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(const char *name); // NOLINT - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(uint32_t id); // NOLINT - /** Set a timeout function with a const char* name. * * Similar to javascript's setTimeout(). Empty name means no cancelling possible. diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 5c5fc5e8b9..2b84c72a3c 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -44,6 +44,7 @@ #define USE_AREAS #define USE_BINARY_SENSOR #define USE_BINARY_SENSOR_FILTER +#define USE_BLE_DEVICE_IRK #define USE_BUTTON #define USE_CAMERA #define USE_CLIMATE @@ -56,6 +57,7 @@ #define USE_DATETIME_TIME #define USE_DEBUG #define USE_DEEP_SLEEP +#define USE_DEEP_SLEEP_ON_WAKE #define USE_DEVICES #define USE_DISPLAY #define USE_ENTITY_DEVICE_CLASS @@ -136,6 +138,7 @@ #define USE_MEDIA_PLAYER #define USE_MEDIA_SOURCE #define USE_NETWORK +#define USE_NETWORK_PRIMARY_INTERFACE_WIFI #define USE_NEXTION_COMMAND_SPACING #define USE_NEXTION_CONF_START_UP_PAGE #define USE_NEXTION_CONFIG_EXIT_REPARSE_ON_START @@ -156,6 +159,7 @@ #define USE_PREFERENCES_SYNC_EVERY_LOOP #define USE_PROVISIONING #define USE_QR_CODE +#define USE_SAFE_MODE_BOOT_IS_GOOD_ON_SHUTDOWN #define USE_SAFE_MODE_CALLBACK #define ESPHOME_SAFE_MODE_CALLBACK_COUNT 1 #define USE_SELECT @@ -205,8 +209,11 @@ #define MAX_API_CONNECTIONS 6 #define USE_MD5 #define USE_SHA256 +#ifndef USE_RP2 // no MQTT backend or esp_wireguard library on RP2 #define USE_MQTT #define USE_MQTT_COVER_JSON +#define USE_WIREGUARD +#endif #define USE_RTTTL_FINISHED_PLAYBACK_CALLBACK #define USE_RUNTIME_IMAGE_BMP #define USE_RUNTIME_IMAGE_PNG @@ -219,7 +226,6 @@ #define USE_WIFI #define USE_WIFI_AP #define USE_WIFI_MANUAL_IP -#define USE_WIREGUARD #endif // Arduino-specific feature flags @@ -252,6 +258,7 @@ #define BLUETOOTH_PROXY_MAX_CONNECTIONS 3 #define BLUETOOTH_PROXY_ADVERTISEMENT_BATCH_SIZE 16 #define USE_CAPTIVE_PORTAL +#define USE_WIFI_SCAN_RESULTS_LOCK #define USE_ESP32_BLE #define USE_ESP32_BLE_MAX_CONNECTIONS 3 #define USE_ESP32_BLE_CLIENT @@ -268,6 +275,7 @@ #define USE_ESP32_BLE_SERVER_ON_DISCONNECT #define ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT 1 #define ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT 1 +#define ESPHOME_BLE_DEVICE_BASE_LISTENER_COUNT 1 #define ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT 2 #define ESPHOME_ESP32_BLE_GAP_SCAN_EVENT_HANDLER_COUNT 1 #define ESPHOME_ESP32_BLE_GATTC_EVENT_HANDLER_COUNT 1 @@ -387,6 +395,7 @@ #define USE_ESP8266_CRASH_HANDLER #define USE_ARDUINO_VERSION_CODE VERSION_CODE(3, 1, 2) #define USE_CAPTIVE_PORTAL +#define USE_WIFI_SCAN_RESULTS_LOCK #define USE_ESP8266_LOGGER_SERIAL #define USE_ESP8266_LOGGER_SERIAL1 #define USE_ESP8266_PREFERENCES_FLASH @@ -432,10 +441,17 @@ #ifndef USE_ETHERNET_SPI #define USE_ETHERNET_SPI #endif +#define USE_ETHERNET_W5500 +#define USE_WIFI_IP_STATE_LISTENERS +#define ESPHOME_WIFI_IP_STATE_LISTENERS 2 +#define USE_ETHERNET_IP_STATE_LISTENERS +#define ESPHOME_ETHERNET_IP_STATE_LISTENERS 2 #endif #ifdef USE_LIBRETINY +#define USE_BK72XX_BLE #define USE_CAPTIVE_PORTAL +#define USE_WIFI_SCAN_RESULTS_LOCK #define USE_SOCKET_IMPL_LWIP_SOCKETS #define USE_LWIP_FAST_SELECT #define USE_WEBSERVER diff --git a/esphome/core/event_pool.h b/esphome/core/event_pool.h index ee8e81225a..55c9254327 100644 --- a/esphome/core/event_pool.h +++ b/esphome/core/event_pool.h @@ -1,6 +1,6 @@ #pragma once -#if defined(USE_ESP32) || defined(USE_ZEPHYR) +#if defined(USE_ESP32) || defined(USE_ZEPHYR) || defined(USE_LIBRETINY) #include #include @@ -86,4 +86,4 @@ template class EventPool { } // namespace esphome -#endif // defined(USE_ESP32) +#endif // defined(USE_ESP32) || defined(USE_ZEPHYR) || defined(USE_LIBRETINY) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index a7b63643a4..c8cf85d7d6 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -335,6 +335,72 @@ char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_ return format_hex_internal(buffer, buffer_size, data, length, 0, 'a'); } +const char *json_escape_into_buffer(std::span buf, StringRef value, bool short_control_escapes) { + if (buf.empty()) + return ""; + // Reserve one byte for the null terminator. + const size_t limit = buf.size() - 1; + size_t pos = 0; + for (char ch : value) { + auto c = static_cast(ch); + // Every short form is a backslash followed by a single character, so only that character is needed here. Keeping + // it a char rather than a string avoids putting the sequences in read only data, which is RAM on the ESP8266. + char escape = '\0'; + switch (c) { + case '"': + escape = '"'; + break; + case '\\': + escape = '\\'; + break; + case '\n': + escape = 'n'; + break; + case '\r': + escape = 'r'; + break; + case '\t': + escape = 't'; + break; + case '\b': + escape = 'b'; + break; + case '\f': + escape = 'f'; + break; + default: + break; + } + // " and \ are always written as two characters, but the control characters fall through to \u00XX when the + // caller did not ask for the short forms. + if (!short_control_escapes && c < 0x20) + escape = '\0'; + if (escape != '\0') { + if (pos + 2 > limit) + break; + buf[pos++] = '\\'; + buf[pos++] = escape; + } else if (c < 0x20) { + // Remaining control characters have no short form and must be written as \u00XX. The value is below 0x20, so + // the two high hex digits are always zero. + if (pos + JSON_ESCAPE_MAX_EXPANSION > limit) + break; + buf[pos++] = '\\'; + buf[pos++] = 'u'; + buf[pos++] = '0'; + buf[pos++] = '0'; + buf[pos++] = format_hex_char(static_cast(c >> 4)); + buf[pos++] = format_hex_char(static_cast(c & 0x0F)); + } else { + if (pos + 1 > limit) + break; + buf[pos++] = static_cast(c); + } + } + buf[pos] = '\0'; + return buf.data(); +} + // format_hex (std::string returning overloads) moved to alloc_helpers.cpp char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator) { diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index e862d015da..7940df8780 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1268,6 +1268,22 @@ ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex /// Convert a nibble (0-15) to uppercase hex char (used for pretty printing) ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); } +/// Largest number of output bytes a single input byte can expand to when JSON escaped (a \u00XX sequence). +static constexpr size_t JSON_ESCAPE_MAX_EXPANSION = 6; + +/// Copy value into buf, escaping the characters that cannot appear raw inside a JSON string literal. +/// +/// Escapes " and \ along with the control characters below 0x20. Bytes >= 0x20 are copied verbatim, so text +/// containing valid UTF-8 survives intact. The result is always null terminated; anything that would not fit is +/// dropped rather than written partially. Returns buf so the call can be used directly as an argument. +/// +/// With short_control_escapes the five control characters JSON gives a short form get it (\n \r \t \b \f) and the +/// rest become \u00XX. Pass false to write every control character as \u00XX, which some consumers expect. +/// +/// To size buf so that no input is ever dropped, allow JSON_ESCAPE_MAX_EXPANSION bytes per input byte plus one for +/// the null terminator. +const char *json_escape_into_buffer(std::span buf, StringRef value, bool short_control_escapes = true); + /// Write int8 value to buffer without modulo operations. /// Buffer must have at least 4 bytes free. Returns pointer past last char written. inline char *int8_to_str(char *buf, int8_t val) { @@ -1625,6 +1641,12 @@ constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f /// Convert degrees Fahrenheit to degrees Celsius. constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; } +enum class TemperatureUnit : uint8_t { + CELSIUS = 0, + FAHRENHEIT = 1, + KELVIN = 2, +}; + ///@} /// @name Utilities diff --git a/esphome/core/lock_free_queue.h b/esphome/core/lock_free_queue.h index 316186ea54..ce54231137 100644 --- a/esphome/core/lock_free_queue.h +++ b/esphome/core/lock_free_queue.h @@ -1,5 +1,7 @@ #pragma once +#include "esphome/core/defines.h" + #include #include @@ -26,6 +28,54 @@ namespace esphome { +namespace lockfree_internal { +#ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS +// Platforms whose cores lack atomic read-modify-write instructions (currently +// the ARMv5TE BK72xx SoCs — no LDREX/STREX, no libatomic; other LibreTiny +// chips such as LN882x/RTL87xx are ARMv7-M and keep std::atomic). For this +// queue's SPSC contract RMW atomics are not needed: aligned 8/16-bit loads and +// stores are single instructions on these cores, so torn reads cannot occur, +// and on a single in-order core a compiler barrier supplies all the +// acquire/release ordering the algorithm requires. Each index has exactly one +// writer (head_: consumer, tail_: producer). The dropped counter's +// increment/exchange pair is not atomic here — a concurrent reset can lose +// counts — which is acceptable for a diagnostic drop counter. +#define ESPHOME_LFQ_COMPILER_BARRIER() __asm__ __volatile__("" ::: "memory") +template class PlainAtomic { + public: + PlainAtomic() = default; + constexpr PlainAtomic(T value) : value_(value) {} + T load(std::memory_order order = std::memory_order_seq_cst) const { + T value = value_; + if (order != std::memory_order_relaxed) + ESPHOME_LFQ_COMPILER_BARRIER(); // acquire: later reads may not hoist above this load + return value; + } + void store(T value, std::memory_order order = std::memory_order_seq_cst) { + if (order != std::memory_order_relaxed) + ESPHOME_LFQ_COMPILER_BARRIER(); // release: earlier writes may not sink below this store + value_ = value; + } + T fetch_add(T amount, std::memory_order /*order*/ = std::memory_order_seq_cst) { + T value = value_; + value_ = value + amount; + return value; + } + T exchange(T desired, std::memory_order /*order*/ = std::memory_order_seq_cst) { + T value = value_; + value_ = desired; + return value; + } + + private: + volatile T value_{0}; +}; +template using AtomicIndex = PlainAtomic; +#else +template using AtomicIndex = std::atomic; +#endif +} // namespace lockfree_internal + // Base lock-free queue without task notification template class LockFreeQueue { public: @@ -126,13 +176,13 @@ template class LockFreeQueue { protected: T *buffer_[SIZE]{}; // Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset) - std::atomic dropped_count_; // 65535 max - more than enough for drop tracking + lockfree_internal::AtomicIndex dropped_count_; // 65535 max - more than enough for drop tracking // Atomic: written by consumer (pop), read by producer (push) to check if full // Using uint8_t limits queue size to 255 elements but saves memory and ensures // atomic operations are efficient on all platforms - std::atomic head_; + lockfree_internal::AtomicIndex head_; // Atomic: written by producer (push), read by consumer (pop) to check if empty - std::atomic tail_; + lockfree_internal::AtomicIndex tail_; }; #ifdef USE_ESP32 diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 8449cba5e8..e9c5bf2c04 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -110,35 +110,16 @@ uint32_t Scheduler::calculate_interval_offset_(uint32_t delay) { return static_cast((static_cast(random_uint32()) * max_offset) >> 32); } -// Check if a retry was already cancelled in items_ or to_add_ -// Extracted from set_timer_common_ to reduce code size - retry path is cold and deprecated -// Remove before 2026.8.0 along with all retry code -bool Scheduler::is_retry_cancelled_locked_(Component *component, NameType name_type, const char *static_name, - uint32_t hash_or_id) { - for (auto *container : {&this->items_, &this->to_add_}) { - for (auto *item : *container) { - if (item != nullptr && this->is_item_removed_locked_(item) && - this->matches_item_locked_(item, component, name_type, static_name, hash_or_id, SchedulerItem::TIMEOUT, - /* match_retry= */ true, /* skip_removed= */ false)) { - return true; - } - } - } - return false; -} - // Common implementation for both timeout and interval // name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, NameType name_type, const char *static_name, uint32_t hash_or_id, uint32_t delay, - std::function &&func, bool is_retry, bool skip_cancel, - const LogString *source) { + std::function &&func, bool skip_cancel, const LogString *source) { if (delay == SCHEDULER_DONT_RUN) { // Still need to cancel existing timer if we have a name/id if (!skip_cancel) { LockGuard guard{this->lock_}; - this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, /* match_retry= */ false, - /* find_first= */ true); + this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, /* find_first= */ true); } return; } @@ -156,23 +137,9 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type delay = 1; } - // Take lock early to protect scheduler_item_pool_head_ access and retry-cancelled check + // Take lock early to protect scheduler_item_pool_head_ access LockGuard guard{this->lock_}; - // For retries, check if there's a cancelled timeout first - before allocating an item. - // Skip check for anonymous retries (STATIC_STRING with nullptr) - they can't be cancelled by name - // Skip check for defer (delay=0) - deferred retries bypass the cancellation check - if (is_retry && delay != 0 && (name_type != NameType::STATIC_STRING || static_name != nullptr) && - type == SchedulerItem::TIMEOUT && - this->is_retry_cancelled_locked_(component, name_type, static_name, hash_or_id)) { -#ifdef ESPHOME_DEBUG_SCHEDULER - SchedulerNameLog skip_name_log; - ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", - skip_name_log.format(name_type, static_name, hash_or_id)); -#endif - return; - } - // Create and populate the scheduler item SchedulerItem *item = this->get_item_from_pool_locked_(); // SELF_POINTER items store the source name (owning script) in the union slot instead of a component. @@ -192,7 +159,6 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type new (&item->callback) std::function(std::move(func)); // Reset remove flag - recycled items may have been cancelled (remove=true) in previous use this->set_item_removed_(item, false); - item->is_retry = is_retry; // Determine target container: defer_queue_ for deferred items, to_add_ for everything else. // Using a pointer lets both paths share the cancel + push_back epilogue. @@ -234,8 +200,7 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type // Common epilogue: atomic cancel-and-add (unless skip_cancel is true or anonymous) // Anonymous items (STATIC_STRING with nullptr) can never match anything, so skip the scan. if (!skip_cancel && (name_type != NameType::STATIC_STRING || static_name != nullptr)) { - this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, /* match_retry= */ false, - /* find_first= */ true); + this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, /* find_first= */ true); } target->push_back(item); if (target == &this->to_add_) { @@ -301,125 +266,6 @@ bool HOT Scheduler::cancel_interval(const void *self) { SchedulerItem::INTERVAL); } -// Suppress deprecation warnings for RetryResult usage in the still-present (but deprecated) retry implementation. -// Remove before 2026.8.0 along with all retry code. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -struct RetryArgs { - // Ordered to minimize padding on 32-bit systems - std::function func; - Component *component; - Scheduler *scheduler; - // Union for name storage - only one is used based on name_type - union { - const char *static_name; // For STATIC_STRING - uint32_t hash_or_id; // For HASHED_STRING or NUMERIC_ID - } name_; - uint32_t current_interval; - float backoff_increase_factor; - Scheduler::NameType name_type; // Discriminator for name_ union - uint8_t retry_countdown; -}; - -void retry_handler(const std::shared_ptr &args) { - RetryResult const retry_result = args->func(--args->retry_countdown); - if (retry_result == RetryResult::DONE || args->retry_countdown <= 0) - return; - // second execution of `func` happens after `initial_wait_time` - // args->name_ is owned by the shared_ptr - // which is captured in the lambda and outlives the SchedulerItem - const char *static_name = (args->name_type == Scheduler::NameType::STATIC_STRING) ? args->name_.static_name : nullptr; - uint32_t hash_or_id = (args->name_type != Scheduler::NameType::STATIC_STRING) ? args->name_.hash_or_id : 0; - args->scheduler->set_timer_common_( - args->component, Scheduler::SchedulerItem::TIMEOUT, args->name_type, static_name, hash_or_id, - args->current_interval, [args]() { retry_handler(args); }, - /* is_retry= */ true); - // backoff_increase_factor applied to third & later executions - args->current_interval *= args->backoff_increase_factor; -} - -void HOT Scheduler::set_retry_common_(Component *component, NameType name_type, const char *static_name, - uint32_t hash_or_id, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor) { - this->cancel_retry_(component, name_type, static_name, hash_or_id); - - if (initial_wait_time == SCHEDULER_DONT_RUN) - return; - -#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE - { - SchedulerNameLog name_log; - ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)", - name_log.format(name_type, static_name, hash_or_id), initial_wait_time, max_attempts, - backoff_increase_factor); - } -#endif - - if (backoff_increase_factor < 0.0001f) { - ESP_LOGE(TAG, "set_retry: backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, - (name_type == NameType::STATIC_STRING && static_name) ? static_name : ""); - backoff_increase_factor = 1; - } - - auto args = std::make_shared(); - args->func = std::move(func); - args->component = component; - args->scheduler = this; - args->name_type = name_type; - if (name_type == NameType::STATIC_STRING) { - args->name_.static_name = static_name; - } else { - args->name_.hash_or_id = hash_or_id; - } - args->current_interval = initial_wait_time; - args->backoff_increase_factor = backoff_increase_factor; - args->retry_countdown = max_attempts; - - // First execution of `func` immediately - use set_timer_common_ with is_retry=true - this->set_timer_common_( - component, SchedulerItem::TIMEOUT, name_type, static_name, hash_or_id, 0, [args]() { retry_handler(args); }, - /* is_retry= */ true); -} - -void HOT Scheduler::set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor) { - this->set_retry_common_(component, NameType::STATIC_STRING, name, 0, initial_wait_time, max_attempts, std::move(func), - backoff_increase_factor); -} - -bool HOT Scheduler::cancel_retry_(Component *component, NameType name_type, const char *static_name, - uint32_t hash_or_id) { - return this->cancel_item_(component, name_type, static_name, hash_or_id, SchedulerItem::TIMEOUT, - /* match_retry= */ true); -} -bool HOT Scheduler::cancel_retry(Component *component, const char *name) { - return this->cancel_retry_(component, NameType::STATIC_STRING, name, 0); -} - -void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, - uint8_t max_attempts, std::function func, - float backoff_increase_factor) { - this->set_retry_common_(component, NameType::HASHED_STRING, nullptr, fnv1a_hash(name), initial_wait_time, - max_attempts, std::move(func), backoff_increase_factor); -} - -bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) { - return this->cancel_retry_(component, NameType::HASHED_STRING, nullptr, fnv1a_hash(name)); -} - -void HOT Scheduler::set_retry(Component *component, uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor) { - this->set_retry_common_(component, NameType::NUMERIC_ID, nullptr, id, initial_wait_time, max_attempts, - std::move(func), backoff_increase_factor); -} - -bool HOT Scheduler::cancel_retry(Component *component, uint32_t id) { - return this->cancel_retry_(component, NameType::NUMERIC_ID, nullptr, id); -} - -#pragma GCC diagnostic pop // End suppression of deprecated RetryResult warnings - optional HOT Scheduler::next_schedule_in(uint32_t now) { // IMPORTANT: This method should only be called from the main thread (loop task). // Accesses items_[0] and the fast-path empty checks without holding a lock, which @@ -806,11 +652,11 @@ uint32_t HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) { // Common implementation for cancel operations - handles locking bool HOT Scheduler::cancel_item_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, - SchedulerItem::Type type, bool match_retry) { + SchedulerItem::Type type) { LockGuard guard{this->lock_}; // Public cancel path uses default find_first=false to cancel ALL matches because // DelayAction parallel mode (skip_cancel=true) can create multiple items with the same key. - return this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type, match_retry); + return this->cancel_item_locked_(component, name_type, static_name, hash_or_id, type); } // Helper to cancel matching items - must be called with lock held. @@ -822,11 +668,10 @@ bool HOT Scheduler::cancel_item_(Component *component, NameType name_type, const size_t Scheduler::mark_matching_items_removed_slow_locked_(std::vector &container, Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, - SchedulerItem::Type type, bool match_retry, - bool find_first) { + SchedulerItem::Type type, bool find_first) { size_t count = 0; for (auto *item : container) { - if (this->matches_item_locked_(item, component, name_type, static_name, hash_or_id, type, match_retry)) { + if (this->matches_item_locked_(item, component, name_type, static_name, hash_or_id, type)) { this->set_item_removed_(item, true); if (find_first) return 1; @@ -837,8 +682,7 @@ size_t Scheduler::mark_matching_items_removed_slow_locked_(std::vectormark_matching_items_removed_locked_(this->defer_queue_, component, name_type, static_name, - hash_or_id, type, match_retry, find_first); + hash_or_id, type, find_first); if (find_first && total_cancelled > 0) return true; } @@ -863,7 +707,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type // Only the main loop in call() should recycle items after execution completes. { size_t heap_cancelled = this->mark_matching_items_removed_locked_(this->items_, component, name_type, static_name, - hash_or_id, type, match_retry, find_first); + hash_or_id, type, find_first); total_cancelled += heap_cancelled; this->to_remove_add_locked_(heap_cancelled); if (find_first && total_cancelled > 0) @@ -872,7 +716,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, NameType name_type // Cancel items in to_add_ total_cancelled += this->mark_matching_items_removed_locked_(this->to_add_, component, name_type, static_name, - hash_or_id, type, match_retry, find_first); + hash_or_id, type, find_first); return total_cancelled > 0; } diff --git a/esphome/core/scheduler.h b/esphome/core/scheduler.h index c7743e5b2a..8ef3499a11 100644 --- a/esphome/core/scheduler.h +++ b/esphome/core/scheduler.h @@ -16,14 +16,8 @@ namespace esphome { class Component; -struct RetryArgs; - -// Forward declaration of retry_handler - needs to be non-static for friend declaration -void retry_handler(const std::shared_ptr &args); class Scheduler { - // Allow retry_handler to access protected members for internal retry mechanism - friend void ::esphome::retry_handler(const std::shared_ptr &args); // Allow DelayAction to call set_timer_common_ with skip_cancel=true for parallel script delays. // This is needed to fix issue #10264 where parallel scripts with delays interfere with each other. // We use friend instead of a public API because skip_cancel is dangerous - it can cause delays @@ -79,32 +73,6 @@ class Scheduler { SchedulerItem::INTERVAL); } - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(Component *component, const std::string &name, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor = 1.0f); - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor = 1.0f); - // Remove before 2026.8.0 - ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", - "2026.2.0") - void set_retry(Component *component, uint32_t id, uint32_t initial_wait_time, uint8_t max_attempts, - std::function func, float backoff_increase_factor = 1.0f); - - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(Component *component, const std::string &name); - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(Component *component, const char *name); - // Remove before 2026.8.0 - ESPDEPRECATED("cancel_retry is deprecated and will be removed in 2026.8.0.", "2026.2.0") - bool cancel_retry(Component *component, uint32_t id); - /// Get 64-bit millisecond timestamp (handles 32-bit millis() rollover) uint64_t millis_64() { return esphome::millis_64(); } @@ -202,19 +170,17 @@ class Scheduler { // std::atomic inlines correctly on all platforms. std::atomic remove{0}; - // Bit-packed fields (5 bits used, 3 bits padding in 1 byte) + // Bit-packed fields (4 bits used, 4 bits padding in 1 byte) enum Type : uint8_t { TIMEOUT, INTERVAL } type : 1; NameType name_type_ : 3; // Discriminator for name_ union (0–4, see NameType enum) - bool is_retry : 1; // True if this is a retry timeout - // 3 bits padding + // 4 bits padding #else // Single-threaded or multi-threaded without atomics: can pack all fields together - // Bit-packed fields (6 bits used, 2 bits padding in 1 byte) + // Bit-packed fields (5 bits used, 3 bits padding in 1 byte) enum Type : uint8_t { TIMEOUT, INTERVAL } type : 1; bool remove : 1; NameType name_type_ : 3; // Discriminator for name_ union (0–4, see NameType enum) - bool is_retry : 1; // True if this is a retry timeout - // 2 bits padding + // 3 bits padding #endif // Constructor @@ -226,13 +192,11 @@ class Scheduler { #ifdef ESPHOME_THREAD_MULTI_ATOMICS // remove is initialized in the member declaration type(TIMEOUT), - name_type_(NameType::STATIC_STRING), - is_retry(false) { + name_type_(NameType::STATIC_STRING) { #else type(TIMEOUT), remove(false), - name_type_(NameType::STATIC_STRING), - is_retry(false) { + name_type_(NameType::STATIC_STRING) { #endif name_.static_name = nullptr; } @@ -306,19 +270,8 @@ class Scheduler { // name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id // `source` is stored (in the union slot) only for SELF_POINTER items; ignored otherwise. void set_timer_common_(Component *component, SchedulerItem::Type type, NameType name_type, const char *static_name, - uint32_t hash_or_id, uint32_t delay, std::function &&func, bool is_retry = false, - bool skip_cancel = false, const LogString *source = nullptr); - - // Common implementation for retry - Remove before 2026.8.0 - // name_type determines storage type: STATIC_STRING uses static_name, others use hash_or_id -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - void set_retry_common_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, - uint32_t initial_wait_time, uint8_t max_attempts, std::function func, - float backoff_increase_factor); -#pragma GCC diagnostic pop - // Common implementation for cancel_retry - bool cancel_retry_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id); + uint32_t hash_or_id, uint32_t delay, std::function &&func, bool skip_cancel = false, + const LogString *source = nullptr); // Extend a 32-bit millis() value to 64-bit. Use when the caller already has a fresh now. // On platforms with native 64-bit time (ESP32, Host, Zephyr, RP2040 — see @@ -374,11 +327,11 @@ class Scheduler { // mode where skip_cancel=true allows multiple items with the same key). // name_type determines matching: STATIC_STRING uses static_name, others use hash_or_id bool cancel_item_locked_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, - SchedulerItem::Type type, bool match_retry = false, bool find_first = false); + SchedulerItem::Type type, bool find_first = false); // Common implementation for cancel operations - handles locking bool cancel_item_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, - SchedulerItem::Type type, bool match_retry = false); + SchedulerItem::Type type); // Helper to check if two static string names match inline bool HOT names_match_static_(const char *name1, const char *name2) const { @@ -394,7 +347,7 @@ class Scheduler { // IMPORTANT: Must be called with scheduler lock held inline bool HOT matches_item_locked_(SchedulerItem *item, Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id, SchedulerItem::Type type, - bool match_retry, bool skip_removed = true) const { + bool skip_removed = true) const { // THREAD SAFETY: Check for nullptr first to prevent LoadProhibited crashes. On multi-threaded // platforms, items can be nulled in defer_queue_ during processing. // Fixes: https://github.com/esphome/esphome/issues/11940 @@ -403,7 +356,7 @@ class Scheduler { // get_component() is nullptr for SELF_POINTER items (their cancels pass nullptr too), so they // match by the `this` key alone. if (item->get_component() != component || item->type != type || - (skip_removed && this->is_item_removed_locked_(item)) || (match_retry && !item->is_retry)) { + (skip_removed && this->is_item_removed_locked_(item))) { return false; } // Name type must match @@ -448,13 +401,6 @@ class Scheduler { // IMPORTANT: Must not be inlined - called only for intervals, keeping it out of the hot path saves flash. uint32_t __attribute__((noinline)) calculate_interval_offset_(uint32_t delay); - // Helper to check if a retry was already cancelled - extracted to reduce code size of set_timer_common_ - // Remove before 2026.8.0 along with all retry code. - // IMPORTANT: Must not be inlined - retry path is cold and deprecated. - // IMPORTANT: Caller must hold the scheduler lock before calling this function. - bool __attribute__((noinline)) - is_retry_cancelled_locked_(Component *component, NameType name_type, const char *static_name, uint32_t hash_or_id); - #ifdef ESPHOME_DEBUG_SCHEDULER // Helper for debug logging in set_timer_common_ - extracted to reduce code size void debug_log_timer_(const SchedulerItem *item, NameType name_type, const char *static_name, uint32_t hash_or_id, @@ -556,19 +502,21 @@ class Scheduler { // Inlined: the fast path (empty container) avoids calling the out-of-line scan. inline size_t HOT mark_matching_items_removed_locked_(std::vector &container, Component *component, NameType name_type, const char *static_name, - uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry, + uint32_t hash_or_id, SchedulerItem::Type type, bool find_first = false) { if (container.empty()) return 0; return this->mark_matching_items_removed_slow_locked_(container, component, name_type, static_name, hash_or_id, - type, match_retry, find_first); + type, find_first); } // Out-of-line slow path for mark_matching_items_removed_locked_ when container is non-empty. // IMPORTANT: Must be called with scheduler lock held - __attribute__((noinline)) size_t mark_matching_items_removed_slow_locked_( - std::vector &container, Component *component, NameType name_type, const char *static_name, - uint32_t hash_or_id, SchedulerItem::Type type, bool match_retry, bool find_first); + __attribute__((noinline)) size_t mark_matching_items_removed_slow_locked_(std::vector &container, + Component *component, NameType name_type, + const char *static_name, + uint32_t hash_or_id, + SchedulerItem::Type type, bool find_first); Mutex lock_; std::vector items_; diff --git a/esphome/core/wake/wake_rp2.cpp b/esphome/core/wake/wake_rp2.cpp index 101c87c818..ac1deba726 100644 --- a/esphome/core/wake/wake_rp2.cpp +++ b/esphome/core/wake/wake_rp2.cpp @@ -20,7 +20,7 @@ volatile bool g_main_loop_woke = false; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static volatile bool s_delay_expired = false; -static int64_t alarm_callback_(alarm_id_t id, void *user_data) { +static int64_t alarm_callback(alarm_id_t id, void *user_data) { (void) id; (void) user_data; s_delay_expired = true; @@ -43,7 +43,7 @@ void wakeable_delay(uint32_t ms) { return; } s_delay_expired = false; - alarm_id_t alarm = add_alarm_in_ms(ms, alarm_callback_, nullptr, true); + alarm_id_t alarm = add_alarm_in_ms(ms, alarm_callback, nullptr, true); if (alarm <= 0) { delay(ms); return; diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index b035e28a7a..b2338e5bc1 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -151,6 +151,17 @@ async def gpio_pin_expression(conf): return await coroutine(pins.PIN_SCHEMA_REGISTRY[CORE.target_platform][0])(conf) +def set_setup_priority(var, priority: float) -> None: + """Emit a setup-priority override for the given component. + + Pairs the ``set_setup_priority()`` call with the ``USE_SETUP_PRIORITY_OVERRIDE`` + define that compiles in the core override support, so callers cannot emit one + without the other. + """ + add_define("USE_SETUP_PRIORITY_OVERRIDE") + add(var.set_setup_priority(priority)) + + async def register_component(var, config): """Register the given obj as a component. @@ -168,8 +179,7 @@ async def register_component(var, config): ) CORE.component_ids.remove(id_) if CONF_SETUP_PRIORITY in config: - add_define("USE_SETUP_PRIORITY_OVERRIDE") - add(var.set_setup_priority(config[CONF_SETUP_PRIORITY])) + set_setup_priority(var, config[CONF_SETUP_PRIORITY]) if CONF_UPDATE_INTERVAL in config: add(var.set_update_interval(config[CONF_UPDATE_INTERVAL])) diff --git a/esphome/espidf/component.py b/esphome/espidf/component.py index e9ec170a5e..cad5bbf665 100644 --- a/esphome/espidf/component.py +++ b/esphome/espidf/component.py @@ -53,9 +53,10 @@ def _apply_extra_script(component: IDFComponent) -> None: if not script_path.is_relative_to(library_root) or not script_path.is_file(): return from esphome.components.esp32 import get_esp32_variant + from esphome.components.esp32.const import variant_to_idf_target from esphome.espidf.extra_script import captured_as_build_flags, run_extra_script - idf_target = get_esp32_variant().lower().replace("-", "") + idf_target = variant_to_idf_target(get_esp32_variant()) result = run_extra_script( script_path, library_dir=component.path, idf_target=idf_target ) @@ -83,11 +84,23 @@ def generate_cmakelists_txt(component: IDFComponent) -> str: Returns: str: The complete CMakeLists.txt content as a string """ + # Late import: this module loads with the esp32 platform on every + # validate/compile, but shlex is only needed when generating component + # CMakeLists. + import shlex def escape_entry(p: PathType) -> str: # In CMakeLists.txt, backslashes need to be escaped return f'"{str(p)}"'.replace("\\", "\\\\") + def escape_path(p: PathType) -> str: + # CMake uses forward slashes for paths on every platform and treats + # backslashes as escape characters. On Windows os.path.relpath yields + # backslash paths, which break CMake's list re-parsing (e.g. "\b" in + # "src\backend" is an invalid character escape). Emit forward slashes, + # which Windows accepts too, so the generated CMakeLists is portable. + return f'"{str(p).replace(os.sep, "/")}"' + # Extract the values build_src_dir = component.data.get("build", {}).get("srcDir", None) if not build_src_dir: @@ -105,6 +118,23 @@ def generate_cmakelists_txt(component: IDFComponent) -> str: build_flags = ensure_list( component.data.get("build", {}).get("flags", DEFAULT_BUILD_FLAGS) ) + # PlatformIO shell-lexes each build.flags entry, so one entry can carry a + # flag and its argument (e.g. "-include cp_custom_alloc.h"). Split the + # same way; emitting such an entry as a single quoted compile option + # hands the compiler one argv with an embedded space. + build_flags = [token for entry in build_flags for token in shlex.split(entry)] + # Re-glue bare -I/-L/-l tokens to their argument ("-I foo" -> "-Ifoo") so + # the prefix classifiers below still route them to INCLUDE_DIRS and the + # link handling. + tokens, build_flags = build_flags, [] + i = 0 + while i < len(tokens): + if tokens[i] in ("-I", "-L", "-l") and i + 1 < len(tokens): + build_flags.append(tokens[i] + tokens[i + 1]) + i += 2 + else: + build_flags.append(tokens[i]) + i += 1 # List all sources files build_src_files = collect_filtered_files( @@ -152,10 +182,10 @@ def generate_cmakelists_txt(component: IDFComponent) -> str: # Generate the component content = "idf_component_register(\n" if build_src_files: - str_srcs = " ".join([escape_entry(p) for p in sorted(build_src_files)]) + str_srcs = " ".join([escape_path(p) for p in sorted(build_src_files)]) content += f" SRCS {str_srcs}\n" if build_include_dirs: - str_include_dirs = " ".join([escape_entry(p) for p in build_include_dirs]) + str_include_dirs = " ".join([escape_path(p) for p in build_include_dirs]) content += f" INCLUDE_DIRS {str_include_dirs}\n" # Project-managed and built-in component lists are set per-project # via idf_build_set_property in the top-level CMakeLists; expanded @@ -190,7 +220,7 @@ def generate_cmakelists_txt(component: IDFComponent) -> str: if link_directories: content += "target_link_directories(${COMPONENT_LIB} INTERFACE\n" for link_directory in link_directories: - str_build_flag = escape_entry(link_directory) + str_build_flag = escape_path(link_directory) content += f" {str_build_flag}\n" content += ")\n" diff --git a/esphome/espidf/extra_script.py b/esphome/espidf/extra_script.py index 4d06fb842a..487fef7cc1 100644 --- a/esphome/espidf/extra_script.py +++ b/esphome/espidf/extra_script.py @@ -107,7 +107,7 @@ def run_extra_script( script shouldn't block the build. """ env = _FakeSConsEnv(board_mcu=idf_target, pio_env=f"esphome_{idf_target}") - code = compile(script_path.read_text(), str(script_path), "exec") + code = compile(script_path.read_text(encoding="utf-8"), str(script_path), "exec") old_cwd = Path.cwd() try: os.chdir(library_dir) diff --git a/esphome/espidf/framework.py b/esphome/espidf/framework.py index 810a63476f..0ca7a9d14b 100644 --- a/esphome/espidf/framework.py +++ b/esphome/espidf/framework.py @@ -1,5 +1,7 @@ """ESP-IDF framework tools for ESPHome.""" +from collections.abc import Callable +from ctypes.util import find_library import json import logging import os @@ -7,7 +9,7 @@ from pathlib import Path import platform import re import shutil -import tempfile +from typing import Any, NoReturn import platformdirs @@ -18,6 +20,7 @@ from esphome.framework_helpers import ( archive_extract_all, create_venv, download_from_mirrors, + download_with_resume, get_python_env_executable_path, get_system_python_path, rmdir, @@ -46,6 +49,10 @@ STAMP_SCHEMA_VERSION = "0" ESPHOME_IDF_DEFAULT_TARGETS = str_to_lst_of_str( os.environ.get("ESPHOME_IDF_DEFAULT_TARGETS", "all") ) +# An explicitly set ESPHOME_IDF_DEFAULT_TARGETS overrides the per-variant +# targets a caller requests, so a builder image can still pre-warm every +# target with one env var. +_IDF_DEFAULT_TARGETS_EXPLICIT = bool(os.environ.get("ESPHOME_IDF_DEFAULT_TARGETS")) ESPHOME_IDF_DEFAULT_TOOLS = str_to_lst_of_str( os.environ.get("ESPHOME_IDF_DEFAULT_TOOLS", "cmake;ninja") @@ -63,7 +70,7 @@ ESPHOME_IDF_FRAMEWORK_MIRRORS = str_to_lst_of_str( os.environ.get("ESPHOME_IDF_FRAMEWORK_MIRRORS") or [ "https://github.com/esphome-libs/esp-idf/releases/download/v{VERSION}/esp-idf-v{VERSION}.tar.xz", - "https://github.com/esphome-libs/esp-idf/releases/download/v{MAJOR}.{MINOR}{EXTRA}/esp-idf-v{MAJOR}.{MINOR}{EXTRA}.tar.xz", + "https://github.com/esphome-libs/esp-idf/releases/download/v{SHORT_VERSION}/esp-idf-v{SHORT_VERSION}.tar.xz", ] ) @@ -196,7 +203,35 @@ def _get_python_env_path(version: str) -> Path: return get_idf_tools_path() / "penvs" / f"{version}" -def _check_stamp(file: PathType, data: dict[str, str]) -> bool: +def _read_stamp(file: PathType) -> dict | None: + """Return a stamp file's dict contents, or None if missing or invalid. + + A missing stamp is the normal first-install case and stays silent; the + other branches indicate a real fault that forces a full reinstall on + every build, so they warn. + """ + try: + with Path(file).open(encoding="utf-8") as f: + data = json.load(f) + except FileNotFoundError: + return None + except json.JSONDecodeError as e: + _LOGGER.warning("Ignoring corrupt stamp file %s: %s", file, e) + return None + except OSError as e: + _LOGGER.warning("Could not read stamp file %s: %s", file, e) + return None + if not isinstance(data, dict): + _LOGGER.warning( + "Ignoring stamp file %s with unexpected type %s", + file, + type(data).__name__, + ) + return None + return data + + +def _check_stamp(file: PathType, data: dict[str, Any]) -> bool: """ Check if a stamp file contains the expected data. @@ -207,17 +242,43 @@ def _check_stamp(file: PathType, data: dict[str, str]) -> bool: Returns: True if file exists and contains expected data, False otherwise """ - if not Path(file).is_file(): + return _read_stamp(file) == data + + +def _stamps_match_except_targets(stored: dict, requested: dict) -> bool: + """Whether two stamps agree on every field other than ``targets``. + + Compares whole dicts (minus ``targets``) rather than named keys so any + stamp field added later participates in invalidation by default instead + of being silently ignored. + """ + + def _strip(stamp: dict) -> dict: + return {k: v for k, v in stamp.items() if k != "targets"} + + return _strip(stored) == _strip(requested) + + +def _stamp_covers(stored: dict | None, requested: dict) -> bool: + """Return True if a stored framework stamp already covers this request. + + Every field except ``targets`` must match exactly. ``targets`` may be a + superset of the requested ones: ``idf_tools.py install`` accumulates + targets in idf-env.json across runs, so a framework installed for more + targets than this build needs is still valid. A stored ``all`` covers + every target. + """ + if stored is None: return False - - try: - with Path(file).open(encoding="utf-8") as f: - return json.load(f) == data - except (json.JSONDecodeError, OSError): + if not _stamps_match_except_targets(stored, requested): return False + stored_targets = stored.get("targets") + if not isinstance(stored_targets, list): + return False + return "all" in stored_targets or set(requested["targets"]) <= set(stored_targets) -def _write_stamp(file: PathType, data: dict[str, str]): +def _write_stamp(file: PathType, data: dict[str, Any]): """ Write data to a stamp file in JSON format. @@ -229,6 +290,40 @@ def _write_stamp(file: PathType, data: dict[str, str]): json.dump(data, fp) +def _run_idf_tools_script( + idf_framework_root: PathType, + script_name: str, + msg: str, + args: list[str] | None = None, + env: dict[str, str] | None = None, +) -> tuple[bool, str | None, str | None]: + """Run one of the sibling idf_tools-backed helper scripts. + + The script is executed with the framework's ``tools`` directory on + PYTHONPATH so it imports the framework's own ``idf_tools`` module. + """ + cmd = [ + get_system_python_path(), + str(_SCRIPTS_DIR / script_name), + str(idf_framework_root), + *(args or []), + ] + return run_command( + cmd, + msg=msg, + env=(env or os.environ) + | {"PYTHONPATH": str(Path(idf_framework_root) / "tools")}, + ) + + +def _raise_script_failure(what: str, root: PathType, stderr: str | None) -> NoReturn: + """Raise RuntimeError for a failed helper script, appending stderr detail.""" + detail = (stderr or "").strip() + raise RuntimeError( + f"Can't get {what} of {root}" + (f": {detail}" if detail else "") + ) + + def _get_idf_version( idf_framework_root: PathType, env: dict[str, str] | None = None ) -> str: @@ -246,26 +341,13 @@ def _get_idf_version( RuntimeError: If ESP-IDF version cannot be determined """ - cmd = [ - get_system_python_path(), - str(_SCRIPTS_DIR / "get_idf_version.py"), - str(idf_framework_root), - ] - - success, stdout, stderr = run_command( - cmd, - msg="ESP-IDF version", - env=(env or os.environ) - | {"PYTHONPATH": str(Path(idf_framework_root) / "tools")}, + success, stdout, stderr = _run_idf_tools_script( + idf_framework_root, "get_idf_version.py", "ESP-IDF version", env=env ) if stdout: stdout = stdout.strip() if not success or not stdout: - detail = (stderr or "").strip() - raise RuntimeError( - f"Can't get ESP-IDF version of {idf_framework_root}" - + (f": {detail}" if detail else "") - ) + _raise_script_failure("ESP-IDF version", idf_framework_root, stderr) return stdout @@ -286,24 +368,11 @@ def _get_idf_tool_paths( RuntimeError: If ESP-IDF tool paths cannot be determined """ - cmd = [ - get_system_python_path(), - str(_SCRIPTS_DIR / "get_idf_tool_paths.py"), - str(idf_framework_root), - ] - - success, stdout, stderr = run_command( - cmd, - msg="ESP-IDF tool paths", - env=(env or os.environ) - | {"PYTHONPATH": str(Path(idf_framework_root) / "tools")}, + success, stdout, stderr = _run_idf_tools_script( + idf_framework_root, "get_idf_tool_paths.py", "ESP-IDF tool paths", env=env ) if not success or not stdout: - detail = (stderr or "").strip() - raise RuntimeError( - f"Can't get ESP-IDF tool paths of {idf_framework_root}" - + (f": {detail}" if detail else "") - ) + _raise_script_failure("ESP-IDF tool paths", idf_framework_root, stderr) # Extract json values try: @@ -386,9 +455,10 @@ def _clone_idf_with_submodules( handles branches, tags, and SHAs uniformly (mirrors the approach in ``esphome.git.clone_or_update``). """ - from esphome.git import run_git_command + from esphome.git import run_git_command, update_submodules - _LOGGER.info("Cloning ESP-IDF from %s%s", git_url, f"@{ref}" if ref else "") + key = f"{git_url}@{ref}" if ref else git_url + _LOGGER.info("Cloning ESP-IDF from %s", key) run_git_command(["git", "clone", "--depth=1", "--", git_url, str(framework_path)]) if ref: run_git_command( @@ -399,25 +469,14 @@ def _clone_idf_with_submodules( ["git", "reset", "--hard", "FETCH_HEAD"], git_dir=framework_path, ) - run_git_command( - [ - "git", - "submodule", - "update", - "--init", - "--recursive", - "--depth=1", - ], - git_dir=framework_path, - ) + update_submodules(framework_path, key) - # Sanity-check the resulting tree. run_git_command only raises when - # stderr is non-empty, so a clone that silently produces no working - # tree would otherwise be marked extracted and stuck until - # ``esphome clean``. + # Sanity-check the resulting tree: a clone can exit 0 yet produce no + # usable ESP-IDF checkout, which would otherwise be marked extracted and + # stuck until ``esphome clean``. if not (framework_path / "tools" / "idf_tools.py").is_file(): raise RuntimeError( - f"Clone of {git_url} produced no usable ESP-IDF tree at {framework_path}" + f"Clone of {key} produced no usable ESP-IDF tree at {framework_path}" ) @@ -466,17 +525,21 @@ _NINJA_ARM64_BACKPORT: dict[str, dict[str, str | int]] = { } -def _patch_tools_json_for_linux_arm64(framework_path: Path) -> None: - """Inject ninja linux-arm64 entries into the framework's tools.json on aarch64. +def _patch_tools_json( + framework_path: Path, + apply_patch: Callable[[dict], bool], + patched_log: str, +) -> None: + """Apply an in-place fixup to the framework's tools/tools.json. - Idempotent: a tools.json that already has the entry, or a host that - isn't aarch64, is a no-op. Applied unconditionally on every install - check so a build dir extracted before the backport got fixed up - without forcing a clean. + Shared plumbing for the tools.json patches below: a missing file is a + no-op, an unparseable file logs a warning and skips, and when + ``apply_patch`` reports a change the file is written back atomically. + ``patched_log`` is the info log line, with a single ``%s`` placeholder + for the tools.json path. Patches are idempotent and applied on every + install check, so an already-extracted framework picks them up on the + next build without forcing a clean. """ - if platform.machine() != "aarch64": - return - tools_json = framework_path / "tools" / "tools.json" if not tools_json.is_file(): return @@ -484,37 +547,206 @@ def _patch_tools_json_for_linux_arm64(framework_path: Path) -> None: try: with tools_json.open(encoding="utf-8") as f: data = json.load(f) - except (json.JSONDecodeError, OSError) as e: + # apply_patch also raises inside the guard: a tools.json that is + # valid JSON but not the expected shape (e.g. a top-level list) + # must skip the patch, not crash the install check this patch is + # meant to recover. + changed = apply_patch(data) + except (json.JSONDecodeError, OSError, AttributeError, TypeError, KeyError) as e: _LOGGER.warning( - "Could not parse %s for linux-arm64 backport (%s); " - "skipping. A clean reinstall of the framework directory " - "may be needed.", + "Could not apply tools.json patch to %s (%s); skipping. A clean " + "reinstall of the framework directory may be needed.", tools_json, e, ) return - changed = False - for tool in data.get("tools", []): - if tool.get("name") != "ninja": - continue - for ver in tool.get("versions", []): - entry = _NINJA_ARM64_BACKPORT.get(ver.get("name")) - if entry is None or ver.get("linux-arm64"): - continue - ver["linux-arm64"] = entry - changed = True - if changed: # write_file_if_changed stages a tempfile in the destination dir # and atomically replaces — safe against mid-write interruption # and concurrent invocations. write_file_if_changed(tools_json, json.dumps(data, indent=2) + "\n") - _LOGGER.info( - "Patched %s to add ninja linux-arm64 download " - "(espressif/esp-idf#18272 backport).", - tools_json, + _LOGGER.info(patched_log, tools_json) + + +def _patch_tools_json_for_linux_arm64(framework_path: Path) -> None: + """Inject ninja linux-arm64 entries into the framework's tools.json on aarch64. + + A tools.json that already has the entry, or a host that isn't aarch64, + is a no-op. + """ + if platform.machine() != "aarch64": + return + + def apply_patch(data: dict) -> bool: + changed = False + for tool in data.get("tools", []): + if tool.get("name") != "ninja": + continue + for ver in tool.get("versions", []): + entry = _NINJA_ARM64_BACKPORT.get(ver.get("name")) + if entry is None or ver.get("linux-arm64"): + continue + ver["linux-arm64"] = entry + changed = True + return changed + + _patch_tools_json( + framework_path, + apply_patch, + "Patched %s to add ninja linux-arm64 download " + "(espressif/esp-idf#18272 backport).", + ) + + +# Tools marked ``install: always`` in tools.json that no ESPHome build ever +# runs. openocd-esp32 is a JTAG debug server (its post-install check also +# fails outright on systems without libusb-1.0, #17685). The gdb bundles are +# debuggers used only by ``idf.py gdb``/``idf.py monitor`` flows ESPHome never +# invokes; stack decoding uses addr2line from the compiler toolchains instead. +# esp32ulp-elf is the ULP coprocessor toolchain, and ESPHome excludes the IDF +# ``ulp`` component from every build. esp-rom-elfs stays required: the cmake +# gdbinit generation reads ESP_ROM_ELF_DIR during every configure and warns +# when it is missing. +_UNUSED_IDF_TOOLS: tuple[str, ...] = ( + "esp32ulp-elf", + "openocd-esp32", + "riscv32-esp-elf-gdb", + "xtensa-esp-elf-gdb", +) + +# tools.json also lists riscv32-esp-elf as supported on the xtensa chips +# because the S2/S3 ULP coprocessor is a RISC-V core, so installing for an +# S2/S3 target pulls in the whole riscv compiler (~290MB download, 2GB disk) +# just for ULP programs — which ESPHome never builds (the IDF ``ulp`` +# component is excluded by default; a user who re-enables it via +# ``include_builtin_idf_components: [ulp]`` on an S2/S3 and hits a missing +# riscv compiler can set ESPHOME_IDF_DEFAULT_TARGETS=all to install it). +# Removing the xtensa chips from its supported targets keeps it out of +# xtensa-only installs; building a RISC-V variant still installs it. Add any +# future Xtensa chip here; a missing entry only costs the download, while a +# wrongly listed RISC-V chip would strip its own compiler. +_XTENSA_TARGETS: tuple[str, ...] = ("esp32", "esp32s2", "esp32s3") + + +def _patch_tools_json_demote_unused_tools(framework_path: Path) -> None: + """Demote tools ESPHome never runs from ``install: always`` to ``on_request``. + + ``idf_tools.py install required`` downloads every tool marked ``always`` + in tools.json and validates each one after extraction by running its + version command. Demoting the tools in ``_UNUSED_IDF_TOOLS`` drops them + from the ``required`` set: they are no longer downloaded or validated, + and the tool-path export treats a missing ``on_request`` tool as fine. + Besides the download and disk savings, this makes the openocd libusb + validation failure (#17685) impossible; because this runs on every + install check, an install stuck in that failing state (which never wrote + its stamp file) heals on the next build without a clean. A user who + wants one of these tools can still name it explicitly in + ESPHOME_IDF_DEFAULT_TOOLS; explicit names bypass install-type filtering. + + Also removes the xtensa chips from riscv32-esp-elf's supported targets + (see ``_XTENSA_TARGETS``) so xtensa-only installs don't pull in the + RISC-V compiler for ULP programs ESPHome never builds. + """ + + def apply_patch(data: dict) -> bool: + changed = False + for tool in data.get("tools", []): + if ( + tool.get("name") in _UNUSED_IDF_TOOLS + and tool.get("install") == "always" + ): + tool["install"] = "on_request" + changed = True + if tool.get("name") == "riscv32-esp-elf": + targets = tool.get("supported_targets") + # Guard the type so unexpected JSON here cannot abort the + # other demotions; this patch is best-effort. Log it so a + # silently resumed riscv download is diagnosable. + if not isinstance(targets, list): + _LOGGER.warning( + "Unexpected supported_targets for riscv32-esp-elf " + "in tools.json (%s); not excluding it from xtensa " + "installs", + type(targets).__name__, + ) + continue + if any(t in targets for t in _XTENSA_TARGETS): + tool["supported_targets"] = [ + t for t in targets if t not in _XTENSA_TARGETS + ] + changed = True + return changed + + _patch_tools_json( + framework_path, + apply_patch, + "Patched %s to skip installing tools ESPHome does not use " + "(openocd, gdb, ULP toolchains).", + ) + + +def _prefetch_idf_tool_archives( + framework_path: Path, + targets_str: str, + tools: list[str], + env: dict[str, str] | None, +) -> None: + """Pre-download the tool archives ``idf_tools.py install`` would fetch. + + ``idf_tools.py``'s own downloader restarts from byte zero on every retry, + which makes large archives effectively impossible to fetch on unstable + connections (#17703). This asks the framework's idf_tools (via + ``get_tool_downloads.py``) which archives the coming install needs, then + downloads each into ``/dist`` with + ``download_with_resume``. The installer then finds the verified archives + already in place ("file ... is already downloaded") and never touches the + network. + + Strictly best-effort: any failure here just logs and returns, leaving + ``idf_tools.py install`` to download whatever is missing exactly as + before. Leftover ``.part`` files live in ``dist/`` and are removed by the + post-install cache prune. + """ + try: + success, stdout, stderr = _run_idf_tools_script( + framework_path, + "get_tool_downloads.py", + "ESP-IDF tool download list", + args=[targets_str, *tools], + env=env, ) + if not success or not stdout: + _LOGGER.warning( + "Could not determine ESP-IDF tool downloads: %s", + (stderr or "").strip(), + ) + return + dist_path = get_idf_tools_path() / "dist" + entries = [ + entry + for entry in json.loads(stdout) + if not (dist_path / entry["dest"]).is_file() + ] + for index, entry in enumerate(entries, start=1): + _LOGGER.info( + "Downloading %s (%d/%d) ...", entry["name"], index, len(entries) + ) + try: + download_with_resume( + entry["url"], + dist_path / entry["dest"], + sha256=entry["sha256"], + size=entry["size"], + ) + except Exception as e: # noqa: BLE001 # pylint: disable=broad-exception-caught + # Keep prefetching the remaining archives; the installer + # will retry this one itself (without resume). + _LOGGER.warning("Could not prefetch %s: %s", entry["name"], e) + except Exception as e: # noqa: BLE001 # pylint: disable=broad-exception-caught + # The installer downloads anything missing itself; never let the + # prefetch become a new way for the install to fail. + _LOGGER.warning("ESP-IDF tool prefetch failed: %s", e) def _check_esphome_idf_framework_install( @@ -536,13 +768,19 @@ def _check_esphome_idf_framework_install( env: Optional dictionary of environment variables to set source_url: Optional override URL for the framework tarball. Supports the same ``{VERSION}`` / ``{MAJOR}`` / ``{MINOR}`` / ``{PATCH}`` / - ``{EXTRA}`` substitutions as ESPHOME_IDF_FRAMEWORK_MIRRORS - (``{EXTRA}`` includes its leading ``-``, e.g. ``-rc1``, or is empty). - When set, it replaces the default mirror list — no implicit fallback, - so a misspelled URL fails loudly. + ``{EXTRA}`` / ``{SHORT_VERSION}`` substitutions as + ESPHOME_IDF_FRAMEWORK_MIRRORS (``{EXTRA}`` includes its leading + ``-``, e.g. ``-rc1``, or is empty; ``{SHORT_VERSION}`` is ``x.y`` + plus any extra and only available for x.y.0 versions — a URL + referencing it is skipped for other versions). When set, it + replaces the default mirror list — no implicit fallback, so a + misspelled or skipped URL fails loudly with an EsphomeError naming + the URL. Returns: - tuple of (framework_path, install_flag) + tuple of (framework_path, fresh_extract_flag). The flag is True only + when the framework tree was downloaded and extracted this run, not + when tools were installed into an existing tree. """ # Sanitize inputs @@ -575,8 +813,8 @@ def _check_esphome_idf_framework_install( # avoids post-extraction renames that race with antivirus on Windows. # Tool install state is tracked separately by the stamp file in step 3, # so we only re-extract when extraction itself is missing or incomplete. - install = force or not extracted_marker.is_file() - if install: + fresh_extract = force or not extracted_marker.is_file() + if fresh_extract: rmdir(framework_path, msg=f"Clean up ESP-IDF {version} framework") git_source = _parse_git_source(source_url) if source_url else None @@ -584,28 +822,51 @@ def _check_esphome_idf_framework_install( git_url, ref = git_source _clone_idf_with_submodules(framework_path, git_url, ref) else: - # Download in temporary file - with tempfile.NamedTemporaryFile() as tmp: - _LOGGER.info("Downloading ESP-IDF %s framework ...", version) + _LOGGER.info("Downloading ESP-IDF %s framework ...", version) - # Create substitutions for the URLs - substitutions = {"VERSION": version} - try: - ver = Version.parse(version) - substitutions["MAJOR"] = str(ver.major) - substitutions["MINOR"] = str(ver.minor) - substitutions["PATCH"] = str(ver.patch) - substitutions["EXTRA"] = f"-{ver.extra}" if ver.extra else "" - except ValueError: - pass - - mirrors = [source_url] if source_url else ESPHOME_IDF_FRAMEWORK_MIRRORS - download_from_mirrors(mirrors, substitutions, tmp.file) - - _LOGGER.info("Extracting ESP-IDF %s framework ...", version) - archive_extract_all( - tmp.file, framework_path, progress_header="Extracting" + # Create substitutions for the URLs. SHORT_VERSION (x.y with + # optional -extra) is only provided for x.y.0 releases, since + # the vX.Y release tags only exist for those; templates that + # reference it are skipped for other versions by + # download_from_mirrors. + substitutions = {"VERSION": version} + try: + ver = Version.parse(version) + substitutions["MAJOR"] = str(ver.major) + substitutions["MINOR"] = str(ver.minor) + substitutions["PATCH"] = str(ver.patch) + substitutions["EXTRA"] = f"-{ver.extra}" if ver.extra else "" + if ver.patch == 0: + substitutions["SHORT_VERSION"] = ( + f"{ver.major}.{ver.minor}{substitutions['EXTRA']}" + ) + except ValueError: + _LOGGER.warning( + "ESP-IDF version '%s' is not a valid version number; " + "only the {VERSION} substitution is available for " + "mirror URLs", + version, ) + + mirrors = [source_url] if source_url else ESPHOME_IDF_FRAMEWORK_MIRRORS + # Download to a persistent file in the tool download cache (not + # a temp file) so an interrupted download resumes on the next + # run; the cache is pruned after a successful install anyway. + tarball_path = get_idf_tools_path() / "dist" / f"esp-idf-{version}.tar.xz" + download_from_mirrors(mirrors, substitutions, tarball_path) + + _LOGGER.info("Extracting ESP-IDF %s framework ...", version) + try: + with tarball_path.open("rb") as tarball: + archive_extract_all( + tarball, framework_path, progress_header="Extracting" + ) + finally: + # Success: drop the archive rather than caching ~70MB twice. + # Failure: a corrupt archive (e.g. torn by an unclean + # shutdown) must not be reused — without a checksum only a + # failed extraction can expose it, so force a re-download. + tarball_path.unlink(missing_ok=True) extracted_marker.touch() # Idempotent post-extract patch: written every invocation so a build @@ -618,10 +879,17 @@ def _check_esphome_idf_framework_install( # a pre-patch tools.json get fixed up without forcing a clean. _patch_tools_json_for_linux_arm64(framework_path) + # Drop tools ESPHome never runs from the required tool set on every + # invocation, so an install that previously failed on the openocd libusb + # check recovers on the next build. + _patch_tools_json_demote_unused_tools(framework_path) + # 3. Check if the framework tools are the same and correctly installed + stored_stamp = None if fresh_extract else _read_stamp(env_stamp_file) + install = fresh_extract if not install: install = True - if _check_stamp(env_stamp_file, stamp_info): + if _stamp_covers(stored_stamp, stamp_info): _LOGGER.info("Checking ESP-IDF %s framework installation ...", version) # Validate via the managed tool-path resolution, not ``idf_tools.py check``: # ``check`` probes tools on the system PATH and aborts if any fail to run (e.g. a @@ -638,6 +906,7 @@ def _check_esphome_idf_framework_install( if install: _LOGGER.info("Installing ESP-IDF %s framework ...", version) targets_str = ",".join(targets) + _prefetch_idf_tool_archives(framework_path, targets_str, tools, env) cmd = [ get_system_python_path(), str(idf_tools_path), @@ -651,11 +920,45 @@ def _check_esphome_idf_framework_install( env=env, stream_output=True, ): + if platform.system() == "Linux" and find_library("usb-1.0") is None: + _LOGGER.error( + "libusb-1.0.so.0 was not found on this system. If the error " + "above mentions it (openocd fails its install check without " + "it), install the libusb 1.0 package, e.g. libusb-1.0-0 " + "(Debian/Ubuntu), libusb1 (Fedora) or libusb (Alpine/Arch), " + "then run the build again." + ) raise RuntimeError(f"ESP-IDF {version} framework installation failure") + # idf_tools.py extracts tool archives from /dist into tools/; the + # archives are not needed afterward and, already compressed, dominate the cached install. + # Best-effort: a failure to prune must not fail an otherwise successful install. + try: + rmdir( + get_idf_tools_path() / "dist", msg="Remove ESP-IDF tool download cache" + ) + except RuntimeError as err: + _LOGGER.debug("Could not remove ESP-IDF tool download cache: %s", err) + + # Record the union of every target installed so far, not just this + # build's. idf_tools.py accumulates targets in idf-env.json and the + # ``required`` metapackage installs tools for all of them, so the + # union is what is actually on disk — and it keeps two variants + # alternating between builds from re-running the installer each time. + # Merge only when everything except targets matches: a reinstall + # triggered by a schema or tools change ran the installer for this + # build's targets alone, so carrying the old targets forward would + # let later builds of those variants skip the reinstall they need. + if ( + stored_stamp + and isinstance(stored_stamp.get("targets"), list) + and _stamps_match_except_targets(stored_stamp, stamp_info) + ): + merged = set(stamp_info["targets"]) | set(stored_stamp["targets"]) + stamp_info["targets"] = ["all"] if "all" in merged else sorted(merged) _write_stamp(env_stamp_file, stamp_info) - return framework_path, install + return framework_path, fresh_extract def _check_esp_idf_python_env_install( @@ -801,7 +1104,11 @@ def check_esp_idf_install( env["IDF_TOOLS_PATH"] = str(get_idf_tools_path()) env["IDF_PATH"] = "" - targets = targets or ESPHOME_IDF_DEFAULT_TARGETS + # An explicit ESPHOME_IDF_DEFAULT_TARGETS wins over the caller's + # per-variant request (builder-image pre-warm); otherwise the caller's + # targets are used, falling back to the default when none were given. + if _IDF_DEFAULT_TARGETS_EXPLICIT or not targets: + targets = ESPHOME_IDF_DEFAULT_TARGETS # Determine which tools need to be installed if not provided if tools is None: @@ -814,15 +1121,18 @@ def check_esp_idf_install( tools.append(tool) # 1) Framework - framework_path, installed = _check_esphome_idf_framework_install( + framework_path, fresh_extract = _check_esphome_idf_framework_install( version, targets, tools, force=force, env=env, source_url=source_url ) features = features or ESPHOME_IDF_DEFAULT_FEATURES - # 2) Python env - python_env_path, installed = _check_esp_idf_python_env_install( - version, features, force=force or installed, env=env + # 2) Python env. Only a freshly extracted framework forces a rebuild — + # the venv depends on the framework version and features, not on which + # toolchains are installed, so adding a target to an existing tree must + # not wipe it. It still self-validates against its own stamp. + python_env_path, _ = _check_esp_idf_python_env_install( + version, features, force=force or fresh_extract, env=env ) return framework_path, python_env_path diff --git a/esphome/espidf/get_tool_downloads.py b/esphome/espidf/get_tool_downloads.py new file mode 100644 index 0000000000..37a6126fae --- /dev/null +++ b/esphome/espidf/get_tool_downloads.py @@ -0,0 +1,88 @@ +"""Print JSON download info for the ESP-IDF tools an install would fetch. + +Run via ``python ...``. +PYTHONPATH must include ``/tools`` so ``idf_tools`` is +importable, and IDF_TOOLS_PATH must be set. Prints a JSON list of +``{name, url, size, sha256, dest}`` for every tool version that is not yet +installed, where ``dest`` is the archive filename ``idf_tools.py install`` +expects to find in ``/dist``. Tools with no download for the +current platform are skipped; already-installed versions are skipped so a +pruned download cache is not re-fetched. + +The target/tool expansion mirrors ``idf_tools.py install`` (targets passed to +``add_and_check_targets`` accumulate with idf-env.json) but nothing is saved +or written — this script only reports what the install would download. +""" + +# pylint: disable=import-error # idf_tools is on PYTHONPATH at runtime only + +from contextlib import redirect_stdout +import json +import os +from pathlib import Path +import sys + +from idf_tools import ( + CURRENT_PLATFORM, + TOOLS_FILE, + IDFEnv, + ToolBinaryError, + add_and_check_targets, + expand_tools_arg, + g, + get_idf_download_url_apply_mirrors, + load_tools_info, +) + + +def collect_downloads() -> list[dict]: + g.idf_path = sys.argv[1] + g.idf_tools_path = os.environ.get("IDF_TOOLS_PATH") + g.tools_json = str(Path(g.idf_path) / TOOLS_FILE) + + targets = add_and_check_targets(IDFEnv.get_idf_env(), sys.argv[2]) + tools_info = load_tools_info() + downloads: list[dict] = [] + + for name in expand_tools_arg(sys.argv[3:], tools_info, targets): + if "@" in name: + name, version = name.split("@", 1) + else: + version = None + tool = tools_info.get(name) + if tool is None or not tool.compatible_with_platform(): + continue + version = version or tool.get_recommended_version() + if version is None: + continue + try: + tool.find_installed_versions() + except ToolBinaryError as e: + # A broken installed binary is idf_tools' problem to repair on + # install; note it and treat the version as not installed. + print(f"tool {name} failed its binary check: {e}", file=sys.stderr) + if version in tool.versions_installed or version not in tool.versions: + continue + download = tool.versions[version].get_download_for_platform(CURRENT_PLATFORM) + if download is None: + continue + downloads.append( + { + "name": f"{name}@{version}", + # Apply the same IDF_MIRROR_PREFIX_MAP / IDF_GITHUB_ASSETS + # rewriting the installer's own downloader applies, so users + # behind a mirror prefetch from the mirror too. + "url": get_idf_download_url_apply_mirrors(None, download.url), + "size": download.size, + "sha256": download.sha256, + "dest": download.rename_dist or Path(download.url).name, + } + ) + return downloads + + +# idf_tools prints informational lines (e.g. mirror URL rewrites) to stdout; +# route them to stderr so stdout carries only the JSON result. +with redirect_stdout(sys.stderr): + result = collect_downloads() +print(json.dumps(result)) diff --git a/esphome/espidf/idedata.py b/esphome/espidf/idedata.py index 0ed357a759..0047d568e2 100644 --- a/esphome/espidf/idedata.py +++ b/esphome/espidf/idedata.py @@ -6,7 +6,7 @@ toolchain has no such command, but its CMake build emits turns that file into the same fields consumers (IDE integration, clang-tidy) expect: - {cxx_path, cxx_flags, defines, includes: {build, toolchain}} + {cc_path, cxx_path, cxx_flags, defines, includes: {build, toolchain}} """ from __future__ import annotations @@ -197,6 +197,28 @@ def _get_toolchain_includes(cxx_path: str) -> list[str]: return includes +def _cc_path_from_cxx(cxx_path: str) -> str: + """Derive the C compiler path from the C++ compiler path. + + compile_commands.json only names the C++ compiler, but consumers reach the + rest of the toolchain (objdump, readelf, addr2line) by rewriting the tail of + ``cc_path``, so they need the ``gcc``-suffixed name. + """ + stem, suffix = ( + (cxx_path[: -len(".exe")], ".exe") + if cxx_path.endswith(".exe") + else (cxx_path, "") + ) + # Rewrite the program name only when it is g++ itself, or a toolchain + # prefixed one such as xtensa-esp32-elf-g++ -> xtensa-esp32-elf-gcc. + # Requiring a separator before the "g++" keeps names that merely end in + # those three characters intact: "clang++" must not become "clangcc". + head = stem[: -len("g++")] + if stem.endswith("g++") and (not head or head.endswith(("-", "/", "\\"))): + stem = f"{head}gcc" + return f"{stem}{suffix}" + + def idedata_from_build(compile_commands: Path) -> dict: """Parse compile_commands.json into the idedata fields consumers expect. @@ -218,6 +240,7 @@ def idedata_from_build(compile_commands: Path) -> dict: build_includes.setdefault(inc, None) return { + "cc_path": _cc_path_from_cxx(cxx_path), "cxx_path": cxx_path, "cxx_flags": cxx_flags, "defines": defines, diff --git a/esphome/espidf/size_summary.py b/esphome/espidf/size_summary.py index 3ba0bf3b4d..7a5305ff0c 100644 --- a/esphome/espidf/size_summary.py +++ b/esphome/espidf/size_summary.py @@ -57,7 +57,7 @@ def _find_app_partition_size(partitions_csv: Path) -> int: """ if not partitions_csv.is_file(): raise ValueError(f"partitions.csv not found at {partitions_csv}") - for row in csv.reader(partitions_csv.read_text().splitlines()): + for row in csv.reader(partitions_csv.read_text(encoding="utf-8").splitlines()): cells = [c.strip() for c in row] if not cells or cells[0].startswith("#") or len(cells) < 5: continue @@ -89,7 +89,7 @@ def print_summary(size_json: Path, partitions_csv: Path | None) -> None: _LOGGER.debug("Skipping size summary: %s not found", size_json) return try: - data = json.loads(size_json.read_text()) + data = json.loads(size_json.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError) as e: _LOGGER.debug("Skipping size summary: %s", e) return diff --git a/esphome/espidf/toolchain.py b/esphome/espidf/toolchain.py index 000ce739db..fd95805c6c 100644 --- a/esphome/espidf/toolchain.py +++ b/esphome/espidf/toolchain.py @@ -9,8 +9,19 @@ import re import shutil import subprocess -from esphome.components.esp32.const import KEY_ESP32, KEY_FLASH_SIZE, KEY_IDF_VERSION -from esphome.const import CONF_FRAMEWORK, CONF_SOURCE +from esphome.components.esp32.const import ( + KEY_ESP32, + KEY_FLASH_SIZE, + KEY_IDF_VERSION, + KEY_VARIANT, + variant_to_idf_target, +) +from esphome.const import ( + CONF_COMPILE_PROCESS_LIMIT, + CONF_ESPHOME, + CONF_FRAMEWORK, + CONF_SOURCE, +) from esphome.core import CORE, EsphomeError from esphome.espidf.framework import check_esp_idf_install, get_framework_env from esphome.espidf.size_summary import print_summary @@ -51,6 +62,27 @@ def _get_framework_source_override() -> str | None: return CORE.config.get(KEY_ESP32, {}).get(CONF_FRAMEWORK, {}).get(CONF_SOURCE) +def _get_configured_targets() -> list[str] | None: + """Return the IDF install target for the configured variant, if known. + + Limiting the toolchain install to the variant being built skips the other + architecture's compiler entirely (several hundred MB of download and 1-2GB + of disk). idf_tools.py accumulates targets across runs, so building a + second variant later installs just its toolchain incrementally. None (no + variant stored, e.g. tooling outside a build) falls back to the default + inside check_esp_idf_install. + + CI always installs every target (None falls through to the "all" + default): runners share one toolchain cache across jobs that build + different variants, so a full install keeps the cached tree identical + everywhere instead of per-variant supersets invalidating each other. + """ + if os.environ.get("CI"): + return None + variant = CORE.data.get(KEY_ESP32, {}).get(KEY_VARIANT) + return [variant_to_idf_target(variant)] if variant else None + + def _get_esphome_esp_idf_paths( version: str | None = None, ) -> tuple[os.PathLike, os.PathLike]: @@ -58,7 +90,9 @@ def _get_esphome_esp_idf_paths( paths = _cache().paths if version not in paths: paths[version] = check_esp_idf_install( - version, source_url=_get_framework_source_override() + version, + targets=_get_configured_targets(), + source_url=_get_framework_source_override(), ) return paths[version] @@ -94,6 +128,14 @@ def _get_idf_env(version: str | None = None) -> dict[str, str]: def _get_cmake_output(build_dir) -> str: cmake_output_cache = _cache().cmake_output if build_dir not in cmake_output_cache: + # Check the build before resolving the env: _get_idf_env() runs + # check_esp_idf_install(), which can download and install the whole + # framework. Never start that for a build that isn't there. Callers + # such as the log stack-trace decoder run against devices that were + # never compiled on this machine. + if not (Path(build_dir) / "CMakeCache.txt").is_file(): + raise EsphomeError(f"No ESP-IDF build found in {build_dir}") + cmd = ["cmake", "-LA", "-N", "."] env = _get_idf_env() @@ -147,7 +189,10 @@ def _get_idf_tool(name: str) -> str: def run_idf_py( - *args, cwd: Path | None = None, capture_output: bool = False + *args, + cwd: Path | None = None, + capture_output: bool = False, + jobs: int | None = None, ) -> int | str: """Run idf.py with the given arguments.""" idf_path = _get_idf_path() @@ -155,6 +200,8 @@ def run_idf_py( raise EsphomeError("ESP-IDF not found") env = _get_idf_env() + if jobs is not None: + env = {**env, "IDF_PY_BUILD_JOBS": str(jobs)} python_executable = _get_idf_tool("python") idf_py = idf_path / "tools" / "idf.py" # Dispatch idf.py through esphome.espidf.runner, which wraps @@ -384,7 +431,7 @@ def run_compile(config, verbose: bool) -> int: args.append("build") args.append("size") - rc = run_idf_py(*args) + rc = run_idf_py(*args, jobs=config[CONF_ESPHOME].get(CONF_COMPILE_PROCESS_LIMIT)) if rc == 0: size_json = CORE.relative_build_path("build", "esp_idf_size.json") partitions = CORE.relative_build_path("partitions.csv") @@ -467,9 +514,16 @@ def get_idedata() -> dict | None: cache = CORE.relative_internal_path("idedata", f"{CORE.name}.json") if cache.is_file() and cache.stat().st_mtime >= compile_commands.stat().st_mtime: try: - return json.loads(cache.read_text(encoding="utf-8")) + cached = json.loads(cache.read_text(encoding="utf-8")) except ValueError: pass + else: + # Caches written before cc_path was emitted stay newer than + # compile_commands.json forever, so rebuild them on the field rather + # than on the timestamp. Check the type too: a corrupted cache can + # still be valid JSON, and "in" would match a substring of a string. + if isinstance(cached, dict) and "cc_path" in cached: + return cached data = idedata_from_build(compile_commands) data["prog_path"] = str(get_elf_path()) diff --git a/esphome/external_files.py b/esphome/external_files.py index 4e73c8dc21..69423d3999 100644 --- a/esphome/external_files.py +++ b/esphome/external_files.py @@ -165,11 +165,8 @@ def download_content(url: str, path: Path, timeout: int = NETWORK_TIMEOUT) -> by _LOGGER.debug("Remote file has not changed %s", url) return path.read_bytes() - _LOGGER.debug( - "Remote file has changed, downloading from %s to %s", - url, - path, - ) + _LOGGER.info("Downloading %s", url) + _LOGGER.debug("Saving to %s", path) try: req = requests.get( @@ -210,9 +207,13 @@ def download_content_many( items: Iterable[tuple[str, Path]], timeout: int = NETWORK_TIMEOUT, max_workers: int = DEFAULT_DOWNLOAD_WORKERS, + description: str = "remote file(s)", ) -> None: """Run `download_content` for each (url, path) pair concurrently. + `description` names the kind of files in the progress log line, e.g. + "wake word manifest(s)". + Wall time drops from `sum(latency)` to roughly `max(latency)` for cached files where the HEAD round-trip dominates. All workers run to completion before this returns; every `cv.Invalid` raised by a worker @@ -230,6 +231,7 @@ def download_content_many( seen: dict[Path, str] = {path: url for url, path in items} if not seen: return + _LOGGER.info("Checking %d %s for updates", len(seen), description) if len(seen) == 1: path, url = next(iter(seen.items())) download_content(url, path, timeout) diff --git a/esphome/framework_helpers.py b/esphome/framework_helpers.py index 70d440d995..202d4a2bfb 100644 --- a/esphome/framework_helpers.py +++ b/esphome/framework_helpers.py @@ -2,21 +2,31 @@ from collections.abc import Iterable from contextlib import ExitStack +import hashlib import io +import json import logging import os from pathlib import Path import subprocess import sys import time -from typing import IO +from typing import IO, TYPE_CHECKING from esphome.helpers import ProgressBar, rmtree +if TYPE_CHECKING: + import requests + PathType = str | os.PathLike _LOGGER = logging.getLogger(__name__) +# Attempts per mirror URL before falling through to the next mirror; only +# mid-stream drops retry (resuming when the server gave a validator), +# connect errors move on immediately. +_MIRROR_ATTEMPTS = 3 + def get_project_link_flags() -> list[str]: """Return the sorted -Wl, linker flags from the current build.""" @@ -394,17 +404,23 @@ def _zip_extract_all( progress.update(1) -def _rename_with_retry(src: Path, dst: Path, attempts: int = 5) -> None: +def _rename_with_retry( + src: Path, dst: Path, attempts: int = 5, overwrite: bool = False +) -> None: """Rename ``src`` to ``dst`` with backoff retries on Windows sharing violations. Antivirus/indexer handles on freshly-written files can briefly block ``os.rename`` with ERROR_SHARING_VIOLATION / ERROR_ACCESS_DENIED. The handle is released within tens of ms in practice, so exponential backoff - works. + works. With ``overwrite`` an existing ``dst`` is replaced instead of + failing. """ for i in range(attempts): try: - src.rename(dst) + if overwrite: + src.replace(dst) + else: + src.rename(dst) return except PermissionError: if i == attempts - 1: @@ -525,8 +541,8 @@ def archive_extract_all( ValueError: If archive format is unsupported """ - # 1. Handle different archive input types with ExitStack() as stack: + # 1. Handle different archive input types archive_ref: io.BufferedIOBase if isinstance(archive, (str, os.PathLike)): archive_ref = stack.enter_context(Path(archive).open("rb")) @@ -552,6 +568,322 @@ def archive_extract_all( matched_fct(archive_ref, extract_dir, progress_header=progress_header) +def _open_ranged( + url: str, offset: int, timeout: int, validator: str | None = None +) -> tuple["requests.Response | None", int]: + """Open a streaming GET, asking the server to resume at ``offset``. + + ``validator`` is an ETag or Last-Modified value from the interrupted + response; it is sent as ``If-Range`` so the server only honors the Range + when the content is unchanged, replying 200 (full body, restart) if the + file was replaced between requests — the resumed bytes can then never be + stitched onto a different file's prefix. + + Returns ``(response, effective_offset)``. The response is None when the + server answered 416 Range Not Satisfiable: the file holds every byte the + server has (a previous attempt was interrupted after the last byte), so + there is nothing to stream and the caller's verification decides whether + the file is good. The offset drops to 0 when the server ignored the + ``Range`` header (no 206), meaning the caller must restart the file. + Raises on connect errors and HTTP error statuses; the response is closed + on failure. + """ + import requests + + headers = {"Range": f"bytes={offset}-"} if offset else {} + if offset and validator: + headers["If-Range"] = validator + resp = requests.get(url, stream=True, timeout=timeout, headers=headers) + if offset and resp.status_code == 416: + resp.close() + return None, offset + if offset and resp.status_code != 206: + _LOGGER.debug( + "Server did not resume %s (HTTP %s), restarting", url, resp.status_code + ) + offset = 0 + if not resp.ok: + resp.close() + resp.raise_for_status() + if offset: + _LOGGER.info("Resuming download at %d bytes ...", offset) + return resp, offset + + +def _verify_file(path: Path, sha256: str | None, size: int | None) -> None: + """Raise EsphomeError when ``path`` fails an available sha256/size check.""" + from esphome.core import EsphomeError + + if size is not None and path.stat().st_size != size: + raise EsphomeError(f"size mismatch: expected {size}, got {path.stat().st_size}") + if sha256 is not None: + with path.open("rb") as f: + digest = hashlib.file_digest(f, "sha256").hexdigest() + if digest != sha256: + raise EsphomeError(f"sha256 mismatch: got {digest}") + + +def _load_download_meta(meta: Path, url: str) -> tuple[str | None, int]: + """Return the ``(validator, total)`` a previous run recorded for ``url``. + + ``(None, 0)`` when there is no sidecar, it is unreadable, or it belongs + to a different URL (e.g. a different mirror was tried last time). + """ + try: + with meta.open(encoding="utf-8") as f: + data = json.load(f) + except (OSError, json.JSONDecodeError): + return None, 0 + if not isinstance(data, dict) or data.get("url") != url: + return None, 0 + validator = data.get("validator") + total = data.get("total") + return ( + validator if isinstance(validator, str) else None, + total if isinstance(total, int) else 0, + ) + + +def _write_download_meta( + meta: Path, url: str, validator: str | None, total: int +) -> None: + """Persist resume metadata next to the part file; best-effort. + + Without a validator there is nothing a later run could resume against, + so any stale sidecar is removed instead. + """ + try: + if validator is None: + meta.unlink(missing_ok=True) + else: + meta.write_text( + json.dumps({"url": url, "validator": validator, "total": total}), + encoding="utf-8", + ) + except OSError as e: + _LOGGER.debug("Could not update download metadata %s: %s", meta, e) + + +def _content_length(resp: "requests.Response") -> int: + """Return the response's Content-Length, or 0 when absent or malformed. + + 0 means "unknown", which downstream disables the progress bar and the + resume/completeness logic — a garbage header from a broken proxy must + degrade to a plain single-stream download, not crash the attempt. + """ + try: + return int(resp.headers.get("content-length", 0)) + except ValueError: + return 0 + + +def _response_validator(resp: "requests.Response") -> str | None: + """Return the response's strong validator for ``If-Range`` resumes. + + Weak ETags (``W/...``) are not usable for byte-range conditionals, so + fall back to Last-Modified, or None when the server offers neither. + """ + etag = resp.headers.get("ETag") + if etag and not etag.startswith("W/"): + return etag + return resp.headers.get("Last-Modified") + + +def _stream_response_to_file( + resp: "requests.Response", f: IO[bytes], offset: int, size: int | None = None +) -> None: + """Stream an open ``_open_ranged`` response body into ``f`` at ``offset``. + + Truncates ``f`` to ``offset`` first, so a server-rejected resume + (effective offset 0) discards the stale bytes. ``offset`` also seeds the + progress bar so a resumed download shows overall progress. ``size`` is + the known full file size; when None it is derived from the response's + content-length, and without either there is no progress bar. + """ + f.seek(offset) + f.truncate(offset) + total_size = size or offset + _content_length(resp) + downloaded = offset + progress = ProgressBar("Downloading") if total_size > 0 else None + for chunk in resp.iter_content(chunk_size=256 * 1024): + if chunk: + f.write(chunk) + downloaded += len(chunk) + if progress is not None: + progress.update(downloaded / total_size) + if progress is not None: + progress.update(1) + + +def download_with_resume( + url: str, + dest: PathType, + sha256: str | None = None, + size: int | None = None, + # More attempts than _MIRROR_ATTEMPTS: a single-URL download has no + # mirror fallback, and each retry only re-fetches the remainder. + attempts: int = 5, + timeout: int = 30, + retry_connect_errors: bool = True, +) -> None: + """Download ``url`` to ``dest``, resuming partial downloads. + + The body streams into ``.part``, which persists across attempts and + esphome runs: a mid-stream connection drop only costs one attempt and the + next continues from where it stopped, so an unstable connection converges + on a complete file instead of restarting from zero each retry (#17703). + When ``size`` / ``sha256`` are given the completed file is verified and a + mismatch restarts from scratch; success renames the part file into place. + An already-present ``dest`` that passes verification is kept as-is. + + Resuming a part file from an earlier run needs proof the content is + unchanged: ``sha256`` when the caller has one, or otherwise the server's + If-Range validator recorded in a ``.part.meta`` sidecar by the run + that started the download — a size alone cannot detect a same-length + content change on the server. + + With ``retry_connect_errors`` disabled, a failure before any body bytes + flow (connect error, HTTP error status) propagates immediately instead + of consuming attempts — for callers with their own fallback, like + ``download_from_mirrors``. + + Raises EsphomeError when all attempts are exhausted. + """ + # Imported lazily: requests is a heavy import (~85ms) and is only needed + # when actually downloading a toolchain, never during config validation. + import requests + + from esphome.core import EsphomeError + + dest = Path(dest) + part = dest.with_name(dest.name + ".part") + meta = part.with_name(part.name + ".meta") + dest.parent.mkdir(parents=True, exist_ok=True) + last_error: Exception | None = None + + # An earlier run already completed this download. Only trust it when + # there is something to verify it against; without sha/size the remote + # content may have changed (e.g. a refreshed constraints file), so + # re-download and atomically replace it. + if dest.is_file() and (sha256 is not None or size is not None): + try: + _verify_file(dest, sha256, size) + return + except EsphomeError: + dest.unlink() + + # Adopt the validator/total the run that started this part file recorded, + # so an unfinished download resumes across runs even without a sha256. + validator, expected_total = _load_download_meta(meta, url) + + for _ in range(attempts): + streamed = False + try: + offset = part.stat().st_size if part.is_file() else 0 + # A stitched resume needs two proofs: content identity (the + # bytes being appended belong to the same file as the prefix) + # and completeness. sha256 provides both, across runs. Without + # it, identity needs this run's If-Range validator — a size + # alone cannot detect a same-length content change, so a + # leftover part file from an earlier run must restart — and + # completeness needs a known total length. + if ( + offset + and sha256 is None + and (validator is None or not (size or expected_total)) + ): + _LOGGER.debug( + "Restarting %s from zero: cannot prove a resumed " + "file correct (no sha256, validator=%s, total=%s)", + url, + validator is not None, + size or expected_total, + ) + offset = 0 + if size is None or offset < size: + resp, offset = _open_ranged(url, offset, timeout, validator) + # A None response means HTTP 416: the part file already holds + # every byte the server has; fall through to verification. + if resp is not None: + with resp, part.open("ab") as f: + streamed = True + if offset == 0: + validator = _response_validator(resp) + expected_total = _content_length(resp) + # Recorded so a later run can prove an If-Range + # resume of this part file safe. + _write_download_meta(meta, url, validator, expected_total) + _stream_response_to_file(resp, f, offset, size) + # else: a previous run already wrote every byte (or more) but + # was killed before the rename below. Skip the network entirely + # — a Range request past EOF would draw HTTP 416 — and let + # verification decide whether to promote the file or discard it + # and start over. + + expected_size = size if size is not None else expected_total + _verify_file(part, sha256, expected_size or None) + if not expected_size and sha256 is None: + # No sha, no size, and the server sent no usable + # content-length: nothing can prove the download complete + # (urllib3 still errors on most short bodies, but not on a + # cleanly closed chunked stream). Promote with a debug + # note rather than fail or warn: some servers (e.g. the + # Espressif constraints host) never send a length, the user + # can do nothing about it, and every current caller + # extracts or parses the file afterwards, where corruption + # fails loudly. + _LOGGER.debug( + "Downloaded %s without any way to verify completeness", + dest.name, + ) + # Retry on Windows sharing violations: an antivirus handle on the + # freshly-written file must not get the verified download deleted + # as corrupt by the except clause below. If even the backoff + # retries fail, keep the verified part so the next attempt (or + # run) only has to redo the rename, not the download. + try: + _rename_with_retry(part, dest, overwrite=True) + except PermissionError as e: + _LOGGER.debug("Could not move %s into place: %s", part, e) + last_error = e + continue + meta.unlink(missing_ok=True) + return + except requests.RequestException as e: + # Network failures — including connect errors, since a single + # URL has no mirror-list fallback — keep the part file for the + # next attempt (or the next esphome run) to resume from. Checked + # before OSError: RequestException subclasses IOError. + if not retry_connect_errors and not streamed: + # The caller falls back to another URL on pre-body failures. + raise + _LOGGER.debug("Download of %s interrupted: %s", url, e) + last_error = e + except (OSError, EsphomeError) as e: + # A completed-but-corrupt file (or local disk error) can't be + # trusted for resume; start over. + _LOGGER.debug("Discarding %s: %s", part, e) + part.unlink(missing_ok=True) + meta.unlink(missing_ok=True) + last_error = e + + raise EsphomeError( + f"Failed to download {url} after {attempts} attempts: " + f"{_failure_reason(last_error)}" + ) from last_error + + +def _failure_reason(e: Exception) -> str: + """Format a download exception for the aggregated error message. + + ``requests`` appends " for url: " to HTTP errors; the URL is already + printed on the line above, so strip the suffix to keep lines short. Falls + back to the repr for exceptions with no message (e.g. ``TimeoutError()``) + so the line always names the failure. + """ + return str(e).split(" for url: ", maxsplit=1)[0] or repr(e) + + def download_from_mirrors( mirrors: list[str], substitutions: dict[str, str], @@ -570,70 +902,170 @@ def download_from_mirrors( Returns: The source URL. + Mirror URL templates that reference a substitution not present in + ``substitutions`` are skipped, so callers can offer templates that only + apply to some downloads. + + A path target downloads through ``download_with_resume``, so an + interrupted download resumes on the next esphome run; a file-like target + only resumes mid-stream drops within this call. + Raises: ValueError: If mirrors list is empty. - Exception: If all download attempts fail. + EsphomeError: If all download attempts fail; the message lists every + attempted URL with its individual failure reason. Also raised if + no template matched the provided substitutions. """ - # Imported lazily: requests is a heavy import (~85ms) and is only needed - # when actually downloading a toolchain, never during config validation. + # Imported lazily: requests is a heavy import (~85ms) and is only + # needed when actually downloading, never during config validation. import requests - # 1. Open target file for writing if path given - with ExitStack() as stack: - if isinstance(target, (str, os.PathLike)): - f = stack.enter_context(Path(target).open("wb")) - elif isinstance(target, (io.RawIOBase, io.IOBase)): - f = target - else: - raise TypeError( - f"target must be str, Path, or file-like object: {type(target)}" - ) + from esphome.core import EsphomeError - # 2. Try each mirror in order - last_exception = None + # 1. Classify the target: filesystem path or open file object + path_target: Path | None = None + f: IO[bytes] | None = None + if isinstance(target, (str, os.PathLike)): + path_target = Path(target) + elif isinstance(target, (io.RawIOBase, io.IOBase)): + f = target + else: + raise TypeError( + f"target must be str, Path, or file-like object: {type(target)}" + ) - for mirror in mirrors: - # 3. Apply substitutions to URL + # 2. Try each mirror in order + failures: list[tuple[str, Exception]] = [] + skipped: list[tuple[str, str]] = [] + + for mirror in mirrors: + # 3. Apply substitutions to URL + try: url = mirror.format(**substitutions) + except KeyError as e: + # The template references a substitution not provided for + # this download (e.g. SHORT_VERSION only exists for x.y.0 + # versions) - expected, the template just doesn't apply. + _LOGGER.debug("Skipping mirror %s: %s not available", mirror, e) + skipped.append((mirror, f"not applicable ({e.args[0]} not available)")) + continue + except (IndexError, ValueError) as e: + # A malformed template (unbalanced braces, bad format spec) + # is an authoring error, not an expected fallthrough - warn + # even if a later mirror succeeds. + _LOGGER.warning("Skipping malformed mirror URL template %s: %r", mirror, e) + skipped.append((mirror, f"skipped ({e!r})")) + continue - _LOGGER.debug("Trying downloading from %s", url) + _LOGGER.debug("Trying to download from %s", url) + + # Path targets delegate to download_with_resume so a partial + # download persists (and resumes) across esphome runs. + if path_target is not None: + try: + download_with_resume( + url, + path_target, + attempts=_MIRROR_ATTEMPTS, + timeout=timeout, + # Pre-body failures (connect/HTTP errors) fall to the + # next mirror immediately; only mid-stream drops + # retry-with-resume on the same URL. + retry_connect_errors=False, + ) + return url + except (requests.RequestException, OSError, EsphomeError) as e: + # Everything download_with_resume classifies as a download + # failure; programming errors propagate. + _LOGGER.debug("Failed to download %s: %s", url, str(e)) + failures.append((url, e)) + continue + + # 4. Download; mid-stream failures retry the same mirror with + # resume (see download_with_resume) instead of starting over. + # There is no checksum to verify a resumed file against, so a + # stitch is only trusted when the server proves consistency: the + # If-Range validator guarantees 206 only for unchanged content, + # and the expected total length (when the first response carried + # one) guards against short or shifted bodies. Without a + # validator the retry restarts from zero. + offset = 0 + expected_total = 0 + validator = None + for attempt in range(_MIRROR_ATTEMPTS): + try: + resp, offset = _open_ranged(url, offset, timeout, validator) + except (requests.RequestException, OSError) as e: + # Connect/HTTP error, no bytes flowed — next mirror. + _LOGGER.debug("Failed to download %s: %s", url, str(e)) + failures.append((url, e)) + break try: - # 4. Reset file pointer and download - f.seek(0) - f.truncate(0) + # A None response means HTTP 416: the file already holds + # every byte the server has (a drop after the last byte); + # only the length check below remains. + if resp is not None: + with resp: + if offset == 0: + validator = _response_validator(resp) + expected_total = _content_length(resp) + _stream_response_to_file(resp, f, offset) - with requests.get(url, stream=True, timeout=timeout) as r: - r.raise_for_status() - - total_size = int(r.headers.get("content-length", 0)) - downloaded = 0 - - progress = ProgressBar("Downloading") if total_size > 0 else None - - for chunk in r.iter_content(chunk_size=8192): - if chunk: - f.write(chunk) - - downloaded += len(chunk) - - if progress is not None: - progress.update(downloaded / total_size) - - if progress is not None: - progress.update(1) + if expected_total and f.tell() != expected_total: + raise EsphomeError( + f"size mismatch: expected {expected_total}, got {f.tell()}" + ) + if not expected_total: + # Same trust decision as download_with_resume's + # unverifiable promotion; surface it at the same level. + _LOGGER.debug( + "Downloaded %s without any way to verify completeness", + url, + ) _LOGGER.debug("Downloaded successfully from: %s", url) - # 6. Reset file pointer and return + # 5. Reset file pointer and return f.seek(0) return url - except Exception as e: # noqa: BLE001 # pylint: disable=broad-exception-caught + except (requests.RequestException, OSError, EsphomeError) as e: + # Mid-stream drop: keep the received bytes and retry this + # mirror from the current position — but only when the + # server gave a validator to resume against safely AND a + # total length to prove the stitched file complete (the + # length check above is the only verification here). _LOGGER.debug("Failed to download %s: %s", url, str(e)) - last_exception = e + if validator and expected_total: + offset = f.tell() + else: + _LOGGER.debug( + "Restarting %s from zero: cannot prove a " + "resumed file complete (validator=%s, total=%s)", + url, + validator is not None, + expected_total, + ) + offset = 0 + if attempt == _MIRROR_ATTEMPTS - 1: + failures.append((url, e)) - # 7. Raise last exception if all mirrors failed - if last_exception: - raise last_exception - raise ValueError("download_from_mirrors called with an empty mirrors list") + # 6. Report every attempted URL if all mirrors failed. Falling back + # past an early mirror is normal (e.g. only one of the framework URL + # templates matches a given version's tag), so raising only the last + # error would hide the failure that actually matters. + if failures: + attempts = "".join( + f"\n {url}\n {_failure_reason(e)}" for url, e in failures + ) + attempts += "".join(f"\n {mirror}\n {reason}" for mirror, reason in skipped) + raise EsphomeError( + f"Failed to download from all mirrors:{attempts}" + ) from failures[0][1] + if skipped: + details = "".join(f"\n {mirror}\n {reason}" for mirror, reason in skipped) + raise EsphomeError( + f"No mirror URL template matched the provided substitutions:{details}" + ) + raise ValueError("download_from_mirrors called with an empty mirrors list") diff --git a/esphome/git.py b/esphome/git.py index c4a612753b..d1dca3b3ae 100644 --- a/esphome/git.py +++ b/esphome/git.py @@ -1,23 +1,82 @@ -from collections.abc import Callable +from collections.abc import Callable, Iterator +from contextlib import contextmanager from dataclasses import dataclass +from enum import Enum, auto +import errno import hashlib import logging +import os from pathlib import Path import re import subprocess import sys import time +from typing import TYPE_CHECKING import urllib.parse import esphome.config_validation as cv -from esphome.core import CORE, TimePeriodSeconds -from esphome.helpers import rmtree +from esphome.core import CORE, EsphomeError, TimePeriodSeconds +from esphome.helpers import ( + add_git_ceiling_directory, + format_duration, + rmtree, + write_file, +) + +if TYPE_CHECKING: + from filelock import FileLock _LOGGER = logging.getLogger(__name__) # Special value to indicate never refresh NEVER_REFRESH = TimePeriodSeconds(seconds=-1) +# `refresh: never` validates to a huge interval rather than the NEVER_REFRESH +# sentinel; treat any interval at least that long as refresh disabled instead +# of logging a countdown of hundreds of years +_REFRESH_DISABLED_SECONDS = cv.source_refresh(cv.SOURCE_REFRESH_NEVER).total_seconds + +# revert() runs on an already-failing path; bound its wait for the cache +# entry lock so that recovery cannot hang forever behind another process. +_REVERT_LOCK_TIMEOUT_SECONDS = 60 + +# When a complete cache entry already exists, a caller does not wait forever +# behind another process's stalled clone or update (git sets no network +# timeouts): after this bound it uses the existing clone without refreshing +# it. With no complete entry there is nothing to fall back to, so the wait +# is unbounded. +_COMPLETE_ENTRY_LOCK_TIMEOUT_SECONDS = 60 + +# Written inside .git only while the entry is a complete, quiescent +# checkout: after every clone step (clone, ref fetch, reset, submodule init) +# has finished, and removed for the duration of a refresh's rewrite +# (stash/fetch/reset). A directory without it is an interrupted clone or +# update (e.g. the process was killed mid-clone) and must be re-cloned; +# without this check such a directory would be trusted forever when the +# caller uses NEVER_REFRESH, and the bounded-wait fallback would hand a +# mid-rewrite tree to a timed-out peer. Lives in .git so +# stash/reset/checkout can never touch it and it does not pollute the +# worktree. +_CLONE_COMPLETE_MARKER = "esphome_clone_complete" + +# Environment variables that scope git to a specific repository. Git hooks and +# some CI wrappers export these; if they leak into the git commands run here, +# git binds to the caller's repository instead of the one being managed. The +# effects range from loud (`git clone` producing a bare-style directory with +# no working tree) to silent (an ambient GIT_INDEX_FILE makes +# `git submodule update --init` exit 0 without initializing anything). +_GIT_REPO_SCOPING_ENV = frozenset( + { + "GIT_DIR", + "GIT_WORK_TREE", + "GIT_INDEX_FILE", + "GIT_OBJECT_DIRECTORY", + "GIT_ALTERNATE_OBJECT_DIRECTORIES", + "GIT_COMMON_DIR", + "GIT_NAMESPACE", + } +) + class GitException(cv.Invalid): """Base exception for git-related errors.""" @@ -35,32 +94,61 @@ class GitRepositoryError(GitException): """Exception raised when a git repository is in an invalid state.""" -def run_git_command(cmd: list[str], git_dir: Path | None = None) -> str: - if git_dir is not None: - _LOGGER.debug( - "Running git command with repository isolation: %s (git_dir=%s)", - " ".join(cmd), - git_dir, - ) - else: - _LOGGER.debug("Running git command: %s", " ".join(cmd)) +def _redact_url_credentials(text: str) -> str: + """Mask userinfo in any URLs embedded in ``text``. - # Set up environment for repository isolation if git_dir is provided - # Force git to only operate on this specific repository by setting - # GIT_DIR and GIT_WORK_TREE. This prevents git from walking up the - # directory tree to find parent repositories when the target repo's - # .git directory is corrupt. Without this, commands like 'git stash' - # could accidentally operate on parent repositories (e.g., the main - # ESPHome repo) instead of failing, causing data loss. - env: dict[str, str] | None = None - cwd: str | None = None + Users can put credentials directly in a git URL, and log output is + routinely pasted into public issues. + """ + return re.sub(r"://[^/@\s]+@", "://***@", text) + + +def run_git_command( + cmd: list[str], git_dir: Path | None = None, *, cwd: Path | None = None +) -> str: + """Run a git command and return its stdout. + + The repository-scoping environment variables in ``_GIT_REPO_SCOPING_ENV`` + are always stripped. ``git_dir`` additionally pins GIT_DIR/GIT_WORK_TREE + to that repository and runs the command there; ``cwd`` alone runs the + command in that directory with GIT_CEILING_DIRECTORIES capping repository + discovery at its parent. + """ + # Every invocation starts from an environment with the repository-scoping + # variables stripped (see _GIT_REPO_SCOPING_ENV) so a git hook or CI + # wrapper invoking ESPHome can never redirect these commands to its own + # repository or index. + # + # ``git_dir`` then re-adds GIT_DIR and GIT_WORK_TREE pointing at the + # managed repository. This prevents git from walking up the directory + # tree to find parent repositories when the target repo's .git directory + # is corrupt. Without this, commands like 'git stash' could accidentally + # operate on parent repositories (e.g., the main ESPHome repo) instead of + # failing, causing data loss. + # + # ``cwd`` (without ``git_dir``) runs the command in that directory + # without GIT_DIR/GIT_WORK_TREE. The ``git submodule`` porcelain needs + # this: on some installations (e.g. Windows setups where a shim hands + # git untranslated paths) it refuses to run when GIT_DIR/GIT_WORK_TREE + # are set, failing with "cannot be used without a working tree". + # GIT_CEILING_DIRECTORIES (which git only honors as an absolute path) + # keeps the parent-repo-walk protection instead: if the repo's .git is + # missing or corrupt, git fails rather than discovering an enclosing + # repository. + env = {k: v for k, v in os.environ.items() if k not in _GIT_REPO_SCOPING_ENV} if git_dir is not None: - env = { - **subprocess.os.environ, - "GIT_DIR": str(Path(git_dir) / ".git"), - "GIT_WORK_TREE": str(git_dir), - } - cwd = str(git_dir) + env["GIT_DIR"] = str(Path(git_dir) / ".git") + env["GIT_WORK_TREE"] = str(git_dir) + cwd = git_dir + elif cwd is not None: + add_git_ceiling_directory(env, Path(cwd).absolute().parent) + + _LOGGER.debug( + "Running git command: %s (cwd=%s, isolated=%s)", + _redact_url_credentials(" ".join(cmd)), + cwd, + git_dir is not None, + ) try: ret = subprocess.run( @@ -78,16 +166,31 @@ def run_git_command(cmd: list[str], git_dir: Path | None = None) -> str: "for installation instructions." ) from err - if ret.returncode != 0 and ret.stderr: - err_str = ret.stderr.decode("utf-8") - lines = [x.strip() for x in err_str.splitlines()] - if lines[-1].startswith("fatal:"): - raise GitCommandError(lines[-1][len("fatal: ") :]) - raise GitCommandError(err_str) + if ret.returncode != 0: + if ret.stderr: + err_str = ret.stderr.decode("utf-8") + lines = [x.strip() for x in err_str.splitlines()] + if lines[-1].startswith("fatal:"): + raise GitCommandError(lines[-1][len("fatal: ") :]) + raise GitCommandError(err_str) + raise GitCommandError( + f"git exited with code {ret.returncode}: " + f"{_redact_url_credentials(' '.join(cmd))}" + ) return ret.stdout.decode("utf-8").strip() +def _cache_key(url: str, ref: str | None) -> str: + """Cache key identifying one repository checkout. + + The lock path and the entry directory both hash this, keeping them in + agreement. (micro_wake_word still rebuilds the format by hand to locate + manifests; fold it in here if the format ever changes.) + """ + return f"{url}@{ref}" + + def _compute_destination_path(key: str, domain: str) -> Path: base_dir = Path(CORE.data_dir) / domain h = hashlib.new("sha256") @@ -95,6 +198,220 @@ def _compute_destination_path(key: str, domain: str) -> Path: return base_dir / h.hexdigest()[:8] +def _repo_entry_dir(key: str, domain: str, subpath: Path | None) -> Path: + """Worktree directory of one cache entry: the hash dir plus optional subpath.""" + repo_dir = _compute_destination_path(key, domain) + if subpath: + repo_dir = repo_dir / subpath + return repo_dir + + +def _repo_lock_path(key: str, domain: str) -> Path: + """Path of the lock file serializing all work on one cache entry. + + Lives next to the hash directory, never inside it, so the removal of a + broken or incomplete clone can never delete a lock another process holds. + """ + repo_dir = _compute_destination_path(key, domain) + return repo_dir.parent / f"{repo_dir.name}.lock" + + +class _LockStatus(Enum): + ACQUIRED = auto() + # A bounded wait expired while another process held the lock. + TIMEOUT = auto() + # The lock could not be taken at all; callers proceed unlocked, + # matching the behavior before the lock existed. + UNAVAILABLE = auto() + + +# Errnos that mean the filesystem genuinely cannot take file locks (NFS +# without a lock daemon, some FUSE mounts). Any other OSError (permissions, +# read-only volume, full disk) is a cache directory problem, which the git +# commands themselves report clearly when it actually matters. EPERM is +# deliberately absent: it usually means a permissions problem, so it takes +# the generic message that names no cause. On Linux ENOTSUP and EOPNOTSUPP +# are the same value; the set folds them. +_NO_LOCK_SUPPORT_ERRNOS = frozenset( + {errno.ENOLCK, errno.ENOSYS, errno.EOPNOTSUPP, errno.ENOTSUP} +) + + +def _acquire_repo_lock( + lock: "FileLock", + safe_key: str, + timeout: float, + wait_message: str = "Waiting for another process to finish updating %s", +) -> _LockStatus: + """Acquire ``lock``, logging ``wait_message`` when a wait actually begins. + + ``timeout`` of -1 waits forever; a positive value bounds the wait and + can yield ``TIMEOUT``. + """ + from filelock import Timeout + + try: + try: + lock.acquire(blocking=False) + except Timeout: + # Waiting on another process's clone or update can take + # minutes; say so instead of appearing hung. + _LOGGER.info(wait_message, safe_key) + lock.acquire(timeout=timeout) + except Timeout: + return _LockStatus.TIMEOUT + except OSError as err: + if err.errno in _NO_LOCK_SUPPORT_ERRNOS: + _LOGGER.warning( + "The filesystem does not support locking the cache entry for " + "%s (%s), continuing without a lock", + safe_key, + err, + ) + else: + # Not a locking problem (permissions, read-only volume, full + # disk). Still continue unlocked: a pre-seeded read-only cache + # with refresh disabled only reads and must keep working, and + # in every other case the git commands fail with the real error. + _LOGGER.warning( + "Could not take the cache entry lock for %s (%s), " + "continuing without a lock", + safe_key, + err, + ) + return _LockStatus.UNAVAILABLE + return _LockStatus.ACQUIRED + + +@contextmanager +def _repo_cache_lock( + key: str, domain: str, repo_dir: Path +) -> Iterator[tuple[bool, "FileLock | None"]]: + """Hold the cache entry lock for ``key`` over the with block. + + Yields ``(use_existing, lock)``. ``use_existing`` is True when the lock + could not be acquired within the bounded wait but ``repo_dir`` is a + complete cache entry; the caller should use it as-is and do nothing + else. Otherwise ``lock`` is the held lock, released when the block + exits, or ``None`` when the lock could not be taken at all and the + caller proceeds unlocked. + """ + # Lazy import: keeps filelock off the CLI startup import path. + from filelock import FileLock + + safe_key = _redact_url_credentials(key) + # acquire() creates the lock file's directory itself; git clone later + # creates the hash directory next to it. fallback_to_soft would silently + # downgrade ENOSYS to a SoftFileLock, whose stale existence marker from + # another host on a shared cache could hang the unbounded wait forever; + # routing it through the OSError handler runs unlocked instead. + lock: FileLock | None = FileLock( + str(_repo_lock_path(key, domain)), fallback_to_soft=False + ) + status = _acquire_repo_lock(lock, safe_key, _COMPLETE_ENTRY_LOCK_TIMEOUT_SECONDS) + if status is _LockStatus.TIMEOUT: + if _clone_complete_marker_path(repo_dir).is_file(): + # Mutual exclusion matters most while no complete entry exists + # (initial clone, recovery re-clone); with one on disk, reading + # it beats hanging behind a stalled holder. + _LOGGER.warning( + "Timed out waiting for another process updating %s, proceeding " + "with the existing clone, which that process may still be " + "changing", + safe_key, + ) + yield True, None + return + # Nothing to fall back to; the holder is producing the clone this + # caller needs. + status = _acquire_repo_lock( + lock, + safe_key, + timeout=-1, + wait_message="Still waiting for the clone of %s, " + "there is no existing clone to fall back on", + ) + if status is not _LockStatus.ACQUIRED: + lock = None + try: + yield False, lock + finally: + if lock is not None: + lock.release() + + +def _clone_complete_marker_path(repo_dir: Path) -> Path: + return repo_dir / ".git" / _CLONE_COMPLETE_MARKER + + +def _clear_clone_complete_marker(repo_dir: Path) -> None: + """Best-effort removal of the completion marker. + + If the unlink fails (e.g. a file lock on Windows), the marker stays and + the entry keeps its previous trust level; every consumer of the marker + tolerates that. + """ + try: + _clone_complete_marker_path(repo_dir).unlink(missing_ok=True) + except OSError as err: + _LOGGER.debug("Could not delete clone completion marker: %s", err) + + +def _write_clone_complete_marker( + repo_dir: Path, key: str, hash_dir_name: str, safe_key: str +) -> None: + """Mark the entry as a complete, quiescent checkout. + + The key and hash dir name are recorded purely to make cache debugging + easier. The marker is only a validity signal, so a failed write must not + fail an otherwise complete clone or update: the only cost is a re-clone + on the next run. + """ + try: + write_file( + _clone_complete_marker_path(repo_dir), + f"key={key}\nhash={hash_dir_name}\n", + ) + except EsphomeError as err: + _LOGGER.warning( + "Could not write clone completion marker for %s: %s", safe_key, err + ) + + +def _remove_repo_dir(repo_dir: Path) -> None: + """Remove a repo directory, deleting the completion marker first. + + Marker-first ordering guarantees an interrupted removal can never leave a + marker behind next to a partially deleted worktree. The unlink is best + effort: if it fails, rmtree below still gets the chance to remove the + directory, marker included. + """ + _clear_clone_complete_marker(repo_dir) + if repo_dir.is_dir(): + rmtree(repo_dir) + + +def update_submodules(repo_dir: Path, key: str) -> None: + """Initialize/update every submodule the repository declares, recursively, + matching how PlatformIO clones libraries. + + Most repositories declare no submodules, so this does nothing when there + is no ``.gitmodules`` file. Which submodules get populated is git's own + policy (``update = none``, ``submodule.active``, sparse checkouts); + git's exit code is the error signal. + + Runs with plain ``cwd`` rather than ``git_dir`` isolation, which the + ``git submodule`` porcelain does not tolerate (see ``run_git_command``). + """ + if not (repo_dir / ".gitmodules").is_file(): + return + _LOGGER.info("Updating submodules for %s", _redact_url_credentials(key)) + run_git_command( + ["git", "submodule", "update", "--init", "--recursive", "--depth=1"], + cwd=repo_dir, + ) + + def resolve_symlink_stub(repo_dir: Path, file_path: Path) -> Path | None: """Return the symlink target if ``file_path`` is a Windows-checked-out symlink stub. @@ -184,28 +501,106 @@ def resolve_symlink_stub(repo_dir: Path, file_path: Path) -> Path | None: def clone_or_update( *, url: str, - ref: str = None, + ref: str | None = None, refresh: TimePeriodSeconds | None, domain: str, - username: str = None, - password: str = None, - submodules: list[str] | None = None, + username: str | None = None, + password: str | None = None, + init_submodules: bool = False, subpath: Path | None = None, - _recover_broken: bool = True, -) -> tuple[Path, Callable[[], None] | None]: - key = f"{url}@{ref}" +) -> tuple[Path, Callable[[], bool] | None]: + """Clone a repository into the cache, or refresh an existing clone. + All work runs under a per-cache-entry inter-process file lock, so + concurrent resolutions of the same repository (two esphome processes, or + a subprocess plus an in-process load) serialize instead of interleaving. + Without the lock, ``repo_dir.is_dir()`` is true from the instant + ``git clone`` creates the directory: a second caller could read a half + populated worktree, or see the missing completion marker and delete the + clone in progress out from under the first caller. + + The lock guards mutation of the cache entry only; it is released when + this function returns, so a caller still reading the worktree can + overlap a later refresh by another process. That residual window is + narrow (the refresh interval is re-checked under the lock) and predates + the lock. + + Locking is best effort: on a filesystem that cannot take file locks a + warning is logged and the work proceeds unlocked, matching the behavior + before the lock existed. A complete cache entry also caps the wait: if + the holder is still busy after a bounded time (e.g. stalled on the + network), the existing clone is used without refreshing it, so a stuck + process cannot hang every peer that already has a good entry. + """ + key = _cache_key(url, ref) + repo_dir = _repo_entry_dir(key, domain, subpath) + with _repo_cache_lock(key, domain, repo_dir) as (use_existing, lock): + if use_existing: + return repo_dir, None + return _clone_or_update_locked( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + username=username, + password=password, + init_submodules=init_submodules, + subpath=subpath, + lock=lock, + ) + + +def _clone_or_update_locked( + *, + url: str, + ref: str | None, + refresh: TimePeriodSeconds | None, + domain: str, + username: str | None, + password: str | None, + init_submodules: bool, + subpath: Path | None, + lock: "FileLock | None", + _recover_broken: bool = True, +) -> tuple[Path, Callable[[], bool] | None]: + """Body of ``clone_or_update``; the caller holds ``lock``. + + Split out because the broken-repository recovery below re-enters this + function: re-acquiring the already-held lock would deadlock, since OS + file locks taken on separate file descriptors conflict even within one + process. ``lock`` is only re-acquired by the returned ``revert`` + callback, which runs after the wrapper's ``finally`` has released it. + ``lock`` is ``None`` when the filesystem cannot take file locks and the + wrapper fell back to running unlocked. + """ + key = _cache_key(url, ref) + # The user may have embedded credentials in the URL itself; log this + # instead of key. + safe_key = _redact_url_credentials(key) + + # Keep the caller's URL for the recovery re-clone below: rewriting the + # rewritten URL would double the userinfo, and the recursive call must + # compute the same cache key as this one. + original_url = url if username is not None and password is not None: url = url.replace( "://", f"://{urllib.parse.quote(username)}:{urllib.parse.quote(password)}@" ) - repo_dir = _compute_destination_path(key, domain) - if subpath: - repo_dir = repo_dir / subpath + hash_dir_name = _compute_destination_path(key, domain).name + repo_dir = _repo_entry_dir(key, domain, subpath) + + if repo_dir.is_dir() and not _clone_complete_marker_path(repo_dir).is_file(): + # The last clone never finished (killed process, container stop) or + # predates the marker; either way it cannot be trusted, especially + # with NEVER_REFRESH where it would otherwise be reused forever. + _LOGGER.warning( + "Removing incomplete clone of %s at %s, will re-clone", safe_key, repo_dir + ) + _remove_repo_dir(repo_dir) if not repo_dir.is_dir(): - _LOGGER.info("Cloning %s", key) + _LOGGER.info("Cloning %s", safe_key) _LOGGER.debug("Location: %s", repo_dir) try: cmd = ["git", "clone", "--depth=1"] @@ -224,33 +619,35 @@ def clone_or_update( ["git", "reset", "--hard", "FETCH_HEAD"], git_dir=repo_dir ) - if submodules is not None: - _LOGGER.info( - "Initializing submodules (%s) for %s", ", ".join(submodules), key - ) - run_git_command( - ["git", "submodule", "update", "--init", "--depth=1", "--"] - + submodules, - git_dir=repo_dir, - ) + if init_submodules: + update_submodules(repo_dir, key) + except GitException: # Remove incomplete clone to prevent stale state. Without this, # a failed ref fetch leaves a clone on the default branch, and # subsequent calls skip the update due to the refresh window. - if repo_dir.is_dir(): - rmtree(repo_dir) + _remove_repo_dir(repo_dir) raise + # Every git step succeeded. + _write_clone_complete_marker(repo_dir, key, hash_dir_name, safe_key) + else: if refresh == NEVER_REFRESH or CORE.skip_external_update: - _LOGGER.debug("Skipping update for %s (refresh disabled)", key) + _LOGGER.debug("Skipping update for %s (refresh disabled)", safe_key) return repo_dir, None file_timestamp = Path(repo_dir / ".git" / "FETCH_HEAD") # On first clone, FETCH_HEAD does not exist if not file_timestamp.exists(): file_timestamp = Path(repo_dir / ".git" / "HEAD") - age_seconds = time.time() - file_timestamp.stat().st_mtime + try: + age_seconds = time.time() - file_timestamp.stat().st_mtime + except OSError: + # A .git with neither FETCH_HEAD nor HEAD is corrupt (e.g. a + # partially deleted clone). Force the update path so the + # broken-repository recovery below removes and re-clones it. + age_seconds = float("inf") if refresh is None or age_seconds > refresh.total_seconds: # Try to update the repository, recovering from broken state if needed old_sha: str | None = None @@ -261,9 +658,16 @@ def clone_or_update( ["git", "rev-parse", "HEAD"], git_dir=repo_dir ) - _LOGGER.info("Updating %s", key) + _LOGGER.info("Updating %s", safe_key) _LOGGER.debug("Location: %s", repo_dir) + # The entry is about to be rewritten; drop the marker so a + # timed-out peer's fallback and the incomplete-entry check + # can tell a quiescent complete entry from one mid-rewrite, + # and so an update interrupted by a crash re-clones instead + # of being trusted. + _clear_clone_complete_marker(repo_dir) + # Stash local changes (if any) # Use git_dir to ensure this only affects the specific repo run_git_command( @@ -287,55 +691,139 @@ def clone_or_update( ["git", "reset", "--hard", "FETCH_HEAD"], git_dir=repo_dir, ) + + # Inside the try so a submodule failure routes through the + # recovery re-clone below instead of leaving a repo that the + # refresh window would silently accept on the next run. + if init_submodules: + update_submodules(repo_dir, key) + + # Recorded so revert() can tell whether the checkout is + # still the one this update produced. + new_sha = run_git_command( + ["git", "rev-parse", "HEAD"], git_dir=repo_dir + ) + + # The rewrite finished; the entry is trustworthy again. + _write_clone_complete_marker(repo_dir, key, hash_dir_name, safe_key) except GitException as err: # Repository is in a broken state or update failed # Only attempt recovery once to prevent infinite recursion if not _recover_broken: _LOGGER.error( "Repository %s recovery failed, cannot retry (already attempted once)", - key, + safe_key, ) raise _LOGGER.warning( "Repository %s has issues (%s), attempting recovery", - key, + safe_key, err, ) _LOGGER.info("Removing broken repository at %s", repo_dir) - rmtree(repo_dir) + _remove_repo_dir(repo_dir) _LOGGER.info("Successfully removed broken repository, re-cloning...") - # Recursively call clone_or_update to re-clone - # Set _recover_broken=False to prevent infinite recursion - result = clone_or_update( - url=url, + # Re-clone while still holding the lock; going through the + # public wrapper would try to re-acquire it and deadlock. + # Set _recover_broken=False to prevent infinite recursion. + result = _clone_or_update_locked( + url=original_url, ref=ref, refresh=refresh, domain=domain, username=username, password=password, - submodules=submodules, + init_submodules=init_submodules, + subpath=subpath, + lock=lock, _recover_broken=False, ) - _LOGGER.info("Repository %s successfully recovered", key) + _LOGGER.info("Repository %s successfully recovered", safe_key) return result - if submodules is not None: - _LOGGER.info( - "Updating submodules (%s) for %s", ", ".join(submodules), key - ) - run_git_command( - ["git", "submodule", "update", "--init", "--depth=1", "--"] - + submodules, - git_dir=repo_dir, - ) + def revert() -> bool: + """Reset the checkout to the pre-update SHA. - def revert(): - _LOGGER.info("Reverting changes to %s -> %s", key, old_sha) - run_git_command(["git", "reset", "--hard", old_sha], git_dir=repo_dir) + Returns False when the revert did not happen: the cache + entry lock could not be acquired in time, the checkout + moved since this update (another process refreshed it), or + the reset itself failed. A retry cannot reach the + pre-update content then. + """ + if lock is None: + # The wrapper already warned about the unlockable + # filesystem; revert unlocked like everything else. + status = _LockStatus.UNAVAILABLE + else: + status = _acquire_repo_lock( + lock, safe_key, _REVERT_LOCK_TIMEOUT_SECONDS + ) + if status is _LockStatus.TIMEOUT: + # revert() only runs on an already-failing path; skip + # rather than hang so the original error can surface. + _LOGGER.warning( + "Could not lock %s to revert to %s, skipping revert; " + "the cached checkout keeps the un-reverted content " + "until its next refresh", + safe_key, + old_sha, + ) + return False + try: + # Anything can happen between the wrapper releasing the + # lock and revert() re-acquiring it; only undo this + # process's own update, never a peer's newer refresh. + head = run_git_command( + ["git", "rev-parse", "HEAD"], git_dir=repo_dir + ) + if head != new_sha: + _LOGGER.warning( + "Not reverting %s: the checkout moved since this " + "update (another process refreshed it)", + safe_key, + ) + return False + # Announced only once every skip check has passed, so + # the log says exactly one thing per outcome. + _LOGGER.info("Reverting changes to %s -> %s", safe_key, old_sha) + run_git_command( + ["git", "reset", "--hard", old_sha], git_dir=repo_dir + ) + except GitException as err: + # GitException is a cv.Invalid; letting it escape would + # replace the caller's original error with a bare git + # message. Report the failed reset like the skip above, + # and drop the marker: an entry whose reset fails cannot + # be trusted, so the next use re-clones it instead of + # the refresh window silently accepting it. + _LOGGER.warning( + "Could not revert %s to %s (%s), the entry will be " + "re-cloned on next use", + safe_key, + old_sha, + err, + ) + _clear_clone_complete_marker(repo_dir) + return False + finally: + if status is _LockStatus.ACQUIRED: + lock.release() + return True return repo_dir, revert + if refresh.total_seconds >= _REFRESH_DISABLED_SECONDS: + # refresh: never + _LOGGER.debug("Skipping update for %s (refresh disabled)", safe_key) + else: + _LOGGER.info( + "Skipping update for %s, will refresh on the next run after %s " + "(refresh: %s); use refresh: always to update now", + safe_key, + format_duration(refresh.total_seconds - age_seconds), + format_duration(refresh.total_seconds), + ) return repo_dir, None diff --git a/esphome/helpers.py b/esphome/helpers.py index 631bcb6f39..683aaedcf5 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -148,6 +148,21 @@ def indent(text, padding=" "): return "\n".join(indent_list(text, padding)) +def format_duration(seconds: float) -> str: + """Format a duration in seconds as a short string like "1d 2h" or "42s". + + Uses the two largest non-zero units, with unit suffixes matching the YAML + time period shorthand (d, h, min, s). + """ + remainder = max(0, int(seconds)) + parts = [] + for suffix, length in (("d", 86400), ("h", 3600), ("min", 60), ("s", 1)): + value, remainder = divmod(remainder, length) + if value: + parts.append(f"{value}{suffix}") + return " ".join(parts[:2]) if parts else "0s" + + # From https://stackoverflow.com/a/14945195/8924614 def cpp_string_escape(string, encoding="utf-8"): def _should_escape(byte: int) -> bool: diff --git a/esphome/idf_component.yml b/esphome/idf_component.yml index 60b00d33c7..e88c0649ea 100644 --- a/esphome/idf_component.yml +++ b/esphome/idf_component.yml @@ -18,13 +18,13 @@ dependencies: esphome/micro-wav: version: 0.2.0 espressif/esp-dsp: - version: "1.7.1" + version: "1.8.2" espressif/esp-tflite-micro: version: 1.3.3~1 espressif/esp32-camera: version: 2.1.7 espressif/mdns: - version: 1.11.0 + version: 1.11.3 espressif/esp_wifi_remote: version: 1.5.1 rules: @@ -98,7 +98,7 @@ dependencies: esp32async/asynctcp: version: 3.4.91 sendspin/sendspin-cpp: - version: 0.6.1 + version: 0.7.0 lvgl/lvgl: version: 9.5.0 fastled/FastLED: diff --git a/esphome/mqtt.py b/esphome/mqtt.py index c6a7a7558b..3198de9d21 100644 --- a/esphome/mqtt.py +++ b/esphome/mqtt.py @@ -110,8 +110,12 @@ def prepare( CONF_CLIENT_CERTIFICATE_KEY ): with ( - tempfile.NamedTemporaryFile(mode="w+", delete=False) as cert_file, - tempfile.NamedTemporaryFile(mode="w+", delete=False) as key_file, + tempfile.NamedTemporaryFile( + encoding="utf-8", mode="w+", delete=False + ) as cert_file, + tempfile.NamedTemporaryFile( + encoding="utf-8", mode="w+", delete=False + ) as key_file, ): try: cert_file.write(config[CONF_MQTT].get(CONF_CLIENT_CERTIFICATE)) diff --git a/esphome/platformio/ccache.py.script b/esphome/platformio/ccache.py.script new file mode 100644 index 0000000000..cc08a8c044 --- /dev/null +++ b/esphome/platformio/ccache.py.script @@ -0,0 +1,34 @@ +import os +import shutil + +# pylint: disable=E0602 +Import("env") # noqa + +# ESPHome decides whether ccache is used and exports the CCACHE_* settings +# into the environment before PlatformIO starts (_ccache_env() in +# esphome/platformio/toolchain.py); this script only supplies the SCons-level +# mechanism. +# +# This is a "pre" script, so the platform's builder (which sets CC/CXX and +# clones the construction environment for framework and library builds) runs +# after it. Replacing CC/CXX here would be overwritten, and replacing them in +# a "post" script would miss the already-cloned library environments. Wrapping +# SPAWN instead is ordering-proof: clones copy the wrapper, and every compiler +# invocation from every environment funnels through it at execution time. +if ( + os.environ.get("ESPHOME_CCACHE_ENABLE") == "1" + and (ccache_path := shutil.which("ccache")) is not None +): + original_spawn = env["SPAWN"] + + def ccache_spawn(sh, escape, cmd, args, child_env): + # Only wrap compile steps (gcc/g++ with -c); linking, archiving and + # the other tools gain nothing from ccache. + prog = os.path.basename(cmd).removesuffix(".exe") + if prog.endswith(("gcc", "g++")) and "-c" in args: + cmd = ccache_path + args = [escape(ccache_path), *args] + return original_spawn(sh, escape, cmd, args, child_env) + + env.Replace(SPAWN=ccache_spawn) + print("ESPHome: Compiling with ccache") diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index 291bedb5cd..1a523ce0ab 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -25,7 +25,7 @@ from pathlib import Path import re import tempfile from typing import Any -from urllib.parse import urlparse, urlsplit, urlunsplit +from urllib.parse import urlsplit, urlunsplit from esphome import git from esphome.core import CORE, Library @@ -134,7 +134,7 @@ class GitSource(Source): ref=self.ref, refresh=git.NEVER_REFRESH if not force else None, domain=domain, - submodules=[], + init_submodules=True, subpath=Path(dir_suffix), ) return path @@ -292,6 +292,12 @@ def collect_filtered_files(src_dir: PathType, src_filters: list[str]) -> list[st for root, _, files in os.walk(item): matched.extend([str(Path(root) / f) for f in files]) + # glob keeps the pattern's literal separators for non-wildcard path + # components, so on Windows the same file can surface with different + # separators depending on where the wildcards sit; normalize so the + # include/exclude set operations below compare equal paths. + matched = [os.path.normpath(m) for m in matched] + # FILTER_REGEX only ever captures "+" or "-", so the else is the "-" case. if sign == "+": selected.update(matched) @@ -517,6 +523,17 @@ class _LibNode: edges: set[str] = field(default_factory=set) +def _url_or_none(value: Any) -> str | None: + """Return ``value`` if it parses as a URL (scheme and host), else None.""" + if not value or not isinstance(value, str): + return None + try: + parsed = urlsplit(value) + except ValueError: + return None + return value if parsed.scheme and parsed.netloc else None + + def _node_key( name: str | None, version: str | None, repository: str | None ) -> tuple[str, bool, tuple[str | None, str | None]]: @@ -527,9 +544,23 @@ def _node_key( inconsistently -- bare ``name`` vs ``owner/name``, or git vs registry -- maps to distinct keys and isn't deduplicated; ``convert_libraries`` warns about that after resolution rather than merging the nodes. + + PlatformIO's Library Manager also accepted a git URL in the *name* + position (``add_library("https://github.com/x/y", None)``), including the + ``git+`` VCS prefix and the ``CustomName=URL`` form; recognize those here + so such specs resolve as git sources instead of failing a registry lookup. """ + if not repository and name and "://" in name: + # Try the whole name first so a bare URL whose query contains ``=`` + # stays intact; fall back to the ``CustomName=URL`` form, where the + # key derives from the URL path and the custom name is irrelevant. + repository = _url_or_none(name) or _url_or_none(name.split("=", 1)[-1]) + if repository is None: + # Anything with ``://`` was meant to be a URL; failing it fast + # beats a confusing registry "package not found" error. + raise RuntimeError(f"Invalid PIO library URL: {name}") if repository: - split_result = urlsplit(repository) + split_result = urlsplit(repository.removeprefix("git+")) key = str(split_result.path).strip("/").removesuffix(".git") ref = split_result.fragment.strip() or None url = urlunsplit(split_result._replace(fragment="")) @@ -638,14 +669,30 @@ def convert_libraries( library_json_path = component.path / "library.json" library_properties_path = component.path / "library.properties" - if library_json_path.is_file(): + has_json = library_json_path.is_file() + has_properties = library_properties_path.is_file() + if not has_json and not has_properties: + # The shared cache can hold a broken copy (e.g. a clone or an + # extraction interrupted by a killed process). Force one + # re-download so a bad cache entry self-heals instead of failing + # every build until the user runs a full clean. + _LOGGER.warning( + "Library %s at %s is missing library.json and library.properties; " + "re-downloading", + key, + component.path, + ) + component.download(force=True, salt=salt, namespace=backend.cache_key) + has_json = library_json_path.is_file() + has_properties = library_properties_path.is_file() + if has_json: component.data = _parse_library_json(library_json_path) - elif library_properties_path.is_file(): + elif has_properties: component.data = _parse_library_properties(library_properties_path) else: raise RuntimeError( f"Invalid PIO library {key}: missing library.json and " - "library.properties" + f"library.properties in {component.path}" ) try: @@ -681,13 +728,9 @@ def convert_libraries( continue # The version field may actually be a URL (git/archive dependency). dep_version = dependency["version"] - dep_url = None - try: - parsed = urlparse(dep_version) - if all([parsed.scheme, parsed.netloc]): - dep_url, dep_version = dep_version, None - except (TypeError, ValueError): - pass + dep_url = _url_or_none(dep_version) + if dep_url is not None: + dep_version = None dep_key = add_spec(dep_name, dep_version, dep_url) node.edges.add(dep_key) worklist.append(dep_key) diff --git a/esphome/platformio/toolchain.py b/esphome/platformio/toolchain.py index c97df812e3..0959cbfffb 100644 --- a/esphome/platformio/toolchain.py +++ b/esphome/platformio/toolchain.py @@ -1,17 +1,44 @@ +from collections.abc import Iterable import json import logging import os from pathlib import Path import re +import shutil import sys +from typing import TYPE_CHECKING + +import platformdirs from esphome.const import CONF_COMPILE_PROCESS_LIMIT, CONF_ESPHOME, KEY_CORE from esphome.core import CORE, EsphomeError -from esphome.helpers import add_git_ceiling_directory +from esphome.helpers import ( + add_git_ceiling_directory, + copy_file_if_changed, + get_bool_env, + rmtree, + write_file, +) from esphome.util import FlashImage, run_external_process +if TYPE_CHECKING: + from platformio.project.config import ProjectConfig + _LOGGER = logging.getLogger(__name__) +# PlatformIO cache subdirs resolved via ProjectConfig. A full ``clean-all`` wipes +# these plus the whole ``core_dir``; a Python-version heal wipes these plus the +# penv while keeping ``core_dir`` (so the sibling stamp/lock survive). +_PIO_CACHE_DIRS = ("cache_dir", "packages_dir", "platforms_dir") + +# Marker recording the Python major.minor the PlatformIO cache was provisioned +# under, plus the lock guarding the check/wipe. Both live in the dir resolved +# by ``_pio_stamp_dir`` (NOT wiped by the heal), so they survive the wipe and +# are rewritten after it. +_PIO_PYTHON_STAMP_FILE = ".esphome.pio.stamp.json" +_PIO_PYTHON_STAMP_LOCK = ".esphome.pio.stamp.lock" +_PIO_PYTHON_STAMP_SCHEMA = "0" + def _strip_win_long_path_prefix(path: str) -> str: r"""Strip the Windows extended-length path prefix from ``path``. @@ -44,7 +71,245 @@ def _strip_win_long_path_prefix(path: str) -> str: return path +def get_platformio_config() -> "ProjectConfig | None": + """Return PlatformIO's ``ProjectConfig``, or None when PlatformIO is absent.""" + try: + from platformio.project.config import ProjectConfig + except ImportError: + return None + return ProjectConfig.get_instance() + + +def _pio_stamp_dir(config: "ProjectConfig") -> Path: + """Return the persistent home for the python-version stamp and lock. + + The parent of ``platforms_dir``, not ``core_dir``: the container/add-on + images relocate the platform/package caches to a persistent volume while + ``core_dir`` stays at the ephemeral default (its ``appstate.json`` must not + move), so a stamp under ``core_dir`` would be wiped on every image update + while the stale cache it guards survives. Everywhere else ``platforms_dir`` + sits inside ``core_dir`` and this resolves to ``core_dir``. + """ + return Path(config.get("platformio", "platforms_dir")).parent + + +def _delete_platformio_dirs(config: "ProjectConfig", pio_dirs: Iterable[str]) -> None: + """Delete each named PlatformIO dir resolved from *config*.""" + for pio_dir in pio_dirs: + path = Path(config.get("platformio", pio_dir)) + if path.is_dir(): + _LOGGER.info("Deleting PlatformIO %s %s", pio_dir, path) + rmtree(path) + + +def clean_platformio_cache() -> None: + """Wipe the whole PlatformIO cache (cache/packages/platforms/core). + + The full set ``clean-all`` (Reset Build Environment) clears. No-op when + PlatformIO is unavailable. + """ + config = get_platformio_config() + if config is None: + return + _delete_platformio_dirs(config, [*_PIO_CACHE_DIRS, "core_dir"]) + + +def _clean_platformio_python_env(config: "ProjectConfig", core_dir: Path) -> None: + """Wipe the cache subdirs + penv for a Python-version change. + + Keeps ``core_dir`` itself (and the stamp/lock siblings under it); otherwise + the same cache set ``clean-all`` clears. + """ + _delete_platformio_dirs(config, _PIO_CACHE_DIRS) + penv = core_dir / "penv" + if penv.is_dir(): + _LOGGER.info("Deleting PlatformIO penv %s", penv) + rmtree(penv) + + +def _current_python_minor() -> str: + """Return the running interpreter's ``major.minor`` (e.g. ``3.13``).""" + return f"{sys.version_info.major}.{sys.version_info.minor}" + + +def _read_pio_stamp_python(stamp_file: Path) -> str | None: + """Return the ``python_version`` recorded in *stamp_file*, or None.""" + try: + with stamp_file.open(encoding="utf-8") as f: + data = json.load(f) + except FileNotFoundError: + return None + except (json.JSONDecodeError, OSError) as err: + # A present-but-unreadable stamp is a distinct signal from an absent + # one, and it drives a cache clean; surface why at normal verbosity. + _LOGGER.warning("Could not read %s: %s", stamp_file, err) + return None + if not isinstance(data, dict): + return None + version = data.get("python_version") + return version if isinstance(version, str) else None + + +def _write_pio_stamp_python(stamp_file: Path, python_version: str) -> None: + """Atomically write the PlatformIO python-version stamp.""" + write_file( + stamp_file, + json.dumps( + { + "schema_version": _PIO_PYTHON_STAMP_SCHEMA, + "python_version": python_version, + } + ), + ) + + +def heal_platformio_python_env() -> None: + """Wipe the PlatformIO cache unless it is stamped for the running Python. + + A PlatformIO platform/tool package pins the Python versions it accepts when + it is provisioned, and ESPHome pins platforms to exact, immutable versions, + so a later interpreter bump (a container upgrading its base Python) leaves + the cached platform rejecting the new interpreter ("Python version must be + between ...") until the cache is wiped. A stamp records the ``major.minor`` + the cache was provisioned for; when it doesn't match the running + interpreter (or has never been written for an existing cache), the same + PlatformIO dirs ``clean-all`` wipes are cleaned so PlatformIO + re-provisions, matching Reset Build Environment automatically. The native + ESP-IDF toolchain already self-heals through its own stamp; this covers the + PlatformIO path. No-op when PlatformIO is unavailable. + """ + config = get_platformio_config() + if config is None: + return + try: + _check_platformio_python_stamp(config) + except (EsphomeError, OSError) as err: + # The check is a best-effort repair; a full or read-only cache volume + # must not abort a build that might otherwise work. The stamp write + # surfaces as EsphomeError (write_file wraps OSError). + _LOGGER.warning("PlatformIO build environment check failed: %s", err) + + +def _check_platformio_python_stamp(config: "ProjectConfig") -> None: + """Compare the stamp to the running interpreter; wipe and restamp on mismatch.""" + current = _current_python_minor() + stamp_dir = _pio_stamp_dir(config) + # Host the stamp/lock even before PlatformIO's first run creates the dir. + stamp_dir.mkdir(parents=True, exist_ok=True) + stamp_file = stamp_dir / _PIO_PYTHON_STAMP_FILE + + from filelock import FileLock + + with FileLock(str(stamp_dir / _PIO_PYTHON_STAMP_LOCK)): + provisioned = _read_pio_stamp_python(stamp_file) + if provisioned == current: + return + core_dir = Path(config.get("platformio", "core_dir")) + has_cache = ( + any( + Path(config.get("platformio", pio_dir)).is_dir() + for pio_dir in _PIO_CACHE_DIRS + ) + or (core_dir / "penv").is_dir() + ) + if has_cache: + if provisioned is None: + # An existing cache with no stamp predates the stamp: its + # provisioning interpreter is unknown, so clean once rather + # than leave a possibly-stale cache failing every build. + _LOGGER.info( + "Cleaning the PlatformIO build environment once so it " + "re-provisions for Python %s", + current, + ) + else: + _LOGGER.info( + "Python version changed (%s -> %s); cleaning PlatformIO " + "build environment so it re-provisions for the new " + "interpreter", + provisioned, + current, + ) + _clean_platformio_python_env(config, core_dir) + _write_pio_stamp_python(stamp_file, current) + + +def _ccache_env() -> dict[str, str]: + """Return ccache settings for PlatformIO builds. + + Enabled by default whenever the ``ccache`` binary is on PATH; set + ``ESPHOME_CCACHE_ENABLE=0`` in the environment to opt out (or ``1`` to + force it on). The decision is normalized into ``ESPHOME_CCACHE_ENABLE`` + so platform build scripts (e.g. the esp8266 ``ccache.py`` extra script, + which wraps compiler invocations inside SCons) only have to check for + ``"1"`` instead of re-implementing the policy. + + The returned values are merged into the environment of the PlatformIO + subprocess only, never into ``os.environ``: a long-running process + (e.g. the dashboard) also runs ESP-IDF builds, whose own ccache setup + skips defaults for ``CCACHE_*`` keys it finds already set, so leaking + these values would hand it the wrong cache dir and a stale basedir. + + This mirrors ``_ccache_env()`` in ``esphome/espidf/framework.py``. The + cache lives under the machine-global ESPHome cache dir, so it is shared + across all projects and removed by ``esphome clean-all``. Unlike the + ESP-IDF path, ``CCACHE_DEPEND`` is not set: SCons compiles don't emit + the depfiles depend mode needs, so ccache's default preprocessor mode + is used. + + ``CCACHE_BASEDIR`` rewrites the per-device absolute paths (the generated + sources under src/, the .pioenvs build dir) so different devices with + identical source share cache entries; it is always set to the current + build dir. The other ``CCACHE_*`` values the user already set in the + environment are respected. + """ + if "ESPHOME_CCACHE_ENABLE" in os.environ: + enabled = get_bool_env("ESPHOME_CCACHE_ENABLE") + else: + enabled = shutil.which("ccache") is not None + env = {"ESPHOME_CCACHE_ENABLE": "1" if enabled else "0"} + if not enabled: + return env + # build_path is set during preload for every config-loading command, so it + # being unset means a caller built the environment too early; fail loudly + # rather than with an opaque TypeError from Path(None). + if CORE.build_path is None: + raise ValueError( + "CORE.build_path must be set before constructing the PlatformIO " + "build environment" + ) + env["CCACHE_BASEDIR"] = str(Path(CORE.build_path).resolve()) + defaults = { + "CCACHE_DIR": str( + Path(platformdirs.user_cache_dir("esphome", appauthor=False)) + / "platformio-ccache" + ), + "CCACHE_NOHASHDIR": "true", + } + env.update({k: v for k, v in defaults.items() if k not in os.environ}) + return env + + +def copy_ccache_script() -> None: + """Copy the shared ccache SCons pre-script into the build dir. + + Platform components call this from their ``copy_files()`` and add + ``pre:ccache.py`` to their ``extra_scripts``. The script wraps compiler + invocations inside SCons with ccache; it is platform-agnostic, so it + lives here next to ``_ccache_env()`` rather than being duplicated per + component. + """ + copy_file_if_changed( + Path(__file__).parent / "ccache.py.script", + CORE.relative_build_path("ccache.py"), + ) + + def run_platformio_cli(*args, **kwargs) -> str | int: + # Re-provision the PlatformIO cache if the interpreter's major.minor changed + # since it was last built; a stale platform otherwise rejects the new Python + # with "Python version must be between ..." until Reset Build Environment. + heal_platformio_python_env() os.environ["PLATFORMIO_FORCE_COLOR"] = "true" os.environ["PLATFORMIO_BUILD_DIR"] = str(CORE.relative_pioenvs_path().absolute()) os.environ.setdefault( @@ -71,7 +336,14 @@ def run_platformio_cli(*args, **kwargs) -> str | int: os.environ["PYTHONEXEPATH"] = python_exe cmd = [python_exe, "-m", "esphome.platformio.runner"] + list(args) - return run_external_process(*cmd, **kwargs) + # ccache settings go into the subprocess environment only (see + # _ccache_env() for why they must not leak into os.environ). A caller + # supplied env is used as the base when present. + base_env = kwargs.pop("env", None) + env = dict(os.environ if base_env is None else base_env) + env.update(_ccache_env()) + + return run_external_process(*cmd, env=env, **kwargs) def run_platformio_cli_run(config, verbose, *args, **kwargs) -> str | int: diff --git a/esphome/util.py b/esphome/util.py index b597b4b42e..f7d33bd2a9 100644 --- a/esphome/util.py +++ b/esphome/util.py @@ -314,6 +314,7 @@ def run_external_process(*cmd: str, **kwargs: Any) -> int | str: encoding="utf-8", check=False, close_fds=False, + env=kwargs.get("env"), ) return proc.stdout if capture_stdout else proc.returncode except KeyboardInterrupt: # pylint: disable=try-except-raise diff --git a/esphome/writer.py b/esphome/writer.py index b7eeec916d..866377d2f5 100644 --- a/esphome/writer.py +++ b/esphome/writer.py @@ -670,18 +670,9 @@ def clean_all(configuration: list[str]): rmtree(install_path) # Clean PlatformIO project files - try: - from platformio.project.config import ProjectConfig - except ImportError: - # PlatformIO is not available, skip cleaning - pass - else: - config = ProjectConfig.get_instance() - for pio_dir in ["cache_dir", "packages_dir", "platforms_dir", "core_dir"]: - path = Path(config.get("platformio", pio_dir)) - if path.is_dir(): - _LOGGER.info("Deleting PlatformIO %s %s", pio_dir, path) - rmtree(path) + from esphome.platformio.toolchain import clean_platformio_cache + + clean_platformio_cache() GITIGNORE_CONTENT = """# Gitignore settings for ESPHome diff --git a/platformio.ini b/platformio.ini index 061e92a64a..9f2ac74ac0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -45,7 +45,7 @@ lib_deps_base = lib_deps = ${common.lib_deps_base} esphome/noise-c@0.1.11 ; api - improv/Improv@1.2.4 ; improv_serial / esp32_improv + improv/Improv@1.2.6 ; improv_serial / esp32_improv kikuchan98/pngle@1.1.0 ; online_image ; Using the repository directly, otherwise ESP-IDF can't use the library https://github.com/bitbank2/JPEGDEC.git#1.8.4 ; online_image @@ -141,10 +141,10 @@ extra_scripts = post:esphome/components/esp8266/post_build.py.script ; This are common settings for the ESP32 (all variants) using Arduino. [common:esp32-arduino] extends = common:arduino -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.39/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip platform_packages = - pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.3.9/esp32-core-3.3.9.tar.xz - pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.4/esp-idf-v5.5.4.tar.xz + pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/3.3.11/esp32-core-3.3.11.tar.xz + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.5/esp-idf-v5.5.5.tar.xz framework = arduino, espidf ; Arduino as an ESP-IDF component lib_deps = @@ -178,9 +178,9 @@ extra_scripts = ; This are common settings for the ESP32 (all variants) using IDF. [common:esp32-idf] extends = common:idf -platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.39/platform-espressif32.zip +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.311/platform-espressif32.zip platform_packages = - pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.4/esp-idf-v5.5.4.tar.xz + pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v5.5.5/esp-idf-v5.5.5.tar.xz framework = espidf lib_deps = @@ -214,8 +214,19 @@ lib_deps = ${common:idf-component-libs.lib_deps} ayushsharma82/RPAsyncTCP@1.3.2 ; async_tcp ESP32Async/ESPAsyncWebServer@3.9.6 ; web_server_base + WiFi ; wifi (arduino-pico built-in) + lwIP_CYW43 ; wifi (arduino-pico built-in, WiFi dependency) + HTTPClient ; http_request (arduino-pico built-in) + Updater ; ota (arduino-pico built-in) + MD5Builder ; md5 (arduino-pico built-in) + LEAmDNS ; mdns (arduino-pico built-in) + lwIP_w5500 ; ethernet (arduino-pico built-in) + lwIP-Ethernet ; ethernet (arduino-pico built-in, lwIP_w5500/lwIP_CYW43 dependency) + WebServer ; web_server_base (arduino-pico built-in, ESPAsyncWebServer dependency) + http-parser ; web_server_base (arduino-pico built-in, ESPAsyncWebServer dependency) build_flags = ${common:arduino.build_flags} + -DUSE_RP2 -DUSE_RP2040 -DUSE_RP2040_FRAMEWORK_ARDUINO build_unflags = @@ -228,9 +239,17 @@ platform = https://github.com/libretiny-eu/libretiny.git#v1.13.0 framework = arduino lib_compat_mode = soft lib_deps = + ${common.lib_deps_base} ${common:idf-component-libs.lib_deps} ESP32Async/ESPAsyncWebServer@3.9.6 ; web_server_base droscy/esp_wireguard@0.4.5 ; wireguard + esphome/noise-c@0.1.11 ; api + ESP32Async/AsyncTCP@3.4.5 ; async_tcp + DNSServer ; captive_portal + heman/AsyncMqttClient-esphome@2.0.0 ; mqtt + improv/Improv@1.2.6 ; improv_serial + kikuchan98/pngle@1.1.0 ; online_image + https://github.com/bitbank2/JPEGDEC.git#1.8.4 ; online_image build_flags = ${common:arduino.build_flags} -DUSE_LIBRETINY @@ -510,6 +529,17 @@ build_flags = build_unflags = ${common.build_unflags} +[env:rp2-tidy] +extends = common:rp2040-arduino +; The W variant so the cyw43 / WiFi library paths are part of the idedata. +board = rpipicow +build_flags = + ${common:rp2040-arduino.build_flags} + ${flags:clangtidy.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH +build_unflags = + ${common.build_unflags} + ;;;;;;;; LibreTiny ;;;;;;;; [env:bk72xx-arduino] @@ -556,6 +586,54 @@ build_flags = build_unflags = ${common.build_unflags} +[env:bk72xx-tidy] +extends = common:libretiny-arduino +board = generic-bk7231n-qfn32-tuya +build_flags = + ${common:libretiny-arduino.build_flags} + ${flags:clangtidy.build_flags} + -DUSE_BK72XX + -DUSE_LIBRETINY_VARIANT_BK7231N +build_unflags = + ${common.build_unflags} + +[env:ln882h-tidy] +extends = common:libretiny-arduino +board = generic-ln882h +build_flags = + ${common:libretiny-arduino.build_flags} + ${flags:clangtidy.build_flags} + -DUSE_LN882X + -DUSE_LIBRETINY_VARIANT_LN882H + ; the SDK lwip port dir is missing from pio idedata; lwipopts.h include_next needs it + -I${platformio.packages_dir}/framework-lightning-ln882h/components/net/lwip-2.1.3/src/port/ln_osal/include +build_unflags = + ${common.build_unflags} + +[env:rtl87xxb-tidy] +extends = common:libretiny-arduino +board = generic-rtl8710bn-2mb-788k +; mirror the libretiny codegen pin: RTL8710B needs 8.2.3+ for task notifications +custom_versions.freertos = 8.2.3 +build_flags = + ${common:libretiny-arduino.build_flags} + ${flags:clangtidy.build_flags} + -DUSE_RTL87XX + -DUSE_LIBRETINY_VARIANT_RTL8710B +build_unflags = + ${common.build_unflags} + +[env:rtl87xxc-tidy] +extends = common:libretiny-arduino +board = generic-rtl8720cf-2mb-992k +build_flags = + ${common:libretiny-arduino.build_flags} + ${flags:clangtidy.build_flags} + -DUSE_RTL87XX + -DUSE_LIBRETINY_VARIANT_RTL8720C +build_unflags = + ${common.build_unflags} + ;;;;;;;; Host ;;;;;;;; [env:host] diff --git a/pyproject.toml b/pyproject.toml index f38633b4ae..eda3c4cf7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,10 @@ target-version = "py312" exclude = ['generated'] [tool.ruff.lint] +# Preview mode is scoped: with explicit-preview-rules only rules named in +# select run in preview, prefixes like "PL" keep their stable set. +preview = true +explicit-preview-rules = true select = [ "B", # flake8-bugbear "BLE", # flake8-blind-except @@ -131,6 +135,7 @@ select = [ "PGH", # pygrep-hooks "PIE", # flake8-pie "PL", # pylint + "PLW1514", # require explicit encoding on text file I/O (Windows defaults to cp1252) "PTH", # flake8-use-pathlib "PYI", # flake8-pyi "Q", # flake8-quotes @@ -151,6 +156,7 @@ ignore = [ "PLR0912", # Too many branches ({branches} > {max_branches}) "PLR0913", # Too many arguments to function call ({c_args} > {max_args}) "PLR0915", # Too many statements ({statements} > {max_statements}) + "PLR0917", # Too many positional arguments ({c_pos} > {max_pos}) "PLW1641", # Object does not implement `__hash__` method "PLR2004", # Magic value used in comparison, consider replacing {value} with a constant variable "PLW2901", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target diff --git a/requirements.txt b/requirements.txt index 5f98111445..6260e4a44a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,7 @@ -cryptography==49.0.0 +# cryptography 49+ ships no Intel macOS wheels (arm64 only); esptool caps <49 there. +# Keep 48.0.1, the last universal2 release, so esphome stays installable on Intel Macs. +cryptography==49.0.0; platform_system != "Darwin" or platform_machine != "x86_64" +cryptography==48.0.1; platform_system == "Darwin" and platform_machine == "x86_64" voluptuous==0.16.0 PyYAML==6.0.3 paho-mqtt==1.6.1 @@ -9,7 +12,7 @@ pyserial==3.5 platformio==6.1.19 esptool==5.3.1 click==8.3.3 -aioesphomeapi==45.6.0 +aioesphomeapi==45.7.0 zeroconf==0.150.0 puremagic==2.2.0 ruamel.yaml==0.19.1 # dashboard_import @@ -23,7 +26,8 @@ bleak==2.1.1 smpclient==7.2.0 requests==2.34.2 py7zr==1.1.3 -platformdirs==4.10.0 # native esp-idf toolchain global cache dir +platformdirs==4.11.0 # native esp-idf toolchain global cache dir +filelock==3.32.0 # inter-process locks (PlatformIO cache heal, git clone cache); >=3.32 for FileLock(fallback_to_soft=...), older versions silently drop the kwarg # esp-idf >= 5.0 requires this pyparsing >= 3.3.2 diff --git a/requirements_test.txt b/requirements_test.txt index 7aa8dab534..e7de8eb5e7 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,6 +1,6 @@ pylint==4.0.6 flake8==7.3.0 # also change in .pre-commit-config.yaml when updating -ruff==0.15.21 # also change in .pre-commit-config.yaml when updating +ruff==0.16.0 # also change in .pre-commit-config.yaml when updating pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating pre-commit diff --git a/script/check_import_time.py b/script/check_import_time.py index 0d5362c968..0f2b395902 100755 --- a/script/check_import_time.py +++ b/script/check_import_time.py @@ -194,7 +194,7 @@ def cmd_update(args: argparse.Namespace) -> int: def cmd_har_only(args: argparse.Namespace) -> int: - Path(args.har).write_text(run_waterfall(TARGET_MODULE)) + Path(args.har).write_text(run_waterfall(TARGET_MODULE), encoding="utf-8") print(f"Wrote waterfall HAR to {args.har}") return 0 diff --git a/script/ci-custom.py b/script/ci-custom.py index 4b16734ebe..90748a13b9 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -345,9 +345,11 @@ def lint_const_ordered(fname, content): ( mi, 1, - f"Constant {highlight(mline)} is not ordered, please make sure all " - f"constants are ordered. See line {mi} (should go to line {target}, " - f"{target_text})", + ( + f"Constant {highlight(mline)} is not ordered, please make sure all " + f"constants are ordered. See line {mi} (should go to line {target}, " + f"{target_text})" + ), ) ) return errs @@ -990,12 +992,14 @@ def lint_log_multiline_continuation(fname, content): ( lineno, col, - "Multi-line log message has a continuation line that does " - "not start with a space. The log viewer uses leading " - "whitespace to detect continuation lines and re-add the " - f"log tag prefix (e.g. {highlight('[C][component:042]:')}).\n" - "Either start the continuation with a space/indent, or " - "split into separate ESP_LOG* calls.", + ( + "Multi-line log message has a continuation line that does " + "not start with a space. The log viewer uses leading " + "whitespace to detect continuation lines and re-add the " + f"log tag prefix (e.g. {highlight('[C][component:042]:')}).\n" + "Either start the continuation with a space/indent, or " + "split into separate ESP_LOG* calls." + ), ) ) return errs @@ -1073,10 +1077,12 @@ def lint_test_package_key_matches_bus(fname, content): ( lineno, 1, - f"Package key {highlight(pkg_key)} does not match bus directory " - f"{highlight(bus_dir)}. The package key must match the directory " - f"name under tests/test_build_components/common/. " - f"Change {highlight(pkg_key)} to {highlight(bus_dir)}.", + ( + f"Package key {highlight(pkg_key)} does not match bus directory " + f"{highlight(bus_dir)}. The package key must match the directory " + f"name under tests/test_build_components/common/. " + f"Change {highlight(pkg_key)} to {highlight(bus_dir)}." + ), ) ) return errs diff --git a/script/ci_check_test_fixture_list_form.py b/script/ci_check_test_fixture_list_form.py new file mode 100755 index 0000000000..6da1f8337d --- /dev/null +++ b/script/ci_check_test_fixture_list_form.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Fail when a test fixture writes a platform-list domain as a single dict. + +Component tests are merged and built in groups in CI (see +``script/merge_component_configs.py``). ESPHome's ``merge_config`` concatenates +two lists, but when one side is a dict it replaces the other side wholesale +(``esphome/config_helpers.py``). A domain such as ``one_wire:`` or ``ota:`` +written in single-dict form therefore deletes every entry other components +contributed to that domain before it in the merge, and is itself deleted by any +list that merges after it. The resulting failure only appears when the affected +components land in the same group -- usually a full component matrix run on an +unrelated PR long after the fixture was written (this is what broke the +dallas_temp tests when ds2484 was added, see #17868). + +This guard scans every fixture under ``tests/components/`` and rejects any +top-level domain written as a dict with a ``platform`` key. Such a domain is by +definition a platform list (single-dict form is only user-config sugar), so the +fix is always to write it as a one-element list: + + one_wire: + - platform: gpio + pin: 4 +""" + +from __future__ import annotations + +from pathlib import Path +import sys + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from esphome.core import EsphomeError # noqa: E402 +from script.analyze_component_buses import ISOLATED_COMPONENTS # noqa: E402 +from script.merge_component_configs import load_yaml_file # noqa: E402 + +# Resolved relative to this file (not the CWD) so the scan cannot silently cover +# nothing when run from a different directory. +ROOT_DIR = Path(__file__).resolve().parent.parent +TESTS_DIR = ROOT_DIR / "tests" / "components" + + +def main() -> int: + offenders: list[str] = [] + parse_errors: list[str] = [] + fixtures_scanned = 0 + + for fixture in sorted(TESTS_DIR.glob("*/*.yaml")): + # Isolated components are never merged with others, so dict form + # cannot clobber anyone there. + if fixture.parent.name in ISOLATED_COMPONENTS: + continue + try: + data = load_yaml_file(fixture) + except EsphomeError as err: + parse_errors.append(f"{fixture.relative_to(ROOT_DIR)}: {err}") + continue + fixtures_scanned += 1 + if not isinstance(data, dict): + continue + for key, value in data.items(): + if isinstance(value, dict) and "platform" in value: + offenders.append(f"{fixture.relative_to(ROOT_DIR)}: '{key}:'") + + if offenders: + print("Test fixtures with platform domains in single-dict form:\n") + for line in offenders: + print(f" - {line}") + print( + "\nWrite the domain as a one-element list ('- platform: ...') so " + "grouped CI builds can merge it with other components' entries; " + "in dict form it replaces or is replaced by their lists wholesale." + ) + + if parse_errors: + # A fixture we could not parse was never scanned, so the run is not a + # clean pass even if no offenders were found among the rest. + print( + f"\n{len(parse_errors)} test fixture(s) could not be parsed and " + "were not checked:" + ) + for line in parse_errors: + print(f" - {line}") + + if fixtures_scanned == 0: + # A scan that covered nothing is a false green -- the whole point of the + # guard is defeated. Fail loudly (wrong working directory or layout change). + print( + f"\nERROR: scanned 0 test fixtures under {TESTS_DIR}; " + "the guard covered nothing.", + file=sys.stderr, + ) + + if offenders or parse_errors or fixtures_scanned == 0: + return 1 + + print( + f"No single-dict platform domains found ({fixtures_scanned} fixtures scanned)." + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/script/ci_memory_impact_extract.py b/script/ci_memory_impact_extract.py index 20a737cdbf..2d74362169 100755 --- a/script/ci_memory_impact_extract.py +++ b/script/ci_memory_impact_extract.py @@ -33,6 +33,11 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) # pylint: disable=wrong-import-position from esphome.analyze_memory import MemoryAnalyzer +from esphome.analyze_memory.toolchain import ( + find_elf_path, + find_idedata_path, + idedata_candidates, +) from esphome.platformio.toolchain import IDEData from script.ci_helpers import write_github_output @@ -130,53 +135,31 @@ def run_detailed_analysis(build_dir: str) -> dict | None: print(f"Build directory not found: {build_dir}", file=sys.stderr) return None - # Find firmware.elf (or raw_firmware.elf for LibreTiny) - elf_path = None - for elf_candidate in [ - build_path / "firmware.elf", - build_path / ".pioenvs" / build_path.name / "firmware.elf", - # LibreTiny uses raw_firmware.elf - build_path / "raw_firmware.elf", - build_path / ".pioenvs" / build_path.name / "raw_firmware.elf", - ]: - if elf_candidate.exists(): - elf_path = str(elf_candidate) - break - + elf_path = find_elf_path(build_path) if not elf_path: - print( - f"firmware.elf/raw_firmware.elf not found in {build_dir}", file=sys.stderr - ) + print(f"No firmware ELF found in {build_dir}", file=sys.stderr) return None - # Find idedata.json - check multiple locations - device_name = build_path.name - idedata_candidates = [ - # In .pioenvs for test builds - build_path / ".pioenvs" / device_name / "idedata.json", - # In .esphome/idedata for regular builds - Path.home() / ".esphome" / "idedata" / f"{device_name}.json", - # Check parent directories for .esphome/idedata (for test_build_components) - build_path.parent.parent.parent / "idedata" / f"{device_name}.json", - ] - idedata = None - for idedata_path in idedata_candidates: - if not idedata_path.exists(): - continue + if idedata_path := find_idedata_path(build_path): try: with idedata_path.open(encoding="utf-8") as f: raw_data = json.load(f) idedata = IDEData(raw_data) print(f"Loaded idedata from: {idedata_path}", file=sys.stderr) - break except (json.JSONDecodeError, OSError) as e: print( f"Warning: Failed to load idedata from {idedata_path}: {e}", file=sys.stderr, ) + else: + # Without idedata the analyzer falls back to whatever binutils are on + # PATH, which are the wrong architecture for a cross build, so say where + # we looked rather than let the results quietly get worse. + searched = "\n ".join(str(p) for p in idedata_candidates(build_path)) + print(f"Warning: idedata not found, searched:\n {searched}", file=sys.stderr) - analyzer = MemoryAnalyzer(elf_path, idedata=idedata) + analyzer = MemoryAnalyzer(str(elf_path), idedata=idedata) components = analyzer.analyze() # Convert to JSON-serializable format @@ -286,7 +269,7 @@ def main() -> int: if args.output_build_dir and build_dir: build_dir_path = Path(args.output_build_dir) build_dir_path.parent.mkdir(parents=True, exist_ok=True) - build_dir_path.write_text(build_dir) + build_dir_path.write_text(build_dir, encoding="utf-8") print(f"Wrote build directory to {args.output_build_dir}", file=sys.stderr) # Run detailed analysis if build directory available @@ -320,6 +303,19 @@ def main() -> int: else: print(f"{ram_bytes},{flash_bytes}") + # The build produced usable totals, so a missing detailed analysis means the + # build layout moved out from under this script rather than a broken build. + # Fail loudly: the comment would otherwise silently drop the component + # breakdown and the symbol tables, which is easy to miss for a long time. + if detailed_analysis is None: + print( + "::error::Detailed memory analysis unavailable even though the build " + f"succeeded (build directory: {build_dir or 'not detected'}). The PR " + "comment would be missing its component breakdown and symbol changes.", + file=sys.stderr, + ) + return 1 + return 0 diff --git a/script/clang-tidy b/script/clang-tidy index 7df46cb2d2..4f1bc6021c 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -29,7 +29,7 @@ from helpers import ( ) -def clang_options(idedata): +def clang_options(idedata, environment): cmd = [] # extract target architecture from triplet in g++ filename @@ -74,6 +74,8 @@ def clang_options(idedata): "-fno-jump-tables", "-fno-shrink-wrap", "-mno-target-align", + # GCC-only flag emitted by the LibreTiny build + "-mthumb-interwork", ) if "zephyr" in triplet: @@ -95,30 +97,59 @@ def clang_options(idedata): [ # disable built-in include directories from the host "-nostdinc", - # replace pgmspace.h, as it uses GNU extensions clang doesn't support - # https://github.com/earlephilhower/newlib-xtensa/pull/18 - "-D_PGMSPACE_H_", - "-Dpgm_read_byte(s)=(*(const uint8_t *)(s))", - "-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))", - "-Dpgm_read_word(s)=(*(const uint16_t *)(s))", - "-Dpgm_read_dword(s)=(*(const uint32_t *)(s))", - "-Dpgm_read_ptr(s)=(*(const void *const *)(s))", - "-DPROGMEM=", - "-DPGM_P=const char *", - "-DPSTR(s)=(s)", - # this next one is also needed with upstream pgmspace.h - # suppress warning about identifier naming in expansion of this macro - "-DPSTRN(s, n)=(s)", - # suppress warning about attribute cannot be applied to type - # https://github.com/esp8266/Arduino/pull/8258 - "-Ddeprecated(x)=", # allow to condition code on the presence of clang-tidy "-DCLANG_TIDY", # (esp-idf) Fix __once_callable in some libstdc++ headers "-D_GLIBCXX_HAVE_TLS", + # suppress warning about attribute cannot be applied to type + # https://github.com/esp8266/Arduino/pull/8258 + # also keeps deprecation diagnostics consistent across environments + "-Ddeprecated(x)=", ] ) + if environment.startswith("rp2"): + # clang's ARM backend doesn't know GCC's long_call attribute (IRAM_ATTR) + cmd.append("-Wno-unknown-attributes") + elif environment.startswith(("bk72xx", "ln882h", "rtl87xx")): + cmd.extend( + [ + # GCC on arm-none-eabi types (u)int32_t as (unsigned) long; clang + # types it as (unsigned) int, clashing with LibreTiny's lwip + # port typedefs. Match the GCC type model. + "-U__UINT32_TYPE__", + "-D__UINT32_TYPE__=long unsigned int", + "-U__INT32_TYPE__", + "-D__INT32_TYPE__=long int", + # newlib's machine/endian.h macroizes __bswap16 into + # __builtin_bswap16; the beken BDK then defines __bswap16 as a + # function, which GCC tolerates as a builtin redeclaration but + # clang rejects + "-D__MACHINE_ENDIAN_H__", + ] + ) + else: + # replace pgmspace.h, as it uses GNU extensions clang doesn't support + # https://github.com/earlephilhower/newlib-xtensa/pull/18 + # arduino-pico ships clang-parseable pgmspace inline functions, so the + # replacements are skipped there (they clash with those definitions). + cmd.extend( + [ + "-D_PGMSPACE_H_", + "-Dpgm_read_byte(s)=(*(const uint8_t *)(s))", + "-Dpgm_read_byte_near(s)=(*(const uint8_t *)(s))", + "-Dpgm_read_word(s)=(*(const uint16_t *)(s))", + "-Dpgm_read_dword(s)=(*(const uint32_t *)(s))", + "-Dpgm_read_ptr(s)=(*(const void *const *)(s))", + "-DPROGMEM=", + "-DPGM_P=const char *", + "-DPSTR(s)=(s)", + # this next one is also needed with upstream pgmspace.h + # suppress warning about identifier naming in expansion of this macro + "-DPSTRN(s, n)=(s)", + ] + ) + # Copy compiler flags, dropping: ones clang doesn't understand; -Werror* # (clang-tidy enforces .clang-tidy's WarningsAsErrors, and a build -Werror # would bypass the -clang-diagnostic-* suppressions); and -std= (the native @@ -142,8 +173,32 @@ def clang_options(idedata): ) cmd.append("-std=gnu++20") - # defines - cmd.extend(f"-D{define}" for define in idedata["defines"]) + if environment.startswith(("bk72xx", "ln882h", "rtl87xx")): + # LibreTiny leaves function-like macro values unparenthesized + # (bugprone-macro-parentheses); its SDK-internal FAL_PART_TABLE macro trips the same + # check. Assumes define values are always expressions (never type- or char-literal), + # which holds for the current LibreTiny idedata. + def sanitize_define(define): + name, sep, value = define.partition("=") + if ( + sep + and value + and not value.startswith(("(", '"')) + and ("(" in name or not re.fullmatch(r"[\w.]+", value)) + ): + value = f"({value})" + return f"-D{name}{sep}{value}" + + # FAL_PART_TABLE and the delay() remap are library-scope LibreTiny flags that the real + # build never applies to esphome sources. Strip LibreTiny's shell quoting from define + # names first so the skip list matches regardless of which names it happens to quote. + for define in idedata["defines"]: + define = define.replace("'", "") + if define.startswith(("FAL_PART_TABLE", "delay(")): + continue + cmd.append(sanitize_define(define)) + else: + cmd.extend(f"-D{define}" for define in idedata["defines"]) # toolchain include directories, using -isystem to suppress their errors # idedata contains include directories for all toolchains of this platform, only use those from the one in use @@ -207,6 +262,16 @@ def run_tidy(executable, args, options, tmpdir, path_queue, lock, failed_files): if sys.stdout.isatty(): invocation.append("--use-color") + if args.environment.startswith(("rp2", "bk72xx", "ln882h", "rtl87xx")): + # MMIO peripheral access on these bare-metal platforms is all + # fixed-address. + # bugprone-pointer-arithmetic-on-polymorphic-object (and its + # cert-ctr56-cpp alias) crashes clang-tidy 22 with infinite matcher + # recursion on lvgl_esphome.h under the RP2 defines. + invocation.append( + "--checks=-clang-analyzer-core.FixedAddressDereference," + "-bugprone-pointer-arithmetic-on-polymorphic-object,-cert-ctr56-cpp" + ) invocation.append(f"--header-filter={Path(basepath).resolve()}/.*") invocation.append(str(Path(path).resolve())) invocation.append("--") @@ -351,7 +416,7 @@ def main(): # Load idedata and options only if we have files to check idedata = load_idedata(args.environment) - options = clang_options(idedata) + options = clang_options(idedata, args.environment) tmpdir = None if args.fix: @@ -418,7 +483,9 @@ def main(): print("Error applying fixes.\n", file=sys.stderr) raise - return len(failed_files) + # Cap at 255: shells truncate exit codes to one byte, so 256 failures + # would otherwise report success + return min(len(failed_files), 255) if __name__ == "__main__": diff --git a/script/helpers.py b/script/helpers.py index 0086a00e85..6ba093b413 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -421,7 +421,10 @@ def _get_github_event_data() -> dict | None: """ github_event_path = os.environ.get("GITHUB_EVENT_PATH") if github_event_path and Path(github_event_path).exists(): - with Path(github_event_path).open() as f: + # The event payload is UTF-8 JSON; without an explicit encoding + # Windows decodes it as cp1252 and any non ASCII byte (an ellipsis in + # a commit title is enough) raises UnicodeDecodeError. + with Path(github_event_path).open(encoding="utf-8") as f: return json.load(f) return None diff --git a/script/import_time_budget.json b/script/import_time_budget.json index e810817507..cda426bb3e 100644 --- a/script/import_time_budget.json +++ b/script/import_time_budget.json @@ -1,5 +1,5 @@ { "target_module": "esphome.__main__", "margin_pct": 20, - "cumulative_us": 95000 + "cumulative_us": 37200 } diff --git a/script/setup_codspeed_lib.py b/script/setup_codspeed_lib.py index 4f5d1bff24..9ddfee27cc 100755 --- a/script/setup_codspeed_lib.py +++ b/script/setup_codspeed_lib.py @@ -84,7 +84,7 @@ def _read_codspeed_version(cmake_path: Path) -> str: """Extract CODSPEED_VERSION from core/CMakeLists.txt.""" if not cmake_path.exists(): return "0.0.0" - for line in cmake_path.read_text().splitlines(): + for line in cmake_path.read_text(encoding="utf-8").splitlines(): if line.startswith("set(CODSPEED_VERSION"): return line.split()[1].rstrip(")") return "0.0.0" diff --git a/script/test_build_components.py b/script/test_build_components.py index c733e2fa3d..ddd8a6a67d 100755 --- a/script/test_build_components.py +++ b/script/test_build_components.py @@ -367,7 +367,7 @@ def run_esphome_test( output_file = build_dir / f"{component}.{test_name}.{platform_with_version}.yaml" # Copy base file and substitute component test file reference - base_content = base_file.read_text() + base_content = base_file.read_text(encoding="utf-8") # Get relative path from build dir to test file repo_root = Path(__file__).parent.parent component_test_ref = f"../../{test_file.relative_to(repo_root / 'tests')}" @@ -524,7 +524,7 @@ def run_grouped_test( # Create test file that includes merged config output_file = build_dir / f"test_{group_name}.{platform_with_version}.yaml" - base_content = base_file.read_text() + base_content = base_file.read_text(encoding="utf-8") merged_ref = merged_config_file.name output_content = base_content.replace("$component_test_file", merged_ref) output_file.write_text(output_content) diff --git a/tests/component_tests/aqi/__init__.py b/tests/component_tests/aqi/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/aqi/test_aqi.py b/tests/component_tests/aqi/test_aqi.py new file mode 100644 index 0000000000..712c277508 --- /dev/null +++ b/tests/component_tests/aqi/test_aqi.py @@ -0,0 +1,35 @@ +"""Config-validation tests for the aqi sensor component.""" + +import pytest +from voluptuous import Invalid + +from esphome.components.aqi import CONF_CALCULATION_TYPE, CONF_EXTENDED_RANGE +from esphome.components.aqi.sensor import _validate_extended_range + + +def test_extended_range_rejected_with_caqi(): + """extended_range has no meaning for CAQI (no spec maximum) and must be rejected.""" + with pytest.raises(Invalid, match="CAQI"): + _validate_extended_range( + {CONF_CALCULATION_TYPE: "CAQI", CONF_EXTENDED_RANGE: True} + ) + + +def test_extended_range_rejected_with_caqi_even_when_false(): + """The option is not allowed at all with CAQI, regardless of its value.""" + with pytest.raises(Invalid, match="CAQI"): + _validate_extended_range( + {CONF_CALCULATION_TYPE: "CAQI", CONF_EXTENDED_RANGE: False} + ) + + +def test_extended_range_allowed_with_aqi(): + """extended_range is valid for the US AQI calculation.""" + config = {CONF_CALCULATION_TYPE: "AQI", CONF_EXTENDED_RANGE: True} + assert _validate_extended_range(config) is config + + +def test_caqi_without_extended_range_ok(): + """CAQI is fine as long as extended_range is not set.""" + config = {CONF_CALCULATION_TYPE: "CAQI"} + assert _validate_extended_range(config) is config diff --git a/tests/component_tests/bk72xx_ble_tracker/test_scan_parameter_validation.py b/tests/component_tests/bk72xx_ble_tracker/test_scan_parameter_validation.py new file mode 100644 index 0000000000..968a24dad5 --- /dev/null +++ b/tests/component_tests/bk72xx_ble_tracker/test_scan_parameter_validation.py @@ -0,0 +1,114 @@ +"""Tests for bk72xx_ble_tracker scan parameter validation.""" + +from __future__ import annotations + +import pytest + +from esphome import config_validation as cv +from esphome.components.bk72xx_ble_tracker import SCAN_PARAMETERS_SCHEMA, to_ble_units + + +def _validate(**kwargs: str) -> dict: + """Run a scan_parameters config through the schema, applying defaults.""" + return SCAN_PARAMETERS_SCHEMA(dict(kwargs)) + + +# --- to_ble_units --- + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + ("2500us", 4), # controller minimum, 2.5 ms + ("30ms", 48), + ("100ms", 160), + ("10240ms", 16384), # controller maximum, 0x4000 + ], +) +def test_to_ble_units_converts_to_controller_units(value: str, expected: int) -> None: + """A time is converted to whole 0.625 ms units.""" + assert to_ble_units(cv.positive_time_period(value)) == expected + + +def test_to_ble_units_truncates() -> None: + """Sub-unit remainders are dropped, which is what makes collapse possible.""" + assert to_ble_units(cv.positive_time_period("3000us")) == 4 + assert to_ble_units(cv.positive_time_period("2500us")) == 4 + + +# --- accepted configurations --- + + +def test_defaults_are_valid() -> None: + """The documented default 100 ms / 30 ms pair validates.""" + config = _validate() + assert to_ble_units(config["interval"]) == 160 + assert to_ble_units(config["window"]) == 48 + + +def test_minimum_separation_accepted() -> None: + """Values one unit apart at the 2.5 ms floor are honest, not collapsed.""" + config = _validate(interval="5000us", window="2500us") + assert to_ble_units(config["interval"]) == 8 + assert to_ble_units(config["window"]) == 4 + + +def test_maximum_interval_accepted() -> None: + """The documented 10240 ms ceiling is inclusive, and maps to 0x4000. + + Pins the ceiling from the accept side, mirroring the 2.5 ms floor above: the + reject cases alone would let the bound silently become exclusive. + """ + config = _validate(interval="10240ms", window="30ms") + assert to_ble_units(config["interval"]) == 16384 + + +def test_maximum_window_accepted() -> None: + """The ceiling applies to the window too, and is likewise inclusive.""" + config = _validate(interval="10240ms", window="10240ms") + assert to_ble_units(config["window"]) == 16384 + + +def test_window_equal_to_interval_accepted() -> None: + """A deliberate 100 % duty cycle is allowed; only an accidental one is not.""" + config = _validate(interval="100ms", window="100ms") + assert to_ble_units(config["interval"]) == to_ble_units(config["window"]) + + +# --- rejected configurations --- + + +def test_window_larger_than_interval_rejected() -> None: + with pytest.raises(cv.Invalid, match="needs to be smaller than scan interval"): + _validate(interval="30ms", window="100ms") + + +@pytest.mark.parametrize( + ("interval", "window", "offender"), + [ + ("2ms", "1ms", "interval"), # below the 2.5 ms controller floor + ("20s", "1s", "interval"), # above the 10240 ms controller ceiling + ("100ms", "1ms", "window"), # window below the floor + ], +) +def test_out_of_range_rejected(interval: str, window: str, offender: str) -> None: + """Values the controller cannot represent are rejected, not silently wrapped.""" + with pytest.raises( + cv.Invalid, match=f"Scan {offender} .* must be between 2.5 ms and 10240 ms" + ): + _validate(interval=interval, window=window) + + +def test_unit_collapse_rejected() -> None: + """Regression: 3000us/2500us both floor to 4 units — a hidden 100 % duty cycle. + + This is the configuration that previously validated and programmed the radio + permanently on despite asking for roughly 83 %. + """ + with pytest.raises(cv.Invalid, match="both round to 4 x 0.625 ms"): + _validate(interval="3000us", window="2500us") + + +def test_duration_shorter_than_three_intervals_rejected() -> None: + with pytest.raises(cv.Invalid, match="must cover at least three scan intervals"): + _validate(duration="1s", interval="500ms", window="100ms") diff --git a/tests/component_tests/deep_sleep/test_deep_sleep.py b/tests/component_tests/deep_sleep/test_deep_sleep.py index 84128d75d7..f105ed5888 100644 --- a/tests/component_tests/deep_sleep/test_deep_sleep.py +++ b/tests/component_tests/deep_sleep/test_deep_sleep.py @@ -33,6 +33,43 @@ def test_deep_sleep_run_duration_simple(generate_main): assert "deepsleep->set_run_duration(10000);" in main_cpp +def test_deep_sleep_on_wake_trigger(generate_main): + """ + When deep sleep is configured with a component-level on_wake automation, + a WakeTrigger component should be registered with the wakeup cause as + the automation argument. + """ + main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep3.yaml") + + assert "deep_sleep::WakeTrigger();" in main_cpp + assert "Automation" in main_cpp + + +def test_deep_sleep_ext1_on_wake_triggers(generate_main): + """ + Each esp32_ext1_wakeup pin with an on_wake automation should get its own + Ext1WakeTrigger with the pin number, and all pins (including the legacy + bare-pin shorthand) should contribute to the ext1 wakeup mask. + """ + main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep3.yaml") + + assert "deep_sleep::Ext1WakeTrigger(2);" in main_cpp + assert "deep_sleep::Ext1WakeTrigger(4);" in main_cpp + # GPIO13 has no on_wake, so no trigger is created for it + assert "deep_sleep::Ext1WakeTrigger(13)" not in main_cpp + # mask covers GPIO2, GPIO4 and GPIO13 + assert ".mask = 8212," in main_cpp + + +def test_deep_sleep_no_on_wake_no_triggers(generate_main): + """ + Without any on_wake automations, no wake trigger code should be generated. + """ + main_cpp = generate_main("tests/component_tests/deep_sleep/test_deep_sleep1.yaml") + + assert "WakeTrigger" not in main_cpp + + def test_deep_sleep_run_duration_dictionary(generate_main): """ When deep sleep is configured with dictionary run duration, it should be set. diff --git a/tests/component_tests/deep_sleep/test_deep_sleep3.yaml b/tests/component_tests/deep_sleep/test_deep_sleep3.yaml new file mode 100644 index 0000000000..71a0340b65 --- /dev/null +++ b/tests/component_tests/deep_sleep/test_deep_sleep3.yaml @@ -0,0 +1,23 @@ +esphome: + name: test + +esp32: + board: nodemcu-32s + +deep_sleep: + id: deepsleep + sleep_duration: 1min + run_duration: 10s + on_wake: + - lambda: 'ESP_LOGD("test", "cause %d", static_cast(cause));' + esp32_ext1_wakeup: + mode: ANY_HIGH + pins: + - pin: GPIO2 + on_wake: + - lambda: 'ESP_LOGD("test", "left");' + - pin: + number: GPIO4 + on_wake: + - lambda: 'ESP_LOGD("test", "right");' + - number: GPIO13 diff --git a/tests/component_tests/epaper_spi/config/t133a01_no_init_sequence.yaml b/tests/component_tests/epaper_spi/config/t133a01_no_init_sequence.yaml new file mode 100644 index 0000000000..1032927146 --- /dev/null +++ b/tests/component_tests/epaper_spi/config/t133a01_no_init_sequence.yaml @@ -0,0 +1,26 @@ +esphome: + name: test + +esp32: + board: esp32-s3-devkitc-1 + variant: esp32s3 + framework: + type: esp-idf + +spi: + clk_pin: GPIO18 + mosi_pin: GPIO19 + +display: + - platform: epaper_spi + id: epaper_display + model: t133a01 + dc_pin: GPIO21 + reset_pin: GPIO38 + cs_pin: GPIO10 + cs1_pin: GPIO2 + busy_pin: GPIO13 + update_interval: never + dimensions: + width: 200 + height: 200 diff --git a/tests/component_tests/epaper_spi/test_init.py b/tests/component_tests/epaper_spi/test_init.py index bc41f3cf11..5e2e7d6013 100644 --- a/tests/component_tests/epaper_spi/test_init.py +++ b/tests/component_tests/epaper_spi/test_init.py @@ -479,3 +479,24 @@ def test_enable_pin_code_generation( # Both pin objects must be passed to the display via set_enable_pins() as a # std::vector initializer list, in the configured order. assert f"set_enable_pins({{{pin_25}, {pin_26}}});" in main_cpp + + +def test_model_with_no_default_init_sequence_generates( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """Test that code generation succeeds for a model with no default init sequence. + + The base "t133a01" model (used directly, not via one of its `.extend()` + variants) doesn't override `get_init_sequence()` or pass `initsequence` to + its constructor, and the user didn't supply `init_sequence:` either. + `EpaperModel.get_init_sequence()` used to default to `None` in this case, + which made `flatten_sequence()` raise a `TypeError` during code + generation. Regression test for that crash. + """ + main_cpp = generate_main(component_config_path("t133a01_no_init_sequence.yaml")) + + # The generated constructor call takes (name, width, height, init_sequence, + # init_sequence_length, ...); a length of 0 confirms the empty init + # sequence array was generated instead of raising during code generation. + assert re.search(r"epaper_spi::EPaperT133A01\([^;]*,\s*\w+,\s*0\);", main_cpp) diff --git a/tests/component_tests/esp32/config/network_wifi_ethernet_priority.yaml b/tests/component_tests/esp32/config/network_wifi_ethernet_priority.yaml new file mode 100644 index 0000000000..d98d19f545 --- /dev/null +++ b/tests/component_tests/esp32/config/network_wifi_ethernet_priority.yaml @@ -0,0 +1,26 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: "test_ssid" + password: "test_password" + +ethernet: + type: W5500 + clk_pin: 19 + mosi_pin: 21 + miso_pin: 23 + cs_pin: 18 + interrupt_pin: 36 + reset_pin: 22 + clock_speed: 10Mhz + +network: + priority: + - ethernet + - wifi diff --git a/tests/component_tests/esp32/test_esp32.py b/tests/component_tests/esp32/test_esp32.py index fdca70bf2c..c374e7c964 100644 --- a/tests/component_tests/esp32/test_esp32.py +++ b/tests/component_tests/esp32/test_esp32.py @@ -108,6 +108,24 @@ def test_esp32_default_toolchain_is_esp_idf( assert CORE.toolchain == expected +@pytest.mark.parametrize( + "config_toolchain", + [Toolchain.SDK_NRF.value, "nonsense"], +) +def test_esp32_rejects_unsupported_toolchains( + set_core_config: SetCoreConfigCallable, + config_toolchain: str, +) -> None: + """Toolchains esp32 does not support are rejected at validation time.""" + set_core_config(PlatformFramework.ESP32_IDF) + + from esphome.components.esp32 import CONFIG_SCHEMA + + CORE.toolchain = None + with pytest.raises(cv.Invalid, match="Unknown value"): + CONFIG_SCHEMA({"variant": VARIANT_ESP32, "toolchain": config_toolchain}) + + @pytest.mark.parametrize( ("config", "error_match"), [ @@ -601,6 +619,23 @@ def test_network_wifi_ble_coexistence_reconciles_end_to_end( assert "CONFIG_ESP_WIFI_ENABLED" not in sdkconfig +def test_network_wifi_ethernet_priority_keeps_wifi_enabled( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """End-to-end: with both WiFi and Ethernet declared under network: priority:, + the reconciler must NOT disable the WiFi stack or coexistence (the + multi-interface case unlocked by composing network priority with the + sdkconfig reconciler).""" + generate_main(component_config_path("network_wifi_ethernet_priority.yaml")) + sdkconfig = CORE.data[KEY_ESP32][KEY_SDKCONFIG_OPTIONS] + assert "CONFIG_ESP_WIFI_ENABLED" not in sdkconfig + assert "CONFIG_SW_COEXIST_ENABLE" not in sdkconfig + # WiFi has no AP here, so SoftAP/DHCP server are still dropped. + assert sdkconfig.get("CONFIG_ESP_WIFI_SOFTAP_SUPPORT") is False + assert sdkconfig.get("CONFIG_LWIP_DHCPS") is False + + def test_esp32_build_internals_are_yaml_only() -> None: """ESP32 raw framework / build inputs are ``YAML_ONLY``. @@ -740,3 +775,28 @@ def test_signed_ota_keys_invalid_combinations(config: dict, match: str) -> None: with pytest.raises(cv.Invalid, match=match): _validate_signed_ota_keys(config) + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + # Full x.y.z versions are rewritten into pioarduino release URLs + ( + "55.3.30", + "https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30/platform-espressif32.zip", + ), + ( + "55.3.31-2", + "https://github.com/pioarduino/platform-espressif32/releases/download/55.03.31-2/platform-espressif32.zip", + ), + # Non-version values pass through untouched + ( + "https://github.com/pioarduino/platform-espressif32.git#develop", + "https://github.com/pioarduino/platform-espressif32.git#develop", + ), + ], +) +def test_parse_pio_platform_version(value: str, expected: str) -> None: + from esphome.components.esp32 import _parse_pio_platform_version + + assert _parse_pio_platform_version(value) == expected diff --git a/tests/component_tests/ethernet/__init__.py b/tests/component_tests/ethernet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/ethernet/test_ethernet.py b/tests/component_tests/ethernet/test_ethernet.py new file mode 100644 index 0000000000..b3d37561c7 --- /dev/null +++ b/tests/component_tests/ethernet/test_ethernet.py @@ -0,0 +1,37 @@ +"""Tests for the ethernet final-validation coexistence gate.""" + +import pytest +from voluptuous import Invalid + +from esphome.components.ethernet import _final_validate +from esphome.components.network import _validate_priority_list +from esphome.const import CONF_PRIORITY +import esphome.final_validate as fv + + +@pytest.fixture(autouse=True) +def _reset_full_config(): + """Reset fv.full_config so each test starts with a clean slate.""" + token = fv.full_config.set({}) + yield + fv.full_config.reset(token) + + +def test_rejects_wifi_and_ethernet_without_priority() -> None: + """Wi-Fi + ethernet without a network: priority: list must be rejected.""" + fv.full_config.set({"wifi": {}, "ethernet": {}}) + with pytest.raises(Invalid, match="cannot be used together with component wifi"): + _final_validate({}) + + +def test_rejects_wifi_and_ethernet_with_incomplete_priority() -> None: + """A priority list missing an interface is rejected and names what's missing.""" + fv.full_config.set( + { + "wifi": {}, + "ethernet": {}, + "network": {CONF_PRIORITY: _validate_priority_list(["ethernet"])}, + } + ) + with pytest.raises(Invalid, match=r"must.*list both interfaces; missing: wifi"): + _final_validate({}) diff --git a/tests/component_tests/gsl3670/test_init.py b/tests/component_tests/gsl3670/test_init.py index 3778cf8aa5..8528cf23ca 100644 --- a/tests/component_tests/gsl3670/test_init.py +++ b/tests/component_tests/gsl3670/test_init.py @@ -254,6 +254,31 @@ def test_config_seeed_model_applies_defaults(tmp_path: Path) -> None: assert CONF_RESET_PIN in result +def test_config_guition_model_applies_defaults(tmp_path: Path) -> None: + """The GUITION model populates transform and calibration defaults.""" + fw = _write_firmware(tmp_path) + result = gsl.CONFIG_SCHEMA( + { + "model": "guition-jc8012p4a1", + "firmware": {"file": str(fw)}, + } + ) + assert result[CONF_MODEL] == "GUITION-JC8012P4A1" + # Transform defaults from the model. + assert result[CONF_TRANSFORM] == { + "swap_xy": True, + "mirror_x": True, + "mirror_y": False, + } + # Calibration defaults from the model. + assert result[CONF_CALIBRATION]["x_min"] == 20 + assert result[CONF_CALIBRATION]["x_max"] == 880 + assert result[CONF_CALIBRATION]["y_min"] == 20 + assert result[CONF_CALIBRATION]["y_max"] == 1648 + assert result[CONF_INTERRUPT_PIN]["number"] == 21 + assert result[CONF_RESET_PIN]["number"] == 22 + + def test_config_rejects_non_dict() -> None: """A non-dict configuration is rejected.""" with pytest.raises(cv.Invalid, match="expected a dictionary"): diff --git a/tests/component_tests/lvgl/test_multi_conf_validate.py b/tests/component_tests/lvgl/test_multi_conf_validate.py new file mode 100644 index 0000000000..b63b7618e7 --- /dev/null +++ b/tests/component_tests/lvgl/test_multi_conf_validate.py @@ -0,0 +1,55 @@ +"""Tests for LVGL's multi-instance config cross-checks.""" + +from __future__ import annotations + +import pytest + +from esphome.components.lvgl import defines as df, multi_conf_validate +from esphome.components.lvgl.schemas import theme_schema +from esphome.config_validation import Invalid + + +def _config(displays: list[str], theme: dict | None = None) -> dict: + config = { + df.CONF_DISPLAYS: displays, + "log_level": "WARN", + "color_depth": 16, + "byte_order": "big_endian", + df.CONF_TRANSPARENCY_KEY: 0x000400, + } + if theme is not None: + config[df.CONF_THEME] = theme + return config + + +class TestThemeOnMultipleInstances: + def test_raises_when_two_instances_have_theme(self) -> None: + configs = [ + _config(["disp_a"], theme={df.CONF_DARK_MODE: True}), + _config(["disp_b"], theme={df.CONF_DARK_MODE: False}), + ] + with pytest.raises(Invalid, match="'theme' may only be set on one"): + multi_conf_validate(configs) + + def test_raises_even_with_an_empty_theme_block(self) -> None: + # `theme: {}` still creates a CONF_THEME key (with dark_mode defaulted + # by the schema), so it should be treated the same as a populated one. + # Run it through the real schema rather than hand-building the dict, + # so this actually pins that defaulting behaviour. + configs = [ + _config(["disp_a"], theme=theme_schema({})), + _config(["disp_b"], theme=theme_schema({})), + ] + with pytest.raises(Invalid, match="'theme' may only be set on one"): + multi_conf_validate(configs) + + def test_passes_when_only_one_instance_has_theme(self) -> None: + configs = [ + _config(["disp_a"], theme={df.CONF_DARK_MODE: True}), + _config(["disp_b"]), + ] + multi_conf_validate(configs) + + def test_passes_when_no_instance_has_theme(self) -> None: + configs = [_config(["disp_a"]), _config(["disp_b"])] + multi_conf_validate(configs) diff --git a/tests/component_tests/lvgl/test_schema_dict_helpers.py b/tests/component_tests/lvgl/test_schema_dict_helpers.py index 16714f54d7..c8b3a76bf9 100644 --- a/tests/component_tests/lvgl/test_schema_dict_helpers.py +++ b/tests/component_tests/lvgl/test_schema_dict_helpers.py @@ -13,12 +13,7 @@ import pytest import voluptuous as vol from esphome import config_validation as cv -import esphome.components.lvgl -from esphome.components.lvgl import ( - _theme_schema, - defines as df, - schemas as lvgl_schemas, -) +from esphome.components.lvgl import defines as df, schemas as lvgl_schemas from esphome.components.lvgl.schemas import ( ALIGN_TO_SCHEMA, FLAG_SCHEMA, @@ -31,6 +26,8 @@ from esphome.components.lvgl.schemas import ( obj_schema, part_dict, part_schema, + theme_schema, + theme_update_schema, ) from esphome.components.lvgl.types import LvType from esphome.components.lvgl.widgets import WidgetType @@ -43,7 +40,7 @@ def _clear_obj_dict_cache() -> Generator[None]: cache.clear() # The lazily-built theme schema is cached on _build_theme_schema; clear it # too so each test starts from a clean slate. - build_theme = getattr(esphome.components.lvgl, "_build_theme_schema", None) + build_theme = getattr(lvgl_schemas, "_build_theme_schema", None) if build_theme is not None and hasattr(build_theme, "cache_clear"): build_theme.cache_clear() yield @@ -173,12 +170,12 @@ def test_spread_sources_carry_no_extra_schemas(schema: cv.Schema) -> None: def test_theme_schema_merges_obj_dict_and_full_style_props() -> None: - # _theme_schema is the riskiest merge: obj_dict(w) and FULL_STYLE_SCHEMA.schema + # theme_schema is the riskiest merge: obj_dict(w) and FULL_STYLE_SCHEMA.schema # share many STYLE_SCHEMA marker instances. Exercise the merged schema # end-to-end with one key from each side (a STATE_SCHEMA part from obj_dict # and a FULL_STYLE-only property) to lock the behaviour against future # regressions in either source. - out = _theme_schema( + out = theme_schema( { df.CONF_DARK_MODE: True, "obj": { @@ -202,7 +199,7 @@ def test_theme_schema_self_heals_when_a_widget_type_is_registered_later() -> Non # any_widget_schema explicitly supports external components registering # widgets lazily, and the device builder revalidates in-process, so a # widget registered after first use must invalidate the cached snapshot. - _theme_schema({df.CONF_DARK_MODE: True}) # populate the cache + theme_schema({df.CONF_DARK_MODE: True}) # populate the cache name = "test_self_heal_widget" assert name not in WIDGET_TYPES @@ -210,18 +207,68 @@ def test_theme_schema_self_heals_when_a_widget_type_is_registered_later() -> Non # manually so the next theme call sees the new entry. WIDGET_TYPES[name] = WidgetType(name, LvType("test_fake_t"), (), is_mock=True) try: - out = _theme_schema({df.CONF_DARK_MODE: False, name: {"bg_color": 0x010203}}) + out = theme_schema({df.CONF_DARK_MODE: False, name: {"bg_color": 0x010203}}) assert out[name]["bg_color"] == 0x010203 finally: WIDGET_TYPES.pop(name, None) +@pytest.mark.parametrize( + ("config", "expected_path"), + [ + ({"button": {"styles": ["foo"]}}, ["button", "styles"]), + ( + {"button": {"pressed": {"styles": ["foo"]}}}, + ["button", "pressed", "styles"], + ), + ( + {"arc": {"indicator": {"styles": ["foo"]}}}, + ["arc", "indicator", "styles"], + ), + ( + {"arc": {"indicator": {"pressed": {"styles": ["foo"]}}}}, + ["arc", "indicator", "pressed", "styles"], + ), + ], +) +def test_theme_schema_rejects_styles_key( + config: dict, expected_path: list[str] +) -> None: + # `styles:` (references to named styles) is accepted by FULL_STYLE_SCHEMA + # but silently dropped by style_set when building a theme's hidden style + # -- it only walks ALL_STYLES. Reject it instead of quietly doing nothing, + # at the top level and when nested under a part and/or state. + with pytest.raises(vol.Invalid, match="'styles:' is not allowed") as exc_info: + theme_schema(config) + assert exc_info.value.path == expected_path + + +def test_theme_update_schema_rejects_styles_key() -> None: + with pytest.raises(vol.Invalid, match="'styles:' is not allowed") as exc_info: + theme_update_schema({"label": {"styles": ["foo"]}}) + assert exc_info.value.path == ["label", "styles"] + + +def test_theme_update_schema_does_not_request_untargeted_main_default() -> None: + # collect_parts() unconditionally seeds a main/default entry even when + # only a specific state (here "pressed") was targeted -- registering a + # request for that spurious entry would make theme_to_code create an + # unused, empty style and attach it to every widget of this type. + theme_update_schema({"label": {"pressed": {"text_color": 0x010203}}}) + assert df.get_theme_update_requests()["label"] == {("main", "pressed"): None} + + +def test_theme_update_schema_requests_explicit_main_default() -> None: + theme_update_schema({"label": {"text_color": 0x010203}}) + assert df.get_theme_update_requests()["label"] == {("main", "default"): None} + + @pytest.mark.parametrize( "schema", [STATE_SCHEMA, FLAG_SCHEMA, STYLE_SCHEMA, FULL_STYLE_SCHEMA], ) def test_spread_sources_have_no_top_level_marker_defaults(schema: cv.Schema) -> None: - # _theme_schema merges obj_dict(w) with FULL_STYLE_SCHEMA.schema; on a key + # theme_schema merges obj_dict(w) with FULL_STYLE_SCHEMA.schema; on a key # collision, dict-spread keeps the first source's marker (and its default) # but the last source's value, whereas .extend() would take both from the # later source. The two are equivalent today because the overlapping diff --git a/tests/component_tests/micro_wake_word/__init__.py b/tests/component_tests/micro_wake_word/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/micro_wake_word/test_init.py b/tests/component_tests/micro_wake_word/test_init.py new file mode 100644 index 0000000000..5e57653585 --- /dev/null +++ b/tests/component_tests/micro_wake_word/test_init.py @@ -0,0 +1,110 @@ +"""Tests for micro_wake_word local model validation.""" + +import json +import logging +from pathlib import Path +from typing import Any + +import pytest + +from esphome.components.micro_wake_word import LOCAL_SCHEMA +from esphome.core import CORE + +MANIFEST: dict[str, Any] = { + "type": "micro", + "model": "hey_jarvis.tflite", + "author": "someone", + "version": 2, + "wake_word": "hey jarvis", + "trained_languages": ["en"], + "micro": { + "feature_step_size": 10, + "tensor_arena_size": 30000, + "probability_cutoff": 0.97, + "sliding_window_size": 5, + "minimum_esphome_version": "2024.7.0", + }, +} + + +def _registered_files() -> list[Path]: + """Files components registered for bundling this run.""" + data = CORE.data.get("bundle") + return list(data.extra_files) if data else [] + + +@pytest.fixture +def config_dir(tmp_path: Path) -> Path: + """A config dir holding a manifest and its model file.""" + (tmp_path / "models").mkdir() + (tmp_path / "models" / "hey_jarvis.tflite").write_bytes(b"fake model") + (tmp_path / "models" / "hey_jarvis.json").write_text(json.dumps(MANIFEST)) + CORE.config_path = tmp_path / "test.yaml" + return tmp_path + + +def test_local_schema_registers_model_file(config_dir: Path) -> None: + """The model file named by the manifest is registered so bundles include it.""" + LOCAL_SCHEMA({"path": "models/hey_jarvis.json"}) + + assert _registered_files() == [config_dir / "models" / "hey_jarvis.tflite"] + + +def test_local_schema_registers_model_file_in_subdirectory(config_dir: Path) -> None: + """The model reference is resolved relative to the manifest, not the config dir.""" + nested = config_dir / "models" / "nested" + nested.mkdir() + (nested / "model.tflite").write_bytes(b"fake model") + (config_dir / "models" / "nested.json").write_text( + json.dumps({**MANIFEST, "model": "nested/model.tflite"}) + ) + + LOCAL_SCHEMA({"path": "models/nested.json"}) + + assert _registered_files() == [nested / "model.tflite"] + + +def test_local_schema_leaves_config_untouched(config_dir: Path) -> None: + """Registration is a side effect; the model file is not a config key.""" + config = LOCAL_SCHEMA({"path": "models/hey_jarvis.json"}) + + assert config == {"path": config_dir / "models" / "hey_jarvis.json"} + + +def test_local_schema_missing_model_file_still_validates(config_dir: Path) -> None: + """A model file that does not exist is registered, not rejected. + + Raising here would be swallowed by the shorthand validator, which would then + report a confusing error about a missing file in a git repository. + """ + (config_dir / "models" / "hey_jarvis.tflite").unlink() + + LOCAL_SCHEMA({"path": "models/hey_jarvis.json"}) + + assert _registered_files() == [config_dir / "models" / "hey_jarvis.tflite"] + + +@pytest.mark.parametrize( + "contents", + [ + pytest.param("{not valid json", id="malformed"), + pytest.param(json.dumps({"type": "micro"}), id="no_model_key"), + pytest.param(json.dumps(["a", "list"]), id="not_an_object"), + pytest.param(json.dumps({"model": 42}), id="model_not_a_string"), + ], +) +def test_local_schema_bad_manifest_does_not_raise( + config_dir: Path, contents: str, caplog: pytest.LogCaptureFixture +) -> None: + """Manifest problems are left to later stages, which report them better. + + The skipped registration is logged so a bundle built without the model file can + be diagnosed. + """ + (config_dir / "models" / "hey_jarvis.json").write_text(contents) + + with caplog.at_level(logging.DEBUG): + LOCAL_SCHEMA({"path": "models/hey_jarvis.json"}) + + assert _registered_files() == [] + assert "Not registering a model file" in caplog.text diff --git a/tests/component_tests/network/__init__.py b/tests/component_tests/network/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/network/config/priority_ethernet_first.yaml b/tests/component_tests/network/config/priority_ethernet_first.yaml new file mode 100644 index 0000000000..d98d19f545 --- /dev/null +++ b/tests/component_tests/network/config/priority_ethernet_first.yaml @@ -0,0 +1,26 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: "test_ssid" + password: "test_password" + +ethernet: + type: W5500 + clk_pin: 19 + mosi_pin: 21 + miso_pin: 23 + cs_pin: 18 + interrupt_pin: 36 + reset_pin: 22 + clock_speed: 10Mhz + +network: + priority: + - ethernet + - wifi diff --git a/tests/component_tests/network/config/priority_wifi_first.yaml b/tests/component_tests/network/config/priority_wifi_first.yaml new file mode 100644 index 0000000000..65247f005f --- /dev/null +++ b/tests/component_tests/network/config/priority_wifi_first.yaml @@ -0,0 +1,26 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: "test_ssid" + password: "test_password" + +ethernet: + type: W5500 + clk_pin: 19 + mosi_pin: 21 + miso_pin: 23 + cs_pin: 18 + interrupt_pin: 36 + reset_pin: 22 + clock_speed: 10Mhz + +network: + priority: + - wifi + - ethernet diff --git a/tests/component_tests/network/config/wifi_only.yaml b/tests/component_tests/network/config/wifi_only.yaml new file mode 100644 index 0000000000..61dfde3e03 --- /dev/null +++ b/tests/component_tests/network/config/wifi_only.yaml @@ -0,0 +1,11 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ssid: "test_ssid" + password: "test_password" diff --git a/tests/component_tests/network/test_priority.py b/tests/component_tests/network/test_priority.py new file mode 100644 index 0000000000..da1c0a061d --- /dev/null +++ b/tests/component_tests/network/test_priority.py @@ -0,0 +1,201 @@ +"""Tests for the ``network: priority:`` list validator.""" + +from collections.abc import Callable +from pathlib import Path +import re + +import pytest +from voluptuous import Invalid + +from esphome.components.network import ( + _SETUP_PRIORITY_AFTER_WIFI, + KEY_NETWORK_PRIORITY, + NETWORK_PRIORITY_BASE, + NETWORK_PRIORITY_STEP, + _final_validate, + _validate_priority_list, + get_network_priority, +) +from esphome.const import CONF_PRIORITY +from esphome.core import CORE +import esphome.final_validate as fv + + +@pytest.fixture(autouse=True) +def _clear_core_data(): + """Wipe CORE.data and reset fv.full_config so each test starts clean.""" + CORE.data.clear() + token = fv.full_config.set({}) + yield + fv.full_config.reset(token) + CORE.data.clear() + + +def test_validates_plain_string_list() -> None: + result = _validate_priority_list(["ethernet", "wifi"]) + assert result == [{"interface": "ethernet"}, {"interface": "wifi"}] + + +def test_normalizes_mixed_case_to_lowercase() -> None: + # Regression check: mixed-case input must be lowercased so downstream + # callers like get_network_priority("ethernet") find a match. + result = _validate_priority_list(["Ethernet", "WIFI"]) + assert result == [{"interface": "ethernet"}, {"interface": "wifi"}] + + +def test_accepts_all_supported_interface_types() -> None: + # Only ethernet and wifi are currently accepted. Other interface types + # (openthread, modem) will be added when their setup-priority consumers + # land — see NETWORK_PLAN.md. + result = _validate_priority_list(["ethernet", "wifi"]) + assert [e["interface"] for e in result] == ["ethernet", "wifi"] + + +def test_rejects_not_yet_supported_interface() -> None: + # openthread / modem are in the long-term roadmap but no setup-priority + # consumer is wired yet, so VALID_NETWORK_TYPES excludes them today. + with pytest.raises(Invalid): + _validate_priority_list(["ethernet", "openthread"]) + with pytest.raises(Invalid): + _validate_priority_list(["wifi", "modem"]) + + +def test_single_interface_is_valid() -> None: + result = _validate_priority_list(["ethernet"]) + assert result == [{"interface": "ethernet"}] + + +def test_rejects_unknown_interface() -> None: + with pytest.raises(Invalid): + _validate_priority_list(["ethernet", "bluetooth"]) + + +def test_rejects_duplicate_entries() -> None: + with pytest.raises(Invalid, match="Duplicate entries"): + _validate_priority_list(["ethernet", "ethernet"]) + + +def test_rejects_duplicates_regardless_of_case() -> None: + # Same interface in mixed cases should still trip the duplicate check + # after normalization. + with pytest.raises(Invalid, match="Duplicate entries"): + _validate_priority_list(["ethernet", "Ethernet"]) + + +def test_rejects_mapping_form() -> None: + # The mapping form (- ethernet: { timeout: 30s }) was removed when the + # timeout option moved to its consumer PR. Verify we reject it cleanly + # instead of silently accepting a no-op. + with pytest.raises(Invalid): + _validate_priority_list([{"ethernet": {"timeout": "30s"}}]) + + +def test_get_network_priority_returns_none_when_unset() -> None: + assert get_network_priority("ethernet") is None + + +def test_get_network_priority_assigns_base_to_first_entry() -> None: + CORE.data[KEY_NETWORK_PRIORITY] = _validate_priority_list(["ethernet", "wifi"]) + assert get_network_priority("ethernet") == NETWORK_PRIORITY_BASE + + +def test_get_network_priority_steps_down_by_step_per_position() -> None: + CORE.data[KEY_NETWORK_PRIORITY] = _validate_priority_list(["ethernet", "wifi"]) + assert get_network_priority("wifi") == NETWORK_PRIORITY_BASE - NETWORK_PRIORITY_STEP + + +def test_get_network_priority_is_case_insensitive_on_query() -> None: + CORE.data[KEY_NETWORK_PRIORITY] = _validate_priority_list(["ethernet"]) + assert get_network_priority("Ethernet") == NETWORK_PRIORITY_BASE + + +def test_get_network_priority_returns_none_for_unlisted_interface() -> None: + CORE.data[KEY_NETWORK_PRIORITY] = _validate_priority_list(["ethernet"]) + assert get_network_priority("wifi") is None + + +def test_final_validate_rejects_priority_iface_without_component() -> None: + """An interface named in 'priority' with no matching component block is rejected.""" + # priority lists wifi, but only ethernet is present in the full config. + fv.full_config.set({"ethernet": {}}) + config = {CONF_PRIORITY: _validate_priority_list(["ethernet", "wifi"])} + with pytest.raises( + Invalid, match=r"'wifi' is listed in 'network: priority:' but no 'wifi:'" + ): + _final_validate(config) + + +def test_final_validate_accepts_when_all_priority_ifaces_present() -> None: + """No error when every interface in 'priority' has a matching component block.""" + fv.full_config.set({"ethernet": {}, "wifi": {}}) + config = {CONF_PRIORITY: _validate_priority_list(["ethernet", "wifi"])} + _final_validate(config) # must not raise + + +def test_final_validate_noop_without_priority_list() -> None: + """A network config without a 'priority' list imposes no component requirements.""" + fv.full_config.set({}) + _final_validate({}) # must not raise + + +def _cpp_setup_priority(name: str) -> float: + """Read a setup_priority constant straight from esphome/core/component.h.""" + header = Path(__file__).parents[3] / "esphome" / "core" / "component.h" + match = re.search( + rf"inline constexpr float {name} = ([\d.]+)f;", header.read_text() + ) + assert match is not None, f"setup_priority::{name} not found in component.h" + return float(match.group(1)) + + +def test_priority_band_constants_match_cpp_setup_priority() -> None: + """The Python priority-band constants mirror the C++ setup_priority values. + + NETWORK_PRIORITY_BASE must equal the historical setup_priority::WIFI / + ::ETHERNET default so a single-entry priority list reproduces the legacy + setup order, and the band guard must track setup_priority::AFTER_WIFI. + Reading the values from component.h turns a silent desync into a CI + failure if either side is ever rebalanced. + """ + assert _cpp_setup_priority("WIFI") == NETWORK_PRIORITY_BASE + assert _cpp_setup_priority("ETHERNET") == NETWORK_PRIORITY_BASE + assert _cpp_setup_priority("AFTER_WIFI") == _SETUP_PRIORITY_AFTER_WIFI + # Must stay below AFTER_BLUETOOTH (NetworkComponent's own priority) so + # interfaces never set up before esp_netif_init(). + assert _cpp_setup_priority("AFTER_BLUETOOTH") > NETWORK_PRIORITY_BASE + + +def test_wifi_first_priority_emits_primary_interface_define( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """A wifi-first priority list emits USE_NETWORK_PRIMARY_INTERFACE_WIFI.""" + generate_main(component_config_path("priority_wifi_first.yaml")) + defines = {d.name for d in CORE.defines} + assert "USE_NETWORK_PRIMARY_INTERFACE_WIFI" in defines + # Emitted by cg.set_setup_priority() at the wifi/ethernet call sites. + assert "USE_SETUP_PRIORITY_OVERRIDE" in defines + + +def test_ethernet_first_priority_emits_no_primary_interface_define( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """Ethernet-first matches the built-in preference order, so no define is emitted.""" + generate_main(component_config_path("priority_ethernet_first.yaml")) + assert not any( + d.name.startswith("USE_NETWORK_PRIMARY_INTERFACE_") for d in CORE.defines + ) + # The setup-priority overrides themselves are still emitted. + assert "USE_SETUP_PRIORITY_OVERRIDE" in {d.name for d in CORE.defines} + + +def test_no_primary_interface_define_without_priority( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], +) -> None: + """Without a priority list, no primary-interface define is emitted.""" + generate_main(component_config_path("wifi_only.yaml")) + assert not any( + d.name.startswith("USE_NETWORK_PRIMARY_INTERFACE_") for d in CORE.defines + ) diff --git a/tests/component_tests/packages/test_packages.py b/tests/component_tests/packages/test_packages.py index 6990c1c051..39bffd31b7 100644 --- a/tests/component_tests/packages/test_packages.py +++ b/tests/component_tests/packages/test_packages.py @@ -1264,6 +1264,75 @@ def test_remote_packages_no_revert( ] +@patch("esphome.yaml_util.load_yaml") +@patch("pathlib.Path.is_file") +@patch("esphome.git.clone_or_update") +def test_remote_packages_skipped_revert_does_not_retry( + mock_clone_or_update, mock_is_file, mock_load_yaml +) -> None: + """When revert() reports the rollback was skipped, the load is not + retried (the checkout is unchanged) and the error says so.""" + mock_revert = MagicMock(return_value=False) + mock_clone_or_update.return_value = (Path("/tmp/noexists"), mock_revert) + mock_is_file.return_value = True + mock_load_yaml.side_effect = cv.Invalid("bad yaml") + + config = { + CONF_PACKAGES: { + "pkg": { + CONF_URL: "https://github.com/esphome/repo", + CONF_REF: "main", + CONF_FILES: [{CONF_PATH: "file.yaml"}], + CONF_REFRESH: "1d", + } + } + } + with pytest.raises(cv.Invalid, match="could not revert the cached checkout"): + packages_pass(config) + + assert mock_revert.call_count == 1 + assert mock_load_yaml.call_count == 1 + + +@patch("esphome.yaml_util.load_yaml") +@patch("pathlib.Path.is_file") +@patch("esphome.git.clone_or_update") +def test_remote_packages_successful_revert_retries( + mock_clone_or_update, mock_is_file, mock_load_yaml, caplog: pytest.LogCaptureFixture +) -> None: + """A successful revert retries the load against the reverted checkout and + logs the original error, the only trace that upstream was broken.""" + mock_revert = MagicMock(return_value=True) + mock_clone_or_update.return_value = (Path("/tmp/noexists"), mock_revert) + mock_is_file.return_value = True + mock_load_yaml.side_effect = [ + cv.Invalid("bad yaml"), + OrderedDict( + {CONF_SENSOR: [{CONF_PLATFORM: TEST_SENSOR_PLATFORM_1, CONF_NAME: "test"}]} + ), + ] + + config = { + CONF_PACKAGES: { + "pkg": { + CONF_URL: "https://github.com/esphome/repo", + CONF_REF: "main", + CONF_FILES: [{CONF_PATH: "file.yaml"}], + CONF_REFRESH: "1d", + } + } + } + with caplog.at_level(logging.WARNING): + actual = packages_pass(config) + + assert actual[CONF_SENSOR] == [ + {CONF_PLATFORM: TEST_SENSOR_PLATFORM_1, CONF_NAME: "test"} + ] + assert mock_revert.call_count == 1 + assert mock_load_yaml.call_count == 2 + assert any("reverted the cached checkout" in r.getMessage() for r in caplog.records) + + def test_raw_config_contains_merged_esphome_from_package(tmp_path) -> None: """Test that CORE.raw_config contains esphome section from merged package. diff --git a/tests/component_tests/safe_mode/__init__.py b/tests/component_tests/safe_mode/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/component_tests/safe_mode/test_safe_mode.py b/tests/component_tests/safe_mode/test_safe_mode.py new file mode 100644 index 0000000000..617a3b4855 --- /dev/null +++ b/tests/component_tests/safe_mode/test_safe_mode.py @@ -0,0 +1,45 @@ +"""Tests for the safe_mode component.""" + +from collections.abc import Callable + +from esphome.core import CORE + +SHUTDOWN_DEFINE = "USE_SAFE_MODE_BOOT_IS_GOOD_ON_SHUTDOWN" + + +def _has_define(name: str) -> bool: + return any(define.name == name for define in CORE.defines) + + +def test_boot_is_good_on_shutdown_default( + generate_main: Callable[[str], str], +) -> None: + """By default, an orderly shutdown confirms the app image.""" + main_cpp = generate_main( + "tests/component_tests/safe_mode/test_safe_mode_default.yaml" + ) + + assert "safe_mode::SafeModeComponent" in main_cpp + assert _has_define(SHUTDOWN_DEFINE) + + +def test_boot_is_good_on_shutdown_disabled( + generate_main: Callable[[str], str], +) -> None: + """With boot_is_good_on_shutdown: false, the define is not added.""" + main_cpp = generate_main( + "tests/component_tests/safe_mode/test_safe_mode_no_shutdown_confirm.yaml" + ) + + assert "safe_mode::SafeModeComponent" in main_cpp + assert not _has_define(SHUTDOWN_DEFINE) + + +def test_safe_mode_disabled(generate_main: Callable[[str], str]) -> None: + """With safe_mode disabled, no component and no define are generated.""" + main_cpp = generate_main( + "tests/component_tests/safe_mode/test_safe_mode_disabled.yaml" + ) + + assert "safe_mode::SafeModeComponent" not in main_cpp + assert not _has_define(SHUTDOWN_DEFINE) diff --git a/tests/component_tests/safe_mode/test_safe_mode_default.yaml b/tests/component_tests/safe_mode/test_safe_mode_default.yaml new file mode 100644 index 0000000000..07c38250aa --- /dev/null +++ b/tests/component_tests/safe_mode/test_safe_mode_default.yaml @@ -0,0 +1,8 @@ +--- +esphome: + name: test + +esp32: + board: nodemcu-32s + +safe_mode: diff --git a/tests/component_tests/safe_mode/test_safe_mode_disabled.yaml b/tests/component_tests/safe_mode/test_safe_mode_disabled.yaml new file mode 100644 index 0000000000..ac9b224191 --- /dev/null +++ b/tests/component_tests/safe_mode/test_safe_mode_disabled.yaml @@ -0,0 +1,9 @@ +--- +esphome: + name: test + +esp32: + board: nodemcu-32s + +safe_mode: + disabled: true diff --git a/tests/component_tests/safe_mode/test_safe_mode_no_shutdown_confirm.yaml b/tests/component_tests/safe_mode/test_safe_mode_no_shutdown_confirm.yaml new file mode 100644 index 0000000000..64df87b381 --- /dev/null +++ b/tests/component_tests/safe_mode/test_safe_mode_no_shutdown_confirm.yaml @@ -0,0 +1,9 @@ +--- +esphome: + name: test + +esp32: + board: nodemcu-32s + +safe_mode: + boot_is_good_on_shutdown: false diff --git a/tests/component_tests/wireguard/__init__.py b/tests/component_tests/wireguard/__init__.py new file mode 100644 index 0000000000..82b57e8fef --- /dev/null +++ b/tests/component_tests/wireguard/__init__.py @@ -0,0 +1 @@ +"""Tests for the wireguard component.""" diff --git a/tests/component_tests/wireguard/test_init.py b/tests/component_tests/wireguard/test_init.py new file mode 100644 index 0000000000..556d14cd00 --- /dev/null +++ b/tests/component_tests/wireguard/test_init.py @@ -0,0 +1,44 @@ +"""Tests for the wireguard component schema.""" + +import pytest + +from esphome.components.wireguard import CONFIG_SCHEMA +from esphome.const import PlatformFramework +from esphome.yaml_util import SensitiveStr +from tests.component_tests.types import SetCoreConfigCallable + +# Any 42 base64 chars plus a valid terminator satisfies _WG_KEY_REGEX. +PRIVATE_KEY = "a" * 42 + "A=" +PEER_PUBLIC_KEY = "b" * 42 + "A=" +PEER_PRESHARED_KEY = "c" * 42 + "A=" + + +@pytest.mark.parametrize( + ("field", "value", "sensitive"), + [ + ("private_key", PRIVATE_KEY, True), + ("peer_preshared_key", PEER_PRESHARED_KEY, True), + ("peer_public_key", PEER_PUBLIC_KEY, False), + ], +) +def test_key_sensitivity( + field: str, + value: str, + sensitive: bool, + set_core_config: SetCoreConfigCallable, +) -> None: + """The private and preshared keys are secrets and must be tagged so dump + tooling redacts them deterministically; the peer's public key is not a + secret and must stay readable in redacted dumps (see issue #17718).""" + set_core_config(PlatformFramework.ESP32_IDF) + config = CONFIG_SCHEMA( + { + "address": "10.0.0.2", + "private_key": PRIVATE_KEY, + "peer_endpoint": "wg.example.com", + "peer_public_key": PEER_PUBLIC_KEY, + "peer_preshared_key": PEER_PRESHARED_KEY, + } + ) + assert isinstance(config[field], SensitiveStr) == sensitive + assert config[field] == value diff --git a/tests/components/README.md b/tests/components/README.md index 145a3440d2..be5e887767 100644 --- a/tests/components/README.md +++ b/tests/components/README.md @@ -28,6 +28,7 @@ create an `__init__.py` in your component's test directory and define `override_ ```python from tests.testing_helpers import ComponentManifestOverride + def override_manifest(manifest: ComponentManifestOverride) -> None: # Re-enable the component's own to_code (needed when the component must # emit C++ setup code that the test binary depends on at link time). @@ -39,6 +40,7 @@ Or supply a lightweight stub instead of the real `to_code`: ```python from tests.testing_helpers import ComponentManifestOverride + def override_manifest(manifest: ComponentManifestOverride) -> None: async def to_code_testing(config): # Only emit what the C++ tests actually need @@ -54,6 +56,7 @@ e.g. `tests/components/my_sensor/sensor/__init__.py`): ```python from tests.testing_helpers import ComponentManifestOverride + def override_manifest(manifest: ComponentManifestOverride) -> None: manifest.enable_codegen() ``` diff --git a/tests/components/aqi/benchmark.yaml b/tests/components/aqi/benchmark.yaml new file mode 100644 index 0000000000..d0d54c50b0 --- /dev/null +++ b/tests/components/aqi/benchmark.yaml @@ -0,0 +1,16 @@ +# Declares the component graph the C++ unit test build needs so that the aqi +# component's sources (which include sensor.h) compile. to_code is suppressed by +# the test harness; this only pulls the sensor + aqi source/include paths in. +# Loaded with plain yaml.safe_load, so avoid lambdas / ESPHome-tagged values here. +sensor: + - platform: template + id: pm25_sensor + name: "PM2.5" + - platform: template + id: pm10_sensor + name: "PM10" + - platform: aqi + name: "AQI" + pm_2_5: pm25_sensor + pm_10_0: pm10_sensor + calculation_type: AQI diff --git a/tests/components/aqi/common.yaml b/tests/components/aqi/common.yaml index 4c8cbbfa3f..cddc1f77cd 100644 --- a/tests/components/aqi/common.yaml +++ b/tests/components/aqi/common.yaml @@ -20,3 +20,10 @@ sensor: pm_2_5: pm25_sensor pm_10_0: pm10_sensor calculation_type: CAQI + + - platform: aqi + name: "Air Quality Index (AQI, extended)" + pm_2_5: pm25_sensor + pm_10_0: pm10_sensor + calculation_type: AQI + extended_range: true diff --git a/tests/components/aqi/test_aqi_calculator.cpp b/tests/components/aqi/test_aqi_calculator.cpp new file mode 100644 index 0000000000..ab95d3ac92 --- /dev/null +++ b/tests/components/aqi/test_aqi_calculator.cpp @@ -0,0 +1,85 @@ +#include + +#include "esphome/components/aqi/aqi_calculator.h" +#include "esphome/components/aqi/caqi_calculator.h" + +namespace esphome::aqi::testing { + +// US AQI (EPA 2024): PM2.5 225.5-500.4 -> 301-500, PM10 425-604 -> 301-500. + +TEST(USAQI, LowRangeUnaffectedByExtendedFlag) { + AQICalculator calc; + // PM2.5 25 drives over PM10 50; well below the top band, so the flag changes nothing. + EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, false), 81); + EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, true), 81); +} + +TEST(USAQI, HazardousInterpolatesNotPinnedAt301) { + AQICalculator calc; + // Regression guard: the old FLT_MAX top bucket collapsed every hazardous reading to 301. + EXPECT_EQ(calc.get_aqi(225.5f, 0.0f, false), 301); // band start + EXPECT_EQ(calc.get_aqi(250.0f, 0.0f, false), 319); // interpolated, not 301 + EXPECT_EQ(calc.get_aqi(500.4f, 0.0f, false), 500); // band top +} + +TEST(USAQI, DefaultClampsAtStandardMaximum) { + AQICalculator calc; + EXPECT_EQ(calc.get_aqi(600.0f, 0.0f, false), 500); + EXPECT_EQ(calc.get_aqi(1000.0f, 0.0f, false), 500); + EXPECT_EQ(calc.get_aqi(0.0f, 604.0f, false), 500); // PM10 top breakpoint +} + +TEST(USAQI, ExtendedRangeExtrapolatesBeyond500) { + AQICalculator calc; + EXPECT_EQ(calc.get_aqi(600.0f, 0.0f, true), 572); + EXPECT_EQ(calc.get_aqi(1000.0f, 0.0f, true), 862); + EXPECT_EQ(calc.get_aqi(0.0f, 700.0f, true), 607); // PM10 extrapolated past 500 +} + +TEST(USAQI, ExtendedRangeSaturatesUint16NoWraparound) { + AQICalculator calc; + // An absurd concentration would overflow uint16_t; it must saturate, not wrap to a small value. + EXPECT_EQ(calc.get_aqi(100000.0f, 0.0f, true), 65535); +} + +TEST(USAQI, WorseOfTwoPollutantsWins) { + AQICalculator calc; + // PM10 604 -> 500 dominates PM2.5 25 -> 81. + EXPECT_EQ(calc.get_aqi(25.0f, 604.0f, false), 500); +} + +// CAQI (CITEAIR): no maximum by spec -- the top ">100" class is open, so it is always unbounded +// and the extended_range flag does not apply. + +TEST(CAQI, LowRange) { + CAQICalculator calc; + EXPECT_EQ(calc.get_aqi(25.0f, 50.0f, false), 50); +} + +TEST(CAQI, ContinuousAt100NoPinAt101) { + CAQICalculator calc; + // Old code pinned everything above the top breakpoint to 101; now it reaches exactly 100. + EXPECT_EQ(calc.get_aqi(110.1f, 0.0f, false), 100); +} + +TEST(CAQI, UnboundedAboveTopBand) { + CAQICalculator calc; + EXPECT_EQ(calc.get_aqi(200.0f, 0.0f, false), 139); + EXPECT_EQ(calc.get_aqi(2000.0f, 0.0f, false), 925); +} + +TEST(CAQI, ExtendedRangeFlagIsIgnored) { + CAQICalculator calc; + // CAQI is always unbounded, so the flag must make no difference either way. + EXPECT_EQ(calc.get_aqi(200.0f, 0.0f, true), calc.get_aqi(200.0f, 0.0f, false)); + EXPECT_EQ(calc.get_aqi(2000.0f, 0.0f, true), calc.get_aqi(2000.0f, 0.0f, false)); +} + +TEST(CAQI, SaturatesUint16NoWraparound) { + CAQICalculator calc; + // CAQI is unbounded, so an extreme reading can extrapolate past uint16_t; it must saturate, + // not wrap around to a small (falsely "good") value. + EXPECT_EQ(calc.get_aqi(200000.0f, 0.0f, false), 65535); +} + +} // namespace esphome::aqi::testing diff --git a/tests/components/bk72xx_ble/common.yaml b/tests/components/bk72xx_ble/common.yaml new file mode 100644 index 0000000000..5ada71a141 --- /dev/null +++ b/tests/components/bk72xx_ble/common.yaml @@ -0,0 +1,2 @@ +bk72xx_ble: + enable_on_boot: true diff --git a/tests/components/bk72xx_ble/validate.bk72xx-ard.yaml b/tests/components/bk72xx_ble/validate.bk72xx-ard.yaml new file mode 100644 index 0000000000..e5009aa940 --- /dev/null +++ b/tests/components/bk72xx_ble/validate.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + bk72xx_ble: !include common.yaml diff --git a/tests/components/bk72xx_ble_tracker/common-boundary.yaml b/tests/components/bk72xx_ble_tracker/common-boundary.yaml new file mode 100644 index 0000000000..24844e18a5 --- /dev/null +++ b/tests/components/bk72xx_ble_tracker/common-boundary.yaml @@ -0,0 +1,11 @@ +bk72xx_ble_tracker: + id: ble_tracker + scan_parameters: + # Boundary coverage: the documented 2.5 ms floor on window (expressible only + # via the microsecond-accurate validation), a non-round interval exercising the + # 0.625 ms unit conversion without collapsing onto the window's unit count, + # and the non-continuous config path. + interval: 5000us + window: 2500us + duration: 5min + continuous: false diff --git a/tests/components/bk72xx_ble_tracker/common.yaml b/tests/components/bk72xx_ble_tracker/common.yaml new file mode 100644 index 0000000000..d8787a9347 --- /dev/null +++ b/tests/components/bk72xx_ble_tracker/common.yaml @@ -0,0 +1,7 @@ +bk72xx_ble_tracker: + id: ble_tracker + scan_parameters: + interval: 100ms + window: 30ms + duration: 5min + continuous: true diff --git a/tests/components/bk72xx_ble_tracker/validate-boundary.bk72xx-ard.yaml b/tests/components/bk72xx_ble_tracker/validate-boundary.bk72xx-ard.yaml new file mode 100644 index 0000000000..932eb4fb55 --- /dev/null +++ b/tests/components/bk72xx_ble_tracker/validate-boundary.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + bk72xx_ble_tracker: !include common-boundary.yaml diff --git a/tests/components/bk72xx_ble_tracker/validate.bk72xx-ard.yaml b/tests/components/bk72xx_ble_tracker/validate.bk72xx-ard.yaml new file mode 100644 index 0000000000..fc89479d06 --- /dev/null +++ b/tests/components/bk72xx_ble_tracker/validate.bk72xx-ard.yaml @@ -0,0 +1,2 @@ +packages: + bk72xx_ble_tracker: !include common.yaml diff --git a/tests/components/ble_device_base/__init__.py b/tests/components/ble_device_base/__init__.py new file mode 100644 index 0000000000..1b041df8df --- /dev/null +++ b/tests/components/ble_device_base/__init__.py @@ -0,0 +1,12 @@ +import esphome.codegen as cg +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + # resolve_irk() is compiled only when a sensor configures irk: + # (request_irk_support() emits USE_BLE_DEVICE_IRK). The unit-test build has + # no sensors, so emit the define here to put the real IRK path under test. + async def to_code_testing(config): + cg.add_define("USE_BLE_DEVICE_IRK") + + manifest.to_code = to_code_testing diff --git a/tests/components/ble_device_base/test_address.cpp b/tests/components/ble_device_base/test_address.cpp new file mode 100644 index 0000000000..9e4bca4c57 --- /dev/null +++ b/tests/components/ble_device_base/test_address.cpp @@ -0,0 +1,46 @@ +#include + +#include + +#include "esphome/components/ble_device_base/ble_device.h" + +namespace esphome::ble_device_base::testing { + +// from_scan_result() ingests BLE controller order (LSB-first); the public +// accessors must expose the historical esp32 semantics: address() in printable +// (MSB-first) order, address_uint64() with byte 0 in the LSB, address_str() +// printed MSB-first. +namespace { +// Device AA:BB:CC:DD:EE:FF — controller order delivers FF first. +const uint8_t MAC_LSB_FIRST[6] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}; +} // namespace + +TEST(BleDeviceAddress, AccessorsMatchEsp32Semantics) { + ESPBTDevice device; + device.from_scan_result(MAC_LSB_FIRST, -50, BLE_ADDR_TYPE_PUBLIC, nullptr, 0); + + const uint8_t *raw = device.address(); + EXPECT_EQ(raw[0], 0xaa); // MSB first, like ESP-IDF's bda + EXPECT_EQ(raw[5], 0xff); + + EXPECT_EQ(device.address_uint64(), 0xAABBCCDDEEFFULL); + + EXPECT_EQ(device.address_str(), "AA:BB:CC:DD:EE:FF"); +} + +// mac_lsb_first_to_uint64() packs the controller-order bytes a raw-advertisement +// callback delivers into the printable-order uint64 the native API speaks — the +// value esp32_ble::ble_addr_to_uint64() has always produced for that address. +TEST(BleDeviceAddress, MacLsbFirstToUint64MatchesWireValue) { + EXPECT_EQ(mac_lsb_first_to_uint64(MAC_LSB_FIRST), 0xAABBCCDDEEFFULL); +} + +// The helper and the parsed-device accessor are two routes to the same wire +// value: byte order must agree no matter which path an advertisement takes. +TEST(BleDeviceAddress, MacLsbFirstToUint64AgreesWithParsedDevice) { + ESPBTDevice device; + device.from_scan_result(MAC_LSB_FIRST, -50, BLE_ADDR_TYPE_PUBLIC, nullptr, 0); + EXPECT_EQ(mac_lsb_first_to_uint64(MAC_LSB_FIRST), device.address_uint64()); +} + +} // namespace esphome::ble_device_base::testing diff --git a/tests/components/ble_device_base/test_aes_ccm.cpp b/tests/components/ble_device_base/test_aes_ccm.cpp new file mode 100644 index 0000000000..39b2f81dcf --- /dev/null +++ b/tests/components/ble_device_base/test_aes_ccm.cpp @@ -0,0 +1,56 @@ +#include + +#include +#include + +#include "esphome/components/ble_device_base/ble_aes_ccm.h" + +namespace esphome::ble_device_base::testing { + +// Reference vector generated with Python `cryptography` AESCCM(tag_length=4), +// using the same AES-128-CCM parameters BTHome advertisements use: a 16-byte +// key, a 13-byte nonce, a 4-byte authentication tag and no associated data. +namespace { +const uint8_t KEY[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; +const uint8_t NONCE[13] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c}; +const uint8_t CIPHERTEXT[7] = {0x68, 0xb4, 0xf6, 0xc5, 0x2b, 0xf8, 0xaf}; +const uint8_t TAG[4] = {0x48, 0x4d, 0xaa, 0x56}; +const uint8_t PLAINTEXT[7] = {0x02, 0x01, 0x64, 0x03, 0x10, 0x8a, 0x01}; +} // namespace + +TEST(BleAesCcm, DecryptsAndAuthenticatesKnownVector) { + uint8_t out[sizeof(PLAINTEXT)] = {}; + EXPECT_TRUE(aes_ccm_auth_decrypt(KEY, NONCE, sizeof(NONCE), nullptr, 0, CIPHERTEXT, sizeof(CIPHERTEXT), out, TAG, + sizeof(TAG))); + EXPECT_EQ(0, memcmp(out, PLAINTEXT, sizeof(PLAINTEXT))); +} + +TEST(BleAesCcm, RejectsTamperedTag) { + uint8_t bad_tag[sizeof(TAG)]; + memcpy(bad_tag, TAG, sizeof(TAG)); + bad_tag[0] ^= 0x01; + uint8_t out[sizeof(PLAINTEXT)] = {}; + EXPECT_FALSE(aes_ccm_auth_decrypt(KEY, NONCE, sizeof(NONCE), nullptr, 0, CIPHERTEXT, sizeof(CIPHERTEXT), out, bad_tag, + sizeof(bad_tag))); +} + +TEST(BleAesCcm, RejectsTamperedCiphertext) { + uint8_t bad_ct[sizeof(CIPHERTEXT)]; + memcpy(bad_ct, CIPHERTEXT, sizeof(CIPHERTEXT)); + bad_ct[0] ^= 0x01; + uint8_t out[sizeof(PLAINTEXT)] = {}; + EXPECT_FALSE( + aes_ccm_auth_decrypt(KEY, NONCE, sizeof(NONCE), nullptr, 0, bad_ct, sizeof(bad_ct), out, TAG, sizeof(TAG))); +} + +TEST(BleAesCcm, RejectsWrongKey) { + uint8_t bad_key[sizeof(KEY)]; + memcpy(bad_key, KEY, sizeof(KEY)); + bad_key[0] ^= 0xFF; + uint8_t out[sizeof(PLAINTEXT)] = {}; + EXPECT_FALSE(aes_ccm_auth_decrypt(bad_key, NONCE, sizeof(NONCE), nullptr, 0, CIPHERTEXT, sizeof(CIPHERTEXT), out, TAG, + sizeof(TAG))); +} + +} // namespace esphome::ble_device_base::testing diff --git a/tests/components/ble_device_base/test_ble_uuid.cpp b/tests/components/ble_device_base/test_ble_uuid.cpp new file mode 100644 index 0000000000..45abd48479 --- /dev/null +++ b/tests/components/ble_device_base/test_ble_uuid.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include + +#include "esphome/components/ble_device_base/ble_device.h" + +namespace esphome::ble_device_base::testing { + +// A 16- or 32-bit UUID must compare equal to its 128-bit Bluetooth Base UUID form, matching +// esp32_ble_tracker. The 128-bit raw is the base UUID (LSB-first) with the short value at +// bytes 12.. : here 0x1234 -> bytes [12]=0x34, [13]=0x12. +TEST(BleDeviceUuid, ShortFormMatchesEquivalentLongForm) { + const ESPBTUUID u16 = ESPBTUUID::from_uint16(0x1234); + const uint8_t raw128[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00}; + const ESPBTUUID u128 = ESPBTUUID::from_raw(raw128); + EXPECT_TRUE(u16 == u128); + EXPECT_TRUE(u128 == u16); // symmetric +} + +TEST(BleDeviceUuid, ThirtyTwoBitMatchesEquivalentLongForm) { + const ESPBTUUID u32 = ESPBTUUID::from_uint32(0x1122AAFF); + const uint8_t raw128[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0xFF, 0xAA, 0x22, 0x11}; + const ESPBTUUID u128 = ESPBTUUID::from_raw(raw128); + EXPECT_TRUE(u32 == u128); +} + +TEST(BleDeviceUuid, DifferentUuidsDoNotMatch) { + EXPECT_FALSE(ESPBTUUID::from_uint16(0x1234) == ESPBTUUID::from_uint16(0x1235)); + const uint8_t raw128[16] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00}; + // Same low bytes but a non-base prefix is a genuinely different 128-bit UUID. + uint8_t custom[16]; + memcpy(custom, raw128, 16); + custom[0] ^= 0x01; + EXPECT_FALSE(ESPBTUUID::from_uint16(0x1234) == ESPBTUUID::from_raw(custom)); +} + +} // namespace esphome::ble_device_base::testing diff --git a/tests/components/ble_device_base/test_irk.cpp b/tests/components/ble_device_base/test_irk.cpp new file mode 100644 index 0000000000..4f507968c6 --- /dev/null +++ b/tests/components/ble_device_base/test_irk.cpp @@ -0,0 +1,48 @@ +#include + +#include + +#include "esphome/components/ble_device_base/ble_device.h" + +namespace esphome::ble_device_base::testing { + +// Reference vector generated with Python `cryptography` AES-128-ECB following +// the RPA resolution procedure (Bluetooth Core, Vol 3 Part H §2.2.2): +// hash = e(IRK, prand), where prand is the top 3 address bytes and the hash +// must equal the low 3 address bytes. +namespace { +const uint8_t IRK[16] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; +// 4A:2B:7C:FB:7B:21 — prand 4A:2B:7C (two MSBs = 01, an RPA), hash FB:7B:21. +const uint8_t RPA_LSB_FIRST[6] = {0x21, 0x7b, 0xfb, 0x7c, 0x2b, 0x4a}; + +ESPBTDevice make_device(const uint8_t mac_lsb_first[6]) { + ESPBTDevice device; + device.from_scan_result(mac_lsb_first, /*rssi=*/-60, /*addr_type=*/BLE_ADDR_TYPE_RPA_RANDOM, nullptr, 0); + return device; +} +} // namespace + +TEST(BleIrk, ResolvesMatchingRpa) { + ESPBTDevice device = make_device(RPA_LSB_FIRST); + EXPECT_TRUE(device.resolve_irk(IRK)); +} + +TEST(BleIrk, RejectsWrongIrk) { + uint8_t wrong_irk[16]; + for (int i = 0; i < 16; i++) + wrong_irk[i] = IRK[i] ^ 0xff; + ESPBTDevice device = make_device(RPA_LSB_FIRST); + EXPECT_FALSE(device.resolve_irk(wrong_irk)); +} + +TEST(BleIrk, RejectsWrongAddress) { + uint8_t other_mac[6]; + for (int i = 0; i < 6; i++) + other_mac[i] = RPA_LSB_FIRST[i]; + other_mac[0] ^= 0x01; // corrupt one hash byte + ESPBTDevice device = make_device(other_mac); + EXPECT_FALSE(device.resolve_irk(IRK)); +} + +} // namespace esphome::ble_device_base::testing diff --git a/tests/components/ble_device_base/test_raw_callback.cpp b/tests/components/ble_device_base/test_raw_callback.cpp new file mode 100644 index 0000000000..cd18c3db59 --- /dev/null +++ b/tests/components/ble_device_base/test_raw_callback.cpp @@ -0,0 +1,101 @@ +#include + +#include + +#include "esphome/components/ble_device_base/ble_hub.h" + +namespace esphome::ble_device_base::testing { + +// Exercises the hub contract around RawAdvertisementCallback, not just the +// struct: a hub stores one slot via set_raw_advertisement_callback(), fires it +// only when set ("no subscriber" is the default-constructed slot), and a new +// registration replaces the old ("one consumer at a time"). +// +// The in-tree emit site (BK72xxBLETracker::on_scan_report) compiles against +// the Beken SDK and cannot run host-side, so the guard-and-fire semantics are +// pinned here through a minimal host BLEHub implementation instead. +namespace { + +class FakeHub : public BLEHub { + public: + void register_listener(ESPBTDeviceListener *listener) override {} + void set_raw_advertisement_callback(RawAdvertisementCallback callback) override { this->callback_ = callback; } + HubCapabilities get_capabilities() const override { return {false, false, false}; } + void get_adapter_mac(uint8_t out[6]) override {} + bool scan_running() override { return false; } + bool scan_active() override { return false; } + + /// The emit path every tracker implements: fire only when a subscriber is set. + void emit(const RawAdvertisement &adv) { + if (this->callback_.is_set()) + this->callback_.invoke(adv); + } + + protected: + RawAdvertisementCallback callback_; // default-constructed: no subscriber +}; + +struct CapturingSubscriber { + RawAdvertisement last{}; + int calls{0}; + + static void trampoline(void *self, const RawAdvertisement &adv) { + auto *sub = static_cast(self); + sub->last = adv; + sub->calls++; + } +}; + +// Device AA:BB:CC:DD:EE:FF — controller order delivers FF first. +const uint8_t MAC_LSB_FIRST[6] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}; +const uint8_t ADV_DATA[4] = {0x02, 0x01, 0x06, 0x00}; + +RawAdvertisement make_test_adv() { + return RawAdvertisement{ + .mac = MAC_LSB_FIRST, .data = ADV_DATA, .data_len = sizeof(ADV_DATA), .rssi = -63, .addr_type = 1}; +} + +} // namespace + +TEST(RawAdvertisementCallback, DefaultConstructedSlotIsNotSet) { + const RawAdvertisementCallback callback{}; + EXPECT_FALSE(callback.is_set()); +} + +TEST(RawAdvertisementCallback, SubscriberSeesFieldsUnchanged) { + FakeHub hub; + CapturingSubscriber subscriber; + hub.set_raw_advertisement_callback({&subscriber, CapturingSubscriber::trampoline}); + + hub.emit(make_test_adv()); + + ASSERT_EQ(subscriber.calls, 1); + EXPECT_EQ(subscriber.last.mac, MAC_LSB_FIRST); + EXPECT_EQ(subscriber.last.data, ADV_DATA); + EXPECT_EQ(subscriber.last.data_len, sizeof(ADV_DATA)); + EXPECT_EQ(subscriber.last.rssi, -63); + EXPECT_EQ(subscriber.last.addr_type, 1); +} + +TEST(RawAdvertisementCallback, NoSubscriberDoesNotFire) { + FakeHub hub; + // No set_raw_advertisement_callback(): emitting must be a guarded no-op, + // not a jump through a garbage pointer. + hub.emit(make_test_adv()); +} + +TEST(RawAdvertisementCallback, NewSubscriberReplacesOld) { + FakeHub hub; + CapturingSubscriber first; + CapturingSubscriber second; + hub.set_raw_advertisement_callback({&first, CapturingSubscriber::trampoline}); + hub.set_raw_advertisement_callback({&second, CapturingSubscriber::trampoline}); + + hub.emit(make_test_adv()); + + EXPECT_EQ(first.calls, 0); // one consumer at a time + ASSERT_EQ(second.calls, 1); + EXPECT_EQ(second.last.rssi, -63); +} + +} // namespace esphome::ble_device_base::testing diff --git a/tests/components/cc1101/common.yaml b/tests/components/cc1101/common.yaml index 9784bfce8b..4d2411e021 100644 --- a/tests/components/cc1101/common.yaml +++ b/tests/components/cc1101/common.yaml @@ -17,6 +17,15 @@ cc1101: sync0: 0x91 sync1: 0xD3 num_preamble: 2 + foc_bs_cs_gate: true + foc_pre_k: "2K" + foc_post_k: "K/2" + foc_limit: "BW/4" + bs_pre_ki: "3KI" + bs_pre_kp: "4KP" + bs_post_ki: "KI/2" + bs_post_kp: "KP" + bs_limit: "12.5%" on_packet: then: - lambda: |- diff --git a/tests/components/core/json_escape_test.cpp b/tests/components/core/json_escape_test.cpp new file mode 100644 index 0000000000..4db6dce2ea --- /dev/null +++ b/tests/components/core/json_escape_test.cpp @@ -0,0 +1,134 @@ +#include + +#include + +#include "esphome/core/helpers.h" +#include "esphome/core/string_ref.h" + +namespace esphome::testing { + +namespace { + +// Large enough that none of the inputs below are ever dropped. +constexpr size_t TEST_BUFFER_SIZE = 64 * JSON_ESCAPE_MAX_EXPANSION + 1; + +// Escape into a stack buffer and return the result as a string so the expectations stay readable. +std::string escape(const std::string &value) { + char buf[TEST_BUFFER_SIZE]; + return json_escape_into_buffer(buf, StringRef(value.c_str(), value.size())); +} + +// Same, but with the short control forms turned off. +std::string escape_long(const std::string &value) { + char buf[TEST_BUFFER_SIZE]; + return json_escape_into_buffer(buf, StringRef(value.c_str(), value.size()), false); +} + +} // namespace + +// Plain ASCII with no special characters is passed through unchanged. +TEST(JsonEscape, PlainStringUnchanged) { + EXPECT_EQ(escape("MyNetwork"), "MyNetwork"); + EXPECT_EQ(escape(""), ""); +} + +// A double quote is escaped so it does not terminate the surrounding JSON string. +TEST(JsonEscape, EscapesDoubleQuote) { + EXPECT_EQ(escape("a\"b"), "a\\\"b"); + // A double quote followed by other characters stays inside the JSON string. + EXPECT_EQ(escape("\">end"), "\\\">end"); +} + +// A backslash is doubled so it does not start an escape sequence in the output. +TEST(JsonEscape, EscapesBackslash) { + EXPECT_EQ(escape("a\\b"), "a\\\\b"); + // A trailing backslash must not escape the closing quote of the JSON string. + EXPECT_EQ(escape("net\\"), "net\\\\"); +} + +// The control characters with short JSON forms use those forms. +TEST(JsonEscape, EscapesShortFormControls) { + EXPECT_EQ(escape("\n"), "\\n"); + EXPECT_EQ(escape("\r"), "\\r"); + EXPECT_EQ(escape("\t"), "\\t"); + EXPECT_EQ(escape("\b"), "\\b"); + EXPECT_EQ(escape("\f"), "\\f"); +} + +// Other control characters (< 0x20) without a short form become \u00XX with lowercase hex. +TEST(JsonEscape, EscapesOtherControlsAsUnicode) { + EXPECT_EQ(escape(std::string("\x00", 1)), "\\u0000"); + EXPECT_EQ(escape("\x01"), "\\u0001"); + EXPECT_EQ(escape("\x10"), "\\u0010"); + EXPECT_EQ(escape("\x1f"), "\\u001f"); + // 0x7f (DEL) is >= 0x20, so it is NOT escaped by this helper. + EXPECT_EQ(escape("\x7f"), "\x7f"); +} + +// With the short forms turned off, every control character is written as \u00XX instead. +TEST(JsonEscape, LongControlEscapes) { + EXPECT_EQ(escape_long("\n"), "\\u000a"); + EXPECT_EQ(escape_long("\r"), "\\u000d"); + EXPECT_EQ(escape_long("\t"), "\\u0009"); + EXPECT_EQ(escape_long("\b"), "\\u0008"); + EXPECT_EQ(escape_long("\f"), "\\u000c"); + // Controls without a short form are unaffected by the flag. + EXPECT_EQ(escape_long("\x01"), "\\u0001"); +} + +// The flag only affects control characters. A quote or backslash is never written as \u00XX, because that form is +// no shorter and both modes have always emitted the two character escape. +TEST(JsonEscape, LongModeStillUsesTwoCharQuoteAndBackslash) { + EXPECT_EQ(escape_long("a\"b"), "a\\\"b"); + EXPECT_EQ(escape_long("a\\b"), "a\\\\b"); + // Ordinary text is untouched in either mode. + EXPECT_EQ(escape_long("MyDevice"), "MyDevice"); +} + +// Bytes >= 0x20, including multi-byte UTF-8 sequences, are passed through verbatim. +TEST(JsonEscape, PassesThroughUtf8) { + // "café" in UTF-8 (é == 0xC3 0xA9). + EXPECT_EQ(escape("caf\xc3\xa9"), "caf\xc3\xa9"); + // Emoji (📶, 4-byte UTF-8) survives unchanged. + EXPECT_EQ(escape("\xf0\x9f\x93\xb6"), "\xf0\x9f\x93\xb6"); +} + +// A mix of special and normal characters is escaped in place without disturbing the rest. +TEST(JsonEscape, MixedContent) { EXPECT_EQ(escape("a\"b\\c\nd"), "a\\\"b\\\\c\\nd"); } + +// A buffer sized at JSON_ESCAPE_MAX_EXPANSION bytes per input byte holds the worst case exactly. +TEST(JsonEscape, WorstCaseInputFitsExactly) { + constexpr size_t input_len = 8; + char buf[input_len * JSON_ESCAPE_MAX_EXPANSION + 1]; + const std::string input(input_len, '\x01'); + std::string expected; + for (size_t i = 0; i < input_len; i++) + expected += "\\u0001"; + EXPECT_EQ(json_escape_into_buffer(buf, StringRef(input.c_str(), input.size())), expected); +} + +// An escape sequence that would not fit is dropped whole rather than written partially, and the result stays null +// terminated. +TEST(JsonEscape, DropsEscapeThatWouldNotFit) { + // Room for one \u00XX sequence plus the null terminator, but two are requested. + char buf[JSON_ESCAPE_MAX_EXPANSION + 1]; + const std::string input(2, '\x01'); + const std::string result = json_escape_into_buffer(buf, StringRef(input.c_str(), input.size())); + EXPECT_EQ(result, "\\u0001"); + EXPECT_EQ(buf[JSON_ESCAPE_MAX_EXPANSION], '\0'); +} + +// Plain characters are truncated at the buffer size, leaving room for the null terminator. +TEST(JsonEscape, TruncatesPlainInput) { + char buf[5]; + const std::string input(20, 'a'); + EXPECT_STREQ(json_escape_into_buffer(buf, StringRef(input.c_str(), input.size())), "aaaa"); +} + +// A zero length buffer cannot even hold a null terminator, so an empty string is returned instead of writing. +TEST(JsonEscape, EmptyBufferIsSafe) { + const std::string input("test"); + EXPECT_STREQ(json_escape_into_buffer(std::span(), StringRef(input.c_str(), input.size())), ""); +} + +} // namespace esphome::testing diff --git a/tests/components/core/test_lock_free_queue.cpp b/tests/components/core/test_lock_free_queue.cpp new file mode 100644 index 0000000000..2f74278129 --- /dev/null +++ b/tests/components/core/test_lock_free_queue.cpp @@ -0,0 +1,96 @@ +// Exercises the no-atomics LockFreeQueue implementation (PlainAtomic indices — +// the path used on cores without atomic RMW instructions, currently BK72xx). +// The define is forced before the include so this TU deterministically compiles +// that path regardless of the host's default thread model; no other test TU +// instantiates this template, so the differing definition is confined here. +#define ESPHOME_THREAD_MULTI_NO_ATOMICS +#include "esphome/core/lock_free_queue.h" + +#include + +namespace esphome::core::testing { + +TEST(LockFreeQueueNoAtomics, EmptyPopReturnsNull) { + esphome::LockFreeQueue q; + EXPECT_EQ(q.pop(), nullptr); + EXPECT_TRUE(q.empty()); + EXPECT_FALSE(q.full()); + EXPECT_EQ(q.size(), 0u); +} + +TEST(LockFreeQueueNoAtomics, FifoOrder) { + esphome::LockFreeQueue q; + int a = 1, b = 2, c = 3; + EXPECT_TRUE(q.push(&a)); + EXPECT_TRUE(q.push(&b)); + EXPECT_TRUE(q.push(&c)); + EXPECT_EQ(q.size(), 3u); + EXPECT_EQ(q.pop(), &a); + EXPECT_EQ(q.pop(), &b); + EXPECT_EQ(q.pop(), &c); + EXPECT_EQ(q.pop(), nullptr); +} + +TEST(LockFreeQueueNoAtomics, CapacityIsSizeMinusOne) { + esphome::LockFreeQueue q; + int v[4] = {0, 1, 2, 3}; + EXPECT_TRUE(q.push(&v[0])); + EXPECT_TRUE(q.push(&v[1])); + EXPECT_TRUE(q.push(&v[2])); + EXPECT_TRUE(q.full()); + // Ring reserves one slot: the SIZEth push fails and is counted as dropped. + EXPECT_FALSE(q.push(&v[3])); + EXPECT_EQ(q.get_and_reset_dropped_count(), 1u); + EXPECT_EQ(q.get_and_reset_dropped_count(), 0u); // reset is sticky +} + +TEST(LockFreeQueueNoAtomics, NullPushRejected) { + esphome::LockFreeQueue q; + EXPECT_FALSE(q.push(nullptr)); + EXPECT_TRUE(q.empty()); +} + +TEST(LockFreeQueueNoAtomics, WrapAround) { + esphome::LockFreeQueue q; + int v[3] = {10, 20, 30}; + // Cycle several times the ring size to cross the wrap boundary repeatedly. + for (int cycle = 0; cycle < 10; cycle++) { + for (auto &value : v) + ASSERT_TRUE(q.push(&value)); + EXPECT_TRUE(q.full()); + for (auto &value : v) + ASSERT_EQ(q.pop(), &value); + EXPECT_TRUE(q.empty()); + } + EXPECT_EQ(q.get_and_reset_dropped_count(), 0u); +} + +TEST(LockFreeQueueNoAtomics, IncrementDroppedCount) { + esphome::LockFreeQueue q; + // Producer-side external drop accounting (pool exhausted before push). + q.increment_dropped_count(); + q.increment_dropped_count(); + EXPECT_EQ(q.get_and_reset_dropped_count(), 2u); +} + +TEST(LockFreeQueueNoAtomics, InterleavedPushPop) { + esphome::LockFreeQueue q; + int v[64]; + int popped = 0; + for (int i = 0; i < 64; i++) { + v[i] = i; + ASSERT_TRUE(q.push(&v[i])); + if (i % 2 == 1) { + int *first = q.pop(); + ASSERT_NE(first, nullptr); + EXPECT_EQ(*first, popped++); + int *second = q.pop(); + ASSERT_NE(second, nullptr); + EXPECT_EQ(*second, popped++); + } + } + EXPECT_TRUE(q.empty()); + EXPECT_EQ(popped, 64); +} + +} // namespace esphome::core::testing diff --git a/tests/components/dallas_temp/common.yaml b/tests/components/dallas_temp/common.yaml index abd8e0cfa3..7f03ffa326 100644 --- a/tests/components/dallas_temp/common.yaml +++ b/tests/components/dallas_temp/common.yaml @@ -1,14 +1,18 @@ one_wire: - platform: gpio + id: ow_dallas_temp pin: ${one_wire_pin} sensor: - platform: dallas_temp + one_wire_id: ow_dallas_temp address: 0x1C0000031EDD2A28 name: Dallas Temperature 1 resolution: 9 - platform: dallas_temp + one_wire_id: ow_dallas_temp name: Dallas Temperature 2 - platform: dallas_temp + one_wire_id: ow_dallas_temp name: Dallas Temperature 3 index: 2 diff --git a/tests/components/deep_sleep/common-esp32-all.yaml b/tests/components/deep_sleep/common-esp32-all.yaml index b97eec76b9..9dc2f87258 100644 --- a/tests/components/deep_sleep/common-esp32-all.yaml +++ b/tests/components/deep_sleep/common-esp32-all.yaml @@ -6,9 +6,15 @@ deep_sleep: sleep_duration: 50s wakeup_pin: ${wakeup_pin} wakeup_pin_mode: INVERT_WAKEUP + on_wake: + - logger.log: + format: "Woke up, cause %d" + args: ["static_cast(cause)"] esp32_ext1_wakeup: pins: - - number: GPIO2 + - pin: GPIO2 + on_wake: + - logger.log: Woken by ext1 pin GPIO2 - number: GPIO13 mode: ANY_HIGH touch_wakeup: true diff --git a/tests/components/deep_sleep/common-esp32-ext1.yaml b/tests/components/deep_sleep/common-esp32-ext1.yaml index 9ed4279a33..c531d44743 100644 --- a/tests/components/deep_sleep/common-esp32-ext1.yaml +++ b/tests/components/deep_sleep/common-esp32-ext1.yaml @@ -5,8 +5,15 @@ deep_sleep: sleep_duration: 50s wakeup_pin: ${wakeup_pin} wakeup_pin_mode: INVERT_WAKEUP + on_wake: + - logger.log: + format: "Woke up, cause %d" + args: ["static_cast(cause)"] esp32_ext1_wakeup: pins: - - number: GPIO2 + - pin: + number: GPIO2 + on_wake: + - logger.log: Woken by ext1 pin GPIO2 - number: GPIO5 mode: ANY_HIGH diff --git a/tests/components/deep_sleep/common-esp32.yaml b/tests/components/deep_sleep/common-esp32.yaml index c20e1a902e..e670787cc0 100644 --- a/tests/components/deep_sleep/common-esp32.yaml +++ b/tests/components/deep_sleep/common-esp32.yaml @@ -5,3 +5,7 @@ deep_sleep: sleep_duration: 50s wakeup_pin: ${wakeup_pin} wakeup_pin_mode: INVERT_WAKEUP + on_wake: + - logger.log: + format: "Woke up, cause %d" + args: ["static_cast(cause)"] diff --git a/tests/components/deep_sleep/test-ota-rollback.esp32-idf.yaml b/tests/components/deep_sleep/test-ota-rollback.esp32-idf.yaml new file mode 100644 index 0000000000..bb24377675 --- /dev/null +++ b/tests/components/deep_sleep/test-ota-rollback.esp32-idf.yaml @@ -0,0 +1,19 @@ +# Deep sleep combined with OTA while bootloader rollback support is enabled +# (the default on ESP-IDF). Entering deep sleep runs the safe shutdown hooks, +# where safe_mode confirms the running app image so the bootloader does not +# roll back a fresh OTA update when the device goes to sleep before +# boot_is_good_after has elapsed. +substitutions: + wakeup_pin: GPIO4 + +packages: + deep_sleep: !include common.yaml + deep_sleep_esp32: !include common-esp32.yaml + +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome + password: "superlongpasswordthatnoonewillknow" diff --git a/tests/components/deep_sleep/test-ota-rollback.nrf52-mcumgr.yaml b/tests/components/deep_sleep/test-ota-rollback.nrf52-mcumgr.yaml new file mode 100644 index 0000000000..485490576d --- /dev/null +++ b/tests/components/deep_sleep/test-ota-rollback.nrf52-mcumgr.yaml @@ -0,0 +1,16 @@ +# Deep sleep combined with mcumgr OTA while MCUboot image rollback is enabled +# (the default on nRF52). Entering system-off deep sleep runs the safe +# shutdown hooks, where safe_mode confirms the running image so MCUboot does +# not revert a fresh OTA update on the next wake. +packages: + deep_sleep: !include common.yaml + +deep_sleep: + run_duration: 10s + +zephyr_ble_server: + +ota: + - platform: zephyr_mcumgr + transport: + ble: true diff --git a/tests/components/deep_sleep/test.bk72xx-ard.yaml b/tests/components/deep_sleep/test.bk72xx-ard.yaml index 2385fbb4db..bdbd27c902 100644 --- a/tests/components/deep_sleep/test.bk72xx-ard.yaml +++ b/tests/components/deep_sleep/test.bk72xx-ard.yaml @@ -1,6 +1,10 @@ deep_sleep: run_duration: 30s sleep_duration: 12h + on_wake: + - logger.log: + format: "Woke up, cause %d" + args: ["static_cast(cause)"] wakeup_pin: - pin: number: P6 diff --git a/tests/components/deep_sleep/test.esp8266-ard.yaml b/tests/components/deep_sleep/test.esp8266-ard.yaml index df08ec8a14..e4c592c095 100644 --- a/tests/components/deep_sleep/test.esp8266-ard.yaml +++ b/tests/components/deep_sleep/test.esp8266-ard.yaml @@ -1,5 +1,9 @@ deep_sleep: run_duration: 10s sleep_duration: 50s + on_wake: + - logger.log: + format: "Woke up, cause %d" + args: ["static_cast(cause)"] <<: !include common.yaml diff --git a/tests/components/ds2484/common.yaml b/tests/components/ds2484/common.yaml index 1e5dcd7dba..b6abc5bd76 100644 --- a/tests/components/ds2484/common.yaml +++ b/tests/components/ds2484/common.yaml @@ -1,6 +1,7 @@ one_wire: - platform: ds2484 - i2c_id: i2c_bus - address: 0x18 - active_pullup: true - strong_pullup: false + - platform: ds2484 + id: ow_ds2484 + i2c_id: i2c_bus + address: 0x18 + active_pullup: true + strong_pullup: false diff --git a/tests/components/ds248x/common.yaml b/tests/components/ds248x/common.yaml new file mode 100644 index 0000000000..53ae56f20a --- /dev/null +++ b/tests/components/ds248x/common.yaml @@ -0,0 +1,115 @@ +# Combined DS248x test covering all chip variants and options: +# - DS2482-100: active pullup, multiple sensors + index access +# - DS2482-101: sleep pin, bus_sleep / hub_sleep +# - DS2482-800: all 8 channels +# - DS2484: adjustable 1-Wire timing + RWPU pullup resistor selection +ds248x: + - id: ds2482_100 + address: 0x18 + type: ds2482-100 + active_pullup: true + - id: ds2482_101 + address: 0x19 + type: ds2482-101 + active_pullup: true + sleep_pin: + number: GPIO12 + inverted: false + bus_sleep: true + hub_sleep: true + - id: ds2482_800 + address: 0x1a + type: ds2482-800 + active_pullup: true + - id: ds2484_hub + address: 0x1b + type: ds2484 + active_pullup: true + # DS2484-specific 1-Wire timing parameters (optional fine-tuning) + reset_low_time: 8 # tRSTL: Reset low time + master_sample_time: 8 # tMSP: Master sample point + write_0_low_time: 8 # tW0L: Write-0 low time + recovery_time: 8 # tREC0: Recovery time + active_pullup_resistance: 1000ohm # RWPU: weak pullup resistor selection + +one_wire: + - platform: ds248x + ds248x_id: ds2482_100 + channel: 0 + id: ow_100 + - platform: ds248x + ds248x_id: ds2482_101 + channel: 0 + id: ow_101 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 0 + id: ow_800_0 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 1 + id: ow_800_1 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 2 + id: ow_800_2 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 3 + id: ow_800_3 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 4 + id: ow_800_4 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 5 + id: ow_800_5 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 6 + id: ow_800_6 + - platform: ds248x + ds248x_id: ds2482_800 + channel: 7 + id: ow_800_7 + - platform: ds248x + ds248x_id: ds2484_hub + channel: 0 + id: ow_2484 + +sensor: + # DS2482-100: explicit address + index-based access on the same bus + - platform: dallas_temp + one_wire_id: ow_100 + address: 0x1c0000031edd2a28 + name: Temp 100 by address + resolution: 12 + - platform: dallas_temp + one_wire_id: ow_100 + index: 0 + name: Temp 100 by index + # DS2482-101 (sleep variant) + - platform: dallas_temp + one_wire_id: ow_101 + address: 0x578295491f64ff28 + name: Temp 101 + # DS2482-800: sensors on a few of the eight channels + - platform: dallas_temp + one_wire_id: ow_800_0 + address: 0x1c0000031edd2a28 + name: Temp 800 CH0 + - platform: dallas_temp + one_wire_id: ow_800_3 + index: 0 + name: Temp 800 CH3 by index + - platform: dallas_temp + one_wire_id: ow_800_7 + address: 0x2800000123456789 + name: Temp 800 CH7 + # DS2484 (adjustable timing) + - platform: dallas_temp + one_wire_id: ow_2484 + address: 0x1c0000031edd2a28 + name: Temp 2484 + resolution: 12 diff --git a/tests/components/ds248x/test.esp32-ard.yaml b/tests/components/ds248x/test.esp32-ard.yaml new file mode 100644 index 0000000000..7c503b0ccb --- /dev/null +++ b/tests/components/ds248x/test.esp32-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/ds248x/test.esp32-idf.yaml b/tests/components/ds248x/test.esp32-idf.yaml new file mode 100644 index 0000000000..b47e39c389 --- /dev/null +++ b/tests/components/ds248x/test.esp32-idf.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml + +<<: !include common.yaml diff --git a/tests/components/ds248x/test.esp8266-ard.yaml b/tests/components/ds248x/test.esp8266-ard.yaml new file mode 100644 index 0000000000..4a98b9388a --- /dev/null +++ b/tests/components/ds248x/test.esp8266-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/ds248x/test.rp2040-ard.yaml b/tests/components/ds248x/test.rp2040-ard.yaml new file mode 100644 index 0000000000..319a7c71a6 --- /dev/null +++ b/tests/components/ds248x/test.rp2040-ard.yaml @@ -0,0 +1,4 @@ +packages: + i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml + +<<: !include common.yaml diff --git a/tests/components/epaper_spi/test.esp32-s3-idf.yaml b/tests/components/epaper_spi/test.esp32-s3-idf.yaml index 1699d16e70..602aeb8d0e 100644 --- a/tests/components/epaper_spi/test.esp32-s3-idf.yaml +++ b/tests/components/epaper_spi/test.esp32-s3-idf.yaml @@ -212,6 +212,29 @@ display: it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color::BLACK); it.circle(it.get_width() / 2, it.get_height() / 2, 15, Color(255, 0, 0)); + # Soldered Inkplate 6COLOR 7-color e-paper (600x448, UC8159-family) + - platform: epaper_spi + spi_id: spi_bus + model: inkplate6color + cs_pin: + allow_other_uses: true + number: GPIO5 + dc_pin: + allow_other_uses: true + number: GPIO17 + reset_pin: + allow_other_uses: true + number: GPIO16 + busy_pin: + allow_other_uses: true + number: GPIO4 + inverted: true + lambda: |- + it.filled_rectangle(0, 0, it.get_width(), it.get_height(), Color::WHITE); + it.circle(it.get_width() / 2, it.get_height() / 2, 30, Color::BLACK); + it.circle(it.get_width() / 2, it.get_height() / 2, 20, Color(255, 0, 0)); + it.circle(it.get_width() / 2, it.get_height() / 2, 10, Color(255, 165, 0)); + # Waveshare 7.5" V2 BWR (800x480, UC8179 controller, EDP_7in5b_V2) - platform: epaper_spi spi_id: spi_bus diff --git a/tests/components/esp32/test.esp32-p4-idf.yaml b/tests/components/esp32/test.esp32-p4-idf.yaml index fd42fac5a3..c16869d06b 100644 --- a/tests/components/esp32/test.esp32-p4-idf.yaml +++ b/tests/components/esp32/test.esp32-p4-idf.yaml @@ -21,7 +21,7 @@ esp32: disable_fatfs: true ota: - platform: esphome + - platform: esphome wifi: ssid: MySSID diff --git a/tests/components/espnow/common.yaml b/tests/components/espnow/common.yaml index ae43baa41a..2f82e794c4 100644 --- a/tests/components/espnow/common.yaml +++ b/tests/components/espnow/common.yaml @@ -74,6 +74,7 @@ sensor: id: espnow_temp_sensor - platform: packet_transport + transport_id: transport1 provider: test-provider remote_id: espnow_temp_sensor id: remote_temp diff --git a/tests/components/gsl3670/test.esp32-s3-idf.yaml b/tests/components/gsl3670/test.esp32-s3-idf.yaml index 5c3f4b931c..384e12eaba 100644 --- a/tests/components/gsl3670/test.esp32-s3-idf.yaml +++ b/tests/components/gsl3670/test.esp32-s3-idf.yaml @@ -1,32 +1,20 @@ packages: i2c: !include ../../test_build_components/common/i2c/esp32-s3-idf.yaml - spi: !include ../../test_build_components/common/spi/esp32-s3-idf.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml xl9535: id: expander -display: - - platform: mipi_spi - id: gsl3670_display - spi_id: spi_bus - model: t-display-s3-pro - # The model's default DC pin (GPIO9) clashes with the shared i2c bus SCL - # pin, so override it onto a free pin for this test. - dc_pin: GPIO5 - -psram: - mode: quad - touchscreen: # Firmware downloaded from the model's default release URL and cached. - platform: gsl3670 model: seeed-reterminal-d1001 - display: gsl3670_display + display: test_display_screen interrupt_pin: 18 # Explicit firmware URL + SHA-256 override. - platform: gsl3670 model: seeed-reterminal-d1001 - display: gsl3670_display + display: test_display_screen reset_pin: 10 interrupt_pin: 11 firmware: diff --git a/tests/components/gt911/common.yaml b/tests/components/gt911/common.yaml index 0fc40737f0..24a67e2e45 100644 --- a/tests/components/gt911/common.yaml +++ b/tests/components/gt911/common.yaml @@ -1,19 +1,8 @@ -display: - - platform: ssd1306_i2c - i2c_id: i2c_bus - id: gt911_ssd1306_i2c_display - model: SSD1306_128X64 - reset_pin: ${display_reset_pin} - pages: - - id: gt911_page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - touchscreen: - platform: gt911 i2c_id: i2c_bus id: gt911_touchscreen - display: gt911_ssd1306_i2c_display + display: test_display_screen interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} diff --git a/tests/components/gt911/test.esp32-idf.yaml b/tests/components/gt911/test.esp32-idf.yaml index 3bce86d9a3..9c2de1a425 100644 --- a/tests/components/gt911/test.esp32-idf.yaml +++ b/tests/components/gt911/test.esp32-idf.yaml @@ -1,9 +1,8 @@ substitutions: - display_reset_pin: "10" interrupt_pin: "20" reset_pin: "21" packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + gt911: !include common.yaml diff --git a/tests/components/gt911/test.esp8266-ard.yaml b/tests/components/gt911/test.esp8266-ard.yaml index c3bc159b5b..59af399be8 100644 --- a/tests/components/gt911/test.esp8266-ard.yaml +++ b/tests/components/gt911/test.esp8266-ard.yaml @@ -1,9 +1,8 @@ substitutions: - display_reset_pin: "10" interrupt_pin: "12" reset_pin: "13" packages: i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + gt911: !include common.yaml diff --git a/tests/components/gt911/test.rp2040-ard.yaml b/tests/components/gt911/test.rp2040-ard.yaml index 0c7f0bc504..efd5d9c2b1 100644 --- a/tests/components/gt911/test.rp2040-ard.yaml +++ b/tests/components/gt911/test.rp2040-ard.yaml @@ -1,9 +1,8 @@ substitutions: - display_reset_pin: "10" interrupt_pin: "20" reset_pin: "21" packages: i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + gt911: !include common.yaml diff --git a/tests/components/http_request/http_request.yaml b/tests/components/http_request/http_request.yaml index 46d4b88ec5..4b3c2ca36b 100644 --- a/tests/components/http_request/http_request.yaml +++ b/tests/components/http_request/http_request.yaml @@ -59,6 +59,24 @@ esphome: id: test_regression_light brightness: 100% effect: "None" + - http_request.get: + url: https://esphome.io + capture_response: true + on_response: + then: + # Regression test: http_request.post with json: (dict variant) inside + # on_response of a capture_response: true request puts std::string& + # (body) into the nested action's Ts..., which exposes a + # const-correctness bug in HttpRequestSendAction::play() where + # encode_json_ receives const copies of non-const reference args. + - http_request.post: + url: https://esphome.io + json: + status: "ok" + # Same with json: lambda variant, exercises json_func_ path + - http_request.post: + url: https://esphome.io + json: !lambda "root[\"status\"] = \"ok\";" http_request: useragent: esphome/tagreader diff --git a/tests/components/light/test_light_call_brightness.cpp b/tests/components/light/test_light_call_brightness.cpp new file mode 100644 index 0000000000..3c5dffd2f1 --- /dev/null +++ b/tests/components/light/test_light_call_brightness.cpp @@ -0,0 +1,120 @@ +#include + +#include "esphome/components/light/light_call.h" +#include "esphome/components/light/light_output.h" +#include "esphome/components/light/light_state.h" + +namespace esphome::light::testing { + +namespace { + +// A light that only supports ON_OFF, like the `binary` platform and `status_led`. +class OnOffOutput : public LightOutput { + public: + LightTraits get_traits() override { + LightTraits traits; + traits.set_supported_color_modes({ColorMode::ON_OFF}); + return traits; + } + void write_state(LightState *state) override {} +}; + +// A dimmable light, like the `monochromatic` platform. +class BrightnessOutput : public LightOutput { + public: + LightTraits get_traits() override { + LightTraits traits; + traits.set_supported_color_modes({ColorMode::BRIGHTNESS}); + return traits; + } + void write_state(LightState *state) override {} +}; + +// validate_() is where zero brightness is resolved against the light's capabilities. +class TestableLightCall : public LightCall { + public: + using LightCall::LightCall; + using LightCall::validate_; +}; + +bool as_binary(const LightColorValues &values) { + bool binary; + values.as_binary(&binary); + return binary; +} + +} // namespace + +// An ON/OFF light has no "on but dark" state, so a zero brightness -- how effects encode +// their dark phase -- must turn the light off. Regression test for +// https://github.com/esphome/esphome/issues/17873. +TEST(LightCallOnOff, ZeroBrightnessTurnsOutputOff) { + OnOffOutput output; + LightState state(&output); + TestableLightCall call(&state); + + call.set_state(true).set_brightness(0.0f); + auto values = call.validate_(); + + EXPECT_FALSE(as_binary(values)); +} + +// The zero must not be stored, or no later turn-on could clear it: the capability check in +// validate_() drops any brightness an ON/OFF light doesn't support, so a stored zero would +// leave the light permanently off. +TEST(LightCallOnOff, ZeroBrightnessIsNotStored) { + OnOffOutput output; + LightState state(&output); + + TestableLightCall dark_call(&state); + dark_call.set_state(true).set_brightness(0.0f); + state.remote_values = dark_call.validate_(); + + EXPECT_FLOAT_EQ(state.remote_values.get_brightness(), 1.0f); + + // A plain turn-on afterwards must switch the light back on. + TestableLightCall on_call(&state); + on_call.set_state(true); + auto values = on_call.validate_(); + + EXPECT_TRUE(as_binary(values)); +} + +// A plain turn-on with no brightness must still light up. +TEST(LightCallOnOff, PlainTurnOnIsVisible) { + OnOffOutput output; + LightState state(&output); + TestableLightCall call(&state); + + call.set_state(true); + auto values = call.validate_(); + + EXPECT_TRUE(as_binary(values)); +} + +TEST(LightCallOnOff, TurnOffTurnsOutputOff) { + OnOffOutput output; + LightState state(&output); + TestableLightCall call(&state); + + call.set_state(false); + auto values = call.validate_(); + + EXPECT_FALSE(as_binary(values)); +} + +// A dimmable light can represent "on but dark", so zero brightness must be kept as-is and +// must not be rewritten into a turn-off. +TEST(LightCallBrightness, ZeroBrightnessStaysOnButDark) { + BrightnessOutput output; + LightState state(&output); + TestableLightCall call(&state); + + call.set_state(true).set_brightness(0.0f); + auto values = call.validate_(); + + EXPECT_TRUE(values.is_on()); + EXPECT_FLOAT_EQ(values.get_brightness(), 0.0f); +} + +} // namespace esphome::light::testing diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml index f085b62cb6..ef8ba13e42 100644 --- a/tests/components/lvgl/lvgl-package.yaml +++ b/tests/components/lvgl/lvgl-package.yaml @@ -295,6 +295,22 @@ lvgl: id: style_test bg_color: blue bg_opa: !lambda return 0.5; + # `obj` is already themed above -- exercises updating an existing hidden style. + - lvgl.theme.update: + obj: + border_width: 2 + # `label` is never mentioned under `theme:` -- exercises lazily creating the + # hidden style and getting it attached to already-built label widgets. + - lvgl.theme.update: + label: + text_color: red + # `button` is never mentioned under `theme:`, and only a non-default state is + # targeted here -- exercises that no spurious, empty main/default style is + # created (and attached to every button) alongside the requested one. + - lvgl.theme.update: + button: + pressed: + bg_color: red - lvgl.image.update: id: lv_image src: diff --git a/tests/components/modbus/common.h b/tests/components/modbus/common.h new file mode 100644 index 0000000000..659b72014c --- /dev/null +++ b/tests/components/modbus/common.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include "esphome/components/uart/uart_component.h" + +namespace esphome::modbus::testing { + +// A UART that discards all writes, for tests that never inspect the wire. +class NullUART : public uart::UARTComponent { + public: + NullUART() { this->set_baud_rate(115200); } + void write_array(const uint8_t *data, size_t len) override {} + bool peek_byte(uint8_t *data) override { return false; } + bool read_array(uint8_t *data, size_t len) override { return false; } + size_t available() override { return 0; } + uart::UARTFlushResult flush() override { return uart::UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } +#if defined(USE_ESP8266) || defined(USE_ESP32) + void load_settings(bool dump_config) override {} +#endif + void check_logger_conflict() override {} +}; + +} // namespace esphome::modbus::testing diff --git a/tests/components/modbus/heap_probe_test.cpp b/tests/components/modbus/heap_probe_test.cpp index af43c6e5e3..2c7d9747bd 100644 --- a/tests/components/modbus/heap_probe_test.cpp +++ b/tests/components/modbus/heap_probe_test.cpp @@ -39,6 +39,50 @@ namespace esphome::modbus::testing { namespace { +// A UART the test can inject received bytes into; sent bytes are discarded. +class InjectableUART : public uart::UARTComponent { + public: + void write_array(const uint8_t *data, size_t len) override {} + bool peek_byte(uint8_t *data) override { + if (this->rx_.empty()) + return false; + *data = this->rx_.front(); + return true; + } + bool read_array(uint8_t *data, size_t len) override { + if (len > this->rx_.size()) + return false; + memcpy(data, this->rx_.data(), len); + this->rx_.erase(this->rx_.begin(), this->rx_.begin() + len); + return true; + } + size_t available() override { return this->rx_.size(); } + uart::UARTFlushResult flush() override { return uart::UARTFlushResult::UART_FLUSH_RESULT_ASSUMED_SUCCESS; } + void check_logger_conflict() override {} + + void inject_frame(uint8_t address, std::span pdu) { + // Wire frame: address + PDU + CRC16(low, high) + size_t start = this->rx_.size(); + this->rx_.push_back(address); + this->rx_.insert(this->rx_.end(), pdu.begin(), pdu.end()); + uint16_t crc = crc16(this->rx_.data() + start, this->rx_.size() - start); + this->rx_.push_back(crc & 0xFF); + this->rx_.push_back(crc >> 8); + } + + private: + std::vector rx_; +}; + +class NullDevice : public ModbusClientDevice { + public: + using ModbusClientDevice::ModbusClientDevice; + void on_response(std::span request_pdu, std::span response_pdu) override { + this->responses++; + } + int responses{0}; +}; + struct Sample { size_t count; size_t bytes; @@ -93,14 +137,61 @@ TEST(HeapProbe, QueueingTypicalCommandsIsAllocationFree) { EXPECT_EQ(total, 0u); } +// End to end: bytes injected at the UART travel through receive, frame parsing, response matching and +// device dispatch. The first response may grow the hub's rx buffer once; after that warm-up, handling a +// response performs zero heap allocations all the way to the device callback. +TEST(HeapProbe, ResponseHandlingIsAllocationFreeAfterWarmup) { + InjectableUART uart; + uart.set_baud_rate(115200); // tx timing math divides by the baud rate + ModbusClientHub hub; + hub.set_uart_parent(&uart); + hub.setup(); // computes frame timing from the baud rate + NullDevice device(&hub, 0x02); + + StaticVector req; + const uint8_t read_pdu[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + req.assign(read_pdu, read_pdu + sizeof(read_pdu)); + + // Largest possible read response first, so the rx buffer warm-up covers every later size. + uint8_t large_resp[252] = {0x03, 250}; + const uint8_t small_resp[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; + + auto round_trip = [&](std::span response_pdu) { + device.send_pdu(req); + hub.loop(); // transmit; the tx queue is empty during the measured receive below + uart.inject_frame(0x02, response_pdu); + return sample([&] { hub.loop(); }); // receive + parse + match + dispatch + }; + + Sample warmup = round_trip(std::span(large_resp, sizeof(large_resp))); + Sample steady_large = round_trip(std::span(large_resp, sizeof(large_resp))); + Sample steady_small = round_trip(small_resp); + + printf("HEAPPROBE warmup count=%zu bytes=%zu\n", warmup.count, warmup.bytes); + printf("HEAPPROBE steady_large count=%zu bytes=%zu\n", steady_large.count, steady_large.bytes); + printf("HEAPPROBE steady_small count=%zu bytes=%zu\n", steady_small.count, steady_small.bytes); + + EXPECT_EQ(device.responses, 3); + EXPECT_LE(warmup.count, 1u); // at most the one-time rx buffer growth + EXPECT_EQ(steady_large.count, 0u); + EXPECT_EQ(steady_small.count, 0u); +} + } // namespace esphome::modbus::testing #else // !HEAP_PROBE_HAS_ASAN +// Stub every ASan-gated test name, so the suite's test list is identical in every build configuration. namespace esphome::modbus::testing { TEST(HeapProbe, TypicalFrameConstructionIsAllocationFree) { GTEST_SKIP() << "allocation counting requires an AddressSanitizer build"; } +TEST(HeapProbe, QueueingTypicalCommandsIsAllocationFree) { + GTEST_SKIP() << "allocation counting requires an AddressSanitizer build"; +} +TEST(HeapProbe, ResponseHandlingIsAllocationFreeAfterWarmup) { + GTEST_SKIP() << "allocation counting requires an AddressSanitizer build"; +} } // namespace esphome::modbus::testing #endif // HEAP_PROBE_HAS_ASAN diff --git a/tests/components/modbus/modbus_client_device_test.cpp b/tests/components/modbus/modbus_client_device_test.cpp new file mode 100644 index 0000000000..8638c37688 --- /dev/null +++ b/tests/components/modbus/modbus_client_device_test.cpp @@ -0,0 +1,344 @@ +#include + +#include +#include +#include +#include + +#include "esphome/components/modbus/modbus.h" + +namespace esphome::modbus::testing { + +namespace { + +// Records every typed callback so tests can assert on the dispatch performed by the default +// on_response()/on_error() implementations. +class RecordingDevice : public ModbusClientDevice { + public: + struct ReadRegistersCall { + uint16_t start_address; + std::vector registers; + ResponseStatus status; + }; + struct ReadBitsCall { + uint16_t start_address; + uint16_t count; + std::vector packed; + ResponseStatus status; + }; + struct WriteCall { + uint16_t address; + uint16_t value; + ResponseStatus status; + }; + + void on_read_holding_registers(uint16_t start_address, std::span registers, + ResponseStatus status) override { + this->holding_calls.push_back({start_address, {registers.begin(), registers.end()}, status}); + } + void on_read_input_registers(uint16_t start_address, std::span registers, + ResponseStatus status) override { + this->input_calls.push_back({start_address, {registers.begin(), registers.end()}, status}); + } + void on_read_coils(uint16_t start_address, PackedBits bits, ResponseStatus status) override { + this->coil_calls.push_back({start_address, bits.size(), {bits.bytes().begin(), bits.bytes().end()}, status}); + } + void on_read_discrete_inputs(uint16_t start_address, PackedBits bits, ResponseStatus status) override { + this->discrete_calls.push_back({start_address, bits.size(), {bits.bytes().begin(), bits.bytes().end()}, status}); + } + void on_write_single_register(uint16_t address, uint16_t value, ResponseStatus status) override { + this->write_single_register_calls.push_back({address, value, status}); + } + void on_write_single_coil(uint16_t address, bool value, ResponseStatus status) override { + this->write_single_coil_calls.push_back({address, static_cast(value), status}); + } + void on_write_multiple_registers(uint16_t start_address, std::span registers, + ResponseStatus status) override { + this->write_multiple_registers_calls.push_back({start_address, {registers.begin(), registers.end()}, status}); + } + void on_write_multiple_coils(uint16_t start_address, PackedBits bits, ResponseStatus status) override { + this->write_multiple_coils_calls.push_back( + {start_address, bits.size(), {bits.bytes().begin(), bits.bytes().end()}, status}); + } + void on_custom_response(std::span request_pdu, std::span response_pdu, + ResponseStatus status) override { + this->custom_requests.emplace_back(request_pdu.begin(), request_pdu.end()); + this->custom_responses.emplace_back(response_pdu.begin(), response_pdu.end()); + this->custom_statuses.push_back(status); + } + + std::vector holding_calls; + std::vector input_calls; + std::vector coil_calls; + std::vector discrete_calls; + std::vector write_single_register_calls; + std::vector write_single_coil_calls; + std::vector write_multiple_registers_calls; + std::vector write_multiple_coils_calls; + std::vector> custom_requests; + std::vector> custom_responses; + std::vector custom_statuses; +}; + +// Overrides only the generic callbacks to verify the typed defaults delegate to them. +class GenericDevice : public ModbusClientDevice { + public: + void on_read_registers(EntityType register_type, uint16_t start_address, std::span registers, + ResponseStatus status) override { + this->register_type = register_type; + this->start_address = start_address; + this->registers.assign(registers.begin(), registers.end()); + this->calls++; + } + void on_read_bits(EntityType register_type, uint16_t start_address, PackedBits bits, ResponseStatus status) override { + this->register_type = register_type; + this->start_address = start_address; + this->bit_count = bits.size(); + this->calls++; + } + EntityType register_type{EntityType::CUSTOM}; + uint16_t start_address{0}; + uint16_t bit_count{0}; + std::vector registers; + int calls{0}; +}; + +} // namespace + +TEST(ModbusClientDeviceFanOut, ReadHoldingRegistersSuccess) { + RecordingDevice device; + const uint8_t request[] = {0x03, 0x01, 0x00, 0x00, 0x02}; // read 2 regs at 0x100 + const uint8_t response[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; // 0x002A, 0x0100 + device.on_response(request, response); + + ASSERT_EQ(device.holding_calls.size(), 1u); + const auto &call = device.holding_calls.front(); + EXPECT_EQ(call.start_address, 0x100); + EXPECT_EQ(call.registers, (std::vector{0x002A, 0x0100})); + EXPECT_FALSE(call.status.has_value()); +} + +TEST(ModbusClientDeviceFanOut, ReadInputRegistersDelegateToGeneric) { + GenericDevice device; + const uint8_t request[] = {0x04, 0x00, 0x10, 0x00, 0x01}; + const uint8_t response[] = {0x04, 0x02, 0x12, 0x34}; + device.on_response(request, response); + + EXPECT_EQ(device.calls, 1); + EXPECT_EQ(device.register_type, EntityType::INPUT_REGISTER); + EXPECT_EQ(device.start_address, 0x10); + EXPECT_EQ(device.registers, (std::vector{0x1234})); +} + +TEST(ModbusClientDeviceFanOut, ReadDiscreteInputsDelegateToGenericBits) { + GenericDevice device; + const uint8_t request[] = {0x02, 0x00, 0x20, 0x00, 0x05}; // 5 inputs at 0x20 + const uint8_t response[] = {0x02, 0x01, 0x15}; + device.on_response(request, response); + + EXPECT_EQ(device.calls, 1); + EXPECT_EQ(device.register_type, EntityType::DISCRETE_INPUT); + EXPECT_EQ(device.start_address, 0x20); + EXPECT_EQ(device.bit_count, 5); +} + +// A CRC-valid response whose length does not match its request cannot be decoded per the +// function-code contract: it goes to the catch-all with the raw PDUs, not to the typed callback. +TEST(ModbusClientDeviceFanOut, ReadRegistersMismatchedLengthGoesToCatchAll) { + RecordingDevice device; + // Request asks for 4 registers but the response only carries 1. + const uint8_t request[] = {0x03, 0x00, 0x00, 0x00, 0x04}; + const uint8_t response[] = {0x03, 0x02, 0xBE, 0xEF}; + device.on_response(request, response); + + EXPECT_TRUE(device.holding_calls.empty()); + ASSERT_EQ(device.custom_responses.size(), 1u); + EXPECT_EQ(device.custom_responses.front(), (std::vector(response, response + sizeof(response)))); +} + +// Coil responses are validated the same way: byte count must be ceil(count / 8). +TEST(ModbusClientDeviceFanOut, ReadCoilsMismatchedLengthGoesToCatchAll) { + RecordingDevice device; + const uint8_t request[] = {0x01, 0x00, 0x13, 0x00, 0x13}; // 19 coils -> 3 packed bytes + const uint8_t response[] = {0x01, 0x02, 0xCD, 0x6B}; // only 2 + device.on_response(request, response); + + EXPECT_TRUE(device.coil_calls.empty()); + EXPECT_EQ(device.custom_responses.size(), 1u); +} + +TEST(ModbusClientDeviceFanOut, ReadCoilsSuccess) { + RecordingDevice device; + const uint8_t request[] = {0x01, 0x00, 0x13, 0x00, 0x13}; // 19 coils at 0x13 + const uint8_t response[] = {0x01, 0x03, 0xCD, 0x6B, 0x05}; + device.on_response(request, response); + + ASSERT_EQ(device.coil_calls.size(), 1u); + const auto &call = device.coil_calls.front(); + EXPECT_EQ(call.start_address, 0x13); + EXPECT_EQ(call.count, 19); + EXPECT_EQ(call.packed, (std::vector{0xCD, 0x6B, 0x05})); + EXPECT_FALSE(call.status.has_value()); + // first coil = bit 0 of byte 0 + EXPECT_TRUE(helpers::bit_from_packed(0, call.packed)); + EXPECT_FALSE(helpers::bit_from_packed(1, call.packed)); +} + +TEST(ModbusClientDeviceFanOut, WriteSingleRegisterSuccess) { + RecordingDevice device; + const uint8_t request[] = {0x06, 0x00, 0x01, 0x00, 0x03}; + device.on_response(request, request); // echo + + ASSERT_EQ(device.write_single_register_calls.size(), 1u); + const auto &call = device.write_single_register_calls.front(); + EXPECT_EQ(call.address, 1); + EXPECT_EQ(call.value, 3); + EXPECT_FALSE(call.status.has_value()); +} + +TEST(ModbusClientDeviceFanOut, WriteSingleCoilSuccess) { + RecordingDevice device; + const uint8_t request[] = {0x05, 0x00, 0xAC, 0xFF, 0x00}; + device.on_response(request, request); + + ASSERT_EQ(device.write_single_coil_calls.size(), 1u); + EXPECT_EQ(device.write_single_coil_calls.front().address, 0xAC); + EXPECT_EQ(device.write_single_coil_calls.front().value, 1u); +} + +TEST(ModbusClientDeviceFanOut, WriteErrorReportsRequestArgumentsAndStatus) { + RecordingDevice device; + const uint8_t request[] = {0x06, 0x00, 0x01, 0x00, 0x03}; + const uint8_t exception[] = {0x86, 0x02}; // ILLEGAL_DATA_ADDRESS + device.on_error(request, static_cast(exception[1])); + + ASSERT_EQ(device.write_single_register_calls.size(), 1u); + const auto &call = device.write_single_register_calls.front(); + EXPECT_EQ(call.address, 1); + EXPECT_EQ(call.value, 3); + EXPECT_EQ(call.status, ExceptionCode::ILLEGAL_DATA_ADDRESS); +} + +TEST(ModbusClientDeviceFanOut, ReadErrorReportsEmptyDataAndStatus) { + RecordingDevice device; + const uint8_t request[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + const uint8_t exception[] = {0x83, 0x02}; + device.on_error(request, static_cast(exception[1])); + + ASSERT_EQ(device.holding_calls.size(), 1u); + const auto &call = device.holding_calls.front(); + EXPECT_EQ(call.start_address, 0x100); + EXPECT_TRUE(call.registers.empty()); + EXPECT_EQ(call.status, ExceptionCode::ILLEGAL_DATA_ADDRESS); +} + +TEST(ModbusClientDeviceFanOut, CustomFunctionCodeGoesToCatchAll) { + RecordingDevice device; + const uint8_t request[] = {0x47, 0x01, 0x02, 0x03, 0x04}; + const uint8_t response[] = {0x47, 0xAA, 0xBB}; + device.on_response(request, response); + + ASSERT_EQ(device.custom_requests.size(), 1u); + EXPECT_EQ(device.custom_requests.front(), (std::vector{0x47, 0x01, 0x02, 0x03, 0x04})); + EXPECT_EQ(device.custom_responses.front(), (std::vector{0x47, 0xAA, 0xBB})); + EXPECT_FALSE(device.custom_statuses.front().has_value()); + EXPECT_TRUE(device.holding_calls.empty()); + + // On failure the catch-all receives an empty response and the status (the exception code). + const uint8_t exception[] = {0xC7, 0x02}; + device.on_error(request, static_cast(exception[1])); + ASSERT_EQ(device.custom_statuses.size(), 2u); + EXPECT_EQ(device.custom_statuses.back(), ExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_TRUE(device.custom_responses.back().empty()); +} + +// A write ack only echoes the start address and count, so the data that was written is decoded from the +// request PDU: [0] function code, [1..2] start address, [3..4] count, [5] byte count, [6..] data. +TEST(ModbusClientDeviceFanOut, WriteMultipleAcksReportStartAndData) { + RecordingDevice device; + // Write 2 registers (0x0001, 0x0002) at 0x0020: byte count 4, data from offset 6. + const uint8_t reg_request[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x02}; + const uint8_t reg_ack[] = {0x10, 0x00, 0x20, 0x00, 0x02}; + device.on_response(reg_request, reg_ack); + // Write 10 coils at 0x0030: byte count 2, packed bits 0xFF 0x03 from offset 6. + const uint8_t coil_request[] = {0x0F, 0x00, 0x30, 0x00, 0x0A, 0x02, 0xFF, 0x03}; + const uint8_t coil_ack[] = {0x0F, 0x00, 0x30, 0x00, 0x0A}; + device.on_response(coil_request, coil_ack); + + ASSERT_EQ(device.write_multiple_registers_calls.size(), 1u); + EXPECT_EQ(device.write_multiple_registers_calls.front().start_address, 0x20); + EXPECT_EQ(device.write_multiple_registers_calls.front().registers, (std::vector{0x0001, 0x0002})); + ASSERT_EQ(device.write_multiple_coils_calls.size(), 1u); + EXPECT_EQ(device.write_multiple_coils_calls.front().start_address, 0x30); + EXPECT_EQ(device.write_multiple_coils_calls.front().count, 10); + EXPECT_EQ(device.write_multiple_coils_calls.front().packed, (std::vector{0xFF, 0x03})); +} + +// A truncated request (byte-count header promises more data than the PDU carries) is not a standard +// write-multiple, so it is diverted to on_custom_response() - never clamped and delivered as if complete. +TEST(ModbusClientDeviceFanOut, WriteMultipleTruncatedRequestDispatchesAsCustom) { + RecordingDevice device; + // Header claims 2 registers / 4 data bytes, but only one register's worth is present. + const uint8_t reg_request[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x00, 0x01}; + const uint8_t reg_ack[] = {0x10, 0x00, 0x20, 0x00, 0x02}; + device.on_response(reg_request, reg_ack); + + EXPECT_TRUE(device.write_multiple_registers_calls.empty()); + ASSERT_EQ(device.custom_requests.size(), 1u); + EXPECT_EQ(device.custom_requests.front(), (std::vector{0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x00, 0x01})); +} + +// A request whose byte-count header disagrees with its own quantity field (here: 2 registers but a +// byte count of 2 instead of 4, with matching data) is non-standard and diverted to the catch-all. +TEST(ModbusClientDeviceFanOut, WriteMultipleInconsistentByteCountDispatchesAsCustom) { + RecordingDevice device; + const uint8_t reg_request[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x02, 0x00, 0x01}; + const uint8_t reg_ack[] = {0x10, 0x00, 0x20, 0x00, 0x02}; + device.on_response(reg_request, reg_ack); + + EXPECT_TRUE(device.write_multiple_registers_calls.empty()); + EXPECT_EQ(device.custom_requests.size(), 1u); +} + +// An exception on a read still dispatches to the typed callback (empty data, status set): the gate must +// not require a standard response on the failure path, because on_error() delivers an empty response by +// design. +TEST(ModbusClientDeviceFanOut, ReadErrorWithEmptyResponseStillDispatchesTyped) { + RecordingDevice device; + const uint8_t read_request[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + device.on_error(read_request, ExceptionCode::ILLEGAL_DATA_ADDRESS); + + ASSERT_EQ(device.holding_calls.size(), 1u); + EXPECT_TRUE(device.holding_calls.front().registers.empty()); + EXPECT_EQ(device.holding_calls.front().status, ExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_TRUE(device.custom_requests.empty()); +} + +// An error on a coil read must deliver a PackedBits view whose size() is zero - the count must never +// promise bits that have no bytes behind them (operator[] is unchecked). +TEST(ModbusClientDeviceFanOut, ReadCoilsErrorDeliversZeroCountBits) { + RecordingDevice device; + const uint8_t read_request[] = {0x01, 0x01, 0x00, 0x00, 0x0A}; + device.on_error(read_request, ExceptionCode::SERVICE_DEVICE_FAILURE); + + ASSERT_EQ(device.coil_calls.size(), 1u); + EXPECT_EQ(device.coil_calls.front().count, 0); + EXPECT_TRUE(device.coil_calls.front().packed.empty()); +} + +// Single-write acks: on success the delivered value is the device's echo (real read-back); +// on an exception it falls back to the request copy. +TEST(ModbusTypedDispatch, SingleWriteAckPrefersTheResponseEcho) { + RecordingDevice device; + const uint8_t request[] = {0x06, 0x00, 0x10, 0x00, 0x2A}; + const uint8_t echo_clamped[] = {0x06, 0x00, 0x10, 0x00, 0x28}; // device clamped 42 -> 40 + device.on_response(request, echo_clamped); + ASSERT_EQ(device.write_single_register_calls.size(), 1u); + EXPECT_EQ(device.write_single_register_calls.front().value, 0x0028); // the echo, not the request + + device.on_error(request, ExceptionCode::ILLEGAL_DATA_VALUE); + ASSERT_EQ(device.write_single_register_calls.size(), 2u); + EXPECT_EQ(device.write_single_register_calls.back().value, 0x002A); // exception: request copy +} + +} // namespace esphome::modbus::testing diff --git a/tests/components/modbus/modbus_client_hub_test.cpp b/tests/components/modbus/modbus_client_hub_test.cpp index d04c4fe10c..11bc10200d 100644 --- a/tests/components/modbus/modbus_client_hub_test.cpp +++ b/tests/components/modbus/modbus_client_hub_test.cpp @@ -1,9 +1,13 @@ #include #include +#include #include +#include +#include "common.h" #include "esphome/components/modbus/modbus.h" +#include "esphome/core/hal.h" namespace esphome::modbus::testing { @@ -16,19 +20,21 @@ class NoResponseProbeHub : public ModbusClientHub { public: size_t queued_frames() const { return this->tx_buffer_.size(); } const ModbusDeviceCommand &front() const { return this->tx_buffer_.front(); } + const ModbusDeviceCommand &queued(size_t i) const { return this->tx_buffer_[i]; } bool waiting() const { return this->waiting_for_response_.has_value(); } const ModbusDeviceCommand &waiting_command() const { EXPECT_TRUE(this->waiting_for_response_.has_value()); return *this->waiting_for_response_; // NOLINT(bugprone-unchecked-optional-access) } + void send_next_for_test() { this->send_next_frame_(); } void force_send_front() { this->waiting_for_response_ = std::move(this->tx_buffer_.front()); this->tx_buffer_.pop_front(); } // Drives the real unexpected-frame branch in process_modbus_server_frame(). - void receive_frame_for_test(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) { - this->process_modbus_server_frame(address, function_code, data, len); + void receive_frame_for_test(uint8_t address, std::span pdu) { + this->process_modbus_server_frame(address, pdu); } void timeout_waiting() { if (this->waiting_for_response_.has_value()) @@ -37,11 +43,11 @@ class NoResponseProbeHub : public ModbusClientHub { } }; -// A device with a scripted answer to on_modbus_no_response(). +// A device with a scripted answer to on_no_response(). class RetryingDevice : public ModbusClientDevice { public: RetryingDevice(ModbusClientHub *hub, uint8_t address, bool retry) : ModbusClientDevice(hub, address), retry_(retry) {} - bool on_modbus_no_response() override { + bool on_no_response(std::span request_pdu) override { this->no_response_count_++; return this->retry_; } @@ -55,7 +61,7 @@ class RetryingDevice : public ModbusClientDevice { class ClearingRetryDevice : public ModbusClientDevice { public: ClearingRetryDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} - bool on_modbus_no_response() override { + bool on_no_response(std::span request_pdu) override { this->no_response_count_++; this->clear_tx_queue_for_device(); // detaches this device from the waiting slot mid-callback return true; // and still requests a retry @@ -94,8 +100,9 @@ TEST(ModbusClientHubNoResponse, RetryRequeuesWaitingFrame) { EXPECT_EQ(requeued.device, &device); // address + PDU + CRC ASSERT_EQ(requeued.frame.size(), sizeof(READ_PDU) + 3); - EXPECT_EQ(requeued.frame.data.data()[0], 0x02); - EXPECT_EQ(0, memcmp(requeued.frame.data.data() + 1, READ_PDU, sizeof(READ_PDU))); + EXPECT_EQ(requeued.frame.address(), 0x02); + ASSERT_EQ(requeued.frame.pdu().size(), sizeof(READ_PDU)); + EXPECT_EQ(0, memcmp(requeued.frame.pdu().data(), READ_PDU, sizeof(READ_PDU))); } // A device that declines the retry has the frame dropped. @@ -143,8 +150,8 @@ TEST(ModbusClientHubNoResponse, RetryBehindInterruptedShell) { hub.force_send_front(); // A frame from the wrong address (0x07, expected 0x02) hits the unexpected-frame branch. - const uint8_t stray_payload[] = {0x04, 0x00, 0x2A, 0x01, 0x00}; - hub.receive_frame_for_test(0x07, 0x03, stray_payload, sizeof(stray_payload)); + const uint8_t stray_pdu[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; + hub.receive_frame_for_test(0x07, stray_pdu); EXPECT_EQ(device.no_response_count_, 1); ASSERT_EQ(hub.queued_frames(), 1u); // exactly one requeue... @@ -175,4 +182,736 @@ TEST(ModbusClientHubNoResponse, MidCallbackClearCancelsRetry) { EXPECT_FALSE(hub.waiting()); } +// A device whose sent/not-sent callbacks are counted. +namespace { +class SentCountingDevice : public ModbusClientDevice { + public: + SentCountingDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_sent(std::span request_pdu) override { + this->sent_count_++; + this->last_sent_pdu_.assign(request_pdu.begin(), request_pdu.end()); + } + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + this->last_not_sent_pdu_.assign(request_pdu.begin(), request_pdu.end()); + } + int sent_count_{0}; + int not_sent_count_{0}; + std::vector last_sent_pdu_; + std::vector last_not_sent_pdu_; +}; +} // namespace + +// on_sent() fires when the frame goes onto the wire, not when it is queued. +TEST(ModbusClientHubSent, FiresOnWireNotOnQueue) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); // frame timing derives from the baud rate + SentCountingDevice device(&hub, 0x02); + + device.send_pdu(read_pdu()); + EXPECT_EQ(device.sent_count_, 0); // queued only - nothing on the wire yet + + hub.send_next_for_test(); + EXPECT_EQ(device.sent_count_, 1); + EXPECT_EQ(device.not_sent_count_, 0); + // The callback identifies which command transmitted: it carries the request PDU. + EXPECT_EQ(device.last_sent_pdu_, (std::vector(READ_PDU, READ_PDU + sizeof(READ_PDU)))); + EXPECT_TRUE(hub.waiting()); +} + +// Counts response deliveries so requeue semantics can be pinned end to end. +namespace { +class DataCountingDevice : public ModbusClientDevice { + public: + DataCountingDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_response(std::span request_pdu, std::span response_pdu) override { + this->data_count_++; + } + void on_error(std::span request_pdu, ExceptionCode exception_code) override { this->error_count_++; } + bool on_no_response(std::span request_pdu) override { + this->no_response_count_++; + this->last_no_response_pdu_.assign(request_pdu.begin(), request_pdu.end()); + if (this->retries_ == 0) + return false; + this->retries_--; + return true; + } + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + this->last_not_sent_pdu_.assign(request_pdu.begin(), request_pdu.end()); + } + void on_sent(std::span request_pdu) override { this->sent_count_++; } + int terminals() const { + return this->data_count_ + this->error_count_ + this->no_response_count_ + this->not_sent_count_; + } + int data_count_{0}; + int error_count_{0}; + int no_response_count_{0}; + int not_sent_count_{0}; + int sent_count_{0}; + int retries_{0}; + std::vector last_not_sent_pdu_; + std::vector last_no_response_pdu_; +}; + +// Runs full send/respond cycles until the queue drains; returns the number of cycles executed. +int drain_with_responses(NoResponseProbeHub &hub, std::span response_pdu, int max_cycles = 10) { + int cycles = 0; + while (hub.queued_frames() != 0 && cycles < max_cycles) { + hub.force_send_front(); + hub.receive_frame_for_test(0x02, response_pdu); + cycles++; + } + return cycles; +} +} // namespace + +constexpr uint8_t OK_RESPONSE[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; + +// One request produces exactly one data callback. +TEST(ModbusClientHubCallbackCount, SingleReadSingleCallback) { + NoResponseProbeHub hub; + DataCountingDevice device(&hub, 0x02); + + device.send_pdu(read_pdu()); + drain_with_responses(hub, OK_RESPONSE); + + EXPECT_EQ(device.data_count_, 1); + EXPECT_EQ(device.not_sent_count_, 0); + EXPECT_EQ(hub.queued_frames(), 0u); + EXPECT_FALSE(hub.waiting()); +} + +// An exception response is a terminal on its own: exactly one on_error(), no others, +// preceded by exactly one on_sent(). +TEST(ModbusClientHubCallbackCount, ErrorResponseIsSoleTerminal) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); + DataCountingDevice device(&hub, 0x02); + + device.send_pdu(read_pdu()); + hub.send_next_for_test(); + const uint8_t exception_response[] = {0x83, 0x02}; + hub.receive_frame_for_test(0x02, exception_response); + + EXPECT_EQ(device.error_count_, 1); + EXPECT_EQ(device.terminals(), 1); + EXPECT_EQ(device.sent_count_, 1); +} + +// A timeout is a terminal on its own: exactly one on_no_response(), preceded by one +// on_sent(); a refused duplicate ends in on_not_sent() with NO on_sent(). +TEST(ModbusClientHubCallbackCount, NoResponseIsSoleTerminalAndNotSentHasNoSent) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); + DataCountingDevice device(&hub, 0x02); + + device.send_pdu(read_pdu()); + hub.send_next_for_test(); + hub.timeout_waiting(); + EXPECT_EQ(device.no_response_count_, 1); + EXPECT_EQ(device.terminals(), 1); + EXPECT_EQ(device.sent_count_, 1); + + // A refused send (empty PDU) is a not_sent terminal, never sent. + const uint8_t write_pdu[] = {0x06, 0x00, 0x10, 0xBE, 0xEF}; + device.send_pdu(write_pdu); + device.send_pdu(std::span{}); + EXPECT_EQ(device.not_sent_count_, 1); + EXPECT_EQ(device.terminals(), 2); // the accepted write is still queued - no terminal for it yet + EXPECT_EQ(device.sent_count_, 1); // and it has not transmitted yet + + // Drain it: the write echo response is its data terminal, and the books balance. + hub.send_next_for_test(); + hub.receive_frame_for_test(0x02, write_pdu); + EXPECT_EQ(device.data_count_, 1); + EXPECT_EQ(device.terminals(), 3); // 3 accepted lifecycles, 3 terminals + EXPECT_EQ(device.sent_count_, 2); // 2 transmissions (read + write); the refused send never sent +} + +// A device-requested retry starts a new lifecycle: each transmission gets its own sent + terminal. +TEST(ModbusClientHubCallbackCount, RetryLifecyclesEachGetSentAndTerminal) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); + DataCountingDevice device(&hub, 0x02); + device.retries_ = 1; // ask for exactly one retry + + device.send_pdu(read_pdu()); + hub.send_next_for_test(); + hub.timeout_waiting(); // lifecycle 1: sent + no_response (retry requested -> re-queued) + ASSERT_EQ(hub.queued_frames(), 1u); + hub.send_next_for_test(); + hub.timeout_waiting(); // lifecycle 2: sent + no_response (retry declined -> done) + + EXPECT_EQ(device.no_response_count_, 2); + EXPECT_EQ(device.terminals(), 2); + EXPECT_EQ(device.sent_count_, 2); + EXPECT_EQ(hub.queued_frames(), 0u); + // The retried lifecycle's timeout carries the SAME request PDU as the first attempt. + EXPECT_EQ(device.last_no_response_pdu_, std::vector(READ_PDU, READ_PDU + sizeof(READ_PDU))); +} + +// A retry re-queue that finds the buffer full is refused like any other send: the device gets +// on_not_sent() carrying the request PDU (the previously uncovered requeue_waiting_frame_ branch). +TEST(ModbusClientHubCallbackCount, FullQueueRetryRefusalDeliversNotSentWithPdu) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); + DataCountingDevice device(&hub, 0x02); + device.retries_ = 1; + SentCountingDevice filler(&hub, 0x05); + + device.send_pdu(read_pdu()); + hub.force_send_front(); // in flight + // Fill the queue with distinct frames. + for (uint16_t i = 0; i < MODBUS_TX_BUFFER_SIZE; i++) { + const uint8_t fill[] = {0x03, static_cast(i >> 8), static_cast(i & 0xFF), 0x00, 0x01}; + filler.send_pdu(fill); + } + ASSERT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); + + hub.timeout_waiting(); // retry requested, but the re-queue is refused: not_sent terminal instead + + EXPECT_EQ(device.no_response_count_, 1); + EXPECT_EQ(device.not_sent_count_, 1); + EXPECT_EQ(device.last_not_sent_pdu_, std::vector(READ_PDU, READ_PDU + sizeof(READ_PDU))); + EXPECT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); +} + +// The deprecated device-side send_raw() refusal delivers through the same guard as every other +// path: a handler that reacts to its own refusal with another empty send_raw() stays bounded. +namespace { +class SendRawOnNotSentDevice : public ModbusClientDevice { + public: + SendRawOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + this->send_raw({}); // refused again; the guard must suppress the nested delivery +#pragma GCC diagnostic pop + } + int not_sent_count_{0}; +}; +} // namespace + +TEST(ModbusClientHubQueue, SendRawRefusalIsGuardedAgainstRecursion) { + NoResponseProbeHub hub; + SendRawOnNotSentDevice device(&hub, 0x02); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + device.send_raw({}); // empty payload refused -> on_not_sent -> nested send_raw({}) suppressed +#pragma GCC diagnostic pop + EXPECT_EQ(device.not_sent_count_, 1); +} + +namespace { +// A device that chains a follow-up send from inside on_sent(). +class ChainOnSentDevice : public ModbusClientDevice { + public: + ChainOnSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_sent(std::span request_pdu) override { + if (!this->chained_) { + this->chained_ = true; + const uint8_t follow[] = {0x03, 0x00, 0x09, 0x00, 0x01}; // read holding 0x0009 x1 + this->send_pdu(follow); + } + } + bool chained_{false}; +}; +} // namespace + +// clear_tx_queue_for_address() resolves every dropped frame via its owner's on_not_sent(), so a device +// sharing the address with the clearer (e.g. a modbus_client action alongside an offline controller) +// observes the drop; frames for other addresses are untouched. +TEST(ModbusClientHubQueue, ClearAddressQueueNotifiesEveryOwner) { + NoResponseProbeHub hub; + SentCountingDevice controller_like(&hub, 0x02); + SentCountingDevice bystander_same(&hub, 0x02); + SentCountingDevice bystander_other(&hub, 0x03); + + const uint8_t read_a[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + const uint8_t read_b[] = {0x03, 0x02, 0x00, 0x00, 0x02}; + const uint8_t read_c[] = {0x03, 0x03, 0x00, 0x00, 0x02}; + controller_like.send_pdu(read_a); + bystander_same.send_pdu(read_b); + bystander_other.send_pdu(read_c); + ASSERT_EQ(hub.queued_frames(), 3u); + + controller_like.clear_tx_queue_for_address(false); + + ASSERT_EQ(hub.queued_frames(), 1u); // only the other-address frame remains + EXPECT_EQ(hub.front().frame.address(), 0x03); + EXPECT_EQ(controller_like.not_sent_count_, 1); + EXPECT_EQ(bystander_same.not_sent_count_, 1); + EXPECT_EQ(bystander_other.not_sent_count_, 0); + // each owner saw its own request PDU + EXPECT_EQ(bystander_same.last_not_sent_pdu_, std::vector(std::begin(read_b), std::end(read_b))); +} + +namespace { +// Re-sends its frame once from inside on_not_sent - the re-queued frame must survive the sweep. +class ResendOnNotSentDevice : public ModbusClientDevice { + public: + ResendOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + if (this->not_sent_count_ == 1) { + const uint8_t again[] = {0x06, 0x00, 0x40, 0x00, 0x01}; + this->send_pdu(again); + } + } + int not_sent_count_{0}; +}; +} // namespace + +// A handler that re-sends to the same address from inside on_not_sent() neither corrupts the sweep nor +// loops it: only initially-marked frames are swept, so the re-queued frame stays queued. +TEST(ModbusClientHubQueue, ClearAddressReentrantResendSurvives) { + NoResponseProbeHub hub; + ResendOnNotSentDevice device(&hub, 0x02); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + device.send_pdu(read); + ASSERT_EQ(hub.queued_frames(), 1u); + + hub.clear_tx_queue_for_address(0x02, false); + + // The original frame resolved via on_not_sent; the re-send from inside that callback remains queued. + EXPECT_EQ(device.not_sent_count_, 1); + ASSERT_EQ(hub.queued_frames(), 1u); + EXPECT_EQ(hub.front().frame.address(), 0x02); +} + +namespace { +// Retries from EVERY on_not_sent - against a full queue this recursed without bound before the guard. +class AlwaysRetryDevice : public ModbusClientDevice { + public: + AlwaysRetryDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + const uint8_t again[] = {0x03, 0x00, 0x50, 0x00, 0x01}; + this->send_pdu(again); + } + int not_sent_count_{0}; +}; + +// From inside on_not_sent, clears ANOTHER address - those victims must still be notified (the per-device +// guard suppresses deliveries only to a device already inside its own on_not_sent()). +class ClearOtherOnNotSentDevice : public ModbusClientDevice { + public: + ClearOtherOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + this->parent_->clear_tx_queue_for_address(0x03, false); + } + int not_sent_count_{0}; +}; +} // namespace + +// A handler that retries from every on_not_sent() against a FULL queue must not recurse: the first +// refusal notifies once, the nested refusal is dropped without a callback (the documented guard). +TEST(ModbusClientHubQueue, FullQueueRetryFromNotSentDoesNotRecurse) { + NoResponseProbeHub hub; + SentCountingDevice filler(&hub, 0x05); + AlwaysRetryDevice retrier(&hub, 0x02); + + // Fill the queue with distinct frames. + for (uint16_t i = 0; i < MODBUS_TX_BUFFER_SIZE; i++) { + const uint8_t fill[] = {0x03, static_cast(i >> 8), static_cast(i & 0xFF), 0x00, 0x01}; + filler.send_pdu(fill); + } + ASSERT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + retrier.send_pdu(read); // refused (full) -> on_not_sent -> retry -> refused under the guard, silently + + EXPECT_EQ(retrier.not_sent_count_, 1); + EXPECT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); +} + +namespace { +// From inside on_not_sent, triggers ANOTHER device's send (which will be refused too). +class SendOtherOnNotSentDevice : public ModbusClientDevice { + public: + SendOtherOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + if (this->other_ != nullptr) { + const uint8_t read[] = {0x03, 0x00, 0x60, 0x00, 0x01}; + this->other_->send_pdu(read); + } + } + ModbusClientDevice *other_{nullptr}; + int not_sent_count_{0}; +}; +} // namespace + +// The refusal recursion guard is per-device: a refusal that lands on a DIFFERENT device while one +// device's notification is on the stack must still deliver - that device did not cause the recursion +// and would otherwise silently lose its terminal callback. +TEST(ModbusClientHubQueue, RefusalForOtherDeviceDeliversDuringNotification) { + NoResponseProbeHub hub; + SentCountingDevice filler(&hub, 0x05); + SendOtherOnNotSentDevice first(&hub, 0x02); + SentCountingDevice second(&hub, 0x03); + first.other_ = &second; + + // Fill the queue with distinct frames. + for (uint16_t i = 0; i < MODBUS_TX_BUFFER_SIZE; i++) { + const uint8_t fill[] = {0x03, static_cast(i >> 8), static_cast(i & 0xFF), 0x00, 0x01}; + filler.send_pdu(fill); + } + ASSERT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + first.send_pdu(read); // refused -> first.on_not_sent -> second's send refused -> second notified + + EXPECT_EQ(first.not_sent_count_, 1); + EXPECT_EQ(second.not_sent_count_, 1); +} + +// Two devices whose handlers each trigger the other's send cannot recurse without bound: each device +// can be on the notification stack at most once, so the cycle dies as soon as it returns to a device +// whose own on_not_sent() is still running. +TEST(ModbusClientHubQueue, TwoDeviceRefusalCycleTerminates) { + NoResponseProbeHub hub; + SentCountingDevice filler(&hub, 0x05); + SendOtherOnNotSentDevice first(&hub, 0x02); + SendOtherOnNotSentDevice second(&hub, 0x03); + first.other_ = &second; + second.other_ = &first; + + // Fill the queue with distinct frames. + for (uint16_t i = 0; i < MODBUS_TX_BUFFER_SIZE; i++) { + const uint8_t fill[] = {0x03, static_cast(i >> 8), static_cast(i & 0xFF), 0x00, 0x01}; + filler.send_pdu(fill); + } + ASSERT_EQ(hub.queued_frames(), MODBUS_TX_BUFFER_SIZE); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + first.send_pdu(read); // refuse -> first -> second refused -> second -> first suppressed -> unwind + + EXPECT_EQ(first.not_sent_count_, 1); + EXPECT_EQ(second.not_sent_count_, 1); +} + +namespace { +// From inside on_not_sent, clears its OWN address - its remaining queued frames resolve silently +// (the guard suppresses self-deliveries), while other owners on the address are still notified. +class ClearOwnAddressOnNotSentDevice : public ModbusClientDevice { + public: + ClearOwnAddressOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + this->clear_tx_queue_for_address(/*clear_sent=*/false); + } + int not_sent_count_{0}; +}; +} // namespace + +// The documented cost of the per-device guard: a clear issued from inside your own on_not_sent() +// resolves your remaining frames silently (like clear_tx_queue_for_device() - you cleared them, you +// know), while other owners sharing the address are still notified. +TEST(ModbusClientHubQueue, SelfClearFromNotSentSilentForClearerNotifiesOthers) { + NoResponseProbeHub hub; + ClearOwnAddressOnNotSentDevice clearer(&hub, 0x02); + SentCountingDevice bystander(&hub, 0x02); + + const uint8_t read_a[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + const uint8_t read_b[] = {0x03, 0x00, 0x20, 0x00, 0x01}; + const uint8_t read_c[] = {0x03, 0x00, 0x30, 0x00, 0x01}; + clearer.send_pdu(read_a); + clearer.send_pdu(read_b); + bystander.send_pdu(read_c); + ASSERT_EQ(hub.queued_frames(), 3u); + + clearer.send_pdu(std::span{}); // refused (empty) -> the handler clears the shared address + + EXPECT_EQ(clearer.not_sent_count_, 1); // only the refusal; the two swept frames resolve silently + EXPECT_EQ(bystander.not_sent_count_, 1); // the bystander's swept frame is still notified + EXPECT_EQ(hub.queued_frames(), 0u); +} + +// The guard must not over-suppress: a sweep started from inside on_not_sent() still delivers its +// victims' notifications (only nested refusals are silenced). +TEST(ModbusClientHubQueue, NestedClearFromNotSentStillNotifiesVictims) { + NoResponseProbeHub hub; + ClearOtherOnNotSentDevice clearer(&hub, 0x02); + SentCountingDevice victim(&hub, 0x03); + + const uint8_t read_a[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + const uint8_t read_b[] = {0x03, 0x00, 0x20, 0x00, 0x01}; + clearer.send_pdu(read_a); + victim.send_pdu(read_b); + ASSERT_EQ(hub.queued_frames(), 2u); + + hub.clear_tx_queue_for_address(0x02, false); // clearer's on_not_sent clears address 0x03 in turn + + EXPECT_EQ(clearer.not_sent_count_, 1); + EXPECT_EQ(victim.not_sent_count_, 1); // delivered despite arriving from a nested sweep + EXPECT_EQ(hub.queued_frames(), 0u); +} + +namespace { +// tx_blocked() flips to blocked after the first check, so send_next_frame_() passes its own gate but +// send_frame_() refuses - a deterministic transmit failure. +class FlakyBlockHub : public NoResponseProbeHub { + public: + bool tx_blocked() override { + this->tx_blocked_calls_++; + return this->tx_blocked_calls_ > 1; + } + int tx_blocked_calls_{0}; +}; + +// Reacts to a transmit failure by sending another frame from inside the failure callback. +class WriteOnNotSentDevice : public ModbusClientDevice { + public: + WriteOnNotSentDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { + this->not_sent_count_++; + const uint8_t write[] = {0x06, 0x00, 0x40, 0x01, 0x02}; + this->send_pdu(write); + } + int not_sent_count_{0}; +}; + +} // namespace + +// A transmit failure must resolve with the failed frame OUT of the queue before its on_not_sent runs: a +// handler that reacts by sending a new frame must not have that frame discarded by the pop that +// follows - the failed frame is popped first, the new frame survives. +TEST(ModbusClientHubQueue, TransmitFailurePopsBeforeNotify) { + FlakyBlockHub hub; + WriteOnNotSentDevice device(&hub, 0x02); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + device.send_pdu(read); + ASSERT_EQ(hub.queued_frames(), 1u); + + hub.send_next_for_test(); // tx_blocked gate passes, send_frame_ refuses -> failure path + + EXPECT_EQ(device.not_sent_count_, 1); + ASSERT_EQ(hub.queued_frames(), 1u); // the handler's write survives... + EXPECT_EQ(hub.front().frame.pdu()[0], 0x06); // ...and it is the write, not the failed read +} + +// clear_tx_queue_for_device() drops queued frames SILENTLY - no terminal callback (the documented +// exception to the exactly-one-terminal contract; used during teardown/offline handling). +TEST(ModbusClientHubQueue, ClearDeviceQueueDropsSilently) { + NoResponseProbeHub hub; + SentCountingDevice device(&hub, 0x02); + + const uint8_t read_a[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + const uint8_t read_b[] = {0x03, 0x02, 0x00, 0x00, 0x02}; + device.send_pdu(read_a); + device.send_pdu(read_b); + ASSERT_EQ(hub.queued_frames(), 2u); + + device.clear_tx_queue_for_device(); + + EXPECT_EQ(hub.queued_frames(), 0u); + EXPECT_EQ(device.not_sent_count_, 0); // silent drop: no terminal callback +} + +// A send_pdu() from inside on_sent() enqueues behind the in-flight frame rather than sending +// immediately or corrupting the in-flight transaction. +TEST(ModbusClientHubSent, ReentrantSendFromOnSentQueues) { + NullUART uart; + NoResponseProbeHub hub; + hub.set_uart_parent(&uart); + hub.setup(); + ChainOnSentDevice device(&hub, 0x02); + + device.send_pdu(read_pdu()); + hub.send_next_for_test(); // first frame goes on the wire -> on_sent chains a follow-up + + EXPECT_TRUE(hub.waiting()); // first frame is in flight + ASSERT_EQ(hub.queued_frames(), 1u); // the follow-up queued behind it, not sent + EXPECT_EQ(hub.queued(0).frame.pdu()[2], 0x09); // it is the chained read (start address 0x0009) +} + +namespace { +// Overrides only the DEPRECATED on_modbus_* names: the new-name default implementations must forward, so +// external devices written against the old names keep working through the deprecation window. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +class LegacyNameDevice : public ModbusClientDevice { + public: + LegacyNameDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_modbus_not_sent() override { this->legacy_not_sent_++; } + bool on_modbus_no_response() override { + this->legacy_no_response_++; + return false; + } + int legacy_not_sent_{0}; + int legacy_no_response_{0}; +}; +#pragma GCC diagnostic pop +} // namespace + +TEST(ModbusClientHubCompat, LegacyCallbackNamesStillForward) { + NoResponseProbeHub hub; + LegacyNameDevice device(&hub, 0x02); + + const uint8_t read[] = {0x03, 0x00, 0x10, 0x00, 0x01}; + device.send_pdu(read); + hub.force_send_front(); + hub.timeout_waiting(); // no reply -> on_no_response -> forwards to on_modbus_no_response + EXPECT_EQ(device.legacy_no_response_, 1); + + device.send_pdu(std::span()); // empty PDU refused -> on_not_sent -> forwards + EXPECT_EQ(device.legacy_not_sent_, 1); +} + +// The send_pdu() capacity bound: a PDU larger than MAX_PDU_SIZE would build a frame past the RTU +// 256-byte limit, so it is refused up front and signalled like any other failed send. +TEST(ModbusClientHub, OversizedPduIsRefusedWithNotSent) { + NoResponseProbeHub hub; + LegacyNameDevice device(&hub, 0x02); + std::vector big(MAX_PDU_SIZE + 1, 0x41); + device.send_pdu(big); + EXPECT_EQ(device.legacy_not_sent_, 1); // on_not_sent, observed via the legacy forward + EXPECT_TRUE(hub.tx_buffer_empty()); +} + +// --- ModbusDevice compatibility shim ------------------------------------------------------------ +// External components written against the pre-2026.8 API subclass ModbusDevice and override the +// old callbacks; the shim adapts the span-based hooks back to those signatures. +namespace { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +class LegacyApiDevice : public ModbusDevice { + public: + LegacyApiDevice(ModbusClientHub *hub, uint8_t address) : ModbusDevice(hub, address) {} + void on_modbus_data(const std::vector &data) override { this->last_data_ = data; } + void on_modbus_error(uint8_t function_code, uint8_t exception_code) override { + this->last_error_fc_ = function_code; + this->last_error_code_ = exception_code; + } + std::vector last_data_; + int last_error_fc_{-1}; + int last_error_code_{-1}; +}; +#pragma GCC diagnostic pop +} // namespace + +TEST(ModbusDeviceShim, LegacyCallbacksReceiveTheOldShapes) { + NoResponseProbeHub hub; + LegacyApiDevice device(&hub, 0x02); + + // Read response: on_modbus_data() historically received the payload after the function code and + // the byte-count byte, as an owning vector. + const uint8_t read_req[] = {0x03, 0x00, 0x10, 0x00, 0x02}; + device.send_pdu(read_req); + hub.force_send_front(); + const uint8_t response[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; + hub.receive_frame_for_test(0x02, response); + const std::vector expected{0x00, 0x2A, 0x01, 0x00}; + EXPECT_EQ(device.last_data_, expected); + + // Write echo: no byte-count byte, so the payload is everything after the function code. + const uint8_t write_req[] = {0x06, 0x00, 0x10, 0x00, 0x2A}; + device.send_pdu(write_req); + hub.force_send_front(); + hub.receive_frame_for_test(0x02, write_req); // single-write responses echo the request + const std::vector expected_echo{0x00, 0x10, 0x00, 0x2A}; + EXPECT_EQ(device.last_data_, expected_echo); + + // Exception response: on_modbus_error() received the masked function code and the exception code. + device.send_pdu(read_req); + hub.force_send_front(); + const uint8_t error[] = {0x83, 0x02}; + hub.receive_frame_for_test(0x02, error); + EXPECT_EQ(device.last_error_fc_, 0x03); + EXPECT_EQ(device.last_error_code_, 0x02); +} + +// --- typed send helpers -------------------------------------------------------------------------- +// Each helper is a one-line forward onto a merged builder; these pin the function code and wire +// bytes each one queues, so a swapped code or transposed field cannot survive review silently. +TEST(ModbusTypedSendHelpers, HelpersQueueExpectedPdus) { + NoResponseProbeHub hub; + ModbusClientDevice device(&hub, 0x02); + auto check = [&](const std::vector &expected) { + ASSERT_EQ(hub.queued_frames(), 1u); + auto pdu = hub.front().frame.pdu(); + EXPECT_EQ(std::vector(pdu.begin(), pdu.end()), expected); + hub.force_send_front(); + hub.timeout_waiting(); // default on_no_response() declines the retry, dropping the frame + }; + + device.read_holding_registers(0x0102, 3); + check({0x03, 0x01, 0x02, 0x00, 0x03}); + device.read_input_registers(0x0010, 2); + check({0x04, 0x00, 0x10, 0x00, 0x02}); + device.read_coils(0x0020, 10); + check({0x01, 0x00, 0x20, 0x00, 0x0A}); + device.read_discrete_inputs(0x0030, 1); + check({0x02, 0x00, 0x30, 0x00, 0x01}); + device.write_single_register(0x0040, 0xABCD); + check({0x06, 0x00, 0x40, 0xAB, 0xCD}); + device.write_single_coil(0x0041, true); + check({0x05, 0x00, 0x41, 0xFF, 0x00}); + device.write_single_coil(0x0041, false); + check({0x05, 0x00, 0x41, 0x00, 0x00}); + const uint16_t regs[] = {0x000B, 0x0016}; + device.write_multiple_registers(0x0050, regs); + check({0x10, 0x00, 0x50, 0x00, 0x02, 0x04, 0x00, 0x0B, 0x00, 0x16}); + const bool coils[] = {true, false, true}; + device.write_multiple_coils(0x0060, coils); + check({0x0F, 0x00, 0x60, 0x00, 0x03, 0x01, 0x05}); + const uint8_t packed[] = {0x05}; + device.write_multiple_coils(0x0060, PackedBits(packed, 3)); // packed overload, same wire bytes + check({0x0F, 0x00, 0x60, 0x00, 0x03, 0x01, 0x05}); +} + +TEST(ModbusTypedSendHelpers, ReadEntitiesDispatchesByTypeAndRejectsInvalid) { + NoResponseProbeHub hub; + ModbusClientDevice device(&hub, 0x02); + + device.read_entities(EntityType::HOLDING, 0x0001, 1); + ASSERT_EQ(hub.queued_frames(), 1u); + EXPECT_EQ(hub.front().frame.pdu()[0], 0x03); + hub.force_send_front(); + hub.timeout_waiting(); + + device.read_entities(EntityType::DISCRETE_INPUT, 0x0001, 1); + ASSERT_EQ(hub.queued_frames(), 1u); + EXPECT_EQ(hub.front().frame.pdu()[0], 0x02); + hub.force_send_front(); + hub.timeout_waiting(); + + device.read_entities(EntityType::CUSTOM, 0x0001, 1); // no read function: logged and not queued + EXPECT_EQ(hub.queued_frames(), 0u); +} + +// A rejected read_entities() signals on_not_sent() like every other refused send. +namespace { +class NotSentCountingDevice : public ModbusClientDevice { + public: + NotSentCountingDevice(ModbusClientHub *hub, uint8_t address) : ModbusClientDevice(hub, address) {} + void on_not_sent(std::span request_pdu) override { this->not_sent_++; } + int not_sent_{0}; +}; +} // namespace + +TEST(ModbusTypedSendHelpers, InvalidReadEntitiesSignalsNotSent) { + NoResponseProbeHub hub; + NotSentCountingDevice device(&hub, 0x02); + device.read_entities(EntityType::CUSTOM, 0x0001, 1); + EXPECT_EQ(device.not_sent_, 1); + EXPECT_EQ(hub.queued_frames(), 0u); +} + } // namespace esphome::modbus::testing diff --git a/tests/components/modbus/modbus_helpers_test.cpp b/tests/components/modbus/modbus_helpers_test.cpp index 1c57a81e6f..49de4f9d14 100644 --- a/tests/components/modbus/modbus_helpers_test.cpp +++ b/tests/components/modbus/modbus_helpers_test.cpp @@ -1,10 +1,12 @@ #include +#include + #include "esphome/components/modbus/modbus_helpers.h" namespace esphome::modbus::helpers { -using FC = ModbusFunctionCode; +using FC = FunctionCode; // --- server_frame_length --------------------------------------------------- // Frame layout: address(1) + function(1) + ... + CRC(2). Fixtures borrowed from @@ -83,6 +85,13 @@ TEST(ModbusClientFrameLength, WriteMultipleByteCountCapped) { EXPECT_EQ(client_frame_length(frame, sizeof(frame)), 9 + MAX_NUM_OF_REGISTERS_TO_WRITE * 2); } +TEST(ModbusClientFrameLength, ReadWriteMultipleByteCountCappedAtSpecLimit) { + // FC 0x17's write byte count caps at the spec 6.17 limit of 121 registers (242 bytes), deliberately + // tighter than FC 0x10's 123, so a corrupt byte count cannot make the parser wait past the real frame. + const uint8_t pdu[] = {0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0xFF}; // claims 255 bytes + EXPECT_EQ(client_pdu_length(pdu, sizeof(pdu)), 10 + MAX_NUM_OF_REGISTERS_TO_WRITE_RW * 2); +} + TEST(ModbusClientFrameLength, WriteMultipleMissingByteCount) { const uint8_t frame[] = {0x01, 0x10, 0x00, 0x00, 0x00, 0x02}; EXPECT_EQ(client_frame_length(frame, sizeof(frame)), 9); @@ -97,6 +106,124 @@ TEST(ModbusClientFrameLength, MiscFixedAndUnknown) { EXPECT_EQ(client_frame_length(unknown, sizeof(unknown)), MIN_FRAME_SIZE); } +// --- file-record length cap -------------------------------------------------- +// FC 0x14/0x15 are parsed only to keep the frame parser in sync; the byte count caps at 251 +// (MAX_PDU_SIZE - 2), reproducing the released frame-relative bound of MAX_FRAME_SIZE - 5. + +TEST(ModbusFileRecordCap, PduLengthCapsByteCountAt251) { + const uint8_t pdu[] = {static_cast(FC::READ_FILE_RECORD), 0xFF}; // claims 255 bytes + EXPECT_EQ(server_pdu_length(pdu, sizeof(pdu)), 2 + (MAX_PDU_SIZE - 2)); + EXPECT_EQ(client_pdu_length(pdu, sizeof(pdu)), 2 + (MAX_PDU_SIZE - 2)); + // Frame wrappers: address(1) + PDU + CRC(2) stays within the RTU 256-byte frame limit. + const uint8_t frame[] = {0x01, static_cast(FC::WRITE_FILE_RECORD), 0xFF}; + EXPECT_EQ(server_frame_length(frame, sizeof(frame)), MAX_FRAME_SIZE); + EXPECT_EQ(client_frame_length(frame, sizeof(frame)), MAX_FRAME_SIZE); +} + +TEST(ModbusFileRecordCap, StandardChecksAcceptUpTo251) { + // A full-length PDU at the cap: function(1) + byte count(1) + 251 data bytes = MAX_PDU_SIZE. + std::vector at_cap(MAX_PDU_SIZE, 0x00); + at_cap[0] = static_cast(FC::READ_FILE_RECORD); + at_cap[1] = MAX_PDU_SIZE - 2; + EXPECT_TRUE(is_server_pdu_standard(at_cap.data(), at_cap.size())); + EXPECT_TRUE(is_client_pdu_standard(at_cap.data(), at_cap.size())); + // Byte count 252 in the same 253-byte buffer: the parsed length still matches (capped), so this + // exercises the byte-count bound itself rather than the length identity. + at_cap[1] = MAX_PDU_SIZE - 1; + EXPECT_FALSE(is_server_pdu_standard(at_cap.data(), at_cap.size())); + EXPECT_FALSE(is_client_pdu_standard(at_cap.data(), at_cap.size())); +} + +// --- is_client_pdu_standard / is_server_pdu_standard ------------------------- +// The gatekeepers for the typed client dispatch: a PDU must be exactly its function code's standard +// shape, with byte count, quantity, and address range all consistent. + +TEST(ModbusPduStandard, ClientReadAndWriteConformant) { + const uint8_t read_regs[] = {0x03, 0x01, 0x00, 0x00, 0x02}; + EXPECT_TRUE(is_client_pdu_standard(read_regs, sizeof(read_regs))); + const uint8_t write_regs[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, 0x02}; + EXPECT_TRUE(is_client_pdu_standard(write_regs, sizeof(write_regs))); + // 10 coils pack into 2 data bytes - the coil formula, not the register one. + const uint8_t write_coils[] = {0x0F, 0x00, 0x30, 0x00, 0x0A, 0x02, 0xFF, 0x03}; + EXPECT_TRUE(is_client_pdu_standard(write_coils, sizeof(write_coils))); +} + +TEST(ModbusPduStandard, ClientRejectsNonConformant) { + // Truncated: header claims 4 data bytes, only 2 present. + const uint8_t truncated[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x04, 0x00, 0x01}; + EXPECT_FALSE(is_client_pdu_standard(truncated, sizeof(truncated))); + // Byte count disagrees with quantity (2 registers need 4 bytes, header says 2). + const uint8_t inconsistent[] = {0x10, 0x00, 0x20, 0x00, 0x02, 0x02, 0x00, 0x01}; + EXPECT_FALSE(is_client_pdu_standard(inconsistent, sizeof(inconsistent))); + // Coil write using the register byte-count formula (10 coils with 20 data bytes). + const uint8_t coil_as_regs[] = {0x0F, 0x00, 0x30, 0x00, 0x0A, 0x14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + EXPECT_FALSE(is_client_pdu_standard(coil_as_regs, sizeof(coil_as_regs))); + // Quantity zero and quantity beyond the per-function-code maximum. + const uint8_t zero_qty[] = {0x03, 0x01, 0x00, 0x00, 0x00}; + EXPECT_FALSE(is_client_pdu_standard(zero_qty, sizeof(zero_qty))); + const uint8_t too_many[] = {0x03, 0x01, 0x00, 0x00, 0x7E}; // 126 > 125 + EXPECT_FALSE(is_client_pdu_standard(too_many, sizeof(too_many))); + // Address range overflow: 0xFFFF + 2 registers exceeds the 16-bit register space. + const uint8_t wraps[] = {0x03, 0xFF, 0xFF, 0x00, 0x02}; + EXPECT_FALSE(is_client_pdu_standard(wraps, sizeof(wraps))); +} + +TEST(ModbusPduStandard, ServerReadResponses) { + const uint8_t ok[] = {0x03, 0x04, 0x00, 0x2A, 0x01, 0x00}; + EXPECT_TRUE(is_server_pdu_standard(ok, sizeof(ok))); + // Byte-count header disagrees with the actual length. + const uint8_t lying[] = {0x03, 0x06, 0x00, 0x2A, 0x01, 0x00}; + EXPECT_FALSE(is_server_pdu_standard(lying, sizeof(lying))); + // An empty PDU (the on_error path) is not a standard response. + EXPECT_FALSE(is_server_pdu_standard(ok, 0)); +} + +TEST(ModbusPduStandard, ServerResponsesRejectDegenerateShapes) { + // A read response always carries data: byte count zero is non-conformant. + const uint8_t zero_bc[] = {0x03, 0x00}; + EXPECT_FALSE(is_server_pdu_standard(zero_bc, sizeof(zero_bc))); + // Registers are 2 bytes each: an odd byte count would silently truncate a register. + const uint8_t odd_bc[] = {0x03, 0x03, 0x00, 0x01, 0x02}; + EXPECT_FALSE(is_server_pdu_standard(odd_bc, sizeof(odd_bc))); + // Bit reads have no parity requirement: one packed byte is a fine coil response. + const uint8_t coil_one_byte[] = {0x01, 0x01, 0x05}; + EXPECT_TRUE(is_server_pdu_standard(coil_one_byte, sizeof(coil_one_byte))); + // A write-multiple echo claiming 65535 registers written is bounded like the request side. + const uint8_t wild_echo[] = {0x10, 0x00, 0x00, 0xFF, 0xFF}; + EXPECT_FALSE(is_server_pdu_standard(wild_echo, sizeof(wild_echo))); + const uint8_t ok_echo[] = {0x10, 0x00, 0x00, 0x00, 0x02}; + EXPECT_TRUE(is_server_pdu_standard(ok_echo, sizeof(ok_echo))); +} + +TEST(ModbusPduStandard, SingleCoilValueMustBeCanonical) { + // FC 0x05's value field allows exactly 0xFF00 (ON) and 0x0000 (OFF); anything else is non-standard. + const uint8_t on[] = {0x05, 0x00, 0x10, 0xFF, 0x00}; + const uint8_t off[] = {0x05, 0x00, 0x10, 0x00, 0x00}; + const uint8_t junk[] = {0x05, 0x00, 0x10, 0x12, 0x34}; + EXPECT_TRUE(is_client_pdu_standard(on, sizeof(on))); + EXPECT_TRUE(is_client_pdu_standard(off, sizeof(off))); + EXPECT_FALSE(is_client_pdu_standard(junk, sizeof(junk))); + EXPECT_TRUE(is_server_pdu_standard(on, sizeof(on))); // the response echoes the request + EXPECT_FALSE(is_server_pdu_standard(junk, sizeof(junk))); +} + +TEST(ModbusPduStandard, NonStandardFunctionCodesAcceptedOnLengthAlone) { + // Custom, unimplemented, and exception function codes have no standard shape to check: they are + // accepted whenever the parsed length matches, so a dispatcher can still route them by function + // code instead of having them rejected outright. This is the documented contract - see the header. + const uint8_t custom[] = {0x42}; // user-defined space; 1 byte matches the MIN_PDU_SIZE fallback + EXPECT_TRUE(is_client_pdu_standard(custom, sizeof(custom))); + EXPECT_TRUE(is_server_pdu_standard(custom, sizeof(custom))); + const uint8_t unimplemented[] = {0x07}; // READ_EXCEPTION_STATUS + EXPECT_TRUE(is_server_pdu_standard(unimplemented, sizeof(unimplemented))); + const uint8_t exception[] = {0x83, 0x02}; // exception response; length pinned to 2 bytes + EXPECT_TRUE(is_server_pdu_standard(exception, sizeof(exception))); + // The length identity still gates: extra bytes beyond the parsed fallback are non-conformant. + const uint8_t custom_long[] = {0x42, 0x01}; + EXPECT_FALSE(is_client_pdu_standard(custom_long, sizeof(custom_long))); +} + // --- create_client_pdu ----------------------------------------------------- // PDU = function code + data (no address, no CRC). @@ -179,6 +306,31 @@ TEST(ModbusCreateClientPdu, WriteMultipleOverEntityLimitReturnsEmpty) { EXPECT_TRUE(pdu.empty()); } +// The generic write path requires the data length to agree exactly with the entity count +// (registers: 2 bytes each; coils: 8 packed per byte) - the same rule the response dispatch +// enforces via is_client_pdu_standard(), so a frame built here always passes that gate. +TEST(ModbusCreateClientPdu, WriteMultipleRejectsMismatchedDataLength) { + const uint8_t values[] = {0x00, 0x0B, 0x00, 0x16}; + // 2 registers need exactly 4 data bytes. + EXPECT_TRUE(create_client_pdu(FC::WRITE_MULTIPLE_REGISTERS, 0x0000, 2, values, 3).empty()); + EXPECT_FALSE(create_client_pdu(FC::WRITE_MULTIPLE_REGISTERS, 0x0000, 2, values, 4).empty()); + // 10 coils pack into exactly 2 data bytes - the coil formula, not the register one. + EXPECT_FALSE(create_client_pdu(FC::WRITE_MULTIPLE_COILS, 0x0000, 10, values, 2).empty()); + EXPECT_TRUE(create_client_pdu(FC::WRITE_MULTIPLE_COILS, 0x0000, 10, values, 4).empty()); +} + +TEST(ModbusCreateClientPdu, WriteCoilsUseTheCoilLimitNotTheRegisterLimit) { + // 200 coils: above the 123-register write limit but well within the 1968-coil limit; 25 data bytes. + std::vector values(25, 0xAA); + auto pdu = create_client_pdu(FC::WRITE_MULTIPLE_COILS, 0x0000, 200, values.data(), values.size()); + ASSERT_FALSE(pdu.empty()); + EXPECT_EQ(pdu[5], 25); // byte count uses the coil formula + EXPECT_TRUE(is_client_pdu_standard(pdu.data(), pdu.size())); // builder output passes the validator + // Builder and validator agree at the top of the range too: 1969 coils rejected. + std::vector big((1969 + 7) / 8, 0x00); + EXPECT_TRUE(create_client_pdu(FC::WRITE_MULTIPLE_COILS, 0x0000, 1969, big.data(), big.size()).empty()); +} + TEST(ModbusHelpersTest, PayloadToNumberRejectsOffsetAtEndOfBuffer) { const std::vector data{0x12, 0x34}; EXPECT_FALSE(payload_to_number(std::span(data), SensorValueType::U_WORD, 2, 0xFFFFFFFF).has_value()); @@ -229,4 +381,183 @@ TEST(ModbusHelpersTest, RegistersToNumberRejectsTruncatedMultiRegisterValue) { EXPECT_FALSE(registers_to_number(registers, 1, SensorValueType::U_DWORD).has_value()); } +// --- typed builders ---------------------------------------------------------- + +TEST(ModbusTypedBuilders, ReadPduWireBytes) { + auto pdu = create_read_pdu(FC::READ_HOLDING_REGISTERS, 0x0102, 3); + const std::vector expected{0x03, 0x01, 0x02, 0x00, 0x03}; + EXPECT_EQ(std::vector(pdu.begin(), pdu.end()), expected); + EXPECT_TRUE(is_client_pdu_standard(pdu.data(), pdu.size())); + // Reads that run past the 16-bit address space are refused. + EXPECT_TRUE(create_read_pdu(FC::READ_HOLDING_REGISTERS, 0xFFFF, 2).empty()); +} + +TEST(ModbusTypedBuilders, WriteSinglePduWireBytes) { + auto reg = create_write_single_register_pdu(0x0010, 0xABCD); + const std::vector expected_reg{0x06, 0x00, 0x10, 0xAB, 0xCD}; + EXPECT_EQ(std::vector(reg.begin(), reg.end()), expected_reg); + EXPECT_TRUE(is_client_pdu_standard(reg.data(), reg.size())); + auto coil_on = create_write_single_coil_pdu(0x0011, true); + auto coil_off = create_write_single_coil_pdu(0x0011, false); + const std::vector expected_on{0x05, 0x00, 0x11, 0xFF, 0x00}; + const std::vector expected_off{0x05, 0x00, 0x11, 0x00, 0x00}; + EXPECT_EQ(std::vector(coil_on.begin(), coil_on.end()), expected_on); + EXPECT_EQ(std::vector(coil_off.begin(), coil_off.end()), expected_off); + EXPECT_TRUE(is_client_pdu_standard(coil_on.data(), coil_on.size())); + EXPECT_TRUE(is_client_pdu_standard(coil_off.data(), coil_off.size())); +} + +TEST(ModbusTypedBuilders, WriteRegistersPduWireBytes) { + const uint16_t values[] = {0x000B, 0x0016}; + auto pdu = create_write_registers_pdu(0x0000, values); + const std::vector expected{0x10, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x0B, 0x00, 0x16}; + EXPECT_EQ(std::vector(pdu.begin(), pdu.end()), expected); + EXPECT_TRUE(is_client_pdu_standard(pdu.data(), pdu.size())); + // Writes that run past the 16-bit address space are refused. + EXPECT_TRUE(create_write_registers_pdu(0xFFFF, values).empty()); +} + +TEST(ModbusTypedBuilders, WriteRegistersPduRejectsOverLimit) { + std::vector values(MAX_NUM_OF_REGISTERS_TO_WRITE + 1, 0xAAAA); + EXPECT_TRUE(create_write_registers_pdu(0x0000, values).empty()); + values.pop_back(); + EXPECT_FALSE(create_write_registers_pdu(0x0000, values).empty()); +} + +TEST(ModbusTypedBuilders, FloatToPayloadAppendsToExistingContent) { + // The container overload appends - the semantic every migrated caller relies on when a lambda + // has already put words into the buffer. + std::vector data{0x1234}; + float_to_payload(data, 1.0f, SensorValueType::U_WORD); + ASSERT_EQ(data.size(), 2u); + EXPECT_EQ(data[0], 0x1234); + EXPECT_EQ(data[1], 0x0001); +} + +TEST(ModbusCreateClientPdu, ExceptionFlaggedWriteCodesRejected) { + // is_function_code_write() masks the exception bit; the builder must not. + const uint8_t values[] = {0x00, 0x0B, 0x00, 0x16}; + EXPECT_TRUE(create_client_pdu(FunctionCode(0x90), 0x0000, 2, values, 4).empty()); + EXPECT_TRUE(create_client_pdu(FunctionCode(0x85), 0x0000, 1, values, 2).empty()); +} + +TEST(ModbusTypedBuilders, BoolSpanCoilBuilderRejectsOverLimit) { + // This early guard is what keeps the 246-byte packing buffer from overflowing - the shared core's + // identical check runs after packing, so it cannot protect it. + auto big = std::make_unique(MAX_NUM_OF_COILS_TO_WRITE + 1); + EXPECT_TRUE(create_write_coils_pdu(0, std::span(big.get(), MAX_NUM_OF_COILS_TO_WRITE + 1)).empty()); +} + +TEST(ModbusCreateClientPdu, GenericCoilWriteMasksTrailingPadBits) { + // 10 coils with junk in the pad bits of the last data byte: the generic path masks them like the + // typed builder, so both produce identical wire bytes. + const uint8_t values[] = {0xFF, 0xFF}; + auto pdu = create_client_pdu(FC::WRITE_MULTIPLE_COILS, 0x0000, 10, values, 2); + ASSERT_FALSE(pdu.empty()); + EXPECT_EQ(pdu[pdu.size() - 1], 0x03); // bits 8-9 kept, pad bits 10-15 zeroed +} + +TEST(ModbusCreateClientPdu, SingleCoilValueValidated) { + const uint8_t on[] = {0xFF, 0x00}; + const uint8_t junk[] = {0x01, 0x00}; + EXPECT_FALSE(create_client_pdu(FC::WRITE_SINGLE_COIL, 0x0003, 1, on, 2).empty()); + EXPECT_TRUE(create_client_pdu(FC::WRITE_SINGLE_COIL, 0x0003, 1, junk, 2).empty()); +} + +// --- create_write_coils_pdu (packed) --------------------------------------- + +TEST(ModbusWriteCoilsPacked, MatchesBoolBuilder) { + const bool coils[] = {true, false, true, true, false, false, true, false, true, true}; + uint8_t packed[] = {0b01001101, 0b00000011}; + auto from_bools = create_write_coils_pdu(0x13, coils); + auto from_packed = create_write_coils_pdu(0x13, PackedBits(packed, 10)); + ASSERT_EQ(from_packed.size(), from_bools.size()); + EXPECT_EQ(0, memcmp(from_packed.data(), from_bools.data(), from_bools.size())); +} + +TEST(ModbusWriteCoilsPacked, MasksUnusedTrailingBits) { + uint8_t packed[] = {0xFF}; + auto pdu = create_write_coils_pdu(0, PackedBits(packed, 3)); + ASSERT_EQ(pdu.size(), 7u); + EXPECT_EQ(pdu[6], 0x07); +} + +TEST(ModbusWriteCoilsPacked, RejectsShortBufferAndZeroCount) { + uint8_t packed[] = {0xFF}; + EXPECT_TRUE(create_write_coils_pdu(0, PackedBits(packed, 9)).empty()); // needs 2 bytes + EXPECT_TRUE(create_write_coils_pdu(0, PackedBits(packed, 0)).empty()); +} + +TEST(ModbusHelpersTest, PackedBitsReadsLsbFirst) { + const uint8_t packed[] = {0x0D, 0x03}; // bits 0,2,3 and 8,9 + PackedBits bits(packed, 11); + EXPECT_EQ(bits.size(), 11u); + EXPECT_TRUE(bits[0]); + EXPECT_FALSE(bits[1]); + EXPECT_TRUE(bits[2]); + EXPECT_TRUE(bits[3]); + EXPECT_FALSE(bits[7]); + EXPECT_TRUE(bits[8]); + EXPECT_TRUE(bits[9]); + EXPECT_FALSE(bits[10]); + EXPECT_EQ(bits.bytes().size(), 2u); +} + +TEST(ModbusHelpersTest, MutablePackedBitsSetsAndClears) { + uint8_t packed[2] = {0x00, 0xFF}; + MutablePackedBits bits(packed, 16); + bits.set(0, true); + bits.set(3, true); + bits.set(9, false); + EXPECT_EQ(packed[0], 0x09); // bits 0 and 3 + EXPECT_EQ(packed[1], 0xFD); // bit 9 (bit 1 of byte 1) cleared +} + +TEST(ModbusHelpersTest, MutablePackedBitsRoundTripAndConversion) { + const bool original[] = {true, true, false, true, false, false, false, false, true, false, true}; + constexpr uint16_t count = sizeof(original); + uint8_t packed[(count + 7) / 8] = {}; + MutablePackedBits out(packed, count); + for (uint16_t i = 0; i != count; i++) + out.set(i, original[i]); + PackedBits view = out; // implicit conversion to the read-only view + ASSERT_EQ(view.size(), count); + for (uint16_t i = 0; i != count; i++) + EXPECT_EQ(view[i], original[i]) << "bit " << i; +} + +TEST(ModbusHelpersTest, PackedBitsViewContractsEnforced) { + uint8_t buf[8] = {}; + PackedBits view(buf, 10); // 10 bits -> 2 bytes, over an 8-byte buffer + EXPECT_EQ(view.bytes().size(), 2u); + + MutablePackedBits bits(std::span(buf, 2), 10); + bits.set(9, true); // in range: lands in byte 1 + bits.set(10, true); // out of range: dropped + bits.set(300, true); // far out of range: dropped, no write past the span + + MutablePackedBits short_bits(std::span(buf, 1), 10); // contract-violating: 10 bits over 1 byte + short_bits.set(9, false); // within count_ but past the span: dropped (would clear bit 9 set above) + EXPECT_EQ(buf[1], 0x02); + for (size_t i = 2; i < sizeof(buf); i++) + EXPECT_EQ(buf[i], 0) << "byte " << i; +} + +// server_pdu_payload() must never classify an exception PDU as a read: [fc|0x80, code] is 2 bytes, and a +// read-offset of 2 would return an empty span, losing the exception code. The payload of an exception PDU +// is the exception code byte, for reads and writes alike. +TEST(ModbusServerPduPayload, ExceptionOfReadYieldsExceptionCode) { + const uint8_t pdu[] = {0x83, 0x02}; // exception response to READ_HOLDING_REGISTERS + auto payload = server_pdu_payload(pdu); + ASSERT_EQ(payload.size(), 1u); + EXPECT_EQ(payload[0], 0x02); +} + +TEST(ModbusServerPduPayload, ExceptionOfWriteYieldsExceptionCode) { + const uint8_t pdu[] = {0x86, 0x03}; // exception response to WRITE_SINGLE_REGISTER + auto payload = server_pdu_payload(pdu); + ASSERT_EQ(payload.size(), 1u); + EXPECT_EQ(payload[0], 0x03); +} + } // namespace esphome::modbus::helpers diff --git a/tests/components/modbus_controller/common.yaml b/tests/components/modbus_controller/common.yaml index 51951a4528..aa2855c2b0 100644 --- a/tests/components/modbus_controller/common.yaml +++ b/tests/components/modbus_controller/common.yaml @@ -18,7 +18,7 @@ binary_sensor: modbus_controller_id: modbus_controller1 id: modbus_binary_sensor2 name: Test Binary Sensor with Lambda - register_type: read + register_type: input address: 0x3201 lambda: |- return x; diff --git a/tests/components/modbus_server/modbus_server_test.cpp b/tests/components/modbus_server/modbus_server_test.cpp index d95bb473c9..8c2e1d16d9 100644 --- a/tests/components/modbus_server/modbus_server_test.cpp +++ b/tests/components/modbus_server/modbus_server_test.cpp @@ -4,7 +4,7 @@ namespace esphome::modbus_server { -using modbus::ModbusExceptionCode; +using modbus::ExceptionCode; using modbus::RegisterValues; namespace { @@ -73,7 +73,7 @@ TEST(ModbusServerWrite, UnderSuppliedValueAppliesNothing) { auto status = server.on_write_registers(0x0000, make_registers({0x1111, 0x2222})); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_VALUE); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_VALUE); EXPECT_FALSE(word_written); // the writable WORD must NOT have been applied EXPECT_FALSE(dword_written); } @@ -87,7 +87,7 @@ TEST(ModbusServerWrite, UnwritableRegisterRejected) { auto status = server.on_write_registers(0x0000, make_registers({0x1234})); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); } // An address with no registered register yields ILLEGAL_DATA_ADDRESS. @@ -96,7 +96,7 @@ TEST(ModbusServerWrite, UnmatchedAddressRejected) { auto status = server.on_write_registers(0x0005, make_registers({0x1234})); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); } // A write_lambda failing at runtime is the one non-atomic case: the earlier register is already @@ -117,7 +117,7 @@ TEST(ModbusServerWrite, CallbackFailureIsServiceDeviceFailure) { auto status = server.on_write_registers(0x0000, make_registers({0xAAAA, 0xBBBB})); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::SERVICE_DEVICE_FAILURE); + EXPECT_EQ(status.value(), ExceptionCode::SERVICE_DEVICE_FAILURE); EXPECT_TRUE(first_written); // pre-validation passed, so the first write applied before the failure } @@ -168,7 +168,7 @@ TEST(ModbusServerRead, StartInsideValueRejected) { auto status = server.on_read_registers(0x0011, 1, out); // the second cell of the DWORD ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); EXPECT_FALSE(read_called); } @@ -187,7 +187,7 @@ TEST(ModbusServerRead, ClippedTailRejected) { auto status = server.on_read_registers(0x0000, 1, out); // only 1 of the DWORD's 2 registers ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); EXPECT_FALSE(read_called); } @@ -203,7 +203,7 @@ TEST(ModbusServerRead, WriteOnlyRegisterRejected) { auto status = server.on_read_registers(0x0000, 1, out); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); } // An unregistered address with courtesy enabled returns the default value for each cell. @@ -227,7 +227,7 @@ TEST(ModbusServerRead, UnregisteredRejectedWithoutCourtesy) { auto status = server.on_read_registers(0x0005, 1, out); ASSERT_TRUE(status.has_value()); if (status.has_value()) - EXPECT_EQ(status.value(), ModbusExceptionCode::ILLEGAL_DATA_ADDRESS); + EXPECT_EQ(status.value(), ExceptionCode::ILLEGAL_DATA_ADDRESS); } // --- partial reads (opt-in) ---------------------------------------------------- diff --git a/tests/components/network/__init__.py b/tests/components/network/__init__.py new file mode 100644 index 0000000000..101f63ac82 --- /dev/null +++ b/tests/components/network/__init__.py @@ -0,0 +1,13 @@ +import esphome.codegen as cg +from tests.testing_helpers import ComponentManifestOverride + + +def override_manifest(manifest: ComponentManifestOverride) -> None: + manifest.enable_codegen() + real_to_code = manifest.to_code + + async def to_code_testing(config): + await real_to_code(config) + cg.add_define("USE_NETWORK_IPV6", True) + + manifest.to_code = to_code_testing diff --git a/tests/components/network/test-priority.esp32-idf.yaml b/tests/components/network/test-priority.esp32-idf.yaml new file mode 100644 index 0000000000..baa821a234 --- /dev/null +++ b/tests/components/network/test-priority.esp32-idf.yaml @@ -0,0 +1,23 @@ +# Compiled dual-stack test: wifi + ethernet coexisting via network: priority:. +# This is the first build path that keeps both radios' stacks compiled in, so +# it must actually compile (not just validate) to guard the reconciler wiring. +# WiFi is listed first so the build also exercises the wifi-primary branch in +# network/util.cpp (the ethernet-primary branch matches the legacy order). +wifi: + ssid: MySSID + password: password1 + +ethernet: + type: W5500 + clk_pin: GPIO19 + mosi_pin: GPIO21 + miso_pin: GPIO23 + cs_pin: GPIO18 + interrupt_pin: GPIO36 + reset_pin: GPIO22 + clock_speed: 10Mhz + +network: + priority: + - wifi + - ethernet diff --git a/tests/components/network/test_ip_address_host.cpp b/tests/components/network/test_ip_address_host.cpp new file mode 100644 index 0000000000..4070f422b1 --- /dev/null +++ b/tests/components/network/test_ip_address_host.cpp @@ -0,0 +1,208 @@ +#include + +#include "esphome/components/network/ip_address.h" + +#ifdef USE_HOST +#if USE_NETWORK_IPV6 + +namespace esphome::network::testing { + +// ========================================================================= +// IPv4 +// ========================================================================= + +TEST(IPAddressHost, IPv4DefaultNotSet) { + IPAddress addr; + EXPECT_FALSE(addr.is_set()); +} + +TEST(IPAddressHost, IPv4DefaultIsIPv4) { + IPAddress addr; + EXPECT_TRUE(addr.is_ip4()); + EXPECT_FALSE(addr.is_ip6()); +} + +TEST(IPAddressHost, IPv4ParseAndSerialize) { + IPAddress addr("192.168.1.1"); + char buf[IP_ADDRESS_BUFFER_SIZE]; + EXPECT_STREQ(addr.str_to(buf), "192.168.1.1"); +} + +TEST(IPAddressHost, IPv4FromOctets) { + IPAddress addr(192, 168, 1, 1); + char buf[IP_ADDRESS_BUFFER_SIZE]; + EXPECT_STREQ(addr.str_to(buf), "192.168.1.1"); +} + +TEST(IPAddressHost, IPv4IsSet) { + IPAddress addr("192.168.1.1"); + EXPECT_TRUE(addr.is_set()); +} + +TEST(IPAddressHost, IPv4IsIp4) { + IPAddress addr("192.168.1.1"); + EXPECT_TRUE(addr.is_ip4()); + EXPECT_FALSE(addr.is_ip6()); +} + +TEST(IPAddressHost, IPv4MulticastDetected) { + IPAddress addr("239.0.60.53"); + EXPECT_TRUE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv4MulticastBoundaryLow) { + IPAddress addr("224.0.0.0"); + EXPECT_TRUE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv4MulticastBoundaryHigh) { + IPAddress addr("239.255.255.255"); + EXPECT_TRUE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv4UnicastNotMulticast) { + IPAddress addr("192.168.1.1"); + EXPECT_FALSE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv4EqualityMatch) { + IPAddress a("192.168.1.1"); + IPAddress b("192.168.1.1"); + EXPECT_EQ(a, b); +} + +TEST(IPAddressHost, IPv4EqualityMismatch) { + IPAddress a("192.168.1.1"); + IPAddress b("192.168.1.2"); + EXPECT_NE(a, b); +} + +TEST(IPAddressHost, IPv4FromOctetsMatchesParse) { + IPAddress from_octets(192, 168, 1, 1); + IPAddress from_string("192.168.1.1"); + EXPECT_EQ(from_octets, from_string); +} + +TEST(IPAddressHost, IPv4FromIPAddrT) { + ip_addr_t raw; + memset(&raw, 0, sizeof(raw)); + raw.u_addr.ip4.s_addr = htonl((192u << 24) | (168u << 16) | (1u << 8) | 1u); + raw.type = IPADDR_TYPE_V4; + IPAddress addr(&raw); + char buf[IP_ADDRESS_BUFFER_SIZE]; + EXPECT_STREQ(addr.str_to(buf), "192.168.1.1"); + EXPECT_TRUE(addr.is_ip4()); + EXPECT_FALSE(addr.is_ip6()); +} + +// ========================================================================= +// IPv6 +// ========================================================================= + +TEST(IPAddressHost, IPv6ParseAndSerialize) { + IPAddress addr("ff12::cafe"); + char buf[IP_ADDRESS_BUFFER_SIZE]; + EXPECT_STREQ(addr.str_to(buf), "ff12::cafe"); +} + +TEST(IPAddressHost, IPv6Loopback) { + IPAddress addr("::1"); + char buf[IP_ADDRESS_BUFFER_SIZE]; + EXPECT_STREQ(addr.str_to(buf), "::1"); +} + +TEST(IPAddressHost, IPv6IsIp6) { + IPAddress addr("ff12::cafe"); + EXPECT_TRUE(addr.is_ip6()); + EXPECT_FALSE(addr.is_ip4()); +} + +TEST(IPAddressHost, IPv6AllZerosNotSet) { + IPAddress addr("::"); + EXPECT_FALSE(addr.is_set()); +} + +TEST(IPAddressHost, IPv6LoopbackIsSet) { + IPAddress addr("::1"); + EXPECT_TRUE(addr.is_set()); +} + +TEST(IPAddressHost, IPv6MulticastDetected) { + IPAddress addr("ff12::cafe"); + EXPECT_TRUE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv6MulticastLinkLocal) { + IPAddress addr("ff02::1"); + EXPECT_TRUE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv6UnicastNotMulticast) { + IPAddress addr("::1"); + EXPECT_FALSE(addr.is_multicast()); +} + +TEST(IPAddressHost, IPv6EqualityMatch) { + IPAddress a("ff12::cafe"); + IPAddress b("ff12::cafe"); + EXPECT_EQ(a, b); +} + +TEST(IPAddressHost, IPv6EqualityMismatch) { + IPAddress a("ff12::cafe"); + IPAddress b("ff02::1"); + EXPECT_NE(a, b); +} + +TEST(IPAddressHost, IPv6OutputIsLowercase) { + // inet_pton is case-insensitive; str_to must lowercase the output + IPAddress addr("FF12::CAFE"); + char buf[IP_ADDRESS_BUFFER_SIZE]; + addr.str_to(buf); + for (const char *p = buf; *p; ++p) { + EXPECT_FALSE(*p >= 'A' && *p <= 'F') << "uppercase letter in: " << buf; + } +} + +TEST(IPAddressHost, IPv6FullAddressRoundTrip) { + // A full 128-bit address with no compression opportunity + const char *input = "fde0:983a:d0d3:a65e:725a:0fff:fe36:9916"; + IPAddress addr(input); + char buf[IP_ADDRESS_BUFFER_SIZE]; + addr.str_to(buf); + EXPECT_NE(buf[0], '\0'); + EXPECT_NE(std::string(buf).find("fde0"), std::string::npos); +} + +// ========================================================================= +// Malformed input +// ========================================================================= + +TEST(IPAddressHost, MalformedIPv4YieldsEmptyAddress) { + IPAddress addr("not-an-ip"); + EXPECT_FALSE(addr.is_set()); + EXPECT_TRUE(addr.is_ip4()); +} + +TEST(IPAddressHost, MalformedIPv6YieldsEmptyAddress) { + // "gg::1" looks like IPv6 (contains ':') but fails inet_pton; addr stays + // zeroed (type=V4 from memset) so is_set() is false and is_ip4() is true. + IPAddress addr("gg::1"); + EXPECT_FALSE(addr.is_set()); + EXPECT_TRUE(addr.is_ip4()); +} + +// ========================================================================= +// Cross-family +// ========================================================================= + +TEST(IPAddressHost, IPv4AndIPv6NotEqual) { + IPAddress v4("192.168.1.1"); + IPAddress v6("::1"); + EXPECT_NE(v4, v6); +} + +} // namespace esphome::network::testing + +#endif // USE_NETWORK_IPV6 +#endif // USE_HOST diff --git a/tests/components/ota/test.nrf52-adafruit.yaml b/tests/components/ota/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..e8ac96f051 --- /dev/null +++ b/tests/components/ota/test.nrf52-adafruit.yaml @@ -0,0 +1,4 @@ +zephyr_ble_server: + +ota: + - platform: zephyr_mcumgr diff --git a/tests/components/packet_transport/common.yaml b/tests/components/packet_transport/common.yaml index 9151cf27dc..5c6c8dd636 100644 --- a/tests/components/packet_transport/common.yaml +++ b/tests/components/packet_transport/common.yaml @@ -7,36 +7,40 @@ udp: addresses: ["239.0.60.53"] packet_transport: - platform: udp - update_interval: 5s - encryption: "our key goes here" - rolling_code_enable: true - ping_pong_enable: true - binary_sensors: - - binary_sensor_id1 - - id: binary_sensor_id1 - broadcast_id: other_id - sensors: - - sensor_id1 - - id: sensor_id1 - broadcast_id: other_id - providers: - - name: some-device-name - encryption: "their key goes here" + - platform: udp + id: transport_udp + update_interval: 5s + encryption: "our key goes here" + rolling_code_enable: true + ping_pong_enable: true + binary_sensors: + - binary_sensor_id1 + - id: binary_sensor_id1 + broadcast_id: other_id + sensors: + - sensor_id1 + - id: sensor_id1 + broadcast_id: other_id + providers: + - name: some-device-name + encryption: "their key goes here" sensor: - platform: template id: sensor_id1 - platform: packet_transport + transport_id: transport_udp provider: some-device-name id: our_id remote_id: some_sensor_id binary_sensor: - platform: packet_transport + transport_id: transport_udp provider: unencrypted-device id: other_binary_sensor_id - platform: packet_transport + transport_id: transport_udp provider: some-device-name type: status name: Some-Device Status diff --git a/tests/components/packet_transport/test.host.yaml b/tests/components/packet_transport/test.host.yaml index 49fdbbc9b2..f67b561226 100644 --- a/tests/components/packet_transport/test.host.yaml +++ b/tests/components/packet_transport/test.host.yaml @@ -3,36 +3,40 @@ udp: addresses: ["239.0.60.53"] packet_transport: - platform: udp - update_interval: 5s - encryption: "our key goes here" - rolling_code_enable: true - ping_pong_enable: true - binary_sensors: - - binary_sensor_id1 - - id: binary_sensor_id1 - broadcast_id: other_id - sensors: - - sensor_id1 - - id: sensor_id1 - broadcast_id: other_id - providers: - - name: some-device-name - encryption: "their key goes here" + - platform: udp + id: transport_udp + update_interval: 5s + encryption: "our key goes here" + rolling_code_enable: true + ping_pong_enable: true + binary_sensors: + - binary_sensor_id1 + - id: binary_sensor_id1 + broadcast_id: other_id + sensors: + - sensor_id1 + - id: sensor_id1 + broadcast_id: other_id + providers: + - name: some-device-name + encryption: "their key goes here" sensor: - platform: template id: sensor_id1 - platform: packet_transport + transport_id: transport_udp provider: some-device-name id: our_id remote_id: some_sensor_id binary_sensor: - platform: packet_transport + transport_id: transport_udp provider: unencrypted-device id: other_binary_sensor_id - platform: packet_transport + transport_id: transport_udp provider: some-device-name type: status name: Some-Device Status diff --git a/tests/components/remote_transmitter/common-buttons.yaml b/tests/components/remote_transmitter/common-buttons.yaml index 5631c48f95..981946a9a4 100644 --- a/tests/components/remote_transmitter/common-buttons.yaml +++ b/tests/components/remote_transmitter/common-buttons.yaml @@ -198,7 +198,7 @@ button: 0xFF, ] - platform: template - name: Haier + name: Haier Long on_press: remote_transmitter.transmit_haier: code: @@ -217,6 +217,21 @@ button: 0x00, 0x05, ] + - platform: template + name: Haier Short + on_press: + remote_transmitter.transmit_haier: + code: + [ + 0xA6, + 0xDA, + 0x00, + 0x00, + 0x40, + 0x40, + 0x00, + 0x80, + ] - platform: template name: Mirage on_press: diff --git a/tests/components/safe_mode/test-no-shutdown-confirm.esp32-idf.yaml b/tests/components/safe_mode/test-no-shutdown-confirm.esp32-idf.yaml new file mode 100644 index 0000000000..5ee1f308a3 --- /dev/null +++ b/tests/components/safe_mode/test-no-shutdown-confirm.esp32-idf.yaml @@ -0,0 +1,11 @@ +# Compile with OTA rollback support active (ota + safe_mode on ESP-IDF, the +# default) but boot_is_good_on_shutdown disabled, so an orderly shutdown does +# not confirm the app image; only boot_is_good_after / mark_successful do. +packages: + safe_mode: !include common-enabled.yaml + +safe_mode: + boot_is_good_on_shutdown: false + +ota: + - platform: esphome diff --git a/tests/components/sen5x/common.yaml b/tests/components/sen5x/common.yaml index a4462a16ea..5cfff15243 100644 --- a/tests/components/sen5x/common.yaml +++ b/tests/components/sen5x/common.yaml @@ -24,10 +24,10 @@ sensor: name: PM <10µm Weight concentration id: pm_10_0 accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC + nox_index: + name: NOx Index + voc_index: + name: VOC Index algorithm_tuning: index_offset: 100 learning_time_offset_hours: 12 @@ -42,4 +42,5 @@ sensor: auto_cleaning_interval: 604800s acceleration_mode: low store_baseline: true + model: sen55 address: 0x69 diff --git a/tests/components/sen6x/common.yaml b/tests/components/sen6x/common.yaml index 61ff9f1e0c..859e012c4a 100644 --- a/tests/components/sen6x/common.yaml +++ b/tests/components/sen6x/common.yaml @@ -26,10 +26,10 @@ sensor: name: PM <10µm Weight concentration id: sen6x_pm_10_0 accuracy_decimals: 1 - nox: - name: NOx - voc: - name: VOC + nox_index: + name: NOx Index + voc_index: + name: VOC Index co2: name: Carbon Dioxide formaldehyde: diff --git a/tests/components/sgp4x/common.yaml b/tests/components/sgp4x/common.yaml index 4edda8fd1b..88b8166876 100644 --- a/tests/components/sgp4x/common.yaml +++ b/tests/components/sgp4x/common.yaml @@ -1,7 +1,7 @@ sensor: - platform: sgp4x i2c_id: i2c_bus - voc: + voc_index: name: VOC Index id: sgp40_voc_index algorithm_tuning: @@ -11,8 +11,8 @@ sensor: gating_max_duration_minutes: 180 std_initial: 50 gain_factor: 230 - nox: - name: NOx + nox_index: + name: NOx Index algorithm_tuning: index_offset: 100 learning_time_offset_hours: 12 diff --git a/tests/components/syslog/test.host.yaml b/tests/components/syslog/test.host.yaml index 31122437d5..d9aa8e529b 100644 --- a/tests/components/syslog/test.host.yaml +++ b/tests/components/syslog/test.host.yaml @@ -2,7 +2,7 @@ udp: addresses: ["239.0.60.53"] time: - platform: host + - platform: host syslog: port: 514 diff --git a/tests/components/test_display/common.yaml b/tests/components/test_display/common.yaml new file mode 100644 index 0000000000..c36cf4b997 --- /dev/null +++ b/tests/components/test_display/common.yaml @@ -0,0 +1,13 @@ +# The test_display platform (and its external_components entry) is provided by +# the shared package included from the test.*.yaml files. These extra instances +# exercise the remaining `dimensions` code paths: the width/height map form and +# the default when omitted. The package's own `test_display_screen` covers the +# "WIDTHxHEIGHT" string form. +display: + - platform: test_display + id: test_display_wh_dimensions + dimensions: + width: 320 + height: 240 + - platform: test_display + id: test_display_default_dimensions diff --git a/tests/components/test_display/components/test_display/__init__.py b/tests/components/test_display/components/test_display/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/components/test_display/components/test_display/display.py b/tests/components/test_display/components/test_display/display.py new file mode 100644 index 0000000000..8503053b46 --- /dev/null +++ b/tests/components/test_display/components/test_display/display.py @@ -0,0 +1,36 @@ +import esphome.codegen as cg +from esphome.components import display +import esphome.config_validation as cv +from esphome.const import CONF_DIMENSIONS, CONF_HEIGHT, CONF_ID, CONF_WIDTH +from esphome.core import CoroPriority, coroutine_with_priority + +test_display_ns = cg.esphome_ns.namespace("test_display") +TestDisplay = test_display_ns.class_("TestDisplay", display.Display) + +CONFIG_SCHEMA = display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TestDisplay), + cv.Optional(CONF_DIMENSIONS, default="100x100"): cv.Any( + cv.dimensions, + cv.Schema( + { + cv.Required(CONF_WIDTH): cv.int_, + cv.Required(CONF_HEIGHT): cv.int_, + } + ), + ), + } +) + + +@coroutine_with_priority(CoroPriority.CORE) +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await display.register_display(var, config) + + dimensions = config[CONF_DIMENSIONS] + if isinstance(dimensions, dict): + width, height = dimensions[CONF_WIDTH], dimensions[CONF_HEIGHT] + else: + width, height = dimensions + cg.add(var.set_dimensions(width, height)) diff --git a/tests/components/test_display/components/test_display/test_display.h b/tests/components/test_display/components/test_display/test_display.h new file mode 100644 index 0000000000..3f2b03a773 --- /dev/null +++ b/tests/components/test_display/components/test_display/test_display.h @@ -0,0 +1,36 @@ +#pragma once + +#include "esphome/components/display/display.h" +#include "esphome/core/color.h" + +namespace esphome::test_display { + +/** A no-op display that draws nothing and uses no pins. + * + * It exists purely to satisfy components that require a display (for example + * touchscreens, which read the display dimensions) in configurations - most + * notably YAML build tests - where a real display driver would only get in the + * way by occupying GPIO pins and pulling in bus dependencies. + */ +class TestDisplay : public display::Display { + public: + void update() override { this->do_update_(); } + + void set_dimensions(int width, int height) { + this->width_ = width; + this->height_ = height; + } + + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + + void draw_pixel_at(int x, int y, Color color) override {} + + protected: + int get_width_internal() override { return this->width_; } + int get_height_internal() override { return this->height_; } + + int width_{0}; + int height_{0}; +}; + +} // namespace esphome::test_display diff --git a/tests/components/test_display/test.esp32-idf.yaml b/tests/components/test_display/test.esp32-idf.yaml new file mode 100644 index 0000000000..dfc8006c38 --- /dev/null +++ b/tests/components/test_display/test.esp32-idf.yaml @@ -0,0 +1,3 @@ +packages: + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + test_display_extra: !include common.yaml diff --git a/tests/components/test_display/test.esp8266-ard.yaml b/tests/components/test_display/test.esp8266-ard.yaml new file mode 100644 index 0000000000..dfc8006c38 --- /dev/null +++ b/tests/components/test_display/test.esp8266-ard.yaml @@ -0,0 +1,3 @@ +packages: + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + test_display_extra: !include common.yaml diff --git a/tests/components/test_display/test.rp2040-ard.yaml b/tests/components/test_display/test.rp2040-ard.yaml new file mode 100644 index 0000000000..dfc8006c38 --- /dev/null +++ b/tests/components/test_display/test.rp2040-ard.yaml @@ -0,0 +1,3 @@ +packages: + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + test_display_extra: !include common.yaml diff --git a/tests/components/tt21100/common.yaml b/tests/components/tt21100/common.yaml index 1f9249f1ba..5cb6b99a8e 100644 --- a/tests/components/tt21100/common.yaml +++ b/tests/components/tt21100/common.yaml @@ -1,19 +1,8 @@ -display: - - platform: ssd1306_i2c - i2c_id: i2c_bus - id: tt21100_ssd1306_i2c_display - model: SSD1306_128X64 - reset_pin: ${disp_reset_pin} - pages: - - id: tt21100_page1 - lambda: |- - it.rectangle(0, 0, it.get_width(), it.get_height()); - touchscreen: - platform: tt21100 i2c_id: i2c_bus id: tt21100_touchscreen - display: tt21100_ssd1306_i2c_display + display: test_display_screen interrupt_pin: ${interrupt_pin} reset_pin: ${reset_pin} diff --git a/tests/components/tt21100/test.esp32-idf.yaml b/tests/components/tt21100/test.esp32-idf.yaml index 033aafb73c..a79695d611 100644 --- a/tests/components/tt21100/test.esp32-idf.yaml +++ b/tests/components/tt21100/test.esp32-idf.yaml @@ -1,9 +1,8 @@ substitutions: - disp_reset_pin: GPIO12 interrupt_pin: GPIO15 reset_pin: GPIO4 packages: i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + tt21100: !include common.yaml diff --git a/tests/components/tt21100/test.esp8266-ard.yaml b/tests/components/tt21100/test.esp8266-ard.yaml index 25d1ff82e3..ae6977c6ec 100644 --- a/tests/components/tt21100/test.esp8266-ard.yaml +++ b/tests/components/tt21100/test.esp8266-ard.yaml @@ -1,9 +1,8 @@ substitutions: - disp_reset_pin: GPIO0 interrupt_pin: GPIO15 reset_pin: GPIO16 packages: i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + tt21100: !include common.yaml diff --git a/tests/components/tt21100/test.rp2040-ard.yaml b/tests/components/tt21100/test.rp2040-ard.yaml index 0d13628294..98b2ad600c 100644 --- a/tests/components/tt21100/test.rp2040-ard.yaml +++ b/tests/components/tt21100/test.rp2040-ard.yaml @@ -1,9 +1,8 @@ substitutions: - disp_reset_pin: GPIO10 interrupt_pin: GPIO2 reset_pin: GPIO3 packages: i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml - -<<: !include common.yaml + test_display: !include ../../test_build_components/common/test_display/test_display.yaml + tt21100: !include common.yaml diff --git a/tests/components/ultrasonic/test.nrf52-adafruit.yaml b/tests/components/ultrasonic/test.nrf52-adafruit.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/ultrasonic/test.nrf52-adafruit.yaml @@ -0,0 +1 @@ +<<: !include common.yaml diff --git a/tests/components/web_server/common.yaml b/tests/components/web_server/common.yaml index 5a05a58c2d..d7b880efe6 100644 --- a/tests/components/web_server/common.yaml +++ b/tests/components/web_server/common.yaml @@ -4,6 +4,17 @@ wifi: binary_sensor: cover: + - platform: template + name: "Template Cover Assumed" + # assumed_state must be reflected in the web_server JSON (detail=all) + assumed_state: true + lambda: 'return COVER_OPEN;' + open_action: + - logger.log: open_action + close_action: + - logger.log: close_action + stop_action: + - logger.log: stop_action fan: light: sensor: diff --git a/tests/components/web_server/test.esp32-ard.yaml b/tests/components/web_server/test.esp32-ard.yaml index 11ad5456ef..2e091b905a 100644 --- a/tests/components/web_server/test.esp32-ard.yaml +++ b/tests/components/web_server/test.esp32-ard.yaml @@ -1,2 +1,8 @@ packages: web_server: !include common_v2.yaml + +web_server: + auth: + username: admin + password: password + type: digest diff --git a/tests/integration/README.md b/tests/integration/README.md index 4de08777b0..44d9e0d644 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -187,6 +187,7 @@ loop = asyncio.get_running_loop() states: dict[int, EntityState] = {} state_future: asyncio.Future[EntityState] = loop.create_future() + def on_state(state: EntityState) -> None: """This callback only receives NEW state changes, not initial states.""" states[state.key] = state @@ -195,6 +196,7 @@ def on_state(state: EntityState) -> None: if not state_future.done(): state_future.set_result(state) + # Get entities and set up state synchronization entities, services = await client.list_entities_services() initial_state_helper = InitialStateHelper(entities) @@ -228,6 +230,7 @@ loop = asyncio.get_running_loop() states: dict[int, EntityState] = {} state_future: asyncio.Future[EntityState] = loop.create_future() + def on_state(state: EntityState) -> None: states[state.key] = state # Check for specific condition using isinstance @@ -235,6 +238,7 @@ def on_state(state: EntityState) -> None: if not state_future.done(): state_future.set_result(state) + client.subscribe_states(on_state) # Wait for state with timeout @@ -263,11 +267,13 @@ entity_count = 50 received_states: set[int] = set() all_states_future: asyncio.Future[bool] = loop.create_future() + def on_state(state: EntityState) -> None: received_states.add(state.key) if len(received_states) >= entity_count and not all_states_future.done(): all_states_future.set_result(True) + client.subscribe_states(on_state) await asyncio.wait_for(all_states_future, timeout=10.0) ``` @@ -367,6 +373,7 @@ service_future = loop.create_future() connected_pattern = re.compile(r"Client .* connected from") service_pattern = re.compile(r"Service called") + def check_output(line: str) -> None: """Check log output for expected messages.""" if not connected_future.done() and connected_pattern.search(line): @@ -374,6 +381,7 @@ def check_output(line: str) -> None: elif not service_future.done() and service_pattern.search(line): service_future.set_result(True) + async with run_compiled(yaml_config, line_callback=check_output): async with api_client_connected() as client: # Wait for specific log message diff --git a/tests/integration/fixtures/api_homeassistant_action_no_subscriber.yaml b/tests/integration/fixtures/api_homeassistant_action_no_subscriber.yaml new file mode 100644 index 0000000000..26791e2cd1 --- /dev/null +++ b/tests/integration/fixtures/api_homeassistant_action_no_subscriber.yaml @@ -0,0 +1,30 @@ +esphome: + name: test-ha-action-no-subscriber + friendly_name: Home Assistant Action No Subscriber Test + on_boot: + # Fires before any client is connected - dropped with a warning. + - homeassistant.action: + action: test.boot_action + +host: + +api: + on_client_connected: + # Fires at authentication time, before the client has subscribed to + # Home Assistant actions - dropped with a warning. + - homeassistant.action: + action: test.connected_action + +logger: + level: DEBUG + +button: + - platform: template + name: Send Action Button + id: send_action_button + on_press: + # Pressed only after the client has subscribed - must be delivered. + - homeassistant.action: + action: test.button_action + data: + value: subscribed diff --git a/tests/integration/fixtures/blocking_warning_log_time_not_charged_to_next_operation.yaml b/tests/integration/fixtures/blocking_warning_log_time_not_charged_to_next_operation.yaml new file mode 100644 index 0000000000..2f30a07d0d --- /dev/null +++ b/tests/integration/fixtures/blocking_warning_log_time_not_charged_to_next_operation.yaml @@ -0,0 +1,66 @@ +esphome: + name: blocking-warning-cascade + on_boot: + then: + - script.execute: blocking_60 + - script.execute: blocking_90 + - script.execute: blocking_120 + +host: + +api: + +logger: + level: DEBUG + on_message: + level: WARN + then: + - lambda: |- + uint32_t injected_delay = 0; + if (strstr(message, "blocking_60 took a long time") != nullptr) { + injected_delay = 60; + } else if (strstr(message, "blocking_90 took a long time") != nullptr) { + injected_delay = 90; + } else if (strstr(message, "blocking_120 took a long time") != nullptr) { + injected_delay = 120; + } + if (injected_delay != 0) { + id(injected_delay_total) += injected_delay; + const uint32_t start = millis(); + while (millis() - start < injected_delay) { + } + } + +globals: + - id: injected_delay_total + type: uint32_t + initial_value: "0" + +script: + - id: blocking_60 + then: + - delay: 20ms + - lambda: |- + const uint32_t start = millis(); + while (millis() - start < 80) { + } + + - id: blocking_90 + then: + - delay: 300ms + - lambda: |- + const uint32_t start = millis(); + while (millis() - start < 80) { + } + + - id: blocking_120 + then: + - delay: 600ms + - lambda: |- + const uint32_t start = millis(); + while (millis() - start < 80) { + } + - delay: 200ms + - logger.log: + format: "BLOCKING_WARNING_CASCADE_TEST_COMPLETE total=%u" + args: [id(injected_delay_total)] diff --git a/tests/integration/fixtures/light_binary_effect_off_phase.yaml b/tests/integration/fixtures/light_binary_effect_off_phase.yaml new file mode 100644 index 0000000000..c5c74001c3 --- /dev/null +++ b/tests/integration/fixtures/light_binary_effect_off_phase.yaml @@ -0,0 +1,29 @@ +esphome: + name: light-binary-effect-off +host: +api: # Port will be automatically injected +logger: + level: DEBUG + +output: + - platform: template + id: binary_output + type: binary + write_action: + - logger.log: + format: "BINARY_OUTPUT:%s" + args: [YESNO(state)] + +light: + - platform: binary + name: "Test Binary Light" + id: test_binary_light + output: binary_output + effects: + - strobe: + name: "Fast Strobe" + colors: + - state: true + duration: 50ms + - state: false + duration: 50ms diff --git a/tests/integration/fixtures/light_binary_zero_brightness_is_recoverable.yaml b/tests/integration/fixtures/light_binary_zero_brightness_is_recoverable.yaml new file mode 100644 index 0000000000..1db8b44fd1 --- /dev/null +++ b/tests/integration/fixtures/light_binary_zero_brightness_is_recoverable.yaml @@ -0,0 +1,21 @@ +esphome: + name: light-binary-zero-bright +host: +api: # Port will be automatically injected +logger: + level: DEBUG + +output: + - platform: template + id: binary_output + type: binary + write_action: + - logger.log: + format: "BINARY_OUTPUT:%s" + args: [YESNO(state)] + +light: + - platform: binary + name: "Test Binary Light" + id: test_binary_light + output: binary_output diff --git a/tests/integration/fixtures/light_effect_zero_brightness.yaml b/tests/integration/fixtures/light_effect_zero_brightness.yaml new file mode 100644 index 0000000000..b98bed84db --- /dev/null +++ b/tests/integration/fixtures/light_effect_zero_brightness.yaml @@ -0,0 +1,35 @@ +esphome: + name: light-effect-zero-bright +host: +api: # Port will be automatically injected +logger: + level: DEBUG + +output: + - platform: template + id: pulse_output + type: float + write_action: + - logger.log: + format: "PULSE_OUTPUT:%.4f" + args: [state] + +light: + - platform: monochromatic + name: "Test Pulse Light" + id: test_pulse_light + output: pulse_output + effects: + - pulse: + name: "Fast Pulse" + transition_length: 20ms + update_interval: 50ms + min_brightness: 0% + max_brightness: 100% + - strobe: + name: "Fast Strobe" + colors: + - state: true + duration: 50ms + - state: false + duration: 50ms diff --git a/tests/integration/fixtures/light_initial_state.yaml b/tests/integration/fixtures/light_initial_state.yaml index 2654c76aa0..052de0a4e5 100644 --- a/tests/integration/fixtures/light_initial_state.yaml +++ b/tests/integration/fixtures/light_initial_state.yaml @@ -21,6 +21,11 @@ output: type: float write_action: - lambda: "" + - platform: template + id: test_restore_and_on_output + type: float + write_action: + - lambda: "" light: - platform: rgb @@ -37,3 +42,16 @@ light: red: 1.0 green: 0.5 blue: 0.0 + + - platform: monochromatic + name: "Test Restore And On Light" + id: test_restore_and_on_light + output: test_restore_and_on_output + restore_mode: RESTORE_AND_ON + # Simulates a stale/persisted zero brightness: RESTORE_AND_ON always forces the light + # on at boot regardless of the recovered state, so a leftover brightness of 0 must not + # leave the light on-but-invisible. + initial_state: + color_mode: BRIGHTNESS + state: false + brightness: 0% diff --git a/tests/integration/fixtures/scheduler_numeric_id_test.yaml b/tests/integration/fixtures/scheduler_numeric_id_test.yaml index 25decf20f5..ae95e095f6 100644 --- a/tests/integration/fixtures/scheduler_numeric_id_test.yaml +++ b/tests/integration/fixtures/scheduler_numeric_id_test.yaml @@ -18,9 +18,6 @@ globals: - id: interval_counter type: int initial_value: '0' - - id: retry_counter - type: int - initial_value: '0' - id: defer_counter type: int initial_value: '0' @@ -118,29 +115,7 @@ script: id(timeout_counter) += 1; }); - // Test 10: set_retry with numeric ID - App.scheduler.set_retry(component1, 6001U, 50, 3, - [](uint8_t retry_countdown) { - id(retry_counter)++; - ESP_LOGI("test", "Numeric retry 6001 attempt %d (countdown=%d)", - id(retry_counter), retry_countdown); - if (id(retry_counter) >= 2) { - ESP_LOGI("test", "Numeric retry 6001 done"); - return RetryResult::DONE; - } - return RetryResult::RETRY; - }); - - // Test 11: cancel_retry with numeric ID - App.scheduler.set_retry(component1, 6002U, 100, 5, - [](uint8_t retry_countdown) { - ESP_LOGE("test", "ERROR: Numeric retry 6002 should have been cancelled"); - return RetryResult::RETRY; - }); - App.scheduler.cancel_retry(component1, 6002U); - ESP_LOGI("test", "Cancelled numeric retry 6002"); - - // Test 12: defer with numeric ID (Component method) + // Test 10: defer with numeric ID (Component method) class TestDeferComponent : public Component { public: void test_defer_methods() { @@ -161,7 +136,7 @@ script: static TestDeferComponent test_defer_component; test_defer_component.test_defer_methods(); - // Test 13: cancel_defer with numeric ID (Component method) + // Test 11: cancel_defer with numeric ID (Component method) class TestCancelDeferComponent : public Component { public: void test_cancel_defer() { @@ -181,8 +156,8 @@ script: - id: report_results then: - lambda: |- - ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Retries: %d, Defers: %d", - id(timeout_counter), id(interval_counter), id(retry_counter), id(defer_counter)); + ESP_LOGI("test", "Final results - Timeouts: %d, Intervals: %d, Defers: %d", + id(timeout_counter), id(interval_counter), id(defer_counter)); sensor: - platform: template diff --git a/tests/integration/fixtures/scheduler_retry_test.yaml b/tests/integration/fixtures/scheduler_retry_test.yaml deleted file mode 100644 index cdf71152bd..0000000000 --- a/tests/integration/fixtures/scheduler_retry_test.yaml +++ /dev/null @@ -1,287 +0,0 @@ -esphome: - debug_scheduler: true # Enable scheduler leak detection - name: scheduler-retry-test - on_boot: - priority: -100 - then: - - logger.log: "Starting scheduler retry tests" - # Run all tests sequentially with delays - - script.execute: run_all_tests - -host: -api: -logger: - level: VERY_VERBOSE - -globals: - - id: simple_retry_counter - type: int - initial_value: '0' - - id: backoff_retry_counter - type: int - initial_value: '0' - - id: backoff_last_attempt_time - type: uint32_t - initial_value: '0' - - id: immediate_done_counter - type: int - initial_value: '0' - - id: cancel_retry_counter - type: int - initial_value: '0' - - id: empty_name_retry_counter - type: int - initial_value: '0' - - id: script_retry_counter - type: int - initial_value: '0' - - id: multiple_same_name_counter - type: int - initial_value: '0' - - id: const_char_retry_counter - type: int - initial_value: '0' - - id: static_char_retry_counter - type: int - initial_value: '0' - -# Using different component types for each test to ensure isolation -sensor: - - platform: template - name: Simple Retry Test Sensor - id: simple_retry_sensor - lambda: return 1.0; - update_interval: never - - - platform: template - name: Backoff Retry Test Sensor - id: backoff_retry_sensor - lambda: return 2.0; - update_interval: never - - - platform: template - name: Immediate Done Test Sensor - id: immediate_done_sensor - lambda: return 3.0; - update_interval: never - -binary_sensor: - - platform: template - name: Cancel Retry Test Binary Sensor - id: cancel_retry_binary_sensor - lambda: return false; - - - platform: template - name: Empty Name Test Binary Sensor - id: empty_name_binary_sensor - lambda: return true; - -switch: - - platform: template - name: Script Retry Test Switch - id: script_retry_switch - optimistic: true - - - platform: template - name: Multiple Same Name Test Switch - id: multiple_same_name_switch - optimistic: true - -script: - - id: run_all_tests - then: - # Test 1: Simple retry - - logger.log: "=== Test 1: Simple retry ===" - - lambda: |- - auto *component = id(simple_retry_sensor); - App.scheduler.set_retry(component, "simple_retry", 50, 3, - [](uint8_t retry_countdown) { - id(simple_retry_counter)++; - ESP_LOGI("test", "Simple retry attempt %d (countdown=%d)", - id(simple_retry_counter), retry_countdown); - - if (id(simple_retry_counter) >= 2) { - ESP_LOGI("test", "Simple retry succeeded on attempt %d", id(simple_retry_counter)); - return RetryResult::DONE; - } - return RetryResult::RETRY; - }); - - # Test 2: Backoff retry - - logger.log: "=== Test 2: Retry with backoff ===" - - lambda: |- - auto *component = id(backoff_retry_sensor); - - App.scheduler.set_retry(component, "backoff_retry", 50, 4, - [](uint8_t retry_countdown) { - id(backoff_retry_counter)++; - uint32_t now = millis(); - uint32_t interval = 0; - - // Only calculate interval after first attempt - if (id(backoff_retry_counter) > 1) { - interval = now - id(backoff_last_attempt_time); - } - id(backoff_last_attempt_time) = now; - - ESP_LOGI("test", "Backoff retry attempt %d (countdown=%d, interval=%dms)", - id(backoff_retry_counter), retry_countdown, interval); - - if (id(backoff_retry_counter) == 1) { - ESP_LOGI("test", "First call was immediate"); - } else if (id(backoff_retry_counter) == 2) { - ESP_LOGI("test", "Second call interval: %dms (expected ~50ms)", interval); - } else if (id(backoff_retry_counter) == 3) { - ESP_LOGI("test", "Third call interval: %dms (expected ~100ms)", interval); - } else if (id(backoff_retry_counter) == 4) { - ESP_LOGI("test", "Fourth call interval: %dms (expected ~200ms)", interval); - ESP_LOGI("test", "Backoff retry completed"); - return RetryResult::DONE; - } - - return RetryResult::RETRY; - }, 2.0f); - - # Test 3: Immediate done - - logger.log: "=== Test 3: Immediate done ===" - - lambda: |- - auto *component = id(immediate_done_sensor); - App.scheduler.set_retry(component, "immediate_done", 50, 5, - [](uint8_t retry_countdown) { - id(immediate_done_counter)++; - ESP_LOGI("test", "Immediate done retry called (countdown=%d)", retry_countdown); - return RetryResult::DONE; - }); - - # Test 4: Cancel retry - - logger.log: "=== Test 4: Cancel retry ===" - - lambda: |- - auto *component = id(cancel_retry_binary_sensor); - App.scheduler.set_retry(component, "cancel_test", 30, 10, - [](uint8_t retry_countdown) { - id(cancel_retry_counter)++; - ESP_LOGI("test", "Cancel test retry attempt %d", id(cancel_retry_counter)); - return RetryResult::RETRY; - }); - - // Cancel it after 100ms - App.scheduler.set_timeout(component, "cancel_timer", 100, []() { - bool cancelled = App.scheduler.cancel_retry(id(cancel_retry_binary_sensor), "cancel_test"); - ESP_LOGI("test", "Retry cancellation result: %s", cancelled ? "true" : "false"); - ESP_LOGI("test", "Cancel retry ran %d times before cancellation", id(cancel_retry_counter)); - }); - - # Test 5: Empty name retry - - logger.log: "=== Test 5: Empty name retry ===" - - lambda: |- - auto *component = id(empty_name_binary_sensor); - App.scheduler.set_retry(component, "", 100, 5, - [](uint8_t retry_countdown) { - id(empty_name_retry_counter)++; - ESP_LOGI("test", "Empty name retry attempt %d", id(empty_name_retry_counter)); - return RetryResult::RETRY; - }); - - // Try to cancel after 150ms - App.scheduler.set_timeout(component, "empty_cancel_timer", 150, []() { - bool cancelled = App.scheduler.cancel_retry(id(empty_name_binary_sensor), ""); - ESP_LOGI("test", "Empty name retry cancel result: %s", - cancelled ? "true" : "false"); - ESP_LOGI("test", "Empty name retry ran %d times", id(empty_name_retry_counter)); - }); - - # Test 6: Component method - - logger.log: "=== Test 6: Component::set_retry method ===" - - lambda: |- - class TestRetryComponent : public Component { - public: - void test_retry() { - this->set_retry(50, 3, - [](uint8_t retry_countdown) { - id(script_retry_counter)++; - ESP_LOGI("test", "Component retry attempt %d", id(script_retry_counter)); - if (id(script_retry_counter) >= 2) { - return RetryResult::DONE; - } - return RetryResult::RETRY; - }, 1.5f); - } - }; - - static TestRetryComponent test_component; - test_component.test_retry(); - - # Test 7: Multiple same name - - logger.log: "=== Test 7: Multiple retries with same name ===" - - lambda: |- - auto *component = id(multiple_same_name_switch); - - // Set first retry - App.scheduler.set_retry(component, "duplicate_retry", 100, 5, - [](uint8_t retry_countdown) { - id(multiple_same_name_counter) += 1; - ESP_LOGI("test", "First duplicate retry - should not run"); - return RetryResult::RETRY; - }); - - // Set second retry with same name (should cancel first) - App.scheduler.set_retry(component, "duplicate_retry", 50, 3, - [](uint8_t retry_countdown) { - id(multiple_same_name_counter) += 10; - ESP_LOGI("test", "Second duplicate retry attempt (counter=%d)", - id(multiple_same_name_counter)); - if (id(multiple_same_name_counter) >= 20) { - return RetryResult::DONE; - } - return RetryResult::RETRY; - }); - - # Test 8: Const char* overloads - - logger.log: "=== Test 8: Const char* overloads ===" - - lambda: |- - auto *component = id(simple_retry_sensor); - - // Test 8a: Direct string literal - App.scheduler.set_retry(component, "const_char_test", 30, 2, - [](uint8_t retry_countdown) { - id(const_char_retry_counter)++; - ESP_LOGI("test", "Const char retry %d", id(const_char_retry_counter)); - return RetryResult::DONE; - }); - - # Test 9: Static const char* variable - - logger.log: "=== Test 9: Static const char* ===" - - lambda: |- - auto *component = id(backoff_retry_sensor); - - static const char* STATIC_NAME = "static_retry_test"; - App.scheduler.set_retry(component, STATIC_NAME, 20, 1, - [](uint8_t retry_countdown) { - id(static_char_retry_counter)++; - ESP_LOGI("test", "Static const char retry %d", id(static_char_retry_counter)); - return RetryResult::DONE; - }); - - // Cancel with same static const char* - App.scheduler.set_timeout(component, "static_cancel", 10, []() { - static const char* STATIC_NAME = "static_retry_test"; - bool result = App.scheduler.cancel_retry(id(backoff_retry_sensor), STATIC_NAME); - ESP_LOGI("test", "Static cancel result: %s", result ? "true" : "false"); - }); - - # Wait for all tests to complete before reporting - - delay: 500ms - - # Final report - - logger.log: "=== Retry Test Results ===" - - lambda: |- - ESP_LOGI("test", "Simple retry counter: %d (expected 2)", id(simple_retry_counter)); - ESP_LOGI("test", "Backoff retry counter: %d (expected 4)", id(backoff_retry_counter)); - ESP_LOGI("test", "Immediate done counter: %d (expected 1)", id(immediate_done_counter)); - ESP_LOGI("test", "Cancel retry counter: %d (expected 2-4)", id(cancel_retry_counter)); - ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter)); - ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter)); - ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter)); - ESP_LOGI("test", "Const char retry counter: %d (expected 1)", id(const_char_retry_counter)); - ESP_LOGI("test", "Static char retry counter: %d (expected 1)", id(static_char_retry_counter)); - ESP_LOGI("test", "All retry tests completed"); diff --git a/tests/integration/fixtures/wait_until_reentrant_restart.yaml b/tests/integration/fixtures/wait_until_reentrant_restart.yaml new file mode 100644 index 0000000000..337d0de837 --- /dev/null +++ b/tests/integration/fixtures/wait_until_reentrant_restart.yaml @@ -0,0 +1,86 @@ +esphome: + name: wait-until-reentrant-restart + +host: + +api: + actions: + - action: start_self_restart + then: + - script.execute: retry_script + - action: start_stop_during_wait + then: + - globals.set: + id: gate_open + value: 'false' + # num 0 is a blocker: its condition never becomes true, so it is still + # waiting (already checked and set aside) when num 1 stops the script - + # it must be cancelled, not restored, so its timeout must never fire + - script.execute: + id: waiter + num: 0 + - script.execute: + id: waiter + num: 1 + - script.execute: + id: waiter + num: 2 + - script.execute: + id: waiter + num: 3 + # Give all three instances time to queue in the same wait_until + - delay: 100ms + - globals.set: + id: gate_open + value: 'true' + - delay: 200ms + - logger.log: "stop test complete" + +logger: + level: DEBUG + +globals: + - id: attempt + type: int + initial_value: '0' + - id: gate_open + type: bool + initial_value: 'false' + +script: + # Self-restart retry pattern: when the wait_until times out, the rest of the + # script runs synchronously from inside the wait queue processing and restarts + # this same script - re-entering the same WaitUntilAction while it is still + # processing its queue. This used to corrupt the queue and crash. + - id: retry_script + mode: restart + then: + - wait_until: + condition: + lambda: 'return false;' + timeout: 20ms + - lambda: |- + id(attempt) += 1; + ESP_LOGD("test", "attempt %d done", id(attempt)); + - if: + condition: + lambda: 'return id(attempt) < 5;' + then: + - script.execute: retry_script + else: + - logger.log: "retry test complete" + + # Parallel waiters all queued in the same wait_until; the first one to pass the + # gate stops the script from its continuation, cancelling the other waiters + # while the queue is still being processed. + - id: waiter + mode: parallel + parameters: + num: int + then: + - wait_until: + condition: + lambda: 'return num != 0 && id(gate_open);' + timeout: 1s + - lambda: 'ESP_LOGD("test", "gate passed %d", num);' + - script.stop: waiter diff --git a/tests/integration/test_api_homeassistant_action_no_subscriber.py b/tests/integration/test_api_homeassistant_action_no_subscriber.py new file mode 100644 index 0000000000..9e7594e0ce --- /dev/null +++ b/tests/integration/test_api_homeassistant_action_no_subscriber.py @@ -0,0 +1,79 @@ +"""Integration test for Home Assistant actions fired without a subscriber. + +Home Assistant subscribes to device actions shortly after authenticating, while +on_client_connected (and similar triggers) fire right at authentication. Actions +fired before any client has subscribed cannot be delivered - they must produce a +warning in the log instead of vanishing silently. +""" + +from __future__ import annotations + +import asyncio + +from aioesphomeapi import ButtonInfo, HomeassistantServiceCall +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_api_homeassistant_action_no_subscriber( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Undeliverable actions warn in the log; actions after subscribing arrive.""" + loop = asyncio.get_running_loop() + + boot_warning_future = loop.create_future() + connected_warning_future = loop.create_future() + button_action_future = loop.create_future() + + def check_output(line: str) -> None: + if ( + not boot_warning_future.done() + and "Home Assistant action 'test.boot_action' dropped; no client connected" + in line + ): + boot_warning_future.set_result(True) + if ( + not connected_warning_future.done() + and "Home Assistant action 'test.connected_action' dropped; " + "client has not subscribed to actions (yet)" + in line + ): + connected_warning_future.set_result(True) + + service_calls: list[HomeassistantServiceCall] = [] + + def on_service_call(service_call: HomeassistantServiceCall) -> None: + service_calls.append(service_call) + if ( + service_call.service == "test.button_action" + and not button_action_future.done() + ): + button_action_future.set_result(service_call) + + async with run_compiled(yaml_config, line_callback=check_output): + # The on_boot action fires with no client connected at all. + await asyncio.wait_for(boot_warning_future, timeout=10.0) + + async with api_client_connected() as client: + device_info = await client.device_info() + assert device_info.name == "test-ha-action-no-subscriber" + + # on_client_connected fired at authentication, before this client + # subscribed to Home Assistant actions. + await asyncio.wait_for(connected_warning_future, timeout=5.0) + + # After subscribing, actions must be delivered normally (and the + # dropped ones must not suddenly show up). + client.subscribe_service_calls(on_service_call) + + entities, _ = await client.list_entities_services() + button = next(e for e in entities if isinstance(e, ButtonInfo)) + client.button_command(button.key) + + button_call = await asyncio.wait_for(button_action_future, timeout=5.0) + assert button_call.data == {"value": "subscribed"} + assert [call.service for call in service_calls] == ["test.button_action"] diff --git a/tests/integration/test_blocking_warning_log_time_not_charged_to_next_operation.py b/tests/integration/test_blocking_warning_log_time_not_charged_to_next_operation.py new file mode 100644 index 0000000000..e8b08f2265 --- /dev/null +++ b/tests/integration/test_blocking_warning_log_time_not_charged_to_next_operation.py @@ -0,0 +1,63 @@ +"""Regression test for blocking-warning log time attribution.""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + +WARN_PATTERN = re.compile( + r"(\S+) took a long time for an operation \((\d+) ms\), max is (\d+) ms" +) +COMPLETE_PATTERN = re.compile(r"BLOCKING_WARNING_CASCADE_TEST_COMPLETE total=(\d+)") +PRIMARY_SOURCES = {"blocking_60", "blocking_90", "blocking_120"} + + +@pytest.mark.asyncio +async def test_blocking_warning_log_time_not_charged_to_next_operation( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Synchronous warning-log delays must not be charged to the next operation.""" + loop = asyncio.get_running_loop() + complete = asyncio.Event() + warnings: list[tuple[str, int, int]] = [] + injected_delay_total = 0 + + def check_output(line: str) -> None: + nonlocal injected_delay_total + if match := WARN_PATTERN.search(line): + warnings.append((match.group(1), int(match.group(2)), int(match.group(3)))) + if match := COMPLETE_PATTERN.search(line): + injected_delay_total = int(match.group(1)) + loop.call_soon_threadsafe(complete.set) + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + assert await client.device_info() is not None + await asyncio.wait_for(complete.wait(), timeout=10.0) + + assert injected_delay_total == 270, ( + f"Expected 270 ms of injected warning-log delay, got {injected_delay_total} ms" + ) + + primary_warnings = [ + warning for warning in warnings if warning[0] in PRIMARY_SOURCES + ] + assert {warning[0] for warning in primary_warnings} == PRIMARY_SOURCES, ( + f"Expected one real blocking warning from each test script, got: {warnings}" + ) + + secondary_warnings = [ + warning for warning in warnings if warning[0] not in PRIMARY_SOURCES + ] + assert not secondary_warnings, ( + "Warning-handler time was incorrectly charged to the next operation: " + f"{secondary_warnings}" + ) diff --git a/tests/integration/test_light_binary_effect_off_phase.py b/tests/integration/test_light_binary_effect_off_phase.py new file mode 100644 index 0000000000..571fca19f6 --- /dev/null +++ b/tests/integration/test_light_binary_effect_off_phase.py @@ -0,0 +1,207 @@ +"""Integration test verifying the off phase of an effect reaches an ON/OFF-only light. + +Regression test for https://github.com/esphome/esphome/issues/17873. A strobe effect +encodes its dark phase as `brightness = 0` while keeping `state = true`, so that the +effect keeps running instead of being stopped by an explicit turn-off. On a dimmable +light that works, because the output is driven by `state * brightness`. On a binary +light the dark phase used to be dropped, so the output stayed on forever. + +Effect ticks are published with `publish: false` (so Home Assistant isn't spammed with +every frame), so the effect's actual output can't be observed via API state broadcasts. +Instead, this test reads the output component's log lines, which are written on every +update regardless of the publish flag. + +The output log line is emitted strictly after the API state response: `perform()` +publishes inline, but the write is deferred to the next `LightState::loop()` iteration +and then has to cross the subprocess stdout pipe. So a future is armed *before* each +command and awaited afterwards, rather than reading the last observed value. +""" + +from __future__ import annotations + +import asyncio +import re +from typing import Any + +from aioesphomeapi import EntityState, LightState +import pytest + +from .state_utils import InitialStateHelper +from .types import APIClientConnectedFactory, RunCompiledFunction + +OUTPUT_PATTERN = re.compile(r"BINARY_OUTPUT:(YES|NO)") + + +@pytest.mark.asyncio +async def test_light_binary_effect_off_phase( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """A strobe effect must drive a binary light's output both on and off.""" + loop = asyncio.get_running_loop() + observed: list[bool] = [] + pending: list[asyncio.Future[bool]] = [] + + def on_log_line(line: str) -> None: + if match := OUTPUT_PATTERN.search(line): + value = match.group(1) == "YES" + observed.append(value) + while pending: + future = pending.pop(0) + if not future.done(): + future.set_result(value) + break + + def arm_output() -> asyncio.Future[bool]: + """Arm a future for the next output write, before sending the command.""" + future: asyncio.Future[bool] = loop.create_future() + pending.append(future) + return future + + async with ( + run_compiled(yaml_config, line_callback=on_log_line), + api_client_connected() as client, + ): + entities, _ = await client.list_entities_services() + light = next(e for e in entities if e.object_id == "test_binary_light") + + state_futures: dict[int, asyncio.Future[LightState]] = {} + + def on_state(state: EntityState) -> None: + if isinstance(state, LightState) and state.key in state_futures: + future = state_futures[state.key] + if not future.done(): + future.set_result(state) + + # ESPHome sends the current state of every entity right after connecting; drain + # that initial burst so it can't be mistaken for the response to a command below. + initial_state_helper = InitialStateHelper(entities) + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + await initial_state_helper.wait_for_initial_states() + + async def send_and_wait(timeout: float = 5.0, **kwargs: Any) -> LightState: + """Send a light command and wait for the matching state response.""" + state_futures[light.key] = loop.create_future() + client.light_command(key=light.key, **kwargs) + return await asyncio.wait_for(state_futures[light.key], timeout=timeout) + + # A plain turn-on must drive the output on -- brightness defaults to 100% and + # must not be mistaken for a dark phase. + output = arm_output() + state = await send_and_wait(state=True) + assert state.state is True + assert await asyncio.wait_for(output, timeout=5.0) is True, ( + "Plain turn-on did not switch the output on" + ) + + # Run the strobe effect; both phases must reach the output. + observed.clear() + state = await send_and_wait(effect="Fast Strobe") + assert state.effect == "Fast Strobe" + # Let several effect cycles run (each phase is 50ms in the fixture). + await asyncio.sleep(1.0) + + assert True in observed, ( + f"Strobe effect never switched the output on -- got {observed}" + ) + assert False in observed, ( + f"Strobe effect never switched the output off; its dark phase was lost -- " + f"got {observed}" + ) + + # Stopping the effect must leave the light usable. + state = await send_and_wait(effect="None") + assert state.effect == "None" + output = arm_output() + state = await send_and_wait(state=True) + assert state.state is True + assert await asyncio.wait_for(output, timeout=5.0) is True, ( + "Light stayed off after the effect stopped" + ) + + # An explicit turn-off still switches the output off. + output = arm_output() + state = await send_and_wait(state=False) + assert state.state is False + assert await asyncio.wait_for(output, timeout=5.0) is False, ( + "Turn-off did not switch the output off" + ) + + +@pytest.mark.asyncio +async def test_light_binary_zero_brightness_is_recoverable( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Zero brightness on an ON/OFF light must not leave it permanently stuck off. + + An ON/OFF light has no brightness capability, so `turn_on` with 0% brightness has + no representable "on but dark" state. It must switch the output off and report the + light as off, and a later plain turn-on must bring it back. + """ + loop = asyncio.get_running_loop() + pending: list[asyncio.Future[bool]] = [] + + def on_log_line(line: str) -> None: + if match := OUTPUT_PATTERN.search(line): + value = match.group(1) == "YES" + while pending: + future = pending.pop(0) + if not future.done(): + future.set_result(value) + break + + def arm_output() -> asyncio.Future[bool]: + future: asyncio.Future[bool] = loop.create_future() + pending.append(future) + return future + + async with ( + run_compiled(yaml_config, line_callback=on_log_line), + api_client_connected() as client, + ): + entities, _ = await client.list_entities_services() + light = next(e for e in entities if e.object_id == "test_binary_light") + + state_futures: dict[int, asyncio.Future[LightState]] = {} + + def on_state(state: EntityState) -> None: + if isinstance(state, LightState) and state.key in state_futures: + future = state_futures[state.key] + if not future.done(): + future.set_result(state) + + initial_state_helper = InitialStateHelper(entities) + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + await initial_state_helper.wait_for_initial_states() + + async def send_and_wait(timeout: float = 5.0, **kwargs: Any) -> LightState: + state_futures[light.key] = loop.create_future() + client.light_command(key=light.key, **kwargs) + return await asyncio.wait_for(state_futures[light.key], timeout=timeout) + + output = arm_output() + state = await send_and_wait(state=True) + assert state.state is True + assert await asyncio.wait_for(output, timeout=5.0) is True + + # Turning on at 0% brightness has no representable "on but dark" state here, + # so the light must switch off and report itself as off. + output = arm_output() + state = await send_and_wait(state=True, brightness=0.0) + assert await asyncio.wait_for(output, timeout=5.0) is False, ( + "Zero brightness did not switch the output off" + ) + assert state.state is False, ( + "Light reported itself as on while its output was off" + ) + + # A plain turn-on must recover -- the stored zero brightness must not persist. + output = arm_output() + state = await send_and_wait(state=True) + assert state.state is True + assert await asyncio.wait_for(output, timeout=5.0) is True, ( + "Light was left permanently off by a zero-brightness turn-on" + ) diff --git a/tests/integration/test_light_calls.py b/tests/integration/test_light_calls.py index a3a4103f5c..b75e2fac62 100644 --- a/tests/integration/test_light_calls.py +++ b/tests/integration/test_light_calls.py @@ -341,14 +341,14 @@ async def test_light_calls( assert state.state is True assert state.brightness == pytest.approx(1.0) - # Test 31b: An explicit turn-on with brightness 0 still resets to full - # brightness - a turn-on must never leave the light on-but-invisible. This - # is the same path the restore logic exercises (set_state(true) + - # set_brightness(0) from a persisted brightness=0 turn-off). + # Test 31b: An explicit turn-on with brightness 0 respects the explicit value and + # stays dark. Only a turn-on with no brightness specified (Test 31) restores + # visibility -- an explicit brightness request (e.g. from a light effect's dark + # phase) is never overridden. client.light_command(key=rgbcw_light.key, state=True, brightness=0.0) state = await wait_for_state_change(rgbcw_light.key) assert state.state is True - assert state.brightness == pytest.approx(1.0) + assert state.brightness == pytest.approx(0.0) # Test 32: Turning a light on when it already has nonzero brightness leaves # the brightness unchanged (the reset only happens when brightness is 0). diff --git a/tests/integration/test_light_effect_zero_brightness.py b/tests/integration/test_light_effect_zero_brightness.py new file mode 100644 index 0000000000..6c386d4229 --- /dev/null +++ b/tests/integration/test_light_effect_zero_brightness.py @@ -0,0 +1,91 @@ +"""Integration test verifying light effects can dim to 0% brightness while staying on. + +Regression test for https://github.com/esphome/esphome/issues/17639, where PR #17103's +"make turn-on visible" logic in LightCall::validate_() also clobbered brightness set by a +running effect (e.g. pulse, strobe), forcing it back to 100% and breaking the dark phase +of those effects. + +Effect ticks are published with `publish: false` (so Home Assistant isn't spammed with +every frame), so the effect's actual output can't be observed via API state broadcasts. +Instead, this test reads the output component's log lines, which are written on every +update regardless of the publish flag. +""" + +from __future__ import annotations + +import asyncio +import re +from typing import Any + +from aioesphomeapi import EntityState, LightState +import pytest + +from .state_utils import InitialStateHelper +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_light_effect_zero_brightness( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Pulse and strobe effects must be able to reach 0% brightness while the light stays on.""" + output_pattern = re.compile(r"PULSE_OUTPUT:([\d.]+)") + observed: list[float] = [] + + def on_log_line(line: str) -> None: + match = output_pattern.search(line) + if match: + observed.append(float(match.group(1))) + + async with ( + run_compiled(yaml_config, line_callback=on_log_line), + api_client_connected() as client, + ): + entities, _ = await client.list_entities_services() + light = next(e for e in entities if e.object_id == "test_pulse_light") + + state_futures: dict[int, asyncio.Future[LightState]] = {} + + def on_state(state: EntityState) -> None: + if isinstance(state, LightState) and state.key in state_futures: + future = state_futures[state.key] + if not future.done(): + future.set_result(state) + + # ESPHome sends the current state of every entity right after connecting; drain + # that initial burst so it can't be mistaken for the response to a command below. + initial_state_helper = InitialStateHelper(entities) + client.subscribe_states(initial_state_helper.on_state_wrapper(on_state)) + await initial_state_helper.wait_for_initial_states() + + async def send_and_wait(timeout: float = 5.0, **kwargs: Any) -> LightState: + """Send a light command and wait for the matching state response.""" + state_futures[light.key] = asyncio.get_running_loop().create_future() + client.light_command(key=light.key, **kwargs) + return await asyncio.wait_for(state_futures[light.key], timeout=timeout) + + # Turn the light on first so the effect starts from a known, visible state. + state = await send_and_wait(state=True, brightness=1.0) + assert state.state is True + assert state.brightness == pytest.approx(1.0) + + for effect_name in ("Fast Pulse", "Fast Strobe"): + observed.clear() + state = await send_and_wait(effect=effect_name) + assert state.effect == effect_name + # Let several effect cycles run (update_interval/duration is 50ms in the fixture). + await asyncio.sleep(1.0) + + assert observed, f"No output observed while running effect {effect_name!r}" + assert min(observed) == pytest.approx(0.0, abs=0.01), ( + f"Effect {effect_name!r} never dimmed to 0% brightness while the light " + f"stayed on -- got min={min(observed):.4f} (values: {observed})" + ) + assert max(observed) > 0.5, ( + f"Effect {effect_name!r} never reached full brightness -- " + f"got max={max(observed):.4f}" + ) + + client.light_command(key=light.key, effect="None") diff --git a/tests/integration/test_light_initial_state.py b/tests/integration/test_light_initial_state.py index f1cd96dbf0..657e273fe7 100644 --- a/tests/integration/test_light_initial_state.py +++ b/tests/integration/test_light_initial_state.py @@ -11,6 +11,14 @@ from .state_utils import InitialStateHelper, require_entity from .types import APIClientConnectedFactory, RunCompiledFunction +@pytest.fixture(autouse=True) +def isolated_preferences(monkeypatch: pytest.MonkeyPatch, tmp_path) -> None: + """Keep host preferences per-test so RESTORE_AND_ON never loads a stale value left + behind by a previous run (host preferences otherwise persist to ~/.esphome/prefs, + keyed only by device name).""" + monkeypatch.setenv("ESPHOME_PREFDIR", str(tmp_path / "prefs")) + + @pytest.mark.asyncio async def test_light_initial_state( yaml_config: str, @@ -36,3 +44,10 @@ async def test_light_initial_state( assert state.red == pytest.approx(1.0, abs=0.01) assert state.green == pytest.approx(0.5, abs=0.01) assert state.blue == pytest.approx(0.0, abs=0.01) + + # Regression test: RESTORE_AND_ON always forces the light on at boot, even when + # the recovered/initial brightness was 0 -- it must never come up on-but-invisible. + restore_and_on_light = require_entity(entities, "test_restore_and_on_light") + restore_and_on_state = helper.initial_states[restore_and_on_light.key] + assert restore_and_on_state.state is True + assert restore_and_on_state.brightness == pytest.approx(1.0) diff --git a/tests/integration/test_scheduler_numeric_id_test.py b/tests/integration/test_scheduler_numeric_id_test.py index c1958db685..3591e3014e 100644 --- a/tests/integration/test_scheduler_numeric_id_test.py +++ b/tests/integration/test_scheduler_numeric_id_test.py @@ -18,7 +18,6 @@ async def test_scheduler_numeric_id_test( # Track counts timeout_count = 0 interval_count = 0 - retry_count = 0 defer_count = 0 # Events for each test completion @@ -32,8 +31,6 @@ async def test_scheduler_numeric_id_test( component_interval_fired = asyncio.Event() zero_id_timeout_fired = asyncio.Event() max_id_timeout_fired = asyncio.Event() - numeric_retry_done = asyncio.Event() - numeric_retry_cancelled = asyncio.Event() numeric_defer_7001_fired = asyncio.Event() numeric_defer_7002_fired = asyncio.Event() numeric_defer_cancelled = asyncio.Event() @@ -41,11 +38,10 @@ async def test_scheduler_numeric_id_test( # Track interval counts numeric_interval_count = 0 - numeric_retry_count = 0 def on_log_line(line: str) -> None: - nonlocal timeout_count, interval_count, retry_count, defer_count - nonlocal numeric_interval_count, numeric_retry_count + nonlocal timeout_count, interval_count, defer_count + nonlocal numeric_interval_count # Strip ANSI color codes clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line) @@ -97,18 +93,6 @@ async def test_scheduler_numeric_id_test( max_id_timeout_fired.set() timeout_count += 1 - # Check for numeric retry tests - elif "Numeric retry 6001 attempt" in clean_line: - match = re.search(r"attempt (\d+)", clean_line) - if match: - numeric_retry_count = int(match.group(1)) - - elif "Numeric retry 6001 done" in clean_line: - numeric_retry_done.set() - - elif "Cancelled numeric retry 6002" in clean_line: - numeric_retry_cancelled.set() - # Check for numeric defer tests elif "Component numeric defer 7001 fired" in clean_line: numeric_defer_7001_fired.set() @@ -122,14 +106,13 @@ async def test_scheduler_numeric_id_test( # Check for final results elif "Final results" in clean_line: match = re.search( - r"Timeouts: (\d+), Intervals: (\d+), Retries: (\d+), Defers: (\d+)", + r"Timeouts: (\d+), Intervals: (\d+), Defers: (\d+)", clean_line, ) if match: timeout_count = int(match.group(1)) interval_count = int(match.group(2)) - retry_count = int(match.group(3)) - defer_count = int(match.group(4)) + defer_count = int(match.group(3)) final_results_logged.set() async with ( @@ -200,23 +183,6 @@ async def test_scheduler_numeric_id_test( except TimeoutError: pytest.fail("Max ID timeout did not fire within 0.5 seconds") - # Wait for numeric retry tests - try: - await asyncio.wait_for(numeric_retry_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Numeric retry 6001 did not complete. Count: {numeric_retry_count}" - ) - - assert numeric_retry_count >= 2, ( - f"Expected at least 2 numeric retry attempts, got {numeric_retry_count}" - ) - - # Verify numeric retry was cancelled - assert numeric_retry_cancelled.is_set(), ( - "Numeric retry 6002 should have been cancelled" - ) - # Wait for numeric defer tests try: await asyncio.wait_for(numeric_defer_7001_fired.wait(), timeout=0.5) @@ -245,7 +211,4 @@ async def test_scheduler_numeric_id_test( assert interval_count >= 3, ( f"Expected at least 3 interval fires, got {interval_count}" ) - assert retry_count >= 2, ( - f"Expected at least 2 retry attempts, got {retry_count}" - ) assert defer_count >= 2, f"Expected at least 2 defer fires, got {defer_count}" diff --git a/tests/integration/test_scheduler_retry_test.py b/tests/integration/test_scheduler_retry_test.py deleted file mode 100644 index 910034e5bb..0000000000 --- a/tests/integration/test_scheduler_retry_test.py +++ /dev/null @@ -1,279 +0,0 @@ -"""Test scheduler retry functionality.""" - -import asyncio -import re - -import pytest - -from .types import APIClientConnectedFactory, RunCompiledFunction - - -@pytest.mark.asyncio -async def test_scheduler_retry_test( - yaml_config: str, - run_compiled: RunCompiledFunction, - api_client_connected: APIClientConnectedFactory, -) -> None: - """Test that scheduler retry functionality works correctly.""" - # Track test progress - simple_retry_done = asyncio.Event() - backoff_retry_done = asyncio.Event() - immediate_done_done = asyncio.Event() - cancel_retry_done = asyncio.Event() - empty_name_retry_done = asyncio.Event() - component_retry_done = asyncio.Event() - multiple_name_done = asyncio.Event() - const_char_done = asyncio.Event() - static_char_done = asyncio.Event() - test_complete = asyncio.Event() - - # Track retry counts - simple_retry_count = 0 - backoff_retry_count = 0 - immediate_done_count = 0 - cancel_retry_count = 0 - empty_name_retry_count = 0 - component_retry_count = 0 - multiple_name_count = 0 - const_char_retry_count = 0 - static_char_retry_count = 0 - - # Track specific test results - cancel_result = None - empty_cancel_result = None - backoff_intervals = [] - - def on_log_line(line: str) -> None: - nonlocal simple_retry_count, backoff_retry_count, immediate_done_count - nonlocal cancel_retry_count, empty_name_retry_count, component_retry_count - nonlocal multiple_name_count, const_char_retry_count, static_char_retry_count - nonlocal cancel_result, empty_cancel_result - - # Strip ANSI color codes - clean_line = re.sub(r"\x1b\[[0-9;]*m", "", line) - - # Simple retry test - if "Simple retry attempt" in clean_line: - if match := re.search(r"Simple retry attempt (\d+)", clean_line): - simple_retry_count = int(match.group(1)) - - elif "Simple retry succeeded on attempt" in clean_line: - simple_retry_done.set() - - # Backoff retry test - elif "Backoff retry attempt" in clean_line: - if match := re.search( - r"Backoff retry attempt (\d+).*interval=(\d+)ms", clean_line - ): - backoff_retry_count = int(match.group(1)) - interval = int(match.group(2)) - if backoff_retry_count > 1: # Skip first (immediate) call - backoff_intervals.append(interval) - - elif "Backoff retry completed" in clean_line: - backoff_retry_done.set() - - # Immediate done test - elif "Immediate done retry called" in clean_line: - immediate_done_count += 1 - immediate_done_done.set() - - # Cancel retry test - elif "Cancel test retry attempt" in clean_line: - cancel_retry_count += 1 - - elif "Retry cancellation result:" in clean_line: - cancel_result = "true" in clean_line - cancel_retry_done.set() - - # Empty name retry test - elif "Empty name retry attempt" in clean_line: - if match := re.search(r"Empty name retry attempt (\d+)", clean_line): - empty_name_retry_count = int(match.group(1)) - - elif "Empty name retry cancel result:" in clean_line: - empty_cancel_result = "true" in clean_line - - elif "Empty name retry ran" in clean_line: - empty_name_retry_done.set() - - # Component retry test - elif "Component retry attempt" in clean_line: - if match := re.search(r"Component retry attempt (\d+)", clean_line): - component_retry_count = int(match.group(1)) - if component_retry_count >= 2: - component_retry_done.set() - - # Multiple same name test - elif "Second duplicate retry attempt" in clean_line: - if match := re.search(r"counter=(\d+)", clean_line): - multiple_name_count = int(match.group(1)) - if multiple_name_count >= 20: - multiple_name_done.set() - - # Const char retry test - elif "Const char retry" in clean_line: - if match := re.search(r"Const char retry (\d+)", clean_line): - const_char_retry_count = int(match.group(1)) - const_char_done.set() - - # Static const char retry test - elif "Static const char retry" in clean_line: - if match := re.search(r"Static const char retry (\d+)", clean_line): - static_char_retry_count = int(match.group(1)) - static_char_done.set() - - elif "Static cancel result:" in clean_line: - # This is part of test 9, but we don't track it separately - pass - - # Test completion - elif "All retry tests completed" in clean_line: - test_complete.set() - - async with ( - run_compiled(yaml_config, line_callback=on_log_line), - api_client_connected() as client, - ): - # Verify we can connect - device_info = await client.device_info() - assert device_info is not None - assert device_info.name == "scheduler-retry-test" - - # Wait for simple retry test - try: - await asyncio.wait_for(simple_retry_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Simple retry test did not complete. Count: {simple_retry_count}" - ) - - assert simple_retry_count == 2, ( - f"Expected 2 simple retry attempts, got {simple_retry_count}" - ) - - # Wait for backoff retry test - try: - await asyncio.wait_for(backoff_retry_done.wait(), timeout=3.0) - except TimeoutError: - pytest.fail( - f"Backoff retry test did not complete. Count: {backoff_retry_count}" - ) - - assert backoff_retry_count == 4, ( - f"Expected 4 backoff retry attempts, got {backoff_retry_count}" - ) - - # Verify backoff intervals (allowing for timing variations) - assert len(backoff_intervals) >= 2, ( - f"Expected at least 2 intervals, got {len(backoff_intervals)}" - ) - if len(backoff_intervals) >= 3: - # First interval should be ~50ms (very wide tolerance for heavy system load) - assert 20 <= backoff_intervals[0] <= 150, ( - f"First interval {backoff_intervals[0]}ms not ~50ms" - ) - # Second interval should be ~100ms (50ms * 2.0) - assert 50 <= backoff_intervals[1] <= 250, ( - f"Second interval {backoff_intervals[1]}ms not ~100ms" - ) - # Third interval should be ~200ms (100ms * 2.0) - assert 100 <= backoff_intervals[2] <= 500, ( - f"Third interval {backoff_intervals[2]}ms not ~200ms" - ) - - # Wait for immediate done test - try: - await asyncio.wait_for(immediate_done_done.wait(), timeout=3.0) - except TimeoutError: - pytest.fail( - f"Immediate done test did not complete. Count: {immediate_done_count}" - ) - - assert immediate_done_count == 1, ( - f"Expected 1 immediate done call, got {immediate_done_count}" - ) - - # Wait for cancel retry test - try: - await asyncio.wait_for(cancel_retry_done.wait(), timeout=3.0) - except TimeoutError: - pytest.fail( - f"Cancel retry test did not complete. Count: {cancel_retry_count}" - ) - - assert cancel_result is True, "Retry cancellation should have succeeded" - assert 2 <= cancel_retry_count <= 5, ( - f"Expected 2-5 cancel retry attempts before cancellation, got {cancel_retry_count}" - ) - - # Wait for empty name retry test - try: - await asyncio.wait_for(empty_name_retry_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Empty name retry test did not complete. Count: {empty_name_retry_count}" - ) - - # Empty name retry should run at least once before being cancelled - assert 1 <= empty_name_retry_count <= 3, ( - f"Expected 1-3 empty name retry attempts, got {empty_name_retry_count}" - ) - assert empty_cancel_result is True, ( - "Empty name retry cancel should have succeeded" - ) - - # Wait for component retry test - try: - await asyncio.wait_for(component_retry_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Component retry test did not complete. Count: {component_retry_count}" - ) - - assert component_retry_count >= 2, ( - f"Expected at least 2 component retry attempts, got {component_retry_count}" - ) - - # Wait for multiple same name test - try: - await asyncio.wait_for(multiple_name_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Multiple same name test did not complete. Count: {multiple_name_count}" - ) - - # Should be 20+ (only second retry should run) - assert multiple_name_count >= 20, ( - f"Expected multiple name count >= 20 (second retry only), got {multiple_name_count}" - ) - - # Wait for const char retry test - try: - await asyncio.wait_for(const_char_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Const char retry test did not complete. Count: {const_char_retry_count}" - ) - - assert const_char_retry_count == 1, ( - f"Expected 1 const char retry call, got {const_char_retry_count}" - ) - - # Wait for static char retry test - try: - await asyncio.wait_for(static_char_done.wait(), timeout=1.0) - except TimeoutError: - pytest.fail( - f"Static char retry test did not complete. Count: {static_char_retry_count}" - ) - - assert static_char_retry_count == 1, ( - f"Expected 1 static char retry call, got {static_char_retry_count}" - ) - - # Wait for test completion - try: - await asyncio.wait_for(test_complete.wait(), timeout=1.0) - except TimeoutError: - pytest.fail("Test did not complete within timeout") diff --git a/tests/integration/test_wait_until_reentrant_restart.py b/tests/integration/test_wait_until_reentrant_restart.py new file mode 100644 index 0000000000..9c73339515 --- /dev/null +++ b/tests/integration/test_wait_until_reentrant_restart.py @@ -0,0 +1,89 @@ +"""Integration test for wait_until queue reentrancy. + +When a wait_until completes, the rest of the action chain runs synchronously +from inside the wait queue processing. That chain can re-enter the very same +WaitUntilAction - for example a script with mode: restart that executes itself +as a retry pattern, or a waiter that stops its own script. Both used to mutate +the std::list while it was being iterated, corrupting it and crashing the +device (Guru Meditation StoreProhibited in _M_transfer). +""" + +from __future__ import annotations + +import asyncio +import re + +import pytest + +from .types import APIClientConnectedFactory, RunCompiledFunction + + +@pytest.mark.asyncio +async def test_wait_until_reentrant_restart( + yaml_config: str, + run_compiled: RunCompiledFunction, + api_client_connected: APIClientConnectedFactory, +) -> None: + """Test that re-entering a wait_until from its own continuation is safe.""" + retry_complete = asyncio.Event() + stop_complete = asyncio.Event() + + attempt_pattern = re.compile(r"attempt (\d+) done") + gate_pattern = re.compile(r"gate passed (\d+)") + + attempts: list[int] = [] + gate_passed: list[int] = [] + + def check_output(line: str) -> None: + """Check log output for expected messages.""" + if mo := attempt_pattern.search(line): + attempts.append(int(mo.group(1))) + elif mo := gate_pattern.search(line): + gate_passed.append(int(mo.group(1))) + elif "retry test complete" in line: + retry_complete.set() + elif "stop test complete" in line: + stop_complete.set() + + async with ( + run_compiled(yaml_config, line_callback=check_output), + api_client_connected() as client, + ): + device_info = await client.device_info() + assert device_info is not None + assert device_info.name == "wait-until-reentrant-restart" + + _, services = await client.list_entities_services() + self_restart_service = next( + (s for s in services if s.name == "start_self_restart"), None + ) + assert self_restart_service is not None, "start_self_restart not found" + stop_service = next( + (s for s in services if s.name == "start_stop_during_wait"), None + ) + assert stop_service is not None, "start_stop_during_wait not found" + + # Scenario 1: the wait_until timeout continuation restarts its own + # script five times, re-entering the same wait_until each time. + await client.execute_service(self_restart_service, {}) + try: + await asyncio.wait_for(retry_complete.wait(), timeout=10.0) + except TimeoutError: + pytest.fail(f"Self-restart retry did not finish. Attempts: {attempts}") + assert attempts == [1, 2, 3, 4, 5], attempts + + # Scenario 2: the first waiter through the gate stops the script while + # the other waiters are still queued in the same wait_until; both the + # not-yet-checked waiters (2, 3) and the already-checked still-waiting + # blocker (0) must be cancelled, not fired. + await client.execute_service(stop_service, {}) + try: + await asyncio.wait_for(stop_complete.wait(), timeout=10.0) + except TimeoutError: + pytest.fail(f"Stop-during-wait did not finish. Gate passed: {gate_passed}") + assert gate_passed == [1], gate_passed + + # If the cancelled blocker had been kept, its 1s wait_until timeout + # would still fire - give it the chance and check it stays silent. + await asyncio.sleep(1.5) + assert gate_passed == [1], gate_passed diff --git a/tests/script/test_determine_jobs.py b/tests/script/test_determine_jobs.py index d018c6dbd0..a05b683a5f 100644 --- a/tests/script/test_determine_jobs.py +++ b/tests/script/test_determine_jobs.py @@ -2993,3 +2993,53 @@ def test_main_force_all_off_uses_detection( assert output["component_test_count"] == 0 mock_determine_integration_tests.assert_called_once() mock_should_run_clang_tidy.assert_called_once() + + +# Every platform the memory impact analysis can select must produce an ELF that +# find_elf_path knows how to locate. The analysis fails the job when it cannot +# find one, so a platform with an unknown layout would turn a clean build red. +_MEMORY_IMPACT_ELF_LAYOUTS = { + # Native ESP-IDF toolchain (the esp32 default): /build/firmware.elf + "esp32-c6-idf": "build/firmware.elf", + "esp32-idf": "build/firmware.elf", + "esp32-c3-idf": "build/firmware.elf", + "esp32-s2-idf": "build/firmware.elf", + "esp32-s3-idf": "build/firmware.elf", + # PlatformIO: /.pioenvs//firmware.elf + "esp8266-ard": ".pioenvs/{name}/firmware.elf", + "rp2040-ard": ".pioenvs/{name}/firmware.elf", + "rp2350-ard": ".pioenvs/{name}/firmware.elf", + # LibreTiny: /.pioenvs//raw_firmware.elf + "bk72xx-ard": ".pioenvs/{name}/raw_firmware.elf", + "rtl87xx-ard": ".pioenvs/{name}/raw_firmware.elf", + "ln882x-ard": ".pioenvs/{name}/raw_firmware.elf", + # Zephyr: /.pioenvs//zephyr/[zephyr/]zephyr.elf + "nrf52-adafruit": ".pioenvs/{name}/zephyr/zephyr/zephyr.elf", +} + + +def test_memory_impact_platforms_have_known_elf_layout() -> None: + """Every selectable memory impact platform has a documented ELF layout. + + Adding a platform to the preference list without teaching find_elf_path + where its ELF lands would fail the memory impact job on a clean build. + """ + selectable = { + platform.value for platform in determine_jobs.MEMORY_IMPACT_PLATFORM_PREFERENCE + } + selectable.add(determine_jobs.MEMORY_IMPACT_FALLBACK_PLATFORM.value) + + assert selectable == set(_MEMORY_IMPACT_ELF_LAYOUTS) + + +def test_memory_impact_elf_layouts_are_found(tmp_path: Path) -> None: + """find_elf_path locates the ELF each memory impact platform produces.""" + from esphome.analyze_memory.toolchain import find_elf_path + + for platform, layout in _MEMORY_IMPACT_ELF_LAYOUTS.items(): + build_path = tmp_path / platform / ".esphome" / "build" / "mydevice" + elf = build_path / layout.format(name=build_path.name) + elf.parent.mkdir(parents=True) + elf.write_text("") + + assert find_elf_path(build_path) == elf, f"{platform} ELF not found" diff --git a/tests/script/test_docker_build.py b/tests/script/test_docker_build.py index 34bcc4e714..06dc21a92e 100644 --- a/tests/script/test_docker_build.py +++ b/tests/script/test_docker_build.py @@ -71,10 +71,12 @@ def test_branch_manifest_targets_ghcr_only( ) assert commands == [ - "docker buildx imagetools create " - "--tag ghcr.io/esphome/esphome-hassio:my-branch " - "ghcr.io/esphome/esphome-hassio-amd64:my-branch " - "ghcr.io/esphome/esphome-hassio-aarch64:my-branch" + ( + "docker buildx imagetools create " + "--tag ghcr.io/esphome/esphome-hassio:my-branch " + "ghcr.io/esphome/esphome-hassio-amd64:my-branch " + "ghcr.io/esphome/esphome-hassio-aarch64:my-branch" + ) ] diff --git a/tests/script/test_helpers.py b/tests/script/test_helpers.py index 886d413ccf..43c4445dcf 100644 --- a/tests/script/test_helpers.py +++ b/tests/script/test_helpers.py @@ -79,6 +79,22 @@ def test_get_pr_number_from_github_env_event_file( assert result == "5678" +def test_get_github_event_data_decodes_utf8_regardless_of_locale( + monkeypatch: MonkeyPatch, tmp_path: Path +) -> None: + """The event payload is UTF-8; parsing must not depend on the platform + default encoding. On Windows the default is cp1252, which raised + UnicodeDecodeError as soon as a commit title carried non ASCII text.""" + event_file = tmp_path / "event.json" + event_data = {"head_commit": {"message": "Answer UNPAIR with Response… é"}} + event_file.write_bytes(json.dumps(event_data, ensure_ascii=False).encode("utf-8")) + monkeypatch.setenv("GITHUB_EVENT_PATH", str(event_file)) + + result = helpers._get_github_event_data() + + assert result == event_data + + def test_get_pr_number_from_github_env_no_pr( monkeypatch: MonkeyPatch, tmp_path: Path ) -> None: diff --git a/tests/test_build_components/common/test_display/test_display.yaml b/tests/test_build_components/common/test_display/test_display.yaml new file mode 100644 index 0000000000..986ab45223 --- /dev/null +++ b/tests/test_build_components/common/test_display/test_display.yaml @@ -0,0 +1,26 @@ +# Shared "test display" package for component tests. +# +# Provides a no-op display (id: test_display_screen) that uses no pins and no +# bus, so tests that only need a display to exist -- touchscreens especially -- +# don't have to instantiate a real driver and fight it over GPIOs. Include it +# like a common bus package; the consuming test does NOT need to declare +# external_components itself: +# +# packages: +# test_display: !include ../../test_build_components/common/test_display/test_display.yaml +# +# then point the touchscreen (or other display consumer) at `test_display_screen`. +# +# The test_display platform lives at tests/components/test_display/components/ and +# is loaded via external_components. The source path is written relative to the +# build directory (tests/test_build_components/build/), which every test -- +# standalone or grouped -- is generated into, so this always resolves to the +# component under tests/components/test_display/. +external_components: + - source: ../../components/test_display/components + components: [test_display] + +display: + - platform: test_display + id: test_display_screen + dimensions: 240x320 diff --git a/tests/unit_tests/analyze_memory/test_build_artifacts.py b/tests/unit_tests/analyze_memory/test_build_artifacts.py new file mode 100644 index 0000000000..ad2f8c2210 --- /dev/null +++ b/tests/unit_tests/analyze_memory/test_build_artifacts.py @@ -0,0 +1,157 @@ +"""Tests for locating build artifacts across the supported toolchain layouts.""" + +from pathlib import Path + +import pytest + +from esphome.analyze_memory.toolchain import ( + find_elf_path, + find_idedata_path, + idedata_candidates, +) +from esphome.espidf.idedata import _cc_path_from_cxx +from esphome.platformio.toolchain import IDEData + + +def _make_build_dir(tmp_path: Path, name: str = "mydevice") -> Path: + """Create /.esphome/build/, mirroring a real data dir.""" + build_path = tmp_path / ".esphome" / "build" / name + build_path.mkdir(parents=True) + return build_path + + +def _touch(path: Path) -> Path: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("", encoding="utf-8") + return path + + +def test_find_elf_path_native_esp_idf(tmp_path: Path) -> None: + """The native ESP-IDF toolchain writes the ELF under build/.""" + build_path = _make_build_dir(tmp_path) + elf = _touch(build_path / "build" / "firmware.elf") + + assert find_elf_path(build_path) == elf + + +def test_find_elf_path_platformio(tmp_path: Path) -> None: + """The PlatformIO toolchain writes the ELF under .pioenvs//.""" + build_path = _make_build_dir(tmp_path) + elf = _touch(build_path / ".pioenvs" / build_path.name / "firmware.elf") + + assert find_elf_path(build_path) == elf + + +def test_find_elf_path_libretiny(tmp_path: Path) -> None: + """The LibreTiny toolchain names the unwrapped ELF raw_firmware.elf.""" + build_path = _make_build_dir(tmp_path) + elf = _touch(build_path / ".pioenvs" / build_path.name / "raw_firmware.elf") + + assert find_elf_path(build_path) == elf + + +@pytest.mark.parametrize( + "relative_elf", + [ + # SDK < 2.9.2 + "zephyr/zephyr.elf", + # SDK >= 2.9.2 nests the artifacts one level deeper + "zephyr/zephyr/zephyr.elf", + ], +) +def test_find_elf_path_zephyr(tmp_path: Path, relative_elf: str) -> None: + """Zephyr (nRF52) keeps the ELF under .pioenvs//zephyr/.""" + build_path = _make_build_dir(tmp_path) + elf = _touch(build_path / ".pioenvs" / build_path.name / relative_elf) + + assert find_elf_path(build_path) == elf + + +def test_find_elf_path_missing(tmp_path: Path) -> None: + """An unknown layout resolves to None rather than a bogus path.""" + assert find_elf_path(_make_build_dir(tmp_path)) is None + + +def test_find_idedata_path_in_data_dir(tmp_path: Path) -> None: + """The idedata cache sits in the data dir that holds the build dir.""" + build_path = _make_build_dir(tmp_path) + idedata = _touch(tmp_path / ".esphome" / "idedata" / f"{build_path.name}.json") + + assert find_idedata_path(build_path) == idedata + + +def test_find_idedata_path_in_pioenvs(tmp_path: Path) -> None: + """Test builds may keep idedata alongside the PlatformIO env.""" + build_path = _make_build_dir(tmp_path) + idedata = _touch(build_path / ".pioenvs" / build_path.name / "idedata.json") + + assert find_idedata_path(build_path) == idedata + + +def test_find_idedata_path_missing( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """A missing idedata resolves to None.""" + # Keep the cwd/home fallbacks from finding an unrelated file on this machine + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path)) + + assert find_idedata_path(_make_build_dir(tmp_path)) is None + + +def test_idedata_candidates_are_what_find_probes( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """Every advertised candidate is one find_idedata_path actually accepts. + + The candidates are reported to the user when idedata is missing, so a list + that drifts from the lookup would send someone hunting in the wrong place. + """ + # Two candidates are relative to the cwd and to home; keep the test from + # writing into the real ones. + monkeypatch.chdir(tmp_path) + monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path)) + + build_path = _make_build_dir(tmp_path) + candidates = idedata_candidates(build_path) + + assert candidates, "no candidates advertised" + for candidate in candidates: + _touch(candidate) + assert find_idedata_path(build_path) == candidate + candidate.unlink() + + +@pytest.mark.parametrize( + ("cxx_path", "expected"), + [ + ("/tools/bin/xtensa-esp32-elf-g++", "/tools/bin/xtensa-esp32-elf-gcc"), + ("/tools/bin/riscv32-esp-elf-g++", "/tools/bin/riscv32-esp-elf-gcc"), + ( + r"C:\tools\bin\xtensa-esp32-elf-g++.exe", + r"C:\tools\bin\xtensa-esp32-elf-gcc.exe", + ), + # Nothing to rewrite; leave the path alone + ("/tools/bin/clang++", "/tools/bin/clang++"), + ], +) +def test_cc_path_from_cxx(cxx_path: str, expected: str) -> None: + """cc_path is derived from the C++ compiler that compile_commands.json names.""" + assert _cc_path_from_cxx(cxx_path) == expected + + +def test_native_idedata_resolves_toolchain_tools() -> None: + """The binutils paths are derived from the native ESP-IDF cc_path. + + Without cc_path, IDEData.objdump_path raises KeyError and the memory + analysis silently degrades to no component or symbol detail. + """ + idedata = IDEData( + { + "cc_path": _cc_path_from_cxx("/tools/bin/xtensa-esp32-elf-g++"), + "cxx_path": "/tools/bin/xtensa-esp32-elf-g++", + } + ) + + assert idedata.objdump_path == "/tools/bin/xtensa-esp32-elf-objdump" + assert idedata.readelf_path == "/tools/bin/xtensa-esp32-elf-readelf" diff --git a/tests/unit_tests/analyze_memory/test_ci_memory_impact_extract.py b/tests/unit_tests/analyze_memory/test_ci_memory_impact_extract.py new file mode 100644 index 0000000000..73a1c63e1a --- /dev/null +++ b/tests/unit_tests/analyze_memory/test_ci_memory_impact_extract.py @@ -0,0 +1,57 @@ +"""Tests for script/ci_memory_impact_extract.py.""" + +import io +from pathlib import Path +import sys + +import pytest + +# Add script directory to path so we can import the module +sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "script")) + +from ci_memory_impact_extract import main # noqa: E402 + +_COMPILE_OUTPUT = ( + "RAM: [==== ] 36.1% (used 29548 bytes from 81920 bytes)\n" + "Flash: [=== ] 34.0% (used 348511 bytes from 1023984 bytes)\n" +) + + +@pytest.fixture(autouse=True) +def _no_github_output(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.delenv("GITHUB_OUTPUT", raising=False) + + +def _run(monkeypatch: pytest.MonkeyPatch, compile_output: str, argv: list[str]) -> int: + monkeypatch.setattr(sys, "stdin", io.StringIO(compile_output)) + monkeypatch.setattr(sys, "argv", ["ci_memory_impact_extract.py", *argv]) + return main() + + +def test_missing_detailed_analysis_fails( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """A build with no usable ELF fails instead of posting a comment without detail.""" + build_dir = tmp_path / ".esphome" / "build" / "mydevice" + build_dir.mkdir(parents=True) + out_json = tmp_path / "analysis.json" + + rc = _run( + monkeypatch, + _COMPILE_OUTPUT, + ["--build-dir", str(build_dir), "--output-json", str(out_json)], + ) + + assert rc == 1 + # The totals are still written so the failure can be diagnosed from the artifact + assert out_json.is_file() + + +def test_undetected_build_dir_fails(monkeypatch: pytest.MonkeyPatch) -> None: + """Compile output without a build path cannot be analyzed, so it fails.""" + assert _run(monkeypatch, _COMPILE_OUTPUT, []) == 1 + + +def test_unparseable_output_fails(monkeypatch: pytest.MonkeyPatch) -> None: + """Output with no memory totals at all is still a failure.""" + assert _run(monkeypatch, "nothing useful here\n", []) == 1 diff --git a/tests/unit_tests/build_gen/test_espidf.py b/tests/unit_tests/build_gen/test_espidf.py index bcd9fa655a..f21549b48c 100644 --- a/tests/unit_tests/build_gen/test_espidf.py +++ b/tests/unit_tests/build_gen/test_espidf.py @@ -184,6 +184,18 @@ def test_get_component_cmakelists_compile_flags_excluded_from_link_opts() -> Non assert "-Wl,--gc-sections" in content +def test_get_component_cmakelists_globs_alternate_cpp_extensions() -> None: + """Both app_sources glob variants include .cc/.cxx/.c++ so vendored sources + are compiled, matching the extensions PlatformIO's builder globs by default.""" + CORE.build_flags = set() + from esphome.build_gen.espidf import get_component_cmakelists + + content = get_component_cmakelists() + for ext in ("cc", "cxx", "c++"): + assert content.count(f'"${{CMAKE_CURRENT_SOURCE_DIR}}/*.{ext}"') == 2 + assert content.count(f'"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.{ext}"') == 2 + + def test_get_project_cmakelists_emits_managed_components_property( tmp_path: Path, ) -> None: diff --git a/tests/unit_tests/components/api/test_client.py b/tests/unit_tests/components/api/test_client.py index 333ef70b22..4ebcecbfff 100644 --- a/tests/unit_tests/components/api/test_client.py +++ b/tests/unit_tests/components/api/test_client.py @@ -2,21 +2,22 @@ from __future__ import annotations -from unittest.mock import patch +from unittest.mock import AsyncMock, patch + +import pytest from esphome.components import esp32 from esphome.components.api import client as api_client -from esphome.core import EsphomeError +from esphome.const import CONF_PORT, KEY_CORE, KEY_TARGET_PLATFORM +from esphome.core import CORE, EsphomeError def test_decoder_swallows_esphome_error() -> None: """A failing stack-trace decode must not propagate. - on_log runs inside an asyncio protocol callback; if EsphomeError - escapes, the loop reports "Fatal error: protocol.data_received() - call failed.", tears the connection down, and ReconnectLogic loops - forever as the device replays the same crash trace on every - reconnect. + aioesphomeapi isolates exceptions raised by log handlers, so an + escaping one logs a full traceback for every line it fires on rather + than being reported once as an unavailable decoder. """ config = {"esphome": {"name": "test"}} @@ -43,6 +44,32 @@ def test_decoder_swallows_platform_handler_error() -> None: assert processor.backtrace_state is False +def test_decoder_swallows_non_esphome_error() -> None: + """Decoding failures that aren't EsphomeError must be contained too. + + A missing build directory surfaces as FileNotFoundError from the toolchain + subprocess. aioesphomeapi isolates it, so the session survives, but it logs + a traceback for every PC/BT line and decoding is never disabled, which + buries the crash dump the user is trying to read. + """ + config = {"esphome": {"name": "test"}} + + with patch.object( + esp32, + "process_stacktrace", + side_effect=FileNotFoundError( + 2, "No such file or directory", "/build/ol/build" + ), + ) as mock_process: + processor = api_client._LogLineProcessor(config, esp32.process_stacktrace) + processor.process_line("PC: 0x4010496e") + processor.process_line("BT0: 0x4010496e") + + # Disabled after the first failure rather than retried per backtrace line. + assert mock_process.call_count == 1 + assert processor.backtrace_state is False + + def test_decoder_warning_uses_fallback_for_empty_error(caplog) -> None: """_run_idedata raises EsphomeError with no message; the warning must show a useful explanation rather than empty parens. @@ -61,7 +88,7 @@ def test_decoder_warning_uses_fallback_for_empty_error(caplog) -> None: def test_decoder_short_circuits_after_failure() -> None: """After one failure, subsequent lines must not retry the decoder. - _decode_pc shells out to PlatformIO; a crash dump can contain many + _decode_pc shells out to the toolchain; a crash dump can contain many PC/BT lines and retrying the failing subprocess for each one would stall log streaming. """ @@ -112,3 +139,30 @@ def test_decoder_uses_platform_handler_when_provided() -> None: assert calls == [(config, "BT0: 0x4010496e", False)] assert mock_generic.called is False assert processor.backtrace_state is True + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("extra_config", "expected_deep_sleep"), + [({"deep_sleep": {}}, True), ({}, False)], +) +async def test_async_run_logs_passes_deep_sleep( + extra_config: dict, expected_deep_sleep: bool +) -> None: + """async_run_logs tells async_run whether the device deep sleeps, from the config.""" + CORE.data[KEY_CORE] = {KEY_TARGET_PLATFORM: "esp32"} + config = {"esphome": {"name": "test"}, "api": {CONF_PORT: 6053}, **extra_config} + # async_run blocks forever after connecting; raise to unwind async_run_logs + # once we have captured how it was called. + sentinel = RuntimeError("stop the wait") + + with ( + patch.object( + api_client, "async_run", AsyncMock(side_effect=sentinel) + ) as mock_run, + patch.object(api_client, "APIClient"), + pytest.raises(RuntimeError, match="stop the wait"), + ): + await api_client.async_run_logs(config, ["1.2.3.4"]) + + assert mock_run.call_args.kwargs["deep_sleep"] is expected_deep_sleep diff --git a/tests/unit_tests/components/micro_wake_word/__init__.py b/tests/unit_tests/components/micro_wake_word/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit_tests/components/micro_wake_word/test_init.py b/tests/unit_tests/components/micro_wake_word/test_init.py new file mode 100644 index 0000000000..84371ab906 --- /dev/null +++ b/tests/unit_tests/components/micro_wake_word/test_init.py @@ -0,0 +1,169 @@ +"""Tests for the micro_wake_word model source validation and downloads.""" + +import json +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from esphome.components import micro_wake_word as mww +import esphome.config_validation as cv +from esphome.const import ( + CONF_FILE, + CONF_MODEL, + CONF_PATH, + CONF_REF, + CONF_TYPE, + CONF_URL, +) + + +@pytest.fixture +def mock_download_content_many() -> MagicMock: + """Patch the concurrent download helper so no network is involved.""" + with patch( + "esphome.components.micro_wake_word.external_files.download_content_many" + ) as m: + yield m + + +def test_shorthand_model_name_resolves_without_network( + mock_download_content_many: MagicMock, +) -> None: + config = mww._validate_source_shorthand("okay_nabu") + assert config[CONF_TYPE] == mww.TYPE_HTTP + assert config[CONF_URL] == ( + "https://github.com/esphome/micro-wake-word-models/raw/main/models/v2/okay_nabu.json" + ) + mock_download_content_many.assert_not_called() + + +def test_shorthand_git_with_ref_not_captured_as_model_name( + setup_core: Path, tmp_path: Path +) -> None: + repo_dir = tmp_path / "repo" + repo_dir.mkdir() + (repo_dir / "model.json").write_text("{}") + with patch( + "esphome.components.micro_wake_word.git.clone_or_update", + return_value=(repo_dir, None), + ): + config = mww._validate_source_shorthand("github://user/repo/model.json@main") + assert config[CONF_TYPE] == "git" + assert config[CONF_URL] == "https://github.com/user/repo.git" + assert config[CONF_FILE] == "model.json" + assert config[CONF_REF] == "main" + + +def test_shorthand_local_path_not_captured_as_model_name( + setup_core: Path, tmp_path: Path +) -> None: + manifest = tmp_path / "model.json" + manifest.write_text("{}") + config = mww.MODEL_SOURCE_SCHEMA(str(manifest)) + assert config[CONF_TYPE] == "local" + assert Path(config[CONF_PATH]) == manifest + + +@pytest.mark.parametrize( + "value", ["some/path/file", "name@ref", "bad:name", "okay_nabu\n", "héllo"] +) +def test_model_name_rejects_non_identifiers(value: str) -> None: + with pytest.raises(cv.Invalid): + mww._validate_source_model_name(value) + + +def _http_model(name: str) -> dict: + return { + CONF_MODEL: { + CONF_TYPE: mww.TYPE_HTTP, + CONF_URL: f"https://example.com/models/{name}.json", + } + } + + +def _write_manifest(model_config: dict, contents: str) -> Path: + path = mww._compute_local_file_path(model_config[CONF_MODEL]) + path.mkdir(parents=True, exist_ok=True) + manifest = path / "manifest.json" + manifest.write_text(contents) + return path + + +def test_download_http_models_batches_manifests_then_models( + setup_core: Path, mock_download_content_many: MagicMock +) -> None: + names = ("okay_nabu", "hey_mycroft", "vad") + models = {name: _http_model(name) for name in names} + paths = { + name: _write_manifest(models[name], json.dumps({"model": f"{name}.tflite"})) + for name in names + } + config = { + mww.CONF_MODELS: [ + models["okay_nabu"], + models["hey_mycroft"], + # non-http sources must be ignored + {CONF_MODEL: {CONF_TYPE: "local", CONF_PATH: "x"}}, + ], + mww.CONF_VAD: models["vad"], + } + + assert mww._download_http_models(config) is config + + assert mock_download_content_many.call_count == 2 + manifest_items = list(mock_download_content_many.call_args_list[0].args[0]) + assert manifest_items == [ + (f"https://example.com/models/{name}.json", paths[name] / "manifest.json") + for name in names + ] + model_items = list(mock_download_content_many.call_args_list[1].args[0]) + assert model_items == [ + (f"https://example.com/models/{name}.tflite", paths[name] / f"{name}.tflite") + for name in names + ] + + +def test_download_http_models_no_http_sources_skips_download( + mock_download_content_many: MagicMock, +) -> None: + config = {mww.CONF_MODELS: [{CONF_MODEL: {CONF_TYPE: "local", CONF_PATH: "x"}}]} + assert mww._download_http_models(config) is config + mock_download_content_many.assert_not_called() + + +@pytest.mark.parametrize( + ("contents", "message"), + [ + ("not json", "Invalid manifest file"), + ("[1, 2]", "must contain a JSON object"), + ("{}", "missing the 'model' key"), + ], +) +def test_download_http_models_bad_manifest_raises( + setup_core: Path, + mock_download_content_many: MagicMock, + contents: str, + message: str, +) -> None: + model = _http_model("okay_nabu") + config = {mww.CONF_MODELS: [model]} + _write_manifest(model, contents) + + with pytest.raises(cv.Invalid, match=message): + mww._download_http_models(config) + # manifests were still fetched in one batch; the model batch never ran + assert mock_download_content_many.call_count == 1 + + +def test_download_http_models_collects_all_manifest_errors( + setup_core: Path, mock_download_content_many: MagicMock +) -> None: + models = {name: _http_model(name) for name in ("one", "two")} + config = {mww.CONF_MODELS: list(models.values())} + _write_manifest(models["one"], "not json") + _write_manifest(models["two"], "[1]") + + with pytest.raises(cv.MultipleInvalid) as excinfo: + mww._download_http_models(config) + assert len(excinfo.value.errors) == 2 diff --git a/tests/unit_tests/components/test_esp8266.py b/tests/unit_tests/components/test_esp8266.py index 318fd2d889..fb0e437d24 100644 --- a/tests/unit_tests/components/test_esp8266.py +++ b/tests/unit_tests/components/test_esp8266.py @@ -1,9 +1,15 @@ """Tests for ESP8266 component.""" +from __future__ import annotations + +from collections.abc import Generator +from unittest.mock import MagicMock, patch + import pytest -from esphome.components.esp8266 import lambdas_use_scanf_float -from esphome.core import Lambda +from esphome.components import esp8266 +from esphome.components.esp8266 import check_rosetta, lambdas_use_scanf_float +from esphome.core import EsphomeError, Lambda from esphome.types import ConfigType @@ -60,3 +66,54 @@ def test_lambdas_use_scanf_float_nested() -> None: """Test detection in deeply nested config.""" config: ConfigType = {"a": {"b": {"c": [Lambda('sscanf(buf, "%f", &v)')]}}} assert lambdas_use_scanf_float(config) is True + + +@pytest.fixture +def apple_silicon_run(monkeypatch: pytest.MonkeyPatch) -> Generator[MagicMock]: + """Simulate an Apple Silicon Mac and yield the mocked subprocess.run.""" + monkeypatch.setattr(esp8266, "IS_MACOS", True) + with ( + patch("esphome.components.esp8266.platform.machine", return_value="arm64"), + patch("esphome.components.esp8266.subprocess.run") as mock_run, + ): + yield mock_run + + +@pytest.mark.parametrize( + ("is_macos", "machine"), + [ + (False, "arm64"), + (True, "x86_64"), + ], +) +def test_check_rosetta_skips_other_systems( + monkeypatch: pytest.MonkeyPatch, is_macos: bool, machine: str +) -> None: + """The check only probes on Apple Silicon Macs.""" + monkeypatch.setattr(esp8266, "IS_MACOS", is_macos) + with ( + patch("esphome.components.esp8266.platform.machine", return_value=machine), + patch("esphome.components.esp8266.subprocess.run") as mock_run, + ): + check_rosetta() + mock_run.assert_not_called() + + +def test_check_rosetta_installed(apple_silicon_run: MagicMock) -> None: + """No error when the x86_64 probe succeeds (Rosetta present).""" + apple_silicon_run.return_value = MagicMock(returncode=0) + check_rosetta() + apple_silicon_run.assert_called_once() + + +def test_check_rosetta_missing(apple_silicon_run: MagicMock) -> None: + """A failing x86_64 probe raises an actionable error.""" + apple_silicon_run.return_value = MagicMock(returncode=1) + with pytest.raises(EsphomeError, match="softwareupdate --install-rosetta"): + check_rosetta() + + +def test_check_rosetta_arch_unavailable(apple_silicon_run: MagicMock) -> None: + """The build proceeds when arch(1) cannot be executed.""" + apple_silicon_run.side_effect = OSError("no such file") + check_rosetta() diff --git a/tests/unit_tests/components/test_esp_stacktrace.py b/tests/unit_tests/components/test_esp_stacktrace.py index f231ac5fb7..eb7e63fc4d 100644 --- a/tests/unit_tests/components/test_esp_stacktrace.py +++ b/tests/unit_tests/components/test_esp_stacktrace.py @@ -137,3 +137,45 @@ def test_process_stacktrace_esp32_crash_handler( state = process_stacktrace(config, line_bt1, False) mock_esp32_decode_pc.assert_called_once_with(config, "42005ABC") assert state is False + + mock_esp32_decode_pc.reset_mock() + + # Reason line carries no address, must not trigger a decode + line_reason = "[E][esp32.crash:079]: Reason: Fault - LoadProhibited (cause 28)" + state = process_stacktrace(config, line_reason, False) + mock_esp32_decode_pc.assert_not_called() + assert state is False + + mock_esp32_decode_pc.reset_mock() + + # EXCVADDR pointing at code (e.g. jumping through a corrupted pointer) decodes + line_excvaddr = "[E][esp32.crash:081]: EXCVADDR: 0x400D9ABC (faulting address)" + state = process_stacktrace(config, line_excvaddr, False) + mock_esp32_decode_pc.assert_called_once_with(config, "400D9ABC") + assert state is False + + mock_esp32_decode_pc.reset_mock() + + # EXCVADDR pointing at data (heap/null) is not a code address, must be ignored + line_excvaddr_data = ( + "[E][esp32.crash:081]: EXCVADDR: 0x0000001C (faulting address)" + ) + state = process_stacktrace(config, line_excvaddr_data, False) + mock_esp32_decode_pc.assert_not_called() + assert state is False + + mock_esp32_decode_pc.reset_mock() + + # RISC-V MTVAL pointing at code decodes + line_mtval = "[E][esp32.crash:081]: MTVAL: 0x42001234 (faulting address)" + state = process_stacktrace(config, line_mtval, False) + mock_esp32_decode_pc.assert_called_once_with(config, "42001234") + assert state is False + + mock_esp32_decode_pc.reset_mock() + + # RISC-V MTVAL pointing at data must be ignored + line_mtval_data = "[E][esp32.crash:081]: MTVAL: 0x3FC80123 (faulting address)" + state = process_stacktrace(config, line_mtval_data, False) + mock_esp32_decode_pc.assert_not_called() + assert state is False diff --git a/tests/unit_tests/fixtures/idf_tools_stub/idf_tools.py b/tests/unit_tests/fixtures/idf_tools_stub/idf_tools.py new file mode 100644 index 0000000000..aeff24fb57 --- /dev/null +++ b/tests/unit_tests/fixtures/idf_tools_stub/idf_tools.py @@ -0,0 +1,120 @@ +"""Minimal idf_tools stand-in for get_tool_downloads.py tests.""" + +from collections.abc import Iterable +import os + +CURRENT_PLATFORM = "linux-amd64" +TOOLS_FILE = "tools/tools.json" + + +class ToolBinaryError(RuntimeError): + pass + + +class _G: + idf_path: str | None = None + idf_tools_path: str | None = None + tools_json: str | None = None + + +g = _G() + + +class IDFEnv: + @classmethod + def get_idf_env(cls) -> "IDFEnv": + return cls() + + +def add_and_check_targets(idf_env_obj: IDFEnv, targets_str: str) -> list[str]: + return targets_str.split(",") + + +class _Download: + def __init__(self, url: str, size: int, sha256: str, rename_dist: str = "") -> None: + self.url = url + self.size = size + self.sha256 = sha256 + self.rename_dist = rename_dist + + +class _Version: + def __init__(self, download: _Download | None) -> None: + self._download = download + + def get_download_for_platform(self, platform_name: str) -> _Download | None: + return self._download + + +class _Tool: + def __init__( + self, + versions: dict[str, _Version], + recommended: str | None, + installed: Iterable[str] = (), + broken: bool = False, + ) -> None: + self.versions = versions + self._recommended = recommended + self.versions_installed = list(installed) + self._broken = broken + + def compatible_with_platform(self) -> bool: + return True + + def get_recommended_version(self) -> str | None: + return self._recommended + + def find_installed_versions(self) -> None: + if self._broken: + raise ToolBinaryError("broken binary") + + +_TOOLS = { + "cmake": _Tool( + {"3.30.2": _Version(_Download("https://gh.test/cmake.tar.gz", 11, "aa"))}, + "3.30.2", + ), + "ninja": _Tool( + { + "1.12.1": _Version( + _Download("https://gh.test/ninja-mac.zip", 22, "bb", "ninja-v1.zip") + ) + }, + "1.12.1", + ), + "installed-tool": _Tool( + {"1.0": _Version(_Download("https://gh.test/x.tar.gz", 33, "cc"))}, + "1.0", + installed=["1.0"], + ), + "broken-tool": _Tool( + {"2.0": _Version(_Download("https://gh.test/y.tar.gz", 44, "dd"))}, + "2.0", + broken=True, + ), + "no-recommended-tool": _Tool({"3.0": _Version(None)}, None), + "no-download-tool": _Tool({"4.0": _Version(None)}, "4.0"), +} + + +def load_tools_info() -> dict[str, _Tool]: + return _TOOLS + + +def expand_tools_arg( + tools_spec: list[str], overall_tools: dict[str, _Tool], targets: list[str] +) -> list[str]: + if "required" in tools_spec: + return list(overall_tools) + return [t for t in tools_spec if "@" not in t] + [t for t in tools_spec if "@" in t] + + +def get_idf_download_url_apply_mirrors( + args: object = None, download_url: str = "" +) -> str: + print(f"Changed download URL: {download_url}") # noise on stdout, like idf_tools + prefix = os.environ.get("TEST_MIRROR_PREFIX") + if prefix: + return prefix + download_url + return download_url diff --git a/tests/unit_tests/test_bundle.py b/tests/unit_tests/test_bundle.py index f15bbf2e29..f0abcc74c6 100644 --- a/tests/unit_tests/test_bundle.py +++ b/tests/unit_tests/test_bundle.py @@ -22,10 +22,12 @@ from esphome.bundle import ( _add_bytes_to_tar, _default_target_dir, _find_used_secret_keys, + add_bundle_file, extract_bundle, is_bundle_path, prepare_bundle_for_compile, read_bundle_manifest, + remap_bundle_path, ) from esphome.core import CORE, EsphomeError from esphome.yaml_util import force_load_include_files @@ -477,7 +479,10 @@ def test_read_bundle_manifest_corrupted_tar(tmp_path: Path) -> None: def test_read_bundle_manifest(tmp_path: Path) -> None: bundle_path = _make_bundle( tmp_path, - manifest_overrides={ManifestKey.HAS_SECRETS: True}, + manifest_overrides={ + ManifestKey.HAS_SECRETS: True, + ManifestKey.CONFIG_DIR: "/original/config", + }, extra_files={"secrets.yaml": b"wifi: test\n"}, ) @@ -488,6 +493,7 @@ def test_read_bundle_manifest(tmp_path: Path) -> None: assert manifest.esphome_version == "2026.2.0-test" assert manifest.config_filename == "test.yaml" assert manifest.has_secrets is True + assert manifest.config_dir == "/original/config" def test_read_bundle_manifest_minimal(tmp_path: Path) -> None: @@ -507,6 +513,266 @@ def test_read_bundle_manifest_minimal(tmp_path: Path) -> None: assert result.esphome_version == "unknown" assert not result.files assert result.has_secrets is False + assert result.config_dir is None + + +def test_read_bundle_manifest_non_string_config_dir(tmp_path: Path) -> None: + """A malformed config_dir value is dropped rather than propagated.""" + bundle_path = _make_bundle( + tmp_path, manifest_overrides={ManifestKey.CONFIG_DIR: 42} + ) + + assert read_bundle_manifest(bundle_path).config_dir is None + + +# --------------------------------------------------------------------------- +# remap_bundle_path +# --------------------------------------------------------------------------- + + +ORIGINAL_CONFIG_DIR = "/original/config" + + +def _bundle_manifest_dict(**overrides: Any) -> dict[str, Any]: + """Manifest content an extracted bundle would contain.""" + manifest: dict[str, Any] = { + ManifestKey.MANIFEST_VERSION: CURRENT_MANIFEST_VERSION, + ManifestKey.CONFIG_FILENAME: "test.yaml", + ManifestKey.CONFIG_DIR: ORIGINAL_CONFIG_DIR, + } + manifest.update(overrides) + return manifest + + +def _setup_extracted_dir( + tmp_path: Path, + manifest: dict[str, Any] | str | None, + files: dict[str, str] | None = None, +) -> Path: + """Create a directory shaped like an extracted bundle and point CORE at it.""" + extract_dir = _setup_config_dir(tmp_path, files) + if manifest is not None: + content = manifest if isinstance(manifest, str) else json.dumps(manifest) + (extract_dir / MANIFEST_FILENAME).write_text(content) + return extract_dir + + +def test_remap_bundle_path_success(tmp_path: Path) -> None: + """A stale absolute path resolves to the bundled copy next to the config.""" + extract_dir = _setup_extracted_dir( + tmp_path, _bundle_manifest_dict(), files={"boards/partitions.csv": "csv\n"} + ) + + remapped = remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/boards/partitions.csv") + + assert remapped == extract_dir / "boards" / "partitions.csv" + assert remapped.is_file() + + +@pytest.mark.parametrize( + "value", + [ + pytest.param(r"C:\Users\nick\esphome\boards\partitions.csv", id="backslashes"), + pytest.param("C:/Users/nick/esphome/boards/partitions.csv", id="forward"), + pytest.param(r"c:\users\NICK\esphome\boards\partitions.csv", id="case"), + ], +) +def test_remap_bundle_path_windows_bundle_on_posix(tmp_path: Path, value: str) -> None: + """A bundle created on Windows remaps on a build server with another layout.""" + extract_dir = _setup_extracted_dir( + tmp_path, + _bundle_manifest_dict(**{ManifestKey.CONFIG_DIR: r"C:\Users\nick\esphome"}), + files={"boards/partitions.csv": "csv\n"}, + ) + + remapped = remap_bundle_path(value) + + assert remapped == extract_dir / "boards" / "partitions.csv" + assert remapped.is_file() + + +def test_remap_bundle_path_windows_bundle_path_not_under_config_dir( + tmp_path: Path, +) -> None: + """A Windows path outside the original config dir is left alone.""" + _setup_extracted_dir( + tmp_path, + _bundle_manifest_dict(**{ManifestKey.CONFIG_DIR: r"C:\Users\nick\esphome"}), + files={"partitions.csv": "csv\n"}, + ) + + assert remap_bundle_path(r"D:\other\partitions.csv") is None + + +def test_remap_bundle_path_windows_profile_with_spaces(tmp_path: Path) -> None: + r"""A Windows profile like C:\Users\First Last remaps like any other dir.""" + extract_dir = _setup_extracted_dir( + tmp_path, + _bundle_manifest_dict( + **{ManifestKey.CONFIG_DIR: r"C:\Users\First Last\esphome"} + ), + files={"boards/my partitions.csv": "csv\n"}, + ) + + remapped = remap_bundle_path( + r"C:\Users\First Last\esphome\boards\my partitions.csv" + ) + + assert remapped == extract_dir / "boards" / "my partitions.csv" + assert remapped.is_file() + + +def test_remap_bundle_path_unc_config_dir(tmp_path: Path) -> None: + """A bundle created from a UNC share remaps like any other Windows path.""" + extract_dir = _setup_extracted_dir( + tmp_path, + _bundle_manifest_dict(**{ManifestKey.CONFIG_DIR: r"\\server\share\esphome"}), + files={"partitions.csv": "csv\n"}, + ) + + remapped = remap_bundle_path(r"\\server\share\esphome\partitions.csv") + + assert remapped == extract_dir / "partitions.csv" + + +def test_remap_bundle_path_flavor_mismatch(tmp_path: Path) -> None: + """A POSIX style value cannot come from a Windows config dir; no remap.""" + _setup_extracted_dir( + tmp_path, + _bundle_manifest_dict(**{ManifestKey.CONFIG_DIR: r"C:\Users\nick\esphome"}), + files={"partitions.csv": "csv\n"}, + ) + + assert remap_bundle_path("/original/config/partitions.csv") is None + + +def test_remap_bundle_path_rejects_traversal(tmp_path: Path) -> None: + """A remap may never escape the extracted config tree.""" + extract_dir = _setup_extracted_dir(tmp_path, _bundle_manifest_dict()) + (tmp_path / "outside.csv").write_text("csv\n") + assert (extract_dir / ".." / "outside.csv").resolve().is_file() + + assert remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/../outside.csv") is None + + +def test_remap_bundle_path_relative_value(tmp_path: Path) -> None: + """Relative references resolve normally and are never remapped.""" + _setup_extracted_dir(tmp_path, _bundle_manifest_dict()) + + assert remap_bundle_path("missing.csv") is None + + +def test_remap_bundle_path_no_manifest(tmp_path: Path) -> None: + """A config dir without a manifest is not an extracted bundle.""" + _setup_extracted_dir(tmp_path, None, files={"partitions.csv": "csv\n"}) + + assert remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") is None + + +@pytest.mark.parametrize( + "manifest", + [ + pytest.param("{not json", id="malformed_json"), + pytest.param("[]", id="not_a_dict"), + pytest.param( + _bundle_manifest_dict(**{ManifestKey.MANIFEST_VERSION: "x"}), + id="version_not_int", + ), + pytest.param( + _bundle_manifest_dict(**{ManifestKey.MANIFEST_VERSION: 0}), + id="version_zero", + ), + pytest.param( + _bundle_manifest_dict(**{ManifestKey.CONFIG_FILENAME: "other.yaml"}), + id="config_filename_mismatch", + ), + pytest.param( + { + ManifestKey.MANIFEST_VERSION: CURRENT_MANIFEST_VERSION, + ManifestKey.CONFIG_FILENAME: "test.yaml", + }, + id="config_dir_missing", + ), + pytest.param( + _bundle_manifest_dict(**{ManifestKey.CONFIG_DIR: ""}), + id="config_dir_empty", + ), + ], +) +def test_remap_bundle_path_untrusted_manifest( + tmp_path: Path, manifest: dict[str, Any] | str +) -> None: + """Manifests that do not look like this bundle's manifest are ignored.""" + _setup_extracted_dir(tmp_path, manifest, files={"partitions.csv": "csv\n"}) + + assert remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") is None + + +def test_remap_bundle_path_unreadable_manifest_warns( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A present but broken manifest is reported, not silently ignored.""" + _setup_extracted_dir(tmp_path, "{not json", files={"partitions.csv": "csv\n"}) + + assert remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") is None + assert "ignoring unreadable" in caplog.text + + +def test_remap_bundle_path_outside_original_config_dir(tmp_path: Path) -> None: + """Paths that were not under the original config dir are left alone.""" + _setup_extracted_dir(tmp_path, _bundle_manifest_dict()) + + assert remap_bundle_path("/elsewhere/partitions.csv") is None + + +def test_remap_bundle_path_bundled_copy_missing(tmp_path: Path) -> None: + """No remap when the bundle does not contain the file.""" + _setup_extracted_dir(tmp_path, _bundle_manifest_dict()) + + assert remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") is None + + +def test_remap_bundle_path_manifest_read_once(tmp_path: Path) -> None: + """The manifest lookup result is cached for the rest of the run.""" + extract_dir = _setup_extracted_dir( + tmp_path, _bundle_manifest_dict(), files={"partitions.csv": "csv\n"} + ) + + first = remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") + assert first == extract_dir / "partitions.csv" + + (extract_dir / MANIFEST_FILENAME).unlink() + second = remap_bundle_path(f"{ORIGINAL_CONFIG_DIR}/partitions.csv") + assert second == first + + +def test_remap_bundle_path_round_trip(tmp_path: Path) -> None: + """A file referenced by absolute path survives bundle create and extract. + + Reproduces https://github.com/esphome/esphome/issues/17755: the config + names its partitions csv by absolute path, the bundle is extracted on a + machine where that path does not exist, and the reference must resolve + to the bundled copy. + """ + config_dir = _setup_config_dir(tmp_path, files={"partitions.csv": "nvs,data\n"}) + abs_path = (config_dir / "partitions.csv").resolve() + + creator = ConfigBundleCreator({"esp32": {"partitions": abs_path}}) + result = creator.create_bundle() + + bundle_path = tmp_path / f"device{BUNDLE_EXTENSION}" + bundle_path.write_bytes(result.data) + target = tmp_path / "build_server" + config_path = extract_bundle(bundle_path, target) + + # Simulate the build server: fresh run, original config dir gone + CORE.reset() + CORE.config_path = config_path + shutil.rmtree(config_dir) + + remapped = remap_bundle_path(str(abs_path)) + assert remapped == target.resolve() / "partitions.csv" + assert remapped.is_file() # --------------------------------------------------------------------------- @@ -611,6 +877,70 @@ def test_discover_files_includes_config(tmp_path: Path) -> None: assert "test.yaml" in paths +def test_discover_files_includes_registered_files(tmp_path: Path) -> None: + """Files registered with add_bundle_file() are included. + + The config does not name them, so discovery cannot find them on its own. + """ + config_dir = _setup_config_dir( + tmp_path, + files={"models/model.tflite": "fake model data"}, + ) + add_bundle_file(config_dir / "models" / "model.tflite") + + creator = ConfigBundleCreator({}) + files = creator.discover_files() + + paths = [f.path for f in files] + assert "models/model.tflite" in paths + + +def test_discover_files_registered_relative_file(tmp_path: Path) -> None: + """A relative registered path is taken as relative to the config directory. + + Not the working directory, which is where Path.resolve() would put it. + """ + _setup_config_dir( + tmp_path, + files={"models/model.tflite": "fake model data"}, + ) + add_bundle_file(Path("models/model.tflite")) + + creator = ConfigBundleCreator({}) + files = creator.discover_files() + + paths = [f.path for f in files] + assert "models/model.tflite" in paths + + +def test_discover_files_registered_file_outside_config_dir(tmp_path: Path) -> None: + """A registered file outside the config directory is skipped, not bundled.""" + _setup_config_dir(tmp_path) + outside = tmp_path / "outside.tflite" + outside.write_text("fake model data") + add_bundle_file(outside) + + creator = ConfigBundleCreator({}) + files = creator.discover_files() + + assert [f.path for f in files] == ["test.yaml"] + + +def test_discover_files_registered_file_deduplicated(tmp_path: Path) -> None: + """Registering the same file twice adds it once.""" + config_dir = _setup_config_dir( + tmp_path, + files={"models/model.tflite": "fake model data"}, + ) + add_bundle_file(config_dir / "models" / "model.tflite") + add_bundle_file(config_dir / "models" / "model.tflite") + + creator = ConfigBundleCreator({}) + files = creator.discover_files() + + assert [f.path for f in files].count("models/model.tflite") == 1 + + def test_discover_files_finds_path_objects(tmp_path: Path) -> None: """Path objects in validated config are discovered.""" config_dir = _setup_config_dir( @@ -1196,7 +1526,7 @@ def test_create_bundle_produces_valid_archive(tmp_path: Path) -> None: def test_create_bundle_manifest_content(tmp_path: Path) -> None: - _setup_config_dir(tmp_path) + config_dir = _setup_config_dir(tmp_path) creator = ConfigBundleCreator({}) result = creator.create_bundle() @@ -1204,6 +1534,7 @@ def test_create_bundle_manifest_content(tmp_path: Path) -> None: manifest = result.manifest assert manifest[ManifestKey.MANIFEST_VERSION] == CURRENT_MANIFEST_VERSION assert manifest[ManifestKey.CONFIG_FILENAME] == "test.yaml" + assert manifest[ManifestKey.CONFIG_DIR] == str(config_dir.resolve()) assert "test.yaml" in manifest[ManifestKey.FILES] diff --git a/tests/unit_tests/test_compiled_config.py b/tests/unit_tests/test_compiled_config.py index e12107152b..e17271e2b4 100644 --- a/tests/unit_tests/test_compiled_config.py +++ b/tests/unit_tests/test_compiled_config.py @@ -74,13 +74,13 @@ def _write_storage( "framework": "arduino", "core_platform": core_platform, } - storage_path.write_text(json.dumps(data)) + storage_path.write_text(json.dumps(data), encoding="utf-8") def _write_cache(cache_path: Path, body: str = _VALIDATED_CONFIG_YAML) -> Path: """Write the cache file and return it.""" cache_path.parent.mkdir(parents=True, exist_ok=True) - cache_path.write_text(body) + cache_path.write_text(body, encoding="utf-8") return cache_path @@ -220,7 +220,7 @@ def test_run_esphome_upload_and_logs_use_cache_when_fresh( with ( caplog.at_level("INFO", logger="esphome.__main__"), - patch("esphome.__main__.read_config") as mock_read, + patch("esphome.config.read_config") as mock_read, patch.dict("esphome.__main__.POST_CONFIG_ACTIONS", {command: _stub}), ): assert run_esphome(["esphome", command, str(fresh_cache_files)]) == 0 @@ -242,7 +242,7 @@ def test_run_esphome_upload_and_logs_fall_back_when_no_cache( yaml_path.write_text("esphome:\n name: lite_test\n") with ( - patch("esphome.__main__.read_config", return_value=None) as mock_read, + patch("esphome.config.read_config", return_value=None) as mock_read, patch.dict( "esphome.__main__.POST_CONFIG_ACTIONS", {command: lambda args, config: 0}, @@ -266,7 +266,7 @@ def test_run_esphome_upload_does_not_refresh_cache_without_sidecar( with ( patch( - "esphome.__main__.read_config", + "esphome.config.read_config", return_value={"esphome": {"name": "lite_test"}}, ), patch("esphome.compiled_config.save_compiled_config") as mock_save, @@ -299,7 +299,7 @@ def test_run_esphome_upload_and_logs_refresh_cache_on_fallback( fresh_config = {"esphome": {"name": "lite_test"}, "logger": {}} with ( - patch("esphome.__main__.read_config", return_value=fresh_config), + patch("esphome.config.read_config", return_value=fresh_config), patch( "esphome.compiled_config.save_compiled_config", wraps=save_compiled_config ) as mock_save, @@ -322,7 +322,7 @@ def test_run_esphome_upload_with_substitution_does_not_refresh_cache( """`-s` substitutions skip the cache on both read and write -- saving here would clobber the cache with a substitution-specific config.""" with ( - patch("esphome.__main__.read_config", return_value={"esphome": {}}), + patch("esphome.config.read_config", return_value={"esphome": {}}), patch("esphome.compiled_config.save_compiled_config") as mock_save, patch.dict( "esphome.__main__.POST_CONFIG_ACTIONS", @@ -341,7 +341,7 @@ def test_run_esphome_compile_does_not_refresh_cache_via_fallback( upload/logs fallback path -- the fallback save would skip the storage_should_clean check.""" with ( - patch("esphome.__main__.read_config", return_value={"esphome": {}}), + patch("esphome.config.read_config", return_value={"esphome": {}}), patch("esphome.compiled_config.save_compiled_config") as mock_save, patch.dict( "esphome.__main__.POST_CONFIG_ACTIONS", @@ -360,7 +360,7 @@ def test_run_esphome_upload_with_substitution_skips_cache( against the prior substitution set, so reusing it would silently ignore the override.""" with ( - patch("esphome.__main__.read_config", return_value=None) as mock_read, + patch("esphome.config.read_config", return_value=None) as mock_read, patch.dict( "esphome.__main__.POST_CONFIG_ACTIONS", {"upload": lambda args, config: 0}, @@ -374,7 +374,7 @@ def test_run_esphome_upload_with_substitution_skips_cache( def test_run_esphome_compile_does_not_use_cache(fresh_cache_files: Path) -> None: """The compile subcommand always re-validates -- it's what writes the cache.""" with ( - patch("esphome.__main__.read_config", return_value=None) as mock_read, + patch("esphome.config.read_config", return_value=None) as mock_read, patch.dict( "esphome.__main__.POST_CONFIG_ACTIONS", {"compile": lambda args, config: 0}, diff --git a/tests/unit_tests/test_config_validation.py b/tests/unit_tests/test_config_validation.py index 17dfaad9b8..79bfc303b7 100644 --- a/tests/unit_tests/test_config_validation.py +++ b/tests/unit_tests/test_config_validation.py @@ -1,3 +1,5 @@ +import json +import logging from pathlib import Path import string @@ -1436,9 +1438,41 @@ def test_version_parse_with_extra() -> None: assert version.extra == "dev20240101" -def test_version_parse_invalid() -> None: +def test_version_parse_without_patch() -> None: + """A two-part version parses with patch defaulting to 0, so framework + shorthands like '6.0' and '6.0-rc1' are accepted.""" + version = cv.Version.parse("6.0") + assert (version.major, version.minor, version.patch, version.extra) == ( + 6, + 0, + 0, + "", + ) + version = cv.Version.parse("6.0-rc1") + assert (version.major, version.minor, version.patch, version.extra) == ( + 6, + 0, + 0, + "rc1", + ) + + +def test_version_parse_numeric_extra() -> None: + """Four-part versions keep the trailing component as extra (pioarduino + packaging revisions, e.g. 5.5.3.1).""" + version = cv.Version.parse("5.5.3.1") + assert (version.major, version.minor, version.patch, version.extra) == ( + 5, + 5, + 3, + "1", + ) + + +@pytest.mark.parametrize("value", ["not.a.version", "6", "a.b", ""]) +def test_version_parse_invalid(value: str) -> None: with pytest.raises(ValueError, match="Not a valid version number"): - cv.Version.parse("not.a.version") + cv.Version.parse(value) def test_version_is_beta() -> None: @@ -2394,6 +2428,58 @@ def test_one_of_string_and_space() -> None: assert cv.one_of("a_b", string=True, space="_")("a b") == "a_b" +def test_one_of_string_and_underscore() -> None: + assert cv.one_of("a-b", string=True, underscore="-")("a_b") == "a-b" + assert cv.one_of("a-b", string=True, underscore="-")("a-b") == "a-b" + + +def test_one_of_string_lower_space_and_underscore() -> None: + validator = cv.one_of("output-mode", lower=True, space="-", underscore="-") + assert validator("output_mode") == "output-mode" + assert validator("OUTPUT_MODE") == "output-mode" + assert validator("output mode") == "output-mode" + assert validator("output-mode") == "output-mode" + + +def test_one_of_string_underscore_unknown() -> None: + with pytest.raises(Invalid): + cv.one_of("a-b", string=True, underscore="-")("c_d") + + +def test_one_of_string_underscore_default_unchanged() -> None: + with pytest.raises(Invalid): + cv.one_of("a-b", string=True)("a_b") + + +def test_one_of_string_and_hyphen() -> None: + assert cv.one_of("a_b", string=True, hyphen="_")("a-b") == "a_b" + assert cv.one_of("a_b", string=True, hyphen="_")("a_b") == "a_b" + + +def test_one_of_string_lower_space_and_hyphen() -> None: + validator = cv.one_of("output_mode", lower=True, space="_", hyphen="_") + assert validator("output-mode") == "output_mode" + assert validator("OUTPUT-MODE") == "output_mode" + assert validator("output mode") == "output_mode" + assert validator("output_mode") == "output_mode" + + +def test_one_of_string_hyphen_unknown() -> None: + with pytest.raises(Invalid): + cv.one_of("a_b", string=True, hyphen="_")("c-d") + + +def test_one_of_string_hyphen_default_unchanged() -> None: + with pytest.raises(Invalid): + cv.one_of("a_b", string=True)("a-b") + + +def test_one_of_string_underscore_hyphen_swap_no_cascade() -> None: + validator = cv.one_of("a-b", "a_b", string=True, underscore="-", hyphen="_") + assert validator("a_b") == "a-b" + assert validator("a-b") == "a_b" + + def test_one_of_int() -> None: assert cv.one_of(1, 2, int=True)("2") == 2 @@ -2432,6 +2518,20 @@ def test_enum_valid() -> None: assert result.enum_value == 10 +def test_enum_valid_with_underscore() -> None: + mapping = {"a-b": 1} + result = cv.enum(mapping, string=True, underscore="-")("a_b") + assert result == "a-b" + assert result.enum_value == 1 + + +def test_enum_valid_with_hyphen() -> None: + mapping = {"a_b": 1} + result = cv.enum(mapping, string=True, hyphen="_")("a-b") + assert result == "a_b" + assert result.enum_value == 1 + + # --------------------------------------------------------------------------- # lambda_ / returning_lambda # --------------------------------------------------------------------------- @@ -2880,3 +2980,135 @@ def test_rename_key_present() -> None: def test_rename_key_absent() -> None: assert cv.rename_key("old", "new")({"other": 5}) == {"other": 5} + + +def test_rename_key_no_removed_in_is_silent( + caplog: pytest.LogCaptureFixture, +) -> None: + with caplog.at_level(logging.WARNING, logger="esphome.config_validation"): + assert cv.rename_key("old", "new")({"old": 5}) == {"new": 5} + assert not caplog.records + + +def test_rename_key_removed_in_renames_and_warns( + caplog: pytest.LogCaptureFixture, +) -> None: + with caplog.at_level(logging.WARNING, logger="esphome.config_validation"): + result = cv.rename_key("old", "new", removed_in="2026.8.0")({"old": 5}) + assert result == {"new": 5} + assert "'old' is deprecated, use 'new'. Will be removed in 2026.8.0" in caplog.text + + +def test_rename_key_removed_in_absent_key_no_warning( + caplog: pytest.LogCaptureFixture, +) -> None: + with caplog.at_level(logging.WARNING, logger="esphome.config_validation"): + result = cv.rename_key("old", "new", removed_in="2026.8.0")({"other": 5}) + assert result == {"other": 5} + assert not caplog.records + + +def test_rename_key_removed_in_with_component_prefixes_warning( + caplog: pytest.LogCaptureFixture, +) -> None: + with caplog.at_level(logging.WARNING, logger="esphome.config_validation"): + result = cv.rename_key( + "old", "new", removed_in="2026.8.0", component="my_component" + )({"old": 5}) + assert result == {"new": 5} + assert ( + "[my_component] 'old' is deprecated, use 'new'. Will be removed in 2026.8.0" + in caplog.text + ) + + +def test_rename_key_both_keys_rejected() -> None: + with pytest.raises(Invalid, match="Cannot specify more than one of"): + cv.rename_key("old", "new")({"old": 5, "new": 6}) + + +def test_rename_key_both_keys_rejected_with_removed_in( + caplog: pytest.LogCaptureFixture, +) -> None: + with ( + caplog.at_level(logging.WARNING, logger="esphome.config_validation"), + pytest.raises(Invalid, match="Cannot specify more than one of"), + ): + cv.rename_key("old", "new", removed_in="2026.8.0")({"old": 5, "new": 6}) + assert not caplog.records + + +def test_file__existing_relative_path(setup_core: Path) -> None: + (setup_core / "partitions.csv").write_text("csv\n") + + assert cv.file_("partitions.csv") == setup_core / "partitions.csv" + + +def test_file__missing_raises(setup_core: Path) -> None: + with pytest.raises(Invalid, match="Could not find file"): + cv.file_("partitions.csv") + + +def test_file__remaps_bundle_absolute_path(setup_core: Path) -> None: + """A stale absolute path in an extracted bundle resolves to the bundled copy.""" + manifest = { + "manifest_version": 1, + "config_filename": "test.yaml", + "config_dir": "/original/config", + } + (setup_core / "manifest.json").write_text(json.dumps(manifest)) + (setup_core / "partitions.csv").write_text("csv\n") + + assert cv.file_("/original/config/partitions.csv") == setup_core / "partitions.csv" + + +def test_file__missing_absolute_path_without_bundle(setup_core: Path) -> None: + with pytest.raises(Invalid, match="Could not find file"): + cv.file_("/original/config/partitions.csv") + + +def test_file__remaps_windows_bundle_absolute_path(setup_core: Path) -> None: + """A bundle created on Windows resolves on a host with another layout.""" + manifest = { + "manifest_version": 1, + "config_filename": "test.yaml", + "config_dir": "C:\\Users\\nick\\esphome", + } + (setup_core / "manifest.json").write_text(json.dumps(manifest)) + (setup_core / "partitions.csv").write_text("csv\n") + + result = cv.file_("C:\\Users\\nick\\esphome\\partitions.csv") + + assert result == setup_core / "partitions.csv" + + +def test_directory_remaps_bundle_absolute_path(setup_core: Path) -> None: + """A stale absolute directory in an extracted bundle resolves to the bundled copy.""" + manifest = { + "manifest_version": 1, + "config_filename": "test.yaml", + "config_dir": "/original/config", + } + (setup_core / "manifest.json").write_text(json.dumps(manifest)) + (setup_core / "headers").mkdir() + + assert cv.directory("/original/config/headers") == setup_core / "headers" + + +def test_directory_missing_raises(setup_core: Path) -> None: + with pytest.raises(Invalid, match="Could not find directory"): + cv.directory("/original/config/headers") + + +def test_file__remapped_path_is_directory_raises(setup_core: Path) -> None: + """A remapped path that is a directory still fails file validation.""" + manifest = { + "manifest_version": 1, + "config_filename": "test.yaml", + "config_dir": "/original/config", + } + (setup_core / "manifest.json").write_text(json.dumps(manifest)) + (setup_core / "headers").mkdir() + + with pytest.raises(Invalid, match="is not a file"): + cv.file_("/original/config/headers") diff --git a/tests/unit_tests/test_espidf_component.py b/tests/unit_tests/test_espidf_component.py index a50024b8e9..879d98c0a7 100644 --- a/tests/unit_tests/test_espidf_component.py +++ b/tests/unit_tests/test_espidf_component.py @@ -1,3 +1,4 @@ +import glob import hashlib import json import os @@ -86,6 +87,48 @@ def test_collect_filtered_files_exclude(tmp_path): assert str(f2) not in result +def test_collect_filtered_files_exclude_pattern_in_subdir(tmp_path): + src = tmp_path / "lib" / "src" + src.mkdir(parents=True) + kept = src / "a.c" + excluded = src / "hasty.c" + kept.write_text("int a;") + excluded.write_text("int b;") + + result = collect_filtered_files(tmp_path, ["+", "-"]) + assert str(kept) in result + assert str(excluded) not in result + + +def test_collect_filtered_files_exclude_unnormalized_glob_output(tmp_path, monkeypatch): + # On Windows, glob keeps the pattern's literal separators for non-wildcard + # path components, so the "+" wildcard pattern and the "-" literal pattern + # yield the same file spelled differently and the exclude set difference + # misses it. Backslash is a regular filename character on POSIX (such paths + # fail the final is_file filter), so reproduce the unnormalized-output + # mismatch portably with dot segments, which normpath also collapses. + src = tmp_path / "lib" / "src" + src.mkdir(parents=True) + kept = src / "a.c" + excluded = src / "hasty.c" + kept.write_text("int a;") + excluded.write_text("int b;") + + real_glob = glob.glob + + def unnormalized_glob(pattern, recursive=False): + if "*" in pattern: + base = str(tmp_path) + return [base + "/lib/./src/a.c", base + "/lib/./src/hasty.c"] + return real_glob(pattern, recursive=recursive) + + monkeypatch.setattr(glob, "glob", unnormalized_glob) + + result = collect_filtered_files(tmp_path, ["+", "-"]) + assert [Path(r).name for r in result] == ["a.c"] + assert str(kept) in result + + def test_split_list_by_condition(): items = ["-Iinclude", "-Llib", "-Wall"] @@ -126,30 +169,98 @@ def test_generate_cmakelists_txt_with_flags(tmp_component, tmp_path): } content = generate_cmakelists_txt(tmp_component) - sep = "\\\\" if os.name == "nt" else "/" + # Paths are always emitted with forward slashes so the CMakeLists is + # portable; on Windows os.path.relpath would otherwise yield backslashes + # that break CMake's list re-parsing. assert ( content - == f"""idf_component_register( - SRCS "src{sep}main.c" + == """idf_component_register( + SRCS "src/main.c" INCLUDE_DIRS "src" - REQUIRES dep ${{ESPHOME_PROJECT_MANAGED_COMPONENTS}} ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}} + REQUIRES dep ${ESPHOME_PROJECT_MANAGED_COMPONENTS} ${ESPHOME_PROJECT_BUILTIN_COMPONENTS} ) -target_compile_options(${{COMPONENT_LIB}} PUBLIC +target_compile_options(${COMPONENT_LIB} PUBLIC "-DTEST" ) -target_compile_options(${{COMPONENT_LIB}} PRIVATE +target_compile_options(${COMPONENT_LIB} PRIVATE "-Wall" ) -target_link_directories(${{COMPONENT_LIB}} INTERFACE +target_link_directories(${COMPONENT_LIB} INTERFACE "lib" ) -target_link_libraries(${{COMPONENT_LIB}} INTERFACE +target_link_libraries(${COMPONENT_LIB} INTERFACE "mylib" ) """ ) +def test_generate_cmakelists_txt_uses_forward_slashes_on_windows( + tmp_component, monkeypatch: pytest.MonkeyPatch +) -> None: + # os.path.relpath yields backslash paths on Windows, which CMake rejects + # when it re-parses the SRCS list (e.g. "\b" in "src\backend" is an invalid + # character escape). Simulate that output and confirm the generated + # CMakeLists normalizes the separators to forward slashes. + src_dir = tmp_component.path / "src" / "backend" + src_dir.mkdir(parents=True) + (src_dir / "cipher.c").write_text("int f() {}") + + tmp_component.data = {} + + monkeypatch.setattr("esphome.espidf.component.os.sep", "\\") + monkeypatch.setattr( + "esphome.espidf.component.os.path.relpath", + lambda *args, **kwargs: "src\\backend\\cipher.c", + ) + + content = generate_cmakelists_txt(tmp_component) + + assert 'SRCS "src/backend/cipher.c"' in content + assert "\\" not in content + + +def test_generate_cmakelists_txt_multi_token_flag(tmp_component): + # PlatformIO shell-lexes each build.flags entry, so a single entry can + # carry a flag and its argument. The generated CMakeLists must emit them + # as separate compile options, not one argument with an embedded space. + src_dir = tmp_component.path / "src" + src_dir.mkdir() + (src_dir / "main.c").write_text("int main() {}") + + tmp_component.data = {"build": {"flags": ["-include cp_custom_alloc.h", "-DTEST"]}} + + content = generate_cmakelists_txt(tmp_component) + assert '"-include cp_custom_alloc.h"' not in content + assert ' "-include"\n "cp_custom_alloc.h"\n' in content + + +def test_generate_cmakelists_txt_space_separated_classified_flags(tmp_component): + # Space-separated -I/-L/-l entries routed to INCLUDE_DIRS and the link + # handling before the shlex split was added; splitting must not leak + # them into raw compile options. + src_dir = tmp_component.path / "src" + src_dir.mkdir() + (src_dir / "main.c").write_text("int main() {}") + (tmp_component.path / "extra_inc").mkdir() + + tmp_component.data = { + "build": {"flags": ["-I extra_inc", "-L extra_lib", "-l extralib", "-DTEST"]} + } + + content = generate_cmakelists_txt(tmp_component) + assert 'INCLUDE_DIRS "src" "extra_inc"' in content + assert 'target_link_directories(${COMPONENT_LIB} INTERFACE\n "extra_lib"\n)' in ( + content + ) + assert 'target_link_libraries(${COMPONENT_LIB} INTERFACE\n "extralib"\n)' in ( + content + ) + assert '"-I"' not in content + assert '"-L"' not in content + assert '"-l"' not in content + + def test_generate_cmakelists_txt_references_project_managed_components_variable( tmp_component: IDFComponent, ) -> None: @@ -372,6 +483,84 @@ def test_node_key_git_no_ref(): assert locator == ("https://github.com/foo/bar.git", None) +def test_node_key_url_in_name_is_git(): + # add_library("https://github.com/x/y", None): PlatformIO accepted a bare + # git URL as the library name, so the converter must too. + key, is_git, locator = _node_key( + "https://github.com/pstolarz/OneWireNg", None, None + ) + assert key == "pstolarz/OneWireNg" + assert is_git is True + assert locator == ("https://github.com/pstolarz/OneWireNg", None) + + +def test_node_key_url_in_name_with_ref(): + key, is_git, locator = _node_key( + "https://github.com/foo/bar.git#v1.2.3", None, None + ) + assert (key, is_git, locator) == ( + "foo/bar", + True, + ("https://github.com/foo/bar.git", "v1.2.3"), + ) + + +def test_node_key_url_in_name_git_plus_prefix(): + key, is_git, locator = _node_key("git+https://github.com/foo/bar", None, None) + assert (key, is_git, locator) == ( + "foo/bar", + True, + ("https://github.com/foo/bar", None), + ) + + +def test_node_key_git_plus_prefix_in_repository(): + _key, is_git, locator = _node_key("name", None, "git+https://github.com/foo/bar") + assert (is_git, locator) == (True, ("https://github.com/foo/bar", None)) + + +def test_node_key_custom_name_equals_url_is_git(): + key, is_git, locator = _node_key( + "OneWireNg=https://github.com/pstolarz/OneWireNg", None, None + ) + assert (key, is_git, locator) == ( + "pstolarz/OneWireNg", + True, + ("https://github.com/pstolarz/OneWireNg", None), + ) + + +def test_node_key_url_in_name_with_query_containing_equals(): + # A bare URL whose query string contains ``=`` must not be split by the + # CustomName=URL handling. + key, is_git, locator = _node_key("https://host/x/y.git?ref=main", None, None) + assert (key, is_git, locator) == ( + "x/y", + True, + ("https://host/x/y.git?ref=main", None), + ) + + +@pytest.mark.parametrize("name", ["http://[::1", "CustomName=http://[::1"]) +def test_node_key_malformed_url_in_name_raises(name: str) -> None: + # A name that was clearly meant to be a URL but does not parse must fail + # fast instead of degrading to a confusing registry lookup error. + with pytest.raises(RuntimeError, match="Invalid PIO library URL"): + _node_key(name, None, None) + + +def test_node_key_name_with_equals_but_no_url_is_registry(): + key, is_git, locator = _node_key("FOO=BAR", "1.0", None) + assert (key, is_git, locator) == ("FOO=BAR", False, (None, "FOO=BAR")) + + +def test_node_key_version_url_still_ignored_when_name_plain(): + # A version that is a URL is handled by the dependency walk, not here; + # a plain name must stay a registry spec regardless of version shape. + key, is_git, _locator = _node_key("bar", "https://github.com/foo/bar", None) + assert (key, is_git) == ("bar", False) + + def test_node_key_registry_owner_name(): key, is_git, locator = _node_key("foo/bar", "^1.0.0", None) assert (key, is_git, locator) == ("foo/bar", False, ("foo", "bar")) diff --git a/tests/unit_tests/test_espidf_framework.py b/tests/unit_tests/test_espidf_framework.py index c5d9ddbaf1..5912facbb3 100644 --- a/tests/unit_tests/test_espidf_framework.py +++ b/tests/unit_tests/test_espidf_framework.py @@ -3,10 +3,14 @@ # pylint: disable=protected-access from contextlib import contextmanager +import importlib.util import io import json import logging +import os from pathlib import Path +import runpy +import subprocess import sys import tarfile from types import SimpleNamespace @@ -15,7 +19,10 @@ from unittest.mock import patch import pytest from esphome.espidf.framework import ( + ESPHOME_STAMP_FILE, + STAMP_SCHEMA_VERSION, _ccache_env, + _check_esphome_idf_framework_install, _check_stamp, _check_windows_path_length, _clone_idf_with_submodules, @@ -25,7 +32,11 @@ from esphome.espidf.framework import ( _get_python_env_path, _get_python_version, _parse_git_source, + _patch_tools_json_demote_unused_tools, _patch_tools_json_for_linux_arm64, + _prefetch_idf_tool_archives, + _read_stamp, + _stamp_covers, _windows_long_paths_enabled, _write_idf_version_txt, _write_stamp, @@ -131,10 +142,17 @@ def test_parse_git_source_rejected(source: str) -> None: assert _parse_git_source(source) is None -def _make_idf_tree(framework_path: Path) -> None: - """Create the minimum tree _clone_idf_with_submodules sanity-checks for.""" +def _make_idf_tree(framework_path: Path, *, gitmodules: bool = True) -> None: + """Create the minimum tree _clone_idf_with_submodules sanity-checks for. + + ``gitmodules=False`` simulates a fork that vendors components in-tree + instead of declaring submodules; update_submodules skips the git call + when that file is missing. + """ (framework_path / "tools").mkdir(parents=True) (framework_path / "tools" / "idf_tools.py").write_text("# stub\n") + if gitmodules: + (framework_path / ".gitmodules").write_text("# stub\n") def test_clone_idf_with_submodules_without_ref(tmp_path: Path) -> None: @@ -208,6 +226,28 @@ def test_clone_idf_with_submodules_raises_when_tree_missing( ) +def test_clone_idf_accepts_flattened_fork_without_gitmodules( + tmp_path: Path, +) -> None: + """A fork that vendors components in-tree instead of as submodules is valid. + + No .gitmodules means the submodule step is skipped entirely. + """ + framework_path = tmp_path / "idf" + framework_path.mkdir() + _make_idf_tree(framework_path, gitmodules=False) + + with patch("esphome.git.run_git_command", return_value="") as run_git_command_mock: + _clone_idf_with_submodules( + framework_path, + "https://github.com/example/flattened-esp-idf.git", + None, + ) + + calls = [c.args[0] for c in run_git_command_mock.call_args_list] + assert not any(c[1] == "submodule" for c in calls) + + # --------------------------------------------------------------------------- # Helpers for _tar_extract_all hard-link prefix-stripping tests # --------------------------------------------------------------------------- @@ -310,6 +350,21 @@ class TestTarExtractHardLinkPrefixStripping: _IDF_VERSION = "5.1.2" +def _fake_download_from_mirrors( + mirrors: list[str], + substitutions: dict[str, str], + target: object, + **kwargs: object, +) -> str: + """Stand-in for download_from_mirrors that creates path targets, since + the framework code opens the downloaded tarball afterwards.""" + if isinstance(target, (str, os.PathLike)): + path = Path(target) + path.parent.mkdir(parents=True, exist_ok=True) + path.touch() + return "https://example.com/idf.tar.xz" + + @pytest.fixture def espidf_mocks(setup_core: Path): """Patch the heavy I/O of check_esp_idf_install and pre-create the framework dir.""" @@ -317,10 +372,10 @@ def espidf_mocks(setup_core: Path): # extracted-marker touch writes into. _get_framework_path(_IDF_VERSION).mkdir(parents=True, exist_ok=True) with ( - patch("esphome.espidf.framework.rmdir"), + patch("esphome.espidf.framework.rmdir") as rmdir_mock, patch( "esphome.espidf.framework.download_from_mirrors", - return_value="https://example.com/idf.tar.xz", + side_effect=_fake_download_from_mirrors, ) as download, patch("esphome.espidf.framework.archive_extract_all") as extract, patch("esphome.espidf.framework.create_venv") as venv, @@ -331,8 +386,11 @@ def espidf_mocks(setup_core: Path): patch("esphome.espidf.framework._clone_idf_with_submodules") as clone, patch("esphome.espidf.framework._write_idf_version_txt"), patch("esphome.espidf.framework._patch_tools_json_for_linux_arm64"), + patch("esphome.espidf.framework._patch_tools_json_demote_unused_tools"), + patch("esphome.espidf.framework._prefetch_idf_tool_archives"), patch("esphome.espidf.framework._write_stamp"), patch("esphome.espidf.framework._check_stamp", return_value=True), + patch("esphome.espidf.framework._stamp_covers", return_value=True), patch("esphome.espidf.framework._get_idf_version", return_value=_IDF_VERSION), patch("esphome.espidf.framework._get_python_version", return_value="3.11.0"), patch("esphome.espidf.framework.get_system_python_path", return_value="python"), @@ -344,6 +402,7 @@ def espidf_mocks(setup_core: Path): run_ok=run_ok, tool_paths=tool_paths, clone=clone, + rmdir=rmdir_mock, ) @@ -358,6 +417,27 @@ def test_check_esp_idf_install_fresh(espidf_mocks: SimpleNamespace) -> None: espidf_mocks.extract.assert_called_once() espidf_mocks.venv.assert_called_once() espidf_mocks.clone.assert_not_called() + # the tool download cache (/dist) is pruned after install + espidf_mocks.rmdir.assert_any_call( + get_idf_tools_path() / "dist", msg="Remove ESP-IDF tool download cache" + ) + + +def test_check_esp_idf_install_dist_prune_failure_ignored( + espidf_mocks: SimpleNamespace, +) -> None: + """A failure to prune the tool download cache must not fail the install.""" + tools_dist = get_idf_tools_path() / "dist" + + def rmdir_side_effect(directory: Path, msg: str | None = None) -> None: + if directory == tools_dist: + raise RuntimeError("cannot remove dist") + + espidf_mocks.rmdir.side_effect = rmdir_side_effect + + # install still succeeds despite the failed prune + framework_path, _ = check_esp_idf_install(_IDF_VERSION, force=True) + assert framework_path == _get_framework_path(_IDF_VERSION) def test_check_esp_idf_install_git_source(espidf_mocks: SimpleNamespace) -> None: @@ -389,6 +469,20 @@ def test_check_esp_idf_install_already_installed(espidf_mocks: SimpleNamespace) espidf_mocks.venv.assert_not_called() +def test_corrupt_tarball_removed_when_extraction_fails( + espidf_mocks: SimpleNamespace, +) -> None: + """A tarball that fails to extract (e.g. torn by an unclean shutdown) is + deleted so the next run re-downloads instead of failing forever.""" + espidf_mocks.extract.side_effect = RuntimeError("xz: unexpected end of input") + tarball = get_idf_tools_path() / "dist" / f"esp-idf-{_IDF_VERSION}.tar.xz" + + with pytest.raises(RuntimeError, match="unexpected end of input"): + check_esp_idf_install(_IDF_VERSION, force=True) + + assert not tarball.exists() + + def test_check_esp_idf_install_framework_failure(espidf_mocks: SimpleNamespace) -> None: """A failing idf_tools install raises.""" espidf_mocks.run_ok.side_effect = [False] @@ -426,13 +520,17 @@ def _mark_installed() -> None: def test_check_esp_idf_install_stamp_mismatch_reinstalls( espidf_mocks: SimpleNamespace, ) -> None: - """A stamp mismatch reinstalls tools (marker present, so no re-extract).""" + """A stamp mismatch reinstalls tools (marker present, so no re-extract). + + The python env is left alone: it depends on the framework version and + features, not on which toolchains are installed. + """ _mark_installed() - with patch("esphome.espidf.framework._check_stamp", return_value=False): + with patch("esphome.espidf.framework._stamp_covers", return_value=False): check_esp_idf_install(_IDF_VERSION) espidf_mocks.extract.assert_not_called() # marker present -> no re-extract - espidf_mocks.venv.assert_called_once() # tools reinstall -> venv rebuilt + espidf_mocks.venv.assert_not_called() # tools-only install -> venv kept def test_check_esp_idf_install_check_command_failure_reinstalls( @@ -445,7 +543,7 @@ def test_check_esp_idf_install_check_command_failure_reinstalls( check_esp_idf_install(_IDF_VERSION, features=["fb"]) espidf_mocks.extract.assert_not_called() - espidf_mocks.venv.assert_called_once() + espidf_mocks.venv.assert_not_called() # tools-only install -> venv kept def test_check_esp_idf_install_unknown_python_version_reinstalls( @@ -465,8 +563,8 @@ def test_check_esp_idf_install_python_stamp_mismatch_rebuilds_venv( ) -> None: """Framework stamp matches but the python-env stamp does not -> venv rebuilt.""" - # _check_stamp passes for the framework (no python_version key) and fails - # for the python env (carries python_version), so only the venv rebuilds. + # _check_stamp only guards the python env now (the framework uses + # _stamp_covers, patched True by the fixture); failing it rebuilds the venv. def stamp_ok(_stamp_file, info: dict) -> bool: return "python_version" not in info @@ -478,6 +576,174 @@ def test_check_esp_idf_install_python_stamp_mismatch_rebuilds_venv( espidf_mocks.venv.assert_called_once() +def _requested_stamp(targets: list[str], tools: list[str] | None = None) -> dict: + return { + "schema_version": STAMP_SCHEMA_VERSION, + "targets": targets, + "tools": tools or ["required"], + } + + +@pytest.mark.parametrize( + ("stored", "targets", "expected"), + [ + # a stored "all" covers any target + (_requested_stamp(["all"]), ["esp32"], True), + # exact match and superset both cover + (_requested_stamp(["esp32"]), ["esp32"], True), + (_requested_stamp(["esp32", "esp32c3"]), ["esp32"], True), + # a new target is not covered + (_requested_stamp(["esp32"]), ["esp32c3"], False), + # tools and schema_version must match exactly + (_requested_stamp(["all"], tools=["cmake", "required"]), ["esp32"], False), + (_requested_stamp(["all"]) | {"schema_version": "no"}, ["esp32"], False), + # an unknown extra field participates in invalidation by default + (_requested_stamp(["all"]) | {"module_version": 1}, ["esp32"], False), + # missing/corrupt stamps never cover + (None, ["esp32"], False), + ( + {"schema_version": STAMP_SCHEMA_VERSION, "tools": ["required"]}, + ["esp32"], + False, + ), + ], +) +def test_stamp_covers(stored: dict | None, targets: list[str], expected: bool) -> None: + assert _stamp_covers(stored, _requested_stamp(targets)) is expected + + +@contextmanager +def _framework_install_patches(): + """Patches for calling _check_esphome_idf_framework_install directly with + real stamp files (unlike espidf_mocks, which stubs the stamp layer).""" + with ( + patch("esphome.espidf.framework.run_command_ok", return_value=True) as run_ok, + patch("esphome.espidf.framework._get_idf_tool_paths", return_value=([], {})), + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + patch("esphome.espidf.framework.rmdir"), + ): + yield run_ok + + +def _extracted_framework_with_stamp(stamp: dict) -> Path: + framework_path = _get_framework_path(_IDF_VERSION) + framework_path.mkdir(parents=True, exist_ok=True) + (framework_path / ".esphome_extracted").touch() + _write_stamp(framework_path / ESPHOME_STAMP_FILE, stamp) + return framework_path + + +def test_framework_install_target_subset_skips_install() -> None: + """A stamp holding a superset of the requested targets skips the installer.""" + framework_path = _extracted_framework_with_stamp(_requested_stamp(["all"])) + + with _framework_install_patches() as run_ok: + _, fresh_extract = _check_esphome_idf_framework_install( + _IDF_VERSION, ["esp32"], ["required"] + ) + + run_ok.assert_not_called() + assert fresh_extract is False + # the stamp is untouched + stamp = json.loads((framework_path / ESPHOME_STAMP_FILE).read_text()) + assert stamp["targets"] == ["all"] + + +def test_framework_install_new_target_installs_and_merges_stamp() -> None: + """A new target runs the installer for just that target and the stamp + records the union of everything installed so far.""" + framework_path = _extracted_framework_with_stamp(_requested_stamp(["esp32"])) + + with _framework_install_patches() as run_ok: + _, fresh_extract = _check_esphome_idf_framework_install( + _IDF_VERSION, ["esp32c3"], ["required"] + ) + + assert fresh_extract is False + assert "--targets=esp32c3" in run_ok.call_args[0][0] + stamp = json.loads((framework_path / ESPHOME_STAMP_FILE).read_text()) + assert stamp["targets"] == ["esp32", "esp32c3"] + + +def test_check_esp_idf_install_env_targets_override_wins( + espidf_mocks: SimpleNamespace, +) -> None: + """An explicitly set ESPHOME_IDF_DEFAULT_TARGETS overrides per-variant targets.""" + with patch("esphome.espidf.framework._IDF_DEFAULT_TARGETS_EXPLICIT", True): + check_esp_idf_install(_IDF_VERSION, force=True, targets=["esp32"]) + + install_cmd = espidf_mocks.run_ok.call_args_list[0][0][0] + assert "--targets=all" in install_cmd + + +def test_check_esp_idf_install_uses_requested_targets( + espidf_mocks: SimpleNamespace, +) -> None: + """Without the env override, the caller's per-variant targets are installed.""" + check_esp_idf_install(_IDF_VERSION, force=True, targets=["esp32"]) + + install_cmd = espidf_mocks.run_ok.call_args_list[0][0][0] + assert "--targets=esp32" in install_cmd + + +def test_framework_install_all_request_collapses_merged_stamp_to_all() -> None: + """Requesting "all" over a per-variant stamp merges and collapses to + ["all"], not ["all", "esp32"], so the stamp shape stays canonical.""" + framework_path = _extracted_framework_with_stamp(_requested_stamp(["esp32"])) + + with _framework_install_patches() as run_ok: + _check_esphome_idf_framework_install(_IDF_VERSION, ["all"], ["required"]) + + run_ok.assert_called_once() + stamp = json.loads((framework_path / ESPHOME_STAMP_FILE).read_text()) + assert stamp["targets"] == ["all"] + + +def test_framework_install_tools_change_resets_stamp_targets() -> None: + """A reinstall triggered by a tools change must not carry the old stamp's + targets forward: the installer only ran for this build's targets, so a + merged stamp would let other variants skip the reinstall they need.""" + framework_path = _extracted_framework_with_stamp( + _requested_stamp(["all"], tools=["cmake", "required"]) + ) + + with _framework_install_patches() as run_ok: + _check_esphome_idf_framework_install(_IDF_VERSION, ["esp32"], ["required"]) + + run_ok.assert_called_once() + stamp = json.loads((framework_path / ESPHOME_STAMP_FILE).read_text()) + assert stamp["targets"] == ["esp32"] + assert stamp["tools"] == ["required"] + + +@pytest.mark.parametrize( + ("lib", "expect_hint"), + [ + (None, True), + ("libusb-1.0.so.0", False), + ], +) +def test_check_esp_idf_install_failure_libusb_hint( + espidf_mocks: SimpleNamespace, + caplog: pytest.LogCaptureFixture, + lib: str | None, + expect_hint: bool, +) -> None: + """A failed tools install only shows the libusb hint when libusb-1.0 is + actually missing.""" + espidf_mocks.run_ok.return_value = False + # Fake Linux so the gate is exercised on all CI hosts; faking Linux is safe + # everywhere (unlike faking Windows, which pulls in winreg on other hosts) + with ( + patch("esphome.espidf.framework.find_library", return_value=lib), + patch("esphome.espidf.framework.platform.system", return_value="Linux"), + caplog.at_level(logging.ERROR, logger="esphome.espidf.framework"), + pytest.raises(RuntimeError, match="framework installation failure"), + ): + check_esp_idf_install(_IDF_VERSION, force=True) + assert ("libusb-1.0.so.0 was not found" in caplog.text) == expect_hint + + def test_check_esp_idf_install_unparseable_version( espidf_mocks: SimpleNamespace, ) -> None: @@ -489,6 +755,29 @@ def test_check_esp_idf_install_unparseable_version( espidf_mocks.extract.assert_called_once() +@pytest.mark.parametrize( + ("version", "short_version"), + [ + ("6.0.0", "6.0"), + ("6.0.0-rc1", "6.0-rc1"), + ("5.5.4", None), # vX.Y tags only exist for X.Y.0 releases + ], +) +def test_check_esp_idf_install_short_version_substitution( + espidf_mocks: SimpleNamespace, version: str, short_version: str | None +) -> None: + """SHORT_VERSION is only offered for x.y.0 releases, so the vX.Y mirror + template is never tried for versions whose tag cannot exist.""" + _get_framework_path(version).mkdir(parents=True, exist_ok=True) + check_esp_idf_install(version, force=True) + + # First call downloads the framework archive; a later call fetches the + # constraints file with its own substitutions. + substitutions = espidf_mocks.download.call_args_list[0][0][1] + assert substitutions.get("SHORT_VERSION") == short_version + assert substitutions["VERSION"] == version + + # --------------------------------------------------------------------------- # _patch_tools_json_for_linux_arm64 (arm64-only ninja backport) # --------------------------------------------------------------------------- @@ -561,6 +850,415 @@ def test_patch_tools_json_already_patched_is_noop(tmp_path: Path) -> None: assert tools_json.read_text(encoding="utf-8") == before +# --------------------------------------------------------------------------- +# _prefetch_idf_tool_archives +# --------------------------------------------------------------------------- + + +_PREFETCH_JSON = json.dumps( + [ + { + "name": "cmake@3.30.2", + "url": "https://example.com/cmake.tar.gz", + "size": 123, + "sha256": "ab" * 32, + "dest": "cmake-3.30.2.tar.gz", + }, + { + "name": "ninja@1.12.1", + "url": "https://example.com/ninja.zip", + "size": 45, + "sha256": "cd" * 32, + "dest": "ninja.zip", + }, + ] +) + + +def test_prefetch_downloads_each_archive_with_resume(tmp_path: Path) -> None: + with ( + patch( + "esphome.espidf.framework.run_command", + return_value=(True, _PREFETCH_JSON, ""), + ), + patch("esphome.espidf.framework.download_with_resume") as download, + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + ): + _prefetch_idf_tool_archives(tmp_path, "esp32", ["required"], None) + + dist = get_idf_tools_path() / "dist" + assert download.call_count == 2 + assert download.call_args_list[0][0] == ( + "https://example.com/cmake.tar.gz", + dist / "cmake-3.30.2.tar.gz", + ) + assert download.call_args_list[0][1] == {"sha256": "ab" * 32, "size": 123} + + +def test_prefetch_skips_already_downloaded_archives(tmp_path: Path) -> None: + dist = get_idf_tools_path() / "dist" + dist.mkdir(parents=True) + (dist / "cmake-3.30.2.tar.gz").write_bytes(b"cached") + with ( + patch( + "esphome.espidf.framework.run_command", + return_value=(True, _PREFETCH_JSON, ""), + ), + patch("esphome.espidf.framework.download_with_resume") as download, + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + ): + _prefetch_idf_tool_archives(tmp_path, "esp32", ["required"], None) + + # only the missing archive is downloaded + assert download.call_count == 1 + assert download.call_args[0][1] == dist / "ninja.zip" + + +@pytest.mark.parametrize( + ("run_result", "download_error", "expected_log"), + [ + ((False, "", "script exploded"), None, "tool downloads"), # script failure + ((True, "{ not json", ""), None, "prefetch failed"), # unparsable output + ( + (True, _PREFETCH_JSON, ""), + OSError("network down"), + "Could not prefetch", + ), # download failure + ], +) +def test_prefetch_failures_never_raise( + tmp_path: Path, + caplog: pytest.LogCaptureFixture, + run_result: tuple[bool, str, str], + download_error: Exception | None, + expected_log: str, +) -> None: + """The prefetch is best-effort; idf_tools downloads whatever is missing.""" + with ( + patch("esphome.espidf.framework.run_command", return_value=run_result), + patch( + "esphome.espidf.framework.download_with_resume", + side_effect=download_error, + ), + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + ): + _prefetch_idf_tool_archives(tmp_path, "esp32", ["required"], None) + + assert expected_log in caplog.text + + +def test_prefetch_one_failed_archive_does_not_stop_the_rest( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A single archive failing its download must not abort the prefetch of + the remaining archives.""" + with ( + patch( + "esphome.espidf.framework.run_command", + return_value=(True, _PREFETCH_JSON, ""), + ), + patch( + "esphome.espidf.framework.download_with_resume", + side_effect=[OSError("network down"), None], + ) as download, + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + ): + _prefetch_idf_tool_archives(tmp_path, "esp32", ["required"], None) + + assert download.call_count == 2 + assert "Could not prefetch cmake@3.30.2" in caplog.text + + +def test_prefetch_passes_targets_and_tools_to_script(tmp_path: Path) -> None: + with ( + patch( + "esphome.espidf.framework.run_command", return_value=(True, "[]", "") + ) as run, + patch("esphome.espidf.framework.get_system_python_path", return_value="python"), + ): + _prefetch_idf_tool_archives( + tmp_path, "esp32,esp32c3", ["required", "cmake"], {"IDF_TOOLS_PATH": "/x"} + ) + + cmd = run.call_args[0][0] + assert cmd[-3:] == ["esp32,esp32c3", "required", "cmake"] + assert cmd[1].endswith("get_tool_downloads.py") + # the script inherits the caller's env plus the framework tools PYTHONPATH + env = run.call_args[1]["env"] + assert env["IDF_TOOLS_PATH"] == "/x" + assert env["PYTHONPATH"] == str(tmp_path / "tools") + + +def test_framework_install_prefetches_before_installer( + espidf_mocks: SimpleNamespace, +) -> None: + """The prefetch runs before idf_tools.py install so the installer finds + the archives already in dist/.""" + calls: list[str] = [] + with ( + patch( + "esphome.espidf.framework._prefetch_idf_tool_archives", + side_effect=lambda *a, **k: calls.append("prefetch"), + ), + ): + espidf_mocks.run_ok.side_effect = lambda *a, **k: ( + calls.append("install") or True + ) + check_esp_idf_install(_IDF_VERSION, force=True) + + assert calls.index("prefetch") < calls.index("install") + + +# --------------------------------------------------------------------------- +# get_tool_downloads.py (against the stub idf_tools module in fixtures/) +# --------------------------------------------------------------------------- + + +_IDF_TOOLS_STUB_DIR = Path(__file__).parent / "fixtures" / "idf_tools_stub" + + +def _run_downloads_script( + tmp_path: Path, *args: str, env_extra: dict[str, str] | None = None +) -> subprocess.CompletedProcess[str]: + """Run the real get_tool_downloads.py against the stub idf_tools module.""" + script = Path(__file__).parents[2] / "esphome" / "espidf" / "get_tool_downloads.py" + env = os.environ | { + "PYTHONPATH": str(_IDF_TOOLS_STUB_DIR), + "IDF_TOOLS_PATH": str(tmp_path / "tp"), + } + if env_extra: + env |= env_extra + return subprocess.run( + [sys.executable, str(script), str(tmp_path / "fw"), *args], + capture_output=True, + text=True, + env=env, + check=False, + ) + + +def test_get_tool_downloads_lists_missing_tools(tmp_path: Path) -> None: + """Installed versions are skipped, tools that fail their binary check are + still listed, rename_dist decides the dist filename, and idf_tools' stdout + chatter stays off the JSON channel.""" + result = _run_downloads_script(tmp_path, "esp32", "required") + + assert result.returncode == 0, result.stderr + downloads = {d["name"]: d for d in json.loads(result.stdout)} + # installed-tool@1.0 is already installed and must not be listed + assert set(downloads) == {"cmake@3.30.2", "ninja@1.12.1", "broken-tool@2.0"} + assert downloads["cmake@3.30.2"]["dest"] == "cmake.tar.gz" + assert downloads["cmake@3.30.2"]["size"] == 11 + assert downloads["cmake@3.30.2"]["sha256"] == "aa" + # rename_dist overrides the URL basename + assert downloads["ninja@1.12.1"]["dest"] == "ninja-v1.zip" + # the stub prints informational lines; they must be on stderr + assert "Changed download URL" in result.stderr + + +def test_get_tool_downloads_applies_mirror_rewrite(tmp_path: Path) -> None: + result = _run_downloads_script( + tmp_path, + "esp32", + "required", + env_extra={"TEST_MIRROR_PREFIX": "https://mirror.test/"}, + ) + + assert result.returncode == 0, result.stderr + downloads = json.loads(result.stdout) + assert all(d["url"].startswith("https://mirror.test/") for d in downloads) + + +def _run_downloads_inprocess( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + *args: str, +) -> list[dict]: + """Execute get_tool_downloads.py in-process against the stub idf_tools. + + Unlike the subprocess variant this runs under coverage, exercising the + script's own lines. + """ + spec = importlib.util.spec_from_file_location( + "idf_tools", _IDF_TOOLS_STUB_DIR / "idf_tools.py" + ) + stub = importlib.util.module_from_spec(spec) + spec.loader.exec_module(stub) + monkeypatch.setitem(sys.modules, "idf_tools", stub) + monkeypatch.setenv("IDF_TOOLS_PATH", str(tmp_path / "tp")) + script = Path(__file__).parents[2] / "esphome" / "espidf" / "get_tool_downloads.py" + monkeypatch.setattr(sys, "argv", [str(script), str(tmp_path / "fw"), *args]) + runpy.run_path(str(script)) + return json.loads(capsys.readouterr().out) + + +def test_get_tool_downloads_inprocess_full_flow( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """In-process run covering the whole script: required expansion, + installed/broken tools, rename_dist, and version pinning via tool@version.""" + downloads = { + d["name"]: d + for d in _run_downloads_inprocess( + tmp_path, monkeypatch, capsys, "esp32", "required" + ) + } + assert set(downloads) == {"cmake@3.30.2", "ninja@1.12.1", "broken-tool@2.0"} + assert downloads["ninja@1.12.1"]["dest"] == "ninja-v1.zip" + assert downloads["cmake@3.30.2"]["url"] == "https://gh.test/cmake.tar.gz" + + +def test_get_tool_downloads_inprocess_explicit_tool_specs( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Explicit tool names and tool@version specs resolve; unknown tools and + unknown versions are skipped.""" + downloads = _run_downloads_inprocess( + tmp_path, + monkeypatch, + capsys, + "esp32", + "cmake@3.30.2", + "no-such-tool", + "cmake@9.9.9", + ) + assert [d["name"] for d in downloads] == ["cmake@3.30.2"] + + +# --------------------------------------------------------------------------- +# _patch_tools_json_demote_unused_tools (openocd, gdb, ULP toolchain optional) +# --------------------------------------------------------------------------- + + +def test_demote_unused_tools_patches_install_type(tmp_path: Path) -> None: + tools_json = _write_tools_json( + tmp_path, + { + "tools": [ + {"name": "openocd-esp32", "install": "always"}, + {"name": "xtensa-esp-elf-gdb", "install": "always"}, + {"name": "riscv32-esp-elf-gdb", "install": "always"}, + {"name": "esp32ulp-elf", "install": "always"}, + {"name": "xtensa-esp-elf", "install": "always"}, + {"name": "esp-rom-elfs", "install": "always"}, + ] + }, + ) + _patch_tools_json_demote_unused_tools(tmp_path) + + data = json.loads(tools_json.read_text(encoding="utf-8")) + install_types = {t["name"]: t["install"] for t in data["tools"]} + assert install_types == { + "openocd-esp32": "on_request", + "xtensa-esp-elf-gdb": "on_request", + "riscv32-esp-elf-gdb": "on_request", + "esp32ulp-elf": "on_request", + # the compiler toolchain and ROM ELFs stay required + "xtensa-esp-elf": "always", + "esp-rom-elfs": "always", + } + + +def test_demote_unused_tools_drops_xtensa_from_riscv_targets(tmp_path: Path) -> None: + """riscv32-esp-elf loses the xtensa chips (ULP-RISC-V only, which ESPHome + never builds) but keeps its RISC-V targets; other tools are untouched.""" + tools_json = _write_tools_json( + tmp_path, + { + "tools": [ + { + "name": "riscv32-esp-elf", + "install": "always", + "supported_targets": ["esp32s2", "esp32s3", "esp32c3", "esp32p4"], + }, + { + "name": "xtensa-esp-elf", + "install": "always", + "supported_targets": ["esp32", "esp32s2", "esp32s3"], + }, + ] + }, + ) + _patch_tools_json_demote_unused_tools(tmp_path) + + data = json.loads(tools_json.read_text(encoding="utf-8")) + riscv = next(t for t in data["tools"] if t["name"] == "riscv32-esp-elf") + xtensa = next(t for t in data["tools"] if t["name"] == "xtensa-esp-elf") + assert riscv["supported_targets"] == ["esp32c3", "esp32p4"] + assert riscv["install"] == "always" + assert xtensa["supported_targets"] == ["esp32", "esp32s2", "esp32s3"] + + +def test_demote_unused_tools_bad_supported_targets_type_still_demotes( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A non-list supported_targets on riscv32-esp-elf must not abort the + other demotions; the targets patch is best-effort and logs the skip so a + silently resumed riscv download is diagnosable.""" + tools_json = _write_tools_json( + tmp_path, + { + "tools": [ + { + "name": "riscv32-esp-elf", + "install": "always", + "supported_targets": None, + }, + {"name": "openocd-esp32", "install": "always"}, + ] + }, + ) + with caplog.at_level(logging.WARNING, logger="esphome.espidf.framework"): + _patch_tools_json_demote_unused_tools(tmp_path) + + data = json.loads(tools_json.read_text(encoding="utf-8")) + openocd = next(t for t in data["tools"] if t["name"] == "openocd-esp32") + riscv = next(t for t in data["tools"] if t["name"] == "riscv32-esp-elf") + assert openocd["install"] == "on_request" + assert riscv["supported_targets"] is None + assert "Unexpected supported_targets" in caplog.text + + +def test_patch_tools_json_unexpected_structure_warns_and_skips( + tmp_path: Path, +) -> None: + """Valid JSON with an unexpected shape must skip the patch, not raise.""" + tools_dir = tmp_path / "tools" + tools_dir.mkdir() + tools_json = tools_dir / "tools.json" + tools_json.write_text('["not", "a", "dict"]', encoding="utf-8") + before = tools_json.read_text(encoding="utf-8") + _patch_tools_json_demote_unused_tools(tmp_path) # AttributeError -> skip + assert tools_json.read_text(encoding="utf-8") == before + + +def test_demote_unused_tools_already_patched_is_noop(tmp_path: Path) -> None: + tools_json = _write_tools_json( + tmp_path, + { + "tools": [ + {"name": "openocd-esp32", "install": "on_request"}, + {"name": "xtensa-esp-elf-gdb", "install": "on_request"}, + {"name": "riscv32-esp-elf-gdb", "install": "on_request"}, + {"name": "esp32ulp-elf", "install": "on_request"}, + { + "name": "riscv32-esp-elf", + "install": "always", + "supported_targets": ["esp32c3", "esp32p4"], + }, + ] + }, + ) + before = tools_json.read_text(encoding="utf-8") + _patch_tools_json_demote_unused_tools(tmp_path) + assert tools_json.read_text(encoding="utf-8") == before + + # --------------------------------------------------------------------------- # Subprocess-backed helpers (_exec -> run_command rename) and get_framework_env # --------------------------------------------------------------------------- @@ -787,6 +1485,54 @@ def test_check_stamp_corrupt_file(tmp_path: Path) -> None: assert _check_stamp(f, {"a": "1"}) is False +def test_read_stamp_corrupt_file_warns( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + # A corrupt stamp forces a full reinstall on every build, so it warns + # where the normal missing-file case stays silent. + f = tmp_path / "s.json" + f.write_text("{ not json", encoding="utf-8") + with caplog.at_level(logging.WARNING, logger="esphome.espidf.framework"): + assert _read_stamp(f) is None + assert "Ignoring corrupt stamp file" in caplog.text + + +def test_read_stamp_unreadable_file_warns( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + # An I/O fault (permissions, disk error) is distinguished from a simply + # missing stamp with a warning before falling back to reinstall. + f = tmp_path / "s.json" + f.write_text(json.dumps({"a": "1"}), encoding="utf-8") + with ( + patch.object(Path, "open", side_effect=PermissionError("denied")), + caplog.at_level(logging.WARNING, logger="esphome.espidf.framework"), + ): + assert _read_stamp(f) is None + assert "Could not read stamp file" in caplog.text + + +def test_read_stamp_non_dict_warns( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + # Well-formed JSON that is not an object is a fault, not a first install; + # it must leave a trace before forcing reinstalls. + f = tmp_path / "s.json" + f.write_text("null", encoding="utf-8") + with caplog.at_level(logging.WARNING, logger="esphome.espidf.framework"): + assert _read_stamp(f) is None + assert "unexpected type NoneType" in caplog.text + + +def test_read_stamp_missing_file_is_silent( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + # Missing stamps are the normal first-install case and must not log. + with caplog.at_level(logging.DEBUG, logger="esphome.espidf.framework"): + assert _read_stamp(tmp_path / "nope.json") is None + assert "stamp file" not in caplog.text + + def test_write_idf_version_txt_writes_when_missing(tmp_path: Path) -> None: _write_idf_version_txt(tmp_path, "5.1.2") assert (tmp_path / "version.txt").read_text(encoding="utf-8") == "v5.1.2\n" diff --git a/tests/unit_tests/test_espidf_toolchain.py b/tests/unit_tests/test_espidf_toolchain.py index 017d8c49b4..56f358a24c 100644 --- a/tests/unit_tests/test_espidf_toolchain.py +++ b/tests/unit_tests/test_espidf_toolchain.py @@ -5,10 +5,19 @@ import json import os from pathlib import Path +import subprocess from unittest.mock import patch -from esphome.const import CONF_FRAMEWORK, CONF_SOURCE -from esphome.core import CORE +import pytest + +from esphome.components.esp32.const import KEY_ESP32, KEY_VARIANT +from esphome.const import ( + CONF_COMPILE_PROCESS_LIMIT, + CONF_ESPHOME, + CONF_FRAMEWORK, + CONF_SOURCE, +) +from esphome.core import CORE, EsphomeError from esphome.espidf import toolchain @@ -47,7 +56,7 @@ def test_get_esphome_esp_idf_paths_forwards_source_override(): toolchain, "check_esp_idf_install", return_value=("/fw", "/penv") ) as mock_install: toolchain._get_esphome_esp_idf_paths("5.5.4") - mock_install.assert_called_once_with("5.5.4", source_url=url) + mock_install.assert_called_once_with("5.5.4", targets=None, source_url=url) def test_get_esphome_esp_idf_paths_no_override(): @@ -58,7 +67,28 @@ def test_get_esphome_esp_idf_paths_no_override(): toolchain, "check_esp_idf_install", return_value=("/fw", "/penv") ) as mock_install: toolchain._get_esphome_esp_idf_paths("5.5.4") - mock_install.assert_called_once_with("5.5.4", source_url=None) + mock_install.assert_called_once_with("5.5.4", targets=None, source_url=None) + + +def test_get_configured_targets_from_variant(monkeypatch: pytest.MonkeyPatch): + """The configured variant restricts the toolchain install to its target.""" + monkeypatch.delenv("CI", raising=False) + CORE.data[KEY_ESP32] = {KEY_VARIANT: "ESP32S3"} + assert toolchain._get_configured_targets() == ["esp32s3"] + + +def test_get_configured_targets_without_variant(monkeypatch: pytest.MonkeyPatch): + """No stored variant (e.g. tooling outside a build) keeps the default.""" + monkeypatch.delenv("CI", raising=False) + CORE.data.pop(KEY_ESP32, None) + assert toolchain._get_configured_targets() is None + + +def test_get_configured_targets_ci_installs_all(monkeypatch: pytest.MonkeyPatch): + """CI installs every target so the shared cache covers all variants.""" + monkeypatch.setenv("CI", "true") + CORE.data[KEY_ESP32] = {KEY_VARIANT: "ESP32S3"} + assert toolchain._get_configured_targets() is None def _setup_build(setup_core: Path) -> tuple[Path, Path]: @@ -100,7 +130,7 @@ def test_get_idedata_uses_cache_when_valid(setup_core: Path) -> None: compile_commands.parent.mkdir(parents=True, exist_ok=True) compile_commands.write_text("[]") cache.parent.mkdir(parents=True, exist_ok=True) - cache.write_text('{"cxx_path": "cached"}') + cache.write_text('{"cc_path": "cached-gcc", "cxx_path": "cached"}') cc_mtime = compile_commands.stat().st_mtime os.utime(cache, (cc_mtime + 1, cc_mtime + 1)) @@ -108,7 +138,31 @@ def test_get_idedata_uses_cache_when_valid(setup_core: Path) -> None: result = toolchain.get_idedata() mock_transform.assert_not_called() - assert result == {"cxx_path": "cached"} + assert result == {"cc_path": "cached-gcc", "cxx_path": "cached"} + + +def test_get_idedata_regenerates_cache_without_cc_path(setup_core: Path) -> None: + """A cache predating cc_path is rebuilt even though it is newer. + + Such a cache stays newer than the compile DB forever, so consumers that + derive the binutils paths from cc_path would keep failing on it. + """ + compile_commands, cache = _setup_build(setup_core) + compile_commands.parent.mkdir(parents=True, exist_ok=True) + compile_commands.write_text("[]") + cache.parent.mkdir(parents=True, exist_ok=True) + cache.write_text('{"cxx_path": "cached"}') + cc_mtime = compile_commands.stat().st_mtime + os.utime(cache, (cc_mtime + 1, cc_mtime + 1)) + + with patch( + "esphome.espidf.idedata.idedata_from_build", + return_value={"cc_path": "gcc", "cxx_path": "g++"}, + ) as mock_transform: + result = toolchain.get_idedata() + + mock_transform.assert_called_once() + assert result["cc_path"] == "gcc" def test_get_idedata_regenerates_when_compile_commands_newer(setup_core: Path) -> None: @@ -131,6 +185,33 @@ def test_get_idedata_regenerates_when_compile_commands_newer(setup_core: Path) - assert result == {"cxx_path": "fresh", "prog_path": str(toolchain.get_elf_path())} +@pytest.mark.parametrize("cached", ['"cc_path is a string"', "[]", "42"]) +def test_get_idedata_regenerates_on_non_dict_cache( + setup_core: Path, cached: str +) -> None: + """A newer cache holding valid JSON that is not an object is regenerated. + + A bare string would otherwise pass the cc_path check by substring and be + handed to consumers expecting a dict. + """ + compile_commands, cache = _setup_build(setup_core) + compile_commands.parent.mkdir(parents=True, exist_ok=True) + compile_commands.write_text("[]") + cache.parent.mkdir(parents=True, exist_ok=True) + cache.write_text(cached) + cc_mtime = compile_commands.stat().st_mtime + os.utime(cache, (cc_mtime + 1, cc_mtime + 1)) + + with patch( + "esphome.espidf.idedata.idedata_from_build", + return_value={"cc_path": "gcc", "cxx_path": "g++"}, + ) as mock_transform: + result = toolchain.get_idedata() + + mock_transform.assert_called_once() + assert isinstance(result, dict) + + def test_get_idedata_regenerates_on_corrupted_cache(setup_core: Path) -> None: """An unparseable (but newer) cache falls back to regeneration.""" compile_commands, cache = _setup_build(setup_core) @@ -184,6 +265,129 @@ def test_get_idf_env_sets_git_ceiling_directories(setup_core: Path) -> None: assert str(CORE.config_dir) in env["GIT_CEILING_DIRECTORIES"].split(os.pathsep) +def test_get_cmake_output_without_build_dir(setup_core: Path) -> None: + """A build dir that was never created raises EsphomeError. + + Without this, subprocess.run(cwd=build_dir) raises FileNotFoundError, which + the log stack-trace decoder doesn't recognise as a decode failure. + """ + _setup_build(setup_core) + build_dir = CORE.relative_build_path("build") + assert not build_dir.exists() + + with pytest.raises(EsphomeError, match="No ESP-IDF build found"): + toolchain._get_cmake_output(build_dir) + + +def test_get_cmake_output_without_cmake_cache(setup_core: Path) -> None: + """A build dir that exists but was never configured raises EsphomeError.""" + _setup_build(setup_core) + build_dir = CORE.relative_build_path("build") + build_dir.mkdir(parents=True) + + with pytest.raises(EsphomeError, match="No ESP-IDF build found"): + toolchain._get_cmake_output(build_dir) + + +def test_get_cmake_output_with_configured_build(setup_core: Path) -> None: + """A configured build still runs cmake and caches the output. + + The missing-build guard must not get in the way of a real build. + """ + _setup_build(setup_core) + build_dir = CORE.relative_build_path("build") + build_dir.mkdir(parents=True) + (build_dir / "CMakeCache.txt").write_text("") + + completed = subprocess.CompletedProcess( + args=[], returncode=0, stdout="CMAKE_ADDR2LINE:FILEPATH=/tool/addr2line\n" + ) + with ( + patch.object(toolchain, "_get_idf_env", return_value={}), + patch.object(toolchain.subprocess, "run", return_value=completed) as mock_run, + ): + assert toolchain._get_cmake_output(build_dir) == completed.stdout + # Second call is served from the cache rather than re-running cmake. + assert toolchain._get_cmake_output(build_dir) == completed.stdout + + mock_run.assert_called_once() + assert toolchain._get_cmake_tool_path("CMAKE_ADDR2LINE") == Path("/tool/addr2line") + + +def test_get_cmake_output_missing_build_does_not_resolve_idf_env( + setup_core: Path, +) -> None: + """The build check runs before the env is resolved. + + Resolving the env calls check_esp_idf_install(), which can download and + extract the whole framework. A doomed call must never start that. + """ + _setup_build(setup_core) + build_dir = CORE.relative_build_path("build") + + with ( + patch.object(toolchain, "_get_idf_env") as mock_env, + patch.object(toolchain.subprocess, "run") as mock_run, + pytest.raises(EsphomeError), + ): + toolchain._get_cmake_output(build_dir) + + mock_env.assert_not_called() + mock_run.assert_not_called() + + +def test_run_idf_py_jobs_sets_build_jobs_env(setup_core: Path) -> None: + """The jobs argument is exported to idf.py as IDF_PY_BUILD_JOBS.""" + _setup_build(setup_core) + + with ( + patch.object(toolchain, "_get_idf_path", return_value=Path("/idf")), + patch.object(toolchain, "_get_idf_env", return_value={"PATH": "/bin"}), + patch.object(toolchain, "_get_idf_tool", return_value="python"), + patch.object(toolchain.subprocess, "run") as mock_run, + ): + mock_run.return_value.returncode = 0 + + toolchain.run_idf_py("build", jobs=2) + env = mock_run.call_args.kwargs["env"] + assert env["IDF_PY_BUILD_JOBS"] == "2" + assert env["PATH"] == "/bin" + + toolchain.run_idf_py("build") + env = mock_run.call_args.kwargs["env"] + assert "IDF_PY_BUILD_JOBS" not in env + + +def test_run_compile_passes_compile_process_limit(setup_core: Path) -> None: + """compile_process_limit is forwarded to run_idf_py as the job limit.""" + _setup_build(setup_core) + config = {CONF_ESPHOME: {CONF_COMPILE_PROCESS_LIMIT: 1}} + + with ( + patch.object(toolchain, "need_reconfigure", return_value=False), + patch.object(toolchain, "run_idf_py", return_value=0) as mock_run, + patch.object(toolchain, "print_summary"), + ): + assert toolchain.run_compile(config, verbose=False) == 0 + + mock_run.assert_called_once_with("build", "size", jobs=1) + + +def test_run_compile_without_compile_process_limit(setup_core: Path) -> None: + """When no compile_process_limit is set, no job limit is passed to idf.py.""" + _setup_build(setup_core) + config = {CONF_ESPHOME: {}} + + with ( + patch.object(toolchain, "need_reconfigure", return_value=False), + patch.object(toolchain, "run_idf_py", return_value=0) as mock_run, + patch.object(toolchain, "print_summary"), + ): + assert toolchain.run_compile(config, verbose=False) == 0 + + mock_run.assert_called_once_with("build", "size", jobs=None) + + def test_get_core_framework_version_from_core_data(): """The version is read from CORE.data when validation populated it.""" from esphome.components.esp32.const import KEY_ESP32, KEY_IDF_VERSION diff --git a/tests/unit_tests/test_framework_helpers.py b/tests/unit_tests/test_framework_helpers.py index 69b9f20eaa..08751879c2 100644 --- a/tests/unit_tests/test_framework_helpers.py +++ b/tests/unit_tests/test_framework_helpers.py @@ -2,8 +2,10 @@ # pylint: disable=protected-access +import hashlib import importlib.util import io +import json import logging import os from pathlib import Path @@ -16,6 +18,8 @@ import zipfile import pytest import requests as req +from esphome import framework_helpers +from esphome.core import EsphomeError from esphome.framework_helpers import ( _7z_extract_all, _detect_archive_root, @@ -25,6 +29,7 @@ from esphome.framework_helpers import ( archive_extract_all, create_venv, download_from_mirrors, + download_with_resume, get_project_compile_flags, get_project_cxx_compile_flags, get_project_link_flags, @@ -506,7 +511,7 @@ class TestArchiveExtractAll: # --------------------------------------------------------------------------- -# download_from_mirrors +# download_from_mirrors / download_with_resume # --------------------------------------------------------------------------- @@ -514,6 +519,8 @@ def _mock_response(content: bytes, ok: bool = True) -> MagicMock: r = MagicMock() r.__enter__.return_value = r r.__exit__.return_value = False + r.status_code = 200 + r.ok = ok if ok: r.raise_for_status.return_value = None else: @@ -523,6 +530,563 @@ def _mock_response(content: bytes, ok: bool = True) -> MagicMock: return r +def _interrupted_response(content: bytes, etag: str | None = None) -> MagicMock: + """A response whose body yields ``content`` and then drops mid-stream. + + ``etag`` makes the response resumable: without a validator the retry + logic restarts from zero rather than stitching unverified bytes. + """ + + def body(chunk_size): + yield content + raise req.exceptions.ChunkedEncodingError("connection dropped") + + r = _mock_response(b"") + if etag is not None: + r.headers = {**r.headers, "ETag": etag} + r.iter_content.side_effect = body + return r + + +def _resumed_response(content: bytes) -> MagicMock: + """An HTTP 206 response continuing an interrupted download.""" + r = _mock_response(content) + r.status_code = 206 + return r + + +class TestOpenRanged: + def test_fresh_download_sends_no_range(self) -> None: + with patch("requests.get", return_value=_mock_response(b"x")) as mock_get: + resp, offset = framework_helpers._open_ranged("https://e.com/f", 0, 30) + assert offset == 0 + assert mock_get.call_args[1]["headers"] == {} + assert resp is mock_get.return_value + + def test_resume_kept_on_206(self) -> None: + with patch("requests.get", return_value=_resumed_response(b"x")): + _, offset = framework_helpers._open_ranged("https://e.com/f", 7, 30) + assert offset == 7 + + def test_resume_downgraded_on_200(self) -> None: + """A server that ignores the Range header forces a restart.""" + with patch("requests.get", return_value=_mock_response(b"x")): + _, offset = framework_helpers._open_ranged("https://e.com/f", 7, 30) + assert offset == 0 + + def test_http_error_closes_response_and_raises(self) -> None: + r = _mock_response(b"", ok=False) + with ( + patch("requests.get", return_value=r), + pytest.raises(req.HTTPError), + ): + framework_helpers._open_ranged("https://e.com/f", 0, 30) + r.close.assert_called_once() + + def test_connect_error_propagates(self) -> None: + with ( + patch("requests.get", side_effect=req.ConnectionError("refused")), + pytest.raises(req.ConnectionError), + ): + framework_helpers._open_ranged("https://e.com/f", 0, 30) + + +class TestDownloadWithResume: + def test_downloads_and_renames(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + with patch("requests.get", return_value=_mock_response(b"data")) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"data" + assert not (tmp_path / "tool.tar.gz.part").exists() + # a fresh download must not send a Range header + assert "Range" not in mock_get.call_args[1]["headers"] + + def test_mid_stream_drop_resumes_with_range(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "8"} + with patch( + "requests.get", + side_effect=[first, _resumed_response(b"5678")], + ) as mock_get: + download_with_resume("https://example.com/t", dest) + # earlier bytes were kept, remainder appended conditionally + assert dest.read_bytes() == b"12345678" + assert mock_get.call_args_list[1][1]["headers"] == { + "Range": "bytes=4-", + "If-Range": '"v1"', + } + + def test_unverifiable_drop_without_length_restarts(self, tmp_path: Path) -> None: + """A validator alone is not enough to stitch when nothing can prove + the stitched file complete (no sha/size and no content-length).""" + dest = tmp_path / "tool.tar.gz" + with patch( + "requests.get", + side_effect=[ + _interrupted_response(b"1234", etag='"v1"'), + _mock_response(b"full"), + ], + ) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"full" + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_resumed_clean_but_short_body_discarded(self, tmp_path: Path) -> None: + """A resumed stream that ends cleanly but short of the advertised + total is rejected and re-downloaded, not promoted.""" + dest = tmp_path / "tool.tar.gz" + first = _interrupted_response(b"abcd", etag='"v1"') + first.headers = {**first.headers, "content-length": "8"} + # resume ends cleanly after only 2 of the 4 missing bytes + short = _resumed_response(b"ef") + full = _mock_response(b"abcdefgh") + full.headers = {**full.headers, "content-length": "8"} + with patch("requests.get", side_effect=[first, short, full]) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"abcdefgh" + # the short stitch was discarded; the final attempt started fresh + assert "Range" not in mock_get.call_args_list[2][1]["headers"] + + def test_unverifiable_drop_without_validator_restarts(self, tmp_path: Path) -> None: + """No sha/size and no server validator: the retry must not stitch.""" + dest = tmp_path / "tool.tar.gz" + with patch( + "requests.get", + side_effect=[_interrupted_response(b"1234"), _mock_response(b"full")], + ) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"full" + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_resume_across_invocations_from_part_file(self, tmp_path: Path) -> None: + """A .part file left by a previous run is resumed, not restarted, + when sha/size verification will vouch for the stitched result.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"12345") + good = hashlib.sha256(b"12345678").hexdigest() + with patch("requests.get", return_value=_resumed_response(b"678")) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=8) + assert dest.read_bytes() == b"12345678" + assert mock_get.call_args[1]["headers"] == {"Range": "bytes=5-"} + + def test_unverifiable_leftover_part_file_ignored(self, tmp_path: Path) -> None: + """Without sha/size there is no way to vouch for a cross-run stitch, + so a leftover part file starts over.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"12345") + with patch("requests.get", return_value=_mock_response(b"fresh")) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"fresh" + assert "Range" not in mock_get.call_args[1]["headers"] + + def test_server_without_range_support_restarts(self, tmp_path: Path) -> None: + """HTTP 200 in response to a Range request truncates and restarts.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"sta") + good = hashlib.sha256(b"fresh").hexdigest() + with patch("requests.get", return_value=_mock_response(b"fresh")) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=5) + # the Range request was sent (verifiable resume) and downgraded + assert mock_get.call_args[1]["headers"] == {"Range": "bytes=3-"} + assert dest.read_bytes() == b"fresh" + + def test_size_only_leftover_part_restarts(self, tmp_path: Path) -> None: + """A size alone cannot detect a same-length content change on the + server, so a cross-run part without sha256 restarts from zero.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"12") + with patch("requests.get", return_value=_mock_response(b"1234")) as mock_get: + download_with_resume("https://example.com/t", dest, size=4) + assert "Range" not in mock_get.call_args[1]["headers"] + assert dest.read_bytes() == b"1234" + + def test_size_only_in_run_drop_resumes_with_validator(self, tmp_path: Path) -> None: + """Within a run the If-Range validator proves identity, so size-only + callers still resume mid-stream drops.""" + dest = tmp_path / "tool.tar.gz" + with patch( + "requests.get", + side_effect=[ + _interrupted_response(b"12", etag='"v1"'), + _resumed_response(b"34"), + ], + ) as mock_get: + download_with_resume("https://example.com/t", dest, size=4) + assert dest.read_bytes() == b"1234" + assert mock_get.call_args_list[1][1]["headers"] == { + "Range": "bytes=2-", + "If-Range": '"v1"', + } + + def test_unverifiable_download_logged( + self, tmp_path: Path, caplog: pytest.LogCaptureFixture + ) -> None: + """No sha, no size, no content-length: the download is promoted with + a debug note (routine for e.g. the constraints host, so not a + warning) that completeness could not be verified.""" + dest = tmp_path / "tool.tar.gz" + with ( + caplog.at_level(logging.DEBUG, logger="esphome.framework_helpers"), + patch("requests.get", return_value=_mock_response(b"data")), + ): + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"data" + assert "without any way to verify completeness" in caplog.text + + def test_416_promotes_complete_part_when_size_unknown(self, tmp_path: Path) -> None: + """sha256-only caller with a byte-complete part file: the server's + 416 confirms nothing is missing, verification promotes in place, and + the 416 must not loop as a retryable error.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"data") + good = hashlib.sha256(b"data").hexdigest() + r416 = _mock_response(b"", ok=False) + r416.status_code = 416 + with patch("requests.get", return_value=r416) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good) + assert mock_get.call_count == 1 + r416.close.assert_called_once() + assert dest.read_bytes() == b"data" + + def test_416_with_corrupt_part_discards_and_redownloads( + self, tmp_path: Path + ) -> None: + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"bad!") + good = hashlib.sha256(b"data").hexdigest() + r416 = _mock_response(b"", ok=False) + r416.status_code = 416 + with patch( + "requests.get", side_effect=[r416, _mock_response(b"data")] + ) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good) + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + assert dest.read_bytes() == b"data" + + def test_hash_mismatch_discards_and_retries(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + good = hashlib.sha256(b"good").hexdigest() + with patch( + "requests.get", + side_effect=[_mock_response(b"bad!"), _mock_response(b"good")], + ) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + assert dest.read_bytes() == b"good" + # the corrupt part file was discarded, so the retry starts fresh + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_size_mismatch_discards_part(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + with ( + patch("requests.get", return_value=_mock_response(b"xx")), + pytest.raises(EsphomeError, match="after 2 attempts"), + ): + download_with_resume("https://example.com/t", dest, size=99, attempts=2) + assert not (tmp_path / "tool.tar.gz.part").exists() + assert not dest.exists() + + def test_attempts_exhausted_keeps_part_file(self, tmp_path: Path) -> None: + """Mid-stream failures keep the partial file so a later run resumes.""" + dest = tmp_path / "tool.tar.gz" + first = _interrupted_response(b"12", etag='"v1"') + first.headers = {**first.headers, "content-length": "4"} + second = _interrupted_response(b"34") + second.status_code = 206 + with ( + patch("requests.get", side_effect=[first, second]), + pytest.raises(EsphomeError, match="after 2 attempts"), + ): + download_with_resume("https://example.com/t", dest, attempts=2) + assert (tmp_path / "tool.tar.gz.part").read_bytes() == b"1234" + + def test_multiple_drops_accumulate_across_attempts(self, tmp_path: Path) -> None: + """Each attempt appends its bytes; three partial responses complete + the file.""" + dest = tmp_path / "tool.tar.gz" + first = _interrupted_response(b"ab", etag='"v1"') + first.headers = {**first.headers, "content-length": "6"} + second = _interrupted_response(b"cd") + second.status_code = 206 + third = _resumed_response(b"ef") + with patch( + "requests.get", + side_effect=[first, second, third], + ) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"abcdef" + expected = {"Range": "bytes=2-", "If-Range": '"v1"'} + assert mock_get.call_args_list[1][1]["headers"] == expected + expected = {"Range": "bytes=4-", "If-Range": '"v1"'} + assert mock_get.call_args_list[2][1]["headers"] == expected + + def test_connect_error_then_success(self, tmp_path: Path) -> None: + """A connect error (no response at all) consumes an attempt and the + next attempt succeeds.""" + dest = tmp_path / "tool.tar.gz" + with patch( + "requests.get", + side_effect=[req.ConnectionError("refused"), _mock_response(b"data")], + ): + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"data" + + def test_http_error_keeps_part_file(self, tmp_path: Path) -> None: + """A transient HTTP error (e.g. 503) must not discard resume state.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"keep") + error = _mock_response(b"", ok=False) + error.status_code = 503 + with ( + patch("requests.get", return_value=error), + pytest.raises(EsphomeError, match="after 1 attempts"), + ): + download_with_resume("https://example.com/t", dest, attempts=1) + assert (tmp_path / "tool.tar.gz.part").read_bytes() == b"keep" + + def test_creates_missing_parent_directories(self, tmp_path: Path) -> None: + dest = tmp_path / "dist" / "nested" / "tool.tar.gz" + with patch("requests.get", return_value=_mock_response(b"data")): + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"data" + + def test_verifies_both_size_and_sha(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get", return_value=_mock_response(b"data")): + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + assert dest.read_bytes() == b"data" + + def test_corrupt_partial_resumed_then_discarded_then_redownloaded( + self, tmp_path: Path + ) -> None: + """The full recovery cycle for a corrupted partial download: the + resume completes it, verification fails, the poisoned part file is + discarded, and the next attempt re-downloads from scratch.""" + dest = tmp_path / "tool.tar.gz" + # a previous run left a corrupted 4-byte prefix behind + (tmp_path / "tool.tar.gz.part").write_bytes(b"BAD!") + good = hashlib.sha256(b"data66").hexdigest() + with patch( + "requests.get", + side_effect=[ + _resumed_response(b"66"), # resume "completes" the bad part + _mock_response(b"data66"), # clean retry from zero + ], + ) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=6) + # first attempt resumed at the corrupt offset, failed verification; + # second attempt started fresh (no Range header) and succeeded + assert mock_get.call_args_list[0][1]["headers"] == {"Range": "bytes=4-"} + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + assert dest.read_bytes() == b"data66" + assert not (tmp_path / "tool.tar.gz.part").exists() + + def test_existing_dest_passing_verification_kept(self, tmp_path: Path) -> None: + """A dest completed by an earlier run is reused without any request.""" + dest = tmp_path / "tool.tar.gz" + dest.write_bytes(b"data") + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get") as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + mock_get.assert_not_called() + assert dest.read_bytes() == b"data" + + @pytest.mark.parametrize( + "stale", + [ + pytest.param(b"corrupt!", id="wrong-size"), + pytest.param(b"bad!", id="right-size-wrong-hash"), + ], + ) + def test_existing_dest_failing_verification_redownloaded( + self, tmp_path: Path, stale: bytes + ) -> None: + dest = tmp_path / "tool.tar.gz" + dest.write_bytes(stale) + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get", return_value=_mock_response(b"data")): + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + assert dest.read_bytes() == b"data" + + def test_existing_dest_with_size_only_kept(self, tmp_path: Path) -> None: + dest = tmp_path / "tool.tar.gz" + dest.write_bytes(b"data") + with patch("requests.get") as mock_get: + download_with_resume("https://example.com/t", dest, size=4) + mock_get.assert_not_called() + + def test_existing_dest_with_sha_only_kept(self, tmp_path: Path) -> None: + """sha-only verification also authorizes reusing a completed dest.""" + dest = tmp_path / "tool.tar.gz" + dest.write_bytes(b"data") + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get") as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good) + mock_get.assert_not_called() + + def test_meta_write_failure_is_best_effort(self, tmp_path: Path) -> None: + """A failure to persist the resume sidecar must not fail the + download itself.""" + dest = tmp_path / "f.tar.xz" + first = _mock_response(b"data") + first.headers = {**first.headers, "ETag": '"v1"', "content-length": "4"} + with ( + patch("requests.get", return_value=first), + patch.object(Path, "write_text", side_effect=OSError("read-only")), + ): + download_with_resume("https://example.com/f", dest) + assert dest.read_bytes() == b"data" + + def test_meta_sidecar_written_and_removed(self, tmp_path: Path) -> None: + """The validator sidecar appears while downloading and is cleaned up + with the promotion.""" + dest = tmp_path / "f.tar.xz" + meta = tmp_path / "f.tar.xz.part.meta" + seen: list[bool] = [] + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "8"} + responses = [first] + + def get(*args: object, **kwargs: object) -> MagicMock: + if responses: + return responses.pop(0) + # the resume request: the sidecar written by the first response + # must already be on disk at this point + seen.append(meta.is_file()) + return _resumed_response(b"5678") + + with patch("requests.get", side_effect=get): + download_with_resume("https://example.com/f", dest) + assert dest.read_bytes() == b"12345678" + assert seen == [True] # sidecar existed during the resume attempt + assert not meta.exists() # cleaned up on success + + def test_locked_promotion_keeps_verified_part(self, tmp_path: Path) -> None: + """A rename that stays blocked (e.g. a long-lived Windows file lock) + must not delete the verified download; the next attempt retries just + the rename without touching the network.""" + dest = tmp_path / "tool.tar.gz" + good = hashlib.sha256(b"data").hexdigest() + with ( + patch("requests.get", return_value=_mock_response(b"data")) as mock_get, + patch( + "esphome.framework_helpers._rename_with_retry", + side_effect=[PermissionError("locked"), None], + ) as rename, + ): + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + # one download; the second attempt only redid the rename + assert mock_get.call_count == 1 + assert rename.call_count == 2 + + def test_locked_promotion_exhausted_keeps_part_for_next_run( + self, tmp_path: Path + ) -> None: + dest = tmp_path / "tool.tar.gz" + good = hashlib.sha256(b"data").hexdigest() + with ( + patch("requests.get", return_value=_mock_response(b"data")), + patch( + "esphome.framework_helpers._rename_with_retry", + side_effect=PermissionError("locked"), + ), + pytest.raises(EsphomeError, match="after 1 attempts"), + ): + download_with_resume( + "https://example.com/t", dest, sha256=good, size=4, attempts=1 + ) + # the verified bytes survive for the next run + assert (tmp_path / "tool.tar.gz.part").read_bytes() == b"data" + + def test_meta_sidecar_resumes_across_runs_without_sha(self, tmp_path: Path) -> None: + """A later run resumes an unfinished download using the validator the + first run stored — the cross-run fix for the framework tarball.""" + dest = tmp_path / "f.tar.xz" + (tmp_path / "f.tar.xz.part").write_bytes(b"1234") + (tmp_path / "f.tar.xz.part.meta").write_text( + json.dumps( + {"url": "https://example.com/f", "validator": '"v1"', "total": 8} + ) + ) + with patch("requests.get", return_value=_resumed_response(b"5678")) as mock_get: + download_with_resume("https://example.com/f", dest) + assert dest.read_bytes() == b"12345678" + assert mock_get.call_args[1]["headers"] == { + "Range": "bytes=4-", + "If-Range": '"v1"', + } + + def test_meta_sidecar_for_other_url_ignored(self, tmp_path: Path) -> None: + """Metadata from a different mirror URL must not authorize a stitch.""" + dest = tmp_path / "f.tar.xz" + (tmp_path / "f.tar.xz.part").write_bytes(b"1234") + (tmp_path / "f.tar.xz.part.meta").write_text( + json.dumps({"url": "https://other.com/f", "validator": '"v1"', "total": 8}) + ) + full = _mock_response(b"12345678") + with patch("requests.get", return_value=full) as mock_get: + download_with_resume("https://example.com/f", dest) + assert "Range" not in mock_get.call_args[1]["headers"] + assert dest.read_bytes() == b"12345678" + + def test_complete_part_file_promoted_without_network(self, tmp_path: Path) -> None: + """A .part holding every byte (killed between write and rename) is + verified in place and promoted; no request is made, so no 416 loop.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"data") + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get") as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + mock_get.assert_not_called() + assert dest.read_bytes() == b"data" + + def test_complete_but_corrupt_part_file_redownloaded(self, tmp_path: Path) -> None: + """A full-size .part with a wrong hash is discarded and re-downloaded + from scratch.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"bad!") + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get", return_value=_mock_response(b"data")) as mock_get: + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + assert "Range" not in mock_get.call_args[1]["headers"] + assert dest.read_bytes() == b"data" + + def test_oversized_part_file_discarded(self, tmp_path: Path) -> None: + """A .part larger than the expected size fails verification and is + replaced by a fresh download.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"toolong") + good = hashlib.sha256(b"data").hexdigest() + with patch("requests.get", return_value=_mock_response(b"data")): + download_with_resume("https://example.com/t", dest, sha256=good, size=4) + assert dest.read_bytes() == b"data" + + def test_malformed_content_length_degrades_gracefully(self, tmp_path: Path) -> None: + """A garbage Content-Length must not crash the attempt; it means + "unknown", so a drop restarts instead of stitching and a clean + download still succeeds.""" + dest = tmp_path / "tool.tar.gz" + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "explode"} + retry = _mock_response(b"full") + retry.headers = {**retry.headers, "content-length": "explode"} + with patch("requests.get", side_effect=[first, retry]) as mock_get: + download_with_resume("https://example.com/t", dest) + assert dest.read_bytes() == b"full" + # unknown length -> completeness unprovable -> no resume attempted + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_zero_byte_part_file_sends_no_range(self, tmp_path: Path) -> None: + """An empty leftover part file is a fresh download, not a resume.""" + dest = tmp_path / "tool.tar.gz" + (tmp_path / "tool.tar.gz.part").write_bytes(b"") + with patch("requests.get", return_value=_mock_response(b"data")) as mock_get: + download_with_resume("https://example.com/t", dest) + assert mock_get.call_args[1]["headers"] == {} + assert dest.read_bytes() == b"data" + + class TestDownloadFromMirrors: def test_success_returns_url_and_writes_content(self, tmp_path: Path) -> None: target = tmp_path / "out.bin" @@ -546,7 +1110,101 @@ class TestDownloadFromMirrors: ) assert mock_get.call_args[0][0] == "https://example.com/1.2.3.bin" - def test_falls_back_to_second_mirror(self, tmp_path: Path) -> None: + def test_template_with_missing_substitution_is_skipped( + self, tmp_path: Path + ) -> None: + """A template referencing an unavailable substitution is skipped, not + formatted into a bogus URL (e.g. SHORT_VERSION only exists for x.y.0 + framework versions).""" + with patch( + "requests.get", + return_value=_mock_response(b"x"), + ) as mock_get: + url = download_from_mirrors( + [ + "https://example.com/{SHORT_VERSION}.bin", + "https://example.com/{VERSION}.bin", + ], + {"VERSION": "1.2.3"}, + tmp_path / "out.bin", + ) + assert url == "https://example.com/1.2.3.bin" + assert mock_get.call_count == 1 + + def test_all_templates_skipped_raises_esphome_error(self, tmp_path: Path) -> None: + with ( + patch("requests.get") as mock_get, + pytest.raises(EsphomeError, match="No mirror URL template matched") as ei, + ): + download_from_mirrors( + ["https://example.com/{MISSING}.bin"], + {"VERSION": "1.2.3"}, + tmp_path / "out.bin", + ) + mock_get.assert_not_called() + # The skipped template and its missing substitution are named + assert "https://example.com/{MISSING}.bin" in str(ei.value) + assert "MISSING" in str(ei.value) + + def test_failure_message_includes_skipped_templates(self, tmp_path: Path) -> None: + """When downloads fail, templates that were skipped for missing + substitutions are also listed so a typo'd custom mirror is + attributable.""" + with ( + patch( + "requests.get", + return_value=_mock_response(b"", ok=False), + ), + pytest.raises(EsphomeError, match="all mirrors") as ei, + ): + download_from_mirrors( + [ + "https://example.com/{TYPO}.bin", + "https://example.com/{VERSION}.bin", + ], + {"VERSION": "1.2.3"}, + tmp_path / "out.bin", + ) + message = str(ei.value) + assert "https://example.com/1.2.3.bin" in message + assert ( + "https://example.com/{TYPO}.bin\n not applicable (TYPO not available)" + in message + ) + + def test_malformed_template_warns_and_is_reported( + self, tmp_path: Path, caplog: pytest.LogCaptureFixture + ) -> None: + """A structurally malformed template is an authoring error: warned + about even when another mirror succeeds, and named in the aggregate + error when everything fails.""" + with ( + patch("requests.get", return_value=_mock_response(b"x")), + caplog.at_level(logging.WARNING, logger="esphome.framework_helpers"), + ): + url = download_from_mirrors( + ["https://example.com/{oops.bin", "https://example.com/{VERSION}.bin"], + {"VERSION": "1.2.3"}, + tmp_path / "out.bin", + ) + assert url == "https://example.com/1.2.3.bin" + assert "malformed mirror URL template" in caplog.text + + with ( + patch("requests.get", return_value=_mock_response(b"", ok=False)), + pytest.raises(EsphomeError, match="all mirrors") as ei, + ): + download_from_mirrors( + ["https://example.com/{oops.bin", "https://example.com/{VERSION}.bin"], + {"VERSION": "1.2.3"}, + tmp_path / "out.bin", + ) + assert "https://example.com/{oops.bin\n skipped (ValueError(" in str( + ei.value + ) + + def test_falls_back_to_second_mirror(self) -> None: + buf = io.BytesIO() with patch( "requests.get", side_effect=[_mock_response(b"", ok=False), _mock_response(b"second")], @@ -554,20 +1212,169 @@ class TestDownloadFromMirrors: url = download_from_mirrors( ["https://mirror1.com/f", "https://mirror2.com/f"], {}, - tmp_path / "out.bin", + buf, ) assert url == "https://mirror2.com/f" - assert (tmp_path / "out.bin").read_bytes() == b"second" + assert buf.getvalue() == b"second" - def test_all_mirrors_fail_reraises_last_exception(self, tmp_path: Path) -> None: + def test_mid_stream_drop_resumes_same_mirror(self) -> None: + """A mid-stream failure retries the same mirror with Range and + If-Range headers, keeping the bytes already received, before falling + to the next.""" + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "8"} + buf = io.BytesIO() + with patch( + "requests.get", + side_effect=[first, _resumed_response(b"5678")], + ) as mock_get: + url = download_from_mirrors( + ["https://mirror1.com/f", "https://mirror2.com/f"], + {}, + buf, + ) + assert url == "https://mirror1.com/f" + assert buf.getvalue() == b"12345678" + assert mock_get.call_count == 2 + assert mock_get.call_args_list[1][0][0] == "https://mirror1.com/f" + # the resume is conditional on the content being unchanged + assert mock_get.call_args_list[1][1]["headers"] == { + "Range": "bytes=4-", + "If-Range": '"v1"', + } + + def test_mid_stream_drop_without_validator_restarts(self) -> None: + """A server offering no ETag/Last-Modified cannot be resumed safely; + the retry restarts from zero instead of stitching unverified bytes.""" + buf = io.BytesIO() + with patch( + "requests.get", + side_effect=[_interrupted_response(b"1234"), _mock_response(b"full")], + ) as mock_get: + download_from_mirrors(["https://mirror1.com/f"], {}, buf) + assert buf.getvalue() == b"full" + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_drop_after_last_byte_recovers_via_416(self) -> None: + """A connection drop after the final body byte leaves a complete file; + the retry's 416 answer plus the length check turn it into success + instead of a wasted refetch.""" + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "4"} + r416 = _mock_response(b"", ok=False) + r416.status_code = 416 + buf = io.BytesIO() + with patch("requests.get", side_effect=[first, r416]) as mock_get: + url = download_from_mirrors(["https://mirror1.com/f"], {}, buf) + assert url == "https://mirror1.com/f" + assert buf.getvalue() == b"1234" + assert mock_get.call_count == 2 + + def test_mirror_drop_without_length_restarts(self) -> None: + """With no content-length there is no way to prove a stitched file + complete, so the retry restarts even though a validator exists.""" + buf = io.BytesIO() + with patch( + "requests.get", + side_effect=[ + _interrupted_response(b"1234", etag='"v1"'), + _mock_response(b"full"), + ], + ) as mock_get: + download_from_mirrors(["https://mirror1.com/f"], {}, buf) + assert buf.getvalue() == b"full" + assert "Range" not in mock_get.call_args_list[1][1]["headers"] + + def test_path_target_resumes_across_runs(self, tmp_path: Path) -> None: + """A path target routes through download_with_resume: a part file and + metadata from a previous run resume instead of restarting.""" + dest = tmp_path / "idf.tar.xz" + (tmp_path / "idf.tar.xz.part").write_bytes(b"1234") + (tmp_path / "idf.tar.xz.part.meta").write_text( + json.dumps( + {"url": "https://mirror1.com/f", "validator": '"v1"', "total": 8} + ) + ) + with patch("requests.get", return_value=_resumed_response(b"5678")) as mock_get: + url = download_from_mirrors(["https://mirror1.com/f"], {}, dest) + assert url == "https://mirror1.com/f" + assert dest.read_bytes() == b"12345678" + assert mock_get.call_args[1]["headers"] == { + "Range": "bytes=4-", + "If-Range": '"v1"', + } + + def test_path_target_falls_back_to_next_mirror(self, tmp_path: Path) -> None: + dest = tmp_path / "idf.tar.xz" + with patch( + "requests.get", + side_effect=[req.ConnectionError("down"), _mock_response(b"data")], + ): + url = download_from_mirrors( + ["https://mirror1.com/f", "https://mirror2.com/f"], {}, dest + ) + assert url == "https://mirror2.com/f" + assert dest.read_bytes() == b"data" + + def test_resumed_short_body_fails_length_check(self) -> None: + """A stitched file whose final length disagrees with the advertised + total is rejected instead of reported as success.""" + first = _interrupted_response(b"1234", etag='"v1"') + first.headers = {**first.headers, "content-length": "8"} + # the resume ends early (5 of 8 bytes); the poisoned part is then + # discarded and the fresh retry also delivers a short body + short_resume = _resumed_response(b"5") + short_fresh = _mock_response(b"56") + short_fresh.headers = {**short_fresh.headers, "content-length": "8"} + buf = io.BytesIO() + with ( + patch("requests.get", side_effect=[first, short_resume, short_fresh]), + pytest.raises(EsphomeError, match="all mirrors"), + ): + download_from_mirrors(["https://mirror1.com/f"], {}, buf) + + def test_failed_mirror_leftovers_not_kept_for_next_mirror(self) -> None: + """Bytes from a mirror that failed all attempts must not leak into the + next mirror's download (no bogus Range request, fresh content).""" + exhausted = [_interrupted_response(b"AAAA", etag='"a1"')] + for _ in range(2): + r = _interrupted_response(b"BB") + r.status_code = 206 + exhausted.append(r) + buf = io.BytesIO() + with patch( + "requests.get", + side_effect=exhausted + [_mock_response(b"clean")], + ) as mock_get: + url = download_from_mirrors( + ["https://mirror1.com/f", "https://mirror2.com/f"], + {}, + buf, + ) + assert url == "https://mirror2.com/f" + assert buf.getvalue() == b"clean" + # the second mirror starts fresh, without a Range header + assert mock_get.call_args_list[3][0][0] == "https://mirror2.com/f" + assert "Range" not in mock_get.call_args_list[3][1]["headers"] + + def test_all_mirrors_fail_raises_error_listing_every_attempt(self) -> None: with ( patch( "requests.get", return_value=_mock_response(b"", ok=False), ), - pytest.raises(req.HTTPError), + pytest.raises(EsphomeError, match="all mirrors") as excinfo, ): - download_from_mirrors(["https://example.com/f"], {}, tmp_path / "out.bin") + download_from_mirrors( + ["https://mirror1.com/f", "https://mirror2.com/f"], + {}, + io.BytesIO(), + ) + # Every attempted URL appears in the message, and the first mirror's + # exception (the primary URL, usually the one that matters) is chained. + assert "https://mirror1.com/f" in str(excinfo.value) + assert "https://mirror2.com/f" in str(excinfo.value) + assert isinstance(excinfo.value.__cause__, req.HTTPError) def test_empty_mirrors_raises_value_error(self, tmp_path: Path) -> None: with pytest.raises(ValueError, match="empty mirrors list"): @@ -626,8 +1433,10 @@ def test_importing_framework_helpers_does_not_import_requests() -> None: [ sys.executable, "-c", - "import sys\nimport esphome.framework_helpers\n" - "print('\\n'.join(sys.modules))", + ( + "import sys\nimport esphome.framework_helpers\n" + "print('\\n'.join(sys.modules))" + ), ], capture_output=True, text=True, diff --git a/tests/unit_tests/test_git.py b/tests/unit_tests/test_git.py index 62d2344069..ec1becf3e8 100644 --- a/tests/unit_tests/test_git.py +++ b/tests/unit_tests/test_git.py @@ -1,15 +1,22 @@ """Tests for git.py module.""" +from collections.abc import Callable +import errno +import logging import os from pathlib import Path +import subprocess +import threading import time from typing import Any from unittest.mock import Mock, patch +from filelock import FileLock import pytest from esphome import git -from esphome.core import CORE, TimePeriodSeconds +import esphome.config_validation as cv +from esphome.core import CORE, EsphomeError, TimePeriodSeconds from esphome.git import GitCommandError @@ -19,6 +26,15 @@ def _compute_repo_dir(url: str, ref: str | None, domain: str) -> Path: return git._compute_destination_path(key, domain) +# The tests must probe the exact location the implementation uses +_marker_path = git._clone_complete_marker_path + + +def _mark_clone_complete(repo_dir: Path) -> None: + """Write the completion marker so a hand-made repo dir is treated as valid.""" + _marker_path(repo_dir).write_text("test") + + def _setup_old_repo(repo_dir: Path, days_old: int = 2) -> None: """Helper to set up a git repo directory structure with an old timestamp. @@ -30,6 +46,7 @@ def _setup_old_repo(repo_dir: Path, days_old: int = 2) -> None: repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) # Create FETCH_HEAD file with old timestamp fetch_head = git_dir / "FETCH_HEAD" @@ -54,6 +71,52 @@ def _get_git_command_type(cmd: list[str]) -> str | None: return None +def _simulate_cloned_repo(repo_dir: Path) -> None: + """Create the directory structure a successful git clone would leave.""" + repo_dir.mkdir(parents=True, exist_ok=True) + (repo_dir / ".git").mkdir(exist_ok=True) + + +def _make_clone_side_effect( + repo_dir: Path, + gitmodules: bool = False, + on_clone: Callable[[], None] | None = None, +) -> Callable[..., str]: + """Return a run_git_command side effect whose clone creates the repo dir. + + With ``gitmodules`` the cloned repo also declares submodules. ``on_clone`` + runs at clone time before the repo dir appears, so a test can probe or + block mid-clone. + """ + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "clone": + if on_clone is not None: + on_clone() + _simulate_cloned_repo(repo_dir) + if gitmodules: + (repo_dir / ".gitmodules").write_text("test") + return "" + + return git_command_side_effect + + +def _submodule_calls(mock: Mock) -> list[Any]: + """Return the mock's `git submodule` calls.""" + return [ + c for c in mock.call_args_list if _get_git_command_type(c[0][0]) == "submodule" + ] + + +def _assert_submodule_runs_without_isolation(call: Any, repo_dir: Path) -> None: + """Assert a git submodule call ran with plain cwd, not GIT_DIR/GIT_WORK_TREE + isolation, which breaks the submodule porcelain on some installations.""" + assert call.kwargs.get("git_dir") is None + assert call.kwargs.get("cwd") == repo_dir + + def test_run_git_command_success(tmp_path: Path) -> None: """Test that run_git_command returns output on success.""" # Create a simple git repo to test with @@ -70,6 +133,22 @@ def test_run_git_command_success(tmp_path: Path) -> None: assert isinstance(result, str) +def test_run_git_command_debug_log_redacts_credentials( + tmp_path: Path, mock_subprocess_run: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """Embedded URL credentials never reach the debug log; -v output is + routinely pasted into public issues. subprocess is mocked so no real + git ever sees the URL (the path is not creatable on Windows).""" + mock_subprocess_run.return_value = Mock(returncode=0, stdout=b"", stderr=b"") + with caplog.at_level(logging.DEBUG, logger="esphome.git"): + git.run_git_command( + ["git", "clone", "https://user:hunter2@github.com/test/repo"], + cwd=tmp_path, + ) + assert "hunter2" not in caplog.text + assert "://***@github.com/test/repo" in caplog.text + + def test_run_git_command_with_git_dir_isolation( tmp_path: Path, mock_subprocess_run: Mock ) -> None: @@ -86,10 +165,17 @@ def test_run_git_command_with_git_dir_isolation( stderr=b"", ) - result = git.run_git_command( - ["git", "rev-parse", "HEAD"], - git_dir=repo_dir, - ) + # Ambient repo-scoping vars simulate a git hook invoking ESPHome; an + # ambient GIT_INDEX_FILE surviving into a git_dir invocation fails + # silently (git operates on the caller's index and exits 0). + with patch.dict( + os.environ, + {"GIT_INDEX_FILE": "/caller/index", "GIT_OBJECT_DIRECTORY": "/caller/objects"}, + ): + result = git.run_git_command( + ["git", "rev-parse", "HEAD"], + git_dir=repo_dir, + ) # Verify subprocess.run was called assert mock_subprocess_run.called @@ -101,6 +187,9 @@ def test_run_git_command_with_git_dir_isolation( assert "GIT_WORK_TREE" in env assert env["GIT_DIR"] == str(repo_dir / ".git") assert env["GIT_WORK_TREE"] == str(repo_dir) + # The ambient scoping vars must be stripped, not passed through. + assert "GIT_INDEX_FILE" not in env + assert "GIT_OBJECT_DIRECTORY" not in env assert result == "test output" @@ -186,6 +275,89 @@ def test_run_git_command_without_git_dir(mock_subprocess_run: Mock) -> None: assert result == "Cloning into 'test_repo'..." +@pytest.mark.parametrize("relative", [False, True], ids=["absolute", "relative"]) +def test_run_git_command_with_cwd_runs_in_dir_without_isolation( + tmp_path: Path, + mock_subprocess_run: Mock, + monkeypatch: pytest.MonkeyPatch, + relative: bool, +) -> None: + """The cwd parameter sets the working directory without GIT_DIR/GIT_WORK_TREE. + + Ambient GIT_DIR/GIT_WORK_TREE (e.g. from a git hook or CI wrapper) must be + stripped too, and GIT_CEILING_DIRECTORIES must stop git from walking up to + an enclosing repository if the target repo's .git is missing or corrupt. + Git silently ignores a relative ceiling entry, so the variable must come + out absolute even when the given cwd is relative. + """ + repo_dir = tmp_path / "test_repo" + repo_dir.mkdir() + if relative: + monkeypatch.chdir(tmp_path) + cwd_arg = Path("test_repo") + else: + cwd_arg = repo_dir + + mock_subprocess_run.return_value = Mock( + returncode=0, + stdout=b"test output", + stderr=b"", + ) + + with patch.dict( + os.environ, + { + "GIT_DIR": "/ambient/.git", + "GIT_WORK_TREE": "/ambient", + "GIT_INDEX_FILE": "/ambient/.git/index", + }, + ): + result = git.run_git_command(["git", "submodule", "update"], cwd=cwd_arg) + + call_args = mock_subprocess_run.call_args + env = call_args[1]["env"] + assert "GIT_DIR" not in env + assert "GIT_WORK_TREE" not in env + assert "GIT_INDEX_FILE" not in env + ceiling = Path(env["GIT_CEILING_DIRECTORIES"]) + assert ceiling.is_absolute() + assert ceiling.samefile(tmp_path) + assert call_args[1]["cwd"] == cwd_arg + assert result == "test output" + + +def test_run_git_command_raises_on_nonfatal_stderr( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """Nonzero exit with stderr lacking a fatal: prefix raises with full stderr.""" + mock_subprocess_run.return_value = Mock( + returncode=1, + stdout=b"", + stderr=b"error: pathspec 'nope' did not match any file(s)\n", + ) + + with pytest.raises(GitCommandError, match="did not match"): + git.run_git_command(["git", "checkout", "nope"], git_dir=tmp_path) + + +def test_run_git_command_raises_on_nonzero_exit_without_stderr( + tmp_path: Path, mock_subprocess_run: Mock +) -> None: + """A nonzero exit must raise even when git printed nothing to stderr. + + Silent nonzero exits were previously treated as success, which is how + broken checkouts could be cached as complete. + """ + mock_subprocess_run.return_value = Mock( + returncode=1, + stdout=b"", + stderr=b"", + ) + + with pytest.raises(GitCommandError, match="exited with code 1"): + git.run_git_command(["git", "submodule", "update"], cwd=tmp_path) + + def test_run_git_command_without_git_dir_raises_error( mock_subprocess_run: Mock, ) -> None: @@ -217,6 +389,7 @@ def test_clone_or_update_with_never_refresh( repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) # Create FETCH_HEAD file with current timestamp fetch_head = git_dir / "FETCH_HEAD" @@ -250,6 +423,7 @@ def test_clone_or_update_skips_when_core_skip_external_update( repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) (git_dir / "FETCH_HEAD").write_text("test") CORE.skip_external_update = True @@ -281,6 +455,7 @@ def test_clone_or_update_with_refresh_updates_old_repo( repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) # Create FETCH_HEAD file with old timestamp (2 days ago) fetch_head = git_dir / "FETCH_HEAD" @@ -314,7 +489,7 @@ def test_clone_or_update_with_refresh_updates_old_repo( def test_clone_or_update_with_refresh_skips_fresh_repo( - tmp_path: Path, mock_run_git_command: Mock + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture ) -> None: """Test that refresh doesn't update fresh repos.""" # Set up CORE.config_path so data_dir uses tmp_path @@ -329,6 +504,7 @@ def test_clone_or_update_with_refresh_skips_fresh_repo( repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) # Create FETCH_HEAD file with recent timestamp (1 hour ago) fetch_head = git_dir / "FETCH_HEAD" @@ -338,20 +514,74 @@ def test_clone_or_update_with_refresh_skips_fresh_repo( # Set modification time to 1 hour ago os.utime(fetch_head, (recent_time, recent_time)) + # Freeze the clock at 1 hour (plus a margin larger than any filesystem + # mtime rounding) after the mtime so the logged countdown is deterministic + frozen_now = fetch_head.stat().st_mtime + 3600.5 + # Call with refresh=1d (1 day) refresh = TimePeriodSeconds(days=1) - result_dir, revert = git.clone_or_update( - url=url, - ref=ref, - refresh=refresh, - domain=domain, - ) + with ( + patch("esphome.git.time.time", return_value=frozen_now), + caplog.at_level(logging.INFO, logger="esphome.git"), + ): + result_dir, revert = git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) # Should NOT call git fetch since repo is fresh mock_run_git_command.assert_not_called() assert result_dir == repo_dir assert revert is None + # Should tell the user the update was skipped and when the next refresh is + assert f"Skipping update for {url}@{ref}" in caplog.text + assert "will refresh on the next run after 22h 59min" in caplog.text + assert "(refresh: 1d)" in caplog.text + + +def test_clone_or_update_with_refresh_never_logs_refresh_disabled( + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """Test that a config-level refresh: never skips without a countdown log.""" + # Set up CORE.config_path so data_dir uses tmp_path + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = None + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Create the git repo directory structure + repo_dir.mkdir(parents=True) + git_dir = repo_dir / ".git" + git_dir.mkdir() + _mark_clone_complete(repo_dir) + + # Create FETCH_HEAD file with current timestamp + fetch_head = git_dir / "FETCH_HEAD" + fetch_head.write_text("test") + + # refresh: never validates to 365250 days, not the NEVER_REFRESH sentinel + refresh = cv.source_refresh("never") + with caplog.at_level(logging.DEBUG, logger="esphome.git"): + result_dir, revert = git.clone_or_update( + url=url, + ref=ref, + refresh=refresh, + domain=domain, + ) + + mock_run_git_command.assert_not_called() + assert result_dir == repo_dir + assert revert is None + + # Should log refresh disabled at debug level, not a countdown + assert f"Skipping update for {url}@{ref} (refresh disabled)" in caplog.text + assert "will refresh on the next run" not in caplog.text + def test_clone_or_update_clones_missing_repo( tmp_path: Path, mock_run_git_command: Mock @@ -371,6 +601,8 @@ def test_clone_or_update_clones_missing_repo( # repo_dir should NOT exist assert not repo_dir.exists() + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + # Test with NEVER_REFRESH - should still clone since repo doesn't exist result_dir, revert = git.clone_or_update( url=url, @@ -405,6 +637,7 @@ def test_clone_or_update_with_none_refresh_always_updates( repo_dir.mkdir(parents=True) git_dir = repo_dir / ".git" git_dir.mkdir() + _mark_clone_complete(repo_dir) # Create FETCH_HEAD file with very recent timestamp (1 second ago) fetch_head = git_dir / "FETCH_HEAD" @@ -486,6 +719,9 @@ def test_clone_or_update_recovers_from_git_failures( # Default successful responses if cmd_type == "rev-parse": return "abc123" + if cmd_type == "clone": + # Simulate the recovery re-clone creating the repo directory + _simulate_cloned_repo(repo_dir) return "" mock_run_git_command.side_effect = git_command_side_effect @@ -813,6 +1049,273 @@ def test_clone_or_update_stale_clone_is_retried_after_cleanup( assert call_count["fetch"] == 2 +def test_clone_or_update_recloned_when_marker_missing_with_never_refresh( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """A repo dir without the completion marker is an interrupted clone. + + It must be removed and re-cloned even with NEVER_REFRESH, which would + otherwise trust the broken directory forever. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "1.8.4" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + # Simulate an interrupted clone: directory exists, no marker + repo_dir.mkdir(parents=True) + (repo_dir / ".git").mkdir() + + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + result_dir, _ = git.clone_or_update( + url=url, ref=ref, refresh=git.NEVER_REFRESH, domain=domain + ) + + clone_calls = [c for c in mock_run_git_command.call_args_list if "clone" in c[0][0]] + assert len(clone_calls) == 1 + assert result_dir == repo_dir + # The fresh clone completed, so the marker must now be present + assert _marker_path(repo_dir).is_file() + + +def test_clone_or_update_recloned_when_marker_missing_with_skip_external_update( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """skip_external_update must not preserve an interrupted clone.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + repo_dir.mkdir(parents=True) + (repo_dir / ".git").mkdir() + + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + CORE.skip_external_update = True + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + + clone_calls = [c for c in mock_run_git_command.call_args_list if "clone" in c[0][0]] + assert len(clone_calls) == 1 + assert result_dir == repo_dir + assert _marker_path(repo_dir).is_file() + + +def test_fresh_clone_writes_completion_marker_with_debug_info( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """The marker is written after a fresh clone and records key and hash dir.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + git.clone_or_update(url=url, ref=ref, refresh=git.NEVER_REFRESH, domain=domain) + + marker = _marker_path(repo_dir) + assert marker.is_file() + content = marker.read_text() + assert f"{url}@{ref}" in content + assert repo_dir.name in content + + +def test_marker_is_deleted_before_rmtree( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """The marker must be gone even if rmtree fails partway. + + Simulated by an rmtree that does nothing: the directory survives but the + marker must already have been deleted, so the next run still re-clones + instead of trusting a partially deleted worktree. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + repo_dir = _compute_repo_dir(url, ref, domain) + + _setup_old_repo(repo_dir) + assert _marker_path(repo_dir).is_file() + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "stash": + raise GitCommandError("fatal: unable to write new index file") + return "abc123" + + mock_run_git_command.side_effect = git_command_side_effect + + with ( + patch("esphome.git.rmtree"), + pytest.raises(GitCommandError), + ): + git.clone_or_update( + url=url, ref=ref, refresh=TimePeriodSeconds(days=1), domain=domain + ) + + # rmtree never deleted anything, yet the marker is gone + assert repo_dir.is_dir() + assert not _marker_path(repo_dir).is_file() + + +def test_failed_marker_write_does_not_fail_the_clone( + tmp_path: Path, + mock_run_git_command: Mock, + caplog: pytest.LogCaptureFixture, +) -> None: + """A marker write failure must not fail an otherwise complete clone. + + The clone is valid; the missing marker only costs a re-clone on the next + run, so the error is logged as a warning instead of propagating. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + with patch( + "esphome.git.write_file", side_effect=EsphomeError("Could not write file") + ): + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + + assert result_dir == repo_dir + assert not _marker_path(repo_dir).is_file() + assert "Could not write clone completion marker" in caplog.text + + +def test_corrupt_git_dir_without_head_recovers( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """A .git with neither FETCH_HEAD nor HEAD must recover, not crash. + + The age check stats FETCH_HEAD falling back to HEAD; if both are gone + (partially deleted clone) the stat raised an unhandled FileNotFoundError + before the broken-repository recovery could run. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + # Marker present but .git gutted: no FETCH_HEAD, no HEAD + repo_dir.mkdir(parents=True) + (repo_dir / ".git").mkdir() + _mark_clone_complete(repo_dir) + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + cmd_type = _get_git_command_type(cmd) + if cmd_type == "rev-parse": + raise GitCommandError("ambiguous argument 'HEAD': unknown revision") + if cmd_type == "clone": + _simulate_cloned_repo(repo_dir) + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + + assert result_dir == repo_dir + clone_calls = [c for c in mock_run_git_command.call_args_list if "clone" in c[0][0]] + assert len(clone_calls) == 1 + assert _marker_path(repo_dir).is_file() + + +def test_remove_repo_dir_tolerates_marker_unlink_failure(tmp_path: Path) -> None: + """A locked marker file must not abort the directory removal.""" + repo_dir = tmp_path / "repo" + repo_dir.mkdir() + (repo_dir / ".git").mkdir() + _mark_clone_complete(repo_dir) + + with patch.object(Path, "unlink", side_effect=PermissionError("locked")): + git._remove_repo_dir(repo_dir) + + # rmtree still removed the directory, marker included + assert not repo_dir.exists() + + +def test_clone_or_update_recovery_preserves_subpath( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """Recovery must re-clone into the same subpath-ed directory. + + Without passing subpath through, the recursive recovery call would + recompute the destination without the subpath and clone (and write the + completion marker) at the wrong location. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + ref = "main" + domain = "test" + subpath = Path("mylib") + repo_dir = _compute_repo_dir(url, ref, domain) / subpath + + _setup_old_repo(repo_dir) + + call_counts: dict[str, int] = {} + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + cmd_type = _get_git_command_type(cmd) + if cmd_type: + call_counts[cmd_type] = call_counts.get(cmd_type, 0) + 1 + # First rev-parse fails (broken repo) to trigger recovery + if cmd_type == "rev-parse" and call_counts[cmd_type] == 1: + raise GitCommandError( + "ambiguous argument 'HEAD': unknown revision or path not in the working tree." + ) + if cmd_type == "clone": + # Create whatever directory the clone was asked to target + target = Path(cmd[-1]) + target.mkdir(parents=True, exist_ok=True) + (target / ".git").mkdir(exist_ok=True) + if cmd_type == "rev-parse": + return "abc123" + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + result_dir, _ = git.clone_or_update( + url=url, + ref=ref, + refresh=TimePeriodSeconds(days=1), + domain=domain, + subpath=subpath, + ) + + # The recovery re-clone must target the subpath-ed directory and the + # completion marker must land there too + clone_calls = [c for c in mock_run_git_command.call_args_list if "clone" in c[0][0]] + assert len(clone_calls) == 1 + assert clone_calls[0][0][0][-1] == str(repo_dir) + assert result_dir == repo_dir + assert _marker_path(repo_dir).is_file() + + def test_clone_with_ref_uses_shallow_fetch( tmp_path: Path, mock_run_git_command: Mock ) -> None: @@ -849,46 +1352,6 @@ def test_clone_with_ref_uses_shallow_fetch( assert ref in fetch_calls[0][0][0] -def test_clone_with_submodules_uses_shallow_submodule_update( - tmp_path: Path, mock_run_git_command: Mock -) -> None: - """Submodule init on a fresh clone should use --depth=1.""" - CORE.config_path = tmp_path / "test.yaml" - - url = "https://github.com/test/repo" - domain = "test" - repo_dir = _compute_repo_dir(url, None, domain) - - def git_command_side_effect( - cmd: list[str], cwd: str | None = None, **kwargs: Any - ) -> str: - if _get_git_command_type(cmd) == "clone": - repo_dir.mkdir(parents=True, exist_ok=True) - (repo_dir / ".git").mkdir(exist_ok=True) - return "" - - mock_run_git_command.side_effect = git_command_side_effect - - git.clone_or_update( - url=url, - ref=None, - refresh=None, - domain=domain, - submodules=["components/foo"], - ) - - submodule_calls = [ - c for c in mock_run_git_command.call_args_list if "submodule" in c[0][0] - ] - assert len(submodule_calls) == 1 - cmd = submodule_calls[0][0][0] - assert "--depth=1" in cmd - assert "components/foo" in cmd - # The `--` terminator must precede the submodule paths so a path - # beginning with `-` cannot be parsed as an option. - assert cmd.index("--") < cmd.index("components/foo") - - def test_refresh_fetch_is_shallow(tmp_path: Path, mock_run_git_command: Mock) -> None: """The refresh-path fetch should use --depth=1.""" CORE.config_path = tmp_path / "test.yaml" @@ -913,10 +1376,91 @@ def test_refresh_fetch_is_shallow(tmp_path: Path, mock_run_git_command: Mock) -> assert cmd[-1] == ref -def test_refresh_submodule_update_is_shallow( +@pytest.mark.parametrize( + "refresh", [None, TimePeriodSeconds(days=1)], ids=["clone", "refresh"] +) +def test_all_submodules_skipped_without_gitmodules( + tmp_path: Path, mock_run_git_command: Mock, refresh: TimePeriodSeconds | None +) -> None: + """init_submodules is a no-op for repos with no .gitmodules. + + This is the esp-idf toolchain library scenario from issue #17860: the + PlatformIO library converter requests "all submodules" for every git + library, and most libraries declare none. The git submodule porcelain + must not run at all in that case — it fails outright on some git + installations. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + if refresh is None: + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + else: + _setup_old_repo(repo_dir) + mock_run_git_command.return_value = "abc123" + + git.clone_or_update( + url=url, + ref=None, + refresh=refresh, + domain=domain, + init_submodules=True, + ) + + assert not _submodule_calls(mock_run_git_command) + + +@pytest.mark.parametrize( + "refresh", [None, TimePeriodSeconds(days=1)], ids=["clone", "refresh"] +) +def test_all_submodules_updated_with_gitmodules( + tmp_path: Path, mock_run_git_command: Mock, refresh: TimePeriodSeconds | None +) -> None: + """init_submodules initializes all submodules when .gitmodules exists.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + if refresh is None: + mock_run_git_command.side_effect = _make_clone_side_effect( + repo_dir, gitmodules=True + ) + else: + _setup_old_repo(repo_dir) + (repo_dir / ".gitmodules").write_text("test") + mock_run_git_command.return_value = "abc123" + + git.clone_or_update( + url=url, + ref=None, + refresh=refresh, + domain=domain, + init_submodules=True, + ) + + submodule_calls = _submodule_calls(mock_run_git_command) + # Which submodules get populated is git's own policy, so no status + # verification follows the update. + assert len(submodule_calls) == 1 + cmd = submodule_calls[0][0][0] + assert cmd[2] == "update" + assert "--depth=1" in cmd + # Recursive, mirroring PlatformIO's recursive library clones. + assert "--recursive" in cmd + _assert_submodule_runs_without_isolation(submodule_calls[0], repo_dir) + + +def test_recovery_reclone_keeps_credentials_and_cache_key( tmp_path: Path, mock_run_git_command: Mock ) -> None: - """The refresh-path submodule update should use --depth=1.""" + """The recovery re-clone must not re-apply credentials to the already + rewritten URL (no doubled userinfo) and must land in the same cache + directory, or a credentialed private repo re-clones on every run.""" CORE.config_path = tmp_path / "test.yaml" url = "https://github.com/test/repo" @@ -924,24 +1468,820 @@ def test_refresh_submodule_update_is_shallow( repo_dir = _compute_repo_dir(url, None, domain) _setup_old_repo(repo_dir) - mock_run_git_command.return_value = "abc123" + (repo_dir / ".gitmodules").write_text("test") - git.clone_or_update( + calls = {"submodule": 0} + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "clone": + _simulate_cloned_repo(repo_dir) + if _get_git_command_type(cmd) == "submodule": + calls["submodule"] += 1 + if calls["submodule"] == 1: + raise git.GitCommandError("git submodule update exited with code 1") + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + recovered_dir, _ = git.clone_or_update( url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain, - submodules=["components/foo"], + username="user", + password="hunter2", + init_submodules=True, ) - submodule_calls = [ - c for c in mock_run_git_command.call_args_list if "submodule" in c[0][0] + assert recovered_dir == repo_dir + clone_cmds = [ + c[0][0] + for c in mock_run_git_command.call_args_list + if _get_git_command_type(c[0][0]) == "clone" ] - assert len(submodule_calls) == 1 - cmd = submodule_calls[0][0][0] - assert "--depth=1" in cmd - assert "components/foo" in cmd - assert cmd.index("--") < cmd.index("components/foo") + assert clone_cmds + clone_url = clone_cmds[0][-2] + assert clone_url == "https://user:hunter2@github.com/test/repo" + assert clone_url.count("@") == 1 + + +def test_refresh_submodule_failure_recovers_then_raises( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """A refresh-path submodule failure routes through the recovery re-clone. + + The broken repo is removed and re-cloned; when the submodule update fails + again on the fresh clone the cache entry is removed and the error + propagates, instead of leaving behind a repo the refresh window would + silently accept on the next run. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + _setup_old_repo(repo_dir) + (repo_dir / ".gitmodules").write_text("test") + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "clone": + _simulate_cloned_repo(repo_dir) + (repo_dir / ".gitmodules").write_text("test") + if _get_git_command_type(cmd) == "submodule": + raise git.GitCommandError("git submodule update exited with code 1") + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + with pytest.raises(git.GitCommandError, match="exited with code 1"): + git.clone_or_update( + url=url, + ref=None, + refresh=TimePeriodSeconds(days=1), + domain=domain, + init_submodules=True, + ) + + assert not repo_dir.is_dir() + # Recovery removed the repo and re-cloned before failing again. + assert any( + _get_git_command_type(c[0][0]) == "clone" + for c in mock_run_git_command.call_args_list + ) + + +def _lock_path(url: str, ref: str | None, domain: str) -> Path: + """The lock file the implementation uses for one cache entry.""" + return git._repo_lock_path(git._cache_key(url, ref), domain) + + +class _SetEventOnWaitLog(logging.Handler): + """Set an event when the 'Waiting for another process' record is emitted, + so tests can react to a caller observably blocking on the lock instead of + racing a wall-clock timer.""" + + def __init__(self, event: threading.Event) -> None: + super().__init__() + self._event = event + + def emit(self, record: logging.LogRecord) -> None: + if "Waiting for another process" in record.getMessage(): + self._event.set() + + +def test_clone_or_update_serializes_concurrent_clones( + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """Two concurrent callers for the same uncached repo must not both clone. + + Without the per-entry lock both callers pass the is_dir() check before + either clone finishes, so the second one either clones on top of the + first or reads a half populated worktree (device-builder issue 2425). + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + + start_together = threading.Barrier(2) + other_caller_waiting = threading.Event() + + def on_clone() -> None: + # Hold the lock until the other caller is observably blocked on it, + # so the interleaving is guaranteed rather than raced on a timer. + assert other_caller_waiting.wait(timeout=30) + + mock_run_git_command.side_effect = _make_clone_side_effect( + repo_dir, on_clone=on_clone + ) + + results: list[Path] = [] + errors: list[BaseException] = [] + + def call() -> None: + try: + start_together.wait() + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + results.append(result_dir) + except BaseException as err: # noqa: BLE001 - re-raised via errors below + errors.append(err) + + handler = _SetEventOnWaitLog(other_caller_waiting) + git_logger = logging.getLogger("esphome.git") + git_logger.addHandler(handler) + try: + with caplog.at_level(logging.INFO): + threads = [threading.Thread(target=call) for _ in range(2)] + for t in threads: + t.start() + for t in threads: + t.join() + finally: + git_logger.removeHandler(handler) + + assert not errors + assert results == [repo_dir, repo_dir] + clone_calls = [ + c + for c in mock_run_git_command.call_args_list + if _get_git_command_type(c[0][0]) == "clone" + ] + # The second caller waited for the lock, then saw the completed clone. + assert len(clone_calls) == 1 + assert _marker_path(repo_dir).is_file() + + +def test_clone_or_update_creates_lock_file_next_to_hash_dir( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """The lock file lives beside the hash dir, never inside it. + + Checked while the clone runs (the lock is held): filelock's Windows + backend deletes the lock file on release, so probing after the call + would only work on Unix. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + lock_path = _lock_path(url, None, domain) + lock_held_during_clone: list[bool] = [] + + mock_run_git_command.side_effect = _make_clone_side_effect( + repo_dir, on_clone=lambda: lock_held_during_clone.append(lock_path.is_file()) + ) + + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + + assert result_dir == repo_dir + assert lock_path.parent == repo_dir.parent + assert lock_held_during_clone == [True] + + +def test_clone_or_update_subpath_locks_at_hash_dir_level( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """With a subpath the lock still guards the whole hash dir cache entry.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + hash_dir = _compute_repo_dir(url, None, domain) + repo_dir = hash_dir / "lib" + lock_path = _lock_path(url, None, domain) + lock_held_during_clone: list[bool] = [] + + mock_run_git_command.side_effect = _make_clone_side_effect( + repo_dir, on_clone=lambda: lock_held_during_clone.append(lock_path.is_file()) + ) + + result_dir, _ = git.clone_or_update( + url=url, + ref=None, + refresh=git.NEVER_REFRESH, + domain=domain, + subpath=Path("lib"), + ) + + assert result_dir == repo_dir + assert lock_path == hash_dir.parent / f"{hash_dir.name}.lock" + assert lock_held_during_clone == [True] + + +def test_clone_or_update_recovery_holds_lock_without_deadlock( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """Recovery re-clones while holding the lock and must not re-acquire it. + + A naive re-acquisition would deadlock here, since OS file locks taken on + separate descriptors conflict even within one process. The lock file + itself must survive the recovery rmtree of the broken repo dir: the + re-clone runs after the rmtree, so probing the lock file there proves + it. Probing after the call would only work on Unix, since filelock's + Windows backend deletes the lock file on release. + """ + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + lock_path = _lock_path(url, None, domain) + lock_held_during_reclone: list[bool] = [] + + _setup_old_repo(repo_dir) + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "stash": + raise git.GitCommandError("broken repository") + if _get_git_command_type(cmd) == "clone": + lock_held_during_reclone.append(lock_path.is_file()) + _simulate_cloned_repo(repo_dir) + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + recovered_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + + assert recovered_dir == repo_dir + assert lock_held_during_reclone == [True] + + +def _hold_lock_in_thread(lock_path: Path) -> tuple[threading.Thread, threading.Event]: + """Hold the lock from another thread; returns the thread and its release event. + + OS file locks taken on separate descriptors conflict even within one + process, so a second FileLock instance in a thread contends the same + way another process would. + """ + held = threading.Event() + release = threading.Event() + + def hold() -> None: + with FileLock(str(lock_path)): + held.set() + release.wait(timeout=30) + + holder = threading.Thread(target=hold) + holder.start() + assert held.wait(timeout=30) + return holder, release + + +def test_clone_or_update_logs_wait_on_contended_lock( + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """A contended acquire logs a redacted waiting message instead of + silently blocking.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://user:hunter2@github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + holder, release = _hold_lock_in_thread(_lock_path(url, None, domain)) + + # Free the lock only once the waiting message has been emitted, so the + # release is caused by the thing being asserted instead of racing it. + handler = _SetEventOnWaitLog(release) + git_logger = logging.getLogger("esphome.git") + git_logger.addHandler(handler) + try: + with caplog.at_level(logging.INFO): + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + finally: + git_logger.removeHandler(handler) + release.set() + holder.join() + + assert result_dir == repo_dir + waiting = [ + r.getMessage() + for r in caplog.records + if "Waiting for another process" in r.getMessage() + ] + assert len(waiting) == 1 + assert "hunter2" not in waiting[0] + assert "://***@" in waiting[0] + + +def test_revert_skips_on_contended_lock( + tmp_path: Path, + mock_run_git_command: Mock, + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """revert() only runs on an already-failing path; when it cannot get the + lock within the bounded timeout it warns and skips instead of hanging.""" + CORE.config_path = tmp_path / "test.yaml" + monkeypatch.setattr(git, "_REVERT_LOCK_TIMEOUT_SECONDS", 0.05) + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + _setup_old_repo(repo_dir) + + # A bare return value satisfies the rev-parse; the other commands' + # outputs are unused. + mock_run_git_command.return_value = "old_sha" + + _, revert = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + assert revert is not None + + holder, release = _hold_lock_in_thread(_lock_path(url, None, domain)) + calls_before = len(mock_run_git_command.call_args_list) + with caplog.at_level(logging.INFO): + assert revert() is False + release.set() + holder.join() + + # No git reset was issued; the wait and the skip were both logged. + assert len(mock_run_git_command.call_args_list) == calls_before + assert any("Waiting for another process" in r.getMessage() for r in caplog.records) + assert any("skipping revert" in r.getMessage() for r in caplog.records) + + +def test_update_clears_marker_while_rewriting( + tmp_path: Path, mock_run_git_command: Mock +) -> None: + """The completion marker is absent while a refresh rewrites the entry + and restored once the rewrite finishes, so a timed-out peer's fallback + never trusts a mid-rewrite tree and a crashed update re-clones.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + _setup_old_repo(repo_dir) + + marker_during_rewrite: list[bool] = [] + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) in ("stash", "fetch", "reset"): + marker_during_rewrite.append(_marker_path(repo_dir).is_file()) + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + + assert marker_during_rewrite == [False, False, False] + assert _marker_path(repo_dir).is_file() + + +def test_revert_skips_when_checkout_moved( + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """revert() only undoes this process's own update; when another process + refreshed the entry in the meantime it skips instead of rolling the + peer's newer checkout backwards.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + _setup_old_repo(repo_dir) + + # Pre-update SHA, post-update SHA, then a peer's SHA at revert time. + shas = iter(["old_sha", "new_sha", "peer_sha"]) + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "rev-parse": + return next(shas) + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + _, revert = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + assert revert is not None + + with caplog.at_level(logging.WARNING): + assert revert() is False + + resets = [ + c[0][0] + for c in mock_run_git_command.call_args_list + if _get_git_command_type(c[0][0]) == "reset" and c[0][0][-1] == "old_sha" + ] + assert resets == [] + assert any("checkout moved" in r.getMessage() for r in caplog.records) + + +def test_revert_returns_false_when_reset_fails( + tmp_path: Path, mock_run_git_command: Mock, caplog: pytest.LogCaptureFixture +) -> None: + """A failed git reset inside revert() is reported through the bool + contract instead of raising a cv.Invalid that would replace the + caller's original error.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + _setup_old_repo(repo_dir) + + def git_command_side_effect( + cmd: list[str], cwd: str | None = None, **kwargs: Any + ) -> str: + if _get_git_command_type(cmd) == "rev-parse": + return "old_sha" + # Only revert's reset targets the recorded SHA; the update path's + # reset targets FETCH_HEAD and must succeed. + if cmd[-1] == "old_sha": + raise git.GitCommandError("object not found") + return "" + + mock_run_git_command.side_effect = git_command_side_effect + + _, revert = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + assert revert is not None + + with caplog.at_level(logging.WARNING): + assert revert() is False + + assert any("Could not revert" in r.getMessage() for r in caplog.records) + # The entry cannot be trusted after a failed reset; the dropped marker + # forces a re-clone on the next use. + assert not _marker_path(repo_dir).is_file() + + +def _raise_oserror_on_acquire( + monkeypatch: pytest.MonkeyPatch, code: int = errno.ENOLCK +) -> None: + """Make every FileLock acquire fail with the given errno.""" + + def broken_acquire(self: FileLock, *args: Any, **kwargs: Any) -> None: + raise OSError(code, os.strerror(code)) + + monkeypatch.setattr(FileLock, "acquire", broken_acquire) + + +@pytest.mark.parametrize( + ("code", "expected_fragment"), + [ + # Genuinely missing lock support is reported as such. + (errno.ENOLCK, "does not support locking"), + # A cache directory problem is not blamed on lock support; git + # reports the real error when it actually matters. + (errno.EROFS, "Could not take the cache entry lock"), + ], +) +def test_clone_or_update_continues_unlocked_when_filesystem_cannot_lock( + tmp_path: Path, + mock_run_git_command: Mock, + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, + code: int, + expected_fragment: str, +) -> None: + """A filesystem where taking the lock fails (e.g. NFS without a lock + daemon) degrades to the old unlocked behavior instead of failing.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + _raise_oserror_on_acquire(monkeypatch, code) + + with caplog.at_level(logging.WARNING): + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + + assert result_dir == repo_dir + assert _marker_path(repo_dir).is_file() + warnings = [ + r.getMessage() + for r in caplog.records + if "continuing without a lock" in r.getMessage() + ] + assert warnings + assert expected_fragment in warnings[0] + + +@pytest.mark.parametrize("broken_from_start", [True, False]) +def test_revert_continues_unlocked_when_filesystem_cannot_lock( + tmp_path: Path, + mock_run_git_command: Mock, + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, + broken_from_start: bool, +) -> None: + """A revert still resets when locking is unavailable, whether the wrapper + already fell back to unlocked (revert sees no lock at all) or the + filesystem stops locking between the update and the revert.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + _setup_old_repo(repo_dir) + + # A bare return value satisfies the rev-parse; the other commands' + # outputs are unused. + mock_run_git_command.return_value = "old_sha" + + if broken_from_start: + _raise_oserror_on_acquire(monkeypatch) + + with caplog.at_level(logging.WARNING): + _, revert = git.clone_or_update( + url=url, ref=None, refresh=TimePeriodSeconds(days=1), domain=domain + ) + assert revert is not None + if not broken_from_start: + _raise_oserror_on_acquire(monkeypatch) + # The reset ran (unlocked), so the revert reports success. + assert revert() is True + + assert mock_run_git_command.call_args_list[-1][0][0] == [ + "git", + "reset", + "--hard", + "old_sha", + ] + assert any("continuing without a lock" in r.getMessage() for r in caplog.records) + + +def _script_acquire_statuses( + monkeypatch: pytest.MonkeyPatch, statuses: list["git._LockStatus"] +) -> list[float]: + """Replace _acquire_repo_lock with a scripted sequence; returns the + timeouts it was called with.""" + timeouts: list[float] = [] + status_iter = iter(statuses) + + def fake_acquire( + lock: FileLock, safe_key: str, timeout: float, **kwargs: Any + ) -> "git._LockStatus": + timeouts.append(timeout) + return next(status_iter) + + monkeypatch.setattr(git, "_acquire_repo_lock", fake_acquire) + return timeouts + + +@pytest.mark.parametrize("subpath", [None, Path("lib")]) +def test_clone_or_update_uses_complete_entry_when_lock_wait_times_out( + tmp_path: Path, + mock_run_git_command: Mock, + caplog: pytest.LogCaptureFixture, + monkeypatch: pytest.MonkeyPatch, + subpath: Path | None, +) -> None: + """A bounded wait behind a stalled holder falls back to an existing + complete cache entry instead of hanging every peer.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + if subpath is not None: + repo_dir = repo_dir / subpath + _simulate_cloned_repo(repo_dir) + _mark_clone_complete(repo_dir) + + timeouts = _script_acquire_statuses(monkeypatch, [git._LockStatus.TIMEOUT]) + + with caplog.at_level(logging.WARNING): + result_dir, revert = git.clone_or_update( + url=url, + ref=None, + refresh=TimePeriodSeconds(days=1), + domain=domain, + subpath=subpath, + ) + + assert result_dir == repo_dir + assert revert is None + # Nothing was cloned or refreshed; the existing entry was used as-is. + assert mock_run_git_command.call_args_list == [] + assert timeouts == [git._COMPLETE_ENTRY_LOCK_TIMEOUT_SECONDS] + assert any( + "proceeding with the existing clone" in r.getMessage() for r in caplog.records + ) + + +def test_clone_or_update_waits_unbounded_without_complete_entry( + tmp_path: Path, + mock_run_git_command: Mock, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """With no complete entry there is nothing to fall back to, so after the + bounded wait expires the caller keeps waiting for the holder's clone.""" + CORE.config_path = tmp_path / "test.yaml" + + url = "https://github.com/test/repo" + domain = "test" + repo_dir = _compute_repo_dir(url, None, domain) + mock_run_git_command.side_effect = _make_clone_side_effect(repo_dir) + + timeouts = _script_acquire_statuses( + monkeypatch, [git._LockStatus.TIMEOUT, git._LockStatus.ACQUIRED] + ) + + result_dir, _ = git.clone_or_update( + url=url, ref=None, refresh=git.NEVER_REFRESH, domain=domain + ) + + assert result_dir == repo_dir + assert timeouts == [git._COMPLETE_ENTRY_LOCK_TIMEOUT_SECONDS, -1] + # The clone proceeded normally once the lock was finally acquired. + assert _marker_path(repo_dir).is_file() + + +def _real_git(*args: str, cwd: Path) -> None: + """Run real git to build a test fixture repository.""" + subprocess.run( + [ + "git", + "-c", + "user.email=test@test.invalid", + "-c", + "user.name=test", + "-c", + "commit.gpgsign=false", + "-c", + "protocol.file.allow=always", + *args, + ], + cwd=cwd, + check=True, + capture_output=True, + ) + + +# Git blocks file-protocol submodules by default (CVE-2022-39253); the e2e +# tests allow them via GIT_CONFIG_* environment variables, which reach the +# child git processes through run_git_command's filtered environment. +_ALLOW_FILE_PROTOCOL_ENV = { + "GIT_CONFIG_COUNT": "1", + "GIT_CONFIG_KEY_0": "protocol.file.allow", + "GIT_CONFIG_VALUE_0": "always", +} + + +def _make_real_repo(path: Path, filename: str) -> None: + """Create a real git repository containing one committed file.""" + path.mkdir() + _real_git("init", "-q", cwd=path) + (path / filename).write_text("content") + _real_git("add", filename, cwd=path) + _real_git("commit", "-q", "-m", "init", cwd=path) + + +def _add_submodule( + repo: Path, url: Path, path: str, *, update_none: bool = False +) -> None: + """Add ``url`` as a submodule of ``repo`` at ``path`` and commit it.""" + _real_git("submodule", "add", str(url), path, cwd=repo) + if update_none: + _real_git( + "config", "-f", ".gitmodules", f"submodule.{path}.update", "none", cwd=repo + ) + _real_git("add", ".gitmodules", cwd=repo) + _real_git("commit", "-q", "-m", f"add submodule {path}", cwd=repo) + + +def test_clone_or_update_real_git_without_submodules(tmp_path: Path) -> None: + """End-to-end with real git: a repo with no .gitmodules clones cleanly. + + This is the issue #17860 scenario: requesting "all submodules" on a + submodule-less repository must not invoke the git submodule porcelain + and must produce a usable checkout. + """ + CORE.config_path = tmp_path / "test.yaml" + + upstream = tmp_path / "upstream" + _make_real_repo(upstream, "README.md") + + repo_dir, _ = git.clone_or_update( + url=str(upstream), + ref=None, + refresh=None, + domain="test_e2e", + init_submodules=True, + ) + + assert (repo_dir / "README.md").is_file() + + +def test_clone_or_update_real_git_initializes_submodules(tmp_path: Path) -> None: + """End-to-end with real git: submodules are actually checked out. + + Exercises the real `git submodule update` invocation, including the + env handling in run_git_command that the mocked tests cannot cover. + """ + CORE.config_path = tmp_path / "test.yaml" + + sub_repo = tmp_path / "sub" + _make_real_repo(sub_repo, "sub_file.txt") + + upstream = tmp_path / "upstream" + _make_real_repo(upstream, "README.md") + _add_submodule(upstream, sub_repo, "vendor/sub") + + with patch.dict(os.environ, _ALLOW_FILE_PROTOCOL_ENV): + repo_dir, _ = git.clone_or_update( + url=str(upstream), + ref=None, + refresh=None, + domain="test_e2e", + init_submodules=True, + ) + + assert (repo_dir / "vendor" / "sub" / "sub_file.txt").is_file() + + +def test_clone_or_update_real_git_honors_update_none_submodule( + tmp_path: Path, +) -> None: + """End-to-end with real git: submodules declared `update = none` stay skipped. + + Shows git itself skipping the declared paths at both nesting levels + (and exiting 0) while the regular submodules check out. + """ + CORE.config_path = tmp_path / "test.yaml" + + sub_repo = tmp_path / "sub" + _make_real_repo(sub_repo, "sub_file.txt") + + # Intermediate submodule that itself declares a skipped nested submodule. + mid_repo = tmp_path / "mid" + _make_real_repo(mid_repo, "mid_file.txt") + _add_submodule(mid_repo, sub_repo, "vendor/leaf", update_none=True) + + upstream = tmp_path / "upstream" + _make_real_repo(upstream, "README.md") + _add_submodule(upstream, sub_repo, "vendor/sub") + _add_submodule(upstream, sub_repo, "vendor/skipped", update_none=True) + _add_submodule(upstream, mid_repo, "vendor/mid") + + with patch.dict(os.environ, _ALLOW_FILE_PROTOCOL_ENV): + repo_dir, _ = git.clone_or_update( + url=str(upstream), + ref=None, + refresh=None, + domain="test_e2e", + init_submodules=True, + ) + + assert (repo_dir / "vendor" / "sub" / "sub_file.txt").is_file() + assert not (repo_dir / "vendor" / "skipped" / "sub_file.txt").exists() + assert (repo_dir / "vendor" / "mid" / "mid_file.txt").is_file() + assert not ( + repo_dir / "vendor" / "mid" / "vendor" / "leaf" / "sub_file.txt" + ).exists() def test_refresh_picks_up_new_remote_commits( @@ -983,7 +2323,8 @@ def test_refresh_picks_up_new_remote_commits( # Verify the refresh sequence: rev-parse -> stash -> fetch (depth=1) -> reset call_list = mock_run_git_command.call_args_list cmd_sequence = [_get_git_command_type(c[0][0]) for c in call_list] - assert cmd_sequence == ["rev-parse", "stash", "fetch", "reset"] + # The trailing rev-parse records the post-update SHA for revert(). + assert cmd_sequence == ["rev-parse", "stash", "fetch", "reset", "rev-parse"] fetch_cmd = call_list[2][0][0] assert "--depth=1" in fetch_cmd @@ -994,7 +2335,7 @@ def test_refresh_picks_up_new_remote_commits( # revert callback should reset back to the recorded pre-update SHA. assert revert is not None - revert() + assert revert() is True assert mock_run_git_command.call_args_list[-1][0][0] == [ "git", "reset", diff --git a/tests/unit_tests/test_helpers.py b/tests/unit_tests/test_helpers.py index fad249b0bb..211fbf5112 100644 --- a/tests/unit_tests/test_helpers.py +++ b/tests/unit_tests/test_helpers.py @@ -1074,3 +1074,21 @@ def test_progressbar_enabled_on_pipe_with_dashboard(monkeypatch) -> None: bar = ProgressBar("Uploading", stream=stream) assert bar.enabled is True + + +@pytest.mark.parametrize( + ("seconds", "expected"), + [ + (0, "0s"), + (42, "42s"), + (60, "1min"), + (3661, "1h 1min"), + (86400, "1d"), + (90000, "1d 1h"), + (86700, "1d 5min"), + (-5, "0s"), + ], +) +def test_format_duration(seconds: float, expected: str) -> None: + """Test that durations are rendered as short human-readable strings.""" + assert helpers.format_duration(seconds) == expected diff --git a/tests/unit_tests/test_lazy_imports.py b/tests/unit_tests/test_lazy_imports.py new file mode 100644 index 0000000000..ee570a84f6 --- /dev/null +++ b/tests/unit_tests/test_lazy_imports.py @@ -0,0 +1,53 @@ +"""Guard the lazy-import contract of ``esphome.__main__``. + +Every ``esphome`` invocation pays for whatever ``esphome.__main__`` +imports at module level before the requested command runs. The +dashboard and device-builder spawn one ``esphome upload`` subprocess +per device, so keeping validation/codegen machinery out of the +top-level import directly lowers the RAM cost of each concurrent +upload (the upload/logs fast path in ``esphome.compiled_config`` +never needs them). + +``script/check_import_time.py`` budgets import *time* in CI; this +test pins down *which* heavy modules must stay out entirely. +""" + +from __future__ import annotations + +import subprocess +import sys + +# Modules that must only load for the commands that actually use them +# (compile/config validation, shell completion), never from a bare +# ``import esphome.__main__``. +HEAVY_MODULES = ( + "argcomplete", + "esphome.codegen", + "esphome.config", + "esphome.config_validation", + "esphome.cpp_generator", + "esphome.loader", + "voluptuous", +) + + +def test_main_module_does_not_import_heavy_modules() -> None: + """A bare ``import esphome.__main__`` must not drag in validation/codegen.""" + check = ( + "import sys; import esphome.__main__; " + f"leaked = [m for m in {HEAVY_MODULES!r} if m in sys.modules]; " + "print(','.join(leaked))" + ) + result = subprocess.run( + [sys.executable, "-c", check], + capture_output=True, + text=True, + check=True, + ) + leaked = result.stdout.strip() + assert not leaked, ( + f"esphome.__main__ imports heavy modules at top level: {leaked}. " + "Import them lazily inside the command that needs them instead; " + "every esphome invocation (including each parallel dashboard " + "upload subprocess) pays for top-level imports." + ) diff --git a/tests/unit_tests/test_main.py b/tests/unit_tests/test_main.py index 9a9aafec43..e575934870 100644 --- a/tests/unit_tests/test_main.py +++ b/tests/unit_tests/test_main.py @@ -442,6 +442,46 @@ def test_redact_with_legacy_fallback__does_not_match_fragment_as_suffix( assert not any("legacy substring" in rec.message for rec in caplog.records) +@pytest.mark.parametrize("field", ["public_key", "peer_public_key"]) +def test_redact_with_legacy_fallback__skips_public_key_fields( + field: str, + caplog: pytest.LogCaptureFixture, +) -> None: + """Public keys are not secret; fields with a ``public`` name segment + must pass through unredacted and without the migration warning + (see issue #17718).""" + text = f"{field}: c29tZXB1YmxpY2tleQ==\n" + with caplog.at_level(logging.WARNING, logger="esphome.__main__"): + out = _redact_with_legacy_fallback(text) + assert out == text + assert not any("legacy substring" in rec.message for rec in caplog.records) + + +def test_redact_with_legacy_fallback__public_substitution_still_redacted( + caplog: pytest.LogCaptureFixture, +) -> None: + """Substitution keys are user-named with no schema behind them, so the + public-key exemption does not apply there; a ``public``-named substitution + keeps the conservative silent redaction.""" + text = "substitutions:\n public_key: something\nesphome:\n name: x\n" + with caplog.at_level(logging.WARNING, logger="esphome.__main__"): + out = _redact_with_legacy_fallback(text) + assert "public_key: \\033[8msomething\\033[28m" in out + assert not any("legacy substring" in rec.message for rec in caplog.records) + + +def test_redact_with_legacy_fallback__public_must_be_a_whole_segment( + caplog: pytest.LogCaptureFixture, +) -> None: + """The exemption matches ``public`` as an underscore-separated segment, + not a substring; an unrelated name like ``republic_key`` keeps the + conservative redaction.""" + with caplog.at_level(logging.WARNING, logger="esphome.__main__"): + out = _redact_with_legacy_fallback("republic_key: abc\n") + assert "republic_key: \\033[8mabc\\033[28m" in out + assert any("'republic_key'" in rec.message for rec in caplog.records) + + def test_redact_with_legacy_fallback__substitutions_redacted_without_warning( caplog: pytest.LogCaptureFixture, ) -> None: @@ -578,7 +618,7 @@ def test_command_config__no_defaults_skips_strip_default_ids( validated.user_config = {"sensor": [{"name": "x"}]} with patch( - "esphome.__main__.strip_default_ids", side_effect=AssertionError + "esphome.config.strip_default_ids", side_effect=AssertionError ) as mock_strip: result = command_config(args, validated) @@ -5410,6 +5450,43 @@ def _setup_build_info_test( return build_info_path, firmware_path +def test_compile_program_esp8266_runs_rosetta_check(tmp_path: Path) -> None: + """Test that compile_program runs the Rosetta preflight for ESP8266 targets.""" + setup_core(platform=PLATFORM_ESP8266, tmp_path=tmp_path, name="test_device") + + config: dict[str, Any] = {CONF_ESPHOME: {CONF_NAME: "test_device"}} + args = MockArgs() + + with ( + patch( + "esphome.components.esp8266.check_rosetta", + side_effect=EsphomeError("Rosetta 2 is not installed"), + ) as mock_check, + pytest.raises(EsphomeError, match="Rosetta 2 is not installed"), + ): + compile_program(args, config) + + mock_check.assert_called_once() + + +def test_compile_program_skips_rosetta_check_on_other_platforms( + tmp_path: Path, + mock_compile_build_info_run_compile: Mock, + mock_compile_build_info_get_idedata: Mock, +) -> None: + """Test that the Rosetta preflight does not run for non-ESP8266 targets.""" + _setup_build_info_test(tmp_path, firmware_first=True) + + config: dict[str, Any] = {CONF_ESPHOME: {CONF_NAME: "test_device"}} + args = MockArgs() + + with patch("esphome.components.esp8266.check_rosetta") as mock_check: + result = compile_program(args, config) + + assert result == 0 + mock_check.assert_not_called() + + def test_compile_program_emits_build_info_when_firmware_rebuilt( tmp_path: Path, caplog: pytest.LogCaptureFixture, @@ -6124,7 +6201,7 @@ def test_run_esphome_bundle_detection(tmp_path: Path) -> None: "esphome.bundle.prepare_bundle_for_compile", return_value=extracted_yaml, ) as mock_prepare, - patch("esphome.__main__.read_config", return_value=None), + patch("esphome.config.read_config", return_value=None), ): result = run_esphome(["esphome", "compile", str(bundle_path)]) @@ -6142,7 +6219,7 @@ def test_run_esphome_non_bundle_skips_extraction(tmp_path: Path) -> None: with ( patch("esphome.bundle.is_bundle_path", return_value=False) as mock_is_bundle, patch("esphome.bundle.prepare_bundle_for_compile") as mock_prepare, - patch("esphome.__main__.read_config", return_value=None), + patch("esphome.config.read_config", return_value=None), ): result = run_esphome(["esphome", "compile", str(yaml_file)]) @@ -6170,7 +6247,7 @@ def test_run_esphome_skip_external_update_per_command( yaml_file = tmp_path / "device.yaml" yaml_file.write_text("esphome:\n name: test\n") - with patch("esphome.__main__.read_config", return_value=None) as mock_read: + with patch("esphome.config.read_config", return_value=None) as mock_read: run_esphome(["esphome", command, str(yaml_file)]) mock_read.assert_called_once() @@ -6328,6 +6405,23 @@ def test_parse_args_logs_states() -> None: assert args.states is True +def test_parse_args_argcomplete_only_runs_when_completing() -> None: + """Only import and invoke argcomplete when _ARGCOMPLETE is set. + + The shell-completion machinery sets _ARGCOMPLETE when it invokes the + CLI; a normal invocation must skip the import entirely so every + esphome subprocess (e.g. parallel dashboard uploads) avoids paying + for it. + """ + fake_argcomplete = MagicMock() + with ( + patch.dict(os.environ, {"_ARGCOMPLETE": "1"}), + patch.dict(sys.modules, {"argcomplete": fake_argcomplete}), + ): + parse_args(["esphome", "version"]) + fake_argcomplete.autocomplete.assert_called_once() + + def test_should_subscribe_states_default() -> None: """Test that states are shown by default when nothing is set.""" from esphome.__main__ import _should_subscribe_states diff --git a/tests/unit_tests/test_nrf52_framework.py b/tests/unit_tests/test_nrf52_framework.py index bb5bc8c064..0a6bddc280 100644 --- a/tests/unit_tests/test_nrf52_framework.py +++ b/tests/unit_tests/test_nrf52_framework.py @@ -1,22 +1,30 @@ """Tests for esphome.components.nrf52.framework helpers.""" import hashlib +import os from pathlib import Path +import sys from types import SimpleNamespace from unittest.mock import patch import pytest from esphome.components.nrf52.framework import ( + _PLATFORMIO_PENV_REQUIREMENTS, _REQUIREMENTS, TOOLCHAIN_VERSION, + _get_penv_site_packages, + _get_platformio_penv_path, _get_toolchain_platform_info, check_and_install, + get_build_env, get_sdk_nrf_tools_path, + setup_platformio_python_env, ) from esphome.config_validation import Version from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION from esphome.core import CORE, EsphomeError +from esphome.framework_helpers import get_python_env_executable_path @pytest.fixture(autouse=True) @@ -193,6 +201,24 @@ class TestCheckAndInstall: assert mock_nrf52_ops.download_from_mirrors.call_count == 2 assert mock_nrf52_ops.archive_extract_all.call_count == 2 + def test_framework_clone_is_shallow( + self, + nrf52_dirs: SimpleNamespace, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """Both the manifest repository and every project are fetched at depth 1.""" + _mark_venv_ready(nrf52_dirs.python_env) + + check_and_install() + + init_cmd, update_cmd = ( + call.args[0] for call in mock_nrf52_ops.run_command_ok.call_args_list[:2] + ) + assert "init" in init_cmd + assert "-o=--depth=1" in init_cmd + assert "update" in update_cmd + assert "--fetch-opt=--depth=1" in update_cmd + def test_requirements_install_failure_raises( self, nrf52_dirs: SimpleNamespace, @@ -252,6 +278,218 @@ class TestCheckAndInstall: assert substitutions["extension"] == "tar.xz" +# --------------------------------------------------------------------------- +# setup_platformio_python_env tests +# --------------------------------------------------------------------------- + + +def _platformio_requirements_hash() -> str: + return hashlib.sha256( + _REQUIREMENTS.read_bytes() + + "\n".join(_PLATFORMIO_PENV_REQUIREMENTS).encode() + + f"python{sys.version_info.major}.{sys.version_info.minor}".encode() + ).hexdigest() + + +@pytest.fixture +def platformio_penv_dir() -> Path: + """Pre-create the PlatformIO penv dir so sentinel writes succeed. + + create_venv is mocked in these tests, so the directory it would have + created must exist for ``sentinel.write_text`` to work. + """ + penv_path = _get_platformio_penv_path() + penv_path.mkdir(parents=True, exist_ok=True) + return penv_path + + +class TestSetupPlatformioPythonEnv: + def test_fresh_install_creates_venv_and_sets_env( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """No sentinel → venv created, requirements installed, env exported.""" + with patch.dict(os.environ): + os.environ.pop("PYTHONPATH", None) + + setup_platformio_python_env() + + mock_nrf52_ops.rmdir.assert_called_once() + mock_nrf52_ops.create_venv.assert_called_once_with( + platformio_penv_dir, msg="PlatformIO toolchain" + ) + mock_nrf52_ops.run_command_ok.assert_called_once() + cmd = mock_nrf52_ops.run_command_ok.call_args[0][0] + assert cmd[1:4] == ["-m", "pip", "install"] + assert "-r" in cmd + assert str(_REQUIREMENTS) in cmd + for requirement in _PLATFORMIO_PENV_REQUIREMENTS: + assert requirement in cmd + sentinel = platformio_penv_dir / ".ready" + assert sentinel.read_text(encoding="utf-8") == ( + _platformio_requirements_hash() + ) + + assert os.environ["VIRTUAL_ENV"] == str(platformio_penv_dir) + site_packages = str(_get_penv_site_packages(platformio_penv_dir)) + assert os.environ["PYTHONPATH"] == site_packages + bin_dir = str( + get_python_env_executable_path(platformio_penv_dir, "python").parent + ) + assert os.environ["PATH"].split(os.pathsep)[0] == bin_dir + + def test_ready_sentinel_skips_install_but_sets_env( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """Current sentinel → no install work, env vars still exported.""" + (platformio_penv_dir / ".ready").write_text( + _platformio_requirements_hash(), encoding="utf-8" + ) + + with patch.dict(os.environ): + setup_platformio_python_env() + + mock_nrf52_ops.rmdir.assert_not_called() + mock_nrf52_ops.create_venv.assert_not_called() + mock_nrf52_ops.run_command_ok.assert_not_called() + assert os.environ["VIRTUAL_ENV"] == str(platformio_penv_dir) + + def test_stale_sentinel_reinstalls( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """A sentinel from different requirements → venv rebuilt from scratch.""" + sentinel = platformio_penv_dir / ".ready" + sentinel.write_text("stale-hash", encoding="utf-8") + + with patch.dict(os.environ): + setup_platformio_python_env() + + mock_nrf52_ops.rmdir.assert_called_once() + mock_nrf52_ops.create_venv.assert_called_once() + mock_nrf52_ops.run_command_ok.assert_called_once() + assert sentinel.read_text(encoding="utf-8") == _platformio_requirements_hash() + + def test_install_failure_raises( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """Failing pip install raises EsphomeError and writes no sentinel.""" + mock_nrf52_ops.run_command_ok.return_value = False + + with ( + patch.dict(os.environ), + pytest.raises( + EsphomeError, match="Install requirements for PlatformIO toolchain" + ), + ): + setup_platformio_python_env() + + assert not (platformio_penv_dir / ".ready").exists() + + def test_repeated_calls_do_not_duplicate_env_entries( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """Compile then upload in one process must not grow PYTHONPATH/PATH.""" + (platformio_penv_dir / ".ready").write_text( + _platformio_requirements_hash(), encoding="utf-8" + ) + site_packages = str(_get_penv_site_packages(platformio_penv_dir)) + bin_dir = str( + get_python_env_executable_path(platformio_penv_dir, "python").parent + ) + + with patch.dict(os.environ): + setup_platformio_python_env() + setup_platformio_python_env() + + assert os.environ["PYTHONPATH"].split(os.pathsep).count(site_packages) == 1 + assert os.environ["PATH"].split(os.pathsep).count(bin_dir) == 1 + + def test_existing_pythonpath_preserved( + self, + platformio_penv_dir: Path, + mock_nrf52_ops: SimpleNamespace, + ) -> None: + """A pre-existing PYTHONPATH keeps its entries after the venv entry.""" + (platformio_penv_dir / ".ready").write_text( + _platformio_requirements_hash(), encoding="utf-8" + ) + site_packages = str(_get_penv_site_packages(platformio_penv_dir)) + + with patch.dict(os.environ, {"PYTHONPATH": "/existing/path"}): + setup_platformio_python_env() + + assert os.environ["PYTHONPATH"] == os.pathsep.join( + [site_packages, "/existing/path"] + ) + + +@pytest.mark.parametrize( + ("os_name", "expected_parts"), + [ + ( + "posix", + ( + "lib", + f"python{sys.version_info.major}.{sys.version_info.minor}", + "site-packages", + ), + ), + ("nt", ("Lib", "site-packages")), + ], +) +def test_get_penv_site_packages( + tmp_path: Path, os_name: str, expected_parts: tuple[str, ...] +) -> None: + penv_path = tmp_path / "penv" + with patch("os.name", os_name): + assert _get_penv_site_packages(penv_path) == penv_path.joinpath(*expected_parts) + + +# --------------------------------------------------------------------------- +# get_build_env tests +# --------------------------------------------------------------------------- + + +def test_get_build_env( + nrf52_dirs: SimpleNamespace, monkeypatch: pytest.MonkeyPatch +) -> None: + """get_build_env exposes ZEPHYR_SDK_INSTALL_DIR pointing at the toolchain root. + + ZEPHYR_SDK_INSTALL_DIR is the variable Zephyr's FindZephyr-sdk.cmake + explicitly consumes (from the environment) and uses as a find_package + HINT. The old Zephyr-sdk_DIR environment hint proved unreliable in + containerized non-root builds and was removed. + """ + monkeypatch.setenv("SOME_PREEXISTING_VAR", "kept") + + env = get_build_env() + + tools = get_sdk_nrf_tools_path() + venv_bin_dir = get_python_env_executable_path( + tools / "penvs" / f"v{_TEST_SDK_VERSION}", "python" + ).parent + assert env["PATH"].startswith(str(venv_bin_dir) + os.pathsep) + assert env["ZEPHYR_BASE"] == str( + tools / "frameworks" / f"v{_TEST_SDK_VERSION}" / "zephyr" + ) + # Toolchain root, not the cmake/ subdir + assert env["ZEPHYR_SDK_INSTALL_DIR"] == str( + tools / "toolchains" / TOOLCHAIN_VERSION + ) + assert "Zephyr-sdk_DIR" not in env + # The rest of the process environment is inherited + assert env["SOME_PREEXISTING_VAR"] == "kept" + + # --------------------------------------------------------------------------- # get_sdk_nrf_tools_path tests # --------------------------------------------------------------------------- diff --git a/tests/unit_tests/test_nrf52_upload.py b/tests/unit_tests/test_nrf52_upload.py index a60e23a337..9b738ebc81 100644 --- a/tests/unit_tests/test_nrf52_upload.py +++ b/tests/unit_tests/test_nrf52_upload.py @@ -146,6 +146,72 @@ class TestUploadProgramPyocd: upload_program(config={}, args=None, host="PYOCD") +# --------------------------------------------------------------------------- +# PlatformIO toolchain paths +# --------------------------------------------------------------------------- + + +class TestRunCompilePlatformio: + def test_prepares_python_env_and_delegates_to_platformio( + self, setup_core: Path, tmp_path: Path + ) -> None: + """The PlatformIO toolchain prepares the env, then returns False so PlatformIO builds.""" + from esphome.components.nrf52 import run_compile + + _setup_nrf52_core(toolchain=Toolchain.PLATFORMIO, build_path=tmp_path / "build") + + with patch( + "esphome.components.nrf52.setup_platformio_python_env" + ) as mock_setup: + assert run_compile(args=None, config={}) is False + + mock_setup.assert_called_once_with() + + +class TestUploadProgramSerialPlatformio: + def _upload(self, host: str, tmp_path: Path, run_result: int) -> tuple: + from esphome.components.nrf52 import upload_program + from esphome.upload_targets import PortType + + _setup_nrf52_core(toolchain=Toolchain.PLATFORMIO, build_path=tmp_path / "build") + CORE.config_path = tmp_path / "test.yaml" + + with ( + patch("esphome.upload_targets.get_port_type", return_value=PortType.SERIAL), + patch("esphome.__main__.check_permissions"), + patch("esphome.components.nrf52.setup_platformio_python_env") as mock_setup, + patch( + "esphome.platformio.toolchain.run_platformio_cli_run", + return_value=run_result, + ) as mock_run, + ): + result = upload_program(config={}, args=None, host=host) + return result, mock_setup, mock_run + + def test_serial_upload_prepares_env_and_runs_platformio( + self, setup_core: Path, tmp_path: Path + ) -> None: + """Serial upload with the PlatformIO toolchain runs pio with -t upload.""" + host = "/dev/ttyACM0" + result, mock_setup, mock_run = self._upload(host, tmp_path, run_result=0) + + assert result is True + mock_setup.assert_called_once_with() + mock_run.assert_called_once() + run_args = mock_run.call_args[0] + assert "-t" in run_args + assert "upload" in run_args + assert "--upload-port" in run_args + assert host in run_args + + def test_serial_upload_failure_raises( + self, setup_core: Path, tmp_path: Path + ) -> None: + """A non-zero PlatformIO result must raise EsphomeError.""" + with pytest.raises(EsphomeError, match="Upload failed"): + self._upload("/dev/ttyACM0", tmp_path, run_result=1) + + # --------------------------------------------------------------------------- # Serial DFU upload path # --------------------------------------------------------------------------- diff --git a/tests/unit_tests/test_platformio_library.py b/tests/unit_tests/test_platformio_library.py index 03360eab37..c0a0c678db 100644 --- a/tests/unit_tests/test_platformio_library.py +++ b/tests/unit_tests/test_platformio_library.py @@ -133,18 +133,8 @@ def test_resolve_registry_version_raises_without_pkg_file(monkeypatch): _resolve_registry_version("owner", "pkg", set()) -def _patch_download_with_manifests(monkeypatch, tmp_path, manifests, *, properties=()): - """Fake ConvertedLibrary.download to materialize canned manifests on disk.""" - - def fake_download(self, force=False, salt="", namespace=""): - self.path = tmp_path / self.get_sanitized_name().replace("/", "__") - self.path.mkdir(parents=True, exist_ok=True) - if self.name in properties: - (self.path / "library.properties").write_text(manifests[self.name]) - else: - (self.path / "library.json").write_text(json.dumps(manifests[self.name])) - - monkeypatch.setattr(ConvertedLibrary, "download", fake_download) +def _patch_registry_resolve(monkeypatch: pytest.MonkeyPatch) -> None: + """Stub the registry lookup so tests never touch the network.""" monkeypatch.setattr( lib, "_resolve_registry_version", @@ -157,6 +147,21 @@ def _patch_download_with_manifests(monkeypatch, tmp_path, manifests, *, properti ) +def _patch_download_with_manifests(monkeypatch, tmp_path, manifests, *, properties=()): + """Fake ConvertedLibrary.download to materialize canned manifests on disk.""" + + def fake_download(self, force=False, salt="", namespace=""): + self.path = tmp_path / self.get_require_name() + self.path.mkdir(parents=True, exist_ok=True) + if self.name in properties: + (self.path / "library.properties").write_text(manifests[self.name]) + else: + (self.path / "library.json").write_text(json.dumps(manifests[self.name])) + + monkeypatch.setattr(ConvertedLibrary, "download", fake_download) + _patch_registry_resolve(monkeypatch) + + def test_convert_libraries_parses_library_properties(tmp_path, monkeypatch): # A manifest provided as library.properties (Arduino style) instead of # library.json must still be parsed and converted. @@ -212,6 +217,106 @@ def test_convert_libraries_handles_unparsable_dependency_version(tmp_path, monke assert [d.name for d in top[0].dependencies] == ["C"] +def _patch_download_without_manifest( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, *, manifest_on_force: bool +) -> list[bool]: + """Fake ConvertedLibrary.download that leaves the manifest missing. + + When ``manifest_on_force`` is set, a forced re-download writes a valid + library.json, simulating a broken cache entry that heals on retry. + Returns the list of ``force`` values download was called with. + """ + calls: list[bool] = [] + + def fake_download( + self: ConvertedLibrary, force: bool = False, salt: str = "", namespace: str = "" + ) -> None: + calls.append(force) + self.path = tmp_path / self.get_require_name() + self.path.mkdir(parents=True, exist_ok=True) + if force and manifest_on_force: + (self.path / "library.json").write_text(json.dumps({"name": "A"})) + + monkeypatch.setattr(ConvertedLibrary, "download", fake_download) + _patch_registry_resolve(monkeypatch) + return calls + + +def test_convert_libraries_redownloads_when_manifest_missing( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + # A cached copy without any manifest (e.g. an interrupted clone or + # extraction) triggers exactly one forced re-download and then succeeds. + calls = _patch_download_without_manifest( + monkeypatch, tmp_path, manifest_on_force=True + ) + + top = convert_libraries([Library("esphome/A", "1.0.0", None)], _backend()) + + assert calls == [False, True] + assert top[0].data["name"] == "A" + + +def test_convert_libraries_raises_when_manifest_missing_after_retry( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + # If the forced re-download still yields no manifest, the error is raised + # after exactly one retry (no retry loop). The error must name the cache + # directory so users can find the broken entry instead of guessing where + # the library was unpacked. + calls = _patch_download_without_manifest( + monkeypatch, tmp_path, manifest_on_force=False + ) + + with pytest.raises(RuntimeError, match="Invalid PIO library") as excinfo: + convert_libraries([Library("esphome/A", "1.0.0", None)], _backend()) + + assert calls == [False, True] + assert str(tmp_path / "esphome__A") in str(excinfo.value) + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + (None, None), + ("", None), + ("http://[::1", None), # malformed IPv6 makes urlsplit raise ValueError + ("foo/bar", None), + ("file:///no/host", None), + ("https://github.com/x/y", "https://github.com/x/y"), + ], +) +def test_url_or_none(value: str | None, expected: str | None) -> None: + assert lib._url_or_none(value) == expected + + +def test_convert_libraries_url_in_name_resolves_as_git( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + # add_library("https://github.com/x/y", None) puts a git URL in the name + # position; it must resolve as a git source and never hit the registry. + _patch_download_with_manifests( + monkeypatch, tmp_path, {"pstolarz/OneWireNg": {"name": "OneWireNg"}} + ) + + def fail_registry(owner: str, pkgname: str, requirements: set[str]) -> None: + raise AssertionError(f"registry consulted for {owner}/{pkgname}") + + # After the helper so this stub wins over the helper's benign one + monkeypatch.setattr(lib, "_resolve_registry_version", fail_registry) + + top = convert_libraries( + [Library("https://github.com/pstolarz/OneWireNg", None, None)], _backend() + ) + + assert [c.name for c in top] == ["pstolarz/OneWireNg"] + assert top[0].data["name"] == "OneWireNg" + source = top[0].source + assert isinstance(source, GitSource) + assert source.url == "https://github.com/pstolarz/OneWireNg" + assert source.ref is None + + def test_convert_libraries_skips_incompatible_dependency(tmp_path, monkeypatch): # A dependency that declares an incompatible platform is skipped (the # top-level library still builds). diff --git a/tests/unit_tests/test_platformio_toolchain.py b/tests/unit_tests/test_platformio_toolchain.py index 568b43a259..723646fbd6 100644 --- a/tests/unit_tests/test_platformio_toolchain.py +++ b/tests/unit_tests/test_platformio_toolchain.py @@ -2,12 +2,14 @@ # pylint: disable=protected-access +from collections.abc import Generator from contextlib import contextmanager from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer import json import os from pathlib import Path import shutil +import sys import threading from types import SimpleNamespace from unittest.mock import MagicMock, Mock, call, patch @@ -320,6 +322,149 @@ def test_run_platformio_cli_sets_environment_variables( assert "arg" in args +def test_ccache_env_enabled_by_default(setup_core: Path) -> None: + """Ccache is enabled when the binary is on PATH and no override is set.""" + CORE.build_path = setup_core / "build" / "test" + + with ( + patch.dict(os.environ, {}, clear=True), + patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"), + ): + env = toolchain._ccache_env() + + assert env["ESPHOME_CCACHE_ENABLE"] == "1" + assert env["CCACHE_BASEDIR"] == str((setup_core / "build" / "test").resolve()) + assert env["CCACHE_DIR"].endswith("platformio-ccache") + assert env["CCACHE_NOHASHDIR"] == "true" + # Nothing may leak into os.environ: a later ESP-IDF build in the same + # process would otherwise skip its own ccache defaults. + assert "CCACHE_BASEDIR" not in os.environ + assert "ESPHOME_CCACHE_ENABLE" not in os.environ + + +def test_ccache_env_disabled_without_binary(setup_core: Path) -> None: + """Ccache stays off when the binary is not on PATH.""" + CORE.build_path = setup_core / "build" / "test" + + with ( + patch.dict(os.environ, {}, clear=True), + patch.object(toolchain.shutil, "which", return_value=None), + ): + env = toolchain._ccache_env() + + assert env == {"ESPHOME_CCACHE_ENABLE": "0"} + + +def test_ccache_env_opt_out(setup_core: Path) -> None: + """ESPHOME_CCACHE_ENABLE=0 disables ccache even with the binary present.""" + CORE.build_path = setup_core / "build" / "test" + + with ( + patch.dict(os.environ, {"ESPHOME_CCACHE_ENABLE": "0"}, clear=True), + patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"), + ): + env = toolchain._ccache_env() + + assert env == {"ESPHOME_CCACHE_ENABLE": "0"} + + +def test_ccache_env_normalizes_enable_value(setup_core: Path) -> None: + """A truthy override value is normalized to "1" for the build scripts.""" + CORE.build_path = setup_core / "build" / "test" + + with ( + patch.dict(os.environ, {"ESPHOME_CCACHE_ENABLE": "yes"}, clear=True), + patch.object(toolchain.shutil, "which", return_value=None), + ): + env = toolchain._ccache_env() + + assert env["ESPHOME_CCACHE_ENABLE"] == "1" + + +def test_ccache_env_respects_user_values_and_refreshes_basedir( + setup_core: Path, +) -> None: + """User CCACHE_* values win, but CCACHE_BASEDIR follows the build dir.""" + user_env = { + "CCACHE_DIR": "/custom/cache", + "CCACHE_BASEDIR": "/stale/other-device", + } + CORE.build_path = setup_core / "build" / "test" + + with ( + patch.dict(os.environ, user_env, clear=True), + patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"), + ): + env = toolchain._ccache_env() + + # CCACHE_DIR is not returned, so the user's os.environ value applies in + # the subprocess; CCACHE_BASEDIR is always refreshed to the build dir. + assert "CCACHE_DIR" not in env + assert env["CCACHE_BASEDIR"] == str((setup_core / "build" / "test").resolve()) + + +def test_run_platformio_cli_passes_ccache_env_to_subprocess_only( + setup_core: Path, mock_run_external_process: Mock +) -> None: + """The ccache settings reach the subprocess env without touching os.environ.""" + CORE.build_path = str(setup_core / "build" / "test") + + with ( + patch.dict(os.environ, {}, clear=False), + patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"), + ): + os.environ.pop("ESPHOME_CCACHE_ENABLE", None) + mock_run_external_process.return_value = 0 + toolchain.run_platformio_cli("test", "arg") + + env = mock_run_external_process.call_args[1]["env"] + assert env["ESPHOME_CCACHE_ENABLE"] == "1" + assert env["CCACHE_BASEDIR"] == str((setup_core / "build" / "test").resolve()) + assert "ESPHOME_CCACHE_ENABLE" not in os.environ + assert "CCACHE_BASEDIR" not in os.environ + + +def test_ccache_env_requires_build_path(setup_core: Path) -> None: + """Enabling ccache without a build path fails loudly.""" + CORE.build_path = None + + with ( + patch.dict(os.environ, {}, clear=True), + patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"), + pytest.raises(ValueError, match="CORE.build_path must be set"), + ): + toolchain._ccache_env() + + +def test_run_platformio_cli_merges_caller_env( + setup_core: Path, mock_run_external_process: Mock +) -> None: + """A caller-supplied env is the base and gains the ccache settings.""" + CORE.build_path = str(setup_core / "build" / "test") + + with patch.object(toolchain.shutil, "which", return_value="/usr/bin/ccache"): + mock_run_external_process.return_value = 0 + toolchain.run_platformio_cli( + "test", env={"CUSTOM_VAR": "1", "ESPHOME_CCACHE_ENABLE": "0"} + ) + + env = mock_run_external_process.call_args[1]["env"] + assert env["CUSTOM_VAR"] == "1" + # The normalized enable flag still lands in the subprocess env. + assert "ESPHOME_CCACHE_ENABLE" in env + + +def test_copy_ccache_script(setup_core: Path) -> None: + """The shared ccache pre-script is copied into the build dir.""" + CORE.build_path = setup_core / "build" / "test" + + toolchain.copy_ccache_script() + + dest = setup_core / "build" / "test" / "ccache.py" + source = Path(toolchain.__file__).parent / "ccache.py.script" + assert dest.read_text() == source.read_text() + + @pytest.mark.parametrize( ("platform", "input_path", "expected"), [ @@ -373,7 +518,10 @@ def test_run_platformio_cli_strips_win_long_path_prefix( ) with ( - patch.dict(os.environ, {}, clear=False), + # Pin ccache off: patching sys.platform to win32 (sys is a singleton, + # so the stdlib sees it too) would send shutil.which down the Windows + # code path, which crashes on a POSIX host. + patch.dict(os.environ, {"ESPHOME_CCACHE_ENABLE": "0"}, clear=False), patch("esphome.platformio.toolchain.sys.platform", "win32"), patch("esphome.platformio.toolchain.sys.executable", prefixed_exe), ): @@ -1093,3 +1241,361 @@ def test_filter_platformio_lines_blocks_noisy_messages(msg: str) -> None: def test_filter_platformio_lines_allows_other_messages(msg: str) -> None: """Test that non-noisy platformio output lines pass through RedirectText.""" assert _filter_through_redirect(msg) == msg + "\n" + + +# --------------------------------------------------------------------------- +# PlatformIO python-version cache heal +# --------------------------------------------------------------------------- + +_CURRENT_MINOR = f"{sys.version_info.major}.{sys.version_info.minor}" +# Captured before the autouse guard patches the name, so tests can exercise the +# real implementation. +_REAL_GET_PLATFORMIO_CONFIG = toolchain.get_platformio_config + + +@pytest.fixture(autouse=True) +def _guard_real_platformio() -> Generator[None, None, None]: + """Default the PlatformIO config lookup to None so no test in this module + touches a real ~/.platformio; the heal tests re-patch it at a temp dir.""" + with patch.object(toolchain, "get_platformio_config", return_value=None): + yield + + +def _pio_layout(core_dir: Path) -> dict[str, Path]: + """Return the PlatformIO dir layout with cache/packages/platforms under core.""" + return { + "core_dir": core_dir, + "packages_dir": core_dir / "packages", + "platforms_dir": core_dir / "platforms", + "cache_dir": core_dir / ".cache", + } + + +def _split_pio_layout(tmp_path: Path) -> dict[str, Path]: + """Container-shape layout: caches on a persistent root, core_dir ephemeral.""" + persistent = tmp_path / "data" / "platformio" + return { + "core_dir": tmp_path / "root" / ".platformio", + "platforms_dir": persistent / "platforms", + "packages_dir": persistent / "packages", + "cache_dir": persistent / "cache", + } + + +def _seed_layout(layout: dict[str, Path]) -> None: + """Populate each cache dir (and the core penv) with a marker file.""" + for key in ("platforms_dir", "packages_dir", "cache_dir"): + layout[key].mkdir(parents=True, exist_ok=True) + (layout[key] / "marker").write_text("x", encoding="utf-8") + penv = layout["core_dir"] / "penv" + penv.mkdir(parents=True, exist_ok=True) + (penv / "marker").write_text("x", encoding="utf-8") + + +def _make_pio_config(layout: dict[str, Path] | Path) -> MagicMock: + """A ProjectConfig stand-in resolving platformio dir options from *layout*.""" + resolved = _pio_layout(layout) if isinstance(layout, Path) else layout + config = MagicMock() + config.get.side_effect = lambda section, option: ( + str(resolved[option]) if section == "platformio" else "" + ) + return config + + +@contextmanager +def _use_pio_config(layout: dict[str, Path] | Path) -> Generator[MagicMock, None, None]: + """Point ``get_platformio_config`` at a temp layout for the block.""" + config = _make_pio_config(layout) + with patch.object(toolchain, "get_platformio_config", return_value=config): + yield config + + +def _stamp_version(core_dir: Path) -> str | None: + """Read the python version recorded in the heal stamp under *core_dir*.""" + return toolchain._read_pio_stamp_python(core_dir / toolchain._PIO_PYTHON_STAMP_FILE) + + +def _cache_wiped(core_dir: Path) -> bool: + """True when the seeded cache subdir markers are gone.""" + return not any( + (core_dir / sub / "marker").exists() + for sub in ("packages", "platforms", ".cache") + ) + + +@pytest.fixture +def pio_core_dir(tmp_path: Path) -> Path: + """A populated PlatformIO core dir (packages/platforms/.cache/penv seeded).""" + core = tmp_path / "dot-platformio" + for sub in ("packages", "platforms", ".cache", "penv"): + seeded = core / sub + seeded.mkdir(parents=True) + (seeded / "marker").write_text("x", encoding="utf-8") + return core + + +def test_current_python_minor_matches_running_interpreter() -> None: + """_current_python_minor returns major.minor of the running interpreter.""" + assert toolchain._current_python_minor() == _CURRENT_MINOR + + +def test_pio_stamp_round_trip(tmp_path: Path) -> None: + """The stamp writer/reader round-trips and records the schema version.""" + stamp = tmp_path / toolchain._PIO_PYTHON_STAMP_FILE + toolchain._write_pio_stamp_python(stamp, "3.13") + assert toolchain._read_pio_stamp_python(stamp) == "3.13" + assert json.loads(stamp.read_text()) == { + "schema_version": toolchain._PIO_PYTHON_STAMP_SCHEMA, + "python_version": "3.13", + } + + +def test_read_pio_stamp_missing(tmp_path: Path) -> None: + """A missing stamp file yields None.""" + assert toolchain._read_pio_stamp_python(tmp_path / "nope.json") is None + + +def test_read_pio_stamp_malformed(tmp_path: Path) -> None: + """A corrupt stamp file yields None instead of raising.""" + stamp = tmp_path / "bad.json" + stamp.write_text("{not json", encoding="utf-8") + assert toolchain._read_pio_stamp_python(stamp) is None + + +def test_read_pio_stamp_unreadable_logs_warning( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A present-but-unreadable stamp yields None and warns.""" + stamp = tmp_path / "stamp.json" + stamp.mkdir() + with caplog.at_level("WARNING"): + assert toolchain._read_pio_stamp_python(stamp) is None + assert "Could not read" in caplog.text + + +def test_read_pio_stamp_without_python_version(tmp_path: Path) -> None: + """A stamp missing python_version yields None.""" + stamp = tmp_path / "s.json" + stamp.write_text(json.dumps({"schema_version": "0"}), encoding="utf-8") + assert toolchain._read_pio_stamp_python(stamp) is None + + +@pytest.mark.parametrize("payload", ["42", '"x"', "[1, 2]", "null"]) +def test_read_pio_stamp_non_object_json(tmp_path: Path, payload: str) -> None: + """Valid-but-non-object JSON in the stamp yields None, not a crash.""" + stamp = tmp_path / "s.json" + stamp.write_text(payload, encoding="utf-8") + assert toolchain._read_pio_stamp_python(stamp) is None + + +def test_clean_platformio_cache_none_config_is_noop() -> None: + """clean_platformio_cache is a no-op when PlatformIO is unavailable.""" + with patch.object(toolchain, "get_platformio_config", return_value=None): + toolchain.clean_platformio_cache() + + +def test_clean_platformio_cache_wipes_everything(pio_core_dir: Path) -> None: + """clean_platformio_cache removes cache/packages/platforms and core_dir.""" + with _use_pio_config(pio_core_dir): + toolchain.clean_platformio_cache() + assert not pio_core_dir.exists() + + +def test_heal_none_config_is_noop() -> None: + """Heal is a no-op (no error) when PlatformIO is unavailable.""" + with patch.object(toolchain, "get_platformio_config", return_value=None): + toolchain.heal_platformio_python_env() + + +def test_heal_fresh_cache_stamps_without_wipe(tmp_path: Path) -> None: + """A fresh core dir (no stamp, no penv) is stamped, not wiped.""" + core = tmp_path / "pio" + with _use_pio_config(core): + toolchain.heal_platformio_python_env() + assert _stamp_version(core) == _CURRENT_MINOR + + +def test_heal_stamp_matches_current_no_wipe(pio_core_dir: Path) -> None: + """A stamp matching the running interpreter leaves the cache untouched.""" + toolchain._write_pio_stamp_python( + pio_core_dir / toolchain._PIO_PYTHON_STAMP_FILE, _CURRENT_MINOR + ) + with _use_pio_config(pio_core_dir): + toolchain.heal_platformio_python_env() + assert not _cache_wiped(pio_core_dir) + assert (pio_core_dir / "penv" / "marker").exists() + + +def test_heal_stale_stamp_wipes_and_restamps(pio_core_dir: Path) -> None: + """A stamp from an older interpreter triggers a wipe + restamp; core_dir stays.""" + toolchain._write_pio_stamp_python( + pio_core_dir / toolchain._PIO_PYTHON_STAMP_FILE, "2.7" + ) + with _use_pio_config(pio_core_dir): + toolchain.heal_platformio_python_env() + assert _cache_wiped(pio_core_dir) + assert not (pio_core_dir / "penv").exists() + assert pio_core_dir.is_dir() + assert _stamp_version(pio_core_dir) == _CURRENT_MINOR + + +def test_heal_no_stamp_existing_cache_wipes_once( + pio_core_dir: Path, caplog: pytest.LogCaptureFixture +) -> None: + """An existing cache with no stamp is cleaned once and stamped.""" + with _use_pio_config(pio_core_dir), caplog.at_level("INFO"): + toolchain.heal_platformio_python_env() + assert _cache_wiped(pio_core_dir) + assert not (pio_core_dir / "penv").exists() + assert _stamp_version(pio_core_dir) == _CURRENT_MINOR + assert "once" in caplog.text + + +def test_heal_no_stamp_penv_only_counts_as_cache(tmp_path: Path) -> None: + """A core dir holding only a penv still triggers the one-time clean.""" + core = tmp_path / "pio" + penv = core / "penv" + penv.mkdir(parents=True) + (penv / "marker").write_text("x", encoding="utf-8") + with _use_pio_config(core): + toolchain.heal_platformio_python_env() + assert not penv.exists() + assert _stamp_version(core) == _CURRENT_MINOR + + +def test_heal_oserror_is_nonfatal( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A filesystem failure during the check warns instead of aborting the build.""" + blocker = tmp_path / "pio" + blocker.write_text("not a directory", encoding="utf-8") + with _use_pio_config(blocker), caplog.at_level("WARNING"): + toolchain.heal_platformio_python_env() + assert "build environment check failed" in caplog.text + + +def test_heal_stamp_write_failure_is_nonfatal( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: + """A failed stamp write (EsphomeError from write_file) warns, not aborts.""" + with ( + _use_pio_config(tmp_path / "pio"), + patch.object( + toolchain, + "_write_pio_stamp_python", + side_effect=EsphomeError("disk full"), + ), + caplog.at_level("WARNING"), + ): + toolchain.heal_platformio_python_env() + assert "build environment check failed" in caplog.text + + +def test_heal_is_idempotent_across_runs(pio_core_dir: Path) -> None: + """After a heal writes the stamp, a re-provisioned cache is not wiped again.""" + with _use_pio_config(pio_core_dir): + toolchain.heal_platformio_python_env() + repop = pio_core_dir / "packages" + repop.mkdir(exist_ok=True) + (repop / "marker").write_text("x", encoding="utf-8") + toolchain.heal_platformio_python_env() + assert (pio_core_dir / "packages" / "marker").exists() + + +def test_pio_stamp_dir_is_platforms_parent(tmp_path: Path) -> None: + """The stamp home is the parent of platforms_dir, not core_dir.""" + layout = _split_pio_layout(tmp_path) + config = _make_pio_config(layout) + assert toolchain._pio_stamp_dir(config) == layout["platforms_dir"].parent + nested = _make_pio_config(tmp_path / "pio") + assert toolchain._pio_stamp_dir(nested) == tmp_path / "pio" + + +def test_heal_container_layout_stamps_persistent_root(tmp_path: Path) -> None: + """Container shape: the stamp lands on the persistent cache root.""" + layout = _split_pio_layout(tmp_path) + with _use_pio_config(layout): + toolchain.heal_platformio_python_env() + persistent = layout["platforms_dir"].parent + assert _stamp_version(persistent) == _CURRENT_MINOR + assert not (layout["core_dir"] / toolchain._PIO_PYTHON_STAMP_FILE).exists() + + +def test_heal_container_layout_stale_stamp_wipes_persistent_cache( + tmp_path: Path, +) -> None: + """Container shape: a stale stamp wipes the relocated persistent caches.""" + layout = _split_pio_layout(tmp_path) + _seed_layout(layout) + persistent = layout["platforms_dir"].parent + toolchain._write_pio_stamp_python( + persistent / toolchain._PIO_PYTHON_STAMP_FILE, "2.7" + ) + with _use_pio_config(layout): + toolchain.heal_platformio_python_env() + for key in ("platforms_dir", "packages_dir", "cache_dir"): + assert not layout[key].exists() + assert not (layout["core_dir"] / "penv").exists() + assert _stamp_version(persistent) == _CURRENT_MINOR + + +def test_heal_container_layout_survives_core_dir_wipe(tmp_path: Path) -> None: + """A python change is still detected after an image update wiped core_dir.""" + layout = _split_pio_layout(tmp_path) + _seed_layout(layout) + shutil.rmtree(layout["core_dir"]) + persistent = layout["platforms_dir"].parent + toolchain._write_pio_stamp_python( + persistent / toolchain._PIO_PYTHON_STAMP_FILE, "2.7" + ) + with _use_pio_config(layout): + toolchain.heal_platformio_python_env() + for key in ("platforms_dir", "packages_dir", "cache_dir"): + assert not layout[key].exists() + assert _stamp_version(persistent) == _CURRENT_MINOR + + +def test_get_platformio_config_returns_project_config() -> None: + """The real lookup returns a usable ProjectConfig when PlatformIO is present.""" + config = _REAL_GET_PLATFORMIO_CONFIG() + assert config is not None + assert hasattr(config, "get") + + +def test_get_platformio_config_none_when_platformio_absent() -> None: + """The lookup returns None when PlatformIO cannot be imported.""" + with patch.dict(sys.modules, {"platformio.project.config": None}): + assert _REAL_GET_PLATFORMIO_CONFIG() is None + + +def test_delete_platformio_dirs_skips_missing(tmp_path: Path) -> None: + """A named dir that does not exist is skipped without error.""" + (tmp_path / "packages").mkdir() + (tmp_path / "packages" / "marker").write_text("x", encoding="utf-8") + config = _make_pio_config(tmp_path) + # platforms_dir does not exist; packages_dir does. + toolchain._delete_platformio_dirs(config, ["packages_dir", "platforms_dir"]) + assert not (tmp_path / "packages").exists() + + +def test_heal_stale_stamp_wipes_when_penv_absent(pio_core_dir: Path) -> None: + """The penv wipe is skipped cleanly when no penv exists.""" + shutil.rmtree(pio_core_dir / "penv") + toolchain._write_pio_stamp_python( + pio_core_dir / toolchain._PIO_PYTHON_STAMP_FILE, "2.7" + ) + with _use_pio_config(pio_core_dir): + toolchain.heal_platformio_python_env() + assert _cache_wiped(pio_core_dir) + assert _stamp_version(pio_core_dir) == _CURRENT_MINOR + + +def test_run_platformio_cli_invokes_heal( + setup_core: Path, mock_run_external_process: Mock +) -> None: + """run_platformio_cli runs the heal before spawning PlatformIO.""" + CORE.build_path = str(setup_core / "build" / "test") + mock_run_external_process.return_value = 0 + with patch.object(toolchain, "heal_platformio_python_env") as mock_heal: + toolchain.run_platformio_cli("test") + mock_heal.assert_called_once()