From 4babf408e452769e560bbb467e2509c5b1938a1d Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Fri, 31 Jul 2026 11:03:59 +0200 Subject: [PATCH 01/77] update version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8ba3fd9f..7b19d147 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ VERSION_MAJOR = 2 VERSION_MINOR = 2 -PATCHLEVEL = 7 +PATCHLEVEL = 8 VERSION_TWEAK = 0 EXTRAVERSION = From a2998d6c2c90653d256f12fc624686604a77a8e7 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 20 Jul 2026 14:13:15 +0200 Subject: [PATCH 02/77] feat(unit_tests): Add unit test framework --- CONTRIBUTING.md | 2 + tests/README.md | 77 +++++++++++++ tests/unit/sensor_component/CMakeLists.txt | 17 +++ tests/unit/sensor_component/prj.conf | 3 + tests/unit/sensor_component/src/main.cpp | 121 +++++++++++++++++++++ tests/unit/sensor_component/testcase.yaml | 9 ++ 6 files changed, 229 insertions(+) create mode 100644 tests/README.md create mode 100644 tests/unit/sensor_component/CMakeLists.txt create mode 100644 tests/unit/sensor_component/prj.conf create mode 100644 tests/unit/sensor_component/src/main.cpp create mode 100644 tests/unit/sensor_component/testcase.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 350fdd3b..2009200d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,8 @@ This repository contains the OpenEarable 2 firmware for the `openearable_v2/nrf5 The main firmware lives at the repository root and is built through Zephyr and the nRF Connect SDK. +Host-based unit tests live under `tests/unit`. See [tests/README.md](tests/README.md) for the TDD workflow, regression-test conventions, and branch-coverage command. + - `src/audio`, `src/bluetooth`, `src/modules`: runtime audio, Bluetooth, and application modules. - `src/SensorManager`, `src/Battery`, `src/SD_Card`, `src/time_sync`: sensor, power, storage, and synchronization subsystems. - `src/drivers`, `src/Wire`, `src/utils`, `src/buttons`, `src/ParseInfo`: reusable device support and shared utilities. diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 00000000..31904a73 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,77 @@ +# Testing + +OpenEarable uses the Unity and CMock integration supplied by nRF Connect SDK, +with Zephyr's Twister test runner. This keeps the tests compatible with the SDK +version pinned in `west.yml` and provides one workflow for unit tests, simulated +integration tests, on-target tests, and coverage. + +## Run the unit tests + +Run these commands from the root of the west workspace (the directory that +contains `zephyr`, `nrf`, and this repository): + +Unity's generated runner requires Ruby. On Debian/Ubuntu, install the host +dependency with `sudo apt install ruby`. + +```sh +python3 zephyr/scripts/twister \ + -T open-earable-v2/tests/unit \ + -p native_sim/native/64 \ + --inline-logs +``` + +Replace `open-earable-v2` if the application directory has a different name. +The `native_sim` target builds a native Linux executable and does not require an +nRF5340 or other target hardware. It is suitable for local TDD. Run it in WSL or +a Linux container on Windows; GitHub Actions provides a Linux environment for +every pull request. + +To run one scenario while developing: + +```sh +python3 zephyr/scripts/twister \ + -T open-earable-v2/tests/unit \ + -p native_sim/native/64 \ + --scenario openearable.unit.sensor_component \ + --inline-logs +``` + +## Generate branch coverage + +Install `gcovr` in the active Python environment, then add Twister's coverage +option: + +```sh +python3 -m pip install gcovr +python3 zephyr/scripts/twister \ + -T open-earable-v2/tests/unit \ + -p native_sim/native/64 \ + --coverage \ + --coverage-basedir open-earable-v2 +``` + +Open `twister-out/coverage/index.html`. The HTML report includes line and branch +coverage. Coverage is a development aid rather than a repository-wide gate: +new, self-contained classes can target full branch coverage without forcing +hardware-dependent firmware paths into host unit tests. + +## Add a test + +Create a directory under `tests/unit//` with: + +- `CMakeLists.txt` using `test_runner_generate(...)` and adding the test plus + production sources to `app`; +- `prj.conf` with `CONFIG_UNITY=y` (and `CONFIG_CPP=y` plus + `CONFIG_REQUIRES_FULL_LIBCPP=y` for production code using the C++ standard + library); +- `testcase.yaml` allowing `native_sim/native/64`; and +- test functions prefixed with `test_` and Unity's `TEST_ASSERT_*` macros. + +Keep business logic independent from Zephyr drivers where practical. For a +module that calls hardware or Zephyr APIs, generate mocks with CMock's +`cmock_handle(...)`. Ztest remains available for tests that specifically benefit +from Zephyr test fixtures or other RTOS-aware scaffolding. + +Every bug fix should add a test that fails before the fix and passes after it. +Name the test after the observable behavior, and mention the issue or regression +in a short comment when the scenario is not self-explanatory. diff --git a/tests/unit/sensor_component/CMakeLists.txt b/tests/unit/sensor_component/CMakeLists.txt new file mode 100644 index 00000000..001cd348 --- /dev/null +++ b/tests/unit/sensor_component/CMakeLists.txt @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sensor_component_tests) + +test_runner_generate(src/main.cpp) + +target_include_directories(app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/ParseInfo +) + +target_sources(app PRIVATE + src/main.cpp + ../../../src/ParseInfo/SensorComponent.cpp +) diff --git a/tests/unit/sensor_component/prj.conf b/tests/unit/sensor_component/prj.conf new file mode 100644 index 00000000..b8c4533b --- /dev/null +++ b/tests/unit/sensor_component/prj.conf @@ -0,0 +1,3 @@ +CONFIG_UNITY=y +CONFIG_CPP=y +CONFIG_REQUIRES_FULL_LIBCPP=y diff --git a/tests/unit/sensor_component/src/main.cpp b/tests/unit/sensor_component/src/main.cpp new file mode 100644 index 00000000..54106e7f --- /dev/null +++ b/tests/unit/sensor_component/src/main.cpp @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +#include "SensorComponent.h" + +static SensorComponent components[] = { + {.name = "x", .unit = "m/s2", .parseType = PARSE_TYPE_FLOAT}, + {.name = "quality", .unit = "%", .parseType = PARSE_TYPE_UINT8}, +}; + +static SensorComponentGroup group = { + .name = "acceleration", + .componentCount = sizeof(components) / sizeof(components[0]), + .components = components, +}; + +void setUp(void) +{ +} + +extern "C" int unity_c_suite_teardown(int failures) asm("test_suiteTearDown"); + +int test_suiteTearDown(int failures) +{ + return unity_c_suite_teardown(failures); +} + +void test_size_matches_serialized_size(void) +{ + char buffer[64] = {0}; + + const size_t expected_size = getSensorComponentGroupSize(&group); + const ssize_t bytes_written = serializeSensorComponentGroup(&group, buffer, sizeof(buffer)); + + TEST_ASSERT_EQUAL_INT64(static_cast(expected_size), bytes_written); +} + +void test_serializes_all_component_fields(void) +{ + char buffer[64] = {0}; + const char *cursor = buffer; + + const ssize_t bytes_written = serializeSensorComponentGroup(&group, buffer, sizeof(buffer)); + + TEST_ASSERT_GREATER_THAN_INT64(0, bytes_written); + + for (size_t i = 0; i < group.componentCount; ++i) { + const SensorComponent *component = &components[i]; + uint8_t length; + + TEST_ASSERT_EQUAL_UINT8(component->parseType, static_cast(*cursor++)); + + length = static_cast(*cursor++); + TEST_ASSERT_EQUAL_UINT8(strlen(group.name), length); + TEST_ASSERT_EQUAL_MEMORY(group.name, cursor, length); + cursor += length; + + length = static_cast(*cursor++); + TEST_ASSERT_EQUAL_UINT8(strlen(component->name), length); + TEST_ASSERT_EQUAL_MEMORY(component->name, cursor, length); + cursor += length; + + length = static_cast(*cursor++); + TEST_ASSERT_EQUAL_UINT8(strlen(component->unit), length); + TEST_ASSERT_EQUAL_MEMORY(component->unit, cursor, length); + cursor += length; + } + + TEST_ASSERT_EQUAL_INT64(bytes_written, cursor - buffer); +} + +void test_accepts_exactly_sized_buffer(void) +{ + char buffer[64] = {0}; + const size_t required_size = getSensorComponentGroupSize(&group); + + const ssize_t bytes_written = serializeSensorComponentGroup(&group, buffer, required_size); + + TEST_ASSERT_EQUAL_INT64(static_cast(required_size), bytes_written); +} + +void test_rejects_buffer_one_byte_too_small(void) +{ + char buffer[64]; + const size_t required_size = getSensorComponentGroupSize(&group); + + memset(buffer, 0x5a, sizeof(buffer)); + const ssize_t result = serializeSensorComponentGroup(&group, buffer, required_size - 1); + + TEST_ASSERT_EQUAL_INT64(-1, result); + for (const char byte : buffer) { + TEST_ASSERT_EQUAL_HEX8(0x5a, byte); + } +} + +void test_empty_group_has_empty_serialization(void) +{ + SensorComponentGroup empty_group = { + .name = "empty", + .componentCount = 0, + .components = nullptr, + }; + char buffer = 0x5a; + + TEST_ASSERT_EQUAL_UINT64(0, getSensorComponentGroupSize(&empty_group)); + TEST_ASSERT_EQUAL_INT64(0, serializeSensorComponentGroup(&empty_group, &buffer, 0)); + TEST_ASSERT_EQUAL_HEX8(0x5a, buffer); +} + +/* Zephyr reserves nonzero returns from main, so ignore Unity's return value. */ +extern int unity_main(void); + +int main(void) +{ + (void)unity_main(); + return 0; +} diff --git a/tests/unit/sensor_component/testcase.yaml b/tests/unit/sensor_component/testcase.yaml new file mode 100644 index 00000000..9f64fc42 --- /dev/null +++ b/tests/unit/sensor_component/testcase.yaml @@ -0,0 +1,9 @@ +tests: + openearable.unit.sensor_component: + tags: + - unit + - parse_info + integration_platforms: + - native_sim/native/64 + platform_allow: + - native_sim/native/64 From e254231bf269310742fd8851e2436b7b3b2ab4fa Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 20 Jul 2026 14:23:15 +0200 Subject: [PATCH 03/77] feat(unit_test_pipeline): Add unit test pipeline --- .github/workflows/unit_tests.yaml | 140 +++++++++++++++++++++++++++ tests/README.md | 7 ++ tools/ci/render_unit_test_comment.py | 134 +++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 .github/workflows/unit_tests.yaml create mode 100644 tools/ci/render_unit_test_comment.py diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml new file mode 100644 index 00000000..6acc2306 --- /dev/null +++ b/.github/workflows/unit_tests.yaml @@ -0,0 +1,140 @@ +name: Unit Tests + +on: + workflow_dispatch: + pull_request: {} + push: + branches: + - main + +permissions: + contents: read + issues: write + pull-requests: write + +concurrency: + group: unit-tests-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + ZEPHYR_TOOLCHAIN_VARIANT: host + +jobs: + unity-native-sim: + name: Unity tests (native_sim 64-bit) + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Check out application + uses: actions/checkout@v5 + with: + # Keep the application below the west workspace root. + path: zephyr-workspace/open-earable-v2 + + - name: Install host dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: git cmake ninja-build gperf device-tree-compiler make gcc g++ ruby python3-dev python3-pip python3-setuptools python3-wheel + version: 1.0 + execute_install_scripts: true + + - name: Cache Python packages + uses: actions/cache@v5 + with: + path: ~/.cache/pip + key: unit-tests-${{ runner.os }}-pip-${{ hashFiles('zephyr-workspace/open-earable-v2/west.yml') }} + restore-keys: | + unit-tests-${{ runner.os }}-pip- + + - name: Initialize nRF Connect SDK workspace + run: | + python3 -m pip install west + cd zephyr-workspace + west init -l open-earable-v2 + west update --narrow -o=--depth=1 + python3 -m pip install -r zephyr/scripts/requirements.txt + + - name: Run Unity tests with Twister + id: twister + continue-on-error: true + run: | + set -o pipefail + cd zephyr-workspace + python3 zephyr/scripts/twister \ + -T open-earable-v2/tests/unit \ + -p native_sim/native/64 \ + --inline-logs \ + --outdir twister-out \ + 2>&1 | tee twister-console.log + + - name: Prepare test summary + if: always() + env: + TWISTER_OUTCOME: ${{ steps.twister.outcome }} + WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + ARTIFACTS_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts + run: | + python3 zephyr-workspace/open-earable-v2/tools/ci/render_unit_test_comment.py \ + --report zephyr-workspace/twister-out/twister.json \ + --console zephyr-workspace/twister-console.log \ + --output unit-test-comment.md \ + --outcome "$TWISTER_OUTCOME" \ + --run-url "$WORKFLOW_RUN_URL" \ + --artifact-url "$ARTIFACTS_URL" + cat unit-test-comment.md >> "$GITHUB_STEP_SUMMARY" + + - name: Publish test summary on pull request + if: always() && github.event_name == 'pull_request' + uses: actions/github-script@v8 + with: + script: | + const fs = require('fs'); + const marker = ''; + const body = fs.readFileSync('unit-test-comment.md', 'utf8'); + const comments = await github.paginate( + github.rest.issues.listComments, + { + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + per_page: 100, + } + ); + const existing = comments.find( + comment => comment.user.type === 'Bot' && comment.body.includes(marker) + ); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } + + - name: Upload Twister reports and logs + if: always() + uses: actions/upload-artifact@v6 + with: + name: unit-test-results + path: | + zephyr-workspace/twister-console.log + zephyr-workspace/twister-out/twister*.xml + zephyr-workspace/twister-out/twister.json + zephyr-workspace/twister-out/**/handler.log + zephyr-workspace/twister-out/**/build.log + if-no-files-found: warn + retention-days: 14 + + - name: Fail workflow when tests failed + if: steps.twister.outcome == 'failure' + run: exit 1 diff --git a/tests/README.md b/tests/README.md index 31904a73..325392c5 100644 --- a/tests/README.md +++ b/tests/README.md @@ -26,6 +26,13 @@ nRF5340 or other target hardware. It is suitable for local TDD. Run it in WSL or a Linux container on Windows; GitHub Actions provides a Linux environment for every pull request. +The dedicated `Unit Tests` GitHub Actions workflow runs the same command for +pull requests and pushes to `main`. Its `Unity tests (native_sim 64-bit)` job is +the check to select when configuring branch protection. The workflow creates or +updates one pull-request comment with the result and Unity failure diagnostics. +Twister XML, JSON, and log files are retained as the `unit-test-results` +artifact for 14 days. + To run one scenario while developing: ```sh diff --git a/tools/ci/render_unit_test_comment.py b/tools/ci/render_unit_test_comment.py new file mode 100644 index 00000000..3fbb5889 --- /dev/null +++ b/tools/ci/render_unit_test_comment.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +"""Render Twister results as a concise GitHub pull-request comment.""" + +from __future__ import annotations + +import argparse +import html +import json +import re +from pathlib import Path + +MARKER = "" +UNITY_RESULT = re.compile(r"^.*:\d+:test_.+:(?:PASS|FAIL)(?::.*)?$") +IMPORTANT_OUTPUT = re.compile( + r"(?:\b(?:ERROR|FAIL|FAILED)\b|PROJECT EXECUTION|Tests? \d+ Failures)", + re.IGNORECASE, +) + + +def load_suites(report_path: Path) -> list[dict]: + if not report_path.is_file(): + return [] + + try: + report = json.loads(report_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return [] + return report.get("testsuites", []) + + +def status_icon(status: str) -> str: + return { + "passed": "✅", + "failed": "❌", + "error": "🚨", + "skipped": "⏭️", + "filtered": "⏭️", + "blocked": "🚫", + }.get(status.lower(), "❔") + + +def failure_details(console_path: Path, suites: list[dict]) -> str: + console = "" + if console_path.is_file(): + console = console_path.read_text(encoding="utf-8", errors="replace") + + selected = [] + for line in console.splitlines(): + stripped = line.strip() + if UNITY_RESULT.match(stripped) or IMPORTANT_OUTPUT.search(stripped): + selected.append(stripped) + + if not selected: + for suite in suites: + if suite.get("status", "").lower() not in {"passed", "skipped", "filtered"}: + reason = suite.get("reason") or "No detailed failure message was reported." + selected.append(f"{suite.get('name', 'unknown suite')}: {reason}") + + if not selected and console: + selected = [line for line in console.splitlines() if line.strip()][-40:] + + # Keep comments readable and below GitHub's comment-size limit. + return "\n".join(selected[-100:])[-12000:] + + +def render(args: argparse.Namespace) -> str: + suites = load_suites(args.report) + statuses = [str(suite.get("status", "unknown")).lower() for suite in suites] + failed = sum(status not in {"passed", "skipped", "filtered"} for status in statuses) + passed = statuses.count("passed") + skipped = sum(status in {"skipped", "filtered"} for status in statuses) + succeeded = args.outcome == "success" and failed == 0 + + heading = "✅ Unit tests passed" if succeeded else "❌ Unit tests failed" + lines = [MARKER, f"## {heading}", ""] + lines.append( + f"**{passed} passed**, **{failed} failed/error**, **{skipped} skipped** " + f"— [view workflow run]({args.run_url})" + ) + + if suites: + lines.extend(["", "| Test scenario | Platform | Result |", "|---|---|---|"]) + for suite in suites: + status = str(suite.get("status", "unknown")) + lines.append( + f"| `{suite.get('name', 'unknown')}` " + f"| `{suite.get('platform', 'unknown')}` " + f"| {status_icon(status)} {status} |" + ) + elif not succeeded: + lines.extend(["", "Twister did not produce a JSON report; this may be an infrastructure failure."]) + + if not succeeded: + details = failure_details(args.console, suites) + if details: + lines.extend( + [ + "", + "
", + "Failure details", + "", + f"
{html.escape(details)}
", + "
", + ] + ) + + lines.extend( + [ + "", + f"[Download the `unit-test-results` artifact]({args.artifact_url}) " + "for full Twister reports and logs.", + ] + ) + return "\n".join(lines) + "\n" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--report", type=Path, required=True) + parser.add_argument("--console", type=Path, required=True) + parser.add_argument("--output", type=Path, required=True) + parser.add_argument("--outcome", choices=("success", "failure", "cancelled", "skipped"), required=True) + parser.add_argument("--run-url", required=True) + parser.add_argument("--artifact-url", required=True) + return parser.parse_args() + + +def main() -> None: + args = parse_args() + args.output.write_text(render(args), encoding="utf-8") + + +if __name__ == "__main__": + main() From 2d988976f755b0b18be78fd06f69daa240825710 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 20 Jul 2026 14:53:06 +0200 Subject: [PATCH 04/77] fix(unit-test-comment): Fix broken artifact link --- .github/workflows/unit_tests.yaml | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml index 6acc2306..3ffd8245 100644 --- a/.github/workflows/unit_tests.yaml +++ b/.github/workflows/unit_tests.yaml @@ -68,12 +68,27 @@ jobs: --outdir twister-out \ 2>&1 | tee twister-console.log + - name: Upload Twister reports and logs + id: unit_test_artifacts + if: always() + uses: actions/upload-artifact@v6 + with: + name: unit-test-results + path: | + zephyr-workspace/twister-console.log + zephyr-workspace/twister-out/twister*.xml + zephyr-workspace/twister-out/twister.json + zephyr-workspace/twister-out/**/handler.log + zephyr-workspace/twister-out/**/build.log + if-no-files-found: warn + retention-days: 14 + - name: Prepare test summary if: always() env: TWISTER_OUTCOME: ${{ steps.twister.outcome }} WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - ARTIFACTS_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts + ARTIFACT_URL: ${{ steps.unit_test_artifacts.outputs.artifact-url }} run: | python3 zephyr-workspace/open-earable-v2/tools/ci/render_unit_test_comment.py \ --report zephyr-workspace/twister-out/twister.json \ @@ -81,7 +96,7 @@ jobs: --output unit-test-comment.md \ --outcome "$TWISTER_OUTCOME" \ --run-url "$WORKFLOW_RUN_URL" \ - --artifact-url "$ARTIFACTS_URL" + --artifact-url "$ARTIFACT_URL" cat unit-test-comment.md >> "$GITHUB_STEP_SUMMARY" - name: Publish test summary on pull request @@ -121,20 +136,6 @@ jobs: }); } - - name: Upload Twister reports and logs - if: always() - uses: actions/upload-artifact@v6 - with: - name: unit-test-results - path: | - zephyr-workspace/twister-console.log - zephyr-workspace/twister-out/twister*.xml - zephyr-workspace/twister-out/twister.json - zephyr-workspace/twister-out/**/handler.log - zephyr-workspace/twister-out/**/build.log - if-no-files-found: warn - retention-days: 14 - - name: Fail workflow when tests failed if: steps.twister.outcome == 'failure' run: exit 1 From ccf090e07fa418cd280ace50f6ebf42727c33b73 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 13:54:33 +0200 Subject: [PATCH 05/77] refactor(ci): move unit test comment script --- {tools/ci => .github/scripts}/render_unit_test_comment.py | 2 +- .github/workflows/unit_tests.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename {tools/ci => .github/scripts}/render_unit_test_comment.py (99%) diff --git a/tools/ci/render_unit_test_comment.py b/.github/scripts/render_unit_test_comment.py similarity index 99% rename from tools/ci/render_unit_test_comment.py rename to .github/scripts/render_unit_test_comment.py index 3fbb5889..cb2ad8a7 100644 --- a/tools/ci/render_unit_test_comment.py +++ b/.github/scripts/render_unit_test_comment.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Render Twister results as a concise GitHub pull-request comment.""" +"""Render Twister results as a concise GitHub pull-request comment for CI.""" from __future__ import annotations diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml index 3ffd8245..68adca61 100644 --- a/.github/workflows/unit_tests.yaml +++ b/.github/workflows/unit_tests.yaml @@ -90,7 +90,7 @@ jobs: WORKFLOW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} ARTIFACT_URL: ${{ steps.unit_test_artifacts.outputs.artifact-url }} run: | - python3 zephyr-workspace/open-earable-v2/tools/ci/render_unit_test_comment.py \ + python3 zephyr-workspace/open-earable-v2/.github/scripts/render_unit_test_comment.py \ --report zephyr-workspace/twister-out/twister.json \ --console zephyr-workspace/twister-console.log \ --output unit-test-comment.md \ From b6e7cfe512c5601a7f45653ca7c52d5af8449a84 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 14:53:00 +0200 Subject: [PATCH 06/77] test: add ring buffer unit test example --- CONTRIBUTING.md | 3 +- tests/README.md | 129 +++++++++++++++----------- tests/unit/ring_buffer/CMakeLists.txt | 16 ++++ tests/unit/ring_buffer/prj.conf | 2 + tests/unit/ring_buffer/src/main.cpp | 101 ++++++++++++++++++++ tests/unit/ring_buffer/testcase.yaml | 9 ++ 6 files changed, 207 insertions(+), 53 deletions(-) create mode 100644 tests/unit/ring_buffer/CMakeLists.txt create mode 100644 tests/unit/ring_buffer/prj.conf create mode 100644 tests/unit/ring_buffer/src/main.cpp create mode 100644 tests/unit/ring_buffer/testcase.yaml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2009200d..fabdda51 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,8 @@ This repository contains the OpenEarable 2 firmware for the `openearable_v2/nrf5 The main firmware lives at the repository root and is built through Zephyr and the nRF Connect SDK. -Host-based unit tests live under `tests/unit`. See [tests/README.md](tests/README.md) for the TDD workflow, regression-test conventions, and branch-coverage command. +Unit tests live under `tests/unit`. See [tests/README.md](tests/README.md) for the +GitHub Actions workflow, optional local execution, and suite structure. - `src/audio`, `src/bluetooth`, `src/modules`: runtime audio, Bluetooth, and application modules. - `src/SensorManager`, `src/Battery`, `src/SD_Card`, `src/time_sync`: sensor, power, storage, and synchronization subsystems. diff --git a/tests/README.md b/tests/README.md index 325392c5..f0fde713 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,17 +1,44 @@ -# Testing +# Tests -OpenEarable uses the Unity and CMock integration supplied by nRF Connect SDK, -with Zephyr's Twister test runner. This keeps the tests compatible with the SDK -version pinned in `west.yml` and provides one workflow for unit tests, simulated -integration tests, on-target tests, and coverage. +Unit tests live in `tests/unit`. Zephyr's +[Twister test runner](https://docs.nordicsemi.com/bundle/ncs-3.0.1/page/zephyr/develop/test/twister.html) +builds each +[Unity suite](https://github.com/nrfconnect/sdk-nrf/blob/v3.0.1/doc/nrf/test_and_optimize/test_framework/testing_unity_cmock.rst) +as a Linux executable using +[`native_sim/native/64`](https://docs.nordicsemi.com/bundle/ncs-3.0.1/page/zephyr/boards/native/native_sim/doc/index.html), +so the tests run on a development computer or GitHub Actions runner and do not +require OpenEarable hardware. +These links target the nRF Connect SDK version pinned in +[`west.yml`](../west.yml). -## Run the unit tests +## GitHub Actions -Run these commands from the root of the west workspace (the directory that -contains `zephyr`, `nrf`, and this repository): +The [`Unit Tests`](../.github/workflows/unit_tests.yaml) workflow is the primary +way to run the test suite. It runs for every pull request, for pushes to `main`, +and when started manually from GitHub Actions. -Unity's generated runner requires Ruby. On Debian/Ubuntu, install the host -dependency with `sudo apt install ruby`. +For pull requests, the workflow creates or updates a comment with the result. +Twister reports and logs are also available as the `unit-test-results` artifact +for 14 days. + +Twister recursively discovers every `testcase.yaml` below `tests/unit`, so a +valid new suite in that directory is included automatically; the workflow does +not need to be edited. The suite must allow `native_sim/native/64` to run in +this workflow. + +## Run the tests locally (optional) + +Run Twister from the west workspace root: the directory containing `zephyr`, +`nrf`, and this repository. The commands below assume the repository directory +is named `open-earable-v2`; adjust the path if it is named differently. + +Unity's test-runner generation requires Ruby. On Debian or Ubuntu: + +```sh +sudo apt install ruby +``` + +Run all unit-test scenarios: ```sh python3 zephyr/scripts/twister \ @@ -20,20 +47,7 @@ python3 zephyr/scripts/twister \ --inline-logs ``` -Replace `open-earable-v2` if the application directory has a different name. -The `native_sim` target builds a native Linux executable and does not require an -nRF5340 or other target hardware. It is suitable for local TDD. Run it in WSL or -a Linux container on Windows; GitHub Actions provides a Linux environment for -every pull request. - -The dedicated `Unit Tests` GitHub Actions workflow runs the same command for -pull requests and pushes to `main`. Its `Unity tests (native_sim 64-bit)` job is -the check to select when configuring branch protection. The workflow creates or -updates one pull-request comment with the result and Unity failure diagnostics. -Twister XML, JSON, and log files are retained as the `unit-test-results` -artifact for 14 days. - -To run one scenario while developing: +Run a single scenario by its name from `testcase.yaml`: ```sh python3 zephyr/scripts/twister \ @@ -43,10 +57,45 @@ python3 zephyr/scripts/twister \ --inline-logs ``` -## Generate branch coverage +On Windows, run the tests in WSL or a Linux container because `native_sim` +produces a Linux executable. + +## Add a test suite + +The existing suites demonstrate two common patterns: -Install `gcovr` in the active Python environment, then add Twister's coverage -option: +- [`sensor_component`](unit/sensor_component) tests C++ production sources and + binary serialization; +- [`ring_buffer`](unit/ring_buffer) tests a header-only C++ template. + +A suite under `tests/unit//` consists of: + +- `testcase.yaml`, defining a unique scenario name and allowing + `native_sim/native/64`; +- `prj.conf`, enabling `CONFIG_UNITY` and any configuration required by the + production code; +- `CMakeLists.txt`, passing the test source to `test_runner_generate(...)` and + adding the production sources and include directories to the `app` target; +- test source files whose test functions start with `test_` and use Unity's + `TEST_ASSERT_*` macros. + +Production sources are not pulled in automatically. List each source and its +include directories in the suite's `CMakeLists.txt`. + +The host build cannot use nRF hardware. Keep hardware-independent behavior +separate from drivers, or provide test doubles for hardware and Zephyr APIs. +The CI workflow discovers new suites automatically when they are placed below +`tests/unit`. + +For C++ tests, enable `CONFIG_CPP`. Add `CONFIG_REQUIRES_FULL_LIBCPP` only when +the tested code requires the full C++ standard library. Keep the +`test_suiteTearDown` linkage adapter shown in the C++ examples so the generated +Unity runner can call the nRF Connect SDK's C teardown function. + +## Generate coverage + +Install `gcovr` in the active Python environment and run Twister with +[coverage enabled](https://docs.nordicsemi.com/bundle/ncs-3.0.1/page/zephyr/develop/test/coverage.html): ```sh python3 -m pip install gcovr @@ -57,28 +106,4 @@ python3 zephyr/scripts/twister \ --coverage-basedir open-earable-v2 ``` -Open `twister-out/coverage/index.html`. The HTML report includes line and branch -coverage. Coverage is a development aid rather than a repository-wide gate: -new, self-contained classes can target full branch coverage without forcing -hardware-dependent firmware paths into host unit tests. - -## Add a test - -Create a directory under `tests/unit//` with: - -- `CMakeLists.txt` using `test_runner_generate(...)` and adding the test plus - production sources to `app`; -- `prj.conf` with `CONFIG_UNITY=y` (and `CONFIG_CPP=y` plus - `CONFIG_REQUIRES_FULL_LIBCPP=y` for production code using the C++ standard - library); -- `testcase.yaml` allowing `native_sim/native/64`; and -- test functions prefixed with `test_` and Unity's `TEST_ASSERT_*` macros. - -Keep business logic independent from Zephyr drivers where practical. For a -module that calls hardware or Zephyr APIs, generate mocks with CMock's -`cmock_handle(...)`. Ztest remains available for tests that specifically benefit -from Zephyr test fixtures or other RTOS-aware scaffolding. - -Every bug fix should add a test that fails before the fix and passes after it. -Name the test after the observable behavior, and mention the issue or regression -in a short comment when the scenario is not self-explanatory. +Open `twister-out/coverage/index.html` after the run. diff --git a/tests/unit/ring_buffer/CMakeLists.txt b/tests/unit/ring_buffer/CMakeLists.txt new file mode 100644 index 00000000..930e4928 --- /dev/null +++ b/tests/unit/ring_buffer/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(ring_buffer_tests) + +test_runner_generate(src/main.cpp) + +target_include_directories(app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/Wire +) + +target_sources(app PRIVATE + src/main.cpp +) diff --git a/tests/unit/ring_buffer/prj.conf b/tests/unit/ring_buffer/prj.conf new file mode 100644 index 00000000..a9136815 --- /dev/null +++ b/tests/unit/ring_buffer/prj.conf @@ -0,0 +1,2 @@ +CONFIG_UNITY=y +CONFIG_CPP=y diff --git a/tests/unit/ring_buffer/src/main.cpp b/tests/unit/ring_buffer/src/main.cpp new file mode 100644 index 00000000..e55ffabe --- /dev/null +++ b/tests/unit/ring_buffer/src/main.cpp @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT + +#include + +#include "RingBuffer.h" + +using arduino::RingBufferN; + +void setUp(void) +{ +} + +void test_new_buffer_is_empty(void) +{ + RingBufferN<4> buffer; + + TEST_ASSERT_EQUAL_INT(0, buffer.available()); + TEST_ASSERT_EQUAL_INT(4, buffer.availableForStore()); + TEST_ASSERT_EQUAL_INT(-1, buffer.peek()); + TEST_ASSERT_EQUAL_INT(-1, buffer.read_char()); + TEST_ASSERT_FALSE(buffer.isFull()); +} + +void test_reads_values_in_fifo_order_across_wraparound(void) +{ + RingBufferN<4> buffer; + + buffer.store_char(1); + buffer.store_char(2); + buffer.store_char(3); + TEST_ASSERT_EQUAL_INT(1, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(2, buffer.read_char()); + + buffer.store_char(4); + buffer.store_char(5); + + TEST_ASSERT_EQUAL_INT(3, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(4, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(5, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(-1, buffer.read_char()); +} + +void test_full_buffer_rejects_new_values(void) +{ + RingBufferN<4> buffer; + + buffer.store_char(1); + buffer.store_char(2); + buffer.store_char(3); + buffer.store_char(4); + + TEST_ASSERT_TRUE(buffer.isFull()); + TEST_ASSERT_EQUAL_INT(0, buffer.availableForStore()); + + buffer.store_char(5); + + TEST_ASSERT_EQUAL_INT(4, buffer.available()); + TEST_ASSERT_EQUAL_INT(1, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(2, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(3, buffer.read_char()); + TEST_ASSERT_EQUAL_INT(4, buffer.read_char()); +} + +void test_peek_does_not_remove_value(void) +{ + RingBufferN<4> buffer; + + buffer.store_char(42); + + TEST_ASSERT_EQUAL_INT(42, buffer.peek()); + TEST_ASSERT_EQUAL_INT(1, buffer.available()); + TEST_ASSERT_EQUAL_INT(42, buffer.read_char()); +} + +void test_clear_discards_values_and_restores_capacity(void) +{ + RingBufferN<4> buffer; + + buffer.store_char(1); + buffer.store_char(2); + buffer.clear(); + + TEST_ASSERT_EQUAL_INT(0, buffer.available()); + TEST_ASSERT_EQUAL_INT(4, buffer.availableForStore()); + TEST_ASSERT_EQUAL_INT(-1, buffer.read_char()); +} + +extern "C" int unity_c_suite_teardown(int failures) asm("test_suiteTearDown"); + +int test_suiteTearDown(int failures) +{ + return unity_c_suite_teardown(failures); +} + +extern int unity_main(void); + +int main(void) +{ + (void)unity_main(); + return 0; +} diff --git a/tests/unit/ring_buffer/testcase.yaml b/tests/unit/ring_buffer/testcase.yaml new file mode 100644 index 00000000..82d31a07 --- /dev/null +++ b/tests/unit/ring_buffer/testcase.yaml @@ -0,0 +1,9 @@ +tests: + openearable.unit.ring_buffer: + tags: + - unit + - wire + integration_platforms: + - native_sim/native/64 + platform_allow: + - native_sim/native/64 From a3f47253e9199dcf6ed090746b692188e30b4f4d Mon Sep 17 00:00:00 2001 From: JakeSW <54622794+JakeSW@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:27:39 +0000 Subject: [PATCH 07/77] Add BT_UUID_DMIC_GAIN characteristic UUID Define UUID 0x1410df99 for DMIC gain control characteristic. This allows BLE clients to adjust the ADAU1860 digital microphone gain over Bluetooth. --- src/bluetooth/gatt_services/audio_config_service.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/bluetooth/gatt_services/audio_config_service.h b/src/bluetooth/gatt_services/audio_config_service.h index 71b69261..40b696be 100644 --- a/src/bluetooth/gatt_services/audio_config_service.h +++ b/src/bluetooth/gatt_services/audio_config_service.h @@ -33,6 +33,13 @@ #define BT_UUID_AUDIO_CHANNEL \ BT_UUID_DECLARE_128(BT_UUID_AUDIO_CHANNEL_VAL) +// DMIC Gain Characteristic UUID +#define BT_UUID_DMIC_GAIN_VAL \ + BT_UUID_128_ENCODE(0x1410df99, 0x5f68, 0x4ebb, 0xa7c7, 0x5e0fb9ae7557) + +#define BT_UUID_DMIC_GAIN \ + BT_UUID_DECLARE_128(BT_UUID_DMIC_GAIN_VAL) + int init_audio_config_service(void); #endif /* _AUDIO_CONFIG_SERVICE_H_ */ From ea84560b0f42e7b91e9587d03f7dcf829f7dd9df Mon Sep 17 00:00:00 2001 From: JakeSW <54622794+JakeSW@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:27:45 +0000 Subject: [PATCH 08/77] Add DMIC gain control methods to ADAU1860 driver Add public methods mic_gain_write() and mic_gain_read() to safely access DMIC_VOL0 and DMIC_VOL1 registers. These methods encapsulate the private register access functions and allow external code to control microphone gain for channels 0 and 1. --- src/drivers/ADAU1860.cpp | 14 ++++++++++++++ src/drivers/ADAU1860.h | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index b75fa521..528bb213 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -210,6 +210,8 @@ int ADAU1860::begin() { // DMIC_VOL0 uint8_t dmic_vol = 0x20; // 12dB + // 0 - left - external mic + // 1 - right - internal mic writeReg(registers::DMIC_VOL0, &dmic_vol, sizeof(dmic_vol)); writeReg(registers::DMIC_VOL1, &dmic_vol, sizeof(dmic_vol)); @@ -495,6 +497,18 @@ uint8_t ADAU1860::fdsp_get_volume() { return 0xFF-dac_vol; } +void ADAU1860::mic_gain_write(uint8_t channel, uint8_t gain) { + uint32_t reg = (channel == 0) ? registers::DMIC_VOL0 : registers::DMIC_VOL1; + writeReg(reg, &gain, sizeof(gain)); +} + +uint8_t ADAU1860::mic_gain_read(uint8_t channel) { + uint8_t val = 0; + uint32_t reg = (channel == 0) ? registers::DMIC_VOL0 : registers::DMIC_VOL1; + readReg(reg, &val, sizeof(val)); + return val; +} + int ADAU1860::soft_reset(bool full_reset) { int ret = 0; diff --git a/src/drivers/ADAU1860.h b/src/drivers/ADAU1860.h index 63e3fd98..1e766403 100644 --- a/src/drivers/ADAU1860.h +++ b/src/drivers/ADAU1860.h @@ -412,6 +412,10 @@ class ADAU1860 { uint8_t get_volume(); + // DMIC gain control + void mic_gain_write(uint8_t channel, uint8_t gain); + uint8_t mic_gain_read(uint8_t channel); + #if CONFIG_FDSP int fdsp_bank_select(uint8_t bank); #endif From 0c9baa8f43ce633a56c909c001246720ad23a01b Mon Sep 17 00:00:00 2001 From: JakeSW <54622794+JakeSW@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:27:50 +0000 Subject: [PATCH 09/77] Implement DMIC gain control in hw_codec layer Implement hw_codec_mic_gain_set(), hw_codec_mic_gain_get_left(), and hw_codec_mic_gain_get_right() functions. These provide a hardware abstraction layer for controlling ADAU186x DMIC_VOL registers with 0.375 dB step resolution across a +24 dB to mute range. --- src/modules/hw_codec.h | 14 ++++++++++++++ src/modules/hw_codec_adau1860.cpp | 28 ++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/modules/hw_codec.h b/src/modules/hw_codec.h index c0347432..e5d92ca6 100644 --- a/src/modules/hw_codec.h +++ b/src/modules/hw_codec.h @@ -119,6 +119,20 @@ int hw_codec_set_audio_mode(enum audio_mode mode); enum audio_mode hw_codec_get_audio_mode(); +/* Microphone (DMIC) gain control (ADAU186x DMIC_VOL registers). + * Register mapping (per ADAU186x datasheet DMIC_VOL0 @ 0x4000C045): + * 0x00 = +24 dB + * 0x01-0x3F = +23.625 to +0.375 dB (decrement by 0.375 dB per step) + * 0x40 = 0 dB (reset value) + * 0x41-0xFD = -0.375 to -70.875 dB (decrement by 0.375 dB per step) + * 0xFE = -71.25 dB + * 0xFF = Mute + * Recommended BLE range: 0x00-0x40 for +24 dB to 0 dB. + */ +int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg); +uint8_t hw_codec_mic_gain_get_left(void); +uint8_t hw_codec_mic_gain_get_right(void); + #ifdef __cplusplus } #endif diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index fd06b2d7..9eb57177 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -308,6 +308,34 @@ int hw_codec_stop_audio(void) return 0; } +/* Microphone (DMIC) gain control using ADAU186x DMIC_VOL0/1 registers. + * Per ADAU186x datasheet register DMIC_VOL0 (addr 0x4000C045): + * 0x00 = +24 dB + * 0x01-0x3F = +23.625 to +0.375 dB (decrement by 0.375 dB per step) + * 0x40 = 0 dB (reset value) + * 0x41-0xFD = -0.375 to -70.875 dB (decrement by 0.375 dB per step) + * 0xFE = -71.25 dB + * 0xFF = Mute + */ +int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg) +{ + /* Write DMIC gain for channels 0 and 1 */ + dac.mic_gain_write(0, gain_left_reg); + dac.mic_gain_write(1, gain_right_reg); + LOG_INF("DMIC gain set: L=0x%02x, R=0x%02x", gain_left_reg, gain_right_reg); + return 0; +} + +uint8_t hw_codec_mic_gain_get_left(void) +{ + return dac.mic_gain_read(0); +} + +uint8_t hw_codec_mic_gain_get_right(void) +{ + return dac.mic_gain_read(1); +} + int hw_codec_soft_reset(void) { int ret; From 849630554ce9caf7f788ee7255d9c70ecfb970ae Mon Sep 17 00:00:00 2001 From: JakeSW <54622794+JakeSW@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:27:55 +0000 Subject: [PATCH 10/77] Add DMIC gain BLE characteristic to audio config service Add read_dmic_gain() and write_dmic_gain() handlers for the BT_UUID_DMIC_GAIN characteristic. This allows BLE clients to read and adjust DMIC gain for left and right channels using a 2-byte payload [left_reg, right_reg]. --- .../gatt_services/audio_config_service.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index d0a8513a..1575b9b4 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -72,6 +72,45 @@ static ssize_t read_audio_channel(struct bt_conn *conn, const struct bt_gatt_att return bt_gatt_attr_read(conn, attr, buf, len, offset, &channel_u8, sizeof(channel)); } +static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + // Mic gain is 2 bytes: [left_reg, right_reg]. + // Per ADAU186x DMIC_VOL register (0x4000C045): + // 0x00 = +24 dB + // 0x01-0x3F = +23.625 to +0.375 dB (0.375 dB steps) + // 0x40 = 0 dB + // 0x41-0xFD = -0.375 to -70.875 dB (0.375 dB steps) + // 0xFE = -71.25 dB + // 0xFF = Mute + if (len != 2) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + uint8_t gain_left = ((uint8_t*)buf)[0]; + uint8_t gain_right = ((uint8_t*)buf)[1]; + + int ret = hw_codec_mic_gain_set(gain_left, gain_right); + if (ret) { + LOG_ERR("Failed to set mic gain: %d", ret); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + LOG_INF("DMIC gain via BLE: L=0x%02x R=0x%02x", gain_left, gain_right); + return len; +} + +static ssize_t read_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + uint8_t gains[2] = { + hw_codec_mic_gain_get_left(), + hw_codec_mic_gain_get_right() + }; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, gains, sizeof(gains)); +} + BT_GATT_SERVICE_DEFINE(audio_config_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_AUDIO_CONFIG_SERVICE), BT_GATT_CHARACTERISTIC(BT_UUID_AUDIO_MODE, @@ -86,6 +125,10 @@ BT_GATT_SERVICE_DEFINE(audio_config_svc, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_audio_channel, NULL, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_DMIC_GAIN, + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ, + BT_GATT_PERM_WRITE | BT_GATT_PERM_READ, + read_dmic_gain, write_dmic_gain, NULL), ); int init_audio_config_service(void) From 2e0a1c0c3e4dccdf0ca6ab1f0ed64b73dc362c7b Mon Sep 17 00:00:00 2001 From: JakeSW <54622794+JakeSW@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:28:00 +0000 Subject: [PATCH 11/77] Add DMIC gain control reference documentation Add comprehensive documentation for DMIC microphone gain control including: - Hardware register mapping and gain calculations - BLE protocol specification - Usage examples - C/C++ API reference - Implementation details Replaces deprecated PDM terminology with DMIC to accurately reflect the ADAU1860 digital microphone interface. --- doc/dmic_gain_reference.rst | 179 ++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 doc/dmic_gain_reference.rst diff --git a/doc/dmic_gain_reference.rst b/doc/dmic_gain_reference.rst new file mode 100644 index 00000000..015416f8 --- /dev/null +++ b/doc/dmic_gain_reference.rst @@ -0,0 +1,179 @@ +.. _dmic_gain_reference: + +DMIC Microphone Gain Control Reference +####################################### + +.. contents:: + :local: + :depth: 2 + +Overview +******** + +The DMIC (Digital Microphone) gain on the ADAU1860 codec can now be adjusted over Bluetooth using the Audio Config Service characteristic ``BT_UUID_DMIC_GAIN`` (UUID: ``1410df99-5f68-4ebb-a7c7-5e0fb9ae7557``). + +Hardware Register Mapping +************************** + +Based on ADAU186x datasheet (register DMIC_VOL0 @ address 0x4000C045): + +.. list-table:: + :header-rows: 1 + :widths: 20 20 60 + + * - Register Value + - Gain (dB) + - Description + * - 0x00 + - +24 + - Maximum positive gain + * - 0x01 + - +23.625 + - + * - 0x02 + - +23.25 + - + * - ... + - ... + - Each step = -0.375 dB + * - 0x3F + - +0.375 + - + * - **0x40** + - **0** + - **Reset/Default value** + * - 0x41 + - -0.375 + - + * - ... + - ... + - Each step = -0.375 dB + * - 0xFD + - -70.875 + - + * - 0xFE + - -71.25 + - + * - 0xFF + - ∞ + - Mute (infinite attenuation) + +Gain Calculation +***************** + +For any register value ``reg`` (0x00–0x3F for positive gains): + +- **Gain (dB) = 24 - (reg × 0.375)** + +For negative gains (0x41–0xFD): + +- **Gain (dB) = -(reg - 0x40) × 0.375** + +Examples +======== + +- ``0x00`` → 24 - (0 × 0.375) = **+24 dB** +- ``0x10`` → 24 - (16 × 0.375) = **+18 dB** +- ``0x20`` → 24 - (32 × 0.375) = **+12 dB** (initial value in this firmware) +- ``0x40`` → **0 dB** +- ``0x50`` → -(80 - 64) × 0.375 = **-6 dB** +- ``0x60`` → -(96 - 64) × 0.375 = **-12 dB** + +BLE Protocol +************ + +Characteristic Details +====================== + +- **UUID**: ``0x1410df99-5f68-4ebb-a7c7-5e0fb9ae7557`` (under Audio Config Service) +- **Properties**: Read, Write +- **Payload**: 2 bytes + +Write Command +============= + +Send 2 bytes ``[left_gain_reg, right_gain_reg]``: + +.. code-block:: text + + Byte 0: Left DMIC channel gain register (0x00–0xFF) + Byte 1: Right DMIC channel gain register (0x00–0xFF) + +Read Response +============= + +Returns 2 bytes showing current DMIC gain for each channel. + +Usage Examples +************** + +Set both channels to +12 dB +============================ + +Send: ``[0x20, 0x20]`` + +Set left to +18 dB, right to +6 dB +=================================== + +Send: ``[0x10, 0x30]`` + +(0x10 = 24 - 16×0.375 = +18 dB, 0x30 = 24 - 48×0.375 = +6 dB) + +Set to 0 dB (neutral/reset) +============================ + +Send: ``[0x40, 0x40]`` + +Mute both channels +================== + +Send: ``[0xFF, 0xFF]`` + +Set left to -12 dB (96 = 0x60) +=============================== + +Send: ``[0x60, 0x40]`` + +API Reference (C/C++) +********************* + +Functions +========= + +.. code-block:: c + + // Set DMIC gain for both channels + // gain_left_reg, gain_right_reg: register values (0x00–0xFF) + int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg); + + // Get current DMIC gain register value for left/right channel + uint8_t hw_codec_mic_gain_get_left(void); + uint8_t hw_codec_mic_gain_get_right(void); + +Example C Code +============== + +.. code-block:: cpp + + // Set left and right DMIC to +12 dB + hw_codec_mic_gain_set(0x20, 0x20); + + // Read back the values + uint8_t left = hw_codec_mic_gain_get_left(); // Should return 0x20 + uint8_t right = hw_codec_mic_gain_get_right(); // Should return 0x20 + +Implementation Details +********************** + +- **File**: ``src/modules/hw_codec_adau1860.cpp`` +- **Registers Written**: ``DMIC_VOL0`` (0x4000C045), ``DMIC_VOL1`` (0x4000C046) +- **BLE Handler**: ``src/bluetooth/gatt_services/audio_config_service.c`` +- **Logging**: INFO level logs show when gain is changed via BLE + +Notes +***** + +- Each register value change takes effect immediately on the hardware. +- Default at startup: 0x20 (+12 dB) for both channels (set in ``ADAU1860::begin()``). +- The characteristic is read/write; clients can query current gain and set new values. +- The 0.375 dB step provides fine-grained control with 256 possible values. From 9e531f8a8a5693059fc26f554e3c13c10f1d1a2a Mon Sep 17 00:00:00 2001 From: habibialireza Date: Mon, 15 Jun 2026 00:19:27 +0200 Subject: [PATCH 12/77] Microphone gain fixed and added to app --- src/modules/hw_codec_adau1860.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 9eb57177..094a942d 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -320,8 +320,8 @@ int hw_codec_stop_audio(void) int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg) { /* Write DMIC gain for channels 0 and 1 */ - dac.mic_gain_write(0, gain_left_reg); - dac.mic_gain_write(1, gain_right_reg); + dac.mic_gain_write(0, gain_right_reg); + dac.mic_gain_write(1, gain_left_reg); LOG_INF("DMIC gain set: L=0x%02x, R=0x%02x", gain_left_reg, gain_right_reg); return 0; } From ec12066c425ed63f42b0401cd7fa2d8042a88ad3 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Mon, 27 Jul 2026 16:18:24 +0200 Subject: [PATCH 13/77] fix: mic gain getters were reading wrong channels --- src/modules/hw_codec_adau1860.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 094a942d..11a414b2 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -326,16 +326,17 @@ int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg) return 0; } -uint8_t hw_codec_mic_gain_get_left(void) +uint8_t hw_codec_mic_gain_get_right(void) { return dac.mic_gain_read(0); } -uint8_t hw_codec_mic_gain_get_right(void) +uint8_t hw_codec_mic_gain_get_left(void) { return dac.mic_gain_read(1); } + int hw_codec_soft_reset(void) { int ret; From ae4d647ee42508984e0a2ab404b2e985955d2186 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Mon, 27 Jul 2026 16:46:55 +0200 Subject: [PATCH 14/77] fix(audio): rename DMIC gain channels to internal and external mics --- doc/dmic_gain_reference.rst | 47 ++++++++++--------- .../gatt_services/audio_config_service.c | 16 ++++--- src/drivers/ADAU1860.cpp | 7 ++- src/modules/hw_codec.h | 8 ++-- src/modules/hw_codec_adau1860.cpp | 17 +++---- 5 files changed, 51 insertions(+), 44 deletions(-) diff --git a/doc/dmic_gain_reference.rst b/doc/dmic_gain_reference.rst index 015416f8..8e6bd763 100644 --- a/doc/dmic_gain_reference.rst +++ b/doc/dmic_gain_reference.rst @@ -17,6 +17,9 @@ Hardware Register Mapping Based on ADAU186x datasheet (register DMIC_VOL0 @ address 0x4000C045): +- ``DMIC_VOL0`` / DMIC channel 0 controls the **external** microphone. +- ``DMIC_VOL1`` / DMIC channel 1 controls the **internal** microphone. + .. list-table:: :header-rows: 1 :widths: 20 20 60 @@ -92,28 +95,28 @@ Characteristic Details Write Command ============= -Send 2 bytes ``[left_gain_reg, right_gain_reg]``: +Send 2 bytes ``[external_gain_reg, internal_gain_reg]``: .. code-block:: text - Byte 0: Left DMIC channel gain register (0x00–0xFF) - Byte 1: Right DMIC channel gain register (0x00–0xFF) + Byte 0: External microphone gain register (DMIC_VOL0, 0x00–0xFF) + Byte 1: Internal microphone gain register (DMIC_VOL1, 0x00–0xFF) Read Response ============= -Returns 2 bytes showing current DMIC gain for each channel. +Returns 2 bytes showing current DMIC gain as ``[external_gain_reg, internal_gain_reg]``. Usage Examples ************** -Set both channels to +12 dB -============================ +Set both microphones to +12 dB +=============================== Send: ``[0x20, 0x20]`` -Set left to +18 dB, right to +6 dB -=================================== +Set external to +18 dB, internal to +6 dB +========================================= Send: ``[0x10, 0x30]`` @@ -124,13 +127,13 @@ Set to 0 dB (neutral/reset) Send: ``[0x40, 0x40]`` -Mute both channels -================== +Mute both microphones +===================== Send: ``[0xFF, 0xFF]`` -Set left to -12 dB (96 = 0x60) -=============================== +Set external to -12 dB (96 = 0x60), internal to 0 dB +===================================================== Send: ``[0x60, 0x40]`` @@ -142,31 +145,31 @@ Functions .. code-block:: c - // Set DMIC gain for both channels - // gain_left_reg, gain_right_reg: register values (0x00–0xFF) - int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg); + // Set DMIC gain for both microphones + // gain_external_reg, gain_internal_reg: register values (0x00–0xFF) + int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg); - // Get current DMIC gain register value for left/right channel - uint8_t hw_codec_mic_gain_get_left(void); - uint8_t hw_codec_mic_gain_get_right(void); + // Get current DMIC gain register value for external/internal microphone + uint8_t hw_codec_mic_gain_get_external(void); + uint8_t hw_codec_mic_gain_get_internal(void); Example C Code ============== .. code-block:: cpp - // Set left and right DMIC to +12 dB + // Set external and internal DMIC to +12 dB hw_codec_mic_gain_set(0x20, 0x20); // Read back the values - uint8_t left = hw_codec_mic_gain_get_left(); // Should return 0x20 - uint8_t right = hw_codec_mic_gain_get_right(); // Should return 0x20 + uint8_t external = hw_codec_mic_gain_get_external(); // Should return 0x20 + uint8_t internal = hw_codec_mic_gain_get_internal(); // Should return 0x20 Implementation Details ********************** - **File**: ``src/modules/hw_codec_adau1860.cpp`` -- **Registers Written**: ``DMIC_VOL0`` (0x4000C045), ``DMIC_VOL1`` (0x4000C046) +- **Registers Written**: external mic ``DMIC_VOL0`` (0x4000C045), internal mic ``DMIC_VOL1`` (0x4000C046) - **BLE Handler**: ``src/bluetooth/gatt_services/audio_config_service.c`` - **Logging**: INFO level logs show when gain is changed via BLE diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index 1575b9b4..489b240e 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -75,7 +75,8 @@ static ssize_t read_audio_channel(struct bt_conn *conn, const struct bt_gatt_att static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { - // Mic gain is 2 bytes: [left_reg, right_reg]. + // Mic gain is 2 bytes: [external_reg, internal_reg]. + // External mic maps to DMIC_VOL0; internal mic maps to DMIC_VOL1. // Per ADAU186x DMIC_VOL register (0x4000C045): // 0x00 = +24 dB // 0x01-0x3F = +23.625 to +0.375 dB (0.375 dB steps) @@ -87,16 +88,17 @@ static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr * return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } - uint8_t gain_left = ((uint8_t*)buf)[0]; - uint8_t gain_right = ((uint8_t*)buf)[1]; + uint8_t gain_external = ((uint8_t*)buf)[0]; + uint8_t gain_internal = ((uint8_t*)buf)[1]; - int ret = hw_codec_mic_gain_set(gain_left, gain_right); + int ret = hw_codec_mic_gain_set(gain_external, gain_internal); if (ret) { LOG_ERR("Failed to set mic gain: %d", ret); return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); } - LOG_INF("DMIC gain via BLE: L=0x%02x R=0x%02x", gain_left, gain_right); + LOG_INF("DMIC gain via BLE: external=0x%02x internal=0x%02x", + gain_external, gain_internal); return len; } @@ -104,8 +106,8 @@ static ssize_t read_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *a void *buf, uint16_t len, uint16_t offset) { uint8_t gains[2] = { - hw_codec_mic_gain_get_left(), - hw_codec_mic_gain_get_right() + hw_codec_mic_gain_get_external(), + hw_codec_mic_gain_get_internal() }; return bt_gatt_attr_read(conn, attr, buf, len, offset, gains, sizeof(gains)); diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index 528bb213..61530c37 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -208,10 +208,9 @@ int ADAU1860::begin() { uint8_t spt0_route1 = 33; // ASCRO 1 writeReg(registers::SPT0_ROUTE1, &spt0_route1, sizeof(spt0_route1)); - // DMIC_VOL0 uint8_t dmic_vol = 0x20; // 12dB - // 0 - left - external mic - // 1 - right - internal mic + // DMIC channel 0 - external mic + // DMIC channel 1 - internal mic writeReg(registers::DMIC_VOL0, &dmic_vol, sizeof(dmic_vol)); writeReg(registers::DMIC_VOL1, &dmic_vol, sizeof(dmic_vol)); @@ -612,4 +611,4 @@ SHELL_STATIC_SUBCMD_SET_CREATE(dsp_cmd, SHELL_SUBCMD_SET_END); SHELL_CMD_REGISTER(dsp, &dsp_cmd, "Set DSP parameters", NULL); -#endif \ No newline at end of file +#endif diff --git a/src/modules/hw_codec.h b/src/modules/hw_codec.h index e5d92ca6..a02173c9 100644 --- a/src/modules/hw_codec.h +++ b/src/modules/hw_codec.h @@ -120,6 +120,8 @@ int hw_codec_set_audio_mode(enum audio_mode mode); enum audio_mode hw_codec_get_audio_mode(); /* Microphone (DMIC) gain control (ADAU186x DMIC_VOL registers). + * DMIC_VOL0 controls the external microphone. + * DMIC_VOL1 controls the internal microphone. * Register mapping (per ADAU186x datasheet DMIC_VOL0 @ 0x4000C045): * 0x00 = +24 dB * 0x01-0x3F = +23.625 to +0.375 dB (decrement by 0.375 dB per step) @@ -129,9 +131,9 @@ enum audio_mode hw_codec_get_audio_mode(); * 0xFF = Mute * Recommended BLE range: 0x00-0x40 for +24 dB to 0 dB. */ -int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg); -uint8_t hw_codec_mic_gain_get_left(void); -uint8_t hw_codec_mic_gain_get_right(void); +int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg); +uint8_t hw_codec_mic_gain_get_external(void); +uint8_t hw_codec_mic_gain_get_internal(void); #ifdef __cplusplus } diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 11a414b2..6f89a852 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -317,21 +317,22 @@ int hw_codec_stop_audio(void) * 0xFE = -71.25 dB * 0xFF = Mute */ -int hw_codec_mic_gain_set(uint8_t gain_left_reg, uint8_t gain_right_reg) +int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg) { - /* Write DMIC gain for channels 0 and 1 */ - dac.mic_gain_write(0, gain_right_reg); - dac.mic_gain_write(1, gain_left_reg); - LOG_INF("DMIC gain set: L=0x%02x, R=0x%02x", gain_left_reg, gain_right_reg); + /* DMIC channel 0 is the external mic, channel 1 is the internal mic. */ + dac.mic_gain_write(0, gain_external_reg); + dac.mic_gain_write(1, gain_internal_reg); + LOG_INF("DMIC gain set: external=0x%02x, internal=0x%02x", + gain_external_reg, gain_internal_reg); return 0; } -uint8_t hw_codec_mic_gain_get_right(void) +uint8_t hw_codec_mic_gain_get_external(void) { return dac.mic_gain_read(0); } -uint8_t hw_codec_mic_gain_get_left(void) +uint8_t hw_codec_mic_gain_get_internal(void) { return dac.mic_gain_read(1); } @@ -374,4 +375,4 @@ int hw_codec_init(void) ERR_CHK(ret); return 0; -} \ No newline at end of file +} From 8e23702340ef6c8a22c088da059da10cb7379e59 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 28 Jul 2026 09:57:50 +0200 Subject: [PATCH 15/77] fix(audio): map DMIC gain channels and handle write failures --- doc/dmic_gain_reference.rst | 9 ++++++++- src/drivers/ADAU1860.cpp | 8 +++++--- src/drivers/ADAU1860.h | 6 +++--- src/modules/hw_codec.h | 8 ++++---- src/modules/hw_codec_adau1860.cpp | 26 +++++++++++++++++++++----- 5 files changed, 41 insertions(+), 16 deletions(-) diff --git a/doc/dmic_gain_reference.rst b/doc/dmic_gain_reference.rst index 8e6bd763..627973a0 100644 --- a/doc/dmic_gain_reference.rst +++ b/doc/dmic_gain_reference.rst @@ -15,11 +15,15 @@ The DMIC (Digital Microphone) gain on the ADAU1860 codec can now be adjusted ove Hardware Register Mapping ************************** -Based on ADAU186x datasheet (register DMIC_VOL0 @ address 0x4000C045): +The ADAU186x names the digital microphone inputs by channel number. This +firmware keeps those chip-level names at the driver boundary and maps them to +board-specific microphone positions in ``hw_codec_adau1860.cpp``: - ``DMIC_VOL0`` / DMIC channel 0 controls the **external** microphone. - ``DMIC_VOL1`` / DMIC channel 1 controls the **internal** microphone. +Based on the ADAU186x datasheet (register DMIC_VOL0 @ address 0x4000C045): + .. list-table:: :header-rows: 1 :widths: 20 20 60 @@ -180,3 +184,6 @@ Notes - Default at startup: 0x20 (+12 dB) for both channels (set in ``ADAU1860::begin()``). - The characteristic is read/write; clients can query current gain and set new values. - The 0.375 dB step provides fine-grained control with 256 possible values. +- To verify the physical mapping experimentally, set one microphone to mute + (0xFF) and the other to 0 dB (0x40), then speak into the external and + internal microphones separately. diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index 61530c37..fc16a923 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -496,9 +496,9 @@ uint8_t ADAU1860::fdsp_get_volume() { return 0xFF-dac_vol; } -void ADAU1860::mic_gain_write(uint8_t channel, uint8_t gain) { +int ADAU1860::mic_gain_write(uint8_t channel, uint8_t gain) { uint32_t reg = (channel == 0) ? registers::DMIC_VOL0 : registers::DMIC_VOL1; - writeReg(reg, &gain, sizeof(gain)); + return writeReg(reg, &gain, sizeof(gain)); } uint8_t ADAU1860::mic_gain_read(uint8_t channel) { @@ -550,7 +550,7 @@ bool ADAU1860::readReg(uint32_t reg, uint8_t * buffer, uint16_t len) { } -void ADAU1860::writeReg(uint32_t reg, uint8_t *buffer, uint16_t len) { +int ADAU1860::writeReg(uint32_t reg, uint8_t *buffer, uint16_t len) { int ret; struct i2c_msg msg[2]; @@ -577,6 +577,8 @@ void ADAU1860::writeReg(uint32_t reg, uint8_t *buffer, uint16_t len) { } _i2c->release(); + + return ret; } #ifdef NOISE_GATE_ACTIVE diff --git a/src/drivers/ADAU1860.h b/src/drivers/ADAU1860.h index 1e766403..9614e704 100644 --- a/src/drivers/ADAU1860.h +++ b/src/drivers/ADAU1860.h @@ -413,7 +413,7 @@ class ADAU1860 { uint8_t get_volume(); // DMIC gain control - void mic_gain_write(uint8_t channel, uint8_t gain); + int mic_gain_write(uint8_t channel, uint8_t gain); uint8_t mic_gain_read(uint8_t channel); #if CONFIG_FDSP @@ -421,7 +421,7 @@ class ADAU1860 { #endif private: bool readReg(uint32_t reg, uint8_t * buffer, uint16_t len); - void writeReg(uint32_t reg, uint8_t * buffer, uint16_t len); + int writeReg(uint32_t reg, uint8_t * buffer, uint16_t len); int setup_EQ(); int setup_FDSP(); @@ -457,4 +457,4 @@ class ADAU1860 { extern ADAU1860 dac; -#endif \ No newline at end of file +#endif diff --git a/src/modules/hw_codec.h b/src/modules/hw_codec.h index a02173c9..9e726ed8 100644 --- a/src/modules/hw_codec.h +++ b/src/modules/hw_codec.h @@ -119,9 +119,10 @@ int hw_codec_set_audio_mode(enum audio_mode mode); enum audio_mode hw_codec_get_audio_mode(); -/* Microphone (DMIC) gain control (ADAU186x DMIC_VOL registers). - * DMIC_VOL0 controls the external microphone. - * DMIC_VOL1 controls the internal microphone. +/* Microphone (DMIC) gain control. + * Board mapping: + * ADAU186x DMIC_VOL0 / DMIC channel 0 controls the external microphone. + * ADAU186x DMIC_VOL1 / DMIC channel 1 controls the internal microphone. * Register mapping (per ADAU186x datasheet DMIC_VOL0 @ 0x4000C045): * 0x00 = +24 dB * 0x01-0x3F = +23.625 to +0.375 dB (decrement by 0.375 dB per step) @@ -129,7 +130,6 @@ enum audio_mode hw_codec_get_audio_mode(); * 0x41-0xFD = -0.375 to -70.875 dB (decrement by 0.375 dB per step) * 0xFE = -71.25 dB * 0xFF = Mute - * Recommended BLE range: 0x00-0x40 for +24 dB to 0 dB. */ int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg); uint8_t hw_codec_mic_gain_get_external(void); diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 6f89a852..a370ae2e 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -38,6 +38,11 @@ K_THREAD_STACK_DEFINE(volume_msg_sub_thread_stack, CONFIG_VOLUME_MSG_SUB_STACK_S static enum audio_mode audio_mode; +enum hw_codec_mic { + HW_CODEC_MIC_EXTERNAL = 0, + HW_CODEC_MIC_INTERNAL = 1, +}; + static int settings_set_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { if (strcmp(name, "mode") == 0 && len == sizeof(audio_mode)) { @@ -319,9 +324,20 @@ int hw_codec_stop_audio(void) */ int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg) { - /* DMIC channel 0 is the external mic, channel 1 is the internal mic. */ - dac.mic_gain_write(0, gain_external_reg); - dac.mic_gain_write(1, gain_internal_reg); + int ret; + + ret = dac.mic_gain_write(HW_CODEC_MIC_EXTERNAL, gain_external_reg); + if (ret) { + LOG_ERR("Failed to set external mic gain: %d", ret); + return ret; + } + + ret = dac.mic_gain_write(HW_CODEC_MIC_INTERNAL, gain_internal_reg); + if (ret) { + LOG_ERR("Failed to set internal mic gain: %d", ret); + return ret; + } + LOG_INF("DMIC gain set: external=0x%02x, internal=0x%02x", gain_external_reg, gain_internal_reg); return 0; @@ -329,12 +345,12 @@ int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg) uint8_t hw_codec_mic_gain_get_external(void) { - return dac.mic_gain_read(0); + return dac.mic_gain_read(HW_CODEC_MIC_EXTERNAL); } uint8_t hw_codec_mic_gain_get_internal(void) { - return dac.mic_gain_read(1); + return dac.mic_gain_read(HW_CODEC_MIC_INTERNAL); } From 2df6aeaf79aa4f8326269203240872ed84f8e15a Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 28 Jul 2026 11:05:27 +0200 Subject: [PATCH 16/77] refactor(audio): rename codec config to ADAU1860 --- prj.conf | 3 +-- prj_fota.conf | 3 +-- src/drivers/CMakeLists.txt | 4 ++-- src/drivers/Kconfig | 32 +++++++++++++++++++++++++++++--- src/modules/CMakeLists.txt | 2 ++ 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/prj.conf b/prj.conf index 5741e028..ad23effc 100644 --- a/prj.conf +++ b/prj.conf @@ -72,8 +72,7 @@ CONFIG_SD_LOG_LEVEL_OFF=y # Suppress LOG_INF messages from hci_core CONFIG_BT_HCI_CORE_LOG_LEVEL_WRN=y -# Michael: missing somehow -CONFIG_NRF5340_AUDIO_CS47L63_DRIVER=y +CONFIG_NRF5340_AUDIO_ADAU1860_DRIVER=y CONFIG_NRF5340_AUDIO_POWER_MEASUREMENT=n CONFIG_NRF5340_AUDIO_SD_CARD_MODULE=y diff --git a/prj_fota.conf b/prj_fota.conf index 0aee2b1b..6356aeb2 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -85,8 +85,7 @@ CONFIG_FLASH_MAP=y CONFIG_SPI_NOR=y CONFIG_SPI_NOR_SFDP_DEVICETREE=y -# Michael: missing somehow -CONFIG_NRF5340_AUDIO_CS47L63_DRIVER=y +CONFIG_NRF5340_AUDIO_ADAU1860_DRIVER=y CONFIG_NRF5340_AUDIO_POWER_MEASUREMENT=n CONFIG_NRF5340_AUDIO_SD_CARD_MODULE=y diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index d1f11d41..37d0f0dc 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -4,7 +4,7 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # -target_sources_ifdef(CONFIG_NRF5340_AUDIO_CS47L63_DRIVER +target_sources_ifdef(CONFIG_NRF5340_AUDIO_ADAU1860_DRIVER app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ADAU1860.cpp ) @@ -12,4 +12,4 @@ add_subdirectory(LED_Controller) zephyr_library_include_directories( LED_Controller -) \ No newline at end of file +) diff --git a/src/drivers/Kconfig b/src/drivers/Kconfig index 97d6acf4..327b92d6 100644 --- a/src/drivers/Kconfig +++ b/src/drivers/Kconfig @@ -8,6 +8,7 @@ menu "Drivers" menuconfig NRF5340_AUDIO_CS47L63_DRIVER bool "CS47L63 HW codec driver" + depends on BOARD_NRF5340_AUDIO_DK_NRF5340_CPUAPP select HW_CODEC_CIRRUS_LOGIC help Include the driver for the Cirrus Logic CS47L63 hardware codec chip @@ -24,6 +25,31 @@ config CS47L63_STACK_SIZE int "Stack size for CS47L63" default 700 +module = CS47L63 +module-str = cs47l63 +source "subsys/logging/Kconfig.template.log_config" + +endif # NRF5340_AUDIO_CS47L63_DRIVER + +menuconfig NRF5340_AUDIO_ADAU1860_DRIVER + bool "ADAU1860 HW codec driver" + depends on BOARD_OPENEARABLE_V2_NRF5340_CPUAPP || BOARD_OPENEARABLE_V2_NRF5340_CPUAPP_NS + default y if BOARD_OPENEARABLE_V2_NRF5340_CPUAPP || BOARD_OPENEARABLE_V2_NRF5340_CPUAPP_NS + help + Include the driver for the Analog Devices ADAU1860 hardware codec chip + +if NRF5340_AUDIO_ADAU1860_DRIVER + +config ADAU1860_THREAD_PRIO + int "Priority for ADAU1860 thread" + default 5 + help + This is a preemptible thread + +config ADAU1860_STACK_SIZE + int "Stack size for ADAU1860" + default 700 + choice EQUALIZER prompt "Choose EQ on DSP or MCU ISO transport" default EQAULIZER_DSP @@ -41,9 +67,9 @@ config FDSP default y bool "Use FDSP the fast DSP core. This is required for Transparancy mode and ANC." -module = CS47L63 -module-str = cs47l63 +module = ADAU1860 +module-str = adau1860 source "subsys/logging/Kconfig.template.log_config" -endif # NRF5340_AUDIO_CS47L63_DRIVER +endif # NRF5340_AUDIO_ADAU1860_DRIVER endmenu # Drivers diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 6780217b..45cf0745 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -9,6 +9,8 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/audio_i2s.c) target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/audio_sync_timer.c) target_sources_ifdef(CONFIG_NRF5340_AUDIO_CS47L63_DRIVER app PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/hw_codec.c) +target_sources_ifdef(CONFIG_NRF5340_AUDIO_ADAU1860_DRIVER app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/hw_codec_adau1860.cpp) target_sources_ifdef(CONFIG_NRF5340_AUDIO_POWER_MEASUREMENT app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/power_meas.c) From 3d78cc338e03e707bd154706e90b91ea6ccdbbdf Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 28 Jul 2026 12:43:34 +0200 Subject: [PATCH 17/77] chore: stop tracking macOS metadata --- src/SensorManager/.DS_Store | Bin 6148 -> 0 bytes tools/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/SensorManager/.DS_Store delete mode 100644 tools/.DS_Store diff --git a/src/SensorManager/.DS_Store b/src/SensorManager/.DS_Store deleted file mode 100644 index dd6b9a1744b2f27a10c6902660cb06572128cc8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKF-yZh6n368uvYbDgZdx2+NC@3WCO|<)XwrtB6X^ z(SX|E)a#r?!%>bc9pZpE@YfumZ+9MA(1s>#LiYQu9A9L|hfxsp22l_Dotyji`{(sT zGtcRN%Nd=e)``j!PQf}3p^iEl);dxezh%eMI?k65Y#ltuFq-I?#<6siZYMfI@L(&u zo;~j*I(R&-<8D2pBjHSROygKO;DdB5=wBto+(0Fy#dMg|lQ(vN#|P zhy#fOv_81l2z|!bpk6vK=_3GQ8M}4hHJv4>#%1&wV}m$?p-d^FDOG&LP^KLFUFP|W zu|ZP~iVq)(pRD+VqV&^oepk~$`35D61LA<~z?`|Q(*D0Y{rqni$(=YL4*V+zR5|Db zEqo>3TXQc*d##JDi;aSKu|ZveiML~YL0j<}HXS(T(gmW=7#qYM4EZCVZIC1m{HgS5T30)q^Qt~;BoQpp>GgNJb4j%E^17PVq${Wd)`1F!}k#ML3{$=z;AYj zn$RD5k|HxO`)y`tb|+s#CPPGSJkLf%Ln4ZxjJ*Md6~gPROVU#dFS@WtMm0TAMR}1g zd7EJu8Q^C(pp@oxPX)D}-)M4Mm9?ZieOv8SR!Nc;Rf^T<;{EIB;pO9OooBYCXMNW` z`IQqmM0PiLub$}&I>W3)wvVtCMc(#4_}uK>&UgBKdXh`N8}=`&-@Lbr=;nXP`I{I6 z#(*)fYYd=fvqbxXHW~xQfHAOOfWHqO$`~s)g5lGFA+!L%KFmQd=U#$iyke}_2x0}| zBorv2PFoBo;jnx4ixnF|2`8t`htrdtb|@~Kj{SXbCl?FaXbczwT?Y2#a>(`ne0~4l z4YDg^z!>;f47h$WNyfM(t*uKp$F Date: Wed, 29 Jul 2026 12:44:32 +0200 Subject: [PATCH 18/77] rename: change external/internal to outer/inner to be consistent with the app --- src/ParseInfo/DefaultSensors.h | 2 +- .../gatt_services/audio_config_service.c | 18 ++++++------- src/drivers/ADAU1860.cpp | 4 +-- src/modules/hw_codec.h | 10 +++---- src/modules/hw_codec_adau1860.cpp | 26 +++++++++---------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/ParseInfo/DefaultSensors.h b/src/ParseInfo/DefaultSensors.h index 97ce4166..e6daef36 100644 --- a/src/ParseInfo/DefaultSensors.h +++ b/src/ParseInfo/DefaultSensors.h @@ -18,8 +18,8 @@ #define MICRO_CHANNEL_COUNT 2 SensorComponent microComponenents[MICRO_CHANNEL_COUNT] = { + { .name = "OUTER", .unit = "ADC", .parseType = PARSE_TYPE_UINT16 }, { .name = "INNER", .unit = "ADC", .parseType = PARSE_TYPE_UINT16 }, - { .name = "Outer", .unit = "ADC", .parseType = PARSE_TYPE_UINT16 }, }; #define MICRO_GROUP_COUNT 1 diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index 489b240e..b106dcb0 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -75,8 +75,8 @@ static ssize_t read_audio_channel(struct bt_conn *conn, const struct bt_gatt_att static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { - // Mic gain is 2 bytes: [external_reg, internal_reg]. - // External mic maps to DMIC_VOL0; internal mic maps to DMIC_VOL1. + // Mic gain is 2 bytes: [outer_reg, inner_reg]. + // Outer mic maps to DMIC_VOL0; inner mic maps to DMIC_VOL1. // Per ADAU186x DMIC_VOL register (0x4000C045): // 0x00 = +24 dB // 0x01-0x3F = +23.625 to +0.375 dB (0.375 dB steps) @@ -88,17 +88,17 @@ static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr * return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } - uint8_t gain_external = ((uint8_t*)buf)[0]; - uint8_t gain_internal = ((uint8_t*)buf)[1]; + uint8_t gain_outer = ((uint8_t*)buf)[0]; + uint8_t gain_inner = ((uint8_t*)buf)[1]; - int ret = hw_codec_mic_gain_set(gain_external, gain_internal); + int ret = hw_codec_mic_gain_set(gain_outer, gain_inner); if (ret) { LOG_ERR("Failed to set mic gain: %d", ret); return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); } - LOG_INF("DMIC gain via BLE: external=0x%02x internal=0x%02x", - gain_external, gain_internal); + LOG_INF("DMIC gain via BLE: outer=0x%02x inner=0x%02x", + gain_outer, gain_inner); return len; } @@ -106,8 +106,8 @@ static ssize_t read_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *a void *buf, uint16_t len, uint16_t offset) { uint8_t gains[2] = { - hw_codec_mic_gain_get_external(), - hw_codec_mic_gain_get_internal() + hw_codec_mic_gain_get_outer(), + hw_codec_mic_gain_get_inner() }; return bt_gatt_attr_read(conn, attr, buf, len, offset, gains, sizeof(gains)); diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index fc16a923..c818c7d0 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -209,8 +209,8 @@ int ADAU1860::begin() { writeReg(registers::SPT0_ROUTE1, &spt0_route1, sizeof(spt0_route1)); uint8_t dmic_vol = 0x20; // 12dB - // DMIC channel 0 - external mic - // DMIC channel 1 - internal mic + // DMIC channel 0 - outer mic + // DMIC channel 1 - inner mic writeReg(registers::DMIC_VOL0, &dmic_vol, sizeof(dmic_vol)); writeReg(registers::DMIC_VOL1, &dmic_vol, sizeof(dmic_vol)); diff --git a/src/modules/hw_codec.h b/src/modules/hw_codec.h index 9e726ed8..f25755e8 100644 --- a/src/modules/hw_codec.h +++ b/src/modules/hw_codec.h @@ -121,8 +121,8 @@ enum audio_mode hw_codec_get_audio_mode(); /* Microphone (DMIC) gain control. * Board mapping: - * ADAU186x DMIC_VOL0 / DMIC channel 0 controls the external microphone. - * ADAU186x DMIC_VOL1 / DMIC channel 1 controls the internal microphone. + * ADAU186x DMIC_VOL0 / DMIC channel 0 controls the outer microphone. + * ADAU186x DMIC_VOL1 / DMIC channel 1 controls the inner microphone. * Register mapping (per ADAU186x datasheet DMIC_VOL0 @ 0x4000C045): * 0x00 = +24 dB * 0x01-0x3F = +23.625 to +0.375 dB (decrement by 0.375 dB per step) @@ -131,9 +131,9 @@ enum audio_mode hw_codec_get_audio_mode(); * 0xFE = -71.25 dB * 0xFF = Mute */ -int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg); -uint8_t hw_codec_mic_gain_get_external(void); -uint8_t hw_codec_mic_gain_get_internal(void); +int hw_codec_mic_gain_set(uint8_t gain_outer_reg, uint8_t gain_inner_reg); +uint8_t hw_codec_mic_gain_get_outer(void); +uint8_t hw_codec_mic_gain_get_inner(void); #ifdef __cplusplus } diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index a370ae2e..ada2b4af 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -39,8 +39,8 @@ K_THREAD_STACK_DEFINE(volume_msg_sub_thread_stack, CONFIG_VOLUME_MSG_SUB_STACK_S static enum audio_mode audio_mode; enum hw_codec_mic { - HW_CODEC_MIC_EXTERNAL = 0, - HW_CODEC_MIC_INTERNAL = 1, + HW_CODEC_MIC_OUTER = 0, + HW_CODEC_MIC_INNER = 1, }; static int settings_set_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) @@ -322,35 +322,35 @@ int hw_codec_stop_audio(void) * 0xFE = -71.25 dB * 0xFF = Mute */ -int hw_codec_mic_gain_set(uint8_t gain_external_reg, uint8_t gain_internal_reg) +int hw_codec_mic_gain_set(uint8_t gain_outer_reg, uint8_t gain_inner_reg) { int ret; - ret = dac.mic_gain_write(HW_CODEC_MIC_EXTERNAL, gain_external_reg); + ret = dac.mic_gain_write(HW_CODEC_MIC_OUTER, gain_outer_reg); if (ret) { - LOG_ERR("Failed to set external mic gain: %d", ret); + LOG_ERR("Failed to set outer mic gain: %d", ret); return ret; } - ret = dac.mic_gain_write(HW_CODEC_MIC_INTERNAL, gain_internal_reg); + ret = dac.mic_gain_write(HW_CODEC_MIC_INNER, gain_inner_reg); if (ret) { - LOG_ERR("Failed to set internal mic gain: %d", ret); + LOG_ERR("Failed to set inner mic gain: %d", ret); return ret; } - LOG_INF("DMIC gain set: external=0x%02x, internal=0x%02x", - gain_external_reg, gain_internal_reg); + LOG_INF("DMIC gain set: outer=0x%02x, inner=0x%02x", + gain_outer_reg, gain_inner_reg); return 0; } -uint8_t hw_codec_mic_gain_get_external(void) +uint8_t hw_codec_mic_gain_get_outer(void) { - return dac.mic_gain_read(HW_CODEC_MIC_EXTERNAL); + return dac.mic_gain_read(HW_CODEC_MIC_OUTER); } -uint8_t hw_codec_mic_gain_get_internal(void) +uint8_t hw_codec_mic_gain_get_inner(void) { - return dac.mic_gain_read(HW_CODEC_MIC_INTERNAL); + return dac.mic_gain_read(HW_CODEC_MIC_INNER); } From 63efde2b3e6c179fb8bf468d03b3e80f1b0af4f7 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Sat, 31 Jan 2026 03:15:06 +0100 Subject: [PATCH 19/77] implemented seal-check --- include/openearable_common.h | 8 + prj.conf | 9 + prj_fota.conf | 4 + src/Battery/BQ25120a.cpp | 8 - src/Battery/PowerManager.cpp | 24 +- src/ParseInfo/ParseType.h | 2 +- src/SD_Card/SDLogger/SDLogger.h | 20 +- src/SensorManager/SensorManager.cpp | 1 + src/audio/audio_datapath.c | 197 +++- src/audio/audio_datapath.h | 44 + src/audio/multitone.h | 1003 +++++++++++++++++ src/bluetooth/gatt_services/CMakeLists.txt | 1 + src/bluetooth/gatt_services/led_service.cpp | 3 - .../gatt_services/seal_check_service.c | 432 +++++++ .../gatt_services/seal_check_service.h | 42 + unicast_server/main.cpp | 4 + 16 files changed, 1748 insertions(+), 54 deletions(-) create mode 100644 src/audio/multitone.h create mode 100644 src/bluetooth/gatt_services/seal_check_service.c create mode 100644 src/bluetooth/gatt_services/seal_check_service.h diff --git a/include/openearable_common.h b/include/openearable_common.h index 12c728a5..2b567497 100644 --- a/include/openearable_common.h +++ b/include/openearable_common.h @@ -115,6 +115,14 @@ struct sensor_config { uint8_t storageOptions; } __attribute__((packed)); +struct seal_check_data { + uint8_t version; + uint8_t quality; + uint8_t mean_magnitude; + uint8_t num_peaks; + uint16_t frequencies[9]; // 12.4 fixed point + uint16_t magnitudes[9]; +} __attribute__((packed)); struct battery_settings { float u_nominal; diff --git a/prj.conf b/prj.conf index ad23effc..c48d47f5 100644 --- a/prj.conf +++ b/prj.conf @@ -113,6 +113,10 @@ CONFIG_SFLOAT=y CONFIG_FPU=y +CONFIG_CMSIS_DSP=y +CONFIG_CMSIS_DSP_TRANSFORM=y +CONFIG_CMSIS_DSP_COMPLEXMATH=y + CONFIG_NEWLIB_LIBC=y CONFIG_NEWLIB_LIBC_FLOAT_PRINTF=y CONFIG_NEWLIB_LIBC_FLOAT_SCANF=y @@ -196,6 +200,11 @@ CONFIG_FS_FATFS_EXFAT=y # Enable exFAT support CONFIG_FDSP=y +# CMSIS-DSP Configuration +CONFIG_CMSIS_DSP=y +CONFIG_CMSIS_DSP_TRANSFORM=y +CONFIG_CMSIS_DSP_COMPLEXMATH=y + CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 #CONFIG_USB_MASS_STORAGE=y diff --git a/prj_fota.conf b/prj_fota.conf index 6356aeb2..e129b55b 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -261,3 +261,7 @@ CONFIG_MCUMGR_GRP_OS_RESET_HOOK=y #CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK=y CONFIG_SETUP_FUEL_GAUGE=y + +CONFIG_CMSIS_DSP=y +CONFIG_CMSIS_DSP_TRANSFORM=y +CONFIG_CMSIS_DSP_COMPLEXMATH=y \ No newline at end of file diff --git a/src/Battery/BQ25120a.cpp b/src/Battery/BQ25120a.cpp index 71da49d9..183f390d 100644 --- a/src/Battery/BQ25120a.cpp +++ b/src/Battery/BQ25120a.cpp @@ -243,10 +243,8 @@ uint8_t BQ25120a::write_LDO_voltage_control(float volt) { readReg(registers::LS_LDO_CTRL, &status, sizeof(status)); - //status |= (((uint16_t)((volt - 0.8) * 10)) & 0x1F) << 2; status &= 1 << 7; status |= ((uint8_t)((volt - 0.8f) * 10 + EPS)) << 2; - //status |= 1 << 7; writeReg(registers::LS_LDO_CTRL, &status, sizeof(status)); @@ -292,8 +290,6 @@ chrg_state BQ25120a::read_termination_control() { struct chrg_state chrg; - // if (!ret) printk("failed to read\n"); - chrg.enabled = status & 0x2; //chrg.high_impedance = status & 0x1; @@ -343,8 +339,6 @@ ilim_uvlo BQ25120a::read_uvlo_ilim() { bool ret = readReg(registers::ILIM_UVLO, (uint8_t *) &status, sizeof(status)); - // if (!ret) printk("failed to read\n"); - param.uvlo_v = CLAMP(3.0f- 0.2f * ((status & 0x7) - 2), 2.2, 3.0); param.lim_mA = 50.f + 50.f * ((status >> 3) & 0x7); @@ -410,8 +404,6 @@ button_state BQ25120a::read_button_state() { uint8_t status = 0; bool ret = readReg(registers::BTN_CTRL, (uint8_t *) &status, sizeof(status)); - // if (!ret) printk("failed to read\n"); - btn.wake_1 = status & 0x2; btn.wake_2 = status & 0x1; diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index 628739da..5a2c4e23 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -631,7 +631,7 @@ int PowerManager::power_down(bool fault) { ret = pm_device_action_run(ls_sd, PM_DEVICE_ACTION_SUSPEND); ret = pm_device_action_run(ls_3_3, PM_DEVICE_ACTION_SUSPEND); ret = pm_device_action_run(ls_1_8, PM_DEVICE_ACTION_SUSPEND); - ret = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); + ret = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); /*const struct device *const i2c = DEVICE_DT_GET(DT_NODELABEL(i2c1)); ret = pm_device_action_run(i2c, PM_DEVICE_ACTION_SUSPEND); @@ -666,27 +666,7 @@ void PowerManager::charge_task() { battery_controller.enable_charge(); } - //if (last_charging_state != charging_state || ) { - k_work_submit(&fuel_gauge_work); - //state_inidicator.set_state() - /*switch (charging_state) { - case 0: - LOG_INF("charging state: ready"); - break; - case 1: - LOG_INF("charging state: charging"); - break; - case 2: - LOG_INF("charging state: done"); - break; - case 3: - LOG_WRN("charging state: fault"); - - //battery_controller.setup(_battery_settings); - - break; - }*/ - //} + k_work_submit(&fuel_gauge_work); last_charging_state = charging_state; } diff --git a/src/ParseInfo/ParseType.h b/src/ParseInfo/ParseType.h index 01dc819a..e92361f7 100644 --- a/src/ParseInfo/ParseType.h +++ b/src/ParseInfo/ParseType.h @@ -15,7 +15,7 @@ enum ParseType { PARSE_TYPE_DOUBLE, }; -const int parseTypeSizes[] = { +static const int parseTypeSizes[] = { 1, // PARSE_TYPE_INT8 1, // PARSE_TYPE_UINT8 diff --git a/src/SD_Card/SDLogger/SDLogger.h b/src/SD_Card/SDLogger/SDLogger.h index 279fdae0..0b037e87 100644 --- a/src/SD_Card/SDLogger/SDLogger.h +++ b/src/SD_Card/SDLogger/SDLogger.h @@ -23,11 +23,6 @@ static_assert(BUFFER_SIZE % SD_BLOCK_SIZE == 0, "BUFFER_SIZE must be a multiple // Singleton pattern class SDLogger { -protected: - // Add static instance pointer for work handler - //static SDLogger* instance_ptr; - //friend void sd_work_handler(struct k_work* work); - private: SDCardManager* sd_card = nullptr; @@ -84,14 +79,23 @@ class SDLogger { */ int write_sensor_data(const void* const* data_blocks, const size_t* lengths, size_t block_count); + /** + * @brief Write a single sensor_data message to the log file + * @param msg The sensor_data message to write + * @return 0 on success, negative error code on failure + */ int write_sensor_data(const sensor_data& msg); /** - * @brief End logging and close the current file - * @return 0 on success, negative error code on failure - */ + * @brief End logging and close the current file + * @return 0 on success, negative error code on failure + */ int end(); + /** + * @brief Check if the logger is currently recording + * @return true if active, false otherwise + */ bool is_active(); SDLogger(SDLogger const&) = delete; diff --git a/src/SensorManager/SensorManager.cpp b/src/SensorManager/SensorManager.cpp index 1eea6daf..60f5e3cd 100644 --- a/src/SensorManager/SensorManager.cpp +++ b/src/SensorManager/SensorManager.cpp @@ -245,6 +245,7 @@ static void config_work_handler(struct k_work *work) { std::string filename = recording_name_prefix + std::to_string(micros()); int ret = sdlogger.begin(filename); if (ret == 0) state_indicator.set_sd_state(SD_RECORDING); + else LOG_ERR("Failed to start SDLogger, ret: %d", ret); } } else if (sd_sensors.find(config.sensorId) != sd_sensors.end()) { sd_sensors.erase(config.sensorId); diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index eac3eb65..9cae119c 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,6 +32,13 @@ #include "sdlogger_wrapper.h" #include "decimation_filter.h" #include "arm_math.h" +#include "hw_codec.h" +//#include "../drivers/ADAU1860.h" + +#include "../SensorManager/SensorManager.h" +#include "openearable_common.h" +#include "SensorScheme.h" +#include "../bluetooth/gatt_services/seal_check_service.h" #include LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); @@ -95,6 +104,9 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); /* How often to print under-run warning */ #define UNDERRUN_LOG_INTERVAL_BLKS 5000 +extern int seal_check_mic_index; +extern int16_t seal_check_mic[]; + enum drift_comp_state { DRIFT_STATE_INIT, /* Waiting for data to be received */ DRIFT_STATE_CALIB, /* Calibrate and zero out local delay */ @@ -175,8 +187,6 @@ static struct { static struct k_msgq * sensor_queue; -//extern struct audio_data fifo_rx; - //K_MSGQ_DEFINE(rx_queue, sizeof(struct audio_data), 16, 4); extern struct k_msgq_t encoder_queue; @@ -190,8 +200,24 @@ static k_tid_t data_thread_id; bool _record_to_sd = false; +// Buffer recording variables +static bool _record_to_buffer = false; +static int16_t *_record_buffer = NULL; +static int _record_num_samples = 0; +static int _record_current_index = 0; +static bool _record_left = false; +static bool _record_right = false; +static void (*_record_callback)(void) = NULL; + int _count = 0; +static int16_t *buffer_play_data = NULL; +static uint32_t buffer_play_pos; +static int buffer_play_num_samples; +static float buffer_play_amplitude; +static bool buffer_play_loop; +static void (*buffer_play_callback)(void) = NULL; + extern struct k_poll_signal encoder_sig; extern struct k_poll_event logger_sig; @@ -226,7 +252,7 @@ static void data_thread(void *arg1, void *arg2, void *arg3) unsigned int logger_signaled; - if (_record_to_sd) { + if (_record_to_sd || _record_to_buffer) { /* Decimate audio data from 48kHz to the desired sampling rate */ int16_t *audio_block = (int16_t *)(audio_item.data + (i * BLOCK_SIZE_BYTES)); uint32_t num_frames = BLOCK_SIZE_BYTES / sizeof(int16_t) / 2; /* stereo frames */ @@ -238,6 +264,48 @@ static void data_thread(void *arg1, void *arg2, void *arg3) continue; } + // Generic buffer recording + if (_record_to_buffer && _record_buffer != NULL) { + for(int i = 0; i < decimated_frames; i++) { + _record_current_index++; + + // Skip samples during initial drop period + if (_record_current_index <= 0) { + continue; + } + + // Calculate actual buffer index (after initial drop) + int buffer_index = _record_current_index - 1; + + if (buffer_index < _record_num_samples) { + if (_record_left && _record_right) { + // Stereo recording - store both channels + if (buffer_index * 2 + 1 < _record_num_samples) { + _record_buffer[buffer_index * 2] = decimated_audio[2 * i]; // Left + _record_buffer[buffer_index * 2 + 1] = decimated_audio[2 * i + 1]; // Right + } + } else if (_record_left) { + // Left channel only + _record_buffer[buffer_index] = decimated_audio[2 * i]; + } else if (_record_right) { + // Right channel only + _record_buffer[buffer_index] = decimated_audio[2 * i + 1]; + } + + } + + // Check if recording is complete + if (buffer_index >= _record_num_samples || + (_record_left && _record_right && buffer_index * 2 >= _record_num_samples)) { + _record_to_buffer = false; + if (_record_callback) { + _record_callback(); + } + break; + } + } + } + struct sensor_msg audio_msg; audio_msg.sd = true; @@ -293,12 +361,41 @@ void record_to_sd(bool active) { } void audio_datapath_stop_recording(void) { + // Stop buffer recording + _record_to_buffer = false; + _record_buffer = NULL; + _record_callback = NULL; + // Stop SD recording _record_to_sd = false; LOG_DBG("Audio recording stopped safely"); } +void record_to_buffer_stop(void) { + _record_to_buffer = false; + _record_buffer = NULL; + _record_callback = NULL; + LOG_DBG("Buffer recording stopped"); +} + +void record_to_buffer(int16_t *buffer, int num_samples, int initial_drop, bool left, bool right, void (*callback)(void)) { + if (buffer == NULL || num_samples <= 0) { + LOG_ERR("Invalid buffer recording parameters"); + return; + } + + _record_buffer = buffer; + _record_num_samples = num_samples; + _record_current_index = -initial_drop; + _record_left = left; + _record_right = right; + _record_callback = callback; + _record_to_buffer = true; + + LOG_INF("Started buffer recording: %d samples, initial_drop=%d, left=%d, right=%d", num_samples, initial_drop, left, right); +} + // Funktion, um den neuen Thread zu starten void start_data_thread(void) { @@ -651,6 +748,16 @@ static void tone_stop_worker(struct k_work *work) { tone_active = false; memset(test_tone_buf, 0, sizeof(test_tone_buf)); + + LOG_INF("Tone playback stopped"); + + // Call buffer playback callback if set + if (buffer_play_callback) { + buffer_play_callback(); + buffer_play_callback = NULL; + } + buffer_play_data = NULL; + LOG_DBG("Tone stopped"); struct sensor_config mic = {ID_MICRO, 0, 0}; @@ -695,25 +802,91 @@ int audio_datapath_tone_play(uint16_t freq, uint16_t dur_ms, float amplitude) return 0; } +int audio_datapath_buffer_play(int16_t *buffer, int num_samples, bool loop, float amplitude, void (*callback)(void)) +{ + if (buffer_play_data != NULL) { + return -EBUSY; + } + + if (!IS_ENABLED(CONFIG_AUDIO_TEST_TONE)) { + LOG_WRN("Test tone disabled"); + return -ENOTSUP; + } + + if (buffer == NULL || num_samples <= 0) { + LOG_ERR("Invalid buffer play parameters"); + return -EINVAL; + } + + buffer_play_data = buffer; + buffer_play_pos = 0; + buffer_play_num_samples = num_samples; + buffer_play_amplitude = amplitude; + buffer_play_loop = loop; + buffer_play_callback = callback; + + LOG_DBG("Buffer playback started: %d samples, loop=%d, amplitude=%.2f", num_samples, loop, (double)amplitude); + return 0; +} + void audio_datapath_tone_stop(void) { k_timer_stop(&tone_stop_timer); k_work_submit(&tone_stop_work); } +void audio_datapath_buffer_stop(void) +{ + k_timer_stop(&tone_stop_timer); + buffer_play_data = NULL; + if (buffer_play_callback) { + buffer_play_callback(); + buffer_play_callback = NULL; + } + LOG_DBG("Buffer playback stopped"); +} + static void tone_mix(uint8_t *tx_buf) { int ret; - int8_t tone_buf_continuous[BLK_MONO_SIZE_OCTETS]; - static uint32_t finite_pos; - ret = contin_array_create(tone_buf_continuous, BLK_MONO_SIZE_OCTETS, test_tone_buf, - test_tone_size, &finite_pos); - ERR_CHK(ret); + if (tone_active) { + int8_t tone_buf_continuous[BLK_MONO_SIZE_OCTETS]; + static uint32_t finite_pos; + + ret = contin_array_create(tone_buf_continuous, BLK_MONO_SIZE_OCTETS, test_tone_buf, + test_tone_size, &finite_pos); + ERR_CHK(ret); + + ret = pcm_mix(tx_buf, BLK_STEREO_SIZE_OCTETS, tone_buf_continuous, BLK_MONO_SIZE_OCTETS, + B_MONO_INTO_A_STEREO_L); + ERR_CHK(ret); + } else if (buffer_play_data != NULL) { + int8_t buffer_play_buf[BLK_MONO_SIZE_OCTETS]; + int samples_per_block = BLK_MONO_SIZE_OCTETS / sizeof(int16_t); + + /* Copy buffer samples to playback buffer with amplitude scaling */ + for (int i = 0; i < samples_per_block; i++) { + if (buffer_play_pos >= buffer_play_num_samples) { + if (buffer_play_loop) { + buffer_play_pos = 0; /* Loop the buffer */ + } else { + /* Stop after one complete playback */ + k_work_submit(&tone_stop_work); + memset(&buffer_play_buf[i * 2], 0, (samples_per_block - i) * 2); + break; + } + } + int16_t sample = (int16_t)(buffer_play_data[buffer_play_pos] * buffer_play_amplitude); + buffer_play_buf[i * 2] = sample & 0xFF; + buffer_play_buf[i * 2 + 1] = (sample >> 8) & 0xFF; + buffer_play_pos++; + } - ret = pcm_mix(tx_buf, BLK_STEREO_SIZE_OCTETS, tone_buf_continuous, BLK_MONO_SIZE_OCTETS, - B_MONO_INTO_A_STEREO_L); - ERR_CHK(ret); + ret = pcm_mix(tx_buf, BLK_STEREO_SIZE_OCTETS, buffer_play_buf, BLK_MONO_SIZE_OCTETS, + B_MONO_INTO_A_STEREO_L); + ERR_CHK(ret); + } } /* Alternate-buffers used when there is no active audio stream. @@ -843,7 +1016,7 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t memset(tx_buf, 0, BLK_STEREO_SIZE_OCTETS); } - if (tone_active) { + if (tone_active || buffer_play_data != NULL) { tone_mix(tx_buf); } } diff --git a/src/audio/audio_datapath.h b/src/audio/audio_datapath.h index 6f758fad..091e0021 100644 --- a/src/audio/audio_datapath.h +++ b/src/audio/audio_datapath.h @@ -35,6 +35,50 @@ int audio_datapath_tone_play(uint16_t freq, uint16_t dur_ms, float amplitude); */ void audio_datapath_tone_stop(void); + + + + +/** + * @brief Plays a buffer over I2S TX stream + * + * @param buffer Pointer to audio buffer to play + * @param num_samples Number of samples in buffer + * @param loop Whether to loop the buffer continuously + * @param amplitude Playback amplitude [0, 1] + * @param callback Callback function called when playback completes (optional) + * + * @return 0 if successful, error otherwise + */ +int audio_datapath_buffer_play(int16_t *buffer, int num_samples, bool loop, float amplitude, void (*callback)(void)); + +/** + * @brief Stops buffer playback + */ +void audio_datapath_buffer_stop(void); + +/** + * @brief Records audio data to a buffer + * + * @param buffer Pointer to buffer where audio will be stored + * @param num_samples Number of samples to record + * @param initial_drop Number of initial samples to drop before recording + * @param left Whether to record left channel + * @param right Whether to record right channel + * @param callback Callback function called when recording completes (optional) + */ +void record_to_buffer(int16_t *buffer, int num_samples, int initial_drop, bool left, bool right, void (*callback)(void)); + +/** + * @brief Stops buffer recording safely + */ +void record_to_buffer_stop(void); + +/** + * @brief Stops all audio recording safely (buffer and SD) + */ +void audio_datapath_stop_recording(void); + /** * @brief Stops buffer recording safely */ diff --git a/src/audio/multitone.h b/src/audio/multitone.h new file mode 100644 index 00000000..61142e50 --- /dev/null +++ b/src/audio/multitone.h @@ -0,0 +1,1003 @@ +const int16_t multitone[] = { + 2470, 2689, 2878, 3033, 3145, 3210, 3222, 3177, 3073, 2907, 2678, 2386, 2032, 1618, 1148, 626, 57, -551, -1194, -1862, -2547, -3241, -3934, -4617, -5281, -5916, -6514, -7066, -7564, -8002, -8372, -8670, + -8891, -9032, -9091, -9067, -8960, -8772, -8506, -8165, -7756, -7283, -6754, -6176, -5559, -4911, -4242, -3562, -2880, -2206, -1550, -922, -330, 217, 712, 1149, 1522, 1826, 2057, 2214, 2294, 2297, 2226, 2081, + 1866, 1585, 1244, 847, 403, -82, -601, -1146, -1708, -2279, -2851, -3417, -3969, -4500, -5004, -5475, -5908, -6299, -6645, -6944, -7195, -7396, -7549, -7655, -7716, -7735, -7716, -7663, -7580, -7474, -7348, -7208, + -7060, -6909, -6760, -6618, -6487, -6371, -6272, -6194, -6138, -6105, -6095, -6108, -6142, -6195, -6263, -6345, -6435, -6529, -6622, -6709, -6784, -6843, -6880, -6889, -6867, -6808, -6709, -6567, -6380, -6145, -5863, -5533, + -5156, -4735, -4272, -3772, -3240, -2680, -2100, -1507, -908, -310, 275, 843, 1382, 1886, 2346, 2753, 3101, 3382, 3592, 3724, 3776, 3743, 3625, 3419, 3128, 2751, 2293, 1758, 1149, 473, -261, -1048, + -1878, -2742, -3631, -4535, -5443, -6346, -7234, -8096, -8925, -9711, -10447, -11126, -11742, -12289, -12765, -13166, -13491, -13740, -13914, -14014, -14044, -14009, -13913, -13764, -13566, -13330, -13061, -12770, -12463, -12152, -11843, -11545, + -11267, -11015, -10798, -10620, -10487, -10404, -10374, -10400, -10481, -10619, -10813, -11060, -11358, -11702, -12087, -12509, -12960, -13434, -13923, -14419, -14915, -15403, -15876, -16325, -16743, -17124, -17462, -17752, -17988, -18167, -18287, -18346, + -18342, -18275, -18148, -17961, -17718, -17422, -17078, -16690, -16264, -15806, -15323, -14820, -14304, -13782, -13260, -12743, -12238, -11748, -11279, -10834, -10417, -10028, -9670, -9343, -9047, -8780, -8541, -8326, -8134, -7959, -7797, -7644, + -7495, -7344, -7186, -7016, -6830, -6622, -6388, -6125, -5830, -5500, -5135, -4733, -4294, -3821, -3314, -2777, -2213, -1628, -1026, -414, 202, 815, 1418, 2003, 2562, 3087, 3572, 4010, 4392, 4714, 4971, 5156, + 5268, 5303, 5260, 5139, 4939, 4664, 4315, 3898, 3417, 2879, 2291, 1660, 996, 308, -394, -1100, -1801, -2486, -3144, -3766, -4342, -4864, -5324, -5715, -6030, -6264, -6413, -6475, -6449, -6333, -6129, -5839, + -5466, -5016, -4493, -3906, -3260, -2564, -1828, -1061, -271, 529, 1332, 2128, 2906, 3659, 4377, 5053, 5679, 6251, 6762, 7209, 7590, 7901, 8143, 8316, 8421, 8461, 8439, 8361, 8230, 8053, 7836, 7586, + 7310, 7015, 6709, 6398, 6089, 5789, 5504, 5240, 5000, 4790, 4612, 4469, 4361, 4291, 4257, 4258, 4292, 4357, 4448, 4562, 4695, 4840, 4994, 5150, 5302, 5446, 5576, 5688, 5775, 5836, 5865, 5860, + 5820, 5743, 5628, 5476, 5288, 5066, 4814, 4535, 4233, 3913, 3580, 3241, 2902, 2569, 2248, 1945, 1668, 1421, 1210, 1041, 916, 840, 815, 843, 925, 1061, 1250, 1489, 1776, 2107, 2477, 2881, + 3312, 3764, 4229, 4700, 5168, 5627, 6069, 6484, 6868, 7211, 7509, 7755, 7945, 8075, 8142, 8145, 8082, 7954, 7763, 7511, 7202, 6840, 6431, 5982, 5500, 4993, 4468, 3936, 3405, 2884, 2383, 1909, + 1472, 1079, 738, 455, 236, 85, 7, 3, 75, 222, 444, 739, 1102, 1529, 2014, 2552, 3133, 3750, 4395, 5057, 5728, 6396, 7054, 7690, 8296, 8862, 9380, 9843, 10244, 10576, 10836, 11018, + 11121, 11143, 11083, 10942, 10723, 10428, 10061, 9628, 9134, 8586, 7991, 7356, 6691, 6004, 5302, 4594, 3889, 3194, 2517, 1865, 1244, 660, 117, -380, -829, -1228, -1576, -1873, -2118, -2314, -2464, -2569, + -2634, -2663, -2661, -2632, -2581, -2514, -2436, -2351, -2264, -2179, -2101, -2032, -1974, -1929, -1900, -1884, -1883, -1895, -1918, -1949, -1985, -2021, -2054, -2079, -2089, -2081, -2048, -1985, -1887, -1750, -1567, -1337, + -1054, -717, -323, 127, 635, 1200, 1819, 2491, 3210, 3973, 4774, 5607, 6464, 7338, 8221, 9105, 9980, 10838, 11670, 12467, 13221, 13924, 14570, 15150, 15661, 16096, 16452, 16726, 16917, 17024, 17048, 16991, + 16857, 16648, 16372, 16033, 15638, 15197, 14717, 14207, 13676, 13135, 12593, 12058, 11542, 11052, 10596, 10184, 9820, 9512, 9263, 9079, 8962, 8913, 8933, 9021, 9174, 9390, 9665, 9993, 10367, 10782, 11229, 11701, + 12188, 12682, 13173, 13654, 14114, 14546, 14942, 15294, 15596, 15842, 16027, 16147, 16199, 16182, 16095, 15938, 15714, 15424, 15072, 14663, 14202, 13696, 13150, 12573, 11972, 11355, 10729, 10104, 9486, 8883, 8302, 7749, + 7231, 6753, 6318, 5930, 5592, 5305, 5070, 4887, 4753, 4668, 4627, 4628, 4665, 4734, 4830, 4946, 5077, 5217, 5358, 5497, 5626, 5742, 5838, 5911, 5957, 5973, 5958, 5910, 5829, 5715, 5569, 5394, + 5192, 4967, 4722, 4463, 4195, 3922, 3651, 3386, 3134, 2900, 2689, 2505, 2353, 2237, 2159, 2122, 2126, 2173, 2261, 2390, 2556, 2758, 2990, 3248, 3527, 3820, 4120, 4421, 4716, 4996, 5254, 5483, + 5676, 5826, 5927, 5972, 5959, 5881, 5737, 5525, 5243, 4891, 4473, 3988, 3443, 2840, 2186, 1488, 753, -9, -792, -1585, -2378, -3161, -3925, -4658, -5352, -5997, -6583, -7105, -7553, -7921, -8205, -8400, + -8503, -8512, -8427, -8249, -7979, -7622, -7181, -6662, -6072, -5418, -4708, -3952, -3158, -2338, -1500, -655, 185, 1014, 1820, 2595, 3330, 4018, 4651, 5226, 5735, 6177, 6549, 6849, 7076, 7233, 7320, 7341, + 7299, 7199, 7047, 6848, 6608, 6336, 6037, 5718, 5388, 5052, 4717, 4390, 4075, 3779, 3504, 3255, 3035, 2844, 2685, 2556, 2458, 2388, 2344, 2323, 2322, 2334, 2357, 2385, 2412, 2433, 2443, 2436, + 2408, 2354, 2269, 2151, 1996, 1803, 1569, 1294, 979, 625, 234, -191, -648, -1131, -1636, -2156, -2687, -3222, -3753, -4275, -4781, -5265, -5719, -6139, -6519, -6853, -7139, -7373, -7552, -7675, -7741, -7751, + -7707, -7610, -7463, -7272, -7040, -6774, -6480, -6165, -5835, -5500, -5165, -4840, -4531, -4246, -3991, -3774, -3599, -3472, -3397, -3377, -3414, -3509, -3663, -3873, -4138, -4455, -4818, -5224, -5665, -6135, -6626, -7130, + -7639, -8142, -8632, -9100, -9536, -9932, -10279, -10572, -10802, -10964, -11053, -11065, -10998, -10849, -10620, -10310, -9921, -9458, -8923, -8324, -7666, -6957, -6205, -5420, -4609, -3784, -2954, -2130, -1321, -537, 211, 917, + 1570, 2164, 2693, 3149, 3530, 3831, 4050, 4186, 4239, 4210, 4101, 3917, 3660, 3337, 2954, 2517, 2033, 1510, 957, 381, -208, -804, -1399, -1984, -2554, -3101, -3621, -4107, -4557, -4966, -5333, -5657, + -5936, -6172, -6365, -6519, -6634, -6717, -6769, -6796, -6802, -6793, -6774, -6749, -6724, -6703, -6690, -6690, -6706, -6740, -6794, -6870, -6968, -7088, -7228, -7387, -7562, -7749, -7945, -8146, -8345, -8539, -8721, -8885, + -9027, -9140, -9220, -9260, -9257, -9207, -9107, -8953, -8745, -8481, -8162, -7788, -7363, -6888, -6368, -5808, -5213, -4590, -3946, -3288, -2624, -1963, -1313, -683, -81, 485, 1007, 1478, 1892, 2243, 2526, 2736, + 2872, 2931, 2913, 2818, 2649, 2407, 2098, 1726, 1296, 818, 297, -256, -835, -1429, -2029, -2624, -3205, -3762, -4287, -4770, -5204, -5581, -5896, -6141, -6314, -6412, -6431, -6371, -6233, -6018, -5729, -5370, + -4946, -4462, -3927, -3346, -2728, -2083, -1418, -743, -67, 600, 1250, 1876, 2468, 3019, 3524, 3976, 4371, 4705, 4977, 5184, 5326, 5404, 5419, 5376, 5277, 5127, 4932, 4698, 4431, 4139, 3829, 3508, + 3185, 2866, 2559, 2271, 2007, 1774, 1577, 1419, 1305, 1236, 1215, 1241, 1315, 1434, 1598, 1802, 2044, 2318, 2619, 2942, 3281, 3630, 3981, 4330, 4669, 4993, 5296, 5572, 5818, 6028, 6200, 6331, + 6420, 6465, 6466, 6425, 6342, 6220, 6063, 5874, 5658, 5418, 5161, 4892, 4617, 4341, 4069, 3807, 3559, 3332, 3127, 2949, 2800, 2681, 2595, 2540, 2516, 2522, 2554, 2609, 2683, 2771, 2867, 2965, + 3058, 3140, 3203, 3240, 3244, 3208, 3125, 2990, 2797, 2540, 2217, 1824, 1359, 822, 212, -469, -1219, -2034, -2910, -3840, -4818, -5836, -6887, -7959, -9045, -10134, -11214, -12277, -13312, -14307, -15254, -16143, + -16965, -17712, -18377, -18954, -19439, -19826, -20114, -20302, -20389, -20376, -20266, -20063, -19772, -19399, -18950, -18433, -17857, -17230, -16563, -15866, -15147, -14418, -13687, -12965, -12261, -11583, -10939, -10335, -9778, -9273, -8824, -8434, + -8105, -7837, -7629, -7482, -7391, -7355, -7367, -7425, -7521, -7650, -7805, -7980, -8167, -8360, -8551, -8735, -8906, -9057, -9184, -9282, -9348, -9379, -9373, -9329, -9248, -9130, -8976, -8790, -8573, -8331, -8068, -7788, + -7497, -7201, -6905, -6615, -6337, -6076, -5837, -5624, -5443, -5297, -5188, -5118, -5088, -5100, -5152, -5243, -5371, -5533, -5725, -5943, -6182, -6436, -6700, -6966, -7230, -7484, -7722, -7938, -8126, -8281, -8398, -8473, + -8503, -8484, -8416, -8297, -8128, -7909, -7643, -7333, -6983, -6598, -6182, -5743, -5286, -4819, -4350, -3886, -3435, -3004, -2601, -2233, -1906, -1627, -1400, -1230, -1120, -1074, -1091, -1172, -1318, -1524, -1790, -2110, + -2479, -2892, -3342, -3820, -4320, -4831, -5345, -5854, -6347, -6815, -7250, -7643, -7986, -8272, -8494, -8647, -8726, -8728, -8651, -8493, -8255, -7938, -7545, -7079, -6545, -5949, -5297, -4599, -3862, -3094, -2306, -1507, + -707, 83, 855, 1599, 2307, 2969, 3578, 4127, 4611, 5024, 5363, 5625, 5809, 5914, 5942, 5893, 5772, 5582, 5328, 5016, 4652, 4244, 3800, 3326, 2832, 2325, 1814, 1306, 809, 330, -123, -546, + -934, -1282, -1587, -1846, -2057, -2221, -2337, -2406, -2431, -2414, -2359, -2269, -2150, -2006, -1842, -1663, -1476, -1284, -1095, -911, -737, -577, -435, -312, -211, -133, -77, -44, -32, -39, -62, -98, + -142, -191, -239, -281, -312, -327, -320, -285, -218, -115, 27, 215, 448, 730, 1060, 1440, 1868, 2341, 2859, 3416, 4008, 4629, 5274, 5934, 6604, 7275, 7938, 8585, 9208, 9797, 10346, 10845, + 11288, 11667, 11976, 12211, 12367, 12441, 12430, 12334, 12153, 11889, 11543, 11119, 10623, 10061, 9438, 8763, 8044, 7290, 6510, 5715, 4914, 4118, 3335, 2577, 1852, 1168, 533, -44, -559, -1007, -1383, -1685, + -1911, -2060, -2133, -2133, -2062, -1924, -1725, -1470, -1168, -824, -448, -47, 369, 792, 1214, 1624, 2017, 2383, 2715, 3007, 3254, 3451, 3595, 3682, 3711, 3682, 3596, 3454, 3258, 3014, 2725, 2397, + 2036, 1649, 1242, 824, 403, -14, -420, -806, -1167, -1494, -1782, -2025, -2219, -2360, -2446, -2473, -2441, -2350, -2201, -1996, -1737, -1428, -1074, -679, -248, 211, 694, 1194, 1704, 2218, 2729, 3232, + 3721, 4190, 4636, 5054, 5442, 5796, 6116, 6400, 6650, 6865, 7048, 7202, 7329, 7433, 7519, 7590, 7653, 7713, 7774, 7841, 7921, 8016, 8133, 8274, 8442, 8640, 8871, 9133, 9428, 9755, 10111, 10495, + 10902, 11328, 11768, 12216, 12666, 13111, 13543, 13955, 14340, 14689, 14997, 15254, 15456, 15594, 15666, 15664, 15587, 15431, 15194, 14877, 14478, 14001, 13448, 12824, 12132, 11381, 10575, 9724, 8836, 7921, 6988, 6048, + 5110, 4186, 3284, 2417, 1592, 819, 107, -537, -1108, -1599, -2006, -2327, -2558, -2700, -2753, -2719, -2601, -2404, -2133, -1794, -1394, -942, -446, 84, 640, 1211, 1789, 2362, 2921, 3459, 3966, 4434, + 4857, 5228, 5543, 5796, 5986, 6110, 6168, 6159, 6086, 5950, 5755, 5505, 5206, 4863, 4482, 4070, 3635, 3183, 2722, 2259, 1801, 1354, 926, 521, 145, -198, -506, -775, -1003, -1190, -1337, -1443, + -1512, -1544, -1545, -1517, -1465, -1394, -1310, -1218, -1123, -1031, -946, -876, -823, -792, -786, -810, -864, -950, -1070, -1221, -1405, -1618, -1858, -2121, -2403, -2701, -3007, -3316, -3623, -3921, -4204, -4466, + -4699, -4900, -5061, -5178, -5248, -5266, -5229, -5136, -4986, -4779, -4515, -4197, -3828, -3410, -2950, -2453, -1924, -1370, -799, -218, 363, 939, 1502, 2042, 2552, 3026, 3458, 3840, 4168, 4438, 4647, 4793, + 4874, 4891, 4845, 4738, 4573, 4355, 4089, 3782, 3440, 3070, 2683, 2285, 1885, 1493, 1118, 769, 453, 179, -45, -215, -323, -365, -337, -238, -64, 182, 502, 893, 1350, 1871, 2448, 3076, + 3746, 4451, 5182, 5930, 6685, 7436, 8175, 8891, 9575, 10217, 10810, 11344, 11814, 12213, 12536, 12778, 12937, 13011, 12999, 12902, 12721, 12459, 12121, 11710, 11232, 10695, 10105, 9469, 8796, 8094, 7371, 6637, + 5898, 5164, 4441, 3737, 3058, 2410, 1798, 1225, 695, 211, -226, -617, -962, -1262, -1519, -1736, -1917, -2066, -2186, -2283, -2363, -2429, -2487, -2542, -2598, -2660, -2732, -2816, -2915, -3031, -3165, -3317, + -3488, -3675, -3878, -4092, -4315, -4543, -4771, -4995, -5209, -5408, -5586, -5738, -5858, -5941, -5984, -5980, -5928, -5823, -5664, -5449, -5179, -4853, -4474, -4043, -3564, -3041, -2481, -1888, -1270, -633, 13, 661, + 1303, 1930, 2532, 3102, 3631, 4111, 4535, 4897, 5190, 5410, 5553, 5616, 5598, 5497, 5315, 5053, 4715, 4304, 3826, 3287, 2694, 2055, 1377, 672, -53, -788, -1522, -2247, -2953, -3631, -4272, -4867, + -5412, -5898, -6320, -6676, -6960, -7172, -7311, -7378, -7373, -7301, -7164, -6968, -6720, -6424, -6090, -5725, -5337, -4936, -4530, -4127, -3738, -3370, -3032, -2730, -2472, -2263, -2108, -2012, -1978, -2007, -2101, -2258, + -2479, -2759, -3097, -3486, -3923, -4402, -4914, -5454, -6014, -6586, -7162, -7735, -8295, -8836, -9352, -9834, -10277, -10677, -11028, -11327, -11571, -11759, -11891, -11965, -11984, -11949, -11864, -11732, -11558, -11346, -11101, -10830, + -10539, -10234, -9920, -9605, -9293, -8990, -8701, -8431, -8183, -7961, -7766, -7600, -7464, -7358, -7280, -7229, -7203, -7197, -7209, -7234, -7266, -7301, -7334, -7358, -7369, -7361, -7330, -7269, -7176, -7046, -6878, -6667, + -6414, -6117, -5778, -5397, -4977, -4520, -4032, -3516, -2978, -2424, -1861, -1297, -737, -191, 334, 831, 1293, 1713, 2083, 2399, 2655, 2848, 2972, 3028, 3013, 2927, 2771, 2547, 2260, 1913, 1511, 1062, + 572, 49, -496, -1056, -1621, -2180, -2724, -3243, -3727, -4166, -4552, -4877, -5133, -5314, -5414, -5428, -5355, -5192, -4937, -4593, -4159, -3641, -3042, -2366, -1622, -816, 43, 948, 1889, 2855, 3836, 4823, + 5805, 6772, 7715, 8625, 9493, 10311, 11074, 11774, 12407, 12969, 13457, 13870, 14208, 14471, 14660, 14779, 14831, 14821, 14754, 14635, 14472, 14270, 14038, 13781, 13508, 13226, 12939, 12657, 12383, 12123, 11882, 11662, + 11468, 11302, 11163, 11053, 10971, 10915, 10883, 10873, 10880, 10901, 10930, 10964, 10995, 11021, 11034, 11030, 11003, 10950, 10865, 10746, 10590, 10394, 10156, 9877, 9556, 9194, 8794, 8357, 7889, 7392, 6872, 6334, + 5784, 5229, 4674, 4128, 3596, 3085, 2602, 2152, 1741, 1374, 1056, 789, 576, 420, 320, 277, 290, 356, 473, 636, 840, 1081, 1352, 1647, 1957, 2276, 2596, 2910, 3209, 3487, 3736, 3951, + 4125, 4253, 4330, 4355, 4324, 4235, 4090, 3888, 3633, 3326, 2972, 2576, 2144, 1683, 1201, 705, 204, -292, -776, -1238, -1669, -2061, -2405, -2694, -2922, -3081, -3168, -3177, -3107, -2955, -2721, -2405, + -2010, -1538, -995, -384, 286, 1010, 1780, 2585, 3417, 4266, 5123, 5975, 6815, 7631, 8415, 9156, 9847, 10480, 11048, 11545, 11967, 12309, 12569, 12745, 12838, 12848, 12777, 12628, 12406, 12115, 11762, 11352, + 10893, 10393, 9859, 9300, 8723, 8137, 7549, 6968, 6399, 5850, 5326, 4833, 4373, 3951, 3569, 3229, 2930, 2673, 2456, 2278, 2135, 2024, 1942, 1883, 1843, 1817, 1800, 1787, 1772, 1751, 1719, 1673, + 1609, 1524, 1416, 1283, 1126, 944, 737, 509, 261, -2, -279, -565, -854, -1141, -1421, -1688, -1936, -2160, -2354, -2514, -2634, -2711, -2742, -2722, -2652, -2529, -2354, -2127, -1851, -1528, -1162, -758, + -320, 143, 627, 1123, 1624, 2121, 2606, 3071, 3506, 3905, 4259, 4561, 4805, 4984, 5093, 5129, 5089, 4971, 4773, 4497, 4144, 3717, 3219, 2656, 2033, 1358, 638, -117, -900, -1702, -2511, -3319, + -4115, -4891, -5635, -6340, -6998, -7600, -8141, -8614, -9014, -9339, -9586, -9754, -9842, -9853, -9788, -9651, -9446, -9180, -8859, -8490, -8081, -7641, -7177, -6700, -6219, -5741, -5277, -4834, -4421, -4044, -3710, -3425, + -3194, -3019, -2904, -2850, -2858, -2927, -3055, -3239, -3476, -3761, -4089, -4453, -4847, -5264, -5696, -6136, -6576, -7008, -7426, -7821, -8189, -8522, -8816, -9065, -9268, -9420, -9520, -9567, -9562, -9505, -9398, -9245, + -9049, -8814, -8545, -8248, -7928, -7592, -7246, -6897, -6550, -6212, -5888, -5584, -5304, -5054, -4835, -4652, -4505, -4396, -4326, -4292, -4295, -4332, -4400, -4495, -4613, -4749, -4899, -5057, -5217, -5374, -5522, -5657, + -5772, -5865, -5929, -5963, -5964, -5930, -5859, -5752, -5609, -5432, -5224, -4988, -4728, -4449, -4156, -3857, -3557, -3264, -2985, -2727, -2497, -2302, -2150, -2047, -1998, -2008, -2081, -2222, -2431, -2711, -3062, -3482, + -3970, -4522, -5133, -5799, -6513, -7268, -8055, -8866, -9691, -10520, -11343, -12149, -12929, -13672, -14368, -15009, -15584, -16087, -16509, -16845, -17091, -17241, -17293, -17246, -17100, -16855, -16515, -16082, -15562, -14960, -14284, -13541, + -12739, -11888, -10996, -10075, -9135, -8184, -7234, -6294, -5373, -4480, -3623, -2809, -2045, -1336, -686, -99, 423, 880, 1271, 1598, 1863, 2068, 2218, 2318, 2372, 2388, 2371, 2328, 2265, 2191, 2110, 2031, + 1959, 1900, 1858, 1838, 1845, 1880, 1946, 2044, 2174, 2335, 2526, 2745, 2988, 3252, 3533, 3824, 4123, 4422, 4715, 4999, 5265, 5510, 5728, 5914, 6064, 6174, 6242, 6265, 6242, 6173, 6057, 5897, + 5694, 5450, 5171, 4859, 4521, 4160, 3785, 3399, 3011, 2626, 2251, 1893, 1556, 1247, 971, 731, 533, 378, 268, 205, 188, 218, 291, 406, 558, 743, 957, 1193, 1444, 1704, 1965, 2220, + 2461, 2681, 2872, 3027, 3141, 3206, 3219, 3173, 3066, 2896, 2661, 2360, 1994, 1565, 1076, 532, -63, -704, -1382, -2092, -2824, -3569, -4319, -5064, -5795, -6503, -7178, -7811, -8395, -8923, -9386, -9780, + -10099, -10340, -10501, -10579, -10574, -10488, -10323, -10082, -9770, -9392, -8954, -8465, -7933, -7366, -6773, -6164, -5550, -4939, -4341, -3766, -3223, -2720, -2265, -1865, -1525, -1251, -1047, -915, -858, -875, -966, -1129, + -1361, -1659, -2017, -2430, -2892, -3395, -3933, -4496, -5077, -5668, -6260, -6846, -7417, -7968, -8490, -8979, -9428, -9834, -10193, -10501, -10759, -10963, -11116, -11217, -11269, -11274, -11236, -11158, -11046, -10903, -10735, -10548, + -10346, -10136, -9922, -9709, -9501, -9304, -9119, -8950, -8798, -8665, -8552, -8458, -8382, -8322, -8275, -8239, -8210, -8182, -8153, -8115, -8065, -7997, -7905, -7784, -7631, -7439, -7205, -6926, -6600, -6224, -5797, -5319, + -4791, -4215, -3594, -2931, -2231, -1499, -742, 33, 821, 1611, 2396, 3168, 3917, 4635, 5313, 5944, 6520, 7033, 7478, 7849, 8142, 8352, 8478, 8518, 8472, 8341, 8128, 7835, 7467, 7030, 6531, 5976, + 5375, 4735, 4066, 3378, 2681, 1984, 1298, 631, -5, -604, -1157, -1657, -2098, -2475, -2783, -3019, -3182, -3270, -3286, -3229, -3104, -2913, -2663, -2359, -2008, -1617, -1195, -749, -289, 176, 639, 1091, + 1522, 1926, 2295, 2622, 2902, 3130, 3302, 3415, 3468, 3460, 3391, 3264, 3080, 2843, 2558, 2229, 1864, 1469, 1051, 617, 175, -266, -700, -1119, -1515, -1882, -2212, -2502, -2745, -2938, -3078, -3162, + -3189, -3159, -3073, -2931, -2737, -2494, -2206, -1878, -1514, -1122, -706, -273, 168, 615, 1060, 1496, 1918, 2321, 2701, 3053, 3374, 3663, 3916, 4134, 4317, 4465, 4581, 4666, 4724, 4759, 4775, 4776, + 4767, 4755, 4743, 4738, 4745, 4767, 4811, 4879, 4975, 5101, 5261, 5454, 5680, 5940, 6232, 6553, 6899, 7267, 7652, 8048, 8449, 8847, 9237, 9610, 9960, 10277, 10556, 10789, 10969, 11090, 11146, 11133, + 11047, 10886, 10646, 10328, 9931, 9458, 8911, 8294, 7612, 6870, 6076, 5238, 4364, 3463, 2545, 1620, 698, -208, -1092, -1941, -2747, -3499, -4191, -4813, -5360, -5825, -6205, -6496, -6696, -6803, -6819, -6745, + -6584, -6341, -6019, -5626, -5169, -4655, -4094, -3493, -2864, -2215, -1556, -898, -249, 380, 983, 1551, 2076, 2553, 2975, 3339, 3641, 3879, 4051, 4159, 4202, 4184, 4107, 3975, 3794, 3568, 3304, 3008, + 2688, 2350, 2003, 1652, 1305, 969, 650, 353, 84, -152, -354, -518, -642, -726, -769, -772, -738, -668, -566, -435, -281, -108, 77, 272, 469, 664, 849, 1021, 1174, 1305, 1409, 1483, + 1524, 1532, 1506, 1444, 1349, 1221, 1064, 881, 675, 451, 215, -27, -271, -510, -738, -947, -1133, -1289, -1410, -1491, -1527, -1515, -1452, -1335, -1164, -939, -660, -329, 51, 477, 944, 1447, + 1980, 2536, 3107, 3688, 4269, 4842, 5400, 5935, 6439, 6905, 7327, 7698, 8014, 8269, 8462, 8589, 8649, 8642, 8569, 8432, 8235, 7982, 7677, 7327, 6938, 6519, 6078, 5623, 5163, 4707, 4264, 3844, + 3453, 3102, 2797, 2544, 2351, 2221, 2160, 2169, 2251, 2406, 2633, 2930, 3295, 3722, 4208, 4744, 5325, 5942, 6587, 7250, 7922, 8593, 9254, 9894, 10504, 11075, 11599, 12067, 12472, 12809, 13071, 13254, + 13357, 13375, 13309, 13160, 12928, 12616, 12228, 11769, 11244, 10659, 10023, 9342, 8624, 7878, 7112, 6335, 5555, 4781, 4019, 3278, 2563, 1882, 1238, 636, 80, -427, -885, -1293, -1651, -1960, -2222, -2441, + -2618, -2758, -2866, -2946, -3003, -3042, -3068, -3086, -3100, -3115, -3135, -3163, -3202, -3253, -3318, -3397, -3490, -3596, -3714, -3839, -3971, -4104, -4234, -4356, -4466, -4557, -4625, -4663, -4667, -4632, -4552, -4423, + -4242, -4006, -3712, -3359, -2946, -2474, -1945, -1360, -723, -38, 688, 1452, 2247, 3063, 3895, 4733, 5569, 6394, 7199, 7976, 8716, 9411, 10054, 10637, 11156, 11604, 11977, 12273, 12489, 12625, 12680, 12657, + 12557, 12385, 12146, 11845, 11489, 11086, 10644, 10172, 9679, 9174, 8667, 8168, 7686, 7231, 6809, 6431, 6102, 5829, 5617, 5471, 5393, 5386, 5451, 5587, 5792, 6064, 6398, 6790, 7235, 7724, 8252, 8809, + 9388, 9979, 10573, 11162, 11736, 12286, 12806, 13286, 13720, 14102, 14425, 14687, 14884, 15013, 15073, 15064, 14987, 14845, 14640, 14376, 14059, 13695, 13289, 12848, 12381, 11895, 11397, 10896, 10400, 9915, 9449, 9009, + 8600, 8228, 7897, 7612, 7374, 7186, 7049, 6962, 6924, 6934, 6988, 7084, 7217, 7381, 7572, 7785, 8012, 8248, 8486, 8722, 8948, 9159, 9350, 9518, 9657, 9764, 9838, 9876, 9879, 9845, 9775, 9673, + 9539, 9377, 9190, 8984, 8763, 8532, 8297, 8062, 7835, 7619, 7420, 7244, 7094, 6975, 6889, 6839, 6826, 6851, 6915, 7015, 7151, 7319, 7515, 7735, 7975, 8226, 8485, 8742, 8992, 9227, 9439, 9620, + 9764, 9864, 9914, 9906, 9837, 9702, 9498, 9223, 8874, 8453, 7959, 7396, 6766, 6073, 5324, 4525, 3682, 2804, 1900, 980, 52, -872, -1784, -2672, -3528, -4340, -5101, -5801, -6434, -6991, -7468, -7859, + -8162, -8373, -8492, -8519, -8455, -8304, -8068, -7754, -7366, -6913, -6402, -5841, -5241, -4610, -3958, -3296, -2633, -1980, -1346, -740, -170, 355, 829, 1247, 1602, 1891, 2112, 2261, 2340, 2348, 2287, 2159, + 1969, 1720, 1418, 1069, 678, 253, -198, -670, -1155, -1646, -2135, -2618, -3087, -3538, -3965, -4365, -4735, -5073, -5377, -5647, -5883, -6086, -6259, -6405, -6526, -6628, -6714, -6789, -6860, -6930, -7005, -7092, + -7194, -7316, -7463, -7638, -7845, -8086, -8363, -8676, -9025, -9410, -9829, -10279, -10757, -11259, -11781, -12316, -12860, -13405, -13946, -14476, -14988, -15475, -15932, -16352, -16730, -17061, -17340, -17563, -17729, -17835, -17881, -17866, + -17791, -17659, -17472, -17235, -16952, -16628, -16270, -15884, -15478, -15059, -14634, -14212, -13800, -13406, -13037, -12699, -12399, -12142, -11932, -11773, -11668, -11618, -11624, -11685, -11799, -11964, -12176, -12430, -12720, -13039, -13382, -13738, + -14101, -14461, -14810, -15138, -15437, -15699, -15914, -16076, -16178, -16213, -16176, -16064, -15872, -15600, -15247, -14812, -14298, -13708, -13046, -12316, -11526, -10683, -9794, -8868, -7915, -6944, -5966, -4991, -4028, -3088, -2181, -1316, + -501, 255, 946, 1566, 2109, 2571, 2950, 3244, 3452, 3575, 3615, 3575, 3458, 3270, 3016, 2702, 2336, 1925, 1476, 999, 501, -9, -525, -1038, -1541, -2028, -2493, -2930, -3336, -3706, -4039, -4332, + -4584, -4797, -4971, -5107, -5209, -5279, -5322, -5341, -5342, -5329, -5308, -5283, -5260, -5243, -5237, -5246, -5273, -5322, -5393, -5490, -5612, -5760, -5933, -6128, -6345, -6578, -6826, -7083, -7345, -7606, -7862, -8105, + -8331, -8533, -8707, -8847, -8948, -9006, -9017, -8979, -8890, -8747, -8552, -8304, -8005, -7658, -7266, -6834, -6367, -5870, -5352, -4818, -4276, -3736, -3204, -2690, -2202, -1748, -1335, -971, -663, -417, -238, -129, + -94, -136, -255, -451, -722, -1066, -1479, -1957, -2494, -3082, -3715, -4383, -5079, -5792, -6514, -7233, -7941, -8628, -9283, -9899, -10466, -10978, -11427, -11807, -12114, -12343, -12493, -12561, -12549, -12456, -12284, -12038, + -11722, -11341, -10902, -10412, -9878, -9309, -8713, -8101, -7481, -6861, -6251, -5660, -5095, -4564, -4074, -3629, -3236, -2899, -2619, -2399, -2240, -2141, -2101, -2117, -2186, -2304, -2464, -2662, -2890, -3143, -3412, -3690, + -3969, -4242, -4502, -4741, -4954, -5133, -5275, -5374, -5426, -5430, -5382, -5282, -5131, -4929, -4677, -4380, -4039, -3661, -3249, -2808, -2346, -1868, -1380, -889, -401, 76, 539, 981, 1397, 1784, 2136, 2451, + 2727, 2964, 3159, 3314, 3430, 3508, 3553, 3566, 3552, 3516, 3463, 3397, 3325, 3251, 3182, 3122, 3076, 3050, 3047, 3070, 3122, 3206, 3323, 3472, 3653, 3865, 4105, 4370, 4656, 4958, 5271, 5588, + 5903, 6209, 6498, 6764, 6999, 7196, 7348, 7449, 7493, 7474, 7389, 7234, 7006, 6704, 6328, 5878, 5356, 4766, 4111, 3397, 2630, 1818, 967, 88, -809, -1717, -2625, -3521, -4396, -5239, -6040, -6790, + -7480, -8102, -8647, -9111, -9486, -9770, -9959, -10051, -10047, -9946, -9750, -9464, -9091, -8638, -8110, -7515, -6861, -6158, -5415, -4641, -3846, -3041, -2235, -1438, -660, 91, 807, 1481, 2106, 2677, 3189, 3639, + 4025, 4345, 4600, 4791, 4919, 4987, 5001, 4963, 4881, 4759, 4604, 4423, 4223, 4011, 3793, 3576, 3368, 3172, 2996, 2843, 2717, 2622, 2559, 2531, 2537, 2577, 2650, 2755, 2887, 3044, 3221, 3413, + 3617, 3825, 4033, 4234, 4424, 4597, 4747, 4869, 4961, 5017, 5035, 5013, 4948, 4842, 4693, 4503, 4273, 4008, 3709, 3382, 3031, 2662, 2280, 1892, 1504, 1123, 755, 406, 82, -210, -466, -682, + -853, -978, -1052, -1076, -1048, -970, -844, -671, -455, -201, 85, 400, 735, 1085, 1442, 1798, 2145, 2477, 2785, 3063, 3303, 3501, 3649, 3744, 3782, 3761, 3677, 3531, 3323, 3055, 2729, 2349, + 1920, 1447, 937, 397, -163, -737, -1316, -1890, -2449, -2985, -3488, -3950, -4363, -4719, -5011, -5234, -5381, -5451, -5439, -5344, -5167, -4907, -4567, -4150, -3661, -3105, -2489, -1819, -1105, -356, 420, 1214, + 2014, 2812, 3597, 4360, 5091, 5782, 6426, 7013, 7540, 7999, 8388, 8702, 8939, 9100, 9184, 9192, 9128, 8994, 8796, 8538, 8228, 7871, 7475, 7048, 6598, 6132, 5660, 5188, 4725, 4278, 3853, 3457, + 3095, 2771, 2488, 2251, 2060, 1917, 1822, 1773, 1768, 1807, 1884, 1996, 2139, 2309, 2498, 2704, 2919, 3138, 3358, 3571, 3774, 3963, 4135, 4285, 4413, 4517, 4596, 4650, 4680, 4689, 4678, 4650, + 4609, 4560, 4508, 4456, 4411, 4378, 4361, 4368, 4401, 4467, 4568, 4709, 4893, 5121, 5395, 5714, 6080, 6489, 6939, 7428, 7950, 8501, 9074, 9663, 10261, 10860, 11451, 12026, 12577, 13095, 13572, 13999, + 14369, 14675, 14911, 15070, 15149, 15144, 15052, 14872, 14603, 14247, 13806, 13282, 12681, 12008, 11269, 10472, 9625, 8736, 7815, 6873, 5918, 4961, 4012, 3081, 2176, 1308, 484, -287, -1001, -1651, -2233, -2743, + -3179, -3541, -3827, -4041, -4183, -4259, -4272, -4228, -4134, -3998, -3825, -3626, -3408, -3181, -2952, -2730, -2525, -2342, -2191, -2077, -2005, -1982, -2011, -2094, -2235, -2432, -2687, -2997, -3360, -3773, -4231, -4728, + -5260, -5820, -6399, -6991, -7588, -8182, -8765, -9329, -9867, -10373, -10839, -11260, -11630, -11946, -12204, -12402, -12538, -12612, -12623, -12574, -12467, -12305, -12092, -11832, -11531, -11195, -10828, -10438, -10032, -9615, -9193, -8774, + -8362, -7964, -7582, -7223, -6888, -6581, -6304, -6056, -5839, -5652, -5494, -5361, -5251, -5160, -5085, -5021, -4963, -4905, -4843, -4771, -4684, -4578, -4448, -4289, -4100, -3876, -3616, -3319, -2984, -2612, -2205, -1765, + -1294, -799, -282, 249, 790, 1333, 1872, 2398, 2903, 3382, 3825, 4225, 4577, 4872, 5106, 5273, 5369, 5392, 5338, 5207, 4999, 4715, 4358, 3930, 3437, 2884, 2278, 1626, 936, 219, -517, -1262, + -2007, -2739, -3450, -4129, -4767, -5354, -5883, -6344, -6733, -7042, -7267, -7405, -7454, -7412, -7279, -7059, -6752, -6363, -5897, -5360, -4760, -4104, -3400, -2658, -1888, -1098, -299, 498, 1286, 2054, 2793, 3496, + 4156, 4764, 5317, 5809, 6236, 6596, 6889, 7113, 7269, 7360, 7388, 7357, 7272, 7138, 6961, 6748, 6506, 6241, 5962, 5674, 5386, 5103, 4833, 4580, 4351, 4149, 3979, 3843, 3742, 3679, 3653, 3663, + 3709, 3787, 3895, 4028, 4183, 4355, 4538, 4727, 4916, 5101, 5275, 5433, 5570, 5683, 5767, 5819, 5836, 5817, 5761, 5668, 5537, 5372, 5175, 4948, 4695, 4421, 4132, 3833, 3529, 3227, 2934, 2655, + 2398, 2167, 1969, 1809, 1691, 1619, 1597, 1626, 1709, 1845, 2033, 2274, 2563, 2898, 3275, 3688, 4131, 4599, 5083, 5577, 6073, 6563, 7039, 7494, 7919, 8307, 8653, 8949, 9191, 9375, 9496, 9552, + 9542, 9466, 9325, 9120, 8854, 8532, 8158, 7739, 7281, 6792, 6281, 5754, 5223, 4694, 4179, 3685, 3221, 2796, 2417, 2091, 1825, 1623, 1490, 1428, 1441, 1528, 1689, 1922, 2226, 2594, 3023, 3507, + 4038, 4608, 5210, 5833, 6469, 7108, 7739, 8354, 8942, 9495, 10003, 10459, 10855, 11185, 11443, 11625, 11727, 11748, 11686, 11541, 11314, 11009, 10628, 10175, 9657, 9080, 8450, 7775, 7062, 6321, 5559, 4786, + 4008, 3235, 2474, 1733, 1018, 335, -309, -913, -1471, -1981, -2441, -2851, -3211, -3522, -3786, -4004, -4182, -4321, -4428, -4506, -4560, -4596, -4619, -4633, -4643, -4655, -4671, -4695, -4730, -4778, -4841, -4920, + -5013, -5122, -5243, -5374, -5513, -5656, -5799, -5936, -6063, -6175, -6266, -6331, -6364, -6361, -6316, -6225, -6084, -5891, -5642, -5337, -4975, -4555, -4080, -3551, -2972, -2346, -1679, -976, -245, 507, 1274, 2047, + 2817, 3575, 4312, 5020, 5690, 6315, 6886, 7396, 7840, 8212, 8507, 8723, 8857, 8908, 8876, 8762, 8569, 8301, 7961, 7557, 7094, 6579, 6022, 5430, 4813, 4181, 3543, 2909, 2287, 1689, 1121, 592, + 110, -318, -689, -996, -1237, -1409, -1510, -1542, -1506, -1403, -1238, -1015, -739, -418, -58, 331, 744, 1171, 1602, 2029, 2443, 2835, 3197, 3522, 3802, 4031, 4204, 4316, 4363, 4344, 4257, 4102, + 3880, 3592, 3243, 2836, 2375, 1867, 1318, 735, 126, -502, -1141, -1783, -2420, -3044, -3648, -4225, -4769, -5275, -5737, -6151, -6515, -6827, -7085, -7288, -7439, -7539, -7589, -7593, -7556, -7482, -7377, -7244, + -7092, -6925, -6750, -6573, -6399, -6234, -6084, -5952, -5842, -5759, -5704, -5679, -5685, -5722, -5790, -5886, -6009, -6155, -6320, -6501, -6693, -6889, -7086, -7277, -7457, -7621, -7762, -7876, -7960, -8007, -8016, -7984, + -7910, -7791, -7628, -7422, -7175, -6889, -6569, -6218, -5841, -5444, -5035, -4618, -4203, -3796, -3405, -3036, -2699, -2399, -2144, -1938, -1788, -1699, -1672, -1712, -1820, -1996, -2239, -2548, -2920, -3351, -3835, -4366, + -4938, -5542, -6171, -6814, -7462, -8106, -8734, -9338, -9908, -10433, -10904, -11314, -11654, -11918, -12100, -12194, -12198, -12108, -11924, -11646, -11275, -10813, -10265, -9635, -8929, -8155, -7321, -6434, -5504, -4542, -3556, -2557, + -1556, -561, 415, 1365, 2281, 3153, 3975, 4741, 5444, 6081, 6648, 7144, 7568, 7918, 8198, 8409, 8554, 8638, 8665, 8642, 8573, 8466, 8327, 8165, 7984, 7794, 7600, 7408, 7225, 7057, 6906, 6778, + 6676, 6602, 6557, 6542, 6556, 6599, 6668, 6760, 6872, 7001, 7140, 7286, 7433, 7576, 7709, 7827, 7925, 7998, 8041, 8051, 8024, 7958, 7850, 7701, 7508, 7274, 7000, 6688, 6340, 5962, 5557, 5131, + 4690, 4239, 3785, 3334, 2894, 2470, 2070, 1699, 1363, 1067, 815, 612, 460, 361, 316, 326, 390, 505, 670, 880, 1131, 1418, 1735, 2074, 2430, 2795, 3161, 3520, 3865, 4188, 4483, 4742, + 4959, 5129, 5248, 5311, 5316, 5262, 5146, 4971, 4738, 4449, 4108, 3720, 3290, 2825, 2333, 1820, 1296, 770, 250, -254, -735, -1183, -1589, -1946, -2247, -2484, -2654, -2751, -2771, -2714, -2576, -2360, + -2065, -1695, -1254, -745, -176, 446, 1115, 1822, 2558, 3313, 4076, 4839, 5591, 6322, 7022, 7682, 8294, 8849, 9341, 9763, 10110, 10378, 10565, 10667, 10685, 10620, 10472, 10244, 9941, 9568, 9129, 8631, + 8081, 7487, 6857, 6198, 5520, 4829, 4135, 3445, 2765, 2104, 1467, 859, 286, -249, -744, -1197, -1606, -1972, -2295, -2577, -2820, -3027, -3203, -3350, -3474, -3580, -3673, -3758, -3839, -3922, -4010, -4109, + -4222, -4352, -4501, -4670, -4861, -5074, -5308, -5562, -5833, -6118, -6414, -6716, -7021, -7322, -7614, -7892, -8149, -8381, -8582, -8746, -8868, -8945, -8973, -8948, -8868, -8733, -8541, -8293, -7991, -7637, -7235, -6788, + -6302, -5783, -5237, -4673, -4096, -3517, -2942, -2381, -1841, -1332, -861, -435, -62, 251, 501, 683, 793, 828, 788, 672, 481, 218, -113, -509, -965, -1474, -2027, -2618, -3237, -3875, -4523, -5171, + -5810, -6428, -7018, -7570, -8076, -8527, -8918, -9242, -9494, -9670, -9767, -9785, -9722, -9580, -9360, -9066, -8702, -8274, -7788, -7251, -6670, -6054, -5413, -4754, -4087, -3422, -2767, -2130, -1521, -947, -414, 70, + 502, 877, 1192, 1445, 1634, 1760, 1825, 1829, 1778, 1674, 1523, 1330, 1102, 846, 569, 279, -17, -312, -598, -868, -1116, -1335, -1520, -1666, -1770, -1828, -1839, -1801, -1714, -1579, -1398, -1172, + -907, -605, -272, 87, 467, 861, 1263, 1667, 2066, 2454, 2825, 3174, 3495, 3784, 4037, 4252, 4425, 4557, 4645, 4690, 4694, 4658, 4585, 4478, 4341, 4180, 3998, 3801, 3595, 3384, 3176, 2974, + 2785, 2613, 2462, 2335, 2237, 2168, 2131, 2126, 2153, 2210, 2295, 2406, 2537, 2686, 2846, 3012, 3177, 3334, 3477, 3597, 3689, 3744, 3756, 3719, 3626, 3473, 3255, 2968, 2610, 2179, 1675, 1098, + 450, -265, -1045, -1884, -2775, -3712, -4686, -5690, -6713, -7745, -8777, -9798, -10797, -11765, -12690, -13564, -14377, -15121, -15788, -16372, -16866, -17266, -17570, -17775, -17880, -17885, -17794, -17607, -17331, -16970, -16530, -16019, + -15444, -14815, -14141, -13432, -12697, -11947, -11191, -10440, -9702, -8986, -8301, -7654, -7052, -6500, -6003, -5564, -5187, -4872, -4620, -4429, -4299, -4226, -4206, -4236, -4309, -4420, -4562, -4729, -4915, -5111, -5312, -5510, + -5699, -5874, -6029, -6159, -6260, -6329, -6363, -6362, -6324, -6250, -6141, -5999, -5826, -5627, -5404, -5164, -4911, -4650, -4387, -4128, -3879, -3645, -3431, -3243, -3084, -2960, -2872, -2823, -2816, -2851, -2927, -3045, + -3201, -3395, -3623, -3879, -4161, -4462, -4777, -5100, -5425, -5744, -6053, -6344, -6611, -6849, -7052, -7217, -7339, -7414, -7442, -7420, -7349, -7227, -7058, -6844, -6587, -6293, -5965, -5611, -5236, -4847, -4451, -4057, + -3671, -3301, -2955, -2641, -2364, -2131, -1948, -1819, -1749, -1741, -1796, -1915, -2099, -2346, -2653, -3018, -3435, -3899, -4403, -4941, -5505, -6085, -6674, -7262, -7840, -8398, -8928, -9420, -9867, -10260, -10593, -10859, + -11054, -11173, -11213, -11173, -11052, -10850, -10569, -10212, -9784, -9290, -8736, -8128, -7476, -6788, -6073, -5340, -4599, -3860, -3134, -2429, -1755, -1120, -533, 0, 470, 875, 1209, 1468, 1652, 1757, 1786, 1738, + 1617, 1426, 1169, 852, 481, 63, -395, -886, -1400, -1931, -2470, -3009, -3539, -4054, -4547, -5011, -5441, -5832, -6180, -6482, -6736, -6941, -7097, -7203, -7262, -7276, -7248, -7182, -7082, -6952, -6797, -6624, + -6437, -6241, -6042, -5844, -5653, -5472, -5304, -5154, -5022, -4911, -4821, -4752, -4704, -4674, -4661, -4660, -4670, -4684, -4700, -4711, -4712, -4699, -4665, -4605, -4515, -4389, -4224, -4015, -3760, -3456, -3102, -2698, + -2243, -1739, -1189, -595, 37, 704, 1400, 2117, 2849, 3588, 4326, 5055, 5765, 6449, 7097, 7702, 8256, 8752, 9183, 9544, 9829, 10034, 10158, 10198, 10153, 10025, 9815, 9526, 9163, 8730, 8234, 7682, + 7082, 6442, 5772, 5081, 4380, 3677, 2984, 2310, 1664, 1055, 491, -18, -469, -855, -1171, -1413, -1580, -1669, -1682, -1620, -1484, -1280, -1010, -681, -299, 127, 592, 1087, 1602, 2130, 2661, 3187, + 3698, 4188, 4648, 5072, 5454, 5788, 6070, 6297, 6467, 6578, 6631, 6627, 6567, 6456, 6296, 6094, 5854, 5584, 5290, 4979, 4659, 4338, 4023, 3723, 3444, 3193, 2978, 2803, 2673, 2594, 2568, 2598, + 2686, 2832, 3036, 3296, 3610, 3975, 4388, 4843, 5335, 5860, 6410, 6980, 7562, 8151, 8739, 9321, 9891, 10444, 10973, 11477, 11949, 12389, 12793, 13161, 13492, 13787, 14047, 14274, 14471, 14642, 14789, 14919, + 15035, 15143, 15247, 15354, 15467, 15593, 15735, 15898, 16085, 16299, 16542, 16815, 17119, 17454, 17818, 18208, 18623, 19058, 19508, 19968, 20431, 20892, 21343, 21777, 22186, 22562, 22899, 23188, 23423, 23598, 23706, 23742, + 23703, 23584, 23384, 23102, 22737, 22290, 21763, 21161, 20486, 19746, 18946, 18094, 17199, 16268, 15312, 14341, 13365, 12395, 11440, 10511, 9617, 8769, 7974, 7242, 6578, 5989, 5480, 5055, 4716, 4465, 4302, 4225, + 4233, 4322, 4487, 4722, 5021, 5376, 5778, 6219, 6690, 7181, 7682, 8184, 8676, 9151, 9598, 10011, 10381, 10701, 10967, 11174, 11317, 11394, 11405, 11348, 11224, 11035, 10785, 10476, 10113, 9703, 9250, 8762, + 8245, 7707, 7154, 6595, 6037, 5485, 4948, 4430, 3937, 3474, 3046, 2654, 2301, 1989, 1717, 1486, 1295, 1140, 1020, 930, 867, 825, 800, 787, 780, 773, 762, 740, 703, 647, 568, 462, + 326, 160, -38, -269, -532, -825, -1147, -1493, -1861, -2246, -2642, -3045, -3448, -3845, -4229, -4596, -4937, -5248, -5522, -5754, -5939, -6075, -6156, -6181, -6148, -6057, -5908, -5702, -5442, -5131, -4773, -4373, + -3936, -3470, -2980, -2475, -1963, -1450, -946, -457, 6, 440, 835, 1187, 1489, 1736, 1926, 2055, 2121, 2125, 2065, 1946, 1767, 1535, 1253, 927, 564, 170, -244, -673, -1107, -1537, -1954, -2349, + -2713, -3039, -3318, -3544, -3710, -3811, -3843, -3802, -3687, -3496, -3229, -2889, -2478, -2000, -1460, -864, -218, 468, 1188, 1931, 2689, 3452, 4209, 4951, 5668, 6350, 6990, 7577, 8106, 8569, 8960, 9275, + 9509, 9662, 9730, 9714, 9615, 9434, 9176, 8845, 8445, 7982, 7463, 6897, 6289, 5649, 4985, 4305, 3618, 2931, 2253, 1591, 952, 342, -234, -771, -1266, -1716, -2120, -2475, -2782, -3042, -3258, -3430, + -3563, -3660, -3725, -3763, -3780, -3779, -3767, -3748, -3726, -3707, -3695, -3692, -3703, -3728, -3771, -3831, -3910, -4005, -4115, -4240, -4374, -4515, -4659, -4801, -4936, -5059, -5163, -5245, -5298, -5317, -5298, -5235, + -5125, -4964, -4750, -4482, -4157, -3777, -3342, -2855, -2317, -1733, -1109, -448, 241, 953, 1679, 2412, 3143, 3864, 4565, 5238, 5874, 6465, 7005, 7484, 7898, 8241, 8509, 8697, 8803, 8827, 8768, 8628, + 8408, 8111, 7744, 7310, 6818, 6273, 5684, 5060, 4410, 3743, 3070, 2399, 1741, 1104, 498, -68, -589, -1057, -1466, -1811, -2088, -2296, -2432, -2496, -2491, -2416, -2277, -2077, -1822, -1518, -1172, -793, + -387, 35, 467, 897, 1319, 1722, 2099, 2441, 2743, 2996, 3196, 3338, 3418, 3434, 3384, 3267, 3085, 2840, 2534, 2170, 1754, 1292, 789, 252, -310, -892, -1484, -2079, -2669, -3246, -3803, -4333, + -4830, -5287, -5701, -6067, -6381, -6642, -6848, -6999, -7096, -7140, -7134, -7080, -6983, -6847, -6677, -6480, -6261, -6025, -5780, -5531, -5284, -5045, -4818, -4610, -4424, -4263, -4130, -4027, -3956, -3916, -3907, -3928, + -3976, -4049, -4143, -4254, -4378, -4509, -4642, -4772, -4894, -5001, -5088, -5151, -5185, -5186, -5151, -5077, -4962, -4805, -4607, -4367, -4087, -3771, -3421, -3042, -2639, -2217, -1783, -1344, -907, -480, -70, 315, + 669, 983, 1251, 1468, 1626, 1723, 1753, 1715, 1605, 1424, 1172, 851, 462, 11, -497, -1059, -1665, -2310, -2983, -3677, -4381, -5086, -5782, -6458, -7106, -7714, -8275, -8778, -9217, -9585, -9875, -10081, + -10202, -10233, -10173, -10021, -9780, -9452, -9039, -8546, -7981, -7348, -6656, -5913, -5128, -4312, -3473, -2622, -1770, -925, -98, 700, 1464, 2184, 2852, 3462, 4009, 4488, 4896, 5231, 5491, 5677, 5790, 5832, + 5806, 5716, 5567, 5364, 5115, 4824, 4500, 4149, 3778, 3395, 3007, 2619, 2239, 1871, 1521, 1192, 889, 613, 367, 151, -34, -191, -321, -425, -508, -574, -626, -669, -708, -750, -798, -859, + -937, -1037, -1164, -1321, -1513, -1741, -2007, -2313, -2659, -3044, -3467, -3926, -4417, -4937, -5481, -6044, -6620, -7204, -7788, -8367, -8933, -9479, -10000, -10488, -10939, -11346, -11706, -12013, -12266, -12461, -12598, -12675, + -12694, -12656, -12564, -12421, -12231, -11999, -11732, -11435, -11115, -10781, -10438, -10096, -9761, -9442, -9145, -8877, -8645, -8453, -8307, -8211, -8167, -8177, -8243, -8364, -8539, -8765, -9038, -9355, -9709, -10095, -10505, -10932, + -11367, -11802, -12227, -12634, -13014, -13358, -13657, -13904, -14092, -14214, -14264, -14237, -14132, -13943, -13672, -13317, -12880, -12363, -11769, -11105, -10374, -9585, -8745, -7862, -6945, -6005, -5050, -4092, -3139, -2204, -1294, -420, + 408, 1185, 1901, 2551, 3129, 3631, 4052, 4391, 4648, 4821, 4914, 4927, 4864, 4730, 4531, 4272, 3960, 3602, 3207, 2781, 2334, 1873, 1407, 942, 486, 46, -370, -761, -1119, -1442, -1726, -1969, + -2170, -2330, -2448, -2527, -2569, -2577, -2554, -2505, -2435, -2348, -2249, -2144, -2038, -1936, -1842, -1761, -1696, -1651, -1628, -1630, -1656, -1709, -1786, -1888, -2011, -2154, -2312, -2482, -2660, -2839, -3015, -3182, + -3335, -3468, -3575, -3652, -3693, -3693, -3650, -3560, -3421, -3231, -2989, -2697, -2354, -1964, -1530, -1056, -547, -8, 552, 1129, 1714, 2299, 2876, 3436, 3971, 4473, 4933, 5343, 5698, 5991, 6216, 6368, + 6444, 6441, 6359, 6196, 5953, 5634, 5240, 4776, 4248, 3662, 3024, 2345, 1631, 892, 138, -621, -1377, -2118, -2836, -3520, -4164, -4758, -5295, -5769, -6175, -6509, -6767, -6947, -7050, -7075, -7024, -6901, + -6708, -6452, -6138, -5773, -5364, -4920, -4450, -3961, -3464, -2967, -2479, -2008, -1564, -1153, -783, -459, -188, 27, 184, 279, 311, 281, 189, 39, -167, -425, -731, -1077, -1458, -1868, -2297, -2740, + -3189, -3635, -4071, -4490, -4886, -5251, -5581, -5870, -6114, -6310, -6456, -6549, -6591, -6581, -6520, -6412, -6259, -6064, -5834, -5572, -5284, -4977, -4656, -4329, -4000, -3677, -3366, -3071, -2798, -2552, -2336, -2154, + -2008, -1899, -1828, -1795, -1799, -1838, -1911, -2012, -2139, -2287, -2452, -2627, -2808, -2989, -3164, -3328, -3476, -3603, -3704, -3777, -3817, -3822, -3791, -3723, -3619, -3479, -3305, -3101, -2870, -2616, -2345, -2063, + -1775, -1490, -1213, -952, -715, -508, -339, -214, -139, -120, -161, -266, -438, -680, -990, -1371, -1819, -2333, -2908, -3540, -4223, -4950, -5714, -6505, -7315, -8134, -8953, -9760, -10545, -11298, -12010, -12669, + -13268, -13798, -14251, -14619, -14899, -15084, -15172, -15160, -15048, -14835, -14524, -14117, -13619, -13034, -12369, -11632, -10829, -9971, -9066, -8124, -7155, -6169, -5176, -4187, -3210, -2255, -1330, -443, 398, 1189, 1925, 2601, + 3214, 3764, 4248, 4669, 5027, 5324, 5566, 5754, 5896, 5996, 6060, 6095, 6108, 6106, 6095, 6082, 6074, 6076, 6093, 6131, 6194, 6285, 6407, 6560, 6747, 6966, 7216, 7497, 7804, 8136, 8487, 8853, + 9230, 9611, 9990, 10364, 10724, 11067, 11386, 11676, 11934, 12154, 12334, 12470, 12562, 12607, 12607, 12561, 12471, 12340, 12170, 11966, 11732, 11473, 11196, 10905, 10607, 10309, 10017, 9738, 9477, 9241, 9034, 8861, + 8727, 8635, 8587, 8585, 8629, 8719, 8854, 9032, 9249, 9502, 9786, 10095, 10422, 10762, 11107, 11450, 11783, 12099, 12389, 12648, 12867, 13041, 13163, 13230, 13237, 13181, 13059, 12871, 12617, 12297, 11915, 11473, + 10976, 10430, 9839, 9213, 8558, 7883, 7197, 6509, 5829, 5165, 4527, 3923, 3364, 2855, 2405, 2020, 1705, 1464, 1302, 1219, 1218, 1297, 1456, 1691, 1998, 2373, 2810, 3301, 3838, 4415, 5020, 5645, + 6281, 6917, 7543, 8151, 8730, 9272, 9769, 10213, 10598, 10918, 11169, 11347, 11450, 11477, 11427, 11302, 11104, 10836, 10502, 10108, 9660, 9165, 8628, 8059, 7466, 6856, 6237, 5619, 5007, 4411, 3837, 3292, + 2781, 2308, 1879, 1497, 1163, 879, 645, 462, 327, 238, 193, 188, 219, 281, 368, 477, 601, 735, 874, 1013, 1147, 1272, 1384, 1480, 1558, 1615, 1652, 1667, 1662, 1638, 1597, 1541, + 1475, 1403, 1329, 1257, 1194, 1144, 1112, 1105, 1127, 1182, 1276, 1411, 1592, 1819, 2095, 2421, 2796, 3219, 3688, 4199, 4749, 5333, 5945, 6579, 7227, 7882, 8536, 9180, 9805, 10404, 10967, 11486, + 11954, 12363, 12706, 12977, 13172, 13285, 13315, 13258, 13115, 12886, 12572, 12175, 11701, 11154, 10539, 9865, 9138, 8367, 7561, 6730, 5883, 5031, 4183, 3350, 2540, 1763, 1027, 340, -290, -860, -1364, -1797, + -2158, -2445, -2657, -2797, -2865, -2866, -2804, -2683, -2511, -2295, -2041, -1759, -1456, -1142, -825, -514, -217, 56, 300, 507, 671, 787, 849, 855, 803, 691, 519, 288, 0, -341, -733, -1171, + -1647, -2157, -2693, -3247, -3813, -4382, -4947, -5499, -6032, -6538, -7010, -7443, -7831, -8169, -8454, -8683, -8854, -8967, -9021, -9017, -8958, -8845, -8684, -8477, -8231, -7950, -7641, -7309, -6961, -6604, -6243, -5885, + -5536, -5202, -4886, -4594, -4329, -4093, -3889, -3719, -3582, -3478, -3406, -3364, -3349, -3357, -3386, -3429, -3483, -3542, -3601, -3655, -3698, -3726, -3733, -3716, -3671, -3594, -3484, -3339, -3158, -2941, -2689, -2404, + -2090, -1749, -1386, -1007, -618, -224, 166, 548, 911, 1250, 1556, 1822, 2042, 2208, 2314, 2356, 2330, 2230, 2056, 1806, 1479, 1076, 599, 51, -561, -1236, -1966, -2743, -3560, -4406, -5274, -6154, + -7034, -7904, -8755, -9577, -10358, -11090, -11765, -12373, -12908, -13363, -13734, -14016, -14206, -14304, -14308, -14219, -14041, -13776, -13428, -13004, -12510, -11954, -11343, -10688, -9996, -9278, -8543, -7802, -7064, -6338, -5633, -4958, + -4321, -3728, -3185, -2698, -2269, -1903, -1601, -1364, -1191, -1080, -1030, -1036, -1095, -1200, -1348, -1530, -1741, -1973, -2219, -2473, -2726, -2972, -3205, -3418, -3607, -3766, -3892, -3982, -4032, -4043, -4013, -3942, + -3833, -3687, -3507, -3297, -3061, -2803, -2528, -2243, -1952, -1662, -1377, -1104, -847, -610, -399, -217, -66, 50, 132, 179, 190, 168, 113, 29, -80, -211, -358, -518, -682, -847, -1004, -1149, + -1274, -1375, -1444, -1477, -1469, -1417, -1316, -1164, -960, -702, -392, -29, 382, 840, 1341, 1880, 2450, 3047, 3662, 4289, 4919, 5546, 6160, 6754, 7320, 7850, 8339, 8779, 9164, 9491, 9754, 9952, + 10082, 10143, 10136, 10062, 9924, 9724, 9469, 9164, 8814, 8427, 8012, 7575, 7127, 6676, 6231, 5801, 5394, 5020, 4687, 4400, 4167, 3994, 3885, 3843, 3872, 3971, 4142, 4382, 4690, 5062, 5494, 5979, + 6511, 7082, 7685, 8309, 8947, 9589, 10223, 10842, 11435, 11992, 12506, 12967, 13368, 13702, 13963, 14147, 14250, 14269, 14202, 14050, 13813, 13493, 13094, 12619, 12073, 11463, 10796, 10077, 9317, 8522, 7700, 6862, + 6015, 5167, 4327, 3501, 2698, 1923, 1182, 480, -178, -792, -1357, -1874, -2340, -2758, -3129, -3454, -3738, -3982, -4192, -4372, -4526, -4660, -4779, -4887, -4989, -5091, -5195, -5305, -5425, -5557, -5702, -5861, + -6035, -6223, -6423, -6633, -6851, -7073, -7295, -7511, -7719, -7911, -8082, -8228, -8342, -8419, -8455, -8443, -8382, -8266, -8094, -7862, -7571, -7220, -6810, -6343, -5821, -5248, -4628, -3968, -3272, -2549, -1806, -1051, + -292, 462, 1202, 1920, 2607, 3255, 3856, 4402, 4888, 5307, 5656, 5929, 6125, 6241, 6278, 6237, 6119, 5928, 5667, 5342, 4960, 4527, 4051, 3542, 3007, 2456, 1899, 1346, 805, 287, -199, -647, + -1047, -1394, -1681, -1903, -2057, -2140, -2149, -2086, -1951, -1745, -1473, -1138, -746, -303, 184, 707, 1259, 1830, 2411, 2994, 3569, 4128, 4662, 5163, 5624, 6038, 6399, 6702, 6943, 7119, 7230, 7272, + 7249, 7159, 7007, 6796, 6530, 6215, 5856, 5461, 5036, 4589, 4127, 3660, 3194, 2737, 2297, 1880, 1492, 1141, 830, 563, 345, 177, 61, -2, -15, 22, 109, 240, 412, 620, 861, 1127, + 1413, 1713, 2021, 2330, 2636, 2931, 3210, 3470, 3705, 3911, 4087, 4229, 4337, 4409, 4448, 4452, 4425, 4370, 4289, 4186, 4067, 3936, 3798, 3659, 3525, 3400, 3291, 3202, 3137, 3102, 3099, 3132, + 3202, 3311, 3458, 3644, 3867, 4125, 4415, 4731, 5070, 5426, 5793, 6164, 6532, 6889, 7229, 7543, 7825, 8066, 8261, 8402, 8486, 8506, 8458, 8341, 8152, 7889, 7554, 7147, 6672, 6132, 5532, 4877, + 4174, 3431, 2657, 1860, 1049, 236, -570, -1360, -2123, -2850, -3530, -4155, -4717, -5209, -5624, -5957, -6204, -6361, -6427, -6402, -6285, -6080, -5789, -5417, -4968, -4451, -3872, -3239, -2562, -1849, -1111, -358, + 400, 1154, 1894, 2610, 3293, 3936, 4530, 5069, 5548, 5962, 6307, 6581, 6783, 6912, 6970, 6959, 6881, 6740, 6542, 6291, 5994, 5657, 5287, 4892, 4477, 4052, 3621, 3193, 2773, 2367, 1980, 1616, + 1279, 972, 696, 453, 242, 62, -86, -208, -305, -381, -441, -489, -530, -570, -614, -667, -735, -821, -932, -1071, -1241, -1446, -1687, -1966, -2283, -2638, -3029, -3456, -3914, -4400, -4910, -5439, + -5981, -6531, -7081, -7626, -8159, -8673, -9162, -9619, -10038, -10415, -10744, -11022, -11245, -11411, -11518, -11566, -11555, -11487, -11363, -11189, -10967, -10702, -10401, -10070, -9715, -9344, -8965, -8585, -8212, -7854, -7517, -7210, + -6937, -6706, -6521, -6386, -6305, -6279, -6309, -6397, -6540, -6737, -6984, -7276, -7610, -7978, -8374, -8790, -9219, -9650, -10077, -10489, -10878, -11234, -11551, -11819, -12031, -12181, -12263, -12272, -12205, -12058, -11830, -11522, + -11133, -10667, -10127, -9517, -8842, -8110, -7328, -6504, -5648, -4768, -3875, -2979, -2090, -1217, -372, 436, 1199, 1908, 2557, 3137, 3644, 4072, 4419, 4681, 4859, 4951, 4959, 4885, 4732, 4505, 4209, 3850, + 3435, 2971, 2466, 1927, 1363, 782, 192, -398, -983, -1555, -2107, -2634, -3131, -3594, -4020, -4407, -4753, -5057, -5322, -5546, -5734, -5887, -6010, -6106, -6179, -6235, -6278, -6314, -6347, -6383, -6426, -6480, + -6549, -6637, -6746, -6878, -7035, -7215, -7420, -7648, -7896, -8163, -8445, -8738, -9037, -9337, -9633, -9919, -10190, -10440, -10663, -10854, -11007, -11118, -11184, -11200, -11164, -11075, -10932, -10734, -10483, -10181, -9830, -9435, + -9000, -8531, -8034, -7516, -6985, -6448, -5914, -5391, -4888, -4412, -3972, -3576, -3230, -2940, -2713, -2554, -2465, -2450, -2511, -2647, -2859, -3144, -3499, -3920, -4402, -4939, -5523, -6147, -6803, -7480, -8170, -8863, + -9549, -10218, -10860, -11467, -12029, -12539, -12989, -13372, -13684, -13919, -14075, -14150, -14142, -14052, -13882, -13633, -13311, -12920, -12466, -11955, -11396, -10795, -10163, -9506, -8836, -8161, -7489, -6830, -6193, -5584, -5011, -4480, + -3998, -3568, -3194, -2878, -2623, -2427, -2291, -2212, -2188, -2215, -2288, -2401, -2549, -2724, -2921, -3130, -3345, -3559, -3763, -3951, -4116, -4251, -4351, -4412, -4428, -4396, -4315, -4182, -3998, -3762, -3476, -3142, + -2764, -2345, -1890, -1403, -892, -361, 181, 731, 1281, 1825, 2356, 2869, 3359, 3820, 4249, 4642, 4997, 5311, 5584, 5815, 6006, 6157, 6272, 6352, 6403, 6427, 6430, 6416, 6392, 6362, 6332, 6308, + 6294, 6295, 6317, 6361, 6433, 6533, 6664, 6827, 7021, 7245, 7498, 7777, 8078, 8397, 8728, 9066, 9405, 9737, 10056, 10354, 10625, 10860, 11053, 11197, 11286, 11315, 11277, 11171, 10991, 10737, 10407, 10001, + 9521, 8968, 8347, 7662, 6919, 6125, 5286, 4412, 3511, 2593, 1668, 747, -159, -1043, -1892, -2697, -3448, -4137, -4756, -5298, -5757, -6128, -6407, -6592, -6681, -6675, -6574, -6382, -6103, -5740, -5300, -4791, + -4219, -3593, -2923, -2218, -1488, -743, 7, 753, 1485, 2193, 2870, 3507, 4098, 4637, 5118, 5538, 5894, 6183, 6406, 6563, 6655, 6684, 6654, 6570, 6437, 6259, 6044, 5798, 5527, 5239, 4941, 4640, + 4341, 4052, 3778, 3524, 3294, 3092, 2921, 2783, 2678, 2608, 2571, 2565, 2589, 2638, 2711, 2801, 2905, 3016, 3130, 3241, 3343, 3431, 3499, 3542, 3556, 3537, 3482, 3387, 3251, 3073, 2852, 2590, + 2287, 1946, 1570, 1164, 730, 275, -194, -674, -1158, -1638, -2109, -2564, -2997, -3401, -3772, -4104, -4393, -4636, -4830, -4973, -5064, -5103, -5092, -5033, -4927, -4780, -4596, -4379, -4137, -3876, -3602, -3324, + -3048, -2783, -2535, -2313, -2123, -1972, -1865, -1807, -1803, -1856, -1969, -2143, -2377, -2671, -3024, -3431, -3889, -4393, -4936, -5512, -6113, -6731, -7356, -7980, -8594, -9189, -9754, -10282, -10763, -11190, -11555, -11853, + -12076, -12222, -12286, -12267, -12162, -11973, -11700, -11347, -10917, -10414, -9846, -9218, -8538, -7815, -7058, -6276, -5480, -4678, -3881, -3099, -2341, -1616, -933, -298, 279, 796, 1246, 1626, 1932, 2164, 2320, 2402, + 2412, 2352, 2227, 2041, 1801, 1512, 1181, 817, 427, 19, -398, -817, -1231, -1631, -2010, -2363, -2683, -2965, -3205, -3400, -3548, -3647, -3696, -3696, -3648, -3554, -3418, -3242, -3031, -2789, -2522, -2234, + -1932, -1620, -1304, -990, -681, -382, -97, 169, 416, 641, 843, 1022, 1177, 1311, 1426, 1523, 1606, 1680, 1748, 1816, 1889, 1971, 2068, 2186, 2330, 2504, 2712, 2959, 3248, 3582, 3961, 4387, + 4860, 5379, 5942, 6545, 7186, 7859, 8559, 9279, 10014, 10754, 11492, 12220, 12929, 13610, 14256, 14857, 15406, 15896, 16319, 16669, 16942, 17132, 17237, 17255, 17185, 17026, 16780, 16451, 16041, 15555, 14999, 14381, + 13707, 12987, 12228, 11441, 10636, 9823, 9010, 8210, 7430, 6681, 5970, 5307, 4697, 4147, 3662, 3247, 2903, 2633, 2437, 2314, 2263, 2280, 2362, 2503, 2697, 2937, 3216, 3526, 3858, 4204, 4555, 4903, + 5238, 5554, 5841, 6093, 6305, 6469, 6583, 6642, 6643, 6586, 6470, 6296, 6066, 5782, 5448, 5070, 4652, 4201, 3724, 3229, 2721, 2211, 1704, 1210, 735, 288, -125, -499, -827, -1106, -1330, -1497, + -1606, -1654, -1642, -1571, -1442, -1258, -1023, -740, -416, -54, 338, 755, 1192, 1640, 2094, 2547, 2994, 3429, 3847, 4243, 4614, 4956, 5268, 5547, 5794, 6009, 6192, 6345, 6471, 6573, 6655, 6722, + 6777, 6827, 6875, 6928, 6990, 7067, 7164, 7283, 7430, 7608, 7818, 8063, 8343, 8658, 9008, 9390, 9802, 10240, 10700, 11176, 11663, 12155, 12643, 13122, 13583, 14020, 14423, 14786, 15102, 15364, 15566, 15702, + 15768, 15760, 15675, 15512, 15269, 14948, 14551, 14079, 13537, 12930, 12264, 11546, 10785, 9988, 9165, 8325, 7479, 6638, 5810, 5007, 4239, 3514, 2842, 2231, 1688, 1220, 832, 528, 311, 183, 144, 195, + 332, 552, 852, 1225, 1666, 2166, 2718, 3313, 3941, 4594, 5261, 5932, 6598, 7249, 7877, 8472, 9027, 9534, 9989, 10384, 10718, 10985, 11184, 11315, 11377, 11373, 11303, 11172, 10984, 10744, 10457, 10131, + 9770, 9383, 8977, 8560, 8137, 7716, 7304, 6907, 6531, 6179, 5858, 5569, 5315, 5099, 4920, 4779, 4675, 4605, 4567, 4558, 4573, 4609, 4660, 4721, 4787, 4852, 4910, 4957, 4986, 4994, 4977, 4930, + 4851, 4737, 4588, 4403, 4181, 3925, 3636, 3316, 2970, 2601, 2214, 1815, 1410, 1004, 604, 216, -152, -496, -810, -1087, -1324, -1514, -1656, -1746, -1783, -1764, -1691, -1563, -1384, -1155, -880, -565, + -214, 166, 569, 988, 1415, 1842, 2262, 2666, 3047, 3398, 3711, 3979, 4198, 4363, 4469, 4513, 4493, 4409, 4260, 4048, 3775, 3444, 3061, 2630, 2159, 1653, 1121, 571, 12, -547, -1097, -1630, + -2136, -2606, -3033, -3410, -3729, -3985, -4173, -4290, -4332, -4298, -4188, -4004, -3746, -3419, -3027, -2575, -2070, -1519, -931, -314, 321, 967, 1614, 2250, 2867, 3455, 4005, 4509, 4959, 5347, 5667, 5915, + 6087, 6179, 6190, 6119, 5966, 5735, 5426, 5044, 4595, 4083, 3515, 2899, 2241, 1551, 836, 104, -634, -1373, -2103, -2817, -3509, -4171, -4798, -5384, -5927, -6423, -6870, -7266, -7611, -7905, -8151, -8350, + -8506, -8621, -8699, -8747, -8767, -8766, -8748, -8718, -8682, -8644, -8608, -8579, -8559, -8550, -8556, -8577, -8613, -8664, -8730, -8807, -8895, -8988, -9084, -9177, -9264, -9339, -9397, -9432, -9439, -9412, -9347, -9239, + -9085, -8879, -8621, -8307, -7936, -7509, -7026, -6488, -5899, -5261, -4580, -3860, -3108, -2331, -1536, -731, 74, 873, 1655, 2413, 3137, 3819, 4451, 5025, 5536, 5976, 6342, 6629, 6834, 6956, 6993, 6947, + 6819, 6612, 6331, 5979, 5564, 5091, 4570, 4008, 3415, 2799, 2170, 1538, 913, 303, -281, -833, -1343, -1806, -2214, -2563, -2849, -3068, -3219, -3301, -3314, -3262, -3146, -2970, -2740, -2461, -2141, -1786, + -1405, -1007, -600, -193, 204, 584, 938, 1257, 1535, 1764, 1938, 2054, 2105, 2090, 2007, 1854, 1632, 1343, 989, 573, 99, -425, -996, -1607, -2249, -2916, -3599, -4291, -4984, -5670, -6340, -6989, + -7609, -8194, -8740, -9241, -9695, -10098, -10448, -10746, -10990, -11183, -11326, -11421, -11474, -11487, -11466, -11416, -11343, -11252, -11150, -11043, -10936, -10836, -10748, -10676, -10624, -10598, -10598, -10628, -10689, -10782, -10905, -11059, + -11241, -11448, -11677, -11924, -12185, -12454, -12726, -12997, -13259, -13508, -13738, -13944, -14121, -14265, -14372, -14440, -14464, -14446, -14383, -14276, -14126, -13936, -13708, -13446, -13155, -12841, -12509, -12166, -11819, -11475, -11142, -10827, + -10537, -10281, -10064, -9894, -9775, -9712, -9711, -9773, -9902, -10097, -10360, -10689, -11082, -11534, -12043, -12602, -13204, -13842, -14509, -15195, -15890, -16585, -17271, -17936, -18571, -19167, -19713, -20201, -20622, -20970, -21238, -21419, + -21511, -21509, -21412, -21219, -20931, -20549, -20077, -19518, -18879, -18165, -17384, -16545, -15656, -14726, -13765, -12784, -11792, -10801, -9818, -8856, -7921, -7023, -6170, -5369, -4624, -3942, -3326, -2778, -2301, -1895, -1559, -1292, + -1091, -952, -871, -842, -861, -919, -1012, -1131, -1270, -1422, -1580, -1737, -1887, -2024, -2144, -2242, -2314, -2358, -2371, -2353, -2304, -2223, -2114, -1978, -1818, -1638, -1442, -1235, -1022, -808, -600, -401, + -219, -56, 79, 187, 260, 298, 297, 255, 173, 49, -113, -314, -551, -819, -1116, -1435, -1773, -2122, -2476, -2830, -3176, -3508, -3820, -4104, -4356, -4569, -4740, -4863, -4935, -4955, -4919, -4828, + -4682, -4482, -4230, -3929, -3584, -3199, -2780, -2332, -1864, -1382, -893, -406, 71, 533, 970, 1377, 1746, 2071, 2348, 2572, 2740, 2849, 2899, 2888, 2819, 2692, 2511, 2280, 2005, 1690, 1344, 973, + 585, 190, -203, -588, -954, -1291, -1593, -1850, -2054, -2200, -2282, -2293, -2230, -2090, -1872, -1575, -1199, -746, -220, 374, 1034, 1750, 2517, 3327, 4169, 5036, 5917, 6802, 7681, 8545, 9382, 10184, + 10942, 11647, 12292, 12870, 13375, 13802, 14147, 14409, 14586, 14677, 14683, 14607, 14452, 14223, 13923, 13559, 13138, 12667, 12153, 11605, 11030, 10438, 9835, 9231, 8632, 8046, 7478, 6936, 6423, 5945, 5504, 5103, + 4744, 4426, 4150, 3915, 3719, 3558, 3430, 3331, 3256, 3200, 3159, 3128, 3101, 3074, 3041, 2999, 2944, 2871, 2780, 2666, 2529, 2369, 2186, 1980, 1754, 1509, 1250, 981, 704, 426, 152, -112, + -362, -591, -794, -965, -1100, -1193, -1240, -1238, -1185, -1078, -917, -702, -432, -111, 258, 673, 1129, 1620, 2140, 2682, 3238, 3801, 4362, 4913, 5446, 5952, 6423, 6852, 7230, 7552, 7811, 8002, + 8123, 8168, 8137, 8029, 7844, 7584, 7252, 6850, 6385, 5862, 5289, 4672, 4020, 3342, 2647, 1946, 1248, 562, -100, -732, -1323, -1866, -2353, -2778, -3134, -3417, -3625, -3753, -3803, -3773, -3665, -3481, + -3226, -2903, -2519, -2081, -1595, -1069, -513, 64, 655, 1250, 1839, 2414, 2967, 3489, 3973, 4413, 4802, 5136, 5412, 5627, 5778, 5867, 5892, 5856, 5762, 5613, 5414, 5170, 4887, 4572, 4232, 3874, + 3506, 3136, 2770, 2416, 2082, 1772, 1495, 1253, 1053, 897, 787, 727, 716, 755, 841, 974, 1150, 1365, 1616, 1897, 2202, 2526, 2863, 3206, 3549, 3886, 4210, 4516, 4798, 5052, 5274, 5459, + 5606, 5712, 5776, 5799, 5780, 5722, 5626, 5496, 5335, 5147, 4937, 4710, 4472, 4228, 3984, 3745, 3517, 3304, 3112, 2943, 2802, 2692, 2613, 2568, 2557, 2578, 2630, 2711, 2817, 2944, 3088, 3242, + 3401, 3558, 3706, 3837, 3946, 4024, 4065, 4061, 4007, 3898, 3727, 3491, 3187, 2813, 2367, 1850, 1261, 605, -115, -897, -1733, -2618, -3544, -4502, -5483, -6479, -7479, -8472, -9449, -10398, -11310, -12174, + -12981, -13722, -14390, -14976, -15475, -15882, -16191, -16402, -16512, -16520, -16429, -16240, -15956, -15583, -15127, -14593, -13991, -13327, -12611, -11853, -11062, -10249, -9424, -8595, -7774, -6969, -6188, -5441, -4733, -4071, -3461, -2906, + -2410, -1976, -1602, -1291, -1040, -847, -710, -623, -583, -583, -618, -681, -766, -865, -973, -1081, -1184, -1275, -1348, -1400, -1424, -1418, -1379, -1304, -1192, -1044, -860, -642, -391, -111, 193, 519, + 860, 1213, 1571, 1928, 2279, 2618, 2939, 3237, 3508, 3747, 3950, 4115, 4240, 4322, 4362, 4360, 4316, 4234, 4115, 3964, 3785, 3581, 3360, 3127, 2887, 2648, 2415, 2196, 1995, 1819, 1674, 1564, + 1494, 1466, 1485, 1551, 1666, 1830, 2041, 2298, 2598, 2937, 3310, 3713, 4139, 4581, 5033, 5487, 5936, 6371, 6785, 7171, 7522, 7830, 8090, 8297, 8446, 8533, 8556, 8514, 8405, 8230, 7992, 7692, + 7336, 6927, 6471, 5976, 5449, 4898, 4331, 3758, 3188, 2630, 2093, 1586, 1118, 696, 329, 24, -215, -383, -475, -491, -427, -284, -64, 232, 600, 1036, 1533, 2086, 2686, 3325, 3995, 4686, + 5389, 6093, 6789, 7466, 8117, 8731, 9300, 9816, 10273, 10664, 10985, 11231, 11400, 11489, 11500, 11431, 11285, 11065, 10775, 10419, 10003, 9534, 9018, 8464, 7878, 7270, 6648, 6019, 5391, 4773, 4171, 3592, + 3043, 2528, 2052, 1618, 1229, 888, 594, 348, 148, -5, -117, -190, -228, -236, -217, -177, -122, -57, 12, 82, 147, 204, 246, 273, 281, 267, 232, 174, 95, -5, -124, -260, + -408, -565, -727, -889, -1044, -1189, -1318, -1424, -1504, -1552, -1563, -1533, -1460, -1339, -1170, -951, -682, -363, 2, 413, 866, 1356, 1877, 2424, 2990, 3567, 4148, 4725, 5288, 5829, 6340, 6813, + 7240, 7612, 7924, 8168, 8340, 8435, 8450, 8382, 8231, 7996, 7678, 7280, 6805, 6258, 5644, 4971, 4245, 3475, 2671, 1840, 994, 141, -706, -1540, -2351, -3129, -3865, -4552, -5182, -5748, -6247, -6673, + -7023, -7296, -7490, -7607, -7648, -7617, -7516, -7351, -7128, -6854, -6536, -6183, -5801, -5401, -4991, -4579, -4175, -3787, -3422, -3087, -2790, -2536, -2330, -2176, -2075, -2031, -2044, -2113, -2237, -2413, -2637, -2906, + -3213, -3553, -3920, -4305, -4703, -5104, -5501, -5887, -6253, -6593, -6900, -7168, -7391, -7565, -7685, -7748, -7752, -7697, -7581, -7406, -7174, -6886, -6547, -6160, -5730, -5263, -4765, -4242, -3699, -3145, -2585, -2025, + -1472, -932, -409, 91, 565, 1010, 1422, 1801, 2145, 2455, 2731, 2974, 3188, 3375, 3539, 3684, 3815, 3936, 4053, 4171, 4295, 4429, 4580, 4751, 4945, 5167, 5418, 5701, 6017, 6365, 6745, 7155, + 7592, 8054, 8535, 9032, 9537, 10045, 10550, 11042, 11517, 11964, 12378, 12750, 13074, 13342, 13549, 13688, 13755, 13746, 13659, 13490, 13240, 12909, 12499, 12011, 11449, 10820, 10128, 9381, 8586, 7751, 6887, 6002, + 5108, 4213, 3329, 2465, 1633, 841, 98, -586, -1205, -1753, -2222, -2609, -2911, -3125, -3250, -3288, -3238, -3106, -2894, -2608, -2253, -1838, -1370, -857, -309, 263, 854, 1450, 2043, 2624, 3183, 3711, + 4201, 4646, 5039, 5376, 5651, 5861, 6005, 6082, 6091, 6033, 5912, 5730, 5490, 5200, 4863, 4486, 4075, 3639, 3184, 2717, 2246, 1777, 1318, 875, 453, 57, -306, -636, -929, -1183, -1397, -1570, + -1705, -1801, -1863, -1893, -1895, -1872, -1831, -1777, -1714, -1648, -1585, -1530, -1488, -1464, -1462, -1487, -1540, -1624, -1741, -1891, -2075, -2292, -2539, -2814, -3115, -3435, -3772, -4120, -4473, -4824, -5169, -5500, + -5811, -6096, -6350, -6565, -6738, -6864, -6940, -6962, -6928, -6837, -6690, -6486, -6227, -5917, -5558, -5156, -4715, -4242, -3743, -3225, -2696, -2164, -1637, -1122, -627, -161, 269, 658, 1000, 1289, 1520, 1691, + 1800, 1844, 1825, 1742, 1598, 1396, 1140, 836, 490, 108, -301, -731, -1172, -1616, -2052, -2474, -2870, -3234, -3557, -3831, -4051, -4209, -4301, -4323, -4273, -4148, -3948, -3675, -3329, -2915, -2437, -1899, + -1309, -673, 0, 700, 1421, 2152, 2882, 3603, 4304, 4975, 5608, 6194, 6724, 7193, 7592, 7917, 8164, 8329, 8411, 8408, 8321, 8152, 7902, 7576, 7178, 6714, 6190, 5613, 4990, 4330, 3641, 2931, + 2209, 1483, 761, 51, -640, -1306, -1940, -2539, -3096, -3610, -4078, -4498, -4870, -5193, -5470, -5702, -5893, -6044, -6162, -6248, -6310, -6351, -6376, -6391, -6401, -6410, -6422, -6441, -6472, -6515, -6574, -6649, + -6742, -6851, -6976, -7115, -7265, -7423, -7586, -7748, -7906, -8053, -8186, -8297, -8382, -8435, -8451, -8425, -8353, -8232, -8058, -7828, -7543, -7200, -6801, -6347, -5839, -5282, -4680, -4038, -3361, -2656, -1931, -1194, + -452, 284, 1009, 1711, 2384, 3018, 3605, 4138, 4610, 5015, 5349, 5607, 5786, 5884, 5900, 5836, 5691, 5470, 5176, 4813, 4389, 3909, 3382, 2816, 2220, 1602, 974, 343, -278, -883, -1461, -2004, + -2504, -2953, -3346, -3676, -3939, -4133, -4255, -4304, -4280, -4186, -4023, -3796, -3509, -3169, -2781, -2353, -1894, -1411, -913, -409, 90, 578, 1046, 1485, 1888, 2247, 2557, 2813, 3010, 3144, 3215, 3220, + 3160, 3035, 2849, 2604, 2305, 1956, 1563, 1134, 674, 191, -305, -809, -1313, -1807, -2285, -2740, -3164, -3553, -3900, -4200, -4452, -4650, -4795, -4884, -4918, -4898, -4825, -4704, -4536, -4327, -4082, -3805, + -3502, -3180, -2844, -2501, -2157, -1817, -1488, -1174, -880, -609, -366, -151, 31, 182, 302, 390, 449, 482, 490, 478, 451, 412, 367, 322, 281, 251, 237, 243, 275, 337, 432, 563, + 734, 945, 1197, 1490, 1822, 2192, 2597, 3032, 3493, 3976, 4472, 4977, 5483, 5983, 6469, 6934, 7369, 7768, 8124, 8430, 8680, 8869, 8992, 9047, 9030, 8941, 8779, 8544, 8240, 7869, 7435, 6943, + 6401, 5814, 5192, 4542, 3875, 3199, 2525, 1862, 1221, 611, 43, -475, -936, -1330, -1653, -1897, -2058, -2134, -2121, -2019, -1828, -1549, -1186, -743, -224, 363, 1013, 1716, 2465, 3250, 4062, 4889, + 5722, 6552, 7367, 8159, 8918, 9636, 10305, 10917, 11468, 11951, 12364, 12702, 12965, 13151, 13261, 13298, 13262, 13158, 12991, 12765, 12486, 12160, 11795, 11398, 10974, 10533, 10080, 9622, 9167, 8719, 8284, 7867, + 7472, 7101, 6757, 6442, 6155, 5896, 5664, 5458, 5273, 5107, 4956, 4815, 4679, 4544, 4403, 4251, 4084, 3896, 3683, 3440, 3164, 2852, 2502, 2111, 1679, 1207, 694, 144, -441, -1059, -1705, -2373, + -3060, -3758, -4461, -5163, -5858, -6537, -7196, -7826, -8423, -8980, -9492, -9956, -10366, -10721, -11019, -11258, -11438, -11561, -11627, -11641, -11606, -11527, -11408, -11256, -11077, -10879, -10669, -10454, -10242, -10042, -9859, -9701, + -9574, -9486, -9440, -9441, -9492, -9597, -9756, -9970, -10239, -10559, -10929, -11345, -11801, -12291, -12810, -13349, -13901, -14456, -15007, -15545, -16059, -16542, -16984, -17378, -17716, -17990, -18195, -18325, -18376, -18346, -18231, -18032, + -17748, -17382, -16936, -16414, -15822, -15166, -14452, -13689, -12886, -12052, -11196, -10329, -9460, -8601, -7760, -6948, -6174, -5446, -4772, -4159, -3614, -3141, -2744, -2427, -2190, -2035, -1960, -1964, -2044, -2196, -2415, -2696, + -3032, -3416, -3840, -4298, -4779, -5278, -5785, -6292, -6793, -7280, -7746, -8186, -8595, -8968, -9301, -9594, -9843, -10047, -10209, -10327, -10405, -10445, -10450, -10425, -10372, -10299, -10208, -10106, -9998, -9889, -9783, -9685, + -9600, -9531, -9480, -9450, -9444, -9460, -9500, -9563, -9647, -9750, -9869, -10000, -10139, -10281, -10421, -10554, -10674, -10775, -10852, -10899, -10912, -10885, -10815, -10698, -10530, -10311, -10039, -9715, -9337, -8909, -8433, -7913, + -7353, -6758, -6136, -5491, -4832, -4167, -3504, -2851, -2217, -1610, -1038, -509, -31, 388, 746, 1034, 1250, 1390, 1452, 1434, 1338, 1164, 915, 594, 207, -241, -743, -1292, -1880, -2497, -3134, -3782, + -4430, -5070, -5691, -6283, -6839, -7349, -7805, -8202, -8534, -8794, -8980, -9090, -9121, -9074, -8950, -8750, -8479, -8141, -7741, -7286, -6782, -6238, -5663, -5064, -4451, -3832, -3218, -2617, -2037, -1486, -972, -501, + -79, 288, 598, 847, 1034, 1158, 1219, 1219, 1161, 1047, 883, 674, 426, 145, -161, -486, -822, -1161, -1496, -1818, -2122, -2400, -2647, -2856, -3025, -3148, -3222, -3247, -3221, -3144, -3017, -2841, + -2620, -2357, -2056, -1721, -1359, -976, -576, -167, 244, 653, 1053, 1438, 1802, 2140, 2449, 2725, 2965, 3166, 3328, 3451, 3533, 3578, 3587, 3563, 3509, 3430, 3330, 3214, 3088, 2957, 2826, 2701, + 2588, 2491, 2416, 2366, 2344, 2354, 2398, 2476, 2589, 2737, 2918, 3130, 3369, 3631, 3913, 4207, 4508, 4810, 5105, 5387, 5648, 5880, 6077, 6233, 6339, 6392, 6385, 6314, 6176, 5968, 5690, 5339, + 4918, 4428, 3873, 3255, 2582, 1859, 1092, 292, -534, -1377, -2227, -3074, -3906, -4715, -5489, -6218, -6894, -7506, -8048, -8511, -8890, -9178, -9373, -9470, -9469, -9368, -9169, -8874, -8486, -8010, -7451, -6816, + -6113, -5349, -4535, -3679, -2791, -1882, -960, -38, 876, 1773, 2644, 3481, 4276, 5022, 5715, 6349, 6922, 7431, 7874, 8252, 8566, 8817, 9009, 9145, 9230, 9270, 9270, 9236, 9176, 9096, 9003, 8903, + 8804, 8712, 8632, 8569, 8529, 8515, 8529, 8575, 8653, 8764, 8907, 9081, 9284, 9513, 9764, 10033, 10316, 10606, 10899, 11189, 11471, 11737, 11984, 12205, 12396, 12553, 12672, 12749, 12783, 12773, 12716, 12614, + 12468, 12280, 12052, 11788, 11491, 11168, 10823, 10462, 10092, 9718, 9348, 8987, 8642, 8318, 8023, 7760, 7534, 7349, 7209, 7114, 7066, 7066, 7113, 7206, 7341, 7515, 7724, 7963, 8226, 8506, 8798, 9093, + 9383, 9663, 9923, 10156, 10356, 10515, 10629, 10690, 10696, 10641, 10524, 10342, 10096, 9785, 9411, 8977, 8487, 7946, 7359, 6733, 6075, 5394, 4698, 3996, 3297, 2611, 1948, 1316, 723, 179, -308, -733, + -1089, -1372, -1577, -1701, -1743, -1702, -1579, -1377, -1098, -746, -328, 149, 681, 1258, 1871, 2511, 3169, 3834, 4497, 5149, 5779, 6379, 6940, 7454, 7915, 8315, 8650, 8915, 9109, 9227, 9271, 9240, + 9136, 8961, 8720, 8417, 8057, 7646, 7192, 6702, 6184, 5646, 5095, 4541, 3991, 3453, 2934, 2441, 1980, 1557, 1175, 839, 551, 313, 127, -7, -91, -127, -116, -63, 29, 156, 313, 495, + 696, 911, 1135, 1361, 1587, 1806, 2015, 2211, 2390, 2550, 2690, 2809, 2908, 2987, 3048, 3093, 3126, 3150, 3169, 3189, 3214, 3249, 3300, 3371, 3469, 3598, 3762, 3966, 4213, 4505, 4846, 5235, + 5674, 6161, 6695, 7274, 7893, 8549, 9235, 9947, 10678, 11419, 12163, 12902, 13627, 14330, 15002, 15634, 16218, 16747, 17213, 17610, 17933, 18176, 18336, 18410, 18397, 18296, 18108, 17836, 17482, 17051, 16548, 15979, + 15353, 14676, 13958, 13207, 12434, 11648, 10860, 10079, 9315, 8576, 7873, 7213, 6603, 6050, 5559, 5134, 4780, 4497, 4288, 4150, 4084, 4086, 4152, 4278, 4457, 4684, 4951, 5250, 5573, 5911, 6256, 6598, + 6928, 7240, 7524, 7773, 7981, 8142, 8251, 8304, 8298, 8231, 8102, 7911, 7661, 7353, 6990, 6578, 6121, 5626, 5099, 4546, 3976, 3397, 2815, 2239, 1677, 1136, 623, 144, -293, -687, -1031, -1323, + -1559, -1738, -1861, -1927, -1937, -1895, -1803, -1665, -1485, -1270, -1024, -753, -464, -164, 141, 446, 745, 1030, 1298, 1544, 1763, 1952, 2110, 2233, 2322, 2375, 2395, 2382, 2339, 2269, 2176, 2063, + 1936, 1800, 1659, 1519, 1385, 1263, 1157, 1072, 1012, 981, 981, 1014, 1082, 1185, 1322, 1492, 1694, 1923, 2176, 2448, 2733, 3025, 3319, 3606, 3879, 4131, 4354, 4541, 4685, 4779, 4816, 4791, + 4700, 4537, 4301, 3988, 3599, 3133, 2592, 1977, 1293, 545, -261, -1120, -2023, -2962, -3928, -4911, -5901, -6888, -7861, -8810, -9725, -10596, -11414, -12171, -12858, -13469, -13998, -14440, -14792, -15051, -15216, -15288, + -15268, -15158, -14963, -14687, -14337, -13919, -13441, -12912, -12341, -11737, -11110, -10470, -9826, -9189, -8566, -7968, -7402, -6875, -6394, -5965, -5591, -5278, -5026, -4838, -4713, -4651, -4650, -4706, -4817, -4976, -5180, -5421, + -5693, -5990, -6304, -6627, -6954, -7277, -7588, -7883, -8154, -8398, -8609, -8784, -8920, -9015, -9068, -9080, -9050, -8981, -8875, -8734, -8564, -8367, -8150, -7917, -7674, -7427, -7180, -6940, -6712, -6501, -6311, -6146, + -6009, -5904, -5831, -5792, -5788, -5816, -5877, -5966, -6083, -6222, -6379, -6549, -6726, -6906, -7081, -7245, -7392, -7516, -7611, -7672, -7693, -7670, -7599, -7477, -7302, -7073, -6790, -6452, -6062, -5622, -5136, -4608, + -4043, -3447, -2828, -2190, -1544, -895, -253, 374, 980, 1556, 2094, 2590, 3035, 3425, 3755, 4022, 4223, 4357, 4423, 4423, 4357, 4230, 4044, 3805, 3519, 3193, 2834, 2450, 2049, 1641, 1235, 839, + 462, 114, -198, -467, -685, -846, -945, -979, -943, -836, -657, -407, -88, 297, 746, 1252, 1809, 2410, 3048, 3714, 4398, 5092, 5785, 6468, 7131, 7764, 8358, 8905, 9396, 9824, 10182, 10466, + 10671, 10792, 10829, 10780, 10646, 10427, 10126, 9746, 9293, 8771, 8187, 7548, 6860, 6134, 5375, 4594, 3798, 2997, 2197, 1408, 635, -112, -830, -1513, -2156, -2755, -3309, -3814, -4272, -4680, -5041, -5357, + -5629, -5861, -6058, -6222, -6359, -6474, -6571, -6656, -6734, -6809, -6885, -6967, -7057, -7160, -7275, -7406, -7553, -7715, -7892, -8081, -8281, -8488, -8699, -8909, -9113, -9307, -9485, -9642, -9771, -9868, -9928, -9945, + -9915, -9834, -9700, -9509, -9260, -8952, -8587, -8164, -7686, -7156, -6578, -5957, -5299, -4610, -3898, -3171, -2436, -1702, -978, -272, 405, 1047, 1646, 2193, 2682, 3107, 3461, 3742, 3945, 4069, 4112, 4074, + 3958, 3764, 3498, 3163, 2765, 2311, 1809, 1266, 692, 95, -514, -1128, -1735, -2326, -2893, -3426, -3917, -4360, -4747, -5073, -5334, -5526, -5646, -5695, -5672, -5578, -5416, -5190, -4904, -4563, -4176, -3748, + -3288, -2803, -2304, -1799, -1296, -805, -335, 106, 511, 873, 1184, 1441, 1637, 1770, 1837, 1837, 1769, 1635, 1436, 1175, 857, 486, 68, -389, -882, -1401, -1939, -2488, -3040, -3588, -4123, -4638, + -5127, -5584, -6002, -6378, -6708, -6988, -7217, -7393, -7517, -7588, -7610, -7585, -7515, -7405, -7261, -7086, -6887, -6670, -6440, -6205, -5969, -5739, -5521, -5320, -5139, -4984, -4857, -4761, -4698, -4669, -4674, -4711, + -4781, -4879, -5004, -5152, -5317, -5497, -5685, -5875, -6064, -6244, -6411, -6559, -6683, -6780, -6844, -6873, -6864, -6815, -6726, -6597, -6428, -6221, -5978, -5705, -5404, -5080, -4741, -4392, -4039, -3691, -3355, -3037, + -2747, -2490, -2273, -2105, -1989, -1933, -1939, -2012, -2154, -2366, -2650, -3003, -3425, -3913, -4461, -5066, -5720, -6417, -7149, -7906, -8681, -9462, -10241, -11007, -11749, -12459, -13125, -13740, -14294, -14781, -15191, -15521, + -15765, -15919, -15980, -15948, -15822, -15604, -15296, -14902, -14427, -13876, -13257, -12578, -11846, -11072, -10264, -9433, -8588, -7740, -6899, -6074, -5275, -4509, -3786, -3112, -2494, -1936, -1442, -1017, -661, -376, -162, -16, + 62, 78, 35, -61, -207, -394, -617, -869, -1141, -1428, -1722, -2016, -2304, -2580, -2839, -3075, -3285, -3465, -3614, -3729, -3811, -3859, -3875, -3860, -3818, -3752, -3665, -3563, -3449, -3331, -3212, -3098, + -2994, -2907, -2839, -2797, -2783, -2801, -2854, -2943, -3069, -3234, -3435, -3671, -3941, -4240, -4565, -4912, -5276, -5650, -6028, -6404, -6772, -7125, -7456, -7760, -8029, -8258, -8442, -8578, -8660, -8686, -8655, -8565, + -8416, -8209, -7946, -7630, -7264, -6852, -6402, -5917, -5406, -4874, -4330, -3782, -3236, -2702, -2185, -1695, -1236, -817, -441, -114, 159, 378, 540, 644, 691, 682, 619, 506, 348, 151, -79, -335, + -610, -895, -1181, -1459, -1721, -1958, -2160, -2321, -2431, -2485, -2475, -2397, -2246, -2019, -1713, -1329, -865, -324, 291, 977, 1730, 2542, 3406, 4316, 5262, 6234, 7224, 8221, 9216, 10197, 11155, 12080, + 12962, 13794, 14567, 15273, 15907, 16463, 16937, 17326, 17628, 17843, 17970, 18012, 17971, 17850, 17656, 17392, 17066, 16684, 16254, 15783, 15280, 14753, 14210, 13659, 13108, 12563, 12032, 11521, 11034, 10577, 10153, 9765, + 9414, 9102, 8829, 8594, 8395, 8230, 8095, 7988, 7903, 7836, 7783, 7738, 7696, 7653, 7602, 7541, 7464, 7369, 7252, 7111, 6944, 6751, 6531, 6285, 6015, 5723, 5411, 5085, 4747, 4404, 4059, 3718, + 3388, 3073, 2780, 2515, 2282, 2086, 1932, 1823, 1764, 1755, 1798, 1894, 2043, 2242, 2490, 2783, 3118, 3488, 3888, 4313, 4753, 5202, 5653, 6095, 6522, 6924, 7295, 7625, 7907, 8135, 8303, 8405, + 8437, 8396, 8279, 8086, 7816, 7470, 7051, 6563, 6009, 5396, 4731, 4020, 3272, 2496, 1701, 897, 93, -699, -1472, -2215, -2919, -3576, -4177, -4716, -5187, -5585, -5905, -6145, -6303, -6379, -6374, -6289, + -6128, -5894, -5595, -5234, -4820, -4361, -3863, -3337, -2791, -2235, -1677, -1126, -591, -80, 399, 840, 1237, 1585, 1878, 2116, 2294, 2413, 2473, 2474, 2420, 2313, 2159, 1960, 1725, 1458, 1168, 860, + 543, 224, -88, -388, -667, -920, -1141, -1323, -1462, -1554, -1597, -1588, -1527, -1412, -1246, -1029, -764, -455, -106, 278, 693, 1131, 1588, 2056, 2530, 3002, 3467, 3918, 4350, 4758, 5137, 5484, + 5795, 6068, 6302, 6496, 6650, 6765, 6843, 6887, 6900, 6886, 6849, 6794, 6727, 6652, 6576, 6503, 6439, 6390, 6359, 6351, 6369, 6416, 6495, 6607, 6751, 6928, 7137, 7374, 7638, 7923, 8225, 8539, + 8859, 9179, 9490, 9787, 10061, 10306, 10515, 10680, 10794, 10853, 10850, 10781, 10642, 10431, 10147, 9787, 9354, 8848, 8272, 7631, 6930, 6174, 5370, 4528, 3654, 2759, 1852, 943, 43, -837, -1689, -2501, + -3264, -3969, -4607, -5171, -5655, -6053, -6360, -6574, -6691, -6712, -6638, -6469, -6209, -5863, -5435, -4933, -4363, -3733, -3053, -2332, -1579, -804, -18, 769, 1548, 2310, 3046, 3747, 4406, 5017, 5573, 6070, + 6505, 6875, 7179, 7416, 7587, 7695, 7742, 7731, 7668, 7557, 7405, 7218, 7003, 6766, 6514, 6255, 5995, 5740, 5497, 5270, 5065, 4886, 4736, 4616, 4530, 4478, 4458, 4471, 4514, 4585, 4680, 4795, + 4926, 5067, 5213, 5358, 5498, 5626, 5736, 5824, 5885, 5914, 5907, 5862, 5776, 5647, 5475, 5260, 5003, 4705, 4369, 3999, 3599, 3173, 2727, 2266, 1797, 1326, 860, 405, -31, -445, -829, -1179, + -1490, -1758, -1979, -2152, -2274, -2346, -2368, -2341, -2268, -2152, -1997, -1808, -1592, -1353, -1099, -837, -575, -320, -80, 137, 325, 477, 587, 649, 658, 611, 504, 336, 105, -187, -540, -952, + -1418, -1935, -2496, -3095, -3725, -4378, -5046, -5719, -6389, -7046, -7680, -8284, -8847, -9362, -9821, -10217, -10543, -10796, -10970, -11062, -11072, -10997, -10840, -10601, -10284, -9893, -9433, -8911, -8334, -7709, -7046, -6353, + -5641, -4919, -4197, -3485, -2792, -2128, -1502, -921, -392, 75, 480, 814, 1077, 1264, 1376, 1413, 1374, 1264, 1085, 843, 541, 186, -214, -654, -1124, -1618, -2127, -2643, -3158, -3665, -4155, -4622, + -5060, -5464, -5828, -6149, -6425, -6652, -6830, -6958, -7038, -7071, -7060, -7006, -6915, -6791, -6638, -6461, -6266, -6058, -5843, -5626, -5411, -5204, -5009, -4828, -4666, -4525, -4405, -4309, -4235, -4183, -4152, -4139, + -4141, -4154, -4175, -4199, -4220, -4234, -4236, -4219, -4179, -4110, -4008, -3869, -3690, -3466, -3196, -2879, -2514, -2101, -1643, -1140, -597, -17, 593, 1230, 1885, 2553, 3224, 3891, 4546, 5180, 5784, 6350, + 6870, 7336, 7741, 8078, 8341, 8527, 8630, 8649, 8581, 8426, 8185, 7860, 7454, 6970, 6415, 5794, 5115, 4385, 3614, 2810, 1984, 1145, 304, -530, -1347, -2138, -2893, -3604, -4264, -4866, -5403, -5873, + -6270, -6592, -6839, -7010, -7106, -7131, -7086, -6978, -6811, -6592, -6328, -6027, -5696, -5345, -4981, -4615, -4253, -3906, -3580, -3283, -3022, -2803, -2631, -2510, -2443, -2432, -2478, -2581, -2741, -2954, -3217, -3526, + -3877, -4262, -4677, -5113, -5564, -6021, -6477, -6923, -7353, -7759, -8134, -8471, -8764, -9008, -9199, -9333, -9408, -9422, -9375, -9266, -9097, -8871, -8589, -8257, -7878, -7457, -7001, -6515, -6006, -5480, -4943, -4402, + -3863, -3332, -2814, -2314, -1837, -1385, -961, -569, -208, 120, 418, 686, 925, 1140, 1332, 1507, 1668, 1822, 1971, 2123, 2281, 2452, 2639, 2848, 3082, 3346, 3641, 3970, 4334, 4734, 5169, 5637, + 6138, 6667, 7220, 7793, 8380, 8976, 9572, 10164, 10742, 11299, 11828, 12321, 12770, 13170, 13512, 13791, 14003, 14142, 14206, 14191, 14097, 13924, 13672, 13344, 12943, 12472, 11939, 11349, 10709, 10028, 9315, 8578, + 7829, 7077, 6332, 5604, 4905, 4242, 3626, 3066, 2568, 2140, 1788, 1517, 1331, 1231, 1219, 1296, 1460, 1708, 2036, 2440, 2914, 3451, 4044, 4683, 5360, 6066, 6790, 7523, 8255, 8977, 9678, 10351, + 10987, 11578, 12117, 12600, 13021, 13376, 13663, 13879, 14026, 14103, 14112, 14057, 13939, 13765, 13540, 13268, 12958, 12615, 12248, 11862, 11466, 11066, 10669, 10282, 9910, 9559, 9234, 8938, 8675, 8446, 8252, 8095, + 7973, 7886, 7831, 7805, 7804, 7824, 7862, 7910, 7965, 8021, 8072, 8112, 8137, 8142, 8122, 8073, 7992, 7876, 7724, 7534, 7306, 7041, 6740, 6406, 6042, 5651, 5238, 4808, 4367, 3920, 3474, 3035, + 2610, 2204, 1825, 1477, 1166, 897, 673, 499, 376, 307, 291, 329, 419, 560, 747, 976, 1244, 1544, 1870, 2215, 2572, 2933, 3290, 3635, 3961, 4260, 4524, 4748, 4925, 5049, 5116, 5122, + 5066, 4944, 4757, 4506, 4192, 3818, 3388, 2906, 2380, 1814, 1218, 598, -35, -675, -1311, -1935, -2538, -3110, -3644, -4131, -4565, -4939, -5248, -5487, -5652, -5743, -5757, -5696, -5559, -5351, -5075, -4735, + -4338, -3890, -3399, -2874, -2323, -1756, -1183, -613, -56, 478, 980, 1441, 1854, 2210, 2503, 2727, 2878, 2951, 2945, 2859, 2692, 2445, 2120, 1721, 1252, 719, 127, -516, -1205, -1930, -2684, -3457, + -4243, -5031, -5814, -6585, -7335, -8058, -8748, -9400, -10008, -10569, -11081, -11542, -11950, -12306, -12611, -12866, -13074, -13238, -13362, -13450, -13506, -13536, -13545, -13538, -13519, -13495, -13468, -13444, -13425, -13416, -13417, -13432, + -13460, -13501, -13556, -13622, -13698, -13781, -13866, -13950, -14029, -14097, -14149, -14180, -14184, -14157, -14092, -13985, -13832, -13628, -13371, -13058, -12688, -12259, -11771, -11227, -10627, -9975, -9275, -8532, -7751, -6939, -6103, -5251, + -4391, -3532, -2681, -1849, -1044, -275, 450, 1124, 1740, 2291, 2771, 3175, 3502, 3747, 3909, 3989, 3987, 3906, 3748, 3519, 3224, 2869, 2462, 2010, 1522, 1008, 476, -62, -600, -1126, -1632, -2108, + -2547, -2941, -3283, -3568, -3791, -3948, -4037, -4056, -4006, -3887, -3703, -3455, -3149, -2791, -2386, -1942, -1466, -967, -453, 65, 581, 1085, 1567, 2021, 2437, 2809, 3130, 3395, 3598, 3737, 3809, 3811, + 3745, 3610, 3408, 3143, 2817, 2436, 2005, 1531, 1021, 481, -80, -655, -1235, -1814, -2382, -2933, -3460, -3957, -4417, -4835, -5209, -5534, -5808, -6030, -6200, -6318, -6386, -6405, -6380, -6314, -6212, -6078, + -5919, -5740, -5546, -5346, -5143, -4944, -4756, -4582, -4427, -4297, -4193, -4118, -4076, -4066, -4088, -4143, -4229, -4343, -4482, -4644, -4823, -5015, -5216, -5419, -5619, -5811, -5988, -6146, -6280, -6385, -6456, -6491, + -6487, -6442, -6355, -6225, -6053, -5841, -5592, -5308, -4995, -4656, -4298, -3926, -3549, -3172, -2804, -2451, -2122, -1823, -1562, -1345, -1178, -1068, -1018, -1032, -1113, -1263, -1482, -1770, -2125, -2545, -3025, -3561, + -4147, -4775, -5439, -6129, -6836, -7551, -8265, -8966, -9645, -10292, -10897, -11451, -11946, -12373, -12726, -12998, -13184, -13281, -13285, -13196, -13013, -12737, -12370, -11917, -11381, -10769, -10087, -9344, -8547, -7705, -6829, -5928, + -5013, -4093, -3178, -2279, -1405, -564, 234, 984, 1678, 2311, 2879, 3378, 3806, 4162, 4445, 4657, 4800, 4877, 4893, 4851, 4757, 4618, 4439, 4229, 3993, 3739, 3474, 3205, 2938, 2679, 2434, 2207, + 2003, 1825, 1675, 1556, 1466, 1408, 1378, 1377, 1400, 1444, 1507, 1583, 1667, 1754, 1839, 1917, 1981, 2028, 2051, 2047, 2011, 1940, 1832, 1684, 1495, 1265, 994, 684, 337, -44, -456, -894, + -1352, -1826, -2310, -2796, -3278, -3750, -4204, -4636, -5037, -5403, -5728, -6008, -6239, -6418, -6542, -6611, -6623, -6580, -6483, -6334, -6138, -5898, -5620, -5310, -4973, -4617, -4250, -3878, -3510, -3153, -2814, -2502, + -2222, -1981, -1785, -1638, -1544, -1506, -1526, -1605, -1742, -1937, -2186, -2486, -2833, -3221, -3643, -4093, -4563, -5045, -5529, -6007, -6470, -6909, -7315, -7679, -7995, -8253, -8449, -8576, -8629, -8605, -8502, -8318, + -8053, -7709, -7287, -6792, -6227, -5599, -4915, -4182, -3408, -2602, -1775, -936, -94, 738, 1554, 2342, 3092, 3797, 4447, 5037, 5559, 6007, 6379, 6669, 6877, 7002, 7043, 7003, 6884, 6689, 6422, 6091, + 5699, 5256, 4767, 4241, 3686, 3110, 2521, 1927, 1337, 756, 193, -345, -856, -1333, -1773, -2172, -2529, -2842, -3111, -3337, -3522, -3666, -3775, -3851, -3898, -3920, -3924, -3913, -3893, -3870, -3846, -3829, + -3821, -3826, -3847, -3888, -3949, -4032, -4138, -4264, -4412, -4577, -4757, -4950, -5150, -5353, -5555, -5749, -5931, -6093, -6232, -6341, -6415, -6449, -6439, -6381, -6271, -6108, -5890, -5617, -5288, -4905, -4471, -3988, + -3462, -2895, -2296, -1669, -1023, -365, 296, 954, 1598, 2222, 2816, 3372, 3883, 4341, 4741, 5077, 5343, 5537, 5656, 5698, 5664, 5553, 5368, 5112, 4789, 4405, 3966, 3479, 2953, 2394, 1814, 1221, + 625, 35, -537, -1084, -1596, -2065, -2482, -2841, -3135, -3359, -3510, -3583, -3579, -3495, -3332, -3093, -2781, -2399, -1953, -1449, -893, -294, 339, 1000, 1678, 2364, 3050, 3726, 4383, 5014, 5609, 6164, + 6670, 7123, 7519, 7854, 8125, 8332, 8475, 8555, 8573, 8533, 8439, 8296, 8109, 7885, 7630, 7352, 7058, 6755, 6452, 6155, 5872, 5610, 5374, 5170, 5003, 4878, 4796, 4761, 4774, 4835, 4943, 5098, + 5296, 5534, 5809, 6116, 6449, 6803, 7172, 7550, 7930, 8306, 8671, 9020, 9347, 9647, 9916, 10148, 10343, 10496, 10607, 10674, 10699, 10681, 10623, 10527, 10396, 10235, 10048, 9839, 9615, 9380, 9140, 8900, + 8667, 8445, 8239, 8053, 7892, 7759, 7655, 7583, 7543, 7535, 7557, 7609, 7687, 7788, 7906, 8037, 8176, 8315, 8449, 8569, 8670, 8743, 8781, 8779, 8728, 8624, 8461, 8234, 7940, 7576, 7140, 6631, + 6050, 5398, 4679, 3895, 3053, 2157, 1215, 235, -773, -1803, -2844, -3885, -4916, -5926, -6906, -7844, -8732, -9559, -10318, -11000, -11598, -12108, -12523, -12841, -13059, -13177, -13193, -13111, -12933, -12662, -12304, -11865, + -11353, -10774, -10139, -9455, -8733, -7982, -7213, -6435, -5658, -4892, -4146, -3428, -2745, -2105, -1512, -973, -491, -68, 293, 594, 833, 1013, 1137, 1207, 1229, 1208, 1148, 1057, 941, 807, 661, 511, + 364, 225, 101, -2, -81, -130, -147, -129, -74, 17, 146, 312, 513, 745, 1006, 1291, 1596, 1916, 2246, 2579, 2910, 3234, 3544, 3836, 4104, 4343, 4550, 4722, 4855, 4948, 5000, 5010, + 4980, 4910, 4804, 4664, 4494, 4299, 4083, 3853, 3615, 3374, 3137, 2911, 2701, 2514, 2355, 2230, 2144, 2100, 2101, 2150, 2249, 2397, 2595, 2841, 3134, 3468, 3842, 4249, 4684, 5141, 5613, 6092, + 6572, 7044, 7500, 7934, 8338, 8705, 9028, 9302, 9521, 9682, 9782, 9817, 9788, 9693, 9534, 9314, 9034, 8701, 8319, 7893, 7432, 6943, 6435, 5915, 5394, 4879, 4382, 3910, 3473, 3079, 2735, 2450, + 2228, 2076, 1998, 1996, 2074, 2231, 2468, 2782, 3171, 3631, 4156, 4741, 5379, 6062, 6781, 7526, 8290, 9061, 9830, 10587, 11323, 12027, 12692, 13309, 13870, 14370, 14801, 15161, 15445, 15650, 15776, 15823, + 15792, 15684, 15503, 15254, 14941, 14571, 14150, 13685, 13184, 12656, 12107, 11548, 10984, 10425, 9878, 9349, 8846, 8373, 7936, 7539, 7186, 6877, 6616, 6401, 6233, 6111, 6031, 5992, 5990, 6020, 6078, 6159, + 6258, 6369, 6487, 6608, 6725, 6834, 6931, 7013, 7076, 7118, 7138, 7134, 7106, 7055, 6984, 6893, 6785, 6666, 6537, 6405, 6274, 6149, 6036, 5940, 5867, 5821, 5807, 5830, 5894, 6002, 6155, 6357, + 6607, 6905, 7250, 7640, 8073, 8543, 9047, 9578, 10131, 10698, 11271, 11842, 12404, 12948, 13464, 13946, 14383, 14769, 15097, 15359, 15550, 15665, 15700, 15651, 15518, 15298, 14993, 14605, 14136, 13590, 12972, 12288, + 11546, 10753, 9917, 9049, 8156, 7250, 6341, 5437, 4550, 3688, 2861, 2077, 1344, 668, 56, -486, -957, -1354, -1674, -1918, -2087, -2183, -2209, -2170, -2070, -1917, -1717, -1479, -1209, -917, -611, -300, + 5, 300, 574, 820, 1030, 1200, 1324, 1396, 1414, 1376, 1279, 1125, 913, 647, 328, -38, -448, -896, -1375, -1879, -2400, -2930, -3462, -3989, -4501, -4993, -5456, -5885, -6273, -6615, -6906, -7144, + -7324, -7446, -7509, -7512, -7457, -7346, -7182, -6968, -6709, -6411, -6077, -5716, -5332, -4932, -4523, -4111, -3703, -3304, -2920, -2556, -2215, -1903, -1621, -1373, -1158, -978, -833, -720, -639, -586, -558, -552, + -562, -585, -614, -646, -674, -693, -699, -687, -653, -593, -503, -383, -230, -44, 175, 426, 708, 1016, 1348, 1699, 2063, 2435, 2808, 3175, 3529, 3861, 4166, 4435, 4660, 4835, 4954, 5009, + 4997, 4912, 4751, 4512, 4193, 3794, 3316, 2760, 2131, 1431, 667, -154, -1027, -1942, -2892, -3866, -4854, -5847, -6833, -7803, -8747, -9653, -10514, -11320, -12063, -12736, -13332, -13847, -14276, -14616, -14866, -15026, + -15096, -15078, -14977, -14796, -14542, -14220, -13838, -13404, -12928, -12418, -11883, -11335, -10782, -10233, -9700, -9189, -8709, -8269, -7875, -7532, -7246, -7020, -6858, -6760, -6729, -6762, -6858, -7015, -7229, -7496, -7810, -8165, + -8556, -8975, -9415, -9868, -10328, -10788, -11240, -11677, -12094, -12486, -12847, -13173, -13462, -13710, -13917, -14082, -14204, -14286, -14329, -14336, -14311, -14258, -14180, -14084, -13974, -13857, -13737, -13620, -13511, -13416, -13338, -13282, + -13252, -13250, -13278, -13338, -13429, -13552, -13705, -13886, -14093, -14321, -14566, -14824, -15089, -15355, -15616, -15866, -16098, -16307, -16485, -16628, -16731, -16787, -16794, -16747, -16645, -16485, -16267, -15991, -15659, -15272, -14833, -14347, + -13818, -13252, -12656, -12035, -11399, -10753, -10107, -9468, -8844, -8243, -7672, -7139, -6650, -6210, -5824, -5496, -5230, -5026, -4887, -4811, -4797, -4842, -4944, -5097, -5297, -5536, -5807, -6103, -6415, -6735, -7054, -7362, + -7652, -7914, -8139, -8321, -8451, -8525, -8535, -8478, -8350, -8149, -7874, -7525, -7103, -6611, -6052, -5432, -4755, -4030, -3263, -2463, -1638, -799, 44, 883, 1707, 2507, 3272, 3995, 4666, 5278, 5823, 6297, + 6693, 7009, 7241, 7387, 7448, 7424, 7318, 7131, 6868, 6534, 6136, 5678, 5170, 4618, 4030, 3416, 2782, 2139, 1493, 853, 227, -379, -959, -1507, -2019, -2490, -2918, -3300, -3636, -3925, -4168, -4367, + -4524, -4642, -4724, -4775, -4800, -4803, -4789, -4763, -4731, -4696, -4665, -4640, -4626, -4625, -4640, -4672, -4724, -4794, -4882, -4987, -5106, -5238, -5377, -5521, -5665, -5803, -5931, -6043, -6134, -6197, -6228, -6222, + -6174, -6080, -5937, -5741, -5492, -5187, -4827, -4412, -3944, -3427, -2863, -2257, -1614, -942, -246, 465, 1184, 1903, 2613, 3304, 3970, 4599, 5186, 5722, 6199, 6612, 6954, 7222, 7411, 7520, 7545, 7488, + 7349, 7131, 6836, 6468, 6034, 5540, 4992, 4400, 3771, 3114, 2439, 1756, 1075, 405, -244, -864, -1447, -1984, -2468, -2894, -3256, -3551, -3775, -3928, -4008, -4017, -3956, -3829, -3639, -3393, -3096, -2754, + -2376, -1970, -1545, -1108, -669, -238, 177, 569, 930, 1251, 1526, 1750, 1917, 2024, 2069, 2048, 1963, 1813, 1599, 1325, 995, 612, 183, -287, -791, -1321, -1871, -2432, -2995, -3554, -4101, -4628, + -5127, -5594, -6021, -6404, -6738, -7021, -7249, -7422, -7539, -7600, -7607, -7561, -7467, -7327, -7147, -6931, -6685, -6414, -6125, -5825, -5518, -5211, -4911, -4621, -4347, -4093, -3863, -3660, -3486, -3341, -3228, -3144, + -3090, -3062, -3059, -3077, -3112, -3159, -3214, -3272, -3326, -3371, -3403, -3415, -3403, -3362, -3288, -3177, -3027, -2836, -2603, -2327, -2009, -1651, -1254, -823, -361, 126, 634, 1155, 1683, 2211, 2732, 3237, + 3719, 4171, 4586, 4955, 5275, 5537, 5739, 5875, 5943, 5940, 5867, 5722, 5507, 5225, 4879, 4475, 4017, 3512, 2968, 2393, 1796, 1187, 575, -29, -616, -1177, -1701, -2178, -2601, -2961, -3251, -3464, + -3596, -3643, -3601, -3468, -3246, -2933, -2533, -2048, -1484, -845, -140, 625, 1443, 2303, 3197, 4113, 5043, 5976, 6901, 7810, 8692, 9539, 10343, 11095, 11789, 12421, 12984, 13475, 13892, 14234, 14501, 14693, + 14812, 14861, 14845, 14767, 14634, 14450, 14224, 13961, 13669, 13355, 13025, 12688, 12348, 12014, 11690, 11381, 11091, 10825, 10585, 10372, 10188, 10032, 9905, 9803, 9726, 9669, 9629, 9602, 9583, 9567, 9549, 9523, + 9484, 9427, 9347, 9239, 9100, 8926, 8714, 8463, 8170, 7836, 7461, 7046, 6593, 6106, 5587, 5041, 4474, 3891, 3298, 2701, 2108, 1524, 956, 412, -102, -582, -1021, -1415, -1759, -2050, -2286, -2465, + -2586, -2651, -2659, -2614, -2519, -2378, -2196, -1978, -1731, -1462, -1178, -886, -594, -310, -41, 205, 422, 604, 744, 838, 882, 873, 808, 686, 509, 276, -9, -344, -725, -1146, -1600, -2081, + -2582, -3093, -3606, -4113, -4604, -5071, -5504, -5895, -6236, -6520, -6740, -6890, -6965, -6962, -6878, -6711, -6461, -6130, -5718, -5231, -4671, -4045, -3360, -2622, -1841, -1025, -183, 673, 1535, 2393, 3236, 4054, + 4838, 5579, 6269, 6899, 7464, 7958, 8376, 8714, 8971, 9145, 9236, 9245, 9174, 9028, 8810, 8525, 8179, 7780, 7333, 6848, 6332, 5792, 5238, 4677, 4117, 3566, 3029, 2514, 2026, 1570, 1149, 768, + 427, 129, -126, -340, -513, -647, -746, -813, -852, -868, -865, -849, -825, -798, -772, -753, -745, -751, -776, -822, -890, -982, -1099, -1240, -1404, -1589, -1792, -2011, -2240, -2477, -2715, -2950, + -3176, -3387, -3577, -3742, -3875, -3972, -4028, -4040, -4004, -3917, -3779, -3587, -3343, -3047, -2702, -2310, -1876, -1404, -900, -371, 176, 735, 1298, 1855, 2398, 2920, 3411, 3864, 4270, 4624, 4917, 5146, + 5304, 5389, 5397, 5326, 5177, 4950, 4646, 4269, 3823, 3313, 2746, 2127, 1466, 771, 51, -684, -1426, -2164, -2888, -3588, -4256, -4883, -5459, -5980, -6436, -6824, -7139, -7378, -7538, -7618, -7619, -7543, + -7392, -7170, -6882, -6534, -6132, -5684, -5198, -4682, -4146, -3597, -3047, -2502, -1973, -1467, -993, -556, -164, 177, 464, 694, 863, 971, 1018, 1003, 930, 800, 619, 391, 121, -184, -518, -873, + -1242, -1617, -1991, -2356, -2705, -3031, -3328, -3589, -3810, -3987, -4116, -4195, -4221, -4196, -4118, -3989, -3812, -3589, -3324, -3022, -2688, -2327, -1946, -1550, -1145, -740, -338, 52, 427, 780, 1107, 1405, + 1669, 1897, 2087, 2239, 2352, 2428, 2467, 2472, 2447, 2395, 2320, 2228, 2122, 2009, 1895, 1784, 1683, 1596, 1528, 1484, 1468, 1482, 1530, 1612, 1730, 1884, 2073, 2294, 2546, 2824, 3124, 3442, + 3770, 4104, 4435, 4758, 5065, 5348, 5601, 5816, 5986, 6106, 6170, 6172, 6110, 5980, 5779, 5507, 5164, 4751, 4270, 3726, 3122, 2464, 1759, 1014, 238, -559, -1370, -2183, -2989, -3777, -4537, -5258, + -5932, -6548, -7098, -7574, -7969, -8278, -8495, -8618, -8643, -8569, -8397, -8128, -7764, -7310, -6770, -6151, -5460, -4704, -3892, -3034, -2139, -1217, -278, 667, 1609, 2539, 3446, 4323, 5162, 5955, 6697, 7381, + 8005, 8566, 9060, 9488, 9850, 10147, 10381, 10557, 10677, 10748, 10774, 10762, 10718, 10648, 10561, 10462, 10358, 10256, 10161, 10080, 10017, 9977, 9962, 9976, 10020, 10096, 10203, 10341, 10507, 10700, 10916, 11151, + 11401, 11661, 11925, 12188, 12445, 12688, 12914, 13115, 13288, 13428, 13530, 13592, 13610, 13582, 13507, 13386, 13218, 13005, 12749, 12454, 12122, 11760, 11370, 10961, 10536, 10103, 9668, 9237, 8817, 8415, 8035, 7684, + 7366, 7085, 6845, 6649, 6499, 6395, 6337, 6324, 6354, 6424, 6531, 6670, 6835, 7020, 7219, 7425, 7630, 7828, 8009, 8167, 8295, 8385, 8432, 8429, 8371, 8255, 8077, 7834, 7526, 7153, 6714, 6213, + 5653, 5038, 4372, 3664, 2918, 2143, 1348, 541, -267, -1070, -1857, -2618, -3344, -4028, -4660, -5234, -5743, -6181, -6544, -6828, -7030, -7150, -7188, -7145, -7023, -6826, -6559, -6228, -5839, -5399, -4919, -4405, + -3868, -3317, -2762, -2213, -1680, -1171, -695, -261, 122, 450, 717, 917, 1046, 1103, 1086, 995, 830, 594, 289, -79, -507, -989, -1517, -2084, -2684, -3308, -3947, -4594, -5241, -5879, -6501, -7100, + -7669, -8203, -8696, -9145, -9545, -9894, -10192, -10437, -10629, -10770, -10862, -10907, -10910, -10873, -10802, -10702, -10578, -10435, -10279, -10114, -9947, -9781, -9621, -9472, -9335, -9214, -9111, -9026, -8961, -8914, -8884, -8869, + -8867, -8873, -8885, -8898, -8907, -8906, -8891, -8856, -8796, -8706, -8581, -8417, -8209, -7955, -7652, -7299, -6895, -6439, -5933, -5380, -4780, -4140, -3463, -2755, -2023, -1273, -513, 248, 1003, 1743, 2460, 3144, + 3787, 4381, 4919, 5394, 5800, 6131, 6383, 6553, 6639, 6639, 6554, 6385, 6134, 5806, 5404, 4935, 4406, 3824, 3197, 2535, 1846, 1141, 429, -279, -974, -1647, -2288, -2888, -3441, -3939, -4375, -4746, + -5047, -5275, -5429, -5508, -5513, -5447, -5313, -5114, -4855, -4544, -4187, -3792, -3366, -2918, -2457, -1992, -1532, -1084, -658, -261, 100, 419, 689, 908, 1070, 1173, 1217, 1199, 1122, 987, 796, 554, + 265, -64, -429, -822, -1237, -1665, -2099, -2530, -2952, -3357, -3737, -4085, -4397, -4665, -4886, -5055, -5171, -5229, -5231, -5175, -5063, -4895, -4676, -4408, -4096, -3745, -3359, -2945, -2509, -2058, -1598, -1134, + -674, -224, 211, 626, 1018, 1381, 1713, 2012, 2276, 2505, 2699, 2858, 2986, 3084, 3156, 3205, 3237, 3255, 3265, 3272, 3281, 3298, 3328, 3375, 3444, 3538, 3662, 3816, 4003, 4224, 4479, 4766, + 5085, 5433, 5805, 6198, 6606, 7024, 7446, 7864, 8271, 8660, 9023, 9353, 9642, 9884, 10071, 10198, 10259, 10250, 10168, 10008, 9770, 9454, 9060, 8589, 8045, 7431, 6753, 6018, 5232, 4402, 3539, 2651, + 1748, 840, -62, -948, -1809, -2633, -3412, -4136, -4797, -5388, -5903, -6335, -6681, -6938, -7104, -7177, -7160, -7054, -6862, -6588, -6239, -5820, -5338, -4802, -4221, -3604, -2960, -2299, -1630, -965, -311, 320, + 923, 1489, 2010, 2481, 2896, 3251, 3544, 3771, 3933, 4030, 4062, 4033, 3945, 3803, 3612, 3377, 3106, 2804, 2478, 2136, 1785, 1432, 1084, 748, 429, 134, -133, -368, -568, -730, -852, -935, + -977, -981, -947, -880, -781, -656, -509, -345, -169, 13, 196, 375, 543, 695, 828, 936, 1016, 1066, 1082, 1063, 1009, 920, 797, 641, 456, 245, 12, -238, -500, -769, -1037, -1300, + -1551, -1783, -1990, -2167, -2308, -2409, -2464, -2472, -2428, -2331, -2180, -1975, -1718, -1409, -1053, -653, -213, 260, 762, 1285, 1821, 2364, 2905, 3436, 3950, 4438, 4893, 5308, 5677, 5993, 6253, 6451, + 6584, 6652, 6651, 6584, 6450, 6253, 5995, 5682, 5318, 4910, 4465, 3990, 3495, 2987, 2476, 1970, 1480, 1012, 577, 181, -166, -460, -694, -864, -966, -996, -955, -841, -655, -400, -79, 302, + 741, 1229, 1759, 2323, 2912, 3518, 4130, 4738, 5333, 5906, 6446, 6945, 7394, 7786, 8113, 8370, 8552, 8653, 8673, 8608, 8459, 8226, 7911, 7516, 7047, 6507, 5902, 5239, 4526, 3769, 2978, 2160, + 1324, 479, -366, -1206, -2031, -2834, -3609, -4350, -5053, -5712, -6325, -6890, -7405, -7869, -8284, -8650, -8970, -9247, -9483, -9684, -9853, -9995, -10115, -10218, -10309, -10393, -10474, -10558, -10646, -10743, -10851, -10971, + -11106, -11254, -11416, -11591, -11776, -11969, -12166, -12364, -12558, -12742, -12913, -13063, -13189, -13284, -13343, -13361, -13333, -13255, -13124, -12937, -12691, -12385, -12020, -11596, -11114, -10576, -9987, -9351, -8673, -7960, -7217, -6453, + -5676, -4894, -4115, -3349, -2604, -1889, -1211, -580, -3, 514, 966, 1347, 1653, 1881, 2030, 2099, 2089, 2001, 1838, 1605, 1306, 947, 536, 80, -411, -931, -1470, -2017, -2562, -3098, -3614, -4100, + -4550, -4955, -5308, -5603, -5835, -6000, -6094, -6117, -6068, -5947, -5757, -5500, -5180, -4803, -4374, -3901, -3391, -2852, -2293, -1723, -1150, -585, -35, 490, 984, 1437, 1844, 2198, 2494, 2729, 2898, 3000, + 3034, 3000, 2900, 2736, 2510, 2229, 1896, 1518, 1100, 652, 179, -310, -808, -1307, -1799, -2276, -2732, -3160, -3554, -3909, -4221, -4486, -4702, -4867, -4980, -5042, -5054, -5018, -4937, -4815, -4656, -4465, + -4247, -4009, -3756, -3495, -3231, -2971, -2720, -2485, -2269, -2077, -1913, -1780, -1681, -1616, -1586, -1591, -1630, -1701, -1802, -1929, -2078, -2245, -2425, -2612, -2802, -2988, -3166, -3329, -3473, -3592, -3683, -3742, + -3766, -3753, -3701, -3610, -3480, -3312, -3109, -2873, -2608, -2320, -2014, -1694, -1369, -1046, -730, -431, -155, 89, 296, 458, 569, 623, 616, 542, 400, 187, -97, -454, -882, -1379, -1940, -2561, + -3237, -3961, -4726, -5523, -6343, -7177, -8015, -8847, -9663, -10452, -11205, -11913, -12566, -13156, -13676, -14119, -14479, -14752, -14935, -15025, -15023, -14927, -14741, -14467, -14110, -13674, -13166, -12593, -11963, -11286, -10569, -9824, + -9060, -8287, -7515, -6754, -6014, -5302, -4629, -4001, -3424, -2906, -2450, -2060, -1739, -1488, -1308, -1198, -1156, -1179, -1263, -1405, -1598, -1837, -2115, -2425, -2760, -3114, -3478, -3847, -4212, -4569, -4911, -5232, + -5530, -5799, -6037, -6241, -6412, -6549, -6651, -6721, -6762, -6775, -6764, -6735, -6691, -6637, -6580, -6523, -6472, -6434, -6412, -6411, -6436, -6490, -6576, -6696, -6852, -7044, -7273, -7538, -7835, -8163, -8519, -8897, + -9294, -9704, -10120, -10537, -10948, -11347, -11726, -12080, -12402, -12686, -12927, -13119, -13259, -13344, -13371, -13337, -13244, -13090, -12877, -12608, -12285, -11913, -11496, -11041, -10552, -10039, -9506, -8963, -8417, -7875, -7345, -6836, + -6353, -5903, -5493, -5127, -4810, -4545, -4335, -4181, -4083, -4040, -4051, -4112, -4219, -4368, -4553, -4766, -5000, -5248, -5501, -5749, -5985, -6199, -6383, -6528, -6625, -6669, -6651, -6566, -6409, -6177, -5865, -5474, + -5001, -4449, -3819, -3114, -2339, -1499, -601, 348, 1340, 2366, 3416, 4481, 5550, 6614, 7661, 8683, 9669, 10610, 11498, 12325, 13084, 13770, 14377, 14902, 15342, 15696, 15963, 16144, 16242, 16259, 16200, 16070, + 15874, 15618, 15312, 14960, 14573, 14157, 13721, 13273, 12821, 12372, 11933, 11511, 11112, 10739, 10399, 10093, 9824, 9594, 9403, 9251, 9136, 9057, 9011, 8995, 9004, 9034, 9081, 9139, 9203, 9268, 9330, 9383, + 9423, 9447, 9450, 9431, 9386, 9315, 9217, 9093, 8943, 8770, 8575, 8362, 8135, 7899, 7658, 7417, 7183, 6961, 6756, 6575, 6422, 6304, 6223, 6186, 6195, 6253, 6361, 6521, 6733, 6996, 7309, 7667, + 8069, 8508, 8981, 9479, 9998, 10529, 11064, 11595, 12115, 12614, 13084, 13518, 13907, 14244, 14523, 14739, 14885, 14958, 14956, 14876, 14718, 14482, 14170, 13785, 13331, 12812, 12235, 11606, 10934, 10227, 9494, 8744, + 7987, 7233, 6492, 5774, 5088, 4442, 3846, 3305, 2828, 2420, 2086, 1828, 1649, 1551, 1533, 1594, 1732, 1942, 2221, 2562, 2959, 3405, 3892, 4410, 4952, 5509, 6070, 6628, 7173, 7698, 8193, 8652, + 9068, 9436, 9751, 10009, 10208, 10346, 10423, 10438, 10395, 10296, 10144, 9944, 9701, 9421, 9112, 8780, 8432, 8076, 7720, 7371, 7037, 6724, 6438, 6186, 5972, 5802, 5677, 5602, 5578, 5605, 5683, 5812, + 5988, 6209, 6472, 6772, 7103, 7461, 7839, 8231, 8631, 9032, 9428, 9813, 10181, 10526, 10845, 11132, 11383, 11598, 11772, 11906, 11999, 12051, 12064, 12040, 11983, 11895, 11780, 11645, 11492, 11329, 11159, 10990, + 10826, 10672, 10534, 10415, 10320, 10253, 10215, 10209, 10235, 10294, 10384, 10505, 10653, 10825, 11018, 11225, 11441, 11660, 11876, 12081, 12268, 12430, 12559, 12649, 12693, 12684, 12617, 12487, 12289, 12021, 11680, 11264, + 10775, 10212, 9579, 8877, 8113, 7291, 6417, 5500, 4548, 3569, 2572, 1569, 569, -416, -1379, -2307, -3191, -4021, -4789, -5486, -6105, -6641, -7088, -7442, -7700, -7860, -7924, -7891, -7764, -7546, -7243, -6860, + -6404, -5882, -5303, -4676, -4010, -3316, -2602, -1880, -1159, -448, 241, 903, 1529, 2112, 2645, 3123, 3542, 3899, 4192, 4420, 4584, 4683, 4722, 4702, 4629, 4507, 4341, 4138, 3904, 3647, 3372, 3087, + 2799, 2514, 2239, 1979, 1740, 1526, 1340, 1187, 1066, 981, 932, 916, 935, 984, 1061, 1162, 1284, 1420, 1567, 1718, 1868, 2011, 2142, 2256, 2346, 2409, 2440, 2436, 2394, 2312, 2189, 2024, + 1817, 1571, 1286, 967, 616, 239, -160, -576, -1002, -1432, -1860, -2280, -2684, -3067, -3422, -3745, -4029, -4271, -4468, -4615, -4713, -4758, -4752, -4694, -4588, -4436, -4241, -4008, -3742, -3449, -3137, -2810, + -2479, -2148, -1828, -1524, -1244, -996, -785, -619, -501, -437, -431, -484, -598, -774, -1011, -1308, -1660, -2065, -2518, -3012, -3540, -4097, -4672, -5258, -5845, -6425, -6988, -7524, -8026, -8484, -8890, -9237, + -9518, -9728, -9862, -9916, -9888, -9776, -9580, -9303, -8945, -8511, -8005, -7433, -6802, -6119, -5392, -4632, -3846, -3045, -2238, -1436, -649, 114, 845, 1534, 2174, 2758, 3279, 3733, 4115, 4423, 4655, 4810, + 4889, 4893, 4826, 4691, 4492, 4236, 3929, 3577, 3189, 2771, 2333, 1881, 1425, 971, 528, 103, -298, -669, -1005, -1302, -1555, -1763, -1924, -2038, -2103, -2122, -2096, -2029, -1923, -1782, -1612, -1416, + -1201, -972, -733, -492, -251, -17, 204, 412, 602, 770, 916, 1037, 1133, 1205, 1253, 1280, 1289, 1281, 1262, 1235, 1205, 1178, 1159, 1152, 1164, 1199, 1263, 1360, 1493, 1667, 1884, 2145, + 2452, 2805, 3204, 3645, 4128, 4647, 5199, 5778, 6378, 6992, 7613, 8233, 8844, 9436, 10002, 10533, 11021, 11458, 11835, 12147, 12387, 12550, 12631, 12627, 12535, 12355, 12087, 11731, 11290, 10767, 10168, 9498, + 8764, 7972, 7133, 6253, 5344, 4415, 3475, 2535, 1605, 694, -187, -1031, -1829, -2575, -3261, -3883, -4436, -4917, -5324, -5656, -5914, -6100, -6216, -6267, -6257, -6191, -6077, -5923, -5735, -5522, -5293, -5057, + -4822, -4596, -4389, -4206, -4057, -3946, -3881, -3864, -3901, -3993, -4143, -4350, -4615, -4935, -5308, -5730, -6197, -6703, -7242, -7807, -8391, -8986, -9585, -10180, -10762, -11325, -11860, -12362, -12823, -13238, -13603, -13912, + -14163, -14355, -14484, -14552, -14558, -14505, -14395, -14231, -14017, -13758, -13460, -13128, -12767, -12386, -11989, -11584, -11176, -10773, -10378, -9998, -9637, -9298, -8986, -8702, -8449, -8226, -8034, -7872, -7738, -7630, -7545, -7479, + -7427, -7386, -7350, -7313, -7272, -7220, -7152, -7065, -6953, -6812, -6640, -6433, -6190, -5911, -5594, -5241, -4853, -4433, -3984, -3512, -3019, -2514, -2001, -1488, -981, -489, -19, 420, 823, 1182, 1490, 1740, + 1927, 2046, 2093, 2066, 1962, 1780, 1521, 1186, 778, 301, -240, -841, -1494, -2190, -2923, -3681, -4457, -5240, -6019, -6785, -7527, -8235, -8899, -9512, -10064, -10548, -10957, -11286, -11530, -11687, -11754, -11731, + -11618, -11416, -11130, -10763, -10320, -9808, -9234, -8605, -7932, -7222, -6486, -5733, -4973, -4217, -3472, -2749, -2057, -1402, -793, -236, 262, 700, 1074, 1380, 1617, 1787, 1889, 1926, 1902, 1820, 1684, 1501, + 1277, 1019, 732, 425, 105, -220, -546, -864, -1168, -1453, -1713, -1945, -2145, -2310, -2439, -2530, -2584, -2601, -2583, -2533, -2454, -2350, -2226, -2085, -1934, -1778, -1622, -1473, -1334, -1212, -1112, -1037, + -991, -978, -999, -1056, -1150, -1282, -1449, -1650, -1883, -2144, -2429, -2733, -3052, -3379, -3709, -4035, -4350, -4649, -4925, -5173, -5386, -5560, -5690, -5772, -5805, -5784, -5710, -5582, -5401, -5168, -4886, -4559, + -4191, -3787, -3354, -2898, -2425, -1944, -1463, -988, -529, -93, 312, 681, 1006, 1281, 1501, 1662, 1761, 1795, 1764, 1667, 1506, 1282, 999, 661, 274, -156, -623, -1118, -1635, -2163, -2694, -3218, + -3728, -4213, -4665, -5077, -5440, -5747, -5994, -6174, -6284, -6321, -6283, -6170, -5983, -5723, -5393, -4998, -4543, -4035, -3481, -2888, -2265, -1621, -967, -312, 334, 963, 1564, 2128, 2646, 3111, 3516, 3853, + 4119, 4308, 4417, 4445, 4391, 4255, 4039, 3745, 3377, 2940, 2439, 1880, 1272, 620, -65, -777, -1508, -2248, -2989, -3723, -4442, -5140, -5810, -6445, -7042, -7595, -8101, -8558, -8965, -9321, -9626, -9881, + -10089, -10253, -10375, -10460, -10512, -10536, -10538, -10521, -10491, -10453, -10412, -10372, -10338, -10311, -10295, -10293, -10305, -10331, -10373, -10428, -10494, -10571, -10653, -10738, -10821, -10897, -10961, -11008, -11033, -11030, -10994, -10919, + -10801, -10636, -10420, -10151, -9825, -9442, -9001, -8503, -7949, -7342, -6685, -5982, -5238, -4460, -3654, -2828, -1990, -1147, -309, 515, 1317, 2089, 2821, 3507, 4137, 4706, 5207, 5635, 5986, 6258, 6447, 6554, + 6578, 6521, 6386, 6176, 5897, 5554, 5155, 4706, 4216, 3694, 3150, 2592, 2031, 1475, 935, 420, -62, -504, -897, -1236, -1515, -1730, -1878, -1957, -1965, -1904, -1774, -1579, -1323, -1010, -646, -237, + 207, 681, 1176, 1682, 2190, 2693, 3180, 3644, 4076, 4469, 4815, 5109, 5346, 5520, 5630, 5672, 5646, 5552, 5392, 5166, 4879, 4535, 4138, 3695, 3213, 2698, 2158, 1600, 1034, 466, -95, -642, + -1168, -1666, -2130, -2555, -2935, -3268, -3550, -3779, -3955, -4078, -4148, -4167, -4139, -4066, -3952, -3804, -3625, -3421, -3199, -2965, -2724, -2482, -2246, -2020, -1811, -1621, -1455, -1316, -1207, -1128, -1081, -1066, + -1081, -1125, -1195, -1289, -1401, -1529, -1667, -1809, -1950, -2086, -2209, -2316, -2400, -2456, -2481, -2471, -2421, -2331, -2199, -2022, -1803, -1542, -1241, -902, -530, -129, 294, 736, 1189, 1647, 2101, 2544, + 2969, 3369, 3736, 4063, 4343, 4572, 4743, 4852, 4896, 4873, 4782, 4621, 4393, 4098, 3741, 3325, 2857, 2342, 1787, 1201, 592, -30, -656, -1276, -1880, -2457, -2999, -3494, -3936, -4314, -4623, -4854, + -5002, -5064, -5034, -4912, -4697, -4387, -3987, -3497, -2922, -2268, -1541, -748, 103, 1004, 1944, 2915, 3906, 4906, 5907, 6897, 7867, 8808, 9711, 10569, 11373, 12119, 12802, 13416, 13959, 14430, 14828, 15152, + 15405, 15589, 15707, 15764, 15765, 15716, 15622, 15491, 15329, 15144, 14942, 14730, 14516, 14304, 14102, 13915, 13746, 13600, 13479, 13386, 13321, 13286, 13279, 13300, 13345, 13413, 13499, 13599, 13709, 13823, 13937, 14045, + 14141, 14220, 14277, 14308, 14308, 14272, 14199, 14085, 13930, 13731, 13489, 13205, 12881, 12518, 12121, 11694, 11240, 10766, 10277, 9780, 9280, 8785, 8302, 7836, 7394, 6982, 6606, 6270, 5979, 5737, 5547, 5409, + 5325, 5296, 5319, 5393, 5515, 5682, 5888, 6128, 6396, 6685, 6988, 7299, 7608, 7909, 8193, 8454, 8684, 8878, 9027, 9129, 9177, 9170, 9103, 8977, 8789, 8542, 8237, 7876, 7465, 7007, 6509, 5977, + 5419, 4843, 4257, 3671, 3092, 2531, 1996, 1496, 1039, 632, 284, 0, -214, -356, -421, -407, -313, -140, 109, 434, 830, 1291, 1812, 2385, 3002, 3655, 4335, 5032, 5736, 6437, 7124, 7789, + 8421, 9011, 9551, 10034, 10451, 10798, 11069, 11260, 11370, 11395, 11337, 11195, 10971, 10670, 10294, 9848, 9339, 8773, 8157, 7499, 6807, 6088, 5352, 4606, 3858, 3116, 2388, 1679, 996, 344, -272, -850, + -1387, -1880, -2329, -2734, -3096, -3418, -3700, -3947, -4163, -4351, -4516, -4664, -4799, -4927, -5052, -5178, -5312, -5456, -5613, -5788, -5981, -6195, -6430, -6687, -6963, -7258, -7569, -7893, -8226, -8564, -8902, -9236, + -9558, -9864, -10148, -10404, -10627, -10812, -10953, -11048, -11091, -11081, -11015, -10893, -10713, -10477, -10187, -9844, -9453, -9018, -8545, -8039, -7507, -6958, -6397, -5835, -5278, -4736, -4217, -3729, -3280, -2878, -2529, -2238, + -2013, -1856, -1770, -1759, -1823, -1961, -2173, -2457, -2807, -3220, -3690, -4210, -4773, -5370, -5993, -6632, -7278, -7921, -8551, -9159, -9735, -10271, -10757, -11188, -11555, -11853, -12077, -12224, -12292, -12278, -12183, -12009, + -11756, -11430, -11034, -10575, -10058, -9490, -8881, -8238, -7569, -6885, -6195, -5506, -4830, -4173, -3544, -2951, -2401, -1898, -1449, -1056, -724, -454, -246, -100, -16, 10, -17, -96, -220, -384, -580, -802, + -1043, -1295, -1551, -1803, -2044, -2266, -2465, -2632, -2764, -2855, -2903, -2904, -2856, -2759, -2613, -2418, -2177, -1891, -1566, -1205, -813, -396, 41, 491, 949, 1407, 1860, 2301, 2724, 3124, 3496, 3836, + 4140, 4405, 4629, 4810, 4950, 5047, 5103, 5120, 5102, 5051, 4972, 4869, 4747, 4612, 4469, 4324, 4182, 4048, 3929, 3827, 3748, 3695, 3670, 3676, 3714, 3785, 3887, 4019, 4179, 4364, 4570, 4791, + 5023, 5259, 5493, 5718, 5927, 6113, 6268, 6385, 6458, 6481, 6446, 6351, 6189, 5958, 5656, 5281, 4832, 4311, 3720, 3061, 2340, 1561, 731, -142, -1052, -1988, -2942, -3904, -4862, -5808, -6729, -7617, + -8460, -9250, -9978, -10634, -11212, -11706, -12109, -12418, -12630, -12742, -12755, -12670, -12488, -12212, -11848, -11400, -10875, -10281, -9627, -8920, -8170, -7388, -6582, -5764, -4943, -4128, -3330, -2556, -1814, -1113, -458, 144, + 690, 1177, 1602, 1964, 2263, 2500, 2677, 2796, 2863, 2882, 2857, 2796, 2704, 2588, 2454, 2311, 2165, 2021, 1888, 1769, 1671, 1599, 1555, 1543, 1566, 1623, 1717, 1846, 2009, 2204, 2428, 2678, + 2948, 3235, 3532, 3836, 4139, 4436, 4722, 4990, 5236, 5455, 5643, 5795, 5909, 5982, 6013, 6001, 5947, 5851, 5715, 5542, 5336, 5100, 4840, 4560, 4268, 3967, 3666, 3370, 3086, 2820, 2578, 2366, + 2188, 2049, 1953, 1902, 1899, 1945, 2039, 2182, 2372, 2605, 2878, 3188, 3527, 3892, 4275, 4670, 5068, 5462, 5846, 6210, 6548, 6853, 7117, 7334, 7500, 7610, 7659, 7644, 7566, 7421, 7212, 6939, + 6605, 6214, 5771, 5281, 4751, 4188, 3601, 2997, 2386, 1777, 1180, 602, 55, -453, -916, -1325, -1673, -1955, -2164, -2298, -2354, -2330, -2226, -2043, -1782, -1447, -1043, -575, -50, 525, 1143, 1794, + 2469, 3158, 3852, 4541, 5215, 5864, 6479, 7053, 7576, 8042, 8444, 8778, 9040, 9225, 9333, 9362, 9314, 9189, 8990, 8721, 8387, 7993, 7546, 7052, 6519, 5954, 5366, 4764, 4154, 3545, 2946, 2362, + 1802, 1271, 774, 316, -98, -467, -788, -1061, -1286, -1462, -1592, -1678, -1724, -1733, -1710, -1659, -1586, -1495, -1392, -1282, -1171, -1064, -964, -876, -803, -747, -712, -698, -706, -736, -786, -855, + -940, -1038, -1146, -1258, -1371, -1478, -1575, -1657, -1717, -1751, -1754, -1720, -1647, -1530, -1366, -1154, -892, -579, -217, 192, 648, 1145, 1681, 2250, 2845, 3460, 4088, 4721, 5351, 5969, 6566, 7135, + 7667, 8153, 8586, 8959, 9266, 9501, 9660, 9738, 9734, 9645, 9473, 9216, 8878, 8463, 7973, 7416, 6796, 6122, 5402, 4645, 3859, 3055, 2242, 1430, 630, -148, -897, -1607, -2270, -2877, -3424, -3905, + -4314, -4649, -4908, -5090, -5195, -5225, -5183, -5072, -4897, -4664, -4379, -4050, -3685, -3293, -2880, -2458, -2034, -1617, -1215, -837, -489, -179, 87, 306, 472, 583, 637, 633, 571, 452, 280, 56, + -212, -523, -870, -1245, -1642, -2054, -2472, -2889, -3298, -3690, -4059, -4397, -4698, -4957, -5169, -5329, -5434, -5483, -5473, -5404, -5278, -5094, -4856, -4568, -4232, -3854, -3440, -2994, -2523, -2033, -1532, -1024, + -518, -18, 467, 936, 1382, 1801, 2190, 2546, 2867, 3152, 3402, 3616, 3796, 3945, 4066, 4161, 4236, 4294, 4341, 4381, 4421, 4464, 4516, 4583, 4667, 4774, 4907, 5068, 5259, 5482, 5737, 6022, + 6338, 6680, 7047, 7434, 7836, 8247, 8661, 9073, 9473, 9856, 10213, 10537, 10820, 11055, 11236, 11356, 11409, 11390, 11296, 11123, 10869, 10534, 10116, 9619, 9043, 8394, 7675, 6892, 6052, 5163, 4233, 3271, + 2287, 1292, 294, -694, -1664, -2605, -3507, -4360, -5158, -5890, -6552, -7136, -7639, -8056, -8385, -8626, -8777, -8841, -8821, -8720, -8543, -8296, -7986, -7621, -7209, -6760, -6282, -5785, -5279, -4774, -4279, -3803, + -3355, -2942, -2573, -2254, -1989, -1784, -1642, -1565, -1555, -1611, -1732, -1917, -2162, -2463, -2816, -3215, -3653, -4125, -4623, -5140, -5669, -6201, -6731, -7252, -7756, -8238, -8693, -9117, -9505, -9855, -10164, -10432, + -10658, -10843, -10988, -11097, -11170, -11214, -11231, -11227, -11206, -11174, -11137, -11099, -11067, -11046, -11040, -11053, -11090, -11153, -11246, -11369, -11524, -11710, -11928, -12175, -12448, -12745, -13062, -13394, -13736, -14083, -14427, -14764, + -15087, -15389, -15664, -15907, -16111, -16271, -16384, -16445, -16451, -16400, -16290, -16121, -15894, -15611, -15273, -14884, -14449, -13973, -13462, -12921, -12359, -11783, -11200, -10618, -10047, -9492, -8963, -8466, -8008, -7595, -7232, -6924, + -6675, -6486, -6360, -6297, -6295, -6354, -6470, -6639, -6856, -7115, -7410, -7733, -8076, -8430, -8787, -9138, -9474, -9786, -10066, -10304, -10495, -10630, -10704, -10712, -10650, -10515, -10304, -10018, -9657, -9222, -8718, -8147, + -7515, -6828, -6094, -5321, -4516, -3690, -2852, -2011, -1178, -362, 425, 1177, 1884, 2538, 3130, 3656, 4108, 4483, 4778, 4989, 5116, 5159, 5118, 4998, 4800, 4529, 4191, 3792, 3338, 2838, 2298, 1727, + 1134, 527, -85, -695, -1296, -1879, -2439, -2968, -3463, -3918, -4331, -4698, -5018, -5291, -5516, -5695, -5829, -5922, -5976, -5996, -5985, -5949, -5892, -5820, -5738, -5650, -5561, -5476, -5399, -5333, -5281, -5245, + -5226, -5226, -5244, -5279, -5329, -5393, -5467, -5547, -5629, -5709, -5781, -5840, -5880, -5897, -5884, -5836, -5750, -5619, -5441, -5212, -4930, -4594, -4202, -3755, -3254, -2702, -2101, -1455, -770, -51, 695, 1461, + 2240, 3023, 3801, 4566, 5309, 6022, 6696, 7323, 7895, 8406, 8849, 9220, 9514, 9727, 9858, 9905, 9870, 9752, 9555, 9283, 8939, 8531, 8064, 7547, 6986, 6393, 5774, 5141, 4502, 3868, 3248, 2651, + 2085, 1560, 1083, 659, 295, -4, -236, -399, -490, -511, -461, -345, -165, 73, 365, 704, 1083, 1493, 1927, 2376, 2831, 3283, 3723, 4142, 4533, 4887, 5198, 5460, 5666, 5813, 5897, 5916, + 5868, 5754, 5574, 5330, 5026, 4665, 4253, 3795, 3298, 2768, 2213, 1640, 1059, 476, -100, -663, -1204, -1717, -2195, -2634, -3028, -3374, -3669, -3910, -4096, -4228, -4306, -4332, -4309, -4239, -4127, -3978, + -3797, -3590, -3362, -3120, -2869, -2617, -2368, -2128, -1903, -1697, -1514, -1357, -1229, -1132, -1066, -1032, -1029, -1055, -1109, -1187, -1285, -1400, -1526, -1659, -1792, -1922, -2041, -2146, -2229, -2288, -2316, -2311, + -2270, -2188, -2066, -1902, -1697, -1450, -1164, -843, -489, -106, 298, 720, 1153, 1589, 2022, 2443, 2846, 3222, 3565, 3867, 4121, 4322, 4465, 4544, 4557, 4500, 4372, 4172, 3902, 3563, 3158, 2691, + 2167, 1593, 975, 321, -359, -1057, -1764, -2470, -3164, -3836, -4476, -5076, -5625, -6116, -6540, -6891, -7163, -7352, -7452, -7463, -7382, -7210, -6948, -6599, -6167, -5656, -5073, -4425, -3718, -2963, -2168, -1343, + -498, 357, 1212, 2058, 2884, 3680, 4439, 5153, 5814, 6416, 6954, 7424, 7822, 8147, 8398, 8575, 8680, 8714, 8682, 8587, 8434, 8230, 7980, 7691, 7371, 7025, 6662, 6289, 5911, 5537, 5171, 4820, + 4487, 4177, 3893, 3638, 3413, 3219, 3054, 2919, 2811, 2728, 2666, 2621, 2589, 2565, 2544, 2520, 2489, 2444, 2381, 2295, 2182, 2038, 1860, 1646, 1393, 1100, 768, 398, -9, -451, -924, -1425, + -1949, -2490, -3043, -3601, -4158, -4707, -5241, -5755, -6241, -6693, -7105, -7473, -7791, -8057, -8266, -8418, -8510, -8543, -8517, -8435, -8298, -8111, -7878, -7604, -7295, -6958, -6600, -6228, -5850, -5473, -5105, -4753, + -4425, -4128, -3866, -3647, -3474, -3350, -3280, -3264, -3303, -3396, -3542, -3739, -3982, -4267, -4588, -4939, -5312, -5699, -6093, -6484, -6863, -7222, -7552, -7843, -8088, -8279, -8409, -8471, -8460, -8372, -8203, -7952, + -7617, -7198, -6697, -6117, -5462, -4736, -3946, -3099, -2202, -1264, -295, 696, 1699, 2704, 3700, 4678, 5628, 6540, 7406, 8217, 8967, 9649, 10258, 10789, 11240, 11609, 11895, 12098, 12220, 12263, 12232, 12131, + 11966, 11742, 11467, 11148, 10793, 10409, 10006, 9590, 9170, 8754, 8349, 7960, 7595, 7258, 6954, 6686, 6458, 6270, 6124, 6019, 5955, 5931, 5942, 5986, 6060, 6159, 6278, 6413, 6557, 6707, 6856, 7000, + 7135, 7255, 7358, 7441, 7500, 7535, 7543, 7526, 7484, 7418, 7330, 7223, 7101, 6968, 6828, 6687, 6550, 6423, 6311, 6220, 6155, 6122, 6125, 6168, 6256, 6391, 6575, 6810, 7095, 7431, 7815, 8246, + 8720, 9233, 9778, 10352, 10946, 11553, 12166, 12777, 13377, 13958, 14511, 15029, 15502, 15925, 16289, 16589, 16819, 16976, 17055, 17054, 16973, 16810, 16568, 16249, 15855, 15392, 14866, 14282, 13648, 12973, 12265, 11533, + 10788, 10038, 9295, 8566, 7863, 7194, 6568, 5991, 5473, 5017, 4631, 4316, 4077, 3915, 3830, 3822, 3888, 4025, 4229, 4496, 4817, 5188, 5599, 6043, 6511, 6993, 7482, 7968, 8442, 8895, 9320, 9709, + 10055, 10353, 10597, 10783, 10908, 10971, 10970, 10906, 10780, 10595, 10354, 10061, 9721, 9341, 8927, 8485, 8024, 7550, 7072, 6597, 6133, 5686, 5263, 4871, 4516, 4200, 3930, 3707, 3535, 3413, 3343, 3323, + 3353, 3429, 3548, 3706, 3899, 4121, 4366, 4629, 4903, 5181, 5459, 5728, 5984, 6221, 6434, 6619, 6772, 6890, 6971, 7013, 7017, 6981, 6908, 6800, 6659, 6488, 6292, 6075, 5842, 5598, 5349, 5101, + 4859, 4628, 4414, 4222, 4054, 3916, 3810, 3739, 3703, 3703, 3739, 3809, 3911, 4043, 4200, 4378, 4571, 4773, 4979, 5180, 5370, 5541, 5687, 5800, 5874, 5901, 5876, 5794, 5650, 5440, 5162, 4814, + 4396, 3908, 3353, 2731, 2049, 1310, 522, -309, -1176, -2069, -2979, -3897, -4812, -5714, -6593, -7438, -8239, -8987, -9673, -10288, -10825, -11277, -11639, -11907, -12077, -12147, -12117, -11988, -11761, -11440, -11028, -10532, + -9958, -9312, -8605, -7843, -7037, -6197, -5333, -4454, -3571, -2693, -1830, -992, -185, 580, 1301, 1969, 2580, 3131, 3618, 4041, 4400, 4694, 4926, 5098, 5214, 5279, 5298, 5277, 5221, 5138, 5034, 4916, + 4791, 4666, 4546, 4438, 4347, 4277, 4233, 4218, 4233, 4281, 4362, 4476, 4621, 4795, 4996, 5220, 5463, 5720, 5986, 6256, 6523, 6783, 7030, 7257, 7459, 7633, 7772, 7874, 7935, 7952, 7925, 7852, + 7733, 7570, 7363, 7116, 6831, 6513, 6167, 5798, 5412, 5014, 4612, 4210, 3817, 3438, 3080, 2747, 2446, 2181, 1955, 1772, 1635, 1543, 1499, 1502, 1549, 1639, 1769, 1933, 2128, 2347, 2584, 2832, + 3085, 3334, 3573, 3793, 3987, 4148, 4269, 4344, 4368, 4335, 4242, 4086, 3866, 3579, 3228, 2812, 2335, 1800, 1212, 576, -100, -810, -1547, -2300, -3062, -3824, -4574, -5306, -6008, -6673, -7292, -7857, + -8361, -8798, -9162, -9451, -9660, -9788, -9833, -9798, -9682, -9491, -9226, -8895, -8502, -8056, -7564, -7034, -6476, -5899, -5314, -4729, -4155, -3601, -3076, -2589, -2148, -1761, -1432, -1169, -974, -852, -805, -833, + -936, -1113, -1362, -1678, -2058, -2496, -2985, -3520, -4092, -4694, -5318, -5956, -6599, -7239, -7869, -8482, -9069, -9626, -10147, -10626, -11060, -11446, -11782, -12067, -12300, -12483, -12616, -12702, -12744, -12747, -12713, -12648, + -12558, -12446, -12320, -12183, -12041, -11900, -11764, -11636, -11521, -11422, -11340, -11278, -11235, -11213, -11210, -11224, -11254, -11296, -11347, -11402, -11457, -11507, -11547, -11571, -11575, -11552, -11498, -11408, -11278, -11105, -10885, -10617, + -10298, -9930, -9511, -9043, -8530, -7973, -7378, -6750, -6093, -5416, -4725, -4028, -3333, -2649, -1984, -1347, -746, -191, 312, 756, 1133, 1438, 1667, 1814, 1878, 1857, 1749, 1557, 1281, 924, 492, -11, + -580, -1206, -1882, -2600, -3349, -4120, -4905, -5691, -6471, -7233, -7969, -8670, -9327, -9933, -10481, -10966, -11382, -11728, -12000, -12196, -12319, -12367, -12345, -12256, -12104, -11894, -11634, -11331, -10991, -10625, -10239, -9843, + -9446, -9056, -8682, -8332, -8013, -7732, -7495, -7307, -7172, -7094, -7074, -7114, -7213, -7371, -7585, -7851, -8166, -8525, -8922, -9349, -9801, -10270, -10748, -11227, -11700, -12159, -12596, -13005, -13378, -13710, -13996, -14232, + -14413, -14538, -14604, -14612, -14561, -14453, -14289, -14073, -13809, -13501, -13154, -12773, -12366, -11937, -11494, -11043, -10590, -10141, -9702, -9278, -8875, -8496, -8145, -7824, -7535, -7280, -7059, -6870, -6713, -6585, -6484, -6406, + -6347, -6302, -6267, -6236, -6204, -6166, -6116, -6050, -5963, -5851, -5710, -5538, -5331, -5090, -4813, -4501, -4155, -3777, -3370, -2938, -2485, -2018, -1542, -1064, -590, -129, 312, 727, 1107, 1445, 1735, 1969, + 2142, 2249, 2286, 2249, 2137, 1947, 1680, 1337, 921, 434, -118, -731, -1398, -2111, -2862, -3642, -4442, -5251, -6059, -6857, -7633, -8378, -9081, -9734, -10328, -10854, -11307, -11679, -11967, -12166, -12273, -12288, + -12210, -12041, -11784, -11441, -11018, -10520, -9955, -9330, -8654, -7935, -7183, -6407, -5618, -4825, -4038, -3267, -2519, -1804, -1128, -499, 77, 596, 1055, 1450, 1780, 2045, 2245, 2382, 2459, 2479, 2447, 2368, + 2248, 2094, 1912, 1710, 1494, 1272, 1050, 837, 637, 457, 302, 176, 84, 27, 10, 31, 93, 193, 331, 505, 711, 945, 1204, 1482, 1775, 2076, 2381, 2684, 2978, 3260, 3523, 3764, + 3977, 4161, 4311, 4427, 4506, 4549, 4557, 4529, 4470, 4381, 4266, 4131, 3978, 3815, 3647, 3480, 3320, 3174, 3047, 2946, 2876, 2843, 2851, 2904, 3005, 3157, 3360, 3616, 3924, 4283, 4690, 5142, + 5635, 6163, 6722, 7304, 7902, 8510, 9118, 9721, 10308, 10873, 11408, 11905, 12357, 12759, 13105, 13391, 13611, 13765, 13850, 13866, 13813, 13693, 13509, 13264, 12964, 12614, 12221, 11793, 11336, 10860, 10374, 9886, + 9406, 8942, 8504, 8099, 7735, 7419, 7159, 6958, 6822, 6753, 6755, 6827, 6969, 7181, 7459, 7800, 8199, 8649, 9144, 9677, 10238, 10820, 11412, 12004, 12588, 13153, 13690, 14189, 14642, 15041, 15377, 15645, + 15839, 15955, 15988, 15936, 15799, 15576, 15268, 14878, 14408, 13865, 13251, 12575, 11842, 11060, 10238, 9383, 8503, 7608, 6706, 5805, 4914, 4038, 3186, 2364, 1577, 830, 126, -530, -1139, -1699, -2209, -2671, + -3086, -3456, -3784, -4075, -4332, -4560, -4764, -4948, -5117, -5278, -5433, -5588, -5746, -5912, -6087, -6274, -6475, -6690, -6920, -7162, -7417, -7681, -7951, -8225, -8497, -8762, -9017, -9255, -9471, -9660, -9816, -9933, + -10007, -10033, -10008, -9927, -9789, -9592, -9334, -9016, -8639, -8204, -7716, -7176, -6591, -5966, -5308, -4622, -3918, -3203, -2486, -1775, -1080, -408, 230, 829, 1380, 1875, 2309, 2676, 2971, 3191, 3334, 3398, + 3383, 3290, 3122, 2882, 2575, 2205, 1780, 1306, 792, 247, -320, -901, -1485, -2063, -2626, -3163, -3667, -4130, -4542, -4899, -5195, -5424, -5583, -5670, -5683, -5622, -5489, -5286, -5017, -4685, -4296, -3858, + -3376, -2859, -2315, -1753, -1182, -610, -48, 496, 1015, 1500, 1944, 2340, 2683, 2967, 3190, 3349, 3441, 3468, 3428, 3324, 3159, 2936, 2660, 2337, 1973, 1575, 1149, 704, 246, -213, -670, -1116, + -1542, -1943, -2312, -2644, -2933, -3176, -3370, -3512, -3601, -3638, -3622, -3556, -3441, -3282, -3082, -2845, -2578, -2285, -1973, -1647, -1314, -981, -652, -334, -31, 250, 507, 735, 933, 1098, 1229, 1326, + 1389, 1419, 1419, 1392, 1341, 1270, 1184, 1088, 987, 887, 793, 711, 646, 603, 586, 600, 647, 731, 854, 1015, 1217, 1457, 1734, 2046, 2389, 2759, 3151, 3559, 3977, 4398, 4815, 5221, + 5607, 5967, 6293, 6578, 6815, 6998, 7123, 7183, 7176, 7099, 6950, 6728, 6435, 6071, 5639, 5144, 4590, 3983, 3331, 2641, 1921, 1182, 431, -319, -1061, -1783, -2476, -3129, -3735, -4283, -4766, -5176, + -5509, -5758, -5920, -5991, -5971, -5859, -5657, -5365, -4989, -4532, -4001, -3402, -2743, -2032, -1278, -490, 319, 1143, 1970, 2791, 3595, 4373, 5117, 5819, 6470, 7066, 7600, 8068, 8467, 8794, 9050, 9233, + 9344, 9387, 9365, 9281, 9140, 8947, 8710, 8435, 8128, 7797, 7449, 7090, 6729, 6371, 6022, 5689, 5375, 5086, 4823, 4591, 4390, 4222, 4086, 3981, 3905, 3856, 3831, 3825, 3835, 3855, 3880, 3906, + 3926, 3935, 3929, 3901, 3849, 3768, 3654, 3504, 3318, 3094, 2831, 2529, 2191, 1819, 1415, 984, 530, 57, -427, -918, -1410, -1895, -2366, -2818, -3243, -3636, -3990, -4302, -4565, -4776, -4932, -5031, + -5072, -5054, -4979, -4846, -4661, -4425, -4143, -3820, -3463, -3078, -2671, -2250, -1824, -1398, -983, -584, -209, 134, 441, 705, 921, 1085, 1195, 1249, 1245, 1183, 1066, 895, 675, 408, 102, -237, + -604, -990, -1387, -1786, -2179, -2556, -2908, -3227, -3505, -3733, -3905, -4014, -4054, -4021, -3911, -3723, -3454, -3106, -2678, -2174, -1598, -953, -247, 514, 1323, 2172, 3050, 3949, 4858, 5767, 6666, 7545, + 8394, 9204, 9966, 10672, 11314, 11886, 12383, 12799, 13133, 13381, 13543, 13620, 13612, 13521, 13353, 13110, 12800, 12426, 11998, 11521, 11004, 10454, 9881, 9292, 8694, 8097, 7506, 6930, 6373, 5842, 5342, 4875, + 4446, 4055, 3705, 3395, 3125, 2892, 2696, 2533, 2399, 2290, 2202, 2129, 2067, 2011, 1956, 1896, 1828, 1746, 1648, 1531, 1391, 1227, 1038, 824, 586, 324, 41, -259, -574, -901, -1232, -1565, + -1893, -2210, -2512, -2791, -3043, -3262, -3444, -3583, -3675, -3719, -3711, -3649, -3534, -3365, -3144, -2872, -2553, -2192, -1792, -1360, -902, -426, 61, 552, 1039, 1511, 1962, 2383, 2766, 3104, 3389, 3616, + 3778, 3873, 3895, 3842, 3713, 3509, 3229, 2876, 2453, 1965, 1417, 815, 167, -518, -1233, -1969, -2716, -3463, -4202, -4922, -5613, -6267, -6875, -7429, -7922, -8347, -8700, -8976, -9172, -9286, -9319, -9269, + -9140, -8934, -8655, -8309, -7901, -7438, -6928, -6379, -5800, -5200, -4588, -3973, -3364, -2769, -2197, -1656, -1153, -693, -282, 74, 375, 617, 799, 920, 983, 989, 941, 843, 701, 519, 304, 63, + -195, -467, -742, -1014, -1275, -1518, -1736, -1923, -2074, -2184, -2249, -2265, -2231, -2145, -2007, -1818, -1579, -1293, -963, -592, -187, 248, 707, 1185, 1675, 2170, 2663, 3150, 3622, 4076, 4505, 4905, + 5273, 5604, 5896, 6149, 6360, 6530, 6660, 6751, 6806, 6829, 6823, 6791, 6740, 6675, 6599, 6520, 6443, 6373, 6314, 6273, 6252, 6256, 6288, 6349, 6442, 6567, 6724, 6911, 7127, 7368, 7631, 7911, + 8203, 8500, 8797, 9087, 9363, 9616, 9841, 10029, 10174, 10269, 10308, 10286, 10198, 10040, 9809, 9504, 9123, 8667, 8138, 7538, 6870, 6141, 5355, 4519, 3642, 2732, 1798, 850, -102, -1048, -1978, -2880, + -3746, -4564, -5327, -6025, -6651, -7198, -7660, -8032, -8312, -8496, -8584, -8577, -8475, -8281, -8001, -7638, -7199, -6691, -6123, -5502, -4838, -4141, -3420, -2686, -1948, -1217, -501, 189, 847, 1465, 2035, 2552, + 3012, 3410, 3743, 4011, 4212, 4348, 4421, 4432, 4385, 4286, 4138, 3949, 3723, 3469, 3192, 2901, 2601, 2299, 2002, 1717, 1448, 1200, 979, 786, 625, 498, 405, 346, 321, 327, 362, 423, + 507, 607, 720, 840, 962, 1080, 1188, 1280, 1353, 1400, 1417, 1401, 1348, 1255, 1122, 946, 728, 469, 171, -164, -533, -933, -1357, -1801, -2260, -2726, -3195, -3658, -4110, -4545, -4955, -5336, + -5682, -5988, -6250, -6465, -6630, -6743, -6805, -6815, -6775, -6686, -6553, -6378, -6167, -5925, -5659, -5374, -5080, -4782, -4488, -4206, -3944, -3708, -3506, -3343, -3227, -3160, -3149, -3194, -3300, -3466, -3694, -3981, + -4325, -4724, -5172, -5664, -6195, -6756, -7341, -7941, -8546, -9149, -9739, -10307, -10845, -11342, -11792, -12186, -12517, -12779, -12966, -13075, -13101, -13043, -12901, -12674, -12364, -11975, -11510, -10974, -10373, -9715, -9007, -8259, + -7478, -6675, -5860, -5042, -4231, -3437, -2670, -1938, -1249, -611, -31, 484, 933, 1311, 1614, 1842, 1995, 2073, 2080, 2017, 1890, 1703, 1462, 1173, 845, 485, 99, -301, -712, -1122, -1525, -1914, + -2281, -2620, -2925, -3193, -3418, -3597, -3730, -3813, -3848, -3834, -3773, -3667, -3519, -3334, -3115, -2867, -2594, -2304, -2000, -1688, -1374, -1063, -759, -466, -189, 68, 305, 520, 710, 877, 1021, 1143, + 1245, 1330, 1401, 1463, 1521, 1578, 1641, 1714, 1803, 1913, 2049, 2216, 2418, 2658, 2941, 3268, 3640, 4059, 4524, 5034, 5586, 6179, 6806, 7465, 8148, 8850, 9563, 10280, 10992, 11692, 12370, 13019, + 13629, 14192, 14701, 15148, 15527, 15831, 16055, 16197, 16252, 16218, 16096, 15885, 15587, 15205, 14743, 14207, 13601, 12933, 12211, 11444, 10641, 9811, 8964, 8110, 7260, 6422, 5608, 4824, 4081, 3386, 2746, 2166, + 1652, 1208, 835, 535, 309, 156, 73, 57, 104, 208, 363, 563, 799, 1064, 1349, 1645, 1944, 2237, 2516, 2772, 2998, 3187, 3334, 3432, 3478, 3468, 3401, 3274, 3087, 2843, 2542, 2189, + 1786, 1339, 854, 337, -204, -764, -1333, -1905, -2471, -3023, -3554, -4057, -4525, -4952, -5333, -5663, -5939, -6157, -6317, -6416, -6455, -6436, -6360, -6230, -6049, -5823, -5555, -5252, -4920, -4564, -4191, -3807, + -3420, -3034, -2657, -2292, -1946, -1623, -1326, -1057, -820, -615, -444, -304, -196, -116, -64, -34, -23, -28, -42, -61, -79, -93, -95, -81, -48, 10, 96, 213, 364, 550, 771, 1028, + 1319, 1641, 1994, 2371, 2769, 3183, 3606, 4033, 4455, 4866, 5258, 5623, 5955, 6244, 6486, 6672, 6797, 6856, 6843, 6757, 6593, 6351, 6031, 5632, 5158, 4611, 3995, 3315, 2579, 1793, 966, 105, + -778, -1676, -2577, -3471, -4349, -5199, -6012, -6779, -7491, -8139, -8717, -9219, -9639, -9974, -10220, -10377, -10444, -10421, -10312, -10120, -9849, -9505, -9095, -8626, -8106, -7546, -6952, -6337, -5708, -5077, -4452, -3843, + -3260, -2709, -2200, -1739, -1332, -983, -697, -477, -324, -239, -221, -270, -381, -552, -779, -1055, -1376, -1735, -2125, -2539, -2969, -3409, -3852, -4290, -4717, -5127, -5514, -5874, -6203, -6498, -6755, -6975, + -7156, -7298, -7404, -7474, -7512, -7521, -7505, -7470, -7419, -7358, -7293, -7230, -7173, -7127, -7099, -7093, -7112, -7160, -7239, -7352, -7501, -7684, -7903, -8155, -8438, -8750, -9087, -9444, -9817, -10200, -10587, -10972, + -11348, -11710, -12051, -12365, -12646, -12888, -13087, -13239, -13341, -13388, -13381, -13317, -13198, -13024, -12798, -12522, -12200, -11838, -11440, -11013, -10563, -10099, -9627, -9156, -8693, -8246, -7823, -7431, -7078, -6768, -6509, -6304, + -6157, -6072, -6050, -6091, -6196, -6362, -6588, -6869, -7201, -7578, -7994, -8441, -8912, -9397, -9889, -10378, -10855, -11312, -11738, -12127, -12470, -12759, -12989, -13154, -13248, -13270, -13216, -13085, -12877, -12594, -12237, -11811, + -11320, -10770, -10168, -9521, -8837, -8126, -7396, -6659, -5922, -5196, -4491, -3817, -3181, -2593, -2060, -1590, -1187, -856, -603, -428, -334, -322, -389, -535, -757, -1049, -1408, -1826, -2299, -2818, -3375, -3963, + -4574, -5197, -5827, -6453, -7069, -7666, -8239, -8780, -9284, -9747, -10164, -10534, -10853, -11121, -11337, -11503, -11620, -11691, -11718, -11705, -11656, -11577, -11471, -11344, -11201, -11048, -10889, -10728, -10570, -10419, -10277, -10148, + -10032, -9932, -9846, -9776, -9719, -9673, -9635, -9603, -9572, -9538, -9496, -9440, -9366, -9268, -9140, -8978, -8776, -8530, -8236, -7891, -7492, -7037, -6526, -5958, -5334, -4656, -3927, -3151, -2332, -1476, -590, 320, + 1246, 2180, 3113, 4037, 4943, 5821, 6664, 7463, 8211, 8899, 9521, 10072, 10548, 10943, 11256, 11485, 11629, 11690, 11669, 11570, 11397, 11155, 10850, 10490, 10082, 9636, 9159, 8661, 8153, 7643, 7141, 6657, + 6198, 5775, 5394, 5061, 4784, 4566, 4413, 4325, 4306, 4355, 4471, 4653, 4897, 5199, 5553, 5954, 6394, 6866, 7362, 7873, 8390, 8904, 9407, 9889, 10342, 10758, 11131, 11453, 11718, 11922, 12061, 12133, + 12134, 12066, 11928, 11723, 11452, 11119, 10730, 10289, 9802, 9278, 8721, 8142, 7547, 6944, 6341, 5746, 5167, 4611, 4083, 3591, 3138, 2730, 2369, 2058, 1799, 1593, 1438, 1333, 1277, 1266, 1297, 1364, + 1464, 1591, 1738, 1901, 2072, 2247, 2418, 2581, 2730, 2860, 2968, 3049, 3102, 3123, 3113, 3069, 2994, 2888, 2753, 2593, 2410, 2209, 1994, 1771, 1545, 1321, 1105, 903, 719, 560, 429, 332, + 270, 248, 268, 330, 436, 584, 773, 1001, 1265, 1561, 1883, 2226, 2585, 2951, 3319, 3682, 4030, 4357, 4656, 4920, 5141, 5314, 5432, 5492, 5489, 5420, 5284, 5080, 4807, 4467, 4063, 3599, + 3079, 2509, 1896, 1248, 572, -121, -824, -1526, -2216, -2886, -3524, -4122, -4669, -5157, -5579, -5926, -6192, -6373, -6464, -6463, -6366, -6175, -5890, -5513, -5048, -4499, -3871, -3171, -2407, -1588, -721, 181, + 1113, 2061, 3017, 3969, 4909, 5826, 6712, 7559, 8358, 9104, 9790, 10412, 10966, 11451, 11863, 12204, 12474, 12675, 12809, 12882, 12897, 12859, 12775, 12652, 12495, 12312, 12110, 11896, 11676, 11458, 11248, 11050, + 10871, 10713, 10581, 10477, 10403, 10360, 10347, 10364, 10409, 10480, 10573, 10684, 10809, 10944, 11082, 11220, 11351, 11469, 11570, 11649, 11701, 11721, 11707, 11656, 11565, 11432, 11258, 11043, 10788, 10495, 10166, 9806, + 9418, 9008, 8581, 8143, 7701, 7260, 6828, 6411, 6016, 5649, 5315, 5020, 4769, 4566, 4413, 4314, 4269, 4280, 4345, 4464, 4634, 4851, 5111, 5410, 5741, 6099, 6475, 6864, 7257, 7648, 8027, 8387, + 8722, 9024, 9287, 9505, 9673, 9788, 9846, 9845, 9783, 9662, 9482, 9245, 8955, 8616, 8233, 7813, 7363, 6890, 6402, 5908, 5417, 4938, 4481, 4052, 3663, 3319, 3029, 2800, 2637, 2545, 2527, 2588, + 2727, 2945, 3242, 3615, 4061, 4575, 5152, 5785, 6467, 7190, 7944, 8720, 9509, 10299, 11082, 11847, 12585, 13285, 13940, 14540, 15079, 15549, 15946, 16264, 16501, 16654, 16722, 16705, 16604, 16422, 16162, 15828, + 15426, 14963, 14443, 13876, 13269, 12629, 11965, 11286, 10599, 9912, 9233, 8568, 7924, 7307, 6721, 6171, 5659, 5188, 4760, 4374, 4030, 3727, 3463, 3234, 3038, 2870, 2726, 2601, 2490, 2388, 2291, 2192, + 2087, 1973, 1845, 1701, 1537, 1351, 1143, 912, 659, 384, 90, -220, -544, -878, -1216, -1553, -1886, -2207, -2511, -2792, -3045, -3265, -3446, -3584, -3674, -3715, -3702, -3636, -3513, -3336, -3105, -2822, + -2489, -2112, -1695, -1243, -763, -262, 251, 771, 1289, 1795, 2281, 2740, 3162, 3541, 3869, 4140, 4348, 4488, 4557, 4551, 4469, 4311, 4077, 3769, 3389, 2943, 2435, 1872, 1259, 607, -78, -786, + -1508, -2234, -2954, -3659, -4338, -4984, -5586, -6137, -6631, -7059, -7418, -7702, -7909, -8037, -8084, -8051, -7940, -7754, -7496, -7172, -6787, -6348, -5863, -5340, -4787, -4214, -3629, -3042, -2462, -1897, -1356, -846, + -376, 48, 423, 742, 1003, 1202, 1339, 1413, 1425, 1377, 1272, 1113, 906, 656, 369, 52, -287, -643, -1006, -1370, -1727, -2070, -2392, -2687, -2949, -3172, -3354, -3490, -3578, -3617, -3605, -3545, + -3436, -3281, -3083, -2846, -2575, -2274, -1949, -1607, -1253, -894, -536, -186, 150, 468, 761, 1025, 1257, 1451, 1607, 1723, 1797, 1829, 1821, 1773, 1689, 1572, 1424, 1252, 1058, 849, 630, 407, + 185, -29, -233, -419, -585, -725, -837, -918, -967, -982, -963, -912, -830, -721, -586, -432, -262, -83, 99, 277, 446, 597, 723, 816, 871, 881, 838, 739, 577, 350, 54, -312, + -750, -1260, -1841, -2489, -3201, -3972, -4797, -5669, -6580, -7522, -8485, -9460, -10437, -11406, -12355, -13275, -14156, -14987, -15759, -16464, -17093, -17640, -18098, -18463, -18731, -18900, -18967, -18934, -18802, -18573, -18250, -17840, + -17348, -16780, -16146, -15452, -14709, -13927, -13114, -12281, -11438, -10595, -9761, -8946, -8157, -7403, -6691, -6027, -5416, -4861, -4366, -3933, -3561, -3252, -3003, -2812, -2676, -2590, -2549, -2549, -2582, -2642, -2722, -2816, + -2917, -3018, -3112, -3193, -3257, -3297, -3310, -3292, -3241, -3155, -3032, -2873, -2679, -2451, -2192, -1905, -1595, -1265, -920, -567, -210, 144, 491, 824, 1137, 1426, 1686, 1913, 2104, 2255, 2364, 2431, + 2455, 2437, 2377, 2278, 2144, 1977, 1782, 1565, 1331, 1085, 833, 583, 341, 113, -95, -277, -429, -545, -621, -653, -640, -579, -469, -312, -108, 140, 431, 759, 1121, 1510, 1920, 2345, + 2777, 3210, 3634, 4044, 4430, 4786, 5104, 5379, 5604, 5774, 5884, 5932, 5915, 5832, 5682, 5467, 5187, 4847, 4451, 4003, 3510, 2978, 2416, 1831, 1232, 629, 30, -554, -1116, -1647, -2137, -2579, + -2965, -3289, -3546, -3730, -3840, -3871, -3824, -3698, -3495, -3217, -2868, -2453, -1979, -1450, -877, -266, 372, 1030, 1697, 2363, 3019, 3654, 4260, 4827, 5347, 5813, 6217, 6555, 6821, 7012, 7124, 7158, + 7111, 6986, 6785, 6510, 6165, 5756, 5288, 4769, 4204, 3603, 2972, 2321, 1656, 987, 321, -333, -969, -1582, -2164, -2710, -3217, -3680, -4098, -4468, -4791, -5066, -5294, -5478, -5621, -5725, -5794, -5834, + -5849, -5844, -5825, -5796, -5764, -5733, -5707, -5691, -5689, -5704, -5738, -5793, -5870, -5969, -6090, -6231, -6390, -6564, -6750, -6944, -7141, -7336, -7524, -7700, -7858, -7993, -8099, -8171, -8206, -8200, -8148, -8048, + -7899, -7700, -7450, -7151, -6804, -6413, -5981, -5512, -5013, -4489, -3947, -3395, -2841, -2294, -1760, -1250, -772, -333, 57, 393, 667, 873, 1006, 1062, 1038, 931, 741, 467, 112, -321, -831, -1411, + -2055, -2757, -3510, -4303, -5130, -5979, -6841, -7707, -8565, -9407, -10223, -11003, -11740, -12424, -13050, -13610, -14101, -14517, -14857, -15119, -15302, -15406, -15435, -15392, -15279, -15103, -14870, -14587, -14261, -13900, -13513, -13108, + -12695, -12281, -11876, -11489, -11125, -10793, -10499, -10249, -10046, -9895, -9799, -9758, -9773, -9843, -9967, -10142, -10364, -10628, -10929, -11261, -11617, -11989, -12371, -12754, -13131, -13494, -13836, -14149, -14427, -14663, -14853, -14992, + -15075, -15101, -15067, -14972, -14817, -14602, -14328, -14000, -13621, -13194, -12725, -12219, -11682, -11121, -10543, -9952, -9357, -8763, -8176, -7602, -7047, -6514, -6007, -5530, -5085, -4673, -4295, -3951, -3640, -3360, -3108, -2882, + -2678, -2490, -2316, -2149, -1985, -1818, -1643, -1456, -1251, -1025, -774, -494, -183, 159, 535, 945, 1386, 1858, 2357, 2879, 3421, 3978, 4542, 5108, 5670, 6218, 6747, 7248, 7715, 8138, 8512, 8829, + 9084, 9271, 9385, 9424, 9383, 9262, 9059, 8776, 8414, 7977, 7467, 6891, 6254, 5564, 4828, 4055, 3255, 2437, 1612, 789, -19, -805, -1558, -2268, -2926, -3525, -4056, -4513, -4891, -5186, -5395, -5515, + -5546, -5489, -5346, -5121, -4816, -4439, -3995, -3491, -2936, -2339, -1708, -1053, -384, 289, 957, 1610, 2240, 2838, 3397, 3908, 4367, 4767, 5106, 5380, 5587, 5726, 5798, 5804, 5747, 5629, 5456, 5232, + 4964, 4657, 4318, 3955, 3574, 3183, 2789, 2400, 2021, 1659, 1320, 1008, 728, 483, 275, 107, -21, -109, -159, -172, -151, -99, -19, 81, 201, 332, 470, 610, 745, 871, 983, 1075, + 1144, 1186, 1198, 1178, 1125, 1038, 917, 764, 580, 369, 133, -122, -394, -675, -960, -1244, -1519, -1780, -2021, -2234, -2414, -2556, -2655, -2707, -2708, -2655, -2546, -2382, -2160, -1884, -1553, -1172, + -745, -275, 230, 767, 1328, 1905, 2491, 3077, 3657, 4222, 4764, 5275, 5749, 6180, 6561, 6887, 7155, 7361, 7504, 7581, 7594, 7544, 7432, 7262, 7040, 6769, 6457, 6110, 5736, 5344, 4941, 4538, + 4142, 3763, 3410, 3091, 2814, 2587, 2415, 2304, 2260, 2286, 2384, 2555, 2801, 3120, 3509, 3965, 4484, 5059, 5685, 6354, 7058, 7787, 8533, 9285, 10035, 10772, 11486, 12167, 12808, 13398, 13931, 14399, + 14797, 15118, 15360, 15518, 15592, 15580, 15484, 15305, 15045, 14710, 14303, 13830, 13298, 12713, 12084, 11419, 10726, 10013, 9289, 8562, 7840, 7131, 6441, 5777, 5145, 4550, 3995, 3483, 3018, 2600, 2229, 1905, + 1627, 1393, 1198, 1041, 917, 821, 748, 695, 654, 623, 595, 566, 532, 490, 435, 366, 280, 177, 56, -81, -235, -404, -584, -771, -963, -1154, -1341, -1516, -1676, -1814, -1925, -2003, + -2045, -2044, -1997, -1900, -1750, -1546, -1285, -968, -596, -169, 308, 835, 1405, 2014, 2655, 3322, 4007, 4702, 5399, 6089, 6763, 7414, 8032, 8609, 9137, 9609, 10018, 10359, 10627, 10817, 10928, 10957, + 10904, 10770, 10556, 10266, 9903, 9473, 8983, 8438, 7847, 7219, 6562, 5886, 5200, 4515, 3839, 3183, 2555, 1963, 1417, 922, 485, 111, -194, -431, -595, -687, -707, -657, -539, -358, -119, 172, + 509, 884, 1289, 1715, 2154, 2597, 3034, 3458, 3859, 4229, 4561, 4849, 5086, 5268, 5390, 5449, 5444, 5373, 5237, 5037, 4777, 4458, 4086, 3666, 3203, 2705, 2179, 1632, 1072, 507, -55, -606, + -1140, -1648, -2125, -2564, -2960, -3310, -3608, -3854, -4044, -4179, -4259, -4285, -4259, -4184, -4064, -3903, -3706, -3479, -3227, -2957, -2674, -2385, -2096, -1812, -1540, -1283, -1046, -834, -649, -493, -368, -275, + -213, -181, -178, -200, -245, -309, -387, -475, -567, -658, -742, -815, -870, -902, -908, -882, -822, -723, -585, -406, -185, 76, 378, 717, 1091, 1495, 1925, 2375, 2839, 3310, 3781, 4245, + 4694, 5120, 5517, 5876, 6191, 6455, 6663, 6810, 6891, 6903, 6844, 6713, 6510, 6236, 5893, 5485, 5016, 4491, 3918, 3303, 2655, 1983, 1295, 602, -85, -759, -1408, -2023, -2594, -3113, -3571, -3960, + -4275, -4511, -4661, -4725, -4699, -4583, -4377, -4084, -3706, -3248, -2715, -2114, -1452, -737, 22, 815, 1634, 2467, 3305, 4138, 4955, 5747, 6505, 7221, 7887, 8497, 9044, 9523, 9932, 10267, 10527, 10711, + 10820, 10857, 10823, 10723, 10561, 10342, 10072, 9759, 9408, 9026, 8622, 8201, 7772, 7340, 6912, 6494, 6091, 5707, 5347, 5012, 4706, 4428, 4180, 3961, 3769, 3602, 3458, 3332, 3220, 3118, 3021, 2923, + 2820, 2706, 2576, 2425, 2248, 2041, 1801, 1525, 1210, 856, 461, 27, -446, -956, -1500, -2075, -2677, -3299, -3937, -4585, -5237, -5885, -6523, -7145, -7743, -8311, -8844, -9335, -9780, -10175, -10516, -10800, + -11025, -11192, -11300, -11350, -11344, -11286, -11179, -11028, -10839, -10617, -10371, -10106, -9830, -9551, -9276, -9013, -8770, -8553, -8368, -8222, -8119, -8064, -8060, -8109, -8212, -8370, -8582, -8845, -9156, -9512, -9907, -10334, + -10788, -11261, -11744, -12229, -12707, -13169, -13606, -14009, -14370, -14681, -14933, -15121, -15238, -15280, -15241, -15120, -14915, -14625, -14250, -13794, -13259, -12649, -11970, -11228, -10431, -9586, -8703, -7792, -6861, -5921, -4982, -4054, + -3148, -2272, -1436, -649, 82, 751, 1351, 1877, 2327, 2696, 2984, 3191, 3317, 3365, 3338, 3239, 3073, 2848, 2568, 2242, 1876, 1479, 1059, 624, 182, -259, -692, -1110, -1507, -1877, -2215, -2517, + -2779, -3000, -3177, -3312, -3403, -3452, -3462, -3435, -3374, -3284, -3169, -3034, -2885, -2725, -2561, -2397, -2239, -2090, -1955, -1837, -1739, -1664, -1611, -1584, -1580, -1599, -1640, -1699, -1775, -1862, -1957, -2055, + -2150, -2238, -2312, -2366, -2396, -2397, -2362, -2287, -2169, -2004, -1789, -1523, -1205, -834, -413, 57, 575, 1135, 1733, 2363, 3019, 3695, 4382, 5072, 5759, 6432, 7083, 7705, 8289, 8827, 9312, 9737, + 10097, 10386, 10601, 10738, 10796, 10774, 10672, 10492, 10236, 9909, 9516, 9062, 8555, 8002, 7411, 6792, 6154, 5506, 4859, 4222, 3606, 3018, 2468, 1965, 1515, 1126, 803, 550, 371, 268, 243, 295, + 423, 624, 895, 1231, 1627, 2075, 2569, 3100, 3660, 4240, 4832, 5426, 6012, 6583, 7130, 7645, 8121, 8551, 8930, 9253, 9516, 9717, 9855, 9928, 9938, 9887, 9777, 9611, 9396, 9135, 8836, 8504, + 8147, 7773, 7389, 7003, 6621, 6253, 5903, 5580, 5289, 5034, 4821, 4653, 4533, 4462, 4441, 4471, 4549, 4674, 4843, 5053, 5298, 5575, 5877, 6199, 6534, 6877, 7220, 7558, 7885, 8194, 8481, 8741, + 8969, 9163, 9319, 9436, 9512, 9548, 9544, 9502, 9424, 9312, 9171, 9005, 8818, 8616, 8404, 8188, 7972, 7763, 7567, 7387, 7228, 7095, 6991, 6918, 6879, 6874, 6903, 6966, 7060, 7185, 7335, 7507, + 7696, 7897, 8102, 8306, 8501, 8681, 8838, 8964, 9053, 9098, 9093, 9031, 8909, 8722, 8467, 8140, 7743, 7273, 6733, 6124, 5450, 4715, 3924, 3085, 2205, 1291, 354, -598, -1555, -2506, -3442, -4351, + -5224, -6051, -6821, -7527, -8161, -8715, -9183, -9560, -9843, -10028, -10114, -10101, -9991, -9785, -9487, -9102, -8636, -8096, -7489, -6823, -6109, -5356, -4573, -3770, -2958, -2146, -1345, -563, 190, 908, 1582, 2208, + 2778, 3289, 3739, 4124, 4443, 4698, 4889, 5018, 5089, 5105, 5072, 4994, 4879, 4731, 4559, 4369, 4168, 3962, 3758, 3562, 3380, 3218, 3078, 2966, 2884, 2833, 2815, 2831, 2879, 2958, 3066, 3199, + 3354, 3527, 3712, 3905, 4099, 4290, 4471, 4636, 4781, 4901, 4989, 5043, 5058, 5033, 4964, 4850, 4692, 4489, 4243, 3955, 3629, 3268, 2878, 2462, 2026, 1576, 1119, 661, 207, -233, -656, -1056, + -1426, -1761, -2058, -2312, -2521, -2683, -2797, -2862, -2880, -2852, -2782, -2672, -2528, -2354, -2157, -1943, -1719, -1492, -1271, -1061, -872, -710, -582, -495, -454, -466, -533, -661, -851, -1104, -1421, -1802, + -2244, -2744, -3299, -3903, -4550, -5234, -5947, -6680, -7426, -8174, -8915, -9641, -10342, -11009, -11632, -12205, -12719, -13168, -13546, -13848, -14071, -14211, -14268, -14242, -14133, -13944, -13678, -13340, -12936, -12472, -11955, -11394, + -10798, -10176, -9538, -8893, -8251, -7622, -7015, -6440, -5905, -5417, -4983, -4610, -4302, -4063, -3896, -3803, -3784, -3838, -3964, -4159, -4418, -4738, -5112, -5534, -5996, -6492, -7014, -7553, -8101, -8650, -9192, -9720, + -10226, -10703, -11147, -11551, -11912, -12225, -12488, -12700, -12860, -12967, -13023, -13031, -12991, -12909, -12788, -12632, -12447, -12237, -12008, -11765, -11514, -11260, -11008, -10762, -10526, -10304, -10098, -9910, -9742, -9594, -9465, -9355, + -9261, -9182, -9114, -9053, -8994, -8934, -8866, -8786, -8689, -8569, -8421, -8240, -8022, -7762, -7459, -7108, -6708, -6259, -5760, -5212, -4616, -3977, -3297, -2580, -1834, -1063, -274, 523, 1323, 2116, 2895, 3649, + 4372, 5053, 5685, 6261, 6774, 7216, 7584, 7871, 8075, 8193, 8225, 8169, 8028, 7802, 7497, 7116, 6665, 6150, 5579, 4961, 4303, 3616, 2909, 2192, 1474, 767, 79, -580, -1203, -1780, -2306, -2773, + -3176, -3512, -3777, -3969, -4089, -4135, -4112, -4020, -3865, -3651, -3384, -3072, -2721, -2339, -1936, -1519, -1097, -680, -276, 107, 461, 780, 1056, 1283, 1458, 1576, 1635, 1632, 1568, 1442, 1257, 1015, + 720, 376, -10, -435, -890, -1368, -1862, -2365, -2867, -3362, -3841, -4299, -4726, -5118, -5469, -5774, -6029, -6230, -6376, -6466, -6498, -6474, -6396, -6265, -6085, -5861, -5596, -5297, -4969, -4618, -4250, -3872, + -3490, -3111, -2740, -2383, -2046, -1731, -1445, -1189, -965, -777, -623, -504, -420, -368, -345, -350, -377, -422, -482, -550, -621, -691, -754, -804, -838, -849, -836, -794, -720, -613, -471, -295, + -85, 156, 428, 727, 1048, 1386, 1736, 2091, 2445, 2791, 3120, 3427, 3703, 3940, 4132, 4272, 4354, 4372, 4322, 4200, 4003, 3729, 3378, 2949, 2445, 1869, 1223, 514, -252, -1070, -1931, -2827, + -3747, -4683, -5625, -6561, -7482, -8377, -9236, -10050, -10809, -11506, -12132, -12681, -13148, -13527, -13816, -14012, -14115, -14125, -14044, -13875, -13622, -13290, -12886, -12416, -11889, -11313, -10698, -10053, -9387, -8710, -8033, -7365, + -6714, -6089, -5499, -4951, -4450, -4002, -3612, -3283, -3017, -2814, -2676, -2600, -2585, -2628, -2724, -2868, -3056, -3280, -3535, -3813, -4107, -4409, -4714, -5013, -5301, -5570, -5816, -6033, -6217, -6366, -6475, -6543, + -6570, -6556, -6501, -6407, -6278, -6115, -5924, -5709, -5474, -5225, -4968, -4708, -4450, -4201, -3965, -3747, -3552, -3383, -3243, -3136, -3062, -3022, -3016, -3044, -3104, -3193, -3308, -3445, -3599, -3765, -3938, -4112, + -4280, -4436, -4575, -4689, -4773, -4822, -4830, -4794, -4709, -4573, -4384, -4141, -3844, -3493, -3090, -2639, -2142, -1605, -1032, -430, 193, 833, 1480, 2127, 2766, 3389, 3988, 4555, 5083, 5567, 5998, 6374, + 6688, 6939, 7123, 7240, 7289, 7272, 7190, 7046, 6846, 6593, 6295, 5958, 5590, 5199, 4793, 4382, 3974, 3579, 3205, 2861, 2554, 2294, 2085, 1934, 1846, 1826, 1874, 1995, 2187, 2450, 2782, 3180, + 3639, 4155, 4720, 5329, 5971, 6640, 7326, 8019, 8709, 9387, 10043, 10667, 11250, 11785, 12262, 12674, 13016, 13283, 13469, 13573, 13591, 13523, 13371, 13134, 12817, 12422, 11955, 11420, 10826, 10177, 9483, 8752, + 7991, 7209, 6415, 5617, 4823, 4041, 3278, 2541, 1836, 1167, 539, -43, -580, -1068, -1507, -1898, -2241, -2539, -2794, -3009, -3189, -3338, -3460, -3560, -3644, -3717, -3782, -3846, -3911, -3983, -4064, -4156, + -4262, -4383, -4519, -4670, -4836, -5013, -5199, -5392, -5587, -5780, -5966, -6140, -6296, -6429, -6534, -6604, -6636, -6623, -6563, -6450, -6283, -6058, -5775, -5433, -5032, -4574, -4062, -3497, -2886, -2232, -1543, -823, + -82, 672, 1434, 2193, 2940, 3667, 4366, 5027, 5643, 6207, 6711, 7149, 7518, 7811, 8028, 8164, 8220, 8197, 8095, 7917, 7667, 7351, 6973, 6542, 6064, 5548, 5002, 4437, 3860, 3283, 2715, 2165, + 1642, 1155, 711, 318, -17, -291, -498, -635, -701, -694, -615, -465, -248, 31, 371, 764, 1202, 1680, 2188, 2719, 3263, 3811, 4355, 4885, 5394, 5871, 6311, 6707, 7051, 7338, 7566, 7729, + 7826, 7856, 7819, 7716, 7549, 7321, 7037, 6701, 6320, 5899, 5447, 4969, 4475, 3972, 3467, 2969, 2485, 2022, 1587, 1186, 824, 506, 235, 13, -155, -272, -336, -349, -312, -228, -102, 63, + 262, 490, 742, 1010, 1289, 1572, 1855, 2129, 2392, 2636, 2859, 3055, 3222, 3357, 3459, 3527, 3562, 3563, 3533, 3474, 3389, 3283, 3159, 3023, 2879, 2733, 2590, 2457, 2338, 2238, 2163, 2117, + 2104, 2126, 2186, 2285, 2425, 2605, 2825, 3081, 3371, 3691, 4038, 4405, 4786, 5175, 5566, 5950, 6321, 6671, 6991, 7276, 7519, 7712, 7850, 7928, 7941, 7887, 7763, 7568, 7301, 6964, 6559, 6088, + 5557, 4971, 4336, 3660, 2950, 2216, 1467, 712, -38, -774, -1484, -2161, -2793, -3372, -3889, -4336, -4708, -4998, -5202, -5316, -5338, -5266, -5102, -4847, -4503, -4075, -3568, -2987, -2340, -1635, -881, -87, + 737, 1582, 2438, 3294, 4140, 4968, 5767, 6529, 7247, 7913, 8522, 9069, 9549, 9960, 10301, 10570, 10768, 10896, 10958, 10957, 10897, 10783, 10622, 10419, 10182, 9917, 9631, 9332, 9026, 8721, 8423, 8137, + 7869, 7623, 7404, 7214, 7055, 6928, 6835, 6774, 6744, 6743, 6768, 6816, 6882, 6962, 7051, 7143, 7233, 7316, 7387, 7440, 7471, 7475, 7449, 7389, 7294, 7160, 6989, 6779, 6532, 6248, 5932, 5585, + 5212, 4819, 4409, 3989, 3565, 3144, 2731, 2335, 1960, 1615, 1303, 1032, 806, 629, 505, 436, 425, 471, 576, 737, 954, 1222, 1537, 1896, 2293, 2721, 3173, 3643, 4123, 4605, 5081, 5545, + 5987, 6401, 6781, 7120, 7414, 7656, 7845, 7977, 8051, 8067, 8024, 7925, 7773, 7571, 7324, 7038, 6721, 6377, 6017, 5648, 5280, 4919, 4577, 4262, 3981, 3744, 3558, 3429, 3364, 3369, 3446, 3599, + 3831, 4141, 4529, 4993, 5531, 6138, 6809, 7537, 8316, 9138, 9993, 10872, 11766, 12665, 13558, 14434, 15285, 16101, 16871, 17589, 18245, 18833, 19346, 19781, 20132, 20397, 20575, 20665, 20667, 20584, 20419, 20175, + 19857, 19471, 19024, 18523, 17974, 17387, 16769, 16129, 15473, 14812, 14151, 13499, 12861, 12244, 11652, 11091, 10563, 10071, 9618, 9203, 8826, 8487, 8183, 7912, 7672, 7457, 7264, 7088, 6924, 6767, 6612, 6455, + 6289, 6112, 5918, 5705, 5470, 5210, 4924, 4612, 4273, 3909, 3521, 3112, 2684, 2243, 1791, 1334, 878, 427, -11, -432, -831, -1201, -1537, -1834, -2089, -2297, -2456, -2563, -2617, -2618, -2567, -2465, + -2315, -2120, -1885, -1614, -1315, -993, -656, -311, 32, 367, 684, 977, 1235, 1452, 1620, 1734, 1786, 1771, 1687, 1529, 1295, 985, 600, 140, -392, -992, -1655, -2376, -3147, -3961, -4810, -5685, + -6575, -7472, -8365, -9245, -10102, -10927, -11710, -12443, -13118, -13729, -14270, -14736, -15123, -15428, -15651, -15790, -15848, -15827, -15729, -15559, -15323, -15028, -14679, -14286, -13856, -13399, -12923, -12438, -11952, -11474, -11013, -10577, + -10173, -9809, -9488, -9218, -9000, -8839, -8736, -8692, -8706, -8777, -8901, -9077, -9299, -9561, -9859, -10185, -10533, -10895, -11264, -11632, -11992, -12336, -12657, -12949, -13206, -13423, -13594, -13718, -13790, -13809, -13775, -13686, + -13545, -13352, -13112, -12827, -12502, -12141, -11751, -11337, -10905, -10462, -10015, -9568, -9130, -8705, -8300, -7919, -7566, -7245, -6960, -6711, -6502, -6331, -6199, -6104, -6044, -6017, -6020, -6047, -6095, -6159, -6232, -6311, + -6390, -6462, -6523, -6568, -6592, -6591, -6563, -6503, -6411, -6285, -6124, -5930, -5705, -5450, -5168, -4865, -4545, -4213, -3876, -3540, -3212, -2899, -2610, -2350, -2128, -1950, -1822, -1750, -1740, -1796, -1922, -2119, + -2389, -2733, -3149, -3637, -4192, -4811, -5488, -6217, -6991, -7802, -8641, -9498, -10363, -11227, -12079, -12909, -13707, -14461, -15164, -15806, -16379, -16875, -17289, -17614, -17847, -17985, -18027, -17971, -17818, -17571, -17234, -16810, + -16305, -15726, -15081, -14377, -13624, -12831, -12008, -11165, -10311, -9458, -8614, -7789, -6992, -6230, -5511, -4841, -4226, -3671, -3177, -2749, -2386, -2089, -1857, -1687, -1577, -1523, -1519, -1562, -1643, -1758, -1899, -2060, + -2233, -2412, -2590, -2760, -2918, -3057, -3173, -3262, -3321, -3348, -3342, -3302, -3228, -3122, -2985, -2822, -2635, -2428, -2207, -1976, -1740, -1507, -1280, -1066, -870, -698, -554, -442, -366, -330, -334, -381, + -471, -604, -779, -994, -1245, -1530, -1845, -2183, -2541, -2912, -3289, -3666, -4038, -4397, -4737, -5052, -5337, -5586, -5795, -5960, -6078, -6147, -6166, -6134, -6053, -5923, -5748, -5531, -5277, -4990, -4676, -4342, + -3994, -3641, -3289, -2946, -2620, -2318, -2047, -1814, -1625, -1485, -1399, -1371, -1403, -1498, -1654, -1873, -2153, -2491, -2882, -3323, -3808, -4329, -4880, -5453, -6039, -6628, -7213, -7783, -8329, -8843, -9314, -9737, + -10102, -10403, -10634, -10790, -10868, -10864, -10778, -10608, -10356, -10024, -9615, -9133, -8585, -7976, -7315, -6609, -5867, -5098, -4312, -3520, -2730, -1953, -1198, -474, 209, 844, 1425, 1945, 2397, 2780, 3088, 3321, + 3477, 3556, 3562, 3495, 3360, 3160, 2903, 2593, 2238, 1845, 1422, 977, 518, 53, -409, -863, -1299, -1712, -2096, -2445, -2754, -3020, -3241, -3414, -3538, -3614, -3643, -3625, -3565, -3465, -3328, -3160, + -2965, -2749, -2517, -2273, -2025, -1776, -1532, -1297, -1075, -871, -685, -522, -382, -266, -173, -102, -53, -22, -7, -2, -5, -11, -14, -10, 7, 43, 103, 191, 313, 472, 672, 915, + 1203, 1539, 1922, 2351, 2825, 3341, 3897, 4486, 5106, 5748, 6407, 7076, 7745, 8408, 9056, 9680, 10272, 10823, 11325, 11771, 12153, 12466, 12703, 12861, 12934, 12922, 12823, 12635, 12361, 12003, 11563, 11047, + 10459, 9807, 9097, 8338, 7539, 6709, 5858, 4995, 4132, 3277, 2440, 1632, 860, 133, -540, -1155, -1707, -2189, -2599, -2935, -3195, -3381, -3493, -3535, -3509, -3421, -3276, -3082, -2844, -2572, -2272, -1955, + -1627, -1300, -979, -675, -395, -146, 64, 232, 352, 419, 431, 386, 283, 123, -92, -361, -680, -1045, -1450, -1889, -2356, -2843, -3343, -3849, -4352, -4844, -5318, -5767, -6183, -6560, -6893, -7176, + -7406, -7578, -7691, -7743, -7733, -7663, -7533, -7346, -7106, -6815, -6479, -6104, -5693, -5255, -4795, -4319, -3835, -3348, -2864, -2389, -1929, -1488, -1070, -679, -317, 14, 313, 581, 817, 1024, 1203, 1358, + 1492, 1609, 1714, 1811, 1907, 2005, 2112, 2232, 2370, 2530, 2717, 2934, 3183, 3467, 3786, 4141, 4530, 4954, 5409, 5891, 6398, 6923, 7461, 8006, 8551, 9090, 9613, 10115, 10587, 11021, 11411, 11749, + 12029, 12245, 12393, 12468, 12468, 12389, 12230, 11993, 11678, 11288, 10826, 10297, 9706, 9060, 8367, 7635, 6874, 6091, 5299, 4506, 3724, 2961, 2229, 1537, 894, 309, -211, -659, -1030, -1319, -1522, -1637, + -1663, -1601, -1452, -1219, -905, -516, -59, 460, 1034, 1653, 2308, 2991, 3690, 4397, 5102, 5794, 6465, 7106, 7709, 8266, 8771, 9219, 9604, 9924, 10176, 10358, 10471, 10515, 10492, 10406, 10260, 10060, + 9809, 9515, 9184, 8823, 8440, 8040, 7633, 7224, 6820, 6428, 6054, 5702, 5377, 5084, 4823, 4599, 4411, 4260, 4145, 4064, 4016, 3997, 4003, 4030, 4074, 4128, 4189, 4249, 4305, 4349, 4378, 4386, + 4369, 4323, 4245, 4133, 3984, 3799, 3577, 3318, 3025, 2700, 2346, 1967, 1568, 1154, 730, 303, -120, -535, -934, -1311, -1660, -1975, -2252, -2485, -2671, -2806, -2889, -2918, -2892, -2812, -2680, -2497, + -2268, -1996, -1687, -1346, -980, -596, -202, 195, 587, 967, 1327, 1658, 1955, 2209, 2416, 2570, 2666, 2702, 2675, 2584, 2428, 2208, 1926, 1586, 1192, 748, 260, -262, -815, -1388, -1974, -2563, + -3145, -3713, -4257, -4768, -5239, -5660, -6027, -6331, -6569, -6736, -6829, -6845, -6785, -6649, -6438, -6155, -5805, -5392, -4923, -4405, -3845, -3252, -2635, -2004, -1368, -738, -122, 468, 1026, 1541, 2005, 2412, + 2755, 3028, 3228, 3349, 3391, 3353, 3234, 3036, 2762, 2414, 1997, 1518, 981, 394, -234, -899, -1589, -2298, -3016, -3735, -4447, -5145, -5821, -6468, -7081, -7654, -8183, -8664, -9095, -9475, -9802, -10077, + -10301, -10476, -10604, -10689, -10735, -10746, -10727, -10683, -10618, -10539, -10449, -10354, -10259, -10167, -10081, -10005, -9941, -9890, -9853, -9829, -9819, -9819, -9829, -9845, -9863, -9880, -9890, -9888, -9870, -9829, -9761, -9659, + -9520, -9338, -9109, -8829, -8496, -8106, -7659, -7154, -6590, -5971, -5297, -4572, -3800, -2987, -2138, -1259, -359, 555, 1475, 2392, 3298, 4183, 5038, 5856, 6628, 7346, 8004, 8595, 9113, 9555, 9918, 10198, + 10395, 10509, 10541, 10494, 10372, 10178, 9919, 9601, 9232, 8820, 8373, 7901, 7413, 6919, 6429, 5951, 5496, 5071, 4684, 4344, 4056, 3826, 3658, 3556, 3522, 3556, 3660, 3831, 4067, 4364, 4717, 5121, + 5570, 6055, 6570, 7105, 7652, 8202, 8746, 9275, 9781, 10255, 10689, 11077, 11412, 11688, 11902, 12050, 12130, 12139, 12079, 11950, 11754, 11494, 11174, 10800, 10376, 9910, 9408, 8878, 8327, 7763, 7195, 6631, + 6077, 5541, 5030, 4551, 4108, 3707, 3350, 3043, 2785, 2579, 2425, 2322, 2267, 2260, 2295, 2370, 2479, 2618, 2780, 2960, 3152, 3350, 3547, 3738, 3917, 4080, 4222, 4338, 4426, 4484, 4508, 4500, + 4458, 4383, 4277, 4143, 3983, 3801, 3602, 3390, 3171, 2950, 2732, 2523, 2329, 2154, 2005, 1884, 1797, 1747, 1735, 1764, 1835, 1948, 2101, 2294, 2523, 2784, 3074, 3387, 3718, 4059, 4405, 4748, + 5080, 5394, 5683, 5939, 6155, 6324, 6442, 6502, 6500, 6433, 6297, 6092, 5818, 5474, 5062, 4587, 4052, 3461, 2822, 2141, 1427, 688, -66, -828, -1585, -2329, -3049, -3735, -4378, -4968, -5497, -5957, + -6342, -6645, -6863, -6990, -7025, -6966, -6814, -6570, -6236, -5817, -5317, -4742, -4100, -3398, -2645, -1850, -1022, -173, 688, 1552, 2407, 3245, 4056, 4831, 5563, 6243, 6867, 7428, 7923, 8348, 8702, 8983, + 9191, 9329, 9398, 9402, 9344, 9231, 9067, 8859, 8613, 8336, 8036, 7719, 7392, 7063, 6737, 6420, 6118, 5836, 5577, 5345, 5141, 4967, 4824, 4712, 4628, 4572, 4540, 4529, 4534, 4552, 4577, 4604, + 4628, 4643, 4644, 4626, 4584, 4514, 4411, 4273, 4097, 3881, 3624, 3326, 2988, 2610, 2197, 1750, 1274, 774, 254, -279, -820, -1363, -1900, -2425, -2931, -3412, -3861, -4274, -4645, -4969, -5243, -5463, + -5629, -5738, -5792, -5789, -5733, -5626, -5472, -5274, -5040, -4774, -4483, -4174, -3855, -3533, -3215, -2910, -2626, -2368, -2144, -1960, -1821, -1731, -1695, -1716, -1794, -1930, -2125, -2375, -2679, -3033, -3432, -3870, + -4341, -4837, -5351, -5874, -6398, -6913, -7410, -7880, -8315, -8707, -9047, -9328, -9545, -9691, -9762, -9755, -9668, -9500, -9250, -8920, -8513, -8033, -7485, -6874, -6208, -5494, -4741, -3958, -3155, -2341, -1527, -723, + 61, 816, 1533, 2202, 2816, 3368, 3852, 4262, 4595, 4846, 5016, 5102, 5106, 5030, 4875, 4646, 4348, 3986, 3567, 3097, 2585, 2037, 1463, 870, 267, -338, -939, -1527, -2096, -2640, -3153, -3631, + -4070, -4468, -4823, -5133, -5399, -5622, -5803, -5944, -6050, -6123, -6167, -6189, -6191, -6180, -6160, -6137, -6116, -6100, -6094, -6102, -6126, -6170, -6234, -6319, -6426, -6554, -6702, -6866, -7045, -7235, -7430, -7628, + -7822, -8007, -8177, -8328, -8453, -8546, -8603, -8618, -8588, -8509, -8377, -8192, -7950, -7653, -7301, -6895, -6437, -5931, -5382, -4795, -4176, -3530, -2867, -2193, -1516, -845, -189, 443, 1045, 1607, 2123, 2586, + 2988, 3326, 3594, 3789, 3908, 3951, 3917, 3808, 3625, 3373, 3055, 2677, 2245, 1768, 1253, 708, 144, -430, -1006, -1573, -2121, -2641, -3124, -3562, -3946, -4271, -4529, -4717, -4830, -4866, -4823, -4701, + -4501, -4225, -3876, -3459, -2979, -2442, -1855, -1227, -566, 119, 820, 1527, 2232, 2924, 3596, 4240, 4847, 5410, 5925, 6385, 6787, 7127, 7404, 7615, 7763, 7847, 7871, 7837, 7750, 7614, 7436, 7222, + 6978, 6713, 6433, 6147, 5860, 5582, 5319, 5078, 4864, 4683, 4540, 4438, 4381, 4370, 4407, 4492, 4624, 4801, 5021, 5280, 5574, 5898, 6248, 6616, 6998, 7387, 7776, 8159, 8530, 8882, 9212, 9513, + 9781, 10012, 10204, 10354, 10460, 10523, 10543, 10520, 10457, 10356, 10221, 10056, 9866, 9655, 9428, 9192, 8951, 8712, 8480, 8260, 8056, 7873, 7715, 7584, 7483, 7414, 7376, 7369, 7392, 7443, 7520, 7617, + 7730, 7855, 7985, 8114, 8234, 8340, 8423, 8477, 8495, 8469, 8393, 8262, 8070, 7813, 7488, 7091, 6621, 6079, 5464, 4778, 4024, 3207, 2332, 1404, 431, -578, -1615, -2672, -3738, -4802, -5855, -6885, + -7883, -8838, -9741, -10582, -11353, -12046, -12655, -13174, -13600, -13927, -14155, -14283, -14311, -14241, -14076, -13820, -13480, -13060, -12569, -12014, -11405, -10750, -10060, -9344, -8612, -7874, -7140, -6420, -5721, -5053, -4422, -3836, + -3299, -2817, -2393, -2029, -1728, -1488, -1309, -1190, -1127, -1116, -1154, -1234, -1352, -1500, -1672, -1861, -2060, -2263, -2462, -2651, -2825, -2978, -3106, -3204, -3269, -3299, -3292, -3249, -3168, -3052, -2902, -2722, + -2514, -2283, -2033, -1770, -1500, -1227, -958, -698, -453, -228, -28, 141, 278, 378, 439, 460, 439, 376, 272, 130, -48, -260, -501, -767, -1051, -1349, -1655, -1961, -2263, -2552, -2824, -3072, + -3290, -3474, -3618, -3720, -3776, -3783, -3741, -3649, -3507, -3318, -3084, -2808, -2494, -2148, -1775, -1382, -976, -564, -153, 248, 632, 993, 1321, 1611, 1857, 2051, 2191, 2272, 2290, 2244, 2134, 1958, + 1719, 1419, 1061, 650, 192, -306, -839, -1399, -1975, -2561, -3146, -3721, -4277, -4806, -5297, -5744, -6137, -6472, -6740, -6938, -7061, -7106, -7071, -6956, -6762, -6491, -6145, -5728, -5247, -4707, -4116, -3482, + -2813, -2118, -1408, -691, 20, 720, 1395, 2039, 2641, 3194, 3691, 4125, 4491, 4784, 5001, 5141, 5201, 5183, 5087, 4917, 4675, 4367, 3996, 3570, 3096, 2580, 2031, 1457, 865, 264, -337, -932, + -1513, -2073, -2606, -3106, -3569, -3990, -4367, -4697, -4979, -5214, -5401, -5543, -5641, -5699, -5720, -5709, -5670, -5608, -5529, -5438, -5341, -5241, -5145, -5057, -4981, -4920, -4877, -4855, -4855, -4878, -4923, -4990, + -5077, -5181, -5300, -5430, -5566, -5705, -5840, -5966, -6079, -6172, -6241, -6280, -6285, -6251, -6175, -6054, -5885, -5667, -5399, -5082, -4718, -4308, -3856, -3366, -2842, -2292, -1721, -1136, -545, 42, 620, 1178, + 1709, 2203, 2653, 3051, 3390, 3663, 3865, 3991, 4037, 4000, 3879, 3673, 3383, 3011, 2560, 2034, 1439, 780, 66, -696, -1497, -2329, -3181, -4044, -4907, -5761, -6595, -7401, -8169, -8891, -9560, -10168, + -10711, -11184, -11582, -11905, -12150, -12318, -12410, -12429, -12377, -12261, -12085, -11855, -11580, -11266, -10923, -10558, -10180, -9799, -9422, -9058, -8716, -8402, -8124, -7888, -7698, -7559, -7473, -7444, -7472, -7557, -7698, -7893, + -8138, -8429, -8761, -9129, -9526, -9945, -10379, -10819, -11259, -11691, -12106, -12498, -12859, -13183, -13465, -13699, -13880, -14006, -14074, -14083, -14031, -13919, -13749, -13523, -13243, -12915, -12541, -12128, -11681, -11207, -10711, -10200, + -9680, -9159, -8642, -8134, -7642, -7171, -6724, -6304, -5916, -5560, -5238, -4950, -4696, -4474, -4282, -4117, -3976, -3855, -3750, -3655, -3565, -3476, -3381, -3277, -3158, -3019, -2857, -2668, -2448, -2197, -1913, -1595, + -1244, -861, -448, -9, 452, 932, 1425, 1924, 2423, 2914, 3391, 3845, 4270, 4657, 4999, 5290, 5523, 5691, 5791, 5818, 5769, 5640, 5432, 5143, 4776, 4332, 3815, 3229, 2580, 1875, 1120, 324, + -502, -1352, -2214, -3078, -3934, -4771, -5579, -6349, -7071, -7737, -8338, -8867, -9319, -9688, -9972, -10166, -10270, -10285, -10210, -10049, -9805, -9483, -9089, -8629, -8112, -7545, -6938, -6299, -5639, -4967, -4294, -3627, + -2978, -2353, -1763, -1214, -712, -264, 126, 454, 718, 917, 1049, 1117, 1121, 1064, 951, 785, 573, 320, 33, -280, -614, -962, -1314, -1666, -2008, -2336, -2643, -2924, -3174, -3389, -3567, -3705, + -3802, -3857, -3871, -3846, -3784, -3687, -3559, -3405, -3228, -3035, -2831, -2621, -2410, -2205, -2009, -1829, -1669, -1532, -1423, -1343, -1294, -1278, -1295, -1344, -1424, -1532, -1666, -1821, -1994, -2179, -2371, -2563, + -2750, -2926, -3084, -3217, -3320, -3388, -3415, -3396, -3327, -3206, -3029, -2796, -2506, -2159, -1757, -1302, -797, -247, 343, 968, 1621, 2295, 2983, 3677, 4368, 5050, 5713, 6351, 6955, 7520, 8038, 8505, + 8915, 9266, 9553, 9776, 9934, 10028, 10059, 10029, 9944, 9806, 9623, 9401, 9147, 8869, 8575, 8275, 7976, 7689, 7421, 7182, 6980, 6822, 6715, 6666, 6680, 6761, 6912, 7135, 7431, 7801, 8241, 8750, + 9323, 9955, 10641, 11374, 12144, 12945, 13767, 14600, 15435, 16262, 17070, 17851, 18595, 19292, 19935, 20516, 21028, 21466, 21825, 22101, 22292, 22396, 22414, 22346, 22195, 21963, 21655, 21277, 20834, 20333, 19781, 19187, + 18558, 17902, 17229, 16547, 15863, 15185, 14522, 13878, 13262, 12678, 12130, 11622, 11158, 10738, 10364, 10036, 9752, 9511, 9311, 9148, 9019, 8919, 8843, 8788, 8747, 8716, 8690, 8664, 8633, 8594, 8543, 8477, + 8393, 8291, 8170, 8029, 7869, 7693, 7501, 7298, 7086, 6871, 6656, 6446, 6248, 6066, 5906, 5774, 5675, 5613, 5593, 5620, 5696, 5824, 6006, 6242, 6534, 6879, 7275, 7721, 8211, 8741, 9307, 9900, + 10514, 11143, 11776, 12407, 13026, 13624, 14194, 14726, 15212, 15646, 16019, 16327, 16562, 16721, 16801, 16799, 16714, 16546, 16296, 15967, 15562, 15085, 14543, 13942, 13289, 12592, 11861, 11104, 10331, 9552, 8776, 8013, + 7273, 6564, 5894, 5271, 4702, 4193, 3748, 3372, 3066, 2832, 2670, 2579, 2557, 2600, 2705, 2866, 3076, 3328, 3615, 3928, 4259, 4599, 4939, 5271, 5584, 5873, 6127, 6341, 6508, 6623, 6680, 6677, + 6610, 6479, 6282, 6022, 5699, 5316, 4878, 4389, 3855, 3282, 2676, 2046, 1399, 743, 86, -563, -1199, -1813, -2398, -2948, -3458, -3922, -4336, -4698, -5004, -5255, -5449, -5587, -5670, -5702, -5685, -5624, + -5522, -5386, -5220, -5031, -4825, -4607, -4384, -4162, -3947, -3744, -3556, -3390, -3247, -3131, -3044, -2986, -2959, -2961, -2991, -3047, -3126, -3225, -3339, -3464, -3595, -3726, -3853, -3969, -4069, -4148, -4201, -4224, + -4212, -4162, -4072, -3940, -3765, -3546, -3286, -2984, -2644, -2270, -1866, -1436, -987, -525, -57, 409, 867, 1309, 1727, 2113, 2462, 2765, 3016, 3211, 3344, 3412, 3412, 3342, 3201, 2990, 2711, 2365, + 1958, 1493, 977, 417, -179, -804, -1449, -2103, -2757, -3400, -4024, -4617, -5171, -5676, -6124, -6506, -6816, -7048, -7197, -7258, -7230, -7111, -6900, -6600, -6211, -5739, -5187, -4562, -3870, -3119, -2318, -1475, + -601, 294, 1201, 2110, 3011, 3893, 4747, 5564, 6337, 7058, 7721, 8319, 8850, 9309, 9694, 10004, 10240, 10402, 10492, 10514, 10472, 10370, 10215, 10012, 9767, 9489, 9183, 8858, 8519, 8175, 7831, 7493, + 7168, 6859, 6572, 6309, 6072, 5865, 5686, 5537, 5416, 5321, 5250, 5199, 5165, 5143, 5129, 5117, 5102, 5079, 5042, 4986, 4907, 4800, 4661, 4487, 4275, 4023, 3731, 3397, 3024, 2611, 2161, 1678, + 1165, 626, 68, -504, -1085, -1668, -2245, -2812, -3360, -3883, -4375, -4830, -5243, -5609, -5925, -6186, -6392, -6540, -6631, -6664, -6642, -6567, -6442, -6272, -6061, -5817, -5544, -5250, -4943, -4630, -4318, -4015, + -3729, -3467, -3235, -3040, -2888, -2782, -2728, -2727, -2782, -2893, -3060, -3282, -3556, -3879, -4246, -4652, -5089, -5552, -6032, -6521, -7010, -7491, -7954, -8390, -8791, -9149, -9454, -9701, -9882, -9992, -10027, -9983, + -9857, -9649, -9358, -8985, -8533, -8006, -7409, -6746, -6026, -5256, -4443, -3599, -2731, -1849, -965, -88, 771, 1603, 2400, 3151, 3849, 4486, 5056, 5553, 5973, 6314, 6571, 6746, 6838, 6848, 6779, 6634, + 6419, 6138, 5797, 5404, 4965, 4489, 3983, 3456, 2915, 2370, 1826, 1292, 774, 279, -187, -622, -1021, -1381, -1699, -1976, -2210, -2403, -2556, -2672, -2753, -2804, -2828, -2830, -2815, -2788, -2754, -2719, + -2687, -2663, -2651, -2656, -2679, -2724, -2793, -2886, -3004, -3147, -3312, -3499, -3704, -3923, -4153, -4390, -4627, -4860, -5083, -5291, -5477, -5637, -5765, -5856, -5905, -5910, -5866, -5773, -5627, -5429, -5179, -4878, + -4529, -4134, -3699, -3227, -2725, -2200, -1658, -1107, -555, -11, 516, 1020, 1491, 1922, 2304, 2631, 2897, 3096, 3224, 3277, 3252, 3148, 2964, 2703, 2366, 1955, 1476, 935, 336, -311, -1000, -1722, + -2468, -3227, -3990, -4748, -5490, -6207, -6889, -7529, -8118, -8649, -9115, -9513, -9836, -10083, -10252, -10341, -10352, -10287, -10147, -9938, -9665, -9333, -8949, -8521, -8057, -7566, -7057, -6538, -6019, -5509, -5016, -4549, + -4114, -3720, -3371, -3074, -2833, -2650, -2528, -2468, -2470, -2533, -2654, -2830, -3058, -3332, -3646, -3994, -4370, -4765, -5173, -5586, -5996, -6395, -6777, -7134, -7461, -7752, -8002, -8207, -8363, -8469, -8522, -8524, + -8473, -8373, -8224, -8030, -7796, -7526, -7225, -6898, -6552, -6193, -5828, -5462, -5102, -4754, -4423, -4115, -3833, -3583, -3366, -3186, -3044, -2940, -2875, -2847, -2855, -2896, -2967, -3064, -3182, -3317, -3463, -3616, + -3769, -3916, -4053, -4175, -4276, -4352, -4400, -4416, -4398, -4345, -4256, -4131, -3972, -3780, -3558, -3311, -3042, -2758, -2463, -2164, -1869, -1583, -1316, -1073, -862, -690, -563, -488, -470, -513, -623, -800, + -1048, -1366, -1756, -2214, -2739, -3327, -3973, -4670, -5413, -6193, -7002, -7831, -8670, -9509, -10338, -11146, -11923, -12659, -13344, -13970, -14527, -15008, -15407, -15718, -15935, -16057, -16080, -16004, -15829, -15558, -15192, -14735, + -14195, -13575, -12885, -12131, -11324, -10471, -9582, -8669, -7740, -6807, -5878, -4963, -4072, -3213, -2393, -1619, -898, -234, 368, 908, 1382, 1790, 2133, 2413, 2631, 2791, 2899, 2958, 2974, 2955, 2905, 2833, + 2744, 2646, 2545, 2449, 2361, 2289, 2237, 2209, 2208, 2236, 2297, 2389, 2513, 2668, 2852, 3063, 3296, 3548, 3815, 4091, 4371, 4650, 4921, 5178, 5417, 5632, 5818, 5970, 6085, 6158, 6189, 6174, + 6113, 6005, 5853, 5656, 5418, 5141, 4830, 4490, 4125, 3741, 3345, 2942, 2540, 2144, 1761, 1397, 1057, 749, 475, 240, 47, -100, -202, -256, -264, -227, -146, -25, 131, 319, 533, 767, + 1013, 1265, 1516, 1757, 1982, 2182, 2351, 2481, 2567, 2603, 2584, 2506, 2366, 2162, 1893, 1559, 1161, 702, 185, -385, -1004, -1664, -2359, -3081, -3820, -4569, -5318, -6058, -6778, -7471, -8127, -8738, + -9295, -9791, -10221, -10579, -10859, -11060, -11179, -11215, -11168, -11040, -10834, -10553, -10203, -9790, -9320, -8802, -8244, -7654, -7043, -6420, -5794, -5176, -4576, -4002, -3463, -2967, -2522, -2133, -1808, -1550, -1362, -1247, + -1206, -1240, -1346, -1523, -1767, -2074, -2438, -2855, -3316, -3816, -4345, -4896, -5461, -6032, -6600, -7158, -7699, -8214, -8699, -9146, -9552, -9913, -10224, -10485, -10692, -10848, -10950, -11002, -11006, -10963, -10879, -10756, + -10601, -10417, -10210, -9986, -9750, -9506, -9261, -9019, -8784, -8560, -8349, -8155, -7979, -7822, -7684, -7564, -7461, -7373, -7297, -7230, -7167, -7104, -7036, -6957, -6864, -6749, -6609, -6437, -6230, -5983, -5692, -5354, + -4968, -4531, -4043, -3505, -2917, -2282, -1602, -883, -129, 654, 1460, 2281, 3111, 3940, 4760, 5563, 6339, 7081, 7780, 8428, 9017, 9542, 9995, 10373, 10670, 10884, 11013, 11055, 11012, 10885, 10677, 10391, + 10033, 9609, 9125, 8589, 8011, 7398, 6760, 6107, 5450, 4797, 4159, 3545, 2964, 2425, 1935, 1500, 1127, 820, 584, 419, 329, 312, 368, 495, 688, 944, 1258, 1622, 2029, 2473, 2944, 3435, + 3936, 4438, 4934, 5414, 5870, 6295, 6681, 7023, 7315, 7553, 7734, 7854, 7914, 7912, 7850, 7729, 7553, 7326, 7053, 6739, 6390, 6014, 5618, 5209, 4795, 4385, 3985, 3603, 3247, 2923, 2636, 2393, + 2197, 2052, 1962, 1927, 1950, 2028, 2162, 2350, 2588, 2872, 3200, 3565, 3962, 4384, 4827, 5283, 5746, 6209, 6667, 7114, 7544, 7951, 8333, 8686, 9005, 9291, 9540, 9754, 9931, 10075, 10186, 10267, + 10322, 10355, 10370, 10373, 10367, 10359, 10354, 10357, 10373, 10406, 10462, 10542, 10652, 10792, 10965, 11171, 11410, 11681, 11982, 12310, 12661, 13031, 13414, 13806, 14198, 14585, 14958, 15311, 15636, 15925, 16172, 16369, + 16509, 16587, 16598, 16537, 16400, 16186, 15893, 15521, 15070, 14543, 13943, 13274, 12542, 11752, 10913, 10033, 9119, 8183, 7233, 6280, 5335, 4407, 3507, 2645, 1830, 1071, 376, -246, -792, -1256, -1633, -1920, + -2116, -2222, -2236, -2163, -2006, -1768, -1456, -1076, -636, -144, 390, 959, 1552, 2159, 2772, 3379, 3971, 4541, 5079, 5577, 6030, 6431, 6774, 7057, 7277, 7430, 7518, 7541, 7500, 7397, 7237, 7024, + 6762, 6459, 6119, 5750, 5360, 4954, 4541, 4128, 3721, 3327, 2951, 2600, 2278, 1989, 1736, 1522, 1347, 1212, 1117, 1061, 1041, 1054, 1098, 1167, 1258, 1364, 1482, 1604, 1726, 1842, 1947, 2035, + 2103, 2145, 2159, 2142, 2091, 2005, 1884, 1729, 1540, 1319, 1070, 796, 501, 190, -130, -456, -780, -1097, -1400, -1682, -1938, -2162, -2349, -2494, -2593, -2642, -2640, -2583, -2473, -2308, -2090, -1822, + -1506, -1146, -748, -316, 141, 620, 1111, 1606, 2099, 2581, 3044, 3480, 3882, 4243, 4557, 4818, 5022, 5164, 5241, 5253, 5197, 5075, 4888, 4639, 4330, 3968, 3557, 3104, 2616, 2102, 1568, 1026, + 482, -52, -570, -1062, -1519, -1934, -2300, -2609, -2857, -3039, -3151, -3191, -3158, -3052, -2874, -2627, -2314, -1939, -1510, -1031, -512, 40, 616, 1207, 1803, 2395, 2971, 3524, 4043, 4519, 4945, 5313, + 5615, 5847, 6002, 6079, 6073, 5984, 5811, 5555, 5218, 4804, 4317, 3761, 3142, 2468, 1746, 983, 188, -630, -1464, -2305, -3145, -3975, -4788, -5577, -6336, -7058, -7741, -8378, -8968, -9509, -9998, -10437, + -10825, -11164, -11457, -11707, -11916, -12089, -12231, -12347, -12441, -12518, -12584, -12643, -12699, -12757, -12821, -12893, -12975, -13070, -13178, -13299, -13433, -13579, -13733, -13894, -14058, -14220, -14377, -14523, -14652, -14760, -14841, -14890, + -14900, -14868, -14788, -14657, -14472, -14228, -13926, -13563, -13139, -12656, -12115, -11519, -10872, -10178, -9442, -8672, -7874, -7056, -6226, -5392, -4563, -3748, -2955, -2193, -1471, -796, -176, 383, 876, 1298, 1645, 1914, + 2104, 2215, 2247, 2202, 2084, 1896, 1645, 1336, 976, 574, 138, -322, -799, -1282, -1762, -2229, -2673, -3087, -3461, -3788, -4062, -4276, -4426, -4508, -4519, -4457, -4324, -4118, -3844, -3503, -3100, -2641, + -2132, -1580, -993, -378, 254, 896, 1539, 2173, 2790, 3380, 3937, 4452, 4920, 5333, 5688, 5980, 6206, 6365, 6456, 6479, 6437, 6330, 6164, 5943, 5671, 5356, 5003, 4620, 4214, 3794, 3366, 2940, + 2522, 2120, 1741, 1391, 1076, 800, 569, 385, 250, 167, 135, 153, 222, 337, 496, 695, 930, 1195, 1486, 1795, 2117, 2446, 2776, 3100, 3413, 3710, 3986, 4236, 4457, 4646, 4802, 4922, + 5006, 5055, 5070, 5053, 5007, 4935, 4841, 4729, 4606, 4476, 4344, 4216, 4098, 3994, 3911, 3852, 3821, 3823, 3860, 3935, 4047, 4198, 4387, 4613, 4873, 5164, 5482, 5822, 6178, 6545, 6915, 7282, + 7638, 7976, 8288, 8566, 8805, 8996, 9134, 9214, 9229, 9178, 9056, 8863, 8596, 8256, 7846, 7367, 6823, 6220, 5562, 4857, 4113, 3337, 2539, 1729, 915, 110, -677, -1438, -2161, -2837, -3457, -4014, + -4500, -4908, -5234, -5472, -5621, -5678, -5643, -5517, -5301, -4999, -4616, -4156, -3627, -3035, -2390, -1700, -974, -222, 544, 1317, 2086, 2840, 3572, 4271, 4929, 5540, 6097, 6593, 7025, 7389, 7683, 7906, + 8057, 8137, 8149, 8096, 7981, 7809, 7586, 7317, 7010, 6671, 6308, 5927, 5535, 5140, 4749, 4367, 4000, 3653, 3330, 3035, 2771, 2540, 2341, 2176, 2044, 1942, 1869, 1821, 1795, 1786, 1789, 1800, + 1813, 1823, 1824, 1812, 1780, 1726, 1644, 1532, 1386, 1204, 986, 729, 436, 107, -254, -648, -1069, -1513, -1975, -2449, -2929, -3410, -3884, -4345, -4786, -5201, -5584, -5928, -6229, -6481, -6681, -6826, + -6913, -6941, -6909, -6818, -6669, -6465, -6209, -5905, -5559, -5176, -4763, -4326, -3874, -3412, -2950, -2496, -2055, -1637, -1248, -894, -582, -315, -99, 63, 170, 220, 214, 152, 37, -128, -339, -592, + -879, -1193, -1528, -1874, -2224, -2569, -2899, -3206, -3482, -3716, -3903, -4034, -4104, -4105, -4034, -3886, -3660, -3354, -2968, -2502, -1960, -1344, -659, 88, 893, 1746, 2640, 3566, 4513, 5473, 6434, 7387, + 8322, 9228, 10096, 10918, 11684, 12388, 13022, 13581, 14061, 14458, 14769, 14993, 15131, 15184, 15153, 15043, 14857, 14601, 14281, 13904, 13476, 13005, 12500, 11969, 11420, 10860, 10297, 9740, 9194, 8667, 8163, 7688, + 7245, 6837, 6467, 6137, 5845, 5593, 5378, 5198, 5050, 4932, 4838, 4765, 4707, 4659, 4617, 4575, 4529, 4474, 4405, 4319, 4212, 4082, 3927, 3747, 3539, 3306, 3047, 2766, 2465, 2147, 1816, 1478, + 1136, 796, 465, 147, -151, -424, -667, -874, -1042, -1165, -1241, -1267, -1241, -1163, -1033, -851, -620, -343, -23, 333, 722, 1137, 1571, 2016, 2464, 2908, 3338, 3746, 4125, 4466, 4762, 5005, + 5189, 5309, 5359, 5337, 5240, 5065, 4813, 4484, 4080, 3605, 3062, 2456, 1795, 1085, 334, -448, -1255, -2075, -2898, -3716, -4518, -5295, -6036, -6734, -7381, -7968, -8491, -8942, -9318, -9616, -9833, -9969, + -10024, -9999, -9897, -9723, -9481, -9177, -8817, -8410, -7963, -7484, -6984, -6470, -5951, -5438, -4938, -4460, -4012, -3601, -3232, -2912, -2645, -2434, -2283, -2191, -2160, -2188, -2274, -2414, -2605, -2842, -3119, -3430, + -3769, -4128, -4500, -4877, -5252, -5618, -5966, -6290, -6585, -6844, -7062, -7235, -7361, -7435, -7458, -7429, -7347, -7215, -7034, -6808, -6541, -6236, -5901, -5539, -5157, -4762, -4359, -3956, -3558, -3172, -2802, -2456, + -2137, -1849, -1595, -1379, -1202, -1065, -968, -911, -891, -906, -954, -1030, -1131, -1251, -1385, -1529, -1676, -1822, -1960, -2085, -2194, -2280, -2342, -2374, -2376, -2344, -2280, -2182, -2052, -1891, -1702, -1490, + -1258, -1011, -756, -498, -245, -4, 219, 416, 579, 703, 779, 802, 767, 668, 502, 266, -43, -426, -882, -1410, -2008, -2672, -3397, -4178, -5008, -5880, -6784, -7712, -8654, -9601, -10541, -11465, + -12362, -13222, -14035, -14792, -15484, -16103, -16642, -17096, -17459, -17728, -17901, -17975, -17952, -17832, -17619, -17315, -16927, -16459, -15920, -15316, -14657, -13952, -13209, -12440, -11653, -10860, -10069, -9291, -8534, -7807, -7118, -6473, + -5879, -5340, -4860, -4444, -4091, -3803, -3579, -3418, -3317, -3274, -3283, -3339, -3437, -3571, -3734, -3920, -4120, -4330, -4540, -4746, -4940, -5118, -5275, -5405, -5505, -5574, -5608, -5607, -5571, -5500, -5396, -5262, + -5101, -4916, -4712, -4494, -4267, -4037, -3810, -3590, -3384, -3197, -3034, -2899, -2797, -2731, -2704, -2717, -2772, -2868, -3006, -3184, -3399, -3648, -3928, -4233, -4560, -4902, -5253, -5607, -5958, -6298, -6623, -6924, + -7197, -7437, -7637, -7795, -7906, -7969, -7980, -7941, -7850, -7708, -7519, -7284, -7008, -6695, -6351, -5982, -5595, -5196, -4794, -4395, -4008, -3639, -3297, -2988, -2719, -2495, -2322, -2203, -2143, -2144, -2206, -2330, + -2515, -2760, -3060, -3412, -3811, -4251, -4724, -5223, -5740, -6266, -6791, -7308, -7806, -8276, -8710, -9099, -9434, -9710, -9919, -10056, -10116, -10096, -9995, -9810, -9542, -9193, -8764, -8261, -7688, -7051, -6357, -5613, + -4828, -4011, -3173, -2321, -1467, -620, 209, 1012, 1780, 2505, 3177, 3792, 4342, 4824, 5232, 5565, 5822, 6001, 6103, 6131, 6088, 5977, 5804, 5573, 5293, 4969, 4610, 4222, 3815, 3396, 2974, 2556, + 2149, 1761, 1398, 1065, 768, 511, 297, 129, 8, -66, -93, -74, -12, 89, 229, 402, 603, 828, 1070, 1325, 1588, 1852, 2113, 2366, 2606, 2831, 3036, 3219, 3378, 3512, 3621, 3705, + 3766, 3805, 3825, 3831, 3825, 3812, 3797, 3785, 3781, 3792, 3821, 3875, 3958, 4074, 4227, 4421, 4658, 4940, 5267, 5639, 6056, 6516, 7015, 7550, 8116, 8707, 9318, 9941, 10568, 11193, 11805, 12398, + 12962, 13489, 13970, 14398, 14766, 15066, 15292, 15440, 15505, 15484, 15375, 15177, 14890, 14517, 14059, 13520, 12905, 12221, 11473, 10671, 9822, 8935, 8020, 7086, 6145, 5205, 4278, 3372, 2496, 1660, 871, 136, + -537, -1145, -1685, -2151, -2544, -2862, -3107, -3279, -3383, -3422, -3401, -3327, -3207, -3047, -2855, -2641, -2413, -2179, -1948, -1728, -1528, -1356, -1217, -1119, -1067, -1064, -1116, -1224, -1389, -1611, -1891, -2226, + -2612, -3047, -3525, -4041, -4588, -5160, -5748, -6347, -6947, -7541, -8121, -8679, -9208, -9702, -10154, -10559, -10912, -11209, -11447, -11624, -11739, -11793, -11785, -11718, -11594, -11417, -11191, -10921, -10613, -10271, -9903, -9515, + -9112, -8703, -8292, -7886, -7490, -7109, -6748, -6410, -6099, -5816, -5564, -5342, -5150, -4988, -4853, -4743, -4655, -4584, -4527, -4479, -4434, -4388, -4335, -4270, -4188, -4085, -3955, -3797, -3605, -3379, -3116, -2815, + -2477, -2103, -1694, -1253, -784, -292, 218, 741, 1271, 1800, 2320, 2825, 3307, 3757, 4169, 4536, 4850, 5106, 5298, 5422, 5473, 5450, 5350, 5172, 4918, 4589, 4188, 3719, 3187, 2599, 1960, 1279, + 565, -172, -924, -1680, -2431, -3165, -3872, -4544, -5170, -5741, -6249, -6688, -7050, -7330, -7525, -7631, -7647, -7572, -7407, -7154, -6817, -6399, -5907, -5347, -4726, -4052, -3335, -2584, -1808, -1017, -220, 570, + 1347, 2101, 2823, 3506, 4142, 4725, 5250, 5713, 6110, 6441, 6703, 6898, 7026, 7090, 7093, 7040, 6935, 6784, 6594, 6371, 6122, 5854, 5575, 5291, 5010, 4738, 4482, 4246, 4036, 3855, 3707, 3595, + 3520, 3483, 3483, 3519, 3590, 3692, 3823, 3979, 4154, 4343, 4543, 4746, 4947, 5142, 5324, 5489, 5631, 5748, 5834, 5887, 5906, 5888, 5833, 5741, 5614, 5453, 5261, 5042, 4800, 4540, 4267, 3986, + 3705, 3428, 3164, 2917, 2694, 2501, 2344, 2227, 2154, 2130, 2156, 2235, 2368, 2555, 2795, 3085, 3424, 3807, 4229, 4686, 5170, 5677, 6197, 6724, 7249, 7766, 8265, 8739, 9182, 9585, 9943, 10249, + 10500, 10690, 10817, 10880, 10876, 10806, 10672, 10475, 10220, 9912, 9554, 9154, 8719, 8257, 7776, 7285, 6792, 6308, 5840, 5398, 4990, 4624, 4308, 4049, 3851, 3720, 3659, 3672, 3759, 3921, 4156, 4463, + 4838, 5277, 5773, 6321, 6914, 7542, 8197, 8871, 9553, 10233, 10902, 11549, 12167, 12745, 13275, 13749, 14160, 14503, 14772, 14963, 15074, 15102, 15047, 14910, 14693, 14398, 14029, 13591, 13091, 12534, 11927, 11279, + 10597, 9890, 9166, 8434, 7701, 6977, 6268, 5582, 4924, 4301, 3718, 3177, 2684, 2239, 1844, 1499, 1204, 957, 757, 599, 481, 399, 347, 322, 318, 329, 352, 381, 411, 438, 459, 470, + 469, 453, 422, 375, 313, 237, 149, 51, -53, -159, -264, -361, -447, -515, -560, -577, -561, -506, -407, -262, -66, 183, 488, 850, 1269, 1744, 2273, 2854, 3483, 4157, 4868, 5612, + 6381, 7168, 7964, 8762, 9553, 10327, 11076, 11792, 12466, 13090, 13656, 14159, 14592, 14951, 15230, 15429, 15544, 15576, 15524, 15391, 15180, 14895, 14540, 14123, 13649, 13127, 12566, 11973, 11359, 10733, 10105, 9484, + 8880, 8301, 7757, 7254, 6800, 6401, 6062, 5788, 5580, 5441, 5372, 5371, 5437, 5568, 5758, 6002, 6296, 6631, 7001, 7397, 7811, 8234, 8657, 9071, 9468, 9838, 10174, 10468, 10714, 10906, 11039, 11108, + 11111, 11046, 10913, 10710, 10441, 10106, 9711, 9259, 8755, 8206, 7618, 6999, 6356, 5697, 5030, 4362, 3703, 3059, 2437, 1844, 1286, 768, 294, -130, -504, -826, -1095, -1311, -1476, -1590, -1658, -1683, + -1668, -1620, -1543, -1443, -1326, -1198, -1066, -934, -810, -697, -601, -527, -478, -456, -464, -504, -575, -679, -812, -974, -1162, -1372, -1601, -1844, -2095, -2351, -2604, -2850, -3083, -3298, -3489, -3652, + -3783, -3877, -3932, -3946, -3916, -3844, -3728, -3570, -3373, -3140, -2874, -2580, -2264, -1931, -1589, -1243, -903, -575, -266, 14, 261, 467, 626, 731, 779, 764, 683, 535, 319, 34, -317, -734, + -1213, -1749, -2336, -2968, -3637, -4335, -5054, -5784, -6515, -7237, -7940, -8614, -9250, -9837, -10368, -10834, -11227, -11542, -11772, -11913, -11963, -11919, -11781, -11550, -11226, -10814, -10318, -9743, -9095, -8382, -7613, -6796, + -5940, -5056, -4154, -3243, -2334, -1437, -561, 284, 1090, 1850, 2556, 3204, 3787, 4302, 4747, 5119, 5419, 5647, 5805, 5895, 5921, 5888, 5801, 5665, 5488, 5276, 5035, 4773, 4497, 4214, 3930, 3651, + 3384, 3133, 2903, 2697, 2518, 2368, 2248, 2158, 2097, 2063, 2055, 2070, 2102, 2149, 2206, 2267, 2327, 2381, 2423, 2448, 2451, 2427, 2372, 2283, 2156, 1989, 1781, 1531, 1239, 906, 535, 127, + -313, -782, -1275, -1787, -2310, -2840, -3369, -3892, -4400, -4888, -5350, -5779, -6171, -6520, -6821, -7073, -7271, -7416, -7505, -7539, -7519, -7448, -7329, -7165, -6962, -6725, -6461, -6175, -5876, -5571, -5267, -4973, + -4695, -4442, -4219, -4034, -3893, -3799, -3759, -3774, -3848, -3980, -4173, -4423, -4731, -5091, -5501, -5954, -6446, -6968, -7514, -8075, -8643, -9208, -9763, -10297, -10802, -11269, -11690, -12058, -12366, -12607, -12777, -12871, + -12887, -12824, -12679, -12455, -12153, -11776, -11328, -10815, -10243, -9619, -8952, -8251, -7524, -6781, -6032, -5288, -4559, -3853, -3182, -2553, -1975, -1456, -1002, -619, -312, -84, 60, 123, 102, 0, -182, -442, + -774, -1173, -1634, -2149, -2712, -3315, -3950, -4608, -5281, -5961, -6641, -7312, -7969, -8603, -9209, -9783, -10320, -10817, -11271, -11681, -12047, -12368, -12645, -12882, -13080, -13243, -13374, -13479, -13562, -13627, -13680, -13726, + -13771, -13817, -13871, -13935, -14014, -14110, -14224, -14359, -14514, -14690, -14886, -15099, -15327, -15567, -15815, -16067, -16317, -16560, -16791, -17005, -17194, -17355, -17480, -17566, -17608, -17601, -17543, -17430, -17261, -17035, -16752, -16412, + -16018, -15573, -15080, -14544, -13971, -13367, -12739, -12093, -11439, -10785, -10138, -9508, -8903, -8330, -7798, -7314, -6885, -6516, -6213, -5979, -5818, -5730, -5718, -5781, -5917, -6123, -6396, -6730, -7120, -7559, -8038, -8551, + -9087, -9638, -10193, -10743, -11279, -11791, -12268, -12704, -13089, -13417, -13680, -13874, -13993, -14035, -13996, -13877, -13678, -13399, -13044, -12616, -12119, -11561, -10948, -10286, -9584, -8851, -8095, -7326, -6553, -5785, -5030, -4298, + -3597, -2932, -2312, -1742, -1227, -770, -374, -41, 228, 436, 583, 671, 706, 690, 629, 530, 399, 243, 69, -114, -300, -481, -651, -801, -927, -1021, -1079, -1096, -1070, -997, -876, -706, + -488, -222, 88, 441, 833, 1259, 1713, 2192, 2688, 3195, 3707, 4217, 4720, 5209, 5678, 6122, 6536, 6916, 7259, 7562, 7822, 8039, 8213, 8344, 8434, 8485, 8500, 8483, 8437, 8368, 8280, 8180, + 8072, 7962, 7855, 7758, 7675, 7609, 7567, 7549, 7561, 7602, 7675, 7779, 7913, 8077, 8267, 8480, 8712, 8957, 9211, 9467, 9719, 9959, 10181, 10377, 10539, 10662, 10738, 10761, 10725, 10627, 10460, 10223, + 9913, 9529, 9072, 8541, 7940, 7271, 6540, 5752, 4913, 4032, 3116, 2174, 1216, 252, -707, -1652, -2571, -3455, -4294, -5078, -5798, -6446, -7015, -7499, -7893, -8193, -8395, -8499, -8504, -8412, -8224, -7944, + -7578, -7130, -6608, -6019, -5372, -4676, -3939, -3173, -2387, -1591, -794, -7, 760, 1501, 2207, 2871, 3486, 4048, 4552, 4996, 5376, 5692, 5945, 6136, 6267, 6341, 6362, 6335, 6267, 6162, 6027, 5869, + 5696, 5513, 5328, 5147, 4976, 4821, 4688, 4580, 4501, 4455, 4442, 4465, 4523, 4616, 4742, 4899, 5083, 5292, 5520, 5763, 6016, 6272, 6527, 6774, 7008, 7224, 7415, 7578, 7709, 7803, 7858, 7871, + 7842, 7770, 7655, 7498, 7302, 7070, 6805, 6511, 6193, 5858, 5510, 5156, 4802, 4455, 4121, 3806, 3516, 3257, 3034, 2850, 2709, 2615, 2568, 2571, 2622, 2721, 2866, 3054, 3281, 3543, 3835, 4150, + 4481, 4823, 5167, 5505, 5831, 6136, 6413, 6655, 6856, 7009, 7109, 7151, 7133, 7052, 6905, 6694, 6417, 6078, 5679, 5224, 4718, 4167, 3577, 2957, 2314, 1658, 996, 339, -304, -925, -1514, -2063, + -2563, -3007, -3389, -3702, -3943, -4106, -4191, -4196, -4119, -3964, -3732, -3426, -3051, -2614, -2120, -1577, -993, -378, 258, 909, 1562, 2208, 2837, 3440, 4008, 4533, 5006, 5422, 5773, 6055, 6264, 6397, + 6453, 6431, 6332, 6158, 5911, 5596, 5217, 4780, 4292, 3759, 3189, 2591, 1972, 1340, 703, 71, -550, -1153, -1731, -2278, -2789, -3259, -3685, -4064, -4395, -4676, -4907, -5091, -5228, -5321, -5374, -5391, + -5375, -5332, -5267, -5185, -5092, -4992, -4891, -4794, -4705, -4628, -4565, -4521, -4496, -4492, -4509, -4546, -4603, -4678, -4768, -4869, -4977, -5089, -5199, -5302, -5393, -5466, -5516, -5538, -5527, -5478, -5387, -5252, + -5068, -4835, -4551, -4216, -3831, -3397, -2918, -2397, -1838, -1247, -630, 5, 653, 1304, 1952, 2586, 3199, 3782, 4327, 4825, 5270, 5653, 5970, 6214, 6382, 6470, 6475, 6397, 6235, 5990, 5666, 5265, + 4792, 4253, 3655, 3004, 2310, 1581, 827, 56, -718, -1490, -2248, -2981, -3682, -4341, -4951, -5503, -5993, -6415, -6764, -7038, -7235, -7354, -7396, -7363, -7257, -7083, -6846, -6552, -6208, -5820, -5397, -4948, + -4481, -4005, -3529, -3062, -2611, -2184, -1790, -1433, -1121, -858, -647, -491, -393, -352, -368, -440, -565, -740, -959, -1218, -1511, -1830, -2170, -2522, -2878, -3231, -3574, -3898, -4197, -4464, -4692, -4877, + -5012, -5096, -5123, -5093, -5005, -4857, -4651, -4389, -4073, -3706, -3294, -2840, -2350, -1830, -1287, -726, -154, 421, 995, 1561, 2113, 2646, 3155, 3637, 4087, 4505, 4887, 5234, 5545, 5821, 6064, 6276, + 6460, 6620, 6760, 6885, 6999, 7109, 7218, 7332, 7457, 7596, 7754, 7936, 8144, 8380, 8647, 8946, 9276, 9637, 10027, 10443, 10883, 11341, 11813, 12293, 12775, 13251, 13715, 14160, 14576, 14958, 15297, 15587, + 15820, 15990, 16093, 16122, 16075, 15948, 15740, 15450, 15077, 14625, 14095, 13492, 12820, 12085, 11294, 10456, 9578, 8671, 7743, 6804, 5866, 4939, 4032, 3157, 2322, 1536, 808, 146, -443, -955, -1384, -1729, + -1985, -2153, -2233, -2227, -2137, -1968, -1725, -1414, -1042, -618, -149, 354, 883, 1429, 1981, 2530, 3066, 3580, 4063, 4509, 4909, 5258, 5551, 5782, 5949, 6050, 6084, 6051, 5952, 5790, 5567, 5289, + 4959, 4585, 4171, 3725, 3253, 2764, 2264, 1761, 1261, 772, 299, -150, -573, -963, -1319, -1636, -1913, -2149, -2344, -2499, -2616, -2697, -2745, -2764, -2759, -2734, -2695, -2646, -2594, -2544, -2501, -2470, + -2456, -2464, -2497, -2557, -2648, -2771, -2926, -3113, -3332, -3580, -3855, -4154, -4472, -4805, -5148, -5494, -5838, -6174, -6494, -6794, -7066, -7304, -7504, -7660, -7768, -7824, -7826, -7770, -7657, -7485, -7256, -6972, + -6635, -6248, -5817, -5347, -4843, -4313, -3764, -3202, -2637, -2076, -1526, -997, -495, -27, 398, 778, 1106, 1377, 1588, 1738, 1824, 1847, 1807, 1708, 1551, 1342, 1085, 787, 455, 95, -283, -671, + -1061, -1444, -1810, -2150, -2457, -2722, -2938, -3098, -3196, -3227, -3188, -3076, -2889, -2626, -2290, -1881, -1404, -862, -260, 392, 1092, 1828, 2593, 3377, 4170, 4964, 5747, 6510, 7243, 7938, 8584, 9176, + 9705, 10164, 10550, 10856, 11081, 11222, 11278, 11250, 11139, 10948, 10680, 10341, 9935, 9468, 8949, 8384, 7782, 7150, 6498, 5833, 5163, 4498, 3844, 3208, 2598, 2018, 1474, 970, 510, 94, -273, -593, + -867, -1095, -1278, -1421, -1527, -1599, -1642, -1660, -1660, -1645, -1620, -1592, -1564, -1541, -1526, -1523, -1535, -1563, -1609, -1673, -1755, -1854, -1969, -2095, -2231, -2373, -2515, -2654, -2784, -2899, -2995, -3066, + -3106, -3110, -3074, -2993, -2863, -2682, -2448, -2158, -1814, -1413, -960, -455, 97, 694, 1329, 1996, 2690, 3402, 4125, 4850, 5570, 6274, 6955, 7603, 8211, 8771, 9275, 9716, 10088, 10388, 10609, 10750, + 10809, 10784, 10677, 10489, 10222, 9881, 9471, 8997, 8467, 7888, 7269, 6619, 5947, 5262, 4574, 3894, 3229, 2591, 1986, 1424, 911, 454, 58, -272, -534, -725, -845, -894, -873, -785, -634, -424, + -163, 144, 490, 866, 1265, 1676, 2092, 2504, 2902, 3279, 3625, 3934, 4199, 4413, 4572, 4671, 4707, 4677, 4582, 4421, 4195, 3906, 3558, 3156, 2703, 2206, 1672, 1108, 521, -80, -689, -1298, + -1897, -2481, -3041, -3570, -4064, -4516, -4922, -5279, -5583, -5832, -6027, -6166, -6252, -6285, -6269, -6207, -6103, -5963, -5792, -5595, -5378, -5148, -4910, -4671, -4436, -4212, -4002, -3812, -3645, -3504, -3392, -3311, + -3261, -3242, -3253, -3293, -3359, -3447, -3555, -3678, -3810, -3948, -4085, -4215, -4334, -4437, -4517, -4570, -4592, -4579, -4529, -4438, -4305, -4130, -3913, -3655, -3358, -3026, -2661, -2269, -1854, -1424, -984, -542, + -105, 319, 723, 1100, 1442, 1743, 1995, 2192, 2330, 2405, 2412, 2350, 2217, 2013, 1739, 1397, 990, 523, 0, -570, -1183, -1830, -2501, -3188, -3881, -4570, -5245, -5895, -6512, -7085, -7606, -8066, + -8458, -8775, -9012, -9163, -9226, -9199, -9080, -8869, -8570, -8184, -7715, -7169, -6551, -5870, -5133, -4349, -3526, -2676, -1808, -932, -58, 803, 1644, 2453, 3223, 3946, 4615, 5223, 5767, 6242, 6645, 6974, + 7229, 7410, 7519, 7559, 7533, 7445, 7301, 7107, 6868, 6592, 6286, 5956, 5610, 5255, 4898, 4545, 4202, 3874, 3565, 3281, 3023, 2793, 2594, 2425, 2286, 2176, 2092, 2032, 1993, 1969, 1957, 1951, + 1947, 1939, 1922, 1890, 1839, 1764, 1660, 1525, 1354, 1146, 899, 612, 286, -78, -480, -916, -1383, -1877, -2393, -2925, -3467, -4014, -4559, -5095, -5615, -6113, -6583, -7017, -7411, -7760, -8059, -8305, + -8495, -8626, -8699, -8713, -8669, -8569, -8416, -8215, -7968, -7683, -7364, -7020, -6656, -6280, -5901, -5525, -5160, -4813, -4493, -4204, -3954, -3747, -3587, -3479, -3424, -3425, -3481, -3592, -3756, -3970, -4230, -4531, + -4868, -5233, -5619, -6019, -6423, -6823, -7210, -7576, -7910, -8205, -8453, -8645, -8775, -8837, -8825, -8736, -8566, -8314, -7978, -7560, -7061, -6484, -5833, -5114, -4332, -3495, -2611, -1689, -738, 231, 1210, 2188, + 3155, 4100, 5015, 5889, 6715, 7484, 8189, 8824, 9385, 9867, 10268, 10586, 10820, 10971, 11040, 11032, 10949, 10797, 10582, 10308, 9985, 9619, 9218, 8789, 8342, 7885, 7424, 6968, 6523, 6097, 5694, 5320, + 4980, 4676, 4411, 4186, 4003, 3861, 3758, 3694, 3664, 3667, 3697, 3751, 3824, 3910, 4005, 4103, 4199, 4289, 4368, 4431, 4476, 4499, 4498, 4472, 4419, 4341, 4236, 4108, 3958, 3790, 3607, 3414, + 3215, 3015, 2820, 2636, 2468, 2322, 2203, 2116, 2066, 2058, 2093, 2177, 2309, 2492, 2726, 3009, 3340, 3717, 4136, 4592, 5079, 5593, 6125, 6669, 7217, 7761, 8291, 8801, 9280, 9722, 10119, 10462, + 10746, 10964, 11111, 11183, 11178, 11092, 10925, 10677, 10350, 9946, 9469, 8924, 8316, 7652, 6941, 6190, 5408, 4605, 3790, 2973, 2164, 1373, 609, -117, -801, -1432, -2004, -2512, -2950, -3314, -3603, -3815, + -3949, -4007, -3991, -3904, -3751, -3537, -3270, -2954, -2600, -2215, -1807, -1386, -961, -541, -134, 250, 605, 922, 1196, 1421, 1591, 1702, 1753, 1742, 1667, 1530, 1331, 1075, 763, 402, -4, -449, + -926, -1429, -1949, -2480, -3012, -3540, -4054, -4549, -5018, -5454, -5853, -6209, -6520, -6781, -6992, -7150, -7257, -7313, -7320, -7280, -7197, -7076, -6921, -6737, -6530, -6306, -6072, -5834, -5599, -5371, -5157, -4963, + -4793, -4652, -4542, -4468, -4430, -4429, -4468, -4544, -4656, -4802, -4980, -5186, -5415, -5664, -5926, -6197, -6470, -6742, -7005, -7254, -7485, -7693, -7874, -8024, -8140, -8221, -8265, -8272, -8243, -8178, -8081, -7955, + -7803, -7631, -7444, -7249, -7051, -6857, -6675, -6513, -6377, -6274, -6211, -6195, -6231, -6325, -6481, -6701, -6990, -7347, -7774, -8269, -8831, -9456, -10141, -10879, -11665, -12491, -13350, -14232, -15127, -16028, -16922, -17799, + -18650, -19464, -20231, -20942, -21587, -22159, -22650, -23055, -23367, -23582, -23699, -23714, -23629, -23443, -23159, -22780, -22312, -21759, -21128, -20427, -19665, -18850, -17992, -17100, -16185, -15258, -14327, -13403, -12494, -11611, -10761, -9950, + -9187, -8476, -7822, -7228, -6696, -6228, -5824, -5482, -5202, -4979, -4810, -4690, -4615, -4577, -4571, -4591, -4628, -4677, -4731, -4783, -4826, -4856, -4867, -4855, -4815, -4745, -4643, -4507, -4338, -4135, -3900, -3635, + -3344, -3030, -2697, -2350, -1994, -1635, -1278, -930, -596, -281, 7, 268, 494, 683, 831, 936, 996, 1012, 983, 910, 795, 641, 451, 230, -17, -285, -570, -863, -1159, -1452, -1734, -2000, + -2244, -2460, -2643, -2789, -2894, -2954, -2969, -2936, -2856, -2728, -2556, -2341, -2088, -1799, -1482, -1140, -782, -413, -41, 325, 680, 1015, 1323, 1597, 1830, 2016, 2150, 2227, 2244, 2198, 2086, 1909, + 1668, 1363, 998, 576, 102, -416, -975, -1566, -2181, -2810, -3446, -4079, -4699, -5297, -5864, -6391, -6871, -7295, -7657, -7950, -8170, -8312, -8375, -8356, -8255, -8073, -7812, -7475, -7067, -6593, -6060, -5474, + -4844, -4179, -3488, -2781, -2067, -1356, -657, 17, 662, 1266, 1823, 2325, 2766, 3141, 3445, 3677, 3833, 3914, 3919, 3851, 3711, 3505, 3236, 2909, 2533, 2112, 1655, 1169, 663, 145, -376, -894, + -1401, -1889, -2351, -2781, -3175, -3527, -3833, -4092, -4300, -4458, -4564, -4621, -4630, -4592, -4512, -4394, -4242, -4060, -3854, -3629, -3390, -3143, -2894, -2646, -2405, -2174, -1957, -1757, -1576, -1415, -1275, -1155, + -1056, -974, -908, -854, -808, -767, -726, -679, -622, -549, -454, -333, -181, 6, 234, 506, 824, 1189, 1605, 2069, 2582, 3143, 3748, 4394, 5077, 5791, 6531, 7289, 8058, 8830, 9597, 10351, + 11082, 11783, 12444, 13057, 13616, 14112, 14540, 14893, 15168, 15359, 15466, 15486, 15419, 15266, 15029, 14711, 14318, 13853, 13324, 12737, 12102, 11426, 10720, 9992, 9252, 8511, 7779, 7064, 6377, 5726, 5120, 4564, + 4067, 3633, 3267, 2971, 2749, 2600, 2525, 2521, 2586, 2717, 2908, 3154, 3448, 3782, 4149, 4541, 4948, 5363, 5775, 6177, 6561, 6918, 7241, 7523, 7759, 7944, 8074, 8145, 8156, 8107, 7997, 7828, + 7603, 7324, 6997, 6626, 6218, 5779, 5316, 4836, 4348, 3860, 3378, 2912, 2467, 2051, 1671, 1332, 1039, 797, 608, 475, 399, 381, 421, 516, 665, 865, 1111, 1398, 1723, 2078, 2458, 2856, + 3267, 3683, 4099, 4508, 4905, 5285, 5642, 5973, 6275, 6545, 6781, 6983, 7151, 7284, 7386, 7458, 7503, 7525, 7528, 7517, 7497, 7472, 7449, 7433, 7428, 7440, 7473, 7531, 7617, 7735, 7886, 8071, + 8292, 8546, 8834, 9152, 9497, 9866, 10253, 10653, 11059, 11466, 11866, 12251, 12614, 12947, 13244, 13496, 13698, 13842, 13924, 13937, 13879, 13746, 13537, 13249, 12884, 12443, 11928, 11343, 10693, 9984, 9221, 8414, + 7570, 6699, 5810, 4913, 4018, 3137, 2278, 1453, 671, -58, -728, -1329, -1855, -2300, -2660, -2931, -3110, -3197, -3192, -3096, -2913, -2646, -2300, -1881, -1396, -854, -263, 368, 1030, 1714, 2408, 3104, + 3792, 4463, 5107, 5717, 6286, 6807, 7274, 7683, 8030, 8313, 8531, 8684, 8772, 8798, 8764, 8674, 8533, 8347, 8121, 7862, 7577, 7273, 6958, 6637, 6320, 6011, 5717, 5445, 5199, 4983, 4802, 4657, + 4551, 4485, 4459, 4472, 4522, 4607, 4724, 4869, 5038, 5225, 5425, 5633, 5844, 6051, 6250, 6434, 6600, 6742, 6857, 6941, 6992, 7008, 6989, 6933, 6842, 6717, 6561, 6377, 6168, 5940, 5697, 5444, + 5189, 4936, 4692, 4464, 4257, 4077, 3931, 3823, 3757, 3737, 3767, 3848, 3981, 4168, 4407, 4697, 5035, 5417, 5840, 6298, 6785, 7294, 7819, 8352, 8885, 9411, 9920, 10406, 10860, 11276, 11646, 11966, + 12228, 12430, 12568, 12638, 12640, 12574, 12439, 12239, 11976, 11655, 11279, 10856, 10392, 9895, 9373, 8834, 8287, 7741, 7205, 6689, 6200, 5748, 5339, 4980, 4678, 4438, 4264, 4159, 4125, 4163, 4271, 4449, + 4693, 5000, 5363, 5778, 6237, 6732, 7255, 7796, 8346, 8896, 9435, 9955, 10444, 10895, 11299, 11646, 11932, 12148, 12289, 12352, 12333, 12229, 12040, 11766, 11409, 10971, 10455, 9867, 9211, 8494, 7723, 6905, + 6050, 5164, 4256, 3336, 2411, 1490, 580, -310, -1175, -2009, -2806, -3562, -4272, -4935, -5549, -6113, -6626, -7091, -7509, -7882, -8213, -8507, -8767, -8999, -9207, -9396, -9571, -9737, -9898, -10060, -10225, -10397, + -10579, -10773, -10980, -11201, -11435, -11682, -11939, -12205, -12476, -12749, -13018, -13280, -13529, -13760, -13967, -14144, -14287, -14390, -14448, -14457, -14413, -14313, -14154, -13935, -13656, -13315, -12916, -12459, -11949, -11388, -10782, -10138, + -9461, -8758, -8038, -7309, -6579, -5857, -5151, -4471, -3825, -3221, -2667, -2168, -1733, -1365, -1069, -849, -706, -641, -655, -746, -912, -1149, -1451, -1815, -2231, -2695, -3196, -3727, -4278, -4840, -5402, -5957, + -6493, -7002, -7476, -7906, -8285, -8606, -8865, -9056, -9177, -9224, -9198, -9099, -8927, -8686, -8379, -8011, -7587, -7115, -6601, -6054, -5482, -4894, -4298, -3704, -3122, -2558, -2023, -1523, -1066, -658, -304, -9, + 222, 389, 490, 524, 493, 398, 243, 31, -232, -543, -893, -1276, -1684, -2111, -2548, -2987, -3420, -3841, -4241, -4615, -4955, -5257, -5516, -5728, -5890, -6001, -6059, -6065, -6018, -5922, -5779, -5592, + -5365, -5103, -4812, -4497, -4165, -3820, -3470, -3121, -2778, -2448, -2134, -1843, -1577, -1341, -1136, -965, -829, -727, -659, -623, -617, -639, -683, -747, -825, -913, -1004, -1094, -1177, -1247, -1300, -1330, + -1333, -1305, -1243, -1144, -1008, -832, -617, -364, -74, 248, 600, 978, 1377, 1789, 2210, 2632, 3048, 3450, 3830, 4183, 4499, 4772, 4996, 5164, 5272, 5314, 5288, 5191, 5021, 4778, 4463, 4077, + 3624, 3109, 2535, 1910, 1240, 534, -199, -952, -1714, -2474, -3224, -3952, -4649, -5305, -5911, -6459, -6940, -7348, -7677, -7922, -8080, -8147, -8123, -8008, -7802, -7509, -7133, -6678, -6150, -5556, -4905, -4204, + -3462, -2690, -1897, -1093, -289, 505, 1281, 2028, 2740, 3406, 4021, 4578, 5072, 5499, 5856, 6141, 6353, 6493, 6562, 6562, 6496, 6370, 6188, 5956, 5680, 5367, 5024, 4657, 4275, 3884, 3491, 3103, + 2725, 2363, 2021, 1704, 1415, 1156, 928, 733, 569, 436, 332, 253, 197, 160, 136, 122, 111, 99, 81, 50, 2, -67, -162, -289, -448, -643, -876, -1147, -1457, -1805, -2190, -2608, + -3057, -3533, -4031, -4546, -5072, -5603, -6133, -6655, -7162, -7648, -8106, -8530, -8915, -9256, -9547, -9786, -9970, -10096, -10163, -10172, -10123, -10018, -9861, -9653, -9401, -9110, -8785, -8434, -8062, -7679, -7291, -6906, + -6532, -6176, -5846, -5548, -5288, -5073, -4906, -4791, -4731, -4728, -4781, -4892, -5058, -5277, -5545, -5856, -6207, -6589, -6996, -7421, -7854, -8287, -8711, -9117, -9497, -9841, -10141, -10390, -10581, -10707, -10764, -10746, + -10650, -10475, -10219, -9883, -9468, -8977, -8414, -7783, -7092, -6347, -5556, -4727, -3870, -2995, -2111, -1228, -357, 490, 1308, 2084, 2811, 3481, 4086, 4620, 5078, 5456, 5750, 5960, 6084, 6122, 6078, 5952, + 5750, 5475, 5134, 4732, 4277, 3776, 3238, 2669, 2079, 1475, 865, 258, -340, -922, -1482, -2015, -2517, -2983, -3411, -3798, -4145, -4451, -4717, -4945, -5136, -5295, -5425, -5530, -5615, -5684, -5744, -5798, + -5851, -5910, -5977, -6058, -6155, -6272, -6411, -6574, -6761, -6972, -7207, -7465, -7741, -8035, -8342, -8658, -8977, -9296, -9608, -9907, -10189, -10446, -10674, -10868, -11022, -11132, -11194, -11206, -11165, -11070, -10920, -10716, + -10459, -10152, -9798, -9402, -8968, -8502, -8012, -7503, -6984, -6464, -5949, -5449, -4972, -4526, -4119, -3759, -3451, -3203, -3019, -2904, -2861, -2892, -2999, -3181, -3437, -3765, -4162, -4622, -5140, -5709, -6322, -6971, + -7647, -8340, -9042, -9741, -10429, -11095, -11730, -12325, -12872, -13362, -13789, -14147, -14430, -14636, -14761, -14803, -14762, -14639, -14437, -14158, -13807, -13389, -12910, -12378, -11800, -11184, -10540, -9876, -9201, -8525, -7857, -7204, + -6576, -5979, -5421, -4907, -4443, -4033, -3680, -3385, -3150, -2975, -2858, -2797, -2788, -2828, -2911, -3031, -3183, -3359, -3551, -3754, -3958, -4157, -4343, -4509, -4648, -4755, -4825, -4852, -4833, -4764, -4645, -4473, + -4250, -3974, -3650, -3278, -2864, -2409, -1921, -1404, -863, -306, 261, 832, 1402, 1962, 2508, 3034, 3534, 4005, 4442, 4842, 5203, 5524, 5804, 6043, 6242, 6403, 6528, 6622, 6688, 6730, 6753, 6762, + 6764, 6763, 6764, 6774, 6796, 6837, 6899, 6986, 7102, 7248, 7425, 7634, 7875, 8145, 8443, 8766, 9110, 9469, 9839, 10213, 10584, 10947, 11293, 11616, 11908, 12162, 12370, 12528, 12627, 12664, 12634, 12533, + 12358, 12108, 11783, 11382, 10908, 10363, 9752, 9080, 8352, 7577, 6761, 5913, 5043, 4161, 3277, 2400, 1543, 714, -76, -817, -1501, -2119, -2664, -3129, -3508, -3798, -3994, -4096, -4102, -4012, -3830, -3558, + -3199, -2761, -2248, -1669, -1031, -344, 383, 1141, 1920, 2710, 3500, 4281, 5044, 5780, 6480, 7138, 7746, 8300, 8794, 9225, 9591, 9890, 10122, 10289, 10391, 10433, 10417, 10348, 10232, 10075, 9882, 9662, + 9420, 9163, 8899, 8635, 8376, 8128, 7898, 7689, 7506, 7352, 7230, 7140, 7085, 7062, 7072, 7113, 7181, 7273, 7385, 7513, 7652, 7795, 7939, 8076, 8201, 8310, 8396, 8454, 8482, 8474, 8428, 8341, + 8212, 8040, 7826, 7569, 7273, 6940, 6573, 6176, 5754, 5313, 4858, 4396, 3932, 3475, 3029, 2601, 2198, 1825, 1487, 1190, 936, 729, 572, 465, 410, 405, 450, 541, 676, 851, 1059, 1296, + 1556, 1831, 2115, 2399, 2676, 2939, 3180, 3392, 3569, 3703, 3790, 3825, 3804, 3724, 3583, 3381, 3117, 2793, 2411, 1976, 1491, 963, 398, -197, -814, -1446, -2081, -2713, -3330, -3925, -4487, -5009, + -5481, -5896, -6248, -6529, -6736, -6863, -6909, -6871, -6749, -6543, -6256, -5890, -5450, -4941, -4369, -3742, -3068, -2356, -1614, -853, -81, 689, 1450, 2191, 2904, 3579, 4208, 4785, 5302, 5754, 6137, 6447, + 6683, 6842, 6926, 6936, 6874, 6743, 6549, 6295, 5989, 5636, 5245, 4823, 4378, 3918, 3450, 2984, 2527, 2085, 1667, 1277, 922, 605, 332, 104, -76, -208, -290, -325, -314, -258, -162, -29, + 136, 329, 546, 779, 1025, 1276, 1529, 1778, 2019, 2247, 2459, 2651, 2822, 2970, 3094, 3195, 3272, 3328, 3364, 3385, 3393, 3392, 3388, 3385, 3389, 3404, 3436, 3491, 3573, 3687, 3837, 4026, + 4258, 4535, 4858, 5228, 5644, 6104, 6607, 7149, 7725, 8331, 8961, 9608, 10266, 10925, 11579, 12218, 12834, 13418, 13963, 14459, 14900, 15278, 15586, 15819, 15971, 16040, 16023, 15917, 15723, 15441, 15073, 14622, + 14092, 13490, 12820, 12091, 11309, 10485, 9627, 8745, 7849, 6949, 6055, 5177, 4324, 3504, 2728, 2001, 1332, 724, 184, -285, -682, -1005, -1254, -1430, -1535, -1574, -1551, -1471, -1340, -1167, -957, -721, + -466, -200, 66, 325, 569, 790, 979, 1132, 1240, 1301, 1308, 1260, 1155, 991, 768, 488, 154, -231, -664, -1140, -1651, -2193, -2757, -3336, -3922, -4508, -5086, -5648, -6186, -6694, -7165, -7593, + -7973, -8300, -8571, -8784, -8937, -9029, -9060, -9032, -8947, -8808, -8619, -8384, -8108, -7797, -7458, -7095, -6717, -6328, -5936, -5546, -5165, -4798, -4449, -4122, -3822, -3550, -3309, -3100, -2923, -2777, -2662, -2574, + -2511, -2469, -2445, -2434, -2431, -2431, -2428, -2418, -2395, -2355, -2293, -2204, -2086, -1936, -1751, -1530, -1272, -979, -652, -292, 96, 511, 945, 1395, 1854, 2314, 2770, 3214, 3637, 4033, 4394, 4713, + 4982, 5196, 5347, 5432, 5446, 5386, 5249, 5034, 4741, 4371, 3926, 3410, 2827, 2183, 1484, 737, -47, -863, -1700, -2548, -3396, -4235, -5053, -5842, -6591, -7291, -7934, -8511, -9016, -9443, -9787, -10044, + -10212, -10289, -10276, -10174, -9985, -9714, -9365, -8943, -8457, -7914, -7321, -6689, -6026, -5342, -4647, -3951, -3264, -2594, -1951, -1343, -777, -260, 201, 604, 943, 1217, 1424, 1562, 1634, 1641, 1585, 1471, + 1303, 1086, 827, 531, 206, -141, -503, -873, -1244, -1609, -1961, -2295, -2605, -2886, -3136, -3351, -3528, -3667, -3767, -3829, -3855, -3845, -3805, -3736, -3643, -3532, -3406, -3273, -3136, -3001, -2875, -2762, + -2667, -2594, -2548, -2532, -2548, -2599, -2684, -2805, -2961, -3151, -3371, -3620, -3892, -4184, -4491, -4807, -5126, -5442, -5748, -6039, -6307, -6547, -6754, -6921, -7045, -7120, -7145, -7116, -7032, -6893, -6698, -6450, + -6150, -5801, -5408, -4977, -4511, -4018, -3505, -2980, -2449, -1921, -1403, -904, -431, 8, 407, 761, 1063, 1308, 1494, 1618, 1678, 1673, 1605, 1475, 1286, 1041, 746, 407, 30, -376, -805, -1249, + -1697, -2141, -2572, -2981, -3359, -3699, -3991, -4229, -4407, -4520, -4562, -4532, -4426, -4244, -3987, -3655, -3251, -2780, -2245, -1654, -1014, -330, 385, 1127, 1883, 2645, 3403, 4147, 4867, 5554, 6198, 6793, + 7330, 7803, 8205, 8534, 8784, 8953, 9040, 9046, 8971, 8817, 8587, 8287, 7921, 7494, 7015, 6490, 5928, 5335, 4722, 4095, 3464, 2837, 2221, 1624, 1053, 513, 10, -451, -867, -1236, -1555, -1824, + -2043, -2212, -2334, -2411, -2446, -2443, -2407, -2342, -2252, -2144, -2022, -1891, -1757, -1623, -1493, -1372, -1263, -1167, -1086, -1021, -972, -939, -920, -912, -914, -921, -929, -933, -930, -913, -877, -817, + -727, -602, -438, -229, 26, 333, 693, 1109, 1581, 2108, 2689, 3323, 4007, 4736, 5505, 6310, 7142, 7996, 8863, 9735, 10603, 11459, 12295, 13100, 13867, 14587, 15253, 15858, 16395, 16858, 17244, 17549, + 17770, 17905, 17956, 17923, 17808, 17614, 17347, 17011, 16614, 16162, 15663, 15127, 14562, 13978, 13384, 12791, 12206, 11641, 11103, 10600, 10141, 9732, 9378, 9084, 8854, 8691, 8594, 8566, 8604, 8706, 8869, 9088, + 9358, 9672, 10023, 10405, 10807, 11222, 11640, 12054, 12453, 12829, 13175, 13481, 13741, 13949, 14099, 14187, 14208, 14160, 14043, 13855, 13597, 13272, 12882, 12431, 11924, 11366, 10764, 10125, 9456, 8765, 8060, 7348, + 6639, 5939, 5256, 4596, 3967, 3374, 2822, 2315, 1855, 1447, 1090, 784, 531, 327, 171, 60, -10, -45, -49, -28, 12, 67, 129, 193, 253, 303, 338, 353, 345, 310, 246, 151, + 23, -136, -328, -550, -800, -1074, -1370, -1683, -2009, -2341, -2675, -3006, -3326, -3632, -3918, -4178, -4408, -4604, -4762, -4881, -4957, -4990, -4979, -4926, -4831, -4698, -4530, -4331, -4105, -3860, -3600, -3333, + -3066, -2807, -2562, -2339, -2146, -1989, -1876, -1812, -1802, -1852, -1965, -2143, -2389, -2702, -3083, -3530, -4039, -4608, -5230, -5900, -6611, -7355, -8124, -8908, -9699, -10485, -11257, -12006, -12720, -13390, -14008, -14564, + -15051, -15461, -15789, -16029, -16178, -16233, -16192, -16056, -15826, -15503, -15092, -14597, -14025, -13382, -12675, -11914, -11107, -10264, -9395, -8510, -7620, -6734, -5862, -5013, -4197, -3421, -2693, -2020, -1406, -856, -373, 38, + 380, 652, 853, 987, 1057, 1066, 1020, 923, 783, 605, 396, 164, -84, -342, -603, -860, -1108, -1340, -1553, -1741, -1903, -2035, -2136, -2205, -2243, -2250, -2229, -2182, -2112, -2024, -1922, -1811, + -1696, -1582, -1476, -1381, -1305, -1250, -1223, -1226, -1264, -1339, -1454, -1608, -1803, -2038, -2312, -2622, -2966, -3340, -3739, -4158, -4592, -5034, -5479, -5919, -6348, -6760, -7148, -7505, -7827, -8108, -8343, -8528, + -8661, -8740, -8763, -8729, -8641, -8498, -8304, -8063, -7778, -7456, -7101, -6721, -6322, -5911, -5497, -5087, -4689, -4311, -3958, -3639, -3360, -3125, -2941, -2810, -2736, -2720, -2763, -2866, -3026, -3241, -3508, -3822, + -4177, -4568, -4986, -5425, -5875, -6329, -6777, -7210, -7620, -7997, -8333, -8621, -8852, -9021, -9122, -9150, -9102, -8976, -8769, -8483, -8118, -7677, -7163, -6581, -5937, -5238, -4491, -3705, -2889, -2053, -1205, -357, + 480, 1299, 2089, 2840, 3544, 4193, 4779, 5296, 5739, 6103, 6386, 6584, 6698, 6728, 6675, 6542, 6332, 6050, 5702, 5293, 4831, 4323, 3778, 3202, 2604, 1993, 1377, 762, 157, -430, -996, -1534, + -2039, -2508, -2937, -3324, -3669, -3971, -4231, -4450, -4631, -4776, -4890, -4976, -5039, -5084, -5116, -5140, -5161, -5183, -5212, -5252, -5305, -5376, -5467, -5580, -5715, -5873, -6053, -6254, -6474, -6710, -6958, -7214, + -7474, -7732, -7984, -8223, -8444, -8641, -8808, -8941, -9035, -9084, -9085, -9036, -8933, -8776, -8563, -8296, -7975, -7602, -7182, -6718, -6215, -5679, -5117, -4535, -3942, -3345, -2753, -2174, -1616, -1089, -600, -156, + 234, 567, 835, 1034, 1160, 1212, 1187, 1086, 908, 657, 335, -52, -500, -1003, -1554, -2143, -2763, -3405, -4058, -4714, -5362, -5992, -6596, -7164, -7688, -8159, -8571, -8918, -9193, -9395, -9518, -9562, + -9527, -9412, -9220, -8954, -8617, -8216, -7756, -7244, -6688, -6097, -5478, -4841, -4195, -3549, -2913, -2294, -1701, -1143, -625, -154, 264, 626, 928, 1168, 1345, 1458, 1510, 1501, 1436, 1318, 1151, 943, + 699, 425, 130, -178, -495, -811, -1119, -1411, -1682, -1924, -2133, -2304, -2432, -2514, -2549, -2534, -2470, -2358, -2198, -1993, -1747, -1463, -1147, -802, -436, -54, 337, 732, 1123, 1506, 1873, 2219, + 2540, 2829, 3085, 3303, 3481, 3617, 3712, 3764, 3776, 3748, 3684, 3587, 3460, 3309, 3137, 2951, 2755, 2556, 2359, 2169, 1992, 1831, 1693, 1580, 1496, 1442, 1421, 1433, 1477, 1554, 1660, 1794, + 1950, 2125, 2314, 2510, 2707, 2899, 3079, 3239, 3372, 3471, 3529, 3540, 3497, 3396, 3232, 3000, 2699, 2326, 1881, 1365, 778, 124, -592, -1368, -2197, -3071, -3983, -4924, -5884, -6855, -7825, -8785, + -9724, -10632, -11498, -12313, -13067, -13752, -14360, -14885, -15320, -15662, -15906, -16050, -16093, -16037, -15881, -15630, -15287, -14857, -14347, -13764, -13115, -12410, -11658, -10868, -10051, -9215, -8373, -7532, -6703, -5894, -5114, -4371, + -3671, -3020, -2423, -1883, -1404, -987, -632, -339, -105, 70, 192, 264, 293, 283, 240, 172, 85, -13, -117, -219, -314, -395, -456, -494, -503, -481, -424, -333, -204, -40, 158, 391, + 654, 944, 1257, 1589, 1934, 2287, 2643, 2994, 3337, 3665, 3972, 4254, 4506, 4725, 4906, 5047, 5147, 5205, 5219, 5191, 5123, 5017, 4876, 4704, 4505, 4285, 4049, 3803, 3554, 3307, 3070, 2849, + 2648, 2476, 2335, 2232, 2170, 2152, 2180, 2257, 2382, 2556, 2776, 3040, 3346, 3689, 4064, 4466, 4888, 5323, 5764, 6204, 6635, 7049, 7439, 7797, 8116, 8391, 8615, 8783, 8892, 8938, 8918, 8833, + 8681, 8465, 8185, 7846, 7452, 7009, 6522, 5998, 5446, 4874, 4290, 3704, 3124, 2561, 2024, 1520, 1059, 648, 295, 6, -214, -361, -432, -424, -337, -171, 70, 387, 775, 1228, 1739, 2303, + 2911, 3554, 4224, 4911, 5606, 6297, 6977, 7634, 8260, 8846, 9384, 9866, 10286, 10638, 10918, 11122, 11249, 11296, 11264, 11154, 10968, 10710, 10384, 9995, 9550, 9054, 8515, 7942, 7341, 6722, 6092, 5459, + 4832, 4218, 3623, 3055, 2518, 2019, 1560, 1146, 778, 457, 185, -38, -216, -350, -442, -497, -518, -510, -478, -428, -363, -291, -215, -141, -74, -17, 24, 49, 55, 40, 3, -56, + -136, -236, -353, -485, -627, -776, -927, -1075, -1214, -1340, -1447, -1529, -1581, -1599, -1578, -1514, -1404, -1247, -1039, -781, -473, -115, 288, 737, 1225, 1749, 2302, 2878, 3471, 4071, 4672, 5265, + 5842, 6393, 6911, 7388, 7815, 8185, 8492, 8730, 8893, 8979, 8983, 8905, 8743, 8498, 8171, 7766, 7287, 6738, 6127, 5458, 4742, 3986, 3199, 2391, 1572, 752, -58, -851, -1616, -2343, -3025, -3653, + -4222, -4725, -5157, -5514, -5795, -5997, -6121, -6168, -6140, -6040, -5873, -5645, -5360, -5028, -4655, -4249, -3820, -3375, -2924, -2475, -2037, -1618, -1226, -867, -548, -274, -49, 122, 239, 299, 303, 252, + 147, -8, -210, -454, -734, -1044, -1377, -1725, -2082, -2438, -2788, -3122, -3434, -3716, -3963, -4167, -4325, -4431, -4481, -4474, -4408, -4281, -4095, -3849, -3547, -3192, -2786, -2335, -1844, -1318, -763, -186, + 406, 1008, 1613, 2215, 2807, 3384, 3942, 4475, 4980, 5455, 5896, 6303, 6675, 7013, 7318, 7591, 7835, 8054, 8252, 8432, 8600, 8761, 8919, 9080, 9250, 9432, 9632, 9854, 10100, 10375, 10680, 11017, + 11387, 11788, 12221, 12682, 13169, 13678, 14204, 14743, 15287, 15831, 16367, 16889, 17388, 17856, 18287, 18673, 19007, 19282, 19493, 19634, 19700, 19689, 19597, 19424, 19168, 18832, 18416, 17925, 17361, 16732, 16042, 15300, + 14513, 13691, 12843, 11978, 11107, 10240, 9388, 8560, 7767, 7018, 6322, 5686, 5119, 4625, 4211, 3881, 3636, 3479, 3410, 3428, 3531, 3714, 3975, 4306, 4702, 5154, 5655, 6196, 6768, 7360, 7964, 8569, + 9166, 9745, 10298, 10817, 11293, 11720, 12093, 12406, 12655, 12838, 12954, 13001, 12980, 12893, 12742, 12532, 12266, 11950, 11590, 11192, 10763, 10311, 9842, 9364, 8884, 8409, 7946, 7500, 7077, 6682, 6319, 5991, + 5701, 5449, 5236, 5063, 4927, 4828, 4761, 4724, 4712, 4721, 4746, 4782, 4822, 4862, 4897, 4920, 4927, 4913, 4875, 4809, 4712, 4582, 4418, 4219, 3987, 3722, 3427, 3105, 2758, 2392, 2012, 1622 +}; +const int multitone_length = 32000; \ No newline at end of file diff --git a/src/bluetooth/gatt_services/CMakeLists.txt b/src/bluetooth/gatt_services/CMakeLists.txt index 141acb9e..c85c1f0b 100644 --- a/src/bluetooth/gatt_services/CMakeLists.txt +++ b/src/bluetooth/gatt_services/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/audio_config_service.c ${CMAKE_CURRENT_SOURCE_DIR}/power_saving_service.c ${CMAKE_CURRENT_SOURCE_DIR}/led_service.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/seal_check_service.c ) diff --git a/src/bluetooth/gatt_services/led_service.cpp b/src/bluetooth/gatt_services/led_service.cpp index 26de1418..547d53e2 100644 --- a/src/bluetooth/gatt_services/led_service.cpp +++ b/src/bluetooth/gatt_services/led_service.cpp @@ -30,9 +30,6 @@ static ssize_t write_state(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { - //printk("Attribute write, handle: %u, conn: %p", attr->handle, - //(void *)conn); - if (len != 1U) { LOG_INF("Write led: Incorrect data length"); return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); diff --git a/src/bluetooth/gatt_services/seal_check_service.c b/src/bluetooth/gatt_services/seal_check_service.c new file mode 100644 index 00000000..cda70a16 --- /dev/null +++ b/src/bluetooth/gatt_services/seal_check_service.c @@ -0,0 +1,432 @@ +#include "seal_check_service.h" +#include +#include +#include +#include +#include +#include "arm_math.h" + +#include "../../SensorManager/SensorManager.h" +#include "audio_datapath.h" +#include "decimation_filter.h" + +#include "multitone.h" + +LOG_MODULE_REGISTER(seal_check_service, CONFIG_LOG_DEFAULT_LEVEL); + +#define NUM_SEAL_CHECK_SAMPLES 2048 +#define INITIAL_SEAL_CHECK_DROP 128 + +int16_t seal_check_mic[NUM_SEAL_CHECK_SAMPLES]; +//int seal_check_mic_index = 0; + +static q15_t fft_output[NUM_SEAL_CHECK_SAMPLES * 2]; // Complex output needs double size +static q15_t magnitude[NUM_SEAL_CHECK_SAMPLES / 2]; // Magnitude spectrum + +#define num_bins 9 +const int bin_tolerance = 2; +static float avg_magnitude = 119.0f; +static float avg_slope = -0.07382279460490486; +static float target_frequencies[] = {40.0, 60.0, 90.0, 135.0, 202.5, 303.75, 455.625, 683.4375, 1025.15625}; +static float target_magnitudes[] = {0.90833731, 1.18334124, 1.38796968, 1.16634027, 0.85781358, 0.65981396, 0.84768657, 0.98236069, 1.00633671}; + +// Service state +static uint8_t seal_check_start_value = 0x00; +static struct seal_check_data seal_check_result_data; +static bool ccc_enabled = false; + +// Function prototypes +//extern int audio_datapath_multitone_play(uint16_t dur_ms, float amplitude); +extern int hw_codec_volume_set(uint8_t volume); + +extern struct data_fifo fifo_rx; + +// Work item for seal check completion +static struct k_work seal_check_complete_work; + +void seal_check_callback(); + +// Callback for start characteristic write +static ssize_t write_seal_check_start(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset, uint8_t flags) +{ + if (offset + len > sizeof(seal_check_start_value)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if (len != 1) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + uint8_t value = *((uint8_t*)buf); + + if (value == 0xFF) { + LOG_INF("Seal check started via BLE"); + seal_check_start_value = 0xFF; + + int ret; + if (!fifo_rx.initialized) { + ret = data_fifo_init(&fifo_rx); + if (ret) { + LOG_ERR("Failed to set up rx FIFO: %d", ret); + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + } + + // Set volume and start multitone + hw_codec_volume_set(0xB0); + + audio_datapath_decimator_init(12); // 12 = 4kHz + audio_datapath_aquire(&fifo_rx); + + // Start multitone playbook (1.0 amplitude) + ret = audio_datapath_buffer_play((int16_t*)multitone, multitone_length, false, 1.0f, NULL); + + record_to_buffer(seal_check_mic, NUM_SEAL_CHECK_SAMPLES, INITIAL_SEAL_CHECK_DROP, false, true, seal_check_callback); + + if (ret != 0) { + LOG_ERR("Failed to start seal check: %d", ret); + seal_check_start_value = 0x00; + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + + LOG_INF("Seal check started successfully"); + } + + return len; +} + +// Callback for start characteristic read +static ssize_t read_seal_check_start(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + return bt_gatt_attr_read(conn, attr, buf, len, offset, + &seal_check_start_value, sizeof(seal_check_start_value)); +} + +// Callback for result characteristic CCC write +static void seal_check_result_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + ccc_enabled = (value == BT_GATT_CCC_NOTIFY); + LOG_INF("Seal check result notifications %s", ccc_enabled ? "enabled" : "disabled"); +} + +// GATT service definition +BT_GATT_SERVICE_DEFINE(seal_check_svc, + BT_GATT_PRIMARY_SERVICE(BT_UUID_SEAL_CHECK_SERVICE), + + // Start Test Characteristic + BT_GATT_CHARACTERISTIC(BT_UUID_SEAL_CHECK_START, + BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, + read_seal_check_start, write_seal_check_start, + &seal_check_start_value), + + // Result Data Characteristic + BT_GATT_CHARACTERISTIC(BT_UUID_SEAL_CHECK_RESULT, + BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_NONE, + NULL, NULL, &seal_check_result_data), + BT_GATT_CCC(seal_check_result_ccc_cfg_changed, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), +); + +// Work handler for seal check completion +static void seal_check_complete_work_handler(struct k_work *work) +{ + on_seal_check_complete(); +} + +void on_seal_check_complete() { + audio_datapath_buffer_stop(); + + audio_datapath_release(); + //audio_datapath_decimator_cleanup(); + + compute_seal_check_result(); +} + +void compute_seal_check_result() +{ + // Compute RFFT Q15 for seal check analysis + static arm_rfft_instance_q15 rfft_instance; + static bool rfft_initialized = false; + + if (!rfft_initialized) { + arm_status status = arm_rfft_init_q15(&rfft_instance, NUM_SEAL_CHECK_SAMPLES, 0, 1); + if (status == ARM_MATH_SUCCESS) { + rfft_initialized = true; + LOG_INF("RFFT Q15 initialized for %d samples", NUM_SEAL_CHECK_SAMPLES); + } else { + LOG_ERR("RFFT Q15 initialization failed with status %d", status); + return; + } + } + + // Perform RFFT + arm_rfft_q15(&rfft_instance, seal_check_mic, fft_output); + + // Calculate magnitude spectrum + arm_cmplx_mag_q15(fft_output, magnitude, NUM_SEAL_CHECK_SAMPLES / 2); + + LOG_INF("Seal check RFFT completed, %d frequency bins calculated", NUM_SEAL_CHECK_SAMPLES / 2); + + // Calculate mean magnitude of the spectrum + float spectrum_sum = 0.0f; + int valid_bins = 0; + for (int bin = 1; bin < NUM_SEAL_CHECK_SAMPLES / 2; bin++) { + spectrum_sum += (float)magnitude[bin]; + valid_bins++; + } + float mean_magnitude = spectrum_sum / valid_bins; + float peak_threshold = 4.0f * mean_magnitude; + + // Analyze center frequencies with magnitude weighting + printk("Center frequency analysis (sampling rate: 4000 Hz, mean_mag: %.1f, threshold: %.1f):\n", + (double)mean_magnitude, (double)peak_threshold); + + // Arrays for linear regression + float valid_frequencies[num_bins]; + float valid_amplitudes[num_bins]; + int valid_peak_count = 0; + + for (int center_idx = 0; center_idx < num_bins; center_idx++) { + float center_freq = target_frequencies[center_idx]; + int center_bin = (int)(center_freq * NUM_SEAL_CHECK_SAMPLES / 4000.0f + 0.5f); + + // Define search range + int start_bin = MAX(1, center_bin - bin_tolerance); + int end_bin = MIN(NUM_SEAL_CHECK_SAMPLES / 2 - 1, center_bin + bin_tolerance); + + // Calculate weighted center frequency and total magnitude + float weighted_freq_sum = 0.0f; + float total_magnitude = 0.0f; + q15_t peak_magnitude = 0; + int peak_bin = center_bin; + + for (int bin = start_bin; bin <= end_bin; bin++) { + float bin_freq = (float)bin * 4000.0f / NUM_SEAL_CHECK_SAMPLES; + float magnitude_weight = (float)magnitude[bin]; + + weighted_freq_sum += bin_freq * magnitude_weight; + total_magnitude += magnitude_weight; + + // Track peak for amplitude calculation + if (magnitude[bin] > peak_magnitude) { + peak_magnitude = magnitude[bin]; + peak_bin = bin; + } + } + + // Calculate weighted center frequency + float actual_center_freq = 0.0f; + bool valid_peak = false; + + if (total_magnitude > 0) { + actual_center_freq = weighted_freq_sum / total_magnitude; + } else { + actual_center_freq = center_freq; // fallback to expected center + } + + // Check if peak is valid (higher than threshold) + if (peak_magnitude > peak_threshold) { + valid_peak = true; + } + + // Interpolate peak amplitude for better accuracy (only for valid peaks) + float interpolated_amplitude = (float)peak_magnitude; + //if (valid_peak && peak_bin > 0 && peak_bin < NUM_SEAL_CHECK_SAMPLES / 2 - 1) { + if (peak_bin > 0 && peak_bin < NUM_SEAL_CHECK_SAMPLES / 2 - 1) { + // Parabolic interpolation for peak refinement + float y1 = (float)magnitude[peak_bin - 1]; + float y2 = (float)magnitude[peak_bin]; + float y3 = (float)magnitude[peak_bin + 1]; + + float a = (y1 - 2*y2 + y3) / 2; + float b = (y3 - y1) / 2; + + if (a != 0) { + float peak_offset = -b / (2*a); + // Limit offset to reasonable range + if (peak_offset > -1.0f && peak_offset < 1.0f) { + interpolated_amplitude = y2 - (b*b)/(4*a); + actual_center_freq += peak_offset * (4000.0f / NUM_SEAL_CHECK_SAMPLES); + } + } + } + + // Store valid peaks for linear regression + if (valid_peak) { + valid_frequencies[valid_peak_count] = actual_center_freq; + valid_amplitudes[valid_peak_count] = interpolated_amplitude; + valid_peak_count++; + } + + printk("Bin %d: Expected %.2f Hz, Found %.2f Hz, Amplitude: %.1f (raw: %d, total_mag: %.1f) %s\n", + center_idx, + (double)center_freq, + (double)actual_center_freq, + (double)interpolated_amplitude, + peak_magnitude, + (double)total_magnitude, + valid_peak ? "VALID" : "WEAK"); + } + + // Perform linear regression on valid peaks (magnitude vs log(frequency)) + if (valid_peak_count >= 2) { + // Calculate log frequencies for regression + float log_frequencies[num_bins]; + for (int i = 0; i < valid_peak_count; i++) { + log_frequencies[i] = logf(valid_frequencies[i]); + } + + // Calculate means + float mean_log_freq = 0.0f; + float mean_amp = 0.0f; + for (int i = 0; i < valid_peak_count; i++) { + mean_log_freq += log_frequencies[i]; + mean_amp += valid_amplitudes[i]; + } + mean_log_freq /= valid_peak_count; + mean_amp /= valid_peak_count; + + // Calculate slope (linear regression: magnitude vs log(frequency)) + float numerator = 0.0f; + float denominator = 0.0f; + for (int i = 0; i < valid_peak_count; i++) { + float log_freq_diff = log_frequencies[i] - mean_log_freq; + float amp_diff = valid_amplitudes[i] - mean_amp; + numerator += log_freq_diff * amp_diff; + denominator += log_freq_diff * log_freq_diff; + } + + float slope = 0.0f; + if (denominator != 0.0f) { + slope = numerator / denominator; + } + + // Calculate correlation coefficient for quality assessment + /*float sum_sq_log_freq = 0.0f; + float sum_sq_amp = 0.0f; + for (int i = 0; i < valid_peak_count; i++) { + float log_freq_diff = log_frequencies[i] - mean_log_freq; + float amp_diff = valid_amplitudes[i] - mean_amp; + sum_sq_log_freq += log_freq_diff * log_freq_diff; + sum_sq_amp += amp_diff * amp_diff; + } + + float correlation = 0.0f; + if (sum_sq_log_freq > 0.0f && sum_sq_amp > 0.0f) { + correlation = numerator / (sqrtf(sum_sq_log_freq) * sqrtf(sum_sq_amp)); + }*/ + + float avg_peak_mag = 0.0f; + for (int i = 0; i < valid_peak_count; i++) { + avg_peak_mag += valid_amplitudes[i]; + } + avg_peak_mag /= valid_peak_count; + + float mse = 0.0f; + for (int i = 0; i < valid_peak_count; i++) { + float freq_error = valid_amplitudes[i] / avg_peak_mag - target_magnitudes[i]; + mse += freq_error * freq_error; + } + mse /= valid_peak_count; + + float seal_quality = fminf(avg_peak_mag / avg_magnitude, 1.f) - mse - (slope / avg_magnitude - avg_slope); + seal_quality = fmaxf(0.0f, fminf(100.0f, seal_quality * 100.f)); // Clamp between 0 and 100 + + printk("Linear Regression Results (magnitude vs log(frequency)):\n"); + printk("Valid peaks: %d, Slope: %.3f\n", //, Correlation: %.3f\n", + valid_peak_count, (double)slope / avg_magnitude); //, (double)correlation); + printk("Seal Quality: %.3f\n", (double)seal_quality); + + // Prepare and send seal check data via GATT service + struct seal_check_data gatt_data; + gatt_data.version = 1; + gatt_data.quality = (uint8_t)(seal_quality); // Scale to 0-255 + gatt_data.mean_magnitude = (uint8_t)(mean_magnitude * 8.0f > 255.0f ? 255 : (uint8_t)(mean_magnitude * 8.0f)); + gatt_data.num_peaks = valid_peak_count; + + // Fill frequency and magnitude arrays + for (int i = 0; i < 9; i++) { + if (i < valid_peak_count) { + // Convert to 12.4 fixed point (multiply by 16) + gatt_data.frequencies[i] = (uint16_t)(valid_frequencies[i] * 16.0f); + gatt_data.magnitudes[i] = (uint16_t)(valid_amplitudes[i] > 65535.0f ? 65535 : (uint16_t)valid_amplitudes[i]); + } else { + gatt_data.frequencies[i] = 0; + gatt_data.magnitudes[i] = 0; + } + } + + // Send via GATT service + seal_check_notify_result(&gatt_data); + } else { + printk("Not enough valid peaks (%d) for linear regression\n", valid_peak_count); + + // Send minimal data even if regression failed + struct seal_check_data gatt_data; + gatt_data.version = 1; + gatt_data.quality = 0; // No quality measurement possible + gatt_data.mean_magnitude = (uint8_t)(mean_magnitude * 8.0f > 255.0f ? 255 : (uint8_t)(mean_magnitude * 8.0f)); + gatt_data.num_peaks = valid_peak_count; + + // Fill available data + for (int i = 0; i < 9; i++) { + if (i < valid_peak_count) { + gatt_data.frequencies[i] = (uint16_t)(valid_frequencies[i] * 16.0f); + gatt_data.magnitudes[i] = (uint16_t)(valid_amplitudes[i] > 65535.0f ? 65535 : (uint16_t)valid_amplitudes[i]); + } else { + gatt_data.frequencies[i] = 0; + gatt_data.magnitudes[i] = 0; + } + } + + seal_check_notify_result(&gatt_data); + } +} + +// Function to notify result data +int seal_check_notify_result(const struct seal_check_data *data) +{ + if (!ccc_enabled) { + LOG_WRN("Seal check result notifications not enabled"); + return -ENOENT; + } + + // Copy data to local storage + memcpy(&seal_check_result_data, data, sizeof(seal_check_result_data)); + + // Reset start value to indicate test completion + seal_check_start_value = 0x00; + + // Send notification + int err = bt_gatt_notify(NULL, &seal_check_svc.attrs[4], + &seal_check_result_data, sizeof(seal_check_result_data)); + + if (err) { + LOG_ERR("Failed to notify seal check result: %d", err); + return err; + } + + LOG_INF("Seal check result notified successfully"); + return 0; +} + +void seal_check_callback() { + k_work_submit(&seal_check_complete_work); +} + +// Service initialization +int init_seal_check_service(void) +{ + // Initialize work item + k_work_init(&seal_check_complete_work, seal_check_complete_work_handler); + + LOG_INF("Seal check service initialized"); + return 0; +} \ No newline at end of file diff --git a/src/bluetooth/gatt_services/seal_check_service.h b/src/bluetooth/gatt_services/seal_check_service.h new file mode 100644 index 00000000..56396f3b --- /dev/null +++ b/src/bluetooth/gatt_services/seal_check_service.h @@ -0,0 +1,42 @@ +#ifndef _SEAL_CHECK_SERVICE_H_ +#define _SEAL_CHECK_SERVICE_H_ + +#include +#include +#include +#include "openearable_common.h" + +// Service UUID +#define BT_UUID_SEAL_CHECK_SERVICE_VAL \ + BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x9abc, 0xdef123456789) + +#define BT_UUID_SEAL_CHECK_SERVICE \ + BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_SERVICE_VAL) + +// Start Test Characteristic UUID (write 0xFF to start, reads back 0x00 when done) +#define BT_UUID_SEAL_CHECK_START_VAL \ + BT_UUID_128_ENCODE(0x12345679, 0x1234, 0x5678, 0x9abc, 0xdef123456789) + +#define BT_UUID_SEAL_CHECK_START \ + BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_START_VAL) + +// Result Data Characteristic UUID (notify with seal_check_data) +#define BT_UUID_SEAL_CHECK_RESULT_VAL \ + BT_UUID_128_ENCODE(0x1234567A, 0x1234, 0x5678, 0x9abc, 0xdef123456789) + +#define BT_UUID_SEAL_CHECK_RESULT \ + BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_RESULT_VAL) + +#ifdef __cplusplus +extern "C" { +#endif + +// Function declarations +int init_seal_check_service(void); +int seal_check_notify_result(const struct seal_check_data *data); + +#ifdef __cplusplus +} +#endif + +#endif /* _SEAL_CHECK_SERVICE_H_ */ \ No newline at end of file diff --git a/unicast_server/main.cpp b/unicast_server/main.cpp index 706cab4e..52ceb60d 100644 --- a/unicast_server/main.cpp +++ b/unicast_server/main.cpp @@ -28,6 +28,7 @@ #include "button_service.h" #include "sensor_service.h" #include "led_service.h" +#include "seal_check_service.h" #include "SensorScheme.h" #include "DefaultSensors.h" @@ -110,6 +111,9 @@ int main(void) { ret = init_sensor_service(); ERR_CHK(ret); + ret = init_seal_check_service(); + ERR_CHK(ret); + bt_mgmt_conn_interval_init(new ConnIntvlLinear( 4, // linear increase step (8ms units) CONFIG_BLE_ACL_CONN_INTERVAL, From def9c15e80187dd543e47ff08671375343702ce4 Mon Sep 17 00:00:00 2001 From: mkuettner97 Date: Wed, 1 Apr 2026 16:52:22 +0200 Subject: [PATCH 20/77] fix unmuting problem for seal check --- src/bluetooth/gatt_services/seal_check_service.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bluetooth/gatt_services/seal_check_service.c b/src/bluetooth/gatt_services/seal_check_service.c index cda70a16..e9feb7c0 100644 --- a/src/bluetooth/gatt_services/seal_check_service.c +++ b/src/bluetooth/gatt_services/seal_check_service.c @@ -75,6 +75,8 @@ static ssize_t write_seal_check_start(struct bt_conn *conn, } } + hw_codec_volume_unmute(); + // Set volume and start multitone hw_codec_volume_set(0xB0); From d7200ac1d5993024d94bdc01887702ede4afe336 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:00:23 +0200 Subject: [PATCH 21/77] feat: integrate openearable-protocol and update CMakeLists for protocol generation --- CMakeLists.txt | 7 +++++++ west.yml | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffa4b8fa..ccab9132 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ zephyr_library_include_directories( src/SD_Card/Benchmark src/SD_Card/SD_Card_Manager src/time_sync + ../protocol/generated/c/include ) zephyr_library_include_directories(app PRIVATE @@ -76,6 +77,10 @@ add_subdirectory(src/SD_Card/SDLogger) add_subdirectory(src/SD_Card/Benchmark) add_subdirectory(src/SD_Card/SD_Card_Manager) add_subdirectory(src/time_sync) +add_subdirectory( + ${CMAKE_CURRENT_SOURCE_DIR}/../external/protocol/generated/c + ${CMAKE_CURRENT_BINARY_DIR}/protocol_generated_c +) ## Cirrus Logic if (CONFIG_HW_CODEC_CIRRUS_LOGIC) @@ -88,3 +93,5 @@ endif() include(version.cmake) include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) + +target_link_libraries(app PRIVATE open_earable_protocols) diff --git a/west.yml b/west.yml index aa70c439..f3cbabe1 100644 --- a/west.yml +++ b/west.yml @@ -7,3 +7,9 @@ manifest: url: https://github.com/nrfconnect/sdk-nrf.git revision: v3.0.1 import: true + + - name: openearable-protocol + url: https://github.com/OpenEarable/protocol.git + revision: main + path: external/protocol + # import: true \ No newline at end of file From 7be1d7a6aa68cf681a0d567dfaf2eff19e461e0c Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Wed, 10 Jun 2026 16:13:46 +0200 Subject: [PATCH 22/77] feat: add openearable-protocol as a submodule and update CMakeLists for integration --- .gitmodules | 3 +++ CMakeLists.txt | 6 +----- protocol | 1 + west.yml | 6 ------ 4 files changed, 5 insertions(+), 11 deletions(-) create mode 100644 .gitmodules create mode 160000 protocol diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..5697f77b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "protocol"] + path = protocol + url = https://github.com/OpenEarable/protocol.git diff --git a/CMakeLists.txt b/CMakeLists.txt index ccab9132..747b8ca3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,6 @@ zephyr_library_include_directories( src/SD_Card/Benchmark src/SD_Card/SD_Card_Manager src/time_sync - ../protocol/generated/c/include ) zephyr_library_include_directories(app PRIVATE @@ -77,10 +76,7 @@ add_subdirectory(src/SD_Card/SDLogger) add_subdirectory(src/SD_Card/Benchmark) add_subdirectory(src/SD_Card/SD_Card_Manager) add_subdirectory(src/time_sync) -add_subdirectory( - ${CMAKE_CURRENT_SOURCE_DIR}/../external/protocol/generated/c - ${CMAKE_CURRENT_BINARY_DIR}/protocol_generated_c -) +add_subdirectory(protocol/generated/c) ## Cirrus Logic if (CONFIG_HW_CODEC_CIRRUS_LOGIC) diff --git a/protocol b/protocol new file mode 160000 index 00000000..c9846577 --- /dev/null +++ b/protocol @@ -0,0 +1 @@ +Subproject commit c9846577275af71613ee4d96f8bb1bd6c57af0f0 diff --git a/west.yml b/west.yml index f3cbabe1..aa70c439 100644 --- a/west.yml +++ b/west.yml @@ -7,9 +7,3 @@ manifest: url: https://github.com/nrfconnect/sdk-nrf.git revision: v3.0.1 import: true - - - name: openearable-protocol - url: https://github.com/OpenEarable/protocol.git - revision: main - path: external/protocol - # import: true \ No newline at end of file From cbae2fc3798ec386b08078d3cdbc310163369f4e Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:11:51 +0200 Subject: [PATCH 23/77] feat(audio-response): Replace Seal Check Service with Audio Response Service - Removed the seal_check_service.c and seal_check_service.h files. - Added audio_response_service.h to define the audio response service interface. - Updated main.cpp to initialize the audio response service instead of the seal check service. --- CMakeLists.txt | 2 + include/openearable_common.h | 9 - prj.conf | 1 + prj_fota.conf | 4 +- protocol | 2 +- src/audio/audio_datapath.c | 4 - src/bluetooth/Kconfig | 8 + src/bluetooth/gatt_services/CMakeLists.txt | 2 +- .../gatt_services/audio_response_service.c | 692 ++++++++++++++++++ .../gatt_services/audio_response_service.h | 24 + .../gatt_services/seal_check_service.c | 434 ----------- .../gatt_services/seal_check_service.h | 42 -- unicast_server/main.cpp | 4 +- 13 files changed, 734 insertions(+), 494 deletions(-) create mode 100644 src/bluetooth/gatt_services/audio_response_service.c create mode 100644 src/bluetooth/gatt_services/audio_response_service.h delete mode 100644 src/bluetooth/gatt_services/seal_check_service.c delete mode 100644 src/bluetooth/gatt_services/seal_check_service.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 747b8ca3..2d73b3c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,8 @@ add_subdirectory(src/SD_Card/SD_Card_Manager) add_subdirectory(src/time_sync) add_subdirectory(protocol/generated/c) +target_link_libraries(open_earable_protocols PRIVATE zephyr_interface) + ## Cirrus Logic if (CONFIG_HW_CODEC_CIRRUS_LOGIC) if (ZEPHYR_CIRRUS_LOGIC_MODULE_DIR) diff --git a/include/openearable_common.h b/include/openearable_common.h index 2b567497..40a1c38c 100644 --- a/include/openearable_common.h +++ b/include/openearable_common.h @@ -115,15 +115,6 @@ struct sensor_config { uint8_t storageOptions; } __attribute__((packed)); -struct seal_check_data { - uint8_t version; - uint8_t quality; - uint8_t mean_magnitude; - uint8_t num_peaks; - uint16_t frequencies[9]; // 12.4 fixed point - uint16_t magnitudes[9]; -} __attribute__((packed)); - struct battery_settings { float u_nominal; float u_term; diff --git a/prj.conf b/prj.conf index c48d47f5..f2099611 100644 --- a/prj.conf +++ b/prj.conf @@ -206,6 +206,7 @@ CONFIG_CMSIS_DSP_TRANSFORM=y CONFIG_CMSIS_DSP_COMPLEXMATH=y CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 +CONFIG_AUDIO_RESPONSE_MAX_SAMPLES=32000 #CONFIG_USB_MASS_STORAGE=y #CONFIG_MASS_STORAGE_STACK_SIZE=1024 diff --git a/prj_fota.conf b/prj_fota.conf index e129b55b..564a6ae7 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -264,4 +264,6 @@ CONFIG_SETUP_FUEL_GAUGE=y CONFIG_CMSIS_DSP=y CONFIG_CMSIS_DSP_TRANSFORM=y -CONFIG_CMSIS_DSP_COMPLEXMATH=y \ No newline at end of file +CONFIG_CMSIS_DSP_COMPLEXMATH=y + +CONFIG_AUDIO_RESPONSE_MAX_SAMPLES=32000 diff --git a/protocol b/protocol index c9846577..9f4dc917 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit c9846577275af71613ee4d96f8bb1bd6c57af0f0 +Subproject commit 9f4dc917cf0664d9ab1c2c5dedb2b21df7f121a2 diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 9cae119c..c50d44ab 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -38,7 +38,6 @@ #include "../SensorManager/SensorManager.h" #include "openearable_common.h" #include "SensorScheme.h" -#include "../bluetooth/gatt_services/seal_check_service.h" #include LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); @@ -104,9 +103,6 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); /* How often to print under-run warning */ #define UNDERRUN_LOG_INTERVAL_BLKS 5000 -extern int seal_check_mic_index; -extern int16_t seal_check_mic[]; - enum drift_comp_state { DRIFT_STATE_INIT, /* Waiting for data to be received */ DRIFT_STATE_CALIB, /* Calibrate and zero out local delay */ diff --git a/src/bluetooth/Kconfig b/src/bluetooth/Kconfig index 2bb2d8d2..00f0f3da 100644 --- a/src/bluetooth/Kconfig +++ b/src/bluetooth/Kconfig @@ -8,6 +8,14 @@ rsource "Kconfig.defaults" menu "Bluetooth" +config AUDIO_RESPONSE_MAX_SAMPLES + int "Maximum uploaded audio response samples" + range 1 48000 + default 32000 + help + Maximum number of signed 16-bit PCM samples retained by the audio + response service. Storage for this buffer is reserved at build time. + rsource "bt_management/Kconfig" #----------------------------------------------------------------------------# diff --git a/src/bluetooth/gatt_services/CMakeLists.txt b/src/bluetooth/gatt_services/CMakeLists.txt index c85c1f0b..d74ba9b3 100644 --- a/src/bluetooth/gatt_services/CMakeLists.txt +++ b/src/bluetooth/gatt_services/CMakeLists.txt @@ -6,5 +6,5 @@ target_sources(app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/audio_config_service.c ${CMAKE_CURRENT_SOURCE_DIR}/power_saving_service.c ${CMAKE_CURRENT_SOURCE_DIR}/led_service.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/seal_check_service.c + ${CMAKE_CURRENT_SOURCE_DIR}/audio_response_service.c ) diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c new file mode 100644 index 00000000..e7bc27e0 --- /dev/null +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -0,0 +1,692 @@ +#include "audio_response_service.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "arm_math.h" +#include "audio_datapath.h" +#include "hw_codec.h" + +LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); + +#define AUDIO_RESPONSE_CAPTURE_SAMPLES 2048 +#define AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE 4000U +#define AUDIO_RESPONSE_INITIAL_DROP 128 +#define AUDIO_RESPONSE_RESULT_POINTS 9 +#define AUDIO_RESPONSE_TRANSFER_CREDITS 1 +#define AUDIO_RESPONSE_TRANSFER_TIMEOUT K_SECONDS(30) +#define AUDIO_RESPONSE_STATUS_ENCODED_SIZE 9 +#define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (AUDIO_RESPONSE_RESULT_POINTS * 4)) + +enum audio_response_transfer_status { + AUDIO_RESPONSE_TRANSFER_READY = 0, + AUDIO_RESPONSE_TRANSFER_COMMITTED = 1, + AUDIO_RESPONSE_TRANSFER_ABORTED = 2, + AUDIO_RESPONSE_TRANSFER_INVALID_STATE = 3, + AUDIO_RESPONSE_TRANSFER_INVALID_CHUNK = 4, + AUDIO_RESPONSE_TRANSFER_INSUFFICIENT_STORAGE = 5, + AUDIO_RESPONSE_TRANSFER_CHECKSUM_MISMATCH = 6, + AUDIO_RESPONSE_TRANSFER_TIMED_OUT = 7, +}; + +struct audio_response_transfer { + uint16_t id; + uint32_t total_samples; + uint32_t sampling_rate; + uint32_t expected_checksum; + uint32_t received_samples; + int16_t *samples; + bool active; + bool committed; +}; + +/** + * Carries the GATT write metadata and result through the generated union dispatcher. + */ +struct transfer_control_dispatch_context { + uint16_t write_len; + ssize_t result; +}; + +static const uint16_t target_frequencies[AUDIO_RESPONSE_RESULT_POINTS] = { + 40, 60, 90, 135, 203, 304, 456, 683, 1025, +}; + +static int16_t captured_samples[AUDIO_RESPONSE_CAPTURE_SAMPLES]; +static int16_t uploaded_samples[CONFIG_AUDIO_RESPONSE_MAX_SAMPLES]; +static q15_t fft_output[AUDIO_RESPONSE_CAPTURE_SAMPLES * 2]; +static q15_t magnitudes[AUDIO_RESPONSE_CAPTURE_SAMPLES / 2]; +static uint16_t result_frequencies[AUDIO_RESPONSE_RESULT_POINTS]; +static uint16_t result_response[AUDIO_RESPONSE_RESULT_POINTS]; +static uint8_t result_payload[AUDIO_RESPONSE_RESULT_ENCODED_SIZE]; + +static struct audio_response_transfer transfer; +static audio_response_config_t pending_config; +static bool measurement_active; +static bool transfer_status_notifications_enabled; +static bool result_notifications_enabled; + +static struct k_work measurement_work; +static struct k_work measurement_complete_work; +static struct k_work_delayable transfer_timeout_work; +K_MUTEX_DEFINE(service_mutex); + +extern struct data_fifo fifo_rx; +extern const struct bt_gatt_service_static audio_response_svc; + +static int notify_transfer_status(enum audio_response_transfer_status status, uint16_t credits); +static void reset_transfer(void); + +/** + * Return a readable transfer status name for logs. + */ +static const char *transfer_status_name(enum audio_response_transfer_status status) +{ + switch (status) { + case AUDIO_RESPONSE_TRANSFER_READY: + return "ready"; + case AUDIO_RESPONSE_TRANSFER_COMMITTED: + return "committed"; + case AUDIO_RESPONSE_TRANSFER_ABORTED: + return "aborted"; + case AUDIO_RESPONSE_TRANSFER_INVALID_STATE: + return "invalid_state"; + case AUDIO_RESPONSE_TRANSFER_INVALID_CHUNK: + return "invalid_chunk"; + case AUDIO_RESPONSE_TRANSFER_INSUFFICIENT_STORAGE: + return "insufficient_storage"; + case AUDIO_RESPONSE_TRANSFER_CHECKSUM_MISMATCH: + return "checksum_mismatch"; + case AUDIO_RESPONSE_TRANSFER_TIMED_OUT: + return "timed_out"; + default: + return "unknown"; + } +} + +/** + * Return a readable transfer-control command name for logs. + */ +static const char *transfer_control_type_name(audio_response_transfer_control_type_t type) +{ + switch (type) { + case AUDIO_RESPONSE_TRANSFER_CONTROL_START: + return "start"; + case AUDIO_RESPONSE_TRANSFER_CONTROL_COMMIT: + return "commit"; + case AUDIO_RESPONSE_TRANSFER_CONTROL_ABORT: + return "abort"; + default: + return "unknown"; + } +} + +/** + * Clear all transfer metadata and make the upload buffer reusable. + */ +static void reset_transfer(void) +{ + LOG_DBG("Reset transfer state: id=%u active=%d committed=%d received=%u/%u", transfer.id, + transfer.active, transfer.committed, transfer.received_samples, transfer.total_samples); + memset(&transfer, 0, sizeof(transfer)); +} + +/** + * Notify connected clients about the current upload state. + */ +static int notify_transfer_status(enum audio_response_transfer_status status, uint16_t credits) +{ + audio_response_transfer_status_t message = { + .transfer_id = transfer.id, + .status = status, + .next_sample_offset = transfer.received_samples, + .credits = credits, + }; + uint8_t payload[AUDIO_RESPONSE_STATUS_ENCODED_SIZE]; + size_t payload_size; + + if (!transfer_status_notifications_enabled) { + LOG_DBG("Skipping transfer status notification: status=%s id=%u notifications=disabled", + transfer_status_name(status), transfer.id); + return -EACCES; + } + + if (audio_response_transfer_status_encode(&message, payload, sizeof(payload), &payload_size) != + PROTOCOL_OK) { + LOG_ERR("Failed to encode transfer status: status=%s id=%u offset=%u credits=%u", + transfer_status_name(status), transfer.id, transfer.received_samples, credits); + return -EMSGSIZE; + } + + int ret = bt_gatt_notify(NULL, &audio_response_svc.attrs[6], payload, payload_size); + if (ret != 0) { + LOG_WRN("Failed to notify transfer status: status=%s id=%u err=%d", + transfer_status_name(status), transfer.id, ret); + return ret; + } + + LOG_DBG("Notified transfer status: status=%s id=%u offset=%u credits=%u bytes=%u", + transfer_status_name(status), transfer.id, transfer.received_samples, credits, + payload_size); + return 0; +} + +/** + * Reject a transfer operation and report the supplied protocol status. + */ +static ssize_t reject_transfer(enum audio_response_transfer_status status, ssize_t att_error) +{ + LOG_WRN("Rejecting transfer operation: status=%s id=%u active=%d committed=%d received=%u/%u att_error=%zd", + transfer_status_name(status), transfer.id, transfer.active, transfer.committed, + transfer.received_samples, transfer.total_samples, att_error); + (void)notify_transfer_status(status, 0); + return att_error; +} + +/** + * Start a new audio-buffer upload. + */ +static ssize_t start_transfer(const audio_response_transfer_start_t *start, uint16_t write_len) +{ + LOG_INF("Transfer start requested: id=%u samples=%u rate=%u checksum=0x%08x", + start->transfer_id, start->total_samples, start->sampling_rate, start->checksum); + + if (measurement_active || transfer.active || start->total_samples == 0 || + start->sampling_rate != CONFIG_AUDIO_SAMPLE_RATE_HZ) { + LOG_WRN("Transfer start rejected: measurement_active=%d active=%d samples=%u rate=%u expected_rate=%u", + measurement_active, transfer.active, start->total_samples, start->sampling_rate, + CONFIG_AUDIO_SAMPLE_RATE_HZ); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, + BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); + } + + if (start->total_samples > CONFIG_AUDIO_RESPONSE_MAX_SAMPLES) { + transfer.id = start->transfer_id; + LOG_WRN("Transfer start rejected: samples=%u max_samples=%u", start->total_samples, + CONFIG_AUDIO_RESPONSE_MAX_SAMPLES); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INSUFFICIENT_STORAGE, + BT_GATT_ERR(BT_ATT_ERR_INSUFFICIENT_RESOURCES)); + } + + reset_transfer(); + transfer.id = start->transfer_id; + transfer.total_samples = start->total_samples; + transfer.sampling_rate = start->sampling_rate; + transfer.expected_checksum = start->checksum; + transfer.samples = uploaded_samples; + transfer.active = true; + k_work_reschedule(&transfer_timeout_work, AUDIO_RESPONSE_TRANSFER_TIMEOUT); + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, AUDIO_RESPONSE_TRANSFER_CREDITS); + + LOG_INF("Transfer started: id=%u samples=%u timeout_ms=%lld", transfer.id, + transfer.total_samples, k_ticks_to_ms_floor64(AUDIO_RESPONSE_TRANSFER_TIMEOUT.ticks)); + return write_len; +} + +/** + * Validate and retain a completely uploaded audio buffer. + */ +static ssize_t commit_transfer(const audio_response_transfer_commit_t *commit, uint16_t write_len) +{ + LOG_INF("Transfer commit requested: id=%u received=%u/%u", commit->transfer_id, + transfer.received_samples, transfer.total_samples); + + if (!transfer.active || transfer.committed || commit->transfer_id != transfer.id || + transfer.received_samples != transfer.total_samples) { + LOG_WRN("Transfer commit rejected: requested_id=%u current_id=%u active=%d committed=%d received=%u/%u", + commit->transfer_id, transfer.id, transfer.active, transfer.committed, + transfer.received_samples, transfer.total_samples); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, + BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); + } + + uint32_t checksum = + crc32_ieee((const uint8_t *)transfer.samples, transfer.total_samples * sizeof(int16_t)); + if (checksum != transfer.expected_checksum) { + LOG_WRN("Transfer checksum mismatch: id=%u expected=0x%08x actual=0x%08x", + transfer.id, transfer.expected_checksum, checksum); + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_CHECKSUM_MISMATCH, 0); + reset_transfer(); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + transfer.committed = true; + k_work_cancel_delayable(&transfer_timeout_work); + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_COMMITTED, 0); + LOG_INF("Transfer committed: id=%u samples=%u checksum=0x%08x", transfer.id, + transfer.total_samples, checksum); + return write_len; +} + +/** + * Abort the active or committed upload. + */ +static ssize_t abort_transfer(const audio_response_transfer_abort_t *abort, uint16_t write_len) +{ + LOG_INF("Transfer abort requested: id=%u", abort->transfer_id); + + if ((!transfer.active && !transfer.committed) || abort->transfer_id != transfer.id || + measurement_active) { + LOG_WRN("Transfer abort rejected: requested_id=%u current_id=%u active=%d committed=%d measurement_active=%d", + abort->transfer_id, transfer.id, transfer.active, transfer.committed, + measurement_active); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, + BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); + } + + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_ABORTED, 0); + k_work_cancel_delayable(&transfer_timeout_work); + LOG_INF("Transfer aborted: id=%u received=%u/%u", transfer.id, transfer.received_samples, + transfer.total_samples); + reset_transfer(); + return write_len; +} + +/** + * Handle a typed transfer-start command dispatched by the generated protocol API. + */ +static protocol_status_t dispatch_transfer_start(void *context, + const audio_response_transfer_start_t *start) +{ + struct transfer_control_dispatch_context *dispatch_context = context; + + dispatch_context->result = start_transfer(start, dispatch_context->write_len); + return PROTOCOL_OK; +} + +/** + * Handle a typed transfer-commit command dispatched by the generated protocol API. + */ +static protocol_status_t dispatch_transfer_commit(void *context, + const audio_response_transfer_commit_t *commit) +{ + struct transfer_control_dispatch_context *dispatch_context = context; + + dispatch_context->result = commit_transfer(commit, dispatch_context->write_len); + return PROTOCOL_OK; +} + +/** + * Handle a typed transfer-abort command dispatched by the generated protocol API. + */ +static protocol_status_t dispatch_transfer_abort(void *context, + const audio_response_transfer_abort_t *abort) +{ + struct transfer_control_dispatch_context *dispatch_context = context; + + dispatch_context->result = abort_transfer(abort, dispatch_context->write_len); + return PROTOCOL_OK; +} + +/** + * Decode and execute a transfer-control command. + */ +static ssize_t write_transfer_control(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + static const audio_response_transfer_control_handler_t handlers = { + .start = dispatch_transfer_start, + .commit = dispatch_transfer_commit, + .abort = dispatch_transfer_abort, + }; + audio_response_transfer_control_t control; + struct transfer_control_dispatch_context dispatch_context = { + .write_len = len, + .result = BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED), + }; + size_t bytes_read = 0; + protocol_status_t status; + + if (offset != 0) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + status = audio_response_transfer_control_decode(&control, buf, len, &bytes_read); + if (status != PROTOCOL_OK || bytes_read != len) { + LOG_ERR("Failed to decode transfer control command: status=%d bytes_read=%u len=%u", + status, bytes_read, len); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, + BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN)); + } + + LOG_DBG("Decoded transfer control command: type=%s len=%u", + transfer_control_type_name(control.type), len); + + k_mutex_lock(&service_mutex, K_FOREVER); + status = audio_response_transfer_control_dispatch(&control, &handlers, &dispatch_context); + k_mutex_unlock(&service_mutex); + + if (status != PROTOCOL_OK) { + LOG_ERR("Failed to dispatch transfer control command: type=%s status=%d", + transfer_control_type_name(control.type), status); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, + BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); + } + LOG_DBG("Handled transfer control command: type=%s result=%zd", + transfer_control_type_name(control.type), dispatch_context.result); + return dispatch_context.result; +} + +/** + * Decode and append one contiguous chunk to the active transfer. + */ +static ssize_t write_transfer_data(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + protocol_reader_t reader = { .buffer = buf, .size = len, .offset = 0 }; + uint16_t transfer_id = 0; + uint32_t sample_offset = 0; + uint16_t sample_count = 0; + + if (offset != 0) { + LOG_WRN("Transfer chunk rejected: invalid offset=%u len=%u", offset, len); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + if (protocol_read_uint16(&reader, &transfer_id) != PROTOCOL_OK || + protocol_read_uint32(&reader, &sample_offset) != PROTOCOL_OK || + protocol_read_uint16(&reader, &sample_count) != PROTOCOL_OK || + reader.size - reader.offset != sample_count * sizeof(int16_t)) { + LOG_WRN("Transfer chunk decode failed: len=%u bytes_remaining=%u sample_count=%u", + len, reader.size - reader.offset, sample_count); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_CHUNK, + BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN)); + } + + LOG_DBG("Transfer chunk received: id=%u offset=%u samples=%u len=%u", transfer_id, + sample_offset, sample_count, len); + + k_mutex_lock(&service_mutex, K_FOREVER); + if (!transfer.active || transfer.committed || transfer_id != transfer.id || + sample_offset != transfer.received_samples || sample_count == 0 || + sample_count > transfer.total_samples - transfer.received_samples) { + LOG_WRN("Transfer chunk rejected: requested_id=%u current_id=%u offset=%u expected_offset=%u samples=%u remaining=%u active=%d committed=%d", + transfer_id, transfer.id, sample_offset, transfer.received_samples, + sample_count, transfer.total_samples - transfer.received_samples, transfer.active, + transfer.committed); + k_mutex_unlock(&service_mutex); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_CHUNK, + BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); + } + + for (size_t index = 0; index < sample_count; ++index) { + if (protocol_read_int16(&reader, &transfer.samples[transfer.received_samples + index]) != + PROTOCOL_OK) { + LOG_WRN("Transfer chunk sample decode failed: id=%u offset=%u sample_index=%u", + transfer_id, sample_offset, index); + k_mutex_unlock(&service_mutex); + return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_CHUNK, + BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN)); + } + } + transfer.received_samples += sample_count; + k_work_reschedule(&transfer_timeout_work, AUDIO_RESPONSE_TRANSFER_TIMEOUT); + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, AUDIO_RESPONSE_TRANSFER_CREDITS); + if (transfer.received_samples == transfer.total_samples) { + LOG_INF("Transfer upload complete: id=%u samples=%u", transfer.id, + transfer.total_samples); + } else { + LOG_DBG("Transfer chunk accepted: id=%u received=%u/%u", transfer.id, + transfer.received_samples, transfer.total_samples); + } + k_mutex_unlock(&service_mutex); + return len; +} + +/** + * Decode a measurement request and queue it outside the Bluetooth callback. + */ +static ssize_t write_audio_response_config(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, + uint8_t flags) +{ + audio_response_config_t config; + size_t bytes_read = 0; + + if (offset != 0) { + LOG_WRN("Audio response config rejected: invalid offset=%u len=%u", offset, len); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + if (audio_response_config_decode(&config, buf, len, &bytes_read) != PROTOCOL_OK || + bytes_read != len) { + LOG_WRN("Audio response config decode failed: bytes_read=%u len=%u", bytes_read, len); + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + LOG_INF("Audio response config requested: id=%u transfer_id=%u volume=%.2f", config.id, + config.transfer_id, (double)config.volume); + if (!isfinite(config.volume) || config.volume < 0.0f || config.volume > 1.0f) { + LOG_WRN("Audio response config rejected: invalid volume=%.3f", + (double)config.volume); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + + k_mutex_lock(&service_mutex, K_FOREVER); + if (!transfer.committed || config.transfer_id != transfer.id || measurement_active) { + LOG_WRN("Audio response config rejected: requested_transfer_id=%u current_id=%u committed=%d measurement_active=%d", + config.transfer_id, transfer.id, transfer.committed, measurement_active); + k_mutex_unlock(&service_mutex); + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } + pending_config = config; + measurement_active = true; + k_mutex_unlock(&service_mutex); + + k_work_submit(&measurement_work); + LOG_INF("Audio response measurement queued: id=%u transfer_id=%u", pending_config.id, + pending_config.transfer_id); + return len; +} + +/** + * Track whether transfer-status notifications are enabled. + */ +static void transfer_status_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + transfer_status_notifications_enabled = value == BT_GATT_CCC_NOTIFY; + LOG_DBG("Audio response transfer status notifications %s", + transfer_status_notifications_enabled ? "enabled" : "disabled"); +} + +/** + * Track whether audio-response result notifications are enabled. + */ +static void result_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + result_notifications_enabled = value == BT_GATT_CCC_NOTIFY; + LOG_DBG("Audio response result notifications %s", + result_notifications_enabled ? "enabled" : "disabled"); +} + +BT_GATT_SERVICE_DEFINE(audio_response_svc, + BT_GATT_PRIMARY_SERVICE(AUDIO_RESPONSE_ZEPHYR_SERVICE_UUID), + BT_GATT_CHARACTERISTIC(AUDIO_RESPONSE_ZEPHYR_TRANSFER_CONTROL_CHARACTERISTIC_UUID, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_CONTROL_CHARACTERISTIC_PROPERTIES, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_CONTROL_CHARACTERISTIC_PERMISSIONS, NULL, + write_transfer_control, NULL), + BT_GATT_CHARACTERISTIC(AUDIO_RESPONSE_ZEPHYR_TRANSFER_DATA_CHARACTERISTIC_UUID, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_DATA_CHARACTERISTIC_PROPERTIES, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_DATA_CHARACTERISTIC_PERMISSIONS, NULL, + write_transfer_data, NULL), + BT_GATT_CHARACTERISTIC(AUDIO_RESPONSE_ZEPHYR_TRANSFER_STATUS_CHARACTERISTIC_UUID, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_STATUS_CHARACTERISTIC_PROPERTIES, + AUDIO_RESPONSE_ZEPHYR_TRANSFER_STATUS_CHARACTERISTIC_PERMISSIONS, NULL, + NULL, NULL), + BT_GATT_CCC(transfer_status_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + BT_GATT_CHARACTERISTIC(AUDIO_RESPONSE_ZEPHYR_CONFIG_CHARACTERISTIC_UUID, + AUDIO_RESPONSE_ZEPHYR_CONFIG_CHARACTERISTIC_PROPERTIES, + AUDIO_RESPONSE_ZEPHYR_CONFIG_CHARACTERISTIC_PERMISSIONS, NULL, + write_audio_response_config, NULL), + BT_GATT_CHARACTERISTIC(AUDIO_RESPONSE_ZEPHYR_RESULT_CHARACTERISTIC_UUID, + AUDIO_RESPONSE_ZEPHYR_RESULT_CHARACTERISTIC_PROPERTIES, + AUDIO_RESPONSE_ZEPHYR_RESULT_CHARACTERISTIC_PERMISSIONS, NULL, NULL, + NULL), + BT_GATT_CCC(result_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), +); + +/** + * Run the audio playback and capture setup from the system work queue. + */ +static void measurement_work_handler(struct k_work *work) +{ + int ret; + + LOG_INF("Starting audio response measurement: id=%u transfer_id=%u samples=%u volume=%.2f", + pending_config.id, pending_config.transfer_id, transfer.total_samples, + (double)pending_config.volume); + + if (!fifo_rx.initialized) { + LOG_DBG("Initializing RX FIFO for audio response measurement"); + ret = data_fifo_init(&fifo_rx); + if (ret != 0) { + LOG_ERR("Failed to initialize RX FIFO: %d", ret); + goto fail; + } + } + + ret = hw_codec_volume_unmute(); + if (ret != 0) { + LOG_ERR("Failed to unmute codec volume: %d", ret); + goto fail; + } + ret = audio_datapath_decimator_init(CONFIG_AUDIO_SAMPLE_RATE_HZ / + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE); + if (ret != 0) { + LOG_ERR("Failed to initialize audio response decimator: %d", ret); + goto fail; + } + ret = audio_datapath_aquire(&fifo_rx); + if (ret != 0) { + LOG_ERR("Failed to acquire audio datapath: %d", ret); + goto fail; + } + ret = audio_datapath_buffer_play(transfer.samples, transfer.total_samples, false, + pending_config.volume, NULL); + if (ret != 0) { + LOG_ERR("Failed to start audio response playback: %d", ret); + audio_datapath_release(); + goto fail; + } + + LOG_INF("Audio response playback started: id=%u capture_samples=%u capture_rate=%u initial_drop=%u", + pending_config.id, AUDIO_RESPONSE_CAPTURE_SAMPLES, + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE, AUDIO_RESPONSE_INITIAL_DROP); + record_to_buffer(captured_samples, AUDIO_RESPONSE_CAPTURE_SAMPLES, + AUDIO_RESPONSE_INITIAL_DROP, false, true, audio_response_capture_complete); + return; + +fail: + LOG_ERR("Failed to start audio response measurement: %d", ret); + k_mutex_lock(&service_mutex, K_FOREVER); + measurement_active = false; + k_mutex_unlock(&service_mutex); +} + +/** + * Encode and notify the FFT response at the configured target frequencies. + */ +static void notify_result(void) +{ + audio_response_result_t result = { + .id = pending_config.id, + .points = AUDIO_RESPONSE_RESULT_POINTS, + .frequencies = result_frequencies, + .response = result_response, + }; + size_t payload_size; + + if (!result_notifications_enabled) { + LOG_WRN("Audio response result notifications are not enabled: id=%u", + pending_config.id); + return; + } + if (audio_response_result_encode(&result, result_payload, sizeof(result_payload), &payload_size) != + PROTOCOL_OK) { + LOG_ERR("Failed to encode audio response result: id=%u points=%u", result.id, + result.points); + return; + } + + int ret = bt_gatt_notify(NULL, &audio_response_svc.attrs[11], result_payload, payload_size); + if (ret != 0) { + LOG_ERR("Failed to notify audio response result: id=%u err=%d", result.id, ret); + return; + } + LOG_INF("Audio response result notified: id=%u points=%u bytes=%u", result.id, + result.points, payload_size); +} + +/** + * Compute the captured signal's FFT response and notify the requesting client. + */ +static void measurement_complete_work_handler(struct k_work *work) +{ + static arm_rfft_instance_q15 rfft; + static bool rfft_initialized; + + LOG_INF("Audio response capture complete: id=%u", pending_config.id); + audio_datapath_buffer_stop(); + audio_datapath_release(); + + if (!rfft_initialized) { + LOG_DBG("Initializing audio response FFT: samples=%u", AUDIO_RESPONSE_CAPTURE_SAMPLES); + if (arm_rfft_init_q15(&rfft, AUDIO_RESPONSE_CAPTURE_SAMPLES, 0, 1) != ARM_MATH_SUCCESS) { + LOG_ERR("Failed to initialize audio response FFT"); + goto complete; + } + rfft_initialized = true; + } + + arm_rfft_q15(&rfft, captured_samples, fft_output); + arm_cmplx_mag_q15(fft_output, magnitudes, AUDIO_RESPONSE_CAPTURE_SAMPLES / 2); + + for (size_t index = 0; index < AUDIO_RESPONSE_RESULT_POINTS; ++index) { + uint32_t bin = ((uint32_t)target_frequencies[index] * AUDIO_RESPONSE_CAPTURE_SAMPLES + + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE / 2) / + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE; + result_frequencies[index] = target_frequencies[index]; + result_response[index] = magnitudes[bin]; + } + LOG_DBG("Audio response FFT complete: id=%u bins=%u result_points=%u", pending_config.id, + AUDIO_RESPONSE_CAPTURE_SAMPLES / 2, AUDIO_RESPONSE_RESULT_POINTS); + notify_result(); + +complete: + k_mutex_lock(&service_mutex, K_FOREVER); + measurement_active = false; + k_mutex_unlock(&service_mutex); + LOG_DBG("Audio response measurement complete: id=%u", pending_config.id); +} + +/** + * Release an incomplete transfer after the protocol timeout. + */ +static void transfer_timeout_work_handler(struct k_work *work) +{ + k_mutex_lock(&service_mutex, K_FOREVER); + if (transfer.active && !transfer.committed) { + LOG_WRN("Transfer timed out: id=%u received=%u/%u", transfer.id, + transfer.received_samples, transfer.total_samples); + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_TIMED_OUT, 0); + reset_transfer(); + } + k_mutex_unlock(&service_mutex); +} + +void audio_response_capture_complete(void) +{ + LOG_DBG("Audio response capture completion callback"); + k_work_submit(&measurement_complete_work); +} + +int init_audio_response_service(void) +{ + LOG_INF("Initializing audio response service"); + k_work_init(&measurement_work, measurement_work_handler); + k_work_init(&measurement_complete_work, measurement_complete_work_handler); + k_work_init_delayable(&transfer_timeout_work, transfer_timeout_work_handler); + return 0; +} diff --git a/src/bluetooth/gatt_services/audio_response_service.h b/src/bluetooth/gatt_services/audio_response_service.h new file mode 100644 index 00000000..948f6da7 --- /dev/null +++ b/src/bluetooth/gatt_services/audio_response_service.h @@ -0,0 +1,24 @@ +#ifndef _AUDIO_RESPONSE_SERVICE_H_ +#define _AUDIO_RESPONSE_SERVICE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Initialize the audio response service's asynchronous work items. + * + * @return 0 on success. + */ +int init_audio_response_service(void); + +/** + * Signal that the microphone capture for an audio response measurement completed. + */ +void audio_response_capture_complete(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _AUDIO_RESPONSE_SERVICE_H_ */ diff --git a/src/bluetooth/gatt_services/seal_check_service.c b/src/bluetooth/gatt_services/seal_check_service.c deleted file mode 100644 index e9feb7c0..00000000 --- a/src/bluetooth/gatt_services/seal_check_service.c +++ /dev/null @@ -1,434 +0,0 @@ -#include "seal_check_service.h" -#include -#include -#include -#include -#include -#include "arm_math.h" - -#include "../../SensorManager/SensorManager.h" -#include "audio_datapath.h" -#include "decimation_filter.h" - -#include "multitone.h" - -LOG_MODULE_REGISTER(seal_check_service, CONFIG_LOG_DEFAULT_LEVEL); - -#define NUM_SEAL_CHECK_SAMPLES 2048 -#define INITIAL_SEAL_CHECK_DROP 128 - -int16_t seal_check_mic[NUM_SEAL_CHECK_SAMPLES]; -//int seal_check_mic_index = 0; - -static q15_t fft_output[NUM_SEAL_CHECK_SAMPLES * 2]; // Complex output needs double size -static q15_t magnitude[NUM_SEAL_CHECK_SAMPLES / 2]; // Magnitude spectrum - -#define num_bins 9 -const int bin_tolerance = 2; -static float avg_magnitude = 119.0f; -static float avg_slope = -0.07382279460490486; -static float target_frequencies[] = {40.0, 60.0, 90.0, 135.0, 202.5, 303.75, 455.625, 683.4375, 1025.15625}; -static float target_magnitudes[] = {0.90833731, 1.18334124, 1.38796968, 1.16634027, 0.85781358, 0.65981396, 0.84768657, 0.98236069, 1.00633671}; - -// Service state -static uint8_t seal_check_start_value = 0x00; -static struct seal_check_data seal_check_result_data; -static bool ccc_enabled = false; - -// Function prototypes -//extern int audio_datapath_multitone_play(uint16_t dur_ms, float amplitude); -extern int hw_codec_volume_set(uint8_t volume); - -extern struct data_fifo fifo_rx; - -// Work item for seal check completion -static struct k_work seal_check_complete_work; - -void seal_check_callback(); - -// Callback for start characteristic write -static ssize_t write_seal_check_start(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, - uint16_t offset, uint8_t flags) -{ - if (offset + len > sizeof(seal_check_start_value)) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); - } - - if (len != 1) { - return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); - } - - uint8_t value = *((uint8_t*)buf); - - if (value == 0xFF) { - LOG_INF("Seal check started via BLE"); - seal_check_start_value = 0xFF; - - int ret; - if (!fifo_rx.initialized) { - ret = data_fifo_init(&fifo_rx); - if (ret) { - LOG_ERR("Failed to set up rx FIFO: %d", ret); - return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); - } - } - - hw_codec_volume_unmute(); - - // Set volume and start multitone - hw_codec_volume_set(0xB0); - - audio_datapath_decimator_init(12); // 12 = 4kHz - audio_datapath_aquire(&fifo_rx); - - // Start multitone playbook (1.0 amplitude) - ret = audio_datapath_buffer_play((int16_t*)multitone, multitone_length, false, 1.0f, NULL); - - record_to_buffer(seal_check_mic, NUM_SEAL_CHECK_SAMPLES, INITIAL_SEAL_CHECK_DROP, false, true, seal_check_callback); - - if (ret != 0) { - LOG_ERR("Failed to start seal check: %d", ret); - seal_check_start_value = 0x00; - return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); - } - - LOG_INF("Seal check started successfully"); - } - - return len; -} - -// Callback for start characteristic read -static ssize_t read_seal_check_start(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - void *buf, uint16_t len, uint16_t offset) -{ - return bt_gatt_attr_read(conn, attr, buf, len, offset, - &seal_check_start_value, sizeof(seal_check_start_value)); -} - -// Callback for result characteristic CCC write -static void seal_check_result_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) -{ - ccc_enabled = (value == BT_GATT_CCC_NOTIFY); - LOG_INF("Seal check result notifications %s", ccc_enabled ? "enabled" : "disabled"); -} - -// GATT service definition -BT_GATT_SERVICE_DEFINE(seal_check_svc, - BT_GATT_PRIMARY_SERVICE(BT_UUID_SEAL_CHECK_SERVICE), - - // Start Test Characteristic - BT_GATT_CHARACTERISTIC(BT_UUID_SEAL_CHECK_START, - BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, - read_seal_check_start, write_seal_check_start, - &seal_check_start_value), - - // Result Data Characteristic - BT_GATT_CHARACTERISTIC(BT_UUID_SEAL_CHECK_RESULT, - BT_GATT_CHRC_NOTIFY, - BT_GATT_PERM_NONE, - NULL, NULL, &seal_check_result_data), - BT_GATT_CCC(seal_check_result_ccc_cfg_changed, - BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), -); - -// Work handler for seal check completion -static void seal_check_complete_work_handler(struct k_work *work) -{ - on_seal_check_complete(); -} - -void on_seal_check_complete() { - audio_datapath_buffer_stop(); - - audio_datapath_release(); - //audio_datapath_decimator_cleanup(); - - compute_seal_check_result(); -} - -void compute_seal_check_result() -{ - // Compute RFFT Q15 for seal check analysis - static arm_rfft_instance_q15 rfft_instance; - static bool rfft_initialized = false; - - if (!rfft_initialized) { - arm_status status = arm_rfft_init_q15(&rfft_instance, NUM_SEAL_CHECK_SAMPLES, 0, 1); - if (status == ARM_MATH_SUCCESS) { - rfft_initialized = true; - LOG_INF("RFFT Q15 initialized for %d samples", NUM_SEAL_CHECK_SAMPLES); - } else { - LOG_ERR("RFFT Q15 initialization failed with status %d", status); - return; - } - } - - // Perform RFFT - arm_rfft_q15(&rfft_instance, seal_check_mic, fft_output); - - // Calculate magnitude spectrum - arm_cmplx_mag_q15(fft_output, magnitude, NUM_SEAL_CHECK_SAMPLES / 2); - - LOG_INF("Seal check RFFT completed, %d frequency bins calculated", NUM_SEAL_CHECK_SAMPLES / 2); - - // Calculate mean magnitude of the spectrum - float spectrum_sum = 0.0f; - int valid_bins = 0; - for (int bin = 1; bin < NUM_SEAL_CHECK_SAMPLES / 2; bin++) { - spectrum_sum += (float)magnitude[bin]; - valid_bins++; - } - float mean_magnitude = spectrum_sum / valid_bins; - float peak_threshold = 4.0f * mean_magnitude; - - // Analyze center frequencies with magnitude weighting - printk("Center frequency analysis (sampling rate: 4000 Hz, mean_mag: %.1f, threshold: %.1f):\n", - (double)mean_magnitude, (double)peak_threshold); - - // Arrays for linear regression - float valid_frequencies[num_bins]; - float valid_amplitudes[num_bins]; - int valid_peak_count = 0; - - for (int center_idx = 0; center_idx < num_bins; center_idx++) { - float center_freq = target_frequencies[center_idx]; - int center_bin = (int)(center_freq * NUM_SEAL_CHECK_SAMPLES / 4000.0f + 0.5f); - - // Define search range - int start_bin = MAX(1, center_bin - bin_tolerance); - int end_bin = MIN(NUM_SEAL_CHECK_SAMPLES / 2 - 1, center_bin + bin_tolerance); - - // Calculate weighted center frequency and total magnitude - float weighted_freq_sum = 0.0f; - float total_magnitude = 0.0f; - q15_t peak_magnitude = 0; - int peak_bin = center_bin; - - for (int bin = start_bin; bin <= end_bin; bin++) { - float bin_freq = (float)bin * 4000.0f / NUM_SEAL_CHECK_SAMPLES; - float magnitude_weight = (float)magnitude[bin]; - - weighted_freq_sum += bin_freq * magnitude_weight; - total_magnitude += magnitude_weight; - - // Track peak for amplitude calculation - if (magnitude[bin] > peak_magnitude) { - peak_magnitude = magnitude[bin]; - peak_bin = bin; - } - } - - // Calculate weighted center frequency - float actual_center_freq = 0.0f; - bool valid_peak = false; - - if (total_magnitude > 0) { - actual_center_freq = weighted_freq_sum / total_magnitude; - } else { - actual_center_freq = center_freq; // fallback to expected center - } - - // Check if peak is valid (higher than threshold) - if (peak_magnitude > peak_threshold) { - valid_peak = true; - } - - // Interpolate peak amplitude for better accuracy (only for valid peaks) - float interpolated_amplitude = (float)peak_magnitude; - //if (valid_peak && peak_bin > 0 && peak_bin < NUM_SEAL_CHECK_SAMPLES / 2 - 1) { - if (peak_bin > 0 && peak_bin < NUM_SEAL_CHECK_SAMPLES / 2 - 1) { - // Parabolic interpolation for peak refinement - float y1 = (float)magnitude[peak_bin - 1]; - float y2 = (float)magnitude[peak_bin]; - float y3 = (float)magnitude[peak_bin + 1]; - - float a = (y1 - 2*y2 + y3) / 2; - float b = (y3 - y1) / 2; - - if (a != 0) { - float peak_offset = -b / (2*a); - // Limit offset to reasonable range - if (peak_offset > -1.0f && peak_offset < 1.0f) { - interpolated_amplitude = y2 - (b*b)/(4*a); - actual_center_freq += peak_offset * (4000.0f / NUM_SEAL_CHECK_SAMPLES); - } - } - } - - // Store valid peaks for linear regression - if (valid_peak) { - valid_frequencies[valid_peak_count] = actual_center_freq; - valid_amplitudes[valid_peak_count] = interpolated_amplitude; - valid_peak_count++; - } - - printk("Bin %d: Expected %.2f Hz, Found %.2f Hz, Amplitude: %.1f (raw: %d, total_mag: %.1f) %s\n", - center_idx, - (double)center_freq, - (double)actual_center_freq, - (double)interpolated_amplitude, - peak_magnitude, - (double)total_magnitude, - valid_peak ? "VALID" : "WEAK"); - } - - // Perform linear regression on valid peaks (magnitude vs log(frequency)) - if (valid_peak_count >= 2) { - // Calculate log frequencies for regression - float log_frequencies[num_bins]; - for (int i = 0; i < valid_peak_count; i++) { - log_frequencies[i] = logf(valid_frequencies[i]); - } - - // Calculate means - float mean_log_freq = 0.0f; - float mean_amp = 0.0f; - for (int i = 0; i < valid_peak_count; i++) { - mean_log_freq += log_frequencies[i]; - mean_amp += valid_amplitudes[i]; - } - mean_log_freq /= valid_peak_count; - mean_amp /= valid_peak_count; - - // Calculate slope (linear regression: magnitude vs log(frequency)) - float numerator = 0.0f; - float denominator = 0.0f; - for (int i = 0; i < valid_peak_count; i++) { - float log_freq_diff = log_frequencies[i] - mean_log_freq; - float amp_diff = valid_amplitudes[i] - mean_amp; - numerator += log_freq_diff * amp_diff; - denominator += log_freq_diff * log_freq_diff; - } - - float slope = 0.0f; - if (denominator != 0.0f) { - slope = numerator / denominator; - } - - // Calculate correlation coefficient for quality assessment - /*float sum_sq_log_freq = 0.0f; - float sum_sq_amp = 0.0f; - for (int i = 0; i < valid_peak_count; i++) { - float log_freq_diff = log_frequencies[i] - mean_log_freq; - float amp_diff = valid_amplitudes[i] - mean_amp; - sum_sq_log_freq += log_freq_diff * log_freq_diff; - sum_sq_amp += amp_diff * amp_diff; - } - - float correlation = 0.0f; - if (sum_sq_log_freq > 0.0f && sum_sq_amp > 0.0f) { - correlation = numerator / (sqrtf(sum_sq_log_freq) * sqrtf(sum_sq_amp)); - }*/ - - float avg_peak_mag = 0.0f; - for (int i = 0; i < valid_peak_count; i++) { - avg_peak_mag += valid_amplitudes[i]; - } - avg_peak_mag /= valid_peak_count; - - float mse = 0.0f; - for (int i = 0; i < valid_peak_count; i++) { - float freq_error = valid_amplitudes[i] / avg_peak_mag - target_magnitudes[i]; - mse += freq_error * freq_error; - } - mse /= valid_peak_count; - - float seal_quality = fminf(avg_peak_mag / avg_magnitude, 1.f) - mse - (slope / avg_magnitude - avg_slope); - seal_quality = fmaxf(0.0f, fminf(100.0f, seal_quality * 100.f)); // Clamp between 0 and 100 - - printk("Linear Regression Results (magnitude vs log(frequency)):\n"); - printk("Valid peaks: %d, Slope: %.3f\n", //, Correlation: %.3f\n", - valid_peak_count, (double)slope / avg_magnitude); //, (double)correlation); - printk("Seal Quality: %.3f\n", (double)seal_quality); - - // Prepare and send seal check data via GATT service - struct seal_check_data gatt_data; - gatt_data.version = 1; - gatt_data.quality = (uint8_t)(seal_quality); // Scale to 0-255 - gatt_data.mean_magnitude = (uint8_t)(mean_magnitude * 8.0f > 255.0f ? 255 : (uint8_t)(mean_magnitude * 8.0f)); - gatt_data.num_peaks = valid_peak_count; - - // Fill frequency and magnitude arrays - for (int i = 0; i < 9; i++) { - if (i < valid_peak_count) { - // Convert to 12.4 fixed point (multiply by 16) - gatt_data.frequencies[i] = (uint16_t)(valid_frequencies[i] * 16.0f); - gatt_data.magnitudes[i] = (uint16_t)(valid_amplitudes[i] > 65535.0f ? 65535 : (uint16_t)valid_amplitudes[i]); - } else { - gatt_data.frequencies[i] = 0; - gatt_data.magnitudes[i] = 0; - } - } - - // Send via GATT service - seal_check_notify_result(&gatt_data); - } else { - printk("Not enough valid peaks (%d) for linear regression\n", valid_peak_count); - - // Send minimal data even if regression failed - struct seal_check_data gatt_data; - gatt_data.version = 1; - gatt_data.quality = 0; // No quality measurement possible - gatt_data.mean_magnitude = (uint8_t)(mean_magnitude * 8.0f > 255.0f ? 255 : (uint8_t)(mean_magnitude * 8.0f)); - gatt_data.num_peaks = valid_peak_count; - - // Fill available data - for (int i = 0; i < 9; i++) { - if (i < valid_peak_count) { - gatt_data.frequencies[i] = (uint16_t)(valid_frequencies[i] * 16.0f); - gatt_data.magnitudes[i] = (uint16_t)(valid_amplitudes[i] > 65535.0f ? 65535 : (uint16_t)valid_amplitudes[i]); - } else { - gatt_data.frequencies[i] = 0; - gatt_data.magnitudes[i] = 0; - } - } - - seal_check_notify_result(&gatt_data); - } -} - -// Function to notify result data -int seal_check_notify_result(const struct seal_check_data *data) -{ - if (!ccc_enabled) { - LOG_WRN("Seal check result notifications not enabled"); - return -ENOENT; - } - - // Copy data to local storage - memcpy(&seal_check_result_data, data, sizeof(seal_check_result_data)); - - // Reset start value to indicate test completion - seal_check_start_value = 0x00; - - // Send notification - int err = bt_gatt_notify(NULL, &seal_check_svc.attrs[4], - &seal_check_result_data, sizeof(seal_check_result_data)); - - if (err) { - LOG_ERR("Failed to notify seal check result: %d", err); - return err; - } - - LOG_INF("Seal check result notified successfully"); - return 0; -} - -void seal_check_callback() { - k_work_submit(&seal_check_complete_work); -} - -// Service initialization -int init_seal_check_service(void) -{ - // Initialize work item - k_work_init(&seal_check_complete_work, seal_check_complete_work_handler); - - LOG_INF("Seal check service initialized"); - return 0; -} \ No newline at end of file diff --git a/src/bluetooth/gatt_services/seal_check_service.h b/src/bluetooth/gatt_services/seal_check_service.h deleted file mode 100644 index 56396f3b..00000000 --- a/src/bluetooth/gatt_services/seal_check_service.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _SEAL_CHECK_SERVICE_H_ -#define _SEAL_CHECK_SERVICE_H_ - -#include -#include -#include -#include "openearable_common.h" - -// Service UUID -#define BT_UUID_SEAL_CHECK_SERVICE_VAL \ - BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x9abc, 0xdef123456789) - -#define BT_UUID_SEAL_CHECK_SERVICE \ - BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_SERVICE_VAL) - -// Start Test Characteristic UUID (write 0xFF to start, reads back 0x00 when done) -#define BT_UUID_SEAL_CHECK_START_VAL \ - BT_UUID_128_ENCODE(0x12345679, 0x1234, 0x5678, 0x9abc, 0xdef123456789) - -#define BT_UUID_SEAL_CHECK_START \ - BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_START_VAL) - -// Result Data Characteristic UUID (notify with seal_check_data) -#define BT_UUID_SEAL_CHECK_RESULT_VAL \ - BT_UUID_128_ENCODE(0x1234567A, 0x1234, 0x5678, 0x9abc, 0xdef123456789) - -#define BT_UUID_SEAL_CHECK_RESULT \ - BT_UUID_DECLARE_128(BT_UUID_SEAL_CHECK_RESULT_VAL) - -#ifdef __cplusplus -extern "C" { -#endif - -// Function declarations -int init_seal_check_service(void); -int seal_check_notify_result(const struct seal_check_data *data); - -#ifdef __cplusplus -} -#endif - -#endif /* _SEAL_CHECK_SERVICE_H_ */ \ No newline at end of file diff --git a/unicast_server/main.cpp b/unicast_server/main.cpp index 52ceb60d..4da7011f 100644 --- a/unicast_server/main.cpp +++ b/unicast_server/main.cpp @@ -28,7 +28,7 @@ #include "button_service.h" #include "sensor_service.h" #include "led_service.h" -#include "seal_check_service.h" +#include "audio_response_service.h" #include "SensorScheme.h" #include "DefaultSensors.h" @@ -111,7 +111,7 @@ int main(void) { ret = init_sensor_service(); ERR_CHK(ret); - ret = init_seal_check_service(); + ret = init_audio_response_service(); ERR_CHK(ret); bt_mgmt_conn_interval_init(new ConnIntvlLinear( From 18af30834bc80161a2fd351c27a61d0f96f35873 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:27:10 +0200 Subject: [PATCH 24/77] fix(build_firmware): set submodule path for Zephyr workspace initialization --- .github/workflows/build_firmware.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_firmware.yaml b/.github/workflows/build_firmware.yaml index c2802f06..8a70d08f 100644 --- a/.github/workflows/build_firmware.yaml +++ b/.github/workflows/build_firmware.yaml @@ -28,6 +28,7 @@ jobs: # Clone the repo to a subdirectory, so we can initialize the Zephyr # workspace in the parent directory. path: zephyr-workspace/open-earable-v2 + submodules: recursive - name: Cache/Install APT Packages uses: awalsh128/cache-apt-pkgs-action@latest From 4889d6e6eb0d75519d1e2fc04a1c84eaa03bbf4d Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:31:26 +0200 Subject: [PATCH 25/77] chore: update submodule commit for protocol --- protocol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol b/protocol index 9f4dc917..925cc17a 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit 9f4dc917cf0664d9ab1c2c5dedb2b21df7f121a2 +Subproject commit 925cc17a540d48a6967d4af4cbbc64043e8692f4 From a0132befb64f5d5c542688024030ee6d2a534114 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:32:46 +0200 Subject: [PATCH 26/77] feat(audio-response): add configurable audio response measurement points --- protocol | 2 +- src/bluetooth/Kconfig | 9 ++ .../gatt_services/audio_response_service.c | 104 +++++++++++++++--- 3 files changed, 96 insertions(+), 19 deletions(-) diff --git a/protocol b/protocol index 925cc17a..53221c84 160000 --- a/protocol +++ b/protocol @@ -1 +1 @@ -Subproject commit 925cc17a540d48a6967d4af4cbbc64043e8692f4 +Subproject commit 53221c847f2c00924ed80e8f0b1b0c90d3a5eba0 diff --git a/src/bluetooth/Kconfig b/src/bluetooth/Kconfig index 00f0f3da..0bafa549 100644 --- a/src/bluetooth/Kconfig +++ b/src/bluetooth/Kconfig @@ -16,6 +16,15 @@ config AUDIO_RESPONSE_MAX_SAMPLES Maximum number of signed 16-bit PCM samples retained by the audio response service. Storage for this buffer is reserved at build time. +config AUDIO_RESPONSE_MAX_POINTS + int "Maximum audio response measurement points" + range 9 60 + default 60 + help + Maximum number of frequency response points accepted in one audio + response config request. The default keeps result notifications within a + 247-byte ATT MTU because the encoded result uses 2 + 4 * points bytes. + rsource "bt_management/Kconfig" #----------------------------------------------------------------------------# diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index e7bc27e0..bec3e7a5 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "arm_math.h" #include "audio_datapath.h" @@ -17,11 +18,14 @@ LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); #define AUDIO_RESPONSE_CAPTURE_SAMPLES 2048 #define AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE 4000U #define AUDIO_RESPONSE_INITIAL_DROP 128 -#define AUDIO_RESPONSE_RESULT_POINTS 9 +#define AUDIO_RESPONSE_DEFAULT_POINTS 9 #define AUDIO_RESPONSE_TRANSFER_CREDITS 1 #define AUDIO_RESPONSE_TRANSFER_TIMEOUT K_SECONDS(30) #define AUDIO_RESPONSE_STATUS_ENCODED_SIZE 9 -#define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (AUDIO_RESPONSE_RESULT_POINTS * 4)) +#define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (CONFIG_AUDIO_RESPONSE_MAX_POINTS * 4)) + +BUILD_ASSERT(CONFIG_AUDIO_RESPONSE_MAX_POINTS >= AUDIO_RESPONSE_DEFAULT_POINTS, + "Default audio response points must fit CONFIG_AUDIO_RESPONSE_MAX_POINTS"); enum audio_response_transfer_status { AUDIO_RESPONSE_TRANSFER_READY = 0, @@ -53,7 +57,7 @@ struct transfer_control_dispatch_context { ssize_t result; }; -static const uint16_t target_frequencies[AUDIO_RESPONSE_RESULT_POINTS] = { +static const uint16_t default_frequencies[AUDIO_RESPONSE_DEFAULT_POINTS] = { 40, 60, 90, 135, 203, 304, 456, 683, 1025, }; @@ -61,8 +65,10 @@ static int16_t captured_samples[AUDIO_RESPONSE_CAPTURE_SAMPLES]; static int16_t uploaded_samples[CONFIG_AUDIO_RESPONSE_MAX_SAMPLES]; static q15_t fft_output[AUDIO_RESPONSE_CAPTURE_SAMPLES * 2]; static q15_t magnitudes[AUDIO_RESPONSE_CAPTURE_SAMPLES / 2]; -static uint16_t result_frequencies[AUDIO_RESPONSE_RESULT_POINTS]; -static uint16_t result_response[AUDIO_RESPONSE_RESULT_POINTS]; +static uint16_t requested_frequencies[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; +static uint16_t decoded_config_frequencies[UINT8_MAX]; +static uint16_t result_frequencies[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; +static uint16_t result_response[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; static uint8_t result_payload[AUDIO_RESPONSE_RESULT_ENCODED_SIZE]; static struct audio_response_transfer transfer; @@ -80,6 +86,9 @@ extern struct data_fifo fifo_rx; extern const struct bt_gatt_service_static audio_response_svc; static int notify_transfer_status(enum audio_response_transfer_status status, uint16_t credits); +static uint32_t response_frequency_to_bin(uint16_t frequency); +static bool is_valid_response_frequency(uint16_t frequency); +static int select_response_points(audio_response_config_t *config); static void reset_transfer(void); /** @@ -126,6 +135,60 @@ static const char *transfer_control_type_name(audio_response_transfer_control_ty } } +/** + * Return the nearest FFT magnitude bin for a requested response frequency. + */ +static uint32_t response_frequency_to_bin(uint16_t frequency) +{ + return ((uint32_t)frequency * AUDIO_RESPONSE_CAPTURE_SAMPLES + + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE / 2) / + AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE; +} + +/** + * Validate that a requested response frequency maps to a captured FFT bin. + */ +static bool is_valid_response_frequency(uint16_t frequency) +{ + return response_frequency_to_bin(frequency) < ARRAY_SIZE(magnitudes); +} + +/** + * Copy the requested response points into service-owned storage. + * + * A zero-point request selects the firmware default table. Nonzero requests + * use the protocol-provided frequency list after validating that every + * frequency maps to the captured FFT range. + */ +static int select_response_points(audio_response_config_t *config) +{ + const uint16_t *frequencies = config->frequencies; + uint8_t points = config->points; + + if (points == 0) { + points = ARRAY_SIZE(default_frequencies); + frequencies = default_frequencies; + LOG_DBG("Using default audio response points: points=%u", points); + } else if (points > CONFIG_AUDIO_RESPONSE_MAX_POINTS) { + LOG_WRN("Audio response config rejected: points=%u max_points=%u", points, + CONFIG_AUDIO_RESPONSE_MAX_POINTS); + return -EINVAL; + } + + for (size_t index = 0; index < points; ++index) { + if (!is_valid_response_frequency(frequencies[index])) { + LOG_WRN("Audio response config rejected: frequency=%u index=%u capture_rate=%u", + frequencies[index], index, AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE); + return -EINVAL; + } + requested_frequencies[index] = frequencies[index]; + } + + config->points = points; + config->frequencies = requested_frequencies; + return 0; +} + /** * Clear all transfer metadata and make the upload buffer reusable. */ @@ -446,23 +509,29 @@ static ssize_t write_audio_response_config(struct bt_conn *conn, const struct bt { audio_response_config_t config; size_t bytes_read = 0; + int ret; if (offset != 0) { LOG_WRN("Audio response config rejected: invalid offset=%u len=%u", offset, len); return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } + config.frequencies = decoded_config_frequencies; if (audio_response_config_decode(&config, buf, len, &bytes_read) != PROTOCOL_OK || bytes_read != len) { LOG_WRN("Audio response config decode failed: bytes_read=%u len=%u", bytes_read, len); return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } - LOG_INF("Audio response config requested: id=%u transfer_id=%u volume=%.2f", config.id, - config.transfer_id, (double)config.volume); + LOG_INF("Audio response config requested: id=%u transfer_id=%u volume=%.2f points=%u", + config.id, config.transfer_id, (double)config.volume, config.points); if (!isfinite(config.volume) || config.volume < 0.0f || config.volume > 1.0f) { LOG_WRN("Audio response config rejected: invalid volume=%.3f", (double)config.volume); return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); } + ret = select_response_points(&config); + if (ret != 0) { + return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); + } k_mutex_lock(&service_mutex, K_FOREVER); if (!transfer.committed || config.transfer_id != transfer.id || measurement_active) { @@ -476,8 +545,8 @@ static ssize_t write_audio_response_config(struct bt_conn *conn, const struct bt k_mutex_unlock(&service_mutex); k_work_submit(&measurement_work); - LOG_INF("Audio response measurement queued: id=%u transfer_id=%u", pending_config.id, - pending_config.transfer_id); + LOG_INF("Audio response measurement queued: id=%u transfer_id=%u points=%u", + pending_config.id, pending_config.transfer_id, pending_config.points); return len; } @@ -534,9 +603,9 @@ static void measurement_work_handler(struct k_work *work) { int ret; - LOG_INF("Starting audio response measurement: id=%u transfer_id=%u samples=%u volume=%.2f", + LOG_INF("Starting audio response measurement: id=%u transfer_id=%u samples=%u volume=%.2f points=%u", pending_config.id, pending_config.transfer_id, transfer.total_samples, - (double)pending_config.volume); + (double)pending_config.volume, pending_config.points); if (!fifo_rx.initialized) { LOG_DBG("Initializing RX FIFO for audio response measurement"); @@ -592,7 +661,7 @@ static void notify_result(void) { audio_response_result_t result = { .id = pending_config.id, - .points = AUDIO_RESPONSE_RESULT_POINTS, + .points = pending_config.points, .frequencies = result_frequencies, .response = result_response, }; @@ -643,15 +712,14 @@ static void measurement_complete_work_handler(struct k_work *work) arm_rfft_q15(&rfft, captured_samples, fft_output); arm_cmplx_mag_q15(fft_output, magnitudes, AUDIO_RESPONSE_CAPTURE_SAMPLES / 2); - for (size_t index = 0; index < AUDIO_RESPONSE_RESULT_POINTS; ++index) { - uint32_t bin = ((uint32_t)target_frequencies[index] * AUDIO_RESPONSE_CAPTURE_SAMPLES + - AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE / 2) / - AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE; - result_frequencies[index] = target_frequencies[index]; + for (size_t index = 0; index < pending_config.points; ++index) { + uint32_t bin = response_frequency_to_bin(pending_config.frequencies[index]); + + result_frequencies[index] = pending_config.frequencies[index]; result_response[index] = magnitudes[bin]; } LOG_DBG("Audio response FFT complete: id=%u bins=%u result_points=%u", pending_config.id, - AUDIO_RESPONSE_CAPTURE_SAMPLES / 2, AUDIO_RESPONSE_RESULT_POINTS); + AUDIO_RESPONSE_CAPTURE_SAMPLES / 2, pending_config.points); notify_result(); complete: From 3d5aa3129f7b47c506c138320b88677225854106 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:31:47 +0200 Subject: [PATCH 27/77] fix(multitone): removed unused multitone --- src/audio/multitone.h | 1003 ----------------------------------------- 1 file changed, 1003 deletions(-) delete mode 100644 src/audio/multitone.h diff --git a/src/audio/multitone.h b/src/audio/multitone.h deleted file mode 100644 index 61142e50..00000000 --- a/src/audio/multitone.h +++ /dev/null @@ -1,1003 +0,0 @@ -const int16_t multitone[] = { - 2470, 2689, 2878, 3033, 3145, 3210, 3222, 3177, 3073, 2907, 2678, 2386, 2032, 1618, 1148, 626, 57, -551, -1194, -1862, -2547, -3241, -3934, -4617, -5281, -5916, -6514, -7066, -7564, -8002, -8372, -8670, - -8891, -9032, -9091, -9067, -8960, -8772, -8506, -8165, -7756, -7283, -6754, -6176, -5559, -4911, -4242, -3562, -2880, -2206, -1550, -922, -330, 217, 712, 1149, 1522, 1826, 2057, 2214, 2294, 2297, 2226, 2081, - 1866, 1585, 1244, 847, 403, -82, -601, -1146, -1708, -2279, -2851, -3417, -3969, -4500, -5004, -5475, -5908, -6299, -6645, -6944, -7195, -7396, -7549, -7655, -7716, -7735, -7716, -7663, -7580, -7474, -7348, -7208, - -7060, -6909, -6760, -6618, -6487, -6371, -6272, -6194, -6138, -6105, -6095, -6108, -6142, -6195, -6263, -6345, -6435, -6529, -6622, -6709, -6784, -6843, -6880, -6889, -6867, -6808, -6709, -6567, -6380, -6145, -5863, -5533, - -5156, -4735, -4272, -3772, -3240, -2680, -2100, -1507, -908, -310, 275, 843, 1382, 1886, 2346, 2753, 3101, 3382, 3592, 3724, 3776, 3743, 3625, 3419, 3128, 2751, 2293, 1758, 1149, 473, -261, -1048, - -1878, -2742, -3631, -4535, -5443, -6346, -7234, -8096, -8925, -9711, -10447, -11126, -11742, -12289, -12765, -13166, -13491, -13740, -13914, -14014, -14044, -14009, -13913, -13764, -13566, -13330, -13061, -12770, -12463, -12152, -11843, -11545, - -11267, -11015, -10798, -10620, -10487, -10404, -10374, -10400, -10481, -10619, -10813, -11060, -11358, -11702, -12087, -12509, -12960, -13434, -13923, -14419, -14915, -15403, -15876, -16325, -16743, -17124, -17462, -17752, -17988, -18167, -18287, -18346, - -18342, -18275, -18148, -17961, -17718, -17422, -17078, -16690, -16264, -15806, -15323, -14820, -14304, -13782, -13260, -12743, -12238, -11748, -11279, -10834, -10417, -10028, -9670, -9343, -9047, -8780, -8541, -8326, -8134, -7959, -7797, -7644, - -7495, -7344, -7186, -7016, -6830, -6622, -6388, -6125, -5830, -5500, -5135, -4733, -4294, -3821, -3314, -2777, -2213, -1628, -1026, -414, 202, 815, 1418, 2003, 2562, 3087, 3572, 4010, 4392, 4714, 4971, 5156, - 5268, 5303, 5260, 5139, 4939, 4664, 4315, 3898, 3417, 2879, 2291, 1660, 996, 308, -394, -1100, -1801, -2486, -3144, -3766, -4342, -4864, -5324, -5715, -6030, -6264, -6413, -6475, -6449, -6333, -6129, -5839, - -5466, -5016, -4493, -3906, -3260, -2564, -1828, -1061, -271, 529, 1332, 2128, 2906, 3659, 4377, 5053, 5679, 6251, 6762, 7209, 7590, 7901, 8143, 8316, 8421, 8461, 8439, 8361, 8230, 8053, 7836, 7586, - 7310, 7015, 6709, 6398, 6089, 5789, 5504, 5240, 5000, 4790, 4612, 4469, 4361, 4291, 4257, 4258, 4292, 4357, 4448, 4562, 4695, 4840, 4994, 5150, 5302, 5446, 5576, 5688, 5775, 5836, 5865, 5860, - 5820, 5743, 5628, 5476, 5288, 5066, 4814, 4535, 4233, 3913, 3580, 3241, 2902, 2569, 2248, 1945, 1668, 1421, 1210, 1041, 916, 840, 815, 843, 925, 1061, 1250, 1489, 1776, 2107, 2477, 2881, - 3312, 3764, 4229, 4700, 5168, 5627, 6069, 6484, 6868, 7211, 7509, 7755, 7945, 8075, 8142, 8145, 8082, 7954, 7763, 7511, 7202, 6840, 6431, 5982, 5500, 4993, 4468, 3936, 3405, 2884, 2383, 1909, - 1472, 1079, 738, 455, 236, 85, 7, 3, 75, 222, 444, 739, 1102, 1529, 2014, 2552, 3133, 3750, 4395, 5057, 5728, 6396, 7054, 7690, 8296, 8862, 9380, 9843, 10244, 10576, 10836, 11018, - 11121, 11143, 11083, 10942, 10723, 10428, 10061, 9628, 9134, 8586, 7991, 7356, 6691, 6004, 5302, 4594, 3889, 3194, 2517, 1865, 1244, 660, 117, -380, -829, -1228, -1576, -1873, -2118, -2314, -2464, -2569, - -2634, -2663, -2661, -2632, -2581, -2514, -2436, -2351, -2264, -2179, -2101, -2032, -1974, -1929, -1900, -1884, -1883, -1895, -1918, -1949, -1985, -2021, -2054, -2079, -2089, -2081, -2048, -1985, -1887, -1750, -1567, -1337, - -1054, -717, -323, 127, 635, 1200, 1819, 2491, 3210, 3973, 4774, 5607, 6464, 7338, 8221, 9105, 9980, 10838, 11670, 12467, 13221, 13924, 14570, 15150, 15661, 16096, 16452, 16726, 16917, 17024, 17048, 16991, - 16857, 16648, 16372, 16033, 15638, 15197, 14717, 14207, 13676, 13135, 12593, 12058, 11542, 11052, 10596, 10184, 9820, 9512, 9263, 9079, 8962, 8913, 8933, 9021, 9174, 9390, 9665, 9993, 10367, 10782, 11229, 11701, - 12188, 12682, 13173, 13654, 14114, 14546, 14942, 15294, 15596, 15842, 16027, 16147, 16199, 16182, 16095, 15938, 15714, 15424, 15072, 14663, 14202, 13696, 13150, 12573, 11972, 11355, 10729, 10104, 9486, 8883, 8302, 7749, - 7231, 6753, 6318, 5930, 5592, 5305, 5070, 4887, 4753, 4668, 4627, 4628, 4665, 4734, 4830, 4946, 5077, 5217, 5358, 5497, 5626, 5742, 5838, 5911, 5957, 5973, 5958, 5910, 5829, 5715, 5569, 5394, - 5192, 4967, 4722, 4463, 4195, 3922, 3651, 3386, 3134, 2900, 2689, 2505, 2353, 2237, 2159, 2122, 2126, 2173, 2261, 2390, 2556, 2758, 2990, 3248, 3527, 3820, 4120, 4421, 4716, 4996, 5254, 5483, - 5676, 5826, 5927, 5972, 5959, 5881, 5737, 5525, 5243, 4891, 4473, 3988, 3443, 2840, 2186, 1488, 753, -9, -792, -1585, -2378, -3161, -3925, -4658, -5352, -5997, -6583, -7105, -7553, -7921, -8205, -8400, - -8503, -8512, -8427, -8249, -7979, -7622, -7181, -6662, -6072, -5418, -4708, -3952, -3158, -2338, -1500, -655, 185, 1014, 1820, 2595, 3330, 4018, 4651, 5226, 5735, 6177, 6549, 6849, 7076, 7233, 7320, 7341, - 7299, 7199, 7047, 6848, 6608, 6336, 6037, 5718, 5388, 5052, 4717, 4390, 4075, 3779, 3504, 3255, 3035, 2844, 2685, 2556, 2458, 2388, 2344, 2323, 2322, 2334, 2357, 2385, 2412, 2433, 2443, 2436, - 2408, 2354, 2269, 2151, 1996, 1803, 1569, 1294, 979, 625, 234, -191, -648, -1131, -1636, -2156, -2687, -3222, -3753, -4275, -4781, -5265, -5719, -6139, -6519, -6853, -7139, -7373, -7552, -7675, -7741, -7751, - -7707, -7610, -7463, -7272, -7040, -6774, -6480, -6165, -5835, -5500, -5165, -4840, -4531, -4246, -3991, -3774, -3599, -3472, -3397, -3377, -3414, -3509, -3663, -3873, -4138, -4455, -4818, -5224, -5665, -6135, -6626, -7130, - -7639, -8142, -8632, -9100, -9536, -9932, -10279, -10572, -10802, -10964, -11053, -11065, -10998, -10849, -10620, -10310, -9921, -9458, -8923, -8324, -7666, -6957, -6205, -5420, -4609, -3784, -2954, -2130, -1321, -537, 211, 917, - 1570, 2164, 2693, 3149, 3530, 3831, 4050, 4186, 4239, 4210, 4101, 3917, 3660, 3337, 2954, 2517, 2033, 1510, 957, 381, -208, -804, -1399, -1984, -2554, -3101, -3621, -4107, -4557, -4966, -5333, -5657, - -5936, -6172, -6365, -6519, -6634, -6717, -6769, -6796, -6802, -6793, -6774, -6749, -6724, -6703, -6690, -6690, -6706, -6740, -6794, -6870, -6968, -7088, -7228, -7387, -7562, -7749, -7945, -8146, -8345, -8539, -8721, -8885, - -9027, -9140, -9220, -9260, -9257, -9207, -9107, -8953, -8745, -8481, -8162, -7788, -7363, -6888, -6368, -5808, -5213, -4590, -3946, -3288, -2624, -1963, -1313, -683, -81, 485, 1007, 1478, 1892, 2243, 2526, 2736, - 2872, 2931, 2913, 2818, 2649, 2407, 2098, 1726, 1296, 818, 297, -256, -835, -1429, -2029, -2624, -3205, -3762, -4287, -4770, -5204, -5581, -5896, -6141, -6314, -6412, -6431, -6371, -6233, -6018, -5729, -5370, - -4946, -4462, -3927, -3346, -2728, -2083, -1418, -743, -67, 600, 1250, 1876, 2468, 3019, 3524, 3976, 4371, 4705, 4977, 5184, 5326, 5404, 5419, 5376, 5277, 5127, 4932, 4698, 4431, 4139, 3829, 3508, - 3185, 2866, 2559, 2271, 2007, 1774, 1577, 1419, 1305, 1236, 1215, 1241, 1315, 1434, 1598, 1802, 2044, 2318, 2619, 2942, 3281, 3630, 3981, 4330, 4669, 4993, 5296, 5572, 5818, 6028, 6200, 6331, - 6420, 6465, 6466, 6425, 6342, 6220, 6063, 5874, 5658, 5418, 5161, 4892, 4617, 4341, 4069, 3807, 3559, 3332, 3127, 2949, 2800, 2681, 2595, 2540, 2516, 2522, 2554, 2609, 2683, 2771, 2867, 2965, - 3058, 3140, 3203, 3240, 3244, 3208, 3125, 2990, 2797, 2540, 2217, 1824, 1359, 822, 212, -469, -1219, -2034, -2910, -3840, -4818, -5836, -6887, -7959, -9045, -10134, -11214, -12277, -13312, -14307, -15254, -16143, - -16965, -17712, -18377, -18954, -19439, -19826, -20114, -20302, -20389, -20376, -20266, -20063, -19772, -19399, -18950, -18433, -17857, -17230, -16563, -15866, -15147, -14418, -13687, -12965, -12261, -11583, -10939, -10335, -9778, -9273, -8824, -8434, - -8105, -7837, -7629, -7482, -7391, -7355, -7367, -7425, -7521, -7650, -7805, -7980, -8167, -8360, -8551, -8735, -8906, -9057, -9184, -9282, -9348, -9379, -9373, -9329, -9248, -9130, -8976, -8790, -8573, -8331, -8068, -7788, - -7497, -7201, -6905, -6615, -6337, -6076, -5837, -5624, -5443, -5297, -5188, -5118, -5088, -5100, -5152, -5243, -5371, -5533, -5725, -5943, -6182, -6436, -6700, -6966, -7230, -7484, -7722, -7938, -8126, -8281, -8398, -8473, - -8503, -8484, -8416, -8297, -8128, -7909, -7643, -7333, -6983, -6598, -6182, -5743, -5286, -4819, -4350, -3886, -3435, -3004, -2601, -2233, -1906, -1627, -1400, -1230, -1120, -1074, -1091, -1172, -1318, -1524, -1790, -2110, - -2479, -2892, -3342, -3820, -4320, -4831, -5345, -5854, -6347, -6815, -7250, -7643, -7986, -8272, -8494, -8647, -8726, -8728, -8651, -8493, -8255, -7938, -7545, -7079, -6545, -5949, -5297, -4599, -3862, -3094, -2306, -1507, - -707, 83, 855, 1599, 2307, 2969, 3578, 4127, 4611, 5024, 5363, 5625, 5809, 5914, 5942, 5893, 5772, 5582, 5328, 5016, 4652, 4244, 3800, 3326, 2832, 2325, 1814, 1306, 809, 330, -123, -546, - -934, -1282, -1587, -1846, -2057, -2221, -2337, -2406, -2431, -2414, -2359, -2269, -2150, -2006, -1842, -1663, -1476, -1284, -1095, -911, -737, -577, -435, -312, -211, -133, -77, -44, -32, -39, -62, -98, - -142, -191, -239, -281, -312, -327, -320, -285, -218, -115, 27, 215, 448, 730, 1060, 1440, 1868, 2341, 2859, 3416, 4008, 4629, 5274, 5934, 6604, 7275, 7938, 8585, 9208, 9797, 10346, 10845, - 11288, 11667, 11976, 12211, 12367, 12441, 12430, 12334, 12153, 11889, 11543, 11119, 10623, 10061, 9438, 8763, 8044, 7290, 6510, 5715, 4914, 4118, 3335, 2577, 1852, 1168, 533, -44, -559, -1007, -1383, -1685, - -1911, -2060, -2133, -2133, -2062, -1924, -1725, -1470, -1168, -824, -448, -47, 369, 792, 1214, 1624, 2017, 2383, 2715, 3007, 3254, 3451, 3595, 3682, 3711, 3682, 3596, 3454, 3258, 3014, 2725, 2397, - 2036, 1649, 1242, 824, 403, -14, -420, -806, -1167, -1494, -1782, -2025, -2219, -2360, -2446, -2473, -2441, -2350, -2201, -1996, -1737, -1428, -1074, -679, -248, 211, 694, 1194, 1704, 2218, 2729, 3232, - 3721, 4190, 4636, 5054, 5442, 5796, 6116, 6400, 6650, 6865, 7048, 7202, 7329, 7433, 7519, 7590, 7653, 7713, 7774, 7841, 7921, 8016, 8133, 8274, 8442, 8640, 8871, 9133, 9428, 9755, 10111, 10495, - 10902, 11328, 11768, 12216, 12666, 13111, 13543, 13955, 14340, 14689, 14997, 15254, 15456, 15594, 15666, 15664, 15587, 15431, 15194, 14877, 14478, 14001, 13448, 12824, 12132, 11381, 10575, 9724, 8836, 7921, 6988, 6048, - 5110, 4186, 3284, 2417, 1592, 819, 107, -537, -1108, -1599, -2006, -2327, -2558, -2700, -2753, -2719, -2601, -2404, -2133, -1794, -1394, -942, -446, 84, 640, 1211, 1789, 2362, 2921, 3459, 3966, 4434, - 4857, 5228, 5543, 5796, 5986, 6110, 6168, 6159, 6086, 5950, 5755, 5505, 5206, 4863, 4482, 4070, 3635, 3183, 2722, 2259, 1801, 1354, 926, 521, 145, -198, -506, -775, -1003, -1190, -1337, -1443, - -1512, -1544, -1545, -1517, -1465, -1394, -1310, -1218, -1123, -1031, -946, -876, -823, -792, -786, -810, -864, -950, -1070, -1221, -1405, -1618, -1858, -2121, -2403, -2701, -3007, -3316, -3623, -3921, -4204, -4466, - -4699, -4900, -5061, -5178, -5248, -5266, -5229, -5136, -4986, -4779, -4515, -4197, -3828, -3410, -2950, -2453, -1924, -1370, -799, -218, 363, 939, 1502, 2042, 2552, 3026, 3458, 3840, 4168, 4438, 4647, 4793, - 4874, 4891, 4845, 4738, 4573, 4355, 4089, 3782, 3440, 3070, 2683, 2285, 1885, 1493, 1118, 769, 453, 179, -45, -215, -323, -365, -337, -238, -64, 182, 502, 893, 1350, 1871, 2448, 3076, - 3746, 4451, 5182, 5930, 6685, 7436, 8175, 8891, 9575, 10217, 10810, 11344, 11814, 12213, 12536, 12778, 12937, 13011, 12999, 12902, 12721, 12459, 12121, 11710, 11232, 10695, 10105, 9469, 8796, 8094, 7371, 6637, - 5898, 5164, 4441, 3737, 3058, 2410, 1798, 1225, 695, 211, -226, -617, -962, -1262, -1519, -1736, -1917, -2066, -2186, -2283, -2363, -2429, -2487, -2542, -2598, -2660, -2732, -2816, -2915, -3031, -3165, -3317, - -3488, -3675, -3878, -4092, -4315, -4543, -4771, -4995, -5209, -5408, -5586, -5738, -5858, -5941, -5984, -5980, -5928, -5823, -5664, -5449, -5179, -4853, -4474, -4043, -3564, -3041, -2481, -1888, -1270, -633, 13, 661, - 1303, 1930, 2532, 3102, 3631, 4111, 4535, 4897, 5190, 5410, 5553, 5616, 5598, 5497, 5315, 5053, 4715, 4304, 3826, 3287, 2694, 2055, 1377, 672, -53, -788, -1522, -2247, -2953, -3631, -4272, -4867, - -5412, -5898, -6320, -6676, -6960, -7172, -7311, -7378, -7373, -7301, -7164, -6968, -6720, -6424, -6090, -5725, -5337, -4936, -4530, -4127, -3738, -3370, -3032, -2730, -2472, -2263, -2108, -2012, -1978, -2007, -2101, -2258, - -2479, -2759, -3097, -3486, -3923, -4402, -4914, -5454, -6014, -6586, -7162, -7735, -8295, -8836, -9352, -9834, -10277, -10677, -11028, -11327, -11571, -11759, -11891, -11965, -11984, -11949, -11864, -11732, -11558, -11346, -11101, -10830, - -10539, -10234, -9920, -9605, -9293, -8990, -8701, -8431, -8183, -7961, -7766, -7600, -7464, -7358, -7280, -7229, -7203, -7197, -7209, -7234, -7266, -7301, -7334, -7358, -7369, -7361, -7330, -7269, -7176, -7046, -6878, -6667, - -6414, -6117, -5778, -5397, -4977, -4520, -4032, -3516, -2978, -2424, -1861, -1297, -737, -191, 334, 831, 1293, 1713, 2083, 2399, 2655, 2848, 2972, 3028, 3013, 2927, 2771, 2547, 2260, 1913, 1511, 1062, - 572, 49, -496, -1056, -1621, -2180, -2724, -3243, -3727, -4166, -4552, -4877, -5133, -5314, -5414, -5428, -5355, -5192, -4937, -4593, -4159, -3641, -3042, -2366, -1622, -816, 43, 948, 1889, 2855, 3836, 4823, - 5805, 6772, 7715, 8625, 9493, 10311, 11074, 11774, 12407, 12969, 13457, 13870, 14208, 14471, 14660, 14779, 14831, 14821, 14754, 14635, 14472, 14270, 14038, 13781, 13508, 13226, 12939, 12657, 12383, 12123, 11882, 11662, - 11468, 11302, 11163, 11053, 10971, 10915, 10883, 10873, 10880, 10901, 10930, 10964, 10995, 11021, 11034, 11030, 11003, 10950, 10865, 10746, 10590, 10394, 10156, 9877, 9556, 9194, 8794, 8357, 7889, 7392, 6872, 6334, - 5784, 5229, 4674, 4128, 3596, 3085, 2602, 2152, 1741, 1374, 1056, 789, 576, 420, 320, 277, 290, 356, 473, 636, 840, 1081, 1352, 1647, 1957, 2276, 2596, 2910, 3209, 3487, 3736, 3951, - 4125, 4253, 4330, 4355, 4324, 4235, 4090, 3888, 3633, 3326, 2972, 2576, 2144, 1683, 1201, 705, 204, -292, -776, -1238, -1669, -2061, -2405, -2694, -2922, -3081, -3168, -3177, -3107, -2955, -2721, -2405, - -2010, -1538, -995, -384, 286, 1010, 1780, 2585, 3417, 4266, 5123, 5975, 6815, 7631, 8415, 9156, 9847, 10480, 11048, 11545, 11967, 12309, 12569, 12745, 12838, 12848, 12777, 12628, 12406, 12115, 11762, 11352, - 10893, 10393, 9859, 9300, 8723, 8137, 7549, 6968, 6399, 5850, 5326, 4833, 4373, 3951, 3569, 3229, 2930, 2673, 2456, 2278, 2135, 2024, 1942, 1883, 1843, 1817, 1800, 1787, 1772, 1751, 1719, 1673, - 1609, 1524, 1416, 1283, 1126, 944, 737, 509, 261, -2, -279, -565, -854, -1141, -1421, -1688, -1936, -2160, -2354, -2514, -2634, -2711, -2742, -2722, -2652, -2529, -2354, -2127, -1851, -1528, -1162, -758, - -320, 143, 627, 1123, 1624, 2121, 2606, 3071, 3506, 3905, 4259, 4561, 4805, 4984, 5093, 5129, 5089, 4971, 4773, 4497, 4144, 3717, 3219, 2656, 2033, 1358, 638, -117, -900, -1702, -2511, -3319, - -4115, -4891, -5635, -6340, -6998, -7600, -8141, -8614, -9014, -9339, -9586, -9754, -9842, -9853, -9788, -9651, -9446, -9180, -8859, -8490, -8081, -7641, -7177, -6700, -6219, -5741, -5277, -4834, -4421, -4044, -3710, -3425, - -3194, -3019, -2904, -2850, -2858, -2927, -3055, -3239, -3476, -3761, -4089, -4453, -4847, -5264, -5696, -6136, -6576, -7008, -7426, -7821, -8189, -8522, -8816, -9065, -9268, -9420, -9520, -9567, -9562, -9505, -9398, -9245, - -9049, -8814, -8545, -8248, -7928, -7592, -7246, -6897, -6550, -6212, -5888, -5584, -5304, -5054, -4835, -4652, -4505, -4396, -4326, -4292, -4295, -4332, -4400, -4495, -4613, -4749, -4899, -5057, -5217, -5374, -5522, -5657, - -5772, -5865, -5929, -5963, -5964, -5930, -5859, -5752, -5609, -5432, -5224, -4988, -4728, -4449, -4156, -3857, -3557, -3264, -2985, -2727, -2497, -2302, -2150, -2047, -1998, -2008, -2081, -2222, -2431, -2711, -3062, -3482, - -3970, -4522, -5133, -5799, -6513, -7268, -8055, -8866, -9691, -10520, -11343, -12149, -12929, -13672, -14368, -15009, -15584, -16087, -16509, -16845, -17091, -17241, -17293, -17246, -17100, -16855, -16515, -16082, -15562, -14960, -14284, -13541, - -12739, -11888, -10996, -10075, -9135, -8184, -7234, -6294, -5373, -4480, -3623, -2809, -2045, -1336, -686, -99, 423, 880, 1271, 1598, 1863, 2068, 2218, 2318, 2372, 2388, 2371, 2328, 2265, 2191, 2110, 2031, - 1959, 1900, 1858, 1838, 1845, 1880, 1946, 2044, 2174, 2335, 2526, 2745, 2988, 3252, 3533, 3824, 4123, 4422, 4715, 4999, 5265, 5510, 5728, 5914, 6064, 6174, 6242, 6265, 6242, 6173, 6057, 5897, - 5694, 5450, 5171, 4859, 4521, 4160, 3785, 3399, 3011, 2626, 2251, 1893, 1556, 1247, 971, 731, 533, 378, 268, 205, 188, 218, 291, 406, 558, 743, 957, 1193, 1444, 1704, 1965, 2220, - 2461, 2681, 2872, 3027, 3141, 3206, 3219, 3173, 3066, 2896, 2661, 2360, 1994, 1565, 1076, 532, -63, -704, -1382, -2092, -2824, -3569, -4319, -5064, -5795, -6503, -7178, -7811, -8395, -8923, -9386, -9780, - -10099, -10340, -10501, -10579, -10574, -10488, -10323, -10082, -9770, -9392, -8954, -8465, -7933, -7366, -6773, -6164, -5550, -4939, -4341, -3766, -3223, -2720, -2265, -1865, -1525, -1251, -1047, -915, -858, -875, -966, -1129, - -1361, -1659, -2017, -2430, -2892, -3395, -3933, -4496, -5077, -5668, -6260, -6846, -7417, -7968, -8490, -8979, -9428, -9834, -10193, -10501, -10759, -10963, -11116, -11217, -11269, -11274, -11236, -11158, -11046, -10903, -10735, -10548, - -10346, -10136, -9922, -9709, -9501, -9304, -9119, -8950, -8798, -8665, -8552, -8458, -8382, -8322, -8275, -8239, -8210, -8182, -8153, -8115, -8065, -7997, -7905, -7784, -7631, -7439, -7205, -6926, -6600, -6224, -5797, -5319, - -4791, -4215, -3594, -2931, -2231, -1499, -742, 33, 821, 1611, 2396, 3168, 3917, 4635, 5313, 5944, 6520, 7033, 7478, 7849, 8142, 8352, 8478, 8518, 8472, 8341, 8128, 7835, 7467, 7030, 6531, 5976, - 5375, 4735, 4066, 3378, 2681, 1984, 1298, 631, -5, -604, -1157, -1657, -2098, -2475, -2783, -3019, -3182, -3270, -3286, -3229, -3104, -2913, -2663, -2359, -2008, -1617, -1195, -749, -289, 176, 639, 1091, - 1522, 1926, 2295, 2622, 2902, 3130, 3302, 3415, 3468, 3460, 3391, 3264, 3080, 2843, 2558, 2229, 1864, 1469, 1051, 617, 175, -266, -700, -1119, -1515, -1882, -2212, -2502, -2745, -2938, -3078, -3162, - -3189, -3159, -3073, -2931, -2737, -2494, -2206, -1878, -1514, -1122, -706, -273, 168, 615, 1060, 1496, 1918, 2321, 2701, 3053, 3374, 3663, 3916, 4134, 4317, 4465, 4581, 4666, 4724, 4759, 4775, 4776, - 4767, 4755, 4743, 4738, 4745, 4767, 4811, 4879, 4975, 5101, 5261, 5454, 5680, 5940, 6232, 6553, 6899, 7267, 7652, 8048, 8449, 8847, 9237, 9610, 9960, 10277, 10556, 10789, 10969, 11090, 11146, 11133, - 11047, 10886, 10646, 10328, 9931, 9458, 8911, 8294, 7612, 6870, 6076, 5238, 4364, 3463, 2545, 1620, 698, -208, -1092, -1941, -2747, -3499, -4191, -4813, -5360, -5825, -6205, -6496, -6696, -6803, -6819, -6745, - -6584, -6341, -6019, -5626, -5169, -4655, -4094, -3493, -2864, -2215, -1556, -898, -249, 380, 983, 1551, 2076, 2553, 2975, 3339, 3641, 3879, 4051, 4159, 4202, 4184, 4107, 3975, 3794, 3568, 3304, 3008, - 2688, 2350, 2003, 1652, 1305, 969, 650, 353, 84, -152, -354, -518, -642, -726, -769, -772, -738, -668, -566, -435, -281, -108, 77, 272, 469, 664, 849, 1021, 1174, 1305, 1409, 1483, - 1524, 1532, 1506, 1444, 1349, 1221, 1064, 881, 675, 451, 215, -27, -271, -510, -738, -947, -1133, -1289, -1410, -1491, -1527, -1515, -1452, -1335, -1164, -939, -660, -329, 51, 477, 944, 1447, - 1980, 2536, 3107, 3688, 4269, 4842, 5400, 5935, 6439, 6905, 7327, 7698, 8014, 8269, 8462, 8589, 8649, 8642, 8569, 8432, 8235, 7982, 7677, 7327, 6938, 6519, 6078, 5623, 5163, 4707, 4264, 3844, - 3453, 3102, 2797, 2544, 2351, 2221, 2160, 2169, 2251, 2406, 2633, 2930, 3295, 3722, 4208, 4744, 5325, 5942, 6587, 7250, 7922, 8593, 9254, 9894, 10504, 11075, 11599, 12067, 12472, 12809, 13071, 13254, - 13357, 13375, 13309, 13160, 12928, 12616, 12228, 11769, 11244, 10659, 10023, 9342, 8624, 7878, 7112, 6335, 5555, 4781, 4019, 3278, 2563, 1882, 1238, 636, 80, -427, -885, -1293, -1651, -1960, -2222, -2441, - -2618, -2758, -2866, -2946, -3003, -3042, -3068, -3086, -3100, -3115, -3135, -3163, -3202, -3253, -3318, -3397, -3490, -3596, -3714, -3839, -3971, -4104, -4234, -4356, -4466, -4557, -4625, -4663, -4667, -4632, -4552, -4423, - -4242, -4006, -3712, -3359, -2946, -2474, -1945, -1360, -723, -38, 688, 1452, 2247, 3063, 3895, 4733, 5569, 6394, 7199, 7976, 8716, 9411, 10054, 10637, 11156, 11604, 11977, 12273, 12489, 12625, 12680, 12657, - 12557, 12385, 12146, 11845, 11489, 11086, 10644, 10172, 9679, 9174, 8667, 8168, 7686, 7231, 6809, 6431, 6102, 5829, 5617, 5471, 5393, 5386, 5451, 5587, 5792, 6064, 6398, 6790, 7235, 7724, 8252, 8809, - 9388, 9979, 10573, 11162, 11736, 12286, 12806, 13286, 13720, 14102, 14425, 14687, 14884, 15013, 15073, 15064, 14987, 14845, 14640, 14376, 14059, 13695, 13289, 12848, 12381, 11895, 11397, 10896, 10400, 9915, 9449, 9009, - 8600, 8228, 7897, 7612, 7374, 7186, 7049, 6962, 6924, 6934, 6988, 7084, 7217, 7381, 7572, 7785, 8012, 8248, 8486, 8722, 8948, 9159, 9350, 9518, 9657, 9764, 9838, 9876, 9879, 9845, 9775, 9673, - 9539, 9377, 9190, 8984, 8763, 8532, 8297, 8062, 7835, 7619, 7420, 7244, 7094, 6975, 6889, 6839, 6826, 6851, 6915, 7015, 7151, 7319, 7515, 7735, 7975, 8226, 8485, 8742, 8992, 9227, 9439, 9620, - 9764, 9864, 9914, 9906, 9837, 9702, 9498, 9223, 8874, 8453, 7959, 7396, 6766, 6073, 5324, 4525, 3682, 2804, 1900, 980, 52, -872, -1784, -2672, -3528, -4340, -5101, -5801, -6434, -6991, -7468, -7859, - -8162, -8373, -8492, -8519, -8455, -8304, -8068, -7754, -7366, -6913, -6402, -5841, -5241, -4610, -3958, -3296, -2633, -1980, -1346, -740, -170, 355, 829, 1247, 1602, 1891, 2112, 2261, 2340, 2348, 2287, 2159, - 1969, 1720, 1418, 1069, 678, 253, -198, -670, -1155, -1646, -2135, -2618, -3087, -3538, -3965, -4365, -4735, -5073, -5377, -5647, -5883, -6086, -6259, -6405, -6526, -6628, -6714, -6789, -6860, -6930, -7005, -7092, - -7194, -7316, -7463, -7638, -7845, -8086, -8363, -8676, -9025, -9410, -9829, -10279, -10757, -11259, -11781, -12316, -12860, -13405, -13946, -14476, -14988, -15475, -15932, -16352, -16730, -17061, -17340, -17563, -17729, -17835, -17881, -17866, - -17791, -17659, -17472, -17235, -16952, -16628, -16270, -15884, -15478, -15059, -14634, -14212, -13800, -13406, -13037, -12699, -12399, -12142, -11932, -11773, -11668, -11618, -11624, -11685, -11799, -11964, -12176, -12430, -12720, -13039, -13382, -13738, - -14101, -14461, -14810, -15138, -15437, -15699, -15914, -16076, -16178, -16213, -16176, -16064, -15872, -15600, -15247, -14812, -14298, -13708, -13046, -12316, -11526, -10683, -9794, -8868, -7915, -6944, -5966, -4991, -4028, -3088, -2181, -1316, - -501, 255, 946, 1566, 2109, 2571, 2950, 3244, 3452, 3575, 3615, 3575, 3458, 3270, 3016, 2702, 2336, 1925, 1476, 999, 501, -9, -525, -1038, -1541, -2028, -2493, -2930, -3336, -3706, -4039, -4332, - -4584, -4797, -4971, -5107, -5209, -5279, -5322, -5341, -5342, -5329, -5308, -5283, -5260, -5243, -5237, -5246, -5273, -5322, -5393, -5490, -5612, -5760, -5933, -6128, -6345, -6578, -6826, -7083, -7345, -7606, -7862, -8105, - -8331, -8533, -8707, -8847, -8948, -9006, -9017, -8979, -8890, -8747, -8552, -8304, -8005, -7658, -7266, -6834, -6367, -5870, -5352, -4818, -4276, -3736, -3204, -2690, -2202, -1748, -1335, -971, -663, -417, -238, -129, - -94, -136, -255, -451, -722, -1066, -1479, -1957, -2494, -3082, -3715, -4383, -5079, -5792, -6514, -7233, -7941, -8628, -9283, -9899, -10466, -10978, -11427, -11807, -12114, -12343, -12493, -12561, -12549, -12456, -12284, -12038, - -11722, -11341, -10902, -10412, -9878, -9309, -8713, -8101, -7481, -6861, -6251, -5660, -5095, -4564, -4074, -3629, -3236, -2899, -2619, -2399, -2240, -2141, -2101, -2117, -2186, -2304, -2464, -2662, -2890, -3143, -3412, -3690, - -3969, -4242, -4502, -4741, -4954, -5133, -5275, -5374, -5426, -5430, -5382, -5282, -5131, -4929, -4677, -4380, -4039, -3661, -3249, -2808, -2346, -1868, -1380, -889, -401, 76, 539, 981, 1397, 1784, 2136, 2451, - 2727, 2964, 3159, 3314, 3430, 3508, 3553, 3566, 3552, 3516, 3463, 3397, 3325, 3251, 3182, 3122, 3076, 3050, 3047, 3070, 3122, 3206, 3323, 3472, 3653, 3865, 4105, 4370, 4656, 4958, 5271, 5588, - 5903, 6209, 6498, 6764, 6999, 7196, 7348, 7449, 7493, 7474, 7389, 7234, 7006, 6704, 6328, 5878, 5356, 4766, 4111, 3397, 2630, 1818, 967, 88, -809, -1717, -2625, -3521, -4396, -5239, -6040, -6790, - -7480, -8102, -8647, -9111, -9486, -9770, -9959, -10051, -10047, -9946, -9750, -9464, -9091, -8638, -8110, -7515, -6861, -6158, -5415, -4641, -3846, -3041, -2235, -1438, -660, 91, 807, 1481, 2106, 2677, 3189, 3639, - 4025, 4345, 4600, 4791, 4919, 4987, 5001, 4963, 4881, 4759, 4604, 4423, 4223, 4011, 3793, 3576, 3368, 3172, 2996, 2843, 2717, 2622, 2559, 2531, 2537, 2577, 2650, 2755, 2887, 3044, 3221, 3413, - 3617, 3825, 4033, 4234, 4424, 4597, 4747, 4869, 4961, 5017, 5035, 5013, 4948, 4842, 4693, 4503, 4273, 4008, 3709, 3382, 3031, 2662, 2280, 1892, 1504, 1123, 755, 406, 82, -210, -466, -682, - -853, -978, -1052, -1076, -1048, -970, -844, -671, -455, -201, 85, 400, 735, 1085, 1442, 1798, 2145, 2477, 2785, 3063, 3303, 3501, 3649, 3744, 3782, 3761, 3677, 3531, 3323, 3055, 2729, 2349, - 1920, 1447, 937, 397, -163, -737, -1316, -1890, -2449, -2985, -3488, -3950, -4363, -4719, -5011, -5234, -5381, -5451, -5439, -5344, -5167, -4907, -4567, -4150, -3661, -3105, -2489, -1819, -1105, -356, 420, 1214, - 2014, 2812, 3597, 4360, 5091, 5782, 6426, 7013, 7540, 7999, 8388, 8702, 8939, 9100, 9184, 9192, 9128, 8994, 8796, 8538, 8228, 7871, 7475, 7048, 6598, 6132, 5660, 5188, 4725, 4278, 3853, 3457, - 3095, 2771, 2488, 2251, 2060, 1917, 1822, 1773, 1768, 1807, 1884, 1996, 2139, 2309, 2498, 2704, 2919, 3138, 3358, 3571, 3774, 3963, 4135, 4285, 4413, 4517, 4596, 4650, 4680, 4689, 4678, 4650, - 4609, 4560, 4508, 4456, 4411, 4378, 4361, 4368, 4401, 4467, 4568, 4709, 4893, 5121, 5395, 5714, 6080, 6489, 6939, 7428, 7950, 8501, 9074, 9663, 10261, 10860, 11451, 12026, 12577, 13095, 13572, 13999, - 14369, 14675, 14911, 15070, 15149, 15144, 15052, 14872, 14603, 14247, 13806, 13282, 12681, 12008, 11269, 10472, 9625, 8736, 7815, 6873, 5918, 4961, 4012, 3081, 2176, 1308, 484, -287, -1001, -1651, -2233, -2743, - -3179, -3541, -3827, -4041, -4183, -4259, -4272, -4228, -4134, -3998, -3825, -3626, -3408, -3181, -2952, -2730, -2525, -2342, -2191, -2077, -2005, -1982, -2011, -2094, -2235, -2432, -2687, -2997, -3360, -3773, -4231, -4728, - -5260, -5820, -6399, -6991, -7588, -8182, -8765, -9329, -9867, -10373, -10839, -11260, -11630, -11946, -12204, -12402, -12538, -12612, -12623, -12574, -12467, -12305, -12092, -11832, -11531, -11195, -10828, -10438, -10032, -9615, -9193, -8774, - -8362, -7964, -7582, -7223, -6888, -6581, -6304, -6056, -5839, -5652, -5494, -5361, -5251, -5160, -5085, -5021, -4963, -4905, -4843, -4771, -4684, -4578, -4448, -4289, -4100, -3876, -3616, -3319, -2984, -2612, -2205, -1765, - -1294, -799, -282, 249, 790, 1333, 1872, 2398, 2903, 3382, 3825, 4225, 4577, 4872, 5106, 5273, 5369, 5392, 5338, 5207, 4999, 4715, 4358, 3930, 3437, 2884, 2278, 1626, 936, 219, -517, -1262, - -2007, -2739, -3450, -4129, -4767, -5354, -5883, -6344, -6733, -7042, -7267, -7405, -7454, -7412, -7279, -7059, -6752, -6363, -5897, -5360, -4760, -4104, -3400, -2658, -1888, -1098, -299, 498, 1286, 2054, 2793, 3496, - 4156, 4764, 5317, 5809, 6236, 6596, 6889, 7113, 7269, 7360, 7388, 7357, 7272, 7138, 6961, 6748, 6506, 6241, 5962, 5674, 5386, 5103, 4833, 4580, 4351, 4149, 3979, 3843, 3742, 3679, 3653, 3663, - 3709, 3787, 3895, 4028, 4183, 4355, 4538, 4727, 4916, 5101, 5275, 5433, 5570, 5683, 5767, 5819, 5836, 5817, 5761, 5668, 5537, 5372, 5175, 4948, 4695, 4421, 4132, 3833, 3529, 3227, 2934, 2655, - 2398, 2167, 1969, 1809, 1691, 1619, 1597, 1626, 1709, 1845, 2033, 2274, 2563, 2898, 3275, 3688, 4131, 4599, 5083, 5577, 6073, 6563, 7039, 7494, 7919, 8307, 8653, 8949, 9191, 9375, 9496, 9552, - 9542, 9466, 9325, 9120, 8854, 8532, 8158, 7739, 7281, 6792, 6281, 5754, 5223, 4694, 4179, 3685, 3221, 2796, 2417, 2091, 1825, 1623, 1490, 1428, 1441, 1528, 1689, 1922, 2226, 2594, 3023, 3507, - 4038, 4608, 5210, 5833, 6469, 7108, 7739, 8354, 8942, 9495, 10003, 10459, 10855, 11185, 11443, 11625, 11727, 11748, 11686, 11541, 11314, 11009, 10628, 10175, 9657, 9080, 8450, 7775, 7062, 6321, 5559, 4786, - 4008, 3235, 2474, 1733, 1018, 335, -309, -913, -1471, -1981, -2441, -2851, -3211, -3522, -3786, -4004, -4182, -4321, -4428, -4506, -4560, -4596, -4619, -4633, -4643, -4655, -4671, -4695, -4730, -4778, -4841, -4920, - -5013, -5122, -5243, -5374, -5513, -5656, -5799, -5936, -6063, -6175, -6266, -6331, -6364, -6361, -6316, -6225, -6084, -5891, -5642, -5337, -4975, -4555, -4080, -3551, -2972, -2346, -1679, -976, -245, 507, 1274, 2047, - 2817, 3575, 4312, 5020, 5690, 6315, 6886, 7396, 7840, 8212, 8507, 8723, 8857, 8908, 8876, 8762, 8569, 8301, 7961, 7557, 7094, 6579, 6022, 5430, 4813, 4181, 3543, 2909, 2287, 1689, 1121, 592, - 110, -318, -689, -996, -1237, -1409, -1510, -1542, -1506, -1403, -1238, -1015, -739, -418, -58, 331, 744, 1171, 1602, 2029, 2443, 2835, 3197, 3522, 3802, 4031, 4204, 4316, 4363, 4344, 4257, 4102, - 3880, 3592, 3243, 2836, 2375, 1867, 1318, 735, 126, -502, -1141, -1783, -2420, -3044, -3648, -4225, -4769, -5275, -5737, -6151, -6515, -6827, -7085, -7288, -7439, -7539, -7589, -7593, -7556, -7482, -7377, -7244, - -7092, -6925, -6750, -6573, -6399, -6234, -6084, -5952, -5842, -5759, -5704, -5679, -5685, -5722, -5790, -5886, -6009, -6155, -6320, -6501, -6693, -6889, -7086, -7277, -7457, -7621, -7762, -7876, -7960, -8007, -8016, -7984, - -7910, -7791, -7628, -7422, -7175, -6889, -6569, -6218, -5841, -5444, -5035, -4618, -4203, -3796, -3405, -3036, -2699, -2399, -2144, -1938, -1788, -1699, -1672, -1712, -1820, -1996, -2239, -2548, -2920, -3351, -3835, -4366, - -4938, -5542, -6171, -6814, -7462, -8106, -8734, -9338, -9908, -10433, -10904, -11314, -11654, -11918, -12100, -12194, -12198, -12108, -11924, -11646, -11275, -10813, -10265, -9635, -8929, -8155, -7321, -6434, -5504, -4542, -3556, -2557, - -1556, -561, 415, 1365, 2281, 3153, 3975, 4741, 5444, 6081, 6648, 7144, 7568, 7918, 8198, 8409, 8554, 8638, 8665, 8642, 8573, 8466, 8327, 8165, 7984, 7794, 7600, 7408, 7225, 7057, 6906, 6778, - 6676, 6602, 6557, 6542, 6556, 6599, 6668, 6760, 6872, 7001, 7140, 7286, 7433, 7576, 7709, 7827, 7925, 7998, 8041, 8051, 8024, 7958, 7850, 7701, 7508, 7274, 7000, 6688, 6340, 5962, 5557, 5131, - 4690, 4239, 3785, 3334, 2894, 2470, 2070, 1699, 1363, 1067, 815, 612, 460, 361, 316, 326, 390, 505, 670, 880, 1131, 1418, 1735, 2074, 2430, 2795, 3161, 3520, 3865, 4188, 4483, 4742, - 4959, 5129, 5248, 5311, 5316, 5262, 5146, 4971, 4738, 4449, 4108, 3720, 3290, 2825, 2333, 1820, 1296, 770, 250, -254, -735, -1183, -1589, -1946, -2247, -2484, -2654, -2751, -2771, -2714, -2576, -2360, - -2065, -1695, -1254, -745, -176, 446, 1115, 1822, 2558, 3313, 4076, 4839, 5591, 6322, 7022, 7682, 8294, 8849, 9341, 9763, 10110, 10378, 10565, 10667, 10685, 10620, 10472, 10244, 9941, 9568, 9129, 8631, - 8081, 7487, 6857, 6198, 5520, 4829, 4135, 3445, 2765, 2104, 1467, 859, 286, -249, -744, -1197, -1606, -1972, -2295, -2577, -2820, -3027, -3203, -3350, -3474, -3580, -3673, -3758, -3839, -3922, -4010, -4109, - -4222, -4352, -4501, -4670, -4861, -5074, -5308, -5562, -5833, -6118, -6414, -6716, -7021, -7322, -7614, -7892, -8149, -8381, -8582, -8746, -8868, -8945, -8973, -8948, -8868, -8733, -8541, -8293, -7991, -7637, -7235, -6788, - -6302, -5783, -5237, -4673, -4096, -3517, -2942, -2381, -1841, -1332, -861, -435, -62, 251, 501, 683, 793, 828, 788, 672, 481, 218, -113, -509, -965, -1474, -2027, -2618, -3237, -3875, -4523, -5171, - -5810, -6428, -7018, -7570, -8076, -8527, -8918, -9242, -9494, -9670, -9767, -9785, -9722, -9580, -9360, -9066, -8702, -8274, -7788, -7251, -6670, -6054, -5413, -4754, -4087, -3422, -2767, -2130, -1521, -947, -414, 70, - 502, 877, 1192, 1445, 1634, 1760, 1825, 1829, 1778, 1674, 1523, 1330, 1102, 846, 569, 279, -17, -312, -598, -868, -1116, -1335, -1520, -1666, -1770, -1828, -1839, -1801, -1714, -1579, -1398, -1172, - -907, -605, -272, 87, 467, 861, 1263, 1667, 2066, 2454, 2825, 3174, 3495, 3784, 4037, 4252, 4425, 4557, 4645, 4690, 4694, 4658, 4585, 4478, 4341, 4180, 3998, 3801, 3595, 3384, 3176, 2974, - 2785, 2613, 2462, 2335, 2237, 2168, 2131, 2126, 2153, 2210, 2295, 2406, 2537, 2686, 2846, 3012, 3177, 3334, 3477, 3597, 3689, 3744, 3756, 3719, 3626, 3473, 3255, 2968, 2610, 2179, 1675, 1098, - 450, -265, -1045, -1884, -2775, -3712, -4686, -5690, -6713, -7745, -8777, -9798, -10797, -11765, -12690, -13564, -14377, -15121, -15788, -16372, -16866, -17266, -17570, -17775, -17880, -17885, -17794, -17607, -17331, -16970, -16530, -16019, - -15444, -14815, -14141, -13432, -12697, -11947, -11191, -10440, -9702, -8986, -8301, -7654, -7052, -6500, -6003, -5564, -5187, -4872, -4620, -4429, -4299, -4226, -4206, -4236, -4309, -4420, -4562, -4729, -4915, -5111, -5312, -5510, - -5699, -5874, -6029, -6159, -6260, -6329, -6363, -6362, -6324, -6250, -6141, -5999, -5826, -5627, -5404, -5164, -4911, -4650, -4387, -4128, -3879, -3645, -3431, -3243, -3084, -2960, -2872, -2823, -2816, -2851, -2927, -3045, - -3201, -3395, -3623, -3879, -4161, -4462, -4777, -5100, -5425, -5744, -6053, -6344, -6611, -6849, -7052, -7217, -7339, -7414, -7442, -7420, -7349, -7227, -7058, -6844, -6587, -6293, -5965, -5611, -5236, -4847, -4451, -4057, - -3671, -3301, -2955, -2641, -2364, -2131, -1948, -1819, -1749, -1741, -1796, -1915, -2099, -2346, -2653, -3018, -3435, -3899, -4403, -4941, -5505, -6085, -6674, -7262, -7840, -8398, -8928, -9420, -9867, -10260, -10593, -10859, - -11054, -11173, -11213, -11173, -11052, -10850, -10569, -10212, -9784, -9290, -8736, -8128, -7476, -6788, -6073, -5340, -4599, -3860, -3134, -2429, -1755, -1120, -533, 0, 470, 875, 1209, 1468, 1652, 1757, 1786, 1738, - 1617, 1426, 1169, 852, 481, 63, -395, -886, -1400, -1931, -2470, -3009, -3539, -4054, -4547, -5011, -5441, -5832, -6180, -6482, -6736, -6941, -7097, -7203, -7262, -7276, -7248, -7182, -7082, -6952, -6797, -6624, - -6437, -6241, -6042, -5844, -5653, -5472, -5304, -5154, -5022, -4911, -4821, -4752, -4704, -4674, -4661, -4660, -4670, -4684, -4700, -4711, -4712, -4699, -4665, -4605, -4515, -4389, -4224, -4015, -3760, -3456, -3102, -2698, - -2243, -1739, -1189, -595, 37, 704, 1400, 2117, 2849, 3588, 4326, 5055, 5765, 6449, 7097, 7702, 8256, 8752, 9183, 9544, 9829, 10034, 10158, 10198, 10153, 10025, 9815, 9526, 9163, 8730, 8234, 7682, - 7082, 6442, 5772, 5081, 4380, 3677, 2984, 2310, 1664, 1055, 491, -18, -469, -855, -1171, -1413, -1580, -1669, -1682, -1620, -1484, -1280, -1010, -681, -299, 127, 592, 1087, 1602, 2130, 2661, 3187, - 3698, 4188, 4648, 5072, 5454, 5788, 6070, 6297, 6467, 6578, 6631, 6627, 6567, 6456, 6296, 6094, 5854, 5584, 5290, 4979, 4659, 4338, 4023, 3723, 3444, 3193, 2978, 2803, 2673, 2594, 2568, 2598, - 2686, 2832, 3036, 3296, 3610, 3975, 4388, 4843, 5335, 5860, 6410, 6980, 7562, 8151, 8739, 9321, 9891, 10444, 10973, 11477, 11949, 12389, 12793, 13161, 13492, 13787, 14047, 14274, 14471, 14642, 14789, 14919, - 15035, 15143, 15247, 15354, 15467, 15593, 15735, 15898, 16085, 16299, 16542, 16815, 17119, 17454, 17818, 18208, 18623, 19058, 19508, 19968, 20431, 20892, 21343, 21777, 22186, 22562, 22899, 23188, 23423, 23598, 23706, 23742, - 23703, 23584, 23384, 23102, 22737, 22290, 21763, 21161, 20486, 19746, 18946, 18094, 17199, 16268, 15312, 14341, 13365, 12395, 11440, 10511, 9617, 8769, 7974, 7242, 6578, 5989, 5480, 5055, 4716, 4465, 4302, 4225, - 4233, 4322, 4487, 4722, 5021, 5376, 5778, 6219, 6690, 7181, 7682, 8184, 8676, 9151, 9598, 10011, 10381, 10701, 10967, 11174, 11317, 11394, 11405, 11348, 11224, 11035, 10785, 10476, 10113, 9703, 9250, 8762, - 8245, 7707, 7154, 6595, 6037, 5485, 4948, 4430, 3937, 3474, 3046, 2654, 2301, 1989, 1717, 1486, 1295, 1140, 1020, 930, 867, 825, 800, 787, 780, 773, 762, 740, 703, 647, 568, 462, - 326, 160, -38, -269, -532, -825, -1147, -1493, -1861, -2246, -2642, -3045, -3448, -3845, -4229, -4596, -4937, -5248, -5522, -5754, -5939, -6075, -6156, -6181, -6148, -6057, -5908, -5702, -5442, -5131, -4773, -4373, - -3936, -3470, -2980, -2475, -1963, -1450, -946, -457, 6, 440, 835, 1187, 1489, 1736, 1926, 2055, 2121, 2125, 2065, 1946, 1767, 1535, 1253, 927, 564, 170, -244, -673, -1107, -1537, -1954, -2349, - -2713, -3039, -3318, -3544, -3710, -3811, -3843, -3802, -3687, -3496, -3229, -2889, -2478, -2000, -1460, -864, -218, 468, 1188, 1931, 2689, 3452, 4209, 4951, 5668, 6350, 6990, 7577, 8106, 8569, 8960, 9275, - 9509, 9662, 9730, 9714, 9615, 9434, 9176, 8845, 8445, 7982, 7463, 6897, 6289, 5649, 4985, 4305, 3618, 2931, 2253, 1591, 952, 342, -234, -771, -1266, -1716, -2120, -2475, -2782, -3042, -3258, -3430, - -3563, -3660, -3725, -3763, -3780, -3779, -3767, -3748, -3726, -3707, -3695, -3692, -3703, -3728, -3771, -3831, -3910, -4005, -4115, -4240, -4374, -4515, -4659, -4801, -4936, -5059, -5163, -5245, -5298, -5317, -5298, -5235, - -5125, -4964, -4750, -4482, -4157, -3777, -3342, -2855, -2317, -1733, -1109, -448, 241, 953, 1679, 2412, 3143, 3864, 4565, 5238, 5874, 6465, 7005, 7484, 7898, 8241, 8509, 8697, 8803, 8827, 8768, 8628, - 8408, 8111, 7744, 7310, 6818, 6273, 5684, 5060, 4410, 3743, 3070, 2399, 1741, 1104, 498, -68, -589, -1057, -1466, -1811, -2088, -2296, -2432, -2496, -2491, -2416, -2277, -2077, -1822, -1518, -1172, -793, - -387, 35, 467, 897, 1319, 1722, 2099, 2441, 2743, 2996, 3196, 3338, 3418, 3434, 3384, 3267, 3085, 2840, 2534, 2170, 1754, 1292, 789, 252, -310, -892, -1484, -2079, -2669, -3246, -3803, -4333, - -4830, -5287, -5701, -6067, -6381, -6642, -6848, -6999, -7096, -7140, -7134, -7080, -6983, -6847, -6677, -6480, -6261, -6025, -5780, -5531, -5284, -5045, -4818, -4610, -4424, -4263, -4130, -4027, -3956, -3916, -3907, -3928, - -3976, -4049, -4143, -4254, -4378, -4509, -4642, -4772, -4894, -5001, -5088, -5151, -5185, -5186, -5151, -5077, -4962, -4805, -4607, -4367, -4087, -3771, -3421, -3042, -2639, -2217, -1783, -1344, -907, -480, -70, 315, - 669, 983, 1251, 1468, 1626, 1723, 1753, 1715, 1605, 1424, 1172, 851, 462, 11, -497, -1059, -1665, -2310, -2983, -3677, -4381, -5086, -5782, -6458, -7106, -7714, -8275, -8778, -9217, -9585, -9875, -10081, - -10202, -10233, -10173, -10021, -9780, -9452, -9039, -8546, -7981, -7348, -6656, -5913, -5128, -4312, -3473, -2622, -1770, -925, -98, 700, 1464, 2184, 2852, 3462, 4009, 4488, 4896, 5231, 5491, 5677, 5790, 5832, - 5806, 5716, 5567, 5364, 5115, 4824, 4500, 4149, 3778, 3395, 3007, 2619, 2239, 1871, 1521, 1192, 889, 613, 367, 151, -34, -191, -321, -425, -508, -574, -626, -669, -708, -750, -798, -859, - -937, -1037, -1164, -1321, -1513, -1741, -2007, -2313, -2659, -3044, -3467, -3926, -4417, -4937, -5481, -6044, -6620, -7204, -7788, -8367, -8933, -9479, -10000, -10488, -10939, -11346, -11706, -12013, -12266, -12461, -12598, -12675, - -12694, -12656, -12564, -12421, -12231, -11999, -11732, -11435, -11115, -10781, -10438, -10096, -9761, -9442, -9145, -8877, -8645, -8453, -8307, -8211, -8167, -8177, -8243, -8364, -8539, -8765, -9038, -9355, -9709, -10095, -10505, -10932, - -11367, -11802, -12227, -12634, -13014, -13358, -13657, -13904, -14092, -14214, -14264, -14237, -14132, -13943, -13672, -13317, -12880, -12363, -11769, -11105, -10374, -9585, -8745, -7862, -6945, -6005, -5050, -4092, -3139, -2204, -1294, -420, - 408, 1185, 1901, 2551, 3129, 3631, 4052, 4391, 4648, 4821, 4914, 4927, 4864, 4730, 4531, 4272, 3960, 3602, 3207, 2781, 2334, 1873, 1407, 942, 486, 46, -370, -761, -1119, -1442, -1726, -1969, - -2170, -2330, -2448, -2527, -2569, -2577, -2554, -2505, -2435, -2348, -2249, -2144, -2038, -1936, -1842, -1761, -1696, -1651, -1628, -1630, -1656, -1709, -1786, -1888, -2011, -2154, -2312, -2482, -2660, -2839, -3015, -3182, - -3335, -3468, -3575, -3652, -3693, -3693, -3650, -3560, -3421, -3231, -2989, -2697, -2354, -1964, -1530, -1056, -547, -8, 552, 1129, 1714, 2299, 2876, 3436, 3971, 4473, 4933, 5343, 5698, 5991, 6216, 6368, - 6444, 6441, 6359, 6196, 5953, 5634, 5240, 4776, 4248, 3662, 3024, 2345, 1631, 892, 138, -621, -1377, -2118, -2836, -3520, -4164, -4758, -5295, -5769, -6175, -6509, -6767, -6947, -7050, -7075, -7024, -6901, - -6708, -6452, -6138, -5773, -5364, -4920, -4450, -3961, -3464, -2967, -2479, -2008, -1564, -1153, -783, -459, -188, 27, 184, 279, 311, 281, 189, 39, -167, -425, -731, -1077, -1458, -1868, -2297, -2740, - -3189, -3635, -4071, -4490, -4886, -5251, -5581, -5870, -6114, -6310, -6456, -6549, -6591, -6581, -6520, -6412, -6259, -6064, -5834, -5572, -5284, -4977, -4656, -4329, -4000, -3677, -3366, -3071, -2798, -2552, -2336, -2154, - -2008, -1899, -1828, -1795, -1799, -1838, -1911, -2012, -2139, -2287, -2452, -2627, -2808, -2989, -3164, -3328, -3476, -3603, -3704, -3777, -3817, -3822, -3791, -3723, -3619, -3479, -3305, -3101, -2870, -2616, -2345, -2063, - -1775, -1490, -1213, -952, -715, -508, -339, -214, -139, -120, -161, -266, -438, -680, -990, -1371, -1819, -2333, -2908, -3540, -4223, -4950, -5714, -6505, -7315, -8134, -8953, -9760, -10545, -11298, -12010, -12669, - -13268, -13798, -14251, -14619, -14899, -15084, -15172, -15160, -15048, -14835, -14524, -14117, -13619, -13034, -12369, -11632, -10829, -9971, -9066, -8124, -7155, -6169, -5176, -4187, -3210, -2255, -1330, -443, 398, 1189, 1925, 2601, - 3214, 3764, 4248, 4669, 5027, 5324, 5566, 5754, 5896, 5996, 6060, 6095, 6108, 6106, 6095, 6082, 6074, 6076, 6093, 6131, 6194, 6285, 6407, 6560, 6747, 6966, 7216, 7497, 7804, 8136, 8487, 8853, - 9230, 9611, 9990, 10364, 10724, 11067, 11386, 11676, 11934, 12154, 12334, 12470, 12562, 12607, 12607, 12561, 12471, 12340, 12170, 11966, 11732, 11473, 11196, 10905, 10607, 10309, 10017, 9738, 9477, 9241, 9034, 8861, - 8727, 8635, 8587, 8585, 8629, 8719, 8854, 9032, 9249, 9502, 9786, 10095, 10422, 10762, 11107, 11450, 11783, 12099, 12389, 12648, 12867, 13041, 13163, 13230, 13237, 13181, 13059, 12871, 12617, 12297, 11915, 11473, - 10976, 10430, 9839, 9213, 8558, 7883, 7197, 6509, 5829, 5165, 4527, 3923, 3364, 2855, 2405, 2020, 1705, 1464, 1302, 1219, 1218, 1297, 1456, 1691, 1998, 2373, 2810, 3301, 3838, 4415, 5020, 5645, - 6281, 6917, 7543, 8151, 8730, 9272, 9769, 10213, 10598, 10918, 11169, 11347, 11450, 11477, 11427, 11302, 11104, 10836, 10502, 10108, 9660, 9165, 8628, 8059, 7466, 6856, 6237, 5619, 5007, 4411, 3837, 3292, - 2781, 2308, 1879, 1497, 1163, 879, 645, 462, 327, 238, 193, 188, 219, 281, 368, 477, 601, 735, 874, 1013, 1147, 1272, 1384, 1480, 1558, 1615, 1652, 1667, 1662, 1638, 1597, 1541, - 1475, 1403, 1329, 1257, 1194, 1144, 1112, 1105, 1127, 1182, 1276, 1411, 1592, 1819, 2095, 2421, 2796, 3219, 3688, 4199, 4749, 5333, 5945, 6579, 7227, 7882, 8536, 9180, 9805, 10404, 10967, 11486, - 11954, 12363, 12706, 12977, 13172, 13285, 13315, 13258, 13115, 12886, 12572, 12175, 11701, 11154, 10539, 9865, 9138, 8367, 7561, 6730, 5883, 5031, 4183, 3350, 2540, 1763, 1027, 340, -290, -860, -1364, -1797, - -2158, -2445, -2657, -2797, -2865, -2866, -2804, -2683, -2511, -2295, -2041, -1759, -1456, -1142, -825, -514, -217, 56, 300, 507, 671, 787, 849, 855, 803, 691, 519, 288, 0, -341, -733, -1171, - -1647, -2157, -2693, -3247, -3813, -4382, -4947, -5499, -6032, -6538, -7010, -7443, -7831, -8169, -8454, -8683, -8854, -8967, -9021, -9017, -8958, -8845, -8684, -8477, -8231, -7950, -7641, -7309, -6961, -6604, -6243, -5885, - -5536, -5202, -4886, -4594, -4329, -4093, -3889, -3719, -3582, -3478, -3406, -3364, -3349, -3357, -3386, -3429, -3483, -3542, -3601, -3655, -3698, -3726, -3733, -3716, -3671, -3594, -3484, -3339, -3158, -2941, -2689, -2404, - -2090, -1749, -1386, -1007, -618, -224, 166, 548, 911, 1250, 1556, 1822, 2042, 2208, 2314, 2356, 2330, 2230, 2056, 1806, 1479, 1076, 599, 51, -561, -1236, -1966, -2743, -3560, -4406, -5274, -6154, - -7034, -7904, -8755, -9577, -10358, -11090, -11765, -12373, -12908, -13363, -13734, -14016, -14206, -14304, -14308, -14219, -14041, -13776, -13428, -13004, -12510, -11954, -11343, -10688, -9996, -9278, -8543, -7802, -7064, -6338, -5633, -4958, - -4321, -3728, -3185, -2698, -2269, -1903, -1601, -1364, -1191, -1080, -1030, -1036, -1095, -1200, -1348, -1530, -1741, -1973, -2219, -2473, -2726, -2972, -3205, -3418, -3607, -3766, -3892, -3982, -4032, -4043, -4013, -3942, - -3833, -3687, -3507, -3297, -3061, -2803, -2528, -2243, -1952, -1662, -1377, -1104, -847, -610, -399, -217, -66, 50, 132, 179, 190, 168, 113, 29, -80, -211, -358, -518, -682, -847, -1004, -1149, - -1274, -1375, -1444, -1477, -1469, -1417, -1316, -1164, -960, -702, -392, -29, 382, 840, 1341, 1880, 2450, 3047, 3662, 4289, 4919, 5546, 6160, 6754, 7320, 7850, 8339, 8779, 9164, 9491, 9754, 9952, - 10082, 10143, 10136, 10062, 9924, 9724, 9469, 9164, 8814, 8427, 8012, 7575, 7127, 6676, 6231, 5801, 5394, 5020, 4687, 4400, 4167, 3994, 3885, 3843, 3872, 3971, 4142, 4382, 4690, 5062, 5494, 5979, - 6511, 7082, 7685, 8309, 8947, 9589, 10223, 10842, 11435, 11992, 12506, 12967, 13368, 13702, 13963, 14147, 14250, 14269, 14202, 14050, 13813, 13493, 13094, 12619, 12073, 11463, 10796, 10077, 9317, 8522, 7700, 6862, - 6015, 5167, 4327, 3501, 2698, 1923, 1182, 480, -178, -792, -1357, -1874, -2340, -2758, -3129, -3454, -3738, -3982, -4192, -4372, -4526, -4660, -4779, -4887, -4989, -5091, -5195, -5305, -5425, -5557, -5702, -5861, - -6035, -6223, -6423, -6633, -6851, -7073, -7295, -7511, -7719, -7911, -8082, -8228, -8342, -8419, -8455, -8443, -8382, -8266, -8094, -7862, -7571, -7220, -6810, -6343, -5821, -5248, -4628, -3968, -3272, -2549, -1806, -1051, - -292, 462, 1202, 1920, 2607, 3255, 3856, 4402, 4888, 5307, 5656, 5929, 6125, 6241, 6278, 6237, 6119, 5928, 5667, 5342, 4960, 4527, 4051, 3542, 3007, 2456, 1899, 1346, 805, 287, -199, -647, - -1047, -1394, -1681, -1903, -2057, -2140, -2149, -2086, -1951, -1745, -1473, -1138, -746, -303, 184, 707, 1259, 1830, 2411, 2994, 3569, 4128, 4662, 5163, 5624, 6038, 6399, 6702, 6943, 7119, 7230, 7272, - 7249, 7159, 7007, 6796, 6530, 6215, 5856, 5461, 5036, 4589, 4127, 3660, 3194, 2737, 2297, 1880, 1492, 1141, 830, 563, 345, 177, 61, -2, -15, 22, 109, 240, 412, 620, 861, 1127, - 1413, 1713, 2021, 2330, 2636, 2931, 3210, 3470, 3705, 3911, 4087, 4229, 4337, 4409, 4448, 4452, 4425, 4370, 4289, 4186, 4067, 3936, 3798, 3659, 3525, 3400, 3291, 3202, 3137, 3102, 3099, 3132, - 3202, 3311, 3458, 3644, 3867, 4125, 4415, 4731, 5070, 5426, 5793, 6164, 6532, 6889, 7229, 7543, 7825, 8066, 8261, 8402, 8486, 8506, 8458, 8341, 8152, 7889, 7554, 7147, 6672, 6132, 5532, 4877, - 4174, 3431, 2657, 1860, 1049, 236, -570, -1360, -2123, -2850, -3530, -4155, -4717, -5209, -5624, -5957, -6204, -6361, -6427, -6402, -6285, -6080, -5789, -5417, -4968, -4451, -3872, -3239, -2562, -1849, -1111, -358, - 400, 1154, 1894, 2610, 3293, 3936, 4530, 5069, 5548, 5962, 6307, 6581, 6783, 6912, 6970, 6959, 6881, 6740, 6542, 6291, 5994, 5657, 5287, 4892, 4477, 4052, 3621, 3193, 2773, 2367, 1980, 1616, - 1279, 972, 696, 453, 242, 62, -86, -208, -305, -381, -441, -489, -530, -570, -614, -667, -735, -821, -932, -1071, -1241, -1446, -1687, -1966, -2283, -2638, -3029, -3456, -3914, -4400, -4910, -5439, - -5981, -6531, -7081, -7626, -8159, -8673, -9162, -9619, -10038, -10415, -10744, -11022, -11245, -11411, -11518, -11566, -11555, -11487, -11363, -11189, -10967, -10702, -10401, -10070, -9715, -9344, -8965, -8585, -8212, -7854, -7517, -7210, - -6937, -6706, -6521, -6386, -6305, -6279, -6309, -6397, -6540, -6737, -6984, -7276, -7610, -7978, -8374, -8790, -9219, -9650, -10077, -10489, -10878, -11234, -11551, -11819, -12031, -12181, -12263, -12272, -12205, -12058, -11830, -11522, - -11133, -10667, -10127, -9517, -8842, -8110, -7328, -6504, -5648, -4768, -3875, -2979, -2090, -1217, -372, 436, 1199, 1908, 2557, 3137, 3644, 4072, 4419, 4681, 4859, 4951, 4959, 4885, 4732, 4505, 4209, 3850, - 3435, 2971, 2466, 1927, 1363, 782, 192, -398, -983, -1555, -2107, -2634, -3131, -3594, -4020, -4407, -4753, -5057, -5322, -5546, -5734, -5887, -6010, -6106, -6179, -6235, -6278, -6314, -6347, -6383, -6426, -6480, - -6549, -6637, -6746, -6878, -7035, -7215, -7420, -7648, -7896, -8163, -8445, -8738, -9037, -9337, -9633, -9919, -10190, -10440, -10663, -10854, -11007, -11118, -11184, -11200, -11164, -11075, -10932, -10734, -10483, -10181, -9830, -9435, - -9000, -8531, -8034, -7516, -6985, -6448, -5914, -5391, -4888, -4412, -3972, -3576, -3230, -2940, -2713, -2554, -2465, -2450, -2511, -2647, -2859, -3144, -3499, -3920, -4402, -4939, -5523, -6147, -6803, -7480, -8170, -8863, - -9549, -10218, -10860, -11467, -12029, -12539, -12989, -13372, -13684, -13919, -14075, -14150, -14142, -14052, -13882, -13633, -13311, -12920, -12466, -11955, -11396, -10795, -10163, -9506, -8836, -8161, -7489, -6830, -6193, -5584, -5011, -4480, - -3998, -3568, -3194, -2878, -2623, -2427, -2291, -2212, -2188, -2215, -2288, -2401, -2549, -2724, -2921, -3130, -3345, -3559, -3763, -3951, -4116, -4251, -4351, -4412, -4428, -4396, -4315, -4182, -3998, -3762, -3476, -3142, - -2764, -2345, -1890, -1403, -892, -361, 181, 731, 1281, 1825, 2356, 2869, 3359, 3820, 4249, 4642, 4997, 5311, 5584, 5815, 6006, 6157, 6272, 6352, 6403, 6427, 6430, 6416, 6392, 6362, 6332, 6308, - 6294, 6295, 6317, 6361, 6433, 6533, 6664, 6827, 7021, 7245, 7498, 7777, 8078, 8397, 8728, 9066, 9405, 9737, 10056, 10354, 10625, 10860, 11053, 11197, 11286, 11315, 11277, 11171, 10991, 10737, 10407, 10001, - 9521, 8968, 8347, 7662, 6919, 6125, 5286, 4412, 3511, 2593, 1668, 747, -159, -1043, -1892, -2697, -3448, -4137, -4756, -5298, -5757, -6128, -6407, -6592, -6681, -6675, -6574, -6382, -6103, -5740, -5300, -4791, - -4219, -3593, -2923, -2218, -1488, -743, 7, 753, 1485, 2193, 2870, 3507, 4098, 4637, 5118, 5538, 5894, 6183, 6406, 6563, 6655, 6684, 6654, 6570, 6437, 6259, 6044, 5798, 5527, 5239, 4941, 4640, - 4341, 4052, 3778, 3524, 3294, 3092, 2921, 2783, 2678, 2608, 2571, 2565, 2589, 2638, 2711, 2801, 2905, 3016, 3130, 3241, 3343, 3431, 3499, 3542, 3556, 3537, 3482, 3387, 3251, 3073, 2852, 2590, - 2287, 1946, 1570, 1164, 730, 275, -194, -674, -1158, -1638, -2109, -2564, -2997, -3401, -3772, -4104, -4393, -4636, -4830, -4973, -5064, -5103, -5092, -5033, -4927, -4780, -4596, -4379, -4137, -3876, -3602, -3324, - -3048, -2783, -2535, -2313, -2123, -1972, -1865, -1807, -1803, -1856, -1969, -2143, -2377, -2671, -3024, -3431, -3889, -4393, -4936, -5512, -6113, -6731, -7356, -7980, -8594, -9189, -9754, -10282, -10763, -11190, -11555, -11853, - -12076, -12222, -12286, -12267, -12162, -11973, -11700, -11347, -10917, -10414, -9846, -9218, -8538, -7815, -7058, -6276, -5480, -4678, -3881, -3099, -2341, -1616, -933, -298, 279, 796, 1246, 1626, 1932, 2164, 2320, 2402, - 2412, 2352, 2227, 2041, 1801, 1512, 1181, 817, 427, 19, -398, -817, -1231, -1631, -2010, -2363, -2683, -2965, -3205, -3400, -3548, -3647, -3696, -3696, -3648, -3554, -3418, -3242, -3031, -2789, -2522, -2234, - -1932, -1620, -1304, -990, -681, -382, -97, 169, 416, 641, 843, 1022, 1177, 1311, 1426, 1523, 1606, 1680, 1748, 1816, 1889, 1971, 2068, 2186, 2330, 2504, 2712, 2959, 3248, 3582, 3961, 4387, - 4860, 5379, 5942, 6545, 7186, 7859, 8559, 9279, 10014, 10754, 11492, 12220, 12929, 13610, 14256, 14857, 15406, 15896, 16319, 16669, 16942, 17132, 17237, 17255, 17185, 17026, 16780, 16451, 16041, 15555, 14999, 14381, - 13707, 12987, 12228, 11441, 10636, 9823, 9010, 8210, 7430, 6681, 5970, 5307, 4697, 4147, 3662, 3247, 2903, 2633, 2437, 2314, 2263, 2280, 2362, 2503, 2697, 2937, 3216, 3526, 3858, 4204, 4555, 4903, - 5238, 5554, 5841, 6093, 6305, 6469, 6583, 6642, 6643, 6586, 6470, 6296, 6066, 5782, 5448, 5070, 4652, 4201, 3724, 3229, 2721, 2211, 1704, 1210, 735, 288, -125, -499, -827, -1106, -1330, -1497, - -1606, -1654, -1642, -1571, -1442, -1258, -1023, -740, -416, -54, 338, 755, 1192, 1640, 2094, 2547, 2994, 3429, 3847, 4243, 4614, 4956, 5268, 5547, 5794, 6009, 6192, 6345, 6471, 6573, 6655, 6722, - 6777, 6827, 6875, 6928, 6990, 7067, 7164, 7283, 7430, 7608, 7818, 8063, 8343, 8658, 9008, 9390, 9802, 10240, 10700, 11176, 11663, 12155, 12643, 13122, 13583, 14020, 14423, 14786, 15102, 15364, 15566, 15702, - 15768, 15760, 15675, 15512, 15269, 14948, 14551, 14079, 13537, 12930, 12264, 11546, 10785, 9988, 9165, 8325, 7479, 6638, 5810, 5007, 4239, 3514, 2842, 2231, 1688, 1220, 832, 528, 311, 183, 144, 195, - 332, 552, 852, 1225, 1666, 2166, 2718, 3313, 3941, 4594, 5261, 5932, 6598, 7249, 7877, 8472, 9027, 9534, 9989, 10384, 10718, 10985, 11184, 11315, 11377, 11373, 11303, 11172, 10984, 10744, 10457, 10131, - 9770, 9383, 8977, 8560, 8137, 7716, 7304, 6907, 6531, 6179, 5858, 5569, 5315, 5099, 4920, 4779, 4675, 4605, 4567, 4558, 4573, 4609, 4660, 4721, 4787, 4852, 4910, 4957, 4986, 4994, 4977, 4930, - 4851, 4737, 4588, 4403, 4181, 3925, 3636, 3316, 2970, 2601, 2214, 1815, 1410, 1004, 604, 216, -152, -496, -810, -1087, -1324, -1514, -1656, -1746, -1783, -1764, -1691, -1563, -1384, -1155, -880, -565, - -214, 166, 569, 988, 1415, 1842, 2262, 2666, 3047, 3398, 3711, 3979, 4198, 4363, 4469, 4513, 4493, 4409, 4260, 4048, 3775, 3444, 3061, 2630, 2159, 1653, 1121, 571, 12, -547, -1097, -1630, - -2136, -2606, -3033, -3410, -3729, -3985, -4173, -4290, -4332, -4298, -4188, -4004, -3746, -3419, -3027, -2575, -2070, -1519, -931, -314, 321, 967, 1614, 2250, 2867, 3455, 4005, 4509, 4959, 5347, 5667, 5915, - 6087, 6179, 6190, 6119, 5966, 5735, 5426, 5044, 4595, 4083, 3515, 2899, 2241, 1551, 836, 104, -634, -1373, -2103, -2817, -3509, -4171, -4798, -5384, -5927, -6423, -6870, -7266, -7611, -7905, -8151, -8350, - -8506, -8621, -8699, -8747, -8767, -8766, -8748, -8718, -8682, -8644, -8608, -8579, -8559, -8550, -8556, -8577, -8613, -8664, -8730, -8807, -8895, -8988, -9084, -9177, -9264, -9339, -9397, -9432, -9439, -9412, -9347, -9239, - -9085, -8879, -8621, -8307, -7936, -7509, -7026, -6488, -5899, -5261, -4580, -3860, -3108, -2331, -1536, -731, 74, 873, 1655, 2413, 3137, 3819, 4451, 5025, 5536, 5976, 6342, 6629, 6834, 6956, 6993, 6947, - 6819, 6612, 6331, 5979, 5564, 5091, 4570, 4008, 3415, 2799, 2170, 1538, 913, 303, -281, -833, -1343, -1806, -2214, -2563, -2849, -3068, -3219, -3301, -3314, -3262, -3146, -2970, -2740, -2461, -2141, -1786, - -1405, -1007, -600, -193, 204, 584, 938, 1257, 1535, 1764, 1938, 2054, 2105, 2090, 2007, 1854, 1632, 1343, 989, 573, 99, -425, -996, -1607, -2249, -2916, -3599, -4291, -4984, -5670, -6340, -6989, - -7609, -8194, -8740, -9241, -9695, -10098, -10448, -10746, -10990, -11183, -11326, -11421, -11474, -11487, -11466, -11416, -11343, -11252, -11150, -11043, -10936, -10836, -10748, -10676, -10624, -10598, -10598, -10628, -10689, -10782, -10905, -11059, - -11241, -11448, -11677, -11924, -12185, -12454, -12726, -12997, -13259, -13508, -13738, -13944, -14121, -14265, -14372, -14440, -14464, -14446, -14383, -14276, -14126, -13936, -13708, -13446, -13155, -12841, -12509, -12166, -11819, -11475, -11142, -10827, - -10537, -10281, -10064, -9894, -9775, -9712, -9711, -9773, -9902, -10097, -10360, -10689, -11082, -11534, -12043, -12602, -13204, -13842, -14509, -15195, -15890, -16585, -17271, -17936, -18571, -19167, -19713, -20201, -20622, -20970, -21238, -21419, - -21511, -21509, -21412, -21219, -20931, -20549, -20077, -19518, -18879, -18165, -17384, -16545, -15656, -14726, -13765, -12784, -11792, -10801, -9818, -8856, -7921, -7023, -6170, -5369, -4624, -3942, -3326, -2778, -2301, -1895, -1559, -1292, - -1091, -952, -871, -842, -861, -919, -1012, -1131, -1270, -1422, -1580, -1737, -1887, -2024, -2144, -2242, -2314, -2358, -2371, -2353, -2304, -2223, -2114, -1978, -1818, -1638, -1442, -1235, -1022, -808, -600, -401, - -219, -56, 79, 187, 260, 298, 297, 255, 173, 49, -113, -314, -551, -819, -1116, -1435, -1773, -2122, -2476, -2830, -3176, -3508, -3820, -4104, -4356, -4569, -4740, -4863, -4935, -4955, -4919, -4828, - -4682, -4482, -4230, -3929, -3584, -3199, -2780, -2332, -1864, -1382, -893, -406, 71, 533, 970, 1377, 1746, 2071, 2348, 2572, 2740, 2849, 2899, 2888, 2819, 2692, 2511, 2280, 2005, 1690, 1344, 973, - 585, 190, -203, -588, -954, -1291, -1593, -1850, -2054, -2200, -2282, -2293, -2230, -2090, -1872, -1575, -1199, -746, -220, 374, 1034, 1750, 2517, 3327, 4169, 5036, 5917, 6802, 7681, 8545, 9382, 10184, - 10942, 11647, 12292, 12870, 13375, 13802, 14147, 14409, 14586, 14677, 14683, 14607, 14452, 14223, 13923, 13559, 13138, 12667, 12153, 11605, 11030, 10438, 9835, 9231, 8632, 8046, 7478, 6936, 6423, 5945, 5504, 5103, - 4744, 4426, 4150, 3915, 3719, 3558, 3430, 3331, 3256, 3200, 3159, 3128, 3101, 3074, 3041, 2999, 2944, 2871, 2780, 2666, 2529, 2369, 2186, 1980, 1754, 1509, 1250, 981, 704, 426, 152, -112, - -362, -591, -794, -965, -1100, -1193, -1240, -1238, -1185, -1078, -917, -702, -432, -111, 258, 673, 1129, 1620, 2140, 2682, 3238, 3801, 4362, 4913, 5446, 5952, 6423, 6852, 7230, 7552, 7811, 8002, - 8123, 8168, 8137, 8029, 7844, 7584, 7252, 6850, 6385, 5862, 5289, 4672, 4020, 3342, 2647, 1946, 1248, 562, -100, -732, -1323, -1866, -2353, -2778, -3134, -3417, -3625, -3753, -3803, -3773, -3665, -3481, - -3226, -2903, -2519, -2081, -1595, -1069, -513, 64, 655, 1250, 1839, 2414, 2967, 3489, 3973, 4413, 4802, 5136, 5412, 5627, 5778, 5867, 5892, 5856, 5762, 5613, 5414, 5170, 4887, 4572, 4232, 3874, - 3506, 3136, 2770, 2416, 2082, 1772, 1495, 1253, 1053, 897, 787, 727, 716, 755, 841, 974, 1150, 1365, 1616, 1897, 2202, 2526, 2863, 3206, 3549, 3886, 4210, 4516, 4798, 5052, 5274, 5459, - 5606, 5712, 5776, 5799, 5780, 5722, 5626, 5496, 5335, 5147, 4937, 4710, 4472, 4228, 3984, 3745, 3517, 3304, 3112, 2943, 2802, 2692, 2613, 2568, 2557, 2578, 2630, 2711, 2817, 2944, 3088, 3242, - 3401, 3558, 3706, 3837, 3946, 4024, 4065, 4061, 4007, 3898, 3727, 3491, 3187, 2813, 2367, 1850, 1261, 605, -115, -897, -1733, -2618, -3544, -4502, -5483, -6479, -7479, -8472, -9449, -10398, -11310, -12174, - -12981, -13722, -14390, -14976, -15475, -15882, -16191, -16402, -16512, -16520, -16429, -16240, -15956, -15583, -15127, -14593, -13991, -13327, -12611, -11853, -11062, -10249, -9424, -8595, -7774, -6969, -6188, -5441, -4733, -4071, -3461, -2906, - -2410, -1976, -1602, -1291, -1040, -847, -710, -623, -583, -583, -618, -681, -766, -865, -973, -1081, -1184, -1275, -1348, -1400, -1424, -1418, -1379, -1304, -1192, -1044, -860, -642, -391, -111, 193, 519, - 860, 1213, 1571, 1928, 2279, 2618, 2939, 3237, 3508, 3747, 3950, 4115, 4240, 4322, 4362, 4360, 4316, 4234, 4115, 3964, 3785, 3581, 3360, 3127, 2887, 2648, 2415, 2196, 1995, 1819, 1674, 1564, - 1494, 1466, 1485, 1551, 1666, 1830, 2041, 2298, 2598, 2937, 3310, 3713, 4139, 4581, 5033, 5487, 5936, 6371, 6785, 7171, 7522, 7830, 8090, 8297, 8446, 8533, 8556, 8514, 8405, 8230, 7992, 7692, - 7336, 6927, 6471, 5976, 5449, 4898, 4331, 3758, 3188, 2630, 2093, 1586, 1118, 696, 329, 24, -215, -383, -475, -491, -427, -284, -64, 232, 600, 1036, 1533, 2086, 2686, 3325, 3995, 4686, - 5389, 6093, 6789, 7466, 8117, 8731, 9300, 9816, 10273, 10664, 10985, 11231, 11400, 11489, 11500, 11431, 11285, 11065, 10775, 10419, 10003, 9534, 9018, 8464, 7878, 7270, 6648, 6019, 5391, 4773, 4171, 3592, - 3043, 2528, 2052, 1618, 1229, 888, 594, 348, 148, -5, -117, -190, -228, -236, -217, -177, -122, -57, 12, 82, 147, 204, 246, 273, 281, 267, 232, 174, 95, -5, -124, -260, - -408, -565, -727, -889, -1044, -1189, -1318, -1424, -1504, -1552, -1563, -1533, -1460, -1339, -1170, -951, -682, -363, 2, 413, 866, 1356, 1877, 2424, 2990, 3567, 4148, 4725, 5288, 5829, 6340, 6813, - 7240, 7612, 7924, 8168, 8340, 8435, 8450, 8382, 8231, 7996, 7678, 7280, 6805, 6258, 5644, 4971, 4245, 3475, 2671, 1840, 994, 141, -706, -1540, -2351, -3129, -3865, -4552, -5182, -5748, -6247, -6673, - -7023, -7296, -7490, -7607, -7648, -7617, -7516, -7351, -7128, -6854, -6536, -6183, -5801, -5401, -4991, -4579, -4175, -3787, -3422, -3087, -2790, -2536, -2330, -2176, -2075, -2031, -2044, -2113, -2237, -2413, -2637, -2906, - -3213, -3553, -3920, -4305, -4703, -5104, -5501, -5887, -6253, -6593, -6900, -7168, -7391, -7565, -7685, -7748, -7752, -7697, -7581, -7406, -7174, -6886, -6547, -6160, -5730, -5263, -4765, -4242, -3699, -3145, -2585, -2025, - -1472, -932, -409, 91, 565, 1010, 1422, 1801, 2145, 2455, 2731, 2974, 3188, 3375, 3539, 3684, 3815, 3936, 4053, 4171, 4295, 4429, 4580, 4751, 4945, 5167, 5418, 5701, 6017, 6365, 6745, 7155, - 7592, 8054, 8535, 9032, 9537, 10045, 10550, 11042, 11517, 11964, 12378, 12750, 13074, 13342, 13549, 13688, 13755, 13746, 13659, 13490, 13240, 12909, 12499, 12011, 11449, 10820, 10128, 9381, 8586, 7751, 6887, 6002, - 5108, 4213, 3329, 2465, 1633, 841, 98, -586, -1205, -1753, -2222, -2609, -2911, -3125, -3250, -3288, -3238, -3106, -2894, -2608, -2253, -1838, -1370, -857, -309, 263, 854, 1450, 2043, 2624, 3183, 3711, - 4201, 4646, 5039, 5376, 5651, 5861, 6005, 6082, 6091, 6033, 5912, 5730, 5490, 5200, 4863, 4486, 4075, 3639, 3184, 2717, 2246, 1777, 1318, 875, 453, 57, -306, -636, -929, -1183, -1397, -1570, - -1705, -1801, -1863, -1893, -1895, -1872, -1831, -1777, -1714, -1648, -1585, -1530, -1488, -1464, -1462, -1487, -1540, -1624, -1741, -1891, -2075, -2292, -2539, -2814, -3115, -3435, -3772, -4120, -4473, -4824, -5169, -5500, - -5811, -6096, -6350, -6565, -6738, -6864, -6940, -6962, -6928, -6837, -6690, -6486, -6227, -5917, -5558, -5156, -4715, -4242, -3743, -3225, -2696, -2164, -1637, -1122, -627, -161, 269, 658, 1000, 1289, 1520, 1691, - 1800, 1844, 1825, 1742, 1598, 1396, 1140, 836, 490, 108, -301, -731, -1172, -1616, -2052, -2474, -2870, -3234, -3557, -3831, -4051, -4209, -4301, -4323, -4273, -4148, -3948, -3675, -3329, -2915, -2437, -1899, - -1309, -673, 0, 700, 1421, 2152, 2882, 3603, 4304, 4975, 5608, 6194, 6724, 7193, 7592, 7917, 8164, 8329, 8411, 8408, 8321, 8152, 7902, 7576, 7178, 6714, 6190, 5613, 4990, 4330, 3641, 2931, - 2209, 1483, 761, 51, -640, -1306, -1940, -2539, -3096, -3610, -4078, -4498, -4870, -5193, -5470, -5702, -5893, -6044, -6162, -6248, -6310, -6351, -6376, -6391, -6401, -6410, -6422, -6441, -6472, -6515, -6574, -6649, - -6742, -6851, -6976, -7115, -7265, -7423, -7586, -7748, -7906, -8053, -8186, -8297, -8382, -8435, -8451, -8425, -8353, -8232, -8058, -7828, -7543, -7200, -6801, -6347, -5839, -5282, -4680, -4038, -3361, -2656, -1931, -1194, - -452, 284, 1009, 1711, 2384, 3018, 3605, 4138, 4610, 5015, 5349, 5607, 5786, 5884, 5900, 5836, 5691, 5470, 5176, 4813, 4389, 3909, 3382, 2816, 2220, 1602, 974, 343, -278, -883, -1461, -2004, - -2504, -2953, -3346, -3676, -3939, -4133, -4255, -4304, -4280, -4186, -4023, -3796, -3509, -3169, -2781, -2353, -1894, -1411, -913, -409, 90, 578, 1046, 1485, 1888, 2247, 2557, 2813, 3010, 3144, 3215, 3220, - 3160, 3035, 2849, 2604, 2305, 1956, 1563, 1134, 674, 191, -305, -809, -1313, -1807, -2285, -2740, -3164, -3553, -3900, -4200, -4452, -4650, -4795, -4884, -4918, -4898, -4825, -4704, -4536, -4327, -4082, -3805, - -3502, -3180, -2844, -2501, -2157, -1817, -1488, -1174, -880, -609, -366, -151, 31, 182, 302, 390, 449, 482, 490, 478, 451, 412, 367, 322, 281, 251, 237, 243, 275, 337, 432, 563, - 734, 945, 1197, 1490, 1822, 2192, 2597, 3032, 3493, 3976, 4472, 4977, 5483, 5983, 6469, 6934, 7369, 7768, 8124, 8430, 8680, 8869, 8992, 9047, 9030, 8941, 8779, 8544, 8240, 7869, 7435, 6943, - 6401, 5814, 5192, 4542, 3875, 3199, 2525, 1862, 1221, 611, 43, -475, -936, -1330, -1653, -1897, -2058, -2134, -2121, -2019, -1828, -1549, -1186, -743, -224, 363, 1013, 1716, 2465, 3250, 4062, 4889, - 5722, 6552, 7367, 8159, 8918, 9636, 10305, 10917, 11468, 11951, 12364, 12702, 12965, 13151, 13261, 13298, 13262, 13158, 12991, 12765, 12486, 12160, 11795, 11398, 10974, 10533, 10080, 9622, 9167, 8719, 8284, 7867, - 7472, 7101, 6757, 6442, 6155, 5896, 5664, 5458, 5273, 5107, 4956, 4815, 4679, 4544, 4403, 4251, 4084, 3896, 3683, 3440, 3164, 2852, 2502, 2111, 1679, 1207, 694, 144, -441, -1059, -1705, -2373, - -3060, -3758, -4461, -5163, -5858, -6537, -7196, -7826, -8423, -8980, -9492, -9956, -10366, -10721, -11019, -11258, -11438, -11561, -11627, -11641, -11606, -11527, -11408, -11256, -11077, -10879, -10669, -10454, -10242, -10042, -9859, -9701, - -9574, -9486, -9440, -9441, -9492, -9597, -9756, -9970, -10239, -10559, -10929, -11345, -11801, -12291, -12810, -13349, -13901, -14456, -15007, -15545, -16059, -16542, -16984, -17378, -17716, -17990, -18195, -18325, -18376, -18346, -18231, -18032, - -17748, -17382, -16936, -16414, -15822, -15166, -14452, -13689, -12886, -12052, -11196, -10329, -9460, -8601, -7760, -6948, -6174, -5446, -4772, -4159, -3614, -3141, -2744, -2427, -2190, -2035, -1960, -1964, -2044, -2196, -2415, -2696, - -3032, -3416, -3840, -4298, -4779, -5278, -5785, -6292, -6793, -7280, -7746, -8186, -8595, -8968, -9301, -9594, -9843, -10047, -10209, -10327, -10405, -10445, -10450, -10425, -10372, -10299, -10208, -10106, -9998, -9889, -9783, -9685, - -9600, -9531, -9480, -9450, -9444, -9460, -9500, -9563, -9647, -9750, -9869, -10000, -10139, -10281, -10421, -10554, -10674, -10775, -10852, -10899, -10912, -10885, -10815, -10698, -10530, -10311, -10039, -9715, -9337, -8909, -8433, -7913, - -7353, -6758, -6136, -5491, -4832, -4167, -3504, -2851, -2217, -1610, -1038, -509, -31, 388, 746, 1034, 1250, 1390, 1452, 1434, 1338, 1164, 915, 594, 207, -241, -743, -1292, -1880, -2497, -3134, -3782, - -4430, -5070, -5691, -6283, -6839, -7349, -7805, -8202, -8534, -8794, -8980, -9090, -9121, -9074, -8950, -8750, -8479, -8141, -7741, -7286, -6782, -6238, -5663, -5064, -4451, -3832, -3218, -2617, -2037, -1486, -972, -501, - -79, 288, 598, 847, 1034, 1158, 1219, 1219, 1161, 1047, 883, 674, 426, 145, -161, -486, -822, -1161, -1496, -1818, -2122, -2400, -2647, -2856, -3025, -3148, -3222, -3247, -3221, -3144, -3017, -2841, - -2620, -2357, -2056, -1721, -1359, -976, -576, -167, 244, 653, 1053, 1438, 1802, 2140, 2449, 2725, 2965, 3166, 3328, 3451, 3533, 3578, 3587, 3563, 3509, 3430, 3330, 3214, 3088, 2957, 2826, 2701, - 2588, 2491, 2416, 2366, 2344, 2354, 2398, 2476, 2589, 2737, 2918, 3130, 3369, 3631, 3913, 4207, 4508, 4810, 5105, 5387, 5648, 5880, 6077, 6233, 6339, 6392, 6385, 6314, 6176, 5968, 5690, 5339, - 4918, 4428, 3873, 3255, 2582, 1859, 1092, 292, -534, -1377, -2227, -3074, -3906, -4715, -5489, -6218, -6894, -7506, -8048, -8511, -8890, -9178, -9373, -9470, -9469, -9368, -9169, -8874, -8486, -8010, -7451, -6816, - -6113, -5349, -4535, -3679, -2791, -1882, -960, -38, 876, 1773, 2644, 3481, 4276, 5022, 5715, 6349, 6922, 7431, 7874, 8252, 8566, 8817, 9009, 9145, 9230, 9270, 9270, 9236, 9176, 9096, 9003, 8903, - 8804, 8712, 8632, 8569, 8529, 8515, 8529, 8575, 8653, 8764, 8907, 9081, 9284, 9513, 9764, 10033, 10316, 10606, 10899, 11189, 11471, 11737, 11984, 12205, 12396, 12553, 12672, 12749, 12783, 12773, 12716, 12614, - 12468, 12280, 12052, 11788, 11491, 11168, 10823, 10462, 10092, 9718, 9348, 8987, 8642, 8318, 8023, 7760, 7534, 7349, 7209, 7114, 7066, 7066, 7113, 7206, 7341, 7515, 7724, 7963, 8226, 8506, 8798, 9093, - 9383, 9663, 9923, 10156, 10356, 10515, 10629, 10690, 10696, 10641, 10524, 10342, 10096, 9785, 9411, 8977, 8487, 7946, 7359, 6733, 6075, 5394, 4698, 3996, 3297, 2611, 1948, 1316, 723, 179, -308, -733, - -1089, -1372, -1577, -1701, -1743, -1702, -1579, -1377, -1098, -746, -328, 149, 681, 1258, 1871, 2511, 3169, 3834, 4497, 5149, 5779, 6379, 6940, 7454, 7915, 8315, 8650, 8915, 9109, 9227, 9271, 9240, - 9136, 8961, 8720, 8417, 8057, 7646, 7192, 6702, 6184, 5646, 5095, 4541, 3991, 3453, 2934, 2441, 1980, 1557, 1175, 839, 551, 313, 127, -7, -91, -127, -116, -63, 29, 156, 313, 495, - 696, 911, 1135, 1361, 1587, 1806, 2015, 2211, 2390, 2550, 2690, 2809, 2908, 2987, 3048, 3093, 3126, 3150, 3169, 3189, 3214, 3249, 3300, 3371, 3469, 3598, 3762, 3966, 4213, 4505, 4846, 5235, - 5674, 6161, 6695, 7274, 7893, 8549, 9235, 9947, 10678, 11419, 12163, 12902, 13627, 14330, 15002, 15634, 16218, 16747, 17213, 17610, 17933, 18176, 18336, 18410, 18397, 18296, 18108, 17836, 17482, 17051, 16548, 15979, - 15353, 14676, 13958, 13207, 12434, 11648, 10860, 10079, 9315, 8576, 7873, 7213, 6603, 6050, 5559, 5134, 4780, 4497, 4288, 4150, 4084, 4086, 4152, 4278, 4457, 4684, 4951, 5250, 5573, 5911, 6256, 6598, - 6928, 7240, 7524, 7773, 7981, 8142, 8251, 8304, 8298, 8231, 8102, 7911, 7661, 7353, 6990, 6578, 6121, 5626, 5099, 4546, 3976, 3397, 2815, 2239, 1677, 1136, 623, 144, -293, -687, -1031, -1323, - -1559, -1738, -1861, -1927, -1937, -1895, -1803, -1665, -1485, -1270, -1024, -753, -464, -164, 141, 446, 745, 1030, 1298, 1544, 1763, 1952, 2110, 2233, 2322, 2375, 2395, 2382, 2339, 2269, 2176, 2063, - 1936, 1800, 1659, 1519, 1385, 1263, 1157, 1072, 1012, 981, 981, 1014, 1082, 1185, 1322, 1492, 1694, 1923, 2176, 2448, 2733, 3025, 3319, 3606, 3879, 4131, 4354, 4541, 4685, 4779, 4816, 4791, - 4700, 4537, 4301, 3988, 3599, 3133, 2592, 1977, 1293, 545, -261, -1120, -2023, -2962, -3928, -4911, -5901, -6888, -7861, -8810, -9725, -10596, -11414, -12171, -12858, -13469, -13998, -14440, -14792, -15051, -15216, -15288, - -15268, -15158, -14963, -14687, -14337, -13919, -13441, -12912, -12341, -11737, -11110, -10470, -9826, -9189, -8566, -7968, -7402, -6875, -6394, -5965, -5591, -5278, -5026, -4838, -4713, -4651, -4650, -4706, -4817, -4976, -5180, -5421, - -5693, -5990, -6304, -6627, -6954, -7277, -7588, -7883, -8154, -8398, -8609, -8784, -8920, -9015, -9068, -9080, -9050, -8981, -8875, -8734, -8564, -8367, -8150, -7917, -7674, -7427, -7180, -6940, -6712, -6501, -6311, -6146, - -6009, -5904, -5831, -5792, -5788, -5816, -5877, -5966, -6083, -6222, -6379, -6549, -6726, -6906, -7081, -7245, -7392, -7516, -7611, -7672, -7693, -7670, -7599, -7477, -7302, -7073, -6790, -6452, -6062, -5622, -5136, -4608, - -4043, -3447, -2828, -2190, -1544, -895, -253, 374, 980, 1556, 2094, 2590, 3035, 3425, 3755, 4022, 4223, 4357, 4423, 4423, 4357, 4230, 4044, 3805, 3519, 3193, 2834, 2450, 2049, 1641, 1235, 839, - 462, 114, -198, -467, -685, -846, -945, -979, -943, -836, -657, -407, -88, 297, 746, 1252, 1809, 2410, 3048, 3714, 4398, 5092, 5785, 6468, 7131, 7764, 8358, 8905, 9396, 9824, 10182, 10466, - 10671, 10792, 10829, 10780, 10646, 10427, 10126, 9746, 9293, 8771, 8187, 7548, 6860, 6134, 5375, 4594, 3798, 2997, 2197, 1408, 635, -112, -830, -1513, -2156, -2755, -3309, -3814, -4272, -4680, -5041, -5357, - -5629, -5861, -6058, -6222, -6359, -6474, -6571, -6656, -6734, -6809, -6885, -6967, -7057, -7160, -7275, -7406, -7553, -7715, -7892, -8081, -8281, -8488, -8699, -8909, -9113, -9307, -9485, -9642, -9771, -9868, -9928, -9945, - -9915, -9834, -9700, -9509, -9260, -8952, -8587, -8164, -7686, -7156, -6578, -5957, -5299, -4610, -3898, -3171, -2436, -1702, -978, -272, 405, 1047, 1646, 2193, 2682, 3107, 3461, 3742, 3945, 4069, 4112, 4074, - 3958, 3764, 3498, 3163, 2765, 2311, 1809, 1266, 692, 95, -514, -1128, -1735, -2326, -2893, -3426, -3917, -4360, -4747, -5073, -5334, -5526, -5646, -5695, -5672, -5578, -5416, -5190, -4904, -4563, -4176, -3748, - -3288, -2803, -2304, -1799, -1296, -805, -335, 106, 511, 873, 1184, 1441, 1637, 1770, 1837, 1837, 1769, 1635, 1436, 1175, 857, 486, 68, -389, -882, -1401, -1939, -2488, -3040, -3588, -4123, -4638, - -5127, -5584, -6002, -6378, -6708, -6988, -7217, -7393, -7517, -7588, -7610, -7585, -7515, -7405, -7261, -7086, -6887, -6670, -6440, -6205, -5969, -5739, -5521, -5320, -5139, -4984, -4857, -4761, -4698, -4669, -4674, -4711, - -4781, -4879, -5004, -5152, -5317, -5497, -5685, -5875, -6064, -6244, -6411, -6559, -6683, -6780, -6844, -6873, -6864, -6815, -6726, -6597, -6428, -6221, -5978, -5705, -5404, -5080, -4741, -4392, -4039, -3691, -3355, -3037, - -2747, -2490, -2273, -2105, -1989, -1933, -1939, -2012, -2154, -2366, -2650, -3003, -3425, -3913, -4461, -5066, -5720, -6417, -7149, -7906, -8681, -9462, -10241, -11007, -11749, -12459, -13125, -13740, -14294, -14781, -15191, -15521, - -15765, -15919, -15980, -15948, -15822, -15604, -15296, -14902, -14427, -13876, -13257, -12578, -11846, -11072, -10264, -9433, -8588, -7740, -6899, -6074, -5275, -4509, -3786, -3112, -2494, -1936, -1442, -1017, -661, -376, -162, -16, - 62, 78, 35, -61, -207, -394, -617, -869, -1141, -1428, -1722, -2016, -2304, -2580, -2839, -3075, -3285, -3465, -3614, -3729, -3811, -3859, -3875, -3860, -3818, -3752, -3665, -3563, -3449, -3331, -3212, -3098, - -2994, -2907, -2839, -2797, -2783, -2801, -2854, -2943, -3069, -3234, -3435, -3671, -3941, -4240, -4565, -4912, -5276, -5650, -6028, -6404, -6772, -7125, -7456, -7760, -8029, -8258, -8442, -8578, -8660, -8686, -8655, -8565, - -8416, -8209, -7946, -7630, -7264, -6852, -6402, -5917, -5406, -4874, -4330, -3782, -3236, -2702, -2185, -1695, -1236, -817, -441, -114, 159, 378, 540, 644, 691, 682, 619, 506, 348, 151, -79, -335, - -610, -895, -1181, -1459, -1721, -1958, -2160, -2321, -2431, -2485, -2475, -2397, -2246, -2019, -1713, -1329, -865, -324, 291, 977, 1730, 2542, 3406, 4316, 5262, 6234, 7224, 8221, 9216, 10197, 11155, 12080, - 12962, 13794, 14567, 15273, 15907, 16463, 16937, 17326, 17628, 17843, 17970, 18012, 17971, 17850, 17656, 17392, 17066, 16684, 16254, 15783, 15280, 14753, 14210, 13659, 13108, 12563, 12032, 11521, 11034, 10577, 10153, 9765, - 9414, 9102, 8829, 8594, 8395, 8230, 8095, 7988, 7903, 7836, 7783, 7738, 7696, 7653, 7602, 7541, 7464, 7369, 7252, 7111, 6944, 6751, 6531, 6285, 6015, 5723, 5411, 5085, 4747, 4404, 4059, 3718, - 3388, 3073, 2780, 2515, 2282, 2086, 1932, 1823, 1764, 1755, 1798, 1894, 2043, 2242, 2490, 2783, 3118, 3488, 3888, 4313, 4753, 5202, 5653, 6095, 6522, 6924, 7295, 7625, 7907, 8135, 8303, 8405, - 8437, 8396, 8279, 8086, 7816, 7470, 7051, 6563, 6009, 5396, 4731, 4020, 3272, 2496, 1701, 897, 93, -699, -1472, -2215, -2919, -3576, -4177, -4716, -5187, -5585, -5905, -6145, -6303, -6379, -6374, -6289, - -6128, -5894, -5595, -5234, -4820, -4361, -3863, -3337, -2791, -2235, -1677, -1126, -591, -80, 399, 840, 1237, 1585, 1878, 2116, 2294, 2413, 2473, 2474, 2420, 2313, 2159, 1960, 1725, 1458, 1168, 860, - 543, 224, -88, -388, -667, -920, -1141, -1323, -1462, -1554, -1597, -1588, -1527, -1412, -1246, -1029, -764, -455, -106, 278, 693, 1131, 1588, 2056, 2530, 3002, 3467, 3918, 4350, 4758, 5137, 5484, - 5795, 6068, 6302, 6496, 6650, 6765, 6843, 6887, 6900, 6886, 6849, 6794, 6727, 6652, 6576, 6503, 6439, 6390, 6359, 6351, 6369, 6416, 6495, 6607, 6751, 6928, 7137, 7374, 7638, 7923, 8225, 8539, - 8859, 9179, 9490, 9787, 10061, 10306, 10515, 10680, 10794, 10853, 10850, 10781, 10642, 10431, 10147, 9787, 9354, 8848, 8272, 7631, 6930, 6174, 5370, 4528, 3654, 2759, 1852, 943, 43, -837, -1689, -2501, - -3264, -3969, -4607, -5171, -5655, -6053, -6360, -6574, -6691, -6712, -6638, -6469, -6209, -5863, -5435, -4933, -4363, -3733, -3053, -2332, -1579, -804, -18, 769, 1548, 2310, 3046, 3747, 4406, 5017, 5573, 6070, - 6505, 6875, 7179, 7416, 7587, 7695, 7742, 7731, 7668, 7557, 7405, 7218, 7003, 6766, 6514, 6255, 5995, 5740, 5497, 5270, 5065, 4886, 4736, 4616, 4530, 4478, 4458, 4471, 4514, 4585, 4680, 4795, - 4926, 5067, 5213, 5358, 5498, 5626, 5736, 5824, 5885, 5914, 5907, 5862, 5776, 5647, 5475, 5260, 5003, 4705, 4369, 3999, 3599, 3173, 2727, 2266, 1797, 1326, 860, 405, -31, -445, -829, -1179, - -1490, -1758, -1979, -2152, -2274, -2346, -2368, -2341, -2268, -2152, -1997, -1808, -1592, -1353, -1099, -837, -575, -320, -80, 137, 325, 477, 587, 649, 658, 611, 504, 336, 105, -187, -540, -952, - -1418, -1935, -2496, -3095, -3725, -4378, -5046, -5719, -6389, -7046, -7680, -8284, -8847, -9362, -9821, -10217, -10543, -10796, -10970, -11062, -11072, -10997, -10840, -10601, -10284, -9893, -9433, -8911, -8334, -7709, -7046, -6353, - -5641, -4919, -4197, -3485, -2792, -2128, -1502, -921, -392, 75, 480, 814, 1077, 1264, 1376, 1413, 1374, 1264, 1085, 843, 541, 186, -214, -654, -1124, -1618, -2127, -2643, -3158, -3665, -4155, -4622, - -5060, -5464, -5828, -6149, -6425, -6652, -6830, -6958, -7038, -7071, -7060, -7006, -6915, -6791, -6638, -6461, -6266, -6058, -5843, -5626, -5411, -5204, -5009, -4828, -4666, -4525, -4405, -4309, -4235, -4183, -4152, -4139, - -4141, -4154, -4175, -4199, -4220, -4234, -4236, -4219, -4179, -4110, -4008, -3869, -3690, -3466, -3196, -2879, -2514, -2101, -1643, -1140, -597, -17, 593, 1230, 1885, 2553, 3224, 3891, 4546, 5180, 5784, 6350, - 6870, 7336, 7741, 8078, 8341, 8527, 8630, 8649, 8581, 8426, 8185, 7860, 7454, 6970, 6415, 5794, 5115, 4385, 3614, 2810, 1984, 1145, 304, -530, -1347, -2138, -2893, -3604, -4264, -4866, -5403, -5873, - -6270, -6592, -6839, -7010, -7106, -7131, -7086, -6978, -6811, -6592, -6328, -6027, -5696, -5345, -4981, -4615, -4253, -3906, -3580, -3283, -3022, -2803, -2631, -2510, -2443, -2432, -2478, -2581, -2741, -2954, -3217, -3526, - -3877, -4262, -4677, -5113, -5564, -6021, -6477, -6923, -7353, -7759, -8134, -8471, -8764, -9008, -9199, -9333, -9408, -9422, -9375, -9266, -9097, -8871, -8589, -8257, -7878, -7457, -7001, -6515, -6006, -5480, -4943, -4402, - -3863, -3332, -2814, -2314, -1837, -1385, -961, -569, -208, 120, 418, 686, 925, 1140, 1332, 1507, 1668, 1822, 1971, 2123, 2281, 2452, 2639, 2848, 3082, 3346, 3641, 3970, 4334, 4734, 5169, 5637, - 6138, 6667, 7220, 7793, 8380, 8976, 9572, 10164, 10742, 11299, 11828, 12321, 12770, 13170, 13512, 13791, 14003, 14142, 14206, 14191, 14097, 13924, 13672, 13344, 12943, 12472, 11939, 11349, 10709, 10028, 9315, 8578, - 7829, 7077, 6332, 5604, 4905, 4242, 3626, 3066, 2568, 2140, 1788, 1517, 1331, 1231, 1219, 1296, 1460, 1708, 2036, 2440, 2914, 3451, 4044, 4683, 5360, 6066, 6790, 7523, 8255, 8977, 9678, 10351, - 10987, 11578, 12117, 12600, 13021, 13376, 13663, 13879, 14026, 14103, 14112, 14057, 13939, 13765, 13540, 13268, 12958, 12615, 12248, 11862, 11466, 11066, 10669, 10282, 9910, 9559, 9234, 8938, 8675, 8446, 8252, 8095, - 7973, 7886, 7831, 7805, 7804, 7824, 7862, 7910, 7965, 8021, 8072, 8112, 8137, 8142, 8122, 8073, 7992, 7876, 7724, 7534, 7306, 7041, 6740, 6406, 6042, 5651, 5238, 4808, 4367, 3920, 3474, 3035, - 2610, 2204, 1825, 1477, 1166, 897, 673, 499, 376, 307, 291, 329, 419, 560, 747, 976, 1244, 1544, 1870, 2215, 2572, 2933, 3290, 3635, 3961, 4260, 4524, 4748, 4925, 5049, 5116, 5122, - 5066, 4944, 4757, 4506, 4192, 3818, 3388, 2906, 2380, 1814, 1218, 598, -35, -675, -1311, -1935, -2538, -3110, -3644, -4131, -4565, -4939, -5248, -5487, -5652, -5743, -5757, -5696, -5559, -5351, -5075, -4735, - -4338, -3890, -3399, -2874, -2323, -1756, -1183, -613, -56, 478, 980, 1441, 1854, 2210, 2503, 2727, 2878, 2951, 2945, 2859, 2692, 2445, 2120, 1721, 1252, 719, 127, -516, -1205, -1930, -2684, -3457, - -4243, -5031, -5814, -6585, -7335, -8058, -8748, -9400, -10008, -10569, -11081, -11542, -11950, -12306, -12611, -12866, -13074, -13238, -13362, -13450, -13506, -13536, -13545, -13538, -13519, -13495, -13468, -13444, -13425, -13416, -13417, -13432, - -13460, -13501, -13556, -13622, -13698, -13781, -13866, -13950, -14029, -14097, -14149, -14180, -14184, -14157, -14092, -13985, -13832, -13628, -13371, -13058, -12688, -12259, -11771, -11227, -10627, -9975, -9275, -8532, -7751, -6939, -6103, -5251, - -4391, -3532, -2681, -1849, -1044, -275, 450, 1124, 1740, 2291, 2771, 3175, 3502, 3747, 3909, 3989, 3987, 3906, 3748, 3519, 3224, 2869, 2462, 2010, 1522, 1008, 476, -62, -600, -1126, -1632, -2108, - -2547, -2941, -3283, -3568, -3791, -3948, -4037, -4056, -4006, -3887, -3703, -3455, -3149, -2791, -2386, -1942, -1466, -967, -453, 65, 581, 1085, 1567, 2021, 2437, 2809, 3130, 3395, 3598, 3737, 3809, 3811, - 3745, 3610, 3408, 3143, 2817, 2436, 2005, 1531, 1021, 481, -80, -655, -1235, -1814, -2382, -2933, -3460, -3957, -4417, -4835, -5209, -5534, -5808, -6030, -6200, -6318, -6386, -6405, -6380, -6314, -6212, -6078, - -5919, -5740, -5546, -5346, -5143, -4944, -4756, -4582, -4427, -4297, -4193, -4118, -4076, -4066, -4088, -4143, -4229, -4343, -4482, -4644, -4823, -5015, -5216, -5419, -5619, -5811, -5988, -6146, -6280, -6385, -6456, -6491, - -6487, -6442, -6355, -6225, -6053, -5841, -5592, -5308, -4995, -4656, -4298, -3926, -3549, -3172, -2804, -2451, -2122, -1823, -1562, -1345, -1178, -1068, -1018, -1032, -1113, -1263, -1482, -1770, -2125, -2545, -3025, -3561, - -4147, -4775, -5439, -6129, -6836, -7551, -8265, -8966, -9645, -10292, -10897, -11451, -11946, -12373, -12726, -12998, -13184, -13281, -13285, -13196, -13013, -12737, -12370, -11917, -11381, -10769, -10087, -9344, -8547, -7705, -6829, -5928, - -5013, -4093, -3178, -2279, -1405, -564, 234, 984, 1678, 2311, 2879, 3378, 3806, 4162, 4445, 4657, 4800, 4877, 4893, 4851, 4757, 4618, 4439, 4229, 3993, 3739, 3474, 3205, 2938, 2679, 2434, 2207, - 2003, 1825, 1675, 1556, 1466, 1408, 1378, 1377, 1400, 1444, 1507, 1583, 1667, 1754, 1839, 1917, 1981, 2028, 2051, 2047, 2011, 1940, 1832, 1684, 1495, 1265, 994, 684, 337, -44, -456, -894, - -1352, -1826, -2310, -2796, -3278, -3750, -4204, -4636, -5037, -5403, -5728, -6008, -6239, -6418, -6542, -6611, -6623, -6580, -6483, -6334, -6138, -5898, -5620, -5310, -4973, -4617, -4250, -3878, -3510, -3153, -2814, -2502, - -2222, -1981, -1785, -1638, -1544, -1506, -1526, -1605, -1742, -1937, -2186, -2486, -2833, -3221, -3643, -4093, -4563, -5045, -5529, -6007, -6470, -6909, -7315, -7679, -7995, -8253, -8449, -8576, -8629, -8605, -8502, -8318, - -8053, -7709, -7287, -6792, -6227, -5599, -4915, -4182, -3408, -2602, -1775, -936, -94, 738, 1554, 2342, 3092, 3797, 4447, 5037, 5559, 6007, 6379, 6669, 6877, 7002, 7043, 7003, 6884, 6689, 6422, 6091, - 5699, 5256, 4767, 4241, 3686, 3110, 2521, 1927, 1337, 756, 193, -345, -856, -1333, -1773, -2172, -2529, -2842, -3111, -3337, -3522, -3666, -3775, -3851, -3898, -3920, -3924, -3913, -3893, -3870, -3846, -3829, - -3821, -3826, -3847, -3888, -3949, -4032, -4138, -4264, -4412, -4577, -4757, -4950, -5150, -5353, -5555, -5749, -5931, -6093, -6232, -6341, -6415, -6449, -6439, -6381, -6271, -6108, -5890, -5617, -5288, -4905, -4471, -3988, - -3462, -2895, -2296, -1669, -1023, -365, 296, 954, 1598, 2222, 2816, 3372, 3883, 4341, 4741, 5077, 5343, 5537, 5656, 5698, 5664, 5553, 5368, 5112, 4789, 4405, 3966, 3479, 2953, 2394, 1814, 1221, - 625, 35, -537, -1084, -1596, -2065, -2482, -2841, -3135, -3359, -3510, -3583, -3579, -3495, -3332, -3093, -2781, -2399, -1953, -1449, -893, -294, 339, 1000, 1678, 2364, 3050, 3726, 4383, 5014, 5609, 6164, - 6670, 7123, 7519, 7854, 8125, 8332, 8475, 8555, 8573, 8533, 8439, 8296, 8109, 7885, 7630, 7352, 7058, 6755, 6452, 6155, 5872, 5610, 5374, 5170, 5003, 4878, 4796, 4761, 4774, 4835, 4943, 5098, - 5296, 5534, 5809, 6116, 6449, 6803, 7172, 7550, 7930, 8306, 8671, 9020, 9347, 9647, 9916, 10148, 10343, 10496, 10607, 10674, 10699, 10681, 10623, 10527, 10396, 10235, 10048, 9839, 9615, 9380, 9140, 8900, - 8667, 8445, 8239, 8053, 7892, 7759, 7655, 7583, 7543, 7535, 7557, 7609, 7687, 7788, 7906, 8037, 8176, 8315, 8449, 8569, 8670, 8743, 8781, 8779, 8728, 8624, 8461, 8234, 7940, 7576, 7140, 6631, - 6050, 5398, 4679, 3895, 3053, 2157, 1215, 235, -773, -1803, -2844, -3885, -4916, -5926, -6906, -7844, -8732, -9559, -10318, -11000, -11598, -12108, -12523, -12841, -13059, -13177, -13193, -13111, -12933, -12662, -12304, -11865, - -11353, -10774, -10139, -9455, -8733, -7982, -7213, -6435, -5658, -4892, -4146, -3428, -2745, -2105, -1512, -973, -491, -68, 293, 594, 833, 1013, 1137, 1207, 1229, 1208, 1148, 1057, 941, 807, 661, 511, - 364, 225, 101, -2, -81, -130, -147, -129, -74, 17, 146, 312, 513, 745, 1006, 1291, 1596, 1916, 2246, 2579, 2910, 3234, 3544, 3836, 4104, 4343, 4550, 4722, 4855, 4948, 5000, 5010, - 4980, 4910, 4804, 4664, 4494, 4299, 4083, 3853, 3615, 3374, 3137, 2911, 2701, 2514, 2355, 2230, 2144, 2100, 2101, 2150, 2249, 2397, 2595, 2841, 3134, 3468, 3842, 4249, 4684, 5141, 5613, 6092, - 6572, 7044, 7500, 7934, 8338, 8705, 9028, 9302, 9521, 9682, 9782, 9817, 9788, 9693, 9534, 9314, 9034, 8701, 8319, 7893, 7432, 6943, 6435, 5915, 5394, 4879, 4382, 3910, 3473, 3079, 2735, 2450, - 2228, 2076, 1998, 1996, 2074, 2231, 2468, 2782, 3171, 3631, 4156, 4741, 5379, 6062, 6781, 7526, 8290, 9061, 9830, 10587, 11323, 12027, 12692, 13309, 13870, 14370, 14801, 15161, 15445, 15650, 15776, 15823, - 15792, 15684, 15503, 15254, 14941, 14571, 14150, 13685, 13184, 12656, 12107, 11548, 10984, 10425, 9878, 9349, 8846, 8373, 7936, 7539, 7186, 6877, 6616, 6401, 6233, 6111, 6031, 5992, 5990, 6020, 6078, 6159, - 6258, 6369, 6487, 6608, 6725, 6834, 6931, 7013, 7076, 7118, 7138, 7134, 7106, 7055, 6984, 6893, 6785, 6666, 6537, 6405, 6274, 6149, 6036, 5940, 5867, 5821, 5807, 5830, 5894, 6002, 6155, 6357, - 6607, 6905, 7250, 7640, 8073, 8543, 9047, 9578, 10131, 10698, 11271, 11842, 12404, 12948, 13464, 13946, 14383, 14769, 15097, 15359, 15550, 15665, 15700, 15651, 15518, 15298, 14993, 14605, 14136, 13590, 12972, 12288, - 11546, 10753, 9917, 9049, 8156, 7250, 6341, 5437, 4550, 3688, 2861, 2077, 1344, 668, 56, -486, -957, -1354, -1674, -1918, -2087, -2183, -2209, -2170, -2070, -1917, -1717, -1479, -1209, -917, -611, -300, - 5, 300, 574, 820, 1030, 1200, 1324, 1396, 1414, 1376, 1279, 1125, 913, 647, 328, -38, -448, -896, -1375, -1879, -2400, -2930, -3462, -3989, -4501, -4993, -5456, -5885, -6273, -6615, -6906, -7144, - -7324, -7446, -7509, -7512, -7457, -7346, -7182, -6968, -6709, -6411, -6077, -5716, -5332, -4932, -4523, -4111, -3703, -3304, -2920, -2556, -2215, -1903, -1621, -1373, -1158, -978, -833, -720, -639, -586, -558, -552, - -562, -585, -614, -646, -674, -693, -699, -687, -653, -593, -503, -383, -230, -44, 175, 426, 708, 1016, 1348, 1699, 2063, 2435, 2808, 3175, 3529, 3861, 4166, 4435, 4660, 4835, 4954, 5009, - 4997, 4912, 4751, 4512, 4193, 3794, 3316, 2760, 2131, 1431, 667, -154, -1027, -1942, -2892, -3866, -4854, -5847, -6833, -7803, -8747, -9653, -10514, -11320, -12063, -12736, -13332, -13847, -14276, -14616, -14866, -15026, - -15096, -15078, -14977, -14796, -14542, -14220, -13838, -13404, -12928, -12418, -11883, -11335, -10782, -10233, -9700, -9189, -8709, -8269, -7875, -7532, -7246, -7020, -6858, -6760, -6729, -6762, -6858, -7015, -7229, -7496, -7810, -8165, - -8556, -8975, -9415, -9868, -10328, -10788, -11240, -11677, -12094, -12486, -12847, -13173, -13462, -13710, -13917, -14082, -14204, -14286, -14329, -14336, -14311, -14258, -14180, -14084, -13974, -13857, -13737, -13620, -13511, -13416, -13338, -13282, - -13252, -13250, -13278, -13338, -13429, -13552, -13705, -13886, -14093, -14321, -14566, -14824, -15089, -15355, -15616, -15866, -16098, -16307, -16485, -16628, -16731, -16787, -16794, -16747, -16645, -16485, -16267, -15991, -15659, -15272, -14833, -14347, - -13818, -13252, -12656, -12035, -11399, -10753, -10107, -9468, -8844, -8243, -7672, -7139, -6650, -6210, -5824, -5496, -5230, -5026, -4887, -4811, -4797, -4842, -4944, -5097, -5297, -5536, -5807, -6103, -6415, -6735, -7054, -7362, - -7652, -7914, -8139, -8321, -8451, -8525, -8535, -8478, -8350, -8149, -7874, -7525, -7103, -6611, -6052, -5432, -4755, -4030, -3263, -2463, -1638, -799, 44, 883, 1707, 2507, 3272, 3995, 4666, 5278, 5823, 6297, - 6693, 7009, 7241, 7387, 7448, 7424, 7318, 7131, 6868, 6534, 6136, 5678, 5170, 4618, 4030, 3416, 2782, 2139, 1493, 853, 227, -379, -959, -1507, -2019, -2490, -2918, -3300, -3636, -3925, -4168, -4367, - -4524, -4642, -4724, -4775, -4800, -4803, -4789, -4763, -4731, -4696, -4665, -4640, -4626, -4625, -4640, -4672, -4724, -4794, -4882, -4987, -5106, -5238, -5377, -5521, -5665, -5803, -5931, -6043, -6134, -6197, -6228, -6222, - -6174, -6080, -5937, -5741, -5492, -5187, -4827, -4412, -3944, -3427, -2863, -2257, -1614, -942, -246, 465, 1184, 1903, 2613, 3304, 3970, 4599, 5186, 5722, 6199, 6612, 6954, 7222, 7411, 7520, 7545, 7488, - 7349, 7131, 6836, 6468, 6034, 5540, 4992, 4400, 3771, 3114, 2439, 1756, 1075, 405, -244, -864, -1447, -1984, -2468, -2894, -3256, -3551, -3775, -3928, -4008, -4017, -3956, -3829, -3639, -3393, -3096, -2754, - -2376, -1970, -1545, -1108, -669, -238, 177, 569, 930, 1251, 1526, 1750, 1917, 2024, 2069, 2048, 1963, 1813, 1599, 1325, 995, 612, 183, -287, -791, -1321, -1871, -2432, -2995, -3554, -4101, -4628, - -5127, -5594, -6021, -6404, -6738, -7021, -7249, -7422, -7539, -7600, -7607, -7561, -7467, -7327, -7147, -6931, -6685, -6414, -6125, -5825, -5518, -5211, -4911, -4621, -4347, -4093, -3863, -3660, -3486, -3341, -3228, -3144, - -3090, -3062, -3059, -3077, -3112, -3159, -3214, -3272, -3326, -3371, -3403, -3415, -3403, -3362, -3288, -3177, -3027, -2836, -2603, -2327, -2009, -1651, -1254, -823, -361, 126, 634, 1155, 1683, 2211, 2732, 3237, - 3719, 4171, 4586, 4955, 5275, 5537, 5739, 5875, 5943, 5940, 5867, 5722, 5507, 5225, 4879, 4475, 4017, 3512, 2968, 2393, 1796, 1187, 575, -29, -616, -1177, -1701, -2178, -2601, -2961, -3251, -3464, - -3596, -3643, -3601, -3468, -3246, -2933, -2533, -2048, -1484, -845, -140, 625, 1443, 2303, 3197, 4113, 5043, 5976, 6901, 7810, 8692, 9539, 10343, 11095, 11789, 12421, 12984, 13475, 13892, 14234, 14501, 14693, - 14812, 14861, 14845, 14767, 14634, 14450, 14224, 13961, 13669, 13355, 13025, 12688, 12348, 12014, 11690, 11381, 11091, 10825, 10585, 10372, 10188, 10032, 9905, 9803, 9726, 9669, 9629, 9602, 9583, 9567, 9549, 9523, - 9484, 9427, 9347, 9239, 9100, 8926, 8714, 8463, 8170, 7836, 7461, 7046, 6593, 6106, 5587, 5041, 4474, 3891, 3298, 2701, 2108, 1524, 956, 412, -102, -582, -1021, -1415, -1759, -2050, -2286, -2465, - -2586, -2651, -2659, -2614, -2519, -2378, -2196, -1978, -1731, -1462, -1178, -886, -594, -310, -41, 205, 422, 604, 744, 838, 882, 873, 808, 686, 509, 276, -9, -344, -725, -1146, -1600, -2081, - -2582, -3093, -3606, -4113, -4604, -5071, -5504, -5895, -6236, -6520, -6740, -6890, -6965, -6962, -6878, -6711, -6461, -6130, -5718, -5231, -4671, -4045, -3360, -2622, -1841, -1025, -183, 673, 1535, 2393, 3236, 4054, - 4838, 5579, 6269, 6899, 7464, 7958, 8376, 8714, 8971, 9145, 9236, 9245, 9174, 9028, 8810, 8525, 8179, 7780, 7333, 6848, 6332, 5792, 5238, 4677, 4117, 3566, 3029, 2514, 2026, 1570, 1149, 768, - 427, 129, -126, -340, -513, -647, -746, -813, -852, -868, -865, -849, -825, -798, -772, -753, -745, -751, -776, -822, -890, -982, -1099, -1240, -1404, -1589, -1792, -2011, -2240, -2477, -2715, -2950, - -3176, -3387, -3577, -3742, -3875, -3972, -4028, -4040, -4004, -3917, -3779, -3587, -3343, -3047, -2702, -2310, -1876, -1404, -900, -371, 176, 735, 1298, 1855, 2398, 2920, 3411, 3864, 4270, 4624, 4917, 5146, - 5304, 5389, 5397, 5326, 5177, 4950, 4646, 4269, 3823, 3313, 2746, 2127, 1466, 771, 51, -684, -1426, -2164, -2888, -3588, -4256, -4883, -5459, -5980, -6436, -6824, -7139, -7378, -7538, -7618, -7619, -7543, - -7392, -7170, -6882, -6534, -6132, -5684, -5198, -4682, -4146, -3597, -3047, -2502, -1973, -1467, -993, -556, -164, 177, 464, 694, 863, 971, 1018, 1003, 930, 800, 619, 391, 121, -184, -518, -873, - -1242, -1617, -1991, -2356, -2705, -3031, -3328, -3589, -3810, -3987, -4116, -4195, -4221, -4196, -4118, -3989, -3812, -3589, -3324, -3022, -2688, -2327, -1946, -1550, -1145, -740, -338, 52, 427, 780, 1107, 1405, - 1669, 1897, 2087, 2239, 2352, 2428, 2467, 2472, 2447, 2395, 2320, 2228, 2122, 2009, 1895, 1784, 1683, 1596, 1528, 1484, 1468, 1482, 1530, 1612, 1730, 1884, 2073, 2294, 2546, 2824, 3124, 3442, - 3770, 4104, 4435, 4758, 5065, 5348, 5601, 5816, 5986, 6106, 6170, 6172, 6110, 5980, 5779, 5507, 5164, 4751, 4270, 3726, 3122, 2464, 1759, 1014, 238, -559, -1370, -2183, -2989, -3777, -4537, -5258, - -5932, -6548, -7098, -7574, -7969, -8278, -8495, -8618, -8643, -8569, -8397, -8128, -7764, -7310, -6770, -6151, -5460, -4704, -3892, -3034, -2139, -1217, -278, 667, 1609, 2539, 3446, 4323, 5162, 5955, 6697, 7381, - 8005, 8566, 9060, 9488, 9850, 10147, 10381, 10557, 10677, 10748, 10774, 10762, 10718, 10648, 10561, 10462, 10358, 10256, 10161, 10080, 10017, 9977, 9962, 9976, 10020, 10096, 10203, 10341, 10507, 10700, 10916, 11151, - 11401, 11661, 11925, 12188, 12445, 12688, 12914, 13115, 13288, 13428, 13530, 13592, 13610, 13582, 13507, 13386, 13218, 13005, 12749, 12454, 12122, 11760, 11370, 10961, 10536, 10103, 9668, 9237, 8817, 8415, 8035, 7684, - 7366, 7085, 6845, 6649, 6499, 6395, 6337, 6324, 6354, 6424, 6531, 6670, 6835, 7020, 7219, 7425, 7630, 7828, 8009, 8167, 8295, 8385, 8432, 8429, 8371, 8255, 8077, 7834, 7526, 7153, 6714, 6213, - 5653, 5038, 4372, 3664, 2918, 2143, 1348, 541, -267, -1070, -1857, -2618, -3344, -4028, -4660, -5234, -5743, -6181, -6544, -6828, -7030, -7150, -7188, -7145, -7023, -6826, -6559, -6228, -5839, -5399, -4919, -4405, - -3868, -3317, -2762, -2213, -1680, -1171, -695, -261, 122, 450, 717, 917, 1046, 1103, 1086, 995, 830, 594, 289, -79, -507, -989, -1517, -2084, -2684, -3308, -3947, -4594, -5241, -5879, -6501, -7100, - -7669, -8203, -8696, -9145, -9545, -9894, -10192, -10437, -10629, -10770, -10862, -10907, -10910, -10873, -10802, -10702, -10578, -10435, -10279, -10114, -9947, -9781, -9621, -9472, -9335, -9214, -9111, -9026, -8961, -8914, -8884, -8869, - -8867, -8873, -8885, -8898, -8907, -8906, -8891, -8856, -8796, -8706, -8581, -8417, -8209, -7955, -7652, -7299, -6895, -6439, -5933, -5380, -4780, -4140, -3463, -2755, -2023, -1273, -513, 248, 1003, 1743, 2460, 3144, - 3787, 4381, 4919, 5394, 5800, 6131, 6383, 6553, 6639, 6639, 6554, 6385, 6134, 5806, 5404, 4935, 4406, 3824, 3197, 2535, 1846, 1141, 429, -279, -974, -1647, -2288, -2888, -3441, -3939, -4375, -4746, - -5047, -5275, -5429, -5508, -5513, -5447, -5313, -5114, -4855, -4544, -4187, -3792, -3366, -2918, -2457, -1992, -1532, -1084, -658, -261, 100, 419, 689, 908, 1070, 1173, 1217, 1199, 1122, 987, 796, 554, - 265, -64, -429, -822, -1237, -1665, -2099, -2530, -2952, -3357, -3737, -4085, -4397, -4665, -4886, -5055, -5171, -5229, -5231, -5175, -5063, -4895, -4676, -4408, -4096, -3745, -3359, -2945, -2509, -2058, -1598, -1134, - -674, -224, 211, 626, 1018, 1381, 1713, 2012, 2276, 2505, 2699, 2858, 2986, 3084, 3156, 3205, 3237, 3255, 3265, 3272, 3281, 3298, 3328, 3375, 3444, 3538, 3662, 3816, 4003, 4224, 4479, 4766, - 5085, 5433, 5805, 6198, 6606, 7024, 7446, 7864, 8271, 8660, 9023, 9353, 9642, 9884, 10071, 10198, 10259, 10250, 10168, 10008, 9770, 9454, 9060, 8589, 8045, 7431, 6753, 6018, 5232, 4402, 3539, 2651, - 1748, 840, -62, -948, -1809, -2633, -3412, -4136, -4797, -5388, -5903, -6335, -6681, -6938, -7104, -7177, -7160, -7054, -6862, -6588, -6239, -5820, -5338, -4802, -4221, -3604, -2960, -2299, -1630, -965, -311, 320, - 923, 1489, 2010, 2481, 2896, 3251, 3544, 3771, 3933, 4030, 4062, 4033, 3945, 3803, 3612, 3377, 3106, 2804, 2478, 2136, 1785, 1432, 1084, 748, 429, 134, -133, -368, -568, -730, -852, -935, - -977, -981, -947, -880, -781, -656, -509, -345, -169, 13, 196, 375, 543, 695, 828, 936, 1016, 1066, 1082, 1063, 1009, 920, 797, 641, 456, 245, 12, -238, -500, -769, -1037, -1300, - -1551, -1783, -1990, -2167, -2308, -2409, -2464, -2472, -2428, -2331, -2180, -1975, -1718, -1409, -1053, -653, -213, 260, 762, 1285, 1821, 2364, 2905, 3436, 3950, 4438, 4893, 5308, 5677, 5993, 6253, 6451, - 6584, 6652, 6651, 6584, 6450, 6253, 5995, 5682, 5318, 4910, 4465, 3990, 3495, 2987, 2476, 1970, 1480, 1012, 577, 181, -166, -460, -694, -864, -966, -996, -955, -841, -655, -400, -79, 302, - 741, 1229, 1759, 2323, 2912, 3518, 4130, 4738, 5333, 5906, 6446, 6945, 7394, 7786, 8113, 8370, 8552, 8653, 8673, 8608, 8459, 8226, 7911, 7516, 7047, 6507, 5902, 5239, 4526, 3769, 2978, 2160, - 1324, 479, -366, -1206, -2031, -2834, -3609, -4350, -5053, -5712, -6325, -6890, -7405, -7869, -8284, -8650, -8970, -9247, -9483, -9684, -9853, -9995, -10115, -10218, -10309, -10393, -10474, -10558, -10646, -10743, -10851, -10971, - -11106, -11254, -11416, -11591, -11776, -11969, -12166, -12364, -12558, -12742, -12913, -13063, -13189, -13284, -13343, -13361, -13333, -13255, -13124, -12937, -12691, -12385, -12020, -11596, -11114, -10576, -9987, -9351, -8673, -7960, -7217, -6453, - -5676, -4894, -4115, -3349, -2604, -1889, -1211, -580, -3, 514, 966, 1347, 1653, 1881, 2030, 2099, 2089, 2001, 1838, 1605, 1306, 947, 536, 80, -411, -931, -1470, -2017, -2562, -3098, -3614, -4100, - -4550, -4955, -5308, -5603, -5835, -6000, -6094, -6117, -6068, -5947, -5757, -5500, -5180, -4803, -4374, -3901, -3391, -2852, -2293, -1723, -1150, -585, -35, 490, 984, 1437, 1844, 2198, 2494, 2729, 2898, 3000, - 3034, 3000, 2900, 2736, 2510, 2229, 1896, 1518, 1100, 652, 179, -310, -808, -1307, -1799, -2276, -2732, -3160, -3554, -3909, -4221, -4486, -4702, -4867, -4980, -5042, -5054, -5018, -4937, -4815, -4656, -4465, - -4247, -4009, -3756, -3495, -3231, -2971, -2720, -2485, -2269, -2077, -1913, -1780, -1681, -1616, -1586, -1591, -1630, -1701, -1802, -1929, -2078, -2245, -2425, -2612, -2802, -2988, -3166, -3329, -3473, -3592, -3683, -3742, - -3766, -3753, -3701, -3610, -3480, -3312, -3109, -2873, -2608, -2320, -2014, -1694, -1369, -1046, -730, -431, -155, 89, 296, 458, 569, 623, 616, 542, 400, 187, -97, -454, -882, -1379, -1940, -2561, - -3237, -3961, -4726, -5523, -6343, -7177, -8015, -8847, -9663, -10452, -11205, -11913, -12566, -13156, -13676, -14119, -14479, -14752, -14935, -15025, -15023, -14927, -14741, -14467, -14110, -13674, -13166, -12593, -11963, -11286, -10569, -9824, - -9060, -8287, -7515, -6754, -6014, -5302, -4629, -4001, -3424, -2906, -2450, -2060, -1739, -1488, -1308, -1198, -1156, -1179, -1263, -1405, -1598, -1837, -2115, -2425, -2760, -3114, -3478, -3847, -4212, -4569, -4911, -5232, - -5530, -5799, -6037, -6241, -6412, -6549, -6651, -6721, -6762, -6775, -6764, -6735, -6691, -6637, -6580, -6523, -6472, -6434, -6412, -6411, -6436, -6490, -6576, -6696, -6852, -7044, -7273, -7538, -7835, -8163, -8519, -8897, - -9294, -9704, -10120, -10537, -10948, -11347, -11726, -12080, -12402, -12686, -12927, -13119, -13259, -13344, -13371, -13337, -13244, -13090, -12877, -12608, -12285, -11913, -11496, -11041, -10552, -10039, -9506, -8963, -8417, -7875, -7345, -6836, - -6353, -5903, -5493, -5127, -4810, -4545, -4335, -4181, -4083, -4040, -4051, -4112, -4219, -4368, -4553, -4766, -5000, -5248, -5501, -5749, -5985, -6199, -6383, -6528, -6625, -6669, -6651, -6566, -6409, -6177, -5865, -5474, - -5001, -4449, -3819, -3114, -2339, -1499, -601, 348, 1340, 2366, 3416, 4481, 5550, 6614, 7661, 8683, 9669, 10610, 11498, 12325, 13084, 13770, 14377, 14902, 15342, 15696, 15963, 16144, 16242, 16259, 16200, 16070, - 15874, 15618, 15312, 14960, 14573, 14157, 13721, 13273, 12821, 12372, 11933, 11511, 11112, 10739, 10399, 10093, 9824, 9594, 9403, 9251, 9136, 9057, 9011, 8995, 9004, 9034, 9081, 9139, 9203, 9268, 9330, 9383, - 9423, 9447, 9450, 9431, 9386, 9315, 9217, 9093, 8943, 8770, 8575, 8362, 8135, 7899, 7658, 7417, 7183, 6961, 6756, 6575, 6422, 6304, 6223, 6186, 6195, 6253, 6361, 6521, 6733, 6996, 7309, 7667, - 8069, 8508, 8981, 9479, 9998, 10529, 11064, 11595, 12115, 12614, 13084, 13518, 13907, 14244, 14523, 14739, 14885, 14958, 14956, 14876, 14718, 14482, 14170, 13785, 13331, 12812, 12235, 11606, 10934, 10227, 9494, 8744, - 7987, 7233, 6492, 5774, 5088, 4442, 3846, 3305, 2828, 2420, 2086, 1828, 1649, 1551, 1533, 1594, 1732, 1942, 2221, 2562, 2959, 3405, 3892, 4410, 4952, 5509, 6070, 6628, 7173, 7698, 8193, 8652, - 9068, 9436, 9751, 10009, 10208, 10346, 10423, 10438, 10395, 10296, 10144, 9944, 9701, 9421, 9112, 8780, 8432, 8076, 7720, 7371, 7037, 6724, 6438, 6186, 5972, 5802, 5677, 5602, 5578, 5605, 5683, 5812, - 5988, 6209, 6472, 6772, 7103, 7461, 7839, 8231, 8631, 9032, 9428, 9813, 10181, 10526, 10845, 11132, 11383, 11598, 11772, 11906, 11999, 12051, 12064, 12040, 11983, 11895, 11780, 11645, 11492, 11329, 11159, 10990, - 10826, 10672, 10534, 10415, 10320, 10253, 10215, 10209, 10235, 10294, 10384, 10505, 10653, 10825, 11018, 11225, 11441, 11660, 11876, 12081, 12268, 12430, 12559, 12649, 12693, 12684, 12617, 12487, 12289, 12021, 11680, 11264, - 10775, 10212, 9579, 8877, 8113, 7291, 6417, 5500, 4548, 3569, 2572, 1569, 569, -416, -1379, -2307, -3191, -4021, -4789, -5486, -6105, -6641, -7088, -7442, -7700, -7860, -7924, -7891, -7764, -7546, -7243, -6860, - -6404, -5882, -5303, -4676, -4010, -3316, -2602, -1880, -1159, -448, 241, 903, 1529, 2112, 2645, 3123, 3542, 3899, 4192, 4420, 4584, 4683, 4722, 4702, 4629, 4507, 4341, 4138, 3904, 3647, 3372, 3087, - 2799, 2514, 2239, 1979, 1740, 1526, 1340, 1187, 1066, 981, 932, 916, 935, 984, 1061, 1162, 1284, 1420, 1567, 1718, 1868, 2011, 2142, 2256, 2346, 2409, 2440, 2436, 2394, 2312, 2189, 2024, - 1817, 1571, 1286, 967, 616, 239, -160, -576, -1002, -1432, -1860, -2280, -2684, -3067, -3422, -3745, -4029, -4271, -4468, -4615, -4713, -4758, -4752, -4694, -4588, -4436, -4241, -4008, -3742, -3449, -3137, -2810, - -2479, -2148, -1828, -1524, -1244, -996, -785, -619, -501, -437, -431, -484, -598, -774, -1011, -1308, -1660, -2065, -2518, -3012, -3540, -4097, -4672, -5258, -5845, -6425, -6988, -7524, -8026, -8484, -8890, -9237, - -9518, -9728, -9862, -9916, -9888, -9776, -9580, -9303, -8945, -8511, -8005, -7433, -6802, -6119, -5392, -4632, -3846, -3045, -2238, -1436, -649, 114, 845, 1534, 2174, 2758, 3279, 3733, 4115, 4423, 4655, 4810, - 4889, 4893, 4826, 4691, 4492, 4236, 3929, 3577, 3189, 2771, 2333, 1881, 1425, 971, 528, 103, -298, -669, -1005, -1302, -1555, -1763, -1924, -2038, -2103, -2122, -2096, -2029, -1923, -1782, -1612, -1416, - -1201, -972, -733, -492, -251, -17, 204, 412, 602, 770, 916, 1037, 1133, 1205, 1253, 1280, 1289, 1281, 1262, 1235, 1205, 1178, 1159, 1152, 1164, 1199, 1263, 1360, 1493, 1667, 1884, 2145, - 2452, 2805, 3204, 3645, 4128, 4647, 5199, 5778, 6378, 6992, 7613, 8233, 8844, 9436, 10002, 10533, 11021, 11458, 11835, 12147, 12387, 12550, 12631, 12627, 12535, 12355, 12087, 11731, 11290, 10767, 10168, 9498, - 8764, 7972, 7133, 6253, 5344, 4415, 3475, 2535, 1605, 694, -187, -1031, -1829, -2575, -3261, -3883, -4436, -4917, -5324, -5656, -5914, -6100, -6216, -6267, -6257, -6191, -6077, -5923, -5735, -5522, -5293, -5057, - -4822, -4596, -4389, -4206, -4057, -3946, -3881, -3864, -3901, -3993, -4143, -4350, -4615, -4935, -5308, -5730, -6197, -6703, -7242, -7807, -8391, -8986, -9585, -10180, -10762, -11325, -11860, -12362, -12823, -13238, -13603, -13912, - -14163, -14355, -14484, -14552, -14558, -14505, -14395, -14231, -14017, -13758, -13460, -13128, -12767, -12386, -11989, -11584, -11176, -10773, -10378, -9998, -9637, -9298, -8986, -8702, -8449, -8226, -8034, -7872, -7738, -7630, -7545, -7479, - -7427, -7386, -7350, -7313, -7272, -7220, -7152, -7065, -6953, -6812, -6640, -6433, -6190, -5911, -5594, -5241, -4853, -4433, -3984, -3512, -3019, -2514, -2001, -1488, -981, -489, -19, 420, 823, 1182, 1490, 1740, - 1927, 2046, 2093, 2066, 1962, 1780, 1521, 1186, 778, 301, -240, -841, -1494, -2190, -2923, -3681, -4457, -5240, -6019, -6785, -7527, -8235, -8899, -9512, -10064, -10548, -10957, -11286, -11530, -11687, -11754, -11731, - -11618, -11416, -11130, -10763, -10320, -9808, -9234, -8605, -7932, -7222, -6486, -5733, -4973, -4217, -3472, -2749, -2057, -1402, -793, -236, 262, 700, 1074, 1380, 1617, 1787, 1889, 1926, 1902, 1820, 1684, 1501, - 1277, 1019, 732, 425, 105, -220, -546, -864, -1168, -1453, -1713, -1945, -2145, -2310, -2439, -2530, -2584, -2601, -2583, -2533, -2454, -2350, -2226, -2085, -1934, -1778, -1622, -1473, -1334, -1212, -1112, -1037, - -991, -978, -999, -1056, -1150, -1282, -1449, -1650, -1883, -2144, -2429, -2733, -3052, -3379, -3709, -4035, -4350, -4649, -4925, -5173, -5386, -5560, -5690, -5772, -5805, -5784, -5710, -5582, -5401, -5168, -4886, -4559, - -4191, -3787, -3354, -2898, -2425, -1944, -1463, -988, -529, -93, 312, 681, 1006, 1281, 1501, 1662, 1761, 1795, 1764, 1667, 1506, 1282, 999, 661, 274, -156, -623, -1118, -1635, -2163, -2694, -3218, - -3728, -4213, -4665, -5077, -5440, -5747, -5994, -6174, -6284, -6321, -6283, -6170, -5983, -5723, -5393, -4998, -4543, -4035, -3481, -2888, -2265, -1621, -967, -312, 334, 963, 1564, 2128, 2646, 3111, 3516, 3853, - 4119, 4308, 4417, 4445, 4391, 4255, 4039, 3745, 3377, 2940, 2439, 1880, 1272, 620, -65, -777, -1508, -2248, -2989, -3723, -4442, -5140, -5810, -6445, -7042, -7595, -8101, -8558, -8965, -9321, -9626, -9881, - -10089, -10253, -10375, -10460, -10512, -10536, -10538, -10521, -10491, -10453, -10412, -10372, -10338, -10311, -10295, -10293, -10305, -10331, -10373, -10428, -10494, -10571, -10653, -10738, -10821, -10897, -10961, -11008, -11033, -11030, -10994, -10919, - -10801, -10636, -10420, -10151, -9825, -9442, -9001, -8503, -7949, -7342, -6685, -5982, -5238, -4460, -3654, -2828, -1990, -1147, -309, 515, 1317, 2089, 2821, 3507, 4137, 4706, 5207, 5635, 5986, 6258, 6447, 6554, - 6578, 6521, 6386, 6176, 5897, 5554, 5155, 4706, 4216, 3694, 3150, 2592, 2031, 1475, 935, 420, -62, -504, -897, -1236, -1515, -1730, -1878, -1957, -1965, -1904, -1774, -1579, -1323, -1010, -646, -237, - 207, 681, 1176, 1682, 2190, 2693, 3180, 3644, 4076, 4469, 4815, 5109, 5346, 5520, 5630, 5672, 5646, 5552, 5392, 5166, 4879, 4535, 4138, 3695, 3213, 2698, 2158, 1600, 1034, 466, -95, -642, - -1168, -1666, -2130, -2555, -2935, -3268, -3550, -3779, -3955, -4078, -4148, -4167, -4139, -4066, -3952, -3804, -3625, -3421, -3199, -2965, -2724, -2482, -2246, -2020, -1811, -1621, -1455, -1316, -1207, -1128, -1081, -1066, - -1081, -1125, -1195, -1289, -1401, -1529, -1667, -1809, -1950, -2086, -2209, -2316, -2400, -2456, -2481, -2471, -2421, -2331, -2199, -2022, -1803, -1542, -1241, -902, -530, -129, 294, 736, 1189, 1647, 2101, 2544, - 2969, 3369, 3736, 4063, 4343, 4572, 4743, 4852, 4896, 4873, 4782, 4621, 4393, 4098, 3741, 3325, 2857, 2342, 1787, 1201, 592, -30, -656, -1276, -1880, -2457, -2999, -3494, -3936, -4314, -4623, -4854, - -5002, -5064, -5034, -4912, -4697, -4387, -3987, -3497, -2922, -2268, -1541, -748, 103, 1004, 1944, 2915, 3906, 4906, 5907, 6897, 7867, 8808, 9711, 10569, 11373, 12119, 12802, 13416, 13959, 14430, 14828, 15152, - 15405, 15589, 15707, 15764, 15765, 15716, 15622, 15491, 15329, 15144, 14942, 14730, 14516, 14304, 14102, 13915, 13746, 13600, 13479, 13386, 13321, 13286, 13279, 13300, 13345, 13413, 13499, 13599, 13709, 13823, 13937, 14045, - 14141, 14220, 14277, 14308, 14308, 14272, 14199, 14085, 13930, 13731, 13489, 13205, 12881, 12518, 12121, 11694, 11240, 10766, 10277, 9780, 9280, 8785, 8302, 7836, 7394, 6982, 6606, 6270, 5979, 5737, 5547, 5409, - 5325, 5296, 5319, 5393, 5515, 5682, 5888, 6128, 6396, 6685, 6988, 7299, 7608, 7909, 8193, 8454, 8684, 8878, 9027, 9129, 9177, 9170, 9103, 8977, 8789, 8542, 8237, 7876, 7465, 7007, 6509, 5977, - 5419, 4843, 4257, 3671, 3092, 2531, 1996, 1496, 1039, 632, 284, 0, -214, -356, -421, -407, -313, -140, 109, 434, 830, 1291, 1812, 2385, 3002, 3655, 4335, 5032, 5736, 6437, 7124, 7789, - 8421, 9011, 9551, 10034, 10451, 10798, 11069, 11260, 11370, 11395, 11337, 11195, 10971, 10670, 10294, 9848, 9339, 8773, 8157, 7499, 6807, 6088, 5352, 4606, 3858, 3116, 2388, 1679, 996, 344, -272, -850, - -1387, -1880, -2329, -2734, -3096, -3418, -3700, -3947, -4163, -4351, -4516, -4664, -4799, -4927, -5052, -5178, -5312, -5456, -5613, -5788, -5981, -6195, -6430, -6687, -6963, -7258, -7569, -7893, -8226, -8564, -8902, -9236, - -9558, -9864, -10148, -10404, -10627, -10812, -10953, -11048, -11091, -11081, -11015, -10893, -10713, -10477, -10187, -9844, -9453, -9018, -8545, -8039, -7507, -6958, -6397, -5835, -5278, -4736, -4217, -3729, -3280, -2878, -2529, -2238, - -2013, -1856, -1770, -1759, -1823, -1961, -2173, -2457, -2807, -3220, -3690, -4210, -4773, -5370, -5993, -6632, -7278, -7921, -8551, -9159, -9735, -10271, -10757, -11188, -11555, -11853, -12077, -12224, -12292, -12278, -12183, -12009, - -11756, -11430, -11034, -10575, -10058, -9490, -8881, -8238, -7569, -6885, -6195, -5506, -4830, -4173, -3544, -2951, -2401, -1898, -1449, -1056, -724, -454, -246, -100, -16, 10, -17, -96, -220, -384, -580, -802, - -1043, -1295, -1551, -1803, -2044, -2266, -2465, -2632, -2764, -2855, -2903, -2904, -2856, -2759, -2613, -2418, -2177, -1891, -1566, -1205, -813, -396, 41, 491, 949, 1407, 1860, 2301, 2724, 3124, 3496, 3836, - 4140, 4405, 4629, 4810, 4950, 5047, 5103, 5120, 5102, 5051, 4972, 4869, 4747, 4612, 4469, 4324, 4182, 4048, 3929, 3827, 3748, 3695, 3670, 3676, 3714, 3785, 3887, 4019, 4179, 4364, 4570, 4791, - 5023, 5259, 5493, 5718, 5927, 6113, 6268, 6385, 6458, 6481, 6446, 6351, 6189, 5958, 5656, 5281, 4832, 4311, 3720, 3061, 2340, 1561, 731, -142, -1052, -1988, -2942, -3904, -4862, -5808, -6729, -7617, - -8460, -9250, -9978, -10634, -11212, -11706, -12109, -12418, -12630, -12742, -12755, -12670, -12488, -12212, -11848, -11400, -10875, -10281, -9627, -8920, -8170, -7388, -6582, -5764, -4943, -4128, -3330, -2556, -1814, -1113, -458, 144, - 690, 1177, 1602, 1964, 2263, 2500, 2677, 2796, 2863, 2882, 2857, 2796, 2704, 2588, 2454, 2311, 2165, 2021, 1888, 1769, 1671, 1599, 1555, 1543, 1566, 1623, 1717, 1846, 2009, 2204, 2428, 2678, - 2948, 3235, 3532, 3836, 4139, 4436, 4722, 4990, 5236, 5455, 5643, 5795, 5909, 5982, 6013, 6001, 5947, 5851, 5715, 5542, 5336, 5100, 4840, 4560, 4268, 3967, 3666, 3370, 3086, 2820, 2578, 2366, - 2188, 2049, 1953, 1902, 1899, 1945, 2039, 2182, 2372, 2605, 2878, 3188, 3527, 3892, 4275, 4670, 5068, 5462, 5846, 6210, 6548, 6853, 7117, 7334, 7500, 7610, 7659, 7644, 7566, 7421, 7212, 6939, - 6605, 6214, 5771, 5281, 4751, 4188, 3601, 2997, 2386, 1777, 1180, 602, 55, -453, -916, -1325, -1673, -1955, -2164, -2298, -2354, -2330, -2226, -2043, -1782, -1447, -1043, -575, -50, 525, 1143, 1794, - 2469, 3158, 3852, 4541, 5215, 5864, 6479, 7053, 7576, 8042, 8444, 8778, 9040, 9225, 9333, 9362, 9314, 9189, 8990, 8721, 8387, 7993, 7546, 7052, 6519, 5954, 5366, 4764, 4154, 3545, 2946, 2362, - 1802, 1271, 774, 316, -98, -467, -788, -1061, -1286, -1462, -1592, -1678, -1724, -1733, -1710, -1659, -1586, -1495, -1392, -1282, -1171, -1064, -964, -876, -803, -747, -712, -698, -706, -736, -786, -855, - -940, -1038, -1146, -1258, -1371, -1478, -1575, -1657, -1717, -1751, -1754, -1720, -1647, -1530, -1366, -1154, -892, -579, -217, 192, 648, 1145, 1681, 2250, 2845, 3460, 4088, 4721, 5351, 5969, 6566, 7135, - 7667, 8153, 8586, 8959, 9266, 9501, 9660, 9738, 9734, 9645, 9473, 9216, 8878, 8463, 7973, 7416, 6796, 6122, 5402, 4645, 3859, 3055, 2242, 1430, 630, -148, -897, -1607, -2270, -2877, -3424, -3905, - -4314, -4649, -4908, -5090, -5195, -5225, -5183, -5072, -4897, -4664, -4379, -4050, -3685, -3293, -2880, -2458, -2034, -1617, -1215, -837, -489, -179, 87, 306, 472, 583, 637, 633, 571, 452, 280, 56, - -212, -523, -870, -1245, -1642, -2054, -2472, -2889, -3298, -3690, -4059, -4397, -4698, -4957, -5169, -5329, -5434, -5483, -5473, -5404, -5278, -5094, -4856, -4568, -4232, -3854, -3440, -2994, -2523, -2033, -1532, -1024, - -518, -18, 467, 936, 1382, 1801, 2190, 2546, 2867, 3152, 3402, 3616, 3796, 3945, 4066, 4161, 4236, 4294, 4341, 4381, 4421, 4464, 4516, 4583, 4667, 4774, 4907, 5068, 5259, 5482, 5737, 6022, - 6338, 6680, 7047, 7434, 7836, 8247, 8661, 9073, 9473, 9856, 10213, 10537, 10820, 11055, 11236, 11356, 11409, 11390, 11296, 11123, 10869, 10534, 10116, 9619, 9043, 8394, 7675, 6892, 6052, 5163, 4233, 3271, - 2287, 1292, 294, -694, -1664, -2605, -3507, -4360, -5158, -5890, -6552, -7136, -7639, -8056, -8385, -8626, -8777, -8841, -8821, -8720, -8543, -8296, -7986, -7621, -7209, -6760, -6282, -5785, -5279, -4774, -4279, -3803, - -3355, -2942, -2573, -2254, -1989, -1784, -1642, -1565, -1555, -1611, -1732, -1917, -2162, -2463, -2816, -3215, -3653, -4125, -4623, -5140, -5669, -6201, -6731, -7252, -7756, -8238, -8693, -9117, -9505, -9855, -10164, -10432, - -10658, -10843, -10988, -11097, -11170, -11214, -11231, -11227, -11206, -11174, -11137, -11099, -11067, -11046, -11040, -11053, -11090, -11153, -11246, -11369, -11524, -11710, -11928, -12175, -12448, -12745, -13062, -13394, -13736, -14083, -14427, -14764, - -15087, -15389, -15664, -15907, -16111, -16271, -16384, -16445, -16451, -16400, -16290, -16121, -15894, -15611, -15273, -14884, -14449, -13973, -13462, -12921, -12359, -11783, -11200, -10618, -10047, -9492, -8963, -8466, -8008, -7595, -7232, -6924, - -6675, -6486, -6360, -6297, -6295, -6354, -6470, -6639, -6856, -7115, -7410, -7733, -8076, -8430, -8787, -9138, -9474, -9786, -10066, -10304, -10495, -10630, -10704, -10712, -10650, -10515, -10304, -10018, -9657, -9222, -8718, -8147, - -7515, -6828, -6094, -5321, -4516, -3690, -2852, -2011, -1178, -362, 425, 1177, 1884, 2538, 3130, 3656, 4108, 4483, 4778, 4989, 5116, 5159, 5118, 4998, 4800, 4529, 4191, 3792, 3338, 2838, 2298, 1727, - 1134, 527, -85, -695, -1296, -1879, -2439, -2968, -3463, -3918, -4331, -4698, -5018, -5291, -5516, -5695, -5829, -5922, -5976, -5996, -5985, -5949, -5892, -5820, -5738, -5650, -5561, -5476, -5399, -5333, -5281, -5245, - -5226, -5226, -5244, -5279, -5329, -5393, -5467, -5547, -5629, -5709, -5781, -5840, -5880, -5897, -5884, -5836, -5750, -5619, -5441, -5212, -4930, -4594, -4202, -3755, -3254, -2702, -2101, -1455, -770, -51, 695, 1461, - 2240, 3023, 3801, 4566, 5309, 6022, 6696, 7323, 7895, 8406, 8849, 9220, 9514, 9727, 9858, 9905, 9870, 9752, 9555, 9283, 8939, 8531, 8064, 7547, 6986, 6393, 5774, 5141, 4502, 3868, 3248, 2651, - 2085, 1560, 1083, 659, 295, -4, -236, -399, -490, -511, -461, -345, -165, 73, 365, 704, 1083, 1493, 1927, 2376, 2831, 3283, 3723, 4142, 4533, 4887, 5198, 5460, 5666, 5813, 5897, 5916, - 5868, 5754, 5574, 5330, 5026, 4665, 4253, 3795, 3298, 2768, 2213, 1640, 1059, 476, -100, -663, -1204, -1717, -2195, -2634, -3028, -3374, -3669, -3910, -4096, -4228, -4306, -4332, -4309, -4239, -4127, -3978, - -3797, -3590, -3362, -3120, -2869, -2617, -2368, -2128, -1903, -1697, -1514, -1357, -1229, -1132, -1066, -1032, -1029, -1055, -1109, -1187, -1285, -1400, -1526, -1659, -1792, -1922, -2041, -2146, -2229, -2288, -2316, -2311, - -2270, -2188, -2066, -1902, -1697, -1450, -1164, -843, -489, -106, 298, 720, 1153, 1589, 2022, 2443, 2846, 3222, 3565, 3867, 4121, 4322, 4465, 4544, 4557, 4500, 4372, 4172, 3902, 3563, 3158, 2691, - 2167, 1593, 975, 321, -359, -1057, -1764, -2470, -3164, -3836, -4476, -5076, -5625, -6116, -6540, -6891, -7163, -7352, -7452, -7463, -7382, -7210, -6948, -6599, -6167, -5656, -5073, -4425, -3718, -2963, -2168, -1343, - -498, 357, 1212, 2058, 2884, 3680, 4439, 5153, 5814, 6416, 6954, 7424, 7822, 8147, 8398, 8575, 8680, 8714, 8682, 8587, 8434, 8230, 7980, 7691, 7371, 7025, 6662, 6289, 5911, 5537, 5171, 4820, - 4487, 4177, 3893, 3638, 3413, 3219, 3054, 2919, 2811, 2728, 2666, 2621, 2589, 2565, 2544, 2520, 2489, 2444, 2381, 2295, 2182, 2038, 1860, 1646, 1393, 1100, 768, 398, -9, -451, -924, -1425, - -1949, -2490, -3043, -3601, -4158, -4707, -5241, -5755, -6241, -6693, -7105, -7473, -7791, -8057, -8266, -8418, -8510, -8543, -8517, -8435, -8298, -8111, -7878, -7604, -7295, -6958, -6600, -6228, -5850, -5473, -5105, -4753, - -4425, -4128, -3866, -3647, -3474, -3350, -3280, -3264, -3303, -3396, -3542, -3739, -3982, -4267, -4588, -4939, -5312, -5699, -6093, -6484, -6863, -7222, -7552, -7843, -8088, -8279, -8409, -8471, -8460, -8372, -8203, -7952, - -7617, -7198, -6697, -6117, -5462, -4736, -3946, -3099, -2202, -1264, -295, 696, 1699, 2704, 3700, 4678, 5628, 6540, 7406, 8217, 8967, 9649, 10258, 10789, 11240, 11609, 11895, 12098, 12220, 12263, 12232, 12131, - 11966, 11742, 11467, 11148, 10793, 10409, 10006, 9590, 9170, 8754, 8349, 7960, 7595, 7258, 6954, 6686, 6458, 6270, 6124, 6019, 5955, 5931, 5942, 5986, 6060, 6159, 6278, 6413, 6557, 6707, 6856, 7000, - 7135, 7255, 7358, 7441, 7500, 7535, 7543, 7526, 7484, 7418, 7330, 7223, 7101, 6968, 6828, 6687, 6550, 6423, 6311, 6220, 6155, 6122, 6125, 6168, 6256, 6391, 6575, 6810, 7095, 7431, 7815, 8246, - 8720, 9233, 9778, 10352, 10946, 11553, 12166, 12777, 13377, 13958, 14511, 15029, 15502, 15925, 16289, 16589, 16819, 16976, 17055, 17054, 16973, 16810, 16568, 16249, 15855, 15392, 14866, 14282, 13648, 12973, 12265, 11533, - 10788, 10038, 9295, 8566, 7863, 7194, 6568, 5991, 5473, 5017, 4631, 4316, 4077, 3915, 3830, 3822, 3888, 4025, 4229, 4496, 4817, 5188, 5599, 6043, 6511, 6993, 7482, 7968, 8442, 8895, 9320, 9709, - 10055, 10353, 10597, 10783, 10908, 10971, 10970, 10906, 10780, 10595, 10354, 10061, 9721, 9341, 8927, 8485, 8024, 7550, 7072, 6597, 6133, 5686, 5263, 4871, 4516, 4200, 3930, 3707, 3535, 3413, 3343, 3323, - 3353, 3429, 3548, 3706, 3899, 4121, 4366, 4629, 4903, 5181, 5459, 5728, 5984, 6221, 6434, 6619, 6772, 6890, 6971, 7013, 7017, 6981, 6908, 6800, 6659, 6488, 6292, 6075, 5842, 5598, 5349, 5101, - 4859, 4628, 4414, 4222, 4054, 3916, 3810, 3739, 3703, 3703, 3739, 3809, 3911, 4043, 4200, 4378, 4571, 4773, 4979, 5180, 5370, 5541, 5687, 5800, 5874, 5901, 5876, 5794, 5650, 5440, 5162, 4814, - 4396, 3908, 3353, 2731, 2049, 1310, 522, -309, -1176, -2069, -2979, -3897, -4812, -5714, -6593, -7438, -8239, -8987, -9673, -10288, -10825, -11277, -11639, -11907, -12077, -12147, -12117, -11988, -11761, -11440, -11028, -10532, - -9958, -9312, -8605, -7843, -7037, -6197, -5333, -4454, -3571, -2693, -1830, -992, -185, 580, 1301, 1969, 2580, 3131, 3618, 4041, 4400, 4694, 4926, 5098, 5214, 5279, 5298, 5277, 5221, 5138, 5034, 4916, - 4791, 4666, 4546, 4438, 4347, 4277, 4233, 4218, 4233, 4281, 4362, 4476, 4621, 4795, 4996, 5220, 5463, 5720, 5986, 6256, 6523, 6783, 7030, 7257, 7459, 7633, 7772, 7874, 7935, 7952, 7925, 7852, - 7733, 7570, 7363, 7116, 6831, 6513, 6167, 5798, 5412, 5014, 4612, 4210, 3817, 3438, 3080, 2747, 2446, 2181, 1955, 1772, 1635, 1543, 1499, 1502, 1549, 1639, 1769, 1933, 2128, 2347, 2584, 2832, - 3085, 3334, 3573, 3793, 3987, 4148, 4269, 4344, 4368, 4335, 4242, 4086, 3866, 3579, 3228, 2812, 2335, 1800, 1212, 576, -100, -810, -1547, -2300, -3062, -3824, -4574, -5306, -6008, -6673, -7292, -7857, - -8361, -8798, -9162, -9451, -9660, -9788, -9833, -9798, -9682, -9491, -9226, -8895, -8502, -8056, -7564, -7034, -6476, -5899, -5314, -4729, -4155, -3601, -3076, -2589, -2148, -1761, -1432, -1169, -974, -852, -805, -833, - -936, -1113, -1362, -1678, -2058, -2496, -2985, -3520, -4092, -4694, -5318, -5956, -6599, -7239, -7869, -8482, -9069, -9626, -10147, -10626, -11060, -11446, -11782, -12067, -12300, -12483, -12616, -12702, -12744, -12747, -12713, -12648, - -12558, -12446, -12320, -12183, -12041, -11900, -11764, -11636, -11521, -11422, -11340, -11278, -11235, -11213, -11210, -11224, -11254, -11296, -11347, -11402, -11457, -11507, -11547, -11571, -11575, -11552, -11498, -11408, -11278, -11105, -10885, -10617, - -10298, -9930, -9511, -9043, -8530, -7973, -7378, -6750, -6093, -5416, -4725, -4028, -3333, -2649, -1984, -1347, -746, -191, 312, 756, 1133, 1438, 1667, 1814, 1878, 1857, 1749, 1557, 1281, 924, 492, -11, - -580, -1206, -1882, -2600, -3349, -4120, -4905, -5691, -6471, -7233, -7969, -8670, -9327, -9933, -10481, -10966, -11382, -11728, -12000, -12196, -12319, -12367, -12345, -12256, -12104, -11894, -11634, -11331, -10991, -10625, -10239, -9843, - -9446, -9056, -8682, -8332, -8013, -7732, -7495, -7307, -7172, -7094, -7074, -7114, -7213, -7371, -7585, -7851, -8166, -8525, -8922, -9349, -9801, -10270, -10748, -11227, -11700, -12159, -12596, -13005, -13378, -13710, -13996, -14232, - -14413, -14538, -14604, -14612, -14561, -14453, -14289, -14073, -13809, -13501, -13154, -12773, -12366, -11937, -11494, -11043, -10590, -10141, -9702, -9278, -8875, -8496, -8145, -7824, -7535, -7280, -7059, -6870, -6713, -6585, -6484, -6406, - -6347, -6302, -6267, -6236, -6204, -6166, -6116, -6050, -5963, -5851, -5710, -5538, -5331, -5090, -4813, -4501, -4155, -3777, -3370, -2938, -2485, -2018, -1542, -1064, -590, -129, 312, 727, 1107, 1445, 1735, 1969, - 2142, 2249, 2286, 2249, 2137, 1947, 1680, 1337, 921, 434, -118, -731, -1398, -2111, -2862, -3642, -4442, -5251, -6059, -6857, -7633, -8378, -9081, -9734, -10328, -10854, -11307, -11679, -11967, -12166, -12273, -12288, - -12210, -12041, -11784, -11441, -11018, -10520, -9955, -9330, -8654, -7935, -7183, -6407, -5618, -4825, -4038, -3267, -2519, -1804, -1128, -499, 77, 596, 1055, 1450, 1780, 2045, 2245, 2382, 2459, 2479, 2447, 2368, - 2248, 2094, 1912, 1710, 1494, 1272, 1050, 837, 637, 457, 302, 176, 84, 27, 10, 31, 93, 193, 331, 505, 711, 945, 1204, 1482, 1775, 2076, 2381, 2684, 2978, 3260, 3523, 3764, - 3977, 4161, 4311, 4427, 4506, 4549, 4557, 4529, 4470, 4381, 4266, 4131, 3978, 3815, 3647, 3480, 3320, 3174, 3047, 2946, 2876, 2843, 2851, 2904, 3005, 3157, 3360, 3616, 3924, 4283, 4690, 5142, - 5635, 6163, 6722, 7304, 7902, 8510, 9118, 9721, 10308, 10873, 11408, 11905, 12357, 12759, 13105, 13391, 13611, 13765, 13850, 13866, 13813, 13693, 13509, 13264, 12964, 12614, 12221, 11793, 11336, 10860, 10374, 9886, - 9406, 8942, 8504, 8099, 7735, 7419, 7159, 6958, 6822, 6753, 6755, 6827, 6969, 7181, 7459, 7800, 8199, 8649, 9144, 9677, 10238, 10820, 11412, 12004, 12588, 13153, 13690, 14189, 14642, 15041, 15377, 15645, - 15839, 15955, 15988, 15936, 15799, 15576, 15268, 14878, 14408, 13865, 13251, 12575, 11842, 11060, 10238, 9383, 8503, 7608, 6706, 5805, 4914, 4038, 3186, 2364, 1577, 830, 126, -530, -1139, -1699, -2209, -2671, - -3086, -3456, -3784, -4075, -4332, -4560, -4764, -4948, -5117, -5278, -5433, -5588, -5746, -5912, -6087, -6274, -6475, -6690, -6920, -7162, -7417, -7681, -7951, -8225, -8497, -8762, -9017, -9255, -9471, -9660, -9816, -9933, - -10007, -10033, -10008, -9927, -9789, -9592, -9334, -9016, -8639, -8204, -7716, -7176, -6591, -5966, -5308, -4622, -3918, -3203, -2486, -1775, -1080, -408, 230, 829, 1380, 1875, 2309, 2676, 2971, 3191, 3334, 3398, - 3383, 3290, 3122, 2882, 2575, 2205, 1780, 1306, 792, 247, -320, -901, -1485, -2063, -2626, -3163, -3667, -4130, -4542, -4899, -5195, -5424, -5583, -5670, -5683, -5622, -5489, -5286, -5017, -4685, -4296, -3858, - -3376, -2859, -2315, -1753, -1182, -610, -48, 496, 1015, 1500, 1944, 2340, 2683, 2967, 3190, 3349, 3441, 3468, 3428, 3324, 3159, 2936, 2660, 2337, 1973, 1575, 1149, 704, 246, -213, -670, -1116, - -1542, -1943, -2312, -2644, -2933, -3176, -3370, -3512, -3601, -3638, -3622, -3556, -3441, -3282, -3082, -2845, -2578, -2285, -1973, -1647, -1314, -981, -652, -334, -31, 250, 507, 735, 933, 1098, 1229, 1326, - 1389, 1419, 1419, 1392, 1341, 1270, 1184, 1088, 987, 887, 793, 711, 646, 603, 586, 600, 647, 731, 854, 1015, 1217, 1457, 1734, 2046, 2389, 2759, 3151, 3559, 3977, 4398, 4815, 5221, - 5607, 5967, 6293, 6578, 6815, 6998, 7123, 7183, 7176, 7099, 6950, 6728, 6435, 6071, 5639, 5144, 4590, 3983, 3331, 2641, 1921, 1182, 431, -319, -1061, -1783, -2476, -3129, -3735, -4283, -4766, -5176, - -5509, -5758, -5920, -5991, -5971, -5859, -5657, -5365, -4989, -4532, -4001, -3402, -2743, -2032, -1278, -490, 319, 1143, 1970, 2791, 3595, 4373, 5117, 5819, 6470, 7066, 7600, 8068, 8467, 8794, 9050, 9233, - 9344, 9387, 9365, 9281, 9140, 8947, 8710, 8435, 8128, 7797, 7449, 7090, 6729, 6371, 6022, 5689, 5375, 5086, 4823, 4591, 4390, 4222, 4086, 3981, 3905, 3856, 3831, 3825, 3835, 3855, 3880, 3906, - 3926, 3935, 3929, 3901, 3849, 3768, 3654, 3504, 3318, 3094, 2831, 2529, 2191, 1819, 1415, 984, 530, 57, -427, -918, -1410, -1895, -2366, -2818, -3243, -3636, -3990, -4302, -4565, -4776, -4932, -5031, - -5072, -5054, -4979, -4846, -4661, -4425, -4143, -3820, -3463, -3078, -2671, -2250, -1824, -1398, -983, -584, -209, 134, 441, 705, 921, 1085, 1195, 1249, 1245, 1183, 1066, 895, 675, 408, 102, -237, - -604, -990, -1387, -1786, -2179, -2556, -2908, -3227, -3505, -3733, -3905, -4014, -4054, -4021, -3911, -3723, -3454, -3106, -2678, -2174, -1598, -953, -247, 514, 1323, 2172, 3050, 3949, 4858, 5767, 6666, 7545, - 8394, 9204, 9966, 10672, 11314, 11886, 12383, 12799, 13133, 13381, 13543, 13620, 13612, 13521, 13353, 13110, 12800, 12426, 11998, 11521, 11004, 10454, 9881, 9292, 8694, 8097, 7506, 6930, 6373, 5842, 5342, 4875, - 4446, 4055, 3705, 3395, 3125, 2892, 2696, 2533, 2399, 2290, 2202, 2129, 2067, 2011, 1956, 1896, 1828, 1746, 1648, 1531, 1391, 1227, 1038, 824, 586, 324, 41, -259, -574, -901, -1232, -1565, - -1893, -2210, -2512, -2791, -3043, -3262, -3444, -3583, -3675, -3719, -3711, -3649, -3534, -3365, -3144, -2872, -2553, -2192, -1792, -1360, -902, -426, 61, 552, 1039, 1511, 1962, 2383, 2766, 3104, 3389, 3616, - 3778, 3873, 3895, 3842, 3713, 3509, 3229, 2876, 2453, 1965, 1417, 815, 167, -518, -1233, -1969, -2716, -3463, -4202, -4922, -5613, -6267, -6875, -7429, -7922, -8347, -8700, -8976, -9172, -9286, -9319, -9269, - -9140, -8934, -8655, -8309, -7901, -7438, -6928, -6379, -5800, -5200, -4588, -3973, -3364, -2769, -2197, -1656, -1153, -693, -282, 74, 375, 617, 799, 920, 983, 989, 941, 843, 701, 519, 304, 63, - -195, -467, -742, -1014, -1275, -1518, -1736, -1923, -2074, -2184, -2249, -2265, -2231, -2145, -2007, -1818, -1579, -1293, -963, -592, -187, 248, 707, 1185, 1675, 2170, 2663, 3150, 3622, 4076, 4505, 4905, - 5273, 5604, 5896, 6149, 6360, 6530, 6660, 6751, 6806, 6829, 6823, 6791, 6740, 6675, 6599, 6520, 6443, 6373, 6314, 6273, 6252, 6256, 6288, 6349, 6442, 6567, 6724, 6911, 7127, 7368, 7631, 7911, - 8203, 8500, 8797, 9087, 9363, 9616, 9841, 10029, 10174, 10269, 10308, 10286, 10198, 10040, 9809, 9504, 9123, 8667, 8138, 7538, 6870, 6141, 5355, 4519, 3642, 2732, 1798, 850, -102, -1048, -1978, -2880, - -3746, -4564, -5327, -6025, -6651, -7198, -7660, -8032, -8312, -8496, -8584, -8577, -8475, -8281, -8001, -7638, -7199, -6691, -6123, -5502, -4838, -4141, -3420, -2686, -1948, -1217, -501, 189, 847, 1465, 2035, 2552, - 3012, 3410, 3743, 4011, 4212, 4348, 4421, 4432, 4385, 4286, 4138, 3949, 3723, 3469, 3192, 2901, 2601, 2299, 2002, 1717, 1448, 1200, 979, 786, 625, 498, 405, 346, 321, 327, 362, 423, - 507, 607, 720, 840, 962, 1080, 1188, 1280, 1353, 1400, 1417, 1401, 1348, 1255, 1122, 946, 728, 469, 171, -164, -533, -933, -1357, -1801, -2260, -2726, -3195, -3658, -4110, -4545, -4955, -5336, - -5682, -5988, -6250, -6465, -6630, -6743, -6805, -6815, -6775, -6686, -6553, -6378, -6167, -5925, -5659, -5374, -5080, -4782, -4488, -4206, -3944, -3708, -3506, -3343, -3227, -3160, -3149, -3194, -3300, -3466, -3694, -3981, - -4325, -4724, -5172, -5664, -6195, -6756, -7341, -7941, -8546, -9149, -9739, -10307, -10845, -11342, -11792, -12186, -12517, -12779, -12966, -13075, -13101, -13043, -12901, -12674, -12364, -11975, -11510, -10974, -10373, -9715, -9007, -8259, - -7478, -6675, -5860, -5042, -4231, -3437, -2670, -1938, -1249, -611, -31, 484, 933, 1311, 1614, 1842, 1995, 2073, 2080, 2017, 1890, 1703, 1462, 1173, 845, 485, 99, -301, -712, -1122, -1525, -1914, - -2281, -2620, -2925, -3193, -3418, -3597, -3730, -3813, -3848, -3834, -3773, -3667, -3519, -3334, -3115, -2867, -2594, -2304, -2000, -1688, -1374, -1063, -759, -466, -189, 68, 305, 520, 710, 877, 1021, 1143, - 1245, 1330, 1401, 1463, 1521, 1578, 1641, 1714, 1803, 1913, 2049, 2216, 2418, 2658, 2941, 3268, 3640, 4059, 4524, 5034, 5586, 6179, 6806, 7465, 8148, 8850, 9563, 10280, 10992, 11692, 12370, 13019, - 13629, 14192, 14701, 15148, 15527, 15831, 16055, 16197, 16252, 16218, 16096, 15885, 15587, 15205, 14743, 14207, 13601, 12933, 12211, 11444, 10641, 9811, 8964, 8110, 7260, 6422, 5608, 4824, 4081, 3386, 2746, 2166, - 1652, 1208, 835, 535, 309, 156, 73, 57, 104, 208, 363, 563, 799, 1064, 1349, 1645, 1944, 2237, 2516, 2772, 2998, 3187, 3334, 3432, 3478, 3468, 3401, 3274, 3087, 2843, 2542, 2189, - 1786, 1339, 854, 337, -204, -764, -1333, -1905, -2471, -3023, -3554, -4057, -4525, -4952, -5333, -5663, -5939, -6157, -6317, -6416, -6455, -6436, -6360, -6230, -6049, -5823, -5555, -5252, -4920, -4564, -4191, -3807, - -3420, -3034, -2657, -2292, -1946, -1623, -1326, -1057, -820, -615, -444, -304, -196, -116, -64, -34, -23, -28, -42, -61, -79, -93, -95, -81, -48, 10, 96, 213, 364, 550, 771, 1028, - 1319, 1641, 1994, 2371, 2769, 3183, 3606, 4033, 4455, 4866, 5258, 5623, 5955, 6244, 6486, 6672, 6797, 6856, 6843, 6757, 6593, 6351, 6031, 5632, 5158, 4611, 3995, 3315, 2579, 1793, 966, 105, - -778, -1676, -2577, -3471, -4349, -5199, -6012, -6779, -7491, -8139, -8717, -9219, -9639, -9974, -10220, -10377, -10444, -10421, -10312, -10120, -9849, -9505, -9095, -8626, -8106, -7546, -6952, -6337, -5708, -5077, -4452, -3843, - -3260, -2709, -2200, -1739, -1332, -983, -697, -477, -324, -239, -221, -270, -381, -552, -779, -1055, -1376, -1735, -2125, -2539, -2969, -3409, -3852, -4290, -4717, -5127, -5514, -5874, -6203, -6498, -6755, -6975, - -7156, -7298, -7404, -7474, -7512, -7521, -7505, -7470, -7419, -7358, -7293, -7230, -7173, -7127, -7099, -7093, -7112, -7160, -7239, -7352, -7501, -7684, -7903, -8155, -8438, -8750, -9087, -9444, -9817, -10200, -10587, -10972, - -11348, -11710, -12051, -12365, -12646, -12888, -13087, -13239, -13341, -13388, -13381, -13317, -13198, -13024, -12798, -12522, -12200, -11838, -11440, -11013, -10563, -10099, -9627, -9156, -8693, -8246, -7823, -7431, -7078, -6768, -6509, -6304, - -6157, -6072, -6050, -6091, -6196, -6362, -6588, -6869, -7201, -7578, -7994, -8441, -8912, -9397, -9889, -10378, -10855, -11312, -11738, -12127, -12470, -12759, -12989, -13154, -13248, -13270, -13216, -13085, -12877, -12594, -12237, -11811, - -11320, -10770, -10168, -9521, -8837, -8126, -7396, -6659, -5922, -5196, -4491, -3817, -3181, -2593, -2060, -1590, -1187, -856, -603, -428, -334, -322, -389, -535, -757, -1049, -1408, -1826, -2299, -2818, -3375, -3963, - -4574, -5197, -5827, -6453, -7069, -7666, -8239, -8780, -9284, -9747, -10164, -10534, -10853, -11121, -11337, -11503, -11620, -11691, -11718, -11705, -11656, -11577, -11471, -11344, -11201, -11048, -10889, -10728, -10570, -10419, -10277, -10148, - -10032, -9932, -9846, -9776, -9719, -9673, -9635, -9603, -9572, -9538, -9496, -9440, -9366, -9268, -9140, -8978, -8776, -8530, -8236, -7891, -7492, -7037, -6526, -5958, -5334, -4656, -3927, -3151, -2332, -1476, -590, 320, - 1246, 2180, 3113, 4037, 4943, 5821, 6664, 7463, 8211, 8899, 9521, 10072, 10548, 10943, 11256, 11485, 11629, 11690, 11669, 11570, 11397, 11155, 10850, 10490, 10082, 9636, 9159, 8661, 8153, 7643, 7141, 6657, - 6198, 5775, 5394, 5061, 4784, 4566, 4413, 4325, 4306, 4355, 4471, 4653, 4897, 5199, 5553, 5954, 6394, 6866, 7362, 7873, 8390, 8904, 9407, 9889, 10342, 10758, 11131, 11453, 11718, 11922, 12061, 12133, - 12134, 12066, 11928, 11723, 11452, 11119, 10730, 10289, 9802, 9278, 8721, 8142, 7547, 6944, 6341, 5746, 5167, 4611, 4083, 3591, 3138, 2730, 2369, 2058, 1799, 1593, 1438, 1333, 1277, 1266, 1297, 1364, - 1464, 1591, 1738, 1901, 2072, 2247, 2418, 2581, 2730, 2860, 2968, 3049, 3102, 3123, 3113, 3069, 2994, 2888, 2753, 2593, 2410, 2209, 1994, 1771, 1545, 1321, 1105, 903, 719, 560, 429, 332, - 270, 248, 268, 330, 436, 584, 773, 1001, 1265, 1561, 1883, 2226, 2585, 2951, 3319, 3682, 4030, 4357, 4656, 4920, 5141, 5314, 5432, 5492, 5489, 5420, 5284, 5080, 4807, 4467, 4063, 3599, - 3079, 2509, 1896, 1248, 572, -121, -824, -1526, -2216, -2886, -3524, -4122, -4669, -5157, -5579, -5926, -6192, -6373, -6464, -6463, -6366, -6175, -5890, -5513, -5048, -4499, -3871, -3171, -2407, -1588, -721, 181, - 1113, 2061, 3017, 3969, 4909, 5826, 6712, 7559, 8358, 9104, 9790, 10412, 10966, 11451, 11863, 12204, 12474, 12675, 12809, 12882, 12897, 12859, 12775, 12652, 12495, 12312, 12110, 11896, 11676, 11458, 11248, 11050, - 10871, 10713, 10581, 10477, 10403, 10360, 10347, 10364, 10409, 10480, 10573, 10684, 10809, 10944, 11082, 11220, 11351, 11469, 11570, 11649, 11701, 11721, 11707, 11656, 11565, 11432, 11258, 11043, 10788, 10495, 10166, 9806, - 9418, 9008, 8581, 8143, 7701, 7260, 6828, 6411, 6016, 5649, 5315, 5020, 4769, 4566, 4413, 4314, 4269, 4280, 4345, 4464, 4634, 4851, 5111, 5410, 5741, 6099, 6475, 6864, 7257, 7648, 8027, 8387, - 8722, 9024, 9287, 9505, 9673, 9788, 9846, 9845, 9783, 9662, 9482, 9245, 8955, 8616, 8233, 7813, 7363, 6890, 6402, 5908, 5417, 4938, 4481, 4052, 3663, 3319, 3029, 2800, 2637, 2545, 2527, 2588, - 2727, 2945, 3242, 3615, 4061, 4575, 5152, 5785, 6467, 7190, 7944, 8720, 9509, 10299, 11082, 11847, 12585, 13285, 13940, 14540, 15079, 15549, 15946, 16264, 16501, 16654, 16722, 16705, 16604, 16422, 16162, 15828, - 15426, 14963, 14443, 13876, 13269, 12629, 11965, 11286, 10599, 9912, 9233, 8568, 7924, 7307, 6721, 6171, 5659, 5188, 4760, 4374, 4030, 3727, 3463, 3234, 3038, 2870, 2726, 2601, 2490, 2388, 2291, 2192, - 2087, 1973, 1845, 1701, 1537, 1351, 1143, 912, 659, 384, 90, -220, -544, -878, -1216, -1553, -1886, -2207, -2511, -2792, -3045, -3265, -3446, -3584, -3674, -3715, -3702, -3636, -3513, -3336, -3105, -2822, - -2489, -2112, -1695, -1243, -763, -262, 251, 771, 1289, 1795, 2281, 2740, 3162, 3541, 3869, 4140, 4348, 4488, 4557, 4551, 4469, 4311, 4077, 3769, 3389, 2943, 2435, 1872, 1259, 607, -78, -786, - -1508, -2234, -2954, -3659, -4338, -4984, -5586, -6137, -6631, -7059, -7418, -7702, -7909, -8037, -8084, -8051, -7940, -7754, -7496, -7172, -6787, -6348, -5863, -5340, -4787, -4214, -3629, -3042, -2462, -1897, -1356, -846, - -376, 48, 423, 742, 1003, 1202, 1339, 1413, 1425, 1377, 1272, 1113, 906, 656, 369, 52, -287, -643, -1006, -1370, -1727, -2070, -2392, -2687, -2949, -3172, -3354, -3490, -3578, -3617, -3605, -3545, - -3436, -3281, -3083, -2846, -2575, -2274, -1949, -1607, -1253, -894, -536, -186, 150, 468, 761, 1025, 1257, 1451, 1607, 1723, 1797, 1829, 1821, 1773, 1689, 1572, 1424, 1252, 1058, 849, 630, 407, - 185, -29, -233, -419, -585, -725, -837, -918, -967, -982, -963, -912, -830, -721, -586, -432, -262, -83, 99, 277, 446, 597, 723, 816, 871, 881, 838, 739, 577, 350, 54, -312, - -750, -1260, -1841, -2489, -3201, -3972, -4797, -5669, -6580, -7522, -8485, -9460, -10437, -11406, -12355, -13275, -14156, -14987, -15759, -16464, -17093, -17640, -18098, -18463, -18731, -18900, -18967, -18934, -18802, -18573, -18250, -17840, - -17348, -16780, -16146, -15452, -14709, -13927, -13114, -12281, -11438, -10595, -9761, -8946, -8157, -7403, -6691, -6027, -5416, -4861, -4366, -3933, -3561, -3252, -3003, -2812, -2676, -2590, -2549, -2549, -2582, -2642, -2722, -2816, - -2917, -3018, -3112, -3193, -3257, -3297, -3310, -3292, -3241, -3155, -3032, -2873, -2679, -2451, -2192, -1905, -1595, -1265, -920, -567, -210, 144, 491, 824, 1137, 1426, 1686, 1913, 2104, 2255, 2364, 2431, - 2455, 2437, 2377, 2278, 2144, 1977, 1782, 1565, 1331, 1085, 833, 583, 341, 113, -95, -277, -429, -545, -621, -653, -640, -579, -469, -312, -108, 140, 431, 759, 1121, 1510, 1920, 2345, - 2777, 3210, 3634, 4044, 4430, 4786, 5104, 5379, 5604, 5774, 5884, 5932, 5915, 5832, 5682, 5467, 5187, 4847, 4451, 4003, 3510, 2978, 2416, 1831, 1232, 629, 30, -554, -1116, -1647, -2137, -2579, - -2965, -3289, -3546, -3730, -3840, -3871, -3824, -3698, -3495, -3217, -2868, -2453, -1979, -1450, -877, -266, 372, 1030, 1697, 2363, 3019, 3654, 4260, 4827, 5347, 5813, 6217, 6555, 6821, 7012, 7124, 7158, - 7111, 6986, 6785, 6510, 6165, 5756, 5288, 4769, 4204, 3603, 2972, 2321, 1656, 987, 321, -333, -969, -1582, -2164, -2710, -3217, -3680, -4098, -4468, -4791, -5066, -5294, -5478, -5621, -5725, -5794, -5834, - -5849, -5844, -5825, -5796, -5764, -5733, -5707, -5691, -5689, -5704, -5738, -5793, -5870, -5969, -6090, -6231, -6390, -6564, -6750, -6944, -7141, -7336, -7524, -7700, -7858, -7993, -8099, -8171, -8206, -8200, -8148, -8048, - -7899, -7700, -7450, -7151, -6804, -6413, -5981, -5512, -5013, -4489, -3947, -3395, -2841, -2294, -1760, -1250, -772, -333, 57, 393, 667, 873, 1006, 1062, 1038, 931, 741, 467, 112, -321, -831, -1411, - -2055, -2757, -3510, -4303, -5130, -5979, -6841, -7707, -8565, -9407, -10223, -11003, -11740, -12424, -13050, -13610, -14101, -14517, -14857, -15119, -15302, -15406, -15435, -15392, -15279, -15103, -14870, -14587, -14261, -13900, -13513, -13108, - -12695, -12281, -11876, -11489, -11125, -10793, -10499, -10249, -10046, -9895, -9799, -9758, -9773, -9843, -9967, -10142, -10364, -10628, -10929, -11261, -11617, -11989, -12371, -12754, -13131, -13494, -13836, -14149, -14427, -14663, -14853, -14992, - -15075, -15101, -15067, -14972, -14817, -14602, -14328, -14000, -13621, -13194, -12725, -12219, -11682, -11121, -10543, -9952, -9357, -8763, -8176, -7602, -7047, -6514, -6007, -5530, -5085, -4673, -4295, -3951, -3640, -3360, -3108, -2882, - -2678, -2490, -2316, -2149, -1985, -1818, -1643, -1456, -1251, -1025, -774, -494, -183, 159, 535, 945, 1386, 1858, 2357, 2879, 3421, 3978, 4542, 5108, 5670, 6218, 6747, 7248, 7715, 8138, 8512, 8829, - 9084, 9271, 9385, 9424, 9383, 9262, 9059, 8776, 8414, 7977, 7467, 6891, 6254, 5564, 4828, 4055, 3255, 2437, 1612, 789, -19, -805, -1558, -2268, -2926, -3525, -4056, -4513, -4891, -5186, -5395, -5515, - -5546, -5489, -5346, -5121, -4816, -4439, -3995, -3491, -2936, -2339, -1708, -1053, -384, 289, 957, 1610, 2240, 2838, 3397, 3908, 4367, 4767, 5106, 5380, 5587, 5726, 5798, 5804, 5747, 5629, 5456, 5232, - 4964, 4657, 4318, 3955, 3574, 3183, 2789, 2400, 2021, 1659, 1320, 1008, 728, 483, 275, 107, -21, -109, -159, -172, -151, -99, -19, 81, 201, 332, 470, 610, 745, 871, 983, 1075, - 1144, 1186, 1198, 1178, 1125, 1038, 917, 764, 580, 369, 133, -122, -394, -675, -960, -1244, -1519, -1780, -2021, -2234, -2414, -2556, -2655, -2707, -2708, -2655, -2546, -2382, -2160, -1884, -1553, -1172, - -745, -275, 230, 767, 1328, 1905, 2491, 3077, 3657, 4222, 4764, 5275, 5749, 6180, 6561, 6887, 7155, 7361, 7504, 7581, 7594, 7544, 7432, 7262, 7040, 6769, 6457, 6110, 5736, 5344, 4941, 4538, - 4142, 3763, 3410, 3091, 2814, 2587, 2415, 2304, 2260, 2286, 2384, 2555, 2801, 3120, 3509, 3965, 4484, 5059, 5685, 6354, 7058, 7787, 8533, 9285, 10035, 10772, 11486, 12167, 12808, 13398, 13931, 14399, - 14797, 15118, 15360, 15518, 15592, 15580, 15484, 15305, 15045, 14710, 14303, 13830, 13298, 12713, 12084, 11419, 10726, 10013, 9289, 8562, 7840, 7131, 6441, 5777, 5145, 4550, 3995, 3483, 3018, 2600, 2229, 1905, - 1627, 1393, 1198, 1041, 917, 821, 748, 695, 654, 623, 595, 566, 532, 490, 435, 366, 280, 177, 56, -81, -235, -404, -584, -771, -963, -1154, -1341, -1516, -1676, -1814, -1925, -2003, - -2045, -2044, -1997, -1900, -1750, -1546, -1285, -968, -596, -169, 308, 835, 1405, 2014, 2655, 3322, 4007, 4702, 5399, 6089, 6763, 7414, 8032, 8609, 9137, 9609, 10018, 10359, 10627, 10817, 10928, 10957, - 10904, 10770, 10556, 10266, 9903, 9473, 8983, 8438, 7847, 7219, 6562, 5886, 5200, 4515, 3839, 3183, 2555, 1963, 1417, 922, 485, 111, -194, -431, -595, -687, -707, -657, -539, -358, -119, 172, - 509, 884, 1289, 1715, 2154, 2597, 3034, 3458, 3859, 4229, 4561, 4849, 5086, 5268, 5390, 5449, 5444, 5373, 5237, 5037, 4777, 4458, 4086, 3666, 3203, 2705, 2179, 1632, 1072, 507, -55, -606, - -1140, -1648, -2125, -2564, -2960, -3310, -3608, -3854, -4044, -4179, -4259, -4285, -4259, -4184, -4064, -3903, -3706, -3479, -3227, -2957, -2674, -2385, -2096, -1812, -1540, -1283, -1046, -834, -649, -493, -368, -275, - -213, -181, -178, -200, -245, -309, -387, -475, -567, -658, -742, -815, -870, -902, -908, -882, -822, -723, -585, -406, -185, 76, 378, 717, 1091, 1495, 1925, 2375, 2839, 3310, 3781, 4245, - 4694, 5120, 5517, 5876, 6191, 6455, 6663, 6810, 6891, 6903, 6844, 6713, 6510, 6236, 5893, 5485, 5016, 4491, 3918, 3303, 2655, 1983, 1295, 602, -85, -759, -1408, -2023, -2594, -3113, -3571, -3960, - -4275, -4511, -4661, -4725, -4699, -4583, -4377, -4084, -3706, -3248, -2715, -2114, -1452, -737, 22, 815, 1634, 2467, 3305, 4138, 4955, 5747, 6505, 7221, 7887, 8497, 9044, 9523, 9932, 10267, 10527, 10711, - 10820, 10857, 10823, 10723, 10561, 10342, 10072, 9759, 9408, 9026, 8622, 8201, 7772, 7340, 6912, 6494, 6091, 5707, 5347, 5012, 4706, 4428, 4180, 3961, 3769, 3602, 3458, 3332, 3220, 3118, 3021, 2923, - 2820, 2706, 2576, 2425, 2248, 2041, 1801, 1525, 1210, 856, 461, 27, -446, -956, -1500, -2075, -2677, -3299, -3937, -4585, -5237, -5885, -6523, -7145, -7743, -8311, -8844, -9335, -9780, -10175, -10516, -10800, - -11025, -11192, -11300, -11350, -11344, -11286, -11179, -11028, -10839, -10617, -10371, -10106, -9830, -9551, -9276, -9013, -8770, -8553, -8368, -8222, -8119, -8064, -8060, -8109, -8212, -8370, -8582, -8845, -9156, -9512, -9907, -10334, - -10788, -11261, -11744, -12229, -12707, -13169, -13606, -14009, -14370, -14681, -14933, -15121, -15238, -15280, -15241, -15120, -14915, -14625, -14250, -13794, -13259, -12649, -11970, -11228, -10431, -9586, -8703, -7792, -6861, -5921, -4982, -4054, - -3148, -2272, -1436, -649, 82, 751, 1351, 1877, 2327, 2696, 2984, 3191, 3317, 3365, 3338, 3239, 3073, 2848, 2568, 2242, 1876, 1479, 1059, 624, 182, -259, -692, -1110, -1507, -1877, -2215, -2517, - -2779, -3000, -3177, -3312, -3403, -3452, -3462, -3435, -3374, -3284, -3169, -3034, -2885, -2725, -2561, -2397, -2239, -2090, -1955, -1837, -1739, -1664, -1611, -1584, -1580, -1599, -1640, -1699, -1775, -1862, -1957, -2055, - -2150, -2238, -2312, -2366, -2396, -2397, -2362, -2287, -2169, -2004, -1789, -1523, -1205, -834, -413, 57, 575, 1135, 1733, 2363, 3019, 3695, 4382, 5072, 5759, 6432, 7083, 7705, 8289, 8827, 9312, 9737, - 10097, 10386, 10601, 10738, 10796, 10774, 10672, 10492, 10236, 9909, 9516, 9062, 8555, 8002, 7411, 6792, 6154, 5506, 4859, 4222, 3606, 3018, 2468, 1965, 1515, 1126, 803, 550, 371, 268, 243, 295, - 423, 624, 895, 1231, 1627, 2075, 2569, 3100, 3660, 4240, 4832, 5426, 6012, 6583, 7130, 7645, 8121, 8551, 8930, 9253, 9516, 9717, 9855, 9928, 9938, 9887, 9777, 9611, 9396, 9135, 8836, 8504, - 8147, 7773, 7389, 7003, 6621, 6253, 5903, 5580, 5289, 5034, 4821, 4653, 4533, 4462, 4441, 4471, 4549, 4674, 4843, 5053, 5298, 5575, 5877, 6199, 6534, 6877, 7220, 7558, 7885, 8194, 8481, 8741, - 8969, 9163, 9319, 9436, 9512, 9548, 9544, 9502, 9424, 9312, 9171, 9005, 8818, 8616, 8404, 8188, 7972, 7763, 7567, 7387, 7228, 7095, 6991, 6918, 6879, 6874, 6903, 6966, 7060, 7185, 7335, 7507, - 7696, 7897, 8102, 8306, 8501, 8681, 8838, 8964, 9053, 9098, 9093, 9031, 8909, 8722, 8467, 8140, 7743, 7273, 6733, 6124, 5450, 4715, 3924, 3085, 2205, 1291, 354, -598, -1555, -2506, -3442, -4351, - -5224, -6051, -6821, -7527, -8161, -8715, -9183, -9560, -9843, -10028, -10114, -10101, -9991, -9785, -9487, -9102, -8636, -8096, -7489, -6823, -6109, -5356, -4573, -3770, -2958, -2146, -1345, -563, 190, 908, 1582, 2208, - 2778, 3289, 3739, 4124, 4443, 4698, 4889, 5018, 5089, 5105, 5072, 4994, 4879, 4731, 4559, 4369, 4168, 3962, 3758, 3562, 3380, 3218, 3078, 2966, 2884, 2833, 2815, 2831, 2879, 2958, 3066, 3199, - 3354, 3527, 3712, 3905, 4099, 4290, 4471, 4636, 4781, 4901, 4989, 5043, 5058, 5033, 4964, 4850, 4692, 4489, 4243, 3955, 3629, 3268, 2878, 2462, 2026, 1576, 1119, 661, 207, -233, -656, -1056, - -1426, -1761, -2058, -2312, -2521, -2683, -2797, -2862, -2880, -2852, -2782, -2672, -2528, -2354, -2157, -1943, -1719, -1492, -1271, -1061, -872, -710, -582, -495, -454, -466, -533, -661, -851, -1104, -1421, -1802, - -2244, -2744, -3299, -3903, -4550, -5234, -5947, -6680, -7426, -8174, -8915, -9641, -10342, -11009, -11632, -12205, -12719, -13168, -13546, -13848, -14071, -14211, -14268, -14242, -14133, -13944, -13678, -13340, -12936, -12472, -11955, -11394, - -10798, -10176, -9538, -8893, -8251, -7622, -7015, -6440, -5905, -5417, -4983, -4610, -4302, -4063, -3896, -3803, -3784, -3838, -3964, -4159, -4418, -4738, -5112, -5534, -5996, -6492, -7014, -7553, -8101, -8650, -9192, -9720, - -10226, -10703, -11147, -11551, -11912, -12225, -12488, -12700, -12860, -12967, -13023, -13031, -12991, -12909, -12788, -12632, -12447, -12237, -12008, -11765, -11514, -11260, -11008, -10762, -10526, -10304, -10098, -9910, -9742, -9594, -9465, -9355, - -9261, -9182, -9114, -9053, -8994, -8934, -8866, -8786, -8689, -8569, -8421, -8240, -8022, -7762, -7459, -7108, -6708, -6259, -5760, -5212, -4616, -3977, -3297, -2580, -1834, -1063, -274, 523, 1323, 2116, 2895, 3649, - 4372, 5053, 5685, 6261, 6774, 7216, 7584, 7871, 8075, 8193, 8225, 8169, 8028, 7802, 7497, 7116, 6665, 6150, 5579, 4961, 4303, 3616, 2909, 2192, 1474, 767, 79, -580, -1203, -1780, -2306, -2773, - -3176, -3512, -3777, -3969, -4089, -4135, -4112, -4020, -3865, -3651, -3384, -3072, -2721, -2339, -1936, -1519, -1097, -680, -276, 107, 461, 780, 1056, 1283, 1458, 1576, 1635, 1632, 1568, 1442, 1257, 1015, - 720, 376, -10, -435, -890, -1368, -1862, -2365, -2867, -3362, -3841, -4299, -4726, -5118, -5469, -5774, -6029, -6230, -6376, -6466, -6498, -6474, -6396, -6265, -6085, -5861, -5596, -5297, -4969, -4618, -4250, -3872, - -3490, -3111, -2740, -2383, -2046, -1731, -1445, -1189, -965, -777, -623, -504, -420, -368, -345, -350, -377, -422, -482, -550, -621, -691, -754, -804, -838, -849, -836, -794, -720, -613, -471, -295, - -85, 156, 428, 727, 1048, 1386, 1736, 2091, 2445, 2791, 3120, 3427, 3703, 3940, 4132, 4272, 4354, 4372, 4322, 4200, 4003, 3729, 3378, 2949, 2445, 1869, 1223, 514, -252, -1070, -1931, -2827, - -3747, -4683, -5625, -6561, -7482, -8377, -9236, -10050, -10809, -11506, -12132, -12681, -13148, -13527, -13816, -14012, -14115, -14125, -14044, -13875, -13622, -13290, -12886, -12416, -11889, -11313, -10698, -10053, -9387, -8710, -8033, -7365, - -6714, -6089, -5499, -4951, -4450, -4002, -3612, -3283, -3017, -2814, -2676, -2600, -2585, -2628, -2724, -2868, -3056, -3280, -3535, -3813, -4107, -4409, -4714, -5013, -5301, -5570, -5816, -6033, -6217, -6366, -6475, -6543, - -6570, -6556, -6501, -6407, -6278, -6115, -5924, -5709, -5474, -5225, -4968, -4708, -4450, -4201, -3965, -3747, -3552, -3383, -3243, -3136, -3062, -3022, -3016, -3044, -3104, -3193, -3308, -3445, -3599, -3765, -3938, -4112, - -4280, -4436, -4575, -4689, -4773, -4822, -4830, -4794, -4709, -4573, -4384, -4141, -3844, -3493, -3090, -2639, -2142, -1605, -1032, -430, 193, 833, 1480, 2127, 2766, 3389, 3988, 4555, 5083, 5567, 5998, 6374, - 6688, 6939, 7123, 7240, 7289, 7272, 7190, 7046, 6846, 6593, 6295, 5958, 5590, 5199, 4793, 4382, 3974, 3579, 3205, 2861, 2554, 2294, 2085, 1934, 1846, 1826, 1874, 1995, 2187, 2450, 2782, 3180, - 3639, 4155, 4720, 5329, 5971, 6640, 7326, 8019, 8709, 9387, 10043, 10667, 11250, 11785, 12262, 12674, 13016, 13283, 13469, 13573, 13591, 13523, 13371, 13134, 12817, 12422, 11955, 11420, 10826, 10177, 9483, 8752, - 7991, 7209, 6415, 5617, 4823, 4041, 3278, 2541, 1836, 1167, 539, -43, -580, -1068, -1507, -1898, -2241, -2539, -2794, -3009, -3189, -3338, -3460, -3560, -3644, -3717, -3782, -3846, -3911, -3983, -4064, -4156, - -4262, -4383, -4519, -4670, -4836, -5013, -5199, -5392, -5587, -5780, -5966, -6140, -6296, -6429, -6534, -6604, -6636, -6623, -6563, -6450, -6283, -6058, -5775, -5433, -5032, -4574, -4062, -3497, -2886, -2232, -1543, -823, - -82, 672, 1434, 2193, 2940, 3667, 4366, 5027, 5643, 6207, 6711, 7149, 7518, 7811, 8028, 8164, 8220, 8197, 8095, 7917, 7667, 7351, 6973, 6542, 6064, 5548, 5002, 4437, 3860, 3283, 2715, 2165, - 1642, 1155, 711, 318, -17, -291, -498, -635, -701, -694, -615, -465, -248, 31, 371, 764, 1202, 1680, 2188, 2719, 3263, 3811, 4355, 4885, 5394, 5871, 6311, 6707, 7051, 7338, 7566, 7729, - 7826, 7856, 7819, 7716, 7549, 7321, 7037, 6701, 6320, 5899, 5447, 4969, 4475, 3972, 3467, 2969, 2485, 2022, 1587, 1186, 824, 506, 235, 13, -155, -272, -336, -349, -312, -228, -102, 63, - 262, 490, 742, 1010, 1289, 1572, 1855, 2129, 2392, 2636, 2859, 3055, 3222, 3357, 3459, 3527, 3562, 3563, 3533, 3474, 3389, 3283, 3159, 3023, 2879, 2733, 2590, 2457, 2338, 2238, 2163, 2117, - 2104, 2126, 2186, 2285, 2425, 2605, 2825, 3081, 3371, 3691, 4038, 4405, 4786, 5175, 5566, 5950, 6321, 6671, 6991, 7276, 7519, 7712, 7850, 7928, 7941, 7887, 7763, 7568, 7301, 6964, 6559, 6088, - 5557, 4971, 4336, 3660, 2950, 2216, 1467, 712, -38, -774, -1484, -2161, -2793, -3372, -3889, -4336, -4708, -4998, -5202, -5316, -5338, -5266, -5102, -4847, -4503, -4075, -3568, -2987, -2340, -1635, -881, -87, - 737, 1582, 2438, 3294, 4140, 4968, 5767, 6529, 7247, 7913, 8522, 9069, 9549, 9960, 10301, 10570, 10768, 10896, 10958, 10957, 10897, 10783, 10622, 10419, 10182, 9917, 9631, 9332, 9026, 8721, 8423, 8137, - 7869, 7623, 7404, 7214, 7055, 6928, 6835, 6774, 6744, 6743, 6768, 6816, 6882, 6962, 7051, 7143, 7233, 7316, 7387, 7440, 7471, 7475, 7449, 7389, 7294, 7160, 6989, 6779, 6532, 6248, 5932, 5585, - 5212, 4819, 4409, 3989, 3565, 3144, 2731, 2335, 1960, 1615, 1303, 1032, 806, 629, 505, 436, 425, 471, 576, 737, 954, 1222, 1537, 1896, 2293, 2721, 3173, 3643, 4123, 4605, 5081, 5545, - 5987, 6401, 6781, 7120, 7414, 7656, 7845, 7977, 8051, 8067, 8024, 7925, 7773, 7571, 7324, 7038, 6721, 6377, 6017, 5648, 5280, 4919, 4577, 4262, 3981, 3744, 3558, 3429, 3364, 3369, 3446, 3599, - 3831, 4141, 4529, 4993, 5531, 6138, 6809, 7537, 8316, 9138, 9993, 10872, 11766, 12665, 13558, 14434, 15285, 16101, 16871, 17589, 18245, 18833, 19346, 19781, 20132, 20397, 20575, 20665, 20667, 20584, 20419, 20175, - 19857, 19471, 19024, 18523, 17974, 17387, 16769, 16129, 15473, 14812, 14151, 13499, 12861, 12244, 11652, 11091, 10563, 10071, 9618, 9203, 8826, 8487, 8183, 7912, 7672, 7457, 7264, 7088, 6924, 6767, 6612, 6455, - 6289, 6112, 5918, 5705, 5470, 5210, 4924, 4612, 4273, 3909, 3521, 3112, 2684, 2243, 1791, 1334, 878, 427, -11, -432, -831, -1201, -1537, -1834, -2089, -2297, -2456, -2563, -2617, -2618, -2567, -2465, - -2315, -2120, -1885, -1614, -1315, -993, -656, -311, 32, 367, 684, 977, 1235, 1452, 1620, 1734, 1786, 1771, 1687, 1529, 1295, 985, 600, 140, -392, -992, -1655, -2376, -3147, -3961, -4810, -5685, - -6575, -7472, -8365, -9245, -10102, -10927, -11710, -12443, -13118, -13729, -14270, -14736, -15123, -15428, -15651, -15790, -15848, -15827, -15729, -15559, -15323, -15028, -14679, -14286, -13856, -13399, -12923, -12438, -11952, -11474, -11013, -10577, - -10173, -9809, -9488, -9218, -9000, -8839, -8736, -8692, -8706, -8777, -8901, -9077, -9299, -9561, -9859, -10185, -10533, -10895, -11264, -11632, -11992, -12336, -12657, -12949, -13206, -13423, -13594, -13718, -13790, -13809, -13775, -13686, - -13545, -13352, -13112, -12827, -12502, -12141, -11751, -11337, -10905, -10462, -10015, -9568, -9130, -8705, -8300, -7919, -7566, -7245, -6960, -6711, -6502, -6331, -6199, -6104, -6044, -6017, -6020, -6047, -6095, -6159, -6232, -6311, - -6390, -6462, -6523, -6568, -6592, -6591, -6563, -6503, -6411, -6285, -6124, -5930, -5705, -5450, -5168, -4865, -4545, -4213, -3876, -3540, -3212, -2899, -2610, -2350, -2128, -1950, -1822, -1750, -1740, -1796, -1922, -2119, - -2389, -2733, -3149, -3637, -4192, -4811, -5488, -6217, -6991, -7802, -8641, -9498, -10363, -11227, -12079, -12909, -13707, -14461, -15164, -15806, -16379, -16875, -17289, -17614, -17847, -17985, -18027, -17971, -17818, -17571, -17234, -16810, - -16305, -15726, -15081, -14377, -13624, -12831, -12008, -11165, -10311, -9458, -8614, -7789, -6992, -6230, -5511, -4841, -4226, -3671, -3177, -2749, -2386, -2089, -1857, -1687, -1577, -1523, -1519, -1562, -1643, -1758, -1899, -2060, - -2233, -2412, -2590, -2760, -2918, -3057, -3173, -3262, -3321, -3348, -3342, -3302, -3228, -3122, -2985, -2822, -2635, -2428, -2207, -1976, -1740, -1507, -1280, -1066, -870, -698, -554, -442, -366, -330, -334, -381, - -471, -604, -779, -994, -1245, -1530, -1845, -2183, -2541, -2912, -3289, -3666, -4038, -4397, -4737, -5052, -5337, -5586, -5795, -5960, -6078, -6147, -6166, -6134, -6053, -5923, -5748, -5531, -5277, -4990, -4676, -4342, - -3994, -3641, -3289, -2946, -2620, -2318, -2047, -1814, -1625, -1485, -1399, -1371, -1403, -1498, -1654, -1873, -2153, -2491, -2882, -3323, -3808, -4329, -4880, -5453, -6039, -6628, -7213, -7783, -8329, -8843, -9314, -9737, - -10102, -10403, -10634, -10790, -10868, -10864, -10778, -10608, -10356, -10024, -9615, -9133, -8585, -7976, -7315, -6609, -5867, -5098, -4312, -3520, -2730, -1953, -1198, -474, 209, 844, 1425, 1945, 2397, 2780, 3088, 3321, - 3477, 3556, 3562, 3495, 3360, 3160, 2903, 2593, 2238, 1845, 1422, 977, 518, 53, -409, -863, -1299, -1712, -2096, -2445, -2754, -3020, -3241, -3414, -3538, -3614, -3643, -3625, -3565, -3465, -3328, -3160, - -2965, -2749, -2517, -2273, -2025, -1776, -1532, -1297, -1075, -871, -685, -522, -382, -266, -173, -102, -53, -22, -7, -2, -5, -11, -14, -10, 7, 43, 103, 191, 313, 472, 672, 915, - 1203, 1539, 1922, 2351, 2825, 3341, 3897, 4486, 5106, 5748, 6407, 7076, 7745, 8408, 9056, 9680, 10272, 10823, 11325, 11771, 12153, 12466, 12703, 12861, 12934, 12922, 12823, 12635, 12361, 12003, 11563, 11047, - 10459, 9807, 9097, 8338, 7539, 6709, 5858, 4995, 4132, 3277, 2440, 1632, 860, 133, -540, -1155, -1707, -2189, -2599, -2935, -3195, -3381, -3493, -3535, -3509, -3421, -3276, -3082, -2844, -2572, -2272, -1955, - -1627, -1300, -979, -675, -395, -146, 64, 232, 352, 419, 431, 386, 283, 123, -92, -361, -680, -1045, -1450, -1889, -2356, -2843, -3343, -3849, -4352, -4844, -5318, -5767, -6183, -6560, -6893, -7176, - -7406, -7578, -7691, -7743, -7733, -7663, -7533, -7346, -7106, -6815, -6479, -6104, -5693, -5255, -4795, -4319, -3835, -3348, -2864, -2389, -1929, -1488, -1070, -679, -317, 14, 313, 581, 817, 1024, 1203, 1358, - 1492, 1609, 1714, 1811, 1907, 2005, 2112, 2232, 2370, 2530, 2717, 2934, 3183, 3467, 3786, 4141, 4530, 4954, 5409, 5891, 6398, 6923, 7461, 8006, 8551, 9090, 9613, 10115, 10587, 11021, 11411, 11749, - 12029, 12245, 12393, 12468, 12468, 12389, 12230, 11993, 11678, 11288, 10826, 10297, 9706, 9060, 8367, 7635, 6874, 6091, 5299, 4506, 3724, 2961, 2229, 1537, 894, 309, -211, -659, -1030, -1319, -1522, -1637, - -1663, -1601, -1452, -1219, -905, -516, -59, 460, 1034, 1653, 2308, 2991, 3690, 4397, 5102, 5794, 6465, 7106, 7709, 8266, 8771, 9219, 9604, 9924, 10176, 10358, 10471, 10515, 10492, 10406, 10260, 10060, - 9809, 9515, 9184, 8823, 8440, 8040, 7633, 7224, 6820, 6428, 6054, 5702, 5377, 5084, 4823, 4599, 4411, 4260, 4145, 4064, 4016, 3997, 4003, 4030, 4074, 4128, 4189, 4249, 4305, 4349, 4378, 4386, - 4369, 4323, 4245, 4133, 3984, 3799, 3577, 3318, 3025, 2700, 2346, 1967, 1568, 1154, 730, 303, -120, -535, -934, -1311, -1660, -1975, -2252, -2485, -2671, -2806, -2889, -2918, -2892, -2812, -2680, -2497, - -2268, -1996, -1687, -1346, -980, -596, -202, 195, 587, 967, 1327, 1658, 1955, 2209, 2416, 2570, 2666, 2702, 2675, 2584, 2428, 2208, 1926, 1586, 1192, 748, 260, -262, -815, -1388, -1974, -2563, - -3145, -3713, -4257, -4768, -5239, -5660, -6027, -6331, -6569, -6736, -6829, -6845, -6785, -6649, -6438, -6155, -5805, -5392, -4923, -4405, -3845, -3252, -2635, -2004, -1368, -738, -122, 468, 1026, 1541, 2005, 2412, - 2755, 3028, 3228, 3349, 3391, 3353, 3234, 3036, 2762, 2414, 1997, 1518, 981, 394, -234, -899, -1589, -2298, -3016, -3735, -4447, -5145, -5821, -6468, -7081, -7654, -8183, -8664, -9095, -9475, -9802, -10077, - -10301, -10476, -10604, -10689, -10735, -10746, -10727, -10683, -10618, -10539, -10449, -10354, -10259, -10167, -10081, -10005, -9941, -9890, -9853, -9829, -9819, -9819, -9829, -9845, -9863, -9880, -9890, -9888, -9870, -9829, -9761, -9659, - -9520, -9338, -9109, -8829, -8496, -8106, -7659, -7154, -6590, -5971, -5297, -4572, -3800, -2987, -2138, -1259, -359, 555, 1475, 2392, 3298, 4183, 5038, 5856, 6628, 7346, 8004, 8595, 9113, 9555, 9918, 10198, - 10395, 10509, 10541, 10494, 10372, 10178, 9919, 9601, 9232, 8820, 8373, 7901, 7413, 6919, 6429, 5951, 5496, 5071, 4684, 4344, 4056, 3826, 3658, 3556, 3522, 3556, 3660, 3831, 4067, 4364, 4717, 5121, - 5570, 6055, 6570, 7105, 7652, 8202, 8746, 9275, 9781, 10255, 10689, 11077, 11412, 11688, 11902, 12050, 12130, 12139, 12079, 11950, 11754, 11494, 11174, 10800, 10376, 9910, 9408, 8878, 8327, 7763, 7195, 6631, - 6077, 5541, 5030, 4551, 4108, 3707, 3350, 3043, 2785, 2579, 2425, 2322, 2267, 2260, 2295, 2370, 2479, 2618, 2780, 2960, 3152, 3350, 3547, 3738, 3917, 4080, 4222, 4338, 4426, 4484, 4508, 4500, - 4458, 4383, 4277, 4143, 3983, 3801, 3602, 3390, 3171, 2950, 2732, 2523, 2329, 2154, 2005, 1884, 1797, 1747, 1735, 1764, 1835, 1948, 2101, 2294, 2523, 2784, 3074, 3387, 3718, 4059, 4405, 4748, - 5080, 5394, 5683, 5939, 6155, 6324, 6442, 6502, 6500, 6433, 6297, 6092, 5818, 5474, 5062, 4587, 4052, 3461, 2822, 2141, 1427, 688, -66, -828, -1585, -2329, -3049, -3735, -4378, -4968, -5497, -5957, - -6342, -6645, -6863, -6990, -7025, -6966, -6814, -6570, -6236, -5817, -5317, -4742, -4100, -3398, -2645, -1850, -1022, -173, 688, 1552, 2407, 3245, 4056, 4831, 5563, 6243, 6867, 7428, 7923, 8348, 8702, 8983, - 9191, 9329, 9398, 9402, 9344, 9231, 9067, 8859, 8613, 8336, 8036, 7719, 7392, 7063, 6737, 6420, 6118, 5836, 5577, 5345, 5141, 4967, 4824, 4712, 4628, 4572, 4540, 4529, 4534, 4552, 4577, 4604, - 4628, 4643, 4644, 4626, 4584, 4514, 4411, 4273, 4097, 3881, 3624, 3326, 2988, 2610, 2197, 1750, 1274, 774, 254, -279, -820, -1363, -1900, -2425, -2931, -3412, -3861, -4274, -4645, -4969, -5243, -5463, - -5629, -5738, -5792, -5789, -5733, -5626, -5472, -5274, -5040, -4774, -4483, -4174, -3855, -3533, -3215, -2910, -2626, -2368, -2144, -1960, -1821, -1731, -1695, -1716, -1794, -1930, -2125, -2375, -2679, -3033, -3432, -3870, - -4341, -4837, -5351, -5874, -6398, -6913, -7410, -7880, -8315, -8707, -9047, -9328, -9545, -9691, -9762, -9755, -9668, -9500, -9250, -8920, -8513, -8033, -7485, -6874, -6208, -5494, -4741, -3958, -3155, -2341, -1527, -723, - 61, 816, 1533, 2202, 2816, 3368, 3852, 4262, 4595, 4846, 5016, 5102, 5106, 5030, 4875, 4646, 4348, 3986, 3567, 3097, 2585, 2037, 1463, 870, 267, -338, -939, -1527, -2096, -2640, -3153, -3631, - -4070, -4468, -4823, -5133, -5399, -5622, -5803, -5944, -6050, -6123, -6167, -6189, -6191, -6180, -6160, -6137, -6116, -6100, -6094, -6102, -6126, -6170, -6234, -6319, -6426, -6554, -6702, -6866, -7045, -7235, -7430, -7628, - -7822, -8007, -8177, -8328, -8453, -8546, -8603, -8618, -8588, -8509, -8377, -8192, -7950, -7653, -7301, -6895, -6437, -5931, -5382, -4795, -4176, -3530, -2867, -2193, -1516, -845, -189, 443, 1045, 1607, 2123, 2586, - 2988, 3326, 3594, 3789, 3908, 3951, 3917, 3808, 3625, 3373, 3055, 2677, 2245, 1768, 1253, 708, 144, -430, -1006, -1573, -2121, -2641, -3124, -3562, -3946, -4271, -4529, -4717, -4830, -4866, -4823, -4701, - -4501, -4225, -3876, -3459, -2979, -2442, -1855, -1227, -566, 119, 820, 1527, 2232, 2924, 3596, 4240, 4847, 5410, 5925, 6385, 6787, 7127, 7404, 7615, 7763, 7847, 7871, 7837, 7750, 7614, 7436, 7222, - 6978, 6713, 6433, 6147, 5860, 5582, 5319, 5078, 4864, 4683, 4540, 4438, 4381, 4370, 4407, 4492, 4624, 4801, 5021, 5280, 5574, 5898, 6248, 6616, 6998, 7387, 7776, 8159, 8530, 8882, 9212, 9513, - 9781, 10012, 10204, 10354, 10460, 10523, 10543, 10520, 10457, 10356, 10221, 10056, 9866, 9655, 9428, 9192, 8951, 8712, 8480, 8260, 8056, 7873, 7715, 7584, 7483, 7414, 7376, 7369, 7392, 7443, 7520, 7617, - 7730, 7855, 7985, 8114, 8234, 8340, 8423, 8477, 8495, 8469, 8393, 8262, 8070, 7813, 7488, 7091, 6621, 6079, 5464, 4778, 4024, 3207, 2332, 1404, 431, -578, -1615, -2672, -3738, -4802, -5855, -6885, - -7883, -8838, -9741, -10582, -11353, -12046, -12655, -13174, -13600, -13927, -14155, -14283, -14311, -14241, -14076, -13820, -13480, -13060, -12569, -12014, -11405, -10750, -10060, -9344, -8612, -7874, -7140, -6420, -5721, -5053, -4422, -3836, - -3299, -2817, -2393, -2029, -1728, -1488, -1309, -1190, -1127, -1116, -1154, -1234, -1352, -1500, -1672, -1861, -2060, -2263, -2462, -2651, -2825, -2978, -3106, -3204, -3269, -3299, -3292, -3249, -3168, -3052, -2902, -2722, - -2514, -2283, -2033, -1770, -1500, -1227, -958, -698, -453, -228, -28, 141, 278, 378, 439, 460, 439, 376, 272, 130, -48, -260, -501, -767, -1051, -1349, -1655, -1961, -2263, -2552, -2824, -3072, - -3290, -3474, -3618, -3720, -3776, -3783, -3741, -3649, -3507, -3318, -3084, -2808, -2494, -2148, -1775, -1382, -976, -564, -153, 248, 632, 993, 1321, 1611, 1857, 2051, 2191, 2272, 2290, 2244, 2134, 1958, - 1719, 1419, 1061, 650, 192, -306, -839, -1399, -1975, -2561, -3146, -3721, -4277, -4806, -5297, -5744, -6137, -6472, -6740, -6938, -7061, -7106, -7071, -6956, -6762, -6491, -6145, -5728, -5247, -4707, -4116, -3482, - -2813, -2118, -1408, -691, 20, 720, 1395, 2039, 2641, 3194, 3691, 4125, 4491, 4784, 5001, 5141, 5201, 5183, 5087, 4917, 4675, 4367, 3996, 3570, 3096, 2580, 2031, 1457, 865, 264, -337, -932, - -1513, -2073, -2606, -3106, -3569, -3990, -4367, -4697, -4979, -5214, -5401, -5543, -5641, -5699, -5720, -5709, -5670, -5608, -5529, -5438, -5341, -5241, -5145, -5057, -4981, -4920, -4877, -4855, -4855, -4878, -4923, -4990, - -5077, -5181, -5300, -5430, -5566, -5705, -5840, -5966, -6079, -6172, -6241, -6280, -6285, -6251, -6175, -6054, -5885, -5667, -5399, -5082, -4718, -4308, -3856, -3366, -2842, -2292, -1721, -1136, -545, 42, 620, 1178, - 1709, 2203, 2653, 3051, 3390, 3663, 3865, 3991, 4037, 4000, 3879, 3673, 3383, 3011, 2560, 2034, 1439, 780, 66, -696, -1497, -2329, -3181, -4044, -4907, -5761, -6595, -7401, -8169, -8891, -9560, -10168, - -10711, -11184, -11582, -11905, -12150, -12318, -12410, -12429, -12377, -12261, -12085, -11855, -11580, -11266, -10923, -10558, -10180, -9799, -9422, -9058, -8716, -8402, -8124, -7888, -7698, -7559, -7473, -7444, -7472, -7557, -7698, -7893, - -8138, -8429, -8761, -9129, -9526, -9945, -10379, -10819, -11259, -11691, -12106, -12498, -12859, -13183, -13465, -13699, -13880, -14006, -14074, -14083, -14031, -13919, -13749, -13523, -13243, -12915, -12541, -12128, -11681, -11207, -10711, -10200, - -9680, -9159, -8642, -8134, -7642, -7171, -6724, -6304, -5916, -5560, -5238, -4950, -4696, -4474, -4282, -4117, -3976, -3855, -3750, -3655, -3565, -3476, -3381, -3277, -3158, -3019, -2857, -2668, -2448, -2197, -1913, -1595, - -1244, -861, -448, -9, 452, 932, 1425, 1924, 2423, 2914, 3391, 3845, 4270, 4657, 4999, 5290, 5523, 5691, 5791, 5818, 5769, 5640, 5432, 5143, 4776, 4332, 3815, 3229, 2580, 1875, 1120, 324, - -502, -1352, -2214, -3078, -3934, -4771, -5579, -6349, -7071, -7737, -8338, -8867, -9319, -9688, -9972, -10166, -10270, -10285, -10210, -10049, -9805, -9483, -9089, -8629, -8112, -7545, -6938, -6299, -5639, -4967, -4294, -3627, - -2978, -2353, -1763, -1214, -712, -264, 126, 454, 718, 917, 1049, 1117, 1121, 1064, 951, 785, 573, 320, 33, -280, -614, -962, -1314, -1666, -2008, -2336, -2643, -2924, -3174, -3389, -3567, -3705, - -3802, -3857, -3871, -3846, -3784, -3687, -3559, -3405, -3228, -3035, -2831, -2621, -2410, -2205, -2009, -1829, -1669, -1532, -1423, -1343, -1294, -1278, -1295, -1344, -1424, -1532, -1666, -1821, -1994, -2179, -2371, -2563, - -2750, -2926, -3084, -3217, -3320, -3388, -3415, -3396, -3327, -3206, -3029, -2796, -2506, -2159, -1757, -1302, -797, -247, 343, 968, 1621, 2295, 2983, 3677, 4368, 5050, 5713, 6351, 6955, 7520, 8038, 8505, - 8915, 9266, 9553, 9776, 9934, 10028, 10059, 10029, 9944, 9806, 9623, 9401, 9147, 8869, 8575, 8275, 7976, 7689, 7421, 7182, 6980, 6822, 6715, 6666, 6680, 6761, 6912, 7135, 7431, 7801, 8241, 8750, - 9323, 9955, 10641, 11374, 12144, 12945, 13767, 14600, 15435, 16262, 17070, 17851, 18595, 19292, 19935, 20516, 21028, 21466, 21825, 22101, 22292, 22396, 22414, 22346, 22195, 21963, 21655, 21277, 20834, 20333, 19781, 19187, - 18558, 17902, 17229, 16547, 15863, 15185, 14522, 13878, 13262, 12678, 12130, 11622, 11158, 10738, 10364, 10036, 9752, 9511, 9311, 9148, 9019, 8919, 8843, 8788, 8747, 8716, 8690, 8664, 8633, 8594, 8543, 8477, - 8393, 8291, 8170, 8029, 7869, 7693, 7501, 7298, 7086, 6871, 6656, 6446, 6248, 6066, 5906, 5774, 5675, 5613, 5593, 5620, 5696, 5824, 6006, 6242, 6534, 6879, 7275, 7721, 8211, 8741, 9307, 9900, - 10514, 11143, 11776, 12407, 13026, 13624, 14194, 14726, 15212, 15646, 16019, 16327, 16562, 16721, 16801, 16799, 16714, 16546, 16296, 15967, 15562, 15085, 14543, 13942, 13289, 12592, 11861, 11104, 10331, 9552, 8776, 8013, - 7273, 6564, 5894, 5271, 4702, 4193, 3748, 3372, 3066, 2832, 2670, 2579, 2557, 2600, 2705, 2866, 3076, 3328, 3615, 3928, 4259, 4599, 4939, 5271, 5584, 5873, 6127, 6341, 6508, 6623, 6680, 6677, - 6610, 6479, 6282, 6022, 5699, 5316, 4878, 4389, 3855, 3282, 2676, 2046, 1399, 743, 86, -563, -1199, -1813, -2398, -2948, -3458, -3922, -4336, -4698, -5004, -5255, -5449, -5587, -5670, -5702, -5685, -5624, - -5522, -5386, -5220, -5031, -4825, -4607, -4384, -4162, -3947, -3744, -3556, -3390, -3247, -3131, -3044, -2986, -2959, -2961, -2991, -3047, -3126, -3225, -3339, -3464, -3595, -3726, -3853, -3969, -4069, -4148, -4201, -4224, - -4212, -4162, -4072, -3940, -3765, -3546, -3286, -2984, -2644, -2270, -1866, -1436, -987, -525, -57, 409, 867, 1309, 1727, 2113, 2462, 2765, 3016, 3211, 3344, 3412, 3412, 3342, 3201, 2990, 2711, 2365, - 1958, 1493, 977, 417, -179, -804, -1449, -2103, -2757, -3400, -4024, -4617, -5171, -5676, -6124, -6506, -6816, -7048, -7197, -7258, -7230, -7111, -6900, -6600, -6211, -5739, -5187, -4562, -3870, -3119, -2318, -1475, - -601, 294, 1201, 2110, 3011, 3893, 4747, 5564, 6337, 7058, 7721, 8319, 8850, 9309, 9694, 10004, 10240, 10402, 10492, 10514, 10472, 10370, 10215, 10012, 9767, 9489, 9183, 8858, 8519, 8175, 7831, 7493, - 7168, 6859, 6572, 6309, 6072, 5865, 5686, 5537, 5416, 5321, 5250, 5199, 5165, 5143, 5129, 5117, 5102, 5079, 5042, 4986, 4907, 4800, 4661, 4487, 4275, 4023, 3731, 3397, 3024, 2611, 2161, 1678, - 1165, 626, 68, -504, -1085, -1668, -2245, -2812, -3360, -3883, -4375, -4830, -5243, -5609, -5925, -6186, -6392, -6540, -6631, -6664, -6642, -6567, -6442, -6272, -6061, -5817, -5544, -5250, -4943, -4630, -4318, -4015, - -3729, -3467, -3235, -3040, -2888, -2782, -2728, -2727, -2782, -2893, -3060, -3282, -3556, -3879, -4246, -4652, -5089, -5552, -6032, -6521, -7010, -7491, -7954, -8390, -8791, -9149, -9454, -9701, -9882, -9992, -10027, -9983, - -9857, -9649, -9358, -8985, -8533, -8006, -7409, -6746, -6026, -5256, -4443, -3599, -2731, -1849, -965, -88, 771, 1603, 2400, 3151, 3849, 4486, 5056, 5553, 5973, 6314, 6571, 6746, 6838, 6848, 6779, 6634, - 6419, 6138, 5797, 5404, 4965, 4489, 3983, 3456, 2915, 2370, 1826, 1292, 774, 279, -187, -622, -1021, -1381, -1699, -1976, -2210, -2403, -2556, -2672, -2753, -2804, -2828, -2830, -2815, -2788, -2754, -2719, - -2687, -2663, -2651, -2656, -2679, -2724, -2793, -2886, -3004, -3147, -3312, -3499, -3704, -3923, -4153, -4390, -4627, -4860, -5083, -5291, -5477, -5637, -5765, -5856, -5905, -5910, -5866, -5773, -5627, -5429, -5179, -4878, - -4529, -4134, -3699, -3227, -2725, -2200, -1658, -1107, -555, -11, 516, 1020, 1491, 1922, 2304, 2631, 2897, 3096, 3224, 3277, 3252, 3148, 2964, 2703, 2366, 1955, 1476, 935, 336, -311, -1000, -1722, - -2468, -3227, -3990, -4748, -5490, -6207, -6889, -7529, -8118, -8649, -9115, -9513, -9836, -10083, -10252, -10341, -10352, -10287, -10147, -9938, -9665, -9333, -8949, -8521, -8057, -7566, -7057, -6538, -6019, -5509, -5016, -4549, - -4114, -3720, -3371, -3074, -2833, -2650, -2528, -2468, -2470, -2533, -2654, -2830, -3058, -3332, -3646, -3994, -4370, -4765, -5173, -5586, -5996, -6395, -6777, -7134, -7461, -7752, -8002, -8207, -8363, -8469, -8522, -8524, - -8473, -8373, -8224, -8030, -7796, -7526, -7225, -6898, -6552, -6193, -5828, -5462, -5102, -4754, -4423, -4115, -3833, -3583, -3366, -3186, -3044, -2940, -2875, -2847, -2855, -2896, -2967, -3064, -3182, -3317, -3463, -3616, - -3769, -3916, -4053, -4175, -4276, -4352, -4400, -4416, -4398, -4345, -4256, -4131, -3972, -3780, -3558, -3311, -3042, -2758, -2463, -2164, -1869, -1583, -1316, -1073, -862, -690, -563, -488, -470, -513, -623, -800, - -1048, -1366, -1756, -2214, -2739, -3327, -3973, -4670, -5413, -6193, -7002, -7831, -8670, -9509, -10338, -11146, -11923, -12659, -13344, -13970, -14527, -15008, -15407, -15718, -15935, -16057, -16080, -16004, -15829, -15558, -15192, -14735, - -14195, -13575, -12885, -12131, -11324, -10471, -9582, -8669, -7740, -6807, -5878, -4963, -4072, -3213, -2393, -1619, -898, -234, 368, 908, 1382, 1790, 2133, 2413, 2631, 2791, 2899, 2958, 2974, 2955, 2905, 2833, - 2744, 2646, 2545, 2449, 2361, 2289, 2237, 2209, 2208, 2236, 2297, 2389, 2513, 2668, 2852, 3063, 3296, 3548, 3815, 4091, 4371, 4650, 4921, 5178, 5417, 5632, 5818, 5970, 6085, 6158, 6189, 6174, - 6113, 6005, 5853, 5656, 5418, 5141, 4830, 4490, 4125, 3741, 3345, 2942, 2540, 2144, 1761, 1397, 1057, 749, 475, 240, 47, -100, -202, -256, -264, -227, -146, -25, 131, 319, 533, 767, - 1013, 1265, 1516, 1757, 1982, 2182, 2351, 2481, 2567, 2603, 2584, 2506, 2366, 2162, 1893, 1559, 1161, 702, 185, -385, -1004, -1664, -2359, -3081, -3820, -4569, -5318, -6058, -6778, -7471, -8127, -8738, - -9295, -9791, -10221, -10579, -10859, -11060, -11179, -11215, -11168, -11040, -10834, -10553, -10203, -9790, -9320, -8802, -8244, -7654, -7043, -6420, -5794, -5176, -4576, -4002, -3463, -2967, -2522, -2133, -1808, -1550, -1362, -1247, - -1206, -1240, -1346, -1523, -1767, -2074, -2438, -2855, -3316, -3816, -4345, -4896, -5461, -6032, -6600, -7158, -7699, -8214, -8699, -9146, -9552, -9913, -10224, -10485, -10692, -10848, -10950, -11002, -11006, -10963, -10879, -10756, - -10601, -10417, -10210, -9986, -9750, -9506, -9261, -9019, -8784, -8560, -8349, -8155, -7979, -7822, -7684, -7564, -7461, -7373, -7297, -7230, -7167, -7104, -7036, -6957, -6864, -6749, -6609, -6437, -6230, -5983, -5692, -5354, - -4968, -4531, -4043, -3505, -2917, -2282, -1602, -883, -129, 654, 1460, 2281, 3111, 3940, 4760, 5563, 6339, 7081, 7780, 8428, 9017, 9542, 9995, 10373, 10670, 10884, 11013, 11055, 11012, 10885, 10677, 10391, - 10033, 9609, 9125, 8589, 8011, 7398, 6760, 6107, 5450, 4797, 4159, 3545, 2964, 2425, 1935, 1500, 1127, 820, 584, 419, 329, 312, 368, 495, 688, 944, 1258, 1622, 2029, 2473, 2944, 3435, - 3936, 4438, 4934, 5414, 5870, 6295, 6681, 7023, 7315, 7553, 7734, 7854, 7914, 7912, 7850, 7729, 7553, 7326, 7053, 6739, 6390, 6014, 5618, 5209, 4795, 4385, 3985, 3603, 3247, 2923, 2636, 2393, - 2197, 2052, 1962, 1927, 1950, 2028, 2162, 2350, 2588, 2872, 3200, 3565, 3962, 4384, 4827, 5283, 5746, 6209, 6667, 7114, 7544, 7951, 8333, 8686, 9005, 9291, 9540, 9754, 9931, 10075, 10186, 10267, - 10322, 10355, 10370, 10373, 10367, 10359, 10354, 10357, 10373, 10406, 10462, 10542, 10652, 10792, 10965, 11171, 11410, 11681, 11982, 12310, 12661, 13031, 13414, 13806, 14198, 14585, 14958, 15311, 15636, 15925, 16172, 16369, - 16509, 16587, 16598, 16537, 16400, 16186, 15893, 15521, 15070, 14543, 13943, 13274, 12542, 11752, 10913, 10033, 9119, 8183, 7233, 6280, 5335, 4407, 3507, 2645, 1830, 1071, 376, -246, -792, -1256, -1633, -1920, - -2116, -2222, -2236, -2163, -2006, -1768, -1456, -1076, -636, -144, 390, 959, 1552, 2159, 2772, 3379, 3971, 4541, 5079, 5577, 6030, 6431, 6774, 7057, 7277, 7430, 7518, 7541, 7500, 7397, 7237, 7024, - 6762, 6459, 6119, 5750, 5360, 4954, 4541, 4128, 3721, 3327, 2951, 2600, 2278, 1989, 1736, 1522, 1347, 1212, 1117, 1061, 1041, 1054, 1098, 1167, 1258, 1364, 1482, 1604, 1726, 1842, 1947, 2035, - 2103, 2145, 2159, 2142, 2091, 2005, 1884, 1729, 1540, 1319, 1070, 796, 501, 190, -130, -456, -780, -1097, -1400, -1682, -1938, -2162, -2349, -2494, -2593, -2642, -2640, -2583, -2473, -2308, -2090, -1822, - -1506, -1146, -748, -316, 141, 620, 1111, 1606, 2099, 2581, 3044, 3480, 3882, 4243, 4557, 4818, 5022, 5164, 5241, 5253, 5197, 5075, 4888, 4639, 4330, 3968, 3557, 3104, 2616, 2102, 1568, 1026, - 482, -52, -570, -1062, -1519, -1934, -2300, -2609, -2857, -3039, -3151, -3191, -3158, -3052, -2874, -2627, -2314, -1939, -1510, -1031, -512, 40, 616, 1207, 1803, 2395, 2971, 3524, 4043, 4519, 4945, 5313, - 5615, 5847, 6002, 6079, 6073, 5984, 5811, 5555, 5218, 4804, 4317, 3761, 3142, 2468, 1746, 983, 188, -630, -1464, -2305, -3145, -3975, -4788, -5577, -6336, -7058, -7741, -8378, -8968, -9509, -9998, -10437, - -10825, -11164, -11457, -11707, -11916, -12089, -12231, -12347, -12441, -12518, -12584, -12643, -12699, -12757, -12821, -12893, -12975, -13070, -13178, -13299, -13433, -13579, -13733, -13894, -14058, -14220, -14377, -14523, -14652, -14760, -14841, -14890, - -14900, -14868, -14788, -14657, -14472, -14228, -13926, -13563, -13139, -12656, -12115, -11519, -10872, -10178, -9442, -8672, -7874, -7056, -6226, -5392, -4563, -3748, -2955, -2193, -1471, -796, -176, 383, 876, 1298, 1645, 1914, - 2104, 2215, 2247, 2202, 2084, 1896, 1645, 1336, 976, 574, 138, -322, -799, -1282, -1762, -2229, -2673, -3087, -3461, -3788, -4062, -4276, -4426, -4508, -4519, -4457, -4324, -4118, -3844, -3503, -3100, -2641, - -2132, -1580, -993, -378, 254, 896, 1539, 2173, 2790, 3380, 3937, 4452, 4920, 5333, 5688, 5980, 6206, 6365, 6456, 6479, 6437, 6330, 6164, 5943, 5671, 5356, 5003, 4620, 4214, 3794, 3366, 2940, - 2522, 2120, 1741, 1391, 1076, 800, 569, 385, 250, 167, 135, 153, 222, 337, 496, 695, 930, 1195, 1486, 1795, 2117, 2446, 2776, 3100, 3413, 3710, 3986, 4236, 4457, 4646, 4802, 4922, - 5006, 5055, 5070, 5053, 5007, 4935, 4841, 4729, 4606, 4476, 4344, 4216, 4098, 3994, 3911, 3852, 3821, 3823, 3860, 3935, 4047, 4198, 4387, 4613, 4873, 5164, 5482, 5822, 6178, 6545, 6915, 7282, - 7638, 7976, 8288, 8566, 8805, 8996, 9134, 9214, 9229, 9178, 9056, 8863, 8596, 8256, 7846, 7367, 6823, 6220, 5562, 4857, 4113, 3337, 2539, 1729, 915, 110, -677, -1438, -2161, -2837, -3457, -4014, - -4500, -4908, -5234, -5472, -5621, -5678, -5643, -5517, -5301, -4999, -4616, -4156, -3627, -3035, -2390, -1700, -974, -222, 544, 1317, 2086, 2840, 3572, 4271, 4929, 5540, 6097, 6593, 7025, 7389, 7683, 7906, - 8057, 8137, 8149, 8096, 7981, 7809, 7586, 7317, 7010, 6671, 6308, 5927, 5535, 5140, 4749, 4367, 4000, 3653, 3330, 3035, 2771, 2540, 2341, 2176, 2044, 1942, 1869, 1821, 1795, 1786, 1789, 1800, - 1813, 1823, 1824, 1812, 1780, 1726, 1644, 1532, 1386, 1204, 986, 729, 436, 107, -254, -648, -1069, -1513, -1975, -2449, -2929, -3410, -3884, -4345, -4786, -5201, -5584, -5928, -6229, -6481, -6681, -6826, - -6913, -6941, -6909, -6818, -6669, -6465, -6209, -5905, -5559, -5176, -4763, -4326, -3874, -3412, -2950, -2496, -2055, -1637, -1248, -894, -582, -315, -99, 63, 170, 220, 214, 152, 37, -128, -339, -592, - -879, -1193, -1528, -1874, -2224, -2569, -2899, -3206, -3482, -3716, -3903, -4034, -4104, -4105, -4034, -3886, -3660, -3354, -2968, -2502, -1960, -1344, -659, 88, 893, 1746, 2640, 3566, 4513, 5473, 6434, 7387, - 8322, 9228, 10096, 10918, 11684, 12388, 13022, 13581, 14061, 14458, 14769, 14993, 15131, 15184, 15153, 15043, 14857, 14601, 14281, 13904, 13476, 13005, 12500, 11969, 11420, 10860, 10297, 9740, 9194, 8667, 8163, 7688, - 7245, 6837, 6467, 6137, 5845, 5593, 5378, 5198, 5050, 4932, 4838, 4765, 4707, 4659, 4617, 4575, 4529, 4474, 4405, 4319, 4212, 4082, 3927, 3747, 3539, 3306, 3047, 2766, 2465, 2147, 1816, 1478, - 1136, 796, 465, 147, -151, -424, -667, -874, -1042, -1165, -1241, -1267, -1241, -1163, -1033, -851, -620, -343, -23, 333, 722, 1137, 1571, 2016, 2464, 2908, 3338, 3746, 4125, 4466, 4762, 5005, - 5189, 5309, 5359, 5337, 5240, 5065, 4813, 4484, 4080, 3605, 3062, 2456, 1795, 1085, 334, -448, -1255, -2075, -2898, -3716, -4518, -5295, -6036, -6734, -7381, -7968, -8491, -8942, -9318, -9616, -9833, -9969, - -10024, -9999, -9897, -9723, -9481, -9177, -8817, -8410, -7963, -7484, -6984, -6470, -5951, -5438, -4938, -4460, -4012, -3601, -3232, -2912, -2645, -2434, -2283, -2191, -2160, -2188, -2274, -2414, -2605, -2842, -3119, -3430, - -3769, -4128, -4500, -4877, -5252, -5618, -5966, -6290, -6585, -6844, -7062, -7235, -7361, -7435, -7458, -7429, -7347, -7215, -7034, -6808, -6541, -6236, -5901, -5539, -5157, -4762, -4359, -3956, -3558, -3172, -2802, -2456, - -2137, -1849, -1595, -1379, -1202, -1065, -968, -911, -891, -906, -954, -1030, -1131, -1251, -1385, -1529, -1676, -1822, -1960, -2085, -2194, -2280, -2342, -2374, -2376, -2344, -2280, -2182, -2052, -1891, -1702, -1490, - -1258, -1011, -756, -498, -245, -4, 219, 416, 579, 703, 779, 802, 767, 668, 502, 266, -43, -426, -882, -1410, -2008, -2672, -3397, -4178, -5008, -5880, -6784, -7712, -8654, -9601, -10541, -11465, - -12362, -13222, -14035, -14792, -15484, -16103, -16642, -17096, -17459, -17728, -17901, -17975, -17952, -17832, -17619, -17315, -16927, -16459, -15920, -15316, -14657, -13952, -13209, -12440, -11653, -10860, -10069, -9291, -8534, -7807, -7118, -6473, - -5879, -5340, -4860, -4444, -4091, -3803, -3579, -3418, -3317, -3274, -3283, -3339, -3437, -3571, -3734, -3920, -4120, -4330, -4540, -4746, -4940, -5118, -5275, -5405, -5505, -5574, -5608, -5607, -5571, -5500, -5396, -5262, - -5101, -4916, -4712, -4494, -4267, -4037, -3810, -3590, -3384, -3197, -3034, -2899, -2797, -2731, -2704, -2717, -2772, -2868, -3006, -3184, -3399, -3648, -3928, -4233, -4560, -4902, -5253, -5607, -5958, -6298, -6623, -6924, - -7197, -7437, -7637, -7795, -7906, -7969, -7980, -7941, -7850, -7708, -7519, -7284, -7008, -6695, -6351, -5982, -5595, -5196, -4794, -4395, -4008, -3639, -3297, -2988, -2719, -2495, -2322, -2203, -2143, -2144, -2206, -2330, - -2515, -2760, -3060, -3412, -3811, -4251, -4724, -5223, -5740, -6266, -6791, -7308, -7806, -8276, -8710, -9099, -9434, -9710, -9919, -10056, -10116, -10096, -9995, -9810, -9542, -9193, -8764, -8261, -7688, -7051, -6357, -5613, - -4828, -4011, -3173, -2321, -1467, -620, 209, 1012, 1780, 2505, 3177, 3792, 4342, 4824, 5232, 5565, 5822, 6001, 6103, 6131, 6088, 5977, 5804, 5573, 5293, 4969, 4610, 4222, 3815, 3396, 2974, 2556, - 2149, 1761, 1398, 1065, 768, 511, 297, 129, 8, -66, -93, -74, -12, 89, 229, 402, 603, 828, 1070, 1325, 1588, 1852, 2113, 2366, 2606, 2831, 3036, 3219, 3378, 3512, 3621, 3705, - 3766, 3805, 3825, 3831, 3825, 3812, 3797, 3785, 3781, 3792, 3821, 3875, 3958, 4074, 4227, 4421, 4658, 4940, 5267, 5639, 6056, 6516, 7015, 7550, 8116, 8707, 9318, 9941, 10568, 11193, 11805, 12398, - 12962, 13489, 13970, 14398, 14766, 15066, 15292, 15440, 15505, 15484, 15375, 15177, 14890, 14517, 14059, 13520, 12905, 12221, 11473, 10671, 9822, 8935, 8020, 7086, 6145, 5205, 4278, 3372, 2496, 1660, 871, 136, - -537, -1145, -1685, -2151, -2544, -2862, -3107, -3279, -3383, -3422, -3401, -3327, -3207, -3047, -2855, -2641, -2413, -2179, -1948, -1728, -1528, -1356, -1217, -1119, -1067, -1064, -1116, -1224, -1389, -1611, -1891, -2226, - -2612, -3047, -3525, -4041, -4588, -5160, -5748, -6347, -6947, -7541, -8121, -8679, -9208, -9702, -10154, -10559, -10912, -11209, -11447, -11624, -11739, -11793, -11785, -11718, -11594, -11417, -11191, -10921, -10613, -10271, -9903, -9515, - -9112, -8703, -8292, -7886, -7490, -7109, -6748, -6410, -6099, -5816, -5564, -5342, -5150, -4988, -4853, -4743, -4655, -4584, -4527, -4479, -4434, -4388, -4335, -4270, -4188, -4085, -3955, -3797, -3605, -3379, -3116, -2815, - -2477, -2103, -1694, -1253, -784, -292, 218, 741, 1271, 1800, 2320, 2825, 3307, 3757, 4169, 4536, 4850, 5106, 5298, 5422, 5473, 5450, 5350, 5172, 4918, 4589, 4188, 3719, 3187, 2599, 1960, 1279, - 565, -172, -924, -1680, -2431, -3165, -3872, -4544, -5170, -5741, -6249, -6688, -7050, -7330, -7525, -7631, -7647, -7572, -7407, -7154, -6817, -6399, -5907, -5347, -4726, -4052, -3335, -2584, -1808, -1017, -220, 570, - 1347, 2101, 2823, 3506, 4142, 4725, 5250, 5713, 6110, 6441, 6703, 6898, 7026, 7090, 7093, 7040, 6935, 6784, 6594, 6371, 6122, 5854, 5575, 5291, 5010, 4738, 4482, 4246, 4036, 3855, 3707, 3595, - 3520, 3483, 3483, 3519, 3590, 3692, 3823, 3979, 4154, 4343, 4543, 4746, 4947, 5142, 5324, 5489, 5631, 5748, 5834, 5887, 5906, 5888, 5833, 5741, 5614, 5453, 5261, 5042, 4800, 4540, 4267, 3986, - 3705, 3428, 3164, 2917, 2694, 2501, 2344, 2227, 2154, 2130, 2156, 2235, 2368, 2555, 2795, 3085, 3424, 3807, 4229, 4686, 5170, 5677, 6197, 6724, 7249, 7766, 8265, 8739, 9182, 9585, 9943, 10249, - 10500, 10690, 10817, 10880, 10876, 10806, 10672, 10475, 10220, 9912, 9554, 9154, 8719, 8257, 7776, 7285, 6792, 6308, 5840, 5398, 4990, 4624, 4308, 4049, 3851, 3720, 3659, 3672, 3759, 3921, 4156, 4463, - 4838, 5277, 5773, 6321, 6914, 7542, 8197, 8871, 9553, 10233, 10902, 11549, 12167, 12745, 13275, 13749, 14160, 14503, 14772, 14963, 15074, 15102, 15047, 14910, 14693, 14398, 14029, 13591, 13091, 12534, 11927, 11279, - 10597, 9890, 9166, 8434, 7701, 6977, 6268, 5582, 4924, 4301, 3718, 3177, 2684, 2239, 1844, 1499, 1204, 957, 757, 599, 481, 399, 347, 322, 318, 329, 352, 381, 411, 438, 459, 470, - 469, 453, 422, 375, 313, 237, 149, 51, -53, -159, -264, -361, -447, -515, -560, -577, -561, -506, -407, -262, -66, 183, 488, 850, 1269, 1744, 2273, 2854, 3483, 4157, 4868, 5612, - 6381, 7168, 7964, 8762, 9553, 10327, 11076, 11792, 12466, 13090, 13656, 14159, 14592, 14951, 15230, 15429, 15544, 15576, 15524, 15391, 15180, 14895, 14540, 14123, 13649, 13127, 12566, 11973, 11359, 10733, 10105, 9484, - 8880, 8301, 7757, 7254, 6800, 6401, 6062, 5788, 5580, 5441, 5372, 5371, 5437, 5568, 5758, 6002, 6296, 6631, 7001, 7397, 7811, 8234, 8657, 9071, 9468, 9838, 10174, 10468, 10714, 10906, 11039, 11108, - 11111, 11046, 10913, 10710, 10441, 10106, 9711, 9259, 8755, 8206, 7618, 6999, 6356, 5697, 5030, 4362, 3703, 3059, 2437, 1844, 1286, 768, 294, -130, -504, -826, -1095, -1311, -1476, -1590, -1658, -1683, - -1668, -1620, -1543, -1443, -1326, -1198, -1066, -934, -810, -697, -601, -527, -478, -456, -464, -504, -575, -679, -812, -974, -1162, -1372, -1601, -1844, -2095, -2351, -2604, -2850, -3083, -3298, -3489, -3652, - -3783, -3877, -3932, -3946, -3916, -3844, -3728, -3570, -3373, -3140, -2874, -2580, -2264, -1931, -1589, -1243, -903, -575, -266, 14, 261, 467, 626, 731, 779, 764, 683, 535, 319, 34, -317, -734, - -1213, -1749, -2336, -2968, -3637, -4335, -5054, -5784, -6515, -7237, -7940, -8614, -9250, -9837, -10368, -10834, -11227, -11542, -11772, -11913, -11963, -11919, -11781, -11550, -11226, -10814, -10318, -9743, -9095, -8382, -7613, -6796, - -5940, -5056, -4154, -3243, -2334, -1437, -561, 284, 1090, 1850, 2556, 3204, 3787, 4302, 4747, 5119, 5419, 5647, 5805, 5895, 5921, 5888, 5801, 5665, 5488, 5276, 5035, 4773, 4497, 4214, 3930, 3651, - 3384, 3133, 2903, 2697, 2518, 2368, 2248, 2158, 2097, 2063, 2055, 2070, 2102, 2149, 2206, 2267, 2327, 2381, 2423, 2448, 2451, 2427, 2372, 2283, 2156, 1989, 1781, 1531, 1239, 906, 535, 127, - -313, -782, -1275, -1787, -2310, -2840, -3369, -3892, -4400, -4888, -5350, -5779, -6171, -6520, -6821, -7073, -7271, -7416, -7505, -7539, -7519, -7448, -7329, -7165, -6962, -6725, -6461, -6175, -5876, -5571, -5267, -4973, - -4695, -4442, -4219, -4034, -3893, -3799, -3759, -3774, -3848, -3980, -4173, -4423, -4731, -5091, -5501, -5954, -6446, -6968, -7514, -8075, -8643, -9208, -9763, -10297, -10802, -11269, -11690, -12058, -12366, -12607, -12777, -12871, - -12887, -12824, -12679, -12455, -12153, -11776, -11328, -10815, -10243, -9619, -8952, -8251, -7524, -6781, -6032, -5288, -4559, -3853, -3182, -2553, -1975, -1456, -1002, -619, -312, -84, 60, 123, 102, 0, -182, -442, - -774, -1173, -1634, -2149, -2712, -3315, -3950, -4608, -5281, -5961, -6641, -7312, -7969, -8603, -9209, -9783, -10320, -10817, -11271, -11681, -12047, -12368, -12645, -12882, -13080, -13243, -13374, -13479, -13562, -13627, -13680, -13726, - -13771, -13817, -13871, -13935, -14014, -14110, -14224, -14359, -14514, -14690, -14886, -15099, -15327, -15567, -15815, -16067, -16317, -16560, -16791, -17005, -17194, -17355, -17480, -17566, -17608, -17601, -17543, -17430, -17261, -17035, -16752, -16412, - -16018, -15573, -15080, -14544, -13971, -13367, -12739, -12093, -11439, -10785, -10138, -9508, -8903, -8330, -7798, -7314, -6885, -6516, -6213, -5979, -5818, -5730, -5718, -5781, -5917, -6123, -6396, -6730, -7120, -7559, -8038, -8551, - -9087, -9638, -10193, -10743, -11279, -11791, -12268, -12704, -13089, -13417, -13680, -13874, -13993, -14035, -13996, -13877, -13678, -13399, -13044, -12616, -12119, -11561, -10948, -10286, -9584, -8851, -8095, -7326, -6553, -5785, -5030, -4298, - -3597, -2932, -2312, -1742, -1227, -770, -374, -41, 228, 436, 583, 671, 706, 690, 629, 530, 399, 243, 69, -114, -300, -481, -651, -801, -927, -1021, -1079, -1096, -1070, -997, -876, -706, - -488, -222, 88, 441, 833, 1259, 1713, 2192, 2688, 3195, 3707, 4217, 4720, 5209, 5678, 6122, 6536, 6916, 7259, 7562, 7822, 8039, 8213, 8344, 8434, 8485, 8500, 8483, 8437, 8368, 8280, 8180, - 8072, 7962, 7855, 7758, 7675, 7609, 7567, 7549, 7561, 7602, 7675, 7779, 7913, 8077, 8267, 8480, 8712, 8957, 9211, 9467, 9719, 9959, 10181, 10377, 10539, 10662, 10738, 10761, 10725, 10627, 10460, 10223, - 9913, 9529, 9072, 8541, 7940, 7271, 6540, 5752, 4913, 4032, 3116, 2174, 1216, 252, -707, -1652, -2571, -3455, -4294, -5078, -5798, -6446, -7015, -7499, -7893, -8193, -8395, -8499, -8504, -8412, -8224, -7944, - -7578, -7130, -6608, -6019, -5372, -4676, -3939, -3173, -2387, -1591, -794, -7, 760, 1501, 2207, 2871, 3486, 4048, 4552, 4996, 5376, 5692, 5945, 6136, 6267, 6341, 6362, 6335, 6267, 6162, 6027, 5869, - 5696, 5513, 5328, 5147, 4976, 4821, 4688, 4580, 4501, 4455, 4442, 4465, 4523, 4616, 4742, 4899, 5083, 5292, 5520, 5763, 6016, 6272, 6527, 6774, 7008, 7224, 7415, 7578, 7709, 7803, 7858, 7871, - 7842, 7770, 7655, 7498, 7302, 7070, 6805, 6511, 6193, 5858, 5510, 5156, 4802, 4455, 4121, 3806, 3516, 3257, 3034, 2850, 2709, 2615, 2568, 2571, 2622, 2721, 2866, 3054, 3281, 3543, 3835, 4150, - 4481, 4823, 5167, 5505, 5831, 6136, 6413, 6655, 6856, 7009, 7109, 7151, 7133, 7052, 6905, 6694, 6417, 6078, 5679, 5224, 4718, 4167, 3577, 2957, 2314, 1658, 996, 339, -304, -925, -1514, -2063, - -2563, -3007, -3389, -3702, -3943, -4106, -4191, -4196, -4119, -3964, -3732, -3426, -3051, -2614, -2120, -1577, -993, -378, 258, 909, 1562, 2208, 2837, 3440, 4008, 4533, 5006, 5422, 5773, 6055, 6264, 6397, - 6453, 6431, 6332, 6158, 5911, 5596, 5217, 4780, 4292, 3759, 3189, 2591, 1972, 1340, 703, 71, -550, -1153, -1731, -2278, -2789, -3259, -3685, -4064, -4395, -4676, -4907, -5091, -5228, -5321, -5374, -5391, - -5375, -5332, -5267, -5185, -5092, -4992, -4891, -4794, -4705, -4628, -4565, -4521, -4496, -4492, -4509, -4546, -4603, -4678, -4768, -4869, -4977, -5089, -5199, -5302, -5393, -5466, -5516, -5538, -5527, -5478, -5387, -5252, - -5068, -4835, -4551, -4216, -3831, -3397, -2918, -2397, -1838, -1247, -630, 5, 653, 1304, 1952, 2586, 3199, 3782, 4327, 4825, 5270, 5653, 5970, 6214, 6382, 6470, 6475, 6397, 6235, 5990, 5666, 5265, - 4792, 4253, 3655, 3004, 2310, 1581, 827, 56, -718, -1490, -2248, -2981, -3682, -4341, -4951, -5503, -5993, -6415, -6764, -7038, -7235, -7354, -7396, -7363, -7257, -7083, -6846, -6552, -6208, -5820, -5397, -4948, - -4481, -4005, -3529, -3062, -2611, -2184, -1790, -1433, -1121, -858, -647, -491, -393, -352, -368, -440, -565, -740, -959, -1218, -1511, -1830, -2170, -2522, -2878, -3231, -3574, -3898, -4197, -4464, -4692, -4877, - -5012, -5096, -5123, -5093, -5005, -4857, -4651, -4389, -4073, -3706, -3294, -2840, -2350, -1830, -1287, -726, -154, 421, 995, 1561, 2113, 2646, 3155, 3637, 4087, 4505, 4887, 5234, 5545, 5821, 6064, 6276, - 6460, 6620, 6760, 6885, 6999, 7109, 7218, 7332, 7457, 7596, 7754, 7936, 8144, 8380, 8647, 8946, 9276, 9637, 10027, 10443, 10883, 11341, 11813, 12293, 12775, 13251, 13715, 14160, 14576, 14958, 15297, 15587, - 15820, 15990, 16093, 16122, 16075, 15948, 15740, 15450, 15077, 14625, 14095, 13492, 12820, 12085, 11294, 10456, 9578, 8671, 7743, 6804, 5866, 4939, 4032, 3157, 2322, 1536, 808, 146, -443, -955, -1384, -1729, - -1985, -2153, -2233, -2227, -2137, -1968, -1725, -1414, -1042, -618, -149, 354, 883, 1429, 1981, 2530, 3066, 3580, 4063, 4509, 4909, 5258, 5551, 5782, 5949, 6050, 6084, 6051, 5952, 5790, 5567, 5289, - 4959, 4585, 4171, 3725, 3253, 2764, 2264, 1761, 1261, 772, 299, -150, -573, -963, -1319, -1636, -1913, -2149, -2344, -2499, -2616, -2697, -2745, -2764, -2759, -2734, -2695, -2646, -2594, -2544, -2501, -2470, - -2456, -2464, -2497, -2557, -2648, -2771, -2926, -3113, -3332, -3580, -3855, -4154, -4472, -4805, -5148, -5494, -5838, -6174, -6494, -6794, -7066, -7304, -7504, -7660, -7768, -7824, -7826, -7770, -7657, -7485, -7256, -6972, - -6635, -6248, -5817, -5347, -4843, -4313, -3764, -3202, -2637, -2076, -1526, -997, -495, -27, 398, 778, 1106, 1377, 1588, 1738, 1824, 1847, 1807, 1708, 1551, 1342, 1085, 787, 455, 95, -283, -671, - -1061, -1444, -1810, -2150, -2457, -2722, -2938, -3098, -3196, -3227, -3188, -3076, -2889, -2626, -2290, -1881, -1404, -862, -260, 392, 1092, 1828, 2593, 3377, 4170, 4964, 5747, 6510, 7243, 7938, 8584, 9176, - 9705, 10164, 10550, 10856, 11081, 11222, 11278, 11250, 11139, 10948, 10680, 10341, 9935, 9468, 8949, 8384, 7782, 7150, 6498, 5833, 5163, 4498, 3844, 3208, 2598, 2018, 1474, 970, 510, 94, -273, -593, - -867, -1095, -1278, -1421, -1527, -1599, -1642, -1660, -1660, -1645, -1620, -1592, -1564, -1541, -1526, -1523, -1535, -1563, -1609, -1673, -1755, -1854, -1969, -2095, -2231, -2373, -2515, -2654, -2784, -2899, -2995, -3066, - -3106, -3110, -3074, -2993, -2863, -2682, -2448, -2158, -1814, -1413, -960, -455, 97, 694, 1329, 1996, 2690, 3402, 4125, 4850, 5570, 6274, 6955, 7603, 8211, 8771, 9275, 9716, 10088, 10388, 10609, 10750, - 10809, 10784, 10677, 10489, 10222, 9881, 9471, 8997, 8467, 7888, 7269, 6619, 5947, 5262, 4574, 3894, 3229, 2591, 1986, 1424, 911, 454, 58, -272, -534, -725, -845, -894, -873, -785, -634, -424, - -163, 144, 490, 866, 1265, 1676, 2092, 2504, 2902, 3279, 3625, 3934, 4199, 4413, 4572, 4671, 4707, 4677, 4582, 4421, 4195, 3906, 3558, 3156, 2703, 2206, 1672, 1108, 521, -80, -689, -1298, - -1897, -2481, -3041, -3570, -4064, -4516, -4922, -5279, -5583, -5832, -6027, -6166, -6252, -6285, -6269, -6207, -6103, -5963, -5792, -5595, -5378, -5148, -4910, -4671, -4436, -4212, -4002, -3812, -3645, -3504, -3392, -3311, - -3261, -3242, -3253, -3293, -3359, -3447, -3555, -3678, -3810, -3948, -4085, -4215, -4334, -4437, -4517, -4570, -4592, -4579, -4529, -4438, -4305, -4130, -3913, -3655, -3358, -3026, -2661, -2269, -1854, -1424, -984, -542, - -105, 319, 723, 1100, 1442, 1743, 1995, 2192, 2330, 2405, 2412, 2350, 2217, 2013, 1739, 1397, 990, 523, 0, -570, -1183, -1830, -2501, -3188, -3881, -4570, -5245, -5895, -6512, -7085, -7606, -8066, - -8458, -8775, -9012, -9163, -9226, -9199, -9080, -8869, -8570, -8184, -7715, -7169, -6551, -5870, -5133, -4349, -3526, -2676, -1808, -932, -58, 803, 1644, 2453, 3223, 3946, 4615, 5223, 5767, 6242, 6645, 6974, - 7229, 7410, 7519, 7559, 7533, 7445, 7301, 7107, 6868, 6592, 6286, 5956, 5610, 5255, 4898, 4545, 4202, 3874, 3565, 3281, 3023, 2793, 2594, 2425, 2286, 2176, 2092, 2032, 1993, 1969, 1957, 1951, - 1947, 1939, 1922, 1890, 1839, 1764, 1660, 1525, 1354, 1146, 899, 612, 286, -78, -480, -916, -1383, -1877, -2393, -2925, -3467, -4014, -4559, -5095, -5615, -6113, -6583, -7017, -7411, -7760, -8059, -8305, - -8495, -8626, -8699, -8713, -8669, -8569, -8416, -8215, -7968, -7683, -7364, -7020, -6656, -6280, -5901, -5525, -5160, -4813, -4493, -4204, -3954, -3747, -3587, -3479, -3424, -3425, -3481, -3592, -3756, -3970, -4230, -4531, - -4868, -5233, -5619, -6019, -6423, -6823, -7210, -7576, -7910, -8205, -8453, -8645, -8775, -8837, -8825, -8736, -8566, -8314, -7978, -7560, -7061, -6484, -5833, -5114, -4332, -3495, -2611, -1689, -738, 231, 1210, 2188, - 3155, 4100, 5015, 5889, 6715, 7484, 8189, 8824, 9385, 9867, 10268, 10586, 10820, 10971, 11040, 11032, 10949, 10797, 10582, 10308, 9985, 9619, 9218, 8789, 8342, 7885, 7424, 6968, 6523, 6097, 5694, 5320, - 4980, 4676, 4411, 4186, 4003, 3861, 3758, 3694, 3664, 3667, 3697, 3751, 3824, 3910, 4005, 4103, 4199, 4289, 4368, 4431, 4476, 4499, 4498, 4472, 4419, 4341, 4236, 4108, 3958, 3790, 3607, 3414, - 3215, 3015, 2820, 2636, 2468, 2322, 2203, 2116, 2066, 2058, 2093, 2177, 2309, 2492, 2726, 3009, 3340, 3717, 4136, 4592, 5079, 5593, 6125, 6669, 7217, 7761, 8291, 8801, 9280, 9722, 10119, 10462, - 10746, 10964, 11111, 11183, 11178, 11092, 10925, 10677, 10350, 9946, 9469, 8924, 8316, 7652, 6941, 6190, 5408, 4605, 3790, 2973, 2164, 1373, 609, -117, -801, -1432, -2004, -2512, -2950, -3314, -3603, -3815, - -3949, -4007, -3991, -3904, -3751, -3537, -3270, -2954, -2600, -2215, -1807, -1386, -961, -541, -134, 250, 605, 922, 1196, 1421, 1591, 1702, 1753, 1742, 1667, 1530, 1331, 1075, 763, 402, -4, -449, - -926, -1429, -1949, -2480, -3012, -3540, -4054, -4549, -5018, -5454, -5853, -6209, -6520, -6781, -6992, -7150, -7257, -7313, -7320, -7280, -7197, -7076, -6921, -6737, -6530, -6306, -6072, -5834, -5599, -5371, -5157, -4963, - -4793, -4652, -4542, -4468, -4430, -4429, -4468, -4544, -4656, -4802, -4980, -5186, -5415, -5664, -5926, -6197, -6470, -6742, -7005, -7254, -7485, -7693, -7874, -8024, -8140, -8221, -8265, -8272, -8243, -8178, -8081, -7955, - -7803, -7631, -7444, -7249, -7051, -6857, -6675, -6513, -6377, -6274, -6211, -6195, -6231, -6325, -6481, -6701, -6990, -7347, -7774, -8269, -8831, -9456, -10141, -10879, -11665, -12491, -13350, -14232, -15127, -16028, -16922, -17799, - -18650, -19464, -20231, -20942, -21587, -22159, -22650, -23055, -23367, -23582, -23699, -23714, -23629, -23443, -23159, -22780, -22312, -21759, -21128, -20427, -19665, -18850, -17992, -17100, -16185, -15258, -14327, -13403, -12494, -11611, -10761, -9950, - -9187, -8476, -7822, -7228, -6696, -6228, -5824, -5482, -5202, -4979, -4810, -4690, -4615, -4577, -4571, -4591, -4628, -4677, -4731, -4783, -4826, -4856, -4867, -4855, -4815, -4745, -4643, -4507, -4338, -4135, -3900, -3635, - -3344, -3030, -2697, -2350, -1994, -1635, -1278, -930, -596, -281, 7, 268, 494, 683, 831, 936, 996, 1012, 983, 910, 795, 641, 451, 230, -17, -285, -570, -863, -1159, -1452, -1734, -2000, - -2244, -2460, -2643, -2789, -2894, -2954, -2969, -2936, -2856, -2728, -2556, -2341, -2088, -1799, -1482, -1140, -782, -413, -41, 325, 680, 1015, 1323, 1597, 1830, 2016, 2150, 2227, 2244, 2198, 2086, 1909, - 1668, 1363, 998, 576, 102, -416, -975, -1566, -2181, -2810, -3446, -4079, -4699, -5297, -5864, -6391, -6871, -7295, -7657, -7950, -8170, -8312, -8375, -8356, -8255, -8073, -7812, -7475, -7067, -6593, -6060, -5474, - -4844, -4179, -3488, -2781, -2067, -1356, -657, 17, 662, 1266, 1823, 2325, 2766, 3141, 3445, 3677, 3833, 3914, 3919, 3851, 3711, 3505, 3236, 2909, 2533, 2112, 1655, 1169, 663, 145, -376, -894, - -1401, -1889, -2351, -2781, -3175, -3527, -3833, -4092, -4300, -4458, -4564, -4621, -4630, -4592, -4512, -4394, -4242, -4060, -3854, -3629, -3390, -3143, -2894, -2646, -2405, -2174, -1957, -1757, -1576, -1415, -1275, -1155, - -1056, -974, -908, -854, -808, -767, -726, -679, -622, -549, -454, -333, -181, 6, 234, 506, 824, 1189, 1605, 2069, 2582, 3143, 3748, 4394, 5077, 5791, 6531, 7289, 8058, 8830, 9597, 10351, - 11082, 11783, 12444, 13057, 13616, 14112, 14540, 14893, 15168, 15359, 15466, 15486, 15419, 15266, 15029, 14711, 14318, 13853, 13324, 12737, 12102, 11426, 10720, 9992, 9252, 8511, 7779, 7064, 6377, 5726, 5120, 4564, - 4067, 3633, 3267, 2971, 2749, 2600, 2525, 2521, 2586, 2717, 2908, 3154, 3448, 3782, 4149, 4541, 4948, 5363, 5775, 6177, 6561, 6918, 7241, 7523, 7759, 7944, 8074, 8145, 8156, 8107, 7997, 7828, - 7603, 7324, 6997, 6626, 6218, 5779, 5316, 4836, 4348, 3860, 3378, 2912, 2467, 2051, 1671, 1332, 1039, 797, 608, 475, 399, 381, 421, 516, 665, 865, 1111, 1398, 1723, 2078, 2458, 2856, - 3267, 3683, 4099, 4508, 4905, 5285, 5642, 5973, 6275, 6545, 6781, 6983, 7151, 7284, 7386, 7458, 7503, 7525, 7528, 7517, 7497, 7472, 7449, 7433, 7428, 7440, 7473, 7531, 7617, 7735, 7886, 8071, - 8292, 8546, 8834, 9152, 9497, 9866, 10253, 10653, 11059, 11466, 11866, 12251, 12614, 12947, 13244, 13496, 13698, 13842, 13924, 13937, 13879, 13746, 13537, 13249, 12884, 12443, 11928, 11343, 10693, 9984, 9221, 8414, - 7570, 6699, 5810, 4913, 4018, 3137, 2278, 1453, 671, -58, -728, -1329, -1855, -2300, -2660, -2931, -3110, -3197, -3192, -3096, -2913, -2646, -2300, -1881, -1396, -854, -263, 368, 1030, 1714, 2408, 3104, - 3792, 4463, 5107, 5717, 6286, 6807, 7274, 7683, 8030, 8313, 8531, 8684, 8772, 8798, 8764, 8674, 8533, 8347, 8121, 7862, 7577, 7273, 6958, 6637, 6320, 6011, 5717, 5445, 5199, 4983, 4802, 4657, - 4551, 4485, 4459, 4472, 4522, 4607, 4724, 4869, 5038, 5225, 5425, 5633, 5844, 6051, 6250, 6434, 6600, 6742, 6857, 6941, 6992, 7008, 6989, 6933, 6842, 6717, 6561, 6377, 6168, 5940, 5697, 5444, - 5189, 4936, 4692, 4464, 4257, 4077, 3931, 3823, 3757, 3737, 3767, 3848, 3981, 4168, 4407, 4697, 5035, 5417, 5840, 6298, 6785, 7294, 7819, 8352, 8885, 9411, 9920, 10406, 10860, 11276, 11646, 11966, - 12228, 12430, 12568, 12638, 12640, 12574, 12439, 12239, 11976, 11655, 11279, 10856, 10392, 9895, 9373, 8834, 8287, 7741, 7205, 6689, 6200, 5748, 5339, 4980, 4678, 4438, 4264, 4159, 4125, 4163, 4271, 4449, - 4693, 5000, 5363, 5778, 6237, 6732, 7255, 7796, 8346, 8896, 9435, 9955, 10444, 10895, 11299, 11646, 11932, 12148, 12289, 12352, 12333, 12229, 12040, 11766, 11409, 10971, 10455, 9867, 9211, 8494, 7723, 6905, - 6050, 5164, 4256, 3336, 2411, 1490, 580, -310, -1175, -2009, -2806, -3562, -4272, -4935, -5549, -6113, -6626, -7091, -7509, -7882, -8213, -8507, -8767, -8999, -9207, -9396, -9571, -9737, -9898, -10060, -10225, -10397, - -10579, -10773, -10980, -11201, -11435, -11682, -11939, -12205, -12476, -12749, -13018, -13280, -13529, -13760, -13967, -14144, -14287, -14390, -14448, -14457, -14413, -14313, -14154, -13935, -13656, -13315, -12916, -12459, -11949, -11388, -10782, -10138, - -9461, -8758, -8038, -7309, -6579, -5857, -5151, -4471, -3825, -3221, -2667, -2168, -1733, -1365, -1069, -849, -706, -641, -655, -746, -912, -1149, -1451, -1815, -2231, -2695, -3196, -3727, -4278, -4840, -5402, -5957, - -6493, -7002, -7476, -7906, -8285, -8606, -8865, -9056, -9177, -9224, -9198, -9099, -8927, -8686, -8379, -8011, -7587, -7115, -6601, -6054, -5482, -4894, -4298, -3704, -3122, -2558, -2023, -1523, -1066, -658, -304, -9, - 222, 389, 490, 524, 493, 398, 243, 31, -232, -543, -893, -1276, -1684, -2111, -2548, -2987, -3420, -3841, -4241, -4615, -4955, -5257, -5516, -5728, -5890, -6001, -6059, -6065, -6018, -5922, -5779, -5592, - -5365, -5103, -4812, -4497, -4165, -3820, -3470, -3121, -2778, -2448, -2134, -1843, -1577, -1341, -1136, -965, -829, -727, -659, -623, -617, -639, -683, -747, -825, -913, -1004, -1094, -1177, -1247, -1300, -1330, - -1333, -1305, -1243, -1144, -1008, -832, -617, -364, -74, 248, 600, 978, 1377, 1789, 2210, 2632, 3048, 3450, 3830, 4183, 4499, 4772, 4996, 5164, 5272, 5314, 5288, 5191, 5021, 4778, 4463, 4077, - 3624, 3109, 2535, 1910, 1240, 534, -199, -952, -1714, -2474, -3224, -3952, -4649, -5305, -5911, -6459, -6940, -7348, -7677, -7922, -8080, -8147, -8123, -8008, -7802, -7509, -7133, -6678, -6150, -5556, -4905, -4204, - -3462, -2690, -1897, -1093, -289, 505, 1281, 2028, 2740, 3406, 4021, 4578, 5072, 5499, 5856, 6141, 6353, 6493, 6562, 6562, 6496, 6370, 6188, 5956, 5680, 5367, 5024, 4657, 4275, 3884, 3491, 3103, - 2725, 2363, 2021, 1704, 1415, 1156, 928, 733, 569, 436, 332, 253, 197, 160, 136, 122, 111, 99, 81, 50, 2, -67, -162, -289, -448, -643, -876, -1147, -1457, -1805, -2190, -2608, - -3057, -3533, -4031, -4546, -5072, -5603, -6133, -6655, -7162, -7648, -8106, -8530, -8915, -9256, -9547, -9786, -9970, -10096, -10163, -10172, -10123, -10018, -9861, -9653, -9401, -9110, -8785, -8434, -8062, -7679, -7291, -6906, - -6532, -6176, -5846, -5548, -5288, -5073, -4906, -4791, -4731, -4728, -4781, -4892, -5058, -5277, -5545, -5856, -6207, -6589, -6996, -7421, -7854, -8287, -8711, -9117, -9497, -9841, -10141, -10390, -10581, -10707, -10764, -10746, - -10650, -10475, -10219, -9883, -9468, -8977, -8414, -7783, -7092, -6347, -5556, -4727, -3870, -2995, -2111, -1228, -357, 490, 1308, 2084, 2811, 3481, 4086, 4620, 5078, 5456, 5750, 5960, 6084, 6122, 6078, 5952, - 5750, 5475, 5134, 4732, 4277, 3776, 3238, 2669, 2079, 1475, 865, 258, -340, -922, -1482, -2015, -2517, -2983, -3411, -3798, -4145, -4451, -4717, -4945, -5136, -5295, -5425, -5530, -5615, -5684, -5744, -5798, - -5851, -5910, -5977, -6058, -6155, -6272, -6411, -6574, -6761, -6972, -7207, -7465, -7741, -8035, -8342, -8658, -8977, -9296, -9608, -9907, -10189, -10446, -10674, -10868, -11022, -11132, -11194, -11206, -11165, -11070, -10920, -10716, - -10459, -10152, -9798, -9402, -8968, -8502, -8012, -7503, -6984, -6464, -5949, -5449, -4972, -4526, -4119, -3759, -3451, -3203, -3019, -2904, -2861, -2892, -2999, -3181, -3437, -3765, -4162, -4622, -5140, -5709, -6322, -6971, - -7647, -8340, -9042, -9741, -10429, -11095, -11730, -12325, -12872, -13362, -13789, -14147, -14430, -14636, -14761, -14803, -14762, -14639, -14437, -14158, -13807, -13389, -12910, -12378, -11800, -11184, -10540, -9876, -9201, -8525, -7857, -7204, - -6576, -5979, -5421, -4907, -4443, -4033, -3680, -3385, -3150, -2975, -2858, -2797, -2788, -2828, -2911, -3031, -3183, -3359, -3551, -3754, -3958, -4157, -4343, -4509, -4648, -4755, -4825, -4852, -4833, -4764, -4645, -4473, - -4250, -3974, -3650, -3278, -2864, -2409, -1921, -1404, -863, -306, 261, 832, 1402, 1962, 2508, 3034, 3534, 4005, 4442, 4842, 5203, 5524, 5804, 6043, 6242, 6403, 6528, 6622, 6688, 6730, 6753, 6762, - 6764, 6763, 6764, 6774, 6796, 6837, 6899, 6986, 7102, 7248, 7425, 7634, 7875, 8145, 8443, 8766, 9110, 9469, 9839, 10213, 10584, 10947, 11293, 11616, 11908, 12162, 12370, 12528, 12627, 12664, 12634, 12533, - 12358, 12108, 11783, 11382, 10908, 10363, 9752, 9080, 8352, 7577, 6761, 5913, 5043, 4161, 3277, 2400, 1543, 714, -76, -817, -1501, -2119, -2664, -3129, -3508, -3798, -3994, -4096, -4102, -4012, -3830, -3558, - -3199, -2761, -2248, -1669, -1031, -344, 383, 1141, 1920, 2710, 3500, 4281, 5044, 5780, 6480, 7138, 7746, 8300, 8794, 9225, 9591, 9890, 10122, 10289, 10391, 10433, 10417, 10348, 10232, 10075, 9882, 9662, - 9420, 9163, 8899, 8635, 8376, 8128, 7898, 7689, 7506, 7352, 7230, 7140, 7085, 7062, 7072, 7113, 7181, 7273, 7385, 7513, 7652, 7795, 7939, 8076, 8201, 8310, 8396, 8454, 8482, 8474, 8428, 8341, - 8212, 8040, 7826, 7569, 7273, 6940, 6573, 6176, 5754, 5313, 4858, 4396, 3932, 3475, 3029, 2601, 2198, 1825, 1487, 1190, 936, 729, 572, 465, 410, 405, 450, 541, 676, 851, 1059, 1296, - 1556, 1831, 2115, 2399, 2676, 2939, 3180, 3392, 3569, 3703, 3790, 3825, 3804, 3724, 3583, 3381, 3117, 2793, 2411, 1976, 1491, 963, 398, -197, -814, -1446, -2081, -2713, -3330, -3925, -4487, -5009, - -5481, -5896, -6248, -6529, -6736, -6863, -6909, -6871, -6749, -6543, -6256, -5890, -5450, -4941, -4369, -3742, -3068, -2356, -1614, -853, -81, 689, 1450, 2191, 2904, 3579, 4208, 4785, 5302, 5754, 6137, 6447, - 6683, 6842, 6926, 6936, 6874, 6743, 6549, 6295, 5989, 5636, 5245, 4823, 4378, 3918, 3450, 2984, 2527, 2085, 1667, 1277, 922, 605, 332, 104, -76, -208, -290, -325, -314, -258, -162, -29, - 136, 329, 546, 779, 1025, 1276, 1529, 1778, 2019, 2247, 2459, 2651, 2822, 2970, 3094, 3195, 3272, 3328, 3364, 3385, 3393, 3392, 3388, 3385, 3389, 3404, 3436, 3491, 3573, 3687, 3837, 4026, - 4258, 4535, 4858, 5228, 5644, 6104, 6607, 7149, 7725, 8331, 8961, 9608, 10266, 10925, 11579, 12218, 12834, 13418, 13963, 14459, 14900, 15278, 15586, 15819, 15971, 16040, 16023, 15917, 15723, 15441, 15073, 14622, - 14092, 13490, 12820, 12091, 11309, 10485, 9627, 8745, 7849, 6949, 6055, 5177, 4324, 3504, 2728, 2001, 1332, 724, 184, -285, -682, -1005, -1254, -1430, -1535, -1574, -1551, -1471, -1340, -1167, -957, -721, - -466, -200, 66, 325, 569, 790, 979, 1132, 1240, 1301, 1308, 1260, 1155, 991, 768, 488, 154, -231, -664, -1140, -1651, -2193, -2757, -3336, -3922, -4508, -5086, -5648, -6186, -6694, -7165, -7593, - -7973, -8300, -8571, -8784, -8937, -9029, -9060, -9032, -8947, -8808, -8619, -8384, -8108, -7797, -7458, -7095, -6717, -6328, -5936, -5546, -5165, -4798, -4449, -4122, -3822, -3550, -3309, -3100, -2923, -2777, -2662, -2574, - -2511, -2469, -2445, -2434, -2431, -2431, -2428, -2418, -2395, -2355, -2293, -2204, -2086, -1936, -1751, -1530, -1272, -979, -652, -292, 96, 511, 945, 1395, 1854, 2314, 2770, 3214, 3637, 4033, 4394, 4713, - 4982, 5196, 5347, 5432, 5446, 5386, 5249, 5034, 4741, 4371, 3926, 3410, 2827, 2183, 1484, 737, -47, -863, -1700, -2548, -3396, -4235, -5053, -5842, -6591, -7291, -7934, -8511, -9016, -9443, -9787, -10044, - -10212, -10289, -10276, -10174, -9985, -9714, -9365, -8943, -8457, -7914, -7321, -6689, -6026, -5342, -4647, -3951, -3264, -2594, -1951, -1343, -777, -260, 201, 604, 943, 1217, 1424, 1562, 1634, 1641, 1585, 1471, - 1303, 1086, 827, 531, 206, -141, -503, -873, -1244, -1609, -1961, -2295, -2605, -2886, -3136, -3351, -3528, -3667, -3767, -3829, -3855, -3845, -3805, -3736, -3643, -3532, -3406, -3273, -3136, -3001, -2875, -2762, - -2667, -2594, -2548, -2532, -2548, -2599, -2684, -2805, -2961, -3151, -3371, -3620, -3892, -4184, -4491, -4807, -5126, -5442, -5748, -6039, -6307, -6547, -6754, -6921, -7045, -7120, -7145, -7116, -7032, -6893, -6698, -6450, - -6150, -5801, -5408, -4977, -4511, -4018, -3505, -2980, -2449, -1921, -1403, -904, -431, 8, 407, 761, 1063, 1308, 1494, 1618, 1678, 1673, 1605, 1475, 1286, 1041, 746, 407, 30, -376, -805, -1249, - -1697, -2141, -2572, -2981, -3359, -3699, -3991, -4229, -4407, -4520, -4562, -4532, -4426, -4244, -3987, -3655, -3251, -2780, -2245, -1654, -1014, -330, 385, 1127, 1883, 2645, 3403, 4147, 4867, 5554, 6198, 6793, - 7330, 7803, 8205, 8534, 8784, 8953, 9040, 9046, 8971, 8817, 8587, 8287, 7921, 7494, 7015, 6490, 5928, 5335, 4722, 4095, 3464, 2837, 2221, 1624, 1053, 513, 10, -451, -867, -1236, -1555, -1824, - -2043, -2212, -2334, -2411, -2446, -2443, -2407, -2342, -2252, -2144, -2022, -1891, -1757, -1623, -1493, -1372, -1263, -1167, -1086, -1021, -972, -939, -920, -912, -914, -921, -929, -933, -930, -913, -877, -817, - -727, -602, -438, -229, 26, 333, 693, 1109, 1581, 2108, 2689, 3323, 4007, 4736, 5505, 6310, 7142, 7996, 8863, 9735, 10603, 11459, 12295, 13100, 13867, 14587, 15253, 15858, 16395, 16858, 17244, 17549, - 17770, 17905, 17956, 17923, 17808, 17614, 17347, 17011, 16614, 16162, 15663, 15127, 14562, 13978, 13384, 12791, 12206, 11641, 11103, 10600, 10141, 9732, 9378, 9084, 8854, 8691, 8594, 8566, 8604, 8706, 8869, 9088, - 9358, 9672, 10023, 10405, 10807, 11222, 11640, 12054, 12453, 12829, 13175, 13481, 13741, 13949, 14099, 14187, 14208, 14160, 14043, 13855, 13597, 13272, 12882, 12431, 11924, 11366, 10764, 10125, 9456, 8765, 8060, 7348, - 6639, 5939, 5256, 4596, 3967, 3374, 2822, 2315, 1855, 1447, 1090, 784, 531, 327, 171, 60, -10, -45, -49, -28, 12, 67, 129, 193, 253, 303, 338, 353, 345, 310, 246, 151, - 23, -136, -328, -550, -800, -1074, -1370, -1683, -2009, -2341, -2675, -3006, -3326, -3632, -3918, -4178, -4408, -4604, -4762, -4881, -4957, -4990, -4979, -4926, -4831, -4698, -4530, -4331, -4105, -3860, -3600, -3333, - -3066, -2807, -2562, -2339, -2146, -1989, -1876, -1812, -1802, -1852, -1965, -2143, -2389, -2702, -3083, -3530, -4039, -4608, -5230, -5900, -6611, -7355, -8124, -8908, -9699, -10485, -11257, -12006, -12720, -13390, -14008, -14564, - -15051, -15461, -15789, -16029, -16178, -16233, -16192, -16056, -15826, -15503, -15092, -14597, -14025, -13382, -12675, -11914, -11107, -10264, -9395, -8510, -7620, -6734, -5862, -5013, -4197, -3421, -2693, -2020, -1406, -856, -373, 38, - 380, 652, 853, 987, 1057, 1066, 1020, 923, 783, 605, 396, 164, -84, -342, -603, -860, -1108, -1340, -1553, -1741, -1903, -2035, -2136, -2205, -2243, -2250, -2229, -2182, -2112, -2024, -1922, -1811, - -1696, -1582, -1476, -1381, -1305, -1250, -1223, -1226, -1264, -1339, -1454, -1608, -1803, -2038, -2312, -2622, -2966, -3340, -3739, -4158, -4592, -5034, -5479, -5919, -6348, -6760, -7148, -7505, -7827, -8108, -8343, -8528, - -8661, -8740, -8763, -8729, -8641, -8498, -8304, -8063, -7778, -7456, -7101, -6721, -6322, -5911, -5497, -5087, -4689, -4311, -3958, -3639, -3360, -3125, -2941, -2810, -2736, -2720, -2763, -2866, -3026, -3241, -3508, -3822, - -4177, -4568, -4986, -5425, -5875, -6329, -6777, -7210, -7620, -7997, -8333, -8621, -8852, -9021, -9122, -9150, -9102, -8976, -8769, -8483, -8118, -7677, -7163, -6581, -5937, -5238, -4491, -3705, -2889, -2053, -1205, -357, - 480, 1299, 2089, 2840, 3544, 4193, 4779, 5296, 5739, 6103, 6386, 6584, 6698, 6728, 6675, 6542, 6332, 6050, 5702, 5293, 4831, 4323, 3778, 3202, 2604, 1993, 1377, 762, 157, -430, -996, -1534, - -2039, -2508, -2937, -3324, -3669, -3971, -4231, -4450, -4631, -4776, -4890, -4976, -5039, -5084, -5116, -5140, -5161, -5183, -5212, -5252, -5305, -5376, -5467, -5580, -5715, -5873, -6053, -6254, -6474, -6710, -6958, -7214, - -7474, -7732, -7984, -8223, -8444, -8641, -8808, -8941, -9035, -9084, -9085, -9036, -8933, -8776, -8563, -8296, -7975, -7602, -7182, -6718, -6215, -5679, -5117, -4535, -3942, -3345, -2753, -2174, -1616, -1089, -600, -156, - 234, 567, 835, 1034, 1160, 1212, 1187, 1086, 908, 657, 335, -52, -500, -1003, -1554, -2143, -2763, -3405, -4058, -4714, -5362, -5992, -6596, -7164, -7688, -8159, -8571, -8918, -9193, -9395, -9518, -9562, - -9527, -9412, -9220, -8954, -8617, -8216, -7756, -7244, -6688, -6097, -5478, -4841, -4195, -3549, -2913, -2294, -1701, -1143, -625, -154, 264, 626, 928, 1168, 1345, 1458, 1510, 1501, 1436, 1318, 1151, 943, - 699, 425, 130, -178, -495, -811, -1119, -1411, -1682, -1924, -2133, -2304, -2432, -2514, -2549, -2534, -2470, -2358, -2198, -1993, -1747, -1463, -1147, -802, -436, -54, 337, 732, 1123, 1506, 1873, 2219, - 2540, 2829, 3085, 3303, 3481, 3617, 3712, 3764, 3776, 3748, 3684, 3587, 3460, 3309, 3137, 2951, 2755, 2556, 2359, 2169, 1992, 1831, 1693, 1580, 1496, 1442, 1421, 1433, 1477, 1554, 1660, 1794, - 1950, 2125, 2314, 2510, 2707, 2899, 3079, 3239, 3372, 3471, 3529, 3540, 3497, 3396, 3232, 3000, 2699, 2326, 1881, 1365, 778, 124, -592, -1368, -2197, -3071, -3983, -4924, -5884, -6855, -7825, -8785, - -9724, -10632, -11498, -12313, -13067, -13752, -14360, -14885, -15320, -15662, -15906, -16050, -16093, -16037, -15881, -15630, -15287, -14857, -14347, -13764, -13115, -12410, -11658, -10868, -10051, -9215, -8373, -7532, -6703, -5894, -5114, -4371, - -3671, -3020, -2423, -1883, -1404, -987, -632, -339, -105, 70, 192, 264, 293, 283, 240, 172, 85, -13, -117, -219, -314, -395, -456, -494, -503, -481, -424, -333, -204, -40, 158, 391, - 654, 944, 1257, 1589, 1934, 2287, 2643, 2994, 3337, 3665, 3972, 4254, 4506, 4725, 4906, 5047, 5147, 5205, 5219, 5191, 5123, 5017, 4876, 4704, 4505, 4285, 4049, 3803, 3554, 3307, 3070, 2849, - 2648, 2476, 2335, 2232, 2170, 2152, 2180, 2257, 2382, 2556, 2776, 3040, 3346, 3689, 4064, 4466, 4888, 5323, 5764, 6204, 6635, 7049, 7439, 7797, 8116, 8391, 8615, 8783, 8892, 8938, 8918, 8833, - 8681, 8465, 8185, 7846, 7452, 7009, 6522, 5998, 5446, 4874, 4290, 3704, 3124, 2561, 2024, 1520, 1059, 648, 295, 6, -214, -361, -432, -424, -337, -171, 70, 387, 775, 1228, 1739, 2303, - 2911, 3554, 4224, 4911, 5606, 6297, 6977, 7634, 8260, 8846, 9384, 9866, 10286, 10638, 10918, 11122, 11249, 11296, 11264, 11154, 10968, 10710, 10384, 9995, 9550, 9054, 8515, 7942, 7341, 6722, 6092, 5459, - 4832, 4218, 3623, 3055, 2518, 2019, 1560, 1146, 778, 457, 185, -38, -216, -350, -442, -497, -518, -510, -478, -428, -363, -291, -215, -141, -74, -17, 24, 49, 55, 40, 3, -56, - -136, -236, -353, -485, -627, -776, -927, -1075, -1214, -1340, -1447, -1529, -1581, -1599, -1578, -1514, -1404, -1247, -1039, -781, -473, -115, 288, 737, 1225, 1749, 2302, 2878, 3471, 4071, 4672, 5265, - 5842, 6393, 6911, 7388, 7815, 8185, 8492, 8730, 8893, 8979, 8983, 8905, 8743, 8498, 8171, 7766, 7287, 6738, 6127, 5458, 4742, 3986, 3199, 2391, 1572, 752, -58, -851, -1616, -2343, -3025, -3653, - -4222, -4725, -5157, -5514, -5795, -5997, -6121, -6168, -6140, -6040, -5873, -5645, -5360, -5028, -4655, -4249, -3820, -3375, -2924, -2475, -2037, -1618, -1226, -867, -548, -274, -49, 122, 239, 299, 303, 252, - 147, -8, -210, -454, -734, -1044, -1377, -1725, -2082, -2438, -2788, -3122, -3434, -3716, -3963, -4167, -4325, -4431, -4481, -4474, -4408, -4281, -4095, -3849, -3547, -3192, -2786, -2335, -1844, -1318, -763, -186, - 406, 1008, 1613, 2215, 2807, 3384, 3942, 4475, 4980, 5455, 5896, 6303, 6675, 7013, 7318, 7591, 7835, 8054, 8252, 8432, 8600, 8761, 8919, 9080, 9250, 9432, 9632, 9854, 10100, 10375, 10680, 11017, - 11387, 11788, 12221, 12682, 13169, 13678, 14204, 14743, 15287, 15831, 16367, 16889, 17388, 17856, 18287, 18673, 19007, 19282, 19493, 19634, 19700, 19689, 19597, 19424, 19168, 18832, 18416, 17925, 17361, 16732, 16042, 15300, - 14513, 13691, 12843, 11978, 11107, 10240, 9388, 8560, 7767, 7018, 6322, 5686, 5119, 4625, 4211, 3881, 3636, 3479, 3410, 3428, 3531, 3714, 3975, 4306, 4702, 5154, 5655, 6196, 6768, 7360, 7964, 8569, - 9166, 9745, 10298, 10817, 11293, 11720, 12093, 12406, 12655, 12838, 12954, 13001, 12980, 12893, 12742, 12532, 12266, 11950, 11590, 11192, 10763, 10311, 9842, 9364, 8884, 8409, 7946, 7500, 7077, 6682, 6319, 5991, - 5701, 5449, 5236, 5063, 4927, 4828, 4761, 4724, 4712, 4721, 4746, 4782, 4822, 4862, 4897, 4920, 4927, 4913, 4875, 4809, 4712, 4582, 4418, 4219, 3987, 3722, 3427, 3105, 2758, 2392, 2012, 1622 -}; -const int multitone_length = 32000; \ No newline at end of file From 368e5cd4b84fcfc09be42590b60dcac1b1dafd42 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:32:30 +0200 Subject: [PATCH 28/77] refactor(audio-response): replace FFT with analysis bin calculations and improve response handling --- .../gatt_services/audio_response_service.c | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index bec3e7a5..7fac1e5e 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -1,6 +1,7 @@ #include "audio_response_service.h" #include +#include #include #include #include @@ -9,7 +10,6 @@ #include #include -#include "arm_math.h" #include "audio_datapath.h" #include "hw_codec.h" @@ -23,6 +23,7 @@ LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); #define AUDIO_RESPONSE_TRANSFER_TIMEOUT K_SECONDS(30) #define AUDIO_RESPONSE_STATUS_ENCODED_SIZE 9 #define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (CONFIG_AUDIO_RESPONSE_MAX_POINTS * 4)) +#define AUDIO_RESPONSE_TWO_PI 6.28318530717958647692f BUILD_ASSERT(CONFIG_AUDIO_RESPONSE_MAX_POINTS >= AUDIO_RESPONSE_DEFAULT_POINTS, "Default audio response points must fit CONFIG_AUDIO_RESPONSE_MAX_POINTS"); @@ -63,8 +64,6 @@ static const uint16_t default_frequencies[AUDIO_RESPONSE_DEFAULT_POINTS] = { static int16_t captured_samples[AUDIO_RESPONSE_CAPTURE_SAMPLES]; static int16_t uploaded_samples[CONFIG_AUDIO_RESPONSE_MAX_SAMPLES]; -static q15_t fft_output[AUDIO_RESPONSE_CAPTURE_SAMPLES * 2]; -static q15_t magnitudes[AUDIO_RESPONSE_CAPTURE_SAMPLES / 2]; static uint16_t requested_frequencies[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; static uint16_t decoded_config_frequencies[UINT8_MAX]; static uint16_t result_frequencies[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; @@ -88,6 +87,8 @@ extern const struct bt_gatt_service_static audio_response_svc; static int notify_transfer_status(enum audio_response_transfer_status status, uint16_t credits); static uint32_t response_frequency_to_bin(uint16_t frequency); static bool is_valid_response_frequency(uint16_t frequency); +static uint16_t response_magnitude_for_bin(const int16_t *samples, size_t sample_count, + uint32_t bin); static int select_response_points(audio_response_config_t *config); static void reset_transfer(void); @@ -136,7 +137,7 @@ static const char *transfer_control_type_name(audio_response_transfer_control_ty } /** - * Return the nearest FFT magnitude bin for a requested response frequency. + * Return the nearest analysis bin for a requested response frequency. */ static uint32_t response_frequency_to_bin(uint16_t frequency) { @@ -146,11 +147,51 @@ static uint32_t response_frequency_to_bin(uint16_t frequency) } /** - * Validate that a requested response frequency maps to a captured FFT bin. + * Validate that a requested response frequency maps to a captured analysis bin. */ static bool is_valid_response_frequency(uint16_t frequency) { - return response_frequency_to_bin(frequency) < ARRAY_SIZE(magnitudes); + return response_frequency_to_bin(frequency) < (AUDIO_RESPONSE_CAPTURE_SAMPLES / 2); +} + +/** + * Estimate a single analysis-bin magnitude using the Goertzel recurrence. + * + * The returned value is normalized into the same unsigned Q15-like range used by + * the protocol result. Non-DC bins are scaled by 2 / N so a full-scale sine near + * the target bin reports close to INT16_MAX. + */ +static uint16_t response_magnitude_for_bin(const int16_t *samples, size_t sample_count, + uint32_t bin) +{ + const float omega = AUDIO_RESPONSE_TWO_PI * (float)bin / (float)sample_count; + const float coefficient = 2.0f * cosf(omega); + float previous = 0.0f; + float previous2 = 0.0f; + + for (size_t index = 0; index < sample_count; ++index) { + const float current = (float)samples[index] + coefficient * previous - previous2; + + previous2 = previous; + previous = current; + } + + float power = previous2 * previous2 + previous * previous - + coefficient * previous * previous2; + + if (power < 0.0f) { + power = 0.0f; + } + + const float scale = (bin == 0U) ? (1.0f / (float)sample_count) : + (2.0f / (float)sample_count); + const float magnitude = sqrtf(power) * scale; + + if (magnitude >= (float)INT16_MAX) { + return INT16_MAX; + } + + return (uint16_t)(magnitude + 0.5f); } /** @@ -158,7 +199,7 @@ static bool is_valid_response_frequency(uint16_t frequency) * * A zero-point request selects the firmware default table. Nonzero requests * use the protocol-provided frequency list after validating that every - * frequency maps to the captured FFT range. + * frequency maps to the captured response range. */ static int select_response_points(audio_response_config_t *config) { @@ -655,7 +696,7 @@ static void measurement_work_handler(struct k_work *work) } /** - * Encode and notify the FFT response at the configured target frequencies. + * Encode and notify the measured response at the configured target frequencies. */ static void notify_result(void) { @@ -689,40 +730,26 @@ static void notify_result(void) } /** - * Compute the captured signal's FFT response and notify the requesting client. + * Compute the captured signal's response at the configured target frequencies. */ static void measurement_complete_work_handler(struct k_work *work) { - static arm_rfft_instance_q15 rfft; - static bool rfft_initialized; - LOG_INF("Audio response capture complete: id=%u", pending_config.id); audio_datapath_buffer_stop(); audio_datapath_release(); - if (!rfft_initialized) { - LOG_DBG("Initializing audio response FFT: samples=%u", AUDIO_RESPONSE_CAPTURE_SAMPLES); - if (arm_rfft_init_q15(&rfft, AUDIO_RESPONSE_CAPTURE_SAMPLES, 0, 1) != ARM_MATH_SUCCESS) { - LOG_ERR("Failed to initialize audio response FFT"); - goto complete; - } - rfft_initialized = true; - } - - arm_rfft_q15(&rfft, captured_samples, fft_output); - arm_cmplx_mag_q15(fft_output, magnitudes, AUDIO_RESPONSE_CAPTURE_SAMPLES / 2); - for (size_t index = 0; index < pending_config.points; ++index) { uint32_t bin = response_frequency_to_bin(pending_config.frequencies[index]); result_frequencies[index] = pending_config.frequencies[index]; - result_response[index] = magnitudes[bin]; + result_response[index] = response_magnitude_for_bin( + captured_samples, ARRAY_SIZE(captured_samples), bin); } - LOG_DBG("Audio response FFT complete: id=%u bins=%u result_points=%u", pending_config.id, + LOG_DBG("Audio response analysis complete: id=%u bins=%u result_points=%u", + pending_config.id, AUDIO_RESPONSE_CAPTURE_SAMPLES / 2, pending_config.points); notify_result(); -complete: k_mutex_lock(&service_mutex, K_FOREVER); measurement_active = false; k_mutex_unlock(&service_mutex); From cba30e9217e3e3bd5c96f40c7ba4dc205f893719 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:48:38 +0200 Subject: [PATCH 29/77] fix(audio-response): eliminate dynamic decimator allocation --- src/audio/audio_datapath_decimator.cpp | 77 +++++------- src/audio/decimation_filter.cpp | 164 ++++++++++++------------- src/audio/decimation_filter.h | 56 ++++++--- 3 files changed, 153 insertions(+), 144 deletions(-) diff --git a/src/audio/audio_datapath_decimator.cpp b/src/audio/audio_datapath_decimator.cpp index 73c9e43d..5650f406 100644 --- a/src/audio/audio_datapath_decimator.cpp +++ b/src/audio/audio_datapath_decimator.cpp @@ -10,24 +10,24 @@ LOG_MODULE_DECLARE(audio_datapath); #ifdef __cplusplus -/* CascadedDecimator instance for direct C++ usage */ -static CascadedDecimator* g_audio_decimator = nullptr; +/* Fixed-capacity storage avoids C++ allocation failure in the audio work queue. */ +static CascadedDecimator g_audio_decimator; +static bool g_audio_decimator_initialized; /* Mutex to protect access to the decimator during cleanup */ K_MUTEX_DEFINE(decimator_mutex); -/* Flag to indicate decimator is being used - provides fast-path check */ -static volatile bool g_decimator_in_use = false; - extern "C" { /** * @brief Reset the audio decimator filter state */ void audio_datapath_decimator_reset(void) { - if (g_audio_decimator) { - g_audio_decimator->reset(); + k_mutex_lock(&decimator_mutex, K_FOREVER); + if (g_audio_decimator_initialized) { + g_audio_decimator.reset(); LOG_DBG("CascadedDecimator state reset"); } + k_mutex_unlock(&decimator_mutex); } /** @@ -35,7 +35,10 @@ void audio_datapath_decimator_reset(void) { * @return Current total decimation factor, or 0 if not initialized */ uint8_t audio_datapath_decimator_get_factor(void) { - return g_audio_decimator ? g_audio_decimator->getTotalFactor() : 0; + k_mutex_lock(&decimator_mutex, K_FOREVER); + uint8_t factor = g_audio_decimator_initialized ? g_audio_decimator.getTotalFactor() : 0; + k_mutex_unlock(&decimator_mutex); + return factor; } /** @@ -44,30 +47,27 @@ uint8_t audio_datapath_decimator_get_factor(void) { * @return 0 on success, negative on error */ int audio_datapath_decimator_init(uint8_t factor) { - /* Lock mutex to ensure no concurrent access during init */ k_mutex_lock(&decimator_mutex, K_FOREVER); - - if (g_audio_decimator) { - delete g_audio_decimator; - g_audio_decimator = nullptr; - } - - g_audio_decimator = new CascadedDecimator(factor); - if (!g_audio_decimator) { - LOG_ERR("Failed to create CascadedDecimator with factor %d", factor); + + g_audio_decimator_initialized = false; + g_audio_decimator.cleanup(); + + int ret = g_audio_decimator.configure(factor); + if (ret != 0) { + LOG_ERR("Failed to configure CascadedDecimator: %d", ret); k_mutex_unlock(&decimator_mutex); - return -ENOMEM; + return ret; } - - int ret = g_audio_decimator->init(); + + ret = g_audio_decimator.init(); if (ret != 0) { LOG_ERR("Failed to initialize CascadedDecimator: %d", ret); - delete g_audio_decimator; - g_audio_decimator = nullptr; + g_audio_decimator.cleanup(); k_mutex_unlock(&decimator_mutex); return ret; } - + + g_audio_decimator_initialized = true; LOG_DBG("CascadedDecimator (%dx) initialized successfully", factor); k_mutex_unlock(&decimator_mutex); return 0; @@ -88,16 +88,14 @@ int audio_datapath_decimator_process(const int16_t* input, int16_t* output, uint return 0; } - if (!g_audio_decimator) { + if (!g_audio_decimator_initialized) { k_mutex_unlock(&decimator_mutex); LOG_WRN("CascadedDecimator not available, returning 0 frames"); return 0; } - - g_decimator_in_use = true; - int result = g_audio_decimator->process(input, output, num_frames); - g_decimator_in_use = false; - + + int result = g_audio_decimator.process(input, output, num_frames); + k_mutex_unlock(&decimator_mutex); return result; } @@ -106,23 +104,16 @@ int audio_datapath_decimator_process(const int16_t* input, int16_t* output, uint * @brief Cleanup the audio decimator */ void audio_datapath_decimator_cleanup(void) { - /* Acquire mutex to ensure no processing is happening */ k_mutex_lock(&decimator_mutex, K_FOREVER); - - if (g_audio_decimator) { - /* Wait briefly if decimator was recently in use (safety margin) */ - if (g_decimator_in_use) { - LOG_WRN("Decimator still in use, waiting..."); - k_sleep(K_MSEC(5)); - } - + + if (g_audio_decimator_initialized) { LOG_DBG("Cleaning up CascadedDecimator"); - delete g_audio_decimator; - g_audio_decimator = nullptr; + g_audio_decimator.cleanup(); + g_audio_decimator_initialized = false; } - + k_mutex_unlock(&decimator_mutex); } }; -#endif \ No newline at end of file +#endif diff --git a/src/audio/decimation_filter.cpp b/src/audio/decimation_filter.cpp index 7d49f1a0..e7cc4b2d 100644 --- a/src/audio/decimation_filter.cpp +++ b/src/audio/decimation_filter.cpp @@ -6,7 +6,6 @@ #include "decimation_filter.h" #include -#include #include LOG_MODULE_REGISTER(decimator_cpp, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); @@ -27,10 +26,16 @@ static const float32_t coeff_dec3[2 * 5] = { }; // Decimator class implementation -Decimator::Decimator(uint8_t factor) - : factor_(factor), initialized_(false) { +Decimator::Decimator() + : factor_(0), initialized_(false) { + memset(state_, 0, sizeof(state_)); +} + +void Decimator::configure(uint8_t factor) { + factor_ = factor; + initialized_ = false; + memset(&biquad_, 0, sizeof(biquad_)); memset(state_, 0, sizeof(state_)); - memset(temp_f32_, 0, sizeof(temp_f32_)); } int Decimator::init() { @@ -54,8 +59,10 @@ int Decimator::init() { return 0; } -int Decimator::process(const int16_t* input, int16_t* output, uint32_t num_frames) { - if (!initialized_ || !input || !output || num_frames == 0 || num_frames > MAX_FRAMES) { +int Decimator::process(const int16_t* input, int16_t* output, uint32_t num_frames, + float32_t* processing_buffer) { + if (!initialized_ || !input || !output || !processing_buffer || num_frames == 0 || + num_frames > MAX_FRAMES) { return -EINVAL; } @@ -63,23 +70,23 @@ int Decimator::process(const int16_t* input, int16_t* output, uint32_t num_frame // Convert int16 to float32 for (uint32_t i = 0; i < num_samples; i++) { - temp_f32_[i] = static_cast(input[i]); + processing_buffer[i] = static_cast(input[i]); } - - // Apply anti-aliasing filter using stereo DF2T - static float32_t filtered[MAX_FRAMES * 2]; - arm_biquad_cascade_stereo_df2T_f32(&biquad_, temp_f32_, filtered, num_frames); - + + // CMSIS-DSP stereo DF2T supports identical source and destination buffers. + arm_biquad_cascade_stereo_df2T_f32(&biquad_, processing_buffer, processing_buffer, + num_frames); + // Clip to prevent overflow - arm_clip_f32(filtered, filtered, -32768.0f, 32767.0f, num_samples); + arm_clip_f32(processing_buffer, processing_buffer, -32768.0f, 32767.0f, num_samples); // Decimate and convert back to int16 uint32_t out_frames = num_frames / factor_; uint32_t step = factor_ * 2; for (uint32_t i = 0; i < out_frames; i++) { - output[i * 2] = static_cast(filtered[i * step]); - output[i * 2 + 1] = static_cast(filtered[i * step + 1]); + output[i * 2] = static_cast(processing_buffer[i * step]); + output[i * 2 + 1] = static_cast(processing_buffer[i * step + 1]); } return out_frames; @@ -100,106 +107,94 @@ const float32_t* Decimator::getCoefficients() const { } // CascadedDecimator class implementation -CascadedDecimator::CascadedDecimator(uint8_t total_factor) - : total_factor_(total_factor), num_stages_(0) { - memset(stages_, 0, sizeof(stages_)); - memset(temp_buffers_, 0, sizeof(temp_buffers_)); - setupStages(); +CascadedDecimator::CascadedDecimator() + : total_factor_(0), num_stages_(0), configured_(false) { + memset(intermediate_buffer_, 0, sizeof(intermediate_buffer_)); } -CascadedDecimator::~CascadedDecimator() { - cleanupStages(); -} +int CascadedDecimator::configure(uint8_t total_factor) { + uint8_t stage_factors[MAX_STAGES] = {}; -void CascadedDecimator::setupStages() { - switch (total_factor_) { + cleanup(); + switch (total_factor) { case 1: // No decimation num_stages_ = 0; break; case 2: // 2x num_stages_ = 1; - stages_[0] = new Decimator(2); + stage_factors[0] = 2; break; case 3: // 3x num_stages_ = 1; - stages_[0] = new Decimator(3); + stage_factors[0] = 3; break; case 4: // 2x -> 2x num_stages_ = 2; - stages_[0] = new Decimator(2); - stages_[1] = new Decimator(2); + stage_factors[0] = 2; + stage_factors[1] = 2; break; - case 6: // 3x -> 2x num_stages_ = 2; - stages_[0] = new Decimator(3); - stages_[1] = new Decimator(2); + stage_factors[0] = 3; + stage_factors[1] = 2; break; - case 8: // 2x -> 2x -> 2x num_stages_ = 3; - stages_[0] = new Decimator(2); - stages_[1] = new Decimator(2); - stages_[2] = new Decimator(2); + stage_factors[0] = 2; + stage_factors[1] = 2; + stage_factors[2] = 2; break; - case 12: // 3x -> 2x -> 2x num_stages_ = 3; - stages_[0] = new Decimator(3); - stages_[1] = new Decimator(2); - stages_[2] = new Decimator(2); + stage_factors[0] = 3; + stage_factors[1] = 2; + stage_factors[2] = 2; break; - case 16: // 2x -> 2x -> 2x -> 2x num_stages_ = 4; - stages_[0] = new Decimator(2); - stages_[1] = new Decimator(2); - stages_[2] = new Decimator(2); - stages_[3] = new Decimator(2); + stage_factors[0] = 2; + stage_factors[1] = 2; + stage_factors[2] = 2; + stage_factors[3] = 2; break; - case 24: // 3x -> 2x -> 2x -> 2x num_stages_ = 4; - stages_[0] = new Decimator(3); - stages_[1] = new Decimator(2); - stages_[2] = new Decimator(2); - stages_[3] = new Decimator(2); + stage_factors[0] = 3; + stage_factors[1] = 2; + stage_factors[2] = 2; + stage_factors[3] = 2; break; - default: - LOG_ERR("Unsupported total decimation factor: %d", total_factor_); - num_stages_ = 0; - return; + LOG_ERR("Unsupported total decimation factor: %d", total_factor); + return -EINVAL; } - - // Allocate temporary buffers between stages - for (uint8_t i = 0; i < num_stages_ - 1; i++) { - temp_buffers_[i] = new int16_t[MAX_FRAMES * 2]; + + for (uint8_t i = 0; i < num_stages_; i++) { + stages_[i].configure(stage_factors[i]); } - + + total_factor_ = total_factor; + configured_ = true; LOG_DBG("CascadedDecimator setup for factor %d with %d stages", total_factor_, num_stages_); + return 0; } -void CascadedDecimator::cleanupStages() { - for (uint8_t i = 0; i < num_stages_; i++) { - delete stages_[i]; - stages_[i] = nullptr; - } - - for (uint8_t i = 0; i < MAX_STAGES - 1; i++) { - delete[] temp_buffers_[i]; - temp_buffers_[i] = nullptr; +void CascadedDecimator::cleanup() { + for (uint8_t i = 0; i < MAX_STAGES; i++) { + stages_[i].configure(0); } + total_factor_ = 0; + num_stages_ = 0; + configured_ = false; } int CascadedDecimator::init() { + if (!configured_) { + return -EINVAL; + } + for (uint8_t i = 0; i < num_stages_; i++) { - if (!stages_[i]) { - LOG_ERR("Stage %d is null", i); - return -EINVAL; - } - - int ret = stages_[i]->init(); + int ret = stages_[i].init(); if (ret != 0) { LOG_ERR("Failed to initialize stage %d: %d", i, ret); return ret; @@ -211,14 +206,15 @@ int CascadedDecimator::init() { } int CascadedDecimator::process(const int16_t* input, int16_t* output, uint32_t num_frames) { + if (!configured_ || !input || !output || num_frames == 0 || num_frames > MAX_FRAMES) { + return -EINVAL; + } + if (num_stages_ == 0) { + memcpy(output, input, num_frames * 2 * sizeof(int16_t)); return num_frames; } - if (!input || !output || num_frames == 0) { - return -EINVAL; - } - const int16_t* stage_input = input; int16_t* stage_output = nullptr; int frames = num_frames; @@ -229,12 +225,12 @@ int CascadedDecimator::process(const int16_t* input, int16_t* output, uint32_t n // Last stage outputs to final output buffer stage_output = output; } else { - // Intermediate stage outputs to temp buffer - stage_output = temp_buffers_[i]; + // Alternate storage so adjacent stages never share input and output. + stage_output = (i % 2 == 0) ? intermediate_buffer_ : output; } // Process this stage - frames = stages_[i]->process(stage_input, stage_output, frames); + frames = stages_[i].process(stage_input, stage_output, frames, processing_buffer_); if (frames < 0) { LOG_ERR("Stage %d processing failed: %d", i, frames); return frames; @@ -249,8 +245,6 @@ int CascadedDecimator::process(const int16_t* input, int16_t* output, uint32_t n void CascadedDecimator::reset() { for (uint8_t i = 0; i < num_stages_; i++) { - if (stages_[i]) { - stages_[i]->reset(); - } + stages_[i].reset(); } -} \ No newline at end of file +} diff --git a/src/audio/decimation_filter.h b/src/audio/decimation_filter.h index 2afd709f..2fd57353 100644 --- a/src/audio/decimation_filter.h +++ b/src/audio/decimation_filter.h @@ -10,24 +10,37 @@ #include #include #include "arm_math.h" +#include "audio_i2s.h" #ifdef __cplusplus +static_assert(BLOCK_SIZE_BYTES % (sizeof(int16_t) * 2U) == 0, + "The audio block must contain complete stereo int16 frames"); + /** * @brief Single stage decimation filter class */ class Decimator { public: + /** Maximum number of stereo frames supplied by one audio datapath block. */ + static constexpr uint32_t MAX_FRAMES = + BLOCK_SIZE_BYTES / (sizeof(int16_t) * 2U); + /** - * @brief Constructor - * @param factor Decimation factor (2 or 3) + * @brief Construct an unconfigured decimation stage. */ - Decimator(uint8_t factor); + Decimator(); /** * @brief Destructor */ ~Decimator() = default; + + /** + * @brief Configure this stage for a decimation factor. + * @param factor Decimation factor (2 or 3) + */ + void configure(uint8_t factor); /** * @brief Initialize the decimator @@ -40,9 +53,11 @@ class Decimator { * @param input Input buffer (interleaved stereo int16) * @param output Output buffer (interleaved stereo int16) * @param num_frames Number of input stereo frames + * @param processing_buffer In-place float processing buffer for stereo frames * @return Number of output frames, or negative on error */ - int process(const int16_t* input, int16_t* output, uint32_t num_frames); + int process(const int16_t* input, int16_t* output, uint32_t num_frames, + float32_t* processing_buffer); /** * @brief Reset filter state @@ -56,14 +71,12 @@ class Decimator { uint8_t getFactor() const { return factor_; } private: - static constexpr uint32_t MAX_FRAMES = 512; static constexpr uint32_t NUM_STAGES = 2; uint8_t factor_; bool initialized_; arm_biquad_cascade_stereo_df2T_instance_f32 biquad_; float32_t state_[4 * NUM_STAGES]; - float32_t temp_f32_[MAX_FRAMES * 2]; const float32_t* getCoefficients() const; }; @@ -74,15 +87,26 @@ class Decimator { class CascadedDecimator { public: /** - * @brief Constructor for predefined cascaded decimation factors - * @param total_factor Total decimation factor (4, 6, 8, or 12) + * @brief Construct an unconfigured cascaded decimator. */ - CascadedDecimator(uint8_t total_factor); + CascadedDecimator(); /** * @brief Destructor */ - ~CascadedDecimator(); + ~CascadedDecimator() = default; + + /** + * @brief Configure the statically allocated filter stages. + * @param total_factor Total decimation factor (1, 2, 3, 4, 6, 8, 12, 16, or 24) + * @return 0 on success, or -EINVAL for an unsupported factor. + */ + int configure(uint8_t total_factor); + + /** + * @brief Clear configuration and filter state without releasing memory. + */ + void cleanup(); /** * @brief Initialize all cascaded decimators @@ -112,15 +136,15 @@ class CascadedDecimator { private: static constexpr uint32_t MAX_STAGES = 4; - static constexpr uint32_t MAX_FRAMES = 512; + static constexpr uint32_t MAX_FRAMES = Decimator::MAX_FRAMES; uint8_t total_factor_; uint8_t num_stages_; - Decimator* stages_[MAX_STAGES]; - int16_t* temp_buffers_[MAX_STAGES - 1]; - - void setupStages(); - void cleanupStages(); + bool configured_; + Decimator stages_[MAX_STAGES]; + /* Stage output alternates between this buffer and the caller's output. */ + int16_t intermediate_buffer_[MAX_FRAMES]; + float32_t processing_buffer_[MAX_FRAMES * 2]; }; #endif #endif /* _DECIMATION_FILTER_H_ */ From f8791b5232d91fc8d13fa67d9469fb1fb297ac2a Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:49:47 +0200 Subject: [PATCH 30/77] fix(audio-response): suspend active audio during measurements --- src/audio/audio_datapath.c | 101 +++++++++++++++- src/audio/audio_datapath.h | 18 ++- src/audio/audio_system.c | 110 ++++++++++++++++- src/audio/audio_system.h | 19 ++- .../gatt_services/audio_response_service.c | 111 ++++++++++++++++-- 5 files changed, 337 insertions(+), 22 deletions(-) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index c50d44ab..8b87caae 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -423,6 +423,28 @@ static bool tone_active; static uint16_t test_tone_buf[CONFIG_AUDIO_SAMPLE_RATE_HZ / 100]; static size_t test_tone_size; +struct auxiliary_audio_state { + bool suspended; + bool record_to_sd; + bool record_to_buffer; + int16_t *record_buffer; + int record_num_samples; + int record_current_index; + bool record_left; + bool record_right; + void (*record_callback)(void); + bool tone_active; + uint32_t tone_remaining_ms; + int16_t *buffer_data; + uint32_t buffer_pos; + int buffer_num_samples; + float buffer_amplitude; + bool buffer_loop; + void (*buffer_callback)(void); +}; + +static struct auxiliary_audio_state auxiliary_audio_state; + /** * @brief Calculate error between sdu_ref and frame_start_ts_us. * @@ -769,6 +791,83 @@ static void tone_stop_timer_handler(struct k_timer *dummy) K_TIMER_DEFINE(tone_stop_timer, tone_stop_timer_handler, NULL); +int audio_datapath_auxiliary_suspend(void) +{ + if (auxiliary_audio_state.suspended) { + return -EBUSY; + } + + auxiliary_audio_state = (struct auxiliary_audio_state) { + .suspended = true, + .record_to_sd = _record_to_sd, + .record_to_buffer = _record_to_buffer, + .record_buffer = _record_buffer, + .record_num_samples = _record_num_samples, + .record_current_index = _record_current_index, + .record_left = _record_left, + .record_right = _record_right, + .record_callback = _record_callback, + .tone_active = tone_active, + .tone_remaining_ms = k_timer_remaining_get(&tone_stop_timer), + .buffer_data = buffer_play_data, + .buffer_pos = buffer_play_pos, + .buffer_num_samples = buffer_play_num_samples, + .buffer_amplitude = buffer_play_amplitude, + .buffer_loop = buffer_play_loop, + .buffer_callback = buffer_play_callback, + }; + + k_timer_stop(&tone_stop_timer); + (void)k_work_cancel(&tone_stop_work); + _record_to_sd = false; + _record_to_buffer = false; + _record_buffer = NULL; + _record_callback = NULL; + tone_active = false; + buffer_play_data = NULL; + buffer_play_callback = NULL; + + LOG_INF("Auxiliary audio suspended: tone=%d buffer=%d buffer_recording=%d sd_recording=%d", + auxiliary_audio_state.tone_active, auxiliary_audio_state.buffer_data != NULL, + auxiliary_audio_state.record_to_buffer, auxiliary_audio_state.record_to_sd); + return 0; +} + +int audio_datapath_auxiliary_resume(void) +{ + if (!auxiliary_audio_state.suspended) { + return -EALREADY; + } + + /* A completion queued by measurement playback must not clear restored state. */ + (void)k_work_cancel(&tone_stop_work); + _record_to_sd = auxiliary_audio_state.record_to_sd; + _record_to_buffer = auxiliary_audio_state.record_to_buffer; + _record_buffer = auxiliary_audio_state.record_buffer; + _record_num_samples = auxiliary_audio_state.record_num_samples; + _record_current_index = auxiliary_audio_state.record_current_index; + _record_left = auxiliary_audio_state.record_left; + _record_right = auxiliary_audio_state.record_right; + _record_callback = auxiliary_audio_state.record_callback; + tone_active = auxiliary_audio_state.tone_active; + buffer_play_data = auxiliary_audio_state.buffer_data; + buffer_play_pos = auxiliary_audio_state.buffer_pos; + buffer_play_num_samples = auxiliary_audio_state.buffer_num_samples; + buffer_play_amplitude = auxiliary_audio_state.buffer_amplitude; + buffer_play_loop = auxiliary_audio_state.buffer_loop; + buffer_play_callback = auxiliary_audio_state.buffer_callback; + + if (tone_active && auxiliary_audio_state.tone_remaining_ms > 0U) { + k_timer_start(&tone_stop_timer, K_MSEC(auxiliary_audio_state.tone_remaining_ms), + K_NO_WAIT); + } + + LOG_INF("Auxiliary audio resumed: tone=%d buffer=%d buffer_recording=%d sd_recording=%d", + tone_active, buffer_play_data != NULL, _record_to_buffer, _record_to_sd); + memset(&auxiliary_audio_state, 0, sizeof(auxiliary_audio_state)); + return 0; +} + int audio_datapath_tone_play(uint16_t freq, uint16_t dur_ms, float amplitude) { int ret; @@ -1421,7 +1520,7 @@ int audio_datapath_aquire(struct data_fifo *fifo_rx) { return ret; } -int audio_datapath_release() { +int audio_datapath_release(void) { int ret = 0; _count --; diff --git a/src/audio/audio_datapath.h b/src/audio/audio_datapath.h index 091e0021..211f7be1 100644 --- a/src/audio/audio_datapath.h +++ b/src/audio/audio_datapath.h @@ -80,14 +80,22 @@ void record_to_buffer_stop(void); void audio_datapath_stop_recording(void); /** - * @brief Stops buffer recording safely + * @brief Suspend local playback and recording without losing their state. + * + * The datapath must be stopped before this function is called. A matching call + * to audio_datapath_auxiliary_resume() restores playback positions, callbacks, + * and recording configuration. + * + * @return 0 on success, or -EBUSY if an auxiliary suspension is already active. */ -void record_to_buffer_stop(void); +int audio_datapath_auxiliary_suspend(void); /** - * @brief Stops all audio recording safely (buffer and SD) + * @brief Restore local playback and recording after an auxiliary suspension. + * + * @return 0 on success, or -EALREADY if no suspension is active. */ -void audio_datapath_stop_recording(void); +int audio_datapath_auxiliary_resume(void); /** * @brief Set the presentation delay @@ -153,7 +161,7 @@ void record_to_sd(bool active); void set_sensor_queue(struct k_msgq *queue); int audio_datapath_aquire(struct data_fifo *fifo_rx); -int audio_datapath_release(); +int audio_datapath_release(void); //void set_ring_buffer(struct ring_buf *ring_buf); diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index 493ee96f..5f92fc79 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -47,6 +47,10 @@ static K_SEM_DEFINE(sem_encoder_start, 0, 1); static struct k_thread encoder_thread_data; static k_tid_t encoder_thread_id; static atomic_t encoder_started; +static bool audio_system_suspended; +static bool audio_system_resume_requested; +static bool encoder_resume_requested; +K_MUTEX_DEFINE(audio_system_state_mutex); struct k_poll_signal encoder_sig; @@ -206,7 +210,7 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) } } -void audio_system_encoder_start(void) +static void audio_system_encoder_start_internal(void) { if (!sw_codec_cfg.initialized || !sw_codec_cfg.encoder.enabled || !sw_codec_is_initialized()) { LOG_WRN("Encoder start ignored because codec is not initialized"); @@ -225,7 +229,7 @@ void audio_system_encoder_start(void) }*/ } -void audio_system_encoder_stop(void) +static void audio_system_encoder_stop_internal(void) { atomic_clear(&encoder_started); k_poll_signal_reset(&encoder_sig); @@ -238,6 +242,28 @@ bool audio_system_encoder_is_started(void) sw_codec_cfg.encoder.enabled && sw_codec_is_initialized(); } +void audio_system_encoder_start(void) +{ + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (audio_system_suspended) { + encoder_resume_requested = true; + } else { + audio_system_encoder_start_internal(); + } + k_mutex_unlock(&audio_system_state_mutex); +} + +void audio_system_encoder_stop(void) +{ + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (audio_system_suspended) { + encoder_resume_requested = false; + } else { + audio_system_encoder_stop_internal(); + } + k_mutex_unlock(&audio_system_state_mutex); +} + int audio_system_encode_test_tone_set(uint32_t freq) { int ret; @@ -406,9 +432,10 @@ int audio_system_decode(void const *const encoded_data, size_t encoded_data_size return 0; } -/**@brief Initializes the FIFOs, the codec, and starts the I2S +/** + * @brief Initialize the FIFOs and codecs, then start the audio datapath. */ -void audio_system_start(void) +static void audio_system_start_internal(void) { int ret; @@ -466,7 +493,7 @@ void audio_system_start(void) #endif /* ((CONFIG_AUDIO_SOURCE_USB) && (CONFIG_AUDIO_DEV == GATEWAY))) */ } -void audio_system_stop(void) +static void audio_system_stop_internal(void) { int ret; @@ -476,7 +503,7 @@ void audio_system_stop(void) } LOG_DBG("Stopping codec"); - audio_system_encoder_stop(); + audio_system_encoder_stop_internal(); #if ((CONFIG_AUDIO_DEV == GATEWAY) && CONFIG_AUDIO_SOURCE_USB) audio_usb_stop(); @@ -501,6 +528,77 @@ void audio_system_stop(void) data_fifo_empty(&fifo_tx); } +void audio_system_start(void) +{ + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (audio_system_suspended) { + audio_system_resume_requested = true; + } else { + audio_system_start_internal(); + } + k_mutex_unlock(&audio_system_state_mutex); +} + +void audio_system_stop(void) +{ + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (audio_system_suspended) { + audio_system_resume_requested = false; + encoder_resume_requested = false; + } else { + audio_system_stop_internal(); + } + k_mutex_unlock(&audio_system_state_mutex); +} + +int audio_system_suspend(void) +{ + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (audio_system_suspended) { + k_mutex_unlock(&audio_system_state_mutex); + return -EBUSY; + } + + audio_system_resume_requested = sw_codec_cfg.initialized; + encoder_resume_requested = audio_system_encoder_is_started(); + audio_system_suspended = true; + if (audio_system_resume_requested) { + audio_system_stop_internal(); + } + LOG_INF("Audio system suspended: resume=%d encoder=%d", audio_system_resume_requested, + encoder_resume_requested); + k_mutex_unlock(&audio_system_state_mutex); + return 0; +} + +int audio_system_resume(void) +{ + bool resume_requested; + bool encoder_requested; + + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); + if (!audio_system_suspended) { + k_mutex_unlock(&audio_system_state_mutex); + return -EALREADY; + } + + audio_system_suspended = false; + resume_requested = audio_system_resume_requested; + encoder_requested = encoder_resume_requested; + if (resume_requested) { + audio_system_start_internal(); + if (encoder_requested) { + audio_system_encoder_start_internal(); + } + } + LOG_INF("Audio system suspension released: running=%d encoder=%d", resume_requested, + encoder_requested); + audio_system_resume_requested = false; + encoder_resume_requested = false; + k_mutex_unlock(&audio_system_state_mutex); + return 0; +} + int audio_system_fifo_rx_block_drop(void) { int ret; diff --git a/src/audio/audio_system.h b/src/audio/audio_system.h index 0c5829e9..26af8584 100644 --- a/src/audio/audio_system.h +++ b/src/audio/audio_system.h @@ -38,6 +38,23 @@ void audio_system_encoder_stop(void); */ bool audio_system_encoder_is_started(void); +/** + * @brief Exclusively suspend the audio system while preserving requested state. + * + * Start, stop, and encoder requests received during the suspension are deferred + * and applied by audio_system_resume(). + * + * @return 0 on success, or -EBUSY if the system is already suspended. + */ +int audio_system_suspend(void); + +/** + * @brief Release an exclusive suspension and apply the latest audio state. + * + * @return 0 on success, or -EALREADY if the system is not suspended. + */ +int audio_system_resume(void); + /** * @brief Toggle a test tone on and off. * @@ -87,7 +104,7 @@ int audio_system_config_set(uint32_t encoder_sample_rate_hz, uint32_t encoder_bi int audio_system_decode(void const *const encoded_data, size_t encoded_data_size, bool bad_frame); /** - * @brief Initialize and start both HW and SW audio codec. + * @brief Initialize and start both HW and SW audio codecs. */ void audio_system_start(void); diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index 7fac1e5e..f63634d8 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -11,9 +11,10 @@ #include #include "audio_datapath.h" +#include "audio_system.h" #include "hw_codec.h" -LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); +LOG_MODULE_REGISTER(audio_response_service, LOG_LEVEL_DBG); #define AUDIO_RESPONSE_CAPTURE_SAMPLES 2048 #define AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE 4000U @@ -50,6 +51,14 @@ struct audio_response_transfer { bool committed; }; +struct audio_response_audio_session { + bool audio_system_suspended; + bool auxiliary_audio_suspended; + bool datapath_acquired; + bool measurement_codec_enabled; + bool measurement_playback_started; +}; + /** * Carries the GATT write metadata and result through the generated union dispatcher. */ @@ -71,6 +80,7 @@ static uint16_t result_response[CONFIG_AUDIO_RESPONSE_MAX_POINTS]; static uint8_t result_payload[AUDIO_RESPONSE_RESULT_ENCODED_SIZE]; static struct audio_response_transfer transfer; +static struct audio_response_audio_session audio_session; static audio_response_config_t pending_config; static bool measurement_active; static bool transfer_status_notifications_enabled; @@ -91,6 +101,8 @@ static uint16_t response_magnitude_for_bin(const int16_t *samples, size_t sample uint32_t bin); static int select_response_points(audio_response_config_t *config); static void reset_transfer(void); +static int suspend_audio_for_measurement(void); +static void restore_audio_after_measurement(void); /** * Return a readable transfer status name for logs. @@ -637,6 +649,74 @@ BT_GATT_SERVICE_DEFINE(audio_response_svc, BT_GATT_CCC(result_ccc_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), ); +/** + * Stop shared audio activity and retain enough state to restore it after the + * response measurement owns the datapath. + */ +static int suspend_audio_for_measurement(void) +{ + int ret; + + memset(&audio_session, 0, sizeof(audio_session)); + ret = audio_system_suspend(); + if (ret != 0) { + LOG_ERR("Failed to suspend audio system: %d", ret); + return ret; + } + audio_session.audio_system_suspended = true; + + ret = audio_datapath_auxiliary_suspend(); + if (ret != 0) { + LOG_ERR("Failed to suspend auxiliary audio activity: %d", ret); + (void)audio_system_resume(); + memset(&audio_session, 0, sizeof(audio_session)); + return ret; + } + LOG_DBG("Auxiliary audio activity suspended for measurement"); + + audio_session.auxiliary_audio_suspended = true; + return 0; +} + +/** + * Stop measurement-specific activity and restore the audio state captured by + * suspend_audio_for_measurement(). Safe to call after partial setup. + */ +static void restore_audio_after_measurement(void) +{ + int ret; + + record_to_buffer_stop(); + if (audio_session.measurement_playback_started) { + audio_datapath_buffer_stop(); + } + if (audio_session.measurement_codec_enabled) { + ret = hw_codec_stop_audio(); + if (ret != 0) { + LOG_ERR("Failed to stop measurement codec: %d", ret); + } + } + if (audio_session.datapath_acquired) { + ret = audio_datapath_release(); + if (ret != 0) { + LOG_ERR("Failed to release measurement datapath: %d", ret); + } + } + if (audio_session.auxiliary_audio_suspended) { + ret = audio_datapath_auxiliary_resume(); + if (ret != 0) { + LOG_ERR("Failed to restore auxiliary audio activity: %d", ret); + } + } + if (audio_session.audio_system_suspended) { + ret = audio_system_resume(); + if (ret != 0) { + LOG_ERR("Failed to resume audio system: %d", ret); + } + } + memset(&audio_session, 0, sizeof(audio_session)); +} + /** * Run the audio playback and capture setup from the system work queue. */ @@ -648,6 +728,12 @@ static void measurement_work_handler(struct k_work *work) pending_config.id, pending_config.transfer_id, transfer.total_samples, (double)pending_config.volume, pending_config.points); + ret = suspend_audio_for_measurement(); + if (ret != 0) { + goto fail; + } + LOG_DBG("Audio system suspended for audio response measurement: id=%u", pending_config.id); + if (!fifo_rx.initialized) { LOG_DBG("Initializing RX FIFO for audio response measurement"); ret = data_fifo_init(&fifo_rx); @@ -656,30 +742,37 @@ static void measurement_work_handler(struct k_work *work) goto fail; } } + LOG_DBG("RX FIFO ready for audio response measurement: id=%u", pending_config.id); - ret = hw_codec_volume_unmute(); - if (ret != 0) { - LOG_ERR("Failed to unmute codec volume: %d", ret); - goto fail; - } ret = audio_datapath_decimator_init(CONFIG_AUDIO_SAMPLE_RATE_HZ / AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE); if (ret != 0) { LOG_ERR("Failed to initialize audio response decimator: %d", ret); goto fail; } + LOG_DBG("Audio response decimator initialized: input_rate=%u output_rate=%u", + CONFIG_AUDIO_SAMPLE_RATE_HZ, AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE); ret = audio_datapath_aquire(&fifo_rx); if (ret != 0) { LOG_ERR("Failed to acquire audio datapath: %d", ret); goto fail; } + LOG_DBG("Audio datapath acquired: id=%u", pending_config.id); + audio_session.datapath_acquired = true; + audio_session.measurement_codec_enabled = true; + ret = hw_codec_default_conf_enable(); + if (ret != 0) { + LOG_ERR("Failed to enable codec for audio response measurement: %d", ret); + goto fail; + } + LOG_DBG("Codec enabled for audio response measurement: id=%u", pending_config.id); ret = audio_datapath_buffer_play(transfer.samples, transfer.total_samples, false, pending_config.volume, NULL); if (ret != 0) { LOG_ERR("Failed to start audio response playback: %d", ret); - audio_datapath_release(); goto fail; } + audio_session.measurement_playback_started = true; LOG_INF("Audio response playback started: id=%u capture_samples=%u capture_rate=%u initial_drop=%u", pending_config.id, AUDIO_RESPONSE_CAPTURE_SAMPLES, @@ -690,6 +783,7 @@ static void measurement_work_handler(struct k_work *work) fail: LOG_ERR("Failed to start audio response measurement: %d", ret); + restore_audio_after_measurement(); k_mutex_lock(&service_mutex, K_FOREVER); measurement_active = false; k_mutex_unlock(&service_mutex); @@ -735,8 +829,7 @@ static void notify_result(void) static void measurement_complete_work_handler(struct k_work *work) { LOG_INF("Audio response capture complete: id=%u", pending_config.id); - audio_datapath_buffer_stop(); - audio_datapath_release(); + restore_audio_after_measurement(); for (size_t index = 0; index < pending_config.points; ++index) { uint32_t bin = response_frequency_to_bin(pending_config.frequencies[index]); From 7d621732baa50855e4a41faf23df443eeeb5b6b7 Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:51:39 +0200 Subject: [PATCH 31/77] fix(audio): propagate playback startup failures --- src/audio/audio_system.c | 86 ++++++++++++++++++++++++++++++++-------- src/audio/audio_system.h | 7 +++- src/audio/streamctrl.c | 6 ++- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index 5f92fc79..51048694 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -434,8 +434,10 @@ int audio_system_decode(void const *const encoded_data, size_t encoded_data_size /** * @brief Initialize the FIFOs and codecs, then start the audio datapath. + * + * @return 0 on success, or a negative error from the failed subsystem. */ -static void audio_system_start_internal(void) +static int audio_system_start_internal(void) { int ret; @@ -445,23 +447,32 @@ static void audio_system_start_internal(void) audio_gateway_configure(); } else { LOG_ERR("Invalid CONFIG_AUDIO_DEV: %d", CONFIG_AUDIO_DEV); - ERR_CHK(-EINVAL); + return -EINVAL; } if (!fifo_tx.initialized) { ret = data_fifo_init(&fifo_tx); - ERR_CHK_MSG(ret, "Failed to set up tx FIFO"); + if (ret) { + LOG_ERR("Failed to set up tx FIFO: %d", ret); + return ret; + } } if (!fifo_rx.initialized) { ret = data_fifo_init(&fifo_rx); - ERR_CHK_MSG(ret, "Failed to set up rx FIFO"); + if (ret) { + LOG_ERR("Failed to set up rx FIFO: %d", ret); + return ret; + } } LOG_INF("Microphone channel set to %d", sw_codec_cfg.encoder.audio_ch); ret = sw_codec_init(sw_codec_cfg); - ERR_CHK_MSG(ret, "Failed to set up codec"); + if (ret) { + LOG_ERR("Failed to set up codec: %d", ret); + return ret; + } sw_codec_cfg.initialized = true; @@ -471,19 +482,37 @@ static void audio_system_start_internal(void) (k_thread_entry_t)encoder_thread, NULL, NULL, NULL, K_PRIO_PREEMPT(CONFIG_ENCODER_THREAD_PRIO), 0, K_NO_WAIT); ret = k_thread_name_set(encoder_thread_id, "ENCODER"); - ERR_CHK(ret); + if (ret) { + LOG_WRN("Failed to name encoder thread: %d", ret); + } } #if ((CONFIG_AUDIO_SOURCE_USB) && (CONFIG_AUDIO_DEV == GATEWAY)) ret = audio_usb_start(&fifo_tx, &fifo_rx); - ERR_CHK(ret); + if (ret) { + LOG_ERR("Failed to start USB audio: %d", ret); + goto cleanup_sw_codec; + } #else ret = audio_datapath_aquire(&fifo_rx); - ERR_CHK(ret); + if (ret) { + LOG_ERR("Failed to acquire audio datapath: %d", ret); + goto cleanup_sw_codec; + } ret = hw_codec_default_conf_enable(); - ERR_CHK(ret); + if (ret) { + int release_ret; + + LOG_ERR("Failed to configure hardware codec: %d", ret); + release_ret = audio_datapath_release(); + if (release_ret) { + LOG_ERR("Failed to release audio datapath after startup error: %d", + release_ret); + } + goto cleanup_sw_codec; + } /*if (IS_ENABLED(CONFIG_AUDIO_MIC_PDM)) { ret = pdm_datapath_start(&fifo_rx); @@ -491,6 +520,19 @@ static void audio_system_start_internal(void) }*/ #endif /* ((CONFIG_AUDIO_SOURCE_USB) && (CONFIG_AUDIO_DEV == GATEWAY))) */ + + return 0; + +cleanup_sw_codec: + { + int uninit_ret = sw_codec_uninit(sw_codec_cfg); + + sw_codec_cfg.initialized = false; + if (uninit_ret) { + LOG_ERR("Failed to uninitialize codec after startup error: %d", uninit_ret); + } + } + return ret; } static void audio_system_stop_internal(void) @@ -528,15 +570,18 @@ static void audio_system_stop_internal(void) data_fifo_empty(&fifo_tx); } -void audio_system_start(void) +int audio_system_start(void) { + int ret = 0; + k_mutex_lock(&audio_system_state_mutex, K_FOREVER); if (audio_system_suspended) { audio_system_resume_requested = true; } else { - audio_system_start_internal(); + ret = audio_system_start_internal(); } k_mutex_unlock(&audio_system_state_mutex); + return ret; } void audio_system_stop(void) @@ -573,6 +618,7 @@ int audio_system_suspend(void) int audio_system_resume(void) { + int ret = 0; bool resume_requested; bool encoder_requested; @@ -586,17 +632,17 @@ int audio_system_resume(void) resume_requested = audio_system_resume_requested; encoder_requested = encoder_resume_requested; if (resume_requested) { - audio_system_start_internal(); - if (encoder_requested) { + ret = audio_system_start_internal(); + if (!ret && encoder_requested) { audio_system_encoder_start_internal(); } } - LOG_INF("Audio system suspension released: running=%d encoder=%d", resume_requested, - encoder_requested); + LOG_INF("Audio system suspension released: running=%d encoder=%d result=%d", + resume_requested && !ret, encoder_requested && !ret, ret); audio_system_resume_requested = false; encoder_resume_requested = false; k_mutex_unlock(&audio_system_state_mutex); - return 0; + return ret; } int audio_system_fifo_rx_block_drop(void) @@ -652,10 +698,16 @@ int audio_system_init(void) static int cmd_audio_system_start(const struct shell *shell, size_t argc, const char **argv) { + int ret; + ARG_UNUSED(argc); ARG_UNUSED(argv); - audio_system_start(); + ret = audio_system_start(); + if (ret) { + shell_error(shell, "Audio system failed to start: %d", ret); + return ret; + } shell_print(shell, "Audio system started"); diff --git a/src/audio/audio_system.h b/src/audio/audio_system.h index 26af8584..c8dc6849 100644 --- a/src/audio/audio_system.h +++ b/src/audio/audio_system.h @@ -51,7 +51,8 @@ int audio_system_suspend(void); /** * @brief Release an exclusive suspension and apply the latest audio state. * - * @return 0 on success, or -EALREADY if the system is not suspended. + * @return 0 on success, -EALREADY if the system is not suspended, or a + * negative startup error when the preserved audio state cannot resume. */ int audio_system_resume(void); @@ -105,8 +106,10 @@ int audio_system_decode(void const *const encoded_data, size_t encoded_data_size /** * @brief Initialize and start both HW and SW audio codecs. + * + * @return 0 on success, or a negative error returned by the codec or datapath. */ -void audio_system_start(void); +int audio_system_start(void); /** * @brief Stop all activities related to audio. diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 8cd15d97..6982d09b 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -199,7 +199,11 @@ static void le_audio_msg_sub_thread(void) break; } - audio_system_start(); + ret = audio_system_start(); + if (ret) { + LOG_ERR("Failed to start audio system: %d", ret); + break; + } stream_state_set(STATE_STREAMING); if (msg.dir == BT_AUDIO_DIR_SOURCE) { audio_system_encoder_start(); From d78b48ad5fc58f2215dcb58854767d95ac480fba Mon Sep 17 00:00:00 2001 From: Dennis Moschina <45356478+DennisMoschina@users.noreply.github.com> Date: Fri, 17 Jul 2026 16:16:21 +0200 Subject: [PATCH 32/77] fix(audio): smooth buffer playback transitions --- src/audio/audio_datapath.c | 19 ++++++++++++++++++- src/modules/hw_codec_adau1860.cpp | 12 ++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 8b87caae..ca43d0b8 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -103,6 +103,10 @@ LOG_MODULE_REGISTER(audio_datapath, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); /* How often to print under-run warning */ #define UNDERRUN_LOG_INTERVAL_BLKS 5000 +/* Smooth the transition from silence to local buffer playback. */ +#define BUFFER_PLAY_FADE_IN_MS 5U +#define BUFFER_PLAY_FADE_IN_SAMPLES ((CONFIG_AUDIO_SAMPLE_RATE_HZ * BUFFER_PLAY_FADE_IN_MS) / 1000U) + enum drift_comp_state { DRIFT_STATE_INIT, /* Waiting for data to be received */ DRIFT_STATE_CALIB, /* Calibrate and zero out local delay */ @@ -209,6 +213,7 @@ int _count = 0; static int16_t *buffer_play_data = NULL; static uint32_t buffer_play_pos; +static uint32_t buffer_play_fade_pos; static int buffer_play_num_samples; static float buffer_play_amplitude; static bool buffer_play_loop; @@ -437,6 +442,7 @@ struct auxiliary_audio_state { uint32_t tone_remaining_ms; int16_t *buffer_data; uint32_t buffer_pos; + uint32_t buffer_fade_pos; int buffer_num_samples; float buffer_amplitude; bool buffer_loop; @@ -811,6 +817,7 @@ int audio_datapath_auxiliary_suspend(void) .tone_remaining_ms = k_timer_remaining_get(&tone_stop_timer), .buffer_data = buffer_play_data, .buffer_pos = buffer_play_pos, + .buffer_fade_pos = buffer_play_fade_pos, .buffer_num_samples = buffer_play_num_samples, .buffer_amplitude = buffer_play_amplitude, .buffer_loop = buffer_play_loop, @@ -852,6 +859,7 @@ int audio_datapath_auxiliary_resume(void) tone_active = auxiliary_audio_state.tone_active; buffer_play_data = auxiliary_audio_state.buffer_data; buffer_play_pos = auxiliary_audio_state.buffer_pos; + buffer_play_fade_pos = auxiliary_audio_state.buffer_fade_pos; buffer_play_num_samples = auxiliary_audio_state.buffer_num_samples; buffer_play_amplitude = auxiliary_audio_state.buffer_amplitude; buffer_play_loop = auxiliary_audio_state.buffer_loop; @@ -915,6 +923,7 @@ int audio_datapath_buffer_play(int16_t *buffer, int num_samples, bool loop, floa buffer_play_data = buffer; buffer_play_pos = 0; + buffer_play_fade_pos = 0; buffer_play_num_samples = num_samples; buffer_play_amplitude = amplitude; buffer_play_loop = loop; @@ -962,6 +971,8 @@ static void tone_mix(uint8_t *tx_buf) /* Copy buffer samples to playback buffer with amplitude scaling */ for (int i = 0; i < samples_per_block; i++) { + float gain = buffer_play_amplitude; + if (buffer_play_pos >= buffer_play_num_samples) { if (buffer_play_loop) { buffer_play_pos = 0; /* Loop the buffer */ @@ -972,7 +983,13 @@ static void tone_mix(uint8_t *tx_buf) break; } } - int16_t sample = (int16_t)(buffer_play_data[buffer_play_pos] * buffer_play_amplitude); + + if (buffer_play_fade_pos < BUFFER_PLAY_FADE_IN_SAMPLES) { + gain *= (float)buffer_play_fade_pos / BUFFER_PLAY_FADE_IN_SAMPLES; + buffer_play_fade_pos++; + } + + int16_t sample = (int16_t)(buffer_play_data[buffer_play_pos] * gain); buffer_play_buf[i * 2] = sample & 0xFF; buffer_play_buf[i * 2 + 1] = (sample >> 8) & 0xFF; buffer_play_pos++; diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index ada2b4af..867c9c5e 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -26,6 +26,10 @@ LOG_MODULE_REGISTER(hw_codec, CONFIG_MODULE_HW_CODEC_LOG_LEVEL); #define VOLUME_ADJUST_STEP_DB 3 +/* Allow the DSP mute ramp and I2S clock domain to settle at source changes. */ +#define CODEC_MUTE_SETTLE_MS 100 +#define CODEC_I2S_PREROLL_MS 100 + ZBUS_SUBSCRIBER_DEFINE(volume_evt_sub, CONFIG_VOLUME_MSG_SUB_QUEUE_SIZE); static uint32_t prev_volume_reg_val = OUT_VOLUME_DEFAULT; @@ -290,6 +294,11 @@ int hw_codec_default_conf_enable(void) return ret; } + /* audio_datapath_aquire() starts I2S before enabling the codec. Keep the + * output muted while the new clock stream becomes stable. + */ + k_msleep(CODEC_I2S_PREROLL_MS); + //ret = dac.setup(); if (!muted) { ret = dac.mute(false); @@ -310,6 +319,9 @@ int hw_codec_stop_audio(void) return ret; } + /* Do not remove the I2S clocks while the DSP is still applying mute. */ + k_msleep(CODEC_MUTE_SETTLE_MS); + return 0; } From d798e13a750c7238cd4d6a38060aa260fa3a29ad Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Mon, 27 Jul 2026 17:16:11 +0200 Subject: [PATCH 33/77] fix(audio-response): prevent decimator buffer overruns --- src/audio/audio_datapath.c | 10 ++++++++-- src/audio/decimation_filter.cpp | 4 +++- src/audio/decimation_filter.h | 4 ++-- src/bluetooth/gatt_services/audio_response_service.c | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index ca43d0b8..25b63b2f 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -222,8 +222,14 @@ static void (*buffer_play_callback)(void) = NULL; extern struct k_poll_signal encoder_sig; extern struct k_poll_event logger_sig; -/* Output buffer sized for the largest processed output: decimation factor 2. */ -static int16_t decimated_audio[BLOCK_SIZE_BYTES / sizeof(int16_t) / 2]; +/* + * Decimation output for one interleaved stereo audio block. + * + * The decimator supports factors down to 1, so its worst-case output contains + * every input sample. Seal check currently uses factor 3 (48 kHz -> 16 kHz); + * sizing this buffer for the old fixed factor 4 overflowed it on every block. + */ +static int16_t decimated_audio[BLOCK_SIZE_BYTES / sizeof(int16_t)]; // Funktion für den neuen Thread static void data_thread(void *arg1, void *arg2, void *arg3) diff --git a/src/audio/decimation_filter.cpp b/src/audio/decimation_filter.cpp index e7cc4b2d..36af29fa 100644 --- a/src/audio/decimation_filter.cpp +++ b/src/audio/decimation_filter.cpp @@ -211,7 +211,9 @@ int CascadedDecimator::process(const int16_t* input, int16_t* output, uint32_t n } if (num_stages_ == 0) { - memcpy(output, input, num_frames * 2 * sizeof(int16_t)); + if (output != input) { + memcpy(output, input, num_frames * 2U * sizeof(int16_t)); + } return num_frames; } diff --git a/src/audio/decimation_filter.h b/src/audio/decimation_filter.h index 2fd57353..a3f0d5d9 100644 --- a/src/audio/decimation_filter.h +++ b/src/audio/decimation_filter.h @@ -142,8 +142,8 @@ class CascadedDecimator { uint8_t num_stages_; bool configured_; Decimator stages_[MAX_STAGES]; - /* Stage output alternates between this buffer and the caller's output. */ - int16_t intermediate_buffer_[MAX_FRAMES]; + /* Stage output is interleaved stereo, so each frame needs two samples. */ + int16_t intermediate_buffer_[MAX_FRAMES * 2U]; float32_t processing_buffer_[MAX_FRAMES * 2]; }; #endif diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index f63634d8..68403d15 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -759,12 +759,12 @@ static void measurement_work_handler(struct k_work *work) } LOG_DBG("Audio datapath acquired: id=%u", pending_config.id); audio_session.datapath_acquired = true; - audio_session.measurement_codec_enabled = true; ret = hw_codec_default_conf_enable(); if (ret != 0) { LOG_ERR("Failed to enable codec for audio response measurement: %d", ret); goto fail; } + audio_session.measurement_codec_enabled = true; LOG_DBG("Codec enabled for audio response measurement: id=%u", pending_config.id); ret = audio_datapath_buffer_play(transfer.samples, transfer.total_samples, false, pending_config.volume, NULL); From 2866691c5322eb01cbcbb4599729f89b6617a828 Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Tue, 28 Jul 2026 09:24:43 +0200 Subject: [PATCH 34/77] fix(audio-response): make repeated seal checks reliable --- prj.conf | 1 + prj_fota.conf | 1 + .../gatt_services/audio_response_service.c | 36 +++++++++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/prj.conf b/prj.conf index f2099611..aae3b8f7 100644 --- a/prj.conf +++ b/prj.conf @@ -140,6 +140,7 @@ CONFIG_BT_USER_PHY_UPDATE=y CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=8 CONFIG_BT_BUF_ACL_TX_SIZE=502 CONFIG_BT_L2CAP_TX_MTU=498 CONFIG_BT_CTLR_PHY_2M=y diff --git a/prj_fota.conf b/prj_fota.conf index 564a6ae7..746c7a26 100644 --- a/prj_fota.conf +++ b/prj_fota.conf @@ -135,6 +135,7 @@ CONFIG_BT_USER_PHY_UPDATE=y CONFIG_BT_CTLR_DATA_LENGTH_MAX=251 CONFIG_BT_BUF_ACL_RX_SIZE=502 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=8 CONFIG_BT_BUF_ACL_TX_SIZE=502 CONFIG_BT_L2CAP_TX_MTU=498 CONFIG_BT_CTLR_PHY_2M=y diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index 68403d15..a8b71ed6 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -14,13 +14,14 @@ #include "audio_system.h" #include "hw_codec.h" -LOG_MODULE_REGISTER(audio_response_service, LOG_LEVEL_DBG); +LOG_MODULE_REGISTER(audio_response_service, CONFIG_LOG_DEFAULT_LEVEL); #define AUDIO_RESPONSE_CAPTURE_SAMPLES 2048 #define AUDIO_RESPONSE_CAPTURE_SAMPLE_RATE 4000U #define AUDIO_RESPONSE_INITIAL_DROP 128 #define AUDIO_RESPONSE_DEFAULT_POINTS 9 #define AUDIO_RESPONSE_TRANSFER_CREDITS 1 +#define AUDIO_RESPONSE_TRANSFER_CREDIT_DELAY K_MSEC(1) #define AUDIO_RESPONSE_TRANSFER_TIMEOUT K_SECONDS(30) #define AUDIO_RESPONSE_STATUS_ENCODED_SIZE 9 #define AUDIO_RESPONSE_RESULT_ENCODED_SIZE (2 + (CONFIG_AUDIO_RESPONSE_MAX_POINTS * 4)) @@ -88,6 +89,7 @@ static bool result_notifications_enabled; static struct k_work measurement_work; static struct k_work measurement_complete_work; +static struct k_work_delayable transfer_ready_work; static struct k_work_delayable transfer_timeout_work; K_MUTEX_DEFINE(service_mutex); @@ -249,6 +251,7 @@ static void reset_transfer(void) { LOG_DBG("Reset transfer state: id=%u active=%d committed=%d received=%u/%u", transfer.id, transfer.active, transfer.committed, transfer.received_samples, transfer.total_samples); + k_work_cancel_delayable(&transfer_ready_work); memset(&transfer, 0, sizeof(transfer)); } @@ -312,11 +315,12 @@ static ssize_t start_transfer(const audio_response_transfer_start_t *start, uint LOG_INF("Transfer start requested: id=%u samples=%u rate=%u checksum=0x%08x", start->transfer_id, start->total_samples, start->sampling_rate, start->checksum); - if (measurement_active || transfer.active || start->total_samples == 0 || + if (measurement_active || (transfer.active && !transfer.committed) || + start->total_samples == 0 || start->sampling_rate != CONFIG_AUDIO_SAMPLE_RATE_HZ) { - LOG_WRN("Transfer start rejected: measurement_active=%d active=%d samples=%u rate=%u expected_rate=%u", - measurement_active, transfer.active, start->total_samples, start->sampling_rate, - CONFIG_AUDIO_SAMPLE_RATE_HZ); + LOG_WRN("Transfer start rejected: measurement_active=%d active=%d committed=%d samples=%u rate=%u expected_rate=%u", + measurement_active, transfer.active, transfer.committed, start->total_samples, + start->sampling_rate, CONFIG_AUDIO_SAMPLE_RATE_HZ); return reject_transfer(AUDIO_RESPONSE_TRANSFER_INVALID_STATE, BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED)); } @@ -541,7 +545,7 @@ static ssize_t write_transfer_data(struct bt_conn *conn, const struct bt_gatt_at } transfer.received_samples += sample_count; k_work_reschedule(&transfer_timeout_work, AUDIO_RESPONSE_TRANSFER_TIMEOUT); - (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, AUDIO_RESPONSE_TRANSFER_CREDITS); + k_work_reschedule(&transfer_ready_work, AUDIO_RESPONSE_TRANSFER_CREDIT_DELAY); if (transfer.received_samples == transfer.total_samples) { LOG_INF("Transfer upload complete: id=%u samples=%u", transfer.id, transfer.total_samples); @@ -766,6 +770,11 @@ static void measurement_work_handler(struct k_work *work) } audio_session.measurement_codec_enabled = true; LOG_DBG("Codec enabled for audio response measurement: id=%u", pending_config.id); + ret = hw_codec_volume_unmute(); + if (ret != 0) { + LOG_ERR("Failed to unmute codec for audio response measurement: %d", ret); + goto fail; + } ret = audio_datapath_buffer_play(transfer.samples, transfer.total_samples, false, pending_config.volume, NULL); if (ret != 0) { @@ -849,6 +858,20 @@ static void measurement_complete_work_handler(struct k_work *work) LOG_DBG("Audio response measurement complete: id=%u", pending_config.id); } +/** + * Grant the next upload credit after the current GATT write has released its + * incoming ACL buffer. + */ +static void transfer_ready_work_handler(struct k_work *work) +{ + k_mutex_lock(&service_mutex, K_FOREVER); + if (transfer.active && !transfer.committed) { + (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, + AUDIO_RESPONSE_TRANSFER_CREDITS); + } + k_mutex_unlock(&service_mutex); +} + /** * Release an incomplete transfer after the protocol timeout. */ @@ -875,6 +898,7 @@ int init_audio_response_service(void) LOG_INF("Initializing audio response service"); k_work_init(&measurement_work, measurement_work_handler); k_work_init(&measurement_complete_work, measurement_complete_work_handler); + k_work_init_delayable(&transfer_ready_work, transfer_ready_work_handler); k_work_init_delayable(&transfer_timeout_work, transfer_timeout_work_handler); return 0; } From 683e1cd3e24d19f89b5a608f09ed5b6522b0146d Mon Sep 17 00:00:00 2001 From: TobiasRoeddiger Date: Fri, 31 Jul 2026 12:25:07 +0200 Subject: [PATCH 35/77] docs: document protocol submodule setup --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 78b3f08a..1c032739 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,16 @@ - In the **nRF Connect** tab, select **"Manage SDK"**. - Install **SDK version 3.0.1**. -7. **Open the Firmware Folder in VS Code** +7. **Clone or Update the Firmware Repository and Open It in VS Code** + - For a new checkout, clone the repository together with its submodules: + ```bash + git clone --recurse-submodules https://github.com/OpenEarable/open-earable-2.git + ``` + - If the repository is already checked out, initialize or update its submodules from the repository root: + ```bash + git submodule update --init --recursive + ``` + - The `protocol` submodule provides the generated C sources in `protocol/generated/c` that are required during CMake configuration. - Use `File > Open Folder` or drag-and-drop the firmware directory into VS Code. - OR in the **APPLICATIONS** section of the nRF Connect tab: - Select `Open Exisiting Application`. @@ -195,4 +204,3 @@ If you are using OpenEarable, please cite is as follows: ``` - From e9f43a626b3c26afd1fbdf2e226003e14eb5181a Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Fri, 31 Jul 2026 18:53:31 +0200 Subject: [PATCH 36/77] fix(audio): read audio channel characteristic with correct size --- src/bluetooth/gatt_services/audio_config_service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index b106dcb0..c4f4bc4b 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -69,7 +69,7 @@ static ssize_t read_audio_channel(struct bt_conn *conn, const struct bt_gatt_att channel_assignment_get(&channel); uint8_t channel_u8 = channel; - return bt_gatt_attr_read(conn, attr, buf, len, offset, &channel_u8, sizeof(channel)); + return bt_gatt_attr_read(conn, attr, buf, len, offset, &channel_u8, sizeof(channel_u8)); } static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, From 96e1e6133e22967438594d732ba3755e060cd98a Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 15:46:48 +0200 Subject: [PATCH 37/77] fix(sca): Add missing API declarations Include the standard and project headers that declare functions used by each translation unit. This lets Clang analyze the files independently instead of relying on transitive includes. --- src/audio/audio_datapath.c | 1 + src/audio/streamctrl.c | 5 +++++ src/bluetooth/bt_management/bt_mgmt.c | 2 ++ src/bluetooth/bt_stream/unicast/unicast_server.c | 4 ++++ src/bluetooth/gatt_services/device_info.c | 2 ++ src/drivers/ADAU1860.cpp | 1 + 6 files changed, 15 insertions(+) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 25b63b2f..13a14472 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -31,6 +31,7 @@ #include "Equalizer.h" #include "sdlogger_wrapper.h" #include "decimation_filter.h" +#include "../SensorManager/SensorManager.h" #include "arm_math.h" #include "hw_codec.h" //#include "../drivers/ADAU1860.h" diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 6982d09b..5ef48656 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -6,6 +6,9 @@ #include "streamctrl.h" +#include +#include +#include "common/bt_str.h" #include #include @@ -28,6 +31,8 @@ #include "AutoOffManager.h" #include "BootState.h" +#include "channel_assignment.h" +#include "uicr.h" #include LOG_MODULE_REGISTER(streamctrl, CONFIG_MAIN_LOG_LEVEL); diff --git a/src/bluetooth/bt_management/bt_mgmt.c b/src/bluetooth/bt_management/bt_mgmt.c index da38a9cb..ab49c474 100644 --- a/src/bluetooth/bt_management/bt_mgmt.c +++ b/src/bluetooth/bt_management/bt_mgmt.c @@ -6,6 +6,7 @@ #include "bt_mgmt.h" +#include #include "channel_assignment.h" #include @@ -19,6 +20,7 @@ #include "macros_common.h" #include "zbus_common.h" #include "button_assignments.h" +#include "uicr.h" #include "bt_mgmt_ctlr_cfg_internal.h" #include "bt_mgmt_adv_internal.h" diff --git a/src/bluetooth/bt_stream/unicast/unicast_server.c b/src/bluetooth/bt_stream/unicast/unicast_server.c index c1aa6384..7aa4cef0 100644 --- a/src/bluetooth/bt_stream/unicast/unicast_server.c +++ b/src/bluetooth/bt_stream/unicast/unicast_server.c @@ -6,6 +6,8 @@ #include "unicast_server.h" + #include + #include #include #include #include @@ -20,7 +22,9 @@ #include "zbus_common.h" #include "bt_mgmt.h" #include "bt_le_audio_tx.h" + #include "channel_assignment.h" #include "le_audio.h" + #include "uicr.h" #include "BootState.h" diff --git a/src/bluetooth/gatt_services/device_info.c b/src/bluetooth/gatt_services/device_info.c index 3cbb548a..cd75ef8b 100644 --- a/src/bluetooth/gatt_services/device_info.c +++ b/src/bluetooth/gatt_services/device_info.c @@ -1,4 +1,6 @@ #include "device_info.h" +#include +#include #include #include diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index c818c7d0..296a5234 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -2,6 +2,7 @@ #include "zbus_common.h" #include "openearable_common.h" #include +#include #include #include From 52c284a0d252221003177f39f0898f8d412e8b43 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 15:47:00 +0200 Subject: [PATCH 38/77] fix(sca): Make narrowing conversions explicit Document the intended loss of precision when storing sensor and parsed values in 32-bit fields. Match variadic format arguments to the unsigned integer type expected by %X while preserving the existing lower-32-bit output. --- src/SensorManager/Baro.cpp | 7 +++++-- src/bluetooth/bt_stream/unicast/unicast_server.c | 4 ++-- src/bluetooth/gatt_services/device_info.c | 5 +++-- src/drivers/ADAU1860.cpp | 11 ++++++----- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/SensorManager/Baro.cpp b/src/SensorManager/Baro.cpp index 955b9519..aac86975 100644 --- a/src/SensorManager/Baro.cpp +++ b/src/SensorManager/Baro.cpp @@ -51,7 +51,10 @@ void Baro::update_sensor(struct k_work *work) { msg_baro.data.size = 2 * sizeof(float); msg_baro.data.time = micros(); - float data[2] = {bmp.temperature, bmp.pressure}; + float data[2] = { + static_cast(bmp.temperature), + static_cast(bmp.pressure), + }; memcpy(msg_baro.data.data, data, 2 * sizeof(float)); @@ -112,4 +115,4 @@ void Baro::stop() { k_timer_stop(&sensor.sensor_timer); pm_device_runtime_put(ls_1_8); -} \ No newline at end of file +} diff --git a/src/bluetooth/bt_stream/unicast/unicast_server.c b/src/bluetooth/bt_stream/unicast/unicast_server.c index 7aa4cef0..82ad636a 100644 --- a/src/bluetooth/bt_stream/unicast/unicast_server.c +++ b/src/bluetooth/bt_stream/unicast/unicast_server.c @@ -712,7 +712,8 @@ static uint8_t device_identifier[] = { if (sirk != 0xFFFFFFFF) { snprintf(sirk_string, 16, "%08X", sirk); //"%016llX" } else { - snprintf(sirk_string, 16, "%08X", oe_boot_state.device_id); //"%016llX" + snprintf(sirk_string, 16, "%08X", + (unsigned int)oe_boot_state.device_id); //"%016llX" } // LOG_INF("SIRK as String: %s", sirk_string); @@ -806,4 +807,3 @@ static uint8_t device_identifier[] = { return 0; } - \ No newline at end of file diff --git a/src/bluetooth/gatt_services/device_info.c b/src/bluetooth/gatt_services/device_info.c index cd75ef8b..13c4438b 100644 --- a/src/bluetooth/gatt_services/device_info.c +++ b/src/bluetooth/gatt_services/device_info.c @@ -19,7 +19,8 @@ static ssize_t read_device_identifier(struct bt_conn *conn, uint16_t len, uint16_t offset) { - snprintf(device_identifier, sizeof(device_identifier), "0x%08X", oe_boot_state.device_id); + snprintf(device_identifier, sizeof(device_identifier), "0x%08X", + (unsigned int)oe_boot_state.device_id); return bt_gatt_attr_read(conn, attr, buf, len, offset, device_identifier, sizeof(device_identifier)); @@ -61,4 +62,4 @@ BT_GATT_CHARACTERISTIC(BT_UUID_FIRMWARE, BT_GATT_CHRC_READ, BT_GATT_PERM_READ, read_firmware, NULL, firmware), -); \ No newline at end of file +); diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index 296a5234..ecf5a4dd 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -591,11 +591,12 @@ int cmd_dsp_noise_gate(const struct shell *shell, size_t argc, char **argv) { safe_load_params params; - params[0] = strtoul(argv[1], NULL, 16) | 0xC80; - params[1] = strtoul(argv[2], NULL, 16) | 0xD00; - params[2] = strtoul(argv[3], NULL, 16); - params[3] = strtoul(argv[4], NULL, 16); - params[4] = strtoul(argv[5], NULL, 16) | 0x80000000; + params[0] = static_cast(strtoul(argv[1], nullptr, 16)) | 0xC80U; + params[1] = static_cast(strtoul(argv[2], nullptr, 16)) | 0xD00U; + params[2] = static_cast(strtoul(argv[3], nullptr, 16)); + params[3] = static_cast(strtoul(argv[4], nullptr, 16)); + params[4] = + static_cast(strtoul(argv[5], nullptr, 16)) | 0x80000000U; shell_print(shell, "Params:"); for (int i = 0; i < FDSP_NUM_PARAMS; i++) { From 2eeadb90be58afea39d4f2f827980df3997d78e5 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 15:47:06 +0200 Subject: [PATCH 39/77] fix(power): Use floating-point absolute value Call std::fabs for the battery-capacity difference so overload resolution is explicit and the comparison remains in floating-point arithmetic. --- src/Battery/PowerManager.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index 5a2c4e23..c77ca391 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -2,6 +2,7 @@ #include "macros_common.h" +#include #include #include #include @@ -404,7 +405,7 @@ int PowerManager::begin() { // check if fuel gauge has wrong value float capacity = fuel_gauge.capacity(); - if (abs(capacity - _battery_settings.capacity) > 1e-4) { + if (std::fabs(capacity - _battery_settings.capacity) > 1e-4F) { fuel_gauge.setup(_battery_settings); set_error_led(); } @@ -738,4 +739,4 @@ SHELL_STATIC_SUBCMD_SET_CREATE(battery_cmd, SHELL_CMD_REGISTER(battery, &battery_cmd, "Power Manager Commands", NULL); -PowerManager power_manager; \ No newline at end of file +PowerManager power_manager; From 61b8e67cb6c71d437fc1aa804d990685e282cf28 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 15:47:10 +0200 Subject: [PATCH 40/77] fix(bluetooth): Return correctly from connection callback The connection callback returns void, so stop processing with a plain return after a parameter-update failure instead of attempting to return the error value. --- src/bluetooth/bt_management/bt_mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bluetooth/bt_management/bt_mgmt.c b/src/bluetooth/bt_management/bt_mgmt.c index ab49c474..f9ed147d 100644 --- a/src/bluetooth/bt_management/bt_mgmt.c +++ b/src/bluetooth/bt_management/bt_mgmt.c @@ -165,7 +165,7 @@ static void connected_cb(struct bt_conn *conn, uint8_t err) err = bt_conn_le_param_update(conn, conn_param); if (err) { LOG_ERR("Cannot update conneciton parameter (err: %d)", err); - return err; + return; } LOG_INF("Connection parameters update requested: interval_min %d interval_max %d latency %d timeout %d", conn_param->interval_min, conn_param->interval_max, From a2172ebab2fdd7c7d43d688860c30276e44ee7e4 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 16:30:26 +0200 Subject: [PATCH 41/77] fix(sca): Initialize state before use Initialize the application LED state and BMP388 FIFO sample data before they are passed to downstream code. This prevents uninitialized fields from being observed on valid error and partial-frame paths. --- src/Battery/PowerManager.cpp | 4 +++- src/SensorManager/BMP388/bmp3.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index c77ca391..09652680 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -263,10 +263,12 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { } int PowerManager::begin() { - earable_state oe_state; + earable_state oe_state{}; oe_state.charging_state = DISCHARGING; oe_state.pairing_state = PAIRED; + oe_state.sd_state = SD_IDLE; + oe_state.led_mode = STATE_INDICATION; battery_controller.begin(); fuel_gauge.begin(); diff --git a/src/SensorManager/BMP388/bmp3.c b/src/SensorManager/BMP388/bmp3.c index 2b614f3a..8af3d209 100644 --- a/src/SensorManager/BMP388/bmp3.c +++ b/src/SensorManager/BMP388/bmp3.c @@ -1089,7 +1089,7 @@ int8_t bmp3_extract_fifo_data(struct bmp3_data *data, struct bmp3_dev *dev) uint8_t header; uint8_t parsed_frames = 0; uint8_t t_p_frame; - struct bmp3_uncomp_data uncomp_data; + struct bmp3_uncomp_data uncomp_data = { 0 }; rslt = null_ptr_check(dev); From 2bb850bd133c486c56b5e9b1b5eb91595feae0b0 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 16:31:07 +0200 Subject: [PATCH 42/77] fix(bmp388): Guard FIFO processing after length read Only adjust and consume the FIFO length after reading it successfully. This avoids updating FIFO state or issuing a read with an undefined length after an I/O failure. --- src/SensorManager/BMP388/bmp3.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/SensorManager/BMP388/bmp3.c b/src/SensorManager/BMP388/bmp3.c index 8af3d209..105c07d3 100644 --- a/src/SensorManager/BMP388/bmp3.c +++ b/src/SensorManager/BMP388/bmp3.c @@ -1028,17 +1028,17 @@ int8_t bmp3_get_fifo_data(struct bmp3_dev *dev) /* Get the total no of bytes available in FIFO */ rslt = bmp3_get_fifo_length(&fifo_len, dev); - /* For sensor time frame */ - if (dev->fifo->settings.time_en == TRUE) + if (rslt == BMP3_OK) { - fifo_len = fifo_len + 4; - } + /* For sensor time frame */ + if (dev->fifo->settings.time_en == TRUE) + { + fifo_len = fifo_len + 4; + } - /* Update the fifo length in the fifo structure */ - dev->fifo->data.byte_count = fifo_len; + /* Update the fifo length in the fifo structure */ + dev->fifo->data.byte_count = fifo_len; - if (rslt == BMP3_OK) - { /* Read the fifo data */ rslt = bmp3_get_regs(BMP3_REG_FIFO_DATA, fifo->data.buffer, fifo_len, dev); } From 5e44e1953e652b60dc4dbc47a6561e9a4c6bd917 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 16:31:11 +0200 Subject: [PATCH 43/77] fix(button): Ignore unrelated GPIO interrupts Return before selecting the Earable button when the callback does not contain its pin. This prevents dereferencing an uninitialized button pointer for an unexpected interrupt mask. --- src/buttons/Button.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/buttons/Button.cpp b/src/buttons/Button.cpp index 6e990342..c0ea3b53 100644 --- a/src/buttons/Button.cpp +++ b/src/buttons/Button.cpp @@ -15,13 +15,12 @@ struct gpio_callback Button::button_cb_data; void Button::button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { - Button * button; - - if (pins & BIT(BUTTON_EARABLE)) { - //earable_btn._read_state(); - button = &earable_btn; + if (!(pins & BIT(BUTTON_EARABLE))) { + return; } + Button *button = &earable_btn; + /*if (pins & BIT(BUTTON_VOLUME_UP)) { volume_up_btn._read_state(); } @@ -114,4 +113,4 @@ button_action Button::getState() const { Button earable_btn(GPIO_DT_SPEC_GET(DT_ALIAS(sw0), gpios)); // Button volume_up_btn(GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw1), gpios, {0})); // Button volume_down_btn(GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw2), gpios, {0})); -// Button four_btn(GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw3), gpios, {0})); \ No newline at end of file +// Button four_btn(GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw3), gpios, {0})); From 8086818f9eb29d153902f94ae11ad83d416f626a Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 16:41:39 +0200 Subject: [PATCH 44/77] fix(sca): Remove unused local values --- src/SensorManager/MLX90632/MLX90632.cpp | 2 -- src/audio/streamctrl.c | 1 - src/bluetooth/bt_stream/bt_le_audio_tx/bt_le_audio_tx.c | 4 ---- 3 files changed, 7 deletions(-) diff --git a/src/SensorManager/MLX90632/MLX90632.cpp b/src/SensorManager/MLX90632/MLX90632.cpp index ab98e9de..f0b1f3eb 100644 --- a/src/SensorManager/MLX90632/MLX90632.cpp +++ b/src/SensorManager/MLX90632/MLX90632.cpp @@ -244,8 +244,6 @@ float MLX90632::getObjectTemp(status& returnError) double AMB = (sixRAM / 12.0) / VRta * pow(2, 19); - double sensorTemp = P_O + (AMB - P_R) / P_G + P_T * pow((AMB - P_R), 2); - float S = (float)(lowerRAM + upperRAM) / 2.0; double VRto = nineRAM + Ka * (sixRAM / 12.0); double Sto = (S / 12.0) / VRto * (double)pow(2, 19); diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 5ef48656..39788522 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -557,7 +557,6 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); - int ret; bool is_le_audio_device = false; uint8_t csis_rsi[6]; uint8_t chip_id[8]; diff --git a/src/bluetooth/bt_stream/bt_le_audio_tx/bt_le_audio_tx.c b/src/bluetooth/bt_stream/bt_le_audio_tx/bt_le_audio_tx.c index 41608e60..81550475 100644 --- a/src/bluetooth/bt_stream/bt_le_audio_tx/bt_le_audio_tx.c +++ b/src/bluetooth/bt_stream/bt_le_audio_tx/bt_le_audio_tx.c @@ -96,10 +96,6 @@ static int iso_stream_send(uint8_t const *const data, size_t size, struct bt_cap */ if (atomic_get(&tx_info->iso_tx_pool_alloc) >= HCI_ISO_BUF_PER_CHAN) { if (!tx_info->hci_wrn_printed) { - struct bt_iso_chan *iso_chan; - - iso_chan = bt_bap_stream_iso_chan_get(&cap_stream->bap_stream); - LOG_WRN("HCI ISO TX overrun on stream %p - Single print", (void *)&cap_stream->bap_stream); tx_info->hci_wrn_printed = true; From 47fc2a91f3b931745216d062a8c768fd16218f72 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 27 Jul 2026 16:43:09 +0200 Subject: [PATCH 45/77] fix(sca): Log discarded errors --- src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp | 3 +++ src/audio/streamctrl.c | 4 ++++ src/drivers/LED_Controller/KTD2026.cpp | 16 +++++++++++++++- src/modules/hw_codec_adau1860.cpp | 13 ++++++++++++- 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp index 9620a2eb..2f4f1cbe 100644 --- a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp +++ b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp @@ -43,6 +43,9 @@ void SDCardManager::unmount_work_handler(struct k_work *work) { if (!_inserted) { ret = sdcard_manager.unmount(); + if (ret != 0) { + LOG_ERR("Failed to unmount SD card: %d", ret); + } LOG_INF("SD card unmounted due to card removal."); ret = zbus_chan_pub(&sd_card_chan, &msg, K_FOREVER); diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 39788522..4105f895 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -639,6 +639,10 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st snprintf(sirk, BT_CSIP_SIRK_SIZE, "%08X", new_sirk); int err = bt_encrypt_le(sirk, res, res); + if (err) { + LOG_ERR("Failed to calculate CSIS hash: %d", err); + return; + } memcpy(out, res, BT_CSIP_CRYPTO_HASH_SIZE); diff --git a/src/drivers/LED_Controller/KTD2026.cpp b/src/drivers/LED_Controller/KTD2026.cpp index feb3ead9..15631f7d 100644 --- a/src/drivers/LED_Controller/KTD2026.cpp +++ b/src/drivers/LED_Controller/KTD2026.cpp @@ -95,10 +95,17 @@ void KTD2026::begin() { if (_active) return; - _active = true; + _active = true; ret = pm_device_runtime_get(ls_1_8); + if (ret) { + LOG_ERR("Failed to enable 1.8 V LED supply: %d", ret); + } + ret = pm_device_runtime_get(ls_3_3); + if (ret) { + LOG_ERR("Failed to enable 3.3 V LED supply: %d", ret); + } _i2c->begin(); @@ -121,7 +128,14 @@ void KTD2026::power_off() { (void)writeReg(registers::CTRL, &val, sizeof(val)); int ret = pm_device_runtime_put(ls_1_8); + if (ret) { + LOG_ERR("Failed to disable 1.8 V LED supply: %d", ret); + } + ret = pm_device_runtime_put(ls_3_3); + if (ret) { + LOG_ERR("Failed to disable 3.3 V LED supply: %d", ret); + } clearCachedColor(); diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 867c9c5e..2a3111f8 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -70,15 +70,26 @@ int hw_codec_set_audio_mode(enum audio_mode mode) { settings_save_one("audio/mode", &mode, sizeof(mode)); ret = dac.fdsp_bank_select((uint8_t) mode); + if (ret) { + LOG_ERR("Failed to select DSP bank, ret: %d", ret); + return ret; + } + // TODO: make writing to bank work k_msleep(200); ret = hw_codec_volume_adjust(0); + if (ret) { + LOG_ERR("Failed to adjust codec volume, ret: %d", ret); + return ret; + } + ret = dac.mute(muted); if (ret) { LOG_ERR("Failed to set audio mode, ret: %d", ret); return ret; } - return ret; + + return 0; } enum audio_mode hw_codec_get_audio_mode() { From 193ae766b4bcbefe0afd778fe93099f197ad6eee Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 13:56:54 +0200 Subject: [PATCH 46/77] fix(sca): Discard unused battery return values --- src/Battery/BQ25120a.cpp | 22 ++++---- src/Battery/BQ27220.cpp | 100 ++++++++++++++++------------------- src/Battery/PowerManager.cpp | 16 +++--- 3 files changed, 66 insertions(+), 72 deletions(-) diff --git a/src/Battery/BQ25120a.cpp b/src/Battery/BQ25120a.cpp index 183f390d..4e69173f 100644 --- a/src/Battery/BQ25120a.cpp +++ b/src/Battery/BQ25120a.cpp @@ -153,28 +153,28 @@ void BQ25120a::setup(const battery_settings &_battery_settings) { uint8_t BQ25120a::read_charging_state() { uint8_t status = 0; - bool ret = readReg(registers::CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::CTRL, (uint8_t *) &status, sizeof(status)); return status; } uint8_t BQ25120a::read_fault() { uint8_t status = 0; - bool ret = readReg(registers::FAULT, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::FAULT, (uint8_t *) &status, sizeof(status)); return status; } uint8_t BQ25120a::read_ts_fault() { uint8_t status = 0; - bool ret = readReg(registers::TS_FAULT, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::TS_FAULT, (uint8_t *) &status, sizeof(status)); return status; } chrg_state BQ25120a::read_charging_control() { uint8_t status = 0; - bool ret = readReg(registers::CHARGE_CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::CHARGE_CTRL, (uint8_t *) &status, sizeof(status)); chrg_state chrg; @@ -200,7 +200,7 @@ chrg_state BQ25120a::read_charging_control() { uint8_t BQ25120a::write_charging_control(float mA) { uint8_t status = 0; - bool ret = readReg(registers::CHARGE_CTRL, &status, sizeof(status)); + (void)readReg(registers::CHARGE_CTRL, &status, sizeof(status)); status &= 0x3; @@ -253,7 +253,7 @@ uint8_t BQ25120a::write_LDO_voltage_control(float volt) { float BQ25120a::read_ldo_voltage() { uint8_t status = 0; - bool ret = readReg(registers::LS_LDO_CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::LS_LDO_CTRL, (uint8_t *) &status, sizeof(status)); float voltage = 0.8f + ((status >> 2 & 0x1F)) * 0.1f; @@ -262,7 +262,7 @@ float BQ25120a::read_ldo_voltage() { float BQ25120a::read_battery_voltage_control() { uint8_t status = 0; - bool ret = readReg(registers::BAT_VOL_CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::BAT_VOL_CTRL, (uint8_t *) &status, sizeof(status)); float voltage = 3.6f + (status >> 1) * 0.01f; @@ -286,7 +286,7 @@ uint8_t BQ25120a::write_battery_voltage_control(float volt) { chrg_state BQ25120a::read_termination_control() { uint8_t status = 0; - bool ret = readReg(registers::TERM_CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::TERM_CTRL, (uint8_t *) &status, sizeof(status)); struct chrg_state chrg; @@ -337,7 +337,7 @@ ilim_uvlo BQ25120a::read_uvlo_ilim() { struct ilim_uvlo param; uint8_t status = 0; - bool ret = readReg(registers::ILIM_UVLO, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::ILIM_UVLO, (uint8_t *) &status, sizeof(status)); param.uvlo_v = CLAMP(3.0f- 0.2f * ((status & 0x7) - 2), 2.2, 3.0); param.lim_mA = 50.f + 50.f * ((status >> 3) & 0x7); @@ -402,7 +402,7 @@ button_state BQ25120a::read_button_state() { struct button_state btn; uint8_t status = 0; - bool ret = readReg(registers::BTN_CTRL, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::BTN_CTRL, (uint8_t *) &status, sizeof(status)); btn.wake_1 = status & 0x2; btn.wake_2 = status & 0x1; @@ -418,4 +418,4 @@ int BQ25120a::set_power_connect_callback(gpio_callback_handler_t handler) { int BQ25120a::set_int_callback(gpio_callback_handler_t handler) { gpio_init_callback(&int_cb_data, handler, int_cb_data.pin_mask | BIT(int_pin.pin)); return gpio_add_callback(int_pin.port, &int_cb_data); -} \ No newline at end of file +} diff --git a/src/Battery/BQ27220.cpp b/src/Battery/BQ27220.cpp index 715f22c1..cf21a764 100644 --- a/src/Battery/BQ27220.cpp +++ b/src/Battery/BQ27220.cpp @@ -96,7 +96,7 @@ void BQ27220::writeReg(uint8_t reg, uint8_t *buffer, uint16_t len) { bat_status BQ27220::battery_status() { bat_status status; uint16_t val = 0; - bool ret = readReg(registers::FLAGS, (uint8_t *) &val, sizeof(val)); + (void)readReg(registers::FLAGS, (uint8_t *) &val, sizeof(val)); status.DSG = val & 0x1; status.SYSDWN = val & (1 << 1); @@ -135,7 +135,7 @@ gauge_status BQ27220::gauging_state() { float BQ27220::temperature() { uint16_t temp_K = 0; - bool ret = readReg(registers::TEMP, (uint8_t *) &temp_K, sizeof(temp_K)); + (void)readReg(registers::TEMP, (uint8_t *) &temp_K, sizeof(temp_K)); float temp = temp_K / 10.0 - 273.15; return temp; @@ -143,7 +143,7 @@ float BQ27220::temperature() { float BQ27220::voltage() { uint16_t mV = 0; - bool ret = readReg(registers::VOLT, (uint8_t *) &mV, sizeof(mV)); + (void)readReg(registers::VOLT, (uint8_t *) &mV, sizeof(mV)); float v = mV / 1000.0; return v; @@ -151,74 +151,74 @@ float BQ27220::voltage() { float BQ27220::capacity() { uint16_t mAh = 0; - bool ret = readReg(registers::FCC, (uint8_t *) &mAh, sizeof(mAh)); + (void)readReg(registers::FCC, (uint8_t *) &mAh, sizeof(mAh)); return mAh; } float BQ27220::time_to_full() { uint16_t minutes = 0; - bool ret = readReg(registers::TTF, (uint8_t *) &minutes, sizeof(minutes)); + (void)readReg(registers::TTF, (uint8_t *) &minutes, sizeof(minutes)); return minutes; } float BQ27220::time_to_empty() { uint16_t minutes = 0; - bool ret = readReg(registers::TTE, (uint8_t *) &minutes, sizeof(minutes)); + (void)readReg(registers::TTE, (uint8_t *) &minutes, sizeof(minutes)); return minutes; } float BQ27220::state_of_charge() { uint16_t soc = 0; - bool ret = readReg(registers::SOC, (uint8_t *) &soc, sizeof(soc)); + (void)readReg(registers::SOC, (uint8_t *) &soc, sizeof(soc)); return soc; } float BQ27220::state_of_health() { uint16_t soc = 0; - bool ret = readReg(registers::SOH, (uint8_t *) &soc, sizeof(soc)); + (void)readReg(registers::SOH, (uint8_t *) &soc, sizeof(soc)); return soc; } float BQ27220::current() { int16_t mA = 0; - bool ret = readReg(registers::NAC, (uint8_t *) &mA, sizeof(mA)); + (void)readReg(registers::NAC, (uint8_t *) &mA, sizeof(mA)); return mA; } float BQ27220::average_current() { int16_t mA = 0; - bool ret = readReg(registers::AI, (uint8_t *) &mA, sizeof(mA)); + (void)readReg(registers::AI, (uint8_t *) &mA, sizeof(mA)); return mA; } float BQ27220::design_cap() { uint16_t mAh = 0; - bool ret = readReg(registers::DCAP, (uint8_t *) &mAh, sizeof(mAh)); + (void)readReg(registers::DCAP, (uint8_t *) &mAh, sizeof(mAh)); return mAh; } float BQ27220::remaining_cap() { uint16_t mAh = 0; - bool ret = readReg(registers::RM, (uint8_t *) &mAh, sizeof(mAh)); + (void)readReg(registers::RM, (uint8_t *) &mAh, sizeof(mAh)); return mAh; } float BQ27220::charge_current() { int16_t mA = 0; - bool ret = readReg(registers::CC, (uint8_t *) &mA, sizeof(mA)); + (void)readReg(registers::CC, (uint8_t *) &mA, sizeof(mA)); return mA; } int BQ27220::cycle_count() { uint16_t n_cycles = 0; - bool ret = readReg(registers::CYCT, (uint8_t *) &n_cycles, sizeof(n_cycles)); + (void)readReg(registers::CYCT, (uint8_t *) &n_cycles, sizeof(n_cycles)); return n_cycles; } float BQ27220::standby_current() { int16_t mA = 0; - bool ret = readReg(registers::SI, (uint8_t *) &mA, sizeof(mA)); + (void)readReg(registers::SI, (uint8_t *) &mA, sizeof(mA)); return mA; } @@ -226,7 +226,7 @@ float BQ27220::standby_current() { op_state BQ27220::operation_state() { op_state state; uint16_t status = 0; - bool ret = readReg(registers::OP_STAT, (uint8_t *) &status, sizeof(status)); + (void)readReg(registers::OP_STAT, (uint8_t *) &status, sizeof(status)); state.CALD = status & 0x01; state.SEC = (status >> 1) & 0x3; @@ -302,11 +302,9 @@ void BQ27220::exit_config_update(bool init) { } void BQ27220::read_RAM(uint16_t ram_address, uint8_t * data, int len) { - bool ret; - writeReg(0x3E, (uint8_t *) &ram_address, sizeof(ram_address)); k_usleep(BQ27220_RAM_TIMEOUT_US); - ret = readReg(0x40, data, len); + (void)readReg(0x40, data, len); } int BQ27220::write_RAM(uint16_t ram_address, uint8_t * data, int len, bool check) { @@ -314,15 +312,13 @@ int BQ27220::write_RAM(uint16_t ram_address, uint8_t * data, int len, bool check uint8_t data_len=0; uint8_t buf[len]; - bool ret; - writeReg(0x3E, (uint8_t *) &ram_address, sizeof(ram_address)); k_usleep(BQ27220_RAM_TIMEOUT_US); - ret = readReg(0x61, (uint8_t *) &data_len, sizeof(data_len)); - ret = readReg(0x40, buf, len); - ret = readReg(0x60, (uint8_t *) &check_sum, sizeof(check_sum)); + (void)readReg(0x61, (uint8_t *) &data_len, sizeof(data_len)); + (void)readReg(0x40, buf, len); + (void)readReg(0x60, (uint8_t *) &check_sum, sizeof(check_sum)); uint8_t my_check = (uint8_t)0xFF-check_sum; // - data[0] - data[1]; @@ -367,8 +363,6 @@ int BQ27220::write_RAM(uint16_t ram_address, uint16_t val, bool check) { } void BQ27220::setup(const battery_settings &_battery_settings, bool init) { - int ret; - // unseal write_command(0x0414); k_msleep(100); @@ -385,29 +379,29 @@ void BQ27220::setup(const battery_settings &_battery_settings, bool init) { //ret = write_RAM(0x9220, 0); // design and full charge capacity - ret = write_RAM(0x929F, _battery_settings.capacity); - ret = write_RAM(0x929D, _battery_settings.capacity); //130 + (void)write_RAM(0x929F, _battery_settings.capacity); + (void)write_RAM(0x929D, _battery_settings.capacity); //130 // near full - ret = write_RAM(0x926B, 5); + (void)write_RAM(0x926B, 5); - ret = write_RAM(0x91F5, _battery_settings.temp_min * 10); - ret = write_RAM(0x91F7, _battery_settings.temp_max * 10); + (void)write_RAM(0x91F5, _battery_settings.temp_min * 10); + (void)write_RAM(0x91F7, _battery_settings.temp_max * 10); // charge current - ret = write_RAM(0x91FB, _battery_settings.i_charge); + (void)write_RAM(0x91FB, _battery_settings.i_charge); // charge voltage - ret = write_RAM(0x91FD, _battery_settings.u_term * 1000); + (void)write_RAM(0x91FD, _battery_settings.u_term * 1000); // taper current - ret = write_RAM(0x9201, _battery_settings.i_term); + (void)write_RAM(0x9201, _battery_settings.i_term); // experimental: min taper capacity - ret = write_RAM(0x9203, 4); // standard: 25 + (void)write_RAM(0x9203, 4); // standard: 25 // deadband uint8_t val = 1; - ret = write_RAM(0x91DE, &val, sizeof(uint8_t)); + (void)write_RAM(0x91DE, &val, sizeof(uint8_t)); // deadband CC (verursacht Probleme, rm zählt zu schnell?) /*val = 5; @@ -415,14 +409,14 @@ void BQ27220::setup(const battery_settings &_battery_settings, bool init) { */ // sleep current - ret = write_RAM(0x9217, 1); + (void)write_RAM(0x9217, 1); // dischage current trd - ret = write_RAM(0x9228, 2); + (void)write_RAM(0x9228, 2); // charge current trd - ret = write_RAM(0x922A, 2); + (void)write_RAM(0x922A, 2); // quit current - ret = write_RAM(0x922C, 1); + (void)write_RAM(0x922C, 1); //dod 0%: 4287 //dod 10%: 4125 @@ -444,29 +438,29 @@ void BQ27220::setup(const battery_settings &_battery_settings, bool init) { //dod: 103.25%: 3089 // sysDown set Voltage - ret = write_RAM(0x9240, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_SYSDOWN_SET_OFFSET); + (void)write_RAM(0x9240, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_SYSDOWN_SET_OFFSET); // sysDown clear Voltage - ret = write_RAM(0x9243, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_SYSDOWN_SET_OFFSET + CONFIG_BATTERY_SYSDOWN_HYSTERESIS); + (void)write_RAM(0x9243, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_SYSDOWN_SET_OFFSET + CONFIG_BATTERY_SYSDOWN_HYSTERESIS); // FD set - ret = write_RAM(0x9282, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_FD_SET_OFFSET); + (void)write_RAM(0x9282, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_FD_SET_OFFSET); // FD clear - ret = write_RAM(0x9284, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_FD_SET_OFFSET + CONFIG_BATTERY_FD_HYSTERESIS); + (void)write_RAM(0x9284, _battery_settings.u_vlo * 1000 + CONFIG_BATTERY_FD_SET_OFFSET + CONFIG_BATTERY_FD_HYSTERESIS); // FC Voltage - ret = write_RAM(0x9288, _battery_settings.u_term * 1000 - CONFIG_BATTERY_FC_VOLTAGE_OFFSET); + (void)write_RAM(0x9288, _battery_settings.u_term * 1000 - CONFIG_BATTERY_FC_VOLTAGE_OFFSET); // Electonic Load in 3µA steps - ret = write_RAM(0x9269, 6); // 18 µA + (void)write_RAM(0x9269, 6); // 18 µA // EMF //write_RAM(0x92A7, 36001); //C0 - ret = write_RAM(0x92A9, 480); //bat1:250 + (void)write_RAM(0x92A9, 480); //bat1:250 //R0 - ret = write_RAM(0x92AB, 19941); //bat1: 19941 //22542 //new bat: 17340 + (void)write_RAM(0x92AB, 19941); //bat1: 19941 //22542 //new bat: 17340 //R1 //write_RAM(0x92AF, 3160); @@ -478,16 +472,16 @@ void BQ27220::setup(const battery_settings &_battery_settings, bool init) { // do not use, only on CT makes sense: // SOC Flag, enable FC voltage detection uint8_t flags_b = 0x8C; - ret = write_RAM(0x9281, &flags_b, sizeof(flags_b)); + (void)write_RAM(0x9281, &flags_b, sizeof(flags_b)); // Overload current - ret = write_RAM(0x9264, _battery_settings.i_max); + (void)write_RAM(0x9264, _battery_settings.i_max); // CEDV Smoothing Config uint8_t cedv_conf = 0x0D; //Default: 0x08, Enable SMEXT, SMEN 0x0D - ret = write_RAM(0x9271, &cedv_conf, sizeof(cedv_conf)); + (void)write_RAM(0x9271, &cedv_conf, sizeof(cedv_conf)); - ret = write_RAM(0x9272, 3700); + (void)write_RAM(0x9272, 3700); exit_config_update(init); @@ -498,4 +492,4 @@ void BQ27220::setup(const battery_settings &_battery_settings, bool init) { int BQ27220::set_int_callback(gpio_callback_handler_t handler) { gpio_init_callback(&int_cb_data, handler, int_cb_data.pin_mask | BIT(gpout_pin.pin)); return gpio_add_callback(gpout_pin.port, &int_cb_data); -} \ No newline at end of file +} diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index 09652680..d65826fe 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -543,7 +543,7 @@ void bt_disconnect_handler(struct bt_conn *conn, void * data) { if (ret != 0) return; if (info.state == BT_CONN_STATE_CONNECTED) { - ret = bt_mgmt_conn_disconnect(conn, *((uint8_t*)data)); + (void)bt_mgmt_conn_disconnect(conn, *((uint8_t*)data)); } } @@ -554,7 +554,7 @@ void PowerManager::reboot() { uint8_t data = BT_HCI_ERR_REMOTE_USER_TERM_CONN; bt_conn_foreach(BT_CONN_TYPE_ALL, bt_disconnect_handler, &data); - ret = bt_le_adv_stop(); + (void)bt_le_adv_stop(); stop_sensor_manager(); @@ -573,7 +573,7 @@ int PowerManager::power_down(bool fault) { uint8_t data = BT_HCI_ERR_REMOTE_USER_TERM_CONN; bt_conn_foreach(BT_CONN_TYPE_ALL, bt_disconnect_handler, &data); - ret = bt_le_adv_stop(); + (void)bt_le_adv_stop(); // power disonnected // prepare interrupts @@ -614,7 +614,7 @@ int PowerManager::power_down(bool fault) { } LOG_PANIC(); - ret = bt_mgmt_stop_watchdog(); + (void)bt_mgmt_stop_watchdog(); //ERR_CHK(ret); dac.end(); @@ -631,10 +631,10 @@ int PowerManager::power_down(bool fault) { return 0; } - ret = pm_device_action_run(ls_sd, PM_DEVICE_ACTION_SUSPEND); - ret = pm_device_action_run(ls_3_3, PM_DEVICE_ACTION_SUSPEND); - ret = pm_device_action_run(ls_1_8, PM_DEVICE_ACTION_SUSPEND); - ret = pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); + (void)pm_device_action_run(ls_sd, PM_DEVICE_ACTION_SUSPEND); + (void)pm_device_action_run(ls_3_3, PM_DEVICE_ACTION_SUSPEND); + (void)pm_device_action_run(ls_1_8, PM_DEVICE_ACTION_SUSPEND); + (void)pm_device_action_run(cons, PM_DEVICE_ACTION_SUSPEND); /*const struct device *const i2c = DEVICE_DT_GET(DT_NODELABEL(i2c1)); ret = pm_device_action_run(i2c, PM_DEVICE_ACTION_SUSPEND); From 12f1bca787ba482b98b3582445e3bdd47d918988 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 13:56:59 +0200 Subject: [PATCH 47/77] fix(sca): Discard unused subsystem return values --- src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp | 14 ++++++-------- src/SensorManager/MAXM86161/MAXM86161.cpp | 4 ++-- src/SensorManager/SensorManager.cpp | 2 +- src/audio/audio_system.c | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp index 2f4f1cbe..3a7dec7e 100644 --- a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp +++ b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp @@ -111,13 +111,11 @@ int SDCardManager::aquire_ls() { } int SDCardManager::release_ls() { - int ret; - if (!ls_aquired) return -EALREADY; - ret = pm_device_runtime_put(ls_1_8); - ret = pm_device_runtime_put(ls_3_3); - ret = pm_device_runtime_put(ls_sd); + (void)pm_device_runtime_put(ls_1_8); + (void)pm_device_runtime_put(ls_3_3); + (void)pm_device_runtime_put(ls_sd); ls_aquired = false; @@ -128,7 +126,7 @@ void SDCardManager::init() { int ret; if (!device_is_ready(sd_state_pin.port)) { - ret = aquire_ls(); + (void)aquire_ls(); LOG_ERR("SD state GPIO device not ready\n"); return; } @@ -178,7 +176,7 @@ int SDCardManager::mount() { uint32_t sector_count; size_t sector_size; - ret = aquire_ls(); + (void)aquire_ls(); bool _sd_inserted = sd_inserted(); @@ -678,4 +676,4 @@ int SDCardManager::sync() { return 0; } -SDCardManager sdcard_manager; \ No newline at end of file +SDCardManager sdcard_manager; diff --git a/src/SensorManager/MAXM86161/MAXM86161.cpp b/src/SensorManager/MAXM86161/MAXM86161.cpp index 7a199066..8774d01f 100644 --- a/src/SensorManager/MAXM86161/MAXM86161.cpp +++ b/src/SensorManager/MAXM86161/MAXM86161.cpp @@ -146,7 +146,7 @@ int MAXM86161::read(ppg_sample * buffer) { if (status == 0){ number_of_bytes = num_samples / LED_NUM * LED_NUM * BYTES_PER_CH; - status = _read_block(REG_FIFO_DATA, number_of_bytes, (uint8_t *) databuffer); + (void)_read_block(REG_FIFO_DATA, number_of_bytes, (uint8_t *) databuffer); for (int i=0; i < num_samples / LED_NUM * LED_NUM; i++) { int idx = BYTES_PER_CH * i; @@ -436,7 +436,7 @@ int MAXM86161::_clear_interrupt(void) int MAXM86161::read_interrupt_state(int &value) { int status; - status = _read_from_reg(REG_IRQ_STATUS2, value); + (void)_read_from_reg(REG_IRQ_STATUS2, value); status = _read_from_reg(REG_IRQ_STATUS1, value); return status; } diff --git a/src/SensorManager/SensorManager.cpp b/src/SensorManager/SensorManager.cpp index 60f5e3cd..40ff9775 100644 --- a/src/SensorManager/SensorManager.cpp +++ b/src/SensorManager/SensorManager.cpp @@ -74,7 +74,7 @@ void sensor_chan_update(void *p1, void *p2, void *p3) { int ret; while (1) { - ret = k_poll(&sensor_manager_evt, 1, K_FOREVER); + (void)k_poll(&sensor_manager_evt, 1, K_FOREVER); k_msgq_get(&sensor_queue, &msg, K_FOREVER); diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index 51048694..04a83027 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -138,7 +138,7 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) while (1) { /* Don't start encoding until the stream needing it has started */ - ret = k_poll(&encoder_evt, 1, K_FOREVER); + (void)k_poll(&encoder_evt, 1, K_FOREVER); /* Get PCM data from I2S */ /* Since one audio frame is divided into a number of From 9d7915b484023f3a483e0abca8baeb0af7596cd7 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 13:57:02 +0200 Subject: [PATCH 48/77] fix(sca): Avoid overwritten sensor results --- src/SensorManager/BMP388/Adafruit_BMP3XX.cpp | 8 +++++--- src/SensorManager/BMX160/DFRobot_BMX160.cpp | 10 +--------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp b/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp index 95748ee2..7775948a 100644 --- a/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp +++ b/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp @@ -112,9 +112,11 @@ bool Adafruit_BMP3XX::_init(void) { if (rslt != BMP3_OK) return false; - rslt = bmp3_init(&the_sensor); #ifdef BMP3XX_DEBUG - printk("Init result: %i\n", rslt); + const int8_t init_rslt = bmp3_init(&the_sensor); + printk("Init result: %i\n", init_rslt); +#else + (void)bmp3_init(&the_sensor); #endif rslt = validate_trimming_param(&the_sensor); @@ -478,4 +480,4 @@ static int8_t cal_crc(uint8_t seed, uint8_t data) { } return (int8_t)seed; -} \ No newline at end of file +} diff --git a/src/SensorManager/BMX160/DFRobot_BMX160.cpp b/src/SensorManager/BMX160/DFRobot_BMX160.cpp index ace06844..659b38a2 100644 --- a/src/SensorManager/BMX160/DFRobot_BMX160.cpp +++ b/src/SensorManager/BMX160/DFRobot_BMX160.cpp @@ -92,15 +92,7 @@ void DFRobot_BMX160::wakeUp(){ bool DFRobot_BMX160::softReset() { - int8_t rslt=BMX160_OK; - if (Obmx160 == NULL){ - rslt = BMX160_E_NULL_PTR; - } - rslt = _softReset(Obmx160); - if (rslt == 0) - return true; - else - return false; + return _softReset(Obmx160) == BMX160_OK; } int8_t DFRobot_BMX160:: _softReset(sBmx160Dev_t *dev) From 64ca770e5980a65906a005058119f48c9cc81233 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 13:57:07 +0200 Subject: [PATCH 49/77] fix(sca): Guard conditional scan parameters --- src/bluetooth/bt_management/scanning/bt_mgmt_scan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c b/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c index 5466c1b0..e8f10d16 100644 --- a/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c +++ b/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c @@ -55,9 +55,13 @@ int bt_mgmt_scan_start(uint16_t scan_intvl, uint16_t scan_win, enum bt_mgmt_scan scan_window = scan_win; } - struct bt_le_scan_param *scan_param = + struct bt_le_scan_param *scan_param; + +#if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_BAP_BROADCAST_SINK) + scan_param = BT_LE_SCAN_PARAM(NRF5340_AUDIO_GATEWAY_SCAN_TYPE, BT_LE_SCAN_OPT_FILTER_DUPLICATE, scan_interval, scan_window); +#endif if (type == BT_MGMT_SCAN_TYPE_CONN && IS_ENABLED(CONFIG_BT_CENTRAL)) { ret = bt_mgmt_scan_for_conn_start(scan_param, srch_name); From 59be7c4c5be0674324f953d7ecfe1797924e4251 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 15:32:58 +0200 Subject: [PATCH 50/77] fix(audio): Avoid null TX buffer dereference Reuse the released TX buffer when no alternative buffer is available so tx_buf cannot remain null before memset(). --- src/audio/audio_datapath.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 13a14472..4941e610 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -1130,7 +1130,13 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t * use alternative buffers */ ret = alt_buffer_get((void **)&tx_buf); - ERR_CHK(ret); + if (ret) { + LOG_DBG("No alternative I2S TX buffer available; reusing released buffer"); + /* I2S no longer owns this buffer; recycle it as silence + * instead of leaving tx_buf NULL. + */ + tx_buf = (uint8_t *)tx_buf_released; + } memset(tx_buf, 0, BLK_STEREO_SIZE_OCTETS); } From 33bd54376bf740ea02768594e2aa43b504171170 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 15:33:23 +0200 Subject: [PATCH 51/77] fix(bluetooth): Ignore missing CSIS RSI data --- src/audio/streamctrl.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 4105f895..a31c5503 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -558,6 +558,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); bool is_le_audio_device = false; + bool csis_rsi_found = false; uint8_t csis_rsi[6]; uint8_t chip_id[8]; @@ -587,8 +588,14 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st memcpy(chip_id, data, sizeof(chip_id)); } + /* A valid RSI contains a 3-byte hash followed by a 3-byte random value. */ if (type == BT_DATA_CSIS_RSI) { - memcpy(csis_rsi, data, sizeof(csis_rsi)); + if ((len - 1) >= sizeof(csis_rsi)) { + memcpy(csis_rsi, data, sizeof(csis_rsi)); + csis_rsi_found = true; + } else { + LOG_DBG("Ignoring short CSIS RSI (%u bytes)", (unsigned int)(len - 1)); + } } // channel @@ -600,7 +607,6 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st if (is_le_audio_device) { LOG_INF("Found LE-Audio device!"); - uint32_t hash_ref = (csis_rsi[2] << 16) | (csis_rsi[1] << 8) | csis_rsi[0]; uint32_t hash; uint32_t new_sirk = *((uint32_t *) chip_id) ^ oe_boot_state.device_id; @@ -620,6 +626,14 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st write_sirk(new_sirk); } else if (channel == AUDIO_CH_R) { + /* Only right-channel matching validates the advertised RSI. */ + if (!csis_rsi_found) { + LOG_DBG("Ignoring right-channel LE Audio device without CSIS RSI"); + return; + } + + uint32_t hash_ref = + (csis_rsi[2] << 16) | (csis_rsi[1] << 8) | csis_rsi[0]; uint8_t res[BT_CSIP_PADDED_RAND_SIZE]; uint8_t sirk[BT_CSIP_SIRK_SIZE + 1]; From bb254b8fe2b9262f8f47866c91b47c8add13c063 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 15:33:52 +0200 Subject: [PATCH 52/77] fix(bluetooth): Ignore missing peer chip IDs --- src/audio/streamctrl.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index a31c5503..7b013edf 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -559,6 +559,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st bool is_le_audio_device = false; bool csis_rsi_found = false; + bool chip_id_found = false; uint8_t csis_rsi[6]; uint8_t chip_id[8]; @@ -584,8 +585,13 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st } if (is_le_audio_device && type == BT_DATA_MANUFACTURER_DATA) { - memset(chip_id, 0, sizeof(chip_id)); - memcpy(chip_id, data, sizeof(chip_id)); + if ((len - 1) >= sizeof(chip_id)) { + memcpy(chip_id, data, sizeof(chip_id)); + chip_id_found = true; + } else { + LOG_DBG("Ignoring short manufacturer chip ID (%u bytes)", + (unsigned int)(len - 1)); + } } /* A valid RSI contains a 3-byte hash followed by a 3-byte random value. */ @@ -607,9 +613,18 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st if (is_le_audio_device) { LOG_INF("Found LE-Audio device!"); + /* Do not derive or persist a SIRK without a complete peer identity. */ + if (!chip_id_found) { + LOG_DBG("Ignoring LE Audio device without manufacturer chip ID"); + return; + } + uint32_t hash; + uint32_t peer_device_id; - uint32_t new_sirk = *((uint32_t *) chip_id) ^ oe_boot_state.device_id; + /* Advertisement storage may not be aligned for a uint32_t access. */ + memcpy(&peer_device_id, chip_id, sizeof(peer_device_id)); + uint32_t new_sirk = peer_device_id ^ oe_boot_state.device_id; enum audio_channel channel; @@ -618,7 +633,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st if (channel == AUDIO_CH_L) { LOG_INF("Device ID 1: %016X", oe_boot_state.device_id); - LOG_INF("Device ID 2: %016X", *((uint32_t *) chip_id)); + LOG_INF("Device ID 2: %016X", peer_device_id); LOG_INF("New Sirk: %016X", new_sirk); //TODO: check if the device wants to pair (sirk == device_id) From 404867ab6aa73241c640e8977f3ca4680a6051a9 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 15:34:26 +0200 Subject: [PATCH 53/77] fix(audio): Reject incomplete stereo encoder setup Validate the stereo channel count before consuming both channel pointers. If encoding fails, drop the frame instead of entering the fatal error path, log once until recovery, and never send stale encoded data. --- src/audio/audio_system.c | 14 ++++++++++++-- src/audio/sw_codec_select.c | 9 +++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index 04a83027..b8eb7374 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -135,6 +135,7 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) static uint8_t *encoded_data; //static size_t pcm_block_size; static uint32_t test_tone_finite_pos; + static bool encode_failed; while (1) { /* Don't start encoding until the stream needing it has started */ @@ -186,8 +187,17 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) ret = sw_codec_encode(pcm_raw_data, FRAME_SIZE_BYTES, &encoded_data, &encoded_data_size); - - ERR_CHK_MSG(ret, "Encode failed"); + if (ret) { + if (!encode_failed) { + LOG_WRN("Audio encode failed; dropping frames until recovery: %d", ret); + } + encode_failed = true; + continue; + } + if (encode_failed) { + LOG_INF("Audio encoder recovered"); + encode_failed = false; + } } /* Print block usage */ diff --git a/src/audio/sw_codec_select.c b/src/audio/sw_codec_select.c index f1564206..96544a1c 100644 --- a/src/audio/sw_codec_select.c +++ b/src/audio/sw_codec_select.c @@ -163,6 +163,15 @@ int sw_codec_encode(void *pcm_data, size_t pcm_size, uint8_t **encoded_data, siz break; } case SW_CODEC_STEREO: { + /* Stereo encoding consumes both channel pointers below. Reject mismatched + * configurations so the caller can drop the frame safely. + */ + if (m_config.encoder.num_ch != AUDIO_CH_NUM) { + LOG_DBG("Rejecting stereo encode with %u configured channels", + (unsigned int)m_config.encoder.num_ch); + return -EINVAL; + } + for (int i = 0; i < m_config.encoder.num_ch; ++i) { ret = sw_codec_sample_rate_convert( &encoder_converters[i], CONFIG_AUDIO_SAMPLE_RATE_HZ, From 9c78c22e0590543bc5a1cd359076668acbdb9941 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 15:34:43 +0200 Subject: [PATCH 54/77] fix(audio): Initialize silence-frame decode inputs --- src/audio/sw_codec_select.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/audio/sw_codec_select.c b/src/audio/sw_codec_select.c index 96544a1c..8f5de1af 100644 --- a/src/audio/sw_codec_select.c +++ b/src/audio/sw_codec_select.c @@ -247,13 +247,19 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool switch (m_config.sw_codec) { case SW_CODEC_LC3: { #if (CONFIG_SW_CODEC_LC3) - char *pcm_in_data_ptrs[m_config.decoder.channel_mode]; + /* PLC override bypasses sample-rate conversion, so its branches assign these + * fixed channel slots directly to zero-filled PCM buffers. + */ + char *pcm_in_data_ptrs[AUDIO_CH_NUM] = {0}; switch (m_config.decoder.channel_mode) { case SW_CODEC_MONO: { if (bad_frame && IS_ENABLED(CONFIG_SW_CODEC_OVERRIDE_PLC)) { memset(decoded_data_mono[AUDIO_CH_L], 0, PCM_NUM_BYTES_MONO); decoded_data_size = PCM_NUM_BYTES_MONO; + pcm_in_data_ptrs[AUDIO_CH_L] = decoded_data_mono[AUDIO_CH_L]; + pcm_size_mono = decoded_data_size; + LOG_DBG("Replacing bad mono frame with silence"); } else { ret = sw_codec_lc3_dec_run( encoded_data, encoded_size, LC3_PCM_NUM_BYTES_MONO, 0, @@ -293,6 +299,10 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool memset(decoded_data_mono[AUDIO_CH_L], 0, PCM_NUM_BYTES_MONO); memset(decoded_data_mono[AUDIO_CH_R], 0, PCM_NUM_BYTES_MONO); decoded_data_size = PCM_NUM_BYTES_MONO; + pcm_in_data_ptrs[AUDIO_CH_L] = decoded_data_mono[AUDIO_CH_L]; + pcm_in_data_ptrs[AUDIO_CH_R] = decoded_data_mono[AUDIO_CH_R]; + pcm_size_mono = decoded_data_size; + LOG_DBG("Replacing bad stereo frame with silence"); } else { /* Decode left channel */ ret = sw_codec_lc3_dec_run( @@ -313,7 +323,8 @@ int sw_codec_decode(uint8_t const *const encoded_data, size_t encoded_size, bool return ret; } - for (int i = 0; i < m_config.decoder.channel_mode; ++i) { + /* Stereo always initializes both slots consumed by pscm_combine(). */ + for (int i = 0; i < AUDIO_CH_NUM; ++i) { ret = sw_codec_sample_rate_convert( &decoder_converters[i], m_config.decoder.sample_rate_hz, From 5bb04880f890449cf9c3f30690dbb9ea50f15d4d Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 19:06:30 +0200 Subject: [PATCH 55/77] fix(bluetooth): clarify guarded scan data Initialize the conditionally compiled scan parameter to NULL and describe the chip-ID copy in terms of its actual alignment and aliasing constraints. --- src/audio/streamctrl.c | 2 +- src/bluetooth/bt_management/scanning/bt_mgmt_scan.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 7b013edf..488f16bf 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -622,7 +622,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st uint32_t hash; uint32_t peer_device_id; - /* Advertisement storage may not be aligned for a uint32_t access. */ + /* Copy from the byte array without alignment or aliasing assumptions. */ memcpy(&peer_device_id, chip_id, sizeof(peer_device_id)); uint32_t new_sirk = peer_device_id ^ oe_boot_state.device_id; diff --git a/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c b/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c index e8f10d16..94cd2567 100644 --- a/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c +++ b/src/bluetooth/bt_management/scanning/bt_mgmt_scan.c @@ -55,7 +55,7 @@ int bt_mgmt_scan_start(uint16_t scan_intvl, uint16_t scan_win, enum bt_mgmt_scan scan_window = scan_win; } - struct bt_le_scan_param *scan_param; + struct bt_le_scan_param *scan_param = NULL; #if defined(CONFIG_BT_CENTRAL) || defined(CONFIG_BT_BAP_BROADCAST_SINK) scan_param = From 808d10655a8b356a01de05a1271f8851c91925e9 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 19:06:34 +0200 Subject: [PATCH 56/77] fix(audio): persist codec mode after apply Continue through volume and mute restoration when a codec step fails, return the first error, and only persist the requested mode after all hardware operations succeed. --- src/modules/hw_codec_adau1860.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/modules/hw_codec_adau1860.cpp b/src/modules/hw_codec_adau1860.cpp index 2a3111f8..f69668d6 100644 --- a/src/modules/hw_codec_adau1860.cpp +++ b/src/modules/hw_codec_adau1860.cpp @@ -63,16 +63,13 @@ static int settings_set_cb(const char *name, size_t len, settings_read_cb read_c SETTINGS_STATIC_HANDLER_DEFINE(audio, "audio", NULL, settings_set_cb, NULL, NULL); int hw_codec_set_audio_mode(enum audio_mode mode) { - int ret; - - audio_mode = mode; - - settings_save_one("audio/mode", &mode, sizeof(mode)); + int first_error = 0; + int ret; ret = dac.fdsp_bank_select((uint8_t) mode); if (ret) { LOG_ERR("Failed to select DSP bank, ret: %d", ret); - return ret; + first_error = ret; } // TODO: make writing to bank work @@ -80,12 +77,28 @@ int hw_codec_set_audio_mode(enum audio_mode mode) { ret = hw_codec_volume_adjust(0); if (ret) { LOG_ERR("Failed to adjust codec volume, ret: %d", ret); - return ret; + if (!first_error) { + first_error = ret; + } } ret = dac.mute(muted); if (ret) { LOG_ERR("Failed to set audio mode, ret: %d", ret); + if (!first_error) { + first_error = ret; + } + } + + if (first_error) { + /* Keep the persisted mode unchanged when the codec could not fully apply it. */ + return first_error; + } + + audio_mode = mode; + ret = settings_save_one("audio/mode", &mode, sizeof(mode)); + if (ret) { + LOG_ERR("Failed to persist audio mode, ret: %d", ret); return ret; } From 37bd93f97155acf4b84c72f6c56027356fe5988a Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 1 Sep 2026 11:24:17 +0200 Subject: [PATCH 57/77] fix(audio): fail on persistent audio encode errors --- src/audio/audio_system.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index b8eb7374..a7ad69f4 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -34,6 +34,7 @@ LOG_MODULE_REGISTER(audio_system, CONFIG_AUDIO_SYSTEM_LOG_LEVEL); #define FIFO_RX_BLOCK_COUNT (CONFIG_FIFO_FRAME_SPLIT_NUM * CONFIG_FIFO_RX_FRAME_COUNT) #define DEBUG_INTERVAL_NUM 1000 +#define ENCODE_FAILURE_FATAL_THRESHOLD 100U #define TEST_TONE_BASE_FREQ_HZ 1000 K_THREAD_STACK_DEFINE(encoder_thread_stack, CONFIG_ENCODER_STACK_SIZE); @@ -135,7 +136,8 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) static uint8_t *encoded_data; //static size_t pcm_block_size; static uint32_t test_tone_finite_pos; - static bool encode_failed; + static uint32_t encode_drop_count; + static uint32_t consecutive_encode_failures; while (1) { /* Don't start encoding until the stream needing it has started */ @@ -188,15 +190,28 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) ret = sw_codec_encode(pcm_raw_data, FRAME_SIZE_BYTES, &encoded_data, &encoded_data_size); if (ret) { - if (!encode_failed) { - LOG_WRN("Audio encode failed; dropping frames until recovery: %d", ret); + encode_drop_count++; + consecutive_encode_failures++; + + if (consecutive_encode_failures == 1U || + (consecutive_encode_failures % ENCODE_FAILURE_FATAL_THRESHOLD) == 0U) { + LOG_WRN("Audio encode failed: ret=%d, dropped=%u, consecutive=%u", + ret, (unsigned int)encode_drop_count, + (unsigned int)consecutive_encode_failures); } - encode_failed = true; + + if (ret == -EINVAL || ret == -ENXIO || ret == -EPERM || + consecutive_encode_failures >= ENCODE_FAILURE_FATAL_THRESHOLD) { + ERR_CHK_MSG(ret, "Persistent/non-recoverable audio encode failure"); + } + continue; } - if (encode_failed) { - LOG_INF("Audio encoder recovered"); - encode_failed = false; + + if (consecutive_encode_failures) { + LOG_INF("Audio encoder recovered after %u dropped frame(s)", + (unsigned int)consecutive_encode_failures); + consecutive_encode_failures = 0U; } } From 24ba3abcd4f81546acde4df620db65c9ee879ec9 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 1 Sep 2026 12:11:14 +0200 Subject: [PATCH 58/77] fix(audio): warn when reusing released TX buffer Track the rare I2S underrun fallback where no alternate TX buffer is available and the released TX buffer is reused as silence. Log the first occurrence and then rate-limit later warnings so this recovery path does not fail silently during testing. --- src/audio/audio_datapath.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 4941e610..20fb6c52 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -1085,6 +1085,7 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t { int ret; static bool underrun_condition; + static uint32_t released_tx_reuse_count; alt_buffer_free(tx_buf_released); @@ -1131,7 +1132,13 @@ static void audio_datapath_i2s_blk_complete(uint32_t frame_start_ts_us, uint32_t */ ret = alt_buffer_get((void **)&tx_buf); if (ret) { - LOG_DBG("No alternative I2S TX buffer available; reusing released buffer"); + released_tx_reuse_count++; + if (released_tx_reuse_count == 1U || + (released_tx_reuse_count % UNDERRUN_LOG_INTERVAL_BLKS) == 0U) { + LOG_WRN("No alternative I2S TX buffer available; " + "reusing released buffer as silence, total: %u", + (unsigned int)released_tx_reuse_count); + } /* I2S no longer owns this buffer; recycle it as silence * instead of leaving tx_buf NULL. */ From e92ceccca6dabb1dfa0377c6e8a46f73a2d239e2 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 1 Sep 2026 12:49:18 +0200 Subject: [PATCH 59/77] fix(audio): avoid odd-length BLE service-data read --- src/audio/streamctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 488f16bf..403982cb 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -576,7 +576,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st // Suchen nach 16-bit Service UUIDs (LE Audio Services) if (type == BT_DATA_SVC_DATA16) { - for (size_t i = 0; i < len - 1; i += 2) { + for (size_t i = 0; i + 1U < (size_t)(len - 1); i += 2) { uint16_t uuid = (data[i + 1] << 8) | data[i]; if (uuid == BT_UUID_CAS_VAL) { is_le_audio_device = true; From 86c394beb4e4939da211506161ddc85ffd4512e2 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 1 Sep 2026 14:06:26 +0200 Subject: [PATCH 60/77] fix(bluetooth): report audio mode switch failures --- src/bluetooth/gatt_services/audio_config_service.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index c4f4bc4b..20931d8d 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -21,7 +21,11 @@ static ssize_t write_audio_mode(struct bt_conn *conn, const struct bt_gatt_attr return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED); } - hw_codec_set_audio_mode((enum audio_mode)mode); + int ret = hw_codec_set_audio_mode((enum audio_mode)mode); + if (ret) { + return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + } + return len; } From f6b7eb4528f61956f095dbc94cc71a213ecf1ebe Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Thu, 3 Sep 2026 15:06:47 +0200 Subject: [PATCH 61/77] fix(audio): treat unavailable codec errors as fatal --- src/audio/audio_system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index a7ad69f4..b2b3d093 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -201,6 +201,7 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) } if (ret == -EINVAL || ret == -ENXIO || ret == -EPERM || + ret == -ENODEV || ret == -ENOTSUP || consecutive_encode_failures >= ENCODE_FAILURE_FATAL_THRESHOLD) { ERR_CHK_MSG(ret, "Persistent/non-recoverable audio encode failure"); } From 6b338ef01fb95f1bf7e7a66a01931f72edec4b1d Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 10:40:20 +0200 Subject: [PATCH 62/77] Revert "fix(audio): fail on persistent audio encode errors" --- src/audio/audio_system.c | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index b2b3d093..31b49528 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -34,7 +34,6 @@ LOG_MODULE_REGISTER(audio_system, CONFIG_AUDIO_SYSTEM_LOG_LEVEL); #define FIFO_RX_BLOCK_COUNT (CONFIG_FIFO_FRAME_SPLIT_NUM * CONFIG_FIFO_RX_FRAME_COUNT) #define DEBUG_INTERVAL_NUM 1000 -#define ENCODE_FAILURE_FATAL_THRESHOLD 100U #define TEST_TONE_BASE_FREQ_HZ 1000 K_THREAD_STACK_DEFINE(encoder_thread_stack, CONFIG_ENCODER_STACK_SIZE); @@ -136,8 +135,7 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) static uint8_t *encoded_data; //static size_t pcm_block_size; static uint32_t test_tone_finite_pos; - static uint32_t encode_drop_count; - static uint32_t consecutive_encode_failures; + static bool encode_failed; while (1) { /* Don't start encoding until the stream needing it has started */ @@ -190,29 +188,16 @@ static void encoder_thread(void *arg1, void *arg2, void *arg3) ret = sw_codec_encode(pcm_raw_data, FRAME_SIZE_BYTES, &encoded_data, &encoded_data_size); if (ret) { - encode_drop_count++; - consecutive_encode_failures++; - - if (consecutive_encode_failures == 1U || - (consecutive_encode_failures % ENCODE_FAILURE_FATAL_THRESHOLD) == 0U) { - LOG_WRN("Audio encode failed: ret=%d, dropped=%u, consecutive=%u", - ret, (unsigned int)encode_drop_count, - (unsigned int)consecutive_encode_failures); + if (!encode_failed) { + LOG_WRN("Audio encode failed; dropping frames until recovery: %d", ret); } - - if (ret == -EINVAL || ret == -ENXIO || ret == -EPERM || - ret == -ENODEV || ret == -ENOTSUP || - consecutive_encode_failures >= ENCODE_FAILURE_FATAL_THRESHOLD) { - ERR_CHK_MSG(ret, "Persistent/non-recoverable audio encode failure"); - } - + encode_failed = true; continue; } - if (consecutive_encode_failures) { - LOG_INF("Audio encoder recovered after %u dropped frame(s)", - (unsigned int)consecutive_encode_failures); - consecutive_encode_failures = 0U; + if (encode_failed) { + LOG_INF("Audio encoder recovered"); + encode_failed = false; } } From 2fdd8bf421da0ed4aa1bd73f859ff01691bde48a Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 20 Jul 2026 13:34:32 +0200 Subject: [PATCH 63/77] feat(SCA): Add CodeChecker pipeline --- .codechecker.json | 14 ++ .github/workflows/codechecker.yaml | 176 +++++++++++++++++++ CONTRIBUTING.md | 27 +++ scripts/ci/codechecker_pr_report.py | 260 ++++++++++++++++++++++++++++ 4 files changed, 477 insertions(+) create mode 100644 .codechecker.json create mode 100644 .github/workflows/codechecker.yaml create mode 100644 scripts/ci/codechecker_pr_report.py diff --git a/.codechecker.json b/.codechecker.json new file mode 100644 index 00000000..4fc6da7e --- /dev/null +++ b/.codechecker.json @@ -0,0 +1,14 @@ +{ + "analyze": [ + "--analyzers", + "clangsa", + "cppcheck", + "--disable", + "severity:STYLE", + "--file", + "*/open-earable-v2/*", + "--capture-analysis-output", + "--report-hash", + "context-free-v2" + ] +} diff --git a/.github/workflows/codechecker.yaml b/.github/workflows/codechecker.yaml new file mode 100644 index 00000000..2711b3dc --- /dev/null +++ b/.github/workflows/codechecker.yaml @@ -0,0 +1,176 @@ +name: CodeChecker static analysis + +on: + pull_request: {} + workflow_dispatch: {} + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + CODECHECKER_VERSION: "6.28.2" + ZEPHYR_SDK_VERSION: "0.17.0" + +jobs: + codechecker: + name: Advisory analysis + runs-on: ubuntu-latest + continue-on-error: true + + steps: + - name: Check out application + uses: actions/checkout@v6 + with: + fetch-depth: 0 + path: zephyr-workspace/open-earable-v2 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install --yes \ + ccache \ + clang \ + cppcheck \ + device-tree-compiler \ + g++-multilib \ + gcc-multilib \ + gperf \ + libmagic1 \ + make \ + ninja-build \ + wget \ + xz-utils + + - name: Install Python tools + run: | + python -m pip install --upgrade pip + python -m pip install "codechecker==${CODECHECKER_VERSION}" west + + - name: Install Zephyr SDK + working-directory: zephyr-workspace + run: | + wget --quiet \ + "https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v${ZEPHYR_SDK_VERSION}/zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz" + tar xf "zephyr-sdk-${ZEPHYR_SDK_VERSION}_linux-x86_64_minimal.tar.xz" + "${PWD}/zephyr-sdk-${ZEPHYR_SDK_VERSION}/setup.sh" -c -t arm-zephyr-eabi + + - name: Initialize Zephyr workspace + working-directory: zephyr-workspace + run: | + west init -l open-earable-v2 + west update --narrow -o=--depth=1 + python -m pip install -r zephyr/scripts/requirements.txt + + - name: Register custom board + working-directory: zephyr-workspace + run: | + mkdir -p zephyr/boards/arm + cp -R open-earable-v2/boards/teco/. zephyr/boards/arm/ + + - name: Run CodeChecker + id: analysis + working-directory: zephyr-workspace + shell: bash + run: | + mkdir -p codechecker-output + set +e + west build \ + --no-sysbuild \ + --board openearable_v2/nrf5340/cpuapp \ + --pristine=always \ + open-earable-v2 \ + -- \ + -DFILE_SUFFIX=fota \ + -DZEPHYR_SCA_VARIANT=codechecker \ + -DCODECHECKER_ANALYZE_JOBS=4 \ + -DCODECHECKER_CONFIG_FILE="${PWD}/open-earable-v2/.codechecker.json" \ + -DCODECHECKER_EXPORT=html,json \ + 2>&1 | tee codechecker-output/analysis.log + analysis_exit=${PIPESTATUS[0]} + echo "exit_code=${analysis_exit}" >> "${GITHUB_OUTPUT}" + echo "CodeChecker/build exit code: ${analysis_exit}" >> codechecker-output/analysis.log + exit 0 + + - name: Prepare PR annotations + if: always() + working-directory: zephyr-workspace/open-earable-v2 + env: + CODECHECKER_REPORT: ../build/sca/codechecker/codechecker.json + COMMENT_OUTPUT: ../codechecker-output/pr-comment.md + GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + python scripts/ci/codechecker_pr_report.py \ + --report "${CODECHECKER_REPORT}" \ + --analysis-dir ../build/sca/codechecker/codechecker.plist \ + --comment-output "${COMMENT_OUTPUT}" \ + --base "${PR_BASE_SHA}" \ + --head "${PR_HEAD_SHA}" + + - name: Collect complete report + if: always() + working-directory: zephyr-workspace + run: | + mkdir -p codechecker-output + if [ -d build/sca/codechecker ]; then + cp -R build/sca/codechecker codechecker-output/report + fi + if [ -f build/compile_commands.json ]; then + cp build/compile_commands.json codechecker-output/compile_commands.json + fi + { + echo "CodeChecker ${CODECHECKER_VERSION}" + echo "Analysis exit code: ${{ steps.analysis.outputs.exit_code }}" + echo "Commit: ${{ github.sha }}" + } > codechecker-output/run-information.txt + + - name: Upload complete CodeChecker report + if: always() + uses: actions/upload-artifact@v7 + with: + name: codechecker-report-${{ github.run_number }} + path: zephyr-workspace/codechecker-output + if-no-files-found: error + retention-days: 30 + + - name: Publish advisory PR comment + if: always() && github.event_name == 'pull_request' + continue-on-error: true + uses: actions/github-script@v8 + env: + COMMENT_FILE: zephyr-workspace/codechecker-output/pr-comment.md + with: + script: | + const fs = require('fs'); + const marker = ''; + const body = fs.readFileSync(process.env.COMMENT_FILE, 'utf8'); + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const comments = await github.paginate( + github.rest.issues.listComments, + { owner, repo, issue_number, per_page: 100 } + ); + const previous = comments.find( + comment => comment.user.type === 'Bot' && comment.body.includes(marker) + ); + if (previous) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: previous.id, + body + }); + } else { + await github.rest.issues.createComment({ owner, repo, issue_number, body }); + } diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fabdda51..28dfd513 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,6 +148,33 @@ This repository expects code to be documented. - Avoid unrelated formatting churn. - Keep headers and source files aligned: declarations, ownership, and invariants should be easy to trace. +### Static Analysis + +GitHub Actions runs advisory CodeChecker analysis for pull requests; findings +and analyzer failures do not block merging. It analyzes the FOTA application +without sysbuild using Clang Static Analyzer and Cppcheck. Style findings, +Clang-Tidy, formatting, and automatic fixes are not enabled. The analyzer +configuration is in [.codechecker.json](.codechecker.json). + +Local execution is optional. In an nRF Connect SDK `v3.0.1` Linux or WSL +workspace, install CodeChecker `6.28.2`, Clang, and Cppcheck, then run: + +```bash +sudo apt-get install clang cppcheck +python -m pip install "codechecker==6.28.2" +codechecker_config="$(pwd)/.codechecker.json" +west build --no-sysbuild --board openearable_v2/nrf5340/cpuapp \ + --pristine=always . -- -DFILE_SUFFIX=fota \ + -DZEPHYR_SCA_VARIANT=codechecker \ + -DCODECHECKER_CONFIG_FILE="${codechecker_config}" \ + -DCODECHECKER_EXPORT=html,json +``` + +Reports are written to `/sca/codechecker/`. See Nordic's +[CodeChecker documentation for nRF Connect SDK 3.0.1](https://nrfconnectdocs.nordicsemi.com/ncs/3.0.1/zephyr/develop/sca/codechecker.html) +or the corresponding [upstream Zephyr 4.0 documentation](https://docs.zephyrproject.org/4.0.0/develop/sca/codechecker.html) +for the available options. + ## Validation Before Opening A Pull Request At minimum, contributors should validate that the firmware still builds with the repository's supported configuration. diff --git a/scripts/ci/codechecker_pr_report.py b/scripts/ci/codechecker_pr_report.py new file mode 100644 index 00000000..8d61684a --- /dev/null +++ b/scripts/ci/codechecker_pr_report.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +"""Create advisory GitHub output from a CodeChecker JSON report.""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import subprocess +from pathlib import Path +from typing import Any + + +MARKER = "" +SOURCE_SUFFIXES = {".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"} +DIFF_HEADER = re.compile(r"^\+\+\+ b/(.+)$") +DIFF_RANGE = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@") + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + parser.add_argument("--report", type=Path, required=True) + parser.add_argument("--analysis-dir", type=Path) + parser.add_argument("--comment-output", type=Path, required=True) + parser.add_argument("--base", default="") + parser.add_argument("--head", default="HEAD") + parser.add_argument("--repo-root", type=Path, default=Path.cwd()) + return parser.parse_args() + + +def changed_lines(repo_root: Path, base: str, head: str) -> dict[str, list[range]]: + if not base: + return {} + + command = [ + "git", + "diff", + "--unified=0", + "--no-color", + f"{base}...{head}", + "--", + "*.c", + "*.cc", + "*.cpp", + "*.cxx", + "*.h", + "*.hh", + "*.hpp", + "*.hxx", + ] + result = subprocess.run( + command, + cwd=repo_root, + check=True, + capture_output=True, + text=True, + ) + + current_file: str | None = None + changed: dict[str, list[range]] = {} + for raw_line in result.stdout.splitlines(): + header = DIFF_HEADER.match(raw_line) + if header: + current_file = header.group(1) + if Path(current_file).suffix.lower() not in SOURCE_SUFFIXES: + current_file = None + continue + + hunk = DIFF_RANGE.match(raw_line) + if current_file and hunk: + start = int(hunk.group(1)) + count = int(hunk.group(2) or "1") + if count: + changed.setdefault(current_file, []).append(range(start, start + count)) + + return changed + + +def report_path(report: dict[str, Any], repo_root: Path) -> str | None: + file_value = report.get("file", {}) + candidates: list[str] = [] + if isinstance(file_value, dict): + candidates.extend( + str(file_value.get(key, "")) for key in ("original_path", "path", "id") + ) + elif file_value: + candidates.append(str(file_value)) + + repo_root = repo_root.resolve() + repo_name = repo_root.name + for candidate in candidates: + if not candidate: + continue + normalized = candidate.replace("\\", "/") + candidate_path = Path(candidate) + if candidate_path.is_absolute(): + try: + return candidate_path.resolve().relative_to(repo_root).as_posix() + except ValueError: + pass + + marker = f"/{repo_name}/" + if marker in normalized: + return normalized.split(marker, 1)[1] + if normalized.startswith(f"{repo_name}/"): + return normalized[len(repo_name) + 1 :] + if (repo_root / normalized).exists(): + return normalized + + return None + + +def load_reports(report_file: Path) -> list[dict[str, Any]]: + if not report_file.is_file(): + return [] + data = json.loads(report_file.read_text(encoding="utf-8")) + if isinstance(data, dict): + reports = data.get("reports", []) + else: + reports = data + return [report for report in reports if isinstance(report, dict)] + + +def escape_annotation(value: str) -> str: + return ( + value.replace("%", "%25") + .replace("\r", "%0D") + .replace("\n", "%0A") + .replace(":", "%3A") + .replace(",", "%2C") + ) + + +def markdown_escape(value: str) -> str: + return value.replace("|", "\\|").replace("\n", " ").replace("\r", " ") + + +def main() -> int: + args = parse_args() + repo_root = args.repo_root.resolve() + try: + changed = changed_lines(repo_root, args.base, args.head) + except subprocess.CalledProcessError as error: + print(f"Could not calculate changed lines: {error}") + changed = {} + + all_reports = load_reports(args.report) + analyzer_errors = ( + len(list(args.analysis_dir.rglob("*.err"))) + if args.analysis_dir and args.analysis_dir.is_dir() + else 0 + ) + findings: list[dict[str, Any]] = [] + for report in all_reports: + path = report_path(report, repo_root) + line = int(report.get("line") or 1) + if path not in changed or not any(line in lines for lines in changed[path]): + continue + if str(report.get("severity", "")).upper() == "STYLE": + continue + finding = dict(report) + finding["repo_path"] = path + finding["line"] = line + findings.append(finding) + + severity_order = { + "CRITICAL": 0, + "HIGH": 1, + "MEDIUM": 2, + "LOW": 3, + "UNSPECIFIED": 4, + } + findings.sort( + key=lambda item: ( + severity_order.get(str(item.get("severity", "UNSPECIFIED")).upper(), 5), + item["repo_path"], + item["line"], + ) + ) + + for finding in findings[:50]: + severity = str(finding.get("severity") or "UNSPECIFIED").upper() + level = "warning" if severity in {"CRITICAL", "HIGH", "MEDIUM"} else "notice" + checker = str(finding.get("checker_name") or "CodeChecker") + message = str(finding.get("message") or "Static analysis finding") + print( + f"::{level} file={escape_annotation(finding['repo_path'])}," + f"line={finding['line']},title={escape_annotation(checker)}::" + f"{escape_annotation(f'[{severity}] {message}')}" + ) + + run_url = os.environ.get("GITHUB_RUN_URL", "") + report_available = args.report.is_file() + lines = [ + MARKER, + "## CodeChecker static analysis (advisory)", + "", + "This report is informational and does not block merging. " + "The complete codebase report is available as the `codechecker-report` workflow artifact.", + "", + ] + if analyzer_errors: + lines.extend( + [ + f"**Coverage note:** {analyzer_errors} analyzer invocation(s) could not be " + "completed. Their diagnostic logs are included in the downloadable artifact.", + "", + ] + ) + if not report_available: + lines.extend( + [ + "CodeChecker did not produce a JSON report. Check the downloadable analysis log " + "for build or analyzer errors.", + "", + ] + ) + elif findings: + lines.extend( + [ + f"Found **{len(findings)}** non-style issue(s) on lines changed by this PR.", + "", + "| Severity | Location | Analyzer/checker | Message |", + "| --- | --- | --- | --- |", + ] + ) + for finding in findings[:50]: + severity = markdown_escape(str(finding.get("severity") or "UNSPECIFIED")) + location = f"`{finding['repo_path']}:{finding['line']}`" + checker = markdown_escape(str(finding.get("checker_name") or "CodeChecker")) + message = markdown_escape(str(finding.get("message") or "Static analysis finding")) + lines.append(f"| {severity} | {location} | `{checker}` | {message} |") + if len(findings) > 50: + lines.extend(["", f"Only the first 50 of {len(findings)} findings are shown here."]) + lines.append("") + else: + lines.extend( + [ + "No non-style findings were reported on lines changed by this PR.", + "", + ] + ) + + if run_url: + lines.append(f"[Open this workflow run to download the complete report]({run_url})") + lines.append("") + + args.comment_output.parent.mkdir(parents=True, exist_ok=True) + args.comment_output.write_text("\n".join(lines), encoding="utf-8") + + summary_file = os.environ.get("GITHUB_STEP_SUMMARY") + if summary_file: + with Path(summary_file).open("a", encoding="utf-8") as summary: + summary.write("\n".join(lines[1:])) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 3f9668d96ed98f058e6e63be4f783d0bde1dfa33 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 20 Jul 2026 13:58:44 +0200 Subject: [PATCH 64/77] feat(SCA): Add extensive reporting --- scripts/ci/codechecker_pr_report.py | 362 +++++++++++++++++++++++++--- 1 file changed, 331 insertions(+), 31 deletions(-) diff --git a/scripts/ci/codechecker_pr_report.py b/scripts/ci/codechecker_pr_report.py index 8d61684a..45ba2d05 100644 --- a/scripts/ci/codechecker_pr_report.py +++ b/scripts/ci/codechecker_pr_report.py @@ -4,18 +4,33 @@ from __future__ import annotations import argparse +from collections import Counter +from html import escape as html_escape import json import os +import plistlib import re import subprocess from pathlib import Path from typing import Any +from urllib.parse import quote MARKER = "" SOURCE_SUFFIXES = {".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"} DIFF_HEADER = re.compile(r"^\+\+\+ b/(.+)$") DIFF_RANGE = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@") +ANALYZER_RESULT = re.compile( + r"_(?P[A-Za-z0-9_-]+)_[0-9a-f]+\.plist(?P\.err)?$" +) +ANSI_ESCAPE = re.compile(r"(?:\\x1b|\x1b)\[[0-9;]*m") +DIAGNOSTIC = re.compile( + r"(?P[^\r\n]+?):(?P\d+):(?P\d+):\s*" + r"(?:fatal\s+)?error:\s*(?P[^\r\n]+)" +) +MAX_DETAILS = 100 +CONTEXT_LINES = 1 +MAX_SNIPPET_LINE_LENGTH = 180 def parse_args() -> argparse.Namespace: @@ -82,7 +97,7 @@ def report_path(report: dict[str, Any], repo_root: Path) -> str | None: candidates: list[str] = [] if isinstance(file_value, dict): candidates.extend( - str(file_value.get(key, "")) for key in ("original_path", "path", "id") + str(file_value.get(key, "")) for key in ("path", "original_path", "id") ) elif file_value: candidates.append(str(file_value)) @@ -100,13 +115,18 @@ def report_path(report: dict[str, Any], repo_root: Path) -> str | None: except ValueError: pass + if (repo_root / normalized).exists(): + return normalized + parts = Path(normalized).parts + for index in range(1, len(parts)): + suffix = Path(*parts[index:]) + if (repo_root / suffix).exists(): + return suffix.as_posix() marker = f"/{repo_name}/" if marker in normalized: - return normalized.split(marker, 1)[1] + return normalized.rsplit(marker, 1)[1] if normalized.startswith(f"{repo_name}/"): return normalized[len(repo_name) + 1 :] - if (repo_root / normalized).exists(): - return normalized return None @@ -122,6 +142,53 @@ def load_reports(report_file: Path) -> list[dict[str, Any]]: return [report for report in reports if isinstance(report, dict)] +def analyzer_results(analysis_dir: Path | None) -> tuple[Counter[str], Counter[str]]: + completed: Counter[str] = Counter() + failed: Counter[str] = Counter() + if not analysis_dir or not analysis_dir.is_dir(): + return completed, failed + + for path in analysis_dir.iterdir(): + match = ANALYZER_RESULT.search(path.name) + if not match: + continue + destination = failed if match.group("failed") else completed + destination[match.group("analyzer")] += 1 + return completed, failed + + +def analyzer_failures( + analysis_dir: Path | None, repo_root: Path +) -> list[dict[str, str | int]]: + failures: list[dict[str, str | int]] = [] + if not analysis_dir or not analysis_dir.is_dir(): + return failures + + for path in sorted(analysis_dir.glob("*.plist.err")): + match = ANALYZER_RESULT.search(path.name) + analyzer = match.group("analyzer") if match else "unknown" + source = path.name.split(f"_{analyzer}_", 1)[0] + line = 1 + message = "Analyzer invocation failed; see the artifact for diagnostics." + try: + data = plistlib.loads(path.read_bytes()) + stderr = ANSI_ESCAPE.sub("", str(data.get("stderr", ""))) + diagnostic = DIAGNOSTIC.search(stderr) + if diagnostic: + candidate = report_path( + {"file": {"original_path": diagnostic.group("path")}}, repo_root + ) + source = candidate or Path(diagnostic.group("path")).name + line = int(diagnostic.group("line")) + message = diagnostic.group("message").strip() + except (OSError, plistlib.InvalidFileException, ValueError): + pass + failures.append( + {"analyzer": analyzer, "path": source, "line": line, "message": message} + ) + return failures + + def escape_annotation(value: str) -> str: return ( value.replace("%", "%25") @@ -136,6 +203,150 @@ def markdown_escape(value: str) -> str: return value.replace("|", "\\|").replace("\n", " ").replace("\r", " ") +def source_url(path: str, line: int, repository: str, revision: str) -> str | None: + if not repository or not revision: + return None + return f"https://github.com/{repository}/blob/{revision}/{quote(path, safe='/')}#L{line}" + + +def source_location(path: str, line: int, repository: str, revision: str) -> str: + label = f"{path}:{line}" + url = source_url(path, line, repository, revision) + if not url: + return f"`{label}`" + return f"[`{label}`]({url})" + + +def source_location_html(path: str, line: int, repository: str, revision: str) -> str: + label = html_escape(f"{path}:{line}") + url = source_url(path, line, repository, revision) + if not url: + return f"{label}" + return f'{label}' + + +def finding_table( + reports: list[dict[str, Any]], repository: str, revision: str +) -> list[str]: + lines = [ + "| Severity | Location | Analyzer/checker | Message |", + "| --- | --- | --- | --- |", + ] + for finding in reports: + severity = markdown_escape(str(finding.get("severity") or "UNSPECIFIED")) + location = source_location( + str(finding["repo_path"]), int(finding["line"]), repository, revision + ) + analyzer = markdown_escape(str(finding.get("analyzer_name") or "unknown")) + checker = markdown_escape(str(finding.get("checker_name") or "CodeChecker")) + message = markdown_escape(str(finding.get("message") or "Static analysis finding")) + lines.append(f"| {severity} | {location} | `{analyzer}/{checker}` | {message} |") + return lines + + +def finding_range( + finding: dict[str, Any], repo_root: Path +) -> tuple[int, int, int, int]: + line = int(finding["line"]) + start_col = int(finding.get("column") or 1) + end_line = line + end_col = start_col + for event in reversed(finding.get("bug_path_events") or []): + if not isinstance(event, dict): + continue + event_path = report_path(event, repo_root) + event_range = event.get("range") or {} + if event_path != finding["repo_path"] or int(event.get("line") or 0) != line: + continue + start_col = int(event_range.get("start_col") or event.get("column") or start_col) + end_line = int(event_range.get("end_line") or line) + end_col = int(event_range.get("end_col") or start_col) + break + return line, start_col, end_line, end_col + + +def source_excerpt(finding: dict[str, Any], repo_root: Path) -> str | None: + source = repo_root / str(finding["repo_path"]) + if not source.is_file(): + return None + try: + source_lines = source.read_text(encoding="utf-8", errors="replace").splitlines() + except OSError: + return None + + line, start_col, end_line, end_col = finding_range(finding, repo_root) + if line < 1 or line > len(source_lines): + return None + first = max(1, line - CONTEXT_LINES) + last = min(len(source_lines), max(line, end_line) + CONTEXT_LINES) + width = len(str(last)) + excerpt: list[str] = [] + for current in range(first, last + 1): + prefix = ">" if line <= current <= end_line else " " + rendered = source_lines[current - 1].expandtabs(4) + if len(rendered) > MAX_SNIPPET_LINE_LENGTH: + rendered = f"{rendered[: MAX_SNIPPET_LINE_LENGTH - 3]}..." + excerpt.append(f"{prefix} {current:>{width}} | {rendered}") + if current == line: + original = source_lines[current - 1] + expanded_prefix = original[: max(0, start_col - 1)].expandtabs(4) + if end_line == line: + selected = original[ + max(0, start_col - 1) : max(start_col, end_col) + ].expandtabs(4) + marker_width = max(1, len(selected)) + else: + marker_width = max(1, len(rendered) - len(expanded_prefix)) + excerpt.append( + f" {'':>{width}} | {' ' * len(expanded_prefix)}{'^' * marker_width}" + ) + return "\n".join(excerpt) + + +def context_section( + reports: list[dict[str, Any]], + repo_root: Path, + repository: str, + revision: str, +) -> list[str]: + details = reports[:MAX_DETAILS] + if not details: + return [] + + lines = [ + "### Complete-codebase findings", + "", + "Each finding includes source context. The `>` line and carets identify the " + "expression reported by the analyzer.", + "", + "
", + f"Show detailed findings with source context ({len(details)}" + f" of {len(reports)})", + "", + ] + for finding in details: + severity = html_escape(str(finding.get("severity") or "UNSPECIFIED")) + message = html_escape(str(finding.get("message") or "Static analysis finding")) + location = source_location_html( + str(finding["repo_path"]), int(finding["line"]), repository, revision + ) + excerpt = source_excerpt(finding, repo_root) + lines.extend( + [ + "
", + f"{severity} — {location} — {message}", + "", + ] + ) + if excerpt: + lines.extend(["```text", excerpt, "```", ""]) + else: + lines.extend(["Source context is unavailable in the workflow checkout.", ""]) + lines.extend(["
", ""]) + lines.extend(["
", ""]) + return lines + + def main() -> int: args = parse_args() repo_root = args.repo_root.resolve() @@ -145,20 +356,20 @@ def main() -> int: print(f"Could not calculate changed lines: {error}") changed = {} - all_reports = load_reports(args.report) - analyzer_errors = ( - len(list(args.analysis_dir.rglob("*.err"))) - if args.analysis_dir and args.analysis_dir.is_dir() - else 0 - ) + all_reports = [ + report + for report in load_reports(args.report) + if str(report.get("severity", "")).upper() != "STYLE" + ] + completed_runs, failed_runs = analyzer_results(args.analysis_dir) + failures = analyzer_failures(args.analysis_dir, repo_root) + analyzer_errors = sum(failed_runs.values()) findings: list[dict[str, Any]] = [] for report in all_reports: path = report_path(report, repo_root) line = int(report.get("line") or 1) if path not in changed or not any(line in lines for lines in changed[path]): continue - if str(report.get("severity", "")).upper() == "STYLE": - continue finding = dict(report) finding["repo_path"] = path finding["line"] = line @@ -179,6 +390,23 @@ def main() -> int: ) ) + normalized_reports: list[dict[str, Any]] = [] + for report in all_reports: + path = report_path(report, repo_root) + if not path: + continue + normalized = dict(report) + normalized["repo_path"] = path + normalized["line"] = int(report.get("line") or 1) + normalized_reports.append(normalized) + normalized_reports.sort( + key=lambda item: ( + severity_order.get(str(item.get("severity", "UNSPECIFIED")).upper(), 5), + item["repo_path"], + item["line"], + ) + ) + for finding in findings[:50]: severity = str(finding.get("severity") or "UNSPECIFIED").upper() level = "warning" if severity in {"CRITICAL", "HIGH", "MEDIUM"} else "notice" @@ -191,23 +419,33 @@ def main() -> int: ) run_url = os.environ.get("GITHUB_RUN_URL", "") + repository = os.environ.get("GITHUB_REPOSITORY", "") + revision = args.head report_available = args.report.is_file() + severity_counts = Counter( + str(report.get("severity") or "UNSPECIFIED").upper() + for report in normalized_reports + ) + analyzer_counts = Counter( + str(report.get("analyzer_name") or "unknown") for report in normalized_reports + ) lines = [ MARKER, "## CodeChecker static analysis (advisory)", "", - "This report is informational and does not block merging. " - "The complete codebase report is available as the `codechecker-report` workflow artifact.", + "> [!NOTE]", + "> This report is informational. It does not block merging and does not change code.", + "", + "### At a glance", + "", + "| Scope | Result |", + "| --- | ---: |", + f"| Findings on lines changed by this PR | **{len(findings)}** |", + f"| Findings in the complete codebase | **{len(normalized_reports)}** |", + f"| Analyzer invocations completed | **{sum(completed_runs.values())}** |", + f"| Analyzer invocations failed | **{analyzer_errors}** |", "", ] - if analyzer_errors: - lines.extend( - [ - f"**Coverage note:** {analyzer_errors} analyzer invocation(s) could not be " - "completed. Their diagnostic logs are included in the downloadable artifact.", - "", - ] - ) if not report_available: lines.extend( [ @@ -219,31 +457,93 @@ def main() -> int: elif findings: lines.extend( [ + "### Findings on changed lines", + "", f"Found **{len(findings)}** non-style issue(s) on lines changed by this PR.", "", - "| Severity | Location | Analyzer/checker | Message |", - "| --- | --- | --- | --- |", ] ) - for finding in findings[:50]: - severity = markdown_escape(str(finding.get("severity") or "UNSPECIFIED")) - location = f"`{finding['repo_path']}:{finding['line']}`" - checker = markdown_escape(str(finding.get("checker_name") or "CodeChecker")) - message = markdown_escape(str(finding.get("message") or "Static analysis finding")) - lines.append(f"| {severity} | {location} | `{checker}` | {message} |") + lines.extend(finding_table(findings[:50], repository, revision)) if len(findings) > 50: lines.extend(["", f"Only the first 50 of {len(findings)} findings are shown here."]) lines.append("") else: lines.extend( [ + "### Findings on changed lines", + "", "No non-style findings were reported on lines changed by this PR.", "", ] ) + if completed_runs or failed_runs: + lines.extend( + [ + "### Analyzer coverage", + "", + "| Analyzer | Completed | Failed |", + "| --- | ---: | ---: |", + ] + ) + for analyzer in sorted(completed_runs.keys() | failed_runs.keys()): + lines.append( + f"| `{analyzer}` | {completed_runs[analyzer]} | {failed_runs[analyzer]} |" + ) + lines.append("") + + if failures: + lines.extend( + [ + "
", + f"{len(failures)} failed analyzer invocation(s)", + "", + "These files received reduced analysis coverage. The first compiler diagnostic " + "from each invocation is shown below.", + "The reason is that the clang frontend was not able to link them because of issues like implicit declaration. Target gcc build may still succeed.", + "", + "| Analyzer | Source | First diagnostic |", + "| --- | --- | --- |", + ] + ) + for failure in failures: + location = source_location( + str(failure["path"]), int(failure["line"]), repository, revision + ) + lines.append( + f"| `{markdown_escape(str(failure['analyzer']))}` | {location} | " + f"{markdown_escape(str(failure['message']))} |" + ) + lines.extend(["", "
", ""]) + + if report_available: + severity_summary = ", ".join( + f"**{severity_counts.get(severity, 0)} {severity.lower()}**" + for severity in ("CRITICAL", "HIGH", "MEDIUM", "LOW", "UNSPECIFIED") + if severity_counts.get(severity, 0) + ) or "no findings" + analyzer_summary = ", ".join( + f"`{name}`: {count}" for name, count in sorted(analyzer_counts.items()) + ) or "none" + lines.extend( + [ + "### Complete-codebase summary", + "", + f"Severity: {severity_summary} ", + f"Reported by analyzer: {analyzer_summary}", + "", + ] + ) + lines.extend( + context_section(normalized_reports, repo_root, repository, revision) + ) + if run_url: - lines.append(f"[Open this workflow run to download the complete report]({run_url})") + lines.append( + "Open the workflow run and download the complete CodeChecker " + f"[report]({run_url}) for a detailed view. Check out " + "`codechecker.html/index.html`." + ) lines.append("") args.comment_output.parent.mkdir(parents=True, exist_ok=True) From 3f78b96886440bc980b327c9a626a6dbbc2894fa Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 3 Aug 2026 18:02:26 +0200 Subject: [PATCH 65/77] ci(codechecker): shorten clean analysis reports --- scripts/ci/codechecker_pr_report.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/ci/codechecker_pr_report.py b/scripts/ci/codechecker_pr_report.py index 45ba2d05..2c405740 100644 --- a/scripts/ci/codechecker_pr_report.py +++ b/scripts/ci/codechecker_pr_report.py @@ -546,6 +546,21 @@ def main() -> int: ) lines.append("") + # Keep successful, finding-free runs deliberately terse. The marker remains so + # the workflow can update the same PR comment on subsequent runs. + if ( + report_available + and not normalized_reports + and not failures + and not analyzer_errors + ): + lines = [ + MARKER, + "## CodeChecker static analysis", + "", + "✅ No non-style issues found.", + ] + args.comment_output.parent.mkdir(parents=True, exist_ok=True) args.comment_output.write_text("\n".join(lines), encoding="utf-8") From 8f490629a35cd4cd3be00b39ea29554751466468 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 12:04:44 +0200 Subject: [PATCH 66/77] fix(ci): require CodeChecker report output --- .github/workflows/codechecker.yaml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codechecker.yaml b/.github/workflows/codechecker.yaml index 2711b3dc..3f605c08 100644 --- a/.github/workflows/codechecker.yaml +++ b/.github/workflows/codechecker.yaml @@ -20,7 +20,6 @@ jobs: codechecker: name: Advisory analysis runs-on: ubuntu-latest - continue-on-error: true steps: - name: Check out application @@ -28,6 +27,7 @@ jobs: with: fetch-depth: 0 path: zephyr-workspace/open-earable-v2 + submodules: recursive - name: Set up Python uses: actions/setup-python@v6 @@ -83,6 +83,7 @@ jobs: shell: bash run: | mkdir -p codechecker-output + set -o pipefail set +e west build \ --no-sysbuild \ @@ -99,7 +100,19 @@ jobs: analysis_exit=${PIPESTATUS[0]} echo "exit_code=${analysis_exit}" >> "${GITHUB_OUTPUT}" echo "CodeChecker/build exit code: ${analysis_exit}" >> codechecker-output/analysis.log - exit 0 + if [ "${analysis_exit}" -ne 0 ]; then + echo "::error::CodeChecker build failed; see codechecker-output/analysis.log" + exit "${analysis_exit}" + fi + if [ ! -s build/sca/codechecker/codechecker.json ]; then + echo "::error::CodeChecker did not produce build/sca/codechecker/codechecker.json" + exit 1 + fi + if ! find build/sca/codechecker/codechecker.plist -maxdepth 1 -type f \ + -name '*.plist' -print -quit | grep -q .; then + echo "::error::CodeChecker completed without analyzing any compilation commands" + exit 1 + fi - name: Prepare PR annotations if: always() From 34e73b19ac55c64d1290de5e0899de640d4f7b99 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 15:46:53 +0200 Subject: [PATCH 67/77] fix: mark intentionally unused callback arguments --- src/ParseInfo/SensorScheme.cpp | 4 ++++ src/SensorManager/BMP388/Adafruit_BMP3XX.cpp | 5 ++++- src/SensorManager/Baro.cpp | 2 ++ src/SensorManager/BoneConduction.cpp | 4 +++- src/SensorManager/IMU.cpp | 4 +++- src/SensorManager/PPG.cpp | 4 +++- src/SensorManager/Temp.cpp | 4 +++- .../media/bt_content_ctrl_media.c | 4 ++++ .../bt_management/advertising/bt_mgmt_adv.c | 20 ++++++++++++------- src/bluetooth/bt_management/bt_mgmt.c | 7 +++++++ .../controller_config/bt_mgmt_ctlr_cfg.c | 6 +++++- src/bluetooth/bt_management/dfu/bt_mgmt_dfu.c | 3 +++ .../volume/bt_vol_rend.c | 2 ++ .../gatt_services/audio_response_service.c | 15 ++++++++++++++ .../gatt_services/battery_service.cpp | 4 +++- src/bluetooth/gatt_services/button_service.c | 3 ++- src/bluetooth/gatt_services/led_service.cpp | 6 ++++++ src/buttons/Button.cpp | 2 ++ src/time_sync/time_sync.c | 5 +++++ src/utils/error_handler.c | 1 + 20 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/ParseInfo/SensorScheme.cpp b/src/ParseInfo/SensorScheme.cpp index 292dc403..6639af5a 100644 --- a/src/ParseInfo/SensorScheme.cpp +++ b/src/ParseInfo/SensorScheme.cpp @@ -54,6 +54,9 @@ static ssize_t write_sensor_request(struct bt_conn *conn, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(attr); + ARG_UNUSED(offset); + ARG_UNUSED(flags); if (len != sizeof(uint8_t)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } @@ -73,6 +76,7 @@ static ssize_t write_sensor_request(struct bt_conn *conn, void scheme_ccc_cfg(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); notify_enabled = (value == BT_GATT_CCC_NOTIFY); } diff --git a/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp b/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp index 7775948a..9a11960f 100644 --- a/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp +++ b/src/SensorManager/BMP388/Adafruit_BMP3XX.cpp @@ -434,7 +434,10 @@ int8_t i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, return 0; } -static void delay_usec(uint32_t us, void *intf_ptr) { k_usleep(us); } +static void delay_usec(uint32_t us, void *intf_ptr) { + ARG_UNUSED(intf_ptr); + k_usleep(us); +} static int8_t validate_trimming_param(struct bmp3_dev *dev) { int8_t rslt; diff --git a/src/SensorManager/Baro.cpp b/src/SensorManager/Baro.cpp index aac86975..0f3fe4e9 100644 --- a/src/SensorManager/Baro.cpp +++ b/src/SensorManager/Baro.cpp @@ -35,6 +35,7 @@ const SampleRateSetting<18> Baro::sample_rates = { }; void Baro::update_sensor(struct k_work *work) { + ARG_UNUSED(work); int ret; bmp.performReading(); @@ -69,6 +70,7 @@ void Baro::update_sensor(struct k_work *work) { */ void Baro::sensor_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); k_work_submit_to_queue(&sensor_work_q, &sensor.sensor_work); }; diff --git a/src/SensorManager/BoneConduction.cpp b/src/SensorManager/BoneConduction.cpp index dbbad21c..fad4f0d6 100644 --- a/src/SensorManager/BoneConduction.cpp +++ b/src/SensorManager/BoneConduction.cpp @@ -48,6 +48,7 @@ void BoneConduction::reset() { } void BoneConduction::update_sensor(struct k_work *work) { + ARG_UNUSED(work); uint64_t _time_stamp = micros(); BoneConduction::sensor._sample_count += (_time_stamp - BoneConduction::sensor._last_time_stamp) / BoneConduction::sensor.t_sample_us; @@ -103,6 +104,7 @@ void BoneConduction::update_sensor(struct k_work *work) { * @brief Submit a k_work on timer expiry. */ void BoneConduction::sensor_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); k_work_submit_to_queue(&sensor_work_q, &sensor.sensor_work); } @@ -137,4 +139,4 @@ void BoneConduction::stop() { bma580.stop(); pm_device_runtime_put(ls_1_8); -} \ No newline at end of file +} diff --git a/src/SensorManager/IMU.cpp b/src/SensorManager/IMU.cpp index 1f0a59e8..0314fbdf 100644 --- a/src/SensorManager/IMU.cpp +++ b/src/SensorManager/IMU.cpp @@ -25,6 +25,7 @@ const SampleRateSetting<6> IMU::sample_rates = { }; void IMU::update_sensor(struct k_work *work) { + ARG_UNUSED(work); int ret; sBmx160SensorData_t magno_data; @@ -57,6 +58,7 @@ void IMU::update_sensor(struct k_work *work) { */ void IMU::sensor_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); k_work_submit_to_queue(&sensor_work_q, &sensor.sensor_work); }; @@ -107,4 +109,4 @@ void IMU::stop() { imu.softReset(); pm_device_runtime_put(ls_1_8); -} \ No newline at end of file +} diff --git a/src/SensorManager/PPG.cpp b/src/SensorManager/PPG.cpp index 33132b3e..993c1fd0 100644 --- a/src/SensorManager/PPG.cpp +++ b/src/SensorManager/PPG.cpp @@ -66,6 +66,7 @@ bool PPG::init(struct k_msgq * queue) { } void PPG::update_sensor(struct k_work *work) { + ARG_UNUSED(work); int int_status; int status; @@ -130,6 +131,7 @@ void PPG::update_sensor(struct k_work *work) { * @brief Submit a k_work on timer expiry. */ void PPG::sensor_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); k_work_submit_to_queue(&sensor_work_q, &sensor.sensor_work); } @@ -165,4 +167,4 @@ void PPG::stop() { pm_device_runtime_put(ls_1_8); pm_device_runtime_put(ls_3_3); -} \ No newline at end of file +} diff --git a/src/SensorManager/Temp.cpp b/src/SensorManager/Temp.cpp index 24862dfd..9ba794d5 100644 --- a/src/SensorManager/Temp.cpp +++ b/src/SensorManager/Temp.cpp @@ -49,6 +49,7 @@ bool Temp::init(struct k_msgq * queue) { } void Temp::update_sensor(struct k_work *work) { + ARG_UNUSED(work); if (!temp.dataAvailable()) return; MLX90632::status returnError; @@ -78,6 +79,7 @@ void Temp::update_sensor(struct k_work *work) { * @brief Submit a k_work on timer expiry. */ void Temp::sensor_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); k_work_submit_to_queue(&sensor_work_q, &sensor.sensor_work); } @@ -106,4 +108,4 @@ void Temp::stop() { pm_device_runtime_put(ls_1_8); pm_device_runtime_put(ls_3_3); -} \ No newline at end of file +} diff --git a/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c b/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c index 18e6e2cd..f76c0efc 100644 --- a/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c +++ b/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c @@ -162,6 +162,7 @@ static void mcc_send_command_cb(struct bt_conn *conn, int err, const struct mpl_ */ static void mcc_cmd_notification_cb(struct bt_conn *conn, int err, const struct mpl_cmd_ntf *ntf) { + ARG_UNUSED(conn); LOG_DBG("mcc_cmd_ntf_cb"); if (err) { @@ -179,6 +180,7 @@ static void mcc_cmd_notification_cb(struct bt_conn *conn, int err, const struct */ static void mcc_read_media_state_cb(struct bt_conn *conn, int err, uint8_t state) { + ARG_UNUSED(conn); LOG_DBG("mcc_read_media_cb, state: %d", state); if (err) { @@ -199,6 +201,7 @@ static void mcc_read_media_state_cb(struct bt_conn *conn, int err, uint8_t state static void mcs_command_recv_cb(struct media_player *plr, int err, const struct mpl_cmd_ntf *cmd_ntf) { + ARG_UNUSED(plr); if (err) { LOG_ERR("Command failed (%d)", err); return; @@ -223,6 +226,7 @@ static void mcs_command_recv_cb(struct media_player *plr, int err, */ static void mcs_media_state_cb(struct media_player *plr, int err, uint8_t state) { + ARG_UNUSED(plr); if (err) { LOG_ERR("Media state failed (%d)", err); return; diff --git a/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c b/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c index ba1e4922..a82cf12c 100644 --- a/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c +++ b/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c @@ -47,8 +47,10 @@ .peer = NULL, }; - static void bond_find(const struct bt_bond_info *info, void *user_data) - { +static void bond_find(const struct bt_bond_info *info, void *user_data) +{ + ARG_UNUSED(user_data); + int ret; struct bt_conn *conn; @@ -83,8 +85,10 @@ } } - static void filter_accept_list_add(const struct bt_bond_info *info, void *user_data) - { +static void filter_accept_list_add(const struct bt_bond_info *info, void *user_data) +{ + ARG_UNUSED(user_data); + int ret; ret = bt_le_filter_accept_list_add(&info->addr); @@ -227,8 +231,10 @@ return 0; } - static void advertising_process(struct k_work *work) - { +static void advertising_process(struct k_work *work) +{ + ARG_UNUSED(work); + int ret; struct bt_mgmt_msg msg; uint8_t ext_adv_index; @@ -474,4 +480,4 @@ { k_work_init(&adv_work, advertising_process); } - \ No newline at end of file + diff --git a/src/bluetooth/bt_management/bt_mgmt.c b/src/bluetooth/bt_management/bt_mgmt.c index f9ed147d..627e2833 100644 --- a/src/bluetooth/bt_management/bt_mgmt.c +++ b/src/bluetooth/bt_management/bt_mgmt.c @@ -69,12 +69,16 @@ static void conn_state_connected_check(struct bt_conn *conn, void *data) void mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx) { + ARG_UNUSED(conn); + LOG_INF("Updated MTU: TX: %d RX: %d bytes", tx, rx); } static void le_data_length_updated(struct bt_conn *conn, struct bt_conn_le_data_len_info *info) { + ARG_UNUSED(conn); + LOG_INF("LE data len updated: TX (len: %d time: %d)" " RX (len: %d time: %d)", info->tx_max_len, info->tx_max_time, info->rx_max_len, info->rx_max_time); @@ -390,6 +394,9 @@ int bt_mgmt_conn_disconnect(struct bt_conn *conn, uint8_t reason) int bonded_device_count = 0; void count_bonds(const struct bt_bond_info *info, void *user_data) { + ARG_UNUSED(info); + ARG_UNUSED(user_data); + bonded_device_count++; } diff --git a/src/bluetooth/bt_management/controller_config/bt_mgmt_ctlr_cfg.c b/src/bluetooth/bt_management/controller_config/bt_mgmt_ctlr_cfg.c index a7ff6734..8bd09f19 100644 --- a/src/bluetooth/bt_management/controller_config/bt_mgmt_ctlr_cfg.c +++ b/src/bluetooth/bt_management/controller_config/bt_mgmt_ctlr_cfg.c @@ -42,6 +42,7 @@ K_TIMER_DEFINE(ctlr_poll_timer, ctlr_poll_timer_handler, NULL); static void work_ctlr_poll_handler(struct k_work *work) { + ARG_UNUSED(work); int ret; uint16_t manufacturer = 0; @@ -54,6 +55,7 @@ static void work_ctlr_poll_handler(struct k_work *work) static void ctlr_poll_timer_handler(struct k_timer *timer_id) { + ARG_UNUSED(timer_id); int ret; ret = k_work_submit_to_queue(&ctrl_poll_work_q, &work_ctlr_poll); @@ -64,6 +66,8 @@ static void ctlr_poll_timer_handler(struct k_timer *timer_id) static void wdt_timeout_cb(int channel_id, void *user_data) { + ARG_UNUSED(channel_id); + ARG_UNUSED(user_data); ERR_CHK_MSG(-ETIMEDOUT, "No response from IPC or controller"); } @@ -139,4 +143,4 @@ int bt_mgmt_stop_watchdog(void) k_timer_stop(&ctlr_poll_timer); int ret = task_wdt_delete(wdt_ch_id); return ret; -} \ No newline at end of file +} diff --git a/src/bluetooth/bt_management/dfu/bt_mgmt_dfu.c b/src/bluetooth/bt_management/dfu/bt_mgmt_dfu.c index 8c50cd94..d1560b9a 100644 --- a/src/bluetooth/bt_management/dfu/bt_mgmt_dfu.c +++ b/src/bluetooth/bt_management/dfu/bt_mgmt_dfu.c @@ -55,11 +55,14 @@ static void smp_adv(void) /* These callbacks are to override callback registed in module le_audio_ */ static void dfu_connected_cb(struct bt_conn *conn, uint8_t err) { + ARG_UNUSED(conn); + ARG_UNUSED(err); LOG_INF("SMP connected\n"); } static void dfu_disconnected_cb(struct bt_conn *conn, uint8_t reason) { + ARG_UNUSED(conn); LOG_INF("SMP disconnected 0x%02x\n", reason); } diff --git a/src/bluetooth/bt_rendering_and_capture/volume/bt_vol_rend.c b/src/bluetooth/bt_rendering_and_capture/volume/bt_vol_rend.c index 8f1f952f..578abaf6 100644 --- a/src/bluetooth/bt_rendering_and_capture/volume/bt_vol_rend.c +++ b/src/bluetooth/bt_rendering_and_capture/volume/bt_vol_rend.c @@ -24,6 +24,7 @@ LOG_MODULE_REGISTER(bt_vol_rend, CONFIG_BT_VOL_LOG_LEVEL); */ static void vcs_state_rend_cb_handler(struct bt_conn *conn, int err, uint8_t volume, uint8_t mute) { + ARG_UNUSED(conn); int ret; static uint8_t prev_mute = 0; @@ -63,6 +64,7 @@ static void vcs_state_rend_cb_handler(struct bt_conn *conn, int err, uint8_t vol */ static void vcs_flags_rend_cb_handler(struct bt_conn *conn, int err, uint8_t flags) { + ARG_UNUSED(conn); if (err) { LOG_ERR("VCS flag callback error: %d", err); } else { diff --git a/src/bluetooth/gatt_services/audio_response_service.c b/src/bluetooth/gatt_services/audio_response_service.c index a8b71ed6..921ae3b7 100644 --- a/src/bluetooth/gatt_services/audio_response_service.c +++ b/src/bluetooth/gatt_services/audio_response_service.c @@ -449,6 +449,9 @@ static protocol_status_t dispatch_transfer_abort(void *context, static ssize_t write_transfer_control(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); static const audio_response_transfer_control_handler_t handlers = { .start = dispatch_transfer_start, .commit = dispatch_transfer_commit, @@ -498,6 +501,9 @@ static ssize_t write_transfer_control(struct bt_conn *conn, const struct bt_gatt static ssize_t write_transfer_data(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); protocol_reader_t reader = { .buffer = buf, .size = len, .offset = 0 }; uint16_t transfer_id = 0; uint32_t sample_offset = 0; @@ -564,6 +570,9 @@ static ssize_t write_audio_response_config(struct bt_conn *conn, const struct bt const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); audio_response_config_t config; size_t bytes_read = 0; int ret; @@ -612,6 +621,7 @@ static ssize_t write_audio_response_config(struct bt_conn *conn, const struct bt */ static void transfer_status_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); transfer_status_notifications_enabled = value == BT_GATT_CCC_NOTIFY; LOG_DBG("Audio response transfer status notifications %s", transfer_status_notifications_enabled ? "enabled" : "disabled"); @@ -622,6 +632,7 @@ static void transfer_status_ccc_changed(const struct bt_gatt_attr *attr, uint16_ */ static void result_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); result_notifications_enabled = value == BT_GATT_CCC_NOTIFY; LOG_DBG("Audio response result notifications %s", result_notifications_enabled ? "enabled" : "disabled"); @@ -726,6 +737,7 @@ static void restore_audio_after_measurement(void) */ static void measurement_work_handler(struct k_work *work) { + ARG_UNUSED(work); int ret; LOG_INF("Starting audio response measurement: id=%u transfer_id=%u samples=%u volume=%.2f points=%u", @@ -837,6 +849,7 @@ static void notify_result(void) */ static void measurement_complete_work_handler(struct k_work *work) { + ARG_UNUSED(work); LOG_INF("Audio response capture complete: id=%u", pending_config.id); restore_audio_after_measurement(); @@ -864,6 +877,7 @@ static void measurement_complete_work_handler(struct k_work *work) */ static void transfer_ready_work_handler(struct k_work *work) { + ARG_UNUSED(work); k_mutex_lock(&service_mutex, K_FOREVER); if (transfer.active && !transfer.committed) { (void)notify_transfer_status(AUDIO_RESPONSE_TRANSFER_READY, @@ -877,6 +891,7 @@ static void transfer_ready_work_handler(struct k_work *work) */ static void transfer_timeout_work_handler(struct k_work *work) { + ARG_UNUSED(work); k_mutex_lock(&service_mutex, K_FOREVER); if (transfer.active && !transfer.committed) { LOG_WRN("Transfer timed out: id=%u received=%u/%u", transfer.id, diff --git a/src/bluetooth/gatt_services/battery_service.cpp b/src/bluetooth/gatt_services/battery_service.cpp index 71e0aa5a..cdad8beb 100644 --- a/src/bluetooth/gatt_services/battery_service.cpp +++ b/src/bluetooth/gatt_services/battery_service.cpp @@ -75,6 +75,7 @@ struct battery_health_status health_status; static void battery_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); notify_enabled = (value == BT_GATT_CCC_NOTIFY); #ifdef CONFIG_LOG if (notify_enabled) LOG_INF("subscribe to battery level"); @@ -158,6 +159,7 @@ BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_HEALTH_STATUS, int bt_send_battery_level(struct battery_data * data) { + ARG_UNUSED(data); if (!notify_enabled) { LOG_WRN("battery level not subscribed"); return -EACCES; @@ -166,4 +168,4 @@ int bt_send_battery_level(struct battery_data * data) LOG_INF("notify battery level change"); return bt_gatt_notify(NULL, &battery_service.attrs[2], &msg.battery_level, sizeof(msg.battery_level)); -} \ No newline at end of file +} diff --git a/src/bluetooth/gatt_services/button_service.c b/src/bluetooth/gatt_services/button_service.c index 6399cf4f..8addf53b 100644 --- a/src/bluetooth/gatt_services/button_service.c +++ b/src/bluetooth/gatt_services/button_service.c @@ -23,6 +23,7 @@ static K_THREAD_STACK_DEFINE(thread_stack, CONFIG_BUTTON_MSG_SUB_STACK_SIZE); static void button_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); notify_enabled = (value == BT_GATT_CCC_NOTIFY); } @@ -103,4 +104,4 @@ int init_button_service() { } return 0; -} \ No newline at end of file +} diff --git a/src/bluetooth/gatt_services/led_service.cpp b/src/bluetooth/gatt_services/led_service.cpp index 547d53e2..f12a82f1 100644 --- a/src/bluetooth/gatt_services/led_service.cpp +++ b/src/bluetooth/gatt_services/led_service.cpp @@ -10,6 +10,9 @@ static ssize_t write_led(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); if (len != 3U) { LOG_INF("Write led: Incorrect data length"); return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); @@ -30,6 +33,9 @@ static ssize_t write_state(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); if (len != 1U) { LOG_INF("Write led: Incorrect data length"); return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); diff --git a/src/buttons/Button.cpp b/src/buttons/Button.cpp index c0ea3b53..51744664 100644 --- a/src/buttons/Button.cpp +++ b/src/buttons/Button.cpp @@ -15,6 +15,8 @@ struct gpio_callback Button::button_cb_data; void Button::button_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); if (!(pins & BIT(BUTTON_EARABLE))) { return; } diff --git a/src/time_sync/time_sync.c b/src/time_sync/time_sync.c index 4d8a0c16..feebf9f3 100644 --- a/src/time_sync/time_sync.c +++ b/src/time_sync/time_sync.c @@ -73,6 +73,7 @@ static ssize_t write_rtt_request( uint16_t offset, uint8_t flags ) { + ARG_UNUSED(flags); uint64_t rx_time = get_current_time_us(); if (offset != 0) { @@ -128,6 +129,9 @@ static ssize_t write_time_offset( uint16_t offset, uint8_t flags ) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(flags); if (offset != 0) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } @@ -180,6 +184,7 @@ uint64_t get_time_since_boot_us(void) { void rtt_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); LOG_DBG("RTT characteristic CCCD changed: %u", value); notify_rtt_enabled = (value == BT_GATT_CCC_NOTIFY); } diff --git a/src/utils/error_handler.c b/src/utils/error_handler.c index 7e5ae97e..024bf4ab 100644 --- a/src/utils/error_handler.c +++ b/src/utils/error_handler.c @@ -23,6 +23,7 @@ static const struct gpio_dt_spec center_led_b = GPIO_DT_SPEC_GET(DT_NODELABEL(rg void error_handler(unsigned int reason, const struct arch_esf *esf) { + ARG_UNUSED(esf); #if (CONFIG_DEBUG) LOG_ERR("Caught system error -- reason %d. Entering infinite loop", reason); LOG_PANIC(); From 3267710c8cf500fb822da307039cdbd826583293 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 15:47:15 +0200 Subject: [PATCH 68/77] fix: initialize structures and remove dead locals --- src/SensorManager/BMA580/BMA580_Sensor.h | 2 +- src/SensorManager/BMA580/bma5.c | 12 +++++------- src/SensorManager/Microphone.cpp | 4 +--- src/Wire/TWIM.cpp | 4 +--- src/utils/macros/macros_custom.h | 4 ++-- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/SensorManager/BMA580/BMA580_Sensor.h b/src/SensorManager/BMA580/BMA580_Sensor.h index d8c0574d..1ff93349 100644 --- a/src/SensorManager/BMA580/BMA580_Sensor.h +++ b/src/SensorManager/BMA580/BMA580_Sensor.h @@ -164,7 +164,7 @@ class BMA580 { uint8_t fifo_data[BMA580_FIFO_RAW_DATA_BUFFER_SIZE] = { 0 }; /* Initialize FIFO frame structure */ - struct bma5_fifo_frame fifoframe = { 0 }; + struct bma5_fifo_frame fifoframe = {}; /* Set FIFO water-mark level */ uint16_t fifo_watermark_level = WATERMARK_LEVEL; diff --git a/src/SensorManager/BMA580/bma5.c b/src/SensorManager/BMA580/bma5.c index 62aa8f95..5b15f41d 100644 --- a/src/SensorManager/BMA580/bma5.c +++ b/src/SensorManager/BMA580/bma5.c @@ -1927,17 +1927,15 @@ static int8_t unpack_accel_sensor_time_16_bit_data(uint8_t frame_header, if ((((*data_start_index) + BMA5_FIFO_16_BIT_ACCEL_XYZ_DATA) <= fifo->fifo_avail_len) && (frame_header != BMA5_FIFO_SENS_TIME_EN_DEDI_FRM)) { - if (frame_header & BMA5_FIFO_ACC_X_MSK) - if (frame_header & BMA5_FIFO_ACC_X_MSK) - { + if (frame_header & BMA5_FIFO_ACC_X_MSK) { + if (frame_header & BMA5_FIFO_ACC_X_MSK) { /* Accel raw x data */ data_lsb = fifo->data[(*data_start_index)++]; data_msb = fifo->data[(*data_start_index)++]; accel_data->x = (int16_t)((data_msb << 8) | data_lsb); - } - else - { - accel_data->x = 0; + } else { + accel_data->x = 0; + } } if (frame_header & BMA5_FIFO_ACC_Y_MSK) diff --git a/src/SensorManager/Microphone.cpp b/src/SensorManager/Microphone.cpp index aaddcdb9..45913d52 100644 --- a/src/SensorManager/Microphone.cpp +++ b/src/SensorManager/Microphone.cpp @@ -56,8 +56,6 @@ bool Microphone::init(struct k_msgq * queue) { void Microphone::start(int sample_rate_idx) { //ARG_UNUSED(sample_rate_idx); - int ret; - if (!_active) return; LOG_INF("Starting Microphone at %f Hz", sample_rates.sample_rates[sample_rate_idx]); @@ -85,4 +83,4 @@ void Microphone::stop() { //audio_datapath_decimator_cleanup(); _running = false; -} \ No newline at end of file +} diff --git a/src/Wire/TWIM.cpp b/src/Wire/TWIM.cpp index 057dada0..93959680 100644 --- a/src/Wire/TWIM.cpp +++ b/src/Wire/TWIM.cpp @@ -7,8 +7,6 @@ LOG_MODULE_REGISTER(twim, CONFIG_AUDIO_DATAPATH_LOG_LEVEL); TWIM::TWIM(const struct device * _device) : master(_device) {} void TWIM::begin() { - int ret; - if (_active) return; _active = true; @@ -52,4 +50,4 @@ void TWIM::release() { TWIM I2C1(DEVICE_DT_GET(DT_NODELABEL(i2c1))); TWIM I2C2(DEVICE_DT_GET(DT_NODELABEL(i2c2))); -TWIM I2C3(DEVICE_DT_GET(DT_NODELABEL(i2c3))); \ No newline at end of file +TWIM I2C3(DEVICE_DT_GET(DT_NODELABEL(i2c3))); diff --git a/src/utils/macros/macros_custom.h b/src/utils/macros/macros_custom.h index 5e102938..fb900645 100644 --- a/src/utils/macros/macros_custom.h +++ b/src/utils/macros/macros_custom.h @@ -15,7 +15,7 @@ ZBUS_SUBSCRIBER_DEFINE(name ## _sub, CONFIG_BUTTON_MSG_SUB_QUEUE_SIZE); \ ZBUS_CHAN_DECLARE(name ## _chan); \ static K_THREAD_STACK_DEFINE(thread_stack, CONFIG_BUTTON_MSG_SUB_STACK_SIZE); \ \ -static void write_ ## name ## _gatt(void)\ +static void write_ ## name ## _gatt(void)\ {\ int ret;\ const struct zbus_channel *chan;\ @@ -60,4 +60,4 @@ int init_ ## name ## _service() {\ } #endif -#endif \ No newline at end of file +#endif From f22ad3767652be3ba4985701bfbc86e80bfbf68e Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 15:47:20 +0200 Subject: [PATCH 69/77] fix: eliminate shadowed declarations --- src/Battery/PowerManager.cpp | 18 +++++- src/SD_Card/Benchmark/Benchmark.cpp | 6 +- src/SD_Card/SDLogger/SDLogger.cpp | 7 ++- .../SD_Card_Manager/SD_Card_Manager.cpp | 34 ++++++----- src/SD_Card/SD_Card_Manager/SD_Card_Manager.h | 11 +++- src/SensorManager/BMA580/BMA580_Sensor.cpp | 51 ++++++++-------- src/SensorManager/MLX90632/MLX90632.cpp | 13 +++-- src/SensorManager/SensorManager.cpp | 12 ++-- src/audio/audio_datapath.c | 34 ++++++----- src/audio/streamctrl.c | 18 +++--- .../conn_intvl_exp_backoff_lin_inc.cpp | 9 +-- .../conn_interval/conn_intvl_linear.cpp | 9 +-- .../bt_stream/unicast/unicast_server.c | 58 +++++++++++++------ src/bluetooth/gatt_services/sensor_service.c | 24 +++++--- src/drivers/ADAU1860.cpp | 17 +++--- src/utils/StateIndicator.cpp | 6 +- 16 files changed, 201 insertions(+), 126 deletions(-) diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index d65826fe..d38de1d2 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -55,15 +55,24 @@ static struct battery_data msg; //LoadSwitch PowerManager::v1_8_switch(GPIO_DT_SPEC_GET(DT_NODELABEL(load_switch), gpios)); void PowerManager::fuel_gauge_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); LOG_DBG("Fuel Gauge GPOUT Interrupt"); k_work_submit(&fuel_gauge_work); } void PowerManager::battery_controller_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); k_work_submit(&battery_controller_work); } void PowerManager::power_good_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); bool power_good = battery_controller.power_connected(); k_work_submit(&fuel_gauge_work); @@ -78,16 +87,19 @@ void PowerManager::power_good_callback(const struct device *dev, struct gpio_cal } void PowerManager::power_down_work_handler(struct k_work * work) { + ARG_UNUSED(work); power_manager.power_down(); } void PowerManager::charge_ctrl_work_handler(struct k_work * work) { + ARG_UNUSED(work); power_manager.charge_task(); // Schedule next execution k_work_schedule(&charge_ctrl_delayable, power_manager.chrg_interval); } void PowerManager::battery_controller_work_handler(struct k_work * work) { + ARG_UNUSED(work); button_state state; //uint8_t val = gpio_pin_get_dt(&power_manager.error_led); @@ -107,6 +119,7 @@ void PowerManager::battery_controller_work_handler(struct k_work * work) { } void PowerManager::fuel_gauge_work_handler(struct k_work * work) { + ARG_UNUSED(work); int ret; battery_level_status status; @@ -197,7 +210,7 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { uint8_t fault = battery_controller.read_fault(); // Battery fuel gauge status - bat_status status = fuel_gauge.battery_status(); + bat_status fault_status = fuel_gauge.battery_status(); voltage = fuel_gauge.voltage(); current = fuel_gauge.current(); @@ -235,7 +248,7 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { LOG_DBG("------------------ Battery Info ------------------"); LOG_DBG("Battery Status:"); LOG_DBG(" Present: %d, Full Charge: %d, Full Discharge: %d", - status.BATTPRES, status.FC, status.FD); + fault_status.BATTPRES, fault_status.FC, fault_status.FD); // Basic measurements LOG_DBG("Basic Measurements:"); @@ -675,6 +688,7 @@ void PowerManager::charge_task() { } int cmd_setup_fuel_gauge(const struct shell *shell, size_t argc, const char **argv) { + ARG_UNUSED(shell); ARG_UNUSED(argc); ARG_UNUSED(argv); diff --git a/src/SD_Card/Benchmark/Benchmark.cpp b/src/SD_Card/Benchmark/Benchmark.cpp index 54a84ed5..48c0750e 100644 --- a/src/SD_Card/Benchmark/Benchmark.cpp +++ b/src/SD_Card/Benchmark/Benchmark.cpp @@ -1,7 +1,7 @@ #include "Benchmark.h" -Benchmark::Benchmark(void (*function)()) { - this->function = function; +Benchmark::Benchmark(void (*benchmark_function)()) { + this->function = benchmark_function; } void Benchmark::start() { @@ -18,4 +18,4 @@ uint32_t Benchmark::get_time() { uint32_t Benchmark::get_cycles() { return this->end_cycles - this->start_cycles; -} \ No newline at end of file +} diff --git a/src/SD_Card/SDLogger/SDLogger.cpp b/src/SD_Card/SDLogger/SDLogger.cpp index 335d4b1b..4635c1da 100644 --- a/src/SD_Card/SDLogger/SDLogger.cpp +++ b/src/SD_Card/SDLogger/SDLogger.cpp @@ -102,6 +102,7 @@ void sensor_listener_cb(const struct zbus_channel *chan) { void sd_listener_callback(const struct zbus_channel *chan) { + ARG_UNUSED(chan); const struct sd_msg * sd_msg_event = (sd_msg *)zbus_chan_const_msg(&sd_card_chan); if (sdlogger.is_open && sd_msg_event->removed) { @@ -438,9 +439,9 @@ int SDLogger::write_sensor_data(const void* const* data_blocks, const size_t* le return 0; } -int SDLogger::write_sensor_data(const sensor_data& msg) { - const size_t data_size = sizeof(sensor_data) - sizeof(msg.data) + msg.size; - const void* msg_ptr = &msg; +int SDLogger::write_sensor_data(const sensor_data& sensor_message) { + const size_t data_size = sizeof(sensor_data) - sizeof(sensor_message.data) + sensor_message.size; + const void* msg_ptr = &sensor_message; return write_sensor_data(&msg_ptr, &data_size, 1); } diff --git a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp index 3a7dec7e..1820f863 100644 --- a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp +++ b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp @@ -35,6 +35,7 @@ bool SDCardManager::sd_inserted() { } void SDCardManager::unmount_work_handler(struct k_work *work) { + ARG_UNUSED(work); int ret; bool _inserted = sdcard_manager.sd_inserted(); @@ -58,10 +59,13 @@ void SDCardManager::unmount_work_handler(struct k_work *work) { K_WORK_DELAYABLE_DEFINE(SDCardManager::unmount_work, SDCardManager::unmount_work_handler); void SDCardManager::sd_card_state_change_isr(const struct device *dev, struct gpio_callback *cb, uint32_t pins) { + ARG_UNUSED(dev); + ARG_UNUSED(cb); + ARG_UNUSED(pins); k_work_reschedule(&sdcard_manager.unmount_work, SD_DEBOUNCE_MS); } -SDCardManager::SDCardManager(): path(SD_ROOT_PATH) { +SDCardManager::SDCardManager(): current_path(SD_ROOT_PATH) { fs_dir_t_init(&this->dirp); } @@ -240,8 +244,8 @@ int SDCardManager::mount() { return ret; } - LOG_DBG("Root dir: %s", this->path.c_str()); - ret = fs_opendir(&this->dirp, this->path.c_str()); + LOG_DBG("Root dir: %s", this->current_path.c_str()); + ret = fs_opendir(&this->dirp, this->current_path.c_str()); k_mutex_unlock(&m_sem_sd_mngr_oper_ongoing); if (ret) { release_ls(); @@ -283,7 +287,7 @@ int SDCardManager::cd(std::string path) { return ret; } - std::string abs_path_name = create_path(this->path, path); + std::string abs_path_name = create_path(this->current_path, path); LOG_DBG("abs path name:\t%s", abs_path_name.c_str()); @@ -293,8 +297,8 @@ int SDCardManager::cd(std::string path) { if (ret) { LOG_ERR("Open SD card dir failed: %d", ret); // Try to revert to the previous path if the new one fails - if (this->path != path) { - int rret = fs_opendir(&this->dirp, this->path.c_str()); + if (this->current_path != path) { + int rret = fs_opendir(&this->dirp, this->current_path.c_str()); if (rret) { LOG_ERR("Failed to cd back to previous dir: %d", rret); } @@ -303,7 +307,7 @@ int SDCardManager::cd(std::string path) { return ret; } - this->path = abs_path_name; + this->current_path = abs_path_name; k_mutex_unlock(&m_sem_sd_mngr_oper_ongoing); return 0; @@ -326,7 +330,7 @@ int SDCardManager::ls(char *buf, size_t *buf_size) { return -ENODEV; } - if (this->path.length() > CONFIG_FS_FATFS_MAX_LFN) { + if (this->current_path.length() > CONFIG_FS_FATFS_MAX_LFN) { LOG_ERR("Path is too long"); k_mutex_unlock(&m_sem_sd_mngr_oper_ongoing); return -FR_INVALID_NAME; @@ -386,7 +390,7 @@ int SDCardManager::mkdir(std::string path) { return -FR_INVALID_NAME; } - std::string abs_path_name = create_path(this->path, path); + std::string abs_path_name = create_path(this->current_path, path); ret = fs_mkdir(abs_path_name.c_str()); if (ret) { @@ -418,7 +422,7 @@ int SDCardManager::open_file(std::string path, bool write, bool append, bool cre return -ENAMETOOLONG; } - std::string abs_path_name = create_path(this->path, path); + std::string abs_path_name = create_path(this->current_path, path); if (this->tracked_file.is_open) { LOG_ERR("File is already open"); @@ -447,7 +451,7 @@ int SDCardManager::open_file(std::string path, bool write, bool append, bool cre } this->tracked_file.is_open = true; - this->path = abs_path_name; + this->current_path = abs_path_name; k_mutex_unlock(&m_sem_sd_mngr_oper_ongoing); return 0; @@ -479,10 +483,10 @@ int SDCardManager::close_file() { return ret; } - LOG_DBG("File %s closed", this->path.c_str()); - size_t last_slash_pos = this->path.find_last_of("/"); + LOG_DBG("File %s closed", this->current_path.c_str()); + size_t last_slash_pos = this->current_path.find_last_of("/"); if (last_slash_pos != std::string::npos) { - this->path = this->path.substr(0, last_slash_pos); + this->current_path = this->current_path.substr(0, last_slash_pos); } this->tracked_file.is_open = false; @@ -640,7 +644,7 @@ int SDCardManager::rm(std::string path) { return -FR_INVALID_NAME; } - std::string abs_path_name = create_path(this->path, path); + std::string abs_path_name = create_path(this->current_path, path); ret = fs_unlink(abs_path_name.c_str()); if (ret) { diff --git a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.h b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.h index a83a8534..804cc504 100644 --- a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.h +++ b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.h @@ -162,7 +162,7 @@ class SDCardManager { bool is_mounted() { return this->mounted; } private: - std::string path; + std::string current_path; bool mounted = false; struct fs_dir_t dirp; @@ -173,14 +173,21 @@ class SDCardManager { FATFS fat_fs; struct fs_mount_t mnt_pt = { + .node = {}, .type = FS_FATFS, + .mnt_point = nullptr, .fs_data = &fat_fs, + .storage_dev = nullptr, + .mountp_len = 0, + .fs = nullptr, + .flags = 0, }; struct tracked_fs_file_t { struct fs_file_t filep; bool is_open; } tracked_file = { + .filep = {}, .is_open = false, }; @@ -199,4 +206,4 @@ class SDCardManager { extern SDCardManager sdcard_manager; -#endif \ No newline at end of file +#endif diff --git a/src/SensorManager/BMA580/BMA580_Sensor.cpp b/src/SensorManager/BMA580/BMA580_Sensor.cpp index 779f69ed..a3e4cc76 100644 --- a/src/SensorManager/BMA580/BMA580_Sensor.cpp +++ b/src/SensorManager/BMA580/BMA580_Sensor.cpp @@ -61,14 +61,14 @@ BMA580_dev_inf dev_info = { BMA5_INTF_RET_TYPE bma5_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr) { int ret; - BMA580_dev_inf * dev_info = (BMA580_dev_inf *) intf_ptr; + BMA580_dev_inf * device_info = (BMA580_dev_inf *) intf_ptr; - dev_info->i2c_dev->aquire(); + device_info->i2c_dev->aquire(); - ret = i2c_burst_read(dev_info->i2c_dev->master, dev_info->addr, reg_addr, reg_data, len); + ret = i2c_burst_read(device_info->i2c_dev->master, device_info->addr, reg_addr, reg_data, len); if (ret) LOG_WRN("I2C read failed: %d\n", ret); - dev_info->i2c_dev->release(); + device_info->i2c_dev->release(); return 0; } @@ -77,14 +77,14 @@ BMA5_INTF_RET_TYPE bma5_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t l */ BMA5_INTF_RET_TYPE bma5_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint32_t len, void *intf_ptr) { - BMA580_dev_inf * dev_info = (BMA580_dev_inf *) intf_ptr; + BMA580_dev_inf * device_info = (BMA580_dev_inf *) intf_ptr; - dev_info->i2c_dev->aquire(); + device_info->i2c_dev->aquire(); - int ret = i2c_burst_write(dev_info->i2c_dev->master, dev_info->addr, reg_addr, reg_data, len); + int ret = i2c_burst_write(device_info->i2c_dev->master, device_info->addr, reg_addr, reg_data, len); if (ret) LOG_WRN("I2C write failed: %d", ret); - dev_info->i2c_dev->release(); + device_info->i2c_dev->release(); return 0; } @@ -94,6 +94,7 @@ BMA5_INTF_RET_TYPE bma5_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uin */ void bma5_delay_us(uint32_t period, void *intf_ptr) { + ARG_UNUSED(intf_ptr); k_usleep(period); } @@ -160,7 +161,7 @@ int8_t bma5_interface_init(struct bma5_dev *bma5, uint8_t intf, enum bma5_contex /*! * @brief This internal API is used to enable accel and interrupt configuration settings. */ -int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) +int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *device) { int8_t rslt; uint8_t n_ints = 1; @@ -171,10 +172,10 @@ int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) int_config.int_src = BMA5_INT_1; /* Get accel configurations */ - rslt = bma5_get_acc_conf_0(&sensor_ctrl, dev); + rslt = bma5_get_acc_conf_0(&sensor_ctrl, device); bma5_check_rslt("bma5_get_acc_conf_0", rslt); - rslt = bma5_get_acc_conf(&acc_cfg, dev); + rslt = bma5_get_acc_conf(&acc_cfg, device); bma5_check_rslt("bma5_get_acc_conf", rslt); /* Set accel configurations */ @@ -188,7 +189,7 @@ int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) acc_cfg.noise_mode = BMA5_NOISE_MODE_LOWER_NOISE; acc_cfg.acc_drdy_int_auto_clear = BMA5_ACC_DRDY_INT_AUTO_CLEAR_DISABLED; - rslt = bma5_set_acc_conf(&acc_cfg, dev); + rslt = bma5_set_acc_conf(&acc_cfg, device); bma5_check_rslt("bma5_get_acc_conf", rslt); /*LOG_DBG("Accel configurations"); @@ -203,7 +204,7 @@ int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) /* Enable accel */ sensor_ctrl = BMA5_SENSOR_CTRL_ENABLE; - rslt = bma5_set_acc_conf_0(sensor_ctrl, dev); + rslt = bma5_set_acc_conf_0(sensor_ctrl, device); bma5_check_rslt("bma5_set_acc_conf_0", rslt); if (rslt == BMA5_OK) @@ -211,20 +212,20 @@ int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) LOG_DBG("Accel enabled"); } - rslt = bma5_get_acc_conf_0(&sensor_ctrl, dev); + rslt = bma5_get_acc_conf_0(&sensor_ctrl, device); bma5_check_rslt("bma5_set_acc_conf_0", rslt); - rslt = bma5_get_acc_conf(&get_acc_cfg, dev); + rslt = bma5_get_acc_conf(&get_acc_cfg, device); bma5_check_rslt("bma5_get_acc_conf", rslt); - rslt = bma5_get_int_conf(&int_config, n_ints, dev); + rslt = bma5_get_int_conf(&int_config, n_ints, device); bma5_check_rslt("bma5_get_int_conf", rslt); int_config.int_conf.int_mode = BMA5_INT1_MODE_LATCHED; int_config.int_conf.int_od = BMA5_INT1_OD_PUSH_PULL; int_config.int_conf.int_lvl = BMA5_INT1_LVL_ACTIVE_HIGH; - rslt = bma5_set_int_conf(&int_config, n_ints, dev); + rslt = bma5_set_int_conf(&int_config, n_ints, device); bma5_check_rslt("bma5_set_int_conf", rslt); /*LOG_DBG("\nInt Configurations"); @@ -238,24 +239,24 @@ int8_t BMA580::get_accel_and_int_settings(struct bma5_dev *dev) /*! * @brief This internal API gets FIFO configurations. */ -int8_t BMA580::get_fifo_conf(const struct bma5_fifo_conf *fifo_conf, struct bma5_dev *dev) +int8_t BMA580::get_fifo_conf(const struct bma5_fifo_conf *fifo_config, struct bma5_dev *device) { int8_t rslt; - struct bma5_fifo_conf read_fifo_conf = { 0 }; + struct bma5_fifo_conf read_fifo_conf{}; /* Set FIFO configuration. * NOTE 1: FIFO works only on header mode */ - rslt = bma5_set_fifo_conf(fifo_conf, dev); + rslt = bma5_set_fifo_conf(fifo_config, device); bma5_check_rslt("bma5_set_fifo_conf", rslt); /* Get FIFO configuration register */ - rslt = bma5_get_fifo_conf(&read_fifo_conf, dev); + rslt = bma5_get_fifo_conf(&read_fifo_conf, device); bma5_check_rslt("bma5_get_fifo_conf", rslt); return rslt; } -int BMA580::init(int odr, int fifo_watermark_level) { +int BMA580::init(int odr, int watermark_level) { int8_t rslt; struct bma580_int_map int_map, get_int_map; //struct bma5_fifo_conf fifo_conf; @@ -315,7 +316,7 @@ int BMA580::init(int odr, int fifo_watermark_level) { /* Update FIFO structure */ fifoframe.data = fifo_data; - rslt = bma5_set_fifo_wm(fifo_watermark_level, &dev); + rslt = bma5_set_fifo_wm(watermark_level, &dev); bma5_check_rslt("bma5_set_fifo_wm", rslt); return rslt; @@ -336,7 +337,7 @@ int BMA580::stop() { int BMA580::read(bma5_sens_fifo_axes_data_16_bit *fifo_accel_data) { int8_t rslt = BMA5_OK; uint8_t n_status = 1; - struct bma580_int_status_types int_status = { 0 }; + struct bma580_int_status_types int_status{}; fifoframe.fifo_avail_frames = 0; @@ -364,4 +365,4 @@ int BMA580::read(bma5_sens_fifo_axes_data_16_bit *fifo_accel_data) { } return fifoframe.fifo_avail_frames; -} \ No newline at end of file +} diff --git a/src/SensorManager/MLX90632/MLX90632.cpp b/src/SensorManager/MLX90632/MLX90632.cpp index f0b1f3eb..7f2bab1b 100644 --- a/src/SensorManager/MLX90632/MLX90632.cpp +++ b/src/SensorManager/MLX90632/MLX90632.cpp @@ -322,9 +322,9 @@ float MLX90632::gatherSensorTemp(status &returnError) double AMB = (sixRAM / 12.0) / VRta * pow(2, 19); - double sensorTemp = P_O + (AMB - P_R) / P_G + P_T * pow((AMB - P_R), 2); + double calculated_sensor_temp = P_O + (AMB - P_R) / P_G + P_T * pow((AMB - P_R), 2); - return(sensorTemp); + return(calculated_sensor_temp); } //Returns true if device is busy doing measurement @@ -346,9 +346,9 @@ bool MLX90632::eepromBusy() //Returns the cycle_pos from status register. cycle_pos is 0 to 31 uint8_t MLX90632::getCyclePosition() { - uint16_t status = getStatus() >> BIT_CYCLE_POS; //Shave off last two bits - status &= 0x1F; //Get lower 5 bits. - return (status); + uint16_t cycle_status = getStatus() >> BIT_CYCLE_POS; //Shave off last two bits + cycle_status &= 0x1F; //Get lower 5 bits. + return (cycle_status); } //Returns true if new data is available @@ -445,6 +445,7 @@ uint8_t MLX90632::getMode(status &returnError) //to something unknown you'll need to use the I2C scanner sketch to detect it again void MLX90632::writeI2CAddress(uint8_t newAddress) { + ARG_UNUSED(newAddress); } @@ -621,4 +622,4 @@ float MLX90632::getSampleRate() { result = (1 << val1) / 2; return result; -} \ No newline at end of file +} diff --git a/src/SensorManager/SensorManager.cpp b/src/SensorManager/SensorManager.cpp index 40ff9775..36f6fa44 100644 --- a/src/SensorManager/SensorManager.cpp +++ b/src/SensorManager/SensorManager.cpp @@ -71,6 +71,9 @@ static const char sensor_manager_auto_off_token[] = "SensorManager"; static void config_work_handler(struct k_work *work); void sensor_chan_update(void *p1, void *p2, void *p3) { + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); int ret; while (1) { @@ -190,6 +193,7 @@ EdgeMlSensor * get_sensor(enum sensor_id id) { // Worker-Funktion für die Sensor-Konfiguration static void config_work_handler(struct k_work *work) { + ARG_UNUSED(work); int ret; struct sensor_config config; @@ -243,9 +247,9 @@ static void config_work_handler(struct k_work *work) { LOG_INF("Starting SDLogger with recording name prefix: %s", recording_name_prefix); // Start SDLogger with timestamp-based filename std::string filename = recording_name_prefix + std::to_string(micros()); - int ret = sdlogger.begin(filename); - if (ret == 0) state_indicator.set_sd_state(SD_RECORDING); - else LOG_ERR("Failed to start SDLogger, ret: %d", ret); + int logger_ret = sdlogger.begin(filename); + if (logger_ret == 0) state_indicator.set_sd_state(SD_RECORDING); + else LOG_ERR("Failed to start SDLogger, ret: %d", logger_ret); } } else if (sd_sensors.find(config.sensorId) != sd_sensors.end()) { sd_sensors.erase(config.sensorId); @@ -278,4 +282,4 @@ void config_sensor(struct sensor_config * config) { //k_work_queue_drain(&sensor_work_q, true); k_work_submit(&config_work); //k_work_queue_unplug(&sensor_work_q); -} \ No newline at end of file +} diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 20fb6c52..3aa3a370 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -189,7 +189,7 @@ static struct { static struct k_msgq * sensor_queue; //K_MSGQ_DEFINE(rx_queue, sizeof(struct audio_data), 16, 4); -extern struct k_msgq_t encoder_queue; +extern struct k_msgq encoder_queue; // Definition eines zbus-Kanals ZBUS_CHAN_DEFINE(audio_channel, struct audio_data, NULL, NULL, ZBUS_OBSERVERS_EMPTY, ZBUS_MSG_INIT(0)); @@ -235,6 +235,10 @@ static int16_t decimated_audio[BLOCK_SIZE_BYTES / sizeof(int16_t)]; // Funktion für den neuen Thread static void data_thread(void *arg1, void *arg2, void *arg3) { + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + //struct audio_data audio_item; void *tmp_pcm_raw_data[CONFIG_FIFO_FRAME_SPLIT_NUM]; //char pcm_raw_data[FRAME_SIZE_BYTES]; @@ -258,8 +262,6 @@ static void data_thread(void *arg1, void *arg2, void *arg3) data_fifo_block_free(ctrl_blk.in.fifo, tmp_pcm_raw_data[i]); - unsigned int logger_signaled; - if (_record_to_sd || _record_to_buffer) { /* Decimate audio data from 48kHz to the desired sampling rate */ int16_t *audio_block = (int16_t *)(audio_item.data + (i * BLOCK_SIZE_BYTES)); @@ -274,7 +276,7 @@ static void data_thread(void *arg1, void *arg2, void *arg3) // Generic buffer recording if (_record_to_buffer && _record_buffer != NULL) { - for(int i = 0; i < decimated_frames; i++) { + for(int frame_index = 0; frame_index < decimated_frames; frame_index++) { _record_current_index++; // Skip samples during initial drop period @@ -289,15 +291,15 @@ static void data_thread(void *arg1, void *arg2, void *arg3) if (_record_left && _record_right) { // Stereo recording - store both channels if (buffer_index * 2 + 1 < _record_num_samples) { - _record_buffer[buffer_index * 2] = decimated_audio[2 * i]; // Left - _record_buffer[buffer_index * 2 + 1] = decimated_audio[2 * i + 1]; // Right + _record_buffer[buffer_index * 2] = decimated_audio[2 * frame_index]; // Left + _record_buffer[buffer_index * 2 + 1] = decimated_audio[2 * frame_index + 1]; // Right } } else if (_record_left) { // Left channel only - _record_buffer[buffer_index] = decimated_audio[2 * i]; + _record_buffer[buffer_index] = decimated_audio[2 * frame_index]; } else if (_record_right) { // Right channel only - _record_buffer[buffer_index] = decimated_audio[2 * i + 1]; + _record_buffer[buffer_index] = decimated_audio[2 * frame_index + 1]; } } @@ -329,17 +331,17 @@ static void data_thread(void *arg1, void *arg2, void *arg3) audio_msg.data.size }; - void *data_ptrs[2] = { + const void *data_ptrs[2] = { &audio_msg.data, decimated_audio }; - if (decimated_frames == num_frames) { + if ((uint32_t)decimated_frames == num_frames) { data_ptrs[1] = audio_block; } if (decimated_frames > 0) { - sdlogger_write_data(&data_ptrs, data_size, 2); + sdlogger_write_data(data_ptrs, data_size, 2); } } @@ -622,8 +624,6 @@ static void audio_datapath_drift_compensation(uint32_t frame_start_ts_us) static void pres_comp_state_set(enum pres_comp_state new_state) { - int ret; - if (new_state == ctrl_blk.pres_comp.state) { return; } @@ -633,6 +633,8 @@ static void pres_comp_state_set(enum pres_comp_state new_state) LOG_INF("Pres comp state: %s", pres_comp_state_names[new_state]); #if CONFIG_BOARD_NRF5340_AUDIO_DK_NRF5340_CPUAPP + int ret; + if (new_state == PRES_STATE_LOCKED) { ret = led_on(LED_APP_2_GREEN); } else { @@ -777,6 +779,8 @@ static void audio_datapath_presentation_compensation(uint32_t recv_frame_ts_us, static void tone_stop_worker(struct k_work *work) { + ARG_UNUSED(work); + tone_active = false; memset(test_tone_buf, 0, sizeof(test_tone_buf)); @@ -799,6 +803,8 @@ K_WORK_DEFINE(tone_stop_work, tone_stop_worker); static void tone_stop_timer_handler(struct k_timer *dummy) { + ARG_UNUSED(dummy); + k_work_submit(&tone_stop_work); }; @@ -1066,6 +1072,8 @@ static void alt_buffer_free_both(void) } __attribute__((weak)) void bt_mgmt_report_audio_underrun(uint32_t count) { + ARG_UNUSED(count); + LOG_ERR("Audio underrun reported to bt_mgmt"); } diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 403982cb..269d0572 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -7,6 +7,7 @@ #include "streamctrl.h" #include +#include #include #include "common/bt_str.h" #include @@ -554,6 +555,9 @@ static void write_sirk(uint32_t sirk) { // Callback-Funktion für gefundene Geräte static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, struct net_buf_simple *ad) { + ARG_UNUSED(rssi); + ARG_UNUSED(type); + char addr_str[BT_ADDR_LE_STR_LEN]; bt_addr_le_to_str(addr, addr_str, sizeof(addr_str)); @@ -569,13 +573,13 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st break; // Ungültige Länge } - uint8_t type = net_buf_simple_pull_u8(ad); + uint8_t data_type = net_buf_simple_pull_u8(ad); const uint8_t *data = ad->data; ad->data += len - 1; ad->len -= len - 1; // Suchen nach 16-bit Service UUIDs (LE Audio Services) - if (type == BT_DATA_SVC_DATA16) { + if (data_type == BT_DATA_SVC_DATA16) { for (size_t i = 0; i + 1U < (size_t)(len - 1); i += 2) { uint16_t uuid = (data[i + 1] << 8) | data[i]; if (uuid == BT_UUID_CAS_VAL) { @@ -584,7 +588,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st } } - if (is_le_audio_device && type == BT_DATA_MANUFACTURER_DATA) { + if (is_le_audio_device && data_type == BT_DATA_MANUFACTURER_DATA) { if ((len - 1) >= sizeof(chip_id)) { memcpy(chip_id, data, sizeof(chip_id)); chip_id_found = true; @@ -595,7 +599,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st } /* A valid RSI contains a 3-byte hash followed by a 3-byte random value. */ - if (type == BT_DATA_CSIS_RSI) { + if (data_type == BT_DATA_CSIS_RSI) { if ((len - 1) >= sizeof(csis_rsi)) { memcpy(csis_rsi, data, sizeof(csis_rsi)); csis_rsi_found = true; @@ -632,9 +636,9 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st channel_assignment_get(&channel); if (channel == AUDIO_CH_L) { - LOG_INF("Device ID 1: %016X", oe_boot_state.device_id); - LOG_INF("Device ID 2: %016X", peer_device_id); - LOG_INF("New Sirk: %016X", new_sirk); + LOG_INF("Device ID 1: %016" PRIX64, oe_boot_state.device_id); + LOG_INF("Device ID 2: %08" PRIX32, peer_device_id); + LOG_INF("New Sirk: %08" PRIX32, new_sirk); //TODO: check if the device wants to pair (sirk == device_id) //TODO: check channel diff --git a/src/bluetooth/bt_management/conn_interval/conn_intvl_exp_backoff_lin_inc.cpp b/src/bluetooth/bt_management/conn_interval/conn_intvl_exp_backoff_lin_inc.cpp index 79ba1f7f..631335b2 100644 --- a/src/bluetooth/bt_management/conn_interval/conn_intvl_exp_backoff_lin_inc.cpp +++ b/src/bluetooth/bt_management/conn_interval/conn_intvl_exp_backoff_lin_inc.cpp @@ -19,10 +19,10 @@ void ConnIntvlExpBackoffLinIncr::init() { } void ConnIntvlExpBackoffLinIncr::on_audio_underrun(uint32_t count) { - uint16_t current_interval_units_ = this->current_interval_units(); + uint16_t current_interval_units = this->current_interval_units(); - LOG_DBG("Audio underrun reported: %u, current interval: %u units", count, current_interval_units_); - uint16_t new_interval_units = current_interval_units_ * backoff_factor_; + LOG_DBG("Audio underrun reported: %u, current interval: %u units", count, current_interval_units); + uint16_t new_interval_units = current_interval_units * backoff_factor_; if (new_interval_units > max_interval_units_) { new_interval_units = max_interval_units_; } @@ -31,6 +31,7 @@ void ConnIntvlExpBackoffLinIncr::on_audio_underrun(uint32_t count) { } void ConnIntvlExpBackoffLinIncr::on_timer_tick(k_timeout_t elapsed) { + ARG_UNUSED(elapsed); bool is_streaming = stream_state_get() == STATE_STREAMING; if (!is_streaming) { return; @@ -41,4 +42,4 @@ void ConnIntvlExpBackoffLinIncr::on_timer_tick(k_timeout_t elapsed) { new_interval_units = min_interval_units_; } this->request_interval(new_interval_units, new_interval_units); -} \ No newline at end of file +} diff --git a/src/bluetooth/bt_management/conn_interval/conn_intvl_linear.cpp b/src/bluetooth/bt_management/conn_interval/conn_intvl_linear.cpp index dfc792f9..f1214a78 100644 --- a/src/bluetooth/bt_management/conn_interval/conn_intvl_linear.cpp +++ b/src/bluetooth/bt_management/conn_interval/conn_intvl_linear.cpp @@ -16,10 +16,10 @@ void ConnIntvlLinear::init() { } void ConnIntvlLinear::on_audio_underrun(uint32_t count) { - uint16_t current_interval_units_ = this->current_interval_units(); + uint16_t current_interval_units = this->current_interval_units(); - LOG_DBG("Audio underrun reported: %u, current interval: %u units", count, current_interval_units_); - uint16_t new_interval_units = current_interval_units_ + inc_step_units_; + LOG_DBG("Audio underrun reported: %u, current interval: %u units", count, current_interval_units); + uint16_t new_interval_units = current_interval_units + inc_step_units_; if (new_interval_units > max_interval_units_) { new_interval_units = max_interval_units_; } @@ -28,6 +28,7 @@ void ConnIntvlLinear::on_audio_underrun(uint32_t count) { } void ConnIntvlLinear::on_timer_tick(k_timeout_t elapsed) { + ARG_UNUSED(elapsed); bool is_streaming = stream_state_get() == STATE_STREAMING; if (!is_streaming) { return; @@ -38,4 +39,4 @@ void ConnIntvlLinear::on_timer_tick(k_timeout_t elapsed) { new_interval_units = min_interval_units_; } this->request_interval(new_interval_units, new_interval_units); -} \ No newline at end of file +} diff --git a/src/bluetooth/bt_stream/unicast/unicast_server.c b/src/bluetooth/bt_stream/unicast/unicast_server.c index 82ad636a..00358cc8 100644 --- a/src/bluetooth/bt_stream/unicast/unicast_server.c +++ b/src/bluetooth/bt_stream/unicast/unicast_server.c @@ -102,15 +102,18 @@ static uint8_t device_identifier[] = { } /* Callback for locking state change from server side */ - static void csip_lock_changed_cb(struct bt_conn *conn, struct bt_csip_set_member_svc_inst *csip, - bool locked) - { +static void csip_lock_changed_cb(struct bt_conn *conn, struct bt_csip_set_member_svc_inst *csip_instance, + bool locked) +{ + ARG_UNUSED(csip_instance); LOG_DBG("Client %p %s the lock", (void *)conn, locked ? "locked" : "released"); } /* Callback for SIRK read request from peer side */ - static uint8_t sirk_read_req_cb(struct bt_conn *conn, struct bt_csip_set_member_svc_inst *csip) - { +static uint8_t sirk_read_req_cb(struct bt_conn *conn, struct bt_csip_set_member_svc_inst *csip_instance) +{ + ARG_UNUSED(conn); + ARG_UNUSED(csip_instance); /* Accept the request to read the SIRK, but return encrypted SIRK instead of plaintext */ return BT_CSIP_READ_SIRK_REQ_RSP_ACCEPT_ENC; } @@ -181,8 +184,10 @@ static uint8_t device_identifier[] = { static int lc3_config_cb(struct bt_conn *conn, const struct bt_bap_ep *ep, enum bt_audio_dir dir, const struct bt_audio_codec_cfg *codec, struct bt_bap_stream **stream, struct bt_bap_qos_cfg_pref *const pref, struct bt_bap_ascs_rsp *rsp) - { +{ int ret; + ARG_UNUSED(ep); + ARG_UNUSED(rsp); LOG_DBG("LC3 config callback"); for (int i = 0; i < ARRAY_SIZE(cap_audio_streams); i++) { @@ -237,7 +242,11 @@ static uint8_t device_identifier[] = { static int lc3_reconfig_cb(struct bt_bap_stream *stream, enum bt_audio_dir dir, const struct bt_audio_codec_cfg *codec, struct bt_bap_qos_cfg_pref *const pref, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(dir); + ARG_UNUSED(codec); + ARG_UNUSED(pref); + ARG_UNUSED(rsp); LOG_DBG("ASE Codec Reconfig: stream %p", (void *)stream); return 0; @@ -245,7 +254,8 @@ static uint8_t device_identifier[] = { static int lc3_qos_cb(struct bt_bap_stream *stream, const struct bt_bap_qos_cfg *qos, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(rsp); enum bt_audio_dir dir; dir = le_audio_stream_dir_get(stream); @@ -263,27 +273,33 @@ static uint8_t device_identifier[] = { static int lc3_enable_cb(struct bt_bap_stream *stream, const uint8_t *meta, size_t meta_len, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(meta); + ARG_UNUSED(rsp); LOG_DBG("Enable: stream %p meta_len %d", (void *)stream, meta_len); return 0; } static int lc3_start_cb(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(rsp); LOG_DBG("Start stream %p", (void *)stream); return 0; } static int lc3_metadata_cb(struct bt_bap_stream *stream, const uint8_t *meta, size_t meta_len, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(meta); + ARG_UNUSED(rsp); LOG_DBG("Metadata: stream %p meta_len %d", (void *)stream, meta_len); return 0; } static int lc3_disable_cb(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(rsp); enum bt_audio_dir dir; dir = le_audio_stream_dir_get(stream); @@ -300,7 +316,8 @@ static uint8_t device_identifier[] = { } static int lc3_stop_cb(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(rsp); enum bt_audio_dir dir; dir = le_audio_stream_dir_get(stream); @@ -317,7 +334,8 @@ static uint8_t device_identifier[] = { } static int lc3_release_cb(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp) - { +{ + ARG_UNUSED(rsp); enum bt_audio_dir dir; dir = le_audio_stream_dir_get(stream); @@ -368,6 +386,7 @@ static uint8_t device_identifier[] = { #if (CONFIG_BT_AUDIO_TX) static void stream_sent_cb(struct bt_bap_stream *stream) { + ARG_UNUSED(stream); /* Unicast server/CIS headset only supports one source stream for now */ struct stream_index idx = { .lvl1 = 0, @@ -401,8 +420,9 @@ static uint8_t device_identifier[] = { } } - static void stream_disabled_cb(struct bt_bap_stream *stream) - { +static void stream_disabled_cb(struct bt_bap_stream *stream) +{ + ARG_UNUSED(stream); LOG_INF("Stream %p disabled", stream); } @@ -445,8 +465,9 @@ static uint8_t device_identifier[] = { le_audio_event_publish(LE_AUDIO_EVT_NOT_STREAMING, stream->conn, dir); } - static void stream_released_cb(struct bt_bap_stream *stream) - { +static void stream_released_cb(struct bt_bap_stream *stream) +{ + ARG_UNUSED(stream); /* NOTE: The string below is used by the Nordic CI system */ LOG_INF("Stream %p released", stream); } @@ -468,6 +489,7 @@ static uint8_t device_identifier[] = { int unicast_server_config_get(struct bt_conn *conn, enum bt_audio_dir dir, uint32_t *bitrate, uint32_t *sampling_rate_hz, uint32_t *pres_delay_us) { + ARG_UNUSED(conn); int ret; if (bitrate == NULL && sampling_rate_hz == NULL && pres_delay_us == NULL) { diff --git a/src/bluetooth/gatt_services/sensor_service.c b/src/bluetooth/gatt_services/sensor_service.c index 3d821d14..2d676e79 100644 --- a/src/bluetooth/gatt_services/sensor_service.c +++ b/src/bluetooth/gatt_services/sensor_service.c @@ -176,6 +176,7 @@ static void connect_evt_handler(const struct zbus_channel *chan) static void sensor_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); k_spinlock_key_t key = k_spin_lock(¬ify_state_lock); notify_enabled = (value == BT_GATT_CCC_NOTIFY); @@ -189,6 +190,7 @@ static void sensor_ccc_cfg_changed(const struct bt_gatt_attr *attr, static void sensor_config_status_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + ARG_UNUSED(attr); k_spinlock_key_t key = k_spin_lock(¬ify_state_lock); sensor_config_status_ntfy_enabled = (value == BT_GATT_CCC_NOTIFY); k_spin_unlock(¬ify_state_lock, key); @@ -199,6 +201,7 @@ static ssize_t write_config(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(flags); LOG_DBG("Attribute write, handle: %u, conn: %p", attr->handle, (void *)conn); if (len != sizeof(struct sensor_config)) { @@ -211,12 +214,12 @@ static ssize_t write_config(struct bt_conn *conn, return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); } - struct sensor_config * config = (struct sensor_config *)buf; +struct sensor_config * sensor_configuration = (struct sensor_config *)buf; - if (config->storageOptions == 0) { - LOG_INF("Setup sensor ID %i (turned off)", config->sensorId); + if (sensor_configuration->storageOptions == 0) { + LOG_INF("Setup sensor ID %i (turned off)", sensor_configuration->sensorId); } else { - LOG_INF("Setup sensor ID %i with samplerateIndex %i", config->sensorId, config->sampleRateIndex); + LOG_INF("Setup sensor ID %i with samplerateIndex %i", sensor_configuration->sensorId, sensor_configuration->sampleRateIndex); } //stop_sensor_manager(); @@ -246,6 +249,8 @@ static ssize_t write_sensor_rec_name(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(offset); + ARG_UNUSED(flags); LOG_DBG("Attribute write, len: %u, handle: %u, conn: %p", len, attr->handle, (void *)conn); if (len > MAX_SENSOR_REC_NAME_LENGTH - 1) { LOG_WRN("Write sensor recording name: Data length exceeds maximum allowed length of %i", MAX_SENSOR_REC_NAME_LENGTH - 1); @@ -405,6 +410,7 @@ static void notification_task(void) { } void sensor_queue_listener_cb(const struct zbus_channel *chan) { + ARG_UNUSED(chan); int ret; const struct sensor_msg * msg; @@ -447,19 +453,19 @@ int init_sensor_config_status() { return 0; } -int set_sensor_config_status(struct sensor_config config) { - LOG_DBG("Setting sensor config status for sensorId: %i", config.sensorId); +int set_sensor_config_status(struct sensor_config sensor_configuration) { + LOG_DBG("Setting sensor config status for sensorId: %i", sensor_configuration.sensorId); ssize_t sensor_config_index = -1; for (size_t i = 0; i < active_sensor_configs_size; i++) { - if (active_sensor_configs[i].sensorId == config.sensorId) { + if (active_sensor_configs[i].sensorId == sensor_configuration.sensorId) { sensor_config_index = i; break; } } if (sensor_config_index >= 0) { - active_sensor_configs[sensor_config_index] = config; + active_sensor_configs[sensor_config_index] = sensor_configuration; LOG_DBG("Found sensor config"); } else { LOG_DBG("Sensor config not found, adding new sensor config"); @@ -471,7 +477,7 @@ int set_sensor_config_status(struct sensor_config config) { return -1; } active_sensor_configs = new_active_sensor_configs; - active_sensor_configs[active_sensor_configs_size - 1] = config; + active_sensor_configs[active_sensor_configs_size - 1] = sensor_configuration; } if (sensor_config_status_ntfy_enabled) { diff --git a/src/drivers/ADAU1860.cpp b/src/drivers/ADAU1860.cpp index ecf5a4dd..4b662914 100644 --- a/src/drivers/ADAU1860.cpp +++ b/src/drivers/ADAU1860.cpp @@ -19,6 +19,7 @@ static struct k_work_delayable ascr_lock_work; void ADAU1860::check_ascr_lock(struct k_work *work) { + ARG_UNUSED(work); uint8_t status2; dac.readReg(registers::STATUS2, &status2, sizeof(status2)); @@ -345,10 +346,10 @@ int ADAU1860::setup_FDSP() { uint8_t fdsp_ctrl4 = 2; // framrate source DMIC01 writeReg(registers::FDSP_CTRL4, &fdsp_ctrl4, sizeof(fdsp_ctrl4)); - /*uint8_t fdsp_ctrl4 = 15; // fixed frame rate + /* uint8_t fdsp_ctrl4 = 15; // fixed frame rate writeReg(registers::FDSP_CTRL4, &fdsp_ctrl4, sizeof(fdsp_ctrl4)); - /*uint8_t fdsp_ctrl5 = 0xFF; // fixed frame rate + uint8_t fdsp_ctrl5 = 0xFF; // fixed frame rate writeReg(registers::FDSP_CTRL5, &fdsp_ctrl5, sizeof(fdsp_ctrl5)); uint8_t fdsp_ctrl6 = 0x01; // fixed frame rate @@ -406,17 +407,17 @@ int ADAU1860::mute(bool active) { #endif } -int ADAU1860::fdsp_safe_load(sl_address address, safe_load_params params, bool update_inactive) { +int ADAU1860::fdsp_safe_load(sl_address safe_load_address, safe_load_params params, bool update_inactive) { // TODO: not working if (update_inactive) { // write to non active banks for (int i = 0; i < FDSP_NUM_BANKS; i++) { if (i == _active_bank) continue; - writeReg(FDSP_BANK(i, address), (uint8_t *) params, sizeof(safe_load_params)); + writeReg(FDSP_BANK(i, safe_load_address), (uint8_t *) params, sizeof(safe_load_params)); } } - uint8_t _address = address; + uint8_t _address = safe_load_address; writeReg(registers::FDSP_SL_ADDR, &_address, sizeof(_address)); writeReg(registers::FDSP_SL_P0_0, (uint8_t *) params, sizeof(safe_load_params)); @@ -426,16 +427,16 @@ int ADAU1860::fdsp_safe_load(sl_address address, safe_load_params params, bool u return 0; } -int ADAU1860::fdsp_safe_load(sl_address address, int n, uint32_t param, bool update_inactive) { +int ADAU1860::fdsp_safe_load(sl_address safe_load_address, int n, uint32_t param, bool update_inactive) { uint32_t params[FDSP_NUM_PARAMS]; for (int i = 0; i < FDSP_NUM_PARAMS; i++) { - params[i] = fdsp_param_bank_a[i][address]; + params[i] = fdsp_param_bank_a[i][safe_load_address]; } params[n] = param; - fdsp_safe_load(address, params, update_inactive); + fdsp_safe_load(safe_load_address, params, update_inactive); return 0; } diff --git a/src/utils/StateIndicator.cpp b/src/utils/StateIndicator.cpp index 2600637d..dc37a050 100644 --- a/src/utils/StateIndicator.cpp +++ b/src/utils/StateIndicator.cpp @@ -63,9 +63,9 @@ void StateIndicator::init(struct earable_state state) { set_state(state); } -void StateIndicator::set_custom_color(const RGBColor &color) { - memcpy(&this->color, color, sizeof(RGBColor)); - if (_state.led_mode == CUSTOM) led_controller.setColor(color); +void StateIndicator::set_custom_color(const RGBColor &custom_color) { + memcpy(&this->color, custom_color, sizeof(RGBColor)); + if (_state.led_mode == CUSTOM) led_controller.setColor(custom_color); } void StateIndicator::set_dfu_active(bool active) { From 3ad72d14516cba8449035bd7525019087da1dfe4 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 15:47:28 +0200 Subject: [PATCH 70/77] fix: mark remaining intentionally unused arguments --- src/audio/audio_system.c | 4 ++++ src/audio/le_audio_rx.c | 4 ++++ src/bluetooth/gatt_services/audio_config_service.c | 14 +++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/audio/audio_system.c b/src/audio/audio_system.c index 31b49528..6ae33b1f 100644 --- a/src/audio/audio_system.c +++ b/src/audio/audio_system.c @@ -122,6 +122,10 @@ static void audio_headset_configure(void) static void encoder_thread(void *arg1, void *arg2, void *arg3) { + ARG_UNUSED(arg1); + ARG_UNUSED(arg2); + ARG_UNUSED(arg3); + int ret; uint32_t blocks_alloced_num; uint32_t blocks_locked_num; diff --git a/src/audio/le_audio_rx.c b/src/audio/le_audio_rx.c index 8c7c9370..9411f230 100644 --- a/src/audio/le_audio_rx.c +++ b/src/audio/le_audio_rx.c @@ -138,6 +138,10 @@ void le_audio_rx_data_handler(uint8_t const *const p_data, size_t data_size, boo */ static void audio_datapath_thread(void *dummy1, void *dummy2, void *dummy3) { + ARG_UNUSED(dummy1); + ARG_UNUSED(dummy2); + ARG_UNUSED(dummy3); + int ret; struct ble_iso_data *iso_received = NULL; size_t iso_received_size; diff --git a/src/bluetooth/gatt_services/audio_config_service.c b/src/bluetooth/gatt_services/audio_config_service.c index 20931d8d..c8a53e10 100644 --- a/src/bluetooth/gatt_services/audio_config_service.c +++ b/src/bluetooth/gatt_services/audio_config_service.c @@ -12,6 +12,10 @@ LOG_MODULE_REGISTER(audio_config_service, CONFIG_BLE_LOG_LEVEL); static ssize_t write_audio_mode(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(offset); + ARG_UNUSED(flags); if (len != sizeof(uint8_t)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } @@ -32,6 +36,10 @@ static ssize_t write_audio_mode(struct bt_conn *conn, const struct bt_gatt_attr static ssize_t write_mic_select(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(offset); + ARG_UNUSED(flags); if (len != sizeof(uint8_t)) { return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); } @@ -77,8 +85,12 @@ static ssize_t read_audio_channel(struct bt_conn *conn, const struct bt_gatt_att } static ssize_t write_dmic_gain(struct bt_conn *conn, const struct bt_gatt_attr *attr, - const void *buf, uint16_t len, uint16_t offset, uint8_t flags) + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + ARG_UNUSED(conn); + ARG_UNUSED(attr); + ARG_UNUSED(offset); + ARG_UNUSED(flags); // Mic gain is 2 bytes: [outer_reg, inner_reg]. // Outer mic maps to DMIC_VOL0; inner mic maps to DMIC_VOL1. // Per ADAU186x DMIC_VOL register (0x4000C045): From 5396639e445eccbb001f949e2164385ab1bde5c6 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 15:47:28 +0200 Subject: [PATCH 71/77] fix: correct compiler-reported type and format issues --- src/bluetooth/bt_management/advertising/bt_mgmt_adv.c | 11 +++++------ src/bluetooth/bt_management/bt_mgmt.c | 9 ++++----- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c b/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c index a82cf12c..d43317b4 100644 --- a/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c +++ b/src/bluetooth/bt_management/advertising/bt_mgmt_adv.c @@ -9,7 +9,7 @@ #include #include #include - + #include "macros_common.h" #include "zbus_common.h" @@ -476,8 +476,7 @@ static void advertising_process(struct k_work *work) return 0; } - void bt_mgmt_adv_init(void) - { - k_work_init(&adv_work, advertising_process); - } - +void bt_mgmt_adv_init(void) +{ + k_work_init(&adv_work, advertising_process); +} diff --git a/src/bluetooth/bt_management/bt_mgmt.c b/src/bluetooth/bt_management/bt_mgmt.c index 627e2833..4d0a1818 100644 --- a/src/bluetooth/bt_management/bt_mgmt.c +++ b/src/bluetooth/bt_management/bt_mgmt.c @@ -20,6 +20,7 @@ #include "macros_common.h" #include "zbus_common.h" #include "button_assignments.h" +#include "../../buttons/button_manager.h" #include "uicr.h" #include "bt_mgmt_ctlr_cfg_internal.h" @@ -89,9 +90,6 @@ static struct bt_le_conn_param *conn_param = BT_LE_CONN_PARAM(CONFIG_BLE_ACL_CON //callback static void conn_params_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { - struct bt_mgmt_msg msg; - int ret; - LOG_INF("Conn params updated: interval %d unit, latency %d, timeout: %d0 ms",interval, latency, timeout); bt_mgmt_ci_on_conn_param_updated(conn, interval, latency, timeout); @@ -325,7 +323,7 @@ static int local_identity_addr_print(void) bt_id_get(addrs, &num_ids); - for (int i = 0; i < num_ids; i++) { + for (size_t i = 0; i < num_ids; i++) { (void)bt_addr_le_to_str(&(addrs[i]), addr_str, BT_ADDR_LE_STR_LEN); LOG_INF("Local identity addr: %s", addr_str); } @@ -417,7 +415,8 @@ int bt_mgmt_init(void) bt_gatt_cb_register(&gatt_callbacks); uint32_t sirk = uicr_sirk_get(); - snprintf(name, CONFIG_BT_DEVICE_NAME_MAX, "%s-%04X", CONFIG_BT_DEVICE_NAME, (sirk != 0xFFFFFFFFU ? sirk : oe_boot_state.device_id) & 0xFFFF); + snprintf(name, CONFIG_BT_DEVICE_NAME_MAX, "%s-%04X", CONFIG_BT_DEVICE_NAME, + (unsigned int)((sirk != 0xFFFFFFFFU ? sirk : oe_boot_state.device_id) & 0xFFFF)); ret = bt_set_name(name); if (ret) { From c95c3856b5ca88fe661987ba214582e4f7c5901d Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 16:06:54 +0200 Subject: [PATCH 72/77] fix: handle Bluetooth management events --- src/bluetooth/gatt_services/sensor_service.c | 4 ++++ src/utils/StateIndicator.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/bluetooth/gatt_services/sensor_service.c b/src/bluetooth/gatt_services/sensor_service.c index 2d676e79..c358ceff 100644 --- a/src/bluetooth/gatt_services/sensor_service.c +++ b/src/bluetooth/gatt_services/sensor_service.c @@ -170,6 +170,10 @@ static void connect_evt_handler(const struct zbus_channel *chan) case BT_MGMT_DISCONNECTED: reset_sensor_notification_state(); break; + + default: + /* Other events do not affect sensor notification state. */ + break; } } diff --git a/src/utils/StateIndicator.cpp b/src/utils/StateIndicator.cpp index dc37a050..89ca40f8 100644 --- a/src/utils/StateIndicator.cpp +++ b/src/utils/StateIndicator.cpp @@ -29,6 +29,10 @@ static void connect_evt_handler(const struct zbus_channel *chan) case BT_MGMT_DISCONNECTED: state_indicator.set_pairing_state(PAIRED); break; + + default: + /* Other events do not affect the pairing state indication. */ + break; } } From ab517e9fed39352af3038de9cb7c6a4bf0a1225f Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 16:06:54 +0200 Subject: [PATCH 73/77] fix: resolve signedness compiler warnings --- src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp | 2 +- src/audio/audio_datapath.c | 4 ++-- src/audio/streamctrl.c | 4 ++-- .../media/bt_content_ctrl_media.c | 2 +- src/bluetooth/bt_stream/unicast/unicast_server.c | 8 ++++---- src/modules/sd_card.c | 2 +- src/time_sync/time_sync.c | 14 +++++++++----- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp index 1820f863..eb2a8ac8 100644 --- a/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp +++ b/src/SD_Card/SD_Card_Manager/SD_Card_Manager.cpp @@ -353,7 +353,7 @@ int SDCardManager::ls(char *buf, size_t *buf_size) { &buf[used_buf_size], remaining_buf_size, "[%s]\t%s\n", entry.type == FS_DIR_ENTRY_DIR ? "DIR " : "FILE", entry.name); - if (len >= remaining_buf_size) { + if (len < 0 || (size_t)len >= remaining_buf_size) { LOG_ERR("Failed to append to buffer, error: %d", len); k_mutex_unlock(&m_sem_sd_mngr_oper_ongoing); return -EINVAL; diff --git a/src/audio/audio_datapath.c b/src/audio/audio_datapath.c index 3aa3a370..ee944999 100644 --- a/src/audio/audio_datapath.c +++ b/src/audio/audio_datapath.c @@ -215,7 +215,7 @@ int _count = 0; static int16_t *buffer_play_data = NULL; static uint32_t buffer_play_pos; static uint32_t buffer_play_fade_pos; -static int buffer_play_num_samples; +static uint32_t buffer_play_num_samples; static float buffer_play_amplitude; static bool buffer_play_loop; static void (*buffer_play_callback)(void) = NULL; @@ -937,7 +937,7 @@ int audio_datapath_buffer_play(int16_t *buffer, int num_samples, bool loop, floa buffer_play_data = buffer; buffer_play_pos = 0; buffer_play_fade_pos = 0; - buffer_play_num_samples = num_samples; + buffer_play_num_samples = (uint32_t)num_samples; buffer_play_amplitude = amplitude; buffer_play_loop = loop; buffer_play_callback = callback; diff --git a/src/audio/streamctrl.c b/src/audio/streamctrl.c index 269d0572..e3dc6df2 100644 --- a/src/audio/streamctrl.c +++ b/src/audio/streamctrl.c @@ -589,7 +589,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st } if (is_le_audio_device && data_type == BT_DATA_MANUFACTURER_DATA) { - if ((len - 1) >= sizeof(chip_id)) { + if (len > 1U && (size_t)(len - 1U) >= sizeof(chip_id)) { memcpy(chip_id, data, sizeof(chip_id)); chip_id_found = true; } else { @@ -600,7 +600,7 @@ static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, st /* A valid RSI contains a 3-byte hash followed by a 3-byte random value. */ if (data_type == BT_DATA_CSIS_RSI) { - if ((len - 1) >= sizeof(csis_rsi)) { + if (len > 1U && (size_t)(len - 1U) >= sizeof(csis_rsi)) { memcpy(csis_rsi, data, sizeof(csis_rsi)); csis_rsi_found = true; } else { diff --git a/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c b/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c index f76c0efc..f1b5e3e3 100644 --- a/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c +++ b/src/bluetooth/bt_content_control/media/bt_content_ctrl_media.c @@ -44,7 +44,7 @@ static struct media_ctlr mcc_peer[CONFIG_BT_MAX_CONN]; */ static int mcc_peer_index_free_get(void) { - for (int i = 0; i < ARRAY_SIZE(mcc_peer); i++) { + for (size_t i = 0; i < ARRAY_SIZE(mcc_peer); i++) { if (mcc_peer[i].conn == NULL) { return i; } diff --git a/src/bluetooth/bt_stream/unicast/unicast_server.c b/src/bluetooth/bt_stream/unicast/unicast_server.c index 00358cc8..cd01b88c 100644 --- a/src/bluetooth/bt_stream/unicast/unicast_server.c +++ b/src/bluetooth/bt_stream/unicast/unicast_server.c @@ -190,7 +190,7 @@ static uint8_t sirk_read_req_cb(struct bt_conn *conn, struct bt_csip_set_member_ ARG_UNUSED(rsp); LOG_DBG("LC3 config callback"); - for (int i = 0; i < ARRAY_SIZE(cap_audio_streams); i++) { + for (size_t i = 0; i < ARRAY_SIZE(cap_audio_streams); i++) { struct bt_cap_stream *cap_audio_stream = &cap_audio_streams[i]; if (!cap_audio_stream->bap_stream.conn) { @@ -656,7 +656,7 @@ static void stream_released_cb(struct bt_bap_stream *stream) struct le_audio_tx_info tx[CONFIG_BT_ASCS_MAX_ASE_SRC_COUNT]; - for (int i = 0; i < ARRAY_SIZE(cap_tx_streams); i++) { + for (size_t i = 0; i < ARRAY_SIZE(cap_tx_streams); i++) { if (!le_audio_ep_state_check(cap_tx_streams[i]->bap_stream.ep, BT_BAP_EP_STATE_STREAMING)) { continue; @@ -746,7 +746,7 @@ static void stream_released_cb(struct bt_bap_stream *stream) // memcpy(csip_param.sirk, CONFIG_BT_SET_IDENTITY_RESOLVING_KEY, BT_CSIP_SIRK_SIZE); } - for (int i = 0; i < ARRAY_SIZE(caps); i++) { + for (size_t i = 0; i < ARRAY_SIZE(caps); i++) { ret = bt_pacs_cap_register(caps_dirs[i], &caps[i]); if (ret) { LOG_ERR("Capability register failed. Err: %d", ret); @@ -807,7 +807,7 @@ static void stream_released_cb(struct bt_bap_stream *stream) return ret; } - for (int i = 0; i < ARRAY_SIZE(cap_audio_streams); i++) { + for (size_t i = 0; i < ARRAY_SIZE(cap_audio_streams); i++) { bt_cap_stream_ops_register(&cap_audio_streams[i], &stream_ops); } diff --git a/src/modules/sd_card.c b/src/modules/sd_card.c index dee80d7e..a147e9a4 100644 --- a/src/modules/sd_card.c +++ b/src/modules/sd_card.c @@ -259,7 +259,7 @@ int sd_card_list_files(char const *const path, char *buf, size_t *buf_size, bool entry.name); } - if (len >= remaining_buf_size) { + if (len < 0 || (size_t)len >= remaining_buf_size) { LOG_ERR("Failed to append to buffer, error: %d", len); return -EINVAL; } diff --git a/src/time_sync/time_sync.c b/src/time_sync/time_sync.c index feebf9f3..1b26991f 100644 --- a/src/time_sync/time_sync.c +++ b/src/time_sync/time_sync.c @@ -166,15 +166,19 @@ bool time_sync_is_synced(void) { uint64_t get_current_time_us(void) { uint64_t base_u = get_time_since_boot_us(); int64_t base_s = (base_u > (uint64_t)INT64_MAX) ? INT64_MAX : (int64_t)base_u; - int64_t now_s = base_s + time_offset_us; + if (time_offset_us > 0 && base_s > INT64_MAX - time_offset_us) { + LOG_WRN("Current time overflow, returning UINT64_MAX"); + return UINT64_MAX; + } + if (time_offset_us < 0 && base_s < INT64_MIN - time_offset_us) { + LOG_WRN("Current time underflow, returning 0"); + return 0; + } + int64_t now_s = base_s + time_offset_us; if (now_s < 0) { LOG_WRN("Current time underflow, returning 0"); return 0; } - if (now_s != base_u + time_offset_us) { - LOG_WRN("Current time overflow, returning UINT64_MAX"); - return UINT64_MAX; - } return (uint64_t)now_s; } From 14864cc89b3982e9c08d307db97e9dea68891265 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 16:06:55 +0200 Subject: [PATCH 74/77] fix: resolve floating-point promotion warnings --- src/Battery/BQ25120a.cpp | 20 +++++------ src/Battery/BQ25120a.h | 4 +-- src/Battery/PowerManager.cpp | 36 +++++++++---------- src/SensorManager/BMP388/bmp3.c | 10 +++--- src/SensorManager/Baro.cpp | 2 +- src/SensorManager/BoneConduction.cpp | 4 +-- src/SensorManager/IMU.cpp | 2 +- src/SensorManager/MLX90632/MLX90632.cpp | 6 ++-- src/SensorManager/Microphone.cpp | 2 +- src/SensorManager/PPG.cpp | 4 +-- src/SensorManager/SensorManager.cpp | 2 +- src/SensorManager/Temp.cpp | 2 +- .../bt_management/bt_mgmt_conn_interval.cpp | 4 +-- 13 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/Battery/BQ25120a.cpp b/src/Battery/BQ25120a.cpp index 4e69173f..b03a3847 100644 --- a/src/Battery/BQ25120a.cpp +++ b/src/Battery/BQ25120a.cpp @@ -206,7 +206,7 @@ uint8_t BQ25120a::write_charging_control(float mA) { if (mA >= 40) { if (mA > 300) mA = 300; - status |= (((uint16_t)((mA - 40) / 10 + EPS)) & 0x1F) << 2; + status |= (((uint16_t)((mA - 40.0f) / 10.0f + EPS)) & 0x1F) << 2; status |= 1 << 7; } else { if (mA > 35) mA = 35; @@ -244,7 +244,7 @@ uint8_t BQ25120a::write_LDO_voltage_control(float volt) { readReg(registers::LS_LDO_CTRL, &status, sizeof(status)); status &= 1 << 7; - status |= ((uint8_t)((volt - 0.8f) * 10 + EPS)) << 2; + status |= ((uint8_t)((volt - 0.8f) * 10.0f + EPS)) << 2; writeReg(registers::LS_LDO_CTRL, &status, sizeof(status)); @@ -277,7 +277,7 @@ uint8_t BQ25120a::write_battery_voltage_control(float volt) { volt = CLAMP(volt, 3.6f, 4.65f); - status |= (((uint16_t)((volt - 3.6f) * 100 + EPS)) & 0x7F) << 1; + status |= (((uint16_t)((volt - 3.6f) * 100.0f + EPS)) & 0x7F) << 1; writeReg(registers::BAT_VOL_CTRL, &status, sizeof(status)); @@ -301,7 +301,7 @@ chrg_state BQ25120a::read_termination_control() { if (status & (1 << 7)) { mAh = 6 + mAh * 1; } else { - mAh = 0.5 + mAh * 0.5; + mAh = 0.5f + mAh * 0.5f; } chrg.mAh = mAh; @@ -321,7 +321,7 @@ uint8_t BQ25120a::write_termination_control(float mA, bool enable_termination) { status |= 1 << 7; } else { if (mA > 5) mA = 5; - status |= (((uint16_t)(2 * (mA - 0.5))) & 0x1F) << 2; + status |= (((uint16_t)(2.0f * (mA - 0.5f))) & 0x1F) << 2; } if (enable_termination) { @@ -339,20 +339,20 @@ ilim_uvlo BQ25120a::read_uvlo_ilim() { (void)readReg(registers::ILIM_UVLO, (uint8_t *) &status, sizeof(status)); - param.uvlo_v = CLAMP(3.0f- 0.2f * ((status & 0x7) - 2), 2.2, 3.0); + param.uvlo_v = CLAMP(3.0f- 0.2f * ((status & 0x7) - 2), 2.2f, 3.0f); param.lim_mA = 50.f + 50.f * ((status >> 3) & 0x7); return param; } uint8_t BQ25120a::write_uvlo_ilim(ilim_uvlo param) { - float mA = CLAMP(param.lim_mA, 50, 400); - float v = CLAMP(param.uvlo_v, 2.2, 3.0); + float mA = CLAMP(param.lim_mA, 50.0f, 400.0f); + float v = CLAMP(param.uvlo_v, 2.2f, 3.0f); uint8_t status = 0; - status |= ((uint16_t)(mA / 50 - 1) & 0x7) << 3; - status |= ((uint16_t)((3.0 - v) * 5 + 2) & 0x7); + status |= ((uint16_t)(mA / 50.0f - 1.0f) & 0x7) << 3; + status |= ((uint16_t)((3.0f - v) * 5.0f + 2.0f) & 0x7); writeReg(registers::ILIM_UVLO, &status, sizeof(status)); diff --git a/src/Battery/BQ25120a.h b/src/Battery/BQ25120a.h index 38eb75f3..79ac3314 100644 --- a/src/Battery/BQ25120a.h +++ b/src/Battery/BQ25120a.h @@ -13,7 +13,7 @@ #define BQ25120a_I2C_TIMEOUT_US 66 #define BQ25120a_HIGH_Z_TIMEOUT_US 1000 -#define EPS 1e-3 +#define EPS 1e-3f struct chrg_state { float mAh = 0; @@ -107,4 +107,4 @@ class BQ25120a { extern BQ25120a battery_controller; -#endif \ No newline at end of file +#endif diff --git a/src/Battery/PowerManager.cpp b/src/Battery/PowerManager.cpp index d38de1d2..77ba0760 100644 --- a/src/Battery/PowerManager.cpp +++ b/src/Battery/PowerManager.cpp @@ -182,16 +182,16 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { msg.charging_state = POWER_CONNECTED; - LOG_DBG("Voltage: %.3f V", voltage); - LOG_DBG("Charging current: %.3f mA", current); - LOG_DBG("Target current: %.3f mA", target_current); - LOG_DBG("State of charge: %.3f %%", fuel_gauge.state_of_charge()); + LOG_DBG("Voltage: %.3f V", (double)voltage); + LOG_DBG("Charging current: %.3f mA", (double)current); + LOG_DBG("Target current: %.3f mA", (double)target_current); + LOG_DBG("State of charge: %.3f %%", (double)fuel_gauge.state_of_charge()); // check if target current is met (if not tapering) - if (current > 0.8 * target_current - 2 * power_manager._battery_settings.i_term) { + if (current > 0.8f * target_current - 2.0f * power_manager._battery_settings.i_term) { msg.charging_state = CHARGING; } - else if (voltage > power_manager._battery_settings.u_term - 0.02) { + else if (voltage > power_manager._battery_settings.u_term - 0.02f) { #ifdef CONFIG_BATTERY_ENABLE_TRICKLE_CHARGE msg.charging_state = TRICKLE_CHARGING; #else @@ -222,10 +222,10 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { // as long as fault exists if (fault & (1 << 5)) { bool power_connected = battery_controller.power_connected(); - if (power_connected && current > 0.5 * power_manager._battery_settings.i_term) { + if (power_connected && current > 0.5f * power_manager._battery_settings.i_term) { msg.charging_state = PRECHARGING; } - LOG_WRN("Battery under voltage: %.3f V", voltage); + LOG_WRN("Battery under voltage: %.3f V", (double)voltage); } // cleared after read @@ -252,8 +252,8 @@ void PowerManager::fuel_gauge_work_handler(struct k_work * work) { // Basic measurements LOG_DBG("Basic Measurements:"); - LOG_DBG(" Voltage: %.3f V", voltage); - LOG_DBG(" Current: %.3f mA", current); + LOG_DBG(" Voltage: %.3f V", (double)voltage); + LOG_DBG(" Current: %.3f mA", (double)current); break; } @@ -712,17 +712,17 @@ static int cmd_battery_info(const struct shell *shell, size_t argc, const char * // Basic measurements shell_print(shell, "Basic Measurements:"); - shell_print(shell, " Voltage: %.3f V", fuel_gauge.voltage()); - shell_print(shell, " Temperature: %.1f °C", fuel_gauge.temperature()); + shell_print(shell, " Voltage: %.3f V", (double)fuel_gauge.voltage()); + shell_print(shell, " Temperature: %.1f °C", (double)fuel_gauge.temperature()); shell_print(shell, " Current: %.1f mA (avg: %.1f mA)", - fuel_gauge.current(), fuel_gauge.average_current()); - shell_print(shell, " State of Charge: %.1f%%", fuel_gauge.state_of_charge()); + (double)fuel_gauge.current(), (double)fuel_gauge.average_current()); + shell_print(shell, " State of Charge: %.1f%%", (double)fuel_gauge.state_of_charge()); // Capacity info shell_print(shell, "Capacity Information:"); - shell_print(shell, " Design Capacity: %.1f mAh", fuel_gauge.design_cap()); - shell_print(shell, " Full Charge Capacity: %.1f mAh", fuel_gauge.capacity()); - shell_print(shell, " Remaining Capacity: %.1f mAh", fuel_gauge.remaining_cap()); + shell_print(shell, " Design Capacity: %.1f mAh", (double)fuel_gauge.design_cap()); + shell_print(shell, " Full Charge Capacity: %.1f mAh", (double)fuel_gauge.capacity()); + shell_print(shell, " Remaining Capacity: %.1f mAh", (double)fuel_gauge.remaining_cap()); // Time estimates float ttf = fuel_gauge.time_to_full(); @@ -741,7 +741,7 @@ static int cmd_battery_info(const struct shell *shell, size_t argc, const char * struct chrg_state charge_ctrl = battery_controller.read_charging_control(); shell_print(shell, " Charge Control: enabled=%i, current=%.1f mA", - charge_ctrl.enabled, charge_ctrl.mAh); + charge_ctrl.enabled, (double)charge_ctrl.mAh); battery_controller.enter_high_impedance(); diff --git a/src/SensorManager/BMP388/bmp3.c b/src/SensorManager/BMP388/bmp3.c index 105c07d3..1e3b494b 100644 --- a/src/SensorManager/BMP388/bmp3.c +++ b/src/SensorManager/BMP388/bmp3.c @@ -2377,18 +2377,18 @@ static double compensate_pressure(const struct bmp3_uncomp_data *uncomp_data, co double partial_out2; partial_data1 = quantized_calib_data->par_p6 * quantized_calib_data->t_lin; - partial_data2 = quantized_calib_data->par_p7 * pow_bmp3(quantized_calib_data->t_lin, 2); - partial_data3 = quantized_calib_data->par_p8 * pow_bmp3(quantized_calib_data->t_lin, 3); + partial_data2 = (double)quantized_calib_data->par_p7 * (double)pow_bmp3(quantized_calib_data->t_lin, 2); + partial_data3 = (double)quantized_calib_data->par_p8 * (double)pow_bmp3(quantized_calib_data->t_lin, 3); partial_out1 = quantized_calib_data->par_p5 + partial_data1 + partial_data2 + partial_data3; partial_data1 = quantized_calib_data->par_p2 * quantized_calib_data->t_lin; - partial_data2 = quantized_calib_data->par_p3 * pow_bmp3(quantized_calib_data->t_lin, 2); - partial_data3 = quantized_calib_data->par_p4 * pow_bmp3(quantized_calib_data->t_lin, 3); + partial_data2 = (double)quantized_calib_data->par_p3 * (double)pow_bmp3(quantized_calib_data->t_lin, 2); + partial_data3 = (double)quantized_calib_data->par_p4 * (double)pow_bmp3(quantized_calib_data->t_lin, 3); partial_out2 = uncomp_data->pressure * (quantized_calib_data->par_p1 + partial_data1 + partial_data2 + partial_data3); partial_data1 = pow_bmp3((double)uncomp_data->pressure, 2); partial_data2 = quantized_calib_data->par_p9 + quantized_calib_data->par_p10 * quantized_calib_data->t_lin; partial_data3 = partial_data1 * partial_data2; - partial_data4 = partial_data3 + pow_bmp3((double)uncomp_data->pressure, 3) * quantized_calib_data->par_p11; + partial_data4 = partial_data3 + (double)pow_bmp3((double)uncomp_data->pressure, 3) * (double)quantized_calib_data->par_p11; comp_press = partial_out1 + partial_out2 + partial_data4; return comp_press; diff --git a/src/SensorManager/Baro.cpp b/src/SensorManager/Baro.cpp index 0f3fe4e9..f8340a08 100644 --- a/src/SensorManager/Baro.cpp +++ b/src/SensorManager/Baro.cpp @@ -98,7 +98,7 @@ bool Baro::init(struct k_msgq * queue) { void Baro::start(int sample_rate_idx) { baro_initial_discard = 1; - k_timeout_t t = K_USEC(1e6 / sample_rates.true_sample_rates[sample_rate_idx]); + k_timeout_t t = K_USEC(1000000.0f / sample_rates.true_sample_rates[sample_rate_idx]); //bmp.set_interrogation_rate(setting.reg_val); //bmp.start(); diff --git a/src/SensorManager/BoneConduction.cpp b/src/SensorManager/BoneConduction.cpp index fad4f0d6..26d4f648 100644 --- a/src/SensorManager/BoneConduction.cpp +++ b/src/SensorManager/BoneConduction.cpp @@ -111,12 +111,12 @@ void BoneConduction::sensor_timer_handler(struct k_timer *dummy) { void BoneConduction::start(int sample_rate_idx) { if (!_active) return; - t_sample_us = 1e6 / sample_rates.true_sample_rates[sample_rate_idx]; + t_sample_us = 1000000.0f / sample_rates.true_sample_rates[sample_rate_idx]; k_timeout_t t = K_USEC(t_sample_us); int word_size = 3 * sizeof(int16_t) + 1; - _num_samples_buffered = MIN(MAX(1, (int) (CONFIG_SENSOR_LATENCY_MS * 1e3 / t_sample_us)), 512 / word_size - 8); // Buffer size is 512 bytes + _num_samples_buffered = MIN(MAX(1, (int) (CONFIG_SENSOR_LATENCY_MS * 1000.0f / t_sample_us)), 512 / word_size - 8); // Buffer size is 512 bytes bma580.init(sample_rates.reg_vals[sample_rate_idx], _num_samples_buffered * word_size); bma580.start(); diff --git a/src/SensorManager/IMU.cpp b/src/SensorManager/IMU.cpp index 0314fbdf..cf7e9e49 100644 --- a/src/SensorManager/IMU.cpp +++ b/src/SensorManager/IMU.cpp @@ -88,7 +88,7 @@ bool IMU::init(struct k_msgq * queue) { void IMU::start(int sample_rate_idx) { if (!_active) return; - k_timeout_t t = K_USEC(1e6 / sample_rates.true_sample_rates[sample_rate_idx]); + k_timeout_t t = K_USEC(1000000.0f / sample_rates.true_sample_rates[sample_rate_idx]); imu.setAccelODR(sample_rates.reg_vals[sample_rate_idx]); diff --git a/src/SensorManager/MLX90632/MLX90632.cpp b/src/SensorManager/MLX90632/MLX90632.cpp index 7f2bab1b..b794e952 100644 --- a/src/SensorManager/MLX90632/MLX90632.cpp +++ b/src/SensorManager/MLX90632/MLX90632.cpp @@ -244,9 +244,9 @@ float MLX90632::getObjectTemp(status& returnError) double AMB = (sixRAM / 12.0) / VRta * pow(2, 19); - float S = (float)(lowerRAM + upperRAM) / 2.0; + float S = (float)(lowerRAM + upperRAM) / 2.0f; double VRto = nineRAM + Ka * (sixRAM / 12.0); - double Sto = (S / 12.0) / VRto * (double)pow(2, 19); + double Sto = ((double)S / 12.0) / VRto * pow(2, 19); double TAdut = (AMB - Eb) / Ea + 25.0; @@ -268,7 +268,7 @@ float MLX90632::getObjectTemp(status& returnError) float MLX90632::getObjectTempF() { float tempC = getObjectTemp(); - float tempF = tempC * 9.0/5.0 + 32.0; + float tempF = tempC * 9.0f / 5.0f + 32.0f; return(tempF); } diff --git a/src/SensorManager/Microphone.cpp b/src/SensorManager/Microphone.cpp index 45913d52..ed1c4f1a 100644 --- a/src/SensorManager/Microphone.cpp +++ b/src/SensorManager/Microphone.cpp @@ -58,7 +58,7 @@ void Microphone::start(int sample_rate_idx) { if (!_active) return; - LOG_INF("Starting Microphone at %f Hz", sample_rates.sample_rates[sample_rate_idx]); + LOG_INF("Starting Microphone at %f Hz", (double)sample_rates.sample_rates[sample_rate_idx]); record_to_sd(_sd_logging); audio_datapath_aquire(&fifo_rx); diff --git a/src/SensorManager/PPG.cpp b/src/SensorManager/PPG.cpp index 993c1fd0..1358fbec 100644 --- a/src/SensorManager/PPG.cpp +++ b/src/SensorManager/PPG.cpp @@ -138,11 +138,11 @@ void PPG::sensor_timer_handler(struct k_timer *dummy) { void PPG::start(int sample_rate_idx) { if (!_active) return; - t_sample_us = 1e6 / sample_rates.true_sample_rates[sample_rate_idx]; + t_sample_us = 1000000.0f / sample_rates.true_sample_rates[sample_rate_idx]; k_timeout_t t = K_USEC(t_sample_us); - _num_samples_buffered = MIN(MAX(1, (int) (CONFIG_SENSOR_LATENCY_MS * 1e3 / t_sample_us)), FIFO_SIZE / LED_NUM - 2); + _num_samples_buffered = MIN(MAX(1, (int) (CONFIG_SENSOR_LATENCY_MS * 1000.0f / t_sample_us)), FIFO_SIZE / LED_NUM - 2); ppg.set_interrogation_rate(sample_rates.reg_vals[sample_rate_idx]); ppg.set_watermark(FIFO_SIZE - _num_samples_buffered * LED_NUM); diff --git a/src/SensorManager/SensorManager.cpp b/src/SensorManager/SensorManager.cpp index 36f6fa44..d5d8364c 100644 --- a/src/SensorManager/SensorManager.cpp +++ b/src/SensorManager/SensorManager.cpp @@ -204,7 +204,7 @@ static void config_work_handler(struct k_work *work) { float sampleRate = getSampleRateForSensorId(config.sensorId, config.sampleRateIndex); if (sampleRate <= 0) { - LOG_ERR("Invalid sample rate %f for sensor %i", sampleRate, config.sensorId); + LOG_ERR("Invalid sample rate %f for sensor %i", (double)sampleRate, config.sensorId); return; } diff --git a/src/SensorManager/Temp.cpp b/src/SensorManager/Temp.cpp index 9ba794d5..935d4812 100644 --- a/src/SensorManager/Temp.cpp +++ b/src/SensorManager/Temp.cpp @@ -86,7 +86,7 @@ void Temp::sensor_timer_handler(struct k_timer *dummy) { void Temp::start(int sample_rate_idx) { if (!_active) return; - k_timeout_t t = K_USEC(1e6 / sample_rates.true_sample_rates[sample_rate_idx]); + k_timeout_t t = K_USEC(1000000.0f / sample_rates.true_sample_rates[sample_rate_idx]); temp.setSampleRateRegVal(sample_rates.reg_vals[sample_rate_idx]); temp.continuousMode(); diff --git a/src/bluetooth/bt_management/bt_mgmt_conn_interval.cpp b/src/bluetooth/bt_management/bt_mgmt_conn_interval.cpp index fa089483..e7fb9836 100644 --- a/src/bluetooth/bt_management/bt_mgmt_conn_interval.cpp +++ b/src/bluetooth/bt_management/bt_mgmt_conn_interval.cpp @@ -146,6 +146,6 @@ void BtMgmtConnIntervalStrategy::request_interval(uint16_t min_units, uint16_t m } else { LOG_INF("Requested CI [%u..%u] units (%.2f..%.2f ms)", min_units, max_units, - min_units * 1.25f, max_units * 1.25f); + (double)(min_units * 1.25f), (double)(max_units * 1.25f)); } -} \ No newline at end of file +} From 1ea6a27ad34c6a002c1de66310812ece784db458 Mon Sep 17 00:00:00 2001 From: Oliver Bagge Date: Tue, 8 Sep 2026 11:03:21 +0200 Subject: [PATCH 75/77] fix(bma580): clear X acceleration when FIFO frame omits axis Remove the duplicated X-axis FIFO header check so the existing else branch is reachable. This makes X-axis handling consistent with Y and Z when a FIFO frame does not contain that axis. --- src/SensorManager/BMA580/bma5.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SensorManager/BMA580/bma5.c b/src/SensorManager/BMA580/bma5.c index 5b15f41d..f47a2034 100644 --- a/src/SensorManager/BMA580/bma5.c +++ b/src/SensorManager/BMA580/bma5.c @@ -1928,14 +1928,14 @@ static int8_t unpack_accel_sensor_time_16_bit_data(uint8_t frame_header, (frame_header != BMA5_FIFO_SENS_TIME_EN_DEDI_FRM)) { if (frame_header & BMA5_FIFO_ACC_X_MSK) { - if (frame_header & BMA5_FIFO_ACC_X_MSK) { /* Accel raw x data */ data_lsb = fifo->data[(*data_start_index)++]; data_msb = fifo->data[(*data_start_index)++]; accel_data->x = (int16_t)((data_msb << 8) | data_lsb); - } else { - accel_data->x = 0; - } + } + else + { + accel_data->x = 0; } if (frame_header & BMA5_FIFO_ACC_Y_MSK) From 8efb8b2e0425f3b05cd7c979adc9bcb773ab4376 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Tue, 8 Sep 2026 14:25:56 +0200 Subject: [PATCH 76/77] ci(warnings): report application compiler warnings on pull requests --- .github/workflows/compiler_warnings.yaml | 143 ++++++++++++++++++ .../ci/extended-warning-compiler-launcher.sh | 12 ++ 2 files changed, 155 insertions(+) create mode 100644 .github/workflows/compiler_warnings.yaml create mode 100755 tools/ci/extended-warning-compiler-launcher.sh diff --git a/.github/workflows/compiler_warnings.yaml b/.github/workflows/compiler_warnings.yaml new file mode 100644 index 00000000..7e605d59 --- /dev/null +++ b/.github/workflows/compiler_warnings.yaml @@ -0,0 +1,143 @@ +name: Report Compiler Warnings + +on: + pull_request: {} + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + warnings: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + path: zephyr-workspace/open-earable-v2 + submodules: recursive + + - name: Install build dependencies + uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: ccache cmake device-tree-compiler dfu-util file gcc gcc-multilib g++-multilib gperf libmagic1 libsdl2-dev make ninja-build python3-dev python3-pip python3-setuptools python3-tk python3-wheel wget xz-utils + version: 1.0 + execute_install_scripts: true + + - name: Set up Zephyr workspace + run: | + pip3 install west + wget -q https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.0/zephyr-sdk-0.17.0_linux-x86_64_minimal.tar.xz + tar xf zephyr-sdk-0.17.0_linux-x86_64_minimal.tar.xz -C ~/ + ~/zephyr-sdk-0.17.0/setup.sh -c -t arm-zephyr-eabi + cd zephyr-workspace + west init -l open-earable-v2 + west update --narrow -o=--depth=1 + pip3 install -r zephyr/scripts/requirements.txt + + - name: Register custom board + run: | + mkdir -p zephyr-workspace/zephyr/boards/arm + cp -R zephyr-workspace/open-earable-v2/boards/teco/. zephyr-workspace/zephyr/boards/arm/ + + - name: Build with extended warnings + id: build + working-directory: zephyr-workspace + run: | + set -o pipefail + west build -d build-warnings --board openearable_v2/nrf5340/cpuapp \ + --pristine=always open-earable-v2 -- \ + -DFILE_SUFFIX=fota \ + -DCMAKE_C_COMPILER_LAUNCHER="$PWD/open-earable-v2/tools/ci/extended-warning-compiler-launcher.sh" \ + -DCMAKE_CXX_COMPILER_LAUNCHER="$PWD/open-earable-v2/tools/ci/extended-warning-compiler-launcher.sh" \ + 2>&1 | tee warning-build.log + + - name: Prepare warning report + if: always() + run: | + mkdir -p warning-report + log=zephyr-workspace/warning-build.log + clean="${RUNNER_TEMP:-/tmp}/warning-build.log" + all=warning-report/compiler-warnings.txt + project=warning-report/project-warnings.txt + config=warning-report/configuration-warnings.txt + sed -E 's/\x1B\[[0-9;]*[mK]//g' "$log" 2>/dev/null > "$clean" + grep -Ei '(^|[[:space:]])warning:' "$clean" > "$all" || true + grep '/zephyr-workspace/open-earable-v2/' "$all" \ + | sed -E 's#^.*/zephyr-workspace/open-earable-v2/##' \ + | awk '!seen[$0]++' > "$project" || true + awk ' + function flush() { + if (capturing && !seen[warning]++) { + print warning "\n" + } + capturing = 0 + warning = "" + } + function is_configuration_warning() { + return $0 ~ /^[[:space:]]*(---[[:space:]]*)?[Ww][Aa][Rr][Nn][Ii][Nn][Gg]:/ || + $0 ~ /^CMake (Developer )?Warning([[:space:]]|$)/ + } + function is_devicetree_warning() { + return $0 ~ /:[[:space:]]+Warning \([^)]*\):/ + } + { + if (is_devicetree_warning()) { + flush() + if (!seen[$0]++) { + print $0 "\n" + } + next + } + if (capturing && ($0 ~ /^[[:space:]]*$/ || is_configuration_warning())) { + flush() + } + if (!capturing && is_configuration_warning()) { + capturing = 1 + } + if (capturing) { + warning = warning (warning == "" ? "" : "\n") $0 + } + } + END { flush() } + ' "$clean" > "$config" || true + + comment=warning-report/pr-comment.md + { + echo '## Compiler warnings' + echo + if [ '${{ steps.build.outcome }}' = success ]; then + echo 'The extended-warning build completed successfully.' + else + echo '⚠️ The extended-warning build failed; this report may be incomplete.' + fi + echo + echo '### Application compiler warnings' + if [ -s "$project" ]; then + echo '
Show warnings' + echo; echo '```text'; head -n 100 "$project"; echo '```'; echo '
' + else + echo 'None.' + fi + echo + echo "[View this workflow run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)" + } | tee "$comment" >> "$GITHUB_STEP_SUMMARY" + + - name: Upload complete warning report + if: always() + uses: actions/upload-artifact@v4 + with: + name: compiler-warnings + path: warning-report + if-no-files-found: error + + - name: Comment on pull request + if: always() + continue-on-error: true + uses: thollander/actions-comment-pull-request@v3 + with: + file-path: warning-report/pr-comment.md + comment-tag: compiler-warnings diff --git a/tools/ci/extended-warning-compiler-launcher.sh b/tools/ci/extended-warning-compiler-launcher.sh new file mode 100755 index 00000000..07a9bd98 --- /dev/null +++ b/tools/ci/extended-warning-compiler-launcher.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env sh +# Apply CI-only diagnostics to this application's sources, not Zephyr/NCS code. +compiler=$1 +shift + +for arg in "$@"; do + case "$arg" in + */open-earable-v2/src/*) exec "$compiler" -Wextra -Wshadow "$@" ;; + esac +done + +exec "$compiler" "$@" From cffd79bba9d57a03d40bd5b3a81c9aacbfee84b4 Mon Sep 17 00:00:00 2001 From: Ruben Lohberg Date: Mon, 7 Sep 2026 17:06:29 +0200 Subject: [PATCH 77/77] ci: add versions to release artifact names --- .github/workflows/release_firmware.yaml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release_firmware.yaml b/.github/workflows/release_firmware.yaml index 9f591176..ebe90ad5 100644 --- a/.github/workflows/release_firmware.yaml +++ b/.github/workflows/release_firmware.yaml @@ -43,6 +43,24 @@ jobs: ls openearable_v2_fota (cd "openearable_v2_fota" && ls && zip -r ../openearable_v2_fota.zip *) + - name: Add version to release asset names + id: release-assets + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + set -euo pipefail + + # v2.2.8 -> 2.2.8 + version="${RELEASE_TAG#v}" + firmware_name="openearable_v2_firmware_${version}.elf" + fota_name="openearable_v2_fota_${version}.zip" + + mv openearable_v2_firmware.elf "$firmware_name" + mv openearable_v2_fota.zip "$fota_name" + + echo "firmware=$firmware_name" >> "$GITHUB_OUTPUT" + echo "fota=$fota_name" >> "$GITHUB_OUTPUT" + - name: Create Github Release & Upload Firmware + FOTA if: startsWith(github.ref, 'refs/tags/${{ inputs.release_tag }}') uses: softprops/action-gh-release@v2 @@ -52,7 +70,7 @@ jobs: draft: false prerelease: false files: | - openearable_v2_firmware.elf - openearable_v2_fota.zip + ${{ steps.release-assets.outputs.firmware }} + ${{ steps.release-assets.outputs.fota }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}