/-
Copyright (c) 2021 Stuart Presnell. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Stuart Presnell

! This file was ported from Lean 3 source module data.nat.factorization.basic
! leanprover-community/mathlib commit f694c7dead66f5d4c80f446c796a5aad14707f0e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.BigOperators.Finsupp
import Mathbin.Data.Finsupp.Multiset
import Mathbin.Data.Nat.PrimeFin
import Mathbin.NumberTheory.Padics.PadicVal
import Mathbin.Data.Nat.Interval
import Mathbin.Tactic.IntervalCases

/-!
# Prime factorizations

 `n.factorization` is the finitely supported function `ℕ →₀ ℕ`
 mapping each prime factor of `n` to its multiplicity in `n`.  For example, since 2000 = 2^4 * 5^3,
  * `factorization 2000 2` is 4
  * `factorization 2000 5` is 3
  * `factorization 2000 k` is 0 for all other `k : ℕ`.

## TODO

* As discussed in this Zulip thread:
https://leanprover.zulipchat.com/#narrow/stream/217875/topic/Multiplicity.20in.20the.20naturals
We have lots of disparate ways of talking about the multiplicity of a prime
in a natural number, including `factors.count`, `padic_val_nat`, `multiplicity`,
and the material in `data/pnat/factors`.  Move some of this material to this file,
prove results about the relationships between these definitions,
and (where appropriate) choose a uniform canonical way of expressing these ideas.

* Moreover, the results here should be generalised to an arbitrary unique factorization monoid
with a normalization function, and then deduplicated.  The basics of this have been started in
`ring_theory/unique_factorization_domain`.

* Extend the inductions to any `normalization_monoid` with unique factorization.

-/

open Nat Finset List Finsupp

open BigOperators

namespace Nat

#print Nat.factorization /-
/-- `n.factorization` is the finitely supported function `ℕ →₀ ℕ`
 mapping each prime factor of `n` to its multiplicity in `n`. -/
def factorization (n : ℕ) : ℕ →₀ ℕ
    where
  support := n.factors.toFinset
  toFun p := if p.Prime then padicValNat p n else 0
  mem_support_toFun := by
    rcases eq_or_ne n 0 with (rfl | hn0); · simp
    simp only [mem_factors hn0, mem_to_finset, Ne.def, ite_eq_right_iff, not_forall, exists_prop,
      and_congr_right_iff]
    rintro p hp
    haveI := fact_iff.mpr hp
    exact dvd_iff_padicValNat_ne_zero hn0
#align nat.factorization Nat.factorization
-/

#print Nat.factorization_def /-
theorem factorization_def (n : ℕ) {p : ℕ} (pp : p.Prime) : n.factorization p = padicValNat p n := by
  simpa [factorization] using absurd pp
#align nat.factorization_def Nat.factorization_def
-/

#print Nat.factors_count_eq /-
/-- We can write both `n.factorization p` and `n.factors.count p` to represent the power
of `p` in the factorization of `n`: we declare the former to be the simp-normal form. -/
@[simp]
theorem factors_count_eq {n p : ℕ} : n.factors.count p = n.factorization p :=
  by
  rcases n.eq_zero_or_pos with (rfl | hn0); · simp [factorization]
  by_cases pp : p.prime; swap
  · rw [count_eq_zero_of_not_mem (mt prime_of_mem_factors pp)]
    simp [factorization, pp]
  simp only [factorization, coe_mk, pp, if_true]
  rw [← PartENat.natCast_inj, padicValNat_def' pp.ne_one hn0,
    UniqueFactorizationMonoid.multiplicity_eq_count_normalizedFactors pp hn0.ne']
  simp [factors_eq]
#align nat.factors_count_eq Nat.factors_count_eq
-/

/- warning: nat.factorization_eq_factors_multiset -> Nat.factorization_eq_factors_multiset is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Eq.{1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization n) (coeFn.{1, 1} (AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Multiset.hasAdd.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (fun (_x : AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Multiset.hasAdd.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) => (Multiset.{0} Nat) -> (Finsupp.{0, 0} Nat Nat Nat.hasZero)) (AddEquiv.hasCoeToFun.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Multiset.hasAdd.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (Multiset.toFinsupp.{0} Nat) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (List.{0} Nat) (Multiset.{0} Nat) (HasLiftT.mk.{1, 1} (List.{0} Nat) (Multiset.{0} Nat) (CoeTCₓ.coe.{1, 1} (List.{0} Nat) (Multiset.{0} Nat) (coeBase.{1, 1} (List.{0} Nat) (Multiset.{0} Nat) (Multiset.hasCoe.{0} Nat)))) (Nat.factors n)))
but is expected to have type
  forall (n : Nat), Eq.{1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization n) (FunLike.coe.{1, 1, 1} (AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Multiset.instAddMultiset.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (Multiset.{0} Nat) (fun (_x : Multiset.{0} Nat) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : Multiset.{0} Nat) => Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) _x) (AddHomClass.toFunLike.{0, 0, 0} (AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Multiset.instAddMultiset.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (AddZeroClass.toAdd.{0} (Multiset.{0} Nat) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat))))))) (AddZeroClass.toAdd.{0} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Finsupp.addZeroClass.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (AddMonoidHomClass.toAddHomClass.{0, 0, 0} (AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Multiset.instAddMultiset.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat)))))) (Finsupp.addZeroClass.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddEquivClass.instAddMonoidHomClass.{0, 0, 0} (AddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Multiset.instAddMultiset.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (AddMonoid.toAddZeroClass.{0} (Multiset.{0} Nat) (AddRightCancelMonoid.toAddMonoid.{0} (Multiset.{0} Nat) (AddCancelMonoid.toAddRightCancelMonoid.{0} (Multiset.{0} Nat) (AddCancelCommMonoid.toAddCancelMonoid.{0} (Multiset.{0} Nat) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} (Multiset.{0} Nat) (Multiset.instOrderedCancelAddCommMonoidMultiset.{0} Nat)))))) (Finsupp.addZeroClass.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (AddEquiv.instAddEquivClassAddEquiv.{0, 0} (Multiset.{0} Nat) (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Multiset.instAddMultiset.{0} Nat) (Finsupp.add.{0, 0} Nat Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))))) (Multiset.toFinsupp.{0} Nat) (Multiset.ofList.{0} Nat (Nat.factors n)))
Case conversion may be inaccurate. Consider using '#align nat.factorization_eq_factors_multiset Nat.factorization_eq_factors_multisetₓ'. -/
theorem factorization_eq_factors_multiset (n : ℕ) :
    n.factorization = (n.factors : Multiset ℕ).toFinsupp :=
  by
  ext p
  simp
#align nat.factorization_eq_factors_multiset Nat.factorization_eq_factors_multiset

/- warning: nat.multiplicity_eq_factorization -> Nat.multiplicity_eq_factorization is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {p : Nat}, (Nat.Prime p) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{1} PartENat (multiplicity.{0} Nat Nat.monoid (fun (a : Nat) (b : Nat) => Nat.decidableDvd a b) p n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat PartENat (HasLiftT.mk.{1, 1} Nat PartENat (CoeTCₓ.coe.{1, 1} Nat PartENat (Nat.castCoe.{0} PartENat (AddMonoidWithOne.toNatCast.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.addCommMonoidWithOne))))) (coeFn.{1, 1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (fun (_x : Finsupp.{0, 0} Nat Nat Nat.hasZero) => Nat -> Nat) (Finsupp.coeFun.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization n) p)))
but is expected to have type
  forall {n : Nat} {p : Nat}, (Nat.Prime p) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{1} PartENat (multiplicity.{0} Nat Nat.monoid (fun (a : Nat) (b : Nat) => Nat.decidable_dvd a b) p n) (Nat.cast.{0} PartENat (AddMonoidWithOne.toNatCast.{0} PartENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} PartENat PartENat.instAddCommMonoidWithOnePartENat)) (FunLike.coe.{1, 1, 1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) _x) (Finsupp.funLike.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization n) p)))
Case conversion may be inaccurate. Consider using '#align nat.multiplicity_eq_factorization Nat.multiplicity_eq_factorizationₓ'. -/
theorem multiplicity_eq_factorization {n p : ℕ} (pp : p.Prime) (hn : n ≠ 0) :
    multiplicity p n = n.factorization p := by
  simp [factorization, pp, padicValNat_def' pp.ne_one hn.bot_lt]
#align nat.multiplicity_eq_factorization Nat.multiplicity_eq_factorization

/-! ### Basic facts about factorization -/

#print Nat.factorization_prod_pow_eq_self /-
@[simp]
theorem factorization_prod_pow_eq_self {n : ℕ} (hn : n ≠ 0) : n.factorization.Prod pow = n :=
  by
  rw [factorization_eq_factors_multiset n]
  simp only [← prod_to_multiset, factorization, Multiset.coe_prod, Multiset.toFinsupp_toMultiset]
  exact prod_factors hn
#align nat.factorization_prod_pow_eq_self Nat.factorization_prod_pow_eq_self
-/

#print Nat.eq_of_factorization_eq /-
theorem eq_of_factorization_eq {a b : ℕ} (ha : a ≠ 0) (hb : b ≠ 0)
    (h : ∀ p : ℕ, a.factorization p = b.factorization p) : a = b :=
  eq_of_perm_factors ha hb (by simpa only [List.perm_iff_count, factors_count_eq] using h)
#align nat.eq_of_factorization_eq Nat.eq_of_factorization_eq
-/

#print Nat.factorization_inj /-
/-- Every nonzero natural number has a unique prime factorization -/
theorem factorization_inj : Set.InjOn factorization { x : ℕ | x ≠ 0 } := fun a ha b hb h =>
  eq_of_factorization_eq ha hb fun p => by simp [h]
#align nat.factorization_inj Nat.factorization_inj
-/

#print Nat.factorization_zero /-
@[simp]
theorem factorization_zero : factorization 0 = 0 := by simpa [factorization]
#align nat.factorization_zero Nat.factorization_zero
-/

#print Nat.factorization_one /-
@[simp]
theorem factorization_one : factorization 1 = 0 := by simpa [factorization]
#align nat.factorization_one Nat.factorization_one
-/

/- warning: nat.support_factorization -> Nat.support_factorization is a dubious translation:
lean 3 declaration is
  forall {n : Nat}, Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization n)) (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n))
but is expected to have type
  forall {n : Nat}, Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization n)) (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n))
Case conversion may be inaccurate. Consider using '#align nat.support_factorization Nat.support_factorizationₓ'. -/
/-- The support of `n.factorization` is exactly `n.factors.to_finset` -/
@[simp]
theorem support_factorization {n : ℕ} : n.factorization.support = n.factors.toFinset := by
  simp [factorization]
#align nat.support_factorization Nat.support_factorization

#print Nat.factor_iff_mem_factorization /-
theorem factor_iff_mem_factorization {n p : ℕ} : p ∈ n.factorization.support ↔ p ∈ n.factors := by
  simp only [support_factorization, List.mem_toFinset]
#align nat.factor_iff_mem_factorization Nat.factor_iff_mem_factorization
-/

#print Nat.prime_of_mem_factorization /-
theorem prime_of_mem_factorization {n p : ℕ} (hp : p ∈ n.factorization.support) : p.Prime :=
  prime_of_mem_factors (factor_iff_mem_factorization.mp hp)
#align nat.prime_of_mem_factorization Nat.prime_of_mem_factorization
-/

#print Nat.pos_of_mem_factorization /-
theorem pos_of_mem_factorization {n p : ℕ} (hp : p ∈ n.factorization.support) : 0 < p :=
  Prime.pos (prime_of_mem_factorization hp)
#align nat.pos_of_mem_factorization Nat.pos_of_mem_factorization
-/

#print Nat.le_of_mem_factorization /-
theorem le_of_mem_factorization {n p : ℕ} (h : p ∈ n.factorization.support) : p ≤ n :=
  le_of_mem_factors (factor_iff_mem_factorization.mp h)
#align nat.le_of_mem_factorization Nat.le_of_mem_factorization
-/

/-! ## Lemmas characterising when `n.factorization p = 0` -/

