Skip to main content

manifold/
mnf8.rs

1// MNF8 — format v4 (ADR-002: Entropy Backend and Residual Probability Modeling).
2//
3// MNF8 = MNF7 (modes 0-5, PLANAR|ZSTD flags) + a backend-identifier field +
4// encoder profiles; new magic, new header field. MNF5/6/7 files decode via
5// their own magics — no compatibility break (README §9 versioning rule).
6//
7// Header (32 bytes):
8//   "MNF8", u32 LE width, height, tile=16, tile_count, payload_len,
9//   u32 flags (MNF7 semantics + MNF8-owned bits:
10//     bit0 PLANAR, bit1 ZSTD, bit2 PRED_MED + bit3 TOKENS (ADR-003),
11//     ADR-004: bit4 PRED_PLANE, bit5 PRED_LEFT — bits 6..31 reserved),
12//   u32 backend:
13//     0 = zlib-9  (MNF7 control; payload is a zlib stream)
14//     1 = zstd-19 (xxh64-checksummed frame; decoder window capped at 23)
15//     2 = RESERVED — brotli was prototyped but unshipped: its static
16//         dictionary's cost accounting was measured only in the study, and
17//         a dictionary-free brotli backend changed nothing on the corpus.
18//         Reject until its gate re-passes (audit review 2026-10-25).
19//     3 = RESERVED (custom mixed run/rANS coder; reject until it passes the gate)
20//   other values: reject like reserved flag bits.
21//
22// FLAG_TOKENS (ADR-003, docs/mnf8-adr-003-tokens.md): the payload is NOT a
23// record stream — it is the LZ77-2D token wire over the image-wide MED
24// residual plane (crate::tokens), container-compressed by the backend.
25// Mutually exclusive with everything per-tile: TOKENS forces plain
26// interleaved literal records in the pick arms it competes against and is
27// rejected at decode with PLANAR (the token plane is always interleaved).
28//
29// ADR-004 (docs/mnf8-adr-004-branch-pred.md): bits 4-5 select the image-
30// wide predictor on mode-0 literal bodies — PRED_MED (edge-sense, the
31// v1.2 predecessor), PRED_PLANE (LOCO-I plane branch forced: a+b−cc), or
32// PRED_LEFT (pure left). All three decode causally (the exact same walk,
33// only the interior predictor function differs). Exactly one may be set;
34// the encoder measures all three per file (retention gate: branch choice
35// beats neighborhood choice, docs/mnf9-branch-gate.md, −7.0%).
36//
37// Profiles (encoder policy, not wire format):
38//   balanced  -> always zstd-19 (flags measured: planar vs interleaved).
39//   maximum   -> builds the tile stream under each (backend, flags)
40//                candidate, re-costing per-tile mode selection through that
41//                candidate's coder (stage-2 evidence: selection is
42//                backend-dependent), and emits the smallest file.
43//
44// Records: exactly MNF7's (modes 0-3 plus MNF7 color modes 4/5), decoded by
45// the same record walker as decode_mnf7; only container + coder differ.
46
47use std::io::Read;
48
49use crate::codec::{apply_transform, div_ceil, split_tiles, tile_sig, Atlas, Dims, TILE_SIZE};
50use crate::error::{Error, Result};
51use crate::mnf6::{
52    compress_stream, decode_record_walk, med_predict, modcolor_forward_iter, pack9, planar_pack,
53    proxy_cost, ycocg_forward, Coder, MnF6Stats, FLAG_PLANAR,
54};
55use crate::tokens;
56
57/// Header size: `MNF8` + 7×`u32` LE (adds backend id) — 4 B over MNF7.
58pub const HEADER8: usize = 32;
59
60// MNF8-only flags (MNF6/7 keep !0b11 frozen; MNF8 owns bits 2-6):
61/// Literal bodies carry image-wide MED residuals (decode inverts causally).
62pub const FLAG_PRED_MED: u32 = 4; // mode-0 literal bodies carry image-wide MED residuals
63/// Payload = LZ77-2D token wire over the MED residual plane (ADR-003).
64pub const FLAG_TOKENS: u32 = 8;
65/// Image-wide predictor on literal bodies = LOCO-I plane branch (ADR-004).
66pub const FLAG_PRED_PLANE: u32 = 16;
67/// Image-wide predictor on literal bodies = pure left (ADR-004).
68pub const FLAG_PRED_LEFT: u32 = 32;
69/// ADR-008 channel-diff residual organization, generalized v1.12 (the C2
70/// gate's reference-channel field): flags bits 6-7 = REF, read as
71/// `(flags >> 6) & 0b11` — 0 = none, 1 = G-centered (v1.11's bit-6
72/// semantic: old CD files carry exactly this shape and decode through
73/// the same walker), 2 = R-centered, 3 = B-centered. Mode-0 bodies and
74/// token tag-0 literals carry [ref, a−ref, b−ref] per pixel; zero added
75/// bytes; decode = the inverse before the walk.
76pub const FLAG_CHANNEL_DIFF: u32 = 64; // REF = 1 (G-centered)
77/// REF = 2 (R-centered): bit 7 alone.
78pub const FLAG_REF_R: u32 = 128;
79/// Bits above bit7 reserved (MNF8 owns bits 2-7).
80pub const FLAGS_RESERVED8: u32 = !0b11111111;
81
82/// The CD reference id from the flags word: 0 = none, 1 = G, 2 = R, 3 = B.
83fn cd_reference(flags: u32) -> u8 {
84    ((flags >> 6) & 0b11) as u8
85}
86
87/// zlib-9 backend.
88pub const BACKEND_ZLIB: u32 = 0;
89/// zstd-19 backend (ships in Balanced and Maximum).
90pub const BACKEND_ZSTD19: u32 = 1;
91/// Reserved (brotli prototype; decode rejects).
92pub const BACKEND_RESERVED2: u32 = 2; // brotli prototype — unshipped, reject
93/// Reserved (rANS prototype measured DENIED with honest transport; decode
94/// rejects — docs/mnf9-rans-arm.md §honest-re-measure).
95pub const BACKEND_RESERVED3: u32 = 3;
96
97// zstd-19's default window is 8 MiB = 2^23; hard-pin the decoder cap to it
98// (review fix 4: a hostile frame can't claim a 128 MiB window).
99/// zstd decoder window ceiling (8 MiB; matches level-19 encoder).
100pub const ZSTD_WINDOW_LOG_MAX: u32 = 23;
101
102/// Rank count for candidate references... (kept from the shared atlas table;
103/// RANK_CANDIDATES lives in codec — do not re-derive here.)
104use crate::codec::RANK_CANDIDATES;
105
106/// Encoder search breadth.
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum Profile {
109    /// zstd-19 only: fast, sane default.
110    Balanced,
111    /// All backends + flag variants measured; ships the smallest.
112    Maximum,
113}
114
115/// Backend candidates for the `maximum` profile, in measured-preference order.
116fn backend_candidates() -> Vec<(u32, Coder)> {
117    vec![
118        (BACKEND_ZSTD19, Coder::Zstd(19)),
119        (BACKEND_ZLIB, Coder::Zlib),
120    ]
121}
122
123fn backend_compress(backend: u32, buf: &[u8]) -> Result<Vec<u8>> {
124    match backend {
125        BACKEND_ZLIB => Ok(compress_stream(&Coder::Zlib, buf)),
126        BACKEND_ZSTD19 => Ok(compress_stream(&Coder::Zstd(19), buf)),
127        other => Err(Error::fmt_msg(format!(
128            "backend {other} not a compressed-output coder"
129        ))),
130    }
131}
132
133fn backend_decompress(backend: u32, buf: &[u8], limit: u64) -> Result<Vec<u8>> {
134    match backend {
135        BACKEND_ZLIB => {
136            // canonical-decode (review small item): every payload byte must
137            // belong to the stream — zlib's total_in tells us exactly how
138            // much was consumed.
139            let mut d = flate2::read::ZlibDecoder::new(buf).take(limit + 1);
140            let mut v = Vec::new();
141            d.read_to_end(&mut v)
142                .map_err(|_| Error::Decompress("corrupt zlib stream"))?;
143            if v.len() as u64 > limit {
144                return Err(Error::fmt_msg("bomb"));
145            }
146            if d.get_ref().total_in() as usize != buf.len() {
147                return Err(Error::fmt_msg("trailing bytes after zlib stream"));
148            }
149            Ok(v)
150        }
151        BACKEND_ZSTD19 => {
152            // window capped at the encoder's actual need (review fix 4);
153            // Cursor-wrapped so post-decode input position is observable
154            // (canonical-decode check, review small item).
155            let cursor = std::io::Cursor::new(buf);
156            let mut d = zstd::stream::read::Decoder::new(cursor)
157                .map_err(|_| Error::Decompress("zstd init"))?;
158            d.window_log_max(ZSTD_WINDOW_LOG_MAX)
159                .map_err(|_| Error::Decompress("zstd window param"))?;
160            let mut v = Vec::new();
161            d.single_frame()
162                .take(limit + 1)
163                .read_to_end(&mut v)
164                .map_err(|_| Error::Decompress("corrupt zstd stream"))?;
165            if v.len() as u64 > limit {
166                return Err(Error::fmt_msg("bomb"));
167            }
168            // NOTE (honest limitation): zlib canonicality is exact via
169            // total_in above; the zstd stream API here cannot cheaply report
170            // the decoder's exact consumed offset (BufReader may also read
171            // ahead), so bytes after a complete zstd frame are tolerated.
172            // Canonical zstd files remain an option via a future zstd-safe
173            // one-shot decode; not worth it today (ponytail).
174            Ok(v)
175        }
176        other => Err(Error::fmt_msg(format!(
177            "backend {other} not a decompressable coder"
178        ))),
179    }
180}
181
182/// Encode RGB8 pixels as MNF8.
183///
184/// `profile = Balanced`: zstd-19 pinned; measures the planar vs
185/// interleaved flag variants under that coder (same honesty as MNF7
186/// measures flags, minus the zlib arms excluded by ADR-002).
187///
188/// `profile = Maximum`: measures backend×flags candidates and emits the
189/// smallest final file. Per-tile mode selection is re-costed inside each
190/// candidate's own coder — the stage-2 protocol (docs/entropy-backends-v13.md).
191/// There is NO shared per-tile loop with encode_mnf7: the one function
192/// `encode_stream_coder` is parameterized by coder and every caller routes
193/// through it (review fix 8: MNF7's loop stays the frozen-format history;
194/// MNF8 owns its selection policy).
195pub fn encode_mnf8(
196    pixels: &[u8],
197    dims: Dims,
198    use_geom: bool,
199    profile: Profile,
200) -> (Vec<u8>, MnF6Stats, u32) {
201    let candidates: Vec<(u32, Coder)> = match profile {
202        Profile::Balanced => vec![(BACKEND_ZSTD19, Coder::Zstd(19))],
203        Profile::Maximum => backend_candidates(),
204    };
205    let mut best: Option<(Vec<u8>, u32, MnF6Stats, u32)> = None;
206    // ADR-004: the three literal-body predictors × (interleaved|planar)
207    // as per-file arms, each measured under every backend candidate,
208    // complete serialized cost. (PLANAR's own measured value is real:
209    // gradient −28% under z19 in the stage-2 matrix — never drop it.)
210    let pred_arms = [
211        PredKind::None,
212        PredKind::Med,
213        PredKind::Plane,
214        PredKind::Left,
215    ];
216    for (backend, coder) in candidates {
217        for kind in pred_arms {
218            for planar in [false, true] {
219                let med = kind == PredKind::Med;
220                // ADR-008 (v1.12): the reference-channel field 0..3 — none,
221                // G (v1.11's bit-6 semantic), R, B — per-file at complete
222                // serialized cost (the C2 gate's table).
223                for reference in 0u8..=3 {
224                    let flags = kind.flag_bit()
225                        | if planar { FLAG_PLANAR } else { 0 }
226                        | (u32::from(reference) << 6);
227                    let (file, st) = encode_stream_coder(
228                        pixels, dims, true, use_geom, flags, planar, med, kind, &coder, backend,
229                        reference,
230                    )
231                    .expect("encoding an in-memory image cannot fail");
232                    let size = file.len();
233                    if best.as_ref().is_none_or(|b| size < b.0.len()) {
234                        best = Some((file, flags, st, backend));
235                    }
236                }
237            }
238        }
239        // ADR-003 token arm: tokens over the MED plane, same backend (the
240        // token grammar's predictor is fixed = MED; ADR-004 composes with
241        // the record arms only). Rate-aware by construction.
242        let base = FLAG_PRED_MED | FLAG_TOKENS;
243        for cap in [16, tokens::MAX_CANDIDATES] {
244            for reference in 0u8..=3 {
245                let flags = base | (u32::from(reference) << 6);
246                let (file, st) =
247                    encode_tokens_coder_cap(pixels, dims, backend, flags, cap, reference)
248                        .expect("encoding an in-memory image cannot fail");
249                let size = file.len();
250                if best.as_ref().is_none_or(|b| size < b.0.len()) {
251                    best = Some((file, flags, st, backend));
252                }
253            }
254        }
255    }
256    let (file, flags, mut st, backend) = best.expect("at least one candidate");
257    st.chosen_flags = flags;
258    (file, st, backend)
259}
260
261/// The image-wide literal-body predictor (ADR-004): which causal rule
262/// mode-0 bodies carry. All three decode with the same causal walk.
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub enum PredKind {
265    /// No pre-prediction (raw literal bodies).
266    None,
267    /// MED edge-sense (v1.2 FLAG_PRED_MED).
268    Med,
269    /// LOCO-I plane branch forced: pred = a + b − cc, clamped (ADR-004).
270    Plane,
271    /// Pure left: pred = left neighbor (ADR-004).
272    Left,
273}
274
275impl PredKind {
276    fn from_flags(flags: u32) -> PredKind {
277        if flags & FLAG_PRED_MED != 0 {
278            PredKind::Med
279        } else if flags & FLAG_PRED_PLANE != 0 {
280            PredKind::Plane
281        } else if flags & FLAG_PRED_LEFT != 0 {
282            PredKind::Left
283        } else {
284            PredKind::None
285        }
286    }
287
288    fn flag_bit(self) -> u32 {
289        match self {
290            PredKind::None => 0,
291            PredKind::Med => FLAG_PRED_MED,
292            PredKind::Plane => FLAG_PRED_PLANE,
293            PredKind::Left => FLAG_PRED_LEFT,
294        }
295    }
296
297    /// The predictor's value at an interior pixel from causal neighbors.
298    /// Decode-side truth; the encode-side helpers MUST match exactly.
299    fn predict(self, a: u32, b: u32, cc: u32) -> u32 {
300        match self {
301            PredKind::None | PredKind::Med => crate::mnf6::med_predict_pixel(a, b, cc),
302            PredKind::Plane => {
303                // LOCO-I plane branch, clamped into byte range (the encode-
304                // side plane_predict clamps identically).
305                (a as i64 + b as i64 - cc as i64).clamp(0, 255) as u32
306            }
307            PredKind::Left => a,
308        }
309    }
310}
311
312/// Image-wide prediction over the WHOLE image with the given rule.
313/// ENCODE-side only: neighbor reads come from TRUE pixels (the decode walk
314/// reads reconstructed pixels; reconstructed-earlier == true by the mod-256
315/// induction, so the two walks agree — reading the pred stream instead of
316/// the pixel stream chains predictions and BREAKS the identity, the bug the
317/// gradient Plane gate caught: pred(0,1) = pred(0,0) instead of left pixel).
318fn image_predict(pixels: &[u8], w: usize, h: usize, kind: PredKind) -> Vec<u8> {
319    let mut out = vec![0u8; pixels.len()];
320    for y in 0..h {
321        for x in 0..w {
322            for c in 0..3usize {
323                let i = (y * w + x) * 3 + c;
324                let pred: u32 = if x == 0 && y == 0 {
325                    127
326                } else if y == 0 {
327                    u32::from(pixels[i - 3])
328                } else if x == 0 {
329                    u32::from(pixels[i - w * 3])
330                } else {
331                    let a = u32::from(pixels[i - 3]);
332                    let b = u32::from(pixels[i - w * 3]);
333                    let cc = u32::from(pixels[i - w * 3 - 3]);
334                    kind.predict(a, b, cc)
335                };
336                out[i] = pred as u8;
337            }
338        }
339    }
340    out
341}
342
343/// ADR-008 channel-diff relabel, applied to RESIDUAL bytes in the
344/// stream's residual-domain bodies (mode-0 record bodies; token tag-0
345/// literals). Per pixel [r,g,b] → [g, r−g, b−g] mod 256 — a reversible
346/// relabel with ZERO added bytes; the decoder applies the inverse before
347/// the standard walk. `planar` relabels per-channel planes ([R][G][B] →
348/// [G][R−G][B−G]); `tokens` relabels tag-0 literal triplets in place.
349fn channel_diff_relabel(
350    stream: &[u8],
351    planar: bool,
352    tokens: bool,
353    w: u32,
354    h: u32,
355    reference: u8,
356) -> Vec<u8> {
357    if tokens {
358        // token wire: relabel tag-0 (TAG_LIT) 3-B literal pixels
359        let mut out = Vec::with_capacity(stream.len());
360        let mut pos = 0usize;
361        let npx = (w as usize) * (h as usize);
362        let mut ppos = 0usize;
363        while ppos < npx && pos < stream.len() {
364            let tag = stream[pos];
365            out.push(tag);
366            pos += 1;
367            match tag {
368                tokens::TAG_LITERAL => {
369                    let px = &stream[pos..pos + 3];
370                    let (refc, a, b) = ref_pick(px, reference);
371                    out.push(refc);
372                    out.push(a.wrapping_sub(refc));
373                    out.push(b.wrapping_sub(refc));
374                    pos += 3;
375                    ppos += 1;
376                }
377                tokens::TAG_MATCH => {
378                    if pos + 5 > stream.len() {
379                        panic!("cd: token match truncated (pos {pos}/{})", stream.len());
380                    }
381                    let len = u32::from(stream[pos + 4]) as usize;
382                    out.extend_from_slice(&stream[pos..pos + 5]);
383                    pos += 5;
384                    ppos += len;
385                }
386                tokens::TAG_NEAR => {
387                    if pos + 3 > stream.len() {
388                        panic!("cd: token near truncated (pos {pos}/{})", stream.len());
389                    }
390                    let len = u32::from(stream[pos + 2]) as usize;
391                    out.extend_from_slice(&stream[pos..pos + 3]);
392                    pos += 3;
393                    ppos += len;
394                }
395                other => panic!(
396                    "token tag {other} outside the recorded grammar (pos {pos}/{}, ppos {ppos}/{npx})",
397                    stream.len()
398                ),
399            }
400        }
401        debug_assert_eq!(ppos, npx);
402        out
403    } else if planar {
404        // planar record bodies: per-tile [R plane][G plane][B plane] →
405        // [G plane][R−G plane][B−G plane]
406        let mut out = Vec::with_capacity(stream.len());
407        let mut pos = 0usize;
408        let cols = (w as usize).div_ceil(TILE_SIZE as usize);
409        let tile_count =
410            (w as usize).div_ceil(TILE_SIZE as usize) * (h as usize).div_ceil(TILE_SIZE as usize);
411        for i in 0..tile_count {
412            let (row, col) = (i / cols, i % cols);
413            let tw = (TILE_SIZE as usize).min(w as usize - col * TILE_SIZE as usize);
414            let th = (TILE_SIZE as usize).min(h as usize - row * TILE_SIZE as usize);
415            let nb = tw * th * 3;
416            let mode = stream[pos];
417            out.push(mode);
418            pos += 1;
419            if mode == 0 {
420                let body = &stream[pos..pos + nb];
421                let third = nb / 3;
422                let (pr, pg, pb) = (&body[..third], &body[third..2 * third], &body[2 * third..]);
423                match reference {
424                    1 => {
425                        out.extend_from_slice(pg);
426                        for (r, g) in pr.iter().zip(pg) {
427                            out.push(r.wrapping_sub(*g));
428                        }
429                        for (b, g) in pb.iter().zip(pg) {
430                            out.push(b.wrapping_sub(*g));
431                        }
432                    }
433                    2 => {
434                        out.extend_from_slice(pr);
435                        for (g, r) in pg.iter().zip(pr) {
436                            out.push(g.wrapping_sub(*r));
437                        }
438                        for (b, r) in pb.iter().zip(pr) {
439                            out.push(b.wrapping_sub(*r));
440                        }
441                    }
442                    3 => {
443                        out.extend_from_slice(pb);
444                        for (r, b) in pr.iter().zip(pb) {
445                            out.push(r.wrapping_sub(*b));
446                        }
447                        for (g, b) in pg.iter().zip(pb) {
448                            out.push(g.wrapping_sub(*b));
449                        }
450                    }
451                    _ => unreachable!("reference constrained"),
452                }
453                pos += nb;
454            } else if mode == 1 {
455                const META: usize = 4;
456                out.extend_from_slice(&stream[pos..pos + META + nb]);
457                pos += META + nb;
458            } else if mode == 2 {
459                const META: usize = 5;
460                out.extend_from_slice(&stream[pos..pos + META + nb]);
461                pos += META + nb;
462            } else if mode == 3 {
463                let np = nb / 3;
464                let n9 = (np * 9).div_ceil(8);
465                out.extend_from_slice(&stream[pos..pos + np + 2 * n9]);
466                pos += np + 2 * n9;
467            } else if mode == 4 || mode == 5 {
468                let meta = if mode == 5 { 5 } else { 4 };
469                out.extend_from_slice(&stream[pos..pos + meta + nb]);
470                pos += meta + nb;
471            } else {
472                panic!(
473                    "mode {mode} outside the recorded grammar (tile {i} pos {pos}/{})",
474                    stream.len()
475                );
476            }
477        }
478        debug_assert_eq!(pos, stream.len());
479        out
480    } else {
481        // interleaved record bodies: relabel every mode-0 body's per-pixel
482        // triplets in place
483        let mut out = Vec::with_capacity(stream.len());
484        let mut pos = 0usize;
485        let cols = (w as usize).div_ceil(TILE_SIZE as usize);
486        let tile_count =
487            (w as usize).div_ceil(TILE_SIZE as usize) * (h as usize).div_ceil(TILE_SIZE as usize);
488        for i in 0..tile_count {
489            let (row, col) = (i / cols, i % cols);
490            let tw = (TILE_SIZE as usize).min(w as usize - col * TILE_SIZE as usize);
491            let th = (TILE_SIZE as usize).min(h as usize - row * TILE_SIZE as usize);
492            let nb = tw * th * 3;
493            let mode = stream[pos];
494            out.push(mode);
495            pos += 1;
496            if mode == 0 {
497                for k in 0..nb / 3 {
498                    let t = &stream[pos + k * 3..pos + k * 3 + 3];
499                    let (refc, a, b) = ref_pick(t, reference);
500                    out.push(refc);
501                    out.push(a.wrapping_sub(refc));
502                    out.push(b.wrapping_sub(refc));
503                }
504                pos += nb;
505            } else {
506                // copy the remaining record bytes for this tile verbatim
507                // (after the mode byte: idx u32 | idx+transform | y+co9+cg9)
508                let extra = match mode {
509                    1 | 4 => 4 + nb,
510                    2 | 5 => 5 + nb,
511                    3 => {
512                        let np = nb / 3;
513                        (np * 9).div_ceil(8) * 2 + np
514                    }
515                    other => panic!(
516                        "mode {other} outside the recorded grammar (pos {pos}/{})",
517                        stream.len()
518                    ),
519                };
520                out.extend_from_slice(&stream[pos..pos + extra]);
521                pos += extra;
522            }
523        }
524        debug_assert_eq!(pos, stream.len());
525        out
526    }
527}
528
529/// The per-triplet channel pick for a reference id: returns (ref, a, b)
530/// in the wire emission order (ref first, then the pair).
531fn ref_pick(px: &[u8], reference: u8) -> (u8, u8, u8) {
532    let (r, g, b) = (px[0], px[1], px[2]);
533    match reference {
534        1 => (g, r, b),
535        2 => (r, g, b),
536        3 => (b, r, g),
537        _ => (0, 0, 0),
538    }
539}
540
541/// Planar inverse reorder: the wire's [ref][a−ref][b−ref] planes (the
542/// wire emission order per reference) → [R][G][B] planes.
543fn unpermute_wire_to_planar(wire: &[u8], reference: u8) -> Vec<u8> {
544    let third = wire.len() / 3;
545    let (pref, pa, pb) = (&wire[..third], &wire[third..2 * third], &wire[2 * third..]);
546    match reference {
547        // G: wire = [G][R−G][B−G]
548        1 => {
549            let mut out: Vec<u8> = pa
550                .iter()
551                .zip(pref)
552                .map(|(&da, &g)| da.wrapping_add(g))
553                .collect();
554            out.extend_from_slice(pref);
555            out.extend(
556                pb.iter()
557                    .zip(pref)
558                    .map(|(&db, &g)| db.wrapping_add(g))
559                    .collect::<Vec<u8>>(),
560            );
561            out
562        }
563        // R: wire = [R][G−R][B−R]
564        2 => {
565            let mut out = pref.to_vec();
566            out.extend(
567                pa.iter()
568                    .zip(pref)
569                    .map(|(&da, &rf)| da.wrapping_add(rf))
570                    .collect::<Vec<u8>>(),
571            );
572            out.extend(
573                pb.iter()
574                    .zip(pref)
575                    .map(|(&db, &rf)| db.wrapping_add(rf))
576                    .collect::<Vec<u8>>(),
577            );
578            out
579        }
580        // B: wire = [B][R−B][G−B]
581        3 => {
582            let mut out: Vec<u8> = pa
583                .iter()
584                .zip(pref)
585                .map(|(&da, &bf)| da.wrapping_add(bf))
586                .collect();
587            out.extend(
588                pb.iter()
589                    .zip(pref)
590                    .map(|(&db, &bf)| db.wrapping_add(bf))
591                    .collect::<Vec<u8>>(),
592            );
593            out.extend_from_slice(pref);
594            out
595        }
596        _ => {
597            let mut out = pref.to_vec();
598            out.extend_from_slice(pa);
599            out.extend_from_slice(pb);
600            out
601        }
602    }
603}
604
605/// Interleaved inverse: one wire pixel [ref, a−ref, b−ref] → [r, g, b].
606fn push_inverse_pixel(out: &mut Vec<u8>, refc: u8, da: u8, db: u8, reference: u8) {
607    match reference {
608        1 => {
609            out.push(da.wrapping_add(refc)); // R
610            out.push(refc); // G
611            out.push(db.wrapping_add(refc)); // B
612        }
613        2 => {
614            out.push(refc); // R
615            out.push(da.wrapping_add(refc)); // G
616            out.push(db.wrapping_add(refc)); // B
617        }
618        3 => {
619            out.push(da.wrapping_add(refc)); // R
620            out.push(db.wrapping_add(refc)); // G
621            out.push(refc); // B
622        }
623        _ => out.extend_from_slice(&[0, 0, 0]),
624    }
625}
626
627/// The decode-side inverse relabel: [G, R−G, B−G] → [R, G, B] across the
628/// same body classes, with explicit bounds checks — malformed streams are
629/// errors, never panics (truncated body/section, unknown mode/tag,
630/// trailing bytes).
631fn channel_diff_unrelabel(
632    stream: &[u8],
633    planar: bool,
634    tokens: bool,
635    w: u32,
636    h: u32,
637    reference: u8,
638) -> Result<Vec<u8>> {
639    if tokens {
640        let mut out = Vec::with_capacity(stream.len());
641        let mut pos = 0usize;
642        let npx = (w as usize) * (h as usize);
643        let mut ppos = 0usize;
644        while ppos < npx {
645            if pos >= stream.len() {
646                return Err(Error::fmt_msg("cd: token wire truncated"));
647            }
648            let tag = stream[pos];
649            out.push(tag);
650            pos += 1;
651            match tag {
652                tokens::TAG_LITERAL => {
653                    if pos + 3 > stream.len() {
654                        return Err(Error::fmt_msg("cd: token literal truncated"));
655                    }
656                    push_inverse_pixel(
657                        &mut out,
658                        stream[pos],
659                        stream[pos + 1],
660                        stream[pos + 2],
661                        reference,
662                    );
663                    pos += 3;
664                    ppos += 1;
665                }
666                tokens::TAG_MATCH => {
667                    if pos + 5 > stream.len() {
668                        return Err(Error::fmt_msg("cd: token match truncated"));
669                    }
670                    let len = u32::from(stream[pos + 4]) as usize;
671                    out.extend_from_slice(&stream[pos..pos + 5]);
672                    pos += 5;
673                    ppos += len;
674                }
675                tokens::TAG_NEAR => {
676                    if pos + 3 > stream.len() {
677                        return Err(Error::fmt_msg("cd: token near truncated"));
678                    }
679                    let len = u32::from(stream[pos + 2]) as usize;
680                    out.extend_from_slice(&stream[pos..pos + 3]);
681                    pos += 3;
682                    ppos += len;
683                }
684                _ => return Err(Error::fmt_msg("token wire: unknown tag")),
685            }
686        }
687        if pos != stream.len() {
688            return Err(Error::fmt_msg("cd: token wire trailing bytes"));
689        }
690        if ppos != npx {
691            return Err(Error::fmt_msg("cd: token pixel accounting"));
692        }
693        Ok(out)
694    } else {
695        let cols = (w as usize).div_ceil(TILE_SIZE as usize);
696        let tile_count =
697            (w as usize).div_ceil(TILE_SIZE as usize) * (h as usize).div_ceil(TILE_SIZE as usize);
698        let mut out = Vec::with_capacity(stream.len());
699        let mut pos = 0usize;
700        for i in 0..tile_count {
701            let (row, col) = (i / cols, i % cols);
702            let tw = (TILE_SIZE as usize).min(w as usize - col * TILE_SIZE as usize);
703            let th = (TILE_SIZE as usize).min(h as usize - row * TILE_SIZE as usize);
704            let nb = tw * th * 3;
705            if pos >= stream.len() {
706                return Err(Error::fmt_msg("cd: record stream truncated"));
707            }
708            let mode = stream[pos];
709            out.push(mode);
710            pos += 1;
711            match mode {
712                0 => {
713                    #[allow(clippy::collapsible_if)]
714                    if pos + nb > stream.len() {
715                        return Err(Error::fmt_msg("cd: mode-0 body truncated"));
716                    }
717                    if planar {
718                        let wire_planes = &stream[pos..pos + nb];
719                        out.extend_from_slice(&unpermute_wire_to_planar(wire_planes, reference));
720                    } else {
721                        for k in 0..nb / 3 {
722                            push_inverse_pixel(
723                                &mut out,
724                                stream[pos + k * 3],
725                                stream[pos + k * 3 + 1],
726                                stream[pos + k * 3 + 2],
727                                reference,
728                            );
729                        }
730                    }
731                    pos += nb;
732                }
733                1 | 4 => {
734                    if pos + 4 + nb > stream.len() {
735                        return Err(Error::fmt_msg("cd: mode truncated"));
736                    }
737                    out.extend_from_slice(&stream[pos..pos + 4 + nb]);
738                    pos += 4 + nb;
739                }
740                2 | 5 => {
741                    if pos + 5 + nb > stream.len() {
742                        return Err(Error::fmt_msg("cd: mode truncated"));
743                    }
744                    out.extend_from_slice(&stream[pos..pos + 5 + nb]);
745                    pos += 5 + nb;
746                }
747                3 => {
748                    let np = nb / 3;
749                    let n9 = (np * 9).div_ceil(8);
750                    if pos + np + 2 * n9 > stream.len() {
751                        return Err(Error::fmt_msg("cd: mode-3 truncated"));
752                    }
753                    out.extend_from_slice(&stream[pos..pos + np + 2 * n9]);
754                    pos += np + 2 * n9;
755                }
756                _ => return Err(Error::fmt_msg(format!("cd: mode {mode}"))),
757            }
758        }
759        if pos != stream.len() {
760            return Err(Error::fmt_msg("cd: trailing stream bytes"));
761        }
762        Ok(out)
763    }
764}
765/// Token-arm stream builder (ADR-003): MED residual plane → token wire →
766/// container coder, with explicit matcher depth (ADR-005: the pick
767/// measures the v1.7 shallow arm AND the deep-chain arm; the pick
768/// absorbs the choice). ADR-008: when `cd`, tag-0 literals carry the
769/// channel-diff relabel (applied before compression).
770fn encode_tokens_coder_cap(
771    pixels: &[u8],
772    dims: Dims,
773    backend: u32,
774    flags: u32,
775    cap: usize,
776    reference: u8,
777) -> Result<(Vec<u8>, MnF6Stats)> {
778    let med_pred = med_predict(pixels, dims.width as usize, dims.height as usize);
779    let res: Vec<u8> = pixels
780        .iter()
781        .zip(&med_pred)
782        .map(|(x, m)| x.wrapping_sub(*m))
783        .collect();
784    let (wire, ts) =
785        tokens::encode_plane_cap(&res, dims.width as usize, dims.height as usize, cap)?;
786    // ADR-008: relabel tag-0 literals before compression (REF field)
787    let wire = if reference != 0 {
788        channel_diff_relabel(&wire, false, true, dims.width, dims.height, reference)
789    } else {
790        wire
791    };
792    let payload = backend_compress(backend, &wire)?;
793    let payload_len =
794        u32::try_from(payload.len()).map_err(|_| Error::fmt_msg("payload exceeds u32"))?;
795    let mut file = Vec::with_capacity(HEADER8 + payload.len());
796    file.extend_from_slice(b"MNF8");
797    file.extend_from_slice(&dims.width.to_le_bytes());
798    file.extend_from_slice(&dims.height.to_le_bytes());
799    file.extend_from_slice(&TILE_SIZE.to_le_bytes());
800    let cols = div_ceil(dims.width, TILE_SIZE);
801    let rows = div_ceil(dims.height, TILE_SIZE);
802    let tile_count: u32 = cols
803        .checked_mul(rows)
804        .ok_or_else(|| Error::fmt_msg("too many tiles"))?;
805    file.extend_from_slice(&tile_count.to_le_bytes());
806    file.extend_from_slice(&payload_len.to_le_bytes());
807    file.extend_from_slice(&flags.to_le_bytes());
808    file.extend_from_slice(&backend.to_le_bytes());
809    file.extend_from_slice(&payload);
810    let st = MnF6Stats {
811        literal: u32::try_from(ts.literals).unwrap_or(u32::MAX),
812        ref_exact: u32::try_from(ts.matches).unwrap_or(u32::MAX),
813        ..MnF6Stats::default()
814    };
815    Ok((file, st))
816}
817
818/// One tile-stream encoder, parameterized by coder + backend — THE selection
819/// loop for MNF8 (no per-variant duplicates of this function).
820///
821/// Arm controls (both default true, plumbable for ablation):
822/// - `use_atlas`: candidate references at all (false = pure prediction arm)
823/// - `use_geom`: D8 transforms on top of exact references (false = t=0 only,
824///   the v0.5 exact-reference semantics — codec.rs pins t=0 for this case
825///   and MNF8 must match it)
826///
827/// Shared YCoCg (mode 3) and planar layout stay ON in both arms (ADR-002).
828#[allow(clippy::too_many_arguments)]
829fn encode_stream_coder(
830    pixels: &[u8],
831    dims: Dims,
832    use_atlas: bool,
833    use_geom: bool,
834    flags: u32,
835    planar: bool,
836    med: bool,
837    kind: PredKind,
838    coder: &Coder,
839    backend: u32,
840    reference: u8,
841) -> Result<(Vec<u8>, MnF6Stats)> {
842    // ADR-004: Plane/Left are PURE prediction arms (the measured gate's
843    // winning form) — no atlas candidates, no YCoCg; mode-0 bodies only.
844    let pure_pred = kind == PredKind::Plane || kind == PredKind::Left;
845    let med_pred = match kind {
846        PredKind::Med => med_predict(pixels, dims.width as usize, dims.height as usize),
847        PredKind::Plane => image_predict(
848            pixels,
849            dims.width as usize,
850            dims.height as usize,
851            PredKind::Plane,
852        ),
853        PredKind::Left => image_predict(
854            pixels,
855            dims.width as usize,
856            dims.height as usize,
857            PredKind::Left,
858        ),
859        PredKind::None => Vec::new(),
860    };
861    let tiles = split_tiles(pixels, dims);
862    let mut raw: Vec<u8> = Vec::with_capacity(tiles.len() * 774);
863    let mut stats = MnF6Stats::default();
864    let mut atlas = Atlas::new();
865
866    for (idx, tile) in tiles.iter().enumerate() {
867        let n = tile.nbytes();
868        let pack = |rgb: &[u8]| -> Vec<u8> {
869            if planar {
870                planar_pack(rgb)
871            } else {
872                rgb.to_vec()
873            }
874        };
875
876        // mode-0 literal candidate; under any ADR-004 predictor the body
877        // carries that predictor's residual (decoder reconstructs causally)
878        let lit_src: Vec<u8> = if med || pure_pred {
879            let (tw, th) = (tile.width as usize, tile.height as usize);
880            let cols_px = dims.width as usize; // med_pred is per-PIXEL flat
881            let tx = (idx % ((dims.width as usize).div_ceil(16))) * 16;
882            let ty = (idx / ((dims.width as usize).div_ceil(16))) * 16;
883            let mut src = Vec::with_capacity(n);
884            for y in 0..th {
885                let row_off = ((ty + y) * cols_px + tx) * 3;
886                src.extend_from_slice(&med_pred[row_off..row_off + tw * 3]);
887            }
888            tile.data
889                .iter()
890                .zip(&src)
891                .map(|(x, m)| x.wrapping_sub(*m))
892                .collect()
893        } else {
894            tile.data.clone()
895        };
896
897        let lit_body = pack(&lit_src);
898        let mut choice_body = lit_body.clone();
899        let mut choice_mode = 0u8;
900        let mut choice_ref: Option<(u32, u8)> = None;
901        let mut best_cost = 1 + proxy_cost(coder, &lit_body);
902
903        // shared literal candidates (both arms): YCoCg-R (mode 3).
904        // ADR-004 pure arms (Plane/Left) skip YCoCg + atlas — measured pure.
905        if !pure_pred {
906            let (cy, cco, ccg) = ycocg_forward(&tile.data);
907            let mut m3 = Vec::with_capacity(n + 160);
908            m3.extend_from_slice(&cy);
909            m3.extend_from_slice(&pack9(&cco));
910            m3.extend_from_slice(&pack9(&ccg));
911            let m3_cost = 1 + proxy_cost(coder, &m3);
912            if m3_cost < best_cost {
913                best_cost = m3_cost;
914                choice_body = m3;
915                choice_mode = 3;
916            }
917        }
918
919        // invariant hoist (review fix 7): T(tile.data) is candidate- and
920        // transform-independent — compute once per tile
921        let tile_t = modcolor_forward_iter(&tile.data);
922
923        // atlas candidates (arm-controlled; review fixes 2 + use_atlas).
924        // ADR-004 pure prediction arms exclude the atlas (measured pure).
925        if use_atlas && !pure_pred && tiles.len() > 1 {
926            let sig = tile_sig(tile.width, tile.height, &tile.data);
927            for prior in atlas.rank_by_sig(&sig, tile.width, tile.height, RANK_CANDIDATES) {
928                let (_, _, ref_rgb) = &atlas.patches[prior as usize];
929                // review fix 2: `use_geom = false` means EXACT references only
930                // (t=0), matching codec.rs's v0.5 ablation semantics; square
931                // tiles may use all 8 D8 transforms, partial tiles only the
932                // shape-keeping subgroup.
933                let transforms: &[u8] = if !use_geom {
934                    &[0]
935                } else if tile.width == tile.height {
936                    &[0, 1, 2, 3, 4, 5, 6, 7]
937                } else {
938                    &[0, 1, 2, 3]
939                };
940                for &t in transforms {
941                    let base = if t == 0 {
942                        ref_rgb.clone()
943                    } else {
944                        apply_transform(t, tile.width, tile.height, ref_rgb)
945                    };
946                    let mut r = vec![0u8; n];
947                    for i in 0..n {
948                        r[i] = tile.data[i].wrapping_sub(base[i]);
949                    }
950                    let body = pack(&r);
951                    // metadata: mode byte + u32 idx + optional transform byte
952                    let meta: u64 = if t == 0 { 5 } else { 6 };
953                    let cost = meta + proxy_cost(coder, &body);
954                    if cost < best_cost {
955                        best_cost = cost;
956                        choice_body = body;
957                        choice_mode = if t == 0 { 1 } else { 2 };
958                        choice_ref = Some((prior, t));
959                    }
960                    // color-domain candidate (MNF7 modes 4/5). Review fix 3:
961                    // the wire carries NOTHING extra for color modes — the
962                    // mode byte already tells the decoder which domain to
963                    // decode the body in. The phantom `+ 1` that the old
964                    // cost model charged made the encoder under-select
965                    // color modes (contaminating any color-mode ablation).
966                    let base_t = modcolor_forward_iter(&base);
967                    let mut r_c = vec![0u8; n];
968                    for i in 0..n {
969                        r_c[i] = tile_t[i].wrapping_sub(base_t[i]);
970                    }
971                    let body_c = pack(&r_c);
972                    let cost_c = meta + proxy_cost(coder, &body_c);
973                    if cost_c < best_cost {
974                        best_cost = cost_c;
975                        choice_body = body_c;
976                        choice_mode = if t == 0 { 4 } else { 5 };
977                        choice_ref = Some((prior, t));
978                    }
979                }
980            }
981        }
982
983        stats.modes.push(choice_mode);
984        stats.refs.push(choice_ref);
985        match choice_mode {
986            0 => stats.literal += 1,
987            1 => stats.ref_exact += 1,
988            2 => {
989                stats.ref_geom += 1;
990                if choice_ref.map(|(_, t)| t != 0).unwrap_or(false) {
991                    stats.geom_nontrivial += 1;
992                }
993            }
994            3 => stats.ycocg_literal += 1,
995            4 => stats.ref_exact_color += 1,
996            5 => {
997                stats.ref_geom_color += 1;
998                if choice_ref.map(|(_, t)| t != 0).unwrap_or(false) {
999                    stats.geom_nontrivial += 1;
1000                }
1001            }
1002            _ => unreachable!("mode byte constrained above"),
1003        }
1004        raw.push(choice_mode);
1005        if let Some((p, t)) = choice_ref {
1006            raw.extend_from_slice(&p.to_le_bytes());
1007            if t != 0 {
1008                raw.push(t);
1009            }
1010        }
1011        raw.extend_from_slice(&choice_body);
1012        atlas.insert(tile);
1013    }
1014
1015    // ADR-008: relabel mode-0 residual bodies before compression (REF)
1016    let raw = if reference != 0 {
1017        channel_diff_relabel(&raw, planar, false, dims.width, dims.height, reference)
1018    } else {
1019        raw
1020    };
1021    let payload = backend_compress(backend, &raw)?;
1022    let payload_len =
1023        u32::try_from(payload.len()).map_err(|_| Error::fmt_msg("payload exceeds u32"))?; // review: no lossy cast
1024    let mut file = Vec::with_capacity(HEADER8 + payload.len());
1025    file.extend_from_slice(b"MNF8");
1026    file.extend_from_slice(&dims.width.to_le_bytes());
1027    file.extend_from_slice(&dims.height.to_le_bytes());
1028    file.extend_from_slice(&TILE_SIZE.to_le_bytes());
1029    let tile_count = u32::try_from(tiles.len()).map_err(|_| Error::fmt_msg("too many tiles"))?;
1030    file.extend_from_slice(&tile_count.to_le_bytes());
1031    file.extend_from_slice(&payload_len.to_le_bytes());
1032    file.extend_from_slice(&flags.to_le_bytes());
1033    file.extend_from_slice(&backend.to_le_bytes());
1034    file.extend_from_slice(&payload);
1035    Ok((file, stats))
1036}
1037
1038// --- decoding ----------------------------------------------------------------
1039
1040/// Decode MNF8. Record walk is MNF7's (shared `decode_record_walk`); the
1041/// container fields differ (32-byte header, backend id) and the coder
1042/// dispatch runs on the backend field. Every non-shipped backend id is an
1043/// ERROR (never a panic path) — review fix 1: backend 2 previously fell
1044/// into `unreachable!()` after passing the too-loose header checks.
1045pub fn decode_mnf8(buf: &[u8]) -> Result<(Dims, Vec<u8>, u32)> {
1046    decode_mnf8_with_limits(buf, &crate::mnf6::DecodeLimits::default())
1047}
1048
1049/// Decode MNF8 with configurable ceilings (library API; same semantics as
1050/// decode_mnf7_with_limits).
1051pub fn decode_mnf8_with_limits(
1052    buf: &[u8],
1053    lim: &crate::mnf6::DecodeLimits,
1054) -> Result<(Dims, Vec<u8>, u32)> {
1055    if buf.len() < HEADER8 {
1056        return Err(Error::fmt_msg("short"));
1057    }
1058    if buf[0..4] != *b"MNF8" {
1059        return Err(Error::fmt_msg("magic"));
1060    }
1061    let u32le = |o: usize| u32::from_le_bytes(buf[o..o + 4].try_into().unwrap());
1062    let width = u32le(4);
1063    let height = u32le(8);
1064    if width == 0 || height == 0 {
1065        return Err(Error::fmt_msg("dims"));
1066    }
1067    if width as u64 * height as u64 > lim.max_pixels {
1068        return Err(Error::Limits {
1069            which: "max_pixels",
1070            limit: lim.max_pixels,
1071            actual: width as u64 * height as u64,
1072        });
1073    }
1074    if u32le(12) != TILE_SIZE {
1075        return Err(Error::fmt_msg("tilesize"));
1076    }
1077    let cols = div_ceil(width, TILE_SIZE);
1078    let rows = div_ceil(height, TILE_SIZE);
1079    let tile_count = u32le(16);
1080    if tile_count != cols * rows {
1081        return Err(Error::fmt_msg("tilecount"));
1082    }
1083    if tile_count > lim.max_tiles {
1084        return Err(Error::Limits {
1085            which: "max_tiles",
1086            limit: u64::from(lim.max_tiles),
1087            actual: u64::from(tile_count),
1088        });
1089    }
1090    let payload_len = u32le(20);
1091    if payload_len > (1 << 31) - 1 {
1092        return Err(Error::fmt_msg("payloadlen"));
1093    }
1094    if buf.len() != HEADER8 + payload_len as usize {
1095        return Err(Error::fmt_msg("filelen"));
1096    }
1097    let flags = u32le(24);
1098    // MNF8 owns bits 2-5 (PRED_MED, TOKENS, PRED_PLANE, PRED_LEFT); MNF6/7
1099    // do not — wider mask here
1100    if flags & FLAGS_RESERVED8 != 0 {
1101        return Err(Error::fmt_msg("reserved-flags"));
1102    }
1103    let token_wire = flags & FLAG_TOKENS != 0;
1104    // ADR-003: the token plane is interleaved RGB8 by definition — PLANAR
1105    // carries no meaning there and no encoder emits the combination.
1106    if token_wire && flags & FLAG_PLANAR != 0 {
1107        return Err(Error::fmt_msg("tokens+planar are exclusive"));
1108    }
1109    // ADR-004: at most ONE image-wide predictor bit may be set.
1110    let pred_count = u32::from(flags & FLAG_PRED_MED != 0)
1111        + u32::from(flags & FLAG_PRED_PLANE != 0)
1112        + u32::from(flags & FLAG_PRED_LEFT != 0);
1113    if pred_count > 1 {
1114        return Err(Error::fmt_msg("multiple predictors selected"));
1115    }
1116    // ADR-003: the token grammar's payload is always MED-residual; a token
1117    // file claiming a different predictor misdescribes its payload.
1118    if token_wire && flags & (FLAG_PRED_PLANE | FLAG_PRED_LEFT) != 0 {
1119        return Err(Error::fmt_msg("tokens+plane/left predictor exclusive"));
1120    }
1121    let backend = u32le(28);
1122    if !matches!(
1123        backend,
1124        BACKEND_ZLIB | BACKEND_ZSTD19 | BACKEND_RESERVED2 | BACKEND_RESERVED3
1125    ) {
1126        return Err(Error::fmt_msg(format!("unknown backend {backend}")));
1127    }
1128    // ADR-006 measured DENIED: rANS arm lost to zstd-19 on every corpus
1129    // file with honest model transport (docs/mnf9-rans-arm.md §verdict).
1130    // Keep backend 3 reject-on-decode until an arm wins honestly.
1131    if matches!(backend, BACKEND_RESERVED2 | BACKEND_RESERVED3) {
1132        return Err(Error::fmt_msg(format!(
1133            "reserved backend {backend} (not shipped)"
1134        )));
1135    }
1136    let planar = flags & FLAG_PLANAR != 0;
1137
1138    // Decompression bound — degenerate-shape-honest (review fix 5):
1139    //   generic record worst case needs 6 metadata bytes + the tile's own
1140    //   body (tw*th*3); summed over tiles that is 6*N + 3WH, NOT N * 833
1141    //   (the old full-tile assumption turned a 1x67108864 image's cap into
1142    //   ~3.5 GB against a ~200 MB true maximum).
1143    //   Mode-3 records: 1 + tw*th + 2 * ceil(9*(tw*th)/8) ≤ 1 + tw*th + 2 * (tw*th*9+7)/8
1144    //   ≤ 1 + 7/4 * tw*th + small; bound it as 1 + (7/4)*np + 2 with np <=
1145    //   TILE_SIZE^2 — the same conservative per-tile add-on as before.
1146    //   Token wire (ADR-003; bound from the token path's own worst case, the
1147    //   same lesson as mode 3): every token covers >= 1 px at <= 4 B
1148    //   (tag + 3), so the wire is <= 4*npx — which EXCEEDS the record bound
1149    //   on large all-literal planes (4npx > 3npx + 6N + small once npx is
1150    //   big), so the token flag gets its own limit rather than inheriting
1151    //   the record one.
1152    let px_bytes_total: u64 = width as u64 * height as u64 * 3;
1153    let limit = if token_wire {
1154        (4 * width as u64)
1155            .checked_mul(height as u64)
1156            .ok_or_else(|| Error::fmt_msg("decompressed size overflow"))?
1157    } else {
1158        let mode3_max: u64 = 1
1159            + (TILE_SIZE * TILE_SIZE) as u64
1160            + 2 * ((TILE_SIZE * TILE_SIZE) as u64 * 9 + 7) / 8
1161            + 2;
1162        px_bytes_total
1163            .checked_add(6 * tile_count as u64)
1164            .and_then(|l| l.checked_add(2 * mode3_max + 2))
1165            .ok_or_else(|| Error::fmt_msg("decompressed size overflow"))?
1166    };
1167
1168    let payload = &buf[HEADER8..];
1169    let raw = match backend {
1170        BACKEND_ZLIB | BACKEND_ZSTD19 => backend_decompress(backend, payload, limit)?,
1171        other => return Err(Error::fmt_msg(format!("unsupported backend {other}"))),
1172    };
1173
1174    // ADR-008: the channel-diff inverse applies to the decompressed
1175    // stream BEFORE any walk (mode-0 bodies / tag-0 literals relabeled
1176    // back to [r,g,b]); the guard walk rejects truncations/unknown modes
1177    // with errors, never panics.
1178    let reference = cd_reference(flags);
1179    let raw = if reference != 0 {
1180        channel_diff_unrelabel(&raw, planar, token_wire, width, height, reference)?
1181    } else {
1182        raw
1183    };
1184    let pixels = if token_wire {
1185        // ADR-003 token payload: wire → residual plane → causal MED invert.
1186        // decode_plane validates the wire structurally (tags, ranges, exact
1187        // plane size) and rejects trailing bytes by construction.
1188        let res = tokens::decode_plane(&raw, width as usize, height as usize)?;
1189        pred_residual_invert(&res, width as usize, height as usize, PredKind::Med)
1190    } else {
1191        let kind = PredKind::from_flags(flags);
1192        // ADR-004 routing at decode: Med keeps the frozen MNF7 walker (its
1193        // in-walker MED branch); None = raw bodies; Plane/Left = pure
1194        // prediction arms (mode-0-only streams) inverted by the ADR-004 walk.
1195        match kind {
1196            PredKind::None => decode_record_walk(&raw, width, height, tile_count, planar, false)?,
1197            PredKind::Med => decode_record_walk(&raw, width, height, tile_count, planar, true)?,
1198            other => decode_pred_walk(&raw, width, height, tile_count, planar, other)?,
1199        }
1200    };
1201    Ok((Dims { width, height }, pixels, backend))
1202}
1203
1204/// Causal invert of an image-wide predictor residual plane. Generalizes the
1205/// token payload's MED inversion to every PredKind (ADR-004); reconstruction
1206/// = this predictor's causal estimate + residual bytes, exact by the
1207/// mod-256 induction.
1208fn pred_residual_invert(res: &[u8], w: usize, h: usize, kind: PredKind) -> Vec<u8> {
1209    let mut out = vec![0u8; w * h * 3];
1210    for y in 0..h {
1211        for x in 0..w {
1212            for c in 0..3usize {
1213                let i = (y * w + x) * 3 + c;
1214                let pred: u32 = if x == 0 && y == 0 {
1215                    127
1216                } else if y == 0 {
1217                    u32::from(out[i - 3])
1218                } else if x == 0 {
1219                    u32::from(out[i - w * 3])
1220                } else {
1221                    let a = u32::from(out[i - 3]);
1222                    let b = u32::from(out[i - w * 3]);
1223                    let cc = u32::from(out[i - w * 3 - 3]);
1224                    kind.predict(a, b, cc)
1225                };
1226                out[i] = (pred + u32::from(res[i])).wrapping_rem_euclid(256) as u8;
1227            }
1228        }
1229    }
1230    out
1231}
1232
1233/// ADR-004 decode walk for Plane/Left files: record layout is MNF7's, but
1234/// mode-0 literal bodies carry PRED_PLANE/PRED_LEFT residuals instead of
1235/// MED ones. Structure mirrors decode_record_walk's mode-0 branch (per-
1236/// tile causal context: in-tile reads come from the in-flight recon,
1237/// out-of-tile from `pixels` — the audit-bug discipline); every other
1238/// record mode is unchanged from MNF7's walker.
1239fn decode_pred_walk(
1240    raw: &[u8],
1241    width: u32,
1242    height: u32,
1243    tile_count: u32,
1244    planar: bool,
1245    kind: PredKind,
1246) -> Result<Vec<u8>> {
1247    // delegate the record structure to the shared walker by REUSING its
1248    // record walk with med=true (BODIES are residuals of SOME predictor),
1249    // then inverting per-tile — but the walker's med inversion happens
1250    // IN-WALKER. So instead: run the walker with med=false (bodies stay
1251    // raw residuals), collect per-tile residual bodies… the walker
1252    // reconstructs directly; simplest correct path: reuse the walker only
1253    // for its record framing by a full re-walk here (mode 0 inverted with
1254    // `kind`; other modes identically to the walker).
1255    let cols = div_ceil(width, TILE_SIZE);
1256    let mut pixels = vec![0u8; width as usize * height as usize * 3];
1257    let mut decoded: Vec<(u32, u32, Vec<u8>)> = Vec::with_capacity(tile_count as usize);
1258    let mut ip = 0usize;
1259    for i in 0..tile_count {
1260        let row = i / cols;
1261        let col = i % cols;
1262        let tw = TILE_SIZE.min(width - col * TILE_SIZE);
1263        let th = TILE_SIZE.min(height - row * TILE_SIZE);
1264        let n = (tw * th * 3) as usize;
1265        if ip >= raw.len() {
1266            return Err(Error::fmt_msg(format!("eof at tile {i}")));
1267        }
1268        let mode = raw[ip];
1269        ip += 1;
1270        let tile = match mode {
1271            0 => {
1272                if ip + n > raw.len() {
1273                    return Err(Error::fmt_msg("body"));
1274                }
1275                let body = raw[ip..ip + n].to_vec();
1276                ip += n;
1277                let body = if planar {
1278                    crate::mnf6::planar_unpack(&body)
1279                } else {
1280                    body
1281                };
1282                // per-tile causal invert with the ADR-004 predictor (same
1283                // context discipline as the walker's MED branch)
1284                let mut recon = vec![0u8; n];
1285                let gy0 = row as usize * TILE_SIZE as usize;
1286                let gx0 = col as usize * TILE_SIZE as usize;
1287                for y in 0..th as usize {
1288                    for x in 0..tw as usize {
1289                        for c in 0..3usize {
1290                            let k = (y * tw as usize + x) * 3 + c;
1291                            let gy = gy0 + y;
1292                            let gx = gx0 + x;
1293                            let tl = |ggy: usize, ggx: usize| -> u32 {
1294                                if ggy >= gy0 && ggx >= gx0 {
1295                                    u32::from(
1296                                        recon[((ggy - gy0) * tw as usize + (ggx - gx0)) * 3 + c],
1297                                    )
1298                                } else {
1299                                    u32::from(pixels[(ggy * width as usize + ggx) * 3 + c])
1300                                }
1301                            };
1302                            let pred: u32 = if gy == 0 && gx == 0 {
1303                                127
1304                            } else if gy == 0 {
1305                                if gx - 1 < gx0 {
1306                                    u32::from(pixels[(gy * width as usize + gx - 1) * 3 + c])
1307                                } else {
1308                                    u32::from(recon[(y * tw as usize + x - 1) * 3 + c])
1309                                }
1310                            } else if gx == 0 {
1311                                if gy - 1 < gy0 {
1312                                    u32::from(pixels[((gy - 1) * width as usize + gx) * 3 + c])
1313                                } else {
1314                                    u32::from(recon[((y - 1) * tw as usize + x) * 3 + c])
1315                                }
1316                            } else {
1317                                let a = tl(gy, gx - 1);
1318                                let b = tl(gy - 1, gx);
1319                                let cc = tl(gy - 1, gx - 1);
1320                                kind.predict(a, b, cc)
1321                            };
1322                            recon[k] = (pred + u32::from(body[k])).wrapping_rem_euclid(256) as u8;
1323                        }
1324                    }
1325                }
1326                recon
1327            }
1328            _ => {
1329                // non-literal modes are UNCHANGED MNF7 records under any
1330                // ADR-004 predictor (predictor applies to literal bodies
1331                // only) — a Plane/Left file may still carry them whenever
1332                // they measured better per tile.
1333                return Err(Error::fmt_msg(format!(
1334                    "tile {i}: mode {mode} under PRED_PLANE/LEFT — not yet admitted"
1335                )));
1336            }
1337        };
1338        decoded.push((tw, th, tile.clone()));
1339        for y_ in 0..th as usize {
1340            let py = row as usize * TILE_SIZE as usize + y_;
1341            for x_ in 0..tw as usize {
1342                let pxx = col as usize * TILE_SIZE as usize + x_;
1343                let d = (py * width as usize + pxx) * 3;
1344                let s = (y_ * tw as usize + x_) * 3;
1345                pixels[d..d + 3].copy_from_slice(&tile[s..s + 3]);
1346            }
1347        }
1348    }
1349    if ip != raw.len() {
1350        return Err(Error::fmt_msg("extra"));
1351    }
1352    Ok(pixels)
1353}
1354
1355#[cfg(test)]
1356mod gate30 {
1357    use super::*;
1358    use crate::mnf6::Coder;
1359    use std::path::Path;
1360    fn load(name: &str) -> (Dims, Vec<u8>) {
1361        let p = format!("{}/testdata/{}", env!("CARGO_MANIFEST_DIR"), name);
1362        let img = image::open(Path::new(&p))
1363            .unwrap_or_else(|e| panic!("{name}: {e}"))
1364            .to_rgb8();
1365        (
1366            Dims {
1367                width: img.width(),
1368                height: img.height(),
1369            },
1370            img.into_raw(),
1371        )
1372    }
1373    // Full-corpus real-wire gate (837 s): #[ignore]d for CI; run explicitly
1374    // when touching the predictor arms. PINS below run always.
1375    #[test]
1376    #[ignore]
1377    fn gate_all_arms_real_wire() {
1378        let files = [
1379            "gradient_320x240.png",
1380            "noise_256x256.png",
1381            "repeat_256x256.png",
1382            "screenshot_640x400.png",
1383            "solid_256x256.png",
1384            "heldout/blob_224x160.png",
1385            "heldout/blocks_192x128.png",
1386            "heldout/document_200x280.png",
1387            "heldout/plaid_192.png",
1388            "heldout/poster_208x144.png",
1389            "heldout-photos/autumn_160x120_s3100006.png",
1390            "heldout-photos/dusk_160x120_s3100003.png",
1391            "heldout-photos/dusk_224x160_s3100002.png",
1392            "heldout-photos/forest_192x144_s3100001.png",
1393            "heldout-photos/forest_224x160_s3100005.png",
1394            "heldout-photos/forest_224x160_s3100008.png",
1395            "heldout-photos/ocean_192x144_s3100007.png",
1396            "heldout-photos/sand_192x144_s3100004.png",
1397            "heldout-photos/slate_160x120_s3100009.png",
1398            "heldout-photos/terrain_160x120_s3100000.png",
1399            "photos/autumn_192x144_s900001.png",
1400            "photos/dusk_160x120_s900003.png",
1401            "photos/forest_224x160_s900002.png",
1402            "photos/ocean_192x144_s900007.png",
1403            "photos/sand_160x120_s900000.png",
1404            "photos/slate_192x144_s900004.png",
1405            "photos/slate_224x160_s900008.png",
1406            "photos/stone_160x120_s900006.png",
1407            "photos/terrain_160x120_s900009.png",
1408            "photos/terrain_224x160_s900005.png",
1409        ];
1410        let mut old_tot = 0usize;
1411        let mut new_tot = 0usize;
1412        let mut fires = 0usize;
1413        for name in files {
1414            let (d, px) = load(name);
1415            // OLD pick (v1.6.0 arms: None/Med × backends via encode paths + tokens)
1416            let mut old = usize::MAX;
1417            for kind in [PredKind::None, PredKind::Med] {
1418                for (backend, coder) in [
1419                    (BACKEND_ZSTD19, Coder::Zstd(19)),
1420                    (BACKEND_ZLIB, Coder::Zlib),
1421                ] {
1422                    for planar in [false, true] {
1423                        let med = kind == PredKind::Med;
1424                        let (file, _) = encode_stream_coder(
1425                            &px,
1426                            d,
1427                            true,
1428                            true,
1429                            kind.flag_bit() | if planar { FLAG_PLANAR } else { 0 },
1430                            planar,
1431                            med,
1432                            kind,
1433                            &coder,
1434                            backend,
1435                            0,
1436                        )
1437                        .unwrap();
1438                        // tokens arm
1439                        let _ = &file;
1440                        old = old.min(file.len());
1441                    }
1442                }
1443            }
1444            for (backend, _coder) in [
1445                (BACKEND_ZSTD19, Coder::Zstd(19)),
1446                (BACKEND_ZLIB, Coder::Zlib),
1447            ] {
1448                let (tw, _) =
1449                    encode_tokens_coder_cap(&px, d, backend, FLAG_PRED_MED | FLAG_TOKENS, 16, 0)
1450                        .unwrap();
1451                old = old.min(tw.len());
1452            }
1453            // NEW pick: + plane/left pure arms
1454            let mut new_ = old;
1455            let mut new_win = String::new();
1456            for kind in [PredKind::Plane, PredKind::Left] {
1457                for (backend, coder) in [
1458                    (BACKEND_ZSTD19, Coder::Zstd(19)),
1459                    (BACKEND_ZLIB, Coder::Zlib),
1460                ] {
1461                    let (file, _) = encode_stream_coder(
1462                        &px,
1463                        d,
1464                        true,
1465                        true,
1466                        kind.flag_bit(),
1467                        false,
1468                        false,
1469                        kind,
1470                        &coder,
1471                        backend,
1472                        0,
1473                    )
1474                    .unwrap();
1475                    // exact decode check on the pure arm
1476                    let (dd, out, _) =
1477                        decode_mnf8(&file).unwrap_or_else(|e| panic!("{name} {kind:?}: {e}"));
1478                    if out != px {
1479                        let pos = out
1480                            .iter()
1481                            .zip(&px)
1482                            .position(|(a, b)| a != b)
1483                            .expect("differs");
1484                        panic!(
1485                            "{name} {kind:?} NOT exact: first diff byte {pos} (pixel y={}, x={}, c={}): dec={} enc={}",
1486                            pos / 3 / d.width as usize, pos / 3 % d.width as usize, pos % 3,
1487                            out[pos], px[pos]
1488                        );
1489                    }
1490                    let _ = dd;
1491                    if file.len() < new_ {
1492                        new_ = file.len();
1493                        new_win = format!("{kind:?}-{backend}");
1494                    }
1495                }
1496            }
1497            if new_ < old {
1498                fires += 1;
1499            }
1500            old_tot += old;
1501            new_tot += new_;
1502            println!(
1503                "{name:44} old={old:>8} new={new_:>8} {:+6} {}",
1504                new_ as i64 - old as i64,
1505                new_win
1506            );
1507        }
1508        println!(
1509            "TOTAL old={old_tot} new={new_tot} delta={:+} fires={fires}",
1510            new_tot as i64 - old_tot as i64
1511        );
1512        assert!(new_tot < old_tot, "aggregate must strictly improve");
1513    }
1514
1515    #[test]
1516    fn branch_arms_roundtrip_and_pick_fires() {
1517        // ADR-004 pins (fast): (1) Plane/Left pure files decode exactly;
1518        // (2) the pick fires a predictor arm on the poster class;
1519        // (3) ties/losses keep shipping arms (never-worse).
1520        let path = format!(
1521            "{}/testdata/heldout/poster_208x144.png",
1522            env!("CARGO_MANIFEST_DIR")
1523        );
1524        let img = image::open(std::path::Path::new(&path)).unwrap().to_rgb8();
1525        let d = Dims {
1526            width: img.width(),
1527            height: img.height(),
1528        };
1529        let px = img.into_raw();
1530        let (buf, st, _) = encode_mnf8(&px, d, true, Profile::Balanced);
1531        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1532        assert_eq!(out, px, "picked arm decodes exactly");
1533        assert!(
1534            st.chosen_flags & (FLAG_PRED_PLANE | FLAG_PRED_LEFT) != 0,
1535            "poster must pick a branch arm (picked {:#x})",
1536            st.chosen_flags
1537        );
1538        // and the new pick must never exceed every old arm on this file:
1539        let (bufold, _, _) = {
1540            // construct the old v1.6.0 pick: None/Med arms + tokens, balanced
1541            let mut best: Option<(Vec<u8>, u32)> = None;
1542            for (backend, coder) in [(BACKEND_ZSTD19, Coder::Zstd(19))] {
1543                for kind in [PredKind::None, PredKind::Med] {
1544                    for planar in [false, true] {
1545                        let (f, _) = encode_stream_coder(
1546                            &px,
1547                            d,
1548                            true,
1549                            true,
1550                            kind.flag_bit() | if planar { FLAG_PLANAR } else { 0 },
1551                            planar,
1552                            kind == PredKind::Med,
1553                            kind,
1554                            &coder,
1555                            backend,
1556                            0,
1557                        )
1558                        .unwrap();
1559                        if best.as_ref().is_none_or(|b| f.len() < b.0.len()) {
1560                            best = Some((f, kind.flag_bit()));
1561                        }
1562                    }
1563                }
1564                let (t, _) =
1565                    encode_tokens_coder_cap(&px, d, backend, FLAG_PRED_MED | FLAG_TOKENS, 16, 0)
1566                        .unwrap();
1567                if best.as_ref().is_none_or(|b| t.len() < b.0.len()) {
1568                    best = Some((t, FLAG_TOKENS));
1569                }
1570            }
1571            let (f, fl) = best.unwrap();
1572            (f, fl, 0u32)
1573        };
1574        assert!(
1575            buf.len() <= bufold.len(),
1576            "new pick ({}) must not exceed the old pick ({})",
1577            buf.len(),
1578            bufold.len()
1579        );
1580    }
1581
1582    #[test]
1583    fn rejects_multiple_predictors() {
1584        // ADR-004 validation: two predictor bits set = Error, never panic.
1585        let mut f = vec![0u8; HEADER8];
1586        f[0..4].copy_from_slice(b"MNF8");
1587        f[4..8].copy_from_slice(&16u32.to_le_bytes());
1588        f[8..12].copy_from_slice(&16u32.to_le_bytes());
1589        f[12..16].copy_from_slice(&16u32.to_le_bytes());
1590        f[16..20].copy_from_slice(&1u32.to_le_bytes());
1591        f[20..24].copy_from_slice(&0u32.to_le_bytes());
1592        f[24..28].copy_from_slice(&(FLAG_PRED_MED | FLAG_PRED_PLANE).to_le_bytes());
1593        f[28..32].copy_from_slice(&BACKEND_ZSTD19.to_le_bytes());
1594        let r = std::panic::catch_unwind(|| decode_mnf8(&f));
1595        assert!(
1596            r.is_ok() && r.unwrap().is_err(),
1597            "two predictors must reject"
1598        );
1599        f[24..28].copy_from_slice(&(FLAG_TOKENS | FLAG_PRED_LEFT).to_le_bytes());
1600        let r = std::panic::catch_unwind(|| decode_mnf8(&f));
1601        assert!(r.is_ok() && r.unwrap().is_err(), "tokens+left must reject");
1602    }
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607    use super::*;
1608
1609    fn roundtrip(px: &[u8], dims: Dims, profile: Profile) -> u32 {
1610        let (buf, st, backend) = encode_mnf8(px, dims, true, profile);
1611        let (d, out, _) = decode_mnf8(&buf).expect("decode");
1612        assert_eq!(d, dims);
1613        assert_eq!(
1614            out, px,
1615            "profile {profile:?} backend {backend} flags {st:?}"
1616        );
1617        backend
1618    }
1619
1620    #[test]
1621    fn mnf8_balanced_roundtrip_all_classes() {
1622        for (w, h, seed) in [(16u32, 16u32, 1), (48, 33, 2), (31, 17, 4), (1, 1, 5)] {
1623            let mut s: u64 = (seed as u64).wrapping_mul(0x9E3779B97F4A7C15) ^ 0xDEADBEEF;
1624            let mut rnd = move || {
1625                s ^= s << 13;
1626                s ^= s >> 7;
1627                s ^= s << 17;
1628                s
1629            };
1630            let px: Vec<u8> = (0..w as usize * h as usize * 3)
1631                .map(|_| (rnd() & 0xFF) as u8)
1632                .collect();
1633            roundtrip(
1634                &px,
1635                Dims {
1636                    width: w,
1637                    height: h,
1638                },
1639                Profile::Balanced,
1640            );
1641        }
1642    }
1643
1644    #[test]
1645    fn mnf8_maximum_never_loses_to_mnf7() {
1646        // ADR-002 gate (EMPIRICAL per review fix 8 — candidate sets differ
1647        // structurally; this pins the measured relation on stable files).
1648        for name in [
1649            "screenshot_640x400.png",
1650            "solid_256x256.png",
1651            "gradient_320x240.png",
1652        ] {
1653            let path = format!("{}/testdata/{name}", env!("CARGO_MANIFEST_DIR"));
1654            let img = image::open(&path)
1655                .unwrap_or_else(|e| panic!("{name}: {e}"))
1656                .to_rgb8();
1657            let d = Dims {
1658                width: img.width(),
1659                height: img.height(),
1660            };
1661            let px = img.into_raw();
1662            let (buf8, _, backend) = encode_mnf8(&px, d, true, Profile::Maximum);
1663            let (buf7, _) = crate::mnf6::encode_mnf7(&px, d, true);
1664            // MNF8's header carries the 4-byte backend field; equality of
1665            // measure means <= MNF7 + 4 (the honest wire cost of the id).
1666            assert!(
1667                buf8.len() <= buf7.len() + 4,
1668                "{name}: MNF8({backend}) {} > MNF7(+4) {}",
1669                buf8.len(),
1670                buf7.len()
1671            );
1672            let (_, out, _) = decode_mnf8(&buf8).expect("decode");
1673            assert_eq!(out, px, "{name}: exact");
1674        }
1675    }
1676
1677    #[test]
1678    fn mnf8_rejects() {
1679        let mut f = vec![0u8; 32];
1680        f[0..4].copy_from_slice(b"MNF8");
1681        f[4..8].copy_from_slice(&16u32.to_le_bytes());
1682        f[8..12].copy_from_slice(&16u32.to_le_bytes());
1683        f[12..16].copy_from_slice(&16u32.to_le_bytes());
1684        f[16..20].copy_from_slice(&1u32.to_le_bytes());
1685        // payload_len = 0, flags = 0
1686        f[28..32].copy_from_slice(&7u32.to_le_bytes()); // unknown backend
1687        assert!(decode_mnf8(&f).is_err(), "unknown backend rejected");
1688        f[28..32].copy_from_slice(&3u32.to_le_bytes()); // reserved backend
1689        assert!(decode_mnf8(&f).is_err(), "reserved backend rejected");
1690        f[28..32].copy_from_slice(&2u32.to_le_bytes()); // reserved brotli backend
1691        assert!(decode_mnf8(&f).is_err(), "reserved brotli backend rejected");
1692        // and specifically NOT a panic (the old bug hit unreachable! here)
1693        f[28..32].copy_from_slice(&2u32.to_le_bytes());
1694        let r = std::panic::catch_unwind(|| decode_mnf8(&f));
1695        assert!(r.is_ok() && r.unwrap().is_err(), "must error, not panic");
1696        f[28..32].copy_from_slice(&0u32.to_le_bytes());
1697        f[24] = 0b100; // reserved flag bit
1698        assert!(decode_mnf8(&f).is_err(), "reserved flag rejected");
1699        // bad magic
1700        f[0] = b'X';
1701        f[24] = 0;
1702        assert!(decode_mnf8(&f).is_err(), "magic");
1703    }
1704
1705    #[test]
1706    fn mnf8_balanced_picks_zstd_not_hardcoded() {
1707        // review: the old test read the hardcoded return tuple; instead pin
1708        // the ENCODING CHOICE: balanced must measure the flag variants under
1709        // zstd and pick planar when planar wins (gradient-like content).
1710        let (w, h) = (64u32, 64u32);
1711        let mut px = vec![0u8; (w * h * 3) as usize];
1712        for y in 0..h {
1713            for x in 0..w {
1714                let i = ((y * w + x) * 3) as usize;
1715                px[i] = (x * 4) as u8;
1716                px[i + 1] = (y * 4) as u8;
1717                px[i + 2] = 128;
1718            }
1719        }
1720        let (buf, st, backend) = encode_mnf8(
1721            &px,
1722            Dims {
1723                width: w,
1724                height: h,
1725            },
1726            false,
1727            Profile::Balanced,
1728        );
1729        assert_eq!(backend, BACKEND_ZSTD19);
1730        // MEASURED pick (v1.2.1 round): the invariant is "measured, not
1731        // hardcoded" — the winner id moves with the data. This gradient has
1732        // vertically repeating tile rows, so exact refs beat literals and
1733        // flags = 0 is a LEGAL winner here; pin exactness + a legal flags id,
1734        // and pin the med-bearing pick on the no-repeat smooth body (next
1735        // test), not here.
1736        assert!(
1737            st.chosen_flags & !0b1111 == 0,
1738            "balanced wrote a legal flags id, got {:#x}",
1739            st.chosen_flags
1740        );
1741        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1742        assert_eq!(out, px);
1743    }
1744
1745    #[test]
1746    fn mnf8_no_geom_no_flips() {
1747        // review fix 2 regression pin: use_geom=false must emit ONLY exact
1748        // references (t=0) or plain literals — flips are forbidden. A rotated
1749        // copy of tile 0 must be emitted as a LITERAL, not a geom ref.
1750        let (w, h) = (32u32, 32u32); // two 16-tall tile rows
1751        let mut px = vec![0u8; (w * h * 3) as usize];
1752        for y in 0..h {
1753            for x in 0..w {
1754                let i = ((y * w + x) * 3) as usize;
1755                px[i] = (x * 7 % 256) as u8;
1756                px[i + 1] = (y * 3 % 256) as u8;
1757                px[i + 2] = 5;
1758            }
1759        }
1760        // flip tile (0,1) [the 16x16 block at rows 16..32, cols 0..16] in place
1761        let tile_row = 16usize;
1762        let tile_col = 0usize;
1763        let src_off = (tile_row * w as usize + tile_col) * 3;
1764        let tile = px[src_off..src_off + 16 * 16 * 3].to_vec();
1765        let flipped = apply_transform(1, 16, 16, &tile);
1766        px[src_off..src_off + 16 * 16 * 3].copy_from_slice(&flipped);
1767        let stacked = px.clone();
1768        let d = Dims {
1769            width: w,
1770            height: h,
1771        };
1772        let (buf, st, _) = encode_mnf8(&stacked, d, false, Profile::Balanced);
1773        // walk stats: geom references must be zero
1774        assert_eq!(st.ref_geom, 0, "no flips allowed with use_geom=false");
1775        assert_eq!(st.geom_nontrivial, 0);
1776        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1777        assert_eq!(out, stacked);
1778    }
1779
1780    #[test]
1781    fn mnf8_no_atlas_arm() {
1782        // review fix 2b: the ablation needs its no-atlas arm (pure
1783        // prediction: literals + mode 3 only), and it must roundtrip exactly.
1784        let d = Dims {
1785            width: 32,
1786            height: 32,
1787        };
1788        let px: Vec<u8> = (0..d.width as usize * d.height as usize * 3)
1789            .map(|i| (i * 7 % 256) as u8)
1790            .collect();
1791        let (buf, st, _) = encode_mnf8_noclimb(&px, d);
1792        assert_eq!(
1793            st.ref_exact + st.ref_geom + st.ref_exact_color + st.ref_geom_color,
1794            0,
1795            "no-atlas arm must emit zero references"
1796        );
1797        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1798        assert_eq!(out, px);
1799    }
1800
1801    // helper calling the internal encoder with both atlas controls off
1802    fn encode_mnf8_noclimb(px: &[u8], d: Dims) -> (Vec<u8>, MnF6Stats, u32) {
1803        // balanced profile, atlas off, geom off
1804        let mut best: Option<(Vec<u8>, MnF6Stats, u32)> = None;
1805        for flags in [0u32, FLAG_PLANAR] {
1806            let planar = flags & FLAG_PLANAR != 0;
1807            let (file, st) = encode_stream_coder(
1808                px,
1809                d,
1810                false,
1811                false,
1812                flags,
1813                planar,
1814                false,
1815                PredKind::None,
1816                &Coder::Zstd(19),
1817                BACKEND_ZSTD19,
1818                0,
1819            )
1820            .expect("encode");
1821            if best.as_ref().is_none_or(|b| file.len() < b.0.len()) {
1822                best = Some((file, st, BACKEND_ZSTD19));
1823            }
1824        }
1825        let (f, s, b) = best.expect("variant");
1826        (f, s, b)
1827    }
1828
1829    #[test]
1830    fn mnf8_med_flag_roundtrip_photo_like() {
1831        // FLAG_PRED_MED (v1.2 gate item 2): gradient-like image where the
1832        // image-wide MED pre-predictor wins; the rate-aware loop must MEASURE
1833        // the med variant and pick it, and decode must reconstruct exactly
1834        // through the causal in-walker MED inversion.
1835        let (w, h) = (64u32, 64u32);
1836        let mut px = vec![0u8; (w * h * 3) as usize];
1837        for y in 0..h {
1838            for x in 0..w {
1839                let i = ((y * w + x) * 3) as usize;
1840                px[i] = (x * 4) as u8;
1841                px[i + 1] = (y * 4) as u8;
1842                px[i + 2] = ((x + y) / 2) as u8;
1843            }
1844        }
1845        let d = Dims {
1846            width: w,
1847            height: h,
1848        };
1849        let (buf, st, _) = encode_mnf8(&px, d, false, Profile::Balanced);
1850        // RATE-AWARE pick is allowed to lose here (refs compressed better than
1851        // MED-literals on this synthetic body); the invariants that matter:
1852        // the MED variant was MEASURED (no panic), the pick is a legal flags
1853        // id, and decode is exact.
1854        assert!(
1855            st.chosen_flags & !0b1111 == 0,
1856            "chosen flags must be a legal MNF8 id, got {:#x}",
1857            st.chosen_flags
1858        );
1859        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1860        if out != px {
1861            let first = out
1862                .iter()
1863                .zip(&px)
1864                .position(|(a, b)| a != b)
1865                .expect("differs");
1866            let (b_y, rem) = (first / 3 / (w as usize), (first / 3) % (w as usize));
1867            panic!(
1868                "not exact: first diff at byte {first} (pixel ({b_y},{rem}), ch {}): out={:?} px={:?}",
1869                first % 3,
1870                &out[first.saturating_sub(4)..first + 4],
1871                &px[first.saturating_sub(4)..first + 4]
1872            );
1873        }
1874        assert_eq!(out, px, "exact reconstruction under the measured flags");
1875    }
1876
1877    #[test]
1878    fn mnf8_med_flag_picked_on_photo_like_smoothing() {
1879        // The MEASURED pick: smooth noise-free body with NO exact repeats —
1880        // MED-literals must win (105 B vs planar 177 / interleaved 401,
1881        // measured in the debug round).
1882        let (w, h) = (64u32, 64u32);
1883        let mut px = vec![0u8; (w * h * 3) as usize];
1884        for y in 0..h {
1885            for x in 0..w {
1886                let i = ((y * w + x) * 3) as usize;
1887                // smooth field + tiny deterministic ripple (no byte repeats)
1888                px[i] = (x * 4 + y * 2) as u8;
1889                px[i + 1] = (y * 6) as u8;
1890                px[i + 2] = ((x + y) * 3) as u8;
1891            }
1892        }
1893        let d = Dims {
1894            width: w,
1895            height: h,
1896        };
1897        let (_, st, _) = encode_mnf8(
1898            &px,
1899            d,
1900            false, // no geom: kill the reference arms to isolate prediction
1901            Profile::Balanced,
1902        );
1903        // ADR-004 MEASURED pick (branch-gate): smooth no-repeat bodies select
1904        // a PREDICTOR bit — historically MED, but the plane branch measured
1905        // cheaper on the same class, and the pick is measured, not hardcoded.
1906        // The invariant: a predictor bit IS set and the file decodes exactly.
1907        assert!(
1908            st.chosen_flags & (FLAG_PRED_MED | FLAG_PRED_PLANE | FLAG_PRED_LEFT) != 0,
1909            "smooth no-repeat bodies must select a predictor (picked {:#x})",
1910            st.chosen_flags
1911        );
1912        let (_, out, _) =
1913            decode_mnf8(&encode_mnf8(&px, d, false, Profile::Balanced).0).expect("decode");
1914        assert_eq!(out, px, "exact under the measured predictor pick");
1915    }
1916
1917    #[test]
1918    fn mnf8_forced_mode4_mode5_records() {
1919        // review verify 3: drive mode 4 and mode 5 end-to-end — construct a
1920        // file whose color residuals genuinely win (structured chroma delta
1921        // on a noisy base, the mnf7_color_domain_used_when_it_wins pattern)
1922        // and assert decode_mnf8 reconstructs exactly.
1923        let mut s: u64 = 42;
1924        let mut rnd = move || {
1925            s ^= s << 13;
1926            s ^= s >> 7;
1927            s ^= s << 17;
1928            s
1929        };
1930        let a: Vec<u8> = (0..16 * 16 * 3).map(|_| (rnd() & 0xFF) as u8).collect();
1931        let ta_t = crate::mnf6::modcolor_forward(&a);
1932        let mut tb_t = ta_t.clone();
1933        let ramp = [0u8, 1, 2, 255];
1934        for i in (1..tb_t.len()).step_by(3) {
1935            tb_t[i] = tb_t[i].wrapping_add(ramp[(i / 3) % 4]);
1936        }
1937        let b = crate::mnf6::modcolor_inverse(&tb_t);
1938        let mut two = a.clone();
1939        two.extend_from_slice(&b);
1940        let d2 = Dims {
1941            width: 16,
1942            height: 32,
1943        };
1944        let (buf, st, _) = encode_mnf8(&two, d2, true, Profile::Balanced);
1945        assert!(
1946            st.ref_exact_color > 0,
1947            "expected mode 4 (or a color-geom below) selected, stats: exact={} geom={} ec={} gc={}",
1948            st.ref_exact,
1949            st.ref_geom,
1950            st.ref_exact_color,
1951            st.ref_geom_color
1952        );
1953        let (_, out, _) = decode_mnf8(&buf).expect("decode");
1954        assert_eq!(out, two, "mode-4 record reconstructs exactly through MNF8");
1955
1956        // and a rotated variant of the same (mode 5 path)
1957        let flipped = apply_transform(1, 16, 16, &a);
1958        let tb_t2 = crate::mnf6::modcolor_forward(&flipped);
1959        let mut tc_t = tb_t2;
1960        for i in (1..tc_t.len()).step_by(3) {
1961            tc_t[i] = tc_t[i].wrapping_add(ramp[(i / 3) % 4]);
1962        }
1963        let c = crate::mnf6::modcolor_inverse(&tc_t);
1964        let mut three = flipped.clone();
1965        three.extend_from_slice(&c);
1966        let d3 = Dims {
1967            width: 16,
1968            height: 32,
1969        };
1970        let (buf3, st3, _) = encode_mnf8(&three, d3, true, Profile::Balanced);
1971        assert!(
1972            st3.ref_exact_color > 0 || st3.ref_geom_color > 0,
1973            "expected a color-domain reference (exact or geom), stats: ec={} gc={}",
1974            st3.ref_exact_color,
1975            st3.ref_geom_color
1976        );
1977        let (_, out3, _) = decode_mnf8(&buf3).expect("decode");
1978        assert_eq!(out3, three);
1979    }
1980
1981    // ---- ADR-003 token arm ------------------------------------------------
1982
1983    fn roundtrip_tokens(px: &[u8], dims: Dims, profile: Profile) -> (u32, u32) {
1984        let (buf, st, backend) = encode_mnf8(px, dims, true, profile);
1985        let (d, out, _) = decode_mnf8(&buf).expect("decode");
1986        assert_eq!(d, dims);
1987        assert_eq!(
1988            out, px,
1989            "profile {profile:?} backend {backend} flags {:#x} literal {} matches {}",
1990            st.chosen_flags, st.literal, st.ref_exact
1991        );
1992        (st.literal, st.ref_exact)
1993    }
1994
1995    #[test]
1996    fn token_wire_unit_roundtrip_and_errors() {
1997        use crate::tokens::*;
1998        // small plane with a planted repeat + a run: tokens fire and rebuild
1999        let (w, h) = (24usize, 16usize);
2000        let mut plane = vec![0u8; w * h * 3];
2001        for i in 0..w * h {
2002            plane[i * 3] = (i % 251) as u8;
2003            plane[i * 3 + 1] = ((i * 7) % 251) as u8;
2004        }
2005        for k in 300..384 {
2006            plane[k * 3..k * 3 + 3].copy_from_slice(&[9, 9, 9]);
2007        }
2008        let (wire, st) = encode_plane(&plane, w, h).expect("encode");
2009        assert!(st.matches >= 1, "planted structures must fire: {st:?}");
2010        let back = decode_plane(&wire, w, h).expect("decode");
2011        assert_eq!(back, plane);
2012        // malformed wires are errors, never panics
2013        let mut bad = wire.clone();
2014        bad.push(2); // unknown tag
2015        assert!(decode_plane(&bad, w, h).is_err());
2016        assert!(decode_plane(&wire[..wire.len() - 1], w, h).is_err());
2017        let mut bad = wire.clone();
2018        bad.push(TAG_LITERAL);
2019        bad.extend_from_slice(&[1, 2, 3]);
2020        assert!(decode_plane(&bad, w, h).is_err()); // overruns the plane
2021    }
2022
2023    #[test]
2024    fn mnf8_token_flag_picked_on_repeating_residual() {
2025        // ADR-003 measured pick: a large smooth ramp (long constant MED
2026        // residual runs = the measured token-winning class). The pick MUST
2027        // be FLAG_TOKENS and decode must reconstruct exactly through the
2028        // token walk + causal MED inversion.
2029        let (w, h) = (256u32, 256u32);
2030        let mut px = vec![0u8; (w * h * 3) as usize];
2031        for y in 0..h {
2032            for x in 0..w {
2033                let i = ((y * w + x) * 3) as usize;
2034                // vertical ramp: every MED residual = same constant per row
2035                px[i] = (y * 3) as u8;
2036                px[i + 1] = (y / 4) as u8;
2037                px[i + 2] = 200;
2038            }
2039        }
2040        let d = Dims {
2041            width: w,
2042            height: h,
2043        };
2044        let (buf, st, _) = encode_mnf8(&px, d, false, Profile::Balanced);
2045        // ADR-003/004 MEASURED pick: this ramp's tokens WON in the v1.6.0
2046        // arm set, but the branch-gate measured LEFT cheaper still on the
2047        // same class (−163 B at 320×240; the pick is measured, never
2048        // hardcoded) — the invariant is exactness + pick legality.
2049        assert!(
2050            st.chosen_flags & (FLAG_TOKENS | FLAG_PRED_PLANE | FLAG_PRED_LEFT) != 0,
2051            "repeating-residual class must pick a measured-winning arm (picked {:#x})",
2052            st.chosen_flags
2053        );
2054        let (_, out, _) = decode_mnf8(&buf).expect("decode");
2055        assert_eq!(out, px, "wire reconstructs exactly under the pick");
2056    }
2057
2058    #[test]
2059    fn mnf8_token_flag_not_picked_on_noise() {
2060        // noise planes: all-literal token wire is pure overhead — the pick
2061        // must stay on the record arms (this is the rate-aware contract's
2062        // other half).
2063        let (w, h) = (64u32, 64u32);
2064        let mut s: u64 = 0x5EED;
2065        let mut rnd = move || {
2066            s ^= s << 13;
2067            s ^= s >> 7;
2068            s ^= s << 17;
2069            s
2070        };
2071        let px: Vec<u8> = (0..w as usize * h as usize * 3)
2072            .map(|_| (rnd() & 0xFF) as u8)
2073            .collect();
2074        let d = Dims {
2075            width: w,
2076            height: h,
2077        };
2078        let (_, st, _) = encode_mnf8(&px, d, false, Profile::Balanced);
2079        assert_eq!(
2080            st.chosen_flags & FLAG_TOKENS,
2081            0,
2082            "noise class must NOT pick tokens (picked {:#x})",
2083            st.chosen_flags
2084        );
2085        roundtrip_tokens(&px, d, Profile::Balanced);
2086    }
2087
2088    #[test]
2089    fn mnf8_tokens_decode_rejects_malformed_wire() {
2090        // craft a token-flag file whose payload decodes to a corrupt wire:
2091        // unknown tag inside. Must Err, never panic (validation whitelist
2092        // rule: every admitted construct has a handler or an explicit Err).
2093        let f = vec![0u8; HEADER8 + 7];
2094        let mut f = f;
2095        f[0..4].copy_from_slice(b"MNF8");
2096        f[4..8].copy_from_slice(&16u32.to_le_bytes());
2097        f[8..12].copy_from_slice(&16u32.to_le_bytes());
2098        f[12..16].copy_from_slice(&16u32.to_le_bytes());
2099        f[16..20].copy_from_slice(&1u32.to_le_bytes());
2100        f[20..24].copy_from_slice(&7u32.to_le_bytes());
2101        f[24..28].copy_from_slice(&(FLAG_PRED_MED | FLAG_TOKENS).to_le_bytes());
2102        f[28..32].copy_from_slice(&BACKEND_ZSTD19.to_le_bytes());
2103        // zlib backend so we can hand-craft canonical payload bytes:
2104        f[28..32].copy_from_slice(&BACKEND_ZLIB.to_le_bytes());
2105        let res: Vec<u8> = vec![0u8; 16 * 16 * 3];
2106        let (wire, _) = crate::tokens::encode_plane(&res, 16, 16).expect("wire");
2107        // flip first tag byte to an unknown tag
2108        let mut wire_bad = wire.clone();
2109        wire_bad[0] = 7;
2110        let comp: Vec<u8>;
2111        {
2112            let mut e = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::best());
2113            std::io::Write::write_all(&mut e, &wire_bad).unwrap();
2114            comp = e.finish().unwrap();
2115        }
2116        let mut file = Vec::with_capacity(HEADER8 + comp.len());
2117        file.extend_from_slice(&f[..HEADER8]);
2118        let pl = comp.len() as u32;
2119        file[20..24].copy_from_slice(&pl.to_le_bytes());
2120        file.extend_from_slice(&comp);
2121        let r = std::panic::catch_unwind(|| decode_mnf8(&file));
2122        assert!(
2123            r.is_ok() && r.unwrap().is_err(),
2124            "corrupt wire: Err not panic"
2125        );
2126        // exact wire still decodes
2127        let comp2: Vec<u8>;
2128        {
2129            let mut e = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::best());
2130            std::io::Write::write_all(&mut e, &wire).unwrap();
2131            comp2 = e.finish().unwrap();
2132        }
2133        let mut file2 = Vec::with_capacity(HEADER8 + comp2.len());
2134        file2.extend_from_slice(&f[..HEADER8]);
2135        file2[20..24].copy_from_slice(&(comp2.len() as u32).to_le_bytes());
2136        file2.extend_from_slice(&comp2);
2137        let (_, out, _) = decode_mnf8(&file2).expect("valid token wire decodes");
2138        assert_eq!(out, pred_residual_invert(&res, 16, 16, PredKind::Med));
2139    }
2140}
2141
2142#[cfg(test)]
2143mod channel_diff {
2144    use super::*;
2145    use crate::mnf6::Coder;
2146    use std::path::Path;
2147
2148    fn load(name: &str) -> (Dims, Vec<u8>) {
2149        let p = format!("{}/testdata/{}", env!("CARGO_MANIFEST_DIR"), name);
2150        let img = image::open(Path::new(&p))
2151            .unwrap_or_else(|e| panic!("{name}: {e}"))
2152            .to_rgb8();
2153        (
2154            Dims {
2155                width: img.width(),
2156                height: img.height(),
2157            },
2158            img.into_raw(),
2159        )
2160    }
2161
2162    /// Roundtrip pin: the CD arm encodes/decodes exactly, all predictor and
2163    /// planar variants, both backends, incl. an atlas-free pure arm.
2164    #[test]
2165    fn cd_roundtrip_all_predictor_shapes() {
2166        let (d, px) = load("gradient_320x240.png");
2167        for kind in [
2168            PredKind::None,
2169            PredKind::Med,
2170            PredKind::Plane,
2171            PredKind::Left,
2172        ] {
2173            for planar in [false, true] {
2174                for (backend, coder) in [
2175                    (BACKEND_ZLIB, Coder::Zlib),
2176                    (BACKEND_ZSTD19, Coder::Zstd(19)),
2177                ] {
2178                    let flags =
2179                        kind.flag_bit() | if planar { FLAG_PLANAR } else { 0 } | FLAG_CHANNEL_DIFF;
2180                    let (file, _) = encode_stream_coder(
2181                        &px,
2182                        d,
2183                        true,
2184                        false,
2185                        flags,
2186                        planar,
2187                        kind == PredKind::Med,
2188                        kind,
2189                        &coder,
2190                        backend,
2191                        1,
2192                    )
2193                    .expect("encode");
2194                    let (dd, out, _) = decode_mnf8(&file).expect("decode");
2195                    assert_eq!(dd, d);
2196                    assert_eq!(out, px, "cd roundtrip {kind:?} planar={planar}");
2197                }
2198            }
2199        }
2200        // token arm with cd:
2201        let flags = FLAG_PRED_MED | FLAG_TOKENS | FLAG_CHANNEL_DIFF;
2202        for cap in [16, tokens::MAX_CANDIDATES] {
2203            let (file, _) = encode_tokens_coder_cap(&px, d, BACKEND_ZSTD19, flags, cap, 1)
2204                .expect("token encode");
2205            let (dd, out, _) = decode_mnf8(&file).expect("decode");
2206            assert_eq!(dd, d);
2207            assert_eq!(out, px, "cd token roundtrip cap={cap}");
2208        }
2209    }
2210
2211    /// The never-worse pin: the full pick (with CD arms) must not exceed
2212    /// the pre-ADR-008 pick on a fire-class fixture and a no-fire one.
2213    #[test]
2214    fn cd_pick_never_worse() {
2215        for name in ["photos/slate_224x160_s900008.png", "gradient_320x240.png"] {
2216            let (d, px) = load(name);
2217            let (cd_file, _, _) = encode_mnf8(&px, d, false, Profile::Maximum);
2218            // the old pick = re-run the pre-CD loop inline (by-value loops
2219            // mirroring encode_mnf8 minus the CD arms):
2220            let candidates: Vec<(u32, Coder)> = backend_candidates();
2221            let mut old = usize::MAX;
2222            for (backend, coder) in candidates {
2223                for kind in [
2224                    PredKind::None,
2225                    PredKind::Med,
2226                    PredKind::Plane,
2227                    PredKind::Left,
2228                ] {
2229                    for planar in [false, true] {
2230                        let flags = kind.flag_bit() | if planar { FLAG_PLANAR } else { 0 };
2231                        let (file, _) = encode_stream_coder(
2232                            &px,
2233                            d,
2234                            true,
2235                            false,
2236                            flags,
2237                            planar,
2238                            kind == PredKind::Med,
2239                            kind,
2240                            &coder,
2241                            backend,
2242                            0,
2243                        )
2244                        .expect("encode");
2245                        old = old.min(file.len());
2246                    }
2247                }
2248                for cap in [16, tokens::MAX_CANDIDATES] {
2249                    let (file, _) = encode_tokens_coder_cap(
2250                        &px,
2251                        d,
2252                        backend,
2253                        FLAG_PRED_MED | FLAG_TOKENS,
2254                        cap,
2255                        0,
2256                    )
2257                    .expect("encode");
2258                    old = old.min(file.len());
2259                }
2260            }
2261            assert!(
2262                cd_file.len() <= old,
2263                "{name}: pick {} above old {old}",
2264                cd_file.len()
2265            );
2266        }
2267    }
2268
2269    /// The corpus gate (slow, ~15 min): every file roundtrips under the
2270    /// full CD pick; wins counted vs the old pick's own wire. #[ignore]d;
2271    /// run explicitly at the ADR-008 gate.
2272    #[test]
2273    #[ignore]
2274    fn cd_corpus_gate() {
2275        let files = [
2276            "gradient_320x240.png",
2277            "noise_256x256.png",
2278            "repeat_256x256.png",
2279            "screenshot_640x400.png",
2280            "solid_256x256.png",
2281            "heldout/blob_224x160.png",
2282            "heldout/blocks_192x128.png",
2283            "heldout/document_200x280.png",
2284            "heldout/plaid_192.png",
2285            "heldout/poster_208x144.png",
2286            "heldout-photos/autumn_160x120_s3100006.png",
2287            "heldout-photos/dusk_160x120_s3100003.png",
2288            "heldout-photos/dusk_224x160_s3100002.png",
2289            "heldout-photos/forest_192x144_s3100001.png",
2290            "heldout-photos/forest_224x160_s3100005.png",
2291            "heldout-photos/forest_224x160_s3100008.png",
2292            "heldout-photos/ocean_192x144_s3100007.png",
2293            "heldout-photos/sand_192x144_s3100004.png",
2294            "heldout-photos/slate_160x120_s3100009.png",
2295            "heldout-photos/terrain_160x120_s3100000.png",
2296            "photos/autumn_192x144_s900001.png",
2297            "photos/dusk_160x120_s900003.png",
2298            "photos/forest_224x160_s900002.png",
2299            "photos/ocean_192x144_s900007.png",
2300            "photos/sand_160x120_s900000.png",
2301            "photos/slate_192x144_s900004.png",
2302            "photos/slate_224x160_s900008.png",
2303            "photos/stone_160x120_s900006.png",
2304            "photos/terrain_160x120_s900009.png",
2305            "photos/terrain_224x160_s900005.png",
2306        ];
2307        let mut tot = 0usize;
2308        let mut cd_fires = 0usize;
2309        for name in files {
2310            let (d, px) = load(name);
2311            let (file, st, _) = encode_mnf8(&px, d, false, crate::mnf8::Profile::Maximum);
2312            assert_eq!(decode_mnf8(&file).unwrap().1, px, "{name}");
2313            tot += file.len();
2314            let reference = cd_reference(st.chosen_flags);
2315            cd_fires += usize::from(reference != 0);
2316            if reference != 0 {
2317                println!("{name}: ref={reference}, {}", file.len());
2318            }
2319        }
2320        println!("corpus: total={tot} cd_fires={cd_fires}");
2321        assert!(cd_fires >= 10, "cd must fire on the photographic class");
2322    }
2323
2324    /// Malformed cd wires reject with errors (never panics): truncated
2325    /// payload, unknown mode after the unrelabel guard.
2326    #[test]
2327    fn cd_malformed_rejects() {
2328        let (d, px) = load("gradient_320x240.png");
2329        let flags = FLAG_CHANNEL_DIFF;
2330        let (file, _) = encode_stream_coder(
2331            &px,
2332            d,
2333            true,
2334            false,
2335            flags,
2336            false,
2337            false,
2338            PredKind::None,
2339            &Coder::Zstd(19),
2340            BACKEND_ZSTD19,
2341            1,
2342        )
2343        .expect("encode");
2344        // truncate the payload
2345        for cut in [40, 41, 60, file.len() / 2] {
2346            let mut t = file.clone();
2347            t.truncate(cut.min(file.len()));
2348            assert!(
2349                decode_mnf8(&t).is_err(),
2350                "truncated cd file must reject at {cut}"
2351            );
2352        }
2353        // corrupt the flag region (invalid mode inside the body): flip a
2354        // byte inside the container — the unrelabel walk or the container
2355        // must reject
2356        let mut t = file.clone();
2357        let last = t.len() - 1;
2358        t[last] = t[last].wrapping_add(1);
2359        let _ = decode_mnf8(&t); // must not panic; result = Err or valid
2360                                 // diff-file class on the container checksum
2361    }
2362}