Zulip Chat Archive
Stream: new members
Topic: Provide divisors of number to prove it's prime
Bernardo Borges (Nov 27 2024 at 20:05):
The proof that a number is prime as stated in Theorem Proving in Lean 4 tells us to prove that any number that divides our prime is either 1 or p itself.
https://live.lean-lang.org/#codez=JYWwDg9gTgLgBAWQIYwBYBtgCMB0ARFJHAORRwAUpQBTfagMwGcAoZgEwbmoDdqA7OAAoBALjikYASjhjKEMDIC8cQMBEcANYzxKADRwBygEwAqdaw704bYN2AdGQq/q0S402VHlLVGl7ufK6sZs5pxgVCDUQqLaUlpyCiLKwnCABkRwAAzSgORE0WlwAIw5QoAARE5iEnrWtvZOAoBJhEJscMoCgBRETspFkqxo1NDUIHAwAO4QAPrAjOPhNFqzkXCG3lgAnsxwcADGEHyMMFAArlsw0BtcAB5IJ+K0jKsgQ4ISOABe1J7jfNTjSGxs412USK5x2ewOx1OUHO1CuNxejGOW3GiK2yO+gO+mXOwD4BwgTkAF+TqHSodSAS/JzgB6AC0oKQjCiVAA5qgYDgWWzzs1NGJAKiE5zJWmWgTgxis50ARUSdJZwDrNLrnGlU86MaBQdbMIA
How can I "iterate" or expand the divisors? Is there another way around?
Ruben Van de Velde (Nov 27 2024 at 20:17):
I approached it with a case bash:
import Mathlib.Data.Nat.Prime.Defs
def even (n : Nat) : Prop := ∃ k : Nat, n = 2*k
def divides ( d n : Nat ) : Prop := ∃ k : Nat, n = k*d
def prime (n : Nat) : Prop := (n ≠ 0) ∧ (n ≠ 1) ∧ (∀ d : Nat, divides d n → (d = n ∨ d = 1))
theorem two_is_prime : prime 2 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 1)
constructor
exact Nat.succ_succ_ne_one 0
intro d ⟨k,hk⟩
match d with
| 0 => simp at hk
| 1 => simp
| 2 => simp
| n + 3 =>
contrapose! hk with _hk
match k with
| 0 => simp
| k + 1 =>
apply ne_of_lt
rw [← one_mul 2]
apply Nat.mul_lt_mul_of_le_of_lt <;> omega
Damiano Testa (Nov 27 2024 at 20:23):
I followed a similar approach:
theorem two_is_prime : prime 2 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 1)
constructor
exact Nat.succ_succ_ne_one 0
intro d ⟨k,hk⟩
obtain _|d := d <;> obtain _|k := k <;>
simp [mul_add, add_mul] at hk; omega
Bernardo Borges (Nov 27 2024 at 23:43):
Very nice! I'm trying to understand what _|d
means tho. Is this a common pattern in case-by-case analysis? @Damiano Testa
Bernardo Borges (Nov 28 2024 at 01:05):
theorem three_is_prime : prime 3 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 2)
constructor
exact Nat.succ_succ_ne_one 1
intro d ⟨k,hk⟩
obtain _|d := d <;>
obtain _|k := k <;>
simp [mul_add,add_mul] at hk ⊢
obtain _|k := k <;>
simp [mul_add,add_mul] at hk <;>
omega
This works, and I don't know why
Kevin Buzzard (Nov 28 2024 at 06:42):
_|d
by itself doesn't mean anything, this is syntax specific to a couple of tactics like obtain
or rcases
. In a perfect world this syntax would be documented in eg the docstring of the rcases
tactic but I'm not at lean right now so can't check if it is. obtain _|d := h
means something like "h is an or
statement (or more generally something with two constructors) and we deconstruct this hypothesis (so we're moving from one goal to two); in the first branch we assume the hypothesis on the left and don't give it a name, and in the second we assume the right hypothesis and call it d"
Kevin Buzzard (Nov 28 2024 at 06:44):
I'm not at lean right now but I'm guessing that actually in this case d is a natural in which case it means "split into two cases: either that d=0 or d is of the form x+1 and call the new variable x also d"
Damiano Testa (Nov 28 2024 at 07:19):
Yes, indeed, Kevin's explanation is exactly right: d
(and k
) are both natural numbers and the syntax is used to separate the case where they are 0
or the successor of something.
Damiano Testa (Nov 28 2024 at 07:32):
Bernardo Borges said:
theorem three_is_prime : prime 3 := by constructor exact Ne.symm (Nat.zero_ne_add_one 2) constructor exact Nat.succ_succ_ne_one 1 intro d ⟨k,hk⟩ obtain _|d := d <;> obtain _|k := k <;> simp [mul_add,add_mul] at hk ⊢ obtain _|k := k <;> simp [mul_add,add_mul] at hk <;> omega
This works, and I don't know why
You can also add the further case split on k
to the previous syntax and compress this a little more:
theorem three_is_prime : prime 3 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 2)
constructor
exact Nat.succ_succ_ne_one 1
intro d ⟨k,hk⟩
obtain _|d := d <;> obtain _|_|k := k <;> -- <--- note the further cases split: "`k=0|k=1|k+2`"
simp [mul_add,add_mul] at hk ⊢ <;> omega
Johan Commelin (Nov 28 2024 at 08:14):
Note that branch in your definition of prime
is redundant. It follows from the other part.
Bernardo Borges (Nov 28 2024 at 12:05):
Very interesting:
theorem five_is_prime : prime 5 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 4)
constructor
exact Nat.succ_succ_ne_one 3
intro d ⟨k,hk⟩
obtain _|d := d <;> obtain _|_|_|k := k <;>
simp [mul_add,add_mul] at hk ⊢ <;> omega
theorem seven_is_prime : prime 7 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one 6)
constructor
exact Nat.succ_succ_ne_one 5
intro d ⟨k,hk⟩
obtain _|d := d <;> obtain _|_|_|_|k := k <;>
simp [mul_add,add_mul] at hk ⊢ <;> omega
I feel like this could be a tactic
of itself. How hard would it be to implement one?
I would just need this scaffold:
theorem p_is_prime : prime p := by
constructor
exact Ne.symm (Nat.zero_ne_add_one <p-1>)
constructor
exact Nat.succ_succ_ne_one <p-2>
intro d ⟨k,hk⟩
obtain _|d := d <;> obtain _|_|_|_|k := k <;> -- <--- as many case splits as needed
simp [mul_add,add_mul] at hk ⊢ <;> omega
Johan Commelin (Nov 28 2024 at 12:17):
Can you avoid figuring out how many case splits you need, by using repeat
?
Johan Commelin (Nov 28 2024 at 12:18):
Also... I think it would be a nice follow-up exercise to improve the running time of your tactic by showing that you only need to check divisors up to
Bernardo Borges (Nov 28 2024 at 12:24):
Yes, I think I could argue that swapping d
and k
folds the proof like we want. But then I have to argue this translation.
Also, if I use the try
combinator I will have to change from the obtain _|k
syntax, right?
Damiano Testa (Nov 28 2024 at 12:51):
Here is a hack to make this work, although it clearly has its limitations:
theorem one_hundred_and_one_is_prime : prime 101 := by
constructor
exact Ne.symm (Nat.zero_ne_add_one _)
constructor
exact Nat.succ_succ_ne_one _
intro d ⟨k,hk⟩
obtain _|d := d <;>
repeat obtain _|k := k <;> try (simp [mul_add,add_mul] at hk ⊢ <;> omega)
(I really wanted to break the 10^2 barrier. :smile: )
Last updated: May 02 2025 at 03:31 UTC