#print Nat.factorization_eq_zero_iff /-
theorem factorization_eq_zero_iff (n p : ℕ) : n.factorization p = 0 ↔ ¬p.Prime ∨ ¬p ∣ n ∨ n = 0 :=
  by
  rw [← not_mem_support_iff, support_factorization, mem_to_finset]
  rcases eq_or_ne n 0 with (rfl | hn)
  · simp
  · simp [hn, Nat.mem_factors, not_and_or]
#align nat.factorization_eq_zero_iff Nat.factorization_eq_zero_iff
-/

#print Nat.factorization_eq_zero_of_non_prime /-
@[simp]
theorem factorization_eq_zero_of_non_prime (n : ℕ) {p : ℕ} (hp : ¬p.Prime) :
    n.factorization p = 0 := by simp [factorization_eq_zero_iff, hp]
#align nat.factorization_eq_zero_of_non_prime Nat.factorization_eq_zero_of_non_prime
-/

#print Nat.factorization_eq_zero_of_not_dvd /-
theorem factorization_eq_zero_of_not_dvd {n p : ℕ} (h : ¬p ∣ n) : n.factorization p = 0 := by
  simp [factorization_eq_zero_iff, h]
#align nat.factorization_eq_zero_of_not_dvd Nat.factorization_eq_zero_of_not_dvd
-/

#print Nat.factorization_eq_zero_of_lt /-
theorem factorization_eq_zero_of_lt {n p : ℕ} (h : n < p) : n.factorization p = 0 :=
  Finsupp.not_mem_support_iff.mp (mt le_of_mem_factorization (not_le_of_lt h))
#align nat.factorization_eq_zero_of_lt Nat.factorization_eq_zero_of_lt
-/

#print Nat.factorization_zero_right /-
@[simp]
theorem factorization_zero_right (n : ℕ) : n.factorization 0 = 0 :=
  factorization_eq_zero_of_non_prime _ not_prime_zero
#align nat.factorization_zero_right Nat.factorization_zero_right
-/

#print Nat.factorization_one_right /-
@[simp]
theorem factorization_one_right (n : ℕ) : n.factorization 1 = 0 :=
  factorization_eq_zero_of_non_prime _ not_prime_one
#align nat.factorization_one_right Nat.factorization_one_right
-/

#print Nat.dvd_of_factorization_pos /-
theorem dvd_of_factorization_pos {n p : ℕ} (hn : n.factorization p ≠ 0) : p ∣ n :=
  dvd_of_mem_factors (factor_iff_mem_factorization.1 (mem_support_iff.2 hn))
#align nat.dvd_of_factorization_pos Nat.dvd_of_factorization_pos
-/

#print Nat.Prime.factorization_pos_of_dvd /-
theorem Prime.factorization_pos_of_dvd {n p : ℕ} (hp : p.Prime) (hn : n ≠ 0) (h : p ∣ n) :
    0 < n.factorization p := by rwa [← factors_count_eq, count_pos, mem_factors_iff_dvd hn hp]
#align nat.prime.factorization_pos_of_dvd Nat.Prime.factorization_pos_of_dvd
-/

#print Nat.factorization_eq_zero_of_remainder /-
theorem factorization_eq_zero_of_remainder {p r : ℕ} (i : ℕ) (hr : ¬p ∣ r) :
    (p * i + r).factorization p = 0 :=
  by
  apply factorization_eq_zero_of_not_dvd
  rwa [← Nat.dvd_add_iff_right (Dvd.intro i rfl)]
#align nat.factorization_eq_zero_of_remainder Nat.factorization_eq_zero_of_remainder
-/

#print Nat.factorization_eq_zero_iff_remainder /-
theorem factorization_eq_zero_iff_remainder {p r : ℕ} (i : ℕ) (pp : p.Prime) (hr0 : r ≠ 0) :
    ¬p ∣ r ↔ (p * i + r).factorization p = 0 :=
  by
  refine' ⟨factorization_eq_zero_of_remainder i, fun h => _⟩
  rw [factorization_eq_zero_iff] at h
  contrapose! h
  refine' ⟨pp, _, _⟩
  · rwa [← Nat.dvd_add_iff_right (Dvd.intro i rfl)]
  · contrapose! hr0
    exact (_root_.add_eq_zero_iff.mp hr0).2
#align nat.factorization_eq_zero_iff_remainder Nat.factorization_eq_zero_iff_remainder
-/

#print Nat.factorization_eq_zero_iff' /-
/-- The only numbers with empty prime factorization are `0` and `1` -/
theorem factorization_eq_zero_iff' (n : ℕ) : n.factorization = 0 ↔ n = 0 ∨ n = 1 :=
  by
  rw [factorization_eq_factors_multiset n]
  simp [factorization, AddEquiv.map_eq_zero_iff, Multiset.coe_eq_zero]
#align nat.factorization_eq_zero_iff' Nat.factorization_eq_zero_iff'
-/

/-! ## Lemmas about factorizations of products and powers -/

#print Nat.factorization_mul /-
/-- For nonzero `a` and `b`, the power of `p` in `a * b` is the sum of the powers in `a` and `b` -/
@[simp]
theorem factorization_mul {a b : ℕ} (ha : a ≠ 0) (hb : b ≠ 0) :
    (a * b).factorization = a.factorization + b.factorization :=
  by
  ext p
  simp only [add_apply, ← factors_count_eq, perm_iff_count.mp (perm_factors_mul ha hb) p,
    count_append]
#align nat.factorization_mul Nat.factorization_mul
-/

/- warning: nat.factorization_mul_support -> Nat.factorization_mul_support is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) a b))) (Union.union.{0} (Finset.{0} Nat) (Finset.hasUnion.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b)) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization b))))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) a b))) (Union.union.{0} (Finset.{0} Nat) (Finset.instUnionFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b)) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization b))))
Case conversion may be inaccurate. Consider using '#align nat.factorization_mul_support Nat.factorization_mul_supportₓ'. -/
theorem factorization_mul_support {a b : ℕ} (ha : a ≠ 0) (hb : b ≠ 0) :
    (a * b).factorization.support = a.factorization.support ∪ b.factorization.support :=
  by
  ext q
  simp only [Finset.mem_union, factor_iff_mem_factorization]
  exact mem_factors_mul ha hb
#align nat.factorization_mul_support Nat.factorization_mul_support

/- warning: nat.prod_factorization_eq_prod_factors -> Nat.prod_factorization_eq_prod_factors is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), Eq.{succ u1} β (Finsupp.prod.{0, 0, u1} Nat Nat β Nat.hasZero _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f p)) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => f p))
but is expected to have type
  forall {n : Nat} {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), Eq.{succ u1} β (Finsupp.prod.{0, 0, u1} Nat Nat β (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f p)) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => f p))
Case conversion may be inaccurate. Consider using '#align nat.prod_factorization_eq_prod_factors Nat.prod_factorization_eq_prod_factorsₓ'. -/
/-- If a product over `n.factorization` doesn't use the multiplicities of the prime factors
then it's equal to the corresponding product over `n.factors.to_finset` -/
theorem prod_factorization_eq_prod_factors {n : ℕ} {β : Type _} [CommMonoid β] (f : ℕ → β) :
    (n.factorization.Prod fun p k => f p) = ∏ p in n.factors.toFinset, f p :=
  by
  apply prod_congr support_factorization
  simp
#align nat.prod_factorization_eq_prod_factors Nat.prod_factorization_eq_prod_factors

#print Nat.factorization_prod /-
/-- For any `p : ℕ` and any function `g : α → ℕ` that's non-zero on `S : finset α`,
the power of `p` in `S.prod g` equals the sum over `x ∈ S` of the powers of `p` in `g x`.
Generalises `factorization_mul`, which is the special case where `S.card = 2` and `g = id`. -/
theorem factorization_prod {α : Type _} {S : Finset α} {g : α → ℕ} (hS : ∀ x ∈ S, g x ≠ 0) :
    (S.Prod g).factorization = S.Sum fun x => (g x).factorization := by
  classical
    ext p
    apply Finset.induction_on' S
    · simp
    · intro x T hxS hTS hxT IH
      have hT : T.prod g ≠ 0 := prod_ne_zero_iff.mpr fun x hx => hS x (hTS hx)
      simp [prod_insert hxT, sum_insert hxT, ← IH, factorization_mul (hS x hxS) hT]
#align nat.factorization_prod Nat.factorization_prod
-/

#print Nat.factorization_pow /-
/-- For any `p`, the power of `p` in `n^k` is `k` times the power in `n` -/
@[simp]
theorem factorization_pow (n k : ℕ) : factorization (n ^ k) = k • n.factorization :=
  by
  induction' k with k ih; · simp
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  rw [pow_succ, factorization_mul hn (pow_ne_zero _ hn), ih, succ_eq_one_add, add_smul, one_smul]
#align nat.factorization_pow Nat.factorization_pow
-/

/-! ## Lemmas about factorizations of primes and prime powers -/

