-
Notifications
You must be signed in to change notification settings - Fork 232
Expand file tree
/
Copy pathAppendix_B_1.lean
More file actions
338 lines (281 loc) · 12.5 KB
/
Appendix_B_1.lean
File metadata and controls
338 lines (281 loc) · 12.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
import Mathlib.Tactic
/-!
# Analysis I, Appendix B.1: The decimal representation of natural numbers
Am implementation of the decimal representation of Mathlib's natural numbers {lean}`ℕ`.
This is separate from the way decimal numerals are already represenated in Mathlib via the {name}`OfNat` typeclass.
-/
namespace AppendixB
/- The ten digits, together with the base 10 -/
example : 0 = Nat.zero := rfl
example : 1 = (0:Nat).succ := rfl
example : 2 = (1:Nat).succ := rfl
example : 3 = (2:Nat).succ := rfl
example : 4 = (3:Nat).succ := rfl
example : 5 = (4:Nat).succ := rfl
example : 6 = (5:Nat).succ := rfl
example : 7 = (6:Nat).succ := rfl
example : 8 = (7:Nat).succ := rfl
example : 9 = (8:Nat).succ := rfl
example : 10 = (9:Nat).succ := rfl
/-- Definition B.1.1 -/
def Digit := Fin 10
instance Digit.instZero : Zero Digit := ⟨0, by decide⟩
instance Digit.instOne : One Digit := ⟨1, by decide⟩
instance Digit.instTwo : OfNat Digit 2 := ⟨2, by decide⟩
instance Digit.instThree : OfNat Digit 3 := ⟨3, by decide⟩
instance Digit.instFour : OfNat Digit 4 := ⟨4, by decide⟩
instance Digit.instFive : OfNat Digit 5 := ⟨5, by decide⟩
instance Digit.instSix : OfNat Digit 6 := ⟨6, by decide⟩
instance Digit.instSeven : OfNat Digit 7 := ⟨7, by decide⟩
instance Digit.instEight : OfNat Digit 8 := ⟨8, by decide⟩
instance Digit.instNine : OfNat Digit 9 := ⟨9, by decide⟩
instance Digit.instFintype : Fintype Digit := Fin.fintype 10
instance Digit.instDecidableEq : DecidableEq Digit := instDecidableEqFin 10
instance Digit.instInhabited : Inhabited Digit := ⟨ 0 ⟩
@[coe]
abbrev Digit.toNat (d:Digit) : ℕ := d.val
instance Digit.instCoeNat : Coe Digit Nat where
coe := toNat
theorem Digit.lt (d:Digit) : (d:ℕ) < 10 := d.isLt
abbrev Digit.mk {n:ℕ} (h: n < 10) : Digit := ⟨n, h⟩
@[simp]
theorem Digit.toNat_mk {n:ℕ} (h: n < 10) : (Digit.mk h:ℕ) = n := rfl
@[simp]
theorem Digit.inj (d d':Digit) : d = d' ↔ (d:ℕ) = d' := by grind
theorem Digit.mk_eq_iff (d:Digit) {n:ℕ} (h: n < 10) : d = mk h ↔ (d:ℕ) = n := by
convert Digit.inj d (mk h)
#check (0:Digit)
#check (1:Digit)
#check (2:Digit)
#check (3:Digit)
#check (4:Digit)
#check (5:Digit)
#check (6:Digit)
#check (7:Digit)
#check (8:Digit)
#check (9:Digit)
theorem Digit.eq (n: Digit) : n = 0 ∨ n = 1 ∨ n = 2 ∨ n = 3 ∨ n = 4 ∨ n = 5 ∨ n = 6 ∨ n = 7 ∨ n = 8 ∨ n = 9 := by
fin_cases n <;> simp +decide
/-- Definition B.1.2 -/
structure PosintDecimal where
digits : List Digit
nonempty : digits ≠ []
nonzero : digits.head nonempty ≠ 0
theorem PosintDecimal.congr' {p q:PosintDecimal} (h: p.digits = q.digits) : p = q := by
obtain ⟨ pd, _, _ ⟩ := p
obtain ⟨ qd, _, _ ⟩ := q
congr
theorem PosintDecimal.congr {p q:PosintDecimal} (h: p.digits.length = q.digits.length)
(h': ∀ (n:ℕ) (h₁ : n < p.digits.length) (h₂: n < q.digits.length), p.digits.get ⟨ n, h₁ ⟩ = q.digits.get ⟨ n, h₂ ⟩) : p = q := by
apply congr'
simp_all [List.ext_get_iff]
abbrev PosintDecimal.head (p:PosintDecimal): Digit := p.digits.head p.nonempty
theorem PosintDecimal.head_ne_zero (p:PosintDecimal) : p.head ≠ 0 := p.nonzero
theorem PosintDecimal.head_ne_zero' (p:PosintDecimal) : (p.head:ℕ) ≠ 0 := by
by_contra!
apply head_ne_zero p
simp_all [Digit.toNat, Digit.inj]; rfl
theorem PosintDecimal.length_pos (p:PosintDecimal) : 0 < p.digits.length := by
simp [List.length_pos_iff, p.nonempty]
/-- A slightly clunky way of creating decimals. -/
def PosintDecimal.mk' (head:Digit) (tail:List Digit) (h: head ≠ 0) : PosintDecimal := {
digits := head :: tail
nonempty := by aesop
nonzero := h
}
-- the positive integer decimal 314
#check PosintDecimal.mk' 3 [1, 4] (by decide)
-- the positive integer decimal 3
#check PosintDecimal.mk' 3 [] (by decide)
-- the positive integer decimal 10
#check PosintDecimal.mk' 1 [0] (by decide)
/-- We are indexing digits in a decimal from left to right rather than from right to left, thus necessitating a reversal here. -/
@[coe]
def PosintDecimal.toNat (p:PosintDecimal) : Nat :=
∑ i:Fin p.digits.length, p.digits[p.digits.length - 1 - ↑i].toNat * 10 ^ (i:ℕ)
instance PosintDecimal.instCoeNat : Coe PosintDecimal Nat where
coe := toNat
example : (PosintDecimal.mk' 3 [1, 4] (by decide):ℕ) = 314 := by decide
/-- Remark B.1.3 -/
@[simp]
theorem PosintDecimal.ten_eq_ten : (mk' 1 [0] (by decide):ℕ) = 10 := by
decide
theorem PosintDecimal.digit_eq {d:Digit} (h: d ≠ 0) : (mk' d [] h:ℕ) = d := by
simp [toNat, mk']
theorem PosintDecimal.pos (p:PosintDecimal) : 0 < (p:ℕ) := by
simp [toNat]
calc
_ < (p.head:ℕ) * 10 ^ (p.digits.length - 1) := by
have := p.head_ne_zero'
positivity
_ ≤ _ := by
have := p.length_pos
set a : Fin p.digits.length := ⟨ p.digits.length - 1, by omega ⟩
convert Finset.single_le_sum _ (Finset.mem_univ a)
. simp [a, head, List.head_eq_getElem]
. infer_instance
grind
/-- An operation implicit in the proof of Theorem B.1.4: -/
abbrev PosintDecimal.append (p:PosintDecimal) (d:Digit) : PosintDecimal :=
mk' p.head (p.digits.tail ++ [d]) p.head_ne_zero
/-- {name}`toNat` equals Horner (left-fold) evaluation of the digit list. -/
theorem PosintDecimal.toNat_eq_foldl (q : PosintDecimal) :
q.toNat = q.digits.foldl (fun acc (d : Digit) => acc * 10 + d.toNat) 0 := by
suffices h : ∀ (L : List Digit) (acc : ℕ),
L.foldl (fun a (d : Digit) => a * 10 + d.toNat) acc =
acc * 10 ^ L.length + ∑ i : Fin L.length, (L[L.length - 1 - ↑i]).toNat * 10 ^ (↑i : ℕ)
from by simp [toNat, h q.digits 0]
intro L; induction L with
| nil => simp
| cons a t ih =>
intro acc; simp only [List.foldl_cons, List.length_cons]
-- Decompose the Fin (t.length+1) sum: last term is a*10^|t|, rest matches the Fin t.length sum
have : ∑ x : Fin (t.length + 1), ((a :: t)[t.length - ↑x] : ℕ) * 10 ^ (↑x : ℕ) =
(∑ x : Fin t.length, (t[t.length - 1 - ↑x] : ℕ) * 10 ^ (↑x : ℕ)) + a * 10 ^ t.length := by
refine (Fin.sum_univ_castSucc _).trans ?_
congr 1 <;> grind
grind
@[simp]
theorem PosintDecimal.append_toNat (p:PosintDecimal) (d:Digit) :
(p.append d:ℕ) = d.toNat + 10 * p.toNat := by
rw [toNat_eq_foldl, toNat_eq_foldl]; simp only [append, mk']
rw [show p.head :: (p.digits.tail ++ [d]) = p.digits ++ [d] from by
simp [head, ← List.cons_append, List.cons_head_tail]]
rw [List.foldl_append]; simp [List.foldl]; ring
theorem PosintDecimal.eq_append {p:PosintDecimal} (h: 2 ≤ p.digits.length) : ∃ (q:PosintDecimal) (d:Digit), p = q.append d := by
use mk' p.head (p.digits.tail.dropLast) p.head_ne_zero
set a := p.digits.getLast p.nonempty; use a
apply congr'
simp [mk']
rw [←p.digits.cons_head_tail p.nonempty]
congr 1
convert (List.dropLast_append_getLast _).symm using 2; grind
simp [←List.length_pos_iff]; omega
/-- Theorem B.1.4 (Uniqueness and existence of decimal representations) -/
theorem PosintDecimal.exists_unique (n:ℕ) : n > 0 → ∃! p:PosintDecimal, (p:ℕ) = n := by
-- this proof is written to follow the structure of the original text.
apply n.case_strong_induction_on
. simp
-- note: the variable `m` in the text is referred to as `m+1` here.
clear n; intro m hind _
obtain hm | hm := lt_or_ge m 9
. apply ExistsUnique.intro (mk' (.mk (show m+1 < 10 by omega)) [] (by simp [Digit.mk]))
. simp [mk', Digit.mk, toNat, Digit.toNat]
intro d hd
obtain hdl | hdl := lt_or_ge d.digits.length 2
. replace hdl : d.digits.length = 1 := by linarith [d.length_pos]
have _subsing : Subsingleton (Fin d.digits.length) := by simp [Fin.subsingleton_iff_le_one, hdl]
let zero : Fin d.digits.length := ⟨ 0, by omega ⟩
simp [toNat, hdl, Fintype.sum_subsingleton _ zero, zero, Digit.toNat] at hd
apply congr
. simp [hdl, mk']
intro i hi₁ hi₂
replace hi₁ : i = 0 := by omega
simp [hi₁, mk', Digit.mk, hd]
have : d.toNat ≥ 10 := calc
_ ≥ (d.head:ℕ) * 10^(d.digits.length-1) := by
set a : Fin d.digits.length := ⟨ d.digits.length - 1, by omega ⟩
convert Finset.single_le_sum _ (Finset.mem_univ a)
. simp [a, head, List.head_eq_getElem]
. infer_instance
intros; positivity
_ ≥ 1 * 10^(2-1) := by
gcongr
. have := d.head_ne_zero'; omega
norm_num
_ = 10 := by norm_num
linarith
have := (m+1).mod_add_div 10
set s := (m+1)/10
set r := (m+1) % 10
have hr : r < 10 := by grind
specialize hind s _ _ <;> try linarith
choose b hb huniq using hind; simp at huniq
apply ExistsUnique.intro (b.append (.mk hr))
. simp [←this, hb]
intro a ha
obtain hal | hal := lt_or_ge a.digits.length 2
. replace hal : a.digits.length = 1 := by linarith [a.length_pos]
have _subsing : Subsingleton (Fin a.digits.length) := by simp [Fin.subsingleton_iff_le_one, hal]
let zero : Fin a.digits.length := ⟨ 0, by linarith ⟩
simp [toNat, hal, Fintype.sum_subsingleton _ zero, zero, Digit.toNat] at ha
observe : a.digits[0].val < 10
linarith
obtain ⟨ b', b'₀, rfl ⟩ := eq_append hal
simp [←this] at ha
observe : (b'₀:ℕ) < 10
replace : (s:ℤ) = (b':ℕ) := by omega
have hb'₀r: (b'₀:ℕ) = (r:ℤ) := by omega
simp at *
rw [←b'₀.mk_eq_iff hr] at hb'₀r
rw [huniq b' this.symm, hb'₀r]
@[simp]
theorem PosintDecimal.coe_inj (p q:PosintDecimal) : (p:ℕ) = (q:ℕ) ↔ p = q := by
constructor <;> intro h
. exact (exists_unique _ q.pos).unique h rfl
rw [h]
inductive IntDecimal where
| zero : IntDecimal
| pos : PosintDecimal → IntDecimal
| neg : PosintDecimal → IntDecimal
def IntDecimal.toInt : IntDecimal → Int
| zero => 0
| pos p => p.toNat
| neg p => -p.toNat
instance IntDecimal.instCoeInt : Coe IntDecimal Int where
coe := toInt
example : (IntDecimal.neg (PosintDecimal.mk' 3 [1, 4] (by decide)):ℤ) = -314 := by decide
theorem IntDecimal.Int_bij : Function.Bijective IntDecimal.toInt := by
constructor
. intro p q hpq
cases p with
| zero => cases q with
| zero => rfl
| pos q => simp [toInt] at hpq; linarith [q.pos]
| neg q => simp [toInt] at hpq; linarith [q.pos]
| pos p => cases q with
| zero => simp [toInt] at hpq; linarith [p.pos]
| pos q => simpa [toInt] using hpq
| neg q => simp [toInt] at hpq; linarith [q.pos]
| neg p => cases q with
| zero => simp [toInt] at hpq; linarith [p.pos]
| pos q => simp [toInt] at hpq; linarith [q.pos]
| neg q => simpa [toInt] using hpq
intro n
obtain h | rfl | h := lt_trichotomy n 0
. generalize e: -n = m
lift m to Nat using (by omega)
choose p hp _ using PosintDecimal.exists_unique _ (show 0 < m by omega)
use neg p
simp [toInt, hp, ←e]
. use zero; simp [toInt]
lift n to Nat using (by omega); simp at h
choose p hp _ using PosintDecimal.exists_unique _ h
use pos p
simp [toInt, hp]
abbrev PosintDecimal.digit (p:PosintDecimal) (i:ℕ) : Digit :=
if h: i < p.digits.length then p.digits[p.digits.length - i - 1] else 0
abbrev PosintDecimal.carry (p q:PosintDecimal) : ℕ → ℕ := Nat.rec 0 (fun i ε ↦ if ((p.digit i:ℕ) + (q.digit i:ℕ) + ε) < 10 then 0 else 1)
theorem PosintDecimal.carry_zero (p q:PosintDecimal) : p.carry q 0 = 0 := by convert Nat.rec_zero _ _
theorem PosintDecimal.carry_succ (p q:PosintDecimal) (i:ℕ) : p.carry q (i+1) = if ((p.digit i:ℕ) + (q.digit i:ℕ) + p.carry q i < 10) then 0 else 1 :=
Nat.rec_add_one 0 (fun i ε ↦ if ((p.digit i:ℕ) + (q.digit i:ℕ) + ε) < 10 then 0 else 1) i
abbrev PosintDecimal.sum_digit (p q:PosintDecimal) (i:ℕ) : ℕ :=
if (p.digit i + q.digit i + (p.carry q) i < 10) then
p.digit i + q.digit i + (p.carry q) i
else
p.digit i + q.digit i + (p.carry q) i - 10
/-- Exercise B.1.1 -/
theorem PosintDecimal.sum_digit_lt (p q:PosintDecimal) (i:ℕ) :
p.sum_digit q i < 10 := by sorry
/-- Define this number such that it satisfies the two following theorems. -/
def PosintDecimal.sum_digit_top (p q:PosintDecimal) : ℕ := by sorry
theorem PosintDecimal.leading_nonzero (p q:PosintDecimal) :
p.sum_digit q (p.sum_digit_top q) ≠ 0 := sorry
theorem PosintDecimal.out_of_range_eq_zero (p q:PosintDecimal) :
∀ i > ↑(p.sum_digit_top q), p.sum_digit q i = 0 := sorry
def PosintDecimal.longAddition (p q : PosintDecimal) : PosintDecimal where
digits := sorry
nonempty := sorry
nonzero := sorry
theorem PosintDecimal.sum_eq (p q:PosintDecimal) (i:ℕ) :
(((p.longAddition q).digit i):ℕ) = p.sum_digit q i ∧ (p.longAddition q:ℕ) = p + q := by sorry