1use crate::codec::Dims;
20use crate::error::{Error, Result};
21
22pub const MAGIC: [u8; 4] = *b"MNF9";
24pub const HEADER_LEN: usize = 32;
26
27pub const MODEL_CONST: u8 = 1;
29pub const MODEL_DIV1D: u8 = 2;
31pub const MODEL_REC1: u8 = 3;
33pub const MODEL_BILIN_JOINT: u8 = 4;
35pub const MODEL_BILIN_NESTED: u8 = 5;
37pub const MODEL_PIECEWISE: u8 = 6;
39
40fn 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
57fn 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 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)?; 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)?; 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
256pub 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
298fn 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
315fn 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); 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 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(¶ms);
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 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 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 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 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(¶ms);
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 let payload_ok = eval_payload(
488 ¶ms,
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
502fn 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 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; 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 let mut p = vec![MODEL_PIECEWISE];
600 p.push(seg_params.len() as u8);
601 for k in 1..bounds.len() - 1 + 1 {
602 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, };
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 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
641pub 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()); f.extend_from_slice(&dims.height.to_le_bytes()); f.extend_from_slice(&0u32.to_le_bytes()); f.extend_from_slice(&0u32.to_le_bytes()); f.extend_from_slice(&(body_len as u32).to_le_bytes()); f.extend_from_slice(&0u32.to_le_bytes()); f.extend_from_slice(&0u32.to_le_bytes()); debug_assert_eq!(f.len(), HEADER_LEN);
676 f.extend_from_slice(payload);
677 Ok(f)
678}
679
680pub 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 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 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 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 let mut bad = good.clone();
775 bad[0] = b'X';
776 assert!(decode_mnf9(&bad).is_err());
777 assert!(decode_mnf9(&good[..10]).is_err());
779 let mut bad = good.clone();
781 bad[24] = 1;
782 assert!(decode_mnf9(&bad).is_err());
783 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 let mut bad = good.clone();
790 bad[32] = 7;
791 assert!(decode_mnf9(&bad).is_err());
792 let pw = exact_piecewise(&px, 160, 120, 4);
796 if pw.is_some() {
797 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; 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}