|
1 | 1 | import Mathlib.Computability.Language |
2 | 2 | import Mathlib.Data.Finset.Basic |
3 | | - |
4 | | -variable {α : Type*} [DecidableEq α] |
5 | | - |
6 | | -/-- An alphabet is a finite set of symbols. -/ |
7 | | -abbrev Alphabet α := Finset α |
8 | | - |
9 | 3 | namespace List |
10 | 4 |
|
11 | | -/-- This theorem provides an induction principle for lists where the inductive step |
12 | | -appends an element to the right (i.e., builds lists by snoc rather than cons). |
13 | | -
|
14 | | -The intuition is that, instead of the usual head recursion, we want to prove a property |
15 | | -for all lists by showing: |
16 | | -- the property holds for the empty list, and |
17 | | -- if it holds for a list `l`, then it holds for `l ++ [a]` for any `a`. -/ |
18 | | -theorem induction_right {α} {P : List α → Prop} |
19 | | - (nil : P []) |
20 | | - (snoc : ∀ (l : List α) (a : α), P l → P (l ++ [a])) : |
21 | | - ∀ l : List α, P l := by |
22 | | - /- |
23 | | - We want to prove P l for all lists l, using right induction. |
24 | | - However, Lean's built-in induction on lists is left-sided (on cons), not right-sided (on snoc). |
25 | | - To overcome this, we use a classic strengthening technique: |
26 | | - instead of proving just P l, we prove a stronger property |
27 | | - Q l := ∀ k, P k → P (k ++ l) |
28 | | - This means: if we know P holds for any prefix k, then it also holds for k ++ l. |
29 | | - Proving Q l for all l is strictly stronger than just P l, but it allows us to perform induction on l (using cons), |
30 | | - and at the end, we recover the original goal by taking k = []. |
31 | | - -/ |
32 | | - intro l |
33 | | - let Q := fun (l : List α) => ∀ (k : List α), P k → P (k ++ l) |
34 | | - -- Base case: l = []. We must show Q []: ∀ k, P k → P (k ++ []). |
35 | | - have h_base : Q [] := by |
36 | | - intro k hk |
37 | | - -- In this case, k ++ [] = k, so P (k ++ []) = P k, which is exactly hk. |
38 | | - rw [append_nil] |
39 | | - exact hk |
40 | | - -- Inductive step: assume Q l', show Q (a :: l') for any a. |
41 | | - have h_step : ∀ (a : α) (l' : List α), Q l' → Q (a :: l') := by |
42 | | - intros a l' IH k hk |
43 | | - /- |
44 | | - Goal: P (k ++ (a :: l')) given P k. |
45 | | - Observe: k ++ (a :: l') = (k ++ [a]) ++ l'. |
46 | | - By the snoc hypothesis, from P k we get P (k ++ [a]). |
47 | | - By the induction hypothesis (IH), from P (k ++ [a]) we get P ((k ++ [a]) ++ l'). |
48 | | - Thus, we chain these two steps to get the result. |
49 | | - -/ |
50 | | - have hka : P (k ++ [a]) := snoc k a hk |
51 | | - have : k ++ (a :: l') = (k ++ [a]) ++ l' := by simp [append_assoc] |
52 | | - rw [this] |
53 | | - exact IH (k ++ [a]) hka |
54 | | - -- Now, by induction on l (using the usual left induction), we get Q l for all l. |
55 | | - have hQ : ∀ l, Q l := by |
56 | | - intro l' |
57 | | - induction l' with |
58 | | - | nil => exact h_base |
59 | | - | cons a l'' IH => exact h_step a l'' IH |
60 | | - -- Finally, to recover the original goal, specialize to k = [] and use h_nil : P []. |
61 | | - exact hQ l [] nil |
62 | | - |
63 | | -/-- The projection of a word onto an alphabet. Removes all symbols not in the alphabet. -/ |
64 | | -def proj (Sigma : Alphabet α) (w : List α) : List α := |
65 | | - match w with |
66 | | - | [] => [] |
67 | | - | a :: w' => if a ∈ Sigma then a :: proj Sigma w' else proj Sigma w' |
68 | | - |
69 | | -/-- Projection distributes over concatenation. -/ |
70 | | -@[simp] |
71 | | -lemma proj_distrib_over_concat (Sigma : Alphabet α) (w₁ w₂ : List α) : |
72 | | - proj Sigma (w₁ ++ w₂) = proj Sigma w₁ ++ proj Sigma w₂ := |
73 | | - by |
74 | | - -- We proceed by induction on w₁. |
75 | | - induction w₁ with |
76 | | - | nil => |
77 | | - -- Base case: w₁ = []. |
78 | | - -- proj Sigma ([] ++ w₂) = proj Sigma w₂, and proj Sigma [] = [], so the equality holds. |
79 | | - simp [proj] |
80 | | - | cons a w₁' IH => |
81 | | - -- Inductive step: w₁ = a :: w₁'. |
82 | | - -- We consider whether a ∈ Sigma. |
83 | | - by_cases h : a ∈ Sigma |
84 | | - · -- If a ∈ Sigma, then proj Sigma (a :: (w₁' ++ w₂)) = a :: proj Sigma (w₁' ++ w₂) |
85 | | - -- By induction, proj Sigma (w₁' ++ w₂) = proj Sigma w₁' ++ proj Sigma w₂ |
86 | | - -- So the result is a :: (proj Sigma w₁' ++ proj Sigma w₂) = (a :: proj Sigma w₁') ++ proj Sigma w₂ |
87 | | - simp [proj, h, IH] |
88 | | - · -- If a ∉ Sigma, then a is skipped in the projection. |
89 | | - -- So proj Sigma (a :: (w₁' ++ w₂)) = proj Sigma (w₁' ++ w₂) |
90 | | - -- By induction, this is proj Sigma w₁' ++ proj Sigma w₂ |
91 | | - simp [proj, h, IH] |
92 | | - |
93 | | -/-- Projection commutes with reversal. -/ |
94 | | -@[simp] |
95 | | -lemma proj_commutes_with_reverse (Sigma : Alphabet α) (w : List α) : |
96 | | - reverse (proj Sigma w) = proj Sigma (reverse w) := by |
97 | | - -- We proceed by induction on w. |
98 | | - induction w with |
99 | | - | nil => |
100 | | - -- Base case: w = []. |
101 | | - -- Both sides are reverse [] = [] and proj Sigma [] = [], so equality holds. |
102 | | - rfl |
103 | | - | cons a w' IH => |
104 | | - -- Inductive step: w = a :: w'. |
105 | | - -- Consider whether a ∈ Sigma. |
106 | | - by_cases h : a ∈ Sigma |
107 | | - · -- If a ∈ Sigma, then proj Sigma (a :: w') = a :: proj Sigma w'. |
108 | | - -- reverse (a :: proj Sigma w') = reverse (proj Sigma w') ++ [a] |
109 | | - -- By induction, reverse (proj Sigma w') = proj Sigma (reverse w') |
110 | | - -- So left side is proj Sigma (reverse w') ++ [a] |
111 | | - -- On the right, reverse (a :: w') = reverse w' ++ [a], so proj Sigma (reverse w' ++ [a]) |
112 | | - -- By proj_distrib_over_concat, this is proj Sigma (reverse w') ++ proj Sigma [a] |
113 | | - -- Since a ∈ Sigma, proj Sigma [a] = [a], so both sides match. |
114 | | - simp [proj, h, IH] |
115 | | - · -- If a ∉ Sigma, then proj Sigma (a :: w') = proj Sigma w'. |
116 | | - -- So reverse (proj Sigma w') = proj Sigma (reverse w') by induction. |
117 | | - -- On the right, reverse (a :: w') = reverse w' ++ [a], proj Sigma (reverse w' ++ [a]) |
118 | | - -- By proj_distrib_over_concat, this is proj Sigma (reverse w') ++ proj Sigma [a] |
119 | | - -- Since a ∉ Sigma, proj Sigma [a] = [], so right side is proj Sigma (reverse w') |
120 | | - simp [proj, h, IH] |
121 | | - |
122 | | -/-- Cancel the first occurrence of a symbol (if any) from the left side of a word. -/ |
123 | | -def cancel_left (w : List α) (a : α) : List α := |
124 | | - match w with |
125 | | - | [] => [] |
126 | | - | b :: w' => if a = b then w' else b :: cancel_left w' a |
127 | | - |
128 | | -@[simp] |
129 | | -lemma cancel_left_nil (a : α) : cancel_left [] a = [] := by rfl |
| 5 | +variable {α : Type} [DecidableEq α] |
130 | 6 |
|
131 | | -@[simp] |
132 | | -lemma cancel_left_cons (w : List α) (a b : α) : |
133 | | - cancel_left (b :: w) a = if a = b then w else b :: cancel_left w a := by |
134 | | - rfl |
| 7 | +/-- The projection of a string onto an alpabet. Removes all symbols not in the Finset.-/ |
| 8 | +def proj (S : Finset α) (w : List α) : List α := w.filter (· ∈ S) |
135 | 9 |
|
136 | | -/-- If the symbol `a` is not in the alphabet, |
137 | | -then projecting and then cancelling `a` from the left does nothing. -/ |
138 | 10 | @[simp] |
139 | | -lemma cancel_left_proj_eq_self_when_symb_notin_alph (Sigma : Alphabet α) (w : List α) (a : α) : |
140 | | - a ∉ Sigma → cancel_left (proj Sigma w) a = proj Sigma w := by |
141 | | - intro h |
142 | | - -- We proceed by induction on w. |
143 | | - induction w with |
144 | | - | nil => |
145 | | - -- Base case: w = []. |
146 | | - -- Both sides are cancel_left [] a = [] and proj Sigma [] = [], so equality holds. |
147 | | - rfl |
148 | | - | cons b w' IH => |
149 | | - -- Inductive step: w = b :: w'. |
150 | | - -- Consider whether a = b. |
151 | | - by_cases h_ab : a = b |
152 | | - · -- If a = b, then in proj Sigma w, b will only appear if b ∈ Sigma. |
153 | | - -- But since a ∉ Sigma, b ∉ Sigma, so proj Sigma (b :: w') = proj Sigma w'. |
154 | | - -- cancel_left (proj Sigma w') a = proj Sigma w' by induction. |
155 | | - have h_bS : b ∉ Sigma := h_ab ▸ h |
156 | | - simp [proj, h_bS] |
157 | | - exact IH |
158 | | - · -- If a ≠ b, consider whether b ∈ Sigma. |
159 | | - by_cases h_bS : b ∈ Sigma |
160 | | - · -- If b ∈ Sigma, then proj Sigma (b :: w') = b :: proj Sigma w'. |
161 | | - -- cancel_left (b :: proj Sigma w') a = b :: cancel_left (proj Sigma w') a |
162 | | - -- By induction, cancel_left (proj Sigma w') a = proj Sigma w', so both sides match. |
163 | | - simp [proj, h_bS] |
164 | | - simp [h_ab] |
165 | | - exact IH |
166 | | - · -- If b ∉ Sigma, then proj Sigma (b :: w') = proj Sigma w'. |
167 | | - -- cancel_left (proj Sigma w') a = proj Sigma w' by induction. |
168 | | - simp [proj, h_bS] |
169 | | - exact IH |
| 11 | +lemma proj_append (S : Finset α) (w₁ w₂ : List α) : |
| 12 | + proj S (w₁ ++ w₂) = proj S w₁ ++ proj S w₂ := by |
| 13 | + simp [proj] |
170 | 14 |
|
171 | | -/-- Projection commutes with left-cancellation -/ |
172 | 15 | @[simp] |
173 | | -lemma proj_commutes_with_cancel_left (Sigma : Alphabet α) (w : List α) (a : α) : |
174 | | - cancel_left (proj Sigma w) a = proj Sigma (cancel_left w a) := by |
175 | | - -- We prove by induction on w. |
176 | | - induction w with |
177 | | - | nil => |
178 | | - -- Base case: w = []. |
179 | | - -- Both sides are cancel_left [] a = [] and proj Sigma [] = [], so equality holds. |
180 | | - rfl |
181 | | - | cons b w' IH => |
182 | | - -- Inductive step: w = b :: w'. |
183 | | - -- Consider whether b ∈ Sigma. |
184 | | - by_cases h_bS : b ∈ Sigma |
185 | | - · -- If b ∈ Sigma, then proj Sigma (b :: w') = b :: proj Sigma w'. |
186 | | - simp [proj, h_bS] |
187 | | - -- Now consider whether a = b. |
188 | | - by_cases h_ab : a = b |
189 | | - · -- If a = b, then cancel_left (b :: proj Sigma w') a = proj Sigma w'. |
190 | | - -- On the right, cancel_left (b :: w') a = w', so proj Sigma w' = proj Sigma w'. |
191 | | - simp [h_ab] |
192 | | - · -- If a ≠ b, then cancel_left (b :: proj Sigma w') a = b :: cancel_left (proj Sigma w') a |
193 | | - -- By induction, cancel_left (proj Sigma w') a = proj Sigma (cancel_left w' a) |
194 | | - -- On the right, cancel_left (b :: w') a = b :: cancel_left w' a, so proj Sigma (b :: cancel_left w' a) = b :: proj Sigma (cancel_left w' a) |
195 | | - simp [h_ab] |
196 | | - simp [proj, h_bS] |
197 | | - exact IH |
198 | | - · -- If b ∉ Sigma, then proj Sigma (b :: w') = proj Sigma w'. |
199 | | - by_cases h_ab : a = b |
200 | | - · -- If a = b, then cancel_left (b :: w') a = w', so proj Sigma w' = proj Sigma w'. |
201 | | - simp [cancel_left, h_ab] |
202 | | - simp [proj, h_bS] |
203 | | - · -- If a ≠ b, then cancel_left (b :: w') a = b :: cancel_left w' a, but b ∉ Sigma so proj Sigma (b :: cancel_left w' a) = proj Sigma (cancel_left w' a) |
204 | | - -- By induction, cancel_left (proj Sigma w') a = proj Sigma (cancel_left w' a) |
205 | | - simp [cancel_left, h_ab] |
206 | | - simp [proj, h_bS] |
207 | | - exact IH |
| 16 | +lemma proj_reverse (S : Finset α) (w : List α) : |
| 17 | + reverse (proj S w) = proj S (reverse w) := by |
| 18 | + simp [proj] |
208 | 19 |
|
209 | 20 | /-- Cancel the first occurrence of a symbol (if any) from the right of a word. -/ |
210 | | -def cancel_right (w : List α) (a : α) : List α := |
211 | | - reverse (cancel_left (reverse w) a) |
| 21 | +def cancelRight (w : List α) (a : α) : List α := reverse (List.erase (reverse w) a) |
212 | 22 |
|
213 | | -@[simp] |
214 | | -lemma cancel_right_nil (a : α) : cancel_right [] a = [] := by |
215 | | - -- By definition, cancel_right [] a = reverse (cancel_left (reverse []) a) = reverse (cancel_left [] a) = reverse [] = [] |
216 | | - simp [cancel_right] |
217 | | - |
218 | | -lemma cancel_right_snoc (w : List α) (a b : α) : |
219 | | - cancel_right (w ++ [b]) a = if a = b then w else cancel_right w a ++ [b] := by |
220 | | - by_cases h : a = b |
221 | | - · -- If a = b, then cancel_right (w ++ [b]) a = reverse (cancel_left (reverse (w ++ [b])) a) |
222 | | - -- = reverse (cancel_left (reverse [b] ++ reverse w) a) = reverse (cancel_left ([b] ++ reverse w) a) |
223 | | - -- = reverse (cancel_left (b :: reverse w) a) = reverse (reverse w) = w |
224 | | - simp [cancel_right, h] |
225 | | - · -- If a ≠ b, then cancel_right (w ++ [b]) a = reverse (cancel_left (reverse (w ++ [b])) a) |
226 | | - -- = reverse (cancel_left (reverse [b] ++ reverse w) a) = reverse (cancel_left ([b] ++ reverse w) a) |
227 | | - -- = reverse (cancel_left (b :: reverse w) a) = reverse (b :: cancel_left (reverse w) a) = reverse (cancel_left (reverse w) a) ++ [b] = cancel_right w a ++ [b] |
228 | | - simp [cancel_right, h] |
229 | | - |
230 | | -lemma cancel_left_eq_rev_cancel_right_rev (w : List α) (a : α) : |
231 | | - cancel_left w a = reverse (cancel_right (reverse w) a) := by |
232 | | - simp [cancel_right] |
| 23 | +/-- Notation for right cancellation. -/ |
| 24 | +infixl:65 " ÷ " => List.cancelRight |
233 | 25 |
|
234 | | -/-- If the symbol `a` is not in the alphabet, |
235 | | -then projecting and then cancelling `a` from the right does nothing. -/ |
236 | 26 | @[simp] |
237 | | -lemma cancel_right_proj_eq_self_when_symb_notin_alph (Sigma : Alphabet α) (w : List α) (a : α) : |
238 | | - a ∉ Sigma → cancel_right (proj Sigma w) a = proj Sigma w := by |
239 | | - induction w using induction_right with |
240 | | - | nil => |
241 | | - -- Base case: w = []. |
242 | | - -- Both sides are cancel_right [] a = [] and proj Sigma [] = [], so equality holds. |
243 | | - simp [cancel_right, proj] |
244 | | - | snoc w' b IH => |
245 | | - intro h |
246 | | - by_cases h_ab : a = b |
247 | | - · -- Case 1: a = b. Since a ∉ Sigma, b ∉ Sigma as well. |
248 | | - -- The projection removes b, so proj Sigma (w' ++ [b]) = proj Sigma w'. |
249 | | - -- Right-cancelling a from proj Sigma w is the same as right-cancelling from proj Sigma w', |
250 | | - -- which by the induction hypothesis is just proj Sigma w'. |
251 | | - have h_bS : b ∉ Sigma := h_ab ▸ h |
252 | | - simp [proj, h_bS] |
253 | | - exact IH h |
254 | | - · -- Case 2: a ≠ b. Now consider whether b ∈ Sigma. |
255 | | - by_cases h_bS : b ∈ Sigma |
256 | | - · -- Subcase: b ∈ Sigma. Then proj Sigma (w' ++ [b]) = proj Sigma w' ++ [b]. |
257 | | - -- Right-cancelling a from proj Sigma w is cancel_right (proj Sigma w') a ++ [b]. |
258 | | - -- By the induction hypothesis, cancel_right (proj Sigma w') a = proj Sigma w', so both sides match. |
259 | | - simp [proj, h_bS] |
260 | | - rw [List.cancel_right_snoc] |
261 | | - simp [h_ab] |
262 | | - exact IH h |
263 | | - · -- Subcase: b ∉ Sigma. Then proj Sigma (w' ++ [b]) = proj Sigma w'. |
264 | | - -- Right-cancelling a from proj Sigma w is the same as right-cancelling from proj Sigma w', |
265 | | - -- which by the induction hypothesis is just proj Sigma w'. |
266 | | - simp [proj, h_bS] |
267 | | - exact IH h |
| 27 | +lemma cancelRight_nil (a : α) : [] ÷ a = [] := by rfl |
268 | 28 |
|
269 | 29 | @[simp] |
270 | | -lemma singleton_cancel_right {a : α} : [a].cancel_right a = [] := by |
271 | | - rw [← List.nil_append [a], List.cancel_right_snoc] |
272 | | - simp |
| 30 | +lemma cancelRight_snoc (w : List α) (a b : α) : |
| 31 | + w ++ [b] ÷ a = if b = a then w else w ÷ a ++ [b] := by |
| 32 | + split_ifs with heq <;> simp [cancelRight, heq] |
273 | 33 |
|
274 | | -@[simp] |
275 | | -lemma cancel_right_over_concat {w₁ w₂ : List α} (a : α) : |
276 | | - (w₁ ++ w₂).cancel_right a |
277 | | - = if (a ∈ w₂) then w₁ ++ (w₂.cancel_right a) else (w₁.cancel_right a) ++ w₂ := by |
278 | | - induction w₂ using List.induction_right with |
| 34 | +lemma append_cancelRight {w₁ w₂ : List α} (a : α) : |
| 35 | + (w₁ ++ w₂) ÷ a = if a ∈ w₂ then w₁ ++ (w₂ ÷ a) else (w₁ ÷ a) ++ w₂ := by |
| 36 | + induction w₂ using List.reverseRecOn with |
279 | 37 | | nil => |
280 | 38 | simp |
281 | | - | snoc w₂' a' ih => |
282 | | - nth_rw 1 [← List.append_assoc] |
283 | | - by_cases ha : a ∈ w₂' |
284 | | - all_goals |
285 | | - by_cases heq : a = a' |
286 | | - · rw [List.cancel_right_snoc, List.cancel_right_snoc] |
287 | | - simp [heq] |
288 | | - · rw [List.cancel_right_snoc, List.cancel_right_snoc] |
289 | | - simp [ha, heq, ih] |
290 | | - |
291 | | -/-- Notation for right cancellation. -/ |
292 | | -infixl:65 " ÷ " => List.cancel_right |
| 39 | + | append_singleton w₂' b ih => |
| 40 | + rw [← List.append_assoc, cancelRight_snoc] |
| 41 | + by_cases heq : b = a <;> by_cases h_mem : a ∈ w₂' |
| 42 | + · simp [heq] |
| 43 | + · simp [heq] |
| 44 | + · simp [heq, h_mem, ih] |
| 45 | + · simp [heq, h_mem, ih, Ne.symm] |
293 | 46 |
|
294 | | -/-- Projection commutes with right-cancellation. -/ |
295 | 47 | @[simp] |
296 | | -lemma proj_commutes_with_cancel_right (Sigma : Alphabet α) (w : List α) (a : α) : |
297 | | - cancel_right (proj Sigma w) a = proj Sigma (cancel_right w a) := by |
298 | | - -- The right-cancellation is defined as reversing, left-cancelling, then reversing again. |
299 | | - -- Since proj and reverse commute (by proj_and_reverse_commute), the operations can be swapped. |
300 | | - simp [cancel_right, proj_commutes_with_reverse] |
| 48 | +lemma singleton_cancelRight {a : α} : [a] ÷ a = [] := by simp [cancelRight] |
| 49 | + |
| 50 | +lemma proj_cancel_right (S : Finset α) (w : List α) (a : α) : |
| 51 | + proj S (w ÷ a) = if a ∈ S then proj S w ÷ a else proj S w := by |
| 52 | + induction w using List.reverseRecOn with |
| 53 | + | nil => |
| 54 | + simp [proj] |
| 55 | + | append_singleton w' b ih => |
| 56 | + split_ifs with ha <;> by_cases heq : b = a |
| 57 | + · simp [heq, ha, proj] |
| 58 | + · simp [heq, ha, ih] |
| 59 | + by_cases hb : b ∈ S |
| 60 | + · simp [proj, hb, heq] |
| 61 | + · simp [proj, hb] |
| 62 | + · simp [heq, ha, proj] |
| 63 | + · simp [heq, ha, ih] |
301 | 64 |
|
302 | 65 | end List |
303 | 66 |
|
|
0 commit comments