From 895a1db8cdaf331480a774b90e2ce0f5467094cf Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 20:04:05 +0000 Subject: [PATCH 1/7] feat: IMG-JPG-2 block painting for every size and plane, any scan component, overlong SOF0 Painting a block is now written as the law-side cover is (decode.splat by rows, columns and pixels), so jpeg_block_cover_all proves it for every sample size, 4 by 4 included, and every plane, and jpeg_block_cover gets a structural proof (the gate drops from about 140 s to 89 s). jpeg_mcu_grid_comp states the A.2.3 grid for any scan component. jpeg_refuse_factor1_any, jpeg_refuse_factor3_any and jpeg_refuse_count refuse a SOF0 with a bad factor or count after any prefix ending at its marker (fill bytes included) and with any length longer than its components. decode.nbits and the entropy walk compare bytes with U32.is_eq instead of literal patterns. Decoded output is byte-identical on every probe and on crafted fill, marker and truncation cases. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 174 ++++++++++++++++++++ PROOF.bend | 215 ++++++++++++++----------- proof/wp10-jpeg-finish.bend | 305 ++++++++++++++++++++++++++++++++++++ src/jpeg.bend | 200 +++++++++++++++-------- 4 files changed, 739 insertions(+), 155 deletions(-) create mode 100644 proof/wp10-jpeg-finish.bend diff --git a/LAWS.bend b/LAWS.bend index 33ba367..ff24886 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -1749,3 +1749,177 @@ law jpeg_walk_mcu: for h_end: {U32.is_lt((comp + 1 : U32), ns) == False{} : Bool} {jpg.adv.at(Jpeg.decode.adv(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri)) == (0, 0, (mcu + 1 : U32)) : U32 & U32 & U32} + +# ---- wp10-jpeg-finish ---- + +# LAW: painting one 8 by 8 block whose samples each cover pw by ph pixels, for every pw and ph (4 by 4 +# included) and onto any plane, one that earlier blocks painted included, writes its sample k over the +# pw by ph pixels at (ox + (k mod 8) * pw, oy + (k div 8) * ph) that lie inside the frame, row by row, +# and nothing else +# IMG-JPG-2 +law jpeg_block_cover_all: + for +pw: U32 + for +ph: U32 + for +ox: U32 + for +oy: U32 + for +ww: U32 + for +hh: U32 + for +samples: List<&2, U32> + for plane: Array + {Jpeg.decode.paint.use(Jpeg.Geom{ox, oy, pw, ph, ww, hh}, True{}, samples, plane) == + jpg.cover(8n, plane, samples, 0, ox, oy, pw, ph, ww, hh) : Array} + +# LAW: a SOF0 segment of one component whose sampling factor is outside 1, 2 and 4 makes decode_jpeg +# none, whatever came before its marker (fill bytes included), however much longer than the component +# its length field makes the segment, and whatever follows +# IMG-JPG-2 +law jpeg_refuse_factor1_any: + for +pre: List<&2, U32> + for +frame: Jpeg.Frame + for +scan: Jpeg.Scan + for +tabs: Jpeg.Tabs + for +ent: List<&2, U32> + for +ri: U32 + for +kind: U32 + for +bad: U32 + for h_walk: {Jpeg.decode.walk(pre, Jpeg.decode.st0()) == Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, kind, + bad} : Jpeg.St} + for +lh: U32 + for +ll: U32 + for +yh: U32 + for +yl: U32 + for +xh: U32 + for +xl: U32 + for +c1: U32 + for +f1: U32 + for +q1: U32 + for +extra: List<&2, U32> + for h_len: {U32.is_lt(Jpeg.decode.u16(lh, ll), 2) == False{} : Bool} + for h_body: {U32.to_nat((Jpeg.decode.u16(lh, ll) - 2 : U32)) == List.length(&2, U32, List.append(&2, U32, + [8, yh, yl, xh, xl, 1, c1, f1, q1], extra)) : Nat} + for h_fac: {jpg.facs(f1) == False{} : Bool} + for +rest: List<&2, U32> + {Img.decode_jpeg(List.append(&2, U32, pre, lh <> ll <> List.append(&2, U32, List.append(&2, U32, + [8, yh, yl, xh, xl, 1, c1, f1, q1], extra), rest))) == None{} : Maybe<&2, Img.Raster>} + +# LAW: a SOF0 segment of three components, one of them with a sampling factor outside 1, 2 and 4, +# makes decode_jpeg none, whatever came before its marker (fill bytes included), however much longer +# than the components its length field makes the segment, and whatever follows +# IMG-JPG-2 +law jpeg_refuse_factor3_any: + for +pre: List<&2, U32> + for +frame: Jpeg.Frame + for +scan: Jpeg.Scan + for +tabs: Jpeg.Tabs + for +ent: List<&2, U32> + for +ri: U32 + for +kind: U32 + for +bad: U32 + for h_walk: {Jpeg.decode.walk(pre, Jpeg.decode.st0()) == Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, kind, + bad} : Jpeg.St} + for +lh: U32 + for +ll: U32 + for +yh: U32 + for +yl: U32 + for +xh: U32 + for +xl: U32 + for +c1: U32 + for +f1: U32 + for +q1: U32 + for +c2: U32 + for +f2: U32 + for +q2: U32 + for +c3: U32 + for +f3: U32 + for +q3: U32 + for +extra: List<&2, U32> + for h_len: {U32.is_lt(Jpeg.decode.u16(lh, ll), 2) == False{} : Bool} + for h_body: {U32.to_nat((Jpeg.decode.u16(lh, ll) - 2 : U32)) == List.length(&2, U32, List.append(&2, U32, + [8, yh, yl, xh, xl, 3, c1, f1, q1, c2, f2, q2, c3, f3, q3], extra)) : Nat} + for h_fac: {Bool.and(Bool.and(jpg.facs(f1), jpg.facs(f2)), jpg.facs(f3)) == False{} : Bool} + for +rest: List<&2, U32> + {Img.decode_jpeg(List.append(&2, U32, pre, lh <> ll <> List.append(&2, U32, List.append(&2, U32, + [8, yh, yl, xh, xl, 3, c1, f1, q1, c2, f2, q2, c3, f3, q3], extra), rest))) == None{} : Maybe<&2, Img.Raster>} + +# LAW: a SOF0 segment whose component count is neither 1 nor 3 makes decode_jpeg none, whatever its +# components and their sampling factors, whatever came before its marker and whatever follows; with the +# two laws above, a SOF0 frame with a factor outside 1, 2 and 4 is none for every component count +# IMG-JPG-2 +law jpeg_refuse_count: + for +pre: List<&2, U32> + for +frame: Jpeg.Frame + for +scan: Jpeg.Scan + for +tabs: Jpeg.Tabs + for +ent: List<&2, U32> + for +ri: U32 + for +kind: U32 + for +bad: U32 + for h_walk: {Jpeg.decode.walk(pre, Jpeg.decode.st0()) == Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, kind, + bad} : Jpeg.St} + for +lh: U32 + for +ll: U32 + for +yh: U32 + for +yl: U32 + for +xh: U32 + for +xl: U32 + for +nf: U32 + for h_nf: {Bool.or(U32.is_eq(nf, 1), U32.is_eq(nf, 3)) == False{} : Bool} + for +comps: List<&2, U32> + for h_len: {U32.is_lt(Jpeg.decode.u16(lh, ll), 2) == False{} : Bool} + for h_body: {U32.to_nat((Jpeg.decode.u16(lh, ll) - 2 : U32)) == List.length(&2, U32, 8 <> yh <> yl <> xh <> xl <> + nf <> comps) : Nat} + for +rest: List<&2, U32> + {Img.decode_jpeg(List.append(&2, U32, pre, lh <> ll <> List.append(&2, U32, 8 <> yh <> yl <> xh <> xl <> nf <> comps, + rest))) == None{} : Maybe<&2, Img.Raster>} + +# the sampling factor in fs (hs or vs) of the frame component that scan component comp names: the +# first frame component whose identifier is the one the scan lists for comp (jpeg_comp_index) +def jpg.fac.of(+comp: U32, +sids: List<&2, U32>, +ids: List<&2, U32>, +fs: List<&2, U32>) -> U32: + Jpeg.decode.at(fs, Jpeg.decode.index(ids, False{}, Jpeg.decode.at(sids, comp), 0)) + +# where the decoder puts data units bi, bi + 1, ... of scan component comp of a scan that lists the +# identifiers sids, in a frame whose components have identifiers ids and factors hs and vs +def jpg.blocks.of( + left: Nat, + +bi: U32, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +mx: U32, + +my: U32, + +hmax: U32, + +vmax: U32, + +ww: U32, + +hh: U32 +) -> List<&2, Jpeg.Geom>: + match left: + case 0n: + [] + case 1n+pp: + Jpeg.decode.geom.go(comp, bi, mx, my, ww, hh, hmax, vmax, sids, ids, hs, vs) <> + jpg.blocks.of(pp, (bi + 1 : U32), comp, sids, ids, hs, vs, mx, my, hmax, vmax, ww, hh) + +# LAW: for every scan component, not only the frame's first, whose frame component has sampling factors +# hi and vi of 1, 2 or 4, the decoder places the component's hi * vi data units of an MCU in T.81 +# A.2.3's order, hi to a row, each sample covering hmax / hi by vmax / vi pixels +# IMG-JPG-2 +law jpeg_mcu_grid_comp: + for +comp: U32 + for +sids: List<&2, U32> + for +ids: List<&2, U32> + for +hs: List<&2, U32> + for +vs: List<&2, U32> + for h_hi: {jpg.fac(jpg.fac.of(comp, sids, ids, hs)) == True{} : Bool} + for h_vi: {jpg.fac(jpg.fac.of(comp, sids, ids, vs)) == True{} : Bool} + for +mx: U32 + for +my: U32 + for +hmax: U32 + for +vmax: U32 + for +ww: U32 + for +hh: U32 + {jpg.blocks.of(U32.to_nat((jpg.fac.of(comp, sids, ids, hs) * jpg.fac.of(comp, sids, ids, vs) : U32)), 0, comp, sids, + ids, hs, vs, mx, my, hmax, vmax, ww, hh) == jpg.grid(U32.to_nat(jpg.fac.of(comp, sids, ids, vs)), 0, + jpg.fac.of(comp, sids, ids, hs), mx, my, hmax, vmax, U32.div(hmax, jpg.fac.of(comp, sids, ids, hs)), + U32.div(vmax, jpg.fac.of(comp, sids, ids, vs)), ww, hh) : List<&2, Jpeg.Geom>} diff --git a/PROOF.bend b/PROOF.bend index cf85c72..871a687 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -16,6 +16,7 @@ import ./proof/wp5-png-decode.bend as W5 import ./proof/wp7-jpeg-struct.bend as W7 import ./src/jpeg_enc.bend as Jenc import ./proof/wp8-jpeg-numeric.bend as W8 +import ./proof/wp10-jpeg-finish.bend as W10 # U32.or with 0xFF000000 first has alpha 255, whatever the other operand: # the 32 bits are taken apart four at a time, and Bool.or(True, b) is True. @@ -4753,97 +4754,6 @@ def Laws.jpeg_mcu_grid(hi, vi, h_hi, h_vi, mx, my, hmax, vmax, ww, hh): w8.grid.vi(2, vi, h_vi, mx, my, hmax, vmax, ww, hh, {==}, {==}, {==}), w8.grid.vi(4, vi, h_vi, mx, my, hmax, vmax, ww, hh, {==}, {==}, {==})) -# what jpeg_block_cover says for sample sizes pw and ph -def w8.cover.eq( - +pw: U32, - +ph: U32, - +ox: U32, - +oy: U32, - +ww: U32, - +hh: U32, - +samples: List<&2, U32>, - +nn: U32 -) -> Type: - {Jpeg.decode.paint.use(Jpeg.Geom{ox, oy, pw, ph, ww, hh}, True{}, samples, Jpeg.decode.plane(nn)) == - Laws.jpg.cover(8n, Jpeg.decode.plane(nn), samples, 0, ox, oy, pw, ph, ww, hh) : Array} - -# that statement unless the sizes are both 4, where nothing is claimed: the 4 by 4 case is never -# normalised, which keeps the gate fast -def w8.cover.if( - both4: Bool, - +pw: U32, - +ph: U32, - +ox: U32, - +oy: U32, - +ww: U32, - +hh: U32, - +samples: List<&2, U32>, - +nn: U32 -) -> Type: - match both4: - case True{}: - Unit - case False{}: - w8.cover.eq(pw, ph, ox, oy, ww, hh, samples, nn) - -# the motive of the case split on pw and ph -def w8.cover.ty( - +pw: U32, - +ph: U32, - +ox: U32, - +oy: U32, - +ww: U32, - +hh: U32, - +samples: List<&2, U32>, - +nn: U32 -) -> Type: - w8.cover.if(Bool.and(U32.is_eq(pw, 4), U32.is_eq(ph, 4)), pw, ph, ox, oy, ww, hh, samples, nn) - -# for one width pw of 1, 2 or 4: every allowed height, one concrete case each -def w8.cover.ph( - +pw: U32, - +ph: U32, - +h_ph: {Laws.jpg.fac(ph) == True{} : Bool}, - +ox: U32, - +oy: U32, - +ww: U32, - +hh: U32, - +samples: List<&2, U32>, - +nn: U32, - p1: w8.cover.ty(pw, 1, ox, oy, ww, hh, samples, nn), - p2: w8.cover.ty(pw, 2, ox, oy, ww, hh, samples, nn), - p4: w8.cover.ty(pw, 4, ox, oy, ww, hh, samples, nn) -) -> w8.cover.ty(pw, ph, ox, oy, ww, hh, samples, nn): - W8.fac_elim(vv => w8.cover.ty(pw, vv, ox, oy, ww, hh, samples, nn), ph, U32.is_eq(ph, 1), {==}, h_ph, p1, p2, p4) - -# with the premise that the sizes are not both 4, the case split's answer is the statement -def w8.cover.use( - both4: Bool, - +pw: U32, - +ph: U32, - +ox: U32, - +oy: U32, - +ww: U32, - +hh: U32, - +samples: List<&2, U32>, - +nn: U32, - h_44: {both4 == False{} : Bool}, - got: w8.cover.if(both4, pw, ph, ox, oy, ww, hh, samples, nn) -) -> w8.cover.eq(pw, ph, ox, oy, ww, hh, samples, nn): - match both4: - case False{}: - got - case True{}: - Empty.absurd(w8.cover.eq(pw, ph, ox, oy, ww, hh, samples, nn), - U32L.false_true(Equal.sym(Bool, True{}, False{}, h_44))) - -def Laws.jpeg_block_cover(pw, ph, h_pw, h_ph, h_44, ox, oy, ww, hh, samples, nn): - w8.cover.use(Bool.and(U32.is_eq(pw, 4), U32.is_eq(ph, 4)), pw, ph, ox, oy, ww, hh, samples, nn, h_44, - W8.fac_elim(vv => w8.cover.ty(vv, ph, ox, oy, ww, hh, samples, nn), pw, U32.is_eq(pw, 1), {==}, h_pw, - w8.cover.ph(1, ph, h_ph, ox, oy, ww, hh, samples, nn, {==}, {==}, {==}), - w8.cover.ph(2, ph, h_ph, ox, oy, ww, hh, samples, nn, {==}, {==}, {==}), - w8.cover.ph(4, ph, h_ph, ox, oy, ww, hh, samples, nn, {==}, {==}, Unit{}))) - # no Cb sample: no colour, whatever the Y and Cr samples def w8.rgb3.nb(aa: Maybe<&2, U32>, cc: Maybe<&2, U32>) -> {None{} == Laws.jpg.rgb3(aa, None{}, cc) : Maybe<&2, U32>}: match aa: @@ -5189,3 +5099,126 @@ def Laws.jpeg_walk_mcu( ): w8.walk.last(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), comp, bi, mx, my, mcu, rst, ww, hmax, ns, ri, h_last, h_end) + +# ---- wp10-jpeg-finish ---- + +def Laws.jpeg_block_cover_all(pw, ph, ox, oy, ww, hh, samples, plane): + W10.cv.all(8n, plane, samples, 0, ox, oy, pw, ph, ww, hh) + +def Laws.jpeg_block_cover(pw, ph, _h_pw, _h_ph, _h_44, ox, oy, ww, hh, samples, nn): + W10.cv.all(8n, Jpeg.decode.plane(nn), samples, 0, ox, oy, pw, ph, ww, hh) + +def Laws.jpeg_mcu_grid_comp(comp, sids, ids, hs, vs, h_hi, h_vi, mx, my, hmax, vmax, ww, hh): + +hi = Laws.jpg.fac.of(comp, sids, ids, hs) + +vi = Laws.jpg.fac.of(comp, sids, ids, vs) + Equal.trans(List<&2, Jpeg.Geom>, Laws.jpg.blocks.of(U32.to_nat((hi * vi : U32)), 0, comp, sids, ids, hs, vs, mx, my, + hmax, vmax, ww, hh), Laws.jpg.blocks(U32.to_nat((hi * vi : U32)), 0, hi, vi, mx, my, hmax, vmax, ww, hh), + Laws.jpg.grid(U32.to_nat(vi), 0, hi, mx, my, hmax, vmax, U32.div(hmax, hi), U32.div(vmax, vi), ww, hh), + W10.grid.of(U32.to_nat((hi * vi : U32)), 0, comp, sids, ids, hs, vs, mx, my, hmax, vmax, ww, hh), + Laws.jpeg_mcu_grid(hi, vi, h_hi, h_vi, mx, my, hmax, vmax, ww, hh)) + +def Laws.jpeg_refuse_factor1_any( + pre, + frame, + scan, + tabs, + ent, + ri, + kind, + bad, + h_walk, + lh, + ll, + yh, + yl, + xh, + xl, + c1, + f1, + q1, + extra, + h_len, + h_body, + h_fac, + rest +): + +body = {List.append(&2, U32, [8, yh, yl, xh, xl, 1, c1, f1, q1], extra) : List<&2, U32>} + w8.tail(pre, lh <> ll <> List.append(&2, U32, body, rest), Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, + kind, bad}, rest, scan, tabs, ent, ri, kind, h_walk, + W10.sof.seg(lh, ll, body, rest, frame, scan, tabs, ent, ri, kind, bad, h_len, h_body, + Equal.cong(Bool, Jpeg.St, ok => Jpeg.decode.read.sof.c(ok, Bool.and(Jpeg.decode.nf.ok(1), U32.is_eq(bad, 0)), + U32.is_eq(Jpeg.decode.vmax([U32.shrn(f1, 4n)], 0), 0), Jpeg.decode.u16(xh, xl), Jpeg.decode.u16(yh, yl), 1, + [c1], [U32.shrn(f1, 4n)], [U32.and(f1, 15)], [q1], scan, tabs, ent, ri, kind), Laws.jpg.facs(f1), False{}, + h_fac))) + +def Laws.jpeg_refuse_factor3_any( + pre, + frame, + scan, + tabs, + ent, + ri, + kind, + bad, + h_walk, + lh, + ll, + yh, + yl, + xh, + xl, + c1, + f1, + q1, + c2, + f2, + q2, + c3, + f3, + q3, + extra, + h_len, + h_body, + h_fac, + rest +): + +body = {List.append(&2, U32, [8, yh, yl, xh, xl, 3, c1, f1, q1, c2, f2, q2, c3, f3, q3], extra) : List<&2, U32>} + w8.tail(pre, lh <> ll <> List.append(&2, U32, body, rest), Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, + kind, bad}, rest, scan, tabs, ent, ri, kind, h_walk, + W10.sof.seg(lh, ll, body, rest, frame, scan, tabs, ent, ri, kind, bad, h_len, h_body, + Equal.cong(Bool, Jpeg.St, ok => Jpeg.decode.read.sof.c(ok, Bool.and(Jpeg.decode.nf.ok(3), U32.is_eq(bad, 0)), + U32.is_eq(Jpeg.decode.vmax([U32.shrn(f1, 4n), U32.shrn(f2, 4n), U32.shrn(f3, 4n)], 0), 0), + Jpeg.decode.u16(xh, xl), Jpeg.decode.u16(yh, yl), 3, [c1, c2, c3], + [U32.shrn(f1, 4n), U32.shrn(f2, 4n), U32.shrn(f3, 4n)], [U32.and(f1, 15), U32.and(f2, 15), U32.and(f3, 15)], + [q1, q2, q3], scan, tabs, ent, ri, kind), + Bool.and(Bool.and(Laws.jpg.facs(f1), Laws.jpg.facs(f2)), Laws.jpg.facs(f3)), False{}, h_fac))) + +def Laws.jpeg_refuse_count( + pre, + frame, + scan, + tabs, + ent, + ri, + kind, + bad, + h_walk, + lh, + ll, + yh, + yl, + xh, + xl, + nf, + h_nf, + comps, + h_len, + h_body, + rest +): + +body = {8 <> yh <> yl <> xh <> xl <> nf <> comps : List<&2, U32>} + w8.tail(pre, lh <> ll <> List.append(&2, U32, body, rest), Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, + kind, bad}, rest, scan, tabs, ent, ri, kind, h_walk, + W10.sof.seg(lh, ll, body, rest, frame, scan, tabs, ent, ri, kind, bad, h_len, h_body, + W10.sof.cnt(Jpeg.decode.read.cs(U32.to_nat(nf), comps, [], [], [], [], True{}), Jpeg.decode.u16(xh, xl), + Jpeg.decode.u16(yh, yl), nf, scan, tabs, ent, ri, kind, bad, h_nf))) diff --git a/proof/wp10-jpeg-finish.bend b/proof/wp10-jpeg-finish.bend new file mode 100644 index 0000000..5c6ed1a --- /dev/null +++ b/proof/wp10-jpeg-finish.bend @@ -0,0 +1,305 @@ +# proof/wp10-jpeg-finish: lemmas for IMG-JPG-2's last parts (block painting, component placement, the +# SOF0 refusals, the MCU walk over the frame, reading the planes out) and IMG-JPG-3's intermediate laws +# (the encoder's header walked by the decoder, the entropy-coded data unstuffed). PROOF.bend imports this +# file as W10, so the gate checks it. +import Base +import ../LAWS.bend as Laws +import ../src/jpeg.bend as Jpeg +import ../src/jpeg_enc.bend as Jenc +import ./u32.bend as U32L +import ./wp7-jpeg-struct.bend as W7 +import ./wp8-jpeg-numeric.bend as W8 + +# ---- block painting (IMG-JPG-2) ---- + +# the decoder's pixel row of one sample's cover is jpg.cover.px's, write for write +def cv.px( + left: Nat, + -plane: Array, + +x0: U32, + +yy: U32, + +px: U32, + +ww: U32, + +hh: U32, + +vv: U32 +) -> {Jpeg.decode.splat.px(left, plane, x0, yy, px, ww, hh, vv) == Laws.jpg.cover.px(left, plane, x0, yy, px, ww, hh, + vv) : Array}: + match left: + case 0n: + {==} + case 1n+pp: + cv.px(pp, Jpeg.decode.splat.in(U32.is_lt((x0 + px : U32), ww), U32.is_lt(yy, hh), plane, (x0 + px : U32), yy, + ww, vv), x0, yy, (px + 1 : U32), ww, hh, vv) + +# the decoder's cover of one sample is jpg.cover.py's, row for row +def cv.py( + left: Nat, + -plane: Array, + +x0: U32, + +y0: U32, + +py: U32, + +pw: U32, + +ww: U32, + +hh: U32, + +vv: U32 +) -> {Jpeg.decode.splat.py(left, plane, x0, y0, py, pw, ww, hh, vv) == Laws.jpg.cover.py(left, plane, x0, y0, py, pw, + ww, hh, vv) : Array}: + match left: + case 0n: + {==} + case 1n+pp: + ee = cv.px(U32.to_nat(pw), plane, x0, (y0 + py : U32), 0, ww, hh, vv) + %ee : {Jpeg.decode.splat.py(pp, Jpeg.decode.splat.px(U32.to_nat(pw), plane, x0, (y0 + py : U32), 0, ww, hh, vv), + x0, y0, (py + 1 : U32), pw, ww, hh, vv) == Laws.jpg.cover.py(pp, _, x0, y0, (py + 1 : U32), pw, ww, hh, vv) : + Array} + cv.py(pp, Jpeg.decode.splat.px(U32.to_nat(pw), plane, x0, (y0 + py : U32), 0, ww, hh, vv), x0, y0, + (py + 1 : U32), pw, ww, hh, vv) + +# the decoder's block row is jpg.cover.col's, sample for sample +def cv.col( + left: Nat, + -plane: Array, + +samples: List<&2, U32>, + +row: U32, + +col: U32, + +ox: U32, + +oy: U32, + +pw: U32, + +ph: U32, + +ww: U32, + +hh: U32 +) -> {Jpeg.decode.splat.col(left, plane, samples, row, col, ox, oy, pw, ph, ww, hh) == + Laws.jpg.cover.col(left, plane, samples, row, col, ox, oy, pw, ph, ww, hh) : Array}: + match left: + case 0n: + {==} + case 1n+pp: + ee = cv.py(U32.to_nat(ph), plane, (ox + col * pw : U32), (oy + row * ph : U32), 0, pw, ww, hh, + Jpeg.decode.at(samples, (row * 8 + col : U32))) + %ee : {Jpeg.decode.splat.col(pp, Jpeg.decode.splat.py(U32.to_nat(ph), plane, (ox + col * pw : U32), + (oy + row * ph : U32), 0, pw, ww, hh, Jpeg.decode.at(samples, (row * 8 + col : U32))), samples, row, + (col + 1 : U32), ox, oy, pw, ph, ww, hh) == Laws.jpg.cover.col(pp, _, samples, row, (col + 1 : U32), ox, oy, + pw, ph, ww, hh) : Array} + cv.col(pp, Jpeg.decode.splat.py(U32.to_nat(ph), plane, (ox + col * pw : U32), (oy + row * ph : U32), 0, pw, + ww, hh, Jpeg.decode.at(samples, (row * 8 + col : U32))), samples, row, (col + 1 : U32), ox, oy, pw, ph, ww, hh) + +# the decoder's block is jpg.cover's, row for row +def cv.all( + left: Nat, + -plane: Array, + +samples: List<&2, U32>, + +row: U32, + +ox: U32, + +oy: U32, + +pw: U32, + +ph: U32, + +ww: U32, + +hh: U32 +) -> {Jpeg.decode.splat(left, plane, samples, row, ox, oy, pw, ph, ww, hh) == + Laws.jpg.cover(left, plane, samples, row, ox, oy, pw, ph, ww, hh) : Array}: + match left: + case 0n: + {==} + case 1n+pp: + ee = cv.col(8n, plane, samples, row, 0, ox, oy, pw, ph, ww, hh) + %ee : {Jpeg.decode.splat(pp, Jpeg.decode.splat.col(8n, plane, samples, row, 0, ox, oy, pw, ph, ww, hh), samples, + (row + 1 : U32), ox, oy, pw, ph, ww, hh) == Laws.jpg.cover(pp, _, samples, (row + 1 : U32), ox, oy, pw, ph, ww, + hh) : Array} + cv.all(pp, Jpeg.decode.splat.col(8n, plane, samples, row, 0, ox, oy, pw, ph, ww, hh), samples, + (row + 1 : U32), ox, oy, pw, ph, ww, hh) + +# ---- placement of any scan component (IMG-JPG-2) ---- + +# a scan component's placement is the placement of a one-component scan with its frame component's factors +def grid.of( + left: Nat, + +bi: U32, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +mx: U32, + +my: U32, + +hmax: U32, + +vmax: U32, + +ww: U32, + +hh: U32 +) -> {Laws.jpg.blocks.of(left, bi, comp, sids, ids, hs, vs, mx, my, hmax, vmax, ww, hh) == Laws.jpg.blocks(left, bi, + Laws.jpg.fac.of(comp, sids, ids, hs), Laws.jpg.fac.of(comp, sids, ids, vs), mx, my, hmax, vmax, ww, hh) : + List<&2, Jpeg.Geom>}: + match left: + case 0n: + {==} + case 1n+pp: + ee = grid.of(pp, (bi + 1 : U32), comp, sids, ids, hs, vs, mx, my, hmax, vmax, ww, hh) + %ee : {Jpeg.decode.geom.go(comp, bi, mx, my, ww, hh, hmax, vmax, sids, ids, hs, vs) <> + Laws.jpg.blocks.of(pp, (bi + 1 : U32), comp, sids, ids, hs, vs, mx, my, hmax, vmax, ww, hh) == + Jpeg.decode.geom.go(0, bi, mx, my, ww, hh, hmax, vmax, [0], [0], [Laws.jpg.fac.of(comp, sids, ids, hs)], + [Laws.jpg.fac.of(comp, sids, ids, vs)]) <> _ : List<&2, Jpeg.Geom>} + {==} + +# ---- the SOF0 refusals (IMG-JPG-2) ---- + +# the state a refused frame header leaves the walk in (the same state as PROOF.bend's w8.stop) +def stop(+sc: Jpeg.Scan, +tb: Jpeg.Tabs, +en: List<&2, U32>, +ri: U32, +kd: U32) -> Jpeg.St: + Jpeg.St{Jpeg.Stop{}, Jpeg.decode.frame0(), sc, tb, en, ri, kd, 1} + +# a SOF0 segment read from just after its marker: when its body makes the frame reader refuse, the +# walk goes on stopped from the end of the body, whatever the body's length +def sof.seg( + +lh: U32, + +ll: U32, + +body: List<&2, U32>, + +rest: List<&2, U32>, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ent: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32, + h_len: {U32.is_lt(Jpeg.decode.u16(lh, ll), 2) == False{} : Bool}, + h_body: {U32.to_nat((Jpeg.decode.u16(lh, ll) - 2 : U32)) == List.length(&2, U32, body) : Nat}, + h_disp: {Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad) == stop(scan, tabs, ent, ri, kind) + : Jpeg.St} +) -> {Jpeg.decode.walk(lh <> ll <> List.append(&2, U32, body, rest), Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, + ri, kind, bad}) == Jpeg.decode.walk(rest, stop(scan, tabs, ent, ri, kind)) : Jpeg.St}: + +slb = {Jpeg.decode.step.len.b(U32.is_lt(Jpeg.decode.u16(lh, ll), 2), 192, Jpeg.decode.u16(lh, ll), frame, scan, tabs, + ent, ri, kind, bad) : Jpeg.St} + Equal.trans(Jpeg.St, Jpeg.decode.walk(List.append(&2, U32, body, rest), slb), + Jpeg.decode.walk(rest, Jpeg.decode.walk(body, slb)), Jpeg.decode.walk(rest, stop(scan, tabs, ent, ri, kind)), + W8.walk_app(body, rest, slb), + Equal.trans(Jpeg.St, Jpeg.decode.walk(rest, Jpeg.decode.walk(body, slb)), + Jpeg.decode.walk(rest, Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad)), + Jpeg.decode.walk(rest, stop(scan, tabs, ent, ri, kind)), + Equal.cong(Jpeg.St, Jpeg.St, st => Jpeg.decode.walk(rest, st), Jpeg.decode.walk(body, slb), + Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad), + W7.seg.read(U32.is_lt(Jpeg.decode.u16(lh, ll), 2), Jpeg.decode.u16(lh, ll), body, 192, frame, scan, tabs, ent, + ri, kind, bad, h_len, h_body)), + Equal.cong(Jpeg.St, Jpeg.St, st => Jpeg.decode.walk(rest, st), + Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad), stop(scan, tabs, ent, ri, kind), + h_disp))) + +# a frame header of a component count the decoder does not read: refused, whatever the components +def sof.cok( + cok: Bool, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ent: List<&2, U32>, + +ri: U32, + +kind: U32 +) -> {Jpeg.decode.read.sof.c(cok, False{}, U32.is_eq(Jpeg.decode.vmax(hs, 0), 0), ww, hh, nf, ids, hs, vs, tq, scan, + tabs, ent, ri, kind) == stop(scan, tabs, ent, ri, kind) : Jpeg.St}: + match cok: + case True{}: + {==} + case False{}: + {==} + +# the components read, whatever they are, then a count the decoder does not read: refused +def sof.cnt( + cc: Jpeg.Comps, + +ww: U32, + +hh: U32, + +nf: U32, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ent: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32, + h_nf: {Bool.or(U32.is_eq(nf, 1), U32.is_eq(nf, 3)) == False{} : Bool} +) -> {Jpeg.decode.read.sof.f(cc, ww, hh, nf, scan, tabs, ent, ri, kind, bad) == stop(scan, tabs, ent, ri, kind) : + Jpeg.St}: + match cc: + case Jpeg.Comps{+ids, +hs, +vs, +tq, cok}: + es = Equal.sym(Bool, Bool.or(U32.is_eq(nf, 1), U32.is_eq(nf, 3)), False{}, h_nf) + %es : {Jpeg.decode.read.sof.c(cok, Bool.and(_, U32.is_eq(bad, 0)), U32.is_eq(Jpeg.decode.vmax(hs, 0), 0), ww, + hh, nf, ids, hs, vs, tq, scan, tabs, ent, ri, kind) == stop(scan, tabs, ent, ri, kind) : Jpeg.St} + sof.cok(cok, ww, hh, nf, ids, hs, vs, tq, scan, tabs, ent, ri, kind) + +# ---- words and bytes ---- + +# Bool.and commutes +def band_comm(aa: Bool, bb: Bool) -> {Bool.and(aa, bb) == Bool.and(bb, aa) : Bool}: + match aa bb: + case True{} True{}: + {==} + case True{} False{}: + {==} + case False{} True{}: + {==} + case False{} False{}: + {==} + +# Word.and commutes +def wand_comm(nn: Nat, aw: Word(nn), bw: Word(nn)) -> {Word.and(nn, aw, bw) == Word.and(nn, bw, aw) : Word(nn)}: + match nn: + case 0n: + {==} + case 1n+pp: + match aw bw: + case WCon{+ab, +at} WCon{+bb, +bt}: + e1 = band_comm(ab, bb) + e2 = wand_comm(pp, at, bt) + %e1 : {WCon{Bool.and(ab, bb), Word.and(pp, at, bt)} == WCon{_, Word.and(pp, bt, at)} : Word(1n+pp)} + %e2 : {WCon{Bool.and(ab, bb), Word.and(pp, at, bt)} == WCon{Bool.and(ab, bb), _} : Word(1n+pp)} + {==} + +# U32.and commutes +def uand_comm(au: U32, bu: U32) -> {U32.and(au, bu) == U32.and(bu, au) : U32}: + match au bu: + case U32{aw} U32{bw}: + Equal.cong(Word(32n), U32, ww => U32{ww}, Word.and(32n, aw, bw), Word.and(32n, bw, aw), wand_comm(32n, aw, bw)) + +# or with False on the right is the bit +def bor_false(bb: Bool) -> {Bool.or(bb, False{}) == bb : Bool}: + match bb: + case True{}: + {==} + case False{}: + {==} + +# or with the zero word on the right is the word +def wor_zero(nn: Nat, aw: Word(nn)) -> {Word.or(nn, aw, Word.zero(nn)) == aw : Word(nn)}: + match nn: + case 0n: + match aw: + case WNil{}: + {==} + case 1n+pp: + match aw: + case WCon{+ab, +at}: + e1 = bor_false(ab) + e2 = wor_zero(pp, at) + es1 = Equal.sym(Bool, Bool.or(ab, False{}), ab, e1) + es2 = Equal.sym(Word(pp), Word.or(pp, at, Word.zero(pp)), at, e2) + %es1 : {WCon{_, Word.or(pp, at, Word.zero(pp))} == WCon{ab, at} : Word(1n+pp)} + %es2 : {WCon{ab, _} == WCon{ab, at} : Word(1n+pp)} + {==} + +# the decoder's u16 of a U32's bits 8 and up and its low byte (the mask first) is the U32, for every U32 +def u16_bits(ww: U32) -> {Jpeg.decode.u16(U32.shrn(ww, 8n), U32.and(255, ww)) == ww : U32}: + match ww: + case U32{WCon{+b0, WCon{+b1, WCon{+b2, WCon{+b3, WCon{+b4, WCon{+b5, WCon{+b6, WCon{+b7, WCon{+b8, WCon{+b9, + WCon{+b10, WCon{+b11, WCon{+b12, WCon{+b13, WCon{+b14, WCon{+b15, WCon{+b16, WCon{+b17, WCon{+b18, WCon{+b19, + WCon{+b20, WCon{+b21, WCon{+b22, WCon{+b23, WCon{+b24, WCon{+b25, WCon{+b26, WCon{+b27, WCon{+b28, WCon{+b29, + WCon{+b30, WCon{+b31, WNil{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}: + +tt = {WCon{b8, WCon{b9, WCon{b10, WCon{b11, WCon{b12, WCon{b13, WCon{b14, WCon{b15, WCon{b16, WCon{b17, WCon{b18, + WCon{b19, WCon{b20, WCon{b21, WCon{b22, WCon{b23, WCon{b24, WCon{b25, WCon{b26, WCon{b27, WCon{b28, WCon{b29, + WCon{b30, WCon{b31, WNil{}}}}}}}}}}}}}}}}}}}}}}}}} : Word(24n)} + ee = Equal.sym(Word(24n), Word.or(24n, tt, Word.zero(24n)), tt, wor_zero(24n, tt)) + %ee : {U32{WCon{b0, WCon{b1, WCon{b2, WCon{b3, WCon{b4, WCon{b5, WCon{b6, WCon{b7, _}}}}}}}}} == U32{WCon{b0, + WCon{b1, WCon{b2, WCon{b3, WCon{b4, WCon{b5, WCon{b6, WCon{b7, WCon{b8, WCon{b9, WCon{b10, WCon{b11, WCon{b12, + WCon{b13, WCon{b14, WCon{b15, WCon{b16, WCon{b17, WCon{b18, WCon{b19, WCon{b20, WCon{b21, WCon{b22, WCon{b23, + WCon{b24, WCon{b25, WCon{b26, WCon{b27, WCon{b28, WCon{b29, WCon{b30, WCon{b31, + WNil{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} : U32} + {==} diff --git a/src/jpeg.bend b/src/jpeg.bend index 8fcb485..4e634c6 100644 --- a/src/jpeg.bend +++ b/src/jpeg.bend @@ -76,10 +76,6 @@ type Adv is Data: type Geom is Data: Geom{ox: U32, oy: U32, pw: U32, ph: U32, w: U32, h: U32} -# sample cursor inside an upsampled block -type Cursor is Data: - Cursor{k: U32, px: U32, py: U32} - # SOF component lists while they are being read, and whether every component # so far was read whole with sampling factors the decoder places type Comps is Data: @@ -489,36 +485,76 @@ def decode.look( case s <> st: decode.look.pref(decode.look(ct, lt, st, code, len), U32.is_eq(c, code), U32.is_eq(l, len), s) -def decode.nbits(left: Nat, eq: Bool, xs: List<&2, U32>, +nn: U32, +buf: U32, +ok: U32, +acc: U32) -> Bits & U32: +# whether a list of bytes opens with want. U32.is_eq, not a literal pattern, so a law reaches a +# symbolic byte. +def decode.head.is(xs: List<&2, U32>, +want: U32) -> Bool: + match xs: + case Nil{}: + False{} + case +bb <> _rest: + U32.is_eq(bb, want) + +# left more bits onto acc, most significant first. nn bits remain in buf, top bit first at bit 7; eq +# says buf is empty. ff says the byte before xs was a 255, and h255 and h0 whether xs opens with 255 +# or 0, each asked with U32.is_eq so a law reaches a symbolic byte. After a 255, 0 is a stuffed 255 +# (eight one bits) and 255 a fill byte; any other byte is a marker, and it or the end of the bytes +# reads zero bits and sets ok to 0. +def decode.nbits( + left: Nat, + eq: Bool, + xs: List<&2, U32>, + ff: Bool, + h255: Bool, + h0: Bool, + +nn: U32, + +buf: U32, + +ok: U32, + +acc: U32 +) -> Bits & U32: match left: case 0n: (Bits{nn, ok, buf, xs}, acc) case 1n+p: match eq: case False{}: - decode.nbits(p, U32.is_eq((nn - 1 : U32), 0), xs, (nn - 1 : U32), U32.and(U32.shl(buf), 255), ok, - U32.or(U32.shl(acc), U32.shrn(buf, 7n))) + decode.nbits(p, U32.is_eq((nn - 1 : U32), 0), xs, ff, h255, h0, (nn - 1 : U32), U32.and(255, U32.shl(buf)), + ok, U32.or(U32.shl(acc), U32.shrn(buf, 7n))) case True{}: match xs: case Nil{}: - decode.nbits(p, False{}, [], 7, 0, 0, U32.shl(acc)) - case 255 <> rest: - match rest: - case Nil{}: - decode.nbits(p, False{}, [], 7, 0, 0, U32.shl(acc)) - case 0 <> more: - decode.nbits(p, False{}, more, 7, 254, ok, U32.or(U32.shl(acc), 1)) - case 255 <> more: - decode.nbits(Succ{p}, True{}, more, 0, 0, ok, acc) - case _m <> more: - decode.nbits(p, False{}, more, 7, 0, 0, U32.shl(acc)) - case +b <> rest: - decode.nbits(p, False{}, rest, 7, U32.and(U32.shl(b), 255), ok, U32.or(U32.shl(acc), U32.shrn(b, 7n))) + decode.nbits(p, False{}, [], False{}, False{}, False{}, 7, 0, 0, U32.shl(acc)) + case +bb <> +rest: + match ff: + case False{}: + match h255: + case True{}: + decode.nbits(Succ{p}, True{}, rest, True{}, decode.head.is(rest, 255), decode.head.is(rest, 0), + 0, 0, ok, acc) + case False{}: + decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), 7, + U32.and(255, U32.shl(bb)), ok, U32.or(U32.shl(acc), U32.shrn(bb, 7n))) + case True{}: + match h255: + case True{}: + decode.nbits(Succ{p}, True{}, rest, True{}, decode.head.is(rest, 255), decode.head.is(rest, 0), + 0, 0, ok, acc) + case False{}: + match h0: + case True{}: + decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), + 7, 254, ok, U32.or(U32.shl(acc), 1)) + case False{}: + decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), + 7, 0, 0, U32.shl(acc)) + +# nn more bits from a reader, the first of them at the next byte when its buffer is empty +def decode.nbits.of(left: Nat, +xs: List<&2, U32>, +nn: U32, +buf: U32, +ok: U32) -> Bits & U32: + decode.nbits(left, U32.is_eq(nn, 0), xs, False{}, decode.head.is(xs, 255), decode.head.is(xs, 0), nn, buf, ok, 0) def decode.one(bits: Bits) -> Bits & U32: match bits: case Bits{+n, +ok, +buf, xs}: - decode.nbits(1n, U32.is_eq(n, 0), xs, n, buf, ok, 0) + decode.nbits.of(1n, xs, n, buf, ok) def decode.ask.bit(tab: Huff, got: Bits & U32, +code: U32, +len: U32) -> Ask: match tab: @@ -563,7 +599,7 @@ def decode.bits.of(bits: Bits) -> Nat & U32 & List<&2, U32> & U32 & U32: def decode.read.n(+cat: U32, bits: Bits) -> Bits & U32: match bits: case Bits{+n, +ok, +buf, xs}: - decode.nbits(U32.to_nat(cat), U32.is_eq(n, 0), xs, n, buf, ok, 0) + decode.nbits.of(U32.to_nat(cat), xs, n, buf, ok) def decode.extend.s(small: Bool, +mag: U32, +cat: U32) -> U32: match small: @@ -1040,26 +1076,6 @@ def decode.geom(ctrl: Ctrl, frame: Frame, scan: Scan) -> Geom: case Scan{_ns, +sids, _td, _ta, _ss, _se, _ah}: decode.geom.go(comp, bi, mx, my, w, h, hmax, vmax, sids, ids, hs, vs) -def decode.cursor.py(more: Bool, +kk: U32, +py: U32) -> Cursor: - match more: - case True{}: - Cursor{kk, 0, (py + 1 : U32)} - case False{}: - Cursor{(kk + 1 : U32), 0, 0} - -def decode.cursor.px(more: Bool, +kk: U32, +px: U32, +py: U32, +ph: U32) -> Cursor: - match more: - case True{}: - Cursor{kk, (px + 1 : U32), py} - case False{}: - decode.cursor.py(U32.is_lt((py + 1 : U32), ph), kk, py) - -def decode.cursor(+kk: U32, +px: U32, +py: U32, +pw: U32, +ph: U32) -> Cursor: - decode.cursor.px(U32.is_lt((px + 1 : U32), pw), kk, px, py, ph) - -def decode.splat.n(+pw: U32, +ph: U32) -> Nat: - U32.to_nat(((pw * ph : U32) * 64 : U32)) - def decode.splat.in(xin: Bool, yin: Bool, plane: Array, +xx: U32, +yy: U32, +ww: U32, +sample: U32) -> Array: match xin: case False{}: @@ -1071,12 +1087,52 @@ def decode.splat.in(xin: Bool, yin: Bool, plane: Array, +xx: U32, +yy: U32, case True{}: Array.set(U32, plane, (yy * ww + xx : U32), sample) -def decode.splat.put( +# one pixel row of the pixels a sample covers: columns x0 + px onward, left pixels left, each written +# only when it lies inside the frame +def decode.splat.px( + left: Nat, plane: Array, - +kk: U32, + +x0: U32, + +yy: U32, +px: U32, + +ww: U32, + +hh: U32, + +vv: U32 +) -> Array: + match left: + case 0n: + plane + case 1n+p: + decode.splat.px(p, decode.splat.in(U32.is_lt((x0 + px : U32), ww), U32.is_lt(yy, hh), plane, (x0 + px : U32), yy, + ww, vv), x0, yy, (px + 1 : U32), ww, hh, vv) + +# the pw by ph pixels one sample covers, from (x0, y0 + py), row by row +def decode.splat.py( + left: Nat, + plane: Array, + +x0: U32, + +y0: U32, +py: U32, - samples: List<&2, U32>, + +pw: U32, + +ww: U32, + +hh: U32, + +vv: U32 +) -> Array: + match left: + case 0n: + plane + case 1n+p: + decode.splat.py(p, decode.splat.px(U32.to_nat(pw), plane, x0, (y0 + py : U32), 0, ww, hh, vv), x0, y0, + (py + 1 : U32), pw, ww, hh, vv) + +# samples 8 * row + col onward of one block row, each over the pw by ph pixels at +# (ox + col * pw, oy + row * ph) +def decode.splat.col( + left: Nat, + plane: Array, + +samples: List<&2, U32>, + +row: U32, + +col: U32, +ox: U32, +oy: U32, +pw: U32, @@ -1084,15 +1140,19 @@ def decode.splat.put( +ww: U32, +hh: U32 ) -> Array: - +x = (ox + U32.mod(kk, 8) * pw + px : U32) - +y = (oy + U32.div(kk, 8) * ph + py : U32) - decode.splat.in(U32.is_lt(x, ww), U32.is_lt(y, hh), plane, x, y, ww, decode.at(samples, kk)) + match left: + case 0n: + plane + case 1n+p: + decode.splat.col(p, decode.splat.py(U32.to_nat(ph), plane, (ox + col * pw : U32), (oy + row * ph : U32), 0, pw, + ww, hh, decode.at(samples, (row * 8 + col : U32))), samples, row, (col + 1 : U32), ox, oy, pw, ph, ww, hh) +# the rows of an 8 by 8 block from row down, each sample replicated over pw by ph pixels def decode.splat( left: Nat, - cur: Cursor, - +samples: List<&2, U32>, plane: Array, + +samples: List<&2, U32>, + +row: U32, +ox: U32, +oy: U32, +pw: U32, @@ -1102,15 +1162,11 @@ def decode.splat( ) -> Array: match left: case 0n: - match cur: - case Cursor{_k, _px, _py}: - +_s = decode.drop(samples) - plane + +_s = decode.drop(samples) + plane case 1n+p: - match cur: - case Cursor{+k, +px, +py}: - decode.splat(p, decode.cursor(k, px, py, pw, ph), samples, - decode.splat.put(plane, k, px, py, samples, ox, oy, pw, ph, ww, hh), ox, oy, pw, ph, ww, hh) + decode.splat(p, decode.splat.col(8n, plane, samples, row, 0, ox, oy, pw, ph, ww, hh), samples, (row + 1 : U32), + ox, oy, pw, ph, ww, hh) def decode.paint.which( which: Bool, @@ -1128,7 +1184,7 @@ def decode.paint.which( +_s = decode.drop(samples) plane case True{}: - decode.splat(decode.splat.n(pw, ph), Cursor{0, 0, 0}, samples, plane, ox, oy, pw, ph, ww, hh) + decode.splat(8n, plane, samples, 0, ox, oy, pw, ph, ww, hh) def decode.paint.use(gg: Geom, which: Bool, samples: List<&2, U32>, plane: Array) -> Array: match gg: @@ -2244,6 +2300,26 @@ def decode.ent.rst.b( case False{}: St{Stop{}, frame, scan, tabs, ent, ri, kind, 1} +# a byte of the entropy-coded data: 255 may open a stuffed pair or a marker, any other byte is data. +# Compares with U32.is_eq, not a literal pattern, so a law reaches a symbolic byte. +def decode.step.ent( + ff: Bool, + +bb: U32, + frame: Frame, + scan: Scan, + tabs: Tabs, + ent: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32 +) -> St: + match ff: + case True{}: + +_b = (bb - bb : U32) + St{EntFF{}, frame, scan, tabs, ent, ri, kind, bad} + case False{}: + St{Ent{}, frame, scan, tabs, bb <> ent, ri, kind, bad} + def decode.step.ph( phase: Phase, +bb: U32, @@ -2272,11 +2348,7 @@ def decode.step.ph( case Pay{+mark, +left, +acc}: decode.step.pay(left, bb, mark, acc, frame, scan, tabs, ent, ri, kind, bad) case Ent{}: - match bb: - case 255: - St{EntFF{}, frame, scan, tabs, ent, ri, kind, bad} - case _: - St{Ent{}, frame, scan, tabs, bb <> ent, ri, kind, bad} + decode.step.ent(U32.is_eq(bb, 255), bb, frame, scan, tabs, ent, ri, kind, bad) case EntFF{}: match bb: case 0: From 245baca7de8bfb5f3091ffb5900ef7c14a5cae63 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 20:27:13 +0000 Subject: [PATCH 2/7] feat: IMG-JPG-2 plane read-out and the MCU walk over the whole frame decode.emit reads a plane point by point in order (decode.points), without the reversed accumulator; the output is the same. jpeg_points_at: sample k of the points is the value Array.get finds at index k, for every plane, through lemmas that a read hands its array back. jpeg_walk_frame: from the scan's first block, the decoder's own decode.adv visits the frame's MCUs in raster order, mw = ceil(w / 8 hmax) to a row, and in each MCU the scan's components and their hi * vi data units in T.81 order, with the unit, component and column counters never wrapping. Decoded output is byte-identical on every probe. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 168 ++++++ PROOF.bend | 40 +- proof/wp10-jpeg-finish.bend | 1119 +++++++++++++++++++++++++++++++++++ src/jpeg.bend | 46 +- 4 files changed, 1339 insertions(+), 34 deletions(-) diff --git a/LAWS.bend b/LAWS.bend index ff24886..c0d95fc 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -1923,3 +1923,171 @@ law jpeg_mcu_grid_comp: ids, hs, vs, mx, my, hmax, vmax, ww, hh) == jpg.grid(U32.to_nat(jpg.fac.of(comp, sids, ids, vs)), 0, jpg.fac.of(comp, sids, ids, hs), mx, my, hmax, vmax, U32.div(hmax, jpg.fac.of(comp, sids, ids, hs)), U32.div(vmax, jpg.fac.of(comp, sids, ids, vs)), ww, hh) : List<&2, Jpeg.Geom>} + +# the value a read of a plane finds +def jpg.val(pp: Array & U32) -> U32: + (_aa, vv) = pp + vv + +# LAW: the decoder reads a plane out point by point: sample k of the first nn points is the value +# Array.get finds at index k, for every plane and every k below nn +# IMG-JPG-2 +law jpeg_points_at: + for +nn: Nat + for plane: Array + for +kk: U32 + for h_kk: {Nat.is_lt(U32.to_nat(kk), nn) == True{} : Bool} + {List.get(&2, U32, Jpeg.decode.points(nn, plane), U32.to_nat(kk)) == Some{jpg.val(Array.get(U32, plane, kk))} : + Maybe<&2, U32>} + +# jj advanced by one, kk times, in U32 +def jpg.idx(kk: Nat, +jj: U32) -> U32: + match kk: + case 0n: + jj + case 1n+pp: + jpg.idx(pp, (jj + 1 : U32)) + +# where a block sits in the walk: scan component, data unit, MCU column, MCU row and MCU number (the +# restart count left at 0) +def jpg.pos(ctrl: Jpeg.Ctrl) -> Jpeg.Ctrl: + match ctrl: + case Jpeg.Ctrl{comp, bi, mx, my, mcu, _rst}: + Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} + +# the positions of the next left blocks the decoder walks to from ctrl, each step its own decode.adv +def jpg.trace( + left: Nat, + +ctrl: Jpeg.Ctrl, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +ri: U32 +) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + jpg.pos(ctrl) <> jpg.trace(pp, Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ctrl, frame, scan, ri)), frame, scan, ri) + +# T.81 A.2.3: data units bi onward of scan component comp in MCU (mx, my), number mcu +def jpg.o.units( + left: Nat, + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32 +) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} <> jpg.o.units(pp, comp, (bi + 1 : U32), mx, my, mcu) + +# scan components comp onward of one MCU, each with its frame component's hi * vi data units +def jpg.o.comps( + left: Nat, + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + List.append(&2, Jpeg.Ctrl, jpg.o.units(U32.to_nat(jpg.units(comp, sids, ids, hs, vs)), comp, 0, + mx, my, mcu), jpg.o.comps(pp, (comp + 1 : U32), mx, my, mcu, sids, ids, hs, vs)) + +# MCUs mx onward of MCU row my, left to right, numbered from mcu, each with the scan's components in order +def jpg.o.row( + left: Nat, + +mx: U32, + +my: U32, + +mcu: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + List.append(&2, Jpeg.Ctrl, jpg.o.comps(U32.to_nat(ns), 0, mx, my, mcu, sids, ids, hs, vs), + jpg.o.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ns, sids, ids, hs, vs)) + +# MCU rows my onward, top to bottom, mw MCUs to a row, numbered from mcu +def jpg.o.rows( + left: Nat, + +my: U32, + +mcu: U32, + +mw: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + List.append(&2, Jpeg.Ctrl, jpg.o.row(U32.to_nat(mw), 0, my, mcu, ns, sids, ids, hs, vs), + jpg.o.rows(pp, (my + 1 : U32), jpg.idx(U32.to_nat(mw), mcu), mw, ns, sids, ids, hs, vs)) + +# ok, and every scan component from comp on, left of them, has at least one data unit +def jpg.units.ok( + left: Nat, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + ok: Bool +) -> Bool: + match left: + case 0n: + ok + case 1n+pp: + jpg.units.ok(pp, (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, U32.is_lt(0, jpg.units(comp, sids, ids, hs, + vs)))) + +# LAW: from the first block of a scan, the decoder's walk (its own decode.adv at every step) visits the +# frame's MCUs in raster order, mw = ceil(w / 8 hmax) to a row and any number of rows, and inside each MCU +# the scan's components in order, each component's hi * vi data units in order (T.81 A.2.1 and A.2.3). +# The U32 counters for the data unit, the component and the MCU column never wrap: each stays below the +# count it is compared with, and the MCU row and number count up in step with the order's own. +# IMG-JPG-2 +law jpeg_walk_frame: + for +ww: U32 + for +hh: U32 + for +nf: U32 + for +ids: List<&2, U32> + for +hs: List<&2, U32> + for +vs: List<&2, U32> + for +tq: List<&2, U32> + for +hmax: U32 + for +vmax: U32 + for +ns: U32 + for +sids: List<&2, U32> + for +td: List<&2, U32> + for +ta: List<&2, U32> + for +ss: U32 + for +se: U32 + for +ah: U32 + for +ri: U32 + for +rst: U32 + for +mh: U32 + for h_ns: {U32.is_lt(0, ns) == True{} : Bool} + for h_mw: {U32.is_lt(0, Jpeg.decode.ceil(ww, (hmax * 8 : U32))) == True{} : Bool} + for h_units: {jpg.units.ok(U32.to_nat(ns), 0, sids, ids, hs, vs, True{}) == True{} : Bool} + {jpg.trace(List.length(&2, Jpeg.Ctrl, jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, + (hmax * 8 : U32)), ns, sids, ids, hs, vs)), Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, + hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == jpg.o.rows(U32.to_nat(mh), 0, 0, + Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} diff --git a/PROOF.bend b/PROOF.bend index 871a687..b0e7614 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -2609,9 +2609,9 @@ def nf3.pk( match three: case True{}: +nn = U32.to_nat((ww * hh : U32)) - +ey = Jpeg.decode.emit(nn, (yy, 0), 0, [], False{}) - +eb = Jpeg.decode.emit(nn, (cb, 0), 0, [], False{}) - +er = Jpeg.decode.emit(nn, (cr, 0), 0, [], False{}) + +ey = Jpeg.decode.points(nn, yy) + +eb = Jpeg.decode.points(nn, cb) + +er = Jpeg.decode.points(nn, cr) (False{}, (ey, (eb, (er, rgbs.eq(ey, eb, er))))) case False{}: none.pk() @@ -2628,7 +2628,7 @@ def nf1.pk( ) -> pk.Ty(pic.px(Jpeg.decode.done.nf1(one, nf, ww, hh, yy, cb, cr))): match one: case True{}: - +ey = Jpeg.decode.emit(U32.to_nat((ww * hh : U32)), (yy, 0), 0, [], False{}) + +ey = Jpeg.decode.points(U32.to_nat((ww * hh : U32)), yy) (True{}, (ey, ([], ([], grays.eq(ey))))) case False{}: nf3.pk(U32.is_eq(nf, 3), ww, hh, yy, cb, cr) @@ -5222,3 +5222,35 @@ def Laws.jpeg_refuse_count( W10.sof.seg(lh, ll, body, rest, frame, scan, tabs, ent, ri, kind, bad, h_len, h_body, W10.sof.cnt(Jpeg.decode.read.cs(U32.to_nat(nf), comps, [], [], [], [], True{}), Jpeg.decode.u16(xh, xl), Jpeg.decode.u16(yh, yl), nf, scan, tabs, ent, ri, kind, bad, h_nf))) + +def Laws.jpeg_points_at(nn, plane, kk, h_kk): + ee = W10.idx.of(kk) + %ee : {List.get(&2, U32, Jpeg.decode.points(nn, plane), U32.to_nat(kk)) == + Some{Laws.jpg.val(Array.get(U32, plane, _))} : Maybe<&2, U32>} + W10.emit.at(nn, U32.to_nat(kk), plane, 0, W10.dup(plane), h_kk) + +def Laws.jpeg_walk_frame( + ww, + hh, + nf, + ids, + hs, + vs, + tq, + hmax, + vmax, + ns, + sids, + td, + ta, + ss, + se, + ah, + ri, + rst, + mh, + h_ns, + h_mw, + h_units +): + W10.walk.frame(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, rst, mh, h_ns, h_mw, h_units) diff --git a/proof/wp10-jpeg-finish.bend b/proof/wp10-jpeg-finish.bend index 5c6ed1a..140b473 100644 --- a/proof/wp10-jpeg-finish.bend +++ b/proof/wp10-jpeg-finish.bend @@ -7,6 +7,7 @@ import ../LAWS.bend as Laws import ../src/jpeg.bend as Jpeg import ../src/jpeg_enc.bend as Jenc import ./u32.bend as U32L +import ./wp6-raster.bend as R import ./wp7-jpeg-struct.bend as W7 import ./wp8-jpeg-numeric.bend as W8 @@ -303,3 +304,1121 @@ def u16_bits(ww: U32) -> {Jpeg.decode.u16(U32.shrn(ww, 8n), U32.and(255, ww)) == WCon{b24, WCon{b25, WCon{b26, WCon{b27, WCon{b28, WCon{b29, WCon{b30, WCon{b31, WNil{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} : U32} {==} + +# ---- reading a plane out (IMG-JPG-2) ---- + +# the size walk keeps the array +def size.eq(aa: Array) -> {Array.size(U32, aa) == (aa, Laws.jpg.val(Array.size(U32, aa))) : + Array & U32}: + match aa: + case ALeaf{_x}: + {==} + case ANode{xs, ys}: + ee = Equal.sym(Array & U32, Array.size(U32, xs), (xs, Laws.jpg.val(Array.size(U32, xs))), size.eq(xs)) + %ee : {Array.size.node(U32, ys, _) == (ANode{xs, ys}, Laws.jpg.val(Array.size.node(U32, ys, _))) : + Array & U32} + {==} + +# a node of equal halves is equal +def node.eq( + -x1: Array, + -y1: Array, + -xs: Array, + -ys: Array, + ex: {x1 == xs : Array}, + ey: {y1 == ys : Array} +) -> {ANode{x1, y1} == ANode{xs, ys} : Array}: + sx = Equal.sym(Array, x1, xs, ex) + sy = Equal.sym(Array, y1, ys, ey) + %sx : {ANode{_, y1} == ANode{xs, ys} : Array} + %sy : {ANode{xs, _} == ANode{xs, ys} : Array} + {==} + +# two copies of an array, each equal to it +def Dup(-aa: Array) -> Type: + &b1: Array -> &b2: Array -> {b1 == aa : Array} & {b2 == aa : Array} + +# copies of an array known equal to another are copies of that one +def dup.tr(-bb: Array, -aa: Array, ee: {bb == aa : Array}, dd: Dup(bb)) -> Dup(aa): + %ee : Dup(_) + dd + +# copies of a node from copies of its halves +def dup.node(-xs: Array, -ys: Array, dx: Dup(xs), dy: Dup(ys)) -> Dup(ANode{xs, ys}): + (x1, x2, e1, e2) = dx + (y1, y2, f1, f2) = dy + (ANode{x1, y1}, ANode{x2, y2}, node.eq(x1, y1, xs, ys, e1, f1), node.eq(x2, y2, xs, ys, e2, f2)) + +# two copies of an array, each equal to it +def dup(aa: Array) -> Dup(aa): + match aa: + case ALeaf{+xx}: + (ALeaf{xx}, ALeaf{xx}, {==}, {==}) + case ANode{xs, ys}: + dup.node(xs, ys, dup(xs), dup(ys)) + +# the result of a read of the tree walk keeps the array: the reads below a node hand the node back +def getif.eq( + -xs: Array, + -ys: Array, + +hh: U32, + +ii: U32, + zz: Bool, + ihx: {Array.get.go(U32, xs, hh, ii) == (xs, Laws.jpg.val(Array.get.go(U32, xs, hh, ii))) : Array & U32}, + ihy: {Array.get.go(U32, ys, hh, U32.sub(ii, hh)) == (ys, Laws.jpg.val(Array.get.go(U32, ys, hh, U32.sub(ii, hh)))) : + Array & U32} +) -> {Array.get.if(U32, xs, ys, hh, ii, zz) == (ANode{xs, ys}, Laws.jpg.val(Array.get.if(U32, xs, ys, hh, ii, zz))) : + Array & U32}: + match zz: + case True{}: + sx = Equal.sym(Array & U32, Array.get.go(U32, xs, hh, ii), (xs, Laws.jpg.val(Array.get.go(U32, xs, hh, ii))), ihx) + %sx : {Array.swap.lo(U32, ys, _) == (ANode{xs, ys}, Laws.jpg.val(Array.swap.lo(U32, ys, _))) : Array & U32} + {==} + case False{}: + sy = Equal.sym(Array & U32, Array.get.go(U32, ys, hh, U32.sub(ii, hh)), + (ys, Laws.jpg.val(Array.get.go(U32, ys, hh, U32.sub(ii, hh)))), ihy) + %sy : {Array.swap.hi(U32, xs, _) == (ANode{xs, ys}, Laws.jpg.val(Array.swap.hi(U32, xs, _))) : Array & U32} + {==} + +# the tree walk of a read hands the array back +def getgo.eq( + aa: Array, + +nn: U32, + +ii: U32 +) -> {Array.get.go(U32, aa, nn, ii) == (aa, Laws.jpg.val(Array.get.go(U32, aa, nn, ii))) : Array & U32}: + match aa: + case ALeaf{+_xx}: + {==} + case ANode{xs, ys}: + getif.eq(xs, ys, U32.shr(nn), ii, U32.is_lt(ii, U32.shr(nn)), getgo.eq(xs, U32.shr(nn), ii), + getgo.eq(ys, U32.shr(nn), U32.sub(ii, U32.shr(nn)))) + +# the size walk's statement moved along an equality of arrays +def size.tr( + -bb: Array, + -aa: Array, + ee: {bb == aa : Array}, + hh: {Array.size(U32, bb) == (bb, Laws.jpg.val(Array.size(U32, bb))) : Array & U32} +) -> {Array.size(U32, aa) == (aa, Laws.jpg.val(Array.size(U32, aa))) : Array & U32}: + %ee : {Array.size(U32, _) == (_, Laws.jpg.val(Array.size(U32, _))) : Array & U32} + hh + +# the tree walk's statement moved along an equality of arrays +def getgo.tr( + -bb: Array, + -aa: Array, + +nn: U32, + +ii: U32, + ee: {bb == aa : Array}, + hh: {Array.get.go(U32, bb, nn, ii) == (bb, Laws.jpg.val(Array.get.go(U32, bb, nn, ii))) : Array & U32} +) -> {Array.get.go(U32, aa, nn, ii) == (aa, Laws.jpg.val(Array.get.go(U32, aa, nn, ii))) : Array & U32}: + %ee : {Array.get.go(U32, _, nn, ii) == (_, Laws.jpg.val(Array.get.go(U32, _, nn, ii))) : Array & U32} + hh + +# a read, once the size walk has handed the array back, hands it back too +def get.from( + -aa: Array, + +ii: U32, + hs: {Array.size(U32, aa) == (aa, Laws.jpg.val(Array.size(U32, aa))) : Array & U32}, + hg: {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == + (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + 1))))) : Array & U32} +) -> {Array.get(U32, aa, ii) == (aa, Laws.jpg.val(Array.get(U32, aa, ii))) : Array & U32}: + ss = Equal.sym(Array & U32, Array.size(U32, aa), (aa, Laws.jpg.val(Array.size(U32, aa))), hs) + %ss : {Array.get.at(U32, ii, _) == (aa, Laws.jpg.val(Array.get.at(U32, ii, _))) : Array & U32} + hg + +# the tree walk's statement at a size read from another copy, moved along both equalities +def getgo.tr2( + -bb: Array, + -cc: Array, + -aa: Array, + +ii: U32, + eb: {bb == aa : Array}, + ec: {cc == aa : Array}, + hh: {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, cc)), 1))) == + (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, cc)), + 1))))) : Array & U32} +) -> {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == + (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + 1))))) : Array & U32}: + %eb : {Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == + (_, Laws.jpg.val(Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + 1))))) : Array & U32} + %ec : {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, _)), 1))) == + (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, _)), + 1))))) : Array & U32} + hh + +# the tree walk hands the array back, at the size read from a second copy +def getgo.at( + -aa: Array, + +ii: U32, + dd: Dup(aa) +) -> {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == + (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + 1))))) : Array & U32}: + (c1, c2, e1, e2) = dd + +nn = Laws.jpg.val(Array.size(U32, c2)) + getgo.tr2(c1, c2, aa, ii, e1, e2, getgo.eq(c1, nn, U32.and(ii, U32.sub(nn, 1)))) + +# a read hands the array back, from copies of it +def get.eq.d( + -aa: Array, + +ii: U32, + dd: Dup(aa) +) -> {Array.get(U32, aa, ii) == (aa, Laws.jpg.val(Array.get(U32, aa, ii))) : Array & U32}: + (b1, b2, e1, e2) = dd + get.from(aa, ii, size.tr(b1, aa, e1, size.eq(b1)), getgo.at(aa, ii, dup.tr(b2, aa, e2, dup(b2)))) + +# a read hands the array back +def get.eq(aa: Array, +ii: U32) -> {Array.get(U32, aa, ii) == (aa, Laws.jpg.val(Array.get(U32, aa, ii))) : + Array & U32}: + get.eq.d(aa, ii, dup(aa)) + +# the read statement moved along an equality of arrays +def get.tr( + -bb: Array, + -aa: Array, + +ii: U32, + ee: {bb == aa : Array}, + hh: {Array.get(U32, bb, ii) == (bb, Laws.jpg.val(Array.get(U32, bb, ii))) : Array & U32} +) -> {Array.get(U32, aa, ii) == (aa, Laws.jpg.val(Array.get(U32, aa, ii))) : Array & U32}: + %ee : {Array.get(U32, _, ii) == (_, Laws.jpg.val(Array.get(U32, _, ii))) : Array & U32} + hh + +# a plane read out from point jj on: element mm is the plane's point jj + mm +def emit.at( + nn: Nat, + mm: Nat, + -aa: Array, + +jj: U32, + dd: Dup(aa), + hm: {Nat.is_lt(mm, nn) == True{} : Bool} +) -> {List.get(&2, U32, Jpeg.decode.emit(nn, Array.get(U32, aa, jj), (jj + 1 : U32)), mm) == + Some{Laws.jpg.val(Array.get(U32, aa, Laws.jpg.idx(mm, jj)))} : Maybe<&2, U32>}: + match nn: + case 0n: + -_d = dd + Empty.absurd({List.get(&2, U32, Jpeg.decode.emit(0n, Array.get(U32, aa, jj), (jj + 1 : U32)), mm) == + Some{Laws.jpg.val(Array.get(U32, aa, Laws.jpg.idx(mm, jj)))} : Maybe<&2, U32>}, U32L.false_true(Equal.trans(Bool, False{}, + Nat.is_lt(mm, 0n), True{}, R.lt_zero_false(mm), hm))) + case 1n+pp: + match mm: + case 0n: + (b1, _b2, e1, _e2) = dd + se = Equal.sym(Array & U32, Array.get(U32, aa, jj), (aa, Laws.jpg.val(Array.get(U32, aa, jj))), + get.tr(b1, aa, jj, e1, get.eq(b1, jj))) + %se : {List.get(&2, U32, Jpeg.decode.emit(1n+pp, _, (jj + 1 : U32)), 0n) == + Some{Laws.jpg.val(Array.get(U32, aa, jj))} : Maybe<&2, U32>} + {==} + case 1n+qq: + (b1, b2, e1, e2) = dd + se = Equal.sym(Array & U32, Array.get(U32, aa, jj), (aa, Laws.jpg.val(Array.get(U32, aa, jj))), + get.tr(b1, aa, jj, e1, get.eq(b1, jj))) + %se : {List.get(&2, U32, Jpeg.decode.emit(1n+pp, _, (jj + 1 : U32)), 1n+qq) == + Some{Laws.jpg.val(Array.get(U32, aa, Laws.jpg.idx(1n+qq, jj)))} : Maybe<&2, U32>} + emit.at(pp, qq, aa, (jj + 1 : U32), dup.tr(b2, aa, e2, dup(b2)), hm) + +# jj advanced one step at a time, mm times, is jj + mm when that stays within the U32 tt +def idx.nat( + +mm: Nat, + +jj: U32, + +tt: U32, + +hh: {Nat.is_le(Nat.add(U32.to_nat(jj), mm), U32.to_nat(tt)) == True{} : Bool} +) -> {U32.to_nat(Laws.jpg.idx(mm, jj)) == Nat.add(U32.to_nat(jj), mm) : Nat}: + match mm: + case 0n: + Equal.sym(Nat, Nat.add(U32.to_nat(jj), 0n), U32.to_nat(jj), R.add_zero(U32.to_nat(jj))) + case 1n+(+pp): + +jn = U32.to_nat(jj) + +h1 = {R.le_trans(Nat.add(jn, 1n), Nat.add(jn, 1n+pp), U32.to_nat(tt), R.le_add_mono(jn, 1n, 1n+pp, + R.le_zero(pp)), hh) : {Nat.is_le(Nat.add(jn, 1n), U32.to_nat(tt)) == True{} : Bool}} + +e1 = {R.u32_add_below(jj, 1, tt, h1) : {U32.to_nat((jj + 1 : U32)) == Nat.add(jn, 1n) : Nat}} + +ea = {Equal.trans(Nat, Nat.add(U32.to_nat((jj + 1 : U32)), pp), Nat.add(Nat.add(jn, 1n), pp), Nat.add(jn, 1n+pp), + Equal.cong(Nat, Nat, xx => Nat.add(xx, pp), U32.to_nat((jj + 1 : U32)), Nat.add(jn, 1n), e1), + R.add_assoc(jn, 1n, pp)) : {Nat.add(U32.to_nat((jj + 1 : U32)), pp) == Nat.add(jn, 1n+pp) : Nat}} + h2 = Equal.trans(Bool, Nat.is_le(Nat.add(U32.to_nat((jj + 1 : U32)), pp), U32.to_nat(tt)), + Nat.is_le(Nat.add(jn, 1n+pp), U32.to_nat(tt)), True{}, Equal.cong(Nat, Bool, xx => Nat.is_le(xx, + U32.to_nat(tt)), Nat.add(U32.to_nat((jj + 1 : U32)), pp), Nat.add(jn, 1n+pp), ea), hh) + Equal.trans(Nat, U32.to_nat(Laws.jpg.idx(pp, (jj + 1 : U32))), Nat.add(U32.to_nat((jj + 1 : U32)), pp), + Nat.add(jn, 1n+pp), idx.nat(pp, (jj + 1 : U32), tt, h2), ea) + +# the index reached by counting kk steps from 0 is kk +def idx.of(+kk: U32) -> {Laws.jpg.idx(U32.to_nat(kk), 0) == kk : U32}: + +kn = U32.to_nat(kk) + +ix = Laws.jpg.idx(kn, 0) + U32L.ueq(ix, kk, Equal.trans(Bool, U32.is_eq(ix, kk), Nat.is_eq(U32.to_nat(ix), kn), True{}, R.u32_eq(ix, kk), + Equal.trans(Bool, Nat.is_eq(U32.to_nat(ix), kn), Nat.is_eq(kn, kn), True{}, Equal.cong(Nat, Bool, + xx => Nat.is_eq(xx, kn), U32.to_nat(ix), kn, idx.nat(kn, 0, kk, R.le_refl(kn))), R.nat_eq_refl(kn)))) + +# ---- the MCU walk over the frame (IMG-JPG-2) ---- + +# the walk's next position, the restart count dropped +def nxt(+ctrl: Jpeg.Ctrl, +fr: Jpeg.Frame, +sc: Jpeg.Scan, +ri: U32) -> Jpeg.Ctrl: + Laws.jpg.pos(Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ctrl, fr, sc, ri))) + +# the positions of the next left blocks, the restart count dropped at every step +def tr(left: Nat, +ctrl: Jpeg.Ctrl, +fr: Jpeg.Frame, +sc: Jpeg.Scan, +ri: U32) -> List<&2, Jpeg.Ctrl>: + match left: + case 0n: + [] + case 1n+pp: + Laws.jpg.pos(ctrl) <> tr(pp, nxt(ctrl, fr, sc, ri), fr, sc, ri) + +# the first MCU of the next MCU row, or the next MCU of this row +def mx.res(inb: Bool, +mcu: U32, +mx: U32, +my: U32) -> Jpeg.Ctrl: + match inb: + case True{}: + Jpeg.Ctrl{0, 0, mx, my, mcu, 0} + case False{}: + Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), mcu, 0} + +# where the walk goes from data unit bi of scan component comp: the next unit, the next component, +# or the next MCU, by whether the component and then the scan have more +def bi.res(more: Bool, next: Bool, inb: Bool, +comp: U32, +bi: U32, +mx: U32, +my: U32, +mcu: U32) -> Jpeg.Ctrl: + match more: + case True{}: + Jpeg.Ctrl{comp, (bi + 1 : U32), mx, my, mcu, 0} + case False{}: + match next: + case True{}: + Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0} + case False{}: + mx.res(inb, (mcu + 1 : U32), (mx + 1 : U32), my) + +# a new MCU starts at unit 0 of component 0, restart or not +def due.pos( + zero: Bool, + hit: Bool, + +mcu: U32, + +mx: U32, + +my: U32, + +rst: U32 +) -> {Laws.jpg.pos(Jpeg.decode.adv.ctrl(Jpeg.decode.adv.due.b(zero, hit, mcu, mx, my, rst))) == + Jpeg.Ctrl{0, 0, mx, my, mcu, 0} : Jpeg.Ctrl}: + match zero hit: + case True{} _hit: + {==} + case False{} True{}: + {==} + case False{} False{}: + {==} + +# the next MCU in this row, or the first of the next row +def mx.pos( + inb: Bool, + +mcu: U32, + +mx: U32, + +my: U32, + +rst: U32, + +ri: U32 +) -> {Laws.jpg.pos(Jpeg.decode.adv.ctrl(Jpeg.decode.adv.mx(inb, mcu, mx, my, rst, ri))) == mx.res(inb, mcu, mx, my) : + Jpeg.Ctrl}: + match inb: + case True{}: + due.pos(U32.is_eq(ri, 0), U32.is_eq(U32.mod(mcu, Jpeg.decode.ri.nz(ri)), 0), mcu, mx, my, rst) + case False{}: + due.pos(U32.is_eq(ri, 0), U32.is_eq(U32.mod(mcu, Jpeg.decode.ri.nz(ri)), 0), mcu, 0, (my + 1 : U32), rst) + +# after a component's last unit: the next component, or the next MCU +def comp.pos( + next: Bool, + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +rst: U32, + +ww: U32, + +hmax: U32, + +ri: U32 +) -> {Laws.jpg.pos(Jpeg.decode.adv.ctrl(Jpeg.decode.adv.comp(next, comp, mx, my, mcu, rst, ww, hmax, ri))) == + bi.res(False{}, next, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu) : + Jpeg.Ctrl}: + match next: + case True{}: + {==} + case False{}: + mx.pos(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my, rst, + ri) + +# one step of the walk from data unit bi +def bi.pos( + more: Bool, + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +rst: U32, + +ww: U32, + +hmax: U32, + +ns: U32, + +ri: U32 +) -> {Laws.jpg.pos(Jpeg.decode.adv.ctrl(Jpeg.decode.adv.bi(more, comp, bi, mx, my, mcu, rst, ww, hmax, ns, ri))) == + bi.res(more, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), + comp, bi, mx, my, mcu) : Jpeg.Ctrl}: + match more: + case True{}: + {==} + case False{}: + comp.pos(U32.is_lt((comp + 1 : U32), ns), comp, bi, mx, my, mcu, rst, ww, hmax, ri) + +# one step of the walk, whatever the restart count: the next unit, component or MCU +def nxt.eq( + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +rst: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32 +) -> {nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == bi.res(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, + vs)), U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, + mx, my, mcu) : Jpeg.Ctrl}: + bi.pos(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), comp, bi, mx, my, mcu, rst, ww, hmax, ns, + ri) + +# the walk from a position does not depend on the restart count it holds +def tr.pos( + left: Nat, + +ctrl: Jpeg.Ctrl, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32 +) -> {tr(left, ctrl, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == + tr(left, Laws.jpg.pos(ctrl), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri) : List<&2, Jpeg.Ctrl>}: + match left: + case 0n: + {==} + case 1n+pp: + match ctrl: + case Jpeg.Ctrl{+comp, +bi, +mx, +my, +mcu, +rst}: + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + +sc = {Jpeg.Scan{ns, sids, td, ta, ss, se, ah} : Jpeg.Scan} + +res = {bi.res(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), + ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu) : Jpeg.Ctrl} + e1 = nxt.eq(comp, bi, mx, my, mcu, rst, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri) + e0 = nxt.eq(comp, bi, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri) + Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, cc => {Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} <> tr(pp, cc, fr, sc, ri) : + List<&2, Jpeg.Ctrl>}, nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, fr, sc, ri), + nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, fr, sc, ri), + Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, fr, sc, ri), res, + nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, fr, sc, ri), e1, Equal.sym(Jpeg.Ctrl, + nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, fr, sc, ri), res, e0))) + +# the decoder's trace is the walk with the restart count dropped +def trace.tr( + +left: Nat, + +ctrl: Jpeg.Ctrl, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32 +) -> {Laws.jpg.trace(left, ctrl, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, + se, ah}, ri) == tr(left, ctrl, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, + se, ah}, ri) : List<&2, Jpeg.Ctrl>}: + match left: + case 0n: + {==} + case 1n+(+pp): + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + +sc = {Jpeg.Scan{ns, sids, td, ta, ss, se, ah} : Jpeg.Scan} + +nc = {Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ctrl, fr, sc, ri)) : Jpeg.Ctrl} + Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, ll => {Laws.jpg.pos(ctrl) <> ll : List<&2, Jpeg.Ctrl>}, + Laws.jpg.trace(pp, nc, fr, sc, ri), tr(pp, Laws.jpg.pos(nc), fr, sc, ri), + Equal.trans(List<&2, Jpeg.Ctrl>, Laws.jpg.trace(pp, nc, fr, sc, ri), tr(pp, nc, fr, sc, ri), + tr(pp, Laws.jpg.pos(nc), fr, sc, ri), + trace.tr(pp, nc, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), + tr.pos(pp, nc, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri))) + +# n is below n + 1 +def lt.succ(+nn: Nat) -> {Nat.is_lt(nn, 1n+nn) == True{} : Bool}: + match nn: + case 0n: + {==} + case 1n+pp: + lt.succ(pp) + +# n is not below n +def lt.irr(+nn: Nat) -> {Nat.is_lt(nn, nn) == False{} : Bool}: + match nn: + case 0n: + {==} + case 1n+pp: + lt.irr(pp) + +# one less than a number above 0 +def npred(nn: Nat) -> Nat: + match nn: + case 0n: + 0n + case 1n+pp: + pp + +# a number above 0 is one more than its predecessor +def succ.of(+nn: Nat, hh: {Nat.is_lt(0n, nn) == True{} : Bool}) -> {1n+npred(nn) == nn : Nat}: + match nn: + case 0n: + Empty.absurd({1n == 0n : Nat}, U32L.false_true(hh)) + case 1n+_pp: + {==} + +# a U32 above 0 is one more than its predecessor, as a number +def pos.nat(+uu: U32, hh: {U32.is_lt(0, uu) == True{} : Bool}) -> {1n+npred(U32.to_nat(uu)) == U32.to_nat(uu) : Nat}: + succ.of(U32.to_nat(uu), Equal.trans(Bool, Nat.is_lt(0n, U32.to_nat(uu)), U32.is_lt(0, uu), True{}, + Equal.sym(Bool, U32.is_lt(0, uu), Nat.is_lt(0n, U32.to_nat(uu)), R.u32_lt(0, uu)), hh)) + +# xx + 1 does not wrap while xx + 1 + qq is some U32's value +def inc.nat( + +xx: U32, + +uu: U32, + +qq: Nat, + +hb: {Nat.add(U32.to_nat(xx), 1n+qq) == U32.to_nat(uu) : Nat} +) -> {U32.to_nat((xx + 1 : U32)) == Nat.add(U32.to_nat(xx), 1n) : Nat}: + +bn = U32.to_nat(xx) + R.u32_add_below(xx, 1, uu, Equal.trans(Bool, Nat.is_le(Nat.add(bn, 1n), U32.to_nat(uu)), + Nat.is_le(Nat.add(bn, 1n), Nat.add(bn, 1n+qq)), True{}, Equal.cong(Nat, Bool, zz => Nat.is_le(Nat.add(bn, 1n), zz), + U32.to_nat(uu), Nat.add(bn, 1n+qq), Equal.sym(Nat, Nat.add(bn, 1n+qq), U32.to_nat(uu), hb)), + R.le_add_mono(bn, 1n, 1n+qq, R.le_zero(qq)))) + +# a counter two or more below its bound: the next is still below it +def lt.more( + +xx: U32, + +uu: U32, + +pp: Nat, + +hb: {Nat.add(U32.to_nat(xx), 1n+(1n+pp)) == U32.to_nat(uu) : Nat} +) -> {U32.is_lt((xx + 1 : U32), uu) == True{} : Bool}: + +bn = U32.to_nat(xx) + +e1 = {inc.nat(xx, uu, 1n+pp, hb) : {U32.to_nat((xx + 1 : U32)) == Nat.add(bn, 1n) : Nat}} + Equal.trans(Bool, U32.is_lt((xx + 1 : U32), uu), Nat.is_lt(U32.to_nat((xx + 1 : U32)), U32.to_nat(uu)), True{}, + R.u32_lt((xx + 1 : U32), uu), Equal.trans(Bool, Nat.is_lt(U32.to_nat((xx + 1 : U32)), U32.to_nat(uu)), + Nat.is_lt(Nat.add(bn, 1n), Nat.add(bn, 1n+(1n+pp))), True{}, Equal.trans(Bool, + Nat.is_lt(U32.to_nat((xx + 1 : U32)), U32.to_nat(uu)), Nat.is_lt(Nat.add(bn, 1n), U32.to_nat(uu)), + Nat.is_lt(Nat.add(bn, 1n), Nat.add(bn, 1n+(1n+pp))), Equal.cong(Nat, Bool, zz => Nat.is_lt(zz, U32.to_nat(uu)), + U32.to_nat((xx + 1 : U32)), Nat.add(bn, 1n), e1), Equal.cong(Nat, Bool, zz => Nat.is_lt(Nat.add(bn, 1n), zz), + U32.to_nat(uu), Nat.add(bn, 1n+(1n+pp)), Equal.sym(Nat, Nat.add(bn, 1n+(1n+pp)), U32.to_nat(uu), hb))), + R.lt_add_mono(bn, 1n, 1n+(1n+pp), {==}))) + +# a counter one below its bound: the next is not below it +def lt.last( + +xx: U32, + +uu: U32, + +hb: {Nat.add(U32.to_nat(xx), 1n) == U32.to_nat(uu) : Nat} +) -> {U32.is_lt((xx + 1 : U32), uu) == False{} : Bool}: + +bn = U32.to_nat(xx) + +e1 = {inc.nat(xx, uu, 0n, hb) : {U32.to_nat((xx + 1 : U32)) == Nat.add(bn, 1n) : Nat}} + Equal.trans(Bool, U32.is_lt((xx + 1 : U32), uu), Nat.is_lt(U32.to_nat((xx + 1 : U32)), U32.to_nat(uu)), False{}, + R.u32_lt((xx + 1 : U32), uu), Equal.trans(Bool, Nat.is_lt(U32.to_nat((xx + 1 : U32)), U32.to_nat(uu)), + Nat.is_lt(U32.to_nat(uu), U32.to_nat(uu)), False{}, Equal.cong(Nat, Bool, zz => Nat.is_lt(zz, U32.to_nat(uu)), + U32.to_nat((xx + 1 : U32)), U32.to_nat(uu), Equal.trans(Nat, U32.to_nat((xx + 1 : U32)), Nat.add(bn, 1n), + U32.to_nat(uu), e1, hb)), lt.irr(U32.to_nat(uu)))) + +# a counter two or more below its bound: the next is one or more below it +def lt.next( + +xx: U32, + +uu: U32, + +pp: Nat, + +hb: {Nat.add(U32.to_nat(xx), 1n+(1n+pp)) == U32.to_nat(uu) : Nat} +) -> {Nat.add(U32.to_nat((xx + 1 : U32)), 1n+pp) == U32.to_nat(uu) : Nat}: + +bn = U32.to_nat(xx) + Equal.trans(Nat, Nat.add(U32.to_nat((xx + 1 : U32)), 1n+pp), Nat.add(Nat.add(bn, 1n), 1n+pp), U32.to_nat(uu), + Equal.cong(Nat, Nat, zz => Nat.add(zz, 1n+pp), U32.to_nat((xx + 1 : U32)), Nat.add(bn, 1n), inc.nat(xx, uu, 1n+pp, + hb)), Equal.trans(Nat, Nat.add(Nat.add(bn, 1n), 1n+pp), Nat.add(bn, 1n+(1n+pp)), U32.to_nat(uu), + R.add_assoc(bn, 1n, 1n+pp), hb)) + +# the last of kk + 1 data units counted from 0 is one below their count +def last.nat( + +kk: Nat, + +uu: U32, + +hs: {1n+kk == U32.to_nat(uu) : Nat} +) -> {Nat.add(U32.to_nat(Laws.jpg.idx(kk, 0)), 1n) == U32.to_nat(uu) : Nat}: + +hle = {Equal.trans(Bool, Nat.is_le(kk, U32.to_nat(uu)), Nat.is_le(kk, 1n+kk), True{}, Equal.cong(Nat, Bool, + zz => Nat.is_le(kk, zz), U32.to_nat(uu), 1n+kk, Equal.sym(Nat, 1n+kk, U32.to_nat(uu), hs)), R.lt_le(kk, 1n+kk, + lt.succ(kk))) : {Nat.is_le(kk, U32.to_nat(uu)) == True{} : Bool}} + +ei = {idx.nat(kk, 0, uu, hle) : {U32.to_nat(Laws.jpg.idx(kk, 0)) == kk : Nat}} + Equal.trans(Nat, Nat.add(U32.to_nat(Laws.jpg.idx(kk, 0)), 1n), Nat.add(kk, 1n), U32.to_nat(uu), + Equal.cong(Nat, Nat, zz => Nat.add(zz, 1n), U32.to_nat(Laws.jpg.idx(kk, 0)), kk, ei), Equal.trans(Nat, + Nat.add(kk, 1n), 1n+Nat.add(kk, 0n), U32.to_nat(uu), R.add_succ(kk, 0n), Equal.trans(Nat, 1n+Nat.add(kk, 0n), + 1n+kk, U32.to_nat(uu), Equal.cong(Nat, Nat, zz => 1n+zz, Nat.add(kk, 0n), kk, R.add_zero(kk)), hs))) + +# a false accumulator stays false +def uok.false( + ll: Nat, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> {Laws.jpg.units.ok(ll, comp, sids, ids, hs, vs, False{}) == False{} : Bool}: + match ll: + case 0n: + {==} + case 1n+pp: + uok.false(pp, (comp + 1 : U32), sids, ids, hs, vs) + +# a true answer had a true accumulator +def uok.acc( + ll: Nat, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + ok: Bool, + hh: {Laws.jpg.units.ok(ll, comp, sids, ids, hs, vs, ok) == True{} : Bool} +) -> {ok == True{} : Bool}: + match ok: + case True{}: + {==} + case False{}: + Empty.absurd({False{} == True{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, Laws.jpg.units.ok(ll, comp, + sids, ids, hs, vs, False{}), True{}, Equal.sym(Bool, Laws.jpg.units.ok(ll, comp, sids, ids, hs, vs, False{}), + False{}, uok.false(ll, comp, sids, ids, hs, vs)), hh))) + +# the length of two lists of positions +def len.app( + xs: List<&2, Jpeg.Ctrl>, + -ys: List<&2, Jpeg.Ctrl> +) -> {List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, xs, ys)) == Nat.add(List.length(&2, Jpeg.Ctrl, xs), + List.length(&2, Jpeg.Ctrl, ys)) : Nat}: + match xs: + case Nil{}: + {==} + case _hd <> tl: + Equal.cong(Nat, Nat, zz => 1n+zz, List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, tl, ys)), + Nat.add(List.length(&2, Jpeg.Ctrl, tl), List.length(&2, Jpeg.Ctrl, ys)), len.app(tl, ys)) + +# appending positions is associative +def app.assoc( + xs: List<&2, Jpeg.Ctrl>, + -ys: List<&2, Jpeg.Ctrl>, + -zs: List<&2, Jpeg.Ctrl> +) -> {List.append(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, xs, ys), zs) == List.append(&2, Jpeg.Ctrl, xs, + List.append(&2, Jpeg.Ctrl, ys, zs)) : List<&2, Jpeg.Ctrl>}: + match xs: + case Nil{}: + {==} + case +hd <> tl: + Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, ll => {hd <> ll : List<&2, Jpeg.Ctrl>}, + List.append(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, tl, ys), zs), List.append(&2, Jpeg.Ctrl, tl, + List.append(&2, Jpeg.Ctrl, ys, zs)), app.assoc(tl, ys, zs)) + +# no positions appended +def app.nil(xs: List<&2, Jpeg.Ctrl>) -> {List.append(&2, Jpeg.Ctrl, xs, []) == xs : List<&2, Jpeg.Ctrl>}: + match xs: + case Nil{}: + {==} + case +hd <> tl: + Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, ll => {hd <> ll : List<&2, Jpeg.Ctrl>}, + List.append(&2, Jpeg.Ctrl, tl, []), tl, app.nil(tl)) + +# the walk from cc visits the positions ll, then goes on from ee: kk blocks after them are the walk from ee +def Run( + -ll: List<&2, Jpeg.Ctrl>, + +cc: Jpeg.Ctrl, + +ee: Jpeg.Ctrl, + +fr: Jpeg.Frame, + +sc: Jpeg.Scan, + +ri: U32, + +kk: Nat +) -> Type: + {tr(Nat.add(List.length(&2, Jpeg.Ctrl, ll), kk), cc, fr, sc, ri) == List.append(&2, Jpeg.Ctrl, ll, tr(kk, ee, fr, sc, + ri)) : List<&2, Jpeg.Ctrl>} + +# two runs, one after the other +def run.cat( + +l1: List<&2, Jpeg.Ctrl>, + +l2: List<&2, Jpeg.Ctrl>, + +c0: Jpeg.Ctrl, + +c1: Jpeg.Ctrl, + +c2: Jpeg.Ctrl, + +fr: Jpeg.Frame, + +sc: Jpeg.Scan, + +ri: U32, + +kk: Nat, + h1: Run(l1, c0, c1, fr, sc, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk)), + h2: Run(l2, c1, c2, fr, sc, ri, kk) +) -> Run(List.append(&2, Jpeg.Ctrl, l1, l2), c0, c2, fr, sc, ri, kk): + +n1 = List.length(&2, Jpeg.Ctrl, l1) + +n2 = List.length(&2, Jpeg.Ctrl, l2) + +t2 = {tr(kk, c2, fr, sc, ri) : List<&2, Jpeg.Ctrl>} + Equal.trans(List<&2, Jpeg.Ctrl>, tr(Nat.add(List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), kk), c0, + fr, sc, ri), tr(Nat.add(n1, Nat.add(n2, kk)), c0, fr, sc, ri), List.append(&2, Jpeg.Ctrl, List.append(&2, + Jpeg.Ctrl, l1, l2), t2), Equal.cong(Nat, List<&2, Jpeg.Ctrl>, zz => tr(zz, c0, fr, sc, ri), + Nat.add(List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), kk), Nat.add(n1, Nat.add(n2, kk)), + Equal.trans(Nat, Nat.add(List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), kk), + Nat.add(Nat.add(n1, n2), kk), Nat.add(n1, Nat.add(n2, kk)), Equal.cong(Nat, Nat, zz => Nat.add(zz, kk), + List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), Nat.add(n1, n2), len.app(l1, l2)), + R.add_assoc(n1, n2, kk))), Equal.trans(List<&2, Jpeg.Ctrl>, tr(Nat.add(n1, Nat.add(n2, kk)), c0, fr, sc, ri), + List.append(&2, Jpeg.Ctrl, l1, tr(Nat.add(n2, kk), c1, fr, sc, ri)), List.append(&2, Jpeg.Ctrl, List.append(&2, + Jpeg.Ctrl, l1, l2), t2), h1, Equal.trans(List<&2, Jpeg.Ctrl>, List.append(&2, Jpeg.Ctrl, l1, tr(Nat.add(n2, kk), c1, + fr, sc, ri)), List.append(&2, Jpeg.Ctrl, l1, List.append(&2, Jpeg.Ctrl, l2, t2)), List.append(&2, Jpeg.Ctrl, + List.append(&2, Jpeg.Ctrl, l1, l2), t2), Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, + zz => List.append(&2, Jpeg.Ctrl, l1, zz), tr(Nat.add(n2, kk), c1, fr, sc, ri), List.append(&2, Jpeg.Ctrl, l2, t2), + h2), Equal.sym(List<&2, Jpeg.Ctrl>, List.append(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2), t2), + List.append(&2, Jpeg.Ctrl, l1, List.append(&2, Jpeg.Ctrl, l2, t2)), app.assoc(l1, l2, t2))))) + +# a run followed by no positions +def run.nil( + +ll: List<&2, Jpeg.Ctrl>, + +cc: Jpeg.Ctrl, + +ee: Jpeg.Ctrl, + +fr: Jpeg.Frame, + +sc: Jpeg.Scan, + +ri: U32, + +kk: Nat, + hh: Run(ll, cc, ee, fr, sc, ri, kk) +) -> Run(List.append(&2, Jpeg.Ctrl, ll, []), cc, ee, fr, sc, ri, kk): + es = Equal.sym(List<&2, Jpeg.Ctrl>, List.append(&2, Jpeg.Ctrl, ll, []), ll, app.nil(ll)) + %es : Run(_, cc, ee, fr, sc, ri, kk) + hh + +# a run whose end is known by another name +def run.end( + -ll: List<&2, Jpeg.Ctrl>, + +cc: Jpeg.Ctrl, + +e1: Jpeg.Ctrl, + +e2: Jpeg.Ctrl, + +fr: Jpeg.Frame, + +sc: Jpeg.Scan, + +ri: U32, + +kk: Nat, + hh: Run(ll, cc, e1, fr, sc, ri, kk), + ee: {e1 == e2 : Jpeg.Ctrl} +) -> Run(ll, cc, e2, fr, sc, ri, kk): + %ee : Run(ll, cc, _, fr, sc, ri, kk) + hh + +# the data units bi onward of a scan component, the last of them ll units on: the walk visits each, then +# goes where the decoder goes from the last +def run.units( + +ll: Nat, + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +kk: Nat, + +hb: {Nat.add(U32.to_nat(bi), 1n+ll) == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat} +) -> Run(Laws.jpg.o.units(1n+ll, comp, bi, mx, my, mcu), Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, + nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(ll, bi), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + match ll: + case 0n: + {==} + case 1n+(+pp): + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + +sc = {Jpeg.Scan{ns, sids, td, ta, ss, se, ah} : Jpeg.Scan} + +cc = {Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} : Jpeg.Ctrl} + +c1 = {Jpeg.Ctrl{comp, (bi + 1 : U32), mx, my, mcu, 0} : Jpeg.Ctrl} + +nn = {Nat.add(List.length(&2, Jpeg.Ctrl, Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu)), kk) : Nat} + +en = {Equal.trans(Jpeg.Ctrl, nxt(cc, fr, sc, ri), bi.res(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu), c1, + nxt.eq(comp, bi, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu), + U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), True{}, lt.more(bi, Laws.jpg.units(comp, sids, ids, hs, vs), pp, hb))) : {nxt(cc, fr, sc, ri) == c1 : Jpeg.Ctrl}} + Equal.trans(List<&2, Jpeg.Ctrl>, cc <> tr(nn, nxt(cc, fr, sc, ri), fr, sc, ri), cc <> tr(nn, c1, fr, sc, ri), + cc <> List.append(&2, Jpeg.Ctrl, Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu), tr(kk, + nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(pp, (bi + 1 : U32)), mx, my, mcu, 0}, fr, sc, ri), fr, sc, ri)), + Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, dd => {cc <> tr(nn, dd, fr, sc, ri) : List<&2, Jpeg.Ctrl>}, nxt(cc, fr, sc, ri), c1, en), + Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, zz => {cc <> zz : List<&2, Jpeg.Ctrl>}, tr(nn, c1, fr, sc, ri), List.append(&2, Jpeg.Ctrl, + Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu), tr(kk, nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(pp, + (bi + 1 : U32)), mx, my, mcu, 0}, fr, sc, ri), fr, sc, ri)), run.units(pp, comp, (bi + 1 : U32), mx, my, mcu, + ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, lt.next(bi, Laws.jpg.units(comp, sids, ids, hs, vs), pp, hb)))) + +# where the walk goes after a component's last unit, when the scan has more components +def comp.end.more( + +kk: Nat, + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +hs1: {1n+kk == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}, + +hnext: {U32.is_lt((comp + 1 : U32), ns) == True{} : Bool} +) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == + Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0} : Jpeg.Ctrl}: + +bl = Laws.jpg.idx(kk, 0) + Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), + bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, + bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), + Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, + Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), + comp, bl, mx, my, mcu), U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), False{}, lt.last(bl, Laws.jpg.units(comp, sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, ids, hs, vs), hs1))), + Equal.cong(Bool, Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + U32.is_lt((comp + 1 : U32), ns), True{}, hnext))) + +# where the walk goes after the last component's last unit: the next MCU +def comp.end.last( + +kk: Nat, + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +hs1: {1n+kk == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}, + +hnext: {U32.is_lt((comp + 1 : U32), ns) == False{} : Bool} +) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl}: + +bl = Laws.jpg.idx(kk, 0) + Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), + bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, + bl, mx, my, mcu), mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), + nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), Equal.cong(Bool, Jpeg.Ctrl, + mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), False{}, lt.last(bl, Laws.jpg.units(comp, sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, ids, hs, vs), hs1))), Equal.cong(Bool, + Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + U32.is_lt((comp + 1 : U32), ns), False{}, hnext))) + +# a scan component's units, counted as the decoder counts them: all of them, from unit 0 +def run.comp.units( + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +kk: Nat, + +ee: Jpeg.Ctrl, + +hpos: {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}, + hend: {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))), 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == ee : + Jpeg.Ctrl} +) -> Run(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) + +es = {pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos) : {1n+k1 == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}} + %es : Run(Laws.jpg.o.units(_, comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, + kk) + run.end(Laws.jpg.o.units(1n+k1, comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, + nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(k1, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, + run.units(k1, comp, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, es), hend) + +# the scan's components comp onward of one MCU, the last ll components on: the walk visits their units in +# order, then goes to the next MCU +def run.comps( + +ll: Nat, + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +kk: Nat, + +ok: Bool, + +hc: {Nat.add(U32.to_nat(comp), 1n+ll) == U32.to_nat(ns) : Nat}, + +hu: {Laws.jpg.units.ok(1n+ll, comp, sids, ids, hs, vs, ok) == True{} : Bool} +) -> Run(Laws.jpg.o.comps(1n+ll, comp, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + match ll: + case 0n: + +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(0n, (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, + U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} + +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + run.nil(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, em, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, em, hpos, comp.end.last(k1, comp, mx, my, + mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos), lt.last(comp, ns, hc)))) + case 1n+(+pp): + +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(1n+pp, (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, + U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} + +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +c1 = {Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0} : Jpeg.Ctrl} + +l2 = {Laws.jpg.o.comps(1n+pp, (comp + 1 : U32), mx, my, mcu, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + run.cat(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), l2, Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, c1, + em, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), + kk), c1, hpos, comp.end.more(k1, comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos), lt.more(comp, ns, pp, hc))), + run.comps(pp, (comp + 1 : U32), mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, Bool.and(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), lt.next(comp, ns, + pp, hc), hu)) + +# the MCUs mx onward of MCU row my, the last ll MCUs on: the walk visits each MCU's units in order, then +# goes to the first MCU of the next row +def run.row( + +ll: Nat, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +kk: Nat, + +n1: Nat, + +hn: {1n+n1 == U32.to_nat(ns) : Nat}, + +hu: {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : Bool}, + +hx: {Nat.add(U32.to_nat(mx), 1n+ll) == U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Nat} +) -> Run(Laws.jpg.o.row(1n+ll, mx, my, mcu, ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, + Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+ll, mcu), 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + match ll: + case 0n: + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +et = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), (mcu + 1 : U32), 0} : Jpeg.Ctrl} + %hn : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.comps(_, 0, mx, my, mcu, sids, ids, hs, vs), []), + Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) + run.nil(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, + 0}, em, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comps(n1, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, True{}, hn, hu), + Equal.cong(Bool, Jpeg.Ctrl, ib => mx.res(ib, (mcu + 1 : U32), (mx + 1 : U32), my), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), False{}, lt.last(mx, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), hx)))) + case 1n+(+pp): + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +c1 = {Jpeg.Ctrl{0, 0, (mx + 1 : U32), my, (mcu + 1 : U32), 0} : Jpeg.Ctrl} + +et = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+(1n+pp), mcu), 0} : Jpeg.Ctrl} + +l2 = {Laws.jpg.o.row(1n+pp, (mx + 1 : U32), my, (mcu + 1 : U32), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + %hn : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.comps(_, 0, mx, my, mcu, sids, ids, hs, vs), l2), + Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) + run.cat(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), l2, Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, c1, et, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, + my, mcu, 0}, em, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.comps(n1, 0, mx, my, + mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), True{}, hn, hu), Equal.cong(Bool, Jpeg.Ctrl, + ib => mx.res(ib, (mcu + 1 : U32), (mx + 1 : U32), my), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), True{}, lt.more(mx, + Jpeg.decode.ceil(ww, (hmax * 8 : U32)), pp, hx))), run.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, n1, hn, hu, lt.next(mx, + Jpeg.decode.ceil(ww, (hmax * 8 : U32)), pp, hx))) + +# where the walk is after mm MCU rows from row my, MCU number mcu, mw MCUs to a row +def rows.end(mm: Nat, +my: U32, +mcu: U32, +mw: U32) -> Jpeg.Ctrl: + match mm: + case 0n: + Jpeg.Ctrl{0, 0, 0, my, mcu, 0} + case 1n+pp: + rows.end(pp, (my + 1 : U32), Laws.jpg.idx(U32.to_nat(mw), mcu), mw) + +# MCU rows my onward, mm of them: the walk visits each row's MCUs in order +def run.rows( + +mm: Nat, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +kk: Nat, + +n1: Nat, + +hn: {1n+n1 == U32.to_nat(ns) : Nat}, + +hu: {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : Bool}, + +w1: Nat, + +hw: {1n+w1 == U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Nat} +) -> Run(Laws.jpg.o.rows(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, + rows.end(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32))), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + match mm: + case 0n: + {==} + case 1n+(+pp): + +m2 = {Laws.jpg.idx(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), mcu) : U32} + +c1 = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), m2, 0} : Jpeg.Ctrl} + +l2 = {Laws.jpg.o.rows(pp, (my + 1 : U32), m2, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +et = {rows.end(pp, (my + 1 : U32), m2, Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Jpeg.Ctrl} + %hw : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.row(_, 0, my, mcu, ns, sids, ids, hs, vs), l2), + Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) + run.cat(Laws.jpg.o.row(1n+w1, 0, my, mcu, ns, sids, ids, hs, vs), l2, Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, c1, et, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.row(1n+w1, 0, my, mcu, ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, 0, my, + mcu, 0}, Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+w1, mcu), 0}, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, + Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.row(w1, 0, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, + l2), kk), n1, hn, hu, hw), Equal.cong(Nat, Jpeg.Ctrl, nn => {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), + Laws.jpg.idx(nn, mcu), 0} : Jpeg.Ctrl}, 1n+w1, U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), hw)), run.rows(pp, (my + 1 : U32), m2, + ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, n1, hn, hu, w1, hw)) + +# jpeg_walk_frame: the decoder's trace from the scan's first block is the T.81 order +def walk.frame( + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +rst: U32, + +mh: U32, + +h_ns: {U32.is_lt(0, ns) == True{} : Bool}, + +h_mw: {U32.is_lt(0, Jpeg.decode.ceil(ww, (hmax * 8 : U32))) == True{} : Bool}, + +h_units: {Laws.jpg.units.ok(U32.to_nat(ns), 0, sids, ids, hs, vs, True{}) == True{} : Bool} +) -> {Laws.jpg.trace(List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs)), + Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, + hs, vs) : List<&2, Jpeg.Ctrl>}: + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + +sc = {Jpeg.Scan{ns, sids, td, ta, ss, se, ah} : Jpeg.Scan} + +ll = {Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +ln = List.length(&2, Jpeg.Ctrl, ll) + +n1 = npred(U32.to_nat(ns)) + +hn = {pos.nat(ns, h_ns) : {1n+n1 == U32.to_nat(ns) : Nat}} + +c0 = {Jpeg.Ctrl{0, 0, 0, 0, 0, 0} : Jpeg.Ctrl} + +hu = {Equal.trans(Bool, Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}), Laws.jpg.units.ok(U32.to_nat(ns), + 0, sids, ids, hs, vs, True{}), True{}, Equal.cong(Nat, Bool, nn => Laws.jpg.units.ok(nn, 0, sids, ids, hs, vs, + True{}), 1n+n1, U32.to_nat(ns), hn), h_units) : {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : + Bool}} + Equal.trans(List<&2, Jpeg.Ctrl>, Laws.jpg.trace(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), tr(ln, + Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), ll, trace.tr(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), + Equal.trans(List<&2, Jpeg.Ctrl>, tr(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), tr(ln, c0, fr, sc, ri), ll, + tr.pos(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.trans(List<&2, Jpeg.Ctrl>, tr(ln, c0, fr, sc, ri), + tr(Nat.add(ln, 0n), c0, fr, sc, ri), ll, Equal.cong(Nat, List<&2, Jpeg.Ctrl>, nn => tr(nn, c0, fr, sc, ri), ln, + Nat.add(ln, 0n), Equal.sym(Nat, Nat.add(ln, 0n), ln, R.add_zero(ln))), Equal.trans(List<&2, Jpeg.Ctrl>, + tr(Nat.add(ln, 0n), c0, fr, sc, ri), List.append(&2, Jpeg.Ctrl, ll, []), ll, run.rows(U32.to_nat(mh), 0, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, + 0n, n1, hn, hu, npred(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32)))), pos.nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32)), h_mw)), app.nil(ll))))) diff --git a/src/jpeg.bend b/src/jpeg.bend index 4e634c6..0ea9394 100644 --- a/src/jpeg.bend +++ b/src/jpeg.bend @@ -1220,36 +1220,23 @@ def decode.depth(+nn: U32) -> Nat: def decode.plane(+nn: U32) -> Array: Array.new(U32, decode.depth(nn), 0) -def decode.emit(left: Nat, got: Array & U32, +ii: U32, acc: List<&2, U32>, fresh: Bool) -> List<&2, U32>: +# the plane's points in order: got is the plane and the value of point ii - 1, just read, then the +# left - 1 points from ii on. One read past the last point is made and dropped. +def decode.emit(left: Nat, got: Array & U32, +ii: U32) -> List<&2, U32>: match left: case 0n: - match got: - case (a, +v): - match ii: - case _j: - match acc: - case ys: - match fresh: - case False{}: - +_a = decode.sink(a) - +_v = (v - v : U32) - List.reverse(&2, U32, ys) - case True{}: - +_a = decode.sink(a) - List.reverse(&2, U32, v <> ys) + (a, +v) = got + +_a = decode.sink(a) + +_v = (v - v : U32) + +_i = (ii - ii : U32) + [] case 1n+p: - match got: - case (a, +v): - match ii: - case +j: - match acc: - case ys: - match fresh: - case False{}: - +_v = (v - v : U32) - decode.emit(p, Array.get(U32, a, j), (j + 1 : U32), ys, True{}) - case True{}: - decode.emit(p, Array.get(U32, a, j), (j + 1 : U32), v <> ys, True{}) + (a, v) = got + v <> decode.emit(p, Array.get(U32, a, ii), (ii + 1 : U32)) + +# the first nn points of a plane, in order +def decode.points(+nn: Nat, plane: Array) -> List<&2, U32>: + decode.emit(nn, Array.get(U32, plane, 0), 1) # a sample's colour bits made opaque: alpha 255 over the low 24 bits. The # constant is the first operand, so a law over any sample sees its alpha @@ -1341,13 +1328,12 @@ def decode.done.drop(yy: Array, cb: Array, cr: Array) -> Maybe<&2 def decode.done.gray(+ww: U32, +hh: U32, yy: Array, cb: Array, cr: Array) -> Maybe<&2, Pic>: +_b = decode.sink(cb) +_c = decode.sink(cr) - Some{Pic{ww, hh, decode.grays(decode.emit(U32.to_nat((ww * hh : U32)), (yy, 0), 0, [], False{}))}} + Some{Pic{ww, hh, decode.grays(decode.points(U32.to_nat((ww * hh : U32)), yy))}} # a colour picture: each point's Y, Cb and Cr read out in order, then packed def decode.done.color(+ww: U32, +hh: U32, yy: Array, cb: Array, cr: Array) -> Maybe<&2, Pic>: +n = U32.to_nat((ww * hh : U32)) - Some{Pic{ww, hh, decode.rgbs(decode.emit(n, (yy, 0), 0, [], False{}), decode.emit(n, (cb, 0), 0, [], False{}), - decode.emit(n, (cr, 0), 0, [], False{}))}} + Some{Pic{ww, hh, decode.rgbs(decode.points(n, yy), decode.points(n, cb), decode.points(n, cr))}} # three components are colour; any other count is none def decode.done.nf3(three: Bool, +ww: U32, +hh: U32, yy: Array, cb: Array, cr: Array) -> Maybe<&2, Pic>: From 737d76ff3b0700e0113020652a66abd50e609c1c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 21:25:09 +0000 Subject: [PATCH 3/7] feat: IMG-JPG-3 round trip up to the scan decode, IMG-JPG-2 block count jpeg_enc_stuffed: the encoder's entropy-coded bytes have every 255 followed by a 0. The encoder now keeps raw bytes and stuffs them once at the flush (encode.stuff.all), and dispatches on its tag with U32.is_eq; its output is the same. jpeg_enc_header_walk: the decoder's walk over the encoder's header reaches the data with the encoder's frame, scan and tables, for every size. jpeg_ent_walk: the walk keeps stuffed data whole and stops at EOI. jpeg_round_trip_scan: decode_jpeg(encode_jpeg(r)) is the scan decode of the encoder's own entropy-coded bytes in r's frame. jpeg_run_sized and jpeg_round_trip_sized: that decode is none or a raster of r's size. jpeg_walk_count: the decoder's U32 block count, now named decode.nblocks, is the length of jpeg_walk_frame's order when it fits a U32. SPEC.md records what is left of IMG-JPG-2 and IMG-JPG-3, and that IMG-JPG-2 is false for frames above 2^31 points (the plane depth wraps). Every probe output identical, Pillow check 40 of 40. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 169 ++++++ PROOF.bend | 76 ++- SPEC.md | 12 +- docs/rfc/ezimg-law-inventory.md | 1 + proof/wp10-jpeg-finish.bend | 992 ++++++++++++++++++++++++++++---- src/jpeg.bend | 18 +- src/jpeg_enc.bend | 60 +- 7 files changed, 1187 insertions(+), 141 deletions(-) diff --git a/LAWS.bend b/LAWS.bend index 5c49c3b..7595f23 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -2271,3 +2271,172 @@ law jpeg_walk_frame: (hmax * 8 : U32)), ns, sids, ids, hs, vs)), Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +# a byte list in which every 255 is followed by a 0 (T.81 F.1.2.3), ff saying the byte before xs was a +# 255 and ok that every 255 so far was +def jpg.stuffed(xs: List<&2, U32>, ff: Bool, ok: Bool) -> Bool: + match xs: + case Nil{}: + Bool.and(ok, Bool.not(ff)) + case +bb <> rest: + match ff: + case True{}: + jpg.stuffed(rest, False{}, Bool.and(ok, U32.is_eq(bb, 0))) + case False{}: + jpg.stuffed(rest, U32.is_eq(bb, 255), ok) + +# LAW: the entropy-coded bytes encode_jpeg writes, for every size and every samples, have every 255 +# followed by a 0 +# IMG-JPG-3 +law jpeg_enc_stuffed: + for +ww: U32 + for +hh: U32 + for px: List<&2, U32> + {jpg.stuffed(Jenc.encode.arm(ww, hh, px), False{}, True{}) == True{} : Bool} + +# the tables the decoder holds after reading encode_jpeg's DQT and two DHT segments: quantisation table 0 +# all ones, and the Annex K luminance DC and AC tables as table 0 of each class +def jpg.enc.tabs() -> Jpeg.Tabs: + Jpeg.Tabs{List.replicate(U32, 64n, 1), [], [], [], Jpeg.decode.canon(272n, 0n, Jenc.encode.dccounts(), + Jenc.encode.dcsyms(), 0, 0, [], [], []), Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), + Jpeg.decode.canon(272n, 0n, Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), Jpeg.decode.huff0(), + Jpeg.decode.huff0(), Jpeg.decode.huff0()} + +# the frame encode_jpeg's SOF0 carries: its width and height, three components 1, 2 and 3, each sampled +# 1 by 1 with quantisation table 0 +def jpg.enc.frame(+ww: U32, +hh: U32) -> Jpeg.Frame: + Jpeg.Frame{ww, hh, 3, [1, 2, 3], [1, 1, 1], [1, 1, 1], [0, 0, 0], 1, 1} + +# the scan encode_jpeg's SOS opens: components 1, 2 and 3 with Huffman tables 0, all of 0 to 63 +def jpg.enc.scan() -> Jpeg.Scan: + Jpeg.Scan{3, [1, 2, 3], [0, 0, 0], [0, 0, 0], 0, 63, 0} + +# LAW: the decoder's marker walk over encode_jpeg's header (SOI, APP0, DQT, SOF0, two DHT, SOS) reaches +# the entropy-coded data with the frame carrying the encoder's width and height, whatever they are, the +# encoder's scan and tables, no restart interval, a baseline frame read and nothing refused +# IMG-JPG-3 +law jpeg_enc_header_walk: + for +ww: U32 + for +hh: U32 + {Jpeg.decode.walk(Jenc.encode.header(ww, hh, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), Jenc.encode.accounts(), + Jenc.encode.acsyms()), Jpeg.decode.st0()) == Jpeg.St{Jpeg.Ent{}, jpg.enc.frame(ww, hh), jpg.enc.scan(), + jpg.enc.tabs(), [], 0, 1, 0} : Jpeg.St} + +# LAW: inside the entropy-coded data, the decoder's walk keeps every byte of stuffed data (the stuffed +# zeros too, which the bit reader drops) and stops at EOI, holding what it held +# IMG-JPG-3 +law jpeg_ent_walk: + for xs: List<&2, U32> + for h_stuffed: {jpg.stuffed(xs, False{}, True{}) == True{} : Bool} + for +frame: Jpeg.Frame + for +scan: Jpeg.Scan + for +tabs: Jpeg.Tabs + for +ent: List<&2, U32> + for +ri: U32 + for +kind: U32 + for +bad: U32 + {Jpeg.decode.walk(List.append(&2, U32, xs, [255, 217]), Jpeg.St{Jpeg.Ent{}, frame, scan, tabs, ent, ri, kind, bad}) == + Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, xs, ent), ri, kind, bad} : Jpeg.St} + +# decode_jpeg of encode_jpeg's bytes, when it wrote some +def jpg.back(mm: Maybe<&2, List<&2, U32>>) -> Maybe<&2, Img.Raster>: + match mm: + case None{}: + None{} + case Some{bytes}: + Img.decode_jpeg(bytes) + +# LAW: for every well-formed raster with both sides from 1 to 65535, decode_jpeg of encode_jpeg's bytes is +# the decoder's scan decode of encode_jpeg's own entropy-coded bytes, in the frame of the raster's width and +# height, with the encoder's scan and tables: the marker walk, the unstuffing and the frame size are done +# IMG-JPG-3 +law jpeg_round_trip_scan: + for +ww: U32 + for +hh: U32 + for +px: List<&2, U32> + for +h_good: {Png.enc.good(ww, hh, px) == True{} : Bool} + for h_w: {U32.is_le(ww, 65535) == True{} : Bool} + for h_h: {U32.is_le(hh, 65535) == True{} : Bool} + {jpg.back(Img.encode_jpeg(Img.Raster{ww, hh, px})) == Img.decode_jpeg.out(Jpeg.decode.run(jpg.enc.frame(ww, + hh), jpg.enc.scan(), jpg.enc.tabs(), Jenc.encode.arm(ww, hh, Img.colours(px)), 0)) : Maybe<&2, Img.Raster>} + +# a decoded raster, if any, has width ww and height hh +def jpg.sized(mm: Maybe<&2, Img.Raster>, +ww: U32, +hh: U32) -> Bool: + match mm: + case None{}: + True{} + case Some{Img.Raster{rw, rh, _px}}: + Bool.and(U32.is_eq(rw, ww), U32.is_eq(rh, hh)) + +# LAW: the scan decode, whatever the bytes, tables and scan, returns no picture or one of its frame's +# width and height +# IMG-JPG-3 +law jpeg_run_sized: + for +ww: U32 + for +hh: U32 + for +nf: U32 + for +ids: List<&2, U32> + for +hs: List<&2, U32> + for +vs: List<&2, U32> + for +tq: List<&2, U32> + for +hmax: U32 + for +vmax: U32 + for +scan: Jpeg.Scan + for +tabs: Jpeg.Tabs + for +ent: List<&2, U32> + for +ri: U32 + {jpg.sized(Img.decode_jpeg.out(Jpeg.decode.run(Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, scan, tabs, ent, + ri)), ww, hh) == True{} : Bool} + +# LAW: for every well-formed raster with both sides from 1 to 65535, decode_jpeg of encode_jpeg's bytes is +# none or a raster of the raster's width and height; every sample of one has alpha 255 (jpeg_decode_opaque) +# IMG-JPG-3 +law jpeg_round_trip_sized: + for +ww: U32 + for +hh: U32 + for +px: List<&2, U32> + for +h_good: {Png.enc.good(ww, hh, px) == True{} : Bool} + for h_w: {U32.is_le(ww, 65535) == True{} : Bool} + for h_h: {U32.is_le(hh, 65535) == True{} : Bool} + {jpg.sized(jpg.back(Img.encode_jpeg(Img.Raster{ww, hh, px})), ww, hh) == True{} : Bool} + +# the data units of the scan's components from comp on, left of them: each one's hi * vi, summed +def jpg.usum( + left: Nat, + +comp: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> Nat: + match left: + case 0n: + 0n + case 1n+pp: + Nat.add(U32.to_nat(jpg.units(comp, sids, ids, hs, vs)), jpg.usum(pp, (comp + 1 : U32), sids, ids, hs, vs)) + +# LAW: the number of blocks the decoder reads, decode.nblocks (ceil(w / 8 hmax) * ceil(h / 8 vmax) MCUs times +# the frame's data units per MCU, all in U32), is the length of jpeg_walk_frame's order over ceil(h / 8 vmax) MCU rows, +# when the scan's components carry the frame's data units and the product fits a U32 (the witness top), so +# the walk visits every MCU of the frame and no counter wraps +# IMG-JPG-2 +law jpeg_walk_count: + for +ww: U32 + for +hh: U32 + for +ids: List<&2, U32> + for +hs: List<&2, U32> + for +vs: List<&2, U32> + for +hmax: U32 + for +vmax: U32 + for +ns: U32 + for +sids: List<&2, U32> + for +top: U32 + for h_ns: {U32.is_lt(0, ns) == True{} : Bool} + for h_units: {jpg.units.ok(U32.to_nat(ns), 0, sids, ids, hs, vs, True{}) == True{} : Bool} + for h_sum: {U32.to_nat(Jpeg.decode.blocks.sum(hs, vs, 0)) == jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs) : Nat} + for h_fit: {Nat.is_le(Nat.mul(Nat.mul(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), + U32.to_nat(Jpeg.decode.ceil(hh, (vmax * 8 : U32)))), jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)), + U32.to_nat(top)) == True{} : Bool} + {U32.to_nat(Jpeg.decode.nblocks(ww, hh, hs, vs, hmax, vmax)) == List.length(&2, Jpeg.Ctrl, + jpg.o.rows(U32.to_nat(Jpeg.decode.ceil(hh, (vmax * 8 : U32))), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, + sids, ids, hs, vs)) : Nat} diff --git a/PROOF.bend b/PROOF.bend index 5189b79..b22c0bc 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -5607,4 +5607,78 @@ def Laws.jpeg_walk_frame( h_mw, h_units ): - W10.walk.frame(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, rst, mh, h_ns, h_mw, h_units) + W10.walk.frame(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, rst, mh, h_ns, h_mw, + h_units) + +def Laws.jpeg_enc_stuffed(ww, hh, px): + W10.st.use(Jenc.encode.watch(U32.to_nat((ww * hh : U32)), px), ww, hh) + +# the decoder's state after the encoder's header, with the sizes as it reads them back +def w10.hdr.st(+ww: U32, +hh: U32) -> Jpeg.St: + Jpeg.St{Jpeg.Ent{}, Jpeg.Frame{ww, hh, 3, [1, 2, 3], [1, 1, 1], [1, 1, 1], [0, 0, 0], 1, 1}, Laws.jpg.enc.scan(), + Laws.jpg.enc.tabs(), [], 0, 1, 0} + +def Laws.jpeg_enc_header_walk(ww, hh): + +wr = Jpeg.decode.u16(U32.shrn(ww, 8n), U32.and(ww, 255)) + +hr = Jpeg.decode.u16(U32.shrn(hh, 8n), U32.and(hh, 255)) + Equal.trans(Jpeg.St, Jpeg.decode.walk(Jenc.encode.header(ww, hh, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), + Jenc.encode.accounts(), Jenc.encode.acsyms()), Jpeg.decode.st0()), w10.hdr.st(wr, hr), w10.hdr.st(ww, hh), {==}, + Equal.trans(Jpeg.St, w10.hdr.st(wr, hr), w10.hdr.st(ww, hr), w10.hdr.st(ww, hh), Equal.cong(U32, Jpeg.St, + xx => w10.hdr.st(xx, hr), wr, ww, W10.u16.eq(ww)), Equal.cong(U32, Jpeg.St, xx => w10.hdr.st(ww, xx), hr, hh, + W10.u16.eq(hh)))) + +def Laws.jpeg_ent_walk(xs, h_stuffed, frame, scan, tabs, ent, ri, kind, bad): + W10.ent.go(xs, False{}, frame, scan, tabs, ent, ri, kind, bad, h_stuffed) + +def Laws.jpeg_round_trip_scan(ww, hh, px, h_good, h_w, h_h): + +cp = Img.colours(px) + +ee = Jenc.encode.arm(ww, hh, cp) + +hd = Jenc.encode.header(ww, hh, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), Jenc.encode.accounts(), + Jenc.encode.acsyms()) + +rr = List.append(&2, U32, ee, [255, 217]) + +fr = Laws.jpg.enc.frame(ww, hh) + +sc = Laws.jpg.enc.scan() + +tb = Laws.jpg.enc.tabs() + +ent = {Jpeg.St{Jpeg.Ent{}, fr, sc, tb, [], 0, 1, 0} : Jpeg.St} + +stop = {Jpeg.St{Jpeg.Stop{}, fr, sc, tb, List.reverse.go(&2, U32, ee, []), 0, 1, 0} : Jpeg.St} + Equal.trans(Maybe<&2, Img.Raster>, Laws.jpg.back(Img.encode_jpeg(Img.Raster{ww, hh, px})), + Laws.jpg.back(Img.encode_jpeg.pick(True{}, ww, hh, cp)), Img.decode_jpeg.out(Jpeg.decode.run(fr, sc, tb, ee, 0)), + Equal.cong(Bool, Maybe<&2, Img.Raster>, bb => Laws.jpg.back(Img.encode_jpeg.pick(bb, ww, hh, cp)), + Png.enc.good(ww, hh, px), True{}, h_good), + Equal.trans(Maybe<&2, Img.Raster>, Img.decode_jpeg(Jenc.encode.pick(Jenc.encode.bad(ww, hh), ww, hh, cp)), + Img.decode_jpeg(Jenc.encode.pick(False{}, ww, hh, cp)), Img.decode_jpeg.out(Jpeg.decode.run(fr, sc, tb, ee, 0)), + Equal.cong(Bool, Maybe<&2, Img.Raster>, bb => Img.decode_jpeg(Jenc.encode.pick(bb, ww, hh, cp)), + Jenc.encode.bad(ww, hh), False{}, W7.bad_of_good(ww, hh, px, h_good, h_w, h_h)), + Equal.trans(Maybe<&2, Img.Raster>, w8.out(Jpeg.decode.walk(List.append(&2, U32, hd, rr), Jpeg.decode.st0())), + w8.out(Jpeg.decode.walk(rr, Jpeg.decode.walk(hd, Jpeg.decode.st0()))), + Img.decode_jpeg.out(Jpeg.decode.run(fr, sc, tb, ee, 0)), Equal.cong(Jpeg.St, Maybe<&2, Img.Raster>, + st => w8.out(st), Jpeg.decode.walk(List.append(&2, U32, hd, rr), Jpeg.decode.st0()), Jpeg.decode.walk(rr, + Jpeg.decode.walk(hd, Jpeg.decode.st0())), W8.walk_app(hd, rr, Jpeg.decode.st0())), + Equal.trans(Maybe<&2, Img.Raster>, w8.out(Jpeg.decode.walk(rr, Jpeg.decode.walk(hd, Jpeg.decode.st0()))), + w8.out(Jpeg.decode.walk(rr, ent)), Img.decode_jpeg.out(Jpeg.decode.run(fr, sc, tb, ee, 0)), + Equal.cong(Jpeg.St, Maybe<&2, Img.Raster>, st => w8.out(Jpeg.decode.walk(rr, st)), Jpeg.decode.walk(hd, + Jpeg.decode.st0()), ent, Laws.jpeg_enc_header_walk(ww, hh)), + Equal.trans(Maybe<&2, Img.Raster>, w8.out(Jpeg.decode.walk(rr, ent)), w8.out(stop), + Img.decode_jpeg.out(Jpeg.decode.run(fr, sc, tb, ee, 0)), Equal.cong(Jpeg.St, Maybe<&2, Img.Raster>, + st => w8.out(st), Jpeg.decode.walk(rr, ent), stop, Laws.jpeg_ent_walk(ee, Laws.jpeg_enc_stuffed(ww, hh, cp), fr, + sc, tb, [], 0, 1, 0)), Equal.cong(List<&2, U32>, Maybe<&2, Img.Raster>, xs => Img.decode_jpeg.out( + Jpeg.decode.run(fr, sc, tb, xs, 0)), List.reverse(&2, U32, List.reverse.go(&2, U32, ee, [])), ee, + Flt.rev_rev(ee))))))) + +def Laws.jpeg_run_sized(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, ent, ri): + W10.run.sized(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, ent, ri) + +def Laws.jpeg_round_trip_sized(ww, hh, px, h_good, h_w, h_h): + +ee = Jenc.encode.arm(ww, hh, Img.colours(px)) + Equal.trans(Bool, Laws.jpg.sized(Laws.jpg.back(Img.encode_jpeg(Img.Raster{ww, hh, px})), ww, hh), + Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.run(Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), + Laws.jpg.enc.tabs(), ee, 0)), ww, hh), True{}, Equal.cong(Maybe<&2, Img.Raster>, Bool, mm => Laws.jpg.sized(mm, ww, + hh), Laws.jpg.back(Img.encode_jpeg(Img.Raster{ww, hh, px})), Img.decode_jpeg.out(Jpeg.decode.run( + Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), Laws.jpg.enc.tabs(), ee, 0)), Laws.jpeg_round_trip_scan(ww, hh, + px, h_good, h_w, h_h)), Laws.jpeg_run_sized(ww, hh, 3, [1, 2, 3], [1, 1, 1], [1, 1, 1], [0, 0, 0], 1, 1, + Laws.jpg.enc.scan(), Laws.jpg.enc.tabs(), ee, 0)) + +def Laws.jpeg_walk_count(ww, hh, ids, hs, vs, hmax, vmax, ns, sids, top, h_ns, h_units, h_sum, h_fit): + W10.walk.count(Jpeg.decode.ceil(ww, (hmax * 8 : U32)), Jpeg.decode.ceil(hh, (vmax * 8 : U32)), + Jpeg.decode.blocks.sum(hs, vs, 0), ns, sids, ids, hs, vs, top, W10.usum.pos(ns, sids, ids, hs, vs, h_ns, h_units), + h_sum, h_fit) diff --git a/SPEC.md b/SPEC.md index 0df640f..75ecfd1 100644 --- a/SPEC.md +++ b/SPEC.md @@ -69,8 +69,8 @@ What a sample means, for every decoder and encoder. | ID | Requirement | Level | Status | Law | | :---- | :---- | :---- | :---- | :---- | | IMG-JPG-1 | `decode_jpeg` returns none for every input that opens with SOI, then segments other than frame headers (DHT, DQT, SOS, DRI, COM, APP0 to APP15) each with a length field that fits its body, then a frame header other than SOF0 or an SOF0 segment whose sample precision is not 8, whatever follows. | Proved | proved | LAWS.bend jpeg_refuse_sofn; LAWS.bend jpeg_refuse_precision | -| IMG-JPG-2 | For a SOF0 frame whose components' sampling factors are each 1, 2 or 4, `decode_jpeg` places each component's samples in the order T.81 A.2.3 gives and replicates each sample over the pixels its sampling covers; for a factor outside 1, 2 and 4, `decode_jpeg` returns none. The sample values themselves are IMG-JPG-7's. | Proved | pending | LAWS.bend jpeg_refuse_factor1; LAWS.bend jpeg_refuse_factor3; LAWS.bend jpeg_comp_index; LAWS.bend jpeg_walk_unit; LAWS.bend jpeg_walk_comp; LAWS.bend jpeg_walk_mcu; LAWS.bend jpeg_mcu_grid; LAWS.bend jpeg_block_cover; LAWS.bend jpeg_rgbs_at | -| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | | +| IMG-JPG-2 | For a SOF0 frame whose components' sampling factors are each 1, 2 or 4, `decode_jpeg` places each component's samples in the order T.81 A.2.3 gives and replicates each sample over the pixels its sampling covers; for a factor outside 1, 2 and 4, `decode_jpeg` returns none. The sample values themselves are IMG-JPG-7's. | Proved | pending | LAWS.bend jpeg_refuse_factor1; LAWS.bend jpeg_refuse_factor3; LAWS.bend jpeg_refuse_factor1_any; LAWS.bend jpeg_refuse_factor3_any; LAWS.bend jpeg_refuse_count; LAWS.bend jpeg_comp_index; LAWS.bend jpeg_walk_unit; LAWS.bend jpeg_walk_comp; LAWS.bend jpeg_walk_mcu; LAWS.bend jpeg_walk_frame; LAWS.bend jpeg_walk_count; LAWS.bend jpeg_mcu_grid; LAWS.bend jpeg_mcu_grid_comp; LAWS.bend jpeg_block_cover; LAWS.bend jpeg_block_cover_all; LAWS.bend jpeg_points_at; LAWS.bend jpeg_rgbs_at | +| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized | | IMG-JPG-4 | `encode_jpeg(r)` is none exactly when `encode_png(r)` is none. | Proved | proved | LAWS.bend jpeg_png_refuse_alike | | IMG-JPG-5 | When `encode_jpeg(r)` is some, it begins with SOI and ends with EOI; when both sides of `r` are at most 65535 it is SOI, APP0 with the JFIF identifier, DQT, SOF0 carrying `r`'s width and height, two DHT, SOS, the entropy-coded data and EOI. | Proved | proved | LAWS.bend jpeg_enc_layout; LAWS.bend jpeg_enc_ends | | IMG-JPG-6 | `Jpeg.rgb(y, cb, cr)` equals the T.871 YCbCr to RGB conversion, rounded to nearest and clamped to 0 to 255, for every `y`, `cb`, `cr` from 0 to 255. | Trusted | | | @@ -81,15 +81,15 @@ What a sample means, for every decoder and encoder. | ID | Proved so far | Missing | | :---- | :---- | :---- | -| IMG-JPG-2 | Refusal: a SOF0 segment of one or three components, one with a factor outside 1, 2 and 4, met where a segment may start, makes `decode_jpeg` none whatever precedes and follows it (`jpeg_refuse_factor1`, `jpeg_refuse_factor3`). Placement: a scan component takes the factors of the frame component whose identifier it names, the first with that identifier (`jpeg_comp_index`); the MCU walk goes from data unit `bi` of a scan component to `bi + 1` while the component has more units in the MCU, then to unit 0 of the next scan component, then to unit 0 of the first component of MCU `mcu + 1` (`jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu`); a component's `hi * vi` units of an MCU sit in T.81 A.2.3's grid, `hi` to a row, each sample covering `hmax / hi` by `vmax / vi` pixels (`jpeg_mcu_grid`); painting a block writes sample `k` over exactly the `pw` by `ph` pixels at `(ox + (k mod 8) * pw, oy + (k div 8) * ph)` inside the frame (`jpeg_block_cover`). Colour: sample `k` of the colour pass is `Jpeg.rgb` of point `k`'s Y, Cb and Cr (`jpeg_rgbs_at`) | that the walk's block count (`ceil(w / 8 hmax) * ceil(h / 8 vmax)` MCUs of the scan's units) makes the steps above reach every MCU, with the U32 counters `mx`, `my` and `mcu` not wrapping; that `decode.emit` reads plane point `k` as sample `k`, which needs `Array.get` after `Array.set` lemmas; `jpeg_mcu_grid` for a scan whose component is not the frame's first (it follows from `jpeg_comp_index` but is stated for one component); `jpeg_block_cover` for `pw = ph = 4` (left out because its proof takes about 100 s of the gate) and for a plane already painted; the refusal for a SOF0 segment with a length longer than its components, or reached after fill bytes | -| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves | that `decode_jpeg` reads `encode_jpeg`'s output back to a raster at all: the marker walk over the encoder's header, the Huffman round trip of the entropy-coded data (bit writer against bit reader, byte stuffing, the Annex K tables), and the frame size carried through | +| IMG-JPG-2 | Refusal: a SOF0 segment with a factor outside 1, 2 and 4 makes `decode_jpeg` none, for one component or three, whatever precedes and follows it (`jpeg_refuse_factor1`, `jpeg_refuse_factor3`), and also after fill bytes before its marker and with a length field longer than its components (`jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any`); a SOF0 segment of any other component count is none whatever its factors (`jpeg_refuse_count`). Placement: a scan component takes the factors of the frame component whose identifier it names, the first with that identifier (`jpeg_comp_index`); the MCU walk goes from data unit `bi` to `bi + 1`, then to the next scan component, then to the next MCU (`jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu`); from the scan's first block the decoder's own walk visits the frame's MCUs in raster order, `ceil(w / 8 hmax)` to a row, and in each MCU the scan's components and their `hi * vi` units in T.81 order, the unit, component and column counters never wrapping (`jpeg_walk_frame`), and the U32 block count the decoder runs, `decode.nblocks`, is that order's length over `ceil(h / 8 vmax)` MCU rows when the scan carries the frame's data units and the count fits a U32 (`jpeg_walk_count`); the `hi * vi` units of any scan component sit in T.81 A.2.3's grid, `hi` to a row, each sample covering `hmax / hi` by `vmax / vi` pixels (`jpeg_mcu_grid`, `jpeg_mcu_grid_comp`); painting a block writes sample `k` over exactly the `pw` by `ph` pixels at `(ox + (k mod 8) * pw, oy + (k div 8) * ph)` inside the frame, for every sample size, 4 by 4 included, and onto every plane, one earlier blocks painted included (`jpeg_block_cover`, `jpeg_block_cover_all`); the decoder reads a plane out point by point, sample `k` being the value `Array.get` finds at index `k` (`jpeg_points_at`). Colour: sample `k` of the colour pass is `Jpeg.rgb` of point `k`'s Y, Cb and Cr (`jpeg_rgbs_at`) | that `Array.get` at an index finds the value the last `Array.set` at that index wrote, and a set at another index leaves it (the planes are perfect binary trees of `2^d` leaves, and the lemmas must follow the masked index down the tree), which joins the painting laws to `jpeg_points_at`. And the row is false as worded for frames of more than 2^31 points, which sides up to 65535 allow: `decode.plane` takes its depth from `U32.shl(nn)`, which wraps above 2^31, so the plane has fewer leaves than points and points alias (`decode.depth` is 0 at 2^31 + 1 points and 30 at 3 * 2^30), and past 2^32 points `w * h` itself wraps (a decision for the maintainer) | +| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves. The encoder's entropy-coded bytes have every 255 followed by a 0, for every size and samples (`jpeg_enc_stuffed`); the decoder's marker walk over the encoder's header reaches the entropy-coded data with the frame carrying the encoder's width and height, its scan and its tables, a baseline frame read and nothing refused (`jpeg_enc_header_walk`); inside the data the walk keeps every byte of stuffed data and stops at EOI (`jpeg_ent_walk`); so for every well-formed raster with both sides from 1 to 65535, `decode_jpeg(encode_jpeg(r))` is the decoder's scan decode, `decode.run`, of the encoder's own entropy-coded bytes in the frame of `r`'s width and height (`jpeg_round_trip_scan`); that scan decode is none or a picture of its frame's width and height, whatever the bytes (`jpeg_run_sized`); so `decode_jpeg(encode_jpeg(r))` is none or a raster of `r`'s size (`jpeg_round_trip_sized`) | that `decode.run` on the encoder's entropy-coded bytes is not none: that every Huffman lookup finds its symbol and every block ends by 64 coefficients, which is the bit writer (`encode.bits`, `encode.pack`, the pad with 1 bits) against the bit reader (`decode.nbits`, which drops a stuffed 0 after a 255), the Annex K tables `encode.huff` builds against the ones `decode.canon` builds and `decode.look` searches, and the encoder's three paths (neutral, solid, `encode.go`) each writing `ceil(w / 8) * ceil(h / 8)` MCUs of three blocks, each a DC code and magnitude, AC run and size codes and magnitudes, and EOB | | IMG-PIX-1 | The JPEG side. `Jpeg.rgb` and `Jpeg.gray` give alpha 255 for every input (`jpeg_rgb_opaque`, `jpeg_gray_opaque`); every sample `decode_jpeg` returns is packed by `Jpeg.gray`, or every one by `Jpeg.rgb` (`jpeg_decode_packed`); every sample it returns has alpha 255 (`jpeg_decode_opaque`); a gray sample carries its level's low byte in R, G and B (`jpeg_gray_level`) | that every sample `decode_png` returns is `0xAARRGGBB`: `png_walk` (IMG-PNG-9) reaches `decode_png` for files in the standard chunk order, not yet for files with ancillary chunks. `jpeg_decode_packed` does not say that the gray packing is the one chosen for a one-component frame | | IMG-PNG-2 | the one-block encoding: every raster `png_ok` accepts (both sides nonzero, `w * h` samples, `w * h` below 2^32) whose scanlines, `h * (1 + w * c)` bytes for c channels (3 when every sample is opaque, 4 otherwise), are at most 65535 decodes from its PNG to itself (`png_roundtrip_one`), through `png_walk`, `inflate_enc_zlib`, `unfilter_filter` with filter None, and `png_px_rgb` / `png_px_rgba` | the two wide paths `encode_png` takes past 65535 scanline bytes: `enc.seal.wide` (colour type 6, `enc.pour`) and `enc.wide.rgb` (colour type 2, `enc.rgb.go`) must be shown to write `zlib.stored(65535, raw)` with the IDAT CRC. And the row is false as worded, even with the approved bound of fewer than 2^32 samples: the scanline count `enc.nbytes` and the IDAT length are U32s, so a raster of 2^32 scanline bytes or more encodes to a file that does not decode (a decision for the maintainer) | | IMG-PNG-9 | `Png.px.of`, the pixel stage, packs unfiltered bytes as the row says for every colour type: gray, gray with a tRNS key, gray and alpha, RGB, RGB with a tRNS key, RGBA (`png_px_grey`, `png_px_grey_key`, `png_px_ga`, `png_px_rgb`, `png_px_rgb_key`, `png_px_rgba`), and each index it decodes as its palette entry with alpha from tRNS or 255 (`png_px_indexed`); the decoder's last stage is `px.of` of `Png.unfilter`'s output with the width and height kept (`png_samples_frame`); and the walker lift, `png_walk`: a file of the signature, IHDR (any nonzero width and height, bit depth 8, a colour type the row names, methods 0), PLTE and tRNS where section 11 allows them, any IDAT chunks and IEND, each chunk framed with its CRC-32 and shorter than 2^32 bytes, decodes to the raster `px.of` packs, for the IHDR colour type and the PLTE and tRNS data, from `Png.unfilter` of the concatenated IDAT data inflated | files whose chunks come in another order the decoder accepts: ancillary chunks (which the walker skips, closing the IDAT run) between the critical ones | -Every Proved row but IMG-JPG-1, IMG-JPG-4, IMG-JPG-5, IMG-PIX-2, IMG-RAS-1, IMG-RAS-2, IMG-RAS-3, IMG-RAS-4, IMG-RAS-5, IMG-PNG-1, IMG-PNG-3, IMG-PNG-4, IMG-PNG-5, IMG-PNG-6, IMG-PNG-7 and IMG-PNG-8 is pending; IMG-PIX-1, IMG-PNG-2 and IMG-PNG-9 have the partial laws above. The rollout in [docs/rfc/ezimg-spec.md](docs/rfc/ezimg-spec.md) orders them: the behavior changes first (IMG-PIX-1 needed BC-1, which has landed; IMG-JPG-2 needed BC-2 and IMG-JPG-4 needed BC-3, which have landed), then refusals and frames (IMG-PNG-3, IMG-PNG-8, IMG-JPG-1, IMG-RAS-1, IMG-RAS-2), then content (IMG-PNG-5, IMG-PNG-7, IMG-PNG-6, IMG-PNG-9, IMG-PNG-4, IMG-RAS-3, IMG-RAS-4, and the headline IMG-PNG-2), and the JPEG content rows last (IMG-PIX-1, IMG-JPG-5, IMG-JPG-2, IMG-JPG-6, IMG-JPG-3). +Every Proved row but IMG-JPG-1, IMG-JPG-4, IMG-JPG-5, IMG-PIX-2, IMG-RAS-1, IMG-RAS-2, IMG-RAS-3, IMG-RAS-4, IMG-RAS-5, IMG-PNG-1, IMG-PNG-3, IMG-PNG-4, IMG-PNG-5, IMG-PNG-6, IMG-PNG-7 and IMG-PNG-8 is pending; IMG-PIX-1, IMG-PNG-2, IMG-PNG-9, IMG-JPG-2 and IMG-JPG-3 have the partial laws above. The rollout in [docs/rfc/ezimg-spec.md](docs/rfc/ezimg-spec.md) orders them: the behavior changes first (IMG-PIX-1 needed BC-1, which has landed; IMG-JPG-2 needed BC-2 and IMG-JPG-4 needed BC-3, which have landed), then refusals and frames (IMG-PNG-3, IMG-PNG-8, IMG-JPG-1, IMG-RAS-1, IMG-RAS-2), then content (IMG-PNG-5, IMG-PNG-7, IMG-PNG-6, IMG-PNG-9, IMG-PNG-4, IMG-RAS-3, IMG-RAS-4, and the headline IMG-PNG-2), and the JPEG content rows last (IMG-PIX-1, IMG-JPG-5, IMG-JPG-2, IMG-JPG-6, IMG-JPG-3). -IMG-PNG-2 is false as worded for rasters of 2^32 scanline bytes or more, which fewer than 2^32 samples do not rule out (above); no other row is known to be false. IMG-JPG-2 covers every sampling layout the frame parser accepts, which BC-2 made decode correctly (REVIEW-13). +IMG-PNG-2 is false as worded for rasters of 2^32 scanline bytes or more, which fewer than 2^32 samples do not rule out, and IMG-JPG-2 for frames of more than 2^31 points (both above); no other row is known to be false. IMG-JPG-2 covers every sampling layout the frame parser accepts, which BC-2 made decode correctly (REVIEW-13). ## Trust boundary diff --git a/docs/rfc/ezimg-law-inventory.md b/docs/rfc/ezimg-law-inventory.md index f11da07..a3a4ee0 100644 --- a/docs/rfc/ezimg-law-inventory.md +++ b/docs/rfc/ezimg-law-inventory.md @@ -329,4 +329,5 @@ requirement depends on. | WP6, the raster (IMG-RAS-1 to IMG-RAS-4) | done; IMG-RAS-2 to IMG-RAS-4 proved after rewording | U32 lemma library `proof/wp6-raster.bend`: `Word.cmp` is `Nat.cmp` of the words' numbers (`word_cmp_nat`, so `u32_lt`, `u32_le`, `u32_eq`); the adder, the subtractor and shift-and-add multiplication read as Nat when the result fits (`adc_nat`, `sbc_nat`, `mulgo_nat`), stated for U32 as `u32_add_below`, `u32_mul_below` (the exact result at most some U32's value) and `u32_sub_nat` (b <= a); `u32_index` (y * w + x does not wrap when x < w, y < h and w * h is some U32's value); Nat order, min, sub, take, drop and append lemmas. A closed 2^32 in a law is expanded in unary by the checker and overflows its stack, so the laws take w * h < 2^32 as a U32 `area` whose value is w * h. IMG-RAS-1: `fill_wf`, `fill_every`. IMG-RAS-2: `get_inside`, `get_inside_some`, `get_outside`, `set_inside`, `set_outside`. IMG-RAS-3: `crop_size`, `crop_wf`, `crop_at`, through a normal form of crop's case tree (`crop.nf_eq`) and the rows walk (`crop.rows_len`, `crop.rows_get`). IMG-RAS-4: `blit_size`, `blit_wf`, `blit_in`, `blit_out`, through the three parts of `blit.join` (`bj.top`, `bj.band`, `bj.bottom`) and one pasted row (`blit.row_left`, `row_mid`, `row_right`). No code change. Mutants: get with x and y swapped, set writing two samples, fill's count off by one and a wrong colour, get and set outside touching sample 0, crop's width off by one at the edge, crop's start and gap off by one, blit ignoring the offset (both axes, and x alone): each fails the gate in its law or its law's helper, and each makes a concrete instance of its law false. Left: IMG-RAS-2 to 4 for well-formed rasters with w * h of 2^32 or more, where they are false as worded (decision) | | WP8, JPEG numeric rows | done; IMG-JPG-6 and the new IMG-JPG-8 Trusted, IMG-JPG-2 and IMG-JPG-3 pending after rewording | IMG-JPG-6: `Jpeg.rgb.bits` rounded G's two terms separately with 16-bit constants and differed from T.871 on 3,320,385 of the 2^24 inputs, by 1; it now computes each channel exactly in millionths (`rgb.ch`: one division, one rounding, then the clamp), and a Python copy of the new U32 arithmetic matches exact T.871 on all 2^24 inputs; 48 of 108 JPEG probe decodes change, each channel by at most 1. A proof would need U32 division and products near 10^9 in Nat terms, and a unary comparison of 255 * 10^6 against 4 * 10^9 already exhausts the checker's memory, so the row moved to Trusted (maintainer decision). IMG-JPG-3 was false (round-trip error up to 7, a 2 by 2 raster suffices): split into the structural row, pending, and the Trusted bound IMG-JPG-8 (within 8; 7 measured before and after the conversion change, libjpeg reaches 4 with the same settings). IMG-JPG-2 reworded (sample values left to IMG-JPG-7) and proved in parts: `jpeg_refuse_factor1`, `jpeg_refuse_factor3` (a SOF0 segment with a factor outside 1, 2 and 4, after any prefix that leaves the walk at a segment boundary, makes `decode_jpeg` none), `jpeg_comp_index` (a scan component finds its frame component), `jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu` (the MCU walk's three steps), `jpeg_mcu_grid` (T.81 A.2.3 grid for every factor pair), `jpeg_block_cover` (sample replication, 4 by 4 left out for gate time) and `jpeg_rgbs_at` (the colour pass is pointwise). Code: factor and count checks as `U32.is_eq` Bools, `Comps.ok`, `decode.read.sof.c`; `decode.index` without its dummy accumulator. Lemmas in `proof/wp8-jpeg-numeric.bend` (`walk_app`, `walk_stop`, `fac_elim`). Each new law caught a planted mutant | | WP9, PNG round trip (IMG-PNG-2, IMG-PNG-9 lift) | done, both partial; IMG-PNG-2 false as worded past 2^32 scanline bytes (decision) | `png_walk` lifts the pixel stage to `decode_png`: a file of the signature, IHDR, PLTE and tRNS where allowed, any IDAT chunks and IEND, each chunk framed (`spec.chunk`: length, type, data, `crc.ref`) and shorter than 2^32 bytes, decodes to the raster `px.of` packs from `Png.unfilter` of the concatenated IDAT data inflated, with the IHDR colour type and the PLTE and tRNS data. `png_roundtrip_one` proves IMG-PNG-2 for every raster `png_ok` accepts whose scanlines fit one stored block (at most 65535 bytes): the encoder's file is `spec.png` with one IDAT (`seal.one.same`), then `png_walk`, `inflate_enc_zlib`, `unfilter_filter` with filter None, and `png_px_rgb` / `png_px_rgba` with the samples read back bit by bit (`be.back`: `be.u32` of `spec.be4`'s bytes is the word, 32 bits taken apart and 24 `Bool.or(b, False)` rewrites). Code, byte-identical on the probe outputs: `enc.r`, `enc.g`, `enc.b` mask with the constant first. Lemmas in `proof/wp9-png-roundtrip.bend`. Mutants (IDAT data appended in the wrong order, PLTE kept reversed, tRNS dropped at finish, IHDR width and height swapped, green written for blue, filter byte 1, alpha written 255): each fails the gate. Left: the wide paths (`enc.seal.wide` / `enc.pour`, `enc.wide.rgb` / `enc.rgb.go`), ancillary chunks for IMG-PNG-9, and the row's wording: `enc.nbytes` and the IDAT length are U32s, so a 32768 by 32768 raster with one non-opaque sample (2^30 samples) counts 32768 scanline bytes and takes the one-block path with a wrong LEN | +| WP10, JPEG placement and the round trip's structure (IMG-JPG-2, IMG-JPG-3) | done, both partial; IMG-JPG-2 false as worded above 2^31 points | IMG-JPG-2: `jpeg_block_cover_all` (every sample size, 4 by 4 included, onto any plane), `jpeg_mcu_grid_comp` (the A.2.3 grid for any scan component), `jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any` (fill bytes before the marker, a length longer than the components), `jpeg_refuse_count` (any other component count), `jpeg_points_at` (sample k of a plane's read-out is `Array.get` at k), `jpeg_walk_frame` (the decoder's own walk visits every MCU in raster order and each MCU's units in T.81 order, no counter wrapping) and `jpeg_walk_count` (the U32 block count `decode.nblocks` is that order's length when it fits). IMG-JPG-3: `jpeg_enc_stuffed`, `jpeg_enc_header_walk`, `jpeg_ent_walk`, `jpeg_round_trip_scan` (`decode_jpeg(encode_jpeg(r))` is the scan decode of the encoder's entropy-coded bytes in `r`'s frame), `jpeg_run_sized`, `jpeg_round_trip_sized` (none or a raster of `r`'s size). Code, byte-identical on all probe outputs and on crafted fill, marker and truncation cases: block painting written by rows, columns and pixels as the law-side cover is (`decode.splat`), which also drops `jpeg_block_cover`'s 1024-write normalisation from the gate; `decode.emit` reads a plane point by point in order (`decode.points`); `decode.nbits` and the entropy walk compare bytes with `U32.is_eq`; the encoder keeps raw bytes and stuffs once at the flush (`encode.stuff.all`), and dispatches on its tag with `U32.is_eq`; the block count is named (`decode.nblocks`). Lemmas in `proof/wp10-jpeg-finish.bend`, among them `dup` (two copies of an array, each equal to it: proofs are live, so an array cannot go to two lemmas) and the MCU-walk runs (`run.units` to `run.rows`). Mutants: a block column at `col * ph`, a scan component's factors taken from frame component `comp`, factor 3 accepted, a component count of 2 accepted, the read-out starting at point 1, an MCU row skipped, the block count summed from 1, a 255 not stuffed, the SOF width's low byte masked with 254, a stuffed 0 dropped by the walk, the entropy bytes not reversed, a gray picture's sides swapped, a colour picture's height as its width: each fails its law or that law's lemma. Left: IMG-JPG-2's `Array.get` after `Array.set` lemmas, and the row is false for frames above 2^31 points (`decode.plane`'s depth wraps; decision); IMG-JPG-3's Huffman round trip, that `decode.run` of the encoder's bytes is not none. Gate about 4 m 20 s, main's 4 m 50 s | | Phase 4b, cheap rows | next | IMG-PNG-3, IMG-PNG-8, IMG-JPG-1, IMG-RAS-1, IMG-RAS-2; these need ordering and product lemmas on U32 (`is_lt`, `is_le`, `*` without wrap) next to `ueq` | diff --git a/proof/wp10-jpeg-finish.bend b/proof/wp10-jpeg-finish.bend index 140b473..fd9f1cc 100644 --- a/proof/wp10-jpeg-finish.bend +++ b/proof/wp10-jpeg-finish.bend @@ -4,6 +4,7 @@ # file as W10, so the gate checks it. import Base import ../LAWS.bend as Laws +import ../main.bend as Img import ../src/jpeg.bend as Jpeg import ../src/jpeg_enc.bend as Jenc import ./u32.bend as U32L @@ -146,6 +147,20 @@ def grid.of( def stop(+sc: Jpeg.Scan, +tb: Jpeg.Tabs, +en: List<&2, U32>, +ri: U32, +kd: U32) -> Jpeg.St: Jpeg.St{Jpeg.Stop{}, Jpeg.decode.frame0(), sc, tb, en, ri, kd, 1} +# the frame reader refuses a SOF0 body +def sof.Disp( + +body: List<&2, U32>, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ent: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32 +) -> Type: + {Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad) == stop(scan, tabs, ent, ri, kind) : + Jpeg.St} + # a SOF0 segment read from just after its marker: when its body makes the frame reader refuse, the # walk goes on stopped from the end of the body, whatever the body's length def sof.seg( @@ -162,8 +177,7 @@ def sof.seg( +bad: U32, h_len: {U32.is_lt(Jpeg.decode.u16(lh, ll), 2) == False{} : Bool}, h_body: {U32.to_nat((Jpeg.decode.u16(lh, ll) - 2 : U32)) == List.length(&2, U32, body) : Nat}, - h_disp: {Jpeg.decode.dispatch(192, body, frame, scan, tabs, ent, ri, kind, bad) == stop(scan, tabs, ent, ri, kind) - : Jpeg.St} + h_disp: sof.Disp(body, frame, scan, tabs, ent, ri, kind, bad) ) -> {Jpeg.decode.walk(lh <> ll <> List.append(&2, U32, body, rest), Jpeg.St{Jpeg.LenHi{192}, frame, scan, tabs, ent, ri, kind, bad}) == Jpeg.decode.walk(rest, stop(scan, tabs, ent, ri, kind)) : Jpeg.St}: +slb = {Jpeg.decode.step.len.b(U32.is_lt(Jpeg.decode.u16(lh, ll), 2), 192, Jpeg.decode.u16(lh, ll), frame, scan, tabs, @@ -371,7 +385,8 @@ def getif.eq( Array & U32}: match zz: case True{}: - sx = Equal.sym(Array & U32, Array.get.go(U32, xs, hh, ii), (xs, Laws.jpg.val(Array.get.go(U32, xs, hh, ii))), ihx) + sx = Equal.sym(Array & U32, Array.get.go(U32, xs, hh, ii), (xs, Laws.jpg.val(Array.get.go(U32, xs, hh, + ii))), ihx) %sx : {Array.swap.lo(U32, ys, _) == (ANode{xs, ys}, Laws.jpg.val(Array.swap.lo(U32, ys, _))) : Array & U32} {==} case False{}: @@ -393,6 +408,18 @@ def getgo.eq( getif.eq(xs, ys, U32.shr(nn), ii, U32.is_lt(ii, U32.shr(nn)), getgo.eq(xs, U32.shr(nn), ii), getgo.eq(ys, U32.shr(nn), U32.sub(ii, U32.shr(nn)))) +# the tree walk of a read, at the array's own size and the masked index, hands the array back +def GoAt(-aa: Array, +ii: U32) -> Type: + {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + 1))) == (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, + U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))))) : Array & U32} + +# the tree walk of a read of bb, at the size read from cc, hands bb back +def GoAt2(-bb: Array, -cc: Array, +ii: U32) -> Type: + {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, cc)), + 1))) == (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, + U32.sub(Laws.jpg.val(Array.size(U32, cc)), 1))))) : Array & U32} + # the size walk's statement moved along an equality of arrays def size.tr( -bb: Array, @@ -420,9 +447,7 @@ def get.from( -aa: Array, +ii: U32, hs: {Array.size(U32, aa) == (aa, Laws.jpg.val(Array.size(U32, aa))) : Array & U32}, - hg: {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == - (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), - 1))))) : Array & U32} + hg: GoAt(aa, ii) ) -> {Array.get(U32, aa, ii) == (aa, Laws.jpg.val(Array.get(U32, aa, ii))) : Array & U32}: ss = Equal.sym(Array & U32, Array.size(U32, aa), (aa, Laws.jpg.val(Array.size(U32, aa))), hs) %ss : {Array.get.at(U32, ii, _) == (aa, Laws.jpg.val(Array.get.at(U32, ii, _))) : Array & U32} @@ -436,17 +461,17 @@ def getgo.tr2( +ii: U32, eb: {bb == aa : Array}, ec: {cc == aa : Array}, - hh: {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, cc)), 1))) == - (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, cc)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, cc)), - 1))))) : Array & U32} -) -> {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == - (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), - 1))))) : Array & U32}: - %eb : {Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == - (_, Laws.jpg.val(Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), + hh: GoAt2(bb, cc, ii) +) -> GoAt(aa, ii): + %eb : {Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, + aa)), 1))) == + (_, Laws.jpg.val(Array.get.go(U32, _, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, + U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))))) : Array & U32} - %ec : {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, _)), 1))) == - (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, _)), + %ec : {Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, + _)), 1))) == + (bb, Laws.jpg.val(Array.get.go(U32, bb, Laws.jpg.val(Array.size(U32, _)), U32.and(ii, + U32.sub(Laws.jpg.val(Array.size(U32, _)), 1))))) : Array & U32} hh @@ -455,9 +480,7 @@ def getgo.at( -aa: Array, +ii: U32, dd: Dup(aa) -) -> {Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), 1))) == - (aa, Laws.jpg.val(Array.get.go(U32, aa, Laws.jpg.val(Array.size(U32, aa)), U32.and(ii, U32.sub(Laws.jpg.val(Array.size(U32, aa)), - 1))))) : Array & U32}: +) -> GoAt(aa, ii): (c1, c2, e1, e2) = dd +nn = Laws.jpg.val(Array.size(U32, c2)) getgo.tr2(c1, c2, aa, ii, e1, e2, getgo.eq(c1, nn, U32.and(ii, U32.sub(nn, 1)))) @@ -501,7 +524,8 @@ def emit.at( case 0n: -_d = dd Empty.absurd({List.get(&2, U32, Jpeg.decode.emit(0n, Array.get(U32, aa, jj), (jj + 1 : U32)), mm) == - Some{Laws.jpg.val(Array.get(U32, aa, Laws.jpg.idx(mm, jj)))} : Maybe<&2, U32>}, U32L.false_true(Equal.trans(Bool, False{}, + Some{Laws.jpg.val(Array.get(U32, aa, Laws.jpg.idx(mm, + jj)))} : Maybe<&2, U32>}, U32L.false_true(Equal.trans(Bool, False{}, Nat.is_lt(mm, 0n), True{}, R.lt_zero_false(mm), hm))) case 1n+pp: match mm: @@ -640,7 +664,8 @@ def comp.pos( case True{}: {==} case False{}: - mx.pos(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my, rst, + mx.pos(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my, + rst, ri) # one step of the walk from data unit bi @@ -718,7 +743,8 @@ def tr.pos( +se: U32, +ah: U32, +ri: U32 -) -> {tr(left, ctrl, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == +) -> {tr(left, ctrl, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, + ri) == tr(left, Laws.jpg.pos(ctrl), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) : List<&2, Jpeg.Ctrl>}: match left: @@ -735,7 +761,8 @@ def tr.pos( ri) e0 = nxt.eq(comp, bi, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri) - Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, cc => {Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} <> tr(pp, cc, fr, sc, ri) : + Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, cc => {Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} <> tr(pp, cc, fr, sc, + ri) : List<&2, Jpeg.Ctrl>}, nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, fr, sc, ri), nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, fr, sc, ri), Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bi, mx, my, mcu, rst}, fr, sc, ri), res, @@ -1067,7 +1094,9 @@ def run.units( +kk: Nat, +hb: {Nat.add(U32.to_nat(bi), 1n+ll) == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat} ) -> Run(Laws.jpg.o.units(1n+ll, comp, bi, mx, my, mcu), Jpeg.Ctrl{comp, bi, mx, my, mcu, 0}, - nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(ll, bi), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(ll, bi), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): match ll: case 0n: {==} @@ -1077,19 +1106,27 @@ def run.units( +cc = {Jpeg.Ctrl{comp, bi, mx, my, mcu, 0} : Jpeg.Ctrl} +c1 = {Jpeg.Ctrl{comp, (bi + 1 : U32), mx, my, mcu, 0} : Jpeg.Ctrl} +nn = {Nat.add(List.length(&2, Jpeg.Ctrl, Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu)), kk) : Nat} - +en = {Equal.trans(Jpeg.Ctrl, nxt(cc, fr, sc, ri), bi.res(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), - U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu), c1, - nxt.eq(comp, bi, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, - U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bi, mx, my, mcu), - U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), True{}, lt.more(bi, Laws.jpg.units(comp, sids, ids, hs, vs), pp, hb))) : {nxt(cc, fr, sc, ri) == c1 : Jpeg.Ctrl}} + +en = {Equal.trans(Jpeg.Ctrl, nxt(cc, fr, sc, ri), bi.res(U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, + ids, hs, vs)), + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bi, mx, my, mcu), c1, + nxt.eq(comp, bi, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri), Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bi, mx, my, mcu), + U32.is_lt((bi + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), True{}, lt.more(bi, Laws.jpg.units(comp, + sids, ids, hs, vs), pp, hb))) : {nxt(cc, fr, sc, ri) == c1 : Jpeg.Ctrl}} Equal.trans(List<&2, Jpeg.Ctrl>, cc <> tr(nn, nxt(cc, fr, sc, ri), fr, sc, ri), cc <> tr(nn, c1, fr, sc, ri), cc <> List.append(&2, Jpeg.Ctrl, Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu), tr(kk, nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(pp, (bi + 1 : U32)), mx, my, mcu, 0}, fr, sc, ri), fr, sc, ri)), - Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, dd => {cc <> tr(nn, dd, fr, sc, ri) : List<&2, Jpeg.Ctrl>}, nxt(cc, fr, sc, ri), c1, en), - Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, zz => {cc <> zz : List<&2, Jpeg.Ctrl>}, tr(nn, c1, fr, sc, ri), List.append(&2, Jpeg.Ctrl, + Equal.cong(Jpeg.Ctrl, List<&2, Jpeg.Ctrl>, dd => {cc <> tr(nn, dd, fr, sc, ri) : List<&2, Jpeg.Ctrl>}, + nxt(cc, fr, sc, ri), c1, en), + Equal.cong(List<&2, Jpeg.Ctrl>, List<&2, Jpeg.Ctrl>, zz => {cc <> zz : List<&2, Jpeg.Ctrl>}, tr(nn, c1, fr, + sc, ri), List.append(&2, Jpeg.Ctrl, Laws.jpg.o.units(1n+pp, comp, (bi + 1 : U32), mx, my, mcu), tr(kk, nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(pp, (bi + 1 : U32)), mx, my, mcu, 0}, fr, sc, ri), fr, sc, ri)), run.units(pp, comp, (bi + 1 : U32), mx, my, mcu, - ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, lt.next(bi, Laws.jpg.units(comp, sids, ids, hs, vs), pp, hb)))) + ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, lt.next(bi, + Laws.jpg.units(comp, sids, ids, hs, vs), pp, hb)))) # where the walk goes after a component's last unit, when the scan has more components def comp.end.more( @@ -1117,18 +1154,29 @@ def comp.end.more( +ri: U32, +hs1: {1n+kk == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}, +hnext: {U32.is_lt((comp + 1 : U32), ns) == True{} : Bool} -) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == +) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0} : Jpeg.Ctrl}: +bl = Laws.jpg.idx(kk, 0) - Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), - bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, - bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), - Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), - U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, U32.is_lt((comp + 1 : U32), ns), - U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, - Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), - comp, bl, mx, my, mcu), U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), False{}, lt.last(bl, Laws.jpg.units(comp, sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, ids, hs, vs), hs1))), - Equal.cong(Bool, Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), + bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, + bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, + ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), + Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), + U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0}, + Equal.cong(Bool, Jpeg.Ctrl, mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), + Jpeg.decode.ceil(ww, (hmax * 8 : U32))), + comp, bl, mx, my, mcu), U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, + vs)), False{}, lt.last(bl, Laws.jpg.units(comp, sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, + ids, hs, vs), hs1))), + Equal.cong(Bool, Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), U32.is_lt((comp + 1 : U32), ns), True{}, hnext))) # where the walk goes after the last component's last unit: the next MCU @@ -1157,21 +1205,62 @@ def comp.end.last( +ri: U32, +hs1: {1n+kk == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}, +hnext: {U32.is_lt((comp + 1 : U32), ns) == False{} : Bool} -) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == - mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl}: +) -> {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(kk, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), + my) : Jpeg.Ctrl}: +bl = Laws.jpg.idx(kk, 0) - Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), - bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, - bl, mx, my, mcu), mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), - nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), - U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, - U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), - mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), Equal.cong(Bool, Jpeg.Ctrl, - mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), - U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), False{}, lt.last(bl, Laws.jpg.units(comp, sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, ids, hs, vs), hs1))), Equal.cong(Bool, - Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, mx, my, mcu), + Equal.trans(Jpeg.Ctrl, nxt(Jpeg.Ctrl{comp, bl, mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), + bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), U32.is_lt((comp + 1 : U32), ns), + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, + bl, mx, my, mcu), mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), + nxt.eq(comp, bl, mx, my, mcu, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri), Equal.trans(Jpeg.Ctrl, bi.res(U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), bi.res(False{}, + U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), + my), Equal.cong(Bool, Jpeg.Ctrl, + mb => bi.res(mb, U32.is_lt((comp + 1 : U32), ns), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), comp, bl, mx, my, mcu), + U32.is_lt((bl + 1 : U32), Laws.jpg.units(comp, sids, ids, hs, vs)), False{}, lt.last(bl, Laws.jpg.units(comp, + sids, ids, hs, vs), last.nat(kk, Laws.jpg.units(comp, sids, ids, hs, vs), hs1))), Equal.cong(Bool, + Jpeg.Ctrl, nb => bi.res(False{}, nb, U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), comp, bl, + mx, my, mcu), U32.is_lt((comp + 1 : U32), ns), False{}, hnext))) +# where the walk goes after the last of a component's units in an MCU +def units.End( + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +ns: U32, + +sids: List<&2, U32>, + +td: List<&2, U32>, + +ta: List<&2, U32>, + +ss: U32, + +se: U32, + +ah: U32, + +ri: U32, + +ee: Jpeg.Ctrl +) -> Type: + {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))), 0), mx, my, mcu, 0}, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == ee : + Jpeg.Ctrl} + # a scan component's units, counted as the decoder counts them: all of them, from unit 0 def run.comp.units( +comp: U32, @@ -1198,17 +1287,23 @@ def run.comp.units( +kk: Nat, +ee: Jpeg.Ctrl, +hpos: {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}, - hend: {nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))), 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == ee : - Jpeg.Ctrl} -) -> Run(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, - Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + hend: units.End(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, ee) +) -> Run(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, + 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, kk): +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) - +es = {pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos) : {1n+k1 == U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) : Nat}} - %es : Run(Laws.jpg.o.units(_, comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, + +es = {pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos) : {1n+k1 == U32.to_nat(Laws.jpg.units(comp, sids, + ids, hs, vs)) : Nat}} + %es : Run(Laws.jpg.o.units(_, comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, ee, Jpeg.Frame{ww, hh, nf, + ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) run.end(Laws.jpg.o.units(1n+k1, comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, - nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(k1, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri), ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, - run.units(k1, comp, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, es), hend) + nxt(Jpeg.Ctrl{comp, Laws.jpg.idx(k1, 0), mx, my, mcu, 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri), ee, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, + ri, kk, + run.units(k1, comp, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, + kk, es), hend) # the scan's components comp onward of one MCU, the last ll components on: the walk visits their units in # order, then goes to the next MCU @@ -1240,27 +1335,43 @@ def run.comps( +hc: {Nat.add(U32.to_nat(comp), 1n+ll) == U32.to_nat(ns) : Nat}, +hu: {Laws.jpg.units.ok(1n+ll, comp, sids, ids, hs, vs, ok) == True{} : Bool} ) -> Run(Laws.jpg.o.comps(1n+ll, comp, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, - mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), + my), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): match ll: case 0n: - +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(0n, (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, - U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} + +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(0n, + (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, + U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, + vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) - +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} - run.nil(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, em, - Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, em, hpos, comp.end.last(k1, comp, mx, my, - mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos), lt.last(comp, ns, hc)))) + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), + (mx + 1 : U32), my) : Jpeg.Ctrl} + run.nil(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), + Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, em, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, + ss, se, ah, ri, kk, em, hpos, comp.end.last(k1, comp, mx, my, + mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, + sids, ids, hs, vs), hpos), lt.last(comp, ns, hc)))) case 1n+(+pp): - +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(1n+pp, (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, - U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} + +hpos = {U32L.and_right(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)), uok.acc(1n+pp, + (comp + 1 : U32), sids, ids, hs, vs, Bool.and(ok, + U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, + vs))), hu)) : {U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs)) == True{} : Bool}} +k1 = npred(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs))) - +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), + (mx + 1 : U32), my) : Jpeg.Ctrl} +c1 = {Jpeg.Ctrl{(comp + 1 : U32), 0, mx, my, mcu, 0} : Jpeg.Ctrl} +l2 = {Laws.jpg.o.comps(1n+pp, (comp + 1 : U32), mx, my, mcu, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} - run.cat(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), l2, Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, c1, - em, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), - kk), c1, hpos, comp.end.more(k1, comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos), lt.more(comp, ns, pp, hc))), - run.comps(pp, (comp + 1 : U32), mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, Bool.and(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), lt.next(comp, ns, + run.cat(Laws.jpg.o.units(U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)), comp, 0, mx, my, mcu), l2, + Jpeg.Ctrl{comp, 0, mx, my, mcu, 0}, c1, + em, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, kk, run.comp.units(comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, + ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), + kk), c1, hpos, comp.end.more(k1, comp, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, + ta, ss, se, ah, ri, pos.nat(Laws.jpg.units(comp, sids, ids, hs, vs), hpos), lt.more(comp, ns, pp, hc))), + run.comps(pp, (comp + 1 : U32), mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, + se, ah, ri, kk, Bool.and(ok, U32.is_lt(0, Laws.jpg.units(comp, sids, ids, hs, vs))), lt.next(comp, ns, pp, hc), hu)) # the MCUs mx onward of MCU row my, the last ll MCUs on: the walk visits each MCU's units in order, then @@ -1293,31 +1404,47 @@ def run.row( +hu: {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : Bool}, +hx: {Nat.add(U32.to_nat(mx), 1n+ll) == U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Nat} ) -> Run(Laws.jpg.o.row(1n+ll, mx, my, mcu, ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, - Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+ll, mcu), 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): + Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+ll, mcu), 0}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): match ll: case 0n: - +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), + (mx + 1 : U32), my) : Jpeg.Ctrl} +et = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), (mcu + 1 : U32), 0} : Jpeg.Ctrl} %hn : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.comps(_, 0, mx, my, mcu, sids, ids, hs, vs), []), - Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) - run.nil(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, - Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, - 0}, em, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.comps(n1, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, True{}, hn, hu), + Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, + td, ta, ss, se, ah}, ri, kk) + run.nil(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, + hs, vs), Jpeg.Ctrl{0, 0, mx, my, mcu, + 0}, em, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, + kk, run.comps(n1, 0, mx, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri, kk, True{}, hn, hu), Equal.cong(Bool, Jpeg.Ctrl, ib => mx.res(ib, (mcu + 1 : U32), (mx + 1 : U32), my), - U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), False{}, lt.last(mx, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), hx)))) + U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), False{}, lt.last(mx, Jpeg.decode.ceil(ww, + (hmax * 8 : U32)), hx)))) case 1n+(+pp): - +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my) : Jpeg.Ctrl} + +em = {mx.res(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), (mcu + 1 : U32), + (mx + 1 : U32), my) : Jpeg.Ctrl} +c1 = {Jpeg.Ctrl{0, 0, (mx + 1 : U32), my, (mcu + 1 : U32), 0} : Jpeg.Ctrl} +et = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+(1n+pp), mcu), 0} : Jpeg.Ctrl} +l2 = {Laws.jpg.o.row(1n+pp, (mx + 1 : U32), my, (mcu + 1 : U32), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} %hn : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.comps(_, 0, mx, my, mcu, sids, ids, hs, vs), l2), - Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) + Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, + td, ta, ss, se, ah}, ri, kk) run.cat(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), l2, Jpeg.Ctrl{0, 0, mx, my, mcu, 0}, c1, et, - Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, - my, mcu, 0}, em, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.comps(n1, 0, mx, my, - mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), True{}, hn, hu), Equal.cong(Bool, Jpeg.Ctrl, - ib => mx.res(ib, (mcu + 1 : U32), (mx + 1 : U32), my), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (hmax * 8 : U32))), True{}, lt.more(mx, - Jpeg.decode.ceil(ww, (hmax * 8 : U32)), pp, hx))), run.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, n1, hn, hu, lt.next(mx, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, kk, run.end(Laws.jpg.o.comps(1n+n1, 0, mx, my, mcu, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, mx, + my, mcu, 0}, em, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.comps(n1, 0, mx, my, + mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, + Jpeg.Ctrl, l2), kk), True{}, hn, hu), Equal.cong(Bool, Jpeg.Ctrl, + ib => mx.res(ib, (mcu + 1 : U32), (mx + 1 : U32), my), U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, + (hmax * 8 : U32))), True{}, lt.more(mx, + Jpeg.decode.ceil(ww, + (hmax * 8 : U32)), pp, hx))), run.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ww, hh, nf, ids, hs, vs, tq, + hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, n1, hn, hu, lt.next(mx, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), pp, hx))) # where the walk is after mm MCU rows from row my, MCU number mcu, mw MCUs to a row @@ -1356,24 +1483,34 @@ def run.rows( +hu: {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : Bool}, +w1: Nat, +hw: {1n+w1 == U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Nat} -) -> Run(Laws.jpg.o.rows(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, - rows.end(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32))), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): +) -> Run(Laws.jpg.o.rows(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, + 0, my, mcu, 0}, + rows.end(mm, my, mcu, Jpeg.decode.ceil(ww, (hmax * 8 : U32))), Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk): match mm: case 0n: {==} case 1n+(+pp): +m2 = {Laws.jpg.idx(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), mcu) : U32} +c1 = {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), m2, 0} : Jpeg.Ctrl} - +l2 = {Laws.jpg.o.rows(pp, (my + 1 : U32), m2, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +l2 = {Laws.jpg.o.rows(pp, (my + 1 : U32), m2, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, + vs) : List<&2, Jpeg.Ctrl>} +et = {rows.end(pp, (my + 1 : U32), m2, Jpeg.decode.ceil(ww, (hmax * 8 : U32))) : Jpeg.Ctrl} %hw : Run(List.append(&2, Jpeg.Ctrl, Laws.jpg.o.row(_, 0, my, mcu, ns, sids, ids, hs, vs), l2), - Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk) + Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, et, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, + td, ta, ss, se, ah}, ri, kk) run.cat(Laws.jpg.o.row(1n+w1, 0, my, mcu, ns, sids, ids, hs, vs), l2, Jpeg.Ctrl{0, 0, 0, my, mcu, 0}, c1, et, - Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, kk, run.end(Laws.jpg.o.row(1n+w1, 0, my, mcu, ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, 0, my, - mcu, 0}, Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+w1, mcu), 0}, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri, - Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.row(w1, 0, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, + Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, + ah}, ri, kk, run.end(Laws.jpg.o.row(1n+w1, 0, my, mcu, ns, sids, ids, hs, vs), Jpeg.Ctrl{0, 0, 0, my, + mcu, 0}, Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), Laws.jpg.idx(1n+w1, + mcu), 0}, c1, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, + ri, + Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), run.row(w1, 0, my, mcu, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, + ns, sids, td, ta, ss, se, ah, ri, Nat.add(List.length(&2, Jpeg.Ctrl, l2), kk), n1, hn, hu, hw), Equal.cong(Nat, Jpeg.Ctrl, nn => {Jpeg.Ctrl{0, 0, 0, (my + 1 : U32), - Laws.jpg.idx(nn, mcu), 0} : Jpeg.Ctrl}, 1n+w1, U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), hw)), run.rows(pp, (my + 1 : U32), m2, + Laws.jpg.idx(nn, + mcu), 0} : Jpeg.Ctrl}, 1n+w1, U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32))), hw)), run.rows(pp, + (my + 1 : U32), m2, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, kk, n1, hn, hu, w1, hw)) # jpeg_walk_frame: the decoder's trace from the scan's first block is the T.81 order @@ -1400,12 +1537,15 @@ def walk.frame( +h_ns: {U32.is_lt(0, ns) == True{} : Bool}, +h_mw: {U32.is_lt(0, Jpeg.decode.ceil(ww, (hmax * 8 : U32))) == True{} : Bool}, +h_units: {Laws.jpg.units.ok(U32.to_nat(ns), 0, sids, ids, hs, vs, True{}) == True{} : Bool} -) -> {Laws.jpg.trace(List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs)), - Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, se, ah}, ri) == Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, +) -> {Laws.jpg.trace(List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, + (hmax * 8 : U32)), ns, sids, ids, hs, vs)), + Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, Jpeg.Scan{ns, sids, td, ta, ss, + se, ah}, ri) == Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>}: +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} +sc = {Jpeg.Scan{ns, sids, td, ta, ss, se, ah} : Jpeg.Scan} - +ll = {Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +ll = {Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, + vs) : List<&2, Jpeg.Ctrl>} +ln = List.length(&2, Jpeg.Ctrl, ll) +n1 = npred(U32.to_nat(ns)) +hn = {pos.nat(ns, h_ns) : {1n+n1 == U32.to_nat(ns) : Nat}} @@ -1415,10 +1555,652 @@ def walk.frame( True{}), 1n+n1, U32.to_nat(ns), hn), h_units) : {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : Bool}} Equal.trans(List<&2, Jpeg.Ctrl>, Laws.jpg.trace(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), tr(ln, - Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), ll, trace.tr(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), + Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), ll, trace.tr(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, + rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.trans(List<&2, Jpeg.Ctrl>, tr(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, fr, sc, ri), tr(ln, c0, fr, sc, ri), ll, - tr.pos(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri), Equal.trans(List<&2, Jpeg.Ctrl>, tr(ln, c0, fr, sc, ri), + tr.pos(ln, Jpeg.Ctrl{0, 0, 0, 0, 0, rst}, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, + ri), Equal.trans(List<&2, Jpeg.Ctrl>, tr(ln, c0, fr, sc, ri), tr(Nat.add(ln, 0n), c0, fr, sc, ri), ll, Equal.cong(Nat, List<&2, Jpeg.Ctrl>, nn => tr(nn, c0, fr, sc, ri), ln, Nat.add(ln, 0n), Equal.sym(Nat, Nat.add(ln, 0n), ln, R.add_zero(ln))), Equal.trans(List<&2, Jpeg.Ctrl>, - tr(Nat.add(ln, 0n), c0, fr, sc, ri), List.append(&2, Jpeg.Ctrl, ll, []), ll, run.rows(U32.to_nat(mh), 0, 0, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, - 0n, n1, hn, hu, npred(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32)))), pos.nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32)), h_mw)), app.nil(ll))))) + tr(Nat.add(ln, 0n), c0, fr, sc, ri), List.append(&2, Jpeg.Ctrl, ll, []), ll, run.rows(U32.to_nat(mh), 0, 0, ww, + hh, nf, ids, hs, vs, tq, hmax, vmax, ns, sids, td, ta, ss, se, ah, ri, + 0n, n1, hn, hu, npred(U32.to_nat(Jpeg.decode.ceil(ww, (hmax * 8 : U32)))), pos.nat(Jpeg.decode.ceil(ww, + (hmax * 8 : U32)), h_mw)), app.nil(ll))))) + +# ---- IMG-JPG-3: stuffing, the header walk, the entropy walk, the frame size ---- + +# a stuffed list stays refused once a 255 was not followed by a 0 +def st.false(xs: List<&2, U32>, ff: Bool) -> {Laws.jpg.stuffed(xs, ff, False{}) == False{} : Bool}: + match xs: + case Nil{}: + {==} + case +bb <> rest: + match ff: + case True{}: + st.false(rest, False{}) + case False{}: + st.false(rest, U32.is_eq(bb, 255)) + +# one byte stuffed in front of stuffed bytes keeps them stuffed +def st.put( + ff: Bool, + +bb: U32, + +acc: List<&2, U32>, + +ee: {U32.is_eq(bb, 255) == ff : Bool}, + hh: {Laws.jpg.stuffed(acc, False{}, True{}) == True{} : Bool} +) -> {Laws.jpg.stuffed(Jenc.encode.stuff.put(ff, bb, acc), False{}, True{}) == True{} : Bool}: + match ff: + case True{}: + es = Equal.sym(Bool, U32.is_eq(bb, 255), True{}, ee) + %es : {Laws.jpg.stuffed(0 <> acc, _, True{}) == True{} : Bool} + hh + case False{}: + es = Equal.sym(Bool, U32.is_eq(bb, 255), False{}, ee) + %es : {Laws.jpg.stuffed(acc, _, True{}) == True{} : Bool} + hh + +# every byte of a written list stuffed onto stuffed bytes gives stuffed bytes +def st.all( + out: List<&2, U32>, + +acc: List<&2, U32>, + hh: {Laws.jpg.stuffed(acc, False{}, True{}) == True{} : Bool} +) -> {Laws.jpg.stuffed(Jenc.encode.stuff.all(out, acc), False{}, True{}) == True{} : Bool}: + match out: + case Nil{}: + hh + case +bb <> rest: + st.all(rest, Jenc.encode.stuff.put(U32.is_eq(bb, 255), bb, acc), st.put(U32.is_eq(bb, 255), bb, acc, {==}, hh)) + +# the entropy-coded bytes the encoder flushes are stuffed, whatever it wrote +def st.pad(pp: Jenc.Put) -> {Laws.jpg.stuffed(Jenc.encode.pad(pp), False{}, True{}) == True{} : Bool}: + match pp: + case Jenc.Put{out, +buf, +nn}: + st.all(Jenc.encode.pad.n(U32.is_eq(nn, 0), out, buf, nn), [], {==}) + +# each of the encoder's three paths ends in a flush +def st.disp( + neu: Bool, + sol: Bool, + +color: U32, + px: List<&2, U32>, + +ww: U32, + +hh: U32 +) -> {Laws.jpg.stuffed(Jenc.encode.dispatch.b(neu, sol, color, px, ww, hh), False{}, True{}) == True{} : Bool}: + match neu: + case True{}: + st.pad(Jenc.encode.neutral.mcus(U32.to_nat((U32.div((ww + 7 : U32), 8) * U32.div((hh + 7 : U32), 8) : U32)), + Jenc.encode.put0())) + case False{}: + match sol: + case True{}: + st.pad(Jenc.encode.neutral.mcus(U32.to_nat(((U32.div((ww + 7 : U32), 8) * U32.div((hh + 7 : U32), 8) : U32) - + 1 : U32)), Jenc.encode.solid.y(Array.set(U32, Jpeg.decode.plane(1), 0, color), + Jenc.encode.book(Jenc.encode.dccounts(), Jenc.encode.dcsyms(), Jenc.encode.accounts(), + Jenc.encode.acsyms()), Jenc.encode.put0(), Jenc.encode.ctx()))) + case False{}: + st.pad(Jenc.encode.mcus(U32.to_nat((U32.div((ww + 7 : U32), 8) * U32.div((hh + 7 : U32), 8) : U32)), + (Jenc.encode.pix.load(U32.to_nat((ww * hh : U32)), px, Jpeg.decode.plane((ww * hh : U32)), 0), + Jenc.encode.book(Jenc.encode.dccounts(), Jenc.encode.dcsyms(), Jenc.encode.accounts(), + Jenc.encode.acsyms()), 0, 0, 0, Jenc.encode.put0()), 0, 0, U32.div((ww + 7 : U32), 8), ww, hh, + Jenc.encode.ctx())) + +# the watch's answer, whatever it is, dispatches to stuffed bytes +def st.use( + got: U32 & U32 & List<&2, U32>, + +ww: U32, + +hh: U32 +) -> {Laws.jpg.stuffed(Jenc.encode.arm.use(got, ww, hh), False{}, True{}) == True{} : Bool}: + match got: + case (+tag, +color, px): + st.disp(U32.is_eq(tag, 0), U32.is_eq(tag, 1), color, px, ww, hh) + +# the phase the entropy walk is in: after a 255, or not +def ent.ph(ff: Bool) -> Jpeg.Phase: + match ff: + case True{}: + Jpeg.EntFF{} + case False{}: + Jpeg.Ent{} + +# the bytes the entropy walk holds: a 255 just read is not yet among them +def ent.acc(ff: Bool, +en: List<&2, U32>) -> List<&2, U32>: + match ff: + case True{}: + 255 <> en + case False{}: + en + +# what the entropy walk holds after one byte: a 255 is held back until its pair is read +def ent.hold(ff: Bool, +bb: U32, +en: List<&2, U32>) -> List<&2, U32>: + match ff: + case True{}: + +_b = (bb - bb : U32) + en + case False{}: + bb <> en + +# the entropy walk from after one byte, as ent.go gives it +def ent.Ih( + ff: Bool, + +bb: U32, + +rest: List<&2, U32>, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +en: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32 +) -> Type: + {Jpeg.decode.walk(List.append(&2, U32, rest, [255, 217]), Jpeg.St{ent.ph(ff), frame, scan, tabs, ent.hold(ff, bb, + en), ri, kind, bad}) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, rest, ent.acc(ff, + ent.hold(ff, bb, en))), ri, kind, bad} : Jpeg.St} + +# the entropy walk over one byte that is not after a 255: 255 opens a pair, any other is kept +def ent.one( + ff: Bool, + +bb: U32, + +rest: List<&2, U32>, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +en: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32, + +ee: {U32.is_eq(bb, 255) == ff : Bool}, + ih: ent.Ih(ff, bb, rest, frame, scan, tabs, en, ri, kind, bad) +) -> {Jpeg.decode.walk(List.append(&2, U32, bb <> rest, [255, 217]), Jpeg.St{Jpeg.Ent{}, frame, scan, tabs, en, ri, + kind, bad}) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, bb <> rest, en), ri, kind, bad} : + Jpeg.St}: + match ff: + case True{}: + es = Equal.sym(Bool, U32.is_eq(bb, 255), True{}, ee) + eb = Equal.sym(U32, bb, 255, U32L.ueq(bb, 255, ee)) + %es : {Jpeg.decode.walk(List.append(&2, U32, rest, [255, 217]), Jpeg.decode.step.ent(_, bb, frame, scan, tabs, + en, ri, kind, bad)) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, rest, bb <> en), ri, + kind, bad} : Jpeg.St} + %eb : {Jpeg.decode.walk(List.append(&2, U32, rest, [255, 217]), Jpeg.decode.step.ent(True{}, bb, frame, scan, + tabs, en, ri, kind, bad)) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, rest, _ <> en), + ri, + kind, bad} : Jpeg.St} + ih + case False{}: + es = Equal.sym(Bool, U32.is_eq(bb, 255), False{}, ee) + %es : {Jpeg.decode.walk(List.append(&2, U32, rest, [255, 217]), Jpeg.decode.step.ent(_, bb, frame, scan, tabs, + en, ri, kind, bad)) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, rest, bb <> en), ri, + kind, bad} : Jpeg.St} + ih + +# after a 255 the next byte of stuffed bytes is 0 +def ent.zero( + zb: Bool, + rest: List<&2, U32>, + +hh: {Laws.jpg.stuffed(rest, False{}, zb) == True{} : Bool} +) -> {zb == True{} : Bool}: + match zb: + case True{}: + {==} + case False{}: + Empty.absurd({False{} == True{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, Laws.jpg.stuffed(rest, + False{}, False{}), True{}, Equal.sym(Bool, Laws.jpg.stuffed(rest, False{}, False{}), False{}, st.false(rest, + False{})), hh))) + +# the entropy walk over stuffed bytes, then EOI: every byte kept, in reverse, onto what it held, and a 255 +# it had just read put back in front of them +def ent.go( + +xs: List<&2, U32>, + ff: Bool, + +frame: Jpeg.Frame, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +en: List<&2, U32>, + +ri: U32, + +kind: U32, + +bad: U32, + +hh: {Laws.jpg.stuffed(xs, ff, True{}) == True{} : Bool} +) -> {Jpeg.decode.walk(List.append(&2, U32, xs, [255, 217]), Jpeg.St{ent.ph(ff), frame, scan, tabs, en, ri, kind, + bad}) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, xs, ent.acc(ff, en)), ri, kind, bad} : + Jpeg.St}: + match xs: + case Nil{}: + match ff: + case False{}: + {==} + case True{}: + Empty.absurd({Jpeg.decode.walk([255, 217], Jpeg.St{Jpeg.EntFF{}, frame, scan, tabs, en, ri, kind, bad}) == + Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, 255 <> en, ri, kind, bad} : Jpeg.St}, U32L.false_true(hh)) + case +bb <> +rest: + match ff: + case False{}: + ent.one(U32.is_eq(bb, 255), bb, rest, frame, scan, tabs, en, ri, kind, bad, {==}, ent.go(rest, + U32.is_eq(bb, 255), frame, scan, tabs, ent.hold(U32.is_eq(bb, 255), bb, en), ri, kind, bad, hh)) + case True{}: + +ez = {ent.zero(U32.is_eq(bb, 0), rest, hh) : {U32.is_eq(bb, 0) == True{} : Bool}} + eb = Equal.sym(U32, bb, 0, U32L.ueq(bb, 0, ez)) + %eb : {Jpeg.decode.walk(List.append(&2, U32, _ <> rest, [255, 217]), Jpeg.St{Jpeg.EntFF{}, frame, scan, tabs, + en, ri, kind, bad}) == Jpeg.St{Jpeg.Stop{}, frame, scan, tabs, List.reverse.go(&2, U32, _ <> rest, + 255 <> en), ri, kind, bad} : Jpeg.St} + h2 = Equal.trans(Bool, Laws.jpg.stuffed(rest, False{}, True{}), Laws.jpg.stuffed(rest, False{}, + U32.is_eq(bb, 0)), True{}, Equal.cong(Bool, Bool, zb => Laws.jpg.stuffed(rest, False{}, zb), True{}, + U32.is_eq(bb, 0), Equal.sym(Bool, U32.is_eq(bb, 0), True{}, ez)), hh) + ent.go(rest, False{}, frame, scan, tabs, 0 <> (255 <> en), ri, kind, bad, h2) + +# the decoder's u16 of the high and low bytes the encoder writes for a size is the size +def u16.eq(+ww: U32) -> {Jpeg.decode.u16(U32.shrn(ww, 8n), U32.and(ww, 255)) == ww : U32}: + Equal.trans(U32, Jpeg.decode.u16(U32.shrn(ww, 8n), U32.and(ww, 255)), Jpeg.decode.u16(U32.shrn(ww, 8n), + U32.and(255, ww)), ww, Equal.cong(U32, U32, xx => Jpeg.decode.u16(U32.shrn(ww, 8n), xx), U32.and(ww, 255), + U32.and(255, ww), uand_comm(ww, 255)), u16_bits(ww)) + +# a raster of width ww and height hh has that size +def sized.pic( + +ww: U32, + +hh: U32, + px: List<&2, U32> +) -> {Laws.jpg.sized(Some{Img.Raster{ww, hh, px}}, ww, hh) == True{} : Bool}: + Equal.trans(Bool, Bool.and(U32.is_eq(ww, ww), U32.is_eq(hh, hh)), Bool.and(True{}, U32.is_eq(hh, hh)), True{}, + Equal.cong(Bool, Bool, bb => Bool.and(bb, U32.is_eq(hh, hh)), U32.is_eq(ww, ww), True{}, U32L.u32_eq_refl(ww)), + U32L.u32_eq_refl(hh)) + +# three components: a colour picture of the frame's size, or none +def nf3.sized( + three: Bool, + +ww: U32, + +hh: U32, + yy: Array, + cb: Array, + cr: Array +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.done.nf3(three, ww, hh, yy, cb, cr)), ww, hh) == True{} : Bool}: + match three: + case True{}: + +nn = U32.to_nat((ww * hh : U32)) + sized.pic(ww, hh, Jpeg.decode.rgbs(Jpeg.decode.points(nn, yy), Jpeg.decode.points(nn, cb), + Jpeg.decode.points(nn, cr))) + case False{}: + -_y = yy + -_b = cb + -_r = cr + {==} + +# one component: a gray picture of the frame's size; any other count as above +def nf1.sized( + one: Bool, + +nf: U32, + +ww: U32, + +hh: U32, + yy: Array, + cb: Array, + cr: Array +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.done.nf1(one, nf, ww, hh, yy, cb, cr)), ww, hh) == True{} : + Bool}: + match one: + case True{}: + -_b = cb + -_r = cr + sized.pic(ww, hh, Jpeg.decode.grays(Jpeg.decode.points(U32.to_nat((ww * hh : U32)), yy))) + case False{}: + nf3.sized(U32.is_eq(nf, 3), ww, hh, yy, cb, cr) + +# the planes of a finished scan as a picture of the frame's size, or none +def dok.sized( + bad: Bool, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + yy: Array, + cb: Array, + cr: Array +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.done.ok(bad, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, + yy, cb, cr)), ww, hh) == True{} : Bool}: + match bad: + case True{}: + -_y = yy + -_b = cb + -_r = cr + {==} + case False{}: + nf1.sized(U32.is_eq(nf, 1), nf, ww, hh, yy, cb, cr) + +# the block loop ends in a picture of the frame's size, or none +def blocks.sized( + left: Nat, + blk: Jpeg.Blk, + +ctrl: Jpeg.Ctrl, + +preds: Jpeg.Preds, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ri: U32, + yy: Array, + cb: Array, + cr: Array, + +ok: U32 +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.blocks(left, blk, ctrl, preds, Jpeg.Frame{ww, hh, nf, ids, hs, vs, + tq, hmax, vmax}, scan, tabs, ri, yy, cb, cr, ok)), ww, hh) == True{} : Bool}: + match left: + case 0n: + match blk: + case Jpeg.Blk{+samples, _bits, _pred, +bok}: + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + dok.sized(U32.is_eq((ok * bok : U32), 0), ww, hh, nf, ids, hs, vs, tq, hmax, vmax, + Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, scan), U32.is_eq(Jpeg.decode.ctrl.comp(ctrl), 0), samples, + yy), Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, scan), U32.is_eq(Jpeg.decode.ctrl.comp(ctrl), 1), + samples, cb), Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, scan), U32.is_eq(Jpeg.decode.ctrl.comp(ctrl), + 2), samples, cr)) + case 1n+pp: + match blk: + case Jpeg.Blk{+samples, +bits, +pred, +bok}: + match ctrl: + case Jpeg.Ctrl{+comp, _bi, _mx, _my, _mcu, _rst}: + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + blocks.sized(pp, Jpeg.decode.block.next(bits, preds, pred, ctrl, fr, scan, tabs, ri), + Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ctrl, fr, scan, ri)), + Jpeg.decode.preds.next(Jpeg.decode.adv.duef(Jpeg.decode.adv(ctrl, fr, scan, ri)), preds, comp, + pred), ww, + hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, ri, Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, + scan), U32.is_eq(comp, 0), samples, yy), Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, scan), + U32.is_eq(comp, 1), samples, cb), Jpeg.decode.paint.use(Jpeg.decode.geom(ctrl, fr, scan), + U32.is_eq(comp, 2), samples, cr), (ok * bok : U32)) + +# the scan decode from its first block: a picture of the frame's size, or none +def start.sized( + nn: Nat, + bits: Jpeg.Bits, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + +ri: U32, + yy: Array, + cb: Array, + cr: Array +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.start(nn, bits, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, scan, tabs, ri, yy, cb, cr)), ww, hh) == True{} : Bool}: + match nn: + case 0n: + -_s = bits + -_y = yy + -_b = cb + -_r = cr + {==} + case 1n+pp: + +fr = {Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax} : Jpeg.Frame} + blocks.sized(pp, Jpeg.decode.block.of(bits, 0, Jpeg.decode.pred.zero(), fr, scan, tabs), Jpeg.Ctrl{0, 0, 0, 0, 0, + 0}, Jpeg.decode.pred.zero(), ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, ri, yy, cb, cr, 1) + +# a frame with a side of 0 is none; any other is decoded from its first block +def runn.sized( + empty: Bool, + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + ent: List<&2, U32>, + +ri: U32 +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.run.n(empty, ww, hh, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, + vmax}, scan, tabs, ent, ri)), ww, hh) == True{} : Bool}: + match empty: + case True{}: + -_e = ent + {==} + case False{}: + start.sized(U32.to_nat(Jpeg.decode.nblocks(ww, hh, hs, vs, hmax, vmax)), Jpeg.Bits{0, 1, 0, ent}, ww, hh, nf, + ids, hs, vs, tq, hmax, vmax, scan, + tabs, ri, Jpeg.decode.plane((ww * hh : U32)), Jpeg.decode.plane((ww * hh : U32)), + Jpeg.decode.plane((ww * hh : U32))) + +# jpeg_run_sized: the scan decode is none or a picture of the frame's size +def run.sized( + +ww: U32, + +hh: U32, + +nf: U32, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +tq: List<&2, U32>, + +hmax: U32, + +vmax: U32, + +scan: Jpeg.Scan, + +tabs: Jpeg.Tabs, + ent: List<&2, U32>, + +ri: U32 +) -> {Laws.jpg.sized(Img.decode_jpeg.out(Jpeg.decode.run(Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, scan, + tabs, ent, ri)), ww, hh) == True{} : Bool}: + runn.sized(Bool.or(U32.is_eq(ww, 0), U32.is_eq(hh, 0)), ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, ent, ri) + +# ---- the decoder's block count (IMG-JPG-2) ---- + +# the length of a component's units in the order +def len.units( + +kk: Nat, + +comp: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32 +) -> {List.length(&2, Jpeg.Ctrl, Laws.jpg.o.units(kk, comp, bi, mx, my, mcu)) == kk : Nat}: + match kk: + case 0n: + {==} + case 1n+(+pp): + Equal.cong(Nat, Nat, nn => 1n+nn, List.length(&2, Jpeg.Ctrl, Laws.jpg.o.units(pp, comp, (bi + 1 : U32), mx, my, + mcu)), pp, len.units(pp, comp, (bi + 1 : U32), mx, my, mcu)) + +# the length of an MCU's components in the order: their data units summed +def len.comps( + +nn: Nat, + +comp: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> {List.length(&2, Jpeg.Ctrl, Laws.jpg.o.comps(nn, comp, mx, my, mcu, sids, ids, hs, vs)) == Laws.jpg.usum(nn, + comp, sids, ids, hs, vs) : Nat}: + match nn: + case 0n: + {==} + case 1n+(+pp): + +un = U32.to_nat(Laws.jpg.units(comp, sids, ids, hs, vs)) + +l1 = {Laws.jpg.o.units(un, comp, 0, mx, my, mcu) : List<&2, Jpeg.Ctrl>} + +l2 = {Laws.jpg.o.comps(pp, (comp + 1 : U32), mx, my, mcu, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +s2 = Laws.jpg.usum(pp, (comp + 1 : U32), sids, ids, hs, vs) + Equal.trans(Nat, List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), Nat.add(List.length(&2, + Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(un, s2), len.app(l1, l2), Equal.trans(Nat, + Nat.add(List.length(&2, Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(un, List.length(&2, + Jpeg.Ctrl, l2)), Nat.add(un, s2), Equal.cong(Nat, Nat, xx => Nat.add(xx, List.length(&2, Jpeg.Ctrl, l2)), + List.length(&2, Jpeg.Ctrl, l1), un, len.units(un, comp, 0, mx, my, mcu)), Equal.cong(Nat, Nat, + xx => Nat.add(un, xx), List.length(&2, Jpeg.Ctrl, l2), s2, len.comps(pp, (comp + 1 : U32), mx, my, mcu, sids, + ids, hs, vs)))) + +# the length of an MCU row in the order: its MCUs times the data units of one +def len.row( + +kk: Nat, + +mx: U32, + +my: U32, + +mcu: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> {List.length(&2, Jpeg.Ctrl, Laws.jpg.o.row(kk, mx, my, mcu, ns, sids, ids, hs, vs)) == Nat.mul(kk, + Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)) : Nat}: + match kk: + case 0n: + {==} + case 1n+(+pp): + +uu = Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs) + +l1 = {Laws.jpg.o.comps(U32.to_nat(ns), 0, mx, my, mcu, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +l2 = {Laws.jpg.o.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + Equal.trans(Nat, List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), Nat.add(List.length(&2, + Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(uu, Nat.mul(pp, uu)), len.app(l1, l2), + Equal.trans(Nat, Nat.add(List.length(&2, Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(uu, + List.length(&2, Jpeg.Ctrl, l2)), Nat.add(uu, Nat.mul(pp, uu)), Equal.cong(Nat, Nat, xx => Nat.add(xx, + List.length(&2, Jpeg.Ctrl, l2)), List.length(&2, Jpeg.Ctrl, l1), uu, len.comps(U32.to_nat(ns), 0, mx, my, mcu, + sids, ids, hs, vs)), Equal.cong(Nat, Nat, xx => Nat.add(uu, xx), List.length(&2, Jpeg.Ctrl, l2), Nat.mul(pp, + uu), len.row(pp, (mx + 1 : U32), my, (mcu + 1 : U32), ns, sids, ids, hs, vs)))) + +# the length of the order over mm MCU rows +def len.rows( + +mm: Nat, + +my: U32, + +mcu: U32, + +mw: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> {List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(mm, my, mcu, mw, ns, sids, ids, hs, vs)) == Nat.mul(mm, + Nat.mul(U32.to_nat(mw), Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs))) : Nat}: + match mm: + case 0n: + {==} + case 1n+(+pp): + +ru = Nat.mul(U32.to_nat(mw), Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)) + +l1 = {Laws.jpg.o.row(U32.to_nat(mw), 0, my, mcu, ns, sids, ids, hs, vs) : List<&2, Jpeg.Ctrl>} + +l2 = {Laws.jpg.o.rows(pp, (my + 1 : U32), Laws.jpg.idx(U32.to_nat(mw), mcu), mw, ns, sids, ids, hs, vs) : + List<&2, Jpeg.Ctrl>} + Equal.trans(Nat, List.length(&2, Jpeg.Ctrl, List.append(&2, Jpeg.Ctrl, l1, l2)), Nat.add(List.length(&2, + Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(ru, Nat.mul(pp, ru)), len.app(l1, l2), + Equal.trans(Nat, Nat.add(List.length(&2, Jpeg.Ctrl, l1), List.length(&2, Jpeg.Ctrl, l2)), Nat.add(ru, + List.length(&2, Jpeg.Ctrl, l2)), Nat.add(ru, Nat.mul(pp, ru)), Equal.cong(Nat, Nat, xx => Nat.add(xx, + List.length(&2, Jpeg.Ctrl, l2)), List.length(&2, Jpeg.Ctrl, l1), ru, len.row(U32.to_nat(mw), 0, my, mcu, ns, + sids, ids, hs, vs)), Equal.cong(Nat, Nat, xx => Nat.add(ru, xx), List.length(&2, Jpeg.Ctrl, l2), Nat.mul(pp, + ru), len.rows(pp, (my + 1 : U32), Laws.jpg.idx(U32.to_nat(mw), mcu), mw, ns, sids, ids, hs, vs)))) + +# multiplication is associative +def mul.assoc(+aa: Nat, +bb: Nat, +cc: Nat) -> {Nat.mul(Nat.mul(aa, bb), cc) == Nat.mul(aa, Nat.mul(bb, cc)) : Nat}: + match aa: + case 0n: + {==} + case 1n+(+pp): + Equal.trans(Nat, Nat.mul(Nat.add(bb, Nat.mul(pp, bb)), cc), Nat.add(Nat.mul(bb, cc), Nat.mul(Nat.mul(pp, bb), + cc)), Nat.add(Nat.mul(bb, cc), Nat.mul(pp, Nat.mul(bb, cc))), R.mul_add(bb, Nat.mul(pp, bb), cc), + Equal.cong(Nat, Nat, xx => Nat.add(Nat.mul(bb, cc), xx), Nat.mul(Nat.mul(pp, bb), cc), Nat.mul(pp, + Nat.mul(bb, cc)), mul.assoc(pp, bb, cc))) + +# x is at most x times a positive number +def le.mulr(+xx: Nat, +uu: Nat, +hu: {1n+npred(uu) == uu : Nat}) -> {Nat.is_le(xx, Nat.mul(xx, uu)) == True{} : Bool}: + +u1 = npred(uu) + Equal.trans(Bool, Nat.is_le(xx, Nat.mul(xx, uu)), Nat.is_le(xx, Nat.add(xx, Nat.mul(xx, u1))), True{}, + Equal.cong(Nat, Bool, nn => Nat.is_le(xx, nn), Nat.mul(xx, uu), Nat.add(xx, Nat.mul(xx, u1)), Equal.trans(Nat, + Nat.mul(xx, uu), Nat.mul(xx, 1n+u1), Nat.add(xx, Nat.mul(xx, u1)), Equal.cong(Nat, Nat, nn => Nat.mul(xx, nn), uu, + 1n+u1, Equal.sym(Nat, 1n+u1, uu, hu)), R.mul_succ(xx, u1))), R.le_add_more(xx, xx, Nat.mul(xx, u1), + R.le_refl(xx))) + +# the scan has at least one data unit per MCU +def usum.pos( + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +h_ns: {U32.is_lt(0, ns) == True{} : Bool}, + +h_units: {Laws.jpg.units.ok(U32.to_nat(ns), 0, sids, ids, hs, vs, True{}) == True{} : Bool} +) -> {1n+npred(Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)) == Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, + hs, vs) : Nat}: + +n1 = npred(U32.to_nat(ns)) + +hn = {pos.nat(ns, h_ns) : {1n+n1 == U32.to_nat(ns) : Nat}} + +u0 = Laws.jpg.units(0, sids, ids, hs, vs) + +hu = {Equal.trans(Bool, Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}), Laws.jpg.units.ok(U32.to_nat(ns), + 0, sids, ids, hs, vs, True{}), True{}, Equal.cong(Nat, Bool, nn => Laws.jpg.units.ok(nn, 0, sids, ids, hs, vs, + True{}), 1n+n1, U32.to_nat(ns), hn), h_units) : {Laws.jpg.units.ok(1n+n1, 0, sids, ids, hs, vs, True{}) == True{} : + Bool}} + +hp = {uok.acc(n1, 1, sids, ids, hs, vs, U32.is_lt(0, u0), hu) : {U32.is_lt(0, u0) == True{} : Bool}} + +k0 = npred(U32.to_nat(u0)) + +rest = Laws.jpg.usum(n1, 1, sids, ids, hs, vs) + succ.of(Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs), Equal.trans(Bool, Nat.is_lt(0n, + Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)), Nat.is_lt(0n, Nat.add(U32.to_nat(u0), rest)), True{}, + Equal.cong(Nat, Bool, nn => Nat.is_lt(0n, Laws.jpg.usum(nn, 0, sids, ids, hs, vs)), U32.to_nat(ns), 1n+n1, + Equal.sym(Nat, 1n+n1, U32.to_nat(ns), hn)), Equal.cong(Nat, Bool, nn => Nat.is_lt(0n, Nat.add(nn, rest)), + U32.to_nat(u0), 1n+k0, Equal.sym(Nat, 1n+k0, U32.to_nat(u0), pos.nat(u0, hp))))) + +# the scan's data units per MCU are one more than their predecessor +def usum.Pos(+ns: U32, +sids: List<&2, U32>, +ids: List<&2, U32>, +hs: List<&2, U32>, +vs: List<&2, U32>) -> Type: + {1n+npred(Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)) == Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs) + : Nat} + +# the blocks of a frame of mw by mh MCUs, as a number +def fit.n( + +mw: U32, + +mh: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32> +) -> Nat: + Nat.mul(Nat.mul(U32.to_nat(mw), U32.to_nat(mh)), Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs)) + +# jpeg_walk_count: the decoder's U32 block count is the order's length +def walk.count( + +mw: U32, + +mh: U32, + +bs: U32, + +ns: U32, + +sids: List<&2, U32>, + +ids: List<&2, U32>, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +top: U32, + h_pos: usum.Pos(ns, sids, ids, hs, vs), + +h_sum: {U32.to_nat(bs) == Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs) : Nat}, + +h_fit: {Nat.is_le(fit.n(mw, mh, ns, sids, ids, hs, vs), U32.to_nat(top)) == True{} : Bool} +) -> {U32.to_nat((mw * mh * bs : U32)) == List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(U32.to_nat(mh), 0, 0, mw, ns, + sids, ids, hs, vs)) : Nat}: + +uu = Laws.jpg.usum(U32.to_nat(ns), 0, sids, ids, hs, vs) + +wn = U32.to_nat(mw) + +hn = U32.to_nat(mh) + +in = {R.u32_mul_below(mw, mh, top, R.le_trans(Nat.mul(wn, hn), Nat.mul(Nat.mul(wn, hn), uu), U32.to_nat(top), + le.mulr(Nat.mul(wn, hn), uu, h_pos), h_fit)) : {U32.to_nat((mw * mh : U32)) == Nat.mul(wn, hn) : Nat}} + +eo = {Equal.trans(Nat, Nat.mul(U32.to_nat((mw * mh : U32)), U32.to_nat(bs)), Nat.mul(Nat.mul(wn, hn), + U32.to_nat(bs)), Nat.mul(Nat.mul(wn, hn), uu), Equal.cong(Nat, Nat, nn => Nat.mul(nn, U32.to_nat(bs)), + U32.to_nat((mw * mh : U32)), Nat.mul(wn, hn), in), Equal.cong(Nat, Nat, nn => Nat.mul(Nat.mul(wn, hn), nn), + U32.to_nat(bs), uu, h_sum)) : {Nat.mul(U32.to_nat((mw * mh : U32)), U32.to_nat(bs)) == Nat.mul(Nat.mul(wn, hn), uu) + : Nat}} + +out = {R.u32_mul_below((mw * mh : U32), bs, top, Equal.trans(Bool, Nat.is_le(Nat.mul(U32.to_nat((mw * mh : U32)), + U32.to_nat(bs)), U32.to_nat(top)), Nat.is_le(Nat.mul(Nat.mul(wn, hn), uu), U32.to_nat(top)), True{}, + Equal.cong(Nat, Bool, nn => Nat.is_le(nn, U32.to_nat(top)), Nat.mul(U32.to_nat((mw * mh : U32)), U32.to_nat(bs)), + Nat.mul(Nat.mul(wn, hn), uu), eo), h_fit)) : {U32.to_nat((mw * mh * bs : U32)) == Nat.mul(U32.to_nat((mw * mh : + U32)), U32.to_nat(bs)) : Nat}} + Equal.trans(Nat, U32.to_nat((mw * mh * bs : U32)), Nat.mul(Nat.mul(wn, hn), uu), List.length(&2, Jpeg.Ctrl, + Laws.jpg.o.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs)), Equal.trans(Nat, U32.to_nat((mw * mh * bs : U32)), + Nat.mul(U32.to_nat((mw * mh : U32)), U32.to_nat(bs)), Nat.mul(Nat.mul(wn, hn), uu), out, eo), Equal.trans(Nat, + Nat.mul(Nat.mul(wn, hn), uu), Nat.mul(Nat.mul(hn, wn), uu), List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(hn, 0, 0, + mw, ns, sids, ids, hs, vs)), Equal.cong(Nat, Nat, nn => Nat.mul(nn, uu), Nat.mul(wn, hn), Nat.mul(hn, wn), + R.mul_comm(wn, hn)), Equal.trans(Nat, Nat.mul(Nat.mul(hn, wn), uu), Nat.mul(hn, Nat.mul(wn, uu)), List.length(&2, + Jpeg.Ctrl, Laws.jpg.o.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs)), mul.assoc(hn, wn, uu), Equal.sym(Nat, + List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs)), Nat.mul(hn, Nat.mul(wn, uu)), + len.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs))))) diff --git a/src/jpeg.bend b/src/jpeg.bend index 0ea9394..b8878e2 100644 --- a/src/jpeg.bend +++ b/src/jpeg.bend @@ -1452,6 +1452,18 @@ def decode.start( decode.blocks(p, decode.block.of(bits, 0, decode.pred.zero(), frame, scan, tabs), Ctrl{0, 0, 0, 0, 0, 0}, decode.pred.zero(), frame, scan, tabs, ri, yy, cb, cr, 1) +# the blocks a scan of the frame holds: ceil(w / 8 hmax) * ceil(h / 8 vmax) MCUs, each with the frame's +# components' hi * vi data units +def decode.nblocks( + +ww: U32, + +hh: U32, + +hs: List<&2, U32>, + +vs: List<&2, U32>, + +hmax: U32, + +vmax: U32 +) -> U32: + (decode.ceil(ww, (hmax * 8 : U32)) * decode.ceil(hh, (vmax * 8 : U32)) * decode.blocks.sum(hs, vs, 0) : U32) + def decode.run.n( empty: Bool, +ww: U32, @@ -1468,10 +1480,8 @@ def decode.run.n( case False{}: match frame: case Frame{_w, _h, _nf, _ids, +hs, +vs, _tq, +hmax, +vmax}: - +n = (decode.ceil(ww, (hmax * 8 : U32)) * decode.ceil(hh, (vmax * 8 : U32)) * decode.blocks.sum(hs, vs, 0) : - U32) - decode.start(U32.to_nat(n), Bits{0, 1, 0, ent}, frame, scan, tabs, ri, decode.plane((ww * hh : U32)), - decode.plane((ww * hh : U32)), decode.plane((ww * hh : U32))) + decode.start(U32.to_nat(decode.nblocks(ww, hh, hs, vs, hmax, vmax)), Bits{0, 1, 0, ent}, frame, scan, tabs, + ri, decode.plane((ww * hh : U32)), decode.plane((ww * hh : U32)), decode.plane((ww * hh : U32))) def decode.run(frame: Frame, scan: Scan, tabs: Tabs, ent: List<&2, U32>, +ri: U32) -> Maybe<&2, Pic>: match frame: diff --git a/src/jpeg_enc.bend b/src/jpeg_enc.bend index 1848cd9..950f7a2 100644 --- a/src/jpeg_enc.bend +++ b/src/jpeg_enc.bend @@ -162,15 +162,9 @@ def encode.bad(+ww: U32, +hh: U32) -> Bool: def encode.put0() -> Put: Put{[], 0, 0} -def encode.stuff.ff(ff: Bool, out: List<&2, U32>, +bb: U32) -> Put: - match ff: - case True{}: - Put{0 <> (255 <> out), 0, 0} - case False{}: - Put{bb <> out, 0, 0} - +# a finished byte. Bytes are kept as written; encode.pad stuffs them once, at the end. def encode.stuff(out: List<&2, U32>, +bb: U32) -> Put: - encode.stuff.ff(U32.is_eq(bb, 255), out, bb) + Put{bb <> out, 0, 0} def encode.bit.n(full: Bool, out: List<&2, U32>, +nb: U32, +nn: U32) -> Put: match full: @@ -195,27 +189,36 @@ def encode.bits.go(left: Nat, pp: Put, +code: U32) -> Put: def encode.bits(pp: Put, +len: U32, +code: U32) -> Put: encode.bits.go(U32.to_nat(len), pp, code) -def encode.pad.byte(ff: Bool, out: List<&2, U32>, +byte: U32) -> List<&2, U32>: +# one byte put in front of the stuffed bytes after it: 255 is followed by a 0 +def encode.stuff.put(ff: Bool, +bb: U32, acc: List<&2, U32>) -> List<&2, U32>: match ff: case True{}: - List.reverse(&2, U32, 0 <> (255 <> out)) + bb <> (0 <> acc) case False{}: - List.reverse(&2, U32, byte <> out) + bb <> acc + +# the written bytes, newest first, stuffed and put in order onto acc: every 255 is followed by a 0 +# (T.81 F.1.2.3). U32.is_eq, not a literal pattern, so a law reaches a symbolic byte. +def encode.stuff.all(out: List<&2, U32>, acc: List<&2, U32>) -> List<&2, U32>: + match out: + case Nil{}: + acc + case +bb <> rest: + encode.stuff.all(rest, encode.stuff.put(U32.is_eq(bb, 255), bb, acc)) def encode.pad.n(zz: Bool, out: List<&2, U32>, +buf: U32, +nn: U32) -> List<&2, U32>: match zz: case True{}: - List.reverse(&2, U32, out) + out case False{}: +sh = (8 - nn : U32) - +ones = (U32.shln(1, U32.to_nat(sh)) - 1 : U32) - encode.pad.byte(U32.is_eq(U32.or(U32.shln(buf, U32.to_nat(sh)), ones), 255), out, - U32.or(U32.shln(buf, U32.to_nat(sh)), ones)) + U32.or(U32.shln(buf, U32.to_nat(sh)), (U32.shln(1, U32.to_nat(sh)) - 1 : U32)) <> out +# the entropy-coded bytes: the open byte padded with 1 bits, then every byte stuffed, in order def encode.pad(pp: Put) -> List<&2, U32>: match pp: case Put{out, +buf, +n}: - encode.pad.n(U32.is_eq(n, 0), out, buf, n) + encode.stuff.all(encode.pad.n(U32.is_eq(n, 0), out, buf, n), []) def encode.drop(xs: List<&2, U32>) -> U32: Jpeg.decode.drop(xs) @@ -1640,18 +1643,25 @@ def encode.go(+ww: U32, +hh: U32, px: List<&2, U32>) -> List<&2, U32>: encode.put0()), 0, 0, mw, ww, hh, encode.ctx())) # the entropy-coded bytes, by what the watch found: neutral, one solid colour, or anything else -def encode.dispatch(+tag: U32, +color: U32, px: List<&2, U32>, +ww: U32, +hh: U32) -> List<&2, U32>: - match tag: - case 0: +def encode.dispatch.b(neu: Bool, sol: Bool, +color: U32, px: List<&2, U32>, +ww: U32, +hh: U32) -> List<&2, U32>: + match neu: + case True{}: +_c = (color - color : U32) +_d = encode.drop(px) + +_s = Bool.not(sol) encode.neutral(ww, hh) - case 1: - +_d = encode.drop(px) - encode.solid(ww, hh, color) - case _: - +_c = (color - color : U32) - encode.go(ww, hh, px) + case False{}: + match sol: + case True{}: + +_d = encode.drop(px) + encode.solid(ww, hh, color) + case False{}: + +_c = (color - color : U32) + encode.go(ww, hh, px) + +# the watch's tag, 0 neutral, 1 solid, 2 mixed, asked with U32.is_eq so a law reaches every tag +def encode.dispatch(+tag: U32, +color: U32, px: List<&2, U32>, +ww: U32, +hh: U32) -> List<&2, U32>: + encode.dispatch.b(U32.is_eq(tag, 0), U32.is_eq(tag, 1), color, px, ww, hh) # the watch's answer, dispatched def encode.arm.use(got: U32 & U32 & List<&2, U32>, +ww: U32, +hh: U32) -> List<&2, U32>: From 5bbb3be645b07ac2cad69afd6837c25a5542252d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 25 Sep 2026 21:39:40 +0000 Subject: [PATCH 4/7] feat: IMG-JPG-3 stuffing and unstuffing are inverses jpeg_unstuff: the decoder's bit reader, reading eight bits at a time from the encoder's stuffing of any bytes (encode.stuff.all), reads the bytes back with the stuffed zeros dropped. The reader ors each new bit in before the shifted accumulator, so a symbolic byte's bits come out as the byte after one rewrite per bit; the value is the same. Decoded output is byte-identical on every probe; a stuffed 255 read as 254 fails the law. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 38 ++++++++++++ PROOF.bend | 9 +++ SPEC.md | 4 +- docs/rfc/ezimg-law-inventory.md | 2 +- proof/wp10-jpeg-finish.bend | 106 ++++++++++++++++++++++++++++++++ src/jpeg.bend | 6 +- 6 files changed, 159 insertions(+), 6 deletions(-) diff --git a/LAWS.bend b/LAWS.bend index 7595f23..c88655b 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -2440,3 +2440,41 @@ law jpeg_walk_count: {U32.to_nat(Jpeg.decode.nblocks(ww, hh, hs, vs, hmax, vmax)) == List.length(&2, Jpeg.Ctrl, jpg.o.rows(U32.to_nat(Jpeg.decode.ceil(hh, (vmax * 8 : U32))), 0, 0, Jpeg.decode.ceil(ww, (hmax * 8 : U32)), ns, sids, ids, hs, vs)) : Nat} + +# one byte as its eight bits, the lowest first +type JByte is Data: + JByte{c0: Bool, c1: Bool, c2: Bool, c3: Bool, c4: Bool, c5: Bool, c6: Bool, c7: Bool} + +# a byte's value: its eight bits over 24 zero bits +def jpg.byte(bb: JByte) -> U32: + match bb: + case JByte{c0, c1, c2, c3, c4, c5, c6, c7}: + U32{WCon{c0, WCon{c1, WCon{c2, WCon{c3, WCon{c4, WCon{c5, WCon{c6, WCon{c7, Word.zero(24n)}}}}}}}}} + +# the bytes' values, in order +def jpg.bytes(bs: List<&2, JByte>) -> List<&2, U32>: + match bs: + case Nil{}: + [] + case bb <> rest: + jpg.byte(bb) <> jpg.bytes(rest) + +# the values the decoder's bit reader reads, eight bits at a time, left times: got is the reader and the +# value just read +def jpg.read8(left: Nat, got: Jpeg.Bits & U32) -> List<&2, U32>: + match left: + case 0n: + [] + case 1n+pp: + (bits, vv) = got + vv <> jpg.read8(pp, Jpeg.decode.read.n(8, bits)) + +# LAW: stuffing and unstuffing are inverses: the decoder's bit reader, reading eight bits at a time from the +# encoder's stuffing of any bytes (encode.stuff.all, which puts a 0 after every 255), reads the bytes back, +# the stuffed zeros dropped, whatever the reader's ok flag +# IMG-JPG-3 +law jpeg_unstuff: + for +bs: List<&2, JByte> + for +ok: U32 + {jpg.read8(List.length(&2, JByte, bs), Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, Jenc.encode.stuff.all( + List.reverse(&2, U32, jpg.bytes(bs)), [])})) == jpg.bytes(bs) : List<&2, U32>} diff --git a/PROOF.bend b/PROOF.bend index b22c0bc..70ecd66 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -5682,3 +5682,12 @@ def Laws.jpeg_walk_count(ww, hh, ids, hs, vs, hmax, vmax, ns, sids, top, h_ns, h W10.walk.count(Jpeg.decode.ceil(ww, (hmax * 8 : U32)), Jpeg.decode.ceil(hh, (vmax * 8 : U32)), Jpeg.decode.blocks.sum(hs, vs, 0), ns, sids, ids, hs, vs, top, W10.usum.pos(ns, sids, ids, hs, vs, h_ns, h_units), h_sum, h_fit) + +def Laws.jpeg_unstuff(bs, ok): + +xs = Laws.jpg.bytes(bs) + +nn = List.length(&2, Laws.JByte, bs) + Equal.trans(List<&2, U32>, Laws.jpg.read8(nn, Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, + Jenc.encode.stuff.all(List.reverse(&2, U32, xs), [])})), Laws.jpg.read8(nn, Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, + 0, W10.stf(xs, [])})), xs, Equal.cong(List<&2, U32>, List<&2, U32>, zs => Laws.jpg.read8(nn, Jpeg.decode.read.n(8, + Jpeg.Bits{0, ok, 0, zs})), Jenc.encode.stuff.all(List.reverse(&2, U32, xs), []), W10.stf(xs, []), + W10.stall.rev(xs, [], [])), W10.rd.list(bs, ok, [])) diff --git a/SPEC.md b/SPEC.md index 75ecfd1..32478ea 100644 --- a/SPEC.md +++ b/SPEC.md @@ -70,7 +70,7 @@ What a sample means, for every decoder and encoder. | :---- | :---- | :---- | :---- | :---- | | IMG-JPG-1 | `decode_jpeg` returns none for every input that opens with SOI, then segments other than frame headers (DHT, DQT, SOS, DRI, COM, APP0 to APP15) each with a length field that fits its body, then a frame header other than SOF0 or an SOF0 segment whose sample precision is not 8, whatever follows. | Proved | proved | LAWS.bend jpeg_refuse_sofn; LAWS.bend jpeg_refuse_precision | | IMG-JPG-2 | For a SOF0 frame whose components' sampling factors are each 1, 2 or 4, `decode_jpeg` places each component's samples in the order T.81 A.2.3 gives and replicates each sample over the pixels its sampling covers; for a factor outside 1, 2 and 4, `decode_jpeg` returns none. The sample values themselves are IMG-JPG-7's. | Proved | pending | LAWS.bend jpeg_refuse_factor1; LAWS.bend jpeg_refuse_factor3; LAWS.bend jpeg_refuse_factor1_any; LAWS.bend jpeg_refuse_factor3_any; LAWS.bend jpeg_refuse_count; LAWS.bend jpeg_comp_index; LAWS.bend jpeg_walk_unit; LAWS.bend jpeg_walk_comp; LAWS.bend jpeg_walk_mcu; LAWS.bend jpeg_walk_frame; LAWS.bend jpeg_walk_count; LAWS.bend jpeg_mcu_grid; LAWS.bend jpeg_mcu_grid_comp; LAWS.bend jpeg_block_cover; LAWS.bend jpeg_block_cover_all; LAWS.bend jpeg_points_at; LAWS.bend jpeg_rgbs_at | -| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized | +| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_unstuff; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized | | IMG-JPG-4 | `encode_jpeg(r)` is none exactly when `encode_png(r)` is none. | Proved | proved | LAWS.bend jpeg_png_refuse_alike | | IMG-JPG-5 | When `encode_jpeg(r)` is some, it begins with SOI and ends with EOI; when both sides of `r` are at most 65535 it is SOI, APP0 with the JFIF identifier, DQT, SOF0 carrying `r`'s width and height, two DHT, SOS, the entropy-coded data and EOI. | Proved | proved | LAWS.bend jpeg_enc_layout; LAWS.bend jpeg_enc_ends | | IMG-JPG-6 | `Jpeg.rgb(y, cb, cr)` equals the T.871 YCbCr to RGB conversion, rounded to nearest and clamped to 0 to 255, for every `y`, `cb`, `cr` from 0 to 255. | Trusted | | | @@ -82,7 +82,7 @@ What a sample means, for every decoder and encoder. | ID | Proved so far | Missing | | :---- | :---- | :---- | | IMG-JPG-2 | Refusal: a SOF0 segment with a factor outside 1, 2 and 4 makes `decode_jpeg` none, for one component or three, whatever precedes and follows it (`jpeg_refuse_factor1`, `jpeg_refuse_factor3`), and also after fill bytes before its marker and with a length field longer than its components (`jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any`); a SOF0 segment of any other component count is none whatever its factors (`jpeg_refuse_count`). Placement: a scan component takes the factors of the frame component whose identifier it names, the first with that identifier (`jpeg_comp_index`); the MCU walk goes from data unit `bi` to `bi + 1`, then to the next scan component, then to the next MCU (`jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu`); from the scan's first block the decoder's own walk visits the frame's MCUs in raster order, `ceil(w / 8 hmax)` to a row, and in each MCU the scan's components and their `hi * vi` units in T.81 order, the unit, component and column counters never wrapping (`jpeg_walk_frame`), and the U32 block count the decoder runs, `decode.nblocks`, is that order's length over `ceil(h / 8 vmax)` MCU rows when the scan carries the frame's data units and the count fits a U32 (`jpeg_walk_count`); the `hi * vi` units of any scan component sit in T.81 A.2.3's grid, `hi` to a row, each sample covering `hmax / hi` by `vmax / vi` pixels (`jpeg_mcu_grid`, `jpeg_mcu_grid_comp`); painting a block writes sample `k` over exactly the `pw` by `ph` pixels at `(ox + (k mod 8) * pw, oy + (k div 8) * ph)` inside the frame, for every sample size, 4 by 4 included, and onto every plane, one earlier blocks painted included (`jpeg_block_cover`, `jpeg_block_cover_all`); the decoder reads a plane out point by point, sample `k` being the value `Array.get` finds at index `k` (`jpeg_points_at`). Colour: sample `k` of the colour pass is `Jpeg.rgb` of point `k`'s Y, Cb and Cr (`jpeg_rgbs_at`) | that `Array.get` at an index finds the value the last `Array.set` at that index wrote, and a set at another index leaves it (the planes are perfect binary trees of `2^d` leaves, and the lemmas must follow the masked index down the tree), which joins the painting laws to `jpeg_points_at`. And the row is false as worded for frames of more than 2^31 points, which sides up to 65535 allow: `decode.plane` takes its depth from `U32.shl(nn)`, which wraps above 2^31, so the plane has fewer leaves than points and points alias (`decode.depth` is 0 at 2^31 + 1 points and 30 at 3 * 2^30), and past 2^32 points `w * h` itself wraps (a decision for the maintainer) | -| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves. The encoder's entropy-coded bytes have every 255 followed by a 0, for every size and samples (`jpeg_enc_stuffed`); the decoder's marker walk over the encoder's header reaches the entropy-coded data with the frame carrying the encoder's width and height, its scan and its tables, a baseline frame read and nothing refused (`jpeg_enc_header_walk`); inside the data the walk keeps every byte of stuffed data and stops at EOI (`jpeg_ent_walk`); so for every well-formed raster with both sides from 1 to 65535, `decode_jpeg(encode_jpeg(r))` is the decoder's scan decode, `decode.run`, of the encoder's own entropy-coded bytes in the frame of `r`'s width and height (`jpeg_round_trip_scan`); that scan decode is none or a picture of its frame's width and height, whatever the bytes (`jpeg_run_sized`); so `decode_jpeg(encode_jpeg(r))` is none or a raster of `r`'s size (`jpeg_round_trip_sized`) | that `decode.run` on the encoder's entropy-coded bytes is not none: that every Huffman lookup finds its symbol and every block ends by 64 coefficients, which is the bit writer (`encode.bits`, `encode.pack`, the pad with 1 bits) against the bit reader (`decode.nbits`, which drops a stuffed 0 after a 255), the Annex K tables `encode.huff` builds against the ones `decode.canon` builds and `decode.look` searches, and the encoder's three paths (neutral, solid, `encode.go`) each writing `ceil(w / 8) * ceil(h / 8)` MCUs of three blocks, each a DC code and magnitude, AC run and size codes and magnitudes, and EOB | +| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves. The encoder's entropy-coded bytes have every 255 followed by a 0, for every size and samples (`jpeg_enc_stuffed`), and the decoder's bit reader, reading eight bits at a time from the encoder's stuffing of any bytes, reads the bytes back with the stuffed zeros dropped (`jpeg_unstuff`); the decoder's marker walk over the encoder's header reaches the entropy-coded data with the frame carrying the encoder's width and height, its scan and its tables, a baseline frame read and nothing refused (`jpeg_enc_header_walk`); inside the data the walk keeps every byte of stuffed data and stops at EOI (`jpeg_ent_walk`); so for every well-formed raster with both sides from 1 to 65535, `decode_jpeg(encode_jpeg(r))` is the decoder's scan decode, `decode.run`, of the encoder's own entropy-coded bytes in the frame of `r`'s width and height (`jpeg_round_trip_scan`); that scan decode is none or a picture of its frame's width and height, whatever the bytes (`jpeg_run_sized`); so `decode_jpeg(encode_jpeg(r))` is none or a raster of `r`'s size (`jpeg_round_trip_sized`) | that `decode.run` on the encoder's entropy-coded bytes is not none: that every Huffman lookup finds its symbol and every block ends by 64 coefficients, which is the bit writer (`encode.bits`, `encode.pack`, the pad with 1 bits: that the bytes it writes carry the codes' bits in order) against the bit reader reading codes of 1 to 16 bits across byte boundaries (the byte-aligned case is `jpeg_unstuff`), the Annex K tables `encode.huff` builds against the ones `decode.canon` builds and `decode.look` searches, and the encoder's three paths (neutral, solid, `encode.go`) each writing `ceil(w / 8) * ceil(h / 8)` MCUs of three blocks, each a DC code and magnitude, AC run and size codes and magnitudes, and EOB | | IMG-PIX-1 | The JPEG side. `Jpeg.rgb` and `Jpeg.gray` give alpha 255 for every input (`jpeg_rgb_opaque`, `jpeg_gray_opaque`); every sample `decode_jpeg` returns is packed by `Jpeg.gray`, or every one by `Jpeg.rgb` (`jpeg_decode_packed`); every sample it returns has alpha 255 (`jpeg_decode_opaque`); a gray sample carries its level's low byte in R, G and B (`jpeg_gray_level`) | that every sample `decode_png` returns is `0xAARRGGBB`: `png_walk` (IMG-PNG-9) reaches `decode_png` for files in the standard chunk order, not yet for files with ancillary chunks. `jpeg_decode_packed` does not say that the gray packing is the one chosen for a one-component frame | | IMG-PNG-2 | the one-block encoding: every raster `png_ok` accepts (both sides nonzero, `w * h` samples, `w * h` below 2^32) whose scanlines, `h * (1 + w * c)` bytes for c channels (3 when every sample is opaque, 4 otherwise), are at most 65535 decodes from its PNG to itself (`png_roundtrip_one`), through `png_walk`, `inflate_enc_zlib`, `unfilter_filter` with filter None, and `png_px_rgb` / `png_px_rgba` | the two wide paths `encode_png` takes past 65535 scanline bytes: `enc.seal.wide` (colour type 6, `enc.pour`) and `enc.wide.rgb` (colour type 2, `enc.rgb.go`) must be shown to write `zlib.stored(65535, raw)` with the IDAT CRC. And the row is false as worded, even with the approved bound of fewer than 2^32 samples: the scanline count `enc.nbytes` and the IDAT length are U32s, so a raster of 2^32 scanline bytes or more encodes to a file that does not decode (a decision for the maintainer) | | IMG-PNG-9 | `Png.px.of`, the pixel stage, packs unfiltered bytes as the row says for every colour type: gray, gray with a tRNS key, gray and alpha, RGB, RGB with a tRNS key, RGBA (`png_px_grey`, `png_px_grey_key`, `png_px_ga`, `png_px_rgb`, `png_px_rgb_key`, `png_px_rgba`), and each index it decodes as its palette entry with alpha from tRNS or 255 (`png_px_indexed`); the decoder's last stage is `px.of` of `Png.unfilter`'s output with the width and height kept (`png_samples_frame`); and the walker lift, `png_walk`: a file of the signature, IHDR (any nonzero width and height, bit depth 8, a colour type the row names, methods 0), PLTE and tRNS where section 11 allows them, any IDAT chunks and IEND, each chunk framed with its CRC-32 and shorter than 2^32 bytes, decodes to the raster `px.of` packs, for the IHDR colour type and the PLTE and tRNS data, from `Png.unfilter` of the concatenated IDAT data inflated | files whose chunks come in another order the decoder accepts: ancillary chunks (which the walker skips, closing the IDAT run) between the critical ones | diff --git a/docs/rfc/ezimg-law-inventory.md b/docs/rfc/ezimg-law-inventory.md index a3a4ee0..1254222 100644 --- a/docs/rfc/ezimg-law-inventory.md +++ b/docs/rfc/ezimg-law-inventory.md @@ -329,5 +329,5 @@ requirement depends on. | WP6, the raster (IMG-RAS-1 to IMG-RAS-4) | done; IMG-RAS-2 to IMG-RAS-4 proved after rewording | U32 lemma library `proof/wp6-raster.bend`: `Word.cmp` is `Nat.cmp` of the words' numbers (`word_cmp_nat`, so `u32_lt`, `u32_le`, `u32_eq`); the adder, the subtractor and shift-and-add multiplication read as Nat when the result fits (`adc_nat`, `sbc_nat`, `mulgo_nat`), stated for U32 as `u32_add_below`, `u32_mul_below` (the exact result at most some U32's value) and `u32_sub_nat` (b <= a); `u32_index` (y * w + x does not wrap when x < w, y < h and w * h is some U32's value); Nat order, min, sub, take, drop and append lemmas. A closed 2^32 in a law is expanded in unary by the checker and overflows its stack, so the laws take w * h < 2^32 as a U32 `area` whose value is w * h. IMG-RAS-1: `fill_wf`, `fill_every`. IMG-RAS-2: `get_inside`, `get_inside_some`, `get_outside`, `set_inside`, `set_outside`. IMG-RAS-3: `crop_size`, `crop_wf`, `crop_at`, through a normal form of crop's case tree (`crop.nf_eq`) and the rows walk (`crop.rows_len`, `crop.rows_get`). IMG-RAS-4: `blit_size`, `blit_wf`, `blit_in`, `blit_out`, through the three parts of `blit.join` (`bj.top`, `bj.band`, `bj.bottom`) and one pasted row (`blit.row_left`, `row_mid`, `row_right`). No code change. Mutants: get with x and y swapped, set writing two samples, fill's count off by one and a wrong colour, get and set outside touching sample 0, crop's width off by one at the edge, crop's start and gap off by one, blit ignoring the offset (both axes, and x alone): each fails the gate in its law or its law's helper, and each makes a concrete instance of its law false. Left: IMG-RAS-2 to 4 for well-formed rasters with w * h of 2^32 or more, where they are false as worded (decision) | | WP8, JPEG numeric rows | done; IMG-JPG-6 and the new IMG-JPG-8 Trusted, IMG-JPG-2 and IMG-JPG-3 pending after rewording | IMG-JPG-6: `Jpeg.rgb.bits` rounded G's two terms separately with 16-bit constants and differed from T.871 on 3,320,385 of the 2^24 inputs, by 1; it now computes each channel exactly in millionths (`rgb.ch`: one division, one rounding, then the clamp), and a Python copy of the new U32 arithmetic matches exact T.871 on all 2^24 inputs; 48 of 108 JPEG probe decodes change, each channel by at most 1. A proof would need U32 division and products near 10^9 in Nat terms, and a unary comparison of 255 * 10^6 against 4 * 10^9 already exhausts the checker's memory, so the row moved to Trusted (maintainer decision). IMG-JPG-3 was false (round-trip error up to 7, a 2 by 2 raster suffices): split into the structural row, pending, and the Trusted bound IMG-JPG-8 (within 8; 7 measured before and after the conversion change, libjpeg reaches 4 with the same settings). IMG-JPG-2 reworded (sample values left to IMG-JPG-7) and proved in parts: `jpeg_refuse_factor1`, `jpeg_refuse_factor3` (a SOF0 segment with a factor outside 1, 2 and 4, after any prefix that leaves the walk at a segment boundary, makes `decode_jpeg` none), `jpeg_comp_index` (a scan component finds its frame component), `jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu` (the MCU walk's three steps), `jpeg_mcu_grid` (T.81 A.2.3 grid for every factor pair), `jpeg_block_cover` (sample replication, 4 by 4 left out for gate time) and `jpeg_rgbs_at` (the colour pass is pointwise). Code: factor and count checks as `U32.is_eq` Bools, `Comps.ok`, `decode.read.sof.c`; `decode.index` without its dummy accumulator. Lemmas in `proof/wp8-jpeg-numeric.bend` (`walk_app`, `walk_stop`, `fac_elim`). Each new law caught a planted mutant | | WP9, PNG round trip (IMG-PNG-2, IMG-PNG-9 lift) | done, both partial; IMG-PNG-2 false as worded past 2^32 scanline bytes (decision) | `png_walk` lifts the pixel stage to `decode_png`: a file of the signature, IHDR, PLTE and tRNS where allowed, any IDAT chunks and IEND, each chunk framed (`spec.chunk`: length, type, data, `crc.ref`) and shorter than 2^32 bytes, decodes to the raster `px.of` packs from `Png.unfilter` of the concatenated IDAT data inflated, with the IHDR colour type and the PLTE and tRNS data. `png_roundtrip_one` proves IMG-PNG-2 for every raster `png_ok` accepts whose scanlines fit one stored block (at most 65535 bytes): the encoder's file is `spec.png` with one IDAT (`seal.one.same`), then `png_walk`, `inflate_enc_zlib`, `unfilter_filter` with filter None, and `png_px_rgb` / `png_px_rgba` with the samples read back bit by bit (`be.back`: `be.u32` of `spec.be4`'s bytes is the word, 32 bits taken apart and 24 `Bool.or(b, False)` rewrites). Code, byte-identical on the probe outputs: `enc.r`, `enc.g`, `enc.b` mask with the constant first. Lemmas in `proof/wp9-png-roundtrip.bend`. Mutants (IDAT data appended in the wrong order, PLTE kept reversed, tRNS dropped at finish, IHDR width and height swapped, green written for blue, filter byte 1, alpha written 255): each fails the gate. Left: the wide paths (`enc.seal.wide` / `enc.pour`, `enc.wide.rgb` / `enc.rgb.go`), ancillary chunks for IMG-PNG-9, and the row's wording: `enc.nbytes` and the IDAT length are U32s, so a 32768 by 32768 raster with one non-opaque sample (2^30 samples) counts 32768 scanline bytes and takes the one-block path with a wrong LEN | -| WP10, JPEG placement and the round trip's structure (IMG-JPG-2, IMG-JPG-3) | done, both partial; IMG-JPG-2 false as worded above 2^31 points | IMG-JPG-2: `jpeg_block_cover_all` (every sample size, 4 by 4 included, onto any plane), `jpeg_mcu_grid_comp` (the A.2.3 grid for any scan component), `jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any` (fill bytes before the marker, a length longer than the components), `jpeg_refuse_count` (any other component count), `jpeg_points_at` (sample k of a plane's read-out is `Array.get` at k), `jpeg_walk_frame` (the decoder's own walk visits every MCU in raster order and each MCU's units in T.81 order, no counter wrapping) and `jpeg_walk_count` (the U32 block count `decode.nblocks` is that order's length when it fits). IMG-JPG-3: `jpeg_enc_stuffed`, `jpeg_enc_header_walk`, `jpeg_ent_walk`, `jpeg_round_trip_scan` (`decode_jpeg(encode_jpeg(r))` is the scan decode of the encoder's entropy-coded bytes in `r`'s frame), `jpeg_run_sized`, `jpeg_round_trip_sized` (none or a raster of `r`'s size). Code, byte-identical on all probe outputs and on crafted fill, marker and truncation cases: block painting written by rows, columns and pixels as the law-side cover is (`decode.splat`), which also drops `jpeg_block_cover`'s 1024-write normalisation from the gate; `decode.emit` reads a plane point by point in order (`decode.points`); `decode.nbits` and the entropy walk compare bytes with `U32.is_eq`; the encoder keeps raw bytes and stuffs once at the flush (`encode.stuff.all`), and dispatches on its tag with `U32.is_eq`; the block count is named (`decode.nblocks`). Lemmas in `proof/wp10-jpeg-finish.bend`, among them `dup` (two copies of an array, each equal to it: proofs are live, so an array cannot go to two lemmas) and the MCU-walk runs (`run.units` to `run.rows`). Mutants: a block column at `col * ph`, a scan component's factors taken from frame component `comp`, factor 3 accepted, a component count of 2 accepted, the read-out starting at point 1, an MCU row skipped, the block count summed from 1, a 255 not stuffed, the SOF width's low byte masked with 254, a stuffed 0 dropped by the walk, the entropy bytes not reversed, a gray picture's sides swapped, a colour picture's height as its width: each fails its law or that law's lemma. Left: IMG-JPG-2's `Array.get` after `Array.set` lemmas, and the row is false for frames above 2^31 points (`decode.plane`'s depth wraps; decision); IMG-JPG-3's Huffman round trip, that `decode.run` of the encoder's bytes is not none. Gate about 4 m 20 s, main's 4 m 50 s | +| WP10, JPEG placement and the round trip's structure (IMG-JPG-2, IMG-JPG-3) | done, both partial; IMG-JPG-2 false as worded above 2^31 points | IMG-JPG-2: `jpeg_block_cover_all` (every sample size, 4 by 4 included, onto any plane), `jpeg_mcu_grid_comp` (the A.2.3 grid for any scan component), `jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any` (fill bytes before the marker, a length longer than the components), `jpeg_refuse_count` (any other component count), `jpeg_points_at` (sample k of a plane's read-out is `Array.get` at k), `jpeg_walk_frame` (the decoder's own walk visits every MCU in raster order and each MCU's units in T.81 order, no counter wrapping) and `jpeg_walk_count` (the U32 block count `decode.nblocks` is that order's length when it fits). IMG-JPG-3: `jpeg_enc_stuffed`, `jpeg_unstuff` (the bit reader reads stuffed bytes back, eight bits at a time), `jpeg_enc_header_walk`, `jpeg_ent_walk`, `jpeg_round_trip_scan` (`decode_jpeg(encode_jpeg(r))` is the scan decode of the encoder's entropy-coded bytes in `r`'s frame), `jpeg_run_sized`, `jpeg_round_trip_sized` (none or a raster of `r`'s size). Code, byte-identical on all probe outputs and on crafted fill, marker and truncation cases: block painting written by rows, columns and pixels as the law-side cover is (`decode.splat`), which also drops `jpeg_block_cover`'s 1024-write normalisation from the gate; `decode.emit` reads a plane point by point in order (`decode.points`); `decode.nbits` and the entropy walk compare bytes with `U32.is_eq`, and `decode.nbits` ors the new bit in first; the encoder keeps raw bytes and stuffs once at the flush (`encode.stuff.all`), and dispatches on its tag with `U32.is_eq`; the block count is named (`decode.nblocks`). Lemmas in `proof/wp10-jpeg-finish.bend`, among them `dup` (two copies of an array, each equal to it: proofs are live, so an array cannot go to two lemmas) and the MCU-walk runs (`run.units` to `run.rows`). Mutants: a block column at `col * ph`, a scan component's factors taken from frame component `comp`, factor 3 accepted, a component count of 2 accepted, the read-out starting at point 1, an MCU row skipped, the block count summed from 1, a 255 not stuffed, the SOF width's low byte masked with 254, a stuffed 0 dropped by the walk, the entropy bytes not reversed, a gray picture's sides swapped, a colour picture's height as its width, a stuffed 255 read as 254: each fails its law or that law's lemma. Left: IMG-JPG-2's `Array.get` after `Array.set` lemmas, and the row is false for frames above 2^31 points (`decode.plane`'s depth wraps; decision); IMG-JPG-3's Huffman round trip, that `decode.run` of the encoder's bytes is not none. Gate about 4 m 20 s, main's 4 m 50 s | | Phase 4b, cheap rows | next | IMG-PNG-3, IMG-PNG-8, IMG-JPG-1, IMG-RAS-1, IMG-RAS-2; these need ordering and product lemmas on U32 (`is_lt`, `is_le`, `*` without wrap) next to `ueq` | diff --git a/proof/wp10-jpeg-finish.bend b/proof/wp10-jpeg-finish.bend index fd9f1cc..8a2611c 100644 --- a/proof/wp10-jpeg-finish.bend +++ b/proof/wp10-jpeg-finish.bend @@ -2204,3 +2204,109 @@ def walk.count( Jpeg.Ctrl, Laws.jpg.o.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs)), mul.assoc(hn, wn, uu), Equal.sym(Nat, List.length(&2, Jpeg.Ctrl, Laws.jpg.o.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs)), Nat.mul(hn, Nat.mul(wn, uu)), len.rows(hn, 0, 0, mw, ns, sids, ids, hs, vs))))) + +# ---- stuffing and unstuffing (IMG-JPG-3) ---- + +# bytes stuffed in order onto acc, the first byte first +def stf(xs: List<&2, U32>, +acc: List<&2, U32>) -> List<&2, U32>: + match xs: + case Nil{}: + acc + case +bb <> rest: + Jenc.encode.stuff.put(U32.is_eq(bb, 255), bb, stf(rest, acc)) + +# the encoder stuffing written bytes, newest first, is stuffing them in order +def stall.rev( + xs: List<&2, U32>, + +rr: List<&2, U32>, + +acc: List<&2, U32> +) -> {Jenc.encode.stuff.all(List.reverse.go(&2, U32, xs, rr), acc) == Jenc.encode.stuff.all(rr, stf(xs, acc)) : + List<&2, U32>}: + match xs: + case Nil{}: + {==} + case +bb <> rest: + stall.rev(rest, bb <> rr, acc) + +# a byte that is not 255, read by the bit reader: its eight bits, top first, each or-ed with False +def rd.plain( + h0: Bool, + +c0: Bool, + +c1: Bool, + +c2: Bool, + +c3: Bool, + +c4: Bool, + +c5: Bool, + +c6: Bool, + +c7: Bool, + +rest: List<&2, U32>, + +ok: U32 +) -> {Jpeg.decode.nbits(8n, True{}, W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7) <> rest, False{}, False{}, h0, 0, 0, ok, + 0) == (Jpeg.Bits{0, ok, 0, rest}, W7.byte.w(Bool.or(c0, False{}), Bool.or(c1, False{}), Bool.or(c2, False{}), + Bool.or(c3, False{}), Bool.or(c4, False{}), Bool.or(c5, False{}), Bool.or(c6, False{}), Bool.or(c7, False{}))) : + Jpeg.Bits & U32}: + {==} + +# one stuffed byte, read by the bit reader: the byte back, the reader at the bytes after it +def rd.z( + zz: Bool, + +c0: Bool, + +c1: Bool, + +c2: Bool, + +c3: Bool, + +c4: Bool, + +c5: Bool, + +c6: Bool, + +c7: Bool, + +rest: List<&2, U32>, + +ok: U32, + +ee: {U32.is_eq(W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7), 255) == zz : Bool} +) -> {Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, Jenc.encode.stuff.put(zz, W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7), + rest)}) == (Jpeg.Bits{0, ok, 0, rest}, W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7)) : Jpeg.Bits & U32}: + match zz: + case False{}: + +bw = W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7) + +h0 = U32.is_eq(bw, 0) + Equal.trans(Jpeg.Bits & U32, Jpeg.decode.nbits(8n, True{}, bw <> rest, False{}, U32.is_eq(bw, 255), h0, 0, 0, + ok, 0), + Jpeg.decode.nbits(8n, True{}, bw <> rest, False{}, False{}, h0, 0, 0, ok, 0), (Jpeg.Bits{0, ok, 0, rest}, bw), + Equal.cong(Bool, Jpeg.Bits & U32, hb => Jpeg.decode.nbits(8n, True{}, bw <> rest, False{}, hb, h0, 0, 0, ok, 0), + U32.is_eq(bw, 255), False{}, ee), Equal.trans(Jpeg.Bits & U32, Jpeg.decode.nbits(8n, True{}, bw <> rest, + False{}, + False{}, h0, 0, 0, ok, 0), (Jpeg.Bits{0, ok, 0, rest}, W7.byte.w(Bool.or(c0, False{}), Bool.or(c1, False{}), + Bool.or(c2, False{}), Bool.or(c3, False{}), Bool.or(c4, False{}), Bool.or(c5, False{}), Bool.or(c6, False{}), + Bool.or(c7, False{}))), (Jpeg.Bits{0, ok, 0, rest}, bw), rd.plain(h0, c0, c1, c2, c3, c4, c5, c6, c7, rest, ok), + Equal.cong(U32, Jpeg.Bits & U32, vv => {(Jpeg.Bits{0, ok, 0, rest}, vv) : Jpeg.Bits & U32}, + W7.byte.w(Bool.or(c0, False{}), Bool.or(c1, False{}), Bool.or(c2, False{}), Bool.or(c3, False{}), + Bool.or(c4, False{}), Bool.or(c5, False{}), Bool.or(c6, False{}), Bool.or(c7, False{})), bw, + W7.or_byte(c0, c1, c2, c3, c4, c5, c6, c7)))) + case True{}: + +bw = W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7) + eb = Equal.sym(U32, bw, 255, U32L.ueq(bw, 255, ee)) + %eb : {Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, _ <> (0 <> rest)}) == (Jpeg.Bits{0, ok, 0, rest}, _) : + Jpeg.Bits & U32} + {==} + +# the bit reader reads stuffed bytes back, eight bits at a time +def rd.list( + +bs: List<&2, Laws.JByte>, + +ok: U32, + +rest: List<&2, U32> +) -> {Laws.jpg.read8(List.length(&2, Laws.JByte, bs), Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, + stf(Laws.jpg.bytes(bs), rest)})) == Laws.jpg.bytes(bs) : List<&2, U32>}: + match bs: + case Nil{}: + {==} + case +bb <> +tl: + match bb: + case Laws.JByte{+c0, +c1, +c2, +c3, +c4, +c5, +c6, +c7}: + +bw = W7.byte.w(c0, c1, c2, c3, c4, c5, c6, c7) + +st = stf(Laws.jpg.bytes(tl), rest) + Equal.trans(List<&2, U32>, Laws.jpg.read8(1n+List.length(&2, Laws.JByte, tl), Jpeg.decode.read.n(8, + Jpeg.Bits{0, ok, 0, Jenc.encode.stuff.put(U32.is_eq(bw, 255), bw, st)})), Laws.jpg.read8(1n+List.length(&2, + Laws.JByte, tl), (Jpeg.Bits{0, ok, 0, st}, bw)), bw <> Laws.jpg.bytes(tl), Equal.cong(Jpeg.Bits & U32, + List<&2, U32>, gg => Laws.jpg.read8(1n+List.length(&2, Laws.JByte, tl), gg), Jpeg.decode.read.n(8, + Jpeg.Bits{0, ok, 0, Jenc.encode.stuff.put(U32.is_eq(bw, 255), bw, st)}), (Jpeg.Bits{0, ok, 0, st}, bw), + rd.z(U32.is_eq(bw, 255), c0, c1, c2, c3, c4, c5, c6, c7, st, ok, {==})), Equal.cong(List<&2, U32>, + List<&2, U32>, ll => {bw <> ll : List<&2, U32>}, Laws.jpg.read8(List.length(&2, Laws.JByte, tl), + Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, st})), Laws.jpg.bytes(tl), rd.list(tl, ok, rest))) diff --git a/src/jpeg.bend b/src/jpeg.bend index b8878e2..d6c7fe6 100644 --- a/src/jpeg.bend +++ b/src/jpeg.bend @@ -518,7 +518,7 @@ def decode.nbits( match eq: case False{}: decode.nbits(p, U32.is_eq((nn - 1 : U32), 0), xs, ff, h255, h0, (nn - 1 : U32), U32.and(255, U32.shl(buf)), - ok, U32.or(U32.shl(acc), U32.shrn(buf, 7n))) + ok, U32.or(U32.shrn(buf, 7n), U32.shl(acc))) case True{}: match xs: case Nil{}: @@ -532,7 +532,7 @@ def decode.nbits( 0, 0, ok, acc) case False{}: decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), 7, - U32.and(255, U32.shl(bb)), ok, U32.or(U32.shl(acc), U32.shrn(bb, 7n))) + U32.and(255, U32.shl(bb)), ok, U32.or(U32.shrn(bb, 7n), U32.shl(acc))) case True{}: match h255: case True{}: @@ -542,7 +542,7 @@ def decode.nbits( match h0: case True{}: decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), - 7, 254, ok, U32.or(U32.shl(acc), 1)) + 7, 254, ok, U32.or(1, U32.shl(acc))) case False{}: decode.nbits(p, False{}, rest, False{}, decode.head.is(rest, 255), decode.head.is(rest, 0), 7, 0, 0, U32.shl(acc)) From 0ec5e59ca1a07a2941ae2ef3ac269461497b36b6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 01:13:15 +0000 Subject: [PATCH 5/7] feat: IMG-JPG-3 bit writer against bit reader, and the Annex K tables' codes decode to their symbols jpeg_bits_round_trip: codes of up to 16 bits written by the encoder's bit writer and padded are read back by the decoder's bit reader, at every byte offset. jpeg_huff_dc and jpeg_huff_ac: every code the encoder's books hold decodes, through decode.huff and decode.look over the table decode.canon builds, to its symbol at any bit offset, the reader left just after it. encode.pad.n puts the pad mask first in its U32.or, so the pad byte is a word of the pending bits; the bytes are the same. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 188 +++ PROOF.bend | 12 + SPEC.md | 2 +- proof/wp12-jpeg-huffman.bend | 2994 ++++++++++++++++++++++++++++++++++ src/jpeg_enc.bend | 2 +- 5 files changed, 3196 insertions(+), 2 deletions(-) create mode 100644 proof/wp12-jpeg-huffman.bend diff --git a/LAWS.bend b/LAWS.bend index c88655b..c3f7773 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -2478,3 +2478,191 @@ law jpeg_unstuff: for +ok: U32 {jpg.read8(List.length(&2, JByte, bs), Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, Jenc.encode.stuff.all( List.reverse(&2, U32, jpg.bytes(bs)), [])})) == jpg.bytes(bs) : List<&2, U32>} + +# ---- wp12-jpeg-huffman ---- + +# a bit as 1 or 0 +def jpg.bitu(bb: Bool) -> U32: + match bb: + case True{}: + 1 + case False{}: + 0 + +# a code's value: its bits, the first highest, shifted onto acc +def jpg.num(bs: List<&2, Bool>, acc: U32) -> U32: + match bs: + case Nil{}: + acc + case bb <> rest: + jpg.num(rest, U32.or(U32.shl(acc), jpg.bitu(bb))) + +# the encoder's bit writer given codes one after another, each code a list of bits, the first highest, written +# with encode.bits at its length and value +def jpg.write(cs: List<&2, List<&2, Bool>>, pp: Jenc.Put) -> Jenc.Put: + match cs: + case Nil{}: + pp + case +cc <> rest: + jpg.write(rest, Jenc.encode.bits(pp, U32.from_nat(List.length(&2, Bool, cc)), jpg.num(cc, 0))) + +# both answers: the first, then the second +def jpg.also(ok: Bool, rest: Bool) -> Bool: + match ok: + case True{}: + rest + case False{}: + match rest: + case _r: + False{} + +# every code has at most 16 bits +def jpg.codes16(cs: List<&2, List<&2, Bool>>) -> Bool: + match cs: + case Nil{}: + True{} + case cc <> rest: + jpg.also(Nat.is_le(List.length(&2, Bool, cc), 16n), jpg.codes16(rest)) + +# the reader a read leaves +def jpg.got.bits(got: Jpeg.Bits & U32) -> Jpeg.Bits: + match got: + case (bits, _v): + bits + +# the value a read reads +def jpg.got.val(got: Jpeg.Bits & U32) -> U32: + match got: + case (_b, +vv): + vv + +# one more value before the values and the flag after it +def jpg.rcons(+vv: U32, got: List<&2, U32> & U32) -> List<&2, U32> & U32: + match got: + case (vs, +ok): + (vv <> vs, ok) + +# the reader's ok flag +def jpg.bits.ok(bits: Jpeg.Bits) -> U32: + match bits: + case Jpeg.Bits{_n, +ok, _b, _x}: + ok + +# the values the decoder's bit reader reads with decode.read.n, one code's length at a time, and its ok flag at +# the end +def jpg.reads(cs: List<&2, List<&2, Bool>>, +bits: Jpeg.Bits) -> List<&2, U32> & U32: + match cs: + case Nil{}: + ([], jpg.bits.ok(bits)) + case +cc <> rest: + +nn = U32.from_nat(List.length(&2, Bool, cc)) + jpg.rcons(jpg.got.val(Jpeg.decode.read.n(nn, bits)), jpg.reads(rest, jpg.got.bits(Jpeg.decode.read.n(nn, + bits)))) + +# each code's value +def jpg.nums(cs: List<&2, List<&2, Bool>>) -> List<&2, U32>: + match cs: + case Nil{}: + [] + case cc <> rest: + jpg.num(cc, 0) <> jpg.nums(rest) + +# LAW: the bit writer against the bit reader: codes of at most 16 bits each, any number of them, written one +# after another by the encoder's bit writer from an empty writer (so they cross byte boundaries at every +# offset) and padded and stuffed by encode.pad, are read back by the decoder's bit reader one code's length at a +# time, each code's value in order, the reader's ok flag still 1 +# IMG-JPG-3 +law jpeg_bits_round_trip: + for +cs: List<&2, List<&2, Bool>> + for h_len: {jpg.codes16(cs) == True{} : Bool} + {jpg.reads(cs, Jpeg.Bits{0, 1, 0, Jenc.encode.pad(jpg.write(cs, Jenc.encode.put0()))}) == (jpg.nums(cs), 1) : + List<&2, U32> & U32} + +# the reader after reading codes of these lengths, one at a time +def jpg.skip(cs: List<&2, List<&2, Bool>>, +bits: Jpeg.Bits) -> Jpeg.Bits: + match cs: + case Nil{}: + bits + case +cc <> rest: + jpg.skip(rest, jpg.got.bits(Jpeg.decode.read.n(U32.from_nat(List.length(&2, Bool, cc)), bits))) + +# the writer after the encoder writes a symbol's code from its book +def jpg.emit(got: Array & Jenc.Put) -> Jenc.Put: + match got: + case (_a, pp): + pp + +# the first answer, or else the second +def jpg.eith(aa: Bool, bb: Bool) -> Bool: + match aa: + case True{}: + match bb: + case _b: + True{} + case False{}: + bb + +# ss is one of xs +def jpg.memb(+ss: U32, xs: List<&2, U32>) -> Bool: + match xs: + case Nil{}: + False{} + case +xx <> rest: + jpg.eith(U32.is_eq(ss, xx), jpg.memb(ss, rest)) + +# a Huffman lookup's symbol, the codes after it read back from its reader, and its ok flag +def jpg.hit(hit: Jpeg.Hit, post: List<&2, List<&2, Bool>>) -> U32 & (List<&2, U32> & U32) & U32: + match hit: + case Jpeg.Hit{+sym, bits, +ok}: + (sym, jpg.reads(post, bits), ok) + +# the entropy-coded bytes of codes pre, the encoder's code for symbol sy from a book, and codes post +def jpg.sym.bytes( + +pre: List<&2, List<&2, Bool>>, + book: Array, + +sy: U32, + +post: List<&2, List<&2, Bool>> +) -> List<&2, U32>: + Jenc.encode.pad(jpg.write(post, jpg.emit(Jenc.encode.emit(Jenc.encode.sym(book, sy), jpg.write(pre, + Jenc.encode.put0()))))) + +# the decoder's lookup in a table, after reading codes pre back from those bytes: the symbol, codes post read back, +# and the ok flag +def jpg.sym.back( + +pre: List<&2, List<&2, Bool>>, + book: Array, + +tab: Jpeg.Huff, + +sy: U32, + +post: List<&2, List<&2, Bool>> +) -> U32 & (List<&2, U32> & U32) & U32: + jpg.hit(Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, jpg.skip(pre, Jpeg.Bits{0, 1, 0, jpg.sym.bytes(pre, book, sy, + post)})}, tab), post) + +# LAW: the Annex K luminance DC table: for every DC symbol, the code the encoder's book (encode.huff) holds for it, +# written at any bit offset between any codes, is decoded by the decoder's Huffman lookup in the table +# decode.canon builds (decode.look) to that symbol, the reader left exactly after the code, ok still 1 +# IMG-JPG-3 +law jpeg_huff_dc: + for +pre: List<&2, List<&2, Bool>> + for h_pre: {jpg.codes16(pre) == True{} : Bool} + for +sy: U32 + for h_sy: {jpg.memb(sy, Jenc.encode.dcsyms()) == True{} : Bool} + for +post: List<&2, List<&2, Bool>> + for h_post: {jpg.codes16(post) == True{} : Bool} + {jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()), Jpeg.decode.canon(272n, 0n, + Jenc.encode.dccounts(), Jenc.encode.dcsyms(), 0, 0, [], [], []), sy, post) == (sy, (jpg.nums(post), 1), 1) : + U32 & (List<&2, U32> & U32) & U32} + +# LAW: the Annex K luminance AC table, the same: every AC symbol's code from the encoder's book decodes to the +# symbol at any bit offset, the reader left exactly after it +# IMG-JPG-3 +law jpeg_huff_ac: + for +pre: List<&2, List<&2, Bool>> + for h_pre: {jpg.codes16(pre) == True{} : Bool} + for +sy: U32 + for h_sy: {jpg.memb(sy, Jenc.encode.acsyms()) == True{} : Bool} + for +post: List<&2, List<&2, Bool>> + for h_post: {jpg.codes16(post) == True{} : Bool} + {jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), Jpeg.decode.canon(272n, 0n, + Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), sy, post) == (sy, (jpg.nums(post), 1), 1) : + U32 & (List<&2, U32> & U32) & U32} diff --git a/PROOF.bend b/PROOF.bend index 70ecd66..a20299d 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -19,6 +19,7 @@ import ./proof/wp8-jpeg-numeric.bend as W8 import ./proof/wp9-png-roundtrip.bend as W9 import ./src/inflate.bend as Inf import ./proof/wp10-jpeg-finish.bend as W10 +import ./proof/wp12-jpeg-huffman.bend as W12 # U32.or with 0xFF000000 first has alpha 255, whatever the other operand: # the 32 bits are taken apart four at a time, and Bool.or(True, b) is True. @@ -5691,3 +5692,14 @@ def Laws.jpeg_unstuff(bs, ok): 0, W10.stf(xs, [])})), xs, Equal.cong(List<&2, U32>, List<&2, U32>, zs => Laws.jpg.read8(nn, Jpeg.decode.read.n(8, Jpeg.Bits{0, ok, 0, zs})), Jenc.encode.stuff.all(List.reverse(&2, U32, xs), []), W10.stf(xs, []), W10.stall.rev(xs, [], [])), W10.rd.list(bs, ok, [])) + +# ---- wp12-jpeg-huffman ---- + +def Laws.jpeg_bits_round_trip(cs, h_len): + W12.round_trip(cs, h_len) + +def Laws.jpeg_huff_dc(pre, h_pre, sy, h_sy, post, h_post): + W12.huff_law.dc(pre, h_pre, sy, h_sy, post, h_post) + +def Laws.jpeg_huff_ac(pre, h_pre, sy, h_sy, post, h_post): + W12.huff_law.ac(pre, h_pre, sy, h_sy, post, h_post) diff --git a/SPEC.md b/SPEC.md index 599fb41..2592a2a 100644 --- a/SPEC.md +++ b/SPEC.md @@ -70,7 +70,7 @@ What a sample means, for every decoder and encoder. | :---- | :---- | :---- | :---- | :---- | | IMG-JPG-1 | `decode_jpeg` returns none for every input that opens with SOI, then segments other than frame headers (DHT, DQT, SOS, DRI, COM, APP0 to APP15) each with a length field that fits its body, then a frame header other than SOF0 or an SOF0 segment whose sample precision is not 8, whatever follows. | Proved | proved | LAWS.bend jpeg_refuse_sofn; LAWS.bend jpeg_refuse_precision | | IMG-JPG-2 | For a SOF0 frame of at most 2^31 points whose components' sampling factors are each 1, 2 or 4, `decode_jpeg` places each component's samples in the order T.81 A.2.3 gives and replicates each sample over the pixels its sampling covers; for a factor outside 1, 2 and 4, `decode_jpeg` returns none. The sample values themselves are IMG-JPG-7's. | Proved | pending | LAWS.bend jpeg_refuse_factor1; LAWS.bend jpeg_refuse_factor3; LAWS.bend jpeg_refuse_factor1_any; LAWS.bend jpeg_refuse_factor3_any; LAWS.bend jpeg_refuse_count; LAWS.bend jpeg_comp_index; LAWS.bend jpeg_walk_unit; LAWS.bend jpeg_walk_comp; LAWS.bend jpeg_walk_mcu; LAWS.bend jpeg_walk_frame; LAWS.bend jpeg_walk_count; LAWS.bend jpeg_mcu_grid; LAWS.bend jpeg_mcu_grid_comp; LAWS.bend jpeg_block_cover; LAWS.bend jpeg_block_cover_all; LAWS.bend jpeg_points_at; LAWS.bend jpeg_rgbs_at | -| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535 and at most 2^31 samples, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_unstuff; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized | +| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535 and at most 2^31 samples, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_unstuff; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized; LAWS.bend jpeg_bits_round_trip; LAWS.bend jpeg_huff_dc; LAWS.bend jpeg_huff_ac | | IMG-JPG-4 | `encode_jpeg(r)` is none exactly when `encode_png(r)` is none. | Proved | proved | LAWS.bend jpeg_png_refuse_alike | | IMG-JPG-5 | When `encode_jpeg(r)` is some, it begins with SOI and ends with EOI; when both sides of `r` are at most 65535 it is SOI, APP0 with the JFIF identifier, DQT, SOF0 carrying `r`'s width and height, two DHT, SOS, the entropy-coded data and EOI. | Proved | proved | LAWS.bend jpeg_enc_layout; LAWS.bend jpeg_enc_ends | | IMG-JPG-6 | `Jpeg.rgb(y, cb, cr)` equals the T.871 YCbCr to RGB conversion, rounded to nearest and clamped to 0 to 255, for every `y`, `cb`, `cr` from 0 to 255. | Trusted | | | diff --git a/proof/wp12-jpeg-huffman.bend b/proof/wp12-jpeg-huffman.bend new file mode 100644 index 0000000..153b2ad --- /dev/null +++ b/proof/wp12-jpeg-huffman.bend @@ -0,0 +1,2994 @@ +# proof/wp12-jpeg-huffman: lemmas for IMG-JPG-3's Huffman round trip: the encoder's bit writer against the +# decoder's bit reader, and the Annex K tables the encoder's books hold against the ones the decoder builds. +# PROOF.bend imports this file as W12, so the gate checks it. +import Base +import ../LAWS.bend as Laws +import ../src/jpeg.bend as Jpeg +import ../src/jpeg_enc.bend as Jenc +import ./u32.bend as U32L +import ./wp6-raster.bend as R +import ./wp10-jpeg-finish.bend as W10 + +# a bit as a U32: 0 or 1 +def ubit(bb: Bool) -> U32: + U32{WCon{bb, Word.zero(31n)}} + +# the low bit of a U32 +def bit0(xx: U32) -> Bool: + match xx: + case U32{ww}: + match ww: + case WCon{b0, _t}: + b0 + +# a word shifted up one place with bb in the low bit, the top bit dropped +def vstep(xx: U32, bb: Bool) -> U32: + match xx: + case U32{ww}: + match ww: + case WCon{w0, wt}: + U32{WCon{bb, Word.shl.put(31n, w0, wt)}} + +# or with zeros on the right is the word +def or_zero_r(nn: Nat, ww: Word(nn)) -> {Word.or(nn, ww, Word.zero(nn)) == ww : Word(nn)}: + match nn: + case 0n: + match ww: + case WNil{}: + {==} + case 1n+pp: + match ww: + case WCon{bb, tt}: + match bb: + case True{}: + Equal.cong(Word(pp), Word.Con, xx => WCon{True{}, xx}, Word.or(pp, tt, Word.zero(pp)), tt, + or_zero_r(pp, tt)) + case False{}: + Equal.cong(Word(pp), Word.Con, xx => WCon{False{}, xx}, Word.or(pp, tt, Word.zero(pp)), tt, + or_zero_r(pp, tt)) + +# or with zeros on the left is the word +def or_zero_l(nn: Nat, ww: Word(nn)) -> {Word.or(nn, Word.zero(nn), ww) == ww : Word(nn)}: + match nn: + case 0n: + match ww: + case WNil{}: + {==} + case 1n+pp: + match ww: + case WCon{bb, tt}: + Equal.cong(Word(pp), Word.Con, xx => WCon{bb, xx}, Word.or(pp, Word.zero(pp), tt), tt, or_zero_l(pp, tt)) + +# and with zeros on the right is zeros +def and_zero_r(nn: Nat, ww: Word(nn)) -> {Word.and(nn, ww, Word.zero(nn)) == Word.zero(nn) : Word(nn)}: + match nn: + case 0n: + match ww: + case WNil{}: + {==} + case 1n+pp: + match ww: + case WCon{bb, tt}: + match bb: + case True{}: + Equal.cong(Word(pp), Word.Con, xx => WCon{False{}, xx}, Word.and(pp, tt, Word.zero(pp)), + Word.zero(pp), and_zero_r(pp, tt)) + case False{}: + Equal.cong(Word(pp), Word.Con, xx => WCon{False{}, xx}, Word.and(pp, tt, Word.zero(pp)), + Word.zero(pp), and_zero_r(pp, tt)) + +# the writer's step: shift up, or in the bit +def or_shl_u(xx: U32, bb: Bool) -> {U32.or(U32.shl(xx), ubit(bb)) == vstep(xx, bb) : U32}: + match xx: + case U32{ww}: + match ww: + case WCon{w0, wt}: + Equal.cong(Word(31n), U32, zz => U32{WCon{bb, zz}}, Word.or(31n, Word.shl.put(31n, w0, wt), Word.zero(31n)), + Word.shl.put(31n, w0, wt), or_zero_r(31n, Word.shl.put(31n, w0, wt))) + +# the reader's step, by the bit +def or_u_shl.b( + bb: Bool, + +sp: Word(31n) +) -> {U32{WCon{Bool.or(bb, False{}), Word.or(31n, Word.zero(31n), sp)}} == U32{WCon{bb, sp}} : U32}: + match bb: + case True{}: + Equal.cong(Word(31n), U32, zz => U32{WCon{True{}, zz}}, Word.or(31n, Word.zero(31n), sp), sp, or_zero_l(31n, sp)) + case False{}: + Equal.cong(Word(31n), U32, zz => U32{WCon{False{}, zz}}, Word.or(31n, Word.zero(31n), sp), sp, or_zero_l(31n, sp)) + +# the reader's step: the bit or-ed onto the shifted value +def or_u_shl(xx: U32, bb: Bool) -> {U32.or(ubit(bb), U32.shl(xx)) == vstep(xx, bb) : U32}: + match xx: + case U32{ww}: + match ww: + case WCon{w0, wt}: + or_u_shl.b(bb, Word.shl.put(31n, w0, wt)) + +# and with 1, by the low bit +def and_one.b( + w0: Bool, + +wt: Word(31n) +) -> {U32{WCon{Bool.and(w0, True{}), Word.and(31n, wt, Word.zero(31n))}} == U32{WCon{w0, Word.zero(31n)}} : U32}: + match w0: + case True{}: + Equal.cong(Word(31n), U32, zz => U32{WCon{True{}, zz}}, Word.and(31n, wt, Word.zero(31n)), Word.zero(31n), + and_zero_r(31n, wt)) + case False{}: + Equal.cong(Word(31n), U32, zz => U32{WCon{False{}, zz}}, Word.and(31n, wt, Word.zero(31n)), Word.zero(31n), + and_zero_r(31n, wt)) + +# and with 1 is the low bit +def and_one(xx: U32) -> {U32.and(xx, 1) == ubit(bit0(xx)) : U32}: + match xx: + case U32{ww}: + match ww: + case WCon{w0, wt}: + and_one.b(w0, wt) + +# bits folded into a value, the first bit highest: each shifts the value up and sits in the low bit +def vfold(bs: List<&2, Bool>, acc: U32) -> U32: + match bs: + case Nil{}: + acc + case bb <> rest: + vfold(rest, vstep(acc, bb)) + +# the encoder's bit writer holding out (bytes, newest first) and the pending bits pp, fewer than 8 +def wst(out: List<&2, U32>, +pp: List<&2, Bool>) -> Jenc.Put: + Jenc.Put{out, vfold(pp, 0), U32.from_nat(List.length(&2, Bool, pp))} + +# seven pending bits: the next one finishes a byte +def is7(pp: List<&2, Bool>) -> Bool: + Nat.is_eq(List.length(&2, Bool, pp), 7n) + +# the writer after one more bit: a finished byte, or one more pending bit +def wone(full: Bool, out: List<&2, U32>, pp: List<&2, Bool>, bb: Bool) -> Jenc.Put: + match full: + case True{}: + Jenc.Put{vfold(List.append(&2, Bool, pp, [bb]), 0) <> out, 0, 0} + case False{}: + wst(out, List.append(&2, Bool, pp, [bb])) + +# fewer than 8 pending bits +def short(pp: List<&2, Bool>) -> Bool: + Nat.is_lt(List.length(&2, Bool, pp), 8n) + +# the type False names: Unit for False, Empty for True +def false_ty(bb: Bool) -> Type: + match bb: + case False{}: + Unit + case True{}: + Empty + +# False is never True +def false_true(ee: {False{} == True{} : Bool}) -> Empty: + %ee : false_ty(_) + Unit{} + +# nothing is below zero +def lt0(xx: Nat) -> {False{} == Nat.is_lt(xx, 0n) : Bool}: + match xx: + case 0n: + {==} + case 1n+_pp: + {==} + +# eight or more pending bits are not short +def long8(xs: List<&2, Bool>, hh: {Nat.is_lt(List.length(&2, Bool, xs), 0n) == True{} : Bool}) -> Empty: + false_true(Equal.trans(Bool, False{}, Nat.is_lt(List.length(&2, Bool, xs), 0n), True{}, + lt0(List.length(&2, Bool, xs)), hh)) + +# the writer's bit step on a model state, by the pending bits +def wbit.p( + out: List<&2, U32>, + pp: List<&2, Bool>, + bb: Bool, + hh: {short(pp) == True{} : Bool} +) -> {Jenc.encode.bit.n(U32.is_eq(U32.from_nat(List.length(&2, Bool, pp)), 7), out, vstep(vfold(pp, 0), bb), + U32.from_nat(List.length(&2, Bool, pp))) == wone(is7(pp), out, pp, bb) : Jenc.Put}: + match pp: + case Nil{}: + {==} + case p0 <> t0: + match t0: + case Nil{}: + {==} + case p1 <> t1: + match t1: + case Nil{}: + {==} + case p2 <> t2: + match t2: + case Nil{}: + {==} + case p3 <> t3: + match t3: + case Nil{}: + {==} + case p4 <> t4: + match t4: + case Nil{}: + {==} + case p5 <> t5: + match t5: + case Nil{}: + {==} + case p6 <> t6: + match t6: + case Nil{}: + {==} + case p7 <> t7: + Empty.absurd({Jenc.encode.bit.n(U32.is_eq(U32.from_nat(List.length(&2, Bool, + p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7)), 7), out, + vstep(vfold(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7, 0), bb), + U32.from_nat(List.length(&2, Bool, p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> + t7))) == wone(is7(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7), out, + p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7, bb) : Jenc.Put}, + long8(t7, hh)) + +# the writer's bit step: the model's next state +def wbit( + out: List<&2, U32>, + +pp: List<&2, Bool>, + +bb: Bool, + hh: {short(pp) == True{} : Bool} +) -> {Jenc.encode.bit(wst(out, pp), ubit(bb)) == wone(is7(pp), out, pp, bb) : Jenc.Put}: + +nn = U32.from_nat(List.length(&2, Bool, pp)) + +vv = vfold(pp, 0) + Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), U32.and(ubit(bb), 1)), nn), + Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), ubit(bb)), nn), wone(is7(pp), out, pp, bb), + Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), xx), nn), + U32.and(ubit(bb), 1), ubit(bb), and_one(ubit(bb))), + Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), ubit(bb)), nn), + Jenc.encode.bit.n(U32.is_eq(nn, 7), out, vstep(vv, bb), nn), wone(is7(pp), out, pp, bb), + Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bit.n(U32.is_eq(nn, 7), out, xx, nn), U32.or(U32.shl(vv), ubit(bb)), + vstep(vv, bb), or_shl_u(vv, bb)), wbit.p(out, pp, bb, hh))) + +# the encoder's bit writer fed bits one at a time +def feed(bs: List<&2, Bool>, pp: Jenc.Put) -> Jenc.Put: + match bs: + case Nil{}: + pp + case bb <> rest: + feed(rest, Jenc.encode.bit(pp, ubit(bb))) + +# the model writer's state: the finished bytes as their bits (newest first) and the pending bits +type WS is Data: + WS{out: List<&2, List<&2, Bool>>, pp: List<&2, Bool>} + +# bytes from their bits +def obytes(os: List<&2, List<&2, Bool>>) -> List<&2, U32>: + match os: + case Nil{}: + [] + case oo <> rest: + vfold(oo, 0) <> obytes(rest) + +# the encoder's writer the model state stands for +def wput(ws: WS) -> Jenc.Put: + match ws: + case WS{out, pp}: + wst(obytes(out), pp) + +# one model step, by whether the pending bits finish a byte +def wstep.b(full: Bool, out: List<&2, List<&2, Bool>>, pp: List<&2, Bool>, bb: Bool) -> WS: + match full: + case True{}: + WS{List.append(&2, Bool, pp, [bb]) <> out, []} + case False{}: + WS{out, List.append(&2, Bool, pp, [bb])} + +# one model step +def wstep(ws: WS, bb: Bool) -> WS: + match ws: + case WS{out, +pp}: + wstep.b(is7(pp), out, pp, bb) + +# the model writer fed bits +def wm(bs: List<&2, Bool>, ws: WS) -> WS: + match bs: + case Nil{}: + ws + case bb <> rest: + wm(rest, wstep(ws, bb)) + +# the pending bits of a model state are fewer than 8 +def wshort(ws: WS) -> Bool: + match ws: + case WS{_out, pp}: + short(pp) + +# the writer's step and the model's agree, by whether the byte is finished +def wone.eq( + full: Bool, + +out: List<&2, List<&2, Bool>>, + +pp: List<&2, Bool>, + bb: Bool +) -> {wone(full, obytes(out), pp, bb) == wput(wstep.b(full, out, pp, bb)) : Jenc.Put}: + match full: + case True{}: + {==} + case False{}: + {==} + +# one bit on the encoder's writer is one model step +def wbit.ws( + ws: WS, + +bb: Bool, + hh: {wshort(ws) == True{} : Bool} +) -> {Jenc.encode.bit(wput(ws), ubit(bb)) == wput(wstep(ws, bb)) : Jenc.Put}: + match ws: + case WS{+out, +pp}: + Equal.trans(Jenc.Put, Jenc.encode.bit(wst(obytes(out), pp), ubit(bb)), wone(is7(pp), obytes(out), pp, bb), + wput(wstep.b(is7(pp), out, pp, bb)), wbit(obytes(out), pp, bb, hh), wone.eq(is7(pp), out, pp, bb)) + +# after a step the pending bits are still fewer than 8, by the pending bits +def wstep.short.p( + out: List<&2, List<&2, Bool>>, + pp: List<&2, Bool>, + bb: Bool, + hh: {short(pp) == True{} : Bool} +) -> {wshort(wstep.b(is7(pp), out, pp, bb)) == True{} : Bool}: + match pp: + case Nil{}: + {==} + case p0 <> t0: + match t0: + case Nil{}: + {==} + case p1 <> t1: + match t1: + case Nil{}: + {==} + case p2 <> t2: + match t2: + case Nil{}: + {==} + case p3 <> t3: + match t3: + case Nil{}: + {==} + case p4 <> t4: + match t4: + case Nil{}: + {==} + case p5 <> t5: + match t5: + case Nil{}: + {==} + case p6 <> t6: + match t6: + case Nil{}: + {==} + case p7 <> t7: + Empty.absurd({wshort(wstep.b(is7(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7), + out, p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7, bb)) == True{} : Bool}, + long8(t7, hh)) + +# after a step the pending bits are still fewer than 8 +def wstep.short(ws: WS, bb: Bool, hh: {wshort(ws) == True{} : Bool}) -> {wshort(wstep(ws, bb)) == True{} : Bool}: + match ws: + case WS{out, pp}: + wstep.short.p(out, pp, bb, hh) + +# feeding bits to the encoder's writer is the model writer +def wfeed( + bs: List<&2, Bool>, + +ws: WS, + +hh: {wshort(ws) == True{} : Bool} +) -> {feed(bs, wput(ws)) == wput(wm(bs, ws)) : Jenc.Put}: + match bs: + case Nil{}: + {==} + case +bb <> rest: + Equal.trans(Jenc.Put, feed(rest, Jenc.encode.bit(wput(ws), ubit(bb))), feed(rest, wput(wstep(ws, bb))), + wput(wm(rest, wstep(ws, bb))), Equal.cong(Jenc.Put, Jenc.Put, qq => feed(rest, qq), + Jenc.encode.bit(wput(ws), ubit(bb)), wput(wstep(ws, bb)), wbit.ws(ws, bb, hh)), + wfeed(rest, wstep(ws, bb), wstep.short(ws, bb, hh))) + +# the bits the encoder's writer takes from a code, top first: bit k - 1 down to bit 0 +def cb(kk: Nat, +code: U32) -> List<&2, Bool>: + match kk: + case 0n: + [] + case 1n+ +pp: + bit0(U32.shrn(code, pp)) <> cb(pp, code) + +# the encoder's bit loop feeds the code's bits +def bits_go( + kk: Nat, + pp: Jenc.Put, + +code: U32 +) -> {Jenc.encode.bits.go(kk, pp, code) == feed(cb(kk, code), pp) : Jenc.Put}: + match kk: + case 0n: + {==} + case 1n+ +qq: + +bt = U32.shrn(code, qq) + Equal.trans(Jenc.Put, Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, U32.and(bt, 1)), code), + Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, ubit(bit0(bt))), code), feed(cb(1n+qq, code), pp), + Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, xx), code), U32.and(bt, 1), + ubit(bit0(bt)), and_one(bt)), bits_go(qq, Jenc.encode.bit(pp, ubit(bit0(bt))), code)) + +# a code of at most 16 bits, written with its length and value, is its bits fed to the writer +def bits_code( + +cc: List<&2, Bool>, + pp: Jenc.Put, + hh: {Nat.is_le(List.length(&2, Bool, cc), 16n) == True{} : Bool} +) -> {Jenc.encode.bits(pp, U32.from_nat(List.length(&2, Bool, cc)), vfold(cc, 0)) == feed(cc, pp) : Jenc.Put}: + match cc: + case Nil{}: + bits_go(0n, pp, vfold([], 0)) + case +h0 <> t0: + match t0: + case Nil{}: + bits_go(1n, pp, vfold(h0 <> [], 0)) + case +h1 <> t1: + match t1: + case Nil{}: + bits_go(2n, pp, vfold(h0 <> h1 <> [], 0)) + case +h2 <> t2: + match t2: + case Nil{}: + bits_go(3n, pp, vfold(h0 <> h1 <> h2 <> [], 0)) + case +h3 <> t3: + match t3: + case Nil{}: + bits_go(4n, pp, vfold(h0 <> h1 <> h2 <> h3 <> [], 0)) + case +h4 <> t4: + match t4: + case Nil{}: + bits_go(5n, pp, vfold(h0 <> h1 <> h2 <> h3 <> h4 <> [], 0)) + case +h5 <> t5: + match t5: + case Nil{}: + bits_go(6n, pp, vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> [], 0)) + case +h6 <> t6: + match t6: + case Nil{}: + bits_go(7n, pp, vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> [], 0)) + case +h7 <> t7: + match t7: + case Nil{}: + bits_go(8n, pp, vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> [], 0)) + case +h8 <> t8: + match t8: + case Nil{}: + bits_go(9n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> [], 0)) + case +h9 <> t9: + match t9: + case Nil{}: + bits_go(10n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> h9 <> [], + 0)) + case +h10 <> t10: + match t10: + case Nil{}: + bits_go(11n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> h9 <> + h10 <> [], 0)) + case +h11 <> t11: + match t11: + case Nil{}: + bits_go(12n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> h9 + <> h10 <> h11 <> [], 0)) + case +h12 <> t12: + match t12: + case Nil{}: + bits_go(13n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> + h9 <> h10 <> h11 <> h12 <> [], 0)) + case +h13 <> t13: + match t13: + case Nil{}: + bits_go(14n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> + h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> [], 0)) + case +h14 <> t14: + match t14: + case Nil{}: + bits_go(15n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 + <> h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> h14 <> + [], 0)) + case +h15 <> t15: + match t15: + case Nil{}: + bits_go(16n, pp, + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> + h7 <> h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> + h14 <> h15 <> [], 0)) + case _h16 <> _t16: + Empty.absurd({Jenc.encode.bits(pp, + U32.from_nat(List.length(&2, Bool, + h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> + h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> h14 <> + h15 <> _h16 <> _t16)), + vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> + h7 <> h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> + h14 <> h15 <> _h16 <> _t16, + 0)) == feed(h0 <> h1 <> h2 <> h3 <> h4 <> h5 + <> h6 <> h7 <> h8 <> h9 <> h10 <> h11 <> h12 + <> h13 <> h14 <> h15 <> _h16 <> _t16, + pp) : Jenc.Put}, false_true(hh)) + +# 1 or 0 is the bit as a U32 +def bitu_u(bb: Bool) -> {Laws.jpg.bitu(bb) == ubit(bb) : U32}: + match bb: + case True{}: + {==} + case False{}: + {==} + +# a code's value is the model's fold of its bits +def num_v(bs: List<&2, Bool>, +acc: U32) -> {Laws.jpg.num(bs, acc) == vfold(bs, acc) : U32}: + match bs: + case Nil{}: + {==} + case +bb <> rest: + Equal.trans(U32, Laws.jpg.num(rest, U32.or(U32.shl(acc), Laws.jpg.bitu(bb))), Laws.jpg.num(rest, vstep(acc, + bb)), vfold(rest, vstep(acc, bb)), + Equal.cong(U32, U32, xx => Laws.jpg.num(rest, xx), U32.or(U32.shl(acc), Laws.jpg.bitu(bb)), vstep(acc, bb), + Equal.trans(U32, U32.or(U32.shl(acc), Laws.jpg.bitu(bb)), U32.or(U32.shl(acc), ubit(bb)), vstep(acc, bb), + Equal.cong(U32, U32, xx => U32.or(U32.shl(acc), xx), Laws.jpg.bitu(bb), ubit(bb), bitu_u(bb)), or_shl_u(acc, + bb))), + num_v(rest, vstep(acc, bb))) + +# feeding two lists is feeding their join +def feed_app( + xs: List<&2, Bool>, + +ys: List<&2, Bool>, + pp: Jenc.Put +) -> {feed(List.append(&2, Bool, xs, ys), pp) == feed(ys, feed(xs, pp)) : Jenc.Put}: + match xs: + case Nil{}: + {==} + case bb <> rest: + feed_app(rest, ys, Jenc.encode.bit(pp, ubit(bb))) + +# the first answer of a true also +def also_l(ok: Bool, -rest: Bool, hh: {Laws.jpg.also(ok, rest) == True{} : Bool}) -> {ok == True{} : Bool}: + match ok: + case True{}: + {==} + case False{}: + Empty.absurd({False{} == True{} : Bool}, false_true(hh)) + +# the second answer of a true also +def also_r(ok: Bool, -rest: Bool, hh: {Laws.jpg.also(ok, rest) == True{} : Bool}) -> {rest == True{} : Bool}: + match ok: + case True{}: + hh + case False{}: + Empty.absurd({rest == True{} : Bool}, false_true(hh)) + +# codes written one after another are their bits fed in order +def wcodes_feed( + cs: List<&2, List<&2, Bool>>, + +pp: Jenc.Put, + +hh: {Laws.jpg.codes16(cs) == True{} : Bool} +) -> {Laws.jpg.write(cs, pp) == feed(List.concat(&2, Bool, cs), pp) : Jenc.Put}: + match cs: + case Nil{}: + {==} + case +cc <> +rest: + +h1 = {also_l(Nat.is_le(List.length(&2, Bool, cc), 16n), Laws.jpg.codes16(rest), hh) : + {Nat.is_le(List.length(&2, Bool, cc), 16n) == True{} : Bool}} + +nn = U32.from_nat(List.length(&2, Bool, cc)) + Equal.trans(Jenc.Put, Laws.jpg.write(rest, Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0))), Laws.jpg.write(rest, + feed(cc, pp)), + feed(List.concat(&2, Bool, cc <> rest), pp), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(rest, qq), + Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0)), feed(cc, pp), Equal.trans(Jenc.Put, Jenc.encode.bits(pp, nn, + Laws.jpg.num(cc, 0)), + Jenc.encode.bits(pp, nn, vfold(cc, 0)), feed(cc, pp), Equal.cong(U32, Jenc.Put, vv => Jenc.encode.bits(pp, nn, + vv), Laws.jpg.num(cc, 0), vfold(cc, 0), num_v(cc, 0)), bits_code(cc, pp, h1))), + Equal.trans(Jenc.Put, Laws.jpg.write(rest, feed(cc, pp)), feed(List.concat(&2, Bool, rest), feed(cc, pp)), + feed(List.concat(&2, Bool, cc <> rest), pp), wcodes_feed(rest, feed(cc, pp), also_r(Nat.is_le(List.length(&2, + Bool, cc), 16n), Laws.jpg.codes16(rest), hh)), Equal.sym(Jenc.Put, feed(List.concat(&2, Bool, cc <> rest), pp), + feed(List.concat(&2, Bool, rest), feed(cc, pp)), feed_app(cc, List.concat(&2, Bool, rest), pp)))) + +# the last byte's bits: the pending bits, then 1 bits to fill the byte; none when nothing is pending +def pendo(pp: List<&2, Bool>) -> List<&2, List<&2, Bool>>: + match pp: + case Nil{}: + [] + case +hh <> +tt: + [List.append(&2, Bool, hh <> tt, List.replicate(Bool, Nat.sub(8n, List.length(&2, Bool, hh <> tt)), True{}))] + +# the pending bits after one more bit +def mo.next(full: Bool, pp: List<&2, Bool>, bb: Bool) -> List<&2, Bool>: + match full: + case True{}: + +_d = pp + +_b = bb + [] + case False{}: + List.append(&2, Bool, pp, [bb]) + +# the byte one more bit finishes, if it does, before the bytes after it +def mo.join(full: Bool, pp: List<&2, Bool>, bb: Bool, rest: List<&2, List<&2, Bool>>) -> List<&2, List<&2, Bool>>: + match full: + case True{}: + List.append(&2, Bool, pp, [bb]) <> rest + case False{}: + +_d = pp + +_b = bb + rest + +# the bytes, as their bits and in order, the writer finishes from pending bits pp and then bits bs, padded +def moct(bs: List<&2, Bool>, +pp: List<&2, Bool>) -> List<&2, List<&2, Bool>>: + match bs: + case Nil{}: + pendo(pp) + case +bb <> rest: + mo.join(is7(pp), pp, bb, moct(rest, mo.next(is7(pp), pp, bb))) + +# the encoder's pad of a model writer is the finished bytes, then the last byte's, stuffed +def pad_base( + +out: List<&2, List<&2, Bool>>, + pp: List<&2, Bool>, + hh: {short(pp) == True{} : Bool} +) -> {Jenc.encode.pad(wst(obytes(out), pp)) == Jenc.encode.stuff.all(obytes(out), W10.stf(obytes(pendo(pp)), + [])) : List<&2, U32>}: + match pp: + case Nil{}: + {==} + case +p0 <> t0: + match t0: + case Nil{}: + {==} + case +p1 <> t1: + match t1: + case Nil{}: + {==} + case +p2 <> t2: + match t2: + case Nil{}: + {==} + case +p3 <> t3: + match t3: + case Nil{}: + {==} + case +p4 <> t4: + match t4: + case Nil{}: + {==} + case +p5 <> t5: + match t5: + case Nil{}: + {==} + case +p6 <> t6: + match t6: + case Nil{}: + {==} + case +p7 <> t7: + Empty.absurd({Jenc.encode.pad(wst(obytes(out), + p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7)) == + Jenc.encode.stuff.all(obytes(out), + W10.stf(obytes(pendo(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7)), + [])) : List<&2, U32>}, long8(t7, hh)) + +# after one more bit the pending bits are still fewer than 8 +def next_short( + pp: List<&2, Bool>, + bb: Bool, + hh: {short(pp) == True{} : Bool} +) -> {short(mo.next(is7(pp), pp, bb)) == True{} : Bool}: + match pp: + case Nil{}: + {==} + case p0 <> t0: + match t0: + case Nil{}: + {==} + case p1 <> t1: + match t1: + case Nil{}: + {==} + case p2 <> t2: + match t2: + case Nil{}: + {==} + case p3 <> t3: + match t3: + case Nil{}: + {==} + case p4 <> t4: + match t4: + case Nil{}: + {==} + case p5 <> t5: + match t5: + case Nil{}: + {==} + case p6 <> t6: + match t6: + case Nil{}: + {==} + case p7 <> t7: + Empty.absurd({short(mo.next(is7(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7), + p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7, bb)) == True{} : Bool}, + long8(t7, hh)) + +# the finished bytes after one more bit +def wo(full: Bool, out: List<&2, List<&2, Bool>>, pp: List<&2, Bool>, bb: Bool) -> List<&2, List<&2, Bool>>: + match full: + case True{}: + List.append(&2, Bool, pp, [bb]) <> out + case False{}: + +_d = pp + +_b = bb + out + +# one model step, as its bytes and pending bits +def wstep.parts( + full: Bool, + +out: List<&2, List<&2, Bool>>, + +pp: List<&2, Bool>, + +bb: Bool +) -> {wstep.b(full, out, pp, bb) == WS{wo(full, out, pp, bb), mo.next(full, pp, bb)} : WS}: + match full: + case True{}: + {==} + case False{}: + {==} + +# a finished byte moves from the writer's bytes to the stuffed ones after them +def join.stuff( + full: Bool, + +out: List<&2, List<&2, Bool>>, + +pp: List<&2, Bool>, + +bb: Bool, + +rest: List<&2, List<&2, Bool>> +) -> {Jenc.encode.stuff.all(obytes(wo(full, out, pp, bb)), W10.stf(obytes(rest), + [])) == Jenc.encode.stuff.all(obytes(out), W10.stf(obytes(mo.join(full, pp, bb, rest)), [])) : List<&2, U32>}: + match full: + case True{}: + {==} + case False{}: + {==} + +# the encoder's pad of the model writer after bits bs is the model's bytes, stuffed +def pad_m( + bs: List<&2, Bool>, + +out: List<&2, List<&2, Bool>>, + +pp: List<&2, Bool>, + +hh: {short(pp) == True{} : Bool} +) -> {Jenc.encode.pad(wput(wm(bs, WS{out, pp}))) == Jenc.encode.stuff.all(obytes(out), W10.stf(obytes(moct(bs, pp)), + [])) : List<&2, U32>}: + match bs: + case Nil{}: + pad_base(out, pp, hh) + case +bb <> +rest: + +fl = is7(pp) + +o2 = wo(fl, out, pp, bb) + +p2 = mo.next(fl, pp, bb) + Equal.trans(List<&2, U32>, Jenc.encode.pad(wput(wm(rest, wstep.b(fl, out, pp, bb)))), + Jenc.encode.pad(wput(wm(rest, WS{o2, p2}))), Jenc.encode.stuff.all(obytes(out), W10.stf(obytes(moct(bb <> rest, + pp)), [])), Equal.cong(WS, List<&2, U32>, ws => Jenc.encode.pad(wput(wm(rest, ws))), wstep.b(fl, out, pp, bb), + WS{o2, p2}, wstep.parts(fl, out, pp, bb)), + Equal.trans(List<&2, U32>, Jenc.encode.pad(wput(wm(rest, WS{o2, p2}))), Jenc.encode.stuff.all(obytes(o2), + W10.stf(obytes(moct(rest, p2)), [])), Jenc.encode.stuff.all(obytes(out), W10.stf(obytes(moct(bb <> rest, pp)), + [])), + pad_m(rest, o2, p2, next_short(pp, bb, hh)), join.stuff(fl, out, pp, bb, moct(rest, p2)))) + +# the reader after taking the first bit of a fresh byte bo: seven bits left, the bit shifted into acc +def rfresh(left: Nat, +bo: U32, +rest: List<&2, U32>, +ok: U32, +acc: U32) -> Jpeg.Bits & U32: + Jpeg.decode.nbits(left, False{}, rest, False{}, Jpeg.decode.head.is(rest, 255), Jpeg.decode.head.is(rest, 0), 7, + U32.and(255, U32.shl(bo)), ok, U32.or(U32.shrn(bo, 7n), U32.shl(acc))) + +# the bit reader with an empty buffer takes the next byte, stuffed or not +def rbyte( + zz: Bool, + +left: Nat, + +bo: U32, + +rest: List<&2, U32>, + +ok: U32, + +acc: U32, + +ee: {U32.is_eq(bo, 255) == zz : Bool} +) -> {Jpeg.decode.nbits(1n+left, True{}, Jenc.encode.stuff.put(zz, bo, rest), False{}, + Jpeg.decode.head.is(Jenc.encode.stuff.put(zz, bo, rest), 255), Jpeg.decode.head.is(Jenc.encode.stuff.put(zz, bo, + rest), 0), 0, 0, ok, acc) == rfresh(left, bo, rest, ok, acc) : Jpeg.Bits & U32}: + match zz: + case False{}: + Equal.cong(Bool, Jpeg.Bits & U32, hb => Jpeg.decode.nbits(1n+left, True{}, bo <> rest, False{}, hb, + U32.is_eq(bo, 0), 0, 0, ok, acc), U32.is_eq(bo, 255), False{}, ee) + case True{}: + eb = Equal.sym(U32, bo, 255, U32L.ueq(bo, 255, ee)) + %eb : {Jpeg.decode.nbits(1n+left, True{}, _ <> (0 <> rest), False{}, Jpeg.decode.head.is(_ <> (0 <> rest), 255), + Jpeg.decode.head.is(_ <> (0 <> rest), 0), 0, 0, ok, acc) == rfresh(left, _, rest, ok, acc) : Jpeg.Bits & U32} + {==} + +# the reader's model: the bits left in its buffer, and the bytes after them as their bits +type RS is Data: + RS{qq: List<&2, Bool>, os: List<&2, List<&2, Bool>>} + +# the reader's buffer holding bits qq: the first at bit 7, the rest below it, zeros under them +def rbuf(+qq: List<&2, Bool>) -> U32: + U32.shln(vfold(qq, 0), Nat.sub(8n, List.length(&2, Bool, qq))) + +# the stuffed bytes of a model reader +def rxs(os: List<&2, List<&2, Bool>>) -> List<&2, U32>: + W10.stf(obytes(os), []) + +# the decoder's bit reader the model stands for +def rbits(ss: RS, +ok: U32) -> Jpeg.Bits: + match ss: + case RS{+qq, os}: + Jpeg.Bits{U32.from_nat(List.length(&2, Bool, qq)), ok, rbuf(qq), rxs(os)} + +# the bit reader's loop reading left more bits onto acc from the model's reader +def canon(left: Nat, ss: RS, +ok: U32, +acc: U32) -> Jpeg.Bits & U32: + match ss: + case RS{+qq, +os}: + +nn = U32.from_nat(List.length(&2, Bool, qq)) + Jpeg.decode.nbits(left, U32.is_eq(nn, 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), + Jpeg.decode.head.is(rxs(os), 0), nn, rbuf(qq), ok, acc) + +# the bits the model reader has left, in order +def rem(ss: RS) -> List<&2, Bool>: + match ss: + case RS{qq, os}: + List.append(&2, Bool, qq, List.concat(&2, Bool, os)) + +# the next bit a model reader reads (False when it has none) +def rhead(ss: RS) -> Bool: + match ss: + case RS{qq, os}: + match qq: + case q0 <> _qt: + +_o = os + q0 + case Nil{}: + match os: + case oo <> _ot: + match oo: + case o0 <> _t: + o0 + case Nil{}: + False{} + case Nil{}: + False{} + +# the model reader after one bit +def rnext(ss: RS) -> RS: + match ss: + case RS{qq, os}: + match qq: + case _q0 <> qt: + RS{qt, os} + case Nil{}: + match os: + case oo <> ot: + match oo: + case _o0 <> tt: + RS{tt, ot} + case Nil{}: + RS{[], ot} + case Nil{}: + RS{[], []} + +# every byte of a model reader has 8 bits +def oct8(os: List<&2, List<&2, Bool>>) -> Bool: + match os: + case Nil{}: + True{} + case oo <> rest: + Laws.jpg.also(Nat.is_eq(List.length(&2, Bool, oo), 8n), oct8(rest)) + +# one bit read from a buffer holding bits +def rstep.q( + +left: Nat, + +q0: Bool, + t0: List<&2, Bool>, + +os: List<&2, List<&2, Bool>>, + +ok: U32, + +acc: U32, + hs: {short(q0 <> t0) == True{} : Bool} +) -> {canon(1n+left, RS{q0 <> t0, os}, ok, acc) == canon(left, rnext(RS{q0 <> t0, os}), ok, vstep(acc, + rhead(RS{q0 <> t0, os}))) : Jpeg.Bits & U32}: + match t0: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, U32.is_eq(U32.from_nat(List.length(&2, Bool, + [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, [])), rbuf([]), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q1 <> t1: + match t1: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, U32.is_eq(U32.from_nat(List.length(&2, Bool, + q1 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> [])), rbuf(q1 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q2 <> t2: + match t2: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, U32.is_eq(U32.from_nat(List.length(&2, + Bool, + q1 <> q2 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), + Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> [])), rbuf(q1 <> q2 <> []), ok, xx), U32.or(ubit(q0), + U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q3 <> t3: + match t3: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, + U32.is_eq(U32.from_nat(List.length(&2, Bool, + q1 <> q2 <> q3 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), + Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> [])), rbuf(q1 <> q2 <> q3 <> []), ok, xx), + U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q4 <> t4: + match t4: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, + U32.is_eq(U32.from_nat(List.length(&2, Bool, + q1 <> q2 <> q3 <> q4 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), + Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> [])), + rbuf(q1 <> q2 <> q3 <> q4 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q5 <> t5: + match t5: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, + U32.is_eq(U32.from_nat(List.length(&2, Bool, + q1 <> q2 <> q3 <> q4 <> q5 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), + 255), Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> q5 <> [])), + rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case +q6 <> t6: + match t6: + case Nil{}: + Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, + U32.is_eq(U32.from_nat(List.length(&2, Bool, + q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> [])), 0), rxs(os), False{}, + Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> [])), + rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + vstep(acc, q0), or_u_shl(acc, q0)) + case _q7 <> t7: + Empty.absurd({canon(1n+left, RS{q0 <> q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> _q7 <> t7, + os}, ok, acc) == canon(left, + rnext(RS{q0 <> q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> _q7 <> t7, os}), ok, vstep(acc, + rhead(RS{q0 <> q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> _q7 <> t7, + os}))) : Jpeg.Bits & U32}, long8(t7, hs)) + +# one bit read with the buffer empty: the next byte's first bit +def rstep.o( + +left: Nat, + oo: List<&2, Bool>, + +ot: List<&2, List<&2, Bool>>, + +ok: U32, + +acc: U32, + h8: {Nat.is_eq(List.length(&2, Bool, oo), 8n) == True{} : Bool} +) -> {canon(1n+left, RS{[], oo <> ot}, ok, acc) == canon(left, rnext(RS{[], oo <> ot}), ok, vstep(acc, rhead(RS{[], + oo <> ot}))) : Jpeg.Bits & U32}: + match oo: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], ([]) <> ot}, ok, acc) == canon(left, rnext(RS{[], ([]) <> ot}), ok, + vstep(acc, rhead(RS{[], ([]) <> ot}))) : Jpeg.Bits & U32}, false_true(h8)) + case +o0 <> t0: + match t0: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> []) <> ot}, ok, acc) == canon(left, rnext(RS{[], + (o0 <> []) <> ot}), ok, vstep(acc, rhead(RS{[], (o0 <> []) <> ot}))) : Jpeg.Bits & U32}, false_true(h8)) + case +o1 <> t1: + match t1: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> []) <> ot}, ok, acc) == canon(left, rnext(RS{[], + (o0 <> o1 <> []) <> ot}), ok, vstep(acc, rhead(RS{[], (o0 <> o1 <> []) <> ot}))) : Jpeg.Bits & U32}, + false_true(h8)) + case +o2 <> t2: + match t2: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> o2 <> []) <> ot}, ok, acc) == canon(left, + rnext(RS{[], (o0 <> o1 <> o2 <> []) <> ot}), ok, vstep(acc, rhead(RS{[], + (o0 <> o1 <> o2 <> []) <> ot}))) : Jpeg.Bits & U32}, false_true(h8)) + case +o3 <> t3: + match t3: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> o2 <> o3 <> []) <> ot}, ok, acc) == canon(left, + rnext(RS{[], (o0 <> o1 <> o2 <> o3 <> []) <> ot}), ok, vstep(acc, rhead(RS{[], + (o0 <> o1 <> o2 <> o3 <> []) <> ot}))) : Jpeg.Bits & U32}, false_true(h8)) + case +o4 <> t4: + match t4: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> []) <> ot}, ok, + acc) == canon(left, rnext(RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> []) <> ot}), ok, + vstep(acc, rhead(RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> []) <> ot}))) : Jpeg.Bits & U32}, + false_true(h8)) + case +o5 <> t5: + match t5: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> []) <> ot}, ok, + acc) == canon(left, rnext(RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> []) <> ot}), ok, + vstep(acc, rhead(RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> []) <> ot}))) : Jpeg.Bits & U32}, false_true(h8)) + case +o6 <> t6: + match t6: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> []) <> ot}, ok, acc) == canon(left, + rnext(RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> []) <> ot}), ok, + vstep(acc, rhead(RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> []) <> ot}))) : Jpeg.Bits & U32}, + false_true(h8)) + case +o7 <> t7: + match t7: + case Nil{}: + Equal.trans(Jpeg.Bits & U32, canon(1n+left, RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> []) <> ot}, ok, acc), + rfresh(left, vfold(o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> [], 0), + rxs(ot), ok, acc), + canon(left, rnext(RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> []) <> ot}), ok, vstep(acc, + rhead(RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> []) <> ot}))), + rbyte(U32.is_eq(vfold(o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> [], + 0), 255), left, vfold(o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> [], 0), + rxs(ot), ok, acc, {==}), Equal.cong(U32, Jpeg.Bits & U32, + xx => Jpeg.decode.nbits(left, False{}, rxs(ot), + False{}, Jpeg.decode.head.is(rxs(ot), 255), Jpeg.decode.head.is(rxs(ot), 0), + 7, rbuf(o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> []), ok, xx), + U32.or(ubit(o0), U32.shl(acc)), vstep(acc, o0), or_u_shl(acc, o0))) + case _o8 <> t8: + Empty.absurd({canon(1n+left, RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> _o8 <> t8) <> ot}, ok, + acc) == canon(left, rnext(RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> _o8 <> t8) <> ot}), ok, + vstep(acc, rhead(RS{[], + (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> _o8 <> t8) <> ot}))) : + Jpeg.Bits & U32}, false_true(h8)) + +# a model reader's buffer holds fewer than 8 bits +def rshort(ss: RS) -> Bool: + match ss: + case RS{qq, _os}: + short(qq) + +# every byte after a model reader's buffer has 8 bits +def roct8(ss: RS) -> Bool: + match ss: + case RS{_qq, os}: + oct8(os) + +# a model reader with a bit left +def rsome(ss: RS) -> Bool: + Nat.is_lt(0n, List.length(&2, Bool, rem(ss))) + +# one bit read from the model reader: its next bit shifted into acc, the reader after it +def rstep( + +left: Nat, + ss: RS, + +ok: U32, + +acc: U32, + hs: {rshort(ss) == True{} : Bool}, + h8: {roct8(ss) == True{} : Bool}, + hn: {rsome(ss) == True{} : Bool} +) -> {canon(1n+left, ss, ok, acc) == canon(left, rnext(ss), ok, vstep(acc, rhead(ss))) : Jpeg.Bits & U32}: + match ss: + case RS{qq, os}: + match qq: + case +q0 <> t0: + rstep.q(left, q0, t0, os, ok, acc, hs) + case Nil{}: + match os: + case Nil{}: + Empty.absurd({canon(1n+left, RS{[], []}, ok, acc) == canon(left, rnext(RS{[], []}), ok, vstep(acc, + rhead(RS{[], []}))) : Jpeg.Bits & U32}, false_true(hn)) + case +oo <> +ot: + rstep.o(left, oo, ot, ok, acc, also_l(Nat.is_eq(List.length(&2, Bool, oo), 8n), oct8(ot), h8)) + +# below b after one more is below b +def lt_succ_l(aa: Nat, bb: Nat, hh: {Nat.is_lt(1n+aa, bb) == True{} : Bool}) -> {Nat.is_lt(aa, bb) == True{} : Bool}: + match aa bb: + case 0n 0n: + Empty.absurd({Nat.is_lt(0n, 0n) == True{} : Bool}, false_true(hh)) + case 0n 1n+_q: + {==} + case 1n+_p 0n: + Empty.absurd({Nat.is_lt(1n+_p, 0n) == True{} : Bool}, false_true(hh)) + case 1n+pp 1n+qq: + lt_succ_l(pp, qq, hh) + +# a number equal to b + 1 less one is below b + 1 +def eq_lt_succ(aa: Nat, bb: Nat, hh: {Nat.is_eq(1n+aa, bb) == True{} : Bool}) -> {Nat.is_lt(aa, bb) == True{} : Bool}: + match aa bb: + case 0n 0n: + Empty.absurd({Nat.is_lt(0n, 0n) == True{} : Bool}, false_true(hh)) + case 0n 1n+_q: + {==} + case 1n+_p 0n: + Empty.absurd({Nat.is_lt(1n+_p, 0n) == True{} : Bool}, false_true(hh)) + case 1n+pp 1n+qq: + eq_lt_succ(pp, qq, hh) + +# m + 1 at most l says l is above 0 +def le_pos(mm: Nat, ll: Nat, hh: {Nat.is_le(1n+mm, ll) == True{} : Bool}) -> {Nat.is_lt(0n, ll) == True{} : Bool}: + match ll: + case 0n: + Empty.absurd({Nat.is_lt(0n, 0n) == True{} : Bool}, false_true(hh)) + case 1n+_q: + {==} + +# the model reader after a bit still holds fewer than 8 bits +def rnext_short( + ss: RS, + hs: {rshort(ss) == True{} : Bool}, + h8: {roct8(ss) == True{} : Bool} +) -> {rshort(rnext(ss)) == True{} : Bool}: + match ss: + case RS{qq, os}: + match qq: + case _q0 <> +qt: + lt_succ_l(List.length(&2, Bool, qt), 8n, hs) + case Nil{}: + match os: + case Nil{}: + {==} + case oo <> +ot: + match oo: + case Nil{}: + {==} + case _o0 <> +tt: + eq_lt_succ(List.length(&2, Bool, tt), 8n, also_l(Nat.is_eq(1n+List.length(&2, Bool, tt), 8n), + oct8(ot), h8)) + +# the model reader's bytes after a bit still have 8 bits each +def rnext_oct8(ss: RS, h8: {roct8(ss) == True{} : Bool}) -> {roct8(rnext(ss)) == True{} : Bool}: + match ss: + case RS{qq, os}: + match qq: + case _q0 <> _qt: + h8 + case Nil{}: + match os: + case Nil{}: + {==} + case oo <> +ot: + match oo: + case Nil{}: + also_r(Nat.is_eq(0n, 8n), oct8(ot), h8) + case _o0 <> +tt: + also_r(Nat.is_eq(1n+List.length(&2, Bool, tt), 8n), oct8(ot), h8) + +# the model reader's bits: the next bit, then the bits after it +def rem_cons( + ss: RS, + h8: {roct8(ss) == True{} : Bool}, + hn: {rsome(ss) == True{} : Bool} +) -> {rem(ss) == rhead(ss) <> rem(rnext(ss)) : List<&2, Bool>}: + match ss: + case RS{qq, os}: + match qq: + case _q0 <> _qt: + {==} + case Nil{}: + match os: + case Nil{}: + Empty.absurd({rem(RS{[], []}) == rhead(RS{[], []}) <> rem(rnext(RS{[], []})) : List<&2, Bool>}, + false_true(hn)) + case oo <> +ot: + match oo: + case Nil{}: + Empty.absurd({rem(RS{[], [] <> ot}) == rhead(RS{[], [] <> ot}) <> rem(rnext(RS{[], [] <> ot})) : + List<&2, Bool>}, false_true(also_l(Nat.is_eq(0n, 8n), oct8(ot), h8))) + case _o0 <> _tt: + {==} + +# the model reader after m bits +def radv(mm: Nat, ss: RS) -> RS: + match mm: + case 0n: + ss + case 1n+pp: + radv(pp, rnext(ss)) + +# nothing taken is nothing +def take0(xs: List<&2, Bool>) -> {List.take(&2, Bool, xs, 0n) == [] : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case _h <> _t: + {==} + +# nothing dropped is the list +def drop0(xs: List<&2, Bool>) -> {List.drop(&2, Bool, xs, 0n) == xs : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case _h <> _t: + {==} + +# the bit reader's loop, m bits from a model reader: the reader m bits on, and those bits folded onto acc +def rread( + mm: Nat, + +ss: RS, + +ok: U32, + +acc: U32, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +hl: {Nat.is_le(mm, List.length(&2, Bool, rem(ss))) == True{} : Bool} +) -> {canon(mm, ss, ok, acc) == (rbits(radv(mm, ss), ok), vfold(List.take(&2, Bool, rem(ss), mm), + acc)) : Jpeg.Bits & U32}: + match mm: + case 0n: + match ss: + case RS{+qq, +os}: + Equal.cong(List<&2, Bool>, Jpeg.Bits & U32, tk => (rbits(RS{qq, os}, ok), vfold(tk, acc)), [], + List.take(&2, Bool, rem(RS{qq, os}), 0n), Equal.sym(List<&2, Bool>, List.take(&2, Bool, rem(RS{qq, os}), + 0n), [], take0(rem(RS{qq, os})))) + case 1n+ +pp: + +hn = {le_pos(pp, List.length(&2, Bool, rem(ss)), hl) : {rsome(ss) == True{} : Bool}} + +nx = rnext(ss) + +er = {rem_cons(ss, h8, hn) : {rem(ss) == rhead(ss) <> rem(nx) : List<&2, Bool>}} + +h2 = {Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(1n+pp, List.length(&2, Bool, xs)), rem(ss), + rhead(ss) <> rem(nx), er) : {Nat.is_le(1n+pp, List.length(&2, Bool, rem(ss))) == Nat.is_le(pp, + List.length(&2, Bool, rem(nx))) : Bool}} + Equal.trans(Jpeg.Bits & U32, canon(1n+pp, ss, ok, acc), canon(pp, nx, ok, vstep(acc, rhead(ss))), + (rbits(radv(1n+pp, ss), ok), vfold(List.take(&2, Bool, rem(ss), 1n+pp), acc)), + rstep(pp, ss, ok, acc, hs, h8, hn), + Equal.trans(Jpeg.Bits & U32, canon(pp, nx, ok, vstep(acc, rhead(ss))), (rbits(radv(pp, nx), ok), + vfold(List.take(&2, Bool, rem(nx), pp), vstep(acc, rhead(ss)))), (rbits(radv(1n+pp, ss), ok), + vfold(List.take(&2, Bool, rem(ss), 1n+pp), acc)), + rread(pp, nx, ok, vstep(acc, rhead(ss)), rnext_short(ss, hs, h8), rnext_oct8(ss, h8), + Equal.trans(Bool, Nat.is_le(pp, List.length(&2, Bool, rem(nx))), Nat.is_le(1n+pp, List.length(&2, Bool, + rem(ss))), True{}, Equal.sym(Bool, Nat.is_le(1n+pp, List.length(&2, Bool, rem(ss))), Nat.is_le(pp, + List.length(&2, Bool, rem(nx))), h2), hl)), + Equal.cong(List<&2, Bool>, Jpeg.Bits & U32, xs => (rbits(radv(pp, nx), ok), vfold(List.take(&2, Bool, xs, + 1n+pp), acc)), rhead(ss) <> rem(nx), rem(ss), Equal.sym(List<&2, Bool>, rem(ss), rhead(ss) <> rem(nx), er)))) + +# the model reader m bits on still holds fewer than 8 bits and its bytes have 8 bits each +def radv_ok( + mm: Nat, + +ss: RS, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool} +) -> {Bool.and(rshort(radv(mm, ss)), roct8(radv(mm, ss))) == True{} : Bool}: + match mm: + case 0n: + es = Equal.sym(Bool, rshort(ss), True{}, hs) + %es : {Bool.and(_, roct8(ss)) == True{} : Bool} + h8 + case 1n+pp: + radv_ok(pp, rnext(ss), rnext_short(ss, hs, h8), rnext_oct8(ss, h8)) + +# the model reader's bits m bits on are its bits with m dropped +def rem_radv( + mm: Nat, + +ss: RS, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +hl: {Nat.is_le(mm, List.length(&2, Bool, rem(ss))) == True{} : Bool} +) -> {rem(radv(mm, ss)) == List.drop(&2, Bool, rem(ss), mm) : List<&2, Bool>}: + match mm: + case 0n: + match ss: + case RS{+qq, +os}: + Equal.sym(List<&2, Bool>, List.drop(&2, Bool, rem(RS{qq, os}), 0n), rem(RS{qq, os}), drop0(rem(RS{qq, os}))) + case 1n+ +pp: + +hn = {le_pos(pp, List.length(&2, Bool, rem(ss)), hl) : {rsome(ss) == True{} : Bool}} + +nx = rnext(ss) + +er = {rem_cons(ss, h8, hn) : {rem(ss) == rhead(ss) <> rem(nx) : List<&2, Bool>}} + +h2 = {Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(1n+pp, List.length(&2, Bool, xs)), rem(ss), + rhead(ss) <> rem(nx), er) : {Nat.is_le(1n+pp, List.length(&2, Bool, rem(ss))) == Nat.is_le(pp, + List.length(&2, Bool, rem(nx))) : Bool}} + Equal.trans(List<&2, Bool>, rem(radv(pp, nx)), List.drop(&2, Bool, rem(nx), pp), List.drop(&2, Bool, rem(ss), + 1n+pp), rem_radv(pp, nx, rnext_short(ss, hs, h8), rnext_oct8(ss, h8), Equal.trans(Bool, Nat.is_le(pp, + List.length(&2, Bool, rem(nx))), Nat.is_le(1n+pp, List.length(&2, Bool, rem(ss))), True{}, Equal.sym(Bool, + Nat.is_le(1n+pp, List.length(&2, Bool, rem(ss))), Nat.is_le(pp, List.length(&2, Bool, rem(nx))), h2), hl)), + Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.drop(&2, Bool, xs, 1n+pp), rhead(ss) <> rem(nx), rem(ss), + Equal.sym(List<&2, Bool>, rem(ss), rhead(ss) <> rem(nx), er))) + +# zero is at most every number +def le0(nn: Nat) -> {Nat.is_le(0n, nn) == True{} : Bool}: + match nn: + case 0n: + {==} + case 1n+_p: + {==} + +# a length up to 16 is its own U32 +def tn16(nn: Nat, hh: {Nat.is_le(nn, 16n) == True{} : Bool}) -> {U32.to_nat(U32.from_nat(nn)) == nn : Nat}: + match nn: + case 0n: + {==} + case 1n+n0: + match n0: + case 0n: + {==} + case 1n+n1: + match n1: + case 0n: + {==} + case 1n+n2: + match n2: + case 0n: + {==} + case 1n+n3: + match n3: + case 0n: + {==} + case 1n+n4: + match n4: + case 0n: + {==} + case 1n+n5: + match n5: + case 0n: + {==} + case 1n+n6: + match n6: + case 0n: + {==} + case 1n+n7: + match n7: + case 0n: + {==} + case 1n+n8: + match n8: + case 0n: + {==} + case 1n+n9: + match n9: + case 0n: + {==} + case 1n+n10: + match n10: + case 0n: + {==} + case 1n+n11: + match n11: + case 0n: + {==} + case 1n+n12: + match n12: + case 0n: + {==} + case 1n+n13: + match n13: + case 0n: + {==} + case 1n+n14: + match n14: + case 0n: + {==} + case 1n+n15: + match n15: + case 0n: + {==} + case 1n+_n16: + Empty.absurd({U32.to_nat(U32.from_nat(nn)) == nn + : Nat}, false_true(hh)) + +# a code's own bits are what taking its length from it and more gives +def take_app( + cc: List<&2, Bool>, + +xs: List<&2, Bool> +) -> {List.take(&2, Bool, List.append(&2, Bool, cc, xs), List.length(&2, Bool, cc)) == cc : List<&2, Bool>}: + match cc: + case Nil{}: + take0(xs) + case +hh <> tt: + Equal.cong(List<&2, Bool>, List<&2, Bool>, ys => hh <> ys, List.take(&2, Bool, List.append(&2, Bool, tt, xs), + List.length(&2, Bool, tt)), tt, take_app(tt, xs)) + +# dropping a code's length from it and more leaves the more +def drop_app( + cc: List<&2, Bool>, + +xs: List<&2, Bool> +) -> {List.drop(&2, Bool, List.append(&2, Bool, cc, xs), List.length(&2, Bool, cc)) == xs : List<&2, Bool>}: + match cc: + case Nil{}: + drop0(xs) + case _hh <> tt: + drop_app(tt, xs) + +# a code is no longer than it and more +def len_app( + cc: List<&2, Bool>, + xs: List<&2, Bool> +) -> {Nat.is_le(List.length(&2, Bool, cc), List.length(&2, Bool, List.append(&2, Bool, cc, xs))) == True{} : Bool}: + match cc: + case Nil{}: + le0(List.length(&2, Bool, xs)) + case _hh <> tt: + len_app(tt, xs) + +# joining is associative +def app_assoc( + xs: List<&2, Bool>, + +ys: List<&2, Bool>, + +zs: List<&2, Bool> +) -> {List.append(&2, Bool, List.append(&2, Bool, xs, ys), zs) == List.append(&2, Bool, xs, List.append(&2, Bool, ys, + zs)) : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case +hh <> tt: + Equal.cong(List<&2, Bool>, List<&2, Bool>, ws => hh <> ws, List.append(&2, Bool, List.append(&2, Bool, tt, ys), + zs), List.append(&2, Bool, tt, List.append(&2, Bool, ys, zs)), app_assoc(tt, ys, zs)) + +# the left of a true and +def and_l(aa: Bool, -bb: Bool, hh: {Bool.and(aa, bb) == True{} : Bool}) -> {aa == True{} : Bool}: + match aa: + case True{}: + {==} + case False{}: + Empty.absurd({False{} == True{} : Bool}, false_true(hh)) + +# the right of a true and +def and_r(aa: Bool, -bb: Bool, hh: {Bool.and(aa, bb) == True{} : Bool}) -> {bb == True{} : Bool}: + match aa: + case True{}: + hh + case False{}: + Empty.absurd({bb == True{} : Bool}, false_true(hh)) + +# a read of n bits from the model's reader is the bit reader's loop +def read_canon( + +nn: U32, + ss: RS, + +ok: U32 +) -> {Jpeg.decode.read.n(nn, rbits(ss, ok)) == canon(U32.to_nat(nn), ss, ok, 0) : Jpeg.Bits & U32}: + match ss: + case RS{_qq, _os}: + {==} + +# the model reader over codes then more bits reads each code's value back, its ok flag kept +def reads_m( + cs: List<&2, List<&2, Bool>>, + +ss: RS, + +ok: U32, + +tl: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +hc: {Laws.jpg.codes16(cs) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, List.concat(&2, Bool, cs), tl) : List<&2, Bool>} +) -> {Laws.jpg.reads(cs, rbits(ss, ok)) == (Laws.jpg.nums(cs), ok) : List<&2, U32> & U32}: + match cs: + case Nil{}: + match ss: + case RS{_qq, _os}: + {==} + case +cc <> +rest: + +ln = List.length(&2, Bool, cc) + +nn = U32.from_nat(ln) + +mr = List.append(&2, Bool, List.concat(&2, Bool, rest), tl) + +h16 = {also_l(Nat.is_le(ln, 16n), Laws.jpg.codes16(rest), hc) : {Nat.is_le(ln, 16n) == True{} : Bool}} + +ea = {Equal.trans(List<&2, Bool>, rem(ss), List.append(&2, Bool, List.concat(&2, Bool, cc <> rest), tl), + List.append(&2, Bool, cc, mr), he, app_assoc(cc, List.concat(&2, Bool, rest), tl)) : + {rem(ss) == List.append(&2, Bool, cc, mr) : List<&2, Bool>}} + +hl = {Equal.trans(Bool, Nat.is_le(ln, List.length(&2, Bool, rem(ss))), Nat.is_le(ln, List.length(&2, Bool, + List.append(&2, Bool, cc, mr))), True{}, Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(ln, List.length(&2, + Bool, xs)), rem(ss), List.append(&2, Bool, cc, mr), ea), len_app(cc, mr)) : + {Nat.is_le(ln, List.length(&2, Bool, rem(ss))) == True{} : Bool}} + +s2 = radv(ln, ss) + +ok2 = {radv_ok(ln, ss, hs, h8) : {Bool.and(rshort(s2), roct8(s2)) == True{} : Bool}} + +e2 = {Equal.trans(List<&2, Bool>, rem(s2), List.drop(&2, Bool, rem(ss), ln), mr, rem_radv(ln, ss, hs, h8, hl), + Equal.trans(List<&2, Bool>, List.drop(&2, Bool, rem(ss), ln), List.drop(&2, Bool, List.append(&2, Bool, cc, mr), + ln), mr, Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.drop(&2, Bool, xs, ln), rem(ss), + List.append(&2, Bool, cc, mr), ea), drop_app(cc, mr))) : {rem(s2) == mr : List<&2, Bool>}} + +vr = {Equal.trans(U32, vfold(List.take(&2, Bool, rem(ss), ln), 0), vfold(List.take(&2, Bool, List.append(&2, + Bool, cc, mr), ln), 0), Laws.jpg.num(cc, 0), Equal.cong(List<&2, Bool>, U32, xs => vfold(List.take(&2, Bool, + xs, ln), 0), + rem(ss), List.append(&2, Bool, cc, mr), ea), Equal.trans(U32, vfold(List.take(&2, Bool, List.append(&2, Bool, + cc, mr), ln), 0), vfold(cc, 0), Laws.jpg.num(cc, 0), Equal.cong(List<&2, Bool>, U32, xs => vfold(xs, 0), + List.take(&2, Bool, List.append(&2, Bool, cc, mr), ln), cc, take_app(cc, mr)), Equal.sym(U32, Laws.jpg.num(cc, + 0), + vfold(cc, 0), num_v(cc, 0)))) : {vfold(List.take(&2, Bool, rem(ss), ln), 0) == Laws.jpg.num(cc, 0) : U32}} + +er = {Equal.trans(Jpeg.Bits & U32, Jpeg.decode.read.n(nn, rbits(ss, ok)), canon(U32.to_nat(nn), ss, ok, 0), + (rbits(s2, ok), Laws.jpg.num(cc, 0)), read_canon(nn, ss, ok), Equal.trans(Jpeg.Bits & U32, + canon(U32.to_nat(nn), ss, + ok, 0), canon(ln, ss, ok, 0), (rbits(s2, ok), Laws.jpg.num(cc, 0)), Equal.cong(Nat, Jpeg.Bits & U32, + kk => canon(kk, ss, + ok, 0), U32.to_nat(nn), ln, tn16(ln, h16)), Equal.trans(Jpeg.Bits & U32, canon(ln, ss, ok, 0), (rbits(s2, ok), + vfold(List.take(&2, Bool, rem(ss), ln), 0)), (rbits(s2, ok), Laws.jpg.num(cc, 0)), rread(ln, ss, ok, 0, hs, + h8, hl), + Equal.cong(U32, Jpeg.Bits & U32, vv => (rbits(s2, ok), vv), vfold(List.take(&2, Bool, rem(ss), ln), 0), + Laws.jpg.num(cc, 0), vr)))) : {Jpeg.decode.read.n(nn, rbits(ss, ok)) == (rbits(s2, ok), Laws.jpg.num(cc, + 0)) : Jpeg.Bits & U32}} + Equal.trans(List<&2, U32> & U32, Laws.jpg.rcons(Laws.jpg.got.val(Jpeg.decode.read.n(nn, rbits(ss, ok))), + Laws.jpg.reads(rest, + Laws.jpg.got.bits(Jpeg.decode.read.n(nn, rbits(ss, ok))))), Laws.jpg.rcons(Laws.jpg.num(cc, 0), + Laws.jpg.reads(rest, rbits(s2, ok))), + (Laws.jpg.nums(cc <> rest), ok), Equal.cong(Jpeg.Bits & U32, List<&2, U32> & U32, + rr => Laws.jpg.rcons(Laws.jpg.got.val(rr), Laws.jpg.reads(rest, + Laws.jpg.got.bits(rr))), Jpeg.decode.read.n(nn, rbits(ss, ok)), (rbits(s2, ok), Laws.jpg.num(cc, 0)), er), + Equal.cong(List<&2, U32> & U32, List<&2, U32> & U32, gg => Laws.jpg.rcons(Laws.jpg.num(cc, 0), gg), + Laws.jpg.reads(rest, rbits(s2, ok)), + (Laws.jpg.nums(rest), ok), reads_m(rest, s2, ok, tl, and_l(rshort(s2), roct8(s2), ok2), and_r(rshort(s2), + roct8(s2), + ok2), also_r(Nat.is_le(ln, 16n), Laws.jpg.codes16(rest), hc), e2))) + +# a list joined with nothing is the list +def app_nil(xs: List<&2, Bool>) -> {List.append(&2, Bool, xs, []) == xs : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case +hh <> tt: + Equal.cong(List<&2, Bool>, List<&2, Bool>, ws => hh <> ws, List.append(&2, Bool, tt, []), tt, app_nil(tt)) + +# the 1 bits that pad the last byte after pending bits pp +def padbits(pp: List<&2, Bool>) -> List<&2, Bool>: + match pp: + case Nil{}: + [] + case +hh <> +tt: + List.replicate(Bool, Nat.sub(8n, List.length(&2, Bool, hh <> tt)), True{}) + +# the pad after pending bits pp and then bits bs +def mpad(bs: List<&2, Bool>, +pp: List<&2, Bool>) -> List<&2, Bool>: + match bs: + case Nil{}: + padbits(pp) + case +bb <> rest: + mpad(rest, mo.next(is7(pp), pp, bb)) + +# the last byte's bits are the pending bits and the pad +def pendo_concat( + +pp: List<&2, Bool> +) -> {List.concat(&2, Bool, pendo(pp)) == List.append(&2, Bool, pp, padbits(pp)) : List<&2, Bool>}: + match pp: + case Nil{}: + {==} + case +hh <> +tt: + app_nil(List.append(&2, Bool, hh <> tt, List.replicate(Bool, Nat.sub(8n, List.length(&2, Bool, hh <> tt)), + True{}))) + +# a byte one more bit may finish goes before the bytes after it, bit for bit +def join_concat( + full: Bool, + +pp: List<&2, Bool>, + +bb: Bool, + +mm: List<&2, List<&2, Bool>>, + +xs: List<&2, Bool>, + +hm: {List.concat(&2, Bool, mm) == List.append(&2, Bool, mo.next(full, pp, bb), xs) : List<&2, Bool>} +) -> {List.concat(&2, Bool, mo.join(full, pp, bb, mm)) == List.append(&2, Bool, pp, bb <> xs) : List<&2, Bool>}: + match full: + case True{}: + Equal.trans(List<&2, Bool>, List.append(&2, Bool, List.append(&2, Bool, pp, [bb]), List.concat(&2, Bool, mm)), + List.append(&2, Bool, List.append(&2, Bool, pp, [bb]), xs), List.append(&2, Bool, pp, bb <> xs), + Equal.cong(List<&2, Bool>, List<&2, Bool>, ws => List.append(&2, Bool, List.append(&2, Bool, pp, [bb]), ws), + List.concat(&2, Bool, mm), xs, hm), app_assoc(pp, [bb], xs)) + case False{}: + Equal.trans(List<&2, Bool>, List.concat(&2, Bool, mm), List.append(&2, Bool, List.append(&2, Bool, pp, [bb]), xs), + List.append(&2, Bool, pp, bb <> xs), hm, app_assoc(pp, [bb], xs)) + +# the model's bytes, bit for bit, are the pending bits, the bits, and the pad +def concat_moct( + bs: List<&2, Bool>, + +pp: List<&2, Bool> +) -> {List.concat(&2, Bool, moct(bs, pp)) == List.append(&2, Bool, pp, List.append(&2, Bool, bs, mpad(bs, + pp))) : List<&2, Bool>}: + match bs: + case Nil{}: + pendo_concat(pp) + case +bb <> +rest: + +fl = is7(pp) + +p2 = mo.next(fl, pp, bb) + join_concat(fl, pp, bb, moct(rest, p2), List.append(&2, Bool, rest, mpad(rest, p2)), concat_moct(rest, p2)) + +# a byte one more bit finishes has 8 bits +def join_oct8( + pp: List<&2, Bool>, + bb: Bool, + mm: List<&2, List<&2, Bool>>, + hs: {short(pp) == True{} : Bool}, + hm: {oct8(mm) == True{} : Bool} +) -> {oct8(mo.join(is7(pp), pp, bb, mm)) == True{} : Bool}: + match pp: + case Nil{}: + hm + case +p0 <> t0: + match t0: + case Nil{}: + hm + case +p1 <> t1: + match t1: + case Nil{}: + hm + case +p2 <> t2: + match t2: + case Nil{}: + hm + case +p3 <> t3: + match t3: + case Nil{}: + hm + case +p4 <> t4: + match t4: + case Nil{}: + hm + case +p5 <> t5: + match t5: + case Nil{}: + hm + case +p6 <> t6: + match t6: + case Nil{}: + hm + case +p7 <> t7: + Empty.absurd({oct8(mo.join(is7(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7), + p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7, bb, mm)) == True{} : Bool}, + long8(t7, hs)) + +# the last byte has 8 bits +def pendo_oct8(pp: List<&2, Bool>, hs: {short(pp) == True{} : Bool}) -> {oct8(pendo(pp)) == True{} : Bool}: + match pp: + case Nil{}: + {==} + case +p0 <> t0: + match t0: + case Nil{}: + {==} + case +p1 <> t1: + match t1: + case Nil{}: + {==} + case +p2 <> t2: + match t2: + case Nil{}: + {==} + case +p3 <> t3: + match t3: + case Nil{}: + {==} + case +p4 <> t4: + match t4: + case Nil{}: + {==} + case +p5 <> t5: + match t5: + case Nil{}: + {==} + case +p6 <> t6: + match t6: + case Nil{}: + {==} + case +p7 <> t7: + Empty.absurd({oct8(pendo(p0 <> p1 <> p2 <> p3 <> p4 <> p5 <> p6 <> p7 <> t7)) == + True{} : Bool}, long8(t7, hs)) + +# every byte of the model has 8 bits +def oct8_moct( + bs: List<&2, Bool>, + +pp: List<&2, Bool>, + +hs: {short(pp) == True{} : Bool} +) -> {oct8(moct(bs, pp)) == True{} : Bool}: + match bs: + case Nil{}: + pendo_oct8(pp, hs) + case +bb <> +rest: + +fl = is7(pp) + +p2 = mo.next(fl, pp, bb) + join_oct8(pp, bb, moct(rest, p2), hs, oct8_moct(rest, p2, next_short(pp, bb, hs))) + +# the bit round trip: codes of at most 16 bits written and padded, then read back one length at a time +def round_trip( + +cs: List<&2, List<&2, Bool>>, + +hc: {Laws.jpg.codes16(cs) == True{} : Bool} +) -> {Laws.jpg.reads(cs, Jpeg.Bits{0, 1, 0, Jenc.encode.pad(Laws.jpg.write(cs, + Jenc.encode.put0()))}) == (Laws.jpg.nums(cs), 1) : List<&2, U32> & U32}: + +bs = List.concat(&2, Bool, cs) + +os = moct(bs, []) + +ep = {Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())), Jenc.encode.pad(feed(bs, + Jenc.encode.put0())), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.write(cs, + Jenc.encode.put0()), feed(bs, Jenc.encode.put0()), wcodes_feed(cs, Jenc.encode.put0(), hc)), + Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], []}))), + rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : + {Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())) == rxs(os) : List<&2, U32>}} + Equal.trans(List<&2, U32> & U32, Laws.jpg.reads(cs, Jpeg.Bits{0, 1, 0, Jenc.encode.pad(Laws.jpg.write(cs, + Jenc.encode.put0()))}), + Laws.jpg.reads(cs, rbits(RS{[], os}, 1)), (Laws.jpg.nums(cs), 1), Equal.cong(List<&2, U32>, List<&2, U32> & U32, + xs => Laws.jpg.reads(cs, + Jpeg.Bits{0, 1, 0, xs}), Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())), rxs(os), ep), + reads_m(cs, RS{[], os}, 1, mpad(bs, []), {==}, oct8_moct(bs, [], {==}), hc, concat_moct(bs, []))) + +# a Huffman walk over bits: the symbol found and the bits it took, or none +type HW is Data: + HWHit{sym: U32, used: Nat} + HWMiss{} + +# the table's answer for one more code bit, before the walk over the bits after it +def hw.pick(look: Maybe<&2, U32>, rest: HW) -> HW: + match look: + case Some{+ss}: + match rest: + case _r: + HWHit{ss, 1n} + case None{}: + match rest: + case HWHit{+ss, +nn}: + HWHit{ss, 1n+nn} + case HWMiss{}: + HWMiss{} + +# the decoder's Huffman lookup over a list of bits, code and len so far: each bit extends the code, the +# table asked at each length +def hw(bs: List<&2, Bool>, +tab: Jpeg.Huff, +code: U32, +len: U32) -> HW: + match bs: + case Nil{}: + HWMiss{} + case +bb <> rest: + match tab: + case Jpeg.Huff{+cs, +ls, +ys}: + hw.pick(Jpeg.decode.look(cs, ls, ys, vstep(code, bb), (len + 1 : U32)), hw(rest, Jpeg.Huff{cs, ls, ys}, + vstep(code, bb), (len + 1 : U32))) + +# the bit reader's loop with nothing left to read hands back the model's reader +def canon0(ss: RS, +ok: U32, +acc: U32) -> {canon(0n, ss, ok, acc) == (rbits(ss, ok), acc) : Jpeg.Bits & U32}: + match ss: + case RS{_qq, _os}: + {==} + +# one bit read from the model's reader is the bit reader's loop for one bit +def one_canon(ss: RS, +ok: U32) -> {Jpeg.decode.one(rbits(ss, ok)) == canon(1n, ss, ok, 0) : Jpeg.Bits & U32}: + match ss: + case RS{_qq, _os}: + {==} + +# the ask of one bit, once the bit is read +def ask.of( + +cs: List<&2, U32>, + +ls: List<&2, U32>, + +ys: List<&2, U32>, + +code: U32, + +len: U32, + bits: Jpeg.Bits, + +bb: Bool +) -> {Jpeg.decode.ask.bit(Jpeg.Huff{cs, ls, ys}, (bits, vstep(0, bb)), code, len) == Jpeg.Ask{Jpeg.decode.look(cs, ls, + ys, vstep(code, bb), (len + 1 : U32)), vstep(code, bb), (len + 1 : U32), bits} : Jpeg.Ask}: + Equal.cong(U32, Jpeg.Ask, xx => Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, xx, (len + 1 : U32)), xx, (len + 1 : U32), + bits}, U32.or(U32.shl(code), ubit(bb)), vstep(code, bb), or_shl_u(code, bb)) + +# one bit asked of the table from the model reader +def ask_step( + +ss: RS, + +ok: U32, + +code: U32, + +len: U32, + +cs: List<&2, U32>, + +ls: List<&2, U32>, + +ys: List<&2, U32>, + hs: {rshort(ss) == True{} : Bool}, + h8: {roct8(ss) == True{} : Bool}, + hn: {rsome(ss) == True{} : Bool} +) -> {Jpeg.decode.ask(rbits(ss, ok), code, len, Jpeg.Huff{cs, ls, ys}) == Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, + vstep(code, rhead(ss)), (len + 1 : U32)), vstep(code, rhead(ss)), (len + 1 : U32), rbits(rnext(ss), ok)} : Jpeg.Ask}: + +hh = {Jpeg.Huff{cs, ls, ys} : Jpeg.Huff} + +nx = rnext(ss) + +bb = rhead(ss) + Equal.trans(Jpeg.Ask, Jpeg.decode.ask.bit(hh, Jpeg.decode.one(rbits(ss, ok)), code, len), + Jpeg.decode.ask.bit(hh, (rbits(nx, ok), vstep(0, bb)), code, len), Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, + vstep(code, bb), (len + 1 : U32)), vstep(code, bb), (len + 1 : U32), rbits(nx, ok)}, + Equal.cong(Jpeg.Bits & U32, Jpeg.Ask, gg => Jpeg.decode.ask.bit(hh, gg, code, len), Jpeg.decode.one(rbits(ss, + ok)), (rbits(nx, ok), vstep(0, bb)), Equal.trans(Jpeg.Bits & U32, Jpeg.decode.one(rbits(ss, ok)), + canon(1n, ss, ok, 0), (rbits(nx, ok), vstep(0, bb)), one_canon(ss, ok), Equal.trans(Jpeg.Bits & U32, + canon(1n, ss, ok, 0), canon(0n, nx, ok, vstep(0, bb)), (rbits(nx, ok), vstep(0, bb)), + rstep(0n, ss, ok, 0, hs, h8, hn), canon0(nx, ok, vstep(0, bb))))), + ask.of(cs, ls, ys, code, len, rbits(nx, ok), bb)) + +# the head of a list of bits, False when empty +def lhead(xs: List<&2, Bool>) -> Bool: + match xs: + case hh <> _t: + hh + case Nil{}: + False{} + +# the tail of a list of bits +def ltail(xs: List<&2, Bool>) -> List<&2, Bool>: + match xs: + case _h <> tt: + tt + case Nil{}: + [] + +# the decoder's answer a walk stands for: the symbol and the reader past its bits, or a miss +def hwres(rr: HW, ss: RS, +ok: U32) -> Jpeg.Hit: + match rr: + case HWHit{+sym, +nn}: + Jpeg.Hit{sym, rbits(radv(nn, ss), ok), ok} + case HWMiss{}: + Jpeg.Hit{0, rbits(ss, ok), 0} + +# a walk that hits within left asks +def hw.fits(rr: HW, +left: Nat) -> Bool: + match rr: + case HWHit{_s, +nn}: + Nat.is_le(nn, left) + case HWMiss{}: + False{} + +# a hit's symbol handed on with the model's reader +def use_some( + +sym: U32, + ss: RS, + +ok: U32 +) -> {Jpeg.decode.huff.use(Some{sym}, rbits(ss, ok)) == Jpeg.Hit{sym, rbits(ss, ok), ok} : Jpeg.Hit}: + match ss: + case RS{_qq, _os}: + {==} + +# no walk fits in zero asks +def pick_pos(look: Maybe<&2, U32>, rest: HW, hf: {hw.fits(hw.pick(look, rest), 0n) == True{} : Bool}) -> Empty: + match look: + case Some{_s}: + false_true(hf) + case None{}: + match rest: + case HWHit{_s, _n}: + false_true(hf) + case HWMiss{}: + false_true(hf) + +# the walk over the bits after the first, as the lookup after one bit needs it +def HwIh(-rest: HW, +_lf: Nat, -s0: RS, +_ok: U32, +_cd: U32, +_ln: U32, -tab: Jpeg.Huff) -> Type: + @hr: {hw.fits(rest, _lf) == True{} : Bool} -> {Jpeg.decode.huff(_lf, Jpeg.Ask{None{}, _cd, _ln, + rbits(rnext(s0), _ok)}, tab) == hwres(rest, rnext(s0), _ok) : Jpeg.Hit} + +# the decoder's lookup after one bit, by the table's answer at that length +def hw.case( + look: Maybe<&2, U32>, + rest: HW, + left: Nat, + +s0: RS, + +ok: U32, + +code: U32, + +len: U32, + +tab: Jpeg.Huff, + hf: {hw.fits(hw.pick(look, rest), 1n+left) == True{} : Bool}, + ih: HwIh(rest, left, s0, ok, code, len, tab) +) -> {Jpeg.decode.huff(left, Jpeg.Ask{look, code, len, rbits(rnext(s0), ok)}, tab) == hwres(hw.pick(look, rest), s0, + ok) : Jpeg.Hit}: + match look: + case Some{+sym}: + match left: + case 0n: + use_some(sym, rnext(s0), ok) + case 1n+_q: + use_some(sym, rnext(s0), ok) + case None{}: + match rest: + case HWHit{+s2, +nn}: + ih(hf) + case HWMiss{}: + Empty.absurd({Jpeg.decode.huff(left, Jpeg.Ask{None{}, code, len, rbits(rnext(s0), ok)}, tab) == + hwres(HWMiss{}, s0, ok) : Jpeg.Hit}, false_true(hf)) + +# the decoder's Huffman lookup from the model's reader holding bits bs and more is the walk over bs +def hwalk( + bs: List<&2, Bool>, + left: Nat, + +ss: RS, + +ok: U32, + +code: U32, + +len: U32, + +cs: List<&2, U32>, + +ls: List<&2, U32>, + +ys: List<&2, U32>, + +tl: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, bs, tl) : List<&2, Bool>}, + hf: {hw.fits(hw(bs, Jpeg.Huff{cs, ls, ys}, code, len), left) == True{} : Bool} +) -> {Jpeg.decode.huff(left, Jpeg.Ask{None{}, code, len, rbits(ss, ok)}, Jpeg.Huff{cs, ls, ys}) == hwres(hw(bs, + Jpeg.Huff{cs, ls, ys}, code, len), ss, ok) : Jpeg.Hit}: + match bs: + case Nil{}: + Empty.absurd({Jpeg.decode.huff(left, Jpeg.Ask{None{}, code, len, rbits(ss, ok)}, Jpeg.Huff{cs, ls, ys}) == + hwres(HWMiss{}, ss, ok) : Jpeg.Hit}, false_true(hf)) + case +bb <> +rest: + match left: + case 0n: + +tab = {Jpeg.Huff{cs, ls, ys} : Jpeg.Huff} + +c2 = vstep(code, bb) + +l2 = (len + 1 : U32) + Empty.absurd({Jpeg.decode.huff(0n, Jpeg.Ask{None{}, code, len, rbits(ss, ok)}, tab) == + hwres(hw(bb <> rest, tab, code, len), ss, ok) : Jpeg.Hit}, pick_pos(Jpeg.decode.look(cs, ls, ys, c2, l2), + hw(rest, tab, c2, l2), hf)) + case 1n+ +pp: + +tab = {Jpeg.Huff{cs, ls, ys} : Jpeg.Huff} + +c2 = vstep(code, bb) + +l2 = (len + 1 : U32) + +lk = Jpeg.decode.look(cs, ls, ys, c2, l2) + +nx = rnext(ss) + +hn = {Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_lt(0n, List.length(&2, Bool, xs)), rem(ss), + bb <> List.append(&2, Bool, rest, tl), he) : {rsome(ss) == True{} : Bool}} + +ec = {Equal.trans(List<&2, Bool>, rhead(ss) <> rem(nx), rem(ss), bb <> List.append(&2, Bool, rest, + tl), Equal.sym(List<&2, Bool>, rem(ss), rhead(ss) <> rem(nx), rem_cons(ss, h8, hn)), he) : + {rhead(ss) <> rem(nx) == bb <> List.append(&2, Bool, rest, tl) : List<&2, Bool>}} + +eh = {Equal.cong(List<&2, Bool>, Bool, xs => lhead(xs), rhead(ss) <> rem(nx), bb <> List.append(&2, + Bool, rest, tl), ec) : {rhead(ss) == bb : Bool}} + +et = {Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => ltail(xs), rhead(ss) <> rem(nx), bb <> + List.append(&2, Bool, rest, tl), ec) : {rem(nx) == List.append(&2, Bool, rest, tl) : List<&2, Bool>}} + Equal.trans(Jpeg.Hit, Jpeg.decode.huff(pp, Jpeg.decode.ask(rbits(ss, ok), code, len, tab), tab), + Jpeg.decode.huff(pp, Jpeg.Ask{lk, c2, l2, rbits(nx, ok)}, tab), hwres(hw(bb <> rest, tab, code, len), ss, + ok), Equal.cong(Jpeg.Ask, Jpeg.Hit, aa => Jpeg.decode.huff(pp, aa, tab), Jpeg.decode.ask(rbits(ss, ok), + code, len, tab), Jpeg.Ask{lk, c2, l2, rbits(nx, ok)}, Equal.trans(Jpeg.Ask, Jpeg.decode.ask(rbits(ss, + ok), code, len, tab), Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, vstep(code, rhead(ss)), l2), + vstep(code, rhead(ss)), l2, rbits(nx, ok)}, Jpeg.Ask{lk, c2, l2, rbits(nx, ok)}, + ask_step(ss, ok, code, len, cs, ls, ys, hs, h8, hn), Equal.cong(Bool, Jpeg.Ask, xb => + Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, vstep(code, xb), l2), vstep(code, xb), l2, rbits(nx, ok)}, + rhead(ss), bb, eh))), + hw.case(lk, hw(rest, tab, c2, l2), pp, ss, ok, c2, l2, tab, hf, hr => hwalk(rest, pp, nx, ok, c2, l2, cs, + ls, ys, tl, rnext_short(ss, hs, h8), rnext_oct8(ss, h8), et, hr))) + +# the first answer, else the second +def orelse(aa: Maybe<&2, U32>, bb: Maybe<&2, U32>) -> Maybe<&2, U32>: + match aa: + case Some{+vv}: + match bb: + case _b: + Some{vv} + case None{}: + bb + +# three lists of one length +def sm3(cs: List<&2, U32>, ls: List<&2, U32>, ys: List<&2, U32>) -> Bool: + match cs ls ys: + case Nil{} Nil{} Nil{}: + True{} + case _c <> ct _l <> lt _y <> yt: + sm3(ct, lt, yt) + case _ _ _: + False{} + +# none second is the first +def orelse_none(aa: Maybe<&2, U32>) -> {aa == orelse(aa, None{}) : Maybe<&2, U32>}: + match aa: + case Some{_v}: + {==} + case None{}: + {==} + +# a lookup's answer before one more entry, over a join +def pref_orelse( + aa: Maybe<&2, U32>, + bb: Maybe<&2, U32>, + eqc: Bool, + eql: Bool, + +ss: U32 +) -> {Jpeg.decode.look.pref(orelse(aa, bb), eqc, eql, ss) == orelse(aa, Jpeg.decode.look.pref(bb, eqc, eql, + ss)) : Maybe<&2, U32>}: + match aa: + case Some{_v}: + match bb: + case Some{_w}: + match eqc: + case True{}: + match eql: + case True{}: + {==} + case False{}: + {==} + case False{}: + match eql: + case True{}: + {==} + case False{}: + {==} + case None{}: + match eqc: + case True{}: + match eql: + case True{}: + {==} + case False{}: + {==} + case False{}: + match eql: + case True{}: + {==} + case False{}: + {==} + case None{}: + {==} + +# the decoder's lookup over joined lists: the later lists' answer, else the earlier ones' +def look_app( + c1: List<&2, U32>, + l1: List<&2, U32>, + s1: List<&2, U32>, + +c2: List<&2, U32>, + +l2: List<&2, U32>, + +s2: List<&2, U32>, + +code: U32, + +len: U32, + hh: {sm3(c1, l1, s1) == True{} : Bool} +) -> {Jpeg.decode.look(List.append(&2, U32, c1, c2), List.append(&2, U32, l1, l2), List.append(&2, U32, s1, s2), code, + len) == orelse(Jpeg.decode.look(c2, l2, s2, code, len), Jpeg.decode.look(c1, l1, s1, code, len)) : Maybe<&2, U32>}: + match c1: + case Nil{}: + match l1: + case Nil{}: + match s1: + case Nil{}: + orelse_none(Jpeg.decode.look(c2, l2, s2, code, len)) + case _y <> _yt: + Empty.absurd({Jpeg.decode.look(c2, l2, List.append(&2, U32, _y <> _yt, s2), code, len) == + orelse(Jpeg.decode.look(c2, l2, s2, code, len), None{}) : Maybe<&2, U32>}, false_true(hh)) + case _l <> _lt: + Empty.absurd({Jpeg.decode.look(c2, List.append(&2, U32, _l <> _lt, l2), List.append(&2, U32, s1, s2), code, + len) == orelse(Jpeg.decode.look(c2, l2, s2, code, len), Jpeg.decode.look([], _l <> _lt, s1, code, len)) : + Maybe<&2, U32>}, false_true(hh)) + case +cc <> +ct: + match l1: + case Nil{}: + Empty.absurd({Jpeg.decode.look(cc <> List.append(&2, U32, ct, c2), l2, List.append(&2, U32, s1, s2), code, + len) == orelse(Jpeg.decode.look(c2, l2, s2, code, len), Jpeg.decode.look(cc <> ct, [], s1, code, len)) : + Maybe<&2, U32>}, false_true(hh)) + case +ll <> +lt: + match s1: + case Nil{}: + Empty.absurd({Jpeg.decode.look(cc <> List.append(&2, U32, ct, c2), ll <> List.append(&2, U32, lt, l2), + s2, code, len) == orelse(Jpeg.decode.look(c2, l2, s2, code, len), Jpeg.decode.look(cc <> ct, ll <> lt, + [], code, len)) : Maybe<&2, U32>}, false_true(hh)) + case +sy <> +st: + +eqc = U32.is_eq(cc, code) + +eql = U32.is_eq(ll, len) + +aa = Jpeg.decode.look(c2, l2, s2, code, len) + +bb = Jpeg.decode.look(ct, lt, st, code, len) + Equal.trans(Maybe<&2, U32>, Jpeg.decode.look.pref(Jpeg.decode.look(List.append(&2, U32, ct, c2), + List.append(&2, U32, lt, l2), List.append(&2, U32, st, s2), code, len), eqc, eql, sy), + Jpeg.decode.look.pref(orelse(aa, bb), eqc, eql, sy), orelse(aa, Jpeg.decode.look.pref(bb, eqc, eql, + sy)), Equal.cong(Maybe<&2, U32>, Maybe<&2, U32>, mm => Jpeg.decode.look.pref(mm, eqc, eql, sy), + Jpeg.decode.look(List.append(&2, U32, ct, c2), List.append(&2, U32, lt, l2), List.append(&2, U32, st, + s2), code, len), orelse(aa, bb), look_app(ct, lt, st, c2, l2, s2, code, len, hh)), + pref_orelse(aa, bb, eqc, eql, sy)) + +# one group of a table: a length, and its codes and symbols in order +type HG is Data: + HG{len: U32, cs: List<&2, U32>, ys: List<&2, U32>} + +# the groups' codes +def gc(gs: List<&2, HG>) -> List<&2, U32>: + match gs: + case Nil{}: + [] + case HG{_l, cs, _y} <> rest: + List.append(&2, U32, cs, gc(rest)) + +# the groups' lengths, one per code +def gl(gs: List<&2, HG>) -> List<&2, U32>: + match gs: + case Nil{}: + [] + case HG{+ln, cs, _y} <> rest: + List.append(&2, U32, List.replicate(U32, List.length(&2, U32, cs), ln), gl(rest)) + +# the groups' symbols +def gsy(gs: List<&2, HG>) -> List<&2, U32>: + match gs: + case Nil{}: + [] + case HG{_l, _c, ys} <> rest: + List.append(&2, U32, ys, gsy(rest)) + +# every group has as many symbols as codes +def wfg(gs: List<&2, HG>) -> Bool: + match gs: + case Nil{}: + True{} + case HG{+ln, +cs, ys} <> rest: + Laws.jpg.also(sm3(cs, List.replicate(U32, List.length(&2, U32, cs), ln), ys), wfg(rest)) + +# one group's answer: asked only when its length is the one sought +def lookgrp(eq: Bool, +ln: U32, cs: List<&2, U32>, ys: List<&2, U32>, +code: U32, +len: U32) -> Maybe<&2, U32>: + match eq: + case True{}: + +cc = cs + Jpeg.decode.look(cc, List.replicate(U32, List.length(&2, U32, cc), ln), ys, code, len) + case False{}: + match cs: + case _c: + match ys: + case _y: + None{} + +# the lookup over groups: the later groups' answer, else this group's +def lookg(gs: List<&2, HG>, +code: U32, +len: U32) -> Maybe<&2, U32>: + match gs: + case Nil{}: + None{} + case HG{+ln, cs, ys} <> rest: + orelse(lookg(rest, code, len), lookgrp(U32.is_eq(ln, len), ln, cs, ys, code, len)) + +# no entry of another length answers +def pref_nf(eqc: Bool, +ss: U32) -> {Jpeg.decode.look.pref(None{}, eqc, False{}, ss) == None{} : Maybe<&2, U32>}: + match eqc: + case True{}: + {==} + case False{}: + {==} + +# a lookup over codes all of another length finds nothing +def look_rep_none( + cs: List<&2, U32>, + ys: List<&2, U32>, + +ln: U32, + +code: U32, + +len: U32, + +hh: {U32.is_eq(ln, len) == False{} : Bool} +) -> {Jpeg.decode.look(cs, List.replicate(U32, List.length(&2, U32, cs), ln), ys, code, len) == None{} : Maybe<&2, + U32>}: + match cs: + case Nil{}: + match ys: + case Nil{}: + {==} + case _y <> _t: + {==} + case +cc <> ct: + match ys: + case Nil{}: + +_c = ct + {==} + case +sy <> st: + +ih = look_rep_none(ct, st, ln, code, len, hh) + Equal.trans(Maybe<&2, U32>, Jpeg.decode.look.pref(Jpeg.decode.look(ct, List.replicate(U32, List.length(&2, + U32, ct), ln), st, code, len), U32.is_eq(cc, code), U32.is_eq(ln, len), sy), + Jpeg.decode.look.pref(None{}, U32.is_eq(cc, code), False{}, sy), None{}, + Equal.trans(Maybe<&2, U32>, Jpeg.decode.look.pref(Jpeg.decode.look(ct, List.replicate(U32, + List.length(&2, U32, ct), ln), st, code, len), U32.is_eq(cc, code), U32.is_eq(ln, len), sy), + Jpeg.decode.look.pref(None{}, U32.is_eq(cc, code), U32.is_eq(ln, len), sy), + Jpeg.decode.look.pref(None{}, U32.is_eq(cc, code), False{}, sy), Equal.cong(Maybe<&2, U32>, + Maybe<&2, U32>, mm => Jpeg.decode.look.pref(mm, U32.is_eq(cc, code), U32.is_eq(ln, len), sy), + Jpeg.decode.look(ct, List.replicate(U32, List.length(&2, U32, ct), ln), st, code, len), None{}, ih), + Equal.cong(Bool, Maybe<&2, U32>, bb => Jpeg.decode.look.pref(None{}, U32.is_eq(cc, code), bb, sy), + U32.is_eq(ln, len), False{}, hh)), pref_nf(U32.is_eq(cc, code), sy)) + +# one group's answer is the lookup over its codes +def lookgrp_eq( + eq: Bool, + +ln: U32, + +cs: List<&2, U32>, + +ys: List<&2, U32>, + +code: U32, + +len: U32, + +he: {U32.is_eq(ln, len) == eq : Bool} +) -> {Jpeg.decode.look(cs, List.replicate(U32, List.length(&2, U32, cs), ln), ys, code, len) == lookgrp(eq, ln, cs, + ys, code, len) : Maybe<&2, U32>}: + match eq: + case True{}: + {==} + case False{}: + look_rep_none(cs, ys, ln, code, len, he) + +# the decoder's lookup over a table in groups is the lookup over groups +def look_g( + gs: List<&2, HG>, + +code: U32, + +len: U32, + +hh: {wfg(gs) == True{} : Bool} +) -> {Jpeg.decode.look(gc(gs), gl(gs), gsy(gs), code, len) == lookg(gs, code, len) : Maybe<&2, U32>}: + match gs: + case Nil{}: + {==} + case HG{+ln, +cs, +ys} <> +rest: + +rp = List.replicate(U32, List.length(&2, U32, cs), ln) + Equal.trans(Maybe<&2, U32>, Jpeg.decode.look(List.append(&2, U32, cs, gc(rest)), List.append(&2, U32, rp, + gl(rest)), + List.append(&2, U32, ys, gsy(rest)), code, len), orelse(Jpeg.decode.look(gc(rest), gl(rest), gsy(rest), code, + len), + Jpeg.decode.look(cs, rp, ys, code, len)), lookg(HG{ln, cs, ys} <> rest, code, len), + look_app(cs, rp, ys, gc(rest), gl(rest), gsy(rest), code, len, also_l(sm3(cs, rp, ys), wfg(rest), hh)), + Equal.trans(Maybe<&2, U32>, orelse(Jpeg.decode.look(gc(rest), gl(rest), gsy(rest), code, len), + Jpeg.decode.look(cs, rp, ys, code, len)), orelse(lookg(rest, code, len), Jpeg.decode.look(cs, rp, ys, code, + len)), + lookg(HG{ln, cs, ys} <> rest, code, len), Equal.cong(Maybe<&2, U32>, Maybe<&2, U32>, mm => orelse(mm, + Jpeg.decode.look(cs, rp, ys, code, len)), Jpeg.decode.look(gc(rest), gl(rest), gsy(rest), code, len), + lookg(rest, code, len), look_g(rest, code, len, also_r(sm3(cs, rp, ys), wfg(rest), hh))), + Equal.cong(Maybe<&2, U32>, Maybe<&2, U32>, mm => orelse(lookg(rest, code, len), mm), Jpeg.decode.look(cs, rp, + ys, code, len), lookgrp(U32.is_eq(ln, len), ln, cs, ys, code, len), lookgrp_eq(U32.is_eq(ln, len), ln, cs, ys, + code, len, {==})))) + +# the walk with the lookup over groups +def hwg(bs: List<&2, Bool>, +gs: List<&2, HG>, +code: U32, +len: U32) -> HW: + match bs: + case Nil{}: + HWMiss{} + case +bb <> rest: + hw.pick(lookg(gs, vstep(code, bb), (len + 1 : U32)), hwg(rest, gs, vstep(code, bb), (len + 1 : U32))) + +# the walk over a table in groups is the walk with the lookup over groups +def hw_g( + bs: List<&2, Bool>, + +gs: List<&2, HG>, + +code: U32, + +len: U32, + +hh: {wfg(gs) == True{} : Bool} +) -> {hw(bs, Jpeg.Huff{gc(gs), gl(gs), gsy(gs)}, code, len) == hwg(bs, gs, code, len) : HW}: + match bs: + case Nil{}: + {==} + case +bb <> rest: + +c2 = vstep(code, bb) + +l2 = (len + 1 : U32) + Equal.trans(HW, hw.pick(Jpeg.decode.look(gc(gs), gl(gs), gsy(gs), c2, l2), hw(rest, Jpeg.Huff{gc(gs), gl(gs), + gsy(gs)}, c2, l2)), hw.pick(lookg(gs, c2, l2), hw(rest, Jpeg.Huff{gc(gs), gl(gs), gsy(gs)}, c2, l2)), + hwg(bb <> rest, gs, code, len), Equal.cong(Maybe<&2, U32>, HW, mm => hw.pick(mm, hw(rest, Jpeg.Huff{gc(gs), + gl(gs), gsy(gs)}, c2, l2)), Jpeg.decode.look(gc(gs), gl(gs), gsy(gs), c2, l2), lookg(gs, c2, l2), look_g(gs, + c2, l2, hh)), Equal.cong(HW, HW, rr => hw.pick(lookg(gs, c2, l2), rr), hw(rest, Jpeg.Huff{gc(gs), gl(gs), + gsy(gs)}, c2, l2), hwg(rest, gs, c2, l2), hw_g(rest, gs, c2, l2, hh))) + +# a walk that hit symbol ss after nn bits +def hw.is(rr: HW, +ss: U32, +nn: Nat) -> Bool: + match rr: + case HWHit{+tt, +jj}: + Bool.and(U32.is_eq(tt, ss), Nat.is_eq(jj, nn)) + case HWMiss{}: + False{} + +# the bits of a book word: its length in the high half, its code in the low half +def cbw(+ww: U32) -> List<&2, Bool>: + cb(U32.to_nat(U32.shrn(ww, 16n)), U32.and(ww, 65535)) + +# a symbol's code, at most 16 bits, walked over the table in groups, hits the symbol at its last bit +def hwchk(+cc: List<&2, Bool>, +gs: List<&2, HG>, +ss: U32) -> Bool: + Bool.and(Nat.is_le(List.length(&2, Bool, cc), 16n), hw.is(hwg(cc, gs, 0, 0), ss, List.length(&2, Bool, cc))) + +# a walk known to hit symbol ss after nn bits +def hwis(rr: HW, +ss: U32, +nn: Nat, +hh: {hw.is(rr, ss, nn) == True{} : Bool}) -> {rr == HWHit{ss, nn} : HW}: + match rr: + case HWHit{+tt, +jj}: + +et = {U32L.ueq(tt, ss, and_l(U32.is_eq(tt, ss), Nat.is_eq(jj, nn), hh)) : {tt == ss : U32}} + +ej = {R.nat_eq_true(jj, nn, and_r(U32.is_eq(tt, ss), Nat.is_eq(jj, nn), hh)) : {jj == nn : Nat}} + Equal.trans(HW, HWHit{tt, jj}, HWHit{ss, jj}, HWHit{ss, nn}, Equal.cong(U32, HW, xx => + HWHit{xx, jj}, tt, ss, et), Equal.cong(Nat, HW, xx => HWHit{ss, xx}, jj, nn, ej)) + case HWMiss{}: + Empty.absurd({HWMiss{} == HWHit{ss, nn} : HW}, false_true(hh)) + +# the dc book the encoder builds, written out +def dcbook.lit() -> Array: + ANode{ANode{ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{131072}, ALeaf{196610}}, ANode{ALeaf{196611}, + ALeaf{196612}}}, ANode{ANode{ALeaf{196613}, ALeaf{196614}}, ANode{ALeaf{262158}, ALeaf{327710}}}}, + ANode{ANode{ANode{ALeaf{393278}, ALeaf{458878}}, ANode{ALeaf{524542}, ALeaf{590334}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, + ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, + ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}}}}, ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, + ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, + ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}}, + ANode{ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, + ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}}}, ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, + ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}, ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, + ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, + ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}, ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}, + ANode{ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, + ANode{ALeaf{0}, ALeaf{0}}}}}}}}} + +# the dc table the decoder builds, in groups of one code length +def dcgrp() -> List<&2, HG>: + [HG{2, [0], [0]}, HG{3, [2, 3, 4, 5, 6], [1, 2, 3, 4, 5]}, HG{4, [14], [6]}, HG{5, [30], [7]}, + HG{6, [62], [8]}, HG{7, [126], [9]}, HG{8, [254], [10]}, HG{9, [510], [11]}] + +# the encoder's book is the one written out +def dcbook.eq() -> {Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()) == dcbook.lit() : Array}: + {==} + +# the statement of dctab.eq +def dctab.eq.ty() -> Type: + {Jpeg.decode.canon(272n, 0n, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), 0, 0, [], [], + []) == Jpeg.Huff{gc(dcgrp()), gl(dcgrp()), gsy(dcgrp())} : Jpeg.Huff} + +# the decoder's table is the groups' +def dctab.eq() -> dctab.eq.ty(): + {==} + +# the dc table in groups, as the decoder holds it +def dctab() -> Jpeg.Huff: + Jpeg.Huff{gc(dcgrp()), gl(dcgrp()), gsy(dcgrp())} + +# every group has as many symbols as codes +def dcgrp.wf() -> {wfg(dcgrp()) == True{} : Bool}: + {==} + +# a symbol's code in the dc book +def code.dc(+sy: U32) -> List<&2, Bool>: + cbw(Laws.jpg.val(Array.get(U32, dcbook.lit(), sy))) + +# the check for one symbol: its book code hits it +def chk.dc(+ss: U32) -> Bool: + hwchk(code.dc(ss), dcgrp(), ss) + +# the check for every symbol of a list +def all.dc(xs: List<&2, U32>) -> Bool: + match xs: + case Nil{}: + True{} + case +xx <> rest: + Laws.jpg.also(chk.dc(xx), all.dc(rest)) + +# every symbol of the table passes +def all.dc.ok() -> {all.dc(Jenc.encode.dcsyms()) == True{} : Bool}: + {==} + +# the rest of the list, as the membership step needs it +def MembIh.dc(+_sy: U32, -rest: List<&2, U32>) -> Type: + @hr: {Laws.jpg.memb(_sy, + rest) == True{} : Bool} -> @ha: {all.dc(rest) == True{} : Bool} -> {chk.dc(_sy) == True{} : Bool} + +# one of the symbols passes, by whether it is the list's first +def memb.dc.b( + eq: Bool, + +ss: U32, + +xx: U32, + +rest: List<&2, U32>, + he: {U32.is_eq(ss, xx) == eq : Bool}, + hin: {Laws.jpg.eith(eq, Laws.jpg.memb(ss, rest)) == True{} : Bool}, + hall: {Laws.jpg.also(chk.dc(xx), all.dc(rest)) == True{} : Bool}, + ih: MembIh.dc(ss, rest) +) -> {chk.dc(ss) == True{} : Bool}: + match eq: + case True{}: + es = Equal.sym(U32, ss, xx, U32L.ueq(ss, xx, he)) + %es : {chk.dc(_) == True{} : Bool} + also_l(chk.dc(xx), all.dc(rest), hall) + case False{}: + ih(hin, also_r(chk.dc(xx), all.dc(rest), hall)) + +# a symbol of a list whose symbols all pass passes +def memb.dc( + xs: List<&2, U32>, + +ss: U32, + hin: {Laws.jpg.memb(ss, xs) == True{} : Bool}, + hall: {all.dc(xs) == True{} : Bool} +) -> {chk.dc(ss) == True{} : Bool}: + match xs: + case Nil{}: + Empty.absurd({chk.dc(ss) == True{} : Bool}, false_true(hin)) + case +xx <> +rest: + memb.dc.b(U32.is_eq(ss, xx), ss, xx, rest, {==}, hin, hall, hr => ha => memb.dc(rest, ss, hr, ha)) + +# the decoder's Huffman lookup, over the dc table in groups, from a model reader holding a symbol's book code and +# more, finds the symbol and leaves the reader after the code +def huff.dc( + +ss: RS, + +ok: U32, + +sy: U32, + +tl: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, code.dc(sy), tl) : List<&2, Bool>}, + +hc: {chk.dc(sy) == True{} : Bool} +) -> {Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, ok)}, Jpeg.Huff{gc(dcgrp()), gl(dcgrp()), + gsy(dcgrp())}) == Jpeg.Hit{sy, rbits(radv(List.length(&2, Bool, code.dc(sy)), ss), ok), ok} : Jpeg.Hit}: + +cc = code.dc(sy) + +nn = List.length(&2, Bool, cc) + +gs = dcgrp() + +tab = {Jpeg.Huff{gc(gs), gl(gs), gsy(gs)} : Jpeg.Huff} + +eh = {hwis(hwg(cc, gs, 0, 0), sy, nn, and_r(Nat.is_le(nn, 16n), hw.is(hwg(cc, gs, 0, 0), sy, nn), hc)) : + {hwg(cc, gs, 0, 0) == HWHit{sy, nn} : HW}} + +ew = {Equal.trans(HW, hw(cc, tab, 0, 0), hwg(cc, gs, 0, 0), HWHit{sy, nn}, hw_g(cc, gs, 0, 0, + dcgrp.wf()), eh) : {hw(cc, tab, 0, 0) == HWHit{sy, nn} : HW}} + +hf = {Equal.trans(Bool, hw.fits(hw(cc, tab, 0, 0), 16n), hw.fits(HWHit{sy, nn}, 16n), True{}, + Equal.cong(HW, Bool, rr => hw.fits(rr, 16n), hw(cc, tab, 0, 0), HWHit{sy, nn}, ew), + and_l(Nat.is_le(nn, 16n), hw.is(hwg(cc, gs, 0, 0), sy, nn), hc)) : + {hw.fits(hw(cc, tab, 0, 0), 16n) == True{} : Bool}} + Equal.trans(Jpeg.Hit, Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, ok)}, tab), hwres(hw(cc, tab, + 0, 0), ss, ok), Jpeg.Hit{sy, rbits(radv(nn, ss), ok), ok}, hwalk(cc, 16n, ss, ok, 0, 0, gc(gs), + gl(gs), gsy(gs), tl, hs, h8, he, hf), Equal.cong(HW, Jpeg.Hit, rr => hwres(rr, ss, ok), hw(cc, tab, + 0, 0), HWHit{sy, nn}, ew)) + +# the ac book the encoder builds, written out +def acbook.lit() -> Array: + ANode{ANode{ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{262154}, ALeaf{131072}}, ANode{ALeaf{131073}, + ALeaf{196612}}}, ANode{ANode{ALeaf{262155}, ALeaf{327706}}, ANode{ALeaf{458872}, ALeaf{524536}}}}, + ANode{ANode{ANode{ALeaf{656374}, ALeaf{1113986}}, ANode{ALeaf{1113987}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{262156}}, ANode{ALeaf{327707}, + ALeaf{458873}}}, ANode{ANode{ALeaf{590326}, ALeaf{722934}}, ANode{ALeaf{1113988}, ALeaf{1113989}}}}, + ANode{ANode{ANode{ALeaf{1113990}, ALeaf{1113991}}, ANode{ALeaf{1113992}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{327708}}, + ANode{ALeaf{524537}, ALeaf{656375}}}, ANode{ANode{ALeaf{790516}, ALeaf{1113993}}, ANode{ALeaf{1113994}, + ALeaf{1113995}}}}, ANode{ANode{ANode{ALeaf{1113996}, ALeaf{1113997}}, ANode{ALeaf{1113998}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{393274}}, + ANode{ALeaf{590327}, ALeaf{790517}}}, ANode{ANode{ALeaf{1113999}, ALeaf{1114000}}, ANode{ALeaf{1114001}, + ALeaf{1114002}}}}, ANode{ANode{ANode{ALeaf{1114003}, ALeaf{1114004}}, ANode{ALeaf{1114005}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}, ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, + ALeaf{393275}}, ANode{ALeaf{656376}, ALeaf{1114006}}}, ANode{ANode{ALeaf{1114007}, ALeaf{1114008}}, + ANode{ALeaf{1114009}, ALeaf{1114010}}}}, ANode{ANode{ANode{ALeaf{1114011}, ALeaf{1114012}}, ANode{ALeaf{1114013}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, + ALeaf{458874}}, ANode{ALeaf{722935}, ALeaf{1114014}}}, ANode{ANode{ALeaf{1114015}, ALeaf{1114016}}, + ANode{ALeaf{1114017}, ALeaf{1114018}}}}, ANode{ANode{ANode{ALeaf{1114019}, ALeaf{1114020}}, ANode{ALeaf{1114021}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, + ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{458875}}, ANode{ALeaf{790518}, ALeaf{1114022}}}, + ANode{ANode{ALeaf{1114023}, ALeaf{1114024}}, ANode{ALeaf{1114025}, ALeaf{1114026}}}}, + ANode{ANode{ANode{ALeaf{1114027}, ALeaf{1114028}}, ANode{ALeaf{1114029}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{524538}}, ANode{ALeaf{790519}, + ALeaf{1114030}}}, ANode{ANode{ALeaf{1114031}, ALeaf{1114032}}, ANode{ALeaf{1114033}, ALeaf{1114034}}}}, + ANode{ANode{ANode{ALeaf{1114035}, ALeaf{1114036}}, ANode{ALeaf{1114037}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}}, ANode{ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{590328}}, + ANode{ALeaf{1015744}, ALeaf{1114038}}}, ANode{ANode{ALeaf{1114039}, ALeaf{1114040}}, ANode{ALeaf{1114041}, + ALeaf{1114042}}}}, ANode{ANode{ANode{ALeaf{1114043}, ALeaf{1114044}}, ANode{ALeaf{1114045}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{590329}}, + ANode{ALeaf{1114046}, ALeaf{1114047}}}, ANode{ANode{ALeaf{1114048}, ALeaf{1114049}}, ANode{ALeaf{1114050}, + ALeaf{1114051}}}}, ANode{ANode{ANode{ALeaf{1114052}, ALeaf{1114053}}, ANode{ALeaf{1114054}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, ANode{ANode{ANode{ANode{ANode{ALeaf{0}, + ALeaf{590330}}, ANode{ALeaf{1114055}, ALeaf{1114056}}}, ANode{ANode{ALeaf{1114057}, ALeaf{1114058}}, + ANode{ALeaf{1114059}, ALeaf{1114060}}}}, ANode{ANode{ANode{ALeaf{1114061}, ALeaf{1114062}}, ANode{ALeaf{1114063}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, + ALeaf{656377}}, ANode{ALeaf{1114064}, ALeaf{1114065}}}, ANode{ANode{ALeaf{1114066}, ALeaf{1114067}}, + ANode{ALeaf{1114068}, ALeaf{1114069}}}}, ANode{ANode{ANode{ALeaf{1114070}, ALeaf{1114071}}, ANode{ALeaf{1114072}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}, + ANode{ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{656378}}, ANode{ALeaf{1114073}, ALeaf{1114074}}}, + ANode{ANode{ALeaf{1114075}, ALeaf{1114076}}, ANode{ALeaf{1114077}, ALeaf{1114078}}}}, + ANode{ANode{ANode{ALeaf{1114079}, ALeaf{1114080}}, ANode{ALeaf{1114081}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{722936}}, ANode{ALeaf{1114082}, + ALeaf{1114083}}}, ANode{ANode{ALeaf{1114084}, ALeaf{1114085}}, ANode{ALeaf{1114086}, ALeaf{1114087}}}}, + ANode{ANode{ANode{ALeaf{1114088}, ALeaf{1114089}}, ANode{ALeaf{1114090}, ALeaf{0}}}, ANode{ANode{ALeaf{0}, + ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}, ANode{ANode{ANode{ANode{ANode{ALeaf{0}, ALeaf{1114091}}, + ANode{ALeaf{1114092}, ALeaf{1114093}}}, ANode{ANode{ALeaf{1114094}, ALeaf{1114095}}, ANode{ALeaf{1114096}, + ALeaf{1114097}}}}, ANode{ANode{ANode{ALeaf{1114098}, ALeaf{1114099}}, ANode{ALeaf{1114100}, ALeaf{0}}}, + ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}, ANode{ANode{ANode{ANode{ALeaf{722937}, + ALeaf{1114101}}, ANode{ALeaf{1114102}, ALeaf{1114103}}}, ANode{ANode{ALeaf{1114104}, ALeaf{1114105}}, + ANode{ALeaf{1114106}, ALeaf{1114107}}}}, ANode{ANode{ANode{ALeaf{1114108}, ALeaf{1114109}}, ANode{ALeaf{1114110}, + ALeaf{0}}}, ANode{ANode{ALeaf{0}, ALeaf{0}}, ANode{ALeaf{0}, ALeaf{0}}}}}}}}} + +# the ac table the decoder builds, in groups of one code length +def acgrp() -> List<&2, HG>: + [HG{2, [0, 1], [1, 2]}, HG{3, [4], [3]}, HG{4, [10, 11, 12], [0, 4, 17]}, HG{5, [26, 27, 28], [5, 18, + 33]}, HG{6, [58, 59], [49, 65]}, HG{7, [120, 121, 122, 123], [6, 19, 81, 97]}, HG{8, [248, 249, 250], [7, + 34, 113]}, HG{9, [502, 503, 504, 505, 506], [20, 50, 129, 145, 161]}, HG{10, [1014, 1015, 1016, 1017, 1018], + [8, 35, 66, 177, 193]}, HG{11, [2038, 2039, 2040, 2041], [21, 82, 209, 240]}, HG{12, [4084, 4085, 4086, + 4087], [36, 51, 98, 114]}, HG{15, [32704], [130]}, HG{16, [65410, 65411, 65412, 65413, 65414, 65415, 65416, + 65417, 65418, 65419, 65420, 65421, 65422, 65423, 65424, 65425, 65426, 65427, 65428, 65429, 65430, 65431, 65432, + 65433, 65434, 65435, 65436, 65437, 65438, 65439, 65440, 65441, 65442, 65443, 65444, 65445, 65446, 65447, 65448, + 65449, 65450, 65451, 65452, 65453, 65454, 65455, 65456, 65457, 65458, 65459, 65460, 65461, 65462, 65463, 65464, + 65465, 65466, 65467, 65468, 65469, 65470, 65471, 65472, 65473, 65474, 65475, 65476, 65477, 65478, 65479, 65480, + 65481, 65482, 65483, 65484, 65485, 65486, 65487, 65488, 65489, 65490, 65491, 65492, 65493, 65494, 65495, 65496, + 65497, 65498, 65499, 65500, 65501, 65502, 65503, 65504, 65505, 65506, 65507, 65508, 65509, 65510, 65511, 65512, + 65513, 65514, 65515, 65516, 65517, 65518, 65519, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, + 65529, 65530, 65531, 65532, 65533, 65534], [9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, + 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, + 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250]}] + +# the encoder's book is the one written out +def acbook.eq() -> {Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()) == acbook.lit() : Array}: + {==} + +# the statement of actab.eq +def actab.eq.ty() -> Type: + {Jpeg.decode.canon(272n, 0n, Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], + []) == Jpeg.Huff{gc(acgrp()), gl(acgrp()), gsy(acgrp())} : Jpeg.Huff} + +# the decoder's table is the groups' +def actab.eq() -> actab.eq.ty(): + {==} + +# the ac table in groups, as the decoder holds it +def actab() -> Jpeg.Huff: + Jpeg.Huff{gc(acgrp()), gl(acgrp()), gsy(acgrp())} + +# every group has as many symbols as codes +def acgrp.wf() -> {wfg(acgrp()) == True{} : Bool}: + {==} + +# a symbol's code in the ac book +def code.ac(+sy: U32) -> List<&2, Bool>: + cbw(Laws.jpg.val(Array.get(U32, acbook.lit(), sy))) + +# the check for one symbol: its book code hits it +def chk.ac(+ss: U32) -> Bool: + hwchk(code.ac(ss), acgrp(), ss) + +# the check for every symbol of a list +def all.ac(xs: List<&2, U32>) -> Bool: + match xs: + case Nil{}: + True{} + case +xx <> rest: + Laws.jpg.also(chk.ac(xx), all.ac(rest)) + +# every symbol of the table passes +def all.ac.ok() -> {all.ac(Jenc.encode.acsyms()) == True{} : Bool}: + {==} + +# the rest of the list, as the membership step needs it +def MembIh.ac(+_sy: U32, -rest: List<&2, U32>) -> Type: + @hr: {Laws.jpg.memb(_sy, + rest) == True{} : Bool} -> @ha: {all.ac(rest) == True{} : Bool} -> {chk.ac(_sy) == True{} : Bool} + +# one of the symbols passes, by whether it is the list's first +def memb.ac.b( + eq: Bool, + +ss: U32, + +xx: U32, + +rest: List<&2, U32>, + he: {U32.is_eq(ss, xx) == eq : Bool}, + hin: {Laws.jpg.eith(eq, Laws.jpg.memb(ss, rest)) == True{} : Bool}, + hall: {Laws.jpg.also(chk.ac(xx), all.ac(rest)) == True{} : Bool}, + ih: MembIh.ac(ss, rest) +) -> {chk.ac(ss) == True{} : Bool}: + match eq: + case True{}: + es = Equal.sym(U32, ss, xx, U32L.ueq(ss, xx, he)) + %es : {chk.ac(_) == True{} : Bool} + also_l(chk.ac(xx), all.ac(rest), hall) + case False{}: + ih(hin, also_r(chk.ac(xx), all.ac(rest), hall)) + +# a symbol of a list whose symbols all pass passes +def memb.ac( + xs: List<&2, U32>, + +ss: U32, + hin: {Laws.jpg.memb(ss, xs) == True{} : Bool}, + hall: {all.ac(xs) == True{} : Bool} +) -> {chk.ac(ss) == True{} : Bool}: + match xs: + case Nil{}: + Empty.absurd({chk.ac(ss) == True{} : Bool}, false_true(hin)) + case +xx <> +rest: + memb.ac.b(U32.is_eq(ss, xx), ss, xx, rest, {==}, hin, hall, hr => ha => memb.ac(rest, ss, hr, ha)) + +# the decoder's Huffman lookup, over the ac table in groups, from a model reader holding a symbol's book code and +# more, finds the symbol and leaves the reader after the code +def huff.ac( + +ss: RS, + +ok: U32, + +sy: U32, + +tl: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, code.ac(sy), tl) : List<&2, Bool>}, + +hc: {chk.ac(sy) == True{} : Bool} +) -> {Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, ok)}, Jpeg.Huff{gc(acgrp()), gl(acgrp()), + gsy(acgrp())}) == Jpeg.Hit{sy, rbits(radv(List.length(&2, Bool, code.ac(sy)), ss), ok), ok} : Jpeg.Hit}: + +cc = code.ac(sy) + +nn = List.length(&2, Bool, cc) + +gs = acgrp() + +tab = {Jpeg.Huff{gc(gs), gl(gs), gsy(gs)} : Jpeg.Huff} + +eh = {hwis(hwg(cc, gs, 0, 0), sy, nn, and_r(Nat.is_le(nn, 16n), hw.is(hwg(cc, gs, 0, 0), sy, nn), hc)) : + {hwg(cc, gs, 0, 0) == HWHit{sy, nn} : HW}} + +ew = {Equal.trans(HW, hw(cc, tab, 0, 0), hwg(cc, gs, 0, 0), HWHit{sy, nn}, hw_g(cc, gs, 0, 0, + acgrp.wf()), eh) : {hw(cc, tab, 0, 0) == HWHit{sy, nn} : HW}} + +hf = {Equal.trans(Bool, hw.fits(hw(cc, tab, 0, 0), 16n), hw.fits(HWHit{sy, nn}, 16n), True{}, + Equal.cong(HW, Bool, rr => hw.fits(rr, 16n), hw(cc, tab, 0, 0), HWHit{sy, nn}, ew), + and_l(Nat.is_le(nn, 16n), hw.is(hwg(cc, gs, 0, 0), sy, nn), hc)) : + {hw.fits(hw(cc, tab, 0, 0), 16n) == True{} : Bool}} + Equal.trans(Jpeg.Hit, Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, ok)}, tab), hwres(hw(cc, tab, + 0, 0), ss, ok), Jpeg.Hit{sy, rbits(radv(nn, ss), ok), ok}, hwalk(cc, 16n, ss, ok, 0, 0, gc(gs), + gl(gs), gsy(gs), tl, hs, h8, he, hf), Equal.cong(HW, Jpeg.Hit, rr => hwres(rr, ss, ok), hw(cc, tab, + 0, 0), HWHit{sy, nn}, ew)) + +# one code read from a model reader holding it and more: the reader after it, and its value +def read1( + +cc: List<&2, Bool>, + +ss: RS, + +ok: U32, + +mr: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +h16: {Nat.is_le(List.length(&2, Bool, cc), 16n) == True{} : Bool}, + +ea: {rem(ss) == List.append(&2, Bool, cc, mr) : List<&2, Bool>} +) -> {Jpeg.decode.read.n(U32.from_nat(List.length(&2, Bool, cc)), rbits(ss, ok)) == (rbits(radv(List.length(&2, Bool, + cc), ss), ok), Laws.jpg.num(cc, 0)) : Jpeg.Bits & U32}: + +ln = List.length(&2, Bool, cc) + +nn = U32.from_nat(ln) + +s2 = radv(ln, ss) + +hl = {Equal.trans(Bool, Nat.is_le(ln, List.length(&2, Bool, rem(ss))), Nat.is_le(ln, List.length(&2, Bool, + List.append(&2, Bool, cc, mr))), True{}, Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(ln, List.length(&2, + Bool, xs)), rem(ss), List.append(&2, Bool, cc, mr), ea), len_app(cc, mr)) : + {Nat.is_le(ln, List.length(&2, Bool, rem(ss))) == True{} : Bool}} + +vr = {Equal.trans(U32, vfold(List.take(&2, Bool, rem(ss), ln), 0), vfold(List.take(&2, Bool, List.append(&2, + Bool, cc, mr), ln), 0), Laws.jpg.num(cc, 0), Equal.cong(List<&2, Bool>, U32, xs => vfold(List.take(&2, Bool, xs, + ln), 0), rem(ss), List.append(&2, Bool, cc, mr), ea), Equal.trans(U32, vfold(List.take(&2, Bool, List.append(&2, + Bool, cc, mr), ln), 0), vfold(cc, 0), Laws.jpg.num(cc, 0), Equal.cong(List<&2, Bool>, U32, xs => vfold(xs, 0), + List.take(&2, Bool, List.append(&2, Bool, cc, mr), ln), cc, take_app(cc, mr)), Equal.sym(U32, + Laws.jpg.num(cc, 0), vfold(cc, 0), num_v(cc, 0)))) : {vfold(List.take(&2, Bool, rem(ss), ln), 0) == + Laws.jpg.num(cc, 0) : U32}} + Equal.trans(Jpeg.Bits & U32, Jpeg.decode.read.n(nn, rbits(ss, ok)), canon(U32.to_nat(nn), ss, ok, 0), + (rbits(s2, ok), Laws.jpg.num(cc, 0)), read_canon(nn, ss, ok), Equal.trans(Jpeg.Bits & U32, canon(U32.to_nat(nn), + ss, ok, 0), canon(ln, ss, ok, 0), (rbits(s2, ok), Laws.jpg.num(cc, 0)), Equal.cong(Nat, Jpeg.Bits & U32, kk => + canon(kk, ss, ok, 0), U32.to_nat(nn), ln, tn16(ln, h16)), Equal.trans(Jpeg.Bits & U32, canon(ln, ss, ok, 0), + (rbits(s2, ok), vfold(List.take(&2, Bool, rem(ss), ln), 0)), (rbits(s2, ok), Laws.jpg.num(cc, 0)), rread(ln, ss, + ok, 0, hs, h8, hl), Equal.cong(U32, Jpeg.Bits & U32, vv => (rbits(s2, ok), vv), vfold(List.take(&2, Bool, rem(ss), + ln), 0), Laws.jpg.num(cc, 0), vr)))) + +# the bits a model reader holds after n bits of bits cc and more are the more +def rem_after( + +cc: List<&2, Bool>, + +ss: RS, + +mr: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +ea: {rem(ss) == List.append(&2, Bool, cc, mr) : List<&2, Bool>} +) -> {rem(radv(List.length(&2, Bool, cc), ss)) == mr : List<&2, Bool>}: + +ln = List.length(&2, Bool, cc) + +hl = {Equal.trans(Bool, Nat.is_le(ln, List.length(&2, Bool, rem(ss))), Nat.is_le(ln, List.length(&2, Bool, + List.append(&2, Bool, cc, mr))), True{}, Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(ln, List.length(&2, + Bool, xs)), rem(ss), List.append(&2, Bool, cc, mr), ea), len_app(cc, mr)) : + {Nat.is_le(ln, List.length(&2, Bool, rem(ss))) == True{} : Bool}} + Equal.trans(List<&2, Bool>, rem(radv(ln, ss)), List.drop(&2, Bool, rem(ss), ln), mr, rem_radv(ln, ss, hs, h8, hl), + Equal.trans(List<&2, Bool>, List.drop(&2, Bool, rem(ss), ln), List.drop(&2, Bool, List.append(&2, Bool, cc, mr), + ln), mr, Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.drop(&2, Bool, xs, ln), rem(ss), + List.append(&2, Bool, cc, mr), ea), drop_app(cc, mr))) + +# the model reader after codes, one length at a time +def skm(cs: List<&2, List<&2, Bool>>, ss: RS) -> RS: + match cs: + case Nil{}: + ss + case cc <> rest: + skm(rest, radv(List.length(&2, Bool, cc), ss)) + +# the decoder's reader after codes is the model's, with the rest of the bits, the model still a reader +def Skip(+cs: List<&2, List<&2, Bool>>, +ss: RS, +ok: U32, +tl: List<&2, Bool>) -> Type: + &e1: {Laws.jpg.skip(cs, rbits(ss, ok)) == rbits(skm(cs, ss), ok) : Jpeg.Bits} -> + &e2: {rem(skm(cs, ss)) == tl : List<&2, Bool>} -> + {Bool.and(rshort(skm(cs, ss)), roct8(skm(cs, ss))) == True{} : Bool} + +# one code read: the reader after it and its value +def ReadOne(+cc: List<&2, Bool>, -ss: RS, +ok: U32) -> Type: + {Jpeg.decode.read.n(U32.from_nat(List.length(&2, Bool, cc)), rbits(ss, ok)) == (rbits(radv(List.length(&2, Bool, + cc), ss), ok), Laws.jpg.num(cc, 0)) : Jpeg.Bits & U32} + +# the reader after one code, then the rest of the codes +def skip.join( + +cc: List<&2, Bool>, + +rest: List<&2, List<&2, Bool>>, + +ss: RS, + +ok: U32, + +tl: List<&2, Bool>, + er: ReadOne(cc, ss, ok), + ih: Skip(rest, radv(List.length(&2, Bool, cc), ss), ok, tl) +) -> Skip(cc <> rest, ss, ok, tl): + (e1, e2, e3) = ih + +ln = List.length(&2, Bool, cc) + +s2 = radv(ln, ss) + (Equal.trans(Jpeg.Bits, Laws.jpg.skip(rest, Laws.jpg.got.bits(Jpeg.decode.read.n(U32.from_nat(ln), rbits(ss, + ok)))), Laws.jpg.skip(rest, rbits(s2, ok)), rbits(skm(rest, s2), ok), Equal.cong(Jpeg.Bits & U32, Jpeg.Bits, + gg => Laws.jpg.skip(rest, Laws.jpg.got.bits(gg)), Jpeg.decode.read.n(U32.from_nat(ln), rbits(ss, ok)), + (rbits(s2, ok), Laws.jpg.num(cc, 0)), er), e1), e2, e3) + +# the decoder's reader after codes held by a model reader, and more bits +def skip_m( + cs: List<&2, List<&2, Bool>>, + +ss: RS, + +ok: U32, + +tl: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +hc: {Laws.jpg.codes16(cs) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, List.concat(&2, Bool, cs), tl) : List<&2, Bool>} +) -> Skip(cs, ss, ok, tl): + match cs: + case Nil{}: + ({==}, he, radv_ok(0n, ss, hs, h8)) + case +cc <> +rest: + +ln = List.length(&2, Bool, cc) + +mr = List.append(&2, Bool, List.concat(&2, Bool, rest), tl) + +h16 = {also_l(Nat.is_le(ln, 16n), Laws.jpg.codes16(rest), hc) : {Nat.is_le(ln, 16n) == True{} : Bool}} + +ea = {Equal.trans(List<&2, Bool>, rem(ss), List.append(&2, Bool, List.concat(&2, Bool, cc <> rest), tl), + List.append(&2, Bool, cc, mr), he, app_assoc(cc, List.concat(&2, Bool, rest), tl)) : + {rem(ss) == List.append(&2, Bool, cc, mr) : List<&2, Bool>}} + +s2 = radv(ln, ss) + +ok2 = {radv_ok(ln, ss, hs, h8) : {Bool.and(rshort(s2), roct8(s2)) == True{} : Bool}} + skip.join(cc, rest, ss, ok, tl, read1(cc, ss, ok, mr, hs, h8, h16, ea), skip_m(rest, s2, ok, tl, and_l(rshort(s2), + roct8(s2), ok2), and_r(rshort(s2), roct8(s2), ok2), also_r(Nat.is_le(ln, 16n), Laws.jpg.codes16(rest), hc), + rem_after(cc, ss, mr, hs, h8, ea))) + +# the encoder's code for a symbol from its dc book is the code's bits fed to the writer +def emit_feed.dc( + +sy: U32, + +pp: Jenc.Put +) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), pp)) == feed(code.dc(sy), pp) : Jenc.Put}: + +vv = Laws.jpg.val(Array.get(U32, dcbook.lit(), sy)) + Equal.trans(Jenc.Put, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(Array.get(U32, dcbook.lit(), sy)), pp)), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((dcbook.lit(), vv)), pp)), feed(code.dc(sy), pp), + Equal.cong(Array & U32, Jenc.Put, gg => Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(gg), pp)), + Array.get(U32, dcbook.lit(), sy), (dcbook.lit(), vv), W10.get.eq(dcbook.lit(), sy)), bits_go(U32.to_nat( + U32.shrn(vv, 16n)), pp, U32.and(vv, 65535))) + +# the model's bits for codes pre, a symbol's code from the dc book, and codes post +def symbits.dc(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> List<&2, Bool>: + List.append(&2, Bool, List.concat(&2, Bool, pre), List.append(&2, Bool, code.dc(sy), List.concat(&2, Bool, post))) + +# the bytes the writer makes of them +def SymBytes.dc(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> Type: + {Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post) == rxs(moct(symbits.dc(pre, sy, post), [])) : List<&2, U32>} + +# the reader after codes pre back +def SymSkip.dc(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> Type: + Skip(pre, RS{[], moct(symbits.dc(pre, sy, post), [])}, 1, List.append(&2, Bool, List.append(&2, Bool, code.dc(sy), + List.concat(&2, Bool, post)), mpad(symbits.dc(pre, sy, post), []))) + +# the round trip once the reader has read codes pre back +def sym_rt2.dc( + +pre: List<&2, List<&2, Bool>>, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.dcsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool}, + eb: SymBytes.dc(pre, sy, post), + sk: SymSkip.dc(pre, sy, post) +) -> {Laws.jpg.sym.back(pre, dcbook.lit(), dctab(), sy, post) == (sy, (Laws.jpg.nums(post), 1), 1) : U32 & (List<&2, + U32> & U32) & U32}: + (k1, k2, k3) = sk + +cc = code.dc(sy) + +cp = List.concat(&2, Bool, pre) + +cq = List.concat(&2, Bool, post) + +bs = List.append(&2, Bool, cp, List.append(&2, Bool, cc, cq)) + +os = moct(bs, []) + +mp = mpad(bs, []) + +s0 = {RS{[], os} : RS} + +mr = List.append(&2, Bool, List.append(&2, Bool, cc, cq), mp) + +s1 = skm(pre, s0) + +tl = List.append(&2, Bool, cq, mp) + +e1 = {Equal.trans(List<&2, Bool>, rem(s1), mr, List.append(&2, Bool, cc, tl), k2, app_assoc(cc, cq, mp)) : + {rem(s1) == List.append(&2, Bool, cc, tl) : List<&2, Bool>}} + +f1 = {k3 : {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}} + +hs1 = {and_l(rshort(s1), roct8(s1), f1) : {rshort(s1) == True{} : Bool}} + +h81 = {and_r(rshort(s1), roct8(s1), f1) : {roct8(s1) == True{} : Bool}} + +s2 = radv(List.length(&2, Bool, cc), s1) + +f2 = {radv_ok(List.length(&2, Bool, cc), s1, hs1, h81) : {Bool.and(rshort(s2), roct8(s2)) == True{} : Bool}} + +eh = {huff.dc(s1, 1, sy, tl, hs1, h81, e1, memb.dc(Jenc.encode.dcsyms(), sy, hy, all.dc.ok())) : + {Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, 1)}, dctab()) == Jpeg.Hit{sy, rbits(s2, 1), 1} : Jpeg.Hit}} + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, dcbook.lit(), dctab(), sy, post), + Laws.jpg.hit(Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, 1)}, dctab()), post), (sy, + (Laws.jpg.nums(post), 1), 1), + Equal.cong(Jpeg.Bits, U32 & (List<&2, U32> & U32) & U32, bb => Laws.jpg.hit(Jpeg.decode.huff(16n, + Jpeg.Ask{None{}, 0, 0, bb}, dctab()), post), Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, Laws.jpg.sym.bytes(pre, + dcbook.lit(), sy, post)}), rbits(s1, 1), Equal.trans(Jpeg.Bits, Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, + Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post)}), Laws.jpg.skip(pre, rbits(s0, + 1)), rbits(s1, 1), Equal.cong(List<&2, U32>, Jpeg.Bits, xs => Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, xs}), + Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post), rxs(os), eb), k1)), + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.hit(Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, + 1)}, dctab()), post), + Laws.jpg.hit(Jpeg.Hit{sy, rbits(s2, 1), 1}, post), (sy, (Laws.jpg.nums(post), 1), 1), Equal.cong(Jpeg.Hit, + U32 & (List<&2, U32> & U32) & U32, hh => Laws.jpg.hit(hh, post), Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, + rbits(s1, 1)}, dctab()), + Jpeg.Hit{sy, rbits(s2, 1), 1}, eh), Equal.cong(List<&2, U32> & U32, U32 & (List<&2, U32> & U32) & U32, rr => (sy, + rr, 1), Laws.jpg.reads(post, rbits(s2, 1)), (Laws.jpg.nums(post), 1), reads_m(post, s2, 1, mp, and_l(rshort(s2), + roct8(s2), f2), and_r(rshort(s2), roct8(s2), f2), hq, rem_after(cc, s1, tl, hs1, h81, e1))))) + +# codes pre, a symbol's code from the dc book and codes post, round trip through the writer and the reader +def sym_rt.dc( + +pre: List<&2, List<&2, Bool>>, + +hp: {Laws.jpg.codes16(pre) == True{} : Bool}, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.dcsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool} +) -> {Laws.jpg.sym.back(pre, dcbook.lit(), dctab(), sy, post) == (sy, (Laws.jpg.nums(post), 1), 1) : U32 & (List<&2, + U32> & U32) & U32}: + +cc = code.dc(sy) + +cp = List.concat(&2, Bool, pre) + +cq = List.concat(&2, Bool, post) + +bs = List.append(&2, Bool, cp, List.append(&2, Bool, cc, cq)) + +os = moct(bs, []) + +mp = mpad(bs, []) + +p0 = Jenc.encode.put0() + +ew = {Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), + Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, feed(cc, + Laws.jpg.write(pre, p0))), feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), Laws.jpg.write(pre, p0))), feed(cc, + Laws.jpg.write(pre, p0)), emit_feed.dc(sy, Laws.jpg.write(pre, p0))), + Equal.trans(Jenc.Put, Laws.jpg.write(post, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, + Laws.jpg.write(pre, p0))), feed(bs, p0), wcodes_feed(post, feed(cc, Laws.jpg.write(pre, p0)), hq), + Equal.trans(Jenc.Put, feed(cq, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, feed(cp, p0))), feed(bs, p0), + Equal.cong(Jenc.Put, Jenc.Put, qq => feed(cq, feed(cc, qq)), Laws.jpg.write(pre, p0), feed(cp, p0), + wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, feed(cq, feed(cc, feed(cp, p0))), feed(List.append(&2, Bool, cc, + cq), feed(cp, p0)), feed(bs, p0), Equal.sym(Jenc.Put, feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed(cq, + feed(cc, feed(cp, p0))), feed_app(cc, cq, feed(cp, p0))), Equal.sym(Jenc.Put, feed(bs, p0), + feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), p0)))))) : + {Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), Laws.jpg.write(pre, + p0)))) == feed(bs, p0) : Jenc.Put}} + +eb = {Equal.trans(List<&2, U32>, Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post), + Jenc.encode.pad(feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => + Jenc.encode.pad(qq), Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), + Laws.jpg.write(pre, p0)))), feed(bs, p0), ew), + Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], + []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : + {Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post) == rxs(os) : List<&2, U32>}} + +s0 = {RS{[], os} : RS} + +mr = List.append(&2, Bool, List.append(&2, Bool, cc, cq), mp) + +e0 = {Equal.trans(List<&2, Bool>, rem(s0), List.append(&2, Bool, bs, mp), List.append(&2, Bool, cp, mr), + concat_moct(bs, []), app_assoc(cp, List.append(&2, Bool, cc, cq), mp)) : {rem(s0) == List.append(&2, Bool, cp, + mr) : List<&2, Bool>}} + sym_rt2.dc(pre, sy, hy, post, hq, eb, skip_m(pre, s0, 1, mr, {==}, oct8_moct(bs, [], {==}), hp, e0)) + +# the dc table law: the real book and table are the ones written out +def huff_law.dc( + +pre: List<&2, List<&2, Bool>>, + +hp: {Laws.jpg.codes16(pre) == True{} : Bool}, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.dcsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool} +) -> {Laws.jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()), Jpeg.decode.canon(272n, + 0n, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), 0, 0, [], [], []), sy, post) == (sy, (Laws.jpg.nums(post), 1), + 1) : U32 & (List<&2, U32> & U32) & U32}: + +cn = Jpeg.decode.canon(272n, 0n, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), 0, 0, [], [], []) + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.dccounts(), + Jenc.encode.dcsyms()), cn, sy, post), Laws.jpg.sym.back(pre, dcbook.lit(), cn, sy, post), (sy, (Laws.jpg.nums(post), + 1), 1), Equal.cong(Array, U32 & (List<&2, U32> & U32) & U32, bk => Laws.jpg.sym.back(pre, bk, cn, sy, post), + Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()), dcbook.lit(), dcbook.eq()), + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, dcbook.lit(), cn, sy, post), + Laws.jpg.sym.back(pre, dcbook.lit(), dctab(), sy, post), (sy, (Laws.jpg.nums(post), 1), 1), Equal.cong(Jpeg.Huff, + U32 & (List<&2, U32> & U32) & U32, tb => Laws.jpg.sym.back(pre, dcbook.lit(), tb, sy, post), cn, dctab(), + dctab.eq()), sym_rt.dc(pre, hp, sy, hy, post, hq))) + +# the encoder's code for a symbol from its ac book is the code's bits fed to the writer +def emit_feed.ac( + +sy: U32, + +pp: Jenc.Put +) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), pp)) == feed(code.ac(sy), pp) : Jenc.Put}: + +vv = Laws.jpg.val(Array.get(U32, acbook.lit(), sy)) + Equal.trans(Jenc.Put, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(Array.get(U32, acbook.lit(), sy)), pp)), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((acbook.lit(), vv)), pp)), feed(code.ac(sy), pp), + Equal.cong(Array & U32, Jenc.Put, gg => Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(gg), pp)), + Array.get(U32, acbook.lit(), sy), (acbook.lit(), vv), W10.get.eq(acbook.lit(), sy)), bits_go(U32.to_nat( + U32.shrn(vv, 16n)), pp, U32.and(vv, 65535))) + +# the model's bits for codes pre, a symbol's code from the ac book, and codes post +def symbits.ac(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> List<&2, Bool>: + List.append(&2, Bool, List.concat(&2, Bool, pre), List.append(&2, Bool, code.ac(sy), List.concat(&2, Bool, post))) + +# the bytes the writer makes of them +def SymBytes.ac(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> Type: + {Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post) == rxs(moct(symbits.ac(pre, sy, post), [])) : List<&2, U32>} + +# the reader after codes pre back +def SymSkip.ac(+pre: List<&2, List<&2, Bool>>, +sy: U32, +post: List<&2, List<&2, Bool>>) -> Type: + Skip(pre, RS{[], moct(symbits.ac(pre, sy, post), [])}, 1, List.append(&2, Bool, List.append(&2, Bool, code.ac(sy), + List.concat(&2, Bool, post)), mpad(symbits.ac(pre, sy, post), []))) + +# the round trip once the reader has read codes pre back +def sym_rt2.ac( + +pre: List<&2, List<&2, Bool>>, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.acsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool}, + eb: SymBytes.ac(pre, sy, post), + sk: SymSkip.ac(pre, sy, post) +) -> {Laws.jpg.sym.back(pre, acbook.lit(), actab(), sy, post) == (sy, (Laws.jpg.nums(post), 1), 1) : U32 & (List<&2, + U32> & U32) & U32}: + (k1, k2, k3) = sk + +cc = code.ac(sy) + +cp = List.concat(&2, Bool, pre) + +cq = List.concat(&2, Bool, post) + +bs = List.append(&2, Bool, cp, List.append(&2, Bool, cc, cq)) + +os = moct(bs, []) + +mp = mpad(bs, []) + +s0 = {RS{[], os} : RS} + +mr = List.append(&2, Bool, List.append(&2, Bool, cc, cq), mp) + +s1 = skm(pre, s0) + +tl = List.append(&2, Bool, cq, mp) + +e1 = {Equal.trans(List<&2, Bool>, rem(s1), mr, List.append(&2, Bool, cc, tl), k2, app_assoc(cc, cq, mp)) : + {rem(s1) == List.append(&2, Bool, cc, tl) : List<&2, Bool>}} + +f1 = {k3 : {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}} + +hs1 = {and_l(rshort(s1), roct8(s1), f1) : {rshort(s1) == True{} : Bool}} + +h81 = {and_r(rshort(s1), roct8(s1), f1) : {roct8(s1) == True{} : Bool}} + +s2 = radv(List.length(&2, Bool, cc), s1) + +f2 = {radv_ok(List.length(&2, Bool, cc), s1, hs1, h81) : {Bool.and(rshort(s2), roct8(s2)) == True{} : Bool}} + +eh = {huff.ac(s1, 1, sy, tl, hs1, h81, e1, memb.ac(Jenc.encode.acsyms(), sy, hy, all.ac.ok())) : + {Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, 1)}, actab()) == Jpeg.Hit{sy, rbits(s2, 1), 1} : Jpeg.Hit}} + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, acbook.lit(), actab(), sy, post), + Laws.jpg.hit(Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, 1)}, actab()), post), (sy, + (Laws.jpg.nums(post), 1), 1), + Equal.cong(Jpeg.Bits, U32 & (List<&2, U32> & U32) & U32, bb => Laws.jpg.hit(Jpeg.decode.huff(16n, + Jpeg.Ask{None{}, 0, 0, bb}, actab()), post), Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, Laws.jpg.sym.bytes(pre, + acbook.lit(), sy, post)}), rbits(s1, 1), Equal.trans(Jpeg.Bits, Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, + Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post)}), Laws.jpg.skip(pre, rbits(s0, + 1)), rbits(s1, 1), Equal.cong(List<&2, U32>, Jpeg.Bits, xs => Laws.jpg.skip(pre, Jpeg.Bits{0, 1, 0, xs}), + Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post), rxs(os), eb), k1)), + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.hit(Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(s1, + 1)}, actab()), post), + Laws.jpg.hit(Jpeg.Hit{sy, rbits(s2, 1), 1}, post), (sy, (Laws.jpg.nums(post), 1), 1), Equal.cong(Jpeg.Hit, + U32 & (List<&2, U32> & U32) & U32, hh => Laws.jpg.hit(hh, post), Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, + rbits(s1, 1)}, actab()), + Jpeg.Hit{sy, rbits(s2, 1), 1}, eh), Equal.cong(List<&2, U32> & U32, U32 & (List<&2, U32> & U32) & U32, rr => (sy, + rr, 1), Laws.jpg.reads(post, rbits(s2, 1)), (Laws.jpg.nums(post), 1), reads_m(post, s2, 1, mp, and_l(rshort(s2), + roct8(s2), f2), and_r(rshort(s2), roct8(s2), f2), hq, rem_after(cc, s1, tl, hs1, h81, e1))))) + +# codes pre, a symbol's code from the ac book and codes post, round trip through the writer and the reader +def sym_rt.ac( + +pre: List<&2, List<&2, Bool>>, + +hp: {Laws.jpg.codes16(pre) == True{} : Bool}, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.acsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool} +) -> {Laws.jpg.sym.back(pre, acbook.lit(), actab(), sy, post) == (sy, (Laws.jpg.nums(post), 1), 1) : U32 & (List<&2, + U32> & U32) & U32}: + +cc = code.ac(sy) + +cp = List.concat(&2, Bool, pre) + +cq = List.concat(&2, Bool, post) + +bs = List.append(&2, Bool, cp, List.append(&2, Bool, cc, cq)) + +os = moct(bs, []) + +mp = mpad(bs, []) + +p0 = Jenc.encode.put0() + +ew = {Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), + Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, feed(cc, + Laws.jpg.write(pre, p0))), feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), Laws.jpg.write(pre, p0))), feed(cc, + Laws.jpg.write(pre, p0)), emit_feed.ac(sy, Laws.jpg.write(pre, p0))), + Equal.trans(Jenc.Put, Laws.jpg.write(post, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, + Laws.jpg.write(pre, p0))), feed(bs, p0), wcodes_feed(post, feed(cc, Laws.jpg.write(pre, p0)), hq), + Equal.trans(Jenc.Put, feed(cq, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, feed(cp, p0))), feed(bs, p0), + Equal.cong(Jenc.Put, Jenc.Put, qq => feed(cq, feed(cc, qq)), Laws.jpg.write(pre, p0), feed(cp, p0), + wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, feed(cq, feed(cc, feed(cp, p0))), feed(List.append(&2, Bool, cc, + cq), feed(cp, p0)), feed(bs, p0), Equal.sym(Jenc.Put, feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed(cq, + feed(cc, feed(cp, p0))), feed_app(cc, cq, feed(cp, p0))), Equal.sym(Jenc.Put, feed(bs, p0), + feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), p0)))))) : + {Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), Laws.jpg.write(pre, + p0)))) == feed(bs, p0) : Jenc.Put}} + +eb = {Equal.trans(List<&2, U32>, Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post), + Jenc.encode.pad(feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => + Jenc.encode.pad(qq), Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), + Laws.jpg.write(pre, p0)))), feed(bs, p0), ew), + Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], + []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : + {Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post) == rxs(os) : List<&2, U32>}} + +s0 = {RS{[], os} : RS} + +mr = List.append(&2, Bool, List.append(&2, Bool, cc, cq), mp) + +e0 = {Equal.trans(List<&2, Bool>, rem(s0), List.append(&2, Bool, bs, mp), List.append(&2, Bool, cp, mr), + concat_moct(bs, []), app_assoc(cp, List.append(&2, Bool, cc, cq), mp)) : {rem(s0) == List.append(&2, Bool, cp, + mr) : List<&2, Bool>}} + sym_rt2.ac(pre, sy, hy, post, hq, eb, skip_m(pre, s0, 1, mr, {==}, oct8_moct(bs, [], {==}), hp, e0)) + +# the ac table law: the real book and table are the ones written out +def huff_law.ac( + +pre: List<&2, List<&2, Bool>>, + +hp: {Laws.jpg.codes16(pre) == True{} : Bool}, + +sy: U32, + +hy: {Laws.jpg.memb(sy, Jenc.encode.acsyms()) == True{} : Bool}, + +post: List<&2, List<&2, Bool>>, + +hq: {Laws.jpg.codes16(post) == True{} : Bool} +) -> {Laws.jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), Jpeg.decode.canon(272n, + 0n, Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), sy, post) == (sy, (Laws.jpg.nums(post), 1), + 1) : U32 & (List<&2, U32> & U32) & U32}: + +cn = Jpeg.decode.canon(272n, 0n, Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []) + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.accounts(), + Jenc.encode.acsyms()), cn, sy, post), Laws.jpg.sym.back(pre, acbook.lit(), cn, sy, post), (sy, (Laws.jpg.nums(post), + 1), 1), Equal.cong(Array, U32 & (List<&2, U32> & U32) & U32, bk => Laws.jpg.sym.back(pre, bk, cn, sy, post), + Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), acbook.lit(), acbook.eq()), + Equal.trans(U32 & (List<&2, U32> & U32) & U32, Laws.jpg.sym.back(pre, acbook.lit(), cn, sy, post), + Laws.jpg.sym.back(pre, acbook.lit(), actab(), sy, post), (sy, (Laws.jpg.nums(post), 1), 1), Equal.cong(Jpeg.Huff, + U32 & (List<&2, U32> & U32) & U32, tb => Laws.jpg.sym.back(pre, acbook.lit(), tb, sy, post), cn, actab(), + actab.eq()), sym_rt.ac(pre, hp, sy, hy, post, hq))) + diff --git a/src/jpeg_enc.bend b/src/jpeg_enc.bend index 950f7a2..96188a3 100644 --- a/src/jpeg_enc.bend +++ b/src/jpeg_enc.bend @@ -212,7 +212,7 @@ def encode.pad.n(zz: Bool, out: List<&2, U32>, +buf: U32, +nn: U32) -> List<&2, out case False{}: +sh = (8 - nn : U32) - U32.or(U32.shln(buf, U32.to_nat(sh)), (U32.shln(1, U32.to_nat(sh)) - 1 : U32)) <> out + U32.or((U32.shln(1, U32.to_nat(sh)) - 1 : U32), U32.shln(buf, U32.to_nat(sh))) <> out # the entropy-coded bytes: the open byte padded with 1 bits, then every byte stuffed, in order def encode.pad(pp: Put) -> List<&2, U32>: From 970d603f52e09346312182be7d1e701bf68c07cb Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 02:34:34 +0000 Subject: [PATCH 6/7] feat: IMG-JPG-3 decode.run returns a picture for any well-formed blocks the encoder's writer writes Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- LAWS.bend | 191 ++++++ PROOF.bend | 3 + SPEC.md | 4 +- proof/wp12-jpeg-huffman.bend | 1138 ++++++++++++++++++++++++++++++---- src/jpeg.bend | 26 +- 5 files changed, 1228 insertions(+), 134 deletions(-) diff --git a/LAWS.bend b/LAWS.bend index c3f7773..13f5c97 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -2666,3 +2666,194 @@ law jpeg_huff_ac: {jpg.sym.back(pre, Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), Jpeg.decode.canon(272n, 0n, Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), sy, post) == (sy, (jpg.nums(post), 1), 1) : U32 & (List<&2, U32> & U32) & U32} + + +# a bit as a U32: bit 0 set to it, the rest clear +def jpg.ubit(bb: Bool) -> U32: + U32{WCon{bb, Word.zero(31n)}} + +# the low bit of a U32 +def jpg.bit0(xx: U32) -> Bool: + match xx: + case U32{ww}: + match ww: + case WCon{b0, _t}: + b0 + +# the bits the encoder's writer takes from a code of kk bits, top first: bit kk - 1 down to bit 0 +def jpg.cb(kk: Nat, +code: U32) -> List<&2, Bool>: + match kk: + case 0n: + [] + case 1n+ +pp: + jpg.bit0(U32.shrn(code, pp)) <> jpg.cb(pp, code) + +# the bits of a book word (encode.huff): its length in the high half, its code in the low half +def jpg.cbw(+ww: U32) -> List<&2, Bool>: + jpg.cb(U32.to_nat(U32.shrn(ww, 16n)), U32.and(ww, 65535)) + +# the encoder's bit writer (encode.bit) fed bits one at a time +def jpg.feed(bs: List<&2, Bool>, pp: Jenc.Put) -> Jenc.Put: + match bs: + case Nil{}: + pp + case bb <> rest: + jpg.feed(rest, Jenc.encode.bit(pp, jpg.ubit(bb))) + +# one AC token: a symbol and the magnitude bits after its code +type JTok is Data: + JTok{sym: U32, ex: List<&2, Bool>} + +# the code the encoder's AC book (encode.huff over the Annex K AC table) holds for symbol sy, as its bits +def jpg.acode(+sy: U32) -> List<&2, Bool>: + jpg.cbw(jpg.val(Array.get(U32, Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), sy))) + +# the code the encoder's DC book holds for symbol sy, as its bits +def jpg.dcode(+sy: U32) -> List<&2, Bool>: + jpg.cbw(jpg.val(Array.get(U32, Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()), sy))) + +# the bits of AC tokens: each symbol's code in the AC book, then its magnitude bits +def jpg.acbits(ts: List<&2, JTok>) -> List<&2, Bool>: + match ts: + case Nil{}: + [] + case JTok{+sym, ex} <> rest: + List.append(&2, Bool, jpg.acode(sym), List.append(&2, Bool, ex, jpg.acbits(rest))) + +# the decoder's AC loop (decode.ac) as tokens follow it: the next index, the lookups left, and whether the block ended +type JAcSt is Data: + JAcSt{kk: U32, left: Nat, fin: Bool} + +# an empty list of bits +def jpg.isnil(xs: List<&2, Bool>) -> Bool: + match xs: + case Nil{}: + True{} + case _h <> _t: + False{} + +# the token's own check, by its kind: EOB with no bits, ZRL with no bits and room for 16 zeros, or a run and a +# nonzero size that stay inside the block, with as many magnitude bits as the size +def jpg.acok.k(eob: Bool, zrl: Bool, +sym: U32, ex: List<&2, Bool>, +kk: U32) -> Bool: + match eob: + case True{}: + jpg.isnil(ex) + case False{}: + match zrl: + case True{}: + Bool.and(jpg.isnil(ex), Bool.or(U32.is_lt((kk + 16 : U32), 64), U32.is_eq((kk + 16 : U32), 64))) + case False{}: + Bool.and(Bool.not(U32.is_eq(U32.and(15, sym), 0)), Bool.and(Bool.not(U32.is_ge((kk + U32.shrn(sym, 4n) : + U32), 64)), Nat.is_eq(List.length(&2, Bool, ex), U32.to_nat(U32.and(15, sym))))) + +# the token is read in state st: the block has not ended, a lookup is left, the symbol is in the AC table +def jpg.acok(tt: JTok, st: JAcSt) -> Bool: + match tt: + case JTok{+sym, ex}: + match st: + case JAcSt{+kk, left, fin}: + Bool.and(Bool.not(fin), Bool.and(Nat.is_lt(0n, left), Bool.and(jpg.memb(sym, Jenc.encode.acsyms()), + jpg.acok.k(U32.is_eq(sym, 0), U32.is_eq(sym, 240), sym, ex, kk)))) + +# one less, and zero stays zero +def jpg.npred(nn: Nat) -> Nat: + match nn: + case 0n: + 0n + case 1n+pp: + pp + +# the state after a token, by its kind +def jpg.acnx.k(eob: Bool, zrl: Bool, +sym: U32, +kk: U32, +left: Nat) -> JAcSt: + match eob: + case True{}: + +_s = sym + +_z = zrl + JAcSt{kk, jpg.npred(left), True{}} + case False{}: + match zrl: + case True{}: + +_s = sym + JAcSt{(kk + 16 : U32), jpg.npred(left), Bool.not(U32.is_lt((kk + 16 : U32), 64))} + case False{}: + +n2 = ((kk + U32.shrn(sym, 4n) : U32) + 1 : U32) + JAcSt{n2, jpg.npred(left), U32.is_eq(n2, 64)} + +# the state after a token +def jpg.acnx(tt: JTok, st: JAcSt) -> JAcSt: + match tt: + case JTok{+sym, _ex}: + match st: + case JAcSt{+kk, +left, _fin}: + jpg.acnx.k(U32.is_eq(sym, 0), U32.is_eq(sym, 240), sym, kk, left) + +# AC tokens the decoder's loop reads in full from state st, the block ending exactly at the last +def jpg.acwf(ts: List<&2, JTok>, +st: JAcSt) -> Bool: + match ts: + case Nil{}: + match st: + case JAcSt{_k, _l, fin}: + fin + case +tt <> rest: + jpg.also(jpg.acok(tt, st), jpg.acwf(rest, jpg.acnx(tt, st))) + +# one block's tokens: the DC symbol and its magnitude bits, then the AC tokens +type JBlkTok is Data: + JBlkTok{dcs: U32, dcx: List<&2, Bool>, acs: List<&2, JTok>} + +# a block's bits: the DC symbol's code in the DC book, its magnitude bits, and the AC tokens' bits +def jpg.blkbits(bt: JBlkTok) -> List<&2, Bool>: + match bt: + case JBlkTok{+dcs, dcx, acs}: + List.append(&2, Bool, jpg.dcode(dcs), List.append(&2, Bool, dcx, jpg.acbits(acs))) + +# a block the decoder reads whole: a DC symbol of the DC table with as many magnitude bits as its size, and AC +# tokens read in full from index 1 with 63 lookups left, ending the block +def jpg.blkwf(bt: JBlkTok) -> Bool: + match bt: + case JBlkTok{+dcs, dcx, acs}: + Bool.and(jpg.memb(dcs, Jenc.encode.dcsyms()), Bool.and(Nat.is_eq(List.length(&2, Bool, dcx), U32.to_nat(dcs)), + jpg.acwf(acs, JAcSt{1, 63n, False{}}))) + +# the bits of blocks, one after another +def jpg.tbits(ts: List<&2, JBlkTok>) -> List<&2, Bool>: + match ts: + case Nil{}: + [] + case tt <> rest: + List.append(&2, Bool, jpg.blkbits(tt), jpg.tbits(rest)) + +# every block is one the decoder reads whole +def jpg.allwf(ts: List<&2, JBlkTok>) -> Bool: + match ts: + case Nil{}: + True{} + case tt <> rest: + jpg.also(jpg.blkwf(tt), jpg.allwf(rest)) + +# a decode that returned a picture +def jpg.psome(mm: Maybe<&2, Jpeg.Pic>) -> Bool: + match mm: + case Some{_p}: + True{} + case None{}: + False{} + +# LAW: the scan side of the JPEG round trip. Take blocks the decoder reads whole: each a DC symbol of the Annex K +# DC table with its magnitude bits, then AC tokens of the AC table (EOB, ZRL, or a run and a nonzero size with its +# magnitude bits) that fill or end the block. Write as many of them as the encoder's frame has blocks, each symbol +# as the code the encoder's book (encode.huff) holds for it, with the encoder's bit writer (encode.bit, then +# encode.pad). decode.run, in the encoder's frame, scan and tables, decodes those bytes to a picture +# IMG-JPG-3 +law jpeg_scan_some: + for +tt: JBlkTok + for +rest: List<&2, JBlkTok> + for +ww: U32 + for +hh: U32 + for h_w: {U32.is_eq(ww, 0) == False{} : Bool} + for h_h: {U32.is_eq(hh, 0) == False{} : Bool} + for h_wf: {jpg.allwf(tt <> rest) == True{} : Bool} + for h_n: {U32.to_nat(Jpeg.decode.nblocks(ww, hh, [1, 1, 1], [1, 1, 1], 1, 1)) == 1n+List.length(&2, JBlkTok, rest) : + Nat} + {jpg.psome(Jpeg.decode.run(jpg.enc.frame(ww, hh), jpg.enc.scan(), jpg.enc.tabs(), Jenc.encode.pad(jpg.feed( + jpg.tbits(tt <> rest), Jenc.encode.put0())), 0)) == True{} : Bool} diff --git a/PROOF.bend b/PROOF.bend index a20299d..d5979ab 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -5703,3 +5703,6 @@ def Laws.jpeg_huff_dc(pre, h_pre, sy, h_sy, post, h_post): def Laws.jpeg_huff_ac(pre, h_pre, sy, h_sy, post, h_post): W12.huff_law.ac(pre, h_pre, sy, h_sy, post, h_post) + +def Laws.jpeg_scan_some(tt, rest, ww, hh, h_w, h_h, h_wf, h_n): + W12.scan_some(tt, rest, ww, hh, h_w, h_h, h_wf, h_n) diff --git a/SPEC.md b/SPEC.md index 2592a2a..f92cce8 100644 --- a/SPEC.md +++ b/SPEC.md @@ -70,7 +70,7 @@ What a sample means, for every decoder and encoder. | :---- | :---- | :---- | :---- | :---- | | IMG-JPG-1 | `decode_jpeg` returns none for every input that opens with SOI, then segments other than frame headers (DHT, DQT, SOS, DRI, COM, APP0 to APP15) each with a length field that fits its body, then a frame header other than SOF0 or an SOF0 segment whose sample precision is not 8, whatever follows. | Proved | proved | LAWS.bend jpeg_refuse_sofn; LAWS.bend jpeg_refuse_precision | | IMG-JPG-2 | For a SOF0 frame of at most 2^31 points whose components' sampling factors are each 1, 2 or 4, `decode_jpeg` places each component's samples in the order T.81 A.2.3 gives and replicates each sample over the pixels its sampling covers; for a factor outside 1, 2 and 4, `decode_jpeg` returns none. The sample values themselves are IMG-JPG-7's. | Proved | pending | LAWS.bend jpeg_refuse_factor1; LAWS.bend jpeg_refuse_factor3; LAWS.bend jpeg_refuse_factor1_any; LAWS.bend jpeg_refuse_factor3_any; LAWS.bend jpeg_refuse_count; LAWS.bend jpeg_comp_index; LAWS.bend jpeg_walk_unit; LAWS.bend jpeg_walk_comp; LAWS.bend jpeg_walk_mcu; LAWS.bend jpeg_walk_frame; LAWS.bend jpeg_walk_count; LAWS.bend jpeg_mcu_grid; LAWS.bend jpeg_mcu_grid_comp; LAWS.bend jpeg_block_cover; LAWS.bend jpeg_block_cover_all; LAWS.bend jpeg_points_at; LAWS.bend jpeg_rgbs_at | -| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535 and at most 2^31 samples, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_unstuff; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized; LAWS.bend jpeg_bits_round_trip; LAWS.bend jpeg_huff_dc; LAWS.bend jpeg_huff_ac | +| IMG-JPG-3 | For every well-formed raster `r` with both sides nonzero and at most 65535 and at most 2^31 samples, `decode_jpeg(encode_jpeg(r))` is some raster of `r`'s size with alpha 255. | Proved | pending | LAWS.bend jpeg_enc_stuffed; LAWS.bend jpeg_unstuff; LAWS.bend jpeg_enc_header_walk; LAWS.bend jpeg_ent_walk; LAWS.bend jpeg_round_trip_scan; LAWS.bend jpeg_run_sized; LAWS.bend jpeg_round_trip_sized; LAWS.bend jpeg_bits_round_trip; LAWS.bend jpeg_huff_dc; LAWS.bend jpeg_huff_ac; LAWS.bend jpeg_scan_some | | IMG-JPG-4 | `encode_jpeg(r)` is none exactly when `encode_png(r)` is none. | Proved | proved | LAWS.bend jpeg_png_refuse_alike | | IMG-JPG-5 | When `encode_jpeg(r)` is some, it begins with SOI and ends with EOI; when both sides of `r` are at most 65535 it is SOI, APP0 with the JFIF identifier, DQT, SOF0 carrying `r`'s width and height, two DHT, SOS, the entropy-coded data and EOI. | Proved | proved | LAWS.bend jpeg_enc_layout; LAWS.bend jpeg_enc_ends | | IMG-JPG-6 | `Jpeg.rgb(y, cb, cr)` equals the T.871 YCbCr to RGB conversion, rounded to nearest and clamped to 0 to 255, for every `y`, `cb`, `cr` from 0 to 255. | Trusted | | | @@ -82,7 +82,7 @@ What a sample means, for every decoder and encoder. | ID | Proved so far | Missing | | :---- | :---- | :---- | | IMG-JPG-2 | Refusal: a SOF0 segment with a factor outside 1, 2 and 4 makes `decode_jpeg` none, for one component or three, whatever precedes and follows it (`jpeg_refuse_factor1`, `jpeg_refuse_factor3`), and also after fill bytes before its marker and with a length field longer than its components (`jpeg_refuse_factor1_any`, `jpeg_refuse_factor3_any`); a SOF0 segment of any other component count is none whatever its factors (`jpeg_refuse_count`). Placement: a scan component takes the factors of the frame component whose identifier it names, the first with that identifier (`jpeg_comp_index`); the MCU walk goes from data unit `bi` to `bi + 1`, then to the next scan component, then to the next MCU (`jpeg_walk_unit`, `jpeg_walk_comp`, `jpeg_walk_mcu`); from the scan's first block the decoder's own walk visits the frame's MCUs in raster order, `ceil(w / 8 hmax)` to a row, and in each MCU the scan's components and their `hi * vi` units in T.81 order, the unit, component and column counters never wrapping (`jpeg_walk_frame`), and the U32 block count the decoder runs, `decode.nblocks`, is that order's length over `ceil(h / 8 vmax)` MCU rows when the scan carries the frame's data units and the count fits a U32 (`jpeg_walk_count`); the `hi * vi` units of any scan component sit in T.81 A.2.3's grid, `hi` to a row, each sample covering `hmax / hi` by `vmax / vi` pixels (`jpeg_mcu_grid`, `jpeg_mcu_grid_comp`); painting a block writes sample `k` over exactly the `pw` by `ph` pixels at `(ox + (k mod 8) * pw, oy + (k div 8) * ph)` inside the frame, for every sample size, 4 by 4 included, and onto every plane, one earlier blocks painted included (`jpeg_block_cover`, `jpeg_block_cover_all`); the decoder reads a plane out point by point, sample `k` being the value `Array.get` finds at index `k` (`jpeg_points_at`). Colour: sample `k` of the colour pass is `Jpeg.rgb` of point `k`'s Y, Cb and Cr (`jpeg_rgbs_at`) | that `Array.get` at an index finds the value the last `Array.set` at that index wrote, and a set at another index leaves it (the planes are perfect binary trees of `2^d` leaves, and the lemmas must follow the masked index down the tree), which joins the painting laws to `jpeg_points_at`. The row is worded for frames of at most 2^31 points: above that `decode.plane`'s depth, taken from `U32.shl(nn)`, wraps and points alias | -| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves. The encoder's entropy-coded bytes have every 255 followed by a 0, for every size and samples (`jpeg_enc_stuffed`), and the decoder's bit reader, reading eight bits at a time from the encoder's stuffing of any bytes, reads the bytes back with the stuffed zeros dropped (`jpeg_unstuff`); the decoder's marker walk over the encoder's header reaches the entropy-coded data with the frame carrying the encoder's width and height, its scan and its tables, a baseline frame read and nothing refused (`jpeg_enc_header_walk`); inside the data the walk keeps every byte of stuffed data and stops at EOI (`jpeg_ent_walk`); so for every well-formed raster with both sides from 1 to 65535, `decode_jpeg(encode_jpeg(r))` is the decoder's scan decode, `decode.run`, of the encoder's own entropy-coded bytes in the frame of `r`'s width and height (`jpeg_round_trip_scan`); that scan decode is none or a picture of its frame's width and height, whatever the bytes (`jpeg_run_sized`); so `decode_jpeg(encode_jpeg(r))` is none or a raster of `r`'s size (`jpeg_round_trip_sized`) | that `decode.run` on the encoder's entropy-coded bytes is not none: that every Huffman lookup finds its symbol and every block ends by 64 coefficients, which is the bit writer (`encode.bits`, `encode.pack`, the pad with 1 bits: that the bytes it writes carry the codes' bits in order) against the bit reader reading codes of 1 to 16 bits across byte boundaries (the byte-aligned case is `jpeg_unstuff`), the Annex K tables `encode.huff` builds against the ones `decode.canon` builds and `decode.look` searches, and the encoder's three paths (neutral, solid, `encode.go`) each writing `ceil(w / 8) * ceil(h / 8)` MCUs of three blocks, each a DC code and magnitude, AC run and size codes and magnitudes, and EOB | +| IMG-JPG-3 | Alpha 255 holds for every sample `decode_jpeg` returns (`jpeg_decode_opaque`, IMG-PIX-1), and `encode_jpeg`'s bytes have the layout IMG-JPG-5 proves. The encoder's entropy-coded bytes have every 255 followed by a 0, for every size and samples (`jpeg_enc_stuffed`), and the decoder's bit reader, reading eight bits at a time from the encoder's stuffing of any bytes, reads the bytes back with the stuffed zeros dropped (`jpeg_unstuff`); the decoder's marker walk over the encoder's header reaches the entropy-coded data with the frame carrying the encoder's width and height, its scan and its tables, a baseline frame read and nothing refused (`jpeg_enc_header_walk`); inside the data the walk keeps every byte of stuffed data and stops at EOI (`jpeg_ent_walk`); so for every well-formed raster with both sides from 1 to 65535, `decode_jpeg(encode_jpeg(r))` is the decoder's scan decode, `decode.run`, of the encoder's own entropy-coded bytes in the frame of `r`'s width and height (`jpeg_round_trip_scan`); that scan decode is none or a picture of its frame's width and height, whatever the bytes (`jpeg_run_sized`); so `decode_jpeg(encode_jpeg(r))` is none or a raster of `r`'s size (`jpeg_round_trip_sized`). The encoder's bit writer (`encode.bits`, then the pad with 1 bits) writes any codes of 1 to 16 bits so that the decoder's bit reader reads them back in order across byte boundaries (`jpeg_bits_round_trip`); every symbol's code in the encoder's Annex K books (`encode.huff`), written between any codes, is decoded by the decoder's lookup (`decode.huff` over the table `decode.canon` builds) to that symbol, the reader left just after it (`jpeg_huff_dc`, `jpeg_huff_ac`); and `decode.run`, in the encoder's frame, scan and tables, returns a picture for any bits the encoder's writer (`encode.bit`, `encode.pad`) writes that are as many blocks as the frame has (`decode.nblocks`), each one the decoder reads whole: a DC code of the table with as many magnitude bits as its size, then AC codes of the table (EOB, ZRL with room for 16 zeros, or a run and nonzero size inside the block, with its magnitude bits) that end the block with EOB or at its 64th coefficient (`jpeg_scan_some`) | that `encode.arm`'s entropy-coded bytes are such bits: that the encoder's three paths (neutral, solid, `encode.go`) each write, through `encode.bits`, `encode.pack` and the pad, `ceil(w / 8) * ceil(h / 8)` MCUs of three blocks, each a DC code and magnitude and AC run and size codes and magnitudes of the tables ending with EOB or at 64 coefficients (DC differences below 2048 and AC coefficients below 1024 in magnitude, runs of at most 15 after ZRLs); and that for at most 2^31 samples this count, times 3, is `decode.nblocks` of the frame with no U32 wrap | | IMG-PIX-1 | The JPEG side. `Jpeg.rgb` and `Jpeg.gray` give alpha 255 for every input (`jpeg_rgb_opaque`, `jpeg_gray_opaque`); every sample `decode_jpeg` returns is packed by `Jpeg.gray`, or every one by `Jpeg.rgb` (`jpeg_decode_packed`); every sample it returns has alpha 255 (`jpeg_decode_opaque`); a gray sample carries its level's low byte in R, G and B (`jpeg_gray_level`) | that every sample `decode_png` returns is `0xAARRGGBB`: `png_walk` (IMG-PNG-9) reaches `decode_png` for files in the standard chunk order, not yet for files with ancillary chunks. `jpeg_decode_packed` does not say that the gray packing is the one chosen for a one-component frame | | IMG-PNG-2 | the one-block encoding: every raster `png_ok` accepts (both sides nonzero, `w * h` samples, `w * h` below 2^32) whose scanlines, `h * (1 + w * c)` bytes for c channels (3 when every sample is opaque, 4 otherwise), are at most 65535 decodes from its PNG to itself (`png_roundtrip_one`), through `png_walk`, `inflate_enc_zlib`, `unfilter_filter` with filter None, and `png_px_rgb` / `png_px_rgba` | the two wide paths `encode_png` takes past 65535 scanline bytes: `enc.seal.wide` (colour type 6, `enc.pour`) and `enc.wide.rgb` (colour type 2, `enc.rgb.go`) must be shown to write `zlib.stored(65535, raw)` with the IDAT CRC. And the row is false as worded, even with the approved bound of fewer than 2^32 samples: the scanline count `enc.nbytes` and the IDAT length are U32s, so a raster of 2^32 scanline bytes or more encodes to a file that does not decode (a decision for the maintainer) | | IMG-PNG-9 | `Png.px.of`, the pixel stage, packs unfiltered bytes as the row says for every colour type: gray, gray with a tRNS key, gray and alpha, RGB, RGB with a tRNS key, RGBA (`png_px_grey`, `png_px_grey_key`, `png_px_ga`, `png_px_rgb`, `png_px_rgb_key`, `png_px_rgba`), and each index it decodes as its palette entry with alpha from tRNS or 255 (`png_px_indexed`); the decoder's last stage is `px.of` of `Png.unfilter`'s output with the width and height kept (`png_samples_frame`); and the walker lift, `png_walk`: a file of the signature, IHDR (any nonzero width and height, bit depth 8, a colour type the row names, methods 0), PLTE and tRNS where section 11 allows them, any IDAT chunks and IEND, each chunk framed with its CRC-32 and shorter than 2^32 bytes, decodes to the raster `px.of` packs, for the IHDR colour type and the PLTE and tRNS data, from `Png.unfilter` of the concatenated IDAT data inflated | files whose chunks come in another order the decoder accepts: ancillary chunks (which the walker skips, closing the IDAT run) between the critical ones | diff --git a/proof/wp12-jpeg-huffman.bend b/proof/wp12-jpeg-huffman.bend index 153b2ad..cf6f273 100644 --- a/proof/wp12-jpeg-huffman.bend +++ b/proof/wp12-jpeg-huffman.bend @@ -1,5 +1,6 @@ # proof/wp12-jpeg-huffman: lemmas for IMG-JPG-3's Huffman round trip: the encoder's bit writer against the -# decoder's bit reader, and the Annex K tables the encoder's books hold against the ones the decoder builds. +# decoder's bit reader, the Annex K tables the encoder's books hold against the ones the decoder builds, and the +# decoder's scan loop over blocks of well-formed tokens (decode.run returns a picture). # PROOF.bend imports this file as W12, so the gate checks it. import Base import ../LAWS.bend as Laws @@ -9,18 +10,6 @@ import ./u32.bend as U32L import ./wp6-raster.bend as R import ./wp10-jpeg-finish.bend as W10 -# a bit as a U32: 0 or 1 -def ubit(bb: Bool) -> U32: - U32{WCon{bb, Word.zero(31n)}} - -# the low bit of a U32 -def bit0(xx: U32) -> Bool: - match xx: - case U32{ww}: - match ww: - case WCon{b0, _t}: - b0 - # a word shifted up one place with bb in the low bit, the top bit dropped def vstep(xx: U32, bb: Bool) -> U32: match xx: @@ -78,7 +67,7 @@ def and_zero_r(nn: Nat, ww: Word(nn)) -> {Word.and(nn, ww, Word.zero(nn)) == Wor Word.zero(pp), and_zero_r(pp, tt)) # the writer's step: shift up, or in the bit -def or_shl_u(xx: U32, bb: Bool) -> {U32.or(U32.shl(xx), ubit(bb)) == vstep(xx, bb) : U32}: +def or_shl_u(xx: U32, bb: Bool) -> {U32.or(U32.shl(xx), Laws.jpg.ubit(bb)) == vstep(xx, bb) : U32}: match xx: case U32{ww}: match ww: @@ -98,7 +87,7 @@ def or_u_shl.b( Equal.cong(Word(31n), U32, zz => U32{WCon{False{}, zz}}, Word.or(31n, Word.zero(31n), sp), sp, or_zero_l(31n, sp)) # the reader's step: the bit or-ed onto the shifted value -def or_u_shl(xx: U32, bb: Bool) -> {U32.or(ubit(bb), U32.shl(xx)) == vstep(xx, bb) : U32}: +def or_u_shl(xx: U32, bb: Bool) -> {U32.or(Laws.jpg.ubit(bb), U32.shl(xx)) == vstep(xx, bb) : U32}: match xx: case U32{ww}: match ww: @@ -119,7 +108,7 @@ def and_one.b( and_zero_r(31n, wt)) # and with 1 is the low bit -def and_one(xx: U32) -> {U32.and(xx, 1) == ubit(bit0(xx)) : U32}: +def and_one(xx: U32) -> {U32.and(xx, 1) == Laws.jpg.ubit(Laws.jpg.bit0(xx)) : U32}: match xx: case U32{ww}: match ww: @@ -234,26 +223,20 @@ def wbit( +pp: List<&2, Bool>, +bb: Bool, hh: {short(pp) == True{} : Bool} -) -> {Jenc.encode.bit(wst(out, pp), ubit(bb)) == wone(is7(pp), out, pp, bb) : Jenc.Put}: +) -> {Jenc.encode.bit(wst(out, pp), Laws.jpg.ubit(bb)) == wone(is7(pp), out, pp, bb) : Jenc.Put}: +nn = U32.from_nat(List.length(&2, Bool, pp)) +vv = vfold(pp, 0) - Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), U32.and(ubit(bb), 1)), nn), - Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), ubit(bb)), nn), wone(is7(pp), out, pp, bb), + Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), U32.and(Laws.jpg.ubit(bb), 1)), + nn), + Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), Laws.jpg.ubit(bb)), nn), wone(is7(pp), out, pp, bb), Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), xx), nn), - U32.and(ubit(bb), 1), ubit(bb), and_one(ubit(bb))), - Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), ubit(bb)), nn), + U32.and(Laws.jpg.ubit(bb), 1), Laws.jpg.ubit(bb), and_one(Laws.jpg.ubit(bb))), + Equal.trans(Jenc.Put, Jenc.encode.bit.n(U32.is_eq(nn, 7), out, U32.or(U32.shl(vv), Laws.jpg.ubit(bb)), nn), Jenc.encode.bit.n(U32.is_eq(nn, 7), out, vstep(vv, bb), nn), wone(is7(pp), out, pp, bb), - Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bit.n(U32.is_eq(nn, 7), out, xx, nn), U32.or(U32.shl(vv), ubit(bb)), + Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bit.n(U32.is_eq(nn, 7), out, xx, nn), U32.or(U32.shl(vv), + Laws.jpg.ubit(bb)), vstep(vv, bb), or_shl_u(vv, bb)), wbit.p(out, pp, bb, hh))) -# the encoder's bit writer fed bits one at a time -def feed(bs: List<&2, Bool>, pp: Jenc.Put) -> Jenc.Put: - match bs: - case Nil{}: - pp - case bb <> rest: - feed(rest, Jenc.encode.bit(pp, ubit(bb))) - # the model writer's state: the finished bytes as their bits (newest first) and the pending bits type WS is Data: WS{out: List<&2, List<&2, Bool>>, pp: List<&2, Bool>} @@ -318,10 +301,11 @@ def wbit.ws( ws: WS, +bb: Bool, hh: {wshort(ws) == True{} : Bool} -) -> {Jenc.encode.bit(wput(ws), ubit(bb)) == wput(wstep(ws, bb)) : Jenc.Put}: +) -> {Jenc.encode.bit(wput(ws), Laws.jpg.ubit(bb)) == wput(wstep(ws, bb)) : Jenc.Put}: match ws: case WS{+out, +pp}: - Equal.trans(Jenc.Put, Jenc.encode.bit(wst(obytes(out), pp), ubit(bb)), wone(is7(pp), obytes(out), pp, bb), + Equal.trans(Jenc.Put, Jenc.encode.bit(wst(obytes(out), pp), Laws.jpg.ubit(bb)), wone(is7(pp), obytes(out), pp, + bb), wput(wstep.b(is7(pp), out, pp, bb)), wbit(obytes(out), pp, bb, hh), wone.eq(is7(pp), out, pp, bb)) # after a step the pending bits are still fewer than 8, by the pending bits @@ -378,46 +362,41 @@ def wfeed( bs: List<&2, Bool>, +ws: WS, +hh: {wshort(ws) == True{} : Bool} -) -> {feed(bs, wput(ws)) == wput(wm(bs, ws)) : Jenc.Put}: +) -> {Laws.jpg.feed(bs, wput(ws)) == wput(wm(bs, ws)) : Jenc.Put}: match bs: case Nil{}: {==} case +bb <> rest: - Equal.trans(Jenc.Put, feed(rest, Jenc.encode.bit(wput(ws), ubit(bb))), feed(rest, wput(wstep(ws, bb))), - wput(wm(rest, wstep(ws, bb))), Equal.cong(Jenc.Put, Jenc.Put, qq => feed(rest, qq), - Jenc.encode.bit(wput(ws), ubit(bb)), wput(wstep(ws, bb)), wbit.ws(ws, bb, hh)), + Equal.trans(Jenc.Put, Laws.jpg.feed(rest, Jenc.encode.bit(wput(ws), Laws.jpg.ubit(bb))), Laws.jpg.feed(rest, + wput(wstep(ws, bb))), + wput(wm(rest, wstep(ws, bb))), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.feed(rest, qq), + Jenc.encode.bit(wput(ws), Laws.jpg.ubit(bb)), wput(wstep(ws, bb)), wbit.ws(ws, bb, hh)), wfeed(rest, wstep(ws, bb), wstep.short(ws, bb, hh))) -# the bits the encoder's writer takes from a code, top first: bit k - 1 down to bit 0 -def cb(kk: Nat, +code: U32) -> List<&2, Bool>: - match kk: - case 0n: - [] - case 1n+ +pp: - bit0(U32.shrn(code, pp)) <> cb(pp, code) - # the encoder's bit loop feeds the code's bits def bits_go( kk: Nat, pp: Jenc.Put, +code: U32 -) -> {Jenc.encode.bits.go(kk, pp, code) == feed(cb(kk, code), pp) : Jenc.Put}: +) -> {Jenc.encode.bits.go(kk, pp, code) == Laws.jpg.feed(Laws.jpg.cb(kk, code), pp) : Jenc.Put}: match kk: case 0n: {==} case 1n+ +qq: +bt = U32.shrn(code, qq) Equal.trans(Jenc.Put, Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, U32.and(bt, 1)), code), - Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, ubit(bit0(bt))), code), feed(cb(1n+qq, code), pp), + Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, Laws.jpg.ubit(Laws.jpg.bit0(bt))), code), + Laws.jpg.feed(Laws.jpg.cb(1n+qq, code), pp), Equal.cong(U32, Jenc.Put, xx => Jenc.encode.bits.go(qq, Jenc.encode.bit(pp, xx), code), U32.and(bt, 1), - ubit(bit0(bt)), and_one(bt)), bits_go(qq, Jenc.encode.bit(pp, ubit(bit0(bt))), code)) + Laws.jpg.ubit(Laws.jpg.bit0(bt)), and_one(bt)), bits_go(qq, Jenc.encode.bit(pp, + Laws.jpg.ubit(Laws.jpg.bit0(bt))), code)) # a code of at most 16 bits, written with its length and value, is its bits fed to the writer def bits_code( +cc: List<&2, Bool>, pp: Jenc.Put, hh: {Nat.is_le(List.length(&2, Bool, cc), 16n) == True{} : Bool} -) -> {Jenc.encode.bits(pp, U32.from_nat(List.length(&2, Bool, cc)), vfold(cc, 0)) == feed(cc, pp) : Jenc.Put}: +) -> {Jenc.encode.bits(pp, U32.from_nat(List.length(&2, Bool, cc)), vfold(cc, 0)) == Laws.jpg.feed(cc, pp) : Jenc.Put}: match cc: case Nil{}: bits_go(0n, pp, vfold([], 0)) @@ -511,13 +490,13 @@ def bits_code( vfold(h0 <> h1 <> h2 <> h3 <> h4 <> h5 <> h6 <> h7 <> h8 <> h9 <> h10 <> h11 <> h12 <> h13 <> h14 <> h15 <> _h16 <> _t16, - 0)) == feed(h0 <> h1 <> h2 <> h3 <> h4 <> h5 - <> h6 <> h7 <> h8 <> h9 <> h10 <> h11 <> h12 - <> h13 <> h14 <> h15 <> _h16 <> _t16, - pp) : Jenc.Put}, false_true(hh)) + 0)) == Laws.jpg.feed(h0 <> h1 <> h2 <> h3 <> + h4 <> h5 <> h6 <> h7 <> h8 <> h9 <> h10 <> + h11 <> h12 <> h13 <> h14 <> h15 <> _h16 <> + _t16, pp) : Jenc.Put}, false_true(hh)) # 1 or 0 is the bit as a U32 -def bitu_u(bb: Bool) -> {Laws.jpg.bitu(bb) == ubit(bb) : U32}: +def bitu_u(bb: Bool) -> {Laws.jpg.bitu(bb) == Laws.jpg.ubit(bb) : U32}: match bb: case True{}: {==} @@ -533,9 +512,10 @@ def num_v(bs: List<&2, Bool>, +acc: U32) -> {Laws.jpg.num(bs, acc) == vfold(bs, Equal.trans(U32, Laws.jpg.num(rest, U32.or(U32.shl(acc), Laws.jpg.bitu(bb))), Laws.jpg.num(rest, vstep(acc, bb)), vfold(rest, vstep(acc, bb)), Equal.cong(U32, U32, xx => Laws.jpg.num(rest, xx), U32.or(U32.shl(acc), Laws.jpg.bitu(bb)), vstep(acc, bb), - Equal.trans(U32, U32.or(U32.shl(acc), Laws.jpg.bitu(bb)), U32.or(U32.shl(acc), ubit(bb)), vstep(acc, bb), - Equal.cong(U32, U32, xx => U32.or(U32.shl(acc), xx), Laws.jpg.bitu(bb), ubit(bb), bitu_u(bb)), or_shl_u(acc, - bb))), + Equal.trans(U32, U32.or(U32.shl(acc), Laws.jpg.bitu(bb)), U32.or(U32.shl(acc), Laws.jpg.ubit(bb)), vstep(acc, + bb), + Equal.cong(U32, U32, xx => U32.or(U32.shl(acc), xx), Laws.jpg.bitu(bb), Laws.jpg.ubit(bb), bitu_u(bb)), + or_shl_u(acc, bb))), num_v(rest, vstep(acc, bb))) # feeding two lists is feeding their join @@ -543,12 +523,12 @@ def feed_app( xs: List<&2, Bool>, +ys: List<&2, Bool>, pp: Jenc.Put -) -> {feed(List.append(&2, Bool, xs, ys), pp) == feed(ys, feed(xs, pp)) : Jenc.Put}: +) -> {Laws.jpg.feed(List.append(&2, Bool, xs, ys), pp) == Laws.jpg.feed(ys, Laws.jpg.feed(xs, pp)) : Jenc.Put}: match xs: case Nil{}: {==} case bb <> rest: - feed_app(rest, ys, Jenc.encode.bit(pp, ubit(bb))) + feed_app(rest, ys, Jenc.encode.bit(pp, Laws.jpg.ubit(bb))) # the first answer of a true also def also_l(ok: Bool, -rest: Bool, hh: {Laws.jpg.also(ok, rest) == True{} : Bool}) -> {ok == True{} : Bool}: @@ -571,7 +551,7 @@ def wcodes_feed( cs: List<&2, List<&2, Bool>>, +pp: Jenc.Put, +hh: {Laws.jpg.codes16(cs) == True{} : Bool} -) -> {Laws.jpg.write(cs, pp) == feed(List.concat(&2, Bool, cs), pp) : Jenc.Put}: +) -> {Laws.jpg.write(cs, pp) == Laws.jpg.feed(List.concat(&2, Bool, cs), pp) : Jenc.Put}: match cs: case Nil{}: {==} @@ -580,16 +560,22 @@ def wcodes_feed( {Nat.is_le(List.length(&2, Bool, cc), 16n) == True{} : Bool}} +nn = U32.from_nat(List.length(&2, Bool, cc)) Equal.trans(Jenc.Put, Laws.jpg.write(rest, Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0))), Laws.jpg.write(rest, - feed(cc, pp)), - feed(List.concat(&2, Bool, cc <> rest), pp), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(rest, qq), - Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0)), feed(cc, pp), Equal.trans(Jenc.Put, Jenc.encode.bits(pp, nn, - Laws.jpg.num(cc, 0)), - Jenc.encode.bits(pp, nn, vfold(cc, 0)), feed(cc, pp), Equal.cong(U32, Jenc.Put, vv => Jenc.encode.bits(pp, nn, + Laws.jpg.feed(cc, pp)), + Laws.jpg.feed(List.concat(&2, Bool, cc <> rest), pp), Equal.cong(Jenc.Put, Jenc.Put, + qq => Laws.jpg.write(rest, qq), + Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0)), Laws.jpg.feed(cc, pp), Equal.trans(Jenc.Put, + Jenc.encode.bits(pp, nn, Laws.jpg.num(cc, 0)), + Jenc.encode.bits(pp, nn, vfold(cc, 0)), Laws.jpg.feed(cc, pp), Equal.cong(U32, Jenc.Put, + vv => Jenc.encode.bits(pp, nn, vv), Laws.jpg.num(cc, 0), vfold(cc, 0), num_v(cc, 0)), bits_code(cc, pp, h1))), - Equal.trans(Jenc.Put, Laws.jpg.write(rest, feed(cc, pp)), feed(List.concat(&2, Bool, rest), feed(cc, pp)), - feed(List.concat(&2, Bool, cc <> rest), pp), wcodes_feed(rest, feed(cc, pp), also_r(Nat.is_le(List.length(&2, - Bool, cc), 16n), Laws.jpg.codes16(rest), hh)), Equal.sym(Jenc.Put, feed(List.concat(&2, Bool, cc <> rest), pp), - feed(List.concat(&2, Bool, rest), feed(cc, pp)), feed_app(cc, List.concat(&2, Bool, rest), pp)))) + Equal.trans(Jenc.Put, Laws.jpg.write(rest, Laws.jpg.feed(cc, pp)), Laws.jpg.feed(List.concat(&2, Bool, rest), + Laws.jpg.feed(cc, pp)), + Laws.jpg.feed(List.concat(&2, Bool, cc <> rest), pp), wcodes_feed(rest, Laws.jpg.feed(cc, pp), + also_r(Nat.is_le(List.length(&2, + Bool, cc), 16n), Laws.jpg.codes16(rest), hh)), Equal.sym(Jenc.Put, Laws.jpg.feed(List.concat(&2, Bool, + cc <> rest), pp), + Laws.jpg.feed(List.concat(&2, Bool, rest), Laws.jpg.feed(cc, pp)), feed_app(cc, List.concat(&2, Bool, rest), + pp)))) # the last byte's bits: the pending bits, then 1 bits to fill the byte; none when nothing is pending def pendo(pp: List<&2, Bool>) -> List<&2, List<&2, Bool>>: @@ -895,14 +881,15 @@ def rstep.q( case Nil{}: Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, U32.is_eq(U32.from_nat(List.length(&2, Bool, [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), - U32.from_nat(List.length(&2, Bool, [])), rbuf([]), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + U32.from_nat(List.length(&2, Bool, [])), rbuf([]), ok, xx), U32.or(Laws.jpg.ubit(q0), U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q1 <> t1: match t1: case Nil{}: Equal.cong(U32, Jpeg.Bits & U32, xx => Jpeg.decode.nbits(left, U32.is_eq(U32.from_nat(List.length(&2, Bool, q1 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), - U32.from_nat(List.length(&2, Bool, q1 <> [])), rbuf(q1 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + U32.from_nat(List.length(&2, Bool, q1 <> [])), rbuf(q1 <> []), ok, xx), U32.or(Laws.jpg.ubit(q0), + U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q2 <> t2: match t2: @@ -911,8 +898,8 @@ def rstep.q( Bool, q1 <> q2 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), - U32.from_nat(List.length(&2, Bool, q1 <> q2 <> [])), rbuf(q1 <> q2 <> []), ok, xx), U32.or(ubit(q0), - U32.shl(acc)), + U32.from_nat(List.length(&2, Bool, q1 <> q2 <> [])), rbuf(q1 <> q2 <> []), ok, xx), + U32.or(Laws.jpg.ubit(q0), U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q3 <> t3: match t3: @@ -922,7 +909,7 @@ def rstep.q( q1 <> q2 <> q3 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> [])), rbuf(q1 <> q2 <> q3 <> []), ok, xx), - U32.or(ubit(q0), U32.shl(acc)), + U32.or(Laws.jpg.ubit(q0), U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q4 <> t4: match t4: @@ -932,7 +919,7 @@ def rstep.q( q1 <> q2 <> q3 <> q4 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> [])), - rbuf(q1 <> q2 <> q3 <> q4 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + rbuf(q1 <> q2 <> q3 <> q4 <> []), ok, xx), U32.or(Laws.jpg.ubit(q0), U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q5 <> t5: match t5: @@ -942,7 +929,7 @@ def rstep.q( q1 <> q2 <> q3 <> q4 <> q5 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> q5 <> [])), - rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> []), ok, xx), U32.or(Laws.jpg.ubit(q0), U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case +q6 <> t6: match t6: @@ -952,7 +939,8 @@ def rstep.q( q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> [])), 0), rxs(os), False{}, Jpeg.decode.head.is(rxs(os), 255), Jpeg.decode.head.is(rxs(os), 0), U32.from_nat(List.length(&2, Bool, q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> [])), - rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> []), ok, xx), U32.or(ubit(q0), U32.shl(acc)), + rbuf(q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> []), ok, xx), U32.or(Laws.jpg.ubit(q0), + U32.shl(acc)), vstep(acc, q0), or_u_shl(acc, q0)) case _q7 <> t7: Empty.absurd({canon(1n+left, RS{q0 <> q1 <> q2 <> q3 <> q4 <> q5 <> q6 <> _q7 <> t7, @@ -1037,7 +1025,7 @@ def rstep.o( xx => Jpeg.decode.nbits(left, False{}, rxs(ot), False{}, Jpeg.decode.head.is(rxs(ot), 255), Jpeg.decode.head.is(rxs(ot), 0), 7, rbuf(o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> []), ok, xx), - U32.or(ubit(o0), U32.shl(acc)), vstep(acc, o0), or_u_shl(acc, o0))) + U32.or(Laws.jpg.ubit(o0), U32.shl(acc)), vstep(acc, o0), or_u_shl(acc, o0))) case _o8 <> t8: Empty.absurd({canon(1n+left, RS{[], (o0 <> o1 <> o2 <> o3 <> o4 <> o5 <> o6 <> o7 <> _o8 <> t8) <> ot}, ok, @@ -1679,11 +1667,13 @@ def round_trip( Jenc.encode.put0()))}) == (Laws.jpg.nums(cs), 1) : List<&2, U32> & U32}: +bs = List.concat(&2, Bool, cs) +os = moct(bs, []) - +ep = {Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())), Jenc.encode.pad(feed(bs, + +ep = {Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())), + Jenc.encode.pad(Laws.jpg.feed(bs, Jenc.encode.put0())), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.write(cs, - Jenc.encode.put0()), feed(bs, Jenc.encode.put0()), wcodes_feed(cs, Jenc.encode.put0(), hc)), - Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], []}))), - rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + Jenc.encode.put0()), Laws.jpg.feed(bs, Jenc.encode.put0()), wcodes_feed(cs, Jenc.encode.put0(), hc)), + Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, + WS{[], []}))), + rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.feed(bs, wput(WS{[], []})), wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : {Jenc.encode.pad(Laws.jpg.write(cs, Jenc.encode.put0())) == rxs(os) : List<&2, U32>}} Equal.trans(List<&2, U32> & U32, Laws.jpg.reads(cs, Jpeg.Bits{0, 1, 0, Jenc.encode.pad(Laws.jpg.write(cs, @@ -1748,7 +1738,7 @@ def ask.of( ) -> {Jpeg.decode.ask.bit(Jpeg.Huff{cs, ls, ys}, (bits, vstep(0, bb)), code, len) == Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, vstep(code, bb), (len + 1 : U32)), vstep(code, bb), (len + 1 : U32), bits} : Jpeg.Ask}: Equal.cong(U32, Jpeg.Ask, xx => Jpeg.Ask{Jpeg.decode.look(cs, ls, ys, xx, (len + 1 : U32)), xx, (len + 1 : U32), - bits}, U32.or(U32.shl(code), ubit(bb)), vstep(code, bb), or_shl_u(code, bb)) + bits}, U32.or(U32.shl(code), Laws.jpg.ubit(bb)), vstep(code, bb), or_shl_u(code, bb)) # one bit asked of the table from the model reader def ask_step( @@ -2229,10 +2219,6 @@ def hw.is(rr: HW, +ss: U32, +nn: Nat) -> Bool: case HWMiss{}: False{} -# the bits of a book word: its length in the high half, its code in the low half -def cbw(+ww: U32) -> List<&2, Bool>: - cb(U32.to_nat(U32.shrn(ww, 16n)), U32.and(ww, 65535)) - # a symbol's code, at most 16 bits, walked over the table in groups, hits the symbol at its last bit def hwchk(+cc: List<&2, Bool>, +gs: List<&2, HG>, +ss: U32) -> Bool: Bool.and(Nat.is_le(List.length(&2, Bool, cc), 16n), hw.is(hwg(cc, gs, 0, 0), ss, List.length(&2, Bool, cc))) @@ -2323,7 +2309,7 @@ def dcgrp.wf() -> {wfg(dcgrp()) == True{} : Bool}: # a symbol's code in the dc book def code.dc(+sy: U32) -> List<&2, Bool>: - cbw(Laws.jpg.val(Array.get(U32, dcbook.lit(), sy))) + Laws.jpg.cbw(Laws.jpg.val(Array.get(U32, dcbook.lit(), sy))) # the check for one symbol: its book code hits it def chk.dc(+ss: U32) -> Bool: @@ -2506,7 +2492,7 @@ def acgrp.wf() -> {wfg(acgrp()) == True{} : Bool}: # a symbol's code in the ac book def code.ac(+sy: U32) -> List<&2, Bool>: - cbw(Laws.jpg.val(Array.get(U32, acbook.lit(), sy))) + Laws.jpg.cbw(Laws.jpg.val(Array.get(U32, acbook.lit(), sy))) # the check for one symbol: its book code hits it def chk.ac(+ss: U32) -> Bool: @@ -2712,10 +2698,11 @@ def skip_m( def emit_feed.dc( +sy: U32, +pp: Jenc.Put -) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), pp)) == feed(code.dc(sy), pp) : Jenc.Put}: +) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), pp)) == Laws.jpg.feed(code.dc(sy), + pp) : Jenc.Put}: +vv = Laws.jpg.val(Array.get(U32, dcbook.lit(), sy)) Equal.trans(Jenc.Put, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(Array.get(U32, dcbook.lit(), sy)), pp)), - Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((dcbook.lit(), vv)), pp)), feed(code.dc(sy), pp), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((dcbook.lit(), vv)), pp)), Laws.jpg.feed(code.dc(sy), pp), Equal.cong(Array & U32, Jenc.Put, gg => Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(gg), pp)), Array.get(U32, dcbook.lit(), sy), (dcbook.lit(), vv), W10.get.eq(dcbook.lit(), sy)), bits_go(U32.to_nat( U32.shrn(vv, 16n)), pp, U32.and(vv, 65535))) @@ -2800,26 +2787,33 @@ def sym_rt.dc( +mp = mpad(bs, []) +p0 = Jenc.encode.put0() +ew = {Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), - Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, feed(cc, - Laws.jpg.write(pre, p0))), feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), - Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), Laws.jpg.write(pre, p0))), feed(cc, + Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, Laws.jpg.feed(cc, + Laws.jpg.write(pre, p0))), Laws.jpg.feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), Laws.jpg.write(pre, p0))), Laws.jpg.feed(cc, Laws.jpg.write(pre, p0)), emit_feed.dc(sy, Laws.jpg.write(pre, p0))), - Equal.trans(Jenc.Put, Laws.jpg.write(post, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, - Laws.jpg.write(pre, p0))), feed(bs, p0), wcodes_feed(post, feed(cc, Laws.jpg.write(pre, p0)), hq), - Equal.trans(Jenc.Put, feed(cq, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, feed(cp, p0))), feed(bs, p0), - Equal.cong(Jenc.Put, Jenc.Put, qq => feed(cq, feed(cc, qq)), Laws.jpg.write(pre, p0), feed(cp, p0), - wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, feed(cq, feed(cc, feed(cp, p0))), feed(List.append(&2, Bool, cc, - cq), feed(cp, p0)), feed(bs, p0), Equal.sym(Jenc.Put, feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed(cq, - feed(cc, feed(cp, p0))), feed_app(cc, cq, feed(cp, p0))), Equal.sym(Jenc.Put, feed(bs, p0), - feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), p0)))))) : + Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0))), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, + Laws.jpg.write(pre, p0))), Laws.jpg.feed(bs, p0), wcodes_feed(post, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0)), hq), + Equal.trans(Jenc.Put, Laws.jpg.feed(cq, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0))), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), Laws.jpg.feed(bs, p0), + Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.feed(cq, Laws.jpg.feed(cc, qq)), Laws.jpg.write(pre, p0), + Laws.jpg.feed(cp, p0), + wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, Laws.jpg.feed(cq, Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), + Laws.jpg.feed(List.append(&2, Bool, cc, + cq), Laws.jpg.feed(cp, p0)), Laws.jpg.feed(bs, p0), Equal.sym(Jenc.Put, Laws.jpg.feed(List.append(&2, Bool, cc, + cq), Laws.jpg.feed(cp, p0)), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), feed_app(cc, cq, Laws.jpg.feed(cp, p0))), Equal.sym(Jenc.Put, + Laws.jpg.feed(bs, p0), + Laws.jpg.feed(List.append(&2, Bool, cc, cq), Laws.jpg.feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), + p0)))))) : {Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), Laws.jpg.write(pre, - p0)))) == feed(bs, p0) : Jenc.Put}} + p0)))) == Laws.jpg.feed(bs, p0) : Jenc.Put}} +eb = {Equal.trans(List<&2, U32>, Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post), - Jenc.encode.pad(feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => + Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(dcbook.lit(), sy), - Laws.jpg.write(pre, p0)))), feed(bs, p0), ew), - Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], - []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + Laws.jpg.write(pre, p0)))), Laws.jpg.feed(bs, p0), ew), + Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], + []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.feed(bs, wput(WS{[], []})), wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : {Laws.jpg.sym.bytes(pre, dcbook.lit(), sy, post) == rxs(os) : List<&2, U32>}} +s0 = {RS{[], os} : RS} @@ -2854,10 +2848,11 @@ def huff_law.dc( def emit_feed.ac( +sy: U32, +pp: Jenc.Put -) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), pp)) == feed(code.ac(sy), pp) : Jenc.Put}: +) -> {Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), pp)) == Laws.jpg.feed(code.ac(sy), + pp) : Jenc.Put}: +vv = Laws.jpg.val(Array.get(U32, acbook.lit(), sy)) Equal.trans(Jenc.Put, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(Array.get(U32, acbook.lit(), sy)), pp)), - Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((acbook.lit(), vv)), pp)), feed(code.ac(sy), pp), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded((acbook.lit(), vv)), pp)), Laws.jpg.feed(code.ac(sy), pp), Equal.cong(Array & U32, Jenc.Put, gg => Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.coded(gg), pp)), Array.get(U32, acbook.lit(), sy), (acbook.lit(), vv), W10.get.eq(acbook.lit(), sy)), bits_go(U32.to_nat( U32.shrn(vv, 16n)), pp, U32.and(vv, 65535))) @@ -2942,26 +2937,33 @@ def sym_rt.ac( +mp = mpad(bs, []) +p0 = Jenc.encode.put0() +ew = {Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), - Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, feed(cc, - Laws.jpg.write(pre, p0))), feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), - Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), Laws.jpg.write(pre, p0))), feed(cc, + Laws.jpg.write(pre, p0)))), Laws.jpg.write(post, Laws.jpg.feed(cc, + Laws.jpg.write(pre, p0))), Laws.jpg.feed(bs, p0), Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.write(post, qq), + Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), Laws.jpg.write(pre, p0))), Laws.jpg.feed(cc, Laws.jpg.write(pre, p0)), emit_feed.ac(sy, Laws.jpg.write(pre, p0))), - Equal.trans(Jenc.Put, Laws.jpg.write(post, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, - Laws.jpg.write(pre, p0))), feed(bs, p0), wcodes_feed(post, feed(cc, Laws.jpg.write(pre, p0)), hq), - Equal.trans(Jenc.Put, feed(cq, feed(cc, Laws.jpg.write(pre, p0))), feed(cq, feed(cc, feed(cp, p0))), feed(bs, p0), - Equal.cong(Jenc.Put, Jenc.Put, qq => feed(cq, feed(cc, qq)), Laws.jpg.write(pre, p0), feed(cp, p0), - wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, feed(cq, feed(cc, feed(cp, p0))), feed(List.append(&2, Bool, cc, - cq), feed(cp, p0)), feed(bs, p0), Equal.sym(Jenc.Put, feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed(cq, - feed(cc, feed(cp, p0))), feed_app(cc, cq, feed(cp, p0))), Equal.sym(Jenc.Put, feed(bs, p0), - feed(List.append(&2, Bool, cc, cq), feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), p0)))))) : + Equal.trans(Jenc.Put, Laws.jpg.write(post, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0))), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, + Laws.jpg.write(pre, p0))), Laws.jpg.feed(bs, p0), wcodes_feed(post, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0)), hq), + Equal.trans(Jenc.Put, Laws.jpg.feed(cq, Laws.jpg.feed(cc, Laws.jpg.write(pre, p0))), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), Laws.jpg.feed(bs, p0), + Equal.cong(Jenc.Put, Jenc.Put, qq => Laws.jpg.feed(cq, Laws.jpg.feed(cc, qq)), Laws.jpg.write(pre, p0), + Laws.jpg.feed(cp, p0), + wcodes_feed(pre, p0, hp)), Equal.trans(Jenc.Put, Laws.jpg.feed(cq, Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), + Laws.jpg.feed(List.append(&2, Bool, cc, + cq), Laws.jpg.feed(cp, p0)), Laws.jpg.feed(bs, p0), Equal.sym(Jenc.Put, Laws.jpg.feed(List.append(&2, Bool, cc, + cq), Laws.jpg.feed(cp, p0)), Laws.jpg.feed(cq, + Laws.jpg.feed(cc, Laws.jpg.feed(cp, p0))), feed_app(cc, cq, Laws.jpg.feed(cp, p0))), Equal.sym(Jenc.Put, + Laws.jpg.feed(bs, p0), + Laws.jpg.feed(List.append(&2, Bool, cc, cq), Laws.jpg.feed(cp, p0)), feed_app(cp, List.append(&2, Bool, cc, cq), + p0)))))) : {Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), Laws.jpg.write(pre, - p0)))) == feed(bs, p0) : Jenc.Put}} + p0)))) == Laws.jpg.feed(bs, p0) : Jenc.Put}} +eb = {Equal.trans(List<&2, U32>, Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post), - Jenc.encode.pad(feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => + Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.write(post, Laws.jpg.emit(Jenc.encode.emit(Jenc.encode.sym(acbook.lit(), sy), - Laws.jpg.write(pre, p0)))), feed(bs, p0), ew), - Equal.trans(List<&2, U32>, Jenc.encode.pad(feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], - []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), feed(bs, wput(WS{[], []})), + Laws.jpg.write(pre, p0)))), Laws.jpg.feed(bs, p0), ew), + Equal.trans(List<&2, U32>, Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], + []}))), rxs(os), Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.feed(bs, wput(WS{[], []})), wput(wm(bs, WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) : {Laws.jpg.sym.bytes(pre, acbook.lit(), sy, post) == rxs(os) : List<&2, U32>}} +s0 = {RS{[], os} : RS} @@ -2992,3 +2994,891 @@ def huff_law.ac( U32 & (List<&2, U32> & U32) & U32, tb => Laws.jpg.sym.back(pre, acbook.lit(), tb, sy, post), cn, actab(), actab.eq()), sym_rt.ac(pre, hp, sy, hy, post, hq))) +# the bits of AC tokens: each symbol's code in the AC book, then its magnitude bits +def acbits(ts: List<&2, Laws.JTok>) -> List<&2, Bool>: + match ts: + case Nil{}: + [] + case Laws.JTok{+sym, ex} <> rest: + List.append(&2, Bool, code.ac(sym), List.append(&2, Bool, ex, acbits(rest))) + +# the AC table the decoder holds, in groups +def actb() -> Jpeg.Huff: + actab() + +# the reader and the ok flag of the decoder's AC state +def acbo(xx: Jpeg.Ac) -> Jpeg.Bits & U32: + match xx: + case Jpeg.Ac{_k, _zz, bits, _d, +ok}: + (bits, ok) + +# the decoder's AC loop ended with a model reader holding tail bits rr, its ok flag 1 +def AcRes(-xx: Jpeg.Ac, +rr: List<&2, Bool>) -> Type: + &se: RS -> &e1: {acbo(xx) == (rbits(se, 1), 1) : Jpeg.Bits & U32} -> &e2: {rem(se) == rr : List<&2, Bool>} -> + {Bool.and(rshort(se), roct8(se)) == True{} : Bool} + +# an ended AC loop stays as it is +def acdone( + pp: Nat, + +kk: U32, + +zz: List<&2, U32>, + +bits: Jpeg.Bits, + +ok: U32 +) -> {Jpeg.decode.ac(pp, Jpeg.Ac{kk, zz, bits, 1, ok}, actb()) == Jpeg.Ac{kk, zz, bits, 1, ok} : Jpeg.Ac}: + match pp: + case 0n: + {==} + case 1n+_q: + {==} + +# an ended AC loop with the model's reader is a result +def acres.done( + +pp: Nat, + +kk: U32, + +zz: List<&2, U32>, + +se: RS, + +rr: List<&2, Bool>, + e2: {rem(se) == rr : List<&2, Bool>}, + e3: {Bool.and(rshort(se), roct8(se)) == True{} : Bool} +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.Ac{kk, zz, rbits(se, 1), 1, 1}, actb()), rr): + ea = Equal.sym(Jpeg.Ac, Jpeg.decode.ac(pp, Jpeg.Ac{kk, zz, rbits(se, 1), 1, 1}, actb()), Jpeg.Ac{kk, zz, + rbits(se, 1), 1, 1}, acdone(pp, kk, zz, rbits(se, 1), 1)) + %ea : AcRes(_, rr) + (se, {==}, e2, e3) + +# no token can follow the end of a block +def acwf.fin( + ts: List<&2, Laws.JTok>, + +kk: U32, + +left: Nat, + hw: {Laws.jpg.acwf(ts, Laws.JAcSt{kk, left, True{}}) == True{} : Bool} +) -> {ts == [] : List<&2, Laws.JTok>}: + match ts: + case Nil{}: + {==} + case tt <> _rest: + match tt: + case Laws.JTok{_s, _e}: + Empty.absurd({Laws.JTok{_s, _e} <> _rest == [] : List<&2, Laws.JTok>}, false_true(hw)) + +# an empty list of bits +def isnil.eq(xs: List<&2, Bool>, hh: {Laws.jpg.isnil(xs) == True{} : Bool}) -> {xs == [] : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case _h <> _t: + Empty.absurd({_h <> _t == [] : List<&2, Bool>}, false_true(hh)) + +# the rest of the AC loop, as a token's step needs it +def IhAc(-rest: List<&2, Laws.JTok>, +pp: Nat, +rr: List<&2, Bool>) -> Type: + @kk: U32 -> @zz: List<&2, U32> -> @ss: RS -> @hs: {rshort(ss) == True{} : Bool} -> + @h8: {roct8(ss) == True{} : Bool} -> @he: {rem(ss) == List.append(&2, Bool, acbits(rest), rr) : + List<&2, Bool>} -> @hw: {Laws.jpg.acwf(rest, Laws.JAcSt{kk, pp, False{}}) == True{} : Bool} -> + AcRes(Jpeg.decode.ac(pp, Jpeg.Ac{kk, zz, rbits(ss, 1), 0, 1}, actb()), rr) + +# the tail after an empty list of magnitude bits and no tokens is the tail +def tail.nil( + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + xe: {ex == [] : List<&2, Bool>}, + re: {rest == [] : List<&2, Laws.JTok>} +) -> {List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) == rr : List<&2, Bool>}: + Equal.trans(List<&2, Bool>, List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)), List.append(&2, Bool, + acbits(rest), rr), rr, Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.append(&2, Bool, xs, + List.append(&2, Bool, acbits(rest), rr)), ex, [], xe), Equal.cong(List<&2, Laws.JTok>, List<&2, Bool>, ts => + List.append(&2, Bool, acbits(ts), rr), rest, [], re)) + +# the tail after an empty list of magnitude bits +def tail.ex( + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + xe: {ex == [] : List<&2, Bool>} +) -> {List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) == List.append(&2, Bool, acbits(rest), + rr) : List<&2, Bool>}: + Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.append(&2, Bool, xs, List.append(&2, Bool, acbits(rest), rr)), + ex, [], xe) + +# an EOB token ends the block +def sim.eob( + +pp: Nat, + +kk: U32, + +zz: List<&2, U32>, + +s1: RS, + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, + hk: {Laws.jpg.isnil(ex) == True{} : Bool}, + hr: {Laws.jpg.acwf(rest, Laws.JAcSt{kk, pp, True{}}) == True{} : Bool} +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.Ac{kk, zz, rbits(s1, 1), 1, 1}, actb()), rr): + acres.done(pp, kk, zz, s1, rr, Equal.trans(List<&2, Bool>, rem(s1), List.append(&2, Bool, ex, List.append(&2, + Bool, acbits(rest), rr)), rr, er, tail.nil(ex, rest, rr, isnil.eq(ex, hk), acwf.fin(rest, kk, pp, hr))), f1) + +# a ZRL token that reaches index 64 ends the block +def sim.zrl2( + c2: Bool, + +pp: Nat, + +kk: U32, + +zz: List<&2, U32>, + +s1: RS, + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, + +hk: {Bool.and(Laws.jpg.isnil(ex), Bool.or(False{}, c2)) == True{} : Bool}, + hr: {Laws.jpg.acwf(rest, Laws.JAcSt{(kk + 16 : U32), pp, True{}}) == True{} : Bool} +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.zrl.eq(c2, zz, rbits(s1, 1), 1), actb()), rr): + match c2: + case True{}: + acres.done(pp, 64, zz, s1, rr, Equal.trans(List<&2, Bool>, rem(s1), List.append(&2, Bool, ex, + List.append(&2, Bool, acbits(rest), rr)), rr, er, tail.nil(ex, rest, rr, isnil.eq(ex, and_l(Laws.jpg.isnil(ex), + True{}, hk)), acwf.fin(rest, (kk + 16 : U32), pp, hr))), f1) + case False{}: + Empty.absurd(AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.zrl.eq(False{}, zz, rbits(s1, 1), 1), actb()), rr), + false_true(Equal.trans(Bool, False{}, Bool.and(Laws.jpg.isnil(ex), False{}), True{}, Equal.sym(Bool, + Bool.and(Laws.jpg.isnil(ex), False{}), False{}, R.and_false_r(Laws.jpg.isnil(ex))), hk))) + +# a ZRL token: sixteen zeros, then the loop goes on or the block ends at index 64 +def sim.zrl( + c1: Bool, + c2: Bool, + +pp: Nat, + +kk: U32, + +zz: List<&2, U32>, + +s1: RS, + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + +f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + +er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, + h2: {U32.is_eq((kk + 16 : U32), 64) == c2 : Bool}, + +hk: {Bool.and(Laws.jpg.isnil(ex), Bool.or(c1, c2)) == True{} : Bool}, + hr: {Laws.jpg.acwf(rest, Laws.JAcSt{(kk + 16 : U32), pp, Bool.not(c1)}) == True{} : Bool}, + ih: IhAc(rest, pp, rr) +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.zrl.k(c1, (kk + 16 : U32), zz, rbits(s1, 1), 1), actb()), rr): + match c1: + case True{}: + ih((kk + 16 : U32), zz, s1, and_l(rshort(s1), roct8(s1), f1), and_r(rshort(s1), roct8(s1), f1), + Equal.trans(List<&2, Bool>, rem(s1), List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)), + List.append(&2, Bool, acbits(rest), rr), er, tail.ex(ex, rest, rr, isnil.eq(ex, and_l(Laws.jpg.isnil(ex), + True{}, + hk)))), hr) + case False{}: + sh = Equal.sym(Bool, U32.is_eq((kk + 16 : U32), 64), c2, h2) + %sh : AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.zrl.eq(_, zz, rbits(s1, 1), 1), actb()), rr) + sim.zrl2(c2, pp, kk, zz, s1, ex, rest, rr, f1, er, hk, hr) + +# a stored coefficient: the loop goes on, or the block ends at index 64 +def sim.st( + c3: Bool, + +pp: Nat, + +n2: U32, + +zz: List<&2, U32>, + +s2: RS, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + +f2: {Bool.and(rshort(s2), roct8(s2)) == True{} : Bool}, + +e2: {rem(s2) == List.append(&2, Bool, acbits(rest), rr) : List<&2, Bool>}, + hr: {Laws.jpg.acwf(rest, Laws.JAcSt{n2, pp, c3}) == True{} : Bool}, + ih: IhAc(rest, pp, rr) +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.stored(c3, n2, zz, rbits(s2, 1), 1), actb()), rr): + match c3: + case True{}: + acres.done(pp, n2, zz, s2, rr, Equal.trans(List<&2, Bool>, rem(s2), List.append(&2, Bool, acbits(rest), rr), + rr, e2, Equal.cong(List<&2, Laws.JTok>, List<&2, Bool>, ts => List.append(&2, Bool, acbits(ts), rr), rest, [], + acwf.fin(rest, n2, pp, hr))), f2) + case False{}: + ih(n2, zz, s2, and_l(rshort(s2), roct8(s2), f2), and_r(rshort(s2), roct8(s2), f2), e2, hr) + +# a run and size token's own check, by the two kinds it is not +def RunOk(-c1: Bool, -c2: Bool, +sym: U32, -ex: List<&2, Bool>) -> Type: + {Bool.and(Bool.not(c1), Bool.and(Bool.not(c2), Nat.is_eq(List.length(&2, Bool, ex), U32.to_nat(U32.and(15, + sym))))) == True{} : Bool} + +# the tokens after a run and size token, read in full from the index after it +def RunRest(-rest: List<&2, Laws.JTok>, +kk: U32, +sym: U32, +pp: Nat) -> Type: + {Laws.jpg.acwf(rest, Laws.JAcSt{((kk + U32.shrn(sym, 4n) : U32) + 1 : U32), pp, U32.is_eq(((kk + U32.shrn(sym, + 4n) : U32) + 1 : + U32), 64)}) == True{} : Bool} + +# a run and size token: its magnitude bits read, the coefficient stored at its index +def sim.run( + c1: Bool, + c2: Bool, + +pp: Nat, + +sym: U32, + +kk: U32, + +zz: List<&2, U32>, + +s1: RS, + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + +f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + +er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, + +hk: RunOk(c1, c2, sym, ex), + +hr: RunRest(rest, kk, sym, pp), + ih: IhAc(rest, pp, rr) +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.run.b(c1, c2, U32.and(15, sym), (kk + U32.shrn(sym, 4n) : U32), rbits(s1, + 1), zz, 1), actb()), rr): + match c1: + case True{}: + Empty.absurd(AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.run.b(True{}, c2, U32.and(15, sym), (kk + U32.shrn(sym, + 4n) : U32), rbits(s1, 1), zz, 1), actb()), rr), false_true(hk)) + case False{}: + match c2: + case True{}: + Empty.absurd(AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.run.b(False{}, True{}, U32.and(15, sym), (kk + + U32.shrn(sym, 4n) : U32), rbits(s1, 1), zz, 1), actb()), rr), false_true(hk)) + case False{}: + +sz = U32.and(15, sym) + +nk = (kk + U32.shrn(sym, 4n) : U32) + +ln = List.length(&2, Bool, ex) + +mr = List.append(&2, Bool, acbits(rest), rr) + +s2 = radv(ln, s1) + +hs1 = {and_l(rshort(s1), roct8(s1), f1) : {rshort(s1) == True{} : Bool}} + +h81 = {and_r(rshort(s1), roct8(s1), f1) : {roct8(s1) == True{} : Bool}} + +el = {R.nat_eq_true(ln, U32.to_nat(sz), hk) : {ln == U32.to_nat(sz) : Nat}} + +hl = {Equal.trans(Bool, Nat.is_le(ln, List.length(&2, Bool, rem(s1))), Nat.is_le(ln, List.length(&2, + Bool, List.append(&2, Bool, ex, mr))), True{}, Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(ln, + List.length(&2, Bool, xs)), rem(s1), List.append(&2, Bool, ex, mr), er), len_app(ex, mr)) : + {Nat.is_le(ln, List.length(&2, Bool, rem(s1))) == True{} : Bool}} + +vv = vfold(List.take(&2, Bool, rem(s1), ln), 0) + +erd = {Equal.trans(Jpeg.Bits & U32, Jpeg.decode.read.n(sz, rbits(s1, 1)), canon(U32.to_nat(sz), s1, + 1, 0), (rbits(s2, 1), vv), read_canon(sz, s1, 1), Equal.trans(Jpeg.Bits & U32, + canon(U32.to_nat(sz), s1, 1, 0), canon(ln, s1, 1, 0), (rbits(s2, 1), vv), Equal.cong(Nat, + Jpeg.Bits & U32, mm => canon(mm, s1, 1, 0), U32.to_nat(sz), ln, Equal.sym(Nat, ln, U32.to_nat(sz), + el)), rread(ln, s1, 1, 0, hs1, h81, hl))) : {Jpeg.decode.read.n(sz, rbits(s1, 1)) == + (rbits(s2, 1), vv) : Jpeg.Bits & U32}} + sr = Equal.sym(Jpeg.Bits & U32, Jpeg.decode.read.n(sz, rbits(s1, 1)), (rbits(s2, 1), vv), erd) + %sr : AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.store(_, sz, nk, zz, 1), actb()), rr) + sim.st(U32.is_eq((nk + 1 : U32), 64), pp, (nk + 1 : U32), List.set(&2, U32, zz, U32.to_nat(nk), + Jpeg.decode.extend(sz, vv)), s2, rest, rr, radv_ok(ln, s1, hs1, h81), rem_after(ex, s1, mr, hs1, h81, + er), hr, ih) + +# one token's step, by its kind +def sim.k( + e0: Bool, + e2: Bool, + +pp: Nat, + +sym: U32, + +kk: U32, + +zz: List<&2, U32>, + +s1: RS, + +ex: List<&2, Bool>, + +rest: List<&2, Laws.JTok>, + +rr: List<&2, Bool>, + +f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + +er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, + +hk: {Laws.jpg.acok.k(e0, e2, sym, ex, kk) == True{} : Bool}, + +hr: {Laws.jpg.acwf(rest, Laws.jpg.acnx.k(e0, e2, sym, kk, 1n+pp)) == True{} : Bool}, + ih: IhAc(rest, pp, rr) +) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.sym.b(e0, e2, sym, rbits(s1, 1), kk, zz, 1), actb()), rr): + match e0: + case True{}: + sim.eob(pp, kk, zz, s1, ex, rest, rr, f1, er, hk, hr) + case False{}: + match e2: + case True{}: + sim.zrl(U32.is_lt((kk + 16 : U32), 64), U32.is_eq((kk + 16 : U32), 64), pp, kk, zz, s1, ex, rest, rr, f1, er, + {==}, hk, hr, ih) + case False{}: + sim.run(U32.is_eq(U32.and(15, sym), 0), U32.is_ge((kk + U32.shrn(sym, 4n) : U32), 64), pp, sym, kk, zz, s1, + ex, rest, rr, f1, er, hk, hr, ih) + +# the decoder's AC loop over AC tokens' bits: every token read, the reader left at the tail, ok still 1 +def sim( + ts: List<&2, Laws.JTok>, + +left: Nat, + +kk: U32, + +zz: List<&2, U32>, + +ss: RS, + +rr: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, acbits(ts), rr) : List<&2, Bool>}, + +hw: {Laws.jpg.acwf(ts, Laws.JAcSt{kk, left, False{}}) == True{} : Bool} +) -> AcRes(Jpeg.decode.ac(left, Jpeg.Ac{kk, zz, rbits(ss, 1), 0, 1}, actb()), rr): + match ts: + case Nil{}: + Empty.absurd(AcRes(Jpeg.decode.ac(left, Jpeg.Ac{kk, zz, rbits(ss, 1), 0, 1}, actb()), rr), false_true(hw)) + case +tt <> +rest: + match tt: + case Laws.JTok{+sym, +ex}: + match left: + case 0n: + Empty.absurd(AcRes(Jpeg.decode.ac(0n, Jpeg.Ac{kk, zz, rbits(ss, 1), 0, 1}, actb()), rr), + false_true(hw)) + case 1n+ +pp: + +e0 = U32.is_eq(sym, 0) + +e2 = U32.is_eq(sym, 240) + +ha = {also_l(Laws.jpg.acok(Laws.JTok{sym, ex}, Laws.JAcSt{kk, 1n+pp, False{}}), Laws.jpg.acwf(rest, + Laws.jpg.acnx(Laws.JTok{sym, ex}, Laws.JAcSt{kk, 1n+pp, + False{}})), hw) : {Bool.and(Laws.jpg.memb(sym, Jenc.encode.acsyms()), Laws.jpg.acok.k(e0, e2, sym, ex, + kk)) == + True{} : Bool}} + +hr = {also_r(Laws.jpg.acok(Laws.JTok{sym, ex}, Laws.JAcSt{kk, 1n+pp, False{}}), Laws.jpg.acwf(rest, + Laws.jpg.acnx(Laws.JTok{sym, ex}, Laws.JAcSt{kk, 1n+pp, + False{}})), hw) : {Laws.jpg.acwf(rest, Laws.jpg.acnx.k(e0, e2, sym, kk, 1n+pp)) == True{} : Bool}} + +cc = code.ac(sym) + +tl = List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) + +he1 = {Equal.trans(List<&2, Bool>, rem(ss), List.append(&2, Bool, List.append(&2, Bool, cc, + List.append(&2, Bool, ex, acbits(rest))), rr), List.append(&2, Bool, cc, tl), he, + Equal.trans(List<&2, Bool>, List.append(&2, Bool, List.append(&2, Bool, cc, List.append(&2, Bool, ex, + acbits(rest))), rr), List.append(&2, Bool, cc, List.append(&2, Bool, List.append(&2, Bool, ex, + acbits(rest)), rr)), List.append(&2, Bool, cc, tl), app_assoc(cc, List.append(&2, Bool, ex, + acbits(rest)), rr), Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.append(&2, Bool, cc, xs), + List.append(&2, Bool, List.append(&2, Bool, ex, acbits(rest)), rr), tl, app_assoc(ex, acbits(rest), + rr)))) : {rem(ss) == List.append(&2, Bool, cc, tl) : List<&2, Bool>}} + +s1 = radv(List.length(&2, Bool, cc), ss) + +eh = {huff.ac(ss, 1, sym, tl, hs, h8, he1, memb.ac(Jenc.encode.acsyms(), sym, and_l(Laws.jpg.memb(sym, + Jenc.encode.acsyms()), Laws.jpg.acok.k(e0, e2, sym, ex, kk), ha), all.ac.ok())) : {Jpeg.decode.huff(16n, + Jpeg.Ask{None{}, 0, 0, rbits(ss, 1)}, actb()) == Jpeg.Hit{sym, rbits(s1, 1), 1} : Jpeg.Hit}} + sh = Equal.sym(Jpeg.Hit, Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, 1)}, actb()), + Jpeg.Hit{sym, rbits(s1, 1), 1}, eh) + %sh : AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.step(_, kk, zz, 1), actb()), rr) + sim.k(e0, e2, pp, sym, kk, zz, s1, ex, rest, rr, radv_ok(List.length(&2, Bool, cc), ss, hs, h8), + rem_after(cc, ss, tl, hs, h8, he1), and_r(Laws.jpg.memb(sym, Jenc.encode.acsyms()), + Laws.jpg.acok.k(e0, e2, + sym, ex, kk), ha), hr, k2 => z2 => s2 => a2 => b2 => c2 => d2 => sim(rest, pp, k2, z2, s2, rr, a2, b2, + c2, d2)) + +# the DC table the decoder holds, in groups +def dctb() -> Jpeg.Huff: + dctab() + +# the quantisation table the decoder holds for the encoder's frame: 64 ones +def ones64() -> List<&2, U32>: + List.replicate(U32, 64n, 1) + +# a block's bits: the DC symbol's code in the DC book, its magnitude bits, and the AC tokens' bits +def blkbits(bt: Laws.JBlkTok) -> List<&2, Bool>: + match bt: + case Laws.JBlkTok{+dcs, dcx, acs}: + List.append(&2, Bool, code.dc(dcs), List.append(&2, Bool, dcx, acbits(acs))) + +# the reader and the ok flag of a decoded block +def blkbo(bk: Jpeg.Blk) -> Jpeg.Bits & U32: + match bk: + case Jpeg.Blk{_s, bits, _p, +ok}: + (bits, ok) + +# a reader and a flag, the flag times qo +def bomul(bo: Jpeg.Bits & U32, +qo: U32) -> Jpeg.Bits & U32: + match bo: + case (bits, +ok): + (bits, (ok * qo : U32)) + +# a decoded block ended with a model reader holding tail bits rr, its ok flag 1 +def BlkRes(-bk: Jpeg.Blk, +rr: List<&2, Bool>) -> Type: + &se: RS -> &e1: {blkbo(bk) == (rbits(se, 1), 1) : Jpeg.Bits & U32} -> &e2: {rem(se) == rr : List<&2, Bool>} + -> {Bool.and(rshort(se), roct8(se)) == True{} : Bool} + +# the block from its AC loop keeps the loop's reader, its flag times the quantisation flag +def blk.ac.eq( + xx: Jpeg.Ac, + +pred: U32, + +qq: List<&2, U32>, + +qo: U32 +) -> {blkbo(Jpeg.decode.block.ac(xx, pred, qq, qo)) == bomul(acbo(xx), qo) : Jpeg.Bits & U32}: + match xx: + case Jpeg.Ac{_k, _zz, _bits, _d, _ok}: + {==} + +# a block from an AC loop that ended well +def blk.from.ac( + xx: Jpeg.Ac, + +pred: U32, + +rr: List<&2, Bool>, + res: AcRes(xx, rr) +) -> BlkRes(Jpeg.decode.block.ac(xx, pred, ones64(), Jpeg.decode.qok(ones64())), rr): + (se, e1, e2, e3) = res + +qo = Jpeg.decode.qok(ones64()) + (se, Equal.trans(Jpeg.Bits & U32, blkbo(Jpeg.decode.block.ac(xx, pred, ones64(), qo)), bomul(acbo(xx), qo), + (rbits(se, 1), 1), blk.ac.eq(xx, pred, ones64(), qo), Equal.cong(Jpeg.Bits & U32, Jpeg.Bits & U32, bo => + bomul(bo, qo), acbo(xx), (rbits(se, 1), 1), e1)), e2, e3) + +# a list of length zero is empty +def len0( + xs: List<&2, Bool>, + hh: {Nat.is_eq(List.length(&2, Bool, xs), 0n) == True{} : Bool} +) -> {xs == [] : List<&2, Bool>}: + match xs: + case Nil{}: + {==} + case _h <> _t: + Empty.absurd({_h <> _t == [] : List<&2, Bool>}, false_true(hh)) + +# the DC magnitude, none for size 0, then the AC loop +def bsim.z( + zz: Bool, + +dcs: U32, + +dcx: List<&2, Bool>, + +acs: List<&2, Laws.JTok>, + +pred: U32, + +s1: RS, + +rr: List<&2, Bool>, + +f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, + +er: {rem(s1) == List.append(&2, Bool, dcx, List.append(&2, Bool, acbits(acs), rr)) : List<&2, Bool>}, + +hz: {U32.is_eq(dcs, 0) == zz : Bool}, + +hl: {Nat.is_eq(List.length(&2, Bool, dcx), U32.to_nat(dcs)) == True{} : Bool}, + +hw: {Laws.jpg.acwf(acs, Laws.JAcSt{1, 63n, False{}}) == True{} : Bool} +) -> BlkRes(Jpeg.decode.block.cat(zz, dcs, rbits(s1, 1), pred, 1, actb(), ones64(), Jpeg.decode.qok(ones64())), rr): + match zz: + case True{}: + +hs1 = {and_l(rshort(s1), roct8(s1), f1) : {rshort(s1) == True{} : Bool}} + +h81 = {and_r(rshort(s1), roct8(s1), f1) : {roct8(s1) == True{} : Bool}} + +mr = List.append(&2, Bool, acbits(acs), rr) + +dz = {U32L.ueq(dcs, 0, hz) : {dcs == 0 : U32}} + +hl0 = {Equal.trans(Bool, Nat.is_eq(List.length(&2, Bool, dcx), U32.to_nat(0)), Nat.is_eq(List.length(&2, Bool, + dcx), U32.to_nat(dcs)), True{}, Equal.cong(U32, Bool, xx => Nat.is_eq(List.length(&2, Bool, dcx), + U32.to_nat(xx)), 0, dcs, Equal.sym(U32, dcs, 0, dz)), hl) : {Nat.is_eq(List.length(&2, Bool, dcx), 0n) == + True{} : Bool}} + +e1 = {Equal.trans(List<&2, Bool>, rem(s1), List.append(&2, Bool, dcx, mr), mr, er, Equal.cong(List<&2, Bool>, + List<&2, Bool>, xs => List.append(&2, Bool, xs, mr), dcx, [], len0(dcx, hl0))) : {rem(s1) == mr : + List<&2, Bool>}} + +dc = (pred + Jpeg.decode.extend(0, 0) : U32) + blk.from.ac(Jpeg.decode.ac(63n, Jpeg.Ac{1, dc <> Jpeg.decode.zeros(63n, []), rbits(s1, 1), 0, 1}, actb()), + dc, rr, sim(acs, 63n, 1, dc <> Jpeg.decode.zeros(63n, []), s1, rr, hs1, h81, e1, hw)) + case False{}: + +hs1 = {and_l(rshort(s1), roct8(s1), f1) : {rshort(s1) == True{} : Bool}} + +h81 = {and_r(rshort(s1), roct8(s1), f1) : {roct8(s1) == True{} : Bool}} + +mr = List.append(&2, Bool, acbits(acs), rr) + +ln = List.length(&2, Bool, dcx) + +s2 = radv(ln, s1) + +el = {R.nat_eq_true(ln, U32.to_nat(dcs), hl) : {ln == U32.to_nat(dcs) : Nat}} + +hl2 = {Equal.trans(Bool, Nat.is_le(ln, List.length(&2, Bool, rem(s1))), Nat.is_le(ln, List.length(&2, + Bool, List.append(&2, Bool, dcx, mr))), True{}, Equal.cong(List<&2, Bool>, Bool, xs => Nat.is_le(ln, + List.length(&2, Bool, xs)), rem(s1), List.append(&2, Bool, dcx, mr), er), len_app(dcx, mr)) : + {Nat.is_le(ln, List.length(&2, Bool, rem(s1))) == True{} : Bool}} + +vv = vfold(List.take(&2, Bool, rem(s1), ln), 0) + +erd = {Equal.trans(Jpeg.Bits & U32, Jpeg.decode.read.n(dcs, rbits(s1, 1)), canon(U32.to_nat(dcs), s1, 1, + 0), (rbits(s2, 1), vv), read_canon(dcs, s1, 1), Equal.trans(Jpeg.Bits & U32, canon(U32.to_nat(dcs), + s1, 1, 0), canon(ln, s1, 1, 0), (rbits(s2, 1), vv), Equal.cong(Nat, Jpeg.Bits & U32, mm => + canon(mm, s1, 1, 0), U32.to_nat(dcs), ln, Equal.sym(Nat, ln, U32.to_nat(dcs), el)), rread(ln, s1, 1, 0, + hs1, h81, hl2))) : {Jpeg.decode.read.n(dcs, rbits(s1, 1)) == (rbits(s2, 1), vv) : Jpeg.Bits & U32}} + +dc = (pred + Jpeg.decode.extend(dcs, vv) : U32) + sr = Equal.sym(Jpeg.Bits & U32, Jpeg.decode.read.n(dcs, rbits(s1, 1)), (rbits(s2, 1), vv), erd) + %sr : BlkRes(Jpeg.decode.block.diff(_, dcs, pred, 1, actb(), ones64(), Jpeg.decode.qok(ones64())), rr) + blk.from.ac(Jpeg.decode.ac(63n, Jpeg.Ac{1, dc <> Jpeg.decode.zeros(63n, []), rbits(s2, 1), 0, 1}, actb()), + dc, rr, sim(acs, 63n, 1, dc <> Jpeg.decode.zeros(63n, []), s2, rr, and_l(rshort(s2), roct8(s2), + radv_ok(ln, s1, hs1, h81)), and_r(rshort(s2), roct8(s2), radv_ok(ln, s1, hs1, h81)), + rem_after(dcx, s1, mr, hs1, h81, er), hw)) + +# the decoder reads one block's bits whole: the reader left at the tail, ok still 1 +def bsim( + bt: Laws.JBlkTok, + +pred: U32, + +ss: RS, + +rr: List<&2, Bool>, + +hs: {rshort(ss) == True{} : Bool}, + +h8: {roct8(ss) == True{} : Bool}, + +he: {rem(ss) == List.append(&2, Bool, blkbits(bt), rr) : List<&2, Bool>}, + +hw: {Laws.jpg.blkwf(bt) == True{} : Bool} +) -> BlkRes(Jpeg.decode.block.go(rbits(ss, 1), pred, dctb(), actb(), ones64()), rr): + match bt: + case Laws.JBlkTok{+dcs, +dcx, +acs}: + +cc = code.dc(dcs) + +tl = List.append(&2, Bool, dcx, List.append(&2, Bool, acbits(acs), rr)) + +he1 = {Equal.trans(List<&2, Bool>, rem(ss), List.append(&2, Bool, List.append(&2, Bool, cc, List.append(&2, + Bool, dcx, acbits(acs))), rr), List.append(&2, Bool, cc, tl), he, Equal.trans(List<&2, Bool>, + List.append(&2, Bool, List.append(&2, Bool, cc, List.append(&2, Bool, dcx, acbits(acs))), rr), + List.append(&2, Bool, cc, List.append(&2, Bool, List.append(&2, Bool, dcx, acbits(acs)), rr)), + List.append(&2, Bool, cc, tl), app_assoc(cc, List.append(&2, Bool, dcx, acbits(acs)), rr), + Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.append(&2, Bool, cc, xs), List.append(&2, Bool, + List.append(&2, Bool, dcx, acbits(acs)), rr), tl, app_assoc(dcx, acbits(acs), rr)))) : {rem(ss) == + List.append(&2, Bool, cc, tl) : List<&2, Bool>}} + +ha = {and_r(Laws.jpg.memb(dcs, Jenc.encode.dcsyms()), Bool.and(Nat.is_eq(List.length(&2, Bool, dcx), + U32.to_nat(dcs)), Laws.jpg.acwf(acs, Laws.JAcSt{1, 63n, False{}})), hw) : {Bool.and(Nat.is_eq(List.length(&2, + Bool, dcx), + U32.to_nat(dcs)), Laws.jpg.acwf(acs, Laws.JAcSt{1, 63n, False{}})) == True{} : Bool}} + +s1 = radv(List.length(&2, Bool, cc), ss) + +eh = {huff.dc(ss, 1, dcs, tl, hs, h8, he1, memb.dc(Jenc.encode.dcsyms(), dcs, and_l(Laws.jpg.memb(dcs, + Jenc.encode.dcsyms()), Bool.and(Nat.is_eq(List.length(&2, Bool, dcx), U32.to_nat(dcs)), Laws.jpg.acwf(acs, + Laws.JAcSt{1, + 63n, False{}})), hw), all.dc.ok())) : {Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, 1)}, + dctb()) == Jpeg.Hit{dcs, rbits(s1, 1), 1} : Jpeg.Hit}} + sh = Equal.sym(Jpeg.Hit, Jpeg.decode.huff(16n, Jpeg.Ask{None{}, 0, 0, rbits(ss, 1)}, dctb()), Jpeg.Hit{dcs, + rbits(s1, 1), 1}, eh) + %sh : BlkRes(Jpeg.decode.block.dc(_, pred, actb(), ones64(), Jpeg.decode.qok(ones64())), rr) + bsim.z(U32.is_eq(dcs, 0), dcs, dcx, acs, pred, s1, rr, radv_ok(List.length(&2, Bool, cc), ss, hs, h8), + rem_after(cc, ss, tl, hs, h8, he1), {==}, and_l(Nat.is_eq(List.length(&2, Bool, dcx), U32.to_nat(dcs)), + Laws.jpg.acwf(acs, Laws.JAcSt{1, 63n, False{}}), ha), and_r(Nat.is_eq(List.length(&2, Bool, dcx), + U32.to_nat(dcs)), + Laws.jpg.acwf(acs, Laws.JAcSt{1, 63n, False{}}), ha)) + +# the tables the decoder holds after the encoder's header, the Huffman tables in groups +def tabsg() -> Jpeg.Tabs: + Jpeg.Tabs{List.replicate(U32, 64n, 1), [], [], [], dctb(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), + Jpeg.decode.huff0(), actb(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.huff0()} + +# the decoder's tables after the encoder's header are the ones in groups +def tabs.eq() -> {Laws.jpg.enc.tabs() == tabsg() : Jpeg.Tabs}: + Equal.trans(Jpeg.Tabs, Laws.jpg.enc.tabs(), Jpeg.Tabs{List.replicate(U32, 64n, 1), [], [], [], dctb(), + Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.canon(272n, 0n, + Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), Jpeg.decode.huff0(), Jpeg.decode.huff0(), + Jpeg.decode.huff0()}, tabsg(), Equal.cong(Jpeg.Huff, Jpeg.Tabs, hh => Jpeg.Tabs{List.replicate(U32, 64n, 1), [], + [], [], hh, Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.canon(272n, 0n, + Jenc.encode.accounts(), Jenc.encode.acsyms(), 0, 0, [], [], []), Jpeg.decode.huff0(), Jpeg.decode.huff0(), + Jpeg.decode.huff0()}, Jpeg.decode.canon(272n, 0n, Jenc.encode.dccounts(), Jenc.encode.dcsyms(), 0, 0, [], [], []), + dctb(), dctab.eq()), Equal.cong(Jpeg.Huff, Jpeg.Tabs, hh => Jpeg.Tabs{List.replicate(U32, 64n, 1), [], [], + [], dctb(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), Jpeg.decode.huff0(), hh, Jpeg.decode.huff0(), + Jpeg.decode.huff0(), Jpeg.decode.huff0()}, Jpeg.decode.canon(272n, 0n, Jenc.encode.accounts(), + Jenc.encode.acsyms(), 0, 0, [], [], []), actb(), actab.eq())) + +# an entry of [0, 0, 0] at a natural index, or its default +def at000.n(nn: Nat) -> {Jpeg.decode.at.m(List.get(&2, U32, [0, 0, 0], nn)) == 0 : U32}: + match nn: + case 0n: + {==} + case 1n+0n: + {==} + case 1n+1n+0n: + {==} + case 1n+1n+1n+_m: + {==} + +# every entry of [0, 0, 0] is 0, and so is its default +def at000(+cc: U32) -> {Jpeg.decode.at([0, 0, 0], cc) == 0 : U32}: + at000.n(U32.to_nat(cc)) + +# a block of the encoder's frame and scan reads with the tables in groups and the quantisation table of ones +def bof( + +bits: Jpeg.Bits, + +cc: U32, + +preds: Jpeg.Preds, + +ww: U32, + +hh: U32 +) -> {Jpeg.decode.block.of(bits, cc, preds, Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), + tabsg()) == Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(preds, cc), dctb(), actb(), ones64()) : Jpeg.Blk}: + +ez = at000(cc) + +ei = at000(Jpeg.decode.index([1, 2, 3], False{}, Jpeg.decode.at([1, 2, 3], cc), 0)) + ez1 = Equal.sym(U32, Jpeg.decode.at([0, 0, 0], cc), 0, ez) + ei1 = Equal.sym(U32, Jpeg.decode.at([0, 0, 0], Jpeg.decode.index([1, 2, 3], False{}, Jpeg.decode.at([1, 2, 3], cc), + 0)), 0, ei) + %ez1 : {Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(preds, cc), Jpeg.decode.dc.get(tabsg(), _), + Jpeg.decode.ac.get(tabsg(), _), Jpeg.decode.q.get(tabsg(), Jpeg.decode.at([0, 0, 0], Jpeg.decode.index([1, 2, 3], + False{}, Jpeg.decode.at([1, 2, 3], cc), 0)))) == Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(preds, cc), + dctb(), actb(), ones64()) : Jpeg.Blk} + %ei1 : {Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(preds, cc), Jpeg.decode.dc.get(tabsg(), 0), + Jpeg.decode.ac.get(tabsg(), 0), Jpeg.decode.q.get(tabsg(), _)) == Jpeg.decode.block.go(bits, + Jpeg.decode.pred.get(preds, cc), dctb(), actb(), ones64()) : Jpeg.Blk} + {==} + +# the next block after a step of the walk: the component the step names, read with the tables in groups +def NextBlk(-aa: Jpeg.Adv, +bits: Jpeg.Bits, +preds: Jpeg.Preds, +pred: U32, +cc: U32, +ww: U32, +hh: U32) -> Type: + {Jpeg.decode.block.next.go(aa, bits, preds, pred, cc, Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), tabsg()) == + Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(Jpeg.decode.preds.next(0, preds, cc, pred), + Jpeg.decode.ctrl.comp(Jpeg.decode.adv.ctrl(aa))), dctb(), actb(), ones64()) : Jpeg.Blk} + +# the walk to the next MCU, in its row or the next: component 0, no restart marker +def nx.mx( + inb: Bool, + +mcu: U32, + +mx: U32, + +my: U32, + +rst: U32, + +bits: Jpeg.Bits, + +preds: Jpeg.Preds, + +pred: U32, + +cc: U32, + +ww: U32, + +hh: U32 +) -> NextBlk(Jpeg.decode.adv.mx(inb, mcu, mx, my, rst, 0), bits, preds, pred, cc, ww, hh): + match inb: + case True{}: + bof(bits, 0, Jpeg.decode.preds.next(0, preds, cc, pred), ww, hh) + case False{}: + bof(bits, 0, Jpeg.decode.preds.next(0, preds, cc, pred), ww, hh) + +# the walk to the next component, or to the next MCU +def nx.comp( + more: Bool, + +cc: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +rst: U32, + +bits: Jpeg.Bits, + +preds: Jpeg.Preds, + +pred: U32, + +ww: U32, + +hh: U32 +) -> NextBlk(Jpeg.decode.adv.comp(more, cc, mx, my, mcu, rst, ww, 1, 0), bits, preds, pred, cc, ww, hh): + match more: + case True{}: + bof(bits, (cc + 1 : U32), Jpeg.decode.preds.next(0, preds, cc, pred), ww, hh) + case False{}: + nx.mx(U32.is_lt((mx + 1 : U32), Jpeg.decode.ceil(ww, (1 * 8 : U32))), (mcu + 1 : U32), (mx + 1 : U32), my, rst, + bits, preds, pred, cc, ww, hh) + +# the walk to the next data unit, or to the next component +def nx.bi( + more: Bool, + +cc: U32, + +bi: U32, + +mx: U32, + +my: U32, + +mcu: U32, + +rst: U32, + +bits: Jpeg.Bits, + +preds: Jpeg.Preds, + +pred: U32, + +ww: U32, + +hh: U32 +) -> NextBlk(Jpeg.decode.adv.bi(more, cc, bi, mx, my, mcu, rst, ww, 1, 3, 0), bits, preds, pred, cc, ww, hh): + match more: + case True{}: + bof(bits, cc, Jpeg.decode.preds.next(0, preds, cc, pred), ww, hh) + case False{}: + nx.comp(U32.is_lt((cc + 1 : U32), 3), cc, mx, my, mcu, rst, bits, preds, pred, ww, hh) + +# with no restart interval, the decoder's next block is the next component's, read with the tables in groups +def bnext( + +bits: Jpeg.Bits, + +preds: Jpeg.Preds, + +pred: U32, + ctrl: Jpeg.Ctrl, + +ww: U32, + +hh: U32 +) -> {Jpeg.decode.block.next(bits, preds, pred, ctrl, Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), tabsg(), + 0) == Jpeg.decode.block.go(bits, Jpeg.decode.pred.get(Jpeg.decode.preds.next(0, preds, Jpeg.decode.ctrl.comp(ctrl), + pred), Jpeg.decode.ctrl.comp(Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ctrl, Laws.jpg.enc.frame(ww, hh), + Laws.jpg.enc.scan(), 0)))), dctb(), actb(), ones64()) : Jpeg.Blk}: + match ctrl: + case Jpeg.Ctrl{+cc, +bi, +mx, +my, +mcu, +rst}: + +fi = Jpeg.decode.index([1, 2, 3], False{}, Jpeg.decode.at([1, 2, 3], cc), 0) + +hi = Jpeg.decode.at([1, 1, 1], fi) + +vi = Jpeg.decode.at([1, 1, 1], fi) + nx.bi(U32.is_lt((bi + 1 : U32), (hi * vi : U32)), cc, bi, mx, my, mcu, rst, bits, preds, pred, ww, hh) + +# the blocks' bits, one after another +def tbits(ts: List<&2, Laws.JBlkTok>) -> List<&2, Bool>: + match ts: + case Nil{}: + [] + case tt <> rest: + List.append(&2, Bool, blkbits(tt), tbits(rest)) + +# the flag of a reader and a flag +def bo.ok(bo: Jpeg.Bits & U32) -> U32: + match bo: + case (_b, +ok): + ok + +# the reader of a reader and a flag +def bo.bits(bo: Jpeg.Bits & U32) -> Jpeg.Bits: + match bo: + case (bits, _o): + bits + +# a block result moved along an equality of blocks +def blkres.tr( + -aa: Jpeg.Blk, + -bb: Jpeg.Blk, + +rr: List<&2, Bool>, + ee: {aa == bb : Jpeg.Blk}, + res: BlkRes(aa, rr) +) -> BlkRes(bb, rr): + %ee : BlkRes(_, rr) + res + +# the decoder's block loop over blocks that each read whole returns a picture +def fl( + ts: List<&2, Laws.JBlkTok>, + blk: Jpeg.Blk, + ctrl: Jpeg.Ctrl, + +preds: Jpeg.Preds, + +ww: U32, + +hh: U32, + yy: Array, + cb: Array, + cr: Array, + +tl: List<&2, Bool>, + res: BlkRes(blk, List.append(&2, Bool, tbits(ts), tl)), + +hw: {Laws.jpg.allwf(ts) == True{} : Bool} +) -> {Laws.jpg.psome(Jpeg.decode.blocks(List.length(&2, Laws.JBlkTok, ts), blk, ctrl, preds, Laws.jpg.enc.frame(ww, + hh), Laws.jpg.enc.scan(), tabsg(), 0, yy, cb, cr, 1)) == True{} : Bool}: + match ts: + case Nil{}: + match blk: + case Jpeg.Blk{+ss, +bb, +pd, +oo}: + (+se, e1, _e2, _e3) = res + eo = Equal.sym(U32, oo, 1, Equal.cong(Jpeg.Bits & U32, U32, xx => bo.ok(xx), (bb, oo), (rbits(se, 1), 1), + e1)) + %eo : {Laws.jpg.psome(Jpeg.decode.blocks(0n, Jpeg.Blk{ss, bb, pd, _}, ctrl, preds, Laws.jpg.enc.frame(ww, hh), + Laws.jpg.enc.scan(), tabsg(), 0, yy, cb, cr, 1)) == True{} : Bool} + {==} + case +tt <> +rest: + match blk: + case Jpeg.Blk{+ss, +bb, +pd, +oo}: + match ctrl: + case Jpeg.Ctrl{+cc, +bi, +mx, +my, +mcu, +rst}: + (+se, e1x, e2, e3x) = res + +e1 = {e1x : {(bb, oo) == (rbits(se, 1), 1) : Jpeg.Bits & U32}} + +e3 = {e3x : {Bool.and(rshort(se), roct8(se)) == True{} : Bool}} + +ct = {Jpeg.Ctrl{cc, bi, mx, my, mcu, rst} : Jpeg.Ctrl} + +fr = Laws.jpg.enc.frame(ww, hh) + +sc = Laws.jpg.enc.scan() + eo = Equal.sym(U32, oo, 1, Equal.cong(Jpeg.Bits & U32, U32, xx => bo.ok(xx), (bb, oo), (rbits(se, 1), + 1), e1)) + eb = Equal.sym(Jpeg.Bits, bb, rbits(se, 1), Equal.cong(Jpeg.Bits & U32, Jpeg.Bits, xx => bo.bits(xx), + (bb, oo), (rbits(se, 1), 1), e1)) + +hs = {and_l(rshort(se), roct8(se), e3) : {rshort(se) == True{} : Bool}} + +h8 = {and_r(rshort(se), roct8(se), e3) : {roct8(se) == True{} : Bool}} + +mr = List.append(&2, Bool, tbits(rest), tl) + +he = {Equal.trans(List<&2, Bool>, rem(se), List.append(&2, Bool, List.append(&2, Bool, + blkbits(tt), tbits(rest)), tl), List.append(&2, Bool, blkbits(tt), mr), e2, + app_assoc(blkbits(tt), tbits(rest), tl)) : {rem(se) == List.append(&2, Bool, blkbits(tt), + mr) : List<&2, Bool>}} + +pn = Jpeg.decode.pred.get(Jpeg.decode.preds.next(0, preds, cc, pd), Jpeg.decode.ctrl.comp( + Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ct, fr, sc, 0)))) + +nb = Jpeg.decode.block.next(rbits(se, 1), preds, pd, ct, fr, sc, tabsg(), 0) + +gb = Jpeg.decode.block.go(rbits(se, 1), pn, dctb(), actb(), ones64()) + +en = {bnext(rbits(se, 1), preds, pd, ct, ww, hh) : {nb == gb : Jpeg.Blk}} + %eo : {Laws.jpg.psome(Jpeg.decode.blocks(1n+List.length(&2, Laws.JBlkTok, rest), Jpeg.Blk{ss, bb, pd, + _}, ct, preds, fr, + sc, tabsg(), 0, yy, cb, cr, 1)) == True{} : Bool} + %eb : {Laws.jpg.psome(Jpeg.decode.blocks(1n+List.length(&2, Laws.JBlkTok, rest), Jpeg.Blk{ss, _, pd, 1}, + ct, preds, fr, + sc, tabsg(), 0, yy, cb, cr, 1)) == True{} : Bool} + fl(rest, nb, Jpeg.decode.adv.ctrl(Jpeg.decode.adv(ct, fr, sc, 0)), Jpeg.decode.preds.next( + Jpeg.decode.adv.duef(Jpeg.decode.adv(ct, fr, sc, 0)), preds, cc, pd), ww, hh, + Jpeg.decode.paint.use(Jpeg.decode.geom(ct, fr, sc), U32.is_eq(cc, 0), ss, yy), + Jpeg.decode.paint.use(Jpeg.decode.geom(ct, fr, sc), U32.is_eq(cc, 1), ss, cb), + Jpeg.decode.paint.use(Jpeg.decode.geom(ct, fr, sc), U32.is_eq(cc, 2), ss, cr), tl, + blkres.tr(gb, nb, mr, Equal.sym(Jpeg.Blk, nb, gb, en), bsim(tt, pn, se, mr, hs, h8, he, + also_l(Laws.jpg.blkwf(tt), Laws.jpg.allwf(rest), hw))), also_r(Laws.jpg.blkwf(tt), + Laws.jpg.allwf(rest), hw)) + +# the frame's block count is the number of blocks +def NBlk(+ww: U32, +hh: U32, -rest: List<&2, Laws.JBlkTok>) -> Type: + {U32.to_nat(Jpeg.decode.nblocks(ww, hh, [1, 1, 1], [1, 1, 1], 1, 1)) == 1n+List.length(&2, Laws.JBlkTok, rest) : Nat} + +# the decoder's scan decode, in the encoder's frame, of bytes holding blocks that each read whole, as many as the +# frame's block count, returns a picture +def dec( + +tt: Laws.JBlkTok, + +rest: List<&2, Laws.JBlkTok>, + +tl: List<&2, Bool>, + +ww: U32, + +hh: U32, + +os: List<&2, List<&2, Bool>>, + hw0: {U32.is_eq(ww, 0) == False{} : Bool}, + hh0: {U32.is_eq(hh, 0) == False{} : Bool}, + +h8: {oct8(os) == True{} : Bool}, + +he: {rem(RS{[], os}) == List.append(&2, Bool, tbits(tt <> rest), tl) : List<&2, Bool>}, + +hw: {Laws.jpg.allwf(tt <> rest) == True{} : Bool}, + hn: NBlk(ww, hh, rest) +) -> {Laws.jpg.psome(Jpeg.decode.run(Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), tabsg(), rxs(os), + 0)) == True{} : Bool}: + +fr = Laws.jpg.enc.frame(ww, hh) + +sc = Laws.jpg.enc.scan() + +s0 = {RS{[], os} : RS} + +mr = List.append(&2, Bool, tbits(rest), tl) + +he1 = {Equal.trans(List<&2, Bool>, rem(s0), List.append(&2, Bool, List.append(&2, Bool, blkbits(tt), + tbits(rest)), tl), List.append(&2, Bool, blkbits(tt), mr), he, app_assoc(blkbits(tt), tbits(rest), tl)) : + {rem(s0) == List.append(&2, Bool, blkbits(tt), mr) : List<&2, Bool>}} + +bo = Jpeg.decode.block.of(rbits(s0, 1), 0, Jpeg.decode.pred.zero(), fr, sc, tabsg()) + +bg = Jpeg.decode.block.go(rbits(s0, 1), Jpeg.decode.pred.get(Jpeg.decode.pred.zero(), 0), dctb(), actb(), + ones64()) + s1 = Equal.sym(Bool, U32.is_eq(ww, 0), False{}, hw0) + s2 = Equal.sym(Bool, U32.is_eq(hh, 0), False{}, hh0) + s3 = Equal.sym(Nat, U32.to_nat(Jpeg.decode.nblocks(ww, hh, [1, 1, 1], [1, 1, 1], 1, 1)), 1n+List.length(&2, + Laws.JBlkTok, + rest), hn) + %s1 : {Laws.jpg.psome(Jpeg.decode.run.n(Bool.or(_, U32.is_eq(hh, 0)), ww, hh, fr, sc, tabsg(), rxs(os), + 0)) == True{} : + Bool} + %s2 : {Laws.jpg.psome(Jpeg.decode.run.n(Bool.or(False{}, _), ww, hh, fr, sc, tabsg(), rxs(os), 0)) == True{} : Bool} + %s3 : {Laws.jpg.psome(Jpeg.decode.start(_, Jpeg.Bits{0, 1, 0, rxs(os)}, fr, sc, tabsg(), 0, + Jpeg.decode.plane((ww * hh : U32)), Jpeg.decode.plane((ww * hh : U32)), Jpeg.decode.plane((ww * hh : U32)))) == + True{} : Bool} + fl(rest, bo, Jpeg.Ctrl{0, 0, 0, 0, 0, 0}, Jpeg.decode.pred.zero(), ww, hh, Jpeg.decode.plane((ww * hh : U32)), + Jpeg.decode.plane((ww * hh : U32)), Jpeg.decode.plane((ww * hh : U32)), tl, blkres.tr(bg, bo, mr, + Equal.sym(Jpeg.Blk, bo, bg, bof(rbits(s0, 1), 0, Jpeg.decode.pred.zero(), ww, hh)), bsim(tt, + Jpeg.decode.pred.get(Jpeg.decode.pred.zero(), 0), s0, mr, {==}, h8, he1, also_l(Laws.jpg.blkwf(tt), + Laws.jpg.allwf(rest), + hw))), also_r(Laws.jpg.blkwf(tt), Laws.jpg.allwf(rest), hw)) + +# the code the encoder's AC book holds for a symbol is the literal book's +def acode.eq(+sy: U32) -> {Laws.jpg.acode(sy) == code.ac(sy) : List<&2, Bool>}: + Equal.cong(Array, List<&2, Bool>, bk => Laws.jpg.cbw(Laws.jpg.val(Array.get(U32, bk, sy))), + Jenc.encode.huff(Jenc.encode.accounts(), Jenc.encode.acsyms()), acbook.lit(), acbook.eq()) + +# the code the encoder's DC book holds for a symbol is the literal book's +def dcode.eq(+sy: U32) -> {Laws.jpg.dcode(sy) == code.dc(sy) : List<&2, Bool>}: + Equal.cong(Array, List<&2, Bool>, bk => Laws.jpg.cbw(Laws.jpg.val(Array.get(U32, bk, sy))), + Jenc.encode.huff(Jenc.encode.dccounts(), Jenc.encode.dcsyms()), dcbook.lit(), dcbook.eq()) + +# AC tokens' bits over the encoder's book are their bits over the literal book +def acbits.eq(ts: List<&2, Laws.JTok>) -> {Laws.jpg.acbits(ts) == acbits(ts) : List<&2, Bool>}: + match ts: + case Nil{}: + {==} + case Laws.JTok{+sym, +ex} <> +rest: + Equal.trans(List<&2, Bool>, List.append(&2, Bool, Laws.jpg.acode(sym), List.append(&2, Bool, ex, + Laws.jpg.acbits(rest))), List.append(&2, Bool, code.ac(sym), List.append(&2, Bool, ex, + Laws.jpg.acbits(rest))), List.append(&2, Bool, code.ac(sym), List.append(&2, Bool, ex, acbits(rest))), + Equal.cong(List<&2, Bool>, List<&2, Bool>, xs => List.append(&2, Bool, xs, List.append(&2, Bool, ex, + Laws.jpg.acbits(rest))), Laws.jpg.acode(sym), code.ac(sym), acode.eq(sym)), Equal.cong(List<&2, Bool>, + List<&2, Bool>, ys => List.append(&2, Bool, code.ac(sym), List.append(&2, Bool, ex, ys)), + Laws.jpg.acbits(rest), acbits(rest), acbits.eq(rest))) + +# a block's bits over the encoder's books are its bits over the literal books +def blkbits.eq(bt: Laws.JBlkTok) -> {Laws.jpg.blkbits(bt) == blkbits(bt) : List<&2, Bool>}: + match bt: + case Laws.JBlkTok{+dcs, +dcx, +acs}: + Equal.trans(List<&2, Bool>, List.append(&2, Bool, Laws.jpg.dcode(dcs), List.append(&2, Bool, dcx, + Laws.jpg.acbits(acs))), List.append(&2, Bool, code.dc(dcs), List.append(&2, Bool, dcx, Laws.jpg.acbits(acs))), + List.append(&2, Bool, code.dc(dcs), List.append(&2, Bool, dcx, acbits(acs))), Equal.cong(List<&2, Bool>, + List<&2, Bool>, xs => List.append(&2, Bool, xs, List.append(&2, Bool, dcx, Laws.jpg.acbits(acs))), + Laws.jpg.dcode(dcs), code.dc(dcs), dcode.eq(dcs)), Equal.cong(List<&2, Bool>, List<&2, Bool>, + ys => List.append(&2, Bool, code.dc(dcs), List.append(&2, Bool, dcx, ys)), Laws.jpg.acbits(acs), acbits(acs), + acbits.eq(acs))) + +# blocks' bits over the encoder's books are their bits over the literal books +def tbits.eq(ts: List<&2, Laws.JBlkTok>) -> {Laws.jpg.tbits(ts) == tbits(ts) : List<&2, Bool>}: + match ts: + case Nil{}: + {==} + case +tt <> +rest: + Equal.trans(List<&2, Bool>, List.append(&2, Bool, Laws.jpg.blkbits(tt), Laws.jpg.tbits(rest)), List.append(&2, + Bool, blkbits(tt), Laws.jpg.tbits(rest)), List.append(&2, Bool, blkbits(tt), tbits(rest)), Equal.cong(List<&2, + Bool>, List<&2, Bool>, xs => List.append(&2, Bool, xs, Laws.jpg.tbits(rest)), Laws.jpg.blkbits(tt), + blkbits(tt), blkbits.eq(tt)), Equal.cong(List<&2, Bool>, List<&2, Bool>, ys => List.append(&2, Bool, + blkbits(tt), ys), Laws.jpg.tbits(rest), tbits(rest), tbits.eq(rest))) + +# the scan side of the round trip: blocks the decoder reads whole, as many as the frame has, written by the +# encoder's bit writer, decode to a picture +def scan_some( + +tt: Laws.JBlkTok, + +rest: List<&2, Laws.JBlkTok>, + +ww: U32, + +hh: U32, + hw0: {U32.is_eq(ww, 0) == False{} : Bool}, + hh0: {U32.is_eq(hh, 0) == False{} : Bool}, + hw: {Laws.jpg.allwf(tt <> rest) == True{} : Bool}, + hn: NBlk(ww, hh, rest) +) -> {Laws.jpg.psome(Jpeg.decode.run(Laws.jpg.enc.frame(ww, hh), Laws.jpg.enc.scan(), Laws.jpg.enc.tabs(), + Jenc.encode.pad(Laws.jpg.feed(Laws.jpg.tbits(tt <> rest), Jenc.encode.put0())), 0)) == True{} : Bool}: + +bs = tbits(tt <> rest) + +os = moct(bs, []) + +fr = Laws.jpg.enc.frame(ww, hh) + +sc = Laws.jpg.enc.scan() + eb = Equal.sym(List<&2, Bool>, Laws.jpg.tbits(tt <> rest), bs, tbits.eq(tt <> rest)) + et = Equal.sym(Jpeg.Tabs, Laws.jpg.enc.tabs(), tabsg(), tabs.eq()) + ep = Equal.sym(List<&2, U32>, Jenc.encode.pad(Laws.jpg.feed(bs, Jenc.encode.put0())), rxs(os), Equal.trans(List<&2, + U32>, Jenc.encode.pad(Laws.jpg.feed(bs, wput(WS{[], []}))), Jenc.encode.pad(wput(wm(bs, WS{[], []}))), rxs(os), + Equal.cong(Jenc.Put, List<&2, U32>, qq => Jenc.encode.pad(qq), Laws.jpg.feed(bs, wput(WS{[], []})), wput(wm(bs, + WS{[], []})), wfeed(bs, WS{[], []}, {==})), pad_m(bs, [], [], {==}))) + %eb : {Laws.jpg.psome(Jpeg.decode.run(fr, sc, Laws.jpg.enc.tabs(), Jenc.encode.pad(Laws.jpg.feed(_, + Jenc.encode.put0())), 0)) == True{} : Bool} + %et : {Laws.jpg.psome(Jpeg.decode.run(fr, sc, _, Jenc.encode.pad(Laws.jpg.feed(bs, Jenc.encode.put0())), + 0)) == True{} : Bool} + %ep : {Laws.jpg.psome(Jpeg.decode.run(fr, sc, tabsg(), _, 0)) == True{} : Bool} + dec(tt, rest, mpad(bs, []), ww, hh, os, hw0, hh0, oct8_moct(bs, [], {==}), concat_moct(bs, []), hw, hn) + diff --git a/src/jpeg.bend b/src/jpeg.bend index d6c7fe6..8b3bea1 100644 --- a/src/jpeg.bend +++ b/src/jpeg.bend @@ -642,7 +642,7 @@ def decode.ac.run.b(zzz: Bool, over: Bool, +sz: U32, +nk: U32, bits: Bits, zz: L decode.ac.store(decode.read.n(sz, bits), sz, nk, zz, ok) def decode.ac.run(+sym: U32, bits: Bits, +kk: U32, zz: List<&2, U32>, +ok: U32) -> Ac: - +sz = U32.and(sym, 15) + +sz = U32.and(15, sym) +nk = (kk + U32.shrn(sym, 4n) : U32) decode.ac.run.b(U32.is_eq(sz, 0), U32.is_ge(nk, 64), sz, nk, bits, zz, ok) @@ -663,14 +663,24 @@ def decode.ac.zrl.k(more: Bool, +nk: U32, zz: List<&2, U32>, bits: Bits, +ok: U3 def decode.ac.zrl(+kk: U32, zz: List<&2, U32>, bits: Bits, +ok: U32) -> Ac: decode.ac.zrl.k(U32.is_lt((kk + 16 : U32), 64), (kk + 16 : U32), zz, bits, ok) -def decode.ac.sym(+sym: U32, bits: Bits, +kk: U32, zz: List<&2, U32>, +ok: U32) -> Ac: - match sym: - case 0: +# an AC symbol: EOB (0), ZRL (240), or a run and size. U32.is_eq, not literal patterns, so a law reaches a +# symbolic symbol. +def decode.ac.sym.b(eob: Bool, zrl: Bool, +sym: U32, bits: Bits, +kk: U32, zz: List<&2, U32>, +ok: U32) -> Ac: + match eob: + case True{}: + +_s = sym + +_z = zrl Ac{kk, zz, bits, 1, ok} - case 240: - decode.ac.zrl(kk, zz, bits, ok) - case _: - decode.ac.run(sym, bits, kk, zz, ok) + case False{}: + match zrl: + case True{}: + +_s = sym + decode.ac.zrl(kk, zz, bits, ok) + case False{}: + decode.ac.run(sym, bits, kk, zz, ok) + +def decode.ac.sym(+sym: U32, bits: Bits, +kk: U32, zz: List<&2, U32>, +ok: U32) -> Ac: + decode.ac.sym.b(U32.is_eq(sym, 0), U32.is_eq(sym, 240), sym, bits, kk, zz, ok) def decode.ac.step(hit: Hit, +kk: U32, zz: List<&2, U32>, +ok: U32) -> Ac: match hit: From 2a00193350c02384ec6891a5966b803466322faa Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 26 Sep 2026 02:57:37 +0000 Subject: [PATCH 7/7] fix: IMG-JPG-3 scan proof takes its alias-typed hypotheses without copying Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A1bVZYFbhKkn2BKHKthcVP --- proof/wp12-jpeg-huffman.bend | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proof/wp12-jpeg-huffman.bend b/proof/wp12-jpeg-huffman.bend index cf6f273..d133208 100644 --- a/proof/wp12-jpeg-huffman.bend +++ b/proof/wp12-jpeg-huffman.bend @@ -3219,8 +3219,8 @@ def sim.run( +rr: List<&2, Bool>, +f1: {Bool.and(rshort(s1), roct8(s1)) == True{} : Bool}, +er: {rem(s1) == List.append(&2, Bool, ex, List.append(&2, Bool, acbits(rest), rr)) : List<&2, Bool>}, - +hk: RunOk(c1, c2, sym, ex), - +hr: RunRest(rest, kk, sym, pp), + hk: RunOk(c1, c2, sym, ex), + hr: RunRest(rest, kk, sym, pp), ih: IhAc(rest, pp, rr) ) -> AcRes(Jpeg.decode.ac(pp, Jpeg.decode.ac.run.b(c1, c2, U32.and(15, sym), (kk + U32.shrn(sym, 4n) : U32), rbits(s1, 1), zz, 1), actb()), rr):