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

! This file was ported from Lean 3 source module analysis.calculus.dslope
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Calculus.Deriv
import Mathbin.LinearAlgebra.AffineSpace.Slope

/-!
# Slope of a differentiable function

Given a function `f : 𝕜 → E` from a nontrivially normed field to a normed space over this field,
`dslope f a b` is defined as `slope f a b = (b - a)⁻¹ • (f b - f a)` for `a ≠ b` and as `deriv f a`
for `a = b`.

In this file we define `dslope` and prove some basic lemmas about its continuity and
differentiability.
-/

open Classical Topology Filter

open Function Set Filter

variable {𝕜 E : Type _} [NontriviallyNormedField 𝕜] [NormedAddCommGroup E] [NormedSpace 𝕜 E]

/-- `dslope f a b` is defined as `slope f a b = (b - a)⁻¹ • (f b - f a)` for `a ≠ b` and
`deriv f a` for `a = b`. -/
noncomputable def dslope (f : 𝕜 → E) (a : 𝕜) : 𝕜 → E :=
  update (slope f a) a (deriv f a)
#align dslope dslope

@[simp]
theorem dslope_same (f : 𝕜 → E) (a : 𝕜) : dslope f a a = deriv f a :=
  update_same _ _ _
#align dslope_same dslope_same

variable {f : 𝕜 → E} {a b : 𝕜} {s : Set 𝕜}

theorem dslope_of_ne (f : 𝕜 → E) (h : b ≠ a) : dslope f a b = slope f a b :=
  update_noteq h _ _
#align dslope_of_ne dslope_of_ne

theorem ContinuousLinearMap.dslope_comp {F : Type _} [NormedAddCommGroup F] [NormedSpace 𝕜 F]
    (f : E →L[𝕜] F) (g : 𝕜 → E) (a b : 𝕜) (H : a = b → DifferentiableAt 𝕜 g a) :
    dslope (f ∘ g) a b = f (dslope g a b) :=
  by
  rcases eq_or_ne b a with (rfl | hne)
  · simp only [dslope_same]
    exact (f.has_fderiv_at.comp_has_deriv_at b (H rfl).HasDerivAt).deriv
  · simpa only [dslope_of_ne _ hne] using f.to_linear_map.slope_comp g a b
#align continuous_linear_map.dslope_comp ContinuousLinearMap.dslope_comp

theorem eqOn_dslope_slope (f : 𝕜 → E) (a : 𝕜) : EqOn (dslope f a) (slope f a) ({a}ᶜ) := fun b =>
  dslope_of_ne f
#align eq_on_dslope_slope eqOn_dslope_slope

theorem dslope_eventuallyEq_slope_of_ne (f : 𝕜 → E) (h : b ≠ a) : dslope f a =ᶠ[𝓝 b] slope f a :=
  (eqOn_dslope_slope f a).eventuallyEq_of_mem (isOpen_ne.mem_nhds h)
#align dslope_eventually_eq_slope_of_ne dslope_eventuallyEq_slope_of_ne

theorem dslope_eventuallyEq_slope_punctured_nhds (f : 𝕜 → E) : dslope f a =ᶠ[𝓝[≠] a] slope f a :=
  (eqOn_dslope_slope f a).eventuallyEq_of_mem self_mem_nhdsWithin
#align dslope_eventually_eq_slope_punctured_nhds dslope_eventuallyEq_slope_punctured_nhds

@[simp]
theorem sub_smul_dslope (f : 𝕜 → E) (a b : 𝕜) : (b - a) • dslope f a b = f b - f a := by
  rcases eq_or_ne b a with (rfl | hne) <;> simp [dslope_of_ne, *]
#align sub_smul_dslope sub_smul_dslope

theorem dslope_sub_smul_of_ne (f : 𝕜 → E) (h : b ≠ a) : dslope (fun x => (x - a) • f x) a b = f b :=
  by rw [dslope_of_ne _ h, slope_sub_smul _ h.symm]
#align dslope_sub_smul_of_ne dslope_sub_smul_of_ne

theorem eqOn_dslope_sub_smul (f : 𝕜 → E) (a : 𝕜) :
    EqOn (dslope (fun x => (x - a) • f x) a) f ({a}ᶜ) := fun b => dslope_sub_smul_of_ne f
#align eq_on_dslope_sub_smul eqOn_dslope_sub_smul

theorem dslope_sub_smul [DecidableEq 𝕜] (f : 𝕜 → E) (a : 𝕜) :
    dslope (fun x => (x - a) • f x) a = update f a (deriv (fun x => (x - a) • f x) a) :=
  eq_update_iff.2 ⟨dslope_same _ _, eqOn_dslope_sub_smul f a⟩
#align dslope_sub_smul dslope_sub_smul

@[simp]
theorem continuousAt_dslope_same : ContinuousAt (dslope f a) a ↔ DifferentiableAt 𝕜 f a := by
  simp only [dslope, continuousAt_update_same, ← hasDerivAt_deriv_iff, hasDerivAt_iff_tendsto_slope]
#align continuous_at_dslope_same continuousAt_dslope_same

theorem ContinuousWithinAt.of_dslope (h : ContinuousWithinAt (dslope f a) s b) :
    ContinuousWithinAt f s b :=
  by
  have : ContinuousWithinAt (fun x => (x - a) • dslope f a x + f a) s b :=
    ((continuousWithinAt_id.sub continuousWithinAt_const).smul h).add continuousWithinAt_const
  simpa only [sub_smul_dslope, sub_add_cancel] using this
#align continuous_within_at.of_dslope ContinuousWithinAt.of_dslope

theorem ContinuousAt.of_dslope (h : ContinuousAt (dslope f a) b) : ContinuousAt f b :=
  (continuousWithinAt_univ _ _).1 h.ContinuousWithinAt.of_dslope
#align continuous_at.of_dslope ContinuousAt.of_dslope

theorem ContinuousOn.of_dslope (h : ContinuousOn (dslope f a) s) : ContinuousOn f s := fun x hx =>
  (h x hx).of_dslope
#align continuous_on.of_dslope ContinuousOn.of_dslope

theorem continuousWithinAt_dslope_of_ne (h : b ≠ a) :
    ContinuousWithinAt (dslope f a) s b ↔ ContinuousWithinAt f s b :=
  by
  refine' ⟨ContinuousWithinAt.of_dslope, fun hc => _⟩
  simp only [dslope, continuousWithinAt_update_of_ne h]
  exact
    ((continuous_within_at_id.sub continuousWithinAt_const).inv₀ (sub_ne_zero.2 h)).smul
      (hc.sub continuousWithinAt_const)
#align continuous_within_at_dslope_of_ne continuousWithinAt_dslope_of_ne

theorem continuousAt_dslope_of_ne (h : b ≠ a) : ContinuousAt (dslope f a) b ↔ ContinuousAt f b := by
  simp only [← continuousWithinAt_univ, continuousWithinAt_dslope_of_ne h]
#align continuous_at_dslope_of_ne continuousAt_dslope_of_ne

theorem continuousOn_dslope (h : s ∈ 𝓝 a) :
    ContinuousOn (dslope f a) s ↔ ContinuousOn f s ∧ DifferentiableAt 𝕜 f a :=
  by
  refine' ⟨fun hc => ⟨hc.of_dslope, continuousAt_dslope_same.1 <| hc.ContinuousAt h⟩, _⟩
  rintro ⟨hc, hd⟩ x hx
  rcases eq_or_ne x a with (rfl | hne)
  exacts[(continuousAt_dslope_same.2 hd).ContinuousWithinAt,
    (continuousWithinAt_dslope_of_ne hne).2 (hc x hx)]
#align continuous_on_dslope continuousOn_dslope

theorem DifferentiableWithinAt.of_dslope (h : DifferentiableWithinAt 𝕜 (dslope f a) s b) :
    DifferentiableWithinAt 𝕜 f s b := by
  simpa only [id, sub_smul_dslope f a, sub_add_cancel] using
    ((differentiable_within_at_id.sub_const a).smul h).AddConst (f a)
#align differentiable_within_at.of_dslope DifferentiableWithinAt.of_dslope

theorem DifferentiableAt.of_dslope (h : DifferentiableAt 𝕜 (dslope f a) b) :
    DifferentiableAt 𝕜 f b :=
  differentiableWithinAt_univ.1 h.DifferentiableWithinAt.of_dslope
#align differentiable_at.of_dslope DifferentiableAt.of_dslope

theorem DifferentiableOn.of_dslope (h : DifferentiableOn 𝕜 (dslope f a) s) :
    DifferentiableOn 𝕜 f s := fun x hx => (h x hx).of_dslope
#align differentiable_on.of_dslope DifferentiableOn.of_dslope

theorem differentiableWithinAt_dslope_of_ne (h : b ≠ a) :
    DifferentiableWithinAt 𝕜 (dslope f a) s b ↔ DifferentiableWithinAt 𝕜 f s b :=
  by
  refine' ⟨DifferentiableWithinAt.of_dslope, fun hd => _⟩
  refine'
    (((differentiable_within_at_id.sub_const a).inv (sub_ne_zero.2 h)).smul
          (hd.sub_const (f a))).congr_of_eventuallyEq
      _ (dslope_of_ne _ h)
  refine' (eqOn_dslope_slope _ _).eventuallyEq_of_mem _
  exact mem_nhdsWithin_of_mem_nhds (is_open_ne.mem_nhds h)
#align differentiable_within_at_dslope_of_ne differentiableWithinAt_dslope_of_ne

theorem differentiableOn_dslope_of_nmem (h : a ∉ s) :
    DifferentiableOn 𝕜 (dslope f a) s ↔ DifferentiableOn 𝕜 f s :=
  forall_congr' fun x =>
    forall_congr' fun hx => differentiableWithinAt_dslope_of_ne <| ne_of_mem_of_not_mem hx h
#align differentiable_on_dslope_of_nmem differentiableOn_dslope_of_nmem

theorem differentiableAt_dslope_of_ne (h : b ≠ a) :
    DifferentiableAt 𝕜 (dslope f a) b ↔ DifferentiableAt 𝕜 f b := by
  simp only [← differentiableWithinAt_univ, differentiableWithinAt_dslope_of_ne h]
#align differentiable_at_dslope_of_ne differentiableAt_dslope_of_ne


/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes

! This file was ported from Lean 3 source module data.matrix.pequiv
! leanprover-community/mathlib commit 3793e32979850b523ad18ac3bd09137f657cbefa
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Matrix.Basic
import Mathbin.Data.Pequiv

/-!
# partial equivalences for matrices

Using partial equivalences to represent matrices.
This file introduces the function `pequiv.to_matrix`, which returns a matrix containing ones and
zeros. For any partial equivalence `f`, `f.to_matrix i j = 1 ↔ f i = some j`.

The following important properties of this function are proved
`to_matrix_trans : (f.trans g).to_matrix = f.to_matrix ⬝ g.to_matrix`
`to_matrix_symm  : f.symm.to_matrix = f.to_matrixᵀ`
`to_matrix_refl : (pequiv.refl n).to_matrix = 1`
`to_matrix_bot : ⊥.to_matrix = 0`

This theory gives the matrix representation of projection linear maps, and their right inverses.
For example, the matrix `(single (0 : fin 1) (i : fin n)).to_matrix` corresponds to the ith
projection map from R^n to R.

Any injective function `fin m → fin n` gives rise to a `pequiv`, whose matrix is the projection
map from R^m → R^n represented by the same function. The transpose of this matrix is the right
inverse of this map, sending anything not in the image to zero.

## notations

This file uses the notation ` ⬝ ` for `matrix.mul` and `ᵀ` for `matrix.transpose`.
-/

namespace PEquiv

open Matrix

universe u v

variable {k l m n : Type _}

variable {α : Type v}

open Matrix

/- warning: pequiv.to_matrix -> PEquiv.toMatrix is a dubious translation:
lean 3 declaration is
  forall {m : Type.{u2}} {n : Type.{u3}} {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u3} n] [_inst_2 : Zero.{u1} α] [_inst_3 : One.{u1} α], (PEquiv.{u2, u3} m n) -> (Matrix.{u2, u3, u1} m n α)
but is expected to have type
  forall {m : Type.{u1}} {n : Type.{u2}} {α : Type.{u3}} [_inst_1 : DecidableEq.{succ u2} n] [_inst_2 : Zero.{u3} α] [_inst_3 : One.{u3} α], (PEquiv.{u1, u2} m n) -> (Matrix.{u1, u2, u3} m n α)
Case conversion may be inaccurate. Consider using '#align pequiv.to_matrix PEquiv.toMatrixₓ'. -/
/-- `to_matrix` returns a matrix containing ones and zeros. `f.to_matrix i j` is `1` if
  `f i = some j` and `0` otherwise -/
def toMatrix [DecidableEq n] [Zero α] [One α] (f : m ≃. n) : Matrix m n α
  | i, j => if j ∈ f i then 1 else 0
#align pequiv.to_matrix PEquiv.toMatrix

theorem mul_matrix_apply [Fintype m] [DecidableEq m] [Semiring α] (f : l ≃. m) (M : Matrix m n α)
    (i j) : (f.toMatrix ⬝ M) i j = Option.casesOn (f i) 0 fun fi => M fi j :=
  by
  dsimp [to_matrix, Matrix.mul_apply]
  cases' h : f i with fi
  · simp [h]
  · rw [Finset.sum_eq_single fi] <;> simp (config := { contextual := true }) [h, eq_comm]
#align pequiv.mul_matrix_apply PEquiv.mul_matrix_apply

theorem toMatrix_symm [DecidableEq m] [DecidableEq n] [Zero α] [One α] (f : m ≃. n) :
    (f.symm.toMatrix : Matrix n m α) = f.toMatrixᵀ := by
  ext <;> simp only [transpose, mem_iff_mem f, to_matrix] <;> congr
#align pequiv.to_matrix_symm PEquiv.toMatrix_symm

@[simp]
theorem toMatrix_refl [DecidableEq n] [Zero α] [One α] :
    ((PEquiv.refl n).toMatrix : Matrix n n α) = 1 := by
  ext <;> simp [to_matrix, one_apply] <;> congr
#align pequiv.to_matrix_refl PEquiv.toMatrix_refl

theorem matrix_mul_apply [Fintype m] [Semiring α] [DecidableEq n] (M : Matrix l m α) (f : m ≃. n)
    (i j) : (M ⬝ f.toMatrix) i j = Option.casesOn (f.symm j) 0 fun fj => M i fj :=
  by
  dsimp [to_matrix, Matrix.mul_apply]
  cases' h : f.symm j with fj
  · simp [h, ← f.eq_some_iff]
  · rw [Finset.sum_eq_single fj]
    · simp [h, ← f.eq_some_iff]
    · intro b H n
      simp [h, ← f.eq_some_iff, n.symm]
    · simp
#align pequiv.matrix_mul_apply PEquiv.matrix_mul_apply

theorem toPEquiv_mul_matrix [Fintype m] [DecidableEq m] [Semiring α] (f : m ≃ m)
    (M : Matrix m n α) : f.toPEquiv.toMatrix ⬝ M = fun i => M (f i) :=
  by
  ext (i j)
  rw [mul_matrix_apply, Equiv.toPEquiv_apply]
#align pequiv.to_pequiv_mul_matrix PEquiv.toPEquiv_mul_matrix

theorem mul_toPEquiv_toMatrix {m n α : Type _} [Fintype n] [DecidableEq n] [Semiring α] (f : n ≃ n)
    (M : Matrix m n α) : M ⬝ f.toPEquiv.toMatrix = M.submatrix id f.symm :=
  Matrix.ext fun i j => by
    rw [PEquiv.matrix_mul_apply, ← Equiv.toPEquiv_symm, Equiv.toPEquiv_apply,
      Matrix.submatrix_apply, id.def]
#align pequiv.mul_to_pequiv_to_matrix PEquiv.mul_toPEquiv_toMatrix

theorem toMatrix_trans [Fintype m] [DecidableEq m] [DecidableEq n] [Semiring α] (f : l ≃. m)
    (g : m ≃. n) : ((f.trans g).toMatrix : Matrix l n α) = f.toMatrix ⬝ g.toMatrix :=
  by
  ext (i j)
  rw [mul_matrix_apply]
  dsimp [to_matrix, PEquiv.trans]
  cases f i <;> simp
#align pequiv.to_matrix_trans PEquiv.toMatrix_trans

@[simp]
theorem toMatrix_bot [DecidableEq n] [Zero α] [One α] :
    ((⊥ : PEquiv m n).toMatrix : Matrix m n α) = 0 :=
  rfl
#align pequiv.to_matrix_bot PEquiv.toMatrix_bot

theorem toMatrix_injective [DecidableEq n] [MonoidWithZero α] [Nontrivial α] :
    Function.Injective (@toMatrix m n α _ _ _) := by
  classical
    intro f g
    refine' not_imp_not.1 _
    simp only [matrix.ext_iff.symm, to_matrix, PEquiv.ext_iff, not_forall, exists_imp]
    intro i hi
    use i
    cases' hf : f i with fi
    · cases' hg : g i with gi
      · cc
      · use gi
        simp
    · use fi
      simp [hf.symm, Ne.symm hi]
#align pequiv.to_matrix_injective PEquiv.toMatrix_injective

theorem toMatrix_swap [DecidableEq n] [Ring α] (i j : n) :
    (Equiv.swap i j).toPEquiv.toMatrix =
      (1 : Matrix n n α) - (single i i).toMatrix - (single j j).toMatrix + (single i j).toMatrix +
        (single j i).toMatrix :=
  by
  ext
  dsimp [to_matrix, single, Equiv.swap_apply_def, Equiv.toPEquiv, one_apply]
  split_ifs <;>
    first
      |· simp_all|·
        exfalso
        assumption
#align pequiv.to_matrix_swap PEquiv.toMatrix_swap

@[simp]
theorem single_mul_single [Fintype n] [DecidableEq k] [DecidableEq m] [DecidableEq n] [Semiring α]
    (a : m) (b : n) (c : k) :
    ((single a b).toMatrix : Matrix _ _ α) ⬝ (single b c).toMatrix = (single a c).toMatrix := by
  rw [← to_matrix_trans, single_trans_single]
#align pequiv.single_mul_single PEquiv.single_mul_single

theorem single_mul_single_of_ne [Fintype n] [DecidableEq n] [DecidableEq k] [DecidableEq m]
    [Semiring α] {b₁ b₂ : n} (hb : b₁ ≠ b₂) (a : m) (c : k) :
    ((single a b₁).toMatrix : Matrix _ _ α) ⬝ (single b₂ c).toMatrix = 0 := by
  rw [← to_matrix_trans, single_trans_single_of_ne hb, to_matrix_bot]
#align pequiv.single_mul_single_of_ne PEquiv.single_mul_single_of_ne

/-- Restatement of `single_mul_single`, which will simplify expressions in `simp` normal form,
  when associativity may otherwise need to be carefully applied. -/
@[simp]
theorem single_mul_single_right [Fintype n] [Fintype k] [DecidableEq n] [DecidableEq k]
    [DecidableEq m] [Semiring α] (a : m) (b : n) (c : k) (M : Matrix k l α) :
    (single a b).toMatrix ⬝ ((single b c).toMatrix ⬝ M) = (single a c).toMatrix ⬝ M := by
  rw [← Matrix.mul_assoc, single_mul_single]
#align pequiv.single_mul_single_right PEquiv.single_mul_single_right

/-- We can also define permutation matrices by permuting the rows of the identity matrix. -/
theorem equiv_toPEquiv_toMatrix [DecidableEq n] [Zero α] [One α] (σ : Equiv n n) (i j : n) :
    σ.toPEquiv.toMatrix i j = (1 : Matrix n n α) (σ i) j :=
  if_congr Option.some_inj rfl rfl
#align pequiv.equiv_to_pequiv_to_matrix PEquiv.equiv_toPEquiv_toMatrix

end PEquiv


namespace fol_11
open classical

axiom not_iff_not_self (P : Prop) : ¬ (P ↔ ¬ P)

-- Example of using the axiom:
example (Q : Prop) : ¬ (Q ↔ ¬ Q) :=
not_iff_not_self Q

section
  variable Person : Type
  variable shaves : Person → Person → Prop
  variable barber : Person
  variable h : ∀ x, shaves barber x ↔ ¬ shaves x x
include h

  theorem fol_11 : false :=
  have h1: shaves barber barber ↔ ¬ shaves barber barber, from h barber,
  have h2: shaves barber barber → ¬ shaves barber barber, from h1.mp,
  have h3: shaves barber barber, from iff.elim_right h1
    (assume h4: shaves barber barber,
     have h5: ¬ shaves barber barber, from h2 h4,
     h5 h4),
  have h6: ¬ shaves barber barber, from h2 h3,
  h6 h3
end

#check fol_11

end fol_11


/-
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.dedekind_domain.ideal
/-!
# Factorization of ideals of Dedekind domains
Every nonzero ideal `I` of a Dedekind domain `R` can be factored as a product `∏_v v^{n_v}` over the
maximal ideals of `R`, where the exponents `n_v` are natural numbers.
TODO: Extend the results in this file to fractional ideals of `R`.
## Main results
- `ideal.finite_factors` : Only finitely many maximal ideals of `R` divide a given nonzero ideal.
- `ideal.finprod_height_one_spectrum_factorization` : The ideal `I` equals the finprod
  `∏_v v^(val_v(I))`,where `val_v(I)` denotes the multiplicity of `v` in the factorization of `I`
  and `v` runs over the maximal ideals of `R`.
## Tags
dedekind domain, ideal, factorization
-/

noncomputable theory
open_locale big_operators classical non_zero_divisors

open set function unique_factorization_monoid is_dedekind_domain
  is_dedekind_domain.height_one_spectrum

/-! ### Factorization of ideals of Dedekind domains -/

variables {R : Type*} [comm_ring R] [is_domain R] [is_dedekind_domain R] {K : Type*} [field K]
  [algebra R K] [is_fraction_ring R K] (v : height_one_spectrum R)

/-- Given a maximal ideal `v` and an ideal `I` of `R`, `max_pow_dividing` returns the maximal
  power of `v` dividing `I`. -/
def is_dedekind_domain.height_one_spectrum.max_pow_dividing (I : ideal R) : ideal R :=
v.as_ideal^(associates.mk v.as_ideal).count (associates.mk I).factors

/-- Only finitely many maximal ideals of `R` divide a given nonzero ideal. -/
lemma ideal.finite_factors {I : ideal R} (hI : I ≠ 0) :
  {v : height_one_spectrum R | v.as_ideal ∣ I}.finite :=
begin
  rw [← set.finite_coe_iff, set.coe_set_of],
  haveI h_fin := fintype_subtype_dvd I hI,
  refine finite.of_injective (λ v, (⟨(v : height_one_spectrum R).as_ideal, v.2⟩ : {x // x ∣ I})) _,
  intros v w hvw,
  simp only at hvw,
  exact subtype.coe_injective ((height_one_spectrum.ext_iff ↑v ↑w).mpr hvw)
end

/-- For every nonzero ideal `I` of `v`, there are finitely many maximal ideals `v` such that the
  multiplicity of `v` in the factorization of `I`, denoted `val_v(I)`, is nonzero. -/
lemma associates.finite_factors {I : ideal R} (hI : I ≠ 0) :
  ∀ᶠ (v : height_one_spectrum R) in filter.cofinite,
    ((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ) = 0 :=
begin
  have h_supp : {v : height_one_spectrum R |
    ¬((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ) = 0} =
    {v : height_one_spectrum R | v.as_ideal ∣ I},
  { ext v,
    simp_rw int.coe_nat_eq_zero,
    exact associates.count_ne_zero_iff_dvd hI v.irreducible, },
  rw [filter.eventually_cofinite, h_supp],
  exact ideal.finite_factors hI,
end

namespace ideal

/-- For every nonzero ideal `I` of `v`, there are finitely many maximal ideals `v` such that
  `v^(val_v(I))` is not the unit ideal. -/
lemma finite_mul_support {I : ideal R} (hI : I ≠ 0) :
  (mul_support (λ (v : height_one_spectrum R), v.max_pow_dividing I)).finite :=
begin
  have h_subset : {v : height_one_spectrum R | v.max_pow_dividing I ≠ 1} ⊆
    {v : height_one_spectrum R |
      ((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ) ≠ 0},
  { intros v hv h_zero,
    have hv' : v.max_pow_dividing I = 1,
    { rw [is_dedekind_domain.height_one_spectrum.max_pow_dividing, int.coe_nat_eq_zero.mp h_zero,
        pow_zero _] },
    exact hv hv', },
  exact finite.subset (filter.eventually_cofinite.mp (associates.finite_factors hI)) h_subset,
end

/-- For every nonzero ideal `I` of `v`, there are finitely many maximal ideals `v` such that
`v^(val_v(I))`, regarded as a fractional ideal, is not `(1)`. -/
lemma finite_mul_support_coe {I : ideal R} (hI : I ≠ 0) :
  (mul_support (λ (v : height_one_spectrum R),
    (v.as_ideal : fractional_ideal R⁰ K) ^
      ((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ))).finite :=
begin
  rw mul_support,
  simp_rw [ne.def, zpow_coe_nat, ← fractional_ideal.coe_ideal_pow,
    fractional_ideal.coe_ideal_eq_one],
  exact finite_mul_support hI,
end

/-- For every nonzero ideal `I` of `v`, there are finitely many maximal ideals `v` such that
`v^-(val_v(I))` is not the unit ideal. -/
lemma finite_mul_support_inv {I : ideal R} (hI : I ≠ 0) :
  (mul_support (λ (v : height_one_spectrum R),
    (v.as_ideal : fractional_ideal R⁰ K) ^
      -((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ))).finite :=
begin
  rw mul_support,
  simp_rw [zpow_neg, ne.def, inv_eq_one],
  exact finite_mul_support_coe hI,
end

/-- For every nonzero ideal `I` of `v`, `v^(val_v(I) + 1)` does not divide `∏_v v^(val_v(I))`. -/
lemma finprod_not_dvd (I : ideal R) (hI : I ≠ 0) :
  ¬ (v.as_ideal) ^ ((associates.mk v.as_ideal).count (associates.mk I).factors + 1) ∣
      (∏ᶠ (v : height_one_spectrum R), v.max_pow_dividing I) :=
begin
  have hf := finite_mul_support hI,
  have h_ne_zero : v.max_pow_dividing I ≠ 0 := pow_ne_zero _ v.ne_bot,
  rw [← mul_finprod_cond_ne v hf, pow_add, pow_one, finprod_cond_ne _ _ hf],
  intro h_contr,
  have hv_prime : prime v.as_ideal := ideal.prime_of_is_prime v.ne_bot v.is_prime,
  obtain ⟨w, hw, hvw'⟩ :=
    prime.exists_mem_finset_dvd hv_prime ((mul_dvd_mul_iff_left h_ne_zero).mp h_contr),
  have hw_prime : prime w.as_ideal := ideal.prime_of_is_prime w.ne_bot w.is_prime,
  have hvw := prime.dvd_of_dvd_pow hv_prime hvw',
  rw [prime.dvd_prime_iff_associated hv_prime hw_prime, associated_iff_eq] at hvw,
  exact (finset.mem_erase.mp hw).1 (height_one_spectrum.ext w v (eq.symm hvw)),
end

end ideal

lemma associates.finprod_ne_zero (I : ideal R) :
  associates.mk (∏ᶠ (v : height_one_spectrum R), v.max_pow_dividing I) ≠ 0 :=
begin
  rw [associates.mk_ne_zero, finprod_def],
  split_ifs,
  { rw finset.prod_ne_zero_iff,
    intros v hv,
    apply pow_ne_zero _ v.ne_bot, },
  { exact one_ne_zero, }
end

namespace ideal

/-- The multiplicity of `v` in `∏_v v^(val_v(I))` equals `val_v(I)`. -/
lemma finprod_count (I : ideal R) (hI : I ≠ 0) : (associates.mk v.as_ideal).count
  (associates.mk (∏ᶠ (v : height_one_spectrum R), v.max_pow_dividing I)).factors =
  (associates.mk v.as_ideal).count (associates.mk I).factors :=
begin
  have h_ne_zero := associates.finprod_ne_zero I,
  have hv : irreducible (associates.mk v.as_ideal) := v.associates_irreducible,
  have h_dvd := finprod_mem_dvd v (ideal.finite_mul_support hI),
  have h_not_dvd := ideal.finprod_not_dvd v I hI,
  simp only [is_dedekind_domain.height_one_spectrum.max_pow_dividing] at h_dvd h_ne_zero h_not_dvd,
  rw [← associates.mk_dvd_mk, associates.dvd_eq_le, associates.mk_pow,
    associates.prime_pow_dvd_iff_le h_ne_zero hv] at h_dvd h_not_dvd,
  rw not_le at h_not_dvd,
  apply nat.eq_of_le_of_lt_succ h_dvd h_not_dvd,
end

/-- The ideal `I` equals the finprod `∏_v v^(val_v(I))`. -/
lemma finprod_height_one_spectrum_factorization (I : ideal R) (hI : I ≠ 0) :
  ∏ᶠ (v : height_one_spectrum R), v.max_pow_dividing I = I :=
begin
  rw [← associated_iff_eq, ← associates.mk_eq_mk_iff_associated],
  apply associates.eq_of_eq_counts,
  { apply associates.finprod_ne_zero I },
  { apply associates.mk_ne_zero.mpr hI },
  intros v hv,
  obtain ⟨J, hJv⟩ := associates.exists_rep v,
  rw [← hJv, associates.irreducible_mk] at hv,
  rw ← hJv,
  apply ideal.finprod_count ⟨J, ideal.is_prime_of_prime (irreducible_iff_prime.mp hv),
    irreducible.ne_zero hv⟩ I hI,
end

/-- The ideal `I` equals the finprod `∏_v v^(val_v(I))`, when both sides are regarded as fractional
ideals of `R`. -/
lemma finprod_height_one_spectrum_factorization_coe (I : ideal R) (hI : I ≠ 0) :
  ∏ᶠ (v : height_one_spectrum R), (v.as_ideal : fractional_ideal R⁰ K) ^
    ((associates.mk v.as_ideal).count (associates.mk I).factors : ℤ) = I :=
begin
  conv_rhs { rw ← ideal.finprod_height_one_spectrum_factorization I hI },
  rw fractional_ideal.coe_ideal_finprod R⁰ K (le_refl _),
  simp_rw [is_dedekind_domain.height_one_spectrum.max_pow_dividing, fractional_ideal.coe_ideal_pow,
    zpow_coe_nat],
end

end ideal


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Floris van Doorn

The (classical) real numbers ℝ. This is a direct construction
from Cauchy sequences.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.conditionally_complete_lattice
import Mathlib.data.real.cau_seq_completion
import Mathlib.algebra.archimedean
import Mathlib.algebra.star.basic
import Mathlib.PostPort

namespace Mathlib

/-- The type `ℝ` of real numbers constructed as equivalence classes of Cauchy sequences of rational
numbers. -/
def real :=
  cau_seq.completion.Cauchy

notation:1024 "ℝ" => Mathlib.real

namespace real

def comm_ring_aux : comm_ring ℝ :=
  cau_seq.completion.Cauchy.comm_ring

protected instance comm_ring : comm_ring ℝ :=
  comm_ring.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add comm_ring.add_zero comm_ring.neg
    comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm comm_ring.mul comm_ring.mul_assoc comm_ring.one
    comm_ring.one_mul comm_ring.mul_one comm_ring.left_distrib comm_ring.right_distrib comm_ring.mul_comm

/- Extra instances to short-circuit type class resolution -/

protected instance ring : ring ℝ :=
  comm_ring.to_ring ℝ

protected instance comm_semiring : comm_semiring ℝ :=
  comm_ring.to_comm_semiring

protected instance semiring : semiring ℝ :=
  ring.to_semiring

protected instance add_comm_group : add_comm_group ℝ :=
  ring.to_add_comm_group ℝ

protected instance add_group : add_group ℝ :=
  add_comm_group.to_add_group ℝ

protected instance add_comm_monoid : add_comm_monoid ℝ :=
  add_comm_group.to_add_comm_monoid ℝ

protected instance add_monoid : add_monoid ℝ :=
  sub_neg_monoid.to_add_monoid ℝ

protected instance add_left_cancel_semigroup : add_left_cancel_semigroup ℝ :=
  add_left_cancel_monoid.to_add_left_cancel_semigroup ℝ

protected instance add_right_cancel_semigroup : add_right_cancel_semigroup ℝ :=
  add_right_cancel_monoid.to_add_right_cancel_semigroup ℝ

protected instance add_comm_semigroup : add_comm_semigroup ℝ :=
  add_comm_monoid.to_add_comm_semigroup ℝ

protected instance add_semigroup : add_semigroup ℝ :=
  add_monoid.to_add_semigroup ℝ

protected instance comm_monoid : comm_monoid ℝ :=
  comm_semiring.to_comm_monoid ℝ

protected instance monoid : monoid ℝ :=
  ring.to_monoid ℝ

protected instance comm_semigroup : comm_semigroup ℝ :=
  comm_ring.to_comm_semigroup ℝ

protected instance semigroup : semigroup ℝ :=
  monoid.to_semigroup ℝ

protected instance inhabited : Inhabited ℝ :=
  { default := 0 }

/-- The real numbers are a *-ring, with the trivial *-structure. -/
protected instance star_ring : star_ring ℝ :=
  star_ring_of_comm

/-- Coercion `ℚ` → `ℝ` as a `ring_hom`. Note that this
is `cau_seq.completion.of_rat`, not `rat.cast`. -/
def of_rat : ℚ →+* ℝ :=
  ring_hom.mk cau_seq.completion.of_rat sorry sorry sorry sorry

/-- Make a real number from a Cauchy sequence of rationals (by taking the equivalence class). -/
def mk (x : cau_seq ℚ abs) : ℝ :=
  cau_seq.completion.mk x

theorem of_rat_sub (x : ℚ) (y : ℚ) : coe_fn of_rat (x - y) = coe_fn of_rat x - coe_fn of_rat y :=
  congr_arg mk (cau_seq.const_sub x y)

protected instance has_lt : HasLess ℝ :=
  { Less := fun (x y : ℝ) => quotient.lift_on₂ x y Less sorry }

@[simp] theorem mk_lt {f : cau_seq ℚ abs} {g : cau_seq ℚ abs} : mk f < mk g ↔ f < g :=
  iff.rfl

theorem mk_eq {f : cau_seq ℚ abs} {g : cau_seq ℚ abs} : mk f = mk g ↔ f ≈ g :=
  cau_seq.completion.mk_eq

theorem quotient_mk_eq_mk (f : cau_seq ℚ abs) : quotient.mk f = mk f :=
  rfl

theorem mk_eq_mk {f : cau_seq ℚ abs} : cau_seq.completion.mk f = mk f :=
  rfl

@[simp] theorem mk_pos {f : cau_seq ℚ abs} : 0 < mk f ↔ cau_seq.pos f :=
  iff_of_eq (congr_arg cau_seq.pos (sub_zero f))

protected def le (x : ℝ) (y : ℝ) :=
  x < y ∨ x = y

protected instance has_le : HasLessEq ℝ :=
  { LessEq := real.le }

@[simp] theorem mk_le {f : cau_seq ℚ abs} {g : cau_seq ℚ abs} : mk f ≤ mk g ↔ f ≤ g :=
  or_congr iff.rfl quotient.eq

theorem add_lt_add_iff_left {a : ℝ} {b : ℝ} (c : ℝ) : c + a < c + b ↔ a < b := sorry

protected instance partial_order : partial_order ℝ :=
  partial_order.mk LessEq Less sorry sorry sorry

protected instance preorder : preorder ℝ :=
  partial_order.to_preorder ℝ

theorem of_rat_lt {x : ℚ} {y : ℚ} : coe_fn of_rat x < coe_fn of_rat y ↔ x < y :=
  cau_seq.const_lt

protected theorem zero_lt_one : 0 < 1 :=
  iff.mpr of_rat_lt zero_lt_one

protected theorem mul_pos {a : ℝ} {b : ℝ} : 0 < a → 0 < b → 0 < a * b := sorry

protected instance ordered_ring : ordered_ring ℝ :=
  ordered_ring.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add comm_ring.add_zero comm_ring.neg
    comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm comm_ring.mul comm_ring.mul_assoc comm_ring.one
    comm_ring.one_mul comm_ring.mul_one comm_ring.left_distrib comm_ring.right_distrib partial_order.le partial_order.lt
    partial_order.le_refl partial_order.le_trans partial_order.le_antisymm sorry sorry real.mul_pos

protected instance ordered_semiring : ordered_semiring ℝ :=
  ordered_ring.to_ordered_semiring

protected instance ordered_add_comm_group : ordered_add_comm_group ℝ :=
  ordered_ring.to_ordered_add_comm_group ℝ

protected instance ordered_cancel_add_comm_monoid : ordered_cancel_add_comm_monoid ℝ :=
  ordered_semiring.to_ordered_cancel_add_comm_monoid ℝ

protected instance ordered_add_comm_monoid : ordered_add_comm_monoid ℝ :=
  ordered_cancel_add_comm_monoid.to_ordered_add_comm_monoid

protected instance has_one : HasOne ℝ :=
  monoid.to_has_one ℝ

protected instance has_zero : HasZero ℝ :=
  mul_zero_class.to_has_zero ℝ

protected instance has_mul : Mul ℝ :=
  distrib.to_has_mul ℝ

protected instance has_add : Add ℝ :=
  distrib.to_has_add ℝ

protected instance has_sub : Sub ℝ :=
  sub_neg_monoid.to_has_sub ℝ

protected instance nontrivial : nontrivial ℝ :=
  nontrivial.mk (Exists.intro 0 (Exists.intro 1 (ne_of_lt real.zero_lt_one)))

protected instance linear_order : linear_order ℝ :=
  linear_order.mk partial_order.le partial_order.lt partial_order.le_refl partial_order.le_trans partial_order.le_antisymm
    sorry (fun (a b : ℝ) => classical.prop_decidable (a ≤ b)) Mathlib.decidable_eq_of_decidable_le
    Mathlib.decidable_lt_of_decidable_le

protected instance linear_ordered_comm_ring : linear_ordered_comm_ring ℝ :=
  linear_ordered_comm_ring.mk ordered_ring.add ordered_ring.add_assoc ordered_ring.zero ordered_ring.zero_add
    ordered_ring.add_zero ordered_ring.neg ordered_ring.sub ordered_ring.add_left_neg ordered_ring.add_comm
    ordered_ring.mul ordered_ring.mul_assoc ordered_ring.one ordered_ring.one_mul ordered_ring.mul_one
    ordered_ring.left_distrib ordered_ring.right_distrib ordered_ring.le ordered_ring.lt ordered_ring.le_refl
    ordered_ring.le_trans ordered_ring.le_antisymm ordered_ring.add_le_add_left ordered_ring.zero_le_one
    ordered_ring.mul_pos linear_order.le_total linear_order.decidable_le linear_order.decidable_eq
    linear_order.decidable_lt nontrivial.exists_pair_ne comm_ring.mul_comm

/- Extra instances to short-circuit type class resolution -/

protected instance linear_ordered_ring : linear_ordered_ring ℝ :=
  linear_ordered_comm_ring.to_linear_ordered_ring ℝ

protected instance linear_ordered_semiring : linear_ordered_semiring ℝ :=
  linear_ordered_comm_ring.to_linear_ordered_semiring

protected instance domain : domain ℝ :=
  domain.mk comm_ring.add comm_ring.add_assoc comm_ring.zero comm_ring.zero_add comm_ring.add_zero comm_ring.neg
    comm_ring.sub comm_ring.add_left_neg comm_ring.add_comm comm_ring.mul comm_ring.mul_assoc comm_ring.one
    comm_ring.one_mul comm_ring.mul_one comm_ring.left_distrib comm_ring.right_distrib nontrivial.exists_pair_ne sorry

protected instance linear_ordered_field : linear_ordered_field ℝ := sorry

/- Extra instances to short-circuit type class resolution -/

protected instance linear_ordered_add_comm_group : linear_ordered_add_comm_group ℝ :=
  linear_ordered_ring.to_linear_ordered_add_comm_group

protected instance field : field ℝ :=
  linear_ordered_field.to_field ℝ

protected instance division_ring : division_ring ℝ :=
  field.to_division_ring

protected instance integral_domain : integral_domain ℝ :=
  field.to_integral_domain

protected instance distrib_lattice : distrib_lattice ℝ :=
  Mathlib.distrib_lattice_of_linear_order

protected instance lattice : lattice ℝ :=
  Mathlib.lattice_of_linear_order

protected instance semilattice_inf : semilattice_inf ℝ :=
  lattice.to_semilattice_inf ℝ

protected instance semilattice_sup : semilattice_sup ℝ :=
  lattice.to_semilattice_sup ℝ

protected instance has_inf : has_inf ℝ :=
  semilattice_inf.to_has_inf ℝ

protected instance has_sup : has_sup ℝ :=
  semilattice_sup.to_has_sup ℝ

protected instance decidable_lt (a : ℝ) (b : ℝ) : Decidable (a < b) :=
  has_lt.lt.decidable a b

protected instance decidable_le (a : ℝ) (b : ℝ) : Decidable (a ≤ b) :=
  has_le.le.decidable a b

protected instance decidable_eq (a : ℝ) (b : ℝ) : Decidable (a = b) :=
  quotient.decidable_eq a b

@[simp] theorem of_rat_eq_cast (x : ℚ) : coe_fn of_rat x = ↑x :=
  ring_hom.eq_rat_cast of_rat

theorem le_mk_of_forall_le {x : ℝ} {f : cau_seq ℚ abs} : (∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → x ≤ ↑(coe_fn f j)) → x ≤ mk f := sorry

theorem mk_le_of_forall_le {f : cau_seq ℚ abs} {x : ℝ} : (∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → ↑(coe_fn f j) ≤ x) → mk f ≤ x := sorry

theorem mk_near_of_forall_near {f : cau_seq ℚ abs} {x : ℝ} {ε : ℝ} (H : ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abs (↑(coe_fn f j) - x) ≤ ε) : abs (mk f - x) ≤ ε := sorry

protected instance archimedean : archimedean ℝ :=
  iff.mpr archimedean_iff_rat_le fun (x : ℝ) => quotient.induction_on x fun (f : cau_seq ℚ abs) => sorry

/- mark `real` irreducible in order to prevent `auto_cases` unfolding reals,
since users rarely want to consider real numbers as Cauchy sequences.
Marking `comm_ring_aux` `irreducible` is done to ensure that there are no problems
with non definitionally equal instances, caused by making `real` irreducible-/

protected instance floor_ring : floor_ring ℝ :=
  archimedean.floor_ring ℝ

theorem is_cau_seq_iff_lift {f : ℕ → ℚ} : is_cau_seq abs f ↔ is_cau_seq abs fun (i : ℕ) => ↑(f i) := sorry

theorem of_near (f : ℕ → ℚ) (x : ℝ) (h : ∀ (ε : ℝ), ε > 0 → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abs (↑(f j) - x) < ε) : ∃ (h' : is_cau_seq abs f), mk { val := f, property := h' } = x := sorry

theorem exists_floor (x : ℝ) : ∃ (ub : ℤ), ↑ub ≤ x ∧ ∀ (z : ℤ), ↑z ≤ x → z ≤ ub := sorry

theorem exists_sup (S : set ℝ) : (∃ (x : ℝ), x ∈ S) → (∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) → ∃ (x : ℝ), ∀ (y : ℝ), x ≤ y ↔ ∀ (z : ℝ), z ∈ S → z ≤ y := sorry

protected instance has_Sup : has_Sup ℝ :=
  has_Sup.mk
    fun (S : set ℝ) =>
      dite ((∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x)
        (fun (h : (∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) => classical.some sorry)
        fun (h : ¬((∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x)) => 0

theorem Sup_def (S : set ℝ) : Sup S =
  dite ((∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x)
    (fun (h : (∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) =>
      classical.some (exists_sup S (and.left h) (and.right h)))
    fun (h : ¬((∃ (x : ℝ), x ∈ S) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x)) => 0 :=
  rfl

theorem Sup_le (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) {y : ℝ} : Sup S ≤ y ↔ ∀ (z : ℝ), z ∈ S → z ≤ y := sorry

-- this proof times out without this

theorem lt_Sup (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) {y : ℝ} : y < Sup S ↔ ∃ (z : ℝ), ∃ (H : z ∈ S), y < z := sorry

theorem le_Sup (S : set ℝ) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → y ≤ x) {x : ℝ} (xS : x ∈ S) : x ≤ Sup S :=
  iff.mp (Sup_le S (Exists.intro x xS) h₂) (le_refl (Sup S)) x xS

theorem Sup_le_ub (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) {ub : ℝ} (h₂ : ∀ (y : ℝ), y ∈ S → y ≤ ub) : Sup S ≤ ub :=
  iff.mpr (Sup_le S h₁ (Exists.intro ub h₂)) h₂

protected theorem is_lub_Sup {s : set ℝ} {a : ℝ} {b : ℝ} (ha : a ∈ s) (hb : b ∈ upper_bounds s) : is_lub s (Sup s) :=
  { left := fun (x : ℝ) (xs : x ∈ s) => le_Sup s (Exists.intro b hb) xs,
    right := fun (u : ℝ) (h : u ∈ upper_bounds s) => Sup_le_ub s (Exists.intro a ha) h }

protected instance has_Inf : has_Inf ℝ :=
  has_Inf.mk fun (S : set ℝ) => -Sup (set_of fun (x : ℝ) => -x ∈ S)

theorem Inf_def (S : set ℝ) : Inf S = -Sup (set_of fun (x : ℝ) => -x ∈ S) :=
  rfl

theorem le_Inf (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → x ≤ y) {y : ℝ} : y ≤ Inf S ↔ ∀ (z : ℝ), z ∈ S → y ≤ z := sorry

-- this proof times out without this

theorem Inf_lt (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → x ≤ y) {y : ℝ} : Inf S < y ↔ ∃ (z : ℝ), ∃ (H : z ∈ S), z < y := sorry

theorem Inf_le (S : set ℝ) (h₂ : ∃ (x : ℝ), ∀ (y : ℝ), y ∈ S → x ≤ y) {x : ℝ} (xS : x ∈ S) : Inf S ≤ x :=
  iff.mp (le_Inf S (Exists.intro x xS) h₂) (le_refl (Inf S)) x xS

theorem lb_le_Inf (S : set ℝ) (h₁ : ∃ (x : ℝ), x ∈ S) {lb : ℝ} (h₂ : ∀ (y : ℝ), y ∈ S → lb ≤ y) : lb ≤ Inf S :=
  iff.mpr (le_Inf S h₁ (Exists.intro lb h₂)) h₂

protected instance conditionally_complete_linear_order : conditionally_complete_linear_order ℝ :=
  conditionally_complete_linear_order.mk lattice.sup linear_order.le linear_order.lt linear_order.le_refl
    linear_order.le_trans linear_order.le_antisymm lattice.le_sup_left lattice.le_sup_right lattice.sup_le lattice.inf
    lattice.inf_le_left lattice.inf_le_right lattice.le_inf Sup Inf sorry sorry sorry sorry linear_order.le_total
    (classical.dec_rel LessEq) linear_order.decidable_eq linear_order.decidable_lt

theorem Sup_empty : Sup ∅ = 0 := sorry

theorem Sup_of_not_bdd_above {s : set ℝ} (hs : ¬bdd_above s) : Sup s = 0 :=
  dif_neg fun (h : (∃ (x : ℝ), x ∈ s) ∧ ∃ (x : ℝ), ∀ (y : ℝ), y ∈ s → y ≤ x) => hs (and.right h)

theorem Sup_univ : Sup set.univ = 0 := sorry

theorem Inf_empty : Inf ∅ = 0 := sorry

theorem Inf_of_not_bdd_below {s : set ℝ} (hs : ¬bdd_below s) : Inf s = 0 := sorry

theorem cau_seq_converges (f : cau_seq ℝ abs) : ∃ (x : ℝ), f ≈ cau_seq.const abs x := sorry

protected instance abs.cau_seq.is_complete : cau_seq.is_complete ℝ abs :=
  cau_seq.is_complete.mk cau_seq_converges


/-
Copyright (c) 2020 Alexander Bentkamp. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Alexander Bentkamp.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.field_theory.algebraic_closure
import Mathlib.linear_algebra.finsupp
import Mathlib.linear_algebra.matrix
import Mathlib.PostPort

universes v w u_1 u_2

namespace Mathlib

/-!
# Eigenvectors and eigenvalues

This file defines eigenspaces, eigenvalues, and eigenvalues, as well as their generalized
counterparts. We follow Axler's approach [axler2015] because it allows us to derive many properties
without choosing a basis and without using matrices.

An eigenspace of a linear map `f` for a scalar `μ` is the kernel of the map `(f - μ • id)`. The
nonzero elements of an eigenspace are eigenvectors `x`. They have the property `f x = μ • x`. If
there are eigenvectors for a scalar `μ`, the scalar `μ` is called an eigenvalue.

There is no consensus in the literature whether `0` is an eigenvector. Our definition of
`has_eigenvector` permits only nonzero vectors. For an eigenvector `x` that may also be `0`, we
write `x ∈ f.eigenspace μ`.

A generalized eigenspace of a linear map `f` for a natural number `k` and a scalar `μ` is the kernel
of the map `(f - μ • id) ^ k`. The nonzero elements of a generalized eigenspace are generalized
eigenvectors `x`. If there are generalized eigenvectors for a natural number `k` and a scalar `μ`,
the scalar `μ` is called a generalized eigenvalue.

## References

* [Sheldon Axler, *Linear Algebra Done Right*][axler2015]
* https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors

## Tags

eigenspace, eigenvector, eigenvalue, eigen
-/

namespace module

namespace End

/-- The submodule `eigenspace f μ` for a linear map `f` and a scalar `μ` consists of all vectors `x`
    such that `f x = μ • x`. (Def 5.36 of [axler2015])-/
def eigenspace {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M] (f : End R M)
    (μ : R) : submodule R M :=
  linear_map.ker (f - coe_fn (algebra_map R (End R M)) μ)

/-- A nonzero element of an eigenspace is an eigenvector. (Def 5.7 of [axler2015]) -/
def has_eigenvector {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M]
    (f : End R M) (μ : R) (x : M) :=
  x ≠ 0 ∧ x ∈ eigenspace f μ

/-- A scalar `μ` is an eigenvalue for a linear map `f` if there are nonzero vectors `x`
    such that `f x = μ • x`. (Def 5.5 of [axler2015]) -/
def has_eigenvalue {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M]
    (f : End R M) (a : R) :=
  eigenspace f a ≠ ⊥

theorem mem_eigenspace_iff {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M]
    {f : End R M} {μ : R} {x : M} : x ∈ eigenspace f μ ↔ coe_fn f x = μ • x :=
  sorry

theorem eigenspace_div {K : Type v} {V : Type w} [field K] [add_comm_group V] [vector_space K V]
    (f : End K V) (a : K) (b : K) (hb : b ≠ 0) :
    eigenspace f (a / b) = linear_map.ker (b • f - coe_fn (algebra_map K (End K V)) a) :=
  sorry

theorem eigenspace_aeval_polynomial_degree_1 {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] (f : End K V) (q : polynomial K) (hq : polynomial.degree q = 1) :
    eigenspace f (-polynomial.coeff q 0 / polynomial.leading_coeff q) =
        linear_map.ker (coe_fn (polynomial.aeval f) q) :=
  sorry

theorem ker_aeval_ring_hom'_unit_polynomial {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] (f : End K V) (c : units (polynomial K)) :
    linear_map.ker (coe_fn (polynomial.aeval f) ↑c) = ⊥ :=
  sorry

theorem aeval_apply_of_has_eigenvector {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] {f : End K V} {p : polynomial K} {μ : K} {x : V}
    (h : has_eigenvector f μ x) :
    coe_fn (coe_fn (polynomial.aeval f) p) x = polynomial.eval μ p • x :=
  sorry

theorem is_root_of_has_eigenvalue {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] {f : End K V} {μ : K} (h : has_eigenvalue f μ) :
    polynomial.is_root (minpoly K f) μ :=
  sorry

protected theorem is_integral {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] [finite_dimensional K V] (f : End K V) : is_integral K f :=
  is_integral_of_noetherian (finite_dimensional.linear_map K V V) f

theorem has_eigenvalue_of_is_root {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] [finite_dimensional K V] {f : End K V} {μ : K}
    (h : polynomial.is_root (minpoly K f) μ) : has_eigenvalue f μ :=
  sorry

theorem has_eigenvalue_iff_is_root {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] [finite_dimensional K V] {f : End K V} {μ : K} :
    has_eigenvalue f μ ↔ polynomial.is_root (minpoly K f) μ :=
  { mp := is_root_of_has_eigenvalue, mpr := has_eigenvalue_of_is_root }

/-- Every linear operator on a vector space over an algebraically closed field has
    an eigenvalue. (Lemma 5.21 of [axler2015]) -/
theorem exists_eigenvalue {K : Type v} {V : Type w} [field K] [add_comm_group V] [vector_space K V]
    [is_alg_closed K] [finite_dimensional K V] [nontrivial V] (f : End K V) :
    ∃ (c : K), has_eigenvalue f c :=
  sorry

/-- Eigenvectors corresponding to distinct eigenvalues of a linear operator are linearly
    independent. (Lemma 5.10 of [axler2015])

    We use the eigenvalues as indexing set to ensure that there is only one eigenvector for each
    eigenvalue in the image of `xs`. -/
theorem eigenvectors_linear_independent {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] (f : End K V) (μs : set K) (xs : ↥μs → V)
    (h_eigenvec : ∀ (μ : ↥μs), has_eigenvector f (↑μ) (xs μ)) : linear_independent K xs :=
  sorry

/-- The generalized eigenspace for a linear map `f`, a scalar `μ`, and an exponent `k ∈ ℕ` is the
    kernel of `(f - μ • id) ^ k`. (Def 8.10 of [axler2015])-/
def generalized_eigenspace {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M]
    (f : End R M) (μ : R) (k : ℕ) : submodule R M :=
  linear_map.ker ((f - coe_fn (algebra_map R (End R M)) μ) ^ k)

/-- A nonzero element of a generalized eigenspace is a generalized eigenvector.
    (Def 8.9 of [axler2015])-/
def has_generalized_eigenvector {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M]
    [module R M] (f : End R M) (μ : R) (k : ℕ) (x : M) :=
  x ≠ 0 ∧ x ∈ generalized_eigenspace f μ k

/-- A scalar `μ` is a generalized eigenvalue for a linear map `f` and an exponent `k ∈ ℕ` if there
    are generalized eigenvectors for `f`, `k`, and `μ`. -/
def has_generalized_eigenvalue {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M]
    [module R M] (f : End R M) (μ : R) (k : ℕ) :=
  generalized_eigenspace f μ k ≠ ⊥

/-- The generalized eigenrange for a linear map `f`, a scalar `μ`, and an exponent `k ∈ ℕ` is the
    range of `(f - μ • id) ^ k`. -/
def generalized_eigenrange {R : Type v} {M : Type w} [comm_ring R] [add_comm_group M] [module R M]
    (f : End R M) (μ : R) (k : ℕ) : submodule R M :=
  linear_map.range ((f - coe_fn (algebra_map R (End R M)) μ) ^ k)

/-- The exponent of a generalized eigenvalue is never 0. -/
theorem exp_ne_zero_of_has_generalized_eigenvalue {R : Type v} {M : Type w} [comm_ring R]
    [add_comm_group M] [module R M] {f : End R M} {μ : R} {k : ℕ}
    (h : has_generalized_eigenvalue f μ k) : k ≠ 0 :=
  id
    fun (ᾰ : k = 0) =>
      Eq._oldrec (fun (h : has_generalized_eigenvalue f μ 0) => h linear_map.ker_id) (Eq.symm ᾰ) h

/-- A generalized eigenspace for some exponent `k` is contained in
    the generalized eigenspace for exponents larger than `k`. -/
theorem generalized_eigenspace_mono {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] {f : End K V} {μ : K} {k : ℕ} {m : ℕ} (hm : k ≤ m) :
    generalized_eigenspace f μ k ≤ generalized_eigenspace f μ m :=
  sorry

/-- A generalized eigenvalue for some exponent `k` is also
    a generalized eigenvalue for exponents larger than `k`. -/
theorem has_generalized_eigenvalue_of_has_generalized_eigenvalue_of_le {K : Type v} {V : Type w}
    [field K] [add_comm_group V] [vector_space K V] {f : End K V} {μ : K} {k : ℕ} {m : ℕ}
    (hm : k ≤ m) (hk : has_generalized_eigenvalue f μ k) : has_generalized_eigenvalue f μ m :=
  sorry

/-- The eigenspace is a subspace of the generalized eigenspace. -/
theorem eigenspace_le_generalized_eigenspace {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] {f : End K V} {μ : K} {k : ℕ} (hk : 0 < k) :
    eigenspace f μ ≤ generalized_eigenspace f μ k :=
  generalized_eigenspace_mono (nat.succ_le_of_lt hk)

/-- All eigenvalues are generalized eigenvalues. -/
theorem has_generalized_eigenvalue_of_has_eigenvalue {K : Type v} {V : Type w} [field K]
    [add_comm_group V] [vector_space K V] {f : End K V} {μ : K} {k : ℕ} (hk : 0 < k)
    (hμ : has_eigenvalue f μ) : has_generalized_eigenvalue f μ k :=
  sorry

/-- Every generalized eigenvector is a generalized eigenvector for exponent `findim K V`.
    (Lemma 8.11 of [axler2015]) -/
theorem generalized_eigenspace_le_generalized_eigenspace_findim {K : Type v} {V : Type w} [field K]
    [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : End K V) (μ : K) (k : ℕ) :
    generalized_eigenspace f μ k ≤ generalized_eigenspace f μ (finite_dimensional.findim K V) :=
  ker_pow_le_ker_pow_findim (f - coe_fn (algebra_map K (End K V)) μ) k

/-- Generalized eigenspaces for exponents at least `findim K V` are equal to each other. -/
theorem generalized_eigenspace_eq_generalized_eigenspace_findim_of_le {K : Type v} {V : Type w}
    [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : End K V) (μ : K)
    {k : ℕ} (hk : finite_dimensional.findim K V ≤ k) :
    generalized_eigenspace f μ k = generalized_eigenspace f μ (finite_dimensional.findim K V) :=
  ker_pow_eq_ker_pow_findim_of_le hk

/-- If `f` maps a subspace `p` into itself, then the generalized eigenspace of the restriction
    of `f` to `p` is the part of the generalized eigenspace of `f` that lies in `p`. -/
theorem generalized_eigenspace_restrict {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] (f : End K V) (p : submodule K V) (k : ℕ) (μ : K)
    (hfp : ∀ (x : V), x ∈ p → coe_fn f x ∈ p) :
    generalized_eigenspace (linear_map.restrict f hfp) μ k =
        submodule.comap (submodule.subtype p) (generalized_eigenspace f μ k) :=
  sorry

/-- Generalized eigenrange and generalized eigenspace for exponent `findim K V` are disjoint. -/
theorem generalized_eigenvec_disjoint_range_ker {K : Type v} {V : Type w} [field K]
    [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : End K V) (μ : K) :
    disjoint (generalized_eigenrange f μ (finite_dimensional.findim K V))
        (generalized_eigenspace f μ (finite_dimensional.findim K V)) :=
  sorry

/-- The generalized eigenspace of an eigenvalue has positive dimension for positive exponents. -/
theorem pos_findim_generalized_eigenspace_of_has_eigenvalue {K : Type v} {V : Type w} [field K]
    [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : End K V} {k : ℕ} {μ : K}
    (hx : has_eigenvalue f μ) (hk : 0 < k) :
    0 < finite_dimensional.findim K ↥(generalized_eigenspace f μ k) :=
  sorry

/-- A linear map maps a generalized eigenrange into itself. -/
theorem map_generalized_eigenrange_le {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] {f : End K V} {μ : K} {n : ℕ} :
    submodule.map f (generalized_eigenrange f μ n) ≤ generalized_eigenrange f μ n :=
  sorry

/-- The generalized eigenvectors span the entire vector space (Lemma 8.21 of [axler2015]). -/
theorem supr_generalized_eigenspace_eq_top {K : Type v} {V : Type w} [field K] [add_comm_group V]
    [vector_space K V] [is_alg_closed K] [finite_dimensional K V] (f : End K V) :
    (supr fun (μ : K) => supr fun (k : ℕ) => generalized_eigenspace f μ k) = ⊤ :=
  sorry

end End

end module

protected theorem linear_map.is_integral {K : Type u_1} {V : Type u_2} [field K] [add_comm_group V]
    [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) : is_integral K f :=
  module.End.is_integral f

end Mathlib


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Yury Kudryashov
-/
import algebra.algebra.equiv

/-!
# The R-algebra structure on families of R-algebras

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

The R-algebra structure on `Π i : I, A i` when each `A i` is an R-algebra.

## Main defintions

* `pi.algebra`
* `pi.eval_alg_hom`
* `pi.const_alg_hom`
-/
universes u v w

namespace pi

variable {I : Type u}     -- The indexing type
variable {R : Type*}      -- The scalar type
variable {f : I → Type v} -- The family of types already equipped with instances
variables (x y : Π i, f i) (i : I)
variables (I f)

instance algebra {r : comm_semiring R}
  [s : ∀ i, semiring (f i)] [∀ i, algebra R (f i)] :
  algebra R (Π i : I, f i) :=
{ commutes' := λ a f, begin ext, simp [algebra.commutes], end,
  smul_def' := λ a f, begin ext, simp [algebra.smul_def], end,
  ..(pi.ring_hom (λ i, algebra_map R (f i)) : R →+* Π i : I, f i) }

lemma algebra_map_def {r : comm_semiring R}
  [s : ∀ i, semiring (f i)] [∀ i, algebra R (f i)] (a : R) :
  algebra_map R (Π i, f i) a = (λ i, algebra_map R (f i) a) := rfl

@[simp] lemma algebra_map_apply {r : comm_semiring R}
  [s : ∀ i, semiring (f i)] [∀ i, algebra R (f i)] (a : R) (i : I) :
  algebra_map R (Π i, f i) a i = algebra_map R (f i) a := rfl

-- One could also build a `Π i, R i`-algebra structure on `Π i, A i`,
-- when each `A i` is an `R i`-algebra, although I'm not sure that it's useful.

variables {I} (R) (f)

/-- `function.eval` as an `alg_hom`. The name matches `pi.eval_ring_hom`, `pi.eval_monoid_hom`,
etc. -/
@[simps]
def eval_alg_hom {r : comm_semiring R} [Π i, semiring (f i)] [Π i, algebra R (f i)] (i : I) :
  (Π i, f i) →ₐ[R] f i :=
{ to_fun := λ f, f i, commutes' := λ r, rfl, .. pi.eval_ring_hom f i}

variables (A B : Type*) [comm_semiring R] [semiring B] [algebra R B]

/-- `function.const` as an `alg_hom`. The name matches `pi.const_ring_hom`, `pi.const_monoid_hom`,
etc. -/
@[simps]
def const_alg_hom : B →ₐ[R] (A → B) :=
{ to_fun := function.const _,
  commutes' := λ r, rfl,
  .. pi.const_ring_hom A B}

/-- When `R` is commutative and permits an `algebra_map`, `pi.const_ring_hom` is equal to that
map. -/
@[simp]

@[simp] lemma const_alg_hom_eq_algebra_of_id : const_alg_hom R A R = algebra.of_id R (A → R) :=
rfl

end pi

/-- A special case of `pi.algebra` for non-dependent types. Lean struggles to elaborate
definitions elsewhere in the library without this, -/
instance function.algebra {R : Type*} (I : Type*)  (A : Type*) [comm_semiring R]
  [semiring A] [algebra R A] : algebra R (I → A) :=
pi.algebra _ _

namespace alg_hom

variables {R : Type u} {A : Type v} {B : Type w} {I : Type*}

variables [comm_semiring R] [semiring A] [semiring B]
variables [algebra R A] [algebra R B]

/-- `R`-algebra homomorphism between the function spaces `I → A` and `I → B`, induced by an
`R`-algebra homomorphism `f` between `A` and `B`. -/
@[simps] protected def comp_left (f : A →ₐ[R] B) (I : Type*) : (I → A) →ₐ[R] (I → B) :=
{ to_fun := λ h, f ∘ h,
  commutes' := λ c, by { ext, exact f.commutes' c },
  .. f.to_ring_hom.comp_left I }

end alg_hom

namespace alg_equiv

/-- A family of algebra equivalences `Π j, (A₁ j ≃ₐ A₂ j)` generates a
multiplicative equivalence between `Π j, A₁ j` and `Π j, A₂ j`.

This is the `alg_equiv` version of `equiv.Pi_congr_right`, and the dependent version of
`alg_equiv.arrow_congr`.
-/
@[simps apply]
def Pi_congr_right {R ι : Type*} {A₁ A₂ : ι → Type*} [comm_semiring R]
  [Π i, semiring (A₁ i)] [Π i, semiring (A₂ i)] [Π i, algebra R (A₁ i)] [Π i, algebra R (A₂ i)]
  (e : Π i, A₁ i ≃ₐ[R] A₂ i) : (Π i, A₁ i) ≃ₐ[R] Π i, A₂ i :=
{ to_fun := λ x j, e j (x j),
  inv_fun := λ x j, (e j).symm (x j),
  commutes' := λ r, by { ext i, simp },
  .. @ring_equiv.Pi_congr_right ι A₁ A₂ _ _ (λ i, (e i).to_ring_equiv) }

@[simp]
lemma Pi_congr_right_refl {R ι : Type*} {A : ι → Type*} [comm_semiring R]
  [Π i, semiring (A i)] [Π i, algebra R (A i)] :
  Pi_congr_right (λ i, (alg_equiv.refl : A i ≃ₐ[R] A i)) = alg_equiv.refl := rfl

@[simp]
lemma Pi_congr_right_symm {R ι : Type*} {A₁ A₂ : ι → Type*} [comm_semiring R]
  [Π i, semiring (A₁ i)] [Π i, semiring (A₂ i)] [Π i, algebra R (A₁ i)] [Π i, algebra R (A₂ i)]
  (e : Π i, A₁ i ≃ₐ[R] A₂ i) : (Pi_congr_right e).symm = (Pi_congr_right $ λ i, (e i).symm) := rfl

@[simp]
lemma Pi_congr_right_trans {R ι : Type*} {A₁ A₂ A₃ : ι → Type*} [comm_semiring R]
  [Π i, semiring (A₁ i)] [Π i, semiring (A₂ i)] [Π i, semiring (A₃ i)]
  [Π i, algebra R (A₁ i)] [Π i, algebra R (A₂ i)] [Π i, algebra R (A₃ i)]
  (e₁ : Π i, A₁ i ≃ₐ[R] A₂ i) (e₂ : Π i, A₂ i ≃ₐ[R] A₃ i) :
  (Pi_congr_right e₁).trans (Pi_congr_right e₂) = (Pi_congr_right $ λ i, (e₁ i).trans (e₂ i)) :=
rfl

end alg_equiv


import category_theory.category.default

universes v u  -- The order in this declaration matters: v often needs to be explicitly specified while u often can be omitted

namespace category_theory

variables (C : Type u) [category.{v} C]

--rewrite this

/-
# Category world

## Level 7: Composition of monomorphisms

Now we show that the composition of two monomorphisms produces another monomorphism.-/

/- Lemma
If $$f : X ⟶ Y$$ and $$g : X ⟶ Y$$ are monomorphisms, then $$f ≫ g : X ⟶ Z$$ is a monomorphism.
-/
lemma mono_comp' {X Y Z : C} (f : X ⟶ Y) [mono f] (g : Y ⟶ Z) [mono g] : mono (f ≫ g) :=
begin
    split,
    intros Z h l hyp,
    rw ← cancel_mono f,
    rw ← cancel_mono g,
    rw category.assoc,
    rw category.assoc,
    exact hyp,
end

end category_theory