From 97a58fcdcf90dee84893cd6b09f5f728a0347a43 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 13:35:33 +0000 Subject: [PATCH 1/6] rational M4: the chains and the coverage matrix, measured MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chain.h: basic_chain over DspTap's chain<> (its flush landed as tap/DspTap#52; the submodule pin follows), constructing each stage at the profile relaxed by the stage's design divisor — its lower rate over the chain's lowest, the largest 2^a 3^b at or below it, or the quotient itself below 1 (the stage at bridge's rate under a 48-family r_min, 147/160) — computed at compile time; chain / chain_q15 / chain_q31; macs_per_output() exact; the 20 named multi-stage chains of the matrix. design.h: profile::relaxed(divisor) and the relaxation tables, the pinned m per band at every divisor the matrix uses, found by the same search as the M2 pins and verified against the shipping designer by test_design. The named ratios move from the umbrella to ratio.h (chain.h needs them). tools/coverage/matrix.py replaces the harris estimates with the numpy search on the 16384-point grid per (profile, band, divisor), costs every stage as stage.h trims its rows, and re-runs the plan's selection rules over all 182 pairs; it writes tests/coverage/matrix_rows.h (every row's stages, divisors and exact MACs / latency per profile) and prints the plan's tables, the relaxation tables, the rows that changed and the rows where the fewest-stages rule bit. 35 rows changed chain against the estimates (the plan's S1 ledger): a relaxed half-band saturates at m = 7 while a relaxed third-band stage is m = 5, so third-band, 4th-band and mixed stages win where the transition is wide. test_matrix.cpp builds every row in double (the cross rows through the bridge adapter in tests/support, 4.2), pins MACs per output and latency exactly at all four profiles, checks the within-family rows are the named chains, measures each row against its promise with a tone battery at exact bins (every image and alias candidate at or below f_pass is at or below -A; worst -71.3 dB at economy, -121.6 at transparent; passband within 0.009 dB), and checks accounting, the impulse and flush. Nothing is skipped: bridge's rate scale (2.2) had landed, so the k > 0 rows are measured too. test_chain.cpp: a chain equals its stages in sequence bit for bit in every format, sequenced-upfirdn vectors for seven chains, accounting from every position of every named chain, chunking, reset, flush as zero padding, latency, MACs, DC, channels. Plan v0.7: section 3 regenerated from the measured counts, the design divisor in 3.1, the fewest-stages list, the C ABI's 28 named chains (the union v0.2 counted as 34), M4 done; READMEs and CLAUDE files follow. The matrix suite and the transparent relaxation search are host suites, excluded on the QEMU legs; the public header count is 6. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- .github/workflows/ci.yml | 11 +- CLAUDE.md | 5 +- PLAN.md | 10 +- README.md | 2 +- rational/CLAUDE.md | 30 +- rational/CMakeLists.txt | 16 +- rational/PLAN.md | 469 ++++--- rational/README.md | 32 +- rational/include/tap/sr/rational/chain.h | 212 +++ rational/include/tap/sr/rational/design.h | 115 +- rational/include/tap/sr/rational/ratio.h | 20 + rational/include/tap/sr/rational/rational.h | 25 +- rational/tests/CMakeLists.txt | 7 +- rational/tests/bare_metal_main.cpp | 11 +- rational/tests/coverage/matrix_rows.h | 199 +++ rational/tests/reference/reference_vectors.h | 1138 +++++++++++++++++ rational/tests/support/bridge_stage.h | 63 + rational/tests/test_chain.cpp | 461 +++++++ rational/tests/test_design.cpp | 36 + rational/tests/test_matrix.cpp | 459 +++++++ rational/tools/coverage/matrix.py | 515 ++++++++ .../tools/reference/make_reference_vectors.py | 44 + submodules/dsptap | 2 +- tests/CMakeLists.txt | 2 +- 24 files changed, 3626 insertions(+), 258 deletions(-) create mode 100644 rational/include/tap/sr/rational/chain.h create mode 100644 rational/tests/coverage/matrix_rows.h create mode 100644 rational/tests/support/bridge_stage.h create mode 100644 rational/tests/test_chain.cpp create mode 100644 rational/tests/test_matrix.cpp create mode 100644 rational/tools/coverage/matrix.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9912964..c0c48b9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,9 +181,12 @@ jobs: toolchain: cmake/hexagon-linux-musl.cmake exclude: BadProfilesThrow|LatencyAndValidation # rational: every EXPECT_THROW test (named *Throw*) is excluded on - # Hexagon for the unwind reason above; on the Arm legs - # bare_metal_main.cpp bakes in its own filter (the fine-grid - # sweeps of the long designs). + # Hexagon for the unwind reason above, with the host measurements + # (the coverage matrix's tone batteries, 182 chains in double per + # profile, and the transparent relaxation table's search up to + # N = 735); on the Arm legs bare_metal_main.cpp bakes in its own + # filter (the fine-grid sweeps of the long designs and the + # matrix suite). - target: hexagon title: Hexagon cross engine: rational @@ -192,7 +195,7 @@ jobs: jobs: 4 build_type: Release toolchain: cmake/hexagon-linux-musl.cmake - exclude: Throw + exclude: Throw|Matrix\.EveryRowMeets|RelaxationTablesAreTheSearchAtTransparent - target: m55 title: Cortex-M55 cross engine: rational diff --git a/CLAUDE.md b/CLAUDE.md index 891acfa..72b0e5d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,8 +8,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co over one substrate. `async/` (`tap::sr::async`) is the asynchronous near-unity converter that *absorbs the clock*; `bridge/` (`tap::sr::bridge`) is the synchronous 44.1 ↔ 48 kHz converter that *converts the number*; `rational/` (`tap::sr::rational`) is the synchronous small-factor L/M -converter within a rate family (L, M ∈ {2^a·3^b}), chains of Nyquist stages, at M3 of its plan -(ratio types, pinned designs and the single stages; the chains follow). All build on DspTap (`submodules/dsptap`, +converter within a rate family (L, M ∈ {2^a·3^b}), chains of Nyquist stages, at M4 of its plan +(ratio types, pinned designs, the single stages, the named chains and the 182-row coverage matrix; +fixed point and the C ABI follow). All build on DspTap (`submodules/dsptap`, `tap::dsp`), pinned once at the root. Each engine has its own `README.md`, `PLAN.md` and `CLAUDE.md`; read the engine's before touching its code. diff --git a/PLAN.md b/PLAN.md index c21e926..a6b225d 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1014,8 +1014,14 @@ G13 and G14, plus G9 from 3.7. tap/DspTap#50). M3 — the single stages for the whole vocabulary, pinned against scipy, with DspTap's `accumulate_row` (tap/DspTap#51) under the decimators' zero-skipping branch sum — landed - (`rational/PLAN.md` v0.6). Next: the chains and the generated 14 × 14 - matrix under 2.1's rule (M4). + (`rational/PLAN.md` v0.6). M4 — the chains (`basic_chain`, + each stage designed at its design divisor; DspTap's `chain<>::flush`, + tap/DspTap#52) and the generated 14 × 14 matrix under 2.1's rule, every + one of the 182 rows measured and pinned, the cross rows through + `bridge` at every k — landed (`rational/PLAN.md` v0.7; the matrix was + regenerated from the measured lengths, 35 rows changed chain, the + ledger in its section 8). Next: the fixed-point profiles (M5), then the + C ABI, notebook and icount baselines with the family version 0.5.0 (M6). --- diff --git a/README.md b/README.md index 8d4dc3a..261df15 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ quantization, measurement instruments). |---|---|---|---| | **`async`** | `tap::sr::async` | Asynchronous, near-unity (±`max_deviation_ppm`, default 1000 ppm): two clock domains at nominally the same rate, one thread pushing at the input clock and one pulling at the output clock. **Absorbs the clock.** | [`async/`](async/README.md) | | **`bridge`** | `tap::sr::bridge` | Synchronous 44.1 ↔ 48 kHz (160/147 up, 147/160 down) and the pair at 2× and 4× (88.2 ↔ 96, 176.4 ↔ 192), direction and rate scale fixed at compile time, speed-first with Q15/Q31 profiles for M33/M55-class targets. **Converts the number.** | [`bridge/`](bridge/README.md) | -| **`rational`** | `tap::sr::rational` | Synchronous small-factor L/M *within* a rate family (L, M ∈ {2^a·3^b}: ↑2, ↓3, 2/3, …), as chains of Nyquist (L-th-band) stages, the ratio a compile-time type. **Converts the number.** M1–M3 landed (ratio types, pinned designs, the single stages for every ratio of the vocabulary); the chains and the coverage matrix follow its plan. | [`rational/`](rational/README.md) | +| **`rational`** | `tap::sr::rational` | Synchronous small-factor L/M *within* a rate family (L, M ∈ {2^a·3^b}: ↑2, ↓3, 2/3, …), as chains of Nyquist (L-th-band) stages, the ratio a compile-time type. **Converts the number.** M1–M4 landed (ratio types, pinned designs, the single stages for every ratio of the vocabulary, the named chains and the 182-row coverage matrix generated from the measured lengths); fixed point and the C ABI follow its plan. | [`rational/`](rational/README.md) | The engines never route by rate. The caller declares the clock topology by choosing a type: `async` when the clocks are independent, `bridge` when the diff --git a/rational/CLAUDE.md b/rational/CLAUDE.md index a23974b..923b55c 100644 --- a/rational/CLAUDE.md +++ b/rational/CLAUDE.md @@ -19,16 +19,21 @@ anything; do not re-derive what it settles (the compile-time ratio, one Nyquist stage factoring by MACs then by stage count, `bridge` at the lowest k, the profile vocabulary, latency as an exact rational). -Current state: **M3** — the tree, `ratio` with its charter `static_assert`s and +Current state: **M4** — the tree, `ratio` with its charter `static_assert`s and `ratio_traits`, `profile` carrying the pinned taps per branch for every band of the vocabulary (the M2 design spike, `notebooks/design_spike.ipynb`, executed; the pins found on a -16384-point grid and enforced by `tests/test_design.cpp`), `design_stage` over -`tap::dsp::nyquist.h`, and the single stages: `basic_stage` (`stage.h`) for every ratio -of the vocabulary with bridge's call shapes, `converter` / `converter_q15` / +16384-point grid and enforced by `tests/test_design.cpp`) and, since M4, the relaxation tables +(the pins at every design divisor the matrix uses, `profile::relaxed`), `design_stage` over +`tap::dsp::nyquist.h`, the single stages — `basic_stage` (`stage.h`) for every ratio of +the vocabulary with bridge's call shapes, `converter` / `converter_q15` / `converter_q31` (`converter.h`), pinned against committed scipy vectors -(`tests/reference/`, generated by `tools/reference/make_reference_vectors.py`). M4 (chains -and the matrix), M5 (fixed point) and M6 (C ABI, notebook, icount baselines; family version -0.5.0) follow in PLAN.md section 6. +(`tests/reference/`, generated by `tools/reference/make_reference_vectors.py`) — and the chains: +`basic_chain` (`chain.h`) over DspTap's `chain<>`, each stage designed at its design +divisor (PLAN.md 3.1), the 20 named multi-stage chains, and the 182-row coverage matrix +generated from the pinned lengths by `tools/coverage/matrix.py` (section 3 of the plan; +`tests/coverage/matrix_rows.h`) and pinned row by row by `tests/test_matrix.cpp` (the +cross-family rows through `tests/support/bridge_stage.h`). M5 (fixed point) and M6 (C ABI, +notebook, icount baselines; family version 0.5.0) follow in PLAN.md section 6. ## The charter constraints (load-bearing) @@ -40,9 +45,14 @@ and the matrix), M5 (fixed point) and M6 (C ABI, notebook, icount baselines; fam there is no `(in_hz, out_hz)` lookup here or in the C ABI (whose enumerators name chains). The coverage matrix in PLAN.md documents chains; it dispatches nothing. - **Speed-first, like `bridge`.** The ratio is a compile-time type, so every trip count and - schedule is a compile-time fact; the stage factoring is chosen by MACs (R3: ↑4 is two - half-bands, never one 4th-band stage); every inaudible quality-vs-speed trade goes to speed in - the default `economy` profile. + schedule is a compile-time fact; the stage factoring is chosen by MACs on the measured lengths + (R3: ↑4 is two half-bands, never one 4th-band stage; on the pinned lengths a relaxed + half-band saturates at m = 7, so third-band and mixed stages win where the transition is wide, + PLAN.md 2.1 and the S1 ledger); every inaudible quality-vs-speed trade goes to speed in the + default `economy` profile. A chain's stages are each designed at their own rate: the design + divisor (PLAN.md 3.1), computed by `chain.h` at compile time, with pinned lengths per divisor. + Change the matrix only through the generator, then regenerate the rows header and the plan's + tables. - **One Nyquist filter per stage (R2, R4).** Length N = 2mL − 1, centre tap exactly 1, every L-th tap from it exactly 0, every polyphase branch at DC gain 1 — `tap::dsp::nyquist.h`'s contract, pinned by `test_design.cpp` through this engine's build. The up and down designs of diff --git a/rational/CMakeLists.txt b/rational/CMakeLists.txt index cee2a59..707f933 100644 --- a/rational/CMakeLists.txt +++ b/rational/CMakeLists.txt @@ -11,8 +11,7 @@ endif() # (L, M in {2^a * 3^b}), as fast as possible. Header-only C++20 # (include/tap/sr/rational/), built on the Tap family's shared substrate from # DspTap (the L-th-band designer, the stage composition, sample-format traits, -# dot kernels, quantization). See PLAN.md for the charter and milestones; this -# is the M1 skeleton: the ratio types and the stage design. +# dot kernels, quantization). See PLAN.md for the charter and milestones. # ============================================================================== # Shared Tap-family substrate (tap::dsp), pinned once at the repository root @@ -50,6 +49,19 @@ if(TAP_SR_RATIONAL_WERROR) $<$:/WX>) endif() +# The sibling bridge engine, dev-only (never part of the shipped +# tap::sr::rational target): the coverage matrix's cross-family rows compose +# bridge's converter with this engine's stages (PLAN.md 3.5 / 3.6), through a +# test-side adapter. Consumed headers-only via include path, so this engine's +# code never links bridge's target (PLAN.md 4.2); tap::dsp serves its +# "tap/dsp/..." includes. +if(TAP_SR_BUILD_TESTS) + add_library(tap_sr_bridge_headers INTERFACE) + # SYSTEM: dependency headers are exempt from this engine's warning gates. + cmake_path(SET _tap_bridge_include NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/../bridge/include") + target_include_directories(tap_sr_bridge_headers SYSTEM INTERFACE ${_tap_bridge_include}) +endif() + if(TAP_SR_BUILD_TESTS) enable_testing() add_subdirectory(tests) diff --git a/rational/PLAN.md b/rational/PLAN.md index f0942ef..a6b9a44 100644 --- a/rational/PLAN.md +++ b/rational/PLAN.md @@ -15,6 +15,7 @@ matrix, layout, test strategy, milestones, non-goals and risks. | v0.3 | 2026-10-01 | M0 landed (DspTap `nyquist.h` and `chain.h`, tap/DspTap#48); 2.3's half- and third-band counts are measured by the shipped designer, the harris estimates kept beside them | | v0.4 | 2026-10-01 | M1 landed: the `rational/` tree, `tap::sr::rational`, `ratio` and `ratio_traits` (R1), `profile` and `design_stage` (R5, R4), the family's dependency-rule tests over three engines, the compile-fail charter test; the M2 table's half- and third-band columns are re-measured through the engine's own design | | v0.6 | 2026-10-02 | M3 landed: `stage.h` (`basic_stage`: the L-phase schedule machine for interpolators and mixed ratios with trimmed phase rows, the M-branch commutator for decimators summing its nonzero branches under one finalize, bridge's call shapes, the exact-rational latency), `converter.h` (the aliases), the committed scipy vectors for every ratio of the vocabulary, the M3 battery; the symmetry-halved table of section 4's layout is deferred to the codegen levers after M6's baselines, as bridge did (M7 lever 3) | +| v0.7 | 2026-10-02 | M4 landed: `chain.h` (`basic_chain` over DspTap's `chain<>`, each stage designed at its **design divisor** — its lower rate over the chain's lowest, 3.1 — and the 20 named multi-stage chains), `tools/coverage/matrix.py` committed and re-run on the pinned lengths with the per-stage relaxation pins (`design.h`'s relaxation tables, verified by `test_design.cpp`), section 3 regenerated from measured counts (35 of 182 rows changed chain, the ledger in section 8 S1), `test_matrix.cpp` over all 182 rows (every row measured, none skipped: 2.2 had landed), DspTap's `chain<>::flush` (tap/DspTap#52) | | v0.5 | 2026-10-02 | M2 landed: the design spike (`notebooks/design_spike.ipynb`, executed) pins N for every band ∈ {2, 3, 4, 6, 8} × profile on a 16384-point grid, the mixed ratios' taps per phase, measured worst stopband and ripple; `profile` carries the pins and `test_design.cpp` enforces them; the M2 table below is filled from measurements. Finding: the designer's 1024-point default grid under-pins two rows (tap/DspTap#50 made the search grid a parameter) | **Status.** This draft is the reviewed-plan deliverable that the family @@ -28,13 +29,14 @@ This draft does not change that order: every milestone below that composes with `bridge` at k > 0 is marked as waiting for it. M0 is implemented (v0.3: DspTap's `nyquist.h` and `chain.h`), M1 (v0.4: the tree, the ratio types, the profiles and the stage design), M2 (v0.5: the -pinned lengths per band and profile, measured) and M3 (v0.6: the single -stages, every ratio of the vocabulary, pinned against scipy); the chains -and the matrix are not. Every number marked *est.* is a Kaiser/harris length estimate -(`tap::dsp::estimate_taps`, `kaiser.h:77-85`) or a MAC count derived from -one, to be measured and pinned by the milestones that name it; 2.3's -half- and third-band counts are now measured and sit beside their -estimates. v0.2 +pinned lengths per band and profile, measured), M3 (v0.6: the single +stages, every ratio of the vocabulary, pinned against scipy) and M4 (v0.7: +the chains and the matrix, every row of section 3 regenerated from the +pinned lengths and pinned by `test_matrix.cpp`). The numbers still marked +*est.* — 2.1's arithmetic and 3.2's worked cases — are Kaiser/harris +length estimates (`tap::dsp::estimate_taps`, `kaiser.h:77-85`) kept as the +derivation of the rules; the measured chains and counts are 3.3–3.6, and +where a worked case chose differently on the pinned lengths, 3.2 says so. v0.2 records the maintainer's decisions on v0.1's ten open questions (section 9, each "as recommended") in the R-rows they touch; no question remains open. @@ -106,7 +108,7 @@ The boundaries are identity, not policy, as `bridge`'s are | R13 | **`bridge` at the lowest k** (decision 1): a cross-family chain contains exactly one `bridge` stage, at the lowest k ∈ {0, 1, 2} whose lower rate satisfies 2.1(a) for the chain's f_pass (3.1). `transparent` needs no exception: its 20 kHz passband clears both stopband edges at k = 0 (44.1 − 20 = 24.1 ≥ 24 up, ≥ 22.05 down) | `bridge` costs 58 (down) or 38 (up) MACs per output at `economy` whatever k is, so its cost scales with the rate it runs at, while the ↑2 / ↓2 stages that move a chain between k levels cost 9–19; 48 → 88.2 at k = 0 is 62 % of the MACs of k = 1 at the same (f_pass, A). The family plan's illustrative row (`../PLAN.md:120`) was corrected to match | | R14 | **Dipping rows are flagged** (decision 3): every row of 3.5/3.6 whose `bridge` stage runs — its output rate — at ≥ 2× the chain's output rate carries ⚑ in a trailing column, "covered, not recommended". Documentation only: the flag dispatches nothing and D12 is untouched | In such a row `bridge` alone costs ≥ 116 MACs per output at `economy` (≥ 368 at `transparent`), 2–4× a within-family chain's total; a consumer who reads the matrix sees the price before choosing. 38 rows carry it | | R15 | **384 kHz stays** (decision 5): the family plan's 14 rates are settled (`../PLAN.md:100-101`) | Its cost is 16 generated rows, the ↑8 / ↓8 stages already in the vocabulary and the 64 / 128 kHz intermediates; no new stage kind | -| R16 | **The C ABI exposes the 34 named within-family chains** of 3.3/3.4 as its `chain` enumerators (decision 9) — chains, not rates, and not the single-stage ratios alone | D12 at the C boundary: an enumerator names what the caller composes, as `bridge`'s `int direction` does; the binding and the notebook then need no chain arithmetic of their own (4.4) | +| R16 | **The C ABI exposes the named within-family chains** of 3.3/3.4 as its `chain` enumerators (decision 9; 28 of them on the pinned lengths, v0.7 — the union of the two tables' chain columns) — chains, not rates, and not the single-stage ratios alone | D12 at the C boundary: an enumerator names what the caller composes, as `bridge`'s `int direction` does; the binding and the notebook then need no chain arithmetic of their own (4.4) | ### 2.1 Stage factoring: the arithmetic behind R3 @@ -153,6 +155,17 @@ ratio. Where the search and a chain with fewer stages differ by less than fewer stages is chosen (R3, decision 2) and the matrix test pins it; the search's pick is recorded next to it where it differs (3.2, 3.3). +**On the pinned lengths (M4, v0.7)** the rules stand but eleven of the 26 +within-48 ratios factor differently from this arithmetic, because a +relaxed stage saturates: a half-band at a passband below 3/16 of its +lower rate stays at m = 7 (N = 27, 7.5 MACs per output) however wide the +transition — the Kaiser fit's floor for the 70 dB tier — while a +third-band stage there is m = 5 (N = 29, 7 per output) and serves three +times the rate, so the "by-2 at the low-rate end" chains lose to +third-band and mixed stages (8/1 is ↑2 · 4/3 · ↑3, 12/1 is ↑2 · ↑6, 1/8 is +↓3 · 3/4 · ↓2). Section 3 holds the measured result; the ledger is in +section 8, S1. + ### 2.2 The Nyquist (L-th-band) design: the math behind R4 An L-th-band lowpass has impulse response h[n] with h[c] = 1/L and @@ -221,11 +234,10 @@ of the full table (band 4 `balanced` 103 → 111, band 8 `transparent` up to N = 399, and tap/DspTap#50 made the search's grid a parameter. The harris estimates (*est.*) are kept beside the measured counts: the Kaiser fit needs 2–8 more taps per branch than the estimate at the half band, 1–7 -at the third, so every MAC figure in section 3 that was derived from an -estimate is low by about that ratio until M4 regenerates the matrix from -the pinned counts. The counts are for the stage adjacent to r_min (the -narrowest transition in a chain); stages further up a chain are shorter -(2.1). `bridge`'s experience says a pinned count can also move +at the third; M4 regenerated section 3 from the pinned counts (v0.7). The +counts are for the stage adjacent to r_min (the narrowest transition in a +chain); stages further up a chain are shorter, designed at their design +divisor (3.1) with the pins of `design.h`'s relaxation tables. `bridge`'s experience says a pinned count can also move non-monotonically with the spec (`design.h:79-82`, the 38-vs-40 quirk in `../bridge/PLAN.md` §4), which is why every (band, profile) was pinned from the designer rather than extrapolated. Ripple, measured per stage on @@ -269,7 +281,10 @@ Mirrors `decimate.h:17-24` and `converter.h:51-56`, per stage: Σ_i (N_i − 1)/2 · (f_out / f_hi,i), an exact rational at the output rate, reported as `latency_output_frames()` (numerator, denominator) and `latency_seconds()`; the matrix test pins it per pair. `flush()` drains - every stage's tail in order, writing `flush_output_frames()` frames. + every stage's tail in order (each stage fed its `window_frames()` of + zeros through the stages after it, DspTap `chain.h`), writing + `flush_output_frames()` frames, bit-identical to zero-padding the input. + Each stage of a chain is designed at its design divisor (3.1). ## 3. The coverage matrix @@ -282,6 +297,23 @@ the Nyquist structure, 2.2) at attenuation A (2.1(b)); the chain's ripple is the sum of its stages' (2.1(c)); the matrix test pins (f_pass, A, δ), MACs per output and latency per pair (2.1(d)). +**Each stage at its own rate: the design divisor (M4).** A stage whose +lower rate is r is designed with its passband at f_pass, i.e. at the +fraction p · r_min / r of its own lower rate. `chain.h` computes that +quotient per stage at compile time and takes its **design divisor** d: the +largest 2^a · 3^b at or below r / r_min — exact within a family, where the +quotient is such a number, and conservative across (a passband at or above +f_pass, so 2.1(a) still holds) — or the quotient itself when it is below 1, +the one stage that runs *below* a 48-family r_min, at `bridge`'s rate +(44.1 · 2^k over 48 · 2^k = 147/160: 48 → 88.2's ↑2 from 44.1 must pass +18 kHz, a tightening). `profile::relaxed(d)` is the stage's profile: the +passband over d and the pinned m per band from the profile's relaxation +table (`design.h`, one row per divisor the matrix uses — 147/160, 1, 2, 3, +4, 6, 8, 9, 12, 16 — found by the same search as the M2 pins and verified +against the shipping designer by `test_design.cpp`; a divisor without a +row, or a custom profile, is searched at construction). The generator +costs every stage this way, so the matrix's counts are the chains'. + **Placing `bridge` (R13, decision 1).** A chain between the families contains exactly one `bridge` stage, at the lowest k ∈ {0, 1, 2} whose lower rate 44.1 · 2^k satisfies 2.1(a) for the chain's f_pass — i.e. the @@ -308,19 +340,22 @@ up, ≥ 22.05 down). MAC-minimal chain, then the MAC-minimal chain for every smaller stage count around the same `bridge` stage (same k, same direction) where there is one; the chain chosen is the one with the fewest stages whose -**rational stages'** MACs per output are within 10 % (*est.*) of the -minimum's. `bridge`'s stage is identical in both candidates and is +**rational stages'** MACs per output are within 10 % of the minimum's. `bridge`'s stage is identical in both candidates and is excluded from the comparison: the rule compares factorizations of the rational part, and a `bridge`-dominated total must not absorb a worse factorization (with the total in the comparison, 8 → 11.025 would have collapsed ↑2 · ↑3 into one ↑6 at 297 → 326 MACs per output). Less latency, less state and fewer test rows are worth a few per cent that -the harris estimate cannot resolve anyway until M2 pins. The rule bites -on 17 of the 182 pairs at `economy`: the three 3/1 pairs, the two 24/1 -and the 48/1 pair, 1/16 and 1/24 in both families (5), 16 → 22.05 / 44.1 -/ 88.2 / 176.4, 384 → 22.05 (↓6 · 3/4 for ↓2 · ↓2 · ↓2) and 22.05 → 384 -(4/3 · ↑6 for ↑2 · ↑2 · ↑2); the search's pick is recorded next to the -chosen chain in 3.2 and 3.3. Within-family chains have no `bridge`, so +the measurement resolves to a few taps. On the pinned lengths (v0.7) the +rule bites on 24 of the 182 pairs at `economy`: the three 3/1 pairs +(↑3 for ↑2 · 3/2, +2.3 %), the three 12/1 (↑2 · ↑6 for ↑2 · ↑3 · ↑2, ++5.0 %), the two 24/1 (+3.0 %) and the 48/1 pair (↑2 · ↑8 · ↑3 for five +stages, +0.5 %), their mirrors 1/12 (↓6 · ↓2, +5.0 %), 1/24 (+3.0 %) and +1/48 (+0.5 %), 16 → 11.025 / 22.05 / 44.1 / 88.2 / 176.4 (↑3 for +↑2 · 3/2 ahead of `bridge`, +0.6–2.3 % on the rational part), and in the +44.1 family 8/1 (↑3 · 8/3 for ↑2 · ↑2 · ↑2, +2.7 %) and 1/8 (3/8 · ↓3 for +three half-bands, +9.7 %); `tools/coverage/matrix.py rule` prints the +list. Within-family chains have no `bridge`, so the rule there is simply 10 % of the chain. The `transparent` columns cost the same chains at `transparent`, not a second search. @@ -352,7 +387,12 @@ synchronous ratios that no engine's charter names (`../PLAN.md:103-109`). ### 3.2 Worked cases Lengths are harris estimates (*est.*) at `economy` unless marked `tr` -(`transparent`); the arithmetic is the one-line form of 2.1. +(`transparent`); the arithmetic is the one-line form of 2.1, kept as the +derivation. The measured chains and numbers are 3.3–3.6 (v0.7); four of +the six cases below are the measured chain too, and two chose differently +on the pinned lengths — 384 → 44.1 is ↓3 · 3/4 · ↓2 · `bridge`↓ k = 0 +(172.3 MACs per output) and 8 → 192 is ↑2 · ↑6 · ↑2 (8.7) — for the reason +2.1 ends with. - **176.4 → 48** (`../PLAN.md:93-95`): r_min = 48, f_pass = 18 kHz (`tr`: 20 kHz). Chain ↓2 · ↓2 · `bridge`↑ k = 0. @@ -417,182 +457,199 @@ Lengths are harris estimates (*est.*) at `economy` unless marked `tr` Chains at `economy` (the `transparent` columns use the same chains); MAC/out = MACs per output frame at the chain's output rate; latency in -output frames. All *est.*; the matrix test pins the measured values. +output frames. **Measured** (v0.7): every number is the shipping chain's, +generated by `tools/coverage/matrix.py` from the pinned lengths and pinned +exactly — as a rational — per row by `tests/test_matrix.cpp`. | Ratio L/M | Pairs (from → to, kHz) | Chain | MAC/out eco | Latency eco (out) | MAC/out tr | Latency tr (out) | |---|---|---|---|---|---|---| -| 4/3 | 12→16, 24→32 | 4/3 | 13.5 | 11.8 | 36 | 31.8 | -| 3/2 | 8→12, 16→24, 32→48 | 3/2 | 12 | 13.2 | 32 | 35.8 | -| 2/1 | 8→16, 12→24, 16→32, 24→48, 48→96, 96→192, 192→384 | ↑2 | 9 | 17 | 24 | 47 | -| 8/3 | 12→32 | ↑2 · 4/3 | 12.8 | 27.8 | 28.5 | 71.8 | -| 3/1 | 8→24, 16→48, 32→96 | ↑3 | 12 | 26 | 32 | 71 | -| 4/1 | 8→32, 12→48, 24→96, 48→192, 96→384 | ↑2 · ↑2 | 8.5 | 41 | 19 | 107 | -| 6/1 | 8→48, 16→96, 32→192 | ↑2 · ↑3 | 8.3 | 62 | 17.3 | 161 | -| 8/1 | 12→96, 24→192, 48→384 | ↑2 · ↑2 · ↑2 | 7.2 | 87 | 15.5 | 225 | -| 12/1 | 8→96, 16→192, 32→384 | ↑2 · ↑2 · ↑3 | 6.8 | 131 | 14.3 | 338 | -| 16/1 | 12→192, 24→384 | ↑2 · ↑2 · ↑2 · ↑2 | 6.6 | 179 | 12.8 | 459 | -| 24/1 | 8→192, 16→384 | ↑3 · ↑8 | 6.8 | 231 | 14.5 | 615 | -| 32/1 | 12→384 | ↑2 · ↑2 · ↑8 | 6.3 | 351 | 11.1 | 895 | -| 48/1 | 8→384 | ↑2 · ↑3 · ↑8 | 6.3 | 519 | 10.9 | 1327 | -| 3/4 | 16→12, 32→24 | 3/4 | 24 | 8.9 | 64 | 23.9 | -| 2/3 | 12→8, 24→16, 48→32 | 2/3 | 26 | 8.5 | 72 | 23.8 | -| 1/2 | 16→8, 24→12, 32→16, 48→24, 96→48, 192→96, 384→192 | ↓2 | 19 | 8.5 | 49 | 23.5 | -| 3/8 | 32→12 | 3/4 · ↓2 | 39 | 10.3 | 85 | 26.8 | -| 1/3 | 24→8, 48→16, 96→32 | ↓3 | 37 | 8.7 | 97 | 23.7 | -| 1/4 | 32→8, 48→12, 96→24, 192→48, 384→96 | ↓2 · ↓2 | 37 | 10.2 | 79 | 26.8 | -| 1/6 | 48→8, 96→16, 192→32 | ↓3 · ↓2 | 53 | 10.3 | 107 | 26.8 | -| 1/8 | 96→12, 192→24, 384→48 | ↓2 · ↓2 · ↓2 | 65 | 10.9 | 131 | 28.1 | -| 1/12 | 96→8, 192→16, 384→32 | ↓3 · ↓2 · ↓2 | 89 | 10.9 | 179 | 28.2 | -| 1/16 | 192→12, 384→24 | ↓8 · ↓2 | 133 | 10.4 | 247 | 26.9 | -| 1/24 | 192→8, 384→16 | ↓8 · ↓3 | 166 | 9.6 | 352 | 25.6 | -| 1/32 | 384→12 | ↓8 · ↓2 · ↓2 | 209 | 11 | 363 | 28 | -| 1/48 | 384→8 | ↓8 · ↓3 · ↓2 | 311 | 10.8 | 533 | 27.6 | - -The ↓8 stages at the top of the long decimation chains are the -"larger factor at the high-rate end" of R3: in 384 → 12 (f_pass = 4.5 kHz) -the first ↓8 (384 → 48; lower rate 48, stopband edge 43.5, transition -39 kHz wide at 384 kHz) is a 47-tap 8th-band filter with 43 nonzero taps, -172 MACs per final output, and the three-stage chain ↓8 · ↓2 · ↓2 costs -209 (*est.*) against 233 for five half-bands (7 · 16 + 7 · 8 + 7 · 4 + -9 · 2 + 19): at 16× the output rate even a 7-MAC half-band is expensive, -so one wide stage at the top wins. Decision 6 keeps the single 4th-band -stage out of the vocabulary, so 1/16 is ↓8 · ↓2 (133, against 121 for -four half-bands — within 10 %, fewer stages, R3) and 1/24 is ↓8 · ↓3 (166 -against 161 for ↓6 · ↓2 · ↓2); v0.1's ↓4 · ↓2 · ↓2 at 113 would be -cheaper, and a latency-first consumer who asks for the 4th-band stage -reopens exactly these rows. Likewise 3/1 is one ↑3 (12.0; the search's -3/2 · ↑2 is 11.0), 24/1 is ↑3 · ↑8 (6.8; ↑2 · ↑2 · ↑6 is 6.4) and 48/1 -is ↑2 · ↑3 · ↑8 (6.3; the four-stage minimum is 6.2). The `transparent` +| 4/3 | 12→16, 24→32 | 4/3 | 16.8 | 14.3 | 37.8 | 33.0 | +| 3/2 | 8→12, 16→24, 32→48 | 3/2 | 15.0 | 16.0 | 33.7 | 37.0 | +| 2/1 | 8→16, 12→24, 16→32, 24→48, 48→96, 96→192, 192→384 | ↑2 | 11.5 | 21.0 | 31.5 | 61.0 | +| 8/3 | 12→32 | ↑2 · 4/3 | 16.4 | 34.3 | 43.4 | 98.3 | +| 3/1 | 8→24, 16→48, 32→96 | ↑3 | 15.0 | 32.0 | 33.7 | 74.0 | +| 4/1 | 8→32, 12→48, 24→96, 48→192, 96→384 | ↑2 · ↑2 | 13.2 | 55.0 | 26.2 | 141.0 | +| 6/1 | 8→48, 16→96, 32→192 | ↑2 · ↑3 | 10.8 | 77.0 | 21.5 | 206.0 | +| 8/1 | 12→96, 24→192, 48→384 | ↑2 · 4/3 · ↑3 | 12.5 | 117.0 | 25.5 | 318.0 | +| 12/1 | 8→96, 16→192, 32→384 | ↑2 · ↑6 | 10.4 | 155.0 | 27.1 | 443.0 | +| 16/1 | 12→192, 24→384 | ↑2 · ↑8 | 10.3 | 207.0 | 26.8 | 591.0 | +| 24/1 | 8→192, 16→384 | ↑2 · ↑6 · ↑2 | 8.7 | 315.0 | 22.0 | 901.0 | +| 32/1 | 12→384 | ↑2 · ↑8 · ↑2 | 8.7 | 419.0 | 20.9 | 1195.0 | +| 48/1 | 8→384 | ↑2 · ↑8 · ↑3 | 7.8 | 629.0 | 18.6 | 1793.0 | +| 3/4 | 16→12, 32→24 | 3/4 | 29.0 | 10.8 | 66.3 | 24.8 | +| 2/3 | 12→8, 24→16, 48→32 | 2/3 | 32.5 | 10.7 | 74.5 | 24.7 | +| 1/2 | 16→8, 24→12, 32→16, 48→24, 96→48, 192→96, 384→192 | ↓2 | 23.0 | 10.5 | 63.0 | 30.5 | +| 3/8 | 32→12 | 3/4 · ↓2 | 49.0 | 12.9 | 131.7 | 36.9 | +| 1/3 | 24→8, 48→16, 96→32 | ↓3 | 45.0 | 10.7 | 101.0 | 24.7 | +| 1/4 | 32→8, 48→12, 96→24, 192→48, 384→96 | ↓2 · ↓2 | 53.0 | 13.8 | 105.0 | 35.2 | +| 1/6 | 48→8, 96→16, 192→32 | ↓3 · ↓2 | 65.0 | 12.8 | 129.0 | 34.3 | +| 1/8 | 96→12, 192→24, 384→48 | ↓3 · 3/4 · ↓2 | 105.0 | 14.6 | 219.7 | 39.8 | +| 1/12 | 96→8, 192→16, 384→32 | ↓6 · ↓2 | 125.0 | 12.9 | 325.0 | 36.9 | +| 1/16 | 192→12, 384→24 | ↓8 · ↓2 | 165.0 | 12.9 | 429.0 | 36.9 | +| 1/24 | 192→8, 384→16 | ↓2 · ↓6 · ↓2 | 209.0 | 13.1 | 529.0 | 37.5 | +| 1/32 | 384→12 | ↓2 · ↓8 · ↓2 | 277.0 | 13.1 | 669.0 | 37.3 | +| 1/48 | 384→8 | ↓3 · ↓8 · ↓2 | 373.0 | 13.1 | 893.0 | 37.4 | + +What the pinned lengths changed (the v0.2 arithmetic is in 2.1 and its +ledger in section 8, S1): a relaxed half-band saturates at m = 7 (7.5 MACs +per output, 15 per output as a decimator), so the chains that used to end +in a run of half-bands now put a third-band, 4th-band or mixed stage where +the transition is wide — 8/1 is ↑2 · 4/3 · ↑3 (12.5 against 14.1 for three +half-bands), 1/8 is ↓3 · 3/4 · ↓2 (105 against 113), 12/1 is ↑2 · ↑6 and +1/12 is ↓6 · ↓2 (the fewer-stages rule, +5 % over ↑2 · ↑3 · ↑2 / ↓2 · ↓3 · +↓2), and the ↑8 / ↓8 stage moves from the high-rate end to the middle +(24/1 is ↑2 · ↑6 · ↑2, 32/1 is ↑2 · ↑8 · ↑2, 1/32 is ↓2 · ↓8 · ↓2, 1/48 is +↓3 · ↓8 · ↓2): at a divisor of 16 the 8th-band stage is m = 4 (N = 63, 57 +nonzero) and a half-band above it m = 3 (N = 11, 3.5 per output), cheaper +than ↓8 at the top. Decision 6 still keeps the single 4th-band stage out +(1/16 is ↓8 · ↓2 at 165; 16/1 is ↑2 · ↑8 at 10.3). The `transparent` columns cost the same chains (3.1). ### 3.4 Within the 44.1 kHz family (20 ordered pairs, 8 ratios) +Six of the eight chains are the 48 family's; 8/1 and 1/8 differ because +the intermediate lattices differ relative to r_min — 11.025 · 3 = 33.075 +kHz is a 2^a · 3^b multiple of the family's base where 12 · 8/3 = 32 kHz +is one of the 48 family's (neither is a supported rate; both are stage +boundaries) — so the 44.1 family's 8/1 is ↑3 · 8/3 and its 1/8 is 3/8 · ↓3, +two more named chains. + | Ratio L/M | Pairs (from → to, kHz) | Chain | MAC/out eco | Latency eco (out) | MAC/out tr | Latency tr (out) | |---|---|---|---|---|---|---| -| 2/1 | 11.025→22.05, 22.05→44.1, 44.1→88.2, 88.2→176.4 | ↑2 | 9 | 17 | 24 | 47 | -| 4/1 | 11.025→44.1, 22.05→88.2, 44.1→176.4 | ↑2 · ↑2 | 8.5 | 41 | 19 | 107 | -| 8/1 | 11.025→88.2, 22.05→176.4 | ↑2 · ↑2 · ↑2 | 7.2 | 87 | 15.5 | 225 | -| 16/1 | 11.025→176.4 | ↑2 · ↑2 · ↑2 · ↑2 | 6.6 | 179 | 12.8 | 459 | -| 1/2 | 22.05→11.025, 44.1→22.05, 88.2→44.1, 176.4→88.2 | ↓2 | 19 | 8.5 | 49 | 23.5 | -| 1/4 | 44.1→11.025, 88.2→22.05, 176.4→44.1 | ↓2 · ↓2 | 37 | 10.2 | 79 | 26.8 | -| 1/8 | 88.2→11.025, 176.4→22.05 | ↓2 · ↓2 · ↓2 | 65 | 10.9 | 131 | 28.1 | -| 1/16 | 176.4→11.025 | ↓8 · ↓2 | 133 | 10.4 | 247 | 26.9 | +| 2/1 | 11.025→22.05, 22.05→44.1, 44.1→88.2, 88.2→176.4 | ↑2 | 11.5 | 21.0 | 31.5 | 61.0 | +| 4/1 | 11.025→44.1, 22.05→88.2, 44.1→176.4 | ↑2 · ↑2 | 13.2 | 55.0 | 26.2 | 141.0 | +| 8/1 | 11.025→88.2, 22.05→176.4 | ↑3 · 8/3 | 14.5 | 98.3 | 33.8 | 229.0 | +| 16/1 | 11.025→176.4 | ↑2 · ↑8 | 10.3 | 207.0 | 26.8 | 591.0 | +| 1/2 | 22.05→11.025, 44.1→22.05, 88.2→44.1, 176.4→88.2 | ↓2 | 23.0 | 10.5 | 63.0 | 30.5 | +| 1/4 | 44.1→11.025, 88.2→22.05, 176.4→44.1 | ↓2 · ↓2 | 53.0 | 13.8 | 105.0 | 35.2 | +| 1/8 | 88.2→11.025, 176.4→22.05 | 3/8 · ↓3 | 124.0 | 12.3 | 292.0 | 28.6 | +| 1/16 | 176.4→11.025 | ↓8 · ↓2 | 165.0 | 12.9 | 429.0 | 36.9 | ### 3.5 48 kHz family → 44.1 kHz family (45 ordered pairs) `bridge`↓ at k is the 147/160 converter running from 48 · 2^k to 44.1 · 2^k; its MACs and latency are `bridge`'s pinned `economy` / -`transparent` numbers (58 / 184 taps per phase, `design.h:86-89`). Rows -with k > 0 wait for the 2.2 follow-up (section 6). ⚑ (R14): the `bridge` -stage's output rate is ≥ 2× the chain's output rate — covered, not -recommended as a default. +`transparent` numbers (58 / 184 taps per phase, `design.h:86-89`). Every +row is measured, the k > 0 rows included (2.2 landed before M4). ⚑ (R14): +the `bridge` stage's output rate is ≥ 2× the chain's output rate — +covered, not recommended as a default. The rational stages of a cross row +are designed at their design divisors (3.1): the lattice floor of the +quotient, or 147/160 for the stage at `bridge`'s rate below a 48-family +r_min (48 → 88.2's ↑2, 96 → 44.1's ↓2 and their k > 0 twins). | From → To | Chain | f_pass eco (kHz) | MAC/out eco | Latency eco (ms) | MAC/out tr | Latency tr (ms) | ⚑ | |---|---|---|---|---|---|---|---| -| 8 → 11.025 | ↑2 · ↑3 · bridge↓k0 · ↓2 · ↓2 | 3.000 | 297.3 | 2.46 | 860.5 | 6.47 | ⚑ | -| 8 → 22.05 | ↑2 · ↑3 · bridge↓k0 · ↓2 | 3.000 | 143.1 | 2.05 | 418.7 | 5.52 | ⚑ | -| 8 → 44.1 | ↑2 · ↑3 · bridge↓k0 | 3.000 | 67.1 | 1.90 | 202.9 | 5.27 | | -| 8 → 88.2 | ↑2 · ↑3 · bridge↓k0 · ↑2 | 3.000 | 36.5 | 1.95 | 106.4 | 5.37 | | -| 8 → 176.4 | ↑2 · ↑3 · bridge↓k0 · ↑2 · ↑2 | 3.000 | 21.3 | 1.98 | 58.2 | 5.42 | | -| 12 → 11.025 | ↑2 · ↑2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 303.8 | 2.30 | 882.5 | 5.99 | ⚑ | -| 12 → 22.05 | ↑2 · ↑2 · bridge↓k0 · ↓2 | 4.500 | 143.5 | 1.62 | 426.4 | 4.49 | ⚑ | -| 12 → 44.1 | ↑2 · ↑2 · bridge↓k0 | 4.500 | 67.3 | 1.46 | 204.7 | 4.15 | | -| 12 → 88.2 | ↑2 · ↑2 · bridge↓k0 · ↑2 | 4.500 | 36.6 | 1.52 | 108.3 | 4.27 | | -| 12 → 176.4 | ↑2 · ↑2 · bridge↓k0 · ↑2 · ↑2 | 4.500 | 21.3 | 1.54 | 59.2 | 4.32 | | -| 16 → 11.025 | ↑3 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 298.0 | 1.83 | 873.0 | 4.95 | ⚑ | -| 16 → 22.05 | ↑3 · bridge↓k0 · ↓2 | 6.000 | 153.1 | 1.35 | 460.7 | 3.87 | ⚑ | -| 16 → 44.1 | ↑3 · bridge↓k0 | 6.000 | 71.1 | 1.15 | 218.8 | 3.40 | | -| 16 → 88.2 | ↑3 · bridge↓k0 · ↑2 | 6.000 | 39.5 | 1.23 | 115.4 | 3.52 | | -| 16 → 176.4 | ↑3 · bridge↓k0 · ↑2 · ↑2 | 6.000 | 22.8 | 1.25 | 62.7 | 3.57 | | -| 24 → 11.025 | ↑2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 286.4 | 1.68 | 845.5 | 4.61 | ⚑ | -| 24 → 22.05 | ↑2 · bridge↓k0 · ↓2 | 8.269 | 152.4 | 1.30 | 454.0 | 3.67 | ⚑ | -| 24 → 44.1 | ↑2 · bridge↓k0 | 9.000 | 67.8 | 0.96 | 210.1 | 2.90 | | -| 24 → 88.2 | ↑2 · bridge↓k0 · ↑2 | 9.000 | 37.9 | 1.04 | 113.1 | 3.07 | | -| 24 → 176.4 | ↑2 · bridge↓k0 · ↑2 · ↑2 | 9.000 | 21.9 | 1.07 | 62.5 | 3.13 | | -| 32 → 11.025 | 3/2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 286.4 | 1.62 | 849.8 | 4.53 | ⚑ | -| 32 → 22.05 | 3/2 · bridge↓k0 · ↓2 | 8.269 | 149.5 | 1.14 | 446.0 | 3.29 | ⚑ | -| 32 → 44.1 | 3/2 · bridge↓k0 | 12.000 | 71.1 | 0.88 | 218.8 | 2.66 | | -| 32 → 88.2 | 3/2 · bridge↓k0 · ↑2 | 12.000 | 40.5 | 0.98 | 120.4 | 2.90 | | -| 32 → 176.4 | 3/2 · bridge↓k0 · ↑2 · ↑2 | 12.000 | 24.3 | 1.02 | 66.2 | 2.96 | | -| 48 → 11.025 | bridge↓k0 · ↓2 · ↓2 | 4.134 | 269.0 | 1.53 | 815.0 | 4.34 | ⚑ | -| 48 → 22.05 | bridge↓k0 · ↓2 | 8.269 | 135.0 | 0.99 | 417.0 | 2.98 | ⚑ | +| 8 → 11.025 | ↑2 · ↑3 · bridge↓k0 · ↓2 · ↓2 | 3.000 | 332.2 | 3.46 | 934.6 | 9.41 | ⚑ | +| 8 → 22.05 | ↑2 · ↑3 · bridge↓k0 · ↓2 | 3.000 | 154.6 | 2.50 | 435.8 | 6.64 | ⚑ | +| 8 → 44.1 | ↑2 · ↑3 · bridge↓k0 | 3.000 | 69.8 | 2.21 | 207.4 | 6.21 | | +| 8 → 88.2 | ↑2 · ↑3 · bridge↓k0 · ↑2 | 3.000 | 42.4 | 2.36 | 113.2 | 6.40 | | +| 8 → 176.4 | ↑2 · ↑3 · bridge↓k0 · ↑2 · ↑2 | 3.000 | 25.7 | 2.40 | 65.1 | 6.49 | | +| 12 → 11.025 | ↑2 · ↑2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 342.7 | 3.00 | 955.3 | 8.05 | ⚑ | +| 12 → 22.05 | ↑2 · ↑2 · bridge↓k0 · ↓2 | 4.500 | 167.8 | 2.23 | 488.1 | 6.24 | ⚑ | +| 12 → 44.1 | ↑2 · ↑2 · bridge↓k0 | 4.500 | 72.4 | 1.75 | 212.6 | 4.85 | | +| 12 → 88.2 | ↑2 · ↑2 · bridge↓k0 · ↑2 | 4.500 | 43.7 | 1.90 | 115.8 | 5.05 | | +| 12 → 176.4 | ↑2 · ↑2 · bridge↓k0 · ↑2 · ↑2 | 4.500 | 26.4 | 1.94 | 67.4 | 5.14 | | +| 16 → 11.025 | ↑3 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 350.3 | 2.52 | 987.6 | 6.66 | ⚑ | +| 16 → 22.05 | ↑3 · bridge↓k0 · ↓2 | 6.000 | 171.7 | 1.75 | 504.3 | 4.84 | ⚑ | +| 16 → 44.1 | ↑3 · bridge↓k0 | 6.000 | 74.3 | 1.27 | 220.6 | 3.46 | | +| 16 → 88.2 | ↑3 · bridge↓k0 · ↑2 | 6.000 | 44.7 | 1.42 | 120.8 | 3.67 | | +| 16 → 176.4 | ↑3 · bridge↓k0 · ↑2 · ↑2 | 6.000 | 29.8 | 1.49 | 69.9 | 3.77 | | +| 24 → 11.025 | ↑2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 317.7 | 2.12 | 886.7 | 5.51 | ⚑ | +| 24 → 22.05 | ↑2 · bridge↓k0 · ↓2 | 8.269 | 164.0 | 1.52 | 499.6 | 4.57 | ⚑ | +| 24 → 44.1 | ↑2 · bridge↓k0 | 9.000 | 70.5 | 1.04 | 218.3 | 3.19 | | +| 24 → 88.2 | ↑2 · bridge↓k0 · ↑2 | 9.000 | 46.8 | 1.28 | 140.6 | 3.88 | | +| 24 → 176.4 | ↑2 · bridge↓k0 · ↑2 · ↑2 | 9.000 | 30.9 | 1.35 | 79.8 | 3.98 | | +| 32 → 11.025 | 3/2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 315.5 | 2.00 | 888.9 | 5.35 | ⚑ | +| 32 → 22.05 | 3/2 · bridge↓k0 · ↓2 | 8.269 | 171.7 | 1.41 | 504.3 | 4.07 | ⚑ | +| 32 → 44.1 | 3/2 · bridge↓k0 | 12.000 | 74.3 | 0.94 | 220.6 | 2.69 | | +| 32 → 88.2 | 3/2 · bridge↓k0 · ↑2 | 12.000 | 48.7 | 1.18 | 141.8 | 3.38 | | +| 32 → 176.4 | 3/2 · bridge↓k0 · ↑2 · ↑2 | 12.000 | 31.8 | 1.25 | 81.4 | 3.49 | | +| 48 → 11.025 | bridge↓k0 · ↓2 · ↓2 | 4.134 | 285.0 | 1.85 | 841.0 | 5.11 | ⚑ | +| 48 → 22.05 | bridge↓k0 · ↓2 | 8.269 | 139.0 | 1.08 | 431.0 | 3.30 | ⚑ | | 48 → 44.1 | bridge↓k0 | 16.538 | 58.0 | 0.60 | 184.0 | 1.92 | | -| 48 → 88.2 | bridge↓k0 · ↑2 | 18.000 | 42.0 | 0.89 | 135.0 | 2.88 | | -| 48 → 176.4 | bridge↓k0 · ↑2 · ↑2 | 18.000 | 25.0 | 0.93 | 75.5 | 2.97 | | -| 96 → 11.025 | ↓2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 299.5 | 1.59 | 871.6 | 4.46 | ⚑ | -| 96 → 22.05 | ↓2 · bridge↓k0 · ↓2 | 8.269 | 154.6 | 1.06 | 449.7 | 3.12 | ⚑ | -| 96 → 44.1 | ↓2 · bridge↓k0 | 16.538 | 76.5 | 0.76 | 222.1 | 2.26 | | +| 48 → 88.2 | bridge↓k0 · ↑2 | 18.000 | 44.5 | 0.93 | 137.5 | 2.93 | | +| 48 → 176.4 | bridge↓k0 · ↑2 · ↑2 | 18.000 | 33.8 | 1.05 | 100.2 | 3.27 | | +| 96 → 11.025 | ↓2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 350.3 | 1.99 | 923.7 | 5.29 | ⚑ | +| 96 → 22.05 | ↓2 · bridge↓k0 · ↓2 | 8.269 | 171.7 | 1.22 | 476.7 | 3.50 | ⚑ | +| 96 → 44.1 | ↓2 · bridge↓k0 | 16.538 | 83.0 | 0.82 | 252.6 | 2.55 | | | 96 → 88.2 | bridge↓k1 | 33.075 | 58.0 | 0.30 | 184.0 | 0.96 | | -| 96 → 176.4 | bridge↓k1 · ↑2 | 36.000 | 42.0 | 0.44 | 135.0 | 1.44 | | -| 192 → 11.025 | ↓2 · ↓2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 360.4 | 1.61 | 967.4 | 4.50 | ⚑ | -| 192 → 22.05 | ↓2 · ↓2 · bridge↓k0 · ↓2 | 8.269 | 185.1 | 1.09 | 506.3 | 3.18 | ⚑ | -| 192 → 44.1 | ↓2 · ↓2 · bridge↓k0 | 16.538 | 96.1 | 0.80 | 254.7 | 2.33 | | -| 192 → 88.2 | ↓2 · bridge↓k1 | 33.075 | 76.5 | 0.38 | 222.1 | 1.13 | | +| 96 → 176.4 | bridge↓k1 · ↑2 | 36.000 | 44.5 | 0.47 | 137.5 | 1.46 | | +| 192 → 11.025 | ↓2 · ↓2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 428.7 | 2.02 | 1089.2 | 5.38 | ⚑ | +| 192 → 22.05 | ↓3 · 3/4 · bridge↓k0 · ↓2 | 8.269 | 228.3 | 1.25 | 601.5 | 3.69 | ⚑ | +| 192 → 44.1 | ↓2 · ↓2 · bridge↓k0 | 16.538 | 115.7 | 0.89 | 298.3 | 2.65 | | +| 192 → 88.2 | ↓2 · bridge↓k1 | 33.075 | 83.0 | 0.41 | 252.6 | 1.28 | | | 192 → 176.4 | bridge↓k2 | 66.150 | 58.0 | 0.15 | 184.0 | 0.48 | | -| 384 → 11.025 | ↓8 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 456.2 | 1.59 | 1124.1 | 4.44 | ⚑ | -| 384 → 22.05 | ↓6 · 3/4 · bridge↓k0 · ↓2 | 8.269 | 246.7 | 1.11 | 633.2 | 3.21 | ⚑ | -| 384 → 44.1 | ↓2 · ↓2 · ↓2 · bridge↓k0 | 16.538 | 126.6 | 0.81 | 311.3 | 2.36 | | -| 384 → 88.2 | ↓2 · ↓2 · bridge↓k1 | 33.075 | 96.1 | 0.40 | 254.7 | 1.16 | | -| 384 → 176.4 | ↓2 · bridge↓k2 | 66.150 | 76.5 | 0.19 | 222.1 | 0.57 | | +| 384 → 11.025 | ↓2 · ↓2 · ↓2 · bridge↓k0 · ↓2 · ↓2 | 4.134 | 550.6 | 2.04 | 1350.4 | 5.41 | ⚑ | +| 384 → 22.05 | ↓8 · bridge↓k0 · ↓2 | 8.269 | 293.6 | 1.18 | 829.4 | 3.57 | ⚑ | +| 384 → 44.1 | ↓3 · 3/4 · ↓2 · bridge↓k0 | 16.538 | 172.3 | 0.91 | 423.1 | 2.74 | | +| 384 → 88.2 | ↓2 · ↓2 · bridge↓k1 | 33.075 | 115.7 | 0.45 | 298.3 | 1.33 | | +| 384 → 176.4 | ↓2 · bridge↓k2 | 66.150 | 83.0 | 0.21 | 252.6 | 0.64 | | ### 3.6 44.1 kHz family → 48 kHz family (45 ordered pairs) | From → To | Chain | f_pass eco (kHz) | MAC/out eco | Latency eco (ms) | MAC/out tr | Latency tr (ms) | ⚑ | |---|---|---|---|---|---|---|---| -| 11.025 → 8 | ↑2 · ↑2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 316.8 | 2.29 | 746.4 | 5.64 | ⚑ | -| 11.025 → 12 | ↑2 · ↑2 · bridge↑k0 · ↓2 · ↓2 | 4.134 | 218.2 | 2.13 | 518.8 | 5.16 | ⚑ | -| 11.025 → 16 | ↑2 · ↑2 · bridge↑k0 · ↓3 | 4.134 | 158.4 | 1.65 | 381.4 | 4.12 | ⚑ | -| 11.025 → 24 | ↑2 · ↑2 · bridge↑k0 · ↓2 | 4.134 | 100.6 | 1.51 | 241.9 | 3.79 | ⚑ | -| 11.025 → 32 | ↑2 · ↑2 · bridge↑k0 · 2/3 | 4.134 | 78.7 | 1.46 | 188.2 | 3.70 | | -| 11.025 → 48 | ↑2 · ↑2 · bridge↑k0 | 4.134 | 45.8 | 1.36 | 113.5 | 3.51 | | -| 11.025 → 96 | ↑2 · ↑2 · bridge↑k0 · ↑2 | 4.134 | 25.9 | 1.41 | 62.7 | 3.63 | | -| 11.025 → 192 | ↑2 · ↑2 · bridge↑k0 · ↑2 · ↑2 | 4.134 | 16.0 | 1.44 | 36.4 | 3.68 | | -| 11.025 → 384 | ↑2 · ↑2 · bridge↑k0 · ↑8 | 4.134 | 11.0 | 1.42 | 22.9 | 3.62 | | -| 22.05 → 8 | ↑2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 303.1 | 1.88 | 716.1 | 4.69 | ⚑ | -| 22.05 → 12 | ↑2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 203.7 | 1.44 | 492.4 | 3.66 | ⚑ | -| 22.05 → 16 | ↑2 · bridge↑k0 · ↓3 | 6.000 | 164.8 | 1.18 | 415.3 | 3.04 | ⚑ | -| 22.05 → 24 | ↑2 · bridge↑k0 · ↓2 | 8.269 | 109.5 | 1.13 | 271.1 | 2.84 | ⚑ | -| 22.05 → 32 | ↑2 · bridge↑k0 · 2/3 | 8.269 | 83.4 | 0.96 | 205.1 | 2.44 | | -| 22.05 → 48 | ↑2 · bridge↑k0 | 8.269 | 46.3 | 0.82 | 118.0 | 2.15 | | -| 22.05 → 96 | ↑2 · bridge↑k0 · ↑2 | 8.269 | 27.1 | 0.89 | 66.0 | 2.29 | | -| 22.05 → 192 | ↑2 · bridge↑k0 · ↑2 · ↑2 | 8.269 | 16.6 | 0.92 | 39.0 | 2.35 | | -| 22.05 → 384 | ↑2 · bridge↑k0 · 4/3 · ↑6 | 8.269 | 11.8 | 0.94 | 26.5 | 2.39 | | -| 44.1 → 8 | bridge↑k0 · ↓3 · ↓2 | 3.000 | 281.0 | 1.72 | 683.0 | 4.44 | ⚑ | -| 44.1 → 12 | bridge↑k0 · ↓2 · ↓2 | 4.500 | 189.0 | 1.29 | 463.0 | 3.32 | ⚑ | -| 44.1 → 16 | bridge↑k0 · ↓3 | 6.000 | 151.0 | 0.97 | 385.0 | 2.57 | ⚑ | -| 44.1 → 24 | bridge↑k0 · ↓2 | 9.000 | 95.0 | 0.79 | 241.0 | 2.07 | ⚑ | -| 44.1 → 32 | bridge↑k0 · 2/3 | 12.000 | 83.0 | 0.70 | 216.0 | 1.83 | | +| 11.025 → 8 | ↑2 · ↑2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 366.0 | 3.28 | 849.7 | 8.58 | ⚑ | +| 11.025 → 12 | ↑2 · ↑2 · bridge↑k0 · ↓2 · ↓2 | 4.134 | 253.7 | 2.82 | 585.5 | 7.22 | ⚑ | +| 11.025 → 16 | ↑2 · ↑2 · bridge↑k0 · ↓3 | 4.134 | 195.5 | 2.34 | 461.4 | 5.83 | ⚑ | +| 11.025 → 24 | ↑2 · ↑2 · bridge↑k0 · ↓2 | 4.134 | 115.3 | 1.95 | 261.2 | 4.68 | ⚑ | +| 11.025 → 32 | ↑2 · ↑2 · bridge↑k0 · 2/3 | 4.134 | 89.8 | 1.82 | 203.7 | 4.53 | | +| 11.025 → 48 | ↑2 · ↑2 · bridge↑k0 | 4.134 | 50.2 | 1.68 | 120.1 | 4.29 | | +| 11.025 → 96 | ↑2 · ↑2 · bridge↑k0 · ↑2 | 4.134 | 32.6 | 1.81 | 69.6 | 4.46 | | +| 11.025 → 192 | ↑2 · ↑2 · bridge↑k0 · ↑2 · ↑2 | 4.134 | 20.8 | 1.85 | 44.3 | 4.55 | | +| 11.025 → 384 | ↑2 · ↑2 · bridge↑k0 · ↑2 · ↑2 · ↑2 | 4.134 | 13.9 | 1.86 | 29.6 | 4.59 | | +| 22.05 → 8 | ↑2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 334.3 | 2.33 | 762.9 | 5.81 | ⚑ | +| 22.05 → 12 | ↑2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 247.3 | 2.05 | 604.8 | 5.41 | ⚑ | +| 22.05 → 16 | ↑2 · bridge↑k0 · ↓3 | 6.000 | 190.7 | 1.57 | 475.8 | 4.01 | ⚑ | +| 22.05 → 24 | ↑2 · bridge↑k0 · ↓2 | 8.269 | 120.1 | 1.34 | 312.9 | 3.74 | ⚑ | +| 22.05 → 32 | ↑2 · bridge↑k0 · 2/3 | 8.269 | 105.3 | 1.24 | 261.9 | 3.24 | | +| 22.05 → 48 | ↑2 · bridge↑k0 | 8.269 | 48.6 | 0.91 | 124.9 | 2.47 | | +| 22.05 → 96 | ↑2 · bridge↑k0 · ↑2 | 8.269 | 31.8 | 1.04 | 73.0 | 2.67 | | +| 22.05 → 192 | ↑2 · bridge↑k0 · 4/3 · ↑3 | 8.269 | 21.7 | 1.08 | 48.8 | 2.86 | | +| 22.05 → 384 | ↑2 · bridge↑k0 · ↑8 | 8.269 | 14.9 | 1.01 | 38.5 | 2.74 | | +| 44.1 → 8 | bridge↑k0 · ↓3 · ↓2 | 3.000 | 293.0 | 2.03 | 705.0 | 5.38 | ⚑ | +| 44.1 → 12 | bridge↑k0 · ↓2 · ↓2 | 4.500 | 205.0 | 1.58 | 489.0 | 4.03 | ⚑ | +| 44.1 → 16 | bridge↑k0 · ↓3 | 6.000 | 159.0 | 1.10 | 389.0 | 2.63 | ⚑ | +| 44.1 → 24 | bridge↑k0 · ↓2 | 9.000 | 99.0 | 0.87 | 255.0 | 2.36 | ⚑ | +| 44.1 → 32 | bridge↑k0 · 2/3 | 12.000 | 89.5 | 0.76 | 218.5 | 1.86 | | | 44.1 → 48 | bridge↑k0 | 16.538 | 38.0 | 0.43 | 96.0 | 1.09 | | -| 44.1 → 96 | bridge↑k0 · ↑2 | 16.538 | 27.0 | 0.59 | 65.0 | 1.43 | | -| 44.1 → 192 | bridge↑k0 · ↑2 · ↑2 | 16.538 | 17.5 | 0.62 | 39.5 | 1.50 | | -| 44.1 → 384 | bridge↑k0 · ↑2 · ↑2 · ↑2 | 16.538 | 11.8 | 0.64 | 25.8 | 1.53 | | -| 88.2 → 8 | ↓2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 319.6 | 1.78 | 743.6 | 4.54 | ⚑ | -| 88.2 → 12 | ↓2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 214.7 | 1.34 | 510.8 | 3.44 | ⚑ | -| 88.2 → 16 | ↓2 · bridge↑k0 · ↓3 | 6.000 | 175.8 | 1.05 | 420.8 | 2.69 | ⚑ | -| 88.2 → 24 | ↓2 · bridge↑k0 · ↓2 | 9.000 | 111.5 | 0.86 | 272.2 | 2.24 | ⚑ | -| 88.2 → 32 | ↓2 · bridge↑k0 · 2/3 | 12.000 | 98.2 | 0.80 | 247.7 | 2.07 | | -| 88.2 → 48 | ↓2 · bridge↑k0 | 18.000 | 62.8 | 0.71 | 175.9 | 2.05 | | +| 44.1 → 96 | bridge↑k0 · ↑2 | 16.538 | 30.5 | 0.65 | 79.5 | 1.72 | | +| 44.1 → 192 | bridge↑k0 · ↑2 · ↑2 | 16.538 | 22.8 | 0.72 | 50.2 | 1.82 | | +| 44.1 → 384 | bridge↑k0 · ↑2 · 4/3 · ↑3 | 16.538 | 17.2 | 0.74 | 37.5 | 1.92 | | +| 88.2 → 8 | ↓2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 375.7 | 2.18 | 809.7 | 5.57 | ⚑ | +| 88.2 → 12 | ↓2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 260.1 | 1.72 | 558.8 | 4.22 | ⚑ | +| 88.2 → 16 | ↓2 · bridge↑k0 · ↓3 | 6.000 | 200.3 | 1.24 | 446.9 | 2.85 | ⚑ | +| 88.2 → 24 | ↓2 · bridge↑k0 · ↓2 | 9.000 | 141.3 | 1.11 | 370.8 | 3.05 | ⚑ | +| 88.2 → 32 | ↓2 · bridge↑k0 · 2/3 | 12.000 | 121.2 | 1.00 | 305.3 | 2.55 | | +| 88.2 → 48 | ↓2 · bridge↑k0 | 18.000 | 66.5 | 0.76 | 179.6 | 2.10 | | | 88.2 → 96 | bridge↑k1 | 33.075 | 38.0 | 0.22 | 96.0 | 0.54 | | -| 88.2 → 192 | bridge↑k1 · ↑2 | 33.075 | 27.0 | 0.29 | 65.0 | 0.72 | | -| 88.2 → 384 | bridge↑k1 · ↑2 · ↑2 | 33.075 | 17.5 | 0.31 | 39.5 | 0.75 | | -| 176.4 → 8 | ↓2 · ↓2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 396.8 | 1.81 | 864.9 | 4.60 | ⚑ | -| 176.4 → 12 | ↓2 · ↓2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 266.2 | 1.37 | 591.6 | 3.49 | ⚑ | -| 176.4 → 16 | ↓2 · ↓2 · bridge↑k0 · ↓3 | 6.000 | 214.4 | 1.08 | 481.5 | 2.74 | ⚑ | -| 176.4 → 24 | ↓2 · ↓2 · bridge↑k0 · ↓2 | 9.000 | 137.3 | 0.89 | 320.0 | 2.30 | ⚑ | -| 176.4 → 32 | ↓2 · ↓2 · bridge↑k0 · 2/3 | 12.000 | 123.0 | 0.84 | 283.5 | 2.13 | | -| 176.4 → 48 | ↓2 · ↓2 · bridge↑k0 | 18.000 | 79.3 | 0.75 | 207.2 | 2.14 | | -| 176.4 → 96 | ↓2 · bridge↑k1 | 36.000 | 62.8 | 0.36 | 175.9 | 1.03 | | +| 88.2 → 192 | bridge↑k1 · ↑2 | 33.075 | 30.5 | 0.32 | 79.5 | 0.86 | | +| 88.2 → 384 | bridge↑k1 · ↑2 · ↑2 | 33.075 | 22.8 | 0.36 | 50.2 | 0.91 | | +| 176.4 → 8 | ↓2 · ↓2 · bridge↑k0 · ↓3 · ↓2 | 3.000 | 474.9 | 2.22 | 997.2 | 5.66 | ⚑ | +| 176.4 → 12 | ↓2 · ↓2 · bridge↑k0 · ↓2 · ↓2 | 4.500 | 326.3 | 1.76 | 698.5 | 4.31 | ⚑ | +| 176.4 → 16 | ↓2 · ↓2 · bridge↑k0 · ↓3 | 6.000 | 283.0 | 1.32 | 551.6 | 2.94 | ⚑ | +| 176.4 → 24 | ↓2 · ↓2 · bridge↑k0 · ↓2 | 9.000 | 196.4 | 1.18 | 440.6 | 3.15 | ⚑ | +| 176.4 → 32 | ↓2 · ↓2 · bridge↑k0 · 2/3 | 12.000 | 162.5 | 1.08 | 363.2 | 2.66 | | +| 176.4 → 48 | ↓2 · ↓2 · bridge↑k0 | 18.000 | 108.7 | 0.88 | 295.4 | 2.44 | | +| 176.4 → 96 | ↓2 · bridge↑k1 | 36.000 | 66.5 | 0.38 | 179.6 | 1.05 | | | 176.4 → 192 | bridge↑k2 | 66.150 | 38.0 | 0.11 | 96.0 | 0.27 | | -| 176.4 → 384 | bridge↑k2 · ↑2 | 66.150 | 27.0 | 0.15 | 65.0 | 0.36 | | - -Count: 72 + 20 + 45 + 45 = 182 = 14 × 13 ordered pairs, every one covered; -38 carry ⚑. The generator (a scratch script, to be committed as -`rational/tools/coverage/matrix.py` at M4 and re-run by the matrix test's -reference step) is deterministic: the same lattice (without a single -4th-band stage, decision 6), the same harris estimate, the same selection -(the MAC minimum, then the fewest stages whose rational part is within -10 % of its rational part around the same `bridge` stage, then latency), -the same flag rule. +| 176.4 → 384 | bridge↑k2 · ↑2 | 66.150 | 30.5 | 0.16 | 79.5 | 0.43 | | + +Count: 72 + 20 + 45 + 45 = 182 = 14 × 13 ordered pairs, every one covered +and measured; 38 carry ⚑. The generator, `rational/tools/coverage/matrix.py` +(committed at M4), is deterministic: the lattice of 2^a · 3^b multiples of +each family's base as intermediates (b ≤ 3; without a single 4th-band +stage, decision 6), the stage lengths from the numpy search on the +16384-point grid at each stage's design divisor (the same criterion as the +M2 pins; its `pins` mode prints `design.h`'s relaxation tables), the MACs +per output as `stage.h` trims its rows, the same selection (the MAC +minimum, then the fewest stages whose rational part is within 10 % of its +rational part around the same `bridge` stage, then latency), the same +flag rule. `matrix.py md` prints 3.3–3.6, `rows` writes +`tests/coverage/matrix_rows.h` (the 182 rows as an X-macro with every +stage's divisor and the exact MACs and latency per profile, which +`test_matrix.cpp` pins), `diff` lists the rows that differ from this +file's tables, `rule` the rows where the fewest-stages rule bit. ## 4. Layout @@ -608,12 +665,17 @@ rational/ │ ├── stage.h basic_stage: table (per phase or per decimating branch, trimmed to the nonzero span; │ │ symmetry halving deferred to the codegen levers after M6, as bridge's M7 lever 3), the │ │ schedule, process/pull/flush/reset, the exact-rational latency -│ ├── chain.h the engine's named chains: converter = tap::dsp::chain, aliases +│ ├── chain.h basic_chain: tap::dsp::chain over basic_stage, each stage at its design +│ │ divisor (3.1); the 20 named multi-stage chains of 3.3 / 3.4 (up_2_up_2 … down_3_down_8_down_2) │ └── converter.h basic_converter = basic_stage under the family's converter name, with the │ float / Q15 / Q31 aliases (converter, converter_q15, converter_q31) ├── tests/ test_ratio, test_design, test_stage, test_chain, test_matrix, test_fixed_point, │ │ test_cross_validation, test_output_hash, test_capi, bare_metal_main.cpp -│ └── reference/ committed scipy vectors (make_reference_vectors.py in tools/reference/) +│ ├── reference/ committed scipy vectors (make_reference_vectors.py in tools/reference/), the +│ │ single stages and the sequenced-upfirdn chain vectors +│ ├── coverage/ matrix_rows.h, generated by tools/coverage/matrix.py: the 182 rows with +│ │ divisors and exact pins per profile +│ └── support/ bridge_stage.h, bridge's converter as a sync_stage for the cross rows (4.2) ├── tools/reference/ make_reference_vectors.py; tools/coverage/matrix.py (section 3's generator) ├── bench/ baselines.json (m33 / m55 / hexagon), icount/{CMakeLists.txt, icount_main.cpp} ├── capi/ tap_sr_rational_capi.{h,cpp} -> libtap_sr_rational_capi @@ -648,12 +710,14 @@ Six public headers (`tests/CMakeLists.txt:10-11` gains nothing. **C ABI (4.4).** `tap_sr_rational_create(int chain, int profile, unsigned -channels)`, where `chain` is an enumerator naming one of **the 34 -within-family chains** of 3.3/3.4 — the 26 of the 48 family and the 8 of -the 44.1 family, one enumerator per distinct chain (`TAP_SR_RATIONAL_UP_2`, -`TAP_SR_RATIONAL_UP_2_UP_2`, `TAP_SR_RATIONAL_RATIO_2_3`, -`TAP_SR_RATIONAL_DOWN_8_DOWN_2`, …; the ↑2 chain serves both families, so -the set is the union of the two tables' chain columns, 34 names) — a +channels)`, where `chain` is an enumerator naming one of **the 28 +within-family chains** of 3.3/3.4 — the 26 of the 48 family and the two +the 44.1 family adds (its 8/1 and 1/8), one enumerator per distinct chain +(`TAP_SR_RATIONAL_UP_2`, `TAP_SR_RATIONAL_UP_2_UP_2`, +`TAP_SR_RATIONAL_RATIO_2_3`, `TAP_SR_RATIONAL_DOWN_8_DOWN_2`, …; the set is +the union of the two tables' chain columns: 8 one-stage, 20 multi-stage, +the 20 `chain.h` names as types; v0.2 counted 34 before the union was +taken) — a chain, never a rate pair and never a bare rate, so D12 holds at the C boundary as `bridge`'s `int direction` does (`../bridge/capi/tap_sr_bridge_capi.h:29`); the single-stage ratios are @@ -691,16 +755,28 @@ As `bridge` (`../bridge/PLAN.md` §6), typed over `float` / `int16_t` / is scipy (1) plus `decimate.h`'s battery as a second golden at ↓2 / ↓3 / ↓6 (same substrate, different design: a disagreement above the two designs' documented difference is a finding). -3. **The coverage-matrix test.** For every one of the 182 pairs: build the - chain of 3.3–3.6 in `double`, measure f_pass (last −δ point), A (worst - stopband product over a swept-tone battery, aliases and images - separately as `test_converter.cpp:296-316` does), δ (passband ripple - over a fine grid), MACs per output (counted by instrumented kernels, not - estimated) and latency (impulse position against `latency_output_frames()`), - and pin them in a committed table that the test reads back. Cross-family - rows build `bridge` from `tests/` (allowed by 4.2); rows at k > 0 are - skipped with a named reason until the 2.2 follow-up lands, and the test - fails if a row is skipped for any other reason. +3. **The coverage-matrix test** (`test_matrix.cpp`, M4). For every one + of the 182 pairs: build the chain of 3.3–3.6 in `double` from the + generated row table (`tests/coverage/matrix_rows.h`); pin MACs per + output (the stages' trimmed-row counts scaled to the output rate — what + the kernels execute, not an estimate) and latency exactly, as + rationals, at all four profiles; check a within-family row is the named + chain (its compile-time divisors are the table's); measure the promise + (2.1) with a tone battery at each profile — seven passband tones and up + to five stopband tones per row, each an exact bin of an analysis window + whose length makes every rate of the chain a bin too, so a rectangular + window measures every image and alias candidate (|k R_j ± f| folded, + over every rate R_j of the chain) leakage-free beside the 0 dB tone: + every candidate that lands at or below f_pass must be at or below −A, + the passband gain within the stages' summed ripple candidates; check + accounting from three positions, the impulse within half a frame of the + latency, and flush. Cross-family rows build `bridge` from `tests/` + (`support/bridge_stage.h`, allowed by 4.2); every row is measured, none + skipped (2.2 had landed). Measured at M4: worst candidate −71.3 dB at + `economy`, −71.8 at `super_economy`, −71.5 at `balanced`, −121.6 at + `transparent`; worst passband deviation 0.0087 / 0.0081 / 0.0070 / + 0.00002 dB (about 1700 tones per profile, 24 s on a host; a host suite, + excluded on the QEMU legs). Plus: @@ -738,7 +814,7 @@ Plus: | M1 | **Skeleton + ratio types + design**: `rational/` tree (section 4), `tap::sr::rational` target, the family tests extended to three engines, `ratio` with its `static_assert`s, `profile`, `design_stage`. **Done** (2026-10-01; three of the six public headers — `rational.h`, `ratio.h`, `design.h` — the header count pinned at 3 until M3/M4 add the rest) | Configure and build from the root and from `cmake -S rational` ✓; the four 4.2 checks and `VersionMacrosAgree` pass for all three engines ✓ (`check_includes.cmake` now takes both siblings); `ratio<5, 1>`, `ratio<4, 2>`, `ratio<2, 2>` fail to compile with the charter's message ✓ (`tests/compile_fail/`, a `try_compile` project run as `rational.Ratio.ChartersFailToCompileWithTheMessage`, on the cross legs with their toolchain file); `BadProfilesThrow` ✓; the M33 leg runs the battery in 134 s with the transparent search excluded | no | | M2 | **Design spike**: `notebooks/design_spike.ipynb`, executed, pins N per (single-stage ratio ∈ {2, 3, 6, 8, 3/2, 2/3, 4/3, 3/4, 8/3, 3/8} × profile; the 4th-band row is measured for the record only, decision 6, and serves 4/3 and 3/4) by the ≥ 1 dB margin criterion on a fine grid, with measured worst stopband and ripple; `test_design.cpp` enforces the pins. **Done** (2026-10-02, v0.5: the notebook is the independent numpy leg from the same published math, `profile::taps_per_branch` carries the pins per band, `design_stage` reads them, a custom profile without pins is searched on the same grid) | The table below filled ✓; every pinned N of the form 2mL − 1 ✓ (mixed: T = ⌈N / L⌉ per phase); measured A ≥ spec + 1 dB on 16384 points ✓ (−71.1 … −72.8 dB at 70 dB, −121.1 … −121.7 dB at 120 dB) with m − 1 missing it ✓; ripple ≤ 2.3's candidate ✓ (≤ 0.0025 / 0.00001 dB); the scipy `upfirdn` end-to-end check of the economy half-band decimator: a tone just past the stopband edge aliases at −72.1 dB, passband tones within 0.001 dB ✓ | no | | M3 | **Single stages, float golden leg**: `basic_stage` for ↑2 / ↓2 (half-band) and ↑3 / ↓3 (third-band) first, then the rest of the vocabulary; both call shapes; `flush`; scipy vectors committed. **Done** (2026-10-02, v0.6: the whole vocabulary at once, since the two machines are generic — the L-phase schedule machine with trimmed phase rows for interpolators and mixed ratios, the M-branch commutator for decimators, its nonzero branches summed under one finalize through DspTap's `accumulate_row` (tap/DspTap#51, the substrate addition M3 needed); MACs per output are the design's nonzero counts for every interpolator and decimator, the half-band decimator 23 of its 43 taps) | Scipy vectors sample-for-sample ✓ (16 vectors, every ratio at `economy` and the by-2 pair at `transparent`; float within 6e-8 of the float64 reference, the 3e-5 tolerance is bridge's); exhaustive phase sweeps ✓ (the impulse reproduces the table bit for bit from every input phase, every format; accounting exact from every position of every superblock); `PullMatchesProcessBitExact` ✓; chunking invariance ✓ (every format); `decimate.h` second-golden agreement at ↓2 / ↓3 / ↓6 within the documented design difference ✓; latency equals (N − 1)/2 at the higher rate by impulse ✓ (every ratio, the exact rational (N − 1)/(2M) at the output rate); DC gain exactly 1 in every format ✓; flush equals zero padding bit for bit ✓ | no | -| M4 | **Chains and the coverage matrix**: `chain.h`'s named chains, `tools/coverage/matrix.py` committed, `test_matrix.cpp` pinning (f_pass, A, δ, MACs/out, latency) for all 182 rows | Every within-family row and every k = 0 cross row measured and pinned; k > 0 rows skipped by name only; chain vectors against sequenced upfirdn; `frames_needed` / `outputs_for` exact from every position of every chain | **k > 0 rows (12 of them: every row of 3.5/3.6 whose chain names k1 or k2) wait; the other 170 do not** | +| M4 | **Chains and the coverage matrix**: `chain.h`'s named chains, `tools/coverage/matrix.py` committed, `test_matrix.cpp` pinning (f_pass, A, δ, MACs/out, latency) for all 182 rows. **Done** (2026-10-02, v0.7: `basic_chain` constructs each stage at its design divisor (3.1) over DspTap's `chain<>`, whose `flush` landed for it (tap/DspTap#52); the relaxation tables in `design.h`; the generator re-run on the pinned lengths, section 3 regenerated — 35 rows changed chain, the ledger in S1 — and the 20 named multi-stage chains follow it) | Every row measured and pinned, the k > 0 rows included (2.2 had landed, so nothing was skipped) ✓; chain vectors against sequenced upfirdn ✓ (seven chains, float within 3e-5 and double within 1e-7 of the float64 reference); a chain equals its stages run in sequence bit for bit in every format ✓; `frames_needed` / `outputs_for` exact from every position of every named chain ✓; flush equals zero padding bit for bit ✓; the tone battery's numbers in section 5 | no | | M5 | **Fixed-point profiles**: Q15 and Q31 datapaths through the traits; per-branch quantization; bit-pinned tables; cross-precision numbers | Q31 within the format floor of float on the reference noise (`bridge`: 5e−8, `../bridge/PLAN.md` §8); Q15 format-limited numbers stated per stage; exact-unity row sums; wrap safety; the Q15 half-band's zero taps absent from the dot (counted MACs equal the nonzero count) | no | | M6 | **C ABI, notebook, icount baselines**: `libtap_sr_rational_capi`, `tap_sr_rational_py.py`, `matrix.ipynb` executed through the C ABIs (bridge's for cross rows), `bench/icount/` with baselines on M33 / M55 / Hexagon, README icount table, `CApi.VersionIsBitPacked` | Notebook measures the shipping C++ and reproduces the pinned matrix numbers; ratchet green two-sided on three targets; `nm -D` symbol set recorded; **family version 0.5.0** (decision 10, R11: all three umbrella headers and the root `project()` bump together, D13) | notebook's k > 0 cells wait | @@ -765,14 +841,13 @@ M2 verified that claim (`Design.UpAndDownOfOneBandAreTheSamePrototype`: the vectors are bit-identical) before relying on it, and a mixed ratio's design is its larger factor's band (2/3 and 3/2 share the third-band design; `test_design.cpp` pins it). The mixed stages' MACs per output are -their T; a 2/3 stage at `economy` costs 33, not the 26 the v0.2 arithmetic -in 2.1 estimated, and the matrix's MAC figures are regenerated from these -counts at M4 (risk S1). +their trimmed rows' lengths over L — a 2/3 stage at `economy` costs 32.5, +not the 26 the v0.2 arithmetic in 2.1 estimated — and the matrix's figures +were regenerated from these counts at M4 (v0.7, section 3; risk S1). **Order and gating.** M0 lands in DspTap and the family bumps its pin -(`../CLAUDE.md`, "Substrate discipline"); M1–M3 are done; M5 is independent of -the 2.2 follow-up; M4's 12 k > 0 rows and M6's corresponding notebook -cells wait for it and are the only items that do. Every milestone's PR +(`../CLAUDE.md`, "Substrate discipline"); M1–M4 are done; the 2.2 +follow-up landed before M4, so nothing waits for it. Every milestone's PR carries its measurements, as `bridge`'s M7 entries do. ## 7. Non-goals @@ -808,7 +883,7 @@ carries its measurements, as `bridge`'s M7 entries do. | # | Risk | Mitigation | |---|---|---| -| S1 | The harris estimates mislead the factoring policy (R3): a pinned N lands on the other side of a stage-count decision | M2 pins before M4 chooses; `matrix.py` is re-run on the pinned Ns and the matrix table is regenerated from measured lengths, not estimates; a flip is recorded in this plan's ledger | +| S1 | The harris estimates mislead the factoring policy (R3): a pinned N lands on the other side of a stage-count decision | M2 pins before M4 chooses; `matrix.py` is re-run on the pinned Ns and the matrix table is regenerated from measured lengths, not estimates; a flip is recorded in this plan's ledger. **Ledger (M4, v0.7): 35 of the 182 rows changed chain** (`matrix.py diff` against the v0.6 tables). Within the 48 family, eleven ratios: 8/1 ↑2 · ↑2 · ↑2 → ↑2 · 4/3 · ↑3; 12/1 ↑2 · ↑2 · ↑3 → ↑2 · ↑6; 16/1 ↑2 · ↑2 · ↑2 · ↑2 → ↑2 · ↑8; 24/1 ↑3 · ↑8 → ↑2 · ↑6 · ↑2; 32/1 ↑2 · ↑2 · ↑8 → ↑2 · ↑8 · ↑2; 48/1 ↑2 · ↑3 · ↑8 → ↑2 · ↑8 · ↑3; 1/8 ↓2 · ↓2 · ↓2 → ↓3 · 3/4 · ↓2; 1/12 ↓3 · ↓2 · ↓2 → ↓6 · ↓2; 1/16 unchanged; 1/24 ↓8 · ↓3 → ↓2 · ↓6 · ↓2; 1/32 ↓8 · ↓2 · ↓2 → ↓2 · ↓8 · ↓2; 1/48 ↓8 · ↓3 · ↓2 → ↓3 · ↓8 · ↓2 (22 rows). Within the 44.1 family, 8/1 ↑2 · ↑2 · ↑2 → ↑3 · 8/3, 16/1 ↑2 · ↑2 · ↑2 · ↑2 → ↑2 · ↑8 and 1/8 ↓2 · ↓2 · ↓2 → 3/8 · ↓3 (5 rows). Cross-family, eight rows: 192 → 22.05 (↓2 · ↓2 → ↓3 · 3/4 ahead of `bridge`), 384 → 11.025 (↓8 → ↓2 · ↓2 · ↓2), 384 → 22.05 (↓6 · 3/4 → ↓8), 384 → 44.1 (↓2 · ↓2 · ↓2 → ↓3 · 3/4 · ↓2), 11.025 → 384 (↑8 → ↑2 · ↑2 · ↑2 after `bridge`), 22.05 → 192 (↑2 · ↑2 → 4/3 · ↑3), 22.05 → 384 (4/3 · ↑6 → ↑8), 44.1 → 384 (↑2 · ↑2 · ↑2 → ↑2 · 4/3 · ↑3). The cause is 2.1's: a relaxed half-band saturates at m = 7 while a relaxed third-band stage is m = 5. The rules were not changed; a maintainer who prefers integer-factor chains at a few per cent adds a tie-break to `best_chain` and regenerates | | S2 | The exact-zero contract breaks under an accelerated kernel or a future compensated design (`design_prototype_compensated` convolves with a rect, `kaiser.h:200-216`, which fills the zeros) | The property test is in DspTap and runs on every leg; the compensated variant is explicitly outside R4 (an L-th-band design with k·fs zeros is a different filter and would be its own documented decision) | | S3 | Chain latency at low rates (1–2.5 ms at 8–11 kHz, 3.3) surprises a consumer expecting `bridge`'s 0.4–0.8 ms | Latency is a pinned number per pair (R7) and printed by the notebook; the minimum-phase lever stays deferred as `bridge`'s is, pulled by a latency need | | S4 | The dipping cross rows (3.1) are used as if they were cheap | Mitigated (decision 3, R14): the matrix carries MACs per output for every row and flags ⚑ the 38 rows whose `bridge` stage runs at ≥ 2× the output rate, "covered, not recommended"; the README and the notebook carry the same flag | diff --git a/rational/README.md b/rational/README.md index e6a3224..66531a6 100644 --- a/rational/README.md +++ b/rational/README.md @@ -14,7 +14,7 @@ substrate ([DspTap](https://github.com/tap/DspTap): the L-th-band designer and the stage composition landed there first, with the float/Q15/Q31 sample-format traits, the dot kernels and the row-sum quantization). -> **Status: M3 of [PLAN.md](PLAN.md) section 6.** The single stages ship +> **Status: M4 of [PLAN.md](PLAN.md) section 6.** The single stages ship > for every ratio of the vocabulary — ↑2, ↓2, ↑3, ↓3, ↑6, ↓6, ↑8, ↓8 and the > mixed 3/2, 2/3, 4/3, 3/4, 8/3, 3/8 — as `converter>` (float, > the golden model pinned sample-for-sample against committed scipy @@ -25,8 +25,16 @@ sample-format traits, the dot kernels and the row-sum quantization). > ([`notebooks/design_spike.ipynb`](notebooks/design_spike.ipynb)); its > structural zeros are never multiplied, so the half-band decimator costs > 23 MACs per output of its 43 taps and an interpolator's centre phase is a -> copy. Chains and the 14 × 14 coverage matrix (M4), the fixed-point floors -> (M5), the C ABI and the ratchet baselines (M6) follow. The plan is +> copy. The chains ship too (`chain.h`): `basic_chain` runs stages +> in sequence with each stage designed at its own rate — the plan's design +> divisor, with pinned lengths per divisor — and the 20 named multi-stage +> chains of the coverage matrix (`up_2_up_2`, `down_3_down_8_down_2`, …), +> and the 14 × 14 matrix of PLAN.md section 3 — one chain for every ordered +> pair of the family's fourteen rates, the cross-family rows through +> `bridge` — is generated from the pinned lengths and pinned row by row by +> `tests/test_matrix.cpp` (MACs per output and latency exactly, the +> passband and the stopband promise by a tone battery). The fixed-point +> floors (M5), the C ABI and the ratchet baselines (M6) follow. The plan is > authoritative: charter, the decisions R1–R16, the generated matrix, > layout, test strategy, non-goals and risks. @@ -45,8 +53,13 @@ std::size_t made = down.process(in, n_in, out.data()); // noexcept, alloc-free std::vector tail(down.flush_output_frames() * 2); down.flush(tail.data()); // end of stream -converter to_32k(1); // 48 -> 32 kHz: one mixed stage, 33 MACs per output +converter to_32k(1); // 48 -> 32 kHz: one mixed stage, 32.5 MACs per output auto lat = to_32k.latency_output_frames(); // exact: {32, 3} output frames = 32 / 3 + +down_3_down_8_down_2 to_8k(1); // 384 -> 8 kHz: the matrix's 1/48 chain, 373 MACs per output +// each stage designed at its own rate: divisors 16, 2, 1 of the chain's lowest rate (PLAN.md 3.1) +static_assert(to_8k.k_divisors[0] == tap::dsp::exact_ratio{16, 1}); +chain to_32k_from_12k(2); // 12 -> 32 kHz: 8/3 as the matrix factors it, stereo ``` The ratio is a compile-time type (`ratio`, L and M of the form @@ -72,8 +85,15 @@ message). `pull(out, n, pop_fn)` is the callback-driven shape and format. - `converter.h` — `basic_converter` and the `converter`, `converter_q15`, `converter_q31` aliases. -- `rational.h` — the umbrella, `TAP_SR_VERSION_*` (0.4.0; 0.5.0 at M6) and - the named ratios of the vocabulary (`up_2` … `ratio_3_8`). +- `chain.h` — `basic_chain`: DspTap's `chain<>` over + `basic_stage`, constructing each stage at the profile relaxed by the + stage's design divisor (its lower rate over the chain's lowest, the + largest 2^a · 3^b at or below it; `profile::relaxed`, the pinned + relaxation tables of `design.h`); `chain` / `chain_q15` / + `chain_q31`; `macs_per_output()` exact; and the 20 named multi-stage + chains of the coverage matrix (`up_2_up_2` … `down_3_down_8_down_2`). +- `rational.h` — the umbrella and `TAP_SR_VERSION_*` (0.4.0; 0.5.0 at M6); + the named ratios of the vocabulary (`up_2` … `ratio_3_8`) are `ratio.h`'s. ## The boundaries are identity, not policy diff --git a/rational/include/tap/sr/rational/chain.h b/rational/include/tap/sr/rational/chain.h new file mode 100644 index 0000000..bd3c81b --- /dev/null +++ b/rational/include/tap/sr/rational/chain.h @@ -0,0 +1,212 @@ +/// @file chain.h +/// @brief basic_chain: stages in sequence at one profile, each designed at its own rate; the named chains. +// SPDX-License-Identifier: MIT +// Copyright 2026 Timothy Place and the SampleRateTap contributors +// +// A conversion by a ratio no single stage of the vocabulary serves is a +// chain of stages (PLAN.md section 3): tap::dsp::chain<> over basic_stage, +// written by the caller as a type and never looked up from a rate pair +// (D12). What this header adds to the DspTap helper is the profile rule: +// the chain's profile declares f_pass = p * r_min at the chain's lowest +// rate (3.1), so each stage is designed at p over its own DESIGN DIVISOR, +// its lower rate over r_min (profile::relaxed; the divisors are the pinned +// rows of the relaxation tables), and the chain constructs its stages that +// way. Within a family the divisor is an exact 2^a 3^b; in general it is +// the largest such number at or below the quotient (a passband at or above +// f_pass, so the stage still meets 2.1(a)), or the quotient itself below 1 +// (the stage at bridge's rate under a 48-family r_min, 147/160; that case +// only arises in a chain through bridge, which the caller composes from +// tap::dsp::chain with profile::relaxed, as bridge/examples does). +// +// Contract: tap::dsp::chain's (process in chunks, outputs_for and +// frames_needed exact, flush = zero padding, reset, the latency as the +// exact rational sum at the output rate) over basic_stage's; noexcept and +// allocation-free after construction (R9). macs_per_output() is the chain's +// cost per output frame per channel: each stage's trimmed-row count scaled +// by its output rate over the chain's, exact. +// +// The named chains are the coverage matrix's within-family chains (3.3 / +// 3.4, generated from the pinned lengths by tools/coverage/matrix.py and +// pinned per row by tests/test_matrix.cpp): one alias per distinct chain of +// two or more stages, named by its stages in order; a one-stage chain is +// basic_converter. The names are the C ABI's enumerators (M6). +#pragma once + +#include +#include +#include +#include + +#include "tap/dsp/chain.h" +#include "tap/dsp/sample_traits.h" +#include "tap/sr/rational/design.h" +#include "tap/sr/rational/ratio.h" +#include "tap/sr/rational/stage.h" + +namespace tap::sr::rational { + + namespace detail { + + using tap::dsp::exact_ratio; + + constexpr bool ratio_less(exact_ratio a, exact_ratio b) noexcept { + return a.num * b.den < b.num * a.den; + } + constexpr exact_ratio ratio_over(exact_ratio a, exact_ratio b) noexcept { + return exact_ratio{a.num * b.den, a.den * b.num}; + } + + /// The design divisor of a stage whose lower rate over the chain's + /// lowest is q (see the file header): the largest 2^a 3^b at or + /// below q, or q itself below 1. + constexpr exact_ratio design_divisor(exact_ratio q) noexcept { + if (ratio_less(q, exact_ratio{1, 1})) { + return q; + } + std::uint64_t best = 1; + for (std::uint64_t three = 1; three <= 27; three *= 3) { + for (std::uint64_t d = three; d * q.den <= q.num; d *= 2) { + best = d > best ? d : best; + } + } + return exact_ratio{best, 1}; + } + + /// The rates of a chain relative to its input: rates[i] before stage + /// i, rates[n] the output; and the lowest of them. + template + struct chain_rates { + static constexpr std::size_t k_stages = sizeof...(Rs); + static constexpr std::array k_rates = [] { + std::array r{}; + r[0] = exact_ratio{1, 1}; + std::size_t i = 0; + ((r[i + 1] = r[i] * exact_ratio{Rs::k_up, Rs::k_down}, ++i), ...); + return r; + }(); + static constexpr exact_ratio k_lowest = [] { + exact_ratio low = k_rates[0]; + for (const auto& r : k_rates) { + low = ratio_less(r, low) ? r : low; + } + return low; + }(); + /// Stage i's design divisor: its lower rate over the lowest. + static constexpr std::array k_divisors = [] { + std::array d{}; + for (std::size_t i = 0; i < k_stages; ++i) { + const exact_ratio lower = ratio_less(k_rates[i], k_rates[i + 1]) ? k_rates[i] : k_rates[i + 1]; + d[i] = design_divisor(ratio_over(lower, k_lowest)); + } + return d; + }(); + }; + + } // namespace detail + + /// A chain of stages at the ratios Rs, in order, over sample format S; + /// see the file header. Constructs each stage at the profile relaxed by + /// the stage's design divisor (k_divisors); everything else is + /// tap::dsp::chain's. + template + class basic_chain : public tap::dsp::chain...> { + using base = tap::dsp::chain...>; + + public: + static_assert(sizeof...(Rs) >= 1, "tap::sr::rational::basic_chain: a chain has at least one stage"); + + static constexpr std::size_t k_stages = sizeof...(Rs); + /// The design divisor of every stage (the file header; PLAN.md 3.1). + static constexpr std::array k_divisors = + detail::chain_rates::k_divisors; + + /// Allocates every stage's state and designs its table; may throw + /// (a bad profile, zero channels). Setup time only (R9). + explicit basic_chain(std::size_t channels = 1, const profile& p = profile::economy()) + : basic_chain(channels, p, std::make_index_sequence{}) {} + + /// MACs per output frame per channel: the sum over the stages of + /// macs_per_output() at the stage's output rate, scaled to the + /// chain's output rate; exact, and its double. + tap::dsp::exact_ratio macs_per_output_exact() const noexcept { + using tap::dsp::exact_ratio; + constexpr auto& rates = detail::chain_rates::k_rates; + exact_ratio total{0, 1}; + [&](std::index_sequence) { + ((total = total + + exact_ratio{this->template stage().macs_per_superblock(), + std::tuple_element_t>::k_up == 1 + ? 1 + : std::tuple_element_t>::k_up} + * detail::ratio_over(rates[I + 1], rates[k_stages])), + ...); + }(std::make_index_sequence{}); + return total; + } + double macs_per_output() const noexcept { return macs_per_output_exact().value(); } + + private: + template + basic_chain(std::size_t channels, const profile& p, std::index_sequence) + : base(channels, basic_stage(channels, p.relaxed(k_divisors[I]))...) {} + }; + + /// The float chain at the ratios Rs (the golden-model profile), and the + /// Q15 / Q31 chains (R8). + template + using chain = basic_chain; + template + using chain_q15 = basic_chain; + template + using chain_q31 = basic_chain; + + // ANCHOR: rational_named_chains + /// The named chains of the coverage matrix (PLAN.md 3.3 / 3.4), one per + /// distinct chain of two or more stages, named by its stages in order + /// (a one-stage chain is basic_converter): the 48 kHz family's + /// twenty, plus the two the 44.1 kHz family's lattice chooses for 8/1 + /// and 1/8 (its r_min * 3 intermediate, 33.075 kHz, where the 48 family + /// has r_min * 8/3). Ratios in comments are the chain's L/M. + template + using up_2_up_2 = basic_chain; ///< 4/1 + template + using up_2_up_3 = basic_chain; ///< 6/1 + template + using up_2_ratio_4_3_up_3 = basic_chain; ///< 8/1 (48 family) + template + using up_3_ratio_8_3 = basic_chain; ///< 8/1 (44.1 family) + template + using up_2_up_6 = basic_chain; ///< 12/1 + template + using up_2_up_8 = basic_chain; ///< 16/1 + template + using up_2_up_6_up_2 = basic_chain; ///< 24/1 + template + using up_2_up_8_up_2 = basic_chain; ///< 32/1 + template + using up_2_up_8_up_3 = basic_chain; ///< 48/1 + template + using up_2_ratio_4_3 = basic_chain; ///< 8/3 + template + using ratio_3_4_down_2 = basic_chain; ///< 3/8 + template + using down_2_down_2 = basic_chain; ///< 1/4 + template + using down_3_down_2 = basic_chain; ///< 1/6 + template + using down_3_ratio_3_4_down_2 = basic_chain; ///< 1/8 (48 family) + template + using ratio_3_8_down_3 = basic_chain; ///< 1/8 (44.1 family) + template + using down_6_down_2 = basic_chain; ///< 1/12 + template + using down_8_down_2 = basic_chain; ///< 1/16 + template + using down_2_down_6_down_2 = basic_chain; ///< 1/24 + template + using down_2_down_8_down_2 = basic_chain; ///< 1/32 + template + using down_3_down_8_down_2 = basic_chain; ///< 1/48 + // ANCHOR_END: rational_named_chains + +} // namespace tap::sr::rational diff --git a/rational/include/tap/sr/rational/design.h b/rational/include/tap/sr/rational/design.h index b42e034..978c459 100644 --- a/rational/include/tap/sr/rational/design.h +++ b/rational/include/tap/sr/rational/design.h @@ -7,15 +7,83 @@ #include #include #include +#include #include #include +#include "tap/dsp/chain.h" #include "tap/dsp/kaiser.h" #include "tap/dsp/nyquist.h" #include "tap/sr/rational/ratio.h" namespace tap::sr::rational { + /// The pinned taps per branch of a named profile at one design divisor + /// (profile::relaxed): m for the bands 2, 3, 4, 6, 8 of a stage whose + /// passband is the profile's over `divisor` times its own lower rate. + struct relaxed_pins { + tap::dsp::exact_ratio divisor; + std::array taps_per_branch; + }; + + // ANCHOR: rational_relaxations + /// The relaxation tables (tools/coverage/matrix.py `pins`, 2026-10-02; + /// verified against the shipping designer by test_design.cpp): for each + /// named profile, the pinned m per band at every design divisor a chain + /// of the coverage matrix uses (PLAN.md 3.1). The 1/1 row is the M2 + /// table; a divisor above 1 relaxes the passband (a stage further up a + /// chain, exact 2^a 3^b within a family); 147/160 is the one stage that + /// runs below a 48-family r_min, at bridge's rate, tightened. + inline constexpr std::array k_super_economy_relaxations = {{ + {{147, 160}, {11, 10, 10, 10, 10}}, + {{1, 1}, {9, 8, 8, 8, 8}}, + {{2, 1}, {7, 5, 5, 5, 5}}, + {{3, 1}, {7, 5, 5, 5, 5}}, + {{4, 1}, {7, 5, 5, 5, 5}}, + {{6, 1}, {4, 4, 4, 4, 4}}, + {{8, 1}, {4, 4, 4, 4, 4}}, + {{9, 1}, {3, 4, 4, 4, 4}}, + {{12, 1}, {3, 3, 3, 4, 4}}, + {{16, 1}, {3, 3, 3, 4, 4}}, + }}; + inline constexpr std::array k_economy_relaxations = {{ + {{147, 160}, {15, 14, 14, 14, 14}}, + {{1, 1}, {11, 11, 11, 12, 12}}, + {{2, 1}, {7, 5, 5, 5, 5}}, + {{3, 1}, {7, 5, 5, 5, 5}}, + {{4, 1}, {7, 5, 5, 5, 5}}, + {{6, 1}, {4, 5, 4, 4, 4}}, + {{8, 1}, {4, 4, 4, 4, 4}}, + {{9, 1}, {4, 4, 4, 4, 4}}, + {{12, 1}, {3, 3, 3, 4, 4}}, + {{16, 1}, {3, 3, 3, 4, 4}}, + }}; + inline constexpr std::array k_balanced_relaxations = {{ + {{147, 160}, {19, 19, 19, 20, 20}}, + {{1, 1}, {13, 13, 14, 14, 14}}, + {{2, 1}, {7, 5, 5, 5, 5}}, + {{3, 1}, {7, 5, 5, 5, 5}}, + {{4, 1}, {7, 5, 5, 5, 5}}, + {{6, 1}, {6, 5, 4, 4, 4}}, + {{8, 1}, {4, 4, 4, 4, 4}}, + {{9, 1}, {4, 4, 4, 4, 4}}, + {{12, 1}, {3, 3, 4, 4, 4}}, + {{16, 1}, {3, 3, 3, 4, 4}}, + }}; + inline constexpr std::array k_transparent_relaxations = {{ + {{147, 160}, {45, 46, 46, 46, 46}}, + {{1, 1}, {31, 25, 25, 25, 25}}, + {{2, 1}, {10, 8, 13, 13, 13}}, + {{3, 1}, {9, 8, 11, 11, 12}}, + {{4, 1}, {9, 8, 8, 7, 10}}, + {{6, 1}, {9, 8, 7, 7, 10}}, + {{8, 1}, {9, 7, 7, 7, 10}}, + {{9, 1}, {8, 7, 7, 7, 10}}, + {{12, 1}, {8, 7, 7, 7, 10}}, + {{16, 1}, {7, 7, 7, 7, 7}}, + }}; + // ANCHOR_END: rational_relaxations + // ANCHOR: rational_profile /// Quality profile: bridge's four names (R5), as (stopband attenuation A /// in dB, passband edge p as a fraction of the chain's LOWEST rate @@ -54,25 +122,66 @@ namespace tap::sr::rational { /// -121.1 .. -121.7 dB at transparent, passband ripple <= 0.0025 dB and /// <= 0.00001 dB (PLAN.md 2.3's candidates are 0.01 / 0.0001). A custom /// profile leaves its pins at 0 and design_stage searches instead. + /// + /// In a chain the profile is the CHAIN's: f_pass = p * r_min with r_min + /// the chain's lowest rate, so a stage whose lower rate is r is designed + /// at the fraction p * r_min / r of its own rate (PLAN.md 3.1, the + /// stages further up a chain are shorter). relaxed(d) is that stage's + /// profile: the passband over d, with the named profiles' pins at the + /// divisors the coverage matrix uses (the relaxation tables above; a + /// divisor without a row, or a custom profile, is searched). struct profile { double passband_frac = 3.0 / 8.0; ///< f_pass / r_min, in (0, 1/2) double stopband_atten_db = 70.0; ///< stopband target, dB /// Pinned taps per branch for the bands 2, 3, 4, 6, 8 (0: search). std::array taps_per_branch = {11, 11, 11, 12, 12}; + /// The pins at the other design divisors (relaxed()); empty for a + /// custom profile. + std::span relaxations = k_economy_relaxations; /// The speed-first default: 70 dB, 3/8 of the lowest rate (18 kHz at 48). static constexpr profile economy() noexcept { return {}; } /// Voice/comms tier: 70 dB, 1/3 (16 kHz at 48). Never a default. static constexpr profile super_economy() noexcept { - return {.passband_frac = 1.0 / 3.0, .stopband_atten_db = 70.0, .taps_per_branch = {9, 8, 8, 8, 8}}; + return {.passband_frac = 1.0 / 3.0, + .stopband_atten_db = 70.0, + .taps_per_branch = {9, 8, 8, 8, 8}, + .relaxations = k_super_economy_relaxations}; } /// 70 dB, flat to 19/48 (19 kHz at 48). static constexpr profile balanced() noexcept { - return {.passband_frac = 19.0 / 48.0, .stopband_atten_db = 70.0, .taps_per_branch = {13, 13, 14, 14, 14}}; + return {.passband_frac = 19.0 / 48.0, + .stopband_atten_db = 70.0, + .taps_per_branch = {13, 13, 14, 14, 14}, + .relaxations = k_balanced_relaxations}; } /// Pristine tier: 120 dB, 5/12 (20 kHz at 48). static constexpr profile transparent() noexcept { - return {.passband_frac = 5.0 / 12.0, .stopband_atten_db = 120.0, .taps_per_branch = {31, 25, 25, 25, 25}}; + return {.passband_frac = 5.0 / 12.0, + .stopband_atten_db = 120.0, + .taps_per_branch = {31, 25, 25, 25, 25}, + .relaxations = k_transparent_relaxations}; + } + + /// This profile for a stage whose lower rate is `divisor` times the + /// chain's lowest rate: the passband fraction over the divisor, the + /// pins from the relaxation table's row for it (all 0, a search, when + /// there is none), no further relaxations. relaxed(1) is this profile. + constexpr profile relaxed(tap::dsp::exact_ratio divisor) const noexcept { + if (divisor == tap::dsp::exact_ratio{1, 1}) { + return *this; + } + profile r; + r.passband_frac = passband_frac * static_cast(divisor.den) / static_cast(divisor.num); + r.stopband_atten_db = stopband_atten_db; + r.taps_per_branch = {0, 0, 0, 0, 0}; + r.relaxations = {}; + for (const auto& row : relaxations) { + if (row.divisor == divisor) { + r.taps_per_branch = row.taps_per_branch; + } + } + return r; } /// The passband edge in Hz for a chain whose lowest rate is r_min. diff --git a/rational/include/tap/sr/rational/ratio.h b/rational/include/tap/sr/rational/ratio.h index 5a2c695..def8427 100644 --- a/rational/include/tap/sr/rational/ratio.h +++ b/rational/include/tap/sr/rational/ratio.h @@ -128,4 +128,24 @@ namespace tap::sr::rational { "the composite rate is k_band times the lower rate for every ratio of the charter"); }; + /// The single-stage vocabulary of the 48 kHz family (PLAN.md section 1): + /// the integer factors and the mixed ratios one stage serves, as named + /// ratios. by_4 is a ratio the chain by 4 is named by, not a stage (R3, + /// decision 6: two half-bands). + using up_2 = ratio<2, 1>; + using down_2 = ratio<1, 2>; + using up_3 = ratio<3, 1>; + using down_3 = ratio<1, 3>; + using up_6 = ratio<6, 1>; + using down_6 = ratio<1, 6>; + using up_8 = ratio<8, 1>; + using down_8 = ratio<1, 8>; + /// The mixed ratios, named by L/M. + using ratio_3_2 = ratio<3, 2>; + using ratio_2_3 = ratio<2, 3>; + using ratio_4_3 = ratio<4, 3>; + using ratio_3_4 = ratio<3, 4>; + using ratio_8_3 = ratio<8, 3>; + using ratio_3_8 = ratio<3, 8>; + } // namespace tap::sr::rational diff --git a/rational/include/tap/sr/rational/rational.h b/rational/include/tap/sr/rational/rational.h index be159bf..b36f0c7 100644 --- a/rational/include/tap/sr/rational/rational.h +++ b/rational/include/tap/sr/rational/rational.h @@ -29,6 +29,7 @@ // chains and the coverage matrix follow (PLAN.md section 6, M4). #pragma once +#include "tap/sr/rational/chain.h" // IWYU pragma: export #include "tap/sr/rational/converter.h" // IWYU pragma: export #include "tap/sr/rational/design.h" // IWYU pragma: export #include "tap/sr/rational/ratio.h" // IWYU pragma: export @@ -37,27 +38,3 @@ #define TAP_SR_VERSION_MAJOR 0 #define TAP_SR_VERSION_MINOR 4 #define TAP_SR_VERSION_PATCH 0 - -namespace tap::sr::rational { - - /// The single-stage vocabulary of the 48 kHz family (PLAN.md section 1): - /// the integer factors and the mixed ratios one stage serves, as named - /// ratios. by_4 is a ratio the chain by 4 is named by, not a stage (R3, - /// decision 6: two half-bands). - using up_2 = ratio<2, 1>; - using down_2 = ratio<1, 2>; - using up_3 = ratio<3, 1>; - using down_3 = ratio<1, 3>; - using up_6 = ratio<6, 1>; - using down_6 = ratio<1, 6>; - using up_8 = ratio<8, 1>; - using down_8 = ratio<1, 8>; - /// The mixed ratios, named by L/M. - using ratio_3_2 = ratio<3, 2>; - using ratio_2_3 = ratio<2, 3>; - using ratio_4_3 = ratio<4, 3>; - using ratio_3_4 = ratio<3, 4>; - using ratio_8_3 = ratio<8, 3>; - using ratio_3_8 = ratio<3, 8>; - -} // namespace tap::sr::rational diff --git a/rational/tests/CMakeLists.txt b/rational/tests/CMakeLists.txt index 117a431..cda316b 100644 --- a/rational/tests/CMakeLists.txt +++ b/rational/tests/CMakeLists.txt @@ -26,15 +26,18 @@ set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) add_executable(tap_sr_rational_tests + test_chain.cpp test_design.cpp + test_matrix.cpp test_output_hash.cpp test_ratio.cpp test_stage.cpp) -# The tests' own headers (the committed reference vectors) resolve relative -# to this directory. +# The tests' own headers (the committed reference vectors, the generated +# matrix rows, the bridge adapter) resolve relative to this directory. target_include_directories(tap_sr_rational_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(tap_sr_rational_tests PRIVATE tap::sr::rational + tap_sr_bridge_headers tap_sr_rational_warnings) if(Threads_FOUND) diff --git a/rational/tests/bare_metal_main.cpp b/rational/tests/bare_metal_main.cpp index 8e2e063..f30393a 100644 --- a/rational/tests/bare_metal_main.cpp +++ b/rational/tests/bare_metal_main.cpp @@ -4,8 +4,11 @@ // qemu-system-arm): there is no argv on the target, so the // emulation-appropriate filter is baked in. Excluded are the tests that // sweep the 16384-point design grid over long designs or search it (the -// transparent rows up to N = 399, the coarse-grid comparison, the unpinned -// search): target-independent design math already covered on every host. +// transparent rows up to N = 399 and the transparent relaxation table up to +// N = 735, the coarse-grid comparison, the unpinned search) and the +// coverage-matrix suite (182 chains in double through a tone battery, a +// host measurement): target-independent design math and measurement +// already covered on every host. // The 70 dB rows' pins, minimality and structure run on target. #include @@ -16,7 +19,9 @@ int main() { "Design.PinsAreMinimalAndMeasuredAtTransparent:" "Design.TheCoarseGridWouldUnderPinTwoRows:" "Design.UnpinnedProfileIsSearchedOnTheDesignGrid:" - "Design.UnmeetableSpecThrows"; + "Design.UnmeetableSpecThrows:" + "Design.RelaxationTablesAreTheSearchAtTransparent:" + "Matrix.*"; ::testing::InitGoogleTest(); const int rc = RUN_ALL_TESTS(); // A filter typo selects zero tests and RUN_ALL_TESTS() returns 0 — an diff --git a/rational/tests/coverage/matrix_rows.h b/rational/tests/coverage/matrix_rows.h new file mode 100644 index 0000000..8dd8c4c --- /dev/null +++ b/rational/tests/coverage/matrix_rows.h @@ -0,0 +1,199 @@ +// Generated by tools/coverage/matrix.py — DO NOT EDIT. +// The coverage matrix (PLAN.md section 3): one chain per ordered pair of +// the fourteen rates, with the relaxation divisor of every rational stage +// and the exact MACs per output (num, den) and latency in output frames +// (num, den) per profile, in the order super_economy, economy, balanced, +// transparent. See the generator for the rules and the provenance. +// SPDX-License-Identifier: MIT +// Copyright 2026 Timothy Place and the SampleRateTap contributors +#pragma once + +// clang-format off +// ROW(src_hz, dst_hz, flagged, (stage types...), (divisors...), +// (macs_num, macs_den, lat_num, lat_den) x 4 profiles) +#define TAP_SR_RATIONAL_MATRIX_ROWS(ROW) \ + ROW(8000, 12000, false, (ratio_3_2), (d(1, 1)), 11, 1, 23, 2, 15, 1, 16, 1, 53, 3, 19, 1, 101, 3, 37, 1) \ + ROW(8000, 16000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(8000, 24000, false, (up_3), (d(1, 1)), 11, 1, 23, 1, 15, 1, 32, 1, 53, 3, 38, 1, 101, 3, 74, 1) \ + ROW(8000, 32000, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(8000, 48000, false, (up_2, up_3), (d(1, 1), d(2, 1)), 61, 6, 65, 1, 65, 6, 77, 1, 23, 2, 89, 1, 43, 2, 206, 1) \ + ROW(8000, 96000, false, (up_2, up_6), (d(1, 1), d(2, 1)), 121, 12, 131, 1, 125, 12, 155, 1, 43, 4, 179, 1, 325, 12, 443, 1) \ + ROW(8000, 192000, false, (up_2, up_6, up_2), (d(1, 1), d(2, 1), d(12, 1)), 205, 24, 267, 1, 209, 24, 315, 1, 71, 8, 363, 1, 529, 24, 901, 1) \ + ROW(8000, 384000, false, (up_2, up_8, up_3), (d(1, 1), d(2, 1), d(16, 1)), 123, 16, 533, 1, 373, 48, 629, 1, 377, 48, 725, 1, 893, 48, 1793, 1) \ + ROW(8000, 11025, true, (up_2, up_3, bridge_down<0>, down_2, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 111689, 441, 40029, 1280, 146485, 441, 48763, 1280, 61603, 147, 57791, 1280, 137387, 147, 132731, 1280) \ + ROW(8000, 22050, true, (up_2, up_3, bridge_down<0>, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1)), 51655, 441, 29149, 640, 68171, 441, 35323, 640, 28817, 147, 41791, 640, 64063, 147, 93691, 640) \ + ROW(8000, 44100, false, (up_2, up_3, bridge_down<0>), (d(1, 1), d(2, 1), d(0, 1)), 22520, 441, 24989, 320, 30778, 441, 31163, 320, 13306, 147, 37631, 320, 30488, 147, 87611, 320) \ + ROW(8000, 88200, false, (up_2, up_3, bridge_down<0>, up_2), (d(1, 1), d(2, 1), d(0, 1), d(4, 1)), 29135, 882, 27069, 160, 37393, 882, 33243, 160, 15511, 294, 39711, 160, 33281, 294, 90331, 160) \ + ROW(8000, 176400, false, (up_2, up_3, bridge_down<0>, up_2, up_2), (d(1, 1), d(2, 1), d(0, 1), d(4, 1), d(9, 1)), 35309, 1764, 27469, 80, 45331, 1764, 33803, 80, 18157, 588, 40271, 80, 38279, 588, 91531, 80) \ + ROW(12000, 8000, false, (ratio_2_3), (d(1, 1)), 47, 2, 23, 3, 65, 2, 32, 3, 77, 2, 38, 3, 149, 2, 74, 3) \ + ROW(12000, 16000, false, (ratio_4_3), (d(1, 1)), 49, 4, 31, 3, 67, 4, 43, 3, 85, 4, 55, 3, 151, 4, 33, 1) \ + ROW(12000, 24000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(12000, 32000, false, (up_2, ratio_4_3), (d(1, 1), d(2, 1)), 119, 8, 29, 1, 131, 8, 103, 3, 143, 8, 119, 3, 347, 8, 295, 3) \ + ROW(12000, 48000, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(12000, 96000, false, (up_2, ratio_4_3, up_3), (d(1, 1), d(2, 1), d(2, 1)), 287, 24, 101, 1, 299, 24, 117, 1, 311, 24, 133, 1, 611, 24, 318, 1) \ + ROW(12000, 192000, false, (up_2, up_8), (d(1, 1), d(2, 1)), 161, 16, 175, 1, 165, 16, 207, 1, 169, 16, 239, 1, 429, 16, 591, 1) \ + ROW(12000, 384000, false, (up_2, up_8, up_2), (d(1, 1), d(2, 1), d(16, 1)), 273, 32, 355, 1, 277, 32, 419, 1, 281, 32, 483, 1, 669, 32, 1195, 1) \ + ROW(12000, 11025, true, (up_2, up_2, bridge_down<0>, down_2, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 787, 3, 34737, 1280, 50375, 147, 8459, 256, 21121, 49, 50147, 1280, 6687, 7, 113621, 1280) \ + ROW(12000, 22050, true, (up_2, up_2, bridge_down<0>, down_2), (d(1, 1), d(2, 1), d(0, 1), d(1, 1)), 377, 3, 25137, 640, 24673, 147, 6283, 128, 10487, 49, 37987, 640, 3417, 7, 88021, 640) \ + ROW(12000, 44100, false, (up_2, up_2, bridge_down<0>), (d(1, 1), d(2, 1), d(0, 1)), 160, 3, 19697, 320, 10646, 147, 4939, 64, 4582, 49, 29987, 320, 1488, 7, 68501, 320) \ + ROW(12000, 88200, false, (up_2, up_2, bridge_down<0>, up_2), (d(1, 1), d(2, 1), d(0, 1), d(3, 1)), 205, 6, 21777, 160, 12851, 294, 5355, 32, 5317, 98, 32067, 160, 1621, 14, 71221, 160) \ + ROW(12000, 176400, false, (up_2, up_2, bridge_down<0>, up_2, up_2), (d(1, 1), d(2, 1), d(0, 1), d(3, 1), d(6, 1)), 259, 12, 22337, 80, 15497, 588, 5467, 16, 6591, 196, 32947, 80, 1887, 28, 72581, 80) \ + ROW(16000, 8000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(16000, 12000, false, (ratio_3_4), (d(1, 1)), 21, 1, 31, 4, 29, 1, 43, 4, 37, 1, 55, 4, 199, 3, 99, 4) \ + ROW(16000, 24000, false, (ratio_3_2), (d(1, 1)), 11, 1, 23, 2, 15, 1, 16, 1, 53, 3, 19, 1, 101, 3, 37, 1) \ + ROW(16000, 32000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(16000, 48000, false, (up_3), (d(1, 1)), 11, 1, 23, 1, 15, 1, 32, 1, 53, 3, 38, 1, 101, 3, 74, 1) \ + ROW(16000, 96000, false, (up_2, up_3), (d(1, 1), d(2, 1)), 61, 6, 65, 1, 65, 6, 77, 1, 23, 2, 89, 1, 43, 2, 206, 1) \ + ROW(16000, 192000, false, (up_2, up_6), (d(1, 1), d(2, 1)), 121, 12, 131, 1, 125, 12, 155, 1, 43, 4, 179, 1, 325, 12, 443, 1) \ + ROW(16000, 384000, false, (up_2, up_6, up_2), (d(1, 1), d(2, 1), d(12, 1)), 205, 24, 267, 1, 209, 24, 315, 1, 71, 8, 363, 1, 529, 24, 901, 1) \ + ROW(16000, 11025, true, (up_3, bridge_down<0>, down_2, down_2), (d(1, 1), d(0, 1), d(2, 1), d(1, 1)), 37763, 147, 27681, 1280, 17165, 49, 35533, 1280, 196649, 441, 42797, 1280, 435521, 441, 93923, 1280) \ + ROW(16000, 22050, true, (up_3, bridge_down<0>, down_2), (d(1, 1), d(0, 1), d(1, 1)), 18073, 147, 18081, 640, 8411, 49, 24653, 640, 97663, 441, 30637, 640, 222391, 441, 68323, 640) \ + ROW(16000, 44100, false, (up_3, bridge_down<0>), (d(1, 1), d(0, 1)), 7640, 147, 12641, 320, 3642, 49, 17933, 320, 42878, 441, 22637, 320, 97304, 441, 48803, 320) \ + ROW(16000, 88200, false, (up_3, bridge_down<0>, up_2), (d(1, 1), d(0, 1), d(2, 1)), 9845, 294, 14721, 160, 4377, 98, 20013, 160, 49493, 882, 24717, 160, 106565, 882, 51843, 160) \ + ROW(16000, 176400, false, (up_3, bridge_down<0>, up_2, up_2), (d(1, 1), d(0, 1), d(2, 1), d(4, 1)), 14255, 588, 15761, 80, 5847, 196, 21053, 80, 62723, 1764, 25757, 80, 123323, 1764, 53203, 80) \ + ROW(24000, 8000, false, (down_3), (d(1, 1)), 33, 1, 23, 3, 45, 1, 32, 3, 53, 1, 38, 3, 101, 1, 74, 3) \ + ROW(24000, 12000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(24000, 16000, false, (ratio_2_3), (d(1, 1)), 47, 2, 23, 3, 65, 2, 32, 3, 77, 2, 38, 3, 149, 2, 74, 3) \ + ROW(24000, 32000, false, (ratio_4_3), (d(1, 1)), 49, 4, 31, 3, 67, 4, 43, 3, 85, 4, 55, 3, 151, 4, 33, 1) \ + ROW(24000, 48000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(24000, 96000, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(24000, 192000, false, (up_2, ratio_4_3, up_3), (d(1, 1), d(2, 1), d(2, 1)), 287, 24, 101, 1, 299, 24, 117, 1, 311, 24, 133, 1, 611, 24, 318, 1) \ + ROW(24000, 384000, false, (up_2, up_8), (d(1, 1), d(2, 1)), 161, 16, 175, 1, 165, 16, 207, 1, 169, 16, 239, 1, 429, 16, 591, 1) \ + ROW(24000, 11025, true, (up_2, bridge_down<0>, down_2, down_2), (d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 11841, 49, 24741, 1280, 15565, 49, 29947, 1280, 19681, 49, 35447, 1280, 6207, 7, 77753, 1280) \ + ROW(24000, 22050, true, (up_2, bridge_down<0>, down_2), (d(1, 1), d(0, 1), d(1, 1)), 17593, 147, 16317, 640, 24113, 147, 21419, 640, 10407, 49, 5363, 128, 3497, 7, 64501, 640) \ + ROW(24000, 44100, false, (up_2, bridge_down<0>), (d(1, 1), d(0, 1)), 7400, 147, 10877, 320, 10366, 147, 14699, 320, 4542, 49, 3763, 64, 1528, 7, 44981, 320) \ + ROW(24000, 88200, false, (up_2, bridge_down<0>, up_2), (d(1, 1), d(0, 1), d(1, 1)), 10193, 294, 13597, 160, 13747, 294, 18059, 160, 5865, 98, 4563, 32, 1969, 14, 54741, 160) \ + ROW(24000, 176400, false, (up_2, bridge_down<0>, up_2, up_2), (d(1, 1), d(0, 1), d(1, 1), d(3, 1)), 14603, 588, 14637, 80, 18157, 588, 19099, 80, 7335, 196, 4771, 16, 2235, 28, 56101, 80) \ + ROW(32000, 8000, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(32000, 12000, false, (ratio_3_4, down_2), (d(2, 1), d(1, 1)), 45, 1, 87, 8, 49, 1, 103, 8, 53, 1, 119, 8, 395, 3, 295, 8) \ + ROW(32000, 16000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(32000, 24000, false, (ratio_3_4), (d(1, 1)), 21, 1, 31, 4, 29, 1, 43, 4, 37, 1, 55, 4, 199, 3, 99, 4) \ + ROW(32000, 48000, false, (ratio_3_2), (d(1, 1)), 11, 1, 23, 2, 15, 1, 16, 1, 53, 3, 19, 1, 101, 3, 37, 1) \ + ROW(32000, 96000, false, (up_3), (d(1, 1)), 11, 1, 23, 1, 15, 1, 32, 1, 53, 3, 38, 1, 101, 3, 74, 1) \ + ROW(32000, 192000, false, (up_2, up_3), (d(1, 1), d(2, 1)), 61, 6, 65, 1, 65, 6, 77, 1, 23, 2, 89, 1, 43, 2, 206, 1) \ + ROW(32000, 384000, false, (up_2, up_6), (d(1, 1), d(2, 1)), 121, 12, 131, 1, 125, 12, 155, 1, 43, 4, 179, 1, 325, 12, 443, 1) \ + ROW(32000, 11025, true, (ratio_3_2, bridge_down<0>, down_2, down_2), (d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 5029, 21, 22977, 1280, 6625, 21, 28183, 1280, 8389, 21, 33683, 1280, 130667, 147, 18887, 320) \ + ROW(32000, 22050, true, (ratio_3_2, bridge_down<0>, down_2), (d(1, 1), d(0, 1), d(1, 1)), 18073, 147, 735, 32, 8411, 49, 19949, 640, 97663, 441, 25051, 640, 222391, 441, 11489, 128) \ + ROW(32000, 44100, false, (ratio_3_2, bridge_down<0>), (d(1, 1), d(0, 1)), 7640, 147, 463, 16, 3642, 49, 13229, 320, 42878, 441, 17051, 320, 97304, 441, 7585, 64) \ + ROW(32000, 88200, false, (ratio_3_2, bridge_down<0>, up_2), (d(1, 1), d(0, 1), d(1, 1)), 10433, 294, 599, 8, 4769, 98, 16589, 160, 54785, 882, 21051, 160, 125087, 882, 9537, 32) \ + ROW(32000, 176400, false, (ratio_3_2, bridge_down<0>, up_2, up_2), (d(1, 1), d(0, 1), d(1, 1), d(2, 1)), 14843, 588, 651, 4, 6239, 196, 17629, 80, 68015, 1764, 22091, 80, 143609, 1764, 9841, 16) \ + ROW(48000, 8000, false, (down_3, down_2), (d(2, 1), d(1, 1)), 61, 1, 65, 6, 65, 1, 77, 6, 69, 1, 89, 6, 129, 1, 103, 3) \ + ROW(48000, 12000, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(48000, 16000, false, (down_3), (d(1, 1)), 33, 1, 23, 3, 45, 1, 32, 3, 53, 1, 38, 3, 101, 1, 74, 3) \ + ROW(48000, 24000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(48000, 32000, false, (ratio_2_3), (d(1, 1)), 47, 2, 23, 3, 65, 2, 32, 3, 77, 2, 38, 3, 149, 2, 74, 3) \ + ROW(48000, 96000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(48000, 192000, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(48000, 384000, false, (up_2, ratio_4_3, up_3), (d(1, 1), d(2, 1), d(2, 1)), 287, 24, 101, 1, 299, 24, 117, 1, 311, 24, 133, 1, 611, 24, 318, 1) \ + ROW(48000, 11025, true, (bridge_down<0>, down_2, down_2), (d(0, 1), d(2, 1), d(1, 1)), 209, 1, 20919, 1280, 285, 1, 5225, 256, 369, 1, 6325, 256, 841, 1, 72167, 1280) \ + ROW(48000, 22050, true, (bridge_down<0>, down_2), (d(0, 1), d(1, 1)), 99, 1, 11319, 640, 139, 1, 3049, 128, 183, 1, 3893, 128, 431, 1, 46567, 640) \ + ROW(48000, 44100, false, (bridge_down<0>), (d(0, 1)), 40, 1, 5879, 320, 58, 1, 1705, 64, 78, 1, 2293, 64, 184, 1, 27047, 320) \ + ROW(48000, 88200, false, (bridge_down<0>, up_2), (d(0, 1), d(147, 160)), 63, 2, 9239, 160, 89, 2, 2633, 32, 117, 2, 3477, 32, 275, 2, 41287, 160) \ + ROW(48000, 176400, false, (bridge_down<0>, up_2, up_2), (d(0, 1), d(147, 160), d(1, 1)), 101, 4, 10599, 80, 135, 4, 2969, 16, 171, 4, 3877, 16, 401, 4, 46167, 80) \ + ROW(96000, 8000, false, (down_6, down_2), (d(2, 1), d(1, 1)), 121, 1, 131, 12, 125, 1, 155, 12, 129, 1, 179, 12, 325, 1, 443, 12) \ + ROW(96000, 12000, false, (down_3, ratio_3_4, down_2), (d(2, 1), d(2, 1), d(1, 1)), 101, 1, 101, 8, 105, 1, 117, 8, 109, 1, 133, 8, 659, 3, 159, 4) \ + ROW(96000, 16000, false, (down_3, down_2), (d(2, 1), d(1, 1)), 61, 1, 65, 6, 65, 1, 77, 6, 69, 1, 89, 6, 129, 1, 103, 3) \ + ROW(96000, 24000, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(96000, 32000, false, (down_3), (d(1, 1)), 33, 1, 23, 3, 45, 1, 32, 3, 53, 1, 38, 3, 101, 1, 74, 3) \ + ROW(96000, 48000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(96000, 192000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(96000, 384000, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(96000, 11025, true, (down_2, bridge_down<0>, down_2, down_2), (d(4, 1), d(0, 1), d(2, 1), d(1, 1)), 13441, 49, 2283, 128, 17165, 49, 7009, 320, 21281, 49, 131, 5, 135787, 147, 37333, 640) \ + ROW(96000, 22050, true, (down_2, bridge_down<0>, down_2), (d(2, 1), d(0, 1), d(1, 1)), 6451, 49, 1323, 64, 8411, 49, 4289, 160, 10567, 49, 167, 5, 3337, 7, 617, 8) \ + ROW(96000, 44100, false, (down_2, bridge_down<0>), (d(1, 1), d(0, 1)), 8920, 147, 4189, 160, 12206, 147, 2903, 80, 5262, 49, 757, 16, 1768, 7, 18007, 160) \ + ROW(96000, 88200, false, (bridge_down<1>), (d(0, 1)), 40, 1, 5879, 320, 58, 1, 1705, 64, 78, 1, 2293, 64, 184, 1, 27047, 320) \ + ROW(96000, 176400, false, (bridge_down<1>, up_2), (d(0, 1), d(147, 160)), 63, 2, 9239, 160, 89, 2, 2633, 32, 117, 2, 3477, 32, 275, 2, 41287, 160) \ + ROW(192000, 8000, false, (down_2, down_6, down_2), (d(12, 1), d(2, 1), d(1, 1)), 205, 1, 89, 8, 209, 1, 105, 8, 213, 1, 121, 8, 529, 1, 901, 24) \ + ROW(192000, 12000, false, (down_8, down_2), (d(2, 1), d(1, 1)), 161, 1, 175, 16, 165, 1, 207, 16, 169, 1, 239, 16, 429, 1, 591, 16) \ + ROW(192000, 16000, false, (down_6, down_2), (d(2, 1), d(1, 1)), 121, 1, 131, 12, 125, 1, 155, 12, 129, 1, 179, 12, 325, 1, 443, 12) \ + ROW(192000, 24000, false, (down_3, ratio_3_4, down_2), (d(2, 1), d(2, 1), d(1, 1)), 101, 1, 101, 8, 105, 1, 117, 8, 109, 1, 133, 8, 659, 3, 159, 4) \ + ROW(192000, 32000, false, (down_3, down_2), (d(2, 1), d(1, 1)), 61, 1, 65, 6, 65, 1, 77, 6, 69, 1, 89, 6, 129, 1, 103, 3) \ + ROW(192000, 48000, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(192000, 96000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(192000, 384000, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(192000, 11025, true, (down_2, down_2, bridge_down<0>, down_2, down_2), (d(8, 1), d(4, 1), d(0, 1), d(2, 1), d(1, 1)), 17281, 49, 46689, 2560, 21005, 49, 57101, 2560, 25121, 49, 68101, 2560, 53369, 49, 151831, 2560) \ + ROW(192000, 22050, true, (down_3, ratio_3_4, bridge_down<0>, down_2), (d(2, 1), d(2, 1), d(0, 1), d(1, 1)), 27673, 147, 27489, 1280, 33553, 147, 35341, 1280, 40021, 147, 43781, 1280, 265271, 441, 26003, 320) \ + ROW(192000, 44100, false, (down_2, down_2, bridge_down<0>), (d(2, 1), d(1, 1), d(0, 1)), 280, 3, 18667, 640, 17006, 147, 5027, 128, 6862, 49, 32191, 640, 2088, 7, 74821, 640) \ + ROW(192000, 88200, false, (down_2, bridge_down<1>), (d(1, 1), d(0, 1)), 8920, 147, 4189, 160, 12206, 147, 2903, 80, 5262, 49, 757, 16, 1768, 7, 18007, 160) \ + ROW(192000, 176400, false, (bridge_down<2>), (d(0, 1)), 40, 1, 5879, 320, 58, 1, 1705, 64, 78, 1, 2293, 64, 184, 1, 27047, 320) \ + ROW(384000, 8000, false, (down_3, down_8, down_2), (d(16, 1), d(2, 1), d(1, 1)), 369, 1, 533, 48, 373, 1, 629, 48, 377, 1, 725, 48, 893, 1, 1793, 48) \ + ROW(384000, 12000, false, (down_2, down_8, down_2), (d(16, 1), d(2, 1), d(1, 1)), 273, 1, 355, 32, 277, 1, 419, 32, 281, 1, 483, 32, 669, 1, 1195, 32) \ + ROW(384000, 16000, false, (down_2, down_6, down_2), (d(12, 1), d(2, 1), d(1, 1)), 205, 1, 89, 8, 209, 1, 105, 8, 213, 1, 121, 8, 529, 1, 901, 24) \ + ROW(384000, 24000, false, (down_8, down_2), (d(2, 1), d(1, 1)), 161, 1, 175, 16, 165, 1, 207, 16, 169, 1, 239, 16, 429, 1, 591, 16) \ + ROW(384000, 32000, false, (down_6, down_2), (d(2, 1), d(1, 1)), 121, 1, 131, 12, 125, 1, 155, 12, 129, 1, 179, 12, 325, 1, 443, 12) \ + ROW(384000, 48000, false, (down_3, ratio_3_4, down_2), (d(2, 1), d(2, 1), d(1, 1)), 101, 1, 101, 8, 105, 1, 117, 8, 109, 1, 133, 8, 659, 3, 159, 4) \ + ROW(384000, 96000, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(384000, 192000, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(384000, 11025, true, (down_2, down_2, down_2, bridge_down<0>, down_2, down_2), (d(16, 1), d(8, 1), d(4, 1), d(0, 1), d(2, 1), d(1, 1)), 69763, 147, 94113, 5120, 80935, 147, 114937, 5120, 93283, 147, 136937, 5120, 66169, 49, 305573, 5120) \ + ROW(384000, 22050, true, (down_8, bridge_down<0>, down_2), (d(2, 1), d(0, 1), d(1, 1)), 37273, 147, 51009, 2560, 43153, 147, 66713, 2560, 49621, 147, 83593, 2560, 40639, 49, 201409, 2560) \ + ROW(384000, 44100, false, (down_3, ratio_3_4, down_2, bridge_down<0>), (d(2, 1), d(2, 1), d(1, 1), d(0, 1)), 22040, 147, 38363, 1280, 1206, 7, 51299, 1280, 28906, 147, 65411, 1280, 186584, 441, 77467, 640) \ + ROW(384000, 88200, false, (down_2, down_2, bridge_down<1>), (d(2, 1), d(1, 1), d(0, 1)), 280, 3, 18667, 640, 17006, 147, 5027, 128, 6862, 49, 32191, 640, 2088, 7, 74821, 640) \ + ROW(384000, 176400, false, (down_2, bridge_down<2>), (d(1, 1), d(0, 1)), 8920, 147, 4189, 160, 12206, 147, 2903, 80, 5262, 49, 757, 16, 1768, 7, 18007, 160) \ + ROW(11025, 8000, true, (up_2, up_2, bridge_up<0>, down_3, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 94889, 320, 38629, 1764, 117133, 320, 15439, 588, 131697, 320, 53365, 1764, 54381, 64, 121043, 1764) \ + ROW(11025, 12000, true, (up_2, up_2, bridge_up<0>, down_2, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 32963, 160, 33337, 1176, 40591, 160, 13283, 392, 45659, 160, 45721, 1176, 18735, 32, 101933, 1176) \ + ROW(11025, 16000, true, (up_2, up_2, bridge_up<0>, down_3), (d(1, 1), d(2, 1), d(0, 1), d(1, 1)), 96489, 640, 26281, 882, 125133, 640, 11029, 294, 143537, 640, 38371, 882, 59053, 128, 82235, 882) \ + ROW(11025, 24000, true, (up_2, up_2, bridge_up<0>, down_2), (d(1, 1), d(2, 1), d(0, 1), d(2, 1)), 29923, 320, 23341, 588, 36911, 320, 9167, 196, 41339, 320, 31021, 588, 16719, 64, 66065, 588) \ + ROW(11025, 32000, false, (up_2, up_2, bridge_up<0>, ratio_2_3), (d(1, 1), d(2, 1), d(0, 1), d(2, 1)), 93929, 1280, 21577, 441, 114893, 1280, 8579, 147, 128177, 1280, 29257, 441, 52141, 256, 63860, 441) \ + ROW(11025, 48000, false, (up_2, up_2, bridge_up<0>), (d(1, 1), d(2, 1), d(0, 1)), 25123, 640, 19519, 294, 32111, 640, 7893, 98, 36539, 640, 27199, 294, 15375, 128, 60479, 294) \ + ROW(11025, 96000, false, (up_2, up_2, bridge_up<0>, up_2), (d(1, 1), d(2, 1), d(0, 1), d(4, 1)), 34723, 1280, 21430, 147, 41711, 1280, 8530, 49, 46139, 1280, 29110, 147, 17807, 256, 62978, 147) \ + ROW(11025, 192000, false, (up_2, up_2, bridge_up<0>, up_2, up_2), (d(1, 1), d(2, 1), d(0, 1), d(4, 1), d(8, 1)), 46243, 2560, 43889, 147, 53231, 2560, 17403, 49, 57659, 2560, 59249, 147, 22671, 512, 128455, 147) \ + ROW(11025, 384000, false, (up_2, up_2, bridge_up<0>, up_2, up_2, up_2), (d(1, 1), d(2, 1), d(0, 1), d(4, 1), d(8, 1), d(16, 1)), 64163, 5120, 88513, 147, 71151, 5120, 35051, 49, 75579, 5120, 119233, 147, 30351, 1024, 258821, 147) \ + ROW(11025, 22050, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(11025, 44100, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(11025, 88200, false, (up_3, ratio_8_3), (d(1, 1), d(3, 1)), 13, 1, 223, 3, 29, 2, 295, 3, 31, 2, 343, 3, 135, 4, 229, 1) \ + ROW(11025, 176400, false, (up_2, up_8), (d(1, 1), d(2, 1)), 161, 16, 175, 1, 165, 16, 207, 1, 169, 16, 239, 1, 429, 16, 591, 1) \ + ROW(22050, 8000, true, (up_2, bridge_up<0>, down_3, down_2), (d(2, 1), d(0, 1), d(2, 1), d(1, 1)), 8651, 32, 27749, 1764, 10699, 32, 3653, 196, 11979, 32, 12455, 588, 122061, 160, 82003, 1764) \ + ROW(22050, 12000, true, (up_2, bridge_up<0>, down_2, down_2), (d(1, 1), d(0, 1), d(2, 1), d(1, 1)), 15673, 80, 3391, 168, 19781, 80, 28969, 1176, 22609, 80, 11187, 392, 48381, 80, 76333, 1176) \ + ROW(22050, 16000, true, (up_2, bridge_up<0>, down_3), (d(1, 1), d(0, 1), d(1, 1)), 45819, 320, 2383, 126, 61023, 320, 22207, 882, 71107, 320, 8737, 294, 152263, 320, 56635, 882) \ + ROW(22050, 24000, true, (up_2, bridge_up<0>, down_2), (d(1, 1), d(0, 1), d(1, 1)), 14793, 160, 2131, 84, 19221, 160, 18973, 588, 22369, 160, 7463, 196, 50061, 160, 52813, 588) \ + ROW(22050, 32000, false, (up_2, bridge_up<0>, ratio_2_3), (d(1, 1), d(0, 1), d(1, 1)), 50299, 640, 1900, 63, 67423, 640, 17503, 441, 78787, 640, 6875, 147, 167623, 640, 45757, 441) \ + ROW(22050, 48000, false, (up_2, bridge_up<0>), (d(1, 1), d(0, 1)), 11753, 320, 1417, 42, 15541, 320, 12799, 294, 18049, 320, 5013, 98, 39981, 320, 34879, 294) \ + ROW(22050, 96000, false, (up_2, bridge_up<0>, up_2), (d(1, 1), d(0, 1), d(2, 1)), 16553, 640, 1690, 21, 20341, 640, 14710, 147, 22849, 640, 5650, 49, 46701, 640, 37672, 147) \ + ROW(22050, 192000, false, (up_2, bridge_up<0>, ratio_4_3, up_3), (d(1, 1), d(0, 1), d(2, 1), d(2, 1)), 72059, 3840, 3527, 21, 83423, 3840, 30449, 147, 90947, 3840, 11643, 49, 187463, 3840, 80636, 147) \ + ROW(22050, 384000, false, (up_2, bridge_up<0>, up_8), (d(1, 1), d(0, 1), d(2, 1)), 34473, 2560, 6487, 21, 38261, 2560, 56929, 147, 40769, 2560, 21963, 49, 98541, 2560, 154657, 147) \ + ROW(22050, 11025, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(22050, 44100, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(22050, 88200, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(22050, 176400, false, (up_3, ratio_8_3), (d(1, 1), d(3, 1)), 13, 1, 223, 3, 29, 2, 295, 3, 31, 2, 343, 3, 135, 4, 229, 1) \ + ROW(44100, 8000, true, (bridge_up<0>, down_3, down_2), (d(0, 1), d(2, 1), d(1, 1)), 229, 1, 2621, 196, 293, 1, 28717, 1764, 333, 1, 33205, 1764, 705, 1, 75923, 1764) \ + ROW(44100, 12000, true, (bridge_up<0>, down_2, down_2), (d(0, 1), d(2, 1), d(1, 1)), 161, 1, 6099, 392, 205, 1, 22249, 1176, 233, 1, 25561, 1176, 489, 1, 56813, 1176) \ + ROW(44100, 16000, true, (bridge_up<0>, down_3), (d(0, 1), d(1, 1)), 117, 1, 1249, 98, 159, 1, 15487, 882, 185, 1, 18211, 882, 389, 1, 37115, 882) \ + ROW(44100, 24000, true, (bridge_up<0>, down_2), (d(0, 1), d(1, 1)), 75, 1, 3159, 196, 99, 1, 12253, 588, 115, 1, 14389, 588, 255, 1, 33293, 588) \ + ROW(44100, 32000, false, (bridge_up<0>, ratio_2_3), (d(0, 1), d(1, 1)), 131, 2, 2620, 147, 179, 2, 10783, 441, 209, 2, 12625, 441, 437, 2, 26237, 441) \ + ROW(44100, 48000, false, (bridge_up<0>), (d(0, 1)), 28, 1, 1493, 98, 38, 1, 6079, 294, 44, 1, 7039, 294, 96, 1, 15359, 294) \ + ROW(44100, 96000, false, (bridge_up<0>, up_2), (d(0, 1), d(1, 1)), 47, 2, 2326, 49, 61, 2, 9166, 147, 71, 2, 10714, 147, 159, 2, 24326, 147) \ + ROW(44100, 192000, false, (bridge_up<0>, up_2, up_2), (d(0, 1), d(1, 1), d(2, 1)), 77, 4, 5289, 49, 91, 4, 20243, 147, 101, 4, 23339, 147, 201, 4, 51445, 147) \ + ROW(44100, 384000, false, (bridge_up<0>, up_2, ratio_4_3, up_3), (d(0, 1), d(1, 1), d(2, 1), d(2, 1)), 371, 24, 10921, 49, 413, 24, 41515, 147, 443, 24, 47707, 147, 899, 24, 108182, 147) \ + ROW(44100, 11025, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(44100, 22050, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(44100, 88200, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(44100, 176400, false, (up_2, up_2), (d(1, 1), d(2, 1)), 49, 4, 47, 1, 53, 4, 55, 1, 57, 4, 63, 1, 105, 4, 141, 1) \ + ROW(88200, 8000, true, (down_2, bridge_up<0>, down_3, down_2), (d(4, 1), d(0, 1), d(2, 1), d(1, 1)), 4987, 16, 3667, 252, 6011, 16, 30797, 1764, 6651, 16, 35285, 1764, 64779, 80, 78643, 1764) \ + ROW(88200, 12000, true, (down_2, bridge_up<0>, down_2, down_2), (d(3, 1), d(0, 1), d(2, 1), d(1, 1)), 1729, 8, 2911, 168, 2081, 8, 24329, 1176, 2305, 8, 27641, 1176, 22353, 40, 59533, 1176) \ + ROW(88200, 16000, true, (down_2, bridge_up<0>, down_3), (d(2, 1), d(0, 1), d(1, 1)), 5067, 32, 1903, 126, 6411, 32, 17567, 882, 7243, 32, 20291, 882, 71501, 160, 13385, 294) \ + ROW(88200, 24000, true, (down_2, bridge_up<0>, down_2), (d(1, 1), d(0, 1), d(1, 1)), 8793, 80, 12197, 588, 11301, 80, 15613, 588, 13169, 80, 2627, 84, 29661, 80, 14351, 196) \ + ROW(88200, 32000, false, (down_2, bridge_up<0>, ratio_2_3), (d(1, 1), d(0, 1), d(1, 1)), 29339, 320, 10580, 441, 38783, 320, 14143, 441, 45347, 320, 2375, 63, 97703, 320, 11999, 147) \ + ROW(88200, 48000, false, (down_2, bridge_up<0>), (d(147, 160), d(0, 1)), 7861, 160, 2613, 98, 10637, 160, 3573, 98, 12773, 160, 12959, 294, 28737, 160, 29599, 294) \ + ROW(88200, 96000, false, (bridge_up<1>), (d(0, 1)), 28, 1, 1493, 98, 38, 1, 6079, 294, 44, 1, 7039, 294, 96, 1, 15359, 294) \ + ROW(88200, 192000, false, (bridge_up<1>, up_2), (d(0, 1), d(1, 1)), 47, 2, 2326, 49, 61, 2, 9166, 147, 71, 2, 10714, 147, 159, 2, 24326, 147) \ + ROW(88200, 384000, false, (bridge_up<1>, up_2, up_2), (d(0, 1), d(1, 1), d(2, 1)), 77, 4, 5289, 49, 91, 4, 20243, 147, 101, 4, 23339, 147, 201, 4, 51445, 147) \ + ROW(88200, 11025, false, (ratio_3_8, down_3), (d(3, 1), d(1, 1)), 112, 1, 223, 24, 124, 1, 295, 24, 132, 1, 343, 24, 292, 1, 229, 8) \ + ROW(88200, 22050, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(88200, 44100, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) \ + ROW(88200, 176400, false, (up_2), (d(1, 1)), 19, 2, 17, 1, 23, 2, 21, 1, 27, 2, 25, 1, 63, 2, 61, 1) \ + ROW(176400, 8000, true, (down_2, down_2, bridge_up<0>, down_3, down_2), (d(9, 1), d(4, 1), d(0, 1), d(2, 1), d(1, 1)), 31109, 80, 26069, 1764, 37993, 80, 31357, 1764, 41193, 80, 35845, 1764, 79773, 80, 79843, 1764) \ + ROW(176400, 12000, true, (down_2, down_2, bridge_up<0>, down_2, down_2), (d(6, 1), d(3, 1), d(0, 1), d(2, 1), d(1, 1)), 11291, 40, 997, 56, 13051, 40, 24889, 1176, 15347, 40, 9507, 392, 27939, 40, 8699, 168) \ + ROW(176400, 16000, true, (down_2, down_2, bridge_up<0>, down_3), (d(4, 1), d(2, 1), d(0, 1), d(1, 1)), 7713, 32, 4787, 294, 9057, 32, 18607, 882, 9889, 32, 21331, 882, 88259, 160, 41515, 882) \ + ROW(176400, 24000, true, (down_2, down_2, bridge_up<0>, down_2), (d(3, 1), d(1, 1), d(0, 1), d(1, 1)), 13203, 80, 1891, 84, 15711, 80, 793, 28, 17579, 80, 19429, 588, 35247, 80, 44413, 588) \ + ROW(176400, 32000, false, (down_2, down_2, bridge_up<0>, ratio_2_3), (d(2, 1), d(1, 1), d(0, 1), d(1, 1)), 42569, 320, 1660, 63, 52013, 320, 241, 7, 58577, 320, 17665, 441, 23245, 64, 37517, 441) \ + ROW(176400, 48000, false, (down_2, down_2, bridge_up<0>), (d(1, 1), d(147, 160), d(0, 1)), 13447, 160, 9199, 294, 17399, 160, 4133, 98, 20711, 160, 2137, 42, 47259, 160, 11493, 98) \ + ROW(176400, 96000, false, (down_2, bridge_up<1>), (d(147, 160), d(0, 1)), 7861, 160, 2613, 98, 10637, 160, 3573, 98, 12773, 160, 12959, 294, 28737, 160, 29599, 294) \ + ROW(176400, 192000, false, (bridge_up<2>), (d(0, 1)), 28, 1, 1493, 98, 38, 1, 6079, 294, 44, 1, 7039, 294, 96, 1, 15359, 294) \ + ROW(176400, 384000, false, (bridge_up<2>, up_2), (d(0, 1), d(1, 1)), 47, 2, 2326, 49, 61, 2, 9166, 147, 71, 2, 10714, 147, 159, 2, 24326, 147) \ + ROW(176400, 11025, false, (down_8, down_2), (d(2, 1), d(1, 1)), 161, 1, 175, 16, 165, 1, 207, 16, 169, 1, 239, 16, 429, 1, 591, 16) \ + ROW(176400, 22050, false, (ratio_3_8, down_3), (d(3, 1), d(1, 1)), 112, 1, 223, 24, 124, 1, 295, 24, 132, 1, 343, 24, 292, 1, 229, 8) \ + ROW(176400, 44100, false, (down_2, down_2), (d(2, 1), d(1, 1)), 49, 1, 47, 4, 53, 1, 55, 4, 57, 1, 63, 4, 105, 1, 141, 4) \ + ROW(176400, 88200, false, (down_2), (d(1, 1)), 19, 1, 17, 2, 23, 1, 21, 2, 27, 1, 25, 2, 63, 1, 61, 2) +// clang-format on + +inline constexpr unsigned k_matrix_rows = 182; diff --git a/rational/tests/reference/reference_vectors.h b/rational/tests/reference/reference_vectors.h index 27192a7..4b5f688 100644 --- a/rational/tests/reference/reference_vectors.h +++ b/rational/tests/reference/reference_vectors.h @@ -3030,5 +3030,1143 @@ namespace rational_ref { 5.088859051e-02f, -2.366346866e-01f, -3.686189353e-01f, -1.454680115e-01f, 3.966058791e-01f, }; + inline constexpr std::array k_chain_up_2_up_2 = { + -3.474167087e-08f, 0.000000000e+00f, 3.296423188e-07f, 0.000000000e+00f, -1.022650736e-06f, + 0.000000000e+00f, 1.384121902e-06f, 0.000000000e+00f, -1.158168061e-06f, 0.000000000e+00f, + 4.441359579e-06f, 0.000000000e+00f, -3.762902634e-05f, -9.537792357e-05f, -1.116292624e-04f, + 0.000000000e+00f, 2.712959249e-04f, 5.481837434e-04f, 5.366309779e-04f, 0.000000000e+00f, + -9.757147636e-04f, -1.786194160e-03f, -1.606690697e-03f, 0.000000000e+00f, 2.522084396e-03f, + 4.356910475e-03f, 3.721082350e-03f, 0.000000000e+00f, -5.340755451e-03f, -8.911188692e-03f, + -7.386048790e-03f, 0.000000000e+00f, 1.007004641e-02f, 1.645057648e-02f, 1.338271238e-02f, + 0.000000000e+00f, -1.766627096e-02f, -2.846571803e-02f, -2.289552987e-02f, 0.000000000e+00f, + 2.968843281e-02f, 4.750296101e-02f, 3.813277930e-02f, 0.000000000e+00f, -4.925198853e-02f, + -7.926012576e-02f, -6.416340172e-02f, 0.000000000e+00f, 8.557070047e-02f, 1.418608576e-01f, + 1.197936907e-01f, 0.000000000e+00f, -1.881868243e-01f, -3.731868267e-01f, -4.704457521e-01f, + -4.230560064e-01f, -2.342694849e-01f, 2.861472405e-02f, 2.602355480e-01f, 3.642242253e-01f, + 2.970597148e-01f, 8.682923019e-02f, -1.816348583e-01f, -4.077575505e-01f, -5.208422542e-01f, + -5.058401823e-01f, -4.006947875e-01f, -2.658279240e-01f, -1.496750712e-01f, -6.596313417e-02f, + 2.491126303e-03f, 8.054351807e-02f, 1.750054657e-01f, 2.627833188e-01f, 3.036977351e-01f, + 2.673385441e-01f, 1.586120427e-01f, 2.277192846e-02f, -7.484942675e-02f, -8.471831679e-02f, + -2.931031864e-03f, 1.217298433e-01f, 2.127823383e-01f, 2.062820345e-01f, 8.891757578e-02f, + -9.243305773e-02f, -2.529405951e-01f, -3.139892519e-01f, -2.460803688e-01f, -8.444348723e-02f, + 8.752544969e-02f, 1.822219789e-01f, 1.517988741e-01f, 1.152049843e-02f, -1.703694612e-01f, + -3.089904785e-01f, -3.452381790e-01f, -2.711545527e-01f, -1.281819344e-01f, 2.044830285e-02f, + 1.233685464e-01f, 1.638594419e-01f, 1.606885344e-01f, 1.478485018e-01f, 1.507985592e-01f, + 1.693348885e-01f, 1.810708344e-01f, 1.574890167e-01f, 8.506937325e-02f, -2.440571226e-02f, + -1.382786185e-01f, -2.205779999e-01f, -2.514865696e-01f, -2.373009473e-01f, -2.069134712e-01f, + -1.949249208e-01f, -2.232199460e-01f, -2.881031632e-01f, -3.609804213e-01f, -4.003967047e-01f, + -3.713490069e-01f, -2.609838247e-01f, -8.797971159e-02f, 1.024475098e-01f, 2.522976100e-01f, + 3.110820949e-01f, 2.559705675e-01f, 1.011291444e-01f, -1.035068929e-01f, -2.899447083e-01f, + -3.973846734e-01f, -3.960433900e-01f, -3.001948595e-01f, -1.611437947e-01f, -4.405778646e-02f, + 1.771545387e-03f, -3.368597105e-02f, -1.184781566e-01f, -1.962436289e-01f, -2.164306641e-01f, + -1.622732878e-01f, -5.912170187e-02f, 3.847149014e-02f, 7.660216838e-02f, 3.167501464e-02f, + -7.723288983e-02f, -1.975204349e-01f, -2.716369629e-01f, -2.669292688e-01f, -1.918000281e-01f, + -9.062219411e-02f, -1.878662035e-02f, -1.450296585e-02f, -7.973974943e-02f, -1.814380586e-01f, + -2.696319520e-01f, -3.029622436e-01f, -2.663080096e-01f, -1.754163802e-01f, -6.402282417e-02f, + 3.308578953e-02f, 9.656518698e-02f, 1.272073388e-01f, 1.405837983e-01f, 1.560781598e-01f, + 1.856768131e-01f, 2.304263562e-01f, 2.815658450e-01f, 3.283160627e-01f, 3.622916043e-01f, + 3.821340203e-01f, 3.897399902e-01f, 3.878564835e-01f, 3.740629554e-01f, 3.401158154e-01f, + 2.735320926e-01f, 1.636854857e-01f, 9.150877595e-03f, -1.761951596e-01f, -3.620544374e-01f, + -5.070831180e-01f, -5.700753331e-01f, -5.251721144e-01f, -3.722717166e-01f, -1.430824548e-01f, + 1.068126038e-01f, 3.122563064e-01f, 4.187850952e-01f, 4.006736577e-01f, 2.680924833e-01f, + 6.518892199e-02f, -1.458572298e-01f, -3.051518798e-01f, -3.747802377e-01f, -3.521999121e-01f, + -2.690688968e-01f, -1.796025634e-01f, -1.384145021e-01f, -1.774464101e-01f, -2.908630371e-01f, + -4.346414506e-01f, -5.418802500e-01f, -5.519369245e-01f, -4.355804324e-01f, -2.126775831e-01f, + 5.498610064e-02f, 2.872089446e-01f, 4.182907045e-01f, 4.253824651e-01f, 3.337527514e-01f, + 2.038085312e-01f, 9.845122695e-02f, 5.447031930e-02f, 6.655591726e-02f, 9.544063359e-02f, + 9.165343642e-02f, 2.475253493e-02f, -1.011349633e-01f, -2.490809262e-01f, -3.701156378e-01f, + -4.281437695e-01f, -4.163150489e-01f, -3.581259251e-01f, -2.908767760e-01f, -2.461083233e-01f, + -2.335432619e-01f, -2.405219674e-01f, -2.428390384e-01f, -2.220296413e-01f, -1.766918749e-01f, + -1.248302981e-01f, -9.283447266e-02f, -1.023751348e-01f, -1.575073302e-01f, -2.424211949e-01f, + -3.269668519e-01f, -3.782097399e-01f, -3.717401922e-01f, -2.989104092e-01f, -1.677749604e-01f, + -1.010736596e-04f, 1.750583053e-01f, 3.276251853e-01f, 4.322982728e-01f, 4.737381339e-01f, + 4.487267435e-01f, 3.680172265e-01f, 2.527816594e-01f, 1.311736256e-01f, 3.061348759e-02f, + -2.715409920e-02f, -3.126983717e-02f, 1.652186364e-02f, 1.020172536e-01f, 2.006335258e-01f, + 2.821426392e-01f, 3.179560602e-01f, 2.898983359e-01f, 1.993389577e-01f, 7.088927925e-02f, + -5.195161328e-02f, -1.199775338e-01f, -9.888679534e-02f, 1.461181603e-02f, 1.857666671e-01f, + 3.525249660e-01f, 4.505745769e-01f, 4.407989383e-01f, 3.294384480e-01f, 1.656671166e-01f, + 2.031152137e-02f, -4.741973802e-02f, -1.785848849e-02f, 8.100195229e-02f, 1.892608255e-01f, + 2.460937500e-01f, 2.214590907e-01f, 1.285629719e-01f, 1.481338777e-02f, -6.697539985e-02f, + -8.809331805e-02f, -5.858496577e-02f, -2.027297206e-02f, -1.994018443e-02f, -8.233005553e-02f, + -1.939990371e-01f, -3.111189604e-01f, -3.825439215e-01f, -3.775000870e-01f, -2.996973693e-01f, + -1.828253865e-01f, -6.882934272e-02f, 1.476419624e-02f, 6.745278090e-02f, 1.090320572e-01f, + 1.602218598e-01f, 2.236414999e-01f, 2.765318751e-01f, 2.835702300e-01f, 2.185180634e-01f, + 8.600336313e-02f, -7.529600710e-02f, -2.072974294e-01f, -2.617492676e-01f, -2.250652313e-01f, + -1.271529943e-01f, -2.674847096e-02f, 1.772918738e-02f, -2.249471098e-02f, -1.334173083e-01f, + -2.675738335e-01f, -3.697448671e-01f, -4.056781530e-01f, -3.765855730e-01f, -3.152261376e-01f, + -2.639053166e-01f, -2.512061000e-01f, -2.771127224e-01f, -3.172793388e-01f, -3.396560550e-01f, + -3.258217275e-01f, -2.809339762e-01f, -2.306632251e-01f, -2.031646669e-01f, -2.115916908e-01f, + -2.436530739e-01f, -2.670910656e-01f, -2.466842532e-01f, -1.628943831e-01f, -2.378451265e-02f, + 1.383349001e-01f, 2.800140381e-01f, 3.654574156e-01f, 3.780608475e-01f, 3.236395121e-01f, + 2.219924927e-01f, 9.658614546e-02f, -3.451175243e-02f, -1.601341069e-01f, -2.722412050e-01f, + -3.606410027e-01f, -4.107375443e-01f, -4.068653882e-01f, -3.392852545e-01f, -2.114399523e-01f, + -4.282632843e-02f, 1.336655021e-01f, 2.798080444e-01f, 3.625652790e-01f, 3.628457189e-01f, + 2.815126777e-01f, 1.387710571e-01f, -3.133395687e-02f, -1.907766014e-01f, -3.097553849e-01f, + -3.750457764e-01f, -3.932006359e-01f, -3.840570152e-01f, -3.683225811e-01f, -3.534988165e-01f, + -3.283405304e-01f, -2.669519484e-01f, -1.476140469e-01f, 2.974548191e-02f, 2.320801616e-01f, + 4.017815292e-01f, 4.825399220e-01f, 4.489150941e-01f, 3.263618052e-01f, 1.831832081e-01f, + 9.898810834e-02f, 1.211929321e-01f, 2.363546193e-01f, 3.712336123e-01f, 4.316355586e-01f, + 3.532928228e-01f, 1.444301605e-01f, -1.127510518e-01f, -3.011306822e-01f, -3.305923343e-01f, + -1.906749904e-01f, 4.029469565e-02f, 2.333514243e-01f, 2.780227661e-01f, + }; + + inline constexpr std::array k_chain_down_3_down_2 = { + -6.985377610e-09f, -2.192871307e-07f, -9.652642348e-07f, 7.038621789e-06f, -2.797197885e-05f, + 6.131163536e-05f, -1.013564179e-04f, 1.929287246e-04f, -4.148490552e-04f, 8.785715909e-04f, + -1.759732841e-03f, 3.553108079e-03f, -8.337127045e-03f, -7.516627014e-02f, -4.098928068e-03f, + -4.242026061e-02f, -1.661794633e-01f, 8.227108046e-03f, 2.670205198e-02f, -1.201812327e-01f, + -1.325891465e-01f, 1.218381226e-01f, 9.328480810e-02f, -1.309523284e-01f, -1.312813759e-01f, + -5.425001308e-02f, 7.653307170e-02f, -4.227804765e-02f, -1.829808801e-01f, -1.891041100e-01f, + -1.901867241e-02f, 2.051078901e-02f, -5.437640473e-02f, -1.609673537e-02f, -5.490825325e-02f, + 2.657425031e-02f, -8.807292581e-02f, -8.466633037e-03f, -6.453436613e-02f, -8.124487102e-02f, + -6.855908316e-03f, 7.907085866e-02f, -6.257387251e-02f, -1.831467450e-02f, -4.742930830e-02f, + 9.069973230e-02f, -1.195786595e-01f, -6.070652977e-02f, -4.843581095e-02f, -1.664664447e-01f, + -2.976723388e-02f, -3.971039504e-02f, -5.024405569e-02f, 1.606530994e-01f, 8.599141985e-02f, + -8.928617835e-02f, 1.391319036e-01f, -8.829476684e-02f, -3.270891681e-02f, -6.929461844e-03f, + 6.967117661e-04f, 1.997491121e-01f, 4.079115018e-02f, -1.201048940e-01f, -1.720266044e-02f, + -1.018534377e-01f, 1.299921125e-01f, 7.046742737e-02f, -1.758096665e-01f, 3.179998696e-02f, + 4.610741511e-03f, 9.608986974e-02f, -8.361396194e-02f, -1.597236693e-01f, -4.003593232e-03f, + -1.391061991e-01f, 4.789104685e-03f, 5.618403479e-02f, -9.242422134e-02f, -3.904184327e-02f, + }; + + inline constexpr std::array k_chain_ratio_3_4_down_2 = { + -1.260525195e-08f, -1.328241979e-06f, -9.721408787e-06f, 5.539173435e-05f, -1.263926824e-04f, + 2.080919076e-04f, -3.048400395e-04f, 4.430256959e-04f, -7.105395198e-04f, 1.289526932e-03f, + -2.559710993e-03f, 5.708388053e-03f, -1.778608374e-02f, -1.069732755e-01f, -1.330049783e-01f, + 1.444399655e-01f, -1.296875179e-01f, 5.439921841e-02f, -8.334325254e-02f, -1.548618078e-01f, + -1.003198996e-01f, -8.674760908e-02f, -2.037393451e-01f, 1.824009866e-01f, 1.427205652e-01f, + -8.495780826e-02f, -2.030528337e-01f, -3.563035652e-02f, 7.591886912e-03f, -3.621187806e-01f, + -1.634835266e-02f, 2.104361504e-01f, 9.685637057e-02f, 2.156435251e-01f, -8.423283696e-02f, + -8.029589802e-02f, 7.260796428e-02f, -3.530961275e-01f, -1.583184600e-01f, 1.473332476e-02f, + -2.420932613e-02f, -1.704563648e-01f, 1.205800027e-01f, 1.622745693e-01f, -1.241129562e-01f, + 1.349808574e-01f, -3.339233696e-01f, -1.360086948e-01f, -1.398977637e-01f, -1.942002922e-01f, + -2.024017125e-01f, -7.655008882e-02f, 5.714797601e-02f, 1.660005748e-02f, 9.055205435e-02f, + -1.053385958e-01f, -1.539699286e-01f, 6.806361675e-02f, 1.327620149e-01f, -2.551322281e-01f, + -7.915242016e-02f, 1.869617552e-01f, -2.353051603e-01f, 2.507363558e-01f, -1.363283247e-01f, + -2.647052407e-01f, 2.460720390e-01f, -1.971015781e-01f, -5.865592044e-03f, 4.616855830e-02f, + -2.293960005e-01f, 3.546148911e-02f, -3.000357375e-02f, -2.231197804e-01f, 8.625548333e-02f, + 6.133389473e-02f, 9.478791803e-02f, 1.494916063e-02f, -6.531524658e-02f, -1.483394206e-02f, + -9.069239348e-02f, 1.513141207e-02f, 3.499122709e-02f, -2.416877300e-01f, 1.513375640e-01f, + 1.218093783e-01f, -4.885636270e-02f, 2.178243920e-02f, -2.347816527e-01f, -7.168114185e-02f, + 6.053236779e-03f, -1.053344384e-01f, -2.317244746e-02f, -1.364338677e-02f, -3.372954726e-01f, + -4.080014676e-02f, -6.922329217e-02f, -1.109631080e-02f, -1.035702508e-02f, -9.410297126e-02f, + -2.901512757e-02f, -5.361341313e-02f, 7.169602811e-02f, -1.869517961e-03f, 3.685490489e-01f, + -1.424846798e-02f, 2.092299759e-01f, -2.421266139e-01f, -1.078102514e-01f, 1.828399152e-01f, + 1.245030090e-01f, 4.009608552e-02f, -1.348937750e-01f, -1.825019717e-02f, -4.271082208e-02f, + -1.620396972e-01f, 2.009027004e-01f, -1.585160643e-01f, 4.143446684e-02f, -2.568087913e-02f, + 1.103314832e-01f, 1.759618670e-01f, 1.493318975e-01f, 2.191381454e-01f, -7.218666375e-02f, + -2.238478959e-01f, 8.034097403e-02f, -1.886836886e-01f, -4.736321419e-02f, 1.986315548e-01f, + -4.114490449e-01f, 1.120276377e-02f, 2.183550000e-01f, 1.121925563e-01f, 5.792768672e-02f, + 2.621578611e-02f, 8.110517263e-02f, -3.245597780e-01f, -1.083876640e-01f, -2.742704004e-02f, + 1.995406747e-01f, -4.310029745e-02f, -4.065555707e-02f, 3.461950272e-02f, 1.842028052e-01f, + 7.938063145e-02f, -2.334578335e-01f, -8.759476990e-02f, -1.375592053e-01f, -1.989566386e-01f, + 3.666681424e-02f, 8.163655549e-02f, -2.586664259e-01f, -1.038620248e-01f, -6.258931477e-03f, + -2.657445706e-02f, -4.833863303e-02f, 1.790552288e-01f, 1.109890118e-01f, -3.086454570e-01f, + 4.967105389e-02f, -1.994212270e-01f, 2.015095204e-01f, -6.712452322e-02f, -1.240525618e-01f, + 3.011454642e-01f, 5.910168588e-02f, -1.908842176e-01f, -3.839240670e-01f, 2.618443966e-01f, + -1.253492683e-01f, -2.721975446e-01f, -7.465651631e-02f, 7.052536588e-03f, 2.495337278e-01f, + 7.466342300e-02f, 1.142069791e-02f, 1.025543641e-02f, 1.072193533e-01f, 1.224721670e-01f, + }; + + inline constexpr std::array k_chain_up_2_ratio_4_3 = { + -3.361671475e-08f, 0.000000000e+00f, 1.253381697e-06f, -4.073054242e-06f, 4.786095360e-06f, + 0.000000000e+00f, -6.244367250e-05f, -1.209186885e-04f, 1.207414534e-04f, 5.481837434e-04f, + 3.507538640e-04f, -9.745176067e-04f, -1.879625022e-03f, 0.000000000e+00f, 3.641281510e-03f, + 3.760672873e-03f, -2.659738064e-03f, -8.911188692e-03f, -4.365415312e-03f, 1.006965805e-02f, + 1.636232808e-02f, 0.000000000e+00f, -2.461388521e-02f, -2.303515561e-02f, 1.506242435e-02f, + 4.750296101e-02f, 2.204621583e-02f, -4.908475652e-02f, -7.834533602e-02f, 0.000000000e+00f, + 1.207482889e-01f, 1.197577566e-01f, -8.922293037e-02f, -3.731868267e-01f, -4.662664533e-01f, + -2.342350781e-01f, 1.552018076e-01f, 3.642242253e-01f, 2.054823637e-01f, -1.817849874e-01f, + -4.808095098e-01f, -5.058401823e-01f, -3.332006037e-01f, -1.496706605e-01f, -3.171688318e-02f, + 8.054351807e-02f, 2.220060974e-01f, 3.039716482e-01f, 2.201896161e-01f, 2.277192846e-02f, + -9.227905422e-02f, -3.143305192e-03f, 1.762849092e-01f, 2.062820345e-01f, 1.380409929e-03f, + -2.529745400e-01f, -2.954624295e-01f, -8.444348723e-02f, 1.489462703e-01f, 1.518293321e-01f, + -7.975117862e-02f, -3.089904785e-01f, -3.204034567e-01f, -1.280917376e-01f, 7.963912189e-02f, + 1.638594419e-01f, 1.533769667e-01f, 1.507356018e-01f, 1.780821979e-01f, 1.574890167e-01f, + 3.336494043e-02f, -1.382346004e-01f, -2.428221256e-01f, -2.373009473e-01f, -1.968737394e-01f, + -2.233130634e-01f, -3.260216713e-01f, -4.003967047e-01f, -3.258733451e-01f, -8.793732524e-02f, + 1.860573739e-01f, 3.110820949e-01f, 1.886207014e-01f, -1.034432650e-01f, -3.563691676e-01f, + -3.960433900e-01f, -2.319923043e-01f, -4.413532838e-02f, -6.942134351e-03f, -1.184781566e-01f, + -2.156111896e-01f, -1.622167975e-01f, -5.812917836e-03f, 7.660216838e-02f, -1.754325069e-02f, + -1.975661516e-01f, -2.796374857e-01f, -1.918000281e-01f, -4.789688438e-02f, -1.448436547e-02f, + -1.291126162e-01f, -2.696319520e-01f, -2.930705249e-01f, -1.753919423e-01f, -1.200888120e-02f, + 9.656518698e-02f, 1.347706318e-01f, 1.559542120e-01f, 2.065791190e-01f, 2.815658450e-01f, + 3.471962810e-01f, 3.823187947e-01f, 3.900465369e-01f, 3.740629554e-01f, 3.117325306e-01f, + 1.635991335e-01f, -8.128231019e-02f, -3.620544374e-01f, -5.510791540e-01f, -5.252025723e-01f, + -2.642036676e-01f, 1.068126038e-01f, 3.802653253e-01f, 4.007566571e-01f, 1.719528437e-01f, + -1.458572298e-01f, -3.520602286e-01f, -3.522333801e-01f, -2.216067910e-01f, -1.384145021e-01f, + -2.269568145e-01f, -4.347419441e-01f, -5.619113445e-01f, -4.355804324e-01f, -7.943358272e-02f, + 2.872607708e-01f, 4.368710816e-01f, 3.337527514e-01f, 1.448854655e-01f, 5.462051556e-02f, + 8.217076957e-02f, 9.165343642e-02f, -3.255265579e-02f, -2.492674291e-01f, -4.082792699e-01f, + -4.163150489e-01f, -3.233228922e-01f, -2.460612506e-01f, -2.360184938e-01f, -2.428390384e-01f, + -2.016322464e-01f, -1.247307360e-01f, -9.168443084e-02f, -1.575073302e-01f, -2.869330645e-01f, + -3.783832192e-01f, -3.435128629e-01f, -1.677749604e-01f, 8.855872601e-02f, 3.277129531e-01f, + 4.614238143e-01f, 4.487267435e-01f, 3.130133450e-01f, 1.312541664e-01f, -4.540308844e-03f, + -3.126983717e-02f, 5.592839047e-02f, 2.004675716e-01f, 3.072074354e-01f, 2.898983359e-01f, + 1.374532729e-01f, -5.183786899e-02f, -1.216853932e-01f, 1.461181603e-02f, 2.739787698e-01f, + 4.505723119e-01f, 3.956345916e-01f, 1.656671166e-01f, -2.565014735e-02f, -1.784934849e-02f, + 1.381873786e-01f, 2.460937500e-01f, 1.810949296e-01f, 1.474832091e-02f, -8.538670093e-02f, + -5.858496577e-02f, -1.292598434e-02f, -8.231307566e-02f, -2.553967834e-01f, -3.825439215e-01f, + -3.462257385e-01f, -1.827609688e-01f, -2.249319106e-02f, 6.745278090e-02f, 1.326813400e-01f, + 2.236100882e-01f, 2.880411148e-01f, 2.185180634e-01f, 5.534411408e-03f, -2.073540986e-01f, + -2.539473176e-01f, -1.271529943e-01f, 5.223449320e-03f, -2.245760523e-02f, -2.012417316e-01f, + -3.697448671e-01f, -3.978017569e-01f, -3.151331544e-01f, -2.519885898e-01f, -2.771127224e-01f, + -3.323637843e-01f, -3.259221017e-01f, -2.545565367e-01f, -2.031646669e-01f, -2.263731956e-01f, + -2.672031224e-01f, -2.129187584e-01f, -2.378451265e-02f, 2.144119442e-01f, 3.656902611e-01f, + 3.582425714e-01f, 2.219924927e-01f, 3.090726398e-02f, -1.602373868e-01f, -3.202908039e-01f, + -4.107375443e-01f, -3.812720180e-01f, -2.115456164e-01f, 4.684323072e-02f, 2.798080444e-01f, + 3.735319376e-01f, 2.818054855e-01f, 5.475886539e-02f, -1.907766014e-01f, -3.492037356e-01f, + -3.934108615e-01f, -3.761641681e-01f, -3.534988165e-01f, -3.038654327e-01f, -1.477966756e-01f, + 1.310563684e-01f, 4.017815292e-01f, 4.798346758e-01f, 3.266109526e-01f, 1.294647753e-01f, + 1.211929321e-01f, 3.072409034e-01f, 4.317552447e-01f, 2.615651488e-01f, -1.127510518e-01f, + -3.387072384e-01f, -1.908664256e-01f, 1.498336494e-01f, 2.780227661e-01f, 2.769911289e-02f, + -3.256598711e-01f, + }; + + inline constexpr std::array k_chain_up_2_up_8_up_3 = { + -1.362491370e-11f, -9.484781710e-11f, 0.000000000e+00f, 7.156318871e-10f, 1.298627428e-09f, + 0.000000000e+00f, -3.703234830e-09f, -9.511573751e-09f, -1.772332858e-08f, -2.825765044e-08f, + -4.087389627e-08f, -5.601702213e-08f, -7.348170072e-08f, -9.256164901e-08f, -1.131958811e-07f, + -1.349934706e-07f, -1.568767516e-07f, -1.780746146e-07f, -1.980628355e-07f, -2.155818208e-07f, + -2.290312011e-07f, -2.379116211e-07f, -2.411317155e-07f, -2.363256613e-07f, -2.232664684e-07f, + -2.014942027e-07f, -1.681657409e-07f, -1.236139013e-07f, -6.849245437e-08f, 0.000000000e+00f, + 8.072259305e-08f, 1.715438458e-07f, 2.745270820e-07f, 3.876133974e-07f, 5.071014471e-07f, + 6.335565672e-07f, 7.641259572e-07f, 8.938511087e-07f, 1.021141543e-06f, 1.142771453e-06f, + 1.253312121e-06f, 1.348661726e-06f, 1.425951382e-06f, 1.480490823e-06f, 1.505763862e-06f, + 1.500212193e-06f, 1.461343118e-06f, 1.380857157e-06f, 1.259583541e-06f, 1.098964162e-06f, + 8.886702290e-07f, 6.299874826e-07f, 3.330628999e-07f, 0.000000000e+00f, -3.589561288e-07f, + -7.347036330e-07f, -1.130644364e-06f, -1.537009098e-06f, -1.940068159e-06f, -2.337761998e-06f, + -2.719288659e-06f, -3.071214906e-06f, -3.388425512e-06f, -3.661504024e-06f, -3.879803444e-06f, + -4.035836810e-06f, -4.123978215e-06f, -4.139033990e-06f, -4.072400316e-06f, -3.924240900e-06f, + -3.696672593e-06f, -3.381305760e-06f, -2.985178526e-06f, -2.518285328e-06f, -1.973535973e-06f, + -1.364604827e-06f, -7.084477716e-07f, 0.000000000e+00f, 7.420712791e-07f, 1.496165396e-06f, + 2.265018338e-06f, 3.027828370e-06f, 3.761628705e-06f, 4.466790415e-06f, 5.123986739e-06f, + 5.712438906e-06f, 6.230032795e-06f, 6.662624401e-06f, 6.994683190e-06f, 7.220955467e-06f, + 7.335652754e-06f, 7.330342214e-06f, 7.194798400e-06f, 6.933642908e-06f, 6.545902124e-06f, + 6.012977337e-06f, 5.350338597e-06f, 4.562546110e-06f, 3.622244321e-06f, 2.564058150e-06f, + 1.384591087e-06f, 0.000000000e+00f, -1.563747901e-06f, -3.286069159e-06f, -5.248602065e-06f, + -7.405506040e-06f, -9.732252693e-06f, -1.234254978e-05f, -1.517958935e-05f, -1.820178841e-05f, + -2.154402318e-05f, -2.513583422e-05f, -2.891444819e-05f, -3.302911500e-05f, -3.739391468e-05f, + -4.192056076e-05f, -4.676203389e-05f, -5.181671077e-05f, -5.696916924e-05f, -6.236315676e-05f, + -6.788387691e-05f, -7.339129661e-05f, -7.900618220e-05f, -8.460762911e-05f, -9.003790910e-05f, + -9.537792357e-05f, -1.005094455e-04f, -1.052724401e-04f, -1.097004424e-04f, -1.136953724e-04f, + -1.171098120e-04f, -1.199146427e-04f, -1.220471531e-04f, -1.233945513e-04f, -1.238602854e-04f, + -1.234357478e-04f, -1.220618506e-04f, -1.195690784e-04f, -1.160192915e-04f, -1.114226616e-04f, + -1.055320172e-04f, -9.849100752e-05f, -9.038855933e-05f, -8.089442417e-05f, -7.023800572e-05f, + -5.858688019e-05f, -4.553274994e-05f, -3.140891931e-05f, -1.642890311e-05f, 0.000000000e+00f, + 1.751539457e-05f, 3.578532778e-05f, 5.543050065e-05f, 7.600190293e-05f, 9.710888844e-05f, + 1.194677825e-04f, 1.425812952e-04f, 1.659839036e-04f, 1.904427045e-04f, 2.154253889e-04f, + 2.404013358e-04f, 2.661533072e-04f, 2.921317646e-04f, 3.177516919e-04f, 3.437656851e-04f, + 3.696286003e-04f, 3.947179066e-04f, 4.197030503e-04f, 4.440696794e-04f, 4.671822535e-04f, + 4.895722377e-04f, 5.107913166e-04f, 5.302214413e-04f, 5.481837434e-04f, 5.643227487e-04f, + 5.780961365e-04f, 5.895944196e-04f, 5.986139877e-04f, 6.047267816e-04f, 6.077252328e-04f, + 6.075904239e-04f, 6.040778244e-04f, 5.966727040e-04f, 5.855894415e-04f, 5.708177923e-04f, + 5.515239900e-04f, 5.281920312e-04f, 5.010875757e-04f, 4.690563946e-04f, 4.328727082e-04f, + 3.931004030e-04f, 3.482702596e-04f, 2.994453534e-04f, 2.474833454e-04f, 1.906344696e-04f, + 1.302699529e-04f, 6.746301369e-05f, 0.000000000e+00f, -7.061694487e-05f, -1.429713884e-04f, + -2.193807304e-04f, -2.981253201e-04f, -3.775972291e-04f, -4.603116831e-04f, -5.444470444e-04f, + -6.282080431e-04f, -7.141704555e-04f, -8.004781557e-04f, -8.852127357e-04f, -9.708967991e-04f, + -1.055719680e-03f, -1.137706684e-03f, -1.219193451e-03f, -1.298500807e-03f, -1.373674837e-03f, + -1.446717419e-03f, -1.516170683e-03f, -1.580188051e-03f, -1.640290953e-03f, -1.695345854e-03f, + -1.743699308e-03f, -1.786194160e-03f, -1.822078018e-03f, -1.850046450e-03f, -1.870235428e-03f, + -1.882418408e-03f, -1.885712845e-03f, -1.879362739e-03f, -1.863715355e-03f, -1.838469179e-03f, + -1.801979612e-03f, -1.755258301e-03f, -1.698681852e-03f, -1.629705890e-03f, -1.550060464e-03f, + -1.460873056e-03f, -1.358724316e-03f, -1.246076310e-03f, -1.124834293e-03f, -9.907546919e-04f, + -8.469944005e-04f, -6.962031475e-04f, -5.334301386e-04f, -3.625182144e-04f, -1.866833481e-04f, + 0.000000000e+00f, 1.933806489e-04f, 3.894856782e-04f, 5.944743170e-04f, 8.037845255e-04f, + 1.013019937e-03f, 1.228662324e-03f, 1.446011011e-03f, 1.660319278e-03f, 1.878059935e-03f, + 2.094581025e-03f, 2.304974943e-03f, 2.515426371e-03f, 2.721542725e-03f, 2.918449929e-03f, + 3.111734521e-03f, 3.297481919e-03f, 3.471055534e-03f, 3.637121292e-03f, 3.792454023e-03f, + 3.932866734e-03f, 4.061790649e-03f, 4.176919814e-03f, 4.274703562e-03f, 4.356910475e-03f, + 4.422248807e-03f, 4.468142521e-03f, 4.494683817e-03f, 4.501889925e-03f, 4.488270730e-03f, + 4.451846704e-03f, 4.393987358e-03f, 4.314595368e-03f, 4.209679086e-03f, 4.082098603e-03f, + 3.933302127e-03f, 3.757296130e-03f, 3.558487864e-03f, 3.339965828e-03f, 3.093832172e-03f, + 2.826009179e-03f, 2.541226801e-03f, 2.229842125e-03f, 1.899158116e-03f, 1.555433962e-03f, + 1.187568181e-03f, 8.041514084e-04f, 4.126006970e-04f, 0.000000000e+00f, -4.245020391e-04f, + -8.520761039e-04f, -1.296063652e-03f, -1.746665337e-03f, -2.194289584e-03f, -2.652729396e-03f, + -3.112077480e-03f, -3.562195925e-03f, -4.016655963e-03f, -4.465842620e-03f, -4.899484571e-03f, + -5.330357235e-03f, -5.749573931e-03f, -6.147145294e-03f, -6.534437649e-03f, -6.903728936e-03f, + -7.245735731e-03f, -7.569791749e-03f, -7.869750261e-03f, -8.137451485e-03f, -8.379610255e-03f, + -8.592081256e-03f, -8.768180385e-03f, -8.911188692e-03f, -9.019105695e-03f, -9.087357670e-03f, + -9.115835652e-03f, -9.105218574e-03f, -9.053193033e-03f, -8.955523372e-03f, -8.815574460e-03f, + -8.633842692e-03f, -8.402138948e-03f, -8.126724511e-03f, -7.811118849e-03f, -7.443257608e-03f, + -7.032331079e-03f, -6.584999617e-03f, -6.085561588e-03f, -5.546007771e-03f, -4.976079799e-03f, + -4.356797785e-03f, -3.702644492e-03f, -3.026176477e-03f, -2.305726055e-03f, -1.558012213e-03f, + -7.977085188e-04f, 0.000000000e+00f, 8.174677496e-04f, 1.637575682e-03f, 2.485845704e-03f, + 3.343657125e-03f, 4.192613065e-03f, 5.058818497e-03f, 5.923659541e-03f, 6.767935120e-03f, + 7.617103867e-03f, 8.453316055e-03f, 9.257352911e-03f, 1.005296409e-02f, 1.082386728e-02f, + 1.155161299e-02f, 1.225713082e-02f, 1.292650029e-02f, 1.354282256e-02f, 1.412309892e-02f, + 1.465650089e-02f, 1.512843184e-02f, 1.555094123e-02f, 1.591703296e-02f, 1.621502079e-02f, + 1.645057648e-02f, 1.662073657e-02f, 1.671783440e-02f, 1.674132608e-02f, 1.669318974e-02f, + 1.656998135e-02f, 1.636365429e-02f, 1.608105563e-02f, 1.572387479e-02f, 1.527696382e-02f, + 1.475233678e-02f, 1.415711641e-02f, 1.346924435e-02f, 1.270588115e-02f, 1.187970676e-02f, + 1.096224133e-02f, 9.975484572e-03f, 8.937491104e-03f, 7.814056240e-03f, 6.631406955e-03f, + 5.412440747e-03f, 4.118322860e-03f, 2.778982511e-03f, 1.420908724e-03f, 0.000000000e+00f, + -1.452378463e-03f, -2.905689413e-03f, -4.405135289e-03f, -5.917925853e-03f, -7.411483210e-03f, + -8.931743912e-03f, -1.044616941e-02f, -1.192102954e-02f, -1.340087689e-02f, -1.485475060e-02f, + -1.624914445e-02f, -1.762537844e-02f, -1.895545237e-02f, -2.020745166e-02f, -2.141756378e-02f, + -2.256209776e-02f, -2.361207269e-02f, -2.459667809e-02f, -2.549774759e-02f, -2.629051730e-02f, + -2.699546702e-02f, -2.760118619e-02f, -2.808814310e-02f, -2.846571803e-02f, -2.872954682e-02f, + -2.886717767e-02f, -2.887727693e-02f, -2.876401320e-02f, -2.852229401e-02f, -2.813793346e-02f, + -2.762343548e-02f, -2.698254399e-02f, -2.618899569e-02f, -2.526405454e-02f, -2.422071807e-02f, + -2.302104793e-02f, -2.169494331e-02f, -2.026476897e-02f, -1.868179813e-02f, -1.698396727e-02f, + -1.520259678e-02f, -1.327941101e-02f, -1.125926338e-02f, -9.181459434e-03f, -6.980068516e-03f, + -4.705967847e-03f, -2.404216444e-03f, 0.000000000e+00f, 2.453745576e-03f, 4.905335605e-03f, + 7.431159727e-03f, 9.976129979e-03f, 1.248539798e-02f, 1.503639575e-02f, 1.757469587e-02f, + 2.004373632e-02f, 2.251842432e-02f, 2.494717389e-02f, 2.727398090e-02f, 2.956811711e-02f, + 3.178310394e-02f, 3.386573866e-02f, 3.587649390e-02f, 3.777618334e-02f, 3.951663524e-02f, + 4.114640132e-02f, 4.263559356e-02f, 4.394317418e-02f, 4.510299861e-02f, 4.609647021e-02f, + 4.689137265e-02f, 4.750296101e-02f, 4.792439193e-02f, 4.813534766e-02f, 4.813322425e-02f, + 4.792539403e-02f, 4.750389233e-02f, 4.684479535e-02f, 4.596934095e-02f, 4.488435015e-02f, + 4.354595765e-02f, 4.198995233e-02f, 4.023855552e-02f, 3.822871298e-02f, 3.601050004e-02f, + 3.362162411e-02f, 3.098132648e-02f, 2.815290354e-02f, 2.518876828e-02f, 2.199244685e-02f, + 1.863852143e-02f, 1.519227028e-02f, 1.154492050e-02f, 7.781466469e-03f, 3.975322470e-03f, + 0.000000000e+00f, -4.057758488e-03f, -8.112457581e-03f, -1.229142025e-02f, -1.650371589e-02f, + -2.065903507e-02f, -2.488707565e-02f, -2.909794077e-02f, -3.319802135e-02f, -3.731267899e-02f, + -4.135643318e-02f, -4.523597658e-02f, -4.906692356e-02f, -5.277179927e-02f, -5.626125261e-02f, + -5.963564292e-02f, -6.282909214e-02f, -6.576004624e-02f, -6.850834191e-02f, -7.102321088e-02f, + -7.323499769e-02f, -7.519739121e-02f, -7.687628269e-02f, -7.822007686e-02f, -7.926012576e-02f, + -7.998640090e-02f, -8.036331832e-02f, -8.038638532e-02f, -8.006720245e-02f, -7.939225435e-02f, + -7.832316309e-02f, -7.689496130e-02f, -7.511801273e-02f, -7.292003930e-02f, -7.036034763e-02f, + -6.747429073e-02f, -6.415621936e-02f, -6.048924848e-02f, -5.653384700e-02f, -5.215269700e-02f, + -4.745100066e-02f, -4.251338914e-02f, -3.717360273e-02f, -3.155611083e-02f, -2.576772869e-02f, + -1.961721852e-02f, -1.324448083e-02f, -6.775729358e-03f, 0.000000000e+00f, 6.932216696e-03f, + 1.387839671e-02f, 2.106159925e-02f, 2.832573466e-02f, 3.551604971e-02f, 4.285887256e-02f, + 5.019771308e-02f, 5.737081915e-02f, 6.459905207e-02f, 7.173147798e-02f, 7.860510796e-02f, + 8.542609215e-02f, 9.205538779e-02f, 9.833502769e-02f, 1.044471264e-01f, 1.102709845e-01f, + 1.156602129e-01f, 1.207636297e-01f, 1.254851967e-01f, 1.296968609e-01f, 1.335047334e-01f, + 1.368423104e-01f, 1.396077722e-01f, 1.418608576e-01f, 1.435723901e-01f, 1.446698010e-01f, + 1.451544166e-01f, 1.450368613e-01f, 1.442799121e-01f, 1.428204775e-01f, 1.407109648e-01f, + 1.379552335e-01f, 1.344248950e-01f, 1.302171797e-01f, 1.253797859e-01f, 1.197171733e-01f, + 1.133721471e-01f, 1.064381078e-01f, 9.865221381e-02f, 9.020193666e-02f, 8.122652769e-02f, + 7.139746845e-02f, 6.094417721e-02f, 5.005060881e-02f, 3.832582012e-02f, 2.603621408e-02f, + 1.340901945e-02f, 0.000000000e+00f, -1.389139611e-02f, -2.799991146e-02f, -4.281771556e-02f, + -5.802081525e-02f, -7.330887765e-02f, -8.920720220e-02f, -1.053734496e-01f, -1.214796528e-01f, + -1.380723566e-01f, -1.547994167e-01f, -1.713132262e-01f, -1.881681830e-01f, -2.050119191e-01f, + -2.214863896e-01f, -2.381399423e-01f, -2.546302378e-01f, -2.705986798e-01f, -2.865732014e-01f, + -3.022318780e-01f, -3.172257543e-01f, -3.320475519e-01f, -3.464062810e-01f, -3.599723577e-01f, + -3.731868267e-01f, -3.857975304e-01f, -3.975054920e-01f, -4.086915553e-01f, -4.191547334e-01f, + -4.286308587e-01f, -4.374151826e-01f, -4.453591108e-01f, -4.522477090e-01f, -4.582886398e-01f, + -4.633944929e-01f, -4.674060345e-01f, -4.704346657e-01f, -4.724593163e-01f, -4.733828902e-01f, + -4.732144475e-01f, -4.720024168e-01f, -4.697161913e-01f, -4.662584066e-01f, -4.617477357e-01f, + -4.562225044e-01f, -4.494770765e-01f, -4.416984022e-01f, -4.329952002e-01f, -4.230560064e-01f, + -4.121320546e-01f, -4.004021883e-01f, -3.874583840e-01f, -3.736139536e-01f, -3.591108322e-01f, + -3.434431851e-01f, -3.269741833e-01f, -3.100091517e-01f, -2.919645309e-01f, -2.732479572e-01f, + -2.542192340e-01f, -2.342295349e-01f, -2.137212753e-01f, -1.930997670e-01f, -1.716654003e-01f, + -1.498839259e-01f, -1.281955540e-01f, -1.058658659e-01f, -8.337137848e-02f, -6.117598712e-02f, + -3.852701560e-02f, -1.589687355e-02f, 6.235862616e-03f, 2.861472405e-02f, 5.077922344e-02f, + 7.225344330e-02f, 9.376214445e-02f, 1.148737594e-01f, 1.351232231e-01f, 1.551901400e-01f, + 1.746810973e-01f, 1.931563467e-01f, 2.112337649e-01f, 2.285687476e-01f, 2.447595000e-01f, + 2.603471279e-01f, 2.750459015e-01f, 2.885034084e-01f, 3.011708260e-01f, 3.128298223e-01f, + 3.231869638e-01f, 3.325919807e-01f, 3.409003019e-01f, 3.478846252e-01f, 3.537846208e-01f, + 3.585355282e-01f, 3.619784117e-01f, 3.642242253e-01f, 3.652815521e-01f, 3.650724590e-01f, + 3.635946214e-01f, 3.609389067e-01f, 3.571015894e-01f, 3.519514203e-01f, 3.456527889e-01f, + 3.382818997e-01f, 3.295899630e-01f, 3.198125064e-01f, 3.091023862e-01f, 2.970995903e-01f, + 2.841051519e-01f, 2.703439593e-01f, 2.553540170e-01f, 2.394937873e-01f, 2.230536491e-01f, + 2.054805905e-01f, 1.871807426e-01f, 1.685016155e-01f, 1.488120854e-01f, 1.285545528e-01f, + 1.081246957e-01f, 8.682923019e-02f, 6.513480097e-02f, 4.347560182e-02f, 2.111603320e-02f, + -1.463507302e-03f, -2.380284667e-02f, -4.666429013e-02f, -6.956693530e-02f, -9.203512222e-02f, + -1.148368567e-01f, -1.375011951e-01f, -1.595504433e-01f, -1.817422062e-01f, -2.036269456e-01f, + -2.247372121e-01f, -2.458048463e-01f, -2.664124668e-01f, -2.861141562e-01f, -3.056013882e-01f, + -3.244982064e-01f, -3.423892856e-01f, -3.599138558e-01f, -3.767482042e-01f, -3.925103843e-01f, + -4.077575505e-01f, -4.222128093e-01f, -4.355462790e-01f, -4.482462108e-01f, -4.600969553e-01f, + -4.708171785e-01f, -4.808047414e-01f, -4.899027646e-01f, -4.978867471e-01f, -5.050635338e-01f, + -5.113360286e-01f, -5.165391564e-01f, -5.208883286e-01f, -5.243455172e-01f, -5.268062353e-01f, + -5.283961892e-01f, -5.291341543e-01f, -5.289748907e-01f, -5.279575586e-01f, -5.261536837e-01f, + -5.235731602e-01f, -5.201752186e-01f, -5.160819292e-01f, -5.113480091e-01f, -5.058401823e-01f, + -4.997118711e-01f, -4.930709302e-01f, -4.857283831e-01f, -4.778748453e-01f, -4.696537554e-01f, + -4.608127177e-01f, -4.515662491e-01f, -4.420916736e-01f, -4.320891500e-01f, -4.217908978e-01f, + -4.113997221e-01f, -4.005785286e-01f, -3.895701170e-01f, -3.785952032e-01f, -3.672895730e-01f, + -3.558996320e-01f, -3.446565866e-01f, -3.331787288e-01f, -3.217095137e-01f, -3.104835451e-01f, + -2.991112173e-01f, -2.878289521e-01f, -2.768668830e-01f, -2.658279240e-01f, -2.549317777e-01f, + -2.444057167e-01f, -2.338683307e-01f, -2.235280871e-01f, -2.135972381e-01f, -2.037000805e-01f, + -1.940256506e-01f, -1.847741306e-01f, -1.755879670e-01f, -1.666387320e-01f, -1.581106186e-01f, + -1.496649534e-01f, -1.414562762e-01f, -1.336518079e-01f, -1.259317696e-01f, -1.184346154e-01f, + -1.113106906e-01f, -1.042578965e-01f, -9.740036726e-02f, -9.087283164e-02f, -8.438880742e-02f, + -7.806083560e-02f, -7.201044261e-02f, -6.596313417e-02f, -6.002160907e-02f, -5.429944769e-02f, + -4.853880405e-02f, -4.284172505e-02f, -3.731101006e-02f, -3.168774769e-02f, -2.607008629e-02f, + -2.056048252e-02f, -1.490601897e-02f, -9.208091535e-03f, -3.570331959e-03f, 2.258879831e-03f, + 8.171389811e-03f, 1.405915152e-02f, 2.017590776e-02f, 2.640449814e-02f, 3.262997419e-02f, + 3.911111876e-02f, 4.572058469e-02f, 5.233474076e-02f, 5.921939760e-02f, 6.623674184e-02f, + 7.325340807e-02f, 8.054351807e-02f, 8.796004206e-02f, 9.535859525e-02f, 1.030189022e-01f, + 1.107853353e-01f, 1.185033768e-01f, 1.264567077e-01f, 1.344835907e-01f, 1.424202025e-01f, + 1.505498737e-01f, 1.587073654e-01f, 1.667220592e-01f, 1.748725027e-01f, 1.829937100e-01f, + 1.909114867e-01f, 1.988943219e-01f, 2.067819238e-01f, 2.144005150e-01f, 2.220026255e-01f, + 2.294375449e-01f, 2.365364581e-01f, 2.435297966e-01f, 2.502824068e-01f, 2.566345632e-01f, + 2.627833188e-01f, 2.686101794e-01f, 2.739745975e-01f, 2.790482640e-01f, 2.837438285e-01f, + 2.879341245e-01f, 2.917353511e-01f, 2.950839996e-01f, 2.978822887e-01f, 3.002023399e-01f, + 3.020135462e-01f, 3.032492399e-01f, 3.039321601e-01f, 3.040683568e-01f, 3.036259115e-01f, + 3.025753796e-01f, 3.009611666e-01f, 2.987881899e-01f, 2.959727943e-01f, 2.925976515e-01f, + 2.887049615e-01f, 2.841571569e-01f, 2.790755630e-01f, 2.735365927e-01f, 2.673385441e-01f, + 2.606292963e-01f, 2.535291016e-01f, 2.458032817e-01f, 2.376383692e-01f, 2.291797847e-01f, + 2.201326936e-01f, 2.107026130e-01f, 2.010708302e-01f, 1.909110099e-01f, 1.804487258e-01f, + 1.698927581e-01f, 1.588923037e-01f, 1.476876289e-01f, 1.365091950e-01f, 1.249913722e-01f, + 1.133818477e-01f, 1.019256189e-01f, 9.025201946e-02f, 7.860795408e-02f, 6.724463403e-02f, + 5.579750612e-02f, 4.450565204e-02f, 3.361590207e-02f, 2.277192846e-02f, 1.219029725e-02f, + 2.112715272e-03f, -7.780542132e-03f, -1.729378104e-02f, -2.620722540e-02f, -3.480980545e-02f, + -4.294003919e-02f, -5.040144175e-02f, -5.743435398e-02f, -6.391441822e-02f, -6.967692822e-02f, + -7.490730286e-02f, -7.952241600e-02f, -8.339484036e-02f, -8.664960414e-02f, -8.924774826e-02f, + -9.110336006e-02f, -9.227728099e-02f, -9.277612716e-02f, -9.255822748e-02f, -9.161832184e-02f, + -9.000821412e-02f, -8.773184568e-02f, -8.471831679e-02f, -8.106246591e-02f, -7.681394368e-02f, + -7.183984667e-02f, -6.627478451e-02f, -6.021231040e-02f, -5.346274376e-02f, -4.619530216e-02f, + -3.854458407e-02f, -3.027291223e-02f, -2.157824486e-02f, -1.263109129e-02f, -3.155350918e-03f, + 6.629651878e-03f, 1.652413607e-02f, 2.683177777e-02f, 3.732045740e-02f, 4.776905105e-02f, + 5.849697441e-02f, 6.926871091e-02f, 7.985059172e-02f, 9.056501091e-02f, 1.011826843e-01f, + 1.114667431e-01f, 1.217298433e-01f, 1.317590028e-01f, 1.413227320e-01f, 1.507088542e-01f, + 1.597290486e-01f, 1.681667864e-01f, 1.762716770e-01f, 1.838878691e-01f, 1.908232570e-01f, + 1.972783208e-01f, 2.031369507e-01f, 2.082400769e-01f, 2.127296180e-01f, 2.165353894e-01f, + 2.195387632e-01f, 2.218152583e-01f, 2.233452648e-01f, 2.240562290e-01f, 2.239513248e-01f, + 2.230640352e-01f, 2.213717550e-01f, 2.188015878e-01f, 2.154414803e-01f, 2.113198787e-01f, + 2.062820345e-01f, 2.004662007e-01f, 1.939559281e-01f, 1.865221262e-01f, 1.783526540e-01f, + 1.695838869e-01f, 1.599157602e-01f, 1.495809853e-01f, 1.387663931e-01f, 1.271085143e-01f, + 1.148786768e-01f, 1.023095697e-01f, 8.898419887e-02f, 7.520484924e-02f, 6.124344468e-02f, + 4.664082825e-02f, 3.172112256e-02f, 1.678725332e-02f, 1.350549865e-03f, -1.425326522e-02f, + -2.969976328e-02f, -4.549298435e-02f, -6.129586324e-02f, -7.677346468e-02f, -9.243305773e-02f, + -1.079484522e-01f, -1.229827777e-01f, -1.380298436e-01f, -1.527857780e-01f, -1.669194251e-01f, + -1.808871478e-01f, -1.944121271e-01f, -2.071840167e-01f, -2.196148038e-01f, -2.314660400e-01f, + -2.424571663e-01f, -2.529429197e-01f, -2.627317011e-01f, -2.715820074e-01f, -2.797806263e-01f, + -2.871893644e-01f, -2.936135828e-01f, -2.992629111e-01f, -3.040572107e-01f, -3.078544736e-01f, + -3.107801676e-01f, -3.128150105e-01f, -3.138732910e-01f, -3.139892519e-01f, -3.132022917e-01f, + -3.114883900e-01f, -3.087972701e-01f, -3.052303791e-01f, -3.008205593e-01f, -2.954229414e-01f, + -2.891916931e-01f, -2.822211683e-01f, -2.742859721e-01f, -2.655907869e-01f, -2.562868893e-01f, + -2.460755259e-01f, -2.352058142e-01f, -2.238805741e-01f, -2.117376477e-01f, -1.990622729e-01f, + -1.861015558e-01f, -1.724412441e-01f, -1.583928615e-01f, -1.442390233e-01f, -1.295263916e-01f, + -1.145821065e-01f, -9.971427917e-02f, -8.444348723e-02f, -6.909981370e-02f, -5.400849879e-02f, + -3.868409619e-02f, -2.345036343e-02f, -8.637681603e-03f, 6.230630912e-03f, 2.084829286e-02f, + 3.489023447e-02f, 4.880747199e-02f, 6.232089177e-02f, 7.512217760e-02f, 8.762250841e-02f, + 9.957940131e-02f, 1.107117683e-01f, 1.213779673e-01f, 1.313805133e-01f, 1.404748410e-01f, + 1.489549577e-01f, 1.566750705e-01f, 1.634349674e-01f, 1.694543958e-01f, 1.746442020e-01f, + 1.788544655e-01f, 1.822219789e-01f, 1.847161055e-01f, 1.862426549e-01f, 1.868523210e-01f, + 1.865747273e-01f, 1.853732914e-01f, 1.832085699e-01f, 1.801681519e-01f, 1.762755066e-01f, + 1.714042127e-01f, 1.656975895e-01f, 1.592381299e-01f, 1.518166661e-01f, 1.436279565e-01f, + 1.348105222e-01f, 1.250789762e-01f, 1.146732569e-01f, 1.037831157e-01f, 9.205523878e-02f, + 7.976697385e-02f, 6.715298444e-02f, 5.380175635e-02f, 4.001796246e-02f, 2.607505210e-02f, + 1.152049843e-02f, -3.320382908e-03f, -1.814727485e-02f, -3.344769031e-02f, -4.889034852e-02f, + -6.415262818e-02f, -7.973239571e-02f, -9.529846907e-02f, -1.105196625e-01f, -1.258939505e-01f, + -1.411015242e-01f, -1.558126211e-01f, -1.705109775e-01f, -1.848982275e-01f, -1.986565143e-01f, + -2.122420818e-01f, -2.253871560e-01f, -2.377952337e-01f, -2.498825639e-01f, -2.614196241e-01f, + -2.721391320e-01f, -2.824068367e-01f, -2.920401394e-01f, -3.008040190e-01f, -3.089904785e-01f, + -3.164595664e-01f, -3.230239749e-01f, -3.289142847e-01f, -3.340463042e-01f, -3.382756710e-01f, + -3.417496085e-01f, -3.444345891e-01f, -3.462384641e-01f, -3.472299278e-01f, -3.474279940e-01f, + -3.467936814e-01f, -3.453170061e-01f, -3.430685997e-01f, -3.400628865e-01f, -3.362134099e-01f, + -3.316396475e-01f, -3.264073730e-01f, -3.203576803e-01f, -3.136540055e-01f, -3.064095378e-01f, + -2.983990312e-01f, -2.898257375e-01f, -2.808422744e-01f, -2.711545527e-01f, -2.609920800e-01f, + -2.505485117e-01f, -2.394840568e-01f, -2.280532718e-01f, -2.164794803e-01f, -2.043812573e-01f, + -1.920287758e-01f, -1.796694100e-01f, -1.668923050e-01f, -1.539758146e-01f, -1.411833614e-01f, + -1.280854940e-01f, -1.149610952e-01f, -1.020818055e-01f, -8.901018649e-02f, -7.601830363e-02f, + -6.337846816e-02f, -5.065540224e-02f, -3.810776398e-02f, -2.600162476e-02f, -1.391325518e-02f, + -2.081812127e-03f, 9.238335304e-03f, 2.044830285e-02f, 3.133153915e-02f, 4.165206850e-02f, + 5.178402737e-02f, 6.153869256e-02f, 7.070080191e-02f, 7.960697263e-02f, 8.809641749e-02f, + 9.598016739e-02f, 1.035558730e-01f, 1.106925756e-01f, 1.172300428e-01f, 1.234251559e-01f, + 1.291773021e-01f, 1.343563646e-01f, 1.391782761e-01f, 1.435719877e-01f, 1.474374086e-01f, + 1.509503424e-01f, 1.540672183e-01f, 1.567161977e-01f, 1.590356529e-01f, 1.610081047e-01f, + 1.625845283e-01f, 1.638594419e-01f, 1.648315340e-01f, 1.654779911e-01f, 1.658663750e-01f, + 1.660129577e-01f, 1.659128219e-01f, 1.656049490e-01f, 1.651168466e-01f, 1.644591391e-01f, + 1.636491418e-01f, 1.627212614e-01f, 1.616974175e-01f, 1.605793238e-01f, 1.594041735e-01f, + 1.582009792e-01f, 1.569617838e-01f, 1.557227224e-01f, 1.545157135e-01f, 1.533285826e-01f, + 1.521929502e-01f, 1.511393189e-01f, 1.501570493e-01f, 1.492722929e-01f, 1.485078037e-01f, + 1.478485018e-01f, 1.473064423e-01f, 1.469012946e-01f, 1.466323435e-01f, 1.465042382e-01f, + 1.465243548e-01f, 1.466961950e-01f, 1.470121145e-01f, 1.474700123e-01f, 1.480831951e-01f, + 1.488334239e-01f, 1.497079283e-01f, 1.507290453e-01f, 1.518693566e-01f, 1.531059891e-01f, + 1.544689387e-01f, 1.559236050e-01f, 1.574382484e-01f, 1.590481997e-01f, 1.607141048e-01f, + 1.623969674e-01f, 1.641351879e-01f, 1.658898294e-01f, 1.676141918e-01f, 1.693348885e-01f, + 1.710090339e-01f, 1.725943685e-01f, 1.741229892e-01f, 1.755637527e-01f, 1.768703014e-01f, + 1.780522168e-01f, 1.790837348e-01f, 1.799275130e-01f, 1.805792004e-01f, 1.810260266e-01f, + 1.812411696e-01f, 1.812009215e-01f, 1.809094995e-01f, 1.803541332e-01f, 1.794877350e-01f, + 1.783342808e-01f, 1.768978983e-01f, 1.751042157e-01f, 1.729987115e-01f, 1.706041396e-01f, + 1.678166538e-01f, 1.647064835e-01f, 1.613132507e-01f, 1.574890167e-01f, 1.533201039e-01f, + 1.488744766e-01f, 1.439830661e-01f, 1.387606561e-01f, 1.332920045e-01f, 1.273645461e-01f, + 1.211109161e-01f, 1.146418452e-01f, 1.077158377e-01f, 1.004857346e-01f, 9.308515489e-02f, + 8.524561673e-02f, 7.713846862e-02f, 6.891854852e-02f, 6.029398739e-02f, 5.145161226e-02f, + 4.256452620e-02f, 3.332155943e-02f, 2.392089739e-02f, 1.454993617e-02f, 4.883582238e-03f, + -4.871680867e-03f, -1.452079974e-02f, -2.440571226e-02f, -3.432195261e-02f, -4.406481609e-02f, + -5.397445709e-02f, -6.384767592e-02f, -7.347857207e-02f, -8.320429176e-02f, -9.282863885e-02f, + -1.021477729e-01f, -1.114884466e-01f, -1.206654906e-01f, -1.294817775e-01f, -1.382478178e-01f, + -1.467933208e-01f, -1.549320817e-01f, -1.629529148e-01f, -1.707038134e-01f, -1.780132800e-01f, + -1.851436198e-01f, -1.919638366e-01f, -1.983204037e-01f, -2.044450045e-01f, -2.102297395e-01f, + -2.155417651e-01f, -2.205779999e-01f, -2.252535820e-01f, -2.294598520e-01f, -2.333629876e-01f, + -2.369057834e-01f, -2.399998754e-01f, -2.427675426e-01f, -2.451727390e-01f, -2.471538782e-01f, + -2.487993836e-01f, -2.500962615e-01f, -2.510080338e-01f, -2.515904903e-01f, -2.518525422e-01f, + -2.517815232e-01f, -2.514036298e-01f, -2.507469952e-01f, -2.498206049e-01f, -2.486242950e-01f, + -2.472018898e-01f, -2.455805540e-01f, -2.437386066e-01f, -2.417331934e-01f, -2.396033406e-01f, + -2.373009473e-01f, -2.348849773e-01f, -2.324105948e-01f, -2.298280746e-01f, -2.272041738e-01f, + -2.245959193e-01f, -2.219385654e-01f, -2.192940712e-01f, -2.167250663e-01f, -2.141691297e-01f, + -2.116828412e-01f, -2.093284428e-01f, -2.070511580e-01f, -2.048990130e-01f, -2.029300332e-01f, + -2.011027783e-01f, -1.994535029e-01f, -1.980319470e-01f, -1.968149990e-01f, -1.958239228e-01f, + -1.950959712e-01f, -1.946316957e-01f, -1.944374144e-01f, -1.945306063e-01f, -1.949249208e-01f, + -1.956003308e-01f, -1.965626627e-01f, -1.978651285e-01f, -1.994731426e-01f, -2.013668120e-01f, + -2.036176771e-01f, -2.061674446e-01f, -2.089777440e-01f, -2.121491730e-01f, -2.156036347e-01f, + -2.192825079e-01f, -2.233132720e-01f, -2.276002914e-01f, -2.320658267e-01f, -2.368621081e-01f, + -2.418785393e-01f, -2.470200360e-01f, -2.524602115e-01f, -2.580766380e-01f, -2.637587786e-01f, + -2.696982324e-01f, -2.757666409e-01f, -2.818374932e-01f, -2.881031632e-01f, -2.944252491e-01f, + -3.006736636e-01f, -3.070569038e-01f, -3.134426475e-01f, -3.196892142e-01f, -3.259909451e-01f, + -3.322165608e-01f, -3.382259607e-01f, -3.442042172e-01f, -3.500292897e-01f, -3.555655479e-01f, + -3.609803617e-01f, -3.661666214e-01f, -3.709982932e-01f, -3.756177723e-01f, -3.799378276e-01f, + -3.838468790e-01f, -3.874552548e-01f, -3.906998932e-01f, -3.934880495e-01f, -3.958913982e-01f, + -3.978755772e-01f, -3.993699253e-01f, -4.003967047e-01f, -4.009498358e-01f, -4.009886086e-01f, + -4.004916847e-01f, -3.994903266e-01f, -3.979699910e-01f, -3.958467543e-01f, -3.931840062e-01f, + -3.900026977e-01f, -3.861634731e-01f, -3.817660809e-01f, -3.768671453e-01f, -3.712697327e-01f, + -3.651106954e-01f, -3.584835231e-01f, -3.511332273e-01f, -3.432331383e-01f, -3.349136114e-01f, + -3.258621097e-01f, -3.162866831e-01f, -3.063536882e-01f, -2.956946194e-01f, -2.845483720e-01f, + -2.731171846e-01f, -2.609838247e-01f, -2.484164834e-01f, -2.356489450e-01f, -2.222173810e-01f, + -2.084135562e-01f, -1.945010424e-01f, -1.799780875e-01f, -1.651553810e-01f, -1.503225416e-01f, + -1.349491030e-01f, -1.193600446e-01f, -1.038656160e-01f, -8.791509271e-02f, -7.184172422e-02f, + -5.597064644e-02f, -3.973984346e-02f, -2.348420396e-02f, -7.537545636e-03f, 8.663970046e-03f, + 2.479063720e-02f, 4.050606117e-02f, 5.636663362e-02f, 7.205639035e-02f, 8.724015951e-02f, + 1.024475098e-01f, 1.173768565e-01f, 1.317070723e-01f, 1.459451616e-01f, 1.598185152e-01f, + 1.730177701e-01f, 1.859998852e-01f, 1.985196620e-01f, 2.102926373e-01f, 2.217250913e-01f, + 2.326072752e-01f, 2.426845729e-01f, 2.523040473e-01f, 2.612968981e-01f, 2.694443464e-01f, + 2.770271599e-01f, 2.839218974e-01f, 2.899505198e-01f, 2.953209579e-01f, 2.999584377e-01f, + 3.037289083e-01f, 3.067630529e-01f, 3.090381920e-01f, 3.104646802e-01f, 3.110820949e-01f, + 3.109134138e-01f, 3.099237978e-01f, 3.080801964e-01f, 3.054622710e-01f, 3.020797670e-01f, + 2.978039086e-01f, 2.927597463e-01f, 2.870115638e-01f, 2.803511620e-01f, 2.729511857e-01f, + 2.649272084e-01f, 2.559946477e-01f, 2.463713884e-01f, 2.362222373e-01f, 2.251914442e-01f, + 2.135380059e-01f, 2.014722973e-01f, 1.885736585e-01f, 1.751364917e-01f, 1.614118069e-01f, + 1.469219625e-01f, 1.319910735e-01f, 1.169039980e-01f, 1.011291444e-01f, 8.500810713e-02f, + 6.885972619e-02f, 5.212491751e-02f, 3.516399488e-02f, 1.831446588e-02f, 9.846648900e-04f, + -1.646165363e-02f, -3.366825357e-02f, -5.123576149e-02f, -6.880109757e-02f, -8.599942178e-02f, + -1.034294069e-01f, -1.207361296e-01f, -1.375543624e-01f, -1.544691771e-01f, -1.711417884e-01f, + -1.872148067e-01f, -2.032484710e-01f, -2.189278007e-01f, -2.339110821e-01f, -2.487228662e-01f, + -2.630795538e-01f, -2.766614556e-01f, -2.899447083e-01f, -3.026821315e-01f, -3.145834208e-01f, + -3.260665834e-01f, -3.369254768e-01f, -3.469058275e-01f, -3.563618660e-01f, -3.651334941e-01f, + -3.730066717e-01f, -3.802631497e-01f, -3.867907226e-01f, -3.924207091e-01f, -3.973594010e-01f, + -4.015441835e-01f, -4.048548937e-01f, -4.074205160e-01f, -4.092286825e-01f, -4.102092087e-01f, + -4.104135633e-01f, -4.098784626e-01f, -4.085834324e-01f, -4.065043032e-01f, -4.037260711e-01f, + -4.002741873e-01f, -3.960433900e-01f, -3.911571503e-01f, -3.856913745e-01f, -3.794792295e-01f, + -3.726850450e-01f, -3.654254079e-01f, -3.574684262e-01f, -3.490094841e-01f, -3.402054012e-01f, + -3.307730556e-01f, -3.209341466e-01f, -3.108792305e-01f, -3.002835214e-01f, -2.893881500e-01f, + -2.784109414e-01f, -2.669959366e-01f, -2.553961277e-01f, -2.438491434e-01f, -2.319787741e-01f, + -2.200418413e-01f, -2.082877904e-01f, -1.963319033e-01f, -1.844279468e-01f, -1.728278846e-01f, + -1.611437947e-01f, -1.496141702e-01f, -1.384915709e-01f, -1.274098605e-01f, -1.165927872e-01f, + -1.062789485e-01f, -9.612131119e-02f, -8.631709963e-02f, -7.708919793e-02f, -6.812670827e-02f, + -5.959749222e-02f, -5.170064420e-02f, -4.416791350e-02f, -3.713366017e-02f, -3.076763451e-02f, + -2.485003509e-02f, -1.947834529e-02f, -1.478859689e-02f, -1.061378419e-02f, -7.012473419e-03f, + -4.083990119e-03f, -1.716958941e-03f, 6.969980313e-05f, 1.214607502e-03f, 1.771545387e-03f, + 1.760440064e-03f, 1.158015104e-03f, -4.185470243e-05f, -1.785493572e-03f, -4.054886755e-03f, + -6.903831847e-03f, -1.024352480e-02f, -1.402097195e-02f, -1.833963394e-02f, -2.308229543e-02f, + -2.816283517e-02f, -3.372696415e-02f, -3.963465244e-02f, -4.577119648e-02f, -5.231665447e-02f, + -5.911432207e-02f, -6.602637470e-02f, -7.325839251e-02f, -8.064368367e-02f, -8.802758157e-02f, + -9.563137591e-02f, -1.032849327e-01f, -1.108240858e-01f, -1.184781566e-01f, -1.260816753e-01f, + -1.334664226e-01f, -1.408581138e-01f, -1.481029838e-01f, -1.550342441e-01f, -1.618592441e-01f, + -1.684392989e-01f, -1.746186763e-01f, -1.805817038e-01f, -1.862120479e-01f, -1.913708746e-01f, + -1.962113678e-01f, -2.006448954e-01f, -2.045555264e-01f, -2.080585510e-01f, -2.110971659e-01f, + -2.135832161e-01f, -2.155884504e-01f, -2.170907259e-01f, -2.180331796e-01f, -2.184398025e-01f, + -2.183242887e-01f, -2.176634818e-01f, -2.164306641e-01f, -2.146751434e-01f, -2.124088258e-01f, + -2.095533907e-01f, -2.061916888e-01f, -2.023718208e-01f, -1.979678273e-01f, -1.930946410e-01f, + -1.878345758e-01f, -1.820164770e-01f, -1.757833362e-01f, -1.692495644e-01f, -1.622045785e-01f, + -1.548147500e-01f, -1.472228765e-01f, -1.391857862e-01f, -1.308876723e-01f, -1.224948242e-01f, + -1.137394160e-01f, -1.048170105e-01f, -9.591173381e-02f, -8.673982322e-02f, -7.750115544e-02f, + -6.839147955e-02f, -5.912170187e-02f, -4.989004880e-02f, -4.089597240e-02f, -3.185048699e-02f, + -2.293950878e-02f, -1.436396036e-02f, -5.856170785e-03f, 2.411432564e-03f, 1.024865173e-02f, + 1.789998077e-02f, 2.521520481e-02f, 3.202055022e-02f, 3.852820769e-02f, 4.461632296e-02f, + 5.013404042e-02f, 5.525365844e-02f, 5.988655612e-02f, 6.391017139e-02f, 6.745021790e-02f, + 7.045472413e-02f, 7.283350080e-02f, 7.466053963e-02f, 7.592339069e-02f, 7.656607777e-02f, + 7.660216838e-02f, 7.605569810e-02f, 7.491116226e-02f, 7.313002646e-02f, 7.077769935e-02f, + 6.787459552e-02f, 6.431968510e-02f, 6.021308899e-02f, 5.561562628e-02f, 5.037328228e-02f, + 4.462017119e-02f, 3.845484927e-02f, 3.167446330e-02f, 2.444356494e-02f, 1.689553447e-02f, + 8.784621954e-03f, 3.008182975e-04f, -8.392071351e-03f, -1.757571474e-02f, -2.704089880e-02f, + -3.659899160e-02f, -4.655924439e-02f, -5.669950321e-02f, -6.681358069e-02f, -7.723288983e-02f, + -8.773212135e-02f, -9.809120744e-02f, -1.086481065e-01f, -1.191795170e-01f, -1.294596195e-01f, + -1.398230493e-01f, -1.500552297e-01f, -1.599320024e-01f, -1.697750688e-01f, -1.793854982e-01f, + -1.885480583e-01f, -1.975619346e-01f, -2.062502950e-01f, -2.144136876e-01f, -2.223201245e-01f, + -2.298204899e-01f, -2.367372215e-01f, -2.432994395e-01f, -2.493901849e-01f, -2.548590899e-01f, + -2.598896325e-01f, -2.644004524e-01f, -2.682723999e-01f, -2.716369629e-01f, -2.744494081e-01f, + -2.766261399e-01f, -2.782461643e-01f, -2.793041468e-01f, -2.797518373e-01f, -2.796091735e-01f, + -2.789070010e-01f, -2.776360214e-01f, -2.757617831e-01f, -2.733509541e-01f, -2.704322934e-01f, + -2.669194043e-01f, -2.629123032e-01f, -2.584760785e-01f, -2.534765601e-01f, -2.480431497e-01f, + -2.422741801e-01f, -2.359929383e-01f, -2.293531895e-01f, -2.224820107e-01f, -2.151669115e-01f, + -2.075803280e-01f, -1.998724043e-01f, -1.918000281e-01f, -1.835460663e-01f, -1.752801239e-01f, + -1.667418033e-01f, -1.581193954e-01f, -1.495946944e-01f, -1.408983916e-01f, -1.322173178e-01f, + -1.237391904e-01f, -1.151960418e-01f, -1.067668200e-01f, -9.863822907e-02f, -9.055304527e-02f, + -8.267600089e-02f, -7.518602163e-02f, -6.784556061e-02f, -6.079921499e-02f, -5.421182141e-02f, + -4.787371308e-02f, -4.190423712e-02f, -3.644836694e-02f, -3.133199364e-02f, -2.664557658e-02f, + -2.250864170e-02f, -1.878662035e-02f, -1.553668268e-02f, -1.285064965e-02f, -1.064162515e-02f, + -8.933099918e-03f, -7.785260677e-03f, -7.161155343e-03f, -7.050015498e-03f, -7.478259970e-03f, + -8.459558710e-03f, -9.949050844e-03f, -1.193860546e-02f, -1.449193247e-02f, -1.753146015e-02f, + -2.101494186e-02f, -2.505415119e-02f, -2.954135835e-02f, -3.440183029e-02f, -3.979212418e-02f, + -4.557796568e-02f, -5.165461823e-02f, -5.821945518e-02f, -6.511750817e-02f, -7.221568376e-02f, + -7.973974943e-02f, -8.751513809e-02f, -9.538929909e-02f, -1.036194563e-01f, -1.120225564e-01f, + -1.204263195e-01f, -1.291013807e-01f, -1.378582418e-01f, -1.465139985e-01f, -1.553484797e-01f, + -1.641729176e-01f, -1.727992594e-01f, -1.815077960e-01f, -1.901168376e-01f, -1.984394044e-01f, + -2.067480236e-01f, -2.148739547e-01f, -2.226371169e-01f, -2.302941978e-01f, -2.376944721e-01f, + -2.446697503e-01f, -2.514540255e-01f, -2.579214871e-01f, -2.639168501e-01f, -2.696319520e-01f, + -2.749614120e-01f, -2.797761559e-01f, -2.842388749e-01f, -2.882770002e-01f, -2.917827368e-01f, + -2.948694825e-01f, -2.974936664e-01f, -2.995773852e-01f, -3.011881113e-01f, -3.023143113e-01f, + -3.029093742e-01f, -3.029930592e-01f, -3.025866747e-01f, -3.016762435e-01f, -3.002335429e-01f, + -2.983122766e-01f, -2.959310710e-01f, -2.930141985e-01f, -2.896459699e-01f, -2.858760059e-01f, + -2.815836370e-01f, -2.768834829e-01f, -2.718502879e-01f, -2.663080096e-01f, -2.603897750e-01f, + -2.542023957e-01f, -2.475375384e-01f, -2.405515909e-01f, -2.333737165e-01f, -2.257583737e-01f, + -2.178783119e-01f, -2.098843902e-01f, -2.015024275e-01f, -1.929178536e-01f, -1.842989624e-01f, + -1.753492504e-01f, -1.662625521e-01f, -1.572204083e-01f, -1.479104012e-01f, -1.385304183e-01f, + -1.292706728e-01f, -1.198091283e-01f, -1.103432998e-01f, -1.010671034e-01f, -9.165599942e-02f, + -8.230470866e-02f, -7.320379466e-02f, -6.402282417e-02f, -5.494435132e-02f, -4.615839943e-02f, + -3.734686598e-02f, -2.868233621e-02f, -2.034747601e-02f, -1.203805860e-02f, -3.914188128e-03f, + 3.851702437e-03f, 1.154676452e-02f, 1.902576908e-02f, 2.612889931e-02f, 3.312305734e-02f, + 3.987935930e-02f, 4.625263065e-02f, 5.248726159e-02f, 5.847154558e-02f, 6.407634169e-02f, + 6.952203810e-02f, 7.471430302e-02f, 7.954093814e-02f, 8.419696987e-02f, 8.860363066e-02f, + 9.266766906e-02f, 9.656518698e-02f, 1.002356559e-01f, 1.035997421e-01f, 1.068074480e-01f, + 1.098115668e-01f, 1.125475392e-01f, 1.151424274e-01f, 1.175599471e-01f, 1.197491586e-01f, + 1.218179688e-01f, 1.237394959e-01f, 1.254742891e-01f, 1.271137595e-01f, 1.286382526e-01f, + 1.300177872e-01f, 1.313303262e-01f, 1.325611770e-01f, 1.336876303e-01f, 1.347774416e-01f, + 1.358186156e-01f, 1.367936730e-01f, 1.377629787e-01f, 1.387140155e-01f, 1.396339834e-01f, + 1.405837983e-01f, 1.415508837e-01f, 1.425221115e-01f, 1.435581595e-01f, 1.446436793e-01f, + 1.457626224e-01f, 1.469750255e-01f, 1.482587010e-01f, 1.495950073e-01f, 1.510500163e-01f, + 1.525949240e-01f, 1.542060226e-01f, 1.559567600e-01f, 1.578110009e-01f, 1.597386748e-01f, + 1.618220359e-01f, 1.640170217e-01f, 1.662863940e-01f, 1.687220633e-01f, 1.712719053e-01f, + 1.738910526e-01f, 1.766815931e-01f, 1.795834154e-01f, 1.825443506e-01f, 1.856768131e-01f, + 1.889124811e-01f, 1.921931356e-01f, 1.956467927e-01f, 1.992022395e-01f, 2.027897835e-01f, + 2.065380663e-01f, 2.103668749e-01f, 2.142024785e-01f, 2.181835920e-01f, 2.222256362e-01f, + 2.262499481e-01f, 2.304028869e-01f, 2.345973998e-01f, 2.387511134e-01f, 2.430162728e-01f, + 2.473047972e-01f, 2.515316904e-01f, 2.558529973e-01f, 2.601807117e-01f, 2.644280195e-01f, + 2.687533200e-01f, 2.730714977e-01f, 2.772925198e-01f, 2.815658450e-01f, 2.858034670e-01f, + 2.899211943e-01f, 2.940773666e-01f, 2.981934845e-01f, 3.021805584e-01f, 3.061800301e-01f, + 3.101131916e-01f, 3.138976097e-01f, 3.176710010e-01f, 3.213599324e-01f, 3.248872459e-01f, + 3.283849955e-01f, 3.317866027e-01f, 3.350214064e-01f, 3.382150531e-01f, 3.413085341e-01f, + 3.442379534e-01f, 3.471215665e-01f, 3.499078453e-01f, 3.525388241e-01f, 3.551261127e-01f, + 3.576289713e-01f, 3.599892557e-01f, 3.622916043e-01f, 3.644921780e-01f, 3.665473759e-01f, + 3.685474992e-01f, 3.704621196e-01f, 3.722459972e-01f, 3.739669323e-01f, 3.755961359e-01f, + 3.770971894e-01f, 3.785293698e-01f, 3.798691630e-01f, 3.810867965e-01f, 3.822331131e-01f, + 3.832903802e-01f, 3.842356205e-01f, 3.851122558e-01f, 3.859082758e-01f, 3.866067827e-01f, + 3.872443140e-01f, 3.878140450e-01f, 3.883031607e-01f, 3.887438476e-01f, 3.891381919e-01f, + 3.894692659e-01f, 3.897399902e-01f, 3.899446726e-01f, 3.900795579e-01f, 3.901568949e-01f, + 3.901816308e-01f, 3.901460171e-01f, 3.900429904e-01f, 3.898778558e-01f, 3.896481395e-01f, + 3.893385530e-01f, 3.889578879e-01f, 3.885076046e-01f, 3.879631460e-01f, 3.873380721e-01f, + 3.866382837e-01f, 3.858298063e-01f, 3.849317729e-01f, 3.839550018e-01f, 3.828550279e-01f, + 3.816573024e-01f, 3.803772926e-01f, 3.789602518e-01f, 3.774424493e-01f, 3.758386374e-01f, + 3.740629554e-01f, 3.721508086e-01f, 3.701311052e-01f, 3.679150939e-01f, 3.655531704e-01f, + 3.630763292e-01f, 3.603685200e-01f, 3.574882746e-01f, 3.544772863e-01f, 3.511988819e-01f, + 3.477236032e-01f, 3.441036940e-01f, 3.401791453e-01f, 3.360342681e-01f, 3.317336440e-01f, + 3.270915151e-01f, 3.222076893e-01f, 3.171603680e-01f, 3.117358983e-01f, 3.060507476e-01f, + 3.001980484e-01f, 2.939342856e-01f, 2.873944640e-01f, 2.806868255e-01f, 2.735320926e-01f, + 2.660817802e-01f, 2.584639490e-01f, 2.503700256e-01f, 2.419724911e-01f, 2.334169447e-01f, + 2.243586928e-01f, 2.149892896e-01f, 2.054749280e-01f, 1.954376698e-01f, 1.850892901e-01f, + 1.746160984e-01f, 1.636066735e-01f, 1.522926092e-01f, 1.408803314e-01f, 1.289251149e-01f, + 1.166775450e-01f, 1.043637022e-01f, 9.150656313e-02f, 7.837432623e-02f, 6.521250308e-02f, + 5.151245371e-02f, 3.755588830e-02f, 2.361048013e-02f, 9.150877595e-03f, -5.522049963e-03f, + -2.012819424e-02f, -3.522426262e-02f, -5.050257221e-02f, -6.566278636e-02f, -8.127029985e-02f, + -9.700654447e-02f, -1.125597209e-01f, -1.285060793e-01f, -1.445216238e-01f, -1.602854580e-01f, + -1.763786972e-01f, -1.924765259e-01f, -2.082530558e-01f, -2.242884785e-01f, -2.402619720e-01f, + -2.558467686e-01f, -2.716156840e-01f, -2.872565091e-01f, -3.024450243e-01f, -3.177411854e-01f, + -3.328497112e-01f, -3.474475443e-01f, -3.620544374e-01f, -3.763833046e-01f, -3.901312947e-01f, + -4.038046896e-01f, -4.171482921e-01f, -4.298644364e-01f, -4.424005151e-01f, -4.545215964e-01f, + -4.659546018e-01f, -4.771000743e-01f, -4.877536297e-01f, -4.976685941e-01f, -5.071904659e-01f, + -5.161508322e-01f, -5.243348479e-01f, -5.320264697e-01f, -5.390979648e-01f, -5.453702211e-01f, + -5.510597825e-01f, -5.560826659e-01f, -5.602987409e-01f, -5.638524890e-01f, -5.667086244e-01f, + -5.687652230e-01f, -5.700753331e-01f, -5.706428289e-01f, -5.704191923e-01f, -5.693958402e-01f, + -5.676355362e-01f, -5.651251674e-01f, -5.617514253e-01f, -5.576178432e-01f, -5.527651906e-01f, + -5.470008254e-01f, -5.404763222e-01f, -5.332826376e-01f, -5.251464844e-01f, -5.162678957e-01f, + -5.067877173e-01f, -4.963538945e-01f, -4.852128625e-01f, -4.735537171e-01f, -4.609483480e-01f, + -4.476861954e-01f, -4.340018928e-01f, -4.193952680e-01f, -4.041941762e-01f, -3.886758983e-01f, + -3.722717166e-01f, -3.553394973e-01f, -3.381997347e-01f, -3.202379048e-01f, -3.018460572e-01f, + -2.833722234e-01f, -2.641402185e-01f, -2.445579469e-01f, -2.250075787e-01f, -2.047805339e-01f, + -1.843006611e-01f, -1.639742106e-01f, -1.430686563e-01f, -1.220175773e-01f, -1.012444794e-01f, + -8.000302315e-02f, -5.872903392e-02f, -3.785583004e-02f, -1.663360931e-02f, 4.507461563e-03f, + 2.513137460e-02f, 4.598148540e-02f, 6.664318591e-02f, 8.668168634e-02f, 1.068126038e-01f, + 1.266389191e-01f, 1.457396001e-01f, 1.647996008e-01f, 1.834498048e-01f, 2.012875378e-01f, + 2.189537436e-01f, 2.361138165e-01f, 2.523896992e-01f, 2.683645487e-01f, 2.837425768e-01f, + 2.981780171e-01f, 3.121884167e-01f, 3.255218565e-01f, 3.378712237e-01f, 3.496810198e-01f, + 3.607479632e-01f, 3.708087206e-01f, 3.802283108e-01f, 3.888555467e-01f, 3.964744806e-01f, + 4.033663869e-01f, 4.094374478e-01f, 4.145178497e-01f, 4.187850952e-01f, 4.221933484e-01f, + 4.246332347e-01f, 4.262031019e-01f, 4.269191623e-01f, 4.267208278e-01f, 4.256020784e-01f, + 4.236304164e-01f, 4.208040535e-01f, 4.170266688e-01f, 4.124189913e-01f, 4.070356190e-01f, + 4.006923437e-01f, 3.935611844e-01f, 3.857514262e-01f, 3.769954741e-01f, 3.675129414e-01f, + 3.574644029e-01f, 3.465042710e-01f, 3.348944783e-01f, 3.228420615e-01f, 3.099310994e-01f, + 2.964625061e-01f, 2.826808393e-01f, 2.680924833e-01f, 2.530194223e-01f, 2.377517819e-01f, + 2.217601091e-01f, 2.053978741e-01f, 1.889784932e-01f, 1.719158441e-01f, 1.545746475e-01f, + 1.372980922e-01f, 1.194737032e-01f, 1.014763042e-01f, 8.366852999e-02f, 6.542135775e-02f, + 4.711339623e-02f, 2.911932580e-02f, 1.080473233e-02f, -7.455416024e-03f, -2.528233081e-02f, + -4.330513626e-02f, -6.116025522e-02f, -7.847293466e-02f, -9.585566819e-02f, -1.129616946e-01f, + -1.294303089e-01f, -1.458572298e-01f, -1.619268656e-01f, -1.772898436e-01f, -1.924927980e-01f, + -2.072446048e-01f, -2.212250382e-01f, -2.349441499e-01f, -2.481490672e-01f, -2.605455220e-01f, + -2.725868523e-01f, -2.840580940e-01f, -2.946982086e-01f, -3.048995137e-01f, -3.144871891e-01f, + -3.232374191e-01f, -3.314772546e-01f, -3.390740454e-01f, -3.458441496e-01f, -3.520465493e-01f, + -3.575918376e-01f, -3.623384833e-01f, -3.664759398e-01f, -3.699575067e-01f, -3.726851642e-01f, + -3.747802377e-01f, -3.762382567e-01f, -3.770038486e-01f, -3.771299720e-01f, -3.766512573e-01f, + -3.755547702e-01f, -3.738301992e-01f, -3.715482354e-01f, -3.687364161e-01f, -3.653264940e-01f, + -3.614213467e-01f, -3.570859134e-01f, -3.522010148e-01f, -3.468969166e-01f, -3.412716389e-01f, + -3.351627588e-01f, -3.287222981e-01f, -3.220760524e-01f, -3.150268793e-01f, -3.077418804e-01f, + -3.003685176e-01f, -2.926847041e-01f, -2.848675847e-01f, -2.770777047e-01f, -2.690688968e-01f, + -2.610149682e-01f, -2.530909777e-01f, -2.450560331e-01f, -2.370840907e-01f, -2.293484807e-01f, + -2.216053307e-01f, -2.140145004e-01f, -2.067486495e-01f, -1.995822042e-01f, -1.926583797e-01f, + -1.861406565e-01f, -1.798302084e-01f, -1.738485247e-01f, -1.683436632e-01f, -1.631521136e-01f, + -1.583685130e-01f, -1.541194171e-01f, -1.502841115e-01f, -1.469256580e-01f, -1.441432089e-01f, + -1.418665648e-01f, -1.401265711e-01f, -1.389860064e-01f, -1.384145021e-01f, -1.383971721e-01f, + -1.389695257e-01f, -1.401743144e-01f, -1.419658959e-01f, -1.443341970e-01f, -1.473667771e-01f, + -1.509748846e-01f, -1.551129222e-01f, -1.599287540e-01f, -1.652964652e-01f, -1.711322963e-01f, + -1.776408404e-01f, -1.846622825e-01f, -1.920762360e-01f, -2.001403570e-01f, -2.086646557e-01f, + -2.174944580e-01f, -2.269350290e-01f, -2.367706746e-01f, -2.468150407e-01f, -2.574154735e-01f, + -2.683394849e-01f, -2.793678641e-01f, -2.908630371e-01f, -3.025694191e-01f, -3.142538667e-01f, + -3.263154626e-01f, -3.384992778e-01f, -3.505475223e-01f, -3.628531396e-01f, -3.751562536e-01f, + -3.871932626e-01f, -3.993550241e-01f, -4.113890231e-01f, -4.230312705e-01f, -4.346578121e-01f, + -4.460325241e-01f, -4.568996131e-01f, -4.676087201e-01f, -4.779486358e-01f, -4.876795113e-01f, + -4.971130788e-01f, -5.060702562e-01f, -5.143342018e-01f, -5.221682787e-01f, -5.294357538e-01f, + -5.359441638e-01f, -5.418802500e-01f, -5.471427441e-01f, -5.515838861e-01f, -5.553330779e-01f, + -5.583449006e-01f, -5.605075955e-01f, -5.618600249e-01f, -5.624056458e-01f, -5.620841384e-01f, + -5.608502626e-01f, -5.587630272e-01f, -5.558157563e-01f, -5.518754125e-01f, -5.470587611e-01f, + -5.414159298e-01f, -5.347246528e-01f, -5.271597505e-01f, -5.188293457e-01f, -5.094218254e-01f, + -4.991683364e-01f, -4.882340729e-01f, -4.762206376e-01f, -4.634141922e-01f, -4.500318170e-01f, + -4.355804324e-01f, -4.203886390e-01f, -4.047315121e-01f, -3.880470991e-01f, -3.707092702e-01f, + -3.530400693e-01f, -3.344063759e-01f, -3.152171075e-01f, -2.958393693e-01f, -2.755843997e-01f, + -2.548896968e-01f, -2.341599315e-01f, -2.126620561e-01f, -1.908538938e-01f, -1.691696942e-01f, + -1.468446404e-01f, -1.243476421e-01f, -1.021336541e-01f, -7.941924036e-02f, -5.667469651e-02f, + -3.436651826e-02f, -1.170629077e-02f, 1.084438711e-02f, 3.281612322e-02f, 5.498610064e-02f, + 7.690929621e-02f, 9.812362492e-02f, 1.193847060e-01f, 1.402759552e-01f, 1.603469551e-01f, + 1.803070158e-01f, 1.997703314e-01f, 2.183119804e-01f, 2.365896851e-01f, 2.542575896e-01f, + 2.709238827e-01f, 2.871844172e-01f, 3.027401567e-01f, 3.172401488e-01f, 3.312100172e-01f, + 3.444028795e-01f, 3.565147519e-01f, 3.679937124e-01f, 3.786486387e-01f, 3.882261813e-01f, + 3.970925212e-01f, 4.051166475e-01f, 4.120943844e-01f, 4.182907045e-01f, 4.236236811e-01f, + 4.279522896e-01f, 4.314670265e-01f, 4.341490567e-01f, 4.359055758e-01f, 4.368250966e-01f, + 4.369366169e-01f, 4.362063706e-01f, 4.346405566e-01f, 4.323171675e-01f, 4.292566478e-01f, + 4.253879488e-01f, 4.208341241e-01f, 4.156663120e-01f, 4.097438455e-01f, 4.032290578e-01f, + 3.962383568e-01f, 3.885697722e-01f, 3.804176152e-01f, 3.719362617e-01f, 3.628732860e-01f, + 3.534504473e-01f, 3.438474536e-01f, 3.337527514e-01f, 3.233934939e-01f, 3.129830062e-01f, + 3.021984696e-01f, 2.912837565e-01f, 2.804594934e-01f, 2.693688869e-01f, 2.582505345e-01f, + 2.473378330e-01f, 2.362704724e-01f, 2.252801806e-01f, 2.146020979e-01f, 2.038814425e-01f, + 1.933373362e-01f, 1.831993312e-01f, 1.731278449e-01f, 1.633234471e-01f, 1.540031284e-01f, + 1.448507458e-01f, 1.360429674e-01f, 1.277775764e-01f, 1.197701469e-01f, 1.121721193e-01f, + 1.051530167e-01f, 9.845122695e-02f, 9.217943251e-02f, 8.648770303e-02f, 8.117154986e-02f, + 7.632037997e-02f, 7.204575092e-02f, 6.817077845e-02f, 6.474824995e-02f, 6.186249852e-02f, + 5.938240513e-02f, 5.733105168e-02f, 5.576239899e-02f, 5.458760262e-02f, 5.380340293e-02f, + 5.343497172e-02f, 5.343222246e-02f, 5.376922712e-02f, 5.444474518e-02f, 5.544237420e-02f, + 5.671774969e-02f, 5.824618414e-02f, 6.003959849e-02f, 6.204214692e-02f, 6.420692056e-02f, + 6.655591726e-02f, 6.901939213e-02f, 7.154221833e-02f, 7.417018712e-02f, 7.683774829e-02f, + 7.947471738e-02f, 8.211812377e-02f, 8.470211923e-02f, 8.715723455e-02f, 8.951694518e-02f, + 9.172526002e-02f, 9.371732175e-02f, 9.551388025e-02f, 9.707523137e-02f, 9.834731370e-02f, + 9.933057427e-02f, 1.000067890e-01f, 1.003380716e-01f, 1.002972722e-01f, 9.989172965e-02f, + 9.910391271e-02f, 9.787328541e-02f, 9.623711556e-02f, 9.419956803e-02f, 9.165343642e-02f, + 8.866250515e-02f, 8.526153117e-02f, 8.130507916e-02f, 7.688987255e-02f, 7.207883894e-02f, + 6.667938083e-02f, 6.081863865e-02f, 5.459125713e-02f, 4.776029661e-02f, 4.048288614e-02f, + 3.288518637e-02f, 2.468763851e-02f, 1.607546769e-02f, 7.205249742e-03f, -2.242041985e-03f, + -1.205615886e-02f, -2.205248363e-02f, -3.258526698e-02f, -4.342341051e-02f, -5.435771123e-02f, + -6.577222049e-02f, -7.741986960e-02f, -8.907147497e-02f, -1.011349633e-01f, -1.133524105e-01f, + -1.254798621e-01f, -1.379460394e-01f, -1.504923701e-01f, -1.628590226e-01f, -1.754715890e-01f, + -1.880686879e-01f, -2.003877312e-01f, -2.128541023e-01f, -2.252132595e-01f, -2.372039557e-01f, + -2.492421418e-01f, -2.610865533e-01f, -2.724835277e-01f, -2.838316560e-01f, -2.949084938e-01f, + -3.054734766e-01f, -3.159001172e-01f, -3.259894252e-01f, -3.355185688e-01f, -3.448293507e-01f, + -3.537513018e-01f, -3.620815575e-01f, -3.701156378e-01f, -3.777078092e-01f, -3.846860826e-01f, + -3.913144767e-01f, -3.974859118e-01f, -4.030488133e-01f, -4.082042277e-01f, -4.128716886e-01f, + -4.169344604e-01f, -4.205466211e-01f, -4.236601889e-01f, -4.261917174e-01f, -4.282467663e-01f, + -4.298103750e-01f, -4.308328927e-01f, -4.313723743e-01f, -4.314456284e-01f, -4.310357273e-01f, + -4.301549792e-01f, -4.288491309e-01f, -4.271316230e-01f, -4.249722362e-01f, -4.224437475e-01f, + -4.195843041e-01f, -4.163150489e-01f, -4.127240181e-01f, -4.088790417e-01f, -4.046788216e-01f, + -4.002336264e-01f, -3.956251740e-01f, -3.907117844e-01f, -3.856109381e-01f, -3.804236054e-01f, + -3.749885261e-01f, -3.694300950e-01f, -3.638616800e-01f, -3.581076562e-01f, -3.522956967e-01f, + -3.465470374e-01f, -3.406782150e-01f, -3.348158598e-01f, -3.290844858e-01f, -3.232986331e-01f, + -3.175798059e-01f, -3.120512366e-01f, -3.065312505e-01f, -3.011333048e-01f, -2.959742248e-01f, + -2.908767760e-01f, -2.859396040e-01f, -2.812739611e-01f, -2.767229378e-01f, -2.723749876e-01f, + -2.683254480e-01f, -2.644265294e-01f, -2.607480586e-01f, -2.573735416e-01f, -2.541758716e-01f, + -2.512085438e-01f, -2.485398650e-01f, -2.460635453e-01f, -2.438171506e-01f, -2.418531030e-01f, + -2.400859743e-01f, -2.385381162e-01f, -2.372461557e-01f, -2.361446768e-01f, -2.352421731e-01f, + -2.345604002e-01f, -2.340521514e-01f, -2.337138504e-01f, -2.335543185e-01f, -2.335432619e-01f, + -2.336673141e-01f, -2.339246571e-01f, -2.343017459e-01f, -2.347811162e-01f, -2.353487164e-01f, + -2.359947413e-01f, -2.366955429e-01f, -2.374322116e-01f, -2.382020950e-01f, -2.389809489e-01f, + -2.397464514e-01f, -2.404981554e-01f, -2.412145585e-01f, -2.418729961e-01f, -2.424709648e-01f, + -2.429925650e-01f, -2.434176803e-01f, -2.437376827e-01f, -2.439446896e-01f, -2.440240085e-01f, + -2.439568341e-01f, -2.437448204e-01f, -2.433820665e-01f, -2.428390384e-01f, -2.421297431e-01f, + -2.412577868e-01f, -2.401789725e-01f, -2.389204800e-01f, -2.374966145e-01f, -2.358447611e-01f, + -2.340040803e-01f, -2.320023775e-01f, -2.297611982e-01f, -2.273326963e-01f, -2.247580290e-01f, + -2.219430059e-01f, -2.189522088e-01f, -2.158399671e-01f, -2.124972492e-01f, -2.089997828e-01f, + -2.054140568e-01f, -2.016178668e-01f, -1.976959854e-01f, -1.937255859e-01f, -1.895735115e-01f, + -1.853308678e-01f, -1.810837090e-01f, -1.766918749e-01f, -1.722507477e-01f, -1.678520739e-01f, + -1.633493453e-01f, -1.588368565e-01f, -1.544123739e-01f, -1.499342471e-01f, -1.454957724e-01f, + -1.411949247e-01f, -1.368952543e-01f, -1.326845288e-01f, -1.286585480e-01f, -1.246912703e-01f, + -1.208615080e-01f, -1.172595471e-01f, -1.137744710e-01f, -1.104730219e-01f, -1.074366793e-01f, + -1.045741290e-01f, -1.019371822e-01f, -9.959524870e-02f, -9.748128057e-02f, -9.563081712e-02f, + -9.409663081e-02f, -9.283447266e-02f, -9.185922891e-02f, -9.120742232e-02f, -9.086360037e-02f, + -9.082075208e-02f, -9.109890461e-02f, -9.171955287e-02f, -9.265705198e-02f, -9.390983731e-02f, + -9.553086758e-02f, -9.747329354e-02f, -9.971456230e-02f, -1.023403034e-01f, -1.052825376e-01f, + -1.084975377e-01f, -1.121035144e-01f, -1.160123423e-01f, -1.201595217e-01f, -1.246949583e-01f, + -1.295119375e-01f, -1.345255971e-01f, -1.399170756e-01f, -1.455661505e-01f, -1.513644606e-01f, + -1.575073302e-01f, -1.638551801e-01f, -1.702875197e-01f, -1.770273596e-01f, -1.839264929e-01f, + -1.908492148e-01f, -1.980347335e-01f, -2.053285092e-01f, -2.125834972e-01f, -2.200489044e-01f, + -2.275666445e-01f, -2.349824458e-01f, -2.425507158e-01f, -2.501136959e-01f, -2.575137019e-01f, + -2.650053203e-01f, -2.724350691e-01f, -2.796457112e-01f, -2.868864536e-01f, -2.940119803e-01f, + -3.008682728e-01f, -3.076952100e-01f, -3.143632412e-01f, -3.207187653e-01f, -3.269668519e-01f, + -3.329843879e-01f, -3.386356235e-01f, -3.441132605e-01f, -3.493170738e-01f, -3.541212380e-01f, + -3.586825430e-01f, -3.629206717e-01f, -3.667282760e-01f, -3.702270985e-01f, -3.733603656e-01f, + -3.760418296e-01f, -3.783543408e-01f, -3.802672029e-01f, -3.817181885e-01f, -3.827481866e-01f, + -3.833545148e-01f, -3.835009634e-01f, -3.831837177e-01f, -3.824291825e-01f, -3.812277615e-01f, + -3.795301318e-01f, -3.773950934e-01f, -3.748358190e-01f, -3.717401922e-01f, -3.681890070e-01f, + -3.642302454e-01f, -3.597125709e-01f, -3.547496498e-01f, -3.494146764e-01f, -3.435011804e-01f, + -3.371493220e-01f, -3.304625452e-01f, -3.231865168e-01f, -3.154883087e-01f, -3.074999154e-01f, + -2.989208996e-01f, -2.899438739e-01f, -2.807282209e-01f, -2.709298134e-01f, -2.607648969e-01f, + -2.504184246e-01f, -2.395054102e-01f, -2.282630950e-01f, -2.168997675e-01f, -2.049931139e-01f, + -1.927993000e-01f, -1.805464476e-01f, -1.677749604e-01f, -1.547539830e-01f, -1.417325735e-01f, + -1.282257736e-01f, -1.145158857e-01f, -1.008679569e-01f, -8.677391708e-02f, -7.252530009e-02f, + -5.840107426e-02f, -4.387682304e-02f, -2.925042436e-02f, -1.481105294e-02f, -2.294098522e-05f, + 1.481318288e-02f, 2.940157615e-02f, 4.428430647e-02f, 5.916148797e-02f, 7.373438030e-02f, + 8.854618669e-02f, 1.033014432e-01f, 1.177015156e-01f, 1.322859675e-01f, 1.467684954e-01f, + 1.608515531e-01f, 1.750583053e-01f, 1.891099662e-01f, 2.027179748e-01f, 2.163950205e-01f, + 2.298790962e-01f, 2.428866625e-01f, 2.559008002e-01f, 2.686730921e-01f, 2.809332013e-01f, + 2.931365371e-01f, 3.050526977e-01f, 3.164267838e-01f, 3.276819885e-01f, 3.386090696e-01f, + 3.489716649e-01f, 3.591572940e-01f, 3.689803779e-01f, 3.782256544e-01f, 3.872416317e-01f, + 3.958682716e-01f, 4.039131403e-01f, 4.116832614e-01f, 4.190477729e-01f, 4.258355498e-01f, + 4.322982728e-01f, 4.383270144e-01f, 4.437823296e-01f, 4.488806725e-01f, 4.535482824e-01f, + 4.576651156e-01f, 4.613839388e-01f, 4.646546245e-01f, 4.673882127e-01f, 4.696886539e-01f, + 4.715342522e-01f, 4.728651643e-01f, 4.737351835e-01f, 4.741517305e-01f, 4.740844369e-01f, + 4.735374749e-01f, 4.725469649e-01f, 4.711116254e-01f, 4.691866338e-01f, 4.668356776e-01f, + 4.640855193e-01f, 4.608439505e-01f, 4.572016597e-01f, 4.532108605e-01f, 4.487267435e-01f, + 4.438587725e-01f, 4.386906922e-01f, 4.330485165e-01f, 4.270712137e-01f, 4.208582640e-01f, + 4.141803086e-01f, 4.071895480e-01f, 4.000122547e-01f, 3.923876584e-01f, 3.844850361e-01f, + 3.764514625e-01f, 3.679969907e-01f, 3.593061268e-01f, 3.505442739e-01f, 3.413961232e-01f, + 3.320585489e-01f, 3.227120936e-01f, 3.130200207e-01f, 3.031881154e-01f, 2.934088111e-01f, + 2.833281755e-01f, 2.731572092e-01f, 2.630974948e-01f, 2.527816594e-01f, 2.424203157e-01f, + 2.322240770e-01f, 2.218287736e-01f, 2.114490271e-01f, 2.012924552e-01f, 1.909831911e-01f, + 1.807267219e-01f, 1.707343012e-01f, 1.606393754e-01f, 1.506406814e-01f, 1.409468651e-01f, + 1.312039495e-01f, 1.216018125e-01f, 1.123433486e-01f, 1.030914113e-01f, 9.402418137e-02f, + 8.533558249e-02f, 7.670909166e-02f, 6.830830872e-02f, 6.031526253e-02f, 5.243755877e-02f, + 4.482140765e-02f, 3.763472289e-02f, 3.061348759e-02f, 2.388412878e-02f, 1.759868115e-02f, + 1.152757742e-02f, 5.777840968e-03f, 4.811582330e-04f, -4.560418893e-03f, -9.263549000e-03f, + -1.351558045e-02f, -1.747548580e-02f, -2.108277008e-02f, -2.424615249e-02f, -2.708445489e-02f, + -2.956039086e-02f, -3.160500526e-02f, -3.329583257e-02f, -3.461905196e-02f, -3.552871570e-02f, + -3.606050089e-02f, -3.622401878e-02f, -3.599681333e-02f, -3.537247330e-02f, -3.438337892e-02f, + -3.303133324e-02f, -3.126983717e-02f, -2.915374748e-02f, -2.670873515e-02f, -2.385057136e-02f, + -2.065691911e-02f, -1.717482135e-02f, -1.327907667e-02f, -9.065892547e-03f, -4.605622962e-03f, + 2.604530309e-04f, 5.417570006e-03f, 1.077445317e-02f, 1.652089693e-02f, 2.252542228e-02f, + 2.867775783e-02f, 3.519567847e-02f, 4.193351045e-02f, 4.876431450e-02f, 5.593045801e-02f, + 6.327498704e-02f, 7.065674663e-02f, 7.833895832e-02f, 8.615688980e-02f, 9.395691752e-02f, + 1.020172536e-01f, 1.101685911e-01f, 1.182469428e-01f, 1.265365332e-01f, 1.348635107e-01f, + 1.430601776e-01f, 1.514203846e-01f, 1.597737819e-01f, 1.679470837e-01f, 1.762298346e-01f, + 1.844555289e-01f, 1.924502105e-01f, 2.004943043e-01f, 2.084281445e-01f, 2.160806358e-01f, + 2.237177491e-01f, 2.311901748e-01f, 2.383333594e-01f, 2.453937531e-01f, 2.522360086e-01f, + 2.587058842e-01f, 2.650250494e-01f, 2.710752487e-01f, 2.767164707e-01f, 2.821426392e-01f, + 2.872574329e-01f, 2.919360995e-01f, 2.963342965e-01f, 3.003768623e-01f, 3.039609194e-01f, + 3.072063327e-01f, 3.100646436e-01f, 3.124544024e-01f, 3.144518733e-01f, 3.160353899e-01f, + 3.171483278e-01f, 3.178209662e-01f, 3.180597425e-01f, 3.178345859e-01f, 3.171279132e-01f, + 3.159749210e-01f, 3.143735826e-01f, 3.122571707e-01f, 3.096900284e-01f, 3.066991568e-01f, + 3.031680286e-01f, 2.991889417e-01f, 2.948195934e-01f, 2.898983359e-01f, 2.845464945e-01f, + 2.788503468e-01f, 2.726007700e-01f, 2.659448683e-01f, 2.589981258e-01f, 2.515093982e-01f, + 2.436494380e-01f, 2.355616242e-01f, 2.269577086e-01f, 2.180297971e-01f, 2.089464962e-01f, + 1.993876249e-01f, 1.895629317e-01f, 1.796630174e-01f, 1.693415046e-01f, 1.588214189e-01f, + 1.483113319e-01f, 1.374451816e-01f, 1.264541745e-01f, 1.155605614e-01f, 1.043856144e-01f, + 9.316266328e-02f, 8.212402463e-02f, 7.088927925e-02f, 5.968987942e-02f, 4.876190424e-02f, + 3.773013502e-02f, 2.681781910e-02f, 1.626021601e-02f, 5.697289482e-03f, -4.661408719e-03f, + -1.458681468e-02f, -2.441371232e-02f, -3.395112231e-02f, -4.298242927e-02f, -5.180907622e-02f, + -6.026405469e-02f, -6.814906746e-02f, -7.572446018e-02f, -8.285238594e-02f, -8.935838193e-02f, + -9.545440227e-02f, -1.010363698e-01f, -1.059585512e-01f, -1.103780046e-01f, -1.142286137e-01f, + -1.173971221e-01f, -1.199775338e-01f, -1.219428629e-01f, -1.232180074e-01f, -1.238336414e-01f, + -1.238060817e-01f, -1.230992749e-01f, -1.216735244e-01f, -1.195874661e-01f, -1.168482602e-01f, + -1.133476049e-01f, -1.091872454e-01f, -1.044183671e-01f, -9.886487573e-02f, -9.267068654e-02f, + -8.593096584e-02f, -7.840406150e-02f, -7.027382404e-02f, -6.167794764e-02f, -5.231276900e-02f, + -4.239844158e-02f, -3.211257979e-02f, -2.109455690e-02f, -9.596202523e-03f, 2.168561565e-03f, + 1.461181603e-02f, 2.745697089e-02f, 4.045626521e-02f, 5.406012014e-02f, 6.797107309e-02f, + 8.191473782e-02f, 9.637356550e-02f, 1.110377088e-01f, 1.256104112e-01f, 1.405920535e-01f, + 1.556665599e-01f, 1.705225855e-01f, 1.856675595e-01f, 2.007868737e-01f, 2.155623585e-01f, + 2.304972410e-01f, 2.452864498e-01f, 2.596130669e-01f, 2.739646733e-01f, 2.880538404e-01f, + 3.015726209e-01f, 3.149818778e-01f, 3.280200660e-01f, 3.403947651e-01f, 3.525249660e-01f, + 3.641797006e-01f, 3.750914335e-01f, 3.856313527e-01f, 3.956072927e-01f, 4.047811031e-01f, + 4.134621918e-01f, 4.215002358e-01f, 4.286944270e-01f, 4.352869093e-01f, 4.411736429e-01f, + 4.461966157e-01f, 4.505255520e-01f, 4.541058242e-01f, 4.568265975e-01f, 4.587816298e-01f, + 4.599675834e-01f, 4.603232741e-01f, 4.598645866e-01f, 4.586394131e-01f, 4.566371143e-01f, + 4.537959099e-01f, 4.502147138e-01f, 4.459310174e-01f, 4.407989383e-01f, 4.349598289e-01f, + 4.285042882e-01f, 4.212203622e-01f, 4.132944643e-01f, 4.048615396e-01f, 3.956391513e-01f, + 3.858492672e-01f, 3.756713867e-01f, 3.647664785e-01f, 3.533874750e-01f, 3.417523205e-01f, + 3.294746876e-01f, 3.168316185e-01f, 3.040730357e-01f, 2.907759249e-01f, 2.772335410e-01f, + 2.637202144e-01f, 2.497874647e-01f, 2.357361615e-01f, 2.218566388e-01f, 2.076873630e-01f, + 1.935288757e-01f, 1.796778888e-01f, 1.656671166e-01f, 1.517844647e-01f, 1.383293122e-01f, + 1.248516515e-01f, 1.116239727e-01f, 9.893567860e-02f, 8.635891974e-02f, 7.414076477e-02f, + 6.255520135e-02f, 5.121134594e-02f, 4.032555595e-02f, 3.014784306e-02f, 2.033305541e-02f, + 1.106117386e-02f, 2.551381011e-03f, -5.488061346e-03f, -1.291821990e-02f, -1.955641434e-02f, + -2.563472278e-02f, -3.105850704e-02f, -3.568585590e-02f, -3.968437016e-02f, -4.300522059e-02f, + -4.555014893e-02f, -4.741973802e-02f, -4.861126840e-02f, -4.907110706e-02f, -4.883038998e-02f, + -4.792973027e-02f, -4.636165127e-02f, -4.409238324e-02f, -4.120426252e-02f, -3.773366660e-02f, + -3.358434886e-02f, -2.887580544e-02f, -2.368598618e-02f, -1.786242984e-02f, -1.155664213e-02f, + -4.884040914e-03f, 2.356582321e-03f, 9.987854399e-03f, 1.786217839e-02f, 2.622083761e-02f, + 3.486757353e-02f, 4.362870380e-02f, 5.277618021e-02f, 6.210310012e-02f, 7.141549140e-02f, + 8.100195229e-02f, 9.065073729e-02f, 1.001569852e-01f, 1.098190099e-01f, 1.194302887e-01f, + 1.287804246e-01f, 1.381616443e-01f, 1.473783702e-01f, 1.562241018e-01f, 1.649759263e-01f, + 1.734566391e-01f, 1.814713627e-01f, 1.892725229e-01f, 1.967078894e-01f, 2.036015391e-01f, + 2.101726830e-01f, 2.162997723e-01f, 2.218319625e-01f, 2.269476950e-01f, 2.315602899e-01f, + 2.355485111e-01f, 2.390444875e-01f, 2.420012057e-01f, 2.443275005e-01f, 2.460937500e-01f, + 2.472874671e-01f, 2.478573620e-01f, 2.478222400e-01f, 2.472096831e-01f, 2.460060269e-01f, + 2.441733181e-01f, 2.417773008e-01f, 2.388431579e-01f, 2.352772504e-01f, 2.311808169e-01f, + 2.266173512e-01f, 2.214416265e-01f, 2.157869935e-01f, 2.097530216e-01f, 2.031481415e-01f, + 1.961334646e-01f, 1.888408065e-01f, 1.810386479e-01f, 1.729102135e-01f, 1.646142751e-01f, + 1.558876485e-01f, 1.469314396e-01f, 1.379222423e-01f, 1.285629719e-01f, 1.190595105e-01f, + 1.096073315e-01f, 9.989650548e-02f, 9.013513476e-02f, 8.052946627e-02f, 7.076757401e-02f, + 6.105571613e-02f, 5.160190538e-02f, 4.209663346e-02f, 3.273608536e-02f, 2.372433618e-02f, + 1.476350799e-02f, 6.033593323e-03f, -2.271719044e-03f, -1.043064054e-02f, -1.828454249e-02f, + -2.565585822e-02f, -3.279591724e-02f, -3.957159445e-02f, -4.582640901e-02f, -5.177824199e-02f, + -5.732138082e-02f, -6.232574955e-02f, -6.697539985e-02f, -7.119590789e-02f, -7.488372922e-02f, + -7.818244398e-02f, -8.104846627e-02f, -8.340591192e-02f, -8.535333723e-02f, -8.687447011e-02f, + -8.792428672e-02f, -8.856201917e-02f, -8.879855275e-02f, -8.861833066e-02f, -8.804366738e-02f, + -8.711011708e-02f, -8.582974225e-02f, -8.419206738e-02f, -8.225351572e-02f, -8.005069941e-02f, + -7.754547149e-02f, -7.481031120e-02f, -7.190223038e-02f, -6.876154244e-02f, -6.547103822e-02f, + -6.210342050e-02f, -5.858496577e-02f, -5.500291660e-02f, -5.144010484e-02f, -4.781672359e-02f, + -4.421833158e-02f, -4.073204100e-02f, -3.728020191e-02f, -3.394059837e-02f, -3.079873696e-02f, + -2.778774314e-02f, -2.497212403e-02f, -2.242966741e-02f, -2.011188678e-02f, -1.806484722e-02f, + -1.635293849e-02f, -1.495306846e-02f, -1.388837770e-02f, -1.320479717e-02f, -1.291078422e-02f, + -1.300300471e-02f, -1.350458991e-02f, -1.446091663e-02f, -1.584029384e-02f, -1.763891242e-02f, + -1.994018443e-02f, -2.268104069e-02f, -2.583031729e-02f, -2.951630019e-02f, -3.364717215e-02f, + -3.816025704e-02f, -4.322361201e-02f, -4.871550575e-02f, -5.454344302e-02f, -6.091625616e-02f, + -6.768511981e-02f, -7.472833246e-02f, -8.229219168e-02f, -9.020408243e-02f, -9.831541032e-02f, + -1.069056019e-01f, -1.157824919e-01f, -1.247738674e-01f, -1.341867000e-01f, -1.438142508e-01f, + -1.534644663e-01f, -1.634660810e-01f, -1.736055613e-01f, -1.836726964e-01f, -1.939990371e-01f, + -2.043626457e-01f, -2.145491838e-01f, -2.249045223e-01f, -2.352157980e-01f, -2.452591509e-01f, + -2.553635240e-01f, -2.653219700e-01f, -2.749161124e-01f, -2.844610810e-01f, -2.937653363e-01f, + -3.026208282e-01f, -3.113199174e-01f, -3.196930289e-01f, -3.275488019e-01f, -3.351494670e-01f, + -3.423527777e-01f, -3.489893973e-01f, -3.552843928e-01f, -3.611267507e-01f, -3.663729727e-01f, + -3.712059855e-01f, -3.755523860e-01f, -3.792926669e-01f, -3.825439215e-01f, -3.852592111e-01f, + -3.873601556e-01f, -3.889266849e-01f, -3.899560869e-01f, -3.903968632e-01f, -3.902598023e-01f, + -3.895739317e-01f, -3.883272707e-01f, -3.864769042e-01f, -3.840871453e-01f, -3.811832070e-01f, + -3.776692450e-01f, -3.736437559e-01f, -3.691684604e-01f, -3.640974760e-01f, -3.585608900e-01f, + -3.526544273e-01f, -3.461860120e-01f, -3.393130302e-01f, -3.321613967e-01f, -3.244983554e-01f, + -3.165062070e-01f, -3.083328605e-01f, -2.996973693e-01f, -2.907925546e-01f, -2.817939520e-01f, + -2.724016011e-01f, -2.628246844e-01f, -2.532511652e-01f, -2.433517724e-01f, -2.333398014e-01f, + -2.234168202e-01f, -2.132397741e-01f, -2.030234784e-01f, -1.929762512e-01f, -1.827475131e-01f, + -1.725491136e-01f, -1.625910550e-01f, -1.525216103e-01f, -1.425453871e-01f, -1.328692883e-01f, + -1.231461987e-01f, -1.135697365e-01f, -1.043395475e-01f, -9.511820972e-02f, -8.608520031e-02f, + -7.742980868e-02f, -6.882934272e-02f, -6.044734269e-02f, -5.245999247e-02f, -4.456305504e-02f, + -3.690315783e-02f, -2.964133583e-02f, -2.249394543e-02f, -1.559036598e-02f, -9.075299837e-03f, + -2.686983673e-03f, 3.461787710e-03f, 9.243417531e-03f, 1.489862800e-02f, 2.033019811e-02f, + 2.542730048e-02f, 3.041129000e-02f, 3.519883752e-02f, 3.969456628e-02f, 4.410275817e-02f, + 4.835152999e-02f, 5.235848948e-02f, 5.631439760e-02f, 6.015752256e-02f, 6.381306797e-02f, + 6.745278090e-02f, 7.101625204e-02f, 7.443930954e-02f, 7.789093256e-02f, 8.131502569e-02f, + 8.464920521e-02f, 8.805377036e-02f, 9.147047251e-02f, 9.483855963e-02f, 9.831639379e-02f, + 1.018416956e-01f, 1.053520069e-01f, 1.090067104e-01f, 1.127375439e-01f, 1.164779514e-01f, + 1.203910187e-01f, 1.244010627e-01f, 1.284354329e-01f, 1.326627433e-01f, 1.369991153e-01f, + 1.413639337e-01f, 1.459330767e-01f, 1.506160200e-01f, 1.553209126e-01f, 1.602218598e-01f, + 1.652162522e-01f, 1.702077240e-01f, 1.753837168e-01f, 1.806400716e-01f, 1.858672649e-01f, + 1.912456453e-01f, 1.966641247e-01f, 2.020084262e-01f, 2.074578702e-01f, 2.129001170e-01f, + 2.182169706e-01f, 2.235816419e-01f, 2.288846672e-01f, 2.340071052e-01f, 2.391112298e-01f, + 2.440945357e-01f, 2.488410622e-01f, 2.534963787e-01f, 2.579692602e-01f, 2.621503174e-01f, + 2.661631405e-01f, 2.699338198e-01f, 2.733613253e-01f, 2.765318751e-01f, 2.793858051e-01f, + 2.818432748e-01f, 2.839686275e-01f, 2.857331336e-01f, 2.870690525e-01f, 2.879859507e-01f, + 2.884770930e-01f, 2.885029018e-01f, 2.880309522e-01f, 2.870850563e-01f, 2.856545448e-01f, + 2.836602628e-01f, 2.811598182e-01f, 2.781746686e-01f, 2.745763659e-01f, 2.704577446e-01f, + 2.658741176e-01f, 2.606459260e-01f, 2.549011409e-01f, 2.487292141e-01f, 2.418997139e-01f, + 2.345760018e-01f, 2.268789411e-01f, 2.185180634e-01f, 2.096818835e-01f, 2.005308419e-01f, + 1.907401234e-01f, 1.805325300e-01f, 1.700938940e-01f, 1.590460539e-01f, 1.476315707e-01f, + 1.360688210e-01f, 1.239478365e-01f, 1.115307808e-01f, 9.906195849e-02f, 8.610600978e-02f, + 7.293985784e-02f, 5.982871354e-02f, 4.632015526e-02f, 3.269952536e-02f, 1.924671046e-02f, + 5.500954576e-03f, -8.251207881e-03f, -2.172230370e-02f, -3.537159041e-02f, -4.891769961e-02f, + -6.207443401e-02f, -7.529600710e-02f, -8.831780404e-02f, -1.008568704e-01f, -1.133416891e-01f, + -1.255264431e-01f, -1.371419430e-01f, -1.485860944e-01f, -1.596394032e-01f, -1.700517833e-01f, + -1.801800728e-01f, -1.898360252e-01f, -1.987954080e-01f, -2.073666602e-01f, -2.153974622e-01f, + -2.226950079e-01f, -2.295130789e-01f, -2.357389331e-01f, -2.412157506e-01f, -2.461376935e-01f, + -2.504339516e-01f, -2.539871335e-01f, -2.569280565e-01f, -2.592284679e-01f, -2.608131468e-01f, + -2.617492676e-01f, -2.620506585e-01f, -2.616853714e-01f, -2.606596649e-01f, -2.590301037e-01f, + -2.568042874e-01f, -2.539210320e-01f, -2.504718602e-01f, -2.465070337e-01f, -2.419103384e-01f, + -2.368068099e-01f, -2.312851101e-01f, -2.251794785e-01f, -2.186438143e-01f, -2.118009925e-01f, + -2.044430077e-01f, -1.967465580e-01f, -1.888632923e-01f, -1.805512011e-01f, -1.720027775e-01f, + -1.633922160e-01f, -1.544525176e-01f, -1.453837156e-01f, -1.363768876e-01f, -1.271529943e-01f, + -1.179127470e-01f, -1.088563129e-01f, -9.970471263e-02f, -9.065353125e-02f, -8.190181106e-02f, + -7.317472249e-02f, -6.465151906e-02f, -5.652644858e-02f, -4.854607582e-02f, -4.086904973e-02f, + -3.367594630e-02f, -2.674324065e-02f, -2.020317502e-02f, -1.421577670e-02f, -8.595600724e-03f, + -3.443202702e-03f, 1.107888995e-03f, 5.197976250e-03f, 8.762272075e-03f, 1.169840246e-02f, + 1.409615763e-02f, 1.593055390e-02f, 1.713203639e-02f, 1.772918738e-02f, 1.773480512e-02f, + 1.711856015e-02f, 1.585052907e-02f, 1.398261450e-02f, 1.152569894e-02f, 8.394345641e-03f, + 4.681150895e-03f, 4.361112660e-04f, -4.484821577e-03f, -9.952437133e-03f, -1.587755792e-02f, + -2.245861478e-02f, -2.953340486e-02f, -3.697605431e-02f, -4.503424838e-02f, -5.351737887e-02f, + -6.226661056e-02f, -7.157255709e-02f, -8.122175932e-02f, -9.102731198e-02f, -1.013150215e-01f, + -1.118542776e-01f, -1.224365756e-01f, -1.334173083e-01f, -1.445566714e-01f, -1.556289345e-01f, + -1.670087278e-01f, -1.784550101e-01f, -1.897282302e-01f, -2.012038529e-01f, -2.126413137e-01f, + -2.237984389e-01f, -2.350489348e-01f, -2.461615503e-01f, -2.568972111e-01f, -2.676180899e-01f, + -2.781089544e-01f, -2.881405652e-01f, -2.980551720e-01f, -3.076592088e-01f, -3.167393208e-01f, + -3.256099820e-01f, -3.341039419e-01f, -3.420287371e-01f, -3.496642411e-01f, -3.568736911e-01f, + -3.634887040e-01f, -3.697448671e-01f, -3.755350411e-01f, -3.807215393e-01f, -3.854997456e-01f, + -3.897984326e-01f, -3.935073316e-01f, -3.967672288e-01f, -3.995374143e-01f, -4.017415345e-01f, + -4.034728408e-01f, -4.047225714e-01f, -4.054476321e-01f, -4.056944549e-01f, -4.054849446e-01f, + -4.048086405e-01f, -4.036677182e-01f, -4.021122158e-01f, -4.001622200e-01f, -3.977791965e-01f, + -3.950375319e-01f, -3.919846416e-01f, -3.885461390e-01f, -3.848168552e-01f, -3.808664083e-01f, + -3.765855730e-01f, -3.720813394e-01f, -3.674446344e-01f, -3.625462949e-01f, -3.575043976e-01f, + -3.524223268e-01f, -3.471510112e-01f, -3.418112993e-01f, -3.365167081e-01f, -3.311093152e-01f, + -3.257088065e-01f, -3.204326928e-01f, -3.151211143e-01f, -3.098877966e-01f, -3.048491478e-01f, + -2.998500466e-01f, -2.949937880e-01f, -2.903909087e-01f, -2.858971059e-01f, -2.816015184e-01f, + -2.776044011e-01f, -2.737779021e-01f, -2.701939940e-01f, -2.669390142e-01f, -2.639053166e-01f, + -2.611447871e-01f, -2.587279975e-01f, -2.565743923e-01f, -2.547167242e-01f, -2.532052696e-01f, + -2.519836128e-01f, -2.510626614e-01f, -2.504731715e-01f, -2.501870394e-01f, -2.501948774e-01f, + -2.505068183e-01f, -2.511219382e-01f, -2.520119846e-01f, -2.531668842e-01f, -2.546111941e-01f, + -2.562997937e-01f, -2.582038641e-01f, -2.603706121e-01f, -2.627408803e-01f, -2.652692199e-01f, + -2.680222094e-01f, -2.709303200e-01f, -2.739339769e-01f, -2.771127224e-01f, -2.803886533e-01f, + -2.836936116e-01f, -2.871218026e-01f, -2.905953228e-01f, -2.940362990e-01f, -2.975374758e-01f, + -3.010199666e-01f, -3.044049740e-01f, -3.077856898e-01f, -3.110885322e-01f, -3.142366111e-01f, + -3.173179924e-01f, -3.202683330e-01f, -3.230167329e-01f, -3.256412446e-01f, -3.280901909e-01f, + -3.303019106e-01f, -3.323400319e-01f, -3.341678977e-01f, -3.357356191e-01f, -3.370890915e-01f, + -3.382100761e-01f, -3.390602171e-01f, -3.396560550e-01f, -3.399927020e-01f, -3.400518894e-01f, + -3.398337364e-01f, -3.393547535e-01f, -3.386109173e-01f, -3.375722468e-01f, -3.362711668e-01f, + -3.347235024e-01f, -3.328763545e-01f, -3.307791650e-01f, -3.284663260e-01f, -3.258636594e-01f, + -3.230361938e-01f, -3.200360835e-01f, -3.167704046e-01f, -3.133177161e-01f, -3.097456992e-01f, + -3.059458137e-01f, -3.020068705e-01f, -2.980087996e-01f, -2.938322425e-01f, -2.895750105e-01f, + -2.853219509e-01f, -2.809339762e-01f, -2.765058875e-01f, -2.721329927e-01f, -2.676791251e-01f, + -2.632398903e-01f, -2.589117885e-01f, -2.545591593e-01f, -2.502731979e-01f, -2.461494207e-01f, + -2.420583367e-01f, -2.380830497e-01f, -2.343146950e-01f, -2.306348383e-01f, -2.271156013e-01f, + -2.238403410e-01f, -2.207062095e-01f, -2.177717388e-01f, -2.151094377e-01f, -2.126357257e-01f, + -2.103934586e-01f, -2.084410489e-01f, -2.067183554e-01f, -2.052538097e-01f, -2.040853500e-01f, + -2.031646669e-01f, -2.024971545e-01f, -2.021078020e-01f, -2.019775510e-01f, -2.020958960e-01f, + -2.024694979e-01f, -2.031028569e-01f, -2.039702982e-01f, -2.050605118e-01f, -2.063981146e-01f, + -2.079429477e-01f, -2.096673846e-01f, -2.116138637e-01f, -2.137301862e-01f, -2.159743011e-01f, + -2.184032947e-01f, -2.209560573e-01f, -2.235787660e-01f, -2.263390124e-01f, -2.291705161e-01f, + -2.320103347e-01f, -2.349327803e-01f, -2.378727794e-01f, -2.407582402e-01f, -2.436530739e-01f, + -2.464906573e-01f, -2.492015660e-01f, -2.518486679e-01f, -2.543752193e-01f, -2.567131221e-01f, + -2.589094639e-01f, -2.609195411e-01f, -2.626828551e-01f, -2.642259598e-01f, -2.655203342e-01f, + -2.665175796e-01f, -2.672180235e-01f, -2.676131129e-01f, -2.676709890e-01f, -2.673603892e-01f, + -2.666963637e-01f, -2.656670809e-01f, -2.642051876e-01f, -2.623519003e-01f, -2.601184249e-01f, + -2.573977113e-01f, -2.542599142e-01f, -2.507403195e-01f, -2.466842532e-01f, -2.421887666e-01f, + -2.373187691e-01f, -2.318770885e-01f, -2.259909213e-01f, -2.197538465e-01f, -2.129230499e-01f, + -2.056546956e-01f, -1.980721354e-01f, -1.898882985e-01f, -1.812870651e-01f, -1.724209785e-01f, + -1.629606336e-01f, -1.531155407e-01f, -1.430659294e-01f, -1.324429512e-01f, -1.214787737e-01f, + -1.103790030e-01f, -9.873924404e-02f, -8.681061864e-02f, -7.482151687e-02f, -6.233607978e-02f, + -4.961855337e-02f, -3.691909090e-02f, -2.378451265e-02f, -1.049306896e-02f, 2.693207469e-03f, + 1.625192724e-02f, 2.990356088e-02f, 4.337039590e-02f, 5.713094771e-02f, 7.090254128e-02f, + 8.440285176e-02f, 9.811186790e-02f, 1.117511913e-01f, 1.250380129e-01f, 1.384461671e-01f, + 1.517071873e-01f, 1.645433456e-01f, 1.774154305e-01f, 1.900701374e-01f, 2.022396475e-01f, + 2.143648863e-01f, 2.262119204e-01f, 2.375265509e-01f, 2.487246394e-01f, 2.595991492e-01f, + 2.699074745e-01f, 2.800140381e-01f, 2.897281349e-01f, 2.988373339e-01f, 3.076829016e-01f, + 3.161109984e-01f, 3.239248991e-01f, 3.314054310e-01f, 3.384239674e-01f, 3.448148072e-01f, + 3.508112133e-01f, 3.563160300e-01f, 3.611943424e-01f, 3.656288385e-01f, 3.695558012e-01f, + 3.728730381e-01f, 3.757116497e-01f, 3.780418336e-01f, 3.797947168e-01f, 3.810490668e-01f, + 3.818082809e-01f, 3.820356131e-01f, 3.817589283e-01f, 3.810169995e-01f, 3.797983527e-01f, + 3.780608475e-01f, 3.758624196e-01f, 3.732312620e-01f, 3.700926900e-01f, 3.665402830e-01f, + 3.626220226e-01f, 3.581997752e-01f, 3.533864319e-01f, 3.482605815e-01f, 3.426413834e-01f, + 3.366636932e-01f, 3.304314911e-01f, 3.237236738e-01f, 3.166953027e-01f, 3.094734550e-01f, + 3.018006682e-01f, 2.938496172e-01f, 2.857675850e-01f, 2.772644758e-01f, 2.685275078e-01f, + 2.597209513e-01f, 2.505262494e-01f, 2.411436141e-01f, 2.317493111e-01f, 2.219924927e-01f, + 2.120772004e-01f, 2.021964341e-01f, 1.919893622e-01f, 1.816717982e-01f, 1.714403331e-01f, + 1.609073281e-01f, 1.502889544e-01f, 1.397916675e-01f, 1.290175319e-01f, 1.181852967e-01f, + 1.075065285e-01f, 9.657467157e-02f, 8.560992032e-02f, 7.482705265e-02f, 6.381320953e-02f, + 5.278856680e-02f, 4.196942598e-02f, 3.093866445e-02f, 1.991511695e-02f, 9.115573019e-03f, + -1.880101045e-03f, -1.285706740e-02f, -2.359643020e-02f, -3.451175243e-02f, -4.538901895e-02f, + -5.601210147e-02f, -6.679218262e-02f, -7.751873881e-02f, -8.797838539e-02f, -9.857884049e-02f, + -1.091154292e-01f, -1.193764880e-01f, -1.297603101e-01f, -1.400669515e-01f, -1.500885487e-01f, + -1.602134407e-01f, -1.702471375e-01f, -1.799866408e-01f, -1.898093671e-01f, -1.995273978e-01f, + -2.089434117e-01f, -2.184227854e-01f, -2.277851552e-01f, -2.368389070e-01f, -2.459373474e-01f, + -2.549120784e-01f, -2.635726929e-01f, -2.722412050e-01f, -2.807511687e-01f, -2.889264524e-01f, + -2.970816493e-01f, -3.050665259e-01f, -3.127083182e-01f, -3.202905953e-01f, -3.276729286e-01f, + -3.346942663e-01f, -3.416129649e-01f, -3.483022749e-01f, -3.546133637e-01f, -3.607765734e-01f, + -3.666810393e-01f, -3.721924424e-01f, -3.775106668e-01f, -3.825429976e-01f, -3.871712387e-01f, + -3.915623128e-01f, -3.956434131e-01f, -3.993134499e-01f, -4.027050436e-01f, -4.057698846e-01f, + -4.084210992e-01f, -4.107375443e-01f, -4.126851857e-01f, -4.142063260e-01f, -4.153555930e-01f, + -4.161320627e-01f, -4.164912105e-01f, -4.164267182e-01f, -4.159574509e-01f, -4.150670767e-01f, + -4.137059152e-01f, -4.119192660e-01f, -4.097172022e-01f, -4.070037007e-01f, -4.038523734e-01f, + -4.003008902e-01f, -3.962059021e-01f, -3.916698098e-01f, -3.867584169e-01f, -3.812800944e-01f, + -3.753657937e-01f, -3.691091537e-01f, -3.622704744e-01f, -3.550091088e-01f, -3.474451602e-01f, + -3.392852545e-01f, -3.307102025e-01f, -3.218728304e-01f, -3.124470115e-01f, -3.026452065e-01f, + -2.926390767e-01f, -2.820453048e-01f, -2.710932493e-01f, -2.599841058e-01f, -2.482994795e-01f, + -2.362893224e-01f, -2.241788805e-01f, -2.115166932e-01f, -1.985713989e-01f, -1.855898052e-01f, + -1.720912755e-01f, -1.583598852e-01f, -1.446608752e-01f, -1.304886192e-01f, -1.161387265e-01f, + -1.018917859e-01f, -8.722117543e-02f, -7.242809981e-02f, -5.780776963e-02f, -4.282632843e-02f, + -2.779028192e-02f, -1.300174464e-02f, 2.079501515e-03f, 1.714750752e-02f, 3.189693391e-02f, + 4.686951637e-02f, 6.176655367e-02f, 7.628109306e-02f, 9.094361216e-02f, 1.054645777e-01f, + 1.195413396e-01f, 1.336874813e-01f, 1.476267278e-01f, 1.610653251e-01f, 1.744939536e-01f, + 1.876539439e-01f, 2.002646774e-01f, 2.127880305e-01f, 2.249868214e-01f, 2.365968972e-01f, + 2.480469197e-01f, 2.591283619e-01f, 2.695915699e-01f, 2.798080444e-01f, 2.895889878e-01f, + 2.987159491e-01f, 3.075262010e-01f, 3.158679307e-01f, 3.235436380e-01f, 3.308281302e-01f, + 3.375995755e-01f, 3.436928689e-01f, 3.493268490e-01f, 3.544144332e-01f, 3.588240147e-01f, + 3.627154827e-01f, 3.660386801e-01f, 3.686976135e-01f, 3.707916439e-01f, 3.723084331e-01f, + 3.731888235e-01f, 3.734701872e-01f, 3.731779158e-01f, 3.722891212e-01f, 3.707801700e-01f, + 3.687169254e-01f, 3.661069572e-01f, 3.628457189e-01f, 3.590241075e-01f, 3.546956182e-01f, + 3.497132063e-01f, 3.442106247e-01f, 3.382675052e-01f, 3.316617012e-01f, 3.245530427e-01f, + 3.170597255e-01f, 3.089070916e-01f, 3.002843261e-01f, 2.913431823e-01f, 2.817592919e-01f, + 2.717494071e-01f, 2.614963949e-01f, 2.506311536e-01f, 2.393945605e-01f, 2.279973477e-01f, + 2.160304189e-01f, 2.037547827e-01f, 1.914046109e-01f, 1.785370559e-01f, 1.654305905e-01f, + 1.523359120e-01f, 1.387710571e-01f, 1.250186563e-01f, 1.113520190e-01f, 9.728185833e-02f, + 8.310417831e-02f, 6.909710914e-02f, 5.474492908e-02f, 4.034131020e-02f, 2.617555112e-02f, + 1.172830351e-02f, -2.708313754e-03f, -1.684129611e-02f, -3.118869662e-02f, -4.546338320e-02f, + -5.937297642e-02f, -7.342834026e-02f, -8.735129237e-02f, -1.008543596e-01f, -1.144357026e-01f, + -1.278298944e-01f, -1.407581270e-01f, -1.537010670e-01f, -1.664106548e-01f, -1.786183119e-01f, + -1.907766014e-01f, -2.026547194e-01f, -2.140004188e-01f, -2.252379060e-01f, -2.361572683e-01f, + -2.465249747e-01f, -2.567337751e-01f, -2.665975392e-01f, -2.759028077e-01f, -2.850053608e-01f, + -2.937428057e-01f, -3.019248545e-01f, -3.098696172e-01f, -3.174393773e-01f, -3.244683444e-01f, + -3.312369883e-01f, -3.376323581e-01f, -3.435137868e-01f, -3.491242528e-01f, -3.543750048e-01f, + -3.591493070e-01f, -3.636552989e-01f, -3.678310215e-01f, -3.715764582e-01f, -3.750457764e-01f, + -3.781881630e-01f, -3.809344471e-01f, -3.834196329e-01f, -3.856185973e-01f, -3.874761164e-01f, + -3.890847564e-01f, -3.904325962e-01f, -3.914857209e-01f, -3.923090100e-01f, -3.929044902e-01f, + -3.932556510e-01f, -3.934029937e-01f, -3.933605254e-01f, -3.931269050e-01f, -3.927225173e-01f, + -3.921708167e-01f, -3.914827406e-01f, -3.906622827e-01f, -3.897395730e-01f, -3.887337744e-01f, + -3.876380920e-01f, -3.864907026e-01f, -3.853094578e-01f, -3.840570152e-01f, -3.827612102e-01f, + -3.814539313e-01f, -3.801115453e-01f, -3.787714541e-01f, -3.774562478e-01f, -3.761196434e-01f, + -3.747914135e-01f, -3.734975159e-01f, -3.721903861e-01f, -3.708972335e-01f, -3.696426153e-01f, + -3.683769107e-01f, -3.671254516e-01f, -3.659109473e-01f, -3.646815717e-01f, -3.634614050e-01f, + -3.622708619e-01f, -3.610546589e-01f, -3.598363400e-01f, -3.586335480e-01f, -3.573868871e-01f, + -3.561235666e-01f, -3.548549116e-01f, -3.534988165e-01f, -3.520762622e-01f, -3.506072462e-01f, + -3.490159512e-01f, -3.473375440e-01f, -3.455849886e-01f, -3.436502218e-01f, -3.415706158e-01f, + -3.393687308e-01f, -3.369202614e-01f, -3.342741430e-01f, -3.314620852e-01f, -3.283376992e-01f, + -3.249658048e-01f, -3.213908076e-01f, -3.174396157e-01f, -3.131962717e-01f, -3.087209463e-01f, + -3.038088679e-01f, -2.985660732e-01f, -2.930706739e-01f, -2.870825827e-01f, -2.807337642e-01f, + -2.741202712e-01f, -2.669519484e-01f, -2.593805790e-01f, -2.515319288e-01f, -2.430847138e-01f, + -2.342233509e-01f, -2.250945866e-01f, -2.153240442e-01f, -2.051205933e-01f, -1.946623474e-01f, + -1.835342944e-01f, -1.719736308e-01f, -1.601886898e-01f, -1.477243900e-01f, -1.348459870e-01f, + -1.217919514e-01f, -1.080697328e-01f, -9.397061169e-02f, -7.976163924e-02f, -6.491623074e-02f, + -4.974815622e-02f, -3.455008566e-02f, -1.876661927e-02f, -2.730545821e-03f, 1.324536651e-02f, + 2.974548191e-02f, 4.642751440e-02f, 6.295806170e-02f, 7.993429899e-02f, 9.700771421e-02f, + 1.138300598e-01f, 1.310003549e-01f, 1.481683403e-01f, 1.649783999e-01f, 1.820243895e-01f, + 1.989622116e-01f, 2.154350728e-01f, 2.320211977e-01f, 2.483901083e-01f, 2.641909719e-01f, + 2.799761295e-01f, 2.954359651e-01f, 3.102331161e-01f, 3.248832524e-01f, 3.391049802e-01f, + 3.525811434e-01f, 3.657810390e-01f, 3.784585893e-01f, 3.903227746e-01f, 4.017815292e-01f, + 4.126251638e-01f, 4.226006866e-01f, 4.320622981e-01f, 4.408522248e-01f, 4.487489164e-01f, + 4.560183287e-01f, 4.625449777e-01f, 4.681567252e-01f, 4.730458260e-01f, 4.771477282e-01f, + 4.803410172e-01f, 4.827403426e-01f, 4.843348861e-01f, 4.850567281e-01f, 4.849425256e-01f, + 4.840351045e-01f, 4.823206663e-01f, 4.797586203e-01f, 4.764430225e-01f, 4.724124074e-01f, + 4.675527811e-01f, 4.620080292e-01f, 4.558618963e-01f, 4.489150941e-01f, 4.413429201e-01f, + 4.332849979e-01f, 4.244999886e-01f, 4.152074456e-01f, 4.055778980e-01f, 3.952988982e-01f, + 3.846096396e-01f, 3.737212420e-01f, 3.622853160e-01f, 3.505596519e-01f, 3.387843668e-01f, + 3.265860975e-01f, 3.142333627e-01f, 3.019869030e-01f, 2.894618511e-01f, 2.769280076e-01f, + 2.646571100e-01f, 2.522660494e-01f, 2.400158197e-01f, 2.281785309e-01f, 2.163873017e-01f, + 2.048870176e-01f, 1.939361095e-01f, 1.831832081e-01f, 1.728374660e-01f, 1.631459296e-01f, + 1.538134962e-01f, 1.450208127e-01f, 1.369793862e-01f, 1.294333488e-01f, 1.225163117e-01f, + 1.164069250e-01f, 1.109128967e-01f, 1.061203107e-01f, 1.021651179e-01f, 9.892335534e-02f, + 9.643068165e-02f, 9.477529675e-02f, 9.390541911e-02f, 9.380473942e-02f, 9.451042116e-02f, + 9.604432434e-02f, 9.833856672e-02f, 1.013778523e-01f, 1.052570641e-01f, 1.098599955e-01f, + 1.151185334e-01f, 1.211929321e-01f, 1.279201806e-01f, 1.351851523e-01f, 1.432166398e-01f, + 1.518174112e-01f, 1.608223617e-01f, 1.705039144e-01f, 1.806313843e-01f, 1.910021603e-01f, + 2.019295692e-01f, 2.131614834e-01f, 2.244641632e-01f, 2.361772358e-01f, 2.480379939e-01f, + 2.597912252e-01f, 2.717878819e-01f, 2.837660313e-01f, 2.954595685e-01f, 3.072148860e-01f, + 3.187823296e-01f, 3.298959136e-01f, 3.408811986e-01f, 3.515131176e-01f, 3.615359366e-01f, + 3.712336123e-01f, 3.804130256e-01f, 3.888437152e-01f, 3.967618048e-01f, 4.040216506e-01f, + 4.104228318e-01f, 4.161259532e-01f, 4.210352600e-01f, 4.249966443e-01f, 4.280925095e-01f, + 4.302867651e-01f, 4.314795136e-01f, 4.316659272e-01f, 4.308755696e-01f, 4.290701151e-01f, + 4.261513650e-01f, 4.222179055e-01f, 4.172975719e-01f, 4.111950994e-01f, 4.040782452e-01f, + 3.960428536e-01f, 3.867963850e-01f, 3.765751421e-01f, 3.655398190e-01f, 3.532928228e-01f, + 3.401272595e-01f, 3.262744546e-01f, 3.112535477e-01f, 2.954141796e-01f, 2.790492475e-01f, + 2.615988255e-01f, 2.434586138e-01f, 2.249785960e-01f, 2.055351138e-01f, 1.855599880e-01f, + 1.654512286e-01f, 1.445374936e-01f, 1.232748255e-01f, 1.020983011e-01f, 8.030661196e-02f, + 5.836658925e-02f, 3.673781455e-02f, 1.470788289e-02f, -7.259938400e-03f, -2.869536355e-02f, + -5.030169338e-02f, -7.163300365e-02f, -9.222369641e-02f, -1.127510518e-01f, -1.328019351e-01f, + -1.519277543e-01f, -1.707595438e-01f, -1.889324039e-01f, -2.060236633e-01f, -2.225851715e-01f, + -2.383038998e-01f, -2.528039813e-01f, -2.665537000e-01f, -2.793058753e-01f, -2.907419801e-01f, + -3.012327254e-01f, -3.106060624e-01f, -3.186111450e-01f, -3.255109489e-01f, -3.312145174e-01f, + -3.355463445e-01f, -3.386537433e-01f, -3.405295908e-01f, -3.410782516e-01f, -3.403272629e-01f, + -3.383542299e-01f, -3.351432979e-01f, -3.305923343e-01f, -3.248532116e-01f, -3.179977238e-01f, + -3.098180592e-01f, -3.005455434e-01f, -2.903256118e-01f, -2.788305283e-01f, -2.663526833e-01f, + -2.531156838e-01f, -2.386991680e-01f, -2.234508097e-01f, -2.076620162e-01f, -1.908344775e-01f, + -1.733598709e-01f, -1.555867791e-01f, -1.369570345e-01f, -1.178923547e-01f, -9.878546000e-02f, + -7.903787494e-02f, -5.908566341e-02f, -3.935082257e-02f, -1.921609789e-02f, 8.833314641e-04f, + 2.051348798e-02f, 4.029469565e-02f, 5.981373787e-02f, 7.863469422e-02f, 9.734543413e-02f, + 1.155639365e-01f, 1.328734457e-01f, 1.498155445e-01f, 1.660564691e-01f, 1.812114567e-01f, + 1.957519799e-01f, 2.094039470e-01f, 2.218301743e-01f, 2.334157974e-01f, 2.439571023e-01f, + 2.531773448e-01f, 2.613613009e-01f, 2.683841288e-01f, 2.740396857e-01f, 2.785012424e-01f, + 2.817280591e-01f, 2.835920155e-01f, 2.841471732e-01f, 2.834396958e-01f, 2.814228237e-01f, + 2.780227661e-01f, 2.733684182e-01f, 2.674998343e-01f, 2.602220774e-01f, 2.517446876e-01f, + 2.421929985e-01f, 2.312565595e-01f, 2.192191482e-01f, 2.062879652e-01f, 1.920468658e-01f, + 1.768444330e-01f, 1.609633565e-01f, 1.438964754e-01f, 1.260456145e-01f, 1.077592075e-01f, + 8.845629543e-02f, 6.857804209e-02f, 4.852625355e-02f, 2.766555361e-02f, 6.460697856e-03f, + -1.464756578e-02f, -3.632741049e-02f, -5.810682476e-02f, -7.952253520e-02f, -1.012547985e-01f, + -1.228424460e-01f, -1.438144296e-01f, -1.648389101e-01f, -1.854840070e-01f, -2.052834332e-01f, + -2.248576432e-01f, -2.438130975e-01f, -2.617109716e-01f, -2.791112065e-01f, -2.956758440e-01f, + -3.110083640e-01f, -3.255881071e-01f, -3.391462564e-01f, -3.513431549e-01f, -3.625608683e-01f, + -3.726101816e-01f, -3.812204897e-01f, -3.886621594e-01f, + }; + + inline constexpr std::array k_chain_down_3_down_8_down_2 = { + -2.838523687e-13f, 4.134455978e-08f, 1.323617056e-07f, -8.285794138e-06f, 3.476158236e-05f, + -9.033401147e-05f, 1.936182671e-04f, -3.745282302e-04f, 6.844580639e-04f, -1.194558688e-03f, + }; + + inline constexpr std::array k_chain_ratio_3_8_down_3 = { + -1.888519341e-09f, 9.543536095e-08f, -2.212608888e-05f, 7.867775275e-05f, -2.214947745e-04f, + 4.947525449e-04f, -1.003982848e-03f, 1.857098658e-03f, -3.172187135e-03f, 5.192522891e-03f, + -8.561138064e-03f, 1.558249630e-02f, -5.336307362e-02f, -1.840328798e-02f, -6.761331111e-02f, + -1.126309410e-01f, 6.316543370e-02f, -1.550042331e-01f, -1.041266508e-02f, 1.258955002e-01f, + -1.338128000e-01f, -1.052539647e-01f, 4.722282290e-02f, -4.446770623e-02f, -2.213823497e-01f, + -5.331099406e-02f, 1.030908525e-02f, -4.886277393e-02f, -2.224011160e-02f, -1.974217780e-02f, + -4.945426807e-02f, -3.925302252e-02f, -8.424393088e-02f, 4.574041814e-02f, -8.553400403e-04f, + -6.936205178e-02f, 4.969200864e-02f, -6.328237802e-02f, -5.813684687e-02f, -1.132668480e-01f, + -6.931363791e-02f, -5.484471470e-02f, 5.634399131e-02f, 1.061660647e-01f, -2.230258659e-02f, + 4.563885927e-02f, -6.877265871e-02f, -1.789376885e-02f, 1.055105329e-01f, 9.242154658e-02f, + -8.985412866e-02f, -7.234854996e-02f, 7.066737115e-02f, 3.177836537e-02f, -1.144622341e-01f, + 5.692136660e-02f, 4.653693736e-02f, -1.316790283e-01f, -5.908179283e-02f, -1.007550508e-01f, + }; + } // namespace rational_ref // NOLINTEND(readability-identifier-naming) diff --git a/rational/tests/support/bridge_stage.h b/rational/tests/support/bridge_stage.h new file mode 100644 index 0000000..148505f --- /dev/null +++ b/rational/tests/support/bridge_stage.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT +// Copyright 2026 Timothy Place and the SampleRateTap contributors +// +// bridge's converter as a tap::dsp::sync_stage, for the cross-family rows +// of the coverage matrix (PLAN.md 3.5 / 3.6): a test-side adapter, since a +// test may name a sibling engine (4.2) and the engines themselves never do. +// It presents the converter's own machine unchanged: k_up / k_down are its +// L / M, process and outputs_for are its (outputs_for narrowed to size_t), +// window_frames() is its taps() (what flush() feeds), the latency is the +// prototype's (L T - 1) / 2 at the composite rate as an exact rational in +// output frames (phase_table.h's group_delay_input_samples times L / M), +// and macs_per_output() is T, its taps per phase. +#pragma once + +#include +#include + +#include "tap/dsp/chain.h" +#include "tap/dsp/sample_traits.h" +#include "tap/sr/bridge/converter.h" + +namespace rational_test { + + template + class bridge_stage { + public: + using sample = S; + using traits = tap::sr::bridge::ratio_traits; + + static constexpr std::size_t k_up = traits::k_phases; ///< L + static constexpr std::size_t k_down = traits::k_decimation; ///< M + static constexpr unsigned k_rate_scale = K; + + explicit bridge_stage(std::size_t channels = 1, + const tap::sr::bridge::profile& p = tap::sr::bridge::profile::economy()) + : m_converter(channels, p) {} + + std::size_t process(const S* in, std::size_t in_frames, S* out) noexcept { + return m_converter.process(in, in_frames, out); + } + std::size_t outputs_for(std::size_t in_frames) const noexcept { + return static_cast(m_converter.outputs_for(in_frames)); + } + void reset() noexcept { m_converter.reset(); } + std::size_t window_frames() const noexcept { return m_converter.taps(); } + tap::dsp::exact_ratio latency_output_frames() const noexcept { + return tap::dsp::exact_ratio{static_cast(k_up * m_converter.taps() - 1), + static_cast(2 * k_down)}; + } + /// T per output, L T per superblock of L outputs (the chain's + /// accounting shape). + std::size_t macs_per_superblock() const noexcept { return k_up * m_converter.taps(); } + double macs_per_output() const noexcept { return static_cast(m_converter.taps()); } + std::size_t taps() const noexcept { return m_converter.taps(); } + + private: + tap::sr::bridge::basic_converter m_converter; + }; + + static_assert(tap::dsp::sync_stage>); + static_assert(tap::dsp::sync_stage>); + +} // namespace rational_test diff --git a/rational/tests/test_chain.cpp b/rational/tests/test_chain.cpp new file mode 100644 index 0000000..e80e2b5 --- /dev/null +++ b/rational/tests/test_chain.cpp @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: MIT +// Copyright 2026 Timothy Place and the SampleRateTap contributors +// +// Contract battery for the chains (milestone M4): the design divisors a +// chain assigns its stages (compile-time, the named chains pinned), a chain +// equal to its stages run in sequence bit for bit in every format, the +// committed sequenced-upfirdn vectors (the scipy leg, independent of +// chain<>'s bookkeeping), accounting exact from every position of every +// named chain, chunking, reset, flush as zero padding, the latency as the +// exact rational sum and by impulse, MACs per output as the stages' sum, +// DC gain exact in every format, channels. + +#include +#include +#include +#include +#include +#include + +#include + +#include "reference/reference_vectors.h" +#include "tap/sr/rational/rational.h" + +namespace { + + using namespace tap::sr::rational; // NOLINT(google-build-using-namespace) + using tap::dsp::exact_ratio; + + template + class chain_test : public ::testing::Test {}; + using sample_types = ::testing::Types; + TYPED_TEST_SUITE(chain_test, sample_types, ); + + template + class chain_golden_test : public ::testing::Test {}; + using golden_types = ::testing::Types; + TYPED_TEST_SUITE(chain_golden_test, golden_types, ); + + template + S to_sample(double v) { + if constexpr (std::is_floating_point_v) { + return static_cast(v); + } + else { + return static_cast(std::llround(v * static_cast(std::numeric_limits::max()))); + } + } + + template + std::vector reference_input() { + std::vector x(rational_ref::k_input.size()); + for (std::size_t i = 0; i < x.size(); ++i) { + x[i] = to_sample(rational_ref::k_input[i]); + } + return x; + } + + // ------------------------------------------------------------------ + // The design divisors (chain.h, PLAN.md 3.1): a stage's lower rate over + // the chain's lowest, the largest 2^a 3^b at or below it. + static_assert(up_2_up_8_up_3::k_divisors + == std::array{exact_ratio{1, 1}, exact_ratio{2, 1}, exact_ratio{16, 1}}); + static_assert(down_3_down_8_down_2::k_divisors + == std::array{exact_ratio{16, 1}, exact_ratio{2, 1}, exact_ratio{1, 1}}); + static_assert(up_2_up_6_up_2::k_divisors + == std::array{exact_ratio{1, 1}, exact_ratio{2, 1}, exact_ratio{12, 1}}); + static_assert(ratio_3_8_down_3::k_divisors == std::array{exact_ratio{3, 1}, exact_ratio{1, 1}}); + static_assert(up_3_ratio_8_3::k_divisors == std::array{exact_ratio{1, 1}, exact_ratio{3, 1}}); + static_assert(up_2_ratio_4_3_up_3::k_divisors + == std::array{exact_ratio{1, 1}, exact_ratio{2, 1}, exact_ratio{2, 1}}); + static_assert(down_3_ratio_3_4_down_2::k_divisors + == std::array{exact_ratio{2, 1}, exact_ratio{2, 1}, exact_ratio{1, 1}}); + static_assert(ratio_3_4_down_2::k_divisors == std::array{exact_ratio{2, 1}, exact_ratio{1, 1}}); + static_assert(basic_chain::k_divisors == std::array{exact_ratio{1, 1}}); + // A chain written outside the matrix's rules still gets a divisor: the + // lattice floor (4/3 then down 2, 12 -> 16 -> 8: the 4/3's lower rate is + // 12 over r_min 8, 3/2, floored to 1). + static_assert(basic_chain::k_divisors + == std::array{exact_ratio{1, 1}, exact_ratio{1, 1}}); + // The chain's own ratio, reduced, and its stage count. + static_assert(up_2_up_8_up_3::k_up == 48 && up_2_up_8_up_3::k_down == 1); + static_assert(ratio_3_8_down_3::k_up == 1 && ratio_3_8_down_3::k_down == 8); + static_assert(up_2_ratio_4_3::k_up == 8 && up_2_ratio_4_3::k_down == 3); + static_assert(down_2_down_6_down_2::k_stages == 3); + + TEST(Chain, DesignDivisorIsTheLatticeFloorOrTheFractionBelowOne) { + using tap::sr::rational::detail::design_divisor; + EXPECT_EQ(design_divisor(exact_ratio{1, 1}), (exact_ratio{1, 1})); + EXPECT_EQ(design_divisor(exact_ratio{441, 80}), (exact_ratio{4, 1})); // 44.1 / 8: 5.51 -> 4 + EXPECT_EQ(design_divisor(exact_ratio{441, 40}), (exact_ratio{9, 1})); // 88.2 / 8: 11.025 -> 9 + EXPECT_EQ(design_divisor(exact_ratio{147, 160}), (exact_ratio{147, 160})); // below 1: itself + EXPECT_EQ(design_divisor(exact_ratio{5, 1}), (exact_ratio{4, 1})); + EXPECT_EQ(design_divisor(exact_ratio{7, 1}), (exact_ratio{6, 1})); + EXPECT_EQ(design_divisor(exact_ratio{48, 1}), (exact_ratio{48, 1})); + EXPECT_EQ(design_divisor(exact_ratio{100, 1}), (exact_ratio{96, 1})); + EXPECT_EQ(design_divisor(exact_ratio{3, 2}), (exact_ratio{1, 1})); + } + + TEST(Chain, RelaxedProfileCarriesTheTablesPins) { + const profile e2 = profile::economy().relaxed(exact_ratio{2, 1}); + EXPECT_DOUBLE_EQ(e2.passband_frac, 3.0 / 16.0); + EXPECT_EQ(e2.stopband_atten_db, 70.0); + EXPECT_EQ(e2.pinned_taps_per_branch(2), 7u); + EXPECT_EQ(e2.pinned_taps_per_branch(3), 5u); + EXPECT_TRUE(e2.relaxations.empty()); + const profile tb = profile::transparent().relaxed(exact_ratio{147, 160}); + EXPECT_DOUBLE_EQ(tb.passband_frac, 5.0 / 12.0 * 160.0 / 147.0); + EXPECT_EQ(tb.pinned_taps_per_branch(2), 45u); + EXPECT_EQ(tb.pinned_taps_per_branch(8), 46u); + // A divisor without a row: no pins, the search. + const profile e5 = profile::economy().relaxed(exact_ratio{5, 1}); + EXPECT_EQ(e5.pinned_taps_per_branch(2), 0u); + EXPECT_DOUBLE_EQ(e5.passband_frac, 3.0 / 40.0); + // relaxed(1) is the profile itself, pins and tables included. + const profile e1 = profile::balanced().relaxed(exact_ratio{1, 1}); + EXPECT_EQ(e1.pinned_taps_per_branch(4), 14u); + EXPECT_EQ(e1.relaxations.size(), k_balanced_relaxations.size()); + // A custom profile relaxes to a search at the scaled passband. + profile custom = profile::economy(); + custom.passband_frac = 0.3; + custom.taps_per_branch = {0, 0, 0, 0, 0}; + custom.relaxations = {}; + EXPECT_DOUBLE_EQ(custom.relaxed(exact_ratio{3, 1}).passband_frac, 0.1); + EXPECT_EQ(custom.relaxed(exact_ratio{3, 1}).pinned_taps_per_branch(2), 0u); + } + + // ------------------------------------------------------------------ + // A chain is its stages run in sequence, each at the relaxed profile: + // bit for bit, every format. + template + std::vector run_one_stage(const std::vector& in, const profile& p, exact_ratio divisor) { + basic_stage s(1, p.relaxed(divisor)); + std::vector out(s.outputs_for(in.size())); + const std::size_t made = s.process(in.data(), in.size(), out.data()); + out.resize(made); + return out; + } + + template + std::vector run_stages_in_sequence(const std::vector& x, const profile& p) { + constexpr auto divisors = basic_chain::k_divisors; + std::vector cur = x; + std::size_t i = 0; + ((cur = run_one_stage(cur, p, divisors[i++])), ...); + return cur; + } + + template + void check_chain_is_its_stages(const profile& p) { + const auto x = reference_input(); + basic_chain c(1, p); + std::vector y(c.outputs_for(x.size())); + ASSERT_EQ(c.process(x.data(), x.size(), y.data()), y.size()); + const auto ref = run_stages_in_sequence(x, p); + ASSERT_EQ(y.size(), ref.size()); + EXPECT_TRUE(y == ref); + } + + TYPED_TEST(chain_test, ChainEqualsItsStagesRunInSequenceBitForBit) { + using sample = TypeParam; + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::economy()); + check_chain_is_its_stages(profile::transparent()); + check_chain_is_its_stages(profile::super_economy()); + check_chain_is_its_stages(profile::balanced()); + } + + // ------------------------------------------------------------------ + // The scipy leg: the committed vectors are upfirdn applied in sequence + // over the relaxed designs (tools/reference/make_reference_vectors.py), + // compared sample for sample from n = 0 at the single stages' + // tolerances (float: the float32 coefficient floor, 3e-5; double: the + // reference's own float32 cast, 1e-7). + // The up chains run on the first 96 frames of the input (their vectors + // are L times longer), the down chains on all 480. + template + void check_matches_scipy(const std::array& ref) { + basic_chain c(1, profile::economy()); + constexpr std::size_t n_in = basic_chain::k_up > basic_chain::k_down ? 96 : 480; + std::vector x(rational_ref::k_input.begin(), rational_ref::k_input.begin() + n_in); + std::vector y(c.outputs_for(x.size())); + ASSERT_EQ(c.process(x.data(), x.size(), y.data()), y.size()); + ASSERT_EQ(y.size(), N); + const double tol = std::is_same_v ? 3e-5 : 1e-7; + double worst = 0.0; + for (std::size_t n = 0; n < N; ++n) { + worst = std::max(worst, std::fabs(static_cast(y[n]) - static_cast(ref[n]))); + } + EXPECT_LE(worst, tol); + } + + TYPED_TEST(chain_golden_test, MatchesSequencedUpfirdn) { + using sample = TypeParam; + check_matches_scipy( + rational_ref::k_chain_up_2_up_2); + check_matches_scipy( + rational_ref::k_chain_down_3_down_2); + check_matches_scipy( + rational_ref::k_chain_ratio_3_4_down_2); + check_matches_scipy( + rational_ref::k_chain_up_2_ratio_4_3); + check_matches_scipy( + rational_ref::k_chain_up_2_up_8_up_3); + check_matches_scipy( + rational_ref::k_chain_down_3_down_8_down_2); + check_matches_scipy( + rational_ref::k_chain_ratio_3_8_down_3); + } + + // ------------------------------------------------------------------ + // Accounting from every position of the chain's superblock (k_up k_down + // inputs): outputs_for(n) is exactly what process(n) writes, and + // frames_needed(k) the smallest n with outputs_for(n) >= k. + template + void check_accounting_from_every_position() { + constexpr std::size_t period = Chain::k_up * Chain::k_down; + Chain c(1); + std::vector x((2 * period + 2) * Chain::k_down + 8, 0.25f); + std::vector y(c.outputs_for(x.size()) + 8); + for (std::size_t pos = 0; pos < 2 * period; ++pos) { + for (std::size_t n = 0; n <= 2 * period; ++n) { + Chain probe = c; + EXPECT_EQ(probe.process(x.data(), n, y.data()), c.outputs_for(n)) << "pos " << pos << " n " << n; + } + EXPECT_EQ(c.frames_needed(0), 0u); + for (std::size_t k = 1; k <= 2 * period; ++k) { + const std::size_t n = c.frames_needed(k); + EXPECT_GE(c.outputs_for(n), k) << "pos " << pos << " k " << k; + EXPECT_LT(c.outputs_for(n - 1), k) << "pos " << pos << " k " << k; + Chain probe = c; + ASSERT_LE(n, x.size()); + EXPECT_GE(probe.process(x.data(), n, y.data()), k) << "pos " << pos << " k " << k; + } + c.process(x.data(), 1, y.data()); + } + } + + TEST(Chain, AccountingExactFromEveryPositionOfEveryNamedChain) { + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + check_accounting_from_every_position>(); + } + + // ------------------------------------------------------------------ + // Bit-exact repeatability: any chunking, reset. + template + void check_chunking(const std::vector& x) { + using chain_type = basic_chain; + chain_type whole(1); + std::vector ref(whole.outputs_for(x.size())); + whole.process(x.data(), x.size(), ref.data()); + for (const std::size_t chunk : {1u, 7u, 63u, 64u, 65u, 200u}) { + chain_type c(1); + std::vector y; + std::vector buf(c.outputs_for(chunk) + chain_type::k_up + 2); + for (std::size_t pos = 0; pos < x.size(); pos += chunk) { + const std::size_t n = pos + chunk <= x.size() ? chunk : x.size() - pos; + const std::size_t want = c.outputs_for(n); + ASSERT_LE(want, buf.size()); + ASSERT_EQ(c.process(x.data() + pos, n, buf.data()), want) << "chunk " << chunk << " at " << pos; + y.insert(y.end(), buf.begin(), buf.begin() + static_cast(want)); + } + EXPECT_TRUE(y == ref) << "chunk " << chunk; + } + } + + TYPED_TEST(chain_test, ChunkingIsBitIdentical) { + using sample = TypeParam; + const auto x = reference_input(); + check_chunking(x); + check_chunking(x); + check_chunking(x); + } + + TYPED_TEST(chain_test, ResetReproducesBitExactly) { + using sample = TypeParam; + const auto x = reference_input(); + up_2_ratio_4_3_up_3 c(1); + std::vector a(c.outputs_for(x.size())); + c.process(x.data(), x.size(), a.data()); + std::vector scratch(c.outputs_for(100)); + c.process(x.data(), 100, scratch.data()); // mid-stream + c.reset(); + std::vector b(c.outputs_for(x.size())); + ASSERT_EQ(a.size(), b.size()); + c.process(x.data(), x.size(), b.data()); + EXPECT_TRUE(a == b); + } + + // ------------------------------------------------------------------ + // flush: flush_output_frames() frames, equal to zero-padding the chain's + // input bit for bit (a prefix of the padded stream), and silence after. + template + void check_flush(const profile& p) { + using chain_type = basic_chain; + const auto x = reference_input(); + chain_type c(1, p); + std::vector y(c.outputs_for(x.size())); + c.process(x.data(), x.size(), y.data()); + chain_type padded = c; + const std::size_t expect = c.flush_output_frames(); + std::vector tail(expect + 4, to_sample(0.5)); + ASSERT_EQ(c.flush(tail.data()), expect); + const std::size_t zeros = 2 * padded.frames_needed(expect + 1) + 64; + std::vector z(zeros, tap::dsp::sample_traits::silence()); + std::vector yz(padded.outputs_for(zeros)); + const std::size_t made = padded.process(z.data(), zeros, yz.data()); + ASSERT_GT(made, expect); + for (std::size_t n = 0; n < expect; ++n) { + ASSERT_EQ(yz[n], tail[n]) << n; + } + for (std::size_t n = expect; n < made; ++n) { + ASSERT_EQ(yz[n], tap::dsp::sample_traits::silence()) << n; + } + } + + TYPED_TEST(chain_test, FlushEqualsZeroPaddingBitForBit) { + using sample = TypeParam; + check_flush(profile::economy()); + check_flush(profile::economy()); + check_flush(profile::economy()); + check_flush(profile::economy()); + check_flush(profile::transparent()); + check_flush(profile::economy()); + } + + // ------------------------------------------------------------------ + // Latency: the exact rational sum of the stages' (N - 1) / 2 at the + // output rate, and the impulse peaks within half a frame of it. + template + void check_latency_by_impulse(exact_ratio expect, double out_rate_hz) { + Chain c(1); + EXPECT_EQ(c.latency_output_frames(), expect); + EXPECT_NEAR(c.latency_seconds(out_rate_hz), expect.value() / out_rate_hz, 1e-15); + const std::size_t n_in = c.frames_needed(static_cast(2.0 * expect.value()) + 16); + std::vector x(n_in, 0.0f); + x[0] = 1.0f; + std::vector y(c.outputs_for(n_in)); + c.process(x.data(), n_in, y.data()); + std::size_t peak = 0; + for (std::size_t n = 1; n < y.size(); ++n) { + if (std::fabs(y[n]) > std::fabs(y[peak])) { + peak = n; + } + } + EXPECT_LE(std::fabs(static_cast(peak) - expect.value()), 0.5 + 1e-9) << "peak " << peak; + } + + TEST(Chain, LatencyIsTheExactRationalSumAndMatchesTheImpulse) { + // Economy: up 2 (N = 43: 21 frames at 2 r) then up 8 at divisor 2 + // (m = 4, N = 63: 31 at 16 r) then up 3 at divisor 16 (m = 3, + // N = 17: 8 at 48 r): 21 * 24 + 31 * 3 + 8 = 605... measured 629 + // with the relaxation table's m (up 8 at 2: m = 5, N = 79: 39 at + // 16 r -> 39 * 3 = 117; 504 + 117 + 8 = 629). + check_latency_by_impulse>(exact_ratio{629, 1}, 384000.0); + check_latency_by_impulse>(exact_ratio{629, 48}, 8000.0); + check_latency_by_impulse>(exact_ratio{295, 24}, 11025.0); + check_latency_by_impulse>(exact_ratio{295, 3}, 88200.0); + check_latency_by_impulse>(exact_ratio{55, 1}, 32000.0); + check_latency_by_impulse>(exact_ratio{55, 4}, 8000.0); + } + + // ------------------------------------------------------------------ + // MACs per output: the stages' trimmed-row counts scaled by their output + // rates over the chain's (the matrix's numbers; test_matrix.cpp pins + // every row, this pins the shape of the sum). + TEST(Chain, MacsPerOutputIsTheStagesSumAtTheOutputRate) { + up_2_up_8_up_3 up(1); + // 23/2 per output at 2 r (over 24 for the chain's rate) + (2 * 5 * 7 + 1) / 8 at 16 r (over 3) + (2 * 3 * 2 + + // 1) / 3 + EXPECT_EQ(up.macs_per_output_exact(), (exact_ratio{373, 48})); + EXPECT_DOUBLE_EQ(up.macs_per_output(), 373.0 / 48.0); + down_3_down_8_down_2 down(1); + EXPECT_EQ(down.macs_per_output_exact(), (exact_ratio{373, 1})); // 13 * 16 + 71 * 2 + 23 + basic_chain one(1); + EXPECT_EQ(one.macs_per_output_exact(), (exact_ratio{23, 2})); + EXPECT_DOUBLE_EQ(one.macs_per_output(), (basic_stage(1).macs_per_output())); + } + + // ------------------------------------------------------------------ + // DC gain 1 through a chain: exact in the fixed-point formats (every + // row sums to unity exactly, so a constant propagates exactly through + // every stage), within rounding of the three stages' sums in float and + // double (the single stages' bounds, test_stage.cpp, times the stages). + TYPED_TEST(chain_test, DcGainIsExactlyOne) { + using sample = TypeParam; + const sample dc = to_sample(0.5); + down_3_ratio_3_4_down_2 c(1); + const std::size_t n_in = c.frames_needed(400); + std::vector x(n_in, dc); + std::vector y(c.outputs_for(n_in)); + const std::size_t made = c.process(x.data(), n_in, y.data()); + ASSERT_GE(made, 400u); + for (std::size_t n = made - 100; n < made; ++n) { + if constexpr (std::is_same_v) { + EXPECT_NEAR(y[n], 0.5, 3e-14) << n; + } + else if constexpr (std::is_same_v) { + EXPECT_NEAR(y[n], 0.5f, 6e-7f) << n; + } + else { + EXPECT_EQ(y[n], dc) << n; + } + } + } + + TEST(Chain, TwoChannelsAreIndependentAndEqualTheMonoRuns) { + const auto x = reference_input(); + std::vector left(x); + std::vector right(x.size()); + for (std::size_t i = 0; i < x.size(); ++i) { + right[i] = -0.5f * x[(i * 7) % x.size()]; + } + std::vector stereo(2 * x.size()); + for (std::size_t i = 0; i < x.size(); ++i) { + stereo[2 * i] = left[i]; + stereo[2 * i + 1] = right[i]; + } + up_2_up_6 c(2); + std::vector y(2 * c.outputs_for(x.size())); + const std::size_t made = c.process(stereo.data(), x.size(), y.data()); + up_2_up_6 l(1); + up_2_up_6 r(1); + std::vector yl(l.outputs_for(x.size())); + std::vector yr(r.outputs_for(x.size())); + ASSERT_EQ(l.process(left.data(), x.size(), yl.data()), made); + ASSERT_EQ(r.process(right.data(), x.size(), yr.data()), made); + for (std::size_t n = 0; n < made; ++n) { + ASSERT_EQ(y[2 * n], yl[n]) << n; + ASSERT_EQ(y[2 * n + 1], yr[n]) << n; + } + EXPECT_EQ(c.channels(), 2u); + } + + TEST(Chain, ZeroChannelsThrows) { + EXPECT_THROW((up_2_up_2(0)), std::invalid_argument); + } + +} // namespace diff --git a/rational/tests/test_design.cpp b/rational/tests/test_design.cpp index acd3f99..64795d5 100644 --- a/rational/tests/test_design.cpp +++ b/rational/tests/test_design.cpp @@ -274,6 +274,42 @@ namespace { // A spec no length within the search bound meets (no pin: searched to // k_design_max_taps_per_branch on the design grid; seconds on a host, // excluded from the emulated legs by bare_metal_main.cpp and ci.yml). + // The relaxation tables (M4, design.h; tools/coverage/matrix.py `pins`): + // every entry of every named profile is what the shipping search finds + // for the band at the relaxed passband, on the design grid with the + // margin — the same criterion as the M2 pins. The 1/1 row is the M2 + // table; 147/160 tightens (the stage at bridge's rate under a 48-family + // r_min); the others relax. The 70 dB tables run on every leg, the + // transparent one (designs up to N = 735) on the hosts. + void expect_relaxations_are_the_search(const profile& p) { + for (const auto& row : p.relaxations) { + const profile r = p.relaxed(row.divisor); + EXPECT_EQ(r.taps_per_branch, row.taps_per_branch); + if (row.divisor == tap::dsp::exact_ratio{1, 1}) { + EXPECT_EQ(row.taps_per_branch, p.taps_per_branch); + } + const std::size_t bands[] = {2, 3, 4, 6, 8}; + for (std::size_t b = 0; b < 5; ++b) { + const std::size_t found = + tap::dsp::search_nyquist_m(bands[b], r.passband_frac, r.stopband_atten_db, k_design_margin_db, + k_design_max_taps_per_branch, k_design_grid_points); + EXPECT_EQ(found, row.taps_per_branch[b]) + << "divisor " << row.divisor.num << "/" << row.divisor.den << " band " << bands[b]; + } + } + } + + TEST(Design, RelaxationTablesAreTheSearchAt70dB) { + expect_relaxations_are_the_search(profile::super_economy()); + expect_relaxations_are_the_search(profile::economy()); + expect_relaxations_are_the_search(profile::balanced()); + EXPECT_EQ(profile::economy().relaxations.size(), 10u); + } + + TEST(Design, RelaxationTablesAreTheSearchAtTransparent) { + expect_relaxations_are_the_search(profile::transparent()); + } + TEST(Design, UnmeetableSpecThrows) { const profile r = {.passband_frac = 0.4999, .stopband_atten_db = 160.0, .taps_per_branch = {0, 0, 0, 0, 0}}; EXPECT_THROW((design_stage(r)), std::runtime_error); diff --git a/rational/tests/test_matrix.cpp b/rational/tests/test_matrix.cpp new file mode 100644 index 0000000..fdbf04a --- /dev/null +++ b/rational/tests/test_matrix.cpp @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: MIT +// Copyright 2026 Timothy Place and the SampleRateTap contributors +// +// The coverage-matrix test (milestone M4, PLAN.md section 5 leg 3): every +// one of the 182 ordered pairs of the family's fourteen rates, the chain +// of section 3 built in double from the generated row table +// (tests/coverage/matrix_rows.h, tools/coverage/matrix.py). Per row and +// profile it pins MACs per output and latency exactly against the table, +// checks the within-family chain is the named chain (its divisors are +// basic_chain's), measures the chain against its promise (f_pass, A, delta; +// 2.1) with a tone battery (images and aliases at the bins arithmetic +// predicts, the passband gain), checks accounting, flush and the impulse +// against the latency. Cross-family rows build bridge through +// support/bridge_stage.h (a test may name a sibling, 4.2); every row is +// measured, none skipped (bridge's rate scale K = 1, 2 landed as 2.2). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "coverage/matrix_rows.h" +#include "support/bridge_stage.h" +#include "tap/sr/rational/rational.h" + +namespace { + + using namespace tap::sr::rational; // NOLINT(google-build-using-namespace) + using tap::dsp::exact_ratio; + + constexpr exact_ratio d(std::uint64_t num, std::uint64_t den) { + return exact_ratio{num, den}; + } + + // The stage descriptors of the row table: a rational ratio type, or + // bridge at a rate scale. + template + struct bridge_down {}; + template + struct bridge_up {}; + + template + struct is_bridge : std::false_type {}; + template + struct is_bridge> : std::true_type {}; + template + struct is_bridge> : std::true_type {}; + + template + struct stage_for; + template + struct stage_for { + using type = basic_stage; + }; + template + struct stage_for> { + using type = rational_test::bridge_stage; + }; + template + struct stage_for> { + using type = rational_test::bridge_stage; + }; + + // The four profiles by index, rational's and bridge's by the same name. + constexpr std::array k_profile_names = {"super_economy", "economy", "balanced", "transparent"}; + profile rational_profile(std::size_t i) { + switch (i) { + case 0: + return profile::super_economy(); + case 1: + return profile::economy(); + case 2: + return profile::balanced(); + default: + return profile::transparent(); + } + } + tap::sr::bridge::profile bridge_profile(std::size_t i) { + switch (i) { + case 0: + return tap::sr::bridge::profile::super_economy(); + case 1: + return tap::sr::bridge::profile::economy(); + case 2: + return tap::sr::bridge::profile::balanced(); + default: + return tap::sr::bridge::profile::transparent(); + } + } + + template + typename stage_for::type make_stage(std::size_t channels, std::size_t prof, exact_ratio divisor) { + if constexpr (is_bridge::value) { + return typename stage_for::type(channels, bridge_profile(prof)); + } + else { + return typename stage_for::type(channels, rational_profile(prof).relaxed(divisor)); + } + } + + struct row_spec { + std::size_t src; + std::size_t dst; + bool flagged; + std::vector divisors; + std::array pins; ///< (macs num, den, latency num, den) per profile + + exact_ratio macs(std::size_t prof) const { return exact_ratio{pins[4 * prof], pins[4 * prof + 1]}; } + exact_ratio latency(std::size_t prof) const { return exact_ratio{pins[4 * prof + 2], pins[4 * prof + 3]}; } + }; + + template + using row_chain = tap::dsp::chain::type...>; + + template + row_chain build_chain(const row_spec& r, std::size_t prof, std::size_t channels, + std::index_sequence) { + return row_chain(channels, make_stage(channels, prof, r.divisors[I])...); + } + template + row_chain build_chain(const row_spec& r, std::size_t prof, std::size_t channels = 1) { + return build_chain(r, prof, channels, std::index_sequence_for{}); + } + + /// The rates along a chain: before each stage, and after the last. + template + std::vector chain_rates(std::size_t src) { + std::vector rates{src}; + ( + [&] { + using st = typename stage_for::type; + rates.push_back(rates.back() * st::k_up / st::k_down); + }(), + ...); + return rates; + } + + /// MACs per output, exact: each stage's superblock count over its + /// outputs per superblock, scaled by its output rate over the chain's. + template + exact_ratio chain_macs(const Chain& c, const std::vector& rates) { + exact_ratio total{0, 1}; + [&](std::index_sequence) { + ((total = + total + + exact_ratio{c.template stage().macs_per_superblock(), + std::tuple_element_t::type...>>::k_up} + * exact_ratio{rates[I + 1], rates.back()}), + ...); + }(std::index_sequence_for{}); + return total; + } + + // ------------------------------------------------------------------ + // The measurement: a tone at input frequency f (an exact bin of the + // analysis window) through the chain in double; the amplitude at every + // bin arithmetic says an image or alias can land on (|k R_j +- f| + // folded to the output's Nyquist, over every rate R_j of the chain) + // that lies in the chain's passband, at or below f_pass — the promise + // (2.1) is the passband; between f_pass and the output's Nyquist the + // output is unspecified — and at the tone's own bin. The analysis window is n output frames + // after the transient, n a multiple of every R_j / f_out's denominator + // so every such frequency is a bin: a rectangular window then measures + // each component exactly, without leakage, which is what resolves + // -120 dB beside a 0 dB tone. + struct tone_result { + double worst_spur_db; + double gain_db; + }; + + template + tone_result measure_tone(Chain& c, const std::vector& rates, std::size_t n, std::size_t bin, + std::size_t skip, std::size_t pass_bin) { + const std::size_t f_in = rates.front(); + const std::size_t f_out = rates.back(); + c.reset(); + const std::size_t n_in = c.frames_needed(skip + n); + const double w = 2.0 * std::numbers::pi * static_cast(bin) * static_cast(f_out) + / static_cast(n) / static_cast(f_in); + std::vector x(n_in); + for (std::size_t i = 0; i < n_in; ++i) { + x[i] = 0.5 * std::sin(w * static_cast(i)); + } + std::vector y(c.outputs_for(n_in)); + const std::size_t made = c.process(x.data(), n_in, y.data()); + EXPECT_GE(made, skip + n); + // The bins to look at: the tone's own, folded, and every image / + // alias candidate. In units of f_out / n, R_j is an integer. + std::set bins; + const auto fold = [n](std::size_t b) { + b %= n; + return b > n / 2 ? n - b : b; + }; + const std::size_t own = fold(bin); + for (const std::size_t r : rates) { + const std::size_t ru = r * n / f_out; // exact by n's construction + for (std::size_t k = 0; k * ru <= 2 * n + bin; ++k) { + bins.insert(fold(k * ru + bin)); + bins.insert(fold(k * ru >= bin ? k * ru - bin : bin - k * ru)); + } + } + const auto amplitude = [&](std::size_t b) { + std::complex acc{0.0, 0.0}; + const double step = -2.0 * std::numbers::pi * static_cast(b) / static_cast(n); + for (std::size_t k = 0; k < n; ++k) { + acc += y[skip + k] * std::polar(1.0, step * static_cast(k)); + } + return 2.0 * std::abs(acc) / static_cast(n); + }; + double worst = 0.0; + for (const std::size_t b : bins) { + if (b != own && b != 0 && b <= pass_bin) { + worst = std::max(worst, amplitude(b)); + } + } + const double gain = amplitude(own) / 0.5; + return {20.0 * std::log10(std::max(worst / 0.5, 1e-300)), 20.0 * std::log10(gain)}; + } + + /// The analysis length: a multiple of every denominator of R_j / f_out, + /// at least `target` frames. + std::size_t analysis_length(const std::vector& rates, std::size_t target) { + const std::size_t f_out = rates.back(); + std::size_t n0 = 1; + for (const std::size_t r : rates) { + n0 = std::lcm(n0, f_out / std::gcd(r, f_out)); + } + return n0 * ((target + n0 - 1) / n0); + } + + struct row_measurement { + double worst_spur_db = -400.0; ///< the worst image or alias over the battery + double worst_ripple_db = 0.0; ///< the largest |gain| deviation over the passband tones + std::size_t tones = 0; + }; + + template + row_measurement measure_row(const row_spec& r, std::size_t prof) { + const auto rates = chain_rates(r.src); + const std::size_t f_in = r.src; + const std::size_t f_out = r.dst; + const double r_min = static_cast(std::min(f_in, f_out)); + const profile p = rational_profile(prof); + const double f_pass = p.passband_hz(r_min); + const double f_stop = r_min - f_pass; + auto c = build_chain(r, prof); + const std::size_t n = analysis_length(rates, 8192); + const std::size_t skip = static_cast(2.0 * c.latency_output_frames().value()) + 64; + const double bin_hz = static_cast(f_out) / static_cast(n); + const auto pass_bin = static_cast(f_pass / bin_hz); + std::set bins; + for (const double frac : {0.1, 0.3, 0.5, 0.7, 0.85, 0.95, 0.99}) { + const auto b = static_cast(frac * f_pass / bin_hz); + if (b >= 1) { + bins.insert(b); + } + } + row_measurement m; + for (const std::size_t b : bins) { + const tone_result t = measure_tone(c, rates, n, b, skip, pass_bin); + m.worst_spur_db = std::max(m.worst_spur_db, t.worst_spur_db); + m.worst_ripple_db = std::max(m.worst_ripple_db, std::fabs(t.gain_db)); + ++m.tones; + } + // Stopband tones, where the input can hold any: from the chain's + // stopband edge to the input's Nyquist. + const double nyq_in = static_cast(f_in) / 2.0; + if (f_stop < nyq_in) { + std::set sbins; + for (const double f : {f_stop * 1.001, f_stop * 1.05, f_stop + 0.25 * (nyq_in - f_stop), + f_stop + 0.5 * (nyq_in - f_stop), nyq_in * 0.98}) { + if (f > f_stop && f < nyq_in) { + sbins.insert(static_cast(std::ceil(f / bin_hz))); + } + } + for (const std::size_t b : sbins) { + const tone_result t = measure_tone(c, rates, n, b, skip, pass_bin); + // A stopband tone must not reach the passband: its own + // folded bin counts as a spur when it lands there. + m.worst_spur_db = std::max(m.worst_spur_db, t.worst_spur_db); + if (b % n <= pass_bin || (n - b % n) <= pass_bin) { + m.worst_spur_db = std::max(m.worst_spur_db, t.gain_db); + } + ++m.tones; + } + } + return m; + } + + // ------------------------------------------------------------------ + // The row runner: what every row is checked for. + struct totals { + std::size_t rows = 0, flagged = 0, within = 0; + std::array worst{}; + }; + + template + void check_row_structure(const row_spec& r, totals& t) { + ++t.rows; + t.flagged += r.flagged ? 1 : 0; + const auto rates = chain_rates(r.src); + ASSERT_EQ(rates.back(), r.dst) << r.src << " -> " << r.dst << ": the chain's ratio is not the pair's"; + ASSERT_EQ(r.divisors.size(), sizeof...(Descs)); + constexpr bool has_bridge = (is_bridge::value || ...); + if constexpr (!has_bridge) { + // A within-family row: the named chain's divisors are the table's. + ++t.within; + constexpr auto divisors = basic_chain::k_divisors; + for (std::size_t i = 0; i < divisors.size(); ++i) { + EXPECT_EQ(divisors[i], r.divisors[i]) << r.src << " -> " << r.dst << " stage " << i; + } + } + else { + // The flag (R14): bridge's output rate >= 2x the chain's. + bool expect_flag = false; + [&](std::index_sequence) { + ((expect_flag |= is_bridge::value && rates[I + 1] >= 2 * r.dst), ...); + }(std::index_sequence_for{}); + EXPECT_EQ(r.flagged, expect_flag) << r.src << " -> " << r.dst; + } + for (std::size_t prof = 0; prof < 4; ++prof) { + auto c = build_chain(r, prof); + EXPECT_EQ((chain_macs(c, rates)), r.macs(prof)) + << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " MACs per output"; + EXPECT_EQ(c.latency_output_frames(), r.latency(prof)) + << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " latency"; + if constexpr (!has_bridge) { + basic_chain named(1, rational_profile(prof)); + EXPECT_EQ(named.macs_per_output_exact(), r.macs(prof)); + EXPECT_EQ(named.latency_output_frames(), c.latency_output_frames()); + } + } + } + + template + void check_row_streaming(const row_spec& r) { + auto c = build_chain(r, 1); + using chain_type = decltype(c); + constexpr std::size_t up = chain_type::k_up; + // Accounting from a few positions, the impulse at the latency, flush. + const std::size_t probe = 2 * up + 3; + std::vector x(c.frames_needed(probe) + 8, S{0.25}); + std::vector y(c.outputs_for(x.size()) + 8); + for (std::size_t pos = 0; pos < 3; ++pos) { + for (std::size_t n = 0; n <= 5; ++n) { + chain_type probe_chain = c; + EXPECT_EQ(probe_chain.process(x.data(), n, y.data()), c.outputs_for(n)) + << r.src << " -> " << r.dst << " pos " << pos << " n " << n; + } + for (std::size_t k = 1; k <= probe; k += up) { + const std::size_t n = c.frames_needed(k); + EXPECT_GE(c.outputs_for(n), k) << r.src << " -> " << r.dst << " k " << k; + EXPECT_LT(c.outputs_for(n - 1), k) << r.src << " -> " << r.dst << " k " << k; + } + c.process(x.data(), 1, y.data()); + } + c.reset(); + const double lat = c.latency_output_frames().value(); + const std::size_t n_in = c.frames_needed(static_cast(2.0 * lat) + 16); + std::vector imp(n_in, S{0}); + imp[0] = S{1}; + std::vector yi(c.outputs_for(n_in)); + c.process(imp.data(), n_in, yi.data()); + std::size_t peak = 0; + for (std::size_t n = 1; n < yi.size(); ++n) { + if (std::fabs(yi[n]) > std::fabs(yi[peak])) { + peak = n; + } + } + EXPECT_LE(std::fabs(static_cast(peak) - lat), 0.5 + 1e-9) + << r.src << " -> " << r.dst << " peak " << peak; + // flush: the stated count, then silence. + c.reset(); + c.process(x.data(), x.size(), y.data()); + const std::size_t expect = c.flush_output_frames(); + std::vector tail(expect + 1, S{1}); + EXPECT_EQ(c.flush(tail.data()), expect) << r.src << " -> " << r.dst; + std::vector z(c.frames_needed(8) + 8, S{0}); + std::vector yz(c.outputs_for(z.size())); + c.process(z.data(), z.size(), yz.data()); + for (const S v : yz) { + EXPECT_EQ(v, S{0}) << r.src << " -> " << r.dst << " after flush"; + } + } + + template + void check_row_spec(const row_spec& r, totals& t, std::size_t prof) { + const row_measurement m = measure_row(r, prof); + const profile p = rational_profile(prof); + // The promise (2.1): every image and alias at or below -A; the + // passband within the sum of the stages' ripple candidates (0.01 + // dB per 70 dB stage, 0.0001 per transparent stage; bridge's own + // are 0.003 / 0.00001, held to the same). + const double delta = static_cast(sizeof...(Descs)) * (p.stopband_atten_db >= 100.0 ? 0.0001 : 0.01); + EXPECT_LE(m.worst_spur_db, -p.stopband_atten_db) + << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " worst spur"; + EXPECT_LE(m.worst_ripple_db, delta) << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " ripple"; + t.worst[prof].worst_spur_db = std::max(t.worst[prof].worst_spur_db, m.worst_spur_db); + t.worst[prof].worst_ripple_db = std::max(t.worst[prof].worst_ripple_db, m.worst_ripple_db); + t.worst[prof].tones += m.tones; + } + +#define TAP_SR_UNPAREN(...) __VA_ARGS__ + + TEST(Matrix, EveryRowPinsItsMacsAndLatencyAndTheNamedChainsDivisors) { + totals t; +#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ + check_row_structure( \ + row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}, t); + TAP_SR_RATIONAL_MATRIX_ROWS(ROW) +#undef ROW + EXPECT_EQ(t.rows, 182u); + EXPECT_EQ(t.rows, k_matrix_rows); + EXPECT_EQ(t.flagged, 38u); + EXPECT_EQ(t.within, 92u); + } + + TEST(Matrix, EveryRowStreamsExactlyAndPeaksAtItsLatency) { +#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ + check_row_streaming(row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}); + TAP_SR_RATIONAL_MATRIX_ROWS(ROW) +#undef ROW + } + + void run_tone_battery(std::size_t prof) { + totals t; +#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ + check_row_spec(row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}, t, prof); + TAP_SR_RATIONAL_MATRIX_ROWS(ROW) +#undef ROW + std::printf("matrix %s: %zu tones over 182 rows, worst spur %.2f dB, worst passband deviation %.5f dB\n", + k_profile_names[prof], t.worst[prof].tones, t.worst[prof].worst_spur_db, + t.worst[prof].worst_ripple_db); + } + + TEST(Matrix, EveryRowMeetsEconomy) { + run_tone_battery(1); + } + TEST(Matrix, EveryRowMeetsTransparent) { + run_tone_battery(3); + } + TEST(Matrix, EveryRowMeetsSuperEconomy) { + run_tone_battery(0); + } + TEST(Matrix, EveryRowMeetsBalanced) { + run_tone_battery(2); + } + +} // namespace diff --git a/rational/tools/coverage/matrix.py b/rational/tools/coverage/matrix.py new file mode 100644 index 0000000..344a475 --- /dev/null +++ b/rational/tools/coverage/matrix.py @@ -0,0 +1,515 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: MIT +# Copyright 2026 Timothy Place and the SampleRateTap contributors +"""The coverage matrix of the rational engine (PLAN.md section 3): one chain +for every ordered pair of the family's fourteen rates, chosen by the plan's +rules over MEASURED stage lengths. + +For every pair the chain declares f_pass = p * r_min (the profile's passband +fraction of the lower of the two rates). Every rational stage of a chain +whose lower rate is r is designed with its passband at f_pass, i.e. at the +fraction p / d of its own lower rate, where the DIVISOR d is the largest +2^a * 3^b at or below r / r_min: exact inside a +family, where r / r_min is such a number, and conservative across (a +passband at or above f_pass, so the stage still meets 2.1(a)); the one +stage that runs BELOW r_min, a 48-family r_min's stage at bridge's rate +(44.1 * 2^k / 48 * 2^k = 147/160), takes that fraction exactly. The stage's +length is the smallest m whose B-th-band Kaiser design meets the stopband +with >= 1 dB margin on the 16384-point grid (design.h's criterion, M2's +search, here in numpy from the same published math), so the d = 1 column +reproduces design.h's pins and the other columns are the relaxed pins +design.h carries per named profile (`relaxations`), which test_design.cpp +verifies against the shipping designer. + +MACs per output are what stage.h executes: each row of the table (an L-phase +row, or a decimating branch) trimmed to its nonzero span, summed over a +superblock, divided by the outputs per superblock; bridge's stage costs its +taps per phase. Latency is the exact rational sum of (N - 1) / 2 at each +stage's composite rate, in the chain's output frames. + +The chain per pair is chosen by the plan's rules (3.1): the MAC-minimal +chain through the 2^a * 3^b lattice over the single-stage vocabulary (no +single 4th-band stage, decision 6; one bridge stage at the lowest k whose +passband admits f_pass, decision 1), then the fewest stages whose rational +stages' MACs are within 10 % of the minimum's around the same bridge stage +(decision 2), then latency; a row whose bridge output rate is >= 2x the +chain's output rate carries the flag (decision 3). + +Usage (from the engine root): + tools/coverage/matrix.py md # section 3's tables (3.3-3.6), markdown + tools/coverage/matrix.py pins # design.h's relaxation tables + tools/coverage/matrix.py rows # writes tests/coverage/matrix_rows.h + tools/coverage/matrix.py diff # chains that differ from PLAN.md's tables + tools/coverage/matrix.py rule # rows where the fewest-stages rule overrode the minimum +""" +import collections +import functools +import heapq +import pathlib +import re +import sys +from fractions import Fraction as F + +import numpy as np + +ROOT = pathlib.Path(__file__).resolve().parents[2] + +A_RATES = [8000, 12000, 16000, 24000, 32000, 48000, 96000, 192000, 384000] +B_RATES = [11025, 22050, 44100, 88200, 176400] +RATES = A_RATES + B_RATES +PROFILES = collections.OrderedDict([ # name: (A dB, p = f_pass / r_min) + ("super_economy", (70.0, F(1, 3))), + ("economy", (70.0, F(3, 8))), + ("balanced", (70.0, F(19, 48))), + ("transparent", (120.0, F(5, 12))), +]) +# bridge's pinned taps per phase (bridge/design.h): (down 48 -> 44.1, up, passband Hz at k = 0). +BRIDGE = {"super_economy": (40, 28, 16000), "economy": (58, 38, 18000), + "balanced": (78, 44, 19000), "transparent": (184, 96, 20000)} +BRIDGE_L = {"down": 147, "up": 160} # output phases: 48 -> 44.1 is L = 147, M = 160 +BRIDGE_M = {"down": 160, "up": 147} +ALLOWED = {1, 2, 3, 4, 6, 8} +NO_SINGLE_STAGE = {4} # decision 6: by 4 is two half-bands; the 4th-band design serves 4/3 and 3/4 +BANDS = [2, 3, 4, 6, 8] +FEWER_STAGES_TOL = F(1, 10) +FLAG_RATIO = 2 +NMAX = 8 +GRID = 16384 +MARGIN_DB = 1.0 +MAX_M = 128 + + +# --- the design (mirrors tap::dsp::design_nyquist and design.h's search) ---- + +def kaiser_beta(atten_db): + if atten_db > 50.0: + return 0.1102 * (atten_db - 8.7) + if atten_db > 21.0: + return 0.5842 * (atten_db - 21.0) ** 0.4 + 0.07886 * (atten_db - 21.0) + return 0.0 + + +def design_nyquist(band, m, beta): + n = 2 * m * band - 1 + c = (n - 1) // 2 + i = np.arange(n) + u = (i - c) / c + w = np.i0(beta * np.sqrt(np.maximum(0.0, 1.0 - u * u))) / np.i0(beta) + h = np.sinc((i - c) / band) / band * w + h[c] = 1.0 + h[(i != c) & ((i - c) % band == 0)] = 0.0 + for j in range(band): + if j == c % band: + continue + sel = (i % band) == j + h[sel] /= h[sel].sum() + return h + + +def worst_stopband_db(h, band, p, grid=GRID): + f_stop = (1.0 - p) / band + f = f_stop + (0.5 - f_stop) * np.arange(grid) / (grid - 1) + m = np.arange(len(h)) + worst = -1e9 + for k in range(0, grid, 512): + e = np.exp(-2j * np.pi * f[k:k + 512, None] * m[None, :]) + worst = max(worst, 20 * np.log10(np.maximum(np.abs(e @ h) / band, 1e-300)).max()) + return worst + + +@functools.lru_cache(maxsize=None) +def search_m(band, p, atten): + """The smallest m meeting the stopband with the margin on the grid (design.h's search).""" + beta = kaiser_beta(atten) + for m in range(1, MAX_M + 1): + if worst_stopband_db(design_nyquist(band, m, beta), band, float(p)) <= -(atten + MARGIN_DB): + return m + raise RuntimeError(f"no length meets band {band} p {p} A {atten}") + + +LATTICE = sorted({2 ** a * 3 ** b for a in range(0, 12) for b in range(0, 4)}) + + +def divisor(lower, rmin): + """The design divisor of a stage whose lower rate is `lower` in a chain + whose lowest rate is rmin: the largest lattice number at or below + lower / rmin (a relaxation: exact within a family, conservative across), + or lower / rmin itself when that is below 1 — the stage runs at bridge's + rate under a 48-family r_min (44.1 * 2^k over 48 * 2^k, always 147/160), + a tightening the pins carry as that one fraction.""" + q = F(lower, rmin) + if q < 1: + return q + return F(max(d for d in LATTICE if d <= q)) + + +# --- one stage, costed as stage.h builds it ------------------------------- + +def span(flags): + idx = [i for i, f in enumerate(flags) if f] + return idx[-1] - idx[0] + 1 if idx else 0 + + +@functools.lru_cache(maxsize=None) +def stage_macs(L, M, m): + """MACs per output of a stage at L/M with taps per branch m: the trimmed + rows of stage.h's table, summed over a superblock, per output.""" + B = max(L, M) + N = 2 * m * B - 1 + c = m * B - 1 + nz = [(i == c) or ((i - c) % B != 0) for i in range(N)] + if L == 1: # the M-branch commutator: branch j holds taps j + s M + return F(sum(span(nz[j::M]) for j in range(M))) + return F(sum(span(nz[p::L]) for p in range(L)), L) # L-phase rows: taps p + t L + + +def stage(x, y, prof, rmin): + """A rational stage from rate x to rate y under the profile, in the chain + whose lowest rate is rmin: (MACs per output, latency in output frames, + label, N, divisor, m) or None when the ratio is not one stage of the + vocabulary.""" + fr = F(y, x) + L, M = fr.numerator, fr.denominator + if L not in ALLOWED or M not in ALLOWED: + return None + if (L == 1 or M == 1) and max(L, M) in NO_SINGLE_STAGE: + return None + A, p = PROFILES[prof] + d = divisor(min(x, y), rmin) + m = search_m(max(L, M), p / d, A) + N = 2 * m * max(L, M) - 1 + lat = F(N - 1, 2 * M) # (N - 1) / 2 at the composite rate, in output frames + label = f"↑{L}" if M == 1 else f"↓{M}" if L == 1 else f"{L}/{M}" + return stage_macs(L, M, m), lat, label, N, d, m + + +def bridge(x, y, prof, rmin): + td, tu, pb = BRIDGE[prof] + A, p = PROFILES[prof] + fpass = p * rmin + for k in range(3): + lo44, hi48 = 44100 * 2 ** k, 48000 * 2 ** k + if (x, y) == (hi48, lo44) and fpass <= pb * 2 ** k: + return F(td), F(td * BRIDGE_L["down"] - 1, 2 * BRIDGE_M["down"]), f"bridge↓k{k}", k + if (x, y) == (lo44, hi48) and fpass <= pb * 2 ** k: + return F(tu), F(tu * BRIDGE_L["up"] - 1, 2 * BRIDGE_M["up"]), f"bridge↑k{k}", k + return None + + +# --- the search ------------------------------------------------------------- + +def lattice_rates(lo, hi): + out = set() + # Intermediate rates: the family's base times 2^a 3^b (b <= 3), the same + # relative lattice in both families, so a ratio's chain is the same chain + # in either (36 kHz and 33.075 kHz are both r_min * 3 for a 12 / 11.025 + # kHz r_min; neither is a supported rate, both are stage boundaries). + for base in (1000, 11025): + for a in range(0, 12): + for b in range(0, 4): + r = base * 2 ** a * 3 ** b + if lo <= r <= hi: + out.add(r) + return out + + +def chains_by_stage_count(src, dst, prof, only_bridge=None): + """Dijkstra over (node, stage count): the MAC-minimal chain to dst for + every stage count n <= NMAX. {n: (macs/out, latency, n, chain)}; a chain + is a tuple of (from, to, label, kind, N, divisor, m).""" + rmin = min(src, dst) + lo, hi = rmin, max(src, dst) + nodes = lattice_rates(lo, max(hi, 48000)) | {src, dst} + for k in range(3): + for r in (44100 * 2 ** k, 48000 * 2 ** k): + if lo * 0.9 <= r <= hi * 1.1: + nodes.add(r) + edges = {} + for u in nodes: + for v in nodes: + if v == u: + continue + b = bridge(u, v, prof, rmin) + if b: + macs, lat, label, k = b + if only_bridge is not None and (u, v, label) != only_bridge: + continue + edges.setdefault(u, []).append((v, macs * v, lat, (u, v, label, "bridge", 0, 0, 0))) + else: + s = stage(u, v, prof, rmin) + if not s: + continue + macs, lat, label, N, d, m = s + edges.setdefault(u, []).append((v, macs * v, lat, (u, v, label, "stage", N, d, m))) + pq = [(F(0), 0, F(0), src, ())] + seen = {} + while pq: + macs, n, lat, u, chain = heapq.heappop(pq) + if (u, n) in seen: + continue + seen[(u, n)] = (macs, n, lat, chain) + if n >= NMAX: + continue + for v, cost, l, e in edges.get(u, ()): + if (v, n + 1) in seen: + continue + # latency of the edge in the CHAIN's output frames: scale by dst / v + heapq.heappush(pq, (macs + cost, n + 1, lat + l * F(dst, v), v, chain + (e,))) + out = {} + for n in range(1, NMAX + 1): + r = seen.get((dst, n)) + if r: + out[n] = (r[0] / dst, r[2], n, r[3]) + return out + + +def rational_macs(r, dst): + macs = r[0] + for u, v, label, kind, N, d, m in r[3]: + if kind == "bridge": + macs -= F(BRIDGE_COST[(label, u, v)]) * F(v, dst) + return macs + + +BRIDGE_COST = {} + + +def best_chain(src, dst, prof): + """The MAC-minimal chain, then the fewest-stages rule (decision 2). + Returns (chosen, minimal).""" + per_n = chains_by_stage_count(src, dst, prof) + if not per_n: + return None, None + minimal = min(per_n.values(), key=lambda r: (r[0], r[2], r[1])) + br = [e for e in minimal[3] if e[3] == "bridge"] + for e in minimal[3]: + if e[3] == "bridge": + BRIDGE_COST[(e[2], e[0], e[1])] = bridge(e[0], e[1], prof, min(src, dst))[0] + if br: + per_n = chains_by_stage_count(src, dst, prof, only_bridge=(br[0][0], br[0][1], br[0][2])) + for r in per_n.values(): + for e in r[3]: + if e[3] == "bridge": + BRIDGE_COST[(e[2], e[0], e[1])] = bridge(e[0], e[1], prof, min(src, dst))[0] + chosen = minimal + for n in sorted(per_n): + if n >= minimal[2]: + break + if rational_macs(per_n[n], dst) <= rational_macs(minimal, dst) * (1 + FEWER_STAGES_TOL): + chosen = per_n[n] + break + return chosen, minimal + + +def cost_chain(chain, src, dst, prof): + """The economy chain's cost under another profile: (macs/out, latency).""" + rmin = min(src, dst) + macs = F(0) + lat = F(0) + stages = [] + for u, v, label, kind, N, d, m in chain: + if kind == "bridge": + b = bridge(u, v, prof, rmin) + assert b, (chain, prof, "bridge passband excludes f_pass") + mo, l = b[0], b[1] + stages.append((u, v, label, kind, 0, 0, 0)) + else: + s = stage(u, v, prof, rmin) + assert s, (chain, prof) + mo, l, _, N, d, m = s + stages.append((u, v, label, kind, N, d, m)) + macs += mo * v + lat += l * F(dst, v) + return macs / dst, lat, tuple(stages) + + +def flagged(chain, dst): + return any(kind == "bridge" and v >= FLAG_RATIO * dst for u, v, label, kind, N, d, m in chain) + + +def shape(chain): + return " · ".join(e[2] for e in chain) + + +def fmt(x): + return f"{x / 1000:g}" + + +@functools.lru_cache(maxsize=None) +def economy_chains(): + """The chain per ordered pair at economy (the table's chains): {(src, dst): (chosen, minimal)}.""" + return {(s, d): best_chain(s, d, "economy") for s in RATES for d in RATES if s != d} + + +def ratio_key(kv): + (L, M), _ = kv + q = L / M + return (q < 1, q if q >= 1 else 1 / q) + + +def within_groups(rates): + groups = collections.OrderedDict() + for s in rates: + for d in rates: + if s == d: + continue + fr = F(d, s) + groups.setdefault((fr.numerator, fr.denominator), []).append((s, d)) + return sorted(groups.items(), key=ratio_key) + + +def g(x): + return f"{float(x):g}" + + +def md(): + allc = economy_chains() + for rates, title in ((A_RATES, "within 48"), (B_RATES, "within 44.1")): + print(f"### {title}") + print("| Ratio L/M | Pairs (from → to, kHz) | Chain | MAC/out eco | Latency eco (out) | MAC/out tr | Latency tr (out) |") + print("|---|---|---|---|---|---|---|") + for (L, M), pairs in within_groups(rates): + cells = set() + for s, d in pairs: + r, _ = allc[(s, d)] + mt, lt, _ = cost_chain(r[3], s, d, "transparent") + cells.add((shape(r[3]), r[0], r[1], mt, lt)) + assert len(cells) == 1, (L, M, cells) + sh, me, le, mt, lt = cells.pop() + pl = ", ".join(f"{fmt(s)}→{fmt(d)}" for s, d in pairs) + print(f"| {L}/{M} | {pl} | {sh} | {float(me):.1f} | {float(le):.1f} | {float(mt):.1f} | {float(lt):.1f} |") + print() + nflag = 0 + for name, srcs, dsts in (("48 → 44.1", A_RATES, B_RATES), ("44.1 → 48", B_RATES, A_RATES)): + print(f"### {name}") + print("| From → To | Chain | f_pass eco (kHz) | MAC/out eco | Latency eco (ms) | MAC/out tr | Latency tr (ms) | ⚑ |") + print("|---|---|---|---|---|---|---|---|") + for s in srcs: + for d in dsts: + r, _ = allc[(s, d)] + mt, lt, _ = cost_chain(r[3], s, d, "transparent") + fl = flagged(r[3], d) + nflag += fl + fp = PROFILES["economy"][1] * min(s, d) + print(f"| {fmt(s)} → {fmt(d)} | {shape(r[3])} | {float(fp) / 1000:.3f} | {float(r[0]):.1f} | " + f"{float(r[1]) / d * 1e3:.2f} | {float(mt):.1f} | {float(lt) / d * 1e3:.2f} | {'⚑' if fl else ''} |") + print() + print(f"flagged rows: {nflag}") + + +def rule(): + """Rows (economy) where the fewest-stages rule overrode the MAC minimum.""" + n = 0 + for (s, d), (c, m) in economy_chains().items(): + if c is not m: + n += 1 + rm, rc = rational_macs(m, d), rational_macs(c, d) + print(f"{fmt(s)} → {fmt(d)}: {shape(m[3])} ({float(m[0]):.1f}, {m[2]} stages) → {shape(c[3])} " + f"({float(c[0]):.1f}, {c[2]} stages) rational {float(rm):.1f} → {float(rc):.1f}, +{100 * float(rc / rm - 1):.1f} %") + print("rows where the rule bit:", n) + + +def used_divisors(): + """{(profile, band): sorted divisors} every chain of the matrix uses.""" + used = collections.defaultdict(set) + for (s, d), (c, _) in economy_chains().items(): + for prof in PROFILES: + _, _, stages = cost_chain(c[3], s, d, prof) + for u, v, label, kind, N, dv, m in stages: + if kind == "stage": + fr = F(v, u) + used[prof].add(dv) + return used + + +def pins(): + """design.h's relaxation tables: m per band at every divisor the matrix uses.""" + used = used_divisors() + for prof, (A, p) in PROFILES.items(): + divs = sorted(set().union(*used.values())) + print(f"// {prof}: A = {A:g} dB, p = {p}") + for dv in divs: + ms = [search_m(B, p / dv, A) for B in BANDS] + print(f" {{{{{dv.numerator}, {dv.denominator}}}, {{{', '.join(str(m) for m in ms)}}}}},") + + +def rows_header(): + """tests/coverage/matrix_rows.h: every row as an X-macro over the chain's + stage types, its divisors and the exact (MACs/out, latency) per profile.""" + allc = economy_chains() + out = [] + out.append("// Generated by tools/coverage/matrix.py — DO NOT EDIT.") + out.append("// The coverage matrix (PLAN.md section 3): one chain per ordered pair of") + out.append("// the fourteen rates, with the relaxation divisor of every rational stage") + out.append("// and the exact MACs per output (num, den) and latency in output frames") + out.append("// (num, den) per profile, in the order super_economy, economy, balanced,") + out.append("// transparent. See the generator for the rules and the provenance.") + out.append("// SPDX-License-Identifier: MIT") + out.append("// Copyright 2026 Timothy Place and the SampleRateTap contributors") + out.append("#pragma once") + out.append("") + out.append("// clang-format off") + out.append("// ROW(src_hz, dst_hz, flagged, (stage types...), (divisors...),") + out.append("// (macs_num, macs_den, lat_num, lat_den) x 4 profiles)") + out.append("#define TAP_SR_RATIONAL_MATRIX_ROWS(ROW) \\") + n = 0 + for s in RATES: + for d in RATES: + if s == d: + continue + c, _ = allc[(s, d)] + types = [] + divs = [] + for u, v, label, kind, N, dv, m in c[3]: + if kind == "bridge": + k = int(label[-1]) + types.append(f"bridge_{'down' if '↓' in label else 'up'}<{k}>") + divs.append("d(0, 1)") + else: + fr = F(v, u) + L, M = fr.numerator, fr.denominator + types.append(f"up_{L}" if M == 1 else f"down_{M}" if L == 1 else f"ratio_{L}_{M}") + divs.append(f"d({dv.numerator}, {dv.denominator})") + cells = [] + for prof in PROFILES: + mo, lat, _ = cost_chain(c[3], s, d, prof) + cells.append(f"{mo.numerator}, {mo.denominator}, {lat.numerator}, {lat.denominator}") + out.append(f" ROW({s}, {d}, {'true' if flagged(c[3], d) else 'false'}, ({', '.join(types)}), " + f"({', '.join(divs)}), {', '.join(cells)}) \\") + n += 1 + out[-1] = out[-1].rstrip(" \\") + out.append("// clang-format on") + out.append("") + out.append(f"inline constexpr unsigned k_matrix_rows = {n};") + out.append("") + path = ROOT / "tests" / "coverage" / "matrix_rows.h" + path.write_text("\n".join(out)) + print(f"wrote {path} ({n} rows)") + + +def diff(): + """Chains that differ from the tables PLAN.md carries (section 3).""" + plan = (ROOT / "PLAN.md").read_text() + table = {} + for line in plan.splitlines(): + m = re.match(r"\| (\d+(?:\.\d+)?) → (\d+(?:\.\d+)?) \| (.+?) \|", line) + if m: + table[(int(float(m.group(1)) * 1000), int(float(m.group(2)) * 1000))] = m.group(3).strip() + m = re.match(r"\| (\d+)/(\d+) \| (.+?) \| (.+?) \|", line) + if m: + for pair in m.group(3).split(","): + a, b = pair.strip().split("→") + table[(int(float(a) * 1000), int(float(b) * 1000))] = m.group(4).strip() + n = 0 + for (s, d), (c, _) in economy_chains().items(): + old = table.get((s, d)) + new = shape(c[3]) + if old != new: + n += 1 + print(f"{fmt(s)} → {fmt(d)}: {old} → {new}") + print("changed rows:", n, "of", len(table), "parsed") + + +if __name__ == "__main__": + cmd = sys.argv[1] if len(sys.argv) > 1 else "md" + {"md": md, "pins": pins, "rows": rows_header, "diff": diff, "rule": rule}[cmd]() diff --git a/rational/tools/reference/make_reference_vectors.py b/rational/tools/reference/make_reference_vectors.py index 8f76c46..e440db7 100644 --- a/rational/tools/reference/make_reference_vectors.py +++ b/rational/tools/reference/make_reference_vectors.py @@ -15,6 +15,13 @@ the design; a decimator's is h / M; a mixed ratio going down has each of its L phases normalized to sum 1. Cast to float32. +The chain vectors (M4) apply the stages' upfirdn in sequence, each stage at +the economy profile relaxed by its design divisor (chain.h, PLAN.md 3.1; +the pins of design.h's economy relaxation table), each stage's output cut +to its streaming prefix before the next — exactly what the C++ chain feeds +forward — so the chain leg is independent of chain<>'s bookkeeping. The up +chains run on the first 96 input frames (their outputs are L times longer). + The streaming stage is zero-primed and causal, so its output equals upfirdn's from sample 0 — transient included — within float32 rounding: the C++ stores float32 coefficients and accumulates in double, this reference @@ -86,6 +93,24 @@ def xorshift_f32(count, seed): # The pinned taps per branch (design.h's table): economy and transparent. PINS = {"economy": {2: 11, 3: 11, 4: 11, 6: 12, 8: 12}, "transparent": {2: 31, 3: 25, 4: 25, 6: 25, 8: 25}} ATTEN = {"economy": 70.0, "transparent": 120.0} +# economy's relaxation rows (design.h k_economy_relaxations): the pins of a +# stage whose lower rate is `divisor` times the chain's lowest (M4). +RELAXED_ECONOMY = {1: {2: 11, 3: 11, 4: 11, 6: 12, 8: 12}, 2: {2: 7, 3: 5, 4: 5, 6: 5, 8: 5}, + 3: {2: 7, 3: 5, 4: 5, 6: 5, 8: 5}, 16: {2: 3, 3: 3, 4: 3, 6: 4, 8: 4}} + +# The chain vectors (M4): the stages' upfirdn applied in sequence, each +# stage at the chain's economy profile relaxed by its design divisor +# (chain.h), each stage's output cut to its streaming prefix before the +# next, which is exactly what the C++ chain feeds forward. +CHAINS = [ # tag, input frames (the prefix of k_input), [(L, M, divisor), ...] + ("chain_up_2_up_2", 96, [(2, 1, 1), (2, 1, 2)]), + ("chain_down_3_down_2", 480, [(1, 3, 2), (1, 2, 1)]), + ("chain_ratio_3_4_down_2", 480, [(3, 4, 2), (1, 2, 1)]), + ("chain_up_2_ratio_4_3", 96, [(2, 1, 1), (4, 3, 2)]), + ("chain_up_2_up_8_up_3", 96, [(2, 1, 1), (8, 1, 2), (3, 1, 16)]), + ("chain_down_3_down_8_down_2", 480, [(1, 3, 16), (1, 8, 2), (1, 2, 1)]), + ("chain_ratio_3_8_down_3", 480, [(3, 8, 3), (1, 3, 1)]), +] CASES = [ # tag, L, M, profile ("up_2", 2, 1, "economy"), ("down_2", 1, 2, "economy"), @@ -142,6 +167,25 @@ def fmt(arr, per_line=8): out.append("};") out.append("") + +def streaming_prefix(y, n_in, L, M): + n_stream = 0 + while (n_stream * M) // L < n_in: + n_stream += 1 + return y[:n_stream] + + +for tag, n_in, stages in CHAINS: + y = x[:n_in].astype(np.float64) + for L, M, d in stages: + h = stage_table(L, M, RELAXED_ECONOMY[d][max(L, M)], kaiser_beta(ATTEN["economy"])) + y = streaming_prefix(signal.upfirdn(h, y, up=L, down=M), len(y), L, M) + y = y.astype(np.float32) + out.append(f"inline constexpr std::array k_{tag} = {{") + out.append(fmt(y)) + out.append("};") + out.append("") + out.append("} // namespace rational_ref") out.append("// NOLINTEND(readability-identifier-naming)") diff --git a/submodules/dsptap b/submodules/dsptap index a298c07..d4a61a9 160000 --- a/submodules/dsptap +++ b/submodules/dsptap @@ -1 +1 @@ -Subproject commit a298c0749097d9f4be212301dd5ed1bd46173757 +Subproject commit d4a61a99ac97f780c775bca988de374140cc47f6 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8674efe..b0dee2e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,7 +12,7 @@ set(_header_count_bridge 5) # rational grows to six by its plan's layout (rational/PLAN.md section 4); # re-pinned by the milestone that adds a header (M1: rational.h, ratio.h, # design.h; M3: stage.h, converter.h; M4 adds chain.h). -set(_header_count_rational 5) +set(_header_count_rational 6) set(_engines async bridge rational) foreach(_engine IN LISTS _engines) From 60384d9387530f9dd6f2d8373f3a89596b422c68 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 14:10:42 +0000 Subject: [PATCH 2/6] rational: keep the M33 one-shot inside its budget The relaxation tables' 70 dB search joins the transparent one in the bare-metal exclusions (design math covered on every host; under qemu-system-arm's soft double it alone takes minutes), and the chains' accounting sweep covers one superblock of positions with probes a superblock long instead of two (every phase of every stage is still visited; a quarter of the copies). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- rational/tests/bare_metal_main.cpp | 6 ++++-- rational/tests/test_chain.cpp | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/rational/tests/bare_metal_main.cpp b/rational/tests/bare_metal_main.cpp index f30393a..4a0d803 100644 --- a/rational/tests/bare_metal_main.cpp +++ b/rational/tests/bare_metal_main.cpp @@ -4,8 +4,9 @@ // qemu-system-arm): there is no argv on the target, so the // emulation-appropriate filter is baked in. Excluded are the tests that // sweep the 16384-point design grid over long designs or search it (the -// transparent rows up to N = 399 and the transparent relaxation table up to -// N = 735, the coarse-grid comparison, the unpinned search) and the +// transparent rows up to N = 399 and the relaxation tables, whose searches +// run every candidate length through the grid, up to N = 735, the +// coarse-grid comparison, the unpinned search) and the // coverage-matrix suite (182 chains in double through a tone battery, a // host measurement): target-independent design math and measurement // already covered on every host. @@ -20,6 +21,7 @@ int main() { "Design.TheCoarseGridWouldUnderPinTwoRows:" "Design.UnpinnedProfileIsSearchedOnTheDesignGrid:" "Design.UnmeetableSpecThrows:" + "Design.RelaxationTablesAreTheSearchAt70dB:" "Design.RelaxationTablesAreTheSearchAtTransparent:" "Matrix.*"; ::testing::InitGoogleTest(); diff --git a/rational/tests/test_chain.cpp b/rational/tests/test_chain.cpp index e80e2b5..e46f5e6 100644 --- a/rational/tests/test_chain.cpp +++ b/rational/tests/test_chain.cpp @@ -216,25 +216,29 @@ namespace { // Accounting from every position of the chain's superblock (k_up k_down // inputs): outputs_for(n) is exactly what process(n) writes, and // frames_needed(k) the smallest n with outputs_for(n) >= k. + // One superblock of positions (period inputs), probes up to a + // superblock and two more: every phase of every stage is visited, at a + // cost the M33 leg can carry (period is 48 for the by-48 chains). template void check_accounting_from_every_position() { constexpr std::size_t period = Chain::k_up * Chain::k_down; + constexpr std::size_t probe = period + 2; Chain c(1); - std::vector x((2 * period + 2) * Chain::k_down + 8, 0.25f); + std::vector x((probe + 2) * Chain::k_down + 8, 0.25f); std::vector y(c.outputs_for(x.size()) + 8); - for (std::size_t pos = 0; pos < 2 * period; ++pos) { - for (std::size_t n = 0; n <= 2 * period; ++n) { - Chain probe = c; - EXPECT_EQ(probe.process(x.data(), n, y.data()), c.outputs_for(n)) << "pos " << pos << " n " << n; + for (std::size_t pos = 0; pos < period; ++pos) { + for (std::size_t n = 0; n <= probe; ++n) { + Chain probe_chain = c; + EXPECT_EQ(probe_chain.process(x.data(), n, y.data()), c.outputs_for(n)) << "pos " << pos << " n " << n; } EXPECT_EQ(c.frames_needed(0), 0u); - for (std::size_t k = 1; k <= 2 * period; ++k) { + for (std::size_t k = 1; k <= probe; ++k) { const std::size_t n = c.frames_needed(k); EXPECT_GE(c.outputs_for(n), k) << "pos " << pos << " k " << k; EXPECT_LT(c.outputs_for(n - 1), k) << "pos " << pos << " k " << k; - Chain probe = c; + Chain probe_chain = c; ASSERT_LE(n, x.size()); - EXPECT_GE(probe.process(x.data(), n, y.data()), k) << "pos " << pos << " k " << k; + EXPECT_GE(probe_chain.process(x.data(), n, y.data()), k) << "pos " << pos << " k " << k; } c.process(x.data(), 1, y.data()); } From 3f96c41f035f06ca011cfa1437950cab419c1e7e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 14:18:13 +0000 Subject: [PATCH 3/6] rational: compile the matrix suite out on bare metal test_matrix.cpp is a host measurement (182 chains in double through a tone battery, bridge composed in) and bare_metal_main.cpp already excludes it by name, but its 182 instantiations alone overflow the M55 image's CODE region by 53 KB at link time, so the TU is only part of the hosted and Hexagon builds. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- rational/tests/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rational/tests/CMakeLists.txt b/rational/tests/CMakeLists.txt index cda316b..76957be 100644 --- a/rational/tests/CMakeLists.txt +++ b/rational/tests/CMakeLists.txt @@ -28,10 +28,16 @@ FetchContent_MakeAvailable(googletest) add_executable(tap_sr_rational_tests test_chain.cpp test_design.cpp - test_matrix.cpp test_output_hash.cpp test_ratio.cpp test_stage.cpp) +# The coverage-matrix suite is a host measurement (182 chains in double +# through a tone battery, bridge composed in); on bare metal it is excluded +# by name anyway (bare_metal_main.cpp) and its 182 instantiations alone +# overflow the M55 image's CODE region, so it is compiled out there. +if(NOT TAP_SR_BARE_METAL) + target_sources(tap_sr_rational_tests PRIVATE test_matrix.cpp) +endif() # The tests' own headers (the committed reference vectors, the generated # matrix rows, the bridge adapter) resolve relative to this directory. target_include_directories(tap_sr_rational_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) From 1693dc20546a5afff71548815950faba1c4883d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 14:39:10 +0000 Subject: [PATCH 4/6] rational: the matrix test's rows behind one interface Every row of the coverage matrix is a distinct chain type, and with the measurement and streaming code templated per row the TU took eight minutes to compile on a host (and the Hexagon cross job longer). The rows are now built once into a type-erased handle (process, accounting, flush, latency, MACs, clone), so the tone battery, the streaming checks and the structural pins are written and instantiated once and a row's template code is its factory alone: the same tests, 129 s to compile. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- rational/tests/test_matrix.cpp | 305 +++++++++++++++++++++------------ 1 file changed, 191 insertions(+), 114 deletions(-) diff --git a/rational/tests/test_matrix.cpp b/rational/tests/test_matrix.cpp index fdbf04a..3f2ab04 100644 --- a/rational/tests/test_matrix.cpp +++ b/rational/tests/test_matrix.cpp @@ -20,9 +20,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include #include @@ -119,18 +123,50 @@ namespace { exact_ratio latency(std::size_t prof) const { return exact_ratio{pins[4 * prof + 2], pins[4 * prof + 3]}; } }; - template - using row_chain = tap::dsp::chain::type...>; + // ------------------------------------------------------------------ + // The chains behind one interface: every row is a distinct chain type, + // so the measurement and streaming code is written once against this + // handle and instantiated once, and a row's template code is only the + // factory (182 rows otherwise cost minutes of compile per test + // function). + struct chain_handle { + virtual ~chain_handle() = default; + virtual std::size_t process(const double* in, std::size_t n, double* out) = 0; + virtual std::size_t outputs_for(std::size_t n) const = 0; + virtual std::size_t frames_needed(std::size_t k) const = 0; + virtual std::size_t flush(double* out) = 0; + virtual std::size_t flush_output_frames() const = 0; + virtual void reset() = 0; + virtual exact_ratio latency_output_frames() const = 0; + virtual exact_ratio macs_per_output() const = 0; + virtual std::size_t ratio_up() const = 0; + virtual std::unique_ptr clone() const = 0; + }; - template - row_chain build_chain(const row_spec& r, std::size_t prof, std::size_t channels, - std::index_sequence) { - return row_chain(channels, make_stage(channels, prof, r.divisors[I])...); - } - template - row_chain build_chain(const row_spec& r, std::size_t prof, std::size_t channels = 1) { - return build_chain(r, prof, channels, std::index_sequence_for{}); - } + template + using row_chain = tap::dsp::chain::type...>; + + template + class row_handle final : public chain_handle { + public: + row_handle(row_chain c, exact_ratio macs) + : m_c(std::move(c)) + , m_macs(macs) {} + std::size_t process(const double* in, std::size_t n, double* out) override { return m_c.process(in, n, out); } + std::size_t outputs_for(std::size_t n) const override { return m_c.outputs_for(n); } + std::size_t frames_needed(std::size_t k) const override { return m_c.frames_needed(k); } + std::size_t flush(double* out) override { return m_c.flush(out); } + std::size_t flush_output_frames() const override { return m_c.flush_output_frames(); } + void reset() override { m_c.reset(); } + exact_ratio latency_output_frames() const override { return m_c.latency_output_frames(); } + exact_ratio macs_per_output() const override { return m_macs; } + std::size_t ratio_up() const override { return row_chain::k_up; } + std::unique_ptr clone() const override { return std::make_unique(*this); } + + private: + row_chain m_c; + exact_ratio m_macs; + }; /// The rates along a chain: before each stage, and after the last. template @@ -147,8 +183,8 @@ namespace { /// MACs per output, exact: each stage's superblock count over its /// outputs per superblock, scaled by its output rate over the chain's. - template - exact_ratio chain_macs(const Chain& c, const std::vector& rates) { + template + exact_ratio chain_macs(const row_chain& c, const std::vector& rates) { exact_ratio total{0, 1}; [&](std::index_sequence) { ((total = @@ -161,6 +197,63 @@ namespace { return total; } + template + row_chain build_chain(const row_spec& r, std::size_t prof, std::index_sequence) { + return row_chain(std::size_t{1}, make_stage(1, prof, r.divisors[I])...); + } + + /// A row of the matrix with its chain type erased: the spec, the rates, + /// a factory per profile, and for a within-family row the named chain's + /// compile-time divisors and its own exact numbers. + struct row { + row_spec spec; + std::vector rates; + bool has_bridge = false; + std::vector named_divisors; + std::function(std::size_t prof)> build; + std::function(std::size_t prof)> named_numbers; + std::string name; + }; + + template + row make_row(row_spec spec) { + row r; + r.name = std::to_string(spec.src) + " -> " + std::to_string(spec.dst); + r.rates = chain_rates(spec.src); + r.has_bridge = (is_bridge::value || ...); + r.spec = std::move(spec); + const row_spec& rs = r.spec; + r.build = [rs, rates = r.rates](std::size_t prof) -> std::unique_ptr { + auto c = build_chain(rs, prof, std::index_sequence_for{}); + auto macs = chain_macs(c, rates); + return std::make_unique>(std::move(c), macs); + }; + if constexpr (!(is_bridge::value || ...)) { + constexpr auto divisors = basic_chain::k_divisors; + r.named_divisors.assign(divisors.begin(), divisors.end()); + r.named_numbers = [](std::size_t prof) { + basic_chain named(1, rational_profile(prof)); + return std::pair{named.macs_per_output_exact(), named.latency_output_frames()}; + }; + } + return r; + } + +#define TAP_SR_UNPAREN(...) __VA_ARGS__ + + /// Every row of the table, built once. + const std::vector& matrix_rows() { + static const std::vector k_rows = [] { + std::vector out; +#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ + out.push_back(make_row(row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}})); + TAP_SR_RATIONAL_MATRIX_ROWS(ROW) +#undef ROW + return out; + }(); + return k_rows; + } + // ------------------------------------------------------------------ // The measurement: a tone at input frequency f (an exact bin of the // analysis window) through the chain in double; the amplitude at every @@ -168,18 +261,17 @@ namespace { // folded to the output's Nyquist, over every rate R_j of the chain) // that lies in the chain's passband, at or below f_pass — the promise // (2.1) is the passband; between f_pass and the output's Nyquist the - // output is unspecified — and at the tone's own bin. The analysis window is n output frames - // after the transient, n a multiple of every R_j / f_out's denominator - // so every such frequency is a bin: a rectangular window then measures - // each component exactly, without leakage, which is what resolves - // -120 dB beside a 0 dB tone. + // output is unspecified — and at the tone's own bin. The analysis + // window is n output frames after the transient, n a multiple of every + // R_j / f_out's denominator so every such frequency is a bin: a + // rectangular window then measures each component exactly, without + // leakage, which is what resolves -120 dB beside a 0 dB tone. struct tone_result { double worst_spur_db; double gain_db; }; - template - tone_result measure_tone(Chain& c, const std::vector& rates, std::size_t n, std::size_t bin, + tone_result measure_tone(chain_handle& c, const std::vector& rates, std::size_t n, std::size_t bin, std::size_t skip, std::size_t pass_bin) { const std::size_t f_in = rates.front(); const std::size_t f_out = rates.back(); @@ -194,8 +286,6 @@ namespace { std::vector y(c.outputs_for(n_in)); const std::size_t made = c.process(x.data(), n_in, y.data()); EXPECT_GE(made, skip + n); - // The bins to look at: the tone's own, folded, and every image / - // alias candidate. In units of f_out / n, R_j is an integer. std::set bins; const auto fold = [n](std::size_t b) { b %= n; @@ -244,18 +334,16 @@ namespace { std::size_t tones = 0; }; - template - row_measurement measure_row(const row_spec& r, std::size_t prof) { - const auto rates = chain_rates(r.src); - const std::size_t f_in = r.src; - const std::size_t f_out = r.dst; + row_measurement measure_row(const row& r, std::size_t prof) { + const std::size_t f_in = r.spec.src; + const std::size_t f_out = r.spec.dst; const double r_min = static_cast(std::min(f_in, f_out)); const profile p = rational_profile(prof); const double f_pass = p.passband_hz(r_min); const double f_stop = r_min - f_pass; - auto c = build_chain(r, prof); - const std::size_t n = analysis_length(rates, 8192); - const std::size_t skip = static_cast(2.0 * c.latency_output_frames().value()) + 64; + auto c = r.build(prof); + const std::size_t n = analysis_length(r.rates, 8192); + const std::size_t skip = static_cast(2.0 * c->latency_output_frames().value()) + 64; const double bin_hz = static_cast(f_out) / static_cast(n); const auto pass_bin = static_cast(f_pass / bin_hz); std::set bins; @@ -267,7 +355,7 @@ namespace { } row_measurement m; for (const std::size_t b : bins) { - const tone_result t = measure_tone(c, rates, n, b, skip, pass_bin); + const tone_result t = measure_tone(*c, r.rates, n, b, skip, pass_bin); m.worst_spur_db = std::max(m.worst_spur_db, t.worst_spur_db); m.worst_ripple_db = std::max(m.worst_ripple_db, std::fabs(t.gain_db)); ++m.tones; @@ -284,7 +372,7 @@ namespace { } } for (const std::size_t b : sbins) { - const tone_result t = measure_tone(c, rates, n, b, skip, pass_bin); + const tone_result t = measure_tone(*c, r.rates, n, b, skip, pass_bin); // A stopband tone must not reach the passband: its own // folded bin counts as a spur when it lands there. m.worst_spur_db = std::max(m.worst_spur_db, t.worst_spur_db); @@ -298,127 +386,118 @@ namespace { } // ------------------------------------------------------------------ - // The row runner: what every row is checked for. + // What every row is checked for. struct totals { std::size_t rows = 0, flagged = 0, within = 0; std::array worst{}; }; - template - void check_row_structure(const row_spec& r, totals& t) { + void check_row_structure(const row& r, totals& t) { ++t.rows; - t.flagged += r.flagged ? 1 : 0; - const auto rates = chain_rates(r.src); - ASSERT_EQ(rates.back(), r.dst) << r.src << " -> " << r.dst << ": the chain's ratio is not the pair's"; - ASSERT_EQ(r.divisors.size(), sizeof...(Descs)); - constexpr bool has_bridge = (is_bridge::value || ...); - if constexpr (!has_bridge) { + t.flagged += r.spec.flagged ? 1 : 0; + ASSERT_EQ(r.rates.back(), r.spec.dst) << r.name << ": the chain's ratio is not the pair's"; + ASSERT_EQ(r.spec.divisors.size(), r.rates.size() - 1); + if (!r.has_bridge) { // A within-family row: the named chain's divisors are the table's. ++t.within; - constexpr auto divisors = basic_chain::k_divisors; - for (std::size_t i = 0; i < divisors.size(); ++i) { - EXPECT_EQ(divisors[i], r.divisors[i]) << r.src << " -> " << r.dst << " stage " << i; + ASSERT_EQ(r.named_divisors.size(), r.spec.divisors.size()); + for (std::size_t i = 0; i < r.named_divisors.size(); ++i) { + EXPECT_EQ(r.named_divisors[i], r.spec.divisors[i]) << r.name << " stage " << i; } } else { - // The flag (R14): bridge's output rate >= 2x the chain's. + // The flag (R14): bridge's output rate >= 2x the chain's. A + // bridge stage is the one whose divisor is 0/1 in the table. bool expect_flag = false; - [&](std::index_sequence) { - ((expect_flag |= is_bridge::value && rates[I + 1] >= 2 * r.dst), ...); - }(std::index_sequence_for{}); - EXPECT_EQ(r.flagged, expect_flag) << r.src << " -> " << r.dst; + for (std::size_t i = 0; i < r.spec.divisors.size(); ++i) { + if (r.spec.divisors[i] == exact_ratio{0, 1} && r.rates[i + 1] >= 2 * r.spec.dst) { + expect_flag = true; + } + } + EXPECT_EQ(r.spec.flagged, expect_flag) << r.name; } for (std::size_t prof = 0; prof < 4; ++prof) { - auto c = build_chain(r, prof); - EXPECT_EQ((chain_macs(c, rates)), r.macs(prof)) - << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " MACs per output"; - EXPECT_EQ(c.latency_output_frames(), r.latency(prof)) - << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " latency"; - if constexpr (!has_bridge) { - basic_chain named(1, rational_profile(prof)); - EXPECT_EQ(named.macs_per_output_exact(), r.macs(prof)); - EXPECT_EQ(named.latency_output_frames(), c.latency_output_frames()); + const auto c = r.build(prof); + EXPECT_EQ(c->macs_per_output(), r.spec.macs(prof)) + << r.name << " " << k_profile_names[prof] << " MACs per output"; + EXPECT_EQ(c->latency_output_frames(), r.spec.latency(prof)) + << r.name << " " << k_profile_names[prof] << " latency"; + if (!r.has_bridge) { + const auto [macs, latency] = r.named_numbers(prof); + EXPECT_EQ(macs, r.spec.macs(prof)) << r.name << " named chain"; + EXPECT_EQ(latency, c->latency_output_frames()) << r.name << " named chain"; } } } - template - void check_row_streaming(const row_spec& r) { - auto c = build_chain(r, 1); - using chain_type = decltype(c); - constexpr std::size_t up = chain_type::k_up; + void check_row_streaming(const row& r) { + auto c = r.build(1); + const std::size_t up = c->ratio_up(); // Accounting from a few positions, the impulse at the latency, flush. - const std::size_t probe = 2 * up + 3; - std::vector x(c.frames_needed(probe) + 8, S{0.25}); - std::vector y(c.outputs_for(x.size()) + 8); + const std::size_t probe = 2 * up + 3; + std::vector x(c->frames_needed(probe) + 8, 0.25); + std::vector y(c->outputs_for(x.size()) + 8); for (std::size_t pos = 0; pos < 3; ++pos) { for (std::size_t n = 0; n <= 5; ++n) { - chain_type probe_chain = c; - EXPECT_EQ(probe_chain.process(x.data(), n, y.data()), c.outputs_for(n)) - << r.src << " -> " << r.dst << " pos " << pos << " n " << n; + const auto probe_chain = c->clone(); + EXPECT_EQ(probe_chain->process(x.data(), n, y.data()), c->outputs_for(n)) + << r.name << " pos " << pos << " n " << n; } for (std::size_t k = 1; k <= probe; k += up) { - const std::size_t n = c.frames_needed(k); - EXPECT_GE(c.outputs_for(n), k) << r.src << " -> " << r.dst << " k " << k; - EXPECT_LT(c.outputs_for(n - 1), k) << r.src << " -> " << r.dst << " k " << k; + const std::size_t n = c->frames_needed(k); + EXPECT_GE(c->outputs_for(n), k) << r.name << " k " << k; + EXPECT_LT(c->outputs_for(n - 1), k) << r.name << " k " << k; } - c.process(x.data(), 1, y.data()); + c->process(x.data(), 1, y.data()); } - c.reset(); - const double lat = c.latency_output_frames().value(); - const std::size_t n_in = c.frames_needed(static_cast(2.0 * lat) + 16); - std::vector imp(n_in, S{0}); - imp[0] = S{1}; - std::vector yi(c.outputs_for(n_in)); - c.process(imp.data(), n_in, yi.data()); + c->reset(); + const double lat = c->latency_output_frames().value(); + const std::size_t n_in = c->frames_needed(static_cast(2.0 * lat) + 16); + std::vector imp(n_in, 0.0); + imp[0] = 1.0; + std::vector yi(c->outputs_for(n_in)); + c->process(imp.data(), n_in, yi.data()); std::size_t peak = 0; for (std::size_t n = 1; n < yi.size(); ++n) { if (std::fabs(yi[n]) > std::fabs(yi[peak])) { peak = n; } } - EXPECT_LE(std::fabs(static_cast(peak) - lat), 0.5 + 1e-9) - << r.src << " -> " << r.dst << " peak " << peak; + EXPECT_LE(std::fabs(static_cast(peak) - lat), 0.5 + 1e-9) << r.name << " peak " << peak; // flush: the stated count, then silence. - c.reset(); - c.process(x.data(), x.size(), y.data()); - const std::size_t expect = c.flush_output_frames(); - std::vector tail(expect + 1, S{1}); - EXPECT_EQ(c.flush(tail.data()), expect) << r.src << " -> " << r.dst; - std::vector z(c.frames_needed(8) + 8, S{0}); - std::vector yz(c.outputs_for(z.size())); - c.process(z.data(), z.size(), yz.data()); - for (const S v : yz) { - EXPECT_EQ(v, S{0}) << r.src << " -> " << r.dst << " after flush"; + c->reset(); + c->process(x.data(), x.size(), y.data()); + const std::size_t expect = c->flush_output_frames(); + std::vector tail(expect + 1, 1.0); + EXPECT_EQ(c->flush(tail.data()), expect) << r.name; + std::vector z(c->frames_needed(8) + 8, 0.0); + std::vector yz(c->outputs_for(z.size())); + c->process(z.data(), z.size(), yz.data()); + for (const double v : yz) { + EXPECT_EQ(v, 0.0) << r.name << " after flush"; } } - template - void check_row_spec(const row_spec& r, totals& t, std::size_t prof) { - const row_measurement m = measure_row(r, prof); + void check_row_spec(const row& r, totals& t, std::size_t prof) { + const row_measurement m = measure_row(r, prof); const profile p = rational_profile(prof); // The promise (2.1): every image and alias at or below -A; the // passband within the sum of the stages' ripple candidates (0.01 // dB per 70 dB stage, 0.0001 per transparent stage; bridge's own // are 0.003 / 0.00001, held to the same). - const double delta = static_cast(sizeof...(Descs)) * (p.stopband_atten_db >= 100.0 ? 0.0001 : 0.01); - EXPECT_LE(m.worst_spur_db, -p.stopband_atten_db) - << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " worst spur"; - EXPECT_LE(m.worst_ripple_db, delta) << r.src << " -> " << r.dst << " " << k_profile_names[prof] << " ripple"; + const double delta = static_cast(r.rates.size() - 1) * (p.stopband_atten_db >= 100.0 ? 0.0001 : 0.01); + EXPECT_LE(m.worst_spur_db, -p.stopband_atten_db) << r.name << " " << k_profile_names[prof] << " worst spur"; + EXPECT_LE(m.worst_ripple_db, delta) << r.name << " " << k_profile_names[prof] << " ripple"; t.worst[prof].worst_spur_db = std::max(t.worst[prof].worst_spur_db, m.worst_spur_db); t.worst[prof].worst_ripple_db = std::max(t.worst[prof].worst_ripple_db, m.worst_ripple_db); t.worst[prof].tones += m.tones; } -#define TAP_SR_UNPAREN(...) __VA_ARGS__ - TEST(Matrix, EveryRowPinsItsMacsAndLatencyAndTheNamedChainsDivisors) { totals t; -#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ - check_row_structure( \ - row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}, t); - TAP_SR_RATIONAL_MATRIX_ROWS(ROW) -#undef ROW + for (const row& r : matrix_rows()) { + check_row_structure(r, t); + } EXPECT_EQ(t.rows, 182u); EXPECT_EQ(t.rows, k_matrix_rows); EXPECT_EQ(t.flagged, 38u); @@ -426,18 +505,16 @@ namespace { } TEST(Matrix, EveryRowStreamsExactlyAndPeaksAtItsLatency) { -#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ - check_row_streaming(row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}); - TAP_SR_RATIONAL_MATRIX_ROWS(ROW) -#undef ROW + for (const row& r : matrix_rows()) { + check_row_streaming(r); + } } void run_tone_battery(std::size_t prof) { totals t; -#define ROW(SRC, DST, FLAG, DESCS, DIVS, ...) \ - check_row_spec(row_spec{SRC, DST, FLAG, {TAP_SR_UNPAREN DIVS}, {{__VA_ARGS__}}}, t, prof); - TAP_SR_RATIONAL_MATRIX_ROWS(ROW) -#undef ROW + for (const row& r : matrix_rows()) { + check_row_spec(r, t, prof); + } std::printf("matrix %s: %zu tones over 182 rows, worst spur %.2f dB, worst passband deviation %.5f dB\n", k_profile_names[prof], t.worst[prof].tones, t.worst[prof].worst_spur_db, t.worst[prof].worst_ripple_db); From ad0bccf48fdbaf40acd4f9d8fa6e63a61d3b148e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 15:18:11 +0000 Subject: [PATCH 5/6] rational: Hexagon excludes both relaxation-table searches; DspTap pin on main The 70 dB relaxation-table search (3 s on a host) ran past the Hexagon job's 20-minute budget under qemu-hexagon, where the design grid's double arithmetic runs some hundreds of times slower (the coarse-grid comparison takes 187 s there); both searches are host suites now, as they already were on the Arm legs. The submodule pin moves from tap/DspTap#52's branch commit to its merge on DspTap main (537ab30, the identical tree). Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- submodules/dsptap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/dsptap b/submodules/dsptap index d4a61a9..537ab30 160000 --- a/submodules/dsptap +++ b/submodules/dsptap @@ -1 +1 @@ -Subproject commit d4a61a99ac97f780c775bca988de374140cc47f6 +Subproject commit 537ab30d31d3ab640e5ccccd1d9562b713b8a339 From e1fd8276680eb9fa4cd51d6284f060388fa97d56 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 2 Oct 2026 15:18:25 +0000 Subject: [PATCH 6/6] ci: the Hexagon rational exclusion the previous commit described The regex edit did not apply in ad0bccf (only the submodule pin moved); this is the exclusion of both relaxation-table searches on Hexagon. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_015VR1VC4SDGxHZQQsQvPBaA --- .github/workflows/ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c0c48b9..eb4f197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -183,8 +183,10 @@ jobs: # rational: every EXPECT_THROW test (named *Throw*) is excluded on # Hexagon for the unwind reason above, with the host measurements # (the coverage matrix's tone batteries, 182 chains in double per - # profile, and the transparent relaxation table's search up to - # N = 735); on the Arm legs bare_metal_main.cpp bakes in its own + # profile, and the relaxation tables' searches, which run every + # candidate length through the design grid: the 70 dB tables alone + # exceed the job's budget under qemu-hexagon); on the Arm legs + # bare_metal_main.cpp bakes in its own # filter (the fine-grid sweeps of the long designs and the # matrix suite). - target: hexagon @@ -195,7 +197,7 @@ jobs: jobs: 4 build_type: Release toolchain: cmake/hexagon-linux-musl.cmake - exclude: Throw|Matrix\.EveryRowMeets|RelaxationTablesAreTheSearchAtTransparent + exclude: Throw|Matrix\.EveryRowMeets|RelaxationTablesAreTheSearch - target: m55 title: Cortex-M55 cross engine: rational