Zulip Chat Archive

Stream: maths

Topic: Proving congruences by cases


Filippo A. E. Nuccio (Dec 15 2023 at 18:04):

With @Cyril Cohen we were discussing about how nice the proof that a natural that is a square and a cube is either 00 or 11 modulo 77 can be made. He had a super-cool 3-lines proof in Coq, and challenged me on Lean. I came up with the following one, and I would be happy to see some :golf: :

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  have hb1 := (b % 7).zero_le
  have hb2 : (b % 7) < 7 := Nat.mod_lt _ (by linarith)
  have hc1 := (c % 7).zero_le
  have hc2 : (c % 7) < 7 := Nat.mod_lt _ (by linarith)
  have : (a % 7 = 1  a % 7 = 0  a % 7 = 6)  (a % 7 = 0  a % 7  = 1  a % 7  = 4  a % 7  = 2)
  · rw (config := {occs := .pos {1,2,3}}) [hab]
    rw [hac, Nat.pow_mod (b := 3), Nat.pow_mod (b := 2)]
    fconstructor
    · interval_cases (b % 7) using hb1, hb2
      all_goals aesop
    · interval_cases (c % 7) using hc1, hc2
      all_goals aesop
  aesop

Up to increasing the maximal recursion from 512 to 700 I can improve it to

set_option maxRecDepth 700

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  have hb1 := (b % 7).zero_le
  have hb2 : (b % 7) < 7 := Nat.mod_lt _ (by linarith)
  have hc1 := (c % 7).zero_le
  have hc2 : (c % 7) < 7 := Nat.mod_lt _ (by linarith)
  have : (a % 7 = 1  a % 7 = 0  a % 7 = 6)  (a % 7 = 0  a % 7  = 1  a % 7  = 4  a % 7  = 2)
  rw (config := {occs := .pos {1,2,3}}) [hab]
  rw [hac, Nat.pow_mod (b := 3), Nat.pow_mod (b := 2)]
  interval_cases (b % 7) using hb1, hb2
  all_goals interval_cases (c % 7) using hc1, hc2
  all_goals aesop

A part from the :golf: ing contest, I was unable to insert in the interval_cases the proof term directly, writing for instance interval_cases (b % 7) using (b % 7).zero_le, Nat.mod_lt _ (by linarith) (at least the first one...); this forced the first four "useless" have. Am I missing something, is it intentional?

Filippo A. E. Nuccio (Dec 15 2023 at 18:11):

I should probably add that we were aiming at a proof that does not go "modulo 77, but sticks to Nat`; most probably one can work in the finite field with 77 elements, but that was not the point of our discussion.

Eric Rodriguez (Dec 15 2023 at 18:15):

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  rw [Nat.one_mod, Nat.zero_mod, ZMod.nat_cast_eq_nat_cast_iff', ZMod.nat_cast_eq_nat_cast_iff']
  apply_fun (fun x :   (x : ZMod 7)) at hab hac
  generalize hx : (a : ZMod 7) = x
  generalize hy : (b : ZMod 7) = y
  generalize hz : (c : ZMod 7) = z
  simp [hx, hy, hz] at *
  revert x y z
  decide

I just wrote this to come back to your second message :b the simp line is ostentively a bug in generalize; it should generalise everywhere, I'd say.

Eric Rodriguez (Dec 15 2023 at 18:16):

what's the coq proof?

Filippo A. E. Nuccio (Dec 15 2023 at 18:16):

Eric Rodriguez said:

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  rw [Nat.one_mod, Nat.zero_mod, ZMod.nat_cast_eq_nat_cast_iff', ZMod.nat_cast_eq_nat_cast_iff']
  apply_fun (fun x :   (x : ZMod 7)) at hab hac
  generalize hx : (a : ZMod 7) = x
  generalize hy : (b : ZMod 7) = y
  generalize hz : (c : ZMod 7) = z
  simp [hx, hy, hz] at *
  revert x y z
  decide

I just wrote this to come back to your second message :b the simp line is ostentively a bug in generalize; it should generalise everywhere, I'd say.

Nice!

Filippo A. E. Nuccio (Dec 15 2023 at 18:16):

Eric Rodriguez said:

what's the coq proof?

I leave it to @Cyril Cohen

Cyril Cohen (Dec 15 2023 at 18:20):

From mathcomp Require Import all_ssreflect.

Lemma test (m n p : nat) : m = n ^ 2 -> m = p ^ 3 ->
  (m == 0 %[mod 7]) || (m == 1 %[mod 7]).