#print Nat.Prime.factorization /-
/-- The only prime factor of prime `p` is `p` itself, with multiplicity `1` -/
@[simp]
theorem Prime.factorization {p : ℕ} (hp : Prime p) : p.factorization = single p 1 :=
  by
  ext q
  rw [← factors_count_eq, factors_prime hp, single_apply, count_singleton', if_congr eq_comm] <;>
    rfl
#align nat.prime.factorization Nat.Prime.factorization
-/

#print Nat.Prime.factorization_self /-
/-- The multiplicity of prime `p` in `p` is `1` -/
@[simp]
theorem Prime.factorization_self {p : ℕ} (hp : Prime p) : p.factorization p = 1 := by simp [hp]
#align nat.prime.factorization_self Nat.Prime.factorization_self
-/

#print Nat.Prime.factorization_pow /-
/-- For prime `p` the only prime factor of `p^k` is `p` with multiplicity `k` -/
theorem Prime.factorization_pow {p k : ℕ} (hp : Prime p) : factorization (p ^ k) = single p k := by
  simp [hp]
#align nat.prime.factorization_pow Nat.Prime.factorization_pow
-/

#print Nat.eq_pow_of_factorization_eq_single /-
/-- If the factorization of `n` contains just one number `p` then `n` is a power of `p` -/
theorem eq_pow_of_factorization_eq_single {n p k : ℕ} (hn : n ≠ 0)
    (h : n.factorization = Finsupp.single p k) : n = p ^ k :=
  by
  rw [← Nat.factorization_prod_pow_eq_self hn, h]
  simp
#align nat.eq_pow_of_factorization_eq_single Nat.eq_pow_of_factorization_eq_single
-/

#print Nat.Prime.eq_of_factorization_pos /-
/-- The only prime factor of prime `p` is `p` itself. -/
theorem Prime.eq_of_factorization_pos {p q : ℕ} (hp : Prime p) (h : p.factorization q ≠ 0) :
    p = q := by simpa [hp.factorization, single_apply] using h
#align nat.prime.eq_of_factorization_pos Nat.Prime.eq_of_factorization_pos
-/

/-! ### Equivalence between `ℕ+` and `ℕ →₀ ℕ` with support in the primes. -/

#print Nat.prod_pow_factorization_eq_self /-
/-- Any finsupp `f : ℕ →₀ ℕ` whose support is in the primes is equal to the factorization of
the product `∏ (a : ℕ) in f.support, a ^ f a`. -/
theorem prod_pow_factorization_eq_self {f : ℕ →₀ ℕ} (hf : ∀ p : ℕ, p ∈ f.support → Prime p) :
    (f.Prod pow).factorization = f :=
  by
  have h : ∀ x : ℕ, x ∈ f.support → x ^ f x ≠ 0 := fun p hp =>
    pow_ne_zero _ (Prime.ne_zero (hf p hp))
  simp only [Finsupp.prod, factorization_prod h]
  nth_rw_rhs 1 [(sum_single f).symm]
  exact sum_congr rfl fun p hp => prime.factorization_pow (hf p hp)
#align nat.prod_pow_factorization_eq_self Nat.prod_pow_factorization_eq_self
-/

#print Nat.eq_factorization_iff /-
theorem eq_factorization_iff {n : ℕ} {f : ℕ →₀ ℕ} (hn : n ≠ 0) (hf : ∀ p ∈ f.support, Prime p) :
    f = n.factorization ↔ f.Prod pow = n :=
  ⟨fun h => by rw [h, factorization_prod_pow_eq_self hn], fun h => by
    rw [← h, prod_pow_factorization_eq_self hf]⟩
#align nat.eq_factorization_iff Nat.eq_factorization_iff
-/

#print Nat.factorizationEquiv /-
/-- The equiv between `ℕ+` and `ℕ →₀ ℕ` with support in the primes. -/
def factorizationEquiv : ℕ+ ≃ { f : ℕ →₀ ℕ | ∀ p ∈ f.support, Prime p }
    where
  toFun := fun ⟨n, hn⟩ => ⟨n.factorization, fun _ => prime_of_mem_factorization⟩
  invFun := fun ⟨f, hf⟩ =>
    ⟨f.Prod pow, prod_pow_pos_of_zero_not_mem_support fun H => not_prime_zero (hf 0 H)⟩
  left_inv := fun ⟨x, hx⟩ => Subtype.ext <| factorization_prod_pow_eq_self hx.Ne.symm
  right_inv := fun ⟨f, hf⟩ => Subtype.ext <| prod_pow_factorization_eq_self hf
#align nat.factorization_equiv Nat.factorizationEquiv
-/

#print Nat.factorizationEquiv_apply /-
theorem factorizationEquiv_apply (n : ℕ+) : (factorizationEquiv n).1 = n.1.factorization :=
  by
  cases n
  rfl
#align nat.factorization_equiv_apply Nat.factorizationEquiv_apply
-/

#print Nat.factorizationEquiv_inv_apply /-
theorem factorizationEquiv_inv_apply {f : ℕ →₀ ℕ} (hf : ∀ p ∈ f.support, Prime p) :
    (factorizationEquiv.symm ⟨f, hf⟩).1 = f.Prod pow :=
  rfl
#align nat.factorization_equiv_inv_apply Nat.factorizationEquiv_inv_apply
-/

/-! ### Generalisation of the "even part" and "odd part" of a natural number

We introduce the notations `ord_proj[p] n` for the largest power of the prime `p` that
divides `n` and `ord_compl[p] n` for the complementary part. The `ord` naming comes from
the $p$-adic order/valuation of a number, and `proj` and `compl` are for the projection and
complementary projection. The term `n.factorization p` is the $p$-adic order itself.
For example, `ord_proj[2] n` is the even part of `n` and `ord_compl[2] n` is the odd part. -/

-- mathport name: «exprord_proj[ ] »
notation "ord_proj[" p "] " n:arg => p ^ Nat.factorization n p

-- mathport name: «exprord_compl[ ] »
notation "ord_compl[" p "] " n:arg => n / ord_proj[p] n

#print Nat.ord_proj_of_not_prime /-
@[simp]
theorem ord_proj_of_not_prime (n p : ℕ) (hp : ¬p.Prime) : ord_proj[p] n = 1 := by
  simp [factorization_eq_zero_of_non_prime n hp]
#align nat.ord_proj_of_not_prime Nat.ord_proj_of_not_prime
-/

#print Nat.ord_compl_of_not_prime /-
@[simp]
theorem ord_compl_of_not_prime (n p : ℕ) (hp : ¬p.Prime) : ord_compl[p] n = n := by
  simp [factorization_eq_zero_of_non_prime n hp]
#align nat.ord_compl_of_not_prime Nat.ord_compl_of_not_prime
-/

#print Nat.ord_proj_dvd /-
theorem ord_proj_dvd (n p : ℕ) : ord_proj[p] n ∣ n :=
  by
  by_cases hp : p.prime; swap; · simp [hp]
  rw [← factors_count_eq]
  apply dvd_of_factors_subperm (pow_ne_zero _ hp.ne_zero)
  rw [hp.factors_pow, List.subperm_ext_iff]
  intro q hq
  simp [List.eq_of_mem_replicate hq]
#align nat.ord_proj_dvd Nat.ord_proj_dvd
-/

#print Nat.ord_compl_dvd /-
theorem ord_compl_dvd (n p : ℕ) : ord_compl[p] n ∣ n :=
  div_dvd_of_dvd (ord_proj_dvd n p)
#align nat.ord_compl_dvd Nat.ord_compl_dvd
-/

#print Nat.ord_proj_pos /-
theorem ord_proj_pos (n p : ℕ) : 0 < ord_proj[p] n :=
  by
  by_cases pp : p.prime
  · simp [pow_pos pp.pos]
  · simp [pp]
#align nat.ord_proj_pos Nat.ord_proj_pos
-/

#print Nat.ord_proj_le /-
theorem ord_proj_le {n : ℕ} (p : ℕ) (hn : n ≠ 0) : ord_proj[p] n ≤ n :=
  le_of_dvd hn.bot_lt (Nat.ord_proj_dvd n p)
#align nat.ord_proj_le Nat.ord_proj_le
-/

#print Nat.ord_compl_pos /-
theorem ord_compl_pos {n : ℕ} (p : ℕ) (hn : n ≠ 0) : 0 < ord_compl[p] n :=
  by
  cases' em' p.prime with pp pp
  · simpa [Nat.factorization_eq_zero_of_non_prime n pp] using hn.bot_lt
  exact Nat.div_pos (ord_proj_le p hn) (ord_proj_pos n p)
#align nat.ord_compl_pos Nat.ord_compl_pos
-/

#print Nat.ord_compl_le /-
theorem ord_compl_le (n p : ℕ) : ord_compl[p] n ≤ n :=
  Nat.div_le_self _ _
#align nat.ord_compl_le Nat.ord_compl_le
-/

#print Nat.ord_proj_mul_ord_compl_eq_self /-
theorem ord_proj_mul_ord_compl_eq_self (n p : ℕ) : ord_proj[p] n * ord_compl[p] n = n :=
  Nat.mul_div_cancel' (ord_proj_dvd n p)
#align nat.ord_proj_mul_ord_compl_eq_self Nat.ord_proj_mul_ord_compl_eq_self
-/

#print Nat.ord_proj_mul /-
theorem ord_proj_mul {a b : ℕ} (p : ℕ) (ha : a ≠ 0) (hb : b ≠ 0) :
    ord_proj[p] (a * b) = ord_proj[p] a * ord_proj[p] b := by
  simp [factorization_mul ha hb, pow_add]
#align nat.ord_proj_mul Nat.ord_proj_mul
-/

#print Nat.ord_compl_mul /-
theorem ord_compl_mul (a b p : ℕ) : ord_compl[p] (a * b) = ord_compl[p] a * ord_compl[p] b :=
  by
  rcases eq_or_ne a 0 with (rfl | ha); · simp
  rcases eq_or_ne b 0 with (rfl | hb); · simp
  simp only [ord_proj_mul p ha hb]
  rw [mul_div_mul_comm_of_dvd_dvd (ord_proj_dvd a p) (ord_proj_dvd b p)]
#align nat.ord_compl_mul Nat.ord_compl_mul
-/

/-! ### Factorization and divisibility -/

#print Nat.dvd_of_mem_factorization /-
theorem dvd_of_mem_factorization {n p : ℕ} (h : p ∈ n.factorization.support) : p ∣ n :=
  by
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  simp [← mem_factors_iff_dvd hn (prime_of_mem_factorization h), factor_iff_mem_factorization.mp h]
#align nat.dvd_of_mem_factorization Nat.dvd_of_mem_factorization
-/

#print Nat.factorization_lt /-
/-- A crude upper bound on `n.factorization p` -/
theorem factorization_lt {n : ℕ} (p : ℕ) (hn : n ≠ 0) : n.factorization p < n :=
  by
  by_cases pp : p.prime; swap;
  · simp [factorization_eq_zero_of_non_prime n pp]
    exact hn.bot_lt
  rw [← pow_lt_iff_lt_right pp.two_le]
  apply lt_of_le_of_lt (ord_proj_le p hn)
  exact lt_of_lt_of_le (lt_two_pow n) (pow_le_pow_of_le_left (by linarith) pp.two_le n)
#align nat.factorization_lt Nat.factorization_lt
-/

#print Nat.factorization_le_of_le_pow /-
/-- An upper bound on `n.factorization p` -/
theorem factorization_le_of_le_pow {n p b : ℕ} (hb : n ≤ p ^ b) : n.factorization p ≤ b :=
  by
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  by_cases pp : p.prime
  · exact (pow_le_iff_le_right pp.two_le).1 (le_trans (ord_proj_le p hn) hb)
  · simp [factorization_eq_zero_of_non_prime n pp]
#align nat.factorization_le_of_le_pow Nat.factorization_le_of_le_pow
-/

#print Nat.factorization_le_iff_dvd /-
theorem factorization_le_iff_dvd {d n : ℕ} (hd : d ≠ 0) (hn : n ≠ 0) :
    d.factorization ≤ n.factorization ↔ d ∣ n :=
  by
  constructor
  · intro hdn
    set K := n.factorization - d.factorization with hK
    use K.prod pow
    rw [← factorization_prod_pow_eq_self hn, ← factorization_prod_pow_eq_self hd, ←
      Finsupp.prod_add_index' pow_zero pow_add, hK, add_tsub_cancel_of_le hdn]
  · rintro ⟨c, rfl⟩
    rw [factorization_mul hd (right_ne_zero_of_mul hn)]
    simp
#align nat.factorization_le_iff_dvd Nat.factorization_le_iff_dvd
-/

#print Nat.factorization_prime_le_iff_dvd /-
theorem factorization_prime_le_iff_dvd {d n : ℕ} (hd : d ≠ 0) (hn : n ≠ 0) :
    (∀ p : ℕ, p.Prime → d.factorization p ≤ n.factorization p) ↔ d ∣ n :=
  by
  rw [← factorization_le_iff_dvd hd hn]
  refine' ⟨fun h p => (em p.Prime).elim (h p) fun hp => _, fun h p _ => h p⟩
  simp_rw [factorization_eq_zero_of_non_prime _ hp]
#align nat.factorization_prime_le_iff_dvd Nat.factorization_prime_le_iff_dvd
-/

#print Nat.pow_succ_factorization_not_dvd /-
theorem pow_succ_factorization_not_dvd {n p : ℕ} (hn : n ≠ 0) (hp : p.Prime) :
    ¬p ^ (n.factorization p + 1) ∣ n := by
  intro h
  rw [← factorization_le_iff_dvd (pow_pos hp.pos _).ne' hn] at h
  simpa [hp.factorization] using h p
#align nat.pow_succ_factorization_not_dvd Nat.pow_succ_factorization_not_dvd
-/

#print Nat.factorization_le_factorization_mul_left /-
theorem factorization_le_factorization_mul_left {a b : ℕ} (hb : b ≠ 0) :
    a.factorization ≤ (a * b).factorization :=
  by
  rcases eq_or_ne a 0 with (rfl | ha); · simp
  rw [factorization_le_iff_dvd ha <| mul_ne_zero ha hb]
  exact Dvd.intro b rfl
#align nat.factorization_le_factorization_mul_left Nat.factorization_le_factorization_mul_left
-/

#print Nat.factorization_le_factorization_mul_right /-
theorem factorization_le_factorization_mul_right {a b : ℕ} (ha : a ≠ 0) :
    b.factorization ≤ (a * b).factorization :=
  by
  rw [mul_comm]
  apply factorization_le_factorization_mul_left ha
#align nat.factorization_le_factorization_mul_right Nat.factorization_le_factorization_mul_right
-/

#print Nat.Prime.pow_dvd_iff_le_factorization /-
theorem Prime.pow_dvd_iff_le_factorization {p k n : ℕ} (pp : Prime p) (hn : n ≠ 0) :
    p ^ k ∣ n ↔ k ≤ n.factorization p := by
  rw [← factorization_le_iff_dvd (pow_pos pp.pos k).ne' hn, pp.factorization_pow, single_le_iff]
#align nat.prime.pow_dvd_iff_le_factorization Nat.Prime.pow_dvd_iff_le_factorization
-/

#print Nat.Prime.pow_dvd_iff_dvd_ord_proj /-
theorem Prime.pow_dvd_iff_dvd_ord_proj {p k n : ℕ} (pp : Prime p) (hn : n ≠ 0) :
    p ^ k ∣ n ↔ p ^ k ∣ ord_proj[p] n := by
  rw [pow_dvd_pow_iff_le_right pp.one_lt, pp.pow_dvd_iff_le_factorization hn]
#align nat.prime.pow_dvd_iff_dvd_ord_proj Nat.Prime.pow_dvd_iff_dvd_ord_proj
-/

#print Nat.Prime.dvd_iff_one_le_factorization /-
theorem Prime.dvd_iff_one_le_factorization {p n : ℕ} (pp : Prime p) (hn : n ≠ 0) :
    p ∣ n ↔ 1 ≤ n.factorization p :=
  Iff.trans (by simp) (pp.pow_dvd_iff_le_factorization hn)
#align nat.prime.dvd_iff_one_le_factorization Nat.Prime.dvd_iff_one_le_factorization
-/

#print Nat.exists_factorization_lt_of_lt /-
theorem exists_factorization_lt_of_lt {a b : ℕ} (ha : a ≠ 0) (hab : a < b) :
    ∃ p : ℕ, a.factorization p < b.factorization p :=
  by
  have hb : b ≠ 0 := (ha.bot_lt.trans hab).ne'
  contrapose! hab
  rw [← Finsupp.le_def, factorization_le_iff_dvd hb ha] at hab
  exact le_of_dvd ha.bot_lt hab
#align nat.exists_factorization_lt_of_lt Nat.exists_factorization_lt_of_lt
-/

#print Nat.factorization_div /-
@[simp]
theorem factorization_div {d n : ℕ} (h : d ∣ n) :
    (n / d).factorization = n.factorization - d.factorization :=
  by
  rcases eq_or_ne d 0 with (rfl | hd); · simp [zero_dvd_iff.mp h]
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  apply add_left_injective d.factorization
  simp only
  rw [tsub_add_cancel_of_le <| (Nat.factorization_le_iff_dvd hd hn).mpr h, ←
    Nat.factorization_mul (Nat.div_pos (Nat.le_of_dvd hn.bot_lt h) hd.bot_lt).ne' hd,
    Nat.div_mul_cancel h]
#align nat.factorization_div Nat.factorization_div
-/

#print Nat.dvd_ord_proj_of_dvd /-
theorem dvd_ord_proj_of_dvd {n p : ℕ} (hn : n ≠ 0) (pp : p.Prime) (h : p ∣ n) : p ∣ ord_proj[p] n :=
  dvd_pow_self p (Prime.factorization_pos_of_dvd pp hn h).ne'
#align nat.dvd_ord_proj_of_dvd Nat.dvd_ord_proj_of_dvd
-/

#print Nat.not_dvd_ord_compl /-
theorem not_dvd_ord_compl {n p : ℕ} (hp : Prime p) (hn : n ≠ 0) : ¬p ∣ ord_compl[p] n :=
  by
  rw [Nat.Prime.dvd_iff_one_le_factorization hp (ord_compl_pos p hn).ne']
  rw [Nat.factorization_div (Nat.ord_proj_dvd n p)]
  simp [hp.factorization]
#align nat.not_dvd_ord_compl Nat.not_dvd_ord_compl
-/

#print Nat.coprime_ord_compl /-
theorem coprime_ord_compl {n p : ℕ} (hp : Prime p) (hn : n ≠ 0) : coprime p (ord_compl[p] n) :=
  (or_iff_left (not_dvd_ord_compl hp hn)).mp <| coprime_or_dvd_of_prime hp _
#align nat.coprime_ord_compl Nat.coprime_ord_compl
-/

#print Nat.factorization_ord_compl /-
theorem factorization_ord_compl (n p : ℕ) :
    (ord_compl[p] n).factorization = n.factorization.eraseₓ p :=
  by
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  by_cases pp : p.prime; swap; · simp [pp]
  ext q
  rcases eq_or_ne q p with (rfl | hqp)
  · simp only [Finsupp.erase_same, factorization_eq_zero_iff, not_dvd_ord_compl pp hn]
    simp
  · rw [Finsupp.erase_ne hqp, factorization_div (ord_proj_dvd n p)]
    simp [pp.factorization, hqp.symm]
#align nat.factorization_ord_compl Nat.factorization_ord_compl
-/

#print Nat.dvd_ord_compl_of_dvd_not_dvd /-
-- `ord_compl[p] n` is the largest divisor of `n` not divisible by `p`.
theorem dvd_ord_compl_of_dvd_not_dvd {p d n : ℕ} (hdn : d ∣ n) (hpd : ¬p ∣ d) :
    d ∣ ord_compl[p] n := by
  rcases eq_or_ne n 0 with (rfl | hn0); · simp
  rcases eq_or_ne d 0 with (rfl | hd0);
  · simp at hpd
    cases hpd
  rw [← factorization_le_iff_dvd hd0 (ord_compl_pos p hn0).ne', factorization_ord_compl]
  intro q
  rcases eq_or_ne q p with (rfl | hqp)
  · simp [factorization_eq_zero_iff, hpd]
  · simp [hqp, (factorization_le_iff_dvd hd0 hn0).2 hdn q]
#align nat.dvd_ord_compl_of_dvd_not_dvd Nat.dvd_ord_compl_of_dvd_not_dvd
-/

#print Nat.exists_eq_pow_mul_and_not_dvd /-
/-- If `n` is a nonzero natural number and `p ≠ 1`, then there are natural numbers `e`
and `n'` such that `n'` is not divisible by `p` and `n = p^e * n'`. -/
theorem exists_eq_pow_mul_and_not_dvd {n : ℕ} (hn : n ≠ 0) (p : ℕ) (hp : p ≠ 1) :
    ∃ e n' : ℕ, ¬p ∣ n' ∧ n = p ^ e * n' :=
  let ⟨a', h₁, h₂⟩ :=
    multiplicity.exists_eq_pow_mul_and_not_dvd
      (multiplicity.finite_nat_iff.mpr ⟨hp, Nat.pos_of_ne_zero hn⟩)
  ⟨_, a', h₂, h₁⟩
#align nat.exists_eq_pow_mul_and_not_dvd Nat.exists_eq_pow_mul_and_not_dvd
-/

#print Nat.dvd_iff_div_factorization_eq_tsub /-
theorem dvd_iff_div_factorization_eq_tsub {d n : ℕ} (hd : d ≠ 0) (hdn : d ≤ n) :
    d ∣ n ↔ (n / d).factorization = n.factorization - d.factorization :=
  by
  refine' ⟨factorization_div, _⟩
  rcases eq_or_lt_of_le hdn with (rfl | hd_lt_n); · simp
  have h1 : n / d ≠ 0 := fun H => Nat.lt_asymm hd_lt_n ((Nat.div_eq_zero_iff hd.bot_lt).mp H)
  intro h
  rw [dvd_iff_le_div_mul n d]
  by_contra h2
  cases' exists_factorization_lt_of_lt (mul_ne_zero h1 hd) (not_le.mp h2) with p hp
  rwa [factorization_mul h1 hd, add_apply, ← lt_tsub_iff_right, h, tsub_apply, lt_self_iff_false] at
    hp
#align nat.dvd_iff_div_factorization_eq_tsub Nat.dvd_iff_div_factorization_eq_tsub
-/

#print Nat.ord_proj_dvd_ord_proj_of_dvd /-
theorem ord_proj_dvd_ord_proj_of_dvd {a b : ℕ} (hb0 : b ≠ 0) (hab : a ∣ b) (p : ℕ) :
    ord_proj[p] a ∣ ord_proj[p] b :=
  by
  rcases em' p.prime with (pp | pp); · simp [pp]
  rcases eq_or_ne a 0 with (rfl | ha0); · simp
  rw [pow_dvd_pow_iff_le_right pp.one_lt]
  exact (factorization_le_iff_dvd ha0 hb0).2 hab p
#align nat.ord_proj_dvd_ord_proj_of_dvd Nat.ord_proj_dvd_ord_proj_of_dvd
-/

#print Nat.ord_proj_dvd_ord_proj_iff_dvd /-
theorem ord_proj_dvd_ord_proj_iff_dvd {a b : ℕ} (ha0 : a ≠ 0) (hb0 : b ≠ 0) :
    (∀ p : ℕ, ord_proj[p] a ∣ ord_proj[p] b) ↔ a ∣ b :=
  by
  refine' ⟨fun h => _, fun hab p => ord_proj_dvd_ord_proj_of_dvd hb0 hab p⟩
  rw [← factorization_le_iff_dvd ha0 hb0]
  intro q
  rcases le_or_lt q 1 with (hq_le | hq1); · interval_cases <;> simp
  exact (pow_dvd_pow_iff_le_right hq1).1 (h q)
#align nat.ord_proj_dvd_ord_proj_iff_dvd Nat.ord_proj_dvd_ord_proj_iff_dvd
-/

#print Nat.ord_compl_dvd_ord_compl_of_dvd /-
theorem ord_compl_dvd_ord_compl_of_dvd {a b : ℕ} (hab : a ∣ b) (p : ℕ) :
    ord_compl[p] a ∣ ord_compl[p] b :=
  by
  rcases em' p.prime with (pp | pp); · simp [pp, hab]
  rcases eq_or_ne b 0 with (rfl | hb0); · simp
  rcases eq_or_ne a 0 with (rfl | ha0); · cases hb0 (zero_dvd_iff.1 hab)
  have ha := (Nat.div_pos (ord_proj_le p ha0) (ord_proj_pos a p)).ne'
  have hb := (Nat.div_pos (ord_proj_le p hb0) (ord_proj_pos b p)).ne'
  rw [← factorization_le_iff_dvd ha hb, factorization_ord_compl a p, factorization_ord_compl b p]
  intro q
  rcases eq_or_ne q p with (rfl | hqp); · simp
  simp_rw [erase_ne hqp]
  exact (factorization_le_iff_dvd ha0 hb0).2 hab q
#align nat.ord_compl_dvd_ord_compl_of_dvd Nat.ord_compl_dvd_ord_compl_of_dvd
-/

#print Nat.ord_compl_dvd_ord_compl_iff_dvd /-
theorem ord_compl_dvd_ord_compl_iff_dvd (a b : ℕ) :
    (∀ p : ℕ, ord_compl[p] a ∣ ord_compl[p] b) ↔ a ∣ b :=
  by
  refine' ⟨fun h => _, fun hab p => ord_compl_dvd_ord_compl_of_dvd hab p⟩
  rcases eq_or_ne b 0 with (rfl | hb0); · simp
  by_cases pa : a.prime; swap; · simpa [pa] using h a
  by_cases pb : b.prime; swap; · simpa [pb] using h b
  rw [prime_dvd_prime_iff_eq pa pb]
  by_contra hab
  apply pa.ne_one
  rw [← Nat.dvd_one, ← Nat.mul_dvd_mul_iff_left hb0.bot_lt, mul_one]
  simpa [prime.factorization_self pb, prime.factorization pa, hab] using h b
#align nat.ord_compl_dvd_ord_compl_iff_dvd Nat.ord_compl_dvd_ord_compl_iff_dvd
-/

#print Nat.dvd_iff_prime_pow_dvd_dvd /-
theorem dvd_iff_prime_pow_dvd_dvd (n d : ℕ) : d ∣ n ↔ ∀ p k : ℕ, Prime p → p ^ k ∣ d → p ^ k ∣ n :=
  by
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  rcases eq_or_ne d 0 with (rfl | hd)
  · simp only [zero_dvd_iff, hn, false_iff_iff, not_forall]
    exact ⟨2, n, prime_two, dvd_zero _, mt (le_of_dvd hn.bot_lt) (lt_two_pow n).not_le⟩
  refine' ⟨fun h p k _ hpkd => dvd_trans hpkd h, _⟩
  rw [← factorization_prime_le_iff_dvd hd hn]
  intro h p pp
  simp_rw [← pp.pow_dvd_iff_le_factorization hn]
  exact h p _ pp (ord_proj_dvd _ _)
#align nat.dvd_iff_prime_pow_dvd_dvd Nat.dvd_iff_prime_pow_dvd_dvd
-/

/- warning: nat.prod_prime_factors_dvd -> Nat.prod_prime_factors_dvd is a dubious translation:
lean 3 declaration is
  forall (n : Nat), Dvd.Dvd.{0} Nat Nat.hasDvd (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) (fun (p : Nat) => p)) n
but is expected to have type
  forall (n : Nat), Dvd.dvd.{0} Nat Nat.instDvdNat (Finset.prod.{0, 0} Nat Nat Nat.commMonoid (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) (fun (p : Nat) => p)) n
Case conversion may be inaccurate. Consider using '#align nat.prod_prime_factors_dvd Nat.prod_prime_factors_dvdₓ'. -/
theorem prod_prime_factors_dvd (n : ℕ) : (∏ p : ℕ in n.factors.toFinset, p) ∣ n :=
  by
  by_cases hn : n = 0;
  · subst hn
    simp
  simpa [prod_factors hn] using Multiset.toFinset_prod_dvd_prod (n.factors : Multiset ℕ)
#align nat.prod_prime_factors_dvd Nat.prod_prime_factors_dvd

/- warning: nat.factorization_gcd -> Nat.factorization_gcd is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization (Nat.gcd a b)) (Inf.inf.{0} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (SemilatticeInf.toHasInf.{0} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Finsupp.semilatticeInf.{0, 0} Nat Nat Nat.hasZero (Lattice.toSemilatticeInf.{0} Nat Nat.lattice))) (Nat.factorization a) (Nat.factorization b)))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization (Nat.gcd a b)) (Inf.inf.{0} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Lattice.toInf.{0} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Finsupp.lattice.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) Nat.instLatticeNat)) (Nat.factorization a) (Nat.factorization b)))
Case conversion may be inaccurate. Consider using '#align nat.factorization_gcd Nat.factorization_gcdₓ'. -/
theorem factorization_gcd {a b : ℕ} (ha_pos : a ≠ 0) (hb_pos : b ≠ 0) :
    (gcd a b).factorization = a.factorization ⊓ b.factorization :=
  by
  let dfac := a.factorization ⊓ b.factorization
  let d := dfac.prod pow
  have dfac_prime : ∀ p : ℕ, p ∈ dfac.support → Prime p :=
    by
    intro p hp
    have : p ∈ a.factors ∧ p ∈ b.factors := by simpa using hp
    exact prime_of_mem_factors this.1
  have h1 : d.factorization = dfac := prod_pow_factorization_eq_self dfac_prime
  have hd_pos : d ≠ 0 := (factorization_equiv.inv_fun ⟨dfac, dfac_prime⟩).2.Ne.symm
  suffices d = gcd a b by rwa [← this]
  apply gcd_greatest
  · rw [← factorization_le_iff_dvd hd_pos ha_pos, h1]
    exact inf_le_left
  · rw [← factorization_le_iff_dvd hd_pos hb_pos, h1]
    exact inf_le_right
  · intro e hea heb
    rcases Decidable.eq_or_ne e 0 with (rfl | he_pos)
    · simp only [zero_dvd_iff] at hea
      contradiction
    have hea' := (factorization_le_iff_dvd he_pos ha_pos).mpr hea
    have heb' := (factorization_le_iff_dvd he_pos hb_pos).mpr heb
    simp [← factorization_le_iff_dvd he_pos hd_pos, h1, hea', heb']
