Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 51cbb83

Browse files
digama0robertylewis
andcommitted
refactor(tactic/norm_num): move norm_num extensions (#4822)
#4820 adds the long awaited ability for `norm_num` to be extended in other files. There are two norm_num extensions currently in mathlib: `norm_digits`, which was previously exposed as a standalone tactic, and `eval_prime`, which was a part of `norm_num` and so caused `tactic.norm_num` to depend on `data.nat.prime`. This PR turns both of these into norm_num extensions, and changes the dependencies so that `data.nat.prime` can import `norm_num` rather than the other way around (which required splitting `pnat` primality and gcd facts to their own file). Co-authored-by: Rob Lewis <rob.y.lewis@gmail.com>
1 parent be161d1 commit 51cbb83

File tree

12 files changed

+212
-213
lines changed

12 files changed

+212
-213
lines changed

archive/imo/imo1959_q1.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Authors: Kevin Lacker
55
-/
66

77
import tactic.ring
8+
import data.nat.prime
89

910
open nat
1011

archive/imo/imo1960_q1.lean

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ begin
8787
have := search_up_to_start,
8888
iterate 82 {
8989
replace := search_up_to_step this (by norm_num1; refl) (by norm_num1; refl)
90-
(by norm_digits; refl) dec_trivial },
90+
(by norm_num1; refl) dec_trivial },
9191
exact search_up_to_end this
9292
end
9393

@@ -100,4 +100,3 @@ by rcases spn with (rfl | rfl); norm_num [problem_predicate, sum_of_squares]
100100

101101
theorem imo1960_q1 (n : ℕ) : problem_predicate n ↔ solution_predicate n :=
102102
⟨right_direction, left_direction n⟩
103-

archive/imo/imo1969_q1.lean

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Kevin Lacker
55
-/
66

7+
import data.nat.prime
78
import tactic.linarith
89
import tactic.norm_cast
910
import tactic.ring
@@ -40,7 +41,7 @@ by exact_mod_cast lt_of_lt_of_le h le_nat_abs
4041
lemma int_not_prime (a b : ℤ) (c : ℕ) (h1 : 1 < a) (h2 : 1 < b) (h3 : a*b = ↑c) : ¬ prime c :=
4142
have h4 : (a*b).nat_abs = a.nat_abs * b.nat_abs, from nat_abs_mul a b,
4243
have h5 : a.nat_abs * b.nat_abs = c, by finish,
43-
norm_num.not_prime_helper a.nat_abs b.nat_abs c h5 (int_large a h1) (int_large b h2)
44+
tactic.norm_num.not_prime_helper a.nat_abs b.nat_abs c h5 (int_large a h1) (int_large b h2)
4445

4546
lemma polynomial_not_prime (m n : ℕ) (h1 : 1 < m) : ¬ prime (n^4 + 4*m^4) :=
4647
have h2 : 1 < of_nat m, from coe_nat_lt.mpr h1,
@@ -66,5 +67,3 @@ begin
6667
assume n,
6768
exact polynomial_not_prime (2+b) n h1
6869
end
69-
70-

archive/imo/imo1988_q6.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Johan Commelin
55
-/
66

7+
import data.nat.prime
78
import data.rat.basic
89
import order.well_founded
910
import tactic.linarith

src/data/nat/digits.lean

Lines changed: 22 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -606,51 +606,37 @@ meta def eval_aux (eb : expr) (b : ℕ) :
606606
return (ic, `(@list.cons ℕ %%er %%el),
607607
`(digits_succ %%eb %%en %%em %%er %%el %%pe %%pr %%p))
608608

