/-
Copyright (c) 2022 María Inés de Frutos-Fernández. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: María Inés de Frutos-Fernández
-/
import ring_theory.class_group
import adeles_number_field
import ideles_R

/-!
# The group of idèles of a number field
We define the group of finite idèles and the group of idèles of a number field `K`, both of which
are topological commutative groups.

We also prove that `K*` can be regarded as a subgroup of `I_K_f` and `I_K` and define the idèle
class group of `K` as the quotient `I_K/K*`. We then show that the ideal class group of `K` is
isomorphic to an explicit quotient of `C_K` as topological groups, by constructing a continuous
surjective group homomorphism from `C_K` to the ideal class group `Cl(K)` of `K` and
computing its kernel.

## Main definitions
- `number_field.I_K_f` : The finite idèle group of the number field `K`.
- `number_field.I_K`   : The idèle group of the number field `K`.
- `number_field.C_K`   : The idèle class group of the number field `K`.
- `number_field.C_K.map_to_class_group` : The natural group homomorphism from `C_K` to the `Cl(K)`.

## Main results
- `number_field.C_K.map_to_class_group.surjective` : The natural map `C_K → Cl(K)` is surjective.
- `number_field.C_K.map_to_class_group.continuous` : The natural map `C_K → Cl(K)` is continuous.
- `number_field.C_K.map_to_class_group.mem_kernel_iff` : We compute the kernel of `C_K → Cl(K)`.

## References
* [J.W.S. Cassels, A. Frölich, *Algebraic Number Theory*][cassels1967algebraic]

## Tags
idèle group, number field
-/

noncomputable theory

open set function
open_locale tensor_product --number_field

namespace number_field
/-! ### The idèle group of a number field
We define the (finite) idèle group of a number field `K`, with its topological group structure.
We define the idèle class group `C_K` of `K` and show that the ideal class group of `K` is
isomorphic to an explicit quotient of `C_K` as topological groups. -/

variables (K : Type) [field K] [number_field K]

/-- The finite idèle group of the number field `K`.-/
def I_K_f := units (A_K_f K)
/-- The idèle group of the number field `K`.-/
def I_K := units (A_K K)

instance : comm_group (I_K_f K) := units.comm_group
instance : comm_group (I_K K) := units.comm_group
instance : topological_space (I_K_f K) :=
finite_idele_group'.topological_space (ring_of_integers K) K
instance : topological_group (I_K_f K) :=
finite_idele_group'.topological_group (ring_of_integers K) K
instance : topological_space (I_K K) := units.topological_space
instance : topological_group (I_K K) := units.topological_group
instance : inhabited (I_K_f K) := ⟨1⟩
instance : inhabited (I_K K) := ⟨1⟩

/-- `I_K` is isomorphic to the product `I_K_f × (ℝ ⊗[ℚ] K)*`, as groups. -/
def I_K.as_prod : I_K K ≃* (I_K_f K) × units (ℝ ⊗[ℚ] K) :=
by apply @mul_equiv.prod_units (A_K_f K) (ℝ ⊗[ℚ] K) _ _

/-- `I_K` is homeomorphic to the product `I_K_f × (ℝ ⊗[ℚ] K)*`. -/
def I_K.as_prod.homeo : homeomorph (I_K K) ((I_K_f K) × units (ℝ ⊗[ℚ] K)) :=
units.homeomorph.prod_units

variable {K}
lemma I_K.as_prod.continuous : continuous ((I_K.as_prod K).to_fun) :=
(I_K.as_prod.homeo K).continuous_to_fun
lemma I_K.as_prod.continuous_inv : continuous ((I_K.as_prod K).inv_fun) :=
(I_K.as_prod.homeo K).continuous_inv_fun

/-- We construct an idèle of `K` given a finite idèle and a unit of `ℝ ⊗[ℚ] K`. -/
def I_K.mk (x : I_K_f K) (u : units (ℝ ⊗[ℚ] K)) : I_K K := (I_K.as_prod K).inv_fun (prod.mk x u)

variable (K)

