Zulip Chat Archive

Stream: new members

Topic: A simple number theory problem to practice lean basics


Ryan Smith (Jun 26 2025 at 23:01):

I am trying to show that n^7 − n is divisible by 42 for every positive integer n. Pretty easy if you know basic number theory, but it has exposed a lot of rough edges in learning how to interact with lean to do basic stuff. Here is an outline so far which has several issues:

example (n : ) (h : n > 0) : n^7 - n  0 [MOD 42] := by
  have hf' : 42 = 6 * 7 := by decide
  rw [hf']
  have h67cop : Coprime 6 7 := by decide
  -- this line fails
  apply Nat.modEq_and_modEq_iff_modEq_mul h67cop
  sorry

The version of the chinese remainder theorem I'm trying to invoke says

Nat.modEq_and_modEq_iff_modEq_mul {a b m n : } (hmn : m.Coprime n) : a  b [MOD m]  a  b [MOD n]  a  b [MOD m * n]

Since my goal is ⊢ n ^ 7 - n ≡ 0 [MOD 6 * 7] I would have thought I could apply the chinese remainder theorem successfully since I've provided a hypothesis that 6 and 7 are coprime as required. I tried a couple variations such as modEq_and_modEq_iff_modEq_mul.mpr since I really want the left part of an iff statement but I can't get the syntax correct.

The second part of my question for this basic exercise comes from figuring out how to justify my steps in a calc block:

  have h3 : n ^ 7 - n  0 [MOD 3] := by
    calc
      n ^ 7 - n  n ^ 6 * n - n [MOD 3] := by ring  -- fails
      _  (n ^ 2) ^ 3 * n - n [MOD 3] := by sorry
      _  1 ^ 3 * n - n [MOD 3] := by sorry
      _  0 [MOD 3] := by sorry
    sorry

Tactics such as ring and simp fail even though we are in a field? Is this even the right way to use a calc block?

I'm still learning lean, there's a bit of a learning curve to express basic arguments at the beginning.

Kenny Lau (Jun 26 2025 at 23:03):

Please include import Mathlib in the beginning of your code blocks so the code can just be run by themselves

Kenny Lau (Jun 26 2025 at 23:04):

(and open Nat too)

Kenny Lau (Jun 26 2025 at 23:05):

ring is for equalities, so here you can use instead ring_nf; rfl

Kenny Lau (Jun 26 2025 at 23:07):

@Ryan Smith that line fails because apply is strictly for implications, but you have an iff.

Kenny Lau (Jun 26 2025 at 23:08):

Basically, if your goal is ⊢ q and you have some_theorem_name : p → q, then apply some_theorem_name changes the goal to ⊢ p. See the following example:

def p : Prop := sorry
def q : Prop := sorry
theorem some_thm_name : p  q := sorry
theorem foo : q := by
  apply some_thm_name

Kenny Lau (Jun 26 2025 at 23:09):

move your cursor from before apply to after apply to see how the goal changes.

Kenny Lau (Jun 26 2025 at 23:09):

so, if you want to use an iff, you use rw instead

Kenny Lau (Jun 26 2025 at 23:10):

so the correct code is:

import Mathlib

open Nat

example (n : ) (h : n > 0) : n^7 - n  0 [MOD 42] := by
  have hf' : 42 = 6 * 7 := by decide
  rw [hf']
  have h67cop : Coprime 6 7 := by decide
  -- this line works now!
  rw [ Nat.modEq_and_modEq_iff_modEq_mul h67cop]
  sorry

Ryan Smith (Jun 26 2025 at 23:14):

Thanks! calc still dislikes rewriting n^7 however,

import Mathlib
open Nat

-- Show that n^7 − n is divisible by 42 for every positive integer n.
example (n : ) (h : n > 0) : n^7 - n  0 [MOD 42] := by
  have hf' : 42 = 6 * 7 := by decide
  rw [hf']
  have h67cop : Coprime 6 7 := by decide
  rw [ Nat.modEq_and_modEq_iff_modEq_mul h67cop]

  have h3 : n ^ 7 - n  0 [MOD 3] := by
    calc
      n ^ 7 - n  n ^ 6 * n - n [MOD 3] := ring_nf; rfl
      _  (n ^ 2) ^ 3 * n - n [MOD 3] := by sorry
      _  1 ^ 3 * n - n [MOD 3] := by sorry
      _  0 [MOD 3] := by sorry
    sorry
  sorry

Kenny Lau (Jun 26 2025 at 23:15):

@Ryan Smith you forgot the by that time

Ryan Smith (Jun 26 2025 at 23:17):

Thank you. So we apply results to hypotheses but rewrite our goal?

Ryan Smith (Jun 26 2025 at 23:21):

Stylistically, is the hypothesis that 42 = 6 * 7 required or could one of the tactics justified that automatically?

Kenny Lau (Jun 26 2025 at 23:21):

Ryan Smith said:

Thank you. So we apply results to hypotheses but rewrite our goal?

as i've explained, they differ by what things you can feed them with; apply is for p -> q only; and rw is for p <-> q only (and equalities too)

Kenny Lau (Jun 26 2025 at 23:22):

in informal maths you would call both "apply", but Lean is not informal maths

Kenny Lau (Jun 26 2025 at 23:22):

btw here's my "standard" proof:

import Mathlib

set_option trace.profiler true
-- Show that n^7 − n is divisible by 42 for every positive integer n.
-- 0.186 seconds
theorem Nat.pow_seven_sub_modEq_zero (n : ) : n^7 - n  0 [MOD 42] := by
  rw [ ZMod.eq_iff_modEq_nat, Nat.cast_sub (Nat.le_self_pow (by norm_num) _),
    Nat.cast_pow, Nat.cast_zero]
  generalize (n : ZMod 42) = m
  decide +revert

Kenny Lau (Jun 26 2025 at 23:23):

in words, reduce this to a problem in Z/42Z, which can then be "checked by brute force" (that's the decide)

Ryan Smith (Jun 26 2025 at 23:26):

Wow, that is quite terse. What does +revert mean?

Kenny Lau (Jun 26 2025 at 23:34):

Ryan Smith said:

Wow, that is quite terse. What does +revert mean?

when you check by brute force (i.e. decide), the statement cannot have free variables, so revert will make the variables bounded first (i.e. quantified under forall), and then decide it

Kenny Lau (Jun 26 2025 at 23:34):

you can try revert n by itself to see what it does

Ryan Smith (Jun 27 2025 at 01:47):

Updated version. Clearly we are doing some of these things the hard way but the goal is to figure out how to translate an argument into lean

import Mathlib
open Nat

-- Show that n^7 − n is divisible by 42 for every positive integer n.
example (n : ) (h : n > 0) : n^7 - n  0 [MOD 42] := by
  have hf : 42 = 6 * 7 := by decide
  rw [hf]
  have h67cop : Coprime 6 7 := by decide
  rw [ Nat.modEq_and_modEq_iff_modEq_mul h67cop]

  have hf' : 6 = 2 * 3 := by decide
  rw [hf']
  have h23cop : Coprime 2 3 := by decide
  rw [ Nat.modEq_and_modEq_iff_modEq_mul h23cop]

  have h2 : n ^ 7 - n  0 [MOD 2] := by sorry
  have h3 : n ^ 7 - n  0 [MOD 3] := by
    calc
      n ^ 7 - n  (n ^ 3) ^ 2 * n - n [MOD 3] := by ring_nf; rfl
      _  n ^ 2 * n - n [MOD 3] := sorry
      _  n ^ 3 - n [MOD 3] := by ring_nf; rfl
      _  n - n [MOD 3] := sorry
      _  0 [MOD 3] := by ring_nf; simp; rfl
  have h7 : n ^ 7 - n  0 [MOD 7] := by
    calc
      n ^ 7 - n  n - n [MOD 7] := by rw [ZMod.pow_card (n ^ 7)]
      _  0 [MOD 7] := by ring_nf; simp; rfl
  tauto

A question about the follow up. The use of rw with ZMod.pow_card fails when I try to use it to simplify n^7 mod 7 to n mod 7

ZMod.pow_card {p : } [Fact (Nat.Prime p)] (x : ZMod p) : x ^ p = x

This seems like a valid use of rw but I'm guessing lean is unhappy about n being a natural number and not an element of Z/7Z ?

For teaching purposes when p=2 is there a way I could express there are two cases to lean so either n \== 0 mod 2 or 1 mod 2 and then do each of those cases separately? Seems so easy to say in English :)

Kenny Lau (Jun 27 2025 at 01:57):

Ryan Smith said:

This seems like a valid use of rw but I'm guessing lean is unhappy about n being a natural number and not an element of Z/7Z ?

You exactly spelled out why this was not a valid use of rw. Be wary of the mathematician's trap where things that are "obviously the same" in maths are not the same in Lean. In this case in particular you exactly said that a ≡ b [MOD 7] is not the same as being equal in Z/7Z.

Ryan Smith said:

when p=2 is there a way I could express there are two cases to lean

That's exactly the line in my code that again does this transformation which is obvious to a mathematician. Refer to the first three lines in my proof provided above.


Last updated: Dec 20 2025 at 21:32 UTC