forked from djvelleman/HTPIwL
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathChap7.qmd
2309 lines (1952 loc) · 93.6 KB
/
Chap7.qmd
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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Number Theory
## 7.1. Greatest Common Divisors
The proofs in this chapter and the next are significantly longer than those in previous chapters. As a result, we will skip some details in the text, leaving proofs of a number of theorems as exercises for you. The most interesting of these exercises are included in the exercise lists at the ends of the sections; for the rest, you can compare your solutions to proofs that can be found in the Lean package that accompanies this book. Also, we will occasionally use theorems that we have not used before without explanation. If necessary, you can use `#check` to look up what they say.
Section 7.1 of *HTPI* introduces the Euclidean algorithm for computing the greatest common divisor (gcd) of two positive integers $a$ and $b$. The motivation for the algorithm is the fact that if $r$ is the remainder when $a$ is divided by $b$, then any natural number that divides both $a$ and $b$ also divides $r$, and any natural number that divides both $b$ and $r$ also divides $a$.
Let's prove these statements in Lean. Recall that in Lean, the remainder when `a` is divided by `b` is called `a` mod `b`, and it is denoted `a % b`. We'll prove the first statement, and leave the second as an exercise for you. It will be convenient for our work with greatest common divisors in Lean to let `a` and `b` be natural numbers rather than positive integers (thus allowing either of them to be zero).
```lean
theorem dvd_mod_of_dvd_a_b {a b d : Nat}
(h1 : d ∣ a) (h2 : d ∣ b) : d ∣ (a % b) := by
set q : Nat := a / b
have h3 : b * q + a % b = a := Nat.div_add_mod a b
obtain (j : Nat) (h4 : a = d * j) from h1
obtain (k : Nat) (h5 : b = d * k) from h2
define --Goal : ∃ (c : Nat), a % b = d * c
apply Exists.intro (j - k * q)
show a % b = d * (j - k * q) from
calc a % b
_ = b * q + a % b - b * q := (Nat.add_sub_cancel_left _ _).symm
_ = a - b * q := by rw [h3]
_ = d * j - d * (k * q) := by rw [h4, h5, mul_assoc]
_ = d * (j - k * q) := (Nat.mul_sub_left_distrib _ _ _).symm
done
theorem dvd_a_of_dvd_b_mod {a b d : Nat}
(h1 : d ∣ b) (h2 : d ∣ (a % b)) : d ∣ a := sorry
```
These theorems tell us that the gcd of `a` and `b` is the same as the gcd of `b` and `a % b`, which suggests that the following recursive definition should compute the gcd of `a` and `b`:
```lean
def **gcd:: (a b : Nat) : Nat :=
match b with
| 0 => a
| n + 1 => gcd (n + 1) (a % (n + 1))
```
Unfortunately, Lean puts a red squiggle under `gcd`, and it displays in the Infoview a long error message that begins `fail to show termination`. What is Lean complaining about?
The problem is that recursive definitions are dangerous. To understand the danger, consider the following recursive definition:
```lean
def loop (n : Nat) : Nat := loop (n + 1)
```
Suppose we try to use this definition to compute `loop 3`. The definition would lead us to perform the following calculation:
::: {.ind}
```
loop 3 = loop 4 = loop 5 = loop 6 = ...
```
:::
Clearly this calculation will go on forever and will never produce an answer. So the definition of `loop` does not actually succeed in defining a function from `Nat` to `Nat`.
Lean insists that recursive definitions must avoid such nonterminating calculations. Why did it accept all of our previous recursive definitions? The reason is that in each case, the definition of the value of the function at a natural number `n` referred only to values of the function at numbers smaller than `n`. Since a decreasing list of natural numbers cannot go on forever, such definitions lead to calculations that are guaranteed to terminate.
What about our recursive definition of `gcd a b`? This function has two arguments, `a` and `b`, and when `b = n + 1`, the definition asks us to compute `gcd (n + 1) (a % (n + 1))`. The first argument here could actually be larger than the first argument in the value we are trying to compute, `gcd a b`. But the second argument will always be smaller, and that will suffice to guarantee that the calculation terminates. We can tell Lean to focus on the second argument `b` by adding a `termination_by` clause to the end of our recursive definition:
```lean
def gcd (a b : Nat) : Nat :=
match b with
| 0 => a
| n + 1 => **gcd (n + 1) (a % (n + 1))::
termination_by b
```
Unfortunately, Lean still isn't satisfied, but the error message this time is more helpful. The message says that Lean failed to prove termination, and at the end of the message it says that the goal it failed to prove is `a % (n + 1) < n + 1`, which is precisely what is needed to show that the second argument of `gcd (n + 1) (a % (n + 1))` is smaller than the second argument of `gcd a b` when `b = n + 1`. We'll need to provide a proof of this goal to convince Lean to accept our recursive definition. Fortunately, it's not hard to prove:
```lean
lemma mod_succ_lt (a n : Nat) : a % (n + 1) < n + 1 := by
have h : n + 1 > 0 := Nat.succ_pos n
show a % (n + 1) < n + 1 from Nat.mod_lt a h
done
```
Lean's error message suggests several ways to fix the problem with our recursive definition. We'll use the first suggestion: ``Use `have`-expressions to prove the remaining goals``. Here, finally, is the definition of `gcd` that Lean is willing to accept. (You can ignore the initial line `@[semireducible]`. For technical reasons that we won't go into, this line is needed to make this complicated recursive definition work in proofs the same way that our previous, simpler recursive definitions did.)
```lean
@[semireducible]
def gcd (a b : Nat) : Nat :=
match b with
| 0 => a
| n + 1 =>
have : a % (n + 1) < n + 1 := mod_succ_lt a n
gcd (n + 1) (a % (n + 1))
termination_by b
```
Notice that in the `have` expression, we have not bothered to specify an identifier for the assertion being proven, since we never need to refer to it. Let's try out our `gcd` function:
```lean
++#eval:: gcd 672 161 --Answer: 7. Note 672 = 7 * 96 and 161 = 7 * 23.
```
To establish the main properties of `gcd a b` we'll need several lemmas. We prove some of them and leave others as exercises.
```lean
lemma gcd_base (a : Nat) : gcd a 0 = a := by rfl
lemma gcd_nonzero (a : Nat) {b : Nat} (h : b ≠ 0) :
gcd a b = gcd b (a % b) := by
obtain (n : Nat) (h2 : b = n + 1) from exists_eq_add_one_of_ne_zero h
rewrite [h2] --Goal : gcd a (n + 1) = gcd (n + 1) (a % (n + 1))
rfl
done
lemma mod_nonzero_lt (a : Nat) {b : Nat} (h : b ≠ 0) : a % b < b := sorry
lemma dvd_self (n : Nat) : n ∣ n := sorry
```
One of the most important properties of `gcd a b` is that it divides both `a` and `b`. We prove it by strong induction on `b`.
```lean
theorem gcd_dvd : ∀ (b a : Nat), (gcd a b) ∣ a ∧ (gcd a b) ∣ b := by
by_strong_induc
fix b : Nat
assume ih : ∀ b_1 < b, ∀ (a : Nat), (gcd a b_1) ∣ a ∧ (gcd a b_1) ∣ b_1
fix a : Nat
by_cases h1 : b = 0
· -- Case 1. h1 : b = 0
rewrite [h1, gcd_base] --Goal: a ∣ a ∧ a ∣ 0
apply And.intro (dvd_self a)
define
apply Exists.intro 0
rfl
done
· -- Case 2. h1 : b ≠ 0
rewrite [gcd_nonzero a h1]
--Goal : gcd b (a % b) ∣ a ∧ gcd b (a % b) ∣ b
have h2 : a % b < b := mod_nonzero_lt a h1
have h3 : (gcd b (a % b)) ∣ b ∧ (gcd b (a % b)) ∣ (a % b) :=
ih (a % b) h2 b
apply And.intro _ h3.left
show (gcd b (a % b)) ∣ a from dvd_a_of_dvd_b_mod h3.left h3.right
done
done
```
You may wonder why we didn't start the proof like this:
```lean
theorem gcd_dvd : ∀ (a b : Nat), (gcd a b) ∣ a ∧ (gcd a b) ∣ b := by
fix a : Nat
by_strong_induc
fix b : Nat
assume ih : ∀ b_1 < b, (gcd a b_1) ∣ a ∧ (gcd a b_1) ∣ b_1
```
In fact, this approach wouldn't have worked. It is an interesting exercise to try to complete this version of the proof and see why it fails.
Another interesting question is why we asserted both `(gcd a b) ∣ a` and `(gcd a b) ∣ b` in the same theorem. Wouldn't it have been easier to give separate proofs of the statements `∀ (b a : Nat), (gcd a b) ∣ a` and `∀ (b a : Nat), (gcd a b) ∣ b`? Again, you might find it enlightening to see why that wouldn't have worked. However, now that we have proven both divisibility statements, we can state them as separate theorems:
```lean
theorem gcd_dvd_left (a b : Nat) : (gcd a b) ∣ a := (gcd_dvd b a).left
theorem gcd_dvd_right (a b : Nat) : (gcd a b) ∣ b := (gcd_dvd b a).right
```
Next we turn to Theorem 7.1.4 in *HTPI*, which says that there are integers $s$ and $t$ such that $\text{gcd}(a, b) = s a + t b$. (We say that $\text{gcd}(a, b)$ can be written as a *linear combination* of $a$ and $b$.) In *HTPI*, this is proven by using an extended version of the Euclidean algorithm to compute the coefficients $s$ and $t$. Here we will use a different recursive procedure to compute $s$ and $t$. If $b = 0$, then $\text{gcd}(a, b) = a = 1 \cdot a + 0 \cdot b$, so we can use the values $s = 1$ and $t = 0$. Otherwise, let $q$ and $r$ be the quotient and remainder when $a$ is divided by $b$. Then $a = bq + r$ and $\text{gcd}(a, b) = \text{gcd}(b, r)$. Now suppose that we have already computed integers $s'$ and $t'$ such that
$$
\text{gcd}(b, r) = s' b + t' r.
$$
Then
\begin{align*}
\text{gcd}(a, b) &= \text{gcd}(b, r) = s' b + t' r\\
&= s' b + t' (a - bq) = t' a + (s' - t'q)b.
\end{align*}
Thus, to write $\text{gcd}(a, b) = s a + t b$ we can use the values
\begin{equation}\tag{$*$}
s = t', \qquad t = s' - t'q.
\end{equation}
We will use these equations as the basis for recursive definitions of Lean functions `gcd_c1` and `gcd_c2` such that the required coefficients can be obtained from the formulas `s = gcd_c1 a b` and `t = gcd_c2 a b`. Notice that `s` and `t` could be negative, so they must have type `Int`, not `Nat`. As a result, in definitions and theorems involving `gcd_c1` and `gcd_c2` we will sometimes have to deal with coercion of natural numbers to integers.
The functions `gcd_c1` and `gcd_c2` will be *mutually recursive*; in other words, each will be defined not only in terms of itself but also in terms of the other. Fortunately, Lean allows for such mutual recursion. Here are the definitions we will use.
```lean
mutual
@[semireducible]
def gcd_c1 (a b : Nat) : Int :=
match b with
| 0 => 1
| n + 1 =>
have : a % (n + 1) < n + 1 := mod_succ_lt a n
gcd_c2 (n + 1) (a % (n + 1))
--Corresponds to s = t'
termination_by b
@[semireducible]
def gcd_c2 (a b : Nat) : Int :=
match b with
| 0 => 0
| n + 1 =>
have : a % (n + 1) < n + 1 := mod_succ_lt a n
gcd_c1 (n + 1) (a % (n + 1)) -
(gcd_c2 (n + 1) (a % (n + 1))) * ↑(a / (n + 1))
--Corresponds to t = s' - t'q
termination_by b
end
```
Notice that in the definition of `gcd_c2`, the quotient `a / (n + 1)` is computed using natural-number division, but it is then coerced to be an integer so that it can be multiplied by the integer `gcd_c2 (n + 1) (a % (n + 1))`.
Our main theorem about these functions is that they give the coefficients needed to write `gcd a b` as a linear combination of `a` and `b`. As usual, stating a few lemmas first helps with the proof. We leave the proofs of two of them as exercises for you (hint: imitate the proof of `gcd_nonzero` above).
```lean
lemma gcd_c1_base (a : Nat) : gcd_c1 a 0 = 1 := by rfl
lemma gcd_c1_nonzero (a : Nat) {b : Nat} (h : b ≠ 0) :
gcd_c1 a b = gcd_c2 b (a % b) := sorry
lemma gcd_c2_base (a : Nat) : gcd_c2 a 0 = 0 := by rfl
lemma gcd_c2_nonzero (a : Nat) {b : Nat} (h : b ≠ 0) :
gcd_c2 a b = gcd_c1 b (a % b) - (gcd_c2 b (a % b)) * ↑(a / b) := sorry
```
With that preparation, we are ready to prove that `gcd_c1 a b` and `gcd_c2 a b` give coefficients for expressing `gcd a b` as a linear combination of `a` and `b`. Of course, the theorem is proven by strong induction. For clarity, we'll write the coercions explicitly in this proof. We'll make a few comments after the proof that may help you follow the details.
```lean
theorem gcd_lin_comb : ∀ (b a : Nat),
(gcd_c1 a b) * ↑a + (gcd_c2 a b) * ↑b = ↑(gcd a b) := by
by_strong_induc
fix b : Nat
assume ih : ∀ b_1 < b, ∀ (a : Nat),
(gcd_c1 a b_1) * ↑a + (gcd_c2 a b_1) * ↑b_1 = ↑(gcd a b_1)
fix a : Nat
by_cases h1 : b = 0
· -- Case 1. h1 : b = 0
rewrite [h1, gcd_c1_base, gcd_c2_base, gcd_base]
--Goal : 1 * ↑a + 0 * ↑0 = ↑a
ring
done
· -- Case 2. h1 : b ≠ 0
rewrite [gcd_c1_nonzero a h1, gcd_c2_nonzero a h1, gcd_nonzero a h1]
--Goal : gcd_c2 b (a % b) * ↑a +
-- (gcd_c1 b (a % b) - gcd_c2 b (a % b) * ↑(a / b)) * ↑b =
-- ↑(gcd b (a % b))
set r : Nat := a % b
set q : Nat := a / b
set s : Int := gcd_c1 b r
set t : Int := gcd_c2 b r
--Goal : t * ↑a + (s - t * ↑q) * ↑b = ↑(gcd b r)
have h2 : r < b := mod_nonzero_lt a h1
have h3 : s * ↑b + t * ↑r = ↑(gcd b r) := ih r h2 b
have h4 : b * q + r = a := Nat.div_add_mod a b
rewrite [←h3, ←h4]
rewrite [Nat.cast_add, Nat.cast_mul]
--Goal : t * (↑b * ↑q + ↑r) + (s - t * ↑q) * ↑b = s * ↑b + t * ↑r
ring
done
done
```
In case 2, we have introduced the variables `r`, `q`, `s`, and `t` to simplify the notation. Notice that the `set` tactic automatically plugs in this notation in the goal. After the step `rewrite [←h3, ←h4]`, the goal contains the expression `↑(b * q + r)`. You can use the `#check` command to see why `Nat.cast_add` and `Nat.cast_mul` convert this expression to first `↑(b * q) + ↑r` and then `↑b * ↑q + ↑r`. Without those steps, the `ring` tactic would not have been able to complete the proof.
We can try out the functions `gcd_c1` and `gcd_c2` as follows:
```lean
++#eval:: gcd_c1 672 161 --Answer: 6
++#eval:: gcd_c2 672 161 --Answer: -25
--Note 6 * 672 - 25 * 161 = 4032 - 4025 = 7 = gcd 672 161
```
Finally, we turn to Theorem 7.1.6 in *HTPI*, which expresses one of the senses in which `gcd a b` is the *greatest* common divisor of `a` and `b`. Our proof follows the strategy of the proof in *HTPI*, with one additional step: we begin by using the theorem `Int.natCast_dvd_natCast` to change the goal from `d ∣ gcd a b` to `↑d ∣ ↑(gcd a b)` (where the coercions are from `Nat` to `Int`), so that the rest of the proof can work with integer algebra rather than natural-number algebra.
```lean
theorem Theorem_7_1_6 {d a b : Nat} (h1 : d ∣ a) (h2 : d ∣ b) :
d ∣ gcd a b := by
rewrite [←Int.natCast_dvd_natCast] --Goal : ↑d ∣ ↑(gcd a b)
set s : Int := gcd_c1 a b
set t : Int := gcd_c2 a b
have h3 : s * ↑a + t * ↑b = ↑(gcd a b) := gcd_lin_comb b a
rewrite [←h3] --Goal : ↑d ∣ s * ↑a + t * ↑b
obtain (j : Nat) (h4 : a = d * j) from h1
obtain (k : Nat) (h5 : b = d * k) from h2
rewrite [h4, h5, Nat.cast_mul, Nat.cast_mul]
--Goal : ↑d ∣ s * (↑d * ↑j) + t * (↑d * ↑k)
define
apply Exists.intro (s * ↑j + t * ↑k)
ring
done
```
We will ask you in the exercises to prove that, among the common divisors of `a` and `b`, `gcd a b` is the greatest with respect to the usual ordering of the natural numbers (as long as `gcd a b ≠ 0`).
### Exercises
::: {.numex arguments="1"}
```lean
theorem dvd_a_of_dvd_b_mod {a b d : Nat}
(h1 : d ∣ b) (h2 : d ∣ (a % b)) : d ∣ a := sorry
```
:::
::: {.numex arguments="2"}
```lean
lemma gcd_comm_lt {a b : Nat} (h : a < b) : gcd a b = gcd b a := sorry
theorem gcd_comm (a b : Nat) : gcd a b = gcd b a := sorry
```
:::
::: {.numex arguments="3"}
```lean
theorem Exercise_7_1_5 (a b : Nat) (n : Int) :
(∃ (s t : Int), s * a + t * b = n) ↔ (↑(gcd a b) : Int) ∣ n := sorry
```
:::
::: {.numex arguments="4"}
```lean
theorem Exercise_7_1_6 (a b c : Nat) :
gcd a b = gcd (a + b * c) b := sorry
```
:::
::: {.numex arguments="5"}
```lean
theorem gcd_is_nonzero {a b : Nat} (h : a ≠ 0 ∨ b ≠ 0) :
gcd a b ≠ 0 := sorry
```
:::
::: {.numex arguments="6"}
```lean
theorem gcd_greatest {a b d : Nat} (h1 : gcd a b ≠ 0)
(h2 : d ∣ a) (h3 : d ∣ b) : d ≤ gcd a b := sorry
```
:::
::: {.numex arguments="7"}
```lean
lemma Lemma_7_1_10a {a b : Nat}
(n : Nat) (h : a ∣ b) : (n * a) ∣ (n * b) := sorry
lemma Lemma_7_1_10b {a b n : Nat}
(h1 : n ≠ 0) (h2 : (n * a) ∣ (n * b)) : a ∣ b := sorry
lemma Lemma_7_1_10c {a b : Nat}
(h1 : a ∣ b) (h2 : b ∣ a) : a = b := sorry
theorem Exercise_7_1_10 (a b n : Nat) :
gcd (n * a) (n * b) = n * gcd a b := sorry
```
:::
## 7.2. Prime Factorization
A natural number $n$ is said to be *prime* if it is at least 2 and it cannot be written as a product of two smaller natural numbers. Of course, we can write this definition in Lean.
```lean
def prime (n : Nat) : Prop :=
2 ≤ n ∧ ¬∃ (a b : Nat), a * b = n ∧ a < n ∧ b < n
```
The main goal of Section 7.2 of *HTPI* is to prove that every positive integer has a unique prime factorization; that is, it can be written in a unique way as the product of a nondecreasing list of prime numbers. To get started on this goal, we first prove that every number greater than or equal to 2 has a prime factor. We leave one lemma as an exercise for you (it is a natural-number version of `Theorem_3_3_7`).
```lean
def prime_factor (p n : Nat) : Prop := prime p ∧ p ∣ n
lemma dvd_trans {a b c : Nat} (h1 : a ∣ b) (h2 : b ∣ c) : a ∣ c := sorry
lemma exists_prime_factor : ∀ (n : Nat), 2 ≤ n →
∃ (p : Nat), prime_factor p n := by
by_strong_induc
fix n : Nat
assume ih : ∀ n_1 < n, 2 ≤ n_1 → ∃ (p : Nat), prime_factor p n_1
assume h1 : 2 ≤ n
by_cases h2 : prime n
· -- Case 1. h2 : prime n
apply Exists.intro n
define --Goal : prime n ∧ n ∣ n
show prime n ∧ n ∣ n from And.intro h2 (dvd_self n)
done
· -- Case 2. h2 : ¬prime n
define at h2
--h2 : ¬(2 ≤ n ∧ ¬∃ (a b : Nat), a * b = n ∧ a < n ∧ b < n)
demorgan at h2
disj_syll h2 h1
obtain (a : Nat) (h3 : ∃ (b : Nat), a * b = n ∧ a < n ∧ b < n) from h2
obtain (b : Nat) (h4 : a * b = n ∧ a < n ∧ b < n) from h3
have h5 : 2 ≤ a := by
by_contra h6
have h7 : a ≤ 1 := by linarith
have h8 : n ≤ b :=
calc n
_ = a * b := h4.left.symm
_ ≤ 1 * b := by rel [h7]
_ = b := by ring
linarith --n ≤ b contradicts b < n
done
have h6 : ∃ (p : Nat), prime_factor p a := ih a h4.right.left h5
obtain (p : Nat) (h7 : prime_factor p a) from h6
apply Exists.intro p
define --Goal : prime p ∧ p ∣ n
define at h7 --h7 : prime p ∧ p ∣ a
apply And.intro h7.left
have h8 : a ∣ n := by
apply Exists.intro b
show n = a * b from (h4.left).symm
done
show p ∣ n from dvd_trans h7.right h8
done
done
```
Of course, by the well-ordering principle, an immediate consequence of this lemma is that every number greater than or equal to 2 has a *smallest* prime factor.
```lean
lemma exists_least_prime_factor {n : Nat} (h : 2 ≤ n) :
∃ (p : Nat), prime_factor p n ∧
∀ (q : Nat), prime_factor q n → p ≤ q := by
set S : Set Nat := {p : Nat | prime_factor p n}
have h2 : ∃ (p : Nat), p ∈ S := exists_prime_factor n h
show ∃ (p : Nat), prime_factor p n ∧
∀ (q : Nat), prime_factor q n → p ≤ q from well_ord_princ S h2
done
```
To talk about prime factorizations of positive integers, we'll need to introduce a new type. If `U` is any type, then `List U` is the type of lists of objects of type `U`. Such a list is written in square brackets, with the entries separated by commas. For example, `[3, 7, 1]` has type `List Nat`. The notation `[]` denotes the empty list, and if `a` has type `U` and `l` has type `List U`, then `a :: l` denotes the list consisting of `a` followed by the entries of `l`. The empty list is sometimes called the `nil` list, and the operation of constructing a list `a :: l` from `a` and `l` is called `cons` (short for *construct*). Every list can be constructed by applying the `cons` operation repeatedly, starting with the `nil` list. For example,
::: {.ind}
```
[3, 7, 1] = 3 :: [7, 1] = 3 :: (7 :: [1]) = 3 :: (7 :: (1 :: [])).
```
:::
If `l` has type `List U` and `a` has type `U`, then `a ∈ l` means that `a` is one of the entries in the list `l`. For example, `7 ∈ [3, 7, 1]`. Lean knows several theorems about this notation:
::: {.ind}
```
@List.not_mem_nil : ∀ {α : Type u_1} (a : α),
a ∉ []
@List.mem_cons : ∀ {α : Type u_1} {a b : α} {l : List α},
a ∈ b :: l ↔ a = b ∨ a ∈ l
@List.mem_cons_self : ∀ {α : Type u_1} (a : α) (l : List α),
a ∈ a :: l
@List.mem_cons_of_mem : ∀ {α : Type u_1} (y : α) {a : α} {l : List α},
a ∈ l → a ∈ y :: l
```
:::
The first two theorems give the conditions under which something is a member of the `nil` list or a list constructed by `cons`, and the last two are easy consequences of the second.
To define prime factorizations, we must define several concepts first. Some of these concepts are most easily defined recursively.
```lean
def all_prime (l : List Nat) : Prop := ∀ p ∈ l, prime p
def nondec (l : List Nat) : Prop :=
match l with
| [] => True --Of course, True is a proposition that is always true
| n :: L => (∀ m ∈ L, n ≤ m) ∧ nondec L
def nondec_prime_list (l : List Nat) : Prop := all_prime l ∧ nondec l
def prod (l : List Nat) : Nat :=
match l with
| [] => 1
| n :: L => n * (prod L)
def prime_factorization (n : Nat) (l : List Nat) : Prop :=
nondec_prime_list l ∧ prod l = n
```
According to these definitions, `all_prime l` means that every member of the list `l` is prime, `nondec l` means that every member of `l` is less than or equal to all later members, `prod l` is the product of all members of `l`, and `prime_factorization n l` means that `l` is a nondecreasing list of prime numbers whose product is `n`. It will be convenient to spell out some consequences of these definitions in several lemmas:
```lean
lemma all_prime_nil : all_prime [] := by
define --Goal : ∀ p ∈ [], prime p
fix p : Nat
contrapos --Goal : ¬prime p → p ∉ []
assume h1 : ¬prime p
show p ∉ [] from List.not_mem_nil p
done
lemma all_prime_cons (n : Nat) (L : List Nat) :
all_prime (n :: L) ↔ prime n ∧ all_prime L := by
apply Iff.intro
· -- (→)
assume h1 : all_prime (n :: L) --Goal : prime n ∧ all_prime L
define at h1 --h1 : ∀ p ∈ n :: L, prime p
apply And.intro (h1 n (List.mem_cons_self n L))
define --Goal : ∀ p ∈ L, prime p
fix p : Nat
assume h2 : p ∈ L
show prime p from h1 p (List.mem_cons_of_mem n h2)
done
· -- (←)
assume h1 : prime n ∧ all_prime L --Goal : all_prime (n :: l)
define : all_prime L at h1
define
fix p : Nat
assume h2 : p ∈ n :: L
rewrite [List.mem_cons] at h2 --h2 : p = n ∨ p ∈ L
by_cases on h2
· -- Case 1. h2 : p = n
rewrite [h2]
show prime n from h1.left
done
· -- Case 2. h2 : p ∈ L
show prime p from h1.right p h2
done
done
done
lemma nondec_nil : nondec [] := by
define --Goal : True
trivial --trivial proves some obviously true statements, such as True
done
lemma nondec_cons (n : Nat) (L : List Nat) :
nondec (n :: L) ↔ (∀ m ∈ L, n ≤ m) ∧ nondec L := by rfl
lemma prod_nil : prod [] = 1 := by rfl
lemma prod_cons : prod (n :: L) = n * (prod L) := by rfl
```
Before we can prove the existence of prime factorizations, we will need one more fact: every member of a list of natural numbers divides the product of the list. The proof will be by induction on the length of the list, so we will need to know how to work with lengths of lists in Lean. If `l` is a list, then the length of `l` is `List.length l`, which can also be written more briefly as `l.length`. We'll need a few more theorems about lists:
::: {.ind}
```
@List.length_eq_zero : ∀ {α : Type u_1} {l : List α},
l.length = 0 ↔ l = []
@List.length_cons : ∀ {α : Type u_1} (a : α) (as : List α),
(a :: as).length = as.length + 1
@List.exists_cons_of_ne_nil : ∀ {α : Type u_1} {l : List α},
l ≠ [] → ∃ (b : α) (L : List α), l = b :: L
```
:::
And we'll need one more lemma, which follows from the three theorems above; we leave the proof as an exercise for you:
```lean
lemma exists_cons_of_length_eq_succ {A : Type}
{l : List A} {n : Nat} (h : l.length = n + 1) :
∃ (a : A) (L : List A), l = a :: L ∧ L.length = n := sorry
```
We can now prove that every member of a list of natural numbers divides the product of the list. After proving it by induction on the length of the list, we restate the lemma in a more convenient form.
```lean
lemma list_elt_dvd_prod_by_length (a : Nat) : ∀ (n : Nat),
∀ (l : List Nat), l.length = n → a ∈ l → a ∣ prod l := by
by_induc
· --Base Case
fix l : List Nat
assume h1 : l.length = 0
rewrite [List.length_eq_zero] at h1 --h1 : l = []
rewrite [h1] --Goal : a ∈ [] → a ∣ prod []
contrapos
assume h2 : ¬a ∣ prod []
show a ∉ [] from List.not_mem_nil a
done
· -- Induction Step
fix n : Nat
assume ih : ∀ (l : List Nat), l.length = n → a ∈ l → a ∣ prod l
fix l : List Nat
assume h1 : l.length = n + 1 --Goal : a ∈ l → a ∣ prod l
obtain (b : Nat) (h2 : ∃ (L : List Nat),
l = b :: L ∧ L.length = n) from exists_cons_of_length_eq_succ h1
obtain (L : List Nat) (h3 : l = b :: L ∧ L.length = n) from h2
have h4 : a ∈ L → a ∣ prod L := ih L h3.right
assume h5 : a ∈ l
rewrite [h3.left, prod_cons] --Goal : a ∣ b * prod L
rewrite [h3.left, List.mem_cons] at h5 --h5 : a = b ∨ a ∈ L
by_cases on h5
· -- Case 1. h5 : a = b
apply Exists.intro (prod L)
rewrite [h5]
rfl
done
· -- Case 2. h5 : a ∈ L
have h6 : a ∣ prod L := h4 h5
have h7 : prod L ∣ b * prod L := by
apply Exists.intro b
ring
done
show a ∣ b * prod L from dvd_trans h6 h7
done
done
done
lemma list_elt_dvd_prod {a : Nat} {l : List Nat}
(h : a ∈ l) : a ∣ prod l := by
set n : Nat := l.length
have h1 : l.length = n := by rfl
show a ∣ prod l from list_elt_dvd_prod_by_length a n l h1 h
done
```
The proof that every positive integer has a prime factorization is now long but straightforward.
```lean
lemma exists_prime_factorization : ∀ (n : Nat), n ≥ 1 →
∃ (l : List Nat), prime_factorization n l := by
by_strong_induc
fix n : Nat
assume ih : ∀ n_1 < n, n_1 ≥ 1 →
∃ (l : List Nat), prime_factorization n_1 l
assume h1 : n ≥ 1
by_cases h2 : n = 1
· -- Case 1. h2 : n = 1
apply Exists.intro []
define
apply And.intro
· -- Proof of nondec_prime_list []
define
show all_prime [] ∧ nondec [] from
And.intro all_prime_nil nondec_nil
done
· -- Proof of prod [] = n
rewrite [prod_nil, h2]
rfl
done
done
· -- Case 2. h2 : n ≠ 1
have h3 : n ≥ 2 := lt_of_le_of_ne' h1 h2
obtain (p : Nat) (h4 : prime_factor p n ∧ ∀ (q : Nat),
prime_factor q n → p ≤ q) from exists_least_prime_factor h3
have p_prime_factor : prime_factor p n := h4.left
define at p_prime_factor
have p_prime : prime p := p_prime_factor.left
have p_dvd_n : p ∣ n := p_prime_factor.right
have p_least : ∀ (q : Nat), prime_factor q n → p ≤ q := h4.right
obtain (m : Nat) (n_eq_pm : n = p * m) from p_dvd_n
have h5 : m ≠ 0 := by
contradict h1 with h6
have h7 : n = 0 :=
calc n
_ = p * m := n_eq_pm
_ = p * 0 := by rw [h6]
_ = 0 := by ring
rewrite [h7]
decide
done
have m_pos : 0 < m := Nat.pos_of_ne_zero h5
have m_lt_n : m < n := by
define at p_prime
show m < n from
calc m
_ < m + m := by linarith
_ = 2 * m := by ring
_ ≤ p * m := by rel [p_prime.left]
_ = n := n_eq_pm.symm
done
obtain (L : List Nat) (h6 : prime_factorization m L)
from ih m m_lt_n m_pos
define at h6
have ndpl_L : nondec_prime_list L := h6.left
define at ndpl_L
apply Exists.intro (p :: L)
define
apply And.intro
· -- Proof of nondec_prime_list (p :: L)
define
apply And.intro
· -- Proof of all_prime (p :: L)
rewrite [all_prime_cons]
show prime p ∧ all_prime L from And.intro p_prime ndpl_L.left
done
· -- Proof of nondec (p :: L)
rewrite [nondec_cons]
apply And.intro _ ndpl_L.right
fix q : Nat
assume q_in_L : q ∈ L
have h7 : q ∣ prod L := list_elt_dvd_prod q_in_L
rewrite [h6.right] at h7 --h7 : q ∣ m
have h8 : m ∣ n := by
apply Exists.intro p
rewrite [n_eq_pm]
ring
done
have q_dvd_n : q ∣ n := dvd_trans h7 h8
have ap_L : all_prime L := ndpl_L.left
define at ap_L
have q_prime_factor : prime_factor q n :=
And.intro (ap_L q q_in_L) q_dvd_n
show p ≤ q from p_least q q_prime_factor
done
done
· -- Proof of prod (p :: L) = n
rewrite [prod_cons, h6.right, n_eq_pm]
rfl
done
done
done
```
We now turn to the proof that the prime factorization of a positive integer is unique. In preparation for that proof, *HTPI* defines two numbers to be *relatively prime* if their greatest common divisor is 1, and then it uses that concept to prove two theorems, 7.2.2 and 7.2.3. Here are similar proofs of those theorems in Lean, with the proof of one lemma left as an exercise. In the proof of Theorem 7.2.2, we begin, as we did in the proof of Theorem 7.1.6, by converting the goal from natural numbers to integers so that we can use integer algebra.
```lean
def rel_prime (a b : Nat) : Prop := gcd a b = 1
theorem Theorem_7_2_2 {a b c : Nat}
(h1 : c ∣ a * b) (h2 : rel_prime a c) : c ∣ b := by
rewrite [←Int.natCast_dvd_natCast] --Goal : ↑c ∣ ↑b
define at h1; define at h2; define
obtain (j : Nat) (h3 : a * b = c * j) from h1
set s : Int := gcd_c1 a c
set t : Int := gcd_c2 a c
have h4 : s * ↑a + t * ↑c = ↑(gcd a c) := gcd_lin_comb c a
rewrite [h2, Nat.cast_one] at h4 --h4 : s * ↑a + t * ↑c = (1 : Int)
apply Exists.intro (s * ↑j + t * ↑b)
show ↑b = ↑c * (s * ↑j + t * ↑b) from
calc ↑b
_ = (1 : Int) * ↑b := (one_mul _).symm
_ = (s * ↑a + t * ↑c) * ↑b := by rw [h4]
_ = s * (↑a * ↑b) + t * ↑c * ↑b := by ring
_ = s * (↑c * ↑j) + t * ↑c * ↑b := by
rw [←Nat.cast_mul a b, h3, Nat.cast_mul c j]
_ = ↑c * (s * ↑j + t * ↑b) := by ring
done
lemma dvd_prime {a p : Nat}
(h1 : prime p) (h2 : a ∣ p) : a = 1 ∨ a = p := sorry
lemma rel_prime_of_prime_not_dvd {a p : Nat}
(h1 : prime p) (h2 : ¬p ∣ a) : rel_prime a p := by
have h3 : gcd a p ∣ a := gcd_dvd_left a p
have h4 : gcd a p ∣ p := gcd_dvd_right a p
have h5 : gcd a p = 1 ∨ gcd a p = p := dvd_prime h1 h4
have h6 : gcd a p ≠ p := by
contradict h2 with h6
rewrite [h6] at h3
show p ∣ a from h3
done
disj_syll h5 h6
show rel_prime a p from h5
done
theorem Theorem_7_2_3 {a b p : Nat}
(h1 : prime p) (h2 : p ∣ a * b) : p ∣ a ∨ p ∣ b := by
or_right with h3
have h4 : rel_prime a p := rel_prime_of_prime_not_dvd h1 h3
show p ∣ b from Theorem_7_2_2 h2 h4
done
```
Theorem 7.2.4 in *HTPI* extends Theorem 7.2.3 to show that if a prime number divides the product of a list of natural numbers, then it divides one of the numbers in the list. (Theorem 7.2.3 is the case of a list of length two.) The proof in *HTPI* is by induction on the length of the list, and we could use that method to prove the theorem in Lean. But look back at our proof of the lemma `list_elt_dvd_prod_by_length`, which also used induction on the length of a list. In the base case, we ended up proving that the `nil` list has the property stated in the lemma, and in the induction step we proved that if a list `L` has the property, then so does any list of the form `b :: L`. We could think of this as a kind of "induction on lists." As we observed earlier, every list can be constructed by starting with the `nil` list and applying `cons` finitely many times. It follows that if the `nil` list has some property, and applying the `cons` operation to a list with the property produces another list with the property, then all lists have the property. (In fact, a similar principle was at work in our recursive definitions of `nondec l` and `prod l`.)
Lean has a theorem called `List.rec` that can be used to justify induction on lists. This is a little more convenient than induction on the length of a list, so we'll use it to prove Theorem 7.2.4. The proof uses two lemmas, whose proofs we leave as exercises for you.
```lean
lemma eq_one_of_dvd_one {n : Nat} (h : n ∣ 1) : n = 1 := sorry
lemma prime_not_one {p : Nat} (h : prime p) : p ≠ 1 := sorry
theorem Theorem_7_2_4 {p : Nat} (h1 : prime p) :
∀ (l : List Nat), p ∣ prod l → ∃ a ∈ l, p ∣ a := by
apply List.rec
· -- Base Case. Goal : p ∣ prod [] → ∃ a ∈ [], p ∣ a
rewrite [prod_nil]
assume h2 : p ∣ 1
show ∃ a ∈ [], p ∣ a from
absurd (eq_one_of_dvd_one h2) (prime_not_one h1)
done
· -- Induction Step
fix b : Nat
fix L : List Nat
assume ih : p ∣ prod L → ∃ a ∈ L, p ∣ a
--Goal : p ∣ prod (b :: L) → ∃ a ∈ b :: L, p ∣ a
assume h2 : p ∣ prod (b :: L)
rewrite [prod_cons] at h2
have h3 : p ∣ b ∨ p ∣ prod L := Theorem_7_2_3 h1 h2
by_cases on h3
· -- Case 1. h3 : p ∣ b
apply Exists.intro b
show b ∈ b :: L ∧ p ∣ b from
And.intro (List.mem_cons_self b L) h3
done
· -- Case 2. h3 : p ∣ prod L
obtain (a : Nat) (h4 : a ∈ L ∧ p ∣ a) from ih h3
apply Exists.intro a
show a ∈ b :: L ∧ p ∣ a from
And.intro (List.mem_cons_of_mem b h4.left) h4.right
done
done
done
```
In Theorem 7.2.4, if all members of the list `l` are prime, then we can conclude not merely that `p` divides some member of `l`, but that `p` is one of the members.
```lean
lemma prime_in_list {p : Nat} {l : List Nat}
(h1 : prime p) (h2 : all_prime l) (h3 : p ∣ prod l) : p ∈ l := by
obtain (a : Nat) (h4 : a ∈ l ∧ p ∣ a) from Theorem_7_2_4 h1 l h3
define at h2
have h5 : prime a := h2 a h4.left
have h6 : p = 1 ∨ p = a := dvd_prime h5 h4.right
disj_syll h6 (prime_not_one h1)
rewrite [h6]
show a ∈ l from h4.left
done
```
The uniqueness of prime factorizations follows from Theorem 7.2.5 of *HTPI*, which says that if two nondecreasing lists of prime numbers have the same product, then the two lists must be the same. In *HTPI*, a key step in the proof of Theorem 7.2.5 is to show that if two nondecreasing lists of prime numbers have the same product, then the last entry of one list is less than or equal to the last entry of the other. In Lean, because of the way the `cons` operation works, it is easier to work with the first entries of the lists.
```lean
lemma first_le_first {p q : Nat} {l m : List Nat}
(h1 : nondec_prime_list (p :: l)) (h2 : nondec_prime_list (q :: m))
(h3 : prod (p :: l) = prod (q :: m)) : p ≤ q := by
define at h1; define at h2
have h4 : q ∣ prod (p :: l) := by
define
apply Exists.intro (prod m)
rewrite [←prod_cons]
show prod (p :: l) = prod (q :: m) from h3
done
have h5 : all_prime (q :: m) := h2.left
rewrite [all_prime_cons] at h5
have h6 : q ∈ p :: l := prime_in_list h5.left h1.left h4
have h7 : nondec (p :: l) := h1.right
rewrite [nondec_cons] at h7
rewrite [List.mem_cons] at h6
by_cases on h6
· -- Case 1. h6 : q = p
linarith
done
· -- Case 2. h6 : q ∈ l
have h8 : ∀ m ∈ l, p ≤ m := h7.left
show p ≤ q from h8 q h6
done
done
```
The proof of Theorem 7.2.5 is another proof by induction on lists. It uses a few more lemmas whose proofs we leave as exercises.
```lean
lemma nondec_prime_list_tail {p : Nat} {l : List Nat}
(h : nondec_prime_list (p :: l)) : nondec_prime_list l := sorry
lemma cons_prod_not_one {p : Nat} {l : List Nat}
(h : nondec_prime_list (p :: l)) : prod (p :: l) ≠ 1 := sorry
lemma list_nil_iff_prod_one {l : List Nat} (h : nondec_prime_list l) :
l = [] ↔ prod l = 1 := sorry
lemma prime_pos {p : Nat} (h : prime p) : p > 0 := sorry
theorem Theorem_7_2_5 : ∀ (l1 l2 : List Nat),
nondec_prime_list l1 → nondec_prime_list l2 →
prod l1 = prod l2 → l1 = l2 := by
apply List.rec
· -- Base Case. Goal : ∀ (l2 : List Nat), nondec_prime_list [] →
-- nondec_prime_list l2 → prod [] = prod l2 → [] = l2
fix l2 : List Nat
assume h1 : nondec_prime_list []
assume h2 : nondec_prime_list l2
assume h3 : prod [] = prod l2
rewrite [prod_nil, eq_comm, ←list_nil_iff_prod_one h2] at h3
show [] = l2 from h3.symm
done
· -- Induction Step
fix p : Nat
fix L1 : List Nat
assume ih : ∀ (L2 : List Nat), nondec_prime_list L1 →
nondec_prime_list L2 → prod L1 = prod L2 → L1 = L2
-- Goal : ∀ (l2 : List Nat), nondec_prime_list (p :: L1) →
-- nondec_prime_list l2 → prod (p :: L1) = prod l2 → p :: L1 = l2
fix l2 : List Nat
assume h1 : nondec_prime_list (p :: L1)
assume h2 : nondec_prime_list l2
assume h3 : prod (p :: L1) = prod l2
have h4 : ¬prod (p :: L1) = 1 := cons_prod_not_one h1
rewrite [h3, ←list_nil_iff_prod_one h2] at h4
obtain (q : Nat) (h5 : ∃ (L : List Nat), l2 = q :: L) from
List.exists_cons_of_ne_nil h4
obtain (L2 : List Nat) (h6 : l2 = q :: L2) from h5
rewrite [h6] at h2 --h2 : nondec_prime_list (q :: L2)
rewrite [h6] at h3 --h3 : prod (p :: L1) = prod (q :: L2)
have h7 : p ≤ q := first_le_first h1 h2 h3
have h8 : q ≤ p := first_le_first h2 h1 h3.symm
have h9 : p = q := by linarith
rewrite [h9, prod_cons, prod_cons] at h3
--h3 : q * prod L1 = q * prod L2
have h10 : nondec_prime_list L1 := nondec_prime_list_tail h1
have h11 : nondec_prime_list L2 := nondec_prime_list_tail h2
define at h2
have h12 : all_prime (q :: L2) := h2.left
rewrite [all_prime_cons] at h12
have h13 : q > 0 := prime_pos h12.left
have h14 : prod L1 = prod L2 := Nat.eq_of_mul_eq_mul_left h13 h3
have h15 : L1 = L2 := ih L2 h10 h11 h14
rewrite [h6, h9, h15]
rfl
done
done
```
Putting it all together, we can finally prove the fundamental theorem of arithmetic, which is stated as Theorem 7.2.6 in *HTPI*:
```lean
theorem fund_thm_arith (n : Nat) (h : n ≥ 1) :
∃! (l : List Nat), prime_factorization n l := by
exists_unique
· -- Existence
show ∃ (l : List Nat), prime_factorization n l from
exists_prime_factorization n h
done
· -- Uniqueness
fix l1 : List Nat; fix l2 : List Nat
assume h1 : prime_factorization n l1
assume h2 : prime_factorization n l2
define at h1; define at h2
have h3 : prod l1 = n := h1.right
rewrite [←h2.right] at h3
show l1 = l2 from Theorem_7_2_5 l1 l2 h1.left h2.left h3
done
done
```
### Exercises
::: {.numex arguments="1"}
```lean
lemma dvd_prime {a p : Nat}
(h1 : prime p) (h2 : a ∣ p) : a = 1 ∨ a = p := sorry
```
:::
::: {.numex arguments="2"}
```lean
--Hints: Start with apply List.rec.
--You may find the theorem mul_ne_zero useful.
theorem prod_nonzero_nonzero : ∀ (l : List Nat),
(∀ a ∈ l, a ≠ 0) → prod l ≠ 0 := sorry
```
:::
::: {.numex arguments="3"}
```lean
theorem rel_prime_iff_no_common_factor (a b : Nat) :
rel_prime a b ↔ ¬∃ (p : Nat), prime p ∧ p ∣ a ∧ p ∣ b := sorry
```
:::
::: {.numex arguments="4"}
```lean
theorem rel_prime_symm {a b : Nat} (h : rel_prime a b) :
rel_prime b a := sorry
```
:::
::: {.numex arguments="5"}
```lean
lemma in_prime_factorization_iff_prime_factor {a : Nat} {l : List Nat}
(h1 : prime_factorization a l) (p : Nat) :
p ∈ l ↔ prime_factor p a := sorry
```
:::
::: {.numex arguments="6"}
```lean
theorem Exercise_7_2_5 {a b : Nat} {l m : List Nat}
(h1 : prime_factorization a l) (h2 : prime_factorization b m) :
rel_prime a b ↔ (¬∃ (p : Nat), p ∈ l ∧ p ∈ m) := sorry
```
:::
::: {.numex arguments="7"}
```lean
theorem Exercise_7_2_6 (a b : Nat) :
rel_prime a b ↔ ∃ (s t : Int), s * a + t * b = 1 := sorry
```
:::
::: {.numex arguments="8"}
```lean
theorem Exercise_7_2_7 {a b a' b' : Nat}
(h1 : rel_prime a b) (h2 : a' ∣ a) (h3 : b' ∣ b) :
rel_prime a' b' := sorry
```
:::
::: {.numex arguments="9"}
```lean
theorem Exercise_7_2_9 {a b j k : Nat}
(h1 : gcd a b ≠ 0) (h2 : a = j * gcd a b) (h3 : b = k * gcd a b) :
rel_prime j k := sorry
```
:::
::: {.numex arguments="10"}
```lean
theorem Exercise_7_2_17a (a b c : Nat) :
gcd a (b * c) ∣ gcd a b * gcd a c := sorry
```