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