@@ -26,6 +26,8 @@ decreasing_by
2626
2727/--
2828Returns the largest prime factor of a given number by iteratively dividing away the smallest factor.
29+
30+ Other than the Python reference implementation, this one runs in `O(sqrt(n) + log(n))`.
2931-/
3032def largestPrimeFactor (n : Nat) : Nat := Id.run do
3133 if hn : 0 < n then
@@ -93,6 +95,21 @@ theorem largestPrimeFactor_dvd :
9395 · .withEarlyReturn (fun cur ⟨m, factor⟩ => ⌜factor ∣ n ∧ m.val ∣ n⌝) (fun ret ⟨m, factor⟩ => ⌜ret ∣ n⌝)
9496 with grind [dividePower_dvd, Nat.dvd_trans, Nat.dvd_refl]
9597
98+ theorem Nat.div_div_eq_div_mul' {a b c : Nat} (h : b ∣ a) (h' : c ∣ b) : a / (b / c) = a / b * c := by
99+ by_cases a = 0
100+ · simp [*]
101+ · have : 0 < b := Nat.pos_of_dvd_of_pos h (by grind)
102+ have : 0 < c := Nat.pos_of_dvd_of_pos h' (by grind)
103+ rw [Nat.div_eq_iff_eq_mul_left]
104+ · rw [Nat.mul_assoc, ← Nat.mul_div_assoc, Nat.mul_div_cancel_left, Nat.div_mul_cancel]
105+ all_goals assumption
106+ · have : c ≤ b := Nat.le_of_dvd ‹_› ‹_›
107+ simp only [Nat.div_pos_iff]
108+ grind
109+ · refine Nat.dvd_trans ?_ h
110+ apply Nat.div_dvd_of_dvd
111+ assumption
112+
96113theorem dvd_or_dvd_of_dvd_self_div_dividePower {e : Nat} (h : m.val ∣ n)
97114 (h : e ∣ n / (dividePower m d hd)) (hp : IsPrime e) :
98115 e ∣ n / m ∨ e ∣ d := by
@@ -102,37 +119,38 @@ theorem dvd_or_dvd_of_dvd_self_div_dividePower {e : Nat} (h : m.val ∣ n)
102119 specialize ih (by grind [Nat.dvd_trans, Nat.div_dvd_of_dvd]) h
103120 cases ih
104121 · rename_i h'
105- have (a b c : Nat) (h : b ∣ a) (h' : c ∣ b) : a / (b / c) = a / b * c := by
106- by_cases a = 0
107- · simp [*]
108- · have : 0 < b := Nat.pos_of_dvd_of_pos h (by grind)
109- have : 0 < c := Nat.pos_of_dvd_of_pos h' (by grind)
110- rw [Nat.div_eq_iff_eq_mul_left]
111- · rw [Nat.mul_assoc, ← Nat.mul_div_assoc, Nat.mul_div_cancel_left, Nat.div_mul_cancel]
112- all_goals assumption
113- · have : c ≤ b := Nat.le_of_dvd ‹_› ‹_›
114- simp only [Nat.div_pos_iff]
115- grind
116- · refine Nat.dvd_trans ?_ h
117- apply Nat.div_dvd_of_dvd
118- assumption
119- grind [hp.dvd_mul_iff]
122+ grind [hp.dvd_mul_iff, Nat.div_div_eq_div_mul']
120123 · grind
121124 · grind
122125
126+ /--
127+ Main theorem: `largestPrimeFactor n` is prime and maximal.
128+ Even though the problem description does not require it, we also prove this statement for
129+ `n` prime.
130+
131+ The loop invariant tracks that `factor` divides `n`, all prime divisors of `m` are ≥ the current
132+ trial divisor, and `factor` is the largest prime factor found so far among those already divided
133+ out. -/
123134theorem isPrime_largestPrimeFactor (h : 1 < n) :
124135 IsPrime (largestPrimeFactor n) ∧ ∀ d : Nat, d ∣ n → IsPrime d → d ≤ (largestPrimeFactor n) := by
125136 generalize hwp : largestPrimeFactor n = wp
126137 apply Id.of_wp_run_eq hwp
127138 mvcgen
128139 invariants
129140 · .withEarlyReturn
130- (fun cur ⟨m, factor⟩ =>
131- ⌜let i := cur.pos + 2 ;
132- factor < i ∧
133- m.val ∣ n ∧
134- (if m.val = n then factor = 1 else IsPrime factor ∧ ∀ e : Nat, e ∣ n / m → IsPrime e → e ≤ factor) ∧
135- ∀ e : Nat, e ∣ m → e = 1 ∨ i ≤ e⌝)
141+ (fun cur ⟨m, mostRecentFactor⟩ =>
142+ ⌜let i := cur.pos + 2 ; -- loop variable
143+ -- At the beginning of the loop, the `mostRecentFactor` variable is less than the loop variable.
144+ mostRecentFactor < i ∧
145+ -- The `m` variable is a divisor of `n`.
146+ m.val ∣ n ∧
147+ -- Except if `m = n`, `mostRecentFactor` is the largest prime factor of `n / m`.
148+ (if m.val = n then
149+ mostRecentFactor = 1
150+ else
151+ IsPrime mostRecentFactor ∧ ∀ e : Nat, e ∣ n / m → IsPrime e → e ≤ mostRecentFactor) ∧
152+ -- Every nontrivial divisor of `m` is at least as large as the loop variable.
153+ ∀ e : Nat, e ∣ m → e = 1 ∨ i ≤ e⌝)
136154 (fun ret ⟨m, factor⟩ => ⌜IsPrime ret ∧ ∀ d : Nat, d ∣ n → IsPrime d → d ≤ ret⌝)
137155 case vc1 pref cur suf _ _ _ m _ _ _ ih =>
138156 simp only [reduceCtorEq, false_and, exists_false, or_false]
@@ -166,7 +184,7 @@ theorem isPrime_largestPrimeFactor (h : 1 < n) :
166184 cases he
167185 · grind
168186 · exact Nat.le_of_dvd (by grind) ‹_›
169- · simp at *
187+ · simp only [List.Cursor.pos_mk, reduceCtorEq, false_and, and_false, exists_const, or_false] at *
170188 intro e he
171189 have : e ≠ cur := by grind [not_dvd_dividePower]
172190 replace ih := ih.2 .2 .2 .2 e
@@ -193,8 +211,9 @@ theorem isPrime_largestPrimeFactor (h : 1 < n) :
193211 have : 0 < e := Nat.pos_of_dvd_of_pos he ‹0 < n›
194212 grind
195213 case vc5 r _ ih =>
196- simp at *
197- simp_all
214+ simp only [List.Cursor.pos_mk, Rcc.length_toList, Nat.size_rcc, Nat.reduceSubDiff,
215+ true_and] at *
216+ simp_all only [true_and, reduceCtorEq, false_and, exists_const, or_false]
198217 have := ih.2 .2 .2 _ (Nat.dvd_refl _)
199218 have : r.2 .1 .val ≤ n := Nat.le_of_dvd ‹0 < n› ih.2 .1
200219 have : r.2 .1 .val = 1 := by grind
@@ -204,7 +223,8 @@ theorem isPrime_largestPrimeFactor (h : 1 < n) :
204223 case vc7 => grind
205224 -- Early return verification conditions:
206225 case vc3 pref cur suff _ _ _ m _ hlt ih =>
207- simp_all
226+ simp_all only [Nat.not_le, List.Cursor.pos_mk, reduceCtorEq, false_and, and_false, exists_const,
227+ or_false, Option.some.injEq, true_and, exists_eq_left', false_or]
208228 rw [max_eq_if]
209229 split
210230 · constructor
0 commit comments