diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml new file mode 100644 index 0000000..56ea175 --- /dev/null +++ b/.github/workflows/native.yml @@ -0,0 +1,497 @@ +name: native + +on: + push: + branches: [main, master] + tags: ["liblecore-v*"] + paths: + - "native/liblecore/**" + - "bindings/python/**" + - "bindings/rust/**" + - "benchmarks/**" + - "tests/native/**" + - "tools/generate_liblecore_*.py" + - "scripts/lecore_*.py" + - "holographic/misc/holographic_reference.py" + - "docs/ISA.md" + - "LICENSE" + - "PRD.md" + - "ENG.md" + - ".github/workflows/native.yml" + pull_request: + paths: + - "native/liblecore/**" + - "bindings/python/**" + - "bindings/rust/**" + - "benchmarks/**" + - "tests/native/**" + - "tools/generate_liblecore_*.py" + - "scripts/lecore_*.py" + - "holographic/misc/holographic_reference.py" + - "docs/ISA.md" + - "LICENSE" + - "PRD.md" + - "ENG.md" + - ".github/workflows/native.yml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + portable: + name: ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: Linux GCC static + os: ubuntu-latest + cc: gcc + cxx: g++ + shared: "OFF" + - name: Linux Clang shared + os: ubuntu-latest + cc: clang + cxx: clang++ + shared: "ON" + - name: macOS Clang static + os: macos-14 + cc: clang + cxx: clang++ + shared: "OFF" + - name: Windows MSVC shared + os: windows-latest + cc: "" + cxx: "" + shared: "ON" + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + steps: + - uses: actions/checkout@v6 + + - name: Configure + run: >- + cmake -S native/liblecore -B build/liblecore + -DLECORE_BUILD_SHARED=${{ matrix.shared }} + -DLECORE_BUILD_TESTS=ON + -DLECORE_BUILD_EXAMPLES=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build/liblecore --config Release --parallel 2 + + - name: Test + run: ctest --test-dir build/liblecore -C Release --output-on-failure + + sanitizers: + name: Linux Clang ASan and UBSan + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + CC: clang + CXX: clang++ + ASAN_OPTIONS: detect_leaks=1:strict_string_checks=1 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + steps: + - uses: actions/checkout@v6 + + - name: Configure + run: >- + cmake -S native/liblecore -B build/liblecore-sanitized + -DLECORE_BUILD_SHARED=OFF + -DLECORE_BUILD_TESTS=ON + -DLECORE_BUILD_EXAMPLES=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DLECORE_ENABLE_SANITIZERS=ON + -DCMAKE_BUILD_TYPE=Debug + + - name: Build + run: cmake --build build/liblecore-sanitized --parallel 2 + + - name: Test + run: ctest --test-dir build/liblecore-sanitized --output-on-failure + + fuzz-smoke: + name: Public C API fuzz smoke + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + CC: clang + CXX: clang++ + ASAN_OPTIONS: detect_leaks=1:strict_string_checks=1:abort_on_error=1 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + steps: + - uses: actions/checkout@v6 + + - name: Configure libFuzzer targets + run: >- + cmake -S native/liblecore -B build/liblecore-fuzz + -DLECORE_BUILD_SHARED=OFF + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=OFF + -DLECORE_ENABLE_FORMAT=ON + -DLECORE_ENABLE_RADIX2=ON + -DLECORE_ENABLE_SANITIZERS=ON + -DLECORE_BUILD_FUZZERS=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DCMAKE_BUILD_TYPE=Debug + + - name: Build public API fuzzers + run: >- + cmake --build build/liblecore-fuzz + --target liblecore_fuzz_public_api liblecore_fuzz_format + --parallel 2 + + - name: Stage writable seed corpora + run: | + cp -R native/liblecore/tests/fuzz/corpus/public_api \ + "${{ runner.temp }}/liblecore-public-api-corpus" + cp -R native/liblecore/tests/fuzz/corpus/format \ + "${{ runner.temp }}/liblecore-format-corpus" + + - name: Smoke the context and vector API + run: >- + build/liblecore-fuzz/tests/fuzz/liblecore_fuzz_public_api + -runs=2000 + "${{ runner.temp }}/liblecore-public-api-corpus" + + - name: Smoke the descriptor codec API + run: >- + build/liblecore-fuzz/tests/fuzz/liblecore_fuzz_format + -runs=2000 + "${{ runner.temp }}/liblecore-format-corpus" + + fixture-drift: + name: Deterministic ISA fixture drift + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install the pinned numeric reference + run: >- + python -m pip install --disable-pip-version-check + --only-binary=:all: numpy==2.4.4 + + - name: Check committed fixtures + run: python tools/generate_liblecore_fixtures.py --check + + python-conformance: + name: Python ${{ matrix.python }} / NumPy ${{ matrix.numpy }} conformance + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + include: + - python: "3.9" + numpy: "1.26.4" + - python: "3.12" + numpy: "2.4.4" + env: + CC: gcc + CXX: g++ + PYTHONHASHSEED: "0" + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python }} + + - name: Install the pinned numeric reference + run: >- + python -m pip install --disable-pip-version-check + --only-binary=:all: numpy==${{ matrix.numpy }} + + - name: Configure the shared Release library + run: >- + cmake -S native/liblecore -B build/python-conformance + -DLECORE_BUILD_SHARED=ON + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=OFF + -DLECORE_ENABLE_FORMAT=ON + -DLECORE_ENABLE_RADIX2=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DCMAKE_BUILD_TYPE=Release + + - name: Build the shared Release library + run: cmake --build build/python-conformance --parallel 2 + + - name: Check direct-backend conformance + run: >- + python tests/native/test_liblecore_conformance.py + --library build/python-conformance/liblecore.so + --backend direct + --quiet + + - name: Check radix-2-backend conformance + run: >- + python tests/native/test_liblecore_conformance.py + --library build/python-conformance/liblecore.so + --backend radix2 + --quiet + + performance-regression: + name: Linux Release performance regression + runs-on: ubuntu-24.04 + timeout-minutes: 15 + env: + CC: gcc + CXX: g++ + PYTHONHASHSEED: "0" + CANDIDATE_BUILD: ${{ github.workspace }}/build/liblecore-performance + PERFORMANCE_REPORT: ${{ github.workspace }}/build/liblecore-performance/liblecore-performance.json + BASELINE_BUILD: ${{ github.workspace }}/build/liblecore-performance-base + BASELINE_REPORT: ${{ github.workspace }}/build/liblecore-performance-base/liblecore-performance-base.json + steps: + - uses: actions/checkout@v6 + + - name: Check out the pull-request base + if: github.event_name == 'pull_request' + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.base.sha }} + path: liblecore-base + persist-credentials: false + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install the pinned numeric reference + run: >- + python -m pip install --disable-pip-version-check + --only-binary=:all: numpy==2.4.4 + + - name: Test the performance policy checker + run: python3 tests/native/test_liblecore_performance_gate.py + + - name: Detect a benchmarkable pull-request base + id: baseline + shell: bash + run: | + if [[ -d liblecore-base/native/liblecore && \ + -f liblecore-base/native/liblecore/CMakeLists.txt ]]; then + echo "available=true" >> "$GITHUB_OUTPUT" + echo "A comparable liblecore base is available." + else + echo "available=false" >> "$GITHUB_OUTPUT" + echo "No comparable liblecore base is available; running the bootstrap invariant gate." + fi + + - name: Configure the shared Release library + run: >- + cmake -S native/liblecore -B "$CANDIDATE_BUILD" + -DLECORE_BUILD_SHARED=ON + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=OFF + -DLECORE_ENABLE_FORMAT=ON + -DLECORE_ENABLE_RADIX2=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DCMAKE_BUILD_TYPE=Release + + - name: Build the shared Release library + run: cmake --build "$CANDIDATE_BUILD" --parallel 2 + + - name: Configure the pull-request base with identical Release settings + if: steps.baseline.outputs.available == 'true' + run: >- + cmake -S liblecore-base/native/liblecore -B "$BASELINE_BUILD" + -DLECORE_BUILD_SHARED=ON + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=OFF + -DLECORE_ENABLE_FORMAT=ON + -DLECORE_ENABLE_RADIX2=ON + -DLECORE_WARNINGS_AS_ERRORS=ON + -DCMAKE_BUILD_TYPE=Release + + - name: Build the pull-request base + if: steps.baseline.outputs.available == 'true' + run: cmake --build "$BASELINE_BUILD" --parallel 2 + + - name: Measure base and candidate as an interleaved pair + if: steps.baseline.outputs.available == 'true' + run: >- + python3 benchmarks/bench_liblecore.py + --library "$CANDIDATE_BUILD/liblecore.so" + --baseline-library "$BASELINE_BUILD/liblecore.so" + --dimensions 256,512,1024 + --profiles both + --repeats 10 + --target-work 64000000 + --sample-target-ms 30 + --gate + --output "$PERFORMANCE_REPORT" + --baseline-output "$BASELINE_REPORT" + + - name: Measure the guarded bootstrap bind regimes + if: steps.baseline.outputs.available != 'true' + run: >- + python3 benchmarks/bench_liblecore.py + --library "$CANDIDATE_BUILD/liblecore.so" + --dimensions 256,512,1024 + --profiles both + --repeats 10 + --target-work 64000000 + --sample-target-ms 30 + --gate + --output "$PERFORMANCE_REPORT" + + - name: Enforce the paired candidate-versus-base policy + if: steps.baseline.outputs.available == 'true' + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$PERFORMANCE_REPORT" + --baseline "$BASELINE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + --summary "$GITHUB_STEP_SUMMARY" + + - name: Enforce the bootstrap invariant policy + if: steps.baseline.outputs.available != 'true' + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$PERFORMANCE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + --summary "$GITHUB_STEP_SUMMARY" + + - name: Upload the raw performance reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: liblecore-performance-${{ github.sha }} + path: | + ${{ env.PERFORMANCE_REPORT }} + ${{ env.BASELINE_REPORT }} + if-no-files-found: warn + retention-days: 30 + + amalgamation: + name: Amalgamation drift and standalone consumers + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + CC: clang + CXX: clang++ + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Check generated-source drift + run: python tools/generate_liblecore_amalgamation.py --check + + - name: Compile feature-on and feature-off consumers + run: python tests/native/check_liblecore_amalgamation.py + + rust: + name: Rust ABI-0 bindings (vendored and installed) + runs-on: ubuntu-latest + timeout-minutes: 20 + env: + CARGO_TERM_COLOR: always + steps: + - uses: actions/checkout@v6 + + - name: Install the declared minimum Rust toolchain + uses: dtolnay/rust-toolchain@1.77.2 + with: + components: clippy, rustfmt + + - name: Restore Cargo dependency cache + uses: actions/cache@v5 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-1.77.2-${{ hashFiles('bindings/rust/lecore-sys/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-1.77.2- + + - name: Fetch the locked dependencies + run: >- + cargo fetch + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --locked + + - name: Check formatting + run: >- + cargo fmt + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --all -- --check + + - name: Lint the vendored bindings offline + run: >- + cargo clippy + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --all-targets --locked --offline -- -D warnings + + - name: Test the vendored debug build offline + run: >- + cargo test + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --locked --offline + + - name: Test the vendored Release build offline + run: >- + cargo test + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --release --locked --offline + + - name: Install a static C prefix + run: | + cmake -S native/liblecore -B build/rust-installed \ + -DCMAKE_INSTALL_PREFIX="${{ runner.temp }}/liblecore-prefix" \ + -DLECORE_BUILD_SHARED=OFF \ + -DLECORE_BUILD_TESTS=OFF \ + -DLECORE_BUILD_EXAMPLES=OFF \ + -DLECORE_WARNINGS_AS_ERRORS=ON \ + -DCMAKE_BUILD_TYPE=Release + cmake --build build/rust-installed --target install --parallel 2 + + - name: Test explicit installed-prefix mode offline + env: + LECORE_SYS_INCLUDE_DIR: ${{ runner.temp }}/liblecore-prefix/include + LECORE_SYS_LIB_DIR: ${{ runner.temp }}/liblecore-prefix/lib + LECORE_SYS_LINK_KIND: static + run: >- + cargo test + --manifest-path bindings/rust/lecore-sys/Cargo.toml + --no-default-features --features installed + --locked --offline + + webassembly: + name: Emscripten WebAssembly standalone smoke + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - uses: actions/setup-node@v6 + with: + node-version: "24" + package-manager-cache: false + + - name: Install the pinned Emscripten SDK + uses: emscripten-core/setup-emsdk@v15 + with: + version: "5.0.1" + actions-cache-folder: emsdk-cache + + - name: Compile and run feature-on and feature-off modules + run: python tests/native/check_liblecore_wasm.py diff --git a/.gitignore b/.gitignore index b84605c..07b2e5d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ holographic_vsa_complete.zip /dist /build /build_pkg +/native/liblecore/build*/ +/native/liblecore/out/ *.egg-info repo.zip /temp diff --git a/ENG.md b/ENG.md new file mode 100644 index 0000000..676c59a --- /dev/null +++ b/ENG.md @@ -0,0 +1,980 @@ +# liblecore — Engineering Plan + +*Status: active · ABI-0 implementation preview, compatibility policy, validation record, and dependency-ordered +backlog.* + +--- + +## 1. Purpose and authority + +This document turns the product requirements in [`PRD.md`](PRD.md) into an implementable native-library plan. + +The authority order is deliberate: + +1. [`docs/ISA.md`](docs/ISA.md) defines observable architecture. +2. [`holographic/misc/holographic_reference.py`](holographic/misc/holographic_reference.py) is the definitional + executable reference. +3. [`tests/test_isa_conformance.py`](tests/test_isa_conformance.py) demonstrates how continuous values and exact + decisions are judged separately. +4. Versioned liblecore fixtures make that contract portable outside Python. +5. C backends implement the contract and may be replaced when a new implementation passes the same gates. +6. Consumer adapters preserve application-specific encoders, trace policies, legal-action rules, and persistence. + +Existing native implementations are behavioral oracles and workload sources. liblecore itself is a clean C11 +implementation derived from leCore's MIT-licensed specification and reference. Code from AGPL or repositories with +unclear license provenance is not copied into the canonical library without an explicit compatible grant. + +### 1.1 Implementation snapshot — 2026-08-08 + +This snapshot records what is present on the `liblecore-implementation` branch. “Locally verified” means the +artifact was exercised on the development host; it does not mean the corresponding GitHub Actions lane ran or that +the backlog item's full cross-platform or adopter acceptance gate is complete. + +| Backlog IDs | Delivered artifacts | Evidence and remaining gate | +|---|---|---| +| LC-001, LC-002, LC-006 | ABI-0 policy in this plan; `` with opaque contexts, fixed-width IDs, C++ guards, allocator hooks, introspection, and typed calls; `VERSION`, `ISA_VERSION`, changelog, and ABI-0 symbol manifest | Headers compile in local C11/C++17 consumers and the exported-symbol check passes locally. ABI v1 remains deliberately unfrozen. | +| LC-003, LC-008, LC-015 | Independent Python reference helpers, deterministic bit-pattern fixture generator, committed f64/f32 corpus, strict `ctypes` adapter, and JSON differential report | Fixture drift is clean; explicit direct and forced-radix suites each pass 11/11 locally under Python 3.9/NumPy 1.26 and the development Python/NumPy runtime, with complete decision agreement. Worst observed radix error was `5.329e-15` for f64 and `2.861e-06` for f32. Hosted Python 3.9/NumPy 1.26 and Python 3.12/NumPy 2.4 matrix evidence remains pending. | +| LC-004 | `native/liblecore/PROVENANCE.md`, MIT notices, and clean-room source boundaries | Source inventory is recorded locally; no Signal AGPL or unclear-license implementation source was copied. | +| LC-007, LC-016 | Optional 96-byte little-endian descriptor codec, CRC64-ECMA implementation, compatibility checks, and format tests | Round-trip, corrupt/truncated/future/incompatible cases pass locally; cross-platform CI remains pending. | +| LC-010–LC-014, LC-018 | C11 direct f64/f32 kernel, dense/scoring/cleanup APIs, context lifecycle, CMake static/shared installs, `pkg-config`, and C/C++ examples/consumer tests | Strict Release static CTest passes 11/11 and shared CTest passes 13/13 locally, including installed consumers, symbol guard, and Python direct conformance. Sanitizer tests and `cppcheck` are clean locally. The required hosted Tier 1 matrix has not yet run. | +| LC-020–LC-022, LC-029 | Portable radix-2 f64/f32 backend, batch/fixed-role/unbind-all calls, and ordered f64-query/f32-row scoring | Direct-versus-radix sweeps and fixture decisions pass locally. Forced radix-2 rejects unsupported dimensions; `AUTO` still resolves to direct. The f32 bound remains explicitly preview-grade pending adopter replay. | +| LC-023 | Reproducible public-ABI benchmark driver, paired CI regression policy, and `native/liblecore/BENCHMARKS.md` with setup, scratch, wins, losses, and numeric error | A local Apple-arm64 baseline and paired pre/post optimization report exist. The hosted base-comparison lane and adopter workload evidence remain pending before changing `AUTO`. | +| LC-024, LC-028 | Deterministic single-header/single-source amalgamation, drift checker, feature-on/off standalone builds, install metadata, SONAME/symbol guard | Generated-source drift, standalone C/C++, installed CMake, `pkg-config`, and local symbol checks pass. Release-trigger and hosted CI gates remain pending. | +| LC-025 | Emscripten feature-on/off compile-and-run checker and a pinned WebAssembly workflow lane | Both variants retain every enabled ABI-0 manifest export and run locally under Node. The GitHub Emscripten lane and full fixture-corpus replay have not yet been observed, so cross-target acceptance remains pending. | +| LC-026; LC-015 and partial LC-027 | Audited `lecore-sys` raw declarations plus safe non-`Send`/non-`Sync` owner and explicit vendored/installed modes; strict explicit-path NumPy adapter | Rust vendored debug/release, Clippy, and explicit installed-prefix tests pass locally offline; Python consumer/conformance tests also pass. The Python artifact is a conformance adapter, not an automatic production dispatcher. No external Rust adopter or published artifact is claimed. | +| LC-017 | Dedicated portable, sanitizer, fixture, Python, amalgamation, Rust, fuzz, and WebAssembly workflow definitions | Workflow implementation is present and `actionlint` is clean locally, but GitHub-hosted results are pending; LC-017 is not complete. | +| LC-038 | Seeded public-API and descriptor libFuzzer targets with ASan/UBSan configuration | Each target completed 2,000 Clang 22 ASan/UBSan runs locally without findings. This is a bounded local smoke only; the configured CI fuzz budget has not run. | +| LC-005, LC-019, LC-030–LC-037, LC-039 | No release tag/archive is published and no downstream repository was modified | Reference/beta publication, Signal replay, NoSQLite proof, other adopter work, and stable ABI v1 remain blocked on CI, release work, and external adopter evidence. | + +## 2. Engineering goals + +- Expose the frozen caller-supplied-vector HRR algebra subset through a stable, language-neutral C ABI; + `random_vector` and atom generation remain explicitly outside version 1. +- Preserve raw leCore semantics, including unnormalized bind and unbind. +- Make f64 and f32 distinct profiles with distinct conformance manifests. +- Support every positive `uint32_t` dimension that passes checked size/allocation limits through a direct backend, + and accelerate power-of-two dimensions with a portable radix-2 backend. +- Allocate all plans and scratch at context creation; allocate nothing in documented hot-path operations. +- Keep opaque implementation state behind caller-owned vector buffers and typed operations. +- Build reproducibly as static, shared, installed, vendored/amalgamated, Cargo, and Emscripten forms. +- Prove continuous-value conformance and exact decision agreement against the Python authority. +- Migrate existing consumers through feature flags, shadow execution, and replay evidence. +- Make failures, backend choice, feature support, and compatibility inspectable. + +## 3. Non-goals of ABI version 1 + +ABI v1 does not contain: + +- `UnifiedMind` or any high-level leCore subsystem; +- seeded atom generation; +- application encoders or vocabularies; +- a prescribed associative-memory update policy; +- application persistence, database, filesystem, network, or process APIs; +- generated scalar/SDF kernel execution; +- MAP/Hadamard binding, integer outer-product memory, or freestanding targets; +- compiler-specific vector types, public complex-number types, or exposed FFT layouts; or +- automatic online performance calibration. + +These boundaries are part of stability. An extension is not admitted by placing a function in the same header; it +must receive a named semantic profile, fixtures, a consumer, and a measured reason to exist. + +## 4. Architecture + +```mermaid +flowchart TD + ISA["ISA.md + Python definitional reference"] --> FIX["Versioned conformance fixtures"] + FIX --> DENSE["Dense vector primitives"] + FIX --> DIRECT["Direct HRR backend"] + FIX --> FFT["Portable radix-2 HRR backend"] + DENSE --> ABI["Stable liblecore C ABI"] + DIRECT --> ABI + FFT --> ABI + FORMAT["Versioned descriptor codec"] --> ABI + ABI --> C["C adapters: Signal / crlplrimes"] + ABI --> RUST["Rust adapter: lecore-sys"] + ABI --> PY["Python conformance adapter"] + ABI --> WASM["Amalgamation / Emscripten"] + C --> POLICY["Application-owned encoders, policy, traces, persistence"] + RUST --> POLICY + PY --> POLICY + WASM --> POLICY +``` + +### 4.1 Layers + +**Base vector layer.** The caller-supplied-vector ISA operations plus normalization, dot, batch scoring, and +validated-buffer utilities. The utilities are public only with their own specified fixtures; they are not silently +promoted to frozen ISA instructions. This layer owns no domain vocabulary. + +**HRR layer.** Raw circular convolution and involution-based unbinding, implemented by interchangeable direct and +FFT backends. + +**Descriptor layer.** Encodes and validates compatibility metadata and payload framing. It owns bytes, not files. + +**Bindings.** Thin Python and Rust adapters that validate language-level buffers and lifetimes without defining new +math. + +**Consumer adapters.** Live in consumer repositories. They own normalization composition, atom streams, feature +encoding, memory update rules, action legality, rollout policy, storage, and migrations. + +### 4.2 Implemented repository layout + +```text +native/liblecore/ + VERSION + ISA_VERSION + CMakeLists.txt + include/lecore/lecore.h + include/lecore/lecore_format.h + src/ + context.c + status.c + vector_f32.c + vector_f64.c + cleanup_f32.c + cleanup_f64.c + hrr_direct_f32.c + hrr_direct_f64.c + hrr_radix2_f32.c + hrr_radix2_f64.c + format.c + crc64.c + internal/ + tests/ + c/ + consumers/ + fuzz/ + cmake/lecoreConfig.cmake.in + pkgconfig/liblecore.pc.in + amalgamation/ +bindings/ + python/lecore_native.py + rust/lecore-sys/ +tests/native/ + fixtures/ + check_liblecore_amalgamation.py + check_liblecore_symbols.py + check_liblecore_wasm.py + test_liblecore_conformance.py +benchmarks/ + bench_liblecore.py +tools/ + generate_liblecore_fixtures.py + generate_liblecore_amalgamation.py +``` + +Generated amalgamation and fixture files are reproducible outputs. The native workflow is configured to regenerate +them and fail on drift; its first hosted run is still pending. + +## 5. Public ABI design + +The installed public headers are `` and, when interchange is enabled, +``. All symbols use the `lecore_` prefix. The CMake import is `lecore::lecore`; the +product and `pkg-config` package are named `liblecore`, while the linker output basename is `lecore`, producing +`liblecore.a`, `liblecore.so`, or `liblecore.dylib` and the conventional linker spelling `-llecore`. + +The following is the proposed stable ABI shape, not yet the frozen header. Preview `0.x` artifacts report ABI `0`, +may break between preview releases, and do not ship `*_v1` symbols as stable. ABI `1` and the prospective names below +are reserved until release milestone `LC-037` freezes them. + +```c +#define LECORE_ABI_VERSION UINT32_C(1) /* reserved for the LC-037 stable ABI */ + +typedef uint32_t lecore_status; +#define LECORE_OK UINT32_C(0) +#define LECORE_EINVAL UINT32_C(1) +#define LECORE_EDIM UINT32_C(2) +#define LECORE_EPROFILE UINT32_C(3) +#define LECORE_EBACKEND UINT32_C(4) +#define LECORE_EOVERFLOW UINT32_C(5) +#define LECORE_ENOMEM UINT32_C(6) +#define LECORE_EUNSUPPORTED UINT32_C(7) +#define LECORE_ENONFINITE UINT32_C(8) +#define LECORE_EFORMAT UINT32_C(9) +#define LECORE_ECHECKSUM UINT32_C(10) + +typedef uint32_t lecore_profile; +#define LECORE_PROFILE_HRR_F64_V1 UINT32_C(0x00010001) +#define LECORE_PROFILE_HRR_F32_V1 UINT32_C(0x00010002) + +typedef uint32_t lecore_backend; +#define LECORE_BACKEND_AUTO UINT32_C(0) +#define LECORE_BACKEND_DIRECT UINT32_C(1) +#define LECORE_BACKEND_RADIX2 UINT32_C(2) + +typedef uint32_t lecore_validation; +#define LECORE_VALIDATION_SHAPE UINT32_C(0) +#define LECORE_VALIDATION_FINITE UINT32_C(1) + +typedef struct lecore_allocator_v1 { + uint32_t struct_size; + void *user; + void *(LECORE_CALL *allocate)(void *user, size_t bytes, size_t alignment); + void (LECORE_CALL *deallocate)(void *user, void *pointer, size_t bytes, size_t alignment); +} lecore_allocator_v1; + +typedef struct lecore_config_v1 { + uint32_t struct_size; + uint32_t abi_version; + uint32_t profile; + uint32_t backend; + uint32_t validation; + uint32_t flags; + uint32_t dimension; + lecore_allocator_v1 allocator; + uint64_t reserved[4]; +} lecore_config_v1; + +typedef struct lecore_context lecore_context; +``` + +`LECORE_API` controls export visibility, `LECORE_CALL` fixes the supported platform calling convention for public +functions and callbacks, and all declarations are enclosed by C++ `extern "C"` guards. ABI-facing +status/profile/backend/validation values are fixed-width integers, not C enums, so compiler enum-size options cannot +change the calling convention. + +Profiles are composite, immutable semantic identities: `HRR_F64_V1` fixes HRR algebra, f64 storage, observable +decision/reduction order, and edge behavior; `HRR_F32_V1` does the same for f32. Internal reduction structure for a +TOL operation remains backend microarchitecture. Scalar type is therefore queryable but cannot conflict with the +selected profile. + +`native/liblecore/ISA_VERSION` is the dedicated source of truth returned by `lecore_isa_version()`. It versions the +declared caller-supplied-vector subset of [`docs/ISA.md`](docs/ISA.md) and advances only when that subset's observable +semantics change. Python `CORE_VERSION`, Python package semver, native package semver, ABI, and format versions do not +advance it automatically. + +Public structures begin with `struct_size`, reserve zeroed expansion space, and are initialized by library helpers. +Numeric constants are append-only. Unknown fields are ignored only when the receiving ABI explicitly permits the +larger structure; unknown major ABI versions fail closed. + +### 5.1 Lifecycle and introspection + +```c +LECORE_API uint32_t LECORE_CALL lecore_abi_version(void); +LECORE_API uint32_t LECORE_CALL lecore_isa_version(void); +LECORE_API const char *LECORE_CALL lecore_version_string(void); +LECORE_API uint64_t LECORE_CALL lecore_capabilities(void); +LECORE_API const char *LECORE_CALL lecore_status_string(lecore_status status); + +LECORE_API void LECORE_CALL lecore_config_init_v1(lecore_config_v1 *config); +LECORE_API lecore_status LECORE_CALL lecore_context_create( + const lecore_config_v1 *config, + lecore_context **out_context); +LECORE_API void LECORE_CALL lecore_context_destroy(lecore_context *context); + +LECORE_API uint32_t LECORE_CALL lecore_context_dimension(const lecore_context *context); +LECORE_API lecore_profile LECORE_CALL lecore_context_profile(const lecore_context *context); +LECORE_API lecore_backend LECORE_CALL lecore_context_backend(const lecore_context *context); +LECORE_API lecore_validation LECORE_CALL lecore_context_validation(const lecore_context *context); +LECORE_API size_t LECORE_CALL lecore_context_scratch_bytes(const lecore_context *context); +``` + +Allocator callbacks are supplied as a pair or not at all. Requests are nonzero and use a power-of-two alignment at +least `alignof(max_align_t)`; a `NULL` result maps to `LECORE_ENOMEM`. Every successful allocation is returned to the +matching callback with the original byte and alignment values. `lecore_context_create` sets `*out_context = NULL` +on every failure, rejects nonzero reserved fields and unknown flags, and exposes no partially built context. + +The eventual `lecore_config_init_v1` zeroes the full known structure, sets its size and ABI version, selects +`HRR_F64_V1`, `AUTO`, and `SHAPE`, and leaves dimension `0` so the caller must provide it explicitly. Preview +initializers use their preview-version name and ABI value. + +If no allocator is supplied, hosted builds use the library's documented default allocator. All allocations occur +during context creation. A later freestanding profile may require caller-provided storage, but that does not need to +complicate the hosted ABI before a consumer exists. + +### 5.2 Typed operation families + +The operation families have parallel f64 and f32 symbols. They do not accept `void *` element buffers or silently +convert precision. + +```text +lecore_normalize_f64 / lecore_normalize_f32 +lecore_dot_f64 / lecore_dot_f32 +lecore_dot_many_f64 / lecore_dot_many_f32 +lecore_cosine_f64 / lecore_cosine_f32 +lecore_cosine_many_f64 / lecore_cosine_many_f32 +lecore_hrr_bind_f64 / lecore_hrr_bind_f32 +lecore_hrr_unbind_f64 / lecore_hrr_unbind_f32 +lecore_involution_f64 / lecore_involution_f32 +lecore_permute_f64 / lecore_permute_f32 +lecore_bundle_f64 / lecore_bundle_f32 +lecore_cleanup_f64 / lecore_cleanup_f32 +lecore_hrr_bind_batch_f64 / lecore_hrr_bind_batch_f32 +lecore_hrr_bind_fixed_f64 / lecore_hrr_bind_fixed_f32 +lecore_hrr_unbind_all_f64 / lecore_hrr_unbind_all_f32 +lecore_cosine_many_f64_f32 +``` + +Scalar outputs such as cosine and cleanup score are written through output pointers so every operation can return a +status consistently. + +### 5.3 Calling rules + +- Context dimension is immutable and in `1..UINT32_MAX`; construction still fails when checked workspace or byte + arithmetic exceeds `size_t` or available memory. Counts and strides use `size_t`. +- An f64 function rejects an f32 context and vice versa. +- Row strides are expressed in elements, never bytes. +- A documented operation may permit output to exactly alias one complete input. Partial overlap is unsupported. +- Batch input and output regions are disjoint in ABI v1 unless the operation explicitly states otherwise. +- `dot` reduces components in ascending index order; cosine uses that reduction, and codebook scoring visits rows + in ascending stable-index order. +- `bundle` rejects an empty input; its internal summation order is free within value and downstream-decision gates. +- `cleanup` scans candidates in ascending index order and selects the lowest index on an exact tie. +- `AUTO` backend selection is deterministic from versioned rules, configuration, dimension, and compiled + capabilities. It does not benchmark during a call. +- A forced unsupported backend returns `LECORE_EUNSUPPORTED`; it never substitutes another backend silently. +- No operation prints, aborts, writes global error state, changes the floating-point environment, opens resources, + or allocates after context creation. +- The library has hidden default symbol visibility; only `LECORE_API` declarations are exported. +- During ABI `0`, a breaking preview change requires a native package minor bump and a new `major.minor` preview + SONAME; patch releases within that preview minor remain compatible. Starting with ABI `1`, every ABI break + increments both the public ABI major and the shared-library SONAME. + +### 5.4 Validation model + +Shape, null, overflow, profile, and backend checks are always enabled. Raw arithmetic must preserve the ISA's frozen +non-finite behavior: NaN propagates through bind, unbind, and bundle; cosine propagates it unless the other operand's +norm is exactly zero, because the pinned zero-norm branch takes precedence and returns `+0.0`. Cleanup follows NumPy +argmax behavior, including selecting the first NaN score. Element-wise finite validation is therefore an explicit +boundary layer: + +- `LECORE_VALIDATION_FINITE` scans inputs and returns `LECORE_ENONFINITE` before computing. +- `LECORE_VALIDATION_SHAPE` performs raw ISA arithmetic without an extra scan. +- Standalone `lecore_validate_f32` and `lecore_validate_f64` helpers let adapters validate once at ingestion. + +The initializer defaults to `LECORE_VALIDATION_SHAPE` so the numeric API itself remains ISA-conformant. For finite +inputs, `FINITE` produces the same profile values; for non-finite input it deliberately returns `LECORE_ENONFINITE` +before arithmetic. External-data adapters must choose `FINITE` or call a standalone validator before entering raw +operations. Conformance fixtures pin NaN/Inf propagation and cleanup decisions in raw mode as well as rejection in +checked mode. The chosen boundary mode remains queryable from the context. + +## 6. Semantic profiles + +The f64 caller-supplied-vector ISA subset retains the authority's existing value rule on its declared corpus: +`max_abs(candidate-reference) <= 1e-9` with no relative-error substitution. Task `LC-003` records that corpus's +dimension, magnitude, norm, and conditioning domain; it may add wider-magnitude stress reports, but those are +supplemental and cannot widen the frozen gate. A broader f64 contract requires a new immutable profile or ISA-subset +revision. Supporting utilities introduced by liblecore publish their own domains and abs/rel metrics. The f32 profile +also receives a separate manifest rather than inheriting or weakening f64. Exact reindexes, zero/non-finite edges, +and decisions are checked separately and are never excused by numeric tolerance. + +### 6.1 `HRR_F64_V1` + +| Operation | Required semantics | Class | +|---|---|---| +| Bind | `out[n] = sum_k a[k] * b[(n-k) mod D]`; no post-normalization | TOL, max absolute error `1e-9` on declared ISA corpus | +| Involution | `out[0] = a[0]`, `out[i] = a[D-i]` | EXACT | +| Unbind | Bind composite with key involution; no post-normalization | TOL | +| Bundle | Sum all rows then normalize; zero sum returns the zero vector | TOL with exact zero edge | +| Normalize | Divide by L2 norm; zero input returns the zero vector | TOL with exact zero edge | +| Dot | Ascending-index f64 multiply/accumulate with f64 output | TOL, per conformance manifest | +| Dot-many | Apply scalar dot to rows in ascending stable-index order; each output matches scalar semantics | TOL values; exact decision corpus | +| Cosine | Dot divided by both norms; either exact zero norm returns `+0.0` before other non-finite handling | TOL with exact zero/NaN precedence | +| Permute | NumPy `roll` convention, shift modulo dimension | EXACT | +| Cleanup | Highest cosine; exact tie selects lowest candidate index | EXACT decision | + +Continuous output bits are not frozen where the ISA declares tolerance. Observable decisions and reindexes are. + +### 6.2 `HRR_F32_V1` + +The f32 profile has the same mathematical operations and edges but a separate reference fixture set, input domain, +and absolute/relative thresholds. `1e-5` on the initial unit-domain corpus is a hypothesis, not a frozen promise; +the final bounds are established from the Signal replay corpus and adversarial fixtures before the profile becomes +stable. Its dot/cosine family multiplies, accumulates in ascending index order, and returns f32. + +`lecore_cosine_many_f64_f32` is the separately named NoSQLite utility defined by `LC-029`: it takes one f64 query, +row-major f32 corpus data, and f64 score output. Each f32 component converts exactly to f64; query norm-square, row +norm-square, and dot accumulate in ascending component order using f64. Either zero norm yields `+0.0`; raw +non-finite behavior follows the ISA boundary rule. Rows are emitted in stable ascending order, while document-ID tie +ordering remains host-owned. This operation is not an implicit widening of `HRR_F32_V1`. + +The f32 profile is not described as bit-identical to f64, and a near-tie that cannot preserve a required decision is +a failed backend/adoption result—not something hidden by widening the value tolerance. + +### 6.3 Floating-point build contract + +Reproducible builds prohibit unsafe reassociation and implicit contraction changes. Where supported, CI uses +`-fno-fast-math` and `-ffp-contract=off` or platform equivalents. The supported environment is round-to-nearest; +the library does not change rounding mode. + +Decision-producing scans and replay-pinned score reductions use their specified order. Other TOL reductions, +including internal FFT and bundle reductions, remain microarchitecture and may vary within value and exact-decision +gates. A future relaxed decision profile requires a different ID, explicit selection, fixtures, and consumer +evidence. + +Tier 1 profiles require `CHAR_BIT == 8`, 32-bit radix-2 `float`, 64-bit radix-2 `double`, `FLT_EVAL_METHOD == 0`, +and IEC 60559/default-rounding/evaluation behavior. The native build enforces representation/evaluation guards and +runs a configure probe; cross builds must explicitly set `LECORE_ASSUME_IEC_60559=ON` after validating the target. +Unsupported representations fail the build rather than masquerading as `HRR_F32_V1`/`HRR_F64_V1`. The ABI-0 +numeric domain excludes subnormal inputs and cases whose required reference output is a nonzero subnormal, exposes +no subnormal-preservation capability, and makes no preserve-versus-flush claim. Any such report or guarantee is +deferred to a later ABI/profile with explicit fixtures. + +## 7. Backend design + +### 7.1 Direct backend + +The direct backend implements circular convolution from its O(D²) definition. It is intentionally simple, supports +every positive `uint32_t` dimension that passes checked resource limits, and is the native correctness oracle. It is +not expected to be the production choice at large dimensions. + +Implementation priorities are clarity, overflow-safe indexing, exact loop order, alias safety, and agreement with +the Python reference. + +### 7.2 Portable radix-2 backend + +The first optimized backend is a dependency-free radix-2 FFT with precomputed bit-reversal and twiddle tables in +the context. It supports power-of-two dimensions and uses preallocated real/complex scratch hidden inside the +implementation. + +Requirements: + +- raw bind output is not normalized; +- unbind follows the ISA's involution semantics; +- direct-versus-FFT results meet the profile tolerance; +- the committed decision corpus agrees exactly; +- all tails and allowed aliases are tested; +- plan construction reports allocation failure without partial context exposure; and +- the backend publishes scratch use and measured crossover against direct and relevant consumer baselines. + +`AUTO` chooses radix-2 for a supported dimension only after the regime has been accepted and encoded as a static, +versioned rule. Otherwise it chooses direct. Platform FFT libraries and SIMD are follow-on backends, not v1 +dependencies. + +### 7.3 Batch operations + +Batch APIs exist only for measured recurring shapes: + +- pairwise rows: `bind(A[i], B[i])`; +- one fixed role against many rows; +- one trace unbound against many keys; and +- one query scored against a row-major codebook. + +The implementation may reuse spectra and fuse loops, but scalar and batch paths share semantic fixtures. A batch +path that stays within numeric tolerance yet changes a required winner fails decision conformance. + +## 8. Context, memory, and threading + +`lecore_context` owns configuration, backend plan, twiddle data, scratch, allocator metadata, and counters useful +for tests or diagnostics. It does not own caller vectors, vocabularies, traces, or codebooks. + +- Calls on a live context are single-thread-owned and must not run concurrently. Destruction is thread-neutral only + after all calls have quiesced; a custom allocator's deallocator must be valid on the destroying thread. +- Distinct contexts are independent and may execute concurrently. +- The library contains no process-global mutable state or global last-error buffer. +- Read-only version/status strings have static lifetime. +- Destruction accepts `NULL` and releases exactly what successful creation acquired. +- Context creation validates all size multiplications before allocation. +- Debug allocation instrumentation is compiled into tests, not required in release consumers. + +An optional associative-memory layer, if admitted later, uses caller-owned trace buffers and explicitly names its +update policy. Raw additive accumulation and normalize-after-each-write are observably different and cannot share +one implicit `store` contract. + +## 9. Interchange descriptor + +Opaque contexts are never serialized. The standard v1 distribution must ship and test an optional format component +that can frame vector and trace payloads with a fixed 96-byte little-endian header. Use and linkage are optional: +`LECORE_ENABLE_FORMAT=OFF` may remove the codec from constrained builds, and capability queries report that fact; +the numeric ABI never depends on the codec. Existing application formats may carry the equivalent contract in their +own metadata or a sidecar without rewriting payload bytes. The exact format bytes become immutable when contract +task `LC-007` is accepted; task `LC-016` implements them. Code encodes/decodes fields explicitly rather than +`fwrite()` a C structure. + +| Offset | Field | Meaning | +|---:|---|---| +| 0 | `magic[8]` = `LECOREV\0` | Format identity | +| 8 | `u16 format_major` | Breaking format version | +| 10 | `u16 format_minor` | Additive format version | +| 12 | `u16 header_bytes` | Forward-compatible header length | +| 14 | `u16 flags` | Defined format flags only | +| 16 | `u32 artifact_kind` | Vector, matrix/codebook, or trace | +| 20 | `u32 semantic_profile` | For example `HRR_F64_V1` | +| 24 | `u32 scalar_type` | IEEE f32 or f64 | +| 28 | `u32 dimension` | Elements per vector | +| 32 | `u32 normalization_policy` | Raw, unit, or application-declared | +| 36 | `u32 atom_scheme` | `EXTERNAL` in v1 | +| 40 | `u64 seed_namespace` | Application namespace or zero | +| 48 | `u64 vector_count` | Payload rows | +| 56 | `u64 payload_bytes` | Exact byte count following header | +| 64 | `u8 application_contract[16]` | Opaque host contract identifier/digest | +| 80 | `u64 content_crc64_ecma` | Header-with-zeroed-checksum plus payload | +| 88 | `u8 reserved[8]` | Must be zero in v1 | + +Payloads use little-endian IEEE-754 bits. The scalar field is required for decoding and must agree with the composite +semantic profile. `application_contract` is a raw 16-byte identifier: all zero means unspecified; any nonzero value +is compared byte-for-byte. Its canonical derivation and domain separation are owned and published by the host. This +lets Signal, for example, bind its encoder, action vocabulary, key generation, and trace-policy versions without +putting those game-specific fields in the generic header. Persisted compatibility follows format and semantic +profiles, not the in-process shared-library ABI. + +CRC is CRC-64/ECMA-182: polynomial `0x42F0E1EBA9EA3693`, init `0`, `refin=false`, `refout=false`, xorout `0`, and +check value `0x6C40DF5F0B497347` for `123456789`. It covers exactly `header_bytes` with bytes 80–87 treated as zero, +followed by the declared payload. Decoding rejects nonzero reserved bytes, unknown flags, arithmetic overflow, +payload-size mismatch, `header_bytes < 96`, and buffers whose exact length is not +`header_bytes + payload_bytes`. For dense vector, matrix/codebook, and trace kinds, overflow-checked +`vector_count * dimension * scalar_bytes` must equal `payload_bytes`, and both dimension and vector count are +nonzero. An unknown major version is rejected. A newer minor with the same major is accepted only when the header +length is sane and every set flag is understood. Every future mandatory extension must set a flag, ensuring that an +unaware reader rejects rather than silently ignores it. + +The base descriptor codec owns no file handles, migrations, memory maps, compression, or databases. A consumer may +use a stronger external digest or signature; CRC64 detects accidental payload/metadata corruption and is not an +authenticity mechanism. + +## 10. Atom generation + +ABI v1 accepts caller-provided vectors. It does not claim that a NumPy seed, Signal seed, or `leos-c` seed denotes +the same atom. + +`PORTABLE_ATOM_V1` is a separately gated future profile. Before it receives an ID, it must define: + +- input byte encoding and Unicode normalization for names; +- domain separation and seed namespace encoding; +- integer hash/PRNG operations and endianness; +- uniform or normal sampling conversion rules; +- unitary-atom construction, if included; +- normalization and zero handling; +- f32/f64 conversion; and +- exact Python, C, Rust, and WASM golden vectors. + +Legacy atom streams remain named adapter formats. They are not quietly reclassified as portable. + +## 11. Build, package, and release + +### 11.1 CMake targets and options + +Required consumption modes: + +- `add_subdirectory()` from pinned source; +- installed `find_package(lecore CONFIG REQUIRED)` and `lecore::lecore`; +- `pkg-config --cflags --libs liblecore`; +- generated `lecore.c` plus `lecore.h` for Make, Cargo, and Emscripten; and +- direct static or shared linking from a release archive. + +Implemented CMake options: + +```text +LECORE_BUILD_SHARED +LECORE_BUILD_TESTS +LECORE_BUILD_EXAMPLES +LECORE_BUILD_AMALGAMATION +LECORE_BUILD_FUZZERS +LECORE_ENABLE_RADIX2 +LECORE_ENABLE_FORMAT +LECORE_ENABLE_SANITIZERS +LECORE_WARNINGS_AS_ERRORS +LECORE_ASSUME_IEC_60559 +``` + +The f64 and f32 profiles are part of the ABI-0 kernel rather than independently removable compile options. Format +and radix-2 may be omitted without changing the enabled base profiles' semantics. Capability queries report what +is present. Cross builds must explicitly assert a validated IEC 60559 target representation. + +### 11.2 Amalgamation + +The amalgamation is generated from canonical sources, carries license/version data, and exposes the same ABI. Its +local drift and standalone consumer checks pass; the native workflow is configured to regenerate it and fail if +the committed output differs. Hand editing an amalgamated file is unsupported. + +### 11.3 Versioning + +- `native/liblecore/VERSION` owns the native package semantic version independently of the Python `VERSION` file. +- Native `0.x` previews report ABI `0` and may break only at an explicitly documented native package minor bump; + ABI `1` begins only at `LC-037`. +- ABI-0 shared libraries use package `major.minor` as their preview SONAME (`0.1.x` maps to `0.1`), so an + incompatible later preview cannot satisfy an older loader identity. ABI-0 patch releases remain compatible. +- Starting with ABI `1`, the public ABI major is the SONAME and every ABI break increments both. +- ISA version, semantic profile IDs, atom scheme, and artifact format version evolve independently. +- Patch releases fix conformant implementation defects without changing ABI or profile meaning. +- Minor releases add symbols, backends, or immutable profiles. +- Native package major releases may accompany stable ABI breaks, but the public ABI version—not package SemVer by + itself—governs the stable SONAME. +- Release tags use a distinct namespace such as `liblecore-v0.1.0`. + +The native validation workflow is configured to trigger on `liblecore-v*`. A release-packaging workflow still +needs to be added for `LC-019`/`LC-039`; it must not assume the repository's existing general `v*` Python workflow +will recognize or correctly package native tags. + +Every release archive contains source, public headers, amalgamation, license notices, changelog, build descriptor, +checksums, and minimal C/C++ examples. Consumers pin the tag/archive and digest. A machine-local installed library +is convenient for development but is not the only CI or deployment path. + +### 11.4 Python packaging boundary + +The existing `leos-core` wheel remains pure Python and NumPy-backed until a separate product decision changes it. +The native library is optional: absence must not break import, the test suite, or documented NumPy behavior. + +Because the current repository automation patch-publishes the Python package after green merges to `main`, native +release automation must not infer its version from those patch bumps. A future optional native wheel is a separate +deliverable with explicit platform and fallback policy. + +## 12. Language bindings + +### 12.1 Python + +The first Python binding is a test and conformance adapter using ctypes. It: + +- resolves only an explicitly supplied build or test artifact; +- verifies ABI and semantic profile before use; +- accepts contiguous NumPy arrays with exact dtype and dimension; +- keeps owning Python objects alive across each call; +- converts status codes into typed exceptions; and +- never silently replaces the canonical NumPy implementation. + +A production dispatcher is out of scope until native benchmarks show a useful regime and the existing leCore +native-dispatch policy can represent its setup cost and decision gate honestly. + +### 12.2 Rust + +`lecore-sys` contains hand-audited raw declarations plus a small safe ownership wrapper: + +- `Context` owns and destroys one C context; +- ABI-0 `Context` is deliberately neither `Send` nor `Sync`; ownership transfer may be reconsidered only after the + native contract explicitly permits it; +- slices are validated for dtype, dimension, count, and overlap before FFI; +- raw profile and status values remain available for forward compatibility; and +- both vendored `cc::Build` and installed-library modes have consumer tests. + +Application crates retain domain state and persistence. No Rust binding mirrors the private C context layout. + +### 12.3 WebAssembly + +Emscripten compiles the amalgamation as a standalone program. The implemented smoke builds feature-on and +feature-off variants, exercises ABI/ISA/capability queries, direct/radix binding, and descriptor round-trip, and +runs them under Node with filesystem support disabled. Loading the complete fixture corpus and emitting a full +conformance report remains follow-on test depth; the first milestone requires no threads, dynamic loading, +filesystem, or network APIs. + +## 13. Testing strategy + +### 13.1 Fixture corpus + +Fixtures contain explicit IEEE bits and metadata; they are not regenerated from an unspecified RNG during a test. +Dimensions include `1`, `2`, `3`, `7`, `8`, `64`, `384`, `1024`, and `4096` where the operation cost is reasonable. + +Required cases: + +- convolution identity and shifted impulse; +- bind commutativity; +- unbind recovery and unitary cases supplied by the reference; +- involution self-inverse and permutation round-trip; +- zero normalization, zero-sum bundle, and zero cosine; +- exact finite ties and ascending-index cleanup; +- raw NaN/Inf propagation and first-NaN cleanup, plus checked-boundary rejection; +- combined zero-vector/NaN-vector cosine, pinning the zero branch's precedence; +- profile-representation probes and explicit exclusion-boundary cases for subnormals; +- null, empty, count, stride, dimension, profile, and overflow failures; +- every documented aliasing case and forbidden partial overlap; +- direct fallback on non-power-of-two dimensions; +- FFT versus direct and C versus Python comparisons; +- batch versus scalar value and decision conformance; +- independent contexts running concurrently; +- allocation instrumentation proving zero hot-path allocations; and +- descriptor golden bytes, truncation, unknown major, mismatch, and checksum corruption. + +### 13.2 Test classes + +**C unit tests.** Lifecycle, statuses, helpers, exact operations, backend internals, allocation, descriptors, and +consumer-header compilation. + +**Python differential tests.** In-scope ISA operations run against the existing definitional reference. Task +`LC-008` adds explicit reference helpers and fixtures for public utilities such as normalize, dot, batch scoring, +and cleanup so no API is tested against an accidental production implementation. The report records target, +compiler, backend, profile, input domain, absolute/relative error, and decision agreement. + +**Property tests.** Commutativity, self-inverse operations, permutation inverse, scale behavior, zero behavior, and +backend equivalence across generated finite inputs. + +**Fuzzing.** Descriptor decoding, configuration, size arithmetic, strides, counts, and public operations with valid +allocated buffers. Fuzzing begins after the ABI shape stabilizes so targets do not become throwaway maintenance. + +**Consumer tests.** Build and run from outside the source tree using installed CMake, `pkg-config`, vendored +amalgamation, Cargo, and Emscripten paths. + +### 13.3 CI matrix + +A dedicated native workflow is defined independently from the Python affected-test selector. It is `actionlint` +clean locally but has not yet run on GitHub as of the dated snapshot. + +| Tier | Lane | Coverage | +|---|---|---| +| 1 | Ubuntu x86_64 GCC | Debug/release, f32/f64, direct/radix-2, C11 and C++17 consumers | +| 1 | Ubuntu x86_64 Clang | Same plus ASan and UBSan | +| 1 | macOS arm64 Clang | Hosted build, installed consumer, profile fixtures | +| 1 | Emscripten | Amalgamated build and fixture self-test | +| 1 | Package | Installed CMake, `pkg-config`, archive checksum, exported-symbol manifest | +| 2 candidate | Windows x86_64 MSVC | Static/shared and C/C++ consumer tests before support is advertised | +| 2 candidate | macOS x86_64 Clang | Hosted and installed-consumer tests before support is advertised | +| 2 candidate | Linux arm64 GCC/Clang | Native or emulated conformance and consumer tests before support is advertised | + +Every profile lane runs the scalar-representation and IEC 60559 configure probes before claiming a capability. +Semantic and memory-safety failures are merge gates. The pinned Linux Release performance lane is also a merge +gate: it enforces same-build numerical, metadata, and measurement-quality invariants and, once the pull-request +base contains liblecore, compares candidate and base samples as aligned, interleaved pairs on one runner. The gate +evaluates the median of the paired candidate/base ratios once—there is no asymmetric retry—and publishes both raw +reports as artifacts and the scorecard in the job summary. + +## 14. Benchmark policy + +Benchmarks record: + +- profile, backend, compiler, flags, target, and library version; +- dimension, row count, codebook size, and call count; +- context construction and first-call cost; +- steady-state latency and throughput distributions; +- scratch and resident memory; +- artifact/code size; and +- numeric error and exact decision agreement beside timing. + +Comparisons include Python/NumPy where relevant, direct C, optimized C, and the consumer's existing implementation. +Results identify their regime; there is no single “C speedup” number. A fast backend that loses in a consumer's +working regime remains disabled there. + +`AUTO` rules change only through reviewed benchmark evidence and are versioned/static. They never depend on a noisy +online calibration that could select a different decision path from run to run. + +## 15. Consumer migration plans + +Implementation work in downstream repositories follows each repository's local contribution rules. For the current +`~/develop` workspace that means an issue, lease, feature branch, and dedicated worktree before edits; canonical +checkouts are not modified in place. These planning documents authorize no cross-repository mutation by themselves. + +### 15.1 Signal — first semantic adopter + +Signal retains its key generator, pilot encoder, action vocabulary, legality, gossip compatibility checks, routed +memory, and trace update policy. + +The adapter exposes three build/runtime modes: + +- `legacy`: current implementation only; +- `lecore`: liblecore operations only; and +- `shadow`: both execute, but legacy alone mutates state and selects actions. + +Signal's historical normalized bind is reproduced by raw liblecore bind followed by explicit normalize. Its existing +ordered dot scoring over normalized vectors maps to `lecore_dot_f32`; an adapter retains Signal's current non-finite +score-to-zero behavior rather than substituting general cosine semantics. Shadow reports compare vectors, every +action score, winner, top-two margin, route, trace metadata, native replay, and WASM replay. Promotion requires zero +unexplained action or route changes across the committed corpus and a documented performance result. Rollback +remains one build flag until at least one release after promotion. + +Signal is AGPL and may consume MIT liblecore. Its implementation is not copied into the MIT library without an +explicit compatible license grant. + +### 15.2 NoSQLite — Rust packaging adopter + +NoSQLite preserves `holographic-hash-v1`, canonical vector storage, quantization, finite-input checks, routing, and +exact result authority in Rust. + +Its first integration may simply compile/link a pinned liblecore and execute ABI/profile smoke tests. A scoring +migration follows only after liblecore has an explicit mixed f64-query/f32-corpus score operation matching +NoSQLite's ordered f64 accumulation. Document-ID tie ordering and exact result authority remain in Rust and are +verified by the host adapter; the query is not downcast merely to fit the first f32 API. + +### 15.3 CosyWorld — advisory ranker + +CosyWorld continues to obtain legal offers from its authoritative C rules kernel before ranking. liblecore state +lives outside the exported `cw_world` layout. The Rust host may use an optional liblecore ranker over the legal set, +while the existing deterministic heuristic remains fallback and verifier. + +With the feature disabled, journals and replays remain byte-identical. liblecore never authorizes a world mutation +or an action absent from the legal offer set. + +### 15.4 Zero Grounded Literary LM — WASM and size adopter + +Zero LM retains its deterministic text encoder, fixed capacity, partition routing, host-facing `lm_holo_*` ABI, +and recall policy. A compile flag may replace only generic normalization, ordered dot scoring over normalized +vectors, and cleanup; its adapter retains legacy non-finite handling. Native and WASM self-tests must preserve +chosen results and stay within a recorded code-size budget. + +Because the repository currently lacks a complete license file, its implementation is not source material for the +canonical library until provenance is resolved. + +### 15.5 crlplrimes — dependency cleanup + +crlplrimes stops compiling generic HNN source through a sibling Signal path. It may consume the same pinned +liblecore artifact behind an experimental ranking adapter while exact verifiers remain authoritative. Evidence +claims are not promoted merely because the dependency builds. + +## 16. Detailed backlog + +These are stable planning IDs, not a substitute for an issue tracker. Their states record evidence visible on this +branch as of 2026-08-08: + +- **Implemented · locally verified:** the artifact exists and its relevant local checks pass; this is not full + completion where the acceptance text requires hosted CI, publication, or an adopter. +- **Implemented · CI pending:** the implementation and local checks exist, but the required GitHub/cross-platform + result has not been observed. +- **Partial:** only part of the acceptance gate is substantiated. +- **Blocked · release/adopter:** meaningful next evidence belongs to publication or a downstream repository. +- **Proposed:** no implementation is claimed. + +Priorities mean: + +- **P0:** required for the first stable product; +- **P1:** required for broad, proven adoption but not the initial reference preview; +- **P2:** earned extension, deliberately outside the critical path. + +### 16.1 Epic-to-task map + +| Product epic | Engineering tasks | +|---|---| +| LC-E0 Contract and governance | LC-001, LC-002, LC-004, LC-006 | +| LC-E1 Reference conformance | LC-003, LC-008, LC-011–LC-015 | +| LC-E2 Portable optimized core | LC-020–LC-023, LC-047 | +| LC-E3 Distribution and CI | LC-010, LC-017–LC-019, LC-024–LC-028, LC-037–LC-039 | +| LC-E4 Signal shadow migration | LC-005, LC-030, LC-031 | +| LC-E5 Rust adoption | LC-026, LC-029, LC-032 | +| LC-E6 Interchange | LC-007, LC-016 | +| LC-E7 Memory and world integrations | LC-033–LC-036, LC-041 | +| LC-E8 Additional profiles | LC-040, LC-042–LC-046 | + +### 16.2 M0 — Contract and provenance + +| ID | Pri | State | Work | Depends on | Acceptance | +|---|---:|---|---|---|---| +| LC-001 | P0 | Implemented · local | Freeze v1 scope and non-goals | — | PRD/ENG review confirms raw HRR, profile naming, atom exclusion, policy exclusion, and consumer ownership. | +| LC-002 | P0 | Implemented · locally verified | Draft and review the public ABI | LC-001 | Header compiles as C11/C++17; IDs, statuses, aliasing, validation, threading, allocator, and evolution rules are documented. | +| LC-003 | P0 | Implemented · locally verified | Define the core ISA fixture schema and corpus | LC-001 | Existing caller-supplied-vector fixtures record their input domain, f64 `atol=1e-9`/`rtol=0`, exact decisions/reindexes, zero and non-finite edges, and representative dimensions. | +| LC-004 | P0 | Implemented · locally reviewed | Create a native provenance/license ledger | LC-001 | Every canonical source is MIT-derived or independently written; no AGPL or unclear-license code is copied. | +| LC-005 | P0 | Blocked · external adopters | Capture adopter replay and benchmark fixtures | LC-001 | Signal replay plus representative Rust/world workloads are reproducible and versioned in their owning repos. | +| LC-006 | P0 | Implemented · local preview policy | Freeze native release/version policy | LC-001 | Native semver, ABI, ISA/profile/format versions, tags, checksums, and compatibility rules are accepted. | +| LC-007 | P0 | Implemented · locally verified | Freeze descriptor mechanics and profile IDs | LC-002, LC-003 | The fixed 96-byte header and compatibility rules are documented; the implemented f64 and f32 profile IDs are registered without conflating payload and semantic metadata. | +| LC-008 | P0 | Implemented · locally verified | Complete utility and batch references/fixtures | LC-001, LC-003 | Normalize, dot, cleanup, and admitted batch scoring have explicit slow Python helpers and fixtures independent of production code. | + +**M0 gate:** a second implementation can be written from the public contract and fixtures without reading a +consumer's private math. + +### 16.3 M1 — Portable reference preview + +| ID | Pri | State | Work | Depends on | Acceptance | +|---|---:|---|---|---|---| +| LC-010 | P0 | Implemented · locally verified | Create CMake/install/source skeleton | LC-002, LC-004 | Static/shared builds, hidden visibility, `lecore::lecore`, and external install test pass. | +| LC-011 | P0 | Implemented · locally verified | Implement context, allocator, status, and introspection | LC-010 | Two contexts execute independently; failures leak nothing; no stderr/global error state. | +| LC-012 | P0 | Implemented · locally verified | Implement f64 dense primitives | LC-003, LC-008, LC-011 | ISA operations retain the frozen absolute gate; supporting utilities pass their separately frozen rules and zero edges. | +| LC-013 | P0 | Implemented · locally verified | Implement direct f64 HRR | LC-003, LC-011 | Raw bind/unbind support every positive fixture dimension with max absolute error `<=1e-9` on the declared ISA corpus. | +| LC-014 | P0 | Implemented · locally verified | Implement scoring, cleanup, and boundary validation | LC-003, LC-008, LC-012, LC-013 | Ordered decisions, first-NaN/lowest-index behavior, raw propagation, checked rejection, and failure tests pass. | +| LC-015 | P0 | Implemented · locally verified | Add Python differential harness | LC-012–LC-014 | ctypes checks every operation and emits target/backend/profile/error/decision report. | +| LC-016 | P0 | Implemented · locally verified | Implement descriptor codec | LC-007, LC-010 | Golden bytes round-trip; truncated, corrupt, future-major, and incompatible records are rejected. | +| LC-017 | P0 | Implemented · CI pending | Add Tier 1 native CI and sanitizers | LC-010–LC-016 | Linux x86_64 GCC/Clang, macOS arm64, sanitizer, and external header-consumer lanes are green. | +| LC-018 | P0 | Implemented · locally verified | Add clean-room embedding examples | LC-017 | C and C++ examples build outside the source tree using installed and vendored paths. | +| LC-019 | P0 | Blocked · CI and publication | Publish ABI-0 reference preview | LC-006, LC-017, LC-018 | Versioned archive, checksums, license, changelog, build descriptor, instability notice, and limitations are available. | + +**M1 gate:** the deliberately slow C backend is trustworthy enough to be a native oracle. + +### 16.4 M2 — Optimized beta and distribution + +| ID | Pri | State | Work | Depends on | Acceptance | +|---|---:|---|---|---|---| +| LC-020 | P0 | Implemented · locally verified | Implement portable f64 radix-2 backend | LC-013, LC-015 | Power-of-two values meet tolerance; decision corpus agrees; unsupported forced use fails loudly. | +| LC-021 | P0 | Implemented · locally verified | Implement f64 batch and fixed-role APIs | LC-014, LC-020 | Declared layouts, strides, aliases, and tails are tested locally; scalar/batch values and decisions conform. Downstream adapter proof remains separate. | +| LC-022 | P0 | Implemented · locally verified preview | Define and implement `HRR_F32_V1` | LC-014, LC-020, LC-021 | The ABI-0 f32 profile, domain, fixtures, and preview bounds are recorded and exact fixture decisions agree. Stable bounds remain gated on Signal replay evidence. | +| LC-023 | P0 | Partial · regression gate implemented; adopter evidence pending | Establish benchmark and static AUTO policy | LC-005, LC-020–LC-022 | Reports real dimensions, setup, memory, code size, wins/losses; AUTO only chooses an earned regime. | +| LC-024 | P0 | Implemented · locally verified | Generate amalgamation | LC-017, LC-021, LC-022, LC-029 | Generated source has no drift and passes the complete supported native fixture subset. | +| LC-025 | P0 | Partial · export/runtime smoke; CI pending | Add Emscripten lane | LC-024 | Amalgamation retains every enabled ABI-0 export and passes the feature-on/off runtime smoke without OS services or dynamic loading; full fixture-corpus replay and hosted CI remain open. | +| LC-026 | P0 | Implemented · locally verified; CI pending | Add `lecore-sys` Rust wrapper | LC-017, LC-024, LC-029 | Safe ownership wrapper, mixed scoring declaration, and vendored/installed Cargo consumer tests pass. | +| LC-027 | P1 | Partial · strict adapter only | Add optional Python runtime wrapper | LC-015, LC-017 | Explicit artifact resolution, dtype/profile checks, exceptions, and canonical NumPy fallback are tested. The delivered conformance adapter never silently dispatches or falls back; a production dispatcher remains a separate decision. | +| LC-028 | P0 | Partial · CI/release pending | Add ABI/release guard | LC-006, LC-017, LC-024, LC-029 | ABI-0/1 policy, SONAME, symbols, capabilities, headers, archives, and `liblecore-v*` triggers are CI-gated. | +| LC-029 | P0 | Implemented · locally verified | Add `lecore_cosine_many_f64_f32` | LC-005, LC-008, LC-014, LC-022 | Independent reference fixtures match ordered f64 scores; host-owned ID tie ordering remains unchanged. A real NoSQLite fixture remains adopter work. | +| LC-038 | P0 | Implemented · bounded local fuzz only | Add public API fuzz targets | LC-002, LC-016, LC-017, LC-022, LC-029 | Descriptor, config, sizes/strides, and every operation complete the configured ASan/UBSan CI budget without findings. | +| LC-039 | P0 | Blocked · CI and publication | Publish ABI-0 optimized beta | LC-006, LC-019, LC-023–LC-026, LC-028, LC-029, LC-038 | Pinned archive/digest includes optimized profiles, amalgamation, C/C++/Rust examples, Emscripten self-test, and instability notice. | +| LC-047 | P1 | Proposed · scalar bind gate only | Extend paired performance coverage to specialized hot paths | LC-014, LC-021, LC-023 | Stable f64/f32 cells cover radix fixed-role bind, unbind-all, cosine-many, and cleanup with raw public-ABI paired samples and Linux-calibrated thresholds. | + +**M2 gate:** `LC-039` publishes a beta that clean C, C++, Rust, and WASM consumers plus the Python conformance +adapter can use without repository-layout assumptions. The optional production Python wrapper (`LC-027`) is not a +beta blocker. + +### 16.5 M3 — Consumer proving + +| ID | Pri | State | Work | Depends on | Acceptance | +|---|---:|---|---|---|---| +| LC-030 | P0 | Blocked · Signal adopter | Build Signal legacy/lecore/shadow adapter | LC-005, LC-022, LC-025, LC-039 | Native and WASM consume the pinned beta; explicit normalization/scoring preserves current contract; rollback is one flag. | +| LC-031 | P0 | Blocked · Signal adopter | Run and publish Signal shadow replay | LC-005, LC-023, LC-030 | Zero unexplained action/route flips; score/margin/vector deltas and performance are recorded. | +| LC-032 | P0 | Blocked · NoSQLite adopter | Prove NoSQLite packaging and mixed scoring | LC-005, LC-026, LC-029, LC-039 | A real score path uses the unmodified pinned beta; encoder/storage bytes stay unchanged and Rust-owned exact IDs/order match. | +| LC-033 | P1 | Proposed · external adopter | Add CosyWorld advisory adapter | LC-022, LC-026 | Legal set remains kernel-owned; disabled mode journal is identical; illegal actions cannot be selected. | +| LC-034 | P1 | Proposed · external adopter | Add Zero LM WASM adapter | LC-022, LC-025 | Native/WASM choices agree with legacy and recorded size budget passes. | +| LC-035 | P1 | Proposed · external adopter | Replace crlplrimes sibling-source coupling | LC-030 or LC-032 | Build consumes pinned liblecore; exact verifier authority and experimental status remain explicit. | +| LC-036 | P1 | Blocked · sustained adopter release | Retire one duplicate generic implementation | LC-037 plus sustained adopter release | Removal follows replay parity and rollback window, not initial compilation. | +| LC-037 | P0 | Blocked · release and adopters | Release stable ABI v1 | LC-006, LC-028, LC-031, LC-032, LC-038, LC-039 | Signal and NoSQLite pass their gates; ABI/profile/format contracts freeze with migration guide. | + +**M3 gate:** C and Rust adopters use the same pinned artifact with replay evidence, and a duplicate-retirement plan +has an owner and rollback window. Actual deletion follows sustained use rather than blocking stable v1. + +### 16.6 M4 — Earned extensions + +| ID | Pri | State | Work | Depends on | Acceptance | +|---|---:|---|---|---|---| +| LC-040 | P2 | Proposed | Specify `PORTABLE_ATOM_V1` | LC-037 | Python, C, Rust, and WASM generate identical checked-in atom bits from the same seed/name bytes. | +| LC-041 | P1 | Proposed | Specify optional associative-trace API | LC-031, LC-033, LC-037 | Caller-owned trace and separately versioned update policy pass capacity, store, query, and top-k tests. | +| LC-042 | P2 | Proposed | Evaluate platform FFT/SIMD backends | LC-037 | Named target workload wins; conformance and decision corpus pass; unsupported platforms retain baseline. | +| LC-043 | P2 | Proposed | Evaluate exact-index profile | LC-032, LC-037 | NoSQLite or Zero proves reuse without changing result authority or persisted semantics. | +| LC-044 | P2 | Proposed | Evaluate MAP profile | LC-037 | Separate ID/API and Holonet evidence; no function or payload can be mistaken for HRR. | +| LC-045 | P2 | Proposed | Evaluate integer/freestanding profile | LC-037 | NSRL/asix supplies a real consumer, exact arithmetic contract, build target, and profile fixtures. | +| LC-046 | P2 | Proposed | Design generated-kernel plugin ABI | LC-037 | Versioned descriptor/IR, target/compiler cache identity, and trusted-input boundary remain separate from vector ABI. | + +M4 items are not promises. A negative evaluation is a completed result when it records why the extension should not +ship. + +## 17. Delivery sequencing + +The dependency map has several required branches; this is a compact release spine with selected prerequisite +branches, not a substitute for the authoritative tables above: + +```text +Contract: LC-001 -> LC-002 -> LC-007 + | -> LC-010 + LC-001 -> LC-003 -> LC-007 / LC-008 + LC-001 -> LC-004 -> LC-010 + LC-001 -> LC-005 -> LC-023 / LC-029 / LC-030 / LC-031 / LC-032 + LC-001 -> LC-006 -> LC-019 / LC-028 / LC-037 + +Reference: LC-010 -> LC-011 + LC-003 + LC-008 + LC-011 -> LC-012 + LC-003 + LC-011 -> LC-013 + LC-003 + LC-008 + LC-012 + LC-013 -> LC-014 -> LC-015 + LC-007 + LC-010 -> LC-016 + LC-010..LC-016 -> LC-017 -> LC-018 -> LC-019 + +Beta: LC-013 + LC-015 -> LC-020 -> LC-021 + LC-014 + LC-020 + LC-021 -> LC-022 + LC-005 + LC-020 + LC-021 + LC-022 -> LC-023 + LC-005 + LC-008 + LC-014 + LC-022 -> LC-029 + LC-017 + LC-021 + LC-022 + LC-029 -> LC-024 -> LC-025 + LC-017 + LC-024 + LC-029 -> LC-026 + LC-006 + LC-017 + LC-024 + LC-029 -> LC-028 + LC-002 + LC-016 + LC-017 + LC-022 + LC-029 -> LC-038 + LC-006 + LC-019 + LC-023–LC-026 + LC-028 + LC-029 + LC-038 -> LC-039 + +Adoption: LC-005 + LC-022 + LC-025 + LC-039 -> LC-030 + LC-005 + LC-023 + LC-030 -> LC-031 + LC-005 + LC-026 + LC-029 + LC-039 -> LC-032 + LC-006 + LC-028 + LC-031 + LC-032 + LC-038 + LC-039 -> LC-037 +``` + +Parallel work after the ABI draft includes: + +- fixtures (`LC-003`) and provenance (`LC-004`); +- release policy (`LC-006`) and descriptor freeze (`LC-007`); +- direct HRR (`LC-013`) and dense primitives (`LC-012`); +- native packaging (`LC-024`) and the fuzz harness (`LC-038`) after the public surface stabilizes; and +- Signal replay and NoSQLite proof after their adapters, independent of one another. + +No optimized implementation begins before its definitional counterpart and fixtures. No consumer deletes its old +backend before shadow evidence and a rollback window. + +## 18. Definition of done + +A backlog item is complete only when: + +- its public semantics and failure behavior are documented; +- relevant unit, conformance, decision, safety, and consumer tests run in CI; +- any generated artifact is reproducible and drift-gated; +- any performance statement includes baseline, regime, setup cost, uncertainty, and kept negative; +- the actual backend/profile/version is introspectable; +- packaging works from outside the source tree where applicable; +- migration and rollback behavior are documented for adopter work; +- licensing/provenance is recorded for new native source; and +- the change preserves the base-versus-extension boundary in this plan. + +Passing a unit test is necessary, but stable ABI v1 is earned only by two real consumers with replay evidence across +different integration styles. diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000..8def07b --- /dev/null +++ b/PRD.md @@ -0,0 +1,467 @@ +# liblecore — Product Requirements + +*Status: active · ABI-0 implementation preview and rollout plan for a portable C implementation of leCore's +frozen vector algebra.* + +--- + +## 1. Executive summary + +`liblecore` is a small, portable C11 library that carries leCore's stable, caller-supplied-vector algebra into +native, Rust, Python, WebAssembly, and other host environments without requiring Python or NumPy at runtime. + +The product is not a C rewrite of leCore. It is the narrow execution substrate defined by +[`docs/ISA.md`](docs/ISA.md): bind, unbind, involution, bundle, cosine similarity, permutation, cleanup, and the +supporting vector operations required to use them safely. The existing Python implementation and the definitional +reference in [`holographic/misc/holographic_reference.py`](holographic/misc/holographic_reference.py) remain the +semantic authority. + +The primary product value is **one interoperable algebra across projects**, not a promised universal speedup. +Optimized FFT, SIMD, and platform backends are replaceable microarchitecture. They ship only after they preserve +the ISA's tolerant values and exact observable decisions and demonstrate a measured win in a named workload. + +The canonical C source now lives in this repository under `native/liblecore/` and retains leCore's MIT license. +The current artifact is an unstable ABI-0 implementation preview: it is suitable for local conformance and +integration work, but it is not a published release and has not earned downstream adoption claims. Consumers will +ultimately use pinned release artifacts rather than copying neighboring repositories or relying on a machine-global +development checkout. + +### 1.1 Current product state — 2026-08-08 + +The branch contains the complete intended ABI-0 kernel surface: direct and portable radix-2 f64/f32 HRR, +caller-owned batch and mixed scoring, the optional descriptor codec, CMake and `pkg-config` packaging, +amalgamation, C/C++ examples, strict Python bindings and differential fixtures, audited Rust bindings, WebAssembly +smoke tooling, ABI symbol guards, benchmarks, and bounded fuzz targets. The delivered artifacts and their local +validation map to backlog IDs in [`ENG.md` section 16](ENG.md#16-detailed-backlog). + +This snapshot does **not** claim a release, green GitHub-hosted CI, a cross-platform `AUTO` crossover policy, or +Signal/NoSQLite integration. `AUTO` deliberately continues to select the direct backend until representative +adopter evidence earns a static dispatch rule. Publication, Tier 1 CI evidence, and all downstream migration gates +remain open. + +## 2. Problem + +Projects around leCore already need portions of its vector algebra in environments where Python is unavailable or +undesirable. Several independent implementations have appeared as a result: + +- a double-precision FFT engine in `leos-c`; +- Signal's float32 holographic pilot memory, compiled for native and WebAssembly targets; +- asix's hosted and freestanding holographic schedulers; +- Holonet's MAP/Hadamard SIMD scorer; +- deterministic text-vector indexes in NoSQLite and Zero Grounded Literary LM; and +- an integer outer-product associative memory in NSRL. + +These implementations prove demand, but they are not interchangeable. Some normalize binding while leCore does +not; some use circular convolution while others use element-wise multiplication; seed generators, data types, +dimensions, trace update rules, and persisted formats also differ. Reusing a function merely named `bind` can +therefore produce plausible but incorrect results. + +The current alternatives have recurring costs: + +1. **Semantic drift.** Independent ports change normalization, inverse, tie-breaking, and zero-vector behavior. +2. **Brittle integration.** Some consumers compile source directly from a sibling checkout instead of depending on + a versioned artifact. +3. **No cross-language contract.** A vector's algebra, scalar type, dimension, generator, and format are often + implicit. +4. **Repeated native plumbing.** C, Rust, Python, and WebAssembly projects each recreate build and FFI conventions. +5. **Unverifiable optimization.** A faster continuous result can still flip an exact cleanup or action decision. + +## 3. Product vision + +> **One semantic substrate, from NumPy to native.** A vector created for a declared liblecore profile can move +> between supported projects and runtimes without changing what its operations mean or silently changing a +> decision. + +In the target state: + +- leCore owns the written ISA, reference implementation, golden fixtures, and canonical C source; +- native projects link the same small library instead of maintaining their own generic HRR primitives; +- language bindings are thin and policy-free; +- new liblecore interchange uses a versioned profile descriptor, while existing application formats may carry the + equivalent compatibility contract in their own metadata or a sidecar without rewriting payload bytes; +- exact decisions are replayable and auditable even when optimized continuous calculations are tolerance-based; +- hosts retain authority over domain policy, legality, persistence, and lifecycle; and +- unsupported profiles, incompatible data, and unavailable accelerators fail loudly rather than silently changing + behavior. + +## 4. Product principles + +1. **Semantics before speed.** The ISA and reference suite decide correctness. Benchmarks decide whether an + optimization is worth keeping. +2. **Architecture is not microarchitecture.** Circular convolution, zero behavior, and cleanup ties are contract; + FFT implementation and SIMD width are replaceable details. +3. **Explicit profiles, never ambiguous verbs.** HRR, MAP, exact-index, and integer memories must not share an + unqualified `bind` contract. +4. **The host owns policy.** liblecore calculates; Signal, CosyWorld, NoSQLite, and other hosts decide what may be + stored, ranked, committed, or exposed. +5. **Portable baseline, optional acceleration.** A conformant C11 path is always available. Accelerators are + additive and may be refused without losing capability. +6. **No silent compatibility guesses.** ABI, semantic profile, dimension, generator, normalization policy, and + persisted format are explicit and versioned at their proper layers. +7. **No hot-path allocation.** A context allocates or receives workspace at initialization; vector operations do + not allocate. +8. **Reproducible consumption.** Projects pin a source or binary artifact and digest. A developer's `~/develop` + layout is never a production dependency. +9. **Keep the negative result.** If a backend fails conformance, loses on the target workload, or cannot amortize + setup cost, record that outcome and keep it out of automatic dispatch. + +## 5. Users and jobs to be done + +### 5.1 leCore maintainer + +**Job:** evolve the vector ISA once and verify every implementation against one reference. + +Needs versioned semantics, differential tests, release discipline, and a clear separation between the Python +research engine and the stable native surface. + +### 5.2 Native simulation maintainer + +**Job:** use deterministic vector memory and ranking from C or WebAssembly without importing Python. + +Signal is the leading example. The maintainer needs float32 support, fixed runtime cost, native/WASM parity, +explicit compatibility descriptors, and a shadow migration path that cannot silently change an action. + +### 5.3 Rust application maintainer + +**Job:** compile a small, pinned C dependency and call it through a minimal safe wrapper. + +NoSQLite and CosyWorld already compile C through Cargo. They need an amalgamated source option, stable symbols, +caller-owned data, and no C-owned domain state exposed through Rust layouts. + +### 5.4 Constrained or browser-runtime developer + +**Job:** use the same vector operations in Emscripten/WASM builds with no dynamic loader and a small dependency +surface. + +The developer needs C11/libm portability, static linking, bounded memory, no threads requirement, and no mandatory +filesystem or process APIs. + +### 5.5 Research and systems developer + +**Job:** compare implementations and introduce optimized or new algebra profiles without weakening existing +contracts. + +The developer needs the slow definitional backend, golden vectors, benchmark fixtures, feature queries, and an +extension process that prevents experimental semantics from entering the base ABI accidentally. + +## 6. Product scope + +### 6.1 Target version 1 scope + +- A stable C11 ABI with C++ linkage guards and symbol-visibility macros. +- Opaque contexts with explicit dimension, composite profile, validation mode, and backend; the profile fixes the + scalar type, which remains queryable rather than independently configurable. +- `HRR_F64_V1`, the reference-compatible double-precision profile. +- `HRR_F32_V1`, the deployment profile for Signal and WebAssembly. +- The caller-supplied-vector ISA subset: raw HRR bind, standalone involution, involution-based unbind, bundle, + cosine, permutation, and cleanup. `random_vector` and other atom generation remain explicitly outside version 1. +- Supporting normalization and dot operations, specified as utilities rather than frozen ISA instructions. +- Adopter-driven batch operations for pairwise bind, fixed-role bind, multi-key unbind, and codebook scoring; each + enters the stable surface only after scalar and decision conformance. +- A separately named f64-query/f32-corpus cosine utility for NoSQLite; liblecore computes ordered scores while Rust + retains document-ID ties and result authority. +- A direct definitional backend and a conformant radix-2 FFT backend. +- Exact lowest-index tie-breaking for cleanup. A future top-k utility, if admitted, orders by descending score and + ascending index. +- Build and consumption paths for CMake, Make, Cargo, ctypes, and Emscripten. +- ABI, ISA, profile, library, and capability queries. +- Golden fixtures and differential conformance against the Python reference. +- A separately layered descriptor for new vector/trace interchange. The canonical codec is built and tested in the + standard v1 distribution, but consumers may omit or decline to use it; opaque C structs are never serialized and + existing consumers need not rewrite compatible persisted payloads. +- A shadow-adoption path for existing consumers. + +### 6.2 Follow-on scope + +- A policy-neutral associative-trace helper layered on the base algebra. +- A portable, language-neutral atom generator with its own version and golden vectors. +- A broader exact-index profile or index-management layer for NoSQLite and Zero LM where benchmarks justify + sharing more than the v1 mixed-score utility. +- Optional platform FFT or SIMD backends selected only after conformance and workload measurements. +- A freestanding allocation profile if asix or another real consumer requires it. +- Separate MAP and integer associative-memory profiles after HRR adoption is stable. + +### 6.3 Non-goals + +liblecore will not: + +- port `UnifiedMind`, rendering, physics, scene construction, service endpoints, or the broader Python engine; +- replace NumPy as leCore's default or required runtime; +- make C mandatory for installing or using the `leos-core` Python package; +- contain game rules, action legality, feature schemas, agent policy, schedulers, networking, or database lifecycle; +- define one supposedly universal meaning for every VSA operation; +- preserve existing implementation accidents that contradict the frozen ISA; +- promise bit-identical floating-point values across all compilers and architectures where the ISA class is + tolerance-based; +- promise a speedup without a workload-specific baseline and an amortization measurement; +- load arbitrary generated Python or C source as part of the foundational ABI; or +- require a system-wide shared library or a sibling checkout at build or runtime. + +## 7. Required product surfaces + +### 7.1 C API + +The public header is the primary product. It must be usable from C11 and C++, contain no exposed mutable object +layouts, use prefixed symbols, return status codes, document buffer and aliasing rules, and make thread behavior +explicit. + +The canonical HRR operations use unnormalized binding. A consumer that historically normalizes a bound vector may +compose `bind` followed by `normalize` in its adapter; that policy must not change the meaning of base bind. + +### 7.2 Semantic profiles and compatibility descriptors + +A semantic profile fixes: + +- algebra and immutable profile version; +- scalar type; +- the adopted ISA-subset version; and +- observable decision/reduction order and edge behavior. + +A compatibility descriptor combines that profile with instance/interchange metadata: dimension, atom-generator +version or `caller_supplied`, application normalization/trace contract, payload format, byte order, length, and +checksum. Semantic profiles are checked before operations; the full compatibility contract is checked before data +is accepted. An unknown major version or mismatched algebra fails closed. + +### 7.3 Distribution artifacts + +Every release intended for downstream consumption provides: + +- installed headers and CMake package metadata; +- static and shared library outputs where the platform supports them; +- a single-header/single-source amalgamation for Make, Cargo, and Emscripten; +- `pkg-config` metadata for local and system integration; +- checksums and the applicable MIT notices; and +- a machine-readable build descriptor containing source version, ABI version, compiler, target, and enabled + features. + +Downstream repositories pin an artifact version and digest. Local package discovery may accelerate development but +must not be the only reproducible build path. + +### 7.4 Conformance kit + +The conformance kit is a product surface, not internal test debris. It includes: + +- hand-verifiable convolution identities; +- seeded vector fixtures with declared generator ownership; +- tolerant expected outputs and exact reindex/decision outputs; +- zero-vector, NaN/Inf, aliasing, invalid-input, batch-tail, and non-power-of-two cases; +- cross-language fixture readers; and +- a report format that records backend, compiler, target, maximum error, and decision agreement. + +## 8. Functional requirements + +| ID | Requirement | State | Acceptance summary | +|---|---|---|---| +| PR-F01 | Implement the caller-supplied-vector ISA subset | Implemented · locally verified | Bind, unbind, involution, bundle, cosine, permutation, and cleanup pass the Python definitional reference; `random_vector` is explicitly excluded until a portable generator is versioned. | +| PR-F02 | Support f64 and f32 HRR profiles | Implemented · locally verified preview | Both profiles have typed APIs, documented preview tolerances, and no implicit conversion; the stable f32 bound remains adopter-gated. | +| PR-F03 | Preserve exact decisions | Implemented · locally verified | Cleanup ties select the lowest stable index; replay fixtures agree across conformant backends. Any later top-k utility orders score-descending/index-ascending. | +| PR-F04 | Be zero-safe | Implemented · locally verified | Zero normalization, zero-sum bundle, and zero-norm cosine return the documented result without NaN or division by zero. | +| PR-F05 | Support repeated calls without allocation | Implemented · locally verified | Allocation instrumentation reports zero allocations after context initialization. | +| PR-F06 | Expose batch operations | Implemented · locally verified | Batch results conform to scalar operations and define layouts, strides, tails, and allowed aliasing. | +| PR-F07 | Identify compatibility | Implemented · locally verified | Callers can query and compare ABI, ISA, profile, dtype, dimension, backend, and feature versions. | +| PR-F08 | Fail loudly without rewriting raw semantics | Implemented · locally verified | Null, size, profile, and backend errors return documented statuses; raw mode preserves the ISA's NaN/Inf propagation and cleanup behavior, while an explicit checked boundary rejects non-finite input; unavailable acceleration is never silently substituted after an explicit request. | +| PR-F09 | Cross target boundaries | Implemented · CI pending | The same source builds and passes smoke tests on supported native and Emscripten targets. | +| PR-F10 | Keep policy outside the kernel | Implemented · locally reviewed | No base API refers to pilots, residents, hypotheses, databases, routes, or other domain concepts. | + +## 9. Quality requirements + +### 9.1 Correctness and determinism + +- f64 continuous operations must satisfy the current ISA tolerance unless an ISA revision explicitly changes it. +- f32 tolerances must be measured, documented per operation, and tight enough to preserve the adopter decision + corpus. +- raw arithmetic must preserve the ISA's documented non-finite propagation and cleanup decision; external-data + adapters validate before invoking it. +- involution and permutation must be bit-exact reindexes. +- backend dispatch must be inspectable; every context reports the backend actually in use. +- reproducible builds disable unsafe reassociation and contraction assumptions by default. + +### 9.2 Portability + +Tier 1 targets for version 1 are: + +- macOS arm64 with Clang; +- Linux x86_64 with GCC or Clang; and +- WebAssembly through Emscripten. + +macOS x86_64, Linux arm64, and Windows x86_64 are Tier 2 candidates. They are advertised only after repeatable CI +and consumer/header smoke tests exist. Other targets may build the direct backend without becoming supported by +implication. + +### 9.3 Performance + +Performance is a gate on adoption, not on semantic existence: + +- the direct backend is the correctness floor and supports the full declared dimension policy; +- optimized backends publish setup cost, steady-state cost, memory use, and crossover points; +- before shadow migration, each consumer declares its performance budget; absent a stronger product-specific + reason, a replacement that regresses representative p95 latency by more than 10% retains the existing backend; + and +- a backend that is never faster on its target fixture remains opt-in or is removed. + +### 9.4 Security and robustness + +- size arithmetic is overflow-checked; +- no operation reads or writes outside declared buffers; +- fuzzing covers descriptors and public entry points; +- sanitizers run in CI; +- no API executes supplied source, opens files, starts processes, or uses network access; and +- opaque contexts are never serialized or shared across incompatible library instances. + +## 10. Adoption priorities + +| Priority | Consumer | Product proof | Migration boundary | +|---|---|---|---| +| 1 | leCore Python | Semantic authority and differential harness | Optional ctypes test adapter only; NumPy stays default. | +| 2 | Signal | Real f32 HRR, native/WASM, replay and compatibility contracts | Replace generic algebra behind a shadow adapter; retain Signal encoding, action policy, legality, gossip, and trace versions. | +| 3 | NoSQLite | Reproducible Rust/C packaging and exact vector scoring | Extend its existing C build seam; preserve `holographic-hash-v1` and Rust-owned persistence. | +| 4 | CosyWorld | Advisory ranking behind an authoritative deterministic kernel | Rank only actions already declared legal; retain existing heuristic as fallback and verifier. | +| 5 | Zero Grounded Literary LM | Small C11/WASM exact memory | Preserve its public host ABI while replacing generic internal math. | +| 6 | crlplrimes | Removal of brittle sibling-source coupling | Keep exact verification authoritative and treat holographic ranking as experimental until measured. | + +Holonet's MAP scorer, NSRL's integer memory, Solana programs, Node native addons, and freestanding asix builds are +not first-wave adopters. They inform later profiles without expanding version 1. + +## 11. Success metrics + +### 11.1 Version 1 release gates + +- 100% of the declared caller-supplied-vector ISA subset passes the f64 tolerant/exact conformance suite. +- The f32 profile has published per-operation error bounds and 100% agreement on the committed decision corpus. +- C and C++ consumer smoke tests pass on all advertised native targets. +- Rust and Python test bindings pass without owning kernel semantics. +- Emscripten builds and executes the same golden suite subset. +- ASan and UBSan runs report no findings; public API fuzz targets complete their configured CI budget. +- Allocation tests demonstrate zero allocations after context construction. +- Release artifacts can be consumed without a sibling checkout or machine-global installation. + +### 11.2 Adoption gates + +- Signal completes a shadow replay with no unexplained action-selection, contract, native/WASM, or persistence + mismatch before any default switch. +- At least one Rust consumer builds from a pinned amalgamation or package artifact in clean CI. +- At least two external downstream product repositories integrate the same unmodified pinned liblecore artifact and + exercise a compatibility-gated operation; leCore's ctypes adapter does not count. A legacy backend may remain + temporarily for shadow comparison and rollback, while actual duplicate retirement is a post-release outcome. +- Every adopter declares equivalent profile compatibility metadata in its existing contract, a sidecar, or the + optional liblecore descriptor; compatible persisted payloads need not be rewritten. +- Representative performance reports include the old implementation, direct backend, optimized backend, setup + cost, steady-state cost, and memory use. + +### 11.3 Product outcome + +Within the initial adoption set, generic HRR semantics have one canonical contract, one canonical source tree, and +one conformance kit; multiple conformant direct/FFT backends may coexist. Remaining domain-specific encoders and +policies are visibly adapters rather than accidental forks. + +## 12. Roadmap + +**Current position (2026-08-08):** the branch implements the Phase 0–2 ABI-0 code and packaging surfaces and has +locally exercised the native reference/optimized paths plus the Emscripten feature-on/off smoke. Phase exits are +evidence gates, however: hosted CI, including its WebAssembly lane, has not yet been observed green; no release +artifact has been published; and Phase 3 adopter work has not begun. + +### Phase 0 — Contract freeze + +Produce the ABI decision record, operation table, error model, profile registry, compatibility descriptor, and +golden-fixture schema. Resolve naming, dimension policy, allocation hooks, and the initial f32 tolerance through +tests rather than prose alone. + +**Exit:** a reviewer can implement the API from the documents and fixtures without reading a consumer's source. + +### Phase 1 — Reference library + +Build the f64 direct-convolution backend, exact operations, checked validation, context lifecycle, C tests, and +ctypes differential harness. + +**Exit:** the C implementation passes the frozen caller-supplied-vector subset independently of FFT or SIMD. +Any published reference preview remains explicitly ABI `0` and unstable. + +### Phase 2 — Deployment profiles and packaging + +Add f32, the portable radix-2 FFT backend, batch and mixed-score APIs, CMake/package exports, amalgamation, CI +matrix, sanitizers, Emscripten, and reproducible release metadata. + +**Exit:** a clean C, C++, Rust, Python, and WASM consumer can build a pinned ABI-`0` beta artifact and run the +conformance smoke suite. + +### Phase 3 — Shadow adoption + +Integrate Signal behind an adapter and feature flag; establish replay and performance baselines. Exercise the Rust +packaging path through NoSQLite without changing its persisted encoder contract. + +**Exit:** the Signal and NoSQLite evidence gates pass. A recorded negative result closes the investigation safely +but still blocks stable v1 until its prerequisite or product scope is explicitly changed. + +### Phase 4 — Shared memory applications + +Add the policy-neutral trace extension, CosyWorld advisory ranking, Zero LM migration, and crlplrimes dependency +cleanup where each project demonstrates a real benefit. + +**Exit:** two or more products use the shared library in production-like paths while retaining deterministic host +fallbacks. + +### Phase 5 — Earned extensions + +Consider exact-index, MAP/SIMD, portable atom generation, freestanding, and integer profiles one at a time. Each +requires a named consumer, separate semantics, fixtures, and a measured reason to enter the product. + +## 13. Product backlog epics + +Detailed engineering tasks and dependencies live in [`ENG.md`](ENG.md). Product priority is: + +| Epic | Priority | State | Outcome | +|---|---:|---|---| +| LC-E0 Contract and governance | P0 | ABI-0 implemented · local | One implementable ABI, profile registry, and release/version policy. Stable ABI v1 remains gated. | +| LC-E1 Reference conformance | P0 | Implemented · locally verified | The f64 caller-supplied-vector subset and supporting utilities are correct before optimization. | +| LC-E2 Portable optimized core | P0 | Implemented preview · adopter evidence open | f32/f64 radix-2 and batch profiles preserve local fixture decisions; the stable f32 bound and automatic optimized dispatch remain unearned. | +| LC-E3 Distribution and CI | P0 | Implemented · CI/release pending | Reproducible C/C++/Rust/Python/WASM consumption. | +| LC-E4 Signal shadow migration | P0 | Blocked · external adopter | Highest-value existing HRR consumer validates the product. | +| LC-E5 Rust adoption | P0 | Wrapper implemented · adopter blocked | NoSQLite proves pinned Cargo consumption and exact host-owned ordering. | +| LC-E6 Interchange | P0 | Implemented · locally verified | The optional descriptor component has immutable, portable bytes and fail-closed compatibility checks. | +| LC-E7 Memory and world integrations | P1 | Proposed | Policy-neutral trace helpers, CosyWorld, Zero LM, and dependency cleanup are earned follow-ons. | +| LC-E8 Additional profiles | P2 | Proposed | MAP, exact-index, fixed-point, and freestanding work only when earned. | + +P0 defines the first usable product. P1 expands proven use. P2 is intentionally excluded from the critical path. + +## 14. Risks and mitigations + +| Risk | Consequence | Mitigation | +|---|---|---| +| Existing ports look compatible but are not | Silent recall or action changes | Treat ISA/reference as authority; migrate through adapters and decision replays. | +| Native work expands into a second leCore | Unbounded scope and duplicated policy | Keep version 1 to frozen primitives; require a named profile and consumer for extensions. | +| Near-equal float scores flip decisions | Nondeterministic observable behavior | Fix reduction order in reproducible backends, pin tie rules, and test decisions separately from values. | +| Seeded atoms differ across languages | Stored vectors cannot interoperate | Start with caller-supplied atoms; version and golden-test a portable generator before adoption. | +| A global shared library causes deployment drift | Works locally, fails in CI or production | Pin source/binary artifact and digest per consumer; make local discovery optional. | +| Performance claims outrun evidence | Complexity without user benefit | Require workload-specific regime maps and preserve fallbacks. | +| Public structs freeze implementation layouts | ABI breaks on internal change | Expose opaque contexts and fixed, sized descriptor records only. | +| Licensing contaminates the MIT kernel | Downstream reuse is constrained unexpectedly | Implement from leCore's MIT reference; use AGPL or unlicensed ports as behavioral oracles only unless provenance is resolved. | +| C release cadence couples to unrelated Python changes | Unnecessary downstream churn | Version the C ABI and native package independently from the Python package release train. | +| Optional C becomes mandatory by accident | Breaks leCore's NumPy-only promise | Keep Python packaging and tests functional without a compiler or native artifact. | + +## 15. Adopted implementation decisions + +| Decision | ABI-0 implementation | +|---|---| +| Canonical location | `native/liblecore/` in leCore; a standalone release artifact remains future publication work. | +| Library and symbols | Product `liblecore`, linker basename `lecore`, and public symbols prefixed `lecore_`. | +| Versioning | Native package semver plus ABI, ISA, profile, and format versions; the preview reports ABI `0`. | +| Allocation | Optional allocator callbacks at context creation; no allocations after creation. | +| Threading | Calls on a live context are single-thread-owned; separate contexts may run concurrently. Destruction may run on another thread only after calls quiesce and the configured deallocator is valid there. | +| Dimensions | Dimension is an unsigned 32-bit value; the direct backend supports every representable positive dimension subject to checked size/allocation limits, while radix-2 advertises its power-of-two requirement and dispatch never lies. | +| Binding normalization | Base HRR bind is unnormalized, matching the ISA. Adapters compose normalization explicitly. | +| Atom generation | Caller-supplied in the first release; `PORTABLE_ATOM_V1` is a separate gated deliverable. | +| Persistence | Separate descriptor/format layer; never dump opaque structs. | +| Dynamic generated kernels | Separate plugin ABI and backlog, not part of liblecore v1. | + +## 16. Definition of product readiness + +liblecore is ready to execute release milestone `LC-037` when every other P0 task in [`ENG.md`](ENG.md) is complete, +the release gates in section 11.1 pass on advertised platforms, Signal completes shadow parity with no unexplained +decision changes, and at least one external Rust consumer builds and passes its declared compatibility gate from a +pinned artifact. `LC-037` records the release; it is not one of its own prerequisites. Earlier `0.x` releases may +publish the reference and optimized library before these ecosystem gates are complete. + +It is not necessary for every project under `~/develop` to adopt liblecore. Success is a trustworthy shared +substrate where it fits and an explicit refusal where a project's algebra, runtime, or economics make it the wrong +tool. diff --git a/README.md b/README.md index 9cb2592..7853092 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,10 @@ Like leOS, leCore is **free and open source**, and the work that keeps it free i ## Learning more +- **[`native/liblecore/README.md`](native/liblecore/README.md)** — build and use the ABI-0 `liblecore` C11 preview, + including its API contract, CMake/`pkg-config` consumption, current capabilities, and limitations. +- **[`PRD.md`](PRD.md) and [`ENG.md`](ENG.md)** — the product vision, native ABI strategy, adoption gates, and + dependency-ordered backlog for the `liblecore` portable C kernel. - **[`FEATURE_GUIDE.md`](FEATURE_GUIDE.md)** — a **hands-on how-to** for the most recently added features: composable materials/textures, the describe-a-scene authoring flow (naming, texturing, external files), external-asset relocation and the queryable file map, the message-bus + optional-agent harness, and the opt-in language layer. Every diff --git a/REFERENCE.md b/REFERENCE.md index 84893a0..1b71792 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -1,7 +1,7 @@ # leCore -- Code Reference *Auto-generated by `docgen.py` -- do not edit by hand; edit the module docstrings instead and re-run it.* -*620 modules, 216,511 lines of engine code.* +*620 modules, 216,588 lines of engine code.* > **New here? Read this first.** leCore represents *everything* -- memory, geometry, physics, rendering -- as > points in one very high-dimensional space (hypervectors), and combines them with a tiny algebra: **bind** @@ -486,7 +486,7 @@ | [`holographic_recipeops.py`](#holographic-recipeops) | StructureRecipe validator + edit operators (ARCH-1): the recipe equivalent of the mesh Euler operators. | 264 | | [`holographic_reclock.py`](#holographic-reclock) | holographic_reclock.py -- sample when an AXIS moves, not when time passes ("make the boring property the | 313 | | [`holographic_recurrent.py`](#holographic-recurrent) | A gradient-free RECURRENT layer for the holographic engine: reservoir computing. | 456 | -| [`holographic_reference.py`](#holographic-reference) | Reference implementations + the conformance harness (ISA-2): the teeth of the ISA contract (ISA.md). | 172 | +| [`holographic_reference.py`](#holographic-reference) | Reference implementations + the conformance harness (ISA-2): the teeth of the ISA contract (ISA.md). | 249 | | [`holographic_refine.py`](#holographic-refine) | holographic_refine.py -- the pipeline middle: produce a result, have a CRITIC judge it, adjust, retry. | 122 | | [`holographic_refresh.py`](#holographic-refresh) | holographic_refresh.py -- W4: information-rate rendering. Shade the news, reproject the rest. | 211 | | [`holographic_regimegate.py`](#holographic-regimegate) | holographic_regimegate.py -- RE-ENABLE a shelved method behind a detector (the adaptive-dispatch pattern). | 76 | @@ -18952,8 +18952,14 @@ - `def ref_involution(a)` -- The reversal used to invert bind: inv[0] = a[0], inv[i] = a[D-i]. Exactly self-inverse. - `def ref_unbind(composite, a)` -- Unbind by its definition: bind the composite with the involution of the key. - `def ref_permute(vec, shift)` -- Cyclic shift by its definition (numpy's roll convention): out[i] = vec[(i - shift) mod D]. Exact. -- `def ref_bundle(vectors)` -- Superpose then renormalize; a zero-sum bundle returns the zero vector (the pinned edge). -- `def ref_cosine(a, b)` -- dot / (|a| |b|); zero norm -> 0.0 (the pinned edge). +- `def ref_bundle(vectors)` -- Superpose in row/index order, then normalize; a zero-sum bundle stays zero. +- `def ref_cosine(a, b)` -- Ascending-order dot / (|a| |b|); zero norm -> 0.0. +- `def ref_normalize(a)` -- Ascending-order normalization: zero stays zero; non-finite values propagate. +- `def ref_dot(a, b)` -- Definition of the ordered f64 dot utility: multiply/accumulate from the lowest index upward. +- `def ref_dot_many(query, matrix)` -- Apply ref_dot to codebook rows in stable ascending order. +- `def ref_cosine_many(query, matrix)` -- Apply ref_cosine to codebook rows in stable ascending order. +- `def ref_cleanup(query, codebook)` -- Return the frozen cleanup decision and score, including NumPy's first-NaN argmax behavior. +- `def ref_cosine_many_f64_f32(query, matrix)` -- Definition of the mixed f64-query/f32-corpus utility planned for NoSQLite. - `def value_conformant(x, y, tol)` -- True iff two CONTINUOUS outputs agree within numeric tolerance (the TOL class). - `def exact_conformant(x, y)` -- True iff two outputs are bit-for-bit identical (the EXACT class: a reindex, an involution). - `def decision_conformant(sims_a, sims_b)` -- True iff two similarity vectors yield the SAME cleanup decision under the contract's tie-break. This is diff --git a/benchmarks/bench_liblecore.py b/benchmarks/bench_liblecore.py new file mode 100644 index 0000000..bb26843 --- /dev/null +++ b/benchmarks/bench_liblecore.py @@ -0,0 +1,747 @@ +#!/usr/bin/env python3 +"""Measure liblecore's direct/radix regimes without changing AUTO policy. + +The timed operation is a pre-resolved ctypes call to the public C ABI. Context +setup, NumPy validation, and Python adapter checks stay outside the timed region. +Results are descriptive evidence; this tool never rewrites dispatch thresholds. +""" + +from __future__ import annotations + +import argparse +import ctypes +from dataclasses import dataclass +from functools import partial +import gc +import json +import math +from pathlib import Path +import platform +import statistics +import sys +import time +from typing import Callable, Optional, Tuple +import uuid + +import numpy as np + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPOSITORY_ROOT)) + +from bindings.python.lecore_native import Library # noqa: E402 + + +MINIMUM_OPTIMIZED_ITERATIONS = 4000 +MAXIMUM_CALIBRATED_ITERATIONS = 1_000_000 +CALIBRATION_HEADROOM = 1.25 +CALIBRATION_PILOTS = 3 +MAXIMUM_MEASUREMENT_ATTEMPTS = 3 +MEASUREMENT_LAYER = "public-c-abi" +MEASUREMENT_MODE = "pre-resolved-bind" + + +@dataclass(frozen=True) +class TimingSamples: + iterations: int + elapsed_ns: list[int] + + @property + def median_ns(self) -> float: + return float( + statistics.median(elapsed / self.iterations for elapsed in self.elapsed_ns) + ) + + +def timed_samples( + operation: Callable[[], object], iterations: int, repeats: int +) -> TimingSamples: + elapsed_samples = [] + operation() + was_enabled = gc.isenabled() + gc.disable() + try: + for _ in range(repeats): + started = time.perf_counter_ns() + for _ in range(iterations): + operation() + elapsed_samples.append(time.perf_counter_ns() - started) + finally: + if was_enabled: + gc.enable() + return TimingSamples(iterations=iterations, elapsed_ns=elapsed_samples) + + +def paired_timed_samples( + candidate_operation: Callable[[], object], + baseline_operation: Callable[[], object], + iterations: int, + repeats: int, +) -> Tuple[TimingSamples, TimingSamples]: + """Capture aligned candidate/base samples while alternating first position.""" + + samples: Tuple[list[int], list[int]] = ([], []) + operations = (candidate_operation, baseline_operation) + was_enabled = gc.isenabled() + gc.disable() + try: + for repeat in range(repeats): + order = (0, 1) if repeat % 2 == 0 else (1, 0) + for index in order: + started = time.perf_counter_ns() + for _ in range(iterations): + operations[index]() + samples[index].append(time.perf_counter_ns() - started) + finally: + if was_enabled: + gc.enable() + return ( + TimingSamples(iterations=iterations, elapsed_ns=samples[0]), + TimingSamples(iterations=iterations, elapsed_ns=samples[1]), + ) + + +def raw_bind_operation( + library: Library, + context: object, + profile: str, + left: np.ndarray, + right: np.ndarray, + output: np.ndarray, +) -> Tuple[Callable[[], int], str]: + """Resolve the exported ABI function, context handle, and pointers once.""" + + scalar = ctypes.c_double if profile == "f64" else ctypes.c_float + pointer = ctypes.POINTER(scalar) + name = f"lecore_hrr_bind_{profile}" + function = getattr(library._dll, name) + handle = context._pointer_locked() + operation = partial( + function, + handle, + left.ctypes.data_as(pointer), + right.ctypes.data_as(pointer), + output.ctypes.data_as(pointer), + ) + return operation, name + + +def verify_operation(library: Library, operation: Callable[[], int], name: str) -> None: + library._check(operation(), name) + + +def _measure_elapsed_ns(operation: Callable[[], object], iterations: int) -> int: + started = time.perf_counter_ns() + for _ in range(iterations): + operation() + return max(1, time.perf_counter_ns() - started) + + +def calibrated_iterations( + operations: Tuple[Callable[[], object], ...], + initial_iterations: int, + sample_target_ns: int, + measure_elapsed: Optional[Callable[[Callable[[], object], int], int]] = None, +) -> int: + """Size from the fastest of several equal-count candidate/base pilots.""" + + if not operations: + raise ValueError("at least one operation is required for calibration") + measure = _measure_elapsed_ns if measure_elapsed is None else measure_elapsed + fastest_elapsed = None + was_enabled = gc.isenabled() + gc.disable() + try: + for pilot in range(CALIBRATION_PILOTS): + order = range(len(operations)) + if pilot % 2: + order = reversed(range(len(operations))) + for index in order: + elapsed = max(1, measure(operations[index], initial_iterations)) + if fastest_elapsed is None or elapsed < fastest_elapsed: + fastest_elapsed = elapsed + finally: + if was_enabled: + gc.enable() + if fastest_elapsed is None: + raise RuntimeError("calibration did not produce a timing") + scaled = math.ceil( + initial_iterations + * sample_target_ns + * CALIBRATION_HEADROOM + / fastest_elapsed + ) + iterations = max(initial_iterations, scaled) + return min(iterations, MAXIMUM_CALIBRATED_ITERATIONS) + + +def measure_with_escalation( + measure: Callable[[int], Tuple[TimingSamples, ...]], + initial_iterations: int, + sample_target_ns: int, +) -> Tuple[TimingSamples, ...]: + """Discard short batches and symmetrically increase work a bounded number of times.""" + + iterations = initial_iterations + shortest_elapsed = 0 + for _ in range(MAXIMUM_MEASUREMENT_ATTEMPTS): + timing_groups = measure(iterations) + if not timing_groups or any( + timing.iterations != iterations or not timing.elapsed_ns + for timing in timing_groups + ): + raise RuntimeError("measurement returned invalid timing groups") + shortest_elapsed = min( + elapsed + for timing in timing_groups + for elapsed in timing.elapsed_ns + ) + if shortest_elapsed >= sample_target_ns: + return timing_groups + scaled = math.ceil( + iterations + * sample_target_ns + * CALIBRATION_HEADROOM + / max(1, shortest_elapsed) + ) + next_iterations = min( + max(iterations + 1, scaled), MAXIMUM_CALIBRATED_ITERATIONS + ) + if next_iterations <= iterations: + break + iterations = next_iterations + raise RuntimeError( + "could not reach the sample-duration target after " + f"{MAXIMUM_MEASUREMENT_ATTEMPTS} bounded measurement attempts; " + f"shortest sample was {shortest_elapsed} ns" + ) + + +def descriptive_numpy_bind(): + """Load the application-level reference only for descriptive reports.""" + + from holographic.agents_and_reasoning.holographic_ai import bind + + return bind + + +def setup_ns( + library: Library, dimension: int, profile: str, backend: str, repeats: int +) -> float: + samples = [] + for _ in range(repeats): + started = time.perf_counter_ns() + context = library.context(dimension, profile=profile, backend=backend) + context.close() + samples.append(time.perf_counter_ns() - started) + return float(statistics.median(samples)) + + +def paired_setup_ns( + candidate_library: Library, + baseline_library: Library, + dimension: int, + profile: str, + backend: str, + repeats: int, +) -> tuple[float, float]: + samples: tuple[list[float], list[float]] = ([], []) + libraries = (candidate_library, baseline_library) + for repeat in range(repeats): + order = (0, 1) if repeat % 2 == 0 else (1, 0) + for index in order: + started = time.perf_counter_ns() + context = libraries[index].context(dimension, profile=profile, backend=backend) + context.close() + samples[index].append(time.perf_counter_ns() - started) + return tuple(float(statistics.median(values)) for values in samples) + + +def dimension_inputs(dimension: int, profile: str) -> tuple[np.ndarray, np.ndarray]: + dtype = np.float64 if profile == "f64" else np.float32 + rng = np.random.default_rng(0x1EC0_0000 + dimension + (0 if profile == "f64" else 1)) + left = rng.standard_normal(dimension).astype(dtype) + right = rng.standard_normal(dimension).astype(dtype) + left /= np.linalg.norm(left) + right /= np.linalg.norm(right) + return left, right + + +def result_entry( + dimension: int, + profile: str, + timings: dict[str, TimingSamples], + outputs: dict[str, np.ndarray], + scratch: dict[str, int], + setup_timings: Optional[dict[str, float]], + auto_backend: int, +) -> dict[str, object]: + entry: dict[str, object] = { + "dimension": dimension, + "profile": profile, + "auto_backend": auto_backend, + "direct_iterations": timings["direct"].iterations, + "optimized_iterations": timings["radix2"].iterations, + "direct_elapsed_ns": timings["direct"].elapsed_ns, + "radix2_elapsed_ns": timings["radix2"].elapsed_ns, + "direct_ns": timings["direct"].median_ns, + "radix2_ns": timings["radix2"].median_ns, + "radix2_speedup": timings["direct"].median_ns + / timings["radix2"].median_ns, + "direct_scratch_bytes": scratch["direct"], + "radix2_scratch_bytes": scratch["radix2"], + "radix2_max_abs_vs_direct": float( + np.max( + np.abs( + outputs["radix2"].astype(np.float64) + - outputs["direct"].astype(np.float64) + ) + ) + ), + } + if setup_timings is not None: + entry.update( + { + "direct_setup_ns": setup_timings["direct"], + "radix2_setup_ns": setup_timings["radix2"], + } + ) + return entry + + +def benchmark_dimension( + library: Library, + dimension: int, + profile: str, + repeats: int, + target_work: int, + sample_target_ns: int, + descriptive_metrics: bool, +) -> dict[str, object]: + dtype = np.float64 if profile == "f64" else np.float32 + left, right = dimension_inputs(dimension, profile) + + direct_iterations = max(1, min(2000, target_work // (dimension * dimension))) + optimized_iterations = max(MINIMUM_OPTIMIZED_ITERATIONS, direct_iterations) + outputs: dict[str, np.ndarray] = {} + timings: dict[str, TimingSamples] = {} + scratch: dict[str, int] = {} + + for backend, iterations in ( + ("direct", direct_iterations), + ("radix2", optimized_iterations), + ): + with library.context(dimension, profile=profile, backend=backend) as context: + output = np.empty(dimension, dtype=dtype) + + operation, operation_name = raw_bind_operation( + library, context, profile, left, right, output + ) + verify_operation(library, operation, operation_name) + iterations = calibrated_iterations( + (operation,), iterations, sample_target_ns + ) + timing_groups = measure_with_escalation( + lambda count: (timed_samples(operation, count, repeats),), + iterations, + sample_target_ns, + ) + timings[backend] = timing_groups[0] + verify_operation(library, operation, operation_name) + outputs[backend] = output.copy() + scratch[backend] = context.scratch_bytes + + with library.context(dimension, profile=profile, backend="auto") as context: + auto_backend = context.backend + + setup_timings = None + if descriptive_metrics: + setup_timings = { + backend: setup_ns(library, dimension, profile, backend, repeats) + for backend in ("direct", "radix2") + } + entry = result_entry( + dimension, + profile, + timings, + outputs, + scratch, + setup_timings, + auto_backend, + ) + + if descriptive_metrics and profile == "f64": + numpy_bind = descriptive_numpy_bind() + numpy_output = np.empty(dimension, dtype=np.float64) + + def call_numpy() -> None: + nonlocal numpy_output + numpy_output = numpy_bind(left, right) + + numpy_time = timed_samples( + call_numpy, timings["radix2"].iterations, repeats + ).median_ns + entry.update( + { + "numpy_fft_ns": numpy_time, + "radix2_speedup_vs_numpy": numpy_time + / timings["radix2"].median_ns, + "numpy_max_abs_vs_direct": float( + np.max(np.abs(numpy_output - outputs["direct"])) + ), + } + ) + return entry + + +def benchmark_dimension_pair( + candidate_library: Library, + baseline_library: Library, + dimension: int, + profile: str, + repeats: int, + target_work: int, + sample_target_ns: int, + descriptive_metrics: bool, +) -> tuple[dict[str, object], dict[str, object]]: + """Benchmark candidate and baseline with identical work and alternating order.""" + + dtype = np.float64 if profile == "f64" else np.float32 + left, right = dimension_inputs(dimension, profile) + direct_iterations = max(1, min(2000, target_work // (dimension * dimension))) + optimized_iterations = max(MINIMUM_OPTIMIZED_ITERATIONS, direct_iterations) + libraries = (candidate_library, baseline_library) + timings: tuple[dict[str, TimingSamples], dict[str, TimingSamples]] = ({}, {}) + outputs: tuple[dict[str, np.ndarray], dict[str, np.ndarray]] = ({}, {}) + scratch: tuple[dict[str, int], dict[str, int]] = ({}, {}) + setup_timings: tuple[dict[str, float], dict[str, float]] = ({}, {}) + + for backend, iterations in ( + ("direct", direct_iterations), + ("radix2", optimized_iterations), + ): + with candidate_library.context( + dimension, profile=profile, backend=backend + ) as candidate_context: + with baseline_library.context( + dimension, profile=profile, backend=backend + ) as baseline_context: + candidate_output = np.empty(dimension, dtype=dtype) + baseline_output = np.empty(dimension, dtype=dtype) + + candidate_operation, candidate_name = raw_bind_operation( + candidate_library, + candidate_context, + profile, + left, + right, + candidate_output, + ) + baseline_operation, baseline_name = raw_bind_operation( + baseline_library, + baseline_context, + profile, + left, + right, + baseline_output, + ) + verify_operation(candidate_library, candidate_operation, candidate_name) + verify_operation(baseline_library, baseline_operation, baseline_name) + iterations = calibrated_iterations( + (candidate_operation, baseline_operation), + iterations, + sample_target_ns, + ) + candidate_samples, baseline_samples = measure_with_escalation( + lambda count: paired_timed_samples( + candidate_operation, + baseline_operation, + count, + repeats, + ), + iterations, + sample_target_ns, + ) + verify_operation(candidate_library, candidate_operation, candidate_name) + verify_operation(baseline_library, baseline_operation, baseline_name) + timings[0][backend] = candidate_samples + timings[1][backend] = baseline_samples + outputs[0][backend] = candidate_output.copy() + outputs[1][backend] = baseline_output.copy() + scratch[0][backend] = candidate_context.scratch_bytes + scratch[1][backend] = baseline_context.scratch_bytes + + if descriptive_metrics: + candidate_setup_ns, baseline_setup_ns = paired_setup_ns( + candidate_library, + baseline_library, + dimension, + profile, + backend, + repeats, + ) + setup_timings[0][backend] = candidate_setup_ns + setup_timings[1][backend] = baseline_setup_ns + + auto_backends = [] + for library in libraries: + with library.context(dimension, profile=profile, backend="auto") as context: + auto_backends.append(context.backend) + + entries = tuple( + result_entry( + dimension, + profile, + timings[index], + outputs[index], + scratch[index], + setup_timings[index] if descriptive_metrics else None, + auto_backends[index], + ) + for index in range(2) + ) + + if descriptive_metrics and profile == "f64": + numpy_bind = descriptive_numpy_bind() + numpy_output = np.empty(dimension, dtype=np.float64) + + def call_numpy() -> None: + nonlocal numpy_output + numpy_output = numpy_bind(left, right) + + numpy_time = timed_samples( + call_numpy, timings[0]["radix2"].iterations, repeats + ).median_ns + for index, entry in enumerate(entries): + entry.update( + { + "numpy_fft_ns": numpy_time, + "radix2_speedup_vs_numpy": numpy_time + / timings[index]["radix2"].median_ns, + "numpy_max_abs_vs_direct": float( + np.max(np.abs(numpy_output - outputs[index]["direct"])) + ), + } + ) + return entries + + +def parse_dimensions(value: str) -> list[int]: + dimensions = [int(item) for item in value.split(",") if item.strip()] + if not dimensions or any( + dimension <= 0 or dimension & (dimension - 1) for dimension in dimensions + ): + raise argparse.ArgumentTypeError( + "dimensions must be a comma-separated list of positive powers of two" + ) + return dimensions + + +def build_report( + library: Library, + results: list[dict[str, object]], + repeats: int, + target_work: int, + sample_target_ns: int, + comparison_mode: str, + metrics_scope: str, + pair_id: Optional[str] = None, +) -> dict[str, object]: + benchmark: dict[str, object] = { + "repeats": repeats, + "target_work": target_work, + "sample_target_ns": sample_target_ns, + "calibration_pilots": CALIBRATION_PILOTS, + "maximum_calibrated_iterations": MAXIMUM_CALIBRATED_ITERATIONS, + "maximum_measurement_attempts": MAXIMUM_MEASUREMENT_ATTEMPTS, + "minimum_optimized_iterations": MINIMUM_OPTIMIZED_ITERATIONS, + "comparison_mode": comparison_mode, + "measurement_layer": MEASUREMENT_LAYER, + "measurement_mode": MEASUREMENT_MODE, + "metrics_scope": metrics_scope, + } + if pair_id is not None: + benchmark["pair_id"] = pair_id + return { + "schema_version": 2, + "policy_effect": "none; AUTO remains unchanged", + "benchmark": benchmark, + "library": { + "path": library.path, + "bytes": Path(library.path).stat().st_size, + "version": library.version, + "abi": library.abi_version, + "isa": library.isa_version, + "capabilities": library.capabilities, + }, + "host": { + "platform": platform.platform(), + "machine": platform.machine(), + "python": platform.python_version(), + "numpy": np.__version__, + }, + "results": results, + } + + +def print_results(results: list[dict[str, object]]) -> None: + print("profile dim direct us radix us speedup max |delta|") + for result in results: + print( + f"{result['profile']:>7} {result['dimension']:>5} " + f"{result['direct_ns'] / 1000:>10.2f} " + f"{result['radix2_ns'] / 1000:>9.2f} " + f"{result['radix2_speedup']:>8.2f} " + f"{result['radix2_max_abs_vs_direct']:.3e}" + ) + + +def print_paired_results( + candidate_results: list[dict[str, object]], + baseline_results: list[dict[str, object]], +) -> None: + print("profile dim candidate direct/radix us baseline direct/radix us cand/base") + for candidate, baseline in zip(candidate_results, baseline_results): + direct_ratios = [ + candidate_elapsed / baseline_elapsed + for candidate_elapsed, baseline_elapsed in zip( + candidate["direct_elapsed_ns"], baseline["direct_elapsed_ns"] + ) + ] + radix2_ratios = [ + candidate_elapsed / baseline_elapsed + for candidate_elapsed, baseline_elapsed in zip( + candidate["radix2_elapsed_ns"], baseline["radix2_elapsed_ns"] + ) + ] + print( + f"{candidate['profile']:>7} {candidate['dimension']:>5} " + f"{candidate['direct_ns'] / 1000:>10.2f}/{candidate['radix2_ns'] / 1000:<9.2f} " + f"{baseline['direct_ns'] / 1000:>10.2f}/{baseline['radix2_ns'] / 1000:<9.2f} " + f"{statistics.median(direct_ratios):>5.2f}x/" + f"{statistics.median(radix2_ratios):.2f}x" + ) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--library", required=True, type=Path) + parser.add_argument("--baseline-library", type=Path) + parser.add_argument("--baseline-output", type=Path) + parser.add_argument( + "--dimensions", + type=parse_dimensions, + default=parse_dimensions("8,16,32,64,128,256,512,1024"), + ) + parser.add_argument("--profiles", choices=("f64", "f32", "both"), default="both") + parser.add_argument("--repeats", type=int, default=5) + parser.add_argument("--target-work", type=int, default=4_000_000) + parser.add_argument( + "--sample-target-ms", + type=float, + default=30.0, + help="calibrate each backend to at least this approximate sample duration", + ) + parser.add_argument("--output", type=Path) + parser.add_argument("--json", action="store_true") + parser.add_argument( + "--gate", + action="store_true", + help="omit descriptive setup and NumPy metrics from a CI-gate report", + ) + arguments = parser.parse_args() + if ( + arguments.repeats <= 0 + or arguments.target_work <= 0 + or not math.isfinite(arguments.sample_target_ms) + or arguments.sample_target_ms <= 0 + ): + parser.error("repeats, target-work, and sample-target-ms must be positive") + if (arguments.baseline_library is None) != (arguments.baseline_output is None): + parser.error("baseline-library and baseline-output must be supplied together") + + library = Library(arguments.library) + profiles = ("f64", "f32") if arguments.profiles == "both" else (arguments.profiles,) + baseline_results = None + baseline_report = None + descriptive_metrics = not arguments.gate + metrics_scope = "descriptive" if descriptive_metrics else "gate" + sample_target_ns = int(arguments.sample_target_ms * 1_000_000) + pair_id = None + if arguments.baseline_library is None: + results = [ + benchmark_dimension( + library, + dimension, + profile, + arguments.repeats, + arguments.target_work, + sample_target_ns, + descriptive_metrics, + ) + for profile in profiles + for dimension in arguments.dimensions + ] + comparison_mode = "single" + else: + baseline_library = Library(arguments.baseline_library) + pair_id = uuid.uuid4().hex + results = [] + baseline_results = [] + for profile in profiles: + for dimension in arguments.dimensions: + candidate_result, baseline_result = benchmark_dimension_pair( + library, + baseline_library, + dimension, + profile, + arguments.repeats, + arguments.target_work, + sample_target_ns, + descriptive_metrics, + ) + results.append(candidate_result) + baseline_results.append(baseline_result) + comparison_mode = "paired-interleaved" + baseline_report = build_report( + baseline_library, + baseline_results, + arguments.repeats, + arguments.target_work, + sample_target_ns, + comparison_mode, + metrics_scope, + pair_id, + ) + + report = build_report( + library, + results, + arguments.repeats, + arguments.target_work, + sample_target_ns, + comparison_mode, + metrics_scope, + pair_id, + ) + encoded = json.dumps(report, indent=2, sort_keys=True) + "\n" + if arguments.output is not None: + arguments.output.write_text(encoded, encoding="utf-8") + if baseline_report is not None: + arguments.baseline_output.write_text( + json.dumps(baseline_report, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + if arguments.json: + print(encoded, end="") + elif baseline_results is not None: + print_paired_results(results, baseline_results) + print("Candidate and baseline repeats interleaved; AUTO policy unchanged.") + else: + print_results(results) + print("AUTO policy unchanged; adopter-backed promotion evidence is still required.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/check_liblecore_performance.py b/benchmarks/check_liblecore_performance.py new file mode 100644 index 0000000..93749df --- /dev/null +++ b/benchmarks/check_liblecore_performance.py @@ -0,0 +1,738 @@ +#!/usr/bin/env python3 +"""Enforce liblecore's portable performance-regression CI policy.""" + +from __future__ import annotations + +import argparse +from dataclasses import dataclass +import json +import math +from pathlib import Path +import statistics +import sys +from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple + + +class GateInputError(ValueError): + """The report or policy is malformed and cannot be evaluated safely.""" + + +@dataclass(frozen=True) +class Measurement: + profile: str + dimension: int + direct_ns: float + radix2_ns: float + speedup: float + minimum_speedup: Optional[float] + max_abs_delta: float + delta_limit: float + max_candidate_slowdown: float + baseline_required: bool = False + baseline_direct_ns: Optional[float] = None + baseline_radix2_ns: Optional[float] = None + direct_slowdown: Optional[float] = None + radix2_slowdown: Optional[float] = None + + @property + def passed(self) -> bool: + return ( + (self.minimum_speedup is None or self.speedup >= self.minimum_speedup) + and self.max_abs_delta <= self.delta_limit + and ( + not self.baseline_required + or ( + self.baseline_direct_ns is not None + and self.baseline_radix2_ns is not None + ) + ) + and ( + self.direct_slowdown is None + or self.direct_slowdown <= self.max_candidate_slowdown + ) + and ( + self.radix2_slowdown is None + or self.radix2_slowdown <= self.max_candidate_slowdown + ) + ) + + + +@dataclass(frozen=True) +class ResultTimings: + direct_iterations: int + radix2_iterations: int + direct_elapsed_ns: Sequence[int] + radix2_elapsed_ns: Sequence[int] + direct_ns: float + radix2_ns: float + speedup: float + max_abs_delta: float + + +@dataclass(frozen=True) +class Evaluation: + measurements: Sequence[Measurement] + failures: Sequence[str] + markdown: str + baseline_compared: bool + + @property + def passed(self) -> bool: + return not self.failures + + +def _load_object(path: Path, label: str) -> Dict[str, Any]: + try: + value = json.loads(path.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError) as error: + raise GateInputError(f"could not read {label} {path}: {error}") from error + if not isinstance(value, dict): + raise GateInputError(f"{label} root must be a JSON object") + return value + + +def _mapping(value: Any, label: str) -> Mapping[str, Any]: + if not isinstance(value, dict): + raise GateInputError(f"{label} must be an object") + return value + + +def _integer(value: Any, label: str, *, minimum: int = 0) -> int: + if isinstance(value, bool) or not isinstance(value, int) or value < minimum: + raise GateInputError(f"{label} must be an integer >= {minimum}") + return value + + +def _finite(value: Any, label: str, *, positive: bool = False) -> float: + if isinstance(value, bool) or not isinstance(value, (int, float)): + raise GateInputError(f"{label} must be a finite number") + number = float(value) + if not math.isfinite(number) or (positive and number <= 0.0): + qualifier = "positive and " if positive else "" + raise GateInputError(f"{label} must be {qualifier}finite") + return number + + +def _policy_regimes( + policy: Mapping[str, Any], +) -> Tuple[ + Mapping[str, Any], + float, + List[Tuple[str, int, Optional[float], float]], +]: + if _integer(policy.get("schema_version"), "policy.schema_version") != 2: + raise GateInputError("unsupported performance policy schema_version") + required_library = _mapping(policy.get("required_library"), "policy.required_library") + max_candidate_slowdown = _finite( + policy.get("max_candidate_slowdown"), + "policy.max_candidate_slowdown", + positive=True, + ) + if max_candidate_slowdown < 1.0: + raise GateInputError("policy.max_candidate_slowdown must be >= 1") + raw_dimensions = policy.get("dimensions") + if not isinstance(raw_dimensions, list) or not raw_dimensions: + raise GateInputError("policy.dimensions must be a non-empty array") + dimensions = [] + for index, raw_dimension in enumerate(raw_dimensions): + dimension = _integer(raw_dimension, f"policy.dimensions[{index}]", minimum=1) + if dimension & (dimension - 1): + raise GateInputError(f"policy dimension {dimension} must be a power of two") + if dimension in dimensions: + raise GateInputError(f"duplicate policy dimension {dimension}") + dimensions.append(dimension) + + profiles = _mapping(policy.get("profiles"), "policy.profiles") + regimes: List[Tuple[str, int, Optional[float], float]] = [] + if not profiles: + raise GateInputError("policy.profiles must not be empty") + for profile, raw_profile_policy in profiles.items(): + if not isinstance(profile, str) or not profile: + raise GateInputError("policy profile names must be non-empty strings") + profile_policy = _mapping(raw_profile_policy, f"policy.profiles.{profile}") + delta_limit = _finite( + profile_policy.get("max_abs_delta"), + f"policy.profiles.{profile}.max_abs_delta", + ) + if delta_limit < 0.0: + raise GateInputError(f"policy.profiles.{profile}.max_abs_delta must be >= 0") + raw_minimums = profile_policy.get("minimum_speedup", {}) + minimums = _mapping( + raw_minimums, f"policy.profiles.{profile}.minimum_speedup" + ) + unknown_dimensions = set(minimums) - {str(item) for item in dimensions} + if unknown_dimensions: + unknown = sorted(unknown_dimensions)[0] + raise GateInputError( + f"policy.profiles.{profile}.minimum_speedup has unguarded dimension {unknown}" + ) + for dimension in dimensions: + raw_minimum = minimums.get(str(dimension)) + minimum = None + if raw_minimum is not None: + minimum = _finite( + raw_minimum, + f"policy.profiles.{profile}.minimum_speedup.{dimension}", + positive=True, + ) + regimes.append((profile, dimension, minimum, delta_limit)) + regimes.sort(key=lambda item: (item[0], item[1])) + return required_library, max_candidate_slowdown, regimes + + +def _validate_metadata( + report: Mapping[str, Any], + policy: Mapping[str, Any], + required_library: Mapping[str, Any], + label: str, +) -> int: + expected_report_schema = _integer( + policy.get("report_schema_version"), "policy.report_schema_version" + ) + if ( + _integer(report.get("schema_version"), f"{label}.schema_version") + != expected_report_schema + ): + raise GateInputError( + f"{label}.schema_version must equal policy report schema {expected_report_schema}" + ) + expected_effect = policy.get("required_policy_effect") + if not isinstance(expected_effect, str) or not expected_effect: + raise GateInputError("policy.required_policy_effect must be a non-empty string") + if report.get("policy_effect") != expected_effect: + raise GateInputError( + f"{label}.policy_effect must be {expected_effect!r}; AUTO policy may have changed" + ) + library = _mapping(report.get("library"), f"{label}.library") + for field in ("abi", "isa"): + expected = _integer(required_library.get(field), f"policy.required_library.{field}") + actual = _integer(library.get(field), f"{label}.library.{field}") + if actual != expected: + raise GateInputError(f"{label}.library.{field} is {actual}, expected {expected}") + mask = _integer( + required_library.get("capabilities_mask"), + "policy.required_library.capabilities_mask", + ) + capabilities = _integer(library.get("capabilities"), f"{label}.library.capabilities") + if capabilities & mask != mask: + raise GateInputError( + f"{label}.library.capabilities 0x{capabilities:x} lacks required mask 0x{mask:x}" + ) + + benchmark = _mapping(report.get("benchmark"), f"{label}.benchmark") + for policy_field, report_field in ( + ("required_measurement_layer", "measurement_layer"), + ("required_measurement_mode", "measurement_mode"), + ("required_metrics_scope", "metrics_scope"), + ): + expected = policy.get(policy_field) + if not isinstance(expected, str) or not expected: + raise GateInputError(f"policy.{policy_field} must be a non-empty string") + actual = benchmark.get(report_field) + if actual != expected: + raise GateInputError( + f"{label}.benchmark.{report_field} is {actual!r}, expected {expected!r}" + ) + minimum_repeats = _integer( + policy.get("minimum_repeats"), "policy.minimum_repeats", minimum=1 + ) + repeats = _integer(benchmark.get("repeats"), f"{label}.benchmark.repeats", minimum=1) + if repeats < minimum_repeats: + raise GateInputError( + f"{label}.benchmark.repeats is {repeats}, below required {minimum_repeats}" + ) + minimum_sample_elapsed_ns = _integer( + policy.get("minimum_sample_elapsed_ns"), + "policy.minimum_sample_elapsed_ns", + minimum=1, + ) + sample_target_ns = _integer( + benchmark.get("sample_target_ns"), + f"{label}.benchmark.sample_target_ns", + minimum=1, + ) + if sample_target_ns < minimum_sample_elapsed_ns: + raise GateInputError( + f"{label}.benchmark.sample_target_ns is {sample_target_ns}, below " + f"the policy sample floor {minimum_sample_elapsed_ns}" + ) + minimum_calibration_pilots = _integer( + policy.get("minimum_calibration_pilots"), + "policy.minimum_calibration_pilots", + minimum=1, + ) + calibration_pilots = _integer( + benchmark.get("calibration_pilots"), + f"{label}.benchmark.calibration_pilots", + minimum=1, + ) + if calibration_pilots < minimum_calibration_pilots: + raise GateInputError( + f"{label}.benchmark.calibration_pilots is {calibration_pilots}, below " + f"required {minimum_calibration_pilots}" + ) + return repeats + + +def _index_results( + report: Mapping[str, Any], label: str +) -> Dict[Tuple[str, int], Mapping[str, Any]]: + raw_results = report.get("results") + if not isinstance(raw_results, list): + raise GateInputError(f"{label}.results must be an array") + + indexed: Dict[Tuple[str, int], Mapping[str, Any]] = {} + for index, raw_result in enumerate(raw_results): + result = _mapping(raw_result, f"{label}.results[{index}]") + profile = result.get("profile") + if not isinstance(profile, str) or not profile: + raise GateInputError( + f"{label}.results[{index}].profile must be a non-empty string" + ) + dimension = _integer( + result.get("dimension"), f"{label}.results[{index}].dimension", minimum=1 + ) + key = (profile, dimension) + if key in indexed: + raise GateInputError( + f"duplicate {label} result for {profile} dimension {dimension}" + ) + indexed[key] = result + return indexed + + +def _comparison_mode(report: Mapping[str, Any], label: str) -> str: + benchmark = _mapping(report.get("benchmark"), f"{label}.benchmark") + mode = benchmark.get("comparison_mode") + if not isinstance(mode, str) or not mode: + raise GateInputError(f"{label}.benchmark.comparison_mode must be a non-empty string") + return mode + + +def _pair_id(report: Mapping[str, Any], label: str) -> str: + benchmark = _mapping(report.get("benchmark"), f"{label}.benchmark") + pair_id = benchmark.get("pair_id") + if not isinstance(pair_id, str) or not pair_id: + raise GateInputError(f"{label}.benchmark.pair_id must be a non-empty string") + return pair_id + + +def _elapsed_samples( + value: Any, + label: str, + repeats: int, + minimum_sample_elapsed_ns: int, +) -> List[int]: + if not isinstance(value, list): + raise GateInputError(f"{label} must be an array") + if len(value) != repeats: + raise GateInputError( + f"{label} has {len(value)} samples, expected benchmark.repeats={repeats}" + ) + samples = [] + for index, raw_elapsed in enumerate(value): + elapsed = _integer(raw_elapsed, f"{label}[{index}]", minimum=1) + if elapsed < minimum_sample_elapsed_ns: + raise GateInputError( + f"{label}[{index}] is {elapsed} ns, below required sample duration " + f"{minimum_sample_elapsed_ns} ns" + ) + samples.append(elapsed) + return samples + + +def _read_result( + result: Mapping[str, Any], + label: str, + required_auto_backend: int, + minimum_optimized_iterations: int, + repeats: int, + minimum_sample_elapsed_ns: int, +) -> ResultTimings: + actual_auto_backend = _integer(result.get("auto_backend"), f"{label}.auto_backend") + if actual_auto_backend != required_auto_backend: + raise GateInputError( + f"{label}.auto_backend is {actual_auto_backend}, expected " + f"{required_auto_backend}; AUTO dispatch changed" + ) + direct_iterations = _integer( + result.get("direct_iterations"), f"{label}.direct_iterations", minimum=1 + ) + optimized_iterations = _integer( + result.get("optimized_iterations"), f"{label}.optimized_iterations", minimum=1 + ) + if optimized_iterations < minimum_optimized_iterations: + raise GateInputError( + f"{label}.optimized_iterations is {optimized_iterations}, below required " + f"{minimum_optimized_iterations}" + ) + + direct_elapsed_ns = _elapsed_samples( + result.get("direct_elapsed_ns"), + f"{label}.direct_elapsed_ns", + repeats, + minimum_sample_elapsed_ns, + ) + radix2_elapsed_ns = _elapsed_samples( + result.get("radix2_elapsed_ns"), + f"{label}.radix2_elapsed_ns", + repeats, + minimum_sample_elapsed_ns, + ) + direct_ns = float( + statistics.median(elapsed / direct_iterations for elapsed in direct_elapsed_ns) + ) + radix2_ns = float( + statistics.median(elapsed / optimized_iterations for elapsed in radix2_elapsed_ns) + ) + reported_direct_ns = _finite( + result.get("direct_ns"), f"{label}.direct_ns", positive=True + ) + reported_radix2_ns = _finite( + result.get("radix2_ns"), f"{label}.radix2_ns", positive=True + ) + for field, reported, calculated in ( + ("direct_ns", reported_direct_ns, direct_ns), + ("radix2_ns", reported_radix2_ns, radix2_ns), + ): + if not math.isclose(reported, calculated, rel_tol=1e-9, abs_tol=1e-6): + raise GateInputError( + f"{label}.{field} is inconsistent with its elapsed samples" + ) + reported_speedup = _finite( + result.get("radix2_speedup"), f"{label}.radix2_speedup", positive=True + ) + speedup = direct_ns / radix2_ns + if not math.isclose(reported_speedup, speedup, rel_tol=1e-9, abs_tol=1e-12): + raise GateInputError( + f"{label}.radix2_speedup is inconsistent with direct_ns / radix2_ns" + ) + delta = _finite( + result.get("radix2_max_abs_vs_direct"), + f"{label}.radix2_max_abs_vs_direct", + ) + if delta < 0.0: + raise GateInputError(f"{label}.radix2_max_abs_vs_direct must be >= 0") + return ResultTimings( + direct_iterations=direct_iterations, + radix2_iterations=optimized_iterations, + direct_elapsed_ns=direct_elapsed_ns, + radix2_elapsed_ns=radix2_elapsed_ns, + direct_ns=direct_ns, + radix2_ns=radix2_ns, + speedup=speedup, + max_abs_delta=delta, + ) + + +def evaluate( + report: Mapping[str, Any], + policy: Mapping[str, Any], + baseline: Optional[Mapping[str, Any]] = None, +) -> Evaluation: + required_library, max_candidate_slowdown, regimes = _policy_regimes(policy) + repeats = _validate_metadata(report, policy, required_library, "report") + indexed = _index_results(report, "report") + + baseline_indexed: Optional[Dict[Tuple[str, int], Mapping[str, Any]]] = None + if baseline is not None: + baseline_repeats = _validate_metadata( + baseline, policy, required_library, "baseline" + ) + if baseline_repeats != repeats: + raise GateInputError( + "report and baseline benchmark.repeats must match for paired samples" + ) + baseline_indexed = _index_results(baseline, "baseline") + expected_mode = policy.get("required_baseline_comparison_mode") + if not isinstance(expected_mode, str) or not expected_mode: + raise GateInputError( + "policy.required_baseline_comparison_mode must be a non-empty string" + ) + for compared_report, label in ((report, "report"), (baseline, "baseline")): + actual_mode = _comparison_mode(compared_report, label) + if actual_mode != expected_mode: + raise GateInputError( + f"{label}.benchmark.comparison_mode is {actual_mode!r}, " + f"expected {expected_mode!r}" + ) + report_pair_id = _pair_id(report, "report") + baseline_pair_id = _pair_id(baseline, "baseline") + if report_pair_id != baseline_pair_id: + raise GateInputError( + "report and baseline benchmark.pair_id must identify the same paired run" + ) + else: + expected_mode = policy.get("required_bootstrap_comparison_mode") + if not isinstance(expected_mode, str) or not expected_mode: + raise GateInputError( + "policy.required_bootstrap_comparison_mode must be a non-empty string" + ) + actual_mode = _comparison_mode(report, "report") + if actual_mode != expected_mode: + raise GateInputError( + f"report.benchmark.comparison_mode is {actual_mode!r}, " + f"expected {expected_mode!r}" + ) + + required_auto_backend = _integer( + required_library.get("auto_backend"), "policy.required_library.auto_backend" + ) + minimum_optimized_iterations = _integer( + policy.get("minimum_optimized_iterations"), + "policy.minimum_optimized_iterations", + minimum=1, + ) + minimum_sample_elapsed_ns = _integer( + policy.get("minimum_sample_elapsed_ns"), + "policy.minimum_sample_elapsed_ns", + minimum=1, + ) + + measurements: List[Measurement] = [] + failures: List[str] = [] + for profile, dimension, minimum, delta_limit in regimes: + key = (profile, dimension) + if key not in indexed: + failures.append(f"missing result for {profile} dimension {dimension}") + continue + result = indexed[key] + label = f"report result {profile}/{dimension}" + candidate_timing = _read_result( + result, + label, + required_auto_backend, + minimum_optimized_iterations, + repeats, + minimum_sample_elapsed_ns, + ) + + baseline_direct_ns: Optional[float] = None + baseline_radix2_ns: Optional[float] = None + direct_slowdown: Optional[float] = None + radix2_slowdown: Optional[float] = None + if baseline_indexed is not None: + if key not in baseline_indexed: + failures.append( + f"missing baseline result for {profile} dimension {dimension}" + ) + else: + baseline_timing = _read_result( + baseline_indexed[key], + f"baseline result {profile}/{dimension}", + required_auto_backend, + minimum_optimized_iterations, + repeats, + minimum_sample_elapsed_ns, + ) + if ( + candidate_timing.direct_iterations + != baseline_timing.direct_iterations + or candidate_timing.radix2_iterations + != baseline_timing.radix2_iterations + ): + raise GateInputError( + f"{profile}/{dimension} candidate and baseline iteration " + "counts must match for paired samples" + ) + baseline_direct_ns = baseline_timing.direct_ns + baseline_radix2_ns = baseline_timing.radix2_ns + direct_slowdown = float( + statistics.median( + candidate_elapsed / baseline_elapsed + for candidate_elapsed, baseline_elapsed in zip( + candidate_timing.direct_elapsed_ns, + baseline_timing.direct_elapsed_ns, + ) + ) + ) + radix2_slowdown = float( + statistics.median( + candidate_elapsed / baseline_elapsed + for candidate_elapsed, baseline_elapsed in zip( + candidate_timing.radix2_elapsed_ns, + baseline_timing.radix2_elapsed_ns, + ) + ) + ) + + measurement = Measurement( + profile=profile, + dimension=dimension, + direct_ns=candidate_timing.direct_ns, + radix2_ns=candidate_timing.radix2_ns, + speedup=candidate_timing.speedup, + minimum_speedup=minimum, + max_abs_delta=candidate_timing.max_abs_delta, + delta_limit=delta_limit, + max_candidate_slowdown=max_candidate_slowdown, + baseline_required=baseline is not None, + baseline_direct_ns=baseline_direct_ns, + baseline_radix2_ns=baseline_radix2_ns, + direct_slowdown=direct_slowdown, + radix2_slowdown=radix2_slowdown, + ) + measurements.append(measurement) + if minimum is not None and candidate_timing.speedup < minimum: + failures.append( + f"{profile}/{dimension} radix-2 speedup " + f"{candidate_timing.speedup:.2f}x is below {minimum:.2f}x" + ) + if candidate_timing.max_abs_delta > delta_limit: + failures.append( + f"{profile}/{dimension} max delta " + f"{candidate_timing.max_abs_delta:.3e} exceeds {delta_limit:.3e}" + ) + if ( + measurement.direct_slowdown is not None + and measurement.direct_slowdown > max_candidate_slowdown + ): + failures.append( + f"{profile}/{dimension} direct candidate/base slowdown " + f"{measurement.direct_slowdown:.2f}x exceeds {max_candidate_slowdown:.2f}x" + ) + if ( + measurement.radix2_slowdown is not None + and measurement.radix2_slowdown > max_candidate_slowdown + ): + failures.append( + f"{profile}/{dimension} radix-2 candidate/base slowdown " + f"{measurement.radix2_slowdown:.2f}x exceeds {max_candidate_slowdown:.2f}x" + ) + + markdown = render_markdown( + report, + measurements, + failures, + baseline_compared=baseline is not None, + max_candidate_slowdown=max_candidate_slowdown, + ) + return Evaluation( + measurements=measurements, + failures=failures, + markdown=markdown, + baseline_compared=baseline is not None, + ) + + +def render_markdown( + report: Mapping[str, Any], + measurements: Iterable[Measurement], + failures: Sequence[str], + *, + baseline_compared: bool, + max_candidate_slowdown: float, +) -> str: + host = report.get("host") if isinstance(report.get("host"), dict) else {} + library = report.get("library") if isinstance(report.get("library"), dict) else {} + status = "PASS" if not failures else "FAIL" + comparison = ( + f"Candidate and base samples are paired and interleaved on this runner; " + f"the median paired slowdown may be at most {max_candidate_slowdown:.2f}x " + "per backend." + if baseline_compared + else "Bootstrap mode: the base commit has no benchmarkable liblecore, so " + "only same-build invariants apply." + ) + lines = [ + "## liblecore performance regression", + "", + f"**Gate: {status}.** {comparison}", + "", + f"Host: `{host.get('platform', 'unknown')}` / `{host.get('machine', 'unknown')}`; " + f"Python `{host.get('python', 'unknown')}`; NumPy `{host.get('numpy', 'unknown')}`; " + f"liblecore `{library.get('version', 'unknown')}` (ABI `{library.get('abi', 'unknown')}`, " + f"ISA `{library.get('isa', 'unknown')}`).", + "", + "Layer: public C ABI through a fixed pre-resolved ctypes transition, with the " + "bind function, context, and array pointers resolved before timing. " + "Every latency cell contains raw per-repeat elapsed times that satisfy the " + "policy duration floor.", + "", + "| Profile | Dimension | Direct (us) | Radix-2 (us) | Speedup | Floor | Median paired direct/base | Median paired radix/base | Max delta | Limit | Result |", + "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | :---: |", + ] + for item in measurements: + direct_base = "--" if item.direct_slowdown is None else f"{item.direct_slowdown:.2f}x" + radix_base = "--" if item.radix2_slowdown is None else f"{item.radix2_slowdown:.2f}x" + speedup_floor = ( + "--" + if item.minimum_speedup is None + else f"{item.minimum_speedup:.2f}x" + ) + lines.append( + f"| {item.profile} | {item.dimension} | {item.direct_ns / 1000.0:.2f} | " + f"{item.radix2_ns / 1000.0:.2f} | {item.speedup:.2f}x | " + f"{speedup_floor} | {direct_base} | {radix_base} | " + f"{item.max_abs_delta:.3e} | " + f"{item.delta_limit:.3e} | {'PASS' if item.passed else 'FAIL'} |" + ) + if failures: + lines.extend(["", "### Failures", ""]) + lines.extend(f"- {failure}" for failure in failures) + lines.extend( + [ + "", + "`LECORE_BACKEND_AUTO` remains direct; this gate does not change dispatch policy.", + "", + ] + ) + return "\n".join(lines) + + +def _input_failure_markdown(error: Exception) -> str: + return "\n".join( + [ + "## liblecore performance regression", + "", + "**Gate: FAIL.** The benchmark report or policy was not valid.", + "", + f"- {error}", + "", + ] + ) + + +def _write_summary(path: Path, markdown: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("a", encoding="utf-8") as destination: + destination.write(markdown) + + +def main(argv: Optional[Sequence[str]] = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--report", required=True, type=Path) + parser.add_argument("--baseline", type=Path) + parser.add_argument("--policy", required=True, type=Path) + parser.add_argument("--summary", type=Path) + arguments = parser.parse_args(argv) + + try: + report = _load_object(arguments.report, "report") + baseline = ( + _load_object(arguments.baseline, "baseline") + if arguments.baseline is not None + else None + ) + policy = _load_object(arguments.policy, "policy") + evaluation = evaluate(report, policy, baseline) + markdown = evaluation.markdown + failures = list(evaluation.failures) + except GateInputError as error: + markdown = _input_failure_markdown(error) + failures = [str(error)] + + print(markdown, end="") + if arguments.summary is not None: + _write_summary(arguments.summary, markdown) + if failures: + for failure in failures: + print(f"performance gate: {failure}", file=sys.stderr) + return 1 + print("performance gate: all guarded regimes passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/benchmarks/liblecore_ci_policy.json b/benchmarks/liblecore_ci_policy.json new file mode 100644 index 0000000..649fcac --- /dev/null +++ b/benchmarks/liblecore_ci_policy.json @@ -0,0 +1,30 @@ +{ + "schema_version": 2, + "report_schema_version": 2, + "required_policy_effect": "none; AUTO remains unchanged", + "required_measurement_layer": "public-c-abi", + "required_measurement_mode": "pre-resolved-bind", + "required_metrics_scope": "gate", + "required_baseline_comparison_mode": "paired-interleaved", + "required_bootstrap_comparison_mode": "single", + "minimum_repeats": 10, + "minimum_calibration_pilots": 3, + "minimum_optimized_iterations": 4000, + "minimum_sample_elapsed_ns": 10000000, + "max_candidate_slowdown": 1.35, + "dimensions": [256, 512, 1024], + "required_library": { + "abi": 0, + "isa": 1, + "capabilities_mask": 15, + "auto_backend": 1 + }, + "profiles": { + "f64": { + "max_abs_delta": 1e-12 + }, + "f32": { + "max_abs_delta": 1e-4 + } + } +} diff --git a/bindings/python/lecore_native.py b/bindings/python/lecore_native.py new file mode 100644 index 0000000..7901e62 --- /dev/null +++ b/bindings/python/lecore_native.py @@ -0,0 +1,1020 @@ +"""Strict NumPy/ctypes adapter for the liblecore ABI-0 conformance suite. + +This is deliberately not a runtime dispatcher. A caller must name one shared +library artifact, and every array crossing the ABI must already have the exact +native dtype, shape, and C-contiguous layout required by its semantic profile. +The adapter never changes precision and never falls back to the Python kernel. +""" + +from __future__ import annotations + +import ctypes +import os +from pathlib import Path +import threading +from typing import Final, Optional, Union +import weakref + +import numpy as np + + +LECORE_ABI_VERSION: Final = 0 +LECORE_ISA_VERSION: Final = 1 + +LECORE_OK: Final = 0 +LECORE_EINVAL: Final = 1 +LECORE_EDIM: Final = 2 +LECORE_EPROFILE: Final = 3 +LECORE_EBACKEND: Final = 4 +LECORE_EOVERFLOW: Final = 5 +LECORE_ENOMEM: Final = 6 +LECORE_EUNSUPPORTED: Final = 7 +LECORE_ENONFINITE: Final = 8 +LECORE_EFORMAT: Final = 9 +LECORE_ECHECKSUM: Final = 10 + +LECORE_PROFILE_HRR_F64_V1: Final = 0x00010001 +LECORE_PROFILE_HRR_F32_V1: Final = 0x00010002 +LECORE_BACKEND_AUTO: Final = 0 +LECORE_BACKEND_DIRECT: Final = 1 +LECORE_BACKEND_RADIX2: Final = 2 +LECORE_VALIDATION_SHAPE: Final = 0 +LECORE_VALIDATION_FINITE: Final = 1 +LECORE_SCALAR_F64: Final = 1 +LECORE_SCALAR_F32: Final = 2 + +LECORE_CAP_HRR_F64: Final = 1 << 0 +LECORE_CAP_HRR_F32: Final = 1 << 1 +LECORE_CAP_DIRECT: Final = 1 << 2 +LECORE_CAP_RADIX2: Final = 1 << 3 +LECORE_CAP_BATCH: Final = 1 << 4 +LECORE_CAP_MIXED_F64_F32: Final = 1 << 5 +LECORE_CAP_FINITE_VALIDATION: Final = 1 << 6 + + +_PROFILE_BY_NAME = { + "f64": LECORE_PROFILE_HRR_F64_V1, + "f32": LECORE_PROFILE_HRR_F32_V1, +} +_DTYPE_BY_PROFILE = { + LECORE_PROFILE_HRR_F64_V1: np.dtype(np.float64), + LECORE_PROFILE_HRR_F32_V1: np.dtype(np.float32), +} +_SCALAR_BY_PROFILE = { + LECORE_PROFILE_HRR_F64_V1: LECORE_SCALAR_F64, + LECORE_PROFILE_HRR_F32_V1: LECORE_SCALAR_F32, +} +_CAPABILITY_BY_PROFILE = { + LECORE_PROFILE_HRR_F64_V1: LECORE_CAP_HRR_F64, + LECORE_PROFILE_HRR_F32_V1: LECORE_CAP_HRR_F32, +} +_BACKEND_BY_NAME = { + "auto": LECORE_BACKEND_AUTO, + "direct": LECORE_BACKEND_DIRECT, + "radix2": LECORE_BACKEND_RADIX2, +} +_CAPABILITY_BY_BACKEND = { + LECORE_BACKEND_DIRECT: LECORE_CAP_DIRECT, + LECORE_BACKEND_RADIX2: LECORE_CAP_RADIX2, +} + + +class _AllocatorV0(ctypes.Structure): + _fields_ = [ + ("struct_size", ctypes.c_uint32), + ("user", ctypes.c_void_p), + ("allocate", ctypes.c_void_p), + ("deallocate", ctypes.c_void_p), + ] + + +class _ConfigV0(ctypes.Structure): + _fields_ = [ + ("struct_size", ctypes.c_uint32), + ("abi_version", ctypes.c_uint32), + ("profile", ctypes.c_uint32), + ("backend", ctypes.c_uint32), + ("validation", ctypes.c_uint32), + ("flags", ctypes.c_uint32), + ("dimension", ctypes.c_uint32), + ("allocator", _AllocatorV0), + ("reserved", ctypes.c_uint64 * 4), + ] + + +class LeCoreError(RuntimeError): + """Base exception for a nonzero native status.""" + + def __init__(self, status: int, operation: str, detail: str): + self.status = int(status) + self.operation = operation + self.detail = detail + super().__init__(f"{operation}: {detail} (status {status})") + + +class LeCoreInvalidArgumentError(LeCoreError): + pass + + +class LeCoreDimensionError(LeCoreError): + pass + + +class LeCoreProfileError(LeCoreError): + pass + + +class LeCoreBackendError(LeCoreError): + pass + + +class LeCoreOverflowError(LeCoreError): + pass + + +class LeCoreMemoryError(LeCoreError): + pass + + +class LeCoreUnsupportedError(LeCoreError): + pass + + +class LeCoreNonFiniteError(LeCoreError): + pass + + +class LeCoreFormatError(LeCoreError): + pass + + +class LeCoreChecksumError(LeCoreError): + pass + + +class LeCoreCompatibilityError(RuntimeError): + """The loaded artifact does not provide the ABI/profile requested.""" + + +class LeCoreClosedError(RuntimeError): + """An operation was attempted after context destruction.""" + + +class LeCoreThreadError(RuntimeError): + """A context was used from a thread other than its creator.""" + + +_ERROR_BY_STATUS: dict[int, type[LeCoreError]] = { + LECORE_EINVAL: LeCoreInvalidArgumentError, + LECORE_EDIM: LeCoreDimensionError, + LECORE_EPROFILE: LeCoreProfileError, + LECORE_EBACKEND: LeCoreBackendError, + LECORE_EOVERFLOW: LeCoreOverflowError, + LECORE_ENOMEM: LeCoreMemoryError, + LECORE_EUNSUPPORTED: LeCoreUnsupportedError, + LECORE_ENONFINITE: LeCoreNonFiniteError, + LECORE_EFORMAT: LeCoreFormatError, + LECORE_ECHECKSUM: LeCoreChecksumError, +} + + +class _NativeContextOwner: + """Thread-neutral, exactly-once owner used by explicit and GC cleanup.""" + + __slots__ = ("lock", "pointer", "destroy") + + def __init__(self, lock: threading.RLock, destroy): + self.lock = lock + self.pointer = ctypes.c_void_p() + self.destroy = destroy + + def close(self) -> None: + with self.lock: + pointer = self.pointer + if not pointer: + return + self.pointer = type(pointer)() + self.destroy(pointer) + + +def _finalize_native_context(owner: _NativeContextOwner) -> None: + owner.close() + + +def _exact_range_alias(a: np.ndarray, b: np.ndarray) -> bool: + return a.ctypes.data == b.ctypes.data and a.nbytes == b.nbytes + + +def _reject_partial_overlap( + output: np.ndarray, + *inputs: np.ndarray, + allow_exact: bool, +) -> None: + for input_array in inputs: + if not np.shares_memory(output, input_array): + continue + if allow_exact and _exact_range_alias(output, input_array): + continue + raise ValueError("output partially overlaps an input array") + + +class Library: + """One explicitly selected liblecore shared-library artifact.""" + + def __init__( + self, + path: Union[str, os.PathLike[str]], + *, + expected_abi: int = LECORE_ABI_VERSION, + expected_isa: int = LECORE_ISA_VERSION, + ): + if ( + isinstance(expected_abi, bool) + or not isinstance(expected_abi, int) + or expected_abi != LECORE_ABI_VERSION + ): + raise LeCoreCompatibilityError( + f"this adapter binds only ABI {LECORE_ABI_VERSION}; " + f"expected_abi={expected_abi!r} is unsupported" + ) + if ( + isinstance(expected_isa, bool) + or not isinstance(expected_isa, int) + or expected_isa != LECORE_ISA_VERSION + ): + raise LeCoreCompatibilityError( + f"this adapter binds only ISA {LECORE_ISA_VERSION}; " + f"expected_isa={expected_isa!r} is unsupported" + ) + if path is None: + raise TypeError("path is required; implicit library discovery is disabled") + supplied = Path(os.fspath(path)).expanduser() + if not supplied.is_file(): + raise FileNotFoundError(f"liblecore artifact does not exist: {supplied}") + self.path = str(supplied.resolve()) + try: + self._dll = ctypes.CDLL(self.path) + except OSError as error: + raise OSError(f"could not load liblecore artifact {self.path}: {error}") from error + self._declare_identity() + if self.abi_version != LECORE_ABI_VERSION: + raise LeCoreCompatibilityError( + f"expected ABI {LECORE_ABI_VERSION}, artifact reports {self.abi_version}" + ) + if self.isa_version != LECORE_ISA_VERSION: + raise LeCoreCompatibilityError( + f"expected ISA {LECORE_ISA_VERSION}, artifact reports {self.isa_version}" + ) + self._declare_abi0() + + def _declare_identity(self) -> None: + dll = self._dll + dll.lecore_abi_version.argtypes = [] + dll.lecore_abi_version.restype = ctypes.c_uint32 + dll.lecore_isa_version.argtypes = [] + dll.lecore_isa_version.restype = ctypes.c_uint32 + + def _declare_abi0(self) -> None: + dll = self._dll + dll.lecore_version_string.argtypes = [] + dll.lecore_version_string.restype = ctypes.c_char_p + dll.lecore_capabilities.argtypes = [] + dll.lecore_capabilities.restype = ctypes.c_uint64 + dll.lecore_status_string.argtypes = [ctypes.c_uint32] + dll.lecore_status_string.restype = ctypes.c_char_p + dll.lecore_config_init_v0.argtypes = [ctypes.POINTER(_ConfigV0)] + dll.lecore_config_init_v0.restype = None + dll.lecore_context_create.argtypes = [ + ctypes.POINTER(_ConfigV0), + ctypes.POINTER(ctypes.c_void_p), + ] + dll.lecore_context_create.restype = ctypes.c_uint32 + dll.lecore_context_destroy.argtypes = [ctypes.c_void_p] + dll.lecore_context_destroy.restype = None + for query in ( + "dimension", + "profile", + "backend", + "validation", + "scalar_type", + ): + function = getattr(dll, f"lecore_context_{query}") + function.argtypes = [ctypes.c_void_p] + function.restype = ctypes.c_uint32 + dll.lecore_context_scratch_bytes.argtypes = [ctypes.c_void_p] + dll.lecore_context_scratch_bytes.restype = ctypes.c_size_t + + for suffix, scalar in (("f64", ctypes.c_double), ("f32", ctypes.c_float)): + pointer = ctypes.POINTER(scalar) + validate = getattr(dll, f"lecore_validate_{suffix}") + validate.argtypes = [pointer, ctypes.c_size_t] + validate.restype = ctypes.c_uint32 + for name in ("normalize", "involution"): + function = getattr(dll, f"lecore_{name}_{suffix}") + function.argtypes = [ctypes.c_void_p, pointer, pointer] + function.restype = ctypes.c_uint32 + for name in ("dot", "cosine"): + function = getattr(dll, f"lecore_{name}_{suffix}") + function.argtypes = [ctypes.c_void_p, pointer, pointer, pointer] + function.restype = ctypes.c_uint32 + for name in ("dot_many", "cosine_many"): + function = getattr(dll, f"lecore_{name}_{suffix}") + function.argtypes = [ + ctypes.c_void_p, + pointer, + pointer, + ctypes.c_size_t, + ctypes.c_size_t, + pointer, + ] + function.restype = ctypes.c_uint32 + for name in ("hrr_bind", "hrr_unbind"): + function = getattr(dll, f"lecore_{name}_{suffix}") + function.argtypes = [ctypes.c_void_p, pointer, pointer, pointer] + function.restype = ctypes.c_uint32 + permute = getattr(dll, f"lecore_permute_{suffix}") + permute.argtypes = [ctypes.c_void_p, pointer, ctypes.c_int64, pointer] + permute.restype = ctypes.c_uint32 + bundle = getattr(dll, f"lecore_bundle_{suffix}") + bundle.argtypes = [ + ctypes.c_void_p, + pointer, + ctypes.c_size_t, + ctypes.c_size_t, + pointer, + ] + bundle.restype = ctypes.c_uint32 + cleanup = getattr(dll, f"lecore_cleanup_{suffix}") + cleanup.argtypes = [ + ctypes.c_void_p, + pointer, + pointer, + ctypes.c_size_t, + ctypes.c_size_t, + ctypes.POINTER(ctypes.c_size_t), + pointer, + ] + cleanup.restype = ctypes.c_uint32 + batch = getattr(dll, f"lecore_hrr_bind_batch_{suffix}") + batch.argtypes = [ + ctypes.c_void_p, + pointer, + ctypes.c_size_t, + pointer, + ctypes.c_size_t, + ctypes.c_size_t, + pointer, + ctypes.c_size_t, + ] + batch.restype = ctypes.c_uint32 + for name in ("hrr_bind_fixed", "hrr_unbind_all"): + function = getattr(dll, f"lecore_{name}_{suffix}") + function.argtypes = [ + ctypes.c_void_p, + pointer, + pointer, + ctypes.c_size_t, + ctypes.c_size_t, + pointer, + ctypes.c_size_t, + ] + function.restype = ctypes.c_uint32 + + dll.lecore_cosine_many_f64_f32.argtypes = [ + ctypes.c_void_p, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_float), + ctypes.c_size_t, + ctypes.c_size_t, + ctypes.POINTER(ctypes.c_double), + ] + dll.lecore_cosine_many_f64_f32.restype = ctypes.c_uint32 + + @property + def abi_version(self) -> int: + return int(self._dll.lecore_abi_version()) + + @property + def isa_version(self) -> int: + return int(self._dll.lecore_isa_version()) + + @property + def version(self) -> str: + value = self._dll.lecore_version_string() + return value.decode("ascii") if value else "" + + @property + def capabilities(self) -> int: + return int(self._dll.lecore_capabilities()) + + def _check(self, status: int, operation: str) -> None: + status = int(status) + if status == LECORE_OK: + return + raw_detail = self._dll.lecore_status_string(status) + detail = raw_detail.decode("utf-8", "replace") if raw_detail else "unknown status" + error_type = _ERROR_BY_STATUS.get(status, LeCoreError) + raise error_type(status, operation, detail) + + def context( + self, + dimension: int, + *, + profile: str = "f64", + backend: str = "auto", + finite: bool = False, + ) -> "Context": + return Context( + self, + dimension, + profile=profile, + backend=backend, + finite=finite, + ) + + def validate(self, values: np.ndarray) -> None: + """Run the standalone finite validator without changing the array.""" + if not isinstance(values, np.ndarray): + raise TypeError("values must be a NumPy ndarray") + if values.dtype == np.dtype(np.float64): + suffix, scalar = "f64", ctypes.c_double + elif values.dtype == np.dtype(np.float32): + suffix, scalar = "f32", ctypes.c_float + else: + raise TypeError("values dtype must be exactly native float64 or float32") + if not values.flags.c_contiguous or not values.flags.aligned: + raise ValueError("values must be aligned and C-contiguous") + pointer = values.ctypes.data_as(ctypes.POINTER(scalar)) + self._check( + getattr(self._dll, f"lecore_validate_{suffix}")(pointer, values.size), + f"lecore_validate_{suffix}", + ) + + +class Context: + """Dimension/profile-specific native context with deterministic ownership.""" + + def __init__( + self, + library: Library, + dimension: int, + *, + profile: str, + backend: str, + finite: bool, + ): + if isinstance(dimension, bool) or not isinstance(dimension, (int, np.integer)): + raise TypeError("dimension must be an integer") + if not 0 < int(dimension) <= 0xFFFFFFFF: + raise ValueError("dimension must be in 1..UINT32_MAX") + if profile not in _PROFILE_BY_NAME: + raise ValueError("profile must be 'f64' or 'f32'") + if backend not in _BACKEND_BY_NAME: + raise ValueError("backend must be 'auto', 'direct', or 'radix2'") + + self.library = library + self.dimension = int(dimension) + self.profile_name = profile + self.profile = _PROFILE_BY_NAME[profile] + self.dtype = _DTYPE_BY_PROFILE[self.profile] + self._ctype = ctypes.c_double if profile == "f64" else ctypes.c_float + self._suffix = profile + self.requested_backend = _BACKEND_BY_NAME[backend] + self.validation = LECORE_VALIDATION_FINITE if finite else LECORE_VALIDATION_SHAPE + self._owner_thread = threading.current_thread() + self._owner_thread_id = threading.get_ident() + self._lock = threading.RLock() + self._native_owner = _NativeContextOwner( + self._lock, + library._dll.lecore_context_destroy, + ) + + capability = _CAPABILITY_BY_PROFILE[self.profile] + if not library.capabilities & capability: + raise LeCoreCompatibilityError( + f"artifact does not advertise the requested {profile} profile" + ) + backend_capability = _CAPABILITY_BY_BACKEND.get(self.requested_backend) + if backend_capability is not None and not library.capabilities & backend_capability: + raise LeCoreCompatibilityError( + f"artifact does not advertise the requested {backend} backend" + ) + + config = _ConfigV0() + library._dll.lecore_config_init_v0(ctypes.byref(config)) + config.dimension = self.dimension + config.profile = self.profile + config.backend = self.requested_backend + config.validation = self.validation + status = library._dll.lecore_context_create( + ctypes.byref(config), ctypes.byref(self._native_owner.pointer) + ) + try: + library._check(status, "lecore_context_create") + except Exception: + self._native_owner.close() + raise + try: + self._finalizer = weakref.finalize( + self, + _finalize_native_context, + self._native_owner, + ) + except Exception: + self._native_owner.close() + raise + try: + self._verify_context() + except Exception: + self.close() + raise + + def _verify_context(self) -> None: + dll = self.library._dll + self._check_owner() + with self._lock: + handle = self._pointer_locked() + observed = { + "dimension": int(dll.lecore_context_dimension(handle)), + "profile": int(dll.lecore_context_profile(handle)), + "validation": int(dll.lecore_context_validation(handle)), + "scalar": int(dll.lecore_context_scalar_type(handle)), + } + backend = int(dll.lecore_context_backend(handle)) + expected = { + "dimension": self.dimension, + "profile": self.profile, + "validation": self.validation, + "scalar": _SCALAR_BY_PROFILE[self.profile], + } + if observed != expected: + raise LeCoreCompatibilityError( + f"created context metadata mismatch: expected {expected}, observed {observed}" + ) + self.backend = backend + if self.requested_backend != LECORE_BACKEND_AUTO and self.backend != self.requested_backend: + raise LeCoreCompatibilityError( + f"requested backend {self.requested_backend}, context reports {self.backend}" + ) + + @property + def scratch_bytes(self) -> int: + self._check_owner() + with self._lock: + return int( + self.library._dll.lecore_context_scratch_bytes( + self._pointer_locked() + ) + ) + + @property + def closed(self) -> bool: + self._check_owner() + with self._lock: + return not bool(self._native_owner.pointer) + + def _check_owner(self) -> None: + current_thread_id = threading.get_ident() + if ( + threading.current_thread() is not self._owner_thread + or current_thread_id != self._owner_thread_id + ): + raise LeCoreThreadError( + "liblecore context belongs to thread " + f"{self._owner_thread_id}, not {current_thread_id}" + ) + + def _pointer_locked(self) -> ctypes.c_void_p: + if not self._native_owner.pointer: + raise LeCoreClosedError("liblecore context is closed") + return self._native_owner.pointer + + def _invoke(self, operation: str, function, *arguments) -> None: + self._check_owner() + with self._lock: + status = function(self._pointer_locked(), *arguments) + self.library._check(status, operation) + + def close(self) -> None: + self._check_owner() + self._finalizer() + + def __enter__(self) -> "Context": + self._check_owner() + with self._lock: + self._pointer_locked() + return self + + def __exit__(self, exc_type, exc, traceback) -> None: + self.close() + + def __copy__(self): + raise TypeError("liblecore Context owns a unique native handle and cannot be copied") + + def __deepcopy__(self, memo): + del memo + raise TypeError("liblecore Context owns a unique native handle and cannot be copied") + + def _pointer_to(self, array: np.ndarray): + return array.ctypes.data_as(ctypes.POINTER(self._ctype)) + + def _array( + self, + values: np.ndarray, + name: str, + *, + ndim: int, + shape: tuple[Optional[int], ...], + writable: bool = False, + ) -> np.ndarray: + if not isinstance(values, np.ndarray): + raise TypeError(f"{name} must be a NumPy ndarray") + if values.dtype != self.dtype: + raise TypeError( + f"{name} dtype must be exactly native {self.dtype.name}, got {values.dtype}" + ) + if values.ndim != ndim: + raise ValueError(f"{name} must have {ndim} dimensions, got {values.ndim}") + for axis, expected in enumerate(shape): + if expected is not None and values.shape[axis] != expected: + raise ValueError( + f"{name} axis {axis} must have length {expected}, got {values.shape[axis]}" + ) + if not values.flags.c_contiguous or not values.flags.aligned: + raise ValueError(f"{name} must be aligned and C-contiguous") + if writable and not values.flags.writeable: + raise ValueError(f"{name} must be writable") + return values + + def _vector(self, values: np.ndarray, name: str = "values") -> np.ndarray: + return self._array(values, name, ndim=1, shape=(self.dimension,)) + + def _rows(self, values: np.ndarray, name: str = "rows") -> np.ndarray: + rows = self._array(values, name, ndim=2, shape=(None, self.dimension)) + if rows.shape[0] == 0: + raise ValueError(f"{name} must contain at least one row") + return rows + + def _output( + self, + output: Optional[np.ndarray], + shape: tuple[int, ...], + name: str = "out", + ) -> np.ndarray: + if output is None: + return np.empty(shape, dtype=self.dtype) + return self._array(output, name, ndim=len(shape), shape=shape, writable=True) + + def _unary( + self, + operation: str, + values: np.ndarray, + *, + out: Optional[np.ndarray], + ) -> np.ndarray: + self._check_owner() + values = self._vector(values) + output = self._output(out, (self.dimension,)) + _reject_partial_overlap(output, values, allow_exact=True) + function = getattr(self.library._dll, f"lecore_{operation}_{self._suffix}") + self._invoke( + f"lecore_{operation}_{self._suffix}", + function, + self._pointer_to(values), + self._pointer_to(output), + ) + return output + + def _binary_vector( + self, + operation: str, + a: np.ndarray, + b: np.ndarray, + *, + out: Optional[np.ndarray], + ) -> np.ndarray: + self._check_owner() + a = self._vector(a, "a") + b = self._vector(b, "b") + output = self._output(out, (self.dimension,)) + _reject_partial_overlap(output, a, b, allow_exact=True) + function = getattr(self.library._dll, f"lecore_{operation}_{self._suffix}") + self._invoke( + f"lecore_{operation}_{self._suffix}", + function, + self._pointer_to(a), + self._pointer_to(b), + self._pointer_to(output), + ) + return output + + def _binary_scalar(self, operation: str, a: np.ndarray, b: np.ndarray) -> float: + self._check_owner() + a = self._vector(a, "a") + b = self._vector(b, "b") + output = self._ctype() + function = getattr(self.library._dll, f"lecore_{operation}_{self._suffix}") + self._invoke( + f"lecore_{operation}_{self._suffix}", + function, + self._pointer_to(a), + self._pointer_to(b), + ctypes.byref(output), + ) + return float(output.value) + + def normalize(self, values: np.ndarray, *, out: Optional[np.ndarray] = None) -> np.ndarray: + return self._unary("normalize", values, out=out) + + def involution(self, values: np.ndarray, *, out: Optional[np.ndarray] = None) -> np.ndarray: + return self._unary("involution", values, out=out) + + def permute( + self, + values: np.ndarray, + shift: int, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + self._check_owner() + values = self._vector(values) + if isinstance(shift, bool) or not isinstance(shift, (int, np.integer)): + raise TypeError("shift must be an integer") + if not -(1 << 63) <= int(shift) < (1 << 63): + raise OverflowError("shift does not fit int64") + output = self._output(out, (self.dimension,)) + _reject_partial_overlap(output, values, allow_exact=True) + function = getattr(self.library._dll, f"lecore_permute_{self._suffix}") + self._invoke( + f"lecore_permute_{self._suffix}", + function, + self._pointer_to(values), + int(shift), + self._pointer_to(output), + ) + return output + + def dot(self, a: np.ndarray, b: np.ndarray) -> float: + return self._binary_scalar("dot", a, b) + + def cosine(self, a: np.ndarray, b: np.ndarray) -> float: + return self._binary_scalar("cosine", a, b) + + def bind( + self, + a: np.ndarray, + b: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + return self._binary_vector("hrr_bind", a, b, out=out) + + def unbind( + self, + composite: np.ndarray, + key: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + return self._binary_vector("hrr_unbind", composite, key, out=out) + + def _score_many( + self, + operation: str, + query: np.ndarray, + rows: np.ndarray, + *, + out: Optional[np.ndarray], + ) -> np.ndarray: + self._check_owner() + query = self._vector(query, "query") + rows = self._rows(rows) + output = self._output(out, (rows.shape[0],)) + _reject_partial_overlap(output, query, rows, allow_exact=False) + function = getattr(self.library._dll, f"lecore_{operation}_{self._suffix}") + self._invoke( + f"lecore_{operation}_{self._suffix}", + function, + self._pointer_to(query), + self._pointer_to(rows), + rows.shape[0], + self.dimension, + self._pointer_to(output), + ) + return output + + def dot_many( + self, + query: np.ndarray, + rows: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + return self._score_many("dot_many", query, rows, out=out) + + def cosine_many( + self, + query: np.ndarray, + rows: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + return self._score_many("cosine_many", query, rows, out=out) + + def bundle(self, rows: np.ndarray, *, out: Optional[np.ndarray] = None) -> np.ndarray: + self._check_owner() + rows = self._rows(rows) + output = self._output(out, (self.dimension,)) + _reject_partial_overlap(output, rows, allow_exact=False) + function = getattr(self.library._dll, f"lecore_bundle_{self._suffix}") + self._invoke( + f"lecore_bundle_{self._suffix}", + function, + self._pointer_to(rows), + rows.shape[0], + self.dimension, + self._pointer_to(output), + ) + return output + + def cleanup(self, query: np.ndarray, candidates: np.ndarray) -> tuple[int, float]: + self._check_owner() + query = self._vector(query, "query") + candidates = self._rows(candidates, "candidates") + index = ctypes.c_size_t() + score = self._ctype() + function = getattr(self.library._dll, f"lecore_cleanup_{self._suffix}") + self._invoke( + f"lecore_cleanup_{self._suffix}", + function, + self._pointer_to(query), + self._pointer_to(candidates), + candidates.shape[0], + self.dimension, + ctypes.byref(index), + ctypes.byref(score), + ) + return int(index.value), float(score.value) + + def bind_batch( + self, + a_rows: np.ndarray, + b_rows: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + self._check_owner() + a_rows = self._rows(a_rows, "a_rows") + b_rows = self._rows(b_rows, "b_rows") + if a_rows.shape != b_rows.shape: + raise ValueError("a_rows and b_rows must have equal shape") + output = self._output(out, a_rows.shape) + _reject_partial_overlap(output, a_rows, b_rows, allow_exact=False) + function = getattr(self.library._dll, f"lecore_hrr_bind_batch_{self._suffix}") + self._invoke( + f"lecore_hrr_bind_batch_{self._suffix}", + function, + self._pointer_to(a_rows), + self.dimension, + self._pointer_to(b_rows), + self.dimension, + a_rows.shape[0], + self._pointer_to(output), + self.dimension, + ) + return output + + def bind_fixed( + self, + role: np.ndarray, + rows: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + self._check_owner() + role = self._vector(role, "role") + rows = self._rows(rows) + output = self._output(out, rows.shape) + _reject_partial_overlap(output, role, rows, allow_exact=False) + function = getattr(self.library._dll, f"lecore_hrr_bind_fixed_{self._suffix}") + self._invoke( + f"lecore_hrr_bind_fixed_{self._suffix}", + function, + self._pointer_to(role), + self._pointer_to(rows), + rows.shape[0], + self.dimension, + self._pointer_to(output), + self.dimension, + ) + return output + + def unbind_all( + self, + trace: np.ndarray, + keys: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + self._check_owner() + trace = self._vector(trace, "trace") + keys = self._rows(keys, "keys") + output = self._output(out, keys.shape) + _reject_partial_overlap(output, trace, keys, allow_exact=False) + function = getattr(self.library._dll, f"lecore_hrr_unbind_all_{self._suffix}") + self._invoke( + f"lecore_hrr_unbind_all_{self._suffix}", + function, + self._pointer_to(trace), + self._pointer_to(keys), + keys.shape[0], + self.dimension, + self._pointer_to(output), + self.dimension, + ) + return output + + def cosine_many_f64_f32( + self, + query: np.ndarray, + rows: np.ndarray, + *, + out: Optional[np.ndarray] = None, + ) -> np.ndarray: + """Score an f64 query against f32 rows using ordered f64 reductions.""" + self._check_owner() + if self.profile != LECORE_PROFILE_HRR_F32_V1: + raise LeCoreCompatibilityError("mixed scoring requires an f32 context") + query = _strict_external_array( + query, "query", np.dtype(np.float64), 1, (self.dimension,) + ) + rows = self._rows(rows) + if out is None: + output = np.empty((rows.shape[0],), dtype=np.float64) + else: + output = _strict_external_array( + out, + "out", + np.dtype(np.float64), + 1, + (rows.shape[0],), + writable=True, + ) + _reject_partial_overlap(output, query, rows, allow_exact=False) + self._invoke( + "lecore_cosine_many_f64_f32", + self.library._dll.lecore_cosine_many_f64_f32, + query.ctypes.data_as(ctypes.POINTER(ctypes.c_double)), + self._pointer_to(rows), + rows.shape[0], + self.dimension, + output.ctypes.data_as(ctypes.POINTER(ctypes.c_double)), + ) + return output + + +def _strict_external_array( + values: np.ndarray, + name: str, + dtype: np.dtype, + ndim: int, + shape: tuple[Optional[int], ...], + *, + writable: bool = False, +) -> np.ndarray: + if not isinstance(values, np.ndarray): + raise TypeError(f"{name} must be a NumPy ndarray") + if values.dtype != dtype: + raise TypeError(f"{name} dtype must be exactly native {dtype.name}, got {values.dtype}") + if values.ndim != ndim: + raise ValueError(f"{name} must have {ndim} dimensions, got {values.ndim}") + for axis, expected in enumerate(shape): + if expected is not None and values.shape[axis] != expected: + raise ValueError( + f"{name} axis {axis} must have length {expected}, got {values.shape[axis]}" + ) + if not values.flags.c_contiguous or not values.flags.aligned: + raise ValueError(f"{name} must be aligned and C-contiguous") + if writable and not values.flags.writeable: + raise ValueError(f"{name} must be writable") + return values + + +__all__ = [ + "Context", + "Library", + "LeCoreError", + "LeCoreInvalidArgumentError", + "LeCoreDimensionError", + "LeCoreProfileError", + "LeCoreBackendError", + "LeCoreOverflowError", + "LeCoreMemoryError", + "LeCoreUnsupportedError", + "LeCoreNonFiniteError", + "LeCoreFormatError", + "LeCoreChecksumError", + "LeCoreCompatibilityError", + "LeCoreClosedError", + "LeCoreThreadError", +] diff --git a/bindings/rust/lecore-sys/.gitignore b/bindings/rust/lecore-sys/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/bindings/rust/lecore-sys/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/bindings/rust/lecore-sys/Cargo.lock b/bindings/rust/lecore-sys/Cargo.lock new file mode 100644 index 0000000..e506190 --- /dev/null +++ b/bindings/rust/lecore-sys/Cargo.lock @@ -0,0 +1,32 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "lecore-sys" +version = "0.1.0" +dependencies = [ + "cc", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" diff --git a/bindings/rust/lecore-sys/Cargo.toml b/bindings/rust/lecore-sys/Cargo.toml new file mode 100644 index 0000000..28f4a8d --- /dev/null +++ b/bindings/rust/lecore-sys/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "lecore-sys" +version = "0.1.0" +edition = "2021" +rust-version = "1.77" +description = "Audited ABI-0 Rust bindings and ownership wrapper for liblecore" +license = "MIT" +repository = "https://github.com/AnOversizedMooseWithSocks/leCore" +publish = false +links = "lecore" +build = "build.rs" + +[features] +default = ["vendored"] +vendored = [] +installed = [] + +[build-dependencies] +cc = "=1.2.65" diff --git a/bindings/rust/lecore-sys/README.md b/bindings/rust/lecore-sys/README.md new file mode 100644 index 0000000..2e290d7 --- /dev/null +++ b/bindings/rust/lecore-sys/README.md @@ -0,0 +1,32 @@ +# lecore-sys + +Audited Rust declarations and a small ownership wrapper for liblecore's unstable ABI-0 preview. + +The default `vendored` feature compiles the repository's deterministic +`native/liblecore/amalgamation/lecore.c` with the cached `cc` crate: + +```sh +cargo test --manifest-path bindings/rust/lecore-sys/Cargo.toml --offline +``` + +A native vendored build compiles and runs liblecore's floating-point contract +probe before advertising either numeric profile. A cross build cannot run that +target probe, so it fails unless the target has been audited and the build sets +`LECORE_SYS_ASSUME_IEC_60559=1` explicitly. + +Installed-library mode never searches the machine implicitly. Disable defaults, select `installed`, and provide +the exact include directory, library directory, and link kind: + +```sh +LECORE_SYS_INCLUDE_DIR=/pinned/prefix/include \ +LECORE_SYS_LIB_DIR=/pinned/prefix/lib \ +LECORE_SYS_LINK_KIND=static \ +cargo test --manifest-path bindings/rust/lecore-sys/Cargo.toml \ + --no-default-features --features installed --offline +``` + +`Context` owns one opaque native context and releases it on drop. Numeric methods take `&mut self` because the C +kernel uses context-owned scratch. Vector lengths, profiles, strides, row spans, and output sizes are checked before +FFI calls. `Context` is deliberately neither `Send` nor `Sync`; ABI-0 does not yet promise that moving a live context +between threads is supported. Raw statuses remain `u32` values, so an additive future status does not create an +invalid Rust enum discriminant. diff --git a/bindings/rust/lecore-sys/build.rs b/bindings/rust/lecore-sys/build.rs new file mode 100644 index 0000000..4de1351 --- /dev/null +++ b/bindings/rust/lecore-sys/build.rs @@ -0,0 +1,188 @@ +use std::env; +use std::path::{Path, PathBuf}; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-env-changed=LECORE_SYS_INCLUDE_DIR"); + println!("cargo:rerun-if-env-changed=LECORE_SYS_LIB_DIR"); + println!("cargo:rerun-if-env-changed=LECORE_SYS_LINK_KIND"); + println!("cargo:rerun-if-env-changed=LECORE_SYS_ASSUME_IEC_60559"); + + let vendored = env::var_os("CARGO_FEATURE_VENDORED").is_some(); + let installed = env::var_os("CARGO_FEATURE_INSTALLED").is_some(); + match (vendored, installed) { + (true, false) => build_vendored(), + (false, true) => link_installed(), + (true, true) => panic!( + "features `vendored` and `installed` are mutually exclusive; \ + use `--no-default-features --features installed` for an installed library" + ), + (false, false) => panic!("enable exactly one of `vendored` or `installed`"), + } +} + +fn build_vendored() { + let manifest_dir = + PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("Cargo sets CARGO_MANIFEST_DIR")); + let amalgamation_dir = manifest_dir.join("../../../native/liblecore/amalgamation"); + let source = amalgamation_dir.join("lecore.c"); + let header = amalgamation_dir.join("lecore.h"); + require_file(&source, "generated liblecore amalgamation source"); + require_file(&header, "generated liblecore amalgamation header"); + validate_vendored_floating_point(); + + println!("cargo:rerun-if-changed={}", source.display()); + println!("cargo:rerun-if-changed={}", header.display()); + + let mut build = cc::Build::new(); + build + .file(&source) + .include(&amalgamation_dir) + .define("LECORE_BUILDING_LIBRARY", "1") + .define("LECORE_ENABLE_FORMAT", "1") + .define("LECORE_ENABLE_RADIX2", "1") + .warnings(true) + .extra_warnings(true) + .std("c11"); + if build.get_compiler().is_like_msvc() { + build.flag("/fp:strict"); + } else { + build + .flag_if_supported("-fno-fast-math") + .flag_if_supported("-ffp-contract=off"); + } + build.compile("lecore"); + link_platform_math(); +} + +fn validate_vendored_floating_point() { + let host = env::var("HOST").expect("Cargo sets HOST"); + let target = env::var("TARGET").expect("Cargo sets TARGET"); + if host != target { + if env::var("LECORE_SYS_ASSUME_IEC_60559").as_deref() != Ok("1") { + panic!( + "cross-compiling vendored liblecore requires \ + LECORE_SYS_ASSUME_IEC_60559=1 after auditing the target's \ + IEC 60559 NaN/Inf, round-to-nearest, and evaluation behavior" + ); + } + return; + } + + let manifest_dir = + PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("Cargo sets CARGO_MANIFEST_DIR")); + let source = manifest_dir.join("build/fp_probe.c"); + let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("Cargo sets OUT_DIR")); + let executable = out_dir.join(if cfg!(windows) { + "lecore_fp_probe.exe" + } else { + "lecore_fp_probe" + }); + require_file(&source, "vendored floating-point probe"); + println!("cargo:rerun-if-changed={}", source.display()); + + let compiler = cc::Build::new().get_compiler(); + let mut compile = compiler.to_command(); + if compiler.is_like_msvc() { + compile + .arg("/nologo") + .arg("/std:c11") + .arg("/fp:strict") + .arg(&source) + .arg(format!("/Fe{}", executable.display())); + } else { + compile + .arg("-std=c11") + .arg("-fno-fast-math") + .arg("-ffp-contract=off") + .arg(&source) + .arg("-o") + .arg(&executable); + let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default(); + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + if target_family == "unix" && target_os != "macos" && target_os != "ios" { + compile.arg("-lm"); + } + } + let compile_status = compile.status().unwrap_or_else(|error| { + panic!("failed to launch liblecore floating-point probe compiler: {error}") + }); + if !compile_status.success() { + panic!("failed to compile liblecore's floating-point probe"); + } + + let run_status = Command::new(&executable) + .status() + .unwrap_or_else(|error| panic!("failed to run liblecore floating-point probe: {error}")); + if !run_status.success() { + panic!( + "the compiler/runtime failed liblecore's IEC 60559 and evaluation probe (status {run_status})" + ); + } +} + +fn link_installed() { + let include_dir = required_env_path("LECORE_SYS_INCLUDE_DIR"); + let lib_dir = required_env_path("LECORE_SYS_LIB_DIR"); + let header = include_dir.join("lecore/lecore.h"); + require_file(&header, "installed "); + if !lib_dir.is_dir() { + panic!( + "LECORE_SYS_LIB_DIR is not a directory: {}", + lib_dir.display() + ); + } + + let link_kind = env::var("LECORE_SYS_LINK_KIND").unwrap_or_else(|_| { + panic!("LECORE_SYS_LINK_KIND must be explicitly set to `static` or `dylib`") + }); + if link_kind != "static" && link_kind != "dylib" { + panic!("LECORE_SYS_LINK_KIND must be `static` or `dylib`, got {link_kind:?}"); + } + + println!("cargo:rerun-if-changed={}", header.display()); + compile_installed_abi_check(&include_dir); + println!("cargo:rustc-link-search=native={}", lib_dir.display()); + println!("cargo:rustc-link-lib={link_kind}=lecore"); + if link_kind == "static" { + link_platform_math(); + } +} + +fn compile_installed_abi_check(include_dir: &Path) { + let manifest_dir = + PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").expect("Cargo sets CARGO_MANIFEST_DIR")); + let source = manifest_dir.join("build/abi_check.c"); + require_file(&source, "installed-header ABI check"); + println!("cargo:rerun-if-changed={}", source.display()); + + cc::Build::new() + .file(source) + .include(include_dir) + .cargo_metadata(false) + .warnings(true) + .extra_warnings(true) + .std("c11") + .compile("lecore_rust_abi_check"); +} + +fn required_env_path(name: &str) -> PathBuf { + let value = env::var_os(name).unwrap_or_else(|| { + panic!("{name} is required with the `installed` feature; no global search is performed") + }); + PathBuf::from(value) +} + +fn require_file(path: &Path, description: &str) { + if !path.is_file() { + panic!("missing {description}: {}", path.display()); + } +} + +fn link_platform_math() { + let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or_default(); + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + if target_family == "unix" && target_os != "macos" && target_os != "ios" { + println!("cargo:rustc-link-lib=m"); + } +} diff --git a/bindings/rust/lecore-sys/build/abi_check.c b/bindings/rust/lecore-sys/build/abi_check.c new file mode 100644 index 0000000..6dcecbb --- /dev/null +++ b/bindings/rust/lecore-sys/build/abi_check.c @@ -0,0 +1,32 @@ +#include + +#include +#include + +_Static_assert(LECORE_ABI_VERSION == UINT32_C(0), + "lecore-sys 0.1 audits only liblecore ABI 0"); +_Static_assert(sizeof(lecore_status) == sizeof(uint32_t), + "lecore_status must remain uint32_t"); +_Static_assert(sizeof(lecore_profile) == sizeof(uint32_t), + "lecore_profile must remain uint32_t"); +_Static_assert(sizeof(lecore_backend) == sizeof(uint32_t), + "lecore_backend must remain uint32_t"); +_Static_assert(offsetof(lecore_config_v0, allocator) % _Alignof(void *) == 0, + "allocator must retain pointer alignment"); + +void lecore_rust_abi_check(void) +{ + lecore_status (LECORE_CALL *bind_f64)( + lecore_context *, const double *, const double *, double *) = + &lecore_hrr_bind_f64; + lecore_status (LECORE_CALL *bind_f32)( + lecore_context *, const float *, const float *, float *) = + &lecore_hrr_bind_f32; + lecore_status (LECORE_CALL *mixed)( + lecore_context *, const double *, const float *, size_t, size_t, + double *) = &lecore_cosine_many_f64_f32; + + (void)bind_f64; + (void)bind_f32; + (void)mixed; +} diff --git a/bindings/rust/lecore-sys/build/fp_probe.c b/bindings/rust/lecore-sys/build/fp_probe.c new file mode 100644 index 0000000..1ec6041 --- /dev/null +++ b/bindings/rust/lecore-sys/build/fp_probe.c @@ -0,0 +1,28 @@ +#include +#include +#include + +int main(void) +{ + volatile double one = 1.0; + volatile double zero = 0.0; + volatile float large = 16777216.0F; + double infinity = one / zero; + double not_a_number = zero / zero; + float rounded = large + 1.0F; + + if (FLT_EVAL_METHOD != 0 || fegetround() != FE_TONEAREST) { + return 1; + } + if (!isinf(infinity) || signbit(infinity) || + !isinf(-infinity) || !signbit(-infinity)) { + return 2; + } + if (!isnan(not_a_number) || !isnan(not_a_number + one)) { + return 3; + } + if (rounded != 16777216.0F) { + return 4; + } + return 0; +} diff --git a/bindings/rust/lecore-sys/src/lib.rs b/bindings/rust/lecore-sys/src/lib.rs new file mode 100644 index 0000000..76fd5a9 --- /dev/null +++ b/bindings/rust/lecore-sys/src/lib.rs @@ -0,0 +1,610 @@ +//! Rust access to liblecore's ABI-0 preview. +//! +//! [`raw`] mirrors the public numeric C header. [`Context`] is the intentionally +//! small safe layer: it owns an opaque context, validates slices and semantic +//! profiles, and serializes scratch-using calls through `&mut self`. + +#![deny(unsafe_op_in_unsafe_fn)] + +pub mod raw; + +use std::ffi::CStr; +use std::fmt; +use std::marker::PhantomData; +use std::mem::MaybeUninit; +use std::ptr::NonNull; +use std::rc::Rc; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Status(pub raw::lecore_status); + +impl Status { + pub const OK: Self = Self(raw::LECORE_OK); + pub const INVALID_ARGUMENT: Self = Self(raw::LECORE_EINVAL); + pub const INVALID_DIMENSION: Self = Self(raw::LECORE_EDIM); + pub const PROFILE: Self = Self(raw::LECORE_EPROFILE); + pub const BACKEND: Self = Self(raw::LECORE_EBACKEND); + pub const OVERFLOW: Self = Self(raw::LECORE_EOVERFLOW); + pub const NO_MEMORY: Self = Self(raw::LECORE_ENOMEM); + pub const UNSUPPORTED: Self = Self(raw::LECORE_EUNSUPPORTED); + pub const NONFINITE: Self = Self(raw::LECORE_ENONFINITE); + pub const FORMAT: Self = Self(raw::LECORE_EFORMAT); + pub const CHECKSUM: Self = Self(raw::LECORE_ECHECKSUM); + + pub fn is_ok(self) -> bool { + self == Self::OK + } + + /// Fetches the library's static description, retaining unknown raw values. + pub fn message(self) -> String { + // SAFETY: The ABI accepts every u32 status and returns a static string. + let pointer = unsafe { raw::lecore_status_string(self.0) }; + if pointer.is_null() { + return "unknown status".to_owned(); + } + // SAFETY: The ABI promises a non-null, NUL-terminated static string. + unsafe { CStr::from_ptr(pointer) } + .to_string_lossy() + .into_owned() + } +} + +impl fmt::Display for Status { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{} ({})", self.message(), self.0) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Profile { + HrrF64V1, + HrrF32V1, +} + +impl Profile { + pub const fn as_raw(self) -> raw::lecore_profile { + match self { + Self::HrrF64V1 => raw::LECORE_PROFILE_HRR_F64_V1, + Self::HrrF32V1 => raw::LECORE_PROFILE_HRR_F32_V1, + } + } + + const fn scalar_type(self) -> raw::lecore_scalar_type { + match self { + Self::HrrF64V1 => raw::LECORE_SCALAR_F64, + Self::HrrF32V1 => raw::LECORE_SCALAR_F32, + } + } + + const fn capability(self) -> u64 { + match self { + Self::HrrF64V1 => raw::LECORE_CAP_HRR_F64, + Self::HrrF32V1 => raw::LECORE_CAP_HRR_F32, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Backend { + Direct, + Radix2, +} + +impl Backend { + pub const fn as_raw(self) -> raw::lecore_backend { + match self { + Self::Direct => raw::LECORE_BACKEND_DIRECT, + Self::Radix2 => raw::LECORE_BACKEND_RADIX2, + } + } + + const fn capability(self) -> u64 { + match self { + Self::Direct => raw::LECORE_CAP_DIRECT, + Self::Radix2 => raw::LECORE_CAP_RADIX2, + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Validation { + Shape, + Finite, +} + +impl Validation { + pub const fn as_raw(self) -> raw::lecore_validation { + match self { + Self::Shape => raw::LECORE_VALIDATION_SHAPE, + Self::Finite => raw::LECORE_VALIDATION_FINITE, + } + } +} + +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + Native(Status), + InvalidDimension(usize), + UnsupportedRadix2Dimension(usize), + ProfileMismatch { + required: Profile, + actual: Profile, + }, + InvalidLength { + argument: &'static str, + expected: usize, + actual: usize, + }, + InvalidRowCount, + InvalidStride { + stride: usize, + dimension: usize, + }, + SizeOverflow, + AbiMismatch { + expected: u32, + actual: u32, + }, + IsaMismatch { + expected: u32, + actual: u32, + }, + LibraryInvariant(&'static str), +} + +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Native(status) => write!(formatter, "liblecore returned {status}"), + Self::InvalidDimension(dimension) => { + write!(formatter, "dimension {dimension} is outside 1..=UINT32_MAX") + } + Self::UnsupportedRadix2Dimension(dimension) => write!( + formatter, + "radix-2 requires a power-of-two dimension, got {dimension}" + ), + Self::ProfileMismatch { required, actual } => { + write!( + formatter, + "operation requires {required:?}, context is {actual:?}" + ) + } + Self::InvalidLength { + argument, + expected, + actual, + } => write!( + formatter, + "{argument} has length {actual}, expected {expected}" + ), + Self::InvalidRowCount => write!(formatter, "row_count must be positive"), + Self::InvalidStride { stride, dimension } => write!( + formatter, + "row stride {stride} is smaller than dimension {dimension}" + ), + Self::SizeOverflow => write!(formatter, "matrix span overflows usize"), + Self::AbiMismatch { expected, actual } => { + write!( + formatter, + "liblecore ABI {actual} does not match expected ABI {expected}" + ) + } + Self::IsaMismatch { expected, actual } => { + write!( + formatter, + "liblecore ISA {actual} does not match expected ISA {expected}" + ) + } + Self::LibraryInvariant(message) => { + write!(formatter, "liblecore invariant failed: {message}") + } + } + } +} + +impl std::error::Error for Error {} + +/// Owned opaque liblecore state. +/// +/// Calls require `&mut self` because both direct exact-alias paths and radix-2 +/// transforms may use context-owned scratch. +/// +/// ABI-0 has not yet promised that a live context may be transferred between +/// threads, so this wrapper is deliberately neither `Send` nor `Sync`. +/// +/// ```compile_fail +/// fn require_send() {} +/// require_send::(); +/// ``` +/// +/// ```compile_fail +/// fn require_sync() {} +/// require_sync::(); +/// ``` +pub struct Context { + pointer: NonNull, + dimension: usize, + profile: Profile, + backend: Backend, + validation: Validation, + _not_send_or_sync: PhantomData>, +} + +impl Context { + pub fn new(dimension: usize, profile: Profile, backend: Backend) -> Result { + Self::with_validation(dimension, profile, backend, Validation::Shape) + } + + pub fn with_validation( + dimension: usize, + profile: Profile, + backend: Backend, + validation: Validation, + ) -> Result { + let dimension_u32 = + u32::try_from(dimension).map_err(|_| Error::InvalidDimension(dimension))?; + if dimension_u32 == 0 { + return Err(Error::InvalidDimension(dimension)); + } + if backend == Backend::Radix2 && !dimension.is_power_of_two() { + return Err(Error::UnsupportedRadix2Dimension(dimension)); + } + + validate_library_versions(abi_version(), isa_version())?; + let available = capabilities(); + if available & profile.capability() == 0 || available & backend.capability() == 0 { + return Err(Error::Native(Status::UNSUPPORTED)); + } + + let mut config = MaybeUninit::::uninit(); + // SAFETY: The initializer writes the complete public ABI-0 structure. + unsafe { raw::lecore_config_init_v0(config.as_mut_ptr()) }; + // SAFETY: Guaranteed by lecore_config_init_v0's public contract. + let mut config = unsafe { config.assume_init() }; + config.dimension = dimension_u32; + config.profile = profile.as_raw(); + config.backend = backend.as_raw(); + config.validation = validation.as_raw(); + + let mut pointer = std::ptr::null_mut(); + // SAFETY: Both pointers are valid and config was initialized by the ABI helper. + let status = unsafe { raw::lecore_context_create(&config, &mut pointer) }; + if status != raw::LECORE_OK { + if !pointer.is_null() { + // SAFETY: A non-null failure result violates the ABI but is still library-owned. + unsafe { raw::lecore_context_destroy(pointer) }; + return Err(Error::LibraryInvariant("failure returned a live context")); + } + return Err(Error::Native(Status(status))); + } + let pointer = NonNull::new(pointer) + .ok_or(Error::LibraryInvariant("success returned a null context"))?; + let context = Self { + pointer, + dimension, + profile, + backend, + validation, + _not_send_or_sync: PhantomData, + }; + context.verify_native_identity()?; + Ok(context) + } + + pub fn direct_f64(dimension: usize) -> Result { + Self::new(dimension, Profile::HrrF64V1, Backend::Direct) + } + + pub fn radix2_f64(dimension: usize) -> Result { + Self::new(dimension, Profile::HrrF64V1, Backend::Radix2) + } + + pub fn direct_f32(dimension: usize) -> Result { + Self::new(dimension, Profile::HrrF32V1, Backend::Direct) + } + + pub fn radix2_f32(dimension: usize) -> Result { + Self::new(dimension, Profile::HrrF32V1, Backend::Radix2) + } + + pub const fn dimension(&self) -> usize { + self.dimension + } + + pub const fn profile(&self) -> Profile { + self.profile + } + + pub const fn backend(&self) -> Backend { + self.backend + } + + pub const fn validation(&self) -> Validation { + self.validation + } + + pub fn scratch_bytes(&self) -> usize { + // SAFETY: self owns a live context for the duration of this call. + unsafe { raw::lecore_context_scratch_bytes(self.pointer.as_ptr()) } + } + + pub fn as_raw(&self) -> *mut raw::lecore_context { + self.pointer.as_ptr() + } + + pub fn bind_f64(&mut self, a: &[f64], b: &[f64], output: &mut [f64]) -> Result<(), Error> { + self.require_profile(Profile::HrrF64V1)?; + self.check_vector(a, "a")?; + self.check_vector(b, "b")?; + self.check_vector(output, "output")?; + // SAFETY: Exact lengths and disjoint mutable output are established by Rust borrows. + status_result(unsafe { + raw::lecore_hrr_bind_f64( + self.pointer.as_ptr(), + a.as_ptr(), + b.as_ptr(), + output.as_mut_ptr(), + ) + }) + } + + pub fn unbind_f64( + &mut self, + composite: &[f64], + key: &[f64], + output: &mut [f64], + ) -> Result<(), Error> { + self.require_profile(Profile::HrrF64V1)?; + self.check_vector(composite, "composite")?; + self.check_vector(key, "key")?; + self.check_vector(output, "output")?; + // SAFETY: Exact lengths and disjoint mutable output are established by Rust borrows. + status_result(unsafe { + raw::lecore_hrr_unbind_f64( + self.pointer.as_ptr(), + composite.as_ptr(), + key.as_ptr(), + output.as_mut_ptr(), + ) + }) + } + + pub fn bind_f32(&mut self, a: &[f32], b: &[f32], output: &mut [f32]) -> Result<(), Error> { + self.require_profile(Profile::HrrF32V1)?; + self.check_vector(a, "a")?; + self.check_vector(b, "b")?; + self.check_vector(output, "output")?; + // SAFETY: Exact lengths and disjoint mutable output are established by Rust borrows. + status_result(unsafe { + raw::lecore_hrr_bind_f32( + self.pointer.as_ptr(), + a.as_ptr(), + b.as_ptr(), + output.as_mut_ptr(), + ) + }) + } + + pub fn unbind_f32( + &mut self, + composite: &[f32], + key: &[f32], + output: &mut [f32], + ) -> Result<(), Error> { + self.require_profile(Profile::HrrF32V1)?; + self.check_vector(composite, "composite")?; + self.check_vector(key, "key")?; + self.check_vector(output, "output")?; + // SAFETY: Exact lengths and disjoint mutable output are established by Rust borrows. + status_result(unsafe { + raw::lecore_hrr_unbind_f32( + self.pointer.as_ptr(), + composite.as_ptr(), + key.as_ptr(), + output.as_mut_ptr(), + ) + }) + } + + pub fn cosine_many_f64_f32( + &mut self, + query: &[f64], + rows: &[f32], + row_count: usize, + row_stride: usize, + output: &mut [f64], + ) -> Result<(), Error> { + self.require_profile(Profile::HrrF32V1)?; + self.check_vector(query, "query")?; + let required_rows = self.matrix_span(row_count, row_stride)?; + if rows.len() < required_rows { + return Err(Error::InvalidLength { + argument: "rows", + expected: required_rows, + actual: rows.len(), + }); + } + if output.len() != row_count { + return Err(Error::InvalidLength { + argument: "output", + expected: row_count, + actual: output.len(), + }); + } + // SAFETY: All spans and output counts were overflow-checked above. + status_result(unsafe { + raw::lecore_cosine_many_f64_f32( + self.pointer.as_ptr(), + query.as_ptr(), + rows.as_ptr(), + row_count, + row_stride, + output.as_mut_ptr(), + ) + }) + } + + fn verify_native_identity(&self) -> Result<(), Error> { + let pointer = self.pointer.as_ptr(); + // SAFETY: pointer denotes the live context owned by self. + let native_dimension = unsafe { raw::lecore_context_dimension(pointer) } as usize; + // SAFETY: pointer denotes the live context owned by self. + let native_profile = unsafe { raw::lecore_context_profile(pointer) }; + // SAFETY: pointer denotes the live context owned by self. + let native_backend = unsafe { raw::lecore_context_backend(pointer) }; + // SAFETY: pointer denotes the live context owned by self. + let native_validation = unsafe { raw::lecore_context_validation(pointer) }; + // SAFETY: pointer denotes the live context owned by self. + let native_scalar = unsafe { raw::lecore_context_scalar_type(pointer) }; + if native_dimension != self.dimension { + return Err(Error::LibraryInvariant("dimension introspection mismatch")); + } + if native_profile != self.profile.as_raw() { + return Err(Error::LibraryInvariant("profile introspection mismatch")); + } + if native_backend != self.backend.as_raw() { + return Err(Error::LibraryInvariant("backend introspection mismatch")); + } + if native_validation != self.validation.as_raw() { + return Err(Error::LibraryInvariant("validation introspection mismatch")); + } + if native_scalar != self.profile.scalar_type() { + return Err(Error::LibraryInvariant("scalar introspection mismatch")); + } + Ok(()) + } + + fn require_profile(&self, required: Profile) -> Result<(), Error> { + if self.profile != required { + return Err(Error::ProfileMismatch { + required, + actual: self.profile, + }); + } + Ok(()) + } + + fn check_vector(&self, values: &[T], argument: &'static str) -> Result<(), Error> { + if values.len() != self.dimension { + return Err(Error::InvalidLength { + argument, + expected: self.dimension, + actual: values.len(), + }); + } + Ok(()) + } + + fn matrix_span(&self, row_count: usize, row_stride: usize) -> Result { + if row_count == 0 { + return Err(Error::InvalidRowCount); + } + if row_stride < self.dimension { + return Err(Error::InvalidStride { + stride: row_stride, + dimension: self.dimension, + }); + } + (row_count - 1) + .checked_mul(row_stride) + .and_then(|last| last.checked_add(self.dimension)) + .ok_or(Error::SizeOverflow) + } +} + +impl Drop for Context { + fn drop(&mut self) { + // SAFETY: Context uniquely owns this live pointer and drops it once. + unsafe { raw::lecore_context_destroy(self.pointer.as_ptr()) }; + } +} + +impl fmt::Debug for Context { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("Context") + .field("dimension", &self.dimension) + .field("profile", &self.profile) + .field("backend", &self.backend) + .field("validation", &self.validation) + .finish_non_exhaustive() + } +} + +pub fn abi_version() -> u32 { + // SAFETY: Pure process-global introspection with no preconditions. + unsafe { raw::lecore_abi_version() } +} + +pub fn isa_version() -> u32 { + // SAFETY: Pure process-global introspection with no preconditions. + unsafe { raw::lecore_isa_version() } +} + +pub fn capabilities() -> u64 { + // SAFETY: Pure process-global introspection with no preconditions. + unsafe { raw::lecore_capabilities() } +} + +pub fn version() -> String { + // SAFETY: Pure process-global introspection returning a static string. + let pointer = unsafe { raw::lecore_version_string() }; + if pointer.is_null() { + return String::new(); + } + // SAFETY: The ABI promises a non-null, NUL-terminated static string. + unsafe { CStr::from_ptr(pointer) } + .to_string_lossy() + .into_owned() +} + +fn status_result(status: raw::lecore_status) -> Result<(), Error> { + if status == raw::LECORE_OK { + Ok(()) + } else { + Err(Error::Native(Status(status))) + } +} + +fn validate_library_versions(actual_abi: u32, actual_isa: u32) -> Result<(), Error> { + if actual_abi != raw::LECORE_ABI_VERSION { + return Err(Error::AbiMismatch { + expected: raw::LECORE_ABI_VERSION, + actual: actual_abi, + }); + } + if actual_isa != raw::LECORE_ISA_VERSION { + return Err(Error::IsaMismatch { + expected: raw::LECORE_ISA_VERSION, + actual: actual_isa, + }); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn independently_rejects_abi_and_isa_mismatches() { + assert_eq!( + validate_library_versions(raw::LECORE_ABI_VERSION + 1, raw::LECORE_ISA_VERSION), + Err(Error::AbiMismatch { + expected: raw::LECORE_ABI_VERSION, + actual: raw::LECORE_ABI_VERSION + 1, + }) + ); + assert_eq!( + validate_library_versions(raw::LECORE_ABI_VERSION, raw::LECORE_ISA_VERSION + 1), + Err(Error::IsaMismatch { + expected: raw::LECORE_ISA_VERSION, + actual: raw::LECORE_ISA_VERSION + 1, + }) + ); + assert_eq!( + validate_library_versions(raw::LECORE_ABI_VERSION, raw::LECORE_ISA_VERSION), + Ok(()) + ); + } +} diff --git a/bindings/rust/lecore-sys/src/raw.rs b/bindings/rust/lecore-sys/src/raw.rs new file mode 100644 index 0000000..7baff6f --- /dev/null +++ b/bindings/rust/lecore-sys/src/raw.rs @@ -0,0 +1,322 @@ +//! Direct declarations for liblecore's public numeric ABI-0 header. +//! +//! Integer-valued C domains intentionally remain integer aliases instead of +//! Rust enums. Unknown additive values are therefore representable safely. + +#![allow(non_camel_case_types)] + +use std::cell::UnsafeCell; +use std::ffi::{c_char, c_void}; +use std::marker::PhantomData; + +pub const LECORE_ABI_VERSION: u32 = 0; +pub const LECORE_ISA_VERSION: u32 = 1; + +pub type lecore_status = u32; +pub const LECORE_OK: lecore_status = 0; +pub const LECORE_EINVAL: lecore_status = 1; +pub const LECORE_EDIM: lecore_status = 2; +pub const LECORE_EPROFILE: lecore_status = 3; +pub const LECORE_EBACKEND: lecore_status = 4; +pub const LECORE_EOVERFLOW: lecore_status = 5; +pub const LECORE_ENOMEM: lecore_status = 6; +pub const LECORE_EUNSUPPORTED: lecore_status = 7; +pub const LECORE_ENONFINITE: lecore_status = 8; +pub const LECORE_EFORMAT: lecore_status = 9; +pub const LECORE_ECHECKSUM: lecore_status = 10; + +pub type lecore_profile = u32; +pub const LECORE_PROFILE_HRR_F64_V1: lecore_profile = 0x0001_0001; +pub const LECORE_PROFILE_HRR_F32_V1: lecore_profile = 0x0001_0002; + +pub type lecore_backend = u32; +pub const LECORE_BACKEND_AUTO: lecore_backend = 0; +pub const LECORE_BACKEND_DIRECT: lecore_backend = 1; +pub const LECORE_BACKEND_RADIX2: lecore_backend = 2; + +pub type lecore_validation = u32; +pub const LECORE_VALIDATION_SHAPE: lecore_validation = 0; +pub const LECORE_VALIDATION_FINITE: lecore_validation = 1; + +pub type lecore_scalar_type = u32; +pub const LECORE_SCALAR_F64: lecore_scalar_type = 1; +pub const LECORE_SCALAR_F32: lecore_scalar_type = 2; + +pub const LECORE_CAP_HRR_F64: u64 = 1 << 0; +pub const LECORE_CAP_HRR_F32: u64 = 1 << 1; +pub const LECORE_CAP_DIRECT: u64 = 1 << 2; +pub const LECORE_CAP_RADIX2: u64 = 1 << 3; +pub const LECORE_CAP_BATCH: u64 = 1 << 4; +pub const LECORE_CAP_MIXED_F64_F32: u64 = 1 << 5; +pub const LECORE_CAP_FINITE_VALIDATION: u64 = 1 << 6; +pub const LECORE_CAP_FORMAT: u64 = 1 << 7; + +pub type lecore_allocate_fn = + Option *mut c_void>; +pub type lecore_deallocate_fn = + Option; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct lecore_allocator_v0 { + pub struct_size: u32, + pub user: *mut c_void, + pub allocate: lecore_allocate_fn, + pub deallocate: lecore_deallocate_fn, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +pub struct lecore_config_v0 { + pub struct_size: u32, + pub abi_version: u32, + pub profile: lecore_profile, + pub backend: lecore_backend, + pub validation: lecore_validation, + pub flags: u32, + pub dimension: u32, + pub allocator: lecore_allocator_v0, + pub reserved: [u64; 4], +} + +/// Opaque native state. Rust never mirrors or dereferences its private layout. +#[repr(C)] +pub struct lecore_context { + _private: [u8; 0], + _not_sync: PhantomData>, +} + +extern "C" { + pub fn lecore_abi_version() -> u32; + pub fn lecore_isa_version() -> u32; + pub fn lecore_version_string() -> *const c_char; + pub fn lecore_capabilities() -> u64; + pub fn lecore_status_string(status: lecore_status) -> *const c_char; + + pub fn lecore_config_init_v0(config: *mut lecore_config_v0); + pub fn lecore_context_create( + config: *const lecore_config_v0, + out_context: *mut *mut lecore_context, + ) -> lecore_status; + pub fn lecore_context_destroy(context: *mut lecore_context); + pub fn lecore_context_dimension(context: *const lecore_context) -> u32; + pub fn lecore_context_profile(context: *const lecore_context) -> lecore_profile; + pub fn lecore_context_backend(context: *const lecore_context) -> lecore_backend; + pub fn lecore_context_validation(context: *const lecore_context) -> lecore_validation; + pub fn lecore_context_scalar_type(context: *const lecore_context) -> lecore_scalar_type; + pub fn lecore_context_scratch_bytes(context: *const lecore_context) -> usize; + + pub fn lecore_validate_f64(values: *const f64, count: usize) -> lecore_status; + pub fn lecore_validate_f32(values: *const f32, count: usize) -> lecore_status; + + pub fn lecore_normalize_f64( + context: *mut lecore_context, + input: *const f64, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_normalize_f32( + context: *mut lecore_context, + input: *const f32, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_dot_f64( + context: *mut lecore_context, + a: *const f64, + b: *const f64, + out_dot: *mut f64, + ) -> lecore_status; + pub fn lecore_dot_f32( + context: *mut lecore_context, + a: *const f32, + b: *const f32, + out_dot: *mut f32, + ) -> lecore_status; + pub fn lecore_dot_many_f64( + context: *mut lecore_context, + query: *const f64, + rows: *const f64, + row_count: usize, + row_stride: usize, + out_scores: *mut f64, + ) -> lecore_status; + pub fn lecore_dot_many_f32( + context: *mut lecore_context, + query: *const f32, + rows: *const f32, + row_count: usize, + row_stride: usize, + out_scores: *mut f32, + ) -> lecore_status; + pub fn lecore_cosine_f64( + context: *mut lecore_context, + a: *const f64, + b: *const f64, + out_cosine: *mut f64, + ) -> lecore_status; + pub fn lecore_cosine_f32( + context: *mut lecore_context, + a: *const f32, + b: *const f32, + out_cosine: *mut f32, + ) -> lecore_status; + pub fn lecore_cosine_many_f64( + context: *mut lecore_context, + query: *const f64, + rows: *const f64, + row_count: usize, + row_stride: usize, + out_scores: *mut f64, + ) -> lecore_status; + pub fn lecore_cosine_many_f32( + context: *mut lecore_context, + query: *const f32, + rows: *const f32, + row_count: usize, + row_stride: usize, + out_scores: *mut f32, + ) -> lecore_status; + + pub fn lecore_hrr_bind_f64( + context: *mut lecore_context, + a: *const f64, + b: *const f64, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_hrr_bind_f32( + context: *mut lecore_context, + a: *const f32, + b: *const f32, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_hrr_unbind_f64( + context: *mut lecore_context, + composite: *const f64, + key: *const f64, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_hrr_unbind_f32( + context: *mut lecore_context, + composite: *const f32, + key: *const f32, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_involution_f64( + context: *mut lecore_context, + input: *const f64, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_involution_f32( + context: *mut lecore_context, + input: *const f32, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_permute_f64( + context: *mut lecore_context, + input: *const f64, + shift: i64, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_permute_f32( + context: *mut lecore_context, + input: *const f32, + shift: i64, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_bundle_f64( + context: *mut lecore_context, + rows: *const f64, + row_count: usize, + row_stride: usize, + output: *mut f64, + ) -> lecore_status; + pub fn lecore_bundle_f32( + context: *mut lecore_context, + rows: *const f32, + row_count: usize, + row_stride: usize, + output: *mut f32, + ) -> lecore_status; + pub fn lecore_cleanup_f64( + context: *mut lecore_context, + query: *const f64, + candidates: *const f64, + candidate_count: usize, + candidate_stride: usize, + out_index: *mut usize, + out_score: *mut f64, + ) -> lecore_status; + pub fn lecore_cleanup_f32( + context: *mut lecore_context, + query: *const f32, + candidates: *const f32, + candidate_count: usize, + candidate_stride: usize, + out_index: *mut usize, + out_score: *mut f32, + ) -> lecore_status; + + pub fn lecore_hrr_bind_batch_f64( + context: *mut lecore_context, + a_rows: *const f64, + a_stride: usize, + b_rows: *const f64, + b_stride: usize, + row_count: usize, + out_rows: *mut f64, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_hrr_bind_batch_f32( + context: *mut lecore_context, + a_rows: *const f32, + a_stride: usize, + b_rows: *const f32, + b_stride: usize, + row_count: usize, + out_rows: *mut f32, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_hrr_bind_fixed_f64( + context: *mut lecore_context, + role: *const f64, + rows: *const f64, + row_count: usize, + row_stride: usize, + out_rows: *mut f64, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_hrr_bind_fixed_f32( + context: *mut lecore_context, + role: *const f32, + rows: *const f32, + row_count: usize, + row_stride: usize, + out_rows: *mut f32, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_hrr_unbind_all_f64( + context: *mut lecore_context, + trace: *const f64, + keys: *const f64, + key_count: usize, + key_stride: usize, + out_rows: *mut f64, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_hrr_unbind_all_f32( + context: *mut lecore_context, + trace: *const f32, + keys: *const f32, + key_count: usize, + key_stride: usize, + out_rows: *mut f32, + out_stride: usize, + ) -> lecore_status; + pub fn lecore_cosine_many_f64_f32( + f32_context: *mut lecore_context, + query: *const f64, + rows: *const f32, + row_count: usize, + row_stride: usize, + out_scores: *mut f64, + ) -> lecore_status; +} diff --git a/bindings/rust/lecore-sys/tests/safe_api.rs b/bindings/rust/lecore-sys/tests/safe_api.rs new file mode 100644 index 0000000..767206f --- /dev/null +++ b/bindings/rust/lecore-sys/tests/safe_api.rs @@ -0,0 +1,199 @@ +use lecore_sys::{ + abi_version, capabilities, isa_version, raw, version, Backend, Context, Error, Profile, Status, + Validation, +}; +use std::mem::{offset_of, size_of, MaybeUninit}; + +fn assert_close(actual: f64, expected: f64, tolerance: f64) { + assert!( + (actual - expected).abs() <= tolerance, + "{actual:.17} != {expected:.17} within {tolerance}" + ); +} + +#[test] +fn raw_layout_and_version_match_abi_zero() { + assert_eq!(abi_version(), raw::LECORE_ABI_VERSION); + assert_eq!(isa_version(), raw::LECORE_ISA_VERSION); + assert_eq!(version(), "0.1.0"); + assert_eq!(size_of::(), 4); + assert_eq!(size_of::(), 4); + assert_eq!(offset_of!(raw::lecore_config_v0, dimension), 24); + assert!(offset_of!(raw::lecore_config_v0, allocator) >= 28); + + if size_of::() == 8 { + assert_eq!(size_of::(), 32); + assert_eq!(offset_of!(raw::lecore_config_v0, allocator), 32); + assert_eq!(size_of::(), 96); + } + + let mut config = MaybeUninit::::uninit(); + // SAFETY: The public initializer writes the complete ABI-0 structure. + unsafe { raw::lecore_config_init_v0(config.as_mut_ptr()) }; + // SAFETY: Guaranteed by lecore_config_init_v0. + let config = unsafe { config.assume_init() }; + assert_eq!( + config.struct_size as usize, + size_of::() + ); + assert_eq!( + config.allocator.struct_size as usize, + size_of::() + ); + assert_eq!(config.abi_version, raw::LECORE_ABI_VERSION); +} + +#[test] +fn isa_mismatch_is_public_and_descriptive() { + let error = Error::IsaMismatch { + expected: raw::LECORE_ISA_VERSION, + actual: raw::LECORE_ISA_VERSION + 1, + }; + assert_eq!( + error.to_string(), + "liblecore ISA 2 does not match expected ISA 1" + ); +} + +#[test] +fn direct_f64_context_owns_and_computes() { + let mut context = Context::direct_f64(4).expect("direct f64 context"); + assert_eq!(context.dimension(), 4); + assert_eq!(context.profile(), Profile::HrrF64V1); + assert_eq!(context.backend(), Backend::Direct); + assert_eq!(context.validation(), Validation::Shape); + assert_eq!(context.scratch_bytes(), 4 * size_of::()); + + let a = [1.0, 2.0, 0.0, -1.0]; + let b = [2.0, 0.0, 1.0, 0.0]; + let mut bound = [0.0; 4]; + context.bind_f64(&a, &b, &mut bound).unwrap(); + assert_eq!(bound, [2.0, 3.0, 1.0, 0.0]); + + let mut unbound = [0.0; 4]; + context.unbind_f64(&bound, &b, &mut unbound).unwrap(); + assert_eq!(unbound, [5.0, 6.0, 4.0, 3.0]); +} + +#[test] +fn f32_and_radix2_are_typed_and_explicit() { + let a = [1.0_f32, 2.0, 0.0, -1.0]; + let b = [2.0_f32, 0.0, 1.0, 0.0]; + let mut direct = Context::direct_f32(4).unwrap(); + let mut direct_output = [0.0_f32; 4]; + direct.bind_f32(&a, &b, &mut direct_output).unwrap(); + assert_eq!(direct_output, [2.0, 3.0, 1.0, 0.0]); + + assert_ne!(capabilities() & raw::LECORE_CAP_RADIX2, 0); + let mut radix = Context::radix2_f32(4).unwrap(); + assert_eq!(radix.backend(), Backend::Radix2); + assert_eq!(radix.scratch_bytes(), 4 * 4 * size_of::()); + let mut radix_output = [0.0_f32; 4]; + radix.bind_f32(&a, &b, &mut radix_output).unwrap(); + for (actual, expected) in radix_output.into_iter().zip(direct_output) { + assert_close(actual as f64, expected as f64, 1e-5); + } + + let mut recovered = [0.0_f32; 4]; + radix.unbind_f32(&radix_output, &b, &mut recovered).unwrap(); + for (actual, expected) in recovered.into_iter().zip([5.0, 6.0, 4.0, 3.0]) { + assert_close(actual as f64, expected, 1e-5); + } +} + +#[test] +fn mixed_f64_f32_cosine_checks_rows_and_scores() { + let mut context = Context::direct_f32(2).unwrap(); + let query = [1.0_f64, 2.0]; + let rows = [1.0_f32, 0.0, 99.0, 0.0, 2.0, 99.0, 0.0, 0.0]; + let mut scores = [0.0_f64; 3]; + context + .cosine_many_f64_f32(&query, &rows, 3, 3, &mut scores) + .unwrap(); + assert_close(scores[0], 1.0 / 5.0_f64.sqrt(), 1e-15); + assert_close(scores[1], 2.0 / 5.0_f64.sqrt(), 1e-15); + assert_eq!(scores[2], 0.0); +} + +#[test] +fn safe_layer_rejects_dimensions_profiles_and_bad_slices() { + assert_eq!( + Context::direct_f64(0).unwrap_err(), + Error::InvalidDimension(0) + ); + assert_eq!( + Context::radix2_f64(3).unwrap_err(), + Error::UnsupportedRadix2Dimension(3) + ); + + let mut f64_context = Context::direct_f64(4).unwrap(); + let mut f32_output = [0.0_f32; 4]; + assert_eq!( + f64_context + .bind_f32(&[0.0; 4], &[0.0; 4], &mut f32_output) + .unwrap_err(), + Error::ProfileMismatch { + required: Profile::HrrF32V1, + actual: Profile::HrrF64V1, + } + ); + let mut short_output = [0.0_f64; 3]; + assert_eq!( + f64_context + .bind_f64(&[0.0; 4], &[0.0; 4], &mut short_output) + .unwrap_err(), + Error::InvalidLength { + argument: "output", + expected: 4, + actual: 3, + } + ); + + let mut f32_context = Context::direct_f32(2).unwrap(); + let mut score = [0.0_f64; 1]; + assert_eq!( + f32_context + .cosine_many_f64_f32(&[1.0, 2.0], &[1.0, 2.0], 0, 2, &mut []) + .unwrap_err(), + Error::InvalidRowCount + ); + assert_eq!( + f32_context + .cosine_many_f64_f32(&[1.0, 2.0], &[1.0, 2.0], 1, 1, &mut score) + .unwrap_err(), + Error::InvalidStride { + stride: 1, + dimension: 2, + } + ); + assert_eq!( + f32_context + .cosine_many_f64_f32(&[1.0, 2.0], &[1.0], 1, 2, &mut score) + .unwrap_err(), + Error::InvalidLength { + argument: "rows", + expected: 2, + actual: 1, + } + ); +} + +#[test] +fn finite_validation_and_unknown_statuses_remain_explicit() { + let mut context = + Context::with_validation(2, Profile::HrrF64V1, Backend::Direct, Validation::Finite) + .unwrap(); + let mut output = [17.0_f64; 2]; + assert_eq!( + context + .bind_f64(&[f64::NAN, 0.0], &[1.0, 0.0], &mut output) + .unwrap_err(), + Error::Native(Status::NONFINITE) + ); + assert_eq!(output, [17.0, 17.0]); + + let unknown = Status(0xffff_fffe); + assert_eq!(unknown.0, 0xffff_fffe); + assert_eq!(unknown.message(), "unknown status"); + assert!(!unknown.is_ok()); +} diff --git a/holographic/misc/holographic_reference.py b/holographic/misc/holographic_reference.py index 0dcf572..5545e5c 100644 --- a/holographic/misc/holographic_reference.py +++ b/holographic/misc/holographic_reference.py @@ -71,21 +71,98 @@ def ref_permute(vec, shift): def ref_bundle(vectors): - """Superpose then renormalize; a zero-sum bundle returns the zero vector (the pinned edge).""" - total = np.sum(np.asarray(vectors, float), axis=0) - norm = np.linalg.norm(total) - return total / norm if norm > 0 else total + """Superpose in row/index order, then normalize; a zero-sum bundle stays zero.""" + vectors = np.asarray(vectors, float) + if vectors.ndim != 2 or not len(vectors): + raise ValueError("ref_bundle expects a non-empty 2-D matrix") + total = np.zeros(vectors.shape[1], dtype=float) + for row in vectors: + for index in range(vectors.shape[1]): + total[index] += float(row[index]) + return ref_normalize(total) def ref_cosine(a, b): - """dot / (|a| |b|); zero norm -> 0.0 (the pinned edge).""" + """Ascending-order dot / (|a| |b|); zero norm -> 0.0.""" a = np.asarray(a, float) b = np.asarray(b, float) - na = np.linalg.norm(a) - nb = np.linalg.norm(b) - if na == 0 or nb == 0: + norm_a_squared = ref_dot(a, a) + norm_b_squared = ref_dot(b, b) + if norm_a_squared == 0.0 or norm_b_squared == 0.0: return 0.0 - return float(np.dot(a, b) / (na * nb)) + norm_a = float(np.sqrt(norm_a_squared)) + norm_b = float(np.sqrt(norm_b_squared)) + return ref_dot(a, b) / (norm_a * norm_b) + + +def ref_normalize(a): + """Ascending-order normalization: zero stays zero; non-finite values propagate.""" + a = np.asarray(a, float) + norm = float(np.sqrt(ref_dot(a, a))) + return a / norm if norm > 0 else a.copy() + + +def ref_dot(a, b): + """Definition of the ordered f64 dot utility: multiply/accumulate from the lowest index upward.""" + a = np.asarray(a, float) + b = np.asarray(b, float) + if a.ndim != 1 or b.ndim != 1 or len(a) != len(b): + raise ValueError("ref_dot expects equal-length 1-D vectors") + acc = 0.0 + for i in range(len(a)): + acc += float(a[i]) * float(b[i]) + return acc + + +def ref_dot_many(query, matrix): + """Apply ref_dot to codebook rows in stable ascending order.""" + query = np.asarray(query, float) + matrix = np.asarray(matrix, float) + if matrix.ndim != 2 or query.ndim != 1 or matrix.shape[1] != len(query): + raise ValueError("ref_dot_many expects a 1-D query and a matching 2-D matrix") + return np.asarray([ref_dot(query, row) for row in matrix], dtype=float) + + +def ref_cosine_many(query, matrix): + """Apply ref_cosine to codebook rows in stable ascending order.""" + query = np.asarray(query, float) + matrix = np.asarray(matrix, float) + if matrix.ndim != 2 or query.ndim != 1 or matrix.shape[1] != len(query): + raise ValueError("ref_cosine_many expects a 1-D query and a matching 2-D matrix") + return np.asarray([ref_cosine(query, row) for row in matrix], dtype=float) + + +def ref_cleanup(query, codebook): + """Return the frozen cleanup decision and score, including NumPy's first-NaN argmax behavior.""" + scores = ref_cosine_many(query, codebook) + if not len(scores): + raise ValueError("ref_cleanup requires a non-empty codebook") + index = int(np.argmax(scores)) + return index, float(scores[index]) + + +def ref_cosine_many_f64_f32(query, matrix): + """Definition of the mixed f64-query/f32-corpus utility planned for NoSQLite. + + Corpus components are first rounded to f32, then converted exactly to f64. Dot and both squared norms are + accumulated from the lowest component index upward in f64. A zero norm takes precedence over a non-finite value, + matching ref_cosine's boundary behavior. + """ + query = np.asarray(query, dtype=np.float64) + matrix = np.asarray(matrix, dtype=np.float32) + if matrix.ndim != 2 or query.ndim != 1 or matrix.shape[1] != len(query): + raise ValueError("ref_cosine_many_f64_f32 expects a 1-D f64 query and matching 2-D f32 matrix") + + query_norm_sq = ref_dot(query, query) + out = np.empty(matrix.shape[0], dtype=np.float64) + for row_index, row_f32 in enumerate(matrix): + row = row_f32.astype(np.float64) + row_norm_sq = ref_dot(row, row) + if query_norm_sq == 0.0 or row_norm_sq == 0.0: + out[row_index] = 0.0 + else: + out[row_index] = ref_dot(query, row) / (np.sqrt(query_norm_sq) * np.sqrt(row_norm_sq)) + return out # ================================================================================================= diff --git a/native/liblecore/ABI0_SYMBOLS.txt b/native/liblecore/ABI0_SYMBOLS.txt new file mode 100644 index 0000000..76e41d9 --- /dev/null +++ b/native/liblecore/ABI0_SYMBOLS.txt @@ -0,0 +1,51 @@ +lecore_abi_version +lecore_bundle_f32 +lecore_bundle_f64 +lecore_capabilities +lecore_cleanup_f32 +lecore_cleanup_f64 +lecore_config_init_v0 +lecore_context_backend +lecore_context_create +lecore_context_destroy +lecore_context_dimension +lecore_context_profile +lecore_context_scalar_type +lecore_context_scratch_bytes +lecore_context_validation +lecore_cosine_f32 +lecore_cosine_f64 +lecore_cosine_many_f32 +lecore_cosine_many_f64 +lecore_cosine_many_f64_f32 +lecore_dot_f32 +lecore_dot_f64 +lecore_dot_many_f32 +lecore_dot_many_f64 +lecore_format_check_compatibility_v1 +lecore_format_check_v1 +lecore_format_decode_v1 +lecore_format_descriptor_init_v1 +lecore_format_encode_v1 +lecore_format_encoded_size_v1 +lecore_hrr_bind_batch_f32 +lecore_hrr_bind_batch_f64 +lecore_hrr_bind_f32 +lecore_hrr_bind_f64 +lecore_hrr_bind_fixed_f32 +lecore_hrr_bind_fixed_f64 +lecore_hrr_unbind_all_f32 +lecore_hrr_unbind_all_f64 +lecore_hrr_unbind_f32 +lecore_hrr_unbind_f64 +lecore_involution_f32 +lecore_involution_f64 +lecore_isa_version +lecore_normalize_f32 +lecore_normalize_f64 +lecore_permute_f32 +lecore_permute_f64 +lecore_status_string +lecore_validate_f32 +lecore_validate_f64 +lecore_version_string diff --git a/native/liblecore/BENCHMARKS.md b/native/liblecore/BENCHMARKS.md new file mode 100644 index 0000000..1915e1c --- /dev/null +++ b/native/liblecore/BENCHMARKS.md @@ -0,0 +1,185 @@ +# liblecore benchmark notes + +These measurements characterize the ABI-0 preview; they do not define the API or change backend dispatch. In +particular, `LECORE_BACKEND_AUTO` remains mapped to the direct reference backend until representative adopters +provide replayable workloads and a cross-platform threshold policy is approved. + +## First adapter-backed baseline (historical) + +The table below records bind latency from a release build on an Apple M-series arm64 host running macOS 26.3.1, +Apple Clang 17, Python 3.14.5, and NumPy 2.4.4. Values are medians of five samples, in microseconds per call. The +original benchmark reused contexts and output buffers but timed the checked Python `Context.bind` adapter as well +as the foreign-function call. These values remain useful historical evidence, but the CI gate described below +times the public C ABI directly and must not be compared with this table. Inputs are deterministic unit vectors. + +### f64 bind + +| Dimension | Direct | Radix-2 | Radix-2 speedup | NumPy FFT | Max radix-2 error | +| ---: | ---: | ---: | ---: | ---: | ---: | +| 8 | 5.87 | 5.04 | 1.16x | 6.55 | 1.11e-16 | +| 16 | 5.11 | 5.06 | 1.01x | 6.33 | 2.22e-16 | +| 32 | 5.15 | 5.27 | 0.98x | 6.65 | 1.11e-16 | +| 64 | 6.28 | 5.39 | 1.16x | 6.76 | 2.22e-16 | +| 128 | 12.12 | 6.20 | 1.95x | 7.25 | 1.53e-16 | +| 256 | 37.37 | 7.64 | 4.89x | 7.94 | 1.67e-16 | +| 512 | 159.65 | 11.11 | 14.36x | 10.04 | 1.39e-16 | +| 1024 | 678.19 | 18.53 | 36.60x | 13.85 | 1.67e-16 | + +### f32 bind + +| Dimension | Direct | Radix-2 | Radix-2 speedup | Max radix-2 error | +| ---: | ---: | ---: | ---: | ---: | +| 8 | 4.95 | 4.92 | 1.01x | 5.96e-08 | +| 16 | 4.86 | 4.93 | 0.99x | 1.19e-07 | +| 32 | 5.01 | 5.24 | 0.95x | 8.94e-08 | +| 64 | 6.26 | 5.37 | 1.17x | 8.20e-08 | +| 128 | 12.01 | 6.10 | 1.97x | 7.45e-08 | +| 256 | 37.47 | 7.66 | 4.89x | 1.19e-07 | +| 512 | 158.08 | 11.20 | 14.12x | 8.94e-08 | +| 1024 | 673.44 | 18.41 | 36.58x | 1.04e-07 | + +At dimension 1024, reported caller scratch is 8 KiB for direct f64 and 32 KiB for radix-2 f64; f32 uses 4 KiB +and 16 KiB respectively. Context creation was about 3.3 microseconds for direct and 8.3 microseconds for radix-2 +at that dimension. Radix-2's plan tables are context-owned and are not included in the scratch-byte query. + +The portable radix-2 implementation clearly improves on the quadratic oracle at dimensions 128 and above on this +host. Optimized NumPy FFT was still faster at dimensions 512 and 1024, so this is evidence for a portable native +backend—not a claim that it replaces platform-tuned FFT libraries. + +## Ordered-kernel optimization review + +The optimization review compared this implementation with pre-optimization commit `bd8da06`. Both Apple Clang 17 +Release libraries were loaded by one process, given identical deterministic inputs and iteration counts, and timed +as ten alternating candidate/base pairs. Values below are median microseconds per pre-resolved public-ABI call; +improvement is the reciprocal of the median paired candidate/base ratio. + +| Profile | Dimension | Direct before | Direct now | Direct improvement | Radix-2 before | Radix-2 now | Radix-2 improvement | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | +| f64 | 256 | 34.05 | 6.86 | 4.97x | 3.26 | 3.18 | 1.02x | +| f64 | 512 | 161.57 | 28.96 | 5.60x | 6.83 | 6.61 | 1.03x | +| f64 | 1024 | 712.05 | 113.73 | 6.26x | 14.76 | 14.18 | 1.04x | +| f32 | 256 | 34.66 | 4.18 | 8.29x | 3.33 | 3.13 | 1.06x | +| f32 | 512 | 161.48 | 14.01 | 11.50x | 6.97 | 6.46 | 1.08x | +| f32 | 1024 | 708.79 | 62.52 | 11.36x | 14.31 | 13.43 | 1.07x | + +The direct result retains the ISA's ascending reduction order; direct conformance remained bit-for-bit exact. +Radix-2 maximum absolute disagreement with direct was `1.665e-16` for f64 and `1.192e-7` for f32 in this run. +These measurements do not change AUTO dispatch. + +## Reproducing + +Build a shared release library, then run: + +```console +python3 benchmarks/bench_liblecore.py \ + --library /absolute/path/to/liblecore \ + --output /tmp/liblecore-benchmark.json +``` + +The default descriptive report captures host and library metadata, setup cost, scratch requirements, iteration +counts, timings, a NumPy reference for f64, and direct-versus-radix numerical error. Its bind timing uses a +pre-resolved public C ABI call; it includes the fixed Python-to-C `ctypes` transition but not `Context.bind` +validation and lock overhead. Pass `--gate` to omit the setup and NumPy metrics and emit the stricter CI-gate +metadata. + +## CI regression gate + +The `Linux Release performance regression` job builds the shared library with GCC in Release mode on GitHub's +Ubuntu 24.04 runner. It pins Python 3.12 and NumPy 2.4.4, then measures f64 and f32 bind at dimensions 256, 512, +and 1024. The timed callable is resolved once per cell to the exported `lecore_hrr_bind_f64` or +`lecore_hrr_bind_f32` symbol, native context handle, and input/output pointers. Context setup, Python adapter +validation, status formatting, scratch queries, and NumPy are outside the timed region. CI passes `--gate`, so +ungated setup and application-level NumPy metrics are not collected. + +Before collecting samples, the harness runs three equal-count pilots per operation and calibrates from the fastest +observed per-call result toward a 30 millisecond sample target with 25% headroom. A paired run considers both +candidate and base pilots and selects one shared iteration count. If any collected candidate or base sample is +still shorter than the target, the entire batch is discarded, the shared count is increased from the shortest +sample, and both sides are measured again. Acquisition is limited to three attempts and 1,000,000 calls per +sample; failure to reach the target aborts without writing an undersized final report. This is a symmetric +measurement-integrity retry, not a second chance for a candidate that failed the regression policy. + +The final report stores the raw elapsed nanoseconds for every repeat and the iteration count used. The checker +independently recomputes each median latency and rejects a report if any of the ten direct or radix-2 samples is +shorter than 10 milliseconds. This protects the gate when an implementation gets substantially faster or a pilot +runs under transient contention; fixed iteration counts or a single slow pilot would silently turn faster final +cells back into timer-noise measurements. + +On pull requests, the job checks out the exact base commit into a separate directory and builds it with the same +compiler and options as the candidate. One benchmark process loads both libraries. Each candidate sample and its +base sample share an index and pair identifier, use the same inputs and iteration count, and alternate which runs +first. The checker computes candidate/base slowdown for every aligned repeat and gates the median of those paired +ratios—not the ratio of two independently aggregated medians. The committed policy permits at most a 1.35x +median paired slowdown for both direct and radix-2 in every guarded profile/dimension. The workflow performs one +reported measurement and one enforcement pass; it does not give a failing candidate an asymmetric policy retry. + +The report schema marks the measurement layer as `public-c-abi`, the mode as `pre-resolved-bind`, the metrics +scope as `gate`, and the comparison as either `paired-interleaved` or bootstrap `single`. The checker requires +those values and also verifies that: + +- every expected profile and dimension is present and finite; +- every candidate and base timing summary agrees with its raw repeat data; +- paired reports have the same pair identifier, repeat count, and per-backend iteration counts; +- radix-2 remains numerically close to the direct oracle within `1e-12` for f64 and `1e-4` for f32; +- the report has the expected schema, ABI, ISA, and capabilities; and +- a real `LECORE_BACKEND_AUTO` context resolves to direct for every measured profile and dimension. + +The policy intentionally does not require a fixed radix-2/direct speedup. Improving the direct backend can reduce +that ratio even when both backends improve, so candidate/base paired ratios are the regression signal. The +checker supports optional per-dimension speedup floors if a future backend contract needs them. + +This pull request introduces liblecore to a base branch that does not yet contain `native/liblecore`. In that one +bootstrap case, no valid base artifact can be built, so CI deliberately runs the candidate integrity, numerical, +AUTO, and measurement-duration checks without `--baseline`; it makes no candidate/base performance claim. Once +liblecore is present on the base branch, pull requests +automatically take the candidate-versus-base path. Pushes and manual runs also use the invariant-only path because +they have no pull-request base SHA. + +The checker writes median public-ABI latency, optional within-candidate speedup, numerical error, and median paired +slowdown to the GitHub job summary. The raw candidate and, when available, base JSON reports are uploaded together +as a 30-day artifact even when the policy +step fails, so a regression can be inspected without rerunning CI. The workflow uses repository read permission +only and does not post or update pull-request comments. + +To reproduce the CI measurement and gate locally from the repository root: + +```console +cmake -S native/liblecore -B build/liblecore-performance \ + -DLECORE_BUILD_SHARED=ON \ + -DLECORE_BUILD_TESTS=OFF \ + -DLECORE_BUILD_EXAMPLES=OFF \ + -DLECORE_ENABLE_FORMAT=ON \ + -DLECORE_ENABLE_RADIX2=ON \ + -DLECORE_WARNINGS_AS_ERRORS=ON \ + -DCMAKE_BUILD_TYPE=Release +cmake --build build/liblecore-performance --parallel 2 +python3 benchmarks/bench_liblecore.py \ + --library /path/to/candidate/liblecore.so \ + --baseline-library /path/to/base/liblecore.so \ + --dimensions 256,512,1024 \ + --profiles both \ + --repeats 10 \ + --target-work 64000000 \ + --sample-target-ms 30 \ + --gate \ + --output build/liblecore-performance/liblecore-performance.json \ + --baseline-output build/liblecore-performance/liblecore-performance-base.json +python3 benchmarks/check_liblecore_performance.py \ + --report build/liblecore-performance/liblecore-performance.json \ + --baseline build/liblecore-performance/liblecore-performance-base.json \ + --policy benchmarks/liblecore_ci_policy.json +``` + +Omit both benchmark-side baseline arguments and checker-side `--baseline` only when bootstrapping against a +revision that has no liblecore artifact. To reproduce the normal pull-request gate, build the base revision in a +separate directory and pass both libraries to the one interleaved benchmark command shown above. + +This gate covers only f32/f64 circular-convolution bind using the direct and portable radix-2 backends on one +hosted Linux configuration. It does not yet cover unbind, cleanup, batch operations, mixed f64-query/f32-corpus +cosine, allocation/setup throughput, concurrency, WebAssembly, other compilers or operating systems, or adopter +workloads. Those require their own stable workloads and policies rather than inferred thresholds from this +microbenchmark. + +Passing the gate is evidence that the two explicit backends retained their expected relationship; it is not a +dispatch decision. `LECORE_BACKEND_AUTO` remains mapped to the direct reference backend. Changing `AUTO` requires +a separate, reviewed policy change supported by measurements from supported platforms and real adopters. diff --git a/native/liblecore/CHANGELOG.md b/native/liblecore/CHANGELOG.md new file mode 100644 index 0000000..59717ac --- /dev/null +++ b/native/liblecore/CHANGELOG.md @@ -0,0 +1,23 @@ +# liblecore changelog + +All notable native-kernel changes are recorded here. Native releases use their own `liblecore-v*` tag namespace and +do not share the Python package's release train. + +## 0.1.0 — unreleased ABI-0 reference preview + +- Added a C11 ABI with opaque contexts, caller-owned vectors, custom allocator hooks, capability queries, and typed + status values. +- Added direct f64 and f32 HRR bind/unbind plus dense, cleanup, batch, and mixed-scoring operations. +- Added an explicitly selected portable radix-2 f64/f32 backend with precomputed plans and allocation-free calls; + automatic selection remains on the direct oracle pending benchmark evidence. +- Added explicit shape-only and finite-input validation modes with pinned zero, non-finite, and tie behavior. +- Added an optional 96-byte little-endian interchange descriptor with CRC-64/ECMA-182 validation. +- Added static/shared CMake builds, installation exports, `pkg-config` metadata, examples, and external C/C++ + consumer tests. +- Added an explicit ABI-0 exported-symbol manifest for release and visibility drift checks. +- Added deterministic Python conformance fixtures, a strict NumPy/`ctypes` adapter, an amalgamated C distribution, + and a reproducible direct-versus-radix benchmark baseline. +- Added bounded Clang libFuzzer targets for the numeric/configuration surface and binary descriptor parser. + +This release intentionally reports ABI `0`. Adopter replay, a benchmark-backed AUTO policy, and stable ABI `1` +remain gated follow-up work. diff --git a/native/liblecore/CMakeLists.txt b/native/liblecore/CMakeLists.txt new file mode 100644 index 0000000..0c4f8fa --- /dev/null +++ b/native/liblecore/CMakeLists.txt @@ -0,0 +1,410 @@ +cmake_minimum_required(VERSION 3.21) + +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" LECORE_VERSION LIMIT_COUNT 1) +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/ISA_VERSION" LECORE_ISA_VERSION LIMIT_COUNT 1) +if(NOT LECORE_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[0-9]+$") + message(FATAL_ERROR "VERSION must contain one MAJOR.MINOR.PATCH version") +endif() +if(NOT LECORE_ISA_VERSION MATCHES "^[0-9]+$") + message(FATAL_ERROR "ISA_VERSION must contain one non-negative integer") +endif() + +project(liblecore VERSION "${LECORE_VERSION}" DESCRIPTION "Portable leCore vector algebra kernel" LANGUAGES C) + +include(CMakePackageConfigHelpers) +include(CheckCSourceRuns) +include(GNUInstallDirs) + +option(LECORE_ENABLE_FORMAT "Build the optional interchange descriptor codec" ON) +option(LECORE_ENABLE_RADIX2 "Build the portable radix-2 HRR backend" ON) +option(LECORE_BUILD_SHARED "Build liblecore as a shared library instead of a static library" OFF) +option(LECORE_BUILD_TESTS "Build liblecore's tests" "${PROJECT_IS_TOP_LEVEL}") +option(LECORE_BUILD_EXAMPLES "Build liblecore's examples" "${PROJECT_IS_TOP_LEVEL}") +option(LECORE_BUILD_AMALGAMATION "Regenerate the checked-in amalgamation during the default build" OFF) +option(LECORE_BUILD_FUZZERS "Build bounded libFuzzer public-API targets" OFF) +option(LECORE_WARNINGS_AS_ERRORS "Treat warnings from liblecore sources as errors" OFF) +option(LECORE_ENABLE_SANITIZERS "Enable AddressSanitizer and UndefinedBehaviorSanitizer" OFF) +option(LECORE_ASSUME_IEC_60559 + "Assert IEC 60559 NaN/Inf/evaluation behavior when cross-compiling" OFF) + +if(LECORE_BUILD_FUZZERS AND NOT LECORE_ENABLE_SANITIZERS) + message(FATAL_ERROR + "LECORE_BUILD_FUZZERS requires LECORE_ENABLE_SANITIZERS=ON") +endif() +if(LECORE_BUILD_FUZZERS AND NOT CMAKE_C_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "LECORE_BUILD_FUZZERS currently requires Clang") +endif() + +if(CMAKE_CROSSCOMPILING) + if(NOT LECORE_ASSUME_IEC_60559) + message(FATAL_ERROR + "Cross builds must explicitly set LECORE_ASSUME_IEC_60559=ON after validating the target") + endif() +else() + set(LECORE_SAVED_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") + set(LECORE_SAVED_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") + if(MSVC) + string(APPEND CMAKE_REQUIRED_FLAGS " /fp:strict") + else() + string(APPEND CMAKE_REQUIRED_FLAGS + " -fno-fast-math -ffp-contract=off") + if(NOT APPLE) + list(APPEND CMAKE_REQUIRED_LIBRARIES m) + endif() + endif() + check_c_source_runs([=[ + #include + #include + #include + + int main(void) { + volatile double one = 1.0; + volatile double zero = 0.0; + volatile float large = 16777216.0F; + double infinity = one / zero; + double not_a_number = zero / zero; + float rounded = large + 1.0F; + + if (FLT_EVAL_METHOD != 0 || fegetround() != FE_TONEAREST) { + return 1; + } + if (!isinf(infinity) || signbit(infinity) || + !isinf(-infinity) || !signbit(-infinity)) { + return 2; + } + if (!isnan(not_a_number) || !isnan(not_a_number + one)) { + return 3; + } + if (rounded != 16777216.0F) { + return 4; + } + return 0; + } + ]=] LECORE_IEC_60559_RUNTIME_OK) + set(CMAKE_REQUIRED_FLAGS "${LECORE_SAVED_REQUIRED_FLAGS}") + set(CMAKE_REQUIRED_LIBRARIES "${LECORE_SAVED_REQUIRED_LIBRARIES}") + unset(LECORE_SAVED_REQUIRED_FLAGS) + unset(LECORE_SAVED_REQUIRED_LIBRARIES) + if(NOT LECORE_IEC_60559_RUNTIME_OK) + message(FATAL_ERROR + "The compiler/runtime failed liblecore's IEC 60559 and evaluation probe") + endif() +endif() + +set(LECORE_ABI_VERSION 0) +if(LECORE_ABI_VERSION EQUAL 0) + # ABI-0 previews may break at a native-package minor boundary. Keep patch + # releases runtime-compatible while preventing a later 0.x minor from + # satisfying the dynamic loader identity of this preview line. + set(LECORE_SOVERSION + "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") +else() + # Stable releases identify their dynamic ABI with the public ABI major. + set(LECORE_SOVERSION "${LECORE_ABI_VERSION}") +endif() +get_filename_component(LECORE_REPOSITORY_ROOT + "${CMAKE_CURRENT_SOURCE_DIR}/../.." ABSOLUTE) +set(LECORE_AMALGAMATION_GENERATOR + "${LECORE_REPOSITORY_ROOT}/tools/generate_liblecore_amalgamation.py") +set(LECORE_AMALGAMATION_HEADER + "${CMAKE_CURRENT_SOURCE_DIR}/amalgamation/lecore.h") +set(LECORE_AMALGAMATION_SOURCE + "${CMAKE_CURRENT_SOURCE_DIR}/amalgamation/lecore.c") + +find_package(Python3 COMPONENTS Interpreter QUIET) +if(EXISTS "${LECORE_AMALGAMATION_GENERATOR}" AND Python3_Interpreter_FOUND) + if(LECORE_BUILD_AMALGAMATION) + add_custom_target(liblecore_amalgamation_generate ALL + COMMAND "${Python3_EXECUTABLE}" "${LECORE_AMALGAMATION_GENERATOR}" + WORKING_DIRECTORY "${LECORE_REPOSITORY_ROOT}" + COMMENT "Regenerating the liblecore amalgamation" + VERBATIM + ) + else() + add_custom_target(liblecore_amalgamation_generate + COMMAND "${Python3_EXECUTABLE}" "${LECORE_AMALGAMATION_GENERATOR}" + WORKING_DIRECTORY "${LECORE_REPOSITORY_ROOT}" + COMMENT "Regenerating the liblecore amalgamation" + VERBATIM + ) + endif() + add_custom_target(liblecore_amalgamation_check + COMMAND "${Python3_EXECUTABLE}" + "${LECORE_AMALGAMATION_GENERATOR}" --check + WORKING_DIRECTORY "${LECORE_REPOSITORY_ROOT}" + COMMENT "Checking the liblecore amalgamation for source drift" + VERBATIM + ) +elseif(LECORE_BUILD_AMALGAMATION) + message(FATAL_ERROR + "LECORE_BUILD_AMALGAMATION requires Python 3 and the repository generator") +endif() + +foreach(amalgamation_file IN ITEMS + "${LECORE_AMALGAMATION_HEADER}" "${LECORE_AMALGAMATION_SOURCE}") + if(NOT EXISTS "${amalgamation_file}") + message(FATAL_ERROR + "Missing checked-in amalgamation file: ${amalgamation_file}") + endif() +endforeach() + +set(LECORE_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/cleanup_f32.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/cleanup_f64.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/context.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/hrr_direct_f32.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/hrr_direct_f64.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/status.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/vector_f32.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/vector_f64.c" +) +set(LECORE_FORMAT_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/crc64.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/format.c" +) +set(LECORE_RADIX2_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/hrr_radix2_f32.c" + "${CMAKE_CURRENT_SOURCE_DIR}/src/hrr_radix2_f64.c" +) +if(LECORE_ENABLE_FORMAT) + foreach(format_source IN LISTS LECORE_FORMAT_SOURCES) + if(NOT EXISTS "${format_source}") + message(FATAL_ERROR "LECORE_ENABLE_FORMAT requires ${format_source}") + endif() + endforeach() + list(APPEND LECORE_SOURCES ${LECORE_FORMAT_SOURCES}) +endif() +if(LECORE_ENABLE_RADIX2) + list(APPEND LECORE_SOURCES ${LECORE_RADIX2_SOURCES}) +endif() +foreach(lecore_source IN LISTS LECORE_SOURCES) + if(NOT EXISTS "${lecore_source}") + message(FATAL_ERROR "A required liblecore source is missing: ${lecore_source}") + endif() +endforeach() + +if(LECORE_BUILD_SHARED) + set(LECORE_LIBRARY_TYPE SHARED) +else() + set(LECORE_LIBRARY_TYPE STATIC) +endif() +add_library(lecore ${LECORE_LIBRARY_TYPE} ${LECORE_SOURCES}) +add_library(lecore::lecore ALIAS lecore) + +target_compile_features(lecore PUBLIC c_std_11) +set_target_properties(lecore PROPERTIES + C_EXTENSIONS OFF + C_VISIBILITY_PRESET hidden + EXPORT_NAME lecore + OUTPUT_NAME lecore + POSITION_INDEPENDENT_CODE ON + SOVERSION "${LECORE_SOVERSION}" + VERSION "${PROJECT_VERSION}" + VISIBILITY_INLINES_HIDDEN YES +) +target_include_directories(lecore + PUBLIC + "$" + "$" + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" +) +target_compile_definitions(lecore + PUBLIC + LECORE_ENABLE_FORMAT=$ + LECORE_ENABLE_RADIX2=$ + LECORE_SHARED_LIBRARY=$ + $<$:LECORE_SHARED=1> + PRIVATE + LECORE_BUILDING_LIBRARY=1 + LECORE_ABI_VERSION_VALUE=${LECORE_ABI_VERSION} + LECORE_ISA_VERSION_VALUE=${LECORE_ISA_VERSION} + LECORE_VERSION_STRING_VALUE="${LECORE_VERSION}" +) + +if(MSVC) + target_compile_options(lecore PRIVATE /W4 /permissive- /fp:strict) + if(LECORE_WARNINGS_AS_ERRORS) + target_compile_options(lecore PRIVATE /WX) + endif() +else() + target_compile_options(lecore PRIVATE + -Wall -Wextra -Wpedantic -Wconversion -Wshadow + -fno-fast-math -ffp-contract=off + ) + if(LECORE_WARNINGS_AS_ERRORS) + target_compile_options(lecore PRIVATE -Werror) + endif() + if(NOT APPLE) + target_link_libraries(lecore PUBLIC m) + endif() +endif() + +if(LECORE_ENABLE_SANITIZERS) + if(MSVC) + message(FATAL_ERROR "LECORE_ENABLE_SANITIZERS currently requires Clang or GCC") + endif() + target_compile_options(lecore PUBLIC + "$" + "$" + ) + target_link_options(lecore PUBLIC + "$" + "$" + ) +endif() + +configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/lecoreConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/lecoreConfig.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/lecore" +) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/lecoreConfigVersion.cmake" + VERSION "${LECORE_VERSION}" + # ABI-0 may break between 0.x minors; do not let 0.2 satisfy a 0.1 request. + COMPATIBILITY SameMinorVersion +) + +if(MSVC) + set(LECORE_PC_PUBLIC_LIBS "") + set(LECORE_PC_PRIVATE_LIBS "") +elseif(LECORE_BUILD_SHARED) + set(LECORE_PC_PUBLIC_LIBS "") + set(LECORE_PC_PRIVATE_LIBS "-lm") +else() + set(LECORE_PC_PUBLIC_LIBS "-lm") + set(LECORE_PC_PRIVATE_LIBS "") +endif() +if(LECORE_ENABLE_FORMAT) + set(LECORE_PC_CFLAGS "-DLECORE_ENABLE_FORMAT=1") +else() + set(LECORE_PC_CFLAGS "-DLECORE_ENABLE_FORMAT=0") +endif() +if(LECORE_ENABLE_RADIX2) + string(APPEND LECORE_PC_CFLAGS " -DLECORE_ENABLE_RADIX2=1") +else() + string(APPEND LECORE_PC_CFLAGS " -DLECORE_ENABLE_RADIX2=0") +endif() +if(LECORE_BUILD_SHARED) + string(APPEND LECORE_PC_CFLAGS " -DLECORE_SHARED_LIBRARY=1 -DLECORE_SHARED=1") +else() + string(APPEND LECORE_PC_CFLAGS " -DLECORE_SHARED_LIBRARY=0") +endif() +if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(LECORE_PC_PREFIX "${CMAKE_INSTALL_PREFIX}") + set(LECORE_PC_LIBDIR "${CMAKE_INSTALL_LIBDIR}") +else() + file(RELATIVE_PATH LECORE_PC_PREFIX_RELATIVE + "/${CMAKE_INSTALL_LIBDIR}/pkgconfig" "/") + set(LECORE_PC_PREFIX "\${pcfiledir}/${LECORE_PC_PREFIX_RELATIVE}") + set(LECORE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") +endif() +if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(LECORE_PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") +else() + set(LECORE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") +endif() +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/pkgconfig/liblecore.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/liblecore.pc" + @ONLY +) + +install(TARGETS lecore + EXPORT liblecoreTargets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/lecore/lecore.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/lecore" +) +if(LECORE_ENABLE_FORMAT) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/lecore/lecore_format.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/lecore" + ) +endif() +install(EXPORT liblecoreTargets + FILE lecoreTargets.cmake + NAMESPACE lecore:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/lecore" +) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/lecoreConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/lecoreConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/lecore" +) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liblecore.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" +) +install(FILES VERSION ISA_VERSION ABI0_SYMBOLS.txt + DESTINATION "${CMAKE_INSTALL_DATADIR}/liblecore") +install(FILES + "${LECORE_AMALGAMATION_HEADER}" + "${LECORE_AMALGAMATION_SOURCE}" + DESTINATION "${CMAKE_INSTALL_DATADIR}/liblecore/source/amalgamation" +) +set(LECORE_DOCUMENTATION_FILES) +foreach(documentation_file IN ITEMS README.md CHANGELOG.md PROVENANCE.md BENCHMARKS.md) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${documentation_file}") + list(APPEND LECORE_DOCUMENTATION_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/${documentation_file}") + endif() +endforeach() +if(LECORE_DOCUMENTATION_FILES) + install(FILES ${LECORE_DOCUMENTATION_FILES} DESTINATION "${CMAKE_INSTALL_DOCDIR}") +endif() +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../LICENSE") + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../LICENSE" + DESTINATION "${CMAKE_INSTALL_DATADIR}/licenses/liblecore" + ) +endif() + +if(LECORE_BUILD_EXAMPLES OR LECORE_BUILD_TESTS) + enable_language(CXX) +endif() + +if(LECORE_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +if(LECORE_BUILD_TESTS) + include(CTest) + add_subdirectory(tests) + set(LECORE_AMALGAMATION_CHECK_SCRIPT + "${LECORE_REPOSITORY_ROOT}/tests/native/check_liblecore_amalgamation.py") + if(Python3_Interpreter_FOUND AND EXISTS "${LECORE_AMALGAMATION_CHECK_SCRIPT}" + AND NOT CMAKE_CROSSCOMPILING AND NOT MSVC) + add_test(NAME liblecore_amalgamation_standalone + COMMAND "${CMAKE_COMMAND}" -E env + "CC=${CMAKE_C_COMPILER}" + "CXX=${CMAKE_CXX_COMPILER}" + "${Python3_EXECUTABLE}" + "${LECORE_AMALGAMATION_CHECK_SCRIPT}" + ) + endif() + set(LECORE_SYMBOL_CHECK_SCRIPT + "${LECORE_REPOSITORY_ROOT}/tests/native/check_liblecore_symbols.py") + if(LECORE_BUILD_SHARED AND UNIX AND NOT WIN32 AND NOT CMAKE_CROSSCOMPILING + AND Python3_Interpreter_FOUND AND CMAKE_NM + AND EXISTS "${LECORE_SYMBOL_CHECK_SCRIPT}") + if(LECORE_ENABLE_FORMAT) + set(LECORE_SYMBOL_CHECK_FORMAT on) + else() + set(LECORE_SYMBOL_CHECK_FORMAT off) + endif() + add_test(NAME liblecore_abi0_symbols + COMMAND "${Python3_EXECUTABLE}" + "${LECORE_SYMBOL_CHECK_SCRIPT}" + --library "$" + --nm "${CMAKE_NM}" + --format "${LECORE_SYMBOL_CHECK_FORMAT}" + ) + endif() +endif() + +if(LECORE_BUILD_FUZZERS) + add_subdirectory(tests/fuzz) +endif() diff --git a/native/liblecore/ISA_VERSION b/native/liblecore/ISA_VERSION new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/native/liblecore/ISA_VERSION @@ -0,0 +1 @@ +1 diff --git a/native/liblecore/PROVENANCE.md b/native/liblecore/PROVENANCE.md new file mode 100644 index 0000000..909a071 --- /dev/null +++ b/native/liblecore/PROVENANCE.md @@ -0,0 +1,27 @@ +# liblecore native provenance + +*Status: clean-room ledger for the ABI-0 preview.* + +All canonical files under `native/liblecore` are newly written for this MIT-licensed repository from the public +semantic descriptions in [`docs/ISA.md`](../../docs/ISA.md), the repository's NumPy definitional reference, and the +requirements in [`PRD.md`](../../PRD.md) and [`ENG.md`](../../ENG.md). No C implementation from another project was +copied. + +| Material | Role | Provenance and license decision | +|---|---|---| +| `include/lecore/*.h`, `src/*.c`, CMake/package files, tests, and examples | Canonical liblecore implementation | Independently written in this repository; covered by the repository MIT license. | +| `docs/ISA.md` and `holographic/misc/holographic_reference.py` | Normative semantics and differential oracle | Existing leCore sources in this MIT repository. | +| CRC-64/ECMA-182 parameters | Interchange checksum specification | Public algorithm parameters; implementation written directly for liblecore. | +| C and C++ standard library interfaces | Hosted allocation, math, and portability substrate | Platform interfaces; no vendored source. | + +Nearby implementations found during workspace analysis are compatibility targets or research evidence only: + +- Signal's holographic kernel is AGPL-licensed and may be used as an adopter/conformance oracle, but its source is + not a source for this MIT implementation. +- `leos-c` and `asix` use normalized or otherwise different HRR semantics and have incomplete or unclear packaging + provenance; no source was copied. +- Holonet's MAP algebra, NoSQLite and Zero LM text hypervectors, and NSRL's integer associative memory are distinct + profiles, not implementations of this HRR profile. + +Any future imported, generated, or vendored native material must be added to this ledger with its exact origin, +license, modifications, and affected files before release. diff --git a/native/liblecore/README.md b/native/liblecore/README.md new file mode 100644 index 0000000..0daf08b --- /dev/null +++ b/native/liblecore/README.md @@ -0,0 +1,164 @@ +# liblecore + +*Portable C11 kernels for leCore's caller-supplied-vector algebra. Status: ABI-0 preview.* + +`liblecore` makes the small, frozen HRR algebra in leCore usable from C, C++, Rust, WebAssembly, and other hosts +without importing Python. It is a semantic interoperability layer first: the direct backend follows +[`docs/ISA.md`](../../docs/ISA.md) and the definitional Python implementation in +[`holographic_reference.py`](../../holographic/misc/holographic_reference.py). It does not replace `UnifiedMind`, +generate atoms, own a vocabulary, or choose application policy. + +The current preview provides: + +- raw circular-convolution bind and involution-based unbind for f64 and f32; +- a dependency-free radix-2 FFT backend for explicitly selected power-of-two dimensions; +- normalize, dot, cosine, bundle, involution, permutation, and deterministic cleanup; +- pairwise, fixed-role, and unbind-all batch operations; +- ordered mixed f64-query/f32-corpus cosine scoring for Rust adopters; +- caller-selected shape-only or finite-input validation; +- a fixed 96-byte little-endian descriptor and CRC64 codec; and +- static/shared CMake builds, installable CMake and `pkg-config` packages, C/C++ consumer tests, and no runtime + dependencies beyond the platform C math library. + +Native version, ABI version, the liblecore ISA subset, semantic profiles, and interchange format are separate +contracts. The `0.x` series reports ABI `0`; its names and layouts may change before adopter replay gates justify +freezing ABI `1`. On platforms with versioned shared-library identities, ABI-0 builds therefore use the native +package's `major.minor`: all `0.1.x` releases use preview SONAME/install-name `0.1`, and an ABI-breaking preview must +bump the package minor and receive a new identity. Patch releases within one preview minor remain ABI-compatible. +Starting with ABI `1`, the public ABI major is the SONAME/install-name; every stable ABI break increments both. + +## Build and test + +An out-of-tree build keeps generated files away from the source checkout: + +```sh +cmake -S native/liblecore -B /tmp/liblecore-build \ + -DLECORE_BUILD_TESTS=ON \ + -DLECORE_WARNINGS_AS_ERRORS=ON +cmake --build /tmp/liblecore-build --parallel +ctest --test-dir /tmp/liblecore-build --output-on-failure +``` + +To install to a project-local prefix: + +```sh +cmake --install /tmp/liblecore-build --prefix /path/to/prefix +``` + +Installed CMake consumers use `find_package(lecore CONFIG REQUIRED)` and link `lecore::lecore`. Consumers using +`pkg-config` use `pkg-config --cflags --libs liblecore`; the linker spelling is `-llecore`. + +## Single-source amalgamation + +The checked-in [`amalgamation/lecore.h`](amalgamation/lecore.h) and +[`amalgamation/lecore.c`](amalgamation/lecore.c) provide the same ABI without CMake. Both optional components +default on; define `LECORE_ENABLE_FORMAT=0` or `LECORE_ENABLE_RADIX2=0` consistently for the source and consumer to +remove one. The amalgamation's compile-time guards verify binary32/binary64 representation and declared-precision +evaluation. Before distributing a raw or cross-compiled build, the consumer must also validate IEC 60559 NaN/Inf, +round-to-nearest, and runtime evaluation behavior for the target; the CMake and vendored Rust paths perform or +require this gate. A minimal hosted build is: + +```sh +cc -std=c11 -O2 -I native/liblecore/amalgamation \ + app.c native/liblecore/amalgamation/lecore.c -lm +``` + +The files are generated from the canonical headers and sources and must not be edited directly. Regenerate or +verify them from the repository root with: + +```sh +python3 tools/generate_liblecore_amalgamation.py +python3 tools/generate_liblecore_amalgamation.py --check +``` + +Configured builds expose the equivalent `liblecore_amalgamation_generate` and +`liblecore_amalgamation_check` targets. Set `LECORE_BUILD_AMALGAMATION=ON` to regenerate during the default build. +Installation places the pair in `share/liblecore/source/amalgamation`, separate from the normal installed API +headers, to make their source-distribution role explicit. + +## Minimal C example + +```c +#include + +int main(void) { + lecore_config_v0 config; + lecore_context *context = NULL; + const double role[4] = {1.0, 0.0, 0.0, 0.0}; + const double value[4] = {0.0, 1.0, 0.0, 0.0}; + double bound[4]; + + lecore_config_init_v0(&config); + config.dimension = 4; + if (lecore_context_create(&config, &context) != LECORE_OK) { + return 1; + } + if (lecore_hrr_bind_f64(context, role, value, bound) != LECORE_OK) { + lecore_context_destroy(context); + return 1; + } + lecore_context_destroy(context); + return 0; +} +``` + +The context owns only its immutable configuration and scratch space. Vectors remain caller-owned. Calls on a live +context are single-thread-owned; distinct contexts are independent. Destruction may run on another thread after all +calls have quiesced, provided a custom allocator's deallocator is valid there. Every allocation happens during +context creation, and the numeric hot path does not allocate. Functions return `lecore_status` and never print, +abort, or set global error state. + +## Semantics that matter + +- Bind and unbind are raw and unnormalized. +- `permute` follows NumPy `roll`: `out[i] = input[(i - shift) mod dimension]`. +- A zero norm normalizes to the unchanged zero vector and cosine returns positive zero. +- In shape-only mode, non-finite arithmetic propagates; cosine's exact-zero branch takes precedence and cleanup + selects the first NaN score, matching NumPy `argmax`. +- Cleanup visits candidates in ascending order and an exact finite tie selects the lowest index. +- Exact input/output aliasing is supported only where the public header says so. Partial overlap and undocumented + batch overlap are rejected. + +`LECORE_BACKEND_AUTO` currently resolves deterministically to the direct backend. A forced radix-2 context uses the +optimized backend for power-of-two dimensions and returns `LECORE_EUNSUPPORTED` otherwise. AUTO will not select it +until adopter-backed benchmarks earn and freeze a crossover rule; the preview never silently substitutes a backend. + +To measure that regime on a built shared artifact without changing dispatch policy: + +```sh +python benchmarks/bench_liblecore.py --library /path/to/liblecore.so +``` + +## Build options + +| Option | Default | Effect | +|---|---:|---| +| `LECORE_BUILD_SHARED` | `OFF` | Build a shared rather than static library. | +| `LECORE_BUILD_TESTS` | top-level builds | Build native, edge, format, and installed-consumer tests. | +| `LECORE_BUILD_EXAMPLES` | top-level builds | Build minimal C and C++ examples. | +| `LECORE_BUILD_AMALGAMATION` | `OFF` | Regenerate the checked-in single-source distribution while building. | +| `LECORE_BUILD_FUZZERS` | `OFF` | Build Clang libFuzzer targets; requires sanitizers. | +| `LECORE_ENABLE_FORMAT` | `ON` | Include the descriptor/CRC codec and install its header. | +| `LECORE_ENABLE_RADIX2` | `ON` | Include the portable power-of-two FFT backend. | +| `LECORE_WARNINGS_AS_ERRORS` | `OFF` | Promote liblecore source warnings to errors. | +| `LECORE_ENABLE_SANITIZERS` | `OFF` | Enable AddressSanitizer and UndefinedBehaviorSanitizer on Clang/GCC. | +| `LECORE_ASSUME_IEC_60559` | `OFF` | Required assertion for cross builds after target floating-point validation. | + +The public-API and format fuzz targets are intentionally opt-in. A bounded local smoke run is: + +```sh +cmake -S native/liblecore -B /tmp/liblecore-fuzz \ + -DCMAKE_C_COMPILER=clang \ + -DLECORE_BUILD_TESTS=OFF \ + -DLECORE_BUILD_EXAMPLES=OFF \ + -DLECORE_ENABLE_SANITIZERS=ON \ + -DLECORE_BUILD_FUZZERS=ON +cmake --build /tmp/liblecore-fuzz --parallel +/tmp/liblecore-fuzz/tests/fuzz/liblecore_fuzz_public_api \ + -runs=2000 native/liblecore/tests/fuzz/corpus/public_api +/tmp/liblecore-fuzz/tests/fuzz/liblecore_fuzz_format \ + -runs=2000 native/liblecore/tests/fuzz/corpus/format +``` + +The product requirements, release gates, adopter strategy, and dependency-ordered backlog are in +[`PRD.md`](../../PRD.md) and [`ENG.md`](../../ENG.md). diff --git a/native/liblecore/VERSION b/native/liblecore/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/native/liblecore/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/native/liblecore/amalgamation/lecore.c b/native/liblecore/amalgamation/lecore.c new file mode 100644 index 0000000..7dae006 --- /dev/null +++ b/native/liblecore/amalgamation/lecore.c @@ -0,0 +1,4161 @@ +/* + * SPDX-License-Identifier: MIT + * + * liblecore 0.1.0 amalgamation (ABI 0, ISA 1) + * Generated by tools/generate_liblecore_amalgamation.py; DO NOT EDIT. + * Canonical-source SHA-256: 04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269 + * + * This is a mechanical distribution of the clean-room canonical sources. + * Provenance details live in native/liblecore/PROVENANCE.md. + * Generated from: + * - LICENSE + * - native/liblecore/VERSION + * - native/liblecore/ISA_VERSION + * - native/liblecore/PROVENANCE.md + * - native/liblecore/include/lecore/lecore.h + * - native/liblecore/include/lecore/lecore_format.h + * - native/liblecore/src/internal/lecore_internal.h + * - native/liblecore/src/internal/format_internal.h + * - native/liblecore/src/context.c + * - native/liblecore/src/status.c + * - native/liblecore/src/vector_f32.c + * - native/liblecore/src/vector_f64.c + * - native/liblecore/src/cleanup_f32.c + * - native/liblecore/src/cleanup_f64.c + * - native/liblecore/src/hrr_direct_f32.c + * - native/liblecore/src/hrr_direct_f64.c + * - native/liblecore/src/hrr_radix2_f32.c + * - native/liblecore/src/hrr_radix2_f64.c + * - native/liblecore/src/crc64.c + * - native/liblecore/src/format.c + * + * MIT License + * + * Copyright (c) 2026 AnOversizedMooseWithSocks + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LECORE_BUILDING_LIBRARY +# define LECORE_BUILDING_LIBRARY 1 +#endif +#include "lecore.h" + +#line 1 "native/liblecore/src/internal/lecore_internal.h" +#ifndef LECORE_INTERNAL_H +#define LECORE_INTERNAL_H + + + +#include +#include +#include + +#if CHAR_BIT != 8 +# error "liblecore profiles require 8-bit bytes" +#endif + +#if FLT_RADIX != 2 +# error "liblecore profiles require radix-2 floating point" +#endif + +#if FLT_MANT_DIG != 24 || FLT_MIN_EXP != -125 || FLT_MAX_EXP != 128 +# error "liblecore HRR_F32_V1 requires IEEE-compatible binary32" +#endif + +#if DBL_MANT_DIG != 53 || DBL_MIN_EXP != -1021 || DBL_MAX_EXP != 1024 +# error "liblecore HRR_F64_V1 requires IEEE-compatible binary64" +#endif + +#if FLT_EVAL_METHOD != 0 +# error "liblecore profiles require operations evaluated in their declared precision" +#endif + +#ifndef LECORE_ENABLE_RADIX2 +# define LECORE_ENABLE_RADIX2 1 +#endif + +#if defined(__cplusplus) +static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#else +_Static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +_Static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#endif + +#define LECORE_INTERNAL_CONTEXT_MAGIC UINT64_C(0x6c65636f72653030) + +/* Internal cross-translation-unit calls become file-local in the generated + * single-translation-unit amalgamation, so standalone objects expose only the + * documented ABI. */ +#if defined(LECORE_AMALGAMATION) +# define LECORE_INTERNAL_API static +#else +# define LECORE_INTERNAL_API +#endif + +struct lecore_context { + uint64_t magic; + uint32_t dimension; + lecore_profile profile; + lecore_backend backend; + lecore_validation validation; + lecore_scalar_type scalar_type; + lecore_allocator_v0 allocator; + void *scratch; + size_t scratch_bytes; + uint32_t *radix2_bit_reversal; + size_t radix2_bit_reversal_bytes; + void *radix2_twiddles; + size_t radix2_twiddle_bytes; + size_t context_bytes; + size_t allocation_alignment; +}; + +static inline lecore_status lecore_internal_check_context( + const lecore_context *context, + lecore_profile expected_profile) +{ + if (context == NULL || context->magic != LECORE_INTERNAL_CONTEXT_MAGIC) { + return LECORE_EINVAL; + } + if (context->profile != expected_profile) { + return LECORE_EPROFILE; + } + if (context->backend != LECORE_BACKEND_DIRECT && + context->backend != LECORE_BACKEND_RADIX2) { + return LECORE_EBACKEND; + } + return LECORE_OK; +} + +#if LECORE_ENABLE_RADIX2 +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride); +#endif + +static inline int lecore_internal_ranges_overlap( + const void *left, + size_t left_bytes, + const void *right, + size_t right_bytes) +{ + uintptr_t left_address; + uintptr_t right_address; + + if (left_bytes == 0 || right_bytes == 0) { + return 0; + } + + left_address = (uintptr_t)left; + right_address = (uintptr_t)right; + if (left_address <= right_address) { + return (right_address - left_address) < left_bytes; + } + return (left_address - right_address) < right_bytes; +} + +static inline lecore_status lecore_internal_check_vector_alias( + const void *input, + const void *output, + size_t vector_bytes, + int allow_exact_alias) +{ + if (!lecore_internal_ranges_overlap( + input, vector_bytes, output, vector_bytes)) { + return LECORE_OK; + } + if (allow_exact_alias && input == output) { + return LECORE_OK; + } + return LECORE_EINVAL; +} + +static inline lecore_status lecore_internal_matrix_span( + uint32_t dimension, + size_t row_count, + size_t row_stride, + size_t scalar_bytes, + size_t *out_span_bytes) +{ + size_t last_row; + size_t span_elements; + + if (out_span_bytes == NULL) { + return LECORE_EINVAL; + } + *out_span_bytes = 0; + if (row_count == 0) { + return LECORE_EINVAL; + } + if (row_stride < (size_t)dimension) { + return LECORE_EDIM; + } + if (row_count - 1 > SIZE_MAX / row_stride) { + return LECORE_EOVERFLOW; + } + last_row = (row_count - 1) * row_stride; + if (last_row > SIZE_MAX - (size_t)dimension) { + return LECORE_EOVERFLOW; + } + span_elements = last_row + (size_t)dimension; + if (span_elements > SIZE_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + *out_span_bytes = span_elements * scalar_bytes; + return LECORE_OK; +} + +#endif /* LECORE_INTERNAL_H */ + +#if LECORE_ENABLE_FORMAT + +#line 1 "native/liblecore/src/internal/format_internal.h" +#ifndef LECORE_INTERNAL_FORMAT_INTERNAL_H +#define LECORE_INTERNAL_FORMAT_INTERNAL_H + +#include +#include + +#ifndef LECORE_INTERNAL_API +# if defined(LECORE_AMALGAMATION) +# define LECORE_INTERNAL_API static +# else +# define LECORE_INTERNAL_API +# endif +#endif + +LECORE_INTERNAL_API uint64_t lecore_format_crc64_ecma_update( + uint64_t crc, + const uint8_t *data, + size_t data_bytes); + +#endif /* LECORE_INTERNAL_FORMAT_INTERNAL_H */ +#endif /* LECORE_ENABLE_FORMAT */ + +#line 1 "native/liblecore/src/context.c" + + +#include +#include +#include +#include + +#ifndef LECORE_ENABLE_FORMAT +# define LECORE_ENABLE_FORMAT 0 +#endif + +#define LECORE_INTERNAL_TAU 6.2831853071795864769252867665590057683943387987502 + +static void *LECORE_CALL lecore_default_allocate( + void *user, + size_t bytes, + size_t alignment) +{ + (void)user; + (void)alignment; + return malloc(bytes); +} + +static void LECORE_CALL lecore_default_deallocate( + void *user, + void *pointer, + size_t bytes, + size_t alignment) +{ + (void)user; + (void)bytes; + (void)alignment; + free(pointer); +} + +static int lecore_is_power_of_two(uintmax_t value) +{ + return value != 0 && (value & (value - 1)) == 0; +} + +static lecore_status lecore_allocate_block( + const lecore_allocator_v0 *allocator, + size_t bytes, + size_t alignment, + void **out_pointer) +{ + void *pointer; + + if (bytes == 0 || out_pointer == NULL) { + return LECORE_EINVAL; + } + *out_pointer = NULL; + pointer = allocator->allocate(allocator->user, bytes, alignment); + if (pointer == NULL) { + return LECORE_ENOMEM; + } + if (((uintptr_t)pointer % alignment) != 0) { + allocator->deallocate( + allocator->user, pointer, bytes, alignment); + return LECORE_EINVAL; + } + *out_pointer = pointer; + return LECORE_OK; +} + +#if LECORE_ENABLE_RADIX2 +static void lecore_initialize_radix2_plan(lecore_context *context) +{ + uint32_t bit_count = 0; + uint32_t value = context->dimension; + uint32_t index; + + while (value > 1) { + ++bit_count; + value >>= 1; + } + + for (index = 0; index < context->dimension; ++index) { + uint32_t source = index; + uint32_t reversed = 0; + uint32_t bit; + + for (bit = 0; bit < bit_count; ++bit) { + reversed = (reversed << 1) | (source & UINT32_C(1)); + source >>= 1; + } + context->radix2_bit_reversal[index] = reversed; + } + + if (context->dimension == 1) { + return; + } + if (context->profile == LECORE_PROFILE_HRR_F64_V1) { + double *twiddles = (double *)context->radix2_twiddles; + uint32_t twiddle; + + for (twiddle = 0; twiddle < context->dimension / 2; ++twiddle) { + double angle = -LECORE_INTERNAL_TAU * (double)twiddle / + (double)context->dimension; + twiddles[(size_t)twiddle * 2] = cos(angle); + twiddles[(size_t)twiddle * 2 + 1] = sin(angle); + } + } else { + float *twiddles = (float *)context->radix2_twiddles; + uint32_t twiddle; + + for (twiddle = 0; twiddle < context->dimension / 2; ++twiddle) { + double angle = -LECORE_INTERNAL_TAU * (double)twiddle / + (double)context->dimension; + twiddles[(size_t)twiddle * 2] = (float)cos(angle); + twiddles[(size_t)twiddle * 2 + 1] = (float)sin(angle); + } + } +} +#endif + +static int lecore_reserved_is_zero(const lecore_config_v0 *config) +{ + size_t index; + + for (index = 0; index < sizeof(config->reserved) / sizeof(config->reserved[0]); + ++index) { + if (config->reserved[index] != 0) { + return 0; + } + } + return 1; +} + +void LECORE_CALL lecore_config_init_v0(lecore_config_v0 *config) +{ + if (config == NULL) { + return; + } + + memset(config, 0, sizeof(*config)); + config->struct_size = (uint32_t)sizeof(*config); + config->abi_version = LECORE_ABI_VERSION; + config->profile = LECORE_PROFILE_HRR_F64_V1; + config->backend = LECORE_BACKEND_AUTO; + config->validation = LECORE_VALIDATION_SHAPE; + config->allocator.struct_size = (uint32_t)sizeof(config->allocator); +} + +lecore_status LECORE_CALL lecore_context_create( + const lecore_config_v0 *config, + lecore_context **out_context) +{ + lecore_allocator_v0 allocator; + lecore_context *context; + lecore_scalar_type scalar_type; + lecore_backend backend; + lecore_status status; + size_t scalar_bytes; + size_t scratch_bytes; + size_t bit_reversal_bytes = 0; + size_t twiddle_bytes = 0; + const size_t alignment = _Alignof(max_align_t); + void *context_memory = NULL; + void *scratch = NULL; + void *bit_reversal = NULL; + void *twiddles = NULL; + int has_allocate; + int has_deallocate; + + if (out_context == NULL) { + return LECORE_EINVAL; + } + *out_context = NULL; + + if (config == NULL) { + return LECORE_EINVAL; + } + if (config->struct_size != sizeof(*config) || + config->abi_version != LECORE_ABI_VERSION || + config->allocator.struct_size != sizeof(config->allocator)) { + return LECORE_EINVAL; + } + if (config->flags != 0 || !lecore_reserved_is_zero(config)) { + return LECORE_EINVAL; + } + if (config->dimension == 0) { + return LECORE_EDIM; + } + + switch (config->profile) { + case LECORE_PROFILE_HRR_F64_V1: + scalar_type = LECORE_SCALAR_F64; + scalar_bytes = sizeof(double); + break; + case LECORE_PROFILE_HRR_F32_V1: + scalar_type = LECORE_SCALAR_F32; + scalar_bytes = sizeof(float); + break; + default: + return LECORE_EPROFILE; + } + + if (config->validation != LECORE_VALIDATION_SHAPE && + config->validation != LECORE_VALIDATION_FINITE) { + return LECORE_EINVAL; + } + if (config->backend == LECORE_BACKEND_AUTO || + config->backend == LECORE_BACKEND_DIRECT) { + /* AUTO remains the correctness oracle until a crossover is earned. */ + backend = LECORE_BACKEND_DIRECT; + } else if (config->backend == LECORE_BACKEND_RADIX2) { +#if LECORE_ENABLE_RADIX2 + if (!lecore_is_power_of_two((uintmax_t)config->dimension)) { + return LECORE_EUNSUPPORTED; + } + backend = LECORE_BACKEND_RADIX2; +#else + return LECORE_EUNSUPPORTED; +#endif + } else { + return LECORE_EBACKEND; + } + + has_allocate = config->allocator.allocate != NULL; + has_deallocate = config->allocator.deallocate != NULL; + if (has_allocate != has_deallocate) { + return LECORE_EINVAL; + } + + allocator = config->allocator; + if (!has_allocate) { + allocator.user = NULL; + allocator.allocate = lecore_default_allocate; + allocator.deallocate = lecore_default_deallocate; + } + + if (!lecore_is_power_of_two((uintmax_t)alignment)) { + return LECORE_EOVERFLOW; + } + if (backend == LECORE_BACKEND_RADIX2) { + const size_t scratch_scalars_per_element = 4; + + if ((uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / + ((uintmax_t)scratch_scalars_per_element * scalar_bytes) || + (uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / sizeof(uint32_t) || + (config->dimension > 1 && + (uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / scalar_bytes)) { + return LECORE_EOVERFLOW; + } + scratch_bytes = (size_t)config->dimension * + scratch_scalars_per_element * scalar_bytes; + bit_reversal_bytes = + (size_t)config->dimension * sizeof(uint32_t); + if (config->dimension > 1) { + twiddle_bytes = (size_t)config->dimension * scalar_bytes; + } + } else { + if ((uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + scratch_bytes = (size_t)config->dimension * scalar_bytes; + } + + status = lecore_allocate_block( + &allocator, sizeof(*context), alignment, &context_memory); + if (status != LECORE_OK) { + return status; + } + context = (lecore_context *)context_memory; + memset(context, 0, sizeof(*context)); + + status = lecore_allocate_block( + &allocator, scratch_bytes, alignment, &scratch); + if (status != LECORE_OK) { + goto fail; + } + if (backend == LECORE_BACKEND_RADIX2) { + status = lecore_allocate_block( + &allocator, + bit_reversal_bytes, + alignment, + &bit_reversal); + if (status != LECORE_OK) { + goto fail; + } + if (twiddle_bytes != 0) { + status = lecore_allocate_block( + &allocator, twiddle_bytes, alignment, &twiddles); + if (status != LECORE_OK) { + goto fail; + } + } + } + + context->dimension = config->dimension; + context->profile = config->profile; + context->backend = backend; + context->validation = config->validation; + context->scalar_type = scalar_type; + context->allocator = allocator; + context->scratch = scratch; + context->scratch_bytes = scratch_bytes; + context->radix2_bit_reversal = (uint32_t *)bit_reversal; + context->radix2_bit_reversal_bytes = bit_reversal_bytes; + context->radix2_twiddles = twiddles; + context->radix2_twiddle_bytes = twiddle_bytes; + context->context_bytes = sizeof(*context); + context->allocation_alignment = alignment; +#if LECORE_ENABLE_RADIX2 + if (backend == LECORE_BACKEND_RADIX2) { + lecore_initialize_radix2_plan(context); + } +#endif + context->magic = LECORE_INTERNAL_CONTEXT_MAGIC; + + *out_context = context; + return LECORE_OK; + +fail: + if (twiddles != NULL) { + allocator.deallocate( + allocator.user, twiddles, twiddle_bytes, alignment); + } + if (bit_reversal != NULL) { + allocator.deallocate( + allocator.user, + bit_reversal, + bit_reversal_bytes, + alignment); + } + if (scratch != NULL) { + allocator.deallocate( + allocator.user, scratch, scratch_bytes, alignment); + } + allocator.deallocate( + allocator.user, context, sizeof(*context), alignment); + return status; +} + +void LECORE_CALL lecore_context_destroy(lecore_context *context) +{ + lecore_allocator_v0 allocator; + void *scratch; + size_t scratch_bytes; + void *bit_reversal; + size_t bit_reversal_bytes; + void *twiddles; + size_t twiddle_bytes; + size_t context_bytes; + size_t alignment; + + if (context == NULL || context->magic != LECORE_INTERNAL_CONTEXT_MAGIC) { + return; + } + + allocator = context->allocator; + scratch = context->scratch; + scratch_bytes = context->scratch_bytes; + bit_reversal = context->radix2_bit_reversal; + bit_reversal_bytes = context->radix2_bit_reversal_bytes; + twiddles = context->radix2_twiddles; + twiddle_bytes = context->radix2_twiddle_bytes; + context_bytes = context->context_bytes; + alignment = context->allocation_alignment; + context->magic = 0; + + if (twiddles != NULL) { + allocator.deallocate( + allocator.user, twiddles, twiddle_bytes, alignment); + } + if (bit_reversal != NULL) { + allocator.deallocate( + allocator.user, + bit_reversal, + bit_reversal_bytes, + alignment); + } + allocator.deallocate( + allocator.user, scratch, scratch_bytes, alignment); + allocator.deallocate(allocator.user, context, context_bytes, alignment); +} + +uint32_t LECORE_CALL lecore_context_dimension(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->dimension + : UINT32_C(0); +} + +lecore_profile LECORE_CALL lecore_context_profile(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->profile + : UINT32_C(0); +} + +lecore_backend LECORE_CALL lecore_context_backend(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->backend + : UINT32_C(0); +} + +lecore_validation LECORE_CALL lecore_context_validation( + const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->validation + : UINT32_C(0); +} + +lecore_scalar_type LECORE_CALL lecore_context_scalar_type( + const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->scalar_type + : UINT32_C(0); +} + +size_t LECORE_CALL lecore_context_scratch_bytes(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->scratch_bytes + : 0; +} + +uint64_t LECORE_CALL lecore_capabilities(void) +{ + uint64_t capabilities = + LECORE_CAP_HRR_F64 | + LECORE_CAP_HRR_F32 | + LECORE_CAP_DIRECT | + LECORE_CAP_BATCH | + LECORE_CAP_MIXED_F64_F32 | + LECORE_CAP_FINITE_VALIDATION; + +#if LECORE_ENABLE_FORMAT + capabilities |= LECORE_CAP_FORMAT; +#endif +#if LECORE_ENABLE_RADIX2 + capabilities |= LECORE_CAP_RADIX2; +#endif + return capabilities; +} + +#line 1 "native/liblecore/src/status.c" + + +uint32_t LECORE_CALL lecore_abi_version(void) +{ + return LECORE_ABI_VERSION; +} + +uint32_t LECORE_CALL lecore_isa_version(void) +{ + return LECORE_ISA_VERSION; +} + +const char *LECORE_CALL lecore_version_string(void) +{ + return LECORE_VERSION_STRING_VALUE; +} + +const char *LECORE_CALL lecore_status_string(lecore_status status) +{ + switch (status) { + case LECORE_OK: + return "ok"; + case LECORE_EINVAL: + return "invalid argument"; + case LECORE_EDIM: + return "invalid dimension or stride"; + case LECORE_EPROFILE: + return "profile mismatch or unknown profile"; + case LECORE_EBACKEND: + return "unknown backend"; + case LECORE_EOVERFLOW: + return "size arithmetic overflow"; + case LECORE_ENOMEM: + return "allocation failed"; + case LECORE_EUNSUPPORTED: + return "unsupported operation or backend"; + case LECORE_ENONFINITE: + return "non-finite input"; + case LECORE_EFORMAT: + return "invalid interchange format"; + case LECORE_ECHECKSUM: + return "checksum mismatch"; + default: + return "unknown status"; + } +} + +#line 1 "native/liblecore/src/vector_f32.c" + + +#include +#include + +static int lecore_f32_vector_is_finite(const float *values, uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_f32_matrix_is_finite( + const float *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_f32_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static float lecore_f32_dot_raw( + const float *a, + const float *b, + uint32_t dimension) +{ + float result = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + result += a[index] * b[index]; + } + return result; +} + +static float lecore_f32_cosine_raw( + const float *a, + const float *b, + uint32_t dimension) +{ + float dot; + float norm_a_squared = 0.0f; + float norm_b_squared = 0.0f; + float norm_a; + float norm_b; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + norm_a_squared += a[index] * a[index]; + norm_b_squared += b[index] * b[index]; + } + if (norm_a_squared == 0.0f || norm_b_squared == 0.0f) { + return 0.0f; + } + norm_a = sqrtf(norm_a_squared); + norm_b = sqrtf(norm_b_squared); + dot = lecore_f32_dot_raw(a, b, dimension); + return dot / (norm_a * norm_b); +} + +static float lecore_f32_cosine_with_query_norm_squared( + const float *query, + float query_norm_squared, + float query_norm, + const float *row, + uint32_t dimension) +{ + float row_norm_squared = 0.0f; + float row_norm; + float dot; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + row_norm_squared += row[index] * row[index]; + } + if (query_norm_squared == 0.0f || row_norm_squared == 0.0f) { + return 0.0f; + } + row_norm = sqrtf(row_norm_squared); + dot = lecore_f32_dot_raw(query, row, dimension); + return dot / (query_norm * row_norm); +} + +static lecore_status lecore_f32_prepare_matrix_output( + const lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + const float *out_values, + size_t *out_rows_bytes) +{ + lecore_status status; + size_t output_bytes; + const size_t vector_bytes = (size_t)context->dimension * sizeof(float); + + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + out_rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(float)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(float); + if (lecore_internal_ranges_overlap( + query, vector_bytes, out_values, output_bytes) || + lecore_internal_ranges_overlap( + rows, *out_rows_bytes, out_values, output_bytes)) { + return LECORE_EINVAL; + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_validate_f32( + const float *values, + size_t count) +{ + size_t index; + + if (count == 0) { + return LECORE_OK; + } + if (values == NULL) { + return LECORE_EINVAL; + } + for (index = 0; index < count; ++index) { + if (!isfinite(values[index])) { + return LECORE_ENONFINITE; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_normalize_f32( + lecore_context *context, + const float *input, + float *output) +{ + lecore_status status; + float norm_squared = 0.0f; + float norm; + uint32_t index; + size_t vector_bytes; + + status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + norm_squared += input[index] * input[index]; + } + norm = sqrtf(norm_squared); + if (norm > 0.0f) { + for (index = 0; index < context->dimension; ++index) { + output[index] = input[index] / norm; + } + } else if (output != input) { + memcpy(output, input, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_dot) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_dot == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(a, context->dimension) || + !lecore_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_dot = lecore_f32_dot_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f32_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(query, context->dimension) || + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f32_dot_raw( + query, rows + row * row_stride, context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_cosine) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_cosine == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(a, context->dimension) || + !lecore_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_cosine = lecore_f32_cosine_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float query_norm; + float query_norm_squared = 0.0f; + size_t rows_bytes; + size_t row; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f32_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(query, context->dimension) || + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrtf(query_norm_squared); + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f32_cosine_with_query_norm_squared( + query, + query_norm_squared, + query_norm, + rows + row * row_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_involution_f32( + lecore_context *context, + const float *input, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + uint32_t index; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + if (input == output) { + for (index = 1; index < context->dimension - index; ++index) { + float temporary = output[index]; + output[index] = output[context->dimension - index]; + output[context->dimension - index] = temporary; + } + } else { + output[0] = input[0]; + for (index = 1; index < context->dimension; ++index) { + output[index] = input[context->dimension - index]; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_permute_f32( + lecore_context *context, + const float *input, + int64_t shift, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + int64_t reduced_shift; + uint32_t normalized_shift; + uint32_t index; + uint32_t source; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + reduced_shift = shift % (int64_t)context->dimension; + if (reduced_shift < 0) { + reduced_shift += (int64_t)context->dimension; + } + normalized_shift = (uint32_t)reduced_shift; + target = input == output ? (float *)context->scratch : output; + + for (index = 0; index < context->dimension; ++index) { + source = index >= normalized_shift + ? index - normalized_shift + : context->dimension - (normalized_shift - index); + target[index] = input[source]; + } + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_bundle_f32( + lecore_context *context, + const float *rows, + size_t row_count, + size_t row_stride, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t row; + uint32_t index; + float norm_squared = 0.0f; + float norm; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (rows == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + rows, rows_bytes, output, vector_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + output[index] = 0.0f; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + for (index = 0; index < context->dimension; ++index) { + output[index] += current[index]; + } + } + for (index = 0; index < context->dimension; ++index) { + norm_squared += output[index] * output[index]; + } + norm = sqrtf(norm_squared); + if (norm > 0.0f) { + for (index = 0; index < context->dimension; ++index) { + output[index] /= norm; + } + } + return LECORE_OK; +} + +#line 1 "native/liblecore/src/vector_f64.c" + + +#include +#include + +static int lecore_f64_vector_is_finite(const double *values, uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_f64_matrix_is_finite( + const double *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_f64_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static double lecore_f64_dot_raw( + const double *a, + const double *b, + uint32_t dimension) +{ + double result = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + result += a[index] * b[index]; + } + return result; +} + +static double lecore_f64_cosine_raw( + const double *a, + const double *b, + uint32_t dimension) +{ + double dot; + double norm_a_squared = 0.0; + double norm_b_squared = 0.0; + double norm_a; + double norm_b; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + norm_a_squared += a[index] * a[index]; + norm_b_squared += b[index] * b[index]; + } + + /* The exact zero branch precedes NaN/Inf propagation by contract. */ + if (norm_a_squared == 0.0 || norm_b_squared == 0.0) { + return 0.0; + } + norm_a = sqrt(norm_a_squared); + norm_b = sqrt(norm_b_squared); + dot = lecore_f64_dot_raw(a, b, dimension); + return dot / (norm_a * norm_b); +} + +static double lecore_f64_cosine_with_query_norm_squared( + const double *query, + double query_norm_squared, + double query_norm, + const double *row, + uint32_t dimension) +{ + double row_norm_squared = 0.0; + double row_norm; + double dot; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + row_norm_squared += row[index] * row[index]; + } + if (query_norm_squared == 0.0 || row_norm_squared == 0.0) { + return 0.0; + } + row_norm = sqrt(row_norm_squared); + dot = lecore_f64_dot_raw(query, row, dimension); + return dot / (query_norm * row_norm); +} + +static lecore_status lecore_f64_prepare_matrix_output( + const lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + const double *out_values, + size_t *out_rows_bytes) +{ + lecore_status status; + size_t output_bytes; + const size_t vector_bytes = (size_t)context->dimension * sizeof(double); + + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + out_rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(double)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(double); + if (lecore_internal_ranges_overlap( + query, vector_bytes, out_values, output_bytes) || + lecore_internal_ranges_overlap( + rows, *out_rows_bytes, out_values, output_bytes)) { + return LECORE_EINVAL; + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_validate_f64( + const double *values, + size_t count) +{ + size_t index; + + if (count == 0) { + return LECORE_OK; + } + if (values == NULL) { + return LECORE_EINVAL; + } + for (index = 0; index < count; ++index) { + if (!isfinite(values[index])) { + return LECORE_ENONFINITE; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_normalize_f64( + lecore_context *context, + const double *input, + double *output) +{ + lecore_status status; + double norm_squared = 0.0; + double norm; + uint32_t index; + size_t vector_bytes; + + status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + norm_squared += input[index] * input[index]; + } + norm = sqrt(norm_squared); + if (norm > 0.0) { + for (index = 0; index < context->dimension; ++index) { + output[index] = input[index] / norm; + } + } else if (output != input) { + memcpy(output, input, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_dot) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_dot == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(a, context->dimension) || + !lecore_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_dot = lecore_f64_dot_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f64_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(query, context->dimension) || + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f64_dot_raw( + query, rows + row * row_stride, context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_cosine) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_cosine == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(a, context->dimension) || + !lecore_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_cosine = lecore_f64_cosine_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double query_norm; + double query_norm_squared = 0.0; + size_t rows_bytes; + size_t row; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f64_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(query, context->dimension) || + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrt(query_norm_squared); + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f64_cosine_with_query_norm_squared( + query, + query_norm_squared, + query_norm, + rows + row * row_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_involution_f64( + lecore_context *context, + const double *input, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + uint32_t index; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + if (input == output) { + for (index = 1; index < context->dimension - index; ++index) { + double temporary = output[index]; + output[index] = output[context->dimension - index]; + output[context->dimension - index] = temporary; + } + } else { + output[0] = input[0]; + for (index = 1; index < context->dimension; ++index) { + output[index] = input[context->dimension - index]; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_permute_f64( + lecore_context *context, + const double *input, + int64_t shift, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + int64_t reduced_shift; + uint32_t normalized_shift; + uint32_t index; + uint32_t source; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + reduced_shift = shift % (int64_t)context->dimension; + if (reduced_shift < 0) { + reduced_shift += (int64_t)context->dimension; + } + normalized_shift = (uint32_t)reduced_shift; + target = input == output ? (double *)context->scratch : output; + + for (index = 0; index < context->dimension; ++index) { + source = index >= normalized_shift + ? index - normalized_shift + : context->dimension - (normalized_shift - index); + target[index] = input[source]; + } + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_bundle_f64( + lecore_context *context, + const double *rows, + size_t row_count, + size_t row_stride, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t row; + uint32_t index; + double norm_squared = 0.0; + double norm; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (rows == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + rows, rows_bytes, output, vector_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + output[index] = 0.0; + } + for (row = 0; row < row_count; ++row) { + const double *current = rows + row * row_stride; + for (index = 0; index < context->dimension; ++index) { + output[index] += current[index]; + } + } + for (index = 0; index < context->dimension; ++index) { + norm_squared += output[index] * output[index]; + } + norm = sqrt(norm_squared); + if (norm > 0.0) { + for (index = 0; index < context->dimension; ++index) { + output[index] /= norm; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f64_f32( + lecore_context *f32_context, + const double *query, + const float *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + f32_context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t output_bytes; + size_t row; + uint32_t index; + double query_norm_squared = 0.0; + size_t query_bytes; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + if ((uintmax_t)f32_context->dimension > + (uintmax_t)SIZE_MAX / (uintmax_t)sizeof(double)) { + return LECORE_EOVERFLOW; + } + query_bytes = (size_t)f32_context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + f32_context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(double)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(double); + if (lecore_internal_ranges_overlap( + query, query_bytes, out_scores, output_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_scores, output_bytes)) { + return LECORE_EINVAL; + } + if (f32_context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_f64_vector_is_finite(query, f32_context->dimension)) { + return LECORE_ENONFINITE; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + for (index = 0; index < f32_context->dimension; ++index) { + if (!isfinite(current[index])) { + return LECORE_ENONFINITE; + } + } + } + } + + for (index = 0; index < f32_context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + double dot; + double row_norm_squared = 0.0; + + for (index = 0; index < f32_context->dimension; ++index) { + const double component = (double)current[index]; + row_norm_squared += component * component; + } + if (query_norm_squared == 0.0 || row_norm_squared == 0.0) { + out_scores[row] = 0.0; + } else { + dot = 0.0; + for (index = 0; index < f32_context->dimension; ++index) { + dot += query[index] * (double)current[index]; + } + out_scores[row] = dot / + (sqrt(query_norm_squared) * sqrt(row_norm_squared)); + } + } + return LECORE_OK; +} + +#line 1 "native/liblecore/src/cleanup_f32.c" + + +#include + +static int lecore_cleanup_f32_vector_is_finite( + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static float lecore_cleanup_f32_cosine( + const float *query, + float query_norm_squared, + float query_norm, + const float *candidate, + uint32_t dimension) +{ + float candidate_norm_squared = 0.0f; + float candidate_norm; + float dot = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + candidate_norm_squared += candidate[index] * candidate[index]; + } + if (query_norm_squared == 0.0f || candidate_norm_squared == 0.0f) { + return 0.0f; + } + for (index = 0; index < dimension; ++index) { + dot += query[index] * candidate[index]; + } + candidate_norm = sqrtf(candidate_norm_squared); + return dot / (query_norm * candidate_norm); +} + +lecore_status LECORE_CALL lecore_cleanup_f32( + lecore_context *context, + const float *query, + const float *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + float *out_score) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t candidates_bytes; + size_t candidate; + size_t best_index; + float best_score; + float query_norm; + float query_norm_squared = 0.0f; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || candidates == NULL || + out_index == NULL || out_score == NULL) { + return LECORE_EINVAL; + } + if (lecore_internal_ranges_overlap( + out_index, sizeof(*out_index), out_score, sizeof(*out_score))) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, + candidate_count, + candidate_stride, + sizeof(float), + &candidates_bytes); + if (status != LECORE_OK) { + return status; + } + (void)candidates_bytes; + + if (context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_cleanup_f32_vector_is_finite( + query, context->dimension)) { + return LECORE_ENONFINITE; + } + for (candidate = 0; candidate < candidate_count; ++candidate) { + if (!lecore_cleanup_f32_vector_is_finite( + candidates + candidate * candidate_stride, + context->dimension)) { + return LECORE_ENONFINITE; + } + } + } + + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrtf(query_norm_squared); + best_index = 0; + best_score = lecore_cleanup_f32_cosine( + query, + query_norm_squared, + query_norm, + candidates, + context->dimension); + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + float score = lecore_cleanup_f32_cosine( + query, + query_norm_squared, + query_norm, + candidates + candidate * candidate_stride, + context->dimension); + if (isnan(score)) { + best_index = candidate; + best_score = score; + break; + } + if (score > best_score) { + best_index = candidate; + best_score = score; + } + } + } + + *out_index = best_index; + *out_score = best_score; + return LECORE_OK; +} + +#line 1 "native/liblecore/src/cleanup_f64.c" + + +#include + +static int lecore_cleanup_f64_vector_is_finite( + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static double lecore_cleanup_f64_cosine( + const double *query, + double query_norm_squared, + double query_norm, + const double *candidate, + uint32_t dimension) +{ + double candidate_norm_squared = 0.0; + double candidate_norm; + double dot = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + candidate_norm_squared += candidate[index] * candidate[index]; + } + if (query_norm_squared == 0.0 || candidate_norm_squared == 0.0) { + return 0.0; + } + for (index = 0; index < dimension; ++index) { + dot += query[index] * candidate[index]; + } + candidate_norm = sqrt(candidate_norm_squared); + return dot / (query_norm * candidate_norm); +} + +lecore_status LECORE_CALL lecore_cleanup_f64( + lecore_context *context, + const double *query, + const double *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + double *out_score) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t candidates_bytes; + size_t candidate; + size_t best_index; + double best_score; + double query_norm; + double query_norm_squared = 0.0; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || candidates == NULL || + out_index == NULL || out_score == NULL) { + return LECORE_EINVAL; + } + if (lecore_internal_ranges_overlap( + out_index, sizeof(*out_index), out_score, sizeof(*out_score))) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, + candidate_count, + candidate_stride, + sizeof(double), + &candidates_bytes); + if (status != LECORE_OK) { + return status; + } + (void)candidates_bytes; + + if (context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_cleanup_f64_vector_is_finite( + query, context->dimension)) { + return LECORE_ENONFINITE; + } + for (candidate = 0; candidate < candidate_count; ++candidate) { + if (!lecore_cleanup_f64_vector_is_finite( + candidates + candidate * candidate_stride, + context->dimension)) { + return LECORE_ENONFINITE; + } + } + } + + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrt(query_norm_squared); + best_index = 0; + best_score = lecore_cleanup_f64_cosine( + query, + query_norm_squared, + query_norm, + candidates, + context->dimension); + + /* NumPy argmax treats the first NaN as the winning stable index. */ + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + double score = lecore_cleanup_f64_cosine( + query, + query_norm_squared, + query_norm, + candidates + candidate * candidate_stride, + context->dimension); + if (isnan(score)) { + best_index = candidate; + best_score = score; + break; + } + if (score > best_score) { + best_index = candidate; + best_score = score; + } + } + } + + *out_index = best_index; + *out_score = best_score; + return LECORE_OK; +} + +#line 1 "native/liblecore/src/hrr_direct_f32.c" + + +#include +#include + +static int lecore_hrr_f32_vector_is_finite( + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_hrr_f32_matrix_is_finite( + const float *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_hrr_f32_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static void lecore_hrr_bind_f32_raw( + const float *a, + const float *b, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t left_index; + + if (dimension >= UINT32_C(32)) { + /* + * Preserve the definitional ascending reduction order per output, + * while exposing contiguous independent updates to the compiler. + */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (left_index = 0; left_index < dimension; ++left_index) { + const float left = a[left_index]; + uint32_t right_index = 0; + const uint32_t first_count = dimension - left_index; + + for (; right_index < first_count; ++right_index) { + output[left_index + right_index] += + left * b[right_index]; + } + for (; right_index < dimension; ++right_index) { + output[right_index - first_count] += + left * b[right_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + + for (left_index = 0; left_index < dimension; ++left_index) { + uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + sum += a[left_index] * b[right_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_unbind_f32_raw( + const float *composite, + const float *key, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t composite_index; + + if (dimension >= UINT32_C(32)) { + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const float value = composite[composite_index]; + + for (output_index = 0; + output_index <= composite_index; + ++output_index) { + output[output_index] += + value * key[composite_index - output_index]; + } + for (; output_index < dimension; ++output_index) { + output[output_index] += value * + key[dimension + composite_index - output_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + + for (composite_index = 0; composite_index < dimension; + ++composite_index) { + uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + sum += composite[composite_index] * key[key_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_bind_f32_selected( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_f32(context, a, b, output); + return; + } +#endif + lecore_hrr_bind_f32_raw(a, b, output, context->dimension); +} + +static void lecore_hrr_unbind_f32_selected( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_f32( + context, composite, key, output); + return; + } +#endif + lecore_hrr_unbind_f32_raw( + composite, key, output, context->dimension); +} + +static lecore_status lecore_hrr_f32_check_scalar_aliases( + const lecore_context *context, + const float *a, + const float *b, + const float *output) +{ + lecore_status status; + const size_t vector_bytes = + (size_t)context->dimension * sizeof(float); + + status = lecore_internal_check_vector_alias( + a, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + return lecore_internal_check_vector_alias( + b, output, vector_bytes, 1); +} + +lecore_status LECORE_CALL lecore_hrr_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f32_check_scalar_aliases(context, a, b, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(a, context->dimension) || + !lecore_hrr_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(float); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == a || output == b) + ? (float *)context->scratch + : output; + lecore_hrr_bind_f32_selected(context, a, b, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (composite == NULL || key == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f32_check_scalar_aliases( + context, composite, key, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite( + composite, context->dimension) || + !lecore_hrr_f32_vector_is_finite(key, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(float); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == composite || output == key) + ? (float *)context->scratch + : output; + lecore_hrr_unbind_f32_selected(context, composite, key, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_batch_f32( + lecore_context *context, + const float *a_rows, + size_t a_stride, + const float *b_rows, + size_t b_stride, + size_t row_count, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t a_bytes; + size_t b_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (a_rows == NULL || b_rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, a_stride, sizeof(float), &a_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, b_stride, sizeof(float), &b_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, out_stride, sizeof(float), &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap(a_rows, a_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap(b_rows, b_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_matrix_is_finite( + a_rows, row_count, a_stride, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + b_rows, row_count, b_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_selected( + context, + a_rows + row * a_stride, + b_rows + row * b_stride, + out_rows + row * out_stride); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t role_bytes; + size_t rows_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (role == NULL || rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + role_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + row_count, + out_stride, + sizeof(float), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + role, role_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(role, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_fixed_f32( + context, + role, + rows, + row_count, + row_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_raw( + role, + rows + row * row_stride, + out_rows + row * out_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t trace_bytes; + size_t keys_bytes; + size_t out_bytes; + size_t key; + + if (status != LECORE_OK) { + return status; + } + if (trace == NULL || keys == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + trace_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + key_count, + key_stride, + sizeof(float), + &keys_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + key_count, + out_stride, + sizeof(float), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + trace, trace_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + keys, keys_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(trace, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + keys, key_count, key_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_all_f32( + context, + trace, + keys, + key_count, + key_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f32_raw( + trace, + keys + key * key_stride, + out_rows + key * out_stride, + context->dimension); + } + return LECORE_OK; +} + +#line 1 "native/liblecore/src/hrr_direct_f64.c" + + +#include +#include + +static int lecore_hrr_f64_vector_is_finite( + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_hrr_f64_matrix_is_finite( + const double *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_hrr_f64_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static void lecore_hrr_bind_f64_raw( + const double *a, + const double *b, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t left_index; + + if (dimension >= UINT32_C(32)) { + /* + * Visit left_index in the same ascending order as the definitional + * reduction, but update independent outputs contiguously. This keeps + * every output's floating-point operation order unchanged while + * making the inner loops vectorizable. Public alias checks guarantee + * that raw output is disjoint from both inputs. + */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (left_index = 0; left_index < dimension; ++left_index) { + const double left = a[left_index]; + uint32_t right_index = 0; + const uint32_t first_count = dimension - left_index; + + for (; right_index < first_count; ++right_index) { + output[left_index + right_index] += + left * b[right_index]; + } + for (; right_index < dimension; ++right_index) { + output[right_index - first_count] += + left * b[right_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + + for (left_index = 0; left_index < dimension; ++left_index) { + uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + sum += a[left_index] * b[right_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_unbind_f64_raw( + const double *composite, + const double *key, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t composite_index; + + if (dimension >= UINT32_C(32)) { + /* Preserve each output's ascending composite-index reduction order. */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const double value = composite[composite_index]; + + for (output_index = 0; + output_index <= composite_index; + ++output_index) { + output[output_index] += + value * key[composite_index - output_index]; + } + for (; output_index < dimension; ++output_index) { + output[output_index] += value * + key[dimension + composite_index - output_index]; + } + } + return; + } + + /* Equivalent to bind(composite, involution(key)), without a temporary. */ + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + + for (composite_index = 0; composite_index < dimension; + ++composite_index) { + uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + sum += composite[composite_index] * key[key_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_bind_f64_selected( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_f64(context, a, b, output); + return; + } +#endif + lecore_hrr_bind_f64_raw(a, b, output, context->dimension); +} + +static void lecore_hrr_unbind_f64_selected( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_f64( + context, composite, key, output); + return; + } +#endif + lecore_hrr_unbind_f64_raw( + composite, key, output, context->dimension); +} + +static lecore_status lecore_hrr_f64_check_scalar_aliases( + const lecore_context *context, + const double *a, + const double *b, + const double *output) +{ + lecore_status status; + const size_t vector_bytes = + (size_t)context->dimension * sizeof(double); + + status = lecore_internal_check_vector_alias( + a, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + return lecore_internal_check_vector_alias( + b, output, vector_bytes, 1); +} + +lecore_status LECORE_CALL lecore_hrr_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f64_check_scalar_aliases(context, a, b, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(a, context->dimension) || + !lecore_hrr_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(double); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == a || output == b) + ? (double *)context->scratch + : output; + lecore_hrr_bind_f64_selected(context, a, b, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (composite == NULL || key == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f64_check_scalar_aliases( + context, composite, key, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite( + composite, context->dimension) || + !lecore_hrr_f64_vector_is_finite(key, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(double); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == composite || output == key) + ? (double *)context->scratch + : output; + lecore_hrr_unbind_f64_selected(context, composite, key, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_batch_f64( + lecore_context *context, + const double *a_rows, + size_t a_stride, + const double *b_rows, + size_t b_stride, + size_t row_count, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t a_bytes; + size_t b_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (a_rows == NULL || b_rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, a_stride, sizeof(double), &a_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, b_stride, sizeof(double), &b_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, out_stride, sizeof(double), &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap(a_rows, a_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap(b_rows, b_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_matrix_is_finite( + a_rows, row_count, a_stride, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + b_rows, row_count, b_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_selected( + context, + a_rows + row * a_stride, + b_rows + row * b_stride, + out_rows + row * out_stride); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t out_bytes; + size_t row; + size_t role_bytes; + + if (status != LECORE_OK) { + return status; + } + if (role == NULL || rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + role_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + row_count, + out_stride, + sizeof(double), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + role, role_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(role, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_fixed_f64( + context, + role, + rows, + row_count, + row_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_raw( + role, + rows + row * row_stride, + out_rows + row * out_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t keys_bytes; + size_t out_bytes; + size_t key; + size_t trace_bytes; + + if (status != LECORE_OK) { + return status; + } + if (trace == NULL || keys == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + trace_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + key_count, + key_stride, + sizeof(double), + &keys_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + key_count, + out_stride, + sizeof(double), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + trace, trace_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + keys, keys_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(trace, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + keys, key_count, key_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_all_f64( + context, + trace, + keys, + key_count, + key_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f64_raw( + trace, + keys + key * key_stride, + out_rows + key * out_stride, + context->dimension); + } + return LECORE_OK; +} + +#line 1 "native/liblecore/src/hrr_radix2_f32.c" + + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f32( + const lecore_context *context, + float *values, + int inverse, + int input_is_bit_reversed) +{ + const uint32_t dimension = context->dimension; + const float *twiddles = (const float *)context->radix2_twiddles; + uint32_t index; + + if (!input_is_bit_reversed) { + for (index = 0; index < dimension; ++index) { + uint32_t reversed = context->radix2_bit_reversal[index]; + if (reversed > index) { + float real = values[(size_t)index * 2]; + float imaginary = values[(size_t)index * 2 + 1]; + values[(size_t)index * 2] = values[(size_t)reversed * 2]; + values[(size_t)index * 2 + 1] = + values[(size_t)reversed * 2 + 1]; + values[(size_t)reversed * 2] = real; + values[(size_t)reversed * 2 + 1] = imaginary; + } + } + } + + if (dimension > 1) { + uint32_t length = 2; + + for (;;) { + const uint32_t half = length / 2; + const uint32_t twiddle_step = dimension / length; + uint32_t block; + + for (block = 0; block < dimension; block += length) { + uint32_t offset; + + for (offset = 0; offset < half; ++offset) { + const uint32_t twiddle_index = offset * twiddle_step; + const uint32_t even_index = block + offset; + const uint32_t odd_index = even_index + half; + const float wr = + twiddles[(size_t)twiddle_index * 2]; + const float wi = inverse + ? -twiddles[(size_t)twiddle_index * 2 + 1] + : twiddles[(size_t)twiddle_index * 2 + 1]; + const float odd_real = + values[(size_t)odd_index * 2]; + const float odd_imaginary = + values[(size_t)odd_index * 2 + 1]; + const float transformed_real = + odd_real * wr - odd_imaginary * wi; + const float transformed_imaginary = + odd_real * wi + odd_imaginary * wr; + const float even_real = + values[(size_t)even_index * 2]; + const float even_imaginary = + values[(size_t)even_index * 2 + 1]; + + values[(size_t)even_index * 2] = + even_real + transformed_real; + values[(size_t)even_index * 2 + 1] = + even_imaginary + transformed_imaginary; + values[(size_t)odd_index * 2] = + even_real - transformed_real; + values[(size_t)odd_index * 2 + 1] = + even_imaginary - transformed_imaginary; + } + } + if (length == dimension) { + break; + } + length <<= 1; + } + } + + if (inverse) { + const float scale = (float)dimension; + + for (index = 0; index < dimension; ++index) { + values[(size_t)index * 2] /= scale; + values[(size_t)index * 2 + 1] /= scale; + } + } +} + +static void lecore_radix2_load_f32( + const lecore_context *context, + float *destination, + const float *source, + int involute) +{ + uint32_t index; + + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; + uint32_t source_index = involute && index != 0 + ? context->dimension - index + : index; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0f; + } +} + +static void lecore_radix2_multiply_f32( + float *destination, + const float *left, + const float *right, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + const float left_real = left[(size_t)index * 2]; + const float left_imaginary = left[(size_t)index * 2 + 1]; + const float right_real = right[(size_t)index * 2]; + const float right_imaginary = right[(size_t)index * 2 + 1]; + + destination[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + destination[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } +} + +static void lecore_radix2_store_real_f32( + float *output, + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + output[index] = values[(size_t)index * 2]; + } +} + +static void lecore_radix2_convolve_f32( + lecore_context *context, + const float *left_input, + const float *right_input, + int involute_right, + float *output) +{ + float *left = (float *)context->scratch; + float *right = left + (size_t)context->dimension * 2; + lecore_radix2_load_f32( + context, left, left_input, 0); + lecore_radix2_load_f32( + context, right, right_input, involute_right); + lecore_radix2_fft_f32(context, left, 0, 1); + lecore_radix2_fft_f32(context, right, 0, 1); + lecore_radix2_multiply_f32( + left, left, right, context->dimension); + lecore_radix2_fft_f32(context, left, 1, 0); + lecore_radix2_store_real_f32(output, left, context->dimension); +} + +static void lecore_radix2_convolve_fixed_left_f32( + lecore_context *context, + const float *fixed_left, + const float *right_rows, + size_t row_count, + size_t right_stride, + int involute_right, + float *out_rows, + size_t out_stride) +{ + float *left = (float *)context->scratch; + float *right = left + (size_t)context->dimension * 2; + size_t row; + + lecore_radix2_load_f32(context, left, fixed_left, 0); + lecore_radix2_fft_f32(context, left, 0, 1); + for (row = 0; row < row_count; ++row) { + lecore_radix2_load_f32( + context, + right, + right_rows + row * right_stride, + involute_right); + lecore_radix2_fft_f32(context, right, 0, 1); + lecore_radix2_multiply_f32( + right, left, right, context->dimension); + lecore_radix2_fft_f32(context, right, 1, 0); + lecore_radix2_store_real_f32( + out_rows + row * out_stride, right, context->dimension); + } +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ + lecore_radix2_convolve_f32(context, a, b, 0, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ + lecore_radix2_convolve_f32(context, composite, key, 1, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f32( + context, role, rows, row_count, row_stride, 0, out_rows, out_stride); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f32( + context, + trace, + keys, + key_count, + key_stride, + 1, + out_rows, + out_stride); +} + +#endif /* LECORE_ENABLE_RADIX2 */ + +#line 1 "native/liblecore/src/hrr_radix2_f64.c" + + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f64( + const lecore_context *context, + double *values, + int inverse, + int input_is_bit_reversed) +{ + const uint32_t dimension = context->dimension; + const double *twiddles = (const double *)context->radix2_twiddles; + uint32_t index; + + if (!input_is_bit_reversed) { + for (index = 0; index < dimension; ++index) { + uint32_t reversed = context->radix2_bit_reversal[index]; + if (reversed > index) { + double real = values[(size_t)index * 2]; + double imaginary = values[(size_t)index * 2 + 1]; + values[(size_t)index * 2] = values[(size_t)reversed * 2]; + values[(size_t)index * 2 + 1] = + values[(size_t)reversed * 2 + 1]; + values[(size_t)reversed * 2] = real; + values[(size_t)reversed * 2 + 1] = imaginary; + } + } + } + + if (dimension > 1) { + uint32_t length = 2; + + for (;;) { + const uint32_t half = length / 2; + const uint32_t twiddle_step = dimension / length; + uint32_t block; + + for (block = 0; block < dimension; block += length) { + uint32_t offset; + + for (offset = 0; offset < half; ++offset) { + const uint32_t twiddle_index = offset * twiddle_step; + const uint32_t even_index = block + offset; + const uint32_t odd_index = even_index + half; + const double wr = + twiddles[(size_t)twiddle_index * 2]; + const double wi = inverse + ? -twiddles[(size_t)twiddle_index * 2 + 1] + : twiddles[(size_t)twiddle_index * 2 + 1]; + const double odd_real = + values[(size_t)odd_index * 2]; + const double odd_imaginary = + values[(size_t)odd_index * 2 + 1]; + const double transformed_real = + odd_real * wr - odd_imaginary * wi; + const double transformed_imaginary = + odd_real * wi + odd_imaginary * wr; + const double even_real = + values[(size_t)even_index * 2]; + const double even_imaginary = + values[(size_t)even_index * 2 + 1]; + + values[(size_t)even_index * 2] = + even_real + transformed_real; + values[(size_t)even_index * 2 + 1] = + even_imaginary + transformed_imaginary; + values[(size_t)odd_index * 2] = + even_real - transformed_real; + values[(size_t)odd_index * 2 + 1] = + even_imaginary - transformed_imaginary; + } + } + if (length == dimension) { + break; + } + length <<= 1; + } + } + + if (inverse) { + const double scale = (double)dimension; + + for (index = 0; index < dimension; ++index) { + values[(size_t)index * 2] /= scale; + values[(size_t)index * 2 + 1] /= scale; + } + } +} + +static void lecore_radix2_load_f64( + const lecore_context *context, + double *destination, + const double *source, + int involute) +{ + uint32_t index; + + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; + uint32_t source_index = involute && index != 0 + ? context->dimension - index + : index; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0; + } +} + +static void lecore_radix2_multiply_f64( + double *destination, + const double *left, + const double *right, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + const double left_real = left[(size_t)index * 2]; + const double left_imaginary = left[(size_t)index * 2 + 1]; + const double right_real = right[(size_t)index * 2]; + const double right_imaginary = right[(size_t)index * 2 + 1]; + + destination[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + destination[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } +} + +static void lecore_radix2_store_real_f64( + double *output, + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + output[index] = values[(size_t)index * 2]; + } +} + +static void lecore_radix2_convolve_f64( + lecore_context *context, + const double *left_input, + const double *right_input, + int involute_right, + double *output) +{ + double *left = (double *)context->scratch; + double *right = left + (size_t)context->dimension * 2; + lecore_radix2_load_f64( + context, left, left_input, 0); + lecore_radix2_load_f64( + context, right, right_input, involute_right); + lecore_radix2_fft_f64(context, left, 0, 1); + lecore_radix2_fft_f64(context, right, 0, 1); + lecore_radix2_multiply_f64( + left, left, right, context->dimension); + lecore_radix2_fft_f64(context, left, 1, 0); + lecore_radix2_store_real_f64(output, left, context->dimension); +} + +static void lecore_radix2_convolve_fixed_left_f64( + lecore_context *context, + const double *fixed_left, + const double *right_rows, + size_t row_count, + size_t right_stride, + int involute_right, + double *out_rows, + size_t out_stride) +{ + double *left = (double *)context->scratch; + double *right = left + (size_t)context->dimension * 2; + size_t row; + + lecore_radix2_load_f64(context, left, fixed_left, 0); + lecore_radix2_fft_f64(context, left, 0, 1); + for (row = 0; row < row_count; ++row) { + lecore_radix2_load_f64( + context, + right, + right_rows + row * right_stride, + involute_right); + lecore_radix2_fft_f64(context, right, 0, 1); + lecore_radix2_multiply_f64( + right, left, right, context->dimension); + lecore_radix2_fft_f64(context, right, 1, 0); + lecore_radix2_store_real_f64( + out_rows + row * out_stride, right, context->dimension); + } +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ + lecore_radix2_convolve_f64(context, a, b, 0, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ + lecore_radix2_convolve_f64(context, composite, key, 1, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f64( + context, role, rows, row_count, row_stride, 0, out_rows, out_stride); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f64( + context, + trace, + keys, + key_count, + key_stride, + 1, + out_rows, + out_stride); +} + +#endif /* LECORE_ENABLE_RADIX2 */ + +#if LECORE_ENABLE_FORMAT + +#line 1 "native/liblecore/src/crc64.c" + + +#define LECORE_CRC64_ECMA_POLYNOMIAL UINT64_C(0x42F0E1EBA9EA3693) + +LECORE_INTERNAL_API uint64_t lecore_format_crc64_ecma_update( + uint64_t crc, + const uint8_t *data, + size_t data_bytes) +{ + size_t byte_index; + + for (byte_index = 0; byte_index < data_bytes; ++byte_index) { + unsigned bit_index; + + crc ^= (uint64_t)data[byte_index] << 56; + for (bit_index = 0; bit_index < 8; ++bit_index) { + if ((crc & UINT64_C(0x8000000000000000)) != 0) { + crc = (crc << 1) ^ LECORE_CRC64_ECMA_POLYNOMIAL; + } else { + crc <<= 1; + } + } + } + + return crc; +} + +#line 1 "native/liblecore/src/format.c" + + +#include +#include + + + +#define LECORE_FORMAT_MAGIC_OFFSET ((size_t)0) +#define LECORE_FORMAT_MAJOR_OFFSET ((size_t)8) +#define LECORE_FORMAT_MINOR_OFFSET ((size_t)10) +#define LECORE_FORMAT_HEADER_BYTES_OFFSET ((size_t)12) +#define LECORE_FORMAT_FLAGS_OFFSET ((size_t)14) +#define LECORE_FORMAT_ARTIFACT_KIND_OFFSET ((size_t)16) +#define LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET ((size_t)20) +#define LECORE_FORMAT_SCALAR_TYPE_OFFSET ((size_t)24) +#define LECORE_FORMAT_DIMENSION_OFFSET ((size_t)28) +#define LECORE_FORMAT_NORMALIZATION_OFFSET ((size_t)32) +#define LECORE_FORMAT_ATOM_SCHEME_OFFSET ((size_t)36) +#define LECORE_FORMAT_SEED_NAMESPACE_OFFSET ((size_t)40) +#define LECORE_FORMAT_VECTOR_COUNT_OFFSET ((size_t)48) +#define LECORE_FORMAT_PAYLOAD_BYTES_OFFSET ((size_t)56) +#define LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET ((size_t)64) +#define LECORE_FORMAT_CHECKSUM_OFFSET ((size_t)80) +#define LECORE_FORMAT_RESERVED_OFFSET ((size_t)88) +#define LECORE_FORMAT_CHECKSUM_BYTES ((size_t)8) +#define LECORE_FORMAT_RESERVED_BYTES ((size_t)8) + +static const uint8_t lecore_format_magic[8] = { + (uint8_t)'L', (uint8_t)'E', (uint8_t)'C', (uint8_t)'O', + (uint8_t)'R', (uint8_t)'E', (uint8_t)'V', UINT8_C(0) +}; + +static uint16_t lecore_format_get_u16_le(const uint8_t *bytes) +{ + return (uint16_t)((uint16_t)bytes[0] + | ((uint16_t)bytes[1] << 8)); +} + +static uint32_t lecore_format_get_u32_le(const uint8_t *bytes) +{ + return (uint32_t)((uint32_t)bytes[0] + | ((uint32_t)bytes[1] << 8) + | ((uint32_t)bytes[2] << 16) + | ((uint32_t)bytes[3] << 24)); +} + +static uint64_t lecore_format_get_u64_le(const uint8_t *bytes) +{ + return (uint64_t)bytes[0] + | ((uint64_t)bytes[1] << 8) + | ((uint64_t)bytes[2] << 16) + | ((uint64_t)bytes[3] << 24) + | ((uint64_t)bytes[4] << 32) + | ((uint64_t)bytes[5] << 40) + | ((uint64_t)bytes[6] << 48) + | ((uint64_t)bytes[7] << 56); +} + +static void lecore_format_put_u16_le(uint8_t *bytes, uint16_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); +} + +static void lecore_format_put_u32_le(uint8_t *bytes, uint32_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); + bytes[2] = (uint8_t)(value >> 16); + bytes[3] = (uint8_t)(value >> 24); +} + +static void lecore_format_put_u64_le(uint8_t *bytes, uint64_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); + bytes[2] = (uint8_t)(value >> 16); + bytes[3] = (uint8_t)(value >> 24); + bytes[4] = (uint8_t)(value >> 32); + bytes[5] = (uint8_t)(value >> 40); + bytes[6] = (uint8_t)(value >> 48); + bytes[7] = (uint8_t)(value >> 56); +} + +static int lecore_format_bytes_are_zero(const uint8_t *bytes, size_t count) +{ + size_t index; + + for (index = 0; index < count; ++index) { + if (bytes[index] != UINT8_C(0)) { + return 0; + } + } + return 1; +} + +static int lecore_format_u64s_are_zero(const uint64_t *values, size_t count) +{ + size_t index; + + for (index = 0; index < count; ++index) { + if (values[index] != UINT64_C(0)) { + return 0; + } + } + return 1; +} + +static lecore_status lecore_format_validate_descriptor( + const lecore_format_descriptor_v1 *descriptor, + int encoding) +{ + uint64_t element_count; + uint64_t expected_payload_bytes; + uint64_t scalar_bytes; + + if (descriptor == NULL + || descriptor->struct_size != (uint32_t)sizeof(*descriptor)) { + return LECORE_EINVAL; + } + if (!lecore_format_u64s_are_zero( + descriptor->reserved, + sizeof(descriptor->reserved) / sizeof(descriptor->reserved[0]))) { + return LECORE_EINVAL; + } + if (descriptor->format_major != LECORE_FORMAT_MAJOR) { + return LECORE_EFORMAT; + } + if (descriptor->flags != LECORE_FORMAT_FLAGS_NONE) { + return LECORE_EFORMAT; + } + if (descriptor->header_bytes < LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + if (descriptor->format_minor == LECORE_FORMAT_MINOR + && descriptor->header_bytes != LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + if (encoding + && (descriptor->format_minor != LECORE_FORMAT_MINOR + || descriptor->header_bytes != LECORE_FORMAT_HEADER_BYTES)) { + return LECORE_EUNSUPPORTED; + } + + if (descriptor->artifact_kind != LECORE_ARTIFACT_VECTOR + && descriptor->artifact_kind != LECORE_ARTIFACT_MATRIX + && descriptor->artifact_kind != LECORE_ARTIFACT_TRACE) { + return LECORE_EFORMAT; + } + if (descriptor->semantic_profile == LECORE_PROFILE_HRR_F64_V1) { + if (descriptor->scalar_type != LECORE_SCALAR_F64) { + return LECORE_EPROFILE; + } + scalar_bytes = UINT64_C(8); + } else if (descriptor->semantic_profile == LECORE_PROFILE_HRR_F32_V1) { + if (descriptor->scalar_type != LECORE_SCALAR_F32) { + return LECORE_EPROFILE; + } + scalar_bytes = UINT64_C(4); + } else { + return LECORE_EPROFILE; + } + + if (descriptor->dimension == UINT32_C(0)) { + return LECORE_EDIM; + } + if (descriptor->normalization_policy != LECORE_NORMALIZATION_RAW + && descriptor->normalization_policy != LECORE_NORMALIZATION_UNIT + && descriptor->normalization_policy + != LECORE_NORMALIZATION_APPLICATION) { + return LECORE_EFORMAT; + } + if (descriptor->atom_scheme != LECORE_ATOM_EXTERNAL) { + return LECORE_EFORMAT; + } + if (descriptor->vector_count == UINT64_C(0)) { + return LECORE_EFORMAT; + } + + if (descriptor->vector_count + > UINT64_MAX / (uint64_t)descriptor->dimension) { + return LECORE_EOVERFLOW; + } + element_count = descriptor->vector_count * (uint64_t)descriptor->dimension; + if (element_count > UINT64_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + expected_payload_bytes = element_count * scalar_bytes; + if (descriptor->payload_bytes != expected_payload_bytes) { + return LECORE_EFORMAT; + } + + return LECORE_OK; +} + +static uint64_t lecore_format_record_crc64( + const uint8_t *record, + size_t header_bytes, + size_t payload_bytes) +{ + static const uint8_t zero_checksum[LECORE_FORMAT_CHECKSUM_BYTES] = { + UINT8_C(0), UINT8_C(0), UINT8_C(0), UINT8_C(0), + UINT8_C(0), UINT8_C(0), UINT8_C(0), UINT8_C(0) + }; + uint64_t crc; + + crc = lecore_format_crc64_ecma_update( + UINT64_C(0), record, LECORE_FORMAT_CHECKSUM_OFFSET); + crc = lecore_format_crc64_ecma_update( + crc, zero_checksum, LECORE_FORMAT_CHECKSUM_BYTES); + crc = lecore_format_crc64_ecma_update( + crc, + record + LECORE_FORMAT_RESERVED_OFFSET, + header_bytes - LECORE_FORMAT_RESERVED_OFFSET); + return lecore_format_crc64_ecma_update( + crc, record + header_bytes, payload_bytes); +} + +LECORE_API void LECORE_CALL lecore_format_descriptor_init_v1( + lecore_format_descriptor_v1 *descriptor) +{ + if (descriptor == NULL) { + return; + } + + memset(descriptor, 0, sizeof(*descriptor)); + descriptor->struct_size = (uint32_t)sizeof(*descriptor); + descriptor->format_major = LECORE_FORMAT_MAJOR; + descriptor->format_minor = LECORE_FORMAT_MINOR; + descriptor->header_bytes = LECORE_FORMAT_HEADER_BYTES; + descriptor->semantic_profile = LECORE_PROFILE_HRR_F64_V1; + descriptor->scalar_type = LECORE_SCALAR_F64; + descriptor->normalization_policy = LECORE_NORMALIZATION_RAW; + descriptor->atom_scheme = LECORE_ATOM_EXTERNAL; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_encoded_size_v1( + const lecore_format_descriptor_v1 *descriptor, + size_t *out_record_bytes) +{ + lecore_format_descriptor_v1 descriptor_copy; + lecore_status status; + + if (out_record_bytes == NULL) { + return LECORE_EINVAL; + } + if (descriptor == NULL) { + *out_record_bytes = (size_t)0; + return LECORE_EINVAL; + } + descriptor_copy = *descriptor; + descriptor = &descriptor_copy; + *out_record_bytes = (size_t)0; + + status = lecore_format_validate_descriptor(descriptor, 1); + if (status != LECORE_OK) { + return status; + } + if (descriptor->payload_bytes + > (uint64_t)(SIZE_MAX - (size_t)descriptor->header_bytes)) { + return LECORE_EOVERFLOW; + } + + *out_record_bytes = (size_t)descriptor->header_bytes + + (size_t)descriptor->payload_bytes; + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_encode_v1( + const lecore_format_descriptor_v1 *descriptor, + const void *payload, + size_t payload_bytes, + void *record, + size_t record_capacity, + size_t *out_record_bytes) +{ + lecore_format_descriptor_v1 descriptor_copy; + uint8_t *record_bytes; + uint64_t crc; + size_t required_bytes; + lecore_status status; + + if (out_record_bytes == NULL) { + return LECORE_EINVAL; + } + if (descriptor == NULL) { + *out_record_bytes = (size_t)0; + return LECORE_EINVAL; + } + descriptor_copy = *descriptor; + descriptor = &descriptor_copy; + *out_record_bytes = (size_t)0; + + status = lecore_format_encoded_size_v1(descriptor, &required_bytes); + if (status != LECORE_OK) { + return status; + } + *out_record_bytes = required_bytes; + + if (payload_bytes != (size_t)descriptor->payload_bytes) { + return LECORE_EFORMAT; + } + if (payload == NULL || record == NULL) { + return LECORE_EINVAL; + } + if (record_capacity < required_bytes) { + return LECORE_EINVAL; + } + + record_bytes = (uint8_t *)record; + memmove( + record_bytes + descriptor->header_bytes, + payload, + payload_bytes); + memset(record_bytes, 0, descriptor->header_bytes); + + memcpy( + record_bytes + LECORE_FORMAT_MAGIC_OFFSET, + lecore_format_magic, + sizeof(lecore_format_magic)); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_MAJOR_OFFSET, + descriptor->format_major); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_MINOR_OFFSET, + descriptor->format_minor); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_HEADER_BYTES_OFFSET, + descriptor->header_bytes); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_FLAGS_OFFSET, + descriptor->flags); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_ARTIFACT_KIND_OFFSET, + descriptor->artifact_kind); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET, + descriptor->semantic_profile); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_SCALAR_TYPE_OFFSET, + descriptor->scalar_type); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_DIMENSION_OFFSET, + descriptor->dimension); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_NORMALIZATION_OFFSET, + descriptor->normalization_policy); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_ATOM_SCHEME_OFFSET, + descriptor->atom_scheme); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_SEED_NAMESPACE_OFFSET, + descriptor->seed_namespace); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_VECTOR_COUNT_OFFSET, + descriptor->vector_count); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_PAYLOAD_BYTES_OFFSET, + descriptor->payload_bytes); + memcpy( + record_bytes + LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET, + descriptor->application_contract, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES); + + crc = lecore_format_record_crc64( + record_bytes, + descriptor->header_bytes, + payload_bytes); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_CHECKSUM_OFFSET, + crc); + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_decode_v1( + const void *record, + size_t record_bytes, + lecore_format_descriptor_v1 *out_descriptor, + const void **out_payload, + size_t *out_payload_bytes) +{ + const uint8_t *bytes; + lecore_format_descriptor_v1 descriptor; + uint64_t actual_crc; + size_t payload_bytes; + size_t total_bytes; + lecore_status status; + + if (out_descriptor == NULL || out_payload == NULL + || out_payload_bytes == NULL) { + return LECORE_EINVAL; + } + memset(out_descriptor, 0, sizeof(*out_descriptor)); + out_descriptor->struct_size = (uint32_t)sizeof(*out_descriptor); + *out_payload = NULL; + *out_payload_bytes = (size_t)0; + + if (record == NULL) { + return LECORE_EINVAL; + } + if (record_bytes < (size_t)LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + + bytes = (const uint8_t *)record; + if (memcmp( + bytes + LECORE_FORMAT_MAGIC_OFFSET, + lecore_format_magic, + sizeof(lecore_format_magic)) != 0) { + return LECORE_EFORMAT; + } + + memset(&descriptor, 0, sizeof(descriptor)); + descriptor.struct_size = (uint32_t)sizeof(descriptor); + descriptor.format_major = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_MAJOR_OFFSET); + descriptor.format_minor = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_MINOR_OFFSET); + descriptor.header_bytes = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_HEADER_BYTES_OFFSET); + descriptor.flags = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_FLAGS_OFFSET); + descriptor.artifact_kind = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_ARTIFACT_KIND_OFFSET); + descriptor.semantic_profile = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET); + descriptor.scalar_type = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_SCALAR_TYPE_OFFSET); + descriptor.dimension = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_DIMENSION_OFFSET); + descriptor.normalization_policy = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_NORMALIZATION_OFFSET); + descriptor.atom_scheme = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_ATOM_SCHEME_OFFSET); + descriptor.seed_namespace = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_SEED_NAMESPACE_OFFSET); + descriptor.vector_count = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_VECTOR_COUNT_OFFSET); + descriptor.payload_bytes = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_PAYLOAD_BYTES_OFFSET); + memcpy( + descriptor.application_contract, + bytes + LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES); + descriptor.content_crc64 = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_CHECKSUM_OFFSET); + + if (!lecore_format_bytes_are_zero( + bytes + LECORE_FORMAT_RESERVED_OFFSET, + LECORE_FORMAT_RESERVED_BYTES)) { + return LECORE_EFORMAT; + } + status = lecore_format_validate_descriptor(&descriptor, 0); + if (status != LECORE_OK) { + return status; + } + if ((size_t)descriptor.header_bytes > record_bytes) { + return LECORE_EFORMAT; + } + if (descriptor.payload_bytes + > (uint64_t)(SIZE_MAX - (size_t)descriptor.header_bytes)) { + return LECORE_EOVERFLOW; + } + payload_bytes = (size_t)descriptor.payload_bytes; + total_bytes = (size_t)descriptor.header_bytes + payload_bytes; + if (record_bytes != total_bytes) { + return LECORE_EFORMAT; + } + + actual_crc = lecore_format_record_crc64( + bytes, descriptor.header_bytes, payload_bytes); + if (actual_crc != descriptor.content_crc64) { + return LECORE_ECHECKSUM; + } + + *out_descriptor = descriptor; + *out_payload = bytes + descriptor.header_bytes; + *out_payload_bytes = payload_bytes; + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_check_v1( + const void *record, + size_t record_bytes) +{ + lecore_format_descriptor_v1 descriptor; + const void *payload; + size_t payload_bytes; + + return lecore_format_decode_v1( + record, + record_bytes, + &descriptor, + &payload, + &payload_bytes); +} + +LECORE_API lecore_status LECORE_CALL lecore_format_check_compatibility_v1( + const lecore_format_descriptor_v1 *expected, + const lecore_format_descriptor_v1 *actual) +{ + lecore_status status; + + status = lecore_format_validate_descriptor(expected, 0); + if (status != LECORE_OK) { + return status; + } + status = lecore_format_validate_descriptor(actual, 0); + if (status != LECORE_OK) { + return status; + } + + if (expected->semantic_profile != actual->semantic_profile + || expected->scalar_type != actual->scalar_type) { + return LECORE_EPROFILE; + } + if (expected->dimension != actual->dimension) { + return LECORE_EDIM; + } + if (expected->format_major != actual->format_major + || expected->flags != actual->flags + || expected->artifact_kind != actual->artifact_kind + || expected->normalization_policy != actual->normalization_policy + || expected->atom_scheme != actual->atom_scheme + || expected->seed_namespace != actual->seed_namespace + || expected->vector_count != actual->vector_count + || expected->payload_bytes != actual->payload_bytes + || memcmp( + expected->application_contract, + actual->application_contract, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES) != 0) { + return LECORE_EFORMAT; + } + + return LECORE_OK; +} +#endif /* LECORE_ENABLE_FORMAT */ diff --git a/native/liblecore/amalgamation/lecore.h b/native/liblecore/amalgamation/lecore.h new file mode 100644 index 0000000..1ba9f07 --- /dev/null +++ b/native/liblecore/amalgamation/lecore.h @@ -0,0 +1,606 @@ +/* + * SPDX-License-Identifier: MIT + * + * liblecore 0.1.0 amalgamation (ABI 0, ISA 1) + * Generated by tools/generate_liblecore_amalgamation.py; DO NOT EDIT. + * Canonical-source SHA-256: 04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269 + * + * This is a mechanical distribution of the clean-room canonical sources. + * Provenance details live in native/liblecore/PROVENANCE.md. + * Generated from: + * - LICENSE + * - native/liblecore/VERSION + * - native/liblecore/ISA_VERSION + * - native/liblecore/PROVENANCE.md + * - native/liblecore/include/lecore/lecore.h + * - native/liblecore/include/lecore/lecore_format.h + * - native/liblecore/src/internal/lecore_internal.h + * - native/liblecore/src/internal/format_internal.h + * - native/liblecore/src/context.c + * - native/liblecore/src/status.c + * - native/liblecore/src/vector_f32.c + * - native/liblecore/src/vector_f64.c + * - native/liblecore/src/cleanup_f32.c + * - native/liblecore/src/cleanup_f64.c + * - native/liblecore/src/hrr_direct_f32.c + * - native/liblecore/src/hrr_direct_f64.c + * - native/liblecore/src/hrr_radix2_f32.c + * - native/liblecore/src/hrr_radix2_f64.c + * - native/liblecore/src/crc64.c + * - native/liblecore/src/format.c + * + * MIT License + * + * Copyright (c) 2026 AnOversizedMooseWithSocks + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LECORE_AMALGAMATION_H +#define LECORE_AMALGAMATION_H + +#define LECORE_AMALGAMATION 1 +#define LECORE_AMALGAMATION_SOURCE_SHA256 "04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269" + + +#ifndef LECORE_VERSION_STRING_VALUE +# define LECORE_VERSION_STRING_VALUE "0.1.0" +#endif +#ifndef LECORE_ISA_VERSION_VALUE +# define LECORE_ISA_VERSION_VALUE 1 +#endif +#ifndef LECORE_ABI_VERSION_VALUE +# define LECORE_ABI_VERSION_VALUE 0 +#endif +#ifndef LECORE_ENABLE_FORMAT +# define LECORE_ENABLE_FORMAT 1 +#endif +#ifndef LECORE_ENABLE_RADIX2 +# define LECORE_ENABLE_RADIX2 1 +#endif + +#line 1 "native/liblecore/include/lecore/lecore.h" +#ifndef LECORE_LECORE_H +#define LECORE_LECORE_H + +#include +#include +#include +#include + +#if CHAR_BIT != 8 +# error "liblecore profiles require 8-bit bytes" +#endif + +#if FLT_RADIX != 2 +# error "liblecore profiles require radix-2 floating point" +#endif + +#if FLT_MANT_DIG != 24 || FLT_MIN_EXP != -125 || FLT_MAX_EXP != 128 +# error "liblecore HRR_F32_V1 requires IEEE-compatible binary32" +#endif + +#if DBL_MANT_DIG != 53 || DBL_MIN_EXP != -1021 || DBL_MAX_EXP != 1024 +# error "liblecore HRR_F64_V1 requires IEEE-compatible binary64" +#endif + +#if FLT_EVAL_METHOD != 0 +# error "liblecore profiles require operations evaluated in their declared precision" +#endif + +#if defined(__cplusplus) +static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#else +_Static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +_Static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(LECORE_SHARED) +# if defined(LECORE_BUILDING_LIBRARY) +# define LECORE_API __declspec(dllexport) +# else +# define LECORE_API __declspec(dllimport) +# endif +# else +# define LECORE_API +# endif +# if defined(_MSC_VER) +# define LECORE_CALL __cdecl +# else +# define LECORE_CALL +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define LECORE_API __attribute__((visibility("default"))) +# define LECORE_CALL +#else +# define LECORE_API +# define LECORE_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is an intentionally unstable 0.x preview ABI. */ +#ifndef LECORE_ABI_VERSION_VALUE +# define LECORE_ABI_VERSION_VALUE 0 +#endif +#define LECORE_ABI_VERSION ((uint32_t)LECORE_ABI_VERSION_VALUE) + +#ifndef LECORE_ISA_VERSION_VALUE +# define LECORE_ISA_VERSION_VALUE 1 +#endif +#define LECORE_ISA_VERSION ((uint32_t)LECORE_ISA_VERSION_VALUE) + +#ifndef LECORE_VERSION_STRING_VALUE +# define LECORE_VERSION_STRING_VALUE "0.1.0" +#endif + +typedef uint32_t lecore_status; +#define LECORE_OK UINT32_C(0) +#define LECORE_EINVAL UINT32_C(1) +#define LECORE_EDIM UINT32_C(2) +#define LECORE_EPROFILE UINT32_C(3) +#define LECORE_EBACKEND UINT32_C(4) +#define LECORE_EOVERFLOW UINT32_C(5) +#define LECORE_ENOMEM UINT32_C(6) +#define LECORE_EUNSUPPORTED UINT32_C(7) +#define LECORE_ENONFINITE UINT32_C(8) +#define LECORE_EFORMAT UINT32_C(9) +#define LECORE_ECHECKSUM UINT32_C(10) + +typedef uint32_t lecore_profile; +#define LECORE_PROFILE_HRR_F64_V1 UINT32_C(0x00010001) +#define LECORE_PROFILE_HRR_F32_V1 UINT32_C(0x00010002) + +typedef uint32_t lecore_backend; +/* AUTO is conservatively DIRECT in ABI 0; RADIX2 requires its capability bit + * and a power-of-two dimension. Forced unsupported selection fails loudly. */ +#define LECORE_BACKEND_AUTO UINT32_C(0) +#define LECORE_BACKEND_DIRECT UINT32_C(1) +#define LECORE_BACKEND_RADIX2 UINT32_C(2) + +typedef uint32_t lecore_validation; +#define LECORE_VALIDATION_SHAPE UINT32_C(0) +#define LECORE_VALIDATION_FINITE UINT32_C(1) + +typedef uint32_t lecore_scalar_type; +#define LECORE_SCALAR_F64 UINT32_C(1) +#define LECORE_SCALAR_F32 UINT32_C(2) + +/* Capability bits returned by lecore_capabilities(). */ +#define LECORE_CAP_HRR_F64 (UINT64_C(1) << 0) +#define LECORE_CAP_HRR_F32 (UINT64_C(1) << 1) +#define LECORE_CAP_DIRECT (UINT64_C(1) << 2) +#define LECORE_CAP_RADIX2 (UINT64_C(1) << 3) +#define LECORE_CAP_BATCH (UINT64_C(1) << 4) +#define LECORE_CAP_MIXED_F64_F32 (UINT64_C(1) << 5) +#define LECORE_CAP_FINITE_VALIDATION (UINT64_C(1) << 6) +#define LECORE_CAP_FORMAT (UINT64_C(1) << 7) + +/* Supply both callbacks or neither. Allocation requests occur only during + * context creation, have nonzero byte counts and power-of-two alignment, and + * must return NULL or a suitably aligned pointer. Every successful request is + * released exactly once with the identical user/bytes/alignment tuple during + * failed creation or context destruction. */ +typedef struct lecore_allocator_v0 { + uint32_t struct_size; + void *user; + void *(LECORE_CALL *allocate)(void *user, size_t bytes, size_t alignment); + void (LECORE_CALL *deallocate)( + void *user, + void *pointer, + size_t bytes, + size_t alignment); +} lecore_allocator_v0; + +typedef struct lecore_config_v0 { + uint32_t struct_size; + uint32_t abi_version; + lecore_profile profile; + lecore_backend backend; + lecore_validation validation; + uint32_t flags; + uint32_t dimension; + lecore_allocator_v0 allocator; + uint64_t reserved[4]; +} lecore_config_v0; + +typedef struct lecore_context lecore_context; + +LECORE_API uint32_t LECORE_CALL lecore_abi_version(void); +LECORE_API uint32_t LECORE_CALL lecore_isa_version(void); +LECORE_API const char *LECORE_CALL lecore_version_string(void); +LECORE_API uint64_t LECORE_CALL lecore_capabilities(void); +LECORE_API const char *LECORE_CALL lecore_status_string(lecore_status status); + +/* Initializes the complete preview-0 configuration. A NULL argument is ignored. */ +LECORE_API void LECORE_CALL lecore_config_init_v0(lecore_config_v0 *config); +LECORE_API lecore_status LECORE_CALL lecore_context_create( + const lecore_config_v0 *config, + lecore_context **out_context); +/* Context calls require exclusive single-thread ownership. Destruction is the + * sole thread-neutral operation: it may run on another thread only after every + * call using the context has quiesced. A custom deallocate callback must be + * valid on the destroying thread. Concurrent destroy/use is invalid. */ +LECORE_API void LECORE_CALL lecore_context_destroy(lecore_context *context); + +LECORE_API uint32_t LECORE_CALL lecore_context_dimension( + const lecore_context *context); +LECORE_API lecore_profile LECORE_CALL lecore_context_profile( + const lecore_context *context); +LECORE_API lecore_backend LECORE_CALL lecore_context_backend( + const lecore_context *context); +LECORE_API lecore_validation LECORE_CALL lecore_context_validation( + const lecore_context *context); +LECORE_API lecore_scalar_type LECORE_CALL lecore_context_scalar_type( + const lecore_context *context); +LECORE_API size_t LECORE_CALL lecore_context_scratch_bytes( + const lecore_context *context); + +/* Standalone ingestion-boundary checks. Empty ranges are valid. */ +LECORE_API lecore_status LECORE_CALL lecore_validate_f64( + const double *values, + size_t count); +LECORE_API lecore_status LECORE_CALL lecore_validate_f32( + const float *values, + size_t count); + +/* + * Vector arguments contain context->dimension elements. Exact input/output + * aliasing is supported by normalize, involution, permute, bind, and unbind. + * Partial overlap is rejected. Scalar outputs are written only after inputs + * have been consumed. + */ +LECORE_API lecore_status LECORE_CALL lecore_normalize_f64( + lecore_context *context, + const double *input, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_normalize_f32( + lecore_context *context, + const float *input, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_dot_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_dot); +LECORE_API lecore_status LECORE_CALL lecore_dot_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_dot); + +LECORE_API lecore_status LECORE_CALL lecore_dot_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores); +LECORE_API lecore_status LECORE_CALL lecore_dot_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores); + +LECORE_API lecore_status LECORE_CALL lecore_cosine_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_cosine); +LECORE_API lecore_status LECORE_CALL lecore_cosine_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_cosine); + +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores); +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_involution_f64( + lecore_context *context, + const double *input, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_involution_f32( + lecore_context *context, + const float *input, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_permute_f64( + lecore_context *context, + const double *input, + int64_t shift, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_permute_f32( + lecore_context *context, + const float *input, + int64_t shift, + float *output); + +/* Matrix rows and strides are measured in scalar elements, not bytes. */ +LECORE_API lecore_status LECORE_CALL lecore_bundle_f64( + lecore_context *context, + const double *rows, + size_t row_count, + size_t row_stride, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_bundle_f32( + lecore_context *context, + const float *rows, + size_t row_count, + size_t row_stride, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_cleanup_f64( + lecore_context *context, + const double *query, + const double *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + double *out_score); +LECORE_API lecore_status LECORE_CALL lecore_cleanup_f32( + lecore_context *context, + const float *query, + const float *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + float *out_score); + +/* Batch input and output matrices must be disjoint in preview ABI 0. */ +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_batch_f64( + lecore_context *context, + const double *a_rows, + size_t a_stride, + const double *b_rows, + size_t b_stride, + size_t row_count, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_batch_f32( + lecore_context *context, + const float *a_rows, + size_t a_stride, + const float *b_rows, + size_t b_stride, + size_t row_count, + float *out_rows, + size_t out_stride); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride); + +/* NoSQLite utility: f64 query, f32 rows, ascending-order f64 reductions. */ +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f64_f32( + lecore_context *f32_context, + const double *query, + const float *rows, + size_t row_count, + size_t row_stride, + double *out_scores); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LECORE_LECORE_H */ + +#if LECORE_ENABLE_FORMAT + +#line 1 "native/liblecore/include/lecore/lecore_format.h" +#ifndef LECORE_LECORE_FORMAT_H +#define LECORE_LECORE_FORMAT_H + +#include +#include + + + +#ifdef __cplusplus +extern "C" { +#endif + +#define LECORE_FORMAT_MAJOR UINT16_C(1) +#define LECORE_FORMAT_MINOR UINT16_C(0) +#define LECORE_FORMAT_HEADER_BYTES UINT16_C(96) +#define LECORE_FORMAT_APPLICATION_CONTRACT_BYTES UINT32_C(16) + +typedef uint32_t lecore_artifact_kind; +#define LECORE_ARTIFACT_VECTOR UINT32_C(1) +#define LECORE_ARTIFACT_MATRIX UINT32_C(2) +#define LECORE_ARTIFACT_TRACE UINT32_C(3) + +typedef uint32_t lecore_normalization_policy; +#define LECORE_NORMALIZATION_RAW UINT32_C(0) +#define LECORE_NORMALIZATION_UNIT UINT32_C(1) +#define LECORE_NORMALIZATION_APPLICATION UINT32_C(2) + +typedef uint32_t lecore_atom_scheme; +#define LECORE_ATOM_EXTERNAL UINT32_C(0) + +/* No format flags are defined in version 1. */ +#define LECORE_FORMAT_FLAGS_NONE UINT16_C(0) + +/* + * An in-memory view of the portable descriptor. This structure is not the + * wire representation; the codec emits and consumes fields explicitly in + * little-endian order. + * + * content_crc64 is populated by decode and ignored by encode, which always + * recomputes it. struct_size must equal sizeof(lecore_format_descriptor_v1), + * and reserved must remain zero when a descriptor is encoded. + */ +typedef struct lecore_format_descriptor_v1 { + uint32_t struct_size; + uint16_t format_major; + uint16_t format_minor; + uint16_t header_bytes; + uint16_t flags; + lecore_artifact_kind artifact_kind; + lecore_profile semantic_profile; + lecore_scalar_type scalar_type; + uint32_t dimension; + lecore_normalization_policy normalization_policy; + lecore_atom_scheme atom_scheme; + uint64_t seed_namespace; + uint64_t vector_count; + uint64_t payload_bytes; + uint8_t application_contract[LECORE_FORMAT_APPLICATION_CONTRACT_BYTES]; + uint64_t content_crc64; + uint64_t reserved[4]; +} lecore_format_descriptor_v1; + +/* Initialize a descriptor for current-format raw, externally supplied data. */ +LECORE_API void LECORE_CALL lecore_format_descriptor_init_v1( + lecore_format_descriptor_v1 *descriptor); + +/* + * Validate descriptor invariants and return header_bytes + payload_bytes. + * The output is zeroed before validation and remains zero on failure. The + * output scalar must not overlap the descriptor. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_encoded_size_v1( + const lecore_format_descriptor_v1 *descriptor, + size_t *out_record_bytes); + +/* + * Encode one descriptor and payload into caller-owned storage. The payload is + * copied verbatim and therefore must already contain little-endian IEEE-754 + * bits. The descriptor and payload may overlap the record; the codec snapshots + * metadata and moves payload bytes before emitting the header. Output scalar + * arguments must not overlap those buffers. out_record_bytes receives the + * required size after descriptor validation, including when record_capacity + * is too small. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_encode_v1( + const lecore_format_descriptor_v1 *descriptor, + const void *payload, + size_t payload_bytes, + void *record, + size_t record_capacity, + size_t *out_record_bytes); + +/* + * Decode and fully validate an exact record, including CRC. On success, + * out_payload points at the raw little-endian payload within record and + * remains valid only as long as record. Output values are initialized to + * empty values before record validation. The record and all three output + * objects must occupy mutually disjoint storage. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_decode_v1( + const void *record, + size_t record_bytes, + lecore_format_descriptor_v1 *out_descriptor, + const void **out_payload, + size_t *out_payload_bytes); + +/* Validate an exact record without returning a payload view. */ +LECORE_API lecore_status LECORE_CALL lecore_format_check_v1( + const void *record, + size_t record_bytes); + +/* + * Check that two individually valid descriptors describe the same artifact + * contract. Format minor/header length and CRC may differ; artifact shape, + * profile, policies, namespace, and the 16 contract bytes must match exactly. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_check_compatibility_v1( + const lecore_format_descriptor_v1 *expected, + const lecore_format_descriptor_v1 *actual); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LECORE_LECORE_FORMAT_H */ +#endif /* LECORE_ENABLE_FORMAT */ + +#endif /* LECORE_AMALGAMATION_H */ diff --git a/native/liblecore/cmake/lecoreConfig.cmake.in b/native/liblecore/cmake/lecoreConfig.cmake.in new file mode 100644 index 0000000..625a4c6 --- /dev/null +++ b/native/liblecore/cmake/lecoreConfig.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/lecoreTargets.cmake") + +check_required_components(lecore) diff --git a/native/liblecore/examples/CMakeLists.txt b/native/liblecore/examples/CMakeLists.txt new file mode 100644 index 0000000..a6bfe70 --- /dev/null +++ b/native/liblecore/examples/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(liblecore_hrr_example hrr_example.c) +target_compile_features(liblecore_hrr_example PRIVATE c_std_11) +target_link_libraries(liblecore_hrr_example PRIVATE lecore::lecore) + +add_executable(liblecore_hrr_cpp_example hrr_example.cpp) +target_compile_features(liblecore_hrr_cpp_example PRIVATE cxx_std_17) +target_link_libraries(liblecore_hrr_cpp_example PRIVATE lecore::lecore) + +if(WIN32 AND LECORE_BUILD_SHARED) + foreach(example_target IN ITEMS + liblecore_hrr_example liblecore_hrr_cpp_example) + add_custom_command(TARGET "${example_target}" POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$" + VERBATIM + ) + endforeach() +endif() diff --git a/native/liblecore/examples/hrr_example.c b/native/liblecore/examples/hrr_example.c new file mode 100644 index 0000000..e04de38 --- /dev/null +++ b/native/liblecore/examples/hrr_example.c @@ -0,0 +1,36 @@ +#include + +#include + +int main(void) +{ + const double role[4] = {1.0, 2.0, 0.0, -1.0}; + const double value[4] = {2.0, 0.0, 1.0, 0.0}; + double composite[4]; + lecore_config_v0 config; + lecore_context *context = NULL; + lecore_status status; + size_t index; + + lecore_config_init_v0(&config); + config.dimension = 4; + status = lecore_context_create(&config, &context); + if (status != LECORE_OK) { + fprintf(stderr, "context: %s\n", lecore_status_string(status)); + return 1; + } + + status = lecore_hrr_bind_f64(context, role, value, composite); + if (status != LECORE_OK) { + fprintf(stderr, "bind: %s\n", lecore_status_string(status)); + lecore_context_destroy(context); + return 1; + } + + for (index = 0; index < 4; ++index) { + printf("%s%.6f", index == 0 ? "" : " ", composite[index]); + } + putchar('\n'); + lecore_context_destroy(context); + return 0; +} diff --git a/native/liblecore/examples/hrr_example.cpp b/native/liblecore/examples/hrr_example.cpp new file mode 100644 index 0000000..3445b26 --- /dev/null +++ b/native/liblecore/examples/hrr_example.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include +#include + +struct ContextDeleter { + void operator()(lecore_context *context) const noexcept + { + lecore_context_destroy(context); + } +}; + +using Context = std::unique_ptr; + +int main() +{ + const std::array role{1.0, 2.0, 0.0, -1.0}; + const std::array value{2.0, 0.0, 1.0, 0.0}; + std::array composite{}; + lecore_config_v0 config{}; + lecore_context *raw_context = nullptr; + + lecore_config_init_v0(&config); + config.dimension = static_cast(role.size()); + const lecore_status create_status = + lecore_context_create(&config, &raw_context); + if (create_status != LECORE_OK) { + throw std::runtime_error(lecore_status_string(create_status)); + } + Context context{raw_context}; + + const lecore_status bind_status = lecore_hrr_bind_f64( + context.get(), role.data(), value.data(), composite.data()); + if (bind_status != LECORE_OK) { + throw std::runtime_error(lecore_status_string(bind_status)); + } + + for (const double component : composite) { + std::cout << component << ' '; + } + std::cout << '\n'; + return 0; +} diff --git a/native/liblecore/include/lecore/lecore.h b/native/liblecore/include/lecore/lecore.h new file mode 100644 index 0000000..3e2b9ac --- /dev/null +++ b/native/liblecore/include/lecore/lecore.h @@ -0,0 +1,396 @@ +#ifndef LECORE_LECORE_H +#define LECORE_LECORE_H + +#include +#include +#include +#include + +#if CHAR_BIT != 8 +# error "liblecore profiles require 8-bit bytes" +#endif + +#if FLT_RADIX != 2 +# error "liblecore profiles require radix-2 floating point" +#endif + +#if FLT_MANT_DIG != 24 || FLT_MIN_EXP != -125 || FLT_MAX_EXP != 128 +# error "liblecore HRR_F32_V1 requires IEEE-compatible binary32" +#endif + +#if DBL_MANT_DIG != 53 || DBL_MIN_EXP != -1021 || DBL_MAX_EXP != 1024 +# error "liblecore HRR_F64_V1 requires IEEE-compatible binary64" +#endif + +#if FLT_EVAL_METHOD != 0 +# error "liblecore profiles require operations evaluated in their declared precision" +#endif + +#if defined(__cplusplus) +static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#else +_Static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +_Static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(LECORE_SHARED) +# if defined(LECORE_BUILDING_LIBRARY) +# define LECORE_API __declspec(dllexport) +# else +# define LECORE_API __declspec(dllimport) +# endif +# else +# define LECORE_API +# endif +# if defined(_MSC_VER) +# define LECORE_CALL __cdecl +# else +# define LECORE_CALL +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define LECORE_API __attribute__((visibility("default"))) +# define LECORE_CALL +#else +# define LECORE_API +# define LECORE_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is an intentionally unstable 0.x preview ABI. */ +#ifndef LECORE_ABI_VERSION_VALUE +# define LECORE_ABI_VERSION_VALUE 0 +#endif +#define LECORE_ABI_VERSION ((uint32_t)LECORE_ABI_VERSION_VALUE) + +#ifndef LECORE_ISA_VERSION_VALUE +# define LECORE_ISA_VERSION_VALUE 1 +#endif +#define LECORE_ISA_VERSION ((uint32_t)LECORE_ISA_VERSION_VALUE) + +#ifndef LECORE_VERSION_STRING_VALUE +# define LECORE_VERSION_STRING_VALUE "0.1.0" +#endif + +typedef uint32_t lecore_status; +#define LECORE_OK UINT32_C(0) +#define LECORE_EINVAL UINT32_C(1) +#define LECORE_EDIM UINT32_C(2) +#define LECORE_EPROFILE UINT32_C(3) +#define LECORE_EBACKEND UINT32_C(4) +#define LECORE_EOVERFLOW UINT32_C(5) +#define LECORE_ENOMEM UINT32_C(6) +#define LECORE_EUNSUPPORTED UINT32_C(7) +#define LECORE_ENONFINITE UINT32_C(8) +#define LECORE_EFORMAT UINT32_C(9) +#define LECORE_ECHECKSUM UINT32_C(10) + +typedef uint32_t lecore_profile; +#define LECORE_PROFILE_HRR_F64_V1 UINT32_C(0x00010001) +#define LECORE_PROFILE_HRR_F32_V1 UINT32_C(0x00010002) + +typedef uint32_t lecore_backend; +/* AUTO is conservatively DIRECT in ABI 0; RADIX2 requires its capability bit + * and a power-of-two dimension. Forced unsupported selection fails loudly. */ +#define LECORE_BACKEND_AUTO UINT32_C(0) +#define LECORE_BACKEND_DIRECT UINT32_C(1) +#define LECORE_BACKEND_RADIX2 UINT32_C(2) + +typedef uint32_t lecore_validation; +#define LECORE_VALIDATION_SHAPE UINT32_C(0) +#define LECORE_VALIDATION_FINITE UINT32_C(1) + +typedef uint32_t lecore_scalar_type; +#define LECORE_SCALAR_F64 UINT32_C(1) +#define LECORE_SCALAR_F32 UINT32_C(2) + +/* Capability bits returned by lecore_capabilities(). */ +#define LECORE_CAP_HRR_F64 (UINT64_C(1) << 0) +#define LECORE_CAP_HRR_F32 (UINT64_C(1) << 1) +#define LECORE_CAP_DIRECT (UINT64_C(1) << 2) +#define LECORE_CAP_RADIX2 (UINT64_C(1) << 3) +#define LECORE_CAP_BATCH (UINT64_C(1) << 4) +#define LECORE_CAP_MIXED_F64_F32 (UINT64_C(1) << 5) +#define LECORE_CAP_FINITE_VALIDATION (UINT64_C(1) << 6) +#define LECORE_CAP_FORMAT (UINT64_C(1) << 7) + +/* Supply both callbacks or neither. Allocation requests occur only during + * context creation, have nonzero byte counts and power-of-two alignment, and + * must return NULL or a suitably aligned pointer. Every successful request is + * released exactly once with the identical user/bytes/alignment tuple during + * failed creation or context destruction. */ +typedef struct lecore_allocator_v0 { + uint32_t struct_size; + void *user; + void *(LECORE_CALL *allocate)(void *user, size_t bytes, size_t alignment); + void (LECORE_CALL *deallocate)( + void *user, + void *pointer, + size_t bytes, + size_t alignment); +} lecore_allocator_v0; + +typedef struct lecore_config_v0 { + uint32_t struct_size; + uint32_t abi_version; + lecore_profile profile; + lecore_backend backend; + lecore_validation validation; + uint32_t flags; + uint32_t dimension; + lecore_allocator_v0 allocator; + uint64_t reserved[4]; +} lecore_config_v0; + +typedef struct lecore_context lecore_context; + +LECORE_API uint32_t LECORE_CALL lecore_abi_version(void); +LECORE_API uint32_t LECORE_CALL lecore_isa_version(void); +LECORE_API const char *LECORE_CALL lecore_version_string(void); +LECORE_API uint64_t LECORE_CALL lecore_capabilities(void); +LECORE_API const char *LECORE_CALL lecore_status_string(lecore_status status); + +/* Initializes the complete preview-0 configuration. A NULL argument is ignored. */ +LECORE_API void LECORE_CALL lecore_config_init_v0(lecore_config_v0 *config); +LECORE_API lecore_status LECORE_CALL lecore_context_create( + const lecore_config_v0 *config, + lecore_context **out_context); +/* Context calls require exclusive single-thread ownership. Destruction is the + * sole thread-neutral operation: it may run on another thread only after every + * call using the context has quiesced. A custom deallocate callback must be + * valid on the destroying thread. Concurrent destroy/use is invalid. */ +LECORE_API void LECORE_CALL lecore_context_destroy(lecore_context *context); + +LECORE_API uint32_t LECORE_CALL lecore_context_dimension( + const lecore_context *context); +LECORE_API lecore_profile LECORE_CALL lecore_context_profile( + const lecore_context *context); +LECORE_API lecore_backend LECORE_CALL lecore_context_backend( + const lecore_context *context); +LECORE_API lecore_validation LECORE_CALL lecore_context_validation( + const lecore_context *context); +LECORE_API lecore_scalar_type LECORE_CALL lecore_context_scalar_type( + const lecore_context *context); +LECORE_API size_t LECORE_CALL lecore_context_scratch_bytes( + const lecore_context *context); + +/* Standalone ingestion-boundary checks. Empty ranges are valid. */ +LECORE_API lecore_status LECORE_CALL lecore_validate_f64( + const double *values, + size_t count); +LECORE_API lecore_status LECORE_CALL lecore_validate_f32( + const float *values, + size_t count); + +/* + * Vector arguments contain context->dimension elements. Exact input/output + * aliasing is supported by normalize, involution, permute, bind, and unbind. + * Partial overlap is rejected. Scalar outputs are written only after inputs + * have been consumed. + */ +LECORE_API lecore_status LECORE_CALL lecore_normalize_f64( + lecore_context *context, + const double *input, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_normalize_f32( + lecore_context *context, + const float *input, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_dot_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_dot); +LECORE_API lecore_status LECORE_CALL lecore_dot_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_dot); + +LECORE_API lecore_status LECORE_CALL lecore_dot_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores); +LECORE_API lecore_status LECORE_CALL lecore_dot_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores); + +LECORE_API lecore_status LECORE_CALL lecore_cosine_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_cosine); +LECORE_API lecore_status LECORE_CALL lecore_cosine_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_cosine); + +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores); +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_involution_f64( + lecore_context *context, + const double *input, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_involution_f32( + lecore_context *context, + const float *input, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_permute_f64( + lecore_context *context, + const double *input, + int64_t shift, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_permute_f32( + lecore_context *context, + const float *input, + int64_t shift, + float *output); + +/* Matrix rows and strides are measured in scalar elements, not bytes. */ +LECORE_API lecore_status LECORE_CALL lecore_bundle_f64( + lecore_context *context, + const double *rows, + size_t row_count, + size_t row_stride, + double *output); +LECORE_API lecore_status LECORE_CALL lecore_bundle_f32( + lecore_context *context, + const float *rows, + size_t row_count, + size_t row_stride, + float *output); + +LECORE_API lecore_status LECORE_CALL lecore_cleanup_f64( + lecore_context *context, + const double *query, + const double *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + double *out_score); +LECORE_API lecore_status LECORE_CALL lecore_cleanup_f32( + lecore_context *context, + const float *query, + const float *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + float *out_score); + +/* Batch input and output matrices must be disjoint in preview ABI 0. */ +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_batch_f64( + lecore_context *context, + const double *a_rows, + size_t a_stride, + const double *b_rows, + size_t b_stride, + size_t row_count, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_batch_f32( + lecore_context *context, + const float *a_rows, + size_t a_stride, + const float *b_rows, + size_t b_stride, + size_t row_count, + float *out_rows, + size_t out_stride); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride); + +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride); +LECORE_API lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride); + +/* NoSQLite utility: f64 query, f32 rows, ascending-order f64 reductions. */ +LECORE_API lecore_status LECORE_CALL lecore_cosine_many_f64_f32( + lecore_context *f32_context, + const double *query, + const float *rows, + size_t row_count, + size_t row_stride, + double *out_scores); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LECORE_LECORE_H */ diff --git a/native/liblecore/include/lecore/lecore_format.h b/native/liblecore/include/lecore/lecore_format.h new file mode 100644 index 0000000..103b499 --- /dev/null +++ b/native/liblecore/include/lecore/lecore_format.h @@ -0,0 +1,125 @@ +#ifndef LECORE_LECORE_FORMAT_H +#define LECORE_LECORE_FORMAT_H + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LECORE_FORMAT_MAJOR UINT16_C(1) +#define LECORE_FORMAT_MINOR UINT16_C(0) +#define LECORE_FORMAT_HEADER_BYTES UINT16_C(96) +#define LECORE_FORMAT_APPLICATION_CONTRACT_BYTES UINT32_C(16) + +typedef uint32_t lecore_artifact_kind; +#define LECORE_ARTIFACT_VECTOR UINT32_C(1) +#define LECORE_ARTIFACT_MATRIX UINT32_C(2) +#define LECORE_ARTIFACT_TRACE UINT32_C(3) + +typedef uint32_t lecore_normalization_policy; +#define LECORE_NORMALIZATION_RAW UINT32_C(0) +#define LECORE_NORMALIZATION_UNIT UINT32_C(1) +#define LECORE_NORMALIZATION_APPLICATION UINT32_C(2) + +typedef uint32_t lecore_atom_scheme; +#define LECORE_ATOM_EXTERNAL UINT32_C(0) + +/* No format flags are defined in version 1. */ +#define LECORE_FORMAT_FLAGS_NONE UINT16_C(0) + +/* + * An in-memory view of the portable descriptor. This structure is not the + * wire representation; the codec emits and consumes fields explicitly in + * little-endian order. + * + * content_crc64 is populated by decode and ignored by encode, which always + * recomputes it. struct_size must equal sizeof(lecore_format_descriptor_v1), + * and reserved must remain zero when a descriptor is encoded. + */ +typedef struct lecore_format_descriptor_v1 { + uint32_t struct_size; + uint16_t format_major; + uint16_t format_minor; + uint16_t header_bytes; + uint16_t flags; + lecore_artifact_kind artifact_kind; + lecore_profile semantic_profile; + lecore_scalar_type scalar_type; + uint32_t dimension; + lecore_normalization_policy normalization_policy; + lecore_atom_scheme atom_scheme; + uint64_t seed_namespace; + uint64_t vector_count; + uint64_t payload_bytes; + uint8_t application_contract[LECORE_FORMAT_APPLICATION_CONTRACT_BYTES]; + uint64_t content_crc64; + uint64_t reserved[4]; +} lecore_format_descriptor_v1; + +/* Initialize a descriptor for current-format raw, externally supplied data. */ +LECORE_API void LECORE_CALL lecore_format_descriptor_init_v1( + lecore_format_descriptor_v1 *descriptor); + +/* + * Validate descriptor invariants and return header_bytes + payload_bytes. + * The output is zeroed before validation and remains zero on failure. The + * output scalar must not overlap the descriptor. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_encoded_size_v1( + const lecore_format_descriptor_v1 *descriptor, + size_t *out_record_bytes); + +/* + * Encode one descriptor and payload into caller-owned storage. The payload is + * copied verbatim and therefore must already contain little-endian IEEE-754 + * bits. The descriptor and payload may overlap the record; the codec snapshots + * metadata and moves payload bytes before emitting the header. Output scalar + * arguments must not overlap those buffers. out_record_bytes receives the + * required size after descriptor validation, including when record_capacity + * is too small. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_encode_v1( + const lecore_format_descriptor_v1 *descriptor, + const void *payload, + size_t payload_bytes, + void *record, + size_t record_capacity, + size_t *out_record_bytes); + +/* + * Decode and fully validate an exact record, including CRC. On success, + * out_payload points at the raw little-endian payload within record and + * remains valid only as long as record. Output values are initialized to + * empty values before record validation. The record and all three output + * objects must occupy mutually disjoint storage. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_decode_v1( + const void *record, + size_t record_bytes, + lecore_format_descriptor_v1 *out_descriptor, + const void **out_payload, + size_t *out_payload_bytes); + +/* Validate an exact record without returning a payload view. */ +LECORE_API lecore_status LECORE_CALL lecore_format_check_v1( + const void *record, + size_t record_bytes); + +/* + * Check that two individually valid descriptors describe the same artifact + * contract. Format minor/header length and CRC may differ; artifact shape, + * profile, policies, namespace, and the 16 contract bytes must match exactly. + */ +LECORE_API lecore_status LECORE_CALL lecore_format_check_compatibility_v1( + const lecore_format_descriptor_v1 *expected, + const lecore_format_descriptor_v1 *actual); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LECORE_LECORE_FORMAT_H */ diff --git a/native/liblecore/pkgconfig/liblecore.pc.in b/native/liblecore/pkgconfig/liblecore.pc.in new file mode 100644 index 0000000..009659b --- /dev/null +++ b/native/liblecore/pkgconfig/liblecore.pc.in @@ -0,0 +1,12 @@ +prefix=@LECORE_PC_PREFIX@ +exec_prefix=${prefix} +libdir=@LECORE_PC_LIBDIR@ +includedir=@LECORE_PC_INCLUDEDIR@ + +Name: liblecore +Description: Portable leCore vector algebra kernel +Version: @LECORE_VERSION@ +URL: https://github.com/AnOversizedMooseWithSocks/leCore +Libs: -L${libdir} -llecore @LECORE_PC_PUBLIC_LIBS@ +Libs.private: @LECORE_PC_PRIVATE_LIBS@ +Cflags: -I${includedir} @LECORE_PC_CFLAGS@ diff --git a/native/liblecore/src/cleanup_f32.c b/native/liblecore/src/cleanup_f32.c new file mode 100644 index 0000000..267b714 --- /dev/null +++ b/native/liblecore/src/cleanup_f32.c @@ -0,0 +1,133 @@ +#include "internal/lecore_internal.h" + +#include + +static int lecore_cleanup_f32_vector_is_finite( + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static float lecore_cleanup_f32_cosine( + const float *query, + float query_norm_squared, + float query_norm, + const float *candidate, + uint32_t dimension) +{ + float candidate_norm_squared = 0.0f; + float candidate_norm; + float dot = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + candidate_norm_squared += candidate[index] * candidate[index]; + } + if (query_norm_squared == 0.0f || candidate_norm_squared == 0.0f) { + return 0.0f; + } + for (index = 0; index < dimension; ++index) { + dot += query[index] * candidate[index]; + } + candidate_norm = sqrtf(candidate_norm_squared); + return dot / (query_norm * candidate_norm); +} + +lecore_status LECORE_CALL lecore_cleanup_f32( + lecore_context *context, + const float *query, + const float *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + float *out_score) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t candidates_bytes; + size_t candidate; + size_t best_index; + float best_score; + float query_norm; + float query_norm_squared = 0.0f; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || candidates == NULL || + out_index == NULL || out_score == NULL) { + return LECORE_EINVAL; + } + if (lecore_internal_ranges_overlap( + out_index, sizeof(*out_index), out_score, sizeof(*out_score))) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, + candidate_count, + candidate_stride, + sizeof(float), + &candidates_bytes); + if (status != LECORE_OK) { + return status; + } + (void)candidates_bytes; + + if (context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_cleanup_f32_vector_is_finite( + query, context->dimension)) { + return LECORE_ENONFINITE; + } + for (candidate = 0; candidate < candidate_count; ++candidate) { + if (!lecore_cleanup_f32_vector_is_finite( + candidates + candidate * candidate_stride, + context->dimension)) { + return LECORE_ENONFINITE; + } + } + } + + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrtf(query_norm_squared); + best_index = 0; + best_score = lecore_cleanup_f32_cosine( + query, + query_norm_squared, + query_norm, + candidates, + context->dimension); + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + float score = lecore_cleanup_f32_cosine( + query, + query_norm_squared, + query_norm, + candidates + candidate * candidate_stride, + context->dimension); + if (isnan(score)) { + best_index = candidate; + best_score = score; + break; + } + if (score > best_score) { + best_index = candidate; + best_score = score; + } + } + } + + *out_index = best_index; + *out_score = best_score; + return LECORE_OK; +} diff --git a/native/liblecore/src/cleanup_f64.c b/native/liblecore/src/cleanup_f64.c new file mode 100644 index 0000000..73ac06b --- /dev/null +++ b/native/liblecore/src/cleanup_f64.c @@ -0,0 +1,135 @@ +#include "internal/lecore_internal.h" + +#include + +static int lecore_cleanup_f64_vector_is_finite( + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static double lecore_cleanup_f64_cosine( + const double *query, + double query_norm_squared, + double query_norm, + const double *candidate, + uint32_t dimension) +{ + double candidate_norm_squared = 0.0; + double candidate_norm; + double dot = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + candidate_norm_squared += candidate[index] * candidate[index]; + } + if (query_norm_squared == 0.0 || candidate_norm_squared == 0.0) { + return 0.0; + } + for (index = 0; index < dimension; ++index) { + dot += query[index] * candidate[index]; + } + candidate_norm = sqrt(candidate_norm_squared); + return dot / (query_norm * candidate_norm); +} + +lecore_status LECORE_CALL lecore_cleanup_f64( + lecore_context *context, + const double *query, + const double *candidates, + size_t candidate_count, + size_t candidate_stride, + size_t *out_index, + double *out_score) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t candidates_bytes; + size_t candidate; + size_t best_index; + double best_score; + double query_norm; + double query_norm_squared = 0.0; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || candidates == NULL || + out_index == NULL || out_score == NULL) { + return LECORE_EINVAL; + } + if (lecore_internal_ranges_overlap( + out_index, sizeof(*out_index), out_score, sizeof(*out_score))) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, + candidate_count, + candidate_stride, + sizeof(double), + &candidates_bytes); + if (status != LECORE_OK) { + return status; + } + (void)candidates_bytes; + + if (context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_cleanup_f64_vector_is_finite( + query, context->dimension)) { + return LECORE_ENONFINITE; + } + for (candidate = 0; candidate < candidate_count; ++candidate) { + if (!lecore_cleanup_f64_vector_is_finite( + candidates + candidate * candidate_stride, + context->dimension)) { + return LECORE_ENONFINITE; + } + } + } + + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrt(query_norm_squared); + best_index = 0; + best_score = lecore_cleanup_f64_cosine( + query, + query_norm_squared, + query_norm, + candidates, + context->dimension); + + /* NumPy argmax treats the first NaN as the winning stable index. */ + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + double score = lecore_cleanup_f64_cosine( + query, + query_norm_squared, + query_norm, + candidates + candidate * candidate_stride, + context->dimension); + if (isnan(score)) { + best_index = candidate; + best_score = score; + break; + } + if (score > best_score) { + best_index = candidate; + best_score = score; + } + } + } + + *out_index = best_index; + *out_score = best_score; + return LECORE_OK; +} diff --git a/native/liblecore/src/context.c b/native/liblecore/src/context.c new file mode 100644 index 0000000..70745ed --- /dev/null +++ b/native/liblecore/src/context.c @@ -0,0 +1,444 @@ +#include "internal/lecore_internal.h" + +#include +#include +#include +#include + +#ifndef LECORE_ENABLE_FORMAT +# define LECORE_ENABLE_FORMAT 0 +#endif + +#define LECORE_INTERNAL_TAU 6.2831853071795864769252867665590057683943387987502 + +static void *LECORE_CALL lecore_default_allocate( + void *user, + size_t bytes, + size_t alignment) +{ + (void)user; + (void)alignment; + return malloc(bytes); +} + +static void LECORE_CALL lecore_default_deallocate( + void *user, + void *pointer, + size_t bytes, + size_t alignment) +{ + (void)user; + (void)bytes; + (void)alignment; + free(pointer); +} + +static int lecore_is_power_of_two(uintmax_t value) +{ + return value != 0 && (value & (value - 1)) == 0; +} + +static lecore_status lecore_allocate_block( + const lecore_allocator_v0 *allocator, + size_t bytes, + size_t alignment, + void **out_pointer) +{ + void *pointer; + + if (bytes == 0 || out_pointer == NULL) { + return LECORE_EINVAL; + } + *out_pointer = NULL; + pointer = allocator->allocate(allocator->user, bytes, alignment); + if (pointer == NULL) { + return LECORE_ENOMEM; + } + if (((uintptr_t)pointer % alignment) != 0) { + allocator->deallocate( + allocator->user, pointer, bytes, alignment); + return LECORE_EINVAL; + } + *out_pointer = pointer; + return LECORE_OK; +} + +#if LECORE_ENABLE_RADIX2 +static void lecore_initialize_radix2_plan(lecore_context *context) +{ + uint32_t bit_count = 0; + uint32_t value = context->dimension; + uint32_t index; + + while (value > 1) { + ++bit_count; + value >>= 1; + } + + for (index = 0; index < context->dimension; ++index) { + uint32_t source = index; + uint32_t reversed = 0; + uint32_t bit; + + for (bit = 0; bit < bit_count; ++bit) { + reversed = (reversed << 1) | (source & UINT32_C(1)); + source >>= 1; + } + context->radix2_bit_reversal[index] = reversed; + } + + if (context->dimension == 1) { + return; + } + if (context->profile == LECORE_PROFILE_HRR_F64_V1) { + double *twiddles = (double *)context->radix2_twiddles; + uint32_t twiddle; + + for (twiddle = 0; twiddle < context->dimension / 2; ++twiddle) { + double angle = -LECORE_INTERNAL_TAU * (double)twiddle / + (double)context->dimension; + twiddles[(size_t)twiddle * 2] = cos(angle); + twiddles[(size_t)twiddle * 2 + 1] = sin(angle); + } + } else { + float *twiddles = (float *)context->radix2_twiddles; + uint32_t twiddle; + + for (twiddle = 0; twiddle < context->dimension / 2; ++twiddle) { + double angle = -LECORE_INTERNAL_TAU * (double)twiddle / + (double)context->dimension; + twiddles[(size_t)twiddle * 2] = (float)cos(angle); + twiddles[(size_t)twiddle * 2 + 1] = (float)sin(angle); + } + } +} +#endif + +static int lecore_reserved_is_zero(const lecore_config_v0 *config) +{ + size_t index; + + for (index = 0; index < sizeof(config->reserved) / sizeof(config->reserved[0]); + ++index) { + if (config->reserved[index] != 0) { + return 0; + } + } + return 1; +} + +void LECORE_CALL lecore_config_init_v0(lecore_config_v0 *config) +{ + if (config == NULL) { + return; + } + + memset(config, 0, sizeof(*config)); + config->struct_size = (uint32_t)sizeof(*config); + config->abi_version = LECORE_ABI_VERSION; + config->profile = LECORE_PROFILE_HRR_F64_V1; + config->backend = LECORE_BACKEND_AUTO; + config->validation = LECORE_VALIDATION_SHAPE; + config->allocator.struct_size = (uint32_t)sizeof(config->allocator); +} + +lecore_status LECORE_CALL lecore_context_create( + const lecore_config_v0 *config, + lecore_context **out_context) +{ + lecore_allocator_v0 allocator; + lecore_context *context; + lecore_scalar_type scalar_type; + lecore_backend backend; + lecore_status status; + size_t scalar_bytes; + size_t scratch_bytes; + size_t bit_reversal_bytes = 0; + size_t twiddle_bytes = 0; + const size_t alignment = _Alignof(max_align_t); + void *context_memory = NULL; + void *scratch = NULL; + void *bit_reversal = NULL; + void *twiddles = NULL; + int has_allocate; + int has_deallocate; + + if (out_context == NULL) { + return LECORE_EINVAL; + } + *out_context = NULL; + + if (config == NULL) { + return LECORE_EINVAL; + } + if (config->struct_size != sizeof(*config) || + config->abi_version != LECORE_ABI_VERSION || + config->allocator.struct_size != sizeof(config->allocator)) { + return LECORE_EINVAL; + } + if (config->flags != 0 || !lecore_reserved_is_zero(config)) { + return LECORE_EINVAL; + } + if (config->dimension == 0) { + return LECORE_EDIM; + } + + switch (config->profile) { + case LECORE_PROFILE_HRR_F64_V1: + scalar_type = LECORE_SCALAR_F64; + scalar_bytes = sizeof(double); + break; + case LECORE_PROFILE_HRR_F32_V1: + scalar_type = LECORE_SCALAR_F32; + scalar_bytes = sizeof(float); + break; + default: + return LECORE_EPROFILE; + } + + if (config->validation != LECORE_VALIDATION_SHAPE && + config->validation != LECORE_VALIDATION_FINITE) { + return LECORE_EINVAL; + } + if (config->backend == LECORE_BACKEND_AUTO || + config->backend == LECORE_BACKEND_DIRECT) { + /* AUTO remains the correctness oracle until a crossover is earned. */ + backend = LECORE_BACKEND_DIRECT; + } else if (config->backend == LECORE_BACKEND_RADIX2) { +#if LECORE_ENABLE_RADIX2 + if (!lecore_is_power_of_two((uintmax_t)config->dimension)) { + return LECORE_EUNSUPPORTED; + } + backend = LECORE_BACKEND_RADIX2; +#else + return LECORE_EUNSUPPORTED; +#endif + } else { + return LECORE_EBACKEND; + } + + has_allocate = config->allocator.allocate != NULL; + has_deallocate = config->allocator.deallocate != NULL; + if (has_allocate != has_deallocate) { + return LECORE_EINVAL; + } + + allocator = config->allocator; + if (!has_allocate) { + allocator.user = NULL; + allocator.allocate = lecore_default_allocate; + allocator.deallocate = lecore_default_deallocate; + } + + if (!lecore_is_power_of_two((uintmax_t)alignment)) { + return LECORE_EOVERFLOW; + } + if (backend == LECORE_BACKEND_RADIX2) { + const size_t scratch_scalars_per_element = 4; + + if ((uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / + ((uintmax_t)scratch_scalars_per_element * scalar_bytes) || + (uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / sizeof(uint32_t) || + (config->dimension > 1 && + (uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / scalar_bytes)) { + return LECORE_EOVERFLOW; + } + scratch_bytes = (size_t)config->dimension * + scratch_scalars_per_element * scalar_bytes; + bit_reversal_bytes = + (size_t)config->dimension * sizeof(uint32_t); + if (config->dimension > 1) { + twiddle_bytes = (size_t)config->dimension * scalar_bytes; + } + } else { + if ((uintmax_t)config->dimension > + (uintmax_t)SIZE_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + scratch_bytes = (size_t)config->dimension * scalar_bytes; + } + + status = lecore_allocate_block( + &allocator, sizeof(*context), alignment, &context_memory); + if (status != LECORE_OK) { + return status; + } + context = (lecore_context *)context_memory; + memset(context, 0, sizeof(*context)); + + status = lecore_allocate_block( + &allocator, scratch_bytes, alignment, &scratch); + if (status != LECORE_OK) { + goto fail; + } + if (backend == LECORE_BACKEND_RADIX2) { + status = lecore_allocate_block( + &allocator, + bit_reversal_bytes, + alignment, + &bit_reversal); + if (status != LECORE_OK) { + goto fail; + } + if (twiddle_bytes != 0) { + status = lecore_allocate_block( + &allocator, twiddle_bytes, alignment, &twiddles); + if (status != LECORE_OK) { + goto fail; + } + } + } + + context->dimension = config->dimension; + context->profile = config->profile; + context->backend = backend; + context->validation = config->validation; + context->scalar_type = scalar_type; + context->allocator = allocator; + context->scratch = scratch; + context->scratch_bytes = scratch_bytes; + context->radix2_bit_reversal = (uint32_t *)bit_reversal; + context->radix2_bit_reversal_bytes = bit_reversal_bytes; + context->radix2_twiddles = twiddles; + context->radix2_twiddle_bytes = twiddle_bytes; + context->context_bytes = sizeof(*context); + context->allocation_alignment = alignment; +#if LECORE_ENABLE_RADIX2 + if (backend == LECORE_BACKEND_RADIX2) { + lecore_initialize_radix2_plan(context); + } +#endif + context->magic = LECORE_INTERNAL_CONTEXT_MAGIC; + + *out_context = context; + return LECORE_OK; + +fail: + if (twiddles != NULL) { + allocator.deallocate( + allocator.user, twiddles, twiddle_bytes, alignment); + } + if (bit_reversal != NULL) { + allocator.deallocate( + allocator.user, + bit_reversal, + bit_reversal_bytes, + alignment); + } + if (scratch != NULL) { + allocator.deallocate( + allocator.user, scratch, scratch_bytes, alignment); + } + allocator.deallocate( + allocator.user, context, sizeof(*context), alignment); + return status; +} + +void LECORE_CALL lecore_context_destroy(lecore_context *context) +{ + lecore_allocator_v0 allocator; + void *scratch; + size_t scratch_bytes; + void *bit_reversal; + size_t bit_reversal_bytes; + void *twiddles; + size_t twiddle_bytes; + size_t context_bytes; + size_t alignment; + + if (context == NULL || context->magic != LECORE_INTERNAL_CONTEXT_MAGIC) { + return; + } + + allocator = context->allocator; + scratch = context->scratch; + scratch_bytes = context->scratch_bytes; + bit_reversal = context->radix2_bit_reversal; + bit_reversal_bytes = context->radix2_bit_reversal_bytes; + twiddles = context->radix2_twiddles; + twiddle_bytes = context->radix2_twiddle_bytes; + context_bytes = context->context_bytes; + alignment = context->allocation_alignment; + context->magic = 0; + + if (twiddles != NULL) { + allocator.deallocate( + allocator.user, twiddles, twiddle_bytes, alignment); + } + if (bit_reversal != NULL) { + allocator.deallocate( + allocator.user, + bit_reversal, + bit_reversal_bytes, + alignment); + } + allocator.deallocate( + allocator.user, scratch, scratch_bytes, alignment); + allocator.deallocate(allocator.user, context, context_bytes, alignment); +} + +uint32_t LECORE_CALL lecore_context_dimension(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->dimension + : UINT32_C(0); +} + +lecore_profile LECORE_CALL lecore_context_profile(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->profile + : UINT32_C(0); +} + +lecore_backend LECORE_CALL lecore_context_backend(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->backend + : UINT32_C(0); +} + +lecore_validation LECORE_CALL lecore_context_validation( + const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->validation + : UINT32_C(0); +} + +lecore_scalar_type LECORE_CALL lecore_context_scalar_type( + const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->scalar_type + : UINT32_C(0); +} + +size_t LECORE_CALL lecore_context_scratch_bytes(const lecore_context *context) +{ + return context != NULL && context->magic == LECORE_INTERNAL_CONTEXT_MAGIC + ? context->scratch_bytes + : 0; +} + +uint64_t LECORE_CALL lecore_capabilities(void) +{ + uint64_t capabilities = + LECORE_CAP_HRR_F64 | + LECORE_CAP_HRR_F32 | + LECORE_CAP_DIRECT | + LECORE_CAP_BATCH | + LECORE_CAP_MIXED_F64_F32 | + LECORE_CAP_FINITE_VALIDATION; + +#if LECORE_ENABLE_FORMAT + capabilities |= LECORE_CAP_FORMAT; +#endif +#if LECORE_ENABLE_RADIX2 + capabilities |= LECORE_CAP_RADIX2; +#endif + return capabilities; +} diff --git a/native/liblecore/src/crc64.c b/native/liblecore/src/crc64.c new file mode 100644 index 0000000..e18f7bb --- /dev/null +++ b/native/liblecore/src/crc64.c @@ -0,0 +1,26 @@ +#include "internal/format_internal.h" + +#define LECORE_CRC64_ECMA_POLYNOMIAL UINT64_C(0x42F0E1EBA9EA3693) + +LECORE_INTERNAL_API uint64_t lecore_format_crc64_ecma_update( + uint64_t crc, + const uint8_t *data, + size_t data_bytes) +{ + size_t byte_index; + + for (byte_index = 0; byte_index < data_bytes; ++byte_index) { + unsigned bit_index; + + crc ^= (uint64_t)data[byte_index] << 56; + for (bit_index = 0; bit_index < 8; ++bit_index) { + if ((crc & UINT64_C(0x8000000000000000)) != 0) { + crc = (crc << 1) ^ LECORE_CRC64_ECMA_POLYNOMIAL; + } else { + crc <<= 1; + } + } + } + + return crc; +} diff --git a/native/liblecore/src/format.c b/native/liblecore/src/format.c new file mode 100644 index 0000000..3e11abd --- /dev/null +++ b/native/liblecore/src/format.c @@ -0,0 +1,536 @@ +#include + +#include +#include + +#include "internal/format_internal.h" + +#define LECORE_FORMAT_MAGIC_OFFSET ((size_t)0) +#define LECORE_FORMAT_MAJOR_OFFSET ((size_t)8) +#define LECORE_FORMAT_MINOR_OFFSET ((size_t)10) +#define LECORE_FORMAT_HEADER_BYTES_OFFSET ((size_t)12) +#define LECORE_FORMAT_FLAGS_OFFSET ((size_t)14) +#define LECORE_FORMAT_ARTIFACT_KIND_OFFSET ((size_t)16) +#define LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET ((size_t)20) +#define LECORE_FORMAT_SCALAR_TYPE_OFFSET ((size_t)24) +#define LECORE_FORMAT_DIMENSION_OFFSET ((size_t)28) +#define LECORE_FORMAT_NORMALIZATION_OFFSET ((size_t)32) +#define LECORE_FORMAT_ATOM_SCHEME_OFFSET ((size_t)36) +#define LECORE_FORMAT_SEED_NAMESPACE_OFFSET ((size_t)40) +#define LECORE_FORMAT_VECTOR_COUNT_OFFSET ((size_t)48) +#define LECORE_FORMAT_PAYLOAD_BYTES_OFFSET ((size_t)56) +#define LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET ((size_t)64) +#define LECORE_FORMAT_CHECKSUM_OFFSET ((size_t)80) +#define LECORE_FORMAT_RESERVED_OFFSET ((size_t)88) +#define LECORE_FORMAT_CHECKSUM_BYTES ((size_t)8) +#define LECORE_FORMAT_RESERVED_BYTES ((size_t)8) + +static const uint8_t lecore_format_magic[8] = { + (uint8_t)'L', (uint8_t)'E', (uint8_t)'C', (uint8_t)'O', + (uint8_t)'R', (uint8_t)'E', (uint8_t)'V', UINT8_C(0) +}; + +static uint16_t lecore_format_get_u16_le(const uint8_t *bytes) +{ + return (uint16_t)((uint16_t)bytes[0] + | ((uint16_t)bytes[1] << 8)); +} + +static uint32_t lecore_format_get_u32_le(const uint8_t *bytes) +{ + return (uint32_t)((uint32_t)bytes[0] + | ((uint32_t)bytes[1] << 8) + | ((uint32_t)bytes[2] << 16) + | ((uint32_t)bytes[3] << 24)); +} + +static uint64_t lecore_format_get_u64_le(const uint8_t *bytes) +{ + return (uint64_t)bytes[0] + | ((uint64_t)bytes[1] << 8) + | ((uint64_t)bytes[2] << 16) + | ((uint64_t)bytes[3] << 24) + | ((uint64_t)bytes[4] << 32) + | ((uint64_t)bytes[5] << 40) + | ((uint64_t)bytes[6] << 48) + | ((uint64_t)bytes[7] << 56); +} + +static void lecore_format_put_u16_le(uint8_t *bytes, uint16_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); +} + +static void lecore_format_put_u32_le(uint8_t *bytes, uint32_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); + bytes[2] = (uint8_t)(value >> 16); + bytes[3] = (uint8_t)(value >> 24); +} + +static void lecore_format_put_u64_le(uint8_t *bytes, uint64_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); + bytes[2] = (uint8_t)(value >> 16); + bytes[3] = (uint8_t)(value >> 24); + bytes[4] = (uint8_t)(value >> 32); + bytes[5] = (uint8_t)(value >> 40); + bytes[6] = (uint8_t)(value >> 48); + bytes[7] = (uint8_t)(value >> 56); +} + +static int lecore_format_bytes_are_zero(const uint8_t *bytes, size_t count) +{ + size_t index; + + for (index = 0; index < count; ++index) { + if (bytes[index] != UINT8_C(0)) { + return 0; + } + } + return 1; +} + +static int lecore_format_u64s_are_zero(const uint64_t *values, size_t count) +{ + size_t index; + + for (index = 0; index < count; ++index) { + if (values[index] != UINT64_C(0)) { + return 0; + } + } + return 1; +} + +static lecore_status lecore_format_validate_descriptor( + const lecore_format_descriptor_v1 *descriptor, + int encoding) +{ + uint64_t element_count; + uint64_t expected_payload_bytes; + uint64_t scalar_bytes; + + if (descriptor == NULL + || descriptor->struct_size != (uint32_t)sizeof(*descriptor)) { + return LECORE_EINVAL; + } + if (!lecore_format_u64s_are_zero( + descriptor->reserved, + sizeof(descriptor->reserved) / sizeof(descriptor->reserved[0]))) { + return LECORE_EINVAL; + } + if (descriptor->format_major != LECORE_FORMAT_MAJOR) { + return LECORE_EFORMAT; + } + if (descriptor->flags != LECORE_FORMAT_FLAGS_NONE) { + return LECORE_EFORMAT; + } + if (descriptor->header_bytes < LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + if (descriptor->format_minor == LECORE_FORMAT_MINOR + && descriptor->header_bytes != LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + if (encoding + && (descriptor->format_minor != LECORE_FORMAT_MINOR + || descriptor->header_bytes != LECORE_FORMAT_HEADER_BYTES)) { + return LECORE_EUNSUPPORTED; + } + + if (descriptor->artifact_kind != LECORE_ARTIFACT_VECTOR + && descriptor->artifact_kind != LECORE_ARTIFACT_MATRIX + && descriptor->artifact_kind != LECORE_ARTIFACT_TRACE) { + return LECORE_EFORMAT; + } + if (descriptor->semantic_profile == LECORE_PROFILE_HRR_F64_V1) { + if (descriptor->scalar_type != LECORE_SCALAR_F64) { + return LECORE_EPROFILE; + } + scalar_bytes = UINT64_C(8); + } else if (descriptor->semantic_profile == LECORE_PROFILE_HRR_F32_V1) { + if (descriptor->scalar_type != LECORE_SCALAR_F32) { + return LECORE_EPROFILE; + } + scalar_bytes = UINT64_C(4); + } else { + return LECORE_EPROFILE; + } + + if (descriptor->dimension == UINT32_C(0)) { + return LECORE_EDIM; + } + if (descriptor->normalization_policy != LECORE_NORMALIZATION_RAW + && descriptor->normalization_policy != LECORE_NORMALIZATION_UNIT + && descriptor->normalization_policy + != LECORE_NORMALIZATION_APPLICATION) { + return LECORE_EFORMAT; + } + if (descriptor->atom_scheme != LECORE_ATOM_EXTERNAL) { + return LECORE_EFORMAT; + } + if (descriptor->vector_count == UINT64_C(0)) { + return LECORE_EFORMAT; + } + + if (descriptor->vector_count + > UINT64_MAX / (uint64_t)descriptor->dimension) { + return LECORE_EOVERFLOW; + } + element_count = descriptor->vector_count * (uint64_t)descriptor->dimension; + if (element_count > UINT64_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + expected_payload_bytes = element_count * scalar_bytes; + if (descriptor->payload_bytes != expected_payload_bytes) { + return LECORE_EFORMAT; + } + + return LECORE_OK; +} + +static uint64_t lecore_format_record_crc64( + const uint8_t *record, + size_t header_bytes, + size_t payload_bytes) +{ + static const uint8_t zero_checksum[LECORE_FORMAT_CHECKSUM_BYTES] = { + UINT8_C(0), UINT8_C(0), UINT8_C(0), UINT8_C(0), + UINT8_C(0), UINT8_C(0), UINT8_C(0), UINT8_C(0) + }; + uint64_t crc; + + crc = lecore_format_crc64_ecma_update( + UINT64_C(0), record, LECORE_FORMAT_CHECKSUM_OFFSET); + crc = lecore_format_crc64_ecma_update( + crc, zero_checksum, LECORE_FORMAT_CHECKSUM_BYTES); + crc = lecore_format_crc64_ecma_update( + crc, + record + LECORE_FORMAT_RESERVED_OFFSET, + header_bytes - LECORE_FORMAT_RESERVED_OFFSET); + return lecore_format_crc64_ecma_update( + crc, record + header_bytes, payload_bytes); +} + +LECORE_API void LECORE_CALL lecore_format_descriptor_init_v1( + lecore_format_descriptor_v1 *descriptor) +{ + if (descriptor == NULL) { + return; + } + + memset(descriptor, 0, sizeof(*descriptor)); + descriptor->struct_size = (uint32_t)sizeof(*descriptor); + descriptor->format_major = LECORE_FORMAT_MAJOR; + descriptor->format_minor = LECORE_FORMAT_MINOR; + descriptor->header_bytes = LECORE_FORMAT_HEADER_BYTES; + descriptor->semantic_profile = LECORE_PROFILE_HRR_F64_V1; + descriptor->scalar_type = LECORE_SCALAR_F64; + descriptor->normalization_policy = LECORE_NORMALIZATION_RAW; + descriptor->atom_scheme = LECORE_ATOM_EXTERNAL; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_encoded_size_v1( + const lecore_format_descriptor_v1 *descriptor, + size_t *out_record_bytes) +{ + lecore_format_descriptor_v1 descriptor_copy; + lecore_status status; + + if (out_record_bytes == NULL) { + return LECORE_EINVAL; + } + if (descriptor == NULL) { + *out_record_bytes = (size_t)0; + return LECORE_EINVAL; + } + descriptor_copy = *descriptor; + descriptor = &descriptor_copy; + *out_record_bytes = (size_t)0; + + status = lecore_format_validate_descriptor(descriptor, 1); + if (status != LECORE_OK) { + return status; + } + if (descriptor->payload_bytes + > (uint64_t)(SIZE_MAX - (size_t)descriptor->header_bytes)) { + return LECORE_EOVERFLOW; + } + + *out_record_bytes = (size_t)descriptor->header_bytes + + (size_t)descriptor->payload_bytes; + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_encode_v1( + const lecore_format_descriptor_v1 *descriptor, + const void *payload, + size_t payload_bytes, + void *record, + size_t record_capacity, + size_t *out_record_bytes) +{ + lecore_format_descriptor_v1 descriptor_copy; + uint8_t *record_bytes; + uint64_t crc; + size_t required_bytes; + lecore_status status; + + if (out_record_bytes == NULL) { + return LECORE_EINVAL; + } + if (descriptor == NULL) { + *out_record_bytes = (size_t)0; + return LECORE_EINVAL; + } + descriptor_copy = *descriptor; + descriptor = &descriptor_copy; + *out_record_bytes = (size_t)0; + + status = lecore_format_encoded_size_v1(descriptor, &required_bytes); + if (status != LECORE_OK) { + return status; + } + *out_record_bytes = required_bytes; + + if (payload_bytes != (size_t)descriptor->payload_bytes) { + return LECORE_EFORMAT; + } + if (payload == NULL || record == NULL) { + return LECORE_EINVAL; + } + if (record_capacity < required_bytes) { + return LECORE_EINVAL; + } + + record_bytes = (uint8_t *)record; + memmove( + record_bytes + descriptor->header_bytes, + payload, + payload_bytes); + memset(record_bytes, 0, descriptor->header_bytes); + + memcpy( + record_bytes + LECORE_FORMAT_MAGIC_OFFSET, + lecore_format_magic, + sizeof(lecore_format_magic)); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_MAJOR_OFFSET, + descriptor->format_major); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_MINOR_OFFSET, + descriptor->format_minor); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_HEADER_BYTES_OFFSET, + descriptor->header_bytes); + lecore_format_put_u16_le( + record_bytes + LECORE_FORMAT_FLAGS_OFFSET, + descriptor->flags); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_ARTIFACT_KIND_OFFSET, + descriptor->artifact_kind); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET, + descriptor->semantic_profile); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_SCALAR_TYPE_OFFSET, + descriptor->scalar_type); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_DIMENSION_OFFSET, + descriptor->dimension); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_NORMALIZATION_OFFSET, + descriptor->normalization_policy); + lecore_format_put_u32_le( + record_bytes + LECORE_FORMAT_ATOM_SCHEME_OFFSET, + descriptor->atom_scheme); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_SEED_NAMESPACE_OFFSET, + descriptor->seed_namespace); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_VECTOR_COUNT_OFFSET, + descriptor->vector_count); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_PAYLOAD_BYTES_OFFSET, + descriptor->payload_bytes); + memcpy( + record_bytes + LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET, + descriptor->application_contract, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES); + + crc = lecore_format_record_crc64( + record_bytes, + descriptor->header_bytes, + payload_bytes); + lecore_format_put_u64_le( + record_bytes + LECORE_FORMAT_CHECKSUM_OFFSET, + crc); + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_decode_v1( + const void *record, + size_t record_bytes, + lecore_format_descriptor_v1 *out_descriptor, + const void **out_payload, + size_t *out_payload_bytes) +{ + const uint8_t *bytes; + lecore_format_descriptor_v1 descriptor; + uint64_t actual_crc; + size_t payload_bytes; + size_t total_bytes; + lecore_status status; + + if (out_descriptor == NULL || out_payload == NULL + || out_payload_bytes == NULL) { + return LECORE_EINVAL; + } + memset(out_descriptor, 0, sizeof(*out_descriptor)); + out_descriptor->struct_size = (uint32_t)sizeof(*out_descriptor); + *out_payload = NULL; + *out_payload_bytes = (size_t)0; + + if (record == NULL) { + return LECORE_EINVAL; + } + if (record_bytes < (size_t)LECORE_FORMAT_HEADER_BYTES) { + return LECORE_EFORMAT; + } + + bytes = (const uint8_t *)record; + if (memcmp( + bytes + LECORE_FORMAT_MAGIC_OFFSET, + lecore_format_magic, + sizeof(lecore_format_magic)) != 0) { + return LECORE_EFORMAT; + } + + memset(&descriptor, 0, sizeof(descriptor)); + descriptor.struct_size = (uint32_t)sizeof(descriptor); + descriptor.format_major = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_MAJOR_OFFSET); + descriptor.format_minor = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_MINOR_OFFSET); + descriptor.header_bytes = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_HEADER_BYTES_OFFSET); + descriptor.flags = lecore_format_get_u16_le( + bytes + LECORE_FORMAT_FLAGS_OFFSET); + descriptor.artifact_kind = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_ARTIFACT_KIND_OFFSET); + descriptor.semantic_profile = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_SEMANTIC_PROFILE_OFFSET); + descriptor.scalar_type = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_SCALAR_TYPE_OFFSET); + descriptor.dimension = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_DIMENSION_OFFSET); + descriptor.normalization_policy = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_NORMALIZATION_OFFSET); + descriptor.atom_scheme = lecore_format_get_u32_le( + bytes + LECORE_FORMAT_ATOM_SCHEME_OFFSET); + descriptor.seed_namespace = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_SEED_NAMESPACE_OFFSET); + descriptor.vector_count = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_VECTOR_COUNT_OFFSET); + descriptor.payload_bytes = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_PAYLOAD_BYTES_OFFSET); + memcpy( + descriptor.application_contract, + bytes + LECORE_FORMAT_APPLICATION_CONTRACT_OFFSET, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES); + descriptor.content_crc64 = lecore_format_get_u64_le( + bytes + LECORE_FORMAT_CHECKSUM_OFFSET); + + if (!lecore_format_bytes_are_zero( + bytes + LECORE_FORMAT_RESERVED_OFFSET, + LECORE_FORMAT_RESERVED_BYTES)) { + return LECORE_EFORMAT; + } + status = lecore_format_validate_descriptor(&descriptor, 0); + if (status != LECORE_OK) { + return status; + } + if ((size_t)descriptor.header_bytes > record_bytes) { + return LECORE_EFORMAT; + } + if (descriptor.payload_bytes + > (uint64_t)(SIZE_MAX - (size_t)descriptor.header_bytes)) { + return LECORE_EOVERFLOW; + } + payload_bytes = (size_t)descriptor.payload_bytes; + total_bytes = (size_t)descriptor.header_bytes + payload_bytes; + if (record_bytes != total_bytes) { + return LECORE_EFORMAT; + } + + actual_crc = lecore_format_record_crc64( + bytes, descriptor.header_bytes, payload_bytes); + if (actual_crc != descriptor.content_crc64) { + return LECORE_ECHECKSUM; + } + + *out_descriptor = descriptor; + *out_payload = bytes + descriptor.header_bytes; + *out_payload_bytes = payload_bytes; + return LECORE_OK; +} + +LECORE_API lecore_status LECORE_CALL lecore_format_check_v1( + const void *record, + size_t record_bytes) +{ + lecore_format_descriptor_v1 descriptor; + const void *payload; + size_t payload_bytes; + + return lecore_format_decode_v1( + record, + record_bytes, + &descriptor, + &payload, + &payload_bytes); +} + +LECORE_API lecore_status LECORE_CALL lecore_format_check_compatibility_v1( + const lecore_format_descriptor_v1 *expected, + const lecore_format_descriptor_v1 *actual) +{ + lecore_status status; + + status = lecore_format_validate_descriptor(expected, 0); + if (status != LECORE_OK) { + return status; + } + status = lecore_format_validate_descriptor(actual, 0); + if (status != LECORE_OK) { + return status; + } + + if (expected->semantic_profile != actual->semantic_profile + || expected->scalar_type != actual->scalar_type) { + return LECORE_EPROFILE; + } + if (expected->dimension != actual->dimension) { + return LECORE_EDIM; + } + if (expected->format_major != actual->format_major + || expected->flags != actual->flags + || expected->artifact_kind != actual->artifact_kind + || expected->normalization_policy != actual->normalization_policy + || expected->atom_scheme != actual->atom_scheme + || expected->seed_namespace != actual->seed_namespace + || expected->vector_count != actual->vector_count + || expected->payload_bytes != actual->payload_bytes + || memcmp( + expected->application_contract, + actual->application_contract, + LECORE_FORMAT_APPLICATION_CONTRACT_BYTES) != 0) { + return LECORE_EFORMAT; + } + + return LECORE_OK; +} diff --git a/native/liblecore/src/hrr_direct_f32.c b/native/liblecore/src/hrr_direct_f32.c new file mode 100644 index 0000000..0f23566 --- /dev/null +++ b/native/liblecore/src/hrr_direct_f32.c @@ -0,0 +1,469 @@ +#include "internal/lecore_internal.h" + +#include +#include + +static int lecore_hrr_f32_vector_is_finite( + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_hrr_f32_matrix_is_finite( + const float *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_hrr_f32_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static void lecore_hrr_bind_f32_raw( + const float *a, + const float *b, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t left_index; + + if (dimension >= UINT32_C(32)) { + /* + * Preserve the definitional ascending reduction order per output, + * while exposing contiguous independent updates to the compiler. + */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (left_index = 0; left_index < dimension; ++left_index) { + const float left = a[left_index]; + uint32_t right_index = 0; + const uint32_t first_count = dimension - left_index; + + for (; right_index < first_count; ++right_index) { + output[left_index + right_index] += + left * b[right_index]; + } + for (; right_index < dimension; ++right_index) { + output[right_index - first_count] += + left * b[right_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + + for (left_index = 0; left_index < dimension; ++left_index) { + uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + sum += a[left_index] * b[right_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_unbind_f32_raw( + const float *composite, + const float *key, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t composite_index; + + if (dimension >= UINT32_C(32)) { + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const float value = composite[composite_index]; + + for (output_index = 0; + output_index <= composite_index; + ++output_index) { + output[output_index] += + value * key[composite_index - output_index]; + } + for (; output_index < dimension; ++output_index) { + output[output_index] += value * + key[dimension + composite_index - output_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + + for (composite_index = 0; composite_index < dimension; + ++composite_index) { + uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + sum += composite[composite_index] * key[key_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_bind_f32_selected( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_f32(context, a, b, output); + return; + } +#endif + lecore_hrr_bind_f32_raw(a, b, output, context->dimension); +} + +static void lecore_hrr_unbind_f32_selected( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_f32( + context, composite, key, output); + return; + } +#endif + lecore_hrr_unbind_f32_raw( + composite, key, output, context->dimension); +} + +static lecore_status lecore_hrr_f32_check_scalar_aliases( + const lecore_context *context, + const float *a, + const float *b, + const float *output) +{ + lecore_status status; + const size_t vector_bytes = + (size_t)context->dimension * sizeof(float); + + status = lecore_internal_check_vector_alias( + a, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + return lecore_internal_check_vector_alias( + b, output, vector_bytes, 1); +} + +lecore_status LECORE_CALL lecore_hrr_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f32_check_scalar_aliases(context, a, b, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(a, context->dimension) || + !lecore_hrr_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(float); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == a || output == b) + ? (float *)context->scratch + : output; + lecore_hrr_bind_f32_selected(context, a, b, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (composite == NULL || key == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f32_check_scalar_aliases( + context, composite, key, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite( + composite, context->dimension) || + !lecore_hrr_f32_vector_is_finite(key, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(float); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == composite || output == key) + ? (float *)context->scratch + : output; + lecore_hrr_unbind_f32_selected(context, composite, key, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_batch_f32( + lecore_context *context, + const float *a_rows, + size_t a_stride, + const float *b_rows, + size_t b_stride, + size_t row_count, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t a_bytes; + size_t b_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (a_rows == NULL || b_rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, a_stride, sizeof(float), &a_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, b_stride, sizeof(float), &b_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, out_stride, sizeof(float), &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap(a_rows, a_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap(b_rows, b_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_matrix_is_finite( + a_rows, row_count, a_stride, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + b_rows, row_count, b_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_selected( + context, + a_rows + row * a_stride, + b_rows + row * b_stride, + out_rows + row * out_stride); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t role_bytes; + size_t rows_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (role == NULL || rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + role_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + row_count, + out_stride, + sizeof(float), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + role, role_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(role, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_fixed_f32( + context, + role, + rows, + row_count, + row_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_raw( + role, + rows + row * row_stride, + out_rows + row * out_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t trace_bytes; + size_t keys_bytes; + size_t out_bytes; + size_t key; + + if (status != LECORE_OK) { + return status; + } + if (trace == NULL || keys == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + trace_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + key_count, + key_stride, + sizeof(float), + &keys_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + key_count, + out_stride, + sizeof(float), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + trace, trace_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + keys, keys_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f32_vector_is_finite(trace, context->dimension) || + !lecore_hrr_f32_matrix_is_finite( + keys, key_count, key_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_all_f32( + context, + trace, + keys, + key_count, + key_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f32_raw( + trace, + keys + key * key_stride, + out_rows + key * out_stride, + context->dimension); + } + return LECORE_OK; +} diff --git a/native/liblecore/src/hrr_direct_f64.c b/native/liblecore/src/hrr_direct_f64.c new file mode 100644 index 0000000..e630351 --- /dev/null +++ b/native/liblecore/src/hrr_direct_f64.c @@ -0,0 +1,474 @@ +#include "internal/lecore_internal.h" + +#include +#include + +static int lecore_hrr_f64_vector_is_finite( + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_hrr_f64_matrix_is_finite( + const double *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_hrr_f64_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static void lecore_hrr_bind_f64_raw( + const double *a, + const double *b, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t left_index; + + if (dimension >= UINT32_C(32)) { + /* + * Visit left_index in the same ascending order as the definitional + * reduction, but update independent outputs contiguously. This keeps + * every output's floating-point operation order unchanged while + * making the inner loops vectorizable. Public alias checks guarantee + * that raw output is disjoint from both inputs. + */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (left_index = 0; left_index < dimension; ++left_index) { + const double left = a[left_index]; + uint32_t right_index = 0; + const uint32_t first_count = dimension - left_index; + + for (; right_index < first_count; ++right_index) { + output[left_index + right_index] += + left * b[right_index]; + } + for (; right_index < dimension; ++right_index) { + output[right_index - first_count] += + left * b[right_index]; + } + } + return; + } + + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + + for (left_index = 0; left_index < dimension; ++left_index) { + uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + sum += a[left_index] * b[right_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_unbind_f64_raw( + const double *composite, + const double *key, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + uint32_t composite_index; + + if (dimension >= UINT32_C(32)) { + /* Preserve each output's ascending composite-index reduction order. */ + memset(output, 0, (size_t)dimension * sizeof(*output)); + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const double value = composite[composite_index]; + + for (output_index = 0; + output_index <= composite_index; + ++output_index) { + output[output_index] += + value * key[composite_index - output_index]; + } + for (; output_index < dimension; ++output_index) { + output[output_index] += value * + key[dimension + composite_index - output_index]; + } + } + return; + } + + /* Equivalent to bind(composite, involution(key)), without a temporary. */ + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + + for (composite_index = 0; composite_index < dimension; + ++composite_index) { + uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + sum += composite[composite_index] * key[key_index]; + } + output[output_index] = sum; + } +} + +static void lecore_hrr_bind_f64_selected( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_f64(context, a, b, output); + return; + } +#endif + lecore_hrr_bind_f64_raw(a, b, output, context->dimension); +} + +static void lecore_hrr_unbind_f64_selected( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_f64( + context, composite, key, output); + return; + } +#endif + lecore_hrr_unbind_f64_raw( + composite, key, output, context->dimension); +} + +static lecore_status lecore_hrr_f64_check_scalar_aliases( + const lecore_context *context, + const double *a, + const double *b, + const double *output) +{ + lecore_status status; + const size_t vector_bytes = + (size_t)context->dimension * sizeof(double); + + status = lecore_internal_check_vector_alias( + a, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + return lecore_internal_check_vector_alias( + b, output, vector_bytes, 1); +} + +lecore_status LECORE_CALL lecore_hrr_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f64_check_scalar_aliases(context, a, b, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(a, context->dimension) || + !lecore_hrr_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(double); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == a || output == b) + ? (double *)context->scratch + : output; + lecore_hrr_bind_f64_selected(context, a, b, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (composite == NULL || key == NULL || output == NULL) { + return LECORE_EINVAL; + } + status = lecore_hrr_f64_check_scalar_aliases( + context, composite, key, output); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite( + composite, context->dimension) || + !lecore_hrr_f64_vector_is_finite(key, context->dimension))) { + return LECORE_ENONFINITE; + } + + vector_bytes = (size_t)context->dimension * sizeof(double); + target = context->backend == LECORE_BACKEND_DIRECT && + (output == composite || output == key) + ? (double *)context->scratch + : output; + lecore_hrr_unbind_f64_selected(context, composite, key, target); + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_batch_f64( + lecore_context *context, + const double *a_rows, + size_t a_stride, + const double *b_rows, + size_t b_stride, + size_t row_count, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t a_bytes; + size_t b_bytes; + size_t out_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (a_rows == NULL || b_rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, a_stride, sizeof(double), &a_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, b_stride, sizeof(double), &b_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, row_count, out_stride, sizeof(double), &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap(a_rows, a_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap(b_rows, b_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_matrix_is_finite( + a_rows, row_count, a_stride, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + b_rows, row_count, b_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_selected( + context, + a_rows + row * a_stride, + b_rows + row * b_stride, + out_rows + row * out_stride); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t out_bytes; + size_t row; + size_t role_bytes; + + if (status != LECORE_OK) { + return status; + } + if (role == NULL || rows == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + role_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + row_count, + out_stride, + sizeof(double), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + role, role_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(role, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_bind_fixed_f64( + context, + role, + rows, + row_count, + row_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_raw( + role, + rows + row * row_stride, + out_rows + row * out_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t keys_bytes; + size_t out_bytes; + size_t key; + size_t trace_bytes; + + if (status != LECORE_OK) { + return status; + } + if (trace == NULL || keys == NULL || out_rows == NULL) { + return LECORE_EINVAL; + } + trace_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + key_count, + key_stride, + sizeof(double), + &keys_bytes); + if (status != LECORE_OK) { + return status; + } + status = lecore_internal_matrix_span( + context->dimension, + key_count, + out_stride, + sizeof(double), + &out_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + trace, trace_bytes, out_rows, out_bytes) || + lecore_internal_ranges_overlap( + keys, keys_bytes, out_rows, out_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_hrr_f64_vector_is_finite(trace, context->dimension) || + !lecore_hrr_f64_matrix_is_finite( + keys, key_count, key_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + +#if LECORE_ENABLE_RADIX2 + if (context->backend == LECORE_BACKEND_RADIX2) { + lecore_internal_hrr_radix2_unbind_all_f64( + context, + trace, + keys, + key_count, + key_stride, + out_rows, + out_stride); + return LECORE_OK; + } +#endif + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f64_raw( + trace, + keys + key * key_stride, + out_rows + key * out_stride, + context->dimension); + } + return LECORE_OK; +} diff --git a/native/liblecore/src/hrr_radix2_f32.c b/native/liblecore/src/hrr_radix2_f32.c new file mode 100644 index 0000000..b946c3b --- /dev/null +++ b/native/liblecore/src/hrr_radix2_f32.c @@ -0,0 +1,244 @@ +#include "internal/lecore_internal.h" + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f32( + const lecore_context *context, + float *values, + int inverse, + int input_is_bit_reversed) +{ + const uint32_t dimension = context->dimension; + const float *twiddles = (const float *)context->radix2_twiddles; + uint32_t index; + + if (!input_is_bit_reversed) { + for (index = 0; index < dimension; ++index) { + uint32_t reversed = context->radix2_bit_reversal[index]; + if (reversed > index) { + float real = values[(size_t)index * 2]; + float imaginary = values[(size_t)index * 2 + 1]; + values[(size_t)index * 2] = values[(size_t)reversed * 2]; + values[(size_t)index * 2 + 1] = + values[(size_t)reversed * 2 + 1]; + values[(size_t)reversed * 2] = real; + values[(size_t)reversed * 2 + 1] = imaginary; + } + } + } + + if (dimension > 1) { + uint32_t length = 2; + + for (;;) { + const uint32_t half = length / 2; + const uint32_t twiddle_step = dimension / length; + uint32_t block; + + for (block = 0; block < dimension; block += length) { + uint32_t offset; + + for (offset = 0; offset < half; ++offset) { + const uint32_t twiddle_index = offset * twiddle_step; + const uint32_t even_index = block + offset; + const uint32_t odd_index = even_index + half; + const float wr = + twiddles[(size_t)twiddle_index * 2]; + const float wi = inverse + ? -twiddles[(size_t)twiddle_index * 2 + 1] + : twiddles[(size_t)twiddle_index * 2 + 1]; + const float odd_real = + values[(size_t)odd_index * 2]; + const float odd_imaginary = + values[(size_t)odd_index * 2 + 1]; + const float transformed_real = + odd_real * wr - odd_imaginary * wi; + const float transformed_imaginary = + odd_real * wi + odd_imaginary * wr; + const float even_real = + values[(size_t)even_index * 2]; + const float even_imaginary = + values[(size_t)even_index * 2 + 1]; + + values[(size_t)even_index * 2] = + even_real + transformed_real; + values[(size_t)even_index * 2 + 1] = + even_imaginary + transformed_imaginary; + values[(size_t)odd_index * 2] = + even_real - transformed_real; + values[(size_t)odd_index * 2 + 1] = + even_imaginary - transformed_imaginary; + } + } + if (length == dimension) { + break; + } + length <<= 1; + } + } + + if (inverse) { + const float scale = (float)dimension; + + for (index = 0; index < dimension; ++index) { + values[(size_t)index * 2] /= scale; + values[(size_t)index * 2 + 1] /= scale; + } + } +} + +static void lecore_radix2_load_f32( + const lecore_context *context, + float *destination, + const float *source, + int involute) +{ + uint32_t index; + + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; + uint32_t source_index = involute && index != 0 + ? context->dimension - index + : index; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0f; + } +} + +static void lecore_radix2_multiply_f32( + float *destination, + const float *left, + const float *right, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + const float left_real = left[(size_t)index * 2]; + const float left_imaginary = left[(size_t)index * 2 + 1]; + const float right_real = right[(size_t)index * 2]; + const float right_imaginary = right[(size_t)index * 2 + 1]; + + destination[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + destination[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } +} + +static void lecore_radix2_store_real_f32( + float *output, + const float *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + output[index] = values[(size_t)index * 2]; + } +} + +static void lecore_radix2_convolve_f32( + lecore_context *context, + const float *left_input, + const float *right_input, + int involute_right, + float *output) +{ + float *left = (float *)context->scratch; + float *right = left + (size_t)context->dimension * 2; + lecore_radix2_load_f32( + context, left, left_input, 0); + lecore_radix2_load_f32( + context, right, right_input, involute_right); + lecore_radix2_fft_f32(context, left, 0, 1); + lecore_radix2_fft_f32(context, right, 0, 1); + lecore_radix2_multiply_f32( + left, left, right, context->dimension); + lecore_radix2_fft_f32(context, left, 1, 0); + lecore_radix2_store_real_f32(output, left, context->dimension); +} + +static void lecore_radix2_convolve_fixed_left_f32( + lecore_context *context, + const float *fixed_left, + const float *right_rows, + size_t row_count, + size_t right_stride, + int involute_right, + float *out_rows, + size_t out_stride) +{ + float *left = (float *)context->scratch; + float *right = left + (size_t)context->dimension * 2; + size_t row; + + lecore_radix2_load_f32(context, left, fixed_left, 0); + lecore_radix2_fft_f32(context, left, 0, 1); + for (row = 0; row < row_count; ++row) { + lecore_radix2_load_f32( + context, + right, + right_rows + row * right_stride, + involute_right); + lecore_radix2_fft_f32(context, right, 0, 1); + lecore_radix2_multiply_f32( + right, left, right, context->dimension); + lecore_radix2_fft_f32(context, right, 1, 0); + lecore_radix2_store_real_f32( + out_rows + row * out_stride, right, context->dimension); + } +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output) +{ + lecore_radix2_convolve_f32(context, a, b, 0, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output) +{ + lecore_radix2_convolve_f32(context, composite, key, 1, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f32( + context, role, rows, row_count, row_stride, 0, out_rows, out_stride); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f32( + context, + trace, + keys, + key_count, + key_stride, + 1, + out_rows, + out_stride); +} + +#endif /* LECORE_ENABLE_RADIX2 */ diff --git a/native/liblecore/src/hrr_radix2_f64.c b/native/liblecore/src/hrr_radix2_f64.c new file mode 100644 index 0000000..644bf70 --- /dev/null +++ b/native/liblecore/src/hrr_radix2_f64.c @@ -0,0 +1,244 @@ +#include "internal/lecore_internal.h" + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f64( + const lecore_context *context, + double *values, + int inverse, + int input_is_bit_reversed) +{ + const uint32_t dimension = context->dimension; + const double *twiddles = (const double *)context->radix2_twiddles; + uint32_t index; + + if (!input_is_bit_reversed) { + for (index = 0; index < dimension; ++index) { + uint32_t reversed = context->radix2_bit_reversal[index]; + if (reversed > index) { + double real = values[(size_t)index * 2]; + double imaginary = values[(size_t)index * 2 + 1]; + values[(size_t)index * 2] = values[(size_t)reversed * 2]; + values[(size_t)index * 2 + 1] = + values[(size_t)reversed * 2 + 1]; + values[(size_t)reversed * 2] = real; + values[(size_t)reversed * 2 + 1] = imaginary; + } + } + } + + if (dimension > 1) { + uint32_t length = 2; + + for (;;) { + const uint32_t half = length / 2; + const uint32_t twiddle_step = dimension / length; + uint32_t block; + + for (block = 0; block < dimension; block += length) { + uint32_t offset; + + for (offset = 0; offset < half; ++offset) { + const uint32_t twiddle_index = offset * twiddle_step; + const uint32_t even_index = block + offset; + const uint32_t odd_index = even_index + half; + const double wr = + twiddles[(size_t)twiddle_index * 2]; + const double wi = inverse + ? -twiddles[(size_t)twiddle_index * 2 + 1] + : twiddles[(size_t)twiddle_index * 2 + 1]; + const double odd_real = + values[(size_t)odd_index * 2]; + const double odd_imaginary = + values[(size_t)odd_index * 2 + 1]; + const double transformed_real = + odd_real * wr - odd_imaginary * wi; + const double transformed_imaginary = + odd_real * wi + odd_imaginary * wr; + const double even_real = + values[(size_t)even_index * 2]; + const double even_imaginary = + values[(size_t)even_index * 2 + 1]; + + values[(size_t)even_index * 2] = + even_real + transformed_real; + values[(size_t)even_index * 2 + 1] = + even_imaginary + transformed_imaginary; + values[(size_t)odd_index * 2] = + even_real - transformed_real; + values[(size_t)odd_index * 2 + 1] = + even_imaginary - transformed_imaginary; + } + } + if (length == dimension) { + break; + } + length <<= 1; + } + } + + if (inverse) { + const double scale = (double)dimension; + + for (index = 0; index < dimension; ++index) { + values[(size_t)index * 2] /= scale; + values[(size_t)index * 2 + 1] /= scale; + } + } +} + +static void lecore_radix2_load_f64( + const lecore_context *context, + double *destination, + const double *source, + int involute) +{ + uint32_t index; + + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; + uint32_t source_index = involute && index != 0 + ? context->dimension - index + : index; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0; + } +} + +static void lecore_radix2_multiply_f64( + double *destination, + const double *left, + const double *right, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + const double left_real = left[(size_t)index * 2]; + const double left_imaginary = left[(size_t)index * 2 + 1]; + const double right_real = right[(size_t)index * 2]; + const double right_imaginary = right[(size_t)index * 2 + 1]; + + destination[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + destination[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } +} + +static void lecore_radix2_store_real_f64( + double *output, + const double *values, + uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + output[index] = values[(size_t)index * 2]; + } +} + +static void lecore_radix2_convolve_f64( + lecore_context *context, + const double *left_input, + const double *right_input, + int involute_right, + double *output) +{ + double *left = (double *)context->scratch; + double *right = left + (size_t)context->dimension * 2; + lecore_radix2_load_f64( + context, left, left_input, 0); + lecore_radix2_load_f64( + context, right, right_input, involute_right); + lecore_radix2_fft_f64(context, left, 0, 1); + lecore_radix2_fft_f64(context, right, 0, 1); + lecore_radix2_multiply_f64( + left, left, right, context->dimension); + lecore_radix2_fft_f64(context, left, 1, 0); + lecore_radix2_store_real_f64(output, left, context->dimension); +} + +static void lecore_radix2_convolve_fixed_left_f64( + lecore_context *context, + const double *fixed_left, + const double *right_rows, + size_t row_count, + size_t right_stride, + int involute_right, + double *out_rows, + size_t out_stride) +{ + double *left = (double *)context->scratch; + double *right = left + (size_t)context->dimension * 2; + size_t row; + + lecore_radix2_load_f64(context, left, fixed_left, 0); + lecore_radix2_fft_f64(context, left, 0, 1); + for (row = 0; row < row_count; ++row) { + lecore_radix2_load_f64( + context, + right, + right_rows + row * right_stride, + involute_right); + lecore_radix2_fft_f64(context, right, 0, 1); + lecore_radix2_multiply_f64( + right, left, right, context->dimension); + lecore_radix2_fft_f64(context, right, 1, 0); + lecore_radix2_store_real_f64( + out_rows + row * out_stride, right, context->dimension); + } +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output) +{ + lecore_radix2_convolve_f64(context, a, b, 0, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output) +{ + lecore_radix2_convolve_f64(context, composite, key, 1, output); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f64( + context, role, rows, row_count, row_stride, 0, out_rows, out_stride); +} + +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride) +{ + lecore_radix2_convolve_fixed_left_f64( + context, + trace, + keys, + key_count, + key_stride, + 1, + out_rows, + out_stride); +} + +#endif /* LECORE_ENABLE_RADIX2 */ diff --git a/native/liblecore/src/internal/format_internal.h b/native/liblecore/src/internal/format_internal.h new file mode 100644 index 0000000..fd0514d --- /dev/null +++ b/native/liblecore/src/internal/format_internal.h @@ -0,0 +1,20 @@ +#ifndef LECORE_INTERNAL_FORMAT_INTERNAL_H +#define LECORE_INTERNAL_FORMAT_INTERNAL_H + +#include +#include + +#ifndef LECORE_INTERNAL_API +# if defined(LECORE_AMALGAMATION) +# define LECORE_INTERNAL_API static +# else +# define LECORE_INTERNAL_API +# endif +#endif + +LECORE_INTERNAL_API uint64_t lecore_format_crc64_ecma_update( + uint64_t crc, + const uint8_t *data, + size_t data_bytes); + +#endif /* LECORE_INTERNAL_FORMAT_INTERNAL_H */ diff --git a/native/liblecore/src/internal/lecore_internal.h b/native/liblecore/src/internal/lecore_internal.h new file mode 100644 index 0000000..55bab3f --- /dev/null +++ b/native/liblecore/src/internal/lecore_internal.h @@ -0,0 +1,215 @@ +#ifndef LECORE_INTERNAL_H +#define LECORE_INTERNAL_H + +#include + +#include +#include +#include + +#if CHAR_BIT != 8 +# error "liblecore profiles require 8-bit bytes" +#endif + +#if FLT_RADIX != 2 +# error "liblecore profiles require radix-2 floating point" +#endif + +#if FLT_MANT_DIG != 24 || FLT_MIN_EXP != -125 || FLT_MAX_EXP != 128 +# error "liblecore HRR_F32_V1 requires IEEE-compatible binary32" +#endif + +#if DBL_MANT_DIG != 53 || DBL_MIN_EXP != -1021 || DBL_MAX_EXP != 1024 +# error "liblecore HRR_F64_V1 requires IEEE-compatible binary64" +#endif + +#if FLT_EVAL_METHOD != 0 +# error "liblecore profiles require operations evaluated in their declared precision" +#endif + +#ifndef LECORE_ENABLE_RADIX2 +# define LECORE_ENABLE_RADIX2 1 +#endif + +#if defined(__cplusplus) +static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#else +_Static_assert(sizeof(float) == 4, "liblecore requires 32-bit float"); +_Static_assert(sizeof(double) == 8, "liblecore requires 64-bit double"); +#endif + +#define LECORE_INTERNAL_CONTEXT_MAGIC UINT64_C(0x6c65636f72653030) + +/* Internal cross-translation-unit calls become file-local in the generated + * single-translation-unit amalgamation, so standalone objects expose only the + * documented ABI. */ +#if defined(LECORE_AMALGAMATION) +# define LECORE_INTERNAL_API static +#else +# define LECORE_INTERNAL_API +#endif + +struct lecore_context { + uint64_t magic; + uint32_t dimension; + lecore_profile profile; + lecore_backend backend; + lecore_validation validation; + lecore_scalar_type scalar_type; + lecore_allocator_v0 allocator; + void *scratch; + size_t scratch_bytes; + uint32_t *radix2_bit_reversal; + size_t radix2_bit_reversal_bytes; + void *radix2_twiddles; + size_t radix2_twiddle_bytes; + size_t context_bytes; + size_t allocation_alignment; +}; + +static inline lecore_status lecore_internal_check_context( + const lecore_context *context, + lecore_profile expected_profile) +{ + if (context == NULL || context->magic != LECORE_INTERNAL_CONTEXT_MAGIC) { + return LECORE_EINVAL; + } + if (context->profile != expected_profile) { + return LECORE_EPROFILE; + } + if (context->backend != LECORE_BACKEND_DIRECT && + context->backend != LECORE_BACKEND_RADIX2) { + return LECORE_EBACKEND; + } + return LECORE_OK; +} + +#if LECORE_ENABLE_RADIX2 +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f64( + lecore_context *context, + const double *a, + const double *b, + double *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( + lecore_context *context, + const double *composite, + const double *key, + double *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f64( + lecore_context *context, + const double *role, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f64( + lecore_context *context, + const double *trace, + const double *keys, + size_t key_count, + size_t key_stride, + double *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_f32( + lecore_context *context, + const float *a, + const float *b, + float *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( + lecore_context *context, + const float *composite, + const float *key, + float *output); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_bind_fixed_f32( + lecore_context *context, + const float *role, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_rows, + size_t out_stride); +LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_all_f32( + lecore_context *context, + const float *trace, + const float *keys, + size_t key_count, + size_t key_stride, + float *out_rows, + size_t out_stride); +#endif + +static inline int lecore_internal_ranges_overlap( + const void *left, + size_t left_bytes, + const void *right, + size_t right_bytes) +{ + uintptr_t left_address; + uintptr_t right_address; + + if (left_bytes == 0 || right_bytes == 0) { + return 0; + } + + left_address = (uintptr_t)left; + right_address = (uintptr_t)right; + if (left_address <= right_address) { + return (right_address - left_address) < left_bytes; + } + return (left_address - right_address) < right_bytes; +} + +static inline lecore_status lecore_internal_check_vector_alias( + const void *input, + const void *output, + size_t vector_bytes, + int allow_exact_alias) +{ + if (!lecore_internal_ranges_overlap( + input, vector_bytes, output, vector_bytes)) { + return LECORE_OK; + } + if (allow_exact_alias && input == output) { + return LECORE_OK; + } + return LECORE_EINVAL; +} + +static inline lecore_status lecore_internal_matrix_span( + uint32_t dimension, + size_t row_count, + size_t row_stride, + size_t scalar_bytes, + size_t *out_span_bytes) +{ + size_t last_row; + size_t span_elements; + + if (out_span_bytes == NULL) { + return LECORE_EINVAL; + } + *out_span_bytes = 0; + if (row_count == 0) { + return LECORE_EINVAL; + } + if (row_stride < (size_t)dimension) { + return LECORE_EDIM; + } + if (row_count - 1 > SIZE_MAX / row_stride) { + return LECORE_EOVERFLOW; + } + last_row = (row_count - 1) * row_stride; + if (last_row > SIZE_MAX - (size_t)dimension) { + return LECORE_EOVERFLOW; + } + span_elements = last_row + (size_t)dimension; + if (span_elements > SIZE_MAX / scalar_bytes) { + return LECORE_EOVERFLOW; + } + *out_span_bytes = span_elements * scalar_bytes; + return LECORE_OK; +} + +#endif /* LECORE_INTERNAL_H */ diff --git a/native/liblecore/src/status.c b/native/liblecore/src/status.c new file mode 100644 index 0000000..96d9802 --- /dev/null +++ b/native/liblecore/src/status.c @@ -0,0 +1,46 @@ +#include + +uint32_t LECORE_CALL lecore_abi_version(void) +{ + return LECORE_ABI_VERSION; +} + +uint32_t LECORE_CALL lecore_isa_version(void) +{ + return LECORE_ISA_VERSION; +} + +const char *LECORE_CALL lecore_version_string(void) +{ + return LECORE_VERSION_STRING_VALUE; +} + +const char *LECORE_CALL lecore_status_string(lecore_status status) +{ + switch (status) { + case LECORE_OK: + return "ok"; + case LECORE_EINVAL: + return "invalid argument"; + case LECORE_EDIM: + return "invalid dimension or stride"; + case LECORE_EPROFILE: + return "profile mismatch or unknown profile"; + case LECORE_EBACKEND: + return "unknown backend"; + case LECORE_EOVERFLOW: + return "size arithmetic overflow"; + case LECORE_ENOMEM: + return "allocation failed"; + case LECORE_EUNSUPPORTED: + return "unsupported operation or backend"; + case LECORE_ENONFINITE: + return "non-finite input"; + case LECORE_EFORMAT: + return "invalid interchange format"; + case LECORE_ECHECKSUM: + return "checksum mismatch"; + default: + return "unknown status"; + } +} diff --git a/native/liblecore/src/vector_f32.c b/native/liblecore/src/vector_f32.c new file mode 100644 index 0000000..ec1d60f --- /dev/null +++ b/native/liblecore/src/vector_f32.c @@ -0,0 +1,497 @@ +#include "internal/lecore_internal.h" + +#include +#include + +static int lecore_f32_vector_is_finite(const float *values, uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_f32_matrix_is_finite( + const float *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_f32_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static float lecore_f32_dot_raw( + const float *a, + const float *b, + uint32_t dimension) +{ + float result = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + result += a[index] * b[index]; + } + return result; +} + +static float lecore_f32_cosine_raw( + const float *a, + const float *b, + uint32_t dimension) +{ + float dot; + float norm_a_squared = 0.0f; + float norm_b_squared = 0.0f; + float norm_a; + float norm_b; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + norm_a_squared += a[index] * a[index]; + norm_b_squared += b[index] * b[index]; + } + if (norm_a_squared == 0.0f || norm_b_squared == 0.0f) { + return 0.0f; + } + norm_a = sqrtf(norm_a_squared); + norm_b = sqrtf(norm_b_squared); + dot = lecore_f32_dot_raw(a, b, dimension); + return dot / (norm_a * norm_b); +} + +static float lecore_f32_cosine_with_query_norm_squared( + const float *query, + float query_norm_squared, + float query_norm, + const float *row, + uint32_t dimension) +{ + float row_norm_squared = 0.0f; + float row_norm; + float dot; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + row_norm_squared += row[index] * row[index]; + } + if (query_norm_squared == 0.0f || row_norm_squared == 0.0f) { + return 0.0f; + } + row_norm = sqrtf(row_norm_squared); + dot = lecore_f32_dot_raw(query, row, dimension); + return dot / (query_norm * row_norm); +} + +static lecore_status lecore_f32_prepare_matrix_output( + const lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + const float *out_values, + size_t *out_rows_bytes) +{ + lecore_status status; + size_t output_bytes; + const size_t vector_bytes = (size_t)context->dimension * sizeof(float); + + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + out_rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(float)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(float); + if (lecore_internal_ranges_overlap( + query, vector_bytes, out_values, output_bytes) || + lecore_internal_ranges_overlap( + rows, *out_rows_bytes, out_values, output_bytes)) { + return LECORE_EINVAL; + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_validate_f32( + const float *values, + size_t count) +{ + size_t index; + + if (count == 0) { + return LECORE_OK; + } + if (values == NULL) { + return LECORE_EINVAL; + } + for (index = 0; index < count; ++index) { + if (!isfinite(values[index])) { + return LECORE_ENONFINITE; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_normalize_f32( + lecore_context *context, + const float *input, + float *output) +{ + lecore_status status; + float norm_squared = 0.0f; + float norm; + uint32_t index; + size_t vector_bytes; + + status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + norm_squared += input[index] * input[index]; + } + norm = sqrtf(norm_squared); + if (norm > 0.0f) { + for (index = 0; index < context->dimension; ++index) { + output[index] = input[index] / norm; + } + } else if (output != input) { + memcpy(output, input, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_dot) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_dot == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(a, context->dimension) || + !lecore_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_dot = lecore_f32_dot_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f32_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(query, context->dimension) || + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f32_dot_raw( + query, rows + row * row_stride, context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_f32( + lecore_context *context, + const float *a, + const float *b, + float *out_cosine) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_cosine == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(a, context->dimension) || + !lecore_f32_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_cosine = lecore_f32_cosine_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f32( + lecore_context *context, + const float *query, + const float *rows, + size_t row_count, + size_t row_stride, + float *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float query_norm; + float query_norm_squared = 0.0f; + size_t rows_bytes; + size_t row; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f32_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f32_vector_is_finite(query, context->dimension) || + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrtf(query_norm_squared); + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f32_cosine_with_query_norm_squared( + query, + query_norm_squared, + query_norm, + rows + row * row_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_involution_f32( + lecore_context *context, + const float *input, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + uint32_t index; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + if (input == output) { + for (index = 1; index < context->dimension - index; ++index) { + float temporary = output[index]; + output[index] = output[context->dimension - index]; + output[context->dimension - index] = temporary; + } + } else { + output[0] = input[0]; + for (index = 1; index < context->dimension; ++index) { + output[index] = input[context->dimension - index]; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_permute_f32( + lecore_context *context, + const float *input, + int64_t shift, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + float *target; + int64_t reduced_shift; + uint32_t normalized_shift; + uint32_t index; + uint32_t source; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + reduced_shift = shift % (int64_t)context->dimension; + if (reduced_shift < 0) { + reduced_shift += (int64_t)context->dimension; + } + normalized_shift = (uint32_t)reduced_shift; + target = input == output ? (float *)context->scratch : output; + + for (index = 0; index < context->dimension; ++index) { + source = index >= normalized_shift + ? index - normalized_shift + : context->dimension - (normalized_shift - index); + target[index] = input[source]; + } + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_bundle_f32( + lecore_context *context, + const float *rows, + size_t row_count, + size_t row_stride, + float *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t row; + uint32_t index; + float norm_squared = 0.0f; + float norm; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (rows == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(float); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + rows, rows_bytes, output, vector_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f32_matrix_is_finite( + rows, row_count, row_stride, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + output[index] = 0.0f; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + for (index = 0; index < context->dimension; ++index) { + output[index] += current[index]; + } + } + for (index = 0; index < context->dimension; ++index) { + norm_squared += output[index] * output[index]; + } + norm = sqrtf(norm_squared); + if (norm > 0.0f) { + for (index = 0; index < context->dimension; ++index) { + output[index] /= norm; + } + } + return LECORE_OK; +} diff --git a/native/liblecore/src/vector_f64.c b/native/liblecore/src/vector_f64.c new file mode 100644 index 0000000..617584d --- /dev/null +++ b/native/liblecore/src/vector_f64.c @@ -0,0 +1,586 @@ +#include "internal/lecore_internal.h" + +#include +#include + +static int lecore_f64_vector_is_finite(const double *values, uint32_t dimension) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + if (!isfinite(values[index])) { + return 0; + } + } + return 1; +} + +static int lecore_f64_matrix_is_finite( + const double *rows, + size_t row_count, + size_t row_stride, + uint32_t dimension) +{ + size_t row; + + for (row = 0; row < row_count; ++row) { + if (!lecore_f64_vector_is_finite( + rows + row * row_stride, dimension)) { + return 0; + } + } + return 1; +} + +static double lecore_f64_dot_raw( + const double *a, + const double *b, + uint32_t dimension) +{ + double result = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + result += a[index] * b[index]; + } + return result; +} + +static double lecore_f64_cosine_raw( + const double *a, + const double *b, + uint32_t dimension) +{ + double dot; + double norm_a_squared = 0.0; + double norm_b_squared = 0.0; + double norm_a; + double norm_b; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + norm_a_squared += a[index] * a[index]; + norm_b_squared += b[index] * b[index]; + } + + /* The exact zero branch precedes NaN/Inf propagation by contract. */ + if (norm_a_squared == 0.0 || norm_b_squared == 0.0) { + return 0.0; + } + norm_a = sqrt(norm_a_squared); + norm_b = sqrt(norm_b_squared); + dot = lecore_f64_dot_raw(a, b, dimension); + return dot / (norm_a * norm_b); +} + +static double lecore_f64_cosine_with_query_norm_squared( + const double *query, + double query_norm_squared, + double query_norm, + const double *row, + uint32_t dimension) +{ + double row_norm_squared = 0.0; + double row_norm; + double dot; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + row_norm_squared += row[index] * row[index]; + } + if (query_norm_squared == 0.0 || row_norm_squared == 0.0) { + return 0.0; + } + row_norm = sqrt(row_norm_squared); + dot = lecore_f64_dot_raw(query, row, dimension); + return dot / (query_norm * row_norm); +} + +static lecore_status lecore_f64_prepare_matrix_output( + const lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + const double *out_values, + size_t *out_rows_bytes) +{ + lecore_status status; + size_t output_bytes; + const size_t vector_bytes = (size_t)context->dimension * sizeof(double); + + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + out_rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(double)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(double); + if (lecore_internal_ranges_overlap( + query, vector_bytes, out_values, output_bytes) || + lecore_internal_ranges_overlap( + rows, *out_rows_bytes, out_values, output_bytes)) { + return LECORE_EINVAL; + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_validate_f64( + const double *values, + size_t count) +{ + size_t index; + + if (count == 0) { + return LECORE_OK; + } + if (values == NULL) { + return LECORE_EINVAL; + } + for (index = 0; index < count; ++index) { + if (!isfinite(values[index])) { + return LECORE_ENONFINITE; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_normalize_f64( + lecore_context *context, + const double *input, + double *output) +{ + lecore_status status; + double norm_squared = 0.0; + double norm; + uint32_t index; + size_t vector_bytes; + + status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + norm_squared += input[index] * input[index]; + } + norm = sqrt(norm_squared); + if (norm > 0.0) { + for (index = 0; index < context->dimension; ++index) { + output[index] = input[index] / norm; + } + } else if (output != input) { + memcpy(output, input, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_dot) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_dot == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(a, context->dimension) || + !lecore_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_dot = lecore_f64_dot_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_dot_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t row; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f64_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(query, context->dimension) || + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f64_dot_raw( + query, rows + row * row_stride, context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_f64( + lecore_context *context, + const double *a, + const double *b, + double *out_cosine) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + + if (status != LECORE_OK) { + return status; + } + if (a == NULL || b == NULL || out_cosine == NULL) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(a, context->dimension) || + !lecore_f64_vector_is_finite(b, context->dimension))) { + return LECORE_ENONFINITE; + } + *out_cosine = lecore_f64_cosine_raw(a, b, context->dimension); + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f64( + lecore_context *context, + const double *query, + const double *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double query_norm; + double query_norm_squared = 0.0; + size_t rows_bytes; + size_t row; + uint32_t index; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + status = lecore_f64_prepare_matrix_output( + context, + query, + rows, + row_count, + row_stride, + out_scores, + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + (!lecore_f64_vector_is_finite(query, context->dimension) || + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension))) { + return LECORE_ENONFINITE; + } + (void)rows_bytes; + for (index = 0; index < context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + query_norm = sqrt(query_norm_squared); + for (row = 0; row < row_count; ++row) { + out_scores[row] = lecore_f64_cosine_with_query_norm_squared( + query, + query_norm_squared, + query_norm, + rows + row * row_stride, + context->dimension); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_involution_f64( + lecore_context *context, + const double *input, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + uint32_t index; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + if (input == output) { + for (index = 1; index < context->dimension - index; ++index) { + double temporary = output[index]; + output[index] = output[context->dimension - index]; + output[context->dimension - index] = temporary; + } + } else { + output[0] = input[0]; + for (index = 1; index < context->dimension; ++index) { + output[index] = input[context->dimension - index]; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_permute_f64( + lecore_context *context, + const double *input, + int64_t shift, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + double *target; + int64_t reduced_shift; + uint32_t normalized_shift; + uint32_t index; + uint32_t source; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (input == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_check_vector_alias( + input, output, vector_bytes, 1); + if (status != LECORE_OK) { + return status; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_vector_is_finite(input, context->dimension)) { + return LECORE_ENONFINITE; + } + + reduced_shift = shift % (int64_t)context->dimension; + if (reduced_shift < 0) { + reduced_shift += (int64_t)context->dimension; + } + normalized_shift = (uint32_t)reduced_shift; + target = input == output ? (double *)context->scratch : output; + + for (index = 0; index < context->dimension; ++index) { + source = index >= normalized_shift + ? index - normalized_shift + : context->dimension - (normalized_shift - index); + target[index] = input[source]; + } + if (target != output) { + memcpy(output, target, vector_bytes); + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_bundle_f64( + lecore_context *context, + const double *rows, + size_t row_count, + size_t row_stride, + double *output) +{ + lecore_status status = lecore_internal_check_context( + context, LECORE_PROFILE_HRR_F64_V1); + size_t rows_bytes; + size_t row; + uint32_t index; + double norm_squared = 0.0; + double norm; + size_t vector_bytes; + + if (status != LECORE_OK) { + return status; + } + if (rows == NULL || output == NULL) { + return LECORE_EINVAL; + } + vector_bytes = (size_t)context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + context->dimension, + row_count, + row_stride, + sizeof(double), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (lecore_internal_ranges_overlap( + rows, rows_bytes, output, vector_bytes)) { + return LECORE_EINVAL; + } + if (context->validation == LECORE_VALIDATION_FINITE && + !lecore_f64_matrix_is_finite( + rows, row_count, row_stride, context->dimension)) { + return LECORE_ENONFINITE; + } + + for (index = 0; index < context->dimension; ++index) { + output[index] = 0.0; + } + for (row = 0; row < row_count; ++row) { + const double *current = rows + row * row_stride; + for (index = 0; index < context->dimension; ++index) { + output[index] += current[index]; + } + } + for (index = 0; index < context->dimension; ++index) { + norm_squared += output[index] * output[index]; + } + norm = sqrt(norm_squared); + if (norm > 0.0) { + for (index = 0; index < context->dimension; ++index) { + output[index] /= norm; + } + } + return LECORE_OK; +} + +lecore_status LECORE_CALL lecore_cosine_many_f64_f32( + lecore_context *f32_context, + const double *query, + const float *rows, + size_t row_count, + size_t row_stride, + double *out_scores) +{ + lecore_status status = lecore_internal_check_context( + f32_context, LECORE_PROFILE_HRR_F32_V1); + size_t rows_bytes; + size_t output_bytes; + size_t row; + uint32_t index; + double query_norm_squared = 0.0; + size_t query_bytes; + + if (status != LECORE_OK) { + return status; + } + if (query == NULL || rows == NULL || out_scores == NULL) { + return LECORE_EINVAL; + } + if ((uintmax_t)f32_context->dimension > + (uintmax_t)SIZE_MAX / (uintmax_t)sizeof(double)) { + return LECORE_EOVERFLOW; + } + query_bytes = (size_t)f32_context->dimension * sizeof(double); + status = lecore_internal_matrix_span( + f32_context->dimension, + row_count, + row_stride, + sizeof(float), + &rows_bytes); + if (status != LECORE_OK) { + return status; + } + if (row_count > SIZE_MAX / sizeof(double)) { + return LECORE_EOVERFLOW; + } + output_bytes = row_count * sizeof(double); + if (lecore_internal_ranges_overlap( + query, query_bytes, out_scores, output_bytes) || + lecore_internal_ranges_overlap( + rows, rows_bytes, out_scores, output_bytes)) { + return LECORE_EINVAL; + } + if (f32_context->validation == LECORE_VALIDATION_FINITE) { + if (!lecore_f64_vector_is_finite(query, f32_context->dimension)) { + return LECORE_ENONFINITE; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + for (index = 0; index < f32_context->dimension; ++index) { + if (!isfinite(current[index])) { + return LECORE_ENONFINITE; + } + } + } + } + + for (index = 0; index < f32_context->dimension; ++index) { + query_norm_squared += query[index] * query[index]; + } + for (row = 0; row < row_count; ++row) { + const float *current = rows + row * row_stride; + double dot; + double row_norm_squared = 0.0; + + for (index = 0; index < f32_context->dimension; ++index) { + const double component = (double)current[index]; + row_norm_squared += component * component; + } + if (query_norm_squared == 0.0 || row_norm_squared == 0.0) { + out_scores[row] = 0.0; + } else { + dot = 0.0; + for (index = 0; index < f32_context->dimension; ++index) { + dot += query[index] * (double)current[index]; + } + out_scores[row] = dot / + (sqrt(query_norm_squared) * sqrt(row_norm_squared)); + } + } + return LECORE_OK; +} diff --git a/native/liblecore/tests/CMakeLists.txt b/native/liblecore/tests/CMakeLists.txt new file mode 100644 index 0000000..8ee7940 --- /dev/null +++ b/native/liblecore/tests/CMakeLists.txt @@ -0,0 +1,138 @@ +function(lecore_add_c_test test_name source_file) + add_executable("${test_name}" "${source_file}") + target_compile_features("${test_name}" PRIVATE c_std_11) + target_link_libraries("${test_name}" PRIVATE lecore::lecore) + if(MSVC) + target_compile_options("${test_name}" PRIVATE /W4 /permissive-) + else() + target_compile_options("${test_name}" PRIVATE -Wall -Wextra -Wpedantic -Wconversion -Wshadow) + endif() + if(WIN32 AND LECORE_BUILD_SHARED) + add_custom_command(TARGET "${test_name}" POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$" + VERBATIM + ) + endif() + add_test(NAME "${test_name}" COMMAND "${test_name}") +endfunction() + +lecore_add_c_test(liblecore_test_api c/test_api.c) +target_compile_definitions(liblecore_test_api PRIVATE + LECORE_TEST_ISA_VERSION=${LECORE_ISA_VERSION} + LECORE_TEST_VERSION="${LECORE_VERSION}" +) +lecore_add_c_test(liblecore_test_numeric c/test_numeric.c) +lecore_add_c_test(liblecore_test_edges c/test_edges.c) + +if(LECORE_ENABLE_FORMAT) + lecore_add_c_test(liblecore_test_format c/test_format.c) +endif() + +add_executable(liblecore_test_cpp_header c/test_cpp_header.cpp) +target_compile_features(liblecore_test_cpp_header PRIVATE cxx_std_17) +target_link_libraries(liblecore_test_cpp_header PRIVATE lecore::lecore) +if(WIN32 AND LECORE_BUILD_SHARED) + add_custom_command(TARGET liblecore_test_cpp_header POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "$" "$" + VERBATIM + ) +endif() +add_test(NAME liblecore_test_cpp_header COMMAND liblecore_test_cpp_header) + +if(NOT CMAKE_CROSSCOMPILING AND NOT LECORE_ENABLE_SANITIZERS) + set(consumer_runner "${CMAKE_CURRENT_SOURCE_DIR}/consumers/run_installed_consumer.cmake") + add_test(NAME liblecore_consumer_cmake + COMMAND "${CMAKE_COMMAND}" + "-DLECORE_BUILD_DIR=${PROJECT_BINARY_DIR}" + "-DLECORE_CONSUMER_SOURCE=${CMAKE_CURRENT_SOURCE_DIR}/consumers/cmake-consumer" + "-DLECORE_TEST_ROOT=${PROJECT_BINARY_DIR}/_consumer_tests/cmake" + "-DLECORE_TEST_CONFIG=$" + "-DLECORE_INSTALL_BINDIR=${CMAKE_INSTALL_BINDIR}" + "-DLECORE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}" + "-DLECORE_EXPECT_FORMAT=${LECORE_ENABLE_FORMAT}" + "-DLECORE_EXPECT_SHARED=${LECORE_BUILD_SHARED}" + -P "${consumer_runner}" + ) + + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + add_test(NAME liblecore_consumer_pkgconfig + COMMAND "${CMAKE_COMMAND}" + "-DLECORE_BUILD_DIR=${PROJECT_BINARY_DIR}" + "-DLECORE_CONSUMER_SOURCE=${CMAKE_CURRENT_SOURCE_DIR}/consumers/pkgconfig-consumer" + "-DLECORE_TEST_ROOT=${PROJECT_BINARY_DIR}/_consumer_tests/pkgconfig" + "-DLECORE_TEST_CONFIG=$" + "-DLECORE_INSTALL_BINDIR=${CMAKE_INSTALL_BINDIR}" + "-DLECORE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}" + "-DLECORE_EXPECT_FORMAT=${LECORE_ENABLE_FORMAT}" + "-DLECORE_EXPECT_SHARED=${LECORE_BUILD_SHARED}" + -DLECORE_USE_PKG_CONFIG=ON + -P "${consumer_runner}" + ) + endif() + + add_test(NAME liblecore_variant_format_off + COMMAND "${CMAKE_COMMAND}" + "-DLECORE_SOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DLECORE_CONSUMER_SOURCE=${CMAKE_CURRENT_SOURCE_DIR}/consumers/cmake-consumer" + "-DLECORE_TEST_ROOT=${PROJECT_BINARY_DIR}/_consumer_tests/format-off" + "-DLECORE_TEST_CONFIG=$" + "-DLECORE_TEST_GENERATOR=${CMAKE_GENERATOR}" + "-DLECORE_TEST_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" + "-DLECORE_TEST_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET}" + -DLECORE_VARIANT_FORMAT=OFF + -DLECORE_VARIANT_RADIX2=ON + -P "${CMAKE_CURRENT_SOURCE_DIR}/consumers/run_format_off_variant.cmake" + ) + add_test(NAME liblecore_variant_radix2_off + COMMAND "${CMAKE_COMMAND}" + "-DLECORE_SOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DLECORE_CONSUMER_SOURCE=${CMAKE_CURRENT_SOURCE_DIR}/consumers/cmake-consumer" + "-DLECORE_TEST_ROOT=${PROJECT_BINARY_DIR}/_consumer_tests/radix2-off" + "-DLECORE_TEST_CONFIG=$" + "-DLECORE_TEST_GENERATOR=${CMAKE_GENERATOR}" + "-DLECORE_TEST_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" + "-DLECORE_TEST_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET}" + -DLECORE_VARIANT_FORMAT=ON + -DLECORE_VARIANT_RADIX2=OFF + -P "${CMAKE_CURRENT_SOURCE_DIR}/consumers/run_format_off_variant.cmake" + ) + add_test(NAME liblecore_variant_examples_only + COMMAND "${CMAKE_COMMAND}" + "-DLECORE_SOURCE_DIR=${PROJECT_SOURCE_DIR}" + "-DLECORE_TEST_ROOT=${PROJECT_BINARY_DIR}/_consumer_tests/examples-only" + "-DLECORE_TEST_GENERATOR=${CMAKE_GENERATOR}" + "-DLECORE_TEST_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" + "-DLECORE_TEST_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/consumers/run_examples_only.cmake" + ) +endif() + +if(LECORE_BUILD_SHARED AND NOT CMAKE_CROSSCOMPILING AND NOT LECORE_ENABLE_SANITIZERS) + find_package(Python3 COMPONENTS Interpreter QUIET) + set(python_conformance_script + "${PROJECT_SOURCE_DIR}/../../tests/native/test_liblecore_conformance.py") + if(Python3_Interpreter_FOUND AND EXISTS "${python_conformance_script}") + execute_process( + COMMAND "${Python3_EXECUTABLE}" -c "import numpy" + RESULT_VARIABLE numpy_import_result + OUTPUT_QUIET + ERROR_QUIET + ) + else() + set(numpy_import_result 1) + endif() + if(Python3_Interpreter_FOUND AND EXISTS "${python_conformance_script}" + AND numpy_import_result EQUAL 0) + add_test(NAME liblecore_python_conformance + COMMAND "${Python3_EXECUTABLE}" + "${python_conformance_script}" + --library "$" + ) + else() + message(STATUS + "Skipping Python conformance test (Python 3, NumPy, or harness unavailable)") + endif() +endif() diff --git a/native/liblecore/tests/c/test_api.c b/native/liblecore/tests/c/test_api.c new file mode 100644 index 0000000..72b7dc7 --- /dev/null +++ b/native/liblecore/tests/c/test_api.c @@ -0,0 +1,448 @@ +#include "test_common.h" + +#include +#include +#include + +typedef struct test_allocation { + void *pointer; + size_t bytes; + size_t alignment; + int active; +} test_allocation; + +typedef struct test_allocator_state { + _Alignas(max_align_t) unsigned char storage[65536]; + size_t used; + size_t allocation_count; + size_t deallocation_count; + size_t fail_at; + int return_misaligned; + int use_misalign_at; + size_t misalign_at; + int mismatch; + test_allocation allocations[16]; +} test_allocator_state; + +static void *LECORE_CALL test_allocate( + void *user, + size_t bytes, + size_t alignment) +{ + test_allocator_state *state = (test_allocator_state *)user; + uintptr_t begin; + uintptr_t aligned; + size_t padding; + void *pointer; + + if (bytes == 0 || alignment < _Alignof(max_align_t) || + (alignment & (alignment - 1)) != 0) { + state->mismatch = 1; + return NULL; + } + if (state->allocation_count == state->fail_at) { + ++state->allocation_count; + return NULL; + } + if (state->allocation_count >= 16) { + state->mismatch = 1; + return NULL; + } + + begin = (uintptr_t)(state->storage + state->used); + aligned = (begin + alignment - 1) & ~(uintptr_t)(alignment - 1); + padding = (size_t)(aligned - begin); + if (padding > sizeof(state->storage) - state->used || + bytes > sizeof(state->storage) - state->used - padding - 1) { + return NULL; + } + { + const int misalign = state->return_misaligned || + (state->use_misalign_at && + state->allocation_count == state->misalign_at); + pointer = (void *)(aligned + (misalign ? 1U : 0U)); + state->used += padding + bytes + (misalign ? 1U : 0U); + } + state->allocations[state->allocation_count].pointer = pointer; + state->allocations[state->allocation_count].bytes = bytes; + state->allocations[state->allocation_count].alignment = alignment; + state->allocations[state->allocation_count].active = 1; + ++state->allocation_count; + return pointer; +} + +static void LECORE_CALL test_deallocate( + void *user, + void *pointer, + size_t bytes, + size_t alignment) +{ + test_allocator_state *state = (test_allocator_state *)user; + size_t index; + + for (index = 0; index < state->allocation_count && index < 16; ++index) { + test_allocation *allocation = &state->allocations[index]; + if (allocation->pointer == pointer && allocation->active) { + if (allocation->bytes != bytes || allocation->alignment != alignment) { + state->mismatch = 1; + } + allocation->active = 0; + ++state->deallocation_count; + return; + } + } + state->mismatch = 1; +} + +static void configure_allocator( + lecore_config_v0 *config, + test_allocator_state *state) +{ + config->allocator.user = state; + config->allocator.allocate = test_allocate; + config->allocator.deallocate = test_deallocate; +} + +static int check_representative_f64_hot_path_allocations( + lecore_context *context, + test_allocator_state *state) +{ + const double a[8] = {1.0, 0.5, -0.25, 0.0, 0.75, -0.5, 0.25, 1.0}; + const double b[8] = {0.0, 1.0, 0.5, -0.5, 0.25, 0.0, -0.25, 0.75}; + const double rows[16] = { + 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }; + double output[16] = {0.0}; + double scores[2] = {0.0, 0.0}; + double score = 0.0; + size_t index = 0; + const size_t before = state->allocation_count; + + CHECK_STATUS(lecore_normalize_f64(context, a, output), LECORE_OK); + CHECK_STATUS(lecore_dot_f64(context, a, b, &score), LECORE_OK); + CHECK_STATUS(lecore_dot_many_f64(context, a, rows, 2, 8, scores), LECORE_OK); + CHECK_STATUS(lecore_cosine_f64(context, a, b, &score), LECORE_OK); + CHECK_STATUS(lecore_cosine_many_f64(context, a, rows, 2, 8, scores), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_f64(context, a, b, output), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_f64(context, a, b, output), LECORE_OK); + CHECK_STATUS(lecore_involution_f64(context, a, output), LECORE_OK); + CHECK_STATUS(lecore_permute_f64(context, a, INT64_C(-3), output), LECORE_OK); + CHECK_STATUS(lecore_bundle_f64(context, rows, 2, 8, output), LECORE_OK); + CHECK_STATUS(lecore_cleanup_f64( + context, a, rows, 2, 8, &index, &score), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_batch_f64( + context, rows, 8, rows, 8, 2, output, 8), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_fixed_f64( + context, a, rows, 2, 8, output, 8), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_all_f64( + context, a, rows, 2, 8, output, 8), LECORE_OK); + CHECK(state->allocation_count == before); + CHECK(state->deallocation_count == 0); + return EXIT_SUCCESS; +} + +static int check_representative_f32_hot_path_allocations( + lecore_context *context, + test_allocator_state *state) +{ + const float a[8] = {1.0F, 0.5F, -0.25F, 0.0F, 0.75F, -0.5F, 0.25F, 1.0F}; + const float b[8] = {0.0F, 1.0F, 0.5F, -0.5F, 0.25F, 0.0F, -0.25F, 0.75F}; + const float rows[16] = { + 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, + 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F + }; + const double query[8] = {1.0, 0.5, -0.25, 0.0, 0.75, -0.5, 0.25, 1.0}; + float output[16] = {0.0F}; + float scores[2] = {0.0F, 0.0F}; + double mixed_scores[2] = {0.0, 0.0}; + float score = 0.0F; + size_t index = 0; + const size_t before = state->allocation_count; + + CHECK_STATUS(lecore_normalize_f32(context, a, output), LECORE_OK); + CHECK_STATUS(lecore_dot_f32(context, a, b, &score), LECORE_OK); + CHECK_STATUS(lecore_dot_many_f32(context, a, rows, 2, 8, scores), LECORE_OK); + CHECK_STATUS(lecore_cosine_f32(context, a, b, &score), LECORE_OK); + CHECK_STATUS(lecore_cosine_many_f32(context, a, rows, 2, 8, scores), LECORE_OK); + CHECK_STATUS(lecore_cosine_many_f64_f32( + context, query, rows, 2, 8, mixed_scores), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_f32(context, a, b, output), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_f32(context, a, b, output), LECORE_OK); + CHECK_STATUS(lecore_involution_f32(context, a, output), LECORE_OK); + CHECK_STATUS(lecore_permute_f32(context, a, INT64_C(-3), output), LECORE_OK); + CHECK_STATUS(lecore_bundle_f32(context, rows, 2, 8, output), LECORE_OK); + CHECK_STATUS(lecore_cleanup_f32( + context, a, rows, 2, 8, &index, &score), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_batch_f32( + context, rows, 8, rows, 8, 2, output, 8), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_fixed_f32( + context, a, rows, 2, 8, output, 8), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_all_f32( + context, a, rows, 2, 8, output, 8), LECORE_OK); + CHECK(state->allocation_count == before); + CHECK(state->deallocation_count == 0); + return EXIT_SUCCESS; +} + +static int test_introspection_and_defaults(void) +{ + lecore_config_v0 config; + uint64_t expected_caps = LECORE_CAP_HRR_F64 | LECORE_CAP_HRR_F32 | + LECORE_CAP_DIRECT | LECORE_CAP_BATCH | LECORE_CAP_MIXED_F64_F32 | + LECORE_CAP_FINITE_VALIDATION; + size_t index; + + CHECK(lecore_abi_version() == UINT32_C(0)); + CHECK(lecore_abi_version() == LECORE_ABI_VERSION); + CHECK(lecore_isa_version() == (uint32_t)LECORE_TEST_ISA_VERSION); + CHECK(lecore_version_string() != NULL); + CHECK(strcmp(lecore_version_string(), LECORE_TEST_VERSION) == 0); +#if LECORE_ENABLE_FORMAT + expected_caps |= LECORE_CAP_FORMAT; +#endif +#if LECORE_ENABLE_RADIX2 + expected_caps |= LECORE_CAP_RADIX2; +#endif + CHECK((lecore_capabilities() & expected_caps) == expected_caps); +#if !LECORE_ENABLE_RADIX2 + CHECK((lecore_capabilities() & LECORE_CAP_RADIX2) == 0); +#endif + + for (index = LECORE_OK; index <= LECORE_ECHECKSUM; ++index) { + CHECK(lecore_status_string((lecore_status)index) != NULL); + CHECK(lecore_status_string((lecore_status)index)[0] != '\0'); + } + CHECK(strcmp(lecore_status_string(UINT32_C(999)), "unknown status") == 0); + + memset(&config, 0xa5, sizeof(config)); + lecore_config_init_v0(&config); + CHECK(config.struct_size == sizeof(config)); + CHECK(config.abi_version == LECORE_ABI_VERSION); + CHECK(config.profile == LECORE_PROFILE_HRR_F64_V1); + CHECK(config.backend == LECORE_BACKEND_AUTO); + CHECK(config.validation == LECORE_VALIDATION_SHAPE); + CHECK(config.dimension == 0); + CHECK(config.flags == 0); + CHECK(config.allocator.struct_size == sizeof(config.allocator)); + CHECK(config.allocator.user == NULL); + CHECK(config.allocator.allocate == NULL); + CHECK(config.allocator.deallocate == NULL); + for (index = 0; index < sizeof(config.reserved) / sizeof(config.reserved[0]); ++index) { + CHECK(config.reserved[index] == 0); + } + lecore_config_init_v0(NULL); + return EXIT_SUCCESS; +} + +static int test_context_validation(void) +{ + lecore_config_v0 config; + lecore_context *context = (lecore_context *)(uintptr_t)1; + + CHECK_STATUS(lecore_context_create(NULL, &context), LECORE_EINVAL); + CHECK(context == NULL); + CHECK_STATUS(lecore_context_create(NULL, NULL), LECORE_EINVAL); + + lecore_config_init_v0(&config); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EDIM); + CHECK(context == NULL); + + config.dimension = 4; + config.struct_size = 0; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + config.struct_size = (uint32_t)sizeof(config); + config.abi_version = UINT32_C(999); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + config.abi_version = LECORE_ABI_VERSION; + config.profile = UINT32_C(999); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EPROFILE); + config.profile = LECORE_PROFILE_HRR_F64_V1; + config.backend = LECORE_BACKEND_RADIX2; +#if LECORE_ENABLE_RADIX2 + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(context != NULL); + CHECK(lecore_context_backend(context) == LECORE_BACKEND_RADIX2); + lecore_context_destroy(context); + context = NULL; +#else + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EUNSUPPORTED); +#endif + config.backend = UINT32_C(999); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EBACKEND); + config.backend = LECORE_BACKEND_AUTO; + config.validation = UINT32_C(999); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + config.validation = LECORE_VALIDATION_SHAPE; + config.flags = 1; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + config.flags = 0; + config.reserved[0] = 1; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + config.reserved[0] = 0; + config.allocator.allocate = test_allocate; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + CHECK(context == NULL); + return EXIT_SUCCESS; +} + +static int test_context_and_allocator(void) +{ + lecore_config_v0 config; + lecore_context *context = NULL; + test_allocator_state state; + + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + lecore_config_init_v0(&config); + config.dimension = 8; + config.validation = LECORE_VALIDATION_FINITE; + configure_allocator(&config, &state); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(context != NULL); + CHECK(lecore_context_dimension(context) == 8); + CHECK(lecore_context_profile(context) == LECORE_PROFILE_HRR_F64_V1); + CHECK(lecore_context_backend(context) == LECORE_BACKEND_DIRECT); + CHECK(lecore_context_validation(context) == LECORE_VALIDATION_FINITE); + CHECK(lecore_context_scalar_type(context) == LECORE_SCALAR_F64); + CHECK(lecore_context_scratch_bytes(context) == 8 * sizeof(double)); + CHECK(state.allocation_count > 0); + CHECK(state.deallocation_count == 0); + CHECK(check_representative_f64_hot_path_allocations(context, &state) == EXIT_SUCCESS); + lecore_context_destroy(context); + CHECK(state.deallocation_count == state.allocation_count); + CHECK(state.mismatch == 0); + + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + lecore_config_init_v0(&config); + config.dimension = 8; + config.profile = LECORE_PROFILE_HRR_F32_V1; + config.validation = LECORE_VALIDATION_FINITE; + configure_allocator(&config, &state); + context = NULL; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(context != NULL); + CHECK(lecore_context_scalar_type(context) == LECORE_SCALAR_F32); + CHECK(lecore_context_scratch_bytes(context) == 8 * sizeof(float)); + CHECK(check_representative_f32_hot_path_allocations(context, &state) == EXIT_SUCCESS); + lecore_context_destroy(context); + CHECK(state.deallocation_count == state.allocation_count); + CHECK(state.mismatch == 0); + +#if LECORE_ENABLE_RADIX2 + { + size_t failure_index; + + for (failure_index = 0; failure_index < 4; ++failure_index) { + memset(&state, 0, sizeof(state)); + state.fail_at = failure_index; + lecore_config_init_v0(&config); + config.dimension = 8; + config.backend = LECORE_BACKEND_RADIX2; + configure_allocator(&config, &state); + context = (lecore_context *)(uintptr_t)1; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_ENOMEM); + CHECK(context == NULL); + CHECK(state.deallocation_count == failure_index); + CHECK(state.mismatch == 0); + } + + for (failure_index = 0; failure_index < 4; ++failure_index) { + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + state.use_misalign_at = 1; + state.misalign_at = failure_index; + lecore_config_init_v0(&config); + config.dimension = 8; + config.backend = LECORE_BACKEND_RADIX2; + configure_allocator(&config, &state); + context = (lecore_context *)(uintptr_t)1; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + CHECK(context == NULL); + CHECK(state.deallocation_count == failure_index + 1); + CHECK(state.mismatch == 0); + } + } + + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + lecore_config_init_v0(&config); + config.dimension = 8; + config.backend = LECORE_BACKEND_RADIX2; + configure_allocator(&config, &state); + context = NULL; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(context != NULL); + CHECK(lecore_context_backend(context) == LECORE_BACKEND_RADIX2); + CHECK(lecore_context_scratch_bytes(context) == 4 * 8 * sizeof(double)); + CHECK(state.allocation_count > 2); + CHECK(check_representative_f64_hot_path_allocations(context, &state) == EXIT_SUCCESS); + lecore_context_destroy(context); + CHECK(state.deallocation_count == state.allocation_count); + CHECK(state.mismatch == 0); + + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + lecore_config_init_v0(&config); + config.dimension = 8; + config.profile = LECORE_PROFILE_HRR_F32_V1; + config.backend = LECORE_BACKEND_RADIX2; + configure_allocator(&config, &state); + context = NULL; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(context != NULL); + CHECK(lecore_context_scratch_bytes(context) == 4 * 8 * sizeof(float)); + CHECK(check_representative_f32_hot_path_allocations(context, &state) == EXIT_SUCCESS); + lecore_context_destroy(context); + CHECK(state.deallocation_count == state.allocation_count); + CHECK(state.mismatch == 0); +#endif + + CHECK(lecore_context_dimension(NULL) == 0); + CHECK(lecore_context_profile(NULL) == 0); + CHECK(lecore_context_backend(NULL) == 0); + CHECK(lecore_context_validation(NULL) == 0); + CHECK(lecore_context_scalar_type(NULL) == 0); + CHECK(lecore_context_scratch_bytes(NULL) == 0); + lecore_context_destroy(NULL); + + memset(&state, 0, sizeof(state)); + state.fail_at = 0; + lecore_config_init_v0(&config); + config.dimension = 4; + configure_allocator(&config, &state); + context = (lecore_context *)(uintptr_t)1; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_ENOMEM); + CHECK(context == NULL); + + memset(&state, 0, sizeof(state)); + state.fail_at = 1; + lecore_config_init_v0(&config); + config.dimension = 4; + configure_allocator(&config, &state); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_ENOMEM); + CHECK(context == NULL); + CHECK(state.deallocation_count == 1); + CHECK(state.mismatch == 0); + + memset(&state, 0, sizeof(state)); + state.fail_at = SIZE_MAX; + state.return_misaligned = 1; + lecore_config_init_v0(&config); + config.dimension = 4; + configure_allocator(&config, &state); + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EINVAL); + CHECK(context == NULL); + CHECK(state.deallocation_count == 1); + CHECK(state.mismatch == 0); + return EXIT_SUCCESS; +} + +int main(void) +{ + CHECK(test_introspection_and_defaults() == EXIT_SUCCESS); + CHECK(test_context_validation() == EXIT_SUCCESS); + CHECK(test_context_and_allocator() == EXIT_SUCCESS); + return EXIT_SUCCESS; +} diff --git a/native/liblecore/tests/c/test_common.h b/native/liblecore/tests/c/test_common.h new file mode 100644 index 0000000..e7fb416 --- /dev/null +++ b/native/liblecore/tests/c/test_common.h @@ -0,0 +1,61 @@ +#ifndef LECORE_TEST_COMMON_H +#define LECORE_TEST_COMMON_H + +#include + +#include +#include +#include + +#define CHECK(condition) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "%s:%d: check failed: %s\n", \ + __FILE__, __LINE__, #condition); \ + return EXIT_FAILURE; \ + } \ + } while (0) + +#define CHECK_STATUS(expression, expected) \ + do { \ + const lecore_status actual_status_ = (expression); \ + if (actual_status_ != (expected)) { \ + fprintf(stderr, \ + "%s:%d: %s returned %u (%s), expected %u (%s)\n", \ + __FILE__, __LINE__, #expression, \ + (unsigned)actual_status_, \ + lecore_status_string(actual_status_), \ + (unsigned)(expected), lecore_status_string(expected)); \ + return EXIT_FAILURE; \ + } \ + } while (0) + +static inline int lecore_test_close_f64(double actual, double expected, double tolerance) +{ + return fabs(actual - expected) <= tolerance; +} + +static inline int lecore_test_close_f32(float actual, float expected, float tolerance) +{ + return fabsf(actual - expected) <= tolerance; +} + +static inline lecore_context *lecore_test_context( + uint32_t dimension, + lecore_profile profile, + lecore_validation validation) +{ + lecore_config_v0 config; + lecore_context *context = NULL; + + lecore_config_init_v0(&config); + config.dimension = dimension; + config.profile = profile; + config.validation = validation; + if (lecore_context_create(&config, &context) != LECORE_OK) { + return NULL; + } + return context; +} + +#endif /* LECORE_TEST_COMMON_H */ diff --git a/native/liblecore/tests/c/test_cpp_header.cpp b/native/liblecore/tests/c/test_cpp_header.cpp new file mode 100644 index 0000000..58ef883 --- /dev/null +++ b/native/liblecore/tests/c/test_cpp_header.cpp @@ -0,0 +1,29 @@ +#include + +#if LECORE_ENABLE_FORMAT +#include +#endif + +#include +#include + +static_assert(std::is_standard_layout::value, + "the public configuration must remain standard-layout"); + +int main() +{ + lecore_config_v0 config{}; + lecore_config_init_v0(&config); + if (lecore_abi_version() != UINT32_C(0) || + config.struct_size != sizeof(config)) { + return 1; + } +#if LECORE_ENABLE_FORMAT + lecore_format_descriptor_v1 descriptor{}; + lecore_format_descriptor_init_v1(&descriptor); + if (descriptor.header_bytes != LECORE_FORMAT_HEADER_BYTES) { + return 2; + } +#endif + return 0; +} diff --git a/native/liblecore/tests/c/test_edges.c b/native/liblecore/tests/c/test_edges.c new file mode 100644 index 0000000..9a304f8 --- /dev/null +++ b/native/liblecore/tests/c/test_edges.c @@ -0,0 +1,123 @@ +#include "test_common.h" + +#include +#include + +static int test_validation_modes(void) +{ + lecore_context *shape_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + lecore_context *finite_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_FINITE); + const double finite[] = {1.0, 0.0, 0.0, 0.0}; + const double zero[] = {0.0, 0.0, 0.0, 0.0}; + const double with_nan[] = {NAN, 1.0, 0.0, 0.0}; + const double with_inf[] = {INFINITY, 1.0, 0.0, 0.0}; + const double candidates[] = { + 1.0, 0.0, 0.0, 0.0, + NAN, 1.0, 0.0, 0.0, + 1.0, 0.0, 0.0, 0.0 + }; + double output[] = {42.0, 42.0, 42.0, 42.0}; + double scores[] = {42.0, 42.0, 42.0}; + double score = 42.0; + size_t index = SIZE_MAX; + + CHECK(shape_context != NULL && finite_context != NULL); + CHECK_STATUS(lecore_validate_f64(NULL, 0), LECORE_OK); + CHECK_STATUS(lecore_validate_f32(NULL, 0), LECORE_OK); + CHECK_STATUS(lecore_validate_f64(NULL, 1), LECORE_EINVAL); + CHECK_STATUS(lecore_validate_f64(finite, 4), LECORE_OK); + CHECK_STATUS(lecore_validate_f64(with_nan, 4), LECORE_ENONFINITE); + CHECK_STATUS(lecore_validate_f64(with_inf, 4), LECORE_ENONFINITE); + + CHECK_STATUS(lecore_hrr_bind_f64( + finite_context, finite, with_nan, output), LECORE_ENONFINITE); + CHECK(output[0] == 42.0 && output[3] == 42.0); + CHECK_STATUS(lecore_hrr_bind_f64( + shape_context, finite, with_nan, output), LECORE_OK); + CHECK(isnan(output[0]) && isnan(output[1]) && isnan(output[2]) && isnan(output[3])); + + CHECK_STATUS(lecore_cosine_f64(shape_context, zero, with_nan, &score), LECORE_OK); + CHECK(score == 0.0 && !signbit(score)); + score = 42.0; + CHECK_STATUS(lecore_cosine_f64(finite_context, zero, with_nan, &score), LECORE_ENONFINITE); + CHECK(score == 42.0); + + CHECK_STATUS(lecore_cleanup_f64( + shape_context, finite, candidates, 3, 4, &index, &score), LECORE_OK); + CHECK(index == 1); + CHECK(isnan(score)); + + CHECK_STATUS(lecore_cosine_many_f64( + shape_context, zero, candidates, 3, 4, scores), LECORE_OK); + CHECK(scores[0] == 0.0 && !signbit(scores[0])); + CHECK(scores[1] == 0.0 && !signbit(scores[1])); + CHECK(scores[2] == 0.0 && !signbit(scores[2])); + index = SIZE_MAX; + score = 42.0; + CHECK_STATUS(lecore_cleanup_f64( + shape_context, zero, candidates, 3, 4, &index, &score), LECORE_OK); + CHECK(index == 0); + CHECK(score == 0.0 && !signbit(score)); + + lecore_context_destroy(finite_context); + lecore_context_destroy(shape_context); + return EXIT_SUCCESS; +} + +static int test_errors_aliases_and_overflow(void) +{ + lecore_context *f64_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + lecore_context *f32_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F32_V1, LECORE_VALIDATION_SHAPE); + const double vector[] = {1.0, 2.0, 3.0, 4.0}; + const double rows[] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0 + }; + double overlap[9] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; + double output[8] = {0.0}; + double score = 17.0; + float f32_output[4]; + size_t index = 17; + + CHECK(f64_context != NULL && f32_context != NULL); + CHECK_STATUS(lecore_normalize_f64(NULL, vector, output), LECORE_EINVAL); + CHECK_STATUS(lecore_normalize_f64(f64_context, NULL, output), LECORE_EINVAL); + CHECK_STATUS(lecore_normalize_f32(f64_context, (const float *)vector, f32_output), LECORE_EPROFILE); + CHECK_STATUS(lecore_normalize_f64(f32_context, vector, output), LECORE_EPROFILE); + CHECK_STATUS(lecore_normalize_f64(f64_context, overlap, overlap + 1), LECORE_EINVAL); + CHECK_STATUS(lecore_hrr_bind_f64(f64_context, overlap, vector, overlap + 1), LECORE_EINVAL); + + CHECK_STATUS(lecore_dot_many_f64(f64_context, vector, rows, 0, 4, output), LECORE_EINVAL); + CHECK_STATUS(lecore_dot_many_f64(f64_context, vector, rows, 2, 3, output), LECORE_EDIM); + CHECK_STATUS(lecore_dot_many_f64( + f64_context, vector, rows, 2, SIZE_MAX, output), LECORE_EOVERFLOW); + CHECK_STATUS(lecore_bundle_f64(f64_context, rows, 0, 4, output), LECORE_EINVAL); + CHECK_STATUS(lecore_bundle_f64(f64_context, rows, 2, 4, (double *)rows), LECORE_EINVAL); + CHECK_STATUS(lecore_cleanup_f64( + f64_context, vector, rows, 0, 4, &index, &score), LECORE_EINVAL); + CHECK(index == 17 && score == 17.0); + + CHECK_STATUS(lecore_hrr_bind_batch_f64( + f64_context, rows, 4, rows, 4, 2, output, 4), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_batch_f64( + f64_context, rows, 4, rows, 4, 2, (double *)rows, 4), LECORE_EINVAL); + CHECK_STATUS(lecore_hrr_bind_batch_f64( + f64_context, rows, 3, rows, 4, 2, output, 4), LECORE_EDIM); + CHECK_STATUS(lecore_hrr_bind_batch_f64( + f64_context, rows, 4, rows, 4, 0, output, 4), LECORE_EINVAL); + + lecore_context_destroy(f32_context); + lecore_context_destroy(f64_context); + return EXIT_SUCCESS; +} + +int main(void) +{ + CHECK(test_validation_modes() == EXIT_SUCCESS); + CHECK(test_errors_aliases_and_overflow() == EXIT_SUCCESS); + return EXIT_SUCCESS; +} diff --git a/native/liblecore/tests/c/test_format.c b/native/liblecore/tests/c/test_format.c new file mode 100644 index 0000000..7eecc94 --- /dev/null +++ b/native/liblecore/tests/c/test_format.c @@ -0,0 +1,315 @@ +#include "test_common.h" + +#include + +#include +#include + +static const uint8_t golden_header[LECORE_FORMAT_HEADER_BYTES] = { + 0x4c, 0x45, 0x43, 0x4f, 0x52, 0x45, 0x56, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x54, 0x2b, 0xeb, 0x98, 0xdc, 0xcc, 0xb6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const uint8_t golden_payload[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xc0 +}; + +static uint64_t test_crc64(const uint8_t *bytes, size_t count) +{ + const uint64_t polynomial = UINT64_C(0x42f0e1eba9ea3693); + uint64_t crc = UINT64_C(0); + size_t byte_index; + + for (byte_index = 0; byte_index < count; ++byte_index) { + unsigned bit_index; + crc ^= (uint64_t)bytes[byte_index] << 56; + for (bit_index = 0; bit_index < 8; ++bit_index) { + crc = (crc & UINT64_C(0x8000000000000000)) != 0 + ? (crc << 1) ^ polynomial + : crc << 1; + } + } + return crc; +} + +static void put_u16_le(uint8_t *bytes, uint16_t value) +{ + bytes[0] = (uint8_t)value; + bytes[1] = (uint8_t)(value >> 8); +} + +static void put_u64_le(uint8_t *bytes, uint64_t value) +{ + unsigned index; + for (index = 0; index < 8; ++index) { + bytes[index] = (uint8_t)(value >> (index * 8)); + } +} + +static void set_record_crc(uint8_t *record, size_t record_bytes) +{ + uint64_t crc; + memset(record + 80, 0, 8); + crc = test_crc64(record, record_bytes); + put_u64_le(record + 80, crc); +} + +static void make_descriptor(lecore_format_descriptor_v1 *descriptor) +{ + lecore_format_descriptor_init_v1(descriptor); + descriptor->artifact_kind = LECORE_ARTIFACT_VECTOR; + descriptor->dimension = 2; + descriptor->vector_count = UINT64_C(1); + descriptor->payload_bytes = UINT64_C(16); +} + +static int test_golden_and_roundtrip(void) +{ + union descriptor_record_storage { + max_align_t alignment; + uint8_t bytes[256]; + } descriptor_record; + lecore_format_descriptor_v1 descriptor; + lecore_format_descriptor_v1 *overlapping_descriptor; + lecore_format_descriptor_v1 decoded; + uint8_t record[112]; + uint8_t overlap_record[112]; + const void *decoded_payload = NULL; + size_t decoded_payload_bytes = 0; + size_t record_bytes = 0; + + memset(&descriptor, 0xa5, sizeof(descriptor)); + lecore_format_descriptor_init_v1(&descriptor); + CHECK(descriptor.struct_size == sizeof(descriptor)); + CHECK(descriptor.format_major == LECORE_FORMAT_MAJOR); + CHECK(descriptor.format_minor == LECORE_FORMAT_MINOR); + CHECK(descriptor.header_bytes == LECORE_FORMAT_HEADER_BYTES); + CHECK(descriptor.semantic_profile == LECORE_PROFILE_HRR_F64_V1); + CHECK(descriptor.scalar_type == LECORE_SCALAR_F64); + CHECK(descriptor.normalization_policy == LECORE_NORMALIZATION_RAW); + CHECK(descriptor.atom_scheme == LECORE_ATOM_EXTERNAL); + lecore_format_descriptor_init_v1(NULL); + + make_descriptor(&descriptor); + CHECK_STATUS(lecore_format_encoded_size_v1(&descriptor, &record_bytes), LECORE_OK); + CHECK(record_bytes == sizeof(record)); + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, + golden_payload, + sizeof(golden_payload), + record, + sizeof(record), + &record_bytes), LECORE_OK); + CHECK(record_bytes == sizeof(record)); + CHECK(memcmp(record, golden_header, sizeof(golden_header)) == 0); + CHECK(memcmp(record + sizeof(golden_header), golden_payload, sizeof(golden_payload)) == 0); + CHECK_STATUS(lecore_format_check_v1(record, sizeof(record)), LECORE_OK); + + CHECK_STATUS(lecore_format_decode_v1( + record, + sizeof(record), + &decoded, + &decoded_payload, + &decoded_payload_bytes), LECORE_OK); + CHECK(decoded_payload == record + LECORE_FORMAT_HEADER_BYTES); + CHECK(decoded_payload_bytes == sizeof(golden_payload)); + CHECK(memcmp(decoded_payload, golden_payload, sizeof(golden_payload)) == 0); + CHECK(decoded.content_crc64 == UINT64_C(0xb6ccdc98eb2b543c)); + CHECK(decoded.dimension == descriptor.dimension); + CHECK_STATUS(lecore_format_check_compatibility_v1(&descriptor, &decoded), LECORE_OK); + + memcpy(overlap_record, golden_payload, sizeof(golden_payload)); + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, + overlap_record, + sizeof(golden_payload), + overlap_record, + sizeof(overlap_record), + &record_bytes), LECORE_OK); + CHECK(memcmp(overlap_record, record, sizeof(record)) == 0); + + overlapping_descriptor = + (lecore_format_descriptor_v1 *)(void *)descriptor_record.bytes; + make_descriptor(overlapping_descriptor); + CHECK_STATUS(lecore_format_encode_v1( + overlapping_descriptor, + golden_payload, + sizeof(golden_payload), + descriptor_record.bytes, + sizeof(descriptor_record.bytes), + &record_bytes), LECORE_OK); + CHECK(memcmp(descriptor_record.bytes, record, sizeof(record)) == 0); + return EXIT_SUCCESS; +} + +static int test_descriptor_errors(void) +{ + lecore_format_descriptor_v1 descriptor; + size_t record_bytes = 99; + + make_descriptor(&descriptor); + CHECK_STATUS(lecore_format_encoded_size_v1(NULL, &record_bytes), LECORE_EINVAL); + CHECK(record_bytes == 0); + CHECK_STATUS(lecore_format_encoded_size_v1(&descriptor, NULL), LECORE_EINVAL); + +#define CHECK_DESCRIPTOR_ERROR(field, value, expected_status) \ + do { \ + lecore_format_descriptor_v1 changed_ = descriptor; \ + changed_.field = (value); \ + record_bytes = 99; \ + CHECK_STATUS(lecore_format_encoded_size_v1( \ + &changed_, &record_bytes), (expected_status)); \ + CHECK(record_bytes == 0); \ + } while (0) + + CHECK_DESCRIPTOR_ERROR(struct_size, 0, LECORE_EINVAL); + CHECK_DESCRIPTOR_ERROR(format_major, 2, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(format_minor, 1, LECORE_EUNSUPPORTED); + CHECK_DESCRIPTOR_ERROR(header_bytes, 95, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(header_bytes, 97, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(flags, 1, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(artifact_kind, 99, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(semantic_profile, 99, LECORE_EPROFILE); + CHECK_DESCRIPTOR_ERROR(scalar_type, LECORE_SCALAR_F32, LECORE_EPROFILE); + CHECK_DESCRIPTOR_ERROR(dimension, 0, LECORE_EDIM); + CHECK_DESCRIPTOR_ERROR(normalization_policy, 99, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(atom_scheme, 99, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(vector_count, 0, LECORE_EFORMAT); + CHECK_DESCRIPTOR_ERROR(payload_bytes, 8, LECORE_EFORMAT); + + descriptor.reserved[0] = 1; + CHECK_STATUS(lecore_format_encoded_size_v1(&descriptor, &record_bytes), LECORE_EINVAL); + descriptor.reserved[0] = 0; + descriptor.dimension = UINT32_MAX; + descriptor.vector_count = UINT64_MAX; + descriptor.payload_bytes = UINT64_MAX; + CHECK_STATUS(lecore_format_encoded_size_v1(&descriptor, &record_bytes), LECORE_EOVERFLOW); +#undef CHECK_DESCRIPTOR_ERROR + return EXIT_SUCCESS; +} + +static int test_record_errors_and_forward_minor(void) +{ + lecore_format_descriptor_v1 descriptor; + lecore_format_descriptor_v1 decoded; + uint8_t record[112]; + uint8_t changed[113]; + const void *payload = (const void *)(uintptr_t)1; + size_t payload_bytes = 99; + size_t record_bytes = 0; + + make_descriptor(&descriptor); + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, golden_payload, sizeof(golden_payload), + record, sizeof(record), &record_bytes), LECORE_OK); + + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, golden_payload, sizeof(golden_payload) - 1, + changed, sizeof(changed), &record_bytes), LECORE_EFORMAT); + CHECK(record_bytes == sizeof(record)); + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, golden_payload, sizeof(golden_payload), + changed, sizeof(record) - 1, &record_bytes), LECORE_EINVAL); + CHECK(record_bytes == sizeof(record)); + CHECK_STATUS(lecore_format_encode_v1( + &descriptor, NULL, sizeof(golden_payload), + changed, sizeof(changed), &record_bytes), LECORE_EINVAL); + + memset(&decoded, 0xa5, sizeof(decoded)); + CHECK_STATUS(lecore_format_decode_v1( + NULL, sizeof(record), &decoded, &payload, &payload_bytes), LECORE_EINVAL); + CHECK(decoded.struct_size == sizeof(decoded)); + CHECK(payload == NULL && payload_bytes == 0); + CHECK_STATUS(lecore_format_decode_v1( + record, 95, &decoded, &payload, &payload_bytes), LECORE_EFORMAT); + CHECK_STATUS(lecore_format_decode_v1( + record, sizeof(record) - 1, &decoded, &payload, &payload_bytes), LECORE_EFORMAT); + memcpy(changed, record, sizeof(record)); + changed[sizeof(record)] = 0; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(changed)), LECORE_EFORMAT); + + memcpy(changed, record, sizeof(record)); + changed[0] ^= 1; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EFORMAT); + memcpy(changed, record, sizeof(record)); + changed[8] = 2; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EFORMAT); + memcpy(changed, record, sizeof(record)); + changed[14] = 1; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EFORMAT); + memcpy(changed, record, sizeof(record)); + changed[24] = LECORE_SCALAR_F32; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EPROFILE); + memcpy(changed, record, sizeof(record)); + changed[28] = 0; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EDIM); + memcpy(changed, record, sizeof(record)); + changed[88] = 1; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_EFORMAT); + memcpy(changed, record, sizeof(record)); + changed[100] ^= 1; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_ECHECKSUM); + memcpy(changed, record, sizeof(record)); + changed[40] ^= 1; + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(record)), LECORE_ECHECKSUM); + + /* A newer minor may extend a sane header when no unknown mandatory flag is set. */ + memcpy(changed, record, LECORE_FORMAT_HEADER_BYTES); + changed[96] = 0; + memcpy(changed + 97, golden_payload, sizeof(golden_payload)); + put_u16_le(changed + 10, 1); + put_u16_le(changed + 12, 97); + set_record_crc(changed, sizeof(changed)); + CHECK_STATUS(lecore_format_check_v1(changed, sizeof(changed)), LECORE_OK); + return EXIT_SUCCESS; +} + +static int test_compatibility(void) +{ + lecore_format_descriptor_v1 expected; + lecore_format_descriptor_v1 actual; + + make_descriptor(&expected); + actual = expected; + actual.format_minor = 1; + actual.header_bytes = 97; + actual.content_crc64 = UINT64_C(123); + CHECK_STATUS(lecore_format_check_compatibility_v1(&expected, &actual), LECORE_OK); + + actual = expected; + actual.semantic_profile = LECORE_PROFILE_HRR_F32_V1; + actual.scalar_type = LECORE_SCALAR_F32; + actual.payload_bytes = 8; + CHECK_STATUS(lecore_format_check_compatibility_v1(&expected, &actual), LECORE_EPROFILE); + actual = expected; + actual.dimension = 1; + actual.payload_bytes = 8; + CHECK_STATUS(lecore_format_check_compatibility_v1(&expected, &actual), LECORE_EDIM); + actual = expected; + actual.application_contract[0] = 1; + CHECK_STATUS(lecore_format_check_compatibility_v1(&expected, &actual), LECORE_EFORMAT); + actual = expected; + actual.seed_namespace = 1; + CHECK_STATUS(lecore_format_check_compatibility_v1(&expected, &actual), LECORE_EFORMAT); + return EXIT_SUCCESS; +} + +int main(void) +{ + CHECK(test_golden_and_roundtrip() == EXIT_SUCCESS); + CHECK(test_descriptor_errors() == EXIT_SUCCESS); + CHECK(test_record_errors_and_forward_minor() == EXIT_SUCCESS); + CHECK(test_compatibility() == EXIT_SUCCESS); + return EXIT_SUCCESS; +} diff --git a/native/liblecore/tests/c/test_numeric.c b/native/liblecore/tests/c/test_numeric.c new file mode 100644 index 0000000..2c838ac --- /dev/null +++ b/native/liblecore/tests/c/test_numeric.c @@ -0,0 +1,665 @@ +#include "test_common.h" + +#include + +static int check_vector_f64( + const double *actual, + const double *expected, + size_t count, + double tolerance) +{ + size_t index; + for (index = 0; index < count; ++index) { + if (!lecore_test_close_f64(actual[index], expected[index], tolerance)) { + fprintf(stderr, "f64[%zu]: %.17g != %.17g\n", + index, actual[index], expected[index]); + return 0; + } + } + return 1; +} + +static int check_vector_f32( + const float *actual, + const float *expected, + size_t count, + float tolerance) +{ + size_t index; + for (index = 0; index < count; ++index) { + if (!lecore_test_close_f32(actual[index], expected[index], tolerance)) { + fprintf(stderr, "f32[%zu]: %.9g != %.9g\n", + index, (double)actual[index], (double)expected[index]); + return 0; + } + } + return 1; +} + +static void reference_bind_f64( + const double *a, + const double *b, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + uint32_t left_index; + + for (left_index = 0; left_index < dimension; ++left_index) { + const uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + const volatile double product = + a[left_index] * b[right_index]; + sum += product; + } + output[output_index] = sum; + } +} + +static void reference_unbind_f64( + const double *composite, + const double *key, + double *output, + uint32_t dimension) +{ + uint32_t output_index; + + for (output_index = 0; output_index < dimension; ++output_index) { + double sum = 0.0; + uint32_t composite_index; + + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + const volatile double product = + composite[composite_index] * key[key_index]; + sum += product; + } + output[output_index] = sum; + } +} + +static void reference_bind_f32( + const float *a, + const float *b, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + uint32_t left_index; + + for (left_index = 0; left_index < dimension; ++left_index) { + const uint32_t right_index = output_index >= left_index + ? output_index - left_index + : dimension - (left_index - output_index); + const volatile float product = a[left_index] * b[right_index]; + sum += product; + } + output[output_index] = sum; + } +} + +static void reference_unbind_f32( + const float *composite, + const float *key, + float *output, + uint32_t dimension) +{ + uint32_t output_index; + + for (output_index = 0; output_index < dimension; ++output_index) { + float sum = 0.0f; + uint32_t composite_index; + + for (composite_index = 0; + composite_index < dimension; + ++composite_index) { + const uint32_t key_index = composite_index >= output_index + ? composite_index - output_index + : dimension - (output_index - composite_index); + const volatile float product = + composite[composite_index] * key[key_index]; + sum += product; + } + output[output_index] = sum; + } +} + +static int test_direct_outer_product_order(void) +{ + enum { dimension = 32 }; + lecore_context *f64_context = lecore_test_context( + dimension, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + lecore_context *f32_context = lecore_test_context( + dimension, LECORE_PROFILE_HRR_F32_V1, LECORE_VALIDATION_SHAPE); + double a_f64[dimension]; + double b_f64[dimension]; + double expected_f64[dimension]; + double actual_f64[dimension]; + float a_f32[dimension]; + float b_f32[dimension]; + float expected_f32[dimension]; + float actual_f32[dimension]; + uint32_t index; + + CHECK(f64_context != NULL && f32_context != NULL); + for (index = 0; index < dimension; ++index) { + const int a_integer = (int)((index * 7U + 3U) % 23U) - 11; + const int b_integer = (int)((index * 11U + 5U) % 29U) - 14; + + a_f64[index] = (double)a_integer / 13.0; + b_f64[index] = (double)b_integer / 17.0; + a_f32[index] = (float)a_f64[index]; + b_f32[index] = (float)b_f64[index]; + } + + reference_bind_f64(a_f64, b_f64, expected_f64, dimension); + CHECK_STATUS(lecore_hrr_bind_f64( + f64_context, a_f64, b_f64, actual_f64), LECORE_OK); + CHECK(memcmp(actual_f64, expected_f64, sizeof(actual_f64)) == 0); + memcpy(actual_f64, a_f64, sizeof(actual_f64)); + CHECK_STATUS(lecore_hrr_bind_f64( + f64_context, actual_f64, b_f64, actual_f64), LECORE_OK); + CHECK(memcmp(actual_f64, expected_f64, sizeof(actual_f64)) == 0); + + reference_unbind_f64(a_f64, b_f64, expected_f64, dimension); + CHECK_STATUS(lecore_hrr_unbind_f64( + f64_context, a_f64, b_f64, actual_f64), LECORE_OK); + CHECK(memcmp(actual_f64, expected_f64, sizeof(actual_f64)) == 0); + + reference_bind_f32(a_f32, b_f32, expected_f32, dimension); + CHECK_STATUS(lecore_hrr_bind_f32( + f32_context, a_f32, b_f32, actual_f32), LECORE_OK); + CHECK(memcmp(actual_f32, expected_f32, sizeof(actual_f32)) == 0); + reference_unbind_f32(a_f32, b_f32, expected_f32, dimension); + CHECK_STATUS(lecore_hrr_unbind_f32( + f32_context, a_f32, b_f32, actual_f32), LECORE_OK); + CHECK(memcmp(actual_f32, expected_f32, sizeof(actual_f32)) == 0); + + lecore_context_destroy(f32_context); + lecore_context_destroy(f64_context); + return EXIT_SUCCESS; +} + +static int test_f64_dense(void) +{ + lecore_context *context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + const double vector[] = {3.0, 4.0, 0.0, 0.0}; + const double expected_normalized[] = {0.6, 0.8, 0.0, 0.0}; + const double zero[] = {0.0, 0.0, 0.0, 0.0}; + const double cancellation[] = {1e16, 1.0, -1e16, 0.0}; + const double ones[] = {1.0, 1.0, 1.0, 1.0}; + const double rows[] = { + 1.0, 0.0, 0.0, 0.0, 99.0, + 0.0, 2.0, 0.0, 0.0, 99.0, + 1.0, 0.0, 0.0, 0.0, 99.0 + }; + const double expected_dot_many[] = {3.0, 8.0, 3.0}; + const double expected_cosine_many[] = {0.6, 0.8, 0.6}; + const double bundle_rows[] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0 + }; + const double cancel_rows[] = { + 1.0, -2.0, 3.0, -4.0, + -1.0, 2.0, -3.0, 4.0 + }; + const double expected_bundle[] = { + 0.7071067811865475244, 0.7071067811865475244, 0.0, 0.0 + }; + double output[4]; + double scores[3]; + double scalar = -1.0; + size_t cleanup_index = SIZE_MAX; + + CHECK(context != NULL); + CHECK_STATUS(lecore_normalize_f64(context, vector, output), LECORE_OK); + CHECK(check_vector_f64(output, expected_normalized, 4, 1e-15)); + memcpy(output, vector, sizeof(output)); + CHECK_STATUS(lecore_normalize_f64(context, output, output), LECORE_OK); + CHECK(check_vector_f64(output, expected_normalized, 4, 1e-15)); + CHECK_STATUS(lecore_normalize_f64(context, zero, output), LECORE_OK); + CHECK(memcmp(output, zero, sizeof(output)) == 0); + + CHECK_STATUS(lecore_dot_f64(context, cancellation, ones, &scalar), LECORE_OK); + CHECK(scalar == 0.0); + CHECK_STATUS(lecore_dot_many_f64(context, vector, rows, 3, 5, scores), LECORE_OK); + CHECK(check_vector_f64(scores, expected_dot_many, 3, 0.0)); + CHECK_STATUS(lecore_cosine_f64(context, vector, zero, &scalar), LECORE_OK); + CHECK(scalar == 0.0 && !signbit(scalar)); + CHECK_STATUS(lecore_cosine_many_f64(context, vector, rows, 3, 5, scores), LECORE_OK); + CHECK(check_vector_f64(scores, expected_cosine_many, 3, 1e-15)); + { + size_t row; + for (row = 0; row < 3; ++row) { + CHECK_STATUS(lecore_cosine_f64( + context, vector, rows + row * 5, &scalar), LECORE_OK); + CHECK(memcmp(&scores[row], &scalar, sizeof(scalar)) == 0); + } + } + + CHECK_STATUS(lecore_bundle_f64(context, bundle_rows, 2, 4, output), LECORE_OK); + CHECK(check_vector_f64(output, expected_bundle, 4, 1e-15)); + CHECK_STATUS(lecore_bundle_f64(context, cancel_rows, 2, 4, output), LECORE_OK); + CHECK(memcmp(output, zero, sizeof(output)) == 0); + + CHECK_STATUS(lecore_cleanup_f64( + context, rows, rows, 3, 5, &cleanup_index, &scalar), LECORE_OK); + CHECK(cleanup_index == 0); + CHECK(scalar == 1.0); + + lecore_context_destroy(context); + return EXIT_SUCCESS; +} + +static int test_f64_hrr_and_batches(void) +{ + lecore_context *context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + const double a[] = {1.0, 2.0, 0.0, -1.0}; + const double b[] = {2.0, 0.0, 1.0, 0.0}; + const double expected_bind[] = {2.0, 3.0, 1.0, 0.0}; + const double expected_involution[] = {1.0, -1.0, 0.0, 2.0}; + const double expected_unbind[] = {5.0, 6.0, 4.0, 3.0}; + const double expected_shift[] = {-1.0, 1.0, 2.0, 0.0}; + const double expected_negative_shift[] = {2.0, 0.0, -1.0, 1.0}; + const double a_rows[] = { + 1.0, 2.0, 0.0, -1.0, 91.0, + 1.0, 0.0, 0.0, 0.0, 92.0 + }; + const double b_rows[] = { + 2.0, 0.0, 1.0, 0.0, 81.0, + 0.0, 1.0, 0.0, 0.0, 82.0 + }; + double batch_output[12] = {0.0}; + double fixed_output[12] = {0.0}; + double unbind_output[12] = {0.0}; + double output[4]; + double expected[4]; + double alias[4]; + size_t row; + + CHECK(context != NULL); + CHECK_STATUS(lecore_hrr_bind_f64(context, a, b, output), LECORE_OK); + CHECK(check_vector_f64(output, expected_bind, 4, 0.0)); + memcpy(alias, a, sizeof(alias)); + CHECK_STATUS(lecore_hrr_bind_f64(context, alias, b, alias), LECORE_OK); + CHECK(check_vector_f64(alias, expected_bind, 4, 0.0)); + + CHECK_STATUS(lecore_involution_f64(context, a, output), LECORE_OK); + CHECK(memcmp(output, expected_involution, sizeof(output)) == 0); + memcpy(alias, a, sizeof(alias)); + CHECK_STATUS(lecore_involution_f64(context, alias, alias), LECORE_OK); + CHECK(memcmp(alias, expected_involution, sizeof(alias)) == 0); + + CHECK_STATUS(lecore_hrr_unbind_f64(context, expected_bind, b, output), LECORE_OK); + CHECK(check_vector_f64(output, expected_unbind, 4, 0.0)); + memcpy(alias, expected_bind, sizeof(alias)); + CHECK_STATUS(lecore_hrr_unbind_f64(context, alias, b, alias), LECORE_OK); + CHECK(check_vector_f64(alias, expected_unbind, 4, 0.0)); + + CHECK_STATUS(lecore_permute_f64(context, a, 1, output), LECORE_OK); + CHECK(memcmp(output, expected_shift, sizeof(output)) == 0); + memcpy(alias, a, sizeof(alias)); + CHECK_STATUS(lecore_permute_f64(context, alias, -1, alias), LECORE_OK); + CHECK(memcmp(alias, expected_negative_shift, sizeof(alias)) == 0); + + CHECK_STATUS(lecore_hrr_bind_batch_f64( + context, a_rows, 5, b_rows, 5, 2, batch_output, 6), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f64( + context, a_rows + row * 5, b_rows + row * 5, expected), LECORE_OK); + CHECK(check_vector_f64(batch_output + row * 6, expected, 4, 0.0)); + } + + CHECK_STATUS(lecore_hrr_bind_fixed_f64( + context, a, b_rows, 2, 5, fixed_output, 6), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f64(context, a, b_rows + row * 5, expected), LECORE_OK); + CHECK(check_vector_f64(fixed_output + row * 6, expected, 4, 0.0)); + } + + CHECK_STATUS(lecore_hrr_unbind_all_f64( + context, expected_bind, b_rows, 2, 5, unbind_output, 6), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_unbind_f64( + context, expected_bind, b_rows + row * 5, expected), LECORE_OK); + CHECK(check_vector_f64(unbind_output + row * 6, expected, 4, 0.0)); + } + + lecore_context_destroy(context); + return EXIT_SUCCESS; +} + +static int test_f32_and_mixed(void) +{ + lecore_context *context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F32_V1, LECORE_VALIDATION_SHAPE); + const float a[] = {1.0f, 2.0f, 0.0f, -1.0f}; + const float b[] = {2.0f, 0.0f, 1.0f, 0.0f}; + const float expected_bind[] = {2.0f, 3.0f, 1.0f, 0.0f}; + const float rows[] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f + }; + const double query[] = {1.0, 2.0, 0.0, 0.0}; + const double expected_mixed[] = { + 0.4472135954999579393, 0.8944271909999158786, 0.0 + }; + float output[4]; + float scores[3]; + float score = -1.0f; + double mixed_scores[3]; + size_t index = SIZE_MAX; + + CHECK(context != NULL); + CHECK(lecore_context_scalar_type(context) == LECORE_SCALAR_F32); + CHECK(lecore_context_scratch_bytes(context) == 4 * sizeof(float)); + CHECK_STATUS(lecore_hrr_bind_f32(context, a, b, output), LECORE_OK); + CHECK(check_vector_f32(output, expected_bind, 4, 0.0f)); + CHECK_STATUS(lecore_cosine_f32(context, a, a, &score), LECORE_OK); + CHECK(lecore_test_close_f32(score, 1.0f, 1e-6f)); + CHECK_STATUS(lecore_cosine_many_f32(context, a, rows, 3, 4, scores), LECORE_OK); + { + size_t row; + for (row = 0; row < 3; ++row) { + CHECK_STATUS(lecore_cosine_f32( + context, a, rows + row * 4, &score), LECORE_OK); + CHECK(memcmp(&scores[row], &score, sizeof(score)) == 0); + } + } + CHECK_STATUS(lecore_cleanup_f32(context, a, rows, 3, 4, &index, &score), LECORE_OK); + CHECK(index == 1); + CHECK(lecore_test_close_f32(score, 2.0f / sqrtf(6.0f), 1e-6f)); + CHECK_STATUS(lecore_cosine_many_f64_f32( + context, query, rows, 3, 4, mixed_scores), LECORE_OK); + CHECK(check_vector_f64(mixed_scores, expected_mixed, 3, 1e-15)); + + lecore_context_destroy(context); + return EXIT_SUCCESS; +} + +#if LECORE_ENABLE_RADIX2 +static lecore_context *test_radix2_context( + uint32_t dimension, + lecore_profile profile) +{ + lecore_config_v0 config; + lecore_context *context = NULL; + + lecore_config_init_v0(&config); + config.dimension = dimension; + config.profile = profile; + config.backend = LECORE_BACKEND_RADIX2; + if (lecore_context_create(&config, &context) != LECORE_OK) { + return NULL; + } + return context; +} + +static int test_forced_radix2(void) +{ + const double a_f64[] = {1.0, 2.0, 0.0, -1.0}; + const double b_f64[] = {2.0, 0.0, 1.0, 0.0}; + const double expected_bind_f64[] = {2.0, 3.0, 1.0, 0.0}; + const double expected_unbind_f64[] = {5.0, 6.0, 4.0, 3.0}; + const float a_f32[] = {1.0f, 2.0f, 0.0f, -1.0f}; + const float b_f32[] = {2.0f, 0.0f, 1.0f, 0.0f}; + const float expected_bind_f32[] = {2.0f, 3.0f, 1.0f, 0.0f}; + const float a_rows_f32[] = { + 1.0f, 2.0f, 0.0f, -1.0f, + 1.0f, 0.0f, 0.0f, 0.0f + }; + const float b_rows_f32[] = { + 2.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f + }; + const double a_rows[] = { + 1.0, 2.0, 0.0, -1.0, + 1.0, 0.0, 0.0, 0.0 + }; + const double b_rows[] = { + 2.0, 0.0, 1.0, 0.0, + 0.0, 1.0, 0.0, 0.0 + }; + lecore_config_v0 config; + lecore_context *context = NULL; + lecore_context *direct_context; + double output_f64[4]; + double alias_f64[4]; + double batch_output[8]; + double expected_row[4]; + float output_f32[4]; + float batch_output_f32[8]; + float expected_row_f32[4]; + size_t row; + + lecore_config_init_v0(&config); + config.dimension = 3; + config.backend = LECORE_BACKEND_RADIX2; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_EUNSUPPORTED); + CHECK(context == NULL); + + config.dimension = 4; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(lecore_context_backend(context) == LECORE_BACKEND_RADIX2); + CHECK(lecore_context_scratch_bytes(context) == 4 * 4 * sizeof(double)); + CHECK_STATUS(lecore_hrr_bind_f64(context, a_f64, b_f64, output_f64), LECORE_OK); + CHECK(check_vector_f64(output_f64, expected_bind_f64, 4, 1e-9)); + memcpy(alias_f64, a_f64, sizeof(alias_f64)); + CHECK_STATUS(lecore_hrr_bind_f64(context, alias_f64, b_f64, alias_f64), LECORE_OK); + CHECK(check_vector_f64(alias_f64, expected_bind_f64, 4, 1e-9)); + CHECK_STATUS(lecore_hrr_unbind_f64( + context, expected_bind_f64, b_f64, output_f64), LECORE_OK); + CHECK(check_vector_f64(output_f64, expected_unbind_f64, 4, 1e-9)); + memcpy(alias_f64, expected_bind_f64, sizeof(alias_f64)); + CHECK_STATUS(lecore_hrr_unbind_f64(context, alias_f64, b_f64, alias_f64), LECORE_OK); + CHECK(check_vector_f64(alias_f64, expected_unbind_f64, 4, 1e-9)); + + direct_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + CHECK(direct_context != NULL); + CHECK(lecore_context_backend(direct_context) == LECORE_BACKEND_DIRECT); + CHECK_STATUS(lecore_hrr_bind_batch_f64( + context, a_rows, 4, b_rows, 4, 2, batch_output, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f64( + direct_context, a_rows + row * 4, b_rows + row * 4, expected_row), LECORE_OK); + CHECK(check_vector_f64(batch_output + row * 4, expected_row, 4, 1e-9)); + } + CHECK_STATUS(lecore_hrr_bind_fixed_f64( + context, a_f64, b_rows, 2, 4, batch_output, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f64( + context, a_f64, b_rows + row * 4, expected_row), LECORE_OK); + CHECK(memcmp( + batch_output + row * 4, expected_row, sizeof(expected_row)) == 0); + CHECK_STATUS(lecore_hrr_bind_f64( + direct_context, a_f64, b_rows + row * 4, expected_row), LECORE_OK); + CHECK(check_vector_f64(batch_output + row * 4, expected_row, 4, 1e-9)); + } + CHECK_STATUS(lecore_hrr_unbind_all_f64( + context, expected_bind_f64, b_rows, 2, 4, batch_output, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_unbind_f64( + context, expected_bind_f64, b_rows + row * 4, expected_row), LECORE_OK); + CHECK(memcmp( + batch_output + row * 4, expected_row, sizeof(expected_row)) == 0); + CHECK_STATUS(lecore_hrr_unbind_f64( + direct_context, expected_bind_f64, b_rows + row * 4, expected_row), LECORE_OK); + CHECK(check_vector_f64(batch_output + row * 4, expected_row, 4, 1e-9)); + } + lecore_context_destroy(direct_context); + lecore_context_destroy(context); + context = NULL; + + context = test_radix2_context(1, LECORE_PROFILE_HRR_F64_V1); + CHECK(context != NULL); + { + const double left[] = {2.0}; + const double right[] = {3.0}; + double scalar_output[1]; + CHECK_STATUS(lecore_hrr_bind_f64(context, left, right, scalar_output), LECORE_OK); + CHECK(lecore_test_close_f64(scalar_output[0], 6.0, 1e-12)); + CHECK_STATUS(lecore_hrr_unbind_f64( + context, scalar_output, right, scalar_output), LECORE_OK); + CHECK(lecore_test_close_f64(scalar_output[0], 18.0, 1e-12)); + } + lecore_context_destroy(context); + context = NULL; + + lecore_config_init_v0(&config); + config.dimension = 4; + config.profile = LECORE_PROFILE_HRR_F32_V1; + config.backend = LECORE_BACKEND_RADIX2; + CHECK_STATUS(lecore_context_create(&config, &context), LECORE_OK); + CHECK(lecore_context_scratch_bytes(context) == 4 * 4 * sizeof(float)); + CHECK_STATUS(lecore_hrr_bind_f32(context, a_f32, b_f32, output_f32), LECORE_OK); + CHECK(check_vector_f32(output_f32, expected_bind_f32, 4, 1e-5f)); + direct_context = lecore_test_context( + 4, LECORE_PROFILE_HRR_F32_V1, LECORE_VALIDATION_SHAPE); + CHECK(direct_context != NULL); + CHECK_STATUS(lecore_hrr_bind_batch_f32( + context, a_rows_f32, 4, b_rows_f32, 4, 2, batch_output_f32, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f32( + direct_context, + a_rows_f32 + row * 4, + b_rows_f32 + row * 4, + expected_row_f32), LECORE_OK); + CHECK(check_vector_f32( + batch_output_f32 + row * 4, expected_row_f32, 4, 1e-5f)); + } + CHECK_STATUS(lecore_hrr_bind_fixed_f32( + context, a_f32, b_rows_f32, 2, 4, batch_output_f32, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_bind_f32( + context, a_f32, b_rows_f32 + row * 4, expected_row_f32), LECORE_OK); + CHECK(memcmp( + batch_output_f32 + row * 4, + expected_row_f32, + sizeof(expected_row_f32)) == 0); + CHECK_STATUS(lecore_hrr_bind_f32( + direct_context, a_f32, b_rows_f32 + row * 4, expected_row_f32), LECORE_OK); + CHECK(check_vector_f32( + batch_output_f32 + row * 4, expected_row_f32, 4, 1e-5f)); + } + CHECK_STATUS(lecore_hrr_unbind_all_f32( + context, expected_bind_f32, b_rows_f32, 2, 4, batch_output_f32, 4), LECORE_OK); + for (row = 0; row < 2; ++row) { + CHECK_STATUS(lecore_hrr_unbind_f32( + context, + expected_bind_f32, + b_rows_f32 + row * 4, + expected_row_f32), LECORE_OK); + CHECK(memcmp( + batch_output_f32 + row * 4, + expected_row_f32, + sizeof(expected_row_f32)) == 0); + CHECK_STATUS(lecore_hrr_unbind_f32( + direct_context, + expected_bind_f32, + b_rows_f32 + row * 4, + expected_row_f32), LECORE_OK); + CHECK(check_vector_f32( + batch_output_f32 + row * 4, expected_row_f32, 4, 1e-5f)); + } + lecore_context_destroy(direct_context); + lecore_context_destroy(context); + return EXIT_SUCCESS; +} + +static int test_radix2_matches_direct_across_dimensions(void) +{ + static const uint32_t dimensions[] = {2, 4, 8, 16}; + double a_f64[16]; + double b_f64[16]; + double direct_f64[16]; + double radix_f64[16]; + float a_f32[16]; + float b_f32[16]; + float direct_f32[16]; + float radix_f32[16]; + size_t case_index; + + for (case_index = 0; + case_index < sizeof(dimensions) / sizeof(dimensions[0]); + ++case_index) { + const uint32_t dimension = dimensions[case_index]; + lecore_context *direct_context; + lecore_context *radix_context; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + const int a_integer = (int)((index * 7U + 3U) % 11U) - 5; + const int b_integer = (int)((index * 5U + 1U) % 13U) - 6; + a_f64[index] = (double)a_integer / 7.0; + b_f64[index] = (double)b_integer / 9.0; + a_f32[index] = (float)a_f64[index]; + b_f32[index] = (float)b_f64[index]; + } + + direct_context = lecore_test_context( + dimension, LECORE_PROFILE_HRR_F64_V1, LECORE_VALIDATION_SHAPE); + radix_context = test_radix2_context( + dimension, LECORE_PROFILE_HRR_F64_V1); + CHECK(direct_context != NULL && radix_context != NULL); + CHECK_STATUS(lecore_hrr_bind_f64( + direct_context, a_f64, b_f64, direct_f64), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_f64( + radix_context, a_f64, b_f64, radix_f64), LECORE_OK); + CHECK(check_vector_f64(radix_f64, direct_f64, dimension, 1e-9)); + CHECK_STATUS(lecore_hrr_unbind_f64( + direct_context, direct_f64, b_f64, direct_f64), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_f64( + radix_context, radix_f64, b_f64, radix_f64), LECORE_OK); + CHECK(check_vector_f64(radix_f64, direct_f64, dimension, 1e-9)); + lecore_context_destroy(radix_context); + lecore_context_destroy(direct_context); + + direct_context = lecore_test_context( + dimension, LECORE_PROFILE_HRR_F32_V1, LECORE_VALIDATION_SHAPE); + radix_context = test_radix2_context( + dimension, LECORE_PROFILE_HRR_F32_V1); + CHECK(direct_context != NULL && radix_context != NULL); + CHECK_STATUS(lecore_hrr_bind_f32( + direct_context, a_f32, b_f32, direct_f32), LECORE_OK); + CHECK_STATUS(lecore_hrr_bind_f32( + radix_context, a_f32, b_f32, radix_f32), LECORE_OK); + CHECK(check_vector_f32(radix_f32, direct_f32, dimension, 1e-5f)); + CHECK_STATUS(lecore_hrr_unbind_f32( + direct_context, direct_f32, b_f32, direct_f32), LECORE_OK); + CHECK_STATUS(lecore_hrr_unbind_f32( + radix_context, radix_f32, b_f32, radix_f32), LECORE_OK); + CHECK(check_vector_f32(radix_f32, direct_f32, dimension, 1e-5f)); + lecore_context_destroy(radix_context); + lecore_context_destroy(direct_context); + } + return EXIT_SUCCESS; +} +#endif + +int main(void) +{ + CHECK(test_direct_outer_product_order() == EXIT_SUCCESS); + CHECK(test_f64_dense() == EXIT_SUCCESS); + CHECK(test_f64_hrr_and_batches() == EXIT_SUCCESS); + CHECK(test_f32_and_mixed() == EXIT_SUCCESS); +#if LECORE_ENABLE_RADIX2 + CHECK(test_forced_radix2() == EXIT_SUCCESS); + CHECK(test_radix2_matches_direct_across_dimensions() == EXIT_SUCCESS); +#endif + return EXIT_SUCCESS; +} diff --git a/native/liblecore/tests/consumers/cmake-consumer/CMakeLists.txt b/native/liblecore/tests/consumers/cmake-consumer/CMakeLists.txt new file mode 100644 index 0000000..364b16b --- /dev/null +++ b/native/liblecore/tests/consumers/cmake-consumer/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.21) +project(liblecore_cmake_consumer LANGUAGES C CXX) + +find_package(lecore CONFIG REQUIRED) + +add_executable(liblecore_consumer main.cpp) +target_compile_features(liblecore_consumer PRIVATE cxx_std_17) +target_link_libraries(liblecore_consumer PRIVATE lecore::lecore) diff --git a/native/liblecore/tests/consumers/cmake-consumer/main.cpp b/native/liblecore/tests/consumers/cmake-consumer/main.cpp new file mode 100644 index 0000000..a3fd387 --- /dev/null +++ b/native/liblecore/tests/consumers/cmake-consumer/main.cpp @@ -0,0 +1,42 @@ +#include + +#if LECORE_ENABLE_FORMAT +#include +#endif + +#include + +int main() +{ + lecore_config_v0 config{}; + lecore_config_init_v0(&config); + if (lecore_abi_version() != UINT32_C(0) || + lecore_isa_version() != UINT32_C(1) || + config.abi_version != UINT32_C(0)) { + return 1; + } +#if LECORE_ENABLE_FORMAT + if ((lecore_capabilities() & LECORE_CAP_FORMAT) == 0) { + return 2; + } + lecore_format_descriptor_v1 descriptor{}; + lecore_format_descriptor_init_v1(&descriptor); + if (descriptor.format_major != LECORE_FORMAT_MAJOR) { + return 3; + } +#else + if ((lecore_capabilities() & LECORE_CAP_FORMAT) != 0) { + return 4; + } +#endif +#if LECORE_ENABLE_RADIX2 + if ((lecore_capabilities() & LECORE_CAP_RADIX2) == 0) { + return 5; + } +#else + if ((lecore_capabilities() & LECORE_CAP_RADIX2) != 0) { + return 6; + } +#endif + return 0; +} diff --git a/native/liblecore/tests/consumers/pkgconfig-consumer/CMakeLists.txt b/native/liblecore/tests/consumers/pkgconfig-consumer/CMakeLists.txt new file mode 100644 index 0000000..f3d172e --- /dev/null +++ b/native/liblecore/tests/consumers/pkgconfig-consumer/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.21) +project(liblecore_pkgconfig_consumer LANGUAGES C) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(liblecore REQUIRED IMPORTED_TARGET liblecore) + +add_executable(liblecore_consumer main.c) +target_compile_features(liblecore_consumer PRIVATE c_std_11) +target_link_libraries(liblecore_consumer PRIVATE PkgConfig::liblecore) diff --git a/native/liblecore/tests/consumers/pkgconfig-consumer/main.c b/native/liblecore/tests/consumers/pkgconfig-consumer/main.c new file mode 100644 index 0000000..d94c0c3 --- /dev/null +++ b/native/liblecore/tests/consumers/pkgconfig-consumer/main.c @@ -0,0 +1,28 @@ +#include + +#include + +int main(void) +{ + lecore_config_v0 config; + lecore_config_init_v0(&config); + if (!(lecore_abi_version() == UINT32_C(0) && + lecore_isa_version() == UINT32_C(1) && + config.abi_version == UINT32_C(0))) { + return 1; + } +#if LECORE_ENABLE_FORMAT + if ((lecore_capabilities() & LECORE_CAP_FORMAT) == 0) { + return 2; + } +#else + if ((lecore_capabilities() & LECORE_CAP_FORMAT) != 0) { + return 3; + } +#endif +#if LECORE_ENABLE_RADIX2 + return (lecore_capabilities() & LECORE_CAP_RADIX2) != 0 ? 0 : 4; +#else + return (lecore_capabilities() & LECORE_CAP_RADIX2) == 0 ? 0 : 5; +#endif +} diff --git a/native/liblecore/tests/consumers/run_examples_only.cmake b/native/liblecore/tests/consumers/run_examples_only.cmake new file mode 100644 index 0000000..4dd6406 --- /dev/null +++ b/native/liblecore/tests/consumers/run_examples_only.cmake @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.21) + +foreach(required_var IN ITEMS LECORE_SOURCE_DIR LECORE_TEST_ROOT) + if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "") + message(FATAL_ERROR "${required_var} is required") + endif() +endforeach() + +file(REMOVE_RECURSE "${LECORE_TEST_ROOT}") +file(MAKE_DIRECTORY "${LECORE_TEST_ROOT}") + +set(generator_args) +if(DEFINED LECORE_TEST_GENERATOR AND NOT LECORE_TEST_GENERATOR STREQUAL "") + list(APPEND generator_args -G "${LECORE_TEST_GENERATOR}") +endif() +if(DEFINED LECORE_TEST_GENERATOR_PLATFORM AND NOT LECORE_TEST_GENERATOR_PLATFORM STREQUAL "") + list(APPEND generator_args -A "${LECORE_TEST_GENERATOR_PLATFORM}") +endif() +if(DEFINED LECORE_TEST_GENERATOR_TOOLSET AND NOT LECORE_TEST_GENERATOR_TOOLSET STREQUAL "") + list(APPEND generator_args -T "${LECORE_TEST_GENERATOR_TOOLSET}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" + -S "${LECORE_SOURCE_DIR}" + -B "${LECORE_TEST_ROOT}" + ${generator_args} + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=ON + -DCMAKE_BUILD_TYPE=Release + RESULT_VARIABLE configure_result +) +if(NOT configure_result EQUAL 0) + message(FATAL_ERROR "Configuring the examples-only build failed: ${configure_result}") +endif() +execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${LECORE_TEST_ROOT}" --config Release + RESULT_VARIABLE build_result +) +if(NOT build_result EQUAL 0) + message(FATAL_ERROR "Building the examples-only variant failed: ${build_result}") +endif() + +foreach(example_name IN ITEMS liblecore_hrr_example liblecore_hrr_cpp_example) + set(example_path + "${LECORE_TEST_ROOT}/examples/${example_name}${CMAKE_EXECUTABLE_SUFFIX}") + set(multiconfig_path + "${LECORE_TEST_ROOT}/examples/Release/${example_name}${CMAKE_EXECUTABLE_SUFFIX}") + if(EXISTS "${multiconfig_path}") + set(example_path "${multiconfig_path}") + endif() + execute_process(COMMAND "${example_path}" RESULT_VARIABLE run_result) + if(NOT run_result EQUAL 0) + message(FATAL_ERROR "${example_name} failed at runtime: ${run_result}") + endif() +endforeach() diff --git a/native/liblecore/tests/consumers/run_format_off_variant.cmake b/native/liblecore/tests/consumers/run_format_off_variant.cmake new file mode 100644 index 0000000..62f20bd --- /dev/null +++ b/native/liblecore/tests/consumers/run_format_off_variant.cmake @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.21) + +foreach(required_var IN ITEMS LECORE_SOURCE_DIR LECORE_TEST_ROOT LECORE_CONSUMER_SOURCE) + if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "") + message(FATAL_ERROR "${required_var} is required") + endif() +endforeach() + +set(variant_build_dir "${LECORE_TEST_ROOT}/liblecore-build") +set(stage_dir "${LECORE_TEST_ROOT}/stage") +set(consumer_build_dir "${LECORE_TEST_ROOT}/consumer-build") +file(REMOVE_RECURSE "${LECORE_TEST_ROOT}") +file(MAKE_DIRECTORY "${LECORE_TEST_ROOT}") + +if(NOT DEFINED LECORE_VARIANT_FORMAT) + set(LECORE_VARIANT_FORMAT OFF) +endif() +if(NOT DEFINED LECORE_VARIANT_RADIX2) + set(LECORE_VARIANT_RADIX2 ON) +endif() + +set(generator_args) +if(DEFINED LECORE_TEST_GENERATOR AND NOT LECORE_TEST_GENERATOR STREQUAL "") + list(APPEND generator_args -G "${LECORE_TEST_GENERATOR}") +endif() +if(DEFINED LECORE_TEST_GENERATOR_PLATFORM AND NOT LECORE_TEST_GENERATOR_PLATFORM STREQUAL "") + list(APPEND generator_args -A "${LECORE_TEST_GENERATOR_PLATFORM}") +endif() +if(DEFINED LECORE_TEST_GENERATOR_TOOLSET AND NOT LECORE_TEST_GENERATOR_TOOLSET STREQUAL "") + list(APPEND generator_args -T "${LECORE_TEST_GENERATOR_TOOLSET}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" + -S "${LECORE_SOURCE_DIR}" + -B "${variant_build_dir}" + ${generator_args} + "-DLECORE_ENABLE_FORMAT=${LECORE_VARIANT_FORMAT}" + "-DLECORE_ENABLE_RADIX2=${LECORE_VARIANT_RADIX2}" + -DLECORE_BUILD_SHARED=OFF + -DLECORE_BUILD_TESTS=OFF + -DLECORE_BUILD_EXAMPLES=OFF + -DCMAKE_BUILD_TYPE=Release + RESULT_VARIABLE configure_result +) +if(NOT configure_result EQUAL 0) + message(FATAL_ERROR "Configuring the format-off build failed: ${configure_result}") +endif() + +set(config_args --config Release) +execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${variant_build_dir}" ${config_args} + RESULT_VARIABLE build_result +) +if(NOT build_result EQUAL 0) + message(FATAL_ERROR "Building the format-off variant failed: ${build_result}") +endif() +execute_process( + COMMAND "${CMAKE_COMMAND}" --install "${variant_build_dir}" --prefix "${stage_dir}" ${config_args} + RESULT_VARIABLE install_result +) +if(NOT install_result EQUAL 0) + message(FATAL_ERROR "Installing the format-off variant failed: ${install_result}") +endif() +if(NOT LECORE_VARIANT_FORMAT AND EXISTS "${stage_dir}/include/lecore/lecore_format.h") + message(FATAL_ERROR "A format-off installation unexpectedly contains lecore_format.h") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" + -S "${LECORE_CONSUMER_SOURCE}" + -B "${consumer_build_dir}" + ${generator_args} + "-DCMAKE_PREFIX_PATH=${stage_dir}" + -DCMAKE_BUILD_TYPE=Release + RESULT_VARIABLE consumer_configure_result +) +if(NOT consumer_configure_result EQUAL 0) + message(FATAL_ERROR "Configuring a format-off consumer failed: ${consumer_configure_result}") +endif() +execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${consumer_build_dir}" ${config_args} + RESULT_VARIABLE consumer_build_result +) +if(NOT consumer_build_result EQUAL 0) + message(FATAL_ERROR "Building a format-off consumer failed: ${consumer_build_result}") +endif() + +set(consumer_executable "${consumer_build_dir}/liblecore_consumer${CMAKE_EXECUTABLE_SUFFIX}") +set(multiconfig_executable + "${consumer_build_dir}/Release/liblecore_consumer${CMAKE_EXECUTABLE_SUFFIX}") +if(EXISTS "${multiconfig_executable}") + set(consumer_executable "${multiconfig_executable}") +endif() +execute_process(COMMAND "${consumer_executable}" RESULT_VARIABLE run_result) +if(NOT run_result EQUAL 0) + message(FATAL_ERROR "The format-off consumer failed at runtime: ${run_result}") +endif() diff --git a/native/liblecore/tests/consumers/run_installed_consumer.cmake b/native/liblecore/tests/consumers/run_installed_consumer.cmake new file mode 100644 index 0000000..4f7d4be --- /dev/null +++ b/native/liblecore/tests/consumers/run_installed_consumer.cmake @@ -0,0 +1,112 @@ +cmake_minimum_required(VERSION 3.21) + +foreach(required_var IN ITEMS + LECORE_BUILD_DIR + LECORE_CONSUMER_SOURCE + LECORE_INSTALL_BINDIR + LECORE_INSTALL_LIBDIR + LECORE_TEST_ROOT) + if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "") + message(FATAL_ERROR "${required_var} is required") + endif() +endforeach() + +set(stage_dir "${LECORE_TEST_ROOT}/stage") +set(consumer_build_dir "${LECORE_TEST_ROOT}/build") +file(REMOVE_RECURSE "${LECORE_TEST_ROOT}") +file(MAKE_DIRECTORY "${LECORE_TEST_ROOT}") + +set(config_args) +if(DEFINED LECORE_TEST_CONFIG AND NOT LECORE_TEST_CONFIG STREQUAL "") + list(APPEND config_args --config "${LECORE_TEST_CONFIG}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" --install "${LECORE_BUILD_DIR}" --prefix "${stage_dir}" ${config_args} + RESULT_VARIABLE install_result +) +if(NOT install_result EQUAL 0) + message(FATAL_ERROR "Installing liblecore for the consumer test failed: ${install_result}") +endif() + +set(required_install_files + "${stage_dir}/include/lecore/lecore.h" + "${stage_dir}/${LECORE_INSTALL_LIBDIR}/cmake/lecore/lecoreConfig.cmake" + "${stage_dir}/${LECORE_INSTALL_LIBDIR}/cmake/lecore/lecoreTargets.cmake" + "${stage_dir}/${LECORE_INSTALL_LIBDIR}/pkgconfig/liblecore.pc" + "${stage_dir}/share/liblecore/VERSION" + "${stage_dir}/share/liblecore/ISA_VERSION" + "${stage_dir}/share/doc/liblecore/README.md" + "${stage_dir}/share/doc/liblecore/CHANGELOG.md" + "${stage_dir}/share/doc/liblecore/PROVENANCE.md" + "${stage_dir}/share/licenses/liblecore/LICENSE" +) +foreach(required_file IN LISTS required_install_files) + if(NOT EXISTS "${required_file}") + message(FATAL_ERROR "The installation is missing ${required_file}") + endif() +endforeach() + +if(DEFINED LECORE_EXPECT_FORMAT AND LECORE_EXPECT_FORMAT) + if(NOT EXISTS "${stage_dir}/include/lecore/lecore_format.h") + message(FATAL_ERROR "A format-enabled installation is missing lecore_format.h") + endif() +endif() + +set(configure_command + "${CMAKE_COMMAND}" + -S "${LECORE_CONSUMER_SOURCE}" + -B "${consumer_build_dir}" + "-DCMAKE_BUILD_TYPE=Release" + "-DCMAKE_PREFIX_PATH=${stage_dir}" +) + +if(DEFINED LECORE_USE_PKG_CONFIG AND LECORE_USE_PKG_CONFIG) + execute_process( + COMMAND "${CMAKE_COMMAND}" -E env + "PKG_CONFIG_PATH=${stage_dir}/${LECORE_INSTALL_LIBDIR}/pkgconfig" + ${configure_command} + RESULT_VARIABLE configure_result + ) +else() + execute_process(COMMAND ${configure_command} RESULT_VARIABLE configure_result) +endif() +if(NOT configure_result EQUAL 0) + message(FATAL_ERROR "Configuring the installed consumer failed: ${configure_result}") +endif() + +execute_process( + COMMAND "${CMAKE_COMMAND}" --build "${consumer_build_dir}" ${config_args} + RESULT_VARIABLE build_result +) +if(NOT build_result EQUAL 0) + message(FATAL_ERROR "Building the installed consumer failed: ${build_result}") +endif() + +set(consumer_executable "${consumer_build_dir}/liblecore_consumer${CMAKE_EXECUTABLE_SUFFIX}") +if(DEFINED LECORE_TEST_CONFIG AND NOT LECORE_TEST_CONFIG STREQUAL "") + set(multiconfig_executable + "${consumer_build_dir}/${LECORE_TEST_CONFIG}/liblecore_consumer${CMAKE_EXECUTABLE_SUFFIX}") + if(EXISTS "${multiconfig_executable}") + set(consumer_executable "${multiconfig_executable}") + endif() +endif() + +if(WIN32 AND LECORE_EXPECT_SHARED) + file(GLOB installed_runtime_dlls + "${stage_dir}/${LECORE_INSTALL_BINDIR}/*lecore*.dll") + list(LENGTH installed_runtime_dlls installed_runtime_count) + if(NOT installed_runtime_count EQUAL 1) + message(FATAL_ERROR + "Expected one installed liblecore runtime DLL, found ${installed_runtime_count}") + endif() + get_filename_component(consumer_executable_dir + "${consumer_executable}" DIRECTORY) + file(COPY "${installed_runtime_dlls}" DESTINATION + "${consumer_executable_dir}") +endif() + +execute_process(COMMAND "${consumer_executable}" RESULT_VARIABLE run_result) +if(NOT run_result EQUAL 0) + message(FATAL_ERROR "The installed consumer failed at runtime: ${run_result}") +endif() diff --git a/native/liblecore/tests/fuzz/CMakeLists.txt b/native/liblecore/tests/fuzz/CMakeLists.txt new file mode 100644 index 0000000..7bd320e --- /dev/null +++ b/native/liblecore/tests/fuzz/CMakeLists.txt @@ -0,0 +1,20 @@ +function(lecore_add_fuzzer target_name source_file) + add_executable("${target_name}" "${source_file}") + target_compile_features("${target_name}" PRIVATE c_std_11) + target_link_libraries("${target_name}" PRIVATE lecore::lecore) + target_compile_options("${target_name}" PRIVATE + -Wall -Wextra -Wpedantic -Wconversion -Wshadow -Werror + -fno-fast-math -ffp-contract=off + -fsanitize=fuzzer,address,undefined + -fno-omit-frame-pointer + ) + target_link_options("${target_name}" PRIVATE + -fsanitize=fuzzer,address,undefined + -fno-omit-frame-pointer + ) +endfunction() + +lecore_add_fuzzer(liblecore_fuzz_public_api fuzz_public_api.c) +if(LECORE_ENABLE_FORMAT) + lecore_add_fuzzer(liblecore_fuzz_format fuzz_format.c) +endif() diff --git a/native/liblecore/tests/fuzz/corpus/format/seed b/native/liblecore/tests/fuzz/corpus/format/seed new file mode 100644 index 0000000..3df3030 --- /dev/null +++ b/native/liblecore/tests/fuzz/corpus/format/seed @@ -0,0 +1 @@ +LECOREV-format-seed-v1 diff --git a/native/liblecore/tests/fuzz/corpus/public_api/seed b/native/liblecore/tests/fuzz/corpus/public_api/seed new file mode 100644 index 0000000..21b2f02 --- /dev/null +++ b/native/liblecore/tests/fuzz/corpus/public_api/seed @@ -0,0 +1 @@ +liblecore-public-api-seed-v1 diff --git a/native/liblecore/tests/fuzz/fuzz_format.c b/native/liblecore/tests/fuzz/fuzz_format.c new file mode 100644 index 0000000..8961ab7 --- /dev/null +++ b/native/liblecore/tests/fuzz/fuzz_format.c @@ -0,0 +1,79 @@ +#include + +#include +#include + +#define FUZZ_RECORD_CAPACITY ((size_t)512) +#define FUZZ_PAYLOAD_CAPACITY ((size_t)256) + +static uint8_t fuzz_byte(const uint8_t *data, size_t size, size_t index) +{ + return size == 0 ? UINT8_C(0) : data[index % size]; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + lecore_format_descriptor_v1 descriptor; + lecore_format_descriptor_v1 decoded; + uint8_t payload[FUZZ_PAYLOAD_CAPACITY]; + uint8_t record[FUZZ_RECORD_CAPACITY]; + const void *decoded_payload = NULL; + size_t decoded_payload_bytes = 0; + size_t record_bytes = 0; + size_t input_bytes = size < FUZZ_RECORD_CAPACITY + ? size + : FUZZ_RECORD_CAPACITY; + size_t index; + + (void)lecore_format_check_v1(data, size); + (void)lecore_format_decode_v1( + data, size, &decoded, &decoded_payload, &decoded_payload_bytes); + + lecore_format_descriptor_init_v1(&descriptor); + descriptor.artifact_kind = (lecore_artifact_kind)( + UINT32_C(1) + (uint32_t)(fuzz_byte(data, size, 0) % UINT8_C(3))); + descriptor.semantic_profile = (fuzz_byte(data, size, 1) & UINT8_C(1)) != 0 + ? LECORE_PROFILE_HRR_F32_V1 + : LECORE_PROFILE_HRR_F64_V1; + descriptor.scalar_type = descriptor.semantic_profile == + LECORE_PROFILE_HRR_F32_V1 + ? LECORE_SCALAR_F32 + : LECORE_SCALAR_F64; + descriptor.dimension = UINT32_C(1) + + (uint32_t)(fuzz_byte(data, size, 2) % UINT8_C(8)); + descriptor.vector_count = UINT64_C(1) + + (uint64_t)(fuzz_byte(data, size, 3) % UINT8_C(4)); + descriptor.payload_bytes = descriptor.vector_count * + (uint64_t)descriptor.dimension * + (descriptor.scalar_type == LECORE_SCALAR_F64 ? UINT64_C(8) : UINT64_C(4)); + + for (index = 0; index < FUZZ_PAYLOAD_CAPACITY; ++index) { + payload[index] = fuzz_byte(data, size, index + (size_t)4); + } + for (index = 0; index < input_bytes; ++index) { + record[index] = data[index]; + } + if (descriptor.payload_bytes <= FUZZ_PAYLOAD_CAPACITY) { + (void)lecore_format_encoded_size_v1(&descriptor, &record_bytes); + (void)lecore_format_encode_v1( + &descriptor, + payload, + (size_t)descriptor.payload_bytes, + record, + sizeof(record), + &record_bytes); + if (record_bytes <= sizeof(record)) { + (void)lecore_format_check_v1(record, record_bytes); + if (lecore_format_decode_v1( + record, + record_bytes, + &decoded, + &decoded_payload, + &decoded_payload_bytes) == LECORE_OK) { + (void)lecore_format_check_compatibility_v1( + &descriptor, &decoded); + } + } + } + return 0; +} diff --git a/native/liblecore/tests/fuzz/fuzz_public_api.c b/native/liblecore/tests/fuzz/fuzz_public_api.c new file mode 100644 index 0000000..de3a9b2 --- /dev/null +++ b/native/liblecore/tests/fuzz/fuzz_public_api.c @@ -0,0 +1,268 @@ +#include + +#include +#include +#include +#include + +#define FUZZ_MAX_DIMENSION ((size_t)64) +#define FUZZ_MAX_STRIDE ((size_t)72) +#define FUZZ_ROWS ((size_t)3) + +static uint8_t fuzz_byte(const uint8_t *data, size_t size, size_t index) +{ + return size == 0 ? UINT8_C(0) : data[index % size]; +} + +static double fuzz_f64(const uint8_t *data, size_t size, size_t index) +{ + uint8_t byte = fuzz_byte(data, size, index); + + if ((byte & UINT8_C(31)) == UINT8_C(31)) { + return NAN; + } + return ((double)(int)byte - 127.0) / 64.0; +} + +static float fuzz_f32(const uint8_t *data, size_t size, size_t index) +{ + uint8_t byte = fuzz_byte(data, size, index); + + if ((byte & UINT8_C(31)) == UINT8_C(30)) { + return INFINITY; + } + return ((float)(int)byte - 127.0F) / 64.0F; +} + +static void exercise_f64( + lecore_context *context, + const uint8_t *data, + size_t size, + size_t dimension) +{ + double a[FUZZ_MAX_DIMENSION + 1]; + double b[FUZZ_MAX_DIMENSION + 1]; + double output[FUZZ_MAX_DIMENSION + 1]; + double rows[FUZZ_ROWS * FUZZ_MAX_STRIDE]; + double out_rows[FUZZ_ROWS * FUZZ_MAX_STRIDE]; + double scores[FUZZ_ROWS]; + double scalar = 0.0; + size_t decision = 0; + size_t stride = dimension + + (size_t)(fuzz_byte(data, size, 7) % UINT8_C(9)); + size_t index; + + for (index = 0; index < FUZZ_MAX_DIMENSION + 1; ++index) { + a[index] = fuzz_f64(data, size, index); + b[index] = fuzz_f64(data, size, index + FUZZ_MAX_DIMENSION); + output[index] = 0.0; + } + for (index = 0; index < FUZZ_ROWS * FUZZ_MAX_STRIDE; ++index) { + rows[index] = fuzz_f64(data, size, index + 2 * FUZZ_MAX_DIMENSION); + out_rows[index] = 0.0; + } + + (void)lecore_validate_f64(a, dimension); + (void)lecore_normalize_f64(context, a, output); + (void)lecore_normalize_f64(context, a, a); + (void)lecore_dot_f64(context, a, b, &scalar); + (void)lecore_dot_many_f64(context, a, rows, FUZZ_ROWS, stride, scores); + (void)lecore_cosine_f64(context, a, b, &scalar); + (void)lecore_cosine_many_f64(context, a, rows, FUZZ_ROWS, stride, scores); + (void)lecore_hrr_bind_f64(context, a, b, output); + (void)lecore_hrr_bind_f64(context, a, b, a); + (void)lecore_hrr_unbind_f64(context, a, b, output); + (void)lecore_hrr_unbind_f64(context, a, b, b); + (void)lecore_involution_f64(context, a, output); + (void)lecore_involution_f64(context, a, a); + (void)lecore_permute_f64( + context, a, (int64_t)(int8_t)fuzz_byte(data, size, 11), output); + (void)lecore_permute_f64( + context, a, (int64_t)(int8_t)fuzz_byte(data, size, 12), a); + (void)lecore_bundle_f64(context, rows, FUZZ_ROWS, stride, output); + (void)lecore_cleanup_f64( + context, a, rows, FUZZ_ROWS, stride, &decision, &scalar); + (void)lecore_hrr_bind_batch_f64( + context, rows, stride, rows + FUZZ_MAX_STRIDE, stride, + (size_t)2, out_rows, FUZZ_MAX_STRIDE); + (void)lecore_hrr_bind_fixed_f64( + context, a, rows, FUZZ_ROWS, stride, out_rows, FUZZ_MAX_STRIDE); + (void)lecore_hrr_unbind_all_f64( + context, a, rows, FUZZ_ROWS, stride, out_rows, FUZZ_MAX_STRIDE); + + /* Invalid pointers, partial aliases, and size arithmetic must fail before + * dereference or writes. Buffers include one padding element so a broken + * partial-alias check remains visible to sanitizers without harness UB. */ + (void)lecore_normalize_f64(context, NULL, output); + (void)lecore_normalize_f64(context, a, a + 1); + (void)lecore_dot_many_f64( + context, a, rows, SIZE_MAX, dimension, scores); + (void)lecore_cosine_many_f64(context, a, rows, 0, stride, scores); + (void)lecore_bundle_f64(context, rows, FUZZ_ROWS, dimension - 1, output); + (void)lecore_hrr_bind_batch_f64( + context, rows, stride, rows, stride, FUZZ_ROWS, + rows + 1, stride); +} + +static void exercise_f32( + lecore_context *context, + const uint8_t *data, + size_t size, + size_t dimension) +{ + float a[FUZZ_MAX_DIMENSION + 1]; + float b[FUZZ_MAX_DIMENSION + 1]; + float output[FUZZ_MAX_DIMENSION + 1]; + float rows[FUZZ_ROWS * FUZZ_MAX_STRIDE]; + float out_rows[FUZZ_ROWS * FUZZ_MAX_STRIDE]; + double query[FUZZ_MAX_DIMENSION + 1]; + double mixed_scores[FUZZ_ROWS]; + float scores[FUZZ_ROWS]; + float scalar = 0.0F; + size_t decision = 0; + size_t stride = dimension + + (size_t)(fuzz_byte(data, size, 9) % UINT8_C(9)); + size_t index; + + for (index = 0; index < FUZZ_MAX_DIMENSION + 1; ++index) { + a[index] = fuzz_f32(data, size, index); + b[index] = fuzz_f32(data, size, index + FUZZ_MAX_DIMENSION); + output[index] = 0.0F; + query[index] = fuzz_f64(data, size, index + 3 * FUZZ_MAX_DIMENSION); + } + for (index = 0; index < FUZZ_ROWS * FUZZ_MAX_STRIDE; ++index) { + rows[index] = fuzz_f32(data, size, index + 2 * FUZZ_MAX_DIMENSION); + out_rows[index] = 0.0F; + } + + (void)lecore_validate_f32(a, dimension); + (void)lecore_normalize_f32(context, a, output); + (void)lecore_normalize_f32(context, a, a); + (void)lecore_dot_f32(context, a, b, &scalar); + (void)lecore_dot_many_f32(context, a, rows, FUZZ_ROWS, stride, scores); + (void)lecore_cosine_f32(context, a, b, &scalar); + (void)lecore_cosine_many_f32(context, a, rows, FUZZ_ROWS, stride, scores); + (void)lecore_hrr_bind_f32(context, a, b, output); + (void)lecore_hrr_bind_f32(context, a, b, a); + (void)lecore_hrr_unbind_f32(context, a, b, output); + (void)lecore_hrr_unbind_f32(context, a, b, b); + (void)lecore_involution_f32(context, a, output); + (void)lecore_involution_f32(context, a, a); + (void)lecore_permute_f32( + context, a, (int64_t)(int8_t)fuzz_byte(data, size, 13), output); + (void)lecore_permute_f32( + context, a, (int64_t)(int8_t)fuzz_byte(data, size, 14), a); + (void)lecore_bundle_f32(context, rows, FUZZ_ROWS, stride, output); + (void)lecore_cleanup_f32( + context, a, rows, FUZZ_ROWS, stride, &decision, &scalar); + (void)lecore_hrr_bind_batch_f32( + context, rows, stride, rows + FUZZ_MAX_STRIDE, stride, + (size_t)2, out_rows, FUZZ_MAX_STRIDE); + (void)lecore_hrr_bind_fixed_f32( + context, a, rows, FUZZ_ROWS, stride, out_rows, FUZZ_MAX_STRIDE); + (void)lecore_hrr_unbind_all_f32( + context, a, rows, FUZZ_ROWS, stride, out_rows, FUZZ_MAX_STRIDE); + (void)lecore_cosine_many_f64_f32( + context, query, rows, FUZZ_ROWS, stride, mixed_scores); + + (void)lecore_normalize_f32(context, NULL, output); + (void)lecore_normalize_f32(context, a, a + 1); + (void)lecore_dot_many_f32( + context, a, rows, SIZE_MAX, dimension, scores); + (void)lecore_cosine_many_f32(context, a, rows, 0, stride, scores); + (void)lecore_bundle_f32(context, rows, FUZZ_ROWS, dimension - 1, output); + (void)lecore_hrr_bind_batch_f32( + context, rows, stride, rows, stride, FUZZ_ROWS, + rows + 1, stride); + (void)lecore_cosine_many_f64_f32( + context, query, rows, SIZE_MAX, dimension, mixed_scores); +} + +static void exercise_invalid_configurations( + const uint8_t *data, + size_t size, + size_t dimension) +{ + lecore_config_v0 config; + lecore_context *context = NULL; + + lecore_config_init_v0(&config); + config.dimension = (uint32_t)dimension; + switch (fuzz_byte(data, size, 5) % UINT8_C(9)) { + case 0: + config.struct_size -= UINT32_C(1); + break; + case 1: + config.abi_version = UINT32_MAX; + break; + case 2: + config.profile = UINT32_MAX; + break; + case 3: + config.backend = UINT32_MAX; + break; + case 4: + config.validation = UINT32_MAX; + break; + case 5: + config.flags = UINT32_C(1); + break; + case 6: + config.dimension = UINT32_C(0); + break; + case 7: + config.allocator.struct_size = UINT32_C(0); + break; + default: + config.reserved[0] = UINT64_C(1); + break; + } + if (lecore_context_create(&config, &context) == LECORE_OK || + context != NULL) { + lecore_context_destroy(context); + abort(); + } +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + lecore_config_v0 config; + lecore_context *context = NULL; + size_t dimension; + + dimension = (size_t)(fuzz_byte(data, size, 0) % + (uint8_t)FUZZ_MAX_DIMENSION) + (size_t)1; + exercise_invalid_configurations(data, size, dimension); + lecore_config_init_v0(&config); + config.dimension = (uint32_t)dimension; + config.profile = (fuzz_byte(data, size, 1) & UINT8_C(1)) != 0 + ? LECORE_PROFILE_HRR_F32_V1 + : LECORE_PROFILE_HRR_F64_V1; + config.backend = (fuzz_byte(data, size, 2) & UINT8_C(1)) != 0 + ? LECORE_BACKEND_RADIX2 + : LECORE_BACKEND_DIRECT; + config.validation = (fuzz_byte(data, size, 3) & UINT8_C(1)) != 0 + ? LECORE_VALIDATION_FINITE + : LECORE_VALIDATION_SHAPE; + + if (lecore_context_create(&config, &context) == LECORE_OK) { + (void)lecore_abi_version(); + (void)lecore_isa_version(); + (void)lecore_version_string(); + (void)lecore_capabilities(); + (void)lecore_status_string((lecore_status)fuzz_byte(data, size, 4)); + (void)lecore_context_dimension(context); + (void)lecore_context_profile(context); + (void)lecore_context_backend(context); + (void)lecore_context_validation(context); + (void)lecore_context_scalar_type(context); + (void)lecore_context_scratch_bytes(context); + if (config.profile == LECORE_PROFILE_HRR_F64_V1) { + exercise_f64(context, data, size, dimension); + } else { + exercise_f32(context, data, size, dimension); + } + } + lecore_context_destroy(context); + return 0; +} diff --git a/tests/native/check_liblecore_amalgamation.py b/tests/native/check_liblecore_amalgamation.py new file mode 100644 index 0000000..1db6821 --- /dev/null +++ b/tests/native/check_liblecore_amalgamation.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +"""Check liblecore amalgamation drift and standalone compiler consumption.""" + +from __future__ import annotations + +import os +from pathlib import Path +import shlex +import subprocess +import sys +import tempfile + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +GENERATOR = REPOSITORY_ROOT / "tools/generate_liblecore_amalgamation.py" +AMALGAMATION = REPOSITORY_ROOT / "native/liblecore/amalgamation" +SYMBOL_CHECKER = REPOSITORY_ROOT / "tests/native/check_liblecore_symbols.py" + +C_SMOKE = r""" +#include "lecore.h" + +#include +#include +#include +#include + +static int close_enough(double left, double right) +{ + return fabs(left - right) <= 1e-9; +} + +int main(void) +{ + const double left[4] = {1.0, 2.0, 0.0, -1.0}; + const double right[4] = {2.0, 0.0, 1.0, 0.0}; + const double expected[4] = {2.0, 3.0, 1.0, 0.0}; + double output[4]; + lecore_config_v0 config; + lecore_context *context = NULL; + lecore_status status; + size_t index; + + if (lecore_abi_version() != 0 || lecore_isa_version() != @ISA_VERSION@ || + strcmp(lecore_version_string(), "@VERSION@") != 0) { + return 1; + } + + lecore_config_init_v0(&config); + config.dimension = 4; + config.backend = LECORE_BACKEND_RADIX2; + status = lecore_context_create(&config, &context); +#if LECORE_ENABLE_RADIX2 + if (status != LECORE_OK || context == NULL || + lecore_context_backend(context) != LECORE_BACKEND_RADIX2 || + (lecore_capabilities() & LECORE_CAP_RADIX2) == 0) { + return 2; + } +#else + if (status != LECORE_EUNSUPPORTED || context != NULL || + (lecore_capabilities() & LECORE_CAP_RADIX2) != 0) { + return 3; + } + config.backend = LECORE_BACKEND_DIRECT; + if (lecore_context_create(&config, &context) != LECORE_OK) { + return 4; + } +#endif + + if (lecore_hrr_bind_f64(context, left, right, output) != LECORE_OK) { + return 5; + } + for (index = 0; index < 4; ++index) { + if (!close_enough(output[index], expected[index])) { + return 6; + } + } + lecore_context_destroy(context); + +#if LECORE_ENABLE_FORMAT + { + lecore_format_descriptor_v1 descriptor; + lecore_format_descriptor_init_v1(&descriptor); + if ((lecore_capabilities() & LECORE_CAP_FORMAT) == 0 || + descriptor.struct_size != sizeof(descriptor)) { + return 7; + } + } +#else + if ((lecore_capabilities() & LECORE_CAP_FORMAT) != 0) { + return 8; + } +#endif + return 0; +} +""" + +CXX_SMOKE = r""" +#include "lecore.h" + +#include +#include + +static_assert(LECORE_ABI_VERSION == 0, "unexpected preview ABI"); + +int main() +{ + lecore_config_v0 config{}; + lecore_context *context = nullptr; + lecore_config_init_v0(&config); + config.dimension = 1; + if (lecore_context_create(&config, &context) != LECORE_OK) { + return 1; + } + const bool valid = lecore_context_dimension(context) == UINT32_C(1) && + std::strcmp(lecore_version_string(), "@VERSION@") == 0; + lecore_context_destroy(context); + return valid ? 0 : 2; +} +""" + + +def command_from_environment(name: str, fallback: str) -> list[str]: + return shlex.split(os.environ.get(name, fallback)) + + +def run(command: list[str]) -> None: + subprocess.run(command, cwd=REPOSITORY_ROOT, check=True) + + +def strict_c_flags() -> list[str]: + return [ + "-std=c11", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wconversion", + "-Wshadow", + "-Werror", + "-fno-fast-math", + "-ffp-contract=off", + ] + + +def main() -> int: + cc = command_from_environment("CC", "cc") + cxx = command_from_environment("CXX", "c++") + run([sys.executable, str(GENERATOR), "--check"]) + version = (REPOSITORY_ROOT / "native/liblecore/VERSION").read_text( + encoding="utf-8" + ).strip() + isa_version = (REPOSITORY_ROOT / "native/liblecore/ISA_VERSION").read_text( + encoding="utf-8" + ).strip() + + with tempfile.TemporaryDirectory(prefix="liblecore-amalgamation-") as directory: + temporary = Path(directory) + c_smoke = temporary / "smoke.c" + cxx_smoke = temporary / "smoke.cpp" + c_smoke.write_text( + C_SMOKE.replace("@VERSION@", version).replace( + "@ISA_VERSION@", isa_version + ), + encoding="utf-8", + ) + cxx_smoke.write_text( + CXX_SMOKE.replace("@VERSION@", version), encoding="utf-8" + ) + + for label, format_enabled, radix2_enabled in ( + ("features-on", 1, 1), + ("features-off", 0, 0), + ): + defines = [ + f"-DLECORE_ENABLE_FORMAT={format_enabled}", + f"-DLECORE_ENABLE_RADIX2={radix2_enabled}", + ] + implementation = temporary / f"{label}.o" + executable = temporary / label + run( + cc + + strict_c_flags() + + defines + + [ + "-I", + str(AMALGAMATION), + "-c", + str(AMALGAMATION / "lecore.c"), + "-o", + str(implementation), + ] + ) + run( + [ + sys.executable, + str(SYMBOL_CHECKER), + "--library", + str(implementation), + "--format", + "on" if format_enabled else "off", + ] + ) + run( + cc + + strict_c_flags() + + defines + + [ + "-I", + str(AMALGAMATION), + str(c_smoke), + str(implementation), + "-lm", + "-o", + str(executable), + ] + ) + run([str(executable)]) + + for label, format_enabled, radix2_enabled in ( + ("features-on", 1, 1), + ("features-off", 0, 0), + ): + cxx_executable = temporary / f"cxx-header-{label}" + run( + cxx + + [ + "-std=c++17", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wconversion", + "-Wshadow", + "-Werror", + "-fno-fast-math", + "-ffp-contract=off", + f"-DLECORE_ENABLE_FORMAT={format_enabled}", + f"-DLECORE_ENABLE_RADIX2={radix2_enabled}", + "-I", + str(AMALGAMATION), + str(cxx_smoke), + str(temporary / f"{label}.o"), + "-lm", + "-o", + str(cxx_executable), + ] + ) + run([str(cxx_executable)]) + + print("liblecore amalgamation: drift and standalone consumers passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/native/check_liblecore_symbols.py b/tests/native/check_liblecore_symbols.py new file mode 100644 index 0000000..3b8b244 --- /dev/null +++ b/tests/native/check_liblecore_symbols.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +"""Fail when a shared liblecore exports an undeclared or missing ABI-0 symbol.""" + +from __future__ import annotations + +import argparse +import os +from pathlib import Path +import shlex +import subprocess +import sys + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +MANIFEST = REPOSITORY_ROOT / "native/liblecore/ABI0_SYMBOLS.txt" + + +def expected_symbols(format_enabled: bool) -> set[str]: + symbols = { + line.strip() + for line in MANIFEST.read_text(encoding="utf-8").splitlines() + if line.strip() and not line.lstrip().startswith("#") + } + if not format_enabled: + symbols = {symbol for symbol in symbols if not symbol.startswith("lecore_format_")} + return symbols + + +def exported_symbols(library: Path, nm_command: str) -> set[str]: + command = shlex.split(nm_command) + ["-g", str(library)] + result = subprocess.run(command, check=True, text=True, capture_output=True) + symbols: set[str] = set() + for line in result.stdout.splitlines(): + fields = line.split() + if len(fields) < 2: + continue + # GNU and LLVM nm put a one-letter symbol class immediately before the name. + symbol_class, name = fields[-2:] + if len(symbol_class) != 1 or symbol_class.upper() not in {"T", "D", "B", "R"}: + continue + if name.startswith("_lecore_"): # Mach-O's conventional C prefix. + name = name[1:] + if name.startswith("lecore_"): + symbols.add(name) + return symbols + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--library", required=True, type=Path) + parser.add_argument("--nm", default=os.environ.get("NM", "nm")) + parser.add_argument("--format", choices=("on", "off"), default="on") + arguments = parser.parse_args() + + if not arguments.library.is_file(): + parser.error(f"shared library does not exist: {arguments.library}") + expected = expected_symbols(arguments.format == "on") + actual = exported_symbols(arguments.library, arguments.nm) + missing = sorted(expected - actual) + unexpected = sorted(actual - expected) + if missing or unexpected: + if missing: + print("missing ABI-0 exports: " + ", ".join(missing), file=sys.stderr) + if unexpected: + print("unexpected liblecore exports: " + ", ".join(unexpected), file=sys.stderr) + return 1 + print(f"liblecore ABI-0 exports: {len(actual)} symbols match the manifest") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/native/check_liblecore_wasm.py b/tests/native/check_liblecore_wasm.py new file mode 100644 index 0000000..5a160ad --- /dev/null +++ b/tests/native/check_liblecore_wasm.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +"""Compile the liblecore amalgamation to WebAssembly and run it under Node.""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import shutil +import subprocess +import tempfile + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +AMALGAMATION = REPOSITORY_ROOT / "native/liblecore/amalgamation" +ABI_SYMBOLS = REPOSITORY_ROOT / "native/liblecore/ABI0_SYMBOLS.txt" + +SMOKE_SOURCE = r""" +#include "lecore.h" + +#include +#include +#include + +static int close_enough(double left, double right) +{ + return fabs(left - right) <= 1e-9; +} + +static int exercise_context(lecore_backend backend) +{ + const double left[8] = {1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + const double right[8] = {0.0, 0.25, 0.0, -0.5, 0.0, 0.75, 0.0, 1.0}; + double output[8] = {0.0}; + lecore_config_v0 config; + lecore_context *context = NULL; + size_t index; + + lecore_config_init_v0(&config); + config.dimension = UINT32_C(8); + config.backend = backend; + if (lecore_context_create(&config, &context) != LECORE_OK || context == NULL) { + return 1; + } + if (lecore_hrr_bind_f64(context, left, right, output) != LECORE_OK) { + lecore_context_destroy(context); + return 2; + } + for (index = 0; index < 8; ++index) { + if (!close_enough(output[index], right[index])) { + lecore_context_destroy(context); + return 3; + } + } + lecore_context_destroy(context); + return 0; +} + +int main(void) +{ + if (lecore_abi_version() != LECORE_ABI_VERSION || + lecore_isa_version() != LECORE_ISA_VERSION || + exercise_context(LECORE_BACKEND_DIRECT) != 0) { + return 10; + } + +#if LECORE_ENABLE_RADIX2 + if ((lecore_capabilities() & LECORE_CAP_RADIX2) == 0 || + exercise_context(LECORE_BACKEND_RADIX2) != 0) { + return 11; + } +#else + if ((lecore_capabilities() & LECORE_CAP_RADIX2) != 0) { + return 12; + } +#endif + +#if LECORE_ENABLE_FORMAT + { + const uint8_t payload[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + uint8_t record[LECORE_FORMAT_HEADER_BYTES + sizeof(payload)]; + lecore_format_descriptor_v1 descriptor; + lecore_format_descriptor_v1 decoded; + const void *decoded_payload = NULL; + size_t decoded_bytes = 0; + size_t record_bytes = 0; + + lecore_format_descriptor_init_v1(&descriptor); + descriptor.artifact_kind = LECORE_ARTIFACT_VECTOR; + descriptor.dimension = UINT32_C(1); + descriptor.vector_count = UINT64_C(1); + descriptor.payload_bytes = sizeof(payload); + if ((lecore_capabilities() & LECORE_CAP_FORMAT) == 0 || + lecore_format_encode_v1(&descriptor, payload, sizeof(payload), + record, sizeof(record), &record_bytes) != LECORE_OK || + record_bytes != sizeof(record) || + lecore_format_decode_v1(record, record_bytes, &decoded, + &decoded_payload, &decoded_bytes) != LECORE_OK || + decoded_bytes != sizeof(payload) || + ((const uint8_t *)decoded_payload)[7] != UINT8_C(8)) { + return 13; + } + } +#else + if ((lecore_capabilities() & LECORE_CAP_FORMAT) != 0) { + return 14; + } +#endif + + return 0; +} +""" + + +def _run(command: list[str]) -> None: + subprocess.run(command, cwd=REPOSITORY_ROOT, check=True) + + +def _exported_functions(format_enabled: bool) -> str: + """Return Emscripten's exact ABI-0 export list for this feature set.""" + symbols = [ + line.strip() + for line in ABI_SYMBOLS.read_text(encoding="utf-8").splitlines() + if line.strip() and not line.lstrip().startswith("#") + ] + if not format_enabled: + symbols = [name for name in symbols if not name.startswith("lecore_format_")] + return json.dumps(["_main", *(f"_{name}" for name in symbols)], separators=(",", ":")) + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--emcc", default=shutil.which("emcc")) + parser.add_argument("--node", default=shutil.which("node")) + arguments = parser.parse_args() + if not arguments.emcc: + parser.error("emcc was not found; pass --emcc") + if not arguments.node: + parser.error("node was not found; pass --node") + + generator = REPOSITORY_ROOT / "tools/generate_liblecore_amalgamation.py" + _run(["python3", str(generator), "--check"]) + + with tempfile.TemporaryDirectory(prefix="liblecore-wasm-") as directory: + temporary = Path(directory) + smoke = temporary / "smoke.c" + smoke.write_text(SMOKE_SOURCE, encoding="utf-8") + for label, format_enabled, radix2_enabled in ( + ("features-on", 1, 1), + ("features-off", 0, 0), + ): + output = temporary / f"{label}.js" + _run( + [ + arguments.emcc, + "-std=c11", + "-O2", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wconversion", + "-Wshadow", + "-Werror", + "-fno-fast-math", + "-ffp-contract=off", + f"-DLECORE_ENABLE_FORMAT={format_enabled}", + f"-DLECORE_ENABLE_RADIX2={radix2_enabled}", + "-I", + str(AMALGAMATION), + str(smoke), + str(AMALGAMATION / "lecore.c"), + "-sASSERTIONS=1", + "-sENVIRONMENT=node", + "-sEXIT_RUNTIME=1", + "-sFILESYSTEM=0", + "-sSTRICT=1", + f"-sEXPORTED_FUNCTIONS={_exported_functions(bool(format_enabled))}", + "-o", + str(output), + ] + ) + _run([arguments.node, str(output)]) + + print( + "liblecore WebAssembly: feature-on/off ABI export and runtime smoke passed" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/native/fixtures/liblecore_isa_v1.json b/tests/native/fixtures/liblecore_isa_v1.json new file mode 100644 index 0000000..4b16b71 --- /dev/null +++ b/tests/native/fixtures/liblecore_isa_v1.json @@ -0,0 +1,41142 @@ +{ + "abi_preview": 0, + "decision_rules": { + "cleanup": "first NaN; otherwise highest score; exact ties choose lowest index", + "reindex": "bit exact" + }, + "dimensions": { + "edge_cases": [ + 3, + 8 + ], + "full_operation_cases": [ + 1, + 2, + 3, + 7, + 8, + 64 + ], + "ordered_reduction_cases": [ + 384, + 1024, + 4096 + ] + }, + "encoding": { + "byte_order": "bit-pattern integers; independent of host byte order", + "float_bits": "fixed-width lowercase IEEE-754 hexadecimal in logical scalar order", + "shape": "row-major C order" + }, + "generator": { + "algorithm": "index-formula-v1/no-rng", + "formula": "sign(i,lane)*((((i+1)*(17*lane+11)+13*lane^2+5*i^2) mod 193)-96)/64", + "owner": "leCore", + "path": "tools/generate_liblecore_fixtures.py", + "sign": "-1 when (i+lane) mod 5 is 0; +1 otherwise", + "zero_numerator_rule": "replace an exact zero numerator with lane+1" + }, + "isa_version": 1, + "profiles": { + "HRR_F32_V1": { + "dtype": "float32", + "edge_cases": [ + { + "dimension": 3, + "expected": { + "finite_validation_status": "LECORE_ENONFINITE", + "first_nan_cleanup_index": 1, + "first_nan_cleanup_score_class": "nan", + "impulse_bind": { + "bits": [ + "bf140000", + "3f740000", + "bf960000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "raw_nan_result_class": "nan", + "shifted_impulse_bind": { + "bits": [ + "bf960000", + "bf140000", + "3f740000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "special_involution": { + "bits": [ + "7fc00042", + "7f800000", + "80000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "special_permute_minus_two": { + "bits": [ + "7f800000", + "7fc00042", + "80000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "tie_cleanup_index": 0, + "zero_nan_cosine": { + "bits": [ + "00000000" + ], + "dtype": "float32", + "shape": [] + }, + "zero_normalize": { + "bits": [ + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "zero_sum_bundle": { + "bits": [ + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + } + }, + "inputs": { + "finite": { + "bits": [ + "bf140000", + "3f740000", + "bf960000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "first_nan_candidates": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "7fc00000", + "3f740000", + "bf960000", + "bf140000", + "3f740000", + "bf960000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "impulse": { + "bits": [ + "3f800000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "inf_vector": { + "bits": [ + "7f800000", + "3f740000", + "bf960000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "nan_vector": { + "bits": [ + "7fc00000", + "3f740000", + "bf960000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "reindex_special": { + "bits": [ + "7fc00042", + "80000000", + "7f800000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "shifted_impulse": { + "bits": [ + "00000000", + "3f800000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "ties": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf140000", + "3f740000", + "bf960000", + "3f140000", + "bf740000", + "3f960000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "zero": { + "bits": [ + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "zero_sum_rows": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "3f140000", + "bf740000", + "3f960000" + ], + "dtype": "float32", + "shape": [ + 2, + 3 + ] + } + } + }, + { + "dimension": 8, + "expected": { + "finite_validation_status": "LECORE_ENONFINITE", + "first_nan_cleanup_index": 1, + "first_nan_cleanup_score_class": "nan", + "impulse_bind": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "raw_nan_result_class": "nan", + "shifted_impulse_bind": { + "bits": [ + "3da00000", + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "special_involution": { + "bits": [ + "7fc00042", + "3da00000", + "3ef80000", + "bf640000", + "bf920000", + "ff800000", + "7f800000", + "80000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "special_permute_minus_two": { + "bits": [ + "7f800000", + "ff800000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "7fc00042", + "80000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "tie_cleanup_index": 0, + "zero_nan_cosine": { + "bits": [ + "00000000" + ], + "dtype": "float32", + "shape": [] + }, + "zero_normalize": { + "bits": [ + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "zero_sum_bundle": { + "bits": [ + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + } + }, + "inputs": { + "finite": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "first_nan_candidates": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "7fc00000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "impulse": { + "bits": [ + "3f800000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "inf_vector": { + "bits": [ + "7f800000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "nan_vector": { + "bits": [ + "7fc00000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "reindex_special": { + "bits": [ + "7fc00042", + "80000000", + "7f800000", + "ff800000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "shifted_impulse": { + "bits": [ + "00000000", + "3f800000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "ties": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "3f140000", + "bf740000", + "3f960000", + "3f9e0000", + "3f920000", + "3f640000", + "bef80000", + "bda00000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "zero": { + "bits": [ + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000", + "00000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "zero_sum_rows": { + "bits": [ + "bf140000", + "3f740000", + "bf960000", + "bf9e0000", + "bf920000", + "bf640000", + "3ef80000", + "3da00000", + "3f140000", + "bf740000", + "3f960000", + "3f9e0000", + "3f920000", + "3f640000", + "bef80000", + "bda00000" + ], + "dtype": "float32", + "shape": [ + 2, + 8 + ] + } + } + } + ], + "finite_cases": [ + { + "dimension": 1, + "expected": { + "bind": { + "bits": [ + "bc5c0000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "bind_batch": { + "bits": [ + "bc5c0000", + "3ca60000", + "bd260000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + }, + "bind_fixed": { + "bits": [ + "bc5c0000", + "bf8ea800", + "3cdc0000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + }, + "bundle": { + "bits": [ + "3f800000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3f800000" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bf800000" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bf800000", + "bf800000", + "3f800000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bff0000000000000", + "bff0000000000000", + "3ff0000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bc5c0000" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "bc5c0000", + "bf8ea800", + "3cdc0000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "normalize": { + "bits": [ + "bf800000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "permute": { + "bits": [ + "bf5c0000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "unbind": { + "bits": [ + "bc5c0000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "unbind_all": { + "bits": [ + "bc5c0000", + "bf8ea800", + "3cdc0000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "b": { + "bits": [ + "3c800000" + ], + "dtype": "float32", + "shape": [ + 1 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "3c800000", + "3fa60000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3fa60000", + "bd000000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3fa60000", + "bd000000" + ], + "dtype": "float32", + "shape": [ + 3, + 1 + ] + } + }, + "name": "formula-d1", + "shift": -3 + }, + { + "dimension": 2, + "expected": { + "bind": { + "bits": [ + "be932000", + "bf30b000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "bind_batch": { + "bits": [ + "be932000", + "bf30b000", + "bf03e000", + "3f82f000", + "3f520000", + "bfd20000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + }, + "bind_fixed": { + "bits": [ + "be932000", + "bf30b000", + "bf623000", + "3e06c000", + "3eef4000", + "3f8e5000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + }, + "bundle": { + "bits": [ + "3f3e0d79", + "bf2b82cb" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3ec9afcc" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bec76f53" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bec76f53", + "bf27503e", + "3ec9afcc" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bfd8a619fac1102c", + "bfe507729b909c6c", + "3fd8ee4df28be4eb" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "be932000" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "be932000", + "bf623000", + "3eef4000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "normalize": { + "bits": [ + "bf6db0a6", + "bebe26eb" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "permute": { + "bits": [ + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "unbind": { + "bits": [ + "be932000", + "bf30b000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "unbind_all": { + "bits": [ + "be932000", + "bf30b000", + "bf623000", + "3e06c000", + "3eef4000", + "3f8e5000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "b": { + "bits": [ + "3c800000", + "3f4c0000" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000", + "bfd5800000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "beb00000", + "3c800000", + "3f4c0000", + "3fa60000", + "bf2c0000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3f4c0000", + "3fa60000", + "bf2c0000", + "bd000000", + "bfa40000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3f4c0000", + "3fa60000", + "bf2c0000", + "bd000000", + "bfa40000" + ], + "dtype": "float32", + "shape": [ + 3, + 2 + ] + } + }, + "name": "formula-d2", + "shift": -4 + }, + { + "dimension": 3, + "expected": { + "bind": { + "bits": [ + "3f304000", + "bf8e2800", + "3f551000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "bind_batch": { + "bits": [ + "3f304000", + "bf8e2800", + "3f551000", + "3eea6000", + "3fda1000", + "c00d2400", + "3f519000", + "bfbff000", + "3f087000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "bind_fixed": { + "bits": [ + "3f304000", + "bf8e2800", + "3f551000", + "bf938000", + "bd2f0000", + "3f8e7800", + "be9ac000", + "3f832800", + "3f289000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "bundle": { + "bits": [ + "3ef2d82c", + "bedb26fc", + "bf44f0df" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3e976b9d" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bef48d69" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bef48d69", + "bf316fde", + "3e976b9d" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bfdef1c3fedbd7c1", + "bfe65227f0b3f68b", + "3fd2716e0c86650a" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bf353000" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "bf353000", + "bf876800", + "3ec2a000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000", + "3ea80000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "normalize": { + "bits": [ + "bf600768", + "beb33920", + "3eab139f" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "permute": { + "bits": [ + "3ea80000", + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "unbind": { + "bits": [ + "bf353000", + "3fadb800", + "be750000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "unbind_all": { + "bits": [ + "bf353000", + "3fadb800", + "be750000", + "bf876800", + "be56c000", + "3f97c000", + "3ec2a000", + "be39c000", + "3f975000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "b": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000", + "bfd5800000000000", + "3fd5800000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3c800000", + "3f4c0000", + "bfa40000", + "3fa60000", + "bf2c0000", + "bf080000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3fa60000", + "bf2c0000", + "bf080000", + "bd000000", + "bfa40000", + "be880000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3fa60000", + "bf2c0000", + "bf080000", + "bd000000", + "bfa40000", + "be880000" + ], + "dtype": "float32", + "shape": [ + 3, + 3 + ] + } + }, + "name": "formula-d3", + "shift": -5 + }, + { + "dimension": 7, + "expected": { + "bind": { + "bits": [ + "bf25d000", + "3fed1000", + "3fc4d800", + "be778000", + "3f1a0000", + "c005bc00", + "bf869800" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "bind_batch": { + "bits": [ + "bf25d000", + "3fed1000", + "3fc4d800", + "be778000", + "3f1a0000", + "c005bc00", + "bf869800", + "3ed98000", + "3e5b0000", + "c08d2000", + "4020b000", + "becf0000", + "3f8d4800", + "beaca000", + "3f413000", + "bfb88800", + "4020e800", + "404eb000", + "3f894000", + "c04d9400", + "bfac1800" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + }, + "bind_fixed": { + "bits": [ + "bf25d000", + "3fed1000", + "3fc4d800", + "be778000", + "3f1a0000", + "c005bc00", + "bf869800", + "bfb67800", + "3e06c000", + "4041e000", + "4017e400", + "3fed1000", + "bfe82000", + "c083fe00", + "404ed000", + "3efbe000", + "c05bf000", + "c03dd800", + "bf8b5800", + "3fb8b800", + "40192c00" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + }, + "bundle": { + "bits": [ + "3ea1459a", + "be9189bd", + "bf02c99e", + "3dd46828", + "3ec6a3c7", + "bf1a6369", + "3e3cce5d" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3f20fbd5" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "be71ede8" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "be71ede8", + "bf289db4", + "3f20fbd5" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bfcdfdd72f720014", + "bfe51c4cb0a4b409", + "3fe4061af423c620" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bf975800" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "bf975800", + "c062a800", + "405dcc00" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "normalize": { + "bits": [ + "bec00cb3", + "be19a3c2", + "3e12a7f4", + "3f01326f", + "3ec38a9a", + "3ded7172", + "bf225e85" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "permute": { + "bits": [ + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "unbind": { + "bits": [ + "bf975800", + "bf5b7000", + "bfc8b000", + "be942000", + "40239000", + "3ef9c000", + "3f507000" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "unbind_all": { + "bits": [ + "bf975800", + "bf5b7000", + "bfc8b000", + "be942000", + "40239000", + "3ef9c000", + "3f507000", + "c062a800", + "bfc39800", + "bf950800", + "402bcc00", + "4050a000", + "3ffa5800", + "bfd26000", + "405dcc00", + "4037c000", + "beaac000", + "c056dc00", + "c034c400", + "bee3a000", + "3f310000" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "b": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000" + ], + "dtype": "float32", + "shape": [ + 7 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000", + "bfd5800000000000", + "3fd5800000000000", + "3ff2a00000000000", + "3fec400000000000", + "3fd1800000000000", + "bff7200000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000" + ], + "dtype": "float32", + "shape": [ + 3, + 7 + ] + } + }, + "name": "formula-d7", + "shift": -9 + }, + { + "dimension": 8, + "expected": { + "bind": { + "bits": [ + "4029a000", + "bdef8000", + "be934000", + "3fb48800", + "c001f400", + "be318000", + "bf87b800", + "bec8c000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "bind_batch": { + "bits": [ + "4029a000", + "bdef8000", + "be934000", + "3fb48800", + "c001f400", + "be318000", + "bf87b800", + "bec8c000", + "bef36000", + "bf9dc800", + "bc440000", + "bf3a3000", + "40074400", + "3e02c000", + "bec32000", + "3d2d0000", + "be966000", + "3dcc8000", + "3f655000", + "4044e400", + "bf2f7000", + "c0041c00", + "bfa49800", + "3fa36800" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "bind_fixed": { + "bits": [ + "4029a000", + "bdef8000", + "be934000", + "3fb48800", + "c001f400", + "be318000", + "bf87b800", + "bec8c000", + "bf983800", + "40054800", + "3f9a0800", + "40863c00", + "bf833800", + "bf3de000", + "c083d600", + "becf4000", + "bf431000", + "c0379400", + "be98c000", + "bdf48000", + "bee30000", + "bf4b8000", + "4019ec00", + "40386800" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "bundle": { + "bits": [ + "3ea0f7d9", + "be914392", + "bf028a8f", + "3dd401c0", + "3ec64402", + "bf1a18fa", + "3e3c7355", + "3d7b4472" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3f1f95df" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "be71b5af" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "be71b5af", + "bf27fef7", + "3f1f95df" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bfce05355ace84a9", + "bfe5064cfdc7584a", + "3fe3ddb3ae72050e" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bf987800" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "bf987800", + "c0625800", + "405e8c00" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000", + "3d000000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "normalize": { + "bits": [ + "bec00820", + "be19a01a", + "3e12a476", + "3f012f5b", + "3ec385f2", + "3ded6bcb", + "bf225aa7", + "3c5f7482" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "permute": { + "bits": [ + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "unbind": { + "bits": [ + "bf987800", + "3e940000", + "c01ed800", + "3e284000", + "3fb02000", + "bf78b000", + "3fc8d800", + "3f9f8000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "unbind_all": { + "bits": [ + "bf987800", + "3e940000", + "c01ed800", + "3e284000", + "3fb02000", + "bf78b000", + "3fc8d800", + "3f9f8000", + "c0625800", + "bf621000", + "c0212800", + "403f2c00", + "4009c800", + "403e3c00", + "bf9d5000", + "3d5f0000", + "405e8c00", + "3fc43800", + "bf392000", + "bfeeb800", + "3eae4000", + "bf2b6000", + "c00c5c00", + "3dcd0000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "b": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000" + ], + "dtype": "float32", + "shape": [ + 8 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000", + "bfd5800000000000", + "3fd5800000000000", + "3ff2a00000000000", + "3fec400000000000", + "3fd1800000000000", + "bff7200000000000", + "3fa4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000", + "3ec00000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000", + "3ec00000" + ], + "dtype": "float32", + "shape": [ + 3, + 8 + ] + } + }, + "name": "formula-d8", + "shift": -10 + }, + { + "dimension": 64, + "expected": { + "bind": { + "bits": [ + "bbc80000", + "c091ba00", + "41029900", + "bfc70000", + "c0ed6c00", + "bdd38000", + "bffed000", + "40cb0800", + "4098ba00", + "bf371000", + "3fcbb000", + "c0c58200", + "411aaa00", + "c1013900", + "c0e06400", + "c0820c00", + "c1937e00", + "bebce000", + "40b02200", + "40e75e00", + "41037400", + "4086f600", + "c05c2400", + "c1410f00", + "c111d800", + "bfbdd800", + "c106cf00", + "c0857a00", + "3f0e0000", + "4027c000", + "40110000", + "c11c6200", + "bf1cf000", + "c141ce00", + "bf489000", + "40481000", + "c0e47200", + "bf1c0000", + "409f8e00", + "40d34200", + "4015a000", + "c13f0600", + "bfae9800", + "c156b300", + "40886400", + "40d6e200", + "bdec8000", + "4149f700", + "3f567000", + "41123a00", + "c0ff7c00", + "c0d7f000", + "c086fc00", + "c148ca00", + "3fd39800", + "40df7800", + "40617c00", + "3faec800", + "c1206500", + "bfe9e800", + "c0f00000", + "41146a00", + "40be4400", + "c0bce400" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "bind_batch": { + "bits": [ + "bbc80000", + "c091ba00", + "41029900", + "bfc70000", + "c0ed6c00", + "bdd38000", + "bffed000", + "40cb0800", + "4098ba00", + "bf371000", + "3fcbb000", + "c0c58200", + "411aaa00", + "c1013900", + "c0e06400", + "c0820c00", + "c1937e00", + "bebce000", + "40b02200", + "40e75e00", + "41037400", + "4086f600", + "c05c2400", + "c1410f00", + "c111d800", + "bfbdd800", + "c106cf00", + "c0857a00", + "3f0e0000", + "4027c000", + "40110000", + "c11c6200", + "bf1cf000", + "c141ce00", + "bf489000", + "40481000", + "c0e47200", + "bf1c0000", + "409f8e00", + "40d34200", + "4015a000", + "c13f0600", + "bfae9800", + "c156b300", + "40886400", + "40d6e200", + "bdec8000", + "4149f700", + "3f567000", + "41123a00", + "c0ff7c00", + "c0d7f000", + "c086fc00", + "c148ca00", + "3fd39800", + "40df7800", + "40617c00", + "3faec800", + "c1206500", + "bfe9e800", + "c0f00000", + "41146a00", + "40be4400", + "c0bce400", + "409c6c00", + "c1117200", + "bffac000", + "40c8e600", + "bf57a000", + "40593400", + "405a7800", + "bf1cd000", + "41004300", + "c169a100", + "bffad800", + "c1162500", + "c0cbae00", + "bfda0800", + "40f23600", + "3fd52800", + "c0c4fe00", + "bf09f000", + "404f6400", + "c06b0400", + "bfaf9000", + "bdcb0000", + "c1078300", + "c0619800", + "bfbce800", + "40956400", + "c01a0c00", + "4107de00", + "c0582400", + "40643400", + "41059a00", + "401ce800", + "c01af800", + "3fe11000", + "40f18400", + "c0aa0800", + "c047f400", + "be588000", + "bffbf800", + "c0b91c00", + "3f910000", + "41a75100", + "40849200", + "408dc800", + "bfda4000", + "bfa9d800", + "c07b4c00", + "41017e00", + "40b76c00", + "4070ac00", + "3fabb000", + "413c8500", + "c0835e00", + "4096a200", + "40d02000", + "bffee800", + "40b12200", + "40f5d200", + "3f236000", + "c01c2800", + "bf077000", + "40155c00", + "4052d800", + "403bb800", + "bf7d9000", + "c0e0f200", + "4100a300", + "c19e1800", + "4036ac00", + "c0ff4800", + "c099c200", + "c1975100", + "3e0a4000", + "bfdc6800", + "bf93f800", + "c192de80", + "c0c96800", + "3fc9e000", + "402f0c00", + "c0b4d800", + "403cd800", + "40aec000", + "c00b6800", + "c050ec00", + "c0285c00", + "bdf80000", + "3f829800", + "bdd78000", + "40a55c00", + "40946000", + "bf9a6800", + "bff5a000", + "40364400", + "be560000", + "40aad400", + "be8fa000", + "c09dec00", + "40b21200", + "403ba800", + "4095ba00", + "c0ed0200", + "41434c00", + "40b47e00", + "c0675800", + "c06ed800", + "40473800", + "3f106000", + "40085c00", + "c068b400", + "40c42600", + "3f05a000", + "3f146000", + "c0b81000", + "40a30200", + "403c2800", + "c107a700", + "bfe3c800", + "405e8800", + "c01f6800", + "c0f77e00", + "40c13e00", + "3ff08800", + "4137c900", + "c02aa000", + "c102c300", + "c0a6c000", + "3f446000", + "c1395200" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + }, + "bind_fixed": { + "bits": [ + "bbc80000", + "c091ba00", + "41029900", + "bfc70000", + "c0ed6c00", + "bdd38000", + "bffed000", + "40cb0800", + "4098ba00", + "bf371000", + "3fcbb000", + "c0c58200", + "411aaa00", + "c1013900", + "c0e06400", + "c0820c00", + "c1937e00", + "bebce000", + "40b02200", + "40e75e00", + "41037400", + "4086f600", + "c05c2400", + "c1410f00", + "c111d800", + "bfbdd800", + "c106cf00", + "c0857a00", + "3f0e0000", + "4027c000", + "40110000", + "c11c6200", + "bf1cf000", + "c141ce00", + "bf489000", + "40481000", + "c0e47200", + "bf1c0000", + "409f8e00", + "40d34200", + "4015a000", + "c13f0600", + "bfae9800", + "c156b300", + "40886400", + "40d6e200", + "bdec8000", + "4149f700", + "3f567000", + "41123a00", + "c0ff7c00", + "c0d7f000", + "c086fc00", + "c148ca00", + "3fd39800", + "40df7800", + "40617c00", + "3faec800", + "c1206500", + "bfe9e800", + "c0f00000", + "41146a00", + "40be4400", + "c0bce400", + "40b9fe00", + "4112fe00", + "bf818800", + "40d87e00", + "3f2d0000", + "c1127900", + "c1a9f600", + "bef1e000", + "bff0b800", + "3fbeb800", + "c0b52a00", + "40056000", + "c03a5000", + "3eec2000", + "c0e9f600", + "c0f9de00", + "c1323b00", + "c049f400", + "407f0000", + "40b71200", + "408fc000", + "3f226000", + "c0120800", + "c0923800", + "bfeb6800", + "c0b89000", + "c0a2be00", + "c0080800", + "40bb9800", + "402e3c00", + "bf146000", + "40a08600", + "c0f24600", + "be558000", + "c0bf6200", + "c026d800", + "c0b91400", + "403a4c00", + "4100a700", + "3ff68800", + "40205000", + "c01a3800", + "bf8bb000", + "bfa33000", + "3eb48000", + "40b41800", + "3f791000", + "c0de4000", + "40934e00", + "c08f1400", + "4003d400", + "c12eb300", + "40b0ea00", + "c03dd000", + "403aa800", + "3f5a7000", + "4090ca00", + "c0287400", + "bfea9000", + "c0bc9e00", + "3f613000", + "40fc0800", + "c017e000", + "c0295400", + "405bac00", + "40d16600", + "c0669400", + "c0e63200", + "3fdd6000", + "41344a00", + "3f453000", + "407e1c00", + "40644000", + "400c1c00", + "3c700000", + "c09d3800", + "bfb4b800", + "404a7000", + "411b3f00", + "41143e00", + "c097d800", + "c056c400", + "bfa9b000", + "3be80000", + "c129cf00", + "c1184400", + "40816400", + "4062c000", + "c066ec00", + "c083ae00", + "c0837800", + "c057e400", + "4073d800", + "40120c00", + "3ee44000", + "c0d70c00", + "41052f00", + "3ff70000", + "40c5c600", + "bf32f000", + "4127f600", + "4185ff80", + "bf321000", + "3fc8f800", + "bf698000", + "41118a00", + "bedc6000", + "bf5cc000", + "bf065000", + "40b37e00", + "410bb900", + "40c6a600", + "c09c7200", + "bddf0000", + "40ada000", + "41265900", + "c116cf00", + "3e024000", + "c0453400", + "41090200", + "be598000", + "40e11800", + "c08e0200", + "3eb16000", + "40e37600", + "bf46d000", + "3eb28000", + "c1023800" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + }, + "bundle": { + "bits": [ + "3de020b4", + "bdca42fa", + "be35c31b", + "3d1398a8", + "3e0a07a7", + "be568fb2", + "3d83325d", + "3caeedd1", + "3c5aa945", + "3dcfba68", + "bd2eedd1", + "3d39dcae", + "be6020b4", + "be436daf", + "3d1e8785", + "3dc20fd4", + "3dd2761f", + "3e074bf0", + "bd7b75dc", + "bd5aa945", + "bd6020b4", + "bcaeedd1", + "be5aa945", + "bde853d9", + "be330764", + "3cc4cb8b", + "bd8e213a", + "3d03325d", + "bdb720f7", + "bea6baab", + "3d965460", + "3e5d64fc", + "be255ccf", + "3c2eedd1", + "3c191017", + "3dbf541c", + "be478742", + "bcaeedd1", + "bd90dcf1", + "3d6b0f91", + "be31a988", + "bc7086ff", + "3e478742", + "3e39dcae", + "3d9bcbce", + "3d39dcae", + "bc83325d", + "3e6b0f91", + "bbaeedd1", + "3d85ee14", + "3e7086ff", + "3d7086ff", + "3da97662", + "3e10dcf1", + "3ccfba68", + "3e0cc35e", + "bd44cb8b", + "3e8a07a7", + "3ddaa945", + "bd34653f", + "3c2eedd1", + "bda1433d", + "3c8e213a", + "3e17b23b" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3dd81098" + ], + "dtype": "float32", + "shape": [] + } + }, + "cosine": { + "bits": [ + "be8f710c" + ], + "dtype": "float32", + "shape": [] + }, + "cosine_many": { + "bits": [ + "be8f710c", + "bd3061f4", + "3dd81098" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "cosine_many_f64_f32": { + "bits": [ + "bfd1dd7976e1e4c0", + "bfa589ef487a213f", + "3fbaa90074e325dc" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "c15f1200" + ], + "dtype": "float32", + "shape": [] + }, + "dot_many": { + "bits": [ + "c15f1200", + "c0076c00", + "40a97400" + ], + "dtype": "float32", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bf5c0000", + "bf780000", + "3f640000", + "bde00000", + "bf740000", + "bfb00000", + "3f580000", + "3ef00000", + "3e800000", + "3e400000", + "be900000", + "3f080000", + "3f700000", + "3fc00000", + "bf4c0000", + "bda00000", + "3f8e0000", + "bf380000", + "3f200000", + "bf640000", + "bf440000", + "bee00000", + "bfbe0000", + "3f240000", + "bdc00000", + "3f2c0000", + "bf8c0000", + "bfae0000", + "bfbc0000", + "bfb60000", + "3f9c0000", + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "bfb40000", + "beb80000", + "bf3c0000", + "3fac0000", + "3f100000", + "bd800000", + "3f080000", + "bf580000", + "bf800000", + "bf800000", + "bf580000", + "3f080000", + "bd800000", + "3f100000", + "3fac0000", + "bf3c0000", + "beb80000", + "bfb40000", + "3d000000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "normalize": { + "bits": [ + "bdfad943", + "bd48adcf", + "3d3f8ea3", + "3e28c0b4", + "3dff68d9", + "3d1b11f2", + "be5414c7", + "3b91f2c5", + "be4d3d66", + "bd51ccfc", + "bdd65c92", + "3e441e39", + "3da4311e", + "bc11f2c5", + "3d9b11f2", + "bdf649ad", + "be11f2c5", + "be11f2c5", + "bdf649ad", + "3d9b11f2", + "bc11f2c5", + "3da4311e", + "3e441e39", + "bdd65c92", + "bd51ccfc", + "be4d3d66", + "3b91f2c5", + "be5414c7", + "3d1b11f2", + "3dff68d9", + "3e28c0b4", + "3d3f8ea3", + "bd48adcf", + "bdfad943", + "3e31dfe1", + "be4f8531", + "be565c92", + "be466604", + "be1fa188", + "3dc41e39", + "bc5aec28", + "3dbaff0d", + "be58a45d", + "bd7f68d9", + "bddf7bbe", + "be01fc38", + "3db66f77", + "bdd1ccfc", + "3e21e953", + "bc366f77", + "bde89aeb", + "3e5aec28", + "3e08d399", + "3d9b11f2", + "bd24311e", + "3cdaec28", + "3d11f2c5", + "3d88d399", + "3df649ad", + "be48adcf", + "be0b1b64", + "bc7f68d9", + "3e01fc38", + "be0d632f" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "permute": { + "bits": [ + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "bfb40000", + "beb80000", + "bf3c0000", + "3fac0000", + "3f100000", + "bd800000", + "3f080000", + "bf580000", + "bf800000", + "bf800000", + "bf580000", + "3f080000", + "bd800000", + "3f100000", + "3fac0000", + "bf3c0000", + "beb80000", + "bfb40000", + "3d000000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000", + "bf5c0000", + "3f9c0000", + "bfb60000", + "bfbc0000", + "bfae0000", + "bf8c0000", + "3f2c0000", + "bdc00000", + "3f240000", + "bfbe0000", + "bee00000", + "bf440000", + "bf640000", + "3f200000", + "bf380000", + "3f8e0000", + "bda00000", + "bf4c0000", + "3fc00000", + "3f700000", + "3f080000", + "be900000", + "3e400000", + "3e800000", + "3ef00000", + "3f580000", + "bfb00000", + "bf740000", + "bde00000", + "3f640000", + "bf780000", + "bf5c0000", + "beb00000" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "unbind": { + "bits": [ + "c15f1200", + "3fb2c800", + "c1076d00", + "40855000", + "413cd900", + "3f9d0800", + "3f868000", + "c099d400", + "c0854200", + "c1148e00", + "c068bc00", + "40fc7800", + "bfd03000", + "4113f000", + "40a1a400", + "bf5a4000", + "3f0cd000", + "c172b500", + "3e9a0000", + "c143f000", + "3f875000", + "40052c00", + "c13e0e00", + "40418400", + "3f6df000", + "41293300", + "be81e000", + "c121cc00", + "c11a9200", + "c194cf00", + "3fe53000", + "40b45200", + "c0b20c00", + "3ff61800", + "402efc00", + "411f5500", + "bf00d000", + "c097c400", + "bf8f6000", + "c11ede00", + "40164000", + "3f211000", + "3fabb000", + "c03dec00", + "c0cef800", + "c0a1d800", + "c107a700", + "bf1d0000", + "40e50000", + "c135a000", + "40923600", + "bfac5800", + "4072dc00", + "40c01c00", + "c0aa7c00", + "c1062300", + "c120de00", + "c008b400", + "4084ee00", + "3f9b9000", + "40b04000", + "41078500", + "40b63e00", + "400c8000" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "unbind_all": { + "bits": [ + "c15f1200", + "3fb2c800", + "c1076d00", + "40855000", + "413cd900", + "3f9d0800", + "3f868000", + "c099d400", + "c0854200", + "c1148e00", + "c068bc00", + "40fc7800", + "bfd03000", + "4113f000", + "40a1a400", + "bf5a4000", + "3f0cd000", + "c172b500", + "3e9a0000", + "c143f000", + "3f875000", + "40052c00", + "c13e0e00", + "40418400", + "3f6df000", + "41293300", + "be81e000", + "c121cc00", + "c11a9200", + "c194cf00", + "3fe53000", + "40b45200", + "c0b20c00", + "3ff61800", + "402efc00", + "411f5500", + "bf00d000", + "c097c400", + "bf8f6000", + "c11ede00", + "40164000", + "3f211000", + "3fabb000", + "c03dec00", + "c0cef800", + "c0a1d800", + "c107a700", + "bf1d0000", + "40e50000", + "c135a000", + "40923600", + "bfac5800", + "4072dc00", + "40c01c00", + "c0aa7c00", + "c1062300", + "c120de00", + "c008b400", + "4084ee00", + "3f9b9000", + "40b04000", + "41078500", + "40b63e00", + "400c8000", + "c0076c00", + "c0a19400", + "bf8c2800", + "bff5d800", + "40ddb600", + "c042cc00", + "3fb69800", + "3e050000", + "bfe2e000", + "c0eb3800", + "c02afc00", + "c05e0400", + "3fd90800", + "411d3c00", + "40dc7600", + "40f5ac00", + "c0928c00", + "c0068400", + "bf275000", + "c0068000", + "40b20800", + "40185c00", + "40e95800", + "bf470000", + "40620800", + "3dca8000", + "c10d7600", + "c1339100", + "c0933800", + "c0f9a200", + "bf4b8000", + "be268000", + "40fabc00", + "c0937e00", + "bfe0e000", + "c04e4c00", + "bf83f800", + "bfb94800", + "c121cd00", + "c0fe7000", + "3cea0000", + "41152b00", + "40b15800", + "40261000", + "c076dc00", + "bfefc800", + "c1550a00", + "3de68000", + "c0875200", + "40270000", + "c1884580", + "40bdf800", + "40ae5000", + "40228000", + "c10acc00", + "4050e000", + "bf151000", + "c0987e00", + "3fcc0800", + "3f940800", + "bf8e6000", + "bee12000", + "402bec00", + "bf8b8000", + "40a97400", + "402ed000", + "c03ca400", + "4144eb00", + "408b2a00", + "41881a00", + "c08dd400", + "3fd03800", + "40ce9600", + "4186df00", + "3f8ba800", + "3c900000", + "c001d400", + "bf6f6000", + "3ffe9000", + "40700800", + "bfea1800", + "bfc13000", + "40ec0200", + "40b1b400", + "c0604800", + "c096a200", + "bef82000", + "40df6000", + "c078ec00", + "c0955800", + "40c6ba00", + "40daee00", + "40390c00", + "bd4b0000", + "3f152000", + "4020e800", + "c0348000", + "c04d5c00", + "c07d7400", + "bd938000", + "40fc2600", + "4128b300", + "c0317000", + "c0219000", + "3ff58000", + "409d8c00", + "c0be8600", + "bfc13800", + "401c2000", + "40486800", + "40b5b600", + "c094f400", + "405e6000", + "c103b000", + "40ee4c00", + "40a3c200", + "c03aa000", + "c1060d00", + "c09f5000", + "402e0000", + "c1367400", + "c0de6000", + "40680c00", + "40f44200", + "40afcc00", + "c0c5d000", + "be2a8000", + "4059d800" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "bfb40000", + "beb80000", + "bf3c0000", + "3fac0000", + "3f100000", + "bd800000", + "3f080000", + "bf580000", + "bf800000", + "bf800000", + "bf580000", + "3f080000", + "bd800000", + "3f100000", + "3fac0000", + "bf3c0000", + "beb80000", + "bfb40000", + "3d000000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000", + "bf5c0000", + "3f9c0000", + "bfb60000", + "bfbc0000", + "bfae0000", + "bf8c0000", + "3f2c0000", + "bdc00000", + "3f240000", + "bfbe0000", + "bee00000", + "bf440000", + "bf640000", + "3f200000", + "bf380000", + "3f8e0000", + "bda00000", + "bf4c0000", + "3fc00000", + "3f700000", + "3f080000", + "be900000", + "3e400000", + "3e800000", + "3ef00000", + "3f580000", + "bfb00000", + "bf740000", + "bde00000", + "3f640000", + "bf780000" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "b": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fb60000", + "3f1c0000", + "be600000", + "bf640000", + "bfb40000", + "bfa00000", + "3f860000", + "3f800000", + "3f8e0000", + "3fb00000", + "3f9c0000", + "bf240000", + "3dc00000", + "3f7c0000", + "bf7c0000", + "be600000", + "bfb80000", + "3da00000", + "bfa20000", + "3f100000", + "3ef00000", + "bfac0000", + "3f740000", + "3ec80000", + "bc800000", + "3e880000", + "beb80000", + "be980000", + "bda00000", + "3e980000", + "bf540000", + "bfc00000", + "bf280000", + "3eb00000", + "3fc00000", + "3e500000", + "3fa20000", + "be000000", + "bfae0000", + "3f140000", + "3eb00000", + "bf8e0000", + "3fa60000", + "3f580000", + "3f0c0000", + "bed00000", + "3ed80000", + "3f180000", + "3f6c0000", + "3fb40000", + "3f780000", + "be300000", + "3f480000", + "bf900000", + "3e100000", + "3fba0000" + ], + "dtype": "float32", + "shape": [ + 64 + ] + }, + "mixed_query": { + "bits": [ + "bfeb400000000000", + "bfd5800000000000", + "3fd5800000000000", + "3ff2a00000000000", + "3fec400000000000", + "3fd1800000000000", + "bff7200000000000", + "3fa4000000000000", + "bff6600000000000", + "bfd6800000000000", + "bfe7400000000000", + "3ff5a00000000000", + "3fe2400000000000", + "bfac000000000000", + "3fe1400000000000", + "bfeac00000000000", + "bfefc00000000000", + "bfefc00000000000", + "bfeac00000000000", + "3fe1400000000000", + "bfac000000000000", + "3fe2400000000000", + "3ff5a00000000000", + "bfe7400000000000", + "bfd6800000000000", + "bff6600000000000", + "3fa4000000000000", + "bff7200000000000", + "3fd1800000000000", + "3fec400000000000", + "3ff2a00000000000", + "3fd5800000000000", + "bfd5800000000000", + "bfeb400000000000", + "3ff3a00000000000", + "bff6a00000000000", + "bff7600000000000", + "bff5a00000000000", + "bff1600000000000", + "3fe5c00000000000", + "bfb6000000000000", + "3fe4c00000000000", + "bff7a00000000000", + "bfdb800000000000", + "bfe8400000000000", + "bfec400000000000", + "3fe4400000000000", + "bfe6c00000000000", + "3ff1e00000000000", + "bfb2000000000000", + "bfe9400000000000", + "3ff8200000000000", + "3fee400000000000", + "3fe1400000000000", + "bfd1800000000000", + "3fc9000000000000", + "3fd0800000000000", + "3fde800000000000", + "3feb400000000000", + "bff5e00000000000", + "bfee400000000000", + "bfba000000000000", + "3fecc00000000000", + "bfeec00000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "paired_left": { + "bits": [ + "bf5c0000", + "beb00000", + "3ea80000", + "3f940000", + "3f600000", + "3e880000", + "bfba0000", + "3d000000", + "bfb40000", + "beb80000", + "bf3c0000", + "3fac0000", + "3f100000", + "bd800000", + "3f080000", + "bf580000", + "bf800000", + "bf800000", + "bf580000", + "3f080000", + "bd800000", + "3f100000", + "3fac0000", + "bf3c0000", + "beb80000", + "bfb40000", + "3d000000", + "bfba0000", + "3e880000", + "3f600000", + "3f940000", + "3ea80000", + "beb00000", + "bf5c0000", + "3f9c0000", + "bfb60000", + "bfbc0000", + "bfae0000", + "bf8c0000", + "3f2c0000", + "bdc00000", + "3f240000", + "bfbe0000", + "bee00000", + "bf440000", + "bf640000", + "3f200000", + "bf380000", + "3f8e0000", + "bda00000", + "bf4c0000", + "3fc00000", + "3f700000", + "3f080000", + "be900000", + "3e400000", + "3e800000", + "3ef00000", + "3f580000", + "bfb00000", + "bf740000", + "bde00000", + "3f640000", + "bf780000", + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fb60000", + "3f1c0000", + "be600000", + "bf640000", + "bfb40000", + "bfa00000", + "3f860000", + "3f800000", + "3f8e0000", + "3fb00000", + "3f9c0000", + "bf240000", + "3dc00000", + "3f7c0000", + "bf7c0000", + "be600000", + "bfb80000", + "3da00000", + "bfa20000", + "3f100000", + "3ef00000", + "bfac0000", + "3f740000", + "3ec80000", + "bc800000", + "3e880000", + "beb80000", + "be980000", + "bda00000", + "3e980000", + "bf540000", + "bfc00000", + "bf280000", + "3eb00000", + "3fc00000", + "3e500000", + "3fa20000", + "be000000", + "bfae0000", + "3f140000", + "3eb00000", + "bf8e0000", + "3fa60000", + "3f580000", + "3f0c0000", + "bed00000", + "3ed80000", + "3f180000", + "3f6c0000", + "3fb40000", + "3f780000", + "be300000", + "3f480000", + "bf900000", + "3e100000", + "3fba0000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000", + "bf840000", + "3fa20000", + "3f340000", + "3e980000", + "bd400000", + "bd400000", + "3c800000", + "3e700000", + "3f1c0000", + "bf920000", + "bf980000", + "beb00000", + "3f280000", + "bf9a0000", + "bde00000", + "bfb80000", + "3e400000", + "bf860000", + "3f640000", + "3d000000", + "bf4c0000", + "bfb40000", + "3f940000", + "3f5c0000", + "bf380000", + "3f3c0000", + "3f680000", + "3f9e0000", + "bfa60000", + "3f280000", + "3e100000", + "3f8c0000", + "bf500000", + "3ee80000", + "3f920000", + "3ee00000", + "bf580000", + "3f860000", + "3da00000", + "3f3c0000", + "bfb20000", + "3f900000", + "3f480000", + "3f180000", + "bf100000", + "3f300000", + "3f780000", + "3fb40000", + "bf820000", + "3e880000", + "3f240000", + "bfa80000", + "bdc00000", + "3fa40000", + "3e500000", + "3fbe0000" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + }, + "paired_right": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fb60000", + "3f1c0000", + "be600000", + "bf640000", + "bfb40000", + "bfa00000", + "3f860000", + "3f800000", + "3f8e0000", + "3fb00000", + "3f9c0000", + "bf240000", + "3dc00000", + "3f7c0000", + "bf7c0000", + "be600000", + "bfb80000", + "3da00000", + "bfa20000", + "3f100000", + "3ef00000", + "bfac0000", + "3f740000", + "3ec80000", + "bc800000", + "3e880000", + "beb80000", + "be980000", + "bda00000", + "3e980000", + "bf540000", + "bfc00000", + "bf280000", + "3eb00000", + "3fc00000", + "3e500000", + "3fa20000", + "be000000", + "bfae0000", + "3f140000", + "3eb00000", + "bf8e0000", + "3fa60000", + "3f580000", + "3f0c0000", + "bed00000", + "3ed80000", + "3f180000", + "3f6c0000", + "3fb40000", + "3f780000", + "be300000", + "3f480000", + "bf900000", + "3e100000", + "3fba0000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000", + "bf840000", + "3fa20000", + "3f340000", + "3e980000", + "bd400000", + "bd400000", + "3c800000", + "3e700000", + "3f1c0000", + "bf920000", + "bf980000", + "beb00000", + "3f280000", + "bf9a0000", + "bde00000", + "bfb80000", + "3e400000", + "bf860000", + "3f640000", + "3d000000", + "bf4c0000", + "bfb40000", + "3f940000", + "3f5c0000", + "bf380000", + "3f3c0000", + "3f680000", + "3f9e0000", + "bfa60000", + "3f280000", + "3e100000", + "3f8c0000", + "bf500000", + "3ee80000", + "3f920000", + "3ee00000", + "bf580000", + "3f860000", + "3da00000", + "3f3c0000", + "bfb20000", + "3f900000", + "3f480000", + "3f180000", + "bf100000", + "3f300000", + "3f780000", + "3fb40000", + "bf820000", + "3e880000", + "3f240000", + "bfa80000", + "bdc00000", + "3fa40000", + "3e500000", + "3fbe0000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000", + "3ec00000", + "be700000", + "bf300000", + "bf7c0000", + "3f900000", + "bf8e0000", + "bf700000", + "bf1c0000", + "be000000", + "bf040000", + "3fa80000", + "bf400000", + "3eb80000", + "bfb20000", + "bd000000", + "bfb40000", + "3ea80000", + "bf4c0000", + "3fa00000", + "bee00000", + "be600000", + "bf380000", + "bf880000", + "bfa00000", + "3fa40000", + "bf940000", + "bf600000", + "bee00000", + "3e200000", + "bf680000", + "bf9a0000", + "be100000", + "3f8a0000", + "bf100000", + "bf780000", + "beb80000", + "3fbe0000", + "3ef00000", + "bec80000", + "3f8c0000", + "3fb00000", + "3f7c0000", + "3f400000", + "3f2c0000", + "bf400000", + "3f7c0000", + "3fb00000", + "bf8c0000", + "bec80000", + "bef00000", + "3fbe0000", + "beb80000", + "3f780000", + "bf100000", + "bf8a0000", + "be100000", + "bf9a0000" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + }, + "rows": { + "bits": [ + "3c800000", + "3f4c0000", + "bfa40000", + "3e400000", + "3f880000", + "bf0c0000", + "3f820000", + "be900000", + "3fb60000", + "3f1c0000", + "be600000", + "bf640000", + "bfb40000", + "bfa00000", + "3f860000", + "3f800000", + "3f8e0000", + "3fb00000", + "3f9c0000", + "bf240000", + "3dc00000", + "3f7c0000", + "bf7c0000", + "be600000", + "bfb80000", + "3da00000", + "bfa20000", + "3f100000", + "3ef00000", + "bfac0000", + "3f740000", + "3ec80000", + "bc800000", + "3e880000", + "beb80000", + "be980000", + "bda00000", + "3e980000", + "bf540000", + "bfc00000", + "bf280000", + "3eb00000", + "3fc00000", + "3e500000", + "3fa20000", + "be000000", + "bfae0000", + "3f140000", + "3eb00000", + "bf8e0000", + "3fa60000", + "3f580000", + "3f0c0000", + "bed00000", + "3ed80000", + "3f180000", + "3f6c0000", + "3fb40000", + "3f780000", + "be300000", + "3f480000", + "bf900000", + "3e100000", + "3fba0000", + "3fa60000", + "bf2c0000", + "bf080000", + "bf900000", + "3ec80000", + "bf740000", + "3f600000", + "3e200000", + "bf840000", + "3fa20000", + "3f340000", + "3e980000", + "bd400000", + "bd400000", + "3c800000", + "3e700000", + "3f1c0000", + "bf920000", + "bf980000", + "beb00000", + "3f280000", + "bf9a0000", + "bde00000", + "bfb80000", + "3e400000", + "bf860000", + "3f640000", + "3d000000", + "bf4c0000", + "bfb40000", + "3f940000", + "3f5c0000", + "bf380000", + "3f3c0000", + "3f680000", + "3f9e0000", + "bfa60000", + "3f280000", + "3e100000", + "3f8c0000", + "bf500000", + "3ee80000", + "3f920000", + "3ee00000", + "bf580000", + "3f860000", + "3da00000", + "3f3c0000", + "bfb20000", + "3f900000", + "3f480000", + "3f180000", + "bf100000", + "3f300000", + "3f780000", + "3fb40000", + "bf820000", + "3e880000", + "3f240000", + "bfa80000", + "bdc00000", + "3fa40000", + "3e500000", + "3fbe0000", + "bd000000", + "bfa40000", + "be880000", + "3fae0000", + "3e000000", + "bf740000", + "bf920000", + "3ec00000", + "be700000", + "bf300000", + "bf7c0000", + "3f900000", + "bf8e0000", + "bf700000", + "bf1c0000", + "be000000", + "bf040000", + "3fa80000", + "bf400000", + "3eb80000", + "bfb20000", + "bd000000", + "bfb40000", + "3ea80000", + "bf4c0000", + "3fa00000", + "bee00000", + "be600000", + "bf380000", + "bf880000", + "bfa00000", + "3fa40000", + "bf940000", + "bf600000", + "bee00000", + "3e200000", + "bf680000", + "bf9a0000", + "be100000", + "3f8a0000", + "bf100000", + "bf780000", + "beb80000", + "3fbe0000", + "3ef00000", + "bec80000", + "3f8c0000", + "3fb00000", + "3f7c0000", + "3f400000", + "3f2c0000", + "bf400000", + "3f7c0000", + "3fb00000", + "bf8c0000", + "bec80000", + "bef00000", + "3fbe0000", + "beb80000", + "3f780000", + "bf100000", + "bf8a0000", + "be100000", + "bf9a0000" + ], + "dtype": "float32", + "shape": [ + 3, + 64 + ] + } + }, + "name": "formula-d64", + "shift": -66 + } + ], + "input_domain": { + "finite_formula_range": [ + -1.5, + 1.5 + ], + "nonzero_subnormal_outputs": "excluded", + "rounding": "round-to-nearest", + "subnormal_inputs": "excluded" + }, + "profile_id": "0x00010002", + "reduction_cases": [ + { + "dimension": 384, + "expected": { + "cosine_many": { + "bits": [ + "bbb0a030", + "3d8ca7cc" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "bfd59800", + "41abe380" + ], + "dtype": "float32", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000" + ], + "dtype": "float32", + "shape": [ + 384 + ] + }, + "rows": { + "bits": [ + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000" + ], + "dtype": "float32", + "shape": [ + 2, + 384 + ] + } + }, + "name": "ordered-reduction-d384" + }, + { + "dimension": 1024, + "expected": { + "cosine_many": { + "bits": [ + "3c44f3c2", + "3c94bd5b" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "411e7200", + "4172be00" + ], + "dtype": "float32", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "bf200000", + "bf240000", + "bf740000", + "3f200000", + "bf280000", + "3f9e0000", + "be880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "bf480000", + "3f400000", + "3f600000", + "3f940000", + "bfb60000", + "3f540000", + "bda00000", + "3f540000", + "bf900000", + "3dc00000", + "bfbc0000", + "bc800000", + "bfac0000", + "3f000000", + "bf040000", + "3fb00000", + "3f700000", + "3ec80000", + "3dc00000", + "be700000", + "3ea00000", + "be700000", + "3dc00000", + "3ec80000", + "3f700000", + "3fb00000", + "bf040000", + "3f000000", + "bfac0000", + "bc800000", + "bfbc0000", + "3dc00000", + "bf900000", + "3f540000", + "bda00000", + "3f540000", + "bfb60000", + "3f940000", + "3f600000", + "3f400000", + "bf480000", + "3f780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "be880000", + "3f9e0000", + "bf280000", + "3f200000", + "bf740000", + "bf240000", + "bf200000", + "3fa40000", + "3ea80000", + "bef00000", + "3f8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "bf860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "bf8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "3f820000", + "3f580000", + "be200000", + "bf800000", + "3faa0000", + "bf4c0000", + "3ed80000", + "3e500000", + "3e100000", + "3e700000", + "bef80000", + "3f640000", + "3fba0000", + "bf580000", + "3d000000", + "bf880000", + "bf440000", + "3f140000", + "bf700000", + "3f380000", + "3ef80000", + "3fbe0000", + "3f180000", + "be100000", + "bf380000", + "3f920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "3f680000", + "bec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "be600000", + "bfc00000", + "bd400000", + "bfba0000", + "3ea00000", + "3f480000", + "3fa60000", + "3f040000", + "bde00000", + "bf140000", + "3f640000", + "bf860000", + "bf860000", + "bf640000", + "bf140000", + "3de00000", + "3f040000", + "3fa60000", + "bf480000", + "3ea00000", + "3fba0000", + "bd400000", + "bfc00000", + "3e600000", + "bf6c0000", + "bf8e0000", + "3e900000", + "bec80000", + "bf680000", + "bfa20000", + "3fbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "bf380000", + "3e100000", + "3f180000", + "3fbe0000", + "bef80000", + "3f380000", + "3f700000", + "3f140000", + "bf440000", + "3f880000", + "3d000000", + "3f580000", + "3fba0000", + "3f640000", + "3ef80000", + "3e700000", + "be100000", + "3e500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "3f800000", + "be200000", + "3f580000", + "bf820000", + "3e980000", + "3fa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "3f1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "3f680000", + "bf6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "bea80000", + "3fa40000", + "bf200000", + "3f240000", + "bf740000", + "bf200000", + "bf280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "3f9a0000", + "3fa80000", + "3f780000", + "3f480000", + "3f400000", + "bf600000", + "3f940000", + "bfb60000", + "bf540000", + "bda00000", + "bf540000", + "bf900000", + "3dc00000", + "3fbc0000", + "bc800000", + "3fac0000", + "3f000000", + "bf040000", + "bfb00000", + "3f700000", + "bec80000", + "3dc00000", + "be700000", + "bea00000", + "be700000", + "bdc00000", + "3ec80000", + "3f700000", + "bfb00000", + "bf040000", + "bf000000", + "bfac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "3f900000", + "3f540000", + "bda00000", + "bf540000", + "bfb60000", + "bf940000", + "3f600000", + "3f400000", + "3f480000", + "3f780000", + "bfa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "3f280000", + "3f200000", + "bf740000", + "3f240000", + "bf200000", + "bfa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "bf8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "3fac0000", + "3f9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "bec00000", + "bfa00000", + "3e980000", + "bf820000", + "3f580000", + "3e200000", + "bf800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "be500000", + "3e100000", + "3e700000", + "3ef80000", + "3f640000", + "bfba0000", + "bf580000", + "3d000000", + "3f880000", + "bf440000", + "bf140000", + "bf700000", + "3f380000", + "bef80000", + "3fbe0000", + "bf180000", + "be100000", + "bf380000", + "bf920000", + "bfb40000", + "bfc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "bec80000", + "be900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "3d400000", + "bfba0000", + "3ea00000", + "bf480000", + "3fa60000", + "bf040000", + "bde00000", + "bf140000", + "bf640000", + "bf860000", + "3f860000", + "bf640000", + "bf140000", + "bde00000", + "3f040000", + "bfa60000", + "bf480000", + "3ea00000", + "bfba0000", + "bd400000", + "3fc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "3ec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "3fb40000", + "bf920000", + "bf380000", + "be100000", + "3f180000", + "bfbe0000", + "bef80000", + "3f380000", + "bf700000", + "3f140000", + "3f440000", + "3f880000", + "3d000000", + "bf580000", + "3fba0000", + "bf640000", + "3ef80000", + "3e700000", + "3e100000", + "3e500000", + "bed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "be200000", + "bf580000", + "bf820000", + "3e980000", + "bfa00000", + "3ec00000", + "3f5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "bfac0000", + "3f860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "bfb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "3f200000", + "3f240000", + "bf740000", + "3f200000", + "bf280000", + "bf9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "bf780000", + "3f480000", + "3f400000", + "3f600000", + "3f940000", + "3fb60000", + "bf540000", + "bda00000", + "3f540000", + "bf900000", + "bdc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "3f000000", + "3f040000", + "bfb00000", + "3f700000", + "3ec80000", + "3dc00000", + "3e700000", + "bea00000", + "be700000", + "3dc00000", + "3ec80000", + "bf700000", + "bfb00000", + "bf040000", + "3f000000", + "bfac0000", + "3c800000", + "3fbc0000", + "3dc00000", + "bf900000", + "3f540000", + "3da00000", + "bf540000", + "bfb60000", + "3f940000", + "3f600000", + "bf400000", + "3f480000", + "3f780000", + "3fa80000", + "bf9a0000", + "3f0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "3f200000", + "3f740000", + "3f240000", + "bf200000", + "3fa40000", + "3ea80000", + "3ef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "bf680000", + "3f860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "be200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "be980000", + "bf820000", + "3f580000", + "be200000", + "bf800000", + "bfaa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "3e100000", + "be700000", + "3ef80000", + "3f640000", + "3fba0000", + "bf580000", + "bd000000", + "3f880000", + "bf440000", + "3f140000", + "bf700000", + "bf380000", + "bef80000", + "3fbe0000", + "3f180000", + "be100000", + "3f380000", + "bf920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "3fa20000", + "bf680000", + "bec80000", + "3e900000", + "3f8e0000", + "3f6c0000", + "3e600000", + "bfc00000", + "bd400000", + "bfba0000", + "bea00000", + "bf480000", + "3fa60000", + "3f040000", + "bde00000", + "3f140000", + "bf640000", + "bf860000", + "bf860000", + "bf640000", + "3f140000", + "bde00000", + "3f040000", + "3fa60000", + "bf480000", + "bea00000", + "bfba0000", + "bd400000", + "bfc00000", + "3e600000", + "3f6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "bf680000", + "3fa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "3f380000", + "be100000", + "3f180000", + "3fbe0000", + "bef80000", + "bf380000", + "bf700000", + "3f140000", + "bf440000", + "3f880000", + "bd000000", + "bf580000", + "3fba0000", + "3f640000", + "3ef80000", + "be700000", + "3e100000", + "3e500000", + "3ed80000", + "3f4c0000", + "bfaa0000", + "bf800000", + "be200000", + "3f580000", + "bf820000", + "be980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "be200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "bf680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "3ef00000", + "3ea80000", + "3fa40000", + "bf200000", + "3f240000", + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000" + ], + "dtype": "float32", + "shape": [ + 1024 + ] + }, + "rows": { + "bits": [ + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "bfac0000", + "be100000", + "bfbc0000", + "3ec00000", + "bf240000", + "3fc00000", + "3f500000", + "3e880000", + "be000000", + "beb80000", + "3ee00000", + "beb80000", + "be000000", + "3e880000", + "3f500000", + "3fc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "be100000", + "bfac0000", + "bd000000", + "bfa00000", + "3f340000", + "be500000", + "3f740000", + "3fbc0000", + "3f840000", + "3f400000", + "3f200000", + "bf280000", + "3f580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "be100000", + "3f8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "bf040000", + "bf400000", + "3f940000", + "3e500000", + "bf180000", + "3f9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "3f480000", + "bf6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "bf740000", + "bf7c0000", + "3e800000", + "bfb00000", + "3e300000", + "3f920000", + "3f380000", + "be900000", + "bf900000", + "3f9a0000", + "bf2c0000", + "3e980000", + "3da00000", + "3c800000", + "3de00000", + "beb80000", + "3f440000", + "3faa0000", + "bf780000", + "bdc00000", + "bf700000", + "bf640000", + "3ee80000", + "bf880000", + "3f180000", + "3f1c0000", + "3fae0000", + "3ef00000", + "be880000", + "bf580000", + "3fa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "3f840000", + "bf040000", + "3e200000", + "3f7c0000", + "bf860000", + "bdc00000", + "3fb20000", + "be300000", + "3fb80000", + "3e400000", + "3f680000", + "3f960000", + "3ec80000", + "be700000", + "bf340000", + "3f820000", + "bf960000", + "bf960000", + "bf820000", + "bf340000", + "3e700000", + "3ec80000", + "3f960000", + "bf680000", + "3e400000", + "bfb80000", + "be300000", + "3fb20000", + "3dc00000", + "bf860000", + "bf7c0000", + "3e200000", + "bf040000", + "bf840000", + "bfb20000", + "bfb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "3e880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "3f880000", + "3ee80000", + "bf640000", + "3f700000", + "bdc00000", + "3f780000", + "3faa0000", + "3f440000", + "3eb80000", + "3de00000", + "bc800000", + "3da00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "3f900000", + "be900000", + "3f380000", + "bf920000", + "3e300000", + "3fb00000", + "3e800000", + "bf7c0000", + "3f740000", + "3d000000", + "3f3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "bf4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "bf180000", + "be500000", + "3f940000", + "bf400000", + "3f040000", + "bf8a0000", + "bf000000", + "bf480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "3faa0000", + "3f980000", + "3f580000", + "3f280000", + "3f200000", + "bf400000", + "3f840000", + "3fbc0000", + "bf740000", + "be500000", + "bf340000", + "bfa00000", + "bd000000", + "3fac0000", + "be100000", + "3fbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "3f500000", + "be880000", + "be000000", + "beb80000", + "bee00000", + "beb80000", + "3e000000", + "3e880000", + "3f500000", + "bfc00000", + "bf240000", + "bec00000", + "bfbc0000", + "be100000", + "3fac0000", + "bd000000", + "3fa00000", + "3f340000", + "be500000", + "bf740000", + "3fbc0000", + "bf840000", + "3f400000", + "3f200000", + "3f280000", + "3f580000", + "bf980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "3f480000", + "3f000000", + "bf8a0000", + "3f040000", + "bf400000", + "bf940000", + "3e500000", + "bf180000", + "bf9e0000", + "3fa60000", + "bf780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "3fac0000", + "bf3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "be800000", + "bfb00000", + "3e300000", + "bf920000", + "3f380000", + "3e900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "bda00000", + "3c800000", + "3de00000", + "3eb80000", + "3f440000", + "bfaa0000", + "bf780000", + "bdc00000", + "3f700000", + "bf640000", + "bee80000", + "bf880000", + "3f180000", + "bf1c0000", + "3fae0000", + "bef00000", + "be880000", + "bf580000", + "bfa20000", + "3fbe0000", + "bfb00000", + "3fb60000", + "bfb20000", + "bf840000", + "bf040000", + "be200000", + "3f7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "3e300000", + "3fb80000", + "3e400000", + "bf680000", + "3f960000", + "bec80000", + "be700000", + "bf340000", + "bf820000", + "bf960000", + "3f960000", + "bf820000", + "bf340000", + "be700000", + "3ec80000", + "bf960000", + "bf680000", + "3e400000", + "3fb80000", + "be300000", + "bfb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "3e200000", + "3f040000", + "bf840000", + "bfb20000", + "3fb60000", + "3fb00000", + "bfbe0000", + "bfa20000", + "bf580000", + "be880000", + "3ef00000", + "bfae0000", + "bf1c0000", + "3f180000", + "bf880000", + "3ee80000", + "3f640000", + "3f700000", + "bdc00000", + "bf780000", + "3faa0000", + "bf440000", + "3eb80000", + "3de00000", + "3c800000", + "3da00000", + "be980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "be900000", + "bf380000", + "bf920000", + "3e300000", + "bfb00000", + "3e800000", + "3f7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "bfac0000", + "bf9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "bfa60000", + "bf9e0000", + "bf180000", + "3e500000", + "3f940000", + "3f400000", + "3f040000", + "bf8a0000", + "3f000000", + "bf480000", + "bf8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "bf580000", + "3f280000", + "3f200000", + "3f400000", + "3f840000", + "bfbc0000", + "bf740000", + "be500000", + "3f340000", + "bfa00000", + "3d000000", + "3fac0000", + "be100000", + "bfbc0000", + "3ec00000", + "3f240000", + "bfc00000", + "3f500000", + "3e880000", + "be000000", + "3eb80000", + "bee00000", + "beb80000", + "be000000", + "3e880000", + "bf500000", + "bfc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "3e100000", + "3fac0000", + "bd000000", + "bfa00000", + "3f340000", + "3e500000", + "bf740000", + "3fbc0000", + "3f840000", + "3f400000", + "bf200000", + "3f280000", + "3f580000", + "3f980000", + "bfaa0000", + "3f2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "3f000000", + "3f8a0000", + "3f040000", + "bf400000", + "3f940000", + "3e500000", + "3f180000", + "bf9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "bf480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "bd000000", + "3f740000", + "bf7c0000", + "3e800000", + "bfb00000", + "be300000", + "bf920000", + "3f380000", + "be900000", + "bf900000", + "bf9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "3c800000", + "bde00000", + "3eb80000", + "3f440000", + "3faa0000", + "bf780000", + "3dc00000", + "3f700000", + "bf640000", + "3ee80000", + "bf880000", + "bf180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "be880000", + "3f580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "3fb20000", + "bf840000", + "bf040000", + "3e200000", + "3f7c0000", + "3f860000", + "3dc00000", + "3fb20000", + "be300000", + "3fb80000", + "be400000", + "bf680000", + "3f960000", + "3ec80000", + "be700000", + "3f340000", + "bf820000", + "bf960000", + "bf960000", + "bf820000", + "3f340000", + "be700000", + "3ec80000", + "3f960000", + "bf680000", + "be400000", + "3fb80000", + "be300000", + "3fb20000", + "3dc00000", + "3f860000", + "3f7c0000", + "3e200000", + "bf040000", + "bf840000", + "3fb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "3f580000", + "be880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "bf180000", + "bf880000", + "3ee80000", + "bf640000", + "3f700000", + "3dc00000", + "bf780000", + "3faa0000", + "3f440000", + "3eb80000", + "bde00000", + "3c800000", + "3da00000", + "3e980000", + "3f2c0000", + "bf9a0000", + "bf900000", + "be900000", + "3f380000", + "bf920000", + "be300000", + "bfb00000", + "3e800000", + "bf7c0000", + "3f740000", + "bd000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "bf480000", + "3f4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "3f180000", + "3e500000", + "3f940000", + "bf400000", + "3f040000", + "3f8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "3e100000", + "3f2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "3f280000", + "bf200000", + "3f400000", + "3f840000", + "3fbc0000", + "bf740000", + "3e500000", + "3f340000", + "bfa00000", + "bd000000", + "3fac0000", + "3e100000", + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000", + "3f340000", + "bf040000", + "3fb80000", + "3f080000", + "3e600000", + "bf500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "bfb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "3d800000", + "bf600000", + "bf960000", + "bd400000", + "3f9e0000", + "beb00000", + "bfa00000", + "bc800000", + "bf900000", + "3f700000", + "3e100000", + "3f000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "3fae0000", + "bf880000", + "bf1c0000", + "3e000000", + "3f440000", + "3faa0000", + "be800000", + "3f7c0000", + "bf240000", + "3f680000", + "3ed00000", + "3fba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "bfb40000", + "3f840000", + "3f500000", + "3f400000", + "3f580000", + "bf8c0000", + "3fc00000", + "bf740000", + "be700000", + "3f240000", + "3fac0000", + "be200000", + "3f980000", + "bea80000", + "3faa0000", + "be000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "3f080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "3e980000", + "3e600000", + "3f640000", + "bfa60000", + "bea00000", + "bf540000", + "bf640000", + "3f100000", + "bf580000", + "3f6c0000", + "3e300000", + "bf8e0000", + "3f900000", + "3f000000", + "3d000000", + "3e900000", + "bee00000", + "bee00000", + "be900000", + "3d000000", + "bf000000", + "3f900000", + "bf8e0000", + "be300000", + "3f6c0000", + "3f580000", + "3f100000", + "bf640000", + "3f540000", + "bea00000", + "3fa60000", + "3f640000", + "3e600000", + "be980000", + "bf280000", + "3f5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "bde00000", + "bef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "3ea80000", + "3f980000", + "be200000", + "bfac0000", + "3f240000", + "3e700000", + "bf740000", + "3fc00000", + "3f8c0000", + "3f580000", + "bf400000", + "3f500000", + "3f840000", + "3fb40000", + "bf8a0000", + "3ec80000", + "3ee80000", + "3fba0000", + "bed00000", + "3f680000", + "3f240000", + "3f7c0000", + "be800000", + "bfaa0000", + "3f440000", + "be000000", + "bf1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "3fbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "3e100000", + "bf700000", + "bf900000", + "bc800000", + "3fa00000", + "beb00000", + "bf9e0000", + "bd400000", + "bf960000", + "3f600000", + "3d800000", + "3f180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "bfbe0000", + "bfa00000", + "bf500000", + "be600000", + "3f080000", + "bfb80000", + "bf040000", + "3f340000", + "bf700000", + "3f180000", + "3f3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "bf800000", + "3f1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "bf1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "3dc00000", + "bf8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "3f340000", + "3f040000", + "3fb80000", + "3f080000", + "be600000", + "bf500000", + "3fa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "3f8c0000", + "bf180000", + "3d800000", + "3f600000", + "bf960000", + "3d400000", + "3f9e0000", + "beb00000", + "3fa00000", + "bc800000", + "3f900000", + "3f700000", + "3e100000", + "bf000000", + "bf7c0000", + "3fa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "3f1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "be800000", + "bf7c0000", + "bf240000", + "3f680000", + "bed00000", + "3fba0000", + "bee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "bf500000", + "3f400000", + "3f580000", + "3f8c0000", + "3fc00000", + "3f740000", + "be700000", + "3f240000", + "bfac0000", + "be200000", + "bf980000", + "bea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "bf9a0000", + "3ef00000", + "bde00000", + "bf080000", + "bf4c0000", + "3f680000", + "bf5c0000", + "bf280000", + "be980000", + "3e600000", + "bf640000", + "bfa60000", + "bea00000", + "3f540000", + "bf640000", + "bf100000", + "bf580000", + "3f6c0000", + "be300000", + "bf8e0000", + "bf900000", + "3f000000", + "3d000000", + "be900000", + "bee00000", + "3ee00000", + "be900000", + "3d000000", + "3f000000", + "3f900000", + "3f8e0000", + "be300000", + "3f6c0000", + "bf580000", + "3f100000", + "3f640000", + "3f540000", + "bea00000", + "bfa60000", + "3f640000", + "be600000", + "be980000", + "bf280000", + "bf5c0000", + "bf680000", + "3f4c0000", + "bf080000", + "bde00000", + "3ef00000", + "3f9a0000", + "3f6c0000", + "3e000000", + "3faa0000", + "bea80000", + "3f980000", + "3e200000", + "bfac0000", + "3f240000", + "be700000", + "bf740000", + "bfc00000", + "3f8c0000", + "3f580000", + "3f400000", + "3f500000", + "bf840000", + "3fb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "bfba0000", + "bed00000", + "3f680000", + "bf240000", + "3f7c0000", + "3e800000", + "bfaa0000", + "3f440000", + "3e000000", + "bf1c0000", + "3f880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "3f7c0000", + "bf000000", + "3e100000", + "3f700000", + "bf900000", + "3c800000", + "3fa00000", + "beb00000", + "3f9e0000", + "bd400000", + "3f960000", + "3f600000", + "3d800000", + "bf180000", + "bf8c0000", + "3fb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "3f500000", + "be600000", + "3f080000", + "3fb80000", + "bf040000", + "bf340000", + "bf700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "bdc00000", + "bf440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "bec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "3f800000", + "3fbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "bf180000", + "bf700000", + "3f340000", + "bf040000", + "3fb80000", + "bf080000", + "be600000", + "bf500000", + "bfa00000", + "3fbe0000", + "bfae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "bd800000", + "3f600000", + "bf960000", + "bd400000", + "3f9e0000", + "3eb00000", + "3fa00000", + "bc800000", + "bf900000", + "3f700000", + "be100000", + "bf000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "3fc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "3e000000", + "bf440000", + "bfaa0000", + "be800000", + "3f7c0000", + "bf240000", + "bf680000", + "bed00000", + "3fba0000", + "3ee80000", + "bec80000", + "3f8a0000", + "3fb40000", + "3f840000", + "3f500000", + "3f400000", + "bf580000", + "3f8c0000", + "3fc00000", + "bf740000", + "be700000", + "bf240000", + "bfac0000", + "be200000", + "3f980000", + "bea80000", + "bfaa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "3de00000", + "bf080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "3f280000", + "be980000", + "3e600000", + "3f640000", + "bfa60000", + "3ea00000", + "3f540000", + "bf640000", + "3f100000", + "bf580000", + "bf6c0000", + "be300000", + "bf8e0000", + "3f900000", + "3f000000", + "bd000000", + "be900000", + "bee00000", + "bee00000", + "be900000", + "bd000000", + "3f000000", + "3f900000", + "bf8e0000", + "be300000", + "bf6c0000", + "bf580000", + "3f100000", + "bf640000", + "3f540000", + "3ea00000", + "bfa60000", + "3f640000", + "3e600000", + "be980000", + "3f280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "3de00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "bfaa0000", + "bea80000", + "3f980000", + "be200000", + "bfac0000", + "bf240000", + "be700000", + "bf740000", + "3fc00000", + "3f8c0000", + "bf580000", + "3f400000", + "3f500000", + "3f840000", + "3fb40000", + "3f8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "bed00000", + "bf680000", + "bf240000", + "3f7c0000", + "be800000", + "bfaa0000", + "bf440000", + "3e000000", + "bf1c0000", + "bf880000", + "bfae0000", + "3fc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "be100000", + "3f700000", + "bf900000", + "bc800000", + "3fa00000", + "3eb00000", + "3f9e0000", + "bd400000", + "bf960000", + "3f600000", + "bd800000", + "bf180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "bfae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "be600000", + "bf080000", + "3fb80000", + "bf040000", + "3f340000", + "bf700000", + "bf180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "3fbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "3e980000", + "bec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "bdc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "bf340000", + "bf040000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000" + ], + "dtype": "float32", + "shape": [ + 2, + 1024 + ] + } + }, + "name": "ordered-reduction-d1024" + }, + { + "dimension": 4096, + "expected": { + "cosine_many": { + "bits": [ + "3a083b49", + "3c73a78d" + ], + "dtype": "float32", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "3fdb4800", + "42462380" + ], + "dtype": "float32", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "bf200000", + "bf240000", + "bf740000", + "3f200000", + "bf280000", + "3f9e0000", + "be880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "bf480000", + "3f400000", + "3f600000", + "3f940000", + "bfb60000", + "3f540000", + "bda00000", + "3f540000", + "bf900000", + "3dc00000", + "bfbc0000", + "bc800000", + "bfac0000", + "3f000000", + "bf040000", + "3fb00000", + "3f700000", + "3ec80000", + "3dc00000", + "be700000", + "3ea00000", + "be700000", + "3dc00000", + "3ec80000", + "3f700000", + "3fb00000", + "bf040000", + "3f000000", + "bfac0000", + "bc800000", + "bfbc0000", + "3dc00000", + "bf900000", + "3f540000", + "bda00000", + "3f540000", + "bfb60000", + "3f940000", + "3f600000", + "3f400000", + "bf480000", + "3f780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "be880000", + "3f9e0000", + "bf280000", + "3f200000", + "bf740000", + "bf240000", + "bf200000", + "3fa40000", + "3ea80000", + "bef00000", + "3f8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "bf860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "bf8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "3f820000", + "3f580000", + "be200000", + "bf800000", + "3faa0000", + "bf4c0000", + "3ed80000", + "3e500000", + "3e100000", + "3e700000", + "bef80000", + "3f640000", + "3fba0000", + "bf580000", + "3d000000", + "bf880000", + "bf440000", + "3f140000", + "bf700000", + "3f380000", + "3ef80000", + "3fbe0000", + "3f180000", + "be100000", + "bf380000", + "3f920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "3f680000", + "bec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "be600000", + "bfc00000", + "bd400000", + "bfba0000", + "3ea00000", + "3f480000", + "3fa60000", + "3f040000", + "bde00000", + "bf140000", + "3f640000", + "bf860000", + "bf860000", + "bf640000", + "bf140000", + "3de00000", + "3f040000", + "3fa60000", + "bf480000", + "3ea00000", + "3fba0000", + "bd400000", + "bfc00000", + "3e600000", + "bf6c0000", + "bf8e0000", + "3e900000", + "bec80000", + "bf680000", + "bfa20000", + "3fbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "bf380000", + "3e100000", + "3f180000", + "3fbe0000", + "bef80000", + "3f380000", + "3f700000", + "3f140000", + "bf440000", + "3f880000", + "3d000000", + "3f580000", + "3fba0000", + "3f640000", + "3ef80000", + "3e700000", + "be100000", + "3e500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "3f800000", + "be200000", + "3f580000", + "bf820000", + "3e980000", + "3fa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "3f1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "3f680000", + "bf6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "bea80000", + "3fa40000", + "bf200000", + "3f240000", + "bf740000", + "bf200000", + "bf280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "3f9a0000", + "3fa80000", + "3f780000", + "3f480000", + "3f400000", + "bf600000", + "3f940000", + "bfb60000", + "bf540000", + "bda00000", + "bf540000", + "bf900000", + "3dc00000", + "3fbc0000", + "bc800000", + "3fac0000", + "3f000000", + "bf040000", + "bfb00000", + "3f700000", + "bec80000", + "3dc00000", + "be700000", + "bea00000", + "be700000", + "bdc00000", + "3ec80000", + "3f700000", + "bfb00000", + "bf040000", + "bf000000", + "bfac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "3f900000", + "3f540000", + "bda00000", + "bf540000", + "bfb60000", + "bf940000", + "3f600000", + "3f400000", + "3f480000", + "3f780000", + "bfa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "3f280000", + "3f200000", + "bf740000", + "3f240000", + "bf200000", + "bfa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "bf8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "3fac0000", + "3f9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "bec00000", + "bfa00000", + "3e980000", + "bf820000", + "3f580000", + "3e200000", + "bf800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "be500000", + "3e100000", + "3e700000", + "3ef80000", + "3f640000", + "bfba0000", + "bf580000", + "3d000000", + "3f880000", + "bf440000", + "bf140000", + "bf700000", + "3f380000", + "bef80000", + "3fbe0000", + "bf180000", + "be100000", + "bf380000", + "bf920000", + "bfb40000", + "bfc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "bec80000", + "be900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "3d400000", + "bfba0000", + "3ea00000", + "bf480000", + "3fa60000", + "bf040000", + "bde00000", + "bf140000", + "bf640000", + "bf860000", + "3f860000", + "bf640000", + "bf140000", + "bde00000", + "3f040000", + "bfa60000", + "bf480000", + "3ea00000", + "bfba0000", + "bd400000", + "3fc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "3ec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "3fb40000", + "bf920000", + "bf380000", + "be100000", + "3f180000", + "bfbe0000", + "bef80000", + "3f380000", + "bf700000", + "3f140000", + "3f440000", + "3f880000", + "3d000000", + "bf580000", + "3fba0000", + "bf640000", + "3ef80000", + "3e700000", + "3e100000", + "3e500000", + "bed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "be200000", + "bf580000", + "bf820000", + "3e980000", + "bfa00000", + "3ec00000", + "3f5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "bfac0000", + "3f860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "bfb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "3f200000", + "3f240000", + "bf740000", + "3f200000", + "bf280000", + "bf9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "bf780000", + "3f480000", + "3f400000", + "3f600000", + "3f940000", + "3fb60000", + "bf540000", + "bda00000", + "3f540000", + "bf900000", + "bdc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "3f000000", + "3f040000", + "bfb00000", + "3f700000", + "3ec80000", + "3dc00000", + "3e700000", + "bea00000", + "be700000", + "3dc00000", + "3ec80000", + "bf700000", + "bfb00000", + "bf040000", + "3f000000", + "bfac0000", + "3c800000", + "3fbc0000", + "3dc00000", + "bf900000", + "3f540000", + "3da00000", + "bf540000", + "bfb60000", + "3f940000", + "3f600000", + "bf400000", + "3f480000", + "3f780000", + "3fa80000", + "bf9a0000", + "3f0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "3f200000", + "3f740000", + "3f240000", + "bf200000", + "3fa40000", + "3ea80000", + "3ef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "bf680000", + "3f860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "be200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "be980000", + "bf820000", + "3f580000", + "be200000", + "bf800000", + "bfaa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "3e100000", + "be700000", + "3ef80000", + "3f640000", + "3fba0000", + "bf580000", + "bd000000", + "3f880000", + "bf440000", + "3f140000", + "bf700000", + "bf380000", + "bef80000", + "3fbe0000", + "3f180000", + "be100000", + "3f380000", + "bf920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "3fa20000", + "bf680000", + "bec80000", + "3e900000", + "3f8e0000", + "3f6c0000", + "3e600000", + "bfc00000", + "bd400000", + "bfba0000", + "bea00000", + "bf480000", + "3fa60000", + "3f040000", + "bde00000", + "3f140000", + "bf640000", + "bf860000", + "bf860000", + "bf640000", + "3f140000", + "bde00000", + "3f040000", + "3fa60000", + "bf480000", + "bea00000", + "bfba0000", + "bd400000", + "bfc00000", + "3e600000", + "3f6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "bf680000", + "3fa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "3f380000", + "be100000", + "3f180000", + "3fbe0000", + "bef80000", + "bf380000", + "bf700000", + "3f140000", + "bf440000", + "3f880000", + "bd000000", + "bf580000", + "3fba0000", + "3f640000", + "3ef80000", + "be700000", + "3e100000", + "3e500000", + "3ed80000", + "3f4c0000", + "bfaa0000", + "bf800000", + "be200000", + "3f580000", + "bf820000", + "be980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "be200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "bf680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "3ef00000", + "3ea80000", + "3fa40000", + "bf200000", + "3f240000", + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "bf200000", + "bf240000", + "bf740000", + "3f200000", + "bf280000", + "3f9e0000", + "be880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "bf480000", + "3f400000", + "3f600000", + "3f940000", + "bfb60000", + "3f540000", + "bda00000", + "3f540000", + "bf900000", + "3dc00000", + "bfbc0000", + "bc800000", + "bfac0000", + "3f000000", + "bf040000", + "3fb00000", + "3f700000", + "3ec80000", + "3dc00000", + "be700000", + "3ea00000", + "be700000", + "3dc00000", + "3ec80000", + "3f700000", + "3fb00000", + "bf040000", + "3f000000", + "bfac0000", + "bc800000", + "bfbc0000", + "3dc00000", + "bf900000", + "3f540000", + "bda00000", + "3f540000", + "bfb60000", + "3f940000", + "3f600000", + "3f400000", + "bf480000", + "3f780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "be880000", + "3f9e0000", + "bf280000", + "3f200000", + "bf740000", + "bf240000", + "bf200000", + "3fa40000", + "3ea80000", + "bef00000", + "3f8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "bf860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "bf8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "3f820000", + "3f580000", + "be200000", + "bf800000", + "3faa0000", + "bf4c0000", + "3ed80000", + "3e500000", + "3e100000", + "3e700000", + "bef80000", + "3f640000", + "3fba0000", + "bf580000", + "3d000000", + "bf880000", + "bf440000", + "3f140000", + "bf700000", + "3f380000", + "3ef80000", + "3fbe0000", + "3f180000", + "be100000", + "bf380000", + "3f920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "3f680000", + "bec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "be600000", + "bfc00000", + "bd400000", + "bfba0000", + "3ea00000", + "3f480000", + "3fa60000", + "3f040000", + "bde00000", + "bf140000", + "3f640000", + "bf860000", + "bf860000", + "bf640000", + "bf140000", + "3de00000", + "3f040000", + "3fa60000", + "bf480000", + "3ea00000", + "3fba0000", + "bd400000", + "bfc00000", + "3e600000", + "bf6c0000", + "bf8e0000", + "3e900000", + "bec80000", + "bf680000", + "bfa20000", + "3fbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "bf380000", + "3e100000", + "3f180000", + "3fbe0000", + "bef80000", + "3f380000", + "3f700000", + "3f140000", + "bf440000", + "3f880000", + "3d000000", + "3f580000", + "3fba0000", + "3f640000", + "3ef80000", + "3e700000", + "be100000", + "3e500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "3f800000", + "be200000", + "3f580000", + "bf820000", + "3e980000", + "3fa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "3f1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "3f680000", + "bf6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "bea80000", + "3fa40000", + "bf200000", + "3f240000", + "bf740000", + "bf200000", + "bf280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "3f9a0000", + "3fa80000", + "3f780000", + "3f480000", + "3f400000", + "bf600000", + "3f940000", + "bfb60000", + "bf540000", + "bda00000", + "bf540000", + "bf900000", + "3dc00000", + "3fbc0000", + "bc800000", + "3fac0000", + "3f000000", + "bf040000", + "bfb00000", + "3f700000", + "bec80000", + "3dc00000", + "be700000", + "bea00000", + "be700000", + "bdc00000", + "3ec80000", + "3f700000", + "bfb00000", + "bf040000", + "bf000000", + "bfac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "3f900000", + "3f540000", + "bda00000", + "bf540000", + "bfb60000", + "bf940000", + "3f600000", + "3f400000", + "3f480000", + "3f780000", + "bfa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "3f280000", + "3f200000", + "bf740000", + "3f240000", + "bf200000", + "bfa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "bf8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "3fac0000", + "3f9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "bec00000", + "bfa00000", + "3e980000", + "bf820000", + "3f580000", + "3e200000", + "bf800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "be500000", + "3e100000", + "3e700000", + "3ef80000", + "3f640000", + "bfba0000", + "bf580000", + "3d000000", + "3f880000", + "bf440000", + "bf140000", + "bf700000", + "3f380000", + "bef80000", + "3fbe0000", + "bf180000", + "be100000", + "bf380000", + "bf920000", + "bfb40000", + "bfc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "bec80000", + "be900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "3d400000", + "bfba0000", + "3ea00000", + "bf480000", + "3fa60000", + "bf040000", + "bde00000", + "bf140000", + "bf640000", + "bf860000", + "3f860000", + "bf640000", + "bf140000", + "bde00000", + "3f040000", + "bfa60000", + "bf480000", + "3ea00000", + "bfba0000", + "bd400000", + "3fc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "3ec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "3fb40000", + "bf920000", + "bf380000", + "be100000", + "3f180000", + "bfbe0000", + "bef80000", + "3f380000", + "bf700000", + "3f140000", + "3f440000", + "3f880000", + "3d000000", + "bf580000", + "3fba0000", + "bf640000", + "3ef80000", + "3e700000", + "3e100000", + "3e500000", + "bed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "be200000", + "bf580000", + "bf820000", + "3e980000", + "bfa00000", + "3ec00000", + "3f5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "bfac0000", + "3f860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "bfb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "3f200000", + "3f240000", + "bf740000", + "3f200000", + "bf280000", + "bf9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "bf780000", + "3f480000", + "3f400000", + "3f600000", + "3f940000", + "3fb60000", + "bf540000", + "bda00000", + "3f540000", + "bf900000", + "bdc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "3f000000", + "3f040000", + "bfb00000", + "3f700000", + "3ec80000", + "3dc00000", + "3e700000", + "bea00000", + "be700000", + "3dc00000", + "3ec80000", + "bf700000", + "bfb00000", + "bf040000", + "3f000000", + "bfac0000", + "3c800000", + "3fbc0000", + "3dc00000", + "bf900000", + "3f540000", + "3da00000", + "bf540000", + "bfb60000", + "3f940000", + "3f600000", + "bf400000", + "3f480000", + "3f780000", + "3fa80000", + "bf9a0000", + "3f0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "3f200000", + "3f740000", + "3f240000", + "bf200000", + "3fa40000", + "3ea80000", + "3ef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "bf680000", + "3f860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "be200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "be980000", + "bf820000", + "3f580000", + "be200000", + "bf800000", + "bfaa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "3e100000", + "be700000", + "3ef80000", + "3f640000", + "3fba0000", + "bf580000", + "bd000000", + "3f880000", + "bf440000", + "3f140000", + "bf700000", + "bf380000", + "bef80000", + "3fbe0000", + "3f180000", + "be100000", + "3f380000", + "bf920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "3fa20000", + "bf680000", + "bec80000", + "3e900000", + "3f8e0000", + "3f6c0000", + "3e600000", + "bfc00000", + "bd400000", + "bfba0000", + "bea00000", + "bf480000", + "3fa60000", + "3f040000", + "bde00000", + "3f140000", + "bf640000", + "bf860000", + "bf860000", + "bf640000", + "3f140000", + "bde00000", + "3f040000", + "3fa60000", + "bf480000", + "bea00000", + "bfba0000", + "bd400000", + "bfc00000", + "3e600000", + "3f6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "bf680000", + "3fa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "3f380000", + "be100000", + "3f180000", + "3fbe0000", + "bef80000", + "bf380000", + "bf700000", + "3f140000", + "bf440000", + "3f880000", + "bd000000", + "bf580000", + "3fba0000", + "3f640000", + "3ef80000", + "be700000", + "3e100000", + "3e500000", + "3ed80000", + "3f4c0000", + "bfaa0000", + "bf800000", + "be200000", + "3f580000", + "bf820000", + "be980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "be200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "bf680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "3ef00000", + "3ea80000", + "3fa40000", + "bf200000", + "3f240000", + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "bf200000", + "bf240000", + "bf740000", + "3f200000", + "bf280000", + "3f9e0000", + "be880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "bf480000", + "3f400000", + "3f600000", + "3f940000", + "bfb60000", + "3f540000", + "bda00000", + "3f540000", + "bf900000", + "3dc00000", + "bfbc0000", + "bc800000", + "bfac0000", + "3f000000", + "bf040000", + "3fb00000", + "3f700000", + "3ec80000", + "3dc00000", + "be700000", + "3ea00000", + "be700000", + "3dc00000", + "3ec80000", + "3f700000", + "3fb00000", + "bf040000", + "3f000000", + "bfac0000", + "bc800000", + "bfbc0000", + "3dc00000", + "bf900000", + "3f540000", + "bda00000", + "3f540000", + "bfb60000", + "3f940000", + "3f600000", + "3f400000", + "bf480000", + "3f780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "be880000", + "3f9e0000", + "bf280000", + "3f200000", + "bf740000", + "bf240000", + "bf200000", + "3fa40000", + "3ea80000", + "bef00000", + "3f8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "bf860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "bf8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "3f820000", + "3f580000", + "be200000", + "bf800000", + "3faa0000", + "bf4c0000", + "3ed80000", + "3e500000", + "3e100000", + "3e700000", + "bef80000", + "3f640000", + "3fba0000", + "bf580000", + "3d000000", + "bf880000", + "bf440000", + "3f140000", + "bf700000", + "3f380000", + "3ef80000", + "3fbe0000", + "3f180000", + "be100000", + "bf380000", + "3f920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "3f680000", + "bec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "be600000", + "bfc00000", + "bd400000", + "bfba0000", + "3ea00000", + "3f480000", + "3fa60000", + "3f040000", + "bde00000", + "bf140000", + "3f640000", + "bf860000", + "bf860000", + "bf640000", + "bf140000", + "3de00000", + "3f040000", + "3fa60000", + "bf480000", + "3ea00000", + "3fba0000", + "bd400000", + "bfc00000", + "3e600000", + "bf6c0000", + "bf8e0000", + "3e900000", + "bec80000", + "bf680000", + "bfa20000", + "3fbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "bf380000", + "3e100000", + "3f180000", + "3fbe0000", + "bef80000", + "3f380000", + "3f700000", + "3f140000", + "bf440000", + "3f880000", + "3d000000", + "3f580000", + "3fba0000", + "3f640000", + "3ef80000", + "3e700000", + "be100000", + "3e500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "3f800000", + "be200000", + "3f580000", + "bf820000", + "3e980000", + "3fa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "3f1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "3f680000", + "bf6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "bea80000", + "3fa40000", + "bf200000", + "3f240000", + "bf740000", + "bf200000", + "bf280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "3f9a0000", + "3fa80000", + "3f780000", + "3f480000", + "3f400000", + "bf600000", + "3f940000", + "bfb60000", + "bf540000", + "bda00000", + "bf540000", + "bf900000", + "3dc00000", + "3fbc0000", + "bc800000", + "3fac0000", + "3f000000", + "bf040000", + "bfb00000", + "3f700000", + "bec80000", + "3dc00000", + "be700000", + "bea00000", + "be700000", + "bdc00000", + "3ec80000", + "3f700000", + "bfb00000", + "bf040000", + "bf000000", + "bfac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "3f900000", + "3f540000", + "bda00000", + "bf540000", + "bfb60000", + "bf940000", + "3f600000", + "3f400000", + "3f480000", + "3f780000", + "bfa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "3f280000", + "3f200000", + "bf740000", + "3f240000", + "bf200000", + "bfa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "bf8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "3fac0000", + "3f9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "bec00000", + "bfa00000", + "3e980000", + "bf820000", + "3f580000", + "3e200000", + "bf800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "be500000", + "3e100000", + "3e700000", + "3ef80000", + "3f640000", + "bfba0000", + "bf580000", + "3d000000", + "3f880000", + "bf440000", + "bf140000", + "bf700000", + "3f380000", + "bef80000", + "3fbe0000", + "bf180000", + "be100000", + "bf380000", + "bf920000", + "bfb40000", + "bfc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "bec80000", + "be900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "3d400000", + "bfba0000", + "3ea00000", + "bf480000", + "3fa60000", + "bf040000", + "bde00000", + "bf140000", + "bf640000", + "bf860000", + "3f860000", + "bf640000", + "bf140000", + "bde00000", + "3f040000", + "bfa60000", + "bf480000", + "3ea00000", + "bfba0000", + "bd400000", + "3fc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "3ec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "3fb40000", + "bf920000", + "bf380000", + "be100000", + "3f180000", + "bfbe0000", + "bef80000", + "3f380000", + "bf700000", + "3f140000", + "3f440000", + "3f880000", + "3d000000", + "bf580000", + "3fba0000", + "bf640000", + "3ef80000", + "3e700000", + "3e100000", + "3e500000", + "bed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "be200000", + "bf580000", + "bf820000", + "3e980000", + "bfa00000", + "3ec00000", + "3f5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "bfac0000", + "3f860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "bfb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "3f200000", + "3f240000", + "bf740000", + "3f200000", + "bf280000", + "bf9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "bf780000", + "3f480000", + "3f400000", + "3f600000", + "3f940000", + "3fb60000", + "bf540000", + "bda00000", + "3f540000", + "bf900000", + "bdc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "3f000000", + "3f040000", + "bfb00000", + "3f700000", + "3ec80000", + "3dc00000", + "3e700000", + "bea00000", + "be700000", + "3dc00000", + "3ec80000", + "bf700000", + "bfb00000", + "bf040000", + "3f000000", + "bfac0000", + "3c800000", + "3fbc0000", + "3dc00000", + "bf900000", + "3f540000", + "3da00000", + "bf540000", + "bfb60000", + "3f940000", + "3f600000", + "bf400000", + "3f480000", + "3f780000", + "3fa80000", + "bf9a0000", + "3f0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "3f200000", + "3f740000", + "3f240000", + "bf200000", + "3fa40000", + "3ea80000", + "3ef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "bf680000", + "3f860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "be200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "be980000", + "bf820000", + "3f580000", + "be200000", + "bf800000", + "bfaa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "3e100000", + "be700000", + "3ef80000", + "3f640000", + "3fba0000", + "bf580000", + "bd000000", + "3f880000", + "bf440000", + "3f140000", + "bf700000", + "bf380000", + "bef80000", + "3fbe0000", + "3f180000", + "be100000", + "3f380000", + "bf920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "3fa20000", + "bf680000", + "bec80000", + "3e900000", + "3f8e0000", + "3f6c0000", + "3e600000", + "bfc00000", + "bd400000", + "bfba0000", + "bea00000", + "bf480000", + "3fa60000", + "3f040000", + "bde00000", + "3f140000", + "bf640000", + "bf860000", + "bf860000", + "bf640000", + "3f140000", + "bde00000", + "3f040000", + "3fa60000", + "bf480000", + "bea00000", + "bfba0000", + "bd400000", + "bfc00000", + "3e600000", + "3f6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "bf680000", + "3fa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "3f380000", + "be100000", + "3f180000", + "3fbe0000", + "bef80000", + "bf380000", + "bf700000", + "3f140000", + "bf440000", + "3f880000", + "bd000000", + "bf580000", + "3fba0000", + "3f640000", + "3ef80000", + "be700000", + "3e100000", + "3e500000", + "3ed80000", + "3f4c0000", + "bfaa0000", + "bf800000", + "be200000", + "3f580000", + "bf820000", + "be980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "be200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "bf680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "3ef00000", + "3ea80000", + "3fa40000", + "bf200000", + "3f240000", + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000", + "bda00000", + "bf540000", + "bfb60000", + "3f940000", + "bf600000", + "3f400000", + "3f480000", + "3f780000", + "3fa80000", + "3f9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "bf200000", + "bf740000", + "3f240000", + "bf200000", + "3fa40000", + "bea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "bf6c0000", + "3f680000", + "3f860000", + "3fac0000", + "bf9c0000", + "3f1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "3fa00000", + "3e980000", + "bf820000", + "3f580000", + "be200000", + "3f800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "be100000", + "3e700000", + "3ef80000", + "3f640000", + "3fba0000", + "3f580000", + "3d000000", + "3f880000", + "bf440000", + "3f140000", + "3f700000", + "3f380000", + "bef80000", + "3fbe0000", + "3f180000", + "3e100000", + "bf380000", + "bf920000", + "bfb40000", + "3fc00000", + "3fbc0000", + "bfa20000", + "bf680000", + "bec80000", + "3e900000", + "bf8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "bd400000", + "3fba0000", + "3ea00000", + "bf480000", + "3fa60000", + "3f040000", + "3de00000", + "bf140000", + "bf640000", + "bf860000", + "bf860000", + "3f640000", + "bf140000", + "bde00000", + "3f040000", + "3fa60000", + "3f480000", + "3ea00000", + "bfba0000", + "bd400000", + "bfc00000", + "be600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "3f680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "3f920000", + "bf380000", + "be100000", + "3f180000", + "3fbe0000", + "3ef80000", + "3f380000", + "bf700000", + "3f140000", + "bf440000", + "bf880000", + "3d000000", + "bf580000", + "3fba0000", + "3f640000", + "bef80000", + "3e700000", + "3e100000", + "3e500000", + "3ed80000", + "bf4c0000", + "3faa0000", + "bf800000", + "be200000", + "3f580000", + "3f820000", + "3e980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "bf8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "bf860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "3f8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "bf200000", + "bf240000", + "bf740000", + "3f200000", + "bf280000", + "3f9e0000", + "be880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "bf480000", + "3f400000", + "3f600000", + "3f940000", + "bfb60000", + "3f540000", + "bda00000", + "3f540000", + "bf900000", + "3dc00000", + "bfbc0000", + "bc800000", + "bfac0000", + "3f000000", + "bf040000", + "3fb00000", + "3f700000", + "3ec80000", + "3dc00000", + "be700000", + "3ea00000", + "be700000", + "3dc00000", + "3ec80000", + "3f700000", + "3fb00000", + "bf040000", + "3f000000", + "bfac0000", + "bc800000", + "bfbc0000", + "3dc00000", + "bf900000", + "3f540000", + "bda00000", + "3f540000", + "bfb60000", + "3f940000", + "3f600000", + "3f400000", + "bf480000", + "3f780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "be880000", + "3f9e0000", + "bf280000", + "3f200000", + "bf740000", + "bf240000", + "bf200000", + "3fa40000", + "3ea80000", + "bef00000", + "3f8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "bf860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "bf8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "3f820000", + "3f580000", + "be200000", + "bf800000", + "3faa0000", + "bf4c0000", + "3ed80000", + "3e500000", + "3e100000", + "3e700000", + "bef80000", + "3f640000", + "3fba0000", + "bf580000", + "3d000000", + "bf880000", + "bf440000", + "3f140000", + "bf700000", + "3f380000", + "3ef80000", + "3fbe0000", + "3f180000", + "be100000", + "bf380000", + "3f920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "3f680000", + "bec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "be600000", + "bfc00000", + "bd400000", + "bfba0000", + "3ea00000", + "3f480000", + "3fa60000", + "3f040000", + "bde00000", + "bf140000", + "3f640000", + "bf860000", + "bf860000", + "bf640000", + "bf140000", + "3de00000", + "3f040000", + "3fa60000", + "bf480000", + "3ea00000", + "3fba0000", + "bd400000", + "bfc00000", + "3e600000", + "bf6c0000", + "bf8e0000", + "3e900000", + "bec80000", + "bf680000", + "bfa20000", + "3fbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "bf380000", + "3e100000", + "3f180000", + "3fbe0000", + "bef80000", + "3f380000", + "3f700000", + "3f140000", + "bf440000", + "3f880000", + "3d000000", + "3f580000", + "3fba0000", + "3f640000", + "3ef80000", + "3e700000", + "be100000", + "3e500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "3f800000", + "be200000", + "3f580000", + "bf820000", + "3e980000", + "3fa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "3f1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "3f680000", + "bf6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "bea80000", + "3fa40000", + "bf200000", + "3f240000", + "bf740000", + "bf200000", + "bf280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "3f9a0000", + "3fa80000", + "3f780000", + "3f480000", + "3f400000", + "bf600000", + "3f940000", + "bfb60000", + "bf540000", + "bda00000", + "bf540000", + "bf900000", + "3dc00000", + "3fbc0000", + "bc800000", + "3fac0000", + "3f000000", + "bf040000", + "bfb00000", + "3f700000", + "bec80000", + "3dc00000", + "be700000", + "bea00000", + "be700000", + "bdc00000", + "3ec80000", + "3f700000", + "bfb00000", + "bf040000", + "bf000000", + "bfac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "3f900000", + "3f540000", + "bda00000", + "bf540000", + "bfb60000", + "bf940000", + "3f600000", + "3f400000", + "3f480000", + "3f780000", + "bfa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "3f9e0000", + "3f280000", + "3f200000", + "bf740000", + "3f240000", + "bf200000", + "bfa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "3fb60000", + "bf8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "3fac0000", + "3f9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "bf5c0000", + "bec00000", + "bfa00000", + "3e980000", + "bf820000", + "3f580000", + "3e200000", + "bf800000", + "3faa0000", + "3f4c0000", + "3ed80000", + "be500000", + "3e100000", + "3e700000", + "3ef80000", + "3f640000", + "bfba0000", + "bf580000", + "3d000000", + "3f880000", + "bf440000", + "bf140000", + "bf700000", + "3f380000", + "bef80000", + "3fbe0000", + "bf180000", + "be100000", + "bf380000", + "bf920000", + "bfb40000", + "bfc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "bec80000", + "be900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "bfc00000", + "3d400000", + "bfba0000", + "3ea00000", + "bf480000", + "3fa60000", + "bf040000", + "bde00000", + "bf140000", + "bf640000", + "bf860000", + "3f860000", + "bf640000", + "bf140000", + "bde00000", + "3f040000", + "bfa60000", + "bf480000", + "3ea00000", + "bfba0000", + "bd400000", + "3fc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "3e900000", + "3ec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "3fc00000", + "3fb40000", + "bf920000", + "bf380000", + "be100000", + "3f180000", + "bfbe0000", + "bef80000", + "3f380000", + "bf700000", + "3f140000", + "3f440000", + "3f880000", + "3d000000", + "bf580000", + "3fba0000", + "bf640000", + "3ef80000", + "3e700000", + "3e100000", + "3e500000", + "bed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "be200000", + "bf580000", + "bf820000", + "3e980000", + "bfa00000", + "3ec00000", + "3f5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "bf9c0000", + "bfac0000", + "3f860000", + "3f680000", + "3f6c0000", + "3f8c0000", + "bfb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "3fa40000", + "3f200000", + "3f240000", + "bf740000", + "3f200000", + "bf280000", + "bf9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "3fa80000", + "bf780000", + "3f480000", + "3f400000", + "3f600000", + "3f940000", + "3fb60000", + "bf540000", + "bda00000", + "3f540000", + "bf900000", + "bdc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "3f000000", + "3f040000", + "bfb00000", + "3f700000", + "3ec80000", + "3dc00000", + "3e700000", + "bea00000", + "be700000", + "3dc00000", + "3ec80000", + "bf700000", + "bfb00000", + "bf040000", + "3f000000", + "bfac0000", + "3c800000", + "3fbc0000", + "3dc00000", + "bf900000", + "3f540000", + "3da00000", + "bf540000", + "bfb60000", + "3f940000", + "3f600000", + "bf400000", + "3f480000", + "3f780000", + "3fa80000", + "bf9a0000", + "3f0c0000", + "3e880000", + "3f9e0000", + "bf280000", + "3f200000", + "3f740000", + "3f240000", + "bf200000", + "3fa40000", + "3ea80000", + "3ef00000", + "bf8e0000", + "3fb60000", + "3f8c0000", + "3f6c0000", + "bf680000", + "3f860000", + "3fac0000", + "bf9c0000", + "bf1c0000", + "be200000", + "3f8a0000", + "bf5c0000", + "3ec00000", + "bfa00000", + "be980000", + "bf820000", + "3f580000", + "be200000", + "bf800000", + "bfaa0000", + "3f4c0000", + "3ed80000", + "3e500000", + "3e100000", + "be700000", + "3ef80000", + "3f640000", + "3fba0000", + "bf580000", + "bd000000", + "3f880000", + "bf440000", + "3f140000", + "bf700000", + "bf380000", + "bef80000", + "3fbe0000", + "3f180000", + "be100000", + "3f380000", + "bf920000", + "bfb40000", + "3fc00000", + "bfbc0000", + "3fa20000", + "bf680000", + "bec80000", + "3e900000", + "3f8e0000", + "3f6c0000", + "3e600000", + "bfc00000", + "bd400000", + "bfba0000", + "bea00000", + "bf480000", + "3fa60000", + "3f040000", + "bde00000", + "3f140000", + "bf640000", + "bf860000", + "bf860000", + "bf640000", + "3f140000", + "bde00000", + "3f040000", + "3fa60000", + "bf480000", + "bea00000", + "bfba0000", + "bd400000", + "bfc00000", + "3e600000", + "3f6c0000", + "3f8e0000", + "3e900000", + "bec80000", + "bf680000", + "3fa20000", + "bfbc0000", + "3fc00000", + "bfb40000", + "bf920000", + "3f380000", + "be100000", + "3f180000", + "3fbe0000", + "bef80000", + "bf380000", + "bf700000", + "3f140000", + "bf440000", + "3f880000", + "bd000000", + "bf580000", + "3fba0000", + "3f640000", + "3ef80000", + "be700000", + "3e100000", + "3e500000", + "3ed80000", + "3f4c0000", + "bfaa0000", + "bf800000", + "be200000", + "3f580000", + "bf820000", + "be980000", + "bfa00000", + "3ec00000", + "bf5c0000", + "3f8a0000", + "be200000", + "bf1c0000", + "bf9c0000", + "3fac0000", + "3f860000", + "bf680000", + "3f6c0000", + "3f8c0000", + "3fb60000", + "bf8e0000", + "3ef00000", + "3ea80000", + "3fa40000", + "bf200000", + "3f240000", + "3f740000", + "3f200000", + "bf280000", + "3f9e0000", + "3e880000", + "3f0c0000", + "bf9a0000", + "3fa80000", + "3f780000", + "3f480000", + "bf400000", + "3f600000", + "3f940000", + "bfb60000", + "bf540000", + "3da00000", + "3f540000", + "bf900000", + "3dc00000", + "3fbc0000", + "3c800000", + "bfac0000", + "3f000000", + "bf040000", + "bfb00000", + "bf700000", + "3ec80000", + "3dc00000", + "be700000", + "bea00000", + "3e700000", + "3dc00000", + "3ec80000", + "3f700000", + "bfb00000", + "3f040000", + "3f000000", + "bfac0000", + "bc800000", + "3fbc0000", + "bdc00000", + "bf900000", + "3f540000", + "bda00000", + "bf540000", + "3fb60000", + "3f940000", + "3f600000", + "3f400000", + "3f480000", + "bf780000", + "3fa80000", + "bf9a0000", + "bf0c0000", + "3e880000", + "bf9e0000", + "bf280000", + "3f200000", + "bf740000", + "3f240000", + "3f200000", + "3fa40000", + "3ea80000", + "bef00000", + "bf8e0000", + "bfb60000", + "3f8c0000", + "3f6c0000", + "3f680000", + "3f860000", + "bfac0000", + "bf9c0000", + "bf1c0000", + "3e200000", + "3f8a0000", + "3f5c0000", + "3ec00000", + "bfa00000", + "3e980000", + "bf820000", + "bf580000", + "be200000", + "bf800000", + "3faa0000", + "3f4c0000", + "bed80000", + "3e500000", + "3e100000", + "3e700000", + "3ef80000", + "bf640000", + "3fba0000", + "bf580000", + "3d000000", + "3f880000", + "3f440000", + "3f140000", + "bf700000", + "3f380000", + "bef80000", + "bfbe0000", + "3f180000", + "be100000", + "bf380000", + "bf920000", + "3fb40000", + "3fc00000", + "bfbc0000", + "bfa20000", + "bf680000", + "3ec80000", + "3e900000", + "3f8e0000", + "bf6c0000", + "3e600000", + "3fc00000", + "bd400000", + "bfba0000", + "3ea00000", + "bf480000", + "bfa60000", + "3f040000", + "bde00000", + "bf140000", + "bf640000", + "3f860000", + "bf860000", + "bf640000", + "bf140000", + "bde00000", + "bf040000", + "3fa60000", + "bf480000", + "3ea00000", + "bfba0000", + "3d400000", + "bfc00000", + "3e600000", + "bf6c0000", + "3f8e0000", + "be900000", + "bec80000", + "bf680000", + "bfa20000", + "bfbc0000", + "bfc00000", + "bfb40000", + "bf920000", + "bf380000", + "be100000", + "bf180000", + "3fbe0000", + "bef80000", + "3f380000", + "bf700000", + "bf140000", + "bf440000", + "3f880000", + "3d000000", + "bf580000", + "bfba0000", + "3f640000", + "3ef80000", + "3e700000", + "3e100000", + "be500000", + "3ed80000", + "3f4c0000", + "3faa0000", + "bf800000", + "3e200000", + "3f580000", + "bf820000", + "3e980000", + "bfa00000", + "bec00000", + "bf5c0000", + "3f8a0000", + "3e200000", + "bf1c0000", + "3f9c0000", + "3fac0000", + "3f860000", + "3f680000", + "3f6c0000", + "bf8c0000", + "3fb60000", + "bf8e0000", + "bef00000", + "3ea80000", + "bfa40000", + "bf200000", + "3f240000", + "bf740000", + "3f200000", + "3f280000", + "3f9e0000", + "3e880000", + "bf0c0000", + "bf9a0000", + "bfa80000", + "3f780000", + "3f480000", + "3f400000", + "3f600000", + "bf940000", + "bfb60000", + "bf540000", + "bda00000", + "3f540000", + "3f900000", + "3dc00000", + "3fbc0000", + "bc800000", + "bfac0000", + "bf000000", + "bf040000", + "bfb00000", + "3f700000", + "3ec80000", + "bdc00000", + "be700000", + "bea00000", + "be700000", + "3dc00000", + "bec80000", + "3f700000", + "bfb00000", + "bf040000", + "3f000000", + "3fac0000", + "bc800000", + "3fbc0000", + "3dc00000", + "bf900000", + "bf540000" + ], + "dtype": "float32", + "shape": [ + 4096 + ] + }, + "rows": { + "bits": [ + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "bfac0000", + "be100000", + "bfbc0000", + "3ec00000", + "bf240000", + "3fc00000", + "3f500000", + "3e880000", + "be000000", + "beb80000", + "3ee00000", + "beb80000", + "be000000", + "3e880000", + "3f500000", + "3fc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "be100000", + "bfac0000", + "bd000000", + "bfa00000", + "3f340000", + "be500000", + "3f740000", + "3fbc0000", + "3f840000", + "3f400000", + "3f200000", + "bf280000", + "3f580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "be100000", + "3f8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "bf040000", + "bf400000", + "3f940000", + "3e500000", + "bf180000", + "3f9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "3f480000", + "bf6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "bf740000", + "bf7c0000", + "3e800000", + "bfb00000", + "3e300000", + "3f920000", + "3f380000", + "be900000", + "bf900000", + "3f9a0000", + "bf2c0000", + "3e980000", + "3da00000", + "3c800000", + "3de00000", + "beb80000", + "3f440000", + "3faa0000", + "bf780000", + "bdc00000", + "bf700000", + "bf640000", + "3ee80000", + "bf880000", + "3f180000", + "3f1c0000", + "3fae0000", + "3ef00000", + "be880000", + "bf580000", + "3fa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "3f840000", + "bf040000", + "3e200000", + "3f7c0000", + "bf860000", + "bdc00000", + "3fb20000", + "be300000", + "3fb80000", + "3e400000", + "3f680000", + "3f960000", + "3ec80000", + "be700000", + "bf340000", + "3f820000", + "bf960000", + "bf960000", + "bf820000", + "bf340000", + "3e700000", + "3ec80000", + "3f960000", + "bf680000", + "3e400000", + "bfb80000", + "be300000", + "3fb20000", + "3dc00000", + "bf860000", + "bf7c0000", + "3e200000", + "bf040000", + "bf840000", + "bfb20000", + "bfb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "3e880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "3f880000", + "3ee80000", + "bf640000", + "3f700000", + "bdc00000", + "3f780000", + "3faa0000", + "3f440000", + "3eb80000", + "3de00000", + "bc800000", + "3da00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "3f900000", + "be900000", + "3f380000", + "bf920000", + "3e300000", + "3fb00000", + "3e800000", + "bf7c0000", + "3f740000", + "3d000000", + "3f3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "bf4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "bf180000", + "be500000", + "3f940000", + "bf400000", + "3f040000", + "bf8a0000", + "bf000000", + "bf480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "3faa0000", + "3f980000", + "3f580000", + "3f280000", + "3f200000", + "bf400000", + "3f840000", + "3fbc0000", + "bf740000", + "be500000", + "bf340000", + "bfa00000", + "bd000000", + "3fac0000", + "be100000", + "3fbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "3f500000", + "be880000", + "be000000", + "beb80000", + "bee00000", + "beb80000", + "3e000000", + "3e880000", + "3f500000", + "bfc00000", + "bf240000", + "bec00000", + "bfbc0000", + "be100000", + "3fac0000", + "bd000000", + "3fa00000", + "3f340000", + "be500000", + "bf740000", + "3fbc0000", + "bf840000", + "3f400000", + "3f200000", + "3f280000", + "3f580000", + "bf980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "3f480000", + "3f000000", + "bf8a0000", + "3f040000", + "bf400000", + "bf940000", + "3e500000", + "bf180000", + "bf9e0000", + "3fa60000", + "bf780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "3fac0000", + "bf3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "be800000", + "bfb00000", + "3e300000", + "bf920000", + "3f380000", + "3e900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "bda00000", + "3c800000", + "3de00000", + "3eb80000", + "3f440000", + "bfaa0000", + "bf780000", + "bdc00000", + "3f700000", + "bf640000", + "bee80000", + "bf880000", + "3f180000", + "bf1c0000", + "3fae0000", + "bef00000", + "be880000", + "bf580000", + "bfa20000", + "3fbe0000", + "bfb00000", + "3fb60000", + "bfb20000", + "bf840000", + "bf040000", + "be200000", + "3f7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "3e300000", + "3fb80000", + "3e400000", + "bf680000", + "3f960000", + "bec80000", + "be700000", + "bf340000", + "bf820000", + "bf960000", + "3f960000", + "bf820000", + "bf340000", + "be700000", + "3ec80000", + "bf960000", + "bf680000", + "3e400000", + "3fb80000", + "be300000", + "bfb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "3e200000", + "3f040000", + "bf840000", + "bfb20000", + "3fb60000", + "3fb00000", + "bfbe0000", + "bfa20000", + "bf580000", + "be880000", + "3ef00000", + "bfae0000", + "bf1c0000", + "3f180000", + "bf880000", + "3ee80000", + "3f640000", + "3f700000", + "bdc00000", + "bf780000", + "3faa0000", + "bf440000", + "3eb80000", + "3de00000", + "3c800000", + "3da00000", + "be980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "be900000", + "bf380000", + "bf920000", + "3e300000", + "bfb00000", + "3e800000", + "3f7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "bfac0000", + "bf9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "bfa60000", + "bf9e0000", + "bf180000", + "3e500000", + "3f940000", + "3f400000", + "3f040000", + "bf8a0000", + "3f000000", + "bf480000", + "bf8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "bf580000", + "3f280000", + "3f200000", + "3f400000", + "3f840000", + "bfbc0000", + "bf740000", + "be500000", + "3f340000", + "bfa00000", + "3d000000", + "3fac0000", + "be100000", + "bfbc0000", + "3ec00000", + "3f240000", + "bfc00000", + "3f500000", + "3e880000", + "be000000", + "3eb80000", + "bee00000", + "beb80000", + "be000000", + "3e880000", + "bf500000", + "bfc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "3e100000", + "3fac0000", + "bd000000", + "bfa00000", + "3f340000", + "3e500000", + "bf740000", + "3fbc0000", + "3f840000", + "3f400000", + "bf200000", + "3f280000", + "3f580000", + "3f980000", + "bfaa0000", + "3f2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "3f000000", + "3f8a0000", + "3f040000", + "bf400000", + "3f940000", + "3e500000", + "3f180000", + "bf9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "bf480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "bd000000", + "3f740000", + "bf7c0000", + "3e800000", + "bfb00000", + "be300000", + "bf920000", + "3f380000", + "be900000", + "bf900000", + "bf9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "3c800000", + "bde00000", + "3eb80000", + "3f440000", + "3faa0000", + "bf780000", + "3dc00000", + "3f700000", + "bf640000", + "3ee80000", + "bf880000", + "bf180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "be880000", + "3f580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "3fb20000", + "bf840000", + "bf040000", + "3e200000", + "3f7c0000", + "3f860000", + "3dc00000", + "3fb20000", + "be300000", + "3fb80000", + "be400000", + "bf680000", + "3f960000", + "3ec80000", + "be700000", + "3f340000", + "bf820000", + "bf960000", + "bf960000", + "bf820000", + "3f340000", + "be700000", + "3ec80000", + "3f960000", + "bf680000", + "be400000", + "3fb80000", + "be300000", + "3fb20000", + "3dc00000", + "3f860000", + "3f7c0000", + "3e200000", + "bf040000", + "bf840000", + "3fb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "3f580000", + "be880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "bf180000", + "bf880000", + "3ee80000", + "bf640000", + "3f700000", + "3dc00000", + "bf780000", + "3faa0000", + "3f440000", + "3eb80000", + "bde00000", + "3c800000", + "3da00000", + "3e980000", + "3f2c0000", + "bf9a0000", + "bf900000", + "be900000", + "3f380000", + "bf920000", + "be300000", + "bfb00000", + "3e800000", + "bf7c0000", + "3f740000", + "bd000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "bf480000", + "3f4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "3f180000", + "3e500000", + "3f940000", + "bf400000", + "3f040000", + "3f8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "3e100000", + "3f2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "3f280000", + "bf200000", + "3f400000", + "3f840000", + "3fbc0000", + "bf740000", + "3e500000", + "3f340000", + "bfa00000", + "bd000000", + "3fac0000", + "3e100000", + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "bfac0000", + "be100000", + "bfbc0000", + "3ec00000", + "bf240000", + "3fc00000", + "3f500000", + "3e880000", + "be000000", + "beb80000", + "3ee00000", + "beb80000", + "be000000", + "3e880000", + "3f500000", + "3fc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "be100000", + "bfac0000", + "bd000000", + "bfa00000", + "3f340000", + "be500000", + "3f740000", + "3fbc0000", + "3f840000", + "3f400000", + "3f200000", + "bf280000", + "3f580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "be100000", + "3f8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "bf040000", + "bf400000", + "3f940000", + "3e500000", + "bf180000", + "3f9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "3f480000", + "bf6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "bf740000", + "bf7c0000", + "3e800000", + "bfb00000", + "3e300000", + "3f920000", + "3f380000", + "be900000", + "bf900000", + "3f9a0000", + "bf2c0000", + "3e980000", + "3da00000", + "3c800000", + "3de00000", + "beb80000", + "3f440000", + "3faa0000", + "bf780000", + "bdc00000", + "bf700000", + "bf640000", + "3ee80000", + "bf880000", + "3f180000", + "3f1c0000", + "3fae0000", + "3ef00000", + "be880000", + "bf580000", + "3fa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "3f840000", + "bf040000", + "3e200000", + "3f7c0000", + "bf860000", + "bdc00000", + "3fb20000", + "be300000", + "3fb80000", + "3e400000", + "3f680000", + "3f960000", + "3ec80000", + "be700000", + "bf340000", + "3f820000", + "bf960000", + "bf960000", + "bf820000", + "bf340000", + "3e700000", + "3ec80000", + "3f960000", + "bf680000", + "3e400000", + "bfb80000", + "be300000", + "3fb20000", + "3dc00000", + "bf860000", + "bf7c0000", + "3e200000", + "bf040000", + "bf840000", + "bfb20000", + "bfb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "3e880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "3f880000", + "3ee80000", + "bf640000", + "3f700000", + "bdc00000", + "3f780000", + "3faa0000", + "3f440000", + "3eb80000", + "3de00000", + "bc800000", + "3da00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "3f900000", + "be900000", + "3f380000", + "bf920000", + "3e300000", + "3fb00000", + "3e800000", + "bf7c0000", + "3f740000", + "3d000000", + "3f3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "bf4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "bf180000", + "be500000", + "3f940000", + "bf400000", + "3f040000", + "bf8a0000", + "bf000000", + "bf480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "3faa0000", + "3f980000", + "3f580000", + "3f280000", + "3f200000", + "bf400000", + "3f840000", + "3fbc0000", + "bf740000", + "be500000", + "bf340000", + "bfa00000", + "bd000000", + "3fac0000", + "be100000", + "3fbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "3f500000", + "be880000", + "be000000", + "beb80000", + "bee00000", + "beb80000", + "3e000000", + "3e880000", + "3f500000", + "bfc00000", + "bf240000", + "bec00000", + "bfbc0000", + "be100000", + "3fac0000", + "bd000000", + "3fa00000", + "3f340000", + "be500000", + "bf740000", + "3fbc0000", + "bf840000", + "3f400000", + "3f200000", + "3f280000", + "3f580000", + "bf980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "3f480000", + "3f000000", + "bf8a0000", + "3f040000", + "bf400000", + "bf940000", + "3e500000", + "bf180000", + "bf9e0000", + "3fa60000", + "bf780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "3fac0000", + "bf3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "be800000", + "bfb00000", + "3e300000", + "bf920000", + "3f380000", + "3e900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "bda00000", + "3c800000", + "3de00000", + "3eb80000", + "3f440000", + "bfaa0000", + "bf780000", + "bdc00000", + "3f700000", + "bf640000", + "bee80000", + "bf880000", + "3f180000", + "bf1c0000", + "3fae0000", + "bef00000", + "be880000", + "bf580000", + "bfa20000", + "3fbe0000", + "bfb00000", + "3fb60000", + "bfb20000", + "bf840000", + "bf040000", + "be200000", + "3f7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "3e300000", + "3fb80000", + "3e400000", + "bf680000", + "3f960000", + "bec80000", + "be700000", + "bf340000", + "bf820000", + "bf960000", + "3f960000", + "bf820000", + "bf340000", + "be700000", + "3ec80000", + "bf960000", + "bf680000", + "3e400000", + "3fb80000", + "be300000", + "bfb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "3e200000", + "3f040000", + "bf840000", + "bfb20000", + "3fb60000", + "3fb00000", + "bfbe0000", + "bfa20000", + "bf580000", + "be880000", + "3ef00000", + "bfae0000", + "bf1c0000", + "3f180000", + "bf880000", + "3ee80000", + "3f640000", + "3f700000", + "bdc00000", + "bf780000", + "3faa0000", + "bf440000", + "3eb80000", + "3de00000", + "3c800000", + "3da00000", + "be980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "be900000", + "bf380000", + "bf920000", + "3e300000", + "bfb00000", + "3e800000", + "3f7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "bfac0000", + "bf9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "bfa60000", + "bf9e0000", + "bf180000", + "3e500000", + "3f940000", + "3f400000", + "3f040000", + "bf8a0000", + "3f000000", + "bf480000", + "bf8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "bf580000", + "3f280000", + "3f200000", + "3f400000", + "3f840000", + "bfbc0000", + "bf740000", + "be500000", + "3f340000", + "bfa00000", + "3d000000", + "3fac0000", + "be100000", + "bfbc0000", + "3ec00000", + "3f240000", + "bfc00000", + "3f500000", + "3e880000", + "be000000", + "3eb80000", + "bee00000", + "beb80000", + "be000000", + "3e880000", + "bf500000", + "bfc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "3e100000", + "3fac0000", + "bd000000", + "bfa00000", + "3f340000", + "3e500000", + "bf740000", + "3fbc0000", + "3f840000", + "3f400000", + "bf200000", + "3f280000", + "3f580000", + "3f980000", + "bfaa0000", + "3f2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "3f000000", + "3f8a0000", + "3f040000", + "bf400000", + "3f940000", + "3e500000", + "3f180000", + "bf9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "bf480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "bd000000", + "3f740000", + "bf7c0000", + "3e800000", + "bfb00000", + "be300000", + "bf920000", + "3f380000", + "be900000", + "bf900000", + "bf9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "3c800000", + "bde00000", + "3eb80000", + "3f440000", + "3faa0000", + "bf780000", + "3dc00000", + "3f700000", + "bf640000", + "3ee80000", + "bf880000", + "bf180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "be880000", + "3f580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "3fb20000", + "bf840000", + "bf040000", + "3e200000", + "3f7c0000", + "3f860000", + "3dc00000", + "3fb20000", + "be300000", + "3fb80000", + "be400000", + "bf680000", + "3f960000", + "3ec80000", + "be700000", + "3f340000", + "bf820000", + "bf960000", + "bf960000", + "bf820000", + "3f340000", + "be700000", + "3ec80000", + "3f960000", + "bf680000", + "be400000", + "3fb80000", + "be300000", + "3fb20000", + "3dc00000", + "3f860000", + "3f7c0000", + "3e200000", + "bf040000", + "bf840000", + "3fb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "3f580000", + "be880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "bf180000", + "bf880000", + "3ee80000", + "bf640000", + "3f700000", + "3dc00000", + "bf780000", + "3faa0000", + "3f440000", + "3eb80000", + "bde00000", + "3c800000", + "3da00000", + "3e980000", + "3f2c0000", + "bf9a0000", + "bf900000", + "be900000", + "3f380000", + "bf920000", + "be300000", + "bfb00000", + "3e800000", + "bf7c0000", + "3f740000", + "bd000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "bf480000", + "3f4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "3f180000", + "3e500000", + "3f940000", + "bf400000", + "3f040000", + "3f8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "3e100000", + "3f2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "3f280000", + "bf200000", + "3f400000", + "3f840000", + "3fbc0000", + "bf740000", + "3e500000", + "3f340000", + "bfa00000", + "bd000000", + "3fac0000", + "3e100000", + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "bfac0000", + "be100000", + "bfbc0000", + "3ec00000", + "bf240000", + "3fc00000", + "3f500000", + "3e880000", + "be000000", + "beb80000", + "3ee00000", + "beb80000", + "be000000", + "3e880000", + "3f500000", + "3fc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "be100000", + "bfac0000", + "bd000000", + "bfa00000", + "3f340000", + "be500000", + "3f740000", + "3fbc0000", + "3f840000", + "3f400000", + "3f200000", + "bf280000", + "3f580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "be100000", + "3f8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "bf040000", + "bf400000", + "3f940000", + "3e500000", + "bf180000", + "3f9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "3f480000", + "bf6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "bf740000", + "bf7c0000", + "3e800000", + "bfb00000", + "3e300000", + "3f920000", + "3f380000", + "be900000", + "bf900000", + "3f9a0000", + "bf2c0000", + "3e980000", + "3da00000", + "3c800000", + "3de00000", + "beb80000", + "3f440000", + "3faa0000", + "bf780000", + "bdc00000", + "bf700000", + "bf640000", + "3ee80000", + "bf880000", + "3f180000", + "3f1c0000", + "3fae0000", + "3ef00000", + "be880000", + "bf580000", + "3fa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "3f840000", + "bf040000", + "3e200000", + "3f7c0000", + "bf860000", + "bdc00000", + "3fb20000", + "be300000", + "3fb80000", + "3e400000", + "3f680000", + "3f960000", + "3ec80000", + "be700000", + "bf340000", + "3f820000", + "bf960000", + "bf960000", + "bf820000", + "bf340000", + "3e700000", + "3ec80000", + "3f960000", + "bf680000", + "3e400000", + "bfb80000", + "be300000", + "3fb20000", + "3dc00000", + "bf860000", + "bf7c0000", + "3e200000", + "bf040000", + "bf840000", + "bfb20000", + "bfb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "3e880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "3f880000", + "3ee80000", + "bf640000", + "3f700000", + "bdc00000", + "3f780000", + "3faa0000", + "3f440000", + "3eb80000", + "3de00000", + "bc800000", + "3da00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "3f900000", + "be900000", + "3f380000", + "bf920000", + "3e300000", + "3fb00000", + "3e800000", + "bf7c0000", + "3f740000", + "3d000000", + "3f3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "bf4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "bf180000", + "be500000", + "3f940000", + "bf400000", + "3f040000", + "bf8a0000", + "bf000000", + "bf480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "3faa0000", + "3f980000", + "3f580000", + "3f280000", + "3f200000", + "bf400000", + "3f840000", + "3fbc0000", + "bf740000", + "be500000", + "bf340000", + "bfa00000", + "bd000000", + "3fac0000", + "be100000", + "3fbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "3f500000", + "be880000", + "be000000", + "beb80000", + "bee00000", + "beb80000", + "3e000000", + "3e880000", + "3f500000", + "bfc00000", + "bf240000", + "bec00000", + "bfbc0000", + "be100000", + "3fac0000", + "bd000000", + "3fa00000", + "3f340000", + "be500000", + "bf740000", + "3fbc0000", + "bf840000", + "3f400000", + "3f200000", + "3f280000", + "3f580000", + "bf980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "3f480000", + "3f000000", + "bf8a0000", + "3f040000", + "bf400000", + "bf940000", + "3e500000", + "bf180000", + "bf9e0000", + "3fa60000", + "bf780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "3fac0000", + "bf3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "be800000", + "bfb00000", + "3e300000", + "bf920000", + "3f380000", + "3e900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "bda00000", + "3c800000", + "3de00000", + "3eb80000", + "3f440000", + "bfaa0000", + "bf780000", + "bdc00000", + "3f700000", + "bf640000", + "bee80000", + "bf880000", + "3f180000", + "bf1c0000", + "3fae0000", + "bef00000", + "be880000", + "bf580000", + "bfa20000", + "3fbe0000", + "bfb00000", + "3fb60000", + "bfb20000", + "bf840000", + "bf040000", + "be200000", + "3f7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "3e300000", + "3fb80000", + "3e400000", + "bf680000", + "3f960000", + "bec80000", + "be700000", + "bf340000", + "bf820000", + "bf960000", + "3f960000", + "bf820000", + "bf340000", + "be700000", + "3ec80000", + "bf960000", + "bf680000", + "3e400000", + "3fb80000", + "be300000", + "bfb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "3e200000", + "3f040000", + "bf840000", + "bfb20000", + "3fb60000", + "3fb00000", + "bfbe0000", + "bfa20000", + "bf580000", + "be880000", + "3ef00000", + "bfae0000", + "bf1c0000", + "3f180000", + "bf880000", + "3ee80000", + "3f640000", + "3f700000", + "bdc00000", + "bf780000", + "3faa0000", + "bf440000", + "3eb80000", + "3de00000", + "3c800000", + "3da00000", + "be980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "be900000", + "bf380000", + "bf920000", + "3e300000", + "bfb00000", + "3e800000", + "3f7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "bfac0000", + "bf9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "bfa60000", + "bf9e0000", + "bf180000", + "3e500000", + "3f940000", + "3f400000", + "3f040000", + "bf8a0000", + "3f000000", + "bf480000", + "bf8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "bf580000", + "3f280000", + "3f200000", + "3f400000", + "3f840000", + "bfbc0000", + "bf740000", + "be500000", + "3f340000", + "bfa00000", + "3d000000", + "3fac0000", + "be100000", + "bfbc0000", + "3ec00000", + "3f240000", + "bfc00000", + "3f500000", + "3e880000", + "be000000", + "3eb80000", + "bee00000", + "beb80000", + "be000000", + "3e880000", + "bf500000", + "bfc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "3e100000", + "3fac0000", + "bd000000", + "bfa00000", + "3f340000", + "3e500000", + "bf740000", + "3fbc0000", + "3f840000", + "3f400000", + "bf200000", + "3f280000", + "3f580000", + "3f980000", + "bfaa0000", + "3f2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "3f000000", + "3f8a0000", + "3f040000", + "bf400000", + "3f940000", + "3e500000", + "3f180000", + "bf9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "bf480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "bd000000", + "3f740000", + "bf7c0000", + "3e800000", + "bfb00000", + "be300000", + "bf920000", + "3f380000", + "be900000", + "bf900000", + "bf9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "3c800000", + "bde00000", + "3eb80000", + "3f440000", + "3faa0000", + "bf780000", + "3dc00000", + "3f700000", + "bf640000", + "3ee80000", + "bf880000", + "bf180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "be880000", + "3f580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "3fb20000", + "bf840000", + "bf040000", + "3e200000", + "3f7c0000", + "3f860000", + "3dc00000", + "3fb20000", + "be300000", + "3fb80000", + "be400000", + "bf680000", + "3f960000", + "3ec80000", + "be700000", + "3f340000", + "bf820000", + "bf960000", + "bf960000", + "bf820000", + "3f340000", + "be700000", + "3ec80000", + "3f960000", + "bf680000", + "be400000", + "3fb80000", + "be300000", + "3fb20000", + "3dc00000", + "3f860000", + "3f7c0000", + "3e200000", + "bf040000", + "bf840000", + "3fb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "3f580000", + "be880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "bf180000", + "bf880000", + "3ee80000", + "bf640000", + "3f700000", + "3dc00000", + "bf780000", + "3faa0000", + "3f440000", + "3eb80000", + "bde00000", + "3c800000", + "3da00000", + "3e980000", + "3f2c0000", + "bf9a0000", + "bf900000", + "be900000", + "3f380000", + "bf920000", + "be300000", + "bfb00000", + "3e800000", + "bf7c0000", + "3f740000", + "bd000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "bf480000", + "3f4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "3f180000", + "3e500000", + "3f940000", + "bf400000", + "3f040000", + "3f8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "3e100000", + "3f2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "3f280000", + "bf200000", + "3f400000", + "3f840000", + "3fbc0000", + "bf740000", + "3e500000", + "3f340000", + "bfa00000", + "bd000000", + "3fac0000", + "3e100000", + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "bf9e0000", + "3fa60000", + "3f780000", + "bf4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "3f3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "3e800000", + "3fb00000", + "3e300000", + "bf920000", + "3f380000", + "be900000", + "3f900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "bc800000", + "3de00000", + "3eb80000", + "3f440000", + "3faa0000", + "3f780000", + "bdc00000", + "3f700000", + "bf640000", + "3ee80000", + "3f880000", + "3f180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "3e880000", + "bf580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "bfb60000", + "bfb20000", + "bf840000", + "bf040000", + "3e200000", + "bf7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "be300000", + "bfb80000", + "3e400000", + "bf680000", + "3f960000", + "3ec80000", + "3e700000", + "bf340000", + "bf820000", + "bf960000", + "bf960000", + "3f820000", + "bf340000", + "be700000", + "3ec80000", + "3f960000", + "3f680000", + "3e400000", + "3fb80000", + "be300000", + "3fb20000", + "bdc00000", + "bf860000", + "3f7c0000", + "3e200000", + "bf040000", + "3f840000", + "bfb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "3fa20000", + "bf580000", + "be880000", + "3ef00000", + "3fae0000", + "3f1c0000", + "3f180000", + "bf880000", + "3ee80000", + "bf640000", + "bf700000", + "bdc00000", + "bf780000", + "3faa0000", + "3f440000", + "beb80000", + "3de00000", + "3c800000", + "3da00000", + "3e980000", + "bf2c0000", + "3f9a0000", + "bf900000", + "be900000", + "3f380000", + "3f920000", + "3e300000", + "bfb00000", + "3e800000", + "bf7c0000", + "bf740000", + "3d000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "bf6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "3fa60000", + "3f9e0000", + "bf180000", + "3e500000", + "3f940000", + "bf400000", + "bf040000", + "bf8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "be100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "bf280000", + "3f200000", + "3f400000", + "3f840000", + "3fbc0000", + "3f740000", + "be500000", + "3f340000", + "bfa00000", + "bd000000", + "bfac0000", + "be100000", + "bfbc0000", + "3ec00000", + "bf240000", + "3fc00000", + "3f500000", + "3e880000", + "be000000", + "beb80000", + "3ee00000", + "beb80000", + "be000000", + "3e880000", + "3f500000", + "3fc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "be100000", + "bfac0000", + "bd000000", + "bfa00000", + "3f340000", + "be500000", + "3f740000", + "3fbc0000", + "3f840000", + "3f400000", + "3f200000", + "bf280000", + "3f580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "be100000", + "3f8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "bf040000", + "bf400000", + "3f940000", + "3e500000", + "bf180000", + "3f9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "3f480000", + "bf6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "bf740000", + "bf7c0000", + "3e800000", + "bfb00000", + "3e300000", + "3f920000", + "3f380000", + "be900000", + "bf900000", + "3f9a0000", + "bf2c0000", + "3e980000", + "3da00000", + "3c800000", + "3de00000", + "beb80000", + "3f440000", + "3faa0000", + "bf780000", + "bdc00000", + "bf700000", + "bf640000", + "3ee80000", + "bf880000", + "3f180000", + "3f1c0000", + "3fae0000", + "3ef00000", + "be880000", + "bf580000", + "3fa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "3f840000", + "bf040000", + "3e200000", + "3f7c0000", + "bf860000", + "bdc00000", + "3fb20000", + "be300000", + "3fb80000", + "3e400000", + "3f680000", + "3f960000", + "3ec80000", + "be700000", + "bf340000", + "3f820000", + "bf960000", + "bf960000", + "bf820000", + "bf340000", + "3e700000", + "3ec80000", + "3f960000", + "bf680000", + "3e400000", + "bfb80000", + "be300000", + "3fb20000", + "3dc00000", + "bf860000", + "bf7c0000", + "3e200000", + "bf040000", + "bf840000", + "bfb20000", + "bfb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "3e880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "3f880000", + "3ee80000", + "bf640000", + "3f700000", + "bdc00000", + "3f780000", + "3faa0000", + "3f440000", + "3eb80000", + "3de00000", + "bc800000", + "3da00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "3f900000", + "be900000", + "3f380000", + "bf920000", + "3e300000", + "3fb00000", + "3e800000", + "bf7c0000", + "3f740000", + "3d000000", + "3f3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "bf4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "bf180000", + "be500000", + "3f940000", + "bf400000", + "3f040000", + "bf8a0000", + "bf000000", + "bf480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "3faa0000", + "3f980000", + "3f580000", + "3f280000", + "3f200000", + "bf400000", + "3f840000", + "3fbc0000", + "bf740000", + "be500000", + "bf340000", + "bfa00000", + "bd000000", + "3fac0000", + "be100000", + "3fbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "3f500000", + "be880000", + "be000000", + "beb80000", + "bee00000", + "beb80000", + "3e000000", + "3e880000", + "3f500000", + "bfc00000", + "bf240000", + "bec00000", + "bfbc0000", + "be100000", + "3fac0000", + "bd000000", + "3fa00000", + "3f340000", + "be500000", + "bf740000", + "3fbc0000", + "bf840000", + "3f400000", + "3f200000", + "3f280000", + "3f580000", + "bf980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "3f480000", + "3f000000", + "bf8a0000", + "3f040000", + "bf400000", + "bf940000", + "3e500000", + "bf180000", + "bf9e0000", + "3fa60000", + "bf780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "3f9c0000", + "3fac0000", + "bf3c0000", + "3d000000", + "3f740000", + "bf7c0000", + "be800000", + "bfb00000", + "3e300000", + "bf920000", + "3f380000", + "3e900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "3e980000", + "bda00000", + "3c800000", + "3de00000", + "3eb80000", + "3f440000", + "bfaa0000", + "bf780000", + "bdc00000", + "3f700000", + "bf640000", + "bee80000", + "bf880000", + "3f180000", + "bf1c0000", + "3fae0000", + "bef00000", + "be880000", + "bf580000", + "bfa20000", + "3fbe0000", + "bfb00000", + "3fb60000", + "bfb20000", + "bf840000", + "bf040000", + "be200000", + "3f7c0000", + "bf860000", + "3dc00000", + "3fb20000", + "3e300000", + "3fb80000", + "3e400000", + "bf680000", + "3f960000", + "bec80000", + "be700000", + "bf340000", + "bf820000", + "bf960000", + "3f960000", + "bf820000", + "bf340000", + "be700000", + "3ec80000", + "bf960000", + "bf680000", + "3e400000", + "3fb80000", + "be300000", + "bfb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "3e200000", + "3f040000", + "bf840000", + "bfb20000", + "3fb60000", + "3fb00000", + "bfbe0000", + "bfa20000", + "bf580000", + "be880000", + "3ef00000", + "bfae0000", + "bf1c0000", + "3f180000", + "bf880000", + "3ee80000", + "3f640000", + "3f700000", + "bdc00000", + "bf780000", + "3faa0000", + "bf440000", + "3eb80000", + "3de00000", + "3c800000", + "3da00000", + "be980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "be900000", + "bf380000", + "bf920000", + "3e300000", + "bfb00000", + "3e800000", + "3f7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "bfac0000", + "bf9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "3f780000", + "bfa60000", + "bf9e0000", + "bf180000", + "3e500000", + "3f940000", + "3f400000", + "3f040000", + "bf8a0000", + "3f000000", + "bf480000", + "bf8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "3f980000", + "bf580000", + "3f280000", + "3f200000", + "3f400000", + "3f840000", + "bfbc0000", + "bf740000", + "be500000", + "3f340000", + "bfa00000", + "3d000000", + "3fac0000", + "be100000", + "bfbc0000", + "3ec00000", + "3f240000", + "bfc00000", + "3f500000", + "3e880000", + "be000000", + "3eb80000", + "bee00000", + "beb80000", + "be000000", + "3e880000", + "bf500000", + "bfc00000", + "bf240000", + "3ec00000", + "bfbc0000", + "3e100000", + "3fac0000", + "bd000000", + "bfa00000", + "3f340000", + "3e500000", + "bf740000", + "3fbc0000", + "3f840000", + "3f400000", + "bf200000", + "3f280000", + "3f580000", + "3f980000", + "bfaa0000", + "3f2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "3f000000", + "3f8a0000", + "3f040000", + "bf400000", + "3f940000", + "3e500000", + "3f180000", + "bf9e0000", + "3fa60000", + "3f780000", + "3f4c0000", + "bf480000", + "3f6c0000", + "3f9c0000", + "bfac0000", + "bf3c0000", + "bd000000", + "3f740000", + "bf7c0000", + "3e800000", + "bfb00000", + "be300000", + "bf920000", + "3f380000", + "be900000", + "bf900000", + "bf9a0000", + "3f2c0000", + "3e980000", + "3da00000", + "3c800000", + "bde00000", + "3eb80000", + "3f440000", + "3faa0000", + "bf780000", + "3dc00000", + "3f700000", + "bf640000", + "3ee80000", + "bf880000", + "bf180000", + "bf1c0000", + "3fae0000", + "3ef00000", + "be880000", + "3f580000", + "bfa20000", + "3fbe0000", + "3fb00000", + "3fb60000", + "3fb20000", + "bf840000", + "bf040000", + "3e200000", + "3f7c0000", + "3f860000", + "3dc00000", + "3fb20000", + "be300000", + "3fb80000", + "be400000", + "bf680000", + "3f960000", + "3ec80000", + "be700000", + "3f340000", + "bf820000", + "bf960000", + "bf960000", + "bf820000", + "3f340000", + "be700000", + "3ec80000", + "3f960000", + "bf680000", + "be400000", + "3fb80000", + "be300000", + "3fb20000", + "3dc00000", + "3f860000", + "3f7c0000", + "3e200000", + "bf040000", + "bf840000", + "3fb20000", + "3fb60000", + "3fb00000", + "3fbe0000", + "bfa20000", + "3f580000", + "be880000", + "3ef00000", + "3fae0000", + "bf1c0000", + "bf180000", + "bf880000", + "3ee80000", + "bf640000", + "3f700000", + "3dc00000", + "bf780000", + "3faa0000", + "3f440000", + "3eb80000", + "bde00000", + "3c800000", + "3da00000", + "3e980000", + "3f2c0000", + "bf9a0000", + "bf900000", + "be900000", + "3f380000", + "bf920000", + "be300000", + "bfb00000", + "3e800000", + "bf7c0000", + "3f740000", + "bd000000", + "bf3c0000", + "bfac0000", + "3f9c0000", + "3f6c0000", + "bf480000", + "3f4c0000", + "3f780000", + "3fa60000", + "bf9e0000", + "3f180000", + "3e500000", + "3f940000", + "bf400000", + "3f040000", + "3f8a0000", + "3f000000", + "bf480000", + "3f8e0000", + "3e100000", + "3f2c0000", + "bfaa0000", + "3f980000", + "3f580000", + "3f280000", + "bf200000", + "3f400000", + "3f840000", + "3fbc0000", + "bf740000", + "3e500000", + "3f340000", + "bfa00000", + "bd000000", + "3fac0000", + "3e100000", + "bfbc0000", + "3ec00000", + "bf240000", + "bfc00000", + "bf500000", + "3e880000", + "be000000", + "beb80000", + "bee00000", + "3eb80000", + "be000000", + "3e880000", + "3f500000", + "bfc00000", + "3f240000", + "3ec00000", + "bfbc0000", + "be100000", + "3fac0000", + "3d000000", + "bfa00000", + "3f340000", + "be500000", + "bf740000", + "bfbc0000", + "3f840000", + "3f400000", + "3f200000", + "3f280000", + "bf580000", + "3f980000", + "bfaa0000", + "bf2c0000", + "3e100000", + "bf8e0000", + "bf480000", + "3f000000", + "bf8a0000", + "3f040000", + "3f400000", + "3f940000", + "3e500000", + "bf180000", + "bf9e0000", + "bfa60000", + "3f780000", + "3f4c0000", + "3f480000", + "3f6c0000", + "bf9c0000", + "bfac0000", + "bf3c0000", + "3d000000", + "3f740000", + "3f7c0000", + "3e800000", + "bfb00000", + "3e300000", + "bf920000", + "bf380000", + "be900000", + "bf900000", + "3f9a0000", + "3f2c0000", + "be980000", + "3da00000", + "3c800000", + "3de00000", + "3eb80000", + "bf440000", + "3faa0000", + "bf780000", + "bdc00000", + "3f700000", + "3f640000", + "3ee80000", + "bf880000", + "3f180000", + "bf1c0000", + "bfae0000", + "3ef00000", + "be880000", + "bf580000", + "bfa20000", + "bfbe0000", + "3fb00000", + "3fb60000", + "bfb20000", + "bf840000", + "3f040000", + "3e200000", + "3f7c0000", + "bf860000", + "3dc00000", + "bfb20000", + "be300000", + "3fb80000", + "3e400000", + "bf680000", + "bf960000", + "3ec80000", + "be700000", + "bf340000", + "bf820000", + "3f960000", + "bf960000", + "bf820000", + "bf340000", + "be700000", + "bec80000", + "3f960000", + "bf680000", + "3e400000", + "3fb80000", + "3e300000", + "3fb20000", + "3dc00000", + "bf860000", + "3f7c0000", + "be200000", + "bf040000", + "bf840000", + "bfb20000", + "3fb60000", + "bfb00000", + "3fbe0000", + "bfa20000", + "bf580000", + "be880000", + "bef00000", + "3fae0000", + "bf1c0000", + "3f180000", + "bf880000", + "bee80000", + "bf640000", + "3f700000", + "bdc00000", + "bf780000", + "bfaa0000", + "3f440000", + "3eb80000", + "3de00000", + "3c800000", + "bda00000", + "3e980000", + "3f2c0000", + "3f9a0000", + "bf900000", + "3e900000", + "3f380000", + "bf920000", + "3e300000", + "bfb00000", + "be800000", + "bf7c0000", + "3f740000", + "3d000000", + "bf3c0000", + "3fac0000", + "3f9c0000", + "3f6c0000", + "3f480000", + "3f4c0000", + "bf780000", + "3fa60000", + "bf9e0000", + "bf180000", + "3e500000", + "bf940000", + "bf400000", + "3f040000", + "bf8a0000", + "3f000000", + "3f480000", + "3f8e0000", + "3e100000", + "bf2c0000", + "bfaa0000", + "bf980000", + "3f580000", + "3f280000", + "3f200000", + "3f400000", + "bf840000", + "3fbc0000", + "bf740000", + "be500000", + "3f340000", + "3fa00000", + "bd000000", + "3fac0000", + "be100000", + "bfbc0000", + "bec00000", + "bf240000", + "bfc00000", + "3f500000", + "3e880000", + "3e000000", + "beb80000", + "bee00000", + "beb80000", + "be000000", + "be880000", + "3f500000", + "bfc00000", + "bf240000", + "3ec00000", + "3fbc0000", + "be100000", + "3fac0000", + "bd000000", + "bfa00000", + "bf340000", + "be500000", + "bf740000", + "3fbc0000", + "3f840000", + "bf400000", + "3f200000", + "3f280000", + "3f580000", + "3f980000", + "3faa0000", + "bf2c0000", + "3e100000", + "3f8e0000", + "bf480000", + "bf000000", + "bf8a0000", + "3f040000", + "bf400000", + "3f940000", + "be500000", + "bf180000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000", + "3f340000", + "bf040000", + "3fb80000", + "3f080000", + "3e600000", + "bf500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "bfb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "3d800000", + "bf600000", + "bf960000", + "bd400000", + "3f9e0000", + "beb00000", + "bfa00000", + "bc800000", + "bf900000", + "3f700000", + "3e100000", + "3f000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "3fae0000", + "bf880000", + "bf1c0000", + "3e000000", + "3f440000", + "3faa0000", + "be800000", + "3f7c0000", + "bf240000", + "3f680000", + "3ed00000", + "3fba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "bfb40000", + "3f840000", + "3f500000", + "3f400000", + "3f580000", + "bf8c0000", + "3fc00000", + "bf740000", + "be700000", + "3f240000", + "3fac0000", + "be200000", + "3f980000", + "bea80000", + "3faa0000", + "be000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "3f080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "3e980000", + "3e600000", + "3f640000", + "bfa60000", + "bea00000", + "bf540000", + "bf640000", + "3f100000", + "bf580000", + "3f6c0000", + "3e300000", + "bf8e0000", + "3f900000", + "3f000000", + "3d000000", + "3e900000", + "bee00000", + "bee00000", + "be900000", + "3d000000", + "bf000000", + "3f900000", + "bf8e0000", + "be300000", + "3f6c0000", + "3f580000", + "3f100000", + "bf640000", + "3f540000", + "bea00000", + "3fa60000", + "3f640000", + "3e600000", + "be980000", + "bf280000", + "3f5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "bde00000", + "bef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "3ea80000", + "3f980000", + "be200000", + "bfac0000", + "3f240000", + "3e700000", + "bf740000", + "3fc00000", + "3f8c0000", + "3f580000", + "bf400000", + "3f500000", + "3f840000", + "3fb40000", + "bf8a0000", + "3ec80000", + "3ee80000", + "3fba0000", + "bed00000", + "3f680000", + "3f240000", + "3f7c0000", + "be800000", + "bfaa0000", + "3f440000", + "be000000", + "bf1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "3fbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "3e100000", + "bf700000", + "bf900000", + "bc800000", + "3fa00000", + "beb00000", + "bf9e0000", + "bd400000", + "bf960000", + "3f600000", + "3d800000", + "3f180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "bfbe0000", + "bfa00000", + "bf500000", + "be600000", + "3f080000", + "bfb80000", + "bf040000", + "3f340000", + "bf700000", + "3f180000", + "3f3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "bf800000", + "3f1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "bf1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "3dc00000", + "bf8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "3f340000", + "3f040000", + "3fb80000", + "3f080000", + "be600000", + "bf500000", + "3fa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "3f8c0000", + "bf180000", + "3d800000", + "3f600000", + "bf960000", + "3d400000", + "3f9e0000", + "beb00000", + "3fa00000", + "bc800000", + "3f900000", + "3f700000", + "3e100000", + "bf000000", + "bf7c0000", + "3fa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "3f1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "be800000", + "bf7c0000", + "bf240000", + "3f680000", + "bed00000", + "3fba0000", + "bee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "bf500000", + "3f400000", + "3f580000", + "3f8c0000", + "3fc00000", + "3f740000", + "be700000", + "3f240000", + "bfac0000", + "be200000", + "bf980000", + "bea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "bf9a0000", + "3ef00000", + "bde00000", + "bf080000", + "bf4c0000", + "3f680000", + "bf5c0000", + "bf280000", + "be980000", + "3e600000", + "bf640000", + "bfa60000", + "bea00000", + "3f540000", + "bf640000", + "bf100000", + "bf580000", + "3f6c0000", + "be300000", + "bf8e0000", + "bf900000", + "3f000000", + "3d000000", + "be900000", + "bee00000", + "3ee00000", + "be900000", + "3d000000", + "3f000000", + "3f900000", + "3f8e0000", + "be300000", + "3f6c0000", + "bf580000", + "3f100000", + "3f640000", + "3f540000", + "bea00000", + "bfa60000", + "3f640000", + "be600000", + "be980000", + "bf280000", + "bf5c0000", + "bf680000", + "3f4c0000", + "bf080000", + "bde00000", + "3ef00000", + "3f9a0000", + "3f6c0000", + "3e000000", + "3faa0000", + "bea80000", + "3f980000", + "3e200000", + "bfac0000", + "3f240000", + "be700000", + "bf740000", + "bfc00000", + "3f8c0000", + "3f580000", + "3f400000", + "3f500000", + "bf840000", + "3fb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "bfba0000", + "bed00000", + "3f680000", + "bf240000", + "3f7c0000", + "3e800000", + "bfaa0000", + "3f440000", + "3e000000", + "bf1c0000", + "3f880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "3f7c0000", + "bf000000", + "3e100000", + "3f700000", + "bf900000", + "3c800000", + "3fa00000", + "beb00000", + "3f9e0000", + "bd400000", + "3f960000", + "3f600000", + "3d800000", + "bf180000", + "bf8c0000", + "3fb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "3f500000", + "be600000", + "3f080000", + "3fb80000", + "bf040000", + "bf340000", + "bf700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "bdc00000", + "bf440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "bec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "3f800000", + "3fbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "bf180000", + "bf700000", + "3f340000", + "bf040000", + "3fb80000", + "bf080000", + "be600000", + "bf500000", + "bfa00000", + "3fbe0000", + "bfae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "bd800000", + "3f600000", + "bf960000", + "bd400000", + "3f9e0000", + "3eb00000", + "3fa00000", + "bc800000", + "bf900000", + "3f700000", + "be100000", + "bf000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "3fc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "3e000000", + "bf440000", + "bfaa0000", + "be800000", + "3f7c0000", + "bf240000", + "bf680000", + "bed00000", + "3fba0000", + "3ee80000", + "bec80000", + "3f8a0000", + "3fb40000", + "3f840000", + "3f500000", + "3f400000", + "bf580000", + "3f8c0000", + "3fc00000", + "bf740000", + "be700000", + "bf240000", + "bfac0000", + "be200000", + "3f980000", + "bea80000", + "bfaa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "3de00000", + "bf080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "3f280000", + "be980000", + "3e600000", + "3f640000", + "bfa60000", + "3ea00000", + "3f540000", + "bf640000", + "3f100000", + "bf580000", + "bf6c0000", + "be300000", + "bf8e0000", + "3f900000", + "3f000000", + "bd000000", + "be900000", + "bee00000", + "bee00000", + "be900000", + "bd000000", + "3f000000", + "3f900000", + "bf8e0000", + "be300000", + "bf6c0000", + "bf580000", + "3f100000", + "bf640000", + "3f540000", + "3ea00000", + "bfa60000", + "3f640000", + "3e600000", + "be980000", + "3f280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "3de00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "bfaa0000", + "bea80000", + "3f980000", + "be200000", + "bfac0000", + "bf240000", + "be700000", + "bf740000", + "3fc00000", + "3f8c0000", + "bf580000", + "3f400000", + "3f500000", + "3f840000", + "3fb40000", + "3f8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "bed00000", + "bf680000", + "bf240000", + "3f7c0000", + "be800000", + "bfaa0000", + "bf440000", + "3e000000", + "bf1c0000", + "bf880000", + "bfae0000", + "3fc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "be100000", + "3f700000", + "bf900000", + "bc800000", + "3fa00000", + "3eb00000", + "3f9e0000", + "bd400000", + "bf960000", + "3f600000", + "bd800000", + "bf180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "bfae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "be600000", + "bf080000", + "3fb80000", + "bf040000", + "3f340000", + "bf700000", + "bf180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "3fbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "3e980000", + "bec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "bdc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "bf340000", + "bf040000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000", + "3f340000", + "bf040000", + "3fb80000", + "3f080000", + "3e600000", + "bf500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "bfb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "3d800000", + "bf600000", + "bf960000", + "bd400000", + "3f9e0000", + "beb00000", + "bfa00000", + "bc800000", + "bf900000", + "3f700000", + "3e100000", + "3f000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "3fae0000", + "bf880000", + "bf1c0000", + "3e000000", + "3f440000", + "3faa0000", + "be800000", + "3f7c0000", + "bf240000", + "3f680000", + "3ed00000", + "3fba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "bfb40000", + "3f840000", + "3f500000", + "3f400000", + "3f580000", + "bf8c0000", + "3fc00000", + "bf740000", + "be700000", + "3f240000", + "3fac0000", + "be200000", + "3f980000", + "bea80000", + "3faa0000", + "be000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "3f080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "3e980000", + "3e600000", + "3f640000", + "bfa60000", + "bea00000", + "bf540000", + "bf640000", + "3f100000", + "bf580000", + "3f6c0000", + "3e300000", + "bf8e0000", + "3f900000", + "3f000000", + "3d000000", + "3e900000", + "bee00000", + "bee00000", + "be900000", + "3d000000", + "bf000000", + "3f900000", + "bf8e0000", + "be300000", + "3f6c0000", + "3f580000", + "3f100000", + "bf640000", + "3f540000", + "bea00000", + "3fa60000", + "3f640000", + "3e600000", + "be980000", + "bf280000", + "3f5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "bde00000", + "bef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "3ea80000", + "3f980000", + "be200000", + "bfac0000", + "3f240000", + "3e700000", + "bf740000", + "3fc00000", + "3f8c0000", + "3f580000", + "bf400000", + "3f500000", + "3f840000", + "3fb40000", + "bf8a0000", + "3ec80000", + "3ee80000", + "3fba0000", + "bed00000", + "3f680000", + "3f240000", + "3f7c0000", + "be800000", + "bfaa0000", + "3f440000", + "be000000", + "bf1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "3fbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "3e100000", + "bf700000", + "bf900000", + "bc800000", + "3fa00000", + "beb00000", + "bf9e0000", + "bd400000", + "bf960000", + "3f600000", + "3d800000", + "3f180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "bfbe0000", + "bfa00000", + "bf500000", + "be600000", + "3f080000", + "bfb80000", + "bf040000", + "3f340000", + "bf700000", + "3f180000", + "3f3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "bf800000", + "3f1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "bf1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "3dc00000", + "bf8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "3f340000", + "3f040000", + "3fb80000", + "3f080000", + "be600000", + "bf500000", + "3fa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "3f8c0000", + "bf180000", + "3d800000", + "3f600000", + "bf960000", + "3d400000", + "3f9e0000", + "beb00000", + "3fa00000", + "bc800000", + "3f900000", + "3f700000", + "3e100000", + "bf000000", + "bf7c0000", + "3fa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "3f1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "be800000", + "bf7c0000", + "bf240000", + "3f680000", + "bed00000", + "3fba0000", + "bee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "bf500000", + "3f400000", + "3f580000", + "3f8c0000", + "3fc00000", + "3f740000", + "be700000", + "3f240000", + "bfac0000", + "be200000", + "bf980000", + "bea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "bf9a0000", + "3ef00000", + "bde00000", + "bf080000", + "bf4c0000", + "3f680000", + "bf5c0000", + "bf280000", + "be980000", + "3e600000", + "bf640000", + "bfa60000", + "bea00000", + "3f540000", + "bf640000", + "bf100000", + "bf580000", + "3f6c0000", + "be300000", + "bf8e0000", + "bf900000", + "3f000000", + "3d000000", + "be900000", + "bee00000", + "3ee00000", + "be900000", + "3d000000", + "3f000000", + "3f900000", + "3f8e0000", + "be300000", + "3f6c0000", + "bf580000", + "3f100000", + "3f640000", + "3f540000", + "bea00000", + "bfa60000", + "3f640000", + "be600000", + "be980000", + "bf280000", + "bf5c0000", + "bf680000", + "3f4c0000", + "bf080000", + "bde00000", + "3ef00000", + "3f9a0000", + "3f6c0000", + "3e000000", + "3faa0000", + "bea80000", + "3f980000", + "3e200000", + "bfac0000", + "3f240000", + "be700000", + "bf740000", + "bfc00000", + "3f8c0000", + "3f580000", + "3f400000", + "3f500000", + "bf840000", + "3fb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "bfba0000", + "bed00000", + "3f680000", + "bf240000", + "3f7c0000", + "3e800000", + "bfaa0000", + "3f440000", + "3e000000", + "bf1c0000", + "3f880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "3f7c0000", + "bf000000", + "3e100000", + "3f700000", + "bf900000", + "3c800000", + "3fa00000", + "beb00000", + "3f9e0000", + "bd400000", + "3f960000", + "3f600000", + "3d800000", + "bf180000", + "bf8c0000", + "3fb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "3f500000", + "be600000", + "3f080000", + "3fb80000", + "bf040000", + "bf340000", + "bf700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "bdc00000", + "bf440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "bec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "3f800000", + "3fbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "bf180000", + "bf700000", + "3f340000", + "bf040000", + "3fb80000", + "bf080000", + "be600000", + "bf500000", + "bfa00000", + "3fbe0000", + "bfae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "bd800000", + "3f600000", + "bf960000", + "bd400000", + "3f9e0000", + "3eb00000", + "3fa00000", + "bc800000", + "bf900000", + "3f700000", + "be100000", + "bf000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "3fc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "3e000000", + "bf440000", + "bfaa0000", + "be800000", + "3f7c0000", + "bf240000", + "bf680000", + "bed00000", + "3fba0000", + "3ee80000", + "bec80000", + "3f8a0000", + "3fb40000", + "3f840000", + "3f500000", + "3f400000", + "bf580000", + "3f8c0000", + "3fc00000", + "bf740000", + "be700000", + "bf240000", + "bfac0000", + "be200000", + "3f980000", + "bea80000", + "bfaa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "3de00000", + "bf080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "3f280000", + "be980000", + "3e600000", + "3f640000", + "bfa60000", + "3ea00000", + "3f540000", + "bf640000", + "3f100000", + "bf580000", + "bf6c0000", + "be300000", + "bf8e0000", + "3f900000", + "3f000000", + "bd000000", + "be900000", + "bee00000", + "bee00000", + "be900000", + "bd000000", + "3f000000", + "3f900000", + "bf8e0000", + "be300000", + "bf6c0000", + "bf580000", + "3f100000", + "bf640000", + "3f540000", + "3ea00000", + "bfa60000", + "3f640000", + "3e600000", + "be980000", + "3f280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "3de00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "bfaa0000", + "bea80000", + "3f980000", + "be200000", + "bfac0000", + "bf240000", + "be700000", + "bf740000", + "3fc00000", + "3f8c0000", + "bf580000", + "3f400000", + "3f500000", + "3f840000", + "3fb40000", + "3f8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "bed00000", + "bf680000", + "bf240000", + "3f7c0000", + "be800000", + "bfaa0000", + "bf440000", + "3e000000", + "bf1c0000", + "bf880000", + "bfae0000", + "3fc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "be100000", + "3f700000", + "bf900000", + "bc800000", + "3fa00000", + "3eb00000", + "3f9e0000", + "bd400000", + "bf960000", + "3f600000", + "bd800000", + "bf180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "bfae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "be600000", + "bf080000", + "3fb80000", + "bf040000", + "3f340000", + "bf700000", + "bf180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "3fbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "3e980000", + "bec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "bdc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "bf340000", + "bf040000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000", + "3f340000", + "bf040000", + "3fb80000", + "3f080000", + "3e600000", + "bf500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "bfb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "3d800000", + "bf600000", + "bf960000", + "bd400000", + "3f9e0000", + "beb00000", + "bfa00000", + "bc800000", + "bf900000", + "3f700000", + "3e100000", + "3f000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "3fae0000", + "bf880000", + "bf1c0000", + "3e000000", + "3f440000", + "3faa0000", + "be800000", + "3f7c0000", + "bf240000", + "3f680000", + "3ed00000", + "3fba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "bfb40000", + "3f840000", + "3f500000", + "3f400000", + "3f580000", + "bf8c0000", + "3fc00000", + "bf740000", + "be700000", + "3f240000", + "3fac0000", + "be200000", + "3f980000", + "bea80000", + "3faa0000", + "be000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "3f080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "3e980000", + "3e600000", + "3f640000", + "bfa60000", + "bea00000", + "bf540000", + "bf640000", + "3f100000", + "bf580000", + "3f6c0000", + "3e300000", + "bf8e0000", + "3f900000", + "3f000000", + "3d000000", + "3e900000", + "bee00000", + "bee00000", + "be900000", + "3d000000", + "bf000000", + "3f900000", + "bf8e0000", + "be300000", + "3f6c0000", + "3f580000", + "3f100000", + "bf640000", + "3f540000", + "bea00000", + "3fa60000", + "3f640000", + "3e600000", + "be980000", + "bf280000", + "3f5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "bde00000", + "bef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "3ea80000", + "3f980000", + "be200000", + "bfac0000", + "3f240000", + "3e700000", + "bf740000", + "3fc00000", + "3f8c0000", + "3f580000", + "bf400000", + "3f500000", + "3f840000", + "3fb40000", + "bf8a0000", + "3ec80000", + "3ee80000", + "3fba0000", + "bed00000", + "3f680000", + "3f240000", + "3f7c0000", + "be800000", + "bfaa0000", + "3f440000", + "be000000", + "bf1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "3fbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "3e100000", + "bf700000", + "bf900000", + "bc800000", + "3fa00000", + "beb00000", + "bf9e0000", + "bd400000", + "bf960000", + "3f600000", + "3d800000", + "3f180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "bfbe0000", + "bfa00000", + "bf500000", + "be600000", + "3f080000", + "bfb80000", + "bf040000", + "3f340000", + "bf700000", + "3f180000", + "3f3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "bf800000", + "3f1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "bf1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "3dc00000", + "bf8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "3f340000", + "3f040000", + "3fb80000", + "3f080000", + "be600000", + "bf500000", + "3fa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "3f8c0000", + "bf180000", + "3d800000", + "3f600000", + "bf960000", + "3d400000", + "3f9e0000", + "beb00000", + "3fa00000", + "bc800000", + "3f900000", + "3f700000", + "3e100000", + "bf000000", + "bf7c0000", + "3fa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "3f1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "be800000", + "bf7c0000", + "bf240000", + "3f680000", + "bed00000", + "3fba0000", + "bee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "bf500000", + "3f400000", + "3f580000", + "3f8c0000", + "3fc00000", + "3f740000", + "be700000", + "3f240000", + "bfac0000", + "be200000", + "bf980000", + "bea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "bf9a0000", + "3ef00000", + "bde00000", + "bf080000", + "bf4c0000", + "3f680000", + "bf5c0000", + "bf280000", + "be980000", + "3e600000", + "bf640000", + "bfa60000", + "bea00000", + "3f540000", + "bf640000", + "bf100000", + "bf580000", + "3f6c0000", + "be300000", + "bf8e0000", + "bf900000", + "3f000000", + "3d000000", + "be900000", + "bee00000", + "3ee00000", + "be900000", + "3d000000", + "3f000000", + "3f900000", + "3f8e0000", + "be300000", + "3f6c0000", + "bf580000", + "3f100000", + "3f640000", + "3f540000", + "bea00000", + "bfa60000", + "3f640000", + "be600000", + "be980000", + "bf280000", + "bf5c0000", + "bf680000", + "3f4c0000", + "bf080000", + "bde00000", + "3ef00000", + "3f9a0000", + "3f6c0000", + "3e000000", + "3faa0000", + "bea80000", + "3f980000", + "3e200000", + "bfac0000", + "3f240000", + "be700000", + "bf740000", + "bfc00000", + "3f8c0000", + "3f580000", + "3f400000", + "3f500000", + "bf840000", + "3fb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "bfba0000", + "bed00000", + "3f680000", + "bf240000", + "3f7c0000", + "3e800000", + "bfaa0000", + "3f440000", + "3e000000", + "bf1c0000", + "3f880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "3f7c0000", + "bf000000", + "3e100000", + "3f700000", + "bf900000", + "3c800000", + "3fa00000", + "beb00000", + "3f9e0000", + "bd400000", + "3f960000", + "3f600000", + "3d800000", + "bf180000", + "bf8c0000", + "3fb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "3f500000", + "be600000", + "3f080000", + "3fb80000", + "bf040000", + "bf340000", + "bf700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "bdc00000", + "bf440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "bec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "3f800000", + "3fbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "bf180000", + "bf700000", + "3f340000", + "bf040000", + "3fb80000", + "bf080000", + "be600000", + "bf500000", + "bfa00000", + "3fbe0000", + "bfae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "bd800000", + "3f600000", + "bf960000", + "bd400000", + "3f9e0000", + "3eb00000", + "3fa00000", + "bc800000", + "bf900000", + "3f700000", + "be100000", + "bf000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "3fc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "3e000000", + "bf440000", + "bfaa0000", + "be800000", + "3f7c0000", + "bf240000", + "bf680000", + "bed00000", + "3fba0000", + "3ee80000", + "bec80000", + "3f8a0000", + "3fb40000", + "3f840000", + "3f500000", + "3f400000", + "bf580000", + "3f8c0000", + "3fc00000", + "bf740000", + "be700000", + "bf240000", + "bfac0000", + "be200000", + "3f980000", + "bea80000", + "bfaa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "3de00000", + "bf080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "3f280000", + "be980000", + "3e600000", + "3f640000", + "bfa60000", + "3ea00000", + "3f540000", + "bf640000", + "3f100000", + "bf580000", + "bf6c0000", + "be300000", + "bf8e0000", + "3f900000", + "3f000000", + "bd000000", + "be900000", + "bee00000", + "bee00000", + "be900000", + "bd000000", + "3f000000", + "3f900000", + "bf8e0000", + "be300000", + "bf6c0000", + "bf580000", + "3f100000", + "bf640000", + "3f540000", + "3ea00000", + "bfa60000", + "3f640000", + "3e600000", + "be980000", + "3f280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "3de00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "bfaa0000", + "bea80000", + "3f980000", + "be200000", + "bfac0000", + "bf240000", + "be700000", + "bf740000", + "3fc00000", + "3f8c0000", + "bf580000", + "3f400000", + "3f500000", + "3f840000", + "3fb40000", + "3f8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "bed00000", + "bf680000", + "bf240000", + "3f7c0000", + "be800000", + "bfaa0000", + "bf440000", + "3e000000", + "bf1c0000", + "bf880000", + "bfae0000", + "3fc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "be100000", + "3f700000", + "bf900000", + "bc800000", + "3fa00000", + "3eb00000", + "3f9e0000", + "bd400000", + "bf960000", + "3f600000", + "bd800000", + "bf180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "bfae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "be600000", + "bf080000", + "3fb80000", + "bf040000", + "3f340000", + "bf700000", + "bf180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "3fbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "3e980000", + "bec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "bdc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "bf340000", + "bf040000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "3f500000", + "bf400000", + "3f580000", + "3f8c0000", + "3fc00000", + "bf740000", + "3e700000", + "3f240000", + "bfac0000", + "be200000", + "3f980000", + "3ea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "bef00000", + "bde00000", + "bf080000", + "bf4c0000", + "bf680000", + "3f5c0000", + "bf280000", + "be980000", + "3e600000", + "3f640000", + "3fa60000", + "bea00000", + "3f540000", + "bf640000", + "3f100000", + "3f580000", + "3f6c0000", + "be300000", + "bf8e0000", + "3f900000", + "bf000000", + "3d000000", + "be900000", + "bee00000", + "bee00000", + "3e900000", + "3d000000", + "3f000000", + "3f900000", + "bf8e0000", + "3e300000", + "3f6c0000", + "bf580000", + "3f100000", + "bf640000", + "bf540000", + "bea00000", + "bfa60000", + "3f640000", + "3e600000", + "3e980000", + "bf280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "3f080000", + "bde00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "be000000", + "3faa0000", + "bea80000", + "3f980000", + "be200000", + "3fac0000", + "3f240000", + "be700000", + "bf740000", + "3fc00000", + "bf8c0000", + "3f580000", + "3f400000", + "3f500000", + "3f840000", + "bfb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "3ed00000", + "3f680000", + "bf240000", + "3f7c0000", + "be800000", + "3faa0000", + "3f440000", + "3e000000", + "bf1c0000", + "bf880000", + "3fae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "3f000000", + "3e100000", + "3f700000", + "bf900000", + "bc800000", + "bfa00000", + "beb00000", + "3f9e0000", + "bd400000", + "bf960000", + "bf600000", + "3d800000", + "bf180000", + "bf8c0000", + "bfb80000", + "bfb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "3e600000", + "3f080000", + "3fb80000", + "bf040000", + "3f340000", + "3f700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "3f440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "be980000", + "3ec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "3f440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "3f700000", + "3f340000", + "bf040000", + "3fb80000", + "3f080000", + "3e600000", + "bf500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "bfb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "3d800000", + "bf600000", + "bf960000", + "bd400000", + "3f9e0000", + "beb00000", + "bfa00000", + "bc800000", + "bf900000", + "3f700000", + "3e100000", + "3f000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "3fae0000", + "bf880000", + "bf1c0000", + "3e000000", + "3f440000", + "3faa0000", + "be800000", + "3f7c0000", + "bf240000", + "3f680000", + "3ed00000", + "3fba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "bfb40000", + "3f840000", + "3f500000", + "3f400000", + "3f580000", + "bf8c0000", + "3fc00000", + "bf740000", + "be700000", + "3f240000", + "3fac0000", + "be200000", + "3f980000", + "bea80000", + "3faa0000", + "be000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "3f080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "3e980000", + "3e600000", + "3f640000", + "bfa60000", + "bea00000", + "bf540000", + "bf640000", + "3f100000", + "bf580000", + "3f6c0000", + "3e300000", + "bf8e0000", + "3f900000", + "3f000000", + "3d000000", + "3e900000", + "bee00000", + "bee00000", + "be900000", + "3d000000", + "bf000000", + "3f900000", + "bf8e0000", + "be300000", + "3f6c0000", + "3f580000", + "3f100000", + "bf640000", + "3f540000", + "bea00000", + "3fa60000", + "3f640000", + "3e600000", + "be980000", + "bf280000", + "3f5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "bde00000", + "bef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "3ea80000", + "3f980000", + "be200000", + "bfac0000", + "3f240000", + "3e700000", + "bf740000", + "3fc00000", + "3f8c0000", + "3f580000", + "bf400000", + "3f500000", + "3f840000", + "3fb40000", + "bf8a0000", + "3ec80000", + "3ee80000", + "3fba0000", + "bed00000", + "3f680000", + "3f240000", + "3f7c0000", + "be800000", + "bfaa0000", + "3f440000", + "be000000", + "bf1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "3fbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "3e100000", + "bf700000", + "bf900000", + "bc800000", + "3fa00000", + "beb00000", + "bf9e0000", + "bd400000", + "bf960000", + "3f600000", + "3d800000", + "3f180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "bfbe0000", + "bfa00000", + "bf500000", + "be600000", + "3f080000", + "bfb80000", + "bf040000", + "3f340000", + "bf700000", + "3f180000", + "3f3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "bf800000", + "3f1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "bf1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "3dc00000", + "bf8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "3f340000", + "3f040000", + "3fb80000", + "3f080000", + "be600000", + "bf500000", + "3fa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "3f8c0000", + "bf180000", + "3d800000", + "3f600000", + "bf960000", + "3d400000", + "3f9e0000", + "beb00000", + "3fa00000", + "bc800000", + "3f900000", + "3f700000", + "3e100000", + "bf000000", + "bf7c0000", + "3fa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "3f1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "be800000", + "bf7c0000", + "bf240000", + "3f680000", + "bed00000", + "3fba0000", + "bee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "3f840000", + "bf500000", + "3f400000", + "3f580000", + "3f8c0000", + "3fc00000", + "3f740000", + "be700000", + "3f240000", + "bfac0000", + "be200000", + "bf980000", + "bea80000", + "3faa0000", + "3e000000", + "bf6c0000", + "bf9a0000", + "3ef00000", + "bde00000", + "bf080000", + "bf4c0000", + "3f680000", + "bf5c0000", + "bf280000", + "be980000", + "3e600000", + "bf640000", + "bfa60000", + "bea00000", + "3f540000", + "bf640000", + "bf100000", + "bf580000", + "3f6c0000", + "be300000", + "bf8e0000", + "bf900000", + "3f000000", + "3d000000", + "be900000", + "bee00000", + "3ee00000", + "be900000", + "3d000000", + "3f000000", + "3f900000", + "3f8e0000", + "be300000", + "3f6c0000", + "bf580000", + "3f100000", + "3f640000", + "3f540000", + "bea00000", + "bfa60000", + "3f640000", + "be600000", + "be980000", + "bf280000", + "bf5c0000", + "bf680000", + "3f4c0000", + "bf080000", + "bde00000", + "3ef00000", + "3f9a0000", + "3f6c0000", + "3e000000", + "3faa0000", + "bea80000", + "3f980000", + "3e200000", + "bfac0000", + "3f240000", + "be700000", + "bf740000", + "bfc00000", + "3f8c0000", + "3f580000", + "3f400000", + "3f500000", + "bf840000", + "3fb40000", + "bf8a0000", + "bec80000", + "3ee80000", + "bfba0000", + "bed00000", + "3f680000", + "bf240000", + "3f7c0000", + "3e800000", + "bfaa0000", + "3f440000", + "3e000000", + "bf1c0000", + "3f880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "bfa80000", + "3f7c0000", + "bf000000", + "3e100000", + "3f700000", + "bf900000", + "3c800000", + "3fa00000", + "beb00000", + "3f9e0000", + "bd400000", + "3f960000", + "3f600000", + "3d800000", + "bf180000", + "bf8c0000", + "3fb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "bfa00000", + "3f500000", + "be600000", + "3f080000", + "3fb80000", + "bf040000", + "bf340000", + "bf700000", + "3f180000", + "bf3c0000", + "3f8e0000", + "bdc00000", + "bf440000", + "bfbc0000", + "3f800000", + "3f1c0000", + "bec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "3f800000", + "3fbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "bf3c0000", + "bf180000", + "bf700000", + "3f340000", + "bf040000", + "3fb80000", + "bf080000", + "be600000", + "bf500000", + "bfa00000", + "3fbe0000", + "bfae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "bf180000", + "bd800000", + "3f600000", + "bf960000", + "bd400000", + "3f9e0000", + "3eb00000", + "3fa00000", + "bc800000", + "bf900000", + "3f700000", + "be100000", + "bf000000", + "bf7c0000", + "bfa80000", + "bfbe0000", + "3fc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "3e000000", + "bf440000", + "bfaa0000", + "be800000", + "3f7c0000", + "bf240000", + "bf680000", + "bed00000", + "3fba0000", + "3ee80000", + "bec80000", + "3f8a0000", + "3fb40000", + "3f840000", + "3f500000", + "3f400000", + "bf580000", + "3f8c0000", + "3fc00000", + "bf740000", + "be700000", + "bf240000", + "bfac0000", + "be200000", + "3f980000", + "bea80000", + "bfaa0000", + "3e000000", + "bf6c0000", + "3f9a0000", + "3ef00000", + "3de00000", + "bf080000", + "bf4c0000", + "bf680000", + "bf5c0000", + "3f280000", + "be980000", + "3e600000", + "3f640000", + "bfa60000", + "3ea00000", + "3f540000", + "bf640000", + "3f100000", + "bf580000", + "bf6c0000", + "be300000", + "bf8e0000", + "3f900000", + "3f000000", + "bd000000", + "be900000", + "bee00000", + "bee00000", + "be900000", + "bd000000", + "3f000000", + "3f900000", + "bf8e0000", + "be300000", + "bf6c0000", + "bf580000", + "3f100000", + "bf640000", + "3f540000", + "3ea00000", + "bfa60000", + "3f640000", + "3e600000", + "be980000", + "3f280000", + "bf5c0000", + "bf680000", + "bf4c0000", + "bf080000", + "3de00000", + "3ef00000", + "3f9a0000", + "bf6c0000", + "3e000000", + "bfaa0000", + "bea80000", + "3f980000", + "be200000", + "bfac0000", + "bf240000", + "be700000", + "bf740000", + "3fc00000", + "3f8c0000", + "bf580000", + "3f400000", + "3f500000", + "3f840000", + "3fb40000", + "3f8a0000", + "bec80000", + "3ee80000", + "3fba0000", + "bed00000", + "bf680000", + "bf240000", + "3f7c0000", + "be800000", + "bfaa0000", + "bf440000", + "3e000000", + "bf1c0000", + "bf880000", + "bfae0000", + "3fc00000", + "bfbe0000", + "bfa80000", + "bf7c0000", + "bf000000", + "be100000", + "3f700000", + "bf900000", + "bc800000", + "3fa00000", + "3eb00000", + "3f9e0000", + "bd400000", + "bf960000", + "3f600000", + "bd800000", + "bf180000", + "bf8c0000", + "bfb80000", + "3fb20000", + "bfae0000", + "3fbe0000", + "bfa00000", + "bf500000", + "be600000", + "bf080000", + "3fb80000", + "bf040000", + "3f340000", + "bf700000", + "bf180000", + "bf3c0000", + "3f8e0000", + "3dc00000", + "bf440000", + "3fbc0000", + "3f800000", + "3f1c0000", + "3ec00000", + "3e980000", + "bec00000", + "3f1c0000", + "3f800000", + "bfbc0000", + "bf440000", + "bdc00000", + "3f8e0000", + "bf3c0000", + "3f180000", + "bf700000", + "bf340000", + "bf040000", + "3fb80000", + "3f080000", + "be600000", + "3f500000", + "bfa00000", + "3fbe0000", + "3fae0000", + "3fb20000", + "3fb80000", + "bf8c0000", + "bf180000", + "3d800000", + "3f600000", + "3f960000", + "bd400000", + "3f9e0000", + "beb00000", + "3fa00000", + "3c800000", + "bf900000", + "3f700000", + "3e100000", + "bf000000", + "3f7c0000", + "bfa80000", + "bfbe0000", + "bfc00000", + "bfae0000", + "3f880000", + "bf1c0000", + "3e000000", + "3f440000", + "bfaa0000", + "3e800000", + "3f7c0000", + "bf240000", + "3f680000", + "bed00000", + "bfba0000", + "3ee80000", + "bec80000", + "bf8a0000", + "3fb40000", + "bf840000", + "3f500000", + "3f400000", + "3f580000", + "3f8c0000", + "bfc00000", + "bf740000", + "be700000", + "3f240000", + "bfac0000", + "3e200000", + "3f980000", + "bea80000", + "3faa0000", + "3e000000", + "3f6c0000", + "3f9a0000", + "3ef00000", + "bde00000", + "bf080000", + "3f4c0000", + "bf680000", + "bf5c0000", + "bf280000", + "be980000", + "be600000", + "3f640000", + "bfa60000", + "bea00000", + "3f540000", + "3f640000", + "3f100000", + "bf580000", + "3f6c0000", + "be300000", + "3f8e0000", + "3f900000", + "3f000000", + "3d000000", + "be900000", + "3ee00000", + "bee00000", + "be900000", + "3d000000", + "3f000000", + "bf900000", + "bf8e0000", + "be300000", + "3f6c0000", + "bf580000", + "bf100000", + "bf640000", + "3f540000", + "bea00000", + "bfa60000", + "bf640000", + "3e600000", + "be980000", + "bf280000", + "bf5c0000", + "3f680000", + "bf4c0000", + "bf080000", + "bde00000", + "3ef00000", + "bf9a0000", + "bf6c0000", + "3e000000", + "3faa0000", + "bea80000", + "bf980000", + "be200000", + "bfac0000", + "3f240000", + "be700000", + "3f740000", + "3fc00000", + "3f8c0000", + "3f580000", + "3f400000", + "bf500000", + "3f840000", + "3fb40000", + "bf8a0000", + "bec80000", + "bee80000", + "3fba0000", + "bed00000", + "3f680000", + "bf240000", + "bf7c0000", + "be800000", + "bfaa0000", + "3f440000", + "3e000000", + "3f1c0000", + "bf880000", + "bfae0000", + "bfc00000", + "bfbe0000", + "3fa80000", + "bf7c0000", + "bf000000", + "3e100000", + "3f700000", + "3f900000", + "bc800000", + "3fa00000", + "beb00000", + "3f9e0000", + "3d400000", + "bf960000", + "3f600000", + "3d800000", + "bf180000", + "3f8c0000", + "bfb80000", + "3fb20000", + "3fae0000", + "3fbe0000", + "3fa00000", + "bf500000", + "be600000", + "3f080000", + "3fb80000", + "3f040000", + "3f340000", + "bf700000", + "3f180000", + "bf3c0000", + "bf8e0000", + "3dc00000", + "bf440000", + "bfbc0000", + "3f800000", + "bf1c0000", + "3ec00000", + "3e980000", + "3ec00000", + "3f1c0000", + "bf800000", + "bfbc0000", + "bf440000", + "3dc00000", + "3f8e0000", + "3f3c0000", + "3f180000", + "bf700000", + "3f340000", + "bf040000", + "bfb80000", + "3f080000", + "be600000", + "bf500000", + "bfa00000", + "bfbe0000", + "3fae0000", + "3fb20000", + "bfb80000", + "bf8c0000", + "3f180000", + "3d800000", + "3f600000", + "bf960000", + "bd400000", + "bf9e0000", + "beb00000", + "3fa00000", + "bc800000", + "bf900000", + "bf700000", + "3e100000", + "bf000000", + "bf7c0000", + "bfa80000", + "3fbe0000", + "bfc00000", + "bfae0000", + "bf880000", + "bf1c0000", + "be000000", + "3f440000", + "bfaa0000", + "be800000", + "3f7c0000", + "3f240000", + "3f680000", + "bed00000", + "3fba0000", + "3ee80000", + "3ec80000", + "bf8a0000", + "3fb40000" + ], + "dtype": "float32", + "shape": [ + 2, + 4096 + ] + } + }, + "name": "ordered-reduction-d4096" + } + ], + "tolerance": { + "atol": 1e-05, + "rtol": 0.0, + "status": "preview-hypothesis" + } + }, + "HRR_F64_V1": { + "dtype": "float64", + "edge_cases": [ + { + "dimension": 3, + "expected": { + "finite_validation_status": "LECORE_ENONFINITE", + "first_nan_cleanup_index": 1, + "first_nan_cleanup_score_class": "nan", + "impulse_bind": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "raw_nan_result_class": "nan", + "shifted_impulse_bind": { + "bits": [ + "bff2c00000000000", + "bfe2800000000000", + "3fee800000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "special_involution": { + "bits": [ + "7ff8000000000042", + "7ff0000000000000", + "8000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "special_permute_minus_two": { + "bits": [ + "7ff0000000000000", + "7ff8000000000042", + "8000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "tie_cleanup_index": 0, + "zero_nan_cosine": { + "bits": [ + "0000000000000000" + ], + "dtype": "float64", + "shape": [] + }, + "zero_normalize": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "zero_sum_bundle": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + } + }, + "inputs": { + "finite": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "first_nan_candidates": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "7ff8000000000000", + "3fee800000000000", + "bff2c00000000000", + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "impulse": { + "bits": [ + "3ff0000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "inf_vector": { + "bits": [ + "7ff0000000000000", + "3fee800000000000", + "bff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "nan_vector": { + "bits": [ + "7ff8000000000000", + "3fee800000000000", + "bff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "reindex_special": { + "bits": [ + "7ff8000000000042", + "8000000000000000", + "7ff0000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "shifted_impulse": { + "bits": [ + "0000000000000000", + "3ff0000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "ties": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "3fe2800000000000", + "bfee800000000000", + "3ff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "zero": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "zero_sum_rows": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "3fe2800000000000", + "bfee800000000000", + "3ff2c00000000000" + ], + "dtype": "float64", + "shape": [ + 2, + 3 + ] + } + } + }, + { + "dimension": 8, + "expected": { + "finite_validation_status": "LECORE_ENONFINITE", + "first_nan_cleanup_index": 1, + "first_nan_cleanup_score_class": "nan", + "impulse_bind": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "raw_nan_result_class": "nan", + "shifted_impulse_bind": { + "bits": [ + "3fb4000000000000", + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "special_involution": { + "bits": [ + "7ff8000000000042", + "3fb4000000000000", + "3fdf000000000000", + "bfec800000000000", + "bff2400000000000", + "fff0000000000000", + "7ff0000000000000", + "8000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "special_permute_minus_two": { + "bits": [ + "7ff0000000000000", + "fff0000000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "7ff8000000000042", + "8000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "tie_cleanup_index": 0, + "zero_nan_cosine": { + "bits": [ + "0000000000000000" + ], + "dtype": "float64", + "shape": [] + }, + "zero_normalize": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "zero_sum_bundle": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + } + }, + "inputs": { + "finite": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "first_nan_candidates": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "7ff8000000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "impulse": { + "bits": [ + "3ff0000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "inf_vector": { + "bits": [ + "7ff0000000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "nan_vector": { + "bits": [ + "7ff8000000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "reindex_special": { + "bits": [ + "7ff8000000000042", + "8000000000000000", + "7ff0000000000000", + "fff0000000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "shifted_impulse": { + "bits": [ + "0000000000000000", + "3ff0000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "ties": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "3fe2800000000000", + "bfee800000000000", + "3ff2c00000000000", + "3ff3c00000000000", + "3ff2400000000000", + "3fec800000000000", + "bfdf000000000000", + "bfb4000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "zero": { + "bits": [ + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000", + "0000000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "zero_sum_rows": { + "bits": [ + "bfe2800000000000", + "3fee800000000000", + "bff2c00000000000", + "bff3c00000000000", + "bff2400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fb4000000000000", + "3fe2800000000000", + "bfee800000000000", + "3ff2c00000000000", + "3ff3c00000000000", + "3ff2400000000000", + "3fec800000000000", + "bfdf000000000000", + "bfb4000000000000" + ], + "dtype": "float64", + "shape": [ + 2, + 8 + ] + } + } + } + ], + "finite_cases": [ + { + "dimension": 1, + "expected": { + "bind": { + "bits": [ + "bf8b800000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "bind_batch": { + "bits": [ + "bf8b800000000000", + "3f94c00000000000", + "bfa4c00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + }, + "bind_fixed": { + "bits": [ + "bf8b800000000000", + "bff1d50000000000", + "3f9b800000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + }, + "bundle": { + "bits": [ + "3ff0000000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3ff0000000000000" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bff0000000000000" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bff0000000000000", + "bff0000000000000", + "3ff0000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bf8b800000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "bf8b800000000000", + "bff1d50000000000", + "3f9b800000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "normalize": { + "bits": [ + "bff0000000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "permute": { + "bits": [ + "bfeb800000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "unbind": { + "bits": [ + "bf8b800000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "unbind_all": { + "bits": [ + "bf8b800000000000", + "bff1d50000000000", + "3f9b800000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "b": { + "bits": [ + "3f90000000000000" + ], + "dtype": "float64", + "shape": [ + 1 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "3f90000000000000", + "3ff4c00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3ff4c00000000000", + "bfa0000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3ff4c00000000000", + "bfa0000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 1 + ] + } + }, + "name": "formula-d1", + "shift": -3 + }, + { + "dimension": 2, + "expected": { + "bind": { + "bits": [ + "bfd2640000000000", + "bfe6160000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "bind_batch": { + "bits": [ + "bfd2640000000000", + "bfe6160000000000", + "bfe07c0000000000", + "3ff05e0000000000", + "3fea400000000000", + "bffa400000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + }, + "bind_fixed": { + "bits": [ + "bfd2640000000000", + "bfe6160000000000", + "bfec460000000000", + "3fc0d80000000000", + "3fdde80000000000", + "3ff1ca0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + }, + "bundle": { + "bits": [ + "3fe7c1af3614b3c3", + "bfe570596f3e63c9" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3fd935f94a2a4a4b" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bfd8edea73b30b4e" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bfd8edea73b30b4e", + "bfe4ea07bff48ccc", + "3fd935f94a2a4a4b" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bfd2640000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "bfd2640000000000", + "bfec460000000000", + "3fdde80000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "normalize": { + "bits": [ + "bfedb614bfce6a6a", + "bfd7c4dd663ebb88" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "permute": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "unbind": { + "bits": [ + "bfd2640000000000", + "bfe6160000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "unbind_all": { + "bits": [ + "bfd2640000000000", + "bfe6160000000000", + "bfec460000000000", + "3fc0d80000000000", + "3fdde80000000000", + "3ff1ca0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "b": { + "bits": [ + "3f90000000000000", + "3fe9800000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3f90000000000000", + "3fe9800000000000", + "3ff4c00000000000", + "bfe5800000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfa0000000000000", + "bff4800000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfa0000000000000", + "bff4800000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 2 + ] + } + }, + "name": "formula-d2", + "shift": -4 + }, + { + "dimension": 3, + "expected": { + "bind": { + "bits": [ + "3fe6080000000000", + "bff1c50000000000", + "3feaa20000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "bind_batch": { + "bits": [ + "3fe6080000000000", + "bff1c50000000000", + "3feaa20000000000", + "3fdd4c0000000000", + "3ffb420000000000", + "c001a48000000000", + "3fea320000000000", + "bff7fe0000000000", + "3fe10e0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "bind_fixed": { + "bits": [ + "3fe6080000000000", + "bff1c50000000000", + "3feaa20000000000", + "bff2700000000000", + "bfa5e00000000000", + "3ff1cf0000000000", + "bfd3580000000000", + "3ff0650000000000", + "3fe5120000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "bundle": { + "bits": [ + "3fde5b0573b424be", + "bfdb64df74e74061", + "bfe89e1bd6072a49" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3fd2ed73a06249b9" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bfde91ad3b4d3ef4" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bfde91ad3b4d3ef4", + "bfe62dfbcb880845", + "3fd2ed73a06249b9" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bfe6a60000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "bfe6a60000000000", + "bff0ed0000000000", + "3fd8540000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000", + "3fd5000000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "normalize": { + "bits": [ + "bfec00ed012c8d7f", + "bfd6672400f07133", + "3fd56273d259e06b" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "permute": { + "bits": [ + "3fd5000000000000", + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "unbind": { + "bits": [ + "bfe6a60000000000", + "3ff5b70000000000", + "bfcea00000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "unbind_all": { + "bits": [ + "bfe6a60000000000", + "3ff5b70000000000", + "bfcea00000000000", + "bff0ed0000000000", + "bfcad80000000000", + "3ff2f80000000000", + "3fd8540000000000", + "bfc7380000000000", + "3ff2ea0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "b": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 3 + ] + } + }, + "name": "formula-d3", + "shift": -5 + }, + { + "dimension": 7, + "expected": { + "bind": { + "bits": [ + "bfe4ba0000000000", + "3ffda20000000000", + "3ff89b0000000000", + "bfcef00000000000", + "3fe3400000000000", + "c000b78000000000", + "bff0d30000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "bind_batch": { + "bits": [ + "bfe4ba0000000000", + "3ffda20000000000", + "3ff89b0000000000", + "bfcef00000000000", + "3fe3400000000000", + "c000b78000000000", + "bff0d30000000000", + "3fdb300000000000", + "3fcb600000000000", + "c011a40000000000", + "4004160000000000", + "bfd9e00000000000", + "3ff1a90000000000", + "bfd5940000000000", + "3fe8260000000000", + "bff7110000000000", + "40041d0000000000", + "4009d60000000000", + "3ff1280000000000", + "c009b28000000000", + "bff5830000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + }, + "bind_fixed": { + "bits": [ + "bfe4ba0000000000", + "3ffda20000000000", + "3ff89b0000000000", + "bfcef00000000000", + "3fe3400000000000", + "c000b78000000000", + "bff0d30000000000", + "bff6cf0000000000", + "3fc0d80000000000", + "40083c0000000000", + "4002fc8000000000", + "3ffda20000000000", + "bffd040000000000", + "c0107fc000000000", + "4009da0000000000", + "3fdf7c0000000000", + "c00b7e0000000000", + "c007bb0000000000", + "bff16b0000000000", + "3ff7170000000000", + "4003258000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + }, + "bundle": { + "bits": [ + "3fd428b348431ac1", + "bfd23137a51d5695", + "bfe05933bc29eeac", + "3fba8d051a7dd84f", + "3fd8d478ebbccca9", + "bfe34c6d30e294ee", + "3fc799cba5c5320d" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3fe41f7aa2ac049f" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bfce3dbcd33bcb3e" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bfce3dbcd33bcb3e", + "bfe513b68563d846", + "3fe41f7aa2ac049f" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bff2eb0000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "bff2eb0000000000", + "c00c550000000000", + "400bb98000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "normalize": { + "bits": [ + "bfd801965f4fb6fa", + "bfc334784c3fc595", + "3fc254fe77542554", + "3fe0264de30714b2", + "3fd8715349c5871b", + "3fbdae2e474b48a0", + "bfe44bd096664de6" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "permute": { + "bits": [ + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "unbind": { + "bits": [ + "bff2eb0000000000", + "bfeb6e0000000000", + "bff9160000000000", + "bfd2840000000000", + "4004720000000000", + "3fdf380000000000", + "3fea0e0000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "unbind_all": { + "bits": [ + "bff2eb0000000000", + "bfeb6e0000000000", + "bff9160000000000", + "bfd2840000000000", + "4004720000000000", + "3fdf380000000000", + "3fea0e0000000000", + "c00c550000000000", + "bff8730000000000", + "bff2a10000000000", + "4005798000000000", + "400a140000000000", + "3fff4b0000000000", + "bffa4c0000000000", + "400bb98000000000", + "4006f80000000000", + "bfd5580000000000", + "c00adb8000000000", + "c006988000000000", + "bfdc740000000000", + "3fe6200000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "b": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000" + ], + "dtype": "float64", + "shape": [ + 7 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 7 + ] + } + }, + "name": "formula-d7", + "shift": -9 + }, + { + "dimension": 8, + "expected": { + "bind": { + "bits": [ + "4005340000000000", + "bfbdf00000000000", + "bfd2680000000000", + "3ff6910000000000", + "c0003e8000000000", + "bfc6300000000000", + "bff0f70000000000", + "bfd9180000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "bind_batch": { + "bits": [ + "4005340000000000", + "bfbdf00000000000", + "bfd2680000000000", + "3ff6910000000000", + "c0003e8000000000", + "bfc6300000000000", + "bff0f70000000000", + "bfd9180000000000", + "bfde6c0000000000", + "bff3b90000000000", + "bf88800000000000", + "bfe7460000000000", + "4000e88000000000", + "3fc0580000000000", + "bfd8640000000000", + "3fa5a00000000000", + "bfd2cc0000000000", + "3fb9900000000000", + "3fecaa0000000000", + "40089c8000000000", + "bfe5ee0000000000", + "c000838000000000", + "bff4930000000000", + "3ff46d0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "bind_fixed": { + "bits": [ + "4005340000000000", + "bfbdf00000000000", + "bfd2680000000000", + "3ff6910000000000", + "c0003e8000000000", + "bfc6300000000000", + "bff0f70000000000", + "bfd9180000000000", + "bff3070000000000", + "4000a90000000000", + "3ff3410000000000", + "4010c78000000000", + "bff0670000000000", + "bfe7bc0000000000", + "c0107ac000000000", + "bfd9e80000000000", + "bfe8620000000000", + "c006f28000000000", + "bfd3180000000000", + "bfbe900000000000", + "bfdc600000000000", + "bfe9700000000000", + "40033d8000000000", + "40070d0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "bundle": { + "bits": [ + "3fd41efb1cc55d12", + "bfd22872392efc94", + "bfe05151e3d2021d", + "3fba8038006e16ae", + "3fd8c880394a823f", + "bfe3431f393392db", + "3fc78e6aab0c85f0", + "3faf688e396607eb" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3fe3f2bbe072bbe0" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bfce36b5d5d9b387" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bfce36b5d5d9b387", + "bfe4ffdeffbd97ca", + "3fe3f2bbe072bbe0" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "bff30f0000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "bff30f0000000000", + "c00c4b0000000000", + "400bd18000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000", + "3fa0000000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "normalize": { + "bits": [ + "bfd801040ac03a41", + "bfc334033bccfb67", + "3fc2548eb9151e85", + "3fe025eb72497651", + "3fd870be4c1c28b2", + "3fbdad795c6b55fd", + "bfe44b54df32cf81", + "3f8bee9056fb9c39" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "permute": { + "bits": [ + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "unbind": { + "bits": [ + "bff30f0000000000", + "3fd2800000000000", + "c003db0000000000", + "3fc5080000000000", + "3ff6040000000000", + "bfef160000000000", + "3ff91b0000000000", + "3ff3f00000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "unbind_all": { + "bits": [ + "bff30f0000000000", + "3fd2800000000000", + "c003db0000000000", + "3fc5080000000000", + "3ff6040000000000", + "bfef160000000000", + "3ff91b0000000000", + "3ff3f00000000000", + "c00c4b0000000000", + "bfec420000000000", + "c004250000000000", + "4007e58000000000", + "4001390000000000", + "4007c78000000000", + "bff3aa0000000000", + "3fabe00000000000", + "400bd18000000000", + "3ff8870000000000", + "bfe7240000000000", + "bffdd70000000000", + "3fd5c80000000000", + "bfe56c0000000000", + "c0018b8000000000", + "3fb9a00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "b": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000" + ], + "dtype": "float64", + "shape": [ + 8 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000", + "3fd8000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000", + "3fd8000000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 8 + ] + } + }, + "name": "formula-d8", + "shift": -10 + }, + { + "dimension": 64, + "expected": { + "bind": { + "bits": [ + "bf79000000000000", + "c012374000000000", + "4020532000000000", + "bff8e00000000000", + "c01dad8000000000", + "bfba700000000000", + "bfffda0000000000", + "4019610000000000", + "4013174000000000", + "bfe6e20000000000", + "3ff9760000000000", + "c018b04000000000", + "4023554000000000", + "c020272000000000", + "c01c0c8000000000", + "c010418000000000", + "c0326fc000000000", + "bfd79c0000000000", + "4016044000000000", + "401cebc000000000", + "40206e8000000000", + "4010dec000000000", + "c00b848000000000", + "c02821e000000000", + "c0223b0000000000", + "bff7bb0000000000", + "c020d9e000000000", + "c010af4000000000", + "3fe1c00000000000", + "4004f80000000000", + "4002200000000000", + "c0238c4000000000", + "bfe39e0000000000", + "c02839c000000000", + "bfe9120000000000", + "4009020000000000", + "c01c8e4000000000", + "bfe3800000000000", + "4013f1c000000000", + "401a684000000000", + "4002b40000000000", + "c027e0c000000000", + "bff5d30000000000", + "c02ad66000000000", + "40110c8000000000", + "401adc4000000000", + "bfbd900000000000", + "40293ee000000000", + "3feace0000000000", + "4022474000000000", + "c01fef8000000000", + "c01afe0000000000", + "c010df8000000000", + "c029194000000000", + "3ffa730000000000", + "401bef0000000000", + "400c2f8000000000", + "3ff5d90000000000", + "c0240ca000000000", + "bffd3d0000000000", + "c01e000000000000", + "40228d4000000000", + "4017c88000000000", + "c0179c8000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "bind_batch": { + "bits": [ + "bf79000000000000", + "c012374000000000", + "4020532000000000", + "bff8e00000000000", + "c01dad8000000000", + "bfba700000000000", + "bfffda0000000000", + "4019610000000000", + "4013174000000000", + "bfe6e20000000000", + "3ff9760000000000", + "c018b04000000000", + "4023554000000000", + "c020272000000000", + "c01c0c8000000000", + "c010418000000000", + "c0326fc000000000", + "bfd79c0000000000", + "4016044000000000", + "401cebc000000000", + "40206e8000000000", + "4010dec000000000", + "c00b848000000000", + "c02821e000000000", + "c0223b0000000000", + "bff7bb0000000000", + "c020d9e000000000", + "c010af4000000000", + "3fe1c00000000000", + "4004f80000000000", + "4002200000000000", + "c0238c4000000000", + "bfe39e0000000000", + "c02839c000000000", + "bfe9120000000000", + "4009020000000000", + "c01c8e4000000000", + "bfe3800000000000", + "4013f1c000000000", + "401a684000000000", + "4002b40000000000", + "c027e0c000000000", + "bff5d30000000000", + "c02ad66000000000", + "40110c8000000000", + "401adc4000000000", + "bfbd900000000000", + "40293ee000000000", + "3feace0000000000", + "4022474000000000", + "c01fef8000000000", + "c01afe0000000000", + "c010df8000000000", + "c029194000000000", + "3ffa730000000000", + "401bef0000000000", + "400c2f8000000000", + "3ff5d90000000000", + "c0240ca000000000", + "bffd3d0000000000", + "c01e000000000000", + "40228d4000000000", + "4017c88000000000", + "c0179c8000000000", + "40138d8000000000", + "c0222e4000000000", + "bfff580000000000", + "40191cc000000000", + "bfeaf40000000000", + "400b268000000000", + "400b4f0000000000", + "bfe39a0000000000", + "4020086000000000", + "c02d342000000000", + "bfff5b0000000000", + "c022c4a000000000", + "c01975c000000000", + "bffb410000000000", + "401e46c000000000", + "3ffaa50000000000", + "c0189fc000000000", + "bfe13e0000000000", + "4009ec8000000000", + "c00d608000000000", + "bff5f20000000000", + "bfb9600000000000", + "c020f06000000000", + "c00c330000000000", + "bff79d0000000000", + "4012ac8000000000", + "c003418000000000", + "4020fbc000000000", + "c00b048000000000", + "400c868000000000", + "4020b34000000000", + "40039d0000000000", + "c0035f0000000000", + "3ffc220000000000", + "401e308000000000", + "c015410000000000", + "c008fe8000000000", + "bfcb100000000000", + "bfff7f0000000000", + "c017238000000000", + "3ff2200000000000", + "4034ea2000000000", + "4010924000000000", + "4011b90000000000", + "bffb480000000000", + "bff53b0000000000", + "c00f698000000000", + "40202fc000000000", + "4016ed8000000000", + "400e158000000000", + "3ff5760000000000", + "402790a000000000", + "c0106bc000000000", + "4012d44000000000", + "401a040000000000", + "bfffdd0000000000", + "4016244000000000", + "401eba4000000000", + "3fe46c0000000000", + "c003850000000000", + "bfe0ee0000000000", + "4002ab8000000000", + "400a5b0000000000", + "4007770000000000", + "bfefb20000000000", + "c01c1e4000000000", + "4020146000000000", + "c033c30000000000", + "4006d58000000000", + "c01fe90000000000", + "c013384000000000", + "c032ea2000000000", + "3fc1480000000000", + "bffb8d0000000000", + "bff27f0000000000", + "c0325bd000000000", + "c0192d0000000000", + "3ff93c0000000000", + "4005e18000000000", + "c0169b0000000000", + "40079b0000000000", + "4015d80000000000", + "c0016d0000000000", + "c00a1d8000000000", + "c0050b8000000000", + "bfbf000000000000", + "3ff0530000000000", + "bfbaf00000000000", + "4014ab8000000000", + "40128c0000000000", + "bff34d0000000000", + "bffeb40000000000", + "4006c88000000000", + "bfcac00000000000", + "40155a8000000000", + "bfd1f40000000000", + "c013bd8000000000", + "4016424000000000", + "4007750000000000", + "4012b74000000000", + "c01da04000000000", + "4028698000000000", + "40168fc000000000", + "c00ceb0000000000", + "c00ddb0000000000", + "4008e70000000000", + "3fe20c0000000000", + "40010b8000000000", + "c00d168000000000", + "401884c000000000", + "3fe0b40000000000", + "3fe28c0000000000", + "c017020000000000", + "4014604000000000", + "4007850000000000", + "c020f4e000000000", + "bffc790000000000", + "400bd10000000000", + "c003ed0000000000", + "c01eefc000000000", + "401827c000000000", + "3ffe110000000000", + "4026f92000000000", + "c005540000000000", + "c020586000000000", + "c014d80000000000", + "3fe88c0000000000", + "c0272a4000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + }, + "bind_fixed": { + "bits": [ + "bf79000000000000", + "c012374000000000", + "4020532000000000", + "bff8e00000000000", + "c01dad8000000000", + "bfba700000000000", + "bfffda0000000000", + "4019610000000000", + "4013174000000000", + "bfe6e20000000000", + "3ff9760000000000", + "c018b04000000000", + "4023554000000000", + "c020272000000000", + "c01c0c8000000000", + "c010418000000000", + "c0326fc000000000", + "bfd79c0000000000", + "4016044000000000", + "401cebc000000000", + "40206e8000000000", + "4010dec000000000", + "c00b848000000000", + "c02821e000000000", + "c0223b0000000000", + "bff7bb0000000000", + "c020d9e000000000", + "c010af4000000000", + "3fe1c00000000000", + "4004f80000000000", + "4002200000000000", + "c0238c4000000000", + "bfe39e0000000000", + "c02839c000000000", + "bfe9120000000000", + "4009020000000000", + "c01c8e4000000000", + "bfe3800000000000", + "4013f1c000000000", + "401a684000000000", + "4002b40000000000", + "c027e0c000000000", + "bff5d30000000000", + "c02ad66000000000", + "40110c8000000000", + "401adc4000000000", + "bfbd900000000000", + "40293ee000000000", + "3feace0000000000", + "4022474000000000", + "c01fef8000000000", + "c01afe0000000000", + "c010df8000000000", + "c029194000000000", + "3ffa730000000000", + "401bef0000000000", + "400c2f8000000000", + "3ff5d90000000000", + "c0240ca000000000", + "bffd3d0000000000", + "c01e000000000000", + "40228d4000000000", + "4017c88000000000", + "c0179c8000000000", + "40173fc000000000", + "40225fc000000000", + "bff0310000000000", + "401b0fc000000000", + "3fe5a00000000000", + "c0224f2000000000", + "c0353ec000000000", + "bfde3c0000000000", + "bffe170000000000", + "3ff7d70000000000", + "c016a54000000000", + "4000ac0000000000", + "c0074a0000000000", + "3fdd840000000000", + "c01d3ec000000000", + "c01f3bc000000000", + "c026476000000000", + "c0093e8000000000", + "400fe00000000000", + "4016e24000000000", + "4011f80000000000", + "3fe44c0000000000", + "c002410000000000", + "c012470000000000", + "bffd6d0000000000", + "c017120000000000", + "c01457c000000000", + "c001010000000000", + "4017730000000000", + "4005c78000000000", + "bfe28c0000000000", + "401410c000000000", + "c01e48c000000000", + "bfcab00000000000", + "c017ec4000000000", + "c004db0000000000", + "c017228000000000", + "4007498000000000", + "402014e000000000", + "3ffed10000000000", + "40040a0000000000", + "c003470000000000", + "bff1760000000000", + "bff4660000000000", + "3fd6900000000000", + "4016830000000000", + "3fef220000000000", + "c01bc80000000000", + "401269c000000000", + "c011e28000000000", + "40007a8000000000", + "c025d66000000000", + "40161d4000000000", + "c007ba0000000000", + "4007550000000000", + "3feb4e0000000000", + "4012194000000000", + "c0050e8000000000", + "bffd520000000000", + "c01793c000000000", + "3fec260000000000", + "401f810000000000", + "c002fc0000000000", + "c0052a8000000000", + "400b758000000000", + "401a2cc000000000", + "c00cd28000000000", + "c01cc64000000000", + "3ffbac0000000000", + "4026894000000000", + "3fe8a60000000000", + "400fc38000000000", + "400c880000000000", + "4001838000000000", + "3f8e000000000000", + "c013a70000000000", + "bff6970000000000", + "40094e0000000000", + "402367e000000000", + "402287c000000000", + "c012fb0000000000", + "c00ad88000000000", + "bff5360000000000", + "3f7d000000000000", + "c02539e000000000", + "c023088000000000", + "40102c8000000000", + "400c580000000000", + "c00cdd8000000000", + "c01075c000000000", + "c0106f0000000000", + "c00afc8000000000", + "400e7b0000000000", + "4002418000000000", + "3fdc880000000000", + "c01ae18000000000", + "4020a5e000000000", + "3ffee00000000000", + "4018b8c000000000", + "bfe65e0000000000", + "4024fec000000000", + "4030bff000000000", + "bfe6420000000000", + "3ff91f0000000000", + "bfed300000000000", + "4022314000000000", + "bfdb8c0000000000", + "bfeb980000000000", + "bfe0ca0000000000", + "40166fc000000000", + "4021772000000000", + "4018d4c000000000", + "c0138e4000000000", + "bfbbe00000000000", + "4015b40000000000", + "4024cb2000000000", + "c022d9e000000000", + "3fc0480000000000", + "c008a68000000000", + "4021204000000000", + "bfcb300000000000", + "401c230000000000", + "c011c04000000000", + "3fd62c0000000000", + "401c6ec000000000", + "bfe8da0000000000", + "3fd6500000000000", + "c020470000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + }, + "bundle": { + "bits": [ + "3fbc0416705272ae", + "bfb9485f2d2b2f4c", + "bfc6b8635e366020", + "3fa27315054906d6", + "3fc140f4d7e7d95c", + "bfcad1f642f14533", + "3fb0664b92eb944d", + "3f95ddba193a1b11", + "3f8b55289f88a1d5", + "3fb9f74cfdf50024", + "bfa5ddba193a1b11", + "3fa73b95bacdbcc2", + "bfcc0416705272ae", + "bfc86db5e82eea3d", + "3fa3d0f0a6dca888", + "3fb841fa73fc7607", + "3fba4ec3e659e891", + "3fc0e97def82f0ef", + "bfaf6ebb844386e9", + "bfab55289f88a1d5", + "bfac0416705272ae", + "bf95ddba193a1b11", + "bfcb55289f88a1d5", + "bfbd0a7b29812bf3", + "bfc660ec75d177b4", + "3f9899715c615e73", + "bfb1c427347f35fe", + "3fa0664b92eb944d", + "bfb6e41ed268d456", + "bfd4d755600b61cc", + "3fb2ca8bedadef43", + "3fcbac9f87ed8a42", + "bfc4ab99ebd8ed96", + "3f85ddba193a1b11", + "3f832202d612d7af", + "3fb7ea838b978d9b", + "bfc8f0e844c646e0", + "bf95ddba193a1b11", + "bfb21b9e1ce41e6a", + "3fad61f211e6145f", + "bfc63531019f037d", + "bf8e10dfe2afe538", + "3fc8f0e844c646e0", + "3fc73b95bacdbcc2", + "3fb37979be77c01b", + "3fa73b95bacdbcc2", + "bf90664b92eb944d", + "3fcd61f211e6145f", + "bf75ddba193a1b11", + "3fb0bdc27b507cb9", + "3fce10dfe2afe538", + "3fae10dfe2afe538", + "3fb52ecc48704a39", + "3fc21b9e1ce41e6a", + "3f99f74cfdf50024", + "3fc1986bc04cc1c8", + "bfa899715c615e73", + "3fd140f4d7e7d95c", + "3fbb55289f88a1d5", + "bfa68ca7ea03ebea", + "3f85ddba193a1b11", + "bfb428678f4190f4", + "3f91c427347f35fe", + "3fc2f64761e06379" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "cleanup": { + "index": 2, + "score": { + "bits": [ + "3fbb0212f9e44101" + ], + "dtype": "float64", + "shape": [] + } + }, + "cosine": { + "bits": [ + "bfd1ee218957f18d" + ], + "dtype": "float64", + "shape": [] + }, + "cosine_many": { + "bits": [ + "bfd1ee218957f18d", + "bfa60c3e7c9cd515", + "3fbb0212f9e44101" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "dot": { + "bits": [ + "c02be24000000000" + ], + "dtype": "float64", + "shape": [] + }, + "dot_many": { + "bits": [ + "c02be24000000000", + "c000ed8000000000", + "40152e8000000000" + ], + "dtype": "float64", + "shape": [ + 3 + ] + }, + "involution": { + "bits": [ + "bfeb800000000000", + "bfef000000000000", + "3fec800000000000", + "bfbc000000000000", + "bfee800000000000", + "bff6000000000000", + "3feb000000000000", + "3fde000000000000", + "3fd0000000000000", + "3fc8000000000000", + "bfd2000000000000", + "3fe1000000000000", + "3fee000000000000", + "3ff8000000000000", + "bfe9800000000000", + "bfb4000000000000", + "3ff1c00000000000", + "bfe7000000000000", + "3fe4000000000000", + "bfec800000000000", + "bfe8800000000000", + "bfdc000000000000", + "bff7c00000000000", + "3fe4800000000000", + "bfb8000000000000", + "3fe5800000000000", + "bff1800000000000", + "bff5c00000000000", + "bff7800000000000", + "bff6c00000000000", + "3ff3800000000000", + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "bff6800000000000", + "bfd7000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3fe2000000000000", + "bfb0000000000000", + "3fe1000000000000", + "bfeb000000000000", + "bff0000000000000", + "bff0000000000000", + "bfeb000000000000", + "3fe1000000000000", + "bfb0000000000000", + "3fe2000000000000", + "3ff5800000000000", + "bfe7800000000000", + "bfd7000000000000", + "bff6800000000000", + "3fa0000000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "normalize": { + "bits": [ + "bfbf5b2876230936", + "bfa915b9f81c075f", + "3fa7f1d46cd4efc3", + "3fc5181690a334be", + "3fbfed1b3bc69504", + "3fa3623e3fb89155", + "bfca8298e634e4e1", + "3f723e58b47179b9", + "bfc9a7acbdbf932c", + "bfaa399f83631efa", + "bfbacb924906aac8", + "3fc883c732787b91", + "3fb48623caffa8f0", + "bf823e58b47179b9", + "3fb3623e3fb89155", + "bfbec935b07f7d68", + "bfc23e58b47179b9", + "bfc23e58b47179b9", + "bfbec935b07f7d68", + "3fb3623e3fb89155", + "bf823e58b47179b9", + "3fb48623caffa8f0", + "3fc883c732787b91", + "bfbacb924906aac8", + "bfaa399f83631efa", + "bfc9a7acbdbf932c", + "3f723e58b47179b9", + "bfca8298e634e4e1", + "3fa3623e3fb89155", + "3fbfed1b3bc69504", + "3fc5181690a334be", + "3fa7f1d46cd4efc3", + "bfa915b9f81c075f", + "bfbf5b2876230936", + "3fc63bfc1bea4c5a", + "bfc9f0a620915913", + "bfcacb924906aac8", + "bfc8ccc0954a4178", + "bfc3f431055c1d23", + "3fb883c732787b91", + "bf8b5d850eaa3696", + "3fb75fe1a73163f5", + "bfcb148babd870af", + "bfafed1b3bc69504", + "bfbbef77d44dc264", + "bfc03f8700b51069", + "3fb6cdeee18dd827", + "bfba399f83631efa", + "3fc43d2a682de309", + "bf86cdeee18dd827", + "bfbd135d5f94d9ff", + "3fcb5d850eaa3696", + "3fc11a73292a621e", + "3fb3623e3fb89155", + "bfa48623caffa8f0", + "3f9b5d850eaa3696", + "3fa23e58b47179b9", + "3fb11a73292a621e", + "3fbec935b07f7d68", + "bfc915b9f81c075f", + "bfc1636c8bfc2804", + "bf8fed1b3bc69504", + "3fc03f8700b51069", + "bfc1ac65eecdedeb" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "permute": { + "bits": [ + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "bff6800000000000", + "bfd7000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3fe2000000000000", + "bfb0000000000000", + "3fe1000000000000", + "bfeb000000000000", + "bff0000000000000", + "bff0000000000000", + "bfeb000000000000", + "3fe1000000000000", + "bfb0000000000000", + "3fe2000000000000", + "3ff5800000000000", + "bfe7800000000000", + "bfd7000000000000", + "bff6800000000000", + "3fa0000000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000", + "bfeb800000000000", + "3ff3800000000000", + "bff6c00000000000", + "bff7800000000000", + "bff5c00000000000", + "bff1800000000000", + "3fe5800000000000", + "bfb8000000000000", + "3fe4800000000000", + "bff7c00000000000", + "bfdc000000000000", + "bfe8800000000000", + "bfec800000000000", + "3fe4000000000000", + "bfe7000000000000", + "3ff1c00000000000", + "bfb4000000000000", + "bfe9800000000000", + "3ff8000000000000", + "3fee000000000000", + "3fe1000000000000", + "bfd2000000000000", + "3fc8000000000000", + "3fd0000000000000", + "3fde000000000000", + "3feb000000000000", + "bff6000000000000", + "bfee800000000000", + "bfbc000000000000", + "3fec800000000000", + "bfef000000000000", + "bfeb800000000000", + "bfd6000000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "unbind": { + "bits": [ + "c02be24000000000", + "3ff6590000000000", + "c020eda000000000", + "4010aa0000000000", + "40279b2000000000", + "3ff3a10000000000", + "3ff0d00000000000", + "c0133a8000000000", + "c010a84000000000", + "c02291c000000000", + "c00d178000000000", + "401f8f0000000000", + "bffa060000000000", + "40227e0000000000", + "4014348000000000", + "bfeb480000000000", + "3fe19a0000000000", + "c02e56a000000000", + "3fd3400000000000", + "c0287e0000000000", + "3ff0ea0000000000", + "4000a58000000000", + "c027c1c000000000", + "4008308000000000", + "3fedbe0000000000", + "4025266000000000", + "bfd03c0000000000", + "c024398000000000", + "c023524000000000", + "c03299e000000000", + "3ffca60000000000", + "40168a4000000000", + "c016418000000000", + "3ffec30000000000", + "4005df8000000000", + "4023eaa000000000", + "bfe01a0000000000", + "c012f88000000000", + "bff1ec0000000000", + "c023dbc000000000", + "4002c80000000000", + "3fe4220000000000", + "3ff5760000000000", + "c007bd8000000000", + "c019df0000000000", + "c0143b0000000000", + "c020f4e000000000", + "bfe3a00000000000", + "401ca00000000000", + "c026b40000000000", + "401246c000000000", + "bff58b0000000000", + "400e5b8000000000", + "4018038000000000", + "c0154f8000000000", + "c020c46000000000", + "c0241bc000000000", + "c001168000000000", + "40109dc000000000", + "3ff3720000000000", + "4016080000000000", + "4020f0a000000000", + "4016c7c000000000", + "4001900000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "unbind_all": { + "bits": [ + "c02be24000000000", + "3ff6590000000000", + "c020eda000000000", + "4010aa0000000000", + "40279b2000000000", + "3ff3a10000000000", + "3ff0d00000000000", + "c0133a8000000000", + "c010a84000000000", + "c02291c000000000", + "c00d178000000000", + "401f8f0000000000", + "bffa060000000000", + "40227e0000000000", + "4014348000000000", + "bfeb480000000000", + "3fe19a0000000000", + "c02e56a000000000", + "3fd3400000000000", + "c0287e0000000000", + "3ff0ea0000000000", + "4000a58000000000", + "c027c1c000000000", + "4008308000000000", + "3fedbe0000000000", + "4025266000000000", + "bfd03c0000000000", + "c024398000000000", + "c023524000000000", + "c03299e000000000", + "3ffca60000000000", + "40168a4000000000", + "c016418000000000", + "3ffec30000000000", + "4005df8000000000", + "4023eaa000000000", + "bfe01a0000000000", + "c012f88000000000", + "bff1ec0000000000", + "c023dbc000000000", + "4002c80000000000", + "3fe4220000000000", + "3ff5760000000000", + "c007bd8000000000", + "c019df0000000000", + "c0143b0000000000", + "c020f4e000000000", + "bfe3a00000000000", + "401ca00000000000", + "c026b40000000000", + "401246c000000000", + "bff58b0000000000", + "400e5b8000000000", + "4018038000000000", + "c0154f8000000000", + "c020c46000000000", + "c0241bc000000000", + "c001168000000000", + "40109dc000000000", + "3ff3720000000000", + "4016080000000000", + "4020f0a000000000", + "4016c7c000000000", + "4001900000000000", + "c000ed8000000000", + "c014328000000000", + "bff1850000000000", + "bffebb0000000000", + "401bb6c000000000", + "c008598000000000", + "3ff6d30000000000", + "3fc0a00000000000", + "bffc5c0000000000", + "c01d670000000000", + "c0055f8000000000", + "c00bc08000000000", + "3ffb210000000000", + "4023a78000000000", + "401b8ec000000000", + "401eb58000000000", + "c012518000000000", + "c000d08000000000", + "bfe4ea0000000000", + "c000d00000000000", + "4016410000000000", + "40030b8000000000", + "401d2b0000000000", + "bfe8e00000000000", + "400c410000000000", + "3fb9500000000000", + "c021aec000000000", + "c026722000000000", + "c012670000000000", + "c01f344000000000", + "bfe9700000000000", + "bfc4d00000000000", + "401f578000000000", + "c0126fc000000000", + "bffc1c0000000000", + "c009c98000000000", + "bff07f0000000000", + "bff7290000000000", + "c02439a000000000", + "c01fce0000000000", + "3f9d400000000000", + "4022a56000000000", + "40162b0000000000", + "4004c20000000000", + "c00edb8000000000", + "bffdf90000000000", + "c02aa14000000000", + "3fbcd00000000000", + "c010ea4000000000", + "4004e00000000000", + "c03108b000000000", + "4017bf0000000000", + "4015ca0000000000", + "4004500000000000", + "c021598000000000", + "400a1c0000000000", + "bfe2a20000000000", + "c0130fc000000000", + "3ff9810000000000", + "3ff2810000000000", + "bff1cc0000000000", + "bfdc240000000000", + "40057d8000000000", + "bff1700000000000", + "40152e8000000000", + "4005da0000000000", + "c007948000000000", + "40289d6000000000", + "4011654000000000", + "4031034000000000", + "c011ba8000000000", + "3ffa070000000000", + "4019d2c000000000", + "4030dbe000000000", + "3ff1750000000000", + "3f92000000000000", + "c0003a8000000000", + "bfedec0000000000", + "3fffd20000000000", + "400e010000000000", + "bffd430000000000", + "bff8260000000000", + "401d804000000000", + "4016368000000000", + "c00c090000000000", + "c012d44000000000", + "bfdf040000000000", + "401bec0000000000", + "c00f1d8000000000", + "c012ab0000000000", + "4018d74000000000", + "401b5dc000000000", + "4007218000000000", + "bfa9600000000000", + "3fe2a40000000000", + "40041d0000000000", + "c006900000000000", + "c009ab8000000000", + "c00fae8000000000", + "bfb2700000000000", + "401f84c000000000", + "4025166000000000", + "c0062e0000000000", + "c004320000000000", + "3ffeb00000000000", + "4013b18000000000", + "c017d0c000000000", + "bff8270000000000", + "4003840000000000", + "40090d0000000000", + "4016b6c000000000", + "c0129e8000000000", + "400bcc0000000000", + "c020760000000000", + "401dc98000000000", + "4014784000000000", + "c007540000000000", + "c020c1a000000000", + "c013ea0000000000", + "4005c00000000000", + "c026ce8000000000", + "c01bcc0000000000", + "400d018000000000", + "401e884000000000", + "4015f98000000000", + "c018ba0000000000", + "bfc5500000000000", + "400b3b0000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + } + }, + "inputs": { + "a": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "bff6800000000000", + "bfd7000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3fe2000000000000", + "bfb0000000000000", + "3fe1000000000000", + "bfeb000000000000", + "bff0000000000000", + "bff0000000000000", + "bfeb000000000000", + "3fe1000000000000", + "bfb0000000000000", + "3fe2000000000000", + "3ff5800000000000", + "bfe7800000000000", + "bfd7000000000000", + "bff6800000000000", + "3fa0000000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000", + "bfeb800000000000", + "3ff3800000000000", + "bff6c00000000000", + "bff7800000000000", + "bff5c00000000000", + "bff1800000000000", + "3fe5800000000000", + "bfb8000000000000", + "3fe4800000000000", + "bff7c00000000000", + "bfdc000000000000", + "bfe8800000000000", + "bfec800000000000", + "3fe4000000000000", + "bfe7000000000000", + "3ff1c00000000000", + "bfb4000000000000", + "bfe9800000000000", + "3ff8000000000000", + "3fee000000000000", + "3fe1000000000000", + "bfd2000000000000", + "3fc8000000000000", + "3fd0000000000000", + "3fde000000000000", + "3feb000000000000", + "bff6000000000000", + "bfee800000000000", + "bfbc000000000000", + "3fec800000000000", + "bfef000000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "b": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff6c00000000000", + "3fe3800000000000", + "bfcc000000000000", + "bfec800000000000", + "bff6800000000000", + "bff4000000000000", + "3ff0c00000000000", + "3ff0000000000000", + "3ff1c00000000000", + "3ff6000000000000", + "3ff3800000000000", + "bfe4800000000000", + "3fb8000000000000", + "3fef800000000000", + "bfef800000000000", + "bfcc000000000000", + "bff7000000000000", + "3fb4000000000000", + "bff4400000000000", + "3fe2000000000000", + "3fde000000000000", + "bff5800000000000", + "3fee800000000000", + "3fd9000000000000", + "bf90000000000000", + "3fd1000000000000", + "bfd7000000000000", + "bfd3000000000000", + "bfb4000000000000", + "3fd3000000000000", + "bfea800000000000", + "bff8000000000000", + "bfe5000000000000", + "3fd6000000000000", + "3ff8000000000000", + "3fca000000000000", + "3ff4400000000000", + "bfc0000000000000", + "bff5c00000000000", + "3fe2800000000000", + "3fd6000000000000", + "bff1c00000000000", + "3ff4c00000000000", + "3feb000000000000", + "3fe1800000000000", + "bfda000000000000", + "3fdb000000000000", + "3fe3000000000000", + "3fed800000000000", + "3ff6800000000000", + "3fef000000000000", + "bfc6000000000000", + "3fe9000000000000", + "bff2000000000000", + "3fc2000000000000", + "3ff7400000000000" + ], + "dtype": "float64", + "shape": [ + 64 + ] + }, + "paired_left": { + "bits": [ + "bfeb800000000000", + "bfd6000000000000", + "3fd5000000000000", + "3ff2800000000000", + "3fec000000000000", + "3fd1000000000000", + "bff7400000000000", + "3fa0000000000000", + "bff6800000000000", + "bfd7000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3fe2000000000000", + "bfb0000000000000", + "3fe1000000000000", + "bfeb000000000000", + "bff0000000000000", + "bff0000000000000", + "bfeb000000000000", + "3fe1000000000000", + "bfb0000000000000", + "3fe2000000000000", + "3ff5800000000000", + "bfe7800000000000", + "bfd7000000000000", + "bff6800000000000", + "3fa0000000000000", + "bff7400000000000", + "3fd1000000000000", + "3fec000000000000", + "3ff2800000000000", + "3fd5000000000000", + "bfd6000000000000", + "bfeb800000000000", + "3ff3800000000000", + "bff6c00000000000", + "bff7800000000000", + "bff5c00000000000", + "bff1800000000000", + "3fe5800000000000", + "bfb8000000000000", + "3fe4800000000000", + "bff7c00000000000", + "bfdc000000000000", + "bfe8800000000000", + "bfec800000000000", + "3fe4000000000000", + "bfe7000000000000", + "3ff1c00000000000", + "bfb4000000000000", + "bfe9800000000000", + "3ff8000000000000", + "3fee000000000000", + "3fe1000000000000", + "bfd2000000000000", + "3fc8000000000000", + "3fd0000000000000", + "3fde000000000000", + "3feb000000000000", + "bff6000000000000", + "bfee800000000000", + "bfbc000000000000", + "3fec800000000000", + "bfef000000000000", + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff6c00000000000", + "3fe3800000000000", + "bfcc000000000000", + "bfec800000000000", + "bff6800000000000", + "bff4000000000000", + "3ff0c00000000000", + "3ff0000000000000", + "3ff1c00000000000", + "3ff6000000000000", + "3ff3800000000000", + "bfe4800000000000", + "3fb8000000000000", + "3fef800000000000", + "bfef800000000000", + "bfcc000000000000", + "bff7000000000000", + "3fb4000000000000", + "bff4400000000000", + "3fe2000000000000", + "3fde000000000000", + "bff5800000000000", + "3fee800000000000", + "3fd9000000000000", + "bf90000000000000", + "3fd1000000000000", + "bfd7000000000000", + "bfd3000000000000", + "bfb4000000000000", + "3fd3000000000000", + "bfea800000000000", + "bff8000000000000", + "bfe5000000000000", + "3fd6000000000000", + "3ff8000000000000", + "3fca000000000000", + "3ff4400000000000", + "bfc0000000000000", + "bff5c00000000000", + "3fe2800000000000", + "3fd6000000000000", + "bff1c00000000000", + "3ff4c00000000000", + "3feb000000000000", + "3fe1800000000000", + "bfda000000000000", + "3fdb000000000000", + "3fe3000000000000", + "3fed800000000000", + "3ff6800000000000", + "3fef000000000000", + "bfc6000000000000", + "3fe9000000000000", + "bff2000000000000", + "3fc2000000000000", + "3ff7400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000", + "bff0800000000000", + "3ff4400000000000", + "3fe6800000000000", + "3fd3000000000000", + "bfa8000000000000", + "bfa8000000000000", + "3f90000000000000", + "3fce000000000000", + "3fe3800000000000", + "bff2400000000000", + "bff3000000000000", + "bfd6000000000000", + "3fe5000000000000", + "bff3400000000000", + "bfbc000000000000", + "bff7000000000000", + "3fc8000000000000", + "bff0c00000000000", + "3fec800000000000", + "3fa0000000000000", + "bfe9800000000000", + "bff6800000000000", + "3ff2800000000000", + "3feb800000000000", + "bfe7000000000000", + "3fe7800000000000", + "3fed000000000000", + "3ff3c00000000000", + "bff4c00000000000", + "3fe5000000000000", + "3fc2000000000000", + "3ff1800000000000", + "bfea000000000000", + "3fdd000000000000", + "3ff2400000000000", + "3fdc000000000000", + "bfeb000000000000", + "3ff0c00000000000", + "3fb4000000000000", + "3fe7800000000000", + "bff6400000000000", + "3ff2000000000000", + "3fe9000000000000", + "3fe3000000000000", + "bfe2000000000000", + "3fe6000000000000", + "3fef000000000000", + "3ff6800000000000", + "bff0400000000000", + "3fd1000000000000", + "3fe4800000000000", + "bff5000000000000", + "bfb8000000000000", + "3ff4800000000000", + "3fca000000000000", + "3ff7c00000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + }, + "paired_right": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff6c00000000000", + "3fe3800000000000", + "bfcc000000000000", + "bfec800000000000", + "bff6800000000000", + "bff4000000000000", + "3ff0c00000000000", + "3ff0000000000000", + "3ff1c00000000000", + "3ff6000000000000", + "3ff3800000000000", + "bfe4800000000000", + "3fb8000000000000", + "3fef800000000000", + "bfef800000000000", + "bfcc000000000000", + "bff7000000000000", + "3fb4000000000000", + "bff4400000000000", + "3fe2000000000000", + "3fde000000000000", + "bff5800000000000", + "3fee800000000000", + "3fd9000000000000", + "bf90000000000000", + "3fd1000000000000", + "bfd7000000000000", + "bfd3000000000000", + "bfb4000000000000", + "3fd3000000000000", + "bfea800000000000", + "bff8000000000000", + "bfe5000000000000", + "3fd6000000000000", + "3ff8000000000000", + "3fca000000000000", + "3ff4400000000000", + "bfc0000000000000", + "bff5c00000000000", + "3fe2800000000000", + "3fd6000000000000", + "bff1c00000000000", + "3ff4c00000000000", + "3feb000000000000", + "3fe1800000000000", + "bfda000000000000", + "3fdb000000000000", + "3fe3000000000000", + "3fed800000000000", + "3ff6800000000000", + "3fef000000000000", + "bfc6000000000000", + "3fe9000000000000", + "bff2000000000000", + "3fc2000000000000", + "3ff7400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000", + "bff0800000000000", + "3ff4400000000000", + "3fe6800000000000", + "3fd3000000000000", + "bfa8000000000000", + "bfa8000000000000", + "3f90000000000000", + "3fce000000000000", + "3fe3800000000000", + "bff2400000000000", + "bff3000000000000", + "bfd6000000000000", + "3fe5000000000000", + "bff3400000000000", + "bfbc000000000000", + "bff7000000000000", + "3fc8000000000000", + "bff0c00000000000", + "3fec800000000000", + "3fa0000000000000", + "bfe9800000000000", + "bff6800000000000", + "3ff2800000000000", + "3feb800000000000", + "bfe7000000000000", + "3fe7800000000000", + "3fed000000000000", + "3ff3c00000000000", + "bff4c00000000000", + "3fe5000000000000", + "3fc2000000000000", + "3ff1800000000000", + "bfea000000000000", + "3fdd000000000000", + "3ff2400000000000", + "3fdc000000000000", + "bfeb000000000000", + "3ff0c00000000000", + "3fb4000000000000", + "3fe7800000000000", + "bff6400000000000", + "3ff2000000000000", + "3fe9000000000000", + "3fe3000000000000", + "bfe2000000000000", + "3fe6000000000000", + "3fef000000000000", + "3ff6800000000000", + "bff0400000000000", + "3fd1000000000000", + "3fe4800000000000", + "bff5000000000000", + "bfb8000000000000", + "3ff4800000000000", + "3fca000000000000", + "3ff7c00000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000", + "3fd8000000000000", + "bfce000000000000", + "bfe6000000000000", + "bfef800000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfee000000000000", + "bfe3800000000000", + "bfc0000000000000", + "bfe0800000000000", + "3ff5000000000000", + "bfe8000000000000", + "3fd7000000000000", + "bff6400000000000", + "bfa0000000000000", + "bff6800000000000", + "3fd5000000000000", + "bfe9800000000000", + "3ff4000000000000", + "bfdc000000000000", + "bfcc000000000000", + "bfe7000000000000", + "bff1000000000000", + "bff4000000000000", + "3ff4800000000000", + "bff2800000000000", + "bfec000000000000", + "bfdc000000000000", + "3fc4000000000000", + "bfed000000000000", + "bff3400000000000", + "bfc2000000000000", + "3ff1400000000000", + "bfe2000000000000", + "bfef000000000000", + "bfd7000000000000", + "3ff7c00000000000", + "3fde000000000000", + "bfd9000000000000", + "3ff1800000000000", + "3ff6000000000000", + "3fef800000000000", + "3fe8000000000000", + "3fe5800000000000", + "bfe8000000000000", + "3fef800000000000", + "3ff6000000000000", + "bff1800000000000", + "bfd9000000000000", + "bfde000000000000", + "3ff7c00000000000", + "bfd7000000000000", + "3fef000000000000", + "bfe2000000000000", + "bff1400000000000", + "bfc2000000000000", + "bff3400000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + }, + "rows": { + "bits": [ + "3f90000000000000", + "3fe9800000000000", + "bff4800000000000", + "3fc8000000000000", + "3ff1000000000000", + "bfe1800000000000", + "3ff0400000000000", + "bfd2000000000000", + "3ff6c00000000000", + "3fe3800000000000", + "bfcc000000000000", + "bfec800000000000", + "bff6800000000000", + "bff4000000000000", + "3ff0c00000000000", + "3ff0000000000000", + "3ff1c00000000000", + "3ff6000000000000", + "3ff3800000000000", + "bfe4800000000000", + "3fb8000000000000", + "3fef800000000000", + "bfef800000000000", + "bfcc000000000000", + "bff7000000000000", + "3fb4000000000000", + "bff4400000000000", + "3fe2000000000000", + "3fde000000000000", + "bff5800000000000", + "3fee800000000000", + "3fd9000000000000", + "bf90000000000000", + "3fd1000000000000", + "bfd7000000000000", + "bfd3000000000000", + "bfb4000000000000", + "3fd3000000000000", + "bfea800000000000", + "bff8000000000000", + "bfe5000000000000", + "3fd6000000000000", + "3ff8000000000000", + "3fca000000000000", + "3ff4400000000000", + "bfc0000000000000", + "bff5c00000000000", + "3fe2800000000000", + "3fd6000000000000", + "bff1c00000000000", + "3ff4c00000000000", + "3feb000000000000", + "3fe1800000000000", + "bfda000000000000", + "3fdb000000000000", + "3fe3000000000000", + "3fed800000000000", + "3ff6800000000000", + "3fef000000000000", + "bfc6000000000000", + "3fe9000000000000", + "bff2000000000000", + "3fc2000000000000", + "3ff7400000000000", + "3ff4c00000000000", + "bfe5800000000000", + "bfe1000000000000", + "bff2000000000000", + "3fd9000000000000", + "bfee800000000000", + "3fec000000000000", + "3fc4000000000000", + "bff0800000000000", + "3ff4400000000000", + "3fe6800000000000", + "3fd3000000000000", + "bfa8000000000000", + "bfa8000000000000", + "3f90000000000000", + "3fce000000000000", + "3fe3800000000000", + "bff2400000000000", + "bff3000000000000", + "bfd6000000000000", + "3fe5000000000000", + "bff3400000000000", + "bfbc000000000000", + "bff7000000000000", + "3fc8000000000000", + "bff0c00000000000", + "3fec800000000000", + "3fa0000000000000", + "bfe9800000000000", + "bff6800000000000", + "3ff2800000000000", + "3feb800000000000", + "bfe7000000000000", + "3fe7800000000000", + "3fed000000000000", + "3ff3c00000000000", + "bff4c00000000000", + "3fe5000000000000", + "3fc2000000000000", + "3ff1800000000000", + "bfea000000000000", + "3fdd000000000000", + "3ff2400000000000", + "3fdc000000000000", + "bfeb000000000000", + "3ff0c00000000000", + "3fb4000000000000", + "3fe7800000000000", + "bff6400000000000", + "3ff2000000000000", + "3fe9000000000000", + "3fe3000000000000", + "bfe2000000000000", + "3fe6000000000000", + "3fef000000000000", + "3ff6800000000000", + "bff0400000000000", + "3fd1000000000000", + "3fe4800000000000", + "bff5000000000000", + "bfb8000000000000", + "3ff4800000000000", + "3fca000000000000", + "3ff7c00000000000", + "bfa0000000000000", + "bff4800000000000", + "bfd1000000000000", + "3ff5c00000000000", + "3fc0000000000000", + "bfee800000000000", + "bff2400000000000", + "3fd8000000000000", + "bfce000000000000", + "bfe6000000000000", + "bfef800000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfee000000000000", + "bfe3800000000000", + "bfc0000000000000", + "bfe0800000000000", + "3ff5000000000000", + "bfe8000000000000", + "3fd7000000000000", + "bff6400000000000", + "bfa0000000000000", + "bff6800000000000", + "3fd5000000000000", + "bfe9800000000000", + "3ff4000000000000", + "bfdc000000000000", + "bfcc000000000000", + "bfe7000000000000", + "bff1000000000000", + "bff4000000000000", + "3ff4800000000000", + "bff2800000000000", + "bfec000000000000", + "bfdc000000000000", + "3fc4000000000000", + "bfed000000000000", + "bff3400000000000", + "bfc2000000000000", + "3ff1400000000000", + "bfe2000000000000", + "bfef000000000000", + "bfd7000000000000", + "3ff7c00000000000", + "3fde000000000000", + "bfd9000000000000", + "3ff1800000000000", + "3ff6000000000000", + "3fef800000000000", + "3fe8000000000000", + "3fe5800000000000", + "bfe8000000000000", + "3fef800000000000", + "3ff6000000000000", + "bff1800000000000", + "bfd9000000000000", + "bfde000000000000", + "3ff7c00000000000", + "bfd7000000000000", + "3fef000000000000", + "bfe2000000000000", + "bff1400000000000", + "bfc2000000000000", + "bff3400000000000" + ], + "dtype": "float64", + "shape": [ + 3, + 64 + ] + } + }, + "name": "formula-d64", + "shift": -66 + } + ], + "input_domain": { + "finite_formula_range": [ + -1.5, + 1.5 + ], + "nonzero_subnormal_outputs": "excluded", + "rounding": "round-to-nearest", + "subnormal_inputs": "excluded" + }, + "profile_id": "0x00010001", + "reduction_cases": [ + { + "dimension": 384, + "expected": { + "cosine_many": { + "bits": [ + "bf761405f77e35b0", + "3fb194f9816358e0" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "bffab30000000000", + "40357c7000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000" + ], + "dtype": "float64", + "shape": [ + 384 + ] + }, + "rows": { + "bits": [ + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000" + ], + "dtype": "float64", + "shape": [ + 2, + 384 + ] + } + }, + "name": "ordered-reduction-d384" + }, + { + "dimension": 1024, + "expected": { + "cosine_many": { + "bits": [ + "3f889e78305ccdb4", + "3f9297ab68120c6e" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "4023ce4000000000", + "402e57c000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "bfe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "bfd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "bfe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "bff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "3ff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "3fd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "3ff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "bff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "bfe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "bfd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "bfe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "3ff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "bff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "bff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "3ff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "bfe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "bfdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "bff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "3fdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "3ff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "3fed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "bfcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "3fe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "3fec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "3fbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "3ff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "bff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "3ff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "3fc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "3fee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "3feb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "bfc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "3ff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "3ff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "3fe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "bfed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "bfd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "bfe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "3ff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "bfec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "3ff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "bfd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "bfb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "bfe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "3ff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "bff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "bff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "bff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "bff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "3ff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "bfd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "3fc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "bfca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "bff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "bfe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "bfe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "bff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "bfd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "3fa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "bfe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "3ff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "bff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "3ff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "3fd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "3ff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "bff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "3fe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "bfdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "bfeb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "3feb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "bff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "bff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "3fe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "bff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "bfef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "3ff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "bfb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "3fe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "3fce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "bfee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "3f90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "3fb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "bfe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "3fe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "3fde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "bfed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "bfc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "bfd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "bff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "bfce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "bfa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "bfe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "3ff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "3fed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "bfd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "3fe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "3fe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "bfd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "3fed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "3ff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "3fe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "bfe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "bfa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "bfce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "bff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "bfd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "bfc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "bfed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "3fde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000" + ], + "dtype": "float64", + "shape": [ + 1024 + ] + }, + "rows": { + "bits": [ + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "bff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "3ff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "3fdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "3ff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "bff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "3fee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "bfe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "bfc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "bfe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "3ff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "bfed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "bfee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "3ff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "bfe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "bfd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "bfee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "3fe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "3ff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "3ff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "bfb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "3fed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "3ff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "3fce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "bff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "bfef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "bff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "3fd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "3ff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "3fef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "bf90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "3ff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "3ff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "3fe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "bfe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "bfca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "bfe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "3ff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "bfe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "3ff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "bfd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "3fc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "bfd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "3ff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "bff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "bff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "3fe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "bff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "bfef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "3ff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "3fd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "bfb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "bfdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "bfde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "bff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "bfc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "3fc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "bfd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "3ff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "bff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "bff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "3fe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "bff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "bff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "3fec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "bfe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "bfd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "3fef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "bff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "bff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "3fe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "bff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "bfeb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "bff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "3fa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "3fe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "3fd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "bfea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "3fc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "3fca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "bfe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "3fe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "3ff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "3fe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "bfe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "bfa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "bfc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "bff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "bfbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "3fb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "bfe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "3feb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "3ff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "3ff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "bfc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "3fe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "3fe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "bfc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "3ff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "3ff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "3feb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "bfe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "3fb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "bfbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "bff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bfc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "bfa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "bfe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "3fe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "3ff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "3fe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "bfe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "3fca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "3fc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "3fcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "bff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "bfec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "bff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "3fe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "3ff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "3fda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "bff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "bff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "3ff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "bfc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "3fe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "3fd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "bfea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "3fc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "3fd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "bfe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "3feb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "3ff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "3feb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "bfde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "3fd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "3fce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "bfe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "3fd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "3fe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "bfc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "3ff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "bfee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "bff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "3fe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "bff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "bff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "3fe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "bff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "bfe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "bff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "3fe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "3ff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "3ff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "3fa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "3ff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "3ff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "3fe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "bfef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "bfdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "bfea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "3fee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "bff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "bff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "3fed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "bfec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "bfe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "bff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "3fdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "3ff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "3fec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "bfcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "3fe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "3fed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "3fc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "bff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "bff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "bff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "3ff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "3fef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "3f90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "3ff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "3ff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "3fea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "bfe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "bfb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "bfd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "3ff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "bfe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "bfe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "bff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "bfb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "3fd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "bfc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "3ff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "bfe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "bfed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "3ff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "bfeb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "bfe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "bff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "3fbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "3fe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "3fd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "bfed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "bfa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "bfa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "bfed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "3fd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "3fe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "3fbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "bff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "bfe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "bfeb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "3ff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "bfed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "bfe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "3ff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "bfc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "3fd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "bfb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "bff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "bfe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "bfe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "3ff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "bfd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "bfb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "bfe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000" + ], + "dtype": "float64", + "shape": [ + 2, + 1024 + ] + } + }, + "name": "ordered-reduction-d1024" + }, + { + "dimension": 4096, + "expected": { + "cosine_many": { + "bits": [ + "3f41076936c3985e", + "3f8e74f185ed18d1" + ], + "dtype": "float64", + "shape": [ + 2 + ] + }, + "dot_many": { + "bits": [ + "3ffb690000000000", + "4048c47000000000" + ], + "dtype": "float64", + "shape": [ + 2 + ] + } + }, + "inputs": { + "query": { + "bits": [ + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "bfe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "bfd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "bfe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "bff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "3ff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "3fd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "3ff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "bff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "bfe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "bfd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "bfe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "3ff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "bff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "bff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "3ff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "bfe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "bfdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "bff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "3fdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "3ff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "3fed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "bfcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "3fe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "3fec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "3fbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "3ff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "bff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "3ff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "3fc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "3fee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "3feb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "bfc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "3ff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "3ff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "3fe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "bfed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "bfd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "bfe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "3ff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "bfec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "3ff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "bfd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "bfb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "bfe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "3ff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "bff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "bff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "bff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "bff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "3ff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "bfd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "3fc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "bfca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "bff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "bfe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "bfe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "bff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "bfd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "3fa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "bfe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "3ff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "bff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "3ff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "3fd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "3ff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "bff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "3fe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "bfdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "bfeb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "3feb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "bff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "bff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "3fe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "bff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "bfef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "3ff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "bfb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "3fe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "3fce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "bfee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "3f90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "3fb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "bfe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "3fe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "3fde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "bfed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "bfc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "bfd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "bff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "bfce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "bfa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "bfe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "3ff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "3fed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "bfd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "3fe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "3fe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "bfd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "3fed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "3ff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "3fe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "bfe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "bfa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "bfce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "bff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "bfd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "bfc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "bfed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "3fde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "bfe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "bfd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "bfe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "bff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "3ff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "3fd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "3ff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "bff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "bfe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "bfd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "bfe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "3ff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "bff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "bff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "3ff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "bfe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "bfdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "bff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "3fdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "3ff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "3fed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "bfcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "3fe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "3fec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "3fbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "3ff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "bff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "3ff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "3fc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "3fee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "3feb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "bfc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "3ff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "3ff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "3fe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "bfed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "bfd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "bfe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "3ff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "bfec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "3ff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "bfd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "bfb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "bfe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "3ff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "bff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "bff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "bff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "bff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "3ff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "bfd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "3fc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "bfca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "bff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "bfe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "bfe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "bff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "bfd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "3fa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "bfe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "3ff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "bff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "3ff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "3fd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "3ff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "bff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "3fe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "bfdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "bfeb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "3feb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "bff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "bff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "3fe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "bff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "bfef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "3ff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "bfb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "3fe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "3fce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "bfee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "3f90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "3fb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "bfe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "3fe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "3fde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "bfed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "bfc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "bfd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "bff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "bfce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "bfa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "bfe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "3ff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "3fed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "bfd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "3fe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "3fe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "bfd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "3fed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "3ff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "3fe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "bfe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "bfa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "bfce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "bff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "bfd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "bfc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "bfed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "3fde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "bfe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "bfd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "bfe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "bff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "3ff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "3fd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "3ff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "bff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "bfe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "bfd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "bfe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "3ff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "bff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "bff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "3ff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "bfe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "bfdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "bff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "3fdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "3ff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "3fed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "bfcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "3fe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "3fec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "3fbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "3ff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "bff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "3ff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "3fc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "3fee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "3feb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "bfc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "3ff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "3ff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "3fe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "bfed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "bfd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "bfe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "3ff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "bfec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "3ff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "bfd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "bfb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "bfe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "3ff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "bff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "bff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "bff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "bff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "3ff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "bfd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "3fc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "bfca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "bff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "bfe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "bfe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "bff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "bfd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "3fa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "bfe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "3ff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "bff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "3ff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "3fd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "3ff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "bff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "3fe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "bfdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "bfeb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "3feb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "bff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "bff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "3fe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "bff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "bfef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "3ff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "bfb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "3fe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "3fce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "bfee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "3f90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "3fb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "bfe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "3fe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "3fde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "bfed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "bfc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "bfd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "bff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "bfce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "bfa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "bfe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "3ff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "3fed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "bfd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "3fe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "3fe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "bfd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "3fed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "3ff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "3fe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "bfe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "bfa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "bfce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "bff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "bfd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "bfc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "bfed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "3fde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "bfec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "3ff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "bfe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "bfd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "bfed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "3fe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "3ff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "3ff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "bfc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "3feb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "3fee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "3fc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "3ff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "bff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "3ff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "3fbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "3fec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "3fe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "bfcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "3fed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "3ff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "3fdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "bff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "bfdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "bfe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "3ff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "bff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "bff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "3ff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "bfe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "bfd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "bfe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "bff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "3ff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "3fd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "3ff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "bff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "bfe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "bfd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "bfe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "3ff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "bff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "bff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "3ff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "bfe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "bfdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "bff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "3fdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "3ff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "3fed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "bfcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "3fe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "3fec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "3fbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "3ff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "bff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "3ff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "3fc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "3fee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "3feb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "bfc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "3ff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "3ff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "3fe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "bfed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "bfd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "bfe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "3ff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "bfec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "3ff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "bfd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "bfb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "bfe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "3ff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "bff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "bff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "bff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "bff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "3ff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "bfd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "3feb000000000000", + "3fc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "bfca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "3fec800000000000", + "bff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "bfe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "bfe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "bff6800000000000", + "bff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "bfd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "bff8000000000000", + "3fa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "bfe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "3ff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "bff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "3ff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "3fd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "3ff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "bff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "3fe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "bfec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "3fca000000000000", + "bfdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "bfeb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "3feb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "bff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "3ff1800000000000", + "bff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "3fe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "bff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "bfef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "3ff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "bff2000000000000", + "bfb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "3fe0000000000000", + "3fe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "3fce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "bfee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "3f90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "3fea800000000000", + "3fb4000000000000", + "bfea800000000000", + "bff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "bfe8000000000000", + "3fe9000000000000", + "3fef000000000000", + "3ff5000000000000", + "bff3400000000000", + "3fe1800000000000", + "3fd1000000000000", + "3ff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fee800000000000", + "3fe4800000000000", + "bfe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "3fde000000000000", + "bff1c00000000000", + "3ff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "bfed000000000000", + "3ff0c00000000000", + "3ff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "bfc4000000000000", + "3ff1400000000000", + "bfeb800000000000", + "3fd8000000000000", + "bff4000000000000", + "bfd3000000000000", + "bff0400000000000", + "3feb000000000000", + "bfc4000000000000", + "bff0000000000000", + "bff5400000000000", + "3fe9800000000000", + "3fdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "bfce000000000000", + "3fdf000000000000", + "3fec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "bfa0000000000000", + "3ff1000000000000", + "bfe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "bfe7000000000000", + "bfdf000000000000", + "3ff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "3ff4400000000000", + "bfed000000000000", + "bfd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "3fed800000000000", + "3fcc000000000000", + "bff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "bfd4000000000000", + "bfe9000000000000", + "3ff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "3fe2800000000000", + "bfec800000000000", + "bff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "3fe2800000000000", + "bfbc000000000000", + "3fe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "bfd4000000000000", + "bff7400000000000", + "bfa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "3fed800000000000", + "3ff1c00000000000", + "3fd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "3ff4400000000000", + "bff7800000000000", + "3ff8000000000000", + "bff6800000000000", + "bff2400000000000", + "3fe7000000000000", + "bfc2000000000000", + "3fe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "bfe7000000000000", + "bfee000000000000", + "3fe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "bfa0000000000000", + "bfeb000000000000", + "3ff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "bfce000000000000", + "3fc2000000000000", + "3fca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "bff5400000000000", + "bff0000000000000", + "bfc4000000000000", + "3feb000000000000", + "bff0400000000000", + "bfd3000000000000", + "bff4000000000000", + "3fd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "bfc4000000000000", + "bfe3800000000000", + "bff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "bfed000000000000", + "3fed800000000000", + "3ff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "3fde000000000000", + "3fd5000000000000", + "3ff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "3fee800000000000", + "3fe4000000000000", + "bfe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "3fe1800000000000", + "bff3400000000000", + "3ff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "bfe8000000000000", + "3fec000000000000", + "3ff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "3fb4000000000000", + "3fea800000000000", + "bff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "3f90000000000000", + "bff5800000000000", + "3fe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "bfee000000000000", + "3fd9000000000000", + "3fb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "3fce000000000000", + "3fb8000000000000", + "3fd9000000000000", + "3fee000000000000", + "bff6000000000000", + "3fe0800000000000", + "3fe0000000000000", + "bff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "bfb8000000000000", + "bff2000000000000", + "3fea800000000000", + "bfb4000000000000", + "bfea800000000000", + "3ff6c00000000000", + "3ff2800000000000", + "3fec000000000000", + "3fe8000000000000", + "3fe9000000000000", + "bfef000000000000", + "3ff5000000000000", + "bff3400000000000", + "bfe1800000000000", + "3fd1000000000000", + "bff3c00000000000", + "bfe5000000000000", + "3fe4000000000000", + "bfee800000000000", + "3fe4800000000000", + "3fe4000000000000", + "3ff4800000000000", + "3fd5000000000000", + "bfde000000000000", + "bff1c00000000000", + "bff6c00000000000", + "3ff1800000000000", + "3fed800000000000", + "3fed000000000000", + "3ff0c00000000000", + "bff5800000000000", + "bff3800000000000", + "bfe3800000000000", + "3fc4000000000000", + "3ff1400000000000", + "3feb800000000000", + "3fd8000000000000", + "bff4000000000000", + "3fd3000000000000", + "bff0400000000000", + "bfeb000000000000", + "bfc4000000000000", + "bff0000000000000", + "3ff5400000000000", + "3fe9800000000000", + "bfdb000000000000", + "3fca000000000000", + "3fc2000000000000", + "3fce000000000000", + "3fdf000000000000", + "bfec800000000000", + "3ff7400000000000", + "bfeb000000000000", + "3fa0000000000000", + "3ff1000000000000", + "3fe8800000000000", + "3fe2800000000000", + "bfee000000000000", + "3fe7000000000000", + "bfdf000000000000", + "bff7c00000000000", + "3fe3000000000000", + "bfc2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3ff6800000000000", + "3ff8000000000000", + "bff7800000000000", + "bff4400000000000", + "bfed000000000000", + "3fd9000000000000", + "3fd2000000000000", + "3ff1c00000000000", + "bfed800000000000", + "3fcc000000000000", + "3ff8000000000000", + "bfa8000000000000", + "bff7400000000000", + "3fd4000000000000", + "bfe9000000000000", + "bff4c00000000000", + "3fe0800000000000", + "bfbc000000000000", + "bfe2800000000000", + "bfec800000000000", + "3ff0c00000000000", + "bff0c00000000000", + "bfec800000000000", + "bfe2800000000000", + "bfbc000000000000", + "bfe0800000000000", + "3ff4c00000000000", + "bfe9000000000000", + "3fd4000000000000", + "bff7400000000000", + "3fa8000000000000", + "bff8000000000000", + "3fcc000000000000", + "bfed800000000000", + "3ff1c00000000000", + "bfd2000000000000", + "bfd9000000000000", + "bfed000000000000", + "bff4400000000000", + "bff7800000000000", + "bff8000000000000", + "bff6800000000000", + "bff2400000000000", + "bfe7000000000000", + "bfc2000000000000", + "bfe3000000000000", + "3ff7c00000000000", + "bfdf000000000000", + "3fe7000000000000", + "bfee000000000000", + "bfe2800000000000", + "bfe8800000000000", + "3ff1000000000000", + "3fa0000000000000", + "bfeb000000000000", + "bff7400000000000", + "3fec800000000000", + "3fdf000000000000", + "3fce000000000000", + "3fc2000000000000", + "bfca000000000000", + "3fdb000000000000", + "3fe9800000000000", + "3ff5400000000000", + "bff0000000000000", + "3fc4000000000000", + "3feb000000000000", + "bff0400000000000", + "3fd3000000000000", + "bff4000000000000", + "bfd8000000000000", + "bfeb800000000000", + "3ff1400000000000", + "3fc4000000000000", + "bfe3800000000000", + "3ff3800000000000", + "3ff5800000000000", + "3ff0c00000000000", + "3fed000000000000", + "3fed800000000000", + "bff1800000000000", + "3ff6c00000000000", + "bff1c00000000000", + "bfde000000000000", + "3fd5000000000000", + "bff4800000000000", + "bfe4000000000000", + "3fe4800000000000", + "bfee800000000000", + "3fe4000000000000", + "3fe5000000000000", + "3ff3c00000000000", + "3fd1000000000000", + "bfe1800000000000", + "bff3400000000000", + "bff5000000000000", + "3fef000000000000", + "3fe9000000000000", + "3fe8000000000000", + "3fec000000000000", + "bff2800000000000", + "bff6c00000000000", + "bfea800000000000", + "bfb4000000000000", + "3fea800000000000", + "3ff2000000000000", + "3fb8000000000000", + "3ff7800000000000", + "bf90000000000000", + "bff5800000000000", + "bfe0000000000000", + "bfe0800000000000", + "bff6000000000000", + "3fee000000000000", + "3fd9000000000000", + "bfb8000000000000", + "bfce000000000000", + "bfd4000000000000", + "bfce000000000000", + "3fb8000000000000", + "bfd9000000000000", + "3fee000000000000", + "bff6000000000000", + "bfe0800000000000", + "3fe0000000000000", + "3ff5800000000000", + "bf90000000000000", + "3ff7800000000000", + "3fb8000000000000", + "bff2000000000000", + "bfea800000000000" + ], + "dtype": "float64", + "shape": [ + 4096 + ] + }, + "rows": { + "bits": [ + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "bff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "3ff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "3fdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "3ff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "bff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "3fee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "bfe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "bfc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "bfe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "3ff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "bfed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "bfee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "3ff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "bfe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "bfd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "bfee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "3fe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "3ff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "3ff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "bfb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "3fed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "3ff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "3fce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "bff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "bfef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "bff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "3fd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "3ff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "3fef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "bf90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "3ff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "3ff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "3fe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "bfe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "bfca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "bfe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "3ff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "bfe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "3ff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "bfd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "3fc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "bfd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "3ff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "bff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "bff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "3fe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "bff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "bfef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "3ff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "3fd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "bfb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "bfdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "bfde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "bff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "bfc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "3fc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "bfd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "3ff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "bff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "bff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "3fe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "bff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "bff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "3fec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "bfe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "bfd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "3fef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "bff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "bff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "3fe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "bff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "bfeb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "bff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "3fa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "3fe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "3fd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "bfea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "3fc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "3fca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "bfe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "3fe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "3ff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "3fe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "bfe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "bfa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "bfc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "bff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "bfbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "3fb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "bfe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "3feb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "3ff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "3ff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "bfc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "3fe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "3fe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "bfc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "3ff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "3ff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "3feb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "bfe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "3fb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "bfbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "bff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bfc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "bfa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "bfe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "3fe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "3ff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "3fe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "bfe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "3fca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "3fc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "bff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "3ff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "3fdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "3ff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "bff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "3fee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "bfe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "bfc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "bfe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "3ff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "bfed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "bfee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "3ff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "bfe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "bfd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "bfee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "3fe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "3ff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "3ff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "bfb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "3fed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "3ff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "3fce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "bff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "bfef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "bff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "3fd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "3ff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "3fef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "bf90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "3ff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "3ff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "3fe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "bfe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "bfca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "bfe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "3ff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "bfe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "3ff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "bfd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "3fc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "bfd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "3ff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "bff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "bff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "3fe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "bff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "bfef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "3ff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "3fd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "bfb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "bfdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "bfde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "bff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "bfc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "3fc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "bfd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "3ff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "bff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "bff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "3fe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "bff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "bff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "3fec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "bfe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "bfd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "3fef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "bff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "bff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "3fe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "bff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "bfeb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "bff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "3fa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "3fe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "3fd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "bfea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "3fc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "3fca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "bfe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "3fe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "3ff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "3fe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "bfe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "bfa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "bfc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "bff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "bfbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "3fb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "bfe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "3feb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "3ff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "3ff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "bfc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "3fe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "3fe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "bfc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "3ff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "3ff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "3feb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "bfe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "3fb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "bfbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "bff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bfc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "bfa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "bfe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "3fe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "3ff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "3fe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "bfe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "3fca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "3fc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "bff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "3ff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "3fdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "3ff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "bff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "3fee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "bfe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "bfc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "bfe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "3ff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "bfed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "bfee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "3ff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "bfe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "bfd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "bfee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "3fe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "3ff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "3ff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "bfb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "3fed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "3ff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "3fce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "bff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "bfef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "bff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "3fd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "3ff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "3fef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "bf90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "3ff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "3ff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "3fe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "bfe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "bfca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "bfe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "3ff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "bfe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "3ff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "bfd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "3fc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "bfd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "3ff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "bff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "bff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "3fe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "bff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "bfef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "3ff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "3fd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "bfb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "bfdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "bfde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "bff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "bfc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "3fc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "bfd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "3ff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "bff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "bff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "3fe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "bff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "bff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "3fec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "bfe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "bfd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "3fef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "bff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "bff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "3fe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "bff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "bfeb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "bff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "3fa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "3fe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "3fd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "bfea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "3fc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "3fca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "bfe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "3fe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "3ff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "3fe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "bfe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "bfa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "bfc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "bff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "bfbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "3fb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "bfe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "3feb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "3ff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "3ff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "bfc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "3fe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "3fe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "bfc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "3ff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "3ff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "3feb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "bfe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "3fb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "bfbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "bff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bfc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "bfa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "bfe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "3fe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "3ff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "3fe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "bfe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "3fca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "3fc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "bfe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "3fe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "3ff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "3ff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "bf90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "3fef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "3ff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "3fd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "bff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "bfef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "bff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "3fce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "3ff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "3fed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "bfb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "3ff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "3ff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "3fe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "bfee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "bfd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "bfe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "3ff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "bfee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "bfed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "3ff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "bfe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "bfc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "bfe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "3fee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "bff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "3ff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "3fdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "3ff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "bff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "3fee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "bfe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "bfc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "bfe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "3ff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "bfed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "bfee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "3ff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "bfe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "bfd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "bfee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "3fe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "3ff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "3ff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "bfb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "3fed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "3ff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "3fce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "bff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "bfef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "bff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "3fd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "3ff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "3fef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "bf90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "3ff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "3ff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "3fe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "bfe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "bfca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "bfe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "3ff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "bfe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "bfe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "3ff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "bfd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "3fc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "bfd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "3ff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "bff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "bff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "3fe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "bff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "bfef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "3ff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "3fd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "bfb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "bfec800000000000", + "bfdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "bfde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "bff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "bfc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "3fc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "bfd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "3ff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "bff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "bff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "3fe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "bff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "3fde000000000000", + "bff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "3fec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "bfe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "bfd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "bfe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "3fef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "bff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "bff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "3fe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "bff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "bfeb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "bff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "bff4000000000000", + "3fa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "3fe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "3fd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "bfea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "3fc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "3fca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "bfe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "bff5400000000000", + "3fe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "3ff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "3fe3000000000000", + "bff3c00000000000", + "3ff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "bfe9000000000000", + "3fed800000000000", + "3ff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "bfa0000000000000", + "3fee800000000000", + "bfef800000000000", + "3fd0000000000000", + "bff6000000000000", + "bfc6000000000000", + "bff2400000000000", + "3fe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "bff3400000000000", + "3fe5800000000000", + "3fd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "bfbc000000000000", + "3fd7000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "3fb8000000000000", + "3fee000000000000", + "bfec800000000000", + "3fdd000000000000", + "bff1000000000000", + "bfe3000000000000", + "bfe3800000000000", + "3ff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "3feb000000000000", + "bff4400000000000", + "3ff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "3ff6400000000000", + "bff0800000000000", + "bfe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "3ff0c00000000000", + "3fb8000000000000", + "3ff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "bfc8000000000000", + "bfed000000000000", + "3ff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "3fe6800000000000", + "bff0400000000000", + "bff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "3fe6800000000000", + "bfce000000000000", + "3fd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "bfc8000000000000", + "3ff7000000000000", + "bfc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "3ff0c00000000000", + "3fef800000000000", + "3fc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "3ff6400000000000", + "3ff6c00000000000", + "3ff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "3feb000000000000", + "bfd1000000000000", + "3fde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "bfe3000000000000", + "bff1000000000000", + "3fdd000000000000", + "bfec800000000000", + "3fee000000000000", + "3fb8000000000000", + "bfef000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "bfbc000000000000", + "3f90000000000000", + "3fb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "bff3400000000000", + "bff2000000000000", + "bfd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "bfc6000000000000", + "bff6000000000000", + "3fd0000000000000", + "bfef800000000000", + "3fee800000000000", + "bfa0000000000000", + "bfe7800000000000", + "bff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "bfe9000000000000", + "3fe9800000000000", + "3fef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "3fe3000000000000", + "3fca000000000000", + "3ff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "3ff1400000000000", + "3fe0000000000000", + "bfe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "3fe5800000000000", + "bff5400000000000", + "3ff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "bfe4000000000000", + "3fe8000000000000", + "3ff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "3fca000000000000", + "3fe6800000000000", + "bff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "3fc2000000000000", + "bff7800000000000", + "3fd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "bfea000000000000", + "3fd1000000000000", + "bfc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "3fd7000000000000", + "bfc0000000000000", + "3fd1000000000000", + "3fea000000000000", + "bff8000000000000", + "3fe4800000000000", + "3fd8000000000000", + "bff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "3fa0000000000000", + "bff4000000000000", + "3fe6800000000000", + "bfca000000000000", + "bfee800000000000", + "bff7800000000000", + "3ff0800000000000", + "3fe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "bfeb000000000000", + "3ff3000000000000", + "bff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "bff1c00000000000", + "bfe9000000000000", + "3fe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "3fe8000000000000", + "3ff2800000000000", + "3fca000000000000", + "bfe3000000000000", + "bff3c00000000000", + "bff4c00000000000", + "3fef000000000000", + "3fe9800000000000", + "3fe9000000000000", + "3fed800000000000", + "bff3800000000000", + "bff5800000000000", + "bfe7800000000000", + "3fa0000000000000", + "3fee800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff6000000000000", + "3fc6000000000000", + "bff2400000000000", + "bfe7000000000000", + "bfd2000000000000", + "bff2000000000000", + "3ff3400000000000", + "3fe5800000000000", + "bfd3000000000000", + "3fb4000000000000", + "3f90000000000000", + "3fbc000000000000", + "3fd7000000000000", + "bfe8800000000000", + "3ff5400000000000", + "bfef000000000000", + "bfb8000000000000", + "3fee000000000000", + "3fec800000000000", + "3fdd000000000000", + "bff1000000000000", + "3fe3000000000000", + "bfe3800000000000", + "bff5c00000000000", + "3fde000000000000", + "bfd1000000000000", + "bfeb000000000000", + "bff4400000000000", + "bff7c00000000000", + "3ff6000000000000", + "3ff6c00000000000", + "bff6400000000000", + "bff0800000000000", + "3fe0800000000000", + "3fc4000000000000", + "3fef800000000000", + "bff0c00000000000", + "3fb8000000000000", + "bff6400000000000", + "bfc6000000000000", + "3ff7000000000000", + "3fc8000000000000", + "bfed000000000000", + "bff2c00000000000", + "3fd9000000000000", + "bfce000000000000", + "bfe6800000000000", + "bff0400000000000", + "3ff2c00000000000", + "bff2c00000000000", + "bff0400000000000", + "bfe6800000000000", + "bfce000000000000", + "bfd9000000000000", + "3ff2c00000000000", + "bfed000000000000", + "3fc8000000000000", + "3ff7000000000000", + "3fc6000000000000", + "3ff6400000000000", + "3fb8000000000000", + "bff0c00000000000", + "3fef800000000000", + "bfc4000000000000", + "bfe0800000000000", + "bff0800000000000", + "bff6400000000000", + "3ff6c00000000000", + "bff6000000000000", + "3ff7c00000000000", + "bff4400000000000", + "bfeb000000000000", + "bfd1000000000000", + "bfde000000000000", + "3ff5c00000000000", + "bfe3800000000000", + "3fe3000000000000", + "bff1000000000000", + "bfdd000000000000", + "bfec800000000000", + "3fee000000000000", + "bfb8000000000000", + "bfef000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fd7000000000000", + "3fbc000000000000", + "3f90000000000000", + "bfb4000000000000", + "3fd3000000000000", + "3fe5800000000000", + "3ff3400000000000", + "bff2000000000000", + "3fd2000000000000", + "3fe7000000000000", + "bff2400000000000", + "3fc6000000000000", + "bff6000000000000", + "bfd0000000000000", + "bfef800000000000", + "3fee800000000000", + "3fa0000000000000", + "bfe7800000000000", + "3ff5800000000000", + "3ff3800000000000", + "3fed800000000000", + "3fe9000000000000", + "3fe9800000000000", + "bfef000000000000", + "3ff4c00000000000", + "bff3c00000000000", + "bfe3000000000000", + "3fca000000000000", + "bff2800000000000", + "bfe8000000000000", + "3fe0800000000000", + "bff1400000000000", + "3fe0000000000000", + "3fe9000000000000", + "3ff1c00000000000", + "3fc2000000000000", + "bfe5800000000000", + "bff5400000000000", + "bff3000000000000", + "3feb000000000000", + "3fe5000000000000", + "3fe4000000000000", + "3fe8000000000000", + "bff0800000000000", + "3ff7800000000000", + "bfee800000000000", + "bfca000000000000", + "3fe6800000000000", + "3ff4000000000000", + "bfa0000000000000", + "3ff5800000000000", + "bfc2000000000000", + "bff7800000000000", + "bfd8000000000000", + "bfe4800000000000", + "bff8000000000000", + "3fea000000000000", + "3fd1000000000000", + "3fc0000000000000", + "bfd7000000000000", + "bfdc000000000000", + "bfd7000000000000", + "bfc0000000000000", + "bfd1000000000000", + "3fea000000000000", + "bff8000000000000", + "bfe4800000000000", + "3fd8000000000000", + "3ff7800000000000", + "bfc2000000000000", + "3ff5800000000000", + "bfa0000000000000", + "bff4000000000000", + "bfe6800000000000", + "bfca000000000000", + "bfee800000000000", + "3ff7800000000000", + "3ff0800000000000", + "bfe8000000000000", + "3fe4000000000000", + "3fe5000000000000", + "3feb000000000000", + "3ff3000000000000", + "3ff5400000000000", + "bfe5800000000000", + "3fc2000000000000", + "3ff1c00000000000", + "bfe9000000000000", + "bfe0000000000000", + "bff1400000000000", + "3fe0800000000000", + "bfe8000000000000", + "3ff2800000000000", + "bfca000000000000", + "bfe3000000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "3fcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "bff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "bfec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "bff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "3fe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "3ff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "3fda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "bff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "bff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "3ff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "bfc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "3fe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "3fd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "bfea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "3fc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "3fd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "bfe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "3feb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "3ff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "3feb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "bfde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "3fd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "3fce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "bfe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "3fd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "3fe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "bfc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "3ff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "bfee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "bff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "3fe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "bff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "bff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "3fe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "bff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "bfe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "bff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "3fe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "3ff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "3ff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "3fa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "3ff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "3ff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "3fe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "bfef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "bfdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "bfea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "3fee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "bff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "bff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "3fed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "bfec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "bfe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "bff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "3fdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "3ff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "3fec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "bfcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "3fe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "3fed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "3fc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "bff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "bff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "bff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "3ff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "3fef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "3f90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "3ff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "3ff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "3fea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "bfe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "bfb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "bfd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "3ff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "bfe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "bfe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "bff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "bfb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "3fd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "bfc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "3ff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "bfe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "bfed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "3ff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "bfeb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "bfe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "bff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "3fbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "3fe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "3fd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "bfed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "bfa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "bfa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "bfed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "3fd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "3fe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "3fbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "bff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "bfe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "bfeb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "3ff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "bfed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "bfe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "3ff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "bfc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "3fd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "bfb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "bff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "bfe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "bfe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "3ff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "bfd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "bfb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "bfe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "3fcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "bff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "bfec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "bff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "3fe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "3ff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "3fda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "bff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "bff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "3ff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "bfc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "3fe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "3fd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "bfea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "3fc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "3fd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "bfe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "3feb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "3ff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "3feb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "bfde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "3fd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "3fce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "bfe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "3fd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "3fe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "bfc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "3ff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "bfee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "bff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "3fe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "bff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "bff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "3fe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "bff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "bfe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "bff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "3fe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "3ff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "3ff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "3fa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "3ff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "3ff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "3fe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "bfef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "bfdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "bfea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "3fee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "bff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "bff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "3fed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "bfec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "bfe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "bff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "3fdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "3ff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "3fec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "bfcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "3fe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "3fed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "3fc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "bff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "bff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "bff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "3ff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "3fef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "3f90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "3ff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "3ff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "3fea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "bfe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "bfb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "bfd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "3ff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "bfe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "bfe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "bff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "bfb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "3fd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "bfc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "3ff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "bfe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "bfed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "3ff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "bfeb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "bfe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "bff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "3fbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "3fe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "3fd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "bfed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "bfa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "bfa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "bfed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "3fd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "3fe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "3fbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "bff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "bfe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "bfeb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "3ff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "bfed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "bfe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "3ff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "bfc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "3fd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "bfb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "bff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "bfe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "bfe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "3ff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "bfd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "bfb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "bfe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "3fcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "bff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "bfec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "bff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "3fe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "3ff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "3fda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "bff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "bff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "3ff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "bfc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "3fe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "3fd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "bfea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "3fc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "3fd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "bfe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "3feb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "3ff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "3feb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "bfde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "3fd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "3fce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "bfe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "3fd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "3fe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "bfc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "3ff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "bfee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "bff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "3fe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "bff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "bff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "3fe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "bff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "bfe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "bff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "3fe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "3ff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "3ff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "3fa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "3ff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "3ff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "3fe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "bfef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "bfdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "bfea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "3fee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "bff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "bff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "3fed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "bfec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "bfe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "bff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "3fdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "3ff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "3fec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "bfcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "3fe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "3fed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "3fc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "bff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "bff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "bff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "3ff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "3fef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "3f90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "3ff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "3ff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "3fea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "bfe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "bfb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "bfd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "3ff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "bfe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "bfe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "bff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "bfb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "3fd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "bfc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "3ff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "bfe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "bfed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "3ff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "bfeb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "bfe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "bff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "3fbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "3fe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "3fd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "bfed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "bfa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "bfa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "bfed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "3fd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "3fe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "3fbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "bff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "bfe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "bfeb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "3ff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "bfed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "bfe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "3ff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "bfc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "3fd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "bfb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "bff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "bfe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "bfe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "3ff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "bfd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "bfb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "bfe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "bfe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "3fce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "3fd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "bfde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "3feb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "3ff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "3feb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "bfe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "3fd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "3fc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "bfea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "3fd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "3fe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "bfc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "3ff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "bff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "bff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "3fda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "3ff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "3ff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "3fe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "bff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "bfec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "bff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "3fcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "3fee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "3fe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "bfd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "3fe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "3fee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "3fcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "bff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "bfec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "bff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "3fe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "3ff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "3ff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "3fda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "bff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "bff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "3ff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "bfc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "3fe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "3fd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "bfea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "3fc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "3fd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "bfe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "3feb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "3ff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "3feb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "bfde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "3fd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "3fce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "bfe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "3fd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "3fe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "bfc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "3ff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "bfee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "bff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "3fe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "bff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "bff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "3fe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "bff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "bfe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "bff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "3fe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "3ff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "3ff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "3fa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "3ff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "3ff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "3fe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "bfef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "bfdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "bfea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "3fee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "bff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "bff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "3fed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "bfec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "bfec800000000000", + "bfe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "bff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "3fdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "3ff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "3fec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "bfcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "3fe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "3fed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "3fc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "bfee800000000000", + "bff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "3fea000000000000", + "bff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "bff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "3fef800000000000", + "3fd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "3ff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "3fef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "bff2000000000000", + "3f90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "3ff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "3ff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "3fea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "bfe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "bfb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "bfd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "3ff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "bfe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "bfe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "bff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "bfb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "3fd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "3fee000000000000", + "bfc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "bff7c00000000000", + "3ff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "bfe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "bfed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "3ff1400000000000", + "3ff6800000000000", + "3ff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "bfeb000000000000", + "3ff1800000000000", + "3ff8000000000000", + "bfee800000000000", + "bfce000000000000", + "bfe4800000000000", + "bff5800000000000", + "bfc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "bff5400000000000", + "3fc0000000000000", + "bfed800000000000", + "3ff3400000000000", + "3fde000000000000", + "3fbc000000000000", + "bfe1000000000000", + "bfe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "3fe5000000000000", + "bfd3000000000000", + "3fcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "3fd4000000000000", + "3fea800000000000", + "bfec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "bfed800000000000", + "bfc6000000000000", + "bff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "bfa0000000000000", + "bfd2000000000000", + "bfdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "bfa0000000000000", + "3fe0000000000000", + "3ff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "bfed800000000000", + "bfeb000000000000", + "3fe2000000000000", + "bfec800000000000", + "3fea800000000000", + "3fd4000000000000", + "bff4c00000000000", + "3fec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "3fe5000000000000", + "bfeb800000000000", + "bfed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "3fbc000000000000", + "3fde000000000000", + "3ff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "bff5400000000000", + "bfd5000000000000", + "3ff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "bfe4800000000000", + "bfce000000000000", + "bfee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "bfeb000000000000", + "3fe8000000000000", + "3fea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "3ff1400000000000", + "bfd9000000000000", + "3fdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "bfed000000000000", + "bfe4800000000000", + "3fef800000000000", + "bfd0000000000000", + "bff5400000000000", + "bfe8800000000000", + "3fc0000000000000", + "bfe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "3ff8000000000000", + "bff7c00000000000", + "bff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "bfc2000000000000", + "3fee000000000000", + "bff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "3fd6000000000000", + "3ff3c00000000000", + "bfa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "bfb0000000000000", + "bfe3000000000000", + "bff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "bff5c00000000000", + "3ff7c00000000000", + "bff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "bfe1000000000000", + "3ff7000000000000", + "bfe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "bfe3000000000000", + "bfe7800000000000", + "3ff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "3ff7800000000000", + "3ff0000000000000", + "3fe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "bfd8000000000000", + "3fe3800000000000", + "3ff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "bfb8000000000000", + "3ff1c00000000000", + "bfe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "bfe6800000000000", + "bfe0800000000000", + "3ff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "3fea000000000000", + "bff4000000000000", + "3ff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "3ff7000000000000", + "bff1800000000000", + "bfe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "3ff2c00000000000", + "bfa8000000000000", + "3ff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "3f90000000000000", + "bff2000000000000", + "3fee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "3fef800000000000", + "bff5000000000000", + "bff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "3ff1000000000000", + "bfe3800000000000", + "3fc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "3fd0000000000000", + "3fef800000000000", + "bfe4800000000000", + "3fed000000000000", + "bfda000000000000", + "bff7400000000000", + "3fdd000000000000", + "bfd9000000000000", + "bff1400000000000", + "3ff6800000000000", + "bff0800000000000", + "3fea000000000000", + "3fe8000000000000", + "3feb000000000000", + "3ff1800000000000", + "bff8000000000000", + "bfee800000000000", + "bfce000000000000", + "3fe4800000000000", + "bff5800000000000", + "3fc4000000000000", + "3ff3000000000000", + "bfd5000000000000", + "3ff5400000000000", + "3fc0000000000000", + "3fed800000000000", + "3ff3400000000000", + "3fde000000000000", + "bfbc000000000000", + "bfe1000000000000", + "3fe9800000000000", + "bfed000000000000", + "bfeb800000000000", + "bfe5000000000000", + "bfd3000000000000", + "bfcc000000000000", + "3fec800000000000", + "bff4c00000000000", + "bfd4000000000000", + "3fea800000000000", + "3fec800000000000", + "3fe2000000000000", + "bfeb000000000000", + "3fed800000000000", + "bfc6000000000000", + "3ff1c00000000000", + "3ff2000000000000", + "3fe0000000000000", + "3fa0000000000000", + "bfd2000000000000", + "3fdc000000000000", + "bfdc000000000000", + "bfd2000000000000", + "3fa0000000000000", + "3fe0000000000000", + "bff2000000000000", + "bff1c00000000000", + "bfc6000000000000", + "3fed800000000000", + "bfeb000000000000", + "bfe2000000000000", + "bfec800000000000", + "3fea800000000000", + "bfd4000000000000", + "bff4c00000000000", + "bfec800000000000", + "3fcc000000000000", + "bfd3000000000000", + "bfe5000000000000", + "bfeb800000000000", + "3fed000000000000", + "bfe9800000000000", + "bfe1000000000000", + "bfbc000000000000", + "3fde000000000000", + "bff3400000000000", + "bfed800000000000", + "3fc0000000000000", + "3ff5400000000000", + "bfd5000000000000", + "bff3000000000000", + "bfc4000000000000", + "bff5800000000000", + "3fe4800000000000", + "bfce000000000000", + "3fee800000000000", + "3ff8000000000000", + "3ff1800000000000", + "3feb000000000000", + "3fe8000000000000", + "bfea000000000000", + "3ff0800000000000", + "3ff6800000000000", + "bff1400000000000", + "bfd9000000000000", + "bfdd000000000000", + "3ff7400000000000", + "bfda000000000000", + "3fed000000000000", + "bfe4800000000000", + "bfef800000000000", + "bfd0000000000000", + "bff5400000000000", + "3fe8800000000000", + "3fc0000000000000", + "3fe3800000000000", + "bff1000000000000", + "bff5c00000000000", + "bff8000000000000", + "bff7c00000000000", + "3ff5000000000000", + "bfef800000000000", + "bfe0000000000000", + "3fc2000000000000", + "3fee000000000000", + "3ff2000000000000", + "bf90000000000000", + "3ff4000000000000", + "bfd6000000000000", + "3ff3c00000000000", + "3fa8000000000000", + "bff2c00000000000", + "3fec000000000000", + "3fb0000000000000", + "bfe3000000000000", + "3ff1800000000000", + "bff7000000000000", + "3ff6400000000000", + "3ff5c00000000000", + "3ff7c00000000000", + "3ff4000000000000", + "bfea000000000000", + "bfcc000000000000", + "3fe1000000000000", + "3ff7000000000000", + "3fe0800000000000", + "3fe6800000000000", + "bfee000000000000", + "3fe3000000000000", + "bfe7800000000000", + "bff1c00000000000", + "3fb8000000000000", + "bfe8800000000000", + "bff7800000000000", + "3ff0000000000000", + "bfe3800000000000", + "3fd8000000000000", + "3fd3000000000000", + "3fd8000000000000", + "3fe3800000000000", + "bff0000000000000", + "bff7800000000000", + "bfe8800000000000", + "3fb8000000000000", + "3ff1c00000000000", + "3fe7800000000000", + "3fe3000000000000", + "bfee000000000000", + "3fe6800000000000", + "bfe0800000000000", + "bff7000000000000", + "3fe1000000000000", + "bfcc000000000000", + "bfea000000000000", + "bff4000000000000", + "bff7c00000000000", + "3ff5c00000000000", + "3ff6400000000000", + "bff7000000000000", + "bff1800000000000", + "3fe3000000000000", + "3fb0000000000000", + "3fec000000000000", + "bff2c00000000000", + "bfa8000000000000", + "bff3c00000000000", + "bfd6000000000000", + "3ff4000000000000", + "bf90000000000000", + "bff2000000000000", + "bfee000000000000", + "3fc2000000000000", + "bfe0000000000000", + "bfef800000000000", + "bff5000000000000", + "3ff7c00000000000", + "bff8000000000000", + "bff5c00000000000", + "bff1000000000000", + "bfe3800000000000", + "bfc0000000000000", + "3fe8800000000000", + "bff5400000000000", + "bfd0000000000000", + "3fef800000000000", + "3fe4800000000000", + "3fed000000000000", + "bfda000000000000", + "3ff7400000000000", + "3fdd000000000000", + "3fd9000000000000", + "bff1400000000000", + "3ff6800000000000" + ], + "dtype": "float64", + "shape": [ + 2, + 4096 + ] + } + }, + "name": "ordered-reduction-d4096" + } + ], + "tolerance": { + "atol": 1e-09, + "rtol": 0.0, + "status": "frozen" + } + } + }, + "schema": "org.lecore.conformance-fixture.v1" +} diff --git a/tests/native/test_liblecore_conformance.py b/tests/native/test_liblecore_conformance.py new file mode 100644 index 0000000..2e294b3 --- /dev/null +++ b/tests/native/test_liblecore_conformance.py @@ -0,0 +1,869 @@ +"""Fixture-driven differential conformance checks for the ABI-0 C kernel. + +Run directly so the shared artifact is always explicit:: + + python3 tests/native/test_liblecore_conformance.py --library /path/to/liblecore.so + +The module is safe to discover in the repository's ordinary Python suite: it +skips when no explicit artifact was supplied and never searches the machine for +a library or dispatches away from the NumPy implementation. +""" + +from __future__ import annotations + +import argparse +import copy +import gc +import json +from pathlib import Path +import platform +import queue +import re +import sys +import threading +from typing import Any, Dict, Optional +import unittest +import weakref + +import numpy as np + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPOSITORY_ROOT)) + +from bindings.python.lecore_native import ( # noqa: E402 + LECORE_BACKEND_DIRECT, + LECORE_CAP_RADIX2, + LECORE_PROFILE_HRR_F32_V1, + LECORE_PROFILE_HRR_F64_V1, + Library, + LeCoreClosedError, + LeCoreCompatibilityError, + LeCoreNonFiniteError, + LeCoreThreadError, +) +from tools.generate_liblecore_fixtures import render_fixture # noqa: E402 + + +DEFAULT_FIXTURE = REPOSITORY_ROOT / "tests/native/fixtures/liblecore_isa_v1.json" +_LIBRARY_PATH: Optional[str] = None +_FIXTURE_PATH: Path = DEFAULT_FIXTURE +_BACKEND = "direct" +_REPORT: Dict[str, Any] = {} +_BACKEND_NAMES = {0: "auto", 1: "direct", 2: "radix2"} + + +def _decode(specification: dict) -> np.ndarray: + dtype = np.dtype(specification["dtype"]) + if dtype == np.dtype(np.float64): + unsigned_dtype = np.uint64 + elif dtype == np.dtype(np.float32): + unsigned_dtype = np.uint32 + else: + raise AssertionError(f"unsupported fixture dtype {dtype}") + bits = np.asarray( + [int(value, 16) for value in specification["bits"]], + dtype=unsigned_dtype, + ) + return bits.view(dtype).reshape(tuple(specification["shape"])).copy(order="C") + + +def _scalar(specification: dict) -> float: + return float(_decode(specification).reshape(())) + + +def _profile_key(profile: str) -> str: + return "HRR_F64_V1" if profile == "f64" else "HRR_F32_V1" + + +def _compiler_metadata(artifact: str) -> dict: + """Read CMake's adjacent, immutable compiler record when available.""" + build_directory = Path(artifact).resolve().parent + metadata = {"status": "not exposed by ABI-0 artifact introspection"} + cache = build_directory / "CMakeCache.txt" + if cache.is_file(): + match = re.search( + r"^CMAKE_C_COMPILER:FILEPATH=(.+)$", + cache.read_text(encoding="utf-8", errors="replace"), + re.MULTILINE, + ) + if match: + metadata["path"] = match.group(1) + records = sorted(build_directory.glob("CMakeFiles/*/CMakeCCompiler.cmake")) + if records: + contents = records[-1].read_text(encoding="utf-8", errors="replace") + for field, key in ( + ("CMAKE_C_COMPILER_ID", "id"), + ("CMAKE_C_COMPILER_VERSION", "version"), + ): + match = re.search(rf'set\({field} "([^"]*)"\)', contents) + if match: + metadata[key] = match.group(1) + if "id" in metadata: + metadata["status"] = "reported by adjacent CMake build metadata" + return metadata + + +def _record_value(profile: str, operation: str, difference: float) -> None: + values = _REPORT.setdefault("profiles", {}).setdefault(profile, {}).setdefault( + "max_abs_error", {} + ) + values[operation] = max(float(values.get(operation, 0.0)), float(difference)) + + +def _record_decision(profile: str, operation: str, passed: bool) -> None: + decisions = _REPORT.setdefault("profiles", {}).setdefault(profile, {}).setdefault( + "decisions", {} + ) + result = decisions.setdefault(operation, {"checked": 0, "agreed": 0}) + result["checked"] += 1 + result["agreed"] += int(bool(passed)) + + +def _require_backend_capability(library, backend: str) -> None: + if backend == "radix2" and not library.capabilities & LECORE_CAP_RADIX2: + raise RuntimeError( + "forced radix2 conformance requires LECORE_CAP_RADIX2; " + "the selected artifact does not advertise it" + ) + + +class LiblecoreConformance(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + if _LIBRARY_PATH is None: + raise unittest.SkipTest("no explicit --library artifact supplied") + cls.library = Library(_LIBRARY_PATH) + cls.fixture_text = _FIXTURE_PATH.read_text(encoding="utf-8") + cls.fixture = json.loads(cls.fixture_text) + _REPORT.update( + { + "schema": "org.lecore.conformance-report.v1", + "artifact": { + "path": cls.library.path, + "version": cls.library.version, + "abi": cls.library.abi_version, + "isa": cls.library.isa_version, + "capabilities": f"0x{cls.library.capabilities:016x}", + }, + "fixture": { + "path": str(_FIXTURE_PATH.resolve()), + "schema": cls.fixture["schema"], + "generator": cls.fixture["generator"], + }, + "target": { + "system": platform.system(), + "release": platform.release(), + "machine": platform.machine(), + "python": platform.python_version(), + "compiler": _compiler_metadata(cls.library.path), + }, + "backend_requested": _BACKEND, + "backends_observed": [], + "errors": {}, + "profiles": { + "f64": { + "profile_id": "0x00010001", + "tolerance": cls.fixture["profiles"]["HRR_F64_V1"]["tolerance"], + }, + "f32": { + "profile_id": "0x00010002", + "tolerance": cls.fixture["profiles"]["HRR_F32_V1"]["tolerance"], + }, + }, + } + ) + _require_backend_capability(cls.library, _BACKEND) + + def _supports_dimension(self, dimension: int) -> bool: + return _BACKEND != "radix2" or ( + dimension > 0 and dimension & (dimension - 1) == 0 + ) + + def _context(self, dimension: int, profile: str, *, finite: bool = False): + context = self.library.context( + dimension, + profile=profile, + backend=_BACKEND, + finite=finite, + ) + observed = _REPORT["backends_observed"] + backend_record = { + "id": context.backend, + "name": _BACKEND_NAMES.get(context.backend, "unknown"), + } + if backend_record not in observed: + observed.append(backend_record) + return context + + def _assert_values( + self, + actual: np.ndarray, + expected: np.ndarray, + *, + profile: str, + operation: str, + atol: float, + exact: bool = False, + ) -> None: + self.assertEqual(actual.dtype, expected.dtype) + self.assertEqual(actual.shape, expected.shape) + self.assertTrue(actual.flags.c_contiguous) + if exact: + unsigned = np.uint64 if actual.dtype.itemsize == 8 else np.uint32 + agreement = np.array_equal( + actual.reshape(-1).view(unsigned), + expected.reshape(-1).view(unsigned), + ) + _record_decision(profile, operation, agreement) + self.assertTrue(agreement, f"{profile}/{operation} is not bit exact") + return + self.assertTrue(np.array_equal(np.isnan(actual), np.isnan(expected))) + self.assertTrue(np.array_equal(np.isposinf(actual), np.isposinf(expected))) + self.assertTrue(np.array_equal(np.isneginf(actual), np.isneginf(expected))) + self.assertTrue(np.array_equal(np.isfinite(actual), np.isfinite(expected))) + finite = np.isfinite(expected) + difference = ( + float(np.max(np.abs(actual[finite] - expected[finite]))) + if np.any(finite) + else 0.0 + ) + _record_value(profile, operation, difference) + self.assertLessEqual( + difference, + atol, + f"{profile}/{operation} max abs error {difference} exceeds {atol}", + ) + + def _assert_scalar( + self, + actual: float, + expected: float, + *, + profile: str, + operation: str, + atol: float, + ) -> None: + if np.isnan(expected): + self.assertTrue(np.isnan(actual)) + difference = 0.0 + elif np.isposinf(expected): + self.assertTrue(np.isposinf(actual)) + difference = 0.0 + elif np.isneginf(expected): + self.assertTrue(np.isneginf(actual)) + difference = 0.0 + else: + self.assertTrue(np.isfinite(actual)) + difference = abs(float(actual) - float(expected)) + self.assertLessEqual(difference, atol) + _record_value(profile, operation, difference) + + def test_00_fixture_metadata_and_regeneration(self) -> None: + self.assertEqual(self.fixture["schema"], "org.lecore.conformance-fixture.v1") + self.assertEqual(self.fixture["abi_preview"], 0) + self.assertEqual(self.fixture["isa_version"], 1) + self.assertEqual(self.fixture["generator"]["algorithm"], "index-formula-v1/no-rng") + self.assertEqual(self.fixture_text, render_fixture()) + missing_radix = type("MissingRadix", (), {"capabilities": 0})() + _require_backend_capability(missing_radix, "direct") + with self.assertRaises(RuntimeError): + _require_backend_capability(missing_radix, "radix2") + + def test_01_explicit_artifact_and_context_metadata(self) -> None: + with self.assertRaises(TypeError): + Library(None) # type: ignore[arg-type] + with self.assertRaises(FileNotFoundError): + Library(_FIXTURE_PATH.parent / "not-a-library") + with self.assertRaises(LeCoreCompatibilityError): + Library(self.library.path, expected_abi=1) + with self.assertRaises(LeCoreCompatibilityError): + Library(self.library.path, expected_isa=2) + with self.assertRaises(LeCoreCompatibilityError): + Library(self.library.path, expected_abi=False) + with self.assertRaises(LeCoreCompatibilityError): + Library(self.library.path, expected_isa=1.0) # type: ignore[arg-type] + # Unsupported adapter contracts are rejected before artifact resolution, + # and therefore before ABI-0 ctypes declarations can be installed. + with self.assertRaises(LeCoreCompatibilityError): + Library( + _FIXTURE_PATH.parent / "not-a-library", + expected_abi=1, + ) + + expected_profiles = { + "f64": LECORE_PROFILE_HRR_F64_V1, + "f32": LECORE_PROFILE_HRR_F32_V1, + } + for profile, profile_id in expected_profiles.items(): + with self._context(8, profile) as context: + self.assertEqual(context.dimension, 8) + self.assertEqual(context.profile, profile_id) + self.assertGreater(context.scratch_bytes, 0) + if _BACKEND == "direct": + self.assertEqual(context.backend, LECORE_BACKEND_DIRECT) + self.assertTrue(context.closed) + with self.assertRaises(LeCoreClosedError): + context.normalize(np.zeros(8, dtype=context.dtype)) + + def test_02_strict_arrays_aliases_and_lifetime(self) -> None: + dimension = 8 if _BACKEND == "radix2" else 3 + case = next( + item + for item in self.fixture["profiles"]["HRR_F64_V1"]["finite_cases"] + if int(item["dimension"]) == dimension + ) + a = _decode(case["inputs"]["a"]) + b = _decode(case["inputs"]["b"]) + with self._context(dimension, "f64") as context: + with self.assertRaises(TypeError): + context.normalize([1.0, 2.0, 3.0]) # type: ignore[arg-type] + with self.assertRaises(TypeError): + context.normalize(a.astype(np.float32)) + with self.assertRaises(ValueError): + context.normalize(np.zeros(dimension + 1, dtype=np.float64)) + with self.assertRaises(ValueError): + context.normalize(np.zeros(dimension * 2, dtype=np.float64)[::2]) + read_only = np.empty(dimension, dtype=np.float64) + read_only.flags.writeable = False + with self.assertRaises(ValueError): + context.normalize(a, out=read_only) + + overlapping = np.arange(dimension + 1, dtype=np.float64) + with self.assertRaises(ValueError): + context.normalize( + overlapping[:dimension], out=overlapping[1 : dimension + 1] + ) + + expected = context.normalize(a) + aliased = a.copy() + returned = context.normalize(aliased, out=aliased) + self.assertIs(returned, aliased) + np.testing.assert_allclose(aliased, expected, atol=1e-9, rtol=0) + + expected = context.involution(a) + aliased = a.copy() + context.involution(aliased, out=aliased) + np.testing.assert_array_equal(aliased, expected) + + expected = context.permute(a, -5) + aliased = a.copy() + context.permute(aliased, -5, out=aliased) + np.testing.assert_array_equal(aliased, expected) + + expected = context.bind(a, b) + aliased_a = a.copy() + context.bind(aliased_a, b, out=aliased_a) + np.testing.assert_allclose(aliased_a, expected, atol=1e-9, rtol=0) + aliased_b = b.copy() + context.bind(a, aliased_b, out=aliased_b) + np.testing.assert_allclose(aliased_b, expected, atol=1e-9, rtol=0) + + expected = context.unbind(a, b) + aliased = a.copy() + context.unbind(aliased, b, out=aliased) + np.testing.assert_allclose(aliased, expected, atol=1e-9, rtol=0) + + rows = np.stack((a, b)) + with self.assertRaises(ValueError): + context.bundle(rows, out=rows[0]) + with self.assertRaises(ValueError): + context.bind_batch(rows, rows, out=rows) + + def test_02b_unique_thread_ownership_and_nonfinite_assertions(self) -> None: + dimension = 8 + case = next( + item + for item in self.fixture["profiles"]["HRR_F64_V1"]["finite_cases"] + if int(item["dimension"]) == dimension + ) + a = _decode(case["inputs"]["a"]) + b = _decode(case["inputs"]["b"]) + with self._context(dimension, "f64") as context: + with self.assertRaises(TypeError): + copy.copy(context) + with self.assertRaises(TypeError): + copy.deepcopy(context) + + barrier = threading.Barrier(5) + results = queue.Queue() # type: queue.Queue + + def attempt(label, operation) -> None: + barrier.wait(timeout=5.0) + try: + operation() + except BaseException as error: + results.put((label, error)) + else: + results.put((label, None)) + + operations = { + "call": lambda: context.dot(a, b), + "close": context.close, + "scratch_property": lambda: context.scratch_bytes, + "closed_property": lambda: context.closed, + } + threads = [ + threading.Thread(target=attempt, args=(label, operation)) + for label, operation in operations.items() + ] + for thread in threads: + thread.start() + barrier.wait(timeout=5.0) + for thread in threads: + thread.join(timeout=5.0) + self.assertFalse(thread.is_alive()) + + observed = {} + for _ in threads: + label, error = results.get_nowait() + observed[label] = error + self.assertEqual(set(observed), set(operations)) + for error in observed.values(): + self.assertIsInstance(error, LeCoreThreadError) + + # Rejected foreign-thread access cannot close or corrupt the owner’s + # context, and the native handle remains usable on its creator. + self.assertFalse(context.closed) + self._assert_scalar( + context.dot(a, b), + _scalar(case["expected"]["dot"]), + profile="f64", + operation="dot_after_thread_rejection", + atol=1e-9, + ) + + expected_nonfinite = np.asarray([np.inf, -np.inf, np.nan], dtype=np.float64) + for wrong_nonfinite in ( + np.asarray([-np.inf, -np.inf, np.nan], dtype=np.float64), + np.asarray([1.0, -np.inf, np.nan], dtype=np.float64), + ): + with self.assertRaises(AssertionError): + self._assert_values( + wrong_nonfinite, + expected_nonfinite, + profile="harness", + operation="nonfinite_classification_regression", + atol=0.0, + ) + + def test_02c_foreign_thread_finalization_is_exactly_once(self) -> None: + tracking_library = Library(self.library.path) + original_destroy = tracking_library._dll.lecore_context_destroy + destroy_threads = [] + destroy_lock = threading.Lock() + + def tracked_destroy(pointer) -> None: + with destroy_lock: + destroy_threads.append(threading.get_ident()) + original_destroy(pointer) + + tracking_library._dll.lecore_context_destroy = tracked_destroy + + def dispose_last_reference_on_foreign_thread(holder, reference): + release_threads = [] + + def release() -> None: + release_threads.append(threading.get_ident()) + value = holder.pop() + del value + gc.collect() + + thread = threading.Thread(target=release) + thread.start() + thread.join(timeout=5.0) + self.assertFalse(thread.is_alive()) + self.assertEqual(holder, []) + self.assertIsNone(reference()) + self.assertEqual(len(release_threads), 1) + return release_threads[0] + + try: + unclosed = tracking_library.context(8, profile="f64", backend="direct") + unclosed_reference = weakref.ref(unclosed) + unclosed_holder = [unclosed] + del unclosed + release_thread = dispose_last_reference_on_foreign_thread( + unclosed_holder, + unclosed_reference, + ) + self.assertEqual(len(destroy_threads), 1) + self.assertEqual(destroy_threads[0], release_thread) + self.assertNotEqual(destroy_threads[0], threading.get_ident()) + gc.collect() + self.assertEqual(len(destroy_threads), 1) + + owner_exit_holder = [] + + def create_then_exit() -> None: + owner_exit_holder.append( + tracking_library.context(8, profile="f64", backend="direct") + ) + + creator_thread = threading.Thread(target=create_then_exit) + creator_thread.start() + creator_thread.join(timeout=5.0) + self.assertFalse(creator_thread.is_alive()) + self.assertEqual(len(owner_exit_holder), 1) + after_exit_reference = weakref.ref(owner_exit_holder[0]) + after_exit_release_thread = dispose_last_reference_on_foreign_thread( + owner_exit_holder, + after_exit_reference, + ) + self.assertEqual(len(destroy_threads), 2) + self.assertEqual(destroy_threads[1], after_exit_release_thread) + + explicitly_closed = tracking_library.context( + 8, profile="f64", backend="direct" + ) + explicitly_closed.close() + self.assertEqual(len(destroy_threads), 3) + self.assertEqual(destroy_threads[2], threading.get_ident()) + closed_reference = weakref.ref(explicitly_closed) + closed_holder = [explicitly_closed] + del explicitly_closed + dispose_last_reference_on_foreign_thread( + closed_holder, + closed_reference, + ) + gc.collect() + self.assertEqual( + len(destroy_threads), + 3, + "finalization destroyed an explicitly closed context twice", + ) + finally: + tracking_library._dll.lecore_context_destroy = original_destroy + + def _exercise_finite_profile(self, profile: str) -> None: + specification = self.fixture["profiles"][_profile_key(profile)] + atol = float(specification["tolerance"]["atol"]) + for case in specification["finite_cases"]: + dimension = int(case["dimension"]) + if not self._supports_dimension(dimension): + continue + inputs = case["inputs"] + expected = case["expected"] + a = _decode(inputs["a"]) + b = _decode(inputs["b"]) + rows = _decode(inputs["rows"]) + paired_left = _decode(inputs["paired_left"]) + paired_right = _decode(inputs["paired_right"]) + with self._context(dimension, profile) as context: + self._assert_values( + context.normalize(a), + _decode(expected["normalize"]), + profile=profile, + operation="normalize", + atol=atol, + ) + self._assert_scalar( + context.dot(a, b), + _scalar(expected["dot"]), + profile=profile, + operation="dot", + atol=atol, + ) + self._assert_values( + context.dot_many(a, rows), + _decode(expected["dot_many"]), + profile=profile, + operation="dot_many", + atol=atol, + ) + self._assert_scalar( + context.cosine(a, b), + _scalar(expected["cosine"]), + profile=profile, + operation="cosine", + atol=atol, + ) + native_scores = context.cosine_many(a, rows) + expected_scores = _decode(expected["cosine_many"]) + self._assert_values( + native_scores, + expected_scores, + profile=profile, + operation="cosine_many", + atol=atol, + ) + decision_agrees = int(np.argmax(native_scores)) == int( + np.argmax(expected_scores) + ) + _record_decision(profile, "cosine_many_argmax", decision_agrees) + self.assertTrue(decision_agrees) + self._assert_values( + context.bind(a, b), + _decode(expected["bind"]), + profile=profile, + operation="bind", + atol=atol, + ) + self._assert_values( + context.bind(b, a), + _decode(expected["bind"]), + profile=profile, + operation="bind_commutativity", + atol=atol, + ) + self._assert_values( + context.unbind(a, b), + _decode(expected["unbind"]), + profile=profile, + operation="unbind", + atol=atol, + ) + self._assert_values( + context.involution(a), + _decode(expected["involution"]), + profile=profile, + operation="involution", + atol=0.0, + exact=True, + ) + self._assert_values( + context.permute(a, int(case["shift"])), + _decode(expected["permute"]), + profile=profile, + operation="permute", + atol=0.0, + exact=True, + ) + self._assert_values( + context.bundle(rows), + _decode(expected["bundle"]), + profile=profile, + operation="bundle", + atol=atol, + ) + index, score = context.cleanup(a, rows) + index_agrees = index == int(expected["cleanup"]["index"]) + _record_decision(profile, "cleanup", index_agrees) + self.assertTrue(index_agrees) + self._assert_scalar( + score, + _scalar(expected["cleanup"]["score"]), + profile=profile, + operation="cleanup_score", + atol=atol, + ) + self._assert_values( + context.bind_batch(paired_left, paired_right), + _decode(expected["bind_batch"]), + profile=profile, + operation="bind_batch", + atol=atol, + ) + self._assert_values( + context.bind_fixed(a, rows), + _decode(expected["bind_fixed"]), + profile=profile, + operation="bind_fixed", + atol=atol, + ) + self._assert_values( + context.unbind_all(a, rows), + _decode(expected["unbind_all"]), + profile=profile, + operation="unbind_all", + atol=atol, + ) + + def test_03_f64_fixture_operations_and_batches(self) -> None: + self._exercise_finite_profile("f64") + + def test_04_f32_fixture_operations_and_batches(self) -> None: + self._exercise_finite_profile("f32") + + def test_05_ordered_reduction_dimensions(self) -> None: + for profile in ("f64", "f32"): + specification = self.fixture["profiles"][_profile_key(profile)] + atol = float(specification["tolerance"]["atol"]) + for case in specification["reduction_cases"]: + dimension = int(case["dimension"]) + if not self._supports_dimension(dimension): + continue + query = _decode(case["inputs"]["query"]) + rows = _decode(case["inputs"]["rows"]) + with self._context(dimension, profile) as context: + self._assert_values( + context.dot_many(query, rows), + _decode(case["expected"]["dot_many"]), + profile=profile, + operation=f"dot_many_d{dimension}", + atol=atol, + ) + self._assert_values( + context.cosine_many(query, rows), + _decode(case["expected"]["cosine_many"]), + profile=profile, + operation=f"cosine_many_d{dimension}", + atol=atol, + ) + + def test_06_mixed_f64_f32_scorer(self) -> None: + profile = self.fixture["profiles"]["HRR_F32_V1"] + for case in profile["finite_cases"]: + dimension = int(case["dimension"]) + if not self._supports_dimension(dimension): + continue + query = _decode(case["inputs"]["mixed_query"]) + rows = _decode(case["inputs"]["rows"]) + expected = _decode(case["expected"]["cosine_many_f64_f32"]) + with self._context(dimension, "f32") as context: + self._assert_values( + context.cosine_many_f64_f32(query, rows), + expected, + profile="mixed_f64_f32", + operation="cosine_many", + atol=1e-9, + ) + with self.assertRaises(TypeError): + context.cosine_many_f64_f32(query.astype(np.float32), rows) + with self._context(dimension, "f64") as wrong_context: + with self.assertRaises(LeCoreCompatibilityError): + wrong_context.cosine_many_f64_f32(query, rows) + + def test_07_zero_nonfinite_and_cleanup_decisions(self) -> None: + for profile in ("f64", "f32"): + specification = self.fixture["profiles"][_profile_key(profile)] + atol = float(specification["tolerance"]["atol"]) + for edge in specification["edge_cases"]: + dimension = int(edge["dimension"]) + if not self._supports_dimension(dimension): + continue + inputs = edge["inputs"] + finite = _decode(inputs["finite"]) + zero = _decode(inputs["zero"]) + nan_vector = _decode(inputs["nan_vector"]) + inf_vector = _decode(inputs["inf_vector"]) + impulse = _decode(inputs["impulse"]) + shifted_impulse = _decode(inputs["shifted_impulse"]) + ties = _decode(inputs["ties"]) + first_nan = _decode(inputs["first_nan_candidates"]) + zero_sum_rows = _decode(inputs["zero_sum_rows"]) + reindex_special = _decode(inputs["reindex_special"]) + expected = edge["expected"] + with self._context(dimension, profile) as context: + self._assert_values( + context.involution(reindex_special), + _decode(expected["special_involution"]), + profile=profile, + operation="special_involution_bits", + atol=0.0, + exact=True, + ) + self._assert_values( + context.permute(reindex_special, -2), + _decode(expected["special_permute_minus_two"]), + profile=profile, + operation="special_permute_bits", + atol=0.0, + exact=True, + ) + self._assert_values( + context.bind(impulse, finite), + _decode(expected["impulse_bind"]), + profile=profile, + operation="impulse_bind", + atol=atol, + ) + self._assert_values( + context.bind(shifted_impulse, finite), + _decode(expected["shifted_impulse_bind"]), + profile=profile, + operation="shifted_impulse_bind", + atol=atol, + ) + np.testing.assert_array_equal( + context.normalize(zero), _decode(expected["zero_normalize"]) + ) + np.testing.assert_array_equal( + context.bundle(zero_sum_rows), _decode(expected["zero_sum_bundle"]) + ) + zero_nan = context.cosine(zero, nan_vector) + self.assertEqual(zero_nan, 0.0) + self.assertFalse(np.signbit(zero_nan)) + tied_index, _ = context.cleanup(finite, ties) + tied_agrees = tied_index == int(expected["tie_cleanup_index"]) + _record_decision(profile, "lowest_index_tie", tied_agrees) + self.assertTrue(tied_agrees) + nan_index, nan_score = context.cleanup(finite, first_nan) + nan_agrees = nan_index == int(expected["first_nan_cleanup_index"]) + _record_decision(profile, "first_nan", nan_agrees) + self.assertTrue(nan_agrees) + self.assertTrue(np.isnan(nan_score)) + self.assertTrue(np.all(np.isnan(context.bind(finite, nan_vector)))) + self.assertTrue(np.any(~np.isfinite(context.bind(finite, inf_vector)))) + + with self._context(dimension, profile, finite=True) as checked: + for bad in (nan_vector, inf_vector): + output = np.full(dimension, 17.0, dtype=finite.dtype) + with self.assertRaises(LeCoreNonFiniteError): + checked.normalize(bad, out=output) + np.testing.assert_array_equal( + output, np.full(dimension, 17.0, dtype=finite.dtype) + ) + _REPORT["errors"][f"{profile}_finite_rejection"] = ( + _REPORT["errors"].get(f"{profile}_finite_rejection", 0) + 1 + ) + for bad in (nan_vector, inf_vector): + with self.assertRaises(LeCoreNonFiniteError): + self.library.validate(bad) + + def test_08_mixed_zero_nan_precedence(self) -> None: + edge = next( + item + for item in self.fixture["profiles"]["HRR_F32_V1"]["edge_cases"] + if self._supports_dimension(int(item["dimension"])) + ) + dimension = int(edge["dimension"]) + query = _decode(edge["inputs"]["zero"]).astype(np.float64) + rows = np.stack( + ( + _decode(edge["inputs"]["nan_vector"]), + _decode(edge["inputs"]["finite"]), + ) + ) + with self._context(dimension, "f32") as context: + scores = context.cosine_many_f64_f32(query, rows) + np.testing.assert_array_equal(scores, np.zeros(2, dtype=np.float64)) + self.assertFalse(np.any(np.signbit(scores))) + + +def _parse_arguments() -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--library", required=True, help="explicit shared-library artifact") + parser.add_argument("--fixture", type=Path, default=DEFAULT_FIXTURE) + parser.add_argument("--backend", choices=("direct", "radix2", "auto"), default="direct") + parser.add_argument("--report", type=Path, help="also write the JSON report to this path") + parser.add_argument("--quiet", action="store_true") + return parser.parse_args() + + +def main() -> None: + global _LIBRARY_PATH, _FIXTURE_PATH, _BACKEND + arguments = _parse_arguments() + _LIBRARY_PATH = arguments.library + _FIXTURE_PATH = arguments.fixture + _BACKEND = arguments.backend + suite = unittest.defaultTestLoader.loadTestsFromTestCase(LiblecoreConformance) + result = unittest.TextTestRunner(verbosity=1 if arguments.quiet else 2).run(suite) + _REPORT["result"] = { + "passed": result.wasSuccessful(), + "tests_run": result.testsRun, + "failures": len(result.failures), + "errors": len(result.errors), + "skipped": len(result.skipped), + } + rendered = json.dumps(_REPORT, indent=2, sort_keys=True) + "\n" + if arguments.report is not None: + arguments.report.parent.mkdir(parents=True, exist_ok=True) + arguments.report.write_text(rendered, encoding="utf-8") + print(rendered, end="") + if not result.wasSuccessful(): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/native/test_liblecore_performance_gate.py b/tests/native/test_liblecore_performance_gate.py new file mode 100644 index 0000000..ffa31a9 --- /dev/null +++ b/tests/native/test_liblecore_performance_gate.py @@ -0,0 +1,383 @@ +#!/usr/bin/env python3 +"""Regression tests for the liblecore performance-policy checker.""" + +from __future__ import annotations + +import copy +from contextlib import redirect_stderr, redirect_stdout +import io +import json +import math +from pathlib import Path +import statistics +import sys +import tempfile +import unittest + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPOSITORY_ROOT)) + +from benchmarks.bench_liblecore import ( # noqa: E402 + MAXIMUM_CALIBRATED_ITERATIONS, + MAXIMUM_MEASUREMENT_ATTEMPTS, + TimingSamples, + calibrated_iterations, + measure_with_escalation, +) +from benchmarks.check_liblecore_performance import GateInputError, evaluate, main # noqa: E402 + + +def policy(): + return { + "schema_version": 2, + "report_schema_version": 2, + "required_policy_effect": "none; AUTO remains unchanged", + "required_measurement_layer": "public-c-abi", + "required_measurement_mode": "pre-resolved-bind", + "required_metrics_scope": "gate", + "required_baseline_comparison_mode": "paired-interleaved", + "required_bootstrap_comparison_mode": "single", + "minimum_repeats": 10, + "minimum_calibration_pilots": 3, + "minimum_optimized_iterations": 4000, + "minimum_sample_elapsed_ns": 10_000_000, + "max_candidate_slowdown": 1.35, + "dimensions": [256], + "required_library": { + "abi": 0, + "isa": 1, + "capabilities_mask": 15, + "auto_backend": 1, + }, + "profiles": { + "f64": {"max_abs_delta": 1e-12, "minimum_speedup": {"256": 2.0}}, + "f32": {"max_abs_delta": 1e-4, "minimum_speedup": {"256": 2.0}}, + }, + } + + +def _set_samples(entry, backend, per_call_ns): + iterations_field = ( + "direct_iterations" if backend == "direct" else "optimized_iterations" + ) + elapsed_field = f"{backend}_elapsed_ns" + latency_field = f"{backend}_ns" + iterations = entry[iterations_field] + elapsed = [int(round(value * iterations)) for value in per_call_ns] + entry[elapsed_field] = elapsed + entry[latency_field] = float( + statistics.median(value / iterations for value in elapsed) + ) + entry["radix2_speedup"] = entry["direct_ns"] / entry["radix2_ns"] + + +def _set_latency(entry, backend, latency_ns): + _set_samples(entry, backend, [latency_ns] * 10) + + +def report(comparison_mode="single"): + results = [] + for profile, delta in (("f64", 1e-15), ("f32", 1e-7)): + results.append( + { + "profile": profile, + "dimension": 256, + "auto_backend": 1, + "direct_iterations": 500, + "optimized_iterations": 4000, + "direct_elapsed_ns": [20_000_000] * 10, + "radix2_elapsed_ns": [32_000_000] * 10, + "direct_ns": 40_000.0, + "radix2_ns": 8_000.0, + "radix2_speedup": 5.0, + "radix2_max_abs_vs_direct": delta, + } + ) + benchmark = { + "comparison_mode": comparison_mode, + "measurement_layer": "public-c-abi", + "measurement_mode": "pre-resolved-bind", + "metrics_scope": "gate", + "repeats": 10, + "target_work": 64_000_000, + "sample_target_ns": 30_000_000, + "calibration_pilots": 3, + "maximum_calibrated_iterations": 1_000_000, + "maximum_measurement_attempts": 3, + "minimum_optimized_iterations": 4000, + } + if comparison_mode == "paired-interleaved": + benchmark["pair_id"] = "test-pair" + return { + "schema_version": 2, + "policy_effect": "none; AUTO remains unchanged", + "benchmark": benchmark, + "library": {"version": "0.1.0", "abi": 0, "isa": 1, "capabilities": 255}, + "host": { + "platform": "test", + "machine": "test", + "python": "3.9", + "numpy": "test", + }, + "results": results, + } + + +class PerformanceGateTests(unittest.TestCase): + def test_calibration_uses_fastest_equal_count_pilot_across_pair(self): + candidate = lambda: None + baseline = lambda: None + pilot_elapsed = { + candidate: iter((100_000_000, 1_000_000, 2_000_000)), + baseline: iter((80_000_000, 3_000_000, 4_000_000)), + } + observed_counts = [] + + def fake_measure(operation, iterations): + observed_counts.append(iterations) + return next(pilot_elapsed[operation]) + + iterations = calibrated_iterations( + (candidate, baseline), + 100, + 30_000_000, + measure_elapsed=fake_measure, + ) + self.assertEqual(iterations, 3750) + self.assertEqual(observed_counts, [100] * 6) + + def test_fast_final_batch_is_discarded_and_pair_is_escalated_together(self): + operation = lambda: None + iterations = calibrated_iterations( + (operation,), + 100, + 30_000_000, + measure_elapsed=lambda _operation, _iterations: 40_000_000, + ) + self.assertEqual(iterations, 100) + measured_counts = [] + + def fake_paired_measure(count): + measured_counts.append(count) + if len(measured_counts) == 1: + return ( + TimingSamples(count, [5_000_000] * 10), + TimingSamples(count, [50_000_000] * 10), + ) + return ( + TimingSamples(count, [37_500_000] * 10), + TimingSamples(count, [375_000_000] * 10), + ) + + candidate, baseline = measure_with_escalation( + fake_paired_measure, iterations, 30_000_000 + ) + self.assertEqual(measured_counts, [100, 750]) + self.assertEqual(candidate.iterations, baseline.iterations) + self.assertEqual(candidate.iterations, 750) + self.assertGreaterEqual(min(candidate.elapsed_ns), 30_000_000) + + def test_measurement_escalation_is_attempt_and_iteration_bounded(self): + measured_counts = [] + + def always_short(count): + measured_counts.append(count) + return (TimingSamples(count, [1_000_000] * 10),) + + with self.assertRaisesRegex(RuntimeError, "bounded measurement attempts"): + measure_with_escalation(always_short, 100, 30_000_000) + self.assertEqual(len(measured_counts), MAXIMUM_MEASUREMENT_ATTEMPTS) + self.assertLessEqual(max(measured_counts), MAXIMUM_CALIBRATED_ITERATIONS) + self.assertEqual(measured_counts, sorted(measured_counts)) + + def test_valid_bootstrap_report_passes_and_renders_scores(self): + result = evaluate(report(), policy()) + self.assertTrue(result.passed) + self.assertIn("Gate: PASS", result.markdown) + self.assertIn("public C ABI", result.markdown) + self.assertIn("5.00x", result.markdown) + self.assertEqual(len(result.measurements), 2) + self.assertFalse(result.baseline_compared) + + def test_same_runner_paired_baseline_passes_and_is_rendered(self): + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") + _set_latency(candidate["results"][0], "direct", 42_000.0) + _set_latency(candidate["results"][0], "radix2", 8_400.0) + result = evaluate(candidate, policy(), baseline) + self.assertTrue(result.passed) + self.assertTrue(result.baseline_compared) + self.assertIn("median paired slowdown", result.markdown) + self.assertIn("1.05x", result.markdown) + + def test_proportional_candidate_slowdown_fails_against_base(self): + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") + for entry in candidate["results"]: + _set_latency(entry, "direct", 60_000.0) + _set_latency(entry, "radix2", 12_000.0) + result = evaluate(candidate, policy(), baseline) + self.assertFalse(result.passed) + joined = "\n".join(result.failures) + self.assertIn("direct candidate/base slowdown 1.50x", joined) + self.assertIn("radix-2 candidate/base slowdown 1.50x", joined) + + def test_gate_uses_median_of_paired_ratios(self): + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") + base_samples = [40_000.0] * 5 + [400_000.0] * 5 + candidate_samples = [80_000.0] * 5 + [400_000.0] * 5 + _set_samples(baseline["results"][0], "direct", base_samples) + _set_samples(candidate["results"][0], "direct", candidate_samples) + + ratio_of_medians = ( + candidate["results"][0]["direct_ns"] + / baseline["results"][0]["direct_ns"] + ) + self.assertLess(ratio_of_medians, 1.35) + result = evaluate(candidate, policy(), baseline) + self.assertFalse(result.passed) + self.assertIn( + "direct candidate/base slowdown 1.50x", "\n".join(result.failures) + ) + + def test_every_backend_sample_must_meet_duration_floor(self): + for backend in ("direct", "radix2"): + with self.subTest(backend=backend): + candidate = report() + candidate["results"][0][f"{backend}_elapsed_ns"][0] = 9_999_999 + with self.assertRaisesRegex(GateInputError, "sample duration"): + evaluate(candidate, policy()) + + def test_pair_identity_and_sample_count_are_required(self): + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") + baseline["benchmark"]["pair_id"] = "another-run" + with self.assertRaisesRegex(GateInputError, "same paired run"): + evaluate(candidate, policy(), baseline) + + candidate = report() + candidate["results"][0]["direct_elapsed_ns"].pop() + with self.assertRaisesRegex(GateInputError, "expected benchmark.repeats"): + evaluate(candidate, policy()) + + def test_speed_regression_fails_when_policy_declares_a_floor(self): + candidate = report() + _set_latency(candidate["results"][0], "radix2", 30_000.0) + result = evaluate(candidate, policy()) + self.assertFalse(result.passed) + self.assertIn("below 2.00x", "\n".join(result.failures)) + + def test_speed_floor_is_optional_for_backend_optimization(self): + candidate = report() + candidate_policy = policy() + candidate_policy["profiles"]["f64"].pop("minimum_speedup") + _set_latency(candidate["results"][0], "radix2", 30_000.0) + result = evaluate(candidate, candidate_policy) + self.assertTrue(result.passed) + + def test_excess_numeric_delta_fails(self): + candidate = report() + candidate["results"][1]["radix2_max_abs_vs_direct"] = 2e-4 + result = evaluate(candidate, policy()) + self.assertFalse(result.passed) + self.assertIn("exceeds", "\n".join(result.failures)) + + def test_missing_regime_fails(self): + candidate = report() + candidate["results"].pop() + result = evaluate(candidate, policy()) + self.assertFalse(result.passed) + self.assertIn("missing result for f32 dimension 256", result.failures) + + def test_missing_baseline_regime_fails_the_rendered_row(self): + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") + baseline["results"].pop(0) + result = evaluate(candidate, policy(), baseline) + self.assertFalse(result.passed) + self.assertIn("missing baseline result for f64 dimension 256", result.failures) + f64 = next(item for item in result.measurements if item.profile == "f64") + self.assertFalse(f64.passed) + + def test_sequential_reports_cannot_claim_a_baseline_comparison(self): + with self.assertRaisesRegex(GateInputError, "comparison_mode"): + evaluate(report(), policy(), report()) + + def test_paired_report_cannot_claim_bootstrap_mode(self): + with self.assertRaisesRegex(GateInputError, "comparison_mode"): + evaluate(report("paired-interleaved"), policy()) + + def test_duplicate_regime_is_rejected(self): + candidate = report() + candidate["results"].append(copy.deepcopy(candidate["results"][0])) + with self.assertRaisesRegex(GateInputError, "duplicate"): + evaluate(candidate, policy()) + + def test_nonfinite_and_inconsistent_values_are_rejected(self): + for field, value, message in ( + ("direct_ns", math.inf, "finite"), + ("radix2_ns", 0.0, "positive"), + ("radix2_speedup", math.nan, "finite"), + ): + with self.subTest(field=field): + candidate = report() + candidate["results"][0][field] = value + with self.assertRaisesRegex(GateInputError, message): + evaluate(candidate, policy()) + candidate = report() + candidate["results"][0]["direct_ns"] = 99.0 + with self.assertRaisesRegex(GateInputError, "elapsed samples"): + evaluate(candidate, policy()) + + def test_metadata_change_is_rejected(self): + candidate = report() + candidate["policy_effect"] = "AUTO changed" + with self.assertRaisesRegex(GateInputError, "AUTO policy"): + evaluate(candidate, policy()) + + def test_measurement_layer_mode_and_scope_are_required(self): + for field in ("measurement_layer", "measurement_mode", "metrics_scope"): + with self.subTest(field=field): + candidate = report() + candidate["benchmark"][field] = "adapter-wrapper" + with self.assertRaisesRegex(GateInputError, field): + evaluate(candidate, policy()) + + def test_actual_auto_backend_change_is_rejected(self): + candidate = report() + candidate["results"][0]["auto_backend"] = 2 + with self.assertRaisesRegex(GateInputError, "AUTO dispatch changed"): + evaluate(candidate, policy()) + + def test_cli_failure_is_nonzero_and_publishes_summary(self): + candidate = report() + _set_latency(candidate["results"][0], "radix2", 30_000.0) + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + report_path = root / "report.json" + policy_path = root / "policy.json" + summary_path = root / "summary.md" + report_path.write_text(json.dumps(candidate), encoding="utf-8") + policy_path.write_text(json.dumps(policy()), encoding="utf-8") + stdout = io.StringIO() + stderr = io.StringIO() + with redirect_stdout(stdout), redirect_stderr(stderr): + status = main( + [ + "--report", + str(report_path), + "--policy", + str(policy_path), + "--summary", + str(summary_path), + ] + ) + self.assertEqual(status, 1) + self.assertIn("Gate: FAIL", summary_path.read_text(encoding="utf-8")) + self.assertIn("below 2.00x", stderr.getvalue()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_liblecore_reference.py b/tests/test_liblecore_reference.py new file mode 100644 index 0000000..ee2314b --- /dev/null +++ b/tests/test_liblecore_reference.py @@ -0,0 +1,43 @@ +"""Definitional utility checks used by the native liblecore conformance harness.""" + +import numpy as np + +from holographic.misc.holographic_reference import ( + ref_cleanup, + ref_cosine_many, + ref_cosine_many_f64_f32, + ref_dot, + ref_dot_many, + ref_normalize, +) + + +def test_reference_normalize_and_ordered_dot_edges(): + zero = np.zeros(4, dtype=np.float64) + assert np.array_equal(ref_normalize(zero), zero) + assert np.allclose(ref_normalize([3.0, 4.0]), [0.6, 0.8]) + + # This cancellation pins the documented ascending component order. + assert ref_dot([1e16, 1.0, -1e16], [1.0, 1.0, 1.0]) == 0.0 + assert np.array_equal(ref_dot_many([1.0, 2.0], [[3.0, 4.0], [5.0, 6.0]]), [11.0, 17.0]) + + +def test_reference_cleanup_pins_ties_nan_and_zero_precedence(): + codebook = np.asarray([[1.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) + assert ref_cleanup([1.0, 0.0], codebook) == (0, 1.0) + + nan_index, nan_score = ref_cleanup([np.nan, 1.0], codebook) + assert nan_index == 0 + assert np.isnan(nan_score) + + # ref_cosine checks the zero norm before evaluating the dot, even when the other row contains NaN. + scores = ref_cosine_many([0.0, 0.0], [[np.nan, 1.0]]) + assert np.array_equal(scores, [0.0]) + + +def test_reference_mixed_f64_f32_cosine_is_explicit(): + query = np.asarray([1.0, 2.0], dtype=np.float64) + corpus = np.asarray([[1.0, 0.0], [0.0, 2.0], [0.0, 0.0]], dtype=np.float32) + scores = ref_cosine_many_f64_f32(query, corpus) + assert scores.dtype == np.float64 + assert np.allclose(scores, [1.0 / np.sqrt(5.0), 2.0 / np.sqrt(5.0), 0.0]) diff --git a/tools/generate_liblecore_amalgamation.py b/tools/generate_liblecore_amalgamation.py new file mode 100644 index 0000000..2254fdb --- /dev/null +++ b/tools/generate_liblecore_amalgamation.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python3 +"""Generate liblecore's reproducible single-header/single-source distribution.""" + +from __future__ import annotations + +import argparse +import difflib +import hashlib +import os +from pathlib import Path +import re +import sys +import tempfile + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +NATIVE_ROOT = Path("native/liblecore") +OUTPUT_HEADER = NATIVE_ROOT / "amalgamation/lecore.h" +OUTPUT_SOURCE = NATIVE_ROOT / "amalgamation/lecore.c" + +PUBLIC_CORE_HEADER = NATIVE_ROOT / "include/lecore/lecore.h" +PUBLIC_FORMAT_HEADER = NATIVE_ROOT / "include/lecore/lecore_format.h" +CORE_INTERNAL_HEADER = NATIVE_ROOT / "src/internal/lecore_internal.h" +FORMAT_INTERNAL_HEADER = NATIVE_ROOT / "src/internal/format_internal.h" + +CORE_SOURCES = ( + NATIVE_ROOT / "src/context.c", + NATIVE_ROOT / "src/status.c", + NATIVE_ROOT / "src/vector_f32.c", + NATIVE_ROOT / "src/vector_f64.c", + NATIVE_ROOT / "src/cleanup_f32.c", + NATIVE_ROOT / "src/cleanup_f64.c", + NATIVE_ROOT / "src/hrr_direct_f32.c", + NATIVE_ROOT / "src/hrr_direct_f64.c", + NATIVE_ROOT / "src/hrr_radix2_f32.c", + NATIVE_ROOT / "src/hrr_radix2_f64.c", +) +FORMAT_SOURCES = ( + NATIVE_ROOT / "src/crc64.c", + NATIVE_ROOT / "src/format.c", +) +METADATA_INPUTS = ( + Path("LICENSE"), + NATIVE_ROOT / "VERSION", + NATIVE_ROOT / "ISA_VERSION", + NATIVE_ROOT / "PROVENANCE.md", +) +CANONICAL_INPUTS = ( + *METADATA_INPUTS, + PUBLIC_CORE_HEADER, + PUBLIC_FORMAT_HEADER, + CORE_INTERNAL_HEADER, + FORMAT_INTERNAL_HEADER, + *CORE_SOURCES, + *FORMAT_SOURCES, +) + +LOCAL_INCLUDE_TARGETS = { + "lecore/lecore.h", + "lecore/lecore_format.h", + "internal/lecore_internal.h", + "internal/format_internal.h", +} +INCLUDE_RE = re.compile( + r"^\s*#\s*include\s*([<\"])([^>\"]+)[>\"]\s*(?://.*)?$" +) + + +def normalized_text(path: Path) -> str: + """Read UTF-8 source with platform-independent newlines.""" + + text = (REPOSITORY_ROOT / path).read_text(encoding="utf-8") + return text.replace("\r\n", "\n").replace("\r", "\n") + + +def canonical_digest() -> str: + digest = hashlib.sha256() + for path in CANONICAL_INPUTS: + digest.update(path.as_posix().encode("utf-8")) + digest.update(b"\0") + digest.update(normalized_text(path).encode("utf-8")) + digest.update(b"\0") + return digest.hexdigest() + + +def strip_local_includes(path: Path) -> str: + """Remove only known includes whose content is embedded in the artifact.""" + + output: list[str] = [] + for line_number, line in enumerate(normalized_text(path).splitlines(), 1): + match = INCLUDE_RE.match(line) + if match is None: + output.append(line) + continue + + delimiter, target = match.groups() + if target in LOCAL_INCLUDE_TARGETS: + # Preserve the original line number for the surrounding #line map. + output.append("") + continue + if delimiter == '"' or target.startswith("lecore/"): + raise RuntimeError( + f"unexpected local include {target!r} in {path}:{line_number}; " + "teach the generator how to embed it before regenerating" + ) + output.append(line) + return "\n".join(output).rstrip() + "\n" + + +def license_notice(version: str, isa_version: str, digest: str) -> str: + listed_inputs = "\n".join( + f" * - {path.as_posix()}" for path in CANONICAL_INPUTS + ) + license_lines = "\n".join( + f" * {line}" if line else " *" + for line in normalized_text(Path("LICENSE")).strip().splitlines() + ) + return f"""/* + * SPDX-License-Identifier: MIT + * + * liblecore {version} amalgamation (ABI 0, ISA {isa_version}) + * Generated by tools/generate_liblecore_amalgamation.py; DO NOT EDIT. + * Canonical-source SHA-256: {digest} + * + * This is a mechanical distribution of the clean-room canonical sources. + * Provenance details live in native/liblecore/PROVENANCE.md. + * Generated from: +{listed_inputs} + * +{license_lines} + */ +""" + + +def fragment(path: Path) -> str: + return ( + f'\n#line 1 "{path.as_posix()}"\n' + f"{strip_local_includes(path)}" + ) + + +def render_header(version: str, isa_version: str, digest: str) -> str: + notice = license_notice(version, isa_version, digest) + return ( + notice + + """ +#ifndef LECORE_AMALGAMATION_H +#define LECORE_AMALGAMATION_H + +#define LECORE_AMALGAMATION 1 +""" + + f'#define LECORE_AMALGAMATION_SOURCE_SHA256 "{digest}"\n' + + f""" + +#ifndef LECORE_VERSION_STRING_VALUE +# define LECORE_VERSION_STRING_VALUE "{version}" +#endif +#ifndef LECORE_ISA_VERSION_VALUE +# define LECORE_ISA_VERSION_VALUE {isa_version} +#endif +#ifndef LECORE_ABI_VERSION_VALUE +# define LECORE_ABI_VERSION_VALUE 0 +#endif +#ifndef LECORE_ENABLE_FORMAT +# define LECORE_ENABLE_FORMAT 1 +#endif +#ifndef LECORE_ENABLE_RADIX2 +# define LECORE_ENABLE_RADIX2 1 +#endif +""" + + fragment(PUBLIC_CORE_HEADER) + + "\n#if LECORE_ENABLE_FORMAT\n" + + fragment(PUBLIC_FORMAT_HEADER) + + "#endif /* LECORE_ENABLE_FORMAT */\n\n" + + "#endif /* LECORE_AMALGAMATION_H */\n" + ) + + +def render_source(version: str, isa_version: str, digest: str) -> str: + output = [ + license_notice(version, isa_version, digest), + """ +#ifndef LECORE_BUILDING_LIBRARY +# define LECORE_BUILDING_LIBRARY 1 +#endif +#include "lecore.h" +""", + fragment(CORE_INTERNAL_HEADER), + "\n#if LECORE_ENABLE_FORMAT\n", + fragment(FORMAT_INTERNAL_HEADER), + "#endif /* LECORE_ENABLE_FORMAT */\n", + ] + output.extend(fragment(path) for path in CORE_SOURCES) + output.append("\n#if LECORE_ENABLE_FORMAT\n") + output.extend(fragment(path) for path in FORMAT_SOURCES) + output.append("#endif /* LECORE_ENABLE_FORMAT */\n") + return "".join(output) + + +def atomic_write_if_changed(path: Path, content: str) -> bool: + destination = REPOSITORY_ROOT / path + if destination.exists() and destination.read_text(encoding="utf-8") == content: + destination.chmod(0o644) + return False + + destination.parent.mkdir(parents=True, exist_ok=True) + temporary_name: str | None = None + try: + with tempfile.NamedTemporaryFile( + mode="w", + encoding="utf-8", + newline="\n", + dir=destination.parent, + prefix=f".{destination.name}.", + delete=False, + ) as temporary: + temporary.write(content) + temporary_name = temporary.name + os.replace(temporary_name, destination) + destination.chmod(0o644) + finally: + if temporary_name is not None and os.path.exists(temporary_name): + os.unlink(temporary_name) + return True + + +def check_output(path: Path, expected: str) -> bool: + destination = REPOSITORY_ROOT / path + if not destination.exists(): + print(f"missing generated artifact: {path}", file=sys.stderr) + return False + + actual = destination.read_text(encoding="utf-8") + mode_matches = os.name != "posix" or destination.stat().st_mode & 0o777 == 0o644 + if actual == expected and mode_matches: + return True + + if not mode_matches: + actual_mode = destination.stat().st_mode & 0o777 + print( + f"generated artifact has mode {actual_mode:04o}, expected 0644: {path}", + file=sys.stderr, + ) + if actual == expected: + return False + + print(f"generated artifact is stale: {path}", file=sys.stderr) + difference = difflib.unified_diff( + actual.splitlines(), + expected.splitlines(), + fromfile=f"{path} (checked in)", + tofile=f"{path} (regenerated)", + lineterm="", + ) + for line_number, line in enumerate(difference): + if line_number == 200: + print("... diff truncated ...", file=sys.stderr) + break + print(line, file=sys.stderr) + return False + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--check", + action="store_true", + help="fail if checked-in amalgamation files differ from canonical inputs", + ) + arguments = parser.parse_args(argv) + + version = normalized_text(NATIVE_ROOT / "VERSION").strip() + isa_version = normalized_text(NATIVE_ROOT / "ISA_VERSION").strip() + if re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", version) is None: + raise RuntimeError(f"invalid native version: {version!r}") + if not isa_version.isdecimal(): + raise RuntimeError(f"invalid ISA version: {isa_version!r}") + + digest = canonical_digest() + expected = { + OUTPUT_HEADER: render_header(version, isa_version, digest), + OUTPUT_SOURCE: render_source(version, isa_version, digest), + } + + if arguments.check: + return 0 if all(check_output(path, content) for path, content in expected.items()) else 1 + + for path, content in expected.items(): + changed = atomic_write_if_changed(path, content) + print(f"{'generated' if changed else 'unchanged'} {path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/generate_liblecore_fixtures.py b/tools/generate_liblecore_fixtures.py new file mode 100644 index 0000000..f664966 --- /dev/null +++ b/tools/generate_liblecore_fixtures.py @@ -0,0 +1,423 @@ +#!/usr/bin/env python3 +"""Generate the committed liblecore ISA-1 differential fixture corpus. + +The corpus uses no random-number generator. Every finite input comes from the +integer/dyadic ``index-formula-v1`` below, and the JSON records the resulting +IEEE-754 bits explicitly. This keeps fixture meaning stable across language +runtimes and avoids making a particular RNG implementation part of the ISA. +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import sys +import numpy as np + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(REPOSITORY_ROOT)) + +from holographic.misc.holographic_reference import ( # noqa: E402 + ref_bind, + ref_bundle, + ref_cleanup, + ref_cosine, + ref_cosine_many, + ref_cosine_many_f64_f32, + ref_dot, + ref_dot_many, + ref_involution, + ref_normalize, + ref_permute, + ref_unbind, +) + + +DEFAULT_OUTPUT = REPOSITORY_ROOT / "tests/native/fixtures/liblecore_isa_v1.json" +FULL_DIMENSIONS = (1, 2, 3, 7, 8, 64) +REDUCTION_DIMENSIONS = (384, 1024, 4096) +EDGE_DIMENSIONS = (3, 8) + + +def _sequence(dimension: int, lane: int, dtype: np.dtype) -> np.ndarray: + """Bounded, nonzero dyadic data defined only by integer arithmetic.""" + values = [] + for index in range(dimension): + numerator = ( + ((index + 1) * (17 * lane + 11) + 13 * lane * lane + 5 * index * index) + % 193 + ) - 96 + if numerator == 0: + numerator = lane + 1 + sign = -1 if (index + lane) % 5 == 0 else 1 + values.append(sign * numerator / 64.0) + return np.asarray(values, dtype=dtype) + + +def _encode(values: object, dtype: np.dtype) -> dict: + dtype = np.dtype(dtype) + array = np.asarray(values, dtype=dtype) + if dtype == np.dtype(np.float64): + unsigned = array.reshape(-1).view(np.uint64) + width = 16 + elif dtype == np.dtype(np.float32): + unsigned = array.reshape(-1).view(np.uint32) + width = 8 + else: + raise TypeError(f"unsupported fixture dtype {dtype}") + return { + "dtype": dtype.name, + "shape": list(array.shape), + "bits": [format(int(value), f"0{width}x") for value in unsigned], + } + + +def _ordered_dot(a: np.ndarray, b: np.ndarray, dtype: np.dtype): + scalar = np.dtype(dtype).type + result = scalar(0.0) + for index in range(len(a)): + result = scalar(result + scalar(a[index] * b[index])) + return result + + +def _normalize(values: np.ndarray, dtype: np.dtype) -> np.ndarray: + scalar = np.dtype(dtype).type + norm_squared = _ordered_dot(values, values, dtype) + norm = scalar(np.sqrt(norm_squared)) + if norm > scalar(0.0): + return np.asarray([scalar(value / norm) for value in values], dtype=dtype) + return values.copy() + + +def _bind(a: np.ndarray, b: np.ndarray, dtype: np.dtype) -> np.ndarray: + scalar = np.dtype(dtype).type + dimension = len(a) + output = np.empty(dimension, dtype=dtype) + for output_index in range(dimension): + total = scalar(0.0) + for left_index in range(dimension): + right_index = (output_index - left_index) % dimension + total = scalar(total + scalar(a[left_index] * b[right_index])) + output[output_index] = total + return output + + +def _involution(values: np.ndarray) -> np.ndarray: + if len(values) == 1: + return values.copy() + return np.concatenate((values[:1], values[:0:-1])) + + +def _unbind(composite: np.ndarray, key: np.ndarray, dtype: np.dtype) -> np.ndarray: + return _bind(composite, _involution(key), dtype) + + +def _cosine(a: np.ndarray, b: np.ndarray, dtype: np.dtype): + scalar = np.dtype(dtype).type + norm_a_squared = _ordered_dot(a, a, dtype) + norm_b_squared = _ordered_dot(b, b, dtype) + if norm_a_squared == scalar(0.0) or norm_b_squared == scalar(0.0): + return scalar(0.0) + norm_a = scalar(np.sqrt(norm_a_squared)) + norm_b = scalar(np.sqrt(norm_b_squared)) + return scalar(_ordered_dot(a, b, dtype) / scalar(norm_a * norm_b)) + + +def _bundle(rows: np.ndarray, dtype: np.dtype) -> np.ndarray: + scalar = np.dtype(dtype).type + total = np.zeros(rows.shape[1], dtype=dtype) + for row in rows: + for index in range(rows.shape[1]): + total[index] = scalar(total[index] + row[index]) + return _normalize(total, dtype) + + +def _f64_references( + a: np.ndarray, + b: np.ndarray, + rows: np.ndarray, + shift: int, +) -> dict: + cleanup_index, cleanup_score = ref_cleanup(a, rows) + return { + "normalize": _encode(ref_normalize(a), np.float64), + "dot": _encode(ref_dot(a, b), np.float64), + "dot_many": _encode(ref_dot_many(a, rows), np.float64), + "cosine": _encode(ref_cosine(a, b), np.float64), + "cosine_many": _encode(ref_cosine_many(a, rows), np.float64), + "bind": _encode(ref_bind(a, b), np.float64), + "unbind": _encode(ref_unbind(a, b), np.float64), + "involution": _encode(ref_involution(a), np.float64), + "permute": _encode(ref_permute(a, shift), np.float64), + "bundle": _encode(ref_bundle(rows), np.float64), + "cleanup": { + "index": cleanup_index, + "score": _encode(cleanup_score, np.float64), + }, + } + + +def _f32_references( + a: np.ndarray, + b: np.ndarray, + rows: np.ndarray, + shift: int, +) -> dict: + scores = np.asarray([_cosine(a, row, np.float32) for row in rows], dtype=np.float32) + cleanup_index = int(np.argmax(scores)) + return { + "normalize": _encode(_normalize(a, np.float32), np.float32), + "dot": _encode(_ordered_dot(a, b, np.float32), np.float32), + "dot_many": _encode( + [_ordered_dot(a, row, np.float32) for row in rows], np.float32 + ), + "cosine": _encode(_cosine(a, b, np.float32), np.float32), + "cosine_many": _encode(scores, np.float32), + "bind": _encode(_bind(a, b, np.float32), np.float32), + "unbind": _encode(_unbind(a, b, np.float32), np.float32), + "involution": _encode(_involution(a), np.float32), + "permute": _encode(np.roll(a, shift), np.float32), + "bundle": _encode(_bundle(rows, np.float32), np.float32), + "cleanup": { + "index": cleanup_index, + "score": _encode(scores[cleanup_index], np.float32), + }, + } + + +def _finite_case(dimension: int, dtype: np.dtype) -> dict: + dtype = np.dtype(dtype) + a = _sequence(dimension, 1, dtype) + b = _sequence(dimension, 2, dtype) + rows = np.stack( + (b, _sequence(dimension, 3, dtype), _sequence(dimension, 4, dtype)) + ) + paired_left = np.stack((a, b, rows[1])) + paired_right = np.stack((b, rows[1], rows[2])) + shift = -(dimension + 2) + if dtype == np.dtype(np.float64): + expected = _f64_references(a, b, rows, shift) + bind = ref_bind + unbind = ref_unbind + else: + expected = _f32_references(a, b, rows, shift) + bind = lambda left, right: _bind(left, right, np.float32) + unbind = lambda trace, key: _unbind(trace, key, np.float32) + + expected["bind_batch"] = _encode( + [bind(left, right) for left, right in zip(paired_left, paired_right)], dtype + ) + expected["bind_fixed"] = _encode([bind(a, row) for row in rows], dtype) + expected["unbind_all"] = _encode([unbind(a, key) for key in rows], dtype) + + result = { + "name": f"formula-d{dimension}", + "dimension": dimension, + "shift": shift, + "inputs": { + "a": _encode(a, dtype), + "b": _encode(b, dtype), + "rows": _encode(rows, dtype), + "paired_left": _encode(paired_left, dtype), + "paired_right": _encode(paired_right, dtype), + }, + "expected": expected, + } + if dtype == np.dtype(np.float32): + mixed_query = np.asarray(a, dtype=np.float64) + np.float64(1.0 / 128.0) + result["inputs"]["mixed_query"] = _encode(mixed_query, np.float64) + result["expected"]["cosine_many_f64_f32"] = _encode( + ref_cosine_many_f64_f32(mixed_query, rows), np.float64 + ) + return result + + +def _reduction_case(dimension: int, dtype: np.dtype) -> dict: + dtype = np.dtype(dtype) + query = _sequence(dimension, 5, dtype) + rows = np.stack((_sequence(dimension, 6, dtype), _sequence(dimension, 7, dtype))) + if dtype == np.dtype(np.float64): + dots = ref_dot_many(query, rows) + cosines = ref_cosine_many(query, rows) + else: + dots = np.asarray( + [_ordered_dot(query, row, np.float32) for row in rows], dtype=np.float32 + ) + cosines = np.asarray( + [_cosine(query, row, np.float32) for row in rows], dtype=np.float32 + ) + return { + "name": f"ordered-reduction-d{dimension}", + "dimension": dimension, + "inputs": {"query": _encode(query, dtype), "rows": _encode(rows, dtype)}, + "expected": { + "dot_many": _encode(dots, dtype), + "cosine_many": _encode(cosines, dtype), + }, + } + + +def _edge_case(dimension: int, dtype: np.dtype) -> dict: + dtype = np.dtype(dtype) + finite = _sequence(dimension, 9, dtype) + zero = np.zeros(dimension, dtype=dtype) + nan_vector = finite.copy() + nan_vector[0] = np.nan + inf_vector = finite.copy() + inf_vector[0] = np.inf + impulse = np.zeros(dimension, dtype=dtype) + impulse[0] = 1.0 + shifted_impulse = np.zeros(dimension, dtype=dtype) + shifted_impulse[1] = 1.0 + ties = np.stack((finite, finite, -finite)) + first_nan_candidates = np.stack((finite, nan_vector, finite)) + reindex_special = finite.copy() + if dtype == np.dtype(np.float64): + reindex_bits = reindex_special.view(np.uint64) + reindex_bits[0] = np.uint64(0x7FF8000000000042) + if dimension > 1: + reindex_bits[1] = np.uint64(0x8000000000000000) + if dimension > 2: + reindex_bits[2] = np.uint64(0x7FF0000000000000) + if dimension > 3: + reindex_bits[3] = np.uint64(0xFFF0000000000000) + else: + reindex_bits = reindex_special.view(np.uint32) + reindex_bits[0] = np.uint32(0x7FC00042) + if dimension > 1: + reindex_bits[1] = np.uint32(0x80000000) + if dimension > 2: + reindex_bits[2] = np.uint32(0x7F800000) + if dimension > 3: + reindex_bits[3] = np.uint32(0xFF800000) + return { + "dimension": dimension, + "inputs": { + "finite": _encode(finite, dtype), + "zero": _encode(zero, dtype), + "nan_vector": _encode(nan_vector, dtype), + "inf_vector": _encode(inf_vector, dtype), + "impulse": _encode(impulse, dtype), + "shifted_impulse": _encode(shifted_impulse, dtype), + "ties": _encode(ties, dtype), + "first_nan_candidates": _encode(first_nan_candidates, dtype), + "zero_sum_rows": _encode(np.stack((finite, -finite)), dtype), + "reindex_special": _encode(reindex_special, dtype), + }, + "expected": { + "impulse_bind": _encode(finite, dtype), + "shifted_impulse_bind": _encode(np.roll(finite, 1), dtype), + "zero_normalize": _encode(zero, dtype), + "zero_sum_bundle": _encode(zero, dtype), + "zero_nan_cosine": _encode(np.asarray(0.0, dtype=dtype), dtype), + "tie_cleanup_index": 0, + "first_nan_cleanup_index": 1, + "first_nan_cleanup_score_class": "nan", + "raw_nan_result_class": "nan", + "finite_validation_status": "LECORE_ENONFINITE", + "special_involution": _encode(_involution(reindex_special), dtype), + "special_permute_minus_two": _encode( + np.roll(reindex_special, -2), dtype + ), + }, + } + + +def build_fixture() -> dict: + profiles = {} + profile_specs = ( + ( + "HRR_F64_V1", + np.dtype(np.float64), + "0x00010001", + {"atol": 1e-9, "rtol": 0.0, "status": "frozen"}, + ), + ( + "HRR_F32_V1", + np.dtype(np.float32), + "0x00010002", + {"atol": 1e-5, "rtol": 0.0, "status": "preview-hypothesis"}, + ), + ) + for name, dtype, profile_id, tolerance in profile_specs: + profiles[name] = { + "profile_id": profile_id, + "dtype": dtype.name, + "tolerance": tolerance, + "input_domain": { + "finite_formula_range": [-1.5, 1.5], + "rounding": "round-to-nearest", + "subnormal_inputs": "excluded", + "nonzero_subnormal_outputs": "excluded", + }, + "finite_cases": [_finite_case(dimension, dtype) for dimension in FULL_DIMENSIONS], + "reduction_cases": [ + _reduction_case(dimension, dtype) for dimension in REDUCTION_DIMENSIONS + ], + "edge_cases": [_edge_case(dimension, dtype) for dimension in EDGE_DIMENSIONS], + } + + return { + "schema": "org.lecore.conformance-fixture.v1", + "abi_preview": 0, + "isa_version": 1, + "generator": { + "owner": "leCore", + "path": "tools/generate_liblecore_fixtures.py", + "algorithm": "index-formula-v1/no-rng", + "formula": "sign(i,lane)*((((i+1)*(17*lane+11)+13*lane^2+5*i^2) mod 193)-96)/64", + "sign": "-1 when (i+lane) mod 5 is 0; +1 otherwise", + "zero_numerator_rule": "replace an exact zero numerator with lane+1", + }, + "encoding": { + "float_bits": "fixed-width lowercase IEEE-754 hexadecimal in logical scalar order", + "shape": "row-major C order", + "byte_order": "bit-pattern integers; independent of host byte order", + }, + "dimensions": { + "full_operation_cases": list(FULL_DIMENSIONS), + "ordered_reduction_cases": list(REDUCTION_DIMENSIONS), + "edge_cases": list(EDGE_DIMENSIONS), + }, + "decision_rules": { + "cleanup": "first NaN; otherwise highest score; exact ties choose lowest index", + "reindex": "bit exact", + }, + "profiles": profiles, + } + + +def render_fixture() -> str: + return json.dumps(build_fixture(), indent=2, sort_keys=True, allow_nan=False) + "\n" + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT) + parser.add_argument( + "--check", + action="store_true", + help="fail instead of writing when the committed fixture differs", + ) + arguments = parser.parse_args() + rendered = render_fixture() + if arguments.check: + try: + existing = arguments.output.read_text(encoding="utf-8") + except FileNotFoundError: + raise SystemExit(f"fixture is missing: {arguments.output}") + if existing != rendered: + raise SystemExit( + f"fixture drift: run {Path(__file__).relative_to(REPOSITORY_ROOT)}" + ) + print(f"fixture is current: {arguments.output}") + return + arguments.output.parent.mkdir(parents=True, exist_ok=True) + arguments.output.write_text(rendered, encoding="utf-8") + print(f"wrote {arguments.output}") + + +if __name__ == "__main__": + main()