CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import data.nat.choose ring_theory.multiplicity data.nat.modeq algebra.gcd_domain
/-!

# Natural number multiplicity

This file contains lemmas about the multiplicity function (the maximum prime power divding a number).

# Main results

There are natural number versions of some basic lemmas about multiplicity.

There are also lemmas about the multiplicity of primes in factorials and in binomial coefficients.
-/

open finset nat multiplicity

namespace nat

/-- The multiplicity of a divisor `m` of `n`, is the cardinality of the set of
  positive natural numbers `i` such that `p ^ i` divides `n`. The set is expressed
  by filtering `Ico 1 b` where `b` is any bound at least `n` -/
lemma multiplicity_eq_card_pow_dvd {m n b : ℕ} (hm1 : m ≠ 1) (hn0 : 0 < n) (hb : n ≤ b):
  multiplicity m n = ↑((finset.Ico 1 b).filter (λ i, m ^ i ∣ n)).card :=
calc multiplicity m n = ↑(Ico 1 $ ((multiplicity m n).get (finite_nat_iff.2 ⟨hm1, hn0⟩) + 1)).card :
  by simp
... = ↑((finset.Ico 1 b).filter (λ i, m ^ i ∣ n)).card : congr_arg coe $ congr_arg card $
  finset.ext.2 $ λ i,
  have hmn : ¬ m ^ n ∣ n,
    from if hm0 : m = 0
    then λ _, by cases n; simp [*, lt_irrefl, nat.pow_succ] at *
    else mt (le_of_dvd hn0) (not_le_of_gt $ lt_pow_self
      (lt_of_le_of_ne (nat.pos_of_ne_zero hm0) hm1.symm) _),
  ⟨λ hi, begin
      simp only [Ico.mem, mem_filter, lt_succ_iff] at *,
      exact ⟨⟨hi.1, lt_of_le_of_lt hi.2 $
        lt_of_lt_of_le (by rw [← enat.coe_lt_coe, enat.coe_get, multiplicity_lt_iff_neg_dvd,
            nat.pow_eq_pow]; exact hmn)
          hb⟩,
        by rw [← nat.pow_eq_pow, pow_dvd_iff_le_multiplicity];
          rw [← @enat.coe_le_coe i, enat.coe_get] at hi; exact hi.2⟩
    end,
  begin
    simp only [Ico.mem, mem_filter, lt_succ_iff, and_imp, true_and] { contextual := tt },
    assume h1i hib hmin,
    rwa [← enat.coe_le_coe, enat.coe_get, ← pow_dvd_iff_le_multiplicity, nat.pow_eq_pow]
  end⟩

namespace prime

lemma multiplicity_one {p : ℕ} (hp : p.prime) :
  multiplicity p 1 = 0 :=
by rw [multiplicity.one_right (mt is_unit_nat.mp (ne_of_gt hp.one_lt))]

lemma multiplicity_mul {p m n : ℕ} (hp : p.prime) :
  multiplicity p (m * n) = multiplicity p m + multiplicity p n :=
by rw [int.coe_nat_multiplicity, int.coe_nat_multiplicity,
  int.coe_nat_multiplicity, int.coe_nat_mul, multiplicity.mul (nat.prime_iff_prime_int.1 hp)]

lemma multiplicity_pow {p m n : ℕ} (hp : p.prime) :
  multiplicity p (m ^ n) = add_monoid.smul n (multiplicity p m) :=
by induction n; simp [nat.pow_succ, hp.multiplicity_mul, *, hp.multiplicity_one, succ_smul]

lemma multiplicity_self {p : ℕ} (hp : p.prime) : multiplicity p p = 1 :=
have h₁ : ¬ is_unit (p : ℤ), from mt is_unit_int.1 (ne_of_gt hp.one_lt),
have h₂ : (p : ℤ) ≠ 0, from int.coe_nat_ne_zero.2 hp.ne_zero,
by rw [int.coe_nat_multiplicity, multiplicity_self h₁ h₂]

lemma multiplicity_pow_self {p n : ℕ} (hp : p.prime) : multiplicity p (p ^ n) = n :=
by induction n; simp [hp.multiplicity_one, nat.pow_succ, hp.multiplicity_mul, *,
  hp.multiplicity_self, succ_eq_add_one]