/-- The projection from `I_K` to `I_K_f`, as a group homomorphism. -/
def I_K.fst : monoid_hom (I_K K) (I_K_f K) :=
{ to_fun := λ x, ((I_K.as_prod K).to_fun x).1,
  map_one' := by {rw [mul_equiv.to_fun_eq_coe, mul_equiv.map_one, prod.fst_one]},
  map_mul' := λ x y, by {simp only [prod.fst_mul, mul_equiv.to_fun_eq_coe, mul_equiv.map_mul]}}

variable {K}
/-- The projection map `I_K.fst` is surjective. -/
lemma I_K.fst.surjective : function.surjective (I_K.fst K) :=
begin
  intro x,
  use I_K.mk x (1 : units (ℝ ⊗[ℚ] K)),
  rw [I_K.fst, monoid_hom.coe_mk, mul_equiv.to_fun_eq_coe, I_K.mk,
    mul_equiv.inv_fun_eq_symm, mul_equiv.apply_symm_apply],
end

/-- The projection map `I_K.fst` is continuous. -/
lemma I_K.fst.continuous : continuous (I_K.fst K) :=
continuous.comp continuous_fst I_K.as_prod.continuous

lemma right_inv (x : units K) : (inj_K.ring_hom K) x.val * (inj_K.ring_hom K) x.inv = 1 :=
begin
  rw [← (inj_K.ring_hom K).map_mul, units.val_eq_coe, units.inv_eq_coe_inv, units.mul_inv],
  exact (inj_K.ring_hom K).map_one
end

lemma left_inv (x : units K) : (inj_K.ring_hom K) x.inv * (inj_K.ring_hom K) x.val  = 1 :=
by rw [mul_comm, right_inv]

variable (K)
/-- The map from `K^*` to `I_K_f` sending `k` to `((k)_v)`. -/
def inj_units_K_f : units K → I_K_f K := units.map (inj_K_f.ring_hom K).to_monoid_hom

/-- The map from `K^*` to `I_K` sending `k` to `((k)_v, 1 ⊗ k)`. -/
def inj_units_K : units K → I_K K := units.map (inj_K.ring_hom K).to_monoid_hom

variable {K}
@[simp] lemma inj_units_K.map_one : inj_units_K K 1 = 1 := by rw [inj_units_K, map_one]

@[simp] lemma inj_units_K.map_mul (x y : units K) :
  inj_units_K K (x*y) = (inj_units_K K x) * (inj_units_K K y) :=  by rw [inj_units_K, map_mul]

variable (K)
/-- `inj_units_K` is a group homomorphism. -/
def inj_units_K.group_hom : monoid_hom (units K) (I_K K) :=
{ to_fun    := inj_units_K K,
  map_one'  := inj_units_K.map_one,
  map_mul'  := inj_units_K.map_mul, }

/-- `inj_units_K` is injective. -/
lemma inj_units_K.injective : injective (inj_units_K K) :=
begin
  intros x y hxy,
  simp only [inj_units_K, units.map, monoid_hom.mk', ring_hom.coe_monoid_hom, monoid_hom.coe_mk,
    ← units.eq_iff, units.coe_mk] at hxy,
  ext,
  exact inj_K.injective K hxy,
end

/-- The idèle class group of `K` is the quotient of `I_K` by the group `K*` of principal idèles. -/
def C_K := (I_K K) ⧸ (inj_units_K.group_hom K).range

instance : comm_group (C_K K) :=
quotient_group.has_quotient.quotient.comm_group ((inj_units_K.group_hom K).range)
instance : topological_space (C_K K) := quotient.topological_space
instance : topological_group (C_K K) := topological_group_quotient ((inj_units_K.group_hom K).range)
instance : inhabited (C_K K) := ⟨1⟩

/-- The `v`-adic absolute value of the `v`th component of the idèle `x`. -/
def v_comp_val (x : I_K K) (v : maximal_spectrum (ring_of_integers K)) :
  with_zero (multiplicative ℤ) := valued.v (x.val.1.val v)

/-- The `v`-adic absolute value of the inverse of the `v`th component of the idèle `x`. -/
def v_comp_inv (x : I_K K) (v : maximal_spectrum (ring_of_integers K)) :
  with_zero (multiplicative ℤ) := valued.v (x.inv.1.val v)

/-- For any finite idèle `x`, there are finitely many maximal ideals `v` of `R` for which
`x_v ∉ R_v` or `x⁻¹_v ∉ R_v`. -/
lemma I_K_f.restricted_product (x : I_K_f K) : finite ({ v : maximal_spectrum (ring_of_integers K) |
  ¬ (x.val.val v) ∈ v.adic_completion_integers K } ∪ { v : maximal_spectrum (ring_of_integers K) |
  ¬ (x.inv.val v) ∈ v.adic_completion_integers K }) :=
restricted_product (ring_of_integers K) K x

lemma prod_val_inv_eq_one (x : I_K K) (v : maximal_spectrum (ring_of_integers K)):
  (x.val.fst.val v) * (x.inv.fst.val v) = 1  :=
begin
  rw [← pi.mul_apply, mul_apply_val, ← prod.fst_mul, units.val_inv,
    prod.fst_one, subtype.val_eq_coe, ← one_def, subtype.coe_mk],
  refl,
end

lemma valuation.prod_val_inv_eq_one (x : I_K K) (v : maximal_spectrum (ring_of_integers K)):
  (v_comp_val K x v) * (v_comp_inv K x v) = 1 :=
begin
  simp only [v_comp_val, v_comp_inv],
  rw [← valued.v.map_mul, prod_val_inv_eq_one K x v],
  exact valuation.map_one _,
end

lemma v_comp.ne_zero (x : I_K K) (v : maximal_spectrum (ring_of_integers K)) :
  (x.val.1.val v) ≠ 0 := left_ne_zero_of_mul_eq_one (prod_val_inv_eq_one K x v)

/-- For any idèle `x`, there are finitely many maximal ideals `v` of `R` for which `x_v ∉ R_v` or
`x⁻¹_v ∉ R_v`. -/
lemma I_K.restricted_product (x : I_K K) : finite ({ v : maximal_spectrum (ring_of_integers K) |
    (¬ (x.val.1.val v) ∈ v.adic_completion_integers K) } ∪ { v : maximal_spectrum
    (ring_of_integers K) | ¬ (x.inv.1.val v) ∈ v.adic_completion_integers K }) :=
finite.union x.val.1.property x.inv.1.property

/-- For any idèle `x`, there are finitely many maximal ideals `v` of `R` for which `|x_v|_v ≠ 1`. -/
lemma I_K.finite_exponents (x : I_K K) :
  finite { v : maximal_spectrum (ring_of_integers K) | v_comp_val K x v ≠ 1 } :=
begin
  have h_subset : { v : maximal_spectrum (ring_of_integers K) | v_comp_val K x v ≠ 1 } ⊆
  { v : maximal_spectrum (ring_of_integers K) | ¬ (x.val.1.val v) ∈ v.adic_completion_integers K } ∪
  { v : maximal_spectrum (ring_of_integers K) | ¬ (x.inv.1.val v) ∈ v.adic_completion_integers K },
  { intros v hv,
    rw [mem_union, mem_set_of_eq, mem_set_of_eq, adic_completion.is_integer,
      adic_completion.is_integer],
    rw mem_set_of_eq at hv,
    cases (lt_or_gt_of_ne hv) with hlt hgt,
    { right,
      have h_one : (v_comp_val K x v) * (v_comp_inv K x v) = 1 :=
      valuation.prod_val_inv_eq_one K x v,
      have h_inv : 1 < v_comp_inv K x v,
      { have hx : v_comp_val K x v ≠ 0,
        { rw [v_comp_val, valuation.ne_zero_iff],
          exact v_comp.ne_zero K x v,},
        rw mul_eq_one_iff_inv_eq₀ hx at h_one,
        rw [← h_one, ← with_zero.inv_one, inv_lt_inv₀ (ne.symm zero_ne_one) hx],
        exact hlt, },
      exact not_le.mpr h_inv,},
    { left, exact not_le.mpr hgt, }},
  exact finite.subset (I_K.restricted_product K x) h_subset,
end

/-- The natural map from `I_K_f` to the group of invertible fractional ideals of `K`, sending a
finite idèle `x` to the product `∏_v v^(val_v(x_v))`, where `val_v` denotes the additive
`v`-adic valuation. -/
def I_K_f.map_to_fractional_ideals : monoid_hom
  (I_K_f K) (units (fractional_ideal (non_zero_divisors (ring_of_integers K)) K)) :=
map_to_fractional_ideals (ring_of_integers K) K

variable {K}
/-- `I_K_f.map_to_fractional_ideals` is surjective. -/
lemma I_K_f.map_to_fractional_ideals.surjective :
  function.surjective (I_K_f.map_to_fractional_ideals K) :=
@map_to_fractional_ideals.surjective (ring_of_integers K) K _ _ _ _ _ _

/-- A finite idèle `x` is in the kernel of `I_K_f.map_to_fractional_ideals` if and only if
`|x_v|_v = 1` for all `v`. -/
lemma I_K_f.map_to_fractional_ideals.mem_kernel_iff (x : I_K_f K) :
  I_K_f.map_to_fractional_ideals K x = 1 ↔
  ∀ v : maximal_spectrum (ring_of_integers K),
    finite_idele.to_add_valuations (ring_of_integers K) K x v = 0 :=
@map_to_fractional_ideals.mem_kernel_iff (ring_of_integers K) K _ _ _ _ _ _ x

/-- `I_K_f.map_to_fractional_ideals` is continuous. -/
lemma I_K_f.map_to_fractional_ideals.continuous :
  continuous (I_K_f.map_to_fractional_ideals K) :=
@map_to_fractional_ideals.continuous (ring_of_integers K) K _ _ _ _ _ _

variable (K)
/-- The natural map from `I_K` to the group of invertible fractional ideals of `K`. -/
def I_K.map_to_fractional_ideals :
  monoid_hom (I_K K) (units (fractional_ideal (non_zero_divisors (ring_of_integers K)) K)) :=
monoid_hom.comp (I_K_f.map_to_fractional_ideals K) (I_K.fst K)

variable {K}
/-- `I_K.map_to_fractional_ideals` is surjective. -/
lemma I_K.map_to_fractional_ideals.surjective :
  function.surjective (I_K.map_to_fractional_ideals K) :=
function.surjective.comp I_K_f.map_to_fractional_ideals.surjective I_K.fst.surjective

/-- An idèle `x` is in the kernel of `I_K_f.map_to_fractional_ideals` if and only if `|x_v|_v = 1`
for all `v`. -/
lemma I_K.map_to_fractional_ideals.mem_kernel_iff (x : I_K K) :
  I_K.map_to_fractional_ideals K x = 1 ↔
  ∀ v : maximal_spectrum (ring_of_integers K),
    finite_idele.to_add_valuations (ring_of_integers K) K (I_K.fst K x) v = 0 :=
I_K_f.map_to_fractional_ideals.mem_kernel_iff (I_K.fst K x)

/-- `I_K.map_to_fractional_ideals` is continuous. -/
lemma I_K.map_to_fractional_ideals.continuous :
  continuous (I_K.map_to_fractional_ideals K) :=
continuous.comp I_K_f.map_to_fractional_ideals.continuous I_K.fst.continuous

variable (K)
/-- The map from `I_K_f` to the ideal  class group of `K` induced by
`I_K_f.map_to_fractional_ideals`. -/
def I_K_f.map_to_class_group :
  (I_K_f K) → (class_group (ring_of_integers K) K) :=
λ x, quotient_group.mk (I_K_f.map_to_fractional_ideals K x)

instance : topological_space (class_group ↥(ring_of_integers K) K) := ⊥
instance : topological_group (class_group ↥(ring_of_integers K) K) :=
{ continuous_mul := continuous_of_discrete_topology,
  continuous_inv := continuous_of_discrete_topology, }

variable {K}
lemma I_K_f.map_to_class_group.surjective : surjective (I_K_f.map_to_class_group K) :=
surjective.comp quotient.surjective_quotient_mk' I_K_f.map_to_fractional_ideals.surjective

lemma I_K_f.map_to_class_group.continuous : continuous (I_K_f.map_to_class_group K) :=
continuous.comp continuous_bot (I_K_f.map_to_fractional_ideals.continuous)

variable (K)
/-- The map from `I_K` to the ideal  class group of `K` induced by
`I_K.map_to_fractional_ideals`. -/
def I_K.map_to_class_group :
  (I_K K) → (class_group (ring_of_integers K) K) :=
λ x, quotient_group.mk' _ (I_K.map_to_fractional_ideals K x)

variable {K}
lemma I_K.map_to_class_group.surjective : surjective (I_K.map_to_class_group K) :=
surjective.comp quotient.surjective_quotient_mk' I_K.map_to_fractional_ideals.surjective

lemma I_K.map_to_class_group.continuous : continuous (I_K.map_to_class_group K) :=
continuous.comp continuous_bot I_K.map_to_fractional_ideals.continuous

variable {K}
lemma I_K.map_to_class_group.map_one : I_K.map_to_class_group K 1 = 1 :=
by {simp only [I_K.map_to_class_group, monoid_hom.map_one] }

lemma I_K.map_to_class_group.map_mul (x y : I_K K) : I_K.map_to_class_group K (x * y) =
  I_K.map_to_class_group K x * I_K.map_to_class_group K y :=
by {simp only [I_K.map_to_class_group, monoid_hom.map_mul] }

/-- The map from `I_K` to the ideal  class group of `K` induced by
`I_K.map_to_fractional_ideals` is a group homomorphism. -/
def I_K.monoid_hom_to_class_group : (I_K K) →* (class_group (ring_of_integers K) K) :=
{ to_fun   := I_K.map_to_class_group K,
  map_one' := I_K.map_to_class_group.map_one,
  map_mul' := λ x y, I_K.map_to_class_group.map_mul x y }

lemma I_K_f.unit_image.mul_inv (k : units K):
  ((inj_K_f.ring_hom K) k.val) * ((inj_K_f.ring_hom K) k.inv) = 1 :=
by rw [← ring_hom.map_mul, units.val_eq_coe, units.inv_eq_coe_inv, units.mul_inv, ring_hom.map_one]

lemma I_K_f.unit_image.inv_mul (k : units K):
  ((inj_K_f.ring_hom K) k.inv) * ((inj_K_f.ring_hom K) k.val) = 1 :=
by rw [mul_comm, I_K_f.unit_image.mul_inv]

open_locale classical

/-- `I_K_f.map_to_fractional_ideals` sends the principal finite idèle `(k)_v` corresponding to
`k ∈ K*` to the principal fractional ideal generated by `k`. -/
lemma I_K_f.map_to_fractional_ideal.map_units (k : units K) :
  fractional_ideal.span_singleton (non_zero_divisors ↥(ring_of_integers K)) (k : K) =
  ((I_K_f.map_to_fractional_ideals K) (units.mk ((inj_K_f.ring_hom K) k.val)
  ((inj_K_f.ring_hom K) k.inv) (I_K_f.unit_image.mul_inv k) (I_K_f.unit_image.inv_mul k))) :=
begin
  set I := (fractional_ideal.span_singleton (non_zero_divisors ↥(ring_of_integers K)) (k : K))
    with hI_def,
  have hI : I ≠ 0,
  { rw [hI_def, fractional_ideal.span_singleton_ne_zero_iff],
    exact units.ne_zero k },
  rw ← fractional_ideal.factorization_principal I hI k hI_def,
  apply finprod_congr,
  intro v,
  apply congr_arg,
  rw finite_idele.to_add_valuations,
  simp only,
  rw [with_zero.to_integer, ← injective.eq_iff multiplicative.of_add.injective, of_add_neg,
    of_add_to_add, ← neg_sub_neg, of_add_sub, ← inv_div'],
  apply congr_arg,
  have hv : valued.v (((inj_K_f.ring_hom K) k.val).val v) ≠ (0 : with_zero (multiplicative ℤ)),
  { rw [valuation.ne_zero_iff, inj_K_f.ring_hom.v_comp, units.val_eq_coe,
      ← uniform_space.completion.coe_zero,
      injective.ne_iff (@uniform_space.completion.coe_inj K v.us' v.ss)],
    exact units.ne_zero k },
  let z :=  classical.some (with_zero.to_integer._proof_1 hv),
  let hz :=  classical.some_spec (with_zero.to_integer._proof_1 hv),
  rw [← with_zero.coe_inj, hz, v.valued_adic_completion_def, inj_K_f.ring_hom,
    inj_K.ring_hom_apply, inj_K_apply, valued.extension_extends, units.val_eq_coe,
    v.adic_valued_def, maximal_spectrum.valuation_def],
  simp only,
  rw [with_zero.coe_div, maximal_spectrum.int_valuation_def_if_neg v
    (non_zero_divisors.coe_ne_zero _), maximal_spectrum.int_valuation_def_if_neg],
  { rw [ne.def, ← @is_fraction_ring.mk'_eq_zero_iff_eq_zero _ _ K _ _ _ _ _],
    convert units.ne_zero k,
    exact classical.some_spec (classical.some_spec
    (maximal_spectrum.valuation_def._proof_1 (k : K))), },
end

/-- `I_K.map_to_fractional_ideals` sends the principal idèle `(k)_v` corresponding to `k ∈ K*` to
the principal fractional ideal generated by `k`. -/
lemma I_K.map_to_fractional_ideals.map_units_K (k : units K) :
  fractional_ideal.span_singleton (non_zero_divisors ↥(ring_of_integers K)) (k : K) =
  ↑((I_K.map_to_fractional_ideals K) ((inj_units_K.group_hom K) k)) :=
I_K_f.map_to_fractional_ideal.map_units k

/-- The kernel of `I_K.map_to_fractional_ideals` contains the principal idèles `(k)_v`, for
`k ∈ K*`. -/
lemma I_K.map_to_class_group.map_units_K (k : units K) :
  I_K.map_to_class_group K ((inj_units_K.group_hom K) k) = 1 :=
begin
  simp only [I_K.map_to_class_group],
  rw [quotient_group.mk'_apply, quotient_group.eq_one_iff, monoid_hom.mem_range],
  simp only [to_principal_ideal_eq_iff],
  use k,
  exact I_K.map_to_fractional_ideals.map_units_K k,
end

/-- Every nonzero element in a field is a unit. -/
def field.units.mk' {F : Type*} [field F] (k : F) (hk : k ≠ 0) : units F :=
{ val     := k,
  inv     := k⁻¹,
  val_inv := mul_inv_cancel hk,
  inv_val := inv_mul_cancel hk}

lemma I_K.map_to_fractional_ideals.apply (x : I_K K) : (((I_K.map_to_fractional_ideals K) x) :
  fractional_ideal (non_zero_divisors ↥(ring_of_integers K)) K) =
  finprod (λ (v : maximal_spectrum ↥(ring_of_integers K)),
    (v.as_ideal : fractional_ideal (non_zero_divisors ↥(ring_of_integers K)) K)^
    finite_idele.to_add_valuations ↥(ring_of_integers K) K ((I_K.fst K) x) v) := rfl

/-- If the image of `x ∈ I_K` under `I_K.map_to_class_group` is the principal fractional ideal
generated by `k ∈ K*`, then for every maximal ideal `v` of the ring of integers of `K`,
`|x_v|_v = |k|_v`. -/
lemma I_K.map_to_class_group.valuation_mem_kernel (x : I_K K) (k : units K)
  (v : maximal_spectrum (ring_of_integers K))
  (hkx : fractional_ideal.span_singleton (non_zero_divisors ↥(ring_of_integers K)) (k : K) =
    (((I_K.map_to_fractional_ideals K) x) :
      fractional_ideal (non_zero_divisors ↥(ring_of_integers K)) K)) :
  valued.v (((I_K.fst K) x).val.val v) = valued.v ((coe : K → v.adic_completion K) k.val) :=
begin
  set nk := classical.some (is_localization.mk'_surjective (non_zero_divisors ↥(ring_of_integers K))
    (k : K)) with h_nk,
  set dk' := classical.some (classical.some_spec (is_localization.mk'_surjective
    (non_zero_divisors ↥(ring_of_integers K)) (k : K))) with h_dk',
  set dk : ↥(ring_of_integers K) := ↑dk' with h_dk,
  have h := classical.some_spec (classical.some_spec (is_localization.mk'_surjective
    (non_zero_divisors ↥(ring_of_integers K)) (k : K))),
  rw [← h_dk', ← h_nk] at h,
  have h_nk_ne_zero : nk ≠ 0,
  { intro h_contr,
    rw [h_contr, is_localization.mk'_zero] at h,
    exact (units.ne_zero k) (eq.symm h)},
  have h_dk_ne_zero : dk ≠ 0,
  { rw h_dk,
    exact non_zero_divisors.coe_ne_zero _, },
  rw I_K.map_to_fractional_ideals.apply at hkx,
  { have h_exps_v:  ((associates.mk v.as_ideal).count
      (associates.mk (ideal.span {nk})).factors : ℤ) -
      ((associates.mk v.as_ideal).count (associates.mk (ideal.span {dk})).factors : ℤ) =
      finite_idele.to_add_valuations ↥(ring_of_integers K) K ((I_K.fst K) x) v,
    { rw [← fractional_ideal.count_finprod K v (finite_idele.to_add_valuations ↥(ring_of_integers K)
        K ((I_K.fst K) x)) (finite_add_support _ _ _), ← hkx,  eq_comm],
      apply fractional_ideal.count_well_defined K v,
      { rw fractional_ideal.span_singleton_ne_zero_iff,
        exact units.ne_zero k, },
      { rw [fractional_ideal.coe_ideal_span_singleton,
          fractional_ideal.span_singleton_mul_span_singleton, ← h, is_fraction_ring.mk'_eq_div,
          h_dk, div_eq_inv_mul], }},
    simp only [finite_idele.to_add_valuations, with_zero.to_integer, eq_neg_iff_eq_neg, neg_sub]
      at h_exps_v,
    conv_rhs {rw [v.valued_adic_completion_def, units.val_eq_coe], },
    rw [valued.extension_extends, v.adic_valued_def],
    simp only [maximal_spectrum.valuation_def],
    rw [← h_dk, ← h_nk, maximal_spectrum.int_valuation_def_if_neg,
    maximal_spectrum.int_valuation_def_if_neg, ← with_zero.coe_div, ← of_add_sub, neg_sub_neg,
    ← h_exps_v, of_add_to_add, eq_comm],
    exact classical.some_spec (with_zero.to_integer._proof_1 _),
    { exact h_dk_ne_zero },
    { exact h_nk_ne_zero }},
end

/-- An element `x ∈ I_K` is in the kernel of `C_K → Cl(K)` if and only if there exist `k ∈ K*` and
`y ∈ I_K` such that `x = k*y` and `|y_v|_v = 1` for all `v`. -/
lemma I_K.map_to_class_group.mem_kernel_iff (x : I_K K) :
  I_K.map_to_class_group K x = 1 ↔ ∃ (k : K) (hk : k ≠ 0),
  ∀ v : maximal_spectrum (ring_of_integers K),
    (finite_idele.to_add_valuations ↥(ring_of_integers K) K ((I_K.fst K) x) v)
    = -with_zero.to_integer (units.valuation_ne_zero (ring_of_integers K) K v hk) :=
begin
  rw [I_K.map_to_class_group, quotient_group.coe_mk', quotient_group.eq_one_iff,
      monoid_hom.mem_range],
    simp_rw [to_principal_ideal_eq_iff],
  refine ⟨λ h, _, λ h, _⟩,
 { obtain ⟨k, hk⟩ := h,
    use (k : K),
    use units.ne_zero k,
    intro v,
    rw [finite_idele.to_add_valuations, neg_inj, with_zero.to_integer,
      with_zero.to_integer, injective.eq_iff multiplicative.to_add.injective],
    apply classical.some_spec2,
    intros a ha,
    rw eq_comm,
    apply classical.some_spec2,
    intros b hb,
    have h_valuations : valued.v (((I_K.fst K) x).val.val v) =
      valued.v ((coe : K → v.adic_completion K) (k : K)),
    { apply I_K.map_to_class_group.valuation_mem_kernel x k v hk },
    rw [← h_valuations, ← hb] at ha,
    rw ← with_zero.coe_inj,
    exact ha, },
  { obtain ⟨k, hk, h_vals⟩ := h,
    use field.units.mk' k hk,
    rw [I_K.map_to_fractional_ideals.map_units_K, I_K.map_to_fractional_ideals,
      I_K_f.map_to_fractional_ideals, map_to_fractional_ideals, monoid_hom.coe_comp,
      comp_app, monoid_hom.coe_mk,map_to_fractional_ideals.def, units.coe_mk],
    simp only [map_to_fractional_ideals.val],
    apply finprod_congr,
    intro v,
    rw h_vals v,
    refl, }
  end

variable (K)
/-- The map `C_K → Cl(K)` induced by `I_K.map_to_class_group`. -/
def C_K.map_to_class_group :
  (C_K K) →* (class_group (ring_of_integers K) K) :=
begin
  apply quotient_group.lift (inj_units_K.group_hom K).range I_K.monoid_hom_to_class_group _,
  { intros x hx,
    obtain ⟨k, hk⟩ := hx,
    rw ← hk,
    exact I_K.map_to_class_group.map_units_K k,},
end

/-- The natural map `C_K → Cl(K)` is surjective. -/
lemma C_K.map_to_class_group.surjective :
  surjective (C_K.map_to_class_group K) :=
begin
  intro y,
  obtain ⟨x, hx⟩ := I_K.map_to_class_group.surjective y,
  use [quotient_group.mk x, hx],
end

/-- The natural map `C_K → Cl(K)` is continuous. -/
lemma C_K.map_to_class_group.continuous :
  continuous (C_K.map_to_class_group K) :=
continuous_quot_lift (quotient_group.lift._proof_1
  (inj_units_K.group_hom K).range I_K.monoid_hom_to_class_group
  (C_K.map_to_class_group._proof_2 K)) I_K.map_to_class_group.continuous

/-- An element `x ∈ C_K` is in the kernel of `C_K → Cl(K)` if and only if `x` comes from an idèle
of the form `k*y`, with `k ∈ K*` and `|y_v|_v = 1` for all `v`. -/
lemma C_K.map_to_class_group.mem_kernel_iff (x : C_K K) :
  C_K.map_to_class_group K x = 1 ↔
  ∃ (k : K) (hk : k ≠ 0), ∀ v : maximal_spectrum (ring_of_integers K),
    (finite_idele.to_add_valuations ↥(ring_of_integers K) K
      ((I_K.fst K) (classical.some (quot.exists_rep x))) v)
      = -with_zero.to_integer (units.valuation_ne_zero (ring_of_integers K) K v hk) :=
begin
  set z := classical.some (quot.exists_rep x) with hz_def,
  have hz := classical.some_spec (quot.exists_rep x),
  have : C_K.map_to_class_group K x = I_K.map_to_class_group K (classical.some (quot.exists_rep x)),
  { rw [← hz_def, ← hz, C_K.map_to_class_group, ← hz_def, quotient_group.lift_quot_mk],
    refl },
  rw this,
  exact I_K.map_to_class_group.mem_kernel_iff _,
end

end number_field


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

! This file was ported from Lean 3 source module group_theory.group_action.units
! leanprover-community/mathlib commit f1a2caaf51ef593799107fe9a8d5e411599f3996
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.GroupTheory.GroupAction.Defs

/-! # Group actions on and by `Mˣ`

This file provides the action of a unit on a type `α`, `SMul Mˣ α`, in the presence of
`SMul M α`, with the obvious definition stated in `Units.smul_def`. This definition preserves
`MulAction` and `DistribMulAction` structures too.

Additionally, a `MulAction G M` for some group `G` satisfying some additional properties admits a
`MulAction G Mˣ` structure, again with the obvious definition stated in `Units.coe_smul`.
These instances use a primed name.

The results are repeated for `AddUnits` and `VAdd` where relevant.
-/

variable {G H M N : Type _} {α : Type _}

namespace Units

/-! ### Action of the units of `M` on a type `α` -/

@[to_additive]
instance [Monoid M] [SMul M α] : SMul Mˣ α where smul m a := (m : M) • a

@[to_additive]
theorem smul_def [Monoid M] [SMul M α] (m : Mˣ) (a : α) : m • a = (m : M) • a :=
  rfl
#align units.smul_def Units.smul_def
#align add_units.vadd_def AddUnits.vadd_def

@[simp]
theorem smul_isUnit [Monoid M] [SMul M α] {m : M} (hm : IsUnit m) (a : α) :
    hm.unit • a = m • a :=
  rfl
#align units.smul_is_unit Units.smul_isUnit

theorem _root_.IsUnit.inv_smul [Monoid α] {a : α} (h : IsUnit a) : h.unit⁻¹ • a = 1 :=
  h.val_inv_mul
#align is_unit.inv_smul IsUnit.inv_smul

@[to_additive]
instance [Monoid M] [SMul M α] [FaithfulSMul M α] : FaithfulSMul Mˣ α where
  eq_of_smul_eq_smul h := Units.ext <| eq_of_smul_eq_smul h

@[to_additive]
instance [Monoid M] [MulAction M α] :
    MulAction Mˣ α where
  one_smul := (one_smul M : _)
  mul_smul m n := mul_smul (m : M) n

instance [Monoid M] [Zero α] [SMulZeroClass M α] :
    SMulZeroClass Mˣ α where
  smul := (· • ·)
  smul_zero m := smul_zero (m : M)

instance [Monoid M] [AddZeroClass α] [DistribSMul M α] :
    DistribSMul Mˣ α where smul_add m := smul_add (m : M)

instance [Monoid M] [AddMonoid α] [DistribMulAction M α] : DistribMulAction Mˣ α :=
  { instDistribSMulUnits with
    one_smul := fun b => one_smul M b
    mul_smul := fun x y b => mul_smul (x : M) y b }

instance [Monoid M] [Monoid α] [MulDistribMulAction M α] :
    MulDistribMulAction Mˣ α where
  smul_mul m := smul_mul' (m : M)
  smul_one m := smul_one (m : M)

instance smulCommClass_left [Monoid M] [SMul M α] [SMul N α] [SMulCommClass M N α] :
    SMulCommClass Mˣ N α where smul_comm m n := (smul_comm (m : M) n : _)
#align units.smul_comm_class_left Units.smulCommClass_left

instance smulCommClass_right [Monoid N] [SMul M α] [SMul N α] [SMulCommClass M N α] :
    SMulCommClass M Nˣ α where smul_comm m n := (smul_comm m (n : N) : _)
#align units.smul_comm_class_right Units.smulCommClass_right

instance [Monoid M] [SMul M N] [SMul M α] [SMul N α] [IsScalarTower M N α] :
    IsScalarTower Mˣ N α where smul_assoc m n := (smul_assoc (m : M) n : _)

/-! ### Action of a group `G` on units of `M` -/

/-- If an action `G` associates and commutes with multiplication on `M`, then it lifts to an
action on `Mˣ`. Notably, this provides `mul_action Mˣ Nˣ` under suitable
conditions.
-/
instance mulAction' [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M]
    [IsScalarTower G M M] :
    MulAction G Mˣ where
  smul g m :=
    ⟨g • (m : M), (g⁻¹ • ((m⁻¹ : Mˣ) : M)),
      by rw [smul_mul_smul, Units.mul_inv, mul_right_inv, one_smul],
      by rw [smul_mul_smul, Units.inv_mul, mul_left_inv, one_smul]⟩
  one_smul m := Units.ext <| one_smul _ _
  mul_smul g₁ g₂ m := Units.ext <| mul_smul _ _ _
#align units.mul_action' Units.mulAction'

@[simp]
theorem val_smul [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    (g : G) (m : Mˣ) : ↑(g • m) = g • (m : M) :=
  rfl
#align units.coe_smul Units.val_smul

/-- Note that this lemma exists more generally as the global `smul_inv` -/
@[simp]
theorem smul_inv [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    (g : G) (m : Mˣ) : (g • m)⁻¹ = g⁻¹ • m⁻¹ :=
  ext rfl
#align units.smul_inv Units.smul_inv

/-- Transfer `SMulCommClass G H M` to `SMulCommClass G H Mˣ` -/
instance smulCommClass' [Group G] [Group H] [Monoid M] [MulAction G M] [SMulCommClass G M M]
    [MulAction H M] [SMulCommClass H M M] [IsScalarTower G M M] [IsScalarTower H M M]
    [SMulCommClass G H M] :
    SMulCommClass G H Mˣ where smul_comm g h m := Units.ext <| smul_comm g h (m : M)
#align units.smul_comm_class' Units.smulCommClass'

/-- Transfer `IsScalarTower G H M` to `IsScalarTower G H Mˣ` -/
instance isScalarTower' [SMul G H] [Group G] [Group H] [Monoid M] [MulAction G M]
    [SMulCommClass G M M] [MulAction H M] [SMulCommClass H M M] [IsScalarTower G M M]
    [IsScalarTower H M M] [IsScalarTower G H M] :
    IsScalarTower G H Mˣ where smul_assoc g h m := Units.ext <| smul_assoc g h (m : M)
#align units.is_scalar_tower' Units.isScalarTower'

/-- Transfer `IsScalarTower G M α` to `IsScalarTower G Mˣ α` -/
instance isScalarTower'_left [Group G] [Monoid M] [MulAction G M] [SMul M α] [SMul G α]
    [SMulCommClass G M M] [IsScalarTower G M M] [IsScalarTower G M α] :
    IsScalarTower G Mˣ α where smul_assoc g m := (smul_assoc g (m : M) : _)
#align units.is_scalar_tower'_left Units.isScalarTower'_left

-- Just to prove this transfers a particularly useful instance.
example [Monoid M] [Monoid N] [MulAction M N] [SMulCommClass M N N] [IsScalarTower M N N] :
    MulAction Mˣ Nˣ :=
  Units.mulAction'

/-- A stronger form of `Units.mul_action'`. -/
instance mulDistribMulAction' [Group G] [Monoid M] [MulDistribMulAction G M] [SMulCommClass G M M]
    [IsScalarTower G M M] : MulDistribMulAction G Mˣ :=
  { Units.mulAction' with
    smul := (· • ·),
    smul_one := fun _ => Units.ext <| smul_one _,
    smul_mul := fun _ _ _ => Units.ext <| smul_mul' _ _ _ }
#align units.mul_distrib_mul_action' Units.mulDistribMulAction'

end Units

theorem IsUnit.smul [Group G] [Monoid M] [MulAction G M] [SMulCommClass G M M] [IsScalarTower G M M]
    {m : M} (g : G) (h : IsUnit m) : IsUnit (g • m) :=
  let ⟨u, hu⟩ := h
  hu ▸ ⟨g • u, Units.val_smul _ _⟩
#align is_unit.smul IsUnit.smul


/-
Copyright (c) 2022 Yuma Mizuno. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yuma Mizuno

! This file was ported from Lean 3 source module category_theory.bicategory.strict
! leanprover-community/mathlib commit dc6c365e751e34d100e80fe6e314c3c3e0fd2988
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.EqToHom
import Mathlib.CategoryTheory.Bicategory.Basic

/-!
# Strict bicategories

A bicategory is called `Strict` if the left unitors, the right unitors, and the associators are
isomorphisms given by equalities.

## Implementation notes

In the literature of category theory, a strict bicategory (usually called a strict 2-category) is
often defined as a bicategory whose left unitors, right unitors, and associators are identities.
We cannot use this definition directly here since the types of 2-morphisms depend on 1-morphisms.
For this reason, we use `eqToIso`, which gives isomorphisms from equalities, instead of
identities.
-/

namespace CategoryTheory

open Bicategory

universe w v u

variable (B : Type u) [Bicategory.{w, v} B]

/-- A bicategory is called `Strict` if the left unitors, the right unitors, and the associators are
isomorphisms given by equalities.
-/
class Bicategory.Strict : Prop where
  /-- Identity morphisms are left identities for composition. -/
  id_comp : ∀ {a b : B} (f : a ⟶ b), 𝟙 a ≫ f = f := by aesop_cat
  /-- Identity morphisms are right identities for composition. -/
  comp_id : ∀ {a b : B} (f : a ⟶ b), f ≫ 𝟙 b = f := by aesop_cat
  /-- Composition in a bicategory is associative. -/
  assoc : ∀ {a b c d : B} (f : a ⟶ b) (g : b ⟶ c) (h : c ⟶ d), (f ≫ g) ≫ h = f ≫ g ≫ h := by
    aesop_cat
  /-- The left unitors are given by equalities -/
  leftUnitor_eqToIso : ∀ {a b : B} (f : a ⟶ b), λ_ f = eqToIso (id_comp f) := by aesop_cat
  /-- The right unitors are given by equalities -/
  rightUnitor_eqToIso : ∀ {a b : B} (f : a ⟶ b), ρ_ f = eqToIso (comp_id f) := by aesop_cat
  /-- The associators are given by equalities -/
  associator_eqToIso :
    ∀ {a b c d : B} (f : a ⟶ b) (g : b ⟶ c) (h : c ⟶ d), α_ f g h = eqToIso (assoc f g h) := by
    aesop_cat
#align category_theory.bicategory.strict CategoryTheory.Bicategory.Strict

-- porting note: not adding simp to:
-- Bicategory.Strict.id_comp
-- Bicategory.Strict.comp_id
-- Bicategory.Strict.assoc
attribute [simp]
   Bicategory.Strict.leftUnitor_eqToIso
   Bicategory.Strict.rightUnitor_eqToIso
   Bicategory.Strict.associator_eqToIso

-- see Note [lower instance priority]
/-- Category structure on a strict bicategory -/
instance (priority := 100) StrictBicategory.category [Bicategory.Strict B] : Category B
    where
  id_comp := Bicategory.Strict.id_comp
  comp_id := Bicategory.Strict.comp_id
  assoc := Bicategory.Strict.assoc
#align category_theory.strict_bicategory.category CategoryTheory.StrictBicategory.category

namespace Bicategory

variable {B}

@[simp]
theorem whiskerLeft_eqToHom {a b c : B} (f : a ⟶ b) {g h : b ⟶ c} (η : g = h) :
    f ◁ eqToHom η = eqToHom (congr_arg₂ (· ≫ ·) rfl η) := by
  cases η
  simp only [whiskerLeft_id, eqToHom_refl]
#align category_theory.bicategory.whisker_left_eq_to_hom
    CategoryTheory.Bicategory.whiskerLeft_eqToHom

@[simp]
theorem eqToHom_whiskerRight {a b c : B} {f g : a ⟶ b} (η : f = g) (h : b ⟶ c) :
    eqToHom η ▷ h = eqToHom (congr_arg₂ (· ≫ ·) η rfl) := by
  cases η
  simp only [id_whiskerRight, eqToHom_refl]
#align category_theory.bicategory.eq_to_hom_whisker_right
    CategoryTheory.Bicategory.eqToHom_whiskerRight

end Bicategory

end CategoryTheory


/-
Copyright (c) 2014 Floris van Doorn (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro

! This file was ported from Lean 3 source module data.nat.basic
! leanprover-community/mathlib commit bd835ef554f37ef9b804f0903089211f89cb370b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.Basic
import Mathbin.Algebra.GroupWithZero.Basic
import Mathbin.Algebra.Ring.Defs

/-!
# Basic operations on the natural numbers

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

This file contains:
- instances on the natural numbers
- some basic lemmas about natural numbers
- extra recursors:
  * `le_rec_on`, `le_induction`: recursion and induction principles starting at non-zero numbers
  * `decreasing_induction`: recursion growing downwards
  * `le_rec_on'`, `decreasing_induction'`: versions with slightly weaker assumptions
  * `strong_rec'`: recursion based on strong inequalities
- decidability instances on predicates about the natural numbers

Many theorems that used to live in this file have been moved to `data.nat.order`,
so that this file requires fewer imports.
For each section here there is a corresponding section in that file with additional results.
It may be possible to move some of these results here, by tweaking their proofs.

-/

universe u v

/-! ### instances -/

instance : Nontrivial ℕ :=
  ⟨⟨0, 1, Nat.zero_ne_one⟩⟩

instance : CommSemiring ℕ where
  add := Nat.add
  add_assoc := Nat.add_assoc
  zero := Nat.zero
  zero_add := Nat.zero_add
  add_zero := Nat.add_zero
  add_comm := Nat.add_comm
  mul := Nat.mul
  mul_assoc := Nat.mul_assoc
  one := Nat.succ Nat.zero
  one_mul := Nat.one_mul
  mul_one := Nat.mul_one
  left_distrib := Nat.left_distrib
  right_distrib := Nat.right_distrib
  zero_mul := Nat.zero_mul
  mul_zero := Nat.mul_zero
  mul_comm := Nat.mul_comm
  natCast n := n
  natCast_zero := rfl
  natCast_succ n := rfl
  nsmul m n := m * n
  nsmul_zero := Nat.zero_mul
  nsmul_succ n x := by rw [Nat.succ_eq_add_one, Nat.add_comm, Nat.right_distrib, Nat.one_mul]

/-! Extra instances to short-circuit type class resolution and ensure computability -/

instance : AddCommMonoid ℕ :=
  inferInstance

instance : AddMonoid ℕ :=
  inferInstance

instance : Monoid ℕ :=
  inferInstance

instance : CommMonoid ℕ :=
  inferInstance

instance : CommSemigroup ℕ :=
  inferInstance

instance : Semigroup ℕ :=
  inferInstance

instance : AddCommSemigroup ℕ :=
  inferInstance

instance : AddSemigroup ℕ :=
  inferInstance

instance : Distrib ℕ :=
  inferInstance

instance : Semiring ℕ :=
  inferInstance

#print Nat.nsmul_eq_mul /-
protected theorem Nat.nsmul_eq_mul (m n : ℕ) : m • n = m * n :=
  rfl
#align nat.nsmul_eq_mul Nat.nsmul_eq_mul
-/

#print Nat.cancelCommMonoidWithZero /-
instance Nat.cancelCommMonoidWithZero : CancelCommMonoidWithZero ℕ :=
  { Nat.commSemiring with
    mul_left_cancel_of_ne_zero := fun _ _ _ h1 h2 =>
      Nat.eq_of_mul_eq_mul_left (Nat.pos_of_ne_zero h1) h2 }
#align nat.cancel_comm_monoid_with_zero Nat.cancelCommMonoidWithZero
-/

attribute [simp]
  Nat.not_lt_zero Nat.succ_ne_zero Nat.succ_ne_self Nat.zero_ne_one Nat.one_ne_zero Nat.zero_ne_bit1 Nat.bit1_ne_zero Nat.bit0_ne_one Nat.one_ne_bit0 Nat.bit0_ne_bit1 Nat.bit1_ne_bit0

variable {m n k : ℕ}

namespace Nat

/-!
### Recursion and `forall`/`exists`
-/

#print Nat.and_forall_succ /-
@[simp]
theorem and_forall_succ {p : ℕ → Prop} : (p 0 ∧ ∀ n, p (n + 1)) ↔ ∀ n, p n :=
  ⟨fun h n => Nat.casesOn n h.1 h.2, fun h => ⟨h _, fun n => h _⟩⟩
#align nat.and_forall_succ Nat.and_forall_succ
-/

#print Nat.or_exists_succ /-
@[simp]
theorem or_exists_succ {p : ℕ → Prop} : (p 0 ∨ ∃ n, p (n + 1)) ↔ ∃ n, p n :=
  ⟨fun h => h.elim (fun h0 => ⟨0, h0⟩) fun ⟨n, hn⟩ => ⟨n + 1, hn⟩,
    by
    rintro ⟨_ | n, hn⟩
    exacts[Or.inl hn, Or.inr ⟨n, hn⟩]⟩
#align nat.or_exists_succ Nat.or_exists_succ
-/

/-! ### `succ` -/

#print LT.lt.nat_succ_le /-
theorem LT.lt.nat_succ_le {n m : ℕ} (h : n < m) : succ n ≤ m :=
  succ_le_of_lt h
#align has_lt.lt.nat_succ_le LT.lt.nat_succ_le
-/

#print Nat.succ_eq_one_add /-
theorem succ_eq_one_add (n : ℕ) : n.succ = 1 + n := by rw [Nat.succ_eq_add_one, Nat.add_comm]
#align nat.succ_eq_one_add Nat.succ_eq_one_add
-/

#print Nat.eq_of_lt_succ_of_not_lt /-
theorem eq_of_lt_succ_of_not_lt {a b : ℕ} (h1 : a < b + 1) (h2 : ¬a < b) : a = b :=
  have h3 : a ≤ b := le_of_lt_succ h1
  Or.elim (eq_or_lt_of_not_lt h2) (fun h => h) fun h => absurd h (not_lt_of_ge h3)
#align nat.eq_of_lt_succ_of_not_lt Nat.eq_of_lt_succ_of_not_lt
-/

#print Nat.eq_of_le_of_lt_succ /-
theorem eq_of_le_of_lt_succ {n m : ℕ} (h₁ : n ≤ m) (h₂ : m < n + 1) : m = n :=
  Nat.le_antisymm (le_of_succ_le_succ h₂) h₁
#align nat.eq_of_le_of_lt_succ Nat.eq_of_le_of_lt_succ
-/

#print Nat.one_add /-
theorem one_add (n : ℕ) : 1 + n = succ n := by simp [add_comm]
#align nat.one_add Nat.one_add
-/

#print Nat.succ_pos' /-
@[simp]
theorem succ_pos' {n : ℕ} : 0 < succ n :=
  succ_pos n
#align nat.succ_pos' Nat.succ_pos'
-/

#print Nat.succ_inj' /-
theorem succ_inj' {n m : ℕ} : succ n = succ m ↔ n = m :=
  ⟨succ.inj, congr_arg _⟩
#align nat.succ_inj' Nat.succ_inj'
-/

#print Nat.succ_injective /-
theorem succ_injective : Function.Injective Nat.succ := fun x y => succ.inj
#align nat.succ_injective Nat.succ_injective
-/

#print Nat.succ_ne_succ /-
theorem succ_ne_succ {n m : ℕ} : succ n ≠ succ m ↔ n ≠ m :=
  succ_injective.ne_iff
#align nat.succ_ne_succ Nat.succ_ne_succ
-/

#print Nat.succ_succ_ne_one /-
@[simp]
theorem succ_succ_ne_one (n : ℕ) : n.succ.succ ≠ 1 :=
  succ_ne_succ.mpr n.succ_ne_zero
#align nat.succ_succ_ne_one Nat.succ_succ_ne_one
-/

#print Nat.one_lt_succ_succ /-
@[simp]
theorem one_lt_succ_succ (n : ℕ) : 1 < n.succ.succ :=
  succ_lt_succ <| succ_pos n
#align nat.one_lt_succ_succ Nat.one_lt_succ_succ
-/

#print Nat.succ_le_succ_iff /-
theorem succ_le_succ_iff {m n : ℕ} : succ m ≤ succ n ↔ m ≤ n :=
  ⟨le_of_succ_le_succ, succ_le_succ⟩
#align nat.succ_le_succ_iff Nat.succ_le_succ_iff
-/

/- warning: nat.max_succ_succ -> Nat.max_succ_succ is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat}, Eq.{1} Nat (LinearOrder.max.{0} Nat Nat.linearOrder (Nat.succ m) (Nat.succ n)) (Nat.succ (LinearOrder.max.{0} Nat Nat.linearOrder m n))
but is expected to have type
  forall {m : Nat} {n : Nat}, Eq.{1} Nat (Max.max.{0} Nat Nat.instMaxNat (Nat.succ m) (Nat.succ n)) (Nat.succ (Max.max.{0} Nat Nat.instMaxNat m n))
Case conversion may be inaccurate. Consider using '#align nat.max_succ_succ Nat.max_succ_succₓ'. -/
theorem max_succ_succ {m n : ℕ} : max (succ m) (succ n) = succ (max m n) :=
  by
  by_cases h1 : m ≤ n
  rw [max_eq_right h1, max_eq_right (succ_le_succ h1)]
  · rw [not_le] at h1
    have h2 := le_of_lt h1
    rw [max_eq_left h2, max_eq_left (succ_le_succ h2)]
#align nat.max_succ_succ Nat.max_succ_succ

#print Nat.not_succ_lt_self /-
theorem not_succ_lt_self {n : ℕ} : ¬succ n < n :=
  not_lt_of_ge (Nat.le_succ _)
#align nat.not_succ_lt_self Nat.not_succ_lt_self
-/

#print Nat.lt_succ_iff /-
theorem lt_succ_iff {m n : ℕ} : m < succ n ↔ m ≤ n :=
  ⟨le_of_lt_succ, lt_succ_of_le⟩
#align nat.lt_succ_iff Nat.lt_succ_iff
-/

#print Nat.succ_le_iff /-
theorem succ_le_iff {m n : ℕ} : succ m ≤ n ↔ m < n :=
  ⟨lt_of_succ_le, succ_le_of_lt⟩
#align nat.succ_le_iff Nat.succ_le_iff
-/

#print Nat.lt_iff_add_one_le /-
theorem lt_iff_add_one_le {m n : ℕ} : m < n ↔ m + 1 ≤ n := by rw [succ_le_iff]
#align nat.lt_iff_add_one_le Nat.lt_iff_add_one_le
-/

#print Nat.lt_add_one_iff /-
-- Just a restatement of `nat.lt_succ_iff` using `+1`.
theorem lt_add_one_iff {a b : ℕ} : a < b + 1 ↔ a ≤ b :=
  lt_succ_iff
#align nat.lt_add_one_iff Nat.lt_add_one_iff
-/

#print Nat.lt_one_add_iff /-
-- A flipped version of `lt_add_one_iff`.
theorem lt_one_add_iff {a b : ℕ} : a < 1 + b ↔ a ≤ b := by simp only [add_comm, lt_succ_iff]
#align nat.lt_one_add_iff Nat.lt_one_add_iff
-/

#print Nat.add_one_le_iff /-
-- This is true reflexively, by the definition of `≤` on ℕ,
-- but it's still useful to have, to convince Lean to change the syntactic type.
theorem add_one_le_iff {a b : ℕ} : a + 1 ≤ b ↔ a < b :=
  Iff.refl _
#align nat.add_one_le_iff Nat.add_one_le_iff
-/

#print Nat.one_add_le_iff /-
theorem one_add_le_iff {a b : ℕ} : 1 + a ≤ b ↔ a < b := by simp only [add_comm, add_one_le_iff]
#align nat.one_add_le_iff Nat.one_add_le_iff
-/

#print Nat.of_le_succ /-
theorem of_le_succ {n m : ℕ} (H : n ≤ m.succ) : n ≤ m ∨ n = m.succ :=
  H.lt_or_eq_dec.imp le_of_lt_succ id
#align nat.of_le_succ Nat.of_le_succ
-/

#print Nat.succ_lt_succ_iff /-
theorem succ_lt_succ_iff {m n : ℕ} : succ m < succ n ↔ m < n :=
  ⟨lt_of_succ_lt_succ, succ_lt_succ⟩
#align nat.succ_lt_succ_iff Nat.succ_lt_succ_iff
-/

#print Nat.div_le_iff_le_mul_add_pred /-
theorem div_le_iff_le_mul_add_pred {m n k : ℕ} (n0 : 0 < n) : m / n ≤ k ↔ m ≤ n * k + (n - 1) :=
  by
  rw [← lt_succ_iff, div_lt_iff_lt_mul n0, succ_mul, mul_comm]
  cases n; · cases n0
  exact lt_succ_iff
#align nat.div_le_iff_le_mul_add_pred Nat.div_le_iff_le_mul_add_pred
-/

#print Nat.two_lt_of_ne /-
theorem two_lt_of_ne : ∀ {n}, n ≠ 0 → n ≠ 1 → n ≠ 2 → 2 < n
  | 0, h, _, _ => (h rfl).elim
  | 1, _, h, _ => (h rfl).elim
  | 2, _, _, h => (h rfl).elim
  | n + 3, _, _, _ => by decide
#align nat.two_lt_of_ne Nat.two_lt_of_ne
-/

#print Nat.forall_lt_succ /-
theorem forall_lt_succ {P : ℕ → Prop} {n : ℕ} : (∀ m < n + 1, P m) ↔ (∀ m < n, P m) ∧ P n := by
  simp only [lt_succ_iff, Decidable.le_iff_eq_or_lt, forall_eq_or_imp, and_comm]
#align nat.forall_lt_succ Nat.forall_lt_succ
-/

/- warning: nat.exists_lt_succ -> Nat.exists_lt_succ is a dubious translation:
lean 3 declaration is
  forall {P : Nat -> Prop} {n : Nat}, Iff (Exists.{1} Nat (fun (m : Nat) => Exists.{0} (LT.lt.{0} Nat Nat.hasLt m (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) (fun (H : LT.lt.{0} Nat Nat.hasLt m (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) => P m))) (Or (Exists.{1} Nat (fun (m : Nat) => Exists.{0} (LT.lt.{0} Nat Nat.hasLt m n) (fun (H : LT.lt.{0} Nat Nat.hasLt m n) => P m))) (P n))
but is expected to have type
  forall {P : Nat -> Prop} {n : Nat}, Iff (Exists.{1} Nat (fun (m : Nat) => And (LT.lt.{0} Nat instLTNat m (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) (P m))) (Or (Exists.{1} Nat (fun (m : Nat) => And (LT.lt.{0} Nat instLTNat m n) (P m))) (P n))
Case conversion may be inaccurate. Consider using '#align nat.exists_lt_succ Nat.exists_lt_succₓ'. -/
theorem exists_lt_succ {P : ℕ → Prop} {n : ℕ} : (∃ m < n + 1, P m) ↔ (∃ m < n, P m) ∨ P n :=
  by
  rw [← not_iff_not]
  push_neg
  exact forall_lt_succ
#align nat.exists_lt_succ Nat.exists_lt_succ

/-! ### `add` -/

#print Nat.add_def /-
-- Sometimes a bare `nat.add` or similar appears as a consequence of unfolding
-- during pattern matching. These lemmas package them back up as typeclass
-- mediated operations.
@[simp]
theorem add_def {a b : ℕ} : Nat.add a b = a + b :=
  rfl
#align nat.add_def Nat.add_def
-/

#print Nat.mul_def /-
@[simp]
theorem mul_def {a b : ℕ} : Nat.mul a b = a * b :=
  rfl
#align nat.mul_def Nat.mul_def
-/

#print Nat.exists_eq_add_of_le /-
theorem exists_eq_add_of_le (h : m ≤ n) : ∃ k : ℕ, n = m + k :=
  ⟨n - m, (Nat.add_sub_of_le h).symm⟩
#align nat.exists_eq_add_of_le Nat.exists_eq_add_of_le
-/

#print Nat.exists_eq_add_of_le' /-
theorem exists_eq_add_of_le' (h : m ≤ n) : ∃ k : ℕ, n = k + m :=
  ⟨n - m, (Nat.sub_add_cancel h).symm⟩
#align nat.exists_eq_add_of_le' Nat.exists_eq_add_of_le'
-/

#print Nat.exists_eq_add_of_lt /-
theorem exists_eq_add_of_lt (h : m < n) : ∃ k : ℕ, n = m + k + 1 :=
  ⟨n - (m + 1), by rw [add_right_comm, Nat.add_sub_of_le h]⟩
#align nat.exists_eq_add_of_lt Nat.exists_eq_add_of_lt
-/

/-! ### `pred` -/

#print Nat.add_succ_sub_one /-
@[simp]
theorem add_succ_sub_one (n m : ℕ) : n + succ m - 1 = n + m := by rw [add_succ, succ_sub_one]
#align nat.add_succ_sub_one Nat.add_succ_sub_one
-/

#print Nat.succ_add_sub_one /-
@[simp]
theorem succ_add_sub_one (n m : ℕ) : succ n + m - 1 = n + m := by rw [succ_add, succ_sub_one]
#align nat.succ_add_sub_one Nat.succ_add_sub_one
-/

#print Nat.pred_eq_sub_one /-
theorem pred_eq_sub_one (n : ℕ) : pred n = n - 1 :=
  rfl
#align nat.pred_eq_sub_one Nat.pred_eq_sub_one
-/

#print Nat.pred_eq_of_eq_succ /-
theorem pred_eq_of_eq_succ {m n : ℕ} (H : m = n.succ) : m.pred = n := by simp [H]
#align nat.pred_eq_of_eq_succ Nat.pred_eq_of_eq_succ
-/

#print Nat.pred_eq_succ_iff /-
@[simp]
theorem pred_eq_succ_iff {n m : ℕ} : pred n = succ m ↔ n = m + 2 := by
  cases n <;> constructor <;> rintro ⟨⟩ <;> rfl
#align nat.pred_eq_succ_iff Nat.pred_eq_succ_iff
-/

#print Nat.pred_sub /-
theorem pred_sub (n m : ℕ) : pred n - m = pred (n - m) := by
  rw [← Nat.sub_one, Nat.sub_sub, one_add, sub_succ]
#align nat.pred_sub Nat.pred_sub
-/

/- warning: nat.le_pred_of_lt -> Nat.le_pred_of_lt is a dubious translation:
lean 3 declaration is
  forall {n : Nat} {m : Nat}, (LT.lt.{0} Nat Nat.hasLt m n) -> (LE.le.{0} Nat Nat.hasLe m (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))
but is expected to have type
  forall {n : Nat} {m : Nat}, (LT.lt.{0} Nat instLTNat n m) -> (LE.le.{0} Nat instLENat n (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))
Case conversion may be inaccurate. Consider using '#align nat.le_pred_of_lt Nat.le_pred_of_ltₓ'. -/
theorem le_pred_of_lt {n m : ℕ} (h : m < n) : m ≤ n - 1 :=
  Nat.sub_le_sub_right h 1
#align nat.le_pred_of_lt Nat.le_pred_of_lt

#print Nat.le_of_pred_lt /-
theorem le_of_pred_lt {m n : ℕ} : pred m < n → m ≤ n :=
  match m with
  | 0 => le_of_lt
  | m + 1 => id
#align nat.le_of_pred_lt Nat.le_of_pred_lt
-/

#print Nat.pred_one_add /-
/-- This ensures that `simp` succeeds on `pred (n + 1) = n`. -/
@[simp]
theorem pred_one_add (n : ℕ) : pred (1 + n) = n := by rw [add_comm, add_one, pred_succ]
#align nat.pred_one_add Nat.pred_one_add
-/

/-! ### `mul` -/

#print Nat.two_mul_ne_two_mul_add_one /-
theorem two_mul_ne_two_mul_add_one {n m} : 2 * n ≠ 2 * m + 1 :=
  mt (congr_arg (· % 2))
    (by rw [add_comm, add_mul_mod_self_left, mul_mod_right, mod_eq_of_lt] <;> simp)
#align nat.two_mul_ne_two_mul_add_one Nat.two_mul_ne_two_mul_add_one
-/

#print Nat.mul_ne_mul_left /-
theorem mul_ne_mul_left {a b c : ℕ} (ha : 0 < a) : b * a ≠ c * a ↔ b ≠ c :=
  (mul_left_injective₀ ha.ne').ne_iff
#align nat.mul_ne_mul_left Nat.mul_ne_mul_left
-/

#print Nat.mul_ne_mul_right /-
theorem mul_ne_mul_right {a b c : ℕ} (ha : 0 < a) : a * b ≠ a * c ↔ b ≠ c :=
  (mul_right_injective₀ ha.ne').ne_iff
#align nat.mul_ne_mul_right Nat.mul_ne_mul_right
-/

#print Nat.mul_right_eq_self_iff /-
theorem mul_right_eq_self_iff {a b : ℕ} (ha : 0 < a) : a * b = a ↔ b = 1 :=
  suffices a * b = a * 1 ↔ b = 1 by rwa [mul_one] at this
  mul_right_inj' ha.ne'
#align nat.mul_right_eq_self_iff Nat.mul_right_eq_self_iff
-/

#print Nat.mul_left_eq_self_iff /-
theorem mul_left_eq_self_iff {a b : ℕ} (hb : 0 < b) : a * b = b ↔ a = 1 := by
  rw [mul_comm, Nat.mul_right_eq_self_iff hb]
#align nat.mul_left_eq_self_iff Nat.mul_left_eq_self_iff
-/

#print Nat.lt_succ_iff_lt_or_eq /-
theorem lt_succ_iff_lt_or_eq {n i : ℕ} : n < i.succ ↔ n < i ∨ n = i :=
  lt_succ_iff.trans Decidable.le_iff_lt_or_eq
#align nat.lt_succ_iff_lt_or_eq Nat.lt_succ_iff_lt_or_eq
-/

/-!
### Recursion and induction principles

This section is here due to dependencies -- the lemmas here require some of the lemmas
proved above, and some of the results in later sections depend on the definitions in this section.
-/

#print Nat.rec_zero /-
@[simp]
theorem rec_zero {C : ℕ → Sort u} (h0 : C 0) (h : ∀ n, C n → C (n + 1)) :
    (Nat.rec h0 h : ∀ n, C n) 0 = h0 :=
  rfl
#align nat.rec_zero Nat.rec_zero
-/

#print Nat.rec_add_one /-
@[simp]
theorem rec_add_one {C : ℕ → Sort u} (h0 : C 0) (h : ∀ n, C n → C (n + 1)) (n : ℕ) :
    (Nat.rec h0 h : ∀ n, C n) (n + 1) = h n ((Nat.rec h0 h : ∀ n, C n) n) :=
  rfl
#align nat.rec_add_one Nat.rec_add_one
-/

#print Nat.leRecOn /-
/-- Recursion starting at a non-zero number: given a map `C k → C (k+1)` for each `k`,
there is a map from `C n` to each `C m`, `n ≤ m`. For a version where the assumption is only made
when `k ≥ n`, see `le_rec_on'`. -/
@[elab_as_elim]
def leRecOn {C : ℕ → Sort u} {n : ℕ} : ∀ {m : ℕ}, n ≤ m → (∀ {k}, C k → C (k + 1)) → C n → C m
  | 0, H, next, x => Eq.recOn (Nat.eq_zero_of_le_zero H) x
  | m + 1, H, next, x =>
    Or.by_cases (of_le_succ H) (fun h : n ≤ m => next <| le_rec_on h (@next) x) fun h : n = m + 1 =>
      Eq.recOn h x
#align nat.le_rec_on Nat.leRecOn
-/

#print Nat.leRecOn_self /-
theorem leRecOn_self {C : ℕ → Sort u} {n} {h : n ≤ n} {next} (x : C n) :
    (leRecOn h next x : C n) = x := by
  cases n <;> unfold le_rec_on Or.by_cases <;> rw [dif_neg n.not_succ_le_self]
#align nat.le_rec_on_self Nat.leRecOn_self
-/

#print Nat.leRecOn_succ /-
theorem leRecOn_succ {C : ℕ → Sort u} {n m} (h1 : n ≤ m) {h2 : n ≤ m + 1} {next} (x : C n) :
    (leRecOn h2 (@next) x : C (m + 1)) = next (leRecOn h1 (@next) x : C m) := by
  conv =>
    lhs
    rw [le_rec_on, Or.by_cases, dif_pos h1]
#align nat.le_rec_on_succ Nat.leRecOn_succ
-/

#print Nat.leRecOn_succ' /-
theorem leRecOn_succ' {C : ℕ → Sort u} {n} {h : n ≤ n + 1} {next} (x : C n) :
    (leRecOn h next x : C (n + 1)) = next x := by rw [le_rec_on_succ (le_refl n), le_rec_on_self]
#align nat.le_rec_on_succ' Nat.leRecOn_succ'
-/

#print Nat.leRecOn_trans /-
theorem leRecOn_trans {C : ℕ → Sort u} {n m k} (hnm : n ≤ m) (hmk : m ≤ k) {next} (x : C n) :
    (leRecOn (le_trans hnm hmk) (@next) x : C k) = leRecOn hmk (@next) (leRecOn hnm (@next) x) :=
  by
  induction' hmk with k hmk ih; · rw [le_rec_on_self]
  rw [le_rec_on_succ (le_trans hnm hmk), ih, le_rec_on_succ]
#align nat.le_rec_on_trans Nat.leRecOn_trans
-/

#print Nat.leRecOn_succ_left /-
theorem leRecOn_succ_left {C : ℕ → Sort u} {n m} (h1 : n ≤ m) (h2 : n + 1 ≤ m)
    {next : ∀ ⦃k⦄, C k → C (k + 1)} (x : C n) :
    (leRecOn h2 next (next x) : C m) = (leRecOn h1 next x : C m) := by
  rw [Subsingleton.elim h1 (le_trans (le_succ n) h2), le_rec_on_trans (le_succ n) h2,
    le_rec_on_succ']
#align nat.le_rec_on_succ_left Nat.leRecOn_succ_left
-/

#print Nat.leRecOn_injective /-
theorem leRecOn_injective {C : ℕ → Sort u} {n m} (hnm : n ≤ m) (next : ∀ n, C n → C (n + 1))
    (Hnext : ∀ n, Function.Injective (next n)) : Function.Injective (leRecOn hnm next) :=
  by
  induction' hnm with m hnm ih;
  · intro x y H
    rwa [le_rec_on_self, le_rec_on_self] at H
  intro x y H; rw [le_rec_on_succ hnm, le_rec_on_succ hnm] at H; exact ih (Hnext _ H)
#align nat.le_rec_on_injective Nat.leRecOn_injective
-/

#print Nat.leRecOn_surjective /-
theorem leRecOn_surjective {C : ℕ → Sort u} {n m} (hnm : n ≤ m) (next : ∀ n, C n → C (n + 1))
    (Hnext : ∀ n, Function.Surjective (next n)) : Function.Surjective (leRecOn hnm next) :=
  by
  induction' hnm with m hnm ih;
  · intro x
    use x
    rw [le_rec_on_self]
  intro x; rcases Hnext _ x with ⟨w, rfl⟩; rcases ih w with ⟨x, rfl⟩; use x; rw [le_rec_on_succ]
#align nat.le_rec_on_surjective Nat.leRecOn_surjective
-/

#print Nat.strongRec' /-
/-- Recursion principle based on `<`. -/
@[elab_as_elim]
protected def strongRec' {p : ℕ → Sort u} (H : ∀ n, (∀ m, m < n → p m) → p n) : ∀ n : ℕ, p n
  | n => H n fun m hm => strong_rec' m
#align nat.strong_rec' Nat.strongRec'
-/

#print Nat.strongRecOn' /-
/-- Recursion principle based on `<` applied to some natural number. -/
@[elab_as_elim]
def strongRecOn' {P : ℕ → Sort _} (n : ℕ) (h : ∀ n, (∀ m, m < n → P m) → P n) : P n :=
  Nat.strongRec' h n
#align nat.strong_rec_on' Nat.strongRecOn'
-/

#print Nat.strongRecOn'_beta /-
theorem strongRecOn'_beta {P : ℕ → Sort _} {h} {n : ℕ} :
    (strongRecOn' n h : P n) = h n fun m hmn => (strongRecOn' m h : P m) :=
  by
  simp only [strong_rec_on']
  rw [Nat.strongRec']
#align nat.strong_rec_on_beta' Nat.strongRecOn'_beta
-/

/- warning: nat.le_induction -> Nat.le_induction is a dubious translation:
lean 3 declaration is
  forall {P : Nat -> Prop} {m : Nat}, (P m) -> (forall (n : Nat), (LE.le.{0} Nat Nat.hasLe m n) -> (P n) -> (P (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) -> (forall (n : Nat), (LE.le.{0} Nat Nat.hasLe m n) -> (P n))
but is expected to have type
  forall {P : Nat} {m : forall (n : Nat), (LE.le.{0} Nat instLENat P n) -> Prop}, (m P (le_rfl.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder)) P)) -> (forall (n : Nat) (ᾰ : LE.le.{0} Nat instLENat P n), (m n ᾰ) -> (m (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (LE.le.trans.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder)) P n (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) ᾰ (Nat.le_succ n)))) -> (forall (n : Nat) (ᾰ : LE.le.{0} Nat instLENat P n), m n ᾰ)
Case conversion may be inaccurate. Consider using '#align nat.le_induction Nat.le_inductionₓ'. -/
/-- Induction principle starting at a non-zero number. For maps to a `Sort*` see `le_rec_on`. -/
@[elab_as_elim]
theorem le_induction {P : Nat → Prop} {m} (h0 : P m) (h1 : ∀ n, m ≤ n → P n → P (n + 1)) :
    ∀ n, m ≤ n → P n := by apply Nat.le.ndrec h0 <;> exact h1
#align nat.le_induction Nat.le_induction

#print Nat.decreasingInduction /-
/-- Decreasing induction: if `P (k+1)` implies `P k`, then `P n` implies `P m` for all `m ≤ n`.
Also works for functions to `Sort*`. For a version assuming only the assumption for `k < n`, see
`decreasing_induction'`. -/
@[elab_as_elim]
def decreasingInduction {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n)
    (hP : P n) : P m :=
  leRecOn mn (fun k ih hsk => ih <| h k hsk) (fun h => h) hP
#align nat.decreasing_induction Nat.decreasingInduction
-/

#print Nat.decreasingInduction_self /-
@[simp]
theorem decreasingInduction_self {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {n : ℕ} (nn : n ≤ n)
    (hP : P n) : (decreasingInduction h nn hP : P n) = hP :=
  by
  dsimp only [decreasing_induction]
  rw [le_rec_on_self]
#align nat.decreasing_induction_self Nat.decreasingInduction_self
-/

#print Nat.decreasingInduction_succ /-
theorem decreasingInduction_succ {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ} (mn : m ≤ n)
    (msn : m ≤ n + 1) (hP : P (n + 1)) :
    (decreasingInduction h msn hP : P m) = decreasingInduction h mn (h n hP) :=
  by
  dsimp only [decreasing_induction]
  rw [le_rec_on_succ]
#align nat.decreasing_induction_succ Nat.decreasingInduction_succ
-/

#print Nat.decreasingInduction_succ' /-
@[simp]
theorem decreasingInduction_succ' {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m : ℕ}
    (msm : m ≤ m + 1) (hP : P (m + 1)) : (decreasingInduction h msm hP : P m) = h m hP :=
  by
  dsimp only [decreasing_induction]
  rw [le_rec_on_succ']
#align nat.decreasing_induction_succ' Nat.decreasingInduction_succ'
-/

#print Nat.decreasingInduction_trans /-
theorem decreasingInduction_trans {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n k : ℕ}
    (mn : m ≤ n) (nk : n ≤ k) (hP : P k) :
    (decreasingInduction h (le_trans mn nk) hP : P m) =
      decreasingInduction h mn (decreasingInduction h nk hP) :=
  by
  induction' nk with k nk ih
  rw [decreasing_induction_self]
  rw [decreasing_induction_succ h (le_trans mn nk), ih, decreasing_induction_succ]
#align nat.decreasing_induction_trans Nat.decreasingInduction_trans
-/

#print Nat.decreasingInduction_succ_left /-
theorem decreasingInduction_succ_left {P : ℕ → Sort _} (h : ∀ n, P (n + 1) → P n) {m n : ℕ}
    (smn : m + 1 ≤ n) (mn : m ≤ n) (hP : P n) :
    (decreasingInduction h mn hP : P m) = h m (decreasingInduction h smn hP) := by
  rw [Subsingleton.elim mn (le_trans (le_succ m) smn), decreasing_induction_trans,
    decreasing_induction_succ']
#align nat.decreasing_induction_succ_left Nat.decreasingInduction_succ_left
-/

#print Nat.strongSubRecursion /-
/-- Given `P : ℕ → ℕ → Sort*`, if for all `a b : ℕ` we can extend `P` from the rectangle
strictly below `(a,b)` to `P a b`, then we have `P n m` for all `n m : ℕ`.
Note that for non-`Prop` output it is preferable to use the equation compiler directly if possible,
since this produces equation lemmas. -/
@[elab_as_elim]
def strongSubRecursion {P : ℕ → ℕ → Sort _} (H : ∀ a b, (∀ x y, x < a → y < b → P x y) → P a b) :
    ∀ n m : ℕ, P n m
  | n, m => H n m fun x y hx hy => strong_sub_recursion x y
#align nat.strong_sub_recursion Nat.strongSubRecursion
-/

#print Nat.pincerRecursion /-
/-- Given `P : ℕ → ℕ → Sort*`, if we have `P i 0` and `P 0 i` for all `i : ℕ`,
and for any `x y : ℕ` we can extend `P` from `(x,y+1)` and `(x+1,y)` to `(x+1,y+1)`
then we have `P n m` for all `n m : ℕ`.
Note that for non-`Prop` output it is preferable to use the equation compiler directly if possible,
since this produces equation lemmas. -/
@[elab_as_elim]
def pincerRecursion {P : ℕ → ℕ → Sort _} (Ha0 : ∀ a : ℕ, P a 0) (H0b : ∀ b : ℕ, P 0 b)
    (H : ∀ x y : ℕ, P x y.succ → P x.succ y → P x.succ y.succ) : ∀ n m : ℕ, P n m
  | a, 0 => Ha0 a
  | 0, b => H0b b
  | Nat.succ a, Nat.succ b => H _ _ (pincer_recursion _ _) (pincer_recursion _ _)
#align nat.pincer_recursion Nat.pincerRecursion
-/

#print Nat.leRecOn' /-
/-- Recursion starting at a non-zero number: given a map `C k → C (k+1)` for each `k ≥ n`,
there is a map from `C n` to each `C m`, `n ≤ m`. -/
@[elab_as_elim]
def leRecOn' {C : ℕ → Sort _} {n : ℕ} :
    ∀ {m : ℕ}, n ≤ m → (∀ ⦃k⦄, n ≤ k → C k → C (k + 1)) → C n → C m
  | 0, H, next, x => Eq.recOn (Nat.eq_zero_of_le_zero H) x
  | m + 1, H, next, x =>
    Or.by_cases (of_le_succ H) (fun h : n ≤ m => next h <| le_rec_on' h next x) fun h : n = m + 1 =>
      Eq.recOn h x
#align nat.le_rec_on' Nat.leRecOn'
-/

#print Nat.decreasingInduction' /-
/-- Decreasing induction: if `P (k+1)` implies `P k` for all `m ≤ k < n`, then `P n` implies `P m`.
Also works for functions to `Sort*`. Weakens the assumptions of `decreasing_induction`. -/
@[elab_as_elim]
def decreasingInduction' {P : ℕ → Sort _} {m n : ℕ} (h : ∀ k < n, m ≤ k → P (k + 1) → P k)
    (mn : m ≤ n) (hP : P n) : P m :=
  by
  -- induction mn using nat.le_rec_on' generalizing h hP -- this doesn't work unfortunately
    refine' le_rec_on' mn _ _ h hP <;>
    clear h hP mn n
  · intro n mn ih h hP
    apply ih
    · exact fun k hk => h k hk.step
    · exact h n (lt_succ_self n) mn hP
  · intro h hP
    exact hP
#align nat.decreasing_induction' Nat.decreasingInduction'
-/

/-! ### `div` -/

attribute [simp] Nat.div_self

#print Nat.div_lt_self' /-
/-- A version of `nat.div_lt_self` using successors, rather than additional hypotheses. -/
theorem div_lt_self' (n b : ℕ) : (n + 1) / (b + 2) < n + 1 :=
  Nat.div_lt_self (Nat.succ_pos n) (Nat.succ_lt_succ (Nat.succ_pos _))
#align nat.div_lt_self' Nat.div_lt_self'
-/

#print Nat.le_div_iff_mul_le' /-
theorem le_div_iff_mul_le' {x y : ℕ} {k : ℕ} (k0 : 0 < k) : x ≤ y / k ↔ x * k ≤ y :=
  le_div_iff_mul_le k0
#align nat.le_div_iff_mul_le' Nat.le_div_iff_mul_le'
-/

#print Nat.div_lt_iff_lt_mul' /-
theorem div_lt_iff_lt_mul' {x y : ℕ} {k : ℕ} (k0 : 0 < k) : x / k < y ↔ x < y * k :=
  lt_iff_lt_of_le_iff_le <| le_div_iff_mul_le' k0
#align nat.div_lt_iff_lt_mul' Nat.div_lt_iff_lt_mul'
-/

#print Nat.one_le_div_iff /-
theorem one_le_div_iff {a b : ℕ} (hb : 0 < b) : 1 ≤ a / b ↔ b ≤ a := by
  rw [le_div_iff_mul_le hb, one_mul]
#align nat.one_le_div_iff Nat.one_le_div_iff
-/

#print Nat.div_lt_one_iff /-
theorem div_lt_one_iff {a b : ℕ} (hb : 0 < b) : a / b < 1 ↔ a < b :=
  lt_iff_lt_of_le_iff_le <| one_le_div_iff hb
#align nat.div_lt_one_iff Nat.div_lt_one_iff
-/

#print Nat.div_le_div_right /-
protected theorem div_le_div_right {n m : ℕ} (h : n ≤ m) {k : ℕ} : n / k ≤ m / k :=
  (Nat.eq_zero_or_pos k).elim (fun k0 => by simp [k0]) fun hk =>
    (le_div_iff_mul_le' hk).2 <| le_trans (Nat.div_mul_le_self _ _) h
#align nat.div_le_div_right Nat.div_le_div_right
-/

#print Nat.lt_of_div_lt_div /-
theorem lt_of_div_lt_div {m n k : ℕ} : m / k < n / k → m < n :=
  lt_imp_lt_of_le_imp_le fun h => Nat.div_le_div_right h
#align nat.lt_of_div_lt_div Nat.lt_of_div_lt_div
-/

#print Nat.div_pos /-
protected theorem div_pos {a b : ℕ} (hba : b ≤ a) (hb : 0 < b) : 0 < a / b :=
  Nat.pos_of_ne_zero fun h =>
    lt_irrefl a
      (calc
        a = a % b := by simpa [h] using (mod_add_div a b).symm
        _ < b := (Nat.mod_lt a hb)
        _ ≤ a := hba
        )
#align nat.div_pos Nat.div_pos
-/

#print Nat.lt_mul_of_div_lt /-
theorem lt_mul_of_div_lt {a b c : ℕ} (h : a / c < b) (w : 0 < c) : a < b * c :=
  lt_of_not_ge <| not_le_of_gt h ∘ (Nat.le_div_iff_mul_le w).2
#align nat.lt_mul_of_div_lt Nat.lt_mul_of_div_lt
-/

#print Nat.mul_div_le_mul_div_assoc /-
theorem mul_div_le_mul_div_assoc (a b c : ℕ) : a * (b / c) ≤ a * b / c :=
  if hc0 : c = 0 then by simp [hc0]
  else
    (Nat.le_div_iff_mul_le (Nat.pos_of_ne_zero hc0)).2
      (by rw [mul_assoc] <;> exact Nat.mul_le_mul_left _ (Nat.div_mul_le_self _ _))
#align nat.mul_div_le_mul_div_assoc Nat.mul_div_le_mul_div_assoc
-/

#print Nat.eq_mul_of_div_eq_right /-
protected theorem eq_mul_of_div_eq_right {a b c : ℕ} (H1 : b ∣ a) (H2 : a / b = c) : a = b * c := by
  rw [← H2, Nat.mul_div_cancel' H1]
#align nat.eq_mul_of_div_eq_right Nat.eq_mul_of_div_eq_right
-/

#print Nat.div_eq_iff_eq_mul_right /-
protected theorem div_eq_iff_eq_mul_right {a b c : ℕ} (H : 0 < b) (H' : b ∣ a) :
    a / b = c ↔ a = b * c :=
  ⟨Nat.eq_mul_of_div_eq_right H', Nat.div_eq_of_eq_mul_right H⟩
#align nat.div_eq_iff_eq_mul_right Nat.div_eq_iff_eq_mul_right
-/

#print Nat.div_eq_iff_eq_mul_left /-
protected theorem div_eq_iff_eq_mul_left {a b c : ℕ} (H : 0 < b) (H' : b ∣ a) :
    a / b = c ↔ a = c * b := by rw [mul_comm] <;> exact Nat.div_eq_iff_eq_mul_right H H'
#align nat.div_eq_iff_eq_mul_left Nat.div_eq_iff_eq_mul_left
-/

#print Nat.eq_mul_of_div_eq_left /-
protected theorem eq_mul_of_div_eq_left {a b c : ℕ} (H1 : b ∣ a) (H2 : a / b = c) : a = c * b := by
  rw [mul_comm, Nat.eq_mul_of_div_eq_right H1 H2]
#align nat.eq_mul_of_div_eq_left Nat.eq_mul_of_div_eq_left
-/

#print Nat.mul_div_cancel_left' /-
protected theorem mul_div_cancel_left' {a b : ℕ} (Hd : a ∣ b) : a * (b / a) = b := by
  rw [mul_comm, Nat.div_mul_cancel Hd]
#align nat.mul_div_cancel_left' Nat.mul_div_cancel_left'
-/

/- warning: nat.mul_div_mul_left -> Nat.mul_div_mul_left is a dubious translation:
lean 3 declaration is
  forall (a : Nat) (b : Nat) {c : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) c) -> (Eq.{1} Nat (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) c a) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) c b)) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) a b))
but is expected to have type
  forall {a : Nat} (b : Nat) (c : Nat), (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) -> (Eq.{1} Nat (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) a b) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) a c)) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) b c))
Case conversion may be inaccurate. Consider using '#align nat.mul_div_mul_left Nat.mul_div_mul_leftₓ'. -/
--TODO: Update `nat.mul_div_mul` in the core?
/-- Alias of `nat.mul_div_mul` -/
protected theorem mul_div_mul_left (a b : ℕ) {c : ℕ} (hc : 0 < c) : c * a / (c * b) = a / b :=
  Nat.mul_div_mul a b hc
#align nat.mul_div_mul_left Nat.mul_div_mul_left

/- warning: nat.mul_div_mul_right -> Nat.mul_div_mul_right is a dubious translation:
lean 3 declaration is
  forall (a : Nat) (b : Nat) {c : Nat}, (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))) c) -> (Eq.{1} Nat (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) a c) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) b c)) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) a b))
but is expected to have type
  forall {a : Nat} (b : Nat) (c : Nat), (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)) a) -> (Eq.{1} Nat (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) b a) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) c a)) (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) b c))