#align nat.factorization_gcd Nat.factorization_gcd

/- warning: nat.factorization_lcm -> Nat.factorization_lcm is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization (Nat.lcm a b)) (Sup.sup.{0} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (SemilatticeSup.toHasSup.{0} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (Finsupp.semilatticeSup.{0, 0} Nat Nat Nat.hasZero (CanonicallyLinearOrderedAddMonoid.semilatticeSup.{0} Nat Nat.canonicallyLinearOrderedAddMonoid))) (Nat.factorization a) (Nat.factorization b)))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Ne.{1} Nat a (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Ne.{1} Nat b (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization (Nat.lcm a b)) (Sup.sup.{0} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (SemilatticeSup.toSup.{0} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Finsupp.semilatticeSup.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Lattice.toSemilatticeSup.{0} Nat Nat.instLatticeNat))) (Nat.factorization a) (Nat.factorization b)))
Case conversion may be inaccurate. Consider using '#align nat.factorization_lcm Nat.factorization_lcmₓ'. -/
theorem factorization_lcm {a b : ℕ} (ha : a ≠ 0) (hb : b ≠ 0) :
    (a.lcm b).factorization = a.factorization ⊔ b.factorization :=
  by
  rw [← add_right_inj (a.gcd b).factorization, ←
    factorization_mul (mt gcd_eq_zero_iff.1 fun h => ha h.1) (lcm_ne_zero ha hb), gcd_mul_lcm,
    factorization_gcd ha hb, factorization_mul ha hb]
  ext1; exact (min_add_max _ _).symm