609-
/-- Helper function for the `norm_digits` tactic. -/
610-
meta def eval : expr → ℕ → expr → ℕ → tactic (expr × expr)
611-
| eb b en 0 := return (`([] : list ℕ), `(nat.digits_zero %%eb))
612-
| eb 0 en n := do
613-
ic ← mk_instance_cache `(ℕ),
614-
(_, pn0) ← norm_num.prove_pos ic en,
615-
return (`([%%en] : list ℕ), `(@nat.digits_zero_succ' %%en %%pn0))
616-
| eb 1 en n := do
617-
ic ← mk_instance_cache `(ℕ),
618-
(_, pn0) ← norm_num.prove_pos ic en,
619-
s ← simp_lemmas.add_simp simp_lemmas.mk `list.repeat,
620-
(rhs, p2) ← simplify s [] `(list.repeat 1 %%en),
621-
p ← mk_eq_trans `(nat.digits_one %%en) p2,
622-
return (rhs, p)
623-
| eb b en n := do
624-
ic ← mk_instance_cache `(ℕ),
625-
(_, l, p) ← eval_aux eb b en n ic,
626-
p ← mk_app ``and.left [p],
627-
return (l, p)
628-
629-
end norm_digits
630-
631-
open tactic
632-
633609
/--
634610
A tactic for normalizing expressions of the form `nat.digits a b = l` where
635611
`a` and `b` are numerals.
636612
637613
```
638-
example : nat.digits 10 123 = [3,2,1] := by norm_digits
614+
example : nat.digits 10 123 = [3,2,1] := by norm_num
639615
```
640616
-/
641-
meta def norm_digits : tactic unit :=
642-
do `(nat.digits %%eb %%en = %%el') ← target,
617+
@[norm_num] meta def eval : expr → tactic (expr × expr)
618+
| `(nat.digits %%eb %%en) := do
643619
b ← expr.to_nat eb,
644620
n ← expr.to_nat en,
645-
(el, p) ← nat.norm_digits.eval eb b en n,
646-
unify el el',
647-
exact p
621+
if n = 0 then return (`([] : list ℕ), `(nat.digits_zero %%eb))
622+
else if b = 0 then do
623+
ic ← mk_instance_cache `(ℕ),
624+
(_, pn0) ← norm_num.prove_pos ic en,
625+
return (`([%%en] : list ℕ), `(@nat.digits_zero_succ' %%en %%pn0))
626+
else if b = 1 then do
627+
ic ← mk_instance_cache `(ℕ),
628+
(_, pn0) ← norm_num.prove_pos ic en,
629+
s ← simp_lemmas.add_simp simp_lemmas.mk `list.repeat,
630+
(rhs, p2) ← simplify s [] `(list.repeat 1 %%en),
631+
p ← mk_eq_trans `(nat.digits_one %%en) p2,
632+
return (rhs, p)
633+
else do
634+
ic ← mk_instance_cache `(ℕ),
635+
(_, l, p) ← eval_aux eb b en n ic,
636+
p ← mk_app ``and.left [p],
637+
return (l, p)
638+
| _ := failed
648639

649-
run_cmd add_interactive [``norm_digits]
640+
end norm_digits
650641