Case conversion may be inaccurate. Consider using '#align nat.mul_div_mul_right Nat.mul_div_mul_rightₓ'. -/
protected theorem mul_div_mul_right (a b : ℕ) {c : ℕ} (hc : 0 < c) : a * c / (b * c) = a / b := by
  rw [mul_comm, mul_comm b, a.mul_div_mul_left b hc]
#align nat.mul_div_mul_right Nat.mul_div_mul_right

#print Nat.lt_div_mul_add /-
theorem lt_div_mul_add {a b : ℕ} (hb : 0 < b) : a < a / b * b + b :=
  by
  rw [← Nat.succ_mul, ← Nat.div_lt_iff_lt_mul hb]
  exact Nat.lt_succ_self _
#align nat.lt_div_mul_add Nat.lt_div_mul_add
-/

#print Nat.div_left_inj /-
@[simp]
protected theorem div_left_inj {a b d : ℕ} (hda : d ∣ a) (hdb : d ∣ b) : a / d = b / d ↔ a = b :=
  by
  refine' ⟨fun h => _, congr_arg _⟩
  rw [← Nat.mul_div_cancel' hda, ← Nat.mul_div_cancel' hdb, h]
#align nat.div_left_inj Nat.div_left_inj
-/

/-! ### `mod`, `dvd` -/