#align nat.factorization_lcm Nat.factorization_lcm

/- warning: nat.prod_factors_gcd_mul_prod_factors_mul -> Nat.prod_factors_gcd_mul_prod_factors_mul is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (m : Nat) (n : Nat) (f : Nat -> β), Eq.{succ u1} β (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toHasMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors (Nat.gcd m n))) f) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) m n))) f)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toHasMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors m)) f) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b) (Nat.factors n)) f))
but is expected to have type
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (m : Nat) (n : Nat) (f : Nat -> β), Eq.{succ u1} β (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors (Nat.gcd m n))) f) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) m n))) f)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors m)) f) (Finset.prod.{u1, 0} β Nat _inst_1 (List.toFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b) (Nat.factors n)) f))
Case conversion may be inaccurate. Consider using '#align nat.prod_factors_gcd_mul_prod_factors_mul Nat.prod_factors_gcd_mul_prod_factors_mulₓ'. -/
@[to_additive sum_factors_gcd_add_sum_factors_mul]
theorem prod_factors_gcd_mul_prod_factors_mul {β : Type _} [CommMonoid β] (m n : ℕ) (f : ℕ → β) :
    (m.gcd n).factors.toFinset.Prod f * (m * n).factors.toFinset.Prod f =
      m.factors.toFinset.Prod f * n.factors.toFinset.Prod f :=
  by
  rcases eq_or_ne n 0 with (rfl | hm0); · simp
  rcases eq_or_ne m 0 with (rfl | hn0); · simp
  rw [← @Finset.prod_union_inter _ _ m.factors.to_finset n.factors.to_finset, mul_comm]
  congr
  · apply factors_mul_to_finset <;> assumption
  · simp only [← support_factorization, factorization_gcd hn0 hm0, Finsupp.support_inf]
#align nat.prod_factors_gcd_mul_prod_factors_mul Nat.prod_factors_gcd_mul_prod_factors_mul
#align nat.sum_factors_gcd_add_sum_factors_mul Nat.sum_factors_gcd_add_sum_factors_mul

#print Nat.setOf_pow_dvd_eq_Icc_factorization /-
theorem setOf_pow_dvd_eq_Icc_factorization {n p : ℕ} (pp : p.Prime) (hn : n ≠ 0) :
    { i : ℕ | i ≠ 0 ∧ p ^ i ∣ n } = Set.Icc 1 (n.factorization p) :=
  by
  ext
  simp [lt_succ_iff, one_le_iff_ne_zero, pp.pow_dvd_iff_le_factorization hn]
#align nat.set_of_pow_dvd_eq_Icc_factorization Nat.setOf_pow_dvd_eq_Icc_factorization
-/

/- warning: nat.Icc_factorization_eq_pow_dvd -> Nat.Icc_factorization_eq_pow_dvd is a dubious translation:
lean 3 declaration is
  forall (n : Nat) {p : Nat}, (Nat.Prime p) -> (Eq.{1} (Finset.{0} Nat) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) (coeFn.{1, 1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (fun (_x : Finsupp.{0, 0} Nat Nat Nat.hasZero) => Nat -> Nat) (Finsupp.coeFun.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization n) p)) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p i) n) (fun (a : Nat) => Nat.decidableDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) n)))
but is expected to have type
  forall (n : Nat) {p : Nat}, (Nat.Prime p) -> (Eq.{1} (Finset.{0} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p)) (Finset.Icc.{0} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p) (PartialOrder.toPreorder.{0} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p) (StrictOrderedSemiring.toPartialOrder.{0} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p) Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p) 1 (instOfNatNat 1)) (FunLike.coe.{1, 1, 1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) _x) (Finsupp.funLike.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization n) p)) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p i) n) (fun (a : Nat) => Nat.decidable_dvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) n)))
Case conversion may be inaccurate. Consider using '#align nat.Icc_factorization_eq_pow_dvd Nat.Icc_factorization_eq_pow_dvdₓ'. -/
/-- The set of positive powers of prime `p` that divide `n` is exactly the set of
positive natural numbers up to `n.factorization p`. -/
theorem Icc_factorization_eq_pow_dvd (n : ℕ) {p : ℕ} (pp : Prime p) :
    Icc 1 (n.factorization p) = (Ico 1 n).filterₓ fun i : ℕ => p ^ i ∣ n :=
  by
  rcases eq_or_ne n 0 with (rfl | hn); · simp
  ext x
  simp only [mem_Icc, Finset.mem_filter, mem_Ico, and_assoc', and_congr_right_iff,
    pp.pow_dvd_iff_le_factorization hn, iff_and_self]
  exact fun H1 H2 => lt_of_le_of_lt H2 (factorization_lt p hn)
#align nat.Icc_factorization_eq_pow_dvd Nat.Icc_factorization_eq_pow_dvd

/- warning: nat.factorization_eq_card_pow_dvd -> Nat.factorization_eq_card_pow_dvd is a dubious translation:
lean 3 declaration is
  forall (n : Nat) {p : Nat}, (Nat.Prime p) -> (Eq.{1} Nat (coeFn.{1, 1} (Finsupp.{0, 0} Nat Nat Nat.hasZero) (fun (_x : Finsupp.{0, 0} Nat Nat Nat.hasZero) => Nat -> Nat) (Finsupp.coeFun.{0, 0} Nat Nat Nat.hasZero) (Nat.factorization n) p) (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p i) n) (fun (a : Nat) => Nat.decidableDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) n))))
but is expected to have type
  forall (n : Nat) {p : Nat}, (Nat.Prime p) -> (Eq.{1} ((fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) p) (FunLike.coe.{1, 1, 1} (Finsupp.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Data.Finsupp.Defs._hyg.779 : Nat) => Nat) _x) (Finsupp.funLike.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (Nat.factorization n) p) (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p i) n) (fun (a : Nat) => Nat.decidable_dvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) n))))
Case conversion may be inaccurate. Consider using '#align nat.factorization_eq_card_pow_dvd Nat.factorization_eq_card_pow_dvdₓ'. -/
theorem factorization_eq_card_pow_dvd (n : ℕ) {p : ℕ} (pp : p.Prime) :
    n.factorization p = ((Ico 1 n).filterₓ fun i => p ^ i ∣ n).card := by
  simp [← Icc_factorization_eq_pow_dvd n pp]
#align nat.factorization_eq_card_pow_dvd Nat.factorization_eq_card_pow_dvd

/- warning: nat.Ico_filter_pow_dvd_eq -> Nat.Ico_filter_pow_dvd_eq is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {p : Nat} {b : Nat}, (Nat.Prime p) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (LE.le.{0} Nat Nat.hasLe n (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p b)) -> (Eq.{1} (Finset.{0} Nat) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p i) n) (fun (a : Nat) => Nat.decidableDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) n)) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p i) n) (fun (a : Nat) => Nat.decidableDvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p a) n) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) b)))
but is expected to have type
  forall {n : Nat} {p : Nat} {b : Nat}, (Nat.Prime p) -> (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (LE.le.{0} Nat instLENat n (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p b)) -> (Eq.{1} (Finset.{0} Nat) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p i) n) (fun (a : Nat) => Nat.decidable_dvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p a) n) (Finset.Ico.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) n)) (Finset.filter.{0} Nat (fun (i : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p i) n) (fun (a : Nat) => Nat.decidable_dvd (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p a) n) (Finset.Icc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) b)))
