Skip to main content

cram_codec/
coefcode.rs

1//! Context-modelled coding of quantized wavelet coefficients.
2//!
3//! Every coefficient `q` is binarized as: significance `|q| > 0`, then
4//! `|q| > k` for `k = 1 … KB`, an adaptive Elias-gamma escape for
5//! `|q| − KB − 1`, and a sign. Each binary decision is coded by the integer
6//! range coder in [`crate::abac`] with a probability supplied by a
7//! [`CoefModel`]:
8//!
9//! - [`HandModel`]: classic hand-designed contexts — subband class (level ×
10//!   orientation), causal neighbour activity in the same band, and the parent
11//!   coefficient's neighbourhood one level up — each indexing a two-rate
12//!   adaptive [`BitModel`];
13//! - the learned models in [`crate::learned`], which predict the bins from the
14//!   same [`Feat`] vector with a fixed-point network.
15//!
16//! Encoder and decoder run the *same* traversal ([`code_bands`]) through the
17//! [`Bits`] trait, so the context computation cannot drift between them.
18
19use crate::abac::{BitModel, Decoder, Encoder, UintModel};
20use crate::wavelet::{Band, Pyramid};
21
22/// Unary magnitude bins after significance (`|q| > 1 … |q| > KB`).
23pub const KB: usize = 14;
24/// Neighbour magnitudes are capped at this before use.
25pub const CAP: u16 = 65535;
26/// Number of subband classes: 3 level groups × 3 orientation counts + LL.
27pub const NCLASS: usize = 10;
28
29/// Symmetric encode/decode of single decisions.
30pub trait Bits {
31    /// Code `bit` (ignored when decoding) with an adaptive model.
32    fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool;
33    /// Code `bit` with a fixed probability `P(1) = p1/2^16`.
34    fn bitp(&mut self, bit: bool, p1: u32) -> bool;
35    /// Code an unbounded non-negative integer.
36    fn uint(&mut self, v: u64, m: &mut UintModel) -> u64;
37    /// Accounting tag for the decisions that follow (no-op for real coders).
38    #[inline]
39    fn tag(&mut self, _t: usize) {}
40}
41
42/// Cost meter: models adapt exactly as in the real coder, ideal code
43/// lengths are tallied per [`Bits::tag`].
44#[derive(Default, Debug)]
45pub struct Meter {
46    /// Bits per tag.
47    pub bits: [f64; 8],
48    cur: usize,
49}
50
51impl Bits for Meter {
52    fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool {
53        self.bits[self.cur] += crate::abac::cost_bits(bit, m.p1());
54        m.update(bit);
55        bit
56    }
57    fn bitp(&mut self, bit: bool, p1: u32) -> bool {
58        self.bits[self.cur] += crate::abac::cost_bits(bit, p1);
59        bit
60    }
61    fn uint(&mut self, v: u64, m: &mut UintModel) -> u64 {
62        let mut e = Encoder::new();
63        m.encode(&mut e, v);
64        self.bits[self.cur] += 8.0 * e.finish().len() as f64 - 32.0;
65        v
66    }
67    fn tag(&mut self, t: usize) {
68        self.cur = t;
69    }
70}
71
72impl Bits for Encoder {
73    #[inline]
74    fn bit(&mut self, bit: bool, m: &mut BitModel) -> bool {
75        self.encode(bit, m);
76        bit
77    }
78    #[inline]
79    fn bitp(&mut self, bit: bool, p1: u32) -> bool {
80        self.encode_p(bit, p1);
81        bit
82    }
83    fn uint(&mut self, v: u64, m: &mut UintModel) -> u64 {
84        m.encode(self, v);
85        v
86    }
87}
88
89impl Bits for Decoder<'_> {
90    #[inline]
91    fn bit(&mut self, _: bool, m: &mut BitModel) -> bool {
92        self.decode(m)
93    }
94    #[inline]
95    fn bitp(&mut self, _: bool, p1: u32) -> bool {
96        self.decode_p(p1)
97    }
98    fn uint(&mut self, _: u64, m: &mut UintModel) -> u64 {
99        m.decode(self)
100    }
101}
102
103/// Context features of one coefficient — all causal (already coded).
104#[derive(Clone, Copy, Debug, Default)]
105pub struct Feat {
106    /// Capped magnitudes of causal neighbours: W, N, U, NW, NE, UW, UN, WW
107    /// (axis 2 = W/E, axis 1 = N, axis 0 = U), 0 outside the band.
108    pub nb: [u16; 8],
109    /// Signs of W, N, U (−1, 0, +1).
110    pub sg: [i8; 3],
111    /// Capped parent magnitude (0 if no parent band).
112    pub par: u16,
113    /// Capped sum of the parent's 6 face neighbours.
114    pub pnb: u16,
115    /// Subband class `0..NCLASS`.
116    pub class: u8,
117    /// Highpass mask.
118    pub mask: u8,
119    /// Whether a parent band exists.
120    pub has_par: bool,
121    /// Capped magnitude of the in-band prediction (0 when prediction is off).
122    pub pm: u16,
123    /// Signed context values: 0–7 behind `nb`, 8 parent, 9 NN, 10 UU,
124    /// 11 UE, 12–17 the parent's face neighbours (−/+ per axis).
125    pub sv: [i32; NSV],
126    /// Signed in-band prediction.
127    pub pv: i32,
128}
129
130/// Signed context values per coefficient.
131pub const NSV: usize = 18;
132
133/// Subband class of a band.
134pub fn band_class(b: &Band) -> u8 {
135    if b.mask == 0 {
136        return (NCLASS - 1) as u8;
137    }
138    let nh = b.mask.count_ones() as usize;
139    (b.level.min(2) * 3 + nh - 1) as u8
140}
141
142/// A probability model for one quantized coefficient.
143pub trait CoefModel {
144    /// Code (or decode) the coefficient `v` with context `f`; returns it.
145    fn code<B: Bits>(&mut self, b: &mut B, f: &Feat, v: i32) -> i32;
146}
147
148fn bucket(a: u32, th: &[u32]) -> usize {
149    th.iter().position(|&t| a <= t).unwrap_or(th.len())
150}
151
152const A_TH: [u32; 11] = [0, 1, 2, 3, 5, 7, 10, 14, 20, 30, 48];
153const NA: usize = A_TH.len() + 1;
154const P_TH: [u32; 4] = [0, 2, 6, 16];
155const NP: usize = P_TH.len() + 1;
156/// Magnitude-scale classes (see [`mag_scale`]).
157pub const NE: usize = 18;
158
159/// Expected-magnitude class `⌊log₂(1 + 2μ)⌋` from the weighted mean `μ` of
160/// the causal neighbours' and parent's magnitudes (an adaptive-Golomb
161/// parameter: the low `e − 2` bits of `|q| − 1` are coded near-raw).
162#[inline]
163pub fn mag_scale(f: &Feat) -> usize {
164    let n = f.nb.map(|v| v as u64);
165    let mut s = 2 * (n[0] + n[1] + n[2]) + n[3] + n[4] + n[5] + n[6];
166    let mut w = 10;
167    s += 2 * f.pm as u64;
168    if f.has_par {
169        s += 2 * f.par as u64;
170        w += 2;
171    }
172    let v = 1 + 2 * s / w;
173    (v.ilog2() as usize).min(NE - 1)
174}
175
176/// Hand-designed context model (adaptive bit models per context).
177#[derive(Clone, Debug)]
178pub struct HandModel {
179    sig: Vec<BitModel>,
180    mag: Vec<BitModel>,
181    low: Vec<BitModel>,
182    esc: Vec<UintModel>,
183    sign: Vec<BitModel>,
184}
185
186impl Default for HandModel {
187    fn default() -> Self {
188        Self {
189            sig: vec![BitModel::default(); NCLASS * NA * NP],
190            mag: vec![BitModel::default(); NCLASS * NE * KB],
191            low: vec![BitModel::default(); NE * 4],
192            esc: vec![UintModel::default(); NCLASS],
193            sign: vec![BitModel::default(); NCLASS * 27],
194        }
195    }
196}
197
198/// Neighbour activity used by the hand model.
199#[inline]
200pub fn activity(f: &Feat) -> u32 {
201    let n = f.nb.map(|v| v.min(15) as u32);
202    2 * (n[0] + n[1] + n[2]) + n[3] + n[4] + n[5] + n[6] + n[7]
203}
204
205impl CoefModel for HandModel {
206    fn code<B: Bits>(&mut self, b: &mut B, f: &Feat, v: i32) -> i32 {
207        let c = f.class as usize;
208        let a = activity(f) + f.pm.min(15) as u32;
209        let pa = 4 * f.par.min(15) as u32 + f.pnb.min(60) as u32;
210        let ab = bucket(a, &A_TH);
211        let pb = if f.has_par {
212            1 + bucket(pa, &P_TH[..NP - 2])
213        } else {
214            0
215        };
216        let mag = v.unsigned_abs();
217        b.tag(0);
218        if !b.bit(mag > 0, &mut self.sig[(c * NA + ab) * NP + pb]) {
219            return 0;
220        }
221        b.tag(1);
222        let e = mag_scale(f);
223        let k = e.saturating_sub(2) as u32;
224        // On decode `v` is a placeholder (mag == 0); the value is rebuilt from
225        // the decoded bits below, so wrap rather than trap in debug builds.
226        let r = mag.wrapping_sub(1);
227        let hi = r >> k;
228        let mut h = 0u32;
229        while (h as usize) < KB {
230            if !b.bit(hi > h, &mut self.mag[(c * NE + e) * KB + h as usize]) {
231                break;
232            }
233            h += 1;
234        }
235        if h as usize == KB {
236            h += b.uint(hi.wrapping_sub(h) as u64, &mut self.esc[c]) as u32;
237        }
238        let mut lo = 0u32;
239        b.tag(2);
240        for j in (0..k).rev() {
241            let bit = (r >> j) & 1 == 1;
242            let pos = (k - 1 - j) as usize;
243            let got = if pos < 2 {
244                b.bit(
245                    bit,
246                    &mut self.low[(e * 2 + pos) * 2 + (pos == 1 && lo & 1 == 1) as usize],
247                )
248            } else {
249                b.bitp(bit, crate::abac::ONE / 2)
250            };
251            lo = (lo << 1) | got as u32;
252        }
253        let m = 1 + ((h << k) | lo);
254        b.tag(3);
255        let sctx = c * 27 + ((f.sg[0] + 1) * 9 + (f.sg[1] + 1) * 3 + (f.sg[2] + 1)) as usize;
256        let neg = b.bit(v < 0, &mut self.sign[sctx]);
257        if neg {
258            -(m as i32)
259        } else {
260            m as i32
261        }
262    }
263}
264
265/// Gather the features of position `(z, y, x)` in band `bi`.
266#[inline]
267pub fn features(
268    p: &Pyramid,
269    bi: usize,
270    pbi: Option<usize>,
271    q: &[i32],
272    z: usize,
273    y: usize,
274    x: usize,
275) -> Feat {
276    let b = &p.bands[bi];
277    let [_, ny, nx] = p.dims;
278    let at = |z: usize, y: usize, x: usize| q[(z * ny + y) * nx + x];
279    let mag = |v: i32| (v.unsigned_abs().min(CAP as u32)) as u16;
280    let sgn = |v: i32| v.signum() as i8;
281    let (z0, y0, x0) = (b.start[0], b.start[1], b.start[2]);
282    let x1 = b.end[2];
283    let mut f = Feat {
284        class: band_class(b),
285        mask: b.mask,
286        ..Default::default()
287    };
288    if x > x0 {
289        let w = at(z, y, x - 1);
290        {
291            let t = w;
292            f.nb[0] = mag(t);
293            f.sv[0] = t;
294        }
295        f.sg[0] = sgn(w);
296        if x > x0 + 1 {
297            {
298                let t = at(z, y, x - 2);
299                f.nb[7] = mag(t);
300                f.sv[7] = t;
301            }
302        }
303    }
304    if y > y0 + 1 {
305        f.sv[9] = at(z, y - 2, x);
306    }
307    if z > z0 + 1 {
308        f.sv[10] = at(z - 2, y, x);
309    }
310    if z > z0 && x + 1 < x1 {
311        f.sv[11] = at(z - 1, y, x + 1);
312    }
313    if y > y0 {
314        let n = at(z, y - 1, x);
315        {
316            let t = n;
317            f.nb[1] = mag(t);
318            f.sv[1] = t;
319        }
320        f.sg[1] = sgn(n);
321        if x > x0 {
322            {
323                let t = at(z, y - 1, x - 1);
324                f.nb[3] = mag(t);
325                f.sv[3] = t;
326            }
327        }
328        if x + 1 < x1 {
329            {
330                let t = at(z, y - 1, x + 1);
331                f.nb[4] = mag(t);
332                f.sv[4] = t;
333            }
334        }
335    }
336    if z > z0 {
337        let u = at(z - 1, y, x);
338        {
339            let t = u;
340            f.nb[2] = mag(t);
341            f.sv[2] = t;
342        }
343        f.sg[2] = sgn(u);
344        if x > x0 {
345            {
346                let t = at(z - 1, y, x - 1);
347                f.nb[5] = mag(t);
348                f.sv[5] = t;
349            }
350        }
351        if y > y0 {
352            {
353                let t = at(z - 1, y - 1, x);
354                f.nb[6] = mag(t);
355                f.sv[6] = t;
356            }
357        }
358    }
359    if let Some(pi) = pbi {
360        let pb = &p.bands[pi];
361        let pp = p.parent_pos(b, pb, [z, y, x]);
362        f.has_par = true;
363        {
364            let t = at(pp[0], pp[1], pp[2]);
365            f.par = mag(t);
366            f.sv[8] = t;
367        }
368        let mut s = 0u32;
369        for a in 0..3 {
370            let mut lo = pp;
371            if lo[a] > pb.start[a] {
372                lo[a] -= 1;
373                let t = at(lo[0], lo[1], lo[2]);
374                f.sv[12 + 2 * a] = t;
375                s += t.unsigned_abs().min(CAP as u32);
376            }
377            let mut hi = pp;
378            if hi[a] + 1 < pb.end[a] {
379                hi[a] += 1;
380                let t = at(hi[0], hi[1], hi[2]);
381                f.sv[13 + 2 * a] = t;
382                s += t.unsigned_abs().min(CAP as u32);
383            }
384        }
385        f.pnb = s.min(CAP as u32) as u16;
386    }
387    f
388}
389
390/// Number of in-band linear-prediction taps: W, N, U, NW, UW, UN, parent.
391pub const NTAP: usize = 7;
392/// Fixed-point shift of predictor weights (Q12).
393pub const PSHIFT: u32 = 12;
394
395/// Per-band causal linear predictor of the quantized value (integer
396/// weights, Q12). Smooth fields leave highpass bands smooth along their
397/// lowpass axes (neighbour correlation ≈ 0.9) and anticorrelated (≈ −0.5)
398/// along the highpass axis — structure significance coders such as SPECK
399/// leave on the table. The coder codes `q − pred`; contexts are computed on
400/// the residuals.
401#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
402pub struct Pred {
403    /// Whether prediction is on for the band.
404    pub on: bool,
405    /// Tap weights, Q12.
406    pub w: [i16; NTAP],
407}
408
409#[inline]
410fn taps(
411    p: &Pyramid,
412    bi: usize,
413    pbi: Option<usize>,
414    q: &[i32],
415    z: usize,
416    y: usize,
417    x: usize,
418) -> [i32; NTAP] {
419    let b = &p.bands[bi];
420    let [_, ny, nx] = p.dims;
421    let at = |z: usize, y: usize, x: usize| q[(z * ny + y) * nx + x];
422    let (zi, yi, xi) = (z > b.start[0], y > b.start[1], x > b.start[2]);
423    let mut t = [0i32; NTAP];
424    if xi {
425        t[0] = at(z, y, x - 1);
426    }
427    if yi {
428        t[1] = at(z, y - 1, x);
429        if xi {
430            t[3] = at(z, y - 1, x - 1);
431        }
432    }
433    if zi {
434        t[2] = at(z - 1, y, x);
435        if xi {
436            t[4] = at(z - 1, y, x - 1);
437        }
438        if yi {
439            t[5] = at(z - 1, y - 1, x);
440        }
441    }
442    if let Some(pi) = pbi {
443        let pb = &p.bands[pi];
444        let pp = p.parent_pos(b, pb, [z, y, x]);
445        t[6] = at(pp[0], pp[1], pp[2]);
446    }
447    t
448}
449
450#[inline]
451fn predict(w: &[i16; NTAP], t: &[i32; NTAP]) -> i32 {
452    let s: i64 = w.iter().zip(t).map(|(&a, &b)| a as i64 * b as i64).sum();
453    ((s + (1 << (PSHIFT - 1))) >> PSHIFT).clamp(-(1 << 30), 1 << 30) as i32
454}
455
456/// Fit per-band predictors by least squares on the quantized values the
457/// decoder will see, and keep a band's predictor only when it lowers the
458/// `Σ log₂(1+|r|)` cost proxy by > 2%.
459pub fn fit_predictors(p: &Pyramid, q: &[i32]) -> Vec<Pred> {
460    let [_, ny, nx] = p.dims;
461    (0..p.bands.len())
462        .map(|bi| {
463            let band = &p.bands[bi];
464            let pbi = p.parent_band(bi);
465            let mut g = [[0.0f64; NTAP]; NTAP];
466            let mut h = [0.0f64; NTAP];
467            let mut n = 0usize;
468            for z in band.start[0]..band.end[0] {
469                for y in band.start[1]..band.end[1] {
470                    for x in band.start[2]..band.end[2] {
471                        let t = taps(p, bi, pbi, q, z, y, x);
472                        let v = q[(z * ny + y) * nx + x] as f64;
473                        if v == 0.0 && t.iter().all(|&a| a == 0) {
474                            continue;
475                        }
476                        n += 1;
477                        for i in 0..NTAP {
478                            h[i] += t[i] as f64 * v;
479                            for j in 0..NTAP {
480                                g[i][j] += t[i] as f64 * t[j] as f64;
481                            }
482                        }
483                    }
484                }
485            }
486            if n < 32 {
487                return Pred::default();
488            }
489            // ridge + Gaussian elimination
490            let mut a = g;
491            let mut r = h;
492            for i in 0..NTAP {
493                a[i][i] += 1e-3 * (g[i][i] + 1.0);
494            }
495            for c in 0..NTAP {
496                let piv = a[c][c];
497                if piv.abs() < 1e-12 {
498                    continue;
499                }
500                for rr in 0..NTAP {
501                    if rr != c {
502                        let f = a[rr][c] / piv;
503                        for k in 0..NTAP {
504                            a[rr][k] -= f * a[c][k];
505                        }
506                        r[rr] -= f * r[c];
507                    }
508                }
509            }
510            let mut w = [0i16; NTAP];
511            for i in 0..NTAP {
512                let wi = if a[i][i].abs() > 1e-12 {
513                    r[i] / a[i][i]
514                } else {
515                    0.0
516                };
517                w[i] = (wi * (1 << PSHIFT) as f64).round().clamp(-32767.0, 32767.0) as i16;
518            }
519            let (mut c0, mut c1) = (0.0f64, 0.0f64);
520            for z in band.start[0]..band.end[0] {
521                for y in band.start[1]..band.end[1] {
522                    for x in band.start[2]..band.end[2] {
523                        let v = q[(z * ny + y) * nx + x];
524                        let pr = predict(&w, &taps(p, bi, pbi, q, z, y, x));
525                        c0 += (1.0 + v.unsigned_abs() as f64).log2();
526                        c1 += (1.0 + (v - pr).unsigned_abs() as f64).log2();
527                    }
528                }
529            }
530            if c1 < 0.98 * c0 {
531                Pred { on: true, w }
532            } else {
533                Pred::default()
534            }
535        })
536        .collect()
537}
538
539/// Serialize predictors.
540pub fn write_preds(w: &mut crate::bitio::ByteWriter, preds: &[Pred]) {
541    for pr in preds {
542        w.u8(pr.on as u8);
543        if pr.on {
544            for &v in &pr.w {
545                w.svarint(v as i64);
546            }
547        }
548    }
549}
550
551/// Parse predictors for `nbands` bands.
552pub fn read_preds(
553    r: &mut crate::bitio::ByteReader,
554    nbands: usize,
555) -> Result<Vec<Pred>, crate::bitio::DecodeError> {
556    (0..nbands)
557        .map(|_| {
558            let mut pr = Pred {
559                on: r.u8()? != 0,
560                ..Default::default()
561            };
562            if pr.on {
563                for v in pr.w.iter_mut() {
564                    *v = r.svarint()?.clamp(-32767, 32767) as i16;
565                }
566            }
567            Ok(pr)
568        })
569        .collect()
570}
571
572/// Traverse every coefficient in coding order. `q` holds values (filled in
573/// when decoding), `res` the residuals `q − pred` (the context source).
574/// `f(features, band, predicted value, residual) -> residual` codes one
575/// coefficient.
576fn traverse(
577    p: &Pyramid,
578    q: &mut [i32],
579    res: &mut [i32],
580    preds: &[Pred],
581    mut f: impl FnMut(&Feat, i32, i32) -> i32,
582) {
583    let [_, ny, nx] = p.dims;
584    for bi in 0..p.bands.len() {
585        let pbi = p.parent_band(bi);
586        let band = p.bands[bi].clone();
587        let pr = preds.get(bi).copied().unwrap_or_default();
588        for z in band.start[0]..band.end[0] {
589            for y in band.start[1]..band.end[1] {
590                for x in band.start[2]..band.end[2] {
591                    let i = (z * ny + y) * nx + x;
592                    let mut ft = features(p, bi, pbi, res, z, y, x);
593                    let pv = if pr.on {
594                        predict(&pr.w, &taps(p, bi, pbi, q, z, y, x))
595                    } else {
596                        0
597                    };
598                    ft.pm = pv.unsigned_abs().min(CAP as u32) as u16;
599                    ft.pv = pv;
600                    let r = f(&ft, pv, q[i].wrapping_sub(pv));
601                    res[i] = r;
602                    q[i] = r + pv;
603                }
604            }
605        }
606    }
607}
608
609/// Tile edge for [`code_bands_tiled`]: about 256 cells per tile (1D 256,
610/// 2D 16×16, 3D 8³ = 512).
611fn tile_edge(nd: usize) -> usize {
612    match nd {
613        0 | 1 => 256,
614        2 => 16,
615        _ => 8,
616    }
617}
618
619/// Tiles of band `b`: `(per-axis edge, tiles per axis)`.
620fn band_tiles(b: &crate::wavelet::Band, nd: usize) -> ([usize; 3], [usize; 3]) {
621    let t = tile_edge(nd);
622    let mut e = [1usize; 3];
623    let mut n = [1usize; 3];
624    for k in 0..3 {
625        let len = b.end[k] - b.start[k];
626        if len > 1 {
627            e[k] = t;
628            n[k] = len.div_ceil(t);
629        }
630    }
631    (e, n)
632}
633
634/// Per band, which tiles hold a nonzero residual (encoder side of
635/// [`code_bands_tiled`]).
636pub fn tile_live(p: &Pyramid, q: &[i32], preds: &[Pred]) -> Vec<Vec<bool>> {
637    let [_, ny, nx] = p.dims;
638    let nd = p.dims.iter().filter(|&&d| d > 1).count();
639    let mut qq = q.to_vec();
640    let mut res = vec![0i32; q.len()];
641    traverse(p, &mut qq, &mut res, preds, |_, _, r| r);
642    p.bands
643        .iter()
644        .map(|b| {
645            let (e, n) = band_tiles(b, nd);
646            let mut live = vec![false; n[0] * n[1] * n[2]];
647            for z in b.start[0]..b.end[0] {
648                for y in b.start[1]..b.end[1] {
649                    for x in b.start[2]..b.end[2] {
650                        if res[(z * ny + y) * nx + x] != 0 {
651                            let t = (((z - b.start[0]) / e[0]) * n[1] + (y - b.start[1]) / e[1])
652                                * n[2]
653                                + (x - b.start[2]) / e[2];
654                            live[t] = true;
655                        }
656                    }
657                }
658            }
659            live
660        })
661        .collect()
662}
663
664/// [`code_bands`] with a tile layer: at the start of each band a flag per
665/// tile (context: band class × previous tile's flag) says whether any
666/// residual in it is nonzero, and the coefficients of dead tiles are skipped
667/// (residual 0, value = prediction). Large all-zero regions then cost one
668/// flag per tile instead of one significance decision per coefficient.
669/// `live` is [`tile_live`] when encoding and ignored (decoded) otherwise.
670pub fn code_bands_tiled<B: Bits, M: CoefModel>(
671    p: &Pyramid,
672    q: &mut [i32],
673    preds: &[Pred],
674    b: &mut B,
675    model: &mut M,
676    live: Option<&[Vec<bool>]>,
677) {
678    let [_, ny, nx] = p.dims;
679    let nd = p.dims.iter().filter(|&&d| d > 1).count();
680    let mut res = vec![0i32; q.len()];
681    let mut flag = [BitModel::new(1 << 15); NCLASS * 2];
682    for bi in 0..p.bands.len() {
683        let pbi = p.parent_band(bi);
684        let band = p.bands[bi].clone();
685        let (e, n) = band_tiles(&band, nd);
686        let c = band_class(&band) as usize;
687        let mut prev = 1usize;
688        let alive: Vec<bool> = (0..n[0] * n[1] * n[2])
689            .map(|t| {
690                let v = live.is_some_and(|l| l[bi][t]);
691                let v = b.bit(v, &mut flag[c * 2 + prev]);
692                prev = v as usize;
693                v
694            })
695            .collect();
696        let pr = preds.get(bi).copied().unwrap_or_default();
697        for z in band.start[0]..band.end[0] {
698            for y in band.start[1]..band.end[1] {
699                for x in band.start[2]..band.end[2] {
700                    let i = (z * ny + y) * nx + x;
701                    let t = (((z - band.start[0]) / e[0]) * n[1] + (y - band.start[1]) / e[1])
702                        * n[2]
703                        + (x - band.start[2]) / e[2];
704                    let pv = if pr.on {
705                        predict(&pr.w, &taps(p, bi, pbi, q, z, y, x))
706                    } else {
707                        0
708                    };
709                    let r = if alive[t] {
710                        let mut ft = features(p, bi, pbi, &res, z, y, x);
711                        ft.pm = pv.unsigned_abs().min(CAP as u32) as u16;
712                        ft.pv = pv;
713                        model.code(b, &ft, q[i].wrapping_sub(pv))
714                    } else {
715                        0
716                    };
717                    res[i] = r;
718                    q[i] = r.wrapping_add(pv);
719                }
720            }
721        }
722    }
723}
724
725/// Code (encode or decode) every band of `q` (volume layout) in pyramid
726/// order with per-band predictors. When decoding, `q` must be zeroed and is
727/// filled in.
728pub fn code_bands<B: Bits, M: CoefModel>(
729    p: &Pyramid,
730    q: &mut [i32],
731    preds: &[Pred],
732    b: &mut B,
733    model: &mut M,
734) {
735    let mut res = vec![0i32; q.len()];
736    traverse(p, q, &mut res, preds, |f, _, r| model.code(b, f, r));
737}
738
739/// Visit every coefficient in coding order: `f(features, residual)` (for
740/// training and analysis).
741pub fn visit<F: FnMut(&Feat, i32)>(p: &Pyramid, q: &[i32], preds: &[Pred], mut f: F) {
742    let mut qq = q.to_vec();
743    let mut res = vec![0i32; q.len()];
744    traverse(p, &mut qq, &mut res, preds, |ft, _, r| {
745        f(ft, r);
746        r
747    });
748}
749
750/// Encode `q` with the hand model; returns the arithmetic-coded bytes.
751pub fn encode_hand(p: &Pyramid, q: &[i32], preds: &[Pred]) -> Vec<u8> {
752    let mut e = Encoder::new();
753    let mut qq = q.to_vec();
754    code_bands(p, &mut qq, preds, &mut e, &mut HandModel::default());
755    e.finish()
756}
757
758/// Decode what [`encode_hand`] produced.
759pub fn decode_hand(p: &Pyramid, bytes: &[u8], preds: &[Pred]) -> Vec<i32> {
760    let mut d = Decoder::new(bytes);
761    let mut q = vec![0i32; p.len()];
762    code_bands(p, &mut q, preds, &mut d, &mut HandModel::default());
763    q
764}
765
766#[cfg(test)]
767mod tests {
768    use super::*;
769    use crate::wavelet::{forward_97, MAX_LEVELS};
770
771    #[test]
772    fn round_trip_hand_model() {
773        let d = [12, 20, 33];
774        let p = Pyramid::new(d, MAX_LEVELS);
775        let mut x: Vec<f64> = (0..p.len())
776            .map(|i| ((i as f64) * 0.013).sin() * 100.0 + (i % 17) as f64)
777            .collect();
778        forward_97(&p, &mut x);
779        let mut q: Vec<i32> = x.iter().map(|v| (v / 0.7).round() as i32).collect();
780        q[5] = 1_000_000; // exercise the escape path
781        q[77] = -123_456;
782        for preds in [vec![], fit_predictors(&p, &q)] {
783            let bytes = encode_hand(&p, &q, &preds);
784            assert_eq!(decode_hand(&p, &bytes, &preds), q);
785            assert_eq!(encode_hand(&p, &q, &preds), bytes, "deterministic");
786        }
787    }
788}