From 64897848715cc8c413a04d6cfd79654caf99f168 Mon Sep 17 00:00:00 2001 From: atimics Date: Sat, 8 Aug 2026 12:10:35 -0700 Subject: [PATCH 1/6] docs: define liblecore product and engineering plan --- ENG.md | 920 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ PRD.md | 446 ++++++++++++++++++++++++++ README.md | 2 + 3 files changed, 1368 insertions(+) create mode 100644 ENG.md create mode 100644 PRD.md diff --git a/ENG.md b/ENG.md new file mode 100644 index 0000000..251aea0 --- /dev/null +++ b/ENG.md @@ -0,0 +1,920 @@ +# liblecore — Engineering Plan + +*Status: proposed · Technical architecture, compatibility policy, delivery plan, 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. + +## 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, object, installed, vendored, 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 Proposed 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/ + cmake/lecoreConfig.cmake.in + pkgconfig/liblecore.pc.in + amalgamation/ +bindings/ + python/lecore_native.py + rust/lecore-sys/ +tests/native/ + fixtures/ + 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. CI regenerates them and fails on drift. + +## 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. +- ABI-breaking changes require a new ABI major and 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`, and IEC 60559 behavior (or +a configure-time probe proving equivalent NaN/Inf and evaluation semantics). Unsupported representations fail the +build or omit the relevant capability; they never masquerade as `HRR_F32_V1`/`HRR_F64_V1`. The v1 numeric domain +excludes subnormal inputs and cases whose required reference output is a nonzero subnormal. Builds report whether +they preserve or flush subnormals, and a future profile must add explicit fixtures before making them portable. + +## 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. + +- A context is single-thread-owned and must not be called concurrently. +- 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. + +Initial CMake options: + +```text +LECORE_BUILD_SHARED +LECORE_BUILD_TESTS +LECORE_BUILD_AMALGAMATION +LECORE_ENABLE_F32 +LECORE_ENABLE_F64 +LECORE_ENABLE_RADIX2 +LECORE_ENABLE_FORMAT +LECORE_ENABLE_SANITIZERS +``` + +Options may remove capabilities from a build but never change an enabled profile's semantics. Capability queries +report what is present. + +### 11.2 Amalgamation + +The amalgamation is generated from canonical sources, carries license/version data, and exposes the same ABI. CI +regenerates it and fails 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 with explicit release notes; ABI `1` begins only at `LC-037`. +- After ABI `1`, `LECORE_ABI_VERSION` changes only for ABI breaks. +- 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. +- Major releases may break ABI and change SONAME. +- Release tags use a distinct namespace such as `liblecore-v0.1.0`. + +The native release workflow triggers explicitly on `liblecore-v*`; it does 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 generated or hand-audited raw declarations plus a small safe ownership wrapper: + +- `Context` owns and destroys one C context; +- `Context` is not `Sync`; `Send` is granted only if ownership transfer is verified safe; +- 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 with a fixed export list. The WASM test loads fixture bytes, runs operations, +and writes a compact conformance report. 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 runs independently from the Python affected-test selector. + +| 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. +Performance timings from noisy hosted runners are recorded artifacts, not merge gates. Semantic and memory-safety +failures are gates. + +## 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. Until an item is accepted into an issue and +assigned, its state is `proposed`; execution status belongs in the owning repository's tracker. 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-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 | Work | Depends on | Acceptance | +|---|---:|---|---|---| +| LC-001 | P0 | Freeze v1 scope and non-goals | — | PRD/ENG review confirms raw HRR, profile naming, atom exclusion, policy exclusion, and consumer ownership. | +| LC-002 | P0 | 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 | 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 | 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 | 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 | Freeze native release/version policy | LC-001 | Native semver, ABI, ISA/profile/format versions, tags, checksums, and compatibility rules are accepted. | +| LC-007 | P0 | Freeze descriptor mechanics and reserve profile IDs | LC-002, LC-003 | C/Python encode the same 96-byte headers; f64 is registered and the f32 ID is reserved without claiming unfinished semantics. | +| LC-008 | P0 | 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 | Work | Depends on | Acceptance | +|---|---:|---|---|---| +| LC-010 | P0 | Create CMake/install/source skeleton | LC-002, LC-004 | Static/shared builds, hidden visibility, `lecore::lecore`, and external install test pass. | +| LC-011 | P0 | Implement context, allocator, status, and introspection | LC-010 | Two contexts execute independently; failures leak nothing; no stderr/global error state. | +| LC-012 | P0 | 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 | 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 | 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 | Add Python differential harness | LC-012–LC-014 | ctypes checks every operation and emits target/backend/profile/error/decision report. | +| LC-016 | P0 | Implement descriptor codec | LC-007, LC-010 | Golden bytes round-trip; truncated, corrupt, future-major, and incompatible records are rejected. | +| LC-017 | P0 | 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 | Add clean-room embedding examples | LC-017 | C and C++ examples build outside the source tree using installed and vendored paths. | +| LC-019 | P0 | 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 | Work | Depends on | Acceptance | +|---|---:|---|---|---| +| LC-020 | P0 | 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 | Implement f64 batch and fixed-role APIs | LC-014, LC-020 | Adopter-backed layouts, strides, aliases, and tails are tested; scalar/batch values and decisions conform. | +| LC-022 | P0 | Define and implement `HRR_F32_V1` | LC-014, LC-020, LC-021 | Complete immutable f32 profile record, domain, fixtures, and bounds freeze; exact reindexes and decision corpus agree. | +| LC-023 | P0 | 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 | 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 | Add Emscripten lane | LC-024 | Amalgamation builds and runs its fixture self-test without OS services or dynamic loading. | +| LC-026 | P0 | 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 | Add Python runtime wrapper | LC-015, LC-017 | Explicit artifact resolution, dtype/profile checks, exceptions, and canonical NumPy fallback are tested. | +| LC-028 | P0 | 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 | Add `lecore_cosine_many_f64_f32` | LC-005, LC-008, LC-014, LC-022 | Independent reference and NoSQLite fixtures match ordered f64 scores; host-owned ID tie ordering remains unchanged. | +| LC-038 | P0 | 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 | 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. | + +**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 | Work | Depends on | Acceptance | +|---|---:|---|---|---| +| LC-030 | P0 | 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 | 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 | 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 | 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 | Add Zero LM WASM adapter | LC-022, LC-025 | Native/WASM choices agree with legacy and recorded size budget passes. | +| LC-035 | P1 | 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 | Retire one duplicate generic implementation | LC-037 plus sustained adopter release | Removal follows replay parity and rollback window, not initial compilation. | +| LC-037 | P0 | 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 | Work | Depends on | Acceptance | +|---|---:|---|---|---| +| LC-040 | P2 | 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 | 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 | Evaluate platform FFT/SIMD backends | LC-037 | Named target workload wins; conformance and decision corpus pass; unsupported platforms retain baseline. | +| LC-043 | P2 | Evaluate exact-index profile | LC-032, LC-037 | NoSQLite or Zero proves reuse without changing result authority or persisted semantics. | +| LC-044 | P2 | Evaluate MAP profile | LC-037 | Separate ID/API and Holonet evidence; no function or payload can be mistaken for HRR. | +| LC-045 | P2 | Evaluate integer/freestanding profile | LC-037 | NSRL/asix supplies a real consumer, exact arithmetic contract, build target, and profile fixtures. | +| LC-046 | P2 | 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..2046e78 --- /dev/null +++ b/PRD.md @@ -0,0 +1,446 @@ +# liblecore — Product Requirements + +*Status: proposed · Product boundary 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 will live in this repository under an optional `native/` tree and retain leCore's MIT +license. Consumers will use pinned release artifacts rather than copying neighboring repositories or relying on a +machine-global development checkout. + +## 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 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 | Acceptance summary | +|---|---|---| +| PR-F01 | Implement the caller-supplied-vector ISA subset | 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 | Both profiles have typed APIs, published tolerances, and no implicit conversion. | +| PR-F03 | Preserve exact decisions | 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 | 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 | Allocation instrumentation reports zero allocations after context initialization. | +| PR-F06 | Expose batch operations | Batch results conform to scalar operations and define layouts, strides, tails, and allowed aliasing. | +| PR-F07 | Identify compatibility | Callers can query and compare ABI, ISA, profile, dtype, dimension, backend, and feature versions. | +| PR-F08 | Fail loudly without rewriting raw semantics | 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 | The same source builds and passes smoke tests on supported native and Emscripten targets. | +| PR-F10 | Keep policy outside the kernel | 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 + +### 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 | Outcome | +|---|---|---| +| LC-E0 Contract and governance | P0 | One implementable ABI, profile registry, and release/version policy. | +| LC-E1 Reference conformance | P0 | The f64 caller-supplied-vector subset and supporting utilities are correct before optimization. | +| LC-E2 Portable optimized core | P0 | f32/f64 FFT and batch profiles preserve decisions. | +| LC-E3 Distribution and CI | P0 | Reproducible C/C++/Rust/Python/WASM consumption. | +| LC-E4 Signal shadow migration | P0 | Highest-value existing HRR consumer validates the product. | +| LC-E5 Rust adoption | P0 | NoSQLite proves pinned Cargo consumption and exact host-owned ordering. | +| LC-E6 Interchange | P0 | The optional descriptor component has immutable, portable bytes and fail-closed compatibility checks. | +| LC-E7 Memory and world integrations | P1 | Policy-neutral trace helpers, CosyWorld, Zero LM, and dependency cleanup are earned follow-ons. | +| LC-E8 Additional profiles | P2 | 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. Decisions to resolve before implementation + +| Decision | Recommended starting point | +|---|---| +| Canonical location | `native/` in leCore; release a small standalone artifact from that source. | +| Library and symbols | Artifact `liblecore`; public symbols prefixed `lecore_`. | +| Versioning | Independent native package semver plus ABI, ISA, profile, generator, and format versions. | +| Allocation | Optional allocator callbacks at context creation; no allocations after creation. | +| Threading | A context is single-thread-owned; separate contexts may run concurrently with no global mutable state. | +| 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..9a88891 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,8 @@ Like leOS, leCore is **free and open source**, and the work that keeps it free i ## Learning more +- **[`PRD.md`](PRD.md) and [`ENG.md`](ENG.md)** — the product vision, native ABI strategy, adoption gates, and + dependency-ordered backlog for the proposed `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 From 567541c1a0d89b5545323206f31fa828c34c33d3 Mon Sep 17 00:00:00 2001 From: atimics Date: Sat, 8 Aug 2026 13:55:38 -0700 Subject: [PATCH 2/6] feat: add portable liblecore ABI-0 kernel --- .github/workflows/native.yml | 363 + .gitignore | 2 + ENG.md | 214 +- PRD.md | 89 +- README.md | 4 +- benchmarks/bench_liblecore.py | 202 + bindings/python/lecore_native.py | 1020 + bindings/rust/lecore-sys/.gitignore | 1 + bindings/rust/lecore-sys/Cargo.lock | 32 + bindings/rust/lecore-sys/Cargo.toml | 19 + bindings/rust/lecore-sys/README.md | 32 + bindings/rust/lecore-sys/build.rs | 188 + bindings/rust/lecore-sys/build/abi_check.c | 32 + bindings/rust/lecore-sys/build/fp_probe.c | 28 + bindings/rust/lecore-sys/src/lib.rs | 610 + bindings/rust/lecore-sys/src/raw.rs | 322 + bindings/rust/lecore-sys/tests/safe_api.rs | 199 + holographic/misc/holographic_reference.py | 95 +- native/liblecore/ABI0_SYMBOLS.txt | 51 + native/liblecore/BENCHMARKS.md | 61 + native/liblecore/CHANGELOG.md | 23 + native/liblecore/CMakeLists.txt | 400 + native/liblecore/ISA_VERSION | 1 + native/liblecore/PROVENANCE.md | 27 + native/liblecore/README.md | 161 + native/liblecore/VERSION | 1 + native/liblecore/amalgamation/lecore.c | 3717 ++ native/liblecore/amalgamation/lecore.h | 606 + native/liblecore/cmake/lecoreConfig.cmake.in | 5 + native/liblecore/examples/CMakeLists.txt | 18 + native/liblecore/examples/hrr_example.c | 36 + native/liblecore/examples/hrr_example.cpp | 46 + native/liblecore/include/lecore/lecore.h | 396 + .../liblecore/include/lecore/lecore_format.h | 125 + native/liblecore/pkgconfig/liblecore.pc.in | 12 + native/liblecore/src/cleanup_f32.c | 119 + native/liblecore/src/cleanup_f64.c | 121 + native/liblecore/src/context.c | 444 + native/liblecore/src/crc64.c | 26 + native/liblecore/src/format.c | 536 + native/liblecore/src/hrr_direct_f32.c | 399 + native/liblecore/src/hrr_direct_f64.c | 400 + native/liblecore/src/hrr_radix2_f32.c | 158 + native/liblecore/src/hrr_radix2_f64.c | 158 + .../liblecore/src/internal/format_internal.h | 20 + .../liblecore/src/internal/lecore_internal.h | 183 + native/liblecore/src/status.c | 46 + native/liblecore/src/vector_f32.c | 463 + native/liblecore/src/vector_f64.c | 552 + native/liblecore/tests/CMakeLists.txt | 138 + native/liblecore/tests/c/test_api.c | 448 + native/liblecore/tests/c/test_common.h | 61 + native/liblecore/tests/c/test_cpp_header.cpp | 29 + native/liblecore/tests/c/test_edges.c | 110 + native/liblecore/tests/c/test_format.c | 315 + native/liblecore/tests/c/test_numeric.c | 468 + .../consumers/cmake-consumer/CMakeLists.txt | 8 + .../tests/consumers/cmake-consumer/main.cpp | 42 + .../pkgconfig-consumer/CMakeLists.txt | 9 + .../tests/consumers/pkgconfig-consumer/main.c | 28 + .../tests/consumers/run_examples_only.cmake | 56 + .../consumers/run_format_off_variant.cmake | 98 + .../consumers/run_installed_consumer.cmake | 112 + native/liblecore/tests/fuzz/CMakeLists.txt | 20 + .../liblecore/tests/fuzz/corpus/format/seed | 1 + .../tests/fuzz/corpus/public_api/seed | 1 + native/liblecore/tests/fuzz/fuzz_format.c | 79 + native/liblecore/tests/fuzz/fuzz_public_api.c | 268 + tests/native/check_liblecore_amalgamation.py | 253 + tests/native/check_liblecore_symbols.py | 72 + tests/native/check_liblecore_wasm.py | 191 + tests/native/fixtures/liblecore_isa_v1.json | 41142 ++++++++++++++++ tests/native/test_liblecore_conformance.py | 869 + tests/test_liblecore_reference.py | 43 + tools/generate_liblecore_amalgamation.py | 295 + tools/generate_liblecore_fixtures.py | 423 + 76 files changed, 58216 insertions(+), 126 deletions(-) create mode 100644 .github/workflows/native.yml create mode 100644 benchmarks/bench_liblecore.py create mode 100644 bindings/python/lecore_native.py create mode 100644 bindings/rust/lecore-sys/.gitignore create mode 100644 bindings/rust/lecore-sys/Cargo.lock create mode 100644 bindings/rust/lecore-sys/Cargo.toml create mode 100644 bindings/rust/lecore-sys/README.md create mode 100644 bindings/rust/lecore-sys/build.rs create mode 100644 bindings/rust/lecore-sys/build/abi_check.c create mode 100644 bindings/rust/lecore-sys/build/fp_probe.c create mode 100644 bindings/rust/lecore-sys/src/lib.rs create mode 100644 bindings/rust/lecore-sys/src/raw.rs create mode 100644 bindings/rust/lecore-sys/tests/safe_api.rs create mode 100644 native/liblecore/ABI0_SYMBOLS.txt create mode 100644 native/liblecore/BENCHMARKS.md create mode 100644 native/liblecore/CHANGELOG.md create mode 100644 native/liblecore/CMakeLists.txt create mode 100644 native/liblecore/ISA_VERSION create mode 100644 native/liblecore/PROVENANCE.md create mode 100644 native/liblecore/README.md create mode 100644 native/liblecore/VERSION create mode 100644 native/liblecore/amalgamation/lecore.c create mode 100644 native/liblecore/amalgamation/lecore.h create mode 100644 native/liblecore/cmake/lecoreConfig.cmake.in create mode 100644 native/liblecore/examples/CMakeLists.txt create mode 100644 native/liblecore/examples/hrr_example.c create mode 100644 native/liblecore/examples/hrr_example.cpp create mode 100644 native/liblecore/include/lecore/lecore.h create mode 100644 native/liblecore/include/lecore/lecore_format.h create mode 100644 native/liblecore/pkgconfig/liblecore.pc.in create mode 100644 native/liblecore/src/cleanup_f32.c create mode 100644 native/liblecore/src/cleanup_f64.c create mode 100644 native/liblecore/src/context.c create mode 100644 native/liblecore/src/crc64.c create mode 100644 native/liblecore/src/format.c create mode 100644 native/liblecore/src/hrr_direct_f32.c create mode 100644 native/liblecore/src/hrr_direct_f64.c create mode 100644 native/liblecore/src/hrr_radix2_f32.c create mode 100644 native/liblecore/src/hrr_radix2_f64.c create mode 100644 native/liblecore/src/internal/format_internal.h create mode 100644 native/liblecore/src/internal/lecore_internal.h create mode 100644 native/liblecore/src/status.c create mode 100644 native/liblecore/src/vector_f32.c create mode 100644 native/liblecore/src/vector_f64.c create mode 100644 native/liblecore/tests/CMakeLists.txt create mode 100644 native/liblecore/tests/c/test_api.c create mode 100644 native/liblecore/tests/c/test_common.h create mode 100644 native/liblecore/tests/c/test_cpp_header.cpp create mode 100644 native/liblecore/tests/c/test_edges.c create mode 100644 native/liblecore/tests/c/test_format.c create mode 100644 native/liblecore/tests/c/test_numeric.c create mode 100644 native/liblecore/tests/consumers/cmake-consumer/CMakeLists.txt create mode 100644 native/liblecore/tests/consumers/cmake-consumer/main.cpp create mode 100644 native/liblecore/tests/consumers/pkgconfig-consumer/CMakeLists.txt create mode 100644 native/liblecore/tests/consumers/pkgconfig-consumer/main.c create mode 100644 native/liblecore/tests/consumers/run_examples_only.cmake create mode 100644 native/liblecore/tests/consumers/run_format_off_variant.cmake create mode 100644 native/liblecore/tests/consumers/run_installed_consumer.cmake create mode 100644 native/liblecore/tests/fuzz/CMakeLists.txt create mode 100644 native/liblecore/tests/fuzz/corpus/format/seed create mode 100644 native/liblecore/tests/fuzz/corpus/public_api/seed create mode 100644 native/liblecore/tests/fuzz/fuzz_format.c create mode 100644 native/liblecore/tests/fuzz/fuzz_public_api.c create mode 100644 tests/native/check_liblecore_amalgamation.py create mode 100644 tests/native/check_liblecore_symbols.py create mode 100644 tests/native/check_liblecore_wasm.py create mode 100644 tests/native/fixtures/liblecore_isa_v1.json create mode 100644 tests/native/test_liblecore_conformance.py create mode 100644 tests/test_liblecore_reference.py create mode 100644 tools/generate_liblecore_amalgamation.py create mode 100644 tools/generate_liblecore_fixtures.py diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml new file mode 100644 index 0000000..e442aa9 --- /dev/null +++ b/.github/workflows/native.yml @@ -0,0 +1,363 @@ +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 + + 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 index 251aea0..ee4d6d0 100644 --- a/ENG.md +++ b/ENG.md @@ -1,6 +1,7 @@ # liblecore — Engineering Plan -*Status: proposed · Technical architecture, compatibility policy, delivery plan, and dependency-ordered backlog.* +*Status: active · ABI-0 implementation preview, compatibility policy, validation record, and dependency-ordered +backlog.* --- @@ -23,6 +24,28 @@ Existing native implementations are behavioral oracles and workload sources. lib 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 benchmark driver and `native/liblecore/BENCHMARKS.md` with setup, scratch, wins, losses, and numeric error | One local Apple-arm64 baseline exists. Cross-platform and adopter workload evidence is still required 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; @@ -33,7 +56,7 @@ unclear license provenance is not copied into the canonical library without an e 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, object, installed, vendored, Cargo, and Emscripten forms. +- 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. @@ -94,7 +117,7 @@ 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 Proposed repository layout +### 4.2 Implemented repository layout ```text native/liblecore/ @@ -120,6 +143,7 @@ native/liblecore/ tests/ c/ consumers/ + fuzz/ cmake/lecoreConfig.cmake.in pkgconfig/liblecore.pc.in amalgamation/ @@ -128,6 +152,9 @@ bindings/ 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 @@ -136,7 +163,8 @@ tools/ generate_liblecore_amalgamation.py ``` -Generated amalgamation and fixture files are reproducible outputs. CI regenerates them and fails on drift. +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 @@ -372,11 +400,13 @@ including internal FFT and bundle reductions, remain microarchitecture and may v 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`, and IEC 60559 behavior (or -a configure-time probe proving equivalent NaN/Inf and evaluation semantics). Unsupported representations fail the -build or omit the relevant capability; they never masquerade as `HRR_F32_V1`/`HRR_F64_V1`. The v1 numeric domain -excludes subnormal inputs and cases whose required reference output is a nonzero subnormal. Builds report whether -they preserve or flush subnormals, and a future profile must add explicit fixtures before making them portable. +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 @@ -426,7 +456,8 @@ path that stays within numeric tolerance yet changes a required winner fails dec `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. -- A context is single-thread-owned and must not be called concurrently. +- 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. @@ -519,26 +550,30 @@ Required consumption modes: - generated `lecore.c` plus `lecore.h` for Make, Cargo, and Emscripten; and - direct static or shared linking from a release archive. -Initial CMake options: +Implemented CMake options: ```text LECORE_BUILD_SHARED LECORE_BUILD_TESTS +LECORE_BUILD_EXAMPLES LECORE_BUILD_AMALGAMATION -LECORE_ENABLE_F32 -LECORE_ENABLE_F64 +LECORE_BUILD_FUZZERS LECORE_ENABLE_RADIX2 LECORE_ENABLE_FORMAT LECORE_ENABLE_SANITIZERS +LECORE_WARNINGS_AS_ERRORS +LECORE_ASSUME_IEC_60559 ``` -Options may remove capabilities from a build but never change an enabled profile's semantics. Capability queries -report what is present. +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. CI -regenerates it and fails if the committed output differs. Hand editing an amalgamated file is unsupported. +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 @@ -551,8 +586,9 @@ regenerates it and fails if the committed output differs. Hand editing an amalga - Major releases may break ABI and change SONAME. - Release tags use a distinct namespace such as `liblecore-v0.1.0`. -The native release workflow triggers explicitly on `liblecore-v*`; it does not assume the repository's existing -general `v*` Python workflow will recognize or correctly package native tags. +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 @@ -585,10 +621,11 @@ native-dispatch policy can represent its setup cost and decision gate honestly. ### 12.2 Rust -`lecore-sys` contains generated or hand-audited raw declarations plus a small safe ownership wrapper: +`lecore-sys` contains hand-audited raw declarations plus a small safe ownership wrapper: - `Context` owns and destroys one C context; -- `Context` is not `Sync`; `Send` is granted only if ownership transfer is verified safe; +- 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. @@ -597,9 +634,11 @@ Application crates retain domain state and persistence. No Rust binding mirrors ### 12.3 WebAssembly -Emscripten compiles the amalgamation with a fixed export list. The WASM test loads fixture bytes, runs operations, -and writes a compact conformance report. The first milestone requires no threads, dynamic loading, filesystem, or -network APIs. +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 @@ -649,7 +688,8 @@ amalgamation, Cargo, and Emscripten paths. ### 13.3 CI matrix -A dedicated native workflow runs independently from the Python affected-test selector. +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 | |---|---|---| @@ -749,8 +789,18 @@ claims are not promoted merely because the dependency builds. ## 16. Detailed backlog -These are stable planning IDs, not a substitute for an issue tracker. Until an item is accepted into an issue and -assigned, its state is `proposed`; execution status belongs in the owning repository's tracker. Priorities mean: +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; @@ -772,53 +822,53 @@ assigned, its state is `proposed`; execution status belongs in the owning reposi ### 16.2 M0 — Contract and provenance -| ID | Pri | Work | Depends on | Acceptance | -|---|---:|---|---|---| -| LC-001 | P0 | Freeze v1 scope and non-goals | — | PRD/ENG review confirms raw HRR, profile naming, atom exclusion, policy exclusion, and consumer ownership. | -| LC-002 | P0 | 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 | 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 | 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 | 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 | Freeze native release/version policy | LC-001 | Native semver, ABI, ISA/profile/format versions, tags, checksums, and compatibility rules are accepted. | -| LC-007 | P0 | Freeze descriptor mechanics and reserve profile IDs | LC-002, LC-003 | C/Python encode the same 96-byte headers; f64 is registered and the f32 ID is reserved without claiming unfinished semantics. | -| LC-008 | P0 | 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. | +| 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 | Work | Depends on | Acceptance | -|---|---:|---|---|---| -| LC-010 | P0 | Create CMake/install/source skeleton | LC-002, LC-004 | Static/shared builds, hidden visibility, `lecore::lecore`, and external install test pass. | -| LC-011 | P0 | Implement context, allocator, status, and introspection | LC-010 | Two contexts execute independently; failures leak nothing; no stderr/global error state. | -| LC-012 | P0 | 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 | 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 | 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 | Add Python differential harness | LC-012–LC-014 | ctypes checks every operation and emits target/backend/profile/error/decision report. | -| LC-016 | P0 | Implement descriptor codec | LC-007, LC-010 | Golden bytes round-trip; truncated, corrupt, future-major, and incompatible records are rejected. | -| LC-017 | P0 | 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 | Add clean-room embedding examples | LC-017 | C and C++ examples build outside the source tree using installed and vendored paths. | -| LC-019 | P0 | Publish ABI-0 reference preview | LC-006, LC-017, LC-018 | Versioned archive, checksums, license, changelog, build descriptor, instability notice, and limitations are available. | +| 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 | Work | Depends on | Acceptance | -|---|---:|---|---|---| -| LC-020 | P0 | 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 | Implement f64 batch and fixed-role APIs | LC-014, LC-020 | Adopter-backed layouts, strides, aliases, and tails are tested; scalar/batch values and decisions conform. | -| LC-022 | P0 | Define and implement `HRR_F32_V1` | LC-014, LC-020, LC-021 | Complete immutable f32 profile record, domain, fixtures, and bounds freeze; exact reindexes and decision corpus agree. | -| LC-023 | P0 | 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 | 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 | Add Emscripten lane | LC-024 | Amalgamation builds and runs its fixture self-test without OS services or dynamic loading. | -| LC-026 | P0 | 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 | Add Python runtime wrapper | LC-015, LC-017 | Explicit artifact resolution, dtype/profile checks, exceptions, and canonical NumPy fallback are tested. | -| LC-028 | P0 | 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 | Add `lecore_cosine_many_f64_f32` | LC-005, LC-008, LC-014, LC-022 | Independent reference and NoSQLite fixtures match ordered f64 scores; host-owned ID tie ordering remains unchanged. | -| LC-038 | P0 | 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 | 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. | +| 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 · 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. | **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 @@ -826,31 +876,31 @@ beta blocker. ### 16.5 M3 — Consumer proving -| ID | Pri | Work | Depends on | Acceptance | -|---|---:|---|---|---| -| LC-030 | P0 | 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 | 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 | 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 | 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 | Add Zero LM WASM adapter | LC-022, LC-025 | Native/WASM choices agree with legacy and recorded size budget passes. | -| LC-035 | P1 | 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 | Retire one duplicate generic implementation | LC-037 plus sustained adopter release | Removal follows replay parity and rollback window, not initial compilation. | -| LC-037 | P0 | 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. | +| 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 | Work | Depends on | Acceptance | -|---|---:|---|---|---| -| LC-040 | P2 | 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 | 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 | Evaluate platform FFT/SIMD backends | LC-037 | Named target workload wins; conformance and decision corpus pass; unsupported platforms retain baseline. | -| LC-043 | P2 | Evaluate exact-index profile | LC-032, LC-037 | NoSQLite or Zero proves reuse without changing result authority or persisted semantics. | -| LC-044 | P2 | Evaluate MAP profile | LC-037 | Separate ID/API and Holonet evidence; no function or payload can be mistaken for HRR. | -| LC-045 | P2 | Evaluate integer/freestanding profile | LC-037 | NSRL/asix supplies a real consumer, exact arithmetic contract, build target, and profile fixtures. | -| LC-046 | P2 | Design generated-kernel plugin ABI | LC-037 | Versioned descriptor/IR, target/compiler cache identity, and trusted-input boundary remain separate from vector ABI. | +| 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. diff --git a/PRD.md b/PRD.md index 2046e78..8def07b 100644 --- a/PRD.md +++ b/PRD.md @@ -1,6 +1,7 @@ # liblecore — Product Requirements -*Status: proposed · Product boundary and rollout plan for a portable C implementation of leCore's frozen vector algebra.* +*Status: active · ABI-0 implementation preview and rollout plan for a portable C implementation of leCore's +frozen vector algebra.* --- @@ -19,9 +20,24 @@ The primary product value is **one interoperable algebra across projects**, not 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 will live in this repository under an optional `native/` tree and retain leCore's MIT -license. Consumers will use pinned release artifacts rather than copying neighboring repositories or relying on a -machine-global development checkout. +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 @@ -130,7 +146,7 @@ extension process that prevents experimental semantics from entering the base AB ## 6. Product scope -### 6.1 Version 1 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 @@ -234,18 +250,18 @@ The conformance kit is a product surface, not internal test debris. It includes: ## 8. Functional requirements -| ID | Requirement | Acceptance summary | -|---|---|---| -| PR-F01 | Implement the caller-supplied-vector ISA subset | 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 | Both profiles have typed APIs, published tolerances, and no implicit conversion. | -| PR-F03 | Preserve exact decisions | 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 | 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 | Allocation instrumentation reports zero allocations after context initialization. | -| PR-F06 | Expose batch operations | Batch results conform to scalar operations and define layouts, strides, tails, and allowed aliasing. | -| PR-F07 | Identify compatibility | Callers can query and compare ABI, ISA, profile, dtype, dimension, backend, and feature versions. | -| PR-F08 | Fail loudly without rewriting raw semantics | 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 | The same source builds and passes smoke tests on supported native and Emscripten targets. | -| PR-F10 | Keep policy outside the kernel | No base API refers to pilots, residents, hypotheses, databases, routes, or other domain concepts. | +| 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 @@ -340,6 +356,11 @@ 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 @@ -389,17 +410,17 @@ requires a named consumer, separate semantics, fixtures, and a measured reason t Detailed engineering tasks and dependencies live in [`ENG.md`](ENG.md). Product priority is: -| Epic | Priority | Outcome | -|---|---|---| -| LC-E0 Contract and governance | P0 | One implementable ABI, profile registry, and release/version policy. | -| LC-E1 Reference conformance | P0 | The f64 caller-supplied-vector subset and supporting utilities are correct before optimization. | -| LC-E2 Portable optimized core | P0 | f32/f64 FFT and batch profiles preserve decisions. | -| LC-E3 Distribution and CI | P0 | Reproducible C/C++/Rust/Python/WASM consumption. | -| LC-E4 Signal shadow migration | P0 | Highest-value existing HRR consumer validates the product. | -| LC-E5 Rust adoption | P0 | NoSQLite proves pinned Cargo consumption and exact host-owned ordering. | -| LC-E6 Interchange | P0 | The optional descriptor component has immutable, portable bytes and fail-closed compatibility checks. | -| LC-E7 Memory and world integrations | P1 | Policy-neutral trace helpers, CosyWorld, Zero LM, and dependency cleanup are earned follow-ons. | -| LC-E8 Additional profiles | P2 | MAP, exact-index, fixed-point, and freestanding work only when earned. | +| 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. @@ -418,15 +439,15 @@ P0 defines the first usable product. P1 expands proven use. P2 is intentionally | 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. Decisions to resolve before implementation +## 15. Adopted implementation decisions -| Decision | Recommended starting point | +| Decision | ABI-0 implementation | |---|---| -| Canonical location | `native/` in leCore; release a small standalone artifact from that source. | -| Library and symbols | Artifact `liblecore`; public symbols prefixed `lecore_`. | -| Versioning | Independent native package semver plus ABI, ISA, profile, generator, and format versions. | +| 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 | A context is single-thread-owned; separate contexts may run concurrently with no global mutable state. | +| 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. | diff --git a/README.md b/README.md index 9a88891..7853092 100644 --- a/README.md +++ b/README.md @@ -171,8 +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 proposed `liblecore` portable C kernel. + 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/benchmarks/bench_liblecore.py b/benchmarks/bench_liblecore.py new file mode 100644 index 0000000..697b0e3 --- /dev/null +++ b/benchmarks/bench_liblecore.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +"""Measure liblecore's direct/radix regimes without changing AUTO policy. + +The benchmark includes Python/ctypes call overhead but reuses output buffers, so it is a realistic lower-level +adapter measurement rather than a claim about one isolated FFT instruction. Results are descriptive evidence; +this tool never rewrites dispatch thresholds. +""" + +from __future__ import annotations + +import argparse +import gc +import json +from pathlib import Path +import platform +import statistics +import sys +import time +from typing import Callable + +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 +from holographic.agents_and_reasoning.holographic_ai import bind as numpy_bind # noqa: E402 + + +def timed_ns(operation: Callable[[], None], iterations: int, repeats: int) -> float: + samples = [] + operation() + was_enabled = gc.isenabled() + gc.disable() + try: + for _ in range(repeats): + started = time.perf_counter_ns() + for _ in range(iterations): + operation() + samples.append((time.perf_counter_ns() - started) / iterations) + finally: + if was_enabled: + gc.enable() + return float(statistics.median(samples)) + + +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 benchmark_dimension( + library: Library, + dimension: int, + profile: str, + repeats: int, + target_work: int, +) -> dict[str, object]: + 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) + + direct_iterations = max(1, min(2000, target_work // (dimension * dimension))) + optimized_iterations = max(50, direct_iterations) + outputs: dict[str, np.ndarray] = {} + timings: dict[str, float] = {} + 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) + + def call_native() -> None: + context.bind(left, right, out=output) + + timings[backend] = timed_ns(call_native, iterations, repeats) + outputs[backend] = output.copy() + scratch[backend] = context.scratch_bytes + + entry: dict[str, object] = { + "dimension": dimension, + "profile": profile, + "direct_iterations": direct_iterations, + "optimized_iterations": optimized_iterations, + "direct_ns": timings["direct"], + "radix2_ns": timings["radix2"], + "radix2_speedup": timings["direct"] / timings["radix2"], + "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))) + ), + "direct_setup_ns": setup_ns(library, dimension, profile, "direct", repeats), + "radix2_setup_ns": setup_ns(library, dimension, profile, "radix2", repeats), + } + + if profile == "f64": + numpy_output = np.empty(dimension, dtype=np.float64) + + def call_numpy() -> None: + nonlocal numpy_output + numpy_output = numpy_bind(left, right) + + numpy_time = timed_ns(call_numpy, optimized_iterations, repeats) + entry.update( + { + "numpy_fft_ns": numpy_time, + "radix2_speedup_vs_numpy": numpy_time / timings["radix2"], + "numpy_max_abs_vs_direct": float( + np.max(np.abs(numpy_output - outputs["direct"])) + ), + } + ) + return entry + + +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 main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--library", required=True, 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("--output", type=Path) + parser.add_argument("--json", action="store_true") + arguments = parser.parse_args() + if arguments.repeats <= 0 or arguments.target_work <= 0: + parser.error("repeats and target-work must be positive") + + library = Library(arguments.library) + profiles = ("f64", "f32") if arguments.profiles == "both" else (arguments.profiles,) + results = [ + benchmark_dimension( + library, + dimension, + profile, + arguments.repeats, + arguments.target_work, + ) + for profile in profiles + for dimension in arguments.dimensions + ] + report = { + "schema_version": 1, + "policy_effect": "none; AUTO remains unchanged", + "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, + } + 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 arguments.json: + print(encoded, end="") + else: + 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}" + ) + print("AUTO policy unchanged; adopter-backed promotion evidence is still required.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) 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..4aa99f9 --- /dev/null +++ b/native/liblecore/BENCHMARKS.md @@ -0,0 +1,61 @@ +# 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 portable-backend baseline + +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 +benchmark reuses contexts and output buffers; as a Python `ctypes` driver, it includes foreign-function call +overhead. Inputs are deterministic unit vectors. Direct-backend iteration counts are reduced at large dimensions +to keep the run bounded. + +### 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. + +## 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 JSON report captures host and library metadata, setup cost, scratch requirements, iteration counts, timings, +and direct-versus-radix numerical error. Benchmark output is observational; changing `AUTO` requires a separate, +reviewed policy change with results 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..ce1e55f --- /dev/null +++ b/native/liblecore/CMakeLists.txt @@ -0,0 +1,400 @@ +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) +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_ABI_VERSION}" + 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..266f125 --- /dev/null +++ b/native/liblecore/README.md @@ -0,0 +1,161 @@ +# 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`. + +## 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..9584a2e --- /dev/null +++ b/native/liblecore/amalgamation/lecore.c @@ -0,0 +1,3717 @@ +/* + * 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: 5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55 + * + * 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_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); +#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 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); + 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_cosine_raw( + query, 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 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); + 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_cosine_raw( + query, 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, + const float *candidate, + uint32_t dimension) +{ + float query_norm_squared = 0.0f; + float candidate_norm_squared = 0.0f; + float dot = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + query_norm_squared += query[index] * query[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]; + } + return dot / + (sqrtf(query_norm_squared) * sqrtf(candidate_norm_squared)); +} + +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; + + 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; + } + } + } + + best_index = 0; + best_score = lecore_cleanup_f32_cosine( + query, candidates, context->dimension); + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + float score = lecore_cleanup_f32_cosine( + query, + 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, + const double *candidate, + uint32_t dimension) +{ + double query_norm_squared = 0.0; + double candidate_norm_squared = 0.0; + double dot = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + query_norm_squared += query[index] * query[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]; + } + return dot / + (sqrt(query_norm_squared) * sqrt(candidate_norm_squared)); +} + +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; + + 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; + } + } + } + + best_index = 0; + best_score = lecore_cleanup_f64_cosine( + query, 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, + 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; + + 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; + + 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; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_selected( + context, + role, + rows + row * row_stride, + out_rows + row * out_stride); + } + 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; + } + + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f32_selected( + context, + trace, + keys + key * key_stride, + out_rows + key * out_stride); + } + 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; + + 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; + + /* 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; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_selected( + context, + role, + rows + row * row_stride, + out_rows + row * out_stride); + } + 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; + } + + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f64_selected( + context, + trace, + keys + key * key_stride, + out_rows + key * out_stride); + } + 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) +{ + const uint32_t dimension = context->dimension; + const float *twiddles = (const float *)context->radix2_twiddles; + uint32_t index; + + 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( + float *destination, + const float *source, + uint32_t dimension, + int involute) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + uint32_t source_index = involute && index != 0 + ? dimension - index + : index; + destination[(size_t)index * 2] = source[source_index]; + destination[(size_t)index * 2 + 1] = 0.0f; + } +} + +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; + uint32_t index; + + lecore_radix2_load_f32( + left, left_input, context->dimension, 0); + lecore_radix2_load_f32( + right, right_input, context->dimension, involute_right); + lecore_radix2_fft_f32(context, left, 0); + lecore_radix2_fft_f32(context, right, 0); + + for (index = 0; index < context->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]; + + left[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + left[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } + + lecore_radix2_fft_f32(context, left, 1); + for (index = 0; index < context->dimension; ++index) { + output[index] = left[(size_t)index * 2]; + } +} + +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); +} + +#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) +{ + const uint32_t dimension = context->dimension; + const double *twiddles = (const double *)context->radix2_twiddles; + uint32_t index; + + 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( + double *destination, + const double *source, + uint32_t dimension, + int involute) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + uint32_t source_index = involute && index != 0 + ? dimension - index + : index; + destination[(size_t)index * 2] = source[source_index]; + destination[(size_t)index * 2 + 1] = 0.0; + } +} + +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; + uint32_t index; + + lecore_radix2_load_f64( + left, left_input, context->dimension, 0); + lecore_radix2_load_f64( + right, right_input, context->dimension, involute_right); + lecore_radix2_fft_f64(context, left, 0); + lecore_radix2_fft_f64(context, right, 0); + + for (index = 0; index < context->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]; + + left[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + left[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } + + lecore_radix2_fft_f64(context, left, 1); + for (index = 0; index < context->dimension; ++index) { + output[index] = left[(size_t)index * 2]; + } +} + +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); +} + +#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..3f5d4e0 --- /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: 5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55 + * + * 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 "5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55" + + +#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..cc4efb9 --- /dev/null +++ b/native/liblecore/src/cleanup_f32.c @@ -0,0 +1,119 @@ +#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, + const float *candidate, + uint32_t dimension) +{ + float query_norm_squared = 0.0f; + float candidate_norm_squared = 0.0f; + float dot = 0.0f; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + query_norm_squared += query[index] * query[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]; + } + return dot / + (sqrtf(query_norm_squared) * sqrtf(candidate_norm_squared)); +} + +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; + + 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; + } + } + } + + best_index = 0; + best_score = lecore_cleanup_f32_cosine( + query, candidates, context->dimension); + if (!isnan(best_score)) { + for (candidate = 1; candidate < candidate_count; ++candidate) { + float score = lecore_cleanup_f32_cosine( + query, + 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..18e09e8 --- /dev/null +++ b/native/liblecore/src/cleanup_f64.c @@ -0,0 +1,121 @@ +#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, + const double *candidate, + uint32_t dimension) +{ + double query_norm_squared = 0.0; + double candidate_norm_squared = 0.0; + double dot = 0.0; + uint32_t index; + + for (index = 0; index < dimension; ++index) { + query_norm_squared += query[index] * query[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]; + } + return dot / + (sqrt(query_norm_squared) * sqrt(candidate_norm_squared)); +} + +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; + + 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; + } + } + } + + best_index = 0; + best_score = lecore_cleanup_f64_cosine( + query, 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, + 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..9cf0b50 --- /dev/null +++ b/native/liblecore/src/hrr_direct_f32.c @@ -0,0 +1,399 @@ +#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; + + 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; + + 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; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f32_selected( + context, + role, + rows + row * row_stride, + out_rows + row * out_stride); + } + 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; + } + + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f32_selected( + context, + trace, + keys + key * key_stride, + out_rows + key * out_stride); + } + 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..efb64be --- /dev/null +++ b/native/liblecore/src/hrr_direct_f64.c @@ -0,0 +1,400 @@ +#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; + + 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; + + /* 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; + } + + for (row = 0; row < row_count; ++row) { + lecore_hrr_bind_f64_selected( + context, + role, + rows + row * row_stride, + out_rows + row * out_stride); + } + 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; + } + + for (key = 0; key < key_count; ++key) { + lecore_hrr_unbind_f64_selected( + context, + trace, + keys + key * key_stride, + out_rows + key * out_stride); + } + 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..a94d523 --- /dev/null +++ b/native/liblecore/src/hrr_radix2_f32.c @@ -0,0 +1,158 @@ +#include "internal/lecore_internal.h" + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f32( + const lecore_context *context, + float *values, + int inverse) +{ + const uint32_t dimension = context->dimension; + const float *twiddles = (const float *)context->radix2_twiddles; + uint32_t index; + + 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( + float *destination, + const float *source, + uint32_t dimension, + int involute) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + uint32_t source_index = involute && index != 0 + ? dimension - index + : index; + destination[(size_t)index * 2] = source[source_index]; + destination[(size_t)index * 2 + 1] = 0.0f; + } +} + +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; + uint32_t index; + + lecore_radix2_load_f32( + left, left_input, context->dimension, 0); + lecore_radix2_load_f32( + right, right_input, context->dimension, involute_right); + lecore_radix2_fft_f32(context, left, 0); + lecore_radix2_fft_f32(context, right, 0); + + for (index = 0; index < context->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]; + + left[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + left[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } + + lecore_radix2_fft_f32(context, left, 1); + for (index = 0; index < context->dimension; ++index) { + output[index] = left[(size_t)index * 2]; + } +} + +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); +} + +#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..965ca50 --- /dev/null +++ b/native/liblecore/src/hrr_radix2_f64.c @@ -0,0 +1,158 @@ +#include "internal/lecore_internal.h" + +#if LECORE_ENABLE_RADIX2 + +static void lecore_radix2_fft_f64( + const lecore_context *context, + double *values, + int inverse) +{ + const uint32_t dimension = context->dimension; + const double *twiddles = (const double *)context->radix2_twiddles; + uint32_t index; + + 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( + double *destination, + const double *source, + uint32_t dimension, + int involute) +{ + uint32_t index; + + for (index = 0; index < dimension; ++index) { + uint32_t source_index = involute && index != 0 + ? dimension - index + : index; + destination[(size_t)index * 2] = source[source_index]; + destination[(size_t)index * 2 + 1] = 0.0; + } +} + +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; + uint32_t index; + + lecore_radix2_load_f64( + left, left_input, context->dimension, 0); + lecore_radix2_load_f64( + right, right_input, context->dimension, involute_right); + lecore_radix2_fft_f64(context, left, 0); + lecore_radix2_fft_f64(context, right, 0); + + for (index = 0; index < context->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]; + + left[(size_t)index * 2] = + left_real * right_real - left_imaginary * right_imaginary; + left[(size_t)index * 2 + 1] = + left_real * right_imaginary + left_imaginary * right_real; + } + + lecore_radix2_fft_f64(context, left, 1); + for (index = 0; index < context->dimension; ++index) { + output[index] = left[(size_t)index * 2]; + } +} + +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); +} + +#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..9759d78 --- /dev/null +++ b/native/liblecore/src/internal/lecore_internal.h @@ -0,0 +1,183 @@ +#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_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); +#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..8cb99c8 --- /dev/null +++ b/native/liblecore/src/vector_f32.c @@ -0,0 +1,463 @@ +#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 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); + 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_cosine_raw( + query, 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..2f0a00e --- /dev/null +++ b/native/liblecore/src/vector_f64.c @@ -0,0 +1,552 @@ +#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 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); + 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_cosine_raw( + query, 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..ab6b87a --- /dev/null +++ b/native/liblecore/tests/c/test_edges.c @@ -0,0 +1,110 @@ +#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 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)); + + 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..c12c3cf --- /dev/null +++ b/native/liblecore/tests/c/test_numeric.c @@ -0,0 +1,468 @@ +#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 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)); + + 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 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_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( + 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( + 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( + 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( + 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_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/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() From e245a58cc0ef2b40d1bc1596d9bbe992aea07dc9 Mon Sep 17 00:00:00 2001 From: docs-bot Date: Sun, 9 Aug 2026 00:40:20 +0000 Subject: [PATCH 3/6] docs: refresh generated docs (REFERENCE, CAPABILITIES, API_QUICKREF, PIPELINE_MAP, FACULTY_MAP, DOC_MAP) [skip ci] --- REFERENCE.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 From e2dc6a248142c67a2000cecaae77fa4ce1684614 Mon Sep 17 00:00:00 2001 From: atimics Date: Sat, 8 Aug 2026 18:08:43 -0700 Subject: [PATCH 4/6] ci: gate liblecore performance regressions --- .github/workflows/native.yml | 127 +++++ benchmarks/bench_liblecore.py | 14 +- benchmarks/check_liblecore_performance.py | 494 ++++++++++++++++++ benchmarks/liblecore_ci_policy.json | 32 ++ native/liblecore/BENCHMARKS.md | 81 ++- .../native/test_liblecore_performance_gate.py | 200 +++++++ 6 files changed, 945 insertions(+), 3 deletions(-) create mode 100644 benchmarks/check_liblecore_performance.py create mode 100644 benchmarks/liblecore_ci_policy.json create mode 100644 tests/native/test_liblecore_performance_gate.py diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index e442aa9..2cf9998 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -241,6 +241,133 @@ jobs: --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 the pull-request base + if: steps.baseline.outputs.available == 'true' + run: >- + python3 benchmarks/bench_liblecore.py + --library "$BASELINE_BUILD/liblecore.so" + --dimensions 256,512,1024 + --profiles both + --repeats 9 + --target-work 16000000 + --output "$BASELINE_REPORT" + + - name: Measure the guarded bind regimes + run: >- + python3 benchmarks/bench_liblecore.py + --library "$CANDIDATE_BUILD/liblecore.so" + --dimensions 256,512,1024 + --profiles both + --repeats 9 + --target-work 16000000 + --output "$PERFORMANCE_REPORT" + + - name: Enforce the same-run 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 diff --git a/benchmarks/bench_liblecore.py b/benchmarks/bench_liblecore.py index 697b0e3..1fd5f91 100644 --- a/benchmarks/bench_liblecore.py +++ b/benchmarks/bench_liblecore.py @@ -28,6 +28,9 @@ from holographic.agents_and_reasoning.holographic_ai import bind as numpy_bind # noqa: E402 +MINIMUM_OPTIMIZED_ITERATIONS = 1000 + + def timed_ns(operation: Callable[[], None], iterations: int, repeats: int) -> float: samples = [] operation() @@ -70,7 +73,7 @@ def benchmark_dimension( right /= np.linalg.norm(right) direct_iterations = max(1, min(2000, target_work // (dimension * dimension))) - optimized_iterations = max(50, direct_iterations) + optimized_iterations = max(MINIMUM_OPTIMIZED_ITERATIONS, direct_iterations) outputs: dict[str, np.ndarray] = {} timings: dict[str, float] = {} scratch: dict[str, int] = {} @@ -86,9 +89,13 @@ def call_native() -> None: outputs[backend] = output.copy() scratch[backend] = context.scratch_bytes + with library.context(dimension, profile=profile, backend="auto") as context: + auto_backend = context.backend + entry: dict[str, object] = { "dimension": dimension, "profile": profile, + "auto_backend": auto_backend, "direct_iterations": direct_iterations, "optimized_iterations": optimized_iterations, "direct_ns": timings["direct"], @@ -163,6 +170,11 @@ def main() -> int: report = { "schema_version": 1, "policy_effect": "none; AUTO remains unchanged", + "benchmark": { + "repeats": arguments.repeats, + "target_work": arguments.target_work, + "minimum_optimized_iterations": MINIMUM_OPTIMIZED_ITERATIONS, + }, "library": { "path": library.path, "bytes": Path(library.path).stat().st_size, diff --git a/benchmarks/check_liblecore_performance.py b/benchmarks/check_liblecore_performance.py new file mode 100644 index 0000000..49b48e2 --- /dev/null +++ b/benchmarks/check_liblecore_performance.py @@ -0,0 +1,494 @@ +#!/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 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: 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 + + @property + def passed(self) -> bool: + return ( + 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 + ) + ) + + @property + def direct_slowdown(self) -> Optional[float]: + if self.baseline_direct_ns is None: + return None + return self.direct_ns / self.baseline_direct_ns + + @property + def radix2_slowdown(self) -> Optional[float]: + if self.baseline_radix2_ns is None: + return None + return self.radix2_ns / self.baseline_radix2_ns + + +@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, float, float]]]: + if _integer(policy.get("schema_version"), "policy.schema_version") != 1: + 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") + profiles = _mapping(policy.get("profiles"), "policy.profiles") + regimes: List[Tuple[str, int, 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") + minimums = _mapping( + profile_policy.get("minimum_speedup"), + f"policy.profiles.{profile}.minimum_speedup", + ) + if not minimums: + raise GateInputError(f"policy.profiles.{profile}.minimum_speedup must not be empty") + for raw_dimension, raw_minimum in minimums.items(): + try: + dimension = int(raw_dimension) + except (TypeError, ValueError) as error: + raise GateInputError( + f"policy dimension {raw_dimension!r} for {profile} is not an integer" + ) from error + if dimension <= 0 or dimension & (dimension - 1): + raise GateInputError(f"policy dimension {dimension} for {profile} must be a positive power of two") + 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, +) -> None: + 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") + 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}" + ) + + +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 _read_result( + result: Mapping[str, Any], + label: str, + required_auto_backend: int, + minimum_optimized_iterations: int, +) -> Tuple[float, float, float, float]: + 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" + ) + _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_ns = _finite(result.get("direct_ns"), f"{label}.direct_ns", positive=True) + radix2_ns = _finite(result.get("radix2_ns"), f"{label}.radix2_ns", positive=True) + 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 direct_ns, radix2_ns, speedup, 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) + _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: + _validate_metadata(baseline, policy, required_library, "baseline") + baseline_indexed = _index_results(baseline, "baseline") + + 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, + ) + + 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}" + direct_ns, radix2_ns, speedup, delta = _read_result( + result, + label, + required_auto_backend, + minimum_optimized_iterations, + ) + + baseline_direct_ns: Optional[float] = None + baseline_radix2_ns: 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_direct_ns, baseline_radix2_ns, _, _ = _read_result( + baseline_indexed[key], + f"baseline result {profile}/{dimension}", + required_auto_backend, + minimum_optimized_iterations, + ) + + measurement = Measurement( + profile=profile, + dimension=dimension, + direct_ns=direct_ns, + radix2_ns=radix2_ns, + speedup=speedup, + minimum_speedup=minimum, + max_abs_delta=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, + ) + measurements.append(measurement) + if speedup < minimum: + failures.append( + f"{profile}/{dimension} radix-2 speedup {speedup:.2f}x is below {minimum:.2f}x" + ) + if delta > delta_limit: + failures.append( + f"{profile}/{dimension} max delta {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 latency is compared with the base commit on this runner; at most " + f"{max_candidate_slowdown:.2f}x slowdown is allowed 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')}`).", + "", + "| Profile | Dimension | Direct (us) | Radix-2 (us) | Speedup | Required | Direct/base | 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" + 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"{item.minimum_speedup:.2f}x | {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..a68e85c --- /dev/null +++ b/benchmarks/liblecore_ci_policy.json @@ -0,0 +1,32 @@ +{ + "schema_version": 1, + "report_schema_version": 1, + "required_policy_effect": "none; AUTO remains unchanged", + "minimum_repeats": 9, + "minimum_optimized_iterations": 1000, + "max_candidate_slowdown": 1.35, + "required_library": { + "abi": 0, + "isa": 1, + "capabilities_mask": 15, + "auto_backend": 1 + }, + "profiles": { + "f64": { + "max_abs_delta": 1e-12, + "minimum_speedup": { + "256": 2.0, + "512": 5.0, + "1024": 10.0 + } + }, + "f32": { + "max_abs_delta": 1e-4, + "minimum_speedup": { + "256": 2.0, + "512": 5.0, + "1024": 10.0 + } + } + } +} diff --git a/native/liblecore/BENCHMARKS.md b/native/liblecore/BENCHMARKS.md index 4aa99f9..60a9d22 100644 --- a/native/liblecore/BENCHMARKS.md +++ b/native/liblecore/BENCHMARKS.md @@ -57,5 +57,82 @@ python3 benchmarks/bench_liblecore.py \ ``` The JSON report captures host and library metadata, setup cost, scratch requirements, iteration counts, timings, -and direct-versus-radix numerical error. Benchmark output is observational; changing `AUTO` requires a separate, -reviewed policy change with results from supported platforms and real adopters. +and direct-versus-radix numerical error. + +## 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 both f64 and f32 bind at dimensions 256, +512, and 1024. Each reported latency is the median of nine samples; the target work is raised to 16,000,000 so +each sample contains enough calls to reduce timer and foreign-function-interface noise. The radix-2 timing loop +always executes at least 1,000 calls per sample, including dimension 1024. + +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 driver then measures both libraries with the same Python +environment, dimensions, work target, and repeat count. The committed policy permits at most a 1.35x candidate +slowdown against that same-run base for both direct and radix-2 latency in every guarded profile/dimension. +Same-run comparisons reduce runner-to-runner noise; they do not pretend that hosted CPU allocation, frequency +scaling, virtualization, or neighboring workload are perfectly controlled. + +The gate also evaluates relative invariants within the candidate report. It checks that: + +- every expected profile and dimension is present and finite; +- radix-2 remains numerically close to the direct oracle within the profile-specific error limit; +- radix-2 keeps a conservative minimum speedup over direct at the guarded dimensions; +- report metadata confirms the expected schema, ABI, ISA, and capabilities; and +- an actual `LECORE_BACKEND_AUTO` context resolves to direct for every measured profile and dimension. + +For both profiles the minimum same-run speedups are 2.0x at dimension 256, 5.0x at 512, and 10.0x at 1024. +The maximum direct-versus-radix absolute delta is `1e-12` for f64 and `1e-4` for f32. These floors sit well below +the recorded baseline ratios, but still fail if radix-2 falls back to quadratic work or suffers a major relative +regression. The conformance jobs retain the tighter, operation-wide numerical contract. + +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 invariant checks without `--baseline`. 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 the measured score table and every applicable policy result 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 build/liblecore-performance/liblecore.so \ + --dimensions 256,512,1024 \ + --profiles both \ + --repeats 9 \ + --target-work 16000000 \ + --output build/liblecore-performance/liblecore-performance.json +python3 benchmarks/check_liblecore_performance.py \ + --report build/liblecore-performance/liblecore-performance.json \ + --baseline /path/to/base-liblecore-performance.json \ + --policy benchmarks/liblecore_ci_policy.json +``` + +Omit `--baseline` only when bootstrapping against a revision that has no liblecore benchmark. To reproduce the +normal pull-request gate, build and measure the base revision with the identical commands and arguments in a +separate build directory, then pass its JSON report as 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/tests/native/test_liblecore_performance_gate.py b/tests/native/test_liblecore_performance_gate.py new file mode 100644 index 0000000..d91df3c --- /dev/null +++ b/tests/native/test_liblecore_performance_gate.py @@ -0,0 +1,200 @@ +#!/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 sys +import tempfile +import unittest + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(REPOSITORY_ROOT)) + +from benchmarks.check_liblecore_performance import GateInputError, evaluate, main # noqa: E402 + + +def policy(): + return { + "schema_version": 1, + "report_schema_version": 1, + "required_policy_effect": "none; AUTO remains unchanged", + "minimum_repeats": 9, + "minimum_optimized_iterations": 1000, + "max_candidate_slowdown": 1.35, + "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 report(): + results = [] + for profile, delta in (("f64", 1e-15), ("f32", 1e-7)): + results.append( + { + "profile": profile, + "dimension": 256, + "auto_backend": 1, + "direct_iterations": 244, + "optimized_iterations": 1000, + "direct_ns": 40_000.0, + "radix2_ns": 8_000.0, + "radix2_speedup": 5.0, + "radix2_max_abs_vs_direct": delta, + } + ) + return { + "schema_version": 1, + "policy_effect": "none; AUTO remains unchanged", + "benchmark": { + "repeats": 9, + "target_work": 16_000_000, + "minimum_optimized_iterations": 1000, + }, + "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_valid_report_passes_and_renders_scores(self): + result = evaluate(report(), policy()) + self.assertTrue(result.passed) + self.assertIn("Gate: PASS", result.markdown) + self.assertIn("5.00x", result.markdown) + self.assertEqual(len(result.measurements), 2) + self.assertFalse(result.baseline_compared) + + def test_same_runner_baseline_passes_and_is_rendered(self): + candidate = report() + baseline = report() + candidate["results"][0]["direct_ns"] = 42_000.0 + candidate["results"][0]["radix2_ns"] = 8_400.0 + result = evaluate(candidate, policy(), baseline) + self.assertTrue(result.passed) + self.assertTrue(result.baseline_compared) + self.assertIn("1.05x", result.markdown) + + def test_proportional_candidate_slowdown_fails_against_base(self): + candidate = report() + baseline = report() + for entry in candidate["results"]: + entry["direct_ns"] *= 1.5 + entry["radix2_ns"] *= 1.5 + 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_speed_regression_fails(self): + candidate = report() + candidate["results"][0]["radix2_ns"] = 30_000.0 + candidate["results"][0]["radix2_speedup"] = 4.0 / 3.0 + result = evaluate(candidate, policy()) + self.assertFalse(result.passed) + self.assertIn("below 2.00x", "\n".join(result.failures)) + + 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): + baseline = report() + baseline["results"].pop(0) + result = evaluate(report(), 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_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]["radix2_speedup"] = 99.0 + with self.assertRaisesRegex(GateInputError, "inconsistent"): + 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_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() + candidate["results"][0]["radix2_ns"] = 30_000.0 + candidate["results"][0]["radix2_speedup"] = 4.0 / 3.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() From bd8da06c8808887aad7bfce1ccebc0d8a5a08b55 Mon Sep 17 00:00:00 2001 From: atimics Date: Sat, 8 Aug 2026 18:25:15 -0700 Subject: [PATCH 5/6] ci: stabilize liblecore benchmark comparisons --- .github/workflows/native.yml | 101 ++++- benchmarks/bench_liblecore.py | 389 +++++++++++++++--- benchmarks/check_liblecore_performance.py | 34 +- benchmarks/liblecore_ci_policy.json | 6 +- native/liblecore/BENCHMARKS.md | 41 +- .../native/test_liblecore_performance_gate.py | 38 +- 6 files changed, 504 insertions(+), 105 deletions(-) diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index 2cf9998..b34ad51 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -253,6 +253,8 @@ jobs: 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 + RETRY_PERFORMANCE_REPORT: ${{ github.workspace }}/build/liblecore-performance/liblecore-performance-retry.json + RETRY_BASELINE_REPORT: ${{ github.workspace }}/build/liblecore-performance-base/liblecore-performance-base-retry.json steps: - uses: actions/checkout@v6 @@ -319,41 +321,116 @@ jobs: if: steps.baseline.outputs.available == 'true' run: cmake --build "$BASELINE_BUILD" --parallel 2 - - name: Measure the pull-request base + - name: Measure base and candidate as an interleaved pair if: steps.baseline.outputs.available == 'true' run: >- python3 benchmarks/bench_liblecore.py - --library "$BASELINE_BUILD/liblecore.so" + --library "$CANDIDATE_BUILD/liblecore.so" + --baseline-library "$BASELINE_BUILD/liblecore.so" --dimensions 256,512,1024 --profiles both - --repeats 9 - --target-work 16000000 - --output "$BASELINE_REPORT" + --repeats 10 + --target-work 64000000 + --output "$PERFORMANCE_REPORT" + --baseline-output "$BASELINE_REPORT" - - name: Measure the guarded bind regimes + - 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 9 - --target-work 16000000 + --repeats 10 + --target-work 64000000 --output "$PERFORMANCE_REPORT" - - name: Enforce the same-run candidate-versus-base policy + - name: Probe the same-run candidate-versus-base policy + id: initial_base_gate if: steps.baseline.outputs.available == 'true' + continue-on-error: 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 + - name: Probe the bootstrap invariant policy + id: initial_bootstrap_gate if: steps.baseline.outputs.available != 'true' + continue-on-error: true + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$PERFORMANCE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + + - name: Publish the passing same-run scorecard + if: >- + steps.baseline.outputs.available == 'true' && + steps.initial_base_gate.outcome == 'success' run: >- python3 benchmarks/check_liblecore_performance.py --report "$PERFORMANCE_REPORT" + --baseline "$BASELINE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + --summary "$GITHUB_STEP_SUMMARY" + + - name: Publish the passing bootstrap scorecard + if: >- + steps.baseline.outputs.available != 'true' && + steps.initial_bootstrap_gate.outcome == 'success' + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$PERFORMANCE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + --summary "$GITHUB_STEP_SUMMARY" + + - name: Re-measure the interleaved pair after a failed probe + if: >- + steps.baseline.outputs.available == 'true' && + steps.initial_base_gate.outcome == 'failure' + 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 + --output "$RETRY_PERFORMANCE_REPORT" + --baseline-output "$RETRY_BASELINE_REPORT" + + - name: Re-measure the bootstrap candidate after a failed probe + if: >- + steps.baseline.outputs.available != 'true' && + steps.initial_bootstrap_gate.outcome == 'failure' + run: >- + python3 benchmarks/bench_liblecore.py + --library "$CANDIDATE_BUILD/liblecore.so" + --dimensions 256,512,1024 + --profiles both + --repeats 10 + --target-work 64000000 + --output "$RETRY_PERFORMANCE_REPORT" + + - name: Enforce the confirmed candidate-versus-base policy + if: >- + steps.baseline.outputs.available == 'true' && + steps.initial_base_gate.outcome == 'failure' + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$RETRY_PERFORMANCE_REPORT" + --baseline "$RETRY_BASELINE_REPORT" + --policy benchmarks/liblecore_ci_policy.json + --summary "$GITHUB_STEP_SUMMARY" + + - name: Enforce the confirmed bootstrap invariant policy + if: >- + steps.baseline.outputs.available != 'true' && + steps.initial_bootstrap_gate.outcome == 'failure' + run: >- + python3 benchmarks/check_liblecore_performance.py + --report "$RETRY_PERFORMANCE_REPORT" --policy benchmarks/liblecore_ci_policy.json --summary "$GITHUB_STEP_SUMMARY" @@ -365,6 +442,8 @@ jobs: path: | ${{ env.PERFORMANCE_REPORT }} ${{ env.BASELINE_REPORT }} + ${{ env.RETRY_PERFORMANCE_REPORT }} + ${{ env.RETRY_BASELINE_REPORT }} if-no-files-found: warn retention-days: 30 diff --git a/benchmarks/bench_liblecore.py b/benchmarks/bench_liblecore.py index 1fd5f91..1861a81 100644 --- a/benchmarks/bench_liblecore.py +++ b/benchmarks/bench_liblecore.py @@ -28,7 +28,7 @@ from holographic.agents_and_reasoning.holographic_ai import bind as numpy_bind # noqa: E402 -MINIMUM_OPTIMIZED_ITERATIONS = 1000 +MINIMUM_OPTIMIZED_ITERATIONS = 4000 def timed_ns(operation: Callable[[], None], iterations: int, repeats: int) -> float: @@ -48,6 +48,34 @@ def timed_ns(operation: Callable[[], None], iterations: int, repeats: int) -> fl return float(statistics.median(samples)) +def paired_timed_ns( + candidate_operation: Callable[[], None], + baseline_operation: Callable[[], None], + iterations: int, + repeats: int, +) -> tuple[float, float]: + """Time two operations in alternating order to reduce ordering bias.""" + + samples: tuple[list[float], list[float]] = ([], []) + operations = (candidate_operation, baseline_operation) + 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) / iterations) + finally: + if was_enabled: + gc.enable() + return tuple(float(statistics.median(values)) for values in samples) + + def setup_ns(library: Library, dimension: int, profile: str, backend: str, repeats: int) -> float: samples = [] for _ in range(repeats): @@ -58,19 +86,80 @@ def setup_ns(library: Library, dimension: int, profile: str, backend: str, repea return float(statistics.median(samples)) -def benchmark_dimension( - library: Library, +def paired_setup_ns( + candidate_library: Library, + baseline_library: Library, dimension: int, profile: str, + backend: str, repeats: int, - target_work: int, -) -> dict[str, object]: +) -> 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, + direct_iterations: int, + optimized_iterations: int, + timings: dict[str, float], + outputs: dict[str, np.ndarray], + scratch: dict[str, int], + setup_timings: dict[str, float], + auto_backend: int, +) -> dict[str, object]: + return { + "dimension": dimension, + "profile": profile, + "auto_backend": auto_backend, + "direct_iterations": direct_iterations, + "optimized_iterations": optimized_iterations, + "direct_ns": timings["direct"], + "radix2_ns": timings["radix2"], + "radix2_speedup": timings["direct"] / timings["radix2"], + "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) + ) + ) + ), + "direct_setup_ns": setup_timings["direct"], + "radix2_setup_ns": setup_timings["radix2"], + } + + +def benchmark_dimension( + library: Library, + dimension: int, + profile: str, + repeats: int, + target_work: int, +) -> 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) @@ -92,23 +181,21 @@ def call_native() -> None: with library.context(dimension, profile=profile, backend="auto") as context: auto_backend = context.backend - entry: dict[str, object] = { - "dimension": dimension, - "profile": profile, - "auto_backend": auto_backend, - "direct_iterations": direct_iterations, - "optimized_iterations": optimized_iterations, - "direct_ns": timings["direct"], - "radix2_ns": timings["radix2"], - "radix2_speedup": timings["direct"] / timings["radix2"], - "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))) - ), - "direct_setup_ns": setup_ns(library, dimension, profile, "direct", repeats), - "radix2_setup_ns": setup_ns(library, dimension, profile, "radix2", repeats), + setup_timings = { + backend: setup_ns(library, dimension, profile, backend, repeats) + for backend in ("direct", "radix2") } + entry = result_entry( + dimension, + profile, + direct_iterations, + optimized_iterations, + timings, + outputs, + scratch, + setup_timings, + auto_backend, + ) if profile == "f64": numpy_output = np.empty(dimension, dtype=np.float64) @@ -130,6 +217,107 @@ def call_numpy() -> None: return entry +def benchmark_dimension_pair( + candidate_library: Library, + baseline_library: Library, + dimension: int, + profile: str, + repeats: int, + target_work: int, +) -> 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, float], dict[str, float]] = ({}, {}) + 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) + + def call_candidate() -> None: + candidate_context.bind(left, right, out=candidate_output) + + def call_baseline() -> None: + baseline_context.bind(left, right, out=baseline_output) + + candidate_ns, baseline_ns = paired_timed_ns( + call_candidate, + call_baseline, + iterations, + repeats, + ) + timings[0][backend] = candidate_ns + timings[1][backend] = baseline_ns + 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 + + 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, + direct_iterations, + optimized_iterations, + timings[index], + outputs[index], + scratch[index], + setup_timings[index], + auto_backends[index], + ) + for index in range(2) + ) + + if profile == "f64": + numpy_output = np.empty(dimension, dtype=np.float64) + + def call_numpy() -> None: + nonlocal numpy_output + numpy_output = numpy_bind(left, right) + + numpy_time = timed_ns(call_numpy, optimized_iterations, repeats) + for index, entry in enumerate(entries): + entry.update( + { + "numpy_fft_ns": numpy_time, + "radix2_speedup_vs_numpy": numpy_time / timings[index]["radix2"], + "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): @@ -137,9 +325,72 @@ def parse_dimensions(value: str) -> list[int]: return dimensions +def build_report( + library: Library, + results: list[dict[str, object]], + repeats: int, + target_work: int, + comparison_mode: str, +) -> dict[str, object]: + return { + "schema_version": 1, + "policy_effect": "none; AUTO remains unchanged", + "benchmark": { + "repeats": repeats, + "target_work": target_work, + "minimum_optimized_iterations": MINIMUM_OPTIMIZED_ITERATIONS, + "comparison_mode": comparison_mode, + }, + "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): + 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"{candidate['direct_ns'] / baseline['direct_ns']:>5.2f}x/" + f"{candidate['radix2_ns'] / baseline['radix2_ns']:.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, @@ -153,59 +404,73 @@ def main() -> int: arguments = parser.parse_args() if arguments.repeats <= 0 or arguments.target_work <= 0: parser.error("repeats and target-work 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,) - results = [ - benchmark_dimension( - library, - dimension, - profile, + baseline_results = None + baseline_report = None + if arguments.baseline_library is None: + results = [ + benchmark_dimension( + library, + dimension, + profile, + arguments.repeats, + arguments.target_work, + ) + for profile in profiles + for dimension in arguments.dimensions + ] + comparison_mode = "single" + else: + baseline_library = Library(arguments.baseline_library) + 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, + ) + results.append(candidate_result) + baseline_results.append(baseline_result) + comparison_mode = "interleaved" + baseline_report = build_report( + baseline_library, + baseline_results, arguments.repeats, arguments.target_work, + comparison_mode, ) - for profile in profiles - for dimension in arguments.dimensions - ] - report = { - "schema_version": 1, - "policy_effect": "none; AUTO remains unchanged", - "benchmark": { - "repeats": arguments.repeats, - "target_work": arguments.target_work, - "minimum_optimized_iterations": MINIMUM_OPTIMIZED_ITERATIONS, - }, - "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, - } + + report = build_report( + library, + results, + arguments.repeats, + arguments.target_work, + comparison_mode, + ) 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("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}" - ) + print_results(results) print("AUTO policy unchanged; adopter-backed promotion evidence is still required.") return 0 diff --git a/benchmarks/check_liblecore_performance.py b/benchmarks/check_liblecore_performance.py index 49b48e2..9b0e756 100644 --- a/benchmarks/check_liblecore_performance.py +++ b/benchmarks/check_liblecore_performance.py @@ -236,6 +236,14 @@ def _index_results( 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 _read_result( result: Mapping[str, Any], label: str, @@ -290,6 +298,30 @@ def evaluate( if baseline is not None: _validate_metadata(baseline, policy, required_library, "baseline") 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}" + ) + 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" @@ -396,7 +428,7 @@ def render_markdown( library = report.get("library") if isinstance(report.get("library"), dict) else {} status = "PASS" if not failures else "FAIL" comparison = ( - f"Candidate latency is compared with the base commit on this runner; at most " + f"Candidate and base latency are interleaved on this runner; at most " f"{max_candidate_slowdown:.2f}x slowdown is allowed per backend." if baseline_compared else "Bootstrap mode: the base commit has no benchmarkable liblecore, so only same-build invariants apply." diff --git a/benchmarks/liblecore_ci_policy.json b/benchmarks/liblecore_ci_policy.json index a68e85c..9185a7c 100644 --- a/benchmarks/liblecore_ci_policy.json +++ b/benchmarks/liblecore_ci_policy.json @@ -2,8 +2,10 @@ "schema_version": 1, "report_schema_version": 1, "required_policy_effect": "none; AUTO remains unchanged", - "minimum_repeats": 9, - "minimum_optimized_iterations": 1000, + "required_baseline_comparison_mode": "interleaved", + "required_bootstrap_comparison_mode": "single", + "minimum_repeats": 10, + "minimum_optimized_iterations": 4000, "max_candidate_slowdown": 1.35, "required_library": { "abi": 0, diff --git a/native/liblecore/BENCHMARKS.md b/native/liblecore/BENCHMARKS.md index 60a9d22..632eb43 100644 --- a/native/liblecore/BENCHMARKS.md +++ b/native/liblecore/BENCHMARKS.md @@ -63,23 +63,30 @@ and direct-versus-radix numerical error. 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 both f64 and f32 bind at dimensions 256, -512, and 1024. Each reported latency is the median of nine samples; the target work is raised to 16,000,000 so +512, and 1024. Each reported latency is the median of ten samples; the target work is raised to 64,000,000 so each sample contains enough calls to reduce timer and foreign-function-interface noise. The radix-2 timing loop -always executes at least 1,000 calls per sample, including dimension 1024. +always executes at least 4,000 calls per sample, including dimension 1024. On the original Apple baseline this +puts the guarded direct and radix-2 samples in roughly the 30–80 millisecond range. 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 driver then measures both libraries with the same Python -environment, dimensions, work target, and repeat count. The committed policy permits at most a 1.35x candidate -slowdown against that same-run base for both direct and radix-2 latency in every guarded profile/dimension. -Same-run comparisons reduce runner-to-runner noise; they do not pretend that hosted CPU allocation, frequency -scaling, virtualization, or neighboring workload are perfectly controlled. +compiler and options as the candidate. One benchmark process loads both libraries and measures each guarded cell +as an interleaved pair: base and candidate alternate which runs first on every repeat, with identical inputs, +iteration counts, Python environment, dimensions, and work target. The committed policy permits at most a 1.35x +candidate slowdown against that same-run base for both direct and radix-2 latency in every guarded +profile/dimension. Interleaving controls short-term frequency and scheduling drift far better than running two +complete reports in sequence; it does not pretend that a hosted runner is perfectly isolated. + +If the first comparison fails, the workflow repeats the interleaved pair once and applies the same policy to that +confirmation report. Only a failure that persists in the confirmation run blocks the job. This single retry +handles a transient scheduled-runner interruption without averaging it into the score, while a repeatable +slowdown or malformed report still fails. Both initial and confirmation reports are retained. The gate also evaluates relative invariants within the candidate report. It checks that: - every expected profile and dimension is present and finite; - radix-2 remains numerically close to the direct oracle within the profile-specific error limit; - radix-2 keeps a conservative minimum speedup over direct at the guarded dimensions; -- report metadata confirms the expected schema, ABI, ISA, and capabilities; and +- report metadata confirms the expected schema, ABI, ISA, capabilities, and interleaved comparison mode; and - an actual `LECORE_BACKEND_AUTO` context resolves to direct for every measured profile and dimension. For both profiles the minimum same-run speedups are 2.0x at dimension 256, 5.0x at 512, and 10.0x at 1024. @@ -111,21 +118,23 @@ cmake -S native/liblecore -B build/liblecore-performance \ -DCMAKE_BUILD_TYPE=Release cmake --build build/liblecore-performance --parallel 2 python3 benchmarks/bench_liblecore.py \ - --library build/liblecore-performance/liblecore.so \ + --library /path/to/candidate/liblecore.so \ + --baseline-library /path/to/base/liblecore.so \ --dimensions 256,512,1024 \ --profiles both \ - --repeats 9 \ - --target-work 16000000 \ - --output build/liblecore-performance/liblecore-performance.json + --repeats 10 \ + --target-work 64000000 \ + --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 /path/to/base-liblecore-performance.json \ + --baseline build/liblecore-performance/liblecore-performance-base.json \ --policy benchmarks/liblecore_ci_policy.json ``` -Omit `--baseline` only when bootstrapping against a revision that has no liblecore benchmark. To reproduce the -normal pull-request gate, build and measure the base revision with the identical commands and arguments in a -separate build directory, then pass its JSON report as shown above. +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 diff --git a/tests/native/test_liblecore_performance_gate.py b/tests/native/test_liblecore_performance_gate.py index d91df3c..02d1b75 100644 --- a/tests/native/test_liblecore_performance_gate.py +++ b/tests/native/test_liblecore_performance_gate.py @@ -25,8 +25,10 @@ def policy(): "schema_version": 1, "report_schema_version": 1, "required_policy_effect": "none; AUTO remains unchanged", - "minimum_repeats": 9, - "minimum_optimized_iterations": 1000, + "required_baseline_comparison_mode": "interleaved", + "required_bootstrap_comparison_mode": "single", + "minimum_repeats": 10, + "minimum_optimized_iterations": 4000, "max_candidate_slowdown": 1.35, "required_library": { "abi": 0, @@ -41,7 +43,7 @@ def policy(): } -def report(): +def report(comparison_mode="single"): results = [] for profile, delta in (("f64", 1e-15), ("f32", 1e-7)): results.append( @@ -50,7 +52,7 @@ def report(): "dimension": 256, "auto_backend": 1, "direct_iterations": 244, - "optimized_iterations": 1000, + "optimized_iterations": 4000, "direct_ns": 40_000.0, "radix2_ns": 8_000.0, "radix2_speedup": 5.0, @@ -61,9 +63,10 @@ def report(): "schema_version": 1, "policy_effect": "none; AUTO remains unchanged", "benchmark": { - "repeats": 9, - "target_work": 16_000_000, - "minimum_optimized_iterations": 1000, + "comparison_mode": comparison_mode, + "repeats": 10, + "target_work": 64_000_000, + "minimum_optimized_iterations": 4000, }, "library": {"version": "0.1.0", "abi": 0, "isa": 1, "capabilities": 255}, "host": {"platform": "test", "machine": "test", "python": "3.9", "numpy": "test"}, @@ -81,8 +84,8 @@ def test_valid_report_passes_and_renders_scores(self): self.assertFalse(result.baseline_compared) def test_same_runner_baseline_passes_and_is_rendered(self): - candidate = report() - baseline = report() + candidate = report("interleaved") + baseline = report("interleaved") candidate["results"][0]["direct_ns"] = 42_000.0 candidate["results"][0]["radix2_ns"] = 8_400.0 result = evaluate(candidate, policy(), baseline) @@ -91,8 +94,8 @@ def test_same_runner_baseline_passes_and_is_rendered(self): self.assertIn("1.05x", result.markdown) def test_proportional_candidate_slowdown_fails_against_base(self): - candidate = report() - baseline = report() + candidate = report("interleaved") + baseline = report("interleaved") for entry in candidate["results"]: entry["direct_ns"] *= 1.5 entry["radix2_ns"] *= 1.5 @@ -125,14 +128,23 @@ def test_missing_regime_fails(self): self.assertIn("missing result for f32 dimension 256", result.failures) def test_missing_baseline_regime_fails_the_rendered_row(self): - baseline = report() + candidate = report("interleaved") + baseline = report("interleaved") baseline["results"].pop(0) - result = evaluate(report(), policy(), baseline) + 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_interleaved_report_cannot_claim_bootstrap_mode(self): + with self.assertRaisesRegex(GateInputError, "comparison_mode"): + evaluate(report("interleaved"), policy()) + def test_duplicate_regime_is_rejected(self): candidate = report() candidate["results"].append(copy.deepcopy(candidate["results"][0])) From 8a803ae2d20634fbebb08e88787eeb78c7d415f8 Mon Sep 17 00:00:00 2001 From: atimics Date: Sat, 8 Aug 2026 22:15:35 -0700 Subject: [PATCH 6/6] perf: optimize and harden liblecore preview --- .github/workflows/native.yml | 86 +-- ENG.md | 28 +- benchmarks/bench_liblecore.py | 450 +++++++++--- benchmarks/check_liblecore_performance.py | 326 +++++++-- benchmarks/liblecore_ci_policy.json | 26 +- native/liblecore/BENCHMARKS.md | 108 ++- native/liblecore/CMakeLists.txt | 12 +- native/liblecore/README.md | 5 +- native/liblecore/amalgamation/lecore.c | 642 +++++++++++++++--- native/liblecore/amalgamation/lecore.h | 4 +- native/liblecore/src/cleanup_f32.c | 24 +- native/liblecore/src/cleanup_f64.c | 24 +- native/liblecore/src/hrr_direct_f32.c | 82 ++- native/liblecore/src/hrr_direct_f64.c | 86 ++- native/liblecore/src/hrr_radix2_f32.c | 160 ++++- native/liblecore/src/hrr_radix2_f64.c | 160 ++++- .../liblecore/src/internal/lecore_internal.h | 32 + native/liblecore/src/vector_f32.c | 38 +- native/liblecore/src/vector_f64.c | 38 +- native/liblecore/tests/c/test_edges.c | 13 + native/liblecore/tests/c/test_numeric.c | 197 ++++++ .../native/test_liblecore_performance_gate.py | 237 ++++++- 22 files changed, 2256 insertions(+), 522 deletions(-) diff --git a/.github/workflows/native.yml b/.github/workflows/native.yml index b34ad51..56ea175 100644 --- a/.github/workflows/native.yml +++ b/.github/workflows/native.yml @@ -253,8 +253,6 @@ jobs: 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 - RETRY_PERFORMANCE_REPORT: ${{ github.workspace }}/build/liblecore-performance/liblecore-performance-retry.json - RETRY_BASELINE_REPORT: ${{ github.workspace }}/build/liblecore-performance-base/liblecore-performance-base-retry.json steps: - uses: actions/checkout@v6 @@ -331,6 +329,8 @@ jobs: --profiles both --repeats 10 --target-work 64000000 + --sample-target-ms 30 + --gate --output "$PERFORMANCE_REPORT" --baseline-output "$BASELINE_REPORT" @@ -343,31 +343,12 @@ jobs: --profiles both --repeats 10 --target-work 64000000 + --sample-target-ms 30 + --gate --output "$PERFORMANCE_REPORT" - - name: Probe the same-run candidate-versus-base policy - id: initial_base_gate + - name: Enforce the paired candidate-versus-base policy if: steps.baseline.outputs.available == 'true' - continue-on-error: true - run: >- - python3 benchmarks/check_liblecore_performance.py - --report "$PERFORMANCE_REPORT" - --baseline "$BASELINE_REPORT" - --policy benchmarks/liblecore_ci_policy.json - - - name: Probe the bootstrap invariant policy - id: initial_bootstrap_gate - if: steps.baseline.outputs.available != 'true' - continue-on-error: true - run: >- - python3 benchmarks/check_liblecore_performance.py - --report "$PERFORMANCE_REPORT" - --policy benchmarks/liblecore_ci_policy.json - - - name: Publish the passing same-run scorecard - if: >- - steps.baseline.outputs.available == 'true' && - steps.initial_base_gate.outcome == 'success' run: >- python3 benchmarks/check_liblecore_performance.py --report "$PERFORMANCE_REPORT" @@ -375,65 +356,14 @@ jobs: --policy benchmarks/liblecore_ci_policy.json --summary "$GITHUB_STEP_SUMMARY" - - name: Publish the passing bootstrap scorecard - if: >- - steps.baseline.outputs.available != 'true' && - steps.initial_bootstrap_gate.outcome == 'success' + - 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: Re-measure the interleaved pair after a failed probe - if: >- - steps.baseline.outputs.available == 'true' && - steps.initial_base_gate.outcome == 'failure' - 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 - --output "$RETRY_PERFORMANCE_REPORT" - --baseline-output "$RETRY_BASELINE_REPORT" - - - name: Re-measure the bootstrap candidate after a failed probe - if: >- - steps.baseline.outputs.available != 'true' && - steps.initial_bootstrap_gate.outcome == 'failure' - run: >- - python3 benchmarks/bench_liblecore.py - --library "$CANDIDATE_BUILD/liblecore.so" - --dimensions 256,512,1024 - --profiles both - --repeats 10 - --target-work 64000000 - --output "$RETRY_PERFORMANCE_REPORT" - - - name: Enforce the confirmed candidate-versus-base policy - if: >- - steps.baseline.outputs.available == 'true' && - steps.initial_base_gate.outcome == 'failure' - run: >- - python3 benchmarks/check_liblecore_performance.py - --report "$RETRY_PERFORMANCE_REPORT" - --baseline "$RETRY_BASELINE_REPORT" - --policy benchmarks/liblecore_ci_policy.json - --summary "$GITHUB_STEP_SUMMARY" - - - name: Enforce the confirmed bootstrap invariant policy - if: >- - steps.baseline.outputs.available != 'true' && - steps.initial_bootstrap_gate.outcome == 'failure' - run: >- - python3 benchmarks/check_liblecore_performance.py - --report "$RETRY_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 @@ -442,8 +372,6 @@ jobs: path: | ${{ env.PERFORMANCE_REPORT }} ${{ env.BASELINE_REPORT }} - ${{ env.RETRY_PERFORMANCE_REPORT }} - ${{ env.RETRY_BASELINE_REPORT }} if-no-files-found: warn retention-days: 30 diff --git a/ENG.md b/ENG.md index ee4d6d0..676c59a 100644 --- a/ENG.md +++ b/ENG.md @@ -38,7 +38,7 @@ the backlog item's full cross-platform or adopter acceptance gate is complete. | 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 benchmark driver and `native/liblecore/BENCHMARKS.md` with setup, scratch, wins, losses, and numeric error | One local Apple-arm64 baseline exists. Cross-platform and adopter workload evidence is still required before changing `AUTO`. | +| 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. | @@ -326,7 +326,9 @@ status consistently. - 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. -- ABI-breaking changes require a new ABI major and shared-library SONAME. +- 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 @@ -578,12 +580,16 @@ 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 with explicit release notes; ABI `1` begins only at `LC-037`. -- After ABI `1`, `LECORE_ABI_VERSION` changes only for ABI breaks. +- 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. -- Major releases may break ABI and change SONAME. +- 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 @@ -703,8 +709,11 @@ clean locally but has not yet run on GitHub as of the dated snapshot. | 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. -Performance timings from noisy hosted runners are recorded artifacts, not merge gates. Semantic and memory-safety -failures are gates. +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 @@ -812,7 +821,7 @@ Priorities mean: |---|---| | 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-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 | @@ -860,7 +869,7 @@ consumer's private math. | 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 · 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-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. | @@ -869,6 +878,7 @@ consumer's private math. | 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 diff --git a/benchmarks/bench_liblecore.py b/benchmarks/bench_liblecore.py index 1861a81..bb26843 100644 --- a/benchmarks/bench_liblecore.py +++ b/benchmarks/bench_liblecore.py @@ -1,22 +1,27 @@ #!/usr/bin/env python3 """Measure liblecore's direct/radix regimes without changing AUTO policy. -The benchmark includes Python/ctypes call overhead but reuses output buffers, so it is a realistic lower-level -adapter measurement rather than a claim about one isolated FFT instruction. Results are descriptive evidence; -this tool never rewrites dispatch thresholds. +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 +from typing import Callable, Optional, Tuple +import uuid import numpy as np @@ -25,14 +30,33 @@ sys.path.insert(0, str(REPOSITORY_ROOT)) from bindings.python.lecore_native import Library # noqa: E402 -from holographic.agents_and_reasoning.holographic_ai import bind as numpy_bind # 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_ns(operation: Callable[[], None], iterations: int, repeats: int) -> float: - samples = [] +def timed_samples( + operation: Callable[[], object], iterations: int, repeats: int +) -> TimingSamples: + elapsed_samples = [] operation() was_enabled = gc.isenabled() gc.disable() @@ -41,25 +65,23 @@ def timed_ns(operation: Callable[[], None], iterations: int, repeats: int) -> fl started = time.perf_counter_ns() for _ in range(iterations): operation() - samples.append((time.perf_counter_ns() - started) / iterations) + elapsed_samples.append(time.perf_counter_ns() - started) finally: if was_enabled: gc.enable() - return float(statistics.median(samples)) + return TimingSamples(iterations=iterations, elapsed_ns=elapsed_samples) -def paired_timed_ns( - candidate_operation: Callable[[], None], - baseline_operation: Callable[[], None], +def paired_timed_samples( + candidate_operation: Callable[[], object], + baseline_operation: Callable[[], object], iterations: int, repeats: int, -) -> tuple[float, float]: - """Time two operations in alternating order to reduce ordering bias.""" +) -> Tuple[TimingSamples, TimingSamples]: + """Capture aligned candidate/base samples while alternating first position.""" - samples: tuple[list[float], list[float]] = ([], []) + samples: Tuple[list[int], list[int]] = ([], []) operations = (candidate_operation, baseline_operation) - candidate_operation() - baseline_operation() was_enabled = gc.isenabled() gc.disable() try: @@ -69,14 +91,143 @@ def paired_timed_ns( started = time.perf_counter_ns() for _ in range(iterations): operations[index]() - samples[index].append((time.perf_counter_ns() - started) / iterations) + samples[index].append(time.perf_counter_ns() - started) finally: if was_enabled: gc.enable() - return tuple(float(statistics.median(values)) for values in samples) + return ( + TimingSamples(iterations=iterations, elapsed_ns=samples[0]), + TimingSamples(iterations=iterations, elapsed_ns=samples[1]), + ) -def setup_ns(library: Library, dimension: int, profile: str, backend: str, repeats: int) -> float: +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() @@ -119,23 +270,24 @@ def dimension_inputs(dimension: int, profile: str) -> tuple[np.ndarray, np.ndarr def result_entry( dimension: int, profile: str, - direct_iterations: int, - optimized_iterations: int, - timings: dict[str, float], + timings: dict[str, TimingSamples], outputs: dict[str, np.ndarray], scratch: dict[str, int], - setup_timings: dict[str, float], + setup_timings: Optional[dict[str, float]], auto_backend: int, ) -> dict[str, object]: - return { + entry: dict[str, object] = { "dimension": dimension, "profile": profile, "auto_backend": auto_backend, - "direct_iterations": direct_iterations, - "optimized_iterations": optimized_iterations, - "direct_ns": timings["direct"], - "radix2_ns": timings["radix2"], - "radix2_speedup": timings["direct"] / timings["radix2"], + "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( @@ -146,9 +298,15 @@ def result_entry( ) ) ), - "direct_setup_ns": setup_timings["direct"], - "radix2_setup_ns": setup_timings["radix2"], } + 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( @@ -157,6 +315,8 @@ def benchmark_dimension( 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) @@ -164,32 +324,45 @@ def benchmark_dimension( 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, float] = {} + timings: dict[str, TimingSamples] = {} scratch: dict[str, int] = {} - for backend, iterations in (("direct", direct_iterations), ("radix2", optimized_iterations)): + 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) - def call_native() -> None: - context.bind(left, right, out=output) - - timings[backend] = timed_ns(call_native, iterations, repeats) + 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 = { - backend: setup_ns(library, dimension, profile, backend, repeats) - for backend in ("direct", "radix2") - } + 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, - direct_iterations, - optimized_iterations, timings, outputs, scratch, @@ -197,18 +370,22 @@ def call_native() -> None: auto_backend, ) - if profile == "f64": + 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_ns(call_numpy, optimized_iterations, repeats) + 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"], + "radix2_speedup_vs_numpy": numpy_time + / timings["radix2"].median_ns, "numpy_max_abs_vs_direct": float( np.max(np.abs(numpy_output - outputs["direct"])) ), @@ -224,6 +401,8 @@ def benchmark_dimension_pair( 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.""" @@ -232,12 +411,15 @@ def benchmark_dimension_pair( 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, float], dict[str, float]] = ({}, {}) + 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)): + for backend, iterations in ( + ("direct", direct_iterations), + ("radix2", optimized_iterations), + ): with candidate_library.context( dimension, profile=profile, backend=backend ) as candidate_context: @@ -247,35 +429,59 @@ def benchmark_dimension_pair( candidate_output = np.empty(dimension, dtype=dtype) baseline_output = np.empty(dimension, dtype=dtype) - def call_candidate() -> None: - candidate_context.bind(left, right, out=candidate_output) - - def call_baseline() -> None: - baseline_context.bind(left, right, out=baseline_output) - - candidate_ns, baseline_ns = paired_timed_ns( - call_candidate, - call_baseline, + 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, - repeats, + sample_target_ns, ) - timings[0][backend] = candidate_ns - timings[1][backend] = baseline_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 - 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 + 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: @@ -286,30 +492,32 @@ def call_baseline() -> None: result_entry( dimension, profile, - direct_iterations, - optimized_iterations, timings[index], outputs[index], scratch[index], - setup_timings[index], + setup_timings[index] if descriptive_metrics else None, auto_backends[index], ) for index in range(2) ) - if profile == "f64": + 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_ns(call_numpy, optimized_iterations, repeats) + 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"], + "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"])) ), @@ -320,8 +528,12 @@ def call_numpy() -> None: 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") + 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 @@ -330,17 +542,30 @@ def build_report( 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": 1, + "schema_version": 2, "policy_effect": "none; AUTO remains unchanged", - "benchmark": { - "repeats": repeats, - "target_work": target_work, - "minimum_optimized_iterations": MINIMUM_OPTIMIZED_ITERATIONS, - "comparison_mode": comparison_mode, - }, + "benchmark": benchmark, "library": { "path": library.path, "bytes": Path(library.path).stat().st_size, @@ -377,12 +602,24 @@ def print_paired_results( ) -> 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"{candidate['direct_ns'] / baseline['direct_ns']:>5.2f}x/" - f"{candidate['radix2_ns'] / baseline['radix2_ns']:.2f}x" + f"{statistics.median(direct_ratios):>5.2f}x/" + f"{statistics.median(radix2_ratios):.2f}x" ) @@ -399,11 +636,27 @@ def main() -> int: 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: - parser.error("repeats and target-work must be positive") + 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") @@ -411,6 +664,10 @@ def main() -> int: 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( @@ -419,6 +676,8 @@ def main() -> int: profile, arguments.repeats, arguments.target_work, + sample_target_ns, + descriptive_metrics, ) for profile in profiles for dimension in arguments.dimensions @@ -426,6 +685,7 @@ def main() -> int: comparison_mode = "single" else: baseline_library = Library(arguments.baseline_library) + pair_id = uuid.uuid4().hex results = [] baseline_results = [] for profile in profiles: @@ -437,16 +697,21 @@ def main() -> int: profile, arguments.repeats, arguments.target_work, + sample_target_ns, + descriptive_metrics, ) results.append(candidate_result) baseline_results.append(baseline_result) - comparison_mode = "interleaved" + 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( @@ -454,7 +719,10 @@ def main() -> int: 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: diff --git a/benchmarks/check_liblecore_performance.py b/benchmarks/check_liblecore_performance.py index 9b0e756..93749df 100644 --- a/benchmarks/check_liblecore_performance.py +++ b/benchmarks/check_liblecore_performance.py @@ -8,6 +8,7 @@ import json import math from pathlib import Path +import statistics import sys from typing import Any, Dict, Iterable, List, Mapping, Optional, Sequence, Tuple @@ -23,18 +24,20 @@ class Measurement: direct_ns: float radix2_ns: float speedup: float - minimum_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.speedup >= self.minimum_speedup + (self.minimum_speedup is None or self.speedup >= self.minimum_speedup) and self.max_abs_delta <= self.delta_limit and ( not self.baseline_required @@ -53,17 +56,18 @@ def passed(self) -> bool: ) ) - @property - def direct_slowdown(self) -> Optional[float]: - if self.baseline_direct_ns is None: - return None - return self.direct_ns / self.baseline_direct_ns - @property - def radix2_slowdown(self) -> Optional[float]: - if self.baseline_radix2_ns is None: - return None - return self.radix2_ns / self.baseline_radix2_ns + +@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) @@ -112,8 +116,12 @@ def _finite(value: Any, label: str, *, positive: bool = False) -> float: def _policy_regimes( policy: Mapping[str, Any], -) -> Tuple[Mapping[str, Any], float, List[Tuple[str, int, float, float]]]: - if _integer(policy.get("schema_version"), "policy.schema_version") != 1: +) -> 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( @@ -123,8 +131,20 @@ def _policy_regimes( ) 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, float, float]] = [] + 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(): @@ -137,26 +157,25 @@ def _policy_regimes( ) 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( - profile_policy.get("minimum_speedup"), - f"policy.profiles.{profile}.minimum_speedup", + raw_minimums, f"policy.profiles.{profile}.minimum_speedup" ) - if not minimums: - raise GateInputError(f"policy.profiles.{profile}.minimum_speedup must not be empty") - for raw_dimension, raw_minimum in minimums.items(): - try: - dimension = int(raw_dimension) - except (TypeError, ValueError) as error: - raise GateInputError( - f"policy dimension {raw_dimension!r} for {profile} is not an integer" - ) from error - if dimension <= 0 or dimension & (dimension - 1): - raise GateInputError(f"policy dimension {dimension} for {profile} must be a positive power of two") - minimum = _finite( - raw_minimum, - f"policy.profiles.{profile}.minimum_speedup.{dimension}", - positive=True, + 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 @@ -167,11 +186,14 @@ def _validate_metadata( policy: Mapping[str, Any], required_library: Mapping[str, Any], label: str, -) -> None: +) -> 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: + 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}" ) @@ -199,6 +221,19 @@ def _validate_metadata( ) 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 ) @@ -207,6 +242,37 @@ def _validate_metadata( 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( @@ -244,19 +310,55 @@ def _comparison_mode(report: Mapping[str, Any], label: str) -> str: 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, -) -> Tuple[float, float, float, float]: + 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" ) - _integer(result.get("direct_iterations"), f"{label}.direct_iterations", minimum=1) + 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 ) @@ -266,8 +368,38 @@ def _read_result( f"{minimum_optimized_iterations}" ) - direct_ns = _finite(result.get("direct_ns"), f"{label}.direct_ns", positive=True) - radix2_ns = _finite(result.get("radix2_ns"), f"{label}.radix2_ns", positive=True) + 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 ) @@ -282,7 +414,16 @@ def _read_result( ) if delta < 0.0: raise GateInputError(f"{label}.radix2_max_abs_vs_direct must be >= 0") - return direct_ns, radix2_ns, speedup, delta + 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( @@ -291,12 +432,18 @@ def evaluate( baseline: Optional[Mapping[str, Any]] = None, ) -> Evaluation: required_library, max_candidate_slowdown, regimes = _policy_regimes(policy) - _validate_metadata(report, policy, required_library, "report") + 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: - _validate_metadata(baseline, policy, required_library, "baseline") + 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: @@ -310,6 +457,12 @@ def evaluate( 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: @@ -331,6 +484,11 @@ def evaluate( "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] = [] @@ -341,48 +499,90 @@ def evaluate( continue result = indexed[key] label = f"report result {profile}/{dimension}" - direct_ns, radix2_ns, speedup, delta = _read_result( + 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}") + failures.append( + f"missing baseline result for {profile} dimension {dimension}" + ) else: - baseline_direct_ns, baseline_radix2_ns, _, _ = _read_result( + 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=direct_ns, - radix2_ns=radix2_ns, - speedup=speedup, + direct_ns=candidate_timing.direct_ns, + radix2_ns=candidate_timing.radix2_ns, + speedup=candidate_timing.speedup, minimum_speedup=minimum, - max_abs_delta=delta, + 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 speedup < minimum: + if minimum is not None and candidate_timing.speedup < minimum: failures.append( - f"{profile}/{dimension} radix-2 speedup {speedup:.2f}x is below {minimum:.2f}x" + f"{profile}/{dimension} radix-2 speedup " + f"{candidate_timing.speedup:.2f}x is below {minimum:.2f}x" ) - if delta > delta_limit: + if candidate_timing.max_abs_delta > delta_limit: failures.append( - f"{profile}/{dimension} max delta {delta:.3e} exceeds {delta_limit:.3e}" + f"{profile}/{dimension} max delta " + f"{candidate_timing.max_abs_delta:.3e} exceeds {delta_limit:.3e}" ) if ( measurement.direct_slowdown is not None @@ -428,10 +628,12 @@ def render_markdown( library = report.get("library") if isinstance(report.get("library"), dict) else {} status = "PASS" if not failures else "FAIL" comparison = ( - f"Candidate and base latency are interleaved on this runner; at most " - f"{max_candidate_slowdown:.2f}x slowdown is allowed per backend." + 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." + else "Bootstrap mode: the base commit has no benchmarkable liblecore, so " + "only same-build invariants apply." ) lines = [ "## liblecore performance regression", @@ -443,16 +645,26 @@ def render_markdown( f"liblecore `{library.get('version', 'unknown')}` (ABI `{library.get('abi', 'unknown')}`, " f"ISA `{library.get('isa', 'unknown')}`).", "", - "| Profile | Dimension | Direct (us) | Radix-2 (us) | Speedup | Required | Direct/base | Radix/base | Max delta | Limit | Result |", + "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"{item.minimum_speedup:.2f}x | {direct_base} | {radix_base} | " + f"{speedup_floor} | {direct_base} | {radix_base} | " f"{item.max_abs_delta:.3e} | " f"{item.delta_limit:.3e} | {'PASS' if item.passed else 'FAIL'} |" ) diff --git a/benchmarks/liblecore_ci_policy.json b/benchmarks/liblecore_ci_policy.json index 9185a7c..649fcac 100644 --- a/benchmarks/liblecore_ci_policy.json +++ b/benchmarks/liblecore_ci_policy.json @@ -1,12 +1,18 @@ { - "schema_version": 1, - "report_schema_version": 1, + "schema_version": 2, + "report_schema_version": 2, "required_policy_effect": "none; AUTO remains unchanged", - "required_baseline_comparison_mode": "interleaved", + "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, @@ -15,20 +21,10 @@ }, "profiles": { "f64": { - "max_abs_delta": 1e-12, - "minimum_speedup": { - "256": 2.0, - "512": 5.0, - "1024": 10.0 - } + "max_abs_delta": 1e-12 }, "f32": { - "max_abs_delta": 1e-4, - "minimum_speedup": { - "256": 2.0, - "512": 5.0, - "1024": 10.0 - } + "max_abs_delta": 1e-4 } } } diff --git a/native/liblecore/BENCHMARKS.md b/native/liblecore/BENCHMARKS.md index 632eb43..1915e1c 100644 --- a/native/liblecore/BENCHMARKS.md +++ b/native/liblecore/BENCHMARKS.md @@ -4,13 +4,13 @@ These measurements characterize the ABI-0 preview; they do not define the API or 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 portable-backend baseline +## 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 -benchmark reuses contexts and output buffers; as a Python `ctypes` driver, it includes foreign-function call -overhead. Inputs are deterministic unit vectors. Direct-backend iteration counts are reduced at large dimensions -to keep the run bounded. +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 @@ -46,6 +46,26 @@ The portable radix-2 implementation clearly improves on the quadratic oracle at 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: @@ -56,52 +76,68 @@ python3 benchmarks/bench_liblecore.py \ --output /tmp/liblecore-benchmark.json ``` -The JSON report captures host and library metadata, setup cost, scratch requirements, iteration counts, timings, -and direct-versus-radix numerical error. +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 both f64 and f32 bind at dimensions 256, -512, and 1024. Each reported latency is the median of ten samples; the target work is raised to 64,000,000 so -each sample contains enough calls to reduce timer and foreign-function-interface noise. The radix-2 timing loop -always executes at least 4,000 calls per sample, including dimension 1024. On the original Apple baseline this -puts the guarded direct and radix-2 samples in roughly the 30–80 millisecond range. +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 and measures each guarded cell -as an interleaved pair: base and candidate alternate which runs first on every repeat, with identical inputs, -iteration counts, Python environment, dimensions, and work target. The committed policy permits at most a 1.35x -candidate slowdown against that same-run base for both direct and radix-2 latency in every guarded -profile/dimension. Interleaving controls short-term frequency and scheduling drift far better than running two -complete reports in sequence; it does not pretend that a hosted runner is perfectly isolated. - -If the first comparison fails, the workflow repeats the interleaved pair once and applies the same policy to that -confirmation report. Only a failure that persists in the confirmation run blocks the job. This single retry -handles a transient scheduled-runner interruption without averaging it into the score, while a repeatable -slowdown or malformed report still fails. Both initial and confirmation reports are retained. +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 gate also evaluates relative invariants within the candidate report. It checks that: +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; -- radix-2 remains numerically close to the direct oracle within the profile-specific error limit; -- radix-2 keeps a conservative minimum speedup over direct at the guarded dimensions; -- report metadata confirms the expected schema, ABI, ISA, capabilities, and interleaved comparison mode; and -- an actual `LECORE_BACKEND_AUTO` context resolves to direct for every measured profile and dimension. +- 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. -For both profiles the minimum same-run speedups are 2.0x at dimension 256, 5.0x at 512, and 10.0x at 1024. -The maximum direct-versus-radix absolute delta is `1e-12` for f64 and `1e-4` for f32. These floors sit well below -the recorded baseline ratios, but still fail if radix-2 falls back to quadratic work or suffers a major relative -regression. The conformance jobs retain the tighter, operation-wide numerical contract. +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 invariant checks without `--baseline`. Once liblecore is present on the base branch, pull requests +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 the measured score table and every applicable policy result 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 +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. @@ -124,6 +160,8 @@ python3 benchmarks/bench_liblecore.py \ --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 \ diff --git a/native/liblecore/CMakeLists.txt b/native/liblecore/CMakeLists.txt index ce1e55f..0c4f8fa 100644 --- a/native/liblecore/CMakeLists.txt +++ b/native/liblecore/CMakeLists.txt @@ -92,6 +92,16 @@ else() 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 @@ -188,7 +198,7 @@ set_target_properties(lecore PROPERTIES EXPORT_NAME lecore OUTPUT_NAME lecore POSITION_INDEPENDENT_CODE ON - SOVERSION "${LECORE_ABI_VERSION}" + SOVERSION "${LECORE_SOVERSION}" VERSION "${PROJECT_VERSION}" VISIBILITY_INLINES_HIDDEN YES ) diff --git a/native/liblecore/README.md b/native/liblecore/README.md index 266f125..0daf08b 100644 --- a/native/liblecore/README.md +++ b/native/liblecore/README.md @@ -22,7 +22,10 @@ The current preview provides: 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`. +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 diff --git a/native/liblecore/amalgamation/lecore.c b/native/liblecore/amalgamation/lecore.c index 9584a2e..7dae006 100644 --- a/native/liblecore/amalgamation/lecore.c +++ b/native/liblecore/amalgamation/lecore.c @@ -3,7 +3,7 @@ * * liblecore 0.1.0 amalgamation (ABI 0, ISA 1) * Generated by tools/generate_liblecore_amalgamation.py; DO NOT EDIT. - * Canonical-source SHA-256: 5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55 + * Canonical-source SHA-256: 04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269 * * This is a mechanical distribution of the clean-room canonical sources. * Provenance details live in native/liblecore/PROVENANCE.md. @@ -156,6 +156,22 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( 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, @@ -166,6 +182,22 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( 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( @@ -835,6 +867,29 @@ static float lecore_f32_cosine_raw( 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, @@ -1036,8 +1091,11 @@ lecore_status LECORE_CALL lecore_cosine_many_f32( { 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; @@ -1063,9 +1121,17 @@ lecore_status LECORE_CALL lecore_cosine_many_f32( 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_raw( - query, rows + row * row_stride, context->dimension); + 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; } @@ -1302,6 +1368,29 @@ static double lecore_f64_cosine_raw( 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, @@ -1503,8 +1592,11 @@ lecore_status LECORE_CALL lecore_cosine_many_f64( { 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; @@ -1530,9 +1622,17 @@ lecore_status LECORE_CALL lecore_cosine_many_f64( 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_raw( - query, rows + row * row_stride, context->dimension); + 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; } @@ -1801,16 +1901,17 @@ static int lecore_cleanup_f32_vector_is_finite( static float lecore_cleanup_f32_cosine( const float *query, + float query_norm_squared, + float query_norm, const float *candidate, uint32_t dimension) { - float query_norm_squared = 0.0f; float candidate_norm_squared = 0.0f; + float candidate_norm; float dot = 0.0f; uint32_t index; for (index = 0; index < dimension; ++index) { - query_norm_squared += query[index] * query[index]; candidate_norm_squared += candidate[index] * candidate[index]; } if (query_norm_squared == 0.0f || candidate_norm_squared == 0.0f) { @@ -1819,8 +1920,8 @@ static float lecore_cleanup_f32_cosine( for (index = 0; index < dimension; ++index) { dot += query[index] * candidate[index]; } - return dot / - (sqrtf(query_norm_squared) * sqrtf(candidate_norm_squared)); + candidate_norm = sqrtf(candidate_norm_squared); + return dot / (query_norm * candidate_norm); } lecore_status LECORE_CALL lecore_cleanup_f32( @@ -1838,6 +1939,9 @@ lecore_status LECORE_CALL lecore_cleanup_f32( 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; @@ -1875,13 +1979,23 @@ lecore_status LECORE_CALL lecore_cleanup_f32( } } + 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, candidates, context->dimension); + 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)) { @@ -1922,16 +2036,17 @@ static int lecore_cleanup_f64_vector_is_finite( static double lecore_cleanup_f64_cosine( const double *query, + double query_norm_squared, + double query_norm, const double *candidate, uint32_t dimension) { - double query_norm_squared = 0.0; double candidate_norm_squared = 0.0; + double candidate_norm; double dot = 0.0; uint32_t index; for (index = 0; index < dimension; ++index) { - query_norm_squared += query[index] * query[index]; candidate_norm_squared += candidate[index] * candidate[index]; } if (query_norm_squared == 0.0 || candidate_norm_squared == 0.0) { @@ -1940,8 +2055,8 @@ static double lecore_cleanup_f64_cosine( for (index = 0; index < dimension; ++index) { dot += query[index] * candidate[index]; } - return dot / - (sqrt(query_norm_squared) * sqrt(candidate_norm_squared)); + candidate_norm = sqrt(candidate_norm_squared); + return dot / (query_norm * candidate_norm); } lecore_status LECORE_CALL lecore_cleanup_f64( @@ -1959,6 +2074,9 @@ lecore_status LECORE_CALL lecore_cleanup_f64( 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; @@ -1996,15 +2114,25 @@ lecore_status LECORE_CALL lecore_cleanup_f64( } } + 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, candidates, context->dimension); + 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)) { @@ -2070,6 +2198,29 @@ static void lecore_hrr_bind_f32_raw( 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; @@ -2092,6 +2243,27 @@ static void lecore_hrr_unbind_f32_raw( 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; @@ -2351,12 +2523,25 @@ lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( return LECORE_ENONFINITE; } - for (row = 0; row < row_count; ++row) { - lecore_hrr_bind_f32_selected( +#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); + out_rows + row * out_stride, + context->dimension); } return LECORE_OK; } @@ -2415,12 +2600,25 @@ lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( return LECORE_ENONFINITE; } - for (key = 0; key < key_count; ++key) { - lecore_hrr_unbind_f32_selected( +#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); + out_rows + key * out_stride, + context->dimension); } return LECORE_OK; } @@ -2471,6 +2669,32 @@ static void lecore_hrr_bind_f64_raw( 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; @@ -2493,6 +2717,28 @@ static void lecore_hrr_unbind_f64_raw( 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; @@ -2753,12 +2999,25 @@ lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( return LECORE_ENONFINITE; } - for (row = 0; row < row_count; ++row) { - lecore_hrr_bind_f64_selected( +#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); + out_rows + row * out_stride, + context->dimension); } return LECORE_OK; } @@ -2817,12 +3076,25 @@ lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( return LECORE_ENONFINITE; } - for (key = 0; key < key_count; ++key) { - lecore_hrr_unbind_f64_selected( +#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); + out_rows + key * out_stride, + context->dimension); } return LECORE_OK; } @@ -2835,22 +3107,25 @@ lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( static void lecore_radix2_fft_f32( const lecore_context *context, float *values, - int inverse) + int inverse, + int input_is_bit_reversed) { const uint32_t dimension = context->dimension; const float *twiddles = (const float *)context->radix2_twiddles; uint32_t index; - 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 (!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; + } } } @@ -2915,19 +3190,53 @@ static void lecore_radix2_fft_f32( } static void lecore_radix2_load_f32( + const lecore_context *context, float *destination, const float *source, - uint32_t dimension, int involute) { uint32_t index; - for (index = 0; index < dimension; ++index) { + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; uint32_t source_index = involute && index != 0 - ? dimension - index + ? context->dimension - index : index; - destination[(size_t)index * 2] = source[source_index]; - destination[(size_t)index * 2 + 1] = 0.0f; + 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]; } } @@ -2940,30 +3249,46 @@ static void lecore_radix2_convolve_f32( { float *left = (float *)context->scratch; float *right = left + (size_t)context->dimension * 2; - uint32_t index; - lecore_radix2_load_f32( - left, left_input, context->dimension, 0); + context, left, left_input, 0); lecore_radix2_load_f32( - right, right_input, context->dimension, involute_right); - lecore_radix2_fft_f32(context, left, 0); - lecore_radix2_fft_f32(context, right, 0); - - for (index = 0; index < context->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]; + 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); +} - left[(size_t)index * 2] = - left_real * right_real - left_imaginary * right_imaginary; - left[(size_t)index * 2 + 1] = - left_real * right_imaginary + left_imaginary * right_real; - } +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_fft_f32(context, left, 1); - for (index = 0; index < context->dimension; ++index) { - output[index] = left[(size_t)index * 2]; + 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); } } @@ -2985,6 +3310,39 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( 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" @@ -2995,22 +3353,25 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( static void lecore_radix2_fft_f64( const lecore_context *context, double *values, - int inverse) + int inverse, + int input_is_bit_reversed) { const uint32_t dimension = context->dimension; const double *twiddles = (const double *)context->radix2_twiddles; uint32_t index; - 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 (!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; + } } } @@ -3075,19 +3436,53 @@ static void lecore_radix2_fft_f64( } static void lecore_radix2_load_f64( + const lecore_context *context, double *destination, const double *source, - uint32_t dimension, int involute) { uint32_t index; - for (index = 0; index < dimension; ++index) { + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; uint32_t source_index = involute && index != 0 - ? dimension - index + ? context->dimension - index : index; - destination[(size_t)index * 2] = source[source_index]; - destination[(size_t)index * 2 + 1] = 0.0; + 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]; } } @@ -3100,30 +3495,46 @@ static void lecore_radix2_convolve_f64( { double *left = (double *)context->scratch; double *right = left + (size_t)context->dimension * 2; - uint32_t index; - lecore_radix2_load_f64( - left, left_input, context->dimension, 0); + context, left, left_input, 0); lecore_radix2_load_f64( - right, right_input, context->dimension, involute_right); - lecore_radix2_fft_f64(context, left, 0); - lecore_radix2_fft_f64(context, right, 0); - - for (index = 0; index < context->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]; + 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); +} - left[(size_t)index * 2] = - left_real * right_real - left_imaginary * right_imaginary; - left[(size_t)index * 2 + 1] = - left_real * right_imaginary + left_imaginary * right_real; - } +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_fft_f64(context, left, 1); - for (index = 0; index < context->dimension; ++index) { - output[index] = left[(size_t)index * 2]; + 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); } } @@ -3145,6 +3556,39 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( 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 diff --git a/native/liblecore/amalgamation/lecore.h b/native/liblecore/amalgamation/lecore.h index 3f5d4e0..1ba9f07 100644 --- a/native/liblecore/amalgamation/lecore.h +++ b/native/liblecore/amalgamation/lecore.h @@ -3,7 +3,7 @@ * * liblecore 0.1.0 amalgamation (ABI 0, ISA 1) * Generated by tools/generate_liblecore_amalgamation.py; DO NOT EDIT. - * Canonical-source SHA-256: 5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55 + * Canonical-source SHA-256: 04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269 * * This is a mechanical distribution of the clean-room canonical sources. * Provenance details live in native/liblecore/PROVENANCE.md. @@ -56,7 +56,7 @@ #define LECORE_AMALGAMATION_H #define LECORE_AMALGAMATION 1 -#define LECORE_AMALGAMATION_SOURCE_SHA256 "5da2817e8f2addcc15d3a97c17107c22289bb2609bbdd19f2c199d33238a5a55" +#define LECORE_AMALGAMATION_SOURCE_SHA256 "04420f63de78b82e9163c584ff36b9b6f3bed3faf65ddb65675b6d5a07faf269" #ifndef LECORE_VERSION_STRING_VALUE diff --git a/native/liblecore/src/cleanup_f32.c b/native/liblecore/src/cleanup_f32.c index cc4efb9..267b714 100644 --- a/native/liblecore/src/cleanup_f32.c +++ b/native/liblecore/src/cleanup_f32.c @@ -18,16 +18,17 @@ static int lecore_cleanup_f32_vector_is_finite( static float lecore_cleanup_f32_cosine( const float *query, + float query_norm_squared, + float query_norm, const float *candidate, uint32_t dimension) { - float query_norm_squared = 0.0f; float candidate_norm_squared = 0.0f; + float candidate_norm; float dot = 0.0f; uint32_t index; for (index = 0; index < dimension; ++index) { - query_norm_squared += query[index] * query[index]; candidate_norm_squared += candidate[index] * candidate[index]; } if (query_norm_squared == 0.0f || candidate_norm_squared == 0.0f) { @@ -36,8 +37,8 @@ static float lecore_cleanup_f32_cosine( for (index = 0; index < dimension; ++index) { dot += query[index] * candidate[index]; } - return dot / - (sqrtf(query_norm_squared) * sqrtf(candidate_norm_squared)); + candidate_norm = sqrtf(candidate_norm_squared); + return dot / (query_norm * candidate_norm); } lecore_status LECORE_CALL lecore_cleanup_f32( @@ -55,6 +56,9 @@ lecore_status LECORE_CALL lecore_cleanup_f32( 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; @@ -92,13 +96,23 @@ lecore_status LECORE_CALL lecore_cleanup_f32( } } + 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, candidates, context->dimension); + 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)) { diff --git a/native/liblecore/src/cleanup_f64.c b/native/liblecore/src/cleanup_f64.c index 18e09e8..73ac06b 100644 --- a/native/liblecore/src/cleanup_f64.c +++ b/native/liblecore/src/cleanup_f64.c @@ -18,16 +18,17 @@ static int lecore_cleanup_f64_vector_is_finite( static double lecore_cleanup_f64_cosine( const double *query, + double query_norm_squared, + double query_norm, const double *candidate, uint32_t dimension) { - double query_norm_squared = 0.0; double candidate_norm_squared = 0.0; + double candidate_norm; double dot = 0.0; uint32_t index; for (index = 0; index < dimension; ++index) { - query_norm_squared += query[index] * query[index]; candidate_norm_squared += candidate[index] * candidate[index]; } if (query_norm_squared == 0.0 || candidate_norm_squared == 0.0) { @@ -36,8 +37,8 @@ static double lecore_cleanup_f64_cosine( for (index = 0; index < dimension; ++index) { dot += query[index] * candidate[index]; } - return dot / - (sqrt(query_norm_squared) * sqrt(candidate_norm_squared)); + candidate_norm = sqrt(candidate_norm_squared); + return dot / (query_norm * candidate_norm); } lecore_status LECORE_CALL lecore_cleanup_f64( @@ -55,6 +56,9 @@ lecore_status LECORE_CALL lecore_cleanup_f64( 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; @@ -92,15 +96,25 @@ lecore_status LECORE_CALL lecore_cleanup_f64( } } + 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, candidates, context->dimension); + 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)) { diff --git a/native/liblecore/src/hrr_direct_f32.c b/native/liblecore/src/hrr_direct_f32.c index 9cf0b50..0f23566 100644 --- a/native/liblecore/src/hrr_direct_f32.c +++ b/native/liblecore/src/hrr_direct_f32.c @@ -43,6 +43,29 @@ static void lecore_hrr_bind_f32_raw( 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; @@ -65,6 +88,27 @@ static void lecore_hrr_unbind_f32_raw( 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; @@ -324,12 +368,25 @@ lecore_status LECORE_CALL lecore_hrr_bind_fixed_f32( return LECORE_ENONFINITE; } - for (row = 0; row < row_count; ++row) { - lecore_hrr_bind_f32_selected( +#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); + out_rows + row * out_stride, + context->dimension); } return LECORE_OK; } @@ -388,12 +445,25 @@ lecore_status LECORE_CALL lecore_hrr_unbind_all_f32( return LECORE_ENONFINITE; } - for (key = 0; key < key_count; ++key) { - lecore_hrr_unbind_f32_selected( +#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); + 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 index efb64be..e630351 100644 --- a/native/liblecore/src/hrr_direct_f64.c +++ b/native/liblecore/src/hrr_direct_f64.c @@ -43,6 +43,32 @@ static void lecore_hrr_bind_f64_raw( 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; @@ -65,6 +91,28 @@ static void lecore_hrr_unbind_f64_raw( 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; @@ -325,12 +373,25 @@ lecore_status LECORE_CALL lecore_hrr_bind_fixed_f64( return LECORE_ENONFINITE; } - for (row = 0; row < row_count; ++row) { - lecore_hrr_bind_f64_selected( +#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); + out_rows + row * out_stride, + context->dimension); } return LECORE_OK; } @@ -389,12 +450,25 @@ lecore_status LECORE_CALL lecore_hrr_unbind_all_f64( return LECORE_ENONFINITE; } - for (key = 0; key < key_count; ++key) { - lecore_hrr_unbind_f64_selected( +#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); + 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 index a94d523..b946c3b 100644 --- a/native/liblecore/src/hrr_radix2_f32.c +++ b/native/liblecore/src/hrr_radix2_f32.c @@ -5,22 +5,25 @@ static void lecore_radix2_fft_f32( const lecore_context *context, float *values, - int inverse) + int inverse, + int input_is_bit_reversed) { const uint32_t dimension = context->dimension; const float *twiddles = (const float *)context->radix2_twiddles; uint32_t index; - 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 (!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; + } } } @@ -85,55 +88,105 @@ static void lecore_radix2_fft_f32( } static void lecore_radix2_load_f32( + const lecore_context *context, float *destination, const float *source, - uint32_t dimension, int involute) { uint32_t index; - for (index = 0; index < dimension; ++index) { + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; uint32_t source_index = involute && index != 0 - ? dimension - index + ? context->dimension - index : index; - destination[(size_t)index * 2] = source[source_index]; - destination[(size_t)index * 2 + 1] = 0.0f; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0f; } } -static void lecore_radix2_convolve_f32( - lecore_context *context, - const float *left_input, - const float *right_input, - int involute_right, - float *output) +static void lecore_radix2_multiply_f32( + float *destination, + const float *left, + const float *right, + uint32_t dimension) { - float *left = (float *)context->scratch; - float *right = left + (size_t)context->dimension * 2; uint32_t index; - lecore_radix2_load_f32( - left, left_input, context->dimension, 0); - lecore_radix2_load_f32( - right, right_input, context->dimension, involute_right); - lecore_radix2_fft_f32(context, left, 0); - lecore_radix2_fft_f32(context, right, 0); - - for (index = 0; index < context->dimension; ++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]; - left[(size_t)index * 2] = + destination[(size_t)index * 2] = left_real * right_real - left_imaginary * right_imaginary; - left[(size_t)index * 2 + 1] = + destination[(size_t)index * 2 + 1] = left_real * right_imaginary + left_imaginary * right_real; } +} - lecore_radix2_fft_f32(context, left, 1); - for (index = 0; index < context->dimension; ++index) { - output[index] = left[(size_t)index * 2]; +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); } } @@ -155,4 +208,37 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( 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 index 965ca50..644bf70 100644 --- a/native/liblecore/src/hrr_radix2_f64.c +++ b/native/liblecore/src/hrr_radix2_f64.c @@ -5,22 +5,25 @@ static void lecore_radix2_fft_f64( const lecore_context *context, double *values, - int inverse) + int inverse, + int input_is_bit_reversed) { const uint32_t dimension = context->dimension; const double *twiddles = (const double *)context->radix2_twiddles; uint32_t index; - 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 (!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; + } } } @@ -85,55 +88,105 @@ static void lecore_radix2_fft_f64( } static void lecore_radix2_load_f64( + const lecore_context *context, double *destination, const double *source, - uint32_t dimension, int involute) { uint32_t index; - for (index = 0; index < dimension; ++index) { + for (index = 0; index < context->dimension; ++index) { + const uint32_t reversed = context->radix2_bit_reversal[index]; uint32_t source_index = involute && index != 0 - ? dimension - index + ? context->dimension - index : index; - destination[(size_t)index * 2] = source[source_index]; - destination[(size_t)index * 2 + 1] = 0.0; + destination[(size_t)reversed * 2] = source[source_index]; + destination[(size_t)reversed * 2 + 1] = 0.0; } } -static void lecore_radix2_convolve_f64( - lecore_context *context, - const double *left_input, - const double *right_input, - int involute_right, - double *output) +static void lecore_radix2_multiply_f64( + double *destination, + const double *left, + const double *right, + uint32_t dimension) { - double *left = (double *)context->scratch; - double *right = left + (size_t)context->dimension * 2; uint32_t index; - lecore_radix2_load_f64( - left, left_input, context->dimension, 0); - lecore_radix2_load_f64( - right, right_input, context->dimension, involute_right); - lecore_radix2_fft_f64(context, left, 0); - lecore_radix2_fft_f64(context, right, 0); - - for (index = 0; index < context->dimension; ++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]; - left[(size_t)index * 2] = + destination[(size_t)index * 2] = left_real * right_real - left_imaginary * right_imaginary; - left[(size_t)index * 2 + 1] = + destination[(size_t)index * 2 + 1] = left_real * right_imaginary + left_imaginary * right_real; } +} - lecore_radix2_fft_f64(context, left, 1); - for (index = 0; index < context->dimension; ++index) { - output[index] = left[(size_t)index * 2]; +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); } } @@ -155,4 +208,37 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( 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/lecore_internal.h b/native/liblecore/src/internal/lecore_internal.h index 9759d78..55bab3f 100644 --- a/native/liblecore/src/internal/lecore_internal.h +++ b/native/liblecore/src/internal/lecore_internal.h @@ -96,6 +96,22 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f64( 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, @@ -106,6 +122,22 @@ LECORE_INTERNAL_API void lecore_internal_hrr_radix2_unbind_f32( 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( diff --git a/native/liblecore/src/vector_f32.c b/native/liblecore/src/vector_f32.c index 8cb99c8..ec1d60f 100644 --- a/native/liblecore/src/vector_f32.c +++ b/native/liblecore/src/vector_f32.c @@ -71,6 +71,29 @@ static float lecore_f32_cosine_raw( 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, @@ -272,8 +295,11 @@ lecore_status LECORE_CALL lecore_cosine_many_f32( { 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; @@ -299,9 +325,17 @@ lecore_status LECORE_CALL lecore_cosine_many_f32( 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_raw( - query, rows + row * row_stride, context->dimension); + 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; } diff --git a/native/liblecore/src/vector_f64.c b/native/liblecore/src/vector_f64.c index 2f0a00e..617584d 100644 --- a/native/liblecore/src/vector_f64.c +++ b/native/liblecore/src/vector_f64.c @@ -73,6 +73,29 @@ static double lecore_f64_cosine_raw( 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, @@ -274,8 +297,11 @@ lecore_status LECORE_CALL lecore_cosine_many_f64( { 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; @@ -301,9 +327,17 @@ lecore_status LECORE_CALL lecore_cosine_many_f64( 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_raw( - query, rows + row * row_stride, context->dimension); + 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; } diff --git a/native/liblecore/tests/c/test_edges.c b/native/liblecore/tests/c/test_edges.c index ab6b87a..9a304f8 100644 --- a/native/liblecore/tests/c/test_edges.c +++ b/native/liblecore/tests/c/test_edges.c @@ -19,6 +19,7 @@ static int test_validation_modes(void) 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; @@ -48,6 +49,18 @@ static int test_validation_modes(void) 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; diff --git a/native/liblecore/tests/c/test_numeric.c b/native/liblecore/tests/c/test_numeric.c index c12c3cf..2c838ac 100644 --- a/native/liblecore/tests/c/test_numeric.c +++ b/native/liblecore/tests/c/test_numeric.c @@ -36,6 +36,161 @@ static int check_vector_f32( 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( @@ -85,6 +240,14 @@ static int test_f64_dense(void) 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)); @@ -196,6 +359,7 @@ static int test_f32_and_mixed(void) 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; @@ -207,6 +371,15 @@ static int test_f32_and_mixed(void) 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)); @@ -309,6 +482,10 @@ static int test_forced_radix2(void) 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)); @@ -316,6 +493,10 @@ static int test_forced_radix2(void) 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)); @@ -364,6 +545,12 @@ static int test_forced_radix2(void) 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( @@ -372,6 +559,15 @@ static int test_forced_radix2(void) 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, @@ -457,6 +653,7 @@ static int test_radix2_matches_direct_across_dimensions(void) 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); diff --git a/tests/native/test_liblecore_performance_gate.py b/tests/native/test_liblecore_performance_gate.py index 02d1b75..ffa31a9 100644 --- a/tests/native/test_liblecore_performance_gate.py +++ b/tests/native/test_liblecore_performance_gate.py @@ -9,6 +9,7 @@ import json import math from pathlib import Path +import statistics import sys import tempfile import unittest @@ -17,19 +18,32 @@ 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": 1, - "report_schema_version": 1, + "schema_version": 2, + "report_schema_version": 2, "required_policy_effect": "none; AUTO remains unchanged", - "required_baseline_comparison_mode": "interleaved", + "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, @@ -43,6 +57,25 @@ def policy(): } +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)): @@ -51,68 +84,199 @@ def report(comparison_mode="single"): "profile": profile, "dimension": 256, "auto_backend": 1, - "direct_iterations": 244, + "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": 1, + "schema_version": 2, "policy_effect": "none; AUTO remains unchanged", - "benchmark": { - "comparison_mode": comparison_mode, - "repeats": 10, - "target_work": 64_000_000, - "minimum_optimized_iterations": 4000, - }, + "benchmark": benchmark, "library": {"version": "0.1.0", "abi": 0, "isa": 1, "capabilities": 255}, - "host": {"platform": "test", "machine": "test", "python": "3.9", "numpy": "test"}, + "host": { + "platform": "test", + "machine": "test", + "python": "3.9", + "numpy": "test", + }, "results": results, } class PerformanceGateTests(unittest.TestCase): - def test_valid_report_passes_and_renders_scores(self): + 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_baseline_passes_and_is_rendered(self): - candidate = report("interleaved") - baseline = report("interleaved") - candidate["results"][0]["direct_ns"] = 42_000.0 - candidate["results"][0]["radix2_ns"] = 8_400.0 + 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("interleaved") - baseline = report("interleaved") + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") for entry in candidate["results"]: - entry["direct_ns"] *= 1.5 - entry["radix2_ns"] *= 1.5 + _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_speed_regression_fails(self): + 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]["radix2_ns"] = 30_000.0 - candidate["results"][0]["radix2_speedup"] = 4.0 / 3.0 + 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 @@ -128,8 +292,8 @@ def test_missing_regime_fails(self): self.assertIn("missing result for f32 dimension 256", result.failures) def test_missing_baseline_regime_fails_the_rendered_row(self): - candidate = report("interleaved") - baseline = report("interleaved") + candidate = report("paired-interleaved") + baseline = report("paired-interleaved") baseline["results"].pop(0) result = evaluate(candidate, policy(), baseline) self.assertFalse(result.passed) @@ -141,9 +305,9 @@ def test_sequential_reports_cannot_claim_a_baseline_comparison(self): with self.assertRaisesRegex(GateInputError, "comparison_mode"): evaluate(report(), policy(), report()) - def test_interleaved_report_cannot_claim_bootstrap_mode(self): + def test_paired_report_cannot_claim_bootstrap_mode(self): with self.assertRaisesRegex(GateInputError, "comparison_mode"): - evaluate(report("interleaved"), policy()) + evaluate(report("paired-interleaved"), policy()) def test_duplicate_regime_is_rejected(self): candidate = report() @@ -163,8 +327,8 @@ def test_nonfinite_and_inconsistent_values_are_rejected(self): with self.assertRaisesRegex(GateInputError, message): evaluate(candidate, policy()) candidate = report() - candidate["results"][0]["radix2_speedup"] = 99.0 - with self.assertRaisesRegex(GateInputError, "inconsistent"): + candidate["results"][0]["direct_ns"] = 99.0 + with self.assertRaisesRegex(GateInputError, "elapsed samples"): evaluate(candidate, policy()) def test_metadata_change_is_rejected(self): @@ -173,6 +337,14 @@ def test_metadata_change_is_rejected(self): 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 @@ -181,8 +353,7 @@ def test_actual_auto_backend_change_is_rejected(self): def test_cli_failure_is_nonzero_and_publishes_summary(self): candidate = report() - candidate["results"][0]["radix2_ns"] = 30_000.0 - candidate["results"][0]["radix2_speedup"] = 4.0 / 3.0 + _set_latency(candidate["results"][0], "radix2", 30_000.0) with tempfile.TemporaryDirectory() as directory: root = Path(directory) report_path = root / "report.json"