651-
add_tactic_doc
652-
{ name := "norm_digits",
653-
category := doc_category.tactic,
654-
decl_names := [`tactic.interactive.norm_digits],
655-
tags := ["arithmetic", "decision procedure"] }
656642
end nat

src/data/nat/prime.lean

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import data.nat.sqrt
88
import data.nat.gcd
99
import algebra.group_power
1010
import tactic.wlog
11+
import tactic.norm_num
1112

1213
/-!
1314
# Prime numbers
@@ -589,7 +590,7 @@ namespace primes
589590
instance : has_repr nat.primes := ⟨λ p, repr p.val⟩
590591
instance : inhabited primes := ⟨⟨2, prime_two⟩⟩
591592

592-
instance coe_nat : has_coe nat.primes ℕ := ⟨subtype.val⟩
593+
instance coe_nat : has_coe nat.primes ℕ := ⟨subtype.val⟩
593594

594595
theorem coe_nat_inj (p q : nat.primes) : (p : ℕ) = (q : ℕ) → p = q :=
595596
λ h, subtype.eq h
@@ -599,3 +600,170 @@ end primes
599600
instance monoid.prime_pow {α : Type*} [monoid α] : has_pow α primes := ⟨λ x p, x^p.val⟩
600601

601602
end nat
603+
604+
/-! ### Primality prover -/
605+
606+
namespace tactic
607+
namespace norm_num
608+
open norm_num
609+
610+
lemma not_prime_helper (a b n : ℕ)
611+
(h : a * b = n) (h₁ : 1 < a) (h₂ : 1 < b) : ¬ nat.prime n :=
612+
by rw ← h; exact nat.not_prime_mul h₁ h₂
613+
614+
lemma is_prime_helper (n : ℕ)
615+
(h₁ : 1 < n) (h₂ : nat.min_fac n = n) : nat.prime n :=
616+
nat.prime_def_min_fac.2 ⟨h₁, h₂⟩
617+
618+
lemma min_fac_bit0 (n : ℕ) : nat.min_fac (bit0 n) = 2 :=
619+
by simp [nat.min_fac_eq, show 2 ∣ bit0 n, by simp [bit0_eq_two_mul n]]
620+
621+
/-- A predicate representing partial progress in a proof of `min_fac`. -/
622+
def min_fac_helper (n k : ℕ) : Prop :=
623+
0 < k ∧ bit1 k ≤ nat.min_fac (bit1 n)
624+
625+
theorem min_fac_helper.n_pos {n k : ℕ} (h : min_fac_helper n k) : 0 < n :=
626+
nat.pos_iff_ne_zero.2 $ λ e,
627+
by rw e at h; exact not_le_of_lt (nat.bit1_lt h.1) h.2
628+
629+
lemma min_fac_ne_bit0 {n k : ℕ} : nat.min_fac (bit1 n) ≠ bit0 k :=
630+
by rw bit0_eq_two_mul; exact λ e, absurd
631+
((nat.dvd_add_iff_right (by simp [bit0_eq_two_mul n])).2
632+
(dvd_trans ⟨_, e⟩ (nat.min_fac_dvd _)))
633+
dec_trivial
634+
635+
lemma min_fac_helper_0 (n : ℕ) (h : 0 < n) : min_fac_helper n 1 :=
636+
begin
637+
refine ⟨zero_lt_one, lt_of_le_of_ne _ min_fac_ne_bit0.symm⟩,
638+
refine @lt_of_le_of_ne ℕ _ _ _ (nat.min_fac_pos _) _,
639+
intro e,
640+
have := nat.min_fac_prime _,
641+
{ rw ← e at this, exact nat.not_prime_one this },
642+
{ exact ne_of_gt (nat.bit1_lt h) }
643+
end
644+
645+
lemma min_fac_helper_1 {n k k' : ℕ} (e : k + 1 = k')
646+
(np : nat.min_fac (bit1 n) ≠ bit1 k)
647+
(h : min_fac_helper n k) : min_fac_helper n k' :=
648+
begin
649+
rw ← e,
650+
refine ⟨nat.succ_pos _,
651+
(lt_of_le_of_ne (lt_of_le_of_ne _ _ : k+1+k < _)
652+
min_fac_ne_bit0.symm : bit0 (k+1) < _)⟩,
653+
{ rw add_right_comm, exact h.2 },
654+
{ rw add_right_comm, exact np.symm }
655+
end
656+
657+
lemma min_fac_helper_2 (n k k' : ℕ) (e : k + 1 = k')
658+
(np : ¬ nat.prime (bit1 k)) (h : min_fac_helper n k) : min_fac_helper n k' :=
659+
begin
660+
refine min_fac_helper_1 e _ h,
661+
intro e₁, rw ← e₁ at np,
662+
exact np (nat.min_fac_prime $ ne_of_gt $ nat.bit1_lt h.n_pos)
663+
end
664+
665+
lemma min_fac_helper_3 (n k k' c : ℕ) (e : k + 1 = k')
666+
(nc : bit1 n % bit1 k = c) (c0 : 0 < c)
667+
(h : min_fac_helper n k) : min_fac_helper n k' :=
668+
begin
669+
refine min_fac_helper_1 e _ h,
670+
refine mt _ (ne_of_gt c0), intro e₁,
671+
rw [← nc, ← nat.dvd_iff_mod_eq_zero, ← e₁],
672+
apply nat.min_fac_dvd
673+
end
674+
675+
lemma min_fac_helper_4 (n k : ℕ) (hd : bit1 n % bit1 k = 0)
676+
(h : min_fac_helper n k) : nat.min_fac (bit1 n) = bit1 k :=
677+
by rw ← nat.dvd_iff_mod_eq_zero at hd; exact
678+
le_antisymm (nat.min_fac_le_of_dvd (nat.bit1_lt h.1) hd) h.2
679+
680+
lemma min_fac_helper_5 (n k k' : ℕ) (e : bit1 k * bit1 k = k')
681+
(hd : bit1 n < k') (h : min_fac_helper n k) : nat.min_fac (bit1 n) = bit1 n :=
682+
begin
683+
refine (nat.prime_def_min_fac.1 (nat.prime_def_le_sqrt.2
684+
⟨nat.bit1_lt h.n_pos, _⟩)).2,
685+
rw ← e at hd,
686+
intros m m2 hm md,
687+
have := le_trans h.2 (le_trans (nat.min_fac_le_of_dvd m2 md) hm),
688+
rw nat.le_sqrt at this,
689+
exact not_le_of_lt hd this
690+
end
691+
692+
/-- Given `e` a natural numeral and `d : nat` a factor of it, return `⊢ ¬ prime e`. -/
693+
meta def prove_non_prime (e : expr) (n d₁ : ℕ) : tactic expr :=
694+
do let e₁ := reflect d₁,
695+
c ← mk_instance_cache `(nat),
696+
(c, p₁) ← prove_lt_nat c `(1) e₁,
697+
let d₂ := n / d₁, let e₂ := reflect d₂,
698+
(c, e', p) ← prove_mul_nat c e₁ e₂,
699+
guard (e' =ₐ e),
700+
(c, p₂) ← prove_lt_nat c `(1) e₂,
701+
return $ `(not_prime_helper).mk_app [e₁, e₂, e, p, p₁, p₂]
702+
703+
/-- Given `a`,`a1 := bit1 a`, `n1` the value of `a1`, `b` and `p : min_fac_helper a b`,
704+
returns `(c, ⊢ min_fac a1 = c)`. -/
705+
meta def prove_min_fac_aux (a a1 : expr) (n1 : ℕ) :
706+
instance_cache → expr → expr → tactic (instance_cache × expr × expr)
707+
| ic b p := do
708+
k ← b.to_nat,
709+
let k1 := bit1 k,
710+
let b1 := `(bit1:ℕ→ℕ).mk_app [b],
711+
if n1 < k1*k1 then do
712+
(ic, e', p₁) ← prove_mul_nat ic b1 b1,
713+
(ic, p₂) ← prove_lt_nat ic a1 e',
714+
return (ic, a1, `(min_fac_helper_5).mk_app [a, b, e', p₁, p₂, p])
715+
else let d := k1.min_fac in
716+
if to_bool (d < k1) then do
717+
let k' := k+1, let e' := reflect k',
718+
(ic, p₁) ← prove_succ ic b e',
719+
p₂ ← prove_non_prime b1 k1 d,
720+
prove_min_fac_aux ic e' $ `(min_fac_helper_2).mk_app [a, b, e', p₁, p₂, p]
721+
else do
722+
let nc := n1 % k1,
723+
(ic, c, pc) ← prove_div_mod ic a1 b1 tt,
724+
if nc = 0 then
725+
return (ic, b1, `(min_fac_helper_4).mk_app [a, b, pc, p])
726+
else do
727+
(ic, p₀) ← prove_pos ic c,
728+
let k' := k+1, let e' := reflect k',
729+
(ic, p₁) ← prove_succ ic b e',
730+
prove_min_fac_aux ic e' $ `(min_fac_helper_3).mk_app [a, b, e', c, p₁, pc, p₀, p]
731+
732+
/-- Given `a` a natural numeral, returns `(b, ⊢ min_fac a = b)`. -/
733+
meta def prove_min_fac (ic : instance_cache) (e : expr) : tactic (instance_cache × expr × expr) :=
734+
match match_numeral e with
735+
| match_numeral_result.zero := return (ic, `(2:ℕ), `(nat.min_fac_zero))
736+
| match_numeral_result.one := return (ic, `(1:ℕ), `(nat.min_fac_one))
737+
| match_numeral_result.bit0 e := return (ic, `(2), `(min_fac_bit0).mk_app [e])
738+
| match_numeral_result.bit1 e := do
739+
n ← e.to_nat,
740+
c ← mk_instance_cache `(nat),
741+
(c, p) ← prove_pos c e,
742+
let a1 := `(bit1:ℕ→ℕ).mk_app [e],
743+
prove_min_fac_aux e a1 (bit1 n) c `(1) (`(min_fac_helper_0).mk_app [e, p])
744+
| _ := failed
745+
end
746+
747+
/-- Evaluates the `prime` and `min_fac` functions. -/
748+
@[norm_num] meta def eval_prime : expr → tactic (expr × expr)
749+
| `(nat.prime %%e) := do
750+
n ← e.to_nat,
751+
match n with
752+
| 0 := false_intro `(nat.not_prime_zero)
753+
| 1 := false_intro `(nat.not_prime_one)
754+
| _ := let d₁ := n.min_fac in
755+
if d₁ < n then prove_non_prime e n d₁ >>= false_intro
756+
else do
757+
let e₁ := reflect d₁,
758+
c ← mk_instance_cache `(nat),
759+
(c, p₁) ← prove_lt_nat c `(1) e₁,
760+
(c, e₁, p) ← prove_min_fac c e,
761+
true_intro $ `(is_prime_helper).mk_app [e, p₁, p]
762+
end
763+
| `(nat.min_fac %%e) := do
764+
ic ← mk_instance_cache `(ℕ),
765+
prod.snd <$> prove_min_fac ic e
766+
| _ := failed
767+
768+
end norm_num
769+
end tactic

src/number_theory/divisors.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Authors: Aaron Anderson
55
-/
66
import algebra.big_operators.order
77
import tactic
8+
import data.nat.prime
89

910
/-!
1011
# Divisor finsets

0 commit comments

Comments
 (0)