Skip to main content

cram_codec/
gpuwav.rs

1//! `tc-gpu` (stream magic `TCG1`): a GPU-decodable, random-access variant of
2//! the wavelet codec whose decode is **integer only**, so a CPU and a GPU
3//! decoder produce the same bytes.
4//!
5//! # Layout
6//!
7//! - The field is cut into independent **bricks** (3D `B³`, 2D `B²` tiles),
8//!   each with its own CDF 9/7 pyramid and its own entropy-coded lanes. A
9//!   header index of brick sizes gives random access: a sub-box decodes only
10//!   the bricks it touches.
11//! - Values live on a fixed-point grid `x ≈ v·2^e` (`v` an `i32`); `e` is
12//!   chosen so `|x|/2^e ≤ 2^23` (the final `i32 → f32` conversion is exact)
13//!   and `|x − mean|/2^e ≤ 2^20` (transform headroom inside `i32`).
14//!
15//! # Decode (integer only)
16//!
17//! 1. **Coefficients.** Each brick is coded in [`lanes`](GpuConfig::lanes)
18//!    independent rANS lanes (32-bit state, 16-bit words, 16-bit
19//!    probabilities). Bands go coarse → fine; inside a band the lines along
20//!    the fastest axis are split into *passes* by the parity of their
21//!    `(y, z)` position (4 passes in 3D: `(0,0) (1,1) (1,0) (0,1)`; 3 in 2D:
22//!    `y≡0 mod 4`, `y≡2 mod 4`, odd `y`). A line conditions only on lines of
23//!    earlier passes, coarser bands, and its own earlier samples, so every
24//!    line of a pass decodes in parallel; lines of a pass are dealt
25//!    round-robin to the lanes. Each lane owns its adaptive binary models
26//!    (the hand model's binarization: significance, adaptive-Golomb unary,
27//!    low bits, sign) — adaptation is per lane, which is what keeps lanes
28//!    independent.
29//! 2. **Prediction.** Per (level, orientation, pass) a 6-tap Q12 predictor
30//!    over `W, WW, Σfaces, Σdiagonals, Σ(±2 lines), parent`, restricted to
31//!    what the pass can see.
32//! 3. **Dequantization** in fixed point: `c = ⌊|q|·m/2^s⌉ − ⌊δ·m/2^(s+9)⌉`
33//!    with a per-band 31-bit step mantissa `m`.
34//! 4. **Inverse CDF 9/7** lifting in `i32` with Q24 constants and
35//!    round-half-up products `⌊(a·k + 2^23)/2^24⌋` (a 32×32→64 multiply).
36//! 5. **Max-abs corrections** `v += k·corr` in the integer domain, then
37//!    `f32 = (v + mean) as f32 · 2^e` — exact, since `|v| < 2^24` and the
38//!    scale is a normal power of two. Cells the grid still pushes past the
39//!    bound are stored as raw `f32` bits.
40
41use crate::bitio::{ByteReader, ByteWriter, DecodeError};
42use crate::codec::ErrorBound;
43use crate::coefcode::{band_class, KB, NCLASS, NE};
44use crate::wavelet::{forward_97, Pyramid, MAX_LEVELS, MAX_LEVELS_1D};
45
46/// Stream magic.
47pub const MAGIC: &[u8; 4] = b"TCG1";
48
49/// Fixed-point shift of the lifting constants.
50pub const QL: u32 = 24;
51
52fn qc(v: f64) -> i32 {
53    (v * (1u64 << QL) as f64).round() as i32
54}
55
56const ALPHA: f64 = -1.586_134_342_059_924;
57const BETA: f64 = -0.052_980_118_572_961;
58const GAMMA: f64 = 0.882_911_075_530_934;
59const DELTA: f64 = 0.443_506_852_043_971;
60const KAPPA: f64 = 1.149_604_398_860_241;
61
62/// Inverse-lifting constants (Q24): `1/κ, κ, −δ, −γ, −β, −α`.
63pub fn lift_consts() -> [i32; 6] {
64    [
65        qc(1.0 / KAPPA),
66        qc(KAPPA),
67        qc(-DELTA),
68        qc(-GAMMA),
69        qc(-BETA),
70        qc(-ALPHA),
71    ]
72}
73
74/// `⌊(a·b + 2^(s−1)) / 2^s⌋`, low 32 bits (the GPU emulates the 64-bit product).
75#[inline]
76pub fn mulsh(a: i32, b: i32, s: u32) -> i32 {
77    ((a as i64 * b as i64 + (1i64 << (s - 1))) >> s) as i32
78}
79
80// ------------------------------------------------------------------ models
81
82/// Two-rate adaptive bit model packed in a `u32` (fast in the low half).
83pub const MINIT: u32 = 0x8000_8000;
84
85#[inline]
86pub(crate) fn mp1(m: u32) -> u32 {
87    (((m & 0xFFFF) + (m >> 16)) >> 1).clamp(32, 65504)
88}
89
90#[inline]
91fn mupd(m: u32, bit: bool) -> u32 {
92    let (mut f, mut s) = (m & 0xFFFF, m >> 16);
93    if bit {
94        f += (65535 - f) >> 4;
95        s += (65535 - s) >> 7;
96    } else {
97        f -= f >> 4;
98        s -= s >> 7;
99    }
100    f | s << 16
101}
102
103const A_TH: [u32; 11] = [0, 1, 2, 3, 5, 7, 10, 14, 20, 30, 48];
104const NA: usize = 12;
105const P_TH: [u32; 3] = [0, 2, 6];
106const NPB: usize = 5;
107
108/// Model offsets inside one lane's state.
109pub const SIG0: usize = 0;
110const NSIG: usize = NCLASS * 2 * NA * NPB;
111/// Magnitude unary models.
112pub const MAG0: usize = SIG0 + NSIG;
113/// Low-bit models.
114pub const LOW0: usize = MAG0 + NCLASS * NE * KB;
115/// Sign models.
116pub const SGN0: usize = LOW0 + NE * 4;
117/// Escape (Elias-gamma unary) models.
118pub const ESC0: usize = SGN0 + NCLASS * 9;
119/// Models per coefficient lane.
120pub const NMODEL: usize = ESC0 + NCLASS * 32;
121/// Models of the outlier lane (count, gap, |k| gamma unaries).
122pub const NOMODEL: usize = 96;
123
124#[inline]
125fn bucket(a: u32, th: &[u32]) -> usize {
126    th.iter().position(|&t| a <= t).unwrap_or(th.len())
127}
128
129// ------------------------------------------------------------------ rANS
130
131/// A symbol sink/source for one brick: binary decisions at a probability, and
132/// raw bit chunks, per lane.
133pub trait Sink {
134    /// Code `bit` with `P(1) = p1/2^16` on `lane`; returns the bit.
135    fn bit(&mut self, lane: usize, bit: bool, p1: u32) -> bool;
136    /// Code the low `n ≤ 16` bits of `v` raw.
137    fn raw(&mut self, lane: usize, v: u32, n: u32) -> u32;
138    /// Observe a modelled decision on model `idx` (prior counting).
139    #[inline]
140    fn note(&mut self, _idx: usize, _bit: bool) {}
141}
142
143/// Encoder sink: records `(cum, freq)` per lane in decode order.
144pub struct RecSink {
145    /// Per-lane symbols.
146    pub syms: Vec<Vec<(u32, u32)>>,
147}
148
149impl Sink for RecSink {
150    #[inline]
151    fn bit(&mut self, lane: usize, bit: bool, p1: u32) -> bool {
152        let f0 = 65536 - p1;
153        self.syms[lane].push(if bit { (f0, p1) } else { (0, f0) });
154        bit
155    }
156    #[inline]
157    fn raw(&mut self, lane: usize, v: u32, n: u32) -> u32 {
158        let v = v & ((1u32 << n) - 1);
159        self.syms[lane].push((v << (16 - n), 1 << (16 - n)));
160        v
161    }
162}
163
164/// rANS-encode one lane (symbols in decode order) into 16-bit words.
165pub fn rans_encode(syms: &[(u32, u32)]) -> Vec<u16> {
166    if syms.is_empty() {
167        return Vec::new();
168    }
169    let mut x: u32 = 1 << 16;
170    let mut out: Vec<u16> = Vec::with_capacity(syms.len() / 8 + 4);
171    for &(cum, f) in syms.iter().rev() {
172        let xmax = f << 16;
173        if x >= xmax {
174            out.push(x as u16);
175            x >>= 16;
176        }
177        x = ((x / f) << 16) + (x % f) + cum;
178    }
179    out.push((x >> 16) as u16);
180    out.push(x as u16);
181    // decode order: state low word, high word, then renorm words
182    out.reverse();
183    out
184}
185
186/// Decoder of one lane.
187#[derive(Clone, Debug)]
188pub struct RansDec<'a> {
189    w: &'a [u16],
190    pos: usize,
191    x: u32,
192}
193
194impl<'a> RansDec<'a> {
195    /// Start decoding `w` (empty lanes are allowed and never read).
196    pub fn new(w: &'a [u16]) -> Self {
197        let x = if w.len() >= 2 {
198            w[0] as u32 | (w[1] as u32) << 16
199        } else {
200            1 << 16
201        };
202        Self { w, pos: 2, x }
203    }
204    #[inline]
205    fn step(&mut self, cum: u32, f: u32) {
206        self.x = f
207            .wrapping_mul(self.x >> 16)
208            .wrapping_add((self.x & 0xFFFF).wrapping_sub(cum));
209        if self.x < 1 << 16 {
210            let v = self.w.get(self.pos).copied().unwrap_or(0) as u32;
211            self.pos += 1;
212            self.x = (self.x << 16) | v;
213        }
214    }
215    /// Decode a bit at `P(1) = p1/2^16`.
216    #[inline]
217    pub fn bit(&mut self, p1: u32) -> bool {
218        let f0 = 65536 - p1;
219        let b = (self.x & 0xFFFF) >= f0;
220        if b {
221            self.step(f0, p1)
222        } else {
223            self.step(0, f0)
224        }
225        b
226    }
227    /// Decode `n ≤ 16` raw bits.
228    #[inline]
229    pub fn raw(&mut self, n: u32) -> u32 {
230        let v = (self.x & 0xFFFF) >> (16 - n);
231        self.step(v << (16 - n), 1 << (16 - n));
232        v
233    }
234    /// Whether more words were consumed than the lane holds.
235    pub fn overrun(&self) -> bool {
236        self.w.len() >= 2 && self.pos > self.w.len()
237    }
238}
239
240/// Decoder sink over a brick's lanes.
241pub struct DecSink<'a> {
242    /// Lanes.
243    pub lanes: Vec<RansDec<'a>>,
244}
245
246impl Sink for DecSink<'_> {
247    #[inline]
248    fn bit(&mut self, lane: usize, _: bool, p1: u32) -> bool {
249        self.lanes[lane].bit(p1)
250    }
251    #[inline]
252    fn raw(&mut self, lane: usize, _: u32, n: u32) -> u32 {
253        self.lanes[lane].raw(n)
254    }
255}
256
257#[inline]
258pub(crate) fn bitm<S: Sink, const A: bool>(
259    s: &mut S,
260    lane: usize,
261    m: &mut [u32],
262    idx: usize,
263    bit: bool,
264) -> bool {
265    let b = s.bit(lane, bit, mp1(m[idx]));
266    if A {
267        m[idx] = mupd(m[idx], b);
268    }
269    s.note(idx, b);
270    b
271}
272
273#[inline]
274pub(crate) fn raw_n<S: Sink>(s: &mut S, lane: usize, v: u32, mut n: u32) -> u32 {
275    let mut out = 0u32;
276    while n > 16 {
277        out = (out << 16) | s.raw(lane, v >> (n - 16), 16);
278        n -= 16;
279    }
280    if n > 0 {
281        out = (out << n) | s.raw(lane, v, n);
282    }
283    out
284}
285
286/// Adaptive Elias-gamma for `v < 2^31`.
287#[inline]
288pub(crate) fn gamma<S: Sink, const A: bool>(
289    s: &mut S,
290    lane: usize,
291    m: &mut [u32],
292    base: usize,
293    v: u32,
294) -> u32 {
295    let w = v.wrapping_add(1);
296    let nbe = 31 - w.max(1).leading_zeros();
297    let mut nb = 0u32;
298    while nb < 31 && bitm::<S, A>(s, lane, m, base + nb as usize, nb < nbe) {
299        nb += 1;
300    }
301    let lo = raw_n(s, lane, w, nb);
302    ((1u32 << nb) | lo).wrapping_sub(1)
303}
304
305// ------------------------------------------------------------------ priors
306
307/// `e^x` from IEEE `+ − × /` only (deterministic on every platform).
308fn exp_det(x: f64) -> f64 {
309    let ln2 = core::f64::consts::LN_2;
310    let k = (x / ln2).round();
311    let r = x - k * ln2;
312    let (mut t, mut s) = (1.0f64, 1.0f64);
313    for i in 1..25 {
314        t = t * r / i as f64;
315        s += t;
316    }
317    s * f64::from_bits(((k as i64 + 1023) as u64) << 52)
318}
319
320/// The 64 prior probabilities `P(1)·2^16` (logistic grid, step 0.28 in logit).
321pub fn prior_table() -> &'static [u32; 64] {
322    use std::sync::OnceLock;
323    static T: OnceLock<[u32; 64]> = OnceLock::new();
324    T.get_or_init(|| {
325        let mut t = [0u32; 64];
326        for (k, v) in t.iter_mut().enumerate() {
327            let l = (k as f64 - 31.5) * 0.28;
328            *v = (65536.0 / (1.0 + exp_det(-l))).round().clamp(32.0, 65504.0) as u32;
329        }
330        t
331    })
332}
333
334/// Counts decisions per model.
335pub(crate) struct CountSink {
336    pub(crate) n: Vec<[u32; 2]>,
337}
338
339impl Sink for CountSink {
340    #[inline]
341    fn bit(&mut self, _: usize, bit: bool, _: u32) -> bool {
342        bit
343    }
344    #[inline]
345    fn raw(&mut self, _: usize, v: u32, n: u32) -> u32 {
346        v & ((1u32 << n) - 1)
347    }
348    #[inline]
349    fn note(&mut self, idx: usize, bit: bool) {
350        self.n[idx][bit as usize] += 1;
351    }
352}
353
354/// Choose priors (index into [`prior_table`]) from pooled counts.
355pub(crate) fn choose_priors(n: &[[u32; 2]], adaptive: bool) -> Vec<Option<u8>> {
356    let th: u32 = std::env::var("TC_PRIOR_MIN")
357        .ok()
358        .and_then(|v| v.parse().ok())
359        .unwrap_or(if adaptive { 16 } else { 4 });
360    let t = prior_table();
361    n.iter()
362        .map(|&[n0, n1]| {
363            if n0 + n1 < th {
364                return None;
365            }
366            let cost = |p: u32| {
367                let p = p as f64 / 65536.0;
368                -(n1 as f64) * p.log2() - (n0 as f64) * (1.0 - p).log2()
369            };
370            (0..64u8).min_by(|&a, &b| cost(t[a as usize]).total_cmp(&cost(t[b as usize])))
371        })
372        .collect()
373}
374
375pub(crate) fn write_priors(w: &mut ByteWriter, pr: &[Option<u8>]) {
376    use crate::abac::{BitModel, Encoder, UintModel};
377    let mut e = Encoder::new();
378    let mut pm = [BitModel::default(); 2];
379    let mut um = UintModel::default();
380    let (mut prev, mut last) = (false, 32i64);
381    for p in pr {
382        e.encode(p.is_some(), &mut pm[prev as usize]);
383        prev = p.is_some();
384        if let Some(k) = p {
385            um.encode(&mut e, crate::bitio::zigzag(*k as i64 - last));
386            last = *k as i64;
387        }
388    }
389    w.block(&e.finish());
390}
391
392pub(crate) fn read_priors(r: &mut ByteReader) -> Result<Vec<u32>, DecodeError> {
393    use crate::abac::{BitModel, Decoder, UintModel};
394    let buf = r.block()?;
395    let mut d = Decoder::new(buf);
396    let mut pm = [BitModel::default(); 2];
397    let mut um = UintModel::default();
398    let (mut prev, mut last) = (false, 32i64);
399    let t = prior_table();
400    let mut init = vec![MINIT; NMODEL];
401    for v in init.iter_mut() {
402        prev = d.decode(&mut pm[prev as usize]);
403        if prev {
404            last += crate::bitio::unzigzag(um.decode(&mut d));
405            let k = last.clamp(0, 63) as usize;
406            *v = t[k] | t[k] << 16;
407        }
408    }
409    if d.overrun() {
410        return Err(DecodeError::Truncated);
411    }
412    Ok(init)
413}
414
415pub(crate) fn init_of(pr: &[Option<u8>]) -> Vec<u32> {
416    let t = prior_table();
417    pr.iter()
418        .map(|p| p.map_or(MINIT, |k| t[k as usize] | t[k as usize] << 16))
419        .collect()
420}
421
422// ------------------------------------------------------------------ shapes
423
424/// Per-band decode parameters.
425#[derive(Clone, Debug)]
426pub struct BandInfo {
427    /// Region start (brick-local).
428    pub start: [usize; 3],
429    /// Region end (exclusive).
430    pub end: [usize; 3],
431    /// Context class.
432    pub class: usize,
433    /// Parent band index.
434    pub parent: Option<usize>,
435    /// Parent-position shift per axis (1 if the parent level splits it).
436    pub pshift: [u32; 3],
437    /// Number of passes.
438    pub npass: usize,
439    /// Predictor key per pass.
440    pub pkey: [usize; 8],
441    /// Dequantization step mantissa.
442    pub m: i32,
443    /// Dequantization step shift.
444    pub sh: u32,
445    /// Synthesis-basis weight.
446    pub w: f64,
447    /// Origin of the subband this region belongs to (parent mapping); equal
448    /// to `start` for whole bands, the band start for a code-block.
449    pub org: [usize; 3],
450    /// Raster order inside the region (one lane, every earlier line
451    /// visible) instead of parity passes.
452    pub raster: bool,
453    /// Axis-grouped value taps: the three line taps (and the learned model's
454    /// line values) sum z-axis / y-axis / diagonal lines instead of face /
455    /// diagonal / far lines.
456    pub ax: bool,
457    /// Lanes this region's lines are dealt to (0 = all of the coder's lanes).
458    pub nl: usize,
459}
460
461/// A brick shape and its pyramid.
462#[derive(Clone, Debug)]
463pub struct Shape {
464    /// Brick extent.
465    pub dims: [usize; 3],
466    /// Pyramid.
467    pub p: Pyramid,
468    /// Bands in coding order.
469    pub bands: Vec<BandInfo>,
470}
471
472pub(crate) fn max_levels(dims: [usize; 3]) -> usize {
473    if dims.iter().filter(|&&d| d > 1).count() <= 1 {
474        MAX_LEVELS_1D
475    } else {
476        MAX_LEVELS
477    }
478}
479
480/// Pass count of a band.
481pub fn npass_of(start: [usize; 3], end: [usize; 3]) -> usize {
482    let (ez, ey) = (end[0] - start[0], end[1] - start[1]);
483    if ez > 1 && ey > 1 {
484        7
485    } else if ez > 1 || ey > 1 {
486        4
487    } else {
488        1
489    }
490}
491
492/// Pass of band-relative line `(zr, yr)`.
493#[inline]
494pub fn pass_of(npass: usize, two_axes: bool, zy_is_y: bool, zr: usize, yr: usize) -> usize {
495    let _ = two_axes;
496    match npass {
497        7 => {
498            if (yr | zr) & 1 == 0 {
499                [0, 2, 3, 1][(yr >> 1 & 1) | (zr >> 1 & 1) << 1]
500            } else {
501                [0, 5, 6, 4][(yr & 1) | (zr & 1) << 1]
502            }
503        }
504        4 => {
505            let t = if zy_is_y { yr } else { zr };
506            if t & 1 == 1 {
507                3
508            } else if t & 3 == 2 {
509                2
510            } else if t & 7 == 4 {
511                1
512            } else {
513                0
514            }
515        }
516        _ => 0,
517    }
518}
519
520#[inline]
521fn band_pass(b: &BandInfo, zr: usize, yr: usize) -> usize {
522    let ey = b.end[1] - b.start[1];
523    pass_of(b.npass, false, ey > 1, zr, yr)
524}
525
526/// Predictor key of `(level, mask, pass)`; the LL band is level 15.
527fn key_id(level: usize, mask: u8, pass: usize, nlev: usize) -> usize {
528    let l = if mask == 0 { 15 } else { level.min(14) };
529    let _ = nlev;
530    (l * 8 + mask as usize) * 8 + pass
531}
532
533/// Number of distinct predictor keys addressable.
534pub const NKEY: usize = 16 * 8 * 8;
535
536impl Shape {
537    pub(crate) fn new(dims: [usize; 3]) -> Self {
538        let p = Pyramid::new(dims, max_levels(dims));
539        let bands = (0..p.bands.len())
540            .map(|bi| {
541                let b = &p.bands[bi];
542                let parent = p.parent_band(bi);
543                let mut pshift = [0; 3];
544                if let Some(pi) = parent {
545                    let pl = p.bands[pi].level;
546                    for a in 0..3 {
547                        pshift[a] = p.split[pl][a] as u32;
548                    }
549                }
550                let npass = npass_of(b.start, b.end);
551                let mut pkey = [0; 8];
552                for (ps, k) in pkey.iter_mut().enumerate().take(npass) {
553                    *k = key_id(b.level, b.mask, ps, p.nlev());
554                }
555                BandInfo {
556                    start: b.start,
557                    end: b.end,
558                    class: band_class(b) as usize,
559                    parent,
560                    pshift,
561                    npass,
562                    pkey,
563                    m: 0,
564                    sh: 1,
565                    w: p.band_weight(b),
566                    org: b.start,
567                    raster: false,
568                    ax: false,
569                    nl: 0,
570                }
571            })
572            .collect();
573        Self { dims, p, bands }
574    }
575
576    pub(crate) fn set_steps(&mut self, delta_g: f64) {
577        for b in self.bands.iter_mut() {
578            let (m, sh) = step_repr(delta_g / b.w);
579            b.m = m;
580            b.sh = sh;
581        }
582    }
583
584    pub(crate) fn len(&self) -> usize {
585        self.dims.iter().product()
586    }
587}
588
589/// `step ≈ m·2^−sh` with `m ∈ [2^29, 2^30]`.
590pub(crate) fn step_repr(step: f64) -> (i32, u32) {
591    let lg = step.log2().floor() as i32;
592    let sh = (29 - lg).clamp(1, 52);
593    let m = (step * 2f64.powi(sh))
594        .round()
595        .clamp(1.0, (1u64 << 31) as f64 - 1.0) as i32;
596    (m, sh as u32)
597}
598
599// ------------------------------------------------------------------ features
600
601/// Row neighbour offsets `(dz, dy, category)`: 0 faces, 1 diagonals, 2 far.
602const ROWNB: [(i32, i32, u8); 20] = [
603    (0, -1, 0),
604    (0, 1, 0),
605    (-1, 0, 0),
606    (1, 0, 0),
607    (-1, -1, 1),
608    (-1, 1, 1),
609    (1, -1, 1),
610    (1, 1, 1),
611    (0, -2, 2),
612    (0, 2, 2),
613    (-2, 0, 2),
614    (2, 0, 2),
615    (-2, -2, 2),
616    (-2, 2, 2),
617    (2, -2, 2),
618    (2, 2, 2),
619    (0, -4, 2),
620    (0, 4, 2),
621    (-4, 0, 2),
622    (4, 0, 2),
623];
624
625/// Number of predictor taps.
626pub const NTAP: usize = 6;
627const TCLAMP: i32 = 1 << 13;
628
629/// Lines (as base indices of `(z, y, start_x)`) visible from a line.
630#[derive(Clone, Copy, Default)]
631pub(crate) struct RowCtx {
632    pub(crate) base: [usize; 20],
633    pub(crate) cat: [u8; 20],
634    /// Axis group of the line: 0 = same y (z-axis), 1 = same z (y-axis), 2 = diagonal.
635    pub(crate) ax: [u8; 20],
636    pub(crate) n: usize,
637}
638
639pub(crate) fn row_ctx(sh: &Shape, b: &BandInfo, p: usize, z: usize, y: usize) -> RowCtx {
640    let [_, ny, nx] = sh.dims;
641    let mut rc = RowCtx::default();
642    for &(dz, dy, cat) in &ROWNB {
643        let (z2, y2) = (z as i32 + dz, y as i32 + dy);
644        if z2 < b.start[0] as i32
645            || z2 >= b.end[0] as i32
646            || y2 < b.start[1] as i32
647            || y2 >= b.end[1] as i32
648        {
649            continue;
650        }
651        let (z2, y2) = (z2 as usize, y2 as usize);
652        if b.raster {
653            if (z2, y2) >= (z, y) {
654                continue;
655            }
656        } else if band_pass(b, z2 - b.start[0], y2 - b.start[1]) >= p {
657            continue;
658        }
659        rc.base[rc.n] = (z2 * ny + y2) * nx;
660        rc.cat[rc.n] = cat;
661        rc.ax[rc.n] = if dy == 0 {
662            0
663        } else if dz == 0 {
664            1
665        } else {
666            2
667        };
668        rc.n += 1;
669    }
670    rc
671}
672
673/// Everything the coder derives for one coefficient.
674#[derive(Clone, Copy, Debug, Default)]
675pub struct Feats {
676    /// Predictor taps.
677    pub taps: [i32; NTAP],
678    /// Significance context.
679    pub sig: usize,
680    /// Magnitude scale.
681    pub e: usize,
682    /// Sign context.
683    pub sctx: usize,
684    /// Raw context quantities for a learned model: W, WW, Σfaces, Σdiag,
685    /// Σfar, parent, prediction (values); |W|, |WW|, Σ|faces|, Σ|diag|,
686    /// Σ|far|, |parent|, Σ|parent nbrs| (context magnitudes); counts of
687    /// visible face/diag/far lines; has-parent.
688    pub raw: [i32; 18],
689}
690
691#[inline]
692pub(crate) fn cap(v: i32) -> u32 {
693    v.unsigned_abs().min(65535)
694}
695
696#[inline]
697#[allow(clippy::too_many_arguments)]
698pub(crate) fn feats(
699    sh: &Shape,
700    b: &BandInfo,
701    rc: &RowCtx,
702    q: &[i32],
703    rs: Option<&[i32]>,
704    z: usize,
705    y: usize,
706    x: usize,
707    w: &[i32; NTAP],
708) -> (Feats, i32) {
709    // magnitudes/signs for contexts come from `m` (the residuals when given)
710    let m = rs.unwrap_or(q);
711    let [_, ny, nx] = sh.dims;
712    let idx = (z * ny + y) * nx + x;
713    let x0 = b.start[2];
714    let wv = if x > x0 { q[idx - 1] } else { 0 };
715    let wwv = if x > x0 + 1 { q[idx - 2] } else { 0 };
716    let wm = if x > x0 { m[idx - 1] } else { 0 };
717    let wwm = if x > x0 + 1 { m[idx - 2] } else { 0 };
718    let (mut sf, mut sd, mut sr) = (0i32, 0i32, 0i32);
719    let (mut af, mut ad, mut ar) = (0u32, 0u32, 0u32);
720    let (mut nf, mut nd, mut nr) = (0u32, 0u32, 0u32);
721    let mut sa = [0i32; 3];
722    for j in 0..rc.n {
723        let v = q[rc.base[j] + x];
724        sa[rc.ax[j] as usize] = sa[rc.ax[j] as usize].wrapping_add(v);
725        let vm = m[rc.base[j] + x];
726        match rc.cat[j] {
727            0 => {
728                sf = sf.wrapping_add(v);
729                af += cap(vm);
730                if b.raster {
731                    // code-blocks: the face lines' x±1 neighbours feed the activity
732                    if x > x0 {
733                        ad += cap(m[rc.base[j] + x - 1]);
734                        nd += 1;
735                    }
736                    if x + 1 < b.end[2] {
737                        ad += cap(m[rc.base[j] + x + 1]);
738                        nd += 1;
739                    }
740                }
741                nf += 1;
742            }
743            1 => {
744                sd = sd.wrapping_add(v);
745                ad += cap(vm);
746                nd += 1;
747            }
748            _ => {
749                sr = sr.wrapping_add(v);
750                ar += cap(vm);
751                nr += 1;
752            }
753        }
754    }
755    let (mut pv, mut pm, mut pnb, mut has_p) = (0i32, 0i32, 0u32, false);
756    if let Some(pi) = b.parent {
757        let pb = &sh.bands[pi];
758        let mut pp = [0usize; 3];
759        let pos = [z, y, x];
760        for a in 0..3 {
761            pp[a] = (pb.start[a] + ((pos[a] - b.org[a]) >> b.pshift[a])).min(pb.end[a] - 1);
762        }
763        let at = |p: [usize; 3]| m[(p[0] * ny + p[1]) * nx + p[2]];
764        pv = q[(pp[0] * ny + pp[1]) * nx + pp[2]];
765        pm = at(pp);
766        has_p = true;
767        for a in 0..3 {
768            if pp[a] > pb.start[a] {
769                let mut t = pp;
770                t[a] -= 1;
771                pnb += cap(at(t));
772            }
773            if pp[a] + 1 < pb.end[a] {
774                let mut t = pp;
775                t[a] += 1;
776                pnb += cap(at(t));
777            }
778        }
779    }
780    let cl = |v: i32| v.clamp(-TCLAMP, TCLAMP);
781    if b.ax {
782        (sf, sd, sr) = (sa[0], sa[1], sa[2]);
783    }
784    let taps = [cl(wv), cl(wwv), cl(sf), cl(sd), cl(sr), cl(pv)];
785    let mut acc = 0i32;
786    for i in 0..NTAP {
787        acc = acc.wrapping_add(w[i].wrapping_mul(taps[i]));
788    }
789    let pred = acc.wrapping_add(2048) >> 12;
790    let s = 2 * cap(wm) + cap(wwm) + 2 * af + ad + ar + 2 * cap(pm) + 2 * cap(pred);
791    let ws = 3 + 2 * nf + nd + nr + 2 * has_p as u32 + 2;
792    let a = 12 * s / ws;
793    let ab = bucket(a, &A_TH);
794    let pb = if has_p {
795        1 + bucket(4 * cap(pm).min(15) + pnb.min(60), &P_TH)
796    } else {
797        0
798    };
799    let g = (nf + nd + nr > 0) as usize;
800    let c = b.class;
801    let e = ((1 + 2 * s / ws).ilog2() as usize).min(NE - 1);
802    let sgn = |v: i32| (v.signum() + 1) as usize;
803    let f = Feats {
804        taps,
805        sig: ((c * 2 + g) * NA + ab) * NPB + pb,
806        e,
807        sctx: c * 9 + sgn(wm) * 3 + sgn(sf.wrapping_add(sd)),
808        raw: [
809            wv,
810            wwv,
811            sf,
812            sd,
813            sr,
814            pv,
815            pred,
816            cap(wm) as i32,
817            cap(wwm) as i32,
818            af as i32,
819            ad as i32,
820            ar as i32,
821            cap(pm) as i32,
822            pnb as i32,
823            nf as i32,
824            nd as i32,
825            nr as i32,
826            has_p as i32,
827        ],
828    };
829    (f, pred)
830}
831
832/// Code one residual.
833#[inline]
834pub(crate) fn code_res<S: Sink, const A: bool>(
835    s: &mut S,
836    lane: usize,
837    m: &mut [u32],
838    c: usize,
839    f: &Feats,
840    r: i32,
841) -> i32 {
842    let mag = r.unsigned_abs();
843    if !bitm::<S, A>(s, lane, m, SIG0 + f.sig, mag > 0) {
844        return 0;
845    }
846    let e = f.e;
847    let k = e.saturating_sub(2) as u32;
848    let rr = mag.wrapping_sub(1);
849    let hi = rr >> k;
850    let mut h = 0usize;
851    while h < KB {
852        if !bitm::<S, A>(s, lane, m, MAG0 + (c * NE + e) * KB + h, hi > h as u32) {
853            break;
854        }
855        h += 1;
856    }
857    let mut hv = h as u32;
858    if h == KB {
859        hv = hv.wrapping_add(gamma::<S, A>(
860            s,
861            lane,
862            m,
863            ESC0 + c * 32,
864            hi.wrapping_sub(KB as u32),
865        ));
866    }
867    let mut lo = 0u32;
868    let mut j = k;
869    while j > 0 {
870        let pos = k - j;
871        if pos < 2 {
872            let idx = LOW0 + (e * 2 + pos as usize) * 2 + (pos == 1 && lo & 1 == 1) as usize;
873            let bit = bitm::<S, A>(s, lane, m, idx, (rr >> (j - 1)) & 1 == 1);
874            lo = (lo << 1) | bit as u32;
875            j -= 1;
876        } else {
877            let got = raw_n(s, lane, rr & ((1u32 << j) - 1), j);
878            lo = (lo << j) | got;
879            j = 0;
880        }
881    }
882    let mv = ((hv << k) | lo).wrapping_add(1);
883    let neg = bitm::<S, A>(s, lane, m, SGN0 + f.sctx, r < 0);
884    if neg {
885        (mv as i32).wrapping_neg()
886    } else {
887        mv as i32
888    }
889}
890
891/// Lines of pass `p` form a lattice `z ≡ rz (mod sz)`, `y ≡ ry (mod sy)`
892/// (band-relative): returns `(rz, sz, ry, sy)`.
893pub fn pass_lattice(npass: usize, zy_is_y: bool, p: usize) -> (usize, usize, usize, usize) {
894    match npass {
895        7 => [
896            (0, 4, 0, 4),
897            (2, 4, 2, 4),
898            (0, 4, 2, 4),
899            (2, 4, 0, 4),
900            (1, 2, 1, 2),
901            (0, 2, 1, 2),
902            (1, 2, 0, 2),
903        ][p],
904        4 => {
905            let (r, st) = [(0, 8), (4, 8), (2, 4), (1, 2)][p];
906            if zy_is_y {
907                (0, 1, r, st)
908            } else {
909                (r, st, 0, 1)
910            }
911        }
912        _ => (0, 1, 0, 1),
913    }
914}
915
916/// Visit the lines of `band` in coding order: `f(pass, lane, z, y)` — per
917/// pass, the pass's lines in raster order, dealt round-robin to the lanes.
918pub(crate) fn for_lines(b: &BandInfo, lanes: usize, mut f: impl FnMut(usize, usize, usize, usize)) {
919    if b.raster {
920        for z in b.start[0]..b.end[0] {
921            for y in b.start[1]..b.end[1] {
922                f(0, 0, z, y);
923            }
924        }
925        return;
926    }
927    let lanes = if b.nl > 0 { b.nl.min(lanes) } else { lanes };
928    let (ez, ey) = (b.end[0] - b.start[0], b.end[1] - b.start[1]);
929    for p in 0..b.npass {
930        let (rz, sz, ry, sy) = pass_lattice(b.npass, ey > 1, p);
931        if rz >= ez || ry >= ey {
932            continue;
933        }
934        let cy = (ey - ry).div_ceil(sy);
935        let n = (ez - rz).div_ceil(sz) * cy;
936        for j in 0..n {
937            f(
938                p,
939                j % lanes,
940                b.start[0] + rz + (j / cy) * sz,
941                b.start[1] + ry + (j % cy) * sy,
942            );
943        }
944    }
945}
946
947/// Code (encode or decode) a brick's coefficients `q`; `adapt` selects
948/// per-lane adaptive models (else the priors are static probabilities).
949pub fn code_brick<S: Sink>(
950    sh: &Shape,
951    q: &mut [i32],
952    preds: &[[i32; NTAP]],
953    init: &[u32],
954    lanes: usize,
955    adapt: bool,
956    s: &mut S,
957) {
958    if adapt {
959        code_brick_impl::<S, true>(sh, q, preds, init, lanes, s)
960    } else {
961        code_brick_impl::<S, false>(sh, q, preds, init, lanes, s)
962    }
963}
964
965fn code_brick_impl<S: Sink, const A: bool>(
966    sh: &Shape,
967    q: &mut [i32],
968    preds: &[[i32; NTAP]],
969    init: &[u32],
970    lanes: usize,
971    s: &mut S,
972) {
973    let [_, ny, nx] = sh.dims;
974    let mut models: Vec<u32> = (0..lanes).flat_map(|_| init.iter().copied()).collect();
975    for b in &sh.bands {
976        for_lines(b, lanes, |p, lane, z, y| {
977            let rc = row_ctx(sh, b, p, z, y);
978            let w = &preds[b.pkey[p]];
979            let m = &mut models[lane * NMODEL..(lane + 1) * NMODEL];
980            for x in b.start[2]..b.end[2] {
981                let (f, pred) = feats(sh, b, &rc, q, None, z, y, x, w);
982                let i = (z * ny + y) * nx + x;
983                let r = code_res::<S, A>(s, lane, m, b.class, &f, q[i].wrapping_sub(pred));
984                q[i] = r.wrapping_add(pred);
985            }
986        });
987    }
988}
989
990// ------------------------------------------------------------------ integer transform
991
992pub(crate) fn inv97_line(x: &mut [i32], tmp: &mut Vec<i32>, k: &[i32; 6]) {
993    let n = x.len();
994    if n < 2 {
995        return;
996    }
997    let ns = n.div_ceil(2);
998    tmp.clear();
999    tmp.extend_from_slice(x);
1000    let (s, d) = tmp.split_at_mut(ns);
1001    let nd = d.len();
1002    for v in s.iter_mut() {
1003        *v = mulsh(*v, k[0], QL);
1004    }
1005    for v in d.iter_mut() {
1006        *v = mulsh(*v, k[1], QL);
1007    }
1008    let lift_s = |s: &mut [i32], d: &[i32], c: i32| {
1009        for i in 0..ns {
1010            let l = if i == 0 { d[0] } else { d[i - 1] };
1011            let r = if i < nd { d[i] } else { d[nd - 1] };
1012            s[i] = s[i].wrapping_add(mulsh(l.wrapping_add(r), c, QL));
1013        }
1014    };
1015    let lift_d = |s: &[i32], d: &mut [i32], c: i32| {
1016        for i in 0..nd {
1017            let r = if i + 1 < ns { s[i + 1] } else { s[ns - 1] };
1018            d[i] = d[i].wrapping_add(mulsh(s[i].wrapping_add(r), c, QL));
1019        }
1020    };
1021    lift_s(s, d, k[2]);
1022    lift_d(s, d, k[3]);
1023    lift_s(s, d, k[4]);
1024    lift_d(s, d, k[5]);
1025    for i in 0..ns {
1026        x[2 * i] = s[i];
1027    }
1028    for i in 0..nd {
1029        x[2 * i + 1] = d[i];
1030    }
1031}
1032
1033/// Inverse multi-level integer CDF 9/7 on a brick.
1034pub fn inverse_int(p: &Pyramid, x: &mut [i32]) {
1035    let k = lift_consts();
1036    let mut line = Vec::new();
1037    let mut tmp = Vec::new();
1038    let st = [p.dims[1] * p.dims[2], p.dims[2], 1];
1039    for l in (0..p.nlev()).rev() {
1040        for a in [2usize, 1, 0] {
1041            if !p.split[l][a] {
1042                continue;
1043            }
1044            let ext = p.lens[l];
1045            let o: Vec<usize> = (0..3).filter(|&b| b != a).collect();
1046            for i in 0..ext[o[0]] {
1047                for j in 0..ext[o[1]] {
1048                    let off = i * st[o[0]] + j * st[o[1]];
1049                    line.clear();
1050                    line.extend((0..ext[a]).map(|t| x[off + t * st[a]]));
1051                    inv97_line(&mut line, &mut tmp, &k);
1052                    for (t, &v) in line.iter().enumerate() {
1053                        x[off + t * st[a]] = v;
1054                    }
1055                }
1056            }
1057        }
1058    }
1059}
1060
1061/// Dequantize one value of band `b`.
1062#[inline]
1063pub fn dequant(v: i32, b: &BandInfo, dl: &[u8; NDL]) -> i32 {
1064    if v == 0 {
1065        return 0;
1066    }
1067    let a = v.unsigned_abs().min(i32::MAX as u32) as i32;
1068    let d = dl[b.class * 3 + (v.unsigned_abs().min(3) as usize) - 1] as i32;
1069    let c = mulsh(a, b.m, b.sh).wrapping_sub(mulsh(d, b.m, b.sh + 9));
1070    if v < 0 {
1071        c.wrapping_neg()
1072    } else {
1073        c
1074    }
1075}
1076
1077/// Number of centroid offsets.
1078pub const NDL: usize = NCLASS * 3;
1079
1080/// Largest magnitude allowed into the final `i32 → f32` conversion.
1081pub const VMAX: i32 = (1 << 24) - 1;
1082
1083/// `f32` bits of fixed-point `v` at scale `2^e` (exact: `|v| < 2^24`).
1084#[inline]
1085pub fn to_f32_bits(v: i32, scale: f32) -> u32 {
1086    ((v.clamp(-VMAX, VMAX) as f32) * scale).to_bits()
1087}
1088
1089// ------------------------------------------------------------------ stream
1090
1091/// tc-gpu options.
1092#[derive(Clone, Debug)]
1093pub struct GpuConfig {
1094    /// Error target.
1095    pub bound: ErrorBound,
1096    /// Brick edge (3D `B³`, 2D `B²`, 1D `B²` samples).
1097    pub brick: usize,
1098    /// rANS lanes per brick.
1099    pub lanes: usize,
1100    /// Deadzone rounding offset.
1101    pub rho: f64,
1102    /// Per-lane adaptive context models (CPU decode only); the default is
1103    /// static per-context probabilities (the transmitted priors), which the
1104    /// GPU decoder requires.
1105    pub adaptive: bool,
1106}
1107
1108impl GpuConfig {
1109    /// Defaults (64³ bricks, 32 lanes).
1110    pub fn new(bound: ErrorBound) -> Self {
1111        Self {
1112            bound,
1113            brick: 64,
1114            lanes: 32,
1115            rho: 0.45,
1116            adaptive: false,
1117        }
1118    }
1119}
1120
1121/// Encoder statistics.
1122#[derive(Clone, Debug, Default)]
1123pub struct GpuStats {
1124    /// Total bytes.
1125    pub bytes: usize,
1126    /// Bits per value.
1127    pub bits_per_value: f64,
1128    /// Header bytes (incl. brick index and predictors).
1129    pub header_bytes: usize,
1130    /// Lane-size index bytes inside bricks.
1131    pub index_bytes: usize,
1132    /// Outlier-lane + escape bytes.
1133    pub outlier_bytes: usize,
1134    /// Corrected cells.
1135    pub n_outliers: usize,
1136    /// Raw escapes.
1137    pub n_escapes: usize,
1138    /// Measured RMSE.
1139    pub rmse: f64,
1140    /// Measured max-abs error.
1141    pub max_abs: f64,
1142    /// Step (absolute units).
1143    pub delta: f64,
1144}
1145
1146/// A brick of a parsed stream.
1147#[derive(Clone, Debug)]
1148pub struct BrickRef {
1149    /// Origin in the field.
1150    pub origin: [usize; 3],
1151    /// Shape index.
1152    pub shape: usize,
1153    /// Byte offset of the brick payload.
1154    pub off: usize,
1155    /// Word offsets (absolute, in 16-bit words of the stream) of the lanes
1156    /// (coefficient lanes, then the outlier lane), and one past the end.
1157    pub lane_word: Vec<usize>,
1158    /// Raw escapes `(local index, f32 bits)`.
1159    pub esc: Vec<(usize, u32)>,
1160}
1161
1162/// A parsed stream header + brick index.
1163#[derive(Clone, Debug)]
1164pub struct Parsed {
1165    /// Original dims.
1166    pub dims: Vec<usize>,
1167    /// 3D dims.
1168    pub d3: [usize; 3],
1169    /// Brick extent.
1170    pub bdims: [usize; 3],
1171    /// Lanes per brick.
1172    pub lanes: usize,
1173    /// Adaptive (vs static) context models.
1174    pub adaptive: bool,
1175    /// Grid exponent.
1176    pub e: i32,
1177    /// Mean on the grid.
1178    pub mean_fix: i32,
1179    /// Step in grid units.
1180    pub delta_g: f64,
1181    /// Centroid offsets.
1182    pub dl: [u8; NDL],
1183    /// Max-abs correction step on the grid (0 = none).
1184    pub corr: i32,
1185    /// Predictor weights per key.
1186    pub preds: Vec<[i32; NTAP]>,
1187    /// Initial model state of every coefficient lane.
1188    pub init: Vec<u32>,
1189    /// Shapes.
1190    pub shapes: Vec<Shape>,
1191    /// Bricks.
1192    pub bricks: Vec<BrickRef>,
1193}
1194
1195impl Parsed {
1196    /// Output scale `2^e`.
1197    pub fn scale(&self) -> f32 {
1198        f32::from_bits(((self.e + 127) as u32) << 23)
1199    }
1200}
1201
1202fn brick_grid(d3: [usize; 3], bd: [usize; 3]) -> Vec<([usize; 3], [usize; 3])> {
1203    let mut v = Vec::new();
1204    for z in (0..d3[0]).step_by(bd[0]) {
1205        for y in (0..d3[1]).step_by(bd[1]) {
1206            for x in (0..d3[2]).step_by(bd[2]) {
1207                let o = [z, y, x];
1208                v.push((o, [0, 1, 2].map(|a| bd[a].min(d3[a] - o[a]))));
1209            }
1210        }
1211    }
1212    v
1213}
1214
1215fn brick_dims(d3: [usize; 3], b: usize) -> [usize; 3] {
1216    let nd = d3.iter().filter(|&&d| d > 1).count();
1217    match nd {
1218        3 => [b.min(d3[0]), b.min(d3[1]), b.min(d3[2])],
1219        2 => [1, b.min(d3[1]), b.min(d3[2])],
1220        _ => [1, 1, (b * b).min(d3[2])],
1221    }
1222}
1223
1224/// Distinct shapes and per-brick shape index.
1225fn shapes_for(grid: &[([usize; 3], [usize; 3])]) -> (Vec<Shape>, Vec<usize>) {
1226    let mut shapes: Vec<Shape> = Vec::new();
1227    let ids = grid
1228        .iter()
1229        .map(|(_, s)| {
1230            if let Some(i) = shapes.iter().position(|sh| sh.dims == *s) {
1231                i
1232            } else {
1233                shapes.push(Shape::new(*s));
1234                shapes.len() - 1
1235            }
1236        })
1237        .collect();
1238    (shapes, ids)
1239}
1240
1241/// Keys used by any shape, sorted.
1242fn used_keys(shapes: &[Shape]) -> Vec<usize> {
1243    let mut k: Vec<usize> = shapes
1244        .iter()
1245        .flat_map(|s| s.bands.iter().flat_map(|b| b.pkey[..b.npass].to_vec()))
1246        .collect();
1247    k.sort_unstable();
1248    k.dedup();
1249    k
1250}
1251
1252/// Parse a stream's header and brick index.
1253pub fn parse(bytes: &[u8]) -> Result<Parsed, DecodeError> {
1254    let mut r = ByteReader::new(bytes);
1255    if r.take(4)? != MAGIC {
1256        return Err(DecodeError::Invalid("not a tc-gpu stream"));
1257    }
1258    let nd = r.varint()? as usize;
1259    if nd == 0 || nd > 3 {
1260        return Err(DecodeError::Invalid("rank"));
1261    }
1262    let dims: Vec<usize> = (0..nd)
1263        .map(|_| r.varint().map(|v| v as usize))
1264        .collect::<Result<_, _>>()?;
1265    let d3 = crate::wavelet::as3(&dims);
1266    let mut bdims = [0usize; 3];
1267    for v in bdims.iter_mut() {
1268        *v = r.varint()? as usize;
1269        if *v == 0 {
1270            return Err(DecodeError::Invalid("brick"));
1271        }
1272    }
1273    let lanes = r.varint()? as usize;
1274    if lanes == 0 || lanes > 1024 {
1275        return Err(DecodeError::Invalid("lanes"));
1276    }
1277    let adaptive = r.u8()? != 0;
1278    let e = r.svarint()? as i32;
1279    let mean_fix = r.svarint()? as i32;
1280    let delta_g = r.f64()?;
1281    let mut dl = [0u8; NDL];
1282    dl.copy_from_slice(r.take(NDL)?);
1283    let corr = r.varint()? as i32;
1284    let grid = brick_grid(d3, bdims);
1285    let (mut shapes, ids) = shapes_for(&grid);
1286    for s in shapes.iter_mut() {
1287        s.set_steps(delta_g);
1288    }
1289    let mut preds = vec![[0i32; NTAP]; NKEY];
1290    for k in used_keys(&shapes) {
1291        if r.u8()? != 0 {
1292            for t in preds[k].iter_mut() {
1293                *t = r.svarint()? as i32;
1294            }
1295        }
1296    }
1297    let init = read_priors(&mut r)?;
1298    let sizes: Vec<usize> = (0..grid.len())
1299        .map(|_| r.varint().map(|v| v as usize))
1300        .collect::<Result<_, _>>()?;
1301    let mut off = r.position().next_multiple_of(2);
1302    let mut bricks = Vec::with_capacity(grid.len());
1303    for (bi, (o, _)) in grid.iter().enumerate() {
1304        let end = off + sizes[bi];
1305        if end > bytes.len() {
1306            return Err(DecodeError::Truncated);
1307        }
1308        let mut br = ByteReader::new(&bytes[off..end]);
1309        let nw: Vec<usize> = (0..=lanes)
1310            .map(|_| br.varint().map(|v| v as usize))
1311            .collect::<Result<_, _>>()?;
1312        let ne = br.varint()? as usize;
1313        let mut esc = Vec::with_capacity(ne);
1314        let mut last = 0usize;
1315        for i in 0..ne {
1316            let g = br.varint()? as usize;
1317            let idx = if i == 0 { g } else { last + g };
1318            last = idx;
1319            let b = br.take(4)?;
1320            esc.push((idx, u32::from_le_bytes([b[0], b[1], b[2], b[3]])));
1321        }
1322        let mut w = (off + br.position().next_multiple_of(2)) / 2;
1323        let mut lane_word = Vec::with_capacity(lanes + 2);
1324        for &n in &nw {
1325            lane_word.push(w);
1326            w += n;
1327        }
1328        lane_word.push(w);
1329        if w * 2 > end {
1330            return Err(DecodeError::Truncated);
1331        }
1332        bricks.push(BrickRef {
1333            origin: *o,
1334            shape: ids[bi],
1335            off,
1336            lane_word,
1337            esc,
1338        });
1339        off = end;
1340    }
1341    Ok(Parsed {
1342        dims,
1343        d3,
1344        bdims,
1345        lanes,
1346        adaptive,
1347        e,
1348        mean_fix,
1349        delta_g,
1350        dl,
1351        corr,
1352        preds,
1353        init,
1354        shapes,
1355        bricks,
1356    })
1357}
1358
1359fn words(bytes: &[u8], a: usize, b: usize) -> Vec<u16> {
1360    (a..b)
1361        .map(|i| u16::from_le_bytes([bytes[2 * i], bytes[2 * i + 1]]))
1362        .collect()
1363}
1364
1365/// Decode one brick to fixed-point values after corrections (brick-local
1366/// layout) — the integer reference pipeline.
1367pub fn decode_brick_fix(ps: &Parsed, bytes: &[u8], bi: usize) -> Result<Vec<i32>, DecodeError> {
1368    let br = &ps.bricks[bi];
1369    let sh = &ps.shapes[br.shape];
1370    let lw: Vec<Vec<u16>> = (0..=ps.lanes)
1371        .map(|l| words(bytes, br.lane_word[l], br.lane_word[l + 1]))
1372        .collect();
1373    let mut sink = DecSink {
1374        lanes: lw.iter().map(|w| RansDec::new(w)).collect(),
1375    };
1376    let mut q = vec![0i32; sh.len()];
1377    code_brick(
1378        sh,
1379        &mut q,
1380        &ps.preds,
1381        &ps.init,
1382        ps.lanes,
1383        ps.adaptive,
1384        &mut sink,
1385    );
1386    let mut c = dequant_brick(sh, &q, &ps.dl);
1387    inverse_int(&sh.p, &mut c);
1388    // outlier lane
1389    let ol = ps.lanes;
1390    let mut om = vec![MINIT; NOMODEL];
1391    let n = gamma::<_, true>(&mut sink, ol, &mut om, 0, 0) as usize;
1392    let mut last: i64 = -1;
1393    for _ in 0..n {
1394        let g = gamma::<_, true>(&mut sink, ol, &mut om, 32, 0) as i64;
1395        let idx = (last + 1 + g) as usize;
1396        last = idx as i64;
1397        let neg = sink.raw(ol, 0, 1) == 1;
1398        let k = gamma::<_, true>(&mut sink, ol, &mut om, 64, 0).wrapping_add(1) as i32;
1399        let k = if neg { -k } else { k };
1400        if idx < c.len() {
1401            c[idx] = c[idx].wrapping_add(k.wrapping_mul(ps.corr));
1402        }
1403    }
1404    if sink.lanes.iter().any(|l| l.overrun()) {
1405        return Err(DecodeError::Truncated);
1406    }
1407    Ok(c)
1408}
1409
1410pub(crate) fn dequant_brick(sh: &Shape, q: &[i32], dl: &[u8; NDL]) -> Vec<i32> {
1411    let [_, ny, nx] = sh.dims;
1412    let mut c = vec![0i32; q.len()];
1413    for b in &sh.bands {
1414        for z in b.start[0]..b.end[0] {
1415            for y in b.start[1]..b.end[1] {
1416                for x in b.start[2]..b.end[2] {
1417                    let i = (z * ny + y) * nx + x;
1418                    c[i] = dequant(q[i], b, dl);
1419                }
1420            }
1421        }
1422    }
1423    c
1424}
1425
1426/// Write a decoded brick into the output (f32 bits), cropping to `[lo, hi)`
1427/// of the field; `out` has the extent of the box.
1428fn emit_brick(ps: &Parsed, bi: usize, c: &[i32], lo: [usize; 3], hi: [usize; 3], out: &mut [u32]) {
1429    let br = &ps.bricks[bi];
1430    let sh = &ps.shapes[br.shape];
1431    let scale = ps.scale();
1432    let bx = [0, 1, 2].map(|a| hi[a] - lo[a]);
1433    let mut esc = br.esc.iter().peekable();
1434    let [bz, by, bxx] = sh.dims;
1435    for z in 0..bz {
1436        for y in 0..by {
1437            for x in 0..bxx {
1438                let i = (z * by + y) * bxx + x;
1439                let mut bits = to_f32_bits(c[i].wrapping_add(ps.mean_fix), scale);
1440                while let Some(&&(ei, eb)) = esc.peek() {
1441                    if ei < i {
1442                        esc.next();
1443                    } else {
1444                        if ei == i {
1445                            bits = eb;
1446                        }
1447                        break;
1448                    }
1449                }
1450                let g = [br.origin[0] + z, br.origin[1] + y, br.origin[2] + x];
1451                if (0..3).all(|a| g[a] >= lo[a] && g[a] < hi[a]) {
1452                    out[((g[0] - lo[0]) * bx[1] + (g[1] - lo[1])) * bx[2] + (g[2] - lo[2])] = bits;
1453                }
1454            }
1455        }
1456    }
1457}
1458
1459fn intersects(ps: &Parsed, bi: usize, lo: [usize; 3], hi: [usize; 3]) -> bool {
1460    let br = &ps.bricks[bi];
1461    let d = ps.shapes[br.shape].dims;
1462    (0..3).all(|a| br.origin[a] < hi[a] && br.origin[a] + d[a] > lo[a])
1463}
1464
1465/// Decode the sub-box `[lo, hi)` (3D coordinates) with `threads` threads;
1466/// returns `f32` bits in box layout.
1467pub fn decode_box_bits(
1468    bytes: &[u8],
1469    lo: [usize; 3],
1470    hi: [usize; 3],
1471    threads: usize,
1472) -> Result<Vec<u32>, DecodeError> {
1473    let ps = parse(bytes)?;
1474    decode_box_parsed(&ps, bytes, lo, hi, threads)
1475}
1476
1477/// [`decode_box_bits`] on an already parsed stream.
1478pub fn decode_box_parsed(
1479    ps: &Parsed,
1480    bytes: &[u8],
1481    lo: [usize; 3],
1482    hi: [usize; 3],
1483    threads: usize,
1484) -> Result<Vec<u32>, DecodeError> {
1485    for a in 0..3 {
1486        if lo[a] >= hi[a] || hi[a] > ps.d3[a] {
1487            return Err(DecodeError::Invalid("box"));
1488        }
1489    }
1490    let n: usize = (0..3).map(|a| hi[a] - lo[a]).product();
1491    let todo: Vec<usize> = (0..ps.bricks.len())
1492        .filter(|&b| intersects(ps, b, lo, hi))
1493        .collect();
1494    let mut out = vec![0u32; n];
1495    if threads <= 1 || todo.len() <= 1 {
1496        for &bi in &todo {
1497            let c = decode_brick_fix(ps, bytes, bi)?;
1498            emit_brick(ps, bi, &c, lo, hi, &mut out);
1499        }
1500        return Ok(out);
1501    }
1502    use std::sync::atomic::{AtomicUsize, Ordering};
1503    use std::sync::Mutex;
1504    let next = AtomicUsize::new(0);
1505    let err = Mutex::new(None);
1506    let outp = SendPtr(out.as_mut_ptr());
1507    std::thread::scope(|s| {
1508        for _ in 0..threads.min(todo.len()) {
1509            let (next, err, todo) = (&next, &err, &todo);
1510            let outp = &outp;
1511            s.spawn(move || loop {
1512                let t = next.fetch_add(1, Ordering::Relaxed);
1513                if t >= todo.len() {
1514                    break;
1515                }
1516                let bi = todo[t];
1517                match decode_brick_fix(ps, bytes, bi) {
1518                    Ok(c) => {
1519                        // bricks write disjoint cells
1520                        let o = unsafe { std::slice::from_raw_parts_mut(outp.0, n) };
1521                        emit_brick(ps, bi, &c, lo, hi, o);
1522                    }
1523                    Err(e) => *err.lock().unwrap() = Some(e),
1524                }
1525            });
1526        }
1527    });
1528    if let Some(e) = err.into_inner().unwrap() {
1529        return Err(e);
1530    }
1531    Ok(out)
1532}
1533
1534struct SendPtr(*mut u32);
1535unsafe impl Sync for SendPtr {}
1536unsafe impl Send for SendPtr {}
1537
1538/// Decode a whole stream to `f32` with `threads` threads.
1539pub fn decode_f32(bytes: &[u8], threads: usize) -> Result<(Vec<usize>, Vec<f32>), DecodeError> {
1540    let ps = parse(bytes)?;
1541    let bits = decode_box_parsed(&ps, bytes, [0; 3], ps.d3, threads)?;
1542    Ok((
1543        ps.dims.clone(),
1544        bits.into_iter().map(f32::from_bits).collect(),
1545    ))
1546}
1547
1548// ------------------------------------------------------------------ encoder
1549
1550pub(crate) struct EncBrick {
1551    pub(crate) shape: usize,
1552    pub(crate) origin: [usize; 3],
1553    /// Float coefficients (grid units), brick layout.
1554    pub(crate) c: Vec<f64>,
1555}
1556
1557pub(crate) struct Enc<'a> {
1558    pub(crate) x: &'a [f32],
1559    pub(crate) d3: [usize; 3],
1560    pub(crate) bdims: [usize; 3],
1561    pub(crate) shapes: Vec<Shape>,
1562    pub(crate) bricks: Vec<EncBrick>,
1563    pub(crate) e: i32,
1564    pub(crate) mean_fix: i32,
1565    pub(crate) threads: usize,
1566}
1567
1568pub(crate) fn par_map<T: Send + Sync, R: Send>(
1569    items: &[T],
1570    threads: usize,
1571    f: impl Fn(&T) -> R + Sync,
1572) -> Vec<R> {
1573    if threads <= 1 || items.len() <= 1 {
1574        return items.iter().map(f).collect();
1575    }
1576    use std::sync::atomic::{AtomicUsize, Ordering};
1577    use std::sync::Mutex;
1578    let next = AtomicUsize::new(0);
1579    let res: Mutex<Vec<Option<R>>> = Mutex::new((0..items.len()).map(|_| None).collect());
1580    std::thread::scope(|s| {
1581        for _ in 0..threads.min(items.len()) {
1582            s.spawn(|| loop {
1583                let i = next.fetch_add(1, Ordering::Relaxed);
1584                if i >= items.len() {
1585                    break;
1586                }
1587                let r = f(&items[i]);
1588                res.lock().unwrap()[i] = Some(r);
1589            });
1590        }
1591    });
1592    res.into_inner()
1593        .unwrap()
1594        .into_iter()
1595        .map(|r| r.unwrap())
1596        .collect()
1597}
1598
1599impl<'a> Enc<'a> {
1600    pub(crate) fn new(x: &'a [f32], dims: &[usize], brick: usize, threads: usize) -> Self {
1601        let d3 = crate::wavelet::as3(dims);
1602        let bdims = brick_dims(d3, brick);
1603        let grid = brick_grid(d3, bdims);
1604        let (shapes, ids) = shapes_for(&grid);
1605        let n = x.len() as f64;
1606        let mean = x.iter().map(|&v| v as f64).sum::<f64>() / n;
1607        let amax = x.iter().fold(0.0f64, |m, &v| m.max((v as f64).abs()));
1608        let dmax = x
1609            .iter()
1610            .fold(0.0f64, |m, &v| m.max((v as f64 - mean).abs()));
1611        let need = |v: f64, lim: f64| {
1612            if v > 0.0 {
1613                (v / lim).log2().ceil() as i32
1614            } else {
1615                -126
1616            }
1617        };
1618        let e = need(amax, (1u32 << 23) as f64)
1619            .max(need(dmax, (1u32 << 20) as f64))
1620            .clamp(-126, 100);
1621        let sc = 2f64.powi(-e);
1622        let mean_fix = (mean * sc).round() as i32;
1623        type Item = (usize, ([usize; 3], [usize; 3]));
1624        let items: Vec<Item> = ids.iter().copied().zip(grid.iter().copied()).collect();
1625        let bricks = par_map(&items, threads, |&(sid, (o, bd))| {
1626            let sh = &shapes[sid];
1627            let mut c = vec![0.0f64; bd.iter().product()];
1628            for z in 0..bd[0] {
1629                for y in 0..bd[1] {
1630                    for xx in 0..bd[2] {
1631                        let g = ((o[0] + z) * d3[1] + o[1] + y) * d3[2] + o[2] + xx;
1632                        c[(z * bd[1] + y) * bd[2] + xx] = x[g] as f64 * sc - mean_fix as f64;
1633                    }
1634                }
1635            }
1636            forward_97(&sh.p, &mut c);
1637            EncBrick {
1638                shape: sid,
1639                origin: o,
1640                c,
1641            }
1642        });
1643        Self {
1644            x,
1645            d3,
1646            bdims,
1647            shapes,
1648            bricks,
1649            e,
1650            mean_fix,
1651            threads,
1652        }
1653    }
1654
1655    pub(crate) fn scale(&self) -> f32 {
1656        f32::from_bits(((self.e + 127) as u32) << 23)
1657    }
1658
1659    /// Quantize all bricks at grid step `dg`; returns q per brick + centroids.
1660    pub(crate) fn quantize(&self, dg: f64, rho: f64) -> (Vec<Vec<i32>>, [u8; NDL]) {
1661        let parts = par_map(&self.bricks, self.threads, |b| {
1662            let sh = &self.shapes[b.shape];
1663            let [_, ny, nx] = sh.dims;
1664            let mut q = vec![0i32; b.c.len()];
1665            let mut sum = [0.0f64; NDL];
1666            let mut cnt = [0u64; NDL];
1667            for bd in &sh.bands {
1668                let inv = bd.w / dg;
1669                for z in bd.start[0]..bd.end[0] {
1670                    for y in bd.start[1]..bd.end[1] {
1671                        for x in bd.start[2]..bd.end[2] {
1672                            let i = (z * ny + y) * nx + x;
1673                            let a = b.c[i].abs() * inv;
1674                            let m = (a + rho).floor();
1675                            if m >= 1.0 {
1676                                let m = m.min(1.0e9);
1677                                let j = bd.class * 3 + (m as usize).min(3) - 1;
1678                                sum[j] += m - a;
1679                                cnt[j] += 1;
1680                                q[i] = if b.c[i] < 0.0 { -(m as i32) } else { m as i32 };
1681                            }
1682                        }
1683                    }
1684                }
1685            }
1686            (q, sum, cnt)
1687        });
1688        let mut sum = [0.0f64; NDL];
1689        let mut cnt = [0u64; NDL];
1690        for (_, s, c) in &parts {
1691            for j in 0..NDL {
1692                sum[j] += s[j];
1693                cnt[j] += c[j];
1694            }
1695        }
1696        let mut d = [0u8; NDL];
1697        for j in 0..NDL {
1698            if cnt[j] > 0 {
1699                d[j] = ((sum[j] / cnt[j] as f64).clamp(0.0, 0.498) * 512.0).round() as u8;
1700            }
1701        }
1702        (parts.into_iter().map(|p| p.0).collect(), d)
1703    }
1704
1705    /// Integer reconstruction per brick (before corrections).
1706    pub(crate) fn reconstruct(
1707        &self,
1708        shapes: &[Shape],
1709        qs: &[Vec<i32>],
1710        dl: &[u8; NDL],
1711    ) -> Vec<Vec<i32>> {
1712        let idx: Vec<usize> = (0..self.bricks.len()).collect();
1713        par_map(&idx, self.threads, |&bi| {
1714            let sh = &shapes[self.bricks[bi].shape];
1715            let mut c = dequant_brick(sh, &qs[bi], dl);
1716            inverse_int(&sh.p, &mut c);
1717            c
1718        })
1719    }
1720
1721    pub(crate) fn global_index(&self, bi: usize, i: usize) -> usize {
1722        let b = &self.bricks[bi];
1723        let d = self.shapes[b.shape].dims;
1724        let (z, y, x) = (i / (d[1] * d[2]), (i / d[2]) % d[1], i % d[2]);
1725        ((b.origin[0] + z) * self.d3[1] + b.origin[1] + y) * self.d3[2] + b.origin[2] + x
1726    }
1727
1728    /// Squared error and max error of fixed-point reconstruction `cs`.
1729    pub(crate) fn errors(&self, cs: &[Vec<i32>]) -> (f64, f64) {
1730        let scale = self.scale();
1731        let idx: Vec<usize> = (0..cs.len()).collect();
1732        let parts = par_map(&idx, self.threads, |&bi| {
1733            let mut se = 0.0;
1734            let mut mx = 0.0f64;
1735            for (i, &v) in cs[bi].iter().enumerate() {
1736                let y = f32::from_bits(to_f32_bits(v.wrapping_add(self.mean_fix), scale)) as f64;
1737                let e = self.x[self.global_index(bi, i)] as f64 - y;
1738                se += e * e;
1739                mx = mx.max(e.abs());
1740            }
1741            (se, mx)
1742        });
1743        parts
1744            .iter()
1745            .fold((0.0, 0.0), |(a, b), &(s, m)| (a + s, b.max(m)))
1746    }
1747
1748    pub(crate) fn shapes_at(&self, dg: f64) -> Vec<Shape> {
1749        let mut s = self.shapes.clone();
1750        for sh in s.iter_mut() {
1751            sh.set_steps(dg);
1752        }
1753        s
1754    }
1755
1756    pub(crate) fn trial_rmse(&self, dg: f64, rho: f64) -> f64 {
1757        let shapes = self.shapes_at(dg);
1758        let (q, d) = self.quantize(dg, rho);
1759        let cs = self.reconstruct(&shapes, &q, &d);
1760        (self.errors(&cs).0 / self.x.len() as f64).sqrt()
1761    }
1762}
1763
1764/// Fit predictors per key on the quantized values (pooled over bricks).
1765fn fit_preds(
1766    shapes: &[Shape],
1767    bricks: &[EncBrick],
1768    qs: &[Vec<i32>],
1769    lanes: usize,
1770) -> Vec<Option<[i32; NTAP]>> {
1771    let mut g = vec![[[0.0f64; NTAP]; NTAP]; NKEY];
1772    let mut h = vec![[0.0f64; NTAP]; NKEY];
1773    let mut n = vec![0usize; NKEY];
1774    let zero = [0i32; NTAP];
1775    let mut obs: Vec<Vec<([i32; NTAP], i32)>> = vec![Vec::new(); NKEY];
1776    for (bi, b) in bricks.iter().enumerate() {
1777        let sh = &shapes[b.shape];
1778        let q = &qs[bi];
1779        let [_, ny, nx] = sh.dims;
1780        for bd in &sh.bands {
1781            for_lines(bd, lanes, |p, _, z, y| {
1782                let rc = row_ctx(sh, bd, p, z, y);
1783                let k = bd.pkey[p];
1784                for x in bd.start[2]..bd.end[2] {
1785                    let (f, _) = feats(sh, bd, &rc, q, None, z, y, x, &zero);
1786                    let v = q[(z * ny + y) * nx + x];
1787                    if v == 0 && f.taps.iter().all(|&t| t == 0) {
1788                        continue;
1789                    }
1790                    n[k] += 1;
1791                    for i in 0..NTAP {
1792                        h[k][i] += f.taps[i] as f64 * v as f64;
1793                        for j in 0..NTAP {
1794                            g[k][i][j] += f.taps[i] as f64 * f.taps[j] as f64;
1795                        }
1796                    }
1797                    if obs[k].len() < 200_000 {
1798                        obs[k].push((f.taps, v));
1799                    }
1800                }
1801            });
1802        }
1803    }
1804    (0..NKEY)
1805        .map(|k| {
1806            if n[k] < 64 {
1807                return None;
1808            }
1809            let mut a = g[k];
1810            let mut r = h[k];
1811            for i in 0..NTAP {
1812                a[i][i] += 1e-3 * (g[k][i][i] + 1.0);
1813            }
1814            for c in 0..NTAP {
1815                let piv = a[c][c];
1816                if piv.abs() < 1e-12 {
1817                    continue;
1818                }
1819                for rr in 0..NTAP {
1820                    if rr != c {
1821                        let f = a[rr][c] / piv;
1822                        for kk in 0..NTAP {
1823                            a[rr][kk] -= f * a[c][kk];
1824                        }
1825                        r[rr] -= f * r[c];
1826                    }
1827                }
1828            }
1829            let mut w = [0i32; NTAP];
1830            for i in 0..NTAP {
1831                let wi = if a[i][i].abs() > 1e-12 {
1832                    r[i] / a[i][i]
1833                } else {
1834                    0.0
1835                };
1836                w[i] = (wi * 4096.0).round().clamp(-32767.0, 32767.0) as i32;
1837            }
1838            let (mut c0, mut c1) = (0.0f64, 0.0f64);
1839            for &(t, v) in &obs[k] {
1840                let mut acc = 0i32;
1841                for i in 0..NTAP {
1842                    acc = acc.wrapping_add(w[i].wrapping_mul(t[i]));
1843                }
1844                let pr = acc.wrapping_add(2048) >> 12;
1845                c0 += (1.0 + v.unsigned_abs() as f64).log2();
1846                c1 += (1.0 + v.wrapping_sub(pr).unsigned_abs() as f64).log2();
1847            }
1848            if c1 < 0.98 * c0 && c0 - c1 > 64.0 {
1849                Some(w)
1850            } else {
1851                None
1852            }
1853        })
1854        .collect()
1855}
1856
1857/// Encode `x` (row-major `f32`, 1D–3D) as a tc-gpu stream.
1858pub fn encode_f32(x: &[f32], dims: &[usize], cfg: &GpuConfig) -> (Vec<u8>, GpuStats) {
1859    encode_f32_mt(x, dims, cfg, 1)
1860}
1861
1862/// [`encode_f32`] with `threads` worker threads (output bytes are identical
1863/// for any thread count).
1864pub fn encode_f32_mt(
1865    x: &[f32],
1866    dims: &[usize],
1867    cfg: &GpuConfig,
1868    threads: usize,
1869) -> (Vec<u8>, GpuStats) {
1870    assert!(cfg.lanes >= 1 && cfg.lanes <= 1024 && cfg.brick >= 1);
1871    rate_control(
1872        x,
1873        dims,
1874        cfg.brick,
1875        cfg.bound,
1876        cfg.rho,
1877        threads,
1878        |enc, dg, mx| encode_at(enc, dims, cfg, dg, mx),
1879    )
1880}
1881
1882/// Shared rate control: bisect the grid step on the measured error of the
1883/// integer decoder (PSNR/RMSE), or try a ladder of steps (max-abs) and keep
1884/// the smallest stream.
1885pub(crate) fn rate_control(
1886    x: &[f32],
1887    dims: &[usize],
1888    brick: usize,
1889    bound: ErrorBound,
1890    rho: f64,
1891    threads: usize,
1892    encode_at: impl Fn(&Enc, f64, Option<f64>) -> (Vec<u8>, GpuStats),
1893) -> (Vec<u8>, GpuStats) {
1894    assert_eq!(dims.iter().product::<usize>(), x.len());
1895    let enc = Enc::new(x, dims, brick, threads);
1896    let (lo, hi) = x
1897        .iter()
1898        .fold((f64::INFINITY, f64::NEG_INFINITY), |(a, b), &v| {
1899            (a.min(v as f64), b.max(v as f64))
1900        });
1901    let range = hi - lo;
1902    let g = 2f64.powi(enc.e);
1903    match bound {
1904        ErrorBound::MaxAbs(b) => {
1905            let mut best: Option<(Vec<u8>, GpuStats)> = None;
1906            for f in [1.0, 1.4, 1.8, 2.2, 2.7, 3.3, 4.0, 5.0] {
1907                let r = encode_at(&enc, (f * b / g).max(1.0), Some(b));
1908                if best.as_ref().is_none_or(|(bb, _)| r.0.len() < bb.len()) {
1909                    best = Some(r);
1910                }
1911            }
1912            best.unwrap()
1913        }
1914        ErrorBound::Psnr(_) | ErrorBound::Rmse(_) => {
1915            let t = match bound {
1916                ErrorBound::Rmse(r) => r,
1917                ErrorBound::Psnr(db) => range * 10f64.powf(-db / 20.0),
1918                _ => unreachable!(),
1919            }
1920            .max(f64::MIN_POSITIVE);
1921            let tg = t / g;
1922            let (mut lo, mut hi) = (tg * 0.5, tg * 8.0);
1923            let mut guard = 0;
1924            while enc.trial_rmse(lo.max(1.0), rho) > t && guard < 40 && lo > 1.0 {
1925                hi = lo;
1926                lo *= 0.5;
1927                guard += 1;
1928            }
1929            while enc.trial_rmse(hi, rho) <= t && guard < 80 {
1930                lo = hi;
1931                hi *= 2.0;
1932                guard += 1;
1933            }
1934            lo = lo.max(1.0);
1935            for _ in 0..14 {
1936                let mid = (lo * hi).sqrt();
1937                if enc.trial_rmse(mid, rho) <= t {
1938                    lo = mid;
1939                } else {
1940                    hi = mid;
1941                }
1942            }
1943            encode_at(&enc, lo, None)
1944        }
1945    }
1946}
1947
1948pub(crate) fn encode_at(
1949    enc: &Enc,
1950    dims: &[usize],
1951    cfg: &GpuConfig,
1952    dg: f64,
1953    maxabs: Option<f64>,
1954) -> (Vec<u8>, GpuStats) {
1955    let shapes = enc.shapes_at(dg);
1956    let (qs, dl) = enc.quantize(dg, cfg.rho);
1957    let fitted = if std::env::var("TC_NOPRED").is_ok() {
1958        vec![None; NKEY]
1959    } else {
1960        fit_preds(&shapes, &enc.bricks, &qs, cfg.lanes)
1961    };
1962    let preds: Vec<[i32; NTAP]> = fitted.iter().map(|p| p.unwrap_or([0; NTAP])).collect();
1963    let mut cs = enc.reconstruct(&shapes, &qs, &dl);
1964    let scale = enc.scale();
1965    let g = 2f64.powi(enc.e);
1966    // max-abs: corrections k·corr on the grid, then raw escapes
1967    let corr = maxabs
1968        .map(|b| ((2.0 * b / g).floor() as i64).clamp(0, 1 << 30) as i32)
1969        .unwrap_or(0);
1970    let nb = enc.bricks.len();
1971    let mut outl: Vec<Vec<(usize, i32)>> = vec![Vec::new(); nb];
1972    let mut escs: Vec<Vec<(usize, u32)>> = vec![Vec::new(); nb];
1973    if let Some(b) = maxabs {
1974        for bi in 0..nb {
1975            for i in 0..cs[bi].len() {
1976                let xv = enc.x[enc.global_index(bi, i)];
1977                let y =
1978                    f32::from_bits(to_f32_bits(cs[bi][i].wrapping_add(enc.mean_fix), scale)) as f64;
1979                let r = xv as f64 - y;
1980                if r.abs() > b && corr > 0 {
1981                    let k = (r / g / corr as f64)
1982                        .round()
1983                        .clamp(-(1 << 20) as f64, (1 << 20) as f64)
1984                        as i32;
1985                    if k != 0 {
1986                        cs[bi][i] = cs[bi][i].wrapping_add(k.wrapping_mul(corr));
1987                        outl[bi].push((i, k));
1988                    }
1989                }
1990                let y =
1991                    f32::from_bits(to_f32_bits(cs[bi][i].wrapping_add(enc.mean_fix), scale)) as f64;
1992                if (xv as f64 - y).abs() > b {
1993                    escs[bi].push((i, xv.to_bits()));
1994                }
1995            }
1996        }
1997    }
1998    // priors: pooled decision counts per model
1999    let idx: Vec<usize> = (0..nb).collect();
2000    let counts = par_map(&idx, enc.threads, |&bi| {
2001        let sh = &shapes[enc.bricks[bi].shape];
2002        let mut cs = CountSink {
2003            n: vec![[0; 2]; NMODEL],
2004        };
2005        let mut q = qs[bi].clone();
2006        code_brick(
2007            sh,
2008            &mut q,
2009            &preds,
2010            &vec![MINIT; NMODEL],
2011            cfg.lanes,
2012            cfg.adaptive,
2013            &mut cs,
2014        );
2015        cs.n
2016    });
2017    let mut n = vec![[0u32; 2]; NMODEL];
2018    for c in &counts {
2019        for (a, b) in n.iter_mut().zip(c) {
2020            a[0] += b[0];
2021            a[1] += b[1];
2022        }
2023    }
2024    let priors = if std::env::var("TC_NOPRIOR").is_ok() {
2025        vec![None; NMODEL]
2026    } else {
2027        choose_priors(&n, cfg.adaptive)
2028    };
2029    let init = init_of(&priors);
2030    // entropy-code bricks
2031    let payloads: Vec<(Vec<u8>, usize, usize)> = par_map(&idx, enc.threads, |&bi| {
2032        let sh = &shapes[enc.bricks[bi].shape];
2033        let mut sink = RecSink {
2034            syms: vec![Vec::new(); cfg.lanes + 1],
2035        };
2036        let mut q = qs[bi].clone();
2037        code_brick(
2038            sh,
2039            &mut q,
2040            &preds,
2041            &init,
2042            cfg.lanes,
2043            cfg.adaptive,
2044            &mut sink,
2045        );
2046        let ol = cfg.lanes;
2047        let mut om = vec![MINIT; NOMODEL];
2048        let ov = &outl[bi];
2049        if maxabs.is_some() {
2050            gamma::<_, true>(&mut sink, ol, &mut om, 0, ov.len() as u32);
2051            let mut last: i64 = -1;
2052            for &(i, k) in ov {
2053                gamma::<_, true>(&mut sink, ol, &mut om, 32, (i as i64 - last - 1) as u32);
2054                last = i as i64;
2055                sink.raw(ol, (k < 0) as u32, 1);
2056                gamma::<_, true>(&mut sink, ol, &mut om, 64, k.unsigned_abs() - 1);
2057            }
2058        }
2059        let lanes: Vec<Vec<u16>> = sink.syms.iter().map(|s| rans_encode(s)).collect();
2060        let mut w = ByteWriter::new();
2061        for l in &lanes {
2062            w.varint(l.len() as u64);
2063        }
2064        let ib = w.buf.len();
2065        w.varint(escs[bi].len() as u64);
2066        let mut last = 0usize;
2067        for (j, &(i, bits)) in escs[bi].iter().enumerate() {
2068            w.varint(if j == 0 { i } else { i - last } as u64);
2069            last = i;
2070            w.bytes(&bits.to_le_bytes());
2071        }
2072        let eb = w.buf.len() - ib;
2073        if w.buf.len() % 2 == 1 {
2074            w.u8(0);
2075        }
2076        let ob = lanes[ol].len() * 2 + eb;
2077        for l in &lanes {
2078            for &v in l {
2079                w.bytes(&v.to_le_bytes());
2080            }
2081        }
2082        (w.buf, ib, ob)
2083    });
2084    // header
2085    let mut w = ByteWriter::new();
2086    w.bytes(MAGIC);
2087    w.varint(dims.len() as u64);
2088    for &d in dims {
2089        w.varint(d as u64);
2090    }
2091    for &d in &enc.bdims {
2092        w.varint(d as u64);
2093    }
2094    w.varint(cfg.lanes as u64);
2095    w.u8(cfg.adaptive as u8);
2096    w.svarint(enc.e as i64);
2097    w.svarint(enc.mean_fix as i64);
2098    w.f64(dg);
2099    w.bytes(&dl);
2100    w.varint(corr as u64);
2101    for k in used_keys(&shapes) {
2102        match fitted[k] {
2103            Some(p) => {
2104                w.u8(1);
2105                for &t in &p {
2106                    w.svarint(t as i64);
2107                }
2108            }
2109            None => w.u8(0),
2110        }
2111    }
2112    write_priors(&mut w, &priors);
2113    for (p, _, _) in &payloads {
2114        w.varint(p.len() as u64);
2115    }
2116    if w.buf.len() % 2 == 1 {
2117        w.u8(0);
2118    }
2119    let header_bytes = w.buf.len();
2120    for (p, _, _) in &payloads {
2121        w.bytes(p);
2122    }
2123    let (se, mx) = {
2124        let (se, _) = enc.errors(&cs);
2125        // escapes are exact
2126        let mut se = se;
2127        let mut mx = 0.0f64;
2128        for bi in 0..nb {
2129            let mut e = escs[bi].iter().peekable();
2130            for i in 0..cs[bi].len() {
2131                let xv = enc.x[enc.global_index(bi, i)] as f64;
2132                let y =
2133                    f32::from_bits(to_f32_bits(cs[bi][i].wrapping_add(enc.mean_fix), scale)) as f64;
2134                if e.peek().is_some_and(|&&(ei, _)| ei == i) {
2135                    e.next();
2136                    se -= (xv - y) * (xv - y);
2137                    continue;
2138                }
2139                mx = mx.max((xv - y).abs());
2140            }
2141        }
2142        (se.max(0.0), mx)
2143    };
2144    let n = enc.x.len();
2145    let st = GpuStats {
2146        bytes: w.buf.len(),
2147        bits_per_value: 8.0 * w.buf.len() as f64 / n as f64,
2148        header_bytes,
2149        index_bytes: payloads.iter().map(|p| p.1).sum(),
2150        outlier_bytes: payloads.iter().map(|p| p.2).sum(),
2151        n_outliers: outl.iter().map(|v| v.len()).sum(),
2152        n_escapes: escs.iter().map(|v| v.len()).sum(),
2153        rmse: (se / n as f64).sqrt(),
2154        max_abs: mx,
2155        delta: dg * g,
2156    };
2157    (w.buf, st)
2158}
2159
2160#[cfg(test)]
2161mod tests {
2162    use super::*;
2163
2164    fn field(d: [usize; 3]) -> Vec<f32> {
2165        let mut v = Vec::new();
2166        for i in 0..d[0] {
2167            for j in 0..d[1] {
2168                for k in 0..d[2] {
2169                    let (x, y, z) = (i as f64, j as f64, k as f64);
2170                    v.push(
2171                        ((0.3 * x).sin() + (0.11 * y * z / 8.0).cos() + 0.01 * x * y
2172                            - (k % 7) as f64 * 0.01
2173                            + 3.0) as f32,
2174                    );
2175                }
2176            }
2177        }
2178        v
2179    }
2180
2181    fn err(x: &[f32], y: &[f32]) -> (f64, f64) {
2182        let mut se = 0.0;
2183        let mut mx = 0.0f64;
2184        for (a, b) in x.iter().zip(y) {
2185            let e = *a as f64 - *b as f64;
2186            se += e * e;
2187            mx = mx.max(e.abs());
2188        }
2189        ((se / x.len() as f64).sqrt(), mx)
2190    }
2191
2192    #[test]
2193    fn rans_round_trip() {
2194        let mut s = RecSink {
2195            syms: vec![Vec::new()],
2196        };
2197        let mut st = 12345u32;
2198        let mut vals = Vec::new();
2199        for i in 0..20000 {
2200            st = st.wrapping_mul(1664525).wrapping_add(1013904223);
2201            if i % 3 == 0 {
2202                let n = 1 + (st >> 28) % 16;
2203                vals.push((1, st >> 7, n));
2204                s.raw(0, st >> 7, n);
2205            } else {
2206                let p = 32 + (st >> 16) % 65000;
2207                let b = (st & 0xFF) < 128;
2208                vals.push((0, b as u32, p));
2209                s.bit(0, b, p);
2210            }
2211        }
2212        let w = rans_encode(&s.syms[0]);
2213        let mut d = RansDec::new(&w);
2214        for &(k, v, n) in &vals {
2215            if k == 1 {
2216                assert_eq!(d.raw(n), v & ((1 << n) - 1));
2217            } else {
2218                assert_eq!(d.bit(n) as u32, v);
2219            }
2220        }
2221        assert!(!d.overrun());
2222    }
2223
2224    #[test]
2225    fn psnr_round_trip_and_bricks() {
2226        let d = [20, 37, 45];
2227        let x = field(d);
2228        for brick in [8, 16, 64] {
2229            for db in [50.0, 90.0] {
2230                let cfg = GpuConfig {
2231                    brick,
2232                    lanes: 4,
2233                    ..GpuConfig::new(ErrorBound::Psnr(db))
2234                };
2235                let (bytes, st) = encode_f32(&x, &d, &cfg);
2236                let (dd, y) = decode_f32(&bytes, 1).unwrap();
2237                assert_eq!(dd, d.to_vec());
2238                let (rmse, mx) = err(&x, &y);
2239                assert!((rmse - st.rmse).abs() <= 1e-12 * rmse, "brick {brick} {db}");
2240                assert_eq!(mx, st.max_abs);
2241                let range = x.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b))
2242                    - x.iter().fold(f32::INFINITY, |a, &b| a.min(b));
2243                assert!(20.0 * (range as f64 / rmse).log10() >= db - 1e-6);
2244                let (_, y4) = decode_f32(&bytes, 4).unwrap();
2245                assert_eq!(
2246                    y.iter().map(|v| v.to_bits()).collect::<Vec<_>>(),
2247                    y4.iter().map(|v| v.to_bits()).collect::<Vec<_>>()
2248                );
2249                assert_eq!(
2250                    encode_f32_mt(&x, &d, &cfg, 3).0,
2251                    bytes,
2252                    "thread-count independent"
2253                );
2254            }
2255        }
2256    }
2257
2258    #[test]
2259    fn maxabs_and_random_access() {
2260        let d = [24, 30, 33];
2261        let x = field(d);
2262        for rel in [1e-2, 1e-4] {
2263            let b = rel * 2.5;
2264            let cfg = GpuConfig {
2265                brick: 16,
2266                lanes: 8,
2267                ..GpuConfig::new(ErrorBound::MaxAbs(b))
2268            };
2269            let (bytes, st) = encode_f32(&x, &d, &cfg);
2270            let (_, y) = decode_f32(&bytes, 2).unwrap();
2271            let (_, mx) = err(&x, &y);
2272            assert!(mx <= b, "{mx} > {b}");
2273            assert_eq!(mx, st.max_abs);
2274            let (lo, hi) = ([3, 5, 7], [21, 29, 20]);
2275            let sub = decode_box_bits(&bytes, lo, hi, 1).unwrap();
2276            let mut k = 0;
2277            for z in lo[0]..hi[0] {
2278                for yy in lo[1]..hi[1] {
2279                    for xx in lo[2]..hi[2] {
2280                        assert_eq!(sub[k], y[(z * d[1] + yy) * d[2] + xx].to_bits());
2281                        k += 1;
2282                    }
2283                }
2284            }
2285        }
2286    }
2287
2288    #[test]
2289    fn two_d_and_one_d() {
2290        let x2: Vec<f32> = (0..70 * 90)
2291            .map(|i| ((i % 90) as f32 * 0.07).sin() * ((i / 90) as f32 * 0.05).cos())
2292            .collect();
2293        let (b, st) = encode_f32(
2294            &x2,
2295            &[70, 90],
2296            &GpuConfig {
2297                brick: 32,
2298                lanes: 8,
2299                ..GpuConfig::new(ErrorBound::Psnr(70.0))
2300            },
2301        );
2302        let (_, y) = decode_f32(&b, 1).unwrap();
2303        assert!((err(&x2, &y).0 - st.rmse).abs() <= 1e-12 * st.rmse);
2304        let x1: Vec<f32> = (0..3000).map(|i| (i as f32 * 0.01).sin()).collect();
2305        let (b, st) = encode_f32(
2306            &x1,
2307            &[3000],
2308            &GpuConfig {
2309                brick: 16,
2310                lanes: 2,
2311                ..GpuConfig::new(ErrorBound::Psnr(70.0))
2312            },
2313        );
2314        let (_, y) = decode_f32(&b, 1).unwrap();
2315        assert!((err(&x1, &y).0 - st.rmse).abs() <= 1e-12 * st.rmse);
2316    }
2317
2318    #[test]
2319    fn lattice_matches_pass_of() {
2320        for (ez, ey) in [(1, 1), (1, 9), (9, 1), (5, 7), (16, 16), (3, 13)] {
2321            let b = BandInfo {
2322                start: [2, 3, 0],
2323                end: [2 + ez, 3 + ey, 4],
2324                class: 0,
2325                parent: None,
2326                pshift: [0; 3],
2327                npass: npass_of([2, 3, 0], [2 + ez, 3 + ey, 4]),
2328                pkey: [0; 8],
2329                m: 1,
2330                sh: 1,
2331                w: 1.0,
2332                org: [2, 3, 0],
2333                raster: false,
2334                ax: false,
2335                nl: 0,
2336            };
2337            let mut got = Vec::new();
2338            for_lines(&b, 3, |p, l, z, y| got.push((p, l, z, y)));
2339            let mut want = Vec::new();
2340            for p in 0..b.npass {
2341                let mut j = 0;
2342                for z in b.start[0]..b.end[0] {
2343                    for y in b.start[1]..b.end[1] {
2344                        if band_pass(&b, z - b.start[0], y - b.start[1]) == p {
2345                            want.push((p, j % 3, z, y));
2346                            j += 1;
2347                        }
2348                    }
2349                }
2350            }
2351            assert_eq!(got, want, "{ez}x{ey}");
2352        }
2353    }
2354
2355    #[test]
2356    fn mulsh_matches_wide() {
2357        let mut s = 7u64;
2358        for _ in 0..10000 {
2359            s = s
2360                .wrapping_mul(6364136223846793005)
2361                .wrapping_add(1442695040888963407);
2362            let a = (s >> 33) as i32;
2363            let b = (s as i32) >> 1;
2364            let sh = 1 + (s >> 58) as u32 % 60;
2365            let w = (a as i128 * b as i128 + (1i128 << (sh - 1))) >> sh;
2366            assert_eq!(mulsh(a, b, sh), w as i64 as i32);
2367        }
2368    }
2369}