#print Nat.mod_eq_iff_lt /-
theorem mod_eq_iff_lt {a b : ℕ} (h : b ≠ 0) : a % b = a ↔ a < b :=
  by
  cases b; contradiction
  exact ⟨fun h => h.ge.trans_lt (mod_lt _ (succ_pos _)), mod_eq_of_lt⟩
#align nat.mod_eq_iff_lt Nat.mod_eq_iff_lt
-/

#print Nat.mod_succ_eq_iff_lt /-
@[simp]
theorem mod_succ_eq_iff_lt {a b : ℕ} : a % b.succ = a ↔ a < b.succ :=
  mod_eq_iff_lt (succ_ne_zero _)
#align nat.mod_succ_eq_iff_lt Nat.mod_succ_eq_iff_lt
-/

#print Nat.div_add_mod /-
theorem div_add_mod (m k : ℕ) : k * (m / k) + m % k = m :=
  (Nat.add_comm _ _).trans (mod_add_div _ _)
#align nat.div_add_mod Nat.div_add_mod
-/

#print Nat.mod_add_div' /-
theorem mod_add_div' (m k : ℕ) : m % k + m / k * k = m :=
  by
  rw [mul_comm]
  exact mod_add_div _ _
#align nat.mod_add_div' Nat.mod_add_div'
-/

