diff --git a/LAWS.bend b/LAWS.bend index 94eccaa..93736a7 100644 --- a/LAWS.bend +++ b/LAWS.bend @@ -2891,6 +2891,115 @@ law jpeg_place: vmax}, scan, xx, yy, 0), jpg.point(jpg.decoded(ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, tabs, List.reverse(&2, U32, ent), ri), 2, Jpeg.Frame{ww, hh, nf, ids, hs, vs, tq, hmax, vmax}, scan, xx, yy, 0))) : Maybe<&2, U32>} +# ---- wp11-png-finish ---- + +# LAW: a well-formed raster with both sides nonzero and fewer than 2^29 samples decodes from its PNG to itself. The +# bound is taken as a8, a U32 whose value is 8 w h: 8 w h is some U32's value exactly when w h is below 2^29 (a closed +# 2^29 in a law would be expanded in unary) +# IMG-PNG-2 +law png_roundtrip: + for +ww: U32 + for +hh: U32 + for +px: List<&2, U32> + for +a8: U32 + for h_ok: {png_ok(ww, hh, px) == True{} : Bool} + for h_a8: {U32.to_nat(a8) == Nat.mul(8n, Nat.mul(U32.to_nat(hh), U32.to_nat(ww))) : Nat} + {spec.png.back(Img.encode_png(Img.raster(ww, hh, px))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, Img.Raster>} + +# an ancillary chunk (PNG section 5.4): its four-byte type, read as one number most significant byte first, and its +# data +type Anc is Data: + Anc{+typ: U32, +data: List<&2, U32>} + +# ancillary chunks laid out as spec.chunk, one after another, then rest +def spec.ancs(cs: List<&2, Anc>, rest: List<&2, U32>) -> List<&2, U32>: + match cs: + case Nil{}: + rest + case Anc{+typ, +data} <> tl: + spec.chunk(spec.be4(typ), data, spec.ancs(tl, rest)) + +# the ancillary bit (PNG section 5.4): bit 5 of the type's first byte is 1 +def spec.anc.bit(+typ: U32) -> Bool: + U32.is_eq(U32.and(U32.shrn(typ, 24n), 32), 32) + +# each chunk is ancillary, is not tRNS (1951551059, the bytes 116 82 78 83), and has fewer than 2^32 data bytes +def spec.ancs.ok(cs: List<&2, Anc>) -> Type: + match cs: + case Nil{}: + Unit + case Anc{+typ, +data} <> tl: + {spec.anc.bit(typ) == True{} : Bool} & ({U32.is_eq(typ, 1951551059) == False{} : Bool} & (spec.fits(data) & + spec.ancs.ok(tl))) + +# PLTE, ancillary chunks a1 and tRNS, then rest; with late, tRNS comes first, an order the decoder also takes when +# the colour type allows tRNS with no PLTE yet +def spec.mid( + late: Bool, + plte: Maybe<&2, List<&2, U32>>, + a1: List<&2, Anc>, + trns: Maybe<&2, List<&2, U32>>, + rest: List<&2, U32> +) -> List<&2, U32>: + match late: + case False{}: + spec.chunk.opt([80, 76, 84, 69], plte, spec.ancs(a1, spec.chunk.opt([116, 82, 78, 83], trns, rest))) + case True{}: + spec.chunk.opt([116, 82, 78, 83], trns, spec.ancs(a1, spec.chunk.opt([80, 76, 84, 69], plte, rest))) + +# a PNG file as spec.png lays it out, with ancillary chunks where PNG section 5.6 allows them: a0 after IHDR, a1 +# between PLTE and tRNS, a2 after them, and a3 after the IDAT chunks, before IEND +def spec.png.anc( + +ww: U32, + +hh: U32, + +cc: U32, + late: Bool, + a0: List<&2, Anc>, + plte: Maybe<&2, List<&2, U32>>, + a1: List<&2, Anc>, + trns: Maybe<&2, List<&2, U32>>, + a2: List<&2, Anc>, + idats: List<&2, List<&2, U32>>, + a3: List<&2, Anc> +) -> List<&2, U32>: + List.append(&2, U32, Img.png_sig(), spec.chunk([73, 72, 68, 82], spec.ihdr.data(ww, hh, cc), spec.ancs(a0, + spec.mid(late, plte, a1, trns, spec.ancs(a2, spec.idats(idats, spec.ancs(a3, spec.chunk([73, 69, 78, 68], [], + [])))))))) + +# LAW: ancillary chunks where PNG section 5.6 allows them leave what decode_png makes of a file: the file png_walk +# reads, with ancillary chunks after IHDR, PLTE, tRNS and the IDAT chunks, and with tRNS before PLTE where the decoder +# takes that order (late), decodes to the raster px.of packs from the unfiltered inflated IDAT data, with the IHDR +# colour type and the PLTE and tRNS data +# IMG-PNG-9 +# IMG-PIX-1 +law png_walk_anc: + for +ww: U32 + for +hh: U32 + for +cc: U32 + for +late: Bool + for +a0: List<&2, Anc> + for +plte: Maybe<&2, List<&2, U32>> + for +a1: List<&2, Anc> + for +trns: Maybe<&2, List<&2, U32>> + for +a2: List<&2, Anc> + for +idats: List<&2, List<&2, U32>> + for +a3: List<&2, Anc> + for h_ww: {U32.is_gt(ww, 0) == True{} : Bool} + for h_hh: {U32.is_gt(hh, 0) == True{} : Bool} + for h_cc: {spec.colour.ok(cc) == True{} : Bool} + for h_pl: {spec.plte.ok(cc, plte) == True{} : Bool} + for h_tr: {spec.trns.ok(cc, plte, trns) == True{} : Bool} + for h_late: {Bool.or(Bool.not(late), spec.trns.ok(cc, None{}, trns)) == True{} : Bool} + for h_fp: spec.fits.opt(plte) + for h_ft: spec.fits.opt(trns) + for h_fi: spec.fits.all(idats) + for h_a0: spec.ancs.ok(a0) + for h_a1: spec.ancs.ok(a1) + for h_a2: spec.ancs.ok(a2) + for h_a3: spec.ancs.ok(a3) + {Img.decode_png(spec.png.anc(ww, hh, cc, late, a0, plte, a1, trns, a2, idats, a3)) == spec.decoded(Inf.inflate( + List.concat(&2, U32, idats)), ww, hh, cc, spec.or.nil(plte), spec.or.nil(trns)) : Maybe<&2, Img.Raster>} + # ---- wp12-jpeg-huffman ---- # a bit as 1 or 0 diff --git a/PROOF.bend b/PROOF.bend index bcaa280..73bd1e6 100644 --- a/PROOF.bend +++ b/PROOF.bend @@ -20,6 +20,7 @@ import ./proof/wp9-png-roundtrip.bend as W9 import ./src/inflate.bend as Inf import ./proof/wp10-jpeg-finish.bend as W10 import ./proof/wp13-jpeg-planes.bend as W13 +import ./proof/wp11-png-finish.bend as W11 import ./proof/wp12-jpeg-huffman.bend as W12 # U32.or with 0xFF000000 first has alpha 255, whatever the other operand: @@ -5260,6 +5261,63 @@ def rt.px( Equal.cong(Maybe<&2, List<&2, U32>>, Maybe<&2, Img.Raster>, mm => Laws.spec.raster(mm, ww, hh), Png.px.of(ct, W9.chans.all(op, px), [], []), Some{px}, rt.pix(op, px, hao))) +# the pixel stage after the layout: the one-block layout around any zlib stream of the scanlines that inflates to +# them decodes to the raster. The layout is stated stuck on zb, which is False +def rt.tail( + +zb: Bool, + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +op: Bool, + +wp: Nat, + +hp: Nat, + +ez: {zb == False{} : Bool}, + hao: {Laws.all_opaque(px) == op : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_h: {U32.to_nat(hh) == 1n+hp : Nat}, + +hl: {List.length(&2, U32, px) == Nat.mul(1n+hp, 1n+wp) : Nat}, + e_m: {U32.to_nat(U32.mul(ww, Png.enc.nch(op))) == Nat.mul(U32.to_nat(ww), U32.to_nat(Png.enc.nch(op))) : Nat}, + h_fz: Laws.spec.fits(Png.enc.zlib(W9.sc(px, 0n, 1n+wp, op), Png.enc.len(W9.sc(px, 0n, 1n+wp, op), 0))), + +h_nl: {U32.to_nat(Png.enc.len(W9.sc(px, 0n, 1n+wp, op), 0)) == List.length(&2, U32, W9.sc(px, 0n, 1n+wp, op)) : Nat} +) -> {Img.decode_png(Png.enc.seal.at(zb, ww, hh, op, W9.sc(px, 0n, 1n+wp, op), Png.enc.len(W9.sc(px, 0n, 1n+wp, op), + 0))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, Img.Raster>}: + +ww1 = {1n+wp : Nat} + +hh1 = {1n+hp : Nat} + +ss = W9.sc(px, 0n, ww1, op) + +rows = W9.rows.of(hh1, ww1, op, px) + +ll = Png.enc.len(ss, 0) + +zz = Png.enc.zlib(ss, ll) + +e_s = {W9.sc.rows(hh1, wp, op, px, hl) : {ss == W9.scan(rows) : List<&2, U32>}} + +h_by = {Equal.trans(Bool, Laws.bytes(ss), Laws.bytes.go(W9.scan(rows), True{}), True{}, Equal.cong(List<&2, U32>, + Bool, xs => Laws.bytes.go(xs, True{}), ss, W9.scan(rows), e_s), W9.bytes.scan(rows, W9.masked.rows.of(hh1, ww1, op, + px))) : {Laws.bytes(ss) == True{} : Bool}} + +e_inf = {Laws.inflate_enc_zlib(ss, h_by, h_nl) : {Inf.inflate(zz) == Some{ss} : Maybe<&2, List<&2, U32>>}} + +ct = Png.enc.ct(op) + +h_ww = W9.gt.pos(ww, wp, e_w) + +h_hh = W9.gt.pos(hh, hp, e_h) + +a6 = Img.decode_png(Png.enc.seal.at(zb, ww, hh, op, ss, ll)) + +a8 = Img.decode_png(Laws.spec.png(ww, hh, ct, None{}, None{}, [zz])) + +a9 = Laws.spec.decoded(Inf.inflate(List.concat(&2, U32, [zz])), ww, hh, ct, [], []) + +a10 = Laws.spec.decoded(Some{ss}, ww, hh, ct, [], []) + Equal.trans(Maybe<&2, Img.Raster>, a6, a8, Some{Img.raster(ww, hh, px)}, + Equal.trans(Maybe<&2, Img.Raster>, a6, Img.decode_png(Png.enc.seal.one(ww, hh, op, ss, ll)), a8, + Equal.cong(Bool, Maybe<&2, Img.Raster>, zc => Img.decode_png(Png.enc.seal.at(zc, ww, hh, op, ss, ll)), + zb, False{}, ez), + Equal.cong(List<&2, U32>, Maybe<&2, Img.Raster>, xs => Img.decode_png(xs), Png.enc.seal.one(ww, hh, op, ss, + ll), Laws.spec.png(ww, hh, ct, None{}, None{}, [zz]), W9.seal.one.same(ww, hh, op, ss, ll))), + Equal.trans(Maybe<&2, Img.Raster>, a8, a9, Some{Img.raster(ww, hh, px)}, + Laws.png_walk(ww, hh, ct, None{}, None{}, [zz], h_ww, h_hh, rt.col(op), rt.plte(op), {==}, Unit{}, Unit{}, + (h_fz, Unit{})), + Equal.trans(Maybe<&2, Img.Raster>, a9, Laws.spec.decoded(Inf.inflate(zz), ww, hh, ct, [], []), + Some{Img.raster(ww, hh, px)}, + Equal.cong(List<&2, U32>, Maybe<&2, Img.Raster>, xs => Laws.spec.decoded(Inf.inflate(xs), ww, hh, ct, [], + []), List.append(&2, U32, zz, []), zz, Wp2.app_nil(zz)), + Equal.trans(Maybe<&2, Img.Raster>, Laws.spec.decoded(Inf.inflate(zz), ww, hh, ct, [], []), a10, + Some{Img.raster(ww, hh, px)}, + Equal.cong(Maybe<&2, List<&2, U32>>, Maybe<&2, Img.Raster>, mm => Laws.spec.decoded(mm, ww, hh, ct, [], + []), Inf.inflate(zz), Some{ss}, e_inf), + rt.px(ww, hh, px, op, wp, hp, hao, e_w, e_h, hl, e_m))))) + # the one-block round trip, with the width and height 1 + w' and 1 + h', and # op whether every sample is opaque def rt.go( @@ -5348,20 +5406,10 @@ def rt.go( {U32.is_gt(ll, 65535) == False{} : Bool}} h_fz = {W9.fits.small(zz, ls, cap, cap2, W9.zlib.len(ss, ll, le_l), h_ls, hc) : Laws.spec.fits(zz)} - +h_by = {Equal.trans(Bool, Laws.bytes(ss), Laws.bytes.go(W9.scan(rows), True{}), True{}, Equal.cong(List<&2, U32>, - Bool, xs => Laws.bytes.go(xs, True{}), ss, W9.scan(rows), e_s), W9.bytes.scan(rows, W9.masked.rows.of(hh1, ww1, op, - px))) : {Laws.bytes(ss) == True{} : Bool}} +h_nl = {Equal.trans(Nat, U32.to_nat(ll), nn, ls, t_l, Equal.sym(Nat, ls, nn, e_ls)) : {U32.to_nat(ll) == ls : Nat}} - +e_inf = {Laws.inflate_enc_zlib(ss, h_by, h_nl) : {Inf.inflate(zz) == Some{ss} : Maybe<&2, List<&2, U32>>}} - +ct = Png.enc.ct(op) - +h_ww = W9.gt.pos(ww, wp, e_w) - +h_hh = W9.gt.pos(hh, hp, e_h) +a2 = Img.decode_png(Png.enc.paint(ww, hh, op, px)) +a4 = Img.decode_png(Png.enc.seal(ww, hh, op, Png.enc.raw(px, 0n, ww1, op, [], 0))) +a6 = Img.decode_png(Png.enc.seal.at(U32.is_gt(ll, 65535), ww, hh, op, ss, ll)) - +a8 = Img.decode_png(Laws.spec.png(ww, hh, ct, None{}, None{}, [zz])) - +a9 = Laws.spec.decoded(Inf.inflate(List.concat(&2, U32, [zz])), ww, hh, ct, [], []) - +a10 = Laws.spec.decoded(Some{ss}, ww, hh, ct, [], []) Equal.trans(Maybe<&2, Img.Raster>, Laws.spec.png.back(Png.enc.open(Png.enc.good(ww, hh, px), ww, hh, px)), a2, Some{Img.raster(ww, hh, px)}, Equal.trans(Maybe<&2, Img.Raster>, Laws.spec.png.back(Png.enc.open(Png.enc.good(ww, hh, px), ww, hh, px)), @@ -5386,24 +5434,7 @@ def rt.go( r0, ss, Wp2.rev_rev(ss)), Equal.cong(U32, Maybe<&2, Img.Raster>, uu => Img.decode_png(Png.enc.seal(ww, hh, op, (ss, uu))), m0, ll, e_m0))), - Equal.trans(Maybe<&2, Img.Raster>, a6, a8, Some{Img.raster(ww, hh, px)}, - Equal.trans(Maybe<&2, Img.Raster>, a6, Img.decode_png(Png.enc.seal.one(ww, hh, op, ss, ll)), a8, - Equal.cong(Bool, Maybe<&2, Img.Raster>, zb => Img.decode_png(Png.enc.seal.at(zb, ww, hh, op, ss, ll)), - U32.is_gt(ll, 65535), False{}, f_ll), - Equal.cong(List<&2, U32>, Maybe<&2, Img.Raster>, xs => Img.decode_png(xs), Png.enc.seal.one(ww, hh, op, ss, - ll), Laws.spec.png(ww, hh, ct, None{}, None{}, [zz]), W9.seal.one.same(ww, hh, op, ss, ll))), - Equal.trans(Maybe<&2, Img.Raster>, a8, a9, Some{Img.raster(ww, hh, px)}, - Laws.png_walk(ww, hh, ct, None{}, None{}, [zz], h_ww, h_hh, rt.col(op), rt.plte(op), {==}, Unit{}, Unit{}, - (h_fz, Unit{})), - Equal.trans(Maybe<&2, Img.Raster>, a9, Laws.spec.decoded(Inf.inflate(zz), ww, hh, ct, [], []), - Some{Img.raster(ww, hh, px)}, - Equal.cong(List<&2, U32>, Maybe<&2, Img.Raster>, xs => Laws.spec.decoded(Inf.inflate(xs), ww, hh, ct, [], - []), List.append(&2, U32, zz, []), zz, Wp2.app_nil(zz)), - Equal.trans(Maybe<&2, Img.Raster>, Laws.spec.decoded(Inf.inflate(zz), ww, hh, ct, [], []), a10, - Some{Img.raster(ww, hh, px)}, - Equal.cong(Maybe<&2, List<&2, U32>>, Maybe<&2, Img.Raster>, mm => Laws.spec.decoded(mm, ww, hh, ct, [], - []), Inf.inflate(zz), Some{ss}, e_inf), - rt.px(ww, hh, px, op, wp, hp, hao, e_w, e_h, hl, e_m)))))))) + rt.tail(U32.is_gt(ll, 65535), ww, hh, px, op, wp, hp, f_ll, hao, e_w, e_h, hl, e_m, h_fz, h_nl)))) # the width and height as successors def rt.pos( @@ -6138,6 +6169,229 @@ def Laws.jpeg_place( w13.fin(U32.is_eq(kind, 1), U32.is_eq(bad, 0), ent, tabs, ri, ww, hh, nf, ids, hs, vs, tq, hmax, vmax, scan, xx, yy, ew, hD, hfD, hK, h_xx, h_yy) +# ---- wp11-png-finish ---- + +# IMG-PNG-9 and IMG-PIX-1: ancillary chunks where PNG section 5.6 allows them leave what the walker hands the pixel +# stage +def Laws.png_walk_anc( + ww, + hh, + cc, + late, + a0, + plte, + a1, + trns, + a2, + idats, + a3, + h_ww, + h_hh, + h_cc, + h_pl, + h_tr, + h_late, + h_fp, + h_ft, + h_fi, + h_a0, + h_a1, + h_a2, + h_a3 +): + +mm = Inf.inflate(List.concat(&2, U32, idats)) + +pl = Laws.spec.or.nil(plte) + +tr = Laws.spec.or.nil(trns) + +fl = Laws.spec.chunk([73, 72, 68, 82], Laws.spec.ihdr.data(ww, hh, cc), Laws.spec.ancs(a0, Laws.spec.mid(late, plte, + a1, trns, Laws.spec.ancs(a2, Laws.spec.idats(idats, Laws.spec.ancs(a3, Laws.spec.chunk([73, 69, 78, 68], [], + []))))))) + Equal.trans(Maybe<&2, Img.Raster>, Img.decode_png.pic(Png.decode(fl)), Img.decode_png.pic(Png.png.samples(mm, ww, hh, + cc, pl, tr)), Laws.spec.decoded(mm, ww, hh, cc, pl, tr), + Equal.cong(Maybe<&2, Png.Pic>, Maybe<&2, Img.Raster>, xx => Img.decode_png.pic(xx), Png.decode(fl), + Png.png.samples(mm, ww, hh, cc, pl, tr), W11.walk.png(ww, hh, cc, late, a0, plte, a1, trns, a2, idats, a3, h_ww, + h_hh, h_cc, h_pl, h_tr, h_late, h_fp, h_ft, h_fi, h_a0, h_a1, h_a2, h_a3)), + W9.frame.decoded(mm, ww, hh, cc, pl, tr)) + +# IMG-PNG-2 when the scanlines fit one stored block: png_roundtrip_one, with the scanline count a word's value +def rt2.one( + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +wp: Nat, + +hp: Nat, + h_ok: {Laws.png_ok(ww, hh, px) == True{} : Bool}, + +hao: {Laws.all_opaque(px) == Png.enc.opaque(px, True{}) : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_h: {U32.to_nat(hh) == 1n+hp : Nat}, + +t_nb: {U32.to_nat(Png.enc.nbytes(W11.opq(px), ww, hh)) == W11.snn(hp, wp, W11.opq(px)) : Nat}, + h_one: {U32.is_le(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535) == True{} : Bool} +) -> {Laws.spec.png.back(Img.encode_png(Img.raster(ww, hh, px))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, + Img.Raster>}: + +op = Png.enc.opaque(px, True{}) + +ao = Laws.all_opaque(px) + +nb = Png.enc.nbytes(op, ww, hh) + +hn = U32.to_nat(hh) + +wn = U32.to_nat(ww) + +e_n = {Equal.trans(Nat, Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))), Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, + Laws.spec.nch(ao))), Laws.spec.scan.len(ww, hh, ao), + Equal.cong(Bool, Nat, oo => Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(oo))), op, ao, Equal.sym(Bool, ao, op, + hao)), + Equal.trans(Nat, Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(ao))), Nat.mul(hn, 1n+Nat.mul(1n+wp, + Laws.spec.nch(ao))), Laws.spec.scan.len(ww, hh, ao), + Equal.cong(Nat, Nat, xx => Nat.mul(xx, 1n+Nat.mul(1n+wp, Laws.spec.nch(ao))), 1n+hp, hn, Equal.sym(Nat, hn, + 1n+hp, e_h)), + Equal.cong(Nat, Nat, xx => Nat.mul(hn, 1n+Nat.mul(xx, Laws.spec.nch(ao))), 1n+wp, wn, Equal.sym(Nat, wn, 1n+wp, + e_w)))) : {Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))) == Laws.spec.scan.len(ww, hh, ao) : Nat}} + Laws.png_roundtrip_one(ww, hh, px, nb, h_ok, Equal.trans(Nat, U32.to_nat(nb), Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, + Laws.spec.nch(op))), Laws.spec.scan.len(ww, hh, ao), t_nb, e_n), h_one) + +# IMG-PNG-2 past one stored block: the stored blocks of 65535 bytes, whose sizes fit a word by the bound 8 w h +def rt2.wide( + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +a8: U32, + +wp: Nat, + +hp: Nat, + e_good: {Png.enc.good(ww, hh, px) == True{} : Bool}, + +hao: {Laws.all_opaque(px) == Png.enc.opaque(px, True{}) : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_h: {U32.to_nat(hh) == 1n+hp : Nat}, + +hl: {List.length(&2, U32, px) == Nat.mul(1n+hp, 1n+wp) : Nat}, + +t_nb: {U32.to_nat(Png.enc.nbytes(W11.opq(px), ww, hh)) == W11.snn(hp, wp, W11.opq(px)) : Nat}, + +e_t: {U32.to_nat(a8) == Nat.mul(8n, Nat.mul(1n+hp, 1n+wp)) : Nat}, + +h5: {Nat.is_le(W11.snn(hp, wp, W11.opq(px)), Nat.mul(Nat.mul(1n+hp, 1n+wp), 5n)) == True{} : Bool}, + +h_n: {Nat.is_le(W11.snn(hp, wp, W11.opq(px)), U32.to_nat(a8)) == True{} : Bool}, + +hbn: {U32.is_le(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535) == False{} : Bool} +) -> {Laws.spec.png.back(Img.encode_png(Img.raster(ww, hh, px))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, + Img.Raster>}: + +op = Png.enc.opaque(px, True{}) + +ww1 = {1n+wp : Nat} + +hh1 = {1n+hp : Nat} + +aa = Nat.mul(hh1, ww1) + +nn = Nat.mul(hh1, 1n+Nat.mul(ww1, Laws.spec.nch(op))) + +ss = W9.sc(px, 0n, ww1, op) + +ll = Png.enc.len(ss, 0) + +nb = Png.enc.nbytes(op, ww, hh) + +ls = List.length(&2, U32, ss) + +rows = W9.rows.of(hh1, ww1, op, px) + +e_s = {W9.sc.rows(hh1, wp, op, px, hl) : {ss == W9.scan(rows) : List<&2, U32>}} + +e_ls = {Equal.trans(Nat, ls, List.length(&2, U32, W9.scan(rows)), nn, Equal.cong(List<&2, U32>, Nat, xs => + List.length(&2, U32, xs), ss, W9.scan(rows), e_s), W9.len.scan(hh1, ww1, op, px, hl)) : {ls == nn : Nat}} + +hn = {W11.len.of(ss, a8, R.le_rw_l(nn, ls, U32.to_nat(a8), Equal.sym(Nat, ls, nn, e_ls), h_n)) : + {U32.to_nat(ll) == ls : Nat}} + +e_nbl = {W11.u32.same(nb, ll, Equal.trans(Nat, U32.to_nat(nb), nn, U32.to_nat(ll), t_nb, Equal.trans(Nat, nn, ls, + U32.to_nat(ll), Equal.sym(Nat, ls, nn, e_ls), Equal.sym(Nat, U32.to_nat(ll), ls, hn)))) : {nb == ll : U32}} + +hbig = {Equal.trans(Bool, U32.is_le(ll, 65535), U32.is_le(nb, 65535), False{}, Equal.cong(U32, Bool, uu => + U32.is_le(uu, 65535), ll, nb, Equal.sym(U32, nb, ll, e_nbl)), hbn) : {U32.is_le(ll, 65535) == False{} : Bool}} + +h_tu = W11.tu.ok(ss, 65535, a8, aa, {==}, hn, hbig, R.le_rw_l(nn, ls, Nat.mul(aa, 5n), Equal.sym(Nat, ls, nn, + e_ls), h5), e_t) + +h_by = {Equal.trans(Bool, Laws.bytes(ss), Laws.bytes.go(W9.scan(rows), True{}), True{}, Equal.cong(List<&2, U32>, + Bool, xs => Laws.bytes.go(xs, True{}), ss, W9.scan(rows), e_s), W9.bytes.scan(rows, W9.masked.rows.of(hh1, ww1, op, + px))) : {Laws.bytes(ss) == True{} : Bool}} + +zf = Nat.is_lt(ls, 0n) + +ef = {Equal.sym(Bool, False{}, zf, R.lt_zero_false(ls)) : {zf == False{} : Bool}} + +kz = Nat.add(Nat.add(ls, Nat.mul(U32.to_nat(Png.enc.nblk(ll)), 5n)), 6n) + h_fz = {W9.nat.fits(List.length(&2, U32, Png.enc.zlib(ss, ll)), a8, R.le_rw_l(kz, List.length(&2, U32, + Laws.zlib.stored(65535, ss)), U32.to_nat(a8), Equal.sym(Nat, List.length(&2, U32, Laws.zlib.stored(65535, ss)), kz, + W11.zlib.len(ss, 65535, {==}, hn, hbig)), h_tu)) : Laws.spec.fits(Png.enc.zlib(ss, ll))} + Equal.trans(Maybe<&2, Img.Raster>, Laws.spec.png.back(Png.enc.open(Png.enc.good(ww, hh, px), ww, hh, px)), + Laws.spec.png.back(Some{Png.enc.seal.at(zf, ww, hh, op, ss, ll)}), Some{Img.raster(ww, hh, px)}, + Equal.cong(Maybe<&2, List<&2, U32>>, Maybe<&2, Img.Raster>, mm => Laws.spec.png.back(mm), + Png.enc.open(Png.enc.good(ww, + hh, px), ww, hh, px), Some{Png.enc.seal.at(zf, ww, hh, op, ss, ll)}, W11.wide.rt(zf, ww, hh, px, wp, a8, ef, + e_good, e_w, e_nbl, hn, hbig, h_by, h_tu)), + rt.tail(zf, ww, hh, px, op, wp, hp, ef, hao, e_w, e_h, hl, W11.nb.em(ww, op, wp, hp, a8, e_w, h_n), h_fz, hn)) + +# IMG-PNG-2: split on how the scanline count compares with 65535 +def rt2.at( + cc: Cmp, + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +a8: U32, + +wp: Nat, + +hp: Nat, + h_ok: {Laws.png_ok(ww, hh, px) == True{} : Bool}, + e_good: {Png.enc.good(ww, hh, px) == True{} : Bool}, + +hao: {Laws.all_opaque(px) == Png.enc.opaque(px, True{}) : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_h: {U32.to_nat(hh) == 1n+hp : Nat}, + +hl: {List.length(&2, U32, px) == Nat.mul(1n+hp, 1n+wp) : Nat}, + +t_nb: {U32.to_nat(Png.enc.nbytes(W11.opq(px), ww, hh)) == W11.snn(hp, wp, W11.opq(px)) : Nat}, + +e_t: {U32.to_nat(a8) == Nat.mul(8n, Nat.mul(1n+hp, 1n+wp)) : Nat}, + +h5: {Nat.is_le(W11.snn(hp, wp, W11.opq(px)), Nat.mul(Nat.mul(1n+hp, 1n+wp), 5n)) == True{} : Bool}, + +h_n: {Nat.is_le(W11.snn(hp, wp, W11.opq(px)), U32.to_nat(a8)) == True{} : Bool}, + +ec: {U32.cmp(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535) == cc : Cmp} +) -> {Laws.spec.png.back(Img.encode_png(Img.raster(ww, hh, px))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, + Img.Raster>}: + match cc: + case LT{}: + rt2.one(ww, hh, px, wp, hp, h_ok, hao, e_w, e_h, t_nb, Equal.cong(Cmp, Bool, xc => Cmp.is_le(xc), + U32.cmp(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535), LT{}, ec)) + case EQ{}: + rt2.one(ww, hh, px, wp, hp, h_ok, hao, e_w, e_h, t_nb, Equal.cong(Cmp, Bool, xc => Cmp.is_le(xc), + U32.cmp(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535), EQ{}, ec)) + case GT{}: + rt2.wide(ww, hh, px, a8, wp, hp, e_good, hao, e_w, e_h, hl, t_nb, e_t, h5, h_n, Equal.cong(Cmp, Bool, xc => + Cmp.is_le(xc), U32.cmp(Png.enc.nbytes(Png.enc.opaque(px, True{}), ww, hh), 65535), GT{}, ec)) + +# IMG-PNG-2 with the width and height as successors: the sizes from the bound 8 w h +def rt2.pos( + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +a8: U32, + h_ok: {Laws.png_ok(ww, hh, px) == True{} : Bool}, + e_good: {Png.enc.good(ww, hh, px) == True{} : Bool}, + +hl: {List.length(&2, U32, px) == Nat.mul(U32.to_nat(hh), U32.to_nat(ww)) : Nat}, + +h_a8: {U32.to_nat(a8) == Nat.mul(8n, Nat.mul(U32.to_nat(hh), U32.to_nat(ww))) : Nat}, + pw: &wp: Nat -> {U32.to_nat(ww) == 1n+wp : Nat}, + ph: &hp: Nat -> {U32.to_nat(hh) == 1n+hp : Nat} +) -> {Laws.spec.png.back(Img.encode_png(Img.raster(ww, hh, px))) == Some{Img.raster(ww, hh, px)} : Maybe<&2, + Img.Raster>}: + (+wp, e_w0) = pw + (+hp, e_h0) = ph + +e_w = {e_w0 : {U32.to_nat(ww) == 1n+wp : Nat}} + +e_h = {e_h0 : {U32.to_nat(hh) == 1n+hp : Nat}} + +op = Png.enc.opaque(px, True{}) + +hn = U32.to_nat(hh) + +wn = U32.to_nat(ww) + +aa = Nat.mul(1n+hp, 1n+wp) + +nn = Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))) + +hao = {Equal.sym(Bool, op, Laws.all_opaque(px), opaque.all(px, True{})) : {Laws.all_opaque(px) == op : Bool}} + +e_l2 = {Equal.trans(Nat, List.length(&2, U32, px), Nat.mul(hn, wn), aa, hl, Equal.trans(Nat, Nat.mul(hn, wn), + Nat.mul(1n+hp, wn), aa, Equal.cong(Nat, Nat, xx => Nat.mul(xx, wn), hn, 1n+hp, e_h), Equal.cong(Nat, Nat, xx => + Nat.mul(1n+hp, xx), wn, 1n+wp, e_w))) : {List.length(&2, U32, px) == aa : Nat}} + +e_t = {Equal.trans(Nat, U32.to_nat(a8), Nat.mul(8n, Nat.mul(hn, wn)), Nat.mul(8n, aa), h_a8, Equal.trans(Nat, + Nat.mul(8n, Nat.mul(hn, wn)), Nat.mul(8n, Nat.mul(1n+hp, wn)), Nat.mul(8n, aa), Equal.cong(Nat, Nat, xx => + Nat.mul(8n, Nat.mul(xx, wn)), hn, 1n+hp, e_h), Equal.cong(Nat, Nat, xx => Nat.mul(8n, Nat.mul(1n+hp, xx)), wn, + 1n+wp, e_w))) : {U32.to_nat(a8) == Nat.mul(8n, aa) : Nat}} + +h5 = {W11.scan5(1n+hp, wp, Laws.spec.nch(op), W11.nch4(op)) : {Nat.is_le(nn, Nat.mul(aa, 5n)) == True{} : Bool}} + +h_n = {R.le_rw_r(nn, Nat.mul(8n, aa), U32.to_nat(a8), Equal.sym(Nat, U32.to_nat(a8), Nat.mul(8n, aa), e_t), + R.le_trans(nn, Nat.mul(aa, 5n), Nat.mul(8n, aa), h5, W11.five.eight(aa))) : {Nat.is_le(nn, U32.to_nat(a8)) == + True{} : Bool}} + +t_nb = {W11.nb.val(ww, hh, op, wp, hp, a8, e_w, e_h, h_n) : {U32.to_nat(Png.enc.nbytes(op, ww, hh)) == nn : Nat}} + rt2.at(U32.cmp(Png.enc.nbytes(op, ww, hh), 65535), ww, hh, px, a8, wp, hp, h_ok, e_good, hao, e_w, e_h, e_l2, t_nb, + e_t, h5, h_n, {==}) + +# IMG-PNG-2: one stored block when the scanlines fit, the stored blocks of 65535 bytes otherwise +def Laws.png_roundtrip(ww, hh, px, a8, h_ok, h_a8): + +aa = Nat.mul(U32.to_nat(hh), U32.to_nat(ww)) + +ll = List.length(&2, U32, px) + +nw = Bool.not(Nat.is_eq(U32.to_nat(ww), 0n)) + +nh = Bool.not(Nat.is_eq(U32.to_nat(hh), 0n)) + +ok = {h_ok : {Laws.png_ok(ww, hh, px) == True{} : Bool}} + +e_sides = {U32L.and_left(Bool.and(nw, nh), Bool.and(Laws.below(32n, aa), Nat.is_eq(ll, aa)), ok) : + {Bool.and(nw, nh) == True{} : Bool}} + +e_r = {U32L.and_right(Bool.and(nw, nh), Bool.and(Laws.below(32n, aa), Nat.is_eq(ll, aa)), ok) : + {Bool.and(Laws.below(32n, aa), Nat.is_eq(ll, aa)) == True{} : Bool}} + +e_la = {Wp4.nat_eq_true(ll, aa, U32L.and_right(Laws.below(32n, aa), Nat.is_eq(ll, aa), e_r)) : {ll == aa : Nat}} + +z_w = not.true(Nat.is_eq(U32.to_nat(ww), 0n), U32L.and_left(nw, nh, e_sides)) + +z_h = not.true(Nat.is_eq(U32.to_nat(hh), 0n), U32L.and_right(nw, nh, e_sides)) + +e_good = rt.good.at(Png.enc.good(ww, hh, px), ww, hh, px, Laws.png_encodes(ww, hh, px, ok)) + rt2.pos(ww, hh, px, a8, ok, e_good, e_la, h_a8, Wp4.nat_pos(U32.to_nat(ww), z_w), Wp4.nat_pos(U32.to_nat(hh), z_h)) + # ---- wp12-jpeg-huffman ---- def Laws.jpeg_bits_round_trip(cs, h_len): diff --git a/SPEC.md b/SPEC.md index d399756..4a93af7 100644 --- a/SPEC.md +++ b/SPEC.md @@ -42,7 +42,7 @@ What a sample means, for every decoder and encoder. | ID | Requirement | Level | Status | Law | | :---- | :---- | :---- | :---- | :---- | -| IMG-PIX-1 | Every sample `decode_png` and `decode_jpeg` return is packed `0xAARRGGBB`. Every JPEG sample has alpha 255, and a gray JPEG sample carries its Y value in R, G and B. | Proved | pending | LAWS.bend jpeg_rgb_opaque; LAWS.bend jpeg_gray_opaque; LAWS.bend jpeg_decode_packed; LAWS.bend jpeg_decode_opaque; LAWS.bend jpeg_gray_level | +| IMG-PIX-1 | Every sample `decode_png` and `decode_jpeg` return is packed `0xAARRGGBB`. Every JPEG sample has alpha 255, and a gray JPEG sample carries its Y value in R, G and B. | Proved | proved | LAWS.bend jpeg_rgb_opaque; LAWS.bend jpeg_gray_opaque; LAWS.bend jpeg_decode_packed; LAWS.bend jpeg_decode_opaque; LAWS.bend jpeg_gray_level; LAWS.bend png_walk_anc | | IMG-PIX-2 | `encode_jpeg` reads only the low 24 bits of each sample: two rasters that differ only in alpha encode to the same bytes. | Proved | proved | LAWS.bend jpeg_alpha_blind | ### PNG (IMG-PNG) @@ -52,14 +52,14 @@ What a sample means, for every decoder and encoder. | ID | Requirement | Level | Status | Law | | :---- | :---- | :---- | :---- | :---- | | IMG-PNG-1 | `parse_signature(xs)` is `Some(png_sig())` exactly when `xs` begins with the eight bytes 137 80 78 71 13 10 26 10, and none otherwise. | Proved | proved | LAWS.bend sig_opens; LAWS.bend sig_only | -| IMG-PNG-2 | For every well-formed raster `r` with both sides nonzero, `decode_png(encode_png(r))` is `Some(r)`. | Proved | pending | LAWS.bend png_roundtrip_one | +| IMG-PNG-2 | For every well-formed raster `r` with both sides nonzero and fewer than 2^29 samples, `decode_png(encode_png(r))` is `Some(r)`. | Proved | proved | LAWS.bend png_roundtrip_one; LAWS.bend png_roundtrip | | IMG-PNG-3 | For every raster `r` of fewer than 2^32 samples, `encode_png(r)` is none exactly when a side of `r` is 0, `r` is not well formed, or `w * h` is 2^32 or more. | Proved | proved | LAWS.bend png_encodes; LAWS.bend png_refuses | | IMG-PNG-4 | When `encode_png(r)` is some, its IHDR has bit depth 8 and interlace 0, and colour type 2 exactly when every sample of `r` has alpha 255, colour type 6 otherwise. | Proved | proved | LAWS.bend png_ihdr | | IMG-PNG-5 | `Crc.crc32(xs)` equals the bitwise ISO 3309 CRC-32 of `xs` for every byte list, and `decode_png` returns none for every input in which some chunk's stored CRC differs from the CRC-32 of its type and data. | Proved | proved | LAWS.bend crc32_bitwise; LAWS.bend png_crc_refused | | IMG-PNG-6 | For every filter type 0 to 4 and every scanlines of a given width and bytes per pixel, `Png.unfilter` applied to the rows filtered by the PNG specification's filter function returns `Some` of the rows. | Proved | proved | LAWS.bend unfilter_filter | | IMG-PNG-7 | For every byte list `xs` shorter than 2^32 bytes and every block size from 1 to 65535, `Inf.inflate` of the zlib stream of `xs` in stored blocks of that size is `Some(xs)`. | Proved | proved | LAWS.bend inflate_stored; LAWS.bend inflate_enc_zlib | | IMG-PNG-8 | `decode_png` returns none for every input whose IHDR has a bit depth other than 8, an interlace method other than 0, or a colour type outside 0, 2, 3, 4 and 6. | Proved | proved | LAWS.bend png_depth_refused; LAWS.bend png_interlace_refused; LAWS.bend png_colour_refused | -| IMG-PNG-9 | For each colour type, each decoded sample is the packing of the unfiltered bytes the PNG specification gives: gray `g` as `0xFFgggggg`, gray and alpha as `0xAAgggggg`, RGB as `0xFFrrggbb`, RGBA as `0xAArrggbb`, an index as its palette entry with alpha from tRNS or 255, and a colour matching a tRNS key with alpha 0. | Proved | pending | LAWS.bend png_px_grey; LAWS.bend png_px_grey_key; LAWS.bend png_px_ga; LAWS.bend png_px_rgb; LAWS.bend png_px_rgb_key; LAWS.bend png_px_rgba; LAWS.bend png_px_indexed; LAWS.bend png_samples_frame; LAWS.bend png_walk | +| IMG-PNG-9 | For each colour type, each decoded sample is the packing of the unfiltered bytes the PNG specification gives: gray `g` as `0xFFgggggg`, gray and alpha as `0xAAgggggg`, RGB as `0xFFrrggbb`, RGBA as `0xAArrggbb`, an index as its palette entry with alpha from tRNS or 255, and a colour matching a tRNS key with alpha 0. | Proved | proved | LAWS.bend png_px_grey; LAWS.bend png_px_grey_key; LAWS.bend png_px_ga; LAWS.bend png_px_rgb; LAWS.bend png_px_rgb_key; LAWS.bend png_px_rgba; LAWS.bend png_px_indexed; LAWS.bend png_samples_frame; LAWS.bend png_walk; LAWS.bend png_walk_anc | | IMG-PNG-10 | `decode_png` reads the fixed and dynamic Huffman DEFLATE streams other encoders (zlib, libpng) write. | Trusted | | | ### JPEG (IMG-JPG) @@ -82,13 +82,10 @@ What a sample means, for every decoder and encoder. | ID | Proved so far | Missing | | :---- | :---- | :---- | | 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 | -Every Proved row but IMG-JPG-1, IMG-JPG-2, 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 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). +Every Proved row but IMG-JPG-1, IMG-JPG-2, IMG-JPG-4, IMG-JPG-5, IMG-PIX-1, IMG-PIX-2, IMG-RAS-1, IMG-RAS-2, IMG-RAS-3, IMG-RAS-4, IMG-RAS-5, IMG-PNG-1, IMG-PNG-2, IMG-PNG-3, IMG-PNG-4, IMG-PNG-5, IMG-PNG-6, IMG-PNG-7, IMG-PNG-8 and IMG-PNG-9 is pending; IMG-JPG-3 has 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). +No 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 4dccb4b..c6cbfc3 100644 --- a/docs/rfc/ezimg-law-inventory.md +++ b/docs/rfc/ezimg-law-inventory.md @@ -332,4 +332,5 @@ requirement depends on. | 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 | | WP13, JPEG planes (IMG-JPG-2) | done; IMG-JPG-2 proved | `jpeg_get_set` (`Array.get` at an index finds what the last `Array.set` there wrote, in any array) and `jpeg_get_set_other` (on a perfect binary tree of 2^d leaves, d below 32, a set at one index below 2^d leaves what `Array.get` finds at another): the lemmas mirror an array as a data tree (`W13.Tr`, so a proof can use it twice), follow the index as `Array.swap.go` and `Array.get.go` walk it, and show the mask `i & (2^d - 1)` is `i` below 2^d (`mask_w`, the size of a perfect tree being the word of bit d, `size_bits`). `jpeg_plane_depth`: for 1 to 2^d points, d at most 31, `decode.depth` is below 32 and its 2^depth leaves are at least the points, the U32 shl wrap at exactly 2^31 points included (a bound on U32.log2 from below and above, `le1`, `le2`). `jpeg_paint_at`: painting a block onto such a plane leaves at pixel (x, y), point y * w + x, the sample whose pw by ph pixels take it in, the last in the block's order, else the old value (`jpg.block.at`). `jpeg_blocks_paint`: `decode.blocks` paints the k-th unit it decodes at `decode.geom` of the k-th place of its walk (`jpg.trace`) onto its component's plane. `jpeg_plane_at`: a plane after those units, read at (x, y), is `jpg.point`. `jpeg_place` composes them over `decode_jpeg`: for a frame of at most 2^31 points, pixel (x, y) of any raster it returns is gray of Y, or rgb of Y, Cb and Cr, each `jpg.point` over the decoded units from a zero plane; with `jpeg_walk_frame` (the walk is T.81 A.2.3's order), `jpeg_mcu_grid_comp` (each unit's A.2.3 grid place and sample size) and `jpeg_paint_at` (replication) that is the row. Where samples of different units would cover one pixel the later one shows; A.2.3's grid tiles the frame so none do, and that tiling arithmetic is not itself a law. Code, byte-identical on every probe: `decode.depth` tests zero with `U32.is_eq` instead of a literal pattern (`decode.depth.of`). Big Nat constants never appear in a checked type: the checker normalises `Nat.pow(2n, 32n)` even against itself, so the bound is `w * h <= 2^d, d <= 31`. Mutants: a pixel written one point on, the depth from nn - 1, Cb painted with component 2's units, a block of another component painted too, Cb and Cr swapped in the colour pass: each fails its law's proof; an `Array.set` one index on fails `jpeg_get_set` and one that also writes the next index fails `jpeg_get_set_other` on a concrete plane. Gate about 4 m 17 s, main's 4 m 25 s | | WP12, JPEG Huffman and scan (IMG-JPG-3) | done, partial; the encoder-side token structure is left | `jpeg_bits_round_trip`: codes of 1 to 16 bits written by `encode.bits` and padded by `encode.pad` are read back in order by `decode.read.n` across byte boundaries and stuffed bytes (a model writer and reader over byte-sized bit lists, `proof/wp12-jpeg-huffman.bend`). `jpeg_huff_dc`, `jpeg_huff_ac`: every Annex K symbol's code in `encode.huff`'s book, written between any codes, is decoded by `decode.huff` over `decode.canon`'s table to that symbol, the reader just after it (closed per-symbol checks over literal copies of the books and tables, grouped by code length so `decode.look` stays cheap). `jpeg_scan_some`: `decode.run`, in the encoder's frame, scan and tables, returns a picture for any bits `encode.bit` and `encode.pad` write that are as many well-formed blocks (DC code and magnitude, AC tokens ending with EOB or at the 64th coefficient) as `decode.nblocks` of the frame. Code: `decode.ac.sym` tests EOB and ZRL with `U32.is_eq` in a helper instead of literal patterns and `decode.ac.run` masks with the constant first, so the laws reduce on a symbolic symbol; `encode.pad.n` puts the constant first; outputs byte-identical on every probe. Left: that `encode.arm`'s bytes are such blocks (the neutral, solid and `encode.go` paths' token structure, with DC differences and AC coefficients in range) and that 3 * ceil(w / 8) * ceil(h / 8) equals `decode.nblocks` without wrap for at most 2^31 samples | +| WP11, PNG round trip past one block and ancillary chunks (IMG-PNG-2, IMG-PNG-9, IMG-PIX-1) | done; IMG-PNG-2 proved after rewording, IMG-PNG-9 and IMG-PIX-1 proved | IMG-PNG-2 reworded to rasters of fewer than 2^29 samples (the bound is a U32 witness `a8` whose value is `8 w h`), and `png_roundtrip` proves it: below 65536 scanline bytes through `png_roundtrip_one`, above it through the two wide paths. Colour type 6 (`enc.seal.wide`, `enc.pour`): `pour.bs` and the `sim` simulation against `enc.feed` give `zlib.stored(65535, raw)` with the IDAT CRC (`wide.tail`); colour type 2 (`enc.wide.rgb`, `enc.rgb.go`): the `rg` simulation with fuel `n + 2k + 1` bytes, `afold.adler` (the running Adler sums are `Inf.adler.of`) and `rgb.tail` give the same chunk. The block count `enc.nblk` is ceil(n / 65535) by U32 div and mod (`nb.k`), the IDAT length `n + 5k + 6` does not wrap (`idat.len`, `tu.ok`: `n <= 5 w h` and `n + 5k + 6 <= 8 w h`), and `rt.tail`, the one-block proof's pixel stage, now shared by both paths. IMG-PNG-9 and the PNG side of IMG-PIX-1: `png_walk_anc` extends `png_walk` to ancillary chunks after IHDR, between PLTE and tRNS, after them and after the IDAT chunks (`anc.walk`: the walker skips each, closing the IDAT run), and to tRNS before PLTE, the one other order the decoder takes (colour type 2); with trailing bytes, a second IHDR, PLTE or tRNS, a critical unknown chunk or an IDAT after the run refused, these are every file `decode_png` accepts. No code change. Lemmas in `proof/wp11-png-finish.bend`; the IHDR's CRC over a symbolic width and height is slow to normalise, so the wide proofs state the layout stuck on flags (`core.f`, `core.t`, `wide.rt`) and unfold it once. Mutants: a stored block's NLEN high byte from LEN in `enc.pour`, colour type 2's Adler B sum missing a byte in `enc.rgb.go`, `enc.nblk` adding 2 for a partial block, the ancillary bit read as bit 4, and PLTE refused after tRNS: each fails its lemma (`pour.bs`, `rg`, `nb.at`, `anc.step`, `plte.late`). No row is known to be false. Gate about 6 m 40 s, main's 4 m 30 s (the W11 lemmas check in about 2 m 30 s, the W9 import included) | | 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/wp11-png-finish.bend b/proof/wp11-png-finish.bend new file mode 100644 index 0000000..2003c08 --- /dev/null +++ b/proof/wp11-png-finish.bend @@ -0,0 +1,4234 @@ +# proof/wp11-png-finish: lemmas for the PNG encodings past one stored block +# (IMG-PNG-2) and for ancillary chunks in the chunk walker (IMG-PNG-9). Each +# is a law proved here, or a def whose type is the fact it proves; PROOF.bend +# imports this file, so the gate checks it. No number near 2^29 or 2^32 is +# written as a Nat: the checker holds a Nat in unary. +import Base +import ../LAWS.bend as Laws +import ../main.bend as Img +import ../src/crc.bend as Crc +import ../src/png.bend as Png +import ../src/inflate.bend as Inf +import ./u32.bend as U32L +import ./wp1-crc.bend as Wp1 +import ./wp2-inflate.bend as Wp2 +import ./wp6-raster.bend as R +import ./wp9-png-roundtrip.bend as W9 + +# ---- A. U32 and Nat facts ---- + +# U32s of the same value are the same U32 +def u32.same(+uu: U32, +vv: U32, en: {U32.to_nat(uu) == U32.to_nat(vv) : Nat}) -> {uu == vv : U32}: + U32L.ueq(uu, vv, Equal.trans(Bool, U32.is_eq(uu, vv), Nat.is_eq(U32.to_nat(vv), U32.to_nat(vv)), True{}, + Equal.trans(Bool, U32.is_eq(uu, vv), Nat.is_eq(U32.to_nat(uu), U32.to_nat(vv)), Nat.is_eq(U32.to_nat(vv), + U32.to_nat(vv)), R.u32_eq(uu, vv), Equal.cong(Nat, Bool, kk => Nat.is_eq(kk, U32.to_nat(vv)), U32.to_nat(uu), + U32.to_nat(vv), en)), + R.nat_eq_refl(U32.to_nat(vv)))) + +# a U32 is_zero calls zero is worth 0 +def zero.val(+uu: U32, ez: {True{} == U32.is_zero(uu) : Bool}) -> {U32.to_nat(uu) == 0n : Nat}: + Equal.cong(U32, Nat, xx => U32.to_nat(xx), uu, 0, U32L.ueq(uu, 0, Equal.sym(Bool, True{}, U32.is_zero(uu), ez))) + +# a U32 worth 0 is zero +def zero.is(+uu: U32, e0: {U32.to_nat(uu) == 0n : Nat}) -> {U32.is_zero(uu) == True{} : Bool}: + Equal.trans(Bool, U32.is_zero(uu), Nat.is_eq(U32.to_nat(uu), 0n), True{}, R.u32_eq(uu, 0), + Equal.cong(Nat, Bool, kk => Nat.is_eq(kk, 0n), U32.to_nat(uu), 0n, e0)) + +# a U32 is_eq calls 1 is worth 1 +def one.val(+uu: U32, e1: {True{} == U32.is_eq(uu, 1) : Bool}) -> {U32.to_nat(uu) == 1n : Nat}: + Equal.cong(U32, Nat, xx => U32.to_nat(xx), uu, 1, U32L.ueq(uu, 1, Equal.sym(Bool, True{}, U32.is_eq(uu, 1), e1))) + +# a U32 above some successor's predecessor is not zero, once is_zero's answer is named +def pos.at( + zz: Bool, + +uu: U32, + +kk: Nat, + +ez: {U32.is_zero(uu) == zz : Bool}, + +hh: {Nat.is_le(1n+kk, U32.to_nat(uu)) == True{} : Bool} +) -> {U32.is_zero(uu) == False{} : Bool}: + match zz: + case False{}: + ez + case True{}: + +e0 = {zero.val(uu, Equal.sym(Bool, U32.is_zero(uu), True{}, ez)) : {U32.to_nat(uu) == 0n : Nat}} + Empty.absurd({U32.is_zero(uu) == False{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, + Nat.is_le(1n+kk, U32.to_nat(uu)), True{}, Equal.sym(Bool, Nat.is_le(1n+kk, U32.to_nat(uu)), False{}, + Equal.cong(Nat, Bool, xx => Nat.is_le(1n+kk, xx), U32.to_nat(uu), 0n, e0)), hh))) + +# a U32 at least a successor is not zero +def pos( + +uu: U32, + +kk: Nat, + hh: {Nat.is_le(1n+kk, U32.to_nat(uu)) == True{} : Bool} +) -> {U32.is_zero(uu) == False{} : Bool}: + pos.at(U32.is_zero(uu), uu, kk, {==}, hh) + +# a U32 neither 0 nor 1 less one is not zero, once is_zero's answer is named +def two.at( + zz: Bool, + +uu: U32, + +ez: {U32.is_zero((uu - 1 : U32)) == zz : Bool}, + +hz: {False{} == U32.is_zero(uu) : Bool}, + +h1: {False{} == U32.is_eq(uu, 1) : Bool} +) -> {U32.is_zero((uu - 1 : U32)) == False{} : Bool}: + match zz: + case False{}: + ez + case True{}: + +e0 = {zero.val((uu - 1 : U32), Equal.sym(Bool, U32.is_zero((uu - 1 : U32)), True{}, ez)) : + {U32.to_nat((uu - 1 : U32)) == 0n : Nat}} + +e1 = {Equal.trans(Nat, U32.to_nat(uu), 1n+U32.to_nat((uu - 1 : U32)), 1n, Wp1.u32_dec(uu, Equal.sym(Bool, + False{}, U32.is_zero(uu), hz)), Equal.cong(Nat, Nat, kk => 1n+kk, U32.to_nat((uu - 1 : U32)), 0n, e0)) : + {U32.to_nat(uu) == 1n : Nat}} + Empty.absurd({U32.is_zero((uu - 1 : U32)) == False{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, + U32.is_eq(uu, 1), True{}, h1, Equal.trans(Bool, U32.is_eq(uu, 1), Nat.is_eq(U32.to_nat(uu), 1n), True{}, + R.u32_eq(uu, 1), Equal.cong(Nat, Bool, kk => Nat.is_eq(kk, 1n), U32.to_nat(uu), 1n, e1))))) + +# a U32 neither 0 nor 1, less one, is not zero +def two( + +uu: U32, + +hz: {False{} == U32.is_zero(uu) : Bool}, + +h1: {False{} == U32.is_eq(uu, 1) : Bool} +) -> {U32.is_zero((uu - 1 : U32)) == False{} : Bool}: + two.at(U32.is_zero((uu - 1 : U32)), uu, {==}, hz, h1) + +# not at most is above +def nle.lt(aa: Nat, bb: Nat, hh: {Nat.is_le(aa, bb) == False{} : Bool}) -> {Nat.is_lt(bb, aa) == True{} : Bool}: + match aa bb: + case 0n bb0: + Empty.absurd({Nat.is_lt(bb0, 0n) == True{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, Nat.is_le(0n, bb0), + True{}, Equal.sym(Bool, Nat.is_le(0n, bb0), False{}, hh), R.le_zero(bb0)))) + case 1n+_ap 0n: + {==} + case 1n+ap 1n+bp: + nle.lt(ap, bp, hh) + +# one more on the left of a sum, one less on the right: the same sum +def shift( + +aa: Nat, + +uu: U32, + +vv: U32, + +nn: Nat, + +ev: {U32.to_nat(uu) == 1n+U32.to_nat(vv) : Nat}, + +ee: {Nat.add(aa, U32.to_nat(uu)) == nn : Nat} +) -> {Nat.add(1n+aa, U32.to_nat(vv)) == nn : Nat}: + Equal.trans(Nat, 1n+Nat.add(aa, U32.to_nat(vv)), Nat.add(aa, U32.to_nat(uu)), nn, + Equal.trans(Nat, 1n+Nat.add(aa, U32.to_nat(vv)), Nat.add(aa, 1n+U32.to_nat(vv)), Nat.add(aa, U32.to_nat(uu)), + Equal.sym(Nat, Nat.add(aa, 1n+U32.to_nat(vv)), 1n+Nat.add(aa, U32.to_nat(vv)), R.add_succ(aa, + U32.to_nat(vv))), + Equal.cong(Nat, Nat, kk => Nat.add(aa, kk), 1n+U32.to_nat(vv), U32.to_nat(uu), Equal.sym(Nat, U32.to_nat(uu), + 1n+U32.to_nat(vv), ev))), + ee) + +# a list at most cap's value long is counted in a U32 to its length +def len.of( + +xs: List<&2, U32>, + +cap: U32, + +hh: {Nat.is_le(List.length(&2, U32, xs), U32.to_nat(cap)) == True{} : Bool} +) -> {U32.to_nat(Png.enc.len(xs, 0)) == List.length(&2, U32, xs) : Nat}: + Equal.trans(Nat, U32.to_nat(Png.enc.len(xs, 0)), U32.to_nat(U32.from_nat(List.length(&2, U32, xs))), + List.length(&2, U32, xs), Equal.cong(U32, Nat, uu => U32.to_nat(uu), Png.enc.len(xs, 0), + U32.from_nat(List.length(&2, U32, xs)), W9.len.word(xs)), W9.nat.fits(List.length(&2, U32, xs), cap, hh)) + +# ---- B. enc.crc.list, and the bytes enc.pour writes ---- + +# enc.crc.list is the CRC fold and the bytes reversed onto the accumulator +def crcl( + xs: List<&2, U32>, + +cc: U32, + +acc: List<&2, U32> +) -> {Png.enc.crc.list(xs, cc, acc) == (Crc.crc.fold(xs, cc), List.reverse.go(&2, U32, xs, acc)) : U32 & List<&2, U32>}: + match xs: + case Nil{}: + {==} + case +hd <> tl: + crcl(tl, Crc.crc.byte(cc, hd), hd <> acc) + +# the bytes enc.pour writes, in order: its walk without the CRC +def bs( + xs: List<&2, U32>, + znew: Bool, + zlast: Bool, + +room: U32, + +left: U32, + +len: U32, + +bf: U32 +) -> List<&2, U32>: + match xs znew zlast: + case Nil{} _z _last: + [] + case +h <> +t False{} False{}: + +room2 = (room - 1 : U32) + h <> bs(t, False{}, U32.is_eq(room2, 1), room2, (left - 1 : U32), len, bf) + case +h <> +t False{} True{}: + +left2 = (left - 1 : U32) + h <> bs(t, True{}, False{}, 0, left2, Png.enc.cap(left2), Png.enc.bf(left2)) + case +h <> t True{} _last: + +nlen = (len .^. 65535 : U32) + +left2 = (left - 1 : U32) + +room2 = (len - 1 : U32) + bf <> Png.enc.byte(len) <> Png.enc.hi(len) <> Png.enc.byte(nlen) <> Png.enc.hi(nlen) <> h <> bs(t, + U32.is_zero(room2), U32.is_eq(room2, 1), room2, left2, Png.enc.cap(left2), Png.enc.bf(left2)) + +# enc.pour is enc.crc.list over bs +def pour.bs( + xs: List<&2, U32>, + znew: Bool, + zlast: Bool, + +room: U32, + +left: U32, + +len: U32, + +bf: U32, + +cc: U32, + +acc: List<&2, U32> +) -> {Png.enc.pour(xs, znew, zlast, room, left, len, bf, cc, acc) == Png.enc.crc.list(bs(xs, znew, zlast, room, left, + len, bf), cc, acc) : U32 & List<&2, U32>}: + match xs znew zlast: + case Nil{} _z _last: + {==} + case +h <> +t False{} False{}: + pour.bs(t, False{}, U32.is_eq((room - 1 : U32), 1), (room - 1 : U32), (left - 1 : U32), len, bf, + Crc.crc.byte(cc, h), h <> acc) + case +h <> +t False{} True{}: + +left2 = (left - 1 : U32) + pour.bs(t, True{}, False{}, 0, left2, Png.enc.cap(left2), Png.enc.bf(left2), Crc.crc.byte(cc, h), h <> acc) + case +h <> t True{} _last: + +nlen = (len .^. 65535 : U32) + +b1 = Png.enc.byte(len) + +b2 = Png.enc.hi(len) + +b3 = Png.enc.byte(nlen) + +b4 = Png.enc.hi(nlen) + +c1 = Crc.crc.byte(cc, bf) + +c2 = Crc.crc.byte(c1, b1) + +c3 = Crc.crc.byte(c2, b2) + +c4 = Crc.crc.byte(c3, b3) + +c5 = Crc.crc.byte(c4, b4) + +left2 = (left - 1 : U32) + +room2 = (len - 1 : U32) + pour.bs(t, U32.is_zero(room2), U32.is_eq(room2, 1), room2, left2, Png.enc.cap(left2), Png.enc.bf(left2), + Crc.crc.byte(c5, h), h <> b4 <> b3 <> b2 <> b1 <> bf <> acc) + +# ---- C. bs against enc.feed, byte by byte ---- + +# The block size, 65535, is named mx here and its value is U32.to_nat(mx): a +# closed Nat as large as 65535 in a type is expanded in unary and overflows +# the checker's stack. Each lemma takes mx with {mx == 65535 : U32}. + +# where the open block stands against the nx bytes still to come: a final +# block holds them all, rp their count; any other block is full, its room the +# feed's, and more bytes follow it +def fin.ok(fin: Bool, +rp: U32, +rf: U32, +nx: Nat) -> Type: + match fin: + case True{}: + {U32.to_nat(rp) == nx : Nat} + case False{}: + {U32.to_nat(rp) == U32.to_nat(rf) : Nat} & {Nat.is_lt(U32.to_nat(rp), nx) == True{} : Bool} + +# at a block boundary the walk carries the next block's length and BFINAL +def at.edge(+rp: U32, +left: U32, +plen: U32, +pbf: U32) -> Type: + {U32.is_zero(rp) == True{} : Bool} -> {plen == Png.enc.cap(left) : U32} & {pbf == Png.enc.bf(left) : U32} + +# what sim proves: the open block's header, the block so far, then the rest +# of bs are enc.feed's blocks of mx bytes +def sim.ty( + +xs: List<&2, U32>, + +znew: Bool, + +zlast: Bool, + +zf: Bool, + +fin: Bool, + +acc: List<&2, U32>, + +rf: U32, + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +ll: U32, + +mx: U32 +) -> Type: + {List.append(&2, U32, Png.enc.head(fin, ll, (ll .^. 65535 : U32)), List.reverse.go(&2, U32, acc, bs(xs, znew, zlast, + rp, left, plen, pbf))) == Png.enc.feed(xs, zf, rf, mx, acc) : List<&2, U32>} + +# one byte less on both sides of fin.ok +def fo.dec( + fin: Bool, + +rp: U32, + +rf: U32, + +rp2: U32, + +rf2: U32, + +nt: Nat, + +ep: {U32.to_nat(rp) == 1n+U32.to_nat(rp2) : Nat}, + +ef: {U32.to_nat(rf) == 1n+U32.to_nat(rf2) : Nat}, + hf: fin.ok(fin, rp, rf, 1n+nt) +) -> fin.ok(fin, rp2, rf2, nt): + match fin: + case True{}: + Wp1.succ_inj(U32.to_nat(rp2), nt, Equal.trans(Nat, 1n+U32.to_nat(rp2), U32.to_nat(rp), 1n+nt, Equal.sym(Nat, + U32.to_nat(rp), 1n+U32.to_nat(rp2), ep), hf)) + case False{}: + (e_rr, h_lt) = hf + +e2 = {Wp1.succ_inj(U32.to_nat(rp2), U32.to_nat(rf2), Equal.trans(Nat, 1n+U32.to_nat(rp2), U32.to_nat(rp), + 1n+U32.to_nat(rf2), Equal.sym(Nat, U32.to_nat(rp), 1n+U32.to_nat(rp2), ep), Equal.trans(Nat, U32.to_nat(rp), + U32.to_nat(rf), 1n+U32.to_nat(rf2), e_rr, ef))) : {U32.to_nat(rp2) == U32.to_nat(rf2) : Nat}} + +l2 = {Equal.trans(Bool, Nat.is_lt(1n+U32.to_nat(rp2), 1n+nt), Nat.is_lt(U32.to_nat(rp), 1n+nt), True{}, + Equal.cong(Nat, Bool, kk => Nat.is_lt(kk, 1n+nt), 1n+U32.to_nat(rp2), U32.to_nat(rp), Equal.sym(Nat, + U32.to_nat(rp), 1n+U32.to_nat(rp2), ep)), h_lt) : {Nat.is_lt(U32.to_nat(rp2), nt) == True{} : Bool}} + (e2, l2) + +# enc.cap.at with the block size named +def capm(zz: Bool, +left: U32, +mx: U32) -> U32: + match zz: + case True{}: + left + case False{}: + mx + +# enc.cap.at is capm at 65535 +def cap.m( + zz: Bool, + +left: U32, + +mx: U32, + +emx: {mx == 65535 : U32} +) -> {Png.enc.cap.at(zz, left) == capm(zz, left, mx) : U32}: + match zz: + case True{}: + {==} + case False{}: + Equal.sym(U32, mx, 65535, emx) + +# the block size is not zero +def mx.nz(+mx: U32, +emx: {mx == 65535 : U32}) -> {U32.is_zero(mx) == False{} : Bool}: + Equal.cong(U32, Bool, uu => U32.is_zero(uu), mx, 65535, emx) + +# at most 65535 is at most the block size +def le.mx(+left: U32, +mx: U32, +emx: {mx == 65535 : U32}) -> {U32.is_le(left, mx) == U32.is_le(left, 65535) : Bool}: + Equal.cong(U32, Bool, uu => U32.is_le(left, uu), mx, 65535, emx) + +# a new block's length is not zero when bytes are left +def cap.nz( + zz: Bool, + +left: U32, + +mx: U32, + +kk: Nat, + +nzm: {U32.is_zero(mx) == False{} : Bool}, + +hn: {U32.to_nat(left) == 1n+kk : Nat} +) -> {U32.is_zero(capm(zz, left, mx)) == False{} : Bool}: + match zz: + case True{}: + Wp1.u32_nonzero(left, kk, hn) + case False{}: + nzm + +# a new block's room after its first byte is within the feed's +def cap.le( + zz: Bool, + +left: U32, + +mx: U32, + +kk: Nat, + +nzm: {U32.is_zero(mx) == False{} : Bool}, + +ez: {U32.is_le(left, mx) == zz : Bool}, + +hn: {U32.to_nat(left) == 1n+kk : Nat} +) -> {Nat.is_le(U32.to_nat((capm(zz, left, mx) - 1 : U32)), U32.to_nat((mx - 1 : U32))) == True{} : Bool}: + match zz: + case True{}: + +l1 = (left - 1 : U32) + +m1 = (mx - 1 : U32) + +e_l = {Wp1.u32_dec(left, Wp1.u32_nonzero(left, kk, hn)) : {U32.to_nat(left) == 1n+U32.to_nat(l1) : Nat}} + +e_m = {Wp1.u32_dec(mx, nzm) : {U32.to_nat(mx) == 1n+U32.to_nat(m1) : Nat}} + +h0 = {Equal.trans(Bool, Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), U32.is_le(left, mx), True{}, + Equal.sym(Bool, U32.is_le(left, mx), Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), R.u32_le(left, mx)), ez) : + {Nat.is_le(U32.to_nat(left), U32.to_nat(mx)) == True{} : Bool}} + R.le_rw_r(1n+U32.to_nat(l1), U32.to_nat(mx), 1n+U32.to_nat(m1), e_m, R.le_rw_l(U32.to_nat(left), + 1n+U32.to_nat(l1), U32.to_nat(mx), e_l, h0)) + case False{}: + R.le_refl(U32.to_nat((mx - 1 : U32))) + +# where a new block stands: final when the bytes left fit, else full +def cap.fo( + zz: Bool, + +left: U32, + +mx: U32, + +kk: Nat, + +nzm: {U32.is_zero(mx) == False{} : Bool}, + +ez: {U32.is_le(left, mx) == zz : Bool}, + +hn: {U32.to_nat(left) == 1n+kk : Nat} +) -> fin.ok(zz, (capm(zz, left, mx) - 1 : U32), (mx - 1 : U32), kk): + match zz: + case True{}: + Wp1.u32_pred(left, kk, hn) + case False{}: + +m1 = (mx - 1 : U32) + +e_m = {Wp1.u32_dec(mx, nzm) : {U32.to_nat(mx) == 1n+U32.to_nat(m1) : Nat}} + +h0 = {Equal.trans(Bool, Nat.is_le(1n+kk, 1n+U32.to_nat(m1)), U32.is_le(left, mx), False{}, + Equal.sym(Bool, U32.is_le(left, mx), Nat.is_le(1n+kk, 1n+U32.to_nat(m1)), Equal.trans(Bool, + U32.is_le(left, mx), Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), Nat.is_le(1n+kk, 1n+U32.to_nat(m1)), + R.u32_le(left, mx), Equal.trans(Bool, Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), Nat.is_le(1n+kk, + U32.to_nat(mx)), Nat.is_le(1n+kk, 1n+U32.to_nat(m1)), Equal.cong(Nat, Bool, xx => Nat.is_le(xx, + U32.to_nat(mx)), U32.to_nat(left), 1n+kk, hn), Equal.cong(Nat, Bool, xx => Nat.is_le(1n+kk, xx), + U32.to_nat(mx), 1n+U32.to_nat(m1), e_m)))), ez) : {Nat.is_le(kk, U32.to_nat(m1)) == False{} : Bool}} + ({==}, nle.lt(kk, U32.to_nat(m1), h0)) + +# fin.ok of enc.cap.at's room from capm's +def fo.cm( + +zz: Bool, + +left: U32, + +mx: U32, + +nt: Nat, + +emx: {mx == 65535 : U32}, + hh: fin.ok(zz, (capm(zz, left, mx) - 1 : U32), (mx - 1 : U32), nt) +) -> fin.ok(zz, (Png.enc.cap.at(zz, left) - 1 : U32), (mx - 1 : U32), nt): + %Equal.sym(U32, Png.enc.cap.at(zz, left), capm(zz, left, mx), cap.m(zz, left, mx, emx)) : fin.ok(zz, (_ - 1 : U32), + (mx - 1 : U32), nt) + hh + +# the header bs writes at a new block is enc.head's for the block's BFINAL +def hdr( + zz: Bool, + +left: U32, + +rest: List<&2, U32> +) -> {Png.enc.bf.at(zz) <> Png.enc.byte(Png.enc.cap.at(zz, left)) <> Png.enc.hi(Png.enc.cap.at(zz, + left)) <> Png.enc.byte((Png.enc.cap.at(zz, left) .^. 65535 : U32)) <> Png.enc.hi((Png.enc.cap.at(zz, + left) .^. 65535 : U32)) <> rest == List.append(&2, U32, Png.enc.head(zz, Png.enc.cap.at(zz, left), + (Png.enc.cap.at(zz, left) .^. 65535 : U32)), rest) : List<&2, U32>}: + match zz: + case True{}: + {==} + case False{}: + {==} + +# the open block, reversed, is counted to its header's length when it ends +def len.blk( + +acc: List<&2, U32>, + +rf: U32, + +ll: U32, + +mx: U32, + +ha: {Nat.add(List.length(&2, U32, acc), U32.to_nat(rf)) == U32.to_nat(mx) : Nat}, + +hl: {U32.to_nat(ll) == List.length(&2, U32, acc) : Nat} +) -> {Png.enc.len(List.reverse(&2, U32, acc), 0) == ll : U32}: + +ra = List.reverse(&2, U32, acc) + +na = List.length(&2, U32, acc) + +e_r = {Wp2.len_rev(acc) : {List.length(&2, U32, ra) == na : Nat}} + +h_b = {Equal.trans(Bool, Nat.is_le(na, U32.to_nat(mx)), Nat.is_le(na, Nat.add(na, U32.to_nat(rf))), True{}, + Equal.cong(Nat, Bool, kk => Nat.is_le(na, kk), U32.to_nat(mx), Nat.add(na, U32.to_nat(rf)), Equal.sym(Nat, + Nat.add(na, U32.to_nat(rf)), U32.to_nat(mx), ha)), Wp2.nle_add(na, U32.to_nat(rf))) : {Nat.is_le(na, + U32.to_nat(mx)) == True{} : Bool}} + +h_r = {R.le_rw_l(na, List.length(&2, U32, ra), U32.to_nat(mx), Equal.sym(Nat, List.length(&2, U32, ra), na, e_r), + h_b) : {Nat.is_le(List.length(&2, U32, ra), U32.to_nat(mx)) == True{} : Bool}} + u32.same(Png.enc.len(ra, 0), ll, Equal.trans(Nat, U32.to_nat(Png.enc.len(ra, 0)), na, U32.to_nat(ll), Equal.trans(Nat, + U32.to_nat(Png.enc.len(ra, 0)), List.length(&2, U32, ra), na, len.of(ra, mx, h_r), e_r), Equal.sym(Nat, + U32.to_nat(ll), na, hl))) + +# no bytes left: the open block is enc.feed's final block +def sim.nil( + fin: Bool, + +acc: List<&2, U32>, + +rf: U32, + +rp: U32, + +ll: U32, + +mx: U32, + +ha: {Nat.add(List.length(&2, U32, acc), U32.to_nat(rf)) == U32.to_nat(mx) : Nat}, + +hl: {U32.to_nat(ll) == Nat.add(List.length(&2, U32, acc), U32.to_nat(rp)) : Nat}, + hf: fin.ok(fin, rp, rf, 0n) +) -> {List.append(&2, U32, Png.enc.head(fin, ll, (ll .^. 65535 : U32)), List.reverse(&2, U32, acc)) == + Png.enc.stored(True{}, List.reverse(&2, U32, acc)) : List<&2, U32>}: + match fin: + case False{}: + (_e_rr, h_lt) = hf + Empty.absurd({List.append(&2, U32, Png.enc.head(False{}, ll, (ll .^. 65535 : U32)), List.reverse(&2, U32, acc)) == + Png.enc.stored(True{}, List.reverse(&2, U32, acc)) : List<&2, U32>}, U32L.false_true(Equal.trans(Bool, + False{}, Nat.is_lt(U32.to_nat(rp), 0n), True{}, R.lt_zero_false(U32.to_nat(rp)), h_lt))) + case True{}: + +e_rp = {hf : {U32.to_nat(rp) == 0n : Nat}} + +ra = List.reverse(&2, U32, acc) + +na = List.length(&2, U32, acc) + +hd = Png.enc.head(True{}, ll, (ll .^. 65535 : U32)) + +hl0 = {Equal.trans(Nat, U32.to_nat(ll), Nat.add(na, 0n), na, Equal.trans(Nat, U32.to_nat(ll), Nat.add(na, + U32.to_nat(rp)), Nat.add(na, 0n), hl, Equal.cong(Nat, Nat, kk => Nat.add(na, kk), U32.to_nat(rp), 0n, e_rp)), + R.add_zero(na)) : {U32.to_nat(ll) == na : Nat}} + +e_len = {len.blk(acc, rf, ll, mx, ha, hl0) : {Png.enc.len(ra, 0) == ll : U32}} + Equal.trans(List<&2, U32>, List.append(&2, U32, hd, ra), Png.enc.cat(hd, ra), Png.enc.stored(True{}, ra), + Equal.sym(List<&2, U32>, Png.enc.cat(hd, ra), List.append(&2, U32, hd, ra), Wp2.cat_app(hd, ra)), + Equal.cong(U32, List<&2, U32>, uu => Png.enc.cat(Png.enc.head(True{}, uu, (uu .^. 65535 : U32)), ra), ll, + Png.enc.len(ra, 0), Equal.sym(U32, Png.enc.len(ra, 0), ll, e_len))) + +# a room that is not zero leaves at.edge nothing to ask +def edge.no( + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +nz: {U32.is_zero(rp) == False{} : Bool} +) -> at.edge(rp, left, plen, pbf): + ez => Empty.absurd({plen == Png.enc.cap(left) : U32} & {pbf == Png.enc.bf(left) : U32}, + U32L.false_true(Equal.trans(Bool, False{}, U32.is_zero(rp), True{}, Equal.sym(Bool, U32.is_zero(rp), False{}, nz), + ez))) + +# a new block's bytes as bs writes them for length u and BFINAL v: the +# header, the first byte, then the rest of the walk +def open.bs( + +hd: U32, + +tl: List<&2, U32>, + +l2: U32, + +uu: U32, + +vv: U32 +) -> List<&2, U32>: + +nlen = (uu .^. 65535 : U32) + +room2 = (uu - 1 : U32) + vv <> Png.enc.byte(uu) <> Png.enc.hi(uu) <> Png.enc.byte(nlen) <> Png.enc.hi(nlen) <> hd <> bs(tl, U32.is_zero(room2), + U32.is_eq(room2, 1), room2, l2, Png.enc.cap(l2), Png.enc.bf(l2)) + +# a new block's header for left bytes, as enc.feed opens it +def hd1(+left: U32) -> List<&2, U32>: + Png.enc.head(U32.is_le(left, 65535), Png.enc.cap(left), (Png.enc.cap(left) .^. 65535 : U32)) + +# bs after a new block's first byte, for left bytes +def bs1(+tl: List<&2, U32>, +left: U32) -> List<&2, U32>: + +room = (Png.enc.cap(left) - 1 : U32) + bs(tl, U32.is_zero(room), U32.is_eq(room, 1), room, (left - 1 : U32), Png.enc.cap((left - 1 : U32)), + Png.enc.bf((left - 1 : U32))) + +# enc.feed after a new block's first byte hd +def fd1(+tl: List<&2, U32>, +mx: U32, +hd: U32) -> List<&2, U32>: + Png.enc.feed(tl, U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [hd]) + +# a new block from left bytes, hd then tl: bs's bytes are enc.feed's from a +# block holding hd alone +def open.feed( + +hd: U32, + +tl: List<&2, U32>, + +left: U32, + +mx: U32, + +ih: {List.append(&2, U32, hd1(left), hd <> bs1(tl, left)) == fd1(tl, mx, hd) : List<&2, U32>} +) -> {open.bs(hd, tl, (left - 1 : U32), Png.enc.cap(left), Png.enc.bf(left)) == Png.enc.feed(tl, + U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [hd]) : List<&2, U32>}: + +zz = U32.is_le(left, 65535) + +cp = Png.enc.cap(left) + +l2 = (left - 1 : U32) + +q = bs(tl, U32.is_zero((cp - 1 : U32)), U32.is_eq((cp - 1 : U32), 1), (cp - 1 : U32), l2, Png.enc.cap(l2), + Png.enc.bf(l2)) + Equal.trans(List<&2, U32>, open.bs(hd, tl, l2, cp, Png.enc.bf(left)), List.append(&2, U32, Png.enc.head(zz, cp, + (cp .^. + 65535 : U32)), hd <> q), Png.enc.feed(tl, U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [hd]), + hdr(zz, left, hd <> q), ih) + +# the simulation's hypothesis for the rest of a block opened by hd +def open.ih.ty(+hd: U32, +tl: List<&2, U32>, +left: U32, +mx: U32) -> Type: + @zn: Bool -> @zl: Bool -> @zf: Bool -> @fin: Bool -> @rf: U32 -> @rp: U32 -> @plen: U32 -> @pbf: U32 -> + @ll: U32 -> {zn == U32.is_zero(rp) : Bool} -> {zl == U32.is_eq(rp, 1) : Bool} -> {zf == U32.is_zero(rf) : Bool} -> + {Nat.add(1n, U32.to_nat(rf)) == U32.to_nat(mx) : Nat} -> {U32.to_nat(ll) == Nat.add(1n, U32.to_nat(rp)) : Nat} + -> {Nat.is_le(U32.to_nat(rp), U32.to_nat(rf)) == True{} : Bool} -> {U32.to_nat((left - 1 : U32)) == + List.length(&2, U32, tl) : Nat} -> fin.ok(fin, rp, rf, List.length(&2, U32, tl)) -> at.edge(rp, (left - 1 : U32), + plen, pbf) -> sim.ty(tl, zn, zl, zf, fin, [hd], rf, rp, (left - 1 : U32), plen, pbf, ll, mx) + +# the facts a new block's walk starts from, for sim: the feed holds hd, with +# mx - 1 bytes of room; bs has cap(left) - 1 left in the block +def open.ih( + +hd: U32, + +tl: List<&2, U32>, + +left: U32, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(left) == 1n+List.length(&2, U32, tl) : Nat}, + sim_t: open.ih.ty(hd, tl, left, mx) +) -> {open.bs(hd, tl, (left - 1 : U32), Png.enc.cap(left), Png.enc.bf(left)) == Png.enc.feed(tl, + U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [hd]) : List<&2, U32>}: + +nt = List.length(&2, U32, tl) + +zz = U32.is_le(left, 65535) + +cp = Png.enc.cap(left) + +cm = capm(zz, left, mx) + +c1 = (cp - 1 : U32) + +m1 = (mx - 1 : U32) + +l2 = (left - 1 : U32) + +nzm = {mx.nz(mx, emx) : {U32.is_zero(mx) == False{} : Bool}} + +e_cm = {cap.m(zz, left, mx, emx) : {cp == cm : U32}} + +ezm = {le.mx(left, mx, emx) : {U32.is_le(left, mx) == zz : Bool}} + +nz_cp = {Equal.trans(Bool, U32.is_zero(cp), U32.is_zero(cm), False{}, Equal.cong(U32, Bool, uu => U32.is_zero(uu), + cp, cm, e_cm), cap.nz(zz, left, mx, nt, nzm, hn)) : {U32.is_zero(cp) == False{} : Bool}} + +ha2 = {Equal.sym(Nat, U32.to_nat(mx), 1n+U32.to_nat(m1), Wp1.u32_dec(mx, nzm)) : {Nat.add(1n, U32.to_nat(m1)) == + U32.to_nat(mx) : Nat}} + +hl2 = {Wp1.u32_dec(cp, nz_cp) : {U32.to_nat(cp) == Nat.add(1n, U32.to_nat(c1)) : Nat}} + +hle2 = {Equal.trans(Bool, Nat.is_le(U32.to_nat(c1), U32.to_nat(m1)), Nat.is_le(U32.to_nat((cm - 1 : U32)), + U32.to_nat(m1)), True{}, Equal.cong(U32, Bool, uu => Nat.is_le(U32.to_nat((uu - 1 : U32)), U32.to_nat(m1)), cp, cm, + e_cm), cap.le(zz, left, mx, nt, nzm, ezm, hn)) : {Nat.is_le(U32.to_nat(c1), U32.to_nat(m1)) == True{} : Bool}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + hf2 = {fo.cm(zz, left, mx, nt, emx, cap.fo(zz, left, mx, nt, nzm, ezm, hn)) : fin.ok(zz, c1, m1, nt)} + open.feed(hd, tl, left, mx, sim_t(U32.is_zero(c1), U32.is_eq(c1, 1), U32.is_zero(m1), zz, m1, c1, Png.enc.cap(l2), + Png.enc.bf(l2), cp, {==}, {==}, {==}, ha2, hl2, hle2, hn2, hf2, _ez => ({==}, {==}))) + +# a block boundary with bytes left: the full block closes and a new one opens +def sim.edge( + fin: Bool, + +hd: U32, + +tl: List<&2, U32>, + +acc: List<&2, U32>, + +rf: U32, + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +ll: U32, + +mx: U32, + +zf: Bool, + +hzf: {zf == U32.is_zero(rf) : Bool}, + +ha: {Nat.add(List.length(&2, U32, acc), U32.to_nat(rf)) == U32.to_nat(mx) : Nat}, + +hl: {U32.to_nat(ll) == Nat.add(List.length(&2, U32, acc), U32.to_nat(rp)) : Nat}, + +e0: {U32.to_nat(rp) == 0n : Nat}, + +e_ob: {open.bs(hd, tl, (left - 1 : U32), Png.enc.cap(left), Png.enc.bf(left)) == fd1(tl, mx, hd) : List<&2, U32>}, + hf: fin.ok(fin, rp, rf, 1n+List.length(&2, U32, tl)), + pe: {plen == Png.enc.cap(left) : U32} & {pbf == Png.enc.bf(left) : U32} +) -> {List.append(&2, U32, Png.enc.head(fin, ll, (ll .^. 65535 : U32)), List.reverse.go(&2, U32, acc, open.bs(hd, tl, + (left - 1 : U32), plen, pbf))) == Png.enc.feed(hd <> tl, zf, rf, mx, acc) : List<&2, U32>}: + match fin: + case True{}: + Empty.absurd({List.append(&2, U32, Png.enc.head(True{}, ll, (ll .^. 65535 : U32)), List.reverse.go(&2, U32, acc, + open.bs(hd, tl, (left - 1 : U32), plen, pbf))) == Png.enc.feed(hd <> tl, zf, rf, mx, acc) : List<&2, U32>}, + Wp1.zero_succ(List.length(&2, U32, tl), Equal.trans(Nat, 0n, U32.to_nat(rp), 1n+List.length(&2, U32, tl), + Equal.sym(Nat, U32.to_nat(rp), 0n, e0), hf))) + case False{}: + (e_rr, _h_lt) = hf + (e_plen, e_pbf) = pe + +na = List.length(&2, U32, acc) + +ra = List.reverse(&2, U32, acc) + +l2 = (left - 1 : U32) + +cp = Png.enc.cap(left) + +e_rf0 = {Equal.trans(Nat, U32.to_nat(rf), U32.to_nat(rp), 0n, Equal.sym(Nat, U32.to_nat(rp), U32.to_nat(rf), + e_rr), e0) : {U32.to_nat(rf) == 0n : Nat}} + +e_zf = {Equal.trans(Bool, zf, U32.is_zero(rf), True{}, hzf, zero.is(rf, e_rf0)) : {zf == True{} : Bool}} + +ff = Png.enc.feed(tl, U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [hd]) + +hd0 = Png.enc.head(False{}, ll, (ll .^. 65535 : U32)) + +hl0 = {Equal.trans(Nat, U32.to_nat(ll), Nat.add(na, 0n), na, Equal.trans(Nat, U32.to_nat(ll), Nat.add(na, + U32.to_nat(rp)), Nat.add(na, 0n), hl, Equal.cong(Nat, Nat, kk => Nat.add(na, kk), U32.to_nat(rp), 0n, e0)), + R.add_zero(na)) : {U32.to_nat(ll) == na : Nat}} + +e_len = {len.blk(acc, rf, ll, mx, ha, hl0) : {Png.enc.len(ra, 0) == ll : U32}} + +e_bs = {Equal.trans(List<&2, U32>, open.bs(hd, tl, l2, plen, pbf), open.bs(hd, tl, l2, cp, Png.enc.bf(left)), ff, + Equal.trans(List<&2, U32>, open.bs(hd, tl, l2, plen, pbf), open.bs(hd, tl, l2, cp, pbf), open.bs(hd, tl, l2, cp, + Png.enc.bf(left)), Equal.cong(U32, List<&2, U32>, uu => open.bs(hd, tl, l2, uu, pbf), plen, cp, e_plen), + Equal.cong(U32, List<&2, U32>, uu => open.bs(hd, tl, l2, cp, uu), pbf, Png.enc.bf(left), e_pbf)), + e_ob) : {open.bs(hd, tl, l2, plen, pbf) == ff : List<&2, U32>}} + Equal.trans(List<&2, U32>, List.append(&2, U32, hd0, List.reverse.go(&2, U32, acc, open.bs(hd, tl, l2, plen, + pbf))), + List.append(&2, U32, List.append(&2, U32, hd0, ra), ff), Png.enc.feed(hd <> tl, zf, rf, mx, acc), + Equal.trans(List<&2, U32>, List.append(&2, U32, hd0, List.reverse.go(&2, U32, acc, open.bs(hd, tl, l2, plen, + pbf))), List.append(&2, U32, hd0, List.append(&2, U32, ra, ff)), List.append(&2, U32, List.append(&2, U32, + hd0, ra), ff), + Equal.trans(List<&2, U32>, List.append(&2, U32, hd0, List.reverse.go(&2, U32, acc, open.bs(hd, tl, l2, plen, + pbf))), List.append(&2, U32, hd0, List.reverse.go(&2, U32, acc, ff)), List.append(&2, U32, hd0, + List.append(&2, U32, ra, ff)), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, hd0, List.reverse.go(&2, U32, acc, + ys)), open.bs(hd, tl, l2, plen, pbf), ff, e_bs), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, hd0, ys), List.reverse.go(&2, U32, acc, + ff), List.append(&2, U32, ra, ff), W9.revgo_acc(acc, [], ff))), + Equal.sym(List<&2, U32>, List.append(&2, U32, List.append(&2, U32, hd0, ra), ff), List.append(&2, U32, hd0, + List.append(&2, U32, ra, ff)), Wp2.app_assoc(hd0, ra, ff))), + Equal.trans(List<&2, U32>, List.append(&2, U32, List.append(&2, U32, hd0, ra), ff), Png.enc.feed(hd <> tl, + True{}, + rf, mx, acc), Png.enc.feed(hd <> tl, zf, rf, mx, acc), + Equal.trans(List<&2, U32>, List.append(&2, U32, List.append(&2, U32, hd0, ra), ff), List.append(&2, U32, + Png.enc.cat(hd0, ra), ff), Png.enc.feed(hd <> tl, True{}, rf, mx, acc), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, ys, ff), List.append(&2, U32, hd0, ra), + Png.enc.cat(hd0, ra), Equal.sym(List<&2, U32>, Png.enc.cat(hd0, ra), List.append(&2, U32, hd0, ra), + Wp2.cat_app(hd0, ra))), + Equal.trans(List<&2, U32>, List.append(&2, U32, Png.enc.cat(hd0, ra), ff), Png.enc.cat(Png.enc.cat(hd0, + ra), ff), Png.enc.feed(hd <> tl, True{}, rf, mx, acc), + Equal.sym(List<&2, U32>, Png.enc.cat(Png.enc.cat(hd0, ra), ff), List.append(&2, U32, Png.enc.cat(hd0, + ra), ff), Wp2.cat_app(Png.enc.cat(hd0, ra), ff)), + Equal.cong(U32, List<&2, U32>, uu => Png.enc.cat(Png.enc.cat(Png.enc.head(False{}, uu, (uu .^. 65535 : + U32)), ra), ff), ll, Png.enc.len(ra, 0), Equal.sym(U32, Png.enc.len(ra, 0), ll, e_len)))), + Equal.cong(Bool, List<&2, U32>, zb => Png.enc.feed(hd <> tl, zb, rf, mx, acc), True{}, zf, Equal.sym(Bool, + zf, True{}, e_zf)))) + +# bs against enc.feed: the open block (acc, newest first, with rf bytes of +# room in the feed) under a header of length ll and BFINAL fin, then bs from +# rp bytes left in the block, are enc.feed's blocks +def sim( + xs: List<&2, U32>, + znew: Bool, + zlast: Bool, + +zf: Bool, + +fin: Bool, + +acc: List<&2, U32>, + +rf: U32, + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +ll: U32, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hzn: {znew == U32.is_zero(rp) : Bool}, + +hzl: {zlast == U32.is_eq(rp, 1) : Bool}, + +hzf: {zf == U32.is_zero(rf) : Bool}, + +ha: {Nat.add(List.length(&2, U32, acc), U32.to_nat(rf)) == U32.to_nat(mx) : Nat}, + +hl: {U32.to_nat(ll) == Nat.add(List.length(&2, U32, acc), U32.to_nat(rp)) : Nat}, + +hle: {Nat.is_le(U32.to_nat(rp), U32.to_nat(rf)) == True{} : Bool}, + +hn: {U32.to_nat(left) == List.length(&2, U32, xs) : Nat}, + hf: fin.ok(fin, rp, rf, List.length(&2, U32, xs)), + hb: at.edge(rp, left, plen, pbf) +) -> sim.ty(xs, znew, zlast, zf, fin, acc, rf, rp, left, plen, pbf, ll, mx): + match xs znew zlast: + case Nil{} _zn _zl: + sim.nil(fin, acc, rf, rp, ll, mx, ha, hl, hf) + case +h <> +t False{} False{}: + +rp2 = (rp - 1 : U32) + +rf2 = (rf - 1 : U32) + +l2 = (left - 1 : U32) + +nt = List.length(&2, U32, t) + +na = List.length(&2, U32, acc) + +nz_rp = {Equal.sym(Bool, False{}, U32.is_zero(rp), hzn) : {U32.is_zero(rp) == False{} : Bool}} + +e_rp = {Wp1.u32_dec(rp, nz_rp) : {U32.to_nat(rp) == 1n+U32.to_nat(rp2) : Nat}} + +nz_rp2 = {two(rp, hzn, hzl) : {U32.is_zero(rp2) == False{} : Bool}} + +h_rf = {R.le_rw_l(U32.to_nat(rp), 1n+U32.to_nat(rp2), U32.to_nat(rf), e_rp, hle) : + {Nat.is_le(1n+U32.to_nat(rp2), U32.to_nat(rf)) == True{} : Bool}} + +nz_rf = {pos(rf, U32.to_nat(rp2), h_rf) : {U32.is_zero(rf) == False{} : Bool}} + +e_rf = {Wp1.u32_dec(rf, nz_rf) : {U32.to_nat(rf) == 1n+U32.to_nat(rf2) : Nat}} + +e_zf = {Equal.trans(Bool, zf, U32.is_zero(rf), False{}, hzf, nz_rf) : {zf == False{} : Bool}} + +ha2 = {shift(na, rf, rf2, U32.to_nat(mx), e_rf, ha) : {Nat.add(1n+na, U32.to_nat(rf2)) == U32.to_nat(mx) : Nat}} + +hl2 = {Equal.sym(Nat, Nat.add(1n+na, U32.to_nat(rp2)), U32.to_nat(ll), shift(na, rp, rp2, U32.to_nat(ll), e_rp, + Equal.sym(Nat, U32.to_nat(ll), Nat.add(na, U32.to_nat(rp)), hl))) : {U32.to_nat(ll) == Nat.add(1n+na, + U32.to_nat(rp2)) : Nat}} + +hle2 = {R.le_rw_r(1n+U32.to_nat(rp2), U32.to_nat(rf), 1n+U32.to_nat(rf2), e_rf, h_rf) : + {Nat.is_le(U32.to_nat(rp2), U32.to_nat(rf2)) == True{} : Bool}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + hf2 = {fo.dec(fin, rp, rf, rp2, rf2, nt, e_rp, e_rf, hf) : fin.ok(fin, rp2, rf2, nt)} + Equal.trans(List<&2, U32>, List.append(&2, U32, Png.enc.head(fin, ll, (ll .^. 65535 : U32)), List.reverse.go(&2, + U32, acc, bs(h <> t, False{}, False{}, rp, left, plen, pbf))), Png.enc.feed(h <> t, False{}, rf, mx, acc), + Png.enc.feed(h <> t, zf, rf, mx, acc), + sim(t, False{}, U32.is_eq(rp2, 1), U32.is_zero(rf2), fin, h <> acc, rf2, rp2, l2, plen, pbf, ll, mx, emx, + Equal.sym(Bool, U32.is_zero(rp2), False{}, nz_rp2), {==}, {==}, ha2, hl2, hle2, hn2, hf2, edge.no(rp2, l2, + plen, pbf, nz_rp2)), + Equal.cong(Bool, List<&2, U32>, zb => Png.enc.feed(h <> t, zb, rf, mx, acc), False{}, zf, Equal.sym(Bool, zf, + False{}, e_zf))) + case +h <> +t False{} True{}: + +rf2 = (rf - 1 : U32) + +l2 = (left - 1 : U32) + +nt = List.length(&2, U32, t) + +na = List.length(&2, U32, acc) + +e_rp = {one.val(rp, hzl) : {U32.to_nat(rp) == 1n+U32.to_nat(0) : Nat}} + +h_rf = {R.le_rw_l(U32.to_nat(rp), 1n, U32.to_nat(rf), e_rp, hle) : {Nat.is_le(1n, U32.to_nat(rf)) == True{} : + Bool}} + +nz_rf = {pos(rf, 0n, h_rf) : {U32.is_zero(rf) == False{} : Bool}} + +e_rf = {Wp1.u32_dec(rf, nz_rf) : {U32.to_nat(rf) == 1n+U32.to_nat(rf2) : Nat}} + +e_zf = {Equal.trans(Bool, zf, U32.is_zero(rf), False{}, hzf, nz_rf) : {zf == False{} : Bool}} + +ha2 = {shift(na, rf, rf2, U32.to_nat(mx), e_rf, ha) : {Nat.add(1n+na, U32.to_nat(rf2)) == U32.to_nat(mx) : Nat}} + +hl2 = {Equal.sym(Nat, Nat.add(1n+na, U32.to_nat(0)), U32.to_nat(ll), shift(na, rp, 0, U32.to_nat(ll), e_rp, + Equal.sym(Nat, U32.to_nat(ll), Nat.add(na, U32.to_nat(rp)), hl))) : {U32.to_nat(ll) == Nat.add(1n+na, + U32.to_nat(0)) : Nat}} + +hle2 = {R.le_zero(U32.to_nat(rf2)) : {Nat.is_le(U32.to_nat(0), U32.to_nat(rf2)) == True{} : Bool}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + hf2 = {fo.dec(fin, rp, rf, 0, rf2, nt, e_rp, e_rf, hf) : fin.ok(fin, 0, rf2, nt)} + Equal.trans(List<&2, U32>, List.append(&2, U32, Png.enc.head(fin, ll, (ll .^. 65535 : U32)), List.reverse.go(&2, + U32, acc, bs(h <> t, False{}, True{}, rp, left, plen, pbf))), Png.enc.feed(h <> t, False{}, rf, mx, acc), + Png.enc.feed(h <> t, zf, rf, mx, acc), + sim(t, True{}, False{}, U32.is_zero(rf2), fin, h <> acc, rf2, 0, l2, Png.enc.cap(l2), Png.enc.bf(l2), ll, mx, + emx, {==}, {==}, {==}, ha2, hl2, hle2, hn2, hf2, _ez => ({==}, {==})), + Equal.cong(Bool, List<&2, U32>, zb => Png.enc.feed(h <> t, zb, rf, mx, acc), False{}, zf, Equal.sym(Bool, zf, + False{}, e_zf))) + case +h <> +t True{} _zl: + +l2 = (left - 1 : U32) + +ez = {Equal.sym(Bool, True{}, U32.is_zero(rp), hzn) : {U32.is_zero(rp) == True{} : Bool}} + +e0 = {zero.val(rp, hzn) : {U32.to_nat(rp) == 0n : Nat}} + +e_ob = {open.ih(h, t, left, mx, emx, hn, zn => zl => zb => fb => rf3 => rp3 => pl3 => pb3 => ll3 => a1 => a2 => + a3 => a4 => a5 => a6 => a7 => a8 => a9 => sim(t, zn, zl, zb, fb, [h], rf3, rp3, l2, pl3, pb3, ll3, mx, emx, + a1, a2, a3, a4, a5, a6, a7, a8, a9)) : {open.bs(h, t, l2, Png.enc.cap(left), Png.enc.bf(left)) == + Png.enc.feed(t, U32.is_zero((mx - 1 : U32)), (mx - 1 : U32), mx, [h]) : List<&2, U32>}} + sim.edge(fin, h, t, acc, rf, rp, left, plen, pbf, ll, mx, zf, hzf, ha, hl, e0, e_ob, hf, hb(ez)) + +# the first block: bs from the start writes enc.feed's blocks for every +# non-empty list of left bytes +def sim.start( + +hd: U32, + +tl: List<&2, U32>, + +left: U32, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(left) == 1n+List.length(&2, U32, tl) : Nat} +) -> {bs(hd <> tl, True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left)) == Png.enc.feed(hd <> tl, False{}, + mx, mx, []) : List<&2, U32>}: + +l2 = (left - 1 : U32) + open.ih(hd, tl, left, mx, emx, hn, zn => zl => zb => fb => rf3 => rp3 => pl3 => pb3 => ll3 => a1 => a2 => a3 => a4 => + a5 => a6 => a7 => a8 => a9 => sim(tl, zn, zl, zb, fb, [hd], rf3, rp3, l2, pl3, pb3, ll3, mx, emx, a1, a2, a3, a4, + a5, + a6, a7, a8, a9)) + +# enc.blocks at 65535 for a list longer than one block is bs from the start +def sim.blocks( + xs: List<&2, U32>, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(xs, 0)) == List.length(&2, U32, xs) : Nat}, + +hbig: {U32.is_le(Png.enc.len(xs, 0), 65535) == False{} : Bool} +) -> {Png.enc.blocks(65535, xs) == bs(xs, True{}, False{}, 0, Png.enc.len(xs, 0), Png.enc.cap(Png.enc.len(xs, 0)), + Png.enc.bf(Png.enc.len(xs, 0))) : List<&2, U32>}: + match xs: + case Nil{}: + Empty.absurd({Png.enc.blocks(65535, []) == bs([], True{}, False{}, 0, 0, Png.enc.cap(0), Png.enc.bf(0)) : + List<&2, U32>}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hbig))) + case +h <> +t: + +nn = Png.enc.len(h <> t, 0) + +bb = bs(h <> t, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn)) + Equal.trans(List<&2, U32>, Png.enc.blocks(65535, h <> t), Png.enc.blocks.fit(False{}, 65535, nn, h <> t), bb, + Equal.cong(Bool, List<&2, U32>, zb => Png.enc.blocks.fit(zb, 65535, nn, h <> t), U32.is_le(nn, 65535), False{}, + hbig), + Equal.trans(List<&2, U32>, Png.enc.feed(h <> t, False{}, 65535, 65535, []), Png.enc.feed(h <> t, False{}, mx, + mx, + []), bb, Equal.cong(U32, List<&2, U32>, uu => Png.enc.feed(h <> t, False{}, uu, uu, []), 65535, mx, + Equal.sym(U32, mx, 65535, emx)), Equal.sym(List<&2, U32>, bb, Png.enc.feed(h <> t, False{}, mx, mx, []), + sim.start(h, t, nn, mx, emx, hn)))) + +# ---- D. U32.div and U32.mod ---- + +# a word shifted left with c coming in: nothing falls out, and it is c + 2 w +def sho.ok(nn: Nat, +cc: Bool, +ww: Word(nn), pp: Bool & Word(nn)) -> Type: + match pp: + case (tt, ss): + {tt == False{} : Bool} & {Word.to_nat(nn, ss) == Nat.add(R.bit(cc), Nat.double(Word.to_nat(nn, ww))) : Nat} + +# one step of so: the lower bits shifted, then this bit in front +def sho.step( + +pp: Nat, + +cc: Bool, + +bb: Bool, + +tt: Word(pp), + yy: Bool & Word(pp), + ih: sho.ok(pp, bb, tt, yy) +) -> sho.ok(1n+pp, cc, WCon{bb, tt}, Word.shl.out.con(pp, cc, yy)): + match yy: + case (hi, +t2): + (e_hi, e_t2) = ih + +vt = Word.to_nat(pp, tt) + (e_hi, Equal.trans(Nat, Word.to_nat(1n+pp, WCon{cc, t2}), Nat.add(R.bit(cc), Nat.double(Word.to_nat(pp, t2))), + Nat.add(R.bit(cc), Nat.double(Word.to_nat(1n+pp, WCon{bb, tt}))), R.to_nat_con(pp, cc, t2), + Equal.cong(Nat, Nat, kk => Nat.add(R.bit(cc), Nat.double(kk)), Word.to_nat(pp, t2), Word.to_nat(1n+pp, + WCon{bb, tt}), Equal.trans(Nat, Word.to_nat(pp, t2), Nat.add(R.bit(bb), Nat.double(vt)), Word.to_nat(1n+pp, + WCon{bb, tt}), e_t2, Equal.sym(Nat, Word.to_nat(1n+pp, WCon{bb, tt}), Nat.add(R.bit(bb), Nat.double(vt)), + R.to_nat_con(pp, bb, tt)))))) + +# shifting a word left with c coming in, when c + 2 w is at most a word's value +def sho( + nn: Nat, + +cc: Bool, + +ww: Word(nn), + +tw: Word(nn), + +hh: {Nat.is_le(Nat.add(R.bit(cc), Nat.double(Word.to_nat(nn, ww))), Word.to_nat(nn, tw)) == True{} : Bool} +) -> sho.ok(nn, cc, ww, Word.shl.out(nn, cc, ww)): + match nn: + case 0n: + match cc ww tw: + case False{} WNil{} WNil{}: + ({==}, {==}) + case True{} WNil{} WNil{}: + Empty.absurd(sho.ok(0n, True{}, WNil{}, (True{}, WNil{})), U32L.false_true(hh)) + case 1n+(+pp): + match ww tw: + case WCon{+bb, +tt} WCon{+sb, +st}: + +vt = Word.to_nat(pp, tt) + +vs = Word.to_nat(pp, st) + +wb = Word.to_nat(1n+pp, WCon{bb, tt}) + +ws = Word.to_nat(1n+pp, WCon{sb, st}) + +xb = Nat.add(R.bit(bb), Nat.double(vt)) + +xs = Nat.add(R.bit(sb), Nat.double(vs)) + +h0 = {R.le_add_l(R.bit(cc), Nat.double(wb), ws, hh) : {Nat.is_le(Nat.double(wb), ws) == True{} : Bool}} + +h1 = {R.le_rw_r(Nat.double(xb), ws, xs, R.to_nat_con(pp, sb, st), R.le_rw_l(Nat.double(wb), Nat.double(xb), + ws, Equal.cong(Nat, Nat, kk => Nat.double(kk), wb, xb, R.to_nat_con(pp, bb, tt)), h0)) : + {Nat.is_le(Nat.double(xb), xs) == True{} : Bool}} + +h2 = {R.le_half(sb, xb, vs, h1) : {Nat.is_le(xb, vs) == True{} : Bool}} + sho.step(pp, cc, bb, tt, Word.shl.out(pp, bb, tt), sho(pp, bb, tt, st, h2)) + +# at least in Cmp is at most the other way +def ge.le( + aa: Nat, + bb: Nat, + +hh: {Cmp.is_ge(Nat.cmp(aa, bb)) == True{} : Bool} +) -> {Nat.is_le(bb, aa) == True{} : Bool}: + match aa bb: + case 0n 0n: + {==} + case 0n 1n+bp: + Empty.absurd({Nat.is_le(1n+bp, 0n) == True{} : Bool}, U32L.false_true(hh)) + case 1n+_ap 0n: + {==} + case 1n+ap 1n+bp: + ge.le(ap, bp, hh) + +# not at least in Cmp is below +def nge.lt( + aa: Nat, + bb: Nat, + +hh: {Cmp.is_ge(Nat.cmp(aa, bb)) == False{} : Bool} +) -> {Nat.is_lt(aa, bb) == True{} : Bool}: + match aa bb: + case 0n 0n: + Empty.absurd({Nat.is_lt(0n, 0n) == True{} : Bool}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hh))) + case 0n 1n+_bp: + {==} + case 1n+ap 0n: + Empty.absurd({Nat.is_lt(1n+ap, 0n) == True{} : Bool}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hh))) + case 1n+ap 1n+bp: + nge.lt(ap, bp, hh) + +# U32.is_ge is Nat's +def u.ge(+uu: U32, +vv: U32) -> {U32.is_ge(uu, vv) == Cmp.is_ge(Nat.cmp(U32.to_nat(uu), U32.to_nat(vv))) : Bool}: + Equal.cong(Cmp, Bool, cc => Cmp.is_ge(cc), U32.cmp(uu, vv), Nat.cmp(U32.to_nat(uu), U32.to_nat(vv)), Wp2.u_cmp(uu, + vv)) + +# a bit plus x is below y when 2 plus x is at most y +def bit.lt( + aa: Bool, + +xx: Nat, + +yy: Nat, + +hh: {Nat.is_le(2n+xx, yy) == True{} : Bool} +) -> {Nat.is_lt(Nat.add(R.bit(aa), xx), yy) == True{} : Bool}: + match aa: + case False{}: + Equal.trans(Bool, Nat.is_lt(xx, yy), Nat.is_le(1n+xx, yy), True{}, R.lt_le_succ(xx, yy), R.le_trans(1n+xx, 2n+xx, + yy, R.le_succ(1n+xx), hh)) + case True{}: + Equal.trans(Bool, Nat.is_lt(1n+xx, yy), Nat.is_le(2n+xx, yy), True{}, R.lt_le_succ(1n+xx, yy), hh) + +# 2 r and a bit is below 2 b when r is below b +def dbl.lt( + +aa: Bool, + +rn: Nat, + +bn: Nat, + +hh: {Nat.is_lt(rn, bn) == True{} : Bool} +) -> {Nat.is_lt(Nat.add(R.bit(aa), Nat.double(rn)), Nat.add(bn, bn)) == True{} : Bool}: + +h1 = {Equal.trans(Bool, Nat.is_le(1n+rn, bn), Nat.is_lt(rn, bn), True{}, Equal.sym(Bool, Nat.is_lt(rn, bn), + Nat.is_le(1n+rn, bn), R.lt_le_succ(rn, bn)), hh) : {Nat.is_le(1n+rn, bn) == True{} : Bool}} + +h2 = {R.le_add_both(1n+rn, bn, 1n+rn, bn, h1, h1) : {Nat.is_le(Nat.add(1n+rn, 1n+rn), Nat.add(bn, bn)) == True{} : + Bool}} + +h3 = {Equal.trans(Nat, 2n+Nat.double(rn), 2n+Nat.add(rn, rn), Nat.add(1n+rn, 1n+rn), Equal.cong(Nat, Nat, kk => + 2n+kk, Nat.double(rn), Nat.add(rn, rn), R.double_add(rn)), Equal.cong(Nat, Nat, kk => 1n+kk, 1n+Nat.add(rn, rn), + Nat.add(rn, 1n+rn), Equal.sym(Nat, Nat.add(rn, 1n+rn), 1n+Nat.add(rn, rn), R.add_succ(rn, rn)))) : + {2n+Nat.double(rn) == Nat.add(1n+rn, 1n+rn) : Nat}} + +h4 = {R.le_rw_l(Nat.add(1n+rn, 1n+rn), 2n+Nat.double(rn), Nat.add(bn, bn), Equal.sym(Nat, 2n+Nat.double(rn), + Nat.add(1n+rn, 1n+rn), h3), h2) : {Nat.is_le(2n+Nat.double(rn), Nat.add(bn, bn)) == True{} : Bool}} + bit.lt(aa, Nat.double(rn), Nat.add(bn, bn), h4) + +# a + 2 (q b + r) is 2 q b + (a + 2 r) +def alg.lhs( + +an: Nat, + +qb: Nat, + +rn: Nat +) -> {Nat.add(an, Nat.double(Nat.add(qb, rn))) == Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))) : Nat}: + Equal.trans(Nat, Nat.add(an, Nat.double(Nat.add(qb, rn))), Nat.add(an, Nat.add(Nat.double(qb), Nat.double(rn))), + Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), Equal.cong(Nat, Nat, kk => Nat.add(an, kk), + Nat.double(Nat.add(qb, rn)), Nat.add(Nat.double(qb), Nat.double(rn)), R.double_dist(qb, rn)), R.add_swap(an, + Nat.double(qb), Nat.double(rn))) + +# what U32.divmod.go gives: the word is q * b + r, with r below b +def dm.ok(mm: Nat, +aw: Word(mm), +bu: U32, qr: Word(mm) & U32) -> Type: + match qr: + case (+qw, +ru): + {Word.to_nat(mm, aw) == Nat.add(Nat.mul(Word.to_nat(mm, qw), U32.to_nat(bu)), U32.to_nat(ru)) : Nat} & + {Nat.is_lt(U32.to_nat(ru), U32.to_nat(bu)) == True{} : Bool} + +# the step where the new bit makes the remainder reach b: q gets a 1 and b comes off +def dm.sub( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +qw: Word(pp), + +rn: Nat, + +su: U32, + +e1: {Word.to_nat(pp, hi) == Nat.add(Nat.mul(Word.to_nat(pp, qw), U32.to_nat(bu)), rn) : Nat}, + +es: {U32.to_nat(su) == Nat.add(R.bit(a0), Nat.double(rn)) : Nat}, + +hge: {Nat.is_le(U32.to_nat(bu), U32.to_nat(su)) == True{} : Bool}, + +hlt: {Nat.is_lt(U32.to_nat(su), Nat.add(U32.to_nat(bu), U32.to_nat(bu))) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, (WCon{True{}, qw}, U32.sub(su, bu))): + +bn = U32.to_nat(bu) + +sn = U32.to_nat(su) + +qn = Word.to_nat(pp, qw) + +qb = Nat.mul(qn, bn) + +an = R.bit(a0) + +dd = Nat.mul(Nat.double(qn), bn) + +e_sub = {R.u32_sub_nat(su, bu, hge) : {U32.to_nat(U32.sub(su, bu)) == Nat.sub(sn, bn) : Nat}} + +e_l = {Equal.trans(Nat, Word.to_nat(1n+pp, WCon{a0, hi}), Nat.add(an, Nat.double(Nat.add(qb, rn))), + Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), Equal.trans(Nat, Word.to_nat(1n+pp, WCon{a0, hi}), + Nat.add(an, Nat.double(Word.to_nat(pp, hi))), Nat.add(an, Nat.double(Nat.add(qb, rn))), R.to_nat_con(pp, a0, hi), + Equal.cong(Nat, Nat, kk => Nat.add(an, Nat.double(kk)), Word.to_nat(pp, hi), Nat.add(qb, rn), e1)), alg.lhs(an, qb, + rn)) : {Word.to_nat(1n+pp, WCon{a0, hi}) == Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))) : Nat}} + +e_r = {Equal.trans(Nat, Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)), Nat.add(dd, sn), Nat.add(Nat.double(qb), + Nat.add(an, Nat.double(rn))), + Equal.trans(Nat, Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)), Nat.add(dd, Nat.add(bn, Nat.sub(sn, bn))), Nat.add(dd, + sn), + Equal.trans(Nat, Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)), Nat.add(bn, Nat.add(dd, Nat.sub(sn, bn))), + Nat.add(dd, Nat.add(bn, Nat.sub(sn, bn))), R.add_assoc(bn, dd, Nat.sub(sn, bn)), R.add_swap(bn, dd, + Nat.sub(sn, bn))), + Equal.cong(Nat, Nat, kk => Nat.add(dd, kk), Nat.add(bn, Nat.sub(sn, bn)), sn, R.add_sub(bn, sn, hge))), + Equal.trans(Nat, Nat.add(dd, sn), Nat.add(Nat.double(qb), sn), Nat.add(Nat.double(qb), Nat.add(an, + Nat.double(rn))), Equal.cong(Nat, Nat, kk => Nat.add(kk, sn), dd, Nat.double(qb), R.mul_double(qn, bn)), + Equal.cong(Nat, Nat, kk => Nat.add(Nat.double(qb), kk), sn, Nat.add(an, Nat.double(rn)), es))) : + {Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)) == Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))) : Nat}} + +e_all = {Equal.trans(Nat, Word.to_nat(1n+pp, WCon{a0, hi}), Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), + Nat.add(Nat.add(bn, dd), U32.to_nat(U32.sub(su, bu))), e_l, Equal.trans(Nat, Nat.add(Nat.double(qb), Nat.add(an, + Nat.double(rn))), Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)), Nat.add(Nat.add(bn, dd), U32.to_nat(U32.sub(su, bu))), + Equal.sym(Nat, Nat.add(Nat.add(bn, dd), Nat.sub(sn, bn)), Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), + e_r), Equal.cong(Nat, Nat, kk => Nat.add(Nat.add(bn, dd), kk), Nat.sub(sn, bn), U32.to_nat(U32.sub(su, bu)), + Equal.sym(Nat, U32.to_nat(U32.sub(su, bu)), Nat.sub(sn, bn), e_sub)))) : {Word.to_nat(1n+pp, WCon{a0, hi}) == + Nat.add(Nat.add(bn, dd), U32.to_nat(U32.sub(su, bu))) : Nat}} + +l_sub = {R.lt_rw_l(Nat.sub(sn, bn), U32.to_nat(U32.sub(su, bu)), bn, Equal.sym(Nat, U32.to_nat(U32.sub(su, bu)), + Nat.sub(sn, bn), e_sub), R.sub_lt(bn, sn, bn, hge, hlt)) : {Nat.is_lt(U32.to_nat(U32.sub(su, bu)), bn) == True{} : + Bool}} + (e_all, l_sub) + +# the step where the remainder stays below b: q gets a 0 +def dm.keep( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +qw: Word(pp), + +rn: Nat, + +su: U32, + +e1: {Word.to_nat(pp, hi) == Nat.add(Nat.mul(Word.to_nat(pp, qw), U32.to_nat(bu)), rn) : Nat}, + +es: {U32.to_nat(su) == Nat.add(R.bit(a0), Nat.double(rn)) : Nat}, + +hlt: {Nat.is_lt(U32.to_nat(su), U32.to_nat(bu)) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, (WCon{False{}, qw}, su)): + +bn = U32.to_nat(bu) + +qn = Word.to_nat(pp, qw) + +qb = Nat.mul(qn, bn) + +an = R.bit(a0) + +e_all = {Equal.trans(Nat, Word.to_nat(1n+pp, WCon{a0, hi}), Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), + Nat.add(Nat.mul(Nat.double(qn), bn), U32.to_nat(su)), Equal.trans(Nat, Word.to_nat(1n+pp, WCon{a0, hi}), + Nat.add(an, Nat.double(Nat.add(qb, rn))), Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), Equal.trans(Nat, + Word.to_nat(1n+pp, WCon{a0, hi}), Nat.add(an, Nat.double(Word.to_nat(pp, hi))), Nat.add(an, Nat.double(Nat.add(qb, + rn))), R.to_nat_con(pp, a0, hi), Equal.cong(Nat, Nat, kk => Nat.add(an, Nat.double(kk)), Word.to_nat(pp, hi), + Nat.add(qb, rn), e1)), alg.lhs(an, qb, rn)), Equal.trans(Nat, Nat.add(Nat.double(qb), Nat.add(an, Nat.double(rn))), + Nat.add(Nat.mul(Nat.double(qn), bn), Nat.add(an, Nat.double(rn))), Nat.add(Nat.mul(Nat.double(qn), bn), + U32.to_nat(su)), Equal.cong(Nat, Nat, kk => Nat.add(kk, Nat.add(an, Nat.double(rn))), Nat.double(qb), + Nat.mul(Nat.double(qn), bn), Equal.sym(Nat, Nat.mul(Nat.double(qn), bn), Nat.double(qb), R.mul_double(qn, bn))), + Equal.cong(Nat, Nat, kk => Nat.add(Nat.mul(Nat.double(qn), bn), kk), Nat.add(an, Nat.double(rn)), U32.to_nat(su), + Equal.sym(Nat, U32.to_nat(su), Nat.add(an, Nat.double(rn)), es)))) : {Word.to_nat(1n+pp, WCon{a0, hi}) == + Nat.add(Nat.mul(Nat.double(qn), bn), U32.to_nat(su)) : Nat}} + (e_all, hlt) + +# divmod.go.fin, as it picks on whether the shifted remainder reached b +def dm.fin( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +qw: Word(pp), + +rn: Nat, + +su: U32, + zg: Bool, + +eg: {U32.is_ge(su, bu) == zg : Bool}, + +e1: {Word.to_nat(pp, hi) == Nat.add(Nat.mul(Word.to_nat(pp, qw), U32.to_nat(bu)), rn) : Nat}, + +es: {U32.to_nat(su) == Nat.add(R.bit(a0), Nat.double(rn)) : Nat}, + +hlt: {Nat.is_lt(U32.to_nat(su), Nat.add(U32.to_nat(bu), U32.to_nat(bu))) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, U32.divmod.go.fin(pp, qw, su, bu, zg)): + match zg: + case True{}: + +hg = {Equal.trans(Bool, Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))), U32.is_ge(su, bu), True{}, + Equal.sym(Bool, U32.is_ge(su, bu), Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))), u.ge(su, bu)), eg) : + {Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))) == True{} : Bool}} + dm.sub(pp, a0, hi, bu, qw, rn, su, e1, es, ge.le(U32.to_nat(su), U32.to_nat(bu), hg), hlt) + case False{}: + +hg = {Equal.trans(Bool, Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))), U32.is_ge(su, bu), False{}, + Equal.sym(Bool, U32.is_ge(su, bu), Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))), u.ge(su, bu)), eg) : + {Cmp.is_ge(Nat.cmp(U32.to_nat(su), U32.to_nat(bu))) == False{} : Bool}} + dm.keep(pp, a0, hi, bu, qw, rn, su, e1, es, nge.lt(U32.to_nat(su), U32.to_nat(bu), hg)) + +# divmod.go.shl: the remainder shifted with the new bit, nothing falling out +def dm.shl( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +qw: Word(pp), + +rw: Word(32n), + yy: Bool & Word(32n), + hy: sho.ok(32n, a0, rw, yy), + +e1: {Word.to_nat(pp, hi) == Nat.add(Nat.mul(Word.to_nat(pp, qw), U32.to_nat(bu)), Word.to_nat(32n, rw)) : Nat}, + +lt1: {Nat.is_lt(Word.to_nat(32n, rw), U32.to_nat(bu)) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, U32.divmod.go.shl(pp, qw, bu, yy)): + match yy: + case (tt, +ss): + (e_t, +e_s) = hy + +rn = Word.to_nat(32n, rw) + +hlt = {R.lt_rw_l(Nat.add(R.bit(a0), Nat.double(rn)), U32.to_nat(U32{ss}), Nat.add(U32.to_nat(bu), + U32.to_nat(bu)), Equal.sym(Nat, Word.to_nat(32n, ss), Nat.add(R.bit(a0), Nat.double(rn)), e_s), dbl.lt(a0, rn, + U32.to_nat(bu), lt1)) : {Nat.is_lt(U32.to_nat(U32{ss}), Nat.add(U32.to_nat(bu), U32.to_nat(bu))) == True{} : + Bool}} + %Equal.sym(Bool, tt, False{}, e_t) : dm.ok(1n+pp, WCon{a0, hi}, bu, U32.divmod.go.fin(pp, qw, U32{ss}, bu, + Bool.or(_, U32.is_ge(U32{ss}, bu)))) + dm.fin(pp, a0, hi, bu, qw, rn, U32{ss}, U32.is_ge(U32{ss}, bu), {==}, e1, e_s, hlt) + +# divmod.go.rec with the remainder and the bound taken apart into words +def dm.rec2( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +qw: Word(pp), + ru: U32, + tu: U32, + +e1: {Word.to_nat(pp, hi) == Nat.add(Nat.mul(Word.to_nat(pp, qw), U32.to_nat(bu)), U32.to_nat(ru)) : Nat}, + +lt1: {Nat.is_lt(U32.to_nat(ru), U32.to_nat(bu)) == True{} : Bool}, + +hb2: {Nat.is_le(Nat.add(U32.to_nat(bu), U32.to_nat(bu)), U32.to_nat(tu)) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, U32.divmod.go.rec(pp, a0, bu, (qw, ru))): + match ru tu: + case U32{+rw} U32{+tw}: + +rn = Word.to_nat(32n, rw) + +bb = Nat.add(U32.to_nat(bu), U32.to_nat(bu)) + +hbd = {R.le_trans(Nat.add(R.bit(a0), Nat.double(rn)), bb, Word.to_nat(32n, tw), R.lt_le(Nat.add(R.bit(a0), + Nat.double(rn)), bb, dbl.lt(a0, rn, U32.to_nat(bu), lt1)), hb2) : {Nat.is_le(Nat.add(R.bit(a0), + Nat.double(rn)), Word.to_nat(32n, tw)) == True{} : Bool}} + dm.shl(pp, a0, hi, bu, qw, rw, Word.shl.out(32n, a0, rw), sho(32n, a0, rw, tw, hbd), e1, lt1) + +# divmod.go.rec: the bits above, divided, then this bit shifted in +def dm.rec( + +pp: Nat, + +a0: Bool, + +hi: Word(pp), + +bu: U32, + +tu: U32, + xx: Word(pp) & U32, + ih: dm.ok(pp, hi, bu, xx), + +hb2: {Nat.is_le(Nat.add(U32.to_nat(bu), U32.to_nat(bu)), U32.to_nat(tu)) == True{} : Bool} +) -> dm.ok(1n+pp, WCon{a0, hi}, bu, U32.divmod.go.rec(pp, a0, bu, xx)): + match xx: + case (+qw, +ru): + (e1, lt1) = ih + dm.rec2(pp, a0, hi, bu, qw, ru, tu, e1, lt1, hb2) + +# U32.divmod.go by a b above 0 with 2 b some U32's value: the word is q b + r, r below b +def dm( + mm: Nat, + aw: Word(mm), + +bu: U32, + +tu: U32, + +hb0: {Nat.is_lt(0n, U32.to_nat(bu)) == True{} : Bool}, + +hb2: {Nat.is_le(Nat.add(U32.to_nat(bu), U32.to_nat(bu)), U32.to_nat(tu)) == True{} : Bool} +) -> dm.ok(mm, aw, bu, U32.divmod.go(mm, aw, bu)): + match mm aw: + case 0n WNil{}: + ({==}, hb0) + case 1n+(+pp) WCon{+a0, +hi}: + dm.rec(pp, a0, hi, bu, tu, U32.divmod.go(pp, hi, bu), dm(pp, hi, bu, tu, hb0, hb2), hb2) + +# what U32.div and U32.mod give +def dvm.ty(+nu: U32, +bu: U32) -> Type: + {U32.to_nat(nu) == Nat.add(Nat.mul(U32.to_nat(U32.div(nu, bu)), U32.to_nat(bu)), U32.to_nat(U32.mod(nu, bu))) : Nat} + & {Nat.is_lt(U32.to_nat(U32.mod(nu, bu)), U32.to_nat(bu)) == True{} : Bool} + +# the quotient and remainder divmod.go leaves, read by div.fin and mod.fin +def dvm.pair( + +aw: Word(32n), + +bu: U32, + xx: Word(32n) & U32, + hh: dm.ok(32n, aw, bu, xx) +) -> {Word.to_nat(32n, aw) == Nat.add(Nat.mul(U32.to_nat(U32.div.fin(xx)), U32.to_nat(bu)), + U32.to_nat(U32.mod.fin(xx))) : Nat} & {Nat.is_lt(U32.to_nat(U32.mod.fin(xx)), U32.to_nat(bu)) == True{} : Bool}: + match xx: + case (+qw, +ru): + hh + +# U32.div and U32.mod by a nonzero b, once is_zero's answer is named +def dvm.at( + zz: Bool, + +aw: Word(32n), + +bu: U32, + +tu: U32, + +ez: {U32.is_zero(bu) == zz : Bool}, + +hb0: {Nat.is_lt(0n, U32.to_nat(bu)) == True{} : Bool}, + +hb2: {Nat.is_le(Nat.add(U32.to_nat(bu), U32.to_nat(bu)), U32.to_nat(tu)) == True{} : Bool} +) -> {Word.to_nat(32n, aw) == Nat.add(Nat.mul(U32.to_nat(U32.div.if(aw, bu, zz)), U32.to_nat(bu)), + U32.to_nat(U32.mod.if(aw, bu, zz))) : Nat} & {Nat.is_lt(U32.to_nat(U32.mod.if(aw, bu, zz)), U32.to_nat(bu)) == + True{} : Bool}: + match zz: + case False{}: + dvm.pair(aw, bu, U32.divmod.go(32n, aw, bu), dm(32n, aw, bu, tu, hb0, hb2)) + case True{}: + +e0 = {zero.val(bu, Equal.sym(Bool, U32.is_zero(bu), True{}, ez)) : {U32.to_nat(bu) == 0n : Nat}} + Empty.absurd({Word.to_nat(32n, aw) == Nat.add(Nat.mul(U32.to_nat(U32.div.if(aw, bu, True{})), U32.to_nat(bu)), + U32.to_nat(U32.mod.if(aw, bu, True{}))) : Nat} & {Nat.is_lt(U32.to_nat(U32.mod.if(aw, bu, True{})), + U32.to_nat(bu)) == True{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, Nat.is_lt(0n, U32.to_nat(bu)), + True{}, Equal.cong(Nat, Bool, kk => Nat.is_lt(0n, kk), 0n, U32.to_nat(bu), Equal.sym(Nat, U32.to_nat(bu), 0n, + e0)), hb0))) + +# U32.div and U32.mod: n is q b + r with r below b, for b above 0 whose double +# is some U32's value +def dvm( + nu: U32, + +bu: U32, + +tu: U32, + +hb0: {Nat.is_lt(0n, U32.to_nat(bu)) == True{} : Bool}, + +hb2: {Nat.is_le(Nat.add(U32.to_nat(bu), U32.to_nat(bu)), U32.to_nat(tu)) == True{} : Bool} +) -> dvm.ty(nu, bu): + match nu: + case U32{+aw}: + dvm.at(U32.is_zero(bu), aw, bu, tu, {==}, hb0, hb2) + +# ---- E. adding without a carry out, and the block count ---- + +# the adder's carry out of the top bit +def cy(nn: Nat, aw: Word(nn), bw: Word(nn), cc: Bool) -> Bool: + match nn: + case 0n: + cc + case 1n+pp: + match aw bw cc: + case WCon{False{}, at} WCon{False{}, bt} False{}: + cy(pp, at, bt, False{}) + case WCon{False{}, at} WCon{False{}, bt} True{}: + cy(pp, at, bt, False{}) + case WCon{False{}, at} WCon{True{}, bt} False{}: + cy(pp, at, bt, False{}) + case WCon{False{}, at} WCon{True{}, bt} True{}: + cy(pp, at, bt, True{}) + case WCon{True{}, at} WCon{False{}, bt} False{}: + cy(pp, at, bt, False{}) + case WCon{True{}, at} WCon{False{}, bt} True{}: + cy(pp, at, bt, True{}) + case WCon{True{}, at} WCon{True{}, bt} False{}: + cy(pp, at, bt, True{}) + case WCon{True{}, at} WCon{True{}, bt} True{}: + cy(pp, at, bt, True{}) + +# two words' values added +def wsum(+pp: Nat, +at: Word(pp), +bt: Word(pp)) -> Nat: + Nat.add(Word.to_nat(pp, at), Word.to_nat(pp, bt)) + +# one bit of cfa: this bit's sum and carry, then the bits above +def cfa.arm( + +pp: Nat, + +at: Word(pp), + +bt: Word(pp), + +ab: Bool, + +bb: Bool, + +cc: Bool, + +ss: Bool, + +kk: Bool, + +e_bits: {Nat.add(R.bit(cc), Nat.add(R.bit(ab), R.bit(bb))) == Nat.add(R.bit(ss), Nat.double(R.bit(kk))) : Nat}, + +er: {Word.to_nat(pp, Word.adc(pp, at, bt, False{}, kk)) == Nat.add(R.bit(kk), wsum(pp, at, bt)) : Nat} +) -> {Word.to_nat(1n+pp, WCon{ss, Word.adc(pp, at, bt, False{}, kk)}) == Nat.add(R.bit(cc), Nat.add(Word.to_nat(1n+pp, + WCon{ab, at}), Word.to_nat(1n+pp, WCon{bb, bt}))) : Nat}: + +na = Word.to_nat(pp, at) + +nb = Word.to_nat(pp, bt) + +mm = Nat.add(R.bit(kk), Nat.add(na, nb)) + +wa = Word.to_nat(1n+pp, WCon{ab, at}) + +wb = Word.to_nat(1n+pp, WCon{bb, bt}) + +xa = Nat.add(R.bit(ab), Nat.double(na)) + +xb = Nat.add(R.bit(bb), Nat.double(nb)) + +e_in = {Equal.trans(Nat, Nat.add(R.bit(cc), Nat.add(wa, wb)), Nat.add(R.bit(cc), Nat.add(xa, wb)), + Nat.add(R.bit(cc), Nat.add(xa, xb)), + Equal.cong(Nat, Nat, xx => Nat.add(R.bit(cc), Nat.add(xx, wb)), wa, xa, R.to_nat_con(pp, ab, at)), + Equal.cong(Nat, Nat, xx => Nat.add(R.bit(cc), Nat.add(xa, xx)), wb, xb, R.to_nat_con(pp, bb, bt))) : + {Nat.add(R.bit(cc), Nat.add(wa, wb)) == Nat.add(R.bit(cc), Nat.add(xa, xb)) : Nat}} + +e_all = {Equal.trans(Nat, Nat.add(R.bit(cc), Nat.add(wa, wb)), Nat.add(R.bit(cc), Nat.add(xa, xb)), + Nat.add(R.bit(ss), Nat.double(mm)), e_in, R.sum_regroup(R.bit(cc), R.bit(ab), R.bit(bb), R.bit(ss), R.bit(kk), na, + nb, e_bits)) : {Nat.add(R.bit(cc), Nat.add(wa, wb)) == Nat.add(R.bit(ss), Nat.double(mm)) : Nat}} + Equal.trans(Nat, Word.to_nat(1n+pp, WCon{ss, Word.adc(pp, at, bt, False{}, kk)}), + Nat.add(R.bit(ss), Nat.double(Word.to_nat(pp, Word.adc(pp, at, bt, False{}, kk)))), + Nat.add(R.bit(cc), Nat.add(wa, wb)), + R.to_nat_con(pp, ss, Word.adc(pp, at, bt, False{}, kk)), + Equal.trans(Nat, Nat.add(R.bit(ss), Nat.double(Word.to_nat(pp, Word.adc(pp, at, bt, False{}, kk)))), + Nat.add(R.bit(ss), Nat.double(mm)), Nat.add(R.bit(cc), Nat.add(wa, wb)), + Equal.cong(Nat, Nat, xx => Nat.add(R.bit(ss), Nat.double(xx)), Word.to_nat(pp, Word.adc(pp, at, bt, False{}, kk)), + mm, er), + Equal.sym(Nat, Nat.add(R.bit(cc), Nat.add(wa, wb)), Nat.add(R.bit(ss), Nat.double(mm)), e_all))) + +# the adder with no carry out is the sum +def cfa( + nn: Nat, + aw: Word(nn), + bw: Word(nn), + cc: Bool, + +hh: {cy(nn, aw, bw, cc) == False{} : Bool} +) -> {Word.to_nat(nn, Word.adc(nn, aw, bw, False{}, cc)) == Nat.add(R.bit(cc), Nat.add(Word.to_nat(nn, aw), + Word.to_nat(nn, bw))) : Nat}: + match nn: + case 0n: + match aw bw cc: + case WNil{} WNil{} False{}: + {==} + case WNil{} WNil{} True{}: + Empty.absurd({0n == 1n : Nat}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hh))) + case 1n+(+pp): + match aw bw cc: + case WCon{False{}, +at} WCon{False{}, +bt} False{}: + cfa.arm(pp, at, bt, False{}, False{}, False{}, False{}, False{}, {==}, cfa(pp, at, bt, False{}, hh)) + case WCon{False{}, +at} WCon{False{}, +bt} True{}: + cfa.arm(pp, at, bt, False{}, False{}, True{}, True{}, False{}, {==}, cfa(pp, at, bt, False{}, hh)) + case WCon{False{}, +at} WCon{True{}, +bt} False{}: + cfa.arm(pp, at, bt, False{}, True{}, False{}, True{}, False{}, {==}, cfa(pp, at, bt, False{}, hh)) + case WCon{False{}, +at} WCon{True{}, +bt} True{}: + cfa.arm(pp, at, bt, False{}, True{}, True{}, False{}, True{}, {==}, cfa(pp, at, bt, True{}, hh)) + case WCon{True{}, +at} WCon{False{}, +bt} False{}: + cfa.arm(pp, at, bt, True{}, False{}, False{}, True{}, False{}, {==}, cfa(pp, at, bt, False{}, hh)) + case WCon{True{}, +at} WCon{False{}, +bt} True{}: + cfa.arm(pp, at, bt, True{}, False{}, True{}, False{}, True{}, {==}, cfa(pp, at, bt, True{}, hh)) + case WCon{True{}, +at} WCon{True{}, +bt} False{}: + cfa.arm(pp, at, bt, True{}, True{}, False{}, False{}, True{}, {==}, cfa(pp, at, bt, True{}, hh)) + case WCon{True{}, +at} WCon{True{}, +bt} True{}: + cfa.arm(pp, at, bt, True{}, True{}, True{}, True{}, True{}, {==}, cfa(pp, at, bt, True{}, hh)) + +# the carry out of a U32 add +def cy32(uu: U32, vv: U32) -> Bool: + match uu vv: + case U32{xw} U32{yw}: + cy(32n, xw, yw, False{}) + +# a U32 add with no carry out is the sum +def add.cf( + uu: U32, + vv: U32, + +hh: {cy32(uu, vv) == False{} : Bool} +) -> {U32.to_nat(U32.add(uu, vv)) == Nat.add(U32.to_nat(uu), U32.to_nat(vv)) : Nat}: + match uu vv: + case U32{+xw} U32{+yw}: + cfa(32n, xw, yw, False{}, hh) + +# the block size doubled is a U32's value +def mx.dbl( + +mx: U32, + +emx: {mx == 65535 : U32} +) -> {U32.to_nat(U32.add(mx, mx)) == Nat.add(U32.to_nat(mx), U32.to_nat(mx)) : Nat}: + add.cf(mx, mx, Equal.cong(U32, Bool, uu => cy32(uu, uu), mx, 65535, emx)) + +# the block size is at least 2 +def mx.two( + +mx: U32, + +emx: {mx == 65535 : U32} +) -> {U32.to_nat(mx) == 2n+U32.to_nat(((mx - 1 : U32) - 1 : U32)) : Nat}: + +m1 = (mx - 1 : U32) + +nz1 = {Equal.cong(U32, Bool, uu => U32.is_zero((uu - 1 : U32)), mx, 65535, emx) : {U32.is_zero(m1) == False{} : + Bool}} + Equal.trans(Nat, U32.to_nat(mx), 1n+U32.to_nat(m1), 2n+U32.to_nat((m1 - 1 : U32)), Wp1.u32_dec(mx, mx.nz(mx, emx)), + Equal.cong(Nat, Nat, kk => 1n+kk, U32.to_nat(m1), 1n+U32.to_nat((m1 - 1 : U32)), Wp1.u32_dec(m1, nz1))) + +# k blocks of m bytes hold x bytes with the last block not empty: k is ceil(x / m) +def kok(+xn: Nat, kk: Nat, +mn: Nat) -> Type: + match kk: + case 0n: + {xn == 0n : Nat} + case 1n+(+kp): + {Nat.is_lt(Nat.mul(kp, mn), xn) == True{} : Bool} & {Nat.is_le(xn, Nat.mul(1n+kp, mn)) == True{} : Bool} + +# x below x + y when y is above 0 +def lt.more( + +xx: Nat, + +yy: Nat, + +hh: {Nat.is_lt(0n, yy) == True{} : Bool} +) -> {Nat.is_lt(xx, Nat.add(xx, yy)) == True{} : Bool}: + R.lt_rw_l(Nat.add(xx, 0n), xx, Nat.add(xx, yy), R.add_zero(xx), R.lt_add_mono(xx, 0n, yy, hh)) + +# q blocks exactly: ceil(q m / m) is q +def nb.exact( + qn: Nat, + +mn: Nat, + +xn: Nat, + +ee: {xn == Nat.mul(qn, mn) : Nat}, + +hm: {Nat.is_lt(0n, mn) == True{} : Bool} +) -> kok(xn, qn, mn): + match qn: + case 0n: + ee + case 1n+(+kp): + +km = Nat.mul(kp, mn) + +e2 = {Equal.trans(Nat, xn, Nat.add(mn, km), Nat.add(km, mn), ee, R.add_comm(mn, km)) : {xn == Nat.add(km, mn) : + Nat}} + (R.lt_rw_r(km, Nat.add(km, mn), xn, Equal.sym(Nat, xn, Nat.add(km, mn), e2), lt.more(km, mn, hm)), + R.le_rw_l(Nat.mul(1n+kp, mn), xn, Nat.mul(1n+kp, mn), Equal.sym(Nat, xn, Nat.mul(1n+kp, mn), ee), + R.le_refl(Nat.mul(1n+kp, mn)))) + +# q blocks and a remainder r from 1 to m - 1: ceil is q + 1 +def nb.rem( + +qn: Nat, + +mn: Nat, + +xn: Nat, + +rn: Nat, + +ee: {xn == Nat.add(Nat.mul(qn, mn), rn) : Nat}, + +h1: {Nat.is_lt(0n, rn) == True{} : Bool}, + +hr: {Nat.is_lt(rn, mn) == True{} : Bool} +) -> kok(xn, 1n+qn, mn): + +qm = Nat.mul(qn, mn) + (R.lt_rw_r(qm, Nat.add(qm, rn), xn, Equal.sym(Nat, xn, Nat.add(qm, rn), ee), lt.more(qm, rn, h1)), + R.le_rw_l(Nat.add(qm, rn), xn, Nat.mul(1n+qn, mn), Equal.sym(Nat, xn, Nat.add(qm, rn), ee), R.le_rw_r(Nat.add(qm, + rn), Nat.add(qm, mn), Nat.mul(1n+qn, mn), R.add_comm(qm, mn), R.le_add_mono(qm, rn, mn, R.lt_le(rn, mn, hr))))) + +# x at most x times m for m above 0 +def le.mul( + +xx: Nat, + +mn: Nat, + +hm: {Nat.is_lt(0n, mn) == True{} : Bool} +) -> {Nat.is_le(xx, Nat.mul(xx, mn)) == True{} : Bool}: + +h1 = {Equal.trans(Bool, Nat.is_le(1n, mn), Nat.is_lt(0n, mn), True{}, Equal.sym(Bool, Nat.is_lt(0n, mn), + Nat.is_le(1n, mn), R.lt_le_succ(0n, mn)), hm) : {Nat.is_le(1n, mn) == True{} : Bool}} + R.le_rw_l(Nat.mul(1n, xx), xx, Nat.mul(xx, mn), R.add_zero(xx), R.le_rw_r(Nat.mul(1n, xx), Nat.mul(mn, xx), + Nat.mul(xx, mn), R.mul_comm(mn, xx), R.le_mul_mono(1n, mn, xx, h1))) + +# enc.nblk with the block size named +def nblk.m(+nn: U32, +mx: U32) -> U32: + Png.enc.nblk.at(U32.is_zero(U32.mod(nn, mx)), U32.div(nn, mx)) + +# enc.nblk is nblk.m at 65535 +def nblk.eq(+nn: U32, +mx: U32, +emx: {mx == 65535 : U32}) -> {Png.enc.nblk(nn) == nblk.m(nn, mx) : U32}: + Equal.cong(U32, U32, uu => Png.enc.nblk.at(U32.is_zero(U32.mod(nn, uu)), U32.div(nn, uu)), 65535, mx, Equal.sym(U32, + mx, 65535, emx)) + +# n's quotient by m times m, plus the remainder +def dq(+nn: U32, +mx: U32) -> Nat: + Nat.add(Nat.mul(U32.to_nat(U32.div(nn, mx)), U32.to_nat(mx)), U32.to_nat(U32.mod(nn, mx))) + +# nblk.m, once whether the remainder is zero is named +def nb.at( + zz: Bool, + +nn: U32, + +mx: U32, + +ez: {U32.is_zero(U32.mod(nn, mx)) == zz : Bool}, + +e_n: {U32.to_nat(nn) == dq(nn, mx) : Nat}, + +l_r: {Nat.is_lt(U32.to_nat(U32.mod(nn, mx)), U32.to_nat(mx)) == True{} : Bool}, + +hm: {Nat.is_lt(0n, U32.to_nat(mx)) == True{} : Bool} +) -> kok(U32.to_nat(nn), U32.to_nat(Png.enc.nblk.at(zz, U32.div(nn, mx))), U32.to_nat(mx)): + match zz: + case True{}: + +qu = U32.div(nn, mx) + +ru = U32.mod(nn, mx) + +qn = U32.to_nat(qu) + +mn = U32.to_nat(mx) + +xn = U32.to_nat(nn) + +e0 = {zero.val(ru, Equal.sym(Bool, U32.is_zero(ru), True{}, ez)) : {U32.to_nat(ru) == 0n : Nat}} + nb.exact(qn, mn, xn, Equal.trans(Nat, xn, Nat.add(Nat.mul(qn, mn), 0n), Nat.mul(qn, mn), Equal.trans(Nat, xn, + Nat.add(Nat.mul(qn, mn), U32.to_nat(ru)), Nat.add(Nat.mul(qn, mn), 0n), e_n, Equal.cong(Nat, Nat, kk => + Nat.add(Nat.mul(qn, mn), kk), U32.to_nat(ru), 0n, e0)), R.add_zero(Nat.mul(qn, mn))), hm) + case False{}: + +qu = U32.div(nn, mx) + +ru = U32.mod(nn, mx) + +qn = U32.to_nat(qu) + +mn = U32.to_nat(mx) + +xn = U32.to_nat(nn) + +h1 = {Equal.trans(Bool, Nat.is_lt(0n, U32.to_nat(ru)), Bool.not(Nat.is_eq(U32.to_nat(ru), 0n)), True{}, + R.pos_eq(U32.to_nat(ru)), Equal.cong(Bool, Bool, bb => Bool.not(bb), Nat.is_eq(U32.to_nat(ru), 0n), False{}, + Equal.trans(Bool, Nat.is_eq(U32.to_nat(ru), 0n), U32.is_zero(ru), False{}, Equal.sym(Bool, U32.is_zero(ru), + Nat.is_eq(U32.to_nat(ru), U32.to_nat(0)), R.u32_eq(ru, 0)), ez))) : {Nat.is_lt(0n, U32.to_nat(ru)) == True{} : + Bool}} + +h_q1 = {R.le_rw_r(Nat.add(qn, 1n), Nat.add(Nat.mul(qn, mn), U32.to_nat(ru)), xn, Equal.sym(Nat, xn, + Nat.add(Nat.mul(qn, mn), U32.to_nat(ru)), e_n), R.le_add_both(qn, Nat.mul(qn, mn), 1n, U32.to_nat(ru), + le.mul(qn, mn, hm), Equal.trans(Bool, Nat.is_le(1n, U32.to_nat(ru)), Nat.is_lt(0n, U32.to_nat(ru)), True{}, + Equal.sym(Bool, Nat.is_lt(0n, U32.to_nat(ru)), Nat.is_le(1n, U32.to_nat(ru)), R.lt_le_succ(0n, + U32.to_nat(ru))), h1))) : {Nat.is_le(Nat.add(qn, 1n), xn) == True{} : Bool}} + +e_q1 = {Equal.trans(Nat, U32.to_nat((qu + 1 : U32)), Nat.add(qn, 1n), 1n+qn, R.u32_add_below(qu, 1, nn, h_q1), + W9.add.one.nat(qn)) : {U32.to_nat((qu + 1 : U32)) == 1n+qn : Nat}} + %Equal.sym(Nat, U32.to_nat((qu + 1 : U32)), 1n+qn, e_q1) : kok(xn, _, mn) + nb.rem(qn, mn, xn, U32.to_nat(ru), e_n, h1, l_r) + +# the pair U32.div and U32.mod give, taken apart +def nb.go( + +nn: U32, + +mx: U32, + pr: dvm.ty(nn, mx), + +hm: {Nat.is_lt(0n, U32.to_nat(mx)) == True{} : Bool} +) -> kok(U32.to_nat(nn), U32.to_nat(nblk.m(nn, mx)), U32.to_nat(mx)): + (+e_n, +l_r) = pr + nb.at(U32.is_zero(U32.mod(nn, mx)), nn, mx, {==}, e_n, l_r, hm) + +# enc.nblk(n) blocks of 65535 bytes hold n bytes, the last one not empty +def nb.k( + +nn: U32, + +mx: U32, + +emx: {mx == 65535 : U32} +) -> kok(U32.to_nat(nn), U32.to_nat(Png.enc.nblk(nn)), U32.to_nat(mx)): + +mn = U32.to_nat(mx) + +hm = {Equal.trans(Bool, Nat.is_lt(0n, mn), Nat.is_lt(0n, 2n+U32.to_nat(((mx - 1 : U32) - 1 : U32))), True{}, + Equal.cong(Nat, Bool, kk => Nat.is_lt(0n, kk), mn, 2n+U32.to_nat(((mx - 1 : U32) - 1 : U32)), mx.two(mx, emx)), + {==}) : {Nat.is_lt(0n, mn) == True{} : Bool}} + +h2 = {R.le_rw_r(Nat.add(mn, mn), Nat.add(mn, mn), U32.to_nat(U32.add(mx, mx)), Equal.sym(Nat, + U32.to_nat(U32.add(mx, mx)), Nat.add(mn, mn), mx.dbl(mx, emx)), R.le_refl(Nat.add(mn, mn))) : + {Nat.is_le(Nat.add(mn, mn), U32.to_nat(U32.add(mx, mx))) == True{} : Bool}} + %Equal.sym(U32, Png.enc.nblk(nn), nblk.m(nn, mx), nblk.eq(nn, mx, emx)) : kok(U32.to_nat(nn), U32.to_nat(_), mn) + nb.go(nn, mx, dvm(nn, mx, U32.add(mx, mx), hm, h2), hm) + +# ---- F. how many bytes bs writes ---- + +# at most 0 is 0 +def le.zero(xx: Nat, +hh: {Nat.is_le(xx, 0n) == True{} : Bool}) -> {xx == 0n : Nat}: + match xx: + case 0n: + {==} + case 1n+pp: + Empty.absurd({1n+pp == 0n : Nat}, U32L.false_true(hh)) + +# nothing is below itself +def lt.irr(mm: Nat, +hh: {Nat.is_lt(mm, mm) == True{} : Bool}) -> Empty: + match mm: + case 0n: + U32L.false_true(hh) + case 1n+pp: + lt.irr(pp, hh) + +# m + x is not below m +def lt.self(+mm: Nat, +xx: Nat, +hh: {Nat.is_lt(Nat.add(mm, xx), mm) == True{} : Bool}) -> Empty: + lt.irr(mm, R.le_lt_trans(mm, Nat.add(mm, xx), mm, Wp2.nle_add(mm, xx), hh)) + +# a sum that is 0 has a right part 0 +def add.zero.r(aa: Nat, +bb: Nat, +hh: {Nat.add(aa, bb) == 0n : Nat}) -> {bb == 0n : Nat}: + match aa: + case 0n: + hh + case 1n+pp: + Empty.absurd({bb == 0n : Nat}, Wp1.zero_succ(Nat.add(pp, bb), Equal.sym(Nat, 1n+Nat.add(pp, bb), 0n, hh))) + +# no bytes left, no blocks left +def kok.nil(kk: Nat, +mn: Nat, hk: kok(0n, kk, mn)) -> {Nat.mul(kk, 5n) == 0n : Nat}: + match kk: + case 0n: + {==} + case 1n+(+kp): + (h_lt, _h_le) = hk + Empty.absurd({Nat.mul(1n+kp, 5n) == 0n : Nat}, U32L.false_true(Equal.trans(Bool, False{}, Nat.is_lt(Nat.mul(kp, + mn), 0n), True{}, R.lt_zero_false(Nat.mul(kp, mn)), h_lt))) + +# a new block's room after its first byte is at most the bytes after it +def ks.le( + zz: Bool, + +left: U32, + +mx: U32, + +nt: Nat, + +nzm: {U32.is_zero(mx) == False{} : Bool}, + +ez: {U32.is_le(left, mx) == zz : Bool}, + +hn: {U32.to_nat(left) == 1n+nt : Nat} +) -> {Nat.is_le(U32.to_nat((capm(zz, left, mx) - 1 : U32)), nt) == True{} : Bool}: + match zz: + case True{}: + R.le_rw_l(nt, U32.to_nat((left - 1 : U32)), nt, Equal.sym(Nat, U32.to_nat((left - 1 : U32)), nt, + Wp1.u32_pred(left, nt, hn)), R.le_refl(nt)) + case False{}: + +m1 = (mx - 1 : U32) + +e_m = {Wp1.u32_dec(mx, nzm) : {U32.to_nat(mx) == 1n+U32.to_nat(m1) : Nat}} + +h0 = {Equal.trans(Bool, Nat.is_le(1n+nt, 1n+U32.to_nat(m1)), U32.is_le(left, mx), False{}, + Equal.sym(Bool, U32.is_le(left, mx), Nat.is_le(1n+nt, 1n+U32.to_nat(m1)), Equal.trans(Bool, + U32.is_le(left, mx), Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), Nat.is_le(1n+nt, 1n+U32.to_nat(m1)), + R.u32_le(left, mx), Equal.trans(Bool, Nat.is_le(U32.to_nat(left), U32.to_nat(mx)), Nat.is_le(1n+nt, + U32.to_nat(mx)), Nat.is_le(1n+nt, 1n+U32.to_nat(m1)), Equal.cong(Nat, Bool, xx => Nat.is_le(xx, + U32.to_nat(mx)), U32.to_nat(left), 1n+nt, hn), Equal.cong(Nat, Bool, xx => Nat.is_le(1n+nt, xx), + U32.to_nat(mx), 1n+U32.to_nat(m1), e_m)))), ez) : {Nat.is_le(nt, U32.to_nat(m1)) == False{} : Bool}} + R.lt_le(U32.to_nat(m1), nt, nle.lt(nt, U32.to_nat(m1), h0)) + +# the blocks after a new block: one fewer than before it +def ks.k( + zz: Bool, + kp: Nat, + +left: U32, + +mx: U32, + +nt: Nat, + +nzm: {U32.is_zero(mx) == False{} : Bool}, + +ez: {U32.is_le(left, mx) == zz : Bool}, + +hn: {U32.to_nat(left) == 1n+nt : Nat}, + +h_lt: {Nat.is_lt(Nat.mul(kp, U32.to_nat(mx)), 1n+nt) == True{} : Bool}, + +h_le: {Nat.is_le(1n+nt, Nat.add(U32.to_nat(mx), Nat.mul(kp, U32.to_nat(mx)))) == True{} : Bool} +) -> kok(Nat.sub(nt, U32.to_nat((capm(zz, left, mx) - 1 : U32))), kp, U32.to_nat(mx)): + match zz kp: + case True{} 0n: + +e_l = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat((left - 1 : U32)) == nt : Nat}} + Equal.trans(Nat, Nat.sub(nt, U32.to_nat((left - 1 : U32))), Nat.sub(nt, nt), 0n, Equal.cong(Nat, Nat, xx => + Nat.sub(nt, xx), U32.to_nat((left - 1 : U32)), nt, e_l), R.sub_zero_le(nt, nt, R.le_refl(nt))) + case True{} 1n+(+k2): + +mn = U32.to_nat(mx) + +h0 = {Equal.trans(Bool, Nat.is_le(U32.to_nat(left), mn), U32.is_le(left, mx), True{}, Equal.sym(Bool, + U32.is_le(left, mx), Nat.is_le(U32.to_nat(left), mn), R.u32_le(left, mx)), ez) : {Nat.is_le(U32.to_nat(left), + mn) == True{} : Bool}} + +h1 = {R.le_rw_l(U32.to_nat(left), 1n+nt, mn, hn, h0) : {Nat.is_le(1n+nt, mn) == True{} : Bool}} + Empty.absurd(kok(Nat.sub(nt, U32.to_nat((left - 1 : U32))), 1n+k2, mn), lt.self(mn, Nat.mul(k2, mn), + R.lt_le_trans(Nat.add(mn, Nat.mul(k2, mn)), 1n+nt, mn, h_lt, h1))) + case False{} 0n: + +mn = U32.to_nat(mx) + +m1 = U32.to_nat((mx - 1 : U32)) + +xn = Nat.sub(nt, m1) + +e_m = {Wp1.u32_dec(mx, nzm) : {mn == 1n+m1 : Nat}} + +h_m = {ks.le(False{}, left, mx, nt, nzm, ez, hn) : {Nat.is_le(m1, nt) == True{} : Bool}} + +e_x = {Equal.trans(Nat, 1n+nt, 1n+Nat.add(m1, xn), Nat.add(mn, xn), Equal.cong(Nat, Nat, kk => 1n+kk, nt, + Nat.add(m1, xn), Equal.sym(Nat, Nat.add(m1, xn), nt, R.add_sub(m1, nt, h_m))), Equal.cong(Nat, Nat, kk => + Nat.add(kk, xn), 1n+m1, mn, Equal.sym(Nat, mn, 1n+m1, e_m))) : {1n+nt == Nat.add(mn, xn) : Nat}} + +h2 = {R.le_rw_l(1n+nt, Nat.add(mn, xn), Nat.add(mn, 0n), e_x, h_le) : {Nat.is_le(Nat.add(mn, xn), Nat.add(mn, + 0n)) == True{} : Bool}} + le.zero(xn, R.le_cancel(mn, xn, 0n, h2)) + case False{} 1n+(+k2): + +mn = U32.to_nat(mx) + +m1 = U32.to_nat((mx - 1 : U32)) + +xn = Nat.sub(nt, m1) + +km = Nat.mul(k2, mn) + +e_m = {Wp1.u32_dec(mx, nzm) : {mn == 1n+m1 : Nat}} + +h_m = {ks.le(False{}, left, mx, nt, nzm, ez, hn) : {Nat.is_le(m1, nt) == True{} : Bool}} + +e_x = {Equal.trans(Nat, 1n+nt, 1n+Nat.add(m1, xn), Nat.add(mn, xn), Equal.cong(Nat, Nat, kk => 1n+kk, nt, + Nat.add(m1, xn), Equal.sym(Nat, Nat.add(m1, xn), nt, R.add_sub(m1, nt, h_m))), Equal.cong(Nat, Nat, kk => + Nat.add(kk, xn), 1n+m1, mn, Equal.sym(Nat, mn, 1n+m1, e_m))) : {1n+nt == Nat.add(mn, xn) : Nat}} + (R.lt_cancel(mn, km, xn, R.lt_rw_r(Nat.add(mn, km), 1n+nt, Nat.add(mn, xn), e_x, h_lt)), R.le_cancel(mn, xn, + Nat.mul(1n+k2, mn), R.le_rw_l(1n+nt, Nat.add(mn, xn), Nat.add(mn, Nat.mul(1n+k2, mn)), e_x, h_le))) + +# what cnt proves: bs writes its bytes and 5 more for each block header +def cnt.ty( + +xs: List<&2, U32>, + +znew: Bool, + +zlast: Bool, + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +kk: Nat +) -> Type: + {List.length(&2, U32, bs(xs, znew, zlast, rp, left, plen, pbf)) == Nat.add(List.length(&2, U32, xs), Nat.mul(kk, + 5n)) : Nat} + +# bs after a block's first byte, with plen bytes in the block and l2 left after it +def bs2(+tl: List<&2, U32>, +plen: U32, +l2: U32) -> List<&2, U32>: + bs(tl, U32.is_zero((plen - 1 : U32)), U32.is_eq((plen - 1 : U32), 1), (plen - 1 : U32), l2, Png.enc.cap(l2), + Png.enc.bf(l2)) + +# the header, the first byte and the rest of a new block: six more bytes +def cnt.open( + +hd: U32, + +tl: List<&2, U32>, + +l2: U32, + +plen: U32, + +pbf: U32, + +kp: Nat, + +ih: {List.length(&2, U32, bs2(tl, plen, l2)) == Nat.add(List.length(&2, U32, tl), Nat.mul(kp, 5n)) : Nat} +) -> {List.length(&2, U32, open.bs(hd, tl, l2, plen, pbf)) == Nat.add(1n+List.length(&2, U32, tl), Nat.mul(1n+kp, + 5n)) : Nat}: + +nt = List.length(&2, U32, tl) + +k5 = Nat.mul(kp, 5n) + Equal.trans(Nat, 6n+List.length(&2, U32, bs(tl, U32.is_zero((plen - 1 : U32)), U32.is_eq((plen - 1 : U32), 1), + (plen - 1 : U32), l2, Png.enc.cap(l2), Png.enc.bf(l2))), 6n+Nat.add(nt, k5), 1n+Nat.add(nt, 5n+k5), + Equal.cong(Nat, Nat, xx => 6n+xx, List.length(&2, U32, bs(tl, U32.is_zero((plen - 1 : U32)), U32.is_eq((plen - 1 : + U32), 1), (plen - 1 : U32), l2, Png.enc.cap(l2), Png.enc.bf(l2))), Nat.add(nt, k5), ih), + Equal.cong(Nat, Nat, xx => 1n+xx, 5n+Nat.add(nt, k5), Nat.add(nt, 5n+k5), Equal.sym(Nat, Nat.add(nt, 5n+k5), + 5n+Nat.add(nt, k5), R.add_swap(nt, 5n, k5)))) + +# kok along an equation of the byte count +def kok.rw(+xa: Nat, +xb: Nat, +kk: Nat, +mn: Nat, +ee: {xa == xb : Nat}, hk: kok(xa, kk, mn)) -> kok(xb, kk, mn): + %ee : kok(_, kk, mn) + hk + +# the count's hypothesis for the rest of a block opened with plen bytes +def cnt.edge.ty(-tl: List<&2, U32>, -left: U32, -plen: U32, -kp: Nat, -mx: U32) -> Type: + @xn: Nat -> {Nat.add(U32.to_nat((plen - 1 : U32)), xn) == List.length(&2, U32, tl) : Nat} -> kok(xn, kp, + U32.to_nat(mx)) -> cnt.ty(tl, U32.is_zero((plen - 1 : U32)), U32.is_eq((plen - 1 : U32), 1), (plen - 1 : U32), + (left - 1 : U32), Png.enc.cap((left - 1 : U32)), Png.enc.bf((left - 1 : U32)), kp) + +# a new block with kp + 1 blocks to go: the header and the first byte, then +# the rest of the walk, with kp blocks after this one +def cnt.edge( + +hd: U32, + +tl: List<&2, U32>, + +left: U32, + +plen: U32, + +pbf: U32, + +mx: U32, + +emx: {mx == 65535 : U32}, + +kp: Nat, + +hn: {U32.to_nat(left) == 1n+List.length(&2, U32, tl) : Nat}, + +h_lt: {Nat.is_lt(Nat.mul(kp, U32.to_nat(mx)), 1n+List.length(&2, U32, tl)) == True{} : Bool}, + +h_le: {Nat.is_le(1n+List.length(&2, U32, tl), Nat.mul(1n+kp, U32.to_nat(mx))) == True{} : Bool}, + pe: {plen == Png.enc.cap(left) : U32} & {pbf == Png.enc.bf(left) : U32}, + cnt_t: cnt.edge.ty(tl, left, plen, kp, mx) +) -> {List.length(&2, U32, open.bs(hd, tl, (left - 1 : U32), plen, pbf)) == Nat.add(1n+List.length(&2, U32, tl), + Nat.mul(1n+kp, 5n)) : Nat}: + (+e_plen, _e_pbf) = pe + +nt = List.length(&2, U32, tl) + +zz = U32.is_le(left, 65535) + +cm = capm(zz, left, mx) + +mn = U32.to_nat(mx) + +c2 = U32.to_nat((plen - 1 : U32)) + +nzm = {mx.nz(mx, emx) : {U32.is_zero(mx) == False{} : Bool}} + +ezm = {le.mx(left, mx, emx) : {U32.is_le(left, mx) == zz : Bool}} + +e_c = {Equal.cong(U32, Nat, uu => U32.to_nat((uu - 1 : U32)), plen, cm, Equal.trans(U32, plen, Png.enc.cap(left), cm, + e_plen, cap.m(zz, left, mx, emx))) : {c2 == U32.to_nat((cm - 1 : U32)) : Nat}} + +h_c = {R.le_rw_l(U32.to_nat((cm - 1 : U32)), c2, nt, Equal.sym(Nat, c2, U32.to_nat((cm - 1 : U32)), e_c), ks.le(zz, + left, mx, nt, nzm, ezm, hn)) : {Nat.is_le(c2, nt) == True{} : Bool}} + +x2 = Nat.sub(nt, c2) + +hx2 = {R.add_sub(c2, nt, h_c) : {Nat.add(c2, x2) == nt : Nat}} + hk2 = kok.rw(Nat.sub(nt, U32.to_nat((cm - 1 : U32))), x2, kp, mn, Equal.cong(Nat, Nat, kk => Nat.sub(nt, kk), + U32.to_nat((cm - 1 : U32)), c2, Equal.sym(Nat, c2, U32.to_nat((cm - 1 : U32)), e_c)), ks.k(zz, kp, left, mx, nt, + nzm, ezm, hn, h_lt, h_le)) + cnt.open(hd, tl, (left - 1 : U32), plen, pbf, kp, cnt_t(x2, hx2, hk2)) + +# bs from a state: its bytes, and a header for each of the kk blocks it opens, +# xn the bytes after the open block (rp of them still in it) +def cnt( + xs: List<&2, U32>, + znew: Bool, + zlast: Bool, + +kk: Nat, + +rp: U32, + +left: U32, + +plen: U32, + +pbf: U32, + +mx: U32, + +xn: Nat, + +emx: {mx == 65535 : U32}, + +hzn: {znew == U32.is_zero(rp) : Bool}, + +hzl: {zlast == U32.is_eq(rp, 1) : Bool}, + +hn: {U32.to_nat(left) == List.length(&2, U32, xs) : Nat}, + +hx: {Nat.add(U32.to_nat(rp), xn) == List.length(&2, U32, xs) : Nat}, + hk: kok(xn, kk, U32.to_nat(mx)), + hb: at.edge(rp, left, plen, pbf) +) -> cnt.ty(xs, znew, zlast, rp, left, plen, pbf, kk): + match xs znew zlast: + case Nil{} _zn _zl: + +e_x = {add.zero.r(U32.to_nat(rp), xn, hx) : {xn == 0n : Nat}} + Equal.sym(Nat, Nat.mul(kk, 5n), 0n, kok.nil(kk, U32.to_nat(mx), kok.rw(xn, 0n, kk, U32.to_nat(mx), e_x, hk))) + case +h <> +t False{} False{}: + +rp2 = (rp - 1 : U32) + +l2 = (left - 1 : U32) + +nt = List.length(&2, U32, t) + +nz_rp = {Equal.sym(Bool, False{}, U32.is_zero(rp), hzn) : {U32.is_zero(rp) == False{} : Bool}} + +e_rp = {Wp1.u32_dec(rp, nz_rp) : {U32.to_nat(rp) == 1n+U32.to_nat(rp2) : Nat}} + +nz_rp2 = {two(rp, hzn, hzl) : {U32.is_zero(rp2) == False{} : Bool}} + +hx2 = {Wp1.succ_inj(Nat.add(U32.to_nat(rp2), xn), nt, Equal.trans(Nat, 1n+Nat.add(U32.to_nat(rp2), xn), + Nat.add(U32.to_nat(rp), xn), 1n+nt, Equal.cong(Nat, Nat, kk2 => Nat.add(kk2, xn), 1n+U32.to_nat(rp2), + U32.to_nat(rp), Equal.sym(Nat, U32.to_nat(rp), 1n+U32.to_nat(rp2), e_rp)), hx)) : {Nat.add(U32.to_nat(rp2), + xn) == nt : Nat}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + Equal.cong(Nat, Nat, x2 => 1n+x2, List.length(&2, U32, bs(t, False{}, U32.is_eq(rp2, 1), rp2, l2, plen, pbf)), + Nat.add(nt, Nat.mul(kk, 5n)), cnt(t, False{}, U32.is_eq(rp2, 1), kk, rp2, l2, plen, pbf, mx, xn, emx, + Equal.sym(Bool, U32.is_zero(rp2), False{}, nz_rp2), {==}, hn2, hx2, hk, edge.no(rp2, l2, plen, pbf, nz_rp2))) + case +h <> +t False{} True{}: + +l2 = (left - 1 : U32) + +nt = List.length(&2, U32, t) + +e_rp = {one.val(rp, hzl) : {U32.to_nat(rp) == 1n : Nat}} + +hx2 = {Wp1.succ_inj(xn, nt, Equal.trans(Nat, 1n+xn, Nat.add(U32.to_nat(rp), xn), 1n+nt, Equal.cong(Nat, Nat, + kk2 => Nat.add(kk2, xn), 1n, U32.to_nat(rp), Equal.sym(Nat, U32.to_nat(rp), 1n, e_rp)), hx)) : + {Nat.add(U32.to_nat(0), xn) == nt : Nat}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + Equal.cong(Nat, Nat, x2 => 1n+x2, List.length(&2, U32, bs(t, True{}, False{}, 0, l2, Png.enc.cap(l2), + Png.enc.bf(l2))), Nat.add(nt, Nat.mul(kk, 5n)), cnt(t, True{}, False{}, kk, 0, l2, Png.enc.cap(l2), + Png.enc.bf(l2), mx, xn, emx, {==}, {==}, hn2, hx2, hk, _ez => ({==}, {==}))) + case +h <> +t True{} _zl: + match kk: + case 0n: + +nt = List.length(&2, U32, t) + +e0 = {zero.val(rp, hzn) : {U32.to_nat(rp) == 0n : Nat}} + +e_xn = {Equal.trans(Nat, xn, Nat.add(U32.to_nat(rp), xn), 1n+nt, Equal.cong(Nat, Nat, kk2 => Nat.add(kk2, + xn), + 0n, U32.to_nat(rp), Equal.sym(Nat, U32.to_nat(rp), 0n, e0)), hx) : {xn == 1n+nt : Nat}} + Empty.absurd(cnt.ty(h <> t, True{}, _zl, rp, left, plen, pbf, 0n), Wp1.zero_succ(nt, Equal.trans(Nat, 0n, xn, + 1n+nt, Equal.sym(Nat, xn, 0n, hk), e_xn))) + case 1n+(+kp): + (+h_lt, +h_le) = hk + +nt = List.length(&2, U32, t) + +l2 = (left - 1 : U32) + +mn = U32.to_nat(mx) + +e0 = {zero.val(rp, hzn) : {U32.to_nat(rp) == 0n : Nat}} + +e_xn = {Equal.trans(Nat, xn, Nat.add(U32.to_nat(rp), xn), 1n+nt, Equal.cong(Nat, Nat, kk2 => Nat.add(kk2, + xn), + 0n, U32.to_nat(rp), Equal.sym(Nat, U32.to_nat(rp), 0n, e0)), hx) : {xn == 1n+nt : Nat}} + +ez = {Equal.sym(Bool, True{}, U32.is_zero(rp), hzn) : {U32.is_zero(rp) == True{} : Bool}} + +hn2 = {Wp1.u32_pred(left, nt, hn) : {U32.to_nat(l2) == nt : Nat}} + cnt.edge(h, t, left, plen, pbf, mx, emx, kp, hn, R.lt_rw_r(Nat.mul(kp, mn), xn, 1n+nt, e_xn, h_lt), + R.le_rw_l(xn, 1n+nt, Nat.mul(1n+kp, mn), e_xn, h_le), hb(ez), x2 => a1 => a2 => cnt(t, U32.is_zero((plen - 1 + : U32)), U32.is_eq((plen - 1 : U32), 1), kp, (plen - 1 : U32), l2, Png.enc.cap(l2), Png.enc.bf(l2), mx, x2, + emx, {==}, {==}, hn2, a1, a2, _ez => ({==}, {==}))) + +# the blocks enc.blocks writes for more than one block: the bytes, and 5 for +# each of the enc.nblk block headers +def cnt.blocks( + +xs: List<&2, U32>, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(xs, 0)) == List.length(&2, U32, xs) : Nat}, + +hbig: {U32.is_le(Png.enc.len(xs, 0), 65535) == False{} : Bool} +) -> {List.length(&2, U32, Png.enc.blocks(65535, xs)) == Nat.add(List.length(&2, U32, xs), + Nat.mul(U32.to_nat(Png.enc.nblk(Png.enc.len(xs, 0))), 5n)) : Nat}: + +nn = Png.enc.len(xs, 0) + +kk = U32.to_nat(Png.enc.nblk(nn)) + +bb = bs(xs, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn)) + Equal.trans(Nat, List.length(&2, U32, Png.enc.blocks(65535, xs)), List.length(&2, U32, bb), Nat.add(List.length(&2, + U32, xs), Nat.mul(kk, 5n)), Equal.cong(List<&2, U32>, Nat, ys => List.length(&2, U32, ys), Png.enc.blocks(65535, + xs), bb, sim.blocks(xs, mx, emx, hn, hbig)), cnt(xs, True{}, False{}, kk, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn), + mx, + List.length(&2, U32, xs), emx, {==}, {==}, hn, {==}, kok.rw(U32.to_nat(nn), List.length(&2, U32, xs), kk, + U32.to_nat(mx), hn, nb.k(nn, mx, emx)), _ez => ({==}, {==}))) + +# ---- G. the wide file, laid out as spec.png ---- + +# the CRC register and the accumulator after a run of bytes +def fr(+xs: List<&2, U32>, +cc: U32, +acc: List<&2, U32>) -> U32 & List<&2, U32>: + (Crc.crc.fold(xs, cc), List.reverse.go(&2, U32, xs, acc)) + +# folding a list then more is folding both +def fold.app( + xs: List<&2, U32>, + +ys: List<&2, U32>, + +cc: U32 +) -> {Crc.crc.fold(List.append(&2, U32, xs, ys), cc) == Crc.crc.fold(ys, Crc.crc.fold(xs, cc)) : U32}: + match xs: + case Nil{}: + {==} + case +hd <> tl: + fold.app(tl, ys, Crc.crc.byte(cc, hd)) + +# a run, then more: one run over both +def fr.app( + +xs: List<&2, U32>, + +ys: List<&2, U32>, + +cc: U32, + +acc: List<&2, U32> +) -> {fr(ys, Crc.crc.fold(xs, cc), List.reverse.go(&2, U32, xs, acc)) == fr(List.append(&2, U32, xs, ys), cc, + acc) : U32 & List<&2, U32>}: + +f2 = Crc.crc.fold(ys, Crc.crc.fold(xs, cc)) + +fa = Crc.crc.fold(List.append(&2, U32, xs, ys), cc) + +r2 = List.reverse.go(&2, U32, ys, List.reverse.go(&2, U32, xs, acc)) + +ra = List.reverse.go(&2, U32, List.append(&2, U32, xs, ys), acc) + Equal.trans(U32 & List<&2, U32>, (f2, r2), (fa, r2), (fa, ra), + Equal.cong(U32, U32 & List<&2, U32>, uu => (uu, r2), f2, fa, Equal.sym(U32, fa, f2, fold.app(xs, ys, cc))), + Equal.cong(List<&2, U32>, U32 & List<&2, U32>, ll => (fa, ll), r2, ra, Equal.sym(List<&2, U32>, ra, r2, + W9.revgo_app(xs, ys, acc)))) + +# three runs reversed onto nothing, reversed back: the runs in order +def rev3( + +aa: List<&2, U32>, + +bb: List<&2, U32>, + +cc: List<&2, U32> +) -> {List.reverse(&2, U32, List.reverse.go(&2, U32, cc, List.reverse.go(&2, U32, bb, List.reverse.go(&2, U32, aa, + [])))) == List.append(&2, U32, aa, List.append(&2, U32, bb, cc)) : List<&2, U32>}: + +ab = List.append(&2, U32, aa, bb) + +abc = List.append(&2, U32, ab, cc) + Equal.trans(List<&2, U32>, List.reverse(&2, U32, List.reverse.go(&2, U32, cc, List.reverse.go(&2, U32, bb, + List.reverse.go(&2, U32, aa, [])))), List.reverse(&2, U32, List.reverse.go(&2, U32, abc, [])), + List.append(&2, U32, aa, List.append(&2, U32, bb, cc)), + Equal.cong(List<&2, U32>, List<&2, U32>, ll => List.reverse(&2, U32, ll), List.reverse.go(&2, U32, cc, + List.reverse.go(&2, U32, bb, List.reverse.go(&2, U32, aa, []))), List.reverse.go(&2, U32, abc, []), + Equal.trans(List<&2, U32>, List.reverse.go(&2, U32, cc, List.reverse.go(&2, U32, bb, List.reverse.go(&2, U32, + aa, []))), List.reverse.go(&2, U32, cc, List.reverse.go(&2, U32, ab, [])), List.reverse.go(&2, U32, abc, []), + Equal.cong(List<&2, U32>, List<&2, U32>, ll => List.reverse.go(&2, U32, cc, ll), List.reverse.go(&2, U32, bb, + List.reverse.go(&2, U32, aa, [])), List.reverse.go(&2, U32, ab, []), Equal.sym(List<&2, U32>, + List.reverse.go(&2, U32, ab, []), List.reverse.go(&2, U32, bb, List.reverse.go(&2, U32, aa, [])), + W9.revgo_app(aa, bb, []))), + Equal.sym(List<&2, U32>, List.reverse.go(&2, U32, abc, []), List.reverse.go(&2, U32, cc, List.reverse.go(&2, + U32, ab, [])), W9.revgo_app(ab, cc, [])))), + Equal.trans(List<&2, U32>, List.reverse(&2, U32, List.reverse.go(&2, U32, abc, [])), abc, List.append(&2, U32, aa, + List.append(&2, U32, bb, cc)), Wp2.rev_rev(abc), Wp2.app_assoc(aa, bb, cc))) + +# the end of the IDAT chunk as seal.crc writes it: the length, the type and +# data, the CRC-32 of them, then IEND, once the whole file is reversed +def tail.eq( + +ll: U32, + +tt: List<&2, U32>, + +ie: List<&2, U32> +) -> {List.reverse(&2, U32, Png.enc.cat.go(ie, Png.enc.cat.go(Png.enc.be((Crc.crc.fold(tt, + 4294967295) .^. 4294967295 : U32)), List.reverse.go(&2, U32, tt, Png.enc.cat.go(Png.enc.be(ll), []))))) == + List.append(&2, U32, Png.enc.be(ll), List.append(&2, U32, tt, List.append(&2, U32, Png.enc.be((Crc.crc.fold(tt, + 4294967295) .^. 4294967295 : U32)), ie))) : List<&2, U32>}: + +bl = Png.enc.be(ll) + +ee = Png.enc.be((Crc.crc.fold(tt, 4294967295) .^. 4294967295 : U32)) + +a0 = List.reverse.go(&2, U32, bl, []) + +a1 = List.reverse.go(&2, U32, tt, a0) + +a2 = List.reverse.go(&2, U32, ee, a1) + +lt = List.append(&2, U32, bl, tt) + Equal.trans(List<&2, U32>, List.reverse(&2, U32, Png.enc.cat.go(ie, Png.enc.cat.go(ee, List.reverse.go(&2, U32, tt, + Png.enc.cat.go(bl, []))))), List.reverse(&2, U32, List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, + List.reverse.go(&2, U32, lt, [])))), List.append(&2, U32, bl, List.append(&2, U32, tt, List.append(&2, U32, ee, + ie))), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.reverse(&2, U32, ys), Png.enc.cat.go(ie, Png.enc.cat.go(ee, + List.reverse.go(&2, U32, tt, Png.enc.cat.go(bl, [])))), List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, + List.reverse.go(&2, U32, lt, []))), + Equal.trans(List<&2, U32>, Png.enc.cat.go(ie, Png.enc.cat.go(ee, List.reverse.go(&2, U32, tt, Png.enc.cat.go(bl, + [])))), Png.enc.cat.go(ie, Png.enc.cat.go(ee, a1)), List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, + List.reverse.go(&2, U32, lt, []))), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => Png.enc.cat.go(ie, Png.enc.cat.go(ee, List.reverse.go(&2, U32, + tt, ys))), Png.enc.cat.go(bl, []), a0, Wp2.catgo_rev(bl, [])), + Equal.trans(List<&2, U32>, Png.enc.cat.go(ie, Png.enc.cat.go(ee, a1)), Png.enc.cat.go(ie, a2), + List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, List.reverse.go(&2, U32, lt, []))), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => Png.enc.cat.go(ie, ys), Png.enc.cat.go(ee, a1), a2, + Wp2.catgo_rev(ee, a1)), + Equal.trans(List<&2, U32>, Png.enc.cat.go(ie, a2), List.reverse.go(&2, U32, ie, a2), List.reverse.go(&2, U32, + ie, List.reverse.go(&2, U32, ee, List.reverse.go(&2, U32, lt, []))), Wp2.catgo_rev(ie, a2), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, + ys)), a1, List.reverse.go(&2, U32, lt, []), Equal.sym(List<&2, U32>, List.reverse.go(&2, U32, lt, []), a1, + W9.revgo_app(bl, tt, []))))))), + Equal.trans(List<&2, U32>, List.reverse(&2, U32, List.reverse.go(&2, U32, ie, List.reverse.go(&2, U32, ee, + List.reverse.go(&2, U32, lt, [])))), List.append(&2, U32, lt, List.append(&2, U32, ee, ie)), List.append(&2, U32, + bl, List.append(&2, U32, tt, List.append(&2, U32, ee, ie))), rev3(lt, ee, ie), Wp2.app_assoc(bl, tt, + List.append(&2, U32, ee, ie)))) + +# the zlib stream of stored blocks bb and an Adler-32 sum +def zb(+bb: List<&2, U32>, +sum: U32) -> List<&2, U32>: + Png.enc.cat([120, 1], Png.enc.cat(bb, Png.enc.be(sum))) + +# the IDAT type, the zlib header and the stored blocks +def t0(+bb: List<&2, U32>) -> List<&2, U32>: + List.append(&2, U32, Png.enc.be(Png.tag.idat()), List.append(&2, U32, [120, 1], bb)) + +# the chunk's type and data as enc.chunk counts them are t0 then the sum +def body.eq( + +bb: List<&2, U32>, + +sum: U32 +) -> {Png.enc.cat(Png.enc.be(Png.tag.idat()), zb(bb, sum)) == List.append(&2, U32, t0(bb), Png.enc.be(sum)) : List<&2, + U32>}: + +bi = Png.enc.be(Png.tag.idat()) + +bs2 = Png.enc.be(sum) + +zz = List.append(&2, U32, [120, 1], List.append(&2, U32, bb, bs2)) + Equal.trans(List<&2, U32>, Png.enc.cat(bi, zb(bb, sum)), List.append(&2, U32, bi, zz), List.append(&2, U32, t0(bb), + bs2), + Equal.trans(List<&2, U32>, Png.enc.cat(bi, zb(bb, sum)), List.append(&2, U32, bi, zb(bb, sum)), List.append(&2, + U32, bi, zz), Wp2.cat_app(bi, zb(bb, sum)), Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, + bi, ys), zb(bb, sum), zz, Equal.trans(List<&2, U32>, zb(bb, sum), List.append(&2, U32, [120, 1], + Png.enc.cat(bb, bs2)), zz, Wp2.cat_app([120, 1], Png.enc.cat(bb, bs2)), Equal.cong(List<&2, U32>, List<&2, U32>, + ys => List.append(&2, U32, [120, 1], ys), Png.enc.cat(bb, bs2), List.append(&2, U32, bb, bs2), + Wp2.cat_app(bb, bs2))))), + Equal.sym(List<&2, U32>, List.append(&2, U32, t0(bb), bs2), List.append(&2, U32, bi, zz), Equal.trans(List<&2, + U32>, List.append(&2, U32, t0(bb), bs2), List.append(&2, U32, bi, List.append(&2, U32, List.append(&2, U32, [120, + 1], bb), bs2)), List.append(&2, U32, bi, zz), Wp2.app_assoc(bi, List.append(&2, U32, [120, 1], bb), bs2), + Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, bi, ys), List.append(&2, U32, + List.append(&2, U32, [120, 1], bb), bs2), zz, Wp2.app_assoc([120, 1], bb, bs2))))) + +# the file after the IDAT type as the encoder writes it, from t0 on +def gg(+xs: List<&2, U32>, +uu: U32, +ie: List<&2, U32>) -> List<&2, U32>: + List.append(&2, U32, Png.enc.be(uu), List.append(&2, U32, xs, List.append(&2, U32, Png.enc.be((Crc.crc.fold(xs, + 4294967295) .^. 4294967295 : U32)), ie))) + +# enc.chunk of IDAT, then IEND, is gg of its type and data +def chunk.gg( + +data: List<&2, U32>, + +ie: List<&2, U32> +) -> {Png.enc.cat(Png.enc.chunk(Png.tag.idat(), data), ie) == gg(Png.enc.cat(Png.enc.be(Png.tag.idat()), data), + Png.enc.len(data, 0), ie) : List<&2, U32>}: + +body = Png.enc.cat(Png.enc.be(Png.tag.idat()), data) + +be_c = Png.enc.be(Crc.crc32(body)) + +be_n = Png.enc.be(Png.enc.len(data, 0)) + Equal.trans(List<&2, U32>, Png.enc.cat(Png.enc.cat(be_n, Png.enc.cat(body, be_c)), ie), List.append(&2, U32, + List.append(&2, U32, be_n, List.append(&2, U32, body, be_c)), ie), gg(body, Png.enc.len(data, 0), ie), + Equal.trans(List<&2, U32>, Png.enc.cat(Png.enc.cat(be_n, Png.enc.cat(body, be_c)), ie), List.append(&2, U32, + Png.enc.cat(be_n, Png.enc.cat(body, be_c)), ie), List.append(&2, U32, List.append(&2, U32, be_n, List.append(&2, + U32, body, be_c)), ie), Wp2.cat_app(Png.enc.cat(be_n, Png.enc.cat(body, be_c)), ie), Equal.cong(List<&2, U32>, + List<&2, U32>, ys => List.append(&2, U32, ys, ie), Png.enc.cat(be_n, Png.enc.cat(body, be_c)), List.append(&2, + U32, be_n, List.append(&2, U32, body, be_c)), Equal.trans(List<&2, U32>, Png.enc.cat(be_n, Png.enc.cat(body, + be_c)), List.append(&2, U32, be_n, Png.enc.cat(body, be_c)), List.append(&2, U32, be_n, List.append(&2, U32, + body, be_c)), Wp2.cat_app(be_n, Png.enc.cat(body, be_c)), Equal.cong(List<&2, U32>, List<&2, U32>, ys => + List.append(&2, U32, be_n, ys), Png.enc.cat(body, be_c), List.append(&2, U32, body, be_c), Wp2.cat_app(body, + be_c))))), + Equal.trans(List<&2, U32>, List.append(&2, U32, List.append(&2, U32, be_n, List.append(&2, U32, body, be_c)), ie), + List.append(&2, U32, be_n, List.append(&2, U32, List.append(&2, U32, body, be_c), ie)), gg(body, + Png.enc.len(data, 0), ie), Wp2.app_assoc(be_n, List.append(&2, U32, body, be_c), ie), Equal.cong(List<&2, U32>, + List<&2, U32>, ys => List.append(&2, U32, be_n, ys), List.append(&2, U32, List.append(&2, U32, body, be_c), ie), + List.append(&2, U32, body, List.append(&2, U32, be_c, ie)), Wp2.app_assoc(body, be_c, ie)))) + +# the IDAT chunk from its CRC run: the run over the type, header and blocks, +# then the Adler-32 sum, the CRC, and IEND, is enc.chunk's IDAT then IEND +def tail.chunk( + +bb: List<&2, U32>, + +sum: U32, + +ll: U32, + +ie: List<&2, U32>, + +hl: {ll == Png.enc.len(zb(bb, sum), 0) : U32} +) -> {Png.enc.seal.adler(fr(t0(bb), 4294967295, Png.enc.cat.go(Png.enc.be(ll), [])), sum, ie) == + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie) : List<&2, U32>}: + +aa = Png.enc.cat.go(Png.enc.be(ll), []) + +bs2 = Png.enc.be(sum) + +tt = List.append(&2, U32, t0(bb), bs2) + +c0 = Crc.crc.fold(t0(bb), 4294967295) + +r0 = List.reverse.go(&2, U32, t0(bb), aa) + +body = Png.enc.cat(Png.enc.be(Png.tag.idat()), zb(bb, sum)) + Equal.trans(List<&2, U32>, Png.enc.seal.crc(Png.enc.crc.list(bs2, c0, r0), ie), gg(tt, ll, ie), + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie), + Equal.trans(List<&2, U32>, Png.enc.seal.crc(Png.enc.crc.list(bs2, c0, r0), ie), Png.enc.seal.crc(fr(tt, + 4294967295, aa), ie), gg(tt, ll, ie), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.seal.crc(pp, ie), Png.enc.crc.list(bs2, c0, r0), + fr(tt, 4294967295, aa), Equal.trans(U32 & List<&2, U32>, Png.enc.crc.list(bs2, c0, r0), fr(bs2, c0, r0), + fr(tt, 4294967295, aa), crcl(bs2, c0, r0), fr.app(t0(bb), bs2, 4294967295, aa))), + tail.eq(ll, tt, ie)), + Equal.sym(List<&2, U32>, Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie), gg(tt, ll, ie), + Equal.trans(List<&2, U32>, Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie), gg(body, + Png.enc.len(zb(bb, sum), 0), ie), gg(tt, ll, ie), chunk.gg(zb(bb, sum), ie), + Equal.trans(List<&2, U32>, gg(body, Png.enc.len(zb(bb, sum), 0), ie), gg(tt, Png.enc.len(zb(bb, sum), 0), ie), + gg(tt, ll, ie), Equal.cong(List<&2, U32>, List<&2, U32>, xs => gg(xs, Png.enc.len(zb(bb, sum), 0), ie), body, + tt, body.eq(bb, sum)), Equal.cong(U32, List<&2, U32>, uu => gg(tt, uu, ie), Png.enc.len(zb(bb, sum), 0), ll, + Equal.sym(U32, ll, Png.enc.len(zb(bb, sum), 0), hl)))))) + +# seal.wide up to the Adler field: the CRC run over the IDAT type, the zlib +# header and bs's blocks +def wide.front( + +raw: List<&2, U32>, + +nn: U32, + +sum: U32, + +aa: List<&2, U32>, + +ie: List<&2, U32> +) -> {Png.enc.seal.body(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, aa), raw, nn, sum, ie) == + Png.enc.seal.adler(fr(t0(bs(raw, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn))), 4294967295, aa), sum, + ie) : List<&2, U32>}: + +bi = Png.enc.be(Png.tag.idat()) + +bb = bs(raw, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn)) + +c1 = Crc.crc.fold(bi, 4294967295) + +a1 = List.reverse.go(&2, U32, bi, aa) + +c2 = Crc.crc.fold([120, 1], c1) + +a2 = List.reverse.go(&2, U32, [120, 1], a1) + Equal.trans(List<&2, U32>, Png.enc.seal.body(Png.enc.crc.list(bi, 4294967295, aa), raw, nn, sum, ie), + Png.enc.seal.body(fr(bi, 4294967295, aa), raw, nn, sum, ie), Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, + ie), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.seal.body(pp, raw, nn, sum, ie), + Png.enc.crc.list(bi, 4294967295, aa), fr(bi, 4294967295, aa), crcl(bi, 4294967295, aa)), + Equal.trans(List<&2, U32>, Png.enc.seal.run(Png.enc.crc.list([120, 1], c1, a1), raw, nn, sum, ie), + Png.enc.seal.run(fr([120, 1], c1, a1), raw, nn, sum, ie), Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, + ie), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.seal.run(pp, raw, nn, sum, ie), + Png.enc.crc.list([120, 1], c1, a1), fr([120, 1], c1, a1), crcl([120, 1], c1, a1)), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.seal.adler(pp, sum, ie), Png.enc.pour(raw, True{}, + False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn), c2, a2), fr(t0(bb), 4294967295, aa), + Equal.trans(U32 & List<&2, U32>, Png.enc.pour(raw, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn), c2, + a2), Png.enc.crc.list(bb, c2, a2), fr(t0(bb), 4294967295, aa), pour.bs(raw, True{}, False{}, 0, nn, + Png.enc.cap(nn), Png.enc.bf(nn), c2, a2), + Equal.trans(U32 & List<&2, U32>, Png.enc.crc.list(bb, c2, a2), fr(bb, c2, a2), fr(t0(bb), 4294967295, aa), + crcl(bb, c2, a2), + Equal.trans(U32 & List<&2, U32>, fr(bb, c2, a2), fr(List.append(&2, U32, [120, 1], bb), c1, a1), + fr(t0(bb), 4294967295, aa), fr.app([120, 1], bb, c1, a1), fr.app(bi, List.append(&2, U32, [120, 1], + bb), 4294967295, aa))))))) + +# seal.wide after the signature and IHDR: the IDAT chunk holding the zlib +# stream of stored blocks of 65535 bytes, then IEND, when its length field is +# right. The IHDR stays out of the statement: its CRC over a symbolic width +# and height costs the checker seconds each time a type holding it is read. +def wide.tail( + +raw: List<&2, U32>, + +mx: U32, + +ie: List<&2, U32>, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(raw, 0)) == List.length(&2, U32, raw) : Nat}, + +hbig: {U32.is_le(Png.enc.len(raw, 0), 65535) == False{} : Bool}, + +hl: {Png.enc.idat.ln(Png.enc.len(raw, 0)) == Png.enc.len(Laws.zlib.stored(65535, raw), 0) : U32} +) -> {Png.enc.seal.body(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, + Png.enc.cat.go(Png.enc.be(Png.enc.idat.ln(Png.enc.len(raw, 0))), [])), raw, Png.enc.len(raw, 0), Inf.adler.of(raw), + ie) == Png.enc.cat(Png.enc.chunk(Png.tag.idat(), Laws.zlib.stored(65535, raw)), ie) : List<&2, U32>}: + +nn = Png.enc.len(raw, 0) + +sum = Inf.adler.of(raw) + +ll = Png.enc.idat.ln(nn) + +aa = Png.enc.cat.go(Png.enc.be(ll), []) + +bb = bs(raw, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn)) + +bl = Png.enc.blocks(65535, raw) + +e_b = {sim.blocks(raw, mx, emx, hn, hbig) : {bl == bb : List<&2, U32>}} + +zz = Laws.zlib.stored(65535, raw) + +hl2 = {Equal.trans(U32, ll, Png.enc.len(zz, 0), Png.enc.len(zb(bb, sum), 0), hl, Equal.cong(List<&2, U32>, U32, ys => + Png.enc.len(zb(ys, sum), 0), bl, bb, e_b)) : {ll == Png.enc.len(zb(bb, sum), 0) : U32}} + Equal.trans(List<&2, U32>, Png.enc.seal.body(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, aa), raw, nn, + sum, ie), Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), + ie), wide.front(raw, nn, sum, aa, ie), Equal.trans(List<&2, U32>, Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), + sum, ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), + zz), ie), tail.chunk(bb, sum, ll, ie, hl2), Equal.cong(List<&2, U32>, List<&2, U32>, ys => + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(ys, sum)), ie), bb, bl, Equal.sym(List<&2, U32>, bl, bb, e_b)))) + +# 2 and x + 4 is x + 6 +def add.six(+xx: Nat) -> {2n+Nat.add(xx, 4n) == Nat.add(xx, 6n) : Nat}: + Equal.sym(Nat, Nat.add(xx, 6n), 2n+Nat.add(xx, 4n), Equal.trans(Nat, Nat.add(xx, 6n), 1n+Nat.add(xx, 5n), + 2n+Nat.add(xx, 4n), R.add_succ(xx, 5n), Equal.cong(Nat, Nat, kk => 1n+kk, Nat.add(xx, 5n), 1n+Nat.add(xx, 4n), + R.add_succ(xx, 4n)))) + +# the zlib stream of stored blocks of 65535 bytes: 6 bytes, the list, and 5 +# for each of enc.nblk's block headers +def zlib.len( + +raw: List<&2, U32>, + +mx: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(raw, 0)) == List.length(&2, U32, raw) : Nat}, + +hbig: {U32.is_le(Png.enc.len(raw, 0), 65535) == False{} : Bool} +) -> {List.length(&2, U32, Laws.zlib.stored(65535, raw)) == Nat.add(Nat.add(List.length(&2, U32, raw), + Nat.mul(U32.to_nat(Png.enc.nblk(Png.enc.len(raw, 0))), 5n)), 6n) : Nat}: + +bl = Png.enc.blocks(65535, raw) + +be_s = Png.enc.be(Inf.adler.of(raw)) + +xx = Nat.add(List.length(&2, U32, raw), Nat.mul(U32.to_nat(Png.enc.nblk(Png.enc.len(raw, 0))), 5n)) + +ap = List.append(&2, U32, [120, 1], List.append(&2, U32, bl, be_s)) + Equal.trans(Nat, List.length(&2, U32, Laws.zlib.stored(65535, raw)), List.length(&2, U32, ap), Nat.add(xx, 6n), + Equal.cong(List<&2, U32>, Nat, ys => List.length(&2, U32, ys), Laws.zlib.stored(65535, raw), ap, + Equal.trans(List<&2, + U32>, Laws.zlib.stored(65535, raw), List.append(&2, U32, [120, 1], Png.enc.cat(bl, be_s)), ap, Wp2.cat_app([120, 1], + Png.enc.cat(bl, be_s)), Equal.cong(List<&2, U32>, List<&2, U32>, ys => List.append(&2, U32, [120, 1], ys), + Png.enc.cat(bl, be_s), List.append(&2, U32, bl, be_s), Wp2.cat_app(bl, be_s)))), + Equal.trans(Nat, List.length(&2, U32, ap), 2n+Nat.add(List.length(&2, U32, bl), 4n), Nat.add(xx, 6n), + Equal.cong(Nat, Nat, kk => 2n+kk, List.length(&2, U32, List.append(&2, U32, bl, be_s)), Nat.add(List.length(&2, + U32, bl), 4n), W9.len.app(bl, be_s)), + Equal.trans(Nat, 2n+Nat.add(List.length(&2, U32, bl), 4n), 2n+Nat.add(xx, 4n), Nat.add(xx, 6n), Equal.cong(Nat, + Nat, kk => 2n+Nat.add(kk, 4n), List.length(&2, U32, bl), xx, cnt.blocks(raw, mx, emx, hn, hbig)), + add.six(xx)))) + +# the IDAT data's length for n raw bytes in k blocks: n + 5 k + 6 +def kzn(+nr: Nat, +nn: U32) -> Nat: + Nat.add(Nat.add(nr, Nat.mul(U32.to_nat(Png.enc.nblk(nn)), 5n)), 6n) + +# the IDAT data's length for the raw bytes xs +def kzs(+xs: List<&2, U32>) -> Nat: + kzn(List.length(&2, U32, xs), Png.enc.len(xs, 0)) + +# the IDAT length the encoder writes is the zlib stream's, when the stream is +# at most some U32's value long +def idat.len( + +raw: List<&2, U32>, + +mx: U32, + +tu: U32, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(raw, 0)) == List.length(&2, U32, raw) : Nat}, + +hbig: {U32.is_le(Png.enc.len(raw, 0), 65535) == False{} : Bool}, + +h_tu: {Nat.is_le(kzs(raw), U32.to_nat(tu)) == True{} : Bool} +) -> {Png.enc.idat.ln(Png.enc.len(raw, 0)) == Png.enc.len(Laws.zlib.stored(65535, raw), 0) : U32}: + +nn = Png.enc.len(raw, 0) + +kb = Png.enc.nblk(nn) + +k5 = Nat.mul(U32.to_nat(kb), 5n) + +n0 = List.length(&2, U32, raw) + +xx = Nat.add(n0, k5) + +tn = U32.to_nat(tu) + +h_x = {R.le_add_r(xx, 6n, tn, h_tu) : {Nat.is_le(xx, tn) == True{} : Bool}} + +h_k = {R.le_add_l(n0, k5, tn, h_x) : {Nat.is_le(k5, tn) == True{} : Bool}} + +e_m = {R.u32_mul_below(kb, 5, tu, h_k) : {U32.to_nat((kb * 5 : U32)) == k5 : Nat}} + +h_a = {R.le_rw_l(xx, Nat.add(U32.to_nat(nn), U32.to_nat((kb * 5 : U32))), tn, Equal.trans(Nat, xx, + Nat.add(U32.to_nat(nn), + k5), Nat.add(U32.to_nat(nn), U32.to_nat((kb * 5 : U32))), Equal.cong(Nat, Nat, kk => Nat.add(kk, k5), n0, + U32.to_nat(nn), Equal.sym(Nat, U32.to_nat(nn), n0, hn)), Equal.cong(Nat, Nat, kk => Nat.add(U32.to_nat(nn), kk), k5, + U32.to_nat((kb * 5 : U32)), Equal.sym(Nat, U32.to_nat((kb * 5 : U32)), k5, e_m))), h_x) : + {Nat.is_le(Nat.add(U32.to_nat(nn), U32.to_nat((kb * 5 : U32))), tn) == True{} : Bool}} + +e_a = {Equal.trans(Nat, U32.to_nat((nn + (kb * 5 : U32) : U32)), Nat.add(U32.to_nat(nn), + U32.to_nat((kb * 5 : U32))), xx, + R.u32_add_below(nn, (kb * 5 : U32), tu, h_a), Equal.trans(Nat, Nat.add(U32.to_nat(nn), U32.to_nat((kb * 5 : U32))), + Nat.add(n0, U32.to_nat((kb * 5 : U32))), xx, Equal.cong(Nat, Nat, kk => Nat.add(kk, U32.to_nat((kb * 5 : U32))), + U32.to_nat(nn), n0, hn), Equal.cong(Nat, Nat, kk => Nat.add(n0, kk), U32.to_nat((kb * 5 : U32)), k5, e_m))) : + {U32.to_nat((nn + (kb * 5 : U32) : U32)) == xx : Nat}} + +s1 = (nn + (kb * 5 : U32) : U32) + +h_6 = {R.le_rw_l(Nat.add(xx, 6n), Nat.add(U32.to_nat(s1), 6n), tn, Equal.cong(Nat, Nat, kk => Nat.add(kk, 6n), xx, + U32.to_nat(s1), Equal.sym(Nat, U32.to_nat(s1), xx, e_a)), h_tu) : {Nat.is_le(Nat.add(U32.to_nat(s1), U32.to_nat(6)), + tn) == True{} : Bool}} + +e_i = {Equal.trans(Nat, U32.to_nat((s1 + 6 : U32)), Nat.add(U32.to_nat(s1), 6n), Nat.add(xx, 6n), + R.u32_add_below(s1, 6, tu, h_6), Equal.cong(Nat, Nat, kk => Nat.add(kk, 6n), U32.to_nat(s1), xx, e_a)) : + {U32.to_nat(Png.enc.idat.ln(nn)) == Nat.add(xx, 6n) : Nat}} + +zz = Laws.zlib.stored(65535, raw) + +e_z = {zlib.len(raw, mx, emx, hn, hbig) : {List.length(&2, U32, zz) == Nat.add(xx, 6n) : Nat}} + +e_zl = {len.of(zz, tu, R.le_rw_l(Nat.add(xx, 6n), List.length(&2, U32, zz), tn, Equal.sym(Nat, List.length(&2, U32, + zz), Nat.add(xx, 6n), e_z), h_tu)) : {U32.to_nat(Png.enc.len(zz, 0)) == List.length(&2, U32, zz) : Nat}} + u32.same(Png.enc.idat.ln(nn), Png.enc.len(zz, 0), Equal.trans(Nat, U32.to_nat(Png.enc.idat.ln(nn)), Nat.add(xx, 6n), + U32.to_nat(Png.enc.len(zz, 0)), e_i, Equal.sym(Nat, U32.to_nat(Png.enc.len(zz, 0)), Nat.add(xx, 6n), + Equal.trans(Nat, + U32.to_nat(Png.enc.len(zz, 0)), List.length(&2, U32, zz), Nat.add(xx, 6n), e_zl, e_z)))) + +# ---- H. enc.rgb.go against bs ---- + +# Adler-32 as enc.rgb.go adds it up, then packed +def afold(xs: List<&2, U32>, +aa: U32, +bb: U32) -> U32: + match xs: + case Nil{}: + ((bb << 16n : U32) .|. aa : U32) + case +x <> t: + +a2 = Png.enc.add(aa, x) + +b2 = Png.enc.add(bb, a2) + afold(t, a2, b2) + +# the raw bytes enc.rgb.go's cursor has still to write, for a picture of +# rows wn samples wide: a filter byte first at a row's start, red while the +# sample is still in px, then green and blue of col +def cur(filt: Bool, zred: Bool, zgrn: Bool, px: List<&2, U32>, +col: U32, +row: U32, +wn: Nat) -> List<&2, U32>: + match filt: + case True{}: + W9.sc(px, 0n, wn, True{}) + case False{}: + match zred: + case True{}: + W9.sc(px, U32.to_nat(row), wn, True{}) + case False{}: + match zgrn: + case True{}: + Png.enc.g(col) <> Png.enc.b(col) <> W9.sc(px, Png.enc.pred(U32.to_nat(row)), wn, True{}) + case False{}: + Png.enc.b(col) <> W9.sc(px, Png.enc.pred(U32.to_nat(row)), wn, True{}) + +# the room bs has in its open block: none at a block's start +def rm(zblock: Bool, +need: U32) -> U32: + match zblock: + case True{}: + 0 + case False{}: + need + +# the facts enc.rgb.go's block state carries, nr the raw bytes still to come: +# at a block's start, left counts them; inside a block, need bytes are left +# in it, and left less len counts the bytes after it +def rg.hyp( + zblock: Bool, + +zdone: Bool, + +zspan: Bool, + +zneed: Bool, + +nr: Nat, + +left: U32, + +len: U32, + +need: U32, + +pl: U32, + +plen: U32, + +pbf: U32 +) -> Type: + match zblock: + case True{}: + {zdone == U32.is_zero(left) : Bool} & {U32.to_nat(left) == nr : Nat} + case False{}: + {zdone == False{} : Bool} & ({zspan == True{} : Bool} & ({zneed == U32.is_zero(need) : Bool} & + ({U32.to_nat(pl) == nr : Nat} & ({Nat.add(U32.to_nat(len), nr) == Nat.add(U32.to_nat(left), U32.to_nat(need)) : + Nat} & at.edge(need, pl, plen, pbf))))) + +# what enc.rgb.go returns: the CRC run over bs's blocks, and Adler-32 of the raw bytes +def rg.val( + zblock: Bool, + +rr: List<&2, U32>, + +cc: U32, + +acc: List<&2, U32>, + +aa: U32, + +bb: U32, + +left: U32, + +need: U32, + +pl: U32, + +plen: U32, + +pbf: U32 +) -> (U32 & List<&2, U32>) & U32: + match zblock: + case True{}: + (fr(bs(rr, True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left)), cc, acc), afold(rr, aa, bb)) + case False{}: + (fr(bs(rr, U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf), cc, acc), afold(rr, aa, bb)) + +# no raw bytes left: enc.rgb.go's end is bs's +def nil.r( + xs: List<&2, U32>, + +z1: Bool, + +z2: Bool, + +r1: U32, + +l1: U32, + +n1: U32, + +f1: U32, + +cc: U32, + +acc: List<&2, U32>, + +aa: U32, + +bb: U32, + +hh: {List.length(&2, U32, xs) == 0n : Nat} +) -> {Png.enc.rgb.fin(cc, acc, aa, bb) == (fr(bs(xs, z1, z2, r1, l1, n1, f1), cc, acc), afold(xs, aa, + bb)) : (U32 & List<&2, U32>) & U32}: + match xs: + case Nil{}: + {==} + case _x <> t: + Empty.absurd({Png.enc.rgb.fin(cc, acc, aa, bb) == (fr(bs(_x <> t, z1, z2, r1, l1, n1, f1), cc, acc), + afold(_x <> t, + aa, bb)) : (U32 & List<&2, U32>) & U32}, Wp1.zero_succ(List.length(&2, U32, t), Equal.sym(Nat, + 1n+List.length(&2, + U32, t), 0n, hh))) + +# the walk's hypothesis for the bytes after xb +def step.ty( + -xb: U32, + -rr: List<&2, U32>, + -need: U32, + -pl: U32, + -cc: U32, + -acc: List<&2, U32>, + -aa: U32, + -bb: U32, + -gg: (U32 & List<&2, U32>) & U32 +) -> Type: + @xx: U32 -> @yy: U32 -> at.edge((need - 1 : U32), (pl - 1 : U32), xx, yy) -> {gg == (fr(bs(rr, U32.is_zero((need - + 1 : U32)), U32.is_eq((need - 1 : U32), 1), (need - 1 : U32), (pl - 1 : U32), xx, yy), Crc.crc.byte(cc, xb), + xb <> acc), afold(rr, Png.enc.add(aa, xb), Png.enc.add(bb, Png.enc.add(aa, xb)))) : (U32 & List<&2, U32>) & U32} + +# one raw byte: bs writes it, and the block goes on or closes +def step( + z1: Bool, + +xb: U32, + +rr: List<&2, U32>, + +need: U32, + +pl: U32, + +plen: U32, + +pbf: U32, + +cc: U32, + +acc: List<&2, U32>, + +aa: U32, + +bb: U32, + -gg: (U32 & List<&2, U32>) & U32, + +ez1: {U32.is_eq(need, 1) == z1 : Bool}, + +nz: {U32.is_zero(need) == False{} : Bool}, + ih: step.ty(xb, rr, need, pl, cc, acc, aa, bb, gg) +) -> {gg == (fr(bs(xb <> rr, False{}, U32.is_eq(need, 1), need, pl, plen, pbf), cc, acc), afold(xb <> rr, aa, + bb)) : (U32 & List<&2, U32>) & U32}: + match z1: + case True{}: + +n1 = (need - 1 : U32) + +p1 = (pl - 1 : U32) + +c1 = Crc.crc.byte(cc, xb) + +a2 = Png.enc.add(aa, xb) + +b2 = Png.enc.add(bb, a2) + +e1 = {one.val(need, Equal.sym(Bool, U32.is_eq(need, 1), True{}, ez1)) : {U32.to_nat(need) == 1n : Nat}} + +e0 = {u32.same(n1, 0, Wp1.u32_pred(need, 0n, e1)) : {n1 == 0 : U32}} + %Equal.sym(Bool, U32.is_eq(need, 1), True{}, ez1) : {gg == (fr(bs(xb <> rr, False{}, _, need, pl, plen, pbf), cc, + acc), afold(xb <> rr, aa, bb)) : (U32 & List<&2, U32>) & U32} + Equal.trans((U32 & List<&2, U32>) & U32, gg, (fr(bs(rr, U32.is_zero(n1), U32.is_eq(n1, 1), n1, p1, + Png.enc.cap(p1), Png.enc.bf(p1)), c1, xb <> acc), afold(rr, a2, b2)), (fr(bs(rr, True{}, False{}, 0, p1, + Png.enc.cap(p1), Png.enc.bf(p1)), c1, xb <> acc), afold(rr, a2, b2)), + ih(Png.enc.cap(p1), Png.enc.bf(p1), _ez => ({==}, {==})), + Equal.cong(U32, (U32 & List<&2, U32>) & U32, uu => (fr(bs(rr, U32.is_zero(uu), U32.is_eq(uu, 1), uu, p1, + Png.enc.cap(p1), Png.enc.bf(p1)), c1, xb <> acc), afold(rr, a2, b2)), n1, 0, e0)) + case False{}: + +n1 = (need - 1 : U32) + +p1 = (pl - 1 : U32) + +c1 = Crc.crc.byte(cc, xb) + +a2 = Png.enc.add(aa, xb) + +b2 = Png.enc.add(bb, a2) + +nz1 = {two(need, Equal.sym(Bool, U32.is_zero(need), False{}, nz), Equal.sym(Bool, U32.is_eq(need, 1), False{}, + ez1)) : {U32.is_zero(n1) == False{} : Bool}} + %Equal.sym(Bool, U32.is_eq(need, 1), False{}, ez1) : {gg == (fr(bs(xb <> rr, False{}, _, need, pl, plen, pbf), cc, + acc), afold(xb <> rr, aa, bb)) : (U32 & List<&2, U32>) & U32} + Equal.trans((U32 & List<&2, U32>) & U32, gg, (fr(bs(rr, U32.is_zero(n1), U32.is_eq(n1, 1), n1, p1, plen, pbf), c1, + xb <> acc), afold(rr, a2, b2)), (fr(bs(rr, False{}, U32.is_eq(n1, 1), n1, p1, plen, pbf), c1, xb <> acc), + afold(rr, a2, b2)), + ih(plen, pbf, edge.no(n1, p1, plen, pbf, nz1)), + Equal.cong(Bool, (U32 & List<&2, U32>) & U32, zb => (fr(bs(rr, zb, U32.is_eq(n1, 1), n1, p1, plen, pbf), c1, + xb <> acc), afold(rr, a2, b2)), U32.is_zero(n1), False{}, nz1)) + +# the walk's hypothesis for the bytes after xb, with the fuel and counts +def rg.byte.ty( + -pp: Nat, + -xb: U32, + -r2: List<&2, U32>, + -need: U32, + -pl: U32, + -cc: U32, + -acc: List<&2, U32>, + -aa: U32, + -bb: U32, + -left: U32, + -len: U32, + -kk: Nat, + -xn: Nat, + -gg: (U32 & List<&2, U32>) & U32 +) -> Type: + @xx: U32 -> @yy: U32 -> at.edge((need - 1 : U32), (pl - 1 : U32), xx, yy) -> {Nat.add(U32.to_nat((need - 1 : U32)), + xn) == List.length(&2, U32, r2) : Nat} -> {Nat.is_le(Nat.add(Nat.add(List.length(&2, U32, r2), 1n), + Nat.double(kk)), 1n+pp) == True{} : Bool} -> {U32.to_nat((pl - 1 : U32)) == List.length(&2, U32, r2) : Nat} -> + {Nat.add(U32.to_nat(len), List.length(&2, U32, r2)) == Nat.add(U32.to_nat(left), U32.to_nat((need - 1 : U32))) : + Nat} -> {gg == (fr(bs(r2, U32.is_zero((need - 1 : U32)), U32.is_eq((need - 1 : U32), 1), (need - 1 : U32), (pl - 1 : + U32), xx, yy), Crc.crc.byte(cc, xb), xb <> acc), afold(r2, Png.enc.add(aa, xb), Png.enc.add(bb, Png.enc.add(aa, + xb)))) : (U32 & List<&2, U32>) & U32} + +# a raw byte in a block, from the rg state: the facts for the state after it, +# then step, then rg.val read with the byte in front +def rg.byte( + +pp: Nat, + +xb: U32, + +rr: List<&2, U32>, + +r2: List<&2, U32>, + +need: U32, + +pl: U32, + +plen: U32, + +pbf: U32, + +cc: U32, + +acc: List<&2, U32>, + +aa: U32, + +bb: U32, + +left: U32, + +len: U32, + +kk: Nat, + +xn: Nat, + -gg: (U32 & List<&2, U32>) & U32, + +e_r: {rr == xb <> r2 : List<&2, U32>}, + +e_zn: {False{} == U32.is_zero(need) : Bool}, + +hx: {Nat.add(U32.to_nat(need), xn) == List.length(&2, U32, rr) : Nat}, + +hw: {Nat.is_le(Nat.add(Nat.add(List.length(&2, U32, rr), 1n), Nat.double(kk)), 2n+pp) == True{} : Bool}, + +e_pl: {U32.to_nat(pl) == List.length(&2, U32, rr) : Nat}, + +e_ll: {Nat.add(U32.to_nat(len), List.length(&2, U32, rr)) == Nat.add(U32.to_nat(left), U32.to_nat(need)) : Nat}, + ih: rg.byte.ty(pp, xb, r2, need, pl, cc, acc, aa, bb, left, len, kk, xn, gg) +) -> {gg == rg.val(False{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf) : (U32 & List<&2, U32>) & U32}: + +n1 = (need - 1 : U32) + +nr = List.length(&2, U32, r2) + +nz = {Equal.sym(Bool, False{}, U32.is_zero(need), e_zn) : {U32.is_zero(need) == False{} : Bool}} + +e_n = {Wp1.u32_dec(need, nz) : {U32.to_nat(need) == 1n+U32.to_nat(n1) : Nat}} + +e_len = {Equal.cong(List<&2, U32>, Nat, ys => List.length(&2, U32, ys), rr, xb <> r2, e_r) : {List.length(&2, U32, + rr) == 1n+nr : Nat}} + +hx2 = {Wp1.succ_inj(Nat.add(U32.to_nat(n1), xn), nr, Equal.trans(Nat, 1n+Nat.add(U32.to_nat(n1), xn), + Nat.add(U32.to_nat(need), xn), 1n+nr, Equal.cong(Nat, Nat, kz => Nat.add(kz, xn), 1n+U32.to_nat(n1), + U32.to_nat(need), Equal.sym(Nat, U32.to_nat(need), 1n+U32.to_nat(n1), + e_n)), Equal.trans(Nat, Nat.add(U32.to_nat(need), + xn), List.length(&2, U32, rr), 1n+nr, hx, e_len))) : {Nat.add(U32.to_nat(n1), xn) == nr : Nat}} + +hw2 = {Equal.trans(Bool, Nat.is_le(Nat.add(Nat.add(1n+nr, 1n), Nat.double(kk)), 2n+pp), Nat.is_le(Nat.add(Nat.add( + List.length(&2, U32, rr), 1n), Nat.double(kk)), 2n+pp), True{}, Equal.cong(Nat, Bool, kz => + Nat.is_le(Nat.add(Nat.add(kz, 1n), Nat.double(kk)), 2n+pp), 1n+nr, List.length(&2, U32, rr), Equal.sym(Nat, + List.length(&2, U32, rr), 1n+nr, e_len)), hw) : {Nat.is_le(Nat.add(Nat.add(nr, 1n), Nat.double(kk)), 1n+pp) == + True{} : Bool}} + +epl2 = {Wp1.u32_pred(pl, nr, Equal.trans(Nat, U32.to_nat(pl), List.length(&2, U32, rr), 1n+nr, e_pl, e_len)) : + {U32.to_nat((pl - 1 : U32)) == nr : Nat}} + +ell2 = {Wp1.succ_inj(Nat.add(U32.to_nat(len), nr), Nat.add(U32.to_nat(left), U32.to_nat(n1)), Equal.trans(Nat, + 1n+Nat.add(U32.to_nat(len), nr), Nat.add(U32.to_nat(len), 1n+nr), 1n+Nat.add(U32.to_nat(left), U32.to_nat(n1)), + Equal.sym(Nat, Nat.add(U32.to_nat(len), 1n+nr), 1n+Nat.add(U32.to_nat(len), nr), R.add_succ(U32.to_nat(len), nr)), + Equal.trans(Nat, Nat.add(U32.to_nat(len), 1n+nr), Nat.add(U32.to_nat(left), 1n+U32.to_nat(n1)), + 1n+Nat.add(U32.to_nat(left), U32.to_nat(n1)), Equal.trans(Nat, Nat.add(U32.to_nat(len), 1n+nr), + Nat.add(U32.to_nat(len), List.length(&2, U32, rr)), Nat.add(U32.to_nat(left), 1n+U32.to_nat(n1)), Equal.cong(Nat, + Nat, + kz => Nat.add(U32.to_nat(len), kz), 1n+nr, List.length(&2, U32, rr), Equal.sym(Nat, List.length(&2, U32, rr), 1n+nr, + e_len)), Equal.trans(Nat, Nat.add(U32.to_nat(len), List.length(&2, U32, rr)), Nat.add(U32.to_nat(left), + U32.to_nat(need)), Nat.add(U32.to_nat(left), 1n+U32.to_nat(n1)), e_ll, Equal.cong(Nat, Nat, kz => + Nat.add(U32.to_nat(left), kz), U32.to_nat(need), 1n+U32.to_nat(n1), e_n))), R.add_succ(U32.to_nat(left), + U32.to_nat(n1))))) : {Nat.add(U32.to_nat(len), nr) == Nat.add(U32.to_nat(left), U32.to_nat(n1)) : Nat}} + Equal.trans((U32 & List<&2, U32>) & U32, gg, (fr(bs(xb <> r2, False{}, U32.is_eq(need, 1), need, pl, plen, pbf), cc, + acc), afold(xb <> r2, aa, bb)), rg.val(False{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), + step(U32.is_eq(need, 1), xb, r2, need, pl, plen, pbf, cc, acc, aa, bb, gg, {==}, nz, xx => yy => ee => ih(xx, yy, + ee, hx2, hw2, epl2, ell2)), + Equal.sym((U32 & List<&2, U32>) & U32, rg.val(False{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), + (fr(bs(xb <> r2, False{}, U32.is_eq(need, 1), need, pl, plen, pbf), cc, acc), afold(xb <> r2, aa, bb)), + Equal.trans((U32 & List<&2, U32>) & U32, rg.val(False{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), + (fr(bs(rr, False{}, U32.is_eq(need, 1), need, pl, plen, pbf), cc, acc), afold(rr, aa, bb)), + (fr(bs(xb <> r2, False{}, U32.is_eq(need, 1), need, pl, plen, pbf), cc, acc), afold(xb <> r2, aa, bb)), + Equal.cong(Bool, (U32 & List<&2, U32>) & U32, zb => (fr(bs(rr, zb, U32.is_eq(need, 1), need, pl, plen, pbf), + cc, acc), afold(rr, aa, bb)), U32.is_zero(need), False{}, nz), + Equal.cong(List<&2, U32>, (U32 & List<&2, U32>) & U32, ys => (fr(bs(ys, False{}, U32.is_eq(need, 1), need, pl, + plen, pbf), cc, acc), afold(ys, aa, bb)), rr, xb <> r2, e_r)))) + +# the five header bytes bs writes at a block's start from left bytes +def hd5(+left: U32) -> List<&2, U32>: + +len = Png.enc.cap(left) + +nlen = (len .^. 65535 : U32) + [Png.enc.bf(left), Png.enc.byte(len), Png.enc.hi(len), Png.enc.byte(nlen), Png.enc.hi(nlen)] + +# a new block, split as enc.rgb.go writes it: the header, then the bytes with +# the block's whole length in hand, once whether that length is 1 is named +def spl.at( + z1: Bool, + +hd: U32, + +tl: List<&2, U32>, + +left: U32, + +ez1: {U32.is_eq(Png.enc.cap(left), 1) == z1 : Bool}, + +nzc: {U32.is_zero(Png.enc.cap(left)) == False{} : Bool} +) -> {open.bs(hd, tl, (left - 1 : U32), Png.enc.cap(left), Png.enc.bf(left)) == List.append(&2, U32, hd5(left), + bs(hd <> tl, False{}, U32.is_eq(Png.enc.cap(left), 1), Png.enc.cap(left), left, Png.enc.cap((left - 1 : U32)), + Png.enc.bf((left - 1 : U32)))) : List<&2, U32>}: + match z1: + case True{}: + +cp = Png.enc.cap(left) + +l1 = (left - 1 : U32) + +e1 = {one.val(cp, Equal.sym(Bool, U32.is_eq(cp, 1), True{}, ez1)) : {U32.to_nat(cp) == 1n : Nat}} + +e0 = {u32.same((cp - 1 : U32), 0, Wp1.u32_pred(cp, 0n, e1)) : {(cp - 1 : U32) == 0 : U32}} + %Equal.sym(Bool, U32.is_eq(cp, 1), True{}, ez1) : {open.bs(hd, tl, l1, cp, Png.enc.bf(left)) == List.append(&2, + U32, + hd5(left), bs(hd <> tl, False{}, _, cp, left, Png.enc.cap(l1), Png.enc.bf(l1))) : List<&2, U32>} + Equal.cong(U32, List<&2, U32>, uu => List.append(&2, U32, hd5(left), hd <> bs(tl, U32.is_zero(uu), U32.is_eq(uu, + 1), + uu, l1, Png.enc.cap(l1), Png.enc.bf(l1))), (cp - 1 : U32), 0, e0) + case False{}: + +cp = Png.enc.cap(left) + +l1 = (left - 1 : U32) + +nz1 = {two(cp, Equal.sym(Bool, U32.is_zero(cp), False{}, nzc), Equal.sym(Bool, U32.is_eq(cp, 1), False{}, ez1)) : + {U32.is_zero((cp - 1 : U32)) == False{} : Bool}} + %Equal.sym(Bool, U32.is_eq(cp, 1), False{}, ez1) : {open.bs(hd, tl, l1, cp, Png.enc.bf(left)) == List.append(&2, + U32, + hd5(left), bs(hd <> tl, False{}, _, cp, left, Png.enc.cap(l1), Png.enc.bf(l1))) : List<&2, U32>} + Equal.cong(Bool, List<&2, U32>, zb => List.append(&2, U32, hd5(left), hd <> bs(tl, zb, U32.is_eq((cp - 1 : U32), + 1), + (cp - 1 : U32), l1, Png.enc.cap(l1), Png.enc.bf(l1))), U32.is_zero((cp - 1 : U32)), False{}, nz1) + +# a block's start with bytes left: the header, then the bytes with the block's +# whole length in hand +def spl( + xs: List<&2, U32>, + +left: U32, + +hn: {U32.to_nat(left) == List.length(&2, U32, xs) : Nat}, + +nzl: {U32.is_zero(left) == False{} : Bool}, + +nzc: {U32.is_zero(Png.enc.cap(left)) == False{} : Bool} +) -> {bs(xs, True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left)) == List.append(&2, U32, hd5(left), bs(xs, + False{}, U32.is_eq(Png.enc.cap(left), 1), Png.enc.cap(left), left, Png.enc.cap((left - 1 : U32)), + Png.enc.bf((left - 1 : U32)))) : List<&2, U32>}: + match xs: + case Nil{}: + Empty.absurd({bs([], True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left)) == List.append(&2, U32, + hd5(left), bs([], False{}, U32.is_eq(Png.enc.cap(left), 1), Png.enc.cap(left), left, Png.enc.cap((left - 1 : + U32)), Png.enc.bf((left - 1 : U32)))) : List<&2, U32>}, U32L.false_true(Equal.trans(Bool, False{}, + U32.is_zero(left), True{}, Equal.sym(Bool, U32.is_zero(left), False{}, nzl), zero.is(left, hn)))) + case +h <> +t: + spl.at(U32.is_eq(Png.enc.cap(left), 1), h, t, left, {==}, nzc) + +# the pair at.edge gives, used to rewrite bs's length and BFINAL +def n0.pe( + +rr: List<&2, U32>, + +pl: U32, + +plen: U32, + +pbf: U32, + pe: {plen == Png.enc.cap(pl) : U32} & {pbf == Png.enc.bf(pl) : U32} +) -> {bs(rr, True{}, False{}, 0, pl, plen, pbf) == bs(rr, True{}, False{}, 0, pl, Png.enc.cap(pl), + Png.enc.bf(pl)) : List<&2, U32>}: + (+e_l, +e_b) = pe + Equal.trans(List<&2, U32>, bs(rr, True{}, False{}, 0, pl, plen, pbf), bs(rr, True{}, False{}, 0, pl, Png.enc.cap(pl), + pbf), bs(rr, True{}, False{}, 0, pl, Png.enc.cap(pl), Png.enc.bf(pl)), Equal.cong(U32, List<&2, U32>, uu => bs(rr, + True{}, False{}, 0, pl, uu, pbf), plen, Png.enc.cap(pl), e_l), Equal.cong(U32, List<&2, U32>, uu => bs(rr, True{}, + False{}, 0, pl, Png.enc.cap(pl), uu), pbf, Png.enc.bf(pl), e_b)) + +# a block's end in enc.rgb.go's state is a block's start in bs's +def n0.bs( + +rr: List<&2, U32>, + +need: U32, + +pl: U32, + +plen: U32, + +pbf: U32, + +left2: U32, + +ez: {True{} == U32.is_zero(need) : Bool}, + +e_pl: {U32.to_nat(pl) == List.length(&2, U32, rr) : Nat}, + +e_l2: {U32.to_nat(left2) == List.length(&2, U32, rr) : Nat}, + hb: at.edge(need, pl, plen, pbf) +) -> {bs(rr, U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf) == bs(rr, True{}, False{}, 0, left2, + Png.enc.cap(left2), Png.enc.bf(left2)) : List<&2, U32>}: + +e0 = {u32.same(need, 0, zero.val(need, ez)) : {need == 0 : U32}} + +e_p = {u32.same(pl, left2, Equal.trans(Nat, U32.to_nat(pl), List.length(&2, U32, rr), U32.to_nat(left2), e_pl, + Equal.sym(Nat, U32.to_nat(left2), List.length(&2, U32, rr), e_l2))) : {pl == left2 : U32}} + Equal.trans(List<&2, U32>, bs(rr, U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf), bs(rr, True{}, False{}, + 0, pl, plen, pbf), bs(rr, True{}, False{}, 0, left2, Png.enc.cap(left2), Png.enc.bf(left2)), + Equal.cong(U32, List<&2, U32>, uu => bs(rr, U32.is_zero(uu), U32.is_eq(uu, 1), uu, pl, plen, pbf), need, 0, e0), + Equal.trans(List<&2, U32>, bs(rr, True{}, False{}, 0, pl, plen, pbf), bs(rr, True{}, False{}, 0, pl, + Png.enc.cap(pl), + Png.enc.bf(pl)), bs(rr, True{}, False{}, 0, left2, Png.enc.cap(left2), Png.enc.bf(left2)), n0.pe(rr, pl, plen, + pbf, + hb(Equal.sym(Bool, True{}, U32.is_zero(need), ez))), Equal.cong(U32, List<&2, U32>, uu => bs(rr, True{}, False{}, + 0, uu, Png.enc.cap(uu), Png.enc.bf(uu)), pl, left2, e_p))) + +# out of fuel at a block's start: no raw bytes are left +def z0.m1( + kk: Nat, + +xn: Nat, + +nr: Nat, + +mn: Nat, + +hx: {xn == nr : Nat}, + hk: kok(xn, kk, mn), + +hw: {Nat.is_le(Nat.add(Nat.add(nr, 0n), Nat.double(kk)), 1n) == True{} : Bool} +) -> {nr == 0n : Nat}: + match kk: + case 0n: + Equal.trans(Nat, nr, xn, 0n, Equal.sym(Nat, xn, nr, hx), hk) + case 1n+kp: + Empty.absurd({nr == 0n : Nat}, U32L.false_true(R.le_add_l(Nat.add(nr, 0n), 2n+Nat.double(kp), 1n, hw))) + +# out of fuel inside a block: no raw bytes are left +def z0.s2(+nr: Nat, +dk: Nat, +hw: {Nat.is_le(Nat.add(Nat.add(nr, 1n), dk), 1n) == True{} : Bool}) -> {nr == 0n : Nat}: + +h1 = {R.le_add_r(Nat.add(nr, 1n), dk, 1n, hw) : {Nat.is_le(Nat.add(nr, 1n), 1n) == True{} : Bool}} + +h2 = {R.le_rw_l(Nat.add(nr, 1n), 1n+Nat.add(nr, 0n), 1n, R.add_succ(nr, 0n), h1) : {Nat.is_le(Nat.add(nr, 0n), 0n) == + True{} : Bool}} + Equal.trans(Nat, nr, Nat.add(nr, 0n), 0n, Equal.sym(Nat, Nat.add(nr, 0n), nr, R.add_zero(nr)), le.zero(Nat.add(nr, + 0n), + h2)) + +# the fuel after a block header: one step fewer, one block fewer, one more to close it +def hw.head( + +nr: Nat, + +dk: Nat, + +pp: Nat, + +hw: {Nat.is_le(Nat.add(Nat.add(nr, 0n), 2n+dk), 2n+pp) == True{} : Bool} +) -> {Nat.is_le(Nat.add(Nat.add(nr, 1n), dk), 1n+pp) == True{} : Bool}: + +ee = {Equal.trans(Nat, Nat.add(Nat.add(nr, 0n), 2n+dk), Nat.add(nr, 2n+dk), 1n+Nat.add(Nat.add(nr, 1n), dk), + Equal.cong(Nat, Nat, kz => Nat.add(kz, 2n+dk), Nat.add(nr, 0n), nr, R.add_zero(nr)), Equal.trans(Nat, Nat.add(nr, + 2n+dk), 1n+Nat.add(nr, 1n+dk), 1n+Nat.add(Nat.add(nr, 1n), dk), R.add_succ(nr, 1n+dk), Equal.cong(Nat, Nat, kz => + 1n+kz, Nat.add(nr, 1n+dk), Nat.add(Nat.add(nr, 1n), dk), Equal.sym(Nat, Nat.add(Nat.add(nr, 1n), dk), Nat.add(nr, + 1n+dk), R.add_assoc(nr, 1n, dk))))) : {Nat.add(Nat.add(nr, 0n), 2n+dk) == 1n+Nat.add(Nat.add(nr, 1n), dk) : Nat}} + R.le_rw_l(Nat.add(Nat.add(nr, 0n), 2n+dk), 1n+Nat.add(Nat.add(nr, 1n), dk), 2n+pp, ee, hw) + +# the fuel at a block's end: the step that closes it +def hw.end( + +nr: Nat, + +dk: Nat, + +pp: Nat, + +hw: {Nat.is_le(Nat.add(Nat.add(nr, 1n), dk), 2n+pp) == True{} : Bool} +) -> {Nat.is_le(Nat.add(Nat.add(nr, 0n), dk), 1n+pp) == True{} : Bool}: + R.le_rw_l(Nat.add(Nat.add(nr, 1n), dk), 1n+Nat.add(Nat.add(nr, 0n), dk), 2n+pp, Equal.cong(Nat, Nat, kz => Nat.add(kz, + dk), Nat.add(nr, 1n), 1n+Nat.add(nr, 0n), R.add_succ(nr, 0n)), hw) + +# a sum that is 0 has a left part 0 +def add.zero.l(aa: Nat, +bb: Nat, +hh: {Nat.add(aa, bb) == 0n : Nat}) -> {aa == 0n : Nat}: + match aa: + case 0n: + {==} + case 1n+pp: + Empty.absurd({1n+pp == 0n : Nat}, Wp1.zero_succ(Nat.add(pp, bb), Equal.sym(Nat, 1n+Nat.add(pp, bb), 0n, hh))) + +# a byte is due in the block, so the raw bytes are not gone +def need.nil( + +need: U32, + +xn: Nat, + +e_zn: {False{} == U32.is_zero(need) : Bool}, + +hx: {Nat.add(U32.to_nat(need), xn) == 0n : Nat} +) -> Empty: + U32L.false_true(Equal.trans(Bool, False{}, U32.is_zero(need), True{}, e_zn, zero.is(need, add.zero.l(U32.to_nat(need), + xn, hx)))) + +# the filter byte: 0, then the row's samples +def cur.filt( + +sv: U32, + +tl: List<&2, U32>, + +row: U32, + +wp: Nat, + +er: {U32.to_nat(row) == 1n+wp : Nat} +) -> {W9.sc(sv <> tl, 0n, 1n+wp, True{}) == 0 <> W9.sc(sv <> tl, U32.to_nat(row), 1n+wp, True{}) : List<&2, U32>}: + Equal.cong(Nat, List<&2, U32>, kz => 0 <> W9.sc(sv <> tl, kz, 1n+wp, True{}), 1n+wp, U32.to_nat(row), Equal.sym(Nat, + U32.to_nat(row), 1n+wp, er)) + +# a red byte: the sample's red, then its green and blue and the rest of the row +def cur.red( + +sv: U32, + +tl: List<&2, U32>, + +row: U32, + +wn: Nat, + +hrow: {U32.is_zero(row) == False{} : Bool} +) -> {W9.sc(sv <> tl, U32.to_nat(row), wn, True{}) == Png.enc.r(sv) <> Png.enc.g(sv) <> Png.enc.b(sv) <> W9.sc(tl, + Png.enc.pred(U32.to_nat(row)), wn, True{}) : List<&2, U32>}: + +r1 = U32.to_nat((row - 1 : U32)) + +er = {Wp1.u32_dec(row, hrow) : {U32.to_nat(row) == 1n+r1 : Nat}} + Equal.trans(List<&2, U32>, W9.sc(sv <> tl, U32.to_nat(row), wn, True{}), W9.sc(sv <> tl, 1n+r1, wn, True{}), + Png.enc.r(sv) + <> Png.enc.g(sv) <> Png.enc.b(sv) <> W9.sc(tl, Png.enc.pred(U32.to_nat(row)), wn, True{}), + Equal.cong(Nat, List<&2, U32>, kz => W9.sc(sv <> tl, kz, wn, True{}), U32.to_nat(row), 1n+r1, er), + Equal.cong(Nat, List<&2, U32>, kz => Png.enc.r(sv) <> Png.enc.g(sv) <> Png.enc.b(sv) <> W9.sc(tl, Png.enc.pred(kz), + wn, + True{}), 1n+r1, U32.to_nat(row), Equal.sym(Nat, U32.to_nat(row), 1n+r1, er))) + +# a row's last blue byte: the next row starts with its filter byte +def cur.last( + +col: U32, + +px: List<&2, U32>, + +row: U32, + +wn: Nat, + +e1: {True{} == U32.is_eq(row, 1) : Bool} +) -> {Png.enc.b(col) <> W9.sc(px, Png.enc.pred(U32.to_nat(row)), wn, True{}) == Png.enc.b(col) <> W9.sc(px, 0n, wn, + True{}) : List<&2, U32>}: + Equal.cong(Nat, List<&2, U32>, kz => Png.enc.b(col) <> W9.sc(px, Png.enc.pred(kz), wn, True{}), U32.to_nat(row), 1n, + one.val(row, e1)) + +# a blue byte inside a row: the next sample's red comes next +def cur.blue( + +col: U32, + +px: List<&2, U32>, + +row: U32, + +wn: Nat, + +hrow: {U32.is_zero(row) == False{} : Bool} +) -> {Png.enc.b(col) <> W9.sc(px, Png.enc.pred(U32.to_nat(row)), wn, True{}) == Png.enc.b(col) <> W9.sc(px, + U32.to_nat((row - 1 : U32)), wn, True{}) : List<&2, U32>}: + Equal.cong(Nat, List<&2, U32>, kz => Png.enc.b(col) <> W9.sc(px, Png.enc.pred(kz), wn, True{}), U32.to_nat(row), + 1n+U32.to_nat((row - 1 : U32)), Wp1.u32_dec(row, hrow)) + +# at a filter byte or a blue channel, the next is not a red or green one, and zlast says the row's last pixel +def rg.hbl(filt: Bool, zred: Bool, zgrn: Bool, zlast: Bool, +row: U32) -> Type: + {filt == False{} : Bool} -> {zred == False{} : Bool} -> {zgrn == False{} : Bool} -> {zlast == U32.is_eq(row, 1) : + Bool} + +# how many bytes are left from the cursor +def curl(filt: Bool, zred: Bool, zgrn: Bool, px: List<&2, U32>, +col: U32, +row: U32, +wp: Nat) -> Nat: + List.length(&2, U32, cur(filt, zred, zgrn, px, col, row, 1n+wp)) + +# the steps the walk takes from the cursor: the bytes, one more when a block is open, and two per block +def rgw( + filt: Bool, + zred: Bool, + zgrn: Bool, + px: List<&2, U32>, + +col: U32, + +row: U32, + +wp: Nat, + zblock: Bool, + +kk: Nat +) -> Nat: + Nat.add(Nat.add(curl(filt, zred, zgrn, px, col, row, wp), R.bit(Bool.not(zblock))), Nat.double(kk)) + +# enc.rgb.go writes bs's blocks of the raw bytes its cursor walks, with their +# CRC run, and the Adler-32 of those bytes, while its fuel covers a step per +# raw byte and two per block (the header, and the step that closes it) +def rg( + fuel: Nat, + +zblock: Bool, + +zdone: Bool, + +zspan: Bool, + +zneed: Bool, + +filt: Bool, + +zred: Bool, + +zgrn: Bool, + +zlast: Bool, + +px: List<&2, U32>, + +col: U32, + +row: U32, + +ww: U32, + +need: U32, + +cc: U32, + +acc: List<&2, U32>, + +aa: U32, + +bb: U32, + +left: U32, + +len: U32, + +pl: U32, + +plen: U32, + +pbf: U32, + +kk: Nat, + +xn: Nat, + +wp: Nat, + +mx: U32, + +emx: {mx == 65535 : U32}, + +ew: {U32.to_nat(ww) == 1n+wp : Nat}, + +hrow: {U32.is_zero(row) == False{} : Bool}, + hfw: {filt == True{} : Bool} -> {row == ww : U32}, + hbl: rg.hbl(filt, zred, zgrn, zlast, row), + +hx: {Nat.add(U32.to_nat(rm(zblock, need)), xn) == curl(filt, zred, zgrn, px, col, row, wp) : Nat}, + hk: kok(xn, kk, U32.to_nat(mx)), + +hw: {Nat.is_le(rgw(filt, zred, zgrn, px, col, row, wp, zblock, kk), 1n+fuel) == True{} : Bool}, + hb: rg.hyp(zblock, zdone, zspan, zneed, curl(filt, zred, zgrn, px, col, row, wp), left, len, need, pl, plen, pbf) +) -> {Png.enc.rgb.go(fuel, zblock, zdone, zspan, zneed, filt, zred, zgrn, zlast, px, col, row, ww, need, cc, acc, aa, + bb, left, len) == rg.val(zblock, cur(filt, zred, zgrn, px, col, row, 1n+wp), cc, acc, aa, bb, left, need, pl, plen, + pbf) : (U32 & List<&2, U32>) & U32}: + match fuel: + case 0n: + match zblock: + case True{}: + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + nil.r(rr, True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left), cc, acc, aa, bb, z0.m1(kk, xn, + List.length(&2, U32, rr), U32.to_nat(mx), hx, hk, hw)) + case False{}: + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + nil.r(rr, U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf, cc, acc, aa, bb, + z0.s2(List.length(&2, U32, rr), Nat.double(kk), hw)) + case 1n+(+pp): + match zblock: + case True{}: + match zdone: + case True{}: + (e_zd, e_l) = hb + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + nil.r(rr, True{}, False{}, 0, left, Png.enc.cap(left), Png.enc.bf(left), cc, acc, aa, bb, + Equal.trans(Nat, List.length(&2, U32, rr), U32.to_nat(left), 0n, Equal.sym(Nat, U32.to_nat(left), + List.length(&2, U32, rr), e_l), zero.val(left, e_zd))) + case False{}: + match kk: + case 0n: + (e_zd, e_l) = hb + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + +e_r0 = {Equal.trans(Nat, U32.to_nat(left), List.length(&2, U32, rr), 0n, e_l, Equal.trans(Nat, + List.length(&2, U32, rr), xn, 0n, Equal.sym(Nat, xn, List.length(&2, U32, rr), hx), hk)) : + {U32.to_nat(left) == 0n : Nat}} + Empty.absurd({Png.enc.rgb.go(1n+pp, True{}, False{}, zspan, zneed, filt, zred, zgrn, zlast, px, col, + row, ww, need, cc, acc, aa, bb, left, len) == rg.val(True{}, rr, cc, acc, aa, bb, left, need, pl, + plen, pbf) : (U32 & List<&2, U32>) & U32}, U32L.false_true(Equal.trans(Bool, False{}, + U32.is_zero(left), True{}, e_zd, zero.is(left, e_r0)))) + case 1n+(+kp): + match hk hb: + case Tuple{h_lt, h_le} Tuple{e_zd, +e_l}: + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + +nr = List.length(&2, U32, rr) + +mn = U32.to_nat(mx) + +l1 = (left - 1 : U32) + +nt = U32.to_nat(l1) + +nzl = {Equal.sym(Bool, False{}, U32.is_zero(left), e_zd) : {U32.is_zero(left) == False{} : Bool}} + +e_lt = {Wp1.u32_dec(left, nzl) : {U32.to_nat(left) == 1n+nt : Nat}} + +e_R = {Equal.trans(Nat, nr, U32.to_nat(left), 1n+nt, Equal.sym(Nat, U32.to_nat(left), nr, e_l), + e_lt) : {nr == 1n+nt : Nat}} + +zz = U32.is_le(left, 65535) + +cp = Png.enc.cap(left) + +cm = capm(zz, left, mx) + +c1 = (cp - 1 : U32) + +nzm = {mx.nz(mx, emx) : {U32.is_zero(mx) == False{} : Bool}} + +ezm = {le.mx(left, mx, emx) : {U32.is_le(left, mx) == zz : Bool}} + +e_cm = {cap.m(zz, left, mx, emx) : {cp == cm : U32}} + +nz_cp = {Equal.trans(Bool, U32.is_zero(cp), U32.is_zero(cm), False{}, Equal.cong(U32, Bool, uu => + U32.is_zero(uu), cp, cm, e_cm), cap.nz(zz, left, mx, nt, nzm, e_lt)) : {U32.is_zero(cp) == + False{} : Bool}} + +e_cp = {Wp1.u32_dec(cp, nz_cp) : {U32.to_nat(cp) == 1n+U32.to_nat(c1) : Nat}} + +e_c = {Equal.cong(U32, Nat, uu => U32.to_nat((uu - 1 : U32)), cp, cm, e_cm) : {U32.to_nat(c1) == + U32.to_nat((cm - 1 : U32)) : Nat}} + +h_c = {R.le_rw_l(U32.to_nat((cm - 1 : U32)), U32.to_nat(c1), nt, Equal.sym(Nat, U32.to_nat(c1), + U32.to_nat((cm - 1 : U32)), e_c), ks.le(zz, left, mx, nt, nzm, ezm, e_lt)) : + {Nat.is_le(U32.to_nat(c1), nt) == True{} : Bool}} + +x2 = Nat.sub(nt, U32.to_nat(c1)) + +hx2 = {Equal.trans(Nat, Nat.add(U32.to_nat(cp), x2), 1n+Nat.add(U32.to_nat(c1), x2), nr, + Equal.cong(Nat, Nat, kz => Nat.add(kz, x2), U32.to_nat(cp), 1n+U32.to_nat(c1), e_cp), + Equal.trans(Nat, 1n+Nat.add(U32.to_nat(c1), x2), 1n+nt, nr, Equal.cong(Nat, Nat, kz => 1n+kz, + Nat.add(U32.to_nat(c1), x2), nt, R.add_sub(U32.to_nat(c1), nt, h_c)), Equal.sym(Nat, nr, 1n+nt, + e_R))) : {Nat.add(U32.to_nat(cp), x2) == nr : Nat}} + +h_lt2 = {R.lt_rw_r(Nat.mul(kp, mn), xn, 1n+nt, Equal.trans(Nat, xn, nr, 1n+nt, hx, e_R), h_lt) : + {Nat.is_lt(Nat.mul(kp, mn), 1n+nt) == True{} : Bool}} + +h_le2 = {R.le_rw_l(xn, 1n+nt, Nat.add(mn, Nat.mul(kp, mn)), Equal.trans(Nat, xn, nr, 1n+nt, hx, + e_R), + h_le) : {Nat.is_le(1n+nt, Nat.add(mn, Nat.mul(kp, mn))) == True{} : Bool}} + hk2 = kok.rw(Nat.sub(nt, U32.to_nat((cm - 1 : U32))), x2, kp, mn, Equal.cong(Nat, Nat, kz => + Nat.sub(nt, kz), U32.to_nat((cm - 1 : U32)), U32.to_nat(c1), Equal.sym(Nat, U32.to_nat(c1), + U32.to_nat((cm - 1 : U32)), e_c)), ks.k(zz, kp, left, mx, nt, nzm, ezm, e_lt, h_lt2, h_le2)) + +hw2 = {hw.head(nr, Nat.double(kp), pp, hw) : {Nat.is_le(Nat.add(Nat.add(nr, 1n), Nat.double(kp)), + 1n+pp) == True{} : Bool}} + +ell2 = {Equal.trans(Nat, Nat.add(U32.to_nat(cp), nr), Nat.add(nr, U32.to_nat(cp)), + Nat.add(U32.to_nat(left), U32.to_nat(cp)), R.add_comm(U32.to_nat(cp), nr), Equal.cong(Nat, Nat, + kz => Nat.add(kz, U32.to_nat(cp)), nr, U32.to_nat(left), Equal.sym(Nat, U32.to_nat(left), nr, + e_l))) : {Nat.add(U32.to_nat(cp), nr) == Nat.add(U32.to_nat(left), U32.to_nat(cp)) : Nat}} + +bf = Png.enc.bf(left) + +nlen = (cp .^. 65535 : U32) + +b1 = Png.enc.byte(cp) + +b2 = Png.enc.hi(cp) + +b3 = Png.enc.byte(nlen) + +b4 = Png.enc.hi(nlen) + +c5 = Crc.crc.byte(Crc.crc.byte(Crc.crc.byte(Crc.crc.byte(Crc.crc.byte(cc, bf), b1), b2), b3), b4) + +a4 = {b4 <> b3 <> b2 <> b1 <> bf <> acc : List<&2, U32>} + +bb2 = bs(rr, False{}, U32.is_eq(cp, 1), cp, left, Png.enc.cap(l1), Png.enc.bf(l1)) + +ih = {rg(pp, False{}, False{}, True{}, False{}, filt, zred, zgrn, zlast, px, col, row, ww, cp, + c5, a4, + aa, bb, left, cp, left, Png.enc.cap(l1), Png.enc.bf(l1), kp, x2, wp, mx, emx, ew, hrow, hfw, + hbl, + hx2, hk2, hw2, ({==}, ({==}, (Equal.sym(Bool, U32.is_zero(cp), False{}, nz_cp), (e_l, (ell2, + edge.no(cp, left, Png.enc.cap(l1), Png.enc.bf(l1), nz_cp))))))) : {Png.enc.rgb.go(pp, False{}, + False{}, True{}, False{}, filt, zred, zgrn, zlast, px, col, row, ww, cp, c5, a4, aa, bb, left, + cp) + == (fr(bs(rr, U32.is_zero(cp), U32.is_eq(cp, 1), cp, left, Png.enc.cap(l1), Png.enc.bf(l1)), c5, + a4), afold(rr, aa, bb)) : (U32 & List<&2, U32>) & U32}} + Equal.trans((U32 & List<&2, U32>) & U32, Png.enc.rgb.go(pp, False{}, False{}, True{}, False{}, + filt, + zred, zgrn, zlast, px, col, row, ww, cp, c5, a4, aa, bb, left, cp), (fr(bb2, c5, a4), afold(rr, + aa, + bb)), rg.val(True{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), + Equal.trans((U32 & List<&2, U32>) & U32, Png.enc.rgb.go(pp, False{}, False{}, True{}, False{}, + filt, + zred, zgrn, zlast, px, col, row, ww, cp, c5, a4, aa, bb, left, cp), (fr(bs(rr, + U32.is_zero(cp), + U32.is_eq(cp, 1), cp, left, Png.enc.cap(l1), Png.enc.bf(l1)), c5, a4), afold(rr, aa, bb)), + (fr(bb2, c5, a4), afold(rr, aa, bb)), ih, Equal.cong(Bool, (U32 & List<&2, U32>) & U32, zb => + (fr(bs(rr, zb, U32.is_eq(cp, 1), cp, left, Png.enc.cap(l1), Png.enc.bf(l1)), c5, a4), + afold(rr, + aa, bb)), U32.is_zero(cp), False{}, nz_cp)), + Equal.sym((U32 & List<&2, U32>) & U32, rg.val(True{}, rr, cc, acc, aa, bb, left, need, pl, plen, + pbf), (fr(bb2, c5, a4), afold(rr, aa, bb)), Equal.trans((U32 & List<&2, U32>) & U32, + rg.val(True{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), (fr(List.append(&2, U32, + hd5(left), bb2), cc, acc), afold(rr, aa, bb)), (fr(bb2, c5, a4), afold(rr, aa, bb)), + Equal.cong(List<&2, U32>, (U32 & List<&2, U32>) & U32, ys => (fr(ys, cc, acc), afold(rr, aa, + bb)), + bs(rr, True{}, False{}, 0, left, cp, bf), List.append(&2, U32, hd5(left), bb2), spl(rr, left, + e_l, nzl, nz_cp)), Equal.cong(U32 & List<&2, U32>, (U32 & List<&2, U32>) & U32, qq => (qq, + afold(rr, aa, bb)), fr(List.append(&2, U32, hd5(left), bb2), cc, acc), fr(bb2, c5, a4), + Equal.sym(U32 & List<&2, U32>, fr(bb2, c5, a4), fr(List.append(&2, U32, hd5(left), bb2), cc, + acc), fr.app(hd5(left), bb2, cc, acc)))))) + case False{}: + match zdone: + case True{}: + (e_d, _rest) = hb + Empty.absurd({Png.enc.rgb.go(1n+pp, False{}, True{}, zspan, zneed, filt, zred, zgrn, zlast, px, col, row, + ww, need, cc, acc, aa, bb, left, len) == rg.val(False{}, cur(filt, zred, zgrn, px, col, row, 1n+wp), cc, + acc, aa, bb, left, need, pl, plen, pbf) : (U32 & List<&2, U32>) & U32}, U32L.false_true(Equal.sym(Bool, + True{}, False{}, e_d))) + case False{}: + match zspan: + case False{}: + (_e_d, (e_s, _rest)) = hb + Empty.absurd({Png.enc.rgb.go(1n+pp, False{}, False{}, False{}, zneed, filt, zred, zgrn, zlast, px, + col, row, ww, need, cc, acc, aa, bb, left, len) == rg.val(False{}, cur(filt, zred, zgrn, px, col, + row, 1n+wp), cc, acc, aa, bb, left, need, pl, plen, pbf) : (U32 & List<&2, U32>) & U32}, + U32L.false_true(e_s)) + case True{}: + match zneed: + case True{}: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, hbe))))) = hb + +rr = cur(filt, zred, zgrn, px, col, row, 1n+wp) + +nr = List.length(&2, U32, rr) + +l2 = (left - len : U32) + +e_n0 = {zero.val(need, e_zn) : {U32.to_nat(need) == 0n : Nat}} + +e_lf = {Equal.trans(Nat, U32.to_nat(left), Nat.add(U32.to_nat(left), 0n), + Nat.add(U32.to_nat(len), + nr), Equal.sym(Nat, Nat.add(U32.to_nat(left), 0n), U32.to_nat(left), + R.add_zero(U32.to_nat(left))), Equal.trans(Nat, Nat.add(U32.to_nat(left), 0n), + Nat.add(U32.to_nat(left), U32.to_nat(need)), Nat.add(U32.to_nat(len), nr), Equal.cong(Nat, Nat, + kz => Nat.add(U32.to_nat(left), kz), 0n, U32.to_nat(need), Equal.sym(Nat, U32.to_nat(need), 0n, + e_n0)), Equal.sym(Nat, Nat.add(U32.to_nat(len), nr), Nat.add(U32.to_nat(left), + U32.to_nat(need)), e_ll))) : {U32.to_nat(left) == Nat.add(U32.to_nat(len), nr) : Nat}} + +h_ll = {R.le_rw_r(U32.to_nat(len), Nat.add(U32.to_nat(len), nr), U32.to_nat(left), Equal.sym(Nat, + U32.to_nat(left), Nat.add(U32.to_nat(len), nr), e_lf), Wp2.nle_add(U32.to_nat(len), nr)) : + {Nat.is_le(U32.to_nat(len), U32.to_nat(left)) == True{} : Bool}} + +e_l2 = {Equal.trans(Nat, U32.to_nat(l2), Nat.sub(U32.to_nat(left), U32.to_nat(len)), nr, + R.u32_sub_nat(left, len, h_ll), Equal.trans(Nat, Nat.sub(U32.to_nat(left), U32.to_nat(len)), + Nat.sub(Nat.add(U32.to_nat(len), nr), U32.to_nat(len)), nr, Equal.cong(Nat, Nat, kz => + Nat.sub(kz, U32.to_nat(len)), U32.to_nat(left), Nat.add(U32.to_nat(len), nr), e_lf), + R.sub_add_cancel(U32.to_nat(len), nr))) : {U32.to_nat(l2) == nr : Nat}} + +hx2 = {Equal.trans(Nat, xn, Nat.add(U32.to_nat(need), xn), nr, Equal.cong(Nat, Nat, kz => + Nat.add(kz, xn), 0n, U32.to_nat(need), Equal.sym(Nat, U32.to_nat(need), 0n, e_n0)), hx) : + {Nat.add(U32.to_nat(0), xn) == nr : Nat}} + +ih = {rg(pp, True{}, U32.is_zero(l2), False{}, False{}, filt, zred, zgrn, zlast, px, col, row, + ww, + need, cc, acc, aa, bb, l2, len, pl, plen, pbf, kk, xn, wp, mx, emx, ew, hrow, hfw, hbl, hx2, hk, + hw.end(nr, Nat.double(kk), pp, hw), ({==}, e_l2)) : {Png.enc.rgb.go(pp, True{}, + U32.is_zero(l2), False{}, False{}, filt, zred, zgrn, zlast, px, col, row, ww, need, cc, acc, aa, + bb, l2, len) == (fr(bs(rr, True{}, False{}, 0, l2, Png.enc.cap(l2), Png.enc.bf(l2)), cc, acc), + afold(rr, aa, bb)) : (U32 & List<&2, U32>) & U32}} + Equal.trans((U32 & List<&2, U32>) & U32, Png.enc.rgb.go(pp, True{}, U32.is_zero(l2), False{}, + False{}, filt, zred, zgrn, zlast, px, col, row, ww, need, cc, acc, aa, bb, l2, len), + (fr(bs(rr, True{}, False{}, 0, l2, Png.enc.cap(l2), Png.enc.bf(l2)), cc, acc), afold(rr, aa, + bb)), rg.val(False{}, rr, cc, acc, aa, bb, left, need, pl, plen, pbf), ih, + Equal.cong(List<&2, U32>, (U32 & List<&2, U32>) & U32, ys => (fr(ys, cc, acc), afold(rr, aa, + bb)), bs(rr, True{}, False{}, 0, l2, Png.enc.cap(l2), Png.enc.bf(l2)), bs(rr, + U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf), Equal.sym(List<&2, U32>, bs(rr, + U32.is_zero(need), U32.is_eq(need, 1), need, pl, plen, pbf), bs(rr, True{}, False{}, 0, l2, + Png.enc.cap(l2), Png.enc.bf(l2)), n0.bs(rr, need, pl, plen, pbf, l2, e_zn, e_pl, e_l2, hbe)))) + case False{}: + match filt: + case True{}: + match px: + case Nil{}: + (_e_d, (_e_s, (e_zn, _rest))) = hb + Empty.absurd({Png.enc.rgb.go(1n+pp, False{}, False{}, True{}, False{}, True{}, zred, + zgrn, zlast, [], col, row, ww, need, cc, acc, aa, bb, left, len) == rg.val(False{}, + cur(True{}, zred, zgrn, [], col, row, 1n+wp), cc, acc, aa, bb, left, need, pl, + plen, pbf) : (U32 & List<&2, U32>) & U32}, need.nil(need, xn, e_zn, hx)) + case +s <> +t: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, _hbe))))) = hb + +r2 = cur(False{}, True{}, False{}, s <> t, col, row, 1n+wp) + +e_R = {cur.filt(s, t, row, wp, Equal.trans(Nat, U32.to_nat(row), U32.to_nat(ww), + 1n+wp, Equal.cong(U32, Nat, uu => U32.to_nat(uu), row, ww, + hfw({==})), ew)) : {cur(True{}, zred, zgrn, s <> t, col, row, 1n+wp) == + 0 <> r2 : List<&2, U32>}} + rg.byte(pp, 0, cur(True{}, zred, zgrn, s <> t, col, row, 1n+wp), r2, need, pl, plen, + pbf, cc, acc, aa, bb, left, len, kk, xn, Png.enc.rgb.go(pp, False{}, False{}, True{}, + U32.is_zero((need - 1 : U32)), False{}, True{}, False{}, False{}, s <> t, col, row, + ww, (need - 1 : U32), Crc.crc.byte(cc, 0), 0 <> acc, Png.enc.add(aa, 0), + Png.enc.add(bb, Png.enc.add(aa, 0)), left, len), e_R, e_zn, hx, hw, e_pl, e_ll, + xx => yy => h1 => h2 => h3 => h4 => h5 => rg(pp, False{}, False{}, True{}, + U32.is_zero((need - 1 : U32)), False{}, True{}, False{}, False{}, s <> t, col, row, + ww, (need - 1 : U32), Crc.crc.byte(cc, 0), 0 <> acc, Png.enc.add(aa, 0), + Png.enc.add(bb, Png.enc.add(aa, 0)), left, len, (pl - 1 : U32), xx, yy, kk, xn, wp, + mx, emx, ew, hrow, ef => Empty.absurd({row == ww : U32}, U32L.false_true(ef)), + _e1 => e2 => _e3 => Empty.absurd({False{} == U32.is_eq(row, 1) : Bool}, + U32L.false_true(Equal.sym(Bool, True{}, False{}, e2))), h2, hk, h3, ({==}, ({==}, + ({==}, (h4, (h5, h1))))))) + case False{}: + match zred: + case True{}: + match zgrn: + case _gn: + match zlast: + case _ls: + match px: + case Nil{}: + (_e_d, (_e_s, (e_zn, _rest))) = hb + Empty.absurd({Png.enc.rgb.go(1n+pp, False{}, False{}, True{}, False{}, + False{}, True{}, _gn, _ls, [], col, row, ww, need, cc, acc, aa, bb, left, + len) == rg.val(False{}, cur(False{}, True{}, _gn, [], col, row, 1n+wp), + cc, acc, aa, bb, left, need, pl, plen, pbf) : (U32 & List<&2, + U32>) & U32}, need.nil(need, xn, e_zn, hx)) + case +s <> +t: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, _hbe))))) = hb + +r2 = cur(False{}, False{}, True{}, t, s, row, 1n+wp) + +e_R = {cur.red(s, t, row, 1n+wp, hrow) : {cur(False{}, True{}, _gn, + s <> t, col, row, 1n+wp) == Png.enc.r(s) <> r2 : List<&2, U32>}} + rg.byte(pp, Png.enc.r(s), cur(False{}, True{}, _gn, s <> t, col, row, + 1n+wp), r2, need, pl, plen, pbf, cc, acc, aa, bb, left, len, kk, xn, + Png.enc.rgb.go(pp, False{}, False{}, True{}, + U32.is_zero((need - 1 : U32)), False{}, False{}, True{}, False{}, t, s, + row, ww, (need - 1 : U32), Crc.crc.byte(cc, Png.enc.r(s)), + Png.enc.r(s) <> acc, Png.enc.add(aa, Png.enc.r(s)), Png.enc.add(bb, + Png.enc.add(aa, Png.enc.r(s))), left, len), e_R, e_zn, hx, hw, e_pl, + e_ll, xx => yy => h1 => h2 => h3 => h4 => h5 => rg(pp, False{}, + False{}, True{}, U32.is_zero((need - 1 : U32)), False{}, False{}, + True{}, False{}, t, s, row, ww, (need - 1 : U32), Crc.crc.byte(cc, + Png.enc.r(s)), Png.enc.r(s) <> acc, Png.enc.add(aa, Png.enc.r(s)), + Png.enc.add(bb, Png.enc.add(aa, Png.enc.r(s))), left, len, + (pl - 1 : U32), xx, yy, kk, xn, wp, mx, emx, ew, hrow, + ef => Empty.absurd({row == ww : U32}, U32L.false_true(ef)), + _e1 => _e2 => e3 => Empty.absurd({False{} == U32.is_eq(row, 1) : Bool}, + U32L.false_true(Equal.sym(Bool, True{}, False{}, e3))), h2, hk, h3, + ({==}, ({==}, ({==}, (h4, (h5, h1))))))) + case False{}: + match zgrn: + case True{}: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, _hbe))))) = hb + +r2 = cur(False{}, False{}, False{}, px, col, row, 1n+wp) + +e_R = {{==} : {cur(False{}, False{}, True{}, px, col, row, + 1n+wp) == Png.enc.g(col) <> r2 : List<&2, U32>}} + rg.byte(pp, Png.enc.g(col), cur(False{}, False{}, True{}, px, col, row, 1n+wp), r2, + need, pl, plen, pbf, cc, acc, aa, bb, left, len, kk, xn, Png.enc.rgb.go(pp, + False{}, False{}, True{}, U32.is_zero((need - 1 : U32)), False{}, False{}, + False{}, U32.is_eq(row, 1), px, col, row, ww, (need - 1 : U32), + Crc.crc.byte(cc, Png.enc.g(col)), Png.enc.g(col) <> acc, Png.enc.add(aa, + Png.enc.g(col)), Png.enc.add(bb, Png.enc.add(aa, Png.enc.g(col))), left, len), + e_R, e_zn, hx, hw, e_pl, e_ll, xx => yy => h1 => h2 => h3 => h4 => h5 => rg(pp, + False{}, False{}, True{}, U32.is_zero((need - 1 : U32)), False{}, False{}, + False{}, U32.is_eq(row, 1), px, col, row, ww, (need - 1 : U32), + Crc.crc.byte(cc, Png.enc.g(col)), Png.enc.g(col) <> acc, Png.enc.add(aa, + Png.enc.g(col)), Png.enc.add(bb, Png.enc.add(aa, Png.enc.g(col))), left, len, + (pl - 1 : U32), xx, yy, kk, xn, wp, mx, emx, ew, hrow, + ef => Empty.absurd({row == ww : U32}, U32L.false_true(ef)), + _e1 => _e2 => _e3 => {==}, h2, hk, h3, ({==}, ({==}, ({==}, (h4, (h5, h1))))))) + case False{}: + match zlast: + case True{}: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, _hbe))))) = hb + +r2 = cur(True{}, True{}, False{}, px, col, ww, 1n+wp) + +e_R = {cur.last(col, px, row, 1n+wp, hbl({==}, {==}, {==})) : {cur(False{}, + False{}, False{}, px, col, row, 1n+wp) == Png.enc.b(col) <> r2 : List<&2, + U32>}} + rg.byte(pp, Png.enc.b(col), cur(False{}, False{}, False{}, px, col, row, + 1n+wp), r2, need, pl, plen, pbf, cc, acc, aa, bb, left, len, kk, xn, + Png.enc.rgb.go(pp, False{}, False{}, True{}, U32.is_zero((need - 1 : U32)), + True{}, True{}, False{}, False{}, px, col, ww, ww, (need - 1 : U32), + Crc.crc.byte(cc, Png.enc.b(col)), Png.enc.b(col) <> acc, Png.enc.add(aa, + Png.enc.b(col)), Png.enc.add(bb, Png.enc.add(aa, Png.enc.b(col))), left, + len), e_R, e_zn, hx, hw, e_pl, e_ll, + xx => yy => h1 => h2 => h3 => h4 => h5 => rg(pp, False{}, False{}, True{}, + U32.is_zero((need - 1 : U32)), True{}, True{}, False{}, False{}, px, col, + ww, ww, (need - 1 : U32), Crc.crc.byte(cc, Png.enc.b(col)), + Png.enc.b(col) <> acc, Png.enc.add(aa, Png.enc.b(col)), Png.enc.add(bb, + Png.enc.add(aa, Png.enc.b(col))), left, len, (pl - 1 : U32), xx, yy, kk, + xn, wp, mx, emx, ew, Wp1.u32_nonzero(ww, wp, ew), _ef => {==}, + ef => Empty.absurd({True{} == False{} : Bool} -> {False{} == + False{} : Bool} -> {False{} == U32.is_eq(ww, 1) : Bool}, + U32L.false_true(Equal.sym(Bool, True{}, False{}, ef))), h2, hk, h3, ({==}, + ({==}, ({==}, (h4, (h5, h1))))))) + case False{}: + (_e_d, (_e_s, (+e_zn, (+e_pl, (+e_ll, _hbe))))) = hb + +r2 = cur(False{}, True{}, False{}, px, col, (row - 1 : U32), 1n+wp) + +e_R = {cur.blue(col, px, row, 1n+wp, hrow) : {cur(False{}, False{}, False{}, + px, col, row, 1n+wp) == Png.enc.b(col) <> r2 : List<&2, U32>}} + rg.byte(pp, Png.enc.b(col), cur(False{}, False{}, False{}, px, col, row, + 1n+wp), r2, need, pl, plen, pbf, cc, acc, aa, bb, left, len, kk, xn, + Png.enc.rgb.go(pp, False{}, False{}, True{}, U32.is_zero((need - 1 : U32)), + False{}, True{}, False{}, False{}, px, col, (row - 1 : U32), ww, + (need - 1 : U32), Crc.crc.byte(cc, Png.enc.b(col)), Png.enc.b(col) <> acc, + Png.enc.add(aa, Png.enc.b(col)), Png.enc.add(bb, Png.enc.add(aa, + Png.enc.b(col))), left, len), e_R, e_zn, hx, hw, e_pl, e_ll, + xx => yy => h1 => h2 => h3 => h4 => h5 => rg(pp, False{}, False{}, True{}, + U32.is_zero((need - 1 : U32)), False{}, True{}, False{}, False{}, px, col, + (row - 1 : U32), ww, (need - 1 : U32), Crc.crc.byte(cc, Png.enc.b(col)), + Png.enc.b(col) <> acc, Png.enc.add(aa, Png.enc.b(col)), Png.enc.add(bb, + Png.enc.add(aa, Png.enc.b(col))), left, len, (pl - 1 : U32), xx, yy, kk, + xn, wp, mx, emx, ew, two(row, Equal.sym(Bool, U32.is_zero(row), False{}, + hrow), hbl({==}, {==}, {==})), ef => Empty.absurd({(row - 1 : U32) == + ww : U32}, U32L.false_true(ef)), + _e1 => e2 => _e3 => Empty.absurd({False{} == U32.is_eq((row - 1 : U32), + 1) : Bool}, U32L.false_true(Equal.sym(Bool, True{}, False{}, e2))), h2, hk, + h3, ({==}, ({==}, ({==}, (h4, (h5, h1))))))) + +# ---- I. Adler-32 as enc.rgb.go adds it is Inf.adler.of ---- + +# The Adler modulus, 65521, is named pm below, for the same reason as mx. + +# enc.add.at with the modulus named +def addm(zz: Bool, +tt: U32, +pm: U32) -> U32: + match zz: + case True{}: + (tt - pm : U32) + case False{}: + tt + +# enc.add.at is addm at 65521 +def addm.eq( + zz: Bool, + +tt: U32, + +pm: U32, + +epm: {pm == 65521 : U32} +) -> {Png.enc.add.at(zz, tt) == addm(zz, tt, pm) : U32}: + match zz: + case True{}: + Equal.cong(U32, U32, uu => (tt - uu : U32), 65521, pm, Equal.sym(U32, pm, 65521, epm)) + case False{}: + {==} + +# at most and below cannot both hold the other way round +def le.lt.no( + +aa: Nat, + +bb: Nat, + +h1: {Nat.is_le(aa, bb) == True{} : Bool}, + +h2: {Nat.is_lt(bb, aa) == True{} : Bool} +) -> Empty: + lt.irr(aa, R.le_lt_trans(aa, bb, aa, h1, h2)) + +# t below 2 m is t mod m after one subtraction of m, or none: which one is t >= m's answer +def md.at( + zz: Bool, + qn: Nat, + +tt: U32, + +pm: U32, + +ez: {U32.is_ge(tt, pm) == zz : Bool}, + +e_t: {U32.to_nat(tt) == Nat.add(Nat.mul(qn, U32.to_nat(pm)), U32.to_nat(U32.mod(tt, pm))) : Nat}, + +l_r: {Nat.is_lt(U32.to_nat(U32.mod(tt, pm)), U32.to_nat(pm)) == True{} : Bool}, + +ht: {Nat.is_lt(U32.to_nat(tt), Nat.add(U32.to_nat(pm), U32.to_nat(pm))) == True{} : Bool} +) -> {addm(zz, tt, pm) == U32.mod(tt, pm) : U32}: + match zz qn: + case True{} 0n: + +mn = U32.to_nat(pm) + +rn = U32.to_nat(U32.mod(tt, pm)) + +hg = {ge.le(U32.to_nat(tt), mn, Equal.trans(Bool, Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), U32.is_ge(tt, pm), + True{}, Equal.sym(Bool, U32.is_ge(tt, pm), Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), u.ge(tt, pm)), ez)) : + {Nat.is_le(mn, U32.to_nat(tt)) == True{} : Bool}} + Empty.absurd({(tt - pm : U32) == U32.mod(tt, pm) : U32}, le.lt.no(mn, rn, R.le_rw_r(mn, U32.to_nat(tt), rn, e_t, + hg), l_r)) + case True{} 1n+qp: + match qp: + case 0n: + +mn = U32.to_nat(pm) + +rn = U32.to_nat(U32.mod(tt, pm)) + +hg = {ge.le(U32.to_nat(tt), mn, Equal.trans(Bool, Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), U32.is_ge(tt, pm), + True{}, Equal.sym(Bool, U32.is_ge(tt, pm), Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), u.ge(tt, pm)), ez)) : + {Nat.is_le(mn, U32.to_nat(tt)) == True{} : Bool}} + +e_t2 = {Equal.trans(Nat, U32.to_nat(tt), Nat.add(Nat.add(mn, 0n), rn), Nat.add(mn, rn), e_t, Equal.cong(Nat, + Nat, kz => Nat.add(kz, rn), Nat.add(mn, 0n), mn, R.add_zero(mn))) : {U32.to_nat(tt) == Nat.add(mn, rn) : + Nat}} + u32.same((tt - pm : U32), U32.mod(tt, pm), Equal.trans(Nat, U32.to_nat((tt - pm : U32)), + Nat.sub(U32.to_nat(tt), + mn), rn, R.u32_sub_nat(tt, pm, hg), Equal.trans(Nat, Nat.sub(U32.to_nat(tt), mn), Nat.sub(Nat.add(mn, rn), + mn), rn, Equal.cong(Nat, Nat, kz => Nat.sub(kz, mn), U32.to_nat(tt), Nat.add(mn, rn), e_t2), + R.sub_add_cancel(mn, rn)))) + case 1n+(+qq): + +mn = U32.to_nat(pm) + +rn = U32.to_nat(U32.mod(tt, pm)) + +yy = Nat.add(mn, Nat.mul(qq, mn)) + +h1 = {R.le_add_mono(mn, mn, yy, Wp2.nle_add(mn, Nat.mul(qq, mn))) : {Nat.is_le(Nat.add(mn, mn), Nat.add(mn, + yy)) == True{} : Bool}} + +h2 = {R.le_rw_r(Nat.add(mn, mn), Nat.add(Nat.add(mn, yy), rn), U32.to_nat(tt), Equal.sym(Nat, U32.to_nat(tt), + Nat.add(Nat.add(mn, yy), rn), e_t), R.le_trans(Nat.add(mn, mn), Nat.add(mn, yy), Nat.add(Nat.add(mn, yy), + rn), h1, Wp2.nle_add(Nat.add(mn, yy), rn))) : {Nat.is_le(Nat.add(mn, mn), U32.to_nat(tt)) == True{} : Bool}} + Empty.absurd({(tt - pm : U32) == U32.mod(tt, pm) : U32}, le.lt.no(Nat.add(mn, mn), U32.to_nat(tt), h2, ht)) + case False{} 0n: + u32.same(tt, U32.mod(tt, pm), e_t) + case False{} 1n+(+qp): + +mn = U32.to_nat(pm) + +rn = U32.to_nat(U32.mod(tt, pm)) + +yy = Nat.add(mn, Nat.mul(qp, mn)) + +hl = {nge.lt(U32.to_nat(tt), mn, Equal.trans(Bool, Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), U32.is_ge(tt, pm), + False{}, Equal.sym(Bool, U32.is_ge(tt, pm), Cmp.is_ge(Nat.cmp(U32.to_nat(tt), mn)), u.ge(tt, pm)), ez)) : + {Nat.is_lt(U32.to_nat(tt), mn) == True{} : Bool}} + +h2 = {R.le_rw_r(mn, Nat.add(yy, rn), U32.to_nat(tt), Equal.sym(Nat, U32.to_nat(tt), Nat.add(yy, rn), e_t), + R.le_trans(mn, yy, Nat.add(yy, rn), Wp2.nle_add(mn, Nat.mul(qp, mn)), Wp2.nle_add(yy, rn))) : {Nat.is_le(mn, + U32.to_nat(tt)) == True{} : Bool}} + Empty.absurd({tt == U32.mod(tt, pm) : U32}, le.lt.no(mn, U32.to_nat(tt), h2, hl)) + +# the modulus doubled is a U32's value +def pm.dbl( + +pm: U32, + +epm: {pm == 65521 : U32} +) -> {U32.to_nat(U32.add(pm, pm)) == Nat.add(U32.to_nat(pm), U32.to_nat(pm)) : Nat}: + add.cf(pm, pm, Equal.cong(U32, Bool, uu => cy32(uu, uu), pm, 65521, epm)) + +# U32.mod by the modulus: a quotient and a remainder below it +def pm.dvm(+tt: U32, +pm: U32, +epm: {pm == 65521 : U32}) -> dvm.ty(tt, pm): + +mn = U32.to_nat(pm) + +hm = {Equal.trans(Bool, Nat.is_lt(0n, mn), U32.is_lt(0, pm), True{}, Equal.sym(Bool, U32.is_lt(0, pm), Nat.is_lt(0n, + mn), R.u32_lt(0, pm)), Equal.cong(U32, Bool, uu => U32.is_lt(0, uu), pm, 65521, epm)) : {Nat.is_lt(0n, mn) == + True{} : + Bool}} + +h2 = {R.le_rw_r(Nat.add(mn, mn), Nat.add(mn, mn), U32.to_nat(U32.add(pm, pm)), Equal.sym(Nat, + U32.to_nat(U32.add(pm, pm)), Nat.add(mn, mn), pm.dbl(pm, epm)), R.le_refl(Nat.add(mn, mn))) : + {Nat.is_le(Nat.add(mn, mn), U32.to_nat(U32.add(pm, pm))) == True{} : Bool}} + dvm(tt, pm, U32.add(pm, pm), hm, h2) + +# the pair pm.dvm gives, taken apart, for one subtraction +def md.go( + +tt: U32, + +pm: U32, + pr: dvm.ty(tt, pm), + +ht: {Nat.is_lt(U32.to_nat(tt), Nat.add(U32.to_nat(pm), U32.to_nat(pm))) == True{} : Bool} +) -> {addm(U32.is_ge(tt, pm), tt, pm) == U32.mod(tt, pm) : U32}: + (+e_t, +l_r) = pr + md.at(U32.is_ge(tt, pm), U32.to_nat(U32.div(tt, pm)), tt, pm, {==}, e_t, l_r, ht) + +# enc.add.at of a sum below twice the modulus is the sum mod 65521 +def mod.once( + +tt: U32, + +pm: U32, + +epm: {pm == 65521 : U32}, + +ht: {Nat.is_lt(U32.to_nat(tt), Nat.add(U32.to_nat(pm), U32.to_nat(pm))) == True{} : Bool} +) -> {Png.enc.add.at(U32.is_ge(tt, 65521), tt) == (tt % 65521 : U32) : U32}: + +e65 = {Equal.sym(U32, pm, 65521, epm) : {65521 == pm : U32}} + Equal.trans(U32, Png.enc.add.at(U32.is_ge(tt, 65521), tt), addm(U32.is_ge(tt, pm), tt, pm), (tt % 65521 : U32), + Equal.trans(U32, Png.enc.add.at(U32.is_ge(tt, 65521), tt), Png.enc.add.at(U32.is_ge(tt, pm), tt), addm(U32.is_ge(tt, + pm), tt, pm), Equal.cong(U32, U32, uu => Png.enc.add.at(U32.is_ge(tt, uu), tt), 65521, pm, e65), + addm.eq(U32.is_ge(tt, pm), tt, pm, epm)), + Equal.trans(U32, addm(U32.is_ge(tt, pm), tt, pm), U32.mod(tt, pm), (tt % 65521 : U32), md.go(tt, pm, pm.dvm(tt, pm, + epm), ht), Equal.cong(U32, U32, uu => U32.mod(tt, uu), pm, 65521, epm))) + +# the remainder pm.dvm gives, read as mod 65521 +def mod.lt.go( + +tt: U32, + +pm: U32, + +epm: {pm == 65521 : U32}, + pr: dvm.ty(tt, pm) +) -> {Nat.is_lt(U32.to_nat((tt % 65521 : U32)), U32.to_nat(pm)) == True{} : Bool}: + (_e_t, +l_r) = pr + R.lt_rw_l(U32.to_nat(U32.mod(tt, pm)), U32.to_nat((tt % 65521 : U32)), U32.to_nat(pm), Equal.cong(U32, Nat, uu => + U32.to_nat(U32.mod(tt, uu)), pm, 65521, epm), l_r) + +# U32.mod by 65521 is below it +def mod.lt( + +tt: U32, + +pm: U32, + +epm: {pm == 65521 : U32} +) -> {Nat.is_lt(U32.to_nat((tt % 65521 : U32)), U32.to_nat(pm)) == True{} : Bool}: + mod.lt.go(tt, pm, epm, pm.dvm(tt, pm, epm)) + +# one Adler-32 add of values below the modulus is the add mod 65521 +def add.mod( + +ss: U32, + +xx: U32, + +pm: U32, + +epm: {pm == 65521 : U32}, + +hs: {Nat.is_lt(U32.to_nat(ss), U32.to_nat(pm)) == True{} : Bool}, + +hx: {Nat.is_lt(U32.to_nat(xx), U32.to_nat(pm)) == True{} : Bool} +) -> {Png.enc.add(ss, xx) == ((ss + xx : U32) % 65521 : U32) : U32}: + +mn = U32.to_nat(pm) + +sn = U32.to_nat(ss) + +xn = U32.to_nat(xx) + +h1 = {Equal.trans(Bool, Nat.is_le(1n+sn, mn), Nat.is_lt(sn, mn), True{}, Equal.sym(Bool, Nat.is_lt(sn, mn), + Nat.is_le(1n+sn, mn), R.lt_le_succ(sn, mn)), hs) : {Nat.is_le(1n+sn, mn) == True{} : Bool}} + +h2 = {R.le_add_both(1n+sn, mn, xn, mn, h1, R.lt_le(xn, mn, hx)) : {Nat.is_le(1n+Nat.add(sn, xn), Nat.add(mn, mn)) == + True{} : Bool}} + +h3 = {Equal.trans(Bool, Nat.is_lt(Nat.add(sn, xn), Nat.add(mn, mn)), Nat.is_le(1n+Nat.add(sn, xn), Nat.add(mn, mn)), + True{}, R.lt_le_succ(Nat.add(sn, xn), Nat.add(mn, mn)), h2) : {Nat.is_lt(Nat.add(sn, xn), Nat.add(mn, mn)) == + True{} : Bool}} + +e_s = {R.u32_add_below(ss, xx, U32.add(pm, pm), R.le_rw_r(Nat.add(sn, xn), Nat.add(mn, mn), U32.to_nat(U32.add(pm, + pm)), Equal.sym(Nat, U32.to_nat(U32.add(pm, pm)), Nat.add(mn, mn), pm.dbl(pm, epm)), R.lt_le(Nat.add(sn, xn), + Nat.add(mn, mn), h3))) : {U32.to_nat((ss + xx : U32)) == Nat.add(sn, xn) : Nat}} + mod.once((ss + xx : U32), pm, epm, R.lt_rw_l(Nat.add(sn, xn), U32.to_nat((ss + xx : U32)), Nat.add(mn, mn), + Equal.sym(Nat, U32.to_nat((ss + xx : U32)), Nat.add(sn, xn), e_s), h3)) + +# a byte is below the modulus +def byte.pm( + +xx: U32, + +pm: U32, + +epm: {pm == 65521 : U32}, + +hh: {U32.is_le(xx, 255) == True{} : Bool} +) -> {Nat.is_lt(U32.to_nat(xx), U32.to_nat(pm)) == True{} : Bool}: + R.le_lt_trans(U32.to_nat(xx), U32.to_nat(255), U32.to_nat(pm), Equal.trans(Bool, Nat.is_le(U32.to_nat(xx), + U32.to_nat(255)), U32.is_le(xx, 255), True{}, Equal.sym(Bool, U32.is_le(xx, 255), Nat.is_le(U32.to_nat(xx), + U32.to_nat(255)), R.u32_le(xx, 255)), hh), Equal.trans(Bool, Nat.is_lt(U32.to_nat(255), U32.to_nat(pm)), + U32.is_lt(255, pm), True{}, Equal.sym(Bool, U32.is_lt(255, pm), Nat.is_lt(U32.to_nat(255), U32.to_nat(pm)), + R.u32_lt(255, pm)), Equal.cong(U32, Bool, uu => U32.is_lt(255, uu), pm, 65521, epm))) + +# afold of bytes is Inf.adler's fold, from sums below the modulus +def ad.eq( + xs: List<&2, U32>, + +aa: U32, + +bb: U32, + +pm: U32, + +epm: {pm == 65521 : U32}, + +hby: {Laws.bytes.go(xs, True{}) == True{} : Bool}, + +ha: {Nat.is_lt(U32.to_nat(aa), U32.to_nat(pm)) == True{} : Bool}, + +hb: {Nat.is_lt(U32.to_nat(bb), U32.to_nat(pm)) == True{} : Bool} +) -> {afold(xs, aa, bb) == Inf.adler.fin(Inf.adler.go(xs, aa, bb)) : U32}: + match xs: + case Nil{}: + {==} + case +x <> +t: + +a2 = ((aa + x : U32) % 65521 : U32) + +b2 = ((bb + a2 : U32) % 65521 : U32) + +hxb = {Wp2.bytes.ok(t, U32.is_le(x, 255), hby) : {U32.is_le(x, 255) == True{} : Bool}} + +e_a = {add.mod(aa, x, pm, epm, ha, byte.pm(x, pm, epm, hxb)) : {Png.enc.add(aa, x) == a2 : U32}} + +l_a = {mod.lt((aa + x : U32), pm, epm) : {Nat.is_lt(U32.to_nat(a2), U32.to_nat(pm)) == True{} : Bool}} + +e_b = {Equal.trans(U32, Png.enc.add(bb, Png.enc.add(aa, x)), Png.enc.add(bb, a2), b2, Equal.cong(U32, U32, uu => + Png.enc.add(bb, uu), Png.enc.add(aa, x), a2, e_a), add.mod(bb, a2, pm, epm, hb, l_a)) : {Png.enc.add(bb, + Png.enc.add(aa, x)) == b2 : U32}} + +l_b = {mod.lt((bb + a2 : U32), pm, epm) : {Nat.is_lt(U32.to_nat(b2), U32.to_nat(pm)) == True{} : Bool}} + Equal.trans(U32, afold(t, Png.enc.add(aa, x), Png.enc.add(bb, Png.enc.add(aa, x))), afold(t, a2, b2), + Inf.adler.fin(Inf.adler.go(t, a2, b2)), + Equal.trans(U32, afold(t, Png.enc.add(aa, x), Png.enc.add(bb, Png.enc.add(aa, x))), afold(t, a2, + Png.enc.add(bb, Png.enc.add(aa, x))), afold(t, a2, b2), Equal.cong(U32, U32, uu => afold(t, uu, + Png.enc.add(bb, + Png.enc.add(aa, x))), Png.enc.add(aa, x), a2, e_a), Equal.cong(U32, U32, uu => afold(t, a2, uu), + Png.enc.add(bb, Png.enc.add(aa, x)), b2, e_b)), + ad.eq(t, a2, b2, pm, epm, Wp2.bytes.tail(t, U32.is_le(x, 255), hby), l_a, l_b)) + +# enc.rgb.go's Adler-32 of bytes is Inf.adler.of +def afold.adler( + +xs: List<&2, U32>, + +pm: U32, + +epm: {pm == 65521 : U32}, + +hby: {Laws.bytes(xs) == True{} : Bool} +) -> {afold(xs, 1, 0) == Inf.adler.of(xs) : U32}: + +lt1 = {Equal.trans(Bool, Nat.is_lt(U32.to_nat(1), U32.to_nat(pm)), U32.is_lt(1, pm), True{}, Equal.sym(Bool, + U32.is_lt(1, pm), Nat.is_lt(U32.to_nat(1), U32.to_nat(pm)), R.u32_lt(1, pm)), Equal.cong(U32, Bool, uu => + U32.is_lt(1, uu), pm, 65521, epm)) : {Nat.is_lt(U32.to_nat(1), U32.to_nat(pm)) == True{} : Bool}} + +lt0 = {Equal.trans(Bool, Nat.is_lt(U32.to_nat(0), U32.to_nat(pm)), U32.is_lt(0, pm), True{}, Equal.sym(Bool, + U32.is_lt(0, pm), Nat.is_lt(U32.to_nat(0), U32.to_nat(pm)), R.u32_lt(0, pm)), Equal.cong(U32, Bool, uu => + U32.is_lt(0, uu), pm, 65521, epm)) : {Nat.is_lt(U32.to_nat(0), U32.to_nat(pm)) == True{} : Bool}} + ad.eq(xs, 1, 0, pm, epm, hby, lt1, lt0) + +# ---- J. enc.wide.rgb after the signature and IHDR ---- + +# twice k, as a product +def mul2(kk: Nat) -> {Nat.mul(kk, 2n) == Nat.double(kk) : Nat}: + match kk: + case 0n: + {==} + case 1n+kp: + Equal.cong(Nat, Nat, xx => 2n+xx, Nat.mul(kp, 2n), Nat.double(kp), mul2(kp)) + +# a count above 65535 is not zero, once is_zero's answer is named +def big.nz.at( + zz: Bool, + +nn: U32, + +ez: {U32.is_zero(nn) == zz : Bool}, + +hbig: {U32.is_le(nn, 65535) == False{} : Bool} +) -> {U32.is_zero(nn) == False{} : Bool}: + match zz: + case False{}: + ez + case True{}: + +e0 = {U32L.ueq(nn, 0, ez) : {nn == 0 : U32}} + Empty.absurd({U32.is_zero(nn) == False{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, U32.is_le(nn, 65535), + True{}, Equal.sym(Bool, U32.is_le(nn, 65535), False{}, hbig), Equal.cong(U32, Bool, uu => U32.is_le(uu, 65535), + nn, 0, e0)))) + +# the fuel enc.wide.rgb.z gives enc.rgb.go: a step per raw byte and two per +# block, less one +def rgb.fuel( + +nn: U32, + +tu: U32, + +nr: Nat, + +hn: {U32.to_nat(nn) == nr : Nat}, + +nz: {U32.is_zero(nn) == False{} : Bool}, + +h_tu: {Nat.is_le(kzn(nr, nn), U32.to_nat(tu)) == True{} : Bool} +) -> {Nat.is_le(Nat.add(Nat.add(nr, 0n), Nat.double(U32.to_nat(Png.enc.nblk(nn)))), + 1n+U32.to_nat(((nn + (Png.enc.nblk(nn) * 2 : U32) : U32) - 1 : U32))) == True{} : Bool}: + +kb = Png.enc.nblk(nn) + +kn = U32.to_nat(kb) + +k2 = Nat.mul(kn, 2n) + +tn = U32.to_nat(tu) + +s1 = (nn + (kb * 2 : U32) : U32) + +h5 = {R.le_add_r(Nat.add(nr, Nat.mul(kn, 5n)), 6n, tn, h_tu) : {Nat.is_le(Nat.add(nr, Nat.mul(kn, 5n)), + tn) == True{} : + Bool}} + +h25 = {R.le_rw_r(k2, Nat.mul(5n, kn), Nat.mul(kn, 5n), R.mul_comm(5n, kn), R.le_rw_l(Nat.mul(2n, kn), k2, + Nat.mul(5n, kn), R.mul_comm(2n, kn), R.le_mul_mono(2n, 5n, kn, {==}))) : {Nat.is_le(k2, Nat.mul(kn, 5n)) == True{} : + Bool}} + +h2 = {R.le_trans(Nat.add(nr, k2), Nat.add(nr, Nat.mul(kn, 5n)), tn, R.le_add_mono(nr, k2, Nat.mul(kn, 5n), h25), + h5) : + {Nat.is_le(Nat.add(nr, k2), tn) == True{} : Bool}} + +e_m = {R.u32_mul_below(kb, 2, tu, R.le_add_l(nr, k2, tn, h2)) : {U32.to_nat((kb * 2 : U32)) == k2 : Nat}} + +e_s = {Equal.trans(Nat, U32.to_nat(s1), Nat.add(U32.to_nat(nn), U32.to_nat((kb * 2 : U32))), Nat.add(nr, k2), + R.u32_add_below(nn, (kb * 2 : U32), tu, R.le_rw_l(Nat.add(nr, k2), Nat.add(U32.to_nat(nn), U32.to_nat((kb * 2 : + U32))), tn, Equal.trans(Nat, Nat.add(nr, k2), Nat.add(U32.to_nat(nn), k2), Nat.add(U32.to_nat(nn), U32.to_nat((kb * + 2 : U32))), Equal.cong(Nat, Nat, xx => Nat.add(xx, k2), nr, U32.to_nat(nn), Equal.sym(Nat, U32.to_nat(nn), nr, hn)), + Equal.cong(Nat, Nat, xx => Nat.add(U32.to_nat(nn), xx), k2, U32.to_nat((kb * 2 : U32)), Equal.sym(Nat, + U32.to_nat((kb * 2 : U32)), k2, e_m))), h2)), Equal.trans(Nat, Nat.add(U32.to_nat(nn), U32.to_nat((kb * 2 : U32))), + Nat.add(nr, U32.to_nat((kb * 2 : U32))), Nat.add(nr, k2), Equal.cong(Nat, Nat, xx => Nat.add(xx, + U32.to_nat((kb * 2 : + U32))), U32.to_nat(nn), nr, hn), Equal.cong(Nat, Nat, xx => Nat.add(nr, xx), U32.to_nat((kb * 2 : U32)), k2, + e_m))) : + {U32.to_nat(s1) == Nat.add(nr, k2) : Nat}} + +e_n = {Equal.trans(Nat, U32.to_nat(nn), 1n+U32.to_nat((nn - 1 : U32)), 1n+U32.to_nat((nn - 1 : U32)), Wp1.u32_dec(nn, + nz), {==}) : {U32.to_nat(nn) == 1n+U32.to_nat((nn - 1 : U32)) : Nat}} + +nz1 = {Wp1.u32_nonzero(s1, Nat.add(U32.to_nat((nn - 1 : U32)), k2), Equal.trans(Nat, U32.to_nat(s1), Nat.add(nr, k2), + 1n+Nat.add(U32.to_nat((nn - 1 : U32)), k2), e_s, Equal.cong(Nat, Nat, xx => Nat.add(xx, k2), nr, 1n+U32.to_nat((nn - + 1 : U32)), Equal.trans(Nat, nr, U32.to_nat(nn), 1n+U32.to_nat((nn - 1 : U32)), Equal.sym(Nat, U32.to_nat(nn), nr, + hn), + e_n)))) : {U32.is_zero(s1) == False{} : Bool}} + +e_st = {Equal.sym(Nat, U32.to_nat(s1), 1n+U32.to_nat((s1 - 1 : U32)), Wp1.u32_dec(s1, nz1)) : {1n+U32.to_nat((s1 - 1 + : U32)) == U32.to_nat(s1) : Nat}} + +e_l = {Equal.trans(Nat, Nat.add(Nat.add(nr, 0n), Nat.double(kn)), Nat.add(nr, Nat.double(kn)), Nat.add(nr, k2), + Equal.cong(Nat, Nat, xx => Nat.add(xx, Nat.double(kn)), Nat.add(nr, 0n), nr, R.add_zero(nr)), Equal.cong(Nat, Nat, + xx => Nat.add(nr, xx), Nat.double(kn), k2, Equal.sym(Nat, k2, Nat.double(kn), mul2(kn)))) : + {Nat.add(Nat.add(nr, 0n), Nat.double(kn)) == Nat.add(nr, k2) : Nat}} + R.le_rw_r(Nat.add(Nat.add(nr, 0n), Nat.double(kn)), Nat.add(nr, k2), 1n+U32.to_nat((s1 - 1 : U32)), Equal.trans(Nat, + Nat.add(nr, k2), U32.to_nat(s1), 1n+U32.to_nat((s1 - 1 : U32)), Equal.sym(Nat, U32.to_nat(s1), Nat.add(nr, k2), + e_s), + Equal.sym(Nat, 1n+U32.to_nat((s1 - 1 : U32)), U32.to_nat(s1), e_st)), R.le_rw_l(Nat.add(nr, k2), + Nat.add(Nat.add(nr, 0n), Nat.double(kn)), Nat.add(nr, k2), Equal.sym(Nat, Nat.add(Nat.add(nr, 0n), Nat.double(kn)), + Nat.add(nr, k2), e_l), R.le_refl(Nat.add(nr, k2)))) + +# the scanlines of a picture 1 + w' wide +def sco(+px: List<&2, U32>, +wp: Nat, +op: Bool) -> List<&2, U32>: + W9.sc(px, 0n, 1n+wp, op) + +# the IDAT data length the encoder writes for the raw bytes xs +def ilen(+xs: List<&2, U32>) -> U32: + Png.enc.idat.ln(Png.enc.len(xs, 0)) + +# the zlib stream of xs in stored blocks of 65535 bytes +def zst(+xs: List<&2, U32>) -> List<&2, U32>: + Laws.zlib.stored(65535, xs) + +# enc.wide.rgb after the signature and IHDR: the IDAT chunk holding the zlib +# stream of the scanlines in stored blocks of 65535 bytes, then IEND +def rgb.tail( + +px: List<&2, U32>, + +ww: U32, + +wp: Nat, + +mx: U32, + +pm: U32, + +tu: U32, + +ie: List<&2, U32>, + +emx: {mx == 65535 : U32}, + +epm: {pm == 65521 : U32}, + +ew: {U32.to_nat(ww) == 1n+wp : Nat}, + +hn: {U32.to_nat(Png.enc.len(sco(px, wp, True{}), 0)) == List.length(&2, U32, sco(px, wp, True{})) : Nat}, + +hbig: {U32.is_le(Png.enc.len(sco(px, wp, True{}), 0), 65535) == False{} : Bool}, + +hby: {Laws.bytes(sco(px, wp, True{})) == True{} : Bool}, + +hl: {ilen(sco(px, wp, True{})) == Png.enc.len(zst(sco(px, wp, True{})), 0) : U32}, + +h_tu: {Nat.is_le(kzs(sco(px, wp, True{})), U32.to_nat(tu)) == True{} : Bool} +) -> {Png.enc.wide.rgb.go(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, + Png.enc.cat.go(Png.enc.be(Png.enc.idat.ln(Png.enc.len(W9.sc(px, 0n, 1n+wp, True{}), 0))), [])), px, ww, + Png.enc.len(W9.sc(px, 0n, 1n+wp, True{}), 0), ie) == Png.enc.cat(Png.enc.chunk(Png.tag.idat(), + Laws.zlib.stored(65535, W9.sc(px, 0n, 1n+wp, True{}))), ie) : List<&2, U32>}: + +raw = W9.sc(px, 0n, 1n+wp, True{}) + +nr = List.length(&2, U32, raw) + +nn = Png.enc.len(raw, 0) + +kn = U32.to_nat(Png.enc.nblk(nn)) + +ll = Png.enc.idat.ln(nn) + +aa = Png.enc.cat.go(Png.enc.be(ll), []) + +bi = Png.enc.be(Png.tag.idat()) + +c1 = Crc.crc.fold(bi, 4294967295) + +a1 = List.reverse.go(&2, U32, bi, aa) + +c2 = Crc.crc.fold([120, 1], c1) + +a2 = List.reverse.go(&2, U32, [120, 1], a1) + +steps = ((nn + (Png.enc.nblk(nn) * 2 : U32) : U32) - 1 : U32) + +sum = Inf.adler.of(raw) + +bb = bs(raw, True{}, False{}, 0, nn, Png.enc.cap(nn), Png.enc.bf(nn)) + +bl = Png.enc.blocks(65535, raw) + +e_b = {sim.blocks(raw, mx, emx, hn, hbig) : {bl == bb : List<&2, U32>}} + +zz = Laws.zlib.stored(65535, raw) + +hl2 = {Equal.trans(U32, ll, Png.enc.len(zz, 0), Png.enc.len(zb(bb, sum), 0), hl, Equal.cong(List<&2, U32>, U32, ys => + Png.enc.len(zb(ys, sum), 0), bl, bb, e_b)) : {ll == Png.enc.len(zb(bb, sum), 0) : U32}} + +nz = {big.nz.at(U32.is_zero(nn), nn, {==}, hbig) : {U32.is_zero(nn) == False{} : Bool}} + -go = Png.enc.rgb.go(U32.to_nat(steps), True{}, False{}, False{}, False{}, True{}, True{}, False{}, False{}, px, 0, + ww, + ww, 0, c2, a2, 1, 0, nn, 0) + +e_go = {rg(U32.to_nat(steps), True{}, False{}, False{}, False{}, True{}, True{}, False{}, False{}, px, 0, ww, ww, 0, + c2, + a2, 1, 0, nn, 0, nn, 0, 0, kn, nr, wp, mx, emx, ew, Wp1.u32_nonzero(ww, wp, ew), _ef => {==}, ef => + Empty.absurd({True{} == False{} : Bool} -> {False{} == False{} : Bool} -> {False{} == U32.is_eq(ww, 1) : Bool}, + U32L.false_true(Equal.sym(Bool, True{}, False{}, ef))), {==}, kok.rw(U32.to_nat(nn), nr, kn, U32.to_nat(mx), hn, + nb.k(nn, mx, emx)), rgb.fuel(nn, tu, nr, hn, nz, h_tu), (Equal.sym(Bool, U32.is_zero(nn), False{}, + nz), hn)) : {go == + (fr(bb, c2, a2), afold(raw, 1, 0)) : (U32 & List<&2, U32>) & U32}} + +e_fr = {Equal.trans(U32 & List<&2, U32>, fr(bb, c2, a2), fr(List.append(&2, U32, [120, 1], bb), c1, a1), fr(t0(bb), + 4294967295, aa), fr.app([120, 1], bb, c1, a1), fr.app(bi, List.append(&2, U32, [120, 1], bb), 4294967295, aa)) : + {fr(bb, c2, a2) == fr(t0(bb), 4294967295, aa) : U32 & List<&2, U32>}} + Equal.trans(List<&2, U32>, Png.enc.wide.rgb.go(Png.enc.crc.list(bi, 4294967295, aa), px, ww, nn, ie), + Png.enc.wide.rgb.go(fr(bi, 4294967295, aa), px, ww, nn, ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), ie), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.wide.rgb.go(pp, px, ww, nn, ie), Png.enc.crc.list(bi, + 4294967295, aa), fr(bi, 4294967295, aa), crcl(bi, 4294967295, aa)), + Equal.trans(List<&2, U32>, Png.enc.wide.rgb.z(Png.enc.crc.list([120, 1], c1, a1), px, ww, nn, ie), + Png.enc.wide.rgb.z(fr([120, 1], c1, a1), px, ww, nn, ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), ie), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.wide.rgb.z(pp, px, ww, nn, ie), + Png.enc.crc.list([120, 1], c1, a1), fr([120, 1], c1, a1), crcl([120, 1], c1, a1)), + Equal.trans(List<&2, U32>, Png.enc.wide.rgb.out(go, ie), Png.enc.wide.rgb.out((fr(bb, c2, a2), afold(raw, 1, 0)), + ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), ie), + Equal.cong((U32 & List<&2, U32>) & U32, List<&2, U32>, gg => Png.enc.wide.rgb.out(gg, ie), go, (fr(bb, c2, a2), + afold(raw, 1, 0)), e_go), + Equal.trans(List<&2, U32>, Png.enc.seal.adler(fr(bb, c2, a2), afold(raw, 1, 0), ie), + Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), ie), + Equal.trans(List<&2, U32>, Png.enc.seal.adler(fr(bb, c2, a2), afold(raw, 1, 0), ie), + Png.enc.seal.adler(fr(bb, c2, a2), sum, ie), Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, ie), + Equal.cong(U32, List<&2, U32>, uu => Png.enc.seal.adler(fr(bb, c2, a2), uu, ie), afold(raw, 1, 0), sum, + afold.adler(raw, pm, epm, hby)), + Equal.cong(U32 & List<&2, U32>, List<&2, U32>, pp => Png.enc.seal.adler(pp, sum, ie), fr(bb, c2, a2), + fr(t0(bb), 4294967295, aa), e_fr)), + Equal.trans(List<&2, U32>, Png.enc.seal.adler(fr(t0(bb), 4294967295, aa), sum, ie), + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(bb, sum)), ie), Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zz), + ie), tail.chunk(bb, sum, ll, ie, hl2), Equal.cong(List<&2, U32>, List<&2, U32>, ys => + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), zb(ys, sum)), ie), bb, bl, Equal.sym(List<&2, U32>, bl, bb, + e_b))))))) + +# ---- K. ancillary chunks in the chunk walker ---- + +# W9.st with the IDAT run's end named: done once a chunk after the IDAT +# chunks has closed the run +def sd( + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + +idat: List<&2, U32>, + +sid: Bool, + +done: Bool +) -> Png.Pg: + Png.Pg{ww, hh, 8, cc, 0, 0, 0, idat, Laws.spec.or.nil(plte), Laws.spec.or.nil(trns), True{}, + Maybe.is_some(&2, List<&2, U32>, plte), sid, done, Maybe.is_some(&2, List<&2, U32>, trns), False{}} + +# the walker's dispatch on a type that is none of the five it reads, with the +# ancillary bit set: the chunk closes the IDAT run +def anc.at( + z1: Bool, + z2: Bool, + z3: Bool, + z4: Bool, + z5: Bool, + an: Bool, + +sp: Bool, + +si: Bool, + +sdn: Bool, + +st: Bool, + +colour: U32, + +data: List<&2, U32>, + +pg: Png.Pg, + +e1: {z1 == False{} : Bool}, + +e2: {z2 == False{} : Bool}, + +e3: {z3 == False{} : Bool}, + +e4: {z4 == False{} : Bool}, + +e5: {z5 == False{} : Bool}, + +ea: {an == True{} : Bool} +) -> {Png.png.apply.at(False{}, z1, z2, z3, z4, z5, an, True{}, sp, si, sdn, st, colour, data, pg) == + Png.NMore{Png.png.seal(pg)} : Png.Nx}: + match z1 z2 z3 z4 z5 an: + case False{} False{} False{} False{} False{} True{}: + {==} + case True{} _z2 _z3 _z4 _z5 _an: + Empty.absurd({Png.png.apply.at(False{}, True{}, _z2, _z3, _z4, _z5, _an, True{}, sp, si, sdn, st, colour, data, + pg) + == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(Equal.sym(Bool, True{}, False{}, e1))) + case False{} True{} _z3 _z4 _z5 _an: + Empty.absurd({Png.png.apply.at(False{}, False{}, True{}, _z3, _z4, _z5, _an, True{}, sp, si, sdn, st, colour, + data, + pg) == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(Equal.sym(Bool, True{}, False{}, e2))) + case False{} False{} True{} _z4 _z5 _an: + Empty.absurd({Png.png.apply.at(False{}, False{}, False{}, True{}, _z4, _z5, _an, True{}, sp, si, sdn, st, colour, + data, pg) == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(Equal.sym(Bool, True{}, False{}, e3))) + case False{} False{} False{} True{} _z5 _an: + Empty.absurd({Png.png.apply.at(False{}, False{}, False{}, False{}, True{}, _z5, _an, True{}, sp, si, sdn, st, + colour, data, pg) == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(Equal.sym(Bool, True{}, False{}, + e4))) + case False{} False{} False{} False{} True{} _an: + Empty.absurd({Png.png.apply.at(False{}, False{}, False{}, False{}, False{}, True{}, _an, True{}, sp, si, sdn, st, + colour, data, pg) == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(Equal.sym(Bool, True{}, False{}, + e5))) + case False{} False{} False{} False{} False{} False{}: + Empty.absurd({Png.png.apply.at(False{}, False{}, False{}, False{}, False{}, False{}, False{}, True{}, sp, si, sdn, + st, colour, data, pg) == Png.NMore{Png.png.seal(pg)} : Png.Nx}, U32L.false_true(ea)) + +# a type with the ancillary bit is not one whose bit is clear, once is_eq's answer is named +def neq.at( + zz: Bool, + +tt: U32, + +kk: U32, + +ez: {U32.is_eq(tt, kk) == zz : Bool}, + +hb: {Laws.spec.anc.bit(tt) == True{} : Bool}, + +hk: {Laws.spec.anc.bit(kk) == False{} : Bool} +) -> {U32.is_eq(tt, kk) == False{} : Bool}: + match zz: + case False{}: + ez + case True{}: + Empty.absurd({U32.is_eq(tt, kk) == False{} : Bool}, U32L.false_true(Equal.trans(Bool, False{}, + Laws.spec.anc.bit(kk), True{}, Equal.sym(Bool, Laws.spec.anc.bit(kk), False{}, hk), Equal.trans(Bool, + Laws.spec.anc.bit(kk), Laws.spec.anc.bit(tt), True{}, Equal.cong(U32, Bool, uu => Laws.spec.anc.bit(uu), kk, tt, + Equal.sym(U32, tt, kk, U32L.ueq(tt, kk, ez))), hb)))) + +# a type with the ancillary bit is not one whose bit is clear +def neq( + +tt: U32, + +kk: U32, + +hb: {Laws.spec.anc.bit(tt) == True{} : Bool}, + +hk: {Laws.spec.anc.bit(kk) == False{} : Bool} +) -> {U32.is_eq(tt, kk) == False{} : Bool}: + neq.at(U32.is_eq(tt, kk), tt, kk, {==}, hb, hk) + +# an ancillary chunk that is not tRNS: the walker closes the IDAT run and goes on +def anc.step( + +tt: U32, + +data: List<&2, U32>, + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + +idat: List<&2, U32>, + +sid: Bool, + +done: Bool, + +hb: {Laws.spec.anc.bit(tt) == True{} : Bool}, + +ht: {U32.is_eq(tt, 1951551059) == False{} : Bool} +) -> {Png.png.apply(tt, data, sd(ww, hh, cc, plte, trns, idat, sid, done)) == Png.NMore{sd(ww, hh, cc, plte, trns, + idat, sid, Bool.or(sid, done))} : Png.Nx}: + anc.at(U32.is_eq(tt, Png.tag.ihdr()), U32.is_eq(tt, Png.tag.idat()), U32.is_eq(tt, Png.tag.iend()), U32.is_eq(tt, + Png.tag.plte()), U32.is_eq(tt, Png.tag.trns()), Png.tag.anc(tt), Maybe.is_some(&2, List<&2, U32>, plte), sid, done, + Maybe.is_some(&2, List<&2, U32>, trns), cc, data, sd(ww, hh, cc, plte, trns, idat, sid, done), neq(tt, + Png.tag.ihdr(), hb, {==}), neq(tt, Png.tag.idat(), hb, {==}), neq(tt, Png.tag.iend(), hb, {==}), neq(tt, + Png.tag.plte(), hb, {==}), ht, hb) + +# the IDAT run's end after ancillary chunks: closed once one follows an IDAT +def dn(cs: List<&2, Laws.Anc>, +sid: Bool, done: Bool) -> Bool: + match cs: + case Nil{}: + done + case _ac <> tl: + dn(tl, sid, Bool.or(sid, done)) + +# before any IDAT, ancillary chunks leave the run open +def dn.ff(cs: List<&2, Laws.Anc>) -> {dn(cs, False{}, False{}) == False{} : Bool}: + match cs: + case Nil{}: + {==} + case _ac <> tl: + dn.ff(tl) + +# ancillary chunks: the walker skips them, closing the IDAT run if one came +def anc.walk( + cs: List<&2, Laws.Anc>, + +rest: List<&2, U32>, + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + +idat: List<&2, U32>, + +sid: Bool, + +done: Bool, + hc: Laws.spec.ancs.ok(cs) +) -> {Png.png.go(Laws.spec.ancs(cs, rest), Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, idat, sid, + done)) == Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, idat, sid, dn(cs, sid, + done))) : Maybe<&2, Png.Pic>}: + match cs: + case Nil{}: + {==} + case Laws.Anc{+tt, +data} <> +tl: + (hb, (ht, (hf, htl))) = hc + +r1 = Laws.spec.ancs(tl, rest) + +pg = sd(ww, hh, cc, plte, trns, idat, sid, done) + +pg2 = sd(ww, hh, cc, plte, trns, idat, sid, Bool.or(sid, done)) + +ea = {Equal.trans(Png.Nx, Png.png.apply(Png.be.u32(W9.bt3(tt), W9.bt2(tt), W9.bt1(tt), W9.bt0(tt)), + List.reverse(&2, U32, data), pg), Png.png.apply(tt, List.reverse(&2, U32, data), pg), Png.NMore{pg2}, + Equal.cong(U32, Png.Nx, uu => Png.png.apply(uu, List.reverse(&2, U32, data), pg), Png.be.u32(W9.bt3(tt), + W9.bt2(tt), W9.bt1(tt), W9.bt0(tt)), tt, W9.be.back(tt)), anc.step(tt, List.reverse(&2, U32, data), ww, hh, cc, + plte, trns, idat, sid, done, hb, ht)) : {Png.png.apply(Png.be.u32(W9.bt3(tt), W9.bt2(tt), W9.bt1(tt), + W9.bt0(tt)), List.reverse(&2, U32, data), pg) == Png.NMore{pg2} : Png.Nx}} + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.chunk([W9.bt3(tt), W9.bt2(tt), W9.bt1(tt), W9.bt0(tt)], data, + r1), Png.ZLen{}, False{}, 0, 0, [], [], pg), Png.png.go(r1, Png.ZLen{}, False{}, 0, 0, [], [], pg2), + Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, idat, sid, dn(tl, sid, + Bool.or(sid, done)))), + W9.chunk.more(W9.bt3(tt), W9.bt2(tt), W9.bt1(tt), W9.bt0(tt), data, r1, pg, pg2, hf, ea), + anc.walk(tl, rest, ww, hh, cc, plte, trns, idat, sid, Bool.or(sid, done), htl)) + +# before any IDAT: ancillary chunks leave the state as it was +def anc.pre( + +cs: List<&2, Laws.Anc>, + +rest: List<&2, U32>, + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + hc: Laws.spec.ancs.ok(cs) +) -> {Png.png.go(Laws.spec.ancs(cs, rest), Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, [], + False{})) == Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, [], + False{})) : Maybe<&2, Png.Pic>}: + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.ancs(cs, rest), Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, + plte, trns, [], False{}, False{})), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, + [], + False{}, dn(cs, False{}, False{}))), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, + [], False{}, False{})), anc.walk(cs, rest, ww, hh, cc, plte, trns, [], False{}, False{}, hc), Equal.cong(Bool, + Maybe<&2, Png.Pic>, bb => Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, cc, plte, trns, [], + False{}, + bb)), dn(cs, False{}, False{}), False{}, dn.ff(cs))) + +# the IDAT chunks, ancillary chunks, then IEND: with done the IDAT data read +# so far, the image is the samples of done and the rest of the data, inflated +def walk.idats( + ds: List<&2, List<&2, U32>>, + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + +a3: List<&2, Laws.Anc>, + +done: List<&2, U32>, + +sid: Bool, + +h_nd: {W9.need(cc, plte) == True{} : Bool}, + hf: Laws.spec.fits.all(ds), + ha: Laws.spec.ancs.ok(a3) +) -> {Png.png.go(Laws.spec.idats(ds, Laws.spec.ancs(a3, Laws.spec.chunk([73, 69, 78, 68], [], []))), Png.ZLen{}, + False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, List.reverse(&2, U32, done), sid)) == + Png.png.samples(Inf.inflate(List.append(&2, U32, done, List.concat(&2, U32, ds))), ww, hh, cc, + Laws.spec.or.nil(plte), Laws.spec.or.nil(trns)) : Maybe<&2, Png.Pic>}: + match ds: + case Nil{}: + +pl = Laws.spec.or.nil(plte) + +tr = Laws.spec.or.nil(trns) + +ie = Laws.spec.chunk([73, 69, 78, 68], [], []) + +pg = sd(ww, hh, cc, plte, trns, List.reverse(&2, U32, done), sid, dn(a3, sid, False{})) + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.ancs(a3, ie), Png.ZLen{}, False{}, 0, 0, [], [], sd(ww, hh, + cc, + plte, trns, List.reverse(&2, U32, done), sid, False{})), Png.png.go(ie, Png.ZLen{}, False{}, 0, 0, [], [], pg), + Png.png.samples(Inf.inflate(List.append(&2, U32, done, [])), ww, hh, cc, pl, tr), + anc.walk(a3, ie, ww, hh, cc, plte, trns, List.reverse(&2, U32, done), sid, False{}, ha), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(ie, Png.ZLen{}, False{}, 0, 0, [], [], pg), + Png.png.finish(Png.pg.iend(pg)), Png.png.samples(Inf.inflate(List.append(&2, U32, done, [])), ww, hh, cc, pl, + tr), + W9.chunk.end(73, 69, 78, 68, [], pg, Png.pg.iend(pg), {==}, W9.iend.ok.true(W9.need(cc, plte), pg, h_nd)), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.finish(Png.pg.iend(pg)), Png.png.samples(Inf.inflate(done), ww, hh, + cc, + pl, tr), Png.png.samples(Inf.inflate(List.append(&2, U32, done, [])), ww, hh, cc, pl, tr), + Equal.cong(List<&2, U32>, Maybe<&2, Png.Pic>, xs => Png.png.samples(Inf.inflate(xs), ww, hh, cc, pl, tr), + List.reverse(&2, U32, List.reverse(&2, U32, done)), done, Wp2.rev_rev(done)), + Equal.cong(List<&2, U32>, Maybe<&2, Png.Pic>, xs => Png.png.samples(Inf.inflate(xs), ww, hh, cc, pl, tr), + done, List.append(&2, U32, done, []), Equal.sym(List<&2, U32>, List.append(&2, U32, done, []), done, + Wp2.app_nil(done)))))) + case +dd <> +tl: + (hd, htl) = hf + +pl = Laws.spec.or.nil(plte) + +tr = Laws.spec.or.nil(trns) + +rest = Laws.spec.idats(tl, Laws.spec.ancs(a3, Laws.spec.chunk([73, 69, 78, 68], [], []))) + +pg = W9.st(ww, hh, cc, plte, trns, List.reverse(&2, U32, done), sid) + +d2 = List.append(&2, U32, done, dd) + Equal.trans(Maybe<&2, Png.Pic>, + Png.png.go(Laws.spec.chunk([73, 68, 65, 84], dd, rest), Png.ZLen{}, False{}, 0, 0, [], [], pg), + Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, List.append(&2, U32, + List.reverse(&2, U32, dd), List.reverse(&2, U32, done)), True{})), + Png.png.samples(Inf.inflate(List.append(&2, U32, done, List.append(&2, U32, dd, List.concat(&2, U32, tl)))), ww, + hh, cc, pl, tr), + W9.chunk.more(73, 68, 65, 84, dd, rest, pg, W9.st(ww, hh, cc, plte, trns, List.append(&2, U32, List.reverse(&2, + U32, dd), List.reverse(&2, U32, done)), True{}), hd, W9.idat.step(ww, hh, cc, plte, trns, dd, List.reverse(&2, + U32, done), sid, h_nd)), + Equal.trans(Maybe<&2, Png.Pic>, + Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, List.append(&2, U32, + List.reverse(&2, U32, dd), List.reverse(&2, U32, done)), True{})), + Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, List.reverse(&2, U32, d2), + True{})), + Png.png.samples(Inf.inflate(List.append(&2, U32, done, List.append(&2, U32, dd, List.concat(&2, U32, tl)))), + ww, hh, cc, pl, tr), + Equal.cong(List<&2, U32>, Maybe<&2, Png.Pic>, xs => Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], + W9.st(ww, hh, cc, plte, trns, xs, True{})), List.append(&2, U32, List.reverse(&2, U32, dd), List.reverse(&2, + U32, done)), List.reverse(&2, U32, d2), W9.rev_app(done, dd)), + Equal.trans(Maybe<&2, Png.Pic>, + Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, List.reverse(&2, U32, d2), + True{})), + Png.png.samples(Inf.inflate(List.append(&2, U32, d2, List.concat(&2, U32, tl))), ww, hh, cc, pl, tr), + Png.png.samples(Inf.inflate(List.append(&2, U32, done, List.append(&2, U32, dd, List.concat(&2, U32, tl)))), + ww, hh, cc, pl, tr), + walk.idats(tl, ww, hh, cc, plte, trns, a3, d2, True{}, h_nd, htl, ha), + Equal.cong(List<&2, U32>, Maybe<&2, Png.Pic>, xs => Png.png.samples(Inf.inflate(xs), ww, hh, cc, pl, tr), + List.append(&2, U32, d2, List.concat(&2, U32, tl)), List.append(&2, U32, done, List.append(&2, U32, dd, + List.concat(&2, U32, tl))), Wp2.app_assoc(done, dd, List.concat(&2, U32, tl)))))) + +# the PLTE chunk after tRNS: its colour type allows it and its length is a palette's, so the state keeps its data +# beside tRNS's +def plte.late( + +ww: U32, + +hh: U32, + +cc: U32, + +pp: List<&2, U32>, + +trns: Maybe<&2, List<&2, U32>>, + h_pl: {Laws.spec.plte.ok(cc, Some{pp}) == True{} : Bool} +) -> {Png.png.apply(Png.be.u32(80, 76, 84, 69), List.reverse(&2, U32, pp), W9.st(ww, hh, cc, None{}, trns, [], + False{})) == Png.NMore{W9.st(ww, hh, cc, Some{pp}, trns, [], False{})} : Png.Nx}: + +ok = Bool.or(U32.is_eq(cc, 2), Bool.or(U32.is_eq(cc, 3), U32.is_eq(cc, 6))) + +rp = List.reverse(&2, U32, pp) + +e_len = {Wp2.len_rev(pp) : {List.length(&2, U32, rp) == List.length(&2, U32, pp) : Nat}} + +e_ok = {Equal.trans(Bool, Bool.and(ok, Png.png.mod3(rp)), Bool.and(ok, Laws.spec.pal.len(pp)), True{}, + Equal.cong(Nat, Bool, kk => Bool.and(ok, Bool.and(U32.is_gt(U32.from_nat(kk), 0), + Bool.and(U32.is_zero((U32.from_nat(kk) % 3 : U32)), U32.is_le(U32.from_nat(kk), 768)))), + List.length(&2, U32, rp), List.length(&2, U32, pp), e_len), h_pl) : + {Bool.and(ok, Png.png.mod3(rp)) == True{} : Bool}} + +pg0 = W9.st(ww, hh, cc, None{}, trns, [], False{}) + Equal.trans(Png.Nx, Png.png.allow(Bool.and(ok, Png.png.mod3(rp)), Png.pg.plte(pg0, List.reverse(&2, U32, rp))), + Png.NMore{Png.pg.plte(pg0, List.reverse(&2, U32, rp))}, + Png.NMore{W9.st(ww, hh, cc, Some{pp}, trns, [], False{})}, + W9.allow.true(Bool.and(ok, Png.png.mod3(rp)), Png.pg.plte(pg0, List.reverse(&2, U32, rp)), e_ok), + Equal.cong(List<&2, U32>, Png.Nx, xs => Png.NMore{Png.pg.plte(pg0, xs)}, List.reverse(&2, U32, rp), pp, + Wp2.rev_rev(pp))) + +# PLTE after tRNS, when there is one +def walk.plte.late( + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +trns: Maybe<&2, List<&2, U32>>, + +rest: List<&2, U32>, + h_pl: {Laws.spec.plte.ok(cc, plte) == True{} : Bool}, + hf: Laws.spec.fits.opt(plte) +) -> {Png.png.go(Laws.spec.chunk.opt([80, 76, 84, 69], plte, rest), Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, + cc, None{}, trns, [], False{})) == Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, + [], False{})) : Maybe<&2, Png.Pic>}: + match plte: + case None{}: + {==} + case Some{+pp}: + W9.chunk.more(80, 76, 84, 69, pp, rest, W9.st(ww, hh, cc, None{}, trns, [], False{}), W9.st(ww, hh, cc, Some{pp}, + trns, [], False{}), hf, plte.late(ww, hh, cc, pp, trns, h_pl)) + +# PLTE, a1 and tRNS, in either order: the state after them keeps both +def walk.mid( + late: Bool, + +ww: U32, + +hh: U32, + +cc: U32, + +plte: Maybe<&2, List<&2, U32>>, + +a1: List<&2, Laws.Anc>, + +trns: Maybe<&2, List<&2, U32>>, + +rest: List<&2, U32>, + h_pl: {Laws.spec.plte.ok(cc, plte) == True{} : Bool}, + h_tr: {Laws.spec.trns.ok(cc, plte, trns) == True{} : Bool}, + h_late: {Bool.or(Bool.not(late), Laws.spec.trns.ok(cc, None{}, trns)) == True{} : Bool}, + h_fp: Laws.spec.fits.opt(plte), + h_ft: Laws.spec.fits.opt(trns), + h_a1: Laws.spec.ancs.ok(a1) +) -> {Png.png.go(Laws.spec.mid(late, plte, a1, trns, rest), Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, + None{}, None{}, [], False{})) == Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], W9.st(ww, hh, cc, plte, trns, + [], False{})) : Maybe<&2, Png.Pic>}: + match late: + case False{}: + +r2 = Laws.spec.chunk.opt([116, 82, 78, 83], trns, rest) + +q1 = Laws.spec.ancs(a1, r2) + +s1 = W9.st(ww, hh, cc, plte, None{}, [], False{}) + +s2 = W9.st(ww, hh, cc, plte, trns, [], False{}) + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.chunk.opt([80, 76, 84, 69], plte, q1), Png.ZLen{}, False{}, + 0, 0, [], [], W9.st(ww, hh, cc, None{}, None{}, [], False{})), Png.png.go(q1, Png.ZLen{}, False{}, 0, 0, [], [], + s1), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], s2), + W9.walk.plte(ww, hh, cc, plte, q1, h_pl, h_fp), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(q1, Png.ZLen{}, False{}, 0, 0, [], [], s1), Png.png.go(r2, + Png.ZLen{}, False{}, 0, 0, [], [], s1), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], s2), + anc.pre(a1, r2, ww, hh, cc, plte, None{}, h_a1), + W9.walk.trns(ww, hh, cc, plte, trns, rest, h_tr, h_ft))) + case True{}: + +r2 = Laws.spec.chunk.opt([80, 76, 84, 69], plte, rest) + +q1 = Laws.spec.ancs(a1, r2) + +s1 = W9.st(ww, hh, cc, None{}, trns, [], False{}) + +s2 = W9.st(ww, hh, cc, plte, trns, [], False{}) + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.chunk.opt([116, 82, 78, 83], trns, q1), Png.ZLen{}, + False{}, 0, 0, [], [], W9.st(ww, hh, cc, None{}, None{}, [], False{})), Png.png.go(q1, Png.ZLen{}, False{}, 0, + 0, [], [], s1), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], s2), + W9.walk.trns(ww, hh, cc, None{}, trns, q1, h_late, h_ft), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(q1, Png.ZLen{}, False{}, 0, 0, [], [], s1), Png.png.go(r2, + Png.ZLen{}, False{}, 0, 0, [], [], s1), Png.png.go(rest, Png.ZLen{}, False{}, 0, 0, [], [], s2), + anc.pre(a1, r2, ww, hh, cc, None{}, trns, h_a1), + walk.plte.late(ww, hh, cc, plte, trns, rest, h_pl, h_fp))) + +# the chunks after the signature, with ancillary chunks after IHDR, between PLTE and tRNS (in either order), after +# them and after the IDAT chunks: the samples of the IDAT data, inflated +def walk.png( + +ww: U32, + +hh: U32, + +cc: U32, + +late: Bool, + +a0: List<&2, Laws.Anc>, + +plte: Maybe<&2, List<&2, U32>>, + +a1: List<&2, Laws.Anc>, + +trns: Maybe<&2, List<&2, U32>>, + +a2: List<&2, Laws.Anc>, + +idats: List<&2, List<&2, U32>>, + +a3: List<&2, Laws.Anc>, + +h_ww: {U32.is_gt(ww, 0) == True{} : Bool}, + +h_hh: {U32.is_gt(hh, 0) == True{} : Bool}, + +h_cc: {Laws.spec.colour.ok(cc) == True{} : Bool}, + +h_pl: {Laws.spec.plte.ok(cc, plte) == True{} : Bool}, + +h_tr: {Laws.spec.trns.ok(cc, plte, trns) == True{} : Bool}, + +h_late: {Bool.or(Bool.not(late), Laws.spec.trns.ok(cc, None{}, trns)) == True{} : Bool}, + h_fp: Laws.spec.fits.opt(plte), + h_ft: Laws.spec.fits.opt(trns), + h_fi: Laws.spec.fits.all(idats), + h_a0: Laws.spec.ancs.ok(a0), + h_a1: Laws.spec.ancs.ok(a1), + h_a2: Laws.spec.ancs.ok(a2), + h_a3: Laws.spec.ancs.ok(a3) +) -> {Png.decode(Laws.spec.chunk([73, 72, 68, 82], Laws.spec.ihdr.data(ww, hh, cc), Laws.spec.ancs(a0, + Laws.spec.mid(late, plte, a1, trns, Laws.spec.ancs(a2, Laws.spec.idats(idats, Laws.spec.ancs(a3, + Laws.spec.chunk([73, 69, 78, 68], [], [])))))))) == Png.png.samples(Inf.inflate(List.concat(&2, U32, idats)), ww, + hh, cc, Laws.spec.or.nil(plte), Laws.spec.or.nil(trns)) : Maybe<&2, Png.Pic>}: + +r3 = Laws.spec.idats(idats, Laws.spec.ancs(a3, Laws.spec.chunk([73, 69, 78, 68], [], []))) + +q2 = Laws.spec.ancs(a2, r3) + +r1 = Laws.spec.mid(late, plte, a1, trns, q2) + +q0 = Laws.spec.ancs(a0, r1) + +s0 = W9.st(ww, hh, cc, None{}, None{}, [], False{}) + +s2 = W9.st(ww, hh, cc, plte, trns, [], False{}) + +out = Png.png.samples(Inf.inflate(List.concat(&2, U32, idats)), ww, hh, cc, Laws.spec.or.nil(plte), + Laws.spec.or.nil(trns)) + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(Laws.spec.chunk([73, 72, 68, 82], Laws.spec.ihdr.data(ww, hh, cc), q0), + Png.ZLen{}, False{}, 0, 0, [], [], Png.pg.empty()), Png.png.go(q0, Png.ZLen{}, False{}, 0, 0, [], [], s0), out, + W9.chunk.more(73, 72, 68, 82, Laws.spec.ihdr.data(ww, hh, cc), q0, Png.pg.empty(), s0, {==}, W9.ihdr.step(ww, hh, + cc, h_ww, h_hh, h_cc)), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(q0, Png.ZLen{}, False{}, 0, 0, [], [], s0), Png.png.go(r1, Png.ZLen{}, + False{}, 0, 0, [], [], s0), out, anc.pre(a0, r1, ww, hh, cc, None{}, None{}, h_a0), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(r1, Png.ZLen{}, False{}, 0, 0, [], [], s0), Png.png.go(q2, Png.ZLen{}, + False{}, 0, 0, [], [], s2), out, walk.mid(late, ww, hh, cc, plte, a1, trns, q2, h_pl, h_tr, h_late, h_fp, h_ft, + h_a1), + Equal.trans(Maybe<&2, Png.Pic>, Png.png.go(q2, Png.ZLen{}, False{}, 0, 0, [], [], s2), Png.png.go(r3, + Png.ZLen{}, False{}, 0, 0, [], [], s2), out, anc.pre(a2, r3, ww, hh, cc, plte, trns, h_a2), + walk.idats(idats, ww, hh, cc, plte, trns, a3, [], False{}, W9.need.ok(cc, plte, h_pl), h_fi, h_a3))))) + +# ---- L. the sizes from 8 w h, and the wide encodings as the one-block layout ---- + +# p a is at most p b when a is at most b +def ml.le( + pp: Nat, + +aa: Nat, + +bb: Nat, + +hh: {Nat.is_le(aa, bb) == True{} : Bool} +) -> {Nat.is_le(Nat.mul(pp, aa), Nat.mul(pp, bb)) == True{} : Bool}: + match pp: + case 0n: + {==} + case 1n+(+qq): + R.le_add_both(aa, bb, Nat.mul(qq, aa), Nat.mul(qq, bb), hh, ml.le(qq, aa, bb, hh)) + +# the encoder's channel count is at most 4 +def nch4(op: Bool) -> {Nat.is_le(Laws.spec.nch(op), 4n) == True{} : Bool}: + match op: + case True{}: + {==} + case False{}: + {==} + +# one row: 1 + w k is at most 5 w, for w above 0 and k at most 4 +def row5( + +wp: Nat, + +kk: Nat, + +hk: {Nat.is_le(kk, 4n) == True{} : Bool} +) -> {Nat.is_le(1n+Nat.mul(1n+wp, kk), Nat.mul(1n+wp, 5n)) == True{} : Bool}: + R.le_add_both(1n+kk, 5n, Nat.mul(wp, kk), Nat.mul(wp, 5n), hk, ml.le(wp, kk, 5n, R.le_trans(kk, 4n, 5n, hk, {==}))) + +# the scanline bytes are at most 5 w h +def scan5( + hn: Nat, + +wp: Nat, + +kk: Nat, + +hk: {Nat.is_le(kk, 4n) == True{} : Bool} +) -> {Nat.is_le(Nat.mul(hn, 1n+Nat.mul(1n+wp, kk)), Nat.mul(Nat.mul(hn, 1n+wp), 5n)) == True{} : Bool}: + match hn: + case 0n: + {==} + case 1n+(+hp): + +bb = {1n+Nat.mul(1n+wp, kk) : Nat} + +wn = {1n+wp : Nat} + R.le_rw_r(Nat.add(bb, Nat.mul(hp, bb)), Nat.add(Nat.mul(wn, 5n), Nat.mul(Nat.mul(hp, wn), 5n)), + Nat.mul(Nat.add(wn, Nat.mul(hp, wn)), 5n), Equal.sym(Nat, Nat.mul(Nat.add(wn, Nat.mul(hp, wn)), 5n), + Nat.add(Nat.mul(wn, 5n), Nat.mul(Nat.mul(hp, wn), 5n)), R.mul_add(wn, Nat.mul(hp, wn), 5n)), + R.le_add_both(bb, Nat.mul(wn, 5n), Nat.mul(hp, bb), Nat.mul(Nat.mul(hp, wn), 5n), row5(wp, kk, hk), + scan5(hp, wp, kk, hk))) + +# 5 x below 5 y: x below y +def lt.c5( + xx: Nat, + yy: Nat, + +hh: {Nat.is_lt(Nat.mul(xx, 5n), Nat.mul(yy, 5n)) == True{} : Bool} +) -> {Nat.is_lt(xx, yy) == True{} : Bool}: + match xx yy: + case 0n 0n: + hh + case 0n 1n+_yp: + {==} + case 1n+_xp 0n: + hh + case 1n+xp 1n+yp: + lt.c5(xp, yp, hh) + +# (5 p) 5 is 25 p +def m25(pp: Nat) -> {Nat.mul(Nat.mul(pp, 5n), 5n) == Nat.mul(pp, 25n) : Nat}: + match pp: + case 0n: + {==} + case 1n+(+qq): + Equal.cong(Nat, Nat, xx => Nat.add(25n, xx), Nat.mul(Nat.mul(qq, 5n), 5n), Nat.mul(qq, 25n), m25(qq)) + +# 5 a is at most 8 a +def five.eight(+aa: Nat) -> {Nat.is_le(Nat.mul(aa, 5n), Nat.mul(8n, aa)) == True{} : Bool}: + R.le_rw_r(Nat.mul(aa, 5n), Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), Nat.mul(8n, aa), Equal.sym(Nat, + Nat.mul(Nat.add(5n, 3n), aa), Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), R.mul_add(5n, 3n, aa)), + R.le_rw_l(Nat.mul(5n, aa), Nat.mul(aa, 5n), Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), R.mul_comm(5n, aa), + R.le_add_more(Nat.mul(5n, aa), Nat.mul(5n, aa), Nat.mul(3n, aa), R.le_refl(Nat.mul(5n, aa))))) + +# 11 + y is at most 3 a when y is at most a and 6 is too +def eleven( + +yy: Nat, + +aa: Nat, + +hy: {Nat.is_le(yy, aa) == True{} : Bool}, + +h6: {Nat.is_le(6n, aa) == True{} : Bool} +) -> {Nat.is_le(11n+yy, Nat.mul(3n, aa)) == True{} : Bool}: + +a0 = Nat.add(aa, 0n) + +h6z = {R.le_rw_r(6n, aa, a0, Equal.sym(Nat, a0, aa, R.add_zero(aa)), h6) : {Nat.is_le(6n, a0) == True{} : Bool}} + +h11 = {R.le_trans(11n, 12n, Nat.add(aa, a0), {==}, R.le_add_both(6n, aa, 6n, a0, h6, h6z)) : {Nat.is_le(11n, + Nat.add(aa, a0)) == True{} : Bool}} + R.le_rw_l(Nat.add(yy, 11n), 11n+yy, Nat.mul(3n, aa), R.add_comm(yy, 11n), R.le_add_both(yy, aa, 11n, Nat.add(aa, a0), + hy, h11)) + +# the stored blocks' bytes fit in 8 a: n + 5 k + 6 is at most 8 a when n is at most 5 a, k blocks of m bytes hold +# the n bytes, m is at least 25 and n is above m +def kb( + kk: Nat, + +xn: Nat, + +mn: Nat, + +aa: Nat, + hk: kok(xn, kk, mn), + +h5: {Nat.is_le(xn, Nat.mul(aa, 5n)) == True{} : Bool}, + +hm: {Nat.is_le(25n, mn) == True{} : Bool}, + +hb: {Nat.is_lt(mn, xn) == True{} : Bool} +) -> {Nat.is_le(Nat.add(Nat.add(xn, Nat.mul(kk, 5n)), 6n), Nat.mul(8n, aa)) == True{} : Bool}: + match kk: + case 0n: + Empty.absurd({Nat.is_le(Nat.add(Nat.add(xn, 0n), 6n), Nat.mul(8n, aa)) == True{} : Bool}, + U32L.false_true(Equal.trans(Bool, False{}, Nat.is_lt(mn, 0n), True{}, R.lt_zero_false(mn), R.lt_rw_r(mn, xn, 0n, + hk, hb)))) + case 1n+(+kp): + (hl, _hr) = hk + +yy = Nat.mul(kp, 5n) + +x5 = Nat.mul(aa, 5n) + +y25 = {R.le_rw_l(Nat.mul(kp, 25n), Nat.mul(yy, 5n), Nat.mul(kp, mn), Equal.sym(Nat, Nat.mul(yy, 5n), + Nat.mul(kp, 25n), m25(kp)), ml.le(kp, 25n, mn, hm)) : {Nat.is_le(Nat.mul(yy, 5n), Nat.mul(kp, mn)) == True{} : + Bool}} + +ya = {lt.c5(yy, aa, R.le_lt_trans(Nat.mul(yy, 5n), Nat.mul(kp, mn), x5, y25, R.lt_le_trans(Nat.mul(kp, mn), xn, + x5, hl, h5))) : {Nat.is_lt(yy, aa) == True{} : Bool}} + +a5 = {lt.c5(5n, aa, R.le_lt_trans(25n, mn, x5, hm, R.lt_le_trans(mn, xn, x5, hb, h5))) : {Nat.is_lt(5n, aa) == + True{} : Bool}} + +h6 = {Equal.trans(Bool, Nat.is_le(6n, aa), Nat.is_lt(5n, aa), True{}, Equal.sym(Bool, Nat.is_lt(5n, aa), + Nat.is_le(6n, aa), R.lt_le_succ(5n, aa)), a5) : {Nat.is_le(6n, aa) == True{} : Bool}} + +h5c = {R.le_rw_r(xn, x5, Nat.mul(5n, aa), R.mul_comm(aa, 5n), h5) : {Nat.is_le(xn, Nat.mul(5n, aa)) == True{} : + Bool}} + +lhs = Nat.add(Nat.add(xn, 5n+yy), 6n) + +e1 = {Equal.trans(Nat, lhs, Nat.add(xn, 5n+Nat.add(yy, 6n)), Nat.add(xn, 11n+yy), R.add_assoc(xn, 5n+yy, 6n), + Equal.cong(Nat, Nat, zz => Nat.add(xn, Nat.add(5n, zz)), Nat.add(yy, 6n), Nat.add(6n, yy), R.add_comm(yy, + 6n))) : {lhs == Nat.add(xn, 11n+yy) : Nat}} + R.le_rw_r(lhs, Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), Nat.mul(8n, aa), Equal.sym(Nat, Nat.mul(Nat.add(5n, 3n), + aa), Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), R.mul_add(5n, 3n, aa)), + R.le_rw_l(Nat.add(xn, 11n+yy), lhs, Nat.add(Nat.mul(5n, aa), Nat.mul(3n, aa)), Equal.sym(Nat, lhs, + Nat.add(xn, 11n+yy), e1), R.le_add_both(xn, Nat.mul(5n, aa), 11n+yy, Nat.mul(3n, aa), h5c, eleven(yy, aa, + R.lt_le(yy, aa, ya), h6)))) + +# the IDAT data of the wide encodings fits in t, a word whose value is 8 a, when the scanlines are at most 5 a bytes +def tu.ok( + +raw: List<&2, U32>, + +mx: U32, + +tu: U32, + +aa: Nat, + +emx: {mx == 65535 : U32}, + +hn: {U32.to_nat(Png.enc.len(raw, 0)) == List.length(&2, U32, raw) : Nat}, + +hbig: {U32.is_le(Png.enc.len(raw, 0), 65535) == False{} : Bool}, + +h5: {Nat.is_le(List.length(&2, U32, raw), Nat.mul(aa, 5n)) == True{} : Bool}, + +et: {U32.to_nat(tu) == Nat.mul(8n, aa) : Nat} +) -> {Nat.is_le(Nat.add(Nat.add(List.length(&2, U32, raw), Nat.mul(U32.to_nat(Png.enc.nblk(Png.enc.len(raw, 0))), 5n)), + 6n), U32.to_nat(tu)) == True{} : Bool}: + +ll = Png.enc.len(raw, 0) + +xn = U32.to_nat(ll) + +ln = List.length(&2, U32, raw) + +mn = U32.to_nat(mx) + +kn = U32.to_nat(Png.enc.nblk(ll)) + +hm = {Equal.trans(Bool, Nat.is_le(25n, mn), U32.is_le(25, mx), True{}, Equal.sym(Bool, U32.is_le(25, mx), + Nat.is_le(25n, mn), R.u32_le(25, mx)), Equal.cong(U32, Bool, uu => U32.is_le(25, uu), mx, 65535, emx)) : + {Nat.is_le(25n, mn) == True{} : Bool}} + +hle = {Equal.trans(Bool, Nat.is_le(xn, mn), U32.is_le(ll, mx), False{}, Equal.sym(Bool, U32.is_le(ll, mx), + Nat.is_le(xn, mn), R.u32_le(ll, mx)), Equal.trans(Bool, U32.is_le(ll, mx), U32.is_le(ll, 65535), False{}, + Equal.cong(U32, Bool, uu => U32.is_le(ll, uu), mx, 65535, emx), hbig)) : {Nat.is_le(xn, mn) == False{} : Bool}} + +h5x = {R.le_rw_l(ln, xn, Nat.mul(aa, 5n), Equal.sym(Nat, xn, ln, hn), h5) : {Nat.is_le(xn, Nat.mul(aa, 5n)) == + True{} : Bool}} + +k1 = {kb(kn, xn, mn, aa, nb.k(ll, mx, emx), h5x, hm, nle.lt(xn, mn, hle)) : {Nat.is_le(Nat.add(Nat.add(xn, + Nat.mul(kn, 5n)), 6n), Nat.mul(8n, aa)) == True{} : Bool}} + R.le_rw_r(Nat.add(Nat.add(ln, Nat.mul(kn, 5n)), 6n), Nat.mul(8n, aa), U32.to_nat(tu), Equal.sym(Nat, U32.to_nat(tu), + Nat.mul(8n, aa), et), R.le_rw_l(Nat.add(Nat.add(xn, Nat.mul(kn, 5n)), 6n), Nat.add(Nat.add(ln, Nat.mul(kn, 5n)), + 6n), Nat.mul(8n, aa), Equal.cong(Nat, Nat, zz => Nat.add(Nat.add(zz, Nat.mul(kn, 5n)), 6n), xn, ln, hn), k1)) + +# w k, taken in a word, does not wrap when h (1 + w k) is at most a word's value +def nb.em( + +ww: U32, + +op: Bool, + +wp: Nat, + +hp: Nat, + +cap: U32, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +h_n: {Nat.is_le(Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))), U32.to_nat(cap)) == True{} : Bool} +) -> {U32.to_nat(U32.mul(ww, Png.enc.nch(op))) == Nat.mul(U32.to_nat(ww), U32.to_nat(Png.enc.nch(op))) : Nat}: + +kk = Laws.spec.nch(op) + +wk = Nat.mul(1n+wp, kk) + +cn = U32.to_nat(cap) + +nc = Png.enc.nch(op) + +h_y = {R.le_add_r(1n+wk, Nat.mul(hp, 1n+wk), cn, h_n) : {Nat.is_le(1n+wk, cn) == True{} : Bool}} + +h_wk = {R.le_trans(wk, 1n+wk, cn, W9.le.succ(wk), h_y) : {Nat.is_le(wk, cn) == True{} : Bool}} + +e_m1n = {Equal.trans(Nat, Nat.mul(U32.to_nat(ww), U32.to_nat(nc)), Nat.mul(1n+wp, U32.to_nat(nc)), wk, + Equal.cong(Nat, Nat, xx => Nat.mul(xx, U32.to_nat(nc)), U32.to_nat(ww), 1n+wp, e_w), Equal.cong(Nat, Nat, xx => + Nat.mul(1n+wp, xx), U32.to_nat(nc), kk, W9.nch.val(op))) : {Nat.mul(U32.to_nat(ww), U32.to_nat(nc)) == wk : Nat}} + R.u32_mul_below(ww, nc, cap, R.le_rw_l(wk, Nat.mul(U32.to_nat(ww), U32.to_nat(nc)), cn, Equal.sym(Nat, + Nat.mul(U32.to_nat(ww), U32.to_nat(nc)), wk, e_m1n), h_wk)) + +# the filtered byte count, taken in a word, is h (1 + w k) when that is at most a word's value +def nb.val( + +ww: U32, + +hh: U32, + +op: Bool, + +wp: Nat, + +hp: Nat, + +cap: U32, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_h: {U32.to_nat(hh) == 1n+hp : Nat}, + +h_n: {Nat.is_le(Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))), U32.to_nat(cap)) == True{} : Bool} +) -> {U32.to_nat(Png.enc.nbytes(op, ww, hh)) == Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))) : Nat}: + +kk = Laws.spec.nch(op) + +wk = Nat.mul(1n+wp, kk) + +nn = Nat.mul(1n+hp, 1n+wk) + +cn = U32.to_nat(cap) + +nc = Png.enc.nch(op) + +m1 = U32.mul(ww, nc) + +h_y = {R.le_add_r(1n+wk, Nat.mul(hp, 1n+wk), cn, h_n) : {Nat.is_le(1n+wk, cn) == True{} : Bool}} + +e_m1n = {Equal.trans(Nat, Nat.mul(U32.to_nat(ww), U32.to_nat(nc)), Nat.mul(1n+wp, U32.to_nat(nc)), wk, + Equal.cong(Nat, Nat, xx => Nat.mul(xx, U32.to_nat(nc)), U32.to_nat(ww), 1n+wp, e_w), Equal.cong(Nat, Nat, xx => + Nat.mul(1n+wp, xx), U32.to_nat(nc), kk, W9.nch.val(op))) : {Nat.mul(U32.to_nat(ww), U32.to_nat(nc)) == wk : Nat}} + +t1 = {Equal.trans(Nat, U32.to_nat(m1), Nat.mul(U32.to_nat(ww), U32.to_nat(nc)), wk, nb.em(ww, op, wp, hp, cap, e_w, + h_n), e_m1n) : {U32.to_nat(m1) == wk : Nat}} + +x1 = U32.add(m1, 1) + +e_x = {Equal.trans(Nat, Nat.add(U32.to_nat(m1), 1n), Nat.add(wk, 1n), 1n+wk, Equal.cong(Nat, Nat, xx => Nat.add(xx, + 1n), U32.to_nat(m1), wk, t1), W9.add.one.nat(wk)) : {Nat.add(U32.to_nat(m1), 1n) == 1n+wk : Nat}} + +t2 = {Equal.trans(Nat, U32.to_nat(x1), Nat.add(U32.to_nat(m1), 1n), 1n+wk, R.u32_add_below(m1, 1, cap, + R.le_rw_l(1n+wk, Nat.add(U32.to_nat(m1), 1n), cn, Equal.sym(Nat, Nat.add(U32.to_nat(m1), 1n), 1n+wk, e_x), h_y)), + e_x) : {U32.to_nat(x1) == 1n+wk : Nat}} + +e_3 = {Equal.trans(Nat, Nat.mul(U32.to_nat(hh), U32.to_nat(x1)), Nat.mul(1n+hp, U32.to_nat(x1)), nn, Equal.cong(Nat, + Nat, xx => Nat.mul(xx, U32.to_nat(x1)), U32.to_nat(hh), 1n+hp, e_h), Equal.cong(Nat, Nat, xx => Nat.mul(1n+hp, xx), + U32.to_nat(x1), 1n+wk, t2)) : {Nat.mul(U32.to_nat(hh), U32.to_nat(x1)) == nn : Nat}} + Equal.trans(Nat, U32.to_nat(U32.mul(hh, x1)), Nat.mul(U32.to_nat(hh), U32.to_nat(x1)), nn, R.u32_mul_below(hh, x1, + cap, R.le_rw_l(nn, Nat.mul(U32.to_nat(hh), U32.to_nat(x1)), cn, Equal.sym(Nat, Nat.mul(U32.to_nat(hh), + U32.to_nat(x1)), nn, e_3), h_n)), e_3) + +# not at most: above +def gt.nle(cc: Cmp, +hh: {Cmp.is_le(cc) == False{} : Bool}) -> {Cmp.is_gt(cc) == True{} : Bool}: + match cc: + case LT{}: + Empty.absurd({Cmp.is_gt(LT{}) == True{} : Bool}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hh))) + case EQ{}: + Empty.absurd({Cmp.is_gt(EQ{}) == True{} : Bool}, U32L.false_true(Equal.sym(Bool, True{}, False{}, hh))) + case GT{}: + {==} + + +# the raw scanline list and its count, as the encoder builds them: the scanlines and their length +def raw.pair( + +px: List<&2, U32>, + +ww: U32, + +wp: Nat, + +op: Bool, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat} +) -> {Png.enc.raw(px, 0n, U32.to_nat(ww), op, [], 0) == (W9.sc(px, 0n, 1n+wp, op), Png.enc.len(W9.sc(px, 0n, 1n+wp, + op), 0)) : List<&2, U32> & U32}: + +ss = W9.sc(px, 0n, 1n+wp, op) + +ll = Png.enc.len(ss, 0) + +r0 = List.reverse(&2, U32, List.reverse.go(&2, U32, ss, [])) + +m0 = W9.cnt(px, 0n, 1n+wp, op, 0) + +ls = List.length(&2, U32, ss) + +e_m0 = {Equal.trans(U32, m0, U32.from_nat(ls), ll, W9.cnt.word(px, 0n, 1n+wp, op, 0n), Equal.sym(U32, ll, + U32.from_nat(ls), W9.len.word(ss))) : {m0 == ll : U32}} + Equal.trans(List<&2, U32> & U32, Png.enc.raw(px, 0n, U32.to_nat(ww), op, [], 0), Png.enc.raw(px, 0n, 1n+wp, op, [], + 0), (ss, ll), + Equal.cong(Nat, List<&2, U32> & U32, kn => Png.enc.raw(px, 0n, kn, op, [], 0), U32.to_nat(ww), 1n+wp, e_w), + Equal.trans(List<&2, U32> & U32, Png.enc.raw(px, 0n, 1n+wp, op, [], 0), (r0, m0), (ss, ll), W9.raw.eq(px, 0n, + 1n+wp, op, [], 0), + Equal.trans(List<&2, U32> & U32, (r0, m0), (ss, m0), (ss, ll), + Equal.cong(List<&2, U32>, List<&2, U32> & U32, xs => (xs, m0), r0, ss, Wp2.rev_rev(ss)), + Equal.cong(U32, List<&2, U32> & U32, uu => (ss, uu), m0, ll, e_m0)))) + +# colour type 6 past one stored block: the wide layout is the one-block layout around the stored blocks of 65535 +# bytes. Both sides are stated stuck on a flag, since the IHDR's CRC over a symbolic width and height is slow to +# normalise; the flags are rewritten to True and False only for the step that unfolds the layouts +def core.f( + +zt: Bool, + +zf: Bool, + +ww: U32, + +hh: U32, + +ss: List<&2, U32>, + +et: {zt == True{} : Bool}, + +ef: {zf == False{} : Bool}, + +hn: {U32.to_nat(Png.enc.len(ss, 0)) == List.length(&2, U32, ss) : Nat}, + +hbig: {U32.is_le(Png.enc.len(ss, 0), 65535) == False{} : Bool}, + +hl: {Png.enc.idat.ln(Png.enc.len(ss, 0)) == Png.enc.len(Laws.zlib.stored(65535, ss), 0) : U32} +) -> {Png.enc.seal.at(zt, ww, hh, False{}, ss, Png.enc.len(ss, 0)) == Png.enc.seal.at(zf, ww, hh, False{}, ss, + Png.enc.len(ss, 0)) : List<&2, U32>}: + %Equal.sym(Bool, zt, True{}, et) : {Png.enc.seal.at(_, ww, hh, False{}, ss, Png.enc.len(ss, 0)) == Png.enc.seal.at(zf, + ww, hh, False{}, ss, Png.enc.len(ss, 0)) : List<&2, U32>} + %Equal.sym(Bool, zf, False{}, ef) : {Png.enc.seal.at(True{}, ww, hh, False{}, ss, Png.enc.len(ss, 0)) == + Png.enc.seal.at(_, ww, hh, False{}, ss, Png.enc.len(ss, 0)) : List<&2, U32>} + Equal.cong(List<&2, U32>, List<&2, U32>, tl => Png.enc.cat(Png.enc.sig(), Png.enc.cat(Png.enc.chunk(Png.tag.ihdr(), + Png.enc.ihdr(ww, hh, Png.enc.ct(False{}))), tl)), + Png.enc.seal.body(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, + Png.enc.cat.go(Png.enc.be(Png.enc.idat.ln(Png.enc.len(ss, 0))), [])), ss, Png.enc.len(ss, 0), Inf.adler.of(ss), + Png.enc.chunk(Png.tag.iend(), [])), + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), Laws.zlib.stored(65535, ss)), Png.enc.chunk(Png.tag.iend(), [])), + wide.tail(ss, 65535, Png.enc.chunk(Png.tag.iend(), []), {==}, hn, hbig, hl)) + +# colour type 2 past one stored block, written from the samples: the one-block layout around the stored blocks of +# 65535 bytes, stated stuck on flags as core.f is +def core.t( + +zo: Bool, + +zf: Bool, + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +wp: Nat, + +tu: U32, + +eo: {zo == True{} : Bool}, + +ef: {zf == False{} : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +hn: {U32.to_nat(Png.enc.len(sco(px, wp, True{}), 0)) == List.length(&2, U32, sco(px, wp, True{})) : Nat}, + +hbig: {U32.is_le(Png.enc.len(sco(px, wp, True{}), 0), 65535) == False{} : Bool}, + +hby: {Laws.bytes(sco(px, wp, True{})) == True{} : Bool}, + +h_tu: {Nat.is_le(kzs(sco(px, wp, True{})), U32.to_nat(tu)) == True{} : Bool} +) -> {Png.enc.paint.wide(zo, ww, hh, px, Png.enc.len(W9.sc(px, 0n, 1n+wp, True{}), 0)) == Png.enc.seal.at(zf, ww, hh, + zo, W9.sc(px, 0n, 1n+wp, True{}), Png.enc.len(W9.sc(px, 0n, 1n+wp, True{}), 0)) : List<&2, U32>}: + +ss = W9.sc(px, 0n, 1n+wp, True{}) + +ll = Png.enc.len(ss, 0) + +ie = Png.enc.chunk(Png.tag.iend(), []) + +hl = idat.len(ss, 65535, tu, {==}, hn, hbig, h_tu) + %Equal.sym(Bool, zo, True{}, eo) : {Png.enc.paint.wide(_, ww, hh, px, ll) == Png.enc.seal.at(zf, ww, hh, _, ss, ll) : + List<&2, U32>} + %Equal.sym(Bool, zf, False{}, ef) : {Png.enc.paint.wide(True{}, ww, hh, px, ll) == Png.enc.seal.at(_, ww, hh, True{}, + ss, ll) : List<&2, U32>} + Equal.cong(List<&2, U32>, List<&2, U32>, tl => Png.enc.cat(Png.enc.sig(), Png.enc.cat(Png.enc.chunk(Png.tag.ihdr(), + Png.enc.ihdr(ww, hh, Png.enc.ct(True{}))), tl)), + Png.enc.wide.rgb.go(Png.enc.crc.list(Png.enc.be(Png.tag.idat()), 4294967295, + Png.enc.cat.go(Png.enc.be(Png.enc.idat.ln(ll)), [])), px, ww, ll, ie), + Png.enc.cat(Png.enc.chunk(Png.tag.idat(), Laws.zlib.stored(65535, ss)), ie), + rgb.tail(px, ww, wp, 65535, 65521, tu, ie, {==}, {==}, e_w, hn, hbig, hby, hl, h_tu)) + +# the wide encodings, colour type 2 from the samples and colour type 6 from the raw list, are the one-block layout +# around the stored blocks of 65535 bytes. The layout is stated stuck on zf, which is False, and on zo, which is op +def wide.op( + op: Bool, + +zo: Bool, + +zf: Bool, + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +wp: Nat, + +nb: U32, + +tu: U32, + +eo: {zo == op : Bool}, + +ef: {zf == False{} : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_nb: {nb == Png.enc.len(W9.sc(px, 0n, 1n+wp, op), 0) : U32}, + +hn: {U32.to_nat(Png.enc.len(sco(px, wp, op), 0)) == List.length(&2, U32, sco(px, wp, op)) : Nat}, + +hbig: {U32.is_le(Png.enc.len(sco(px, wp, op), 0), 65535) == False{} : Bool}, + +hby: {Laws.bytes(sco(px, wp, op)) == True{} : Bool}, + +h_tu: {Nat.is_le(kzs(sco(px, wp, op)), U32.to_nat(tu)) == True{} : Bool} +) -> {Png.enc.paint.wide(zo, ww, hh, px, nb) == Png.enc.seal.at(zf, ww, hh, zo, W9.sc(px, 0n, 1n+wp, op), + Png.enc.len(W9.sc(px, 0n, 1n+wp, op), 0)) : List<&2, U32>}: + match op: + case True{}: + +ss = W9.sc(px, 0n, 1n+wp, True{}) + +ll = Png.enc.len(ss, 0) + Equal.trans(List<&2, U32>, Png.enc.paint.wide(zo, ww, hh, px, nb), Png.enc.paint.wide(zo, ww, hh, px, ll), + Png.enc.seal.at(zf, ww, hh, zo, ss, ll), + Equal.cong(U32, List<&2, U32>, uu => Png.enc.paint.wide(zo, ww, hh, px, uu), nb, ll, e_nb), + core.t(zo, zf, ww, hh, px, wp, tu, eo, ef, e_w, hn, hbig, hby, h_tu)) + case False{}: + +ss = W9.sc(px, 0n, 1n+wp, False{}) + +ll = Png.enc.len(ss, 0) + +hl = idat.len(ss, 65535, tu, {==}, hn, hbig, h_tu) + +hg = {gt.nle(U32.cmp(ll, 65535), hbig) : {U32.is_gt(ll, 65535) == True{} : Bool}} + %Equal.sym(Bool, zo, False{}, eo) : {Png.enc.paint.wide(_, ww, hh, px, nb) == Png.enc.seal.at(zf, ww, hh, _, ss, + ll) : List<&2, U32>} + Equal.trans(List<&2, U32>, Png.enc.paint.wide(False{}, ww, hh, px, nb), Png.enc.seal(ww, hh, False{}, (ss, ll)), + Png.enc.seal.at(zf, ww, hh, False{}, ss, ll), + Equal.cong(List<&2, U32> & U32, List<&2, U32>, pr => Png.enc.seal(ww, hh, False{}, pr), Png.enc.raw(px, 0n, + U32.to_nat(ww), False{}, [], 0), (ss, ll), raw.pair(px, ww, wp, False{}, e_w)), + core.f(U32.is_gt(ll, 65535), zf, ww, hh, ss, hg, ef, hn, hbig, hl)) + +# whether every sample is opaque, as the encoder asks +def opq(+px: List<&2, U32>) -> Bool: + Png.enc.opaque(px, True{}) + +# the scanline byte count h (1 + w k), for 1 + h' rows of 1 + w' pixels of the encoder's channels +def snn(+hp: Nat, +wp: Nat, op: Bool) -> Nat: + Nat.mul(1n+hp, 1n+Nat.mul(1n+wp, Laws.spec.nch(op))) + +# a picture past one stored block: its encoding is the one-block layout around the stored blocks of 65535 bytes, +# the layout stated stuck on zf, which is False +def wide.rt( + +zf: Bool, + +ww: U32, + +hh: U32, + +px: List<&2, U32>, + +wp: Nat, + +tu: U32, + +ef: {zf == False{} : Bool}, + e_good: {Png.enc.good(ww, hh, px) == True{} : Bool}, + +e_w: {U32.to_nat(ww) == 1n+wp : Nat}, + +e_nb: {Png.enc.nbytes(opq(px), ww, hh) == Png.enc.len(sco(px, wp, opq(px)), 0) : U32}, + +hn: {U32.to_nat(Png.enc.len(sco(px, wp, opq(px)), 0)) == List.length(&2, U32, sco(px, wp, opq(px))) : Nat}, + +hbig: {U32.is_le(Png.enc.len(sco(px, wp, opq(px)), 0), 65535) == False{} : Bool}, + +hby: {Laws.bytes(sco(px, wp, opq(px))) == True{} : Bool}, + +h_tu: {Nat.is_le(kzs(sco(px, wp, opq(px))), U32.to_nat(tu)) == True{} : Bool} +) -> {Png.enc.open(Png.enc.good(ww, hh, px), ww, hh, px) == Some{Png.enc.seal.at(zf, ww, hh, Png.enc.opaque(px, + True{}), W9.sc(px, 0n, 1n+wp, Png.enc.opaque(px, True{})), Png.enc.len(W9.sc(px, 0n, 1n+wp, Png.enc.opaque(px, + True{})), 0))} : Maybe<&2, List<&2, U32>>}: + +op = Png.enc.opaque(px, True{}) + +nb = Png.enc.nbytes(op, ww, hh) + +ss = W9.sc(px, 0n, 1n+wp, op) + +ll = Png.enc.len(ss, 0) + +so = Png.enc.seal.at(zf, ww, hh, op, ss, ll) + +hg = {Equal.trans(Bool, U32.is_gt(nb, 65535), U32.is_gt(ll, 65535), True{}, Equal.cong(U32, Bool, uu => + U32.is_gt(uu, 65535), nb, ll, e_nb), gt.nle(U32.cmp(ll, 65535), hbig)) : {U32.is_gt(nb, 65535) == True{} : Bool}} + Equal.trans(Maybe<&2, List<&2, U32>>, Png.enc.open(Png.enc.good(ww, hh, px), ww, hh, px), Png.enc.open(True{}, ww, + hh, px), Some{so}, + Equal.cong(Bool, Maybe<&2, List<&2, U32>>, zb => Png.enc.open(zb, ww, hh, px), Png.enc.good(ww, hh, px), True{}, + e_good), + Equal.trans(Maybe<&2, List<&2, U32>>, Png.enc.open(True{}, ww, hh, px), Some{Png.enc.paint.at(True{}, ww, hh, op, + px, nb)}, Some{so}, + Equal.cong(Bool, Maybe<&2, List<&2, U32>>, zb => Some{Png.enc.paint.at(zb, ww, hh, op, px, nb)}, U32.is_gt(nb, + 65535), True{}, hg), + Equal.cong(List<&2, U32>, Maybe<&2, List<&2, U32>>, xs => Some{xs}, Png.enc.paint.wide(op, ww, hh, px, nb), so, + wide.op(op, op, zf, ww, hh, px, wp, nb, tu, {==}, ef, e_w, e_nb, hn, hbig, hby, h_tu))))