Proof.
move=> -> /(congr1 (modn^~ 7)); rewrite -modnXm -[in RHS]modnXm.
move: (n %% 7) (p %% 7) (@ltn_pmod n 7 isT) (@ltn_pmod p 7 isT).
by do 7?[case=> //]; do 7?[case=> //].
Qed.

Cyril Cohen (Dec 15 2023 at 18:21):

(but I'm still working on the ½ line version :wink: )

Eric Rodriguez (Dec 15 2023 at 18:28):

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  suffices :  a < 7,  b < 7,  c < 7, a = (b^3 % 7)  a=(c^2%7)  (a % 7 = 1  a % 7 = 0)
  · simpa using this (a % 7) (a.mod_lt <| by linarith) (b % 7) (b.mod_lt <| by linarith) (c % 7) (c.mod_lt <| by linarith) (by simp [hab, Nat.pow_mod]) (by simp [hac, Nat.pow_mod])
  decide

a Lean 3-liner (although you can argue one of the lines is a bit long!)

Riccardo Brasca (Dec 15 2023 at 18:36):

Does decide immediately prove the same statement modulo 7?

Riccardo Brasca (Dec 15 2023 at 18:36):

I mean, when all the variables are module 7

Riccardo Brasca (Dec 15 2023 at 18:37):

decide or fin_cases

Michael Stoll (Dec 15 2023 at 18:37):

Yes: This works (although it is not exactly the statement you want):

example :  a : ZMod 7, ( b c : ZMod 7, a = b ^ 3  a = c ^ 2)  a = 0  a = 1 := by decide

Michael Stoll (Dec 15 2023 at 18:37):

I would argue that the statement mod 7 is the good one.

Michael Stoll (Dec 15 2023 at 18:38):

It seems to be the least painful way of proving statements about conguences.

Filippo A. E. Nuccio (Dec 15 2023 at 18:39):

Sure, we agree; but we arrived at the discussion coming from a different path, so the point was "try to make it readable even before introducing (to students) the ring-hom of reduction modulo pp".

Riccardo Brasca (Dec 15 2023 at 18:39):

maybe some meta code magic could automatize that we can move to ZMod 7 even if we want a statement about Nat

Filippo A. E. Nuccio (Dec 15 2023 at 18:39):

This "meta code magic" will soon exist in Coq....

Riccardo Brasca (Dec 15 2023 at 18:40):

well, then we also want it!

Cyril Cohen (Dec 15 2023 at 18:45):

Yes, actually that's the ½ line proof I'm still working on

Eric Rodriguez (Dec 15 2023 at 18:56):

Eric Rodriguez said:

example (a b c : ) (hab : a = b^3) (hac : a=c^2) : (a % 7 = 1  a % 7 = 0) := by
  suffices :  a < 7,  b < 7,  c < 7, a = (b^3 % 7)  a=(c^2%7)  (a % 7 = 1  a % 7 = 0)
  · simpa using this (a % 7) (a.mod_lt <| by linarith) (b % 7) (b.mod_lt <| by linarith) (c % 7) (c.mod_lt <| by linarith) (by simp [hab, Nat.pow_mod]) (by simp [hac, Nat.pow_mod])
  decide

a Lean 3-liner (although you can argue one of the lines is a bit long!)

I'd argue this proof could be much nicer if we could do ?_s in the simpa invocation, as every required parameter could be solved by (probably, on tube) simp_arith [*, Nat.pow_mod]

Filippo A. E. Nuccio (Dec 15 2023 at 18:57):

Yes, good point.

Yaël Dillies (Dec 15 2023 at 18:57):

simpa not supporting placeholders is a pain point for me too.

Filippo A. E. Nuccio (Dec 15 2023 at 18:57):

And is it clear to you why we cannot use terms in the interval_cases? Apart from the fact that my proof was too complicated, isn't it also a pain?

Mario Carneiro (Dec 15 2023 at 19:00):

it should be possible to do this with mod_cases

Mario Carneiro (Dec 15 2023 at 19:00):

it was designed for exactly this kind of problem

Filippo A. E. Nuccio (Dec 15 2023 at 19:00):

The problem is that it switches from % to MOD and you have to go back-and-forth

David Renshaw (Dec 15 2023 at 20:22):

Mario Carneiro said:

it should be possible to do this with mod_cases

I put up a pull request a while ago to make mod_cases work on natural numbers:
https://github.com/leanprover-community/mathlib4/pull/3922

David Renshaw (Dec 15 2023 at 20:30):

(looking at resolving the merge conflict now...)

Filippo A. E. Nuccio (Dec 15 2023 at 22:09):

Great!


Last updated: Dec 20 2023 at 11:08 UTC