#print Nat.div_add_mod' /-
theorem div_add_mod' (m k : ℕ) : m / k * k + m % k = m :=
  by
  rw [mul_comm]
  exact div_add_mod _ _
#align nat.div_add_mod' Nat.div_add_mod'
-/

#print Nat.div_mod_unique /-
/-- See also `nat.div_mod_equiv` for a similar statement as an `equiv`. -/
protected theorem div_mod_unique {n k m d : ℕ} (h : 0 < k) :
    n / k = d ∧ n % k = m ↔ m + k * d = n ∧ m < k :=
  ⟨fun ⟨e₁, e₂⟩ => e₁ ▸ e₂ ▸ ⟨mod_add_div _ _, mod_lt _ h⟩, fun ⟨h₁, h₂⟩ =>
    h₁ ▸ by
      rw [add_mul_div_left _ _ h, add_mul_mod_self_left] <;> simp [div_eq_of_lt, mod_eq_of_lt, h₂]⟩
#align nat.div_mod_unique Nat.div_mod_unique
-/

#print Nat.dvd_add_left /-
protected theorem dvd_add_left {k m n : ℕ} (h : k ∣ n) : k ∣ m + n ↔ k ∣ m :=
  (Nat.dvd_add_iff_left h).symm
#align nat.dvd_add_left Nat.dvd_add_left
-/

