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