参考文档 / ZLIB × LEAN
crc32.c.lean
此页源文件快照 ·
722536179905e22d · 对应提交的原文 ↗本页保留 Lean 源码及行号。点击行号可定位语句,点击 import 中的模块名可查看依赖。
1import ZlibF.Zlib.«crc32.c»
2import ZlibF.Spec.Format.Checksum
3
4/-!
5# crc32.c: safety and correctness
6
7Every function of `crc32.c.lean` has a safety theorem (`*_safe`: no fault, the heap is unchanged, so every heap
8invariant and frame is preserved; preconditions explicit) and a correctness theorem (`*_correct`: the value, against
9`ZlibF.Spec.crc32Update` / `ZlibF.Spec.crc32` and `ZlibF.Spec.crcBit`, which do not mention the translation).
10
11The heap hypothesis `StaticsOK` says that the static tables `crc_table` and `x2n_table` hold the contents `crc32.h`
12fixes (`Zlib.Crc32.initStatics`). The other tables (`crc_braid_table`, `crc_big_table`, `crc_braid_big_table`) are
13constants of the translation.
14
15Method. A CRC register step is linear over GF(2) (`crcBit_xor`). `pw m` is `m` steps of `ZlibF.Spec.crcBit`
16(multiplication by `x^m`). From the table contents (checked by kernel evaluation, `decide +kernel`, no axioms):
17`crc_table[n] = pw 8 n` (`crc_table_eq`), `crc_braid_table[j][n] = pw (8 * (40 - j)) n` (`braid_eq`), and the
18squaring chain of `x2n_table` with `x^(2^32) = x` (`Xt_sq`, `Xt_wrap`). The word step of `crc_word` is a linear map of
1964-bit words that agrees with the closed form `Psi` on the 64 unit vectors (`Phi_eq_Psi`), hence everywhere. The braided
20loop keeps the invariant `Val c0 .. c4 = pw 320 R` where `R` is the register of the bytes consumed so far
21(`loopBlk_ok`); the final chain of `crc_word` calls then equals `rbFrom R ow 40` (`chain_aff`, `chain_zero`,
22`val_step`). `multmodp` is multiplication in the algebra of registers (`mulp`): bilinear, associative
23(`mulp_assoc`), with `mulp (x^m) v = pw m v` (`mulp_pw_left'`).
24
25Not covered: the big-endian helpers (`byte_swap`, `blockBig`, `crc_word_big`, dead at run time on the target): only
26`crc_word_big_safe`; `byte_swap` is a total pure function with no correctness theorem; `blockBig` has none.
27-/
28
29namespace Zlib.Crc32.Proofs
30open C
31open ZlibF.Spec (crcBit crcPoly)
32
33/-! ### Bit lemmas -/
34
35theorem u32_ext {x y : UInt32} (h : ∀ i, i < 32 → x.toBitVec.getLsbD i = y.toBitVec.getLsbD i) : x = y :=
36 UInt32.eq_of_toBitVec_eq (BitVec.eq_of_getLsbD_eq h)
37
38theorem u64_ext {x y : UInt64} (h : ∀ i, i < 64 → x.toBitVec.getLsbD i = y.toBitVec.getLsbD i) : x = y :=
39 UInt64.eq_of_toBitVec_eq (BitVec.eq_of_getLsbD_eq h)
40
41theorem and_xor32 (a b m : UInt32) : (a ^^^ b) &&& m = (a &&& m) ^^^ (b &&& m) := by
42 apply u32_ext; intro i _
43 simp only [UInt32.toBitVec_and, UInt32.toBitVec_xor, BitVec.getLsbD_and, BitVec.getLsbD_xor]
44 cases a.toBitVec.getLsbD i <;> cases b.toBitVec.getLsbD i <;> cases m.toBitVec.getLsbD i <;> rfl
45
46theorem and_xor64 (a b m : UInt64) : (a ^^^ b) &&& m = (a &&& m) ^^^ (b &&& m) := by
47 apply u64_ext; intro i _
48 simp only [UInt64.toBitVec_and, UInt64.toBitVec_xor, BitVec.getLsbD_and, BitVec.getLsbD_xor]
49 cases a.toBitVec.getLsbD i <;> cases b.toBitVec.getLsbD i <;> cases m.toBitVec.getLsbD i <;> rfl
50
51theorem and1_cases (x : UInt32) : x &&& 1 = 0 ∨ x &&& 1 = 1 := by
52 have h : (x &&& 1).toNat = x.toNat % 2 := by
53 rw [UInt32.toNat_and]; exact Nat.and_one_is_mod _
54 rcases (by omega : x.toNat % 2 = 0 ∨ x.toNat % 2 = 1) with h0 | h0
55 · left; apply UInt32.toNat_inj.mp; simp [h, h0]
56 · right; apply UInt32.toNat_inj.mp; simp [h, h0]
57
58theorem xor_cancel32 (a b : UInt32) : a ^^^ (a ^^^ b) = b := by
59 rw [← UInt32.xor_assoc, UInt32.xor_self, UInt32.zero_xor]
60theorem xor_cancel64 (a b : UInt64) : a ^^^ (a ^^^ b) = b := by
61 rw [← UInt64.xor_assoc, UInt64.xor_self, UInt64.zero_xor]
62theorem xor_pp32 (a b p : UInt32) : (a ^^^ p) ^^^ (b ^^^ p) = a ^^^ b := by
63 apply u32_ext; intro i _
64 simp only [UInt32.toBitVec_xor, BitVec.getLsbD_xor]
65 cases a.toBitVec.getLsbD i <;> cases b.toBitVec.getLsbD i <;> cases p.toBitVec.getLsbD i <;> rfl
66
67theorem crcBit_xor (x y : UInt32) : crcBit (x ^^^ y) = crcBit x ^^^ crcBit y := by
68 unfold crcBit
69 have e : (x ^^^ y) &&& 1 = (x &&& 1) ^^^ (y &&& 1) := and_xor32 _ _ _
70 rcases and1_cases x with hx | hx <;> rcases and1_cases y with hy | hy <;>
71 simp [e, hx, hy, UInt32.shiftRight_xor, xor_pp32, xor_cancel32]
72 · ac_rfl
73 · ac_rfl
74
75theorem crcBit_zero : crcBit 0 = 0 := by decide
76
77/-- `m` register steps of the CRC (multiplication by `x^m` in the bit-reflected representation). -/
78def pw : Nat → UInt32 → UInt32
79 | 0, v => v
80 | m + 1, v => pw m (ZlibF.Spec.crcBit v)
81
82theorem pw_zero_n (v : UInt32) : pw 0 v = v := rfl
83theorem pw_succ (m : Nat) (v : UInt32) : pw (m + 1) v = pw m (ZlibF.Spec.crcBit v) := rfl
84theorem pw_succ' (m : Nat) (v : UInt32) : pw (m + 1) v = ZlibF.Spec.crcBit (pw m v) := by
85 induction m generalizing v with
86 | zero => rfl
87 | succ m ih => rw [pw_succ, ih, pw_succ]
88theorem pw_add (m n : Nat) (v : UInt32) : pw (m + n) v = pw m (pw n v) := by
89 induction n generalizing v with
90 | zero => rfl
91 | succ n ih => rw [← Nat.add_assoc, pw_succ, ih, pw_succ]
92theorem pw_xor (m : Nat) (x y : UInt32) : pw m (x ^^^ y) = pw m x ^^^ pw m y := by
93 induction m generalizing x y with
94 | zero => rfl
95 | succ m ih => rw [pw_succ, pw_succ, pw_succ, crcBit_xor, ih]
96theorem pw_zero (m : Nat) : pw m 0 = 0 := by
97 induction m with
98 | zero => rfl
99 | succ m ih => rw [pw_succ, crcBit_zero, ih]
100
101theorem crcByte_eq (c : UInt32) (b : UInt8) : ZlibF.Spec.crcByte c b = pw 8 (c ^^^ b.toUInt32) := by
102 unfold ZlibF.Spec.crcByte
103 generalize c ^^^ b.toUInt32 = v
104 have : ∀ n, (List.range n).foldl (fun c _ => ZlibF.Spec.crcBit c) v = pw n v := by
105 intro n
106 induction n with
107 | zero => rfl
108 | succ n ih => rw [List.range_succ, List.foldl_append, ih, pw_succ']; rfl
109 exact this 8
110
111
112/-! ### Tables -/
113
114theorem crc_table_size : Crc32H.crc_table.size = 256 := by decide +kernel
115
116set_option maxRecDepth 100000 in
117theorem crc_table_eq : ∀ n : Fin 256, Crc32H.crc_table[n.val]! = pw 8 (UInt32.ofNat n.val) := by decide +kernel
118
119
120theorem braid_size : Crc32H.crc_braid_table.size = 8 ∧ ∀ j : Fin 8, Crc32H.crc_braid_table[j.val]!.size = 256 := by
121 decide +kernel
122
123set_option maxRecDepth 100000 in
124theorem braid_eq : ∀ j : Fin 8, ∀ n : Fin 256,
125 Crc32H.crc_braid_table[j.val]![n.val]! = pw (8 * (40 - j.val)) (UInt32.ofNat n.val) := by decide +kernel
126
127/-! ### Linear maps are determined by their values on the unit vectors -/
128
129theorem lin_ext_nat (w : Nat) (F G : Nat → Nat)
130 (hF : ∀ x y, F (x ^^^ y) = F x ^^^ F y) (hG : ∀ x y, G (x ^^^ y) = G x ^^^ G y)
131 (hb : ∀ t, t < w → F (2 ^ t) = G (2 ^ t)) : ∀ n, n ≤ w → ∀ x, x < 2 ^ n → F x = G x := by
132 have F0 : F 0 = 0 := by have := hF 0 0; simpa [Nat.xor_self] using this
133 have G0 : G 0 = 0 := by have := hG 0 0; simpa [Nat.xor_self] using this
134 intro n
135 induction n with
136 | zero => intro _ x hx; have : x = 0 := by simpa using hx
137 subst this; rw [F0, G0]
138 | succ n ih =>
139 intro hn x hx
140 by_cases hbit : x.testBit n
141 · have hy : (x ^^^ 2 ^ n) < 2 ^ n := by
142 apply Nat.lt_pow_two_of_testBit
143 intro i hi
144 rw [Nat.testBit_xor, Nat.testBit_two_pow]
145 rcases Nat.eq_or_lt_of_le hi with rfl | hlt
146 · simp [hbit]
147 · have : x.testBit i = false := Nat.testBit_lt_two_pow (by
148 exact Nat.lt_of_lt_of_le hx (Nat.pow_le_pow_right (by omega) (by omega)))
149 simp [this]; omega
150 have hx' : x = (x ^^^ 2 ^ n) ^^^ 2 ^ n := by
151 rw [Nat.xor_assoc, Nat.xor_self, Nat.xor_zero]
152 rw [hx', hF, hG, ih (by omega) _ hy, hb n (by omega)]
153 · have : x < 2 ^ n := by
154 apply Nat.lt_pow_two_of_testBit
155 intro i hi
156 rcases Nat.eq_or_lt_of_le hi with rfl | hlt
157 · simpa using hbit
158 · exact Nat.testBit_lt_two_pow (Nat.lt_of_lt_of_le hx (Nat.pow_le_pow_right (by omega) (by omega)))
159 exact ih (by omega) x this
160
161def Lin32 (f : UInt32 → UInt32) : Prop := ∀ x y, f (x ^^^ y) = f x ^^^ f y
162
163/-- The unit vector with bit `s` set. -/
164def e (s : Nat) : UInt32 := UInt32.ofNat (2 ^ s)
165
166theorem lin32_ext {f g : UInt32 → UInt32} (hf : Lin32 f) (hg : Lin32 g)
167 (hb : ∀ t, t < 32 → f (e t) = g (e t)) : ∀ x, f x = g x := by
168 intro x
169 have hf' : ∀ x y, f (x ^^^ y) = f x ^^^ f y := hf
170 have hg' : ∀ x y, g (x ^^^ y) = g x ^^^ g y := hg
171 have key := lin_ext_nat 32 (fun n => (f (UInt32.ofNat n)).toNat) (fun n => (g (UInt32.ofNat n)).toNat)
172 (by intro a b; simp only [UInt32.ofNat_xor, hf', UInt32.toNat_xor])
173 (by intro a b; simp only [UInt32.ofNat_xor, hg', UInt32.toNat_xor])
174 (by intro t ht; simp only [e] at hb; rw [hb t ht]) 32 (Nat.le_refl _) x.toNat (by simpa using UInt32.toNat_lt x)
175 simp only [UInt32.ofNat_toNat] at key
176 exact UInt32.toNat_inj.mp key
177
178theorem lin32_zero {f : UInt32 → UInt32} (hf : Lin32 f) : f 0 = 0 := by
179 have := hf 0 0
180 simpa using this
181
182/-! ### Multiplication in the bit-reflected representation -/
183
184/-- coefficient of `x^j` in `a` (bit `31 - j`) -/
185def bitAt (a : UInt32) (j : Nat) : Bool := decide (j < 32) && a.toNat.testBit (31 - j)
186
187def mulpAux (a b : UInt32) : Nat → UInt32
188 | 0 => 0
189 | n + 1 => mulpAux a b n ^^^ (if bitAt a n then pw n b else 0)
190
191/-- the product of `multmodp`: `a * b` modulo the CRC polynomial -/
192def mulp (a b : UInt32) : UInt32 := mulpAux a b 32
193
194theorem bitAt_xor (a b : UInt32) (j : Nat) : bitAt (a ^^^ b) j = (bitAt a j ^^ bitAt b j) := by
195 unfold bitAt; rw [UInt32.toNat_xor, Nat.testBit_xor]
196 by_cases h : j < 32 <;> simp [h]
197
198theorem ite_xor (p q : Bool) (t : UInt32) :
199 (if (p ^^ q) = true then t else 0) = (if p = true then t else 0) ^^^ (if q = true then t else 0) := by
200 cases p <;> cases q <;> simp [UInt32.xor_self]
201
202theorem mulpAux_xor_left (a a' b : UInt32) (n : Nat) :
203 mulpAux (a ^^^ a') b n = mulpAux a b n ^^^ mulpAux a' b n := by
204 induction n with
205 | zero => simp [mulpAux]
206 | succ n ih =>
207 simp only [mulpAux, ih, bitAt_xor, ite_xor]; ac_rfl
208
209theorem mulpAux_xor_right (a b b' : UInt32) (n : Nat) :
210 mulpAux a (b ^^^ b') n = mulpAux a b n ^^^ mulpAux a b' n := by
211 induction n with
212 | zero => simp [mulpAux]
213 | succ n ih =>
214 simp only [mulpAux, ih, pw_xor]
215 cases bitAt a n <;> simp <;> ac_rfl
216
217theorem mulp_xor_left (a a' b : UInt32) : mulp (a ^^^ a') b = mulp a b ^^^ mulp a' b := mulpAux_xor_left ..
218theorem mulp_xor_right (a b b' : UInt32) : mulp a (b ^^^ b') = mulp a b ^^^ mulp a b' := mulpAux_xor_right ..
219
220theorem mulpAux_zero_left (b : UInt32) (n : Nat) : mulpAux 0 b n = 0 := by
221 induction n with
222 | zero => rfl
223 | succ n ih => simp [mulpAux, ih, bitAt]
224theorem mulp_zero_left (b : UInt32) : mulp 0 b = 0 := mulpAux_zero_left b 32
225
226theorem tb_e (s i : Nat) (hs : s < 32) : (e s).toNat.testBit i = decide (s = i) := by
227 simp only [e, UInt32.toNat_ofNat']
228 rw [Nat.mod_eq_of_lt (Nat.pow_lt_pow_right (by omega) hs), Nat.testBit_two_pow]
229
230theorem bitAt_e (s : Nat) (hs : s < 32) (j : Nat) : bitAt (e s) j = decide (j < 32 ∧ 31 - j = s) := by
231 unfold bitAt; rw [tb_e s _ hs]
232 by_cases h : j < 32
233 · simp [h]; omega
234 · simp [h]
235
236theorem mulpAux_e (s : Nat) (hs : s < 32) (b : UInt32) (n : Nat) (hn : n ≤ 32) :
237 mulpAux (e s) b n = if 31 - s < n then pw (31 - s) b else 0 := by
238 induction n with
239 | zero => simp [mulpAux]
240 | succ n ih =>
241 simp only [mulpAux, ih (by omega), bitAt_e s hs]
242 by_cases h2 : 31 - s = n
243 · have h3 : n < 32 := by omega
244 have h5 : 31 - n = s := by omega
245 simp [h2, h3, h5]
246 · have h3 : ¬ (n < 32 ∧ 31 - n = s) := by omega
247 simp [h3]
248 by_cases h4 : 31 - s < n
249 · simp [h4, show 31 - s < n + 1 by omega]
250 · simp [h4, show ¬ 31 - s < n + 1 by omega]
251
252theorem mulp_e (s : Nat) (hs : s < 32) (b : UInt32) : mulp (e s) b = pw (31 - s) b := by
253 unfold mulp; rw [mulpAux_e s hs b 32 (Nat.le_refl _)]; simp; omega
254
255set_option maxRecDepth 100000 in
256theorem crcBit_e : ∀ s : Fin 32, crcBit (e s.val) = if s.val = 0 then crcPoly else e (s.val - 1) := by
257 decide +kernel
258
259set_option maxRecDepth 100000 in
260theorem mulp_poly_basis : ∀ t : Fin 32, mulp crcPoly (e t.val) = pw 32 (e t.val) := by decide +kernel
261
262theorem mulp_lin_right (a : UInt32) : Lin32 (mulp a) := fun x y => mulp_xor_right a x y
263theorem pw_lin (m : Nat) : Lin32 (pw m) := fun x y => pw_xor m x y
264
265theorem mulp_poly (v : UInt32) : mulp crcPoly v = pw 32 v :=
266 lin32_ext (mulp_lin_right crcPoly) (pw_lin 32) (fun t ht => mulp_poly_basis ⟨t, ht⟩) v
267
268theorem mulp_crcBit_left (a v : UInt32) : mulp (crcBit a) v = crcBit (mulp a v) := by
269 refine lin32_ext (f := fun a => mulp (crcBit a) v) (g := fun a => crcBit (mulp a v)) ?_ ?_ ?_ a
270 · intro x y; simp only [crcBit_xor, mulp_xor_left]
271 · intro x y; simp only [mulp_xor_left, crcBit_xor]
272 · intro t ht
273 have h := crcBit_e ⟨t, ht⟩
274 simp only at h
275 show mulp (crcBit (e t)) v = crcBit (mulp (e t) v)
276 rw [h, mulp_e t ht]
277 by_cases h0 : t = 0
278 · subst h0
279 simp only [ite_true]
280 rw [mulp_poly]
281 show pw 32 v = crcBit (pw 31 v)
282 rw [← pw_succ']
283 · simp only [h0, ite_false]
284 rw [mulp_e (t - 1) (by omega)]
285 rw [show 31 - (t - 1) = (31 - t) + 1 by omega, pw_succ']
286
287theorem mulp_pw_left' (j : Nat) (a v : UInt32) : mulp (pw j a) v = pw j (mulp a v) := by
288 induction j generalizing a with
289 | zero => rfl
290 | succ j ih => rw [pw_succ', pw_succ', mulp_crcBit_left, ih]
291
292theorem mulp_one (v : UInt32) : mulp (e 31) v = v := by
293 rw [mulp_e 31 (by omega)]; rfl
294
295theorem mulpAux_assoc (a b v : UInt32) (n : Nat) : mulp (mulpAux a b n) v = mulpAux a (mulp b v) n := by
296 induction n with
297 | zero => simp [mulpAux, mulp_zero_left]
298 | succ n ih =>
299 simp only [mulpAux, mulp_xor_left, ih]
300 cases bitAt a n
301 · simp [mulp_zero_left]
302 · simp [mulp_pw_left']
303
304theorem mulp_assoc (a b v : UInt32) : mulp (mulp a b) v = mulp a (mulp b v) := mulpAux_assoc a b v 32
305
306/-! ### The table `x2n_table` -/
307
308def Xt (i : Nat) : UInt32 := Crc32H.x2n_table[i]!
309
310theorem x2n_size : Crc32H.x2n_table.size = 32 := by decide +kernel
311set_option maxRecDepth 100000 in
312theorem Xt_zero : Xt 0 = pw 1 (e 31) := by decide +kernel
313set_option maxRecDepth 100000 in
314theorem Xt_sq : ∀ i : Fin 31, Xt (i.val + 1) = mulp (Xt i.val) (Xt i.val) := by decide +kernel
315set_option maxRecDepth 100000 in
316theorem Xt_wrap : mulp (Xt 31) (Xt 31) = Xt 0 := by decide +kernel
317set_option maxRecDepth 100000 in
318theorem Xt_ne : ∀ i : Fin 32, Xt i.val ≠ 0 := by decide +kernel
319
320theorem Xt_pw : ∀ i, i < 32 → ∀ v, mulp (Xt i) v = pw (2 ^ i) v := by
321 intro i
322 induction i with
323 | zero =>
324 intro _ v
325 rw [Xt_zero, mulp_pw_left', mulp_one]
326 | succ i ih =>
327 intro hi v
328 rw [Xt_sq ⟨i, by omega⟩]
329 simp only []
330 rw [mulp_assoc, ih (by omega), ih (by omega), ← pw_add, Nat.pow_succ, Nat.mul_two]
331
332theorem pw_period_one (v : UInt32) : pw (2 ^ 32) v = pw 1 v := by
333 have h := mulp_assoc (Xt 31) (Xt 31) v
334 have h1 : mulp (Xt 31) v = pw (2 ^ 31) v := Xt_pw 31 (by omega) v
335 have h2 : mulp (Xt 31) (pw (2 ^ 31) v) = pw (2 ^ 31) (pw (2 ^ 31) v) := Xt_pw 31 (by omega) _
336 have h3 : mulp (Xt 0) v = pw 1 v := Xt_pw 0 (by omega) v
337 rw [Xt_wrap, h3, h1, h2, ← pw_add] at h
338 rw [show 2 ^ 32 = 2 ^ 31 + 2 ^ 31 by omega]
339 exact h.symm
340
341theorem pw_period (a : Nat) (v : UInt32) : pw (2 ^ 32 * a) v = pw a v := by
342 induction a with
343 | zero => rfl
344 | succ a ih =>
345 rw [Nat.mul_succ, Nat.add_comm, pw_add, ih, pw_period_one, ← pw_add, Nat.add_comm]
346
347theorem Xt_mod (k : Nat) (v : UInt32) : mulp (Xt (k % 32)) v = pw (2 ^ k) v := by
348 induction k using Nat.strongRecOn with
349 | _ k ih =>
350 by_cases hk : k < 32
351 · rw [Nat.mod_eq_of_lt hk]; exact Xt_pw k hk v
352 · obtain ⟨m, rfl⟩ : ∃ m, k = m + 32 := ⟨k - 32, by omega⟩
353 have := ih m (by omega)
354 rw [show (m + 32) % 32 = m % 32 by omega, this, Nat.pow_add, Nat.mul_comm, pw_period]
355
356/-- `StaticsOK h`: the static tables `crc_table` and `x2n_table` of the heap hold the contents `crc32.h` gives them
357(`Zlib.Crc32.initStatics`). -/
358def StaticsOK (h : Heap) : Prop :=
359 h.static.crc_table = Crc32H.crc_table ∧ h.static.x2n_table = Crc32H.x2n_table
360
361/-! ### Bits of 64-bit values -/
362
363theorem lowbit_aux (x : Nat) : ∀ n, x.testBit n = true →
364 ∃ t, x.testBit t = true ∧ ∀ s, s < t → x.testBit s = false := by
365 intro n
366 induction n using Nat.strongRecOn with
367 | _ n ih =>
368 intro hn
369 by_cases h : ∃ s, s < n ∧ x.testBit s = true
370 · obtain ⟨s, hs, hs2⟩ := h
371 exact ih s hs hs2
372 · refine ⟨n, hn, ?_⟩
373 intro s hs
374 cases hb : x.testBit s
375 · rfl
376 · exact absurd ⟨s, hs, hb⟩ h
377
378theorem lowbit_exists (x : Nat) (hx : x ≠ 0) : ∃ t, x.testBit t = true ∧ ∀ s, s < t → x.testBit s = false := by
379 obtain ⟨i, hi⟩ := Nat.exists_testBit_of_ne_zero hx
380 exact lowbit_aux x i hi
381
382theorem mod_pow_zero_iff (x t i : Nat) (ht : x.testBit t = true) (hlow : ∀ s, s < t → x.testBit s = false) :
383 x % 2 ^ i = 0 ↔ i ≤ t := by
384 constructor
385 · intro h
386 apply Classical.byContradiction
387 intro hlt
388 have h1 : (x % 2 ^ i).testBit t = true := by
389 rw [Nat.testBit_mod_two_pow]; simp [ht]; omega
390 rw [h] at h1; simp at h1
391 · intro h
392 apply Nat.eq_of_testBit_eq
393 intro k
394 rw [Nat.testBit_mod_two_pow, Nat.zero_testBit]
395 by_cases hk : k < i
396 · simp [hlow k (by omega)]
397 · simp [hk]
398
399def mm (j : Nat) : UInt64 := UInt64.ofNat (2 ^ (31 - j))
400
401theorem mm_toNat (j : Nat) : (mm j).toNat = 2 ^ (31 - j) := by
402 unfold mm; rw [UInt64.toNat_ofNat']
403 exact Nat.mod_eq_of_lt (Nat.pow_lt_pow_right (by omega) (by omega))
404
405theorem and_pow_ne (n i : Nat) : (n &&& 2 ^ i ≠ 0) ↔ n.testBit i = true := by
406 constructor
407 · intro h
408 apply Classical.byContradiction
409 intro hb
410 apply h
411 apply Nat.eq_of_testBit_eq
412 intro k
413 rw [Nat.testBit_and, Nat.testBit_two_pow, Nat.zero_testBit]
414 by_cases hk : i = k
415 · subst hk; simp at hb; simp [hb]
416 · simp [hk]
417 · intro h h0
418 have := congrArg (fun z => z.testBit i) h0
419 simp [Nat.testBit_and, Nat.testBit_two_pow, h] at this
420
421theorem and_mm_ne (a : UInt64) (j : Nat) (hj : j ≤ 31) :
422 (a &&& mm j != 0) = decide (a.toUInt32.toNat.testBit (31 - j) = true) := by
423 have h1 : (a &&& mm j ≠ 0) ↔ a.toNat.testBit (31 - j) = true := by
424 rw [← and_pow_ne, ← mm_toNat j, ← UInt64.toNat_and]
425 constructor
426 · intro h h0; apply h; apply UInt64.toNat_inj.mp; simpa using h0
427 · intro h h0; apply h; rw [h0]; rfl
428 have h2 : a.toUInt32.toNat.testBit (31 - j) = a.toNat.testBit (31 - j) := by
429 rw [UInt64.toNat_toUInt32, Nat.testBit_mod_two_pow]; simp; omega
430 rw [h2]
431 by_cases h : a.toNat.testBit (31 - j) = true
432 · simp [h, h1.mpr h]
433 · have : ¬ (a &&& mm j ≠ 0) := fun hh => h (h1.mp hh)
434 simp [h]; simpa using this
435
436theorem and_mm_pred_eq (a : UInt64) (j : Nat) (hj : j ≤ 31) :
437 (a &&& (mm j - 1) == 0) = decide (a.toNat % 2 ^ (31 - j) = 0) := by
438 have h1 : (mm j - 1).toNat = 2 ^ (31 - j) - 1 := by
439 rw [UInt64.toNat_sub_of_le, mm_toNat]; rfl
440 rw [UInt64.le_iff_toNat_le, mm_toNat]; simp
441 exact Nat.one_le_two_pow
442 have h2 : (a &&& (mm j - 1)).toNat = a.toNat % 2 ^ (31 - j) := by
443 rw [UInt64.toNat_and, h1, Nat.and_two_pow_sub_one_eq_mod]
444 by_cases h : a.toNat % 2 ^ (31 - j) = 0
445 · have : a &&& (mm j - 1) = 0 := by apply UInt64.toNat_inj.mp; rw [h2, h]; rfl
446 simp [this, h]
447 · have : ¬ a &&& (mm j - 1) = 0 := by
448 intro hh; apply h; rw [← h2, hh]; rfl
449 simp [this, h]
450
451theorem mod_32 (a : UInt64) (i : Nat) (hi : i ≤ 32) : a.toUInt32.toNat % 2 ^ i = a.toNat % 2 ^ i := by
452 rw [UInt64.toNat_toUInt32]
453 exact Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 hi)
454
455theorem mm_shr (j : Nat) (hj : j < 31) : mm j >>> 1 = mm (j + 1) := by
456 apply UInt64.toNat_inj.mp
457 rw [UInt64.toNat_shiftRight, mm_toNat, mm_toNat]
458 have h1 : (1 : UInt64).toNat % 64 = 1 := by decide
459 rw [h1, Nat.shiftRight_eq_div_pow]
460 have : 31 - j = (31 - (j + 1)) + 1 := by omega
461 rw [this, Nat.pow_succ]; simp
462
463theorem POLY_eq : Zlib.Crc32.POLY = crcPoly.toUInt64 := by decide
464
465theorem b_step (v : UInt32) :
466 (if (v.toUInt64 &&& 1 != 0) = true then v.toUInt64 >>> 1 ^^^ Zlib.Crc32.POLY else v.toUInt64 >>> 1)
467 = (crcBit v).toUInt64 := by
468 unfold crcBit
469 have h1 : (v.toUInt64 &&& 1) = (v &&& 1).toUInt64 := (UInt32.toUInt64_and v 1).symm
470 have hs : v.toUInt64 >>> 1 = (v >>> 1).toUInt64 := by rw [UInt32.toUInt64_shiftRight]; rfl
471 rcases and1_cases v with h0 | h0
472 · have hz : (v.toUInt64 &&& 1 != 0) = false := by rw [h1, h0]; decide
473 have hz2 : ¬ (v &&& 1 = 1) := by rw [h0]; decide
474 rw [hz, ite_eq_right hz2, ite_eq_right (by decide), hs]
475 · have hz : (v.toUInt64 &&& 1 != 0) = true := by rw [h1, h0]; decide
476 rw [hz, ite_eq_left h0, ite_eq_left rfl, UInt32.toUInt64_xor, POLY_eq, hs]
477
478/-! ### `multmodp` -/
479
480abbrev MS := UInt64 × UInt64 × UInt64 × Bool
481
482/-- the body of the loop of `multmodp` as a pure function -/
483def mmStep (a : UInt64) (s : MS) : MS :=
484 match s with
485 | (m, p, b, brk) =>
486 if (a &&& m != 0) = true then
487 if (a &&& (m - 1) == 0) = true then (m, p ^^^ b, b, true)
488 else (m >>> 1, p ^^^ b, (if (b &&& 1 != 0) = true then b >>> 1 ^^^ Zlib.Crc32.POLY else b >>> 1), brk)
489 else (m >>> 1, p, (if (b &&& 1 != 0) = true then b >>> 1 ^^^ Zlib.Crc32.POLY else b >>> 1), brk)
490
491theorem mulpAux_stable (a b : UInt32) (t : Nat) (ht : t < 32) (hlow : ∀ s, s < t → a.toNat.testBit s = false) :
492 ∀ n, 31 - t + 1 ≤ n → n ≤ 32 → mulpAux a b n = mulpAux a b (31 - t + 1) := by
493 intro n
494 induction n with
495 | zero => intro h; omega
496 | succ n ih =>
497 intro h1 h2
498 by_cases h : 31 - t + 1 ≤ n
499 · rw [mulpAux, ih h (by omega)]
500 have : bitAt a n = false := by
501 unfold bitAt
502 have := hlow (31 - n) (by omega)
503 simp [this]
504 simp [this]
505 · have : n + 1 = 31 - t + 1 := by omega
506 rw [this]
507
508def Pmm (a32 b32 : UInt32) (J : Nat) : Nat → MS → Prop
509 | 0, s => s.2.2.2 = true ∧ s.2.1 = (mulp a32 b32).toUInt64
510 | k + 1, s => ∃ j, j + k = J ∧ s = (mm j, (mulpAux a32 b32 j).toUInt64, (pw j b32).toUInt64, false)
511
512theorem multmodp_ok (h : Heap) (a b : UInt64) (ha : a.toUInt32 ≠ 0) (hb : b.toNat < 2 ^ 32) :
513 OkOn h (multmodp a b) (mulp a.toUInt32 b.toUInt32).toUInt64 := by
514 have ha' : a.toUInt32.toNat ≠ 0 := fun h0 => ha (UInt32.toNat_inj.mp (by simpa using h0))
515 obtain ⟨t, ht, hlow⟩ := lowbit_exists _ ha'
516 have ht32 : t < 32 := by
517 apply Classical.byContradiction; intro hh
518 have := Nat.testBit_lt_two_pow (x := a.toUInt32.toNat) (i := t)
519 (Nat.lt_of_lt_of_le (UInt32.toNat_lt _) (Nat.pow_le_pow_right (by omega) (by omega)))
520 rw [ht] at this; simp at this
521 have hb64 : b.toUInt32.toUInt64 = b := by
522 apply UInt64.toNat_inj.mp
523 rw [UInt32.toNat_toUInt64, UInt64.toNat_toUInt32, Nat.mod_eq_of_lt hb]
524 generalize hJ : 31 - t = J
525 suffices H : ∃ y, OkOn h (multmodp a b) y ∧ y = (mulp a.toUInt32 b.toUInt32).toUInt64 by
526 obtain ⟨y, hy, rfl⟩ := H; exact hy
527 unfold multmodp
528 refine OkOn.bind_ex (R := fun x : MS => x.2.1 = (mulp a.toUInt32 b.toUInt32).toUInt64) ?_ ?_
529 · refine cwhile_ok (Pmm a.toUInt32 b.toUInt32 J) (fun x : MS => x.2.1 = (mulp a.toUInt32 b.toUInt32).toUInt64)
530 ?_ ?_ (k := J + 1) (s := ((1 : UInt64) <<< 31, 0, b, false)) ?_ ?_
531 · intro k s hP
532 obtain ⟨j, hjk, rfl⟩ := hP
533 have hj31 : j ≤ 31 := by omega
534 have e1 : (a &&& mm j != 0) = bitAt a.toUInt32 j := by
535 rw [and_mm_ne a j hj31]; unfold bitAt; simp [show j < 32 by omega]
536 have e2 : (a &&& (mm j - 1) == 0) = decide (31 - j ≤ t) := by
537 rw [and_mm_pred_eq a j hj31, ← mod_32 a (31 - j) (by omega)]
538 simp only [mod_pow_zero_iff _ t _ ht hlow]
539 refine ⟨(mm j, (mulpAux a.toUInt32 b.toUInt32 j).toUInt64, (pw j b.toUInt32).toUInt64, false),
540 mmStep a (mm j, (mulpAux a.toUInt32 b.toUInt32 j).toUInt64, (pw j b.toUInt32).toUInt64, false), rfl, ?_, ?_⟩
541 · by_cases c1 : (a &&& mm j != 0) = true <;> by_cases c2 : (a &&& mm j - 1 == 0) = true <;>
542 simp [mmStep, c1, c2, OkOn]
543 simp only [mmStep, e1, e2]
544 cases k with
545 | zero =>
546 have hj : j = J := by omega
547 have hbit : bitAt a.toUInt32 j = true := by
548 unfold bitAt; rw [show 31 - j = t by omega, ht]; simp [show j < 32 by omega]
549 have hst : decide (31 - j ≤ t) = true := by simp; omega
550 rw [ite_eq_left hbit, ite_eq_left hst]
551 refine ⟨rfl, ?_⟩
552 have : mulpAux a.toUInt32 b.toUInt32 (j + 1) = mulpAux a.toUInt32 b.toUInt32 j ^^^ pw j b.toUInt32 := by
553 simp [mulpAux, hbit]
554 have hst2 := mulpAux_stable a.toUInt32 b.toUInt32 t ht32 hlow 32 (by omega) (by omega)
555 show _ = (mulpAux a.toUInt32 b.toUInt32 32).toUInt64
556 rw [hst2, show 31 - t + 1 = j + 1 by omega, this, UInt32.toUInt64_xor]
557 | succ k =>
558 have hst : decide (31 - j ≤ t) = false := by simp; omega
559 have hst' : ¬ (decide (31 - j ≤ t) = true) := by rw [hst]; decide
560 refine ⟨j + 1, by omega, ?_⟩
561 have hb' := b_step (pw j b.toUInt32)
562 rw [← pw_succ'] at hb'
563 have hm : mm j >>> 1 = mm (j + 1) := mm_shr j (by omega)
564 cases hbit : bitAt a.toUInt32 j
565 · rw [ite_eq_right (by simp [hbit]), hm, hb']; simp [mulpAux, hbit]
566 · rw [ite_eq_left (by simp [hbit]), ite_eq_right hst', hm, hb']; simp [mulpAux, hbit, UInt32.toUInt64_xor]
567 · intro s hP
568 obtain ⟨m, p, bb, brk⟩ := s
569 obtain ⟨h1, h2⟩ := hP
570 simp only at h1 h2
571 subst h1
572 exact ⟨_, rfl, h2⟩
573 · refine ⟨0, by omega, ?_⟩
574 simp only [mulpAux, pw]
575 rw [hb64]; rfl
576 · show J + 1 < 2 ^ 64
577 omega
578 · intro x hx
579 obtain ⟨m, p, bb, brk⟩ := x
580 exact ⟨p, rfl, hx⟩
581
582theorem int64_shr (nn : Nat) (h : nn < 2 ^ 63) : (Int64.ofNat nn) >>> 1 = Int64.ofNat (nn / 2) := by
583 apply Int64.toBitVec_inj.mp
584 rw [Int64.toBitVec_shiftRight]
585 simp [Int64.toBitVec_ofNat']
586 rw [BitVec.sshiftRight_eq_of_msb_false]
587 · apply BitVec.eq_of_toNat_eq
588 rw [BitVec.toNat_ushiftRight, BitVec.toNat_ofNat, BitVec.toNat_ofNat,
589 Nat.mod_eq_of_lt (by omega : nn < 2 ^ 64), Nat.mod_eq_of_lt (by omega : nn / 2 < 2 ^ 64),
590 Nat.shiftRight_eq_div_pow]
591 · rw [BitVec.msb_eq_false_iff_two_mul_lt, BitVec.toNat_ofNat, Nat.mod_eq_of_lt (by omega : nn < 2 ^ 64)]
592 omega
593
594
595theorem int64_and1 (nn : Nat) (h : nn < 2 ^ 63) : ((Int64.ofNat nn) &&& 1 != 0) = decide (nn % 2 = 1) := by
596 have h1 : ((Int64.ofNat nn) &&& 1).toBitVec.toNat = nn % 2 := by
597 rw [Int64.toBitVec_and, BitVec.toNat_and, Int64.toBitVec_ofNat', BitVec.toNat_ofNat,
598 Nat.mod_eq_of_lt (by omega : nn < 2 ^ 64)]
599 have : (Int64.toBitVec 1).toNat = 1 := by decide
600 rw [this, Nat.and_one_is_mod]
601 by_cases h2 : nn % 2 = 1
602 · have : ((Int64.ofNat nn) &&& 1) ≠ 0 := by
603 intro h0
604 rw [h0] at h1
605 have : (Int64.toBitVec 0).toNat = 0 := by decide
606 omega
607 simp [this, h2]
608 · have : ((Int64.ofNat nn) &&& 1) = 0 := by
609 apply Int64.toBitVec_inj.mp
610 apply BitVec.eq_of_toNat_eq
611 rw [h1]; have : (Int64.toBitVec 0).toNat = 0 := by decide
612 rw [this]; omega
613 simp [this, h2]
614
615theorem int64_ne0 (nn : Nat) (h : nn < 2 ^ 63) : ((Int64.ofNat nn) != 0) = decide (nn ≠ 0) := by
616 by_cases h2 : nn = 0
617 · subst h2; decide
618 · have : Int64.ofNat nn ≠ 0 := by
619 intro h0
620 have := congrArg (fun x => x.toBitVec.toNat) h0
621 simp only [Int64.toBitVec_ofNat', BitVec.toNat_ofNat] at this
622 have h3 : (Int64.toBitVec 0).toNat = 0 := by decide
623 rw [h3, Nat.mod_eq_of_lt (by omega : nn < 2 ^ 64)] at this
624 exact h2 this
625 simp [this, h2]
626
627/-! ### `x2nmodp` -/
628
629/-- the number of loop iterations of `x2nmodp` for `n`: its bit length -/
630def bl (n : Nat) : Nat := if n = 0 then 0 else bl (n / 2) + 1
631termination_by n
632decreasing_by omega
633
634theorem bl_zero : bl 0 = 0 := by rw [bl]; simp
635theorem bl_pos (n : Nat) (h : n ≠ 0) : bl n = bl (n / 2) + 1 := by rw [bl]; simp [h]
636
637theorem bl_div (n i : Nat) : bl (n / 2 ^ i / 2) = bl (n / 2 ^ (i + 1)) := by
638 rw [Nat.pow_succ, Nat.div_div_eq_div_mul]
639
640theorem bl_le (n : Nat) : bl n ≤ n := by
641 induction n using Nat.strongRecOn with
642 | _ n ih =>
643 by_cases h : n = 0
644 · subst h; rw [bl_zero]; exact Nat.le_refl _
645 · rw [bl_pos n h]
646 have := ih (n / 2) (by omega)
647 omega
648
649theorem lt_pow_bl (n : Nat) : n < 2 ^ bl n := by
650 induction n using Nat.strongRecOn with
651 | _ n ih =>
652 by_cases h : n = 0
653 · subst h; rw [bl_zero]; simp
654 · rw [bl_pos n h, Nat.pow_succ]
655 have := ih (n / 2) (by omega)
656 omega
657
658
659theorem u32_roundtrip (x : UInt32) : x.toUInt64.toUInt32 = x := by
660 apply UInt32.toNat_inj.mp
661 rw [UInt64.toNat_toUInt32, UInt32.toNat_toUInt64, Nat.mod_eq_of_lt (UInt32.toNat_lt _)]
662
663theorem x2nTab_ok (h : Heap) (hs : StaticsOK h) (i : UInt32) (hi : i.toNat < 32) :
664 OkOn h (x2nTab i) (Xt i.toNat) := by
665 unfold x2nTab
666 have hg : Space.get h URef.st_x2n_table = some Crc32H.x2n_table := by rw [URef.get_st_x2n_table, hs.2]
667 have hlt : 0 + i.toNat < Crc32H.x2n_table.size := by rw [x2n_size]; omega
668 have := Ptr.index_ok (r := URef.st_x2n_table) (off := 0) (i := i.toNat) hg hlt
669 have e : Xt i.toNat = Crc32H.x2n_table[0 + i.toNat] := by
670 simp only [Xt, Nat.zero_add]; exact getElem!_pos _ _ (by omega)
671 rw [e]; exact this
672
673theorem crcTab_ok (h : Heap) (hs : StaticsOK h) (i : UInt64) (hi : i.toNat < 256) :
674 OkOn h (crcTab i) Crc32H.crc_table[i.toNat]! := by
675 unfold crcTab
676 have hg : Space.get h URef.st_crc_table = some Crc32H.crc_table := by rw [URef.get_st_crc_table, hs.1]
677 have hlt : 0 + i.toNat < Crc32H.crc_table.size := by rw [crc_table_size]; omega
678 have := Ptr.index_ok (r := URef.st_crc_table) (off := 0) (i := i.toNat) hg hlt
679 have e : Crc32H.crc_table[i.toNat]! = Crc32H.crc_table[0 + i.toNat] := by
680 simp only [Nat.zero_add]; exact getElem!_pos _ _ (by omega)
681 rw [e]; exact this
682
683theorem and31 (x : UInt32) : (x &&& 31).toNat = x.toNat % 32 := by
684 rw [UInt32.toNat_and]
685 exact Nat.and_two_pow_sub_one_eq_mod x.toNat 5
686
687def Px (n0 k0 : Nat) (cnt : Nat) (s : UInt64 × Int64 × UInt32) : Prop :=
688 ∃ i, cnt = bl (n0 / 2 ^ i) ∧ s.1.toNat < 2 ^ 32 ∧ s.2.1 = Int64.ofNat (n0 / 2 ^ i) ∧
689 s.2.2 = UInt32.ofNat (k0 + i) ∧ ∀ v, mulp s.1.toUInt32 v = pw ((n0 % 2 ^ i) * 2 ^ k0) v
690
691theorem int64_nat_lt (n : Int64) (hn : 0 ≤ n.toInt) : n.toInt.toNat < 2 ^ 63 := by
692 have h1 : n.toInt < 2 ^ 63 := by
693 have := Int64.toInt_lt n
694 omega
695 omega
696
697theorem x2nmodp_ok (h : Heap) (hs : StaticsOK h) (n : Int64) (hn : 0 ≤ n.toInt) (k : UInt32) :
698 ∃ p, OkOn h (x2nmodp n k) p ∧ p.toNat < 2 ^ 32 ∧
699 ∀ v, mulp p.toUInt32 v = pw (n.toInt.toNat * 2 ^ k.toNat) v := by
700 have hn63 := int64_nat_lt n hn
701 generalize hn0 : n.toInt.toNat = n0 at hn63 ⊢
702 have hnn : n = Int64.ofNat n0 := by
703 rw [← hn0]
704 have := Int64.ofNat_toNatClampNeg n (by simpa [Int64.le_iff_toInt_le] using hn)
705 simpa using this.symm
706 generalize hk0 : k.toNat = k0
707 have hkk : k = UInt32.ofNat k0 := by rw [← hk0]; simp
708 subst hnn hkk
709 suffices H : ∃ y, OkOn h (x2nmodp (Int64.ofNat n0) (UInt32.ofNat k0)) y ∧
710 (y.toNat < 2 ^ 32 ∧ ∀ v, mulp y.toUInt32 v = pw (n0 * 2 ^ k0) v) by
711 obtain ⟨y, hy, h2⟩ := H; exact ⟨y, hy, h2⟩
712 unfold x2nmodp
713 refine OkOn.bind_ex (R := fun x : UInt64 × Int64 × UInt32 =>
714 x.1.toNat < 2 ^ 32 ∧ ∀ v, mulp x.1.toUInt32 v = pw (n0 * 2 ^ k0) v) ?_ ?_
715 · refine cwhile_ok (Px n0 k0) _ ?_ ?_ (k := bl n0) (s := ((1 : UInt64) <<< 31, Int64.ofNat n0, UInt32.ofNat k0)) ?_ ?_
716 · intro cnt s hP
717 obtain ⟨p, n, kk⟩ := s
718 obtain ⟨i, hcnt, hp32, hn', hk', hm⟩ := hP
719 simp only at hp32 hn' hk' hm
720 subst hn' hk'
721 have hne : n0 / 2 ^ i ≠ 0 := by
722 intro h0; rw [h0, bl_zero] at hcnt; omega
723 have hlt63 : n0 / 2 ^ i < 2 ^ 63 := Nat.lt_of_le_of_lt (Nat.div_le_self _ _) hn63
724 have hc : OkOn h (pure ((Int64.ofNat (n0 / 2 ^ i) != 0), p, Int64.ofNat (n0 / 2 ^ i), UInt32.ofNat (k0 + i)) :
725 M (Bool × UInt64 × Int64 × UInt32)) (true, p, Int64.ofNat (n0 / 2 ^ i), UInt32.ofNat (k0 + i)) := by
726 rw [int64_ne0 _ hlt63]; simp [hne]; exact OkOn.pure _ _
727 have hbit : (Int64.ofNat (n0 / 2 ^ i) &&& 1 != 0) = decide ((n0 / 2 ^ i) % 2 = 1) := int64_and1 _ hlt63
728 have hnext : Int64.ofNat (n0 / 2 ^ i) >>> 1 = Int64.ofNat (n0 / 2 ^ (i + 1)) := by
729 rw [int64_shr _ hlt63, Nat.pow_succ, Nat.div_div_eq_div_mul]
730 have hknext : UInt32.ofNat (k0 + i) + 1 = UInt32.ofNat (k0 + (i + 1)) := by
731 apply UInt32.toNat_inj.mp
732 simp [UInt32.toNat_add, UInt32.toNat_ofNat']
733 omega
734 have hcnt' : cnt = bl (n0 / 2 ^ (i + 1)) := by
735 rw [bl_pos _ hne, bl_div] at hcnt; omega
736 have hkt : (UInt32.ofNat (k0 + i) &&& 31).toNat = (k0 + i) % 32 := by
737 rw [and31, UInt32.toNat_ofNat']
738 exact Nat.mod_mod_of_dvd _ (by decide : 32 ∣ 2 ^ 32)
739 by_cases hb : (n0 / 2 ^ i) % 2 = 1
740 · have hX := x2nTab_ok h hs (UInt32.ofNat (k0 + i) &&& 31) (by rw [hkt]; omega)
741 rw [hkt] at hX
742 have hXne : (Xt ((k0 + i) % 32)).toUInt64.toUInt32 ≠ 0 := by
743 have : (Xt ((k0 + i) % 32)).toUInt64.toUInt32 = Xt ((k0 + i) % 32) := by
744 apply UInt32.toNat_inj.mp
745 rw [UInt64.toNat_toUInt32, UInt32.toNat_toUInt64, Nat.mod_eq_of_lt (UInt32.toNat_lt _)]
746 rw [this]; exact Xt_ne ⟨(k0 + i) % 32, by omega⟩
747 have hM := multmodp_ok h (Xt ((k0 + i) % 32)).toUInt64 p hXne (by omega)
748 refine ⟨(p, Int64.ofNat (n0 / 2 ^ i), UInt32.ofNat (k0 + i)),
749 ((mulp (Xt ((k0 + i) % 32)).toUInt64.toUInt32 p.toUInt32).toUInt64,
750 Int64.ofNat (n0 / 2 ^ (i + 1)), UInt32.ofNat (k0 + (i + 1))), hc, ?_, ?_⟩
751 · simp only [hbit, hb, decide_true, ite_true]
752 exact OkOn.bind hX (OkOn.bind hM (by rw [hnext, hknext]; exact OkOn.pure _ _))
753 · refine ⟨i + 1, hcnt', ?_, rfl, rfl, ?_⟩
754 · simp only [UInt32.toNat_toUInt64]; exact UInt32.toNat_lt _
755 · intro v
756 rw [u32_roundtrip, u32_roundtrip, mulp_assoc, Xt_mod, hm v, ← pw_add]
757 rw [Nat.mod_pow_succ, hb]
758 congr 1
759 rw [Nat.mul_one, Nat.add_mul, Nat.pow_add, Nat.mul_comm (2 ^ k0)]
760 omega
761 · have hb' : ¬ (n0 / 2 ^ i % 2 = 1) := hb
762 refine ⟨(p, Int64.ofNat (n0 / 2 ^ i), UInt32.ofNat (k0 + i)),
763 (p, Int64.ofNat (n0 / 2 ^ (i + 1)), UInt32.ofNat (k0 + (i + 1))), hc, ?_, ?_⟩
764 · simp only [hbit, hb', decide_false]
765 rw [ite_eq_right (by decide)]
766 rw [hnext, hknext]; exact OkOn.pure _ _
767 · refine ⟨i + 1, hcnt', hp32, rfl, rfl, ?_⟩
768 intro v
769 rw [hm v]
770 congr 1
771 rw [Nat.mod_pow_succ]
772 have : n0 / 2 ^ i % 2 = 0 := by omega
773 rw [this]; simp
774 · intro s hP
775 obtain ⟨p, n, kk⟩ := s
776 obtain ⟨i, hcnt, hp32, hn', hk', hm⟩ := hP
777 simp only at hp32 hn' hk' hm
778 subst hn' hk'
779 have h0 : n0 / 2 ^ i = 0 := by
780 apply Classical.byContradiction; intro hne
781 rw [bl_pos _ hne] at hcnt; omega
782 have hlt63 : n0 / 2 ^ i < 2 ^ 63 := Nat.lt_of_le_of_lt (Nat.div_le_self _ _) hn63
783 refine ⟨(p, Int64.ofNat (n0 / 2 ^ i), UInt32.ofNat (k0 + i)), ?_, hp32, ?_⟩
784 · show OkOn h (pure _) _
785 rw [int64_ne0 _ hlt63]; simp [h0]; exact OkOn.pure _ _
786 · have hmod : n0 % 2 ^ i = n0 := Nat.mod_eq_of_lt (by
787 rcases Nat.div_eq_zero_iff.mp h0 with h1 | h1
788 · have := Nat.two_pow_pos i; omega
789 · exact h1)
790 intro v; rw [hm v, hmod]
791 · refine ⟨0, by simp, by show ((1 : UInt64) <<< 31).toNat < 2 ^ 32; decide, by simp, by simp, ?_⟩
792 intro v
793 have : ((1 : UInt64) <<< 31).toUInt32 = e 31 := by decide
794 simp only [this, Nat.pow_zero, Nat.mod_one, Nat.zero_mul]
795 exact mulp_one v
796 · show bl n0 < 2 ^ 64
797 have := bl_le n0
798 have : 2 ^ 63 < 2 ^ 64 := by decide
799 omega
800 · intro x hx
801 obtain ⟨p, n, kk⟩ := x
802 exact ⟨p, rfl, hx⟩
803
804/-! ### `crcBit` is injective, so a power of `x` times a nonzero register is nonzero -/
805
806theorem crcBit_eq_zero (c : UInt32) (h : crcBit c = 0) : c = 0 := by
807 have e1 : (1 : UInt32).toNat = 1 := rfl
808 have e0 : (0 : UInt32).toNat = 0 := rfl
809 have hb : c &&& 1 = 0 := by
810 apply Classical.byContradiction; intro hne
811 rcases and1_cases c with h0 | h0
812 · exact hne h0
813 · unfold ZlibF.Spec.crcBit at h
814 rw [ite_eq_left h0] at h
815 have h1 := congrArg (fun x => x.toNat.testBit 31) h
816 simp only [UInt32.toNat_xor, Nat.testBit_xor, UInt32.toNat_shiftRight, e1, e0] at h1
817 have h2 : (c.toNat >>> (1 % 32)).testBit 31 = false := by
818 rw [Nat.testBit_shiftRight]
819 exact Nat.testBit_lt_two_pow (Nat.lt_of_lt_of_le (UInt32.toNat_lt c) (by decide))
820 have h3 : crcPoly.toNat.testBit 31 = true := by decide
821 rw [h2, h3] at h1
822 simp at h1
823 unfold ZlibF.Spec.crcBit at h
824 rw [ite_eq_right (by rw [hb]; decide)] at h
825 have h1 : c.toNat >>> 1 = 0 := by
826 have := congrArg UInt32.toNat h
827 rw [UInt32.toNat_shiftRight, e1, e0] at this
828 simpa using this
829 have h2 : c.toNat < 2 := by
830 rw [Nat.shiftRight_eq_div_pow] at h1
831 have := Nat.div_eq_zero_iff.mp h1
832 omega
833 have h3 : (c &&& 1).toNat = 0 := by rw [hb]; rfl
834 rw [UInt32.toNat_and, e1, Nat.and_one_is_mod] at h3
835 apply UInt32.toNat_inj.mp
836 rw [e0]; omega
837
838theorem pw_eq_zero (m : Nat) (c : UInt32) (h : pw m c = 0) : c = 0 := by
839 induction m generalizing c with
840 | zero => exact h
841 | succ m ih => exact crcBit_eq_zero c (ih _ (by rw [← pw_succ]; exact h))
842
843theorem e_ne_zero (s : Nat) (hs : s < 32) : e s ≠ 0 := by
844 intro h
845 have := congrArg (fun x => x.toNat) h
846 simp only [e, UInt32.toNat_ofNat'] at this
847 rw [Nat.mod_eq_of_lt (Nat.pow_lt_pow_right (by omega) hs)] at this
848 have h0 : (0 : UInt32).toNat = 0 := rfl
849 rw [h0] at this
850 exact absurd this (Nat.pos_iff_ne_zero.mp (Nat.two_pow_pos s))
851
852
853/-! ### The specification side: the CRC register is affine in its start value -/
854
855/-- the register after processing `l` from the start value `c` -/
856def rbl (c : UInt32) (l : List UInt8) : UInt32 := l.foldl ZlibF.Spec.crcByte c
857
858theorem rbl_nil (c : UInt32) : rbl c [] = c := rfl
859theorem rbl_cons (c : UInt32) (b : UInt8) (l : List UInt8) : rbl c (b :: l) = rbl (ZlibF.Spec.crcByte c b) l := rfl
860
861theorem rbl_affine (l : List UInt8) : ∀ c : UInt32, rbl c l = pw (8 * l.length) c ^^^ rbl 0 l := by
862 induction l with
863 | nil => intro c; simp [rbl_nil, pw]
864 | cons b l ih =>
865 intro c
866 rw [rbl_cons, rbl_cons, ih, ih (ZlibF.Spec.crcByte 0 b)]
867 simp only [crcByte_eq, UInt32.zero_xor, pw_xor, List.length_cons]
868 rw [show 8 * (l.length + 1) = 8 * l.length + 8 by omega, pw_add]
869 ac_rfl
870
871theorem not32_eq (x : UInt32) : ~~~ x = x ^^^ 0xffffffff := by
872 apply UInt32.eq_of_toBitVec_eq
873 have : (0xffffffff : UInt32).toBitVec = BitVec.allOnes 32 := by decide
874 rw [UInt32.toBitVec_xor, this, BitVec.xor_allOnes]
875 rfl
876
877theorem crc32Update_affine (c : UInt32) (l : List UInt8) :
878 ZlibF.Spec.crc32Update c l = pw (8 * l.length) c ^^^ ZlibF.Spec.crc32 l := by
879 unfold ZlibF.Spec.crc32 ZlibF.Spec.crc32Update
880 change ~~~ rbl (~~~ c) l = pw (8 * l.length) c ^^^ ~~~ rbl (~~~ 0) l
881 rw [rbl_affine l (~~~ c), rbl_affine l (~~~ 0), not32_eq, not32_eq c, not32_eq 0, not32_eq]
882 simp only [pw_xor]
883 rw [pw_zero, UInt32.zero_xor]
884 ac_rfl
885
886/-- `op` is the operator for `n` bytes: a 32-bit value with a bit set among the low 32 (so `multmodp` terminates
887on it) that multiplies a CRC register by `x^(8n)` (the advance over `n` zero bytes). -/
888def IsOp (op : UInt64) (n : Nat) : Prop :=
889 op.toNat < 2 ^ 32 ∧ op.toUInt32 ≠ 0 ∧ ∀ v, mulp op.toUInt32 v = pw (8 * n) v
890
891theorem and_ffffffff (x : UInt64) : x &&& 0xffffffff = x.toUInt32.toUInt64 := by
892 apply UInt64.toNat_inj.mp
893 rw [UInt64.toNat_and, UInt32.toNat_toUInt64, UInt64.toNat_toUInt32]
894 have : (0xffffffff : UInt64).toNat = 2 ^ 32 - 1 := by decide
895 rw [this, Nat.and_two_pow_sub_one_eq_mod]
896
897theorem gen64_neg (h : Heap) (len2 : Int64) (hneg : len2.toInt < 0) :
898 OkOn h (crc32_combine_gen64 len2) 0 := by
899 unfold crc32_combine_gen64
900 have : len2 < 0 := by rw [Int64.lt_iff_toInt_lt]; simpa using hneg
901 rw [ite_eq_left this]; exact OkOn.pure _ _
902
903theorem gen64_op (h : Heap) (hs : StaticsOK h) (len2 : Int64) (hpos : 0 ≤ len2.toInt) :
904 ∃ op, OkOn h (crc32_combine_gen64 len2) op ∧ IsOp op len2.toInt.toNat := by
905 obtain ⟨p, hp, hp32, hpv⟩ := x2nmodp_ok h hs len2 hpos 3
906 have hlt : ¬ (len2 < 0) := by rw [Int64.lt_iff_toInt_lt]; simpa using hpos
907 have h3 : (3 : UInt32).toNat = 3 := rfl
908 have hpv' : ∀ v, mulp p.toUInt32 v = pw (8 * len2.toInt.toNat) v := by
909 intro v; rw [hpv v, h3, Nat.mul_comm]
910 refine ⟨p, ?_, hp32, ?_, hpv'⟩
911 · unfold crc32_combine_gen64; rw [ite_eq_right hlt]; exact hp
912 · intro h0
913 have := hpv' (e 31)
914 rw [h0, mulp_zero_left] at this
915 exact e_ne_zero 31 (by omega) (pw_eq_zero _ _ this.symm)
916
917theorem combine_op_ok (h : Heap) (crc1 crc2 op : UInt64) (hop : op.toUInt32 ≠ 0) :
918 OkOn h (crc32_combine_op crc1 crc2 op) (mulp op.toUInt32 crc1.toUInt32 ^^^ crc2.toUInt32).toUInt64 := by
919 have hne : ¬ (op == 0) = true := by
920 intro hh
921 have : op = 0 := by simpa using hh
922 exact hop (by rw [this]; rfl)
923 have hb : (crc1 &&& 0xffffffff).toNat < 2 ^ 32 := by
924 rw [and_ffffffff, UInt32.toNat_toUInt64]; exact UInt32.toNat_lt _
925 have hm := multmodp_ok h op (crc1 &&& 0xffffffff) hop hb
926 rw [and_ffffffff] at hm
927 rw [u32_roundtrip] at hm
928 unfold crc32_combine_op
929 rw [ite_eq_right hne, and_ffffffff crc1, and_ffffffff crc2, UInt32.toUInt64_xor]
930 exact OkOn.bind hm (OkOn.pure _ _)
931
932theorem combine64_ok (h : Heap) (hs : StaticsOK h) (crc1 crc2 : UInt64) (len2 : Int64) :
933 OkOn h (crc32_combine64 crc1 crc2 len2)
934 (if len2.toInt < 0 then 0 else (pw (8 * len2.toInt.toNat) crc1.toUInt32 ^^^ crc2.toUInt32).toUInt64) := by
935 unfold crc32_combine64
936 by_cases hneg : len2.toInt < 0
937 · rw [ite_eq_left hneg]
938 refine OkOn.bind (gen64_neg h len2 hneg) ?_
939 unfold crc32_combine_op
940 rw [ite_eq_left (by decide)]; exact OkOn.pure _ _
941 · rw [ite_eq_right hneg]
942 obtain ⟨op, hop, hnz, hlt, hv⟩ := gen64_op h hs len2 (by omega)
943 refine OkOn.bind hop ?_
944 have := combine_op_ok h crc1 crc2 op hlt
945 rw [hv] at this
946 exact this
947
948
949/-! ### `multmodp` does not terminate when the low 32 bits of `a` are zero -/
950
951theorem go_diverges {σ : Type} (cond : σ → M (Bool × σ)) (body : σ → M σ) (Inv : σ → Prop)
952 (hstep : ∀ s, Inv s → ∀ h, cond s h = .ok ((true, s), h) ∧ ∃ s2, body s h = .ok (s2, h) ∧ Inv s2) :
953 ∀ n s h, Inv s → cwhile.go cond body n s h = .error .outOfFuel := by
954 intro n
955 induction n with
956 | zero => intro s h _; simp [cwhile.go]
957 | succ n ih =>
958 intro s h hI
959 obtain ⟨hc, s2, hb, hI2⟩ := hstep s hI h
960 simp [cwhile.go, hc, hb, ih s2 h hI2]
961
962theorem and_low_zero (a m : UInt64) (ha : a.toUInt32 = 0) (hm : m.toNat < 2 ^ 32) : a &&& m = 0 := by
963 apply UInt64.toNat_inj.mp
964 rw [UInt64.toNat_and]
965 have e0 : (0 : UInt64).toNat = 0 := rfl
966 rw [e0]
967 apply Nat.eq_of_testBit_eq
968 intro k
969 rw [Nat.testBit_and, Nat.zero_testBit]
970 by_cases hk : k < 32
971 · have h0 : a.toNat % 2 ^ 32 = 0 := by
972 have := congrArg UInt32.toNat ha
973 rwa [UInt64.toNat_toUInt32] at this
974 have : a.toNat.testBit k = false := by
975 have h1 := Nat.testBit_mod_two_pow a.toNat 32 k
976 rw [h0, Nat.zero_testBit] at h1
977 simpa [hk] using h1.symm
978 simp [this]
979 · have : m.toNat.testBit k = false :=
980 Nat.testBit_lt_two_pow (Nat.lt_of_lt_of_le hm (Nat.pow_le_pow_right (by omega) (by omega)))
981 simp [this]
982
983/-- **multmodp, non-termination.** If the low 32 bits of `a` are zero, the loop of `multmodp` never finds a set bit
984(`a & m` is 0 for every `m <= 2^31`, `m` then becomes 0): the translation runs out of fuel for every `b` and every heap.
985The real C (zlib `develop`) hangs on `crc32_combine_op(1, 2, 1UL << 32)`. -/
986theorem multmodp_diverges (a b : UInt64) (ha : a.toUInt32 = 0) (h : Heap) :
987 multmodp a b h = .error .outOfFuel := by
988 unfold multmodp
989 rw [bind_app]
990 have key : cwhile (fun x : MS => match x with | (m, p, b, brk) => pure (!brk, m, p, b, brk))
991 (fun x : MS => match x with
992 | (m, p, b, brk) =>
993 have p := p;
994 have __do_jp := fun __r p =>
995 have m := m >>> 1;
996 have b := if (b &&& 1 != 0) = true then b >>> 1 ^^^ POLY else b >>> 1;
997 pure (m, p, b, brk);
998 if (a &&& m != 0) = true then
999 have p := p ^^^ b;
1000 if (a &&& m - 1 == 0) = true then pure (m, p, b, true) else __do_jp () p
1001 else __do_jp () p)
1002 ((1 : UInt64) <<< 31, 0, b, false) h = .error .outOfFuel := by
1003 refine go_diverges _ _ (fun s : MS => s.2.2.2 = false ∧ s.1.toNat < 2 ^ 32) ?_ fuel _ h ⟨rfl, ?_⟩
1004 · intro s hI h
1005 obtain ⟨m, p, bb, brk⟩ := s
1006 obtain ⟨h1, h2⟩ := hI
1007 simp only at h1 h2
1008 subst h1
1009 have hz : (a &&& m != 0) = false := by rw [and_low_zero a m ha h2]; rfl
1010 refine ⟨rfl, (m >>> 1, p, (if (bb &&& 1 != 0) = true then bb >>> 1 ^^^ POLY else bb >>> 1), false), ?_, ?_⟩
1011 · simp [hz]
1012 · refine ⟨rfl, ?_⟩
1013 show (m >>> 1).toNat < 2 ^ 32
1014 rw [UInt64.toNat_shiftRight, Nat.shiftRight_eq_div_pow]
1015 exact Nat.lt_of_le_of_lt (Nat.div_le_self _ _) h2
1016 · show ((1 : UInt64) <<< 31).toNat < 2 ^ 32
1017 decide
1018 rw [key]
1019
1020theorem combine_op_diverges (h : Heap) (crc1 crc2 op : UInt64) (hop : op ≠ 0) (hlow : op.toUInt32 = 0) :
1021 crc32_combine_op crc1 crc2 op h = .error .outOfFuel := by
1022 unfold crc32_combine_op
1023 have hne : ¬ (op == 0) = true := by simpa using hop
1024 rw [ite_eq_right hne, bind_app, multmodp_diverges _ _ hlow]
1025
1026/-! ### Linear maps on 64-bit words -/
1027
1028def Lin64 (f : UInt64 → UInt32) : Prop := ∀ x y, f (x ^^^ y) = f x ^^^ f y
1029
1030theorem lin64_ext {f g : UInt64 → UInt32} (hf : Lin64 f) (hg : Lin64 g)
1031 (hb : ∀ t, t < 64 → f (UInt64.ofNat (2 ^ t)) = g (UInt64.ofNat (2 ^ t))) : ∀ x, f x = g x := by
1032 intro x
1033 have hf' : ∀ x y, f (x ^^^ y) = f x ^^^ f y := hf
1034 have hg' : ∀ x y, g (x ^^^ y) = g x ^^^ g y := hg
1035 have key := lin_ext_nat 64 (fun n => (f (UInt64.ofNat n)).toNat) (fun n => (g (UInt64.ofNat n)).toNat)
1036 (by intro a b; simp only [UInt64.ofNat_xor, hf', UInt32.toNat_xor])
1037 (by intro a b; simp only [UInt64.ofNat_xor, hg', UInt32.toNat_xor])
1038 (by intro t ht; rw [hb t ht]) 64 (Nat.le_refl _) x.toNat (by simpa using UInt64.toNat_lt x)
1039 simp only [UInt64.ofNat_toNat] at key
1040 exact UInt32.toNat_inj.mp key
1041
1042/-! ### The word recurrence of `crc_word` -/
1043
1044/-- one step of the loop of `crc_word` -/
1045def wstep (d : UInt64) : UInt64 := (d >>> 8) ^^^ (Crc32H.crc_table[(d &&& 0xff).toNat]!).toUInt64
1046
1047def wsteps : Nat → UInt64 → UInt64
1048 | 0, d => d
1049 | n + 1, d => wstep (wsteps n d)
1050
1051theorem toUInt32_ofNat_eq (x : UInt64) (h : x.toNat < 256) : UInt32.ofNat x.toNat = x.toUInt32 := by
1052 apply UInt32.toNat_inj.mp
1053 rw [UInt32.toNat_ofNat', UInt64.toNat_toUInt32]
1054
1055theorem and255_lt (d : UInt64) : (d &&& 0xff).toNat < 256 := by
1056 rw [UInt64.toNat_and]
1057 have : (0xff : UInt64).toNat = 2 ^ 8 - 1 := by decide
1058 rw [this, Nat.and_two_pow_sub_one_eq_mod]
1059 exact Nat.mod_lt _ (by decide)
1060
1061theorem tab_lookup (d : UInt64) : Crc32H.crc_table[(d &&& 0xff).toNat]! = pw 8 (d &&& 0xff).toUInt32 := by
1062 have := crc_table_eq ⟨(d &&& 0xff).toNat, and255_lt d⟩
1063 simp only at this
1064 rw [this, toUInt32_ofNat_eq _ (and255_lt d)]
1065
1066theorem wstep_xor (a b : UInt64) : wstep (a ^^^ b) = wstep a ^^^ wstep b := by
1067 unfold wstep
1068 rw [UInt64.shiftRight_xor, tab_lookup, tab_lookup, tab_lookup, and_xor64, UInt64.toUInt32_xor, pw_xor,
1069 UInt32.toUInt64_xor]
1070 ac_rfl
1071
1072theorem wsteps_xor (n : Nat) (a b : UInt64) : wsteps n (a ^^^ b) = wsteps n a ^^^ wsteps n b := by
1073 induction n generalizing a b with
1074 | zero => rfl
1075 | succ n ih => simp only [wsteps, ih, wstep_xor]
1076
1077/-- the byte `j` of `d`, as a register value -/
1078def bx (d : UInt64) (j : Nat) : UInt32 := ((d >>> (8 * j).toUInt64) &&& 0xff).toUInt32
1079
1080theorem bx_xor (a b : UInt64) (j : Nat) : bx (a ^^^ b) j = bx a j ^^^ bx b j := by
1081 unfold bx
1082 rw [UInt64.shiftRight_xor, and_xor64, UInt64.toUInt32_xor]
1083
1084/-- the closed form of `crc_word`: byte `j` advanced over `8 - j` bytes -/
1085def psiW (d : UInt64) : Nat → UInt32
1086 | 0 => 0
1087 | m + 1 => psiW d m ^^^ pw (8 * (8 - m)) (bx d m)
1088
1089def Psi (d : UInt64) : UInt32 := psiW d 8
1090
1091theorem psiW_xor (a b : UInt64) (m : Nat) : psiW (a ^^^ b) m = psiW a m ^^^ psiW b m := by
1092 induction m with
1093 | zero => simp [psiW]
1094 | succ m ih => simp only [psiW, ih, bx_xor, pw_xor]; ac_rfl
1095
1096theorem Psi_xor (a b : UInt64) : Psi (a ^^^ b) = Psi a ^^^ Psi b := psiW_xor a b 8
1097
1098theorem Phi_xor (a b : UInt64) : (wsteps 8 (a ^^^ b)).toUInt32 = (wsteps 8 a).toUInt32 ^^^ (wsteps 8 b).toUInt32 := by
1099 rw [wsteps_xor, UInt64.toUInt32_xor]
1100
1101set_option maxRecDepth 100000 in
1102theorem Phi_basis : ∀ t : Fin 64, (wsteps 8 (UInt64.ofNat (2 ^ t.val))).toUInt32 = Psi (UInt64.ofNat (2 ^ t.val)) := by
1103 decide +kernel
1104
1105theorem Phi_eq_Psi (d : UInt64) : (wsteps 8 d).toUInt32 = Psi d :=
1106 lin64_ext (f := fun d => (wsteps 8 d).toUInt32) (g := Psi) Phi_xor Psi_xor (fun t ht => Phi_basis ⟨t, ht⟩) d
1107
1108
1109/-- the 64-bit word that `loadWord` assembles from eight bytes (little endian) -/
1110def wob (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : UInt64 :=
1111 (0 : UInt64) ||| b0.toUInt64 <<< (8 * 0).toUInt64 ||| b1.toUInt64 <<< (8 * 1).toUInt64 ||| b2.toUInt64 <<< (8 * 2).toUInt64 |||
1112 b3.toUInt64 <<< (8 * 3).toUInt64 ||| b4.toUInt64 <<< (8 * 4).toUInt64 ||| b5.toUInt64 <<< (8 * 5).toUInt64 |||
1113 b6.toUInt64 <<< (8 * 6).toUInt64 ||| b7.toUInt64 <<< (8 * 7).toUInt64
1114
1115theorem getLsbD_255 (i : Nat) (h : 8 ≤ i) : (255#32).getLsbD i = false := by
1116 rw [BitVec.getLsbD_ofNat]
1117 simp
1118 intro _
1119 exact Nat.testBit_lt_two_pow (Nat.lt_of_lt_of_le (by decide) (Nat.pow_le_pow_right (by omega) h))
1120
1121theorem bx_wob_0 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 0 = b0.toUInt32 := by
1122 unfold bx
1123 apply u32_ext
1124 intro i hi
1125 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1126 by_cases h8 : i < 8
1127 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1128 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1129 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1130 simp [this]
1131 intro _
1132 exact BitVec.getLsbD_of_ge _ _ (by omega)
1133
1134theorem bx_wob_1 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 1 = b1.toUInt32 := by
1135 unfold bx
1136 apply u32_ext
1137 intro i hi
1138 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1139 by_cases h8 : i < 8
1140 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1141 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1142 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1143 simp [this]
1144 intro _
1145 exact BitVec.getLsbD_of_ge _ _ (by omega)
1146
1147theorem bx_wob_2 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 2 = b2.toUInt32 := by
1148 unfold bx
1149 apply u32_ext
1150 intro i hi
1151 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1152 by_cases h8 : i < 8
1153 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1154 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1155 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1156 simp [this]
1157 intro _
1158 exact BitVec.getLsbD_of_ge _ _ (by omega)
1159
1160theorem bx_wob_3 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 3 = b3.toUInt32 := by
1161 unfold bx
1162 apply u32_ext
1163 intro i hi
1164 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1165 by_cases h8 : i < 8
1166 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1167 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1168 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1169 simp [this]
1170 intro _
1171 exact BitVec.getLsbD_of_ge _ _ (by omega)
1172
1173theorem bx_wob_4 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 4 = b4.toUInt32 := by
1174 unfold bx
1175 apply u32_ext
1176 intro i hi
1177 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1178 by_cases h8 : i < 8
1179 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1180 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1181 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1182 simp [this]
1183 intro _
1184 exact BitVec.getLsbD_of_ge _ _ (by omega)
1185
1186theorem bx_wob_5 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 5 = b5.toUInt32 := by
1187 unfold bx
1188 apply u32_ext
1189 intro i hi
1190 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1191 by_cases h8 : i < 8
1192 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1193 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1194 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1195 simp [this]
1196 intro _
1197 exact BitVec.getLsbD_of_ge _ _ (by omega)
1198
1199theorem bx_wob_6 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 6 = b6.toUInt32 := by
1200 unfold bx
1201 apply u32_ext
1202 intro i hi
1203 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1204 by_cases h8 : i < 8
1205 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1206 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1207 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1208 simp [this]
1209 intro _
1210 exact BitVec.getLsbD_of_ge _ _ (by omega)
1211
1212theorem bx_wob_7 (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) : bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 7 = b7.toUInt32 := by
1213 unfold bx
1214 apply u32_ext
1215 intro i hi
1216 simp [wob, UInt64.toBitVec_or, UInt64.toBitVec_shiftRight, UInt64.toBitVec_shiftLeft]
1217 by_cases h8 : i < 8
1218 · rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1219 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> simp [BitVec.getLsbD_of_ge]
1220 · have : ¬ (255#32).getLsbD i = true := by rw [getLsbD_255 i (by omega)]; decide
1221 simp [this]
1222 intro _
1223 exact BitVec.getLsbD_of_ge _ _ (by omega)
1224
1225set_option maxRecDepth 100000 in
1226theorem Psi_basis : ∀ t : Fin 32, Psi (e t.val).toUInt64 = pw 64 (e t.val) := by decide +kernel
1227
1228theorem Psi_reg (c : UInt32) : Psi c.toUInt64 = pw 64 c :=
1229 lin32_ext (f := fun c => Psi c.toUInt64) (g := pw 64)
1230 (by intro x y; simp only [UInt32.toUInt64_xor, Psi_xor]) (pw_lin 64) (fun t ht => Psi_basis ⟨t, ht⟩) c
1231
1232theorem Psi_wob (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) :
1233 Psi (wob b0 b1 b2 b3 b4 b5 b6 b7) =
1234 pw 64 b0.toUInt32 ^^^ pw 56 b1.toUInt32 ^^^ pw 48 b2.toUInt32 ^^^ pw 40 b3.toUInt32 ^^^
1235 pw 32 b4.toUInt32 ^^^ pw 24 b5.toUInt32 ^^^ pw 16 b6.toUInt32 ^^^ pw 8 b7.toUInt32 := by
1236 have : Psi (wob b0 b1 b2 b3 b4 b5 b6 b7) =
1237 (((((((0 ^^^ pw (8 * (8 - 0)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 0)) ^^^
1238 pw (8 * (8 - 1)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 1)) ^^^
1239 pw (8 * (8 - 2)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 2)) ^^^
1240 pw (8 * (8 - 3)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 3)) ^^^
1241 pw (8 * (8 - 4)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 4)) ^^^
1242 pw (8 * (8 - 5)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 5)) ^^^
1243 pw (8 * (8 - 6)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 6)) ^^^
1244 pw (8 * (8 - 7)) (bx (wob b0 b1 b2 b3 b4 b5 b6 b7) 7) := rfl
1245 rw [this, bx_wob_0, bx_wob_1, bx_wob_2, bx_wob_3, bx_wob_4, bx_wob_5, bx_wob_6, bx_wob_7, UInt32.zero_xor]
1246
1247theorem rbl_bytes (c : UInt32) (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) :
1248 rbl c [b0, b1, b2, b3, b4, b5, b6, b7] =
1249 pw 64 c ^^^ (pw 64 b0.toUInt32 ^^^ pw 56 b1.toUInt32 ^^^ pw 48 b2.toUInt32 ^^^ pw 40 b3.toUInt32 ^^^
1250 pw 32 b4.toUInt32 ^^^ pw 24 b5.toUInt32 ^^^ pw 16 b6.toUInt32 ^^^ pw 8 b7.toUInt32) := by
1251 rw [rbl_affine]
1252 simp only [List.length_cons, List.length_nil]
1253 have : rbl 0 [b0, b1, b2, b3, b4, b5, b6, b7] =
1254 pw 64 b0.toUInt32 ^^^ pw 56 b1.toUInt32 ^^^ pw 48 b2.toUInt32 ^^^ pw 40 b3.toUInt32 ^^^
1255 pw 32 b4.toUInt32 ^^^ pw 24 b5.toUInt32 ^^^ pw 16 b6.toUInt32 ^^^ pw 8 b7.toUInt32 := by
1256 simp only [rbl_cons, rbl_nil, crcByte_eq, UInt32.zero_xor, pw_xor, ← pw_add]
1257 rw [this]
1258
1259/-- the word that `crc_word` is applied to, for the register `c` and the eight bytes: its result is the register after
1260the eight bytes. -/
1261theorem Psi_word (c : UInt32) (b0 b1 b2 b3 b4 b5 b6 b7 : UInt8) :
1262 Psi (c.toUInt64 ^^^ wob b0 b1 b2 b3 b4 b5 b6 b7) = rbl c [b0, b1, b2, b3, b4, b5, b6, b7] := by
1263 rw [Psi_xor, Psi_reg, Psi_wob, rbl_bytes]
1264
1265theorem crc_word_ok (h : Heap) (hs : StaticsOK h) (data : UInt64) : OkOn h (crc_word data) (Psi data) := by
1266 suffices H : ∃ y, OkOn h (crc_word data) y ∧ y = Psi data by obtain ⟨y, hy, rfl⟩ := H; exact hy
1267 unfold crc_word
1268 refine OkOn.bind_ex (R := fun x : UInt64 × Int32 => x.1.toUInt32 = Psi data) ?_ ?_
1269 · refine cwhile_ok (fun cnt s => ∃ i, i + cnt = 8 ∧ s = (wsteps i data, Int32.ofNat i))
1270 (fun x : UInt64 × Int32 => x.1.toUInt32 = Psi data) ?_ ?_ (k := 8) (s := (data, (0 : Int32))) ?_ ?_
1271 · intro cnt s hP
1272 obtain ⟨i, hi, rfl⟩ := hP
1273 have hc : (Int32.ofNat i < (8 : Int32)) = true := by
1274 rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1275 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1276 have hk : Int32.ofNat i + 1 = Int32.ofNat (i + 1) := by
1277 rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
1278 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1279 have ht := crcTab_ok h hs (wsteps i data &&& 0xff) (and255_lt _)
1280 refine ⟨(wsteps i data, Int32.ofNat i), (wsteps (i + 1) data, Int32.ofNat (i + 1)), ?_, ?_, ⟨i + 1, by omega, rfl⟩⟩
1281 · show OkOn h (pure _) _
1282 simp only [W, hc]
1283 exact OkOn.pure _ _
1284 · show OkOn h (do let t ← crcTab (wsteps i data &&& 0xff); pure ((wsteps i data >>> 8) ^^^ t.toUInt64, Int32.ofNat i + 1)) _
1285 rw [hk]
1286 exact OkOn.bind ht (OkOn.pure _ _)
1287 · intro s hP
1288 obtain ⟨i, hi, rfl⟩ := hP
1289 have : i = 8 := by omega
1290 subst this
1291 have hc : (Int32.ofNat 8 < (8 : Int32)) = false := by decide
1292 refine ⟨(wsteps 8 data, Int32.ofNat 8), ?_, ?_⟩
1293 · show OkOn h (pure _) _
1294 simp only [W, hc]
1295 exact OkOn.pure _ _
1296 · exact Phi_eq_Psi data
1297 · exact ⟨0, rfl, rfl⟩
1298 · show 8 < 2 ^ 64; omega
1299 · intro x hx
1300 obtain ⟨d, k⟩ := x
1301 exact ⟨d.toUInt32, OkOn.pure _ _, hx⟩
1302
1303/-! ### Loading a word -/
1304
1305theorem idx8 (h : Heap) (blk off i : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs) (hlt : off + i < bs.size) :
1306 OkOn h (index8 (.ref (.blk blk) off) i) bs[off + i]! := by
1307 have := index8_ok hb hlt
1308 rw [getElem!_pos bs (off + i) hlt]; exact this
1309
1310set_option maxRecDepth 4000 in
1311theorem loadWord_ok (h : Heap) (blk off : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1312 (hlt : off + 8 ≤ bs.size) :
1313 OkOn h (loadWord (.ref (.blk blk) off))
1314 (wob bs[off + 0]! bs[off + 1]! bs[off + 2]! bs[off + 3]! bs[off + 4]! bs[off + 5]! bs[off + 6]! bs[off + 7]!) := by
1315 unfold loadWord
1316 simp only [forIn, Std.Legacy.Range.forIn'_eq_forIn'_range', Std.Legacy.Range.size]
1317 simp only [List.range'_succ, List.range'_zero, List.forIn'_cons, List.forIn'_nil]
1318 refine OkOn.bind (x := wob bs[off + 0]! bs[off + 1]! bs[off + 2]! bs[off + 3]! bs[off + 4]! bs[off + 5]! bs[off + 6]! bs[off + 7]!) ?_ (OkOn.pure _ _)
1319 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1320 try dsimp only
1321 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1322 try dsimp only
1323 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1324 try dsimp only
1325 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1326 try dsimp only
1327 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1328 try dsimp only
1329 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1330 try dsimp only
1331 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1332 try dsimp only
1333 refine OkOn.bind (x := ForInStep.yield _) (OkOn.bind (idx8 h blk off _ bs hb (by omega)) (OkOn.pure _ _)) ?_
1334 try dsimp only
1335 exact OkOn.pure _ _
1336
1337/-! ### The braid tables -/
1338
1339def psum (d : UInt64) : Nat → UInt32
1340 | 0 => 0
1341 | m + 1 => psum d m ^^^ pw (8 * (40 - m)) (bx d m)
1342
1343/-- the effect of the eight table lookups on a 64-bit word -/
1344def bw (d : UInt64) : UInt32 := psum d 8
1345
1346theorem idx2_ok (h : Heap) (kn i : Nat) (hk : kn < 8) (hi : i < 256) :
1347 OkOn h (idx2 Crc32H.crc_braid_table kn i) Crc32H.crc_braid_table[kn]![i]! := by
1348 have hs := braid_size
1349 have hrow : Crc32H.crc_braid_table[kn]? = some Crc32H.crc_braid_table[kn]! := by
1350 rw [getElem!_pos Crc32H.crc_braid_table kn (by omega)]; simp
1351 have hrs := hs.2 ⟨kn, hk⟩
1352 have hcol : Crc32H.crc_braid_table[kn]![i]? = some Crc32H.crc_braid_table[kn]![i]! := by
1353 rw [getElem!_pos Crc32H.crc_braid_table[kn]! i (by simp only at hrs; omega)]; simp
1354 unfold idx2
1355 rw [hrow]
1356 simp only
1357 rw [hcol]
1358 exact OkOn.pure _ _
1359
1360theorem braidTab_ok (h : Heap) (kn : Nat) (hk : kn < 8) (word : UInt64) :
1361 OkOn h (braidTab (Int32.ofNat kn) word) (pw (8 * (40 - kn)) (bx word kn)) := by
1362 have h3 : (Int32.ofNat kn <<< 3).toInt.toNat.toUInt64 = (8 * kn).toUInt64 := by
1363 rcases (by omega : kn = 0 ∨ kn = 1 ∨ kn = 2 ∨ kn = 3 ∨ kn = 4 ∨ kn = 5 ∨ kn = 6 ∨ kn = 7) with
1364 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1365 have hk' : (Int32.ofNat kn).toInt.toNat = kn := by
1366 rcases (by omega : kn = 0 ∨ kn = 1 ∨ kn = 2 ∨ kn = 3 ∨ kn = 4 ∨ kn = 5 ∨ kn = 6 ∨ kn = 7) with
1367 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1368 have h64 : (8 * kn).toUInt64 < 64 := by
1369 rcases (by omega : kn = 0 ∨ kn = 1 ∨ kn = 2 ∨ kn = 3 ∨ kn = 4 ∨ kn = 5 ∨ kn = 6 ∨ kn = 7) with
1370 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1371 have hsh : OkOn h (shr64 word (8 * kn).toUInt64) (word >>> (8 * kn).toUInt64) := by
1372 unfold shr64; rw [ite_eq_left h64]; exact OkOn.pure _ _
1373 have hid := idx2_ok h kn ((word >>> (8 * kn).toUInt64) &&& 0xff).toNat hk (and255_lt _)
1374 have hv : Crc32H.crc_braid_table[kn]![((word >>> (8 * kn).toUInt64) &&& 0xff).toNat]! =
1375 pw (8 * (40 - kn)) (bx word kn) := by
1376 have := braid_eq ⟨kn, hk⟩ ⟨_, and255_lt (word >>> (8 * kn).toUInt64)⟩
1377 simp only at this
1378 rw [this, toUInt32_ofNat_eq _ (and255_lt _)]; rfl
1379 unfold braidTab
1380 rw [h3, hk']
1381 rw [hv] at hid
1382 exact OkOn.bind hsh hid
1383
1384theorem psum_eq (d : UInt64) : ∀ m, m ≤ 8 → psum d m = pw 256 (psiW d m) := by
1385 intro m
1386 induction m with
1387 | zero => intro _; simp [psum, psiW, pw_zero]
1388 | succ m ih =>
1389 intro hm
1390 simp only [psum, psiW, ih (by omega), pw_xor, ← pw_add]
1391 congr 2; omega
1392
1393theorem bw_eq (d : UInt64) : bw d = pw 256 (Psi d) := psum_eq d 8 (Nat.le_refl _)
1394
1395/-- the word at byte offset `o` of `bs` -/
1396def WA (bs : Array UInt8) (o : Nat) : UInt64 :=
1397 wob bs[o + 0]! bs[o + 1]! bs[o + 2]! bs[o + 3]! bs[o + 4]! bs[o + 5]! bs[o + 6]! bs[o + 7]!
1398
1399theorem wordAt_ok (h : Heap) (blk ow i : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1400 (hlt : ow + 8 * i + 8 ≤ bs.size) :
1401 OkOn h (wordAt (.ref (.blk blk) ow) i) (WA bs (ow + 8 * i)) := by
1402 unfold wordAt
1403 have hadd : OkOn h (Ptr.add (Ptr.ref (BRef.blk blk) ow) (8 * (i : Int))) (Ptr.ref (BRef.blk blk) (ow + 8 * i)) := by
1404 have := Ptr.add_ok (r := BRef.blk blk) (off := ow) (k := 8 * (i : Int)) (a := bs) hb (by omega) (by omega)
1405 have e : ((ow : Int) + 8 * (i : Int)).toNat = ow + 8 * i := by omega
1406 rwa [e] at this
1407 exact OkOn.bind hadd (loadWord_ok h blk (ow + 8 * i) bs hb hlt)
1408
1409theorem bind_of {α β : Type} {h : Heap} {m : M α} {f : α → M β} {y : β} (R : α → Prop)
1410 (hm : ∃ x, OkOn h m x ∧ R x) (hf : ∀ x, R x → OkOn h (f x) y) : OkOn h (m >>= f) y := by
1411 obtain ⟨x, hx, hR⟩ := hm
1412 exact OkOn.bind hx (hf x hR)
1413
1414theorem blockLittle_ok (h : Heap) (s : BL) (blk ow : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1415 (hlt : ow + 40 ≤ bs.size) (hw : s.words = .ref (.blk blk) ow) :
1416 OkOn h (blockLittle s)
1417 { crc0 := bw (s.crc0.toUInt64 ^^^ WA bs ow), crc1 := bw (s.crc1.toUInt64 ^^^ WA bs (ow + 8)),
1418 crc2 := bw (s.crc2.toUInt64 ^^^ WA bs (ow + 16)), crc3 := bw (s.crc3.toUInt64 ^^^ WA bs (ow + 24)),
1419 crc4 := bw (s.crc4.toUInt64 ^^^ WA bs (ow + 32)), words := .ref (.blk blk) (ow + 40), blks := s.blks } := by
1420 have w0 := wordAt_ok h blk ow 0 bs hb (by omega)
1421 have w1 := wordAt_ok h blk ow 1 bs hb (by omega)
1422 have w2 := wordAt_ok h blk ow 2 bs hb (by omega)
1423 have w3 := wordAt_ok h blk ow 3 bs hb (by omega)
1424 have w4 := wordAt_ok h blk ow 4 bs hb (by omega)
1425 simp only [Nat.mul_zero, Nat.add_zero] at w0
1426 have hadd : OkOn h (Ptr.add s.words 40) (Ptr.ref (BRef.blk blk) (ow + 40)) := by
1427 rw [hw]
1428 have := Ptr.add_nat_ok (r := BRef.blk blk) (off := ow) (n := 40) (a := bs) hb hlt
1429 simpa using this
1430 rw [← hw] at w0 w1 w2 w3 w4
1431 unfold blockLittle
1432 refine OkOn.bind w0 ?_
1433 try dsimp only
1434 refine OkOn.bind w1 ?_
1435 try dsimp only
1436 refine OkOn.bind w2 ?_
1437 try dsimp only
1438 refine OkOn.bind w3 ?_
1439 try dsimp only
1440 refine OkOn.bind w4 ?_
1441 try dsimp only
1442 refine OkOn.bind hadd ?_
1443 try dsimp only
1444 refine OkOn.bind (braidTab_ok h 0 (by omega) _) ?_
1445 try dsimp only
1446 refine OkOn.bind (braidTab_ok h 0 (by omega) _) ?_
1447 try dsimp only
1448 refine OkOn.bind (braidTab_ok h 0 (by omega) _) ?_
1449 try dsimp only
1450 refine OkOn.bind (braidTab_ok h 0 (by omega) _) ?_
1451 try dsimp only
1452 refine OkOn.bind (braidTab_ok h 0 (by omega) _) ?_
1453 try dsimp only
1454 generalize hW0 : s.crc0.toUInt64 ^^^ WA bs (ow + 8 * 0) = W0
1455 generalize hW1 : s.crc1.toUInt64 ^^^ WA bs (ow + 8 * 1) = W1
1456 generalize hW2 : s.crc2.toUInt64 ^^^ WA bs (ow + 8 * 2) = W2
1457 generalize hW3 : s.crc3.toUInt64 ^^^ WA bs (ow + 8 * 3) = W3
1458 generalize hW4 : s.crc4.toUInt64 ^^^ WA bs (ow + 8 * 4) = W4
1459 refine bind_of (fun s => s = (bw W0, bw W1, bw W2, bw W3, bw W4, Int32.ofNat 8)) ?_ ?_
1460 · refine cwhile_ok (fun cnt s => ∃ m, m + cnt = 8 ∧ 1 ≤ m ∧
1461 s = (psum W0 m, psum W1 m, psum W2 m, psum W3 m, psum W4 m, Int32.ofNat m))
1462 (fun s => s = (bw W0, bw W1, bw W2, bw W3, bw W4, Int32.ofNat 8)) ?_ ?_ (k := 7)
1463 (s := (pw (8 * (40 - 0)) (bx W0 0), pw (8 * (40 - 0)) (bx W1 0), pw (8 * (40 - 0)) (bx W2 0),
1464 pw (8 * (40 - 0)) (bx W3 0), pw (8 * (40 - 0)) (bx W4 0), 1)) ?_ ?_
1465 · intro cnt s hP
1466 obtain ⟨m, hm, hm1, rfl⟩ := hP
1467 have hc : (Int32.ofNat m < W) = true := by
1468 rcases (by omega : m = 1 ∨ m = 2 ∨ m = 3 ∨ m = 4 ∨ m = 5 ∨ m = 6 ∨ m = 7) with
1469 rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1470 have hk : Int32.ofNat m + 1 = Int32.ofNat (m + 1) := by
1471 rcases (by omega : m = 1 ∨ m = 2 ∨ m = 3 ∨ m = 4 ∨ m = 5 ∨ m = 6 ∨ m = 7) with
1472 rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
1473 refine ⟨(psum W0 m, psum W1 m, psum W2 m, psum W3 m, psum W4 m, Int32.ofNat m), (psum W0 (m + 1), psum W1 (m + 1), psum W2 (m + 1), psum W3 (m + 1), psum W4 (m + 1),
1474 Int32.ofNat (m + 1)), ?_, ?_, ⟨m + 1, by omega, by omega, rfl⟩⟩
1475 · show OkOn h (pure _) _
1476 simp only [hc]
1477 exact OkOn.pure _ _
1478 · show OkOn h (do
1479 let a0 ← braidTab (Int32.ofNat m) W0
1480 let a1 ← braidTab (Int32.ofNat m) W1
1481 let a2 ← braidTab (Int32.ofNat m) W2
1482 let a3 ← braidTab (Int32.ofNat m) W3
1483 let a4 ← braidTab (Int32.ofNat m) W4
1484 pure (psum W0 m ^^^ a0, psum W1 m ^^^ a1, psum W2 m ^^^ a2, psum W3 m ^^^ a3, psum W4 m ^^^ a4,
1485 Int32.ofNat m + 1)) _
1486 rw [hk]
1487 exact OkOn.bind (braidTab_ok h m (by omega) _) (OkOn.bind (braidTab_ok h m (by omega) _)
1488 (OkOn.bind (braidTab_ok h m (by omega) _) (OkOn.bind (braidTab_ok h m (by omega) _)
1489 (OkOn.bind (braidTab_ok h m (by omega) _) (OkOn.pure _ _)))))
1490 · intro s hP
1491 obtain ⟨m, hm, hm1, rfl⟩ := hP
1492 have : m = 8 := by omega
1493 subst this
1494 have hc : (Int32.ofNat 8 < W) = false := by decide
1495 refine ⟨_, ?_, rfl⟩
1496 · show OkOn h (pure _) _
1497 simp only [hc]
1498 exact OkOn.pure _ _
1499 · exact ⟨1, by omega, Nat.le_refl _, by simp [psum]⟩
1500 · show 7 < 2 ^ 64; omega
1501 · intro x hx
1502 subst hx
1503 exact OkOn.pure _ _
1504
1505/-! ### The register over a range of a byte array -/
1506
1507/-- the register after `n` bytes of `bs` from index `o`, starting from `c` -/
1508def rbFrom (bs : Array UInt8) : UInt32 → Nat → Nat → UInt32
1509 | c, _, 0 => c
1510 | c, o, n + 1 => rbFrom bs (ZlibF.Spec.crcByte c bs[o]!) (o + 1) n
1511
1512theorem rbFrom_zero (bs : Array UInt8) (c : UInt32) (o : Nat) : rbFrom bs c o 0 = c := rfl
1513theorem rbFrom_succ (bs : Array UInt8) (c : UInt32) (o n : Nat) :
1514 rbFrom bs c o (n + 1) = rbFrom bs (ZlibF.Spec.crcByte c bs[o]!) (o + 1) n := rfl
1515
1516theorem rbFrom_add (bs : Array UInt8) (n m : Nat) : ∀ (c : UInt32) (o : Nat),
1517 rbFrom bs c o (n + m) = rbFrom bs (rbFrom bs c o n) (o + n) m := by
1518 induction n with
1519 | zero => intro c o; simp [rbFrom_zero]
1520 | succ n ih =>
1521 intro c o
1522 rw [Nat.succ_add, rbFrom_succ, ih, rbFrom_succ]
1523 rw [show o + 1 + n = o + (n + 1) by omega]
1524
1525theorem rbFrom_aff (bs : Array UInt8) (n : Nat) : ∀ (c : UInt32) (o : Nat),
1526 rbFrom bs c o n = pw (8 * n) c ^^^ rbFrom bs 0 o n := by
1527 induction n with
1528 | zero => intro c o; simp [rbFrom_zero, pw]
1529 | succ n ih =>
1530 intro c o
1531 rw [rbFrom_succ, rbFrom_succ, ih, ih (ZlibF.Spec.crcByte 0 bs[o]!)]
1532 simp only [crcByte_eq, UInt32.zero_xor, pw_xor]
1533 rw [show 8 * (n + 1) = 8 * n + 8 by omega, pw_add]
1534 ac_rfl
1535
1536theorem rbFrom_list (bs : Array UInt8) (n : Nat) : ∀ (c : UInt32) (o : Nat), o + n ≤ bs.size →
1537 rbFrom bs c o n = rbl c ((bs.toList.drop o).take n) := by
1538 induction n with
1539 | zero => intro c o _; simp [rbFrom_zero, rbl_nil]
1540 | succ n ih =>
1541 intro c o h
1542 have ho : o < bs.size := by omega
1543 have : (bs.toList.drop o) = bs[o] :: bs.toList.drop (o + 1) := by
1544 rw [List.drop_eq_getElem_cons (by simpa using ho)]; simp
1545 rw [rbFrom_succ, ih _ _ (by omega), this, List.take_succ_cons, rbl_cons]
1546 rw [getElem!_pos bs o ho]
1547
1548theorem rbFrom8 (bs : Array UInt8) (c : UInt32) (o : Nat) :
1549 rbFrom bs c o 8 = rbl c [bs[o + 0]!, bs[o + 1]!, bs[o + 2]!, bs[o + 3]!, bs[o + 4]!, bs[o + 5]!, bs[o + 6]!, bs[o + 7]!] := rfl
1550
1551theorem Psi_WA (c : UInt32) (bs : Array UInt8) (o : Nat) : Psi (c.toUInt64 ^^^ WA bs o) = rbFrom bs c o 8 := by
1552 rw [rbFrom8]; exact Psi_word c _ _ _ _ _ _ _ _
1553
1554theorem rbFrom8_xor (bs : Array UInt8) (a b : UInt32) (o : Nat) :
1555 rbFrom bs (a ^^^ b) o 8 = rbFrom bs a o 8 ^^^ pw 64 b := by
1556 rw [rbFrom_aff bs 8 (a ^^^ b), rbFrom_aff bs 8 a, pw_xor]
1557 simp only [Nat.reduceMul]
1558 ac_rfl
1559
1560/-! ### The five-word chain at the end of the braided loop -/
1561
1562def Val (c0 c1 c2 c3 c4 : UInt32) : UInt32 :=
1563 pw 320 c0 ^^^ pw 256 c1 ^^^ pw 192 c2 ^^^ pw 128 c3 ^^^ pw 64 c4
1564
1565def chain (bs : Array UInt8) (c0 c1 c2 c3 c4 : UInt32) (o : Nat) : UInt32 :=
1566 rbFrom bs (c4 ^^^ rbFrom bs (c3 ^^^ rbFrom bs (c2 ^^^ rbFrom bs (c1 ^^^ rbFrom bs c0 o 8) (o + 8) 8) (o + 16) 8)
1567 (o + 24) 8) (o + 32) 8
1568
1569theorem chain_aff (bs : Array UInt8) (c0 c1 c2 c3 c4 : UInt32) (o : Nat) :
1570 chain bs c0 c1 c2 c3 c4 o = Val c0 c1 c2 c3 c4 ^^^ chain bs 0 0 0 0 0 o := by
1571 unfold chain Val
1572 simp only [rbFrom8_xor]
1573 rw [rbFrom_aff bs 8 c0 o, rbFrom_aff bs 8 c1 (o + 8), rbFrom_aff bs 8 c2 (o + 16), rbFrom_aff bs 8 c3 (o + 24),
1574 rbFrom_aff bs 8 c4 (o + 32)]
1575 simp only [pw_xor, Nat.reduceMul, ← pw_add, Nat.reduceAdd]
1576 ac_rfl
1577
1578theorem chain_zero (bs : Array UInt8) (o : Nat) : chain bs 0 0 0 0 0 o = rbFrom bs 0 o 40 := by
1579 unfold chain
1580 simp only [UInt32.zero_xor]
1581 have e1 : ∀ c o, rbFrom bs c o 40 = rbFrom bs (rbFrom bs c o 8) (o + 8) 32 := fun c o => rbFrom_add bs 8 32 c o
1582 have e2 : ∀ c o, rbFrom bs c o 32 = rbFrom bs (rbFrom bs c o 8) (o + 8) 24 := fun c o => rbFrom_add bs 8 24 c o
1583 have e3 : ∀ c o, rbFrom bs c o 24 = rbFrom bs (rbFrom bs c o 8) (o + 8) 16 := fun c o => rbFrom_add bs 8 16 c o
1584 have e4 : ∀ c o, rbFrom bs c o 16 = rbFrom bs (rbFrom bs c o 8) (o + 8) 8 := fun c o => rbFrom_add bs 8 8 c o
1585 rw [e1, e2, e3, e4]
1586
1587set_option maxRecDepth 100000 in
1588/-- `Val` after one pass of the braids: `pw 320` of the chain result -/
1589theorem val_step (bs : Array UInt8) (c0 c1 c2 c3 c4 : UInt32) (o : Nat) :
1590 Val (pw 256 (rbFrom bs c0 o 8)) (pw 256 (rbFrom bs c1 (o + 8) 8)) (pw 256 (rbFrom bs c2 (o + 16) 8))
1591 (pw 256 (rbFrom bs c3 (o + 24) 8)) (pw 256 (rbFrom bs c4 (o + 32) 8)) = pw 320 (chain bs c0 c1 c2 c3 c4 o) := by
1592 unfold chain Val
1593 simp only [rbFrom8_xor, pw_xor, Nat.reduceMul, ← pw_add, Nat.reduceAdd]
1594 ac_rfl
1595
1596/-! ### One byte of the table-driven CRC -/
1597
1598set_option maxRecDepth 100000 in
1599theorem pw8_basis : ∀ t : Fin 32,
1600 pw 8 (e t.val) = (e t.val >>> 8) ^^^ pw 8 (e t.val &&& 0xff) := by decide +kernel
1601
1602theorem pw8_split (v : UInt32) : pw 8 v = (v >>> 8) ^^^ pw 8 (v &&& 0xff) :=
1603 lin32_ext (f := pw 8) (g := fun v => (v >>> 8) ^^^ pw 8 (v &&& 0xff)) (pw_lin 8)
1604 (by intro x y; simp only [UInt32.shiftRight_xor, and_xor32, pw_xor]; ac_rfl)
1605 (fun t ht => pw8_basis ⟨t, ht⟩) v
1606
1607theorem wstep_u32 (v : UInt32) : wstep v.toUInt64 = (pw 8 v).toUInt64 := by
1608 unfold wstep
1609 rw [tab_lookup, pw8_split v]
1610 have h1 : (v.toUInt64 &&& 0xff).toUInt32 = v &&& 0xff := by
1611 apply UInt32.toNat_inj.mp
1612 rw [UInt64.toNat_toUInt32, UInt64.toNat_and, UInt32.toNat_and, UInt32.toNat_toUInt64]
1613 have : (0xff : UInt64).toNat = 255 := by decide
1614 have h2 : (0xff : UInt32).toNat = 255 := by decide
1615 rw [this, h2]
1616 exact Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt Nat.and_le_right (by decide))
1617 have h2 : v.toUInt64 >>> 8 = (v >>> 8).toUInt64 := by
1618 apply UInt64.toNat_inj.mp
1619 rw [UInt64.toNat_shiftRight, UInt32.toNat_toUInt64, UInt32.toNat_toUInt64, UInt32.toNat_shiftRight]
1620 rfl
1621 rw [h1, h2, UInt32.toUInt64_xor]
1622
1623theorem u8_u32_u64 (b : UInt8) : b.toUInt32.toUInt64 = b.toUInt64 := by
1624 apply UInt64.toNat_inj.mp
1625 simp
1626
1627theorem crcByte_ok (h : Heap) (hs : StaticsOK h) (blk o : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1628 (ho : o < bs.size) (c : UInt32) :
1629 OkOn h (crcByte c.toUInt64 (.ref (.blk blk) o))
1630 ((ZlibF.Spec.crcByte c bs[o]!).toUInt64, Ptr.ref (BRef.blk blk) (o + 1)) := by
1631 unfold crcByte
1632 have hl := load8_ok hb ho
1633 have hadd := Ptr.add_nat_ok (r := BRef.blk blk) (off := o) (n := 1) (a := bs) hb (by omega)
1634 have ht := crcTab_ok h hs ((c.toUInt64 ^^^ bs[o].toUInt64) &&& 0xff) (and255_lt _)
1635 have e : ((c.toUInt64 >>> 8) ^^^ Crc32H.crc_table[((c.toUInt64 ^^^ bs[o].toUInt64) &&& 0xff).toNat]!.toUInt64) =
1636 (ZlibF.Spec.crcByte c bs[o]).toUInt64 := by
1637 have := wstep_u32 (c ^^^ bs[o].toUInt32)
1638 rw [UInt32.toUInt64_xor, u8_u32_u64] at this
1639 unfold wstep at this
1640 have h8 : bs[o].toUInt64 >>> 8 = 0 := by
1641 apply UInt64.toNat_inj.mp
1642 rw [UInt64.toNat_shiftRight, Nat.shiftRight_eq_div_pow]
1643 have := UInt8.toNat_lt bs[o]
1644 simp
1645 omega
1646 rw [UInt64.shiftRight_xor, h8, UInt64.xor_zero] at this
1647 rw [crcByte_eq, this]
1648 rw [getElem!_pos bs o ho]
1649 rw [← e]
1650 exact OkOn.bind hl (OkOn.bind hadd (OkOn.bind ht (OkOn.pure _ _)))
1651
1652/-! ### The tail of `crc32_z`: the loops over eight bytes and over single bytes -/
1653
1654def cond8 (x : UInt64 × BPtr × UInt64) : M (Bool × UInt64 × BPtr × UInt64) :=
1655 match x with
1656 | (c, b, l) => pure (decide (l ≥ 8), c, b, l)
1657
1658def body8 (x : UInt64 × BPtr × UInt64) : M (UInt64 × BPtr × UInt64) :=
1659 match x with
1660 | (c, b, l) =>
1661 have l := l - 8
1662 do
1663 let __x ← crcByte c b
1664 match __x with
1665 | (c, b) => do
1666 let __x ← crcByte c b
1667 match __x with
1668 | (c, b) => do
1669 let __x ← crcByte c b
1670 match __x with
1671 | (c, b) => do
1672 let __x ← crcByte c b
1673 match __x with
1674 | (c, b) => do
1675 let __x ← crcByte c b
1676 match __x with
1677 | (c, b) => do
1678 let __x ← crcByte c b
1679 match __x with
1680 | (c, b) => do
1681 let __x ← crcByte c b
1682 match __x with
1683 | (c, b) => do
1684 let __x ← crcByte c b
1685 match __x with
1686 | (c, b) => pure (c, b, l)
1687
1688def cond1 (x : UInt64 × BPtr × UInt64) : M (Bool × UInt64 × BPtr × UInt64) :=
1689 match x with
1690 | (c, b, l) => pure (l != 0, c, b, l)
1691
1692def body1 (x : UInt64 × BPtr × UInt64) : M (UInt64 × BPtr × UInt64) :=
1693 match x with
1694 | (c, b, l) =>
1695 have l := l - 1
1696 do
1697 let __x ← crcByte c b
1698 match __x with
1699 | (c, b) => pure (c, b, l)
1700
1701/-- the end of `crc32_z` (the local join point of the translation) -/
1702def tailP (crc : UInt64) (buf : BPtr) (len : UInt64) : M UInt64 := do
1703 let __x ← cwhile cond8 body8 (crc, buf, len)
1704 match __x with
1705 | (c, b, l) => do
1706 let __x ← cwhile cond1 body1 (c, b, l)
1707 match __x with
1708 | (c, _, _) => pure (c ^^^ 4294967295)
1709
1710theorem body1_ok (h : Heap) (hs : StaticsOK h) (blk o : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1711 (ho : o < bs.size) (c : UInt32) (l : UInt64) :
1712 OkOn h (body1 (c.toUInt64, .ref (.blk blk) o, l))
1713 ((ZlibF.Spec.crcByte c bs[o]!).toUInt64, Ptr.ref (BRef.blk blk) (o + 1), l - 1) := by
1714 unfold body1
1715 exact OkOn.bind (crcByte_ok h hs blk o bs hb ho c) (OkOn.pure _ _)
1716
1717theorem body8_ok (h : Heap) (hs : StaticsOK h) (blk o : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1718 (ho : o + 8 ≤ bs.size) (c : UInt32) (l : UInt64) :
1719 OkOn h (body8 (c.toUInt64, .ref (.blk blk) o, l))
1720 ((rbFrom bs c o 8).toUInt64, Ptr.ref (BRef.blk blk) (o + 8), l - 8) := by
1721 unfold body8
1722 refine OkOn.bind (crcByte_ok h hs blk o bs hb (by omega) c) ?_
1723 refine OkOn.bind (crcByte_ok h hs blk (o + 1) bs hb (by omega) _) ?_
1724 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1) bs hb (by omega) _) ?_
1725 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1 + 1) bs hb (by omega) _) ?_
1726 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1 + 1 + 1) bs hb (by omega) _) ?_
1727 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1 + 1 + 1 + 1) bs hb (by omega) _) ?_
1728 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1 + 1 + 1 + 1 + 1) bs hb (by omega) _) ?_
1729 refine OkOn.bind (crcByte_ok h hs blk (o + 1 + 1 + 1 + 1 + 1 + 1 + 1) bs hb (by omega) _) ?_
1730 exact OkOn.pure _ _
1731
1732theorem rbFrom_snoc (bs : Array UInt8) (c : UInt32) (o j : Nat) :
1733 rbFrom bs c o (j + 1) = ZlibF.Spec.crcByte (rbFrom bs c o j) bs[o + j]! := by
1734 have := rbFrom_add bs j 1 c o
1735 rw [this]; rfl
1736
1737theorem ofNat_sub64 (n k : Nat) (hk : k ≤ n) (hn : n < 2 ^ 64) :
1738 UInt64.ofNat n - UInt64.ofNat k = UInt64.ofNat (n - k) := by
1739 apply UInt64.toNat_inj.mp
1740 rw [UInt64.toNat_sub_of_le, UInt64.toNat_ofNat', UInt64.toNat_ofNat', UInt64.toNat_ofNat',
1741 Nat.mod_eq_of_lt hn, Nat.mod_eq_of_lt (by omega), Nat.mod_eq_of_lt (by omega)]
1742 rw [UInt64.le_iff_toNat_le, UInt64.toNat_ofNat', UInt64.toNat_ofNat', Nat.mod_eq_of_lt hn,
1743 Nat.mod_eq_of_lt (by omega)]
1744 exact hk
1745
1746theorem ofNat_toNat_lt (n : Nat) (hn : n < 2 ^ 64) : (UInt64.ofNat n).toNat = n := by
1747 rw [UInt64.toNat_ofNat', Nat.mod_eq_of_lt hn]
1748
1749theorem loop1_ok (h : Heap) (hs : StaticsOK h) (blk o L : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1750 (hle : o + L ≤ bs.size) (hL : L < 2 ^ 64) (c : UInt32) :
1751 OkOn h (cwhile cond1 body1 (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L))
1752 ((rbFrom bs c o L).toUInt64, Ptr.ref (BRef.blk blk) (o + L), 0) := by
1753 suffices H : ∃ s, OkOn h (cwhile cond1 body1 (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) s ∧
1754 s = ((rbFrom bs c o L).toUInt64, Ptr.ref (BRef.blk blk) (o + L), 0) by
1755 obtain ⟨s, hs', rfl⟩ := H; exact hs'
1756 refine cwhile_ok (fun k s => ∃ j, j + k = L ∧
1757 s = ((rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j))) _ ?_ ?_ (k := L)
1758 (s := (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) ⟨0, by omega, by simp [rbFrom_zero]⟩ hL
1759 · intro k s hP
1760 obtain ⟨j, hj, rfl⟩ := hP
1761 have hne : (UInt64.ofNat (L - j) != 0) = true := by
1762 have : UInt64.ofNat (L - j) ≠ 0 := by
1763 intro h0
1764 have := congrArg UInt64.toNat h0
1765 rw [ofNat_toNat_lt _ (by omega)] at this
1766 have h1 : (0 : UInt64).toNat = 0 := rfl
1767 omega
1768 simpa using this
1769 refine ⟨((rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j)), ((ZlibF.Spec.crcByte (rbFrom bs c o j) bs[o + j]!).toUInt64, Ptr.ref (BRef.blk blk) (o + j + 1),
1770 UInt64.ofNat (L - j) - 1), ?_, ?_, ?_⟩
1771 · show OkOn h (pure _) _
1772 simp only [hne]
1773 exact OkOn.pure _ _
1774 · exact body1_ok h hs blk (o + j) bs hb (by omega) _ _
1775 · refine ⟨j + 1, by omega, ?_⟩
1776 rw [rbFrom_snoc, show (1 : UInt64) = UInt64.ofNat 1 from rfl, ofNat_sub64 _ _ (by omega) (by omega)]
1777 rw [show L - j - 1 = L - (j + 1) by omega]
1778 rfl
1779 · intro s hP
1780 obtain ⟨j, hj, rfl⟩ := hP
1781 have hjL : L = j := by omega
1782 subst hjL
1783 refine ⟨((rbFrom bs c o L).toUInt64, Ptr.ref (BRef.blk blk) (o + L), UInt64.ofNat (L - L)), ?_, ?_⟩
1784 · show OkOn h (pure _) _
1785 simp only [Nat.sub_self]
1786 exact OkOn.pure _ _
1787 · simp [Nat.sub_self]
1788
1789theorem loop8_ok (h : Heap) (hs : StaticsOK h) (blk o L : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1790 (hle : o + L ≤ bs.size) (hL : L < 2 ^ 64) (c : UInt32) :
1791 OkOn h (cwhile cond8 body8 (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L))
1792 ((rbFrom bs c o (8 * (L / 8))).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * (L / 8)),
1793 UInt64.ofNat (L - 8 * (L / 8))) := by
1794 suffices H : ∃ s, OkOn h (cwhile cond8 body8 (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) s ∧
1795 s = ((rbFrom bs c o (8 * (L / 8))).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * (L / 8)),
1796 UInt64.ofNat (L - 8 * (L / 8))) by
1797 obtain ⟨s, hs', rfl⟩ := H; exact hs'
1798 refine cwhile_ok (fun k s => ∃ j, j + k = L / 8 ∧
1799 s = ((rbFrom bs c o (8 * j)).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * j), UInt64.ofNat (L - 8 * j))) _ ?_ ?_
1800 (k := L / 8) (s := (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) ⟨0, by omega, by simp [rbFrom_zero]⟩
1801 (by show L / 8 < 2 ^ 64; omega)
1802 · intro k s hP
1803 obtain ⟨j, hj, rfl⟩ := hP
1804 have hge : (decide (UInt64.ofNat (L - 8 * j) ≥ 8)) = true := by
1805 simp only [decide_eq_true_eq, ge_iff_le, UInt64.le_iff_toNat_le]
1806 rw [ofNat_toNat_lt _ (by omega)]
1807 have : (8 : UInt64).toNat = 8 := rfl
1808 omega
1809 refine ⟨((rbFrom bs c o (8 * j)).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * j), UInt64.ofNat (L - 8 * j)),
1810 ((rbFrom bs (rbFrom bs c o (8 * j)) (o + 8 * j) 8).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * j + 8),
1811 UInt64.ofNat (L - 8 * j) - 8), ?_, ?_, ?_⟩
1812 · show OkOn h (pure _) _
1813 simp only [hge]
1814 exact OkOn.pure _ _
1815 · exact body8_ok h hs blk (o + 8 * j) bs hb (by omega) _ _
1816 · refine ⟨j + 1, by omega, ?_⟩
1817 rw [show 8 * (j + 1) = 8 * j + 8 by omega, rbFrom_add,
1818 show (8 : UInt64) = UInt64.ofNat 8 from rfl, ofNat_sub64 _ _ (by omega) (by omega)]
1819 rw [show L - 8 * j - 8 = L - (8 * j + 8) by omega]
1820 rfl
1821 · intro s hP
1822 obtain ⟨j, hj, rfl⟩ := hP
1823 have hjq : j = L / 8 := by omega
1824 have hlt : (decide (UInt64.ofNat (L - 8 * j) ≥ 8)) = false := by
1825 simp only [decide_eq_false_iff_not, ge_iff_le, UInt64.le_iff_toNat_le, Nat.not_le]
1826 rw [ofNat_toNat_lt _ (by omega)]
1827 have : (8 : UInt64).toNat = 8 := rfl
1828 omega
1829 subst hjq
1830 refine ⟨((rbFrom bs c o (8 * (L / 8))).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * (L / 8)),
1831 UInt64.ofNat (L - 8 * (L / 8))), ?_, rfl⟩
1832 show OkOn h (pure _) _
1833 simp only [hlt]
1834 exact OkOn.pure _ _
1835
1836theorem tail_ok (h : Heap) (hs : StaticsOK h) (blk o L : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1837 (hle : o + L ≤ bs.size) (hL : L < 2 ^ 64) (c : UInt32) :
1838 OkOn h (tailP c.toUInt64 (.ref (.blk blk) o) (UInt64.ofNat L)) ((rbFrom bs c o L).toUInt64 ^^^ 0xffffffff) := by
1839 unfold tailP
1840 refine bind_of (fun x => x = ((rbFrom bs c o (8 * (L / 8))).toUInt64, Ptr.ref (BRef.blk blk) (o + 8 * (L / 8)),
1841 UInt64.ofNat (L - 8 * (L / 8)))) ⟨_, loop8_ok h hs blk o L bs hb hle hL c, rfl⟩ ?_
1842 intro x hx
1843 subst hx
1844 show OkOn h (cwhile cond1 body1 _ >>= _) _
1845 refine bind_of (fun x => x = ((rbFrom bs (rbFrom bs c o (8 * (L / 8))) (o + 8 * (L / 8)) (L - 8 * (L / 8))).toUInt64,
1846 Ptr.ref (BRef.blk blk) (o + 8 * (L / 8) + (L - 8 * (L / 8))), 0))
1847 ⟨_, loop1_ok h hs blk (o + 8 * (L / 8)) (L - 8 * (L / 8)) bs hb (by omega) (by omega) _, rfl⟩ ?_
1848 intro x hx
1849 subst hx
1850 rw [← rbFrom_add]
1851 rw [show 8 * (L / 8) + (L - 8 * (L / 8)) = L by omega]
1852 exact OkOn.pure _ _
1853
1854/-! ### The alignment loop -/
1855
1856def condA (x : UInt64 × BPtr × UInt64) : M (Bool × UInt64 × BPtr × UInt64) :=
1857 match x with
1858 | (c, b, l) =>
1859 if (l == 0) = true then pure (false, c, b, l)
1860 else do
1861 let __do_lift ← addrOf b
1862 pure (__do_lift &&& (W - 1).toInt.toNat.toUInt64 != 0, c, b, l)
1863
1864def bodyA (x : UInt64 × BPtr × UInt64) : M (UInt64 × BPtr × UInt64) :=
1865 match x with
1866 | (c, b, l) =>
1867 have l := l - 1
1868 do
1869 let __x ← crcByte c b
1870 match __x with
1871 | (c, b) => pure (c, b, l)
1872
1873theorem W_pred : (W - 1).toInt.toNat.toUInt64 = 7 := by decide
1874
1875theorem and7 (n : Nat) : (UInt64.ofNat n &&& 7).toNat = n % 8 := by
1876 rw [UInt64.toNat_and, UInt64.toNat_ofNat']
1877 have : (7 : UInt64).toNat = 2 ^ 3 - 1 := by decide
1878 rw [this, Nat.and_two_pow_sub_one_eq_mod]
1879 have : (n % 2 ^ 64) % 2 ^ 3 = n % 2 ^ 3 := Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 (by decide))
1880 rw [this]
1881
1882def alignLen (o : Nat) : Nat := (8 - o % 8) % 8
1883
1884theorem loopA_ok (h : Heap) (blk o L : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1885 (hle : o + L ≤ bs.size) (hL : L < 2 ^ 64) (h8 : 8 ≤ L) (hs : StaticsOK h) (c : UInt32) :
1886 OkOn h (cwhile condA bodyA (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L))
1887 ((rbFrom bs c o (alignLen o)).toUInt64, Ptr.ref (BRef.blk blk) (o + alignLen o),
1888 UInt64.ofNat (L - alignLen o)) := by
1889 suffices H : ∃ s, OkOn h (cwhile condA bodyA (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) s ∧
1890 s = ((rbFrom bs c o (alignLen o)).toUInt64, Ptr.ref (BRef.blk blk) (o + alignLen o),
1891 UInt64.ofNat (L - alignLen o)) by
1892 obtain ⟨s, hs', rfl⟩ := H; exact hs'
1893 have ha7 : alignLen o ≤ 7 := by unfold alignLen; omega
1894 have hcondA : ∀ j, j ≤ alignLen o →
1895 OkOn h (condA ((rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j)))
1896 (decide (j < alignLen o), (rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j)) := by
1897 intro j hj
1898 have hne : (UInt64.ofNat (L - j) == 0) = false := by
1899 have : UInt64.ofNat (L - j) ≠ 0 := by
1900 intro h0
1901 have := congrArg UInt64.toNat h0
1902 rw [ofNat_toNat_lt _ (by omega)] at this
1903 have h1 : (0 : UInt64).toNat = 0 := rfl
1904 omega
1905 simpa using this
1906 have hmod : decide ((UInt64.ofNat (o + j) &&& 7) ≠ 0) = decide (j < alignLen o) := by
1907 have h1 : (UInt64.ofNat (o + j) &&& 7).toNat = (o + j) % 8 := and7 _
1908 by_cases hj' : j < alignLen o
1909 · have : (UInt64.ofNat (o + j) &&& 7) ≠ 0 := by
1910 intro h0; rw [h0] at h1
1911 have h2 : (0 : UInt64).toNat = 0 := rfl
1912 unfold alignLen at hj'; omega
1913 rw [decide_eq_true this, decide_eq_true hj']
1914 · have : (UInt64.ofNat (o + j) &&& 7) = 0 := by
1915 apply UInt64.toNat_inj.mp
1916 have h2 : (0 : UInt64).toNat = 0 := rfl
1917 rw [h1, h2]; unfold alignLen at hj hj'; omega
1918 rw [decide_eq_false (by rw [this]; simp), decide_eq_false hj']
1919 unfold condA
1920 dsimp only
1921 rw [ite_eq_right (by simp [hne])]
1922 show OkOn h (addrOf (Ptr.ref (BRef.blk blk) (o + j)) >>= _) _
1923 refine OkOn.bind (x := UInt64.ofNat (o + j)) (OkOn.pure _ _) ?_
1924 rw [W_pred]
1925 show OkOn h (pure _) _
1926 have hb : ((UInt64.ofNat (o + j) &&& 7) != 0) = decide (j < alignLen o) := by
1927 rw [← hmod]
1928 generalize UInt64.ofNat (o + j) &&& 7 = x
1929 by_cases hx : x = 0 <;> simp [hx]
1930 rw [hb]
1931 exact OkOn.pure _ _
1932 refine cwhile_ok (fun k s => ∃ j, j + k = alignLen o ∧
1933 s = ((rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j))) _ ?_ ?_
1934 (k := alignLen o) (s := (c.toUInt64, .ref (.blk blk) o, UInt64.ofNat L)) ⟨0, by omega, by simp [rbFrom_zero]⟩
1935 (by show alignLen o < 2 ^ 64; omega)
1936 · intro k s hP
1937 obtain ⟨j, hj, rfl⟩ := hP
1938 refine ⟨((rbFrom bs c o j).toUInt64, Ptr.ref (BRef.blk blk) (o + j), UInt64.ofNat (L - j)),
1939 ((ZlibF.Spec.crcByte (rbFrom bs c o j) bs[o + j]!).toUInt64, Ptr.ref (BRef.blk blk) (o + j + 1),
1940 UInt64.ofNat (L - j) - 1), ?_, ?_, ?_⟩
1941 · have := hcondA j (by omega)
1942 rwa [show decide (j < alignLen o) = true by simp; omega] at this
1943 · exact body1_ok h hs blk (o + j) bs hb (by omega) _ _
1944 · refine ⟨j + 1, by omega, ?_⟩
1945 rw [rbFrom_snoc, show (1 : UInt64) = UInt64.ofNat 1 from rfl, ofNat_sub64 _ _ (by omega) (by omega)]
1946 rw [show L - j - 1 = L - (j + 1) by omega]
1947 rfl
1948 · intro s hP
1949 obtain ⟨j, hj, rfl⟩ := hP
1950 have hjL : alignLen o = j := by omega
1951 subst hjL
1952 refine ⟨((rbFrom bs c o (alignLen o)).toUInt64, Ptr.ref (BRef.blk blk) (o + alignLen o),
1953 UInt64.ofNat (L - alignLen o)), ?_, rfl⟩
1954 have := hcondA (alignLen o) (Nat.le_refl _)
1955 simpa using this
1956
1957/-! ### The braided loop -/
1958
1959def condBlk (s : BL) : M (Bool × BL) :=
1960 have blks := s.blks - 1
1961 pure (blks != 0, { crc0 := s.crc0, crc1 := s.crc1, crc2 := s.crc2, crc3 := s.crc3, crc4 := s.crc4,
1962 words := s.words, blks := blks })
1963
1964theorem bw_Psi (c : UInt32) (bs : Array UInt8) (o : Nat) :
1965 bw (c.toUInt64 ^^^ WA bs o) = pw 256 (rbFrom bs c o 8) := by
1966 rw [bw_eq, Psi_WA]
1967
1968theorem val_inv (bs : Array UInt8) (c0 c1 c2 c3 c4 R : UInt32) (ow : Nat)
1969 (hV : Val c0 c1 c2 c3 c4 = pw 320 R) :
1970 Val (pw 256 (rbFrom bs c0 ow 8)) (pw 256 (rbFrom bs c1 (ow + 8) 8)) (pw 256 (rbFrom bs c2 (ow + 16) 8))
1971 (pw 256 (rbFrom bs c3 (ow + 24) 8)) (pw 256 (rbFrom bs c4 (ow + 32) 8)) = pw 320 (rbFrom bs R ow 40) := by
1972 rw [val_step, chain_aff, hV, chain_zero, rbFrom_aff bs 40 R ow]
1973
1974theorem loopBlk_ok (h : Heap) (blk o1 B : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
1975 (hB : 1 ≤ B) (hle : o1 + 40 * B ≤ bs.size) (hB64 : B < 2 ^ 64) (c : UInt32) :
1976 ∃ s : BL, OkOn h (cwhile condBlk blockLittle
1977 { crc0 := c, crc1 := 0, crc2 := 0, crc3 := 0, crc4 := 0, words := (.ref (.blk blk) o1 : BPtr), blks := UInt64.ofNat B }) s ∧
1978 Val s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 = pw 320 (rbFrom bs c o1 (40 * (B - 1))) ∧
1979 s.words = .ref (.blk blk) (o1 + 40 * (B - 1)) := by
1980 refine cwhile_ok (cond := condBlk) (body := blockLittle) (fun k s => ∃ j, j + k = B - 1 ∧ s.words = .ref (.blk blk) (o1 + 40 * j) ∧
1981 s.blks = UInt64.ofNat (B - j) ∧
1982 Val s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 = pw 320 (rbFrom bs c o1 (40 * j)))
1983 (fun s => Val s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 = pw 320 (rbFrom bs c o1 (40 * (B - 1))) ∧
1984 s.words = .ref (.blk blk) (o1 + 40 * (B - 1))) ?_ ?_ (k := B - 1)
1985 (s := { crc0 := c, crc1 := 0, crc2 := 0, crc3 := 0, crc4 := 0, words := (.ref (.blk blk) o1 : BPtr), blks := UInt64.ofNat B }) ?_ (by show B - 1 < 2 ^ 64; omega)
1986 · intro k s hP
1987 obtain ⟨c0, c1, c2, c3, c4, words, blks⟩ := s
1988 obtain ⟨j, hj, hw, hbl, hV⟩ := hP
1989 simp only at hw hbl hV
1990 subst hw hbl
1991 have hne : (UInt64.ofNat (B - j) - 1 != 0) = true := by
1992 rw [show (1 : UInt64) = UInt64.ofNat 1 from rfl, ofNat_sub64 _ _ (by omega) (by omega)]
1993 have : UInt64.ofNat (B - j - 1) ≠ 0 := by
1994 intro h0
1995 have := congrArg UInt64.toNat h0
1996 rw [ofNat_toNat_lt _ (by omega)] at this
1997 have h1 : (0 : UInt64).toNat = 0 := rfl
1998 omega
1999 simpa using this
2000 let s1 : BL := { crc0 := c0, crc1 := c1, crc2 := c2, crc3 := c3, crc4 := c4,
2001 words := (.ref (.blk blk) (o1 + 40 * j) : BPtr), blks := UInt64.ofNat (B - j) - 1 }
2002 have hbk := blockLittle_ok h s1 blk (o1 + 40 * j) bs hb (by omega) rfl
2003 refine ⟨s1, _, ?_, hbk, ?_⟩
2004 · show OkOn h (pure _) _
2005 simp only [hne]
2006 exact OkOn.pure _ _
2007 · refine ⟨j + 1, by omega, rfl, ?_, ?_⟩
2008 · show UInt64.ofNat (B - j) - 1 = UInt64.ofNat (B - (j + 1))
2009 rw [show (1 : UInt64) = UInt64.ofNat 1 from rfl, ofNat_sub64 _ _ (by omega) (by omega)]
2010 rfl
2011 · show Val (bw (c0.toUInt64 ^^^ WA bs (o1 + 40 * j))) (bw (c1.toUInt64 ^^^ WA bs (o1 + 40 * j + 8)))
2012 (bw (c2.toUInt64 ^^^ WA bs (o1 + 40 * j + 16))) (bw (c3.toUInt64 ^^^ WA bs (o1 + 40 * j + 24)))
2013 (bw (c4.toUInt64 ^^^ WA bs (o1 + 40 * j + 32))) = _
2014 rw [bw_Psi, bw_Psi, bw_Psi, bw_Psi, bw_Psi, val_inv bs c0 c1 c2 c3 c4 _ _ hV]
2015 rw [show 40 * (j + 1) = 40 * j + 40 by omega, rbFrom_add]
2016 · intro s hP
2017 obtain ⟨c0, c1, c2, c3, c4, words, blks⟩ := s
2018 obtain ⟨j, hj, hw, hbl, hV⟩ := hP
2019 simp only at hw hbl hV
2020 subst hw hbl
2021 have hjB : j = B - 1 := by omega
2022 subst hjB
2023 have hz : (UInt64.ofNat (B - (B - 1)) - 1 != 0) = false := by
2024 rw [show B - (B - 1) = 1 by omega]
2025 decide
2026 refine ⟨{ crc0 := c0, crc1 := c1, crc2 := c2, crc3 := c3, crc4 := c4,
2027 words := (.ref (.blk blk) (o1 + 40 * (B - 1)) : BPtr), blks := UInt64.ofNat (B - (B - 1)) - 1 }, ?_, hV, rfl⟩
2028 show OkOn h (pure _) _
2029 simp only [hz]
2030 exact OkOn.pure _ _
2031 · refine ⟨0, by omega, rfl, rfl, ?_⟩
2032 simp [Val, rbFrom_zero, pw_zero]
2033
2034theorem ofNat_div64 (n k : Nat) (hn : n < 2 ^ 64) (hk : 0 < k) (hk64 : k < 2 ^ 64) :
2035 UInt64.ofNat n / UInt64.ofNat k = UInt64.ofNat (n / k) := by
2036 apply UInt64.toNat_inj.mp
2037 rw [UInt64.toNat_div, UInt64.toNat_ofNat', UInt64.toNat_ofNat', UInt64.toNat_ofNat',
2038 Nat.mod_eq_of_lt hn, Nat.mod_eq_of_lt hk64, Nat.mod_eq_of_lt (Nat.lt_of_le_of_lt (Nat.div_le_self _ _) hn)]
2039
2040theorem ofNat_mul64 (a b : Nat) (h : a * b < 2 ^ 64) (ha : a < 2 ^ 64) (hb : b < 2 ^ 64) :
2041 UInt64.ofNat a * UInt64.ofNat b = UInt64.ofNat (a * b) := by
2042 apply UInt64.toNat_inj.mp
2043 rw [UInt64.toNat_mul, UInt64.toNat_ofNat', UInt64.toNat_ofNat', UInt64.toNat_ofNat',
2044 Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb, Nat.mod_eq_of_lt h]
2045
2046theorem crc_init (crc : UInt64) : (~~~crc &&& 4294967295) = (~~~ crc.toUInt32).toUInt64 := by
2047 apply UInt64.toNat_inj.mp
2048 have h1 : (4294967295 : UInt64).toNat = 2 ^ 32 - 1 := by decide
2049 rw [UInt64.toNat_and, h1, Nat.and_two_pow_sub_one_eq_mod, UInt32.toNat_toUInt64]
2050 rw [UInt64.toNat_not, UInt32.toNat_not, UInt64.toNat_toUInt32]
2051 have := UInt64.toNat_lt crc
2052 have e1 : UInt64.size = 2 ^ 64 := rfl
2053 have e2 : UInt32.size = 2 ^ 32 := rfl
2054 simp only [e1, e2] at *
2055 omega
2056
2057theorem ge47 (L : Nat) (hL : L < 2 ^ 64) : (UInt64.ofNat L ≥ 47) ↔ 47 ≤ L := by
2058 have : (47 : UInt64).toNat = 47 := rfl
2059 rw [ge_iff_le, UInt64.le_iff_toNat_le, this, ofNat_toNat_lt _ hL]
2060
2061theorem NW_val : (N * W).toInt.toNat.toUInt64 = 40 := by decide
2062theorem NWW_val : (N * W + W - 1).toInt.toNat.toUInt64 = 47 := by decide
2063
2064theorem crc32_z_ok (h : Heap) (hs : StaticsOK h) (crc : UInt64) (blk off : Nat) (bs : Array UInt8)
2065 (hb : h.bytes blk = some bs) (len : UInt64) (hlen : off + len.toNat ≤ bs.size) :
2066 OkOn h (crc32_z crc (.ref (.blk blk) off) len)
2067 ((rbFrom bs (~~~ crc.toUInt32) off len.toNat).toUInt64 ^^^ 0xffffffff) := by
2068 have hlen' : len = UInt64.ofNat len.toNat := UInt64.ofNat_toNat.symm
2069 have hL : len.toNat < 2 ^ 64 := UInt64.toNat_lt len
2070 generalize len.toNat = L at hlen' hlen hL ⊢
2071 subst hlen'
2072 unfold crc32_z
2073 rw [ite_eq_right (by simp)]
2074 dsimp only
2075 rw [crc_init]
2076 by_cases hlong : 47 ≤ L
2077 · rw [ite_eq_left (by rw [NWW_val]; exact (ge47 L hL).mpr hlong)]
2078 have ha7 : alignLen off ≤ 7 := by unfold alignLen; omega
2079 refine bind_of (fun x => x = ((rbFrom bs (~~~ crc.toUInt32) off (alignLen off)).toUInt64,
2080 Ptr.ref (BRef.blk blk) (off + alignLen off), UInt64.ofNat (L - alignLen off)))
2081 ⟨_, loopA_ok h blk off L bs hb hlen hL (by omega) hs _, rfl⟩ ?_
2082 intro x hx
2083 subst hx
2084 dsimp only
2085 rw [NW_val]
2086 rw [ite_eq_left (by decide), u32_roundtrip]
2087 have hB1 : 1 ≤ (L - alignLen off) / 40 := by omega
2088 have hBle : off + alignLen off + 40 * ((L - alignLen off) / 40) ≤ bs.size := by omega
2089 have hdiv : UInt64.ofNat (L - alignLen off) / 40 = UInt64.ofNat ((L - alignLen off) / 40) := by
2090 rw [show (40 : UInt64) = UInt64.ofNat 40 from rfl, ofNat_div64 _ _ (by omega) (by omega) (by omega)]
2091 have hrem : UInt64.ofNat (L - alignLen off) - UInt64.ofNat ((L - alignLen off) / 40) * 40 =
2092 UInt64.ofNat (L - alignLen off - (L - alignLen off) / 40 * 40) := by
2093 rw [show (40 : UInt64) = UInt64.ofNat 40 from rfl, ofNat_mul64 _ _ (by omega) (by omega) (by omega),
2094 ofNat_sub64 _ _ (by omega) (by omega)]
2095 rw [hdiv, hrem]
2096 refine bind_of (fun s : BL => Val s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 =
2097 pw 320 (rbFrom bs (rbFrom bs (~~~ crc.toUInt32) off (alignLen off)) (off + alignLen off)
2098 (40 * ((L - alignLen off) / 40 - 1))) ∧
2099 s.words = .ref (.blk blk) (off + alignLen off + 40 * ((L - alignLen off) / 40 - 1)))
2100 (loopBlk_ok h blk (off + alignLen off) ((L - alignLen off) / 40) bs hb hB1 hBle (by omega) _) ?_
2101 intro s hR
2102 obtain ⟨hV, hw⟩ := hR
2103 rw [hw]
2104 generalize hA : alignLen off = a at ha7 hB1 hBle hdiv hrem hV hw ⊢
2105 generalize hB : (L - a) / 40 = B at hB1 hBle hdiv hrem hV hw ⊢
2106 generalize hc' : rbFrom bs (~~~crc.toUInt32) off a = c' at hV ⊢
2107 generalize how : off + a + 40 * (B - 1) = ow at hV hw ⊢
2108 have w0 := wordAt_ok h blk ow 0 bs hb (by omega)
2109 have w1 := wordAt_ok h blk ow 1 bs hb (by omega)
2110 have w2 := wordAt_ok h blk ow 2 bs hb (by omega)
2111 have w3 := wordAt_ok h blk ow 3 bs hb (by omega)
2112 have w4 := wordAt_ok h blk ow 4 bs hb (by omega)
2113 have hadd : OkOn h (Ptr.add (Ptr.ref (BRef.blk blk) ow) 40) (Ptr.ref (BRef.blk blk) (ow + 40)) := by
2114 have := Ptr.add_nat_ok (r := BRef.blk blk) (off := ow) (n := 40) (a := bs) hb (by omega)
2115 simpa using this
2116 have e1 : ∀ (a b : UInt32) (W : UInt64), a.toUInt64 ^^^ W ^^^ b.toUInt64 = (a ^^^ b).toUInt64 ^^^ W := by
2117 intro a b W; rw [UInt32.toUInt64_xor]; ac_rfl
2118 refine OkOn.bind w0 ?_
2119 refine OkOn.bind (crc_word_ok h hs _) ?_
2120 rw [Psi_WA]
2121 refine OkOn.bind w1 ?_
2122 rw [e1]
2123 refine OkOn.bind (crc_word_ok h hs _) ?_
2124 rw [Psi_WA]
2125 refine OkOn.bind w2 ?_
2126 rw [e1]
2127 refine OkOn.bind (crc_word_ok h hs _) ?_
2128 rw [Psi_WA]
2129 refine OkOn.bind w3 ?_
2130 rw [e1]
2131 refine OkOn.bind (crc_word_ok h hs _) ?_
2132 rw [Psi_WA]
2133 refine OkOn.bind w4 ?_
2134 rw [e1]
2135 refine OkOn.bind (crc_word_ok h hs _) ?_
2136 rw [Psi_WA]
2137 refine OkOn.bind hadd ?_
2138 have hx4 : rbFrom bs (s.crc4 ^^^ rbFrom bs (s.crc3 ^^^ rbFrom bs (s.crc2 ^^^ rbFrom bs (s.crc1 ^^^ rbFrom bs s.crc0 (ow + 8 * 0) 8)
2139 (ow + 8 * 1) 8) (ow + 8 * 2) 8) (ow + 8 * 3) 8) (ow + 8 * 4) 8 = rbFrom bs c' (off + a) (40 * B) := by
2140 have h1 := chain_aff bs s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 ow
2141 rw [hV, chain_zero] at h1
2142 change chain bs s.crc0 s.crc1 s.crc2 s.crc3 s.crc4 ow = _
2143 rw [h1, ← rbFrom_aff bs 40 _ ow, ← how, ← rbFrom_add]
2144 congr 1; omega
2145 have hall : rbFrom bs c' (off + a) (40 * B + (L - a - B * 40)) = rbFrom bs (~~~crc.toUInt32) off L := by
2146 rw [← hc']
2147 have := rbFrom_add bs a (L - a) (~~~crc.toUInt32) off
2148 rw [show a + (L - a) = L by omega] at this
2149 rw [this, hc', show 40 * B + (L - a - B * 40) = L - a by omega]
2150 rw [hx4]
2151 rw [rbFrom_add, show off + a + 40 * B = ow + 40 by omega] at hall
2152 rw [← hall]
2153 exact tail_ok h hs blk (ow + 40) (L - a - B * 40) bs hb (by omega) (by omega) _
2154 · rw [ite_eq_right (by rw [NWW_val]; exact fun hh => hlong ((ge47 L hL).mp hh))]
2155 exact tail_ok h hs blk off L bs hb hlen hL _
2156
2157/-! ### Statement helpers -/
2158
2159theorem pw_foldl (m : Nat) (v : UInt32) : (List.range m).foldl (fun c _ => crcBit c) v = pw m v := by
2160 induction m generalizing v with
2161 | zero => rfl
2162 | succ m ih => rw [List.range_succ, List.foldl_append, ih, pw_succ']; rfl
2163
2164theorem e31_eq : (0x80000000 : UInt32) = e 31 := by decide
2165
2166set_option maxRecDepth 100000 in
2167theorem mulp_one_basis : ∀ t : Fin 32, mulp (e t.val) (e 31) = e t.val := by decide +kernel
2168
2169theorem mulp_one_right (a : UInt32) : mulp a (e 31) = a :=
2170 lin32_ext (f := fun a => mulp a (e 31)) (g := fun a => a)
2171 (by intro x y; exact mulp_xor_left x y _) (by intro x y; rfl) (fun t ht => mulp_one_basis ⟨t, ht⟩) a
2172
2173theorem not32_toUInt64 (x : UInt32) : (~~~ x).toUInt64 = x.toUInt64 ^^^ 0xffffffff := by
2174 rw [not32_eq, UInt32.toUInt64_xor]; rfl
2175
2176theorem u64_roundtrip (x : UInt64) (h : x.toNat < 2 ^ 32) : x.toUInt32.toUInt64 = x := by
2177 apply UInt64.toNat_inj.mp
2178 rw [UInt32.toNat_toUInt64, UInt64.toNat_toUInt32, Nat.mod_eq_of_lt h]
2179
2180theorem crc32_append (A B : List UInt8) :
2181 ZlibF.Spec.crc32 (A ++ B) = pw (8 * B.length) (ZlibF.Spec.crc32 A) ^^^ ZlibF.Spec.crc32 B := by
2182 unfold ZlibF.Spec.crc32
2183 rw [← ZlibF.Spec.crc32Update_append, crc32Update_affine]
2184 rfl
2185
2186/-! ## Theorems -/
2187
2188/-- **crc32_z, `buf == NULL`.** For every heap and every `crc`, `len`: `crc32_z(crc, Z_NULL, len)` returns `0` (the
2189initial value of `zlib.h`) and does not touch the heap. -/
2190theorem crc32_z_null (h : Heap) (crc len : UInt64) :
2191 C.run (crc32_z crc .null len) h = .ok (0, h) := by
2192 unfold C.run crc32_z
2193 rw [ite_eq_left (by decide)]
2194 rfl
2195
2196/-- **crc32_z, correctness.** For every heap whose static tables are the ones of `crc32.h` (`StaticsOK`), every `crc`
2197(only its low 32 bits matter, as in C), and every buffer in a heap block (`off + len ≤ size` of the block; the model
2198resolves the alignment test `(z_size_t)buf & 7` through the offset in the block, see `crc32.c.lean`): `crc32_z` returns the
2199CRC-32 of the `len` bytes at `buf` continuing `crc`, that is `ZlibF.Spec.crc32Update` of the byte list, and leaves the
2200heap unchanged. This covers every length (the byte loop, the 8-byte loop, the braided word loop of 5 words of 8 bytes)
2201and every alignment of `off`. This statement covers only heap byte blocks. Static and embedded arrays require
2202the general byte-view result, including their symbolic origin residues. -/
2203theorem crc32_z_correct (h : Heap) (hs : StaticsOK h) (crc : UInt64) (blk off : Nat) (bs : Array UInt8)
2204 (hb : h.bytes blk = some bs) (len : UInt64) (hlen : off + len.toNat ≤ bs.size) :
2205 C.run (crc32_z crc (.ref (.blk blk) off) len) h =
2206 .ok (((ZlibF.Spec.crc32Update crc.toUInt32 ((bs.toList.drop off).take len.toNat))).toUInt64, h) := by
2207 have := crc32_z_ok h hs crc blk off bs hb len hlen
2208 unfold OkOn at this
2209 unfold C.run
2210 rw [this, rbFrom_list bs len.toNat _ off hlen]
2211 have e1 : ZlibF.Spec.crc32Update crc.toUInt32 ((bs.toList.drop off).take len.toNat) =
2212 ~~~ rbl (~~~ crc.toUInt32) ((bs.toList.drop off).take len.toNat) := rfl
2213 rw [e1, not32_toUInt64]
2214
2215/-- **crc32_z, safety.** Same hypotheses as `crc32_z_correct`: no fault (no out-of-bounds read, no bad table index, no
2216shift overflow, no fuel exhaustion), the heap is unchanged (so every heap invariant and every block is preserved). -/
2217theorem crc32_z_safe (h : Heap) (hs : StaticsOK h) (crc : UInt64) (blk off : Nat) (bs : Array UInt8)
2218 (hb : h.bytes blk = some bs) (len : UInt64) (hlen : off + len.toNat ≤ bs.size) :
2219 ∃ r, C.run (crc32_z crc (.ref (.blk blk) off) len) h = .ok (r, h) :=
2220 ⟨_, crc32_z_correct h hs crc blk off bs hb len hlen⟩
2221
2222/-- **crc32, `buf == NULL`.** Returns `0` for every `crc` and `len`; heap unchanged. -/
2223theorem crc32_null (h : Heap) (crc : UInt64) (len : UInt32) :
2224 C.run (crc32 crc .null len) h = .ok (0, h) := crc32_z_null h crc len.toUInt64
2225
2226/-- **crc32, correctness.** As `crc32_z_correct` with `len : uInt` (32 bits). -/
2227theorem crc32_correct (h : Heap) (hs : StaticsOK h) (crc : UInt64) (blk off : Nat) (bs : Array UInt8)
2228 (hb : h.bytes blk = some bs) (len : UInt32) (hlen : off + len.toNat ≤ bs.size) :
2229 C.run (crc32 crc (.ref (.blk blk) off) len) h =
2230 .ok (((ZlibF.Spec.crc32Update crc.toUInt32 ((bs.toList.drop off).take len.toNat))).toUInt64, h) := by
2231 have := crc32_z_correct h hs crc blk off bs hb len.toUInt64 (by rwa [UInt32.toNat_toUInt64])
2232 rw [UInt32.toNat_toUInt64] at this
2233 exact this
2234
2235/-- **crc32, safety.** Same hypotheses as `crc32_correct`; no fault, heap unchanged. -/
2236theorem crc32_safe (h : Heap) (hs : StaticsOK h) (crc : UInt64) (blk off : Nat) (bs : Array UInt8)
2237 (hb : h.bytes blk = some bs) (len : UInt32) (hlen : off + len.toNat ≤ bs.size) :
2238 ∃ r, C.run (crc32 crc (.ref (.blk blk) off) len) h = .ok (r, h) :=
2239 ⟨_, crc32_correct h hs crc blk off bs hb len hlen⟩
2240
2241/-! ### `get_crc_table` -/
2242
2243/-- **get_crc_table, safety.** Returns the pointer to the static table (index 0); no fault; heap unchanged. -/
2244theorem get_crc_table_safe (h : Heap) :
2245 C.run get_crc_table h = .ok (Ptr.ref URef.st_crc_table 0, h) := rfl
2246
2247/-- **get_crc_table, correctness.** With the static tables of `crc32.h`, entry `i < 256` of the returned table is the
2248CRC register after the single byte `i` from the zero register (`ZlibF.Spec.crcByte 0 i`), and reading it does not fault. -/
2249theorem get_crc_table_correct (h : Heap) (hs : StaticsOK h) (i : Nat) (hi : i < 256) :
2250 ∃ p, C.run get_crc_table h = .ok (p, h) ∧
2251 C.run (Ptr.index p (i : Int)) h = .ok (ZlibF.Spec.crcByte 0 (UInt8.ofNat i), h) := by
2252 refine ⟨Ptr.ref URef.st_crc_table 0, rfl, ?_⟩
2253 have hg : Space.get h URef.st_crc_table = some Crc32H.crc_table := by rw [URef.get_st_crc_table, hs.1]
2254 have hlt : 0 + i < Crc32H.crc_table.size := by rw [crc_table_size]; omega
2255 have := Ptr.index_ok (r := URef.st_crc_table) (off := 0) (i := i) hg hlt
2256 unfold OkOn at this
2257 unfold C.run
2258 rw [this]
2259 have e : Crc32H.crc_table[0 + i] = ZlibF.Spec.crcByte 0 (UInt8.ofNat i) := by
2260 have h1 := crc_table_eq ⟨i, hi⟩
2261 simp only at h1
2262 rw [getElem!_pos _ _ (by omega)] at h1
2263 simp only [Nat.zero_add]
2264 rw [h1, crcByte_eq, UInt32.zero_xor]
2265 congr 1
2266 apply UInt32.toNat_inj.mp
2267 simp [UInt8.toNat_ofNat, UInt32.toNat_ofNat']
2268 omega
2269 rw [e]
2270
2271/-! ### `multmodp`, `x2nmodp` -/
2272
2273/-- **multmodp, safety.** If the low 32 bits of `a` are not all zero and `b < 2^32`, `multmodp(a, b)` terminates
2274without fault, returns a value below `2^32` and leaves the heap unchanged. (If the low 32 bits of `a` are zero, the C
2275function loops forever: `multmodp_diverges`.) This is wider than the callers need (`zlib.h` has no `multmodp`). -/
2276theorem multmodp_safe (h : Heap) (a b : UInt64) (ha : a.toUInt32 ≠ 0) (hb : b.toNat < 2 ^ 32) :
2277 ∃ r, C.run (multmodp a b) h = .ok (r, h) ∧ r.toNat < 2 ^ 32 :=
2278 ⟨_, multmodp_ok h a b ha hb, by rw [UInt32.toNat_toUInt64]; exact UInt32.toNat_lt _⟩
2279
2280/-- **multmodp, correctness.** `multmodp(x^n, b)` for the register `x^n` (the register `0x80000000`, which is `x^0`,
2281advanced by `n` steps of the bit-reflected CRC polynomial division `ZlibF.Spec.crcBit`) is `b` advanced by `n` steps:
2282multiplication by `x^n` modulo the CRC polynomial. For every `n` and every `b < 2^32`. -/
2283theorem multmodp_correct (h : Heap) (n : Nat) (b : UInt64) (hb : b.toNat < 2 ^ 32) :
2284 C.run (multmodp ((((List.range n).foldl (fun c _ => crcBit c) (0x80000000 : UInt32))).toUInt64) b) h =
2285 .ok (((List.range n).foldl (fun c _ => crcBit c) b.toUInt32).toUInt64, h) := by
2286 rw [pw_foldl, pw_foldl, e31_eq]
2287 have hne : (pw n (e 31)).toUInt64.toUInt32 ≠ 0 := by
2288 rw [u32_roundtrip]
2289 intro h0
2290 exact e_ne_zero 31 (by omega) (pw_eq_zero _ _ h0)
2291 have := multmodp_ok h (pw n (e 31)).toUInt64 b hne hb
2292 unfold OkOn at this
2293 unfold C.run
2294 rw [this, u32_roundtrip, mulp_pw_left', mulp_one]
2295
2296/-- **x2nmodp, safety.** With the static tables of `crc32.h`, for every `n >= 0` (`z_off64_t`) and every `k`:
2297`x2nmodp(n, k)` terminates without fault, returns a value below `2^32` and leaves the heap unchanged. (`n < 0` is
2298excluded by the callers; the loop `while (n)` would run on the arithmetic shift.) -/
2299theorem x2nmodp_safe (h : Heap) (hs : StaticsOK h) (n : Int64) (hn : 0 ≤ n.toInt) (k : UInt32) :
2300 ∃ r, C.run (x2nmodp n k) h = .ok (r, h) ∧ r.toNat < 2 ^ 32 := by
2301 obtain ⟨p, hp, hp32, _⟩ := x2nmodp_ok h hs n hn k
2302 exact ⟨p, hp, hp32⟩
2303
2304/-- **x2nmodp, correctness.** `x2nmodp(n, k)` is `x^(n * 2^k) mod p(x)`: the register `0x80000000` (`x^0`) advanced by
2305`n * 2^k` steps of `ZlibF.Spec.crcBit`, for `n >= 0` and every `k` (the table index `k & 31` is justified by
2306`x^(2^32) = x`, checked on the table). -/
2307theorem x2nmodp_correct (h : Heap) (hs : StaticsOK h) (n : Int64) (hn : 0 ≤ n.toInt) (k : UInt32) :
2308 C.run (x2nmodp n k) h =
2309 .ok ((((List.range (n.toInt.toNat * 2 ^ k.toNat)).foldl (fun c _ => crcBit c) (0x80000000 : UInt32))).toUInt64, h) := by
2310 obtain ⟨p, hp, hp32, hv⟩ := x2nmodp_ok h hs n hn k
2311 rw [pw_foldl, e31_eq]
2312 have h1 : p.toUInt32 = pw (n.toInt.toNat * 2 ^ k.toNat) (e 31) := by
2313 have := hv (e 31)
2314 rwa [mulp_one_right] at this
2315 unfold OkOn at hp
2316 unfold C.run
2317 rw [hp, ← h1, u64_roundtrip p hp32]
2318
2319/-! ### `crc32_combine` and friends -/
2320
2321/-- **crc32_combine_gen64, safety.** With the static tables of `crc32.h`, no fault and heap unchanged for every
2322`len2`. For `len2 < 0` the result is `0`; for `len2 >= 0` it is below `2^32` and nonzero (so `crc32_combine_op`
2323terminates on it). -/
2324theorem crc32_combine_gen64_safe (h : Heap) (hs : StaticsOK h) (len2 : Int64) :
2325 ∃ op, C.run (crc32_combine_gen64 len2) h = .ok (op, h) ∧ (len2.toInt < 0 → op = 0) ∧
2326 (0 ≤ len2.toInt → op.toNat < 2 ^ 32 ∧ op.toUInt32 ≠ 0) := by
2327 by_cases hneg : len2.toInt < 0
2328 · exact ⟨0, gen64_neg h len2 hneg, fun _ => rfl, fun hh => absurd hh (by omega)⟩
2329 · obtain ⟨op, hop, hlt, hnz, _⟩ := gen64_op h hs len2 (by omega)
2330 exact ⟨op, hop, fun hh => absurd hh hneg, fun _ => ⟨hlt, hnz⟩⟩
2331
2332/-- **crc32_combine_gen64, correctness.** For `len2 >= 0`, the operator is `x^(8 * len2) mod p(x)`: the register
2333`0x80000000` advanced by `8 * len2` steps of `ZlibF.Spec.crcBit` (the effect of `len2` zero bytes on a CRC register). -/
2334theorem crc32_combine_gen64_correct (h : Heap) (hs : StaticsOK h) (len2 : Int64) (hpos : 0 ≤ len2.toInt) :
2335 C.run (crc32_combine_gen64 len2) h =
2336 .ok ((((List.range (8 * len2.toInt.toNat)).foldl (fun c _ => crcBit c) (0x80000000 : UInt32))).toUInt64, h) := by
2337 obtain ⟨op, hop, hlt, hnz, hv⟩ := gen64_op h hs len2 hpos
2338 rw [pw_foldl, e31_eq]
2339 have h1 : op.toUInt32 = pw (8 * len2.toInt.toNat) (e 31) := by
2340 have := hv (e 31)
2341 rwa [mulp_one_right] at this
2342 unfold OkOn at hop
2343 unfold C.run
2344 rw [hop, ← h1, u64_roundtrip op hlt]
2345
2346/-- **crc32_combine_gen, safety.** `z_off_t` is `long` (64 bits): same as `crc32_combine_gen64`. -/
2347theorem crc32_combine_gen_safe (h : Heap) (hs : StaticsOK h) (len2 : Int64) :
2348 ∃ op, C.run (crc32_combine_gen len2) h = .ok (op, h) ∧ (len2.toInt < 0 → op = 0) ∧
2349 (0 ≤ len2.toInt → op.toNat < 2 ^ 32 ∧ op.toUInt32 ≠ 0) :=
2350 crc32_combine_gen64_safe h hs len2
2351
2352/-- **crc32_combine_gen, correctness.** As `crc32_combine_gen64_correct`. -/
2353theorem crc32_combine_gen_correct (h : Heap) (hs : StaticsOK h) (len2 : Int64) (hpos : 0 ≤ len2.toInt) :
2354 C.run (crc32_combine_gen len2) h =
2355 .ok ((((List.range (8 * len2.toInt.toNat)).foldl (fun c _ => crcBit c) (0x80000000 : UInt32))).toUInt64, h) :=
2356 crc32_combine_gen64_correct h hs len2 hpos
2357
2358/-- **crc32_combine_op, safety.** For every `crc1`, `crc2`, and every `op` that is `0` or has a bit set among its low
235932 bits (in particular every value `crc32_combine_gen` returns, and every `op < 2^32`): no fault, heap unchanged.
2360Narrower than the C signature: for `op` nonzero with zero low 32 bits the C function never terminates
2361(`crc32_combine_op_diverges`; reproduced on the real zlib: `crc32_combine_op(1, 2, 1UL << 32)` does not return). -/
2362theorem crc32_combine_op_safe (h : Heap) (crc1 crc2 op : UInt64) (hop : op = 0 ∨ op.toUInt32 ≠ 0) :
2363 ∃ r, C.run (crc32_combine_op crc1 crc2 op) h = .ok (r, h) := by
2364 rcases hop with rfl | hop
2365 · exact ⟨0, by unfold C.run crc32_combine_op; rw [ite_eq_left (by decide)]; rfl⟩
2366 · exact ⟨_, combine_op_ok h crc1 crc2 op hop⟩
2367
2368/-- **crc32_combine_op, the candidate hang.** If `op != 0` and the low 32 bits of `op` are zero (for example
2369`op = 1UL << 32`), the translation of `crc32_combine_op` runs out of fuel (the C loop in `multmodp` does not end):
2370`multmodp` finds no set bit among bits 0..31 of `a`, so `(a & (m - 1)) == 0` is never reached. -/
2371theorem crc32_combine_op_diverges (h : Heap) (crc1 crc2 op : UInt64) (hop : op ≠ 0) (hlow : op.toUInt32 = 0) :
2372 C.run (crc32_combine_op crc1 crc2 op) h = .error .outOfFuel :=
2373 combine_op_diverges h crc1 crc2 op hop hlow
2374
2375/-- **crc32_combine64, safety.** With the static tables of `crc32.h`: every `crc1`, `crc2`, `len2`: no fault, heap
2376unchanged; the result is `0` for `len2 < 0`. -/
2377theorem crc32_combine64_safe (h : Heap) (hs : StaticsOK h) (crc1 crc2 : UInt64) (len2 : Int64) :
2378 ∃ r, C.run (crc32_combine64 crc1 crc2 len2) h = .ok (r, h) ∧ (len2.toInt < 0 → r = 0) :=
2379 ⟨_, combine64_ok h hs crc1 crc2 len2, fun hh => by rw [ite_eq_left hh]⟩
2380
2381/-- **crc32_combine64, correctness.** For two byte sequences `A`, `B` with `len2 = B.length` and the CRC values
2382`crc1 = crc32 A`, `crc2 = crc32 B` (`ZlibF.Spec.crc32`), the result is `crc32 (A ++ B)`. -/
2383theorem crc32_combine64_correct (h : Heap) (hs : StaticsOK h) (A B : List UInt8) (len2 : Int64)
2384 (hlen : len2.toInt = B.length) :
2385 C.run (crc32_combine64 (ZlibF.Spec.crc32 A).toUInt64 (ZlibF.Spec.crc32 B).toUInt64 len2) h =
2386 .ok ((ZlibF.Spec.crc32 (A ++ B)).toUInt64, h) := by
2387 have := combine64_ok h hs (ZlibF.Spec.crc32 A).toUInt64 (ZlibF.Spec.crc32 B).toUInt64 len2
2388 unfold OkOn at this
2389 unfold C.run
2390 rw [this, ite_eq_right (by omega), u32_roundtrip, u32_roundtrip, hlen, Int.toNat_natCast, crc32_append]
2391
2392/-- **crc32_combine, safety.** `z_off_t` is `long`: the same as `crc32_combine64`. -/
2393theorem crc32_combine_safe (h : Heap) (hs : StaticsOK h) (crc1 crc2 : UInt64) (len2 : Int64) :
2394 ∃ r, C.run (crc32_combine crc1 crc2 len2) h = .ok (r, h) ∧ (len2.toInt < 0 → r = 0) :=
2395 crc32_combine64_safe h hs crc1 crc2 len2
2396
2397/-- **crc32_combine, correctness.** As `crc32_combine64_correct`: `crc32_combine(crc32 A, crc32 B, |B|) = crc32 (A ++ B)`. -/
2398theorem crc32_combine_correct (h : Heap) (hs : StaticsOK h) (A B : List UInt8) (len2 : Int64)
2399 (hlen : len2.toInt = B.length) :
2400 C.run (crc32_combine (ZlibF.Spec.crc32 A).toUInt64 (ZlibF.Spec.crc32 B).toUInt64 len2) h =
2401 .ok ((ZlibF.Spec.crc32 (A ++ B)).toUInt64, h) :=
2402 crc32_combine64_correct h hs A B len2 hlen
2403
2404/-- **crc32_combine_op, correctness (with the generator).** For `A`, `B` and `len2 = |B|`: running
2405`crc32_combine_gen(len2)` and then `crc32_combine_op(crc32 A, crc32 B, op)` returns `crc32 (A ++ B)`. -/
2406theorem crc32_combine_op_correct (h : Heap) (hs : StaticsOK h) (A B : List UInt8) (len2 : Int64)
2407 (hlen : len2.toInt = B.length) :
2408 C.run (crc32_combine_gen len2 >>= fun op =>
2409 crc32_combine_op (ZlibF.Spec.crc32 A).toUInt64 (ZlibF.Spec.crc32 B).toUInt64 op) h =
2410 .ok ((ZlibF.Spec.crc32 (A ++ B)).toUInt64, h) :=
2411 crc32_combine64_correct h hs A B len2 hlen
2412
2413/-! ### `crc_word` and the helpers -/
2414
2415/-- **crc_word, safety.** With the static tables of `crc32.h`, `crc_word(data)` does not fault for any 64-bit `data`;
2416heap unchanged. -/
2417theorem crc_word_safe (h : Heap) (hs : StaticsOK h) (data : UInt64) :
2418 ∃ r, C.run (crc_word data) h = .ok (r, h) := ⟨_, crc_word_ok h hs data⟩
2419
2420/-- **crc_word, correctness.** For a register `c` and the eight bytes at `off` of a heap block (read by `loadWord`, little
2421endian), `crc_word(c ^ word)` is the register after those eight bytes: the left fold of `ZlibF.Spec.crcByte`. -/
2422theorem crc_word_correct (h : Heap) (hs : StaticsOK h) (c : UInt32) (blk off : Nat) (bs : Array UInt8)
2423 (hb : h.bytes blk = some bs) (hlen : off + 8 ≤ bs.size) :
2424 C.run (loadWord (.ref (.blk blk) off) >>= fun w => crc_word (c.toUInt64 ^^^ w)) h =
2425 .ok ((((bs.toList.drop off).take 8).foldl ZlibF.Spec.crcByte c), h) := by
2426 have h1 := OkOn.bind (f := fun w => crc_word (c.toUInt64 ^^^ w)) (loadWord_ok h blk off bs hb hlen) (crc_word_ok h hs (c.toUInt64 ^^^
2427 wob bs[off + 0]! bs[off + 1]! bs[off + 2]! bs[off + 3]! bs[off + 4]! bs[off + 5]! bs[off + 6]! bs[off + 7]!))
2428 have h2 : Psi (c.toUInt64 ^^^ wob bs[off + 0]! bs[off + 1]! bs[off + 2]! bs[off + 3]! bs[off + 4]! bs[off + 5]!
2429 bs[off + 6]! bs[off + 7]!) = rbFrom bs c off 8 := Psi_WA c bs off
2430 rw [h2, rbFrom_list bs 8 c off hlen] at h1
2431 exact h1
2432
2433/-- **loadWord, safety.** Reading eight bytes inside a block never faults and leaves the heap unchanged. -/
2434theorem loadWord_safe (h : Heap) (blk off : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
2435 (hlen : off + 8 ≤ bs.size) : ∃ w, C.run (loadWord (.ref (.blk blk) off)) h = .ok (w, h) :=
2436 ⟨_, loadWord_ok h blk off bs hb hlen⟩
2437
2438/-- **crcTab / x2nTab, safety.** Table reads with an index below the table size (256, 32) do not fault. -/
2439theorem crcTab_safe (h : Heap) (hs : StaticsOK h) (i : UInt64) (hi : i.toNat < 256) :
2440 ∃ t, C.run (crcTab i) h = .ok (t, h) := ⟨_, crcTab_ok h hs i hi⟩
2441
2442/-- **x2nTab, safety.** A read of `x2n_table[i]` with `i < 32` does not fault; heap unchanged. -/
2443theorem x2nTab_safe (h : Heap) (hs : StaticsOK h) (i : UInt32) (hi : i.toNat < 32) :
2444 ∃ t, C.run (x2nTab i) h = .ok (t, h) := ⟨_, x2nTab_ok h hs i hi⟩
2445
2446/-- **crcByte, safety.** One table-driven byte step inside a block does not fault, and returns the next register and
2447pointer (`ZlibF.Spec.crcByte`). -/
2448theorem crcByte_safe (h : Heap) (hs : StaticsOK h) (blk o : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
2449 (ho : o < bs.size) (c : UInt32) :
2450 C.run (crcByte c.toUInt64 (.ref (.blk blk) o)) h =
2451 .ok (((ZlibF.Spec.crcByte c bs[o]!).toUInt64, Ptr.ref (BRef.blk blk) (o + 1)), h) :=
2452 crcByte_ok h hs blk o bs hb ho c
2453
2454/-- **wordAt, safety.** Word `i` of a word array inside a block: no fault. -/
2455theorem wordAt_safe (h : Heap) (blk ow i : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
2456 (hlt : ow + 8 * i + 8 ≤ bs.size) : ∃ w, C.run (wordAt (.ref (.blk blk) ow) i) h = .ok (w, h) :=
2457 ⟨_, wordAt_ok h blk ow i bs hb hlt⟩
2458
2459/-- **braidTab, safety.** The braid table lookup for `k < 8` and any word: no fault. -/
2460theorem braidTab_safe (h : Heap) (kn : Nat) (hk : kn < 8) (word : UInt64) :
2461 ∃ t, C.run (braidTab (Int32.ofNat kn) word) h = .ok (t, h) := ⟨_, braidTab_ok h kn hk word⟩
2462
2463/-- **blockLittle, safety.** One block of 5 words (40 bytes) of the braided loop inside a block: no fault. -/
2464theorem blockLittle_safe (h : Heap) (s : BL) (blk ow : Nat) (bs : Array UInt8) (hb : h.bytes blk = some bs)
2465 (hlt : ow + 40 ≤ bs.size) (hw : s.words = .ref (.blk blk) ow) :
2466 ∃ s', C.run (blockLittle s) h = .ok (s', h) := ⟨_, blockLittle_ok h s blk ow bs hb hlt hw⟩
2467
2468/-- **addrOf, raw block case.** The aligned-origin convention retains the offset as its address representative.
2469Static and embedded origins use separate symbolic residues. -/
2470theorem addrOf_safe (h : Heap) (blk off : Nat) :
2471 C.run (addrOf (.ref (.blk blk) off)) h = .ok (UInt64.ofNat off, h) := rfl
2472
2473/-- **shr64, safety.** A shift count below 64 does not fault. -/
2474theorem shr64_safe (h : Heap) (x n : UInt64) (hn : n < 64) : C.run (shr64 x n) h = .ok (x >>> n, h) := by
2475 unfold C.run shr64; rw [ite_eq_left hn]; rfl
2476
2477theorem crc_big_table_size : Crc32H.crc_big_table.size = 256 := by decide +kernel
2478
2479theorem idx1_ok {α : Type} [Inhabited α] (h : Heap) (t : Array α) (i : Nat) (hi : i < t.size) :
2480 OkOn h (idx1 t i) t[i]! := by
2481 unfold idx1
2482 have : t[i]? = some t[i]! := by rw [getElem!_pos t i hi]; simp [hi]
2483 rw [this]
2484 exact OkOn.pure _ _
2485
2486/-- **crc_word_big, safety.** The big-endian word step (dead on the little-endian target, translated for
2487completeness) does not fault for any `data`; heap unchanged. Its correctness is not proved. -/
2488theorem crc_word_big_safe (h : Heap) (data : UInt64) :
2489 ∃ r, C.run (crc_word_big data) h = .ok (r, h) := by
2490 have key : ∃ y, OkOn h (crc_word_big data) y ∧ True := by
2491 unfold crc_word_big
2492 refine OkOn.bind_ex (R := fun _ : UInt64 × Int32 => True) ?_ (fun x _ => ⟨x.1, OkOn.pure _ _, trivial⟩)
2493 refine cwhile_ok (fun cnt s => ∃ i, i + cnt = 8 ∧ s.2 = Int32.ofNat i) (fun _ => True) ?_ ?_ (k := 8)
2494 (s := (data, (0 : Int32))) ⟨0, rfl, rfl⟩ (by show 8 < 2 ^ 64; omega)
2495 · intro cnt s hP
2496 obtain ⟨d, k⟩ := s
2497 obtain ⟨i, hi, hk⟩ := hP
2498 simp only at hk
2499 subst hk
2500 have hc : (Int32.ofNat i < (8 : Int32)) = true := by
2501 rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
2502 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
2503 have hk : Int32.ofNat i + 1 = Int32.ofNat (i + 1) := by
2504 rcases (by omega : i = 0 ∨ i = 1 ∨ i = 2 ∨ i = 3 ∨ i = 4 ∨ i = 5 ∨ i = 6 ∨ i = 7) with
2505 rfl | rfl | rfl | rfl | rfl | rfl | rfl | rfl <;> decide
2506 have ht := idx1_ok h Crc32H.crc_big_table ((d >>> 56) &&& 0xff).toNat (by
2507 rw [crc_big_table_size]; exact and255_lt _)
2508 refine ⟨(d, Int32.ofNat i), ((d <<< 8) ^^^ Crc32H.crc_big_table[((d >>> 56) &&& 0xff).toNat]!, Int32.ofNat (i + 1)),
2509 ?_, ?_, ⟨i + 1, by omega, rfl⟩⟩
2510 · show OkOn h (pure _) _
2511 simp only [W, hc]
2512 exact OkOn.pure _ _
2513 · show OkOn h (do let t ← idx1 Crc32H.crc_big_table (((d >>> 56) &&& 0xff).toNat); pure ((d <<< 8) ^^^ t, Int32.ofNat i + 1)) _
2514 rw [hk]
2515 exact OkOn.bind ht (OkOn.pure _ _)
2516 · intro s hP
2517 obtain ⟨d, k⟩ := s
2518 obtain ⟨i, hi, hk⟩ := hP
2519 simp only at hk
2520 subst hk
2521 have : i = 8 := by omega
2522 subst this
2523 have hc : (Int32.ofNat 8 < (8 : Int32)) = false := by decide
2524 refine ⟨(d, Int32.ofNat 8), ?_, trivial⟩
2525 show OkOn h (pure _) _
2526 simp only [W, hc]
2527 exact OkOn.pure _ _
2528 obtain ⟨y, hy, _⟩ := key
2529 unfold OkOn at hy
2530 refine ⟨y, ?_⟩
2531 unfold C.run
2532 exact hy
2533
2534/-! ### Non-vacuity: concrete heaps built by `allocBytes`
2535
2536The heap `h0` has the static tables `crc32.c` fixes (`initStatics`); `allocBytes` (the runtime function) puts a buffer in
2537a fresh block; the hypotheses of the theorems hold there and the translated functions run on it. -/
2538
2539/-- the empty heap with the static tables of `crc32.h` -/
2540def h0 : Heap := { Heap.empty with static := Zlib.Crc32.initStatics Heap.empty.static }
2541
2542/-- 100 bytes, enough for the braided path at an unaligned offset -/
2543def buf100 : Array UInt8 := (Array.range 100).map (fun i => UInt8.ofNat (i * 7 + 3))
2544
2545example : StaticsOK h0 := ⟨rfl, rfl⟩
2546
2547/-- `allocBytes` gives a pointer into block 0, in a heap where the hypotheses hold. -/
2548example : ∃ p h1, C.allocBytes buf100 h0 = .ok (p, h1) ∧ p = .ref (.blk 0) 0 ∧ StaticsOK h1 ∧
2549 h1.bytes 0 = some buf100 ∧ 3 + (90 : UInt64).toNat ≤ buf100.size :=
2550 ⟨_, _, rfl, rfl, ⟨rfl, rfl⟩, rfl, by decide +kernel⟩
2551
2552/-- `crc32_z_correct` on a concrete heap: 90 bytes from the unaligned offset 3 (alignment loop, braided loop,
2553tails), value as in the specification. -/
2554example : ∃ p h1, C.allocBytes buf100 h0 = .ok (p, h1) ∧
2555 C.run (crc32_z 0 (.ref (.blk 0) 3) 90) h1 =
2556 .ok ((ZlibF.Spec.crc32Update 0 ((buf100.toList.drop 3).take 90)).toUInt64, h1) :=
2557 ⟨_, _, rfl, crc32_z_correct _ ⟨rfl, rfl⟩ 0 0 3 buf100 rfl 90 (by decide +kernel)⟩
2558
2559/-- the heap after `allocBytes arr` on `h0` -/
2560def heapOf (arr : Array UInt8) : Heap := match C.allocBytes arr h0 with | .ok (_, h1) => h1 | .error _ => h0
2561
2562/-- the value of a run, `0` on a fault -/
2563def valOf (r : Except Fault (UInt64 × Heap)) : UInt64 := match r with | .ok (v, _) => v | .error _ => 0
2564
2565/-- the value computed by running the translation on the 90 bytes from offset 3 equals the specification (an
2566independent check of the theorem on this instance) -/
2567example : valOf (C.run (crc32_z 0 (.ref (.blk 0) 3) 90) (heapOf buf100)) =
2568 (ZlibF.Spec.crc32Update 0 ((buf100.toList.drop 3).take 90)).toUInt64 := by decide +kernel
2569
2570/-- `crc32("123456789") = 0xCBF43926`, by running the translation on a block made by `allocBytes` -/
2571example : valOf (C.run (crc32_z 0 (.ref (.blk 0) 0) 9) (heapOf "123456789".toUTF8.data)) = 0xCBF43926 := by
2572 decide +kernel
2573
2574/-- the same through the theorem: hypotheses hold, the result is the specification's -/
2575example : ∃ p h1, C.allocBytes "123456789".toUTF8.data h0 = .ok (p, h1) ∧
2576 C.run (crc32_z 0 p 9) h1 =
2577 .ok ((ZlibF.Spec.crc32Update 0 (("123456789".toUTF8.data.toList.drop 0).take 9)).toUInt64, h1) :=
2578 ⟨_, _, rfl, crc32_z_correct _ ⟨rfl, rfl⟩ 0 0 0 _ rfl 9 (by decide +kernel)⟩
2579
2580/-- the combine functions on a concrete instance -/
2581example : ∃ h1, StaticsOK h1 ∧
2582 C.run (crc32_combine64 (ZlibF.Spec.crc32 [1, 2, 3]).toUInt64 (ZlibF.Spec.crc32 [4, 5, 6, 7]).toUInt64 4) h1 =
2583 .ok ((ZlibF.Spec.crc32 [1, 2, 3, 4, 5, 6, 7]).toUInt64, h1) :=
2584 ⟨h0, ⟨rfl, rfl⟩, crc32_combine64_correct h0 ⟨rfl, rfl⟩ [1, 2, 3] [4, 5, 6, 7] 4 (by decide)⟩
2585
2586/-- the hang, on the translation -/
2587example : C.run (crc32_combine_op 1 2 ((1 : UInt64) <<< 32)) h0 = .error .outOfFuel :=
2588 crc32_combine_op_diverges h0 1 2 _ (by decide) (by decide)
2589
2590end Zlib.Crc32.Proofs