#print Nat.dvd_add_right /-
protected theorem dvd_add_right {k m n : ℕ} (h : k ∣ m) : k ∣ m + n ↔ k ∣ n :=
  (Nat.dvd_add_iff_right h).symm
#align nat.dvd_add_right Nat.dvd_add_right
-/

#print Nat.mul_dvd_mul_iff_left /-
protected theorem mul_dvd_mul_iff_left {a b c : ℕ} (ha : 0 < a) : a * b ∣ a * c ↔ b ∣ c :=
  exists_congr fun d => by rw [mul_assoc, mul_right_inj' ha.ne']
#align nat.mul_dvd_mul_iff_left Nat.mul_dvd_mul_iff_left
-/

#print Nat.mul_dvd_mul_iff_right /-
protected theorem mul_dvd_mul_iff_right {a b c : ℕ} (hc : 0 < c) : a * c ∣ b * c ↔ a ∣ b :=
  exists_congr fun d => by rw [mul_right_comm, mul_left_inj' hc.ne']
#align nat.mul_dvd_mul_iff_right Nat.mul_dvd_mul_iff_right
-/

#print Nat.mod_mod_of_dvd /-
@[simp]
theorem mod_mod_of_dvd (n : Nat) {m k : Nat} (h : m ∣ k) : n % k % m = n % m :=
  by
  conv =>
    rhs
    rw [← mod_add_div n k]
  rcases h with ⟨t, rfl⟩; rw [mul_assoc, add_mul_mod_self_left]
#align nat.mod_mod_of_dvd Nat.mod_mod_of_dvd
-/

#print Nat.mod_mod /-
@[simp]
theorem mod_mod (a n : ℕ) : a % n % n = a % n :=
  (Nat.eq_zero_or_pos n).elim (fun n0 => by simp [n0]) fun npos => mod_eq_of_lt (mod_lt _ npos)
#align nat.mod_mod Nat.mod_mod
-/

#print Nat.mod_add_mod /-
@[simp]
theorem mod_add_mod (m n k : ℕ) : (m % n + k) % n = (m + k) % n := by
  have := (add_mul_mod_self_left (m % n + k) n (m / n)).symm <;>
    rwa [add_right_comm, mod_add_div] at this
#align nat.mod_add_mod Nat.mod_add_mod
-/

#print Nat.add_mod_mod /-
@[simp]
theorem add_mod_mod (m n k : ℕ) : (m + n % k) % k = (m + n) % k := by
  rw [add_comm, mod_add_mod, add_comm]
#align nat.add_mod_mod Nat.add_mod_mod
-/

#print Nat.add_mod /-
theorem add_mod (a b n : ℕ) : (a + b) % n = (a % n + b % n) % n := by rw [add_mod_mod, mod_add_mod]
#align nat.add_mod Nat.add_mod
-/

#print Nat.add_mod_eq_add_mod_right /-
theorem add_mod_eq_add_mod_right {m n k : ℕ} (i : ℕ) (H : m % n = k % n) :
    (m + i) % n = (k + i) % n := by rw [← mod_add_mod, ← mod_add_mod k, H]
#align nat.add_mod_eq_add_mod_right Nat.add_mod_eq_add_mod_right
-/

#print Nat.add_mod_eq_add_mod_left /-
theorem add_mod_eq_add_mod_left {m n k : ℕ} (i : ℕ) (H : m % n = k % n) :
    (i + m) % n = (i + k) % n := by rw [add_comm, add_mod_eq_add_mod_right _ H, add_comm]
#align nat.add_mod_eq_add_mod_left Nat.add_mod_eq_add_mod_left
-/