Case conversion may be inaccurate. Consider using '#align nat.Ico_filter_pow_dvd_eq Nat.Ico_filter_pow_dvd_eqₓ'. -/
theorem Ico_filter_pow_dvd_eq {n p b : ℕ} (pp : p.Prime) (hn : n ≠ 0) (hb : n ≤ p ^ b) :
    ((Ico 1 n).filterₓ fun i => p ^ i ∣ n) = (Icc 1 b).filterₓ fun i => p ^ i ∣ n :=
  by
  ext x
  simp only [Finset.mem_filter, mem_Ico, mem_Icc, and_congr_left_iff, and_congr_right_iff]
  rintro h1 -
  simp [lt_of_pow_dvd_right hn pp.two_le h1,
    (pow_le_iff_le_right pp.two_le).1 ((le_of_dvd hn.bot_lt h1).trans hb)]
#align nat.Ico_filter_pow_dvd_eq Nat.Ico_filter_pow_dvd_eq

/-! ### Factorization and coprimes -/

#print Nat.factorization_mul_apply_of_coprime /-
/-- For coprime `a` and `b`, the power of `p` in `a * b` is the sum of the powers in `a` and `b` -/
theorem factorization_mul_apply_of_coprime {p a b : ℕ} (hab : coprime a b) :
    (a * b).factorization p = a.factorization p + b.factorization p := by
  simp only [← factors_count_eq, perm_iff_count.mp (perm_factors_mul_of_coprime hab), count_append]
#align nat.factorization_mul_apply_of_coprime Nat.factorization_mul_apply_of_coprime
-/

#print Nat.factorization_mul_of_coprime /-
/-- For coprime `a` and `b`, the power of `p` in `a * b` is the sum of the powers in `a` and `b` -/
theorem factorization_mul_of_coprime {a b : ℕ} (hab : coprime a b) :
    (a * b).factorization = a.factorization + b.factorization :=
  by
  ext q
  simp only [Finsupp.coe_add, add_apply, ← factors_count_eq, factorization_mul_apply_of_coprime hab]
#align nat.factorization_mul_of_coprime Nat.factorization_mul_of_coprime
-/

#print Nat.factorization_eq_of_coprime_left /-
/-- If `p` is a prime factor of `a` then the power of `p` in `a` is the same that in `a * b`,
for any `b` coprime to `a`. -/
theorem factorization_eq_of_coprime_left {p a b : ℕ} (hab : coprime a b) (hpa : p ∈ a.factors) :
    (a * b).factorization p = a.factorization p :=
  by
  rw [factorization_mul_apply_of_coprime hab, ← factors_count_eq, ← factors_count_eq]
  simpa only [count_eq_zero_of_not_mem (coprime_factors_disjoint hab hpa)]
#align nat.factorization_eq_of_coprime_left Nat.factorization_eq_of_coprime_left
-/

#print Nat.factorization_eq_of_coprime_right /-
/-- If `p` is a prime factor of `b` then the power of `p` in `b` is the same that in `a * b`,
for any `a` coprime to `b`. -/
theorem factorization_eq_of_coprime_right {p a b : ℕ} (hab : coprime a b) (hpb : p ∈ b.factors) :
    (a * b).factorization p = b.factorization p :=
  by
  rw [mul_comm]
  exact factorization_eq_of_coprime_left (coprime_comm.mp hab) hpb
#align nat.factorization_eq_of_coprime_right Nat.factorization_eq_of_coprime_right
-/

/- warning: nat.factorization_disjoint_of_coprime -> Nat.factorization_disjoint_of_coprime is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Disjoint.{0} (Finset.{0} Nat) (Finset.partialOrder.{0} Nat) (Finset.orderBot.{0} Nat) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization b)))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Disjoint.{0} (Finset.{0} Nat) (Finset.partialOrder.{0} Nat) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{0} Nat) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization b)))
Case conversion may be inaccurate. Consider using '#align nat.factorization_disjoint_of_coprime Nat.factorization_disjoint_of_coprimeₓ'. -/
/-- The prime factorizations of coprime `a` and `b` are disjoint -/
theorem factorization_disjoint_of_coprime {a b : ℕ} (hab : coprime a b) :
    Disjoint a.factorization.support b.factorization.support := by
  simpa only [support_factorization] using
    disjoint_to_finset_iff_disjoint.mpr (coprime_factors_disjoint hab)
#align nat.factorization_disjoint_of_coprime Nat.factorization_disjoint_of_coprime

/- warning: nat.factorization_mul_support_of_coprime -> Nat.factorization_mul_support_of_coprime is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) a b))) (Union.union.{0} (Finset.{0} Nat) (Finset.hasUnion.{0} Nat (fun (a : Nat) (b : Nat) => Nat.decidableEq a b)) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat Nat.hasZero (Nat.factorization b))))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Eq.{1} (Finset.{0} Nat) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) a b))) (Union.union.{0} (Finset.{0} Nat) (Finset.instUnionFinset.{0} Nat (fun (a : Nat) (b : Nat) => instDecidableEqNat a b)) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization a)) (Finsupp.support.{0, 0} Nat Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) (Nat.factorization b))))
Case conversion may be inaccurate. Consider using '#align nat.factorization_mul_support_of_coprime Nat.factorization_mul_support_of_coprimeₓ'. -/
/-- For coprime `a` and `b` the prime factorization `a * b` is the union of those of `a` and `b` -/
theorem factorization_mul_support_of_coprime {a b : ℕ} (hab : coprime a b) :
    (a * b).factorization.support = a.factorization.support ∪ b.factorization.support :=
  by
  rw [factorization_mul_of_coprime hab]
  exact support_add_eq (factorization_disjoint_of_coprime hab)
#align nat.factorization_mul_support_of_coprime Nat.factorization_mul_support_of_coprime

/-! ### Induction principles involving factorizations -/

#print Nat.recOnPrimePow /-
/-- Given `P 0, P 1` and a way to extend `P a` to `P (p ^ n * a)` for prime `p` not dividing `a`,
we can define `P` for all natural numbers. -/
@[elab_as_elim]
def recOnPrimePow {P : ℕ → Sort _} (h0 : P 0) (h1 : P 1)
    (h : ∀ a p n : ℕ, p.Prime → ¬p ∣ a → 0 < n → P a → P (p ^ n * a)) : ∀ a : ℕ, P a := fun a =>
  Nat.strongRecOn a fun n =>
    match n with
    | 0 => fun _ => h0
    | 1 => fun _ => h1
    | k + 2 => fun hk => by
      let p := (k + 2).minFac
      have hp : Prime p := min_fac_prime (succ_succ_ne_one k)
      -- the awkward `let` stuff here is because `factorization` is noncomputable (finsupp);
      -- we get around this by using the computable `factors.count`, and rewriting when we want
      -- to use the `factorization` API
      let t := (k + 2).factors.count p
      have ht : t = (k + 2).factorization p := factors_count_eq
      have hpt : p ^ t ∣ k + 2 := by
        rw [ht]
        exact ord_proj_dvd _ _
      have htp : 0 < t := by
        rw [ht]
        exact hp.factorization_pos_of_dvd (Nat.succ_ne_zero _) (min_fac_dvd _)
      convert h ((k + 2) / p ^ t) p t hp _ _ _
      · rw [Nat.mul_div_cancel' hpt]
      · rw [Nat.dvd_div_iff hpt, ← pow_succ', ht]
        exact pow_succ_factorization_not_dvd (k + 1).succ_ne_zero hp
      · exact htp
      · apply hk _ (Nat.div_lt_of_lt_mul _)
        simp [lt_mul_iff_one_lt_left Nat.succ_pos', one_lt_pow_iff htp.ne, hp.one_lt]
#align nat.rec_on_prime_pow Nat.recOnPrimePow
-/

#print Nat.recOnPosPrimePosCoprime /-
/-- Given `P 0`, `P 1`, and `P (p ^ n)` for positive prime powers, and a way to extend `P a` and
`P b` to `P (a * b)` when `a, b` are positive coprime, we can define `P` for all natural numbers. -/
@[elab_as_elim]
def recOnPosPrimePosCoprime {P : ℕ → Sort _} (hp : ∀ p n : ℕ, Prime p → 0 < n → P (p ^ n))
    (h0 : P 0) (h1 : P 1) (h : ∀ a b, 1 < a → 1 < b → coprime a b → P a → P b → P (a * b)) :
    ∀ a, P a :=
  recOnPrimePow h0 h1 <| by
    intro a p n hp' hpa hn hPa
    by_cases ha1 : a = 1
    · rw [ha1, mul_one]
      exact hp p n hp' hn
    refine' h (p ^ n) a (hp'.one_lt.trans_le (le_self_pow hn.ne' _)) _ _ (hp _ _ hp' hn) hPa
    · contrapose! hpa
      simp [lt_one_iff.1 (lt_of_le_of_ne hpa ha1)]
    simpa [hn, Prime.coprime_iff_not_dvd hp']
#align nat.rec_on_pos_prime_pos_coprime Nat.recOnPosPrimePosCoprime
-/

#print Nat.recOnPrimeCoprime /-
/-- Given `P 0`, `P (p ^ n)` for all prime powers, and a way to extend `P a` and `P b` to
`P (a * b)` when `a, b` are positive coprime, we can define `P` for all natural numbers. -/
@[elab_as_elim]
def recOnPrimeCoprime {P : ℕ → Sort _} (h0 : P 0) (hp : ∀ p n : ℕ, Prime p → P (p ^ n))
    (h : ∀ a b, 1 < a → 1 < b → coprime a b → P a → P b → P (a * b)) : ∀ a, P a :=
  recOnPosPrimePosCoprime (fun p n h _ => hp p n h) h0 (hp 2 0 prime_two) h
#align nat.rec_on_prime_coprime Nat.recOnPrimeCoprime
-/

#print Nat.recOnMul /-
/-- Given `P 0`, `P 1`, `P p` for all primes, and a way to extend `P a` and `P b` to
`P (a * b)`, we can define `P` for all natural numbers. -/
@[elab_as_elim]
def recOnMul {P : ℕ → Sort _} (h0 : P 0) (h1 : P 1) (hp : ∀ p, Prime p → P p)
    (h : ∀ a b, P a → P b → P (a * b)) : ∀ a, P a :=
  let hp : ∀ p n : ℕ, Prime p → P (p ^ n) := fun p n hp' =>
    match n with
    | 0 => h1
    | n + 1 => h _ _ (hp p hp') (_match _)
  recOnPrimeCoprime h0 hp fun a b _ _ _ => h a b
#align nat.rec_on_mul Nat.recOnMul
-/

/- warning: nat.multiplicative_factorization -> Nat.multiplicative_factorization is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), (forall (x : Nat) (y : Nat), (Nat.coprime x y) -> (Eq.{succ u1} β (f (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) x y)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toHasMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (f x) (f y)))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{u1} β 1 (OfNat.mk.{u1} β 1 (One.one.{u1} β (MulOneClass.toHasOne.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))))) -> (forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (Eq.{succ u1} β (f n) (Finsupp.prod.{0, 0, u1} Nat Nat β Nat.hasZero _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p k)))))
but is expected to have type
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), (forall (x : Nat) (y : Nat), (Nat.coprime x y) -> (Eq.{succ u1} β (f (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) x y)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (f x) (f y)))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{u1} β 1 (One.toOfNat1.{u1} β (Monoid.toOne.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))) -> (forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (Eq.{succ u1} β (f n) (Finsupp.prod.{0, 0, u1} Nat Nat β (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p k)))))
Case conversion may be inaccurate. Consider using '#align nat.multiplicative_factorization Nat.multiplicative_factorizationₓ'. -/
/-- For any multiplicative function `f` with `f 1 = 1` and any `n ≠ 0`,
we can evaluate `f n` by evaluating `f` at `p ^ k` over the factorization of `n` -/
theorem multiplicative_factorization {β : Type _} [CommMonoid β] (f : ℕ → β)
    (h_mult : ∀ x y : ℕ, coprime x y → f (x * y) = f x * f y) (hf : f 1 = 1) :
    ∀ {n : ℕ}, n ≠ 0 → f n = n.factorization.Prod fun p k => f (p ^ k) :=
  by
  apply Nat.recOnPosPrimePosCoprime
  · intro p k hp hk hpk
    simp [prime.factorization_pow hp, Finsupp.prod_single_index _, hf]
  · simp
  · rintro -
    rw [factorization_one, hf]
    simp
  · intro a b _ _ hab ha hb hab_pos
    rw [h_mult a b hab, ha (left_ne_zero_of_mul hab_pos), hb (right_ne_zero_of_mul hab_pos),
      factorization_mul_of_coprime hab, ← prod_add_index_of_disjoint]
    convert factorization_disjoint_of_coprime hab
