diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9912964..eb4f197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,9 +181,14 @@ 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 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 title: Hexagon cross engine: rational @@ -192,7 +197,7 @@ jobs: jobs: 4 build_type: Release toolchain: cmake/hexagon-linux-musl.cmake - exclude: Throw + exclude: Throw|Matrix\.EveryRowMeets|RelaxationTablesAreTheSearch - 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..76957be 100644 --- a/rational/tests/CMakeLists.txt +++ b/rational/tests/CMakeLists.txt @@ -26,15 +26,24 @@ set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) add_executable(tap_sr_rational_tests + test_chain.cpp test_design.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 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}) 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..4a0d803 100644 --- a/rational/tests/bare_metal_main.cpp +++ b/rational/tests/bare_metal_main.cpp @@ -4,8 +4,12 @@ // 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 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. // The 70 dB rows' pins, minimality and structure run on target. #include @@ -16,7 +20,10 @@ int main() { "Design.PinsAreMinimalAndMeasuredAtTransparent:" "Design.TheCoarseGridWouldUnderPinTwoRows:" "Design.UnpinnedProfileIsSearchedOnTheDesignGrid:" - "Design.UnmeetableSpecThrows"; + "Design.UnmeetableSpecThrows:" + "Design.RelaxationTablesAreTheSearchAt70dB:" + "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..e46f5e6 --- /dev/null +++ b/rational/tests/test_chain.cpp @@ -0,0 +1,465 @@ +// 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. + // 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((probe + 2) * Chain::k_down + 8, 0.25f); + std::vector y(c.outputs_for(x.size()) + 8); + 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 <= 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_chain = c; + ASSERT_LE(n, x.size()); + EXPECT_GE(probe_chain.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..3f2ab04 --- /dev/null +++ b/rational/tests/test_matrix.cpp @@ -0,0 +1,536 @@ +// 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 +#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]}; } + }; + + // ------------------------------------------------------------------ + // 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 + 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 + 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 row_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; + } + + 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 + // 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; + }; + + 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(); + 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); + 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; + }; + + 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 = 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; + 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, 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; + } + // 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, 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); + 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; + } + + // ------------------------------------------------------------------ + // What every row is checked for. + struct totals { + std::size_t rows = 0, flagged = 0, within = 0; + std::array worst{}; + }; + + void check_row_structure(const row& r, totals& t) { + ++t.rows; + 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; + 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. A + // bridge stage is the one whose divisor is 0/1 in the table. + bool expect_flag = false; + 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) { + 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"; + } + } + } + + 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, 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) { + 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.name << " k " << k; + EXPECT_LT(c->outputs_for(n - 1), k) << r.name << " 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, 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.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, 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"; + } + } + + 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(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; + } + + TEST(Matrix, EveryRowPinsItsMacsAndLatencyAndTheNamedChainsDivisors) { + totals t; + 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); + EXPECT_EQ(t.within, 92u); + } + + TEST(Matrix, EveryRowStreamsExactlyAndPeaksAtItsLatency) { + for (const row& r : matrix_rows()) { + check_row_streaming(r); + } + } + + void run_tone_battery(std::size_t prof) { + totals t; + 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); + } + + 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..537ab30 160000 --- a/submodules/dsptap +++ b/submodules/dsptap @@ -1 +1 @@ -Subproject commit a298c0749097d9f4be212301dd5ed1bd46173757 +Subproject commit 537ab30d31d3ab640e5ccccd1d9562b713b8a339 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)