跳到正文
参考文档 / ZLIB × LEAN

deflate.c.engine.match.lean

此页源文件快照 · f260b9023639b31e · 对应提交的原文 ↗

本页保留 Lean 源码及行号。点击行号可定位语句,点击 import 中的模块名可查看依赖。

1/-!
2# deflate.c, engine: the match length of `longest_match`
3
4For a candidate match at window position `C` and the string at `S = strstart`, `longest_match` first compares
5`match[best_len]`, `match[best_len - 1]`, `match[0]`, `match[1]` with the scan; if all agree it assumes
6`match[2] = scan[2]` (the hash chain guarantees this, `hkey_third`) and compares from index 3 on, up to `MAX_MATCH = 258`.
7The unrolled loop compares eight positions per round and stops at the first mismatch or at `strend = S + 258`.
8The length is `258 - (strend - scan)`, that is the scan offset at which the loop stopped.
9
10`firstMis w S C j` is that offset when the loop is at offset `j - 1` and no position below `j` is known to
11differ: the first `k >= j` below 258 where the two strings differ, or 258. `firstMis_spec` is the characterisation
12the heap proof of the loop maintains step by step.
13-/
14
15namespace Zlib.Deflate.Engine.Proofs
16
17/-- The first `k` with `j <= k < 258` where `w (S + k) != w (C + k)`, or `258` when there is none. -/
18def firstMis (w : Nat → UInt8) (S C j : Nat) : Nat :=
19  if h : j < 258 then (if w (S + j) = w (C + j) then firstMis w S C (j + 1) else j) else 258
20termination_by 258 - j
21
22theorem firstMis_ge (w : Nat → UInt8) (S C : Nat) : ∀ n j, 258 - j = n → j ≤ 258 → j ≤ firstMis w S C j := by
23  intro n
24  induction n using Nat.strongRecOn with
25  | _ n ih =>
26    intro j hn hj
27    unfold firstMis
28    split
29    · rename_i h
30      split
31      · have := ih (258 - (j + 1)) (by omega) (j + 1) rfl (by omega)
32        omega
33      · omega
34    · omega
35
36theorem firstMis_le (w : Nat → UInt8) (S C : Nat) : ∀ n j, 258 - j = n → firstMis w S C j ≤ 258 := by
37  intro n
38  induction n using Nat.strongRecOn with
39  | _ n ih =>
40    intro j hn
41    unfold firstMis
42    split
43    · rename_i h
44      split
45      · exact ih (258 - (j + 1)) (by omega) (j + 1) rfl
46      · omega
47    · omega
48
49/-- The characterisation of `firstMis`: the strings agree on `[j, r)` and, when `r < 258`, differ at `r`. -/
50theorem firstMis_spec (w : Nat → UInt8) (S C : Nat) : ∀ n j, 258 - j = n → j ≤ 258 →
51    (∀ k, j ≤ k → k < firstMis w S C j → w (S + k) = w (C + k)) ∧
52    (firstMis w S C j < 258 → w (S + firstMis w S C j) ≠ w (C + firstMis w S C j)) := by
53  intro n
54  induction n using Nat.strongRecOn with
55  | _ n ih =>
56    intro j hn hj
57    have e : firstMis w S C j = if j < 258 then (if w (S + j) = w (C + j) then firstMis w S C (j + 1) else j) else 258 := by
58      rw [firstMis]; rfl
59    by_cases h : j < 258
60    · by_cases hw : w (S + j) = w (C + j)
61      · have e2 : firstMis w S C j = firstMis w S C (j + 1) := by rw [e]; simp [h, hw]
62        obtain ⟨a, b⟩ := ih (258 - (j + 1)) (by omega) (j + 1) rfl (by omega)
63        rw [e2]
64        refine ⟨fun k hk1 hk2 => ?_, b⟩
65        by_cases hkj : k = j
66        · subst hkj; exact hw
67        · exact a k (by omega) hk2
68      · have e2 : firstMis w S C j = j := by rw [e]; simp [h, hw]
69        rw [e2]
70        exact ⟨fun k hk1 hk2 => by omega, fun _ => hw⟩
71    · have e2 : firstMis w S C j = 258 := by rw [e]; simp [h]
72      rw [e2]
73      exact ⟨fun k hk1 hk2 => by omega, fun h' => by omega⟩
74
75/-- The scan offset the loop reaches is determined by agreement below it and disagreement at it: if the strings
76agree on `[j, r)`, `j <= r <= 258`, and differ at `r` when `r < 258`, then `r = firstMis j`. -/
77theorem firstMis_unique (w : Nat → UInt8) (S C : Nat) {j r : Nat} (hj : j ≤ r) (hr : r ≤ 258)
78    (hag : ∀ k, j ≤ k → k < r → w (S + k) = w (C + k)) (hst : r < 258 → w (S + r) ≠ w (C + r)) :
79    firstMis w S C j = r := by
80  obtain ⟨a, b⟩ := firstMis_spec w S C _ j rfl (by omega)
81  have hge := firstMis_ge w S C _ j rfl (by omega)
82  have hle := firstMis_le w S C _ j rfl
83  refine Classical.byContradiction fun hne => ?_
84  rcases Nat.lt_or_gt_of_ne hne with hlt | hgt
85  · exact b (by omega) (hag _ hge hlt)
86  · by_cases h258 : r < 258
87    · exact hst h258 (a r hj hgt)
88    · omega
89
90/-- The length that `longest_match` computes for a candidate that passed the four early tests and whose third
91byte agrees: `firstMis` from index 3. It is at least 3 and at most 258, and the strings agree on `[0, len)`
92given they agree on `[0, 3)`. -/
93def matchLen (w : Nat → UInt8) (S C : Nat) : Nat := firstMis w S C 3
94
95theorem matchLen_bounds (w : Nat → UInt8) (S C : Nat) : 3 ≤ matchLen w S C ∧ matchLen w S C ≤ 258 :=
96  ⟨firstMis_ge w S C _ 3 rfl (by omega), firstMis_le w S C _ 3 rfl⟩
97
98/-- If the strings at `S` and `C` agree on the first three bytes, they agree on `[0, matchLen)`: the bytes before the
99first position from index 3 on where they differ (or 258). -/
100theorem matchLen_agree (w : Nat → UInt8) (S C : Nat)
101    (h0 : w S = w C) (h1 : w (S + 1) = w (C + 1)) (h2 : w (S + 2) = w (C + 2)) :
102    ∀ j, j < matchLen w S C → w (S + j) = w (C + j) := by
103  intro j hj
104  by_cases h3 : j < 3
105  · obtain h | h | h : j = 0 ∨ j = 1 ∨ j = 2 := by omega
106    · subst h; simpa using h0
107    · subst h; exact h1
108    · subst h; exact h2
109  · exact (firstMis_spec w S C _ 3 rfl (by omega)).1 j (by omega) hj
110
111theorem matchLen_stop (w : Nat → UInt8) (S C : Nat) (h : matchLen w S C < 258) :
112    w (S + matchLen w S C) ≠ w (C + matchLen w S C) :=
113  (firstMis_spec w S C _ 3 rfl (by omega)).2 h
114
115/-! ## The best length
116
117`best_len` only grows, and only to a length the loop measured. The cap at `lookahead` at the end keeps a
118returned length below the valid data. -/
119
120/-- The result `longest_match` returns: `best_len` if it is at most `lookahead`, else `lookahead`. -/
121def lmResult (best la : Nat) : Nat := if best ≤ la then best else la
122
123theorem lmResult_le (best la : Nat) : lmResult best la ≤ best ∧ lmResult best la ≤ la := by
124  unfold lmResult; split <;> omega
125
126end Zlib.Deflate.Engine.Proofs
ZLIB × LEAN

全局目录

当前位置deflate.c.engine.match.lean