#align nat.multiplicative_factorization Nat.multiplicative_factorization

/- warning: nat.multiplicative_factorization' -> Nat.multiplicative_factorization' is a dubious translation:
lean 3 declaration is
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), (forall (x : Nat) (y : Nat), (Nat.coprime x y) -> (Eq.{succ u1} β (f (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) x y)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toHasMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (f x) (f y)))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (OfNat.ofNat.{u1} β 1 (OfNat.mk.{u1} β 1 (One.one.{u1} β (MulOneClass.toHasOne.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (OfNat.ofNat.{u1} β 1 (OfNat.mk.{u1} β 1 (One.one.{u1} β (MulOneClass.toHasOne.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))))) -> (forall {n : Nat}, Eq.{succ u1} β (f n) (Finsupp.prod.{0, 0, u1} Nat Nat β Nat.hasZero _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat (Monoid.Pow.{0} Nat Nat.monoid)) p k))))
but is expected to have type
  forall {β : Type.{u1}} [_inst_1 : CommMonoid.{u1} β] (f : Nat -> β), (forall (x : Nat) (y : Nat), (Nat.coprime x y) -> (Eq.{succ u1} β (f (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) x y)) (HMul.hMul.{u1, u1, u1} β β β (instHMul.{u1} β (MulOneClass.toMul.{u1} β (Monoid.toMulOneClass.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1)))) (f x) (f y)))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (OfNat.ofNat.{u1} β 1 (One.toOfNat1.{u1} β (Monoid.toOne.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))) -> (Eq.{succ u1} β (f (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (OfNat.ofNat.{u1} β 1 (One.toOfNat1.{u1} β (Monoid.toOne.{u1} β (CommMonoid.toMonoid.{u1} β _inst_1))))) -> (forall {n : Nat}, Eq.{succ u1} β (f n) (Finsupp.prod.{0, 0, u1} Nat Nat β (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero) _inst_1 (Nat.factorization n) (fun (p : Nat) (k : Nat) => f (HPow.hPow.{0, 0, 0} Nat Nat Nat (instHPow.{0, 0} Nat Nat instPowNat) p k))))
Case conversion may be inaccurate. Consider using '#align nat.multiplicative_factorization' Nat.multiplicative_factorization'ₓ'. -/
/-- For any multiplicative function `f` with `f 1 = 1` and `f 0 = 1`,
we can evaluate `f n` by evaluating `f` at `p ^ k` over the factorization of `n` -/
theorem multiplicative_factorization' {β : Type _} [CommMonoid β] (f : ℕ → β)
    (h_mult : ∀ x y : ℕ, coprime x y → f (x * y) = f x * f y) (hf0 : f 0 = 1) (hf1 : f 1 = 1) :
    ∀ {n : ℕ}, f n = n.factorization.Prod fun p k => f (p ^ k) :=
  by
  apply Nat.recOnPosPrimePosCoprime
  · intro p k hp hk
    simp only [hp.factorization_pow]
    rw [prod_single_index _]
    simp [hf1]
  · simp [hf0]
  · rw [factorization_one, hf1]
    simp
  · intro a b _ _ hab ha hb
    rw [h_mult a b hab, ha, hb, factorization_mul_of_coprime hab, ← prod_add_index_of_disjoint]
    convert factorization_disjoint_of_coprime hab
#align nat.multiplicative_factorization' Nat.multiplicative_factorization'

#print Nat.eq_iff_prime_padicValNat_eq /-
/-- Two positive naturals are equal if their prime padic valuations are equal -/
theorem eq_iff_prime_padicValNat_eq (a b : ℕ) (ha : a ≠ 0) (hb : b ≠ 0) :
    a = b ↔ ∀ p : ℕ, p.Prime → padicValNat p a = padicValNat p b :=
  by
  constructor
  · rintro rfl
    simp
  · intro h
    refine' eq_of_factorization_eq ha hb fun p => _
    by_cases pp : p.prime
    · simp [factorization_def, pp, h p pp]
    · simp [factorization_eq_zero_of_non_prime, pp]
#align nat.eq_iff_prime_padic_val_nat_eq Nat.eq_iff_prime_padicValNat_eq
-/

#print Nat.prod_pow_prime_padicValNat /-
theorem prod_pow_prime_padicValNat (n : Nat) (hn : n ≠ 0) (m : Nat) (pr : n < m) :
    (∏ p in Finset.filter Nat.Prime (Finset.range m), p ^ padicValNat p n) = n :=
  by
  nth_rw_rhs 1 [← factorization_prod_pow_eq_self hn]
  rw [eq_comm]
  apply Finset.prod_subset_one_on_sdiff
  ·
    exact fun p hp =>
      finset.mem_filter.mpr
        ⟨finset.mem_range.mpr (gt_of_gt_of_ge pr (le_of_mem_factorization hp)),
          prime_of_mem_factorization hp⟩
  · intro p hp
    cases' finset.mem_sdiff.mp hp with hp1 hp2
    rw [← factorization_def n (finset.mem_filter.mp hp1).2]
    simp [finsupp.not_mem_support_iff.mp hp2]
  · intro p hp
    simp [factorization_def n (prime_of_mem_factorization hp)]
#align nat.prod_pow_prime_padic_val_nat Nat.prod_pow_prime_padicValNat
-/

/-! ### Lemmas about factorizations of particular functions -/

/- warning: nat.card_multiples -> Nat.card_multiples is a dubious translation:
lean 3 declaration is
  forall (n : Nat) (p : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (e : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd p (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) e (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (a : Nat) => Nat.decidableDvd p (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) a (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (Finset.range n))) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) n p)
but is expected to have type
  forall (n : Nat) (p : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (e : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat p (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) e (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (fun (a : Nat) => Nat.decidable_dvd p (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) a (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (Finset.range n))) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) n p)
Case conversion may be inaccurate. Consider using '#align nat.card_multiples Nat.card_multiplesₓ'. -/
-- TODO: Port lemmas from `data/nat/multiplicity` to here, re-written in terms of `factorization`
/-- Exactly `n / p` naturals in `[1, n]` are multiples of `p`. -/
theorem card_multiples (n p : ℕ) : card ((Finset.range n).filterₓ fun e => p ∣ e + 1) = n / p :=
  by
  induction' n with n hn; · simp
  simp [Nat.succ_div, add_ite, add_zero, Finset.range_succ, filter_insert, apply_ite card,
    card_insert_of_not_mem, hn]
#align nat.card_multiples Nat.card_multiples

/- warning: nat.Ioc_filter_dvd_card_eq_div -> Nat.Ioc_filter_dvd_card_eq_div is a dubious translation:
lean 3 declaration is
  forall (n : Nat) (p : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (x : Nat) => Dvd.Dvd.{0} Nat Nat.hasDvd p x) (fun (a : Nat) => Nat.decidableDvd p a) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) Nat.locallyFiniteOrder (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) n))) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) n p)
but is expected to have type
  forall (n : Nat) (p : Nat), Eq.{1} Nat (Finset.card.{0} Nat (Finset.filter.{0} Nat (fun (x : Nat) => Dvd.dvd.{0} Nat Nat.instDvdNat p x) (fun (a : Nat) => Nat.decidable_dvd p a) (Finset.Ioc.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)) instLocallyFiniteOrderNatToPreorderToPartialOrderStrictOrderedSemiring (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) n))) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) n p)
Case conversion may be inaccurate. Consider using '#align nat.Ioc_filter_dvd_card_eq_div Nat.Ioc_filter_dvd_card_eq_divₓ'. -/
/-- Exactly `n / p` naturals in `(0, n]` are multiples of `p`. -/
theorem Ioc_filter_dvd_card_eq_div (n p : ℕ) : ((Ioc 0 n).filterₓ fun x => p ∣ x).card = n / p :=
  by
  induction' n with n IH
  · simp
  -- TODO: Golf away `h1` after Yaël PRs a lemma asserting this
  have h1 : Ioc 0 n.succ = insert n.succ (Ioc 0 n) :=
    by
    rcases n.eq_zero_or_pos with (rfl | hn)
    · simp
    simp_rw [← Ico_succ_succ, Ico_insert_right (succ_le_succ hn.le), Ico_succ_right]
  simp [Nat.succ_div, add_ite, add_zero, h1, filter_insert, apply_ite card, card_insert_eq_ite, IH,
    Finset.mem_filter, mem_Ioc, not_le.2 (lt_add_one n)]
#align nat.Ioc_filter_dvd_card_eq_div Nat.Ioc_filter_dvd_card_eq_div

end Nat


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

! This file was ported from Lean 3 source module data.int.lemmas
! leanprover-community/mathlib commit 09597669f02422ed388036273d8848119699c22f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Set.Function
import Mathlib.Data.Int.Order.Lemmas
import Mathlib.Data.Int.Bitwise
import Mathlib.Data.Nat.Cast.Basic
import Mathlib.Data.Nat.Order.Lemmas

/-!
# Miscellaneous lemmas about the integers

This file contains lemmas about integers, which require further imports than
`Data.Int.Basic` or `Data.Int.Order`.

-/

open Nat

namespace Int

theorem le_coe_nat_sub (m n : ℕ) : (m - n : ℤ) ≤ ↑(m - n : ℕ) := by
  by_cases h : m ≥ n
  · exact le_of_eq (Int.ofNat_sub h).symm
  · simp [le_of_not_ge h, ofNat_le]
#align int.le_coe_nat_sub Int.le_coe_nat_sub

/-! ### `succ` and `pred` -/

-- Porting note: simp can prove this @[simp]
theorem succ_coe_nat_pos (n : ℕ) : 0 < (n : ℤ) + 1 :=
  lt_add_one_iff.mpr (by simp)
#align int.succ_coe_nat_pos Int.succ_coe_nat_pos

/-! ### `natAbs` -/

variable {a b : ℤ} {n : ℕ}

theorem natAbs_eq_iff_sq_eq {a b : ℤ} : a.natAbs = b.natAbs ↔ a ^ 2 = b ^ 2 := by
  rw [sq, sq]
  exact natAbs_eq_iff_mul_self_eq
#align int.nat_abs_eq_iff_sq_eq Int.natAbs_eq_iff_sq_eq

theorem natAbs_le_iff_sq_le {a b : ℤ} : a.natAbs ≤ b.natAbs ↔ a ^ 2 ≤ b ^ 2 := by
  rw [sq, sq]
  exact natAbs_le_iff_mul_self_le
#align int.nat_abs_le_iff_sq_le Int.natAbs_le_iff_sq_le

theorem natAbs_inj_of_nonneg_of_nonneg {a b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) :
    natAbs a = natAbs b ↔ a = b := by rw [← sq_eq_sq ha hb, ← natAbs_eq_iff_sq_eq]
#align int.nat_abs_inj_of_nonneg_of_nonneg Int.natAbs_inj_of_nonneg_of_nonneg

theorem natAbs_inj_of_nonpos_of_nonpos {a b : ℤ} (ha : a ≤ 0) (hb : b ≤ 0) :
    natAbs a = natAbs b ↔ a = b := by
  simpa only [Int.natAbs_neg, neg_inj] using
    natAbs_inj_of_nonneg_of_nonneg (neg_nonneg_of_nonpos ha) (neg_nonneg_of_nonpos hb)
#align int.nat_abs_inj_of_nonpos_of_nonpos Int.natAbs_inj_of_nonpos_of_nonpos

theorem natAbs_inj_of_nonneg_of_nonpos {a b : ℤ} (ha : 0 ≤ a) (hb : b ≤ 0) :
    natAbs a = natAbs b ↔ a = -b := by
  simpa only [Int.natAbs_neg] using natAbs_inj_of_nonneg_of_nonneg ha (neg_nonneg_of_nonpos hb)
#align int.nat_abs_inj_of_nonneg_of_nonpos Int.natAbs_inj_of_nonneg_of_nonpos

theorem natAbs_inj_of_nonpos_of_nonneg {a b : ℤ} (ha : a ≤ 0) (hb : 0 ≤ b) :
    natAbs a = natAbs b ↔ -a = b := by
  simpa only [Int.natAbs_neg] using natAbs_inj_of_nonneg_of_nonneg (neg_nonneg_of_nonpos ha) hb
#align int.nat_abs_inj_of_nonpos_of_nonneg Int.natAbs_inj_of_nonpos_of_nonneg

section Intervals

open Set

theorem strictMonoOn_natAbs : StrictMonoOn natAbs (Ici 0) := fun _ ha _ _ hab =>
  natAbs_lt_natAbs_of_nonneg_of_lt ha hab
#align int.strict_mono_on_nat_abs Int.strictMonoOn_natAbs

theorem strictAntiOn_natAbs : StrictAntiOn natAbs (Iic 0) := fun a _ b hb hab => by
  simpa [Int.natAbs_neg] using
    natAbs_lt_natAbs_of_nonneg_of_lt (Right.nonneg_neg_iff.mpr hb) (neg_lt_neg_iff.mpr hab)
#align int.strict_anti_on_nat_abs Int.strictAntiOn_natAbs

theorem injOn_natAbs_Ici : InjOn natAbs (Ici 0) :=
  strictMonoOn_natAbs.injOn
#align int.inj_on_nat_abs_Ici Int.injOn_natAbs_Ici

theorem injOn_natAbs_Iic : InjOn natAbs (Iic 0) :=
  strictAntiOn_natAbs.injOn
#align int.inj_on_nat_abs_Iic Int.injOn_natAbs_Iic

end Intervals

/-! ### `toNat` -/

theorem toNat_of_nonpos : ∀ {z : ℤ}, z ≤ 0 → z.toNat = 0
  | 0, _ => rfl
  | (n + 1 : ℕ), h => (h.not_lt (by simp)).elim
  | -[n+1], _ => rfl
#align int.to_nat_of_nonpos Int.toNat_of_nonpos

/-! ### bitwise ops

This lemma is orphaned from `Data.Int.Bitwise` as it also requires material from `Data.Int.Order`.
-/

attribute [local simp] Int.zero_div

@[simp]
theorem div2_bit (b n) : div2 (bit b n) = n := by
  rw [bit_val, div2_val, add_comm, Int.add_mul_ediv_left, (_ : (_ / 2 : ℤ) = 0), zero_add]
  cases b
  · simp
  · show ofNat _ = _
    rw [Nat.div_eq_zero] <;> simp
  · decide
#align int.div2_bit Int.div2_bit

end Int


/-
Copyright (c) 2020 Aaron Anderson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson
-/
import algebra.gcd_monoid.basic
import data.multiset.finset_ops
import data.multiset.fold

/-!
# GCD and LCM operations on multisets

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

## Main definitions

- `multiset.gcd` - the greatest common denominator of a `multiset` of elements of a `gcd_monoid`
- `multiset.lcm` - the least common multiple of a `multiset` of elements of a `gcd_monoid`

## Implementation notes

TODO: simplify with a tactic and `data.multiset.lattice`

## Tags

multiset, gcd
-/

namespace multiset
variables {α : Type*} [cancel_comm_monoid_with_zero α] [normalized_gcd_monoid α]

/-! ### lcm -/
section lcm

/-- Least common multiple of a multiset -/
def lcm (s : multiset α) : α := s.fold gcd_monoid.lcm 1

@[simp] lemma lcm_zero : (0 : multiset α).lcm = 1 :=
fold_zero _ _

@[simp] lemma lcm_cons (a : α) (s : multiset α) :
  (a ::ₘ s).lcm = gcd_monoid.lcm a s.lcm :=
fold_cons_left _ _ _ _

@[simp] lemma lcm_singleton {a : α} : ({a} : multiset α).lcm = normalize a :=
(fold_singleton _ _ _).trans $ lcm_one_right _

@[simp] lemma lcm_add (s₁ s₂ : multiset α) : (s₁ + s₂).lcm = gcd_monoid.lcm s₁.lcm s₂.lcm :=
eq.trans (by simp [lcm]) (fold_add _ _ _ _ _)

lemma lcm_dvd {s : multiset α} {a : α} : s.lcm ∣ a ↔ (∀ b ∈ s, b ∣ a) :=
multiset.induction_on s (by simp)
  (by simp [or_imp_distrib, forall_and_distrib, lcm_dvd_iff] {contextual := tt})

lemma dvd_lcm {s : multiset α} {a : α} (h : a ∈ s) : a ∣ s.lcm :=
lcm_dvd.1 dvd_rfl _ h

lemma lcm_mono {s₁ s₂ : multiset α} (h : s₁ ⊆ s₂) : s₁.lcm ∣ s₂.lcm :=
lcm_dvd.2 $ assume b hb, dvd_lcm (h hb)

@[simp] lemma normalize_lcm (s : multiset α) : normalize (s.lcm) = s.lcm :=
multiset.induction_on s (by simp) $ λ a s IH, by simp

@[simp] theorem lcm_eq_zero_iff [nontrivial α] (s : multiset α) : s.lcm = 0 ↔ (0 : α) ∈ s :=
begin
  induction s using multiset.induction_on with a s ihs,
  { simp only [lcm_zero, one_ne_zero, not_mem_zero] },
  { simp only [mem_cons, lcm_cons, lcm_eq_zero_iff, ihs, @eq_comm _ a] },
end

variables [decidable_eq α]

@[simp] lemma lcm_dedup (s : multiset α) : (dedup s).lcm = s.lcm :=
multiset.induction_on s (by simp) $ λ a s IH, begin
  by_cases a ∈ s; simp [IH, h],
  unfold lcm,
  rw [← cons_erase h, fold_cons_left, ← lcm_assoc, lcm_same],
  apply lcm_eq_of_associated_left (associated_normalize _),
end

@[simp] lemma lcm_ndunion (s₁ s₂ : multiset α) :
  (ndunion s₁ s₂).lcm = gcd_monoid.lcm s₁.lcm s₂.lcm :=
by { rw [← lcm_dedup, dedup_ext.2, lcm_dedup, lcm_add], simp }

@[simp] lemma lcm_union (s₁ s₂ : multiset α) :
  (s₁ ∪ s₂).lcm = gcd_monoid.lcm s₁.lcm s₂.lcm :=
by { rw [← lcm_dedup, dedup_ext.2, lcm_dedup, lcm_add], simp }

@[simp] lemma lcm_ndinsert (a : α) (s : multiset α) :
  (ndinsert a s).lcm = gcd_monoid.lcm a s.lcm :=
by { rw [← lcm_dedup, dedup_ext.2, lcm_dedup, lcm_cons], simp }

end lcm

/-! ### gcd -/
section gcd

/-- Greatest common divisor of a multiset -/
def gcd (s : multiset α) : α := s.fold gcd_monoid.gcd 0

@[simp] lemma gcd_zero : (0 : multiset α).gcd = 0 :=
fold_zero _ _

@[simp] lemma gcd_cons (a : α) (s : multiset α) :
  (a ::ₘ s).gcd = gcd_monoid.gcd a s.gcd :=
fold_cons_left _ _ _ _

@[simp] lemma gcd_singleton {a : α} : ({a} : multiset α).gcd = normalize a :=
(fold_singleton _ _ _).trans $ gcd_zero_right _

@[simp] lemma gcd_add (s₁ s₂ : multiset α) : (s₁ + s₂).gcd = gcd_monoid.gcd s₁.gcd s₂.gcd :=
eq.trans (by simp [gcd]) (fold_add _ _ _ _ _)

lemma dvd_gcd {s : multiset α} {a : α} : a ∣ s.gcd ↔ (∀ b ∈ s, a ∣ b) :=
multiset.induction_on s (by simp)
  (by simp [or_imp_distrib, forall_and_distrib, dvd_gcd_iff] {contextual := tt})

lemma gcd_dvd {s : multiset α} {a : α} (h : a ∈ s) : s.gcd ∣ a :=
dvd_gcd.1 dvd_rfl _ h

lemma gcd_mono {s₁ s₂ : multiset α} (h : s₁ ⊆ s₂) : s₂.gcd ∣ s₁.gcd :=
dvd_gcd.2 $ assume b hb, gcd_dvd (h hb)

@[simp] lemma normalize_gcd (s : multiset α) : normalize (s.gcd) = s.gcd :=
multiset.induction_on s (by simp) $ λ a s IH, by simp

theorem gcd_eq_zero_iff (s : multiset α) : s.gcd = 0 ↔ ∀ (x : α), x ∈ s → x = 0 :=
begin
  split,
  { intros h x hx,
    apply eq_zero_of_zero_dvd,
    rw ← h,
    apply gcd_dvd hx },
  { apply s.induction_on,
    { simp },
    intros a s sgcd h,
    simp [h a (mem_cons_self a s), sgcd (λ x hx, h x (mem_cons_of_mem hx))] }
end

lemma gcd_map_mul (a : α) (s : multiset α) :
  (s.map ((*) a)).gcd = normalize a * s.gcd :=
begin
  refine s.induction_on _ (λ b s ih, _),
  { simp_rw [map_zero, gcd_zero, mul_zero] },
  { simp_rw [map_cons, gcd_cons, ← gcd_mul_left], rw ih,
    apply ((normalize_associated a).mul_right _).gcd_eq_right },
end

section

variables [decidable_eq α]

@[simp] lemma gcd_dedup (s : multiset α) : (dedup s).gcd = s.gcd :=
multiset.induction_on s (by simp) $ λ a s IH, begin
  by_cases a ∈ s; simp [IH, h],
  unfold gcd,
  rw [← cons_erase h, fold_cons_left, ← gcd_assoc, gcd_same],
  apply (associated_normalize _).gcd_eq_left,
end

@[simp] lemma gcd_ndunion (s₁ s₂ : multiset α) :
  (ndunion s₁ s₂).gcd = gcd_monoid.gcd s₁.gcd s₂.gcd :=
by { rw [← gcd_dedup, dedup_ext.2, gcd_dedup, gcd_add], simp }

@[simp] lemma gcd_union (s₁ s₂ : multiset α) :
  (s₁ ∪ s₂).gcd = gcd_monoid.gcd s₁.gcd s₂.gcd :=
by { rw [← gcd_dedup, dedup_ext.2, gcd_dedup, gcd_add], simp }

@[simp] lemma gcd_ndinsert (a : α) (s : multiset α) :
  (ndinsert a s).gcd = gcd_monoid.gcd a s.gcd :=
by { rw [← gcd_dedup, dedup_ext.2, gcd_dedup, gcd_cons], simp }

end

lemma extract_gcd' (s t : multiset α) (hs : ∃ x, x ∈ s ∧ x ≠ (0 : α))
  (ht : s = t.map ((*) s.gcd)) : t.gcd = 1 :=
((@mul_right_eq_self₀ _ _ s.gcd _).1 $ by conv_lhs { rw [← normalize_gcd, ← gcd_map_mul, ← ht] })
  .resolve_right $ by { contrapose! hs, exact s.gcd_eq_zero_iff.1 hs }

lemma extract_gcd (s : multiset α) (hs : s ≠ 0) :
  ∃ t : multiset α, s = t.map ((*) s.gcd) ∧ t.gcd = 1 :=
begin
  classical,
  by_cases h : ∀ x ∈ s, x = (0 : α),
  { use replicate s.card 1,
    rw [map_replicate, eq_replicate, mul_one, s.gcd_eq_zero_iff.2 h, ←nsmul_singleton, ←gcd_dedup],
    rw [dedup_nsmul (card_pos.2 hs).ne', dedup_singleton, gcd_singleton],
    exact ⟨⟨rfl, h⟩, normalize_one⟩ },
  { choose f hf using @gcd_dvd _ _ _ s,
    have := _, push_neg at h,
    refine ⟨s.pmap @f (λ _, id), this, extract_gcd' s _ h this⟩,
    rw map_pmap, conv_lhs { rw [← s.map_id, ← s.pmap_eq_map _ _ (λ _, id)] },
    congr' with x hx, rw [id, ← hf hx] },
end

end gcd

end multiset