Skip to main content

manifold/
mnf9.rs

1//! MNF9 — exact generative whole-frame records (ADR-007; Track A promotion).
2//!
3//! Ported from the research prototype crates/mnf-mans/src/mnf10.rs (the
4//! discovery probe's measured families) with the shipped lib's discipline:
5//! Error (never panic) on malformed input, bounded decode, exact-size
6//! accounting. The ENCODER attempts each model family's exact fit against
7//! the raw pixels and emits a whole-frame rule only on ZERO residual —
8//! the pick (mnf8.rs) treats the resulting MNF9 stream as one more arm on
9//! final file size; a losing file keeps its MNF8 encoding.
10//!
11//! Wire: magic "MNF9" (4 B; the ADR-003 reservation), 32-B header identical
12//! in field layout to MNF8 (flags/backend = 0 on write, rejected if set on
13//! decode — no flags/containers exists in this grammar yet), payload =
14//! one parametric record per ADR-007's model table.
15//!
16//! ponytail: six integer families, one decode dispatch; if a seventh family
17//! ever earns its place it comes with its own probe record first.
18
19use crate::codec::Dims;
20use crate::error::{Error, Result};
21
22/// Wire magic (4 B; the ADR-003 reservation — see docs/format.md §5).
23pub const MAGIC: [u8; 4] = *b"MNF9";
24/// Header length (32 B; field offsets mirror MNF8's).
25pub const HEADER_LEN: usize = 32;
26
27/// Whole-frame constant.
28pub const MODEL_CONST: u8 = 1;
29/// Per-channel div-form ramp along x: (N·x + T) div_euclid D.
30pub const MODEL_DIV1D: u8 = 2;
31/// Per-channel constant-step recurrence mod 256 (orientation bit).
32pub const MODEL_REC1: u8 = 3;
33/// Corner-anchored joint floor bilinear.
34pub const MODEL_BILIN_JOINT: u8 = 4;
35/// Corner-anchored nested round bilinear (x then y, half-up).
36pub const MODEL_BILIN_NESTED: u8 = 5;
37/// Per-segment div-ramps with declared boundaries.
38pub const MODEL_PIECEWISE: u8 = 6;
39
40/// Denominator convention table (ADR-007): idx 3 = w−1 (index 3/4 are
41/// SEGMENT-relative in the piecewise arm, resolved by the caller).
42fn den_full(idx: u8, w: u32) -> Result<i64> {
43    let w1 = i64::from(w).saturating_sub(1).max(1);
44    match idx {
45        0 => Ok(1),
46        1 => Ok(2),
47        2 => Ok(4),
48        3 => Ok(w1),
49        4 => Ok(i64::from(w)),
50        5 => Ok(2 * w1),
51        6 => Ok(256),
52        7 => Ok(65_536),
53        _ => Err(Error::fmt_msg("mnf9: denominator index out of table")),
54    }
55}
56
57// ------------------------------------------------------------------ decoder
58
59fn dims_ok(dims: Dims) -> Result<(usize, usize)> {
60    if dims.width == 0 || dims.height == 0 {
61        return Err(Error::fmt_msg("mnf9: empty dims"));
62    }
63    let (wu, hu) = (dims.width as usize, dims.height as usize);
64    if wu.checked_mul(hu).and_then(|n| n.checked_mul(3)).is_none() {
65        return Err(Error::fmt_msg("mnf9: dims overflow"));
66    }
67    Ok((wu, hu))
68}
69
70fn eval_payload(payload: &[u8], dims: Dims) -> Result<Vec<u8>> {
71    // Identical evaluation to the prototype's decoder (byte-for-byte port of
72    // the mnf10.rs logic; the D=3/4 segment-relative rule included).
73    let (wu, hu) = dims_ok(dims)?;
74    if payload.is_empty() {
75        return Err(Error::fmt_msg("mnf9: empty payload"));
76    }
77    let model = payload[0];
78    let body = &payload[1..];
79    let need = |n: usize| -> Result<&[u8]> {
80        if body.len() < n {
81            Err(Error::fmt_msg("mnf9: truncated params"))
82        } else {
83            Ok(&body[..n])
84        }
85    };
86    let i16le = |o: usize| i16::from_le_bytes([body[o], body[o + 1]]);
87    let out: Vec<u8> = match model {
88        MODEL_CONST => {
89            let p = need(3)?;
90            let mut v = vec![0u8; wu * hu * 3];
91            for k in 0..wu * hu {
92                v[3 * k..3 * k + 3].copy_from_slice(p);
93            }
94            v
95        }
96        MODEL_DIV1D => {
97            let p = need(25)?; // 3*(N,T) i32 + shared D index
98            let d = den_full(p[24], dims.width)?;
99            let mut v = vec![0u8; wu * hu * 3];
100            for c in 0..3 {
101                let n = i32::from_le_bytes(p[8 * c..8 * c + 4].try_into().unwrap()) as i64;
102                let t = i32::from_le_bytes(p[8 * c + 4..8 * c + 8].try_into().unwrap()) as i64;
103                for x in 0..wu {
104                    let val = (n * x as i64 + t).div_euclid(d);
105                    if !(0..=255).contains(&val) {
106                        return Err(Error::fmt_msg("mnf9: div-1D value out of byte range"));
107                    }
108                    for y in 0..hu {
109                        v[(y * wu + x) * 3 + c] = val as u8;
110                    }
111                }
112            }
113            v
114        }
115        MODEL_REC1 => {
116            let p = need(10)?; // 3*(a0,d) + orientation
117            let orientation = p[9];
118            let lin = if orientation == 0 { wu } else { hu };
119            let lines = if orientation == 0 { hu } else { wu };
120            let mut v = vec![0u8; wu * hu * 3];
121            let mut line = vec![0u8; lin * 3];
122            for c in 0..3 {
123                let a0 = u32::from(p[3 * c]) as i64;
124                let d = i64::from(i16le(3 * c + 1));
125                for k in 0..lin {
126                    line[3 * k + c] = (a0 + d * k as i64).rem_euclid(256) as u8;
127                }
128            }
129            for k in 0..lines {
130                for j in 0..lin {
131                    let (x, y) = if orientation == 0 { (j, k) } else { (k, j) };
132                    v[(y * wu + x) * 3..(y * wu + x) * 3 + 3]
133                        .copy_from_slice(&line[3 * j..3 * j + 3]);
134                }
135            }
136            v
137        }
138        MODEL_BILIN_JOINT => {
139            let p = need(12)?;
140            if dims.width == 1 || dims.height == 1 {
141                return Err(Error::fmt_msg("mnf9: bilinear degenerate dims"));
142            }
143            let mut v = vec![0u8; wu * hu * 3];
144            let dm = u64::from(dims.width - 1) * u64::from(dims.height - 1);
145            let wm1 = u64::from(dims.width - 1);
146            let hm1 = u64::from(dims.height - 1);
147            for c in 0..3 {
148                let (v00, v10, v01, v11) = (
149                    u64::from(p[c]),
150                    u64::from(p[3 + c]),
151                    u64::from(p[6 + c]),
152                    u64::from(p[9 + c]),
153                );
154                for y in 0..hu {
155                    for x in 0..wu {
156                        let (xu, yu) = (x as u64, y as u64);
157                        let num = v00 * (wm1 - xu) * (hm1 - yu)
158                            + v10 * xu * (hm1 - yu)
159                            + v01 * (wm1 - xu) * yu
160                            + v11 * xu * yu;
161                        v[(y * wu + x) * 3 + c] = (num / dm) as u8;
162                    }
163                }
164            }
165            v
166        }
167        MODEL_BILIN_NESTED => {
168            let p = need(12)?;
169            if dims.width == 1 || dims.height == 1 {
170                return Err(Error::fmt_msg("mnf9: bilinear degenerate dims"));
171            }
172            let mut v = vec![0u8; wu * hu * 3];
173            let dx = i64::from(dims.width - 1);
174            let dy = i64::from(dims.height - 1);
175            let hx = dx / 2;
176            let hy = dy / 2;
177            for c in 0..3 {
178                let (v00, v10, v01, v11) = (
179                    i64::from(p[c]),
180                    i64::from(p[3 + c]),
181                    i64::from(p[6 + c]),
182                    i64::from(p[9 + c]),
183                );
184                let mut toprow = vec![0i64; wu];
185                let mut botrow = vec![0i64; wu];
186                for x in 0..wu {
187                    let xi = x as i64;
188                    toprow[x] = v00 + (xi * (v10 - v00) + hx).div_euclid(dx);
189                    botrow[x] = v01 + (xi * (v11 - v01) + hx).div_euclid(dx);
190                }
191                for y in 0..hu {
192                    let yi = y as i64;
193                    for x in 0..wu {
194                        let val = toprow[x] + (yi * (botrow[x] - toprow[x]) + hy).div_euclid(dy);
195                        if !(0..=255).contains(&val) {
196                            return Err(Error::fmt_msg("mnf9: nested value out of range"));
197                        }
198                        v[(y * wu + x) * 3 + c] = val as u8;
199                    }
200                }
201            }
202            v
203        }
204        MODEL_PIECEWISE => {
205            if body.is_empty() {
206                return Err(Error::fmt_msg("mnf9: piecewise truncated"));
207            }
208            let nseg = body[0] as usize;
209            if !(1..=32).contains(&nseg) {
210                return Err(Error::fmt_msg("mnf9: piecewise segment count"));
211            }
212            let nb = nseg - 1;
213            let p = need(1 + nb * 2 + nseg * 27)?;
214            let mut bounds = vec![0usize];
215            for k in 0..nb {
216                let b =
217                    u16::from_le_bytes(body[1 + k * 2..1 + k * 2 + 2].try_into().unwrap()) as usize;
218                if b == 0 || b >= wu {
219                    return Err(Error::fmt_msg("mnf9: piecewise boundary out of range"));
220                }
221                bounds.push(b);
222            }
223            let mut v = vec![0u8; wu * hu * 3];
224            for si in 0..bounds.len() {
225                let a = bounds[si];
226                let b = bounds.get(si + 1).copied().unwrap_or(wu);
227                for c in 0..3 {
228                    let o = 1 + nb * 2 + si * 27 + c * 9;
229                    let n = i32::from_le_bytes(p[o..o + 4].try_into().unwrap()) as i64;
230                    let t = i32::from_le_bytes(p[o + 4..o + 8].try_into().unwrap()) as i64;
231                    let segw = (b - a) as u32;
232                    let d = match p[o + 8] {
233                        3 => i64::from(segw.saturating_sub(1).max(1)),
234                        4 => i64::from(segw),
235                        other => den_full(other, dims.width)?,
236                    };
237                    for xi in a..b {
238                        let sx = (xi - a) as i64;
239                        let val = (n * sx + t).div_euclid(d);
240                        if !(0..=255).contains(&val) {
241                            return Err(Error::fmt_msg("mnf9: piecewise value out of range"));
242                        }
243                        for y in 0..hu {
244                            v[(y * wu + xi) * 3 + c] = val as u8;
245                        }
246                    }
247                }
248            }
249            v
250        }
251        _ => return Err(Error::fmt_msg("mnf9: reserved model id")),
252    };
253    Ok(out)
254}
255
256/// Decode a full MNF9 file (magic + header + payload) to RGB. Every
257/// malformed field is an Err; nothing panics on untrusted input.
258pub fn decode_mnf9(buf: &[u8]) -> Result<(Dims, Vec<u8>)> {
259    if buf.len() < HEADER_LEN {
260        return Err(Error::fmt_msg("mnf9: shorter than header"));
261    }
262    if buf[0..4] != MAGIC {
263        return Err(Error::fmt_msg("mnf9: bad magic"));
264    }
265    let width = u32::from_le_bytes(buf[4..8].try_into().expect("slice"));
266    let height = u32::from_le_bytes(buf[8..12].try_into().expect("slice"));
267    let res1 = u32::from_le_bytes(buf[12..16].try_into().expect("slice"));
268    let res2 = u32::from_le_bytes(buf[16..20].try_into().expect("slice"));
269    let payload_len = u32::from_le_bytes(buf[20..24].try_into().expect("slice")) as usize;
270    let flags = u32::from_le_bytes(buf[24..28].try_into().expect("slice"));
271    let backend = u32::from_le_bytes(buf[28..32].try_into().expect("slice"));
272    if res1 != 0 || res2 != 0 {
273        return Err(Error::fmt_msg("mnf9: reserved header words set"));
274    }
275    if flags != 0 {
276        return Err(Error::fmt_msg(
277            "mnf9: reserved flags set (grammar has none yet)",
278        ));
279    }
280    if backend != 0 {
281        return Err(Error::fmt_msg("mnf9: reserved backend set"));
282    }
283    if width
284        .checked_mul(height)
285        .and_then(|n| n.checked_mul(3))
286        .is_none()
287    {
288        return Err(Error::fmt_msg("mnf9: dims overflow"));
289    }
290    if buf.len() != HEADER_LEN + payload_len {
291        return Err(Error::fmt_msg("mnf9: payload length mismatch"));
292    }
293    let dims = Dims { width, height };
294    let px = eval_payload(&buf[HEADER_LEN..], dims)?;
295    Ok((dims, px))
296}
297
298// ------------------------------------------------------------------ encoder
299//
300// Discovery solvers (the probe's algorithms, one per family). Each returns
301// the payload (WITHOUT the model-id framing differences) on an exact fit or
302// None. All are bounded (interval intersection, not D-wide scans).
303
304fn exact_const(px: &[u8], wu: usize, hu: usize) -> Option<Vec<u8>> {
305    let first = &px[0..3];
306    (0..wu * hu)
307        .all(|k| &px[3 * k..3 * k + 3] == first)
308        .then(|| {
309            let mut p = vec![MODEL_CONST];
310            p.extend_from_slice(first);
311            p
312        })
313}
314
315/// Solve (N*x + T) div_euclid(D) == col for the D-table; interval-confined.
316fn solve_div_col(col: &[i64], xlen: usize, d: i64) -> Option<(i64, i64)> {
317    if xlen < 2 {
318        return None;
319    }
320    let v0 = col[0];
321    let vend = col[xlen - 1];
322    let num = (vend - v0) * d;
323    let den = xlen as i64 - 1;
324    let base = (num * 2 + den).div_euclid(2 * den); // half-up round
325    for n in base - 2..=base + 2 {
326        let mut lo = v0 * d;
327        let mut hi = v0 * d + d - 1;
328        for &xi in [1usize, xlen / 2, xlen - 1].iter() {
329            if xi >= xlen {
330                continue;
331            }
332            let tl = col[xi] * d - n * xi as i64;
333            lo = lo.max(tl);
334            hi = hi.min(tl + d - 1);
335        }
336        if lo <= hi {
337            for t in lo..=hi {
338                let ok = (0..xlen).all(|x| (n * x as i64 + t).div_euclid(d) == col[x]);
339                if ok {
340                    return Some((n, t));
341                }
342            }
343        }
344    }
345    None
346}
347
348fn exact_div1d(px: &[u8], wu: usize, hu: usize) -> Option<Vec<u8>> {
349    // rows identical + per-channel solve at each table D
350    for y in 0..hu {
351        if px[y * wu * 3..y * wu * 3 + wu * 3] != px[..wu * 3] {
352            return None;
353        }
354    }
355    let row0: Vec<Vec<i64>> = (0..3)
356        .map(|c| (0..wu).map(|x| i64::from(px[x * 3 + c])).collect())
357        .collect();
358    for didx in [0u8, 1, 2, 3, 4, 5, 6, 7] {
359        let d = match den_full(didx, wu as u32) {
360            Ok(d) => d,
361            Err(_) => continue,
362        };
363        let mut params = Vec::with_capacity(24);
364        let mut ok = true;
365        for col_row in row0.iter().take(3) {
366            match solve_div_col(col_row, wu, d) {
367                Some((n, t)) => {
368                    params.extend_from_slice(&(n as i32).to_le_bytes());
369                    params.extend_from_slice(&(t as i32).to_le_bytes());
370                }
371                None => {
372                    ok = false;
373                    break;
374                }
375            }
376        }
377        if ok {
378            let mut p = vec![MODEL_DIV1D];
379            p.extend_from_slice(&params);
380            p.push(didx);
381            return Some(p);
382        }
383    }
384    None
385}
386
387fn exact_rec1(px: &[u8], wu: usize, hu: usize) -> Option<Vec<u8>> {
388    for orientation in [0u8, 1] {
389        let (lin, lines) = if orientation == 0 { (wu, hu) } else { (hu, wu) };
390        if lin < 2 {
391            continue;
392        }
393        let get = |k: usize, li: usize| -> &[u8] {
394            let idx = if orientation == 0 {
395                li * wu * 3 + k * 3
396            } else {
397                k * wu * 3 + li * 3
398            };
399            &px[idx..idx + 3]
400        };
401        // scan lines identical?
402        let mut same = true;
403        for li in 1..lines {
404            for k in 0..lin {
405                if get(k, li) != get(k, 0) {
406                    same = false;
407                    break;
408                }
409            }
410            if !same {
411                break;
412            }
413        }
414        if !same {
415            continue;
416        }
417        // constant signed diff per channel inside the scan line?
418        let mut params = Vec::with_capacity(10);
419        let mut ok = true;
420        for c in 0..3 {
421            let v0 = i64::from(get(0, 0)[c]);
422            let d0 = i64::from(get(1, 0)[c]) - v0;
423            let per_c_step: i16 = d0.clamp(-128, 127) as i16;
424            // verify step constancy under mod-256 signed diff
425            let mut step_ok = true;
426            for k in 2..lin {
427                let prev = i64::from(get(k - 1, 0)[c]);
428                let cur = i64::from(get(k, 0)[c]);
429                let d = (cur - prev + 128).rem_euclid(256) - 128;
430                let expected = (d0 + 128).rem_euclid(256) - 128;
431                if d != expected {
432                    step_ok = false;
433                    break;
434                }
435            }
436            if !step_ok {
437                ok = false;
438                break;
439            }
440            // reconstruction exact?
441            let mut recon_ok = true;
442            for k in 0..lin {
443                let val = (v0 + per_c_step as i64 * k as i64).rem_euclid(256) as u8;
444                if val != get(k, 0)[c] {
445                    recon_ok = false;
446                    break;
447                }
448            }
449            if !recon_ok {
450                ok = false;
451                break;
452            }
453            params.push(get(0, 0)[c]);
454            params.extend_from_slice(&per_c_step.to_le_bytes());
455        }
456        if ok {
457            let mut p = vec![MODEL_REC1];
458            p.extend_from_slice(&params);
459            p.push(orientation);
460            return Some(p);
461        }
462    }
463    None
464}
465
466fn exact_bilinear(px: &[u8], wu: usize, hu: usize, nested: bool) -> Option<Vec<u8>> {
467    if wu < 2 || hu < 2 {
468        return None;
469    }
470    let corners = [0usize, wu - 1];
471    let getv = |y: usize, x: usize, c: usize| u64::from(px[(y * wu + x) * 3 + c]);
472    let (r0, r1) = (0, hu - 1);
473    let (x0, x1) = (0, wu - 1);
474    let mut params = vec![if nested {
475        MODEL_BILIN_NESTED
476    } else {
477        MODEL_BILIN_JOINT
478    }];
479    for c in 0..3 {
480        params.push(getv(r0, x0, c) as u8);
481        params.push(getv(r0, x1, c) as u8);
482        params.push(getv(r1, x0, c) as u8);
483        params.push(getv(r1, x1, c) as u8);
484    }
485    let _ = corners;
486    // verify: the corner values ARE the params; evaluate and compare
487    let payload_ok = eval_payload(
488        &params,
489        Dims {
490            width: wu as u32,
491            height: hu as u32,
492        },
493    )
494    .ok()?;
495    if payload_ok == px {
496        Some(params)
497    } else {
498        None
499    }
500}
501
502/// Segment-local discovery: grow [a, b) over all channels while some
503/// per-segment D solves all three; bounded by the 3*last-good-length rule
504/// measured in the probe (rounding-gap runs of 14+ failures occur).
505fn exact_piecewise(px: &[u8], wu: usize, hu: usize, max_segments: usize) -> Option<Vec<u8>> {
506    for y in 0..hu {
507        if px[y * wu * 3..y * wu * 3 + wu * 3] != px[..wu * 3] {
508            return None;
509        }
510    }
511    let row0: Vec<Vec<i64>> = (0..3)
512        .map(|c| (0..wu).map(|x| i64::from(px[x * 3 + c])).collect())
513        .collect();
514    let dense_pair = |len: usize| -> Vec<i64> {
515        // per-segment natural denominators: {1, 2, 4, len-1, len}
516        let mut v = vec![1i64, 2, 4];
517        if len > 1 {
518            v.push(len as i64 - 1);
519            v.push(len as i64);
520        }
521        v.into_iter().filter(|d| *d > 0).collect()
522    };
523    let solve_seg = |a: usize, b: usize| -> Option<Vec<(i64, i64, i64)>> {
524        let per = dense_pair(b - a);
525        let mut out = Vec::with_capacity(3);
526        for col in row0.iter().take(3) {
527            let col: Vec<i64> = col[a..b].to_vec();
528            let xs = |k: usize| k as i64; // segment-local x
529            let mut got = None;
530            for d in &per {
531                if col.len() < 2 {
532                    break;
533                }
534                let v0 = col[0];
535                let vend = col[col.len() - 1];
536                let num = (vend - v0) * d;
537                let den = col.len() as i64 - 1;
538                let base = (num * 2 + den).div_euclid(2 * den);
539                for n in base - 2..=base + 2 {
540                    let mut lo = v0 * d;
541                    let mut hi = v0 * d + d - 1;
542                    for xi in [1usize, col.len() / 2, col.len() - 1] {
543                        let tl = col[xi] * d - n * xs(xi);
544                        lo = lo.max(tl);
545                        hi = hi.min(tl + d - 1);
546                    }
547                    if lo <= hi {
548                        for t in lo..=hi {
549                            if (0..col.len()).all(|k| (n * xs(k) + t).div_euclid(*d) == col[k]) {
550                                got = Some((*d, n, t));
551                                break;
552                            }
553                        }
554                    }
555                    if got.is_some() {
556                        break;
557                    }
558                }
559                if got.is_some() {
560                    break;
561                }
562            }
563            got?;
564            out.push(got.unwrap());
565        }
566        Some(out)
567    };
568    let mut bounds: Vec<usize> = vec![0];
569    let mut seg_params: Vec<Vec<(i64, i64, i64)>> = Vec::new();
570    let mut a = 0usize;
571    while a < wu {
572        let mut best_b = 0usize;
573        let mut best_entry: Option<Vec<(i64, i64, i64)>> = None;
574        let mut last_ok = 2usize;
575        for b in a + 2..=wu {
576            match solve_seg(a, b) {
577                Some(entry) => {
578                    best_b = b;
579                    best_entry = Some(entry);
580                    last_ok = b - a;
581                }
582                None => {
583                    if best_entry.is_some() && b - a > 3 * last_ok {
584                        break;
585                    }
586                }
587            }
588        }
589        let entry = best_entry.as_ref()?;
590        seg_params.push(entry.clone());
591        bounds.push(best_b);
592        a = best_b;
593        if bounds.len() > max_segments {
594            return None;
595        }
596    }
597    // wire: u8 nseg; (nseg-1) u16 boundaries; per seg+channel: i32 N, i32 T, u8 D-id
598    // D-id must map the solved d onto the convention set {1,2,4,seglen-1,seglen}
599    let mut p = vec![MODEL_PIECEWISE];
600    p.push(seg_params.len() as u8);
601    for k in 1..bounds.len() - 1 + 1 {
602        // boundaries BETWEEN segments only (the final edge = w implicit)
603        if k < bounds.len() - 1 {
604            p.extend_from_slice(&(bounds[k] as u16).to_le_bytes());
605        }
606    }
607    for si in 0..seg_params.len() {
608        let a = bounds[si];
609        let b = bounds[si + 1];
610        let seglen = b - a;
611        for (d, n, t) in &seg_params[si] {
612            let d_index = match d {
613                1 => 0u8,
614                2 => 1,
615                4 => 2,
616                _ if *d == seglen as i64 - 1 => 3,
617                _ if *d == seglen as i64 => 4,
618                _ => return None, // unencodable D (would need more table)
619            };
620            p.extend_from_slice(&(*n as i32).to_le_bytes());
621            p.extend_from_slice(&(*t as i32).to_le_bytes());
622            p.push(d_index);
623        }
624    }
625    // end-to-end self-verification: the pick only claims what decodes exact
626    let recon = eval_payload(
627        &p,
628        Dims {
629            width: wu as u32,
630            height: hu as u32,
631        },
632    )
633    .ok()?;
634    if recon == px {
635        Some(p)
636    } else {
637        None
638    }
639}
640
641/// Try all six families; return the smallest exact-fit payload, or None.
642pub fn discover(px: &[u8], dims: Dims) -> Option<Vec<u8>> {
643    let (wu, hu) = dims_ok(dims).ok()?;
644    let mut best: Option<Vec<u8>> = None;
645    let mut consider = |p: Option<Vec<u8>>| {
646        if let Some(p) = p {
647            if best.as_ref().map(|b| p.len() < b.len()).unwrap_or(true) {
648                best = Some(p);
649            }
650        }
651    };
652    consider(exact_const(px, wu, hu));
653    consider(exact_div1d(px, wu, hu));
654    consider(exact_rec1(px, wu, hu));
655    consider(exact_bilinear(px, wu, hu, false));
656    consider(exact_bilinear(px, wu, hu, true));
657    consider(exact_piecewise(px, wu, hu, 4));
658    best
659}
660
661fn file_bytes(payload: &[u8], dims: Dims) -> Result<Vec<u8>> {
662    let body_len = payload.len();
663    if body_len > u32::MAX as usize {
664        return Err(Error::fmt_msg("mnf9: payload too large"));
665    }
666    let mut f = Vec::with_capacity(HEADER_LEN + body_len);
667    f.extend_from_slice(&MAGIC);
668    f.extend_from_slice(&dims.width.to_le_bytes()); // 4..
669    f.extend_from_slice(&dims.height.to_le_bytes()); // 8..
670    f.extend_from_slice(&0u32.to_le_bytes()); // 12: reserved
671    f.extend_from_slice(&0u32.to_le_bytes()); // 16: reserved
672    f.extend_from_slice(&(body_len as u32).to_le_bytes()); // 20: payload_len
673    f.extend_from_slice(&0u32.to_le_bytes()); // 24: flags (none yet)
674    f.extend_from_slice(&0u32.to_le_bytes()); // 28: backend (none)
675    debug_assert_eq!(f.len(), HEADER_LEN);
676    f.extend_from_slice(payload);
677    Ok(f)
678}
679
680/// Whole-frame generative candidate: an exact rule => Some(file_bytes) else
681/// None (the pick treats None as "arm not available for this image").
682pub fn encode_exact(px: &[u8], dims: Dims) -> Option<Vec<u8>> {
683    let payload = discover(px, dims)?;
684    file_bytes(&payload, dims).ok()
685}
686
687#[cfg(test)]
688mod tests {
689    use super::*;
690
691    fn floor_lerp(w: usize, h: usize) -> Vec<u8> {
692        let mut px = vec![0u8; w * h * 3];
693        for x in 0..w {
694            let v = [
695                (20 * 159 + x * 210) / 159,
696                (230 * 159 - x * 210) / 159,
697                (60 * 159 + x * 140) / 159,
698            ];
699            for y in 0..h {
700                for c in 0..3 {
701                    px[(y * w + x) * 3 + c] = v[c] as u8;
702                }
703            }
704        }
705        px
706    }
707
708    fn recurrence(w: usize, h: usize) -> Vec<u8> {
709        let mut px = vec![0u8; w * h * 3];
710        for x in 0..w {
711            let v = [
712                ((30 + 7 * x) % 256) as u8,
713                ((200 + 7 * x) % 256) as u8,
714                ((90 + 7 * x) % 256) as u8,
715            ];
716            for y in 0..h {
717                px[(y * w + x) * 3..(y * w + x) * 3 + 3].copy_from_slice(&v);
718            }
719        }
720        px
721    }
722
723    #[test]
724    fn exact_discovery_fires_on_rule_classes() {
725        let d = Dims {
726            width: 160,
727            height: 120,
728        };
729        let px = floor_lerp(160, 120);
730        let f = encode_exact(&px, d).expect("floor-lerp discovers");
731        let (rd, rp) = decode_mnf9(&f).expect("mnf9 decode");
732        assert_eq!((rd.width, rd.height), (160u32, 120u32));
733        assert_eq!(rp, px, "exact generative reconstruction");
734        // wire economy: the pick comparator measured 50 B for this class
735        assert!(f.len() < 128, "floor-lerp file = {} B", f.len());
736
737        let px = recurrence(160, 120);
738        let f = encode_exact(&px, d).expect("recurrence discovers");
739        let (_, rp) = decode_mnf9(&f).expect("decode");
740        assert_eq!(rp, px);
741        assert!(f.len() < 64, "rec file = {} B", f.len());
742    }
743
744    #[test]
745    fn no_rule_no_candidate() {
746        // noise: no family fits exactly; the arm stays None and the pick
747        // keeps MNF8 (the probe's measured 14/16 = this polarity)
748        let mut s = 0x1234567u64;
749        let mut px = vec![0u8; 64 * 64 * 3];
750        for b in px.iter_mut() {
751            s ^= s << 13;
752            s ^= s >> 7;
753            s ^= s << 17;
754            *b = (s & 0xFF) as u8;
755        }
756        let d = Dims {
757            width: 64,
758            height: 64,
759        };
760        assert!(encode_exact(&px, d).is_none(), "noise must not discover");
761    }
762
763    #[test]
764    fn malformed_files_error_not_panic() {
765        // bad magic / short / flags set / backend set / payload mismatch /
766        // reserved model id / truncated params / oob piecewise boundary
767        let d = Dims {
768            width: 160,
769            height: 120,
770        };
771        let px = floor_lerp(160, 120);
772        let good = encode_exact(&px, d).expect("discover");
773        // bad magic
774        let mut bad = good.clone();
775        bad[0] = b'X';
776        assert!(decode_mnf9(&bad).is_err());
777        // truncated
778        assert!(decode_mnf9(&good[..10]).is_err());
779        // flags set
780        let mut bad = good.clone();
781        bad[24] = 1;
782        assert!(decode_mnf9(&bad).is_err());
783        // payload length mismatch
784        let mut bad = good.clone();
785        let plen = u32::from_le_bytes(bad[20..24].try_into().unwrap());
786        bad[20..24].copy_from_slice(&(plen + 1).to_le_bytes());
787        assert!(decode_mnf9(&bad).is_err());
788        // reserved model id
789        let mut bad = good.clone();
790        bad[32] = 7;
791        assert!(decode_mnf9(&bad).is_err());
792        // boundary out of range (piecewise)
793        // construct a piecewise payload with boundary 9999 via the encoder:
794        // instead, flip a boundary byte in a crafted piecewise file
795        let pw = exact_piecewise(&px, 160, 120, 4);
796        if pw.is_some() {
797            // piecewise boundaries start at payload offset 2; set 0xFFFF
798            let mut bad = good.clone();
799            let payload = &mut bad[32..];
800            let is_pw = payload[0] == MODEL_PIECEWISE;
801            if is_pw {
802                payload[2] = 0xFF;
803                payload[3] = 0xFF;
804            }
805            let _ = payload; // borrow ends here
806            let is_pw2 = bad[32] == MODEL_PIECEWISE;
807            if is_pw && is_pw2 {
808                assert!(decode_mnf9(&bad).is_err(), "oob boundary must reject");
809            }
810        }
811    }
812}