/-- The multiplicity of a prime in `fact n` is the sum of the quotients `n / p ^ i`.
  This sum is expressed over the set `Ico 1 b` where `b` is any bound at least `n` -/
lemma multiplicity_fact {p : ℕ} (hp : p.prime) :
  ∀ {n b : ℕ}, n ≤ b → multiplicity p n.fact = ((Ico 1 b).sum (λ i, n / p ^ i) : ℕ)
| 0     b hb := by simp [Ico, hp.multiplicity_one]
| (n+1) b hb :=
  calc multiplicity p (n+1).fact = multiplicity p n.fact + multiplicity p (n+1) :
    by rw [fact_succ, hp.multiplicity_mul, add_comm]
  ... = ((Ico 1 b).sum (λ i, n / p ^ i) : ℕ) + ((finset.Ico 1 b).filter (λ i, p ^ i ∣ n+1)).card :
    by rw [multiplicity_fact (le_of_succ_le hb),
      ← multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) (succ_pos _) hb]
  ... = ((Ico 1 b).sum (λ i, n / p ^ i + if p^i ∣ n+1 then 1 else 0) : ℕ) :
    by rw [sum_add_distrib, sum_ite (λ _, 1) (λ _, 0) (λ x, x)]; simp
  ... = ((Ico 1 b).sum (λ i, (n + 1) / p ^ i) : ℕ) :
    congr_arg coe $ finset.sum_congr rfl (by intros; simp [nat.succ_div]; congr)

/-- A prime power divides `fact n` iff it is at most the sum of the quotients `n / p ^ i`.
  This sum is expressed over the set `Ico 1 b` where `b` is any bound at least `n` -/
lemma pow_dvd_fact_iff {p : ℕ} {n r b : ℕ} (hp : p.prime) (hbn : n ≤ b) :
   p ^ r ∣ fact n ↔ r ≤ (Ico 1 b).sum (λ i, n / p ^ i) :=
by rw [← enat.coe_le_coe, ← hp.multiplicity_fact hbn, ← pow_dvd_iff_le_multiplicity, nat.pow_eq_pow]

lemma multiplicity_choose_aux {p n b k : ℕ} (hp : p.prime) (hkn : k ≤ n) :
  (finset.Ico 1 b).sum (λ i, n / p ^ i) =
  (finset.Ico 1 b).sum (λ i, k / p ^ i) + (finset.Ico 1 b).sum (λ i, (n - k) / p ^ i) +
  ((finset.Ico 1 b).filter (λ i, p ^ i ≤ k % p ^ i + (n - k) % p ^ i)).card :=
calc (finset.Ico 1 b).sum (λ i, n / p ^ i)
    = (finset.Ico 1 b).sum (λ i, (k + (n - k)) / p ^ i) :
    by simp only [nat.add_sub_cancel' hkn]
... = (finset.Ico 1 b).sum (λ i, k / p ^ i + (n - k) / p ^ i +
      if p ^ i ≤ k % p ^ i + (n - k) % p ^ i then 1 else 0) : by simp only [nat.add_div (nat.pow_pos hp.pos _)]
... = _ : by simp [sum_add_distrib, sum_ite (λ _, 1) (λ _, 0) (λ x, x)]

/-- The multiplity of `p` in `choose n k` is the number of carries when `k` and `n - k`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound at least `n`. -/
lemma multiplicity_choose {p n k b : ℕ} (hp : p.prime) (hkn : k ≤ n) (hnb : n ≤ b) :
  multiplicity p (choose n k) =
  ((Ico 1 b).filter (λ i, p ^ i ≤ k % p ^ i + (n - k) % p ^ i)).card :=
have h₁ : multiplicity p (choose n k) + multiplicity p (k.fact * (n - k).fact) =
    ((finset.Ico 1 b).filter (λ i, p ^ i ≤ k % p ^ i + (n - k) % p ^ i)).card +
    multiplicity p (k.fact * (n - k).fact),
  begin
    rw [← hp.multiplicity_mul, ← mul_assoc, choose_mul_fact_mul_fact hkn,
      hp.multiplicity_fact hnb, hp.multiplicity_mul, hp.multiplicity_fact (le_trans hkn hnb),
      hp.multiplicity_fact (le_trans (nat.sub_le_self _ _) hnb),
      multiplicity_choose_aux hp hkn],
    simp,
  end,
(enat.add_right_cancel_iff
  (enat.ne_top_iff_dom.2 $
    by exact finite_nat_iff.2 ⟨ne_of_gt hp.one_lt, mul_pos (fact_pos k) (fact_pos (n - k))⟩)).1
  h₁

/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
lemma multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.prime) (n k : ℕ) :
  multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k :=
if hkn : n < k then by simp [choose_eq_zero_of_lt hkn]
else if hk0 : k = 0 then by simp [hk0]
else if hn0 : n = 0 then by cases k; simp [hn0, *] at *
else begin
  rw [multiplicity_choose hp (le_of_not_gt hkn) (le_refl _),
    multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) (nat.pos_of_ne_zero hk0) (le_of_not_gt hkn),
    multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) (nat.pos_of_ne_zero hn0) (le_refl _),
    ← enat.coe_add, enat.coe_le_coe],
  calc ((Ico 1 n).filter (λ i, p ^ i ∣ n)).card
      ≤ ((Ico 1 n).filter (λ i, p ^ i ≤ k % p ^ i + (n - k) % p ^ i) ∪
        (Ico 1 n).filter (λ i, p ^ i ∣ k) ).card :
    card_le_of_subset $ λ i, begin
      have := @le_mod_add_mod_of_dvd_add_of_not_dvd k (n - k) (p ^ i),
      simp [nat.add_sub_cancel' (le_of_not_gt hkn)] at * {contextual := tt},
      tauto
    end
  ... ≤ ((Ico 1 n).filter (λ i, p ^ i ≤ k % p ^ i + (n - k) % p ^ i)).card +
        ((Ico 1 n).filter (λ i, p ^ i ∣ k)).card :
    card_union_le _ _
end

lemma multiplicity_choose_prime_pow {p n k : ℕ} (hp : p.prime)
  (hkn : k ≤ p ^ n) (hk0 : 0 < k) :
  multiplicity p (choose (p ^ n) k) + multiplicity p k = n :=
le_antisymm
  (have hdisj : disjoint
      ((Ico 1 (p ^ n)).filter (λ i, p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i))
      ((Ico 1 (p ^ n)).filter (λ i, p ^ i ∣ k)),
    by simp [disjoint_right, *, dvd_iff_mod_eq_zero, nat.mod_lt _ (nat.pow_pos hp.pos _)]
        {contextual := tt},
  have filter_subset_Ico : filter (λ i, p ^ i ≤ k % p ^ i +
      (p ^ n - k) % p ^ i ∨ p ^ i ∣ k) (Ico 1 (p ^ n)) ⊆ Ico 1 n.succ,
    from begin
      simp only [finset.subset_iff, Ico.mem, mem_filter, and_imp, true_and] {contextual := tt},
      assume i h1i hip h,
      refine lt_succ_of_le (le_of_not_gt (λ hin, _)),
      have hpik : ¬ p ^ i ∣ k, from mt (le_of_dvd hk0)
        (not_le_of_gt (lt_of_le_of_lt hkn (pow_right_strict_mono hp.two_le hin))),
      have hpn : k % p ^ i + (p ^ n - k) % p ^ i < p ^ i,
        from calc k % p ^ i + (p ^ n - k) % p ^ i
              ≤ k + (p ^ n - k) : add_le_add (mod_le _ _) (mod_le _ _)
          ... = p ^ n : nat.add_sub_cancel' hkn
          ... < p ^ i : pow_right_strict_mono hp.two_le hin,
      simpa [hpik, not_le_of_gt hpn] using h
    end,
  begin
    rw [multiplicity_choose hp hkn (le_refl _),
      multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) hk0 hkn, ← enat.coe_add,
      enat.coe_le_coe, ← card_disjoint_union hdisj, filter_union_right],
    exact le_trans (card_le_of_subset filter_subset_Ico) (by simp)
  end)
  (by rw [← hp.multiplicity_pow_self];
    exact multiplicity_le_multiplicity_choose_add hp _ _)

end prime
end nat