#print Nat.mul_mod /-
theorem mul_mod (a b n : ℕ) : a * b % n = a % n * (b % n) % n := by
  conv_lhs =>
    rw [← mod_add_div a n, ← mod_add_div' b n, right_distrib, left_distrib, left_distrib, mul_assoc,
      mul_assoc, ← left_distrib n _ _, add_mul_mod_self_left, ← mul_assoc, add_mul_mod_self_right]
#align nat.mul_mod Nat.mul_mod
-/

#print Nat.mul_dvd_of_dvd_div /-
theorem mul_dvd_of_dvd_div {a b c : ℕ} (hab : c ∣ b) (h : a ∣ b / c) : c * a ∣ b :=
  have h1 : ∃ d, b / c = a * d := h
  have h2 : ∃ e, b = c * e := hab
  let ⟨d, hd⟩ := h1
  let ⟨e, he⟩ := h2
  have h3 : b = a * d * c := Nat.eq_mul_of_div_eq_left hab hd
  show ∃ d, b = c * a * d from ⟨d, by cc⟩
#align nat.mul_dvd_of_dvd_div Nat.mul_dvd_of_dvd_div
-/

#print Nat.eq_of_dvd_of_div_eq_one /-
theorem eq_of_dvd_of_div_eq_one {a b : ℕ} (w : a ∣ b) (h : b / a = 1) : a = b := by
  rw [← Nat.div_mul_cancel w, h, one_mul]
#align nat.eq_of_dvd_of_div_eq_one Nat.eq_of_dvd_of_div_eq_one
-/

#print Nat.eq_zero_of_dvd_of_div_eq_zero /-
theorem eq_zero_of_dvd_of_div_eq_zero {a b : ℕ} (w : a ∣ b) (h : b / a = 0) : b = 0 := by
  rw [← Nat.div_mul_cancel w, h, MulZeroClass.zero_mul]
#align nat.eq_zero_of_dvd_of_div_eq_zero Nat.eq_zero_of_dvd_of_div_eq_zero
-/

#print Nat.div_le_div_left /-
theorem div_le_div_left {a b c : ℕ} (h₁ : c ≤ b) (h₂ : 0 < c) : a / b ≤ a / c :=
  (Nat.le_div_iff_mul_le h₂).2 <| le_trans (Nat.mul_le_mul_left _ h₁) (div_mul_le_self _ _)
#align nat.div_le_div_left Nat.div_le_div_left
-/

#print Nat.lt_iff_le_pred /-
theorem lt_iff_le_pred : ∀ {m n : ℕ}, 0 < n → (m < n ↔ m ≤ n - 1)
  | m, n + 1, _ => lt_succ_iff
#align nat.lt_iff_le_pred Nat.lt_iff_le_pred
-/

#print Nat.mul_div_le /-
theorem mul_div_le (m n : ℕ) : n * (m / n) ≤ m :=
  by
  cases' Nat.eq_zero_or_pos n with n0 h
  · rw [n0, MulZeroClass.zero_mul]
    exact m.zero_le
  · rw [mul_comm, ← Nat.le_div_iff_mul_le' h]
#align nat.mul_div_le Nat.mul_div_le
-/

#print Nat.lt_mul_div_succ /-
theorem lt_mul_div_succ (m : ℕ) {n : ℕ} (n0 : 0 < n) : m < n * (m / n + 1) :=
  by
  rw [mul_comm, ← Nat.div_lt_iff_lt_mul' n0]
  exact lt_succ_self _
#align nat.lt_mul_div_succ Nat.lt_mul_div_succ
-/

#print Nat.mul_add_mod /-
theorem mul_add_mod (a b c : ℕ) : (a * b + c) % b = c % b := by simp [Nat.add_mod]
#align nat.mul_add_mod Nat.mul_add_mod
-/

#print Nat.mul_add_mod_of_lt /-
theorem mul_add_mod_of_lt {a b c : ℕ} (h : c < b) : (a * b + c) % b = c := by
  rw [Nat.mul_add_mod, Nat.mod_eq_of_lt h]
#align nat.mul_add_mod_of_lt Nat.mul_add_mod_of_lt
-/

#print Nat.pred_eq_self_iff /-
theorem pred_eq_self_iff {n : ℕ} : n.pred = n ↔ n = 0 := by
  cases n <;> simp [(Nat.succ_ne_self _).symm]
#align nat.pred_eq_self_iff Nat.pred_eq_self_iff
-/

/-! ### `find` -/

section Find

variable {p q : ℕ → Prop} [DecidablePred p] [DecidablePred q]

#print Nat.find_eq_iff /-
theorem find_eq_iff (h : ∃ n : ℕ, p n) : Nat.find h = m ↔ p m ∧ ∀ n < m, ¬p n :=
  by
  constructor
  · rintro rfl
    exact ⟨Nat.find_spec h, fun _ => Nat.find_min h⟩
  · rintro ⟨hm, hlt⟩
    exact le_antisymm (Nat.find_min' h hm) (not_lt.1 <| imp_not_comm.1 (hlt _) <| Nat.find_spec h)
#align nat.find_eq_iff Nat.find_eq_iff
-/

/- warning: nat.find_lt_iff -> Nat.find_lt_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (h : Exists.{1} Nat (fun (n : Nat) => p n)) (n : Nat), Iff (LT.lt.{0} Nat Nat.hasLt (Nat.find (fun (n : Nat) => p n) (fun (a : Nat) => _inst_1 a) h) n) (Exists.{1} Nat (fun (m : Nat) => Exists.{0} (LT.lt.{0} Nat Nat.hasLt m n) (fun (H : LT.lt.{0} Nat Nat.hasLt m n) => p m)))
but is expected to have type
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (h : Exists.{1} Nat (fun (n : Nat) => p n)) (n : Nat), Iff (LT.lt.{0} Nat instLTNat (Nat.find (fun (n : Nat) => p n) (fun (a : Nat) => _inst_1 a) h) n) (Exists.{1} Nat (fun (m : Nat) => And (LT.lt.{0} Nat instLTNat m n) (p m)))
Case conversion may be inaccurate. Consider using '#align nat.find_lt_iff Nat.find_lt_iffₓ'. -/
@[simp]
theorem find_lt_iff (h : ∃ n : ℕ, p n) (n : ℕ) : Nat.find h < n ↔ ∃ m < n, p m :=
  ⟨fun h2 => ⟨Nat.find h, h2, Nat.find_spec h⟩, fun ⟨m, hmn, hm⟩ =>
    (Nat.find_min' h hm).trans_lt hmn⟩
#align nat.find_lt_iff Nat.find_lt_iff

/- warning: nat.find_le_iff -> Nat.find_le_iff is a dubious translation:
lean 3 declaration is
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (h : Exists.{1} Nat (fun (n : Nat) => p n)) (n : Nat), Iff (LE.le.{0} Nat Nat.hasLe (Nat.find (fun (n : Nat) => p n) (fun (a : Nat) => _inst_1 a) h) n) (Exists.{1} Nat (fun (m : Nat) => Exists.{0} (LE.le.{0} Nat Nat.hasLe m n) (fun (H : LE.le.{0} Nat Nat.hasLe m n) => p m)))
but is expected to have type
  forall {p : Nat -> Prop} [_inst_1 : DecidablePred.{1} Nat p] (h : Exists.{1} Nat (fun (n : Nat) => p n)) (n : Nat), Iff (LE.le.{0} Nat instLENat (Nat.find (fun (n : Nat) => p n) (fun (a : Nat) => _inst_1 a) h) n) (Exists.{1} Nat (fun (m : Nat) => And (LE.le.{0} Nat instLENat m n) (p m)))
Case conversion may be inaccurate. Consider using '#align nat.find_le_iff Nat.find_le_iffₓ'. -/
@[simp]
theorem find_le_iff (h : ∃ n : ℕ, p n) (n : ℕ) : Nat.find h ≤ n ↔ ∃ m ≤ n, p m := by
  simp only [exists_prop, ← lt_succ_iff, find_lt_iff]
#align nat.find_le_iff Nat.find_le_iff

#print Nat.le_find_iff /-
@[simp]
theorem le_find_iff (h : ∃ n : ℕ, p n) (n : ℕ) : n ≤ Nat.find h ↔ ∀ m < n, ¬p m := by
  simp_rw [← not_lt, find_lt_iff, not_exists]
#align nat.le_find_iff Nat.le_find_iff
-/

#print Nat.lt_find_iff /-
@[simp]
theorem lt_find_iff (h : ∃ n : ℕ, p n) (n : ℕ) : n < Nat.find h ↔ ∀ m ≤ n, ¬p m := by
  simp only [← succ_le_iff, le_find_iff, succ_le_succ_iff]
#align nat.lt_find_iff Nat.lt_find_iff
-/

#print Nat.find_eq_zero /-
@[simp]
theorem find_eq_zero (h : ∃ n : ℕ, p n) : Nat.find h = 0 ↔ p 0 := by simp [find_eq_iff]
#align nat.find_eq_zero Nat.find_eq_zero
-/

#print Nat.find_mono /-
theorem find_mono (h : ∀ n, q n → p n) {hp : ∃ n, p n} {hq : ∃ n, q n} :
    Nat.find hp ≤ Nat.find hq :=
  Nat.find_min' _ (h _ (Nat.find_spec hq))
#align nat.find_mono Nat.find_mono
-/

#print Nat.find_le /-
theorem find_le {h : ∃ n, p n} (hn : p n) : Nat.find h ≤ n :=
  (Nat.find_le_iff _ _).2 ⟨n, le_rfl, hn⟩
#align nat.find_le Nat.find_le
-/

#print Nat.find_comp_succ /-
theorem find_comp_succ (h₁ : ∃ n, p n) (h₂ : ∃ n, p (n + 1)) (h0 : ¬p 0) :
    Nat.find h₁ = Nat.find h₂ + 1 :=
  by
  refine' (find_eq_iff _).2 ⟨Nat.find_spec h₂, fun n hn => _⟩
  cases' n with n
  exacts[h0, @Nat.find_min (fun n => p (n + 1)) _ h₂ _ (succ_lt_succ_iff.1 hn)]
#align nat.find_comp_succ Nat.find_comp_succ
-/

end Find

/-! ### `find_greatest` -/

section FindGreatest

#print Nat.findGreatest /-
/-- `find_greatest P b` is the largest `i ≤ bound` such that `P i` holds, or `0` if no such `i`
exists -/
protected def findGreatest (P : ℕ → Prop) [DecidablePred P] : ℕ → ℕ
  | 0 => 0
  | n + 1 => if P (n + 1) then n + 1 else find_greatest n
#align nat.find_greatest Nat.findGreatest
-/

variable {P Q : ℕ → Prop} [DecidablePred P] {b : ℕ}

#print Nat.findGreatest_zero /-
@[simp]
theorem findGreatest_zero : Nat.findGreatest P 0 = 0 :=
  rfl
#align nat.find_greatest_zero Nat.findGreatest_zero
-/

#print Nat.findGreatest_succ /-
theorem findGreatest_succ (n : ℕ) :
    Nat.findGreatest P (n + 1) = if P (n + 1) then n + 1 else Nat.findGreatest P n :=
  rfl
#align nat.find_greatest_succ Nat.findGreatest_succ
-/

#print Nat.findGreatest_eq /-
@[simp]
theorem findGreatest_eq : ∀ {b}, P b → Nat.findGreatest P b = b
  | 0, h => rfl
  | n + 1, h => by simp [Nat.findGreatest, h]
#align nat.find_greatest_eq Nat.findGreatest_eq
-/

#print Nat.findGreatest_of_not /-
@[simp]
theorem findGreatest_of_not (h : ¬P (b + 1)) : Nat.findGreatest P (b + 1) = Nat.findGreatest P b :=
  by simp [Nat.findGreatest, h]
#align nat.find_greatest_of_not Nat.findGreatest_of_not
-/

end FindGreatest

/-! ### decidability of predicates -/

#print Nat.decidableBallLT /-
instance decidableBallLT (n : Nat) (P : ∀ k < n, Prop) :
    ∀ [H : ∀ n h, Decidable (P n h)], Decidable (∀ n h, P n h) :=
  by
  induction' n with n IH <;> intro <;> skip
  · exact is_true fun n => by decide
  cases' IH fun k h => P k (lt_succ_of_lt h) with h
  · refine' is_false (mt _ h)
    intro hn k h
    apply hn
  by_cases p : P n (lt_succ_self n)
  ·
    exact
      is_true fun k h' =>
        (le_of_lt_succ h').lt_or_eq_dec.elim (h _) fun e =>
          match k, e, h' with
          | _, rfl, h => p
  · exact is_false (mt (fun hn => hn _ _) p)
#align nat.decidable_ball_lt Nat.decidableBallLT
-/

#print Nat.decidableForallFin /-
instance decidableForallFin {n : ℕ} (P : Fin n → Prop) [H : DecidablePred P] :
    Decidable (∀ i, P i) :=
  decidable_of_iff (∀ k h, P ⟨k, h⟩) ⟨fun a ⟨k, h⟩ => a k h, fun a k h => a ⟨k, h⟩⟩
#align nat.decidable_forall_fin Nat.decidableForallFin
-/

#print Nat.decidableBallLe /-
instance decidableBallLe (n : ℕ) (P : ∀ k ≤ n, Prop) [H : ∀ n h, Decidable (P n h)] :
    Decidable (∀ n h, P n h) :=
  decidable_of_iff (∀ (k) (h : k < succ n), P k (le_of_lt_succ h))
    ⟨fun a k h => a k (lt_succ_of_le h), fun a k h => a k _⟩
#align nat.decidable_ball_le Nat.decidableBallLe
-/

#print Nat.decidableExistsLT /-
instance decidableExistsLT {P : ℕ → Prop} [h : DecidablePred P] :
    DecidablePred fun n => ∃ m : ℕ, m < n ∧ P m
  | 0 => isFalse (by simp)
  | n + 1 =>
    decidable_of_decidable_of_iff (@Or.decidable _ _ (decidable_exists_lt n) (h n))
      (by simp only [lt_succ_iff_lt_or_eq, or_and_right, exists_or, exists_eq_left])
#align nat.decidable_exists_lt Nat.decidableExistsLT
-/

#print Nat.decidableExistsLe /-
instance decidableExistsLe {P : ℕ → Prop} [h : DecidablePred P] :
    DecidablePred fun n => ∃ m : ℕ, m ≤ n ∧ P m := fun n =>
  decidable_of_iff (∃ m, m < n + 1 ∧ P m) (exists_congr fun x => and_congr_left' lt_succ_iff)
#align nat.decidable_exists_le Nat.decidableExistsLe
-/

end Nat


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import logic.function.iterate
import order.monotone.basic

/-!
# Inequalities on iterates

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

In this file we prove some inequalities comparing `f^[n] x` and `g^[n] x` where `f` and `g` are
two self-maps that commute with each other.

Current selection of inequalities is motivated by formalization of the rotation number of
a circle homeomorphism.
-/
variables {α β : Type*}
open function

namespace monotone

variables [preorder α] {f : α → α} {x y : ℕ → α}

/-!
### Comparison of two sequences

If $f$ is a monotone function, then $∀ k, x_{k+1} ≤ f(x_k)$ implies that $x_k$ grows slower than
$f^k(x_0)$, and similarly for the reversed inequalities. If $x_k$ and $y_k$ are two sequences such
that $x_{k+1} ≤ f(x_k)$ and $y_{k+1} ≥ f(y_k)$ for all $k < n$, then $x_0 ≤ y_0$ implies
$x_n ≤ y_n$, see `monotone.seq_le_seq`.

If some of the inequalities in this lemma are strict, then we have $x_n < y_n$. The rest of the
lemmas in this section formalize this fact for different inequalities made strict.
-/

lemma seq_le_seq (hf : monotone f) (n : ℕ) (h₀ : x 0 ≤ y 0)
  (hx : ∀ k < n, x (k + 1) ≤ f (x k)) (hy : ∀ k < n, f (y k) ≤ y (k + 1)) :
  x n ≤ y n :=
begin
  induction n with n ihn,
  { exact h₀ },
  { refine (hx _ n.lt_succ_self).trans ((hf $ ihn _ _).trans (hy _ n.lt_succ_self)),
    exact λ k hk, hx _ (hk.trans n.lt_succ_self),
    exact λ k hk, hy _ (hk.trans n.lt_succ_self) }
end

lemma seq_pos_lt_seq_of_lt_of_le (hf : monotone f) {n : ℕ} (hn : 0 < n) (h₀ : x 0 ≤ y 0)
  (hx : ∀ k < n, x (k + 1) < f (x k)) (hy : ∀ k < n, f (y k) ≤ y (k + 1)) :
  x n < y n :=
begin
  induction n with n ihn, { exact hn.false.elim },
  suffices : x n ≤ y n,
    from (hx n n.lt_succ_self).trans_le ((hf this).trans $ hy n n.lt_succ_self),
  cases n, { exact h₀ },
  refine (ihn n.zero_lt_succ (λ k hk, hx _ _) (λ k hk, hy _ _)).le;
    exact hk.trans n.succ.lt_succ_self
end

lemma seq_pos_lt_seq_of_le_of_lt (hf : monotone f) {n : ℕ} (hn : 0 < n) (h₀ : x 0 ≤ y 0)
  (hx : ∀ k < n, x (k + 1) ≤ f (x k)) (hy : ∀ k < n, f (y k) < y (k + 1)) :
  x n < y n :=
hf.dual.seq_pos_lt_seq_of_lt_of_le hn h₀ hy hx

lemma seq_lt_seq_of_lt_of_le (hf : monotone f) (n : ℕ) (h₀ : x 0 < y 0)
  (hx : ∀ k < n, x (k + 1) < f (x k)) (hy : ∀ k < n, f (y k) ≤ y (k + 1)) :
  x n < y n :=
by { cases n, exacts [h₀, hf.seq_pos_lt_seq_of_lt_of_le n.zero_lt_succ h₀.le hx hy] }

lemma seq_lt_seq_of_le_of_lt (hf : monotone f) (n : ℕ) (h₀ : x 0 < y 0)
  (hx : ∀ k < n, x (k + 1) ≤ f (x k)) (hy : ∀ k < n, f (y k) < y (k + 1)) :
  x n < y n :=
hf.dual.seq_lt_seq_of_lt_of_le n h₀ hy hx

/-!
### Iterates of two functions

In this section we compare the iterates of a monotone function `f : α → α` to iterates of any
function `g : β → β`. If `h : β → α` satisfies `h ∘ g ≤ f ∘ h`, then `h (g^[n] x)` grows slower
than `f^[n] (h x)`, and similarly for the reversed inequality.

Then we specialize these two lemmas to the case `β = α`, `h = id`.
-/

variables {g : β → β} {h : β → α}
open function

lemma le_iterate_comp_of_le (hf : monotone f) (H : h ∘ g ≤ f ∘ h) (n : ℕ) :
  h ∘ (g^[n]) ≤ (f^[n]) ∘ h :=
λ x, by refine hf.seq_le_seq n _ (λ k hk, _) (λ k hk, _); simp [iterate_succ', H _]

lemma iterate_comp_le_of_le (hf : monotone f) (H : f ∘ h ≤ h ∘ g) (n : ℕ) :
  f^[n] ∘ h ≤ h ∘ (g^[n]) :=
hf.dual.le_iterate_comp_of_le H n

/-- If `f ≤ g` and `f` is monotone, then `f^[n] ≤ g^[n]`. -/

/-- If `f ≤ g` and `g` is monotone, then `f^[n] ≤ g^[n]`. -/
lemma le_iterate_of_le {g : α → α} (hg : monotone g) (h : f ≤ g) (n : ℕ) :
  f^[n] ≤ (g^[n]) :=
hg.dual.iterate_le_of_le h n

end monotone

/-!
### Comparison of iterations and the identity function

If $f(x) ≤ x$ for all $x$ (we express this as `f ≤ id` in the code), then the same is true for
any iterate of $f$, and similarly for the reversed inequality.
-/

namespace function

section preorder
variables [preorder α] {f : α → α}

/-- If $x ≤ f x$ for all $x$ (we write this as `id ≤ f`), then the same is true for any iterate
`f^[n]` of `f`. -/
lemma id_le_iterate_of_id_le (h : id ≤ f) (n : ℕ) : id ≤ (f^[n]) :=
by simpa only [iterate_id] using monotone_id.iterate_le_of_le h n

lemma iterate_le_id_of_le_id (h : f ≤ id) (n : ℕ) : (f^[n]) ≤ id :=
@id_le_iterate_of_id_le αᵒᵈ _ f h n

lemma monotone_iterate_of_id_le (h : id ≤ f) : monotone (λ m, f^[m]) :=
monotone_nat_of_le_succ $ λ n x, by { rw iterate_succ_apply', exact h _ }

lemma antitone_iterate_of_le_id (h : f ≤ id) : antitone (λ m, f^[m]) :=
λ m n hmn, @monotone_iterate_of_id_le αᵒᵈ _ f h m n hmn

end preorder

/-!
### Iterates of commuting functions

If `f` and `g` are monotone and commute, then `f x ≤ g x` implies `f^[n] x ≤ g^[n] x`, see
`function.commute.iterate_le_of_map_le`. We also prove two strict inequality versions of this lemma,
as well as `iff` versions.
-/

namespace commute

section preorder
variables [preorder α] {f g : α → α}

lemma iterate_le_of_map_le (h : commute f g) (hf : monotone f)  (hg : monotone g)
  {x} (hx : f x ≤ g x) (n : ℕ) :
  f^[n] x ≤ (g^[n]) x :=
by refine hf.seq_le_seq n _ (λ k hk, _) (λ k hk, _);
  simp [iterate_succ' f, h.iterate_right _ _, hg.iterate _ hx]

lemma iterate_pos_lt_of_map_lt (h : commute f g) (hf : monotone f) (hg : strict_mono g)
  {x} (hx : f x < g x) {n} (hn : 0 < n) :
  f^[n] x < (g^[n]) x :=
by refine hf.seq_pos_lt_seq_of_le_of_lt hn _ (λ k hk, _) (λ k hk, _);
  simp [iterate_succ' f, h.iterate_right _ _, hg.iterate _ hx]

lemma iterate_pos_lt_of_map_lt' (h : commute f g) (hf : strict_mono f) (hg : monotone g)
  {x} (hx : f x < g x) {n} (hn : 0 < n) :
  f^[n] x < (g^[n]) x :=
@iterate_pos_lt_of_map_lt αᵒᵈ _ g f h.symm hg.dual hf.dual x hx n hn

end preorder

variables [linear_order α] {f g : α → α}

lemma iterate_pos_lt_iff_map_lt (h : commute f g) (hf : monotone f)
  (hg : strict_mono g) {x n} (hn : 0 < n) :
  f^[n] x < (g^[n]) x ↔ f x < g x :=
begin
  rcases lt_trichotomy (f x) (g x) with H|H|H,
  { simp only [*, iterate_pos_lt_of_map_lt] },
  { simp only [*, h.iterate_eq_of_map_eq, lt_irrefl] },
  { simp only [lt_asymm H, lt_asymm (h.symm.iterate_pos_lt_of_map_lt' hg hf H hn)] }
end

lemma iterate_pos_lt_iff_map_lt' (h : commute f g) (hf : strict_mono f)
  (hg : monotone g) {x n} (hn : 0 < n) :
  f^[n] x < (g^[n]) x ↔ f x < g x :=
@iterate_pos_lt_iff_map_lt αᵒᵈ _ _ _ h.symm hg.dual hf.dual x n hn

lemma iterate_pos_le_iff_map_le (h : commute f g) (hf : monotone f)
  (hg : strict_mono g) {x n} (hn : 0 < n) :
  f^[n] x ≤ (g^[n]) x ↔ f x ≤ g x :=
by simpa only [not_lt] using not_congr (h.symm.iterate_pos_lt_iff_map_lt' hg hf hn)

lemma iterate_pos_le_iff_map_le' (h : commute f g) (hf : strict_mono f)
  (hg : monotone g) {x n} (hn : 0 < n) :
  f^[n] x ≤ (g^[n]) x ↔ f x ≤ g x :=
by simpa only [not_lt] using not_congr (h.symm.iterate_pos_lt_iff_map_lt hg hf hn)

lemma iterate_pos_eq_iff_map_eq (h : commute f g) (hf : monotone f)
  (hg : strict_mono g) {x n} (hn : 0 < n) :
  f^[n] x = (g^[n]) x ↔ f x = g x :=
by simp only [le_antisymm_iff, h.iterate_pos_le_iff_map_le hf hg hn,
  h.symm.iterate_pos_le_iff_map_le' hg hf hn]

end commute

end function

namespace monotone

variables [preorder α] {f : α → α} {x : α}

/-- If `f` is a monotone map and `x ≤ f x` at some point `x`, then the iterates `f^[n] x` form
a monotone sequence. -/
lemma monotone_iterate_of_le_map (hf : monotone f) (hx : x ≤ f x) : monotone (λ n, f^[n] x) :=
monotone_nat_of_le_succ $ λ n, by { rw iterate_succ_apply, exact hf.iterate n hx  }

/-- If `f` is a monotone map and `f x ≤ x` at some point `x`, then the iterates `f^[n] x` form
a antitone sequence. -/
lemma antitone_iterate_of_map_le (hf : monotone f) (hx : f x ≤ x) : antitone (λ n, f^[n] x) :=
hf.dual.monotone_iterate_of_le_map hx

end monotone

namespace strict_mono

variables [preorder α] {f : α → α} {x : α}

/-- If `f` is a strictly monotone map and `x < f x` at some point `x`, then the iterates `f^[n] x`
form a strictly monotone sequence. -/
lemma strict_mono_iterate_of_lt_map (hf : strict_mono f) (hx : x < f x) :
  strict_mono (λ n, f^[n] x) :=
strict_mono_nat_of_lt_succ $ λ n, by { rw iterate_succ_apply, exact hf.iterate n hx  }

/-- If `f` is a strictly antitone map and `f x < x` at some point `x`, then the iterates `f^[n] x`
form a strictly antitone sequence. -/
lemma strict_anti_iterate_of_map_lt (hf : strict_mono f) (hx : f x < x) :
  strict_anti (λ n, f^[n] x) :=
hf.dual.strict_mono_iterate_of_lt_map hx

end strict_mono


import data.nat.prime help

open nat

theorem Euclid (n : ℕ) : ∃ p ≥ n, prime p :=
begin
  let N := n.fact + 1,
  let p := min_fac N,
  use_this p,
  have p_is_prime : prime p,
    apply min_fac_prime,
    have key_fact := fact_pos n,
    have now : N > 1 := succ_lt_succ key_fact,
    apply ne_of_gt,
    assumption,
  split,
  swap,
    assumption,
  by_contradiction contra_hyp,
  have key_fact : p ∣ n.fact,
    apply dvd_fact,
      apply p_is_prime.pos,
      apply le_of_not_ge,
      assumption,
  have old_fact : p ∣ n.fact + 1 := min_fac_dvd N,
  have oops : p ∣ 1,
    have tada := dvd_add_iff_right_of_left,
    rewrite ←tada at old_fact,
    assumption',
  have := p_is_prime.not_dvd_one,
  contradiction,
end


variable L  (n : nat) : data
variable R  (n : nat) : data
variable Ld (n : nat) : data
variable Rd (n : nat) : data

premise BaseLeft  : Ld 0 = R 16
premise BaseRight : Rd 0 = L 16
premise StepLeft  : ∀ i : nat, Ld i = R (16 - i) → Ld (i + 1) = R (16 - (i + 1))
premise StepRight : ∀ i : nat, Rd i = L (16 - i) → Rd (i + 1) = L (16 - (i + 1))

theorem ProofLeft : Ld 16 = R 0 :=
have  H0 : Ld   0      = R  16,              from  BaseLeft,
have  H1 : Ld ( 0 + 1) = R (16 - ( 0 + 1)), from (StepLeft  0)  H0,
have  H2 : Ld ( 1 + 1) = R (16 - ( 1 + 1)), from (StepLeft  1)  H1,
have  H3 : Ld ( 2 + 1) = R (16 - ( 2 + 1)), from (StepLeft  2)  H2,
have  H4 : Ld ( 3 + 1) = R (16 - ( 3 + 1)), from (StepLeft  3)  H3,
have  H5 : Ld ( 4 + 1) = R (16 - ( 4 + 1)), from (StepLeft  4)  H4,
have  H6 : Ld ( 5 + 1) = R (16 - ( 5 + 1)), from (StepLeft  5)  H5,
have  H7 : Ld ( 6 + 1) = R (16 - ( 6 + 1)), from (StepLeft  6)  H6,
have  H8 : Ld ( 7 + 1) = R (16 - ( 7 + 1)), from (StepLeft  7)  H7,
have  H9 : Ld ( 8 + 1) = R (16 - ( 8 + 1)), from (StepLeft  8)  H8,
have H10 : Ld ( 9 + 1) = R (16 - ( 9 + 1)), from (StepLeft  9)  H9,
have H11 : Ld (10 + 1) = R (16 - (10 + 1)), from (StepLeft 10) H10,
have H12 : Ld (11 + 1) = R (16 - (11 + 1)), from (StepLeft 11) H11,
have H13 : Ld (12 + 1) = R (16 - (12 + 1)), from (StepLeft 12) H12,
have H14 : Ld (13 + 1) = R (16 - (13 + 1)), from (StepLeft 13) H13,
have H15 : Ld (14 + 1) = R (16 - (14 + 1)), from (StepLeft 14) H14,
have H16 : Ld (15 + 1) = R (16 - (15 + 1)), from (StepLeft 15) H15,
show Ld 16 = R 0, from H16


def order_dual (α : Type*) := α

namespace order_dual
instance has_le (α : Type*) [has_le α] : has_le (order_dual α) := ⟨λx y:α, y ≤ x⟩
instance has_lt (α : Type*) [has_lt α] : has_lt (order_dual α) := ⟨λx y:α, y < x⟩

instance has_preorder (α : Type*) [preorder α] : preorder (order_dual α) :=
{ le_refl  := le_refl,
  le_trans := assume a b c hab hbc, le_trans hbc hab,
  .. order_dual.has_le α
}
instance has_partial_order (α : Type*) [po : partial_order α] : partial_order (order_dual α) :=
{ le_antisymm := begin intros, apply @le_antisymm _ po, assumption, assumption end
, ..(order_dual.has_preorder α)
}
instance has_linear_order (α : Type*) [po : linear_order α] : linear_order (order_dual α) :=
{ le_total := λ a b, or.swap $ le_total a b
, ..(order_dual.has_partial_order α)
}
instance has_decidable_le (α : Type*) [hle : has_le α] [d : decidable_rel hle.le]
    : decidable_rel (order_dual.has_le α).le := λ x y, d y x
instance has_decidable_lt (α : Type*) [hlt : has_lt α] [d : decidable_rel hlt.lt]
    : decidable_rel (order_dual.has_lt α).lt := λ x y, d y x
lemma transitive_lt (α : Type*) [hlt : has_lt α] [tr : transitive hlt.lt]
    : transitive (@order_dual.has_lt α hlt).lt := λ x y z p q, tr q p

instance has_decidable_linear_order (α : Type*) [dlo : decidable_linear_order α]
    : decidable_linear_order (order_dual α) :=
{  decidable_eq := λ a b, @decidable_linear_order.decidable_eq α dlo a b
, decidable_le := λ a b, @decidable_linear_order.decidable_le α dlo b a
, ..(order_dual.has_linear_order α)
}
end order_dual