l3lab/ntp-mathlib huggingface dataset:
/-- The multiplicity of `p` in `(p * n)!` is `n` more than that of `n!`. -/
theorem multiplicity_factorial_mul {n p : ℕ} (hp : p.Prime) :
    multiplicity p (p * n)! = multiplicity p n ! + n := by
  induction' n with n ih
  · simp
  · 

Proof state:
case succ
p : ℕ
hp : Prime p
n : ℕ
ih : multiplicity p (p * n)! = multiplicity p n ! + ↑n
⊢ multiplicity p (p * succ n)! = multiplicity p (succ n)! + ↑(succ n)

Next tactic:
simp only [succ_eq_add_one, multiplicity.mul, hp, hp.prime, ih, multiplicity_factorial_mul_succ,
      ← add_assoc, Nat.cast_one, Nat.cast_add, factorial_succ]
/-- The multiplicity of `p` in `(p * n)!` is `n` more than that of `n!`. -/
theorem multiplicity_factorial_mul {n p : ℕ} (hp : p.Prime) :
    multiplicity p (p * n)! = multiplicity p n ! + n := by
  induction' n with n ih
  · simp
  · simp only [succ_eq_add_one, multiplicity.mul, hp, hp.prime, ih, multiplicity_factorial_mul_succ,
      ← add_assoc, Nat.cast_one, Nat.cast_add, factorial_succ]
    

Proof state:
case succ
p : ℕ
hp : Prime p
n : ℕ
ih : multiplicity p (p * n)! = multiplicity p n ! + ↑n
⊢ multiplicity p n ! + ↑n + multiplicity p (n + 1) + 1 = multiplicity p (n + 1) + multiplicity p n ! + ↑n + 1

Next tactic:
congr 1
/-- The multiplicity of `p` in `(p * n)!` is `n` more than that of `n!`. -/
theorem multiplicity_factorial_mul {n p : ℕ} (hp : p.Prime) :
    multiplicity p (p * n)! = multiplicity p n ! + n := by
  induction' n with n ih
  · simp
  · simp only [succ_eq_add_one, multiplicity.mul, hp, hp.prime, ih, multiplicity_factorial_mul_succ,
      ← add_assoc, Nat.cast_one, Nat.cast_add, factorial_succ]
    congr 1
    

Proof state:
case succ.e_a
p : ℕ
hp : Prime p
n : ℕ
ih : multiplicity p (p * n)! = multiplicity p n ! + ↑n
⊢ multiplicity p n ! + ↑n + multiplicity p (n + 1) = multiplicity p (n + 1) + multiplicity p n ! + ↑n

Next tactic:
rw [add_comm, add_assoc]
/-- A prime power divides `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 greater than `log p n` -/
theorem pow_dvd_factorial_iff {p : ℕ} {n r b : ℕ} (hp : p.Prime) (hbn : log p n < b) :
    p ^ r ∣ n ! ↔ r ≤ ∑ i in Ico 1 b, n / p ^ i := by
  

Proof state:
p n r b : ℕ
hp : Prime p
hbn : log p n < b
⊢ p ^ r ∣ n ! ↔ r ≤ ∑ i in Ico 1 b, n / p ^ i

Next tactic:
rw [← PartENat.coe_le_coe, ← hp.multiplicity_factorial hbn, ← pow_dvd_iff_le_multiplicity]
theorem multiplicity_factorial_le_div_pred {p : ℕ} (hp : p.Prime) (n : ℕ) :
    multiplicity p n ! ≤ (n / (p - 1) : ℕ) := by
  

Proof state:
p : ℕ
hp : Prime p
n : ℕ
⊢ multiplicity p n ! ≤ ↑(n / (p - 1))

Next tactic:
rw [hp.multiplicity_factorial (lt_succ_self _), PartENat.coe_le_coe]
theorem multiplicity_factorial_le_div_pred {p : ℕ} (hp : p.Prime) (n : ℕ) :
    multiplicity p n ! ≤ (n / (p - 1) : ℕ) := by
  rw [hp.multiplicity_factorial (lt_succ_self _), PartENat.coe_le_coe]
  

Proof state:
p : ℕ
hp : Prime p
n : ℕ
⊢ ∑ i in Ico 1 (succ (log p n)), n / p ^ i ≤ n / (p - 1)

Next tactic:
exact Nat.geom_sum_Ico_le hp.two_le _ _
theorem multiplicity_choose_aux {p n b k : ℕ} (hp : p.Prime) (hkn : k ≤ n) :
    ∑ i in Finset.Ico 1 b, n / p ^ i =
      ((∑ i in Finset.Ico 1 b, k / p ^ i) + ∑ i in Finset.Ico 1 b, (n - k) / p ^ i) +
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card :=
  calc
    ∑ i in Finset.Ico 1 b, n / p ^ i = ∑ i in Finset.Ico 1 b, (k + (n - k)) / p ^ i := by
      

Proof state:
p n b k : ℕ
hp : Prime p
hkn : k ≤ n
⊢ ∑ i in Ico 1 b, n / p ^ i = ∑ i in Ico 1 b, (k + (n - k)) / p ^ i

Next tactic:
simp only [add_tsub_cancel_of_le hkn]
theorem multiplicity_choose_aux {p n b k : ℕ} (hp : p.Prime) (hkn : k ≤ n) :
    ∑ i in Finset.Ico 1 b, n / p ^ i =
      ((∑ i in Finset.Ico 1 b, k / p ^ i) + ∑ i in Finset.Ico 1 b, (n - k) / p ^ i) +
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card :=
  calc
    ∑ i in Finset.Ico 1 b, n / p ^ i = ∑ i in Finset.Ico 1 b, (k + (n - k)) / p ^ i := by
      simp only [add_tsub_cancel_of_le hkn]
    _ = ∑ i in Finset.Ico 1 b,
          (k / p ^ i + (n - k) / p ^ i + if p ^ i ≤ k % p ^ i + (n - k) % p ^ i then 1 else 0) :=
      by 

Proof state:
p n b k : ℕ
hp : Prime p
hkn : k ≤ n
⊢ ∑ i in Ico 1 b, (k + (n - k)) / p ^ i =
    ∑ i in Ico 1 b, (k / p ^ i + (n - k) / p ^ i + if p ^ i ≤ k % p ^ i + (n - k) % p ^ i then 1 else 0)

Next tactic:
simp only [Nat.add_div (pow_pos hp.pos _)]
theorem multiplicity_choose_aux {p n b k : ℕ} (hp : p.Prime) (hkn : k ≤ n) :
    ∑ i in Finset.Ico 1 b, n / p ^ i =
      ((∑ i in Finset.Ico 1 b, k / p ^ i) + ∑ i in Finset.Ico 1 b, (n - k) / p ^ i) +
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card :=
  calc
    ∑ i in Finset.Ico 1 b, n / p ^ i = ∑ i in Finset.Ico 1 b, (k + (n - k)) / p ^ i := by
      simp only [add_tsub_cancel_of_le hkn]
    _ = ∑ i in Finset.Ico 1 b,
          (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 (pow_pos hp.pos _)]
    _ = _ := by 

Proof state:
p n b k : ℕ
hp : Prime p
hkn : k ≤ n
⊢ ∑ i in Ico 1 b, (k / p ^ i + (n - k) / p ^ i + if p ^ i ≤ k % p ^ i + (n - k) % p ^ i then 1 else 0) =
    ∑ i in Ico 1 b, k / p ^ i + ∑ i in Ico 1 b, (n - k) / p ^ i +
      card (filter (fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i) (Ico 1 b))

Next tactic:
simp [sum_add_distrib, sum_boole]
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
⊢ multiplicity p (choose (n + k) k) = ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b)))

Next tactic:
have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
    simp [add_comm]
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
⊢ multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)

Next tactic:
rw [← hp.multiplicity_mul, ← mul_assoc]
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
⊢ multiplicity p (choose (n + k) k * k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)

Next tactic:
have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
this : choose (n + k) k * k ! * n ! = (n + k)!
⊢ multiplicity p (choose (n + k) k * k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)

Next tactic:
rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
    

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
this : choose (n + k) k * k ! * n ! = (n + k)!
⊢ ↑(∑ i in Ico 1 b, k / p ^ i + ∑ i in Ico 1 b, (n + k - k) / p ^ i +
        card (filter (fun i => p ^ i ≤ k % p ^ i + (n + k - k) % p ^ i) (Ico 1 b))) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) +
      (↑(∑ i in Ico 1 b, k / p ^ i) + ↑(∑ i in Ico 1 b, n / p ^ i))

Next tactic:
simp [add_comm]
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
    simp [add_comm]
  

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
h₁ :
  multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)
⊢ multiplicity p (choose (n + k) k) = ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b)))

Next tactic:
refine (PartENat.add_right_cancel_iff ?_).1 h₁
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
    simp [add_comm]
  refine (PartENat.add_right_cancel_iff ?_).1 h₁
  

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
h₁ :
  multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)
⊢ multiplicity p (k ! * n !) ≠ ⊤

Next tactic:
apply PartENat.ne_top_iff_dom.2
/-- The multiplicity of `p` in `choose (n + k) k` is the number of carries when `k` and `n`
  are added in base `p`. The set is expressed by filtering `Ico 1 b` where `b`
  is any bound greater than `log p (n + k)`. -/
theorem multiplicity_choose' {p n k b : ℕ} (hp : p.Prime) (hnb : log p (n + k) < b) :
    multiplicity p (choose (n + k) k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card := by
  have h₁ :
      multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
        ((Finset.Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + n % p ^ i).card +
          multiplicity p (k ! * n !) := by
    rw [← hp.multiplicity_mul, ← mul_assoc]
    have := (add_tsub_cancel_right n k) ▸ choose_mul_factorial_mul_factorial (le_add_left k n)
    rw [this, hp.multiplicity_factorial hnb, hp.multiplicity_mul,
      hp.multiplicity_factorial ((log_mono_right (le_add_left k n)).trans_lt hnb),
      hp.multiplicity_factorial ((log_mono_right (le_add_left n k)).trans_lt
      (add_comm n k ▸ hnb)), multiplicity_choose_aux hp (le_add_left k n)]
    simp [add_comm]
  refine (PartENat.add_right_cancel_iff ?_).1 h₁
  apply PartENat.ne_top_iff_dom.2
  

Proof state:
p n k b : ℕ
hp : Prime p
hnb : log p (n + k) < b
h₁ :
  multiplicity p (choose (n + k) k) + multiplicity p (k ! * n !) =
    ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + n % p ^ i) (Ico 1 b))) + multiplicity p (k ! * n !)
⊢ (multiplicity p (k ! * n !)).Dom

Next tactic:
exact finite_nat_iff.2 ⟨hp.ne_one, mul_pos (factorial_pos k) (factorial_pos n)⟩
/-- The multiplicity 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 greater than `log p n`. -/
theorem multiplicity_choose {p n k b : ℕ} (hp : p.Prime) (hkn : k ≤ n) (hnb : log p n < b) :
    multiplicity p (choose n k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card := by
  

Proof state:
p n k b : ℕ
hp : Prime p
hkn : k ≤ n
hnb : log p n < b
⊢ multiplicity p (choose n k) = ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i) (Ico 1 b)))

Next tactic:
have := Nat.sub_add_cancel hkn
/-- The multiplicity 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 greater than `log p n`. -/
theorem multiplicity_choose {p n k b : ℕ} (hp : p.Prime) (hkn : k ≤ n) (hnb : log p n < b) :
    multiplicity p (choose n k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card := by
  have := Nat.sub_add_cancel hkn
  

Proof state:
p n k b : ℕ
hp : Prime p
hkn : k ≤ n
hnb : log p n < b
this : n - k + k = n
⊢ multiplicity p (choose n k) = ↑(card (filter (fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i) (Ico 1 b)))

Next tactic:
convert @multiplicity_choose' p (n - k) k b hp _
/-- The multiplicity 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 greater than `log p n`. -/
theorem multiplicity_choose {p n k b : ℕ} (hp : p.Prime) (hkn : k ≤ n) (hnb : log p n < b) :
    multiplicity p (choose n k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card := by
  have := Nat.sub_add_cancel hkn
  convert @multiplicity_choose' p (n - k) k b hp _
  · 

Proof state:
case h.e'_2.h.e'_5.h.e'_1
p n k b : ℕ
hp : Prime p
hkn : k ≤ n
hnb : log p n < b
this : n - k + k = n
⊢ n = n - k + k

Next tactic:
rw [this]
/-- The multiplicity 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 greater than `log p n`. -/
theorem multiplicity_choose {p n k b : ℕ} (hp : p.Prime) (hkn : k ≤ n) (hnb : log p n < b) :
    multiplicity p (choose n k) =
      ((Ico 1 b).filter fun i => p ^ i ≤ k % p ^ i + (n - k) % p ^ i).card := by
  have := Nat.sub_add_cancel hkn
  convert @multiplicity_choose' p (n - k) k b hp _
  · rw [this]
  

Proof state:
p n k b : ℕ
hp : Prime p
hkn : k ≤ n
hnb : log p n < b
this : n - k + k = n
⊢ log p (n - k + k) < b

Next tactic:
exact this.symm ▸ hnb
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by 

Proof state:
p : ℕ
hp : Prime p
x✝ : ℕ
⊢ multiplicity p x✝ ≤ multiplicity p (choose x✝ 0) + multiplicity p 0

Next tactic:
simp
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by 

Proof state:
p : ℕ
hp : Prime p
n✝ : ℕ
⊢ multiplicity p 0 ≤ multiplicity p (choose 0 (n✝ + 1)) + multiplicity p (n✝ + 1)

Next tactic:
simp
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by simp
  | n + 1, k + 1 => by
    

Proof state:
p : ℕ
hp : Prime p
n k : ℕ
⊢ multiplicity p (n + 1) ≤ multiplicity p (choose (n + 1) (k + 1)) + multiplicity p (k + 1)

Next tactic:
rw [← hp.multiplicity_mul]
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by simp
  | n + 1, k + 1 => by
    rw [← hp.multiplicity_mul]
    

Proof state:
p : ℕ
hp : Prime p
n k : ℕ
⊢ multiplicity p (n + 1) ≤ multiplicity p (choose (n + 1) (k + 1) * (k + 1))

Next tactic:
refine' multiplicity_le_multiplicity_of_dvd_right _
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by simp
  | n + 1, k + 1 => by
    rw [← hp.multiplicity_mul]
    refine' multiplicity_le_multiplicity_of_dvd_right _
    

Proof state:
p : ℕ
hp : Prime p
n k : ℕ
⊢ n + 1 ∣ choose (n + 1) (k + 1) * (k + 1)

Next tactic:
rw [← succ_mul_choose_eq]
/-- A lower bound on the multiplicity of `p` in `choose n k`. -/
theorem multiplicity_le_multiplicity_choose_add {p : ℕ} (hp : p.Prime) :
    ∀ n k : ℕ, multiplicity p n ≤ multiplicity p (choose n k) + multiplicity p k
  | _, 0 => by simp
  | 0, _ + 1 => by simp
  | n + 1, k + 1 => by
    rw [← hp.multiplicity_mul]
    refine' multiplicity_le_multiplicity_of_dvd_right _
    rw [← succ_mul_choose_eq]
    

Proof state:
p : ℕ
hp : Prime p
n k : ℕ
⊢ n + 1 ∣ succ n * choose n k

Next tactic:
exact dvd_mul_right _ _
theorem multiplicity_choose_prime_pow_add_multiplicity (hp : p.Prime) (hkn : k ≤ p ^ n)
    (hk0 : k ≠ 0) : multiplicity p (choose (p ^ n) k) + multiplicity p k = n :=
  le_antisymm
    (by
      

Proof state:
p n k : ℕ
hp : Prime p
hkn : k ≤ p ^ n
hk0 : k ≠ 0
⊢ multiplicity p (choose (p ^ n) k) + multiplicity p k ≤ ↑n

Next tactic:
have hdisj :
        Disjoint ((Ico 1 n.succ).filter fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i)
          ((Ico 1 n.succ).filter fun i => p ^ i ∣ k) := by
        simp (config := { contextual := true }) [disjoint_right, *, dvd_iff_mod_eq_zero,
          Nat.mod_lt _ (pow_pos hp.pos _)]
theorem multiplicity_choose_prime_pow_add_multiplicity (hp : p.Prime) (hkn : k ≤ p ^ n)
    (hk0 : k ≠ 0) : multiplicity p (choose (p ^ n) k) + multiplicity p k = n :=
  le_antisymm
    (by
      have hdisj :
        Disjoint ((Ico 1 n.succ).filter fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i)
          ((Ico 1 n.succ).filter fun i => p ^ i ∣ k) := by
        

Proof state:
p n k : ℕ
hp : Prime p
hkn : k ≤ p ^ n
hk0 : k ≠ 0
⊢ Disjoint (filter (fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i) (Ico 1 (succ n)))
    (filter (fun i => p ^ i ∣ k) (Ico 1 (succ n)))

Next tactic:
simp (config := { contextual := true }) [disjoint_right, *, dvd_iff_mod_eq_zero,
          Nat.mod_lt _ (pow_pos hp.pos _)]
theorem multiplicity_choose_prime_pow_add_multiplicity (hp : p.Prime) (hkn : k ≤ p ^ n)
    (hk0 : k ≠ 0) : multiplicity p (choose (p ^ n) k) + multiplicity p k = n :=
  le_antisymm
    (by
      have hdisj :
        Disjoint ((Ico 1 n.succ).filter fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i)
          ((Ico 1 n.succ).filter fun i => p ^ i ∣ k) := by
        simp (config := { contextual := true }) [disjoint_right, *, dvd_iff_mod_eq_zero,
          Nat.mod_lt _ (pow_pos hp.pos _)]
      

Proof state:
p n k : ℕ
hp : Prime p
hkn : k ≤ p ^ n
hk0 : k ≠ 0
hdisj :
  Disjoint (filter (fun i => p ^ i ≤ k % p ^ i + (p ^ n - k) % p ^ i) (Ico 1 (succ n)))
    (filter (fun i => p ^ i ∣ k) (Ico 1 (succ n)))
⊢ multiplicity p (choose (p ^ n) k) + multiplicity p k ≤ ↑n

Next tactic:
rw [multiplicity_choose hp hkn (lt_succ_self _),
        multiplicity_eq_card_pow_dvd (ne_of_gt hp.one_lt) hk0.bot_lt
          (lt_succ_of_le (log_mono_right hkn)),
        ← Nat.cast_add, PartENat.coe_le_coe, log_pow hp.one_lt, ← card_disjoint_union hdisj,
        filter_union_right]