-- Formalization of matrices defined over 'grid's.

import grid utils data.vector2 tactic.elide

open utils

namespace matrix

structure matrix (m n : ℕ) (α : Type) :=
  (g  : dep_vec_grid α m n)

private lemma matrix_of_f_ {x : ℤ} {m} (h : x < ↑m) (h₁ : 0 ≤ x) : |x| < m :=
  by rwa [← int.coe_nat_lt_coe_nat_iff, int.nat_abs_of_nonneg h₁]

def matrix_of_f {m n} {α} (h : m * n > 0) (f : fin m → fin n → α) : matrix m n α :=
  ⟨⟨h, ⟨℘(fgrid₀.mk m n h ⟨0, 0⟩
    (λx y, f
      ⟨|x.1|,
         begin
           rcases x with ⟨x, ⟨hx₁, hx₂⟩⟩, simp at hx₁ hx₂ ⊢,
           exact matrix_of_f_ hx₂ hx₁
         end
       ⟩
      ⟨|y.1|,
         begin
           rcases y with ⟨y, ⟨hy₁, hy₂⟩⟩, simp at hy₁ hy₂ ⊢,
           exact matrix_of_f_ hy₂ hy₁
         end
      ⟩)), by simpa [length_generate_eq_size, size]⟩⟩⟩

private lemma matrix_at_ {m n} {α} (m₁ : matrix m n α) (i : fin m) :
  (grid.bl (vec_grid_of_dep_vec_grid (m₁.g))).y ≤ ↑(i.val) ∧
  ↑(i.val) < (gtr (vec_grid_of_dep_vec_grid (m₁.g))).y :=
begin
  rcases m₁ with ⟨⟨h, ⟨d, hd⟩⟩⟩,
  simp [grid.bl, expand_gtr, relative_grid.rows, vec_grid_of_dep_vec_grid],
  norm_cast,
  exact ⟨zero_le _, i.2⟩
end

private lemma matrix_at_ {m n} {α} (m₁ : matrix m n α) (j : fin n) :
  (grid.bl (vec_grid_of_dep_vec_grid (m₁.g))).x ≤ ↑(j.val) ∧
  ↑(j.val) < (gtr (vec_grid_of_dep_vec_grid (m₁.g))).x :=
begin
  rcases m₁ with ⟨⟨h, ⟨d, hd⟩⟩⟩,
  simp [grid.bl, expand_gtr, relative_grid.rows, vec_grid_of_dep_vec_grid],
  norm_cast,
  exact ⟨zero_le _, j.2⟩
end

def matrix_at {m n} {α} (m₁ : matrix m n α) (i : fin m) (j : fin n) : α :=
  abs_data (vec_grid_of_dep_vec_grid m₁.1)
    ⟨⟨i.1, matrix_at_ m₁ i⟩, ⟨j.1, matrix_at_ m₁ j⟩⟩

@[simp]
lemma matrix_get_mk {α} {m n} (h : m * n > 0)
  (f : fin m → fin n → α) (i : fin m) (j : fin n) :
  matrix_at (matrix_of_f h f) i j = f i j :=
begin
  simp [matrix_of_f, matrix_at], unfold_projs,
  delta vec_grid_of_dep_vec_grid, unfold_projs,
  rw abs_data_eq_nth_v₀, unfold_projs,
  simp [
    vector.nth, nth_generate, grid.bl, relative_grid.cols,
    grid_point_to_fin, rel_point_to_fin, relpoint_of_gpoint,
    abs_data, relative_grid.contents
  ],
  cases i with i hi, cases j with i hj,
  have intzero_add : ∀x, int.zero + x = x,
    by intros; simp [int.zero]; rw int.of_nat_eq_coe; ring,
  congr,
    {
      simp, norm_cast,
      have : ↑i + -int.zero = ↑i, by ring, simp [this, intzero_add],
      unfold_coes, simp [fin.val],
      rw [← int.coe_nat_eq_coe_nat_iff, int.coe_nat_div, int.coe_nat_add], simp,
      have h₁ : int.of_nat i + -int.zero = ↑i, by ring,
      have : int.of_nat i + -int.zero ≥ (0 : ℤ), by norm_cast,
      rw int.nat_abs_of_nonneg, swap 2, exact this,
      have : ↑n ≠ (0 : ℤ), by simp; clear_except h; intros contra; subst contra; linarith,
      rw int.add_mul_div_right _ _ this,
      have : ↑i / ↑n = (0 : ℤ), by norm_cast; exact nat.div_eq_of_lt hj, simp [this],
      ring
    },
    {
      have : -int.zero = int.zero, by refl, simp [this, intzero_add],
      unfold_coes, simp [fin.val], rw ← int.coe_nat_eq_coe_nat_iff,
      have : int.of_nat i % int.of_nat n ≥ (0 : ℤ), by norm_cast,
      rw int.nat_abs_of_nonneg this, repeat { rw int.of_nat_eq_coe },
      rw [← int.coe_nat_mod, int.coe_nat_eq_coe_nat_iff],
      exact nat.mod_eq_of_lt hj
    }
end

section ext

variables {m n : ℕ} {α : Type} {m₁ m₂ : matrix m n α}

theorem ext_iff : m₁.g = m₂.g ↔ m₁ = m₂ :=
  by cases m₁; rcases m₂; simp

@[extensionality] theorem ext : m₁.g = m₂.g → m₁ = m₂ := ext_iff.1

@[extensionality]
lemma m_ext {m₁ m₂ : matrix m n α}
  (h : ∀i j, matrix_at m₁ i j = matrix_at m₂ i j) : m₁ = m₂ :=
begin
  let m₁' := @matrix_of_f m n _ m₁.1.1
    (λx y, abs_data (vec_grid_of_dep_vec_grid m₁.1) ⟨⟨x.1, ⟨_, _⟩⟩, ⟨y.1, ⟨_, _⟩⟩⟩),
  let m₂' := @matrix_of_f m n _ m₂.1.1
    (λx y, abs_data (vec_grid_of_dep_vec_grid m₂.1) ⟨⟨x.1, ⟨_, _⟩⟩, ⟨y.1, ⟨_, _⟩⟩⟩),
  swap 2, { simp [vec_grid_of_dep_vec_grid] },
  swap 2, {
    simp [vec_grid_of_dep_vec_grid, expand_gtr, grid.bl, relative_grid.rows, x.2]
  },
  swap 2, { simp [vec_grid_of_dep_vec_grid] },
  swap 2, {
    simp [vec_grid_of_dep_vec_grid, expand_gtr, grid.bl, relative_grid.cols, y.2]
  },
  swap 2, { simp [vec_grid_of_dep_vec_grid] },
  swap 2, {
    simp [vec_grid_of_dep_vec_grid, expand_gtr, grid.bl, relative_grid.rows, x.2]
  },
  swap 2, { simp [vec_grid_of_dep_vec_grid] },
  swap 2, {
    simp [vec_grid_of_dep_vec_grid, expand_gtr, grid.bl, relative_grid.cols, y.2]
  },
  have heq₁ : m₁ = m₁',
    {
      rcases m₁ with ⟨⟨h, ⟨d, hd⟩⟩⟩,
      simp [m₁', matrix_of_f],
      apply list.ext_le _ _,
        {
          simp [
            length_generate_eq_size, hd, size, relative_grid.rows,
            relative_grid.cols
          ]
        },
        {
          intros k h₁ h₂, rw nth_generate,
          simp [
            abs_data, relative_grid.cols, relpoint_of_gpoint, relative_grid.contents,
            grid.bl, vec_grid_of_dep_vec_grid
          ],
          delta vec_grid_of_dep_vec_grid, simp [vector.nth],
          congr, norm_cast, unfold_coes, simp [fin.val],
          rw [
            ← int.coe_nat_eq_coe_nat_iff, int.coe_nat_add,
            int.coe_nat_mul, int.coe_nat_div
          ],
          have : int.of_nat k % int.of_nat n ≥ (0 : ℤ), by norm_cast,
          rw int.nat_abs_of_nonneg this, repeat { rw int.of_nat_eq_coe },
          symmetry, rw [mul_comm, int.mod_add_div]
        }
    },
  rw heq₁ at h,
  have heq₂ : m₂ = m₂',
    {
      rcases m₂ with ⟨⟨h, ⟨d, hd⟩⟩⟩,
      simp [m₂', matrix_of_f],
      apply list.ext_le _ _,
        {
          simp [
            length_generate_eq_size, hd, size, relative_grid.rows,
            relative_grid.cols
          ]
        },
        {
          intros k h₁ h₂, rw nth_generate,
          simp [
            abs_data, relative_grid.cols, relpoint_of_gpoint, relative_grid.contents,
            grid.bl, vec_grid_of_dep_vec_grid
          ],
          delta vec_grid_of_dep_vec_grid, simp [vector.nth],
          congr, norm_cast, unfold_coes, simp [fin.val],
          rw [
            ← int.coe_nat_eq_coe_nat_iff, int.coe_nat_add,
            int.coe_nat_mul, int.coe_nat_div
          ],
          have : int.of_nat k % int.of_nat n ≥ (0 : ℤ), by norm_cast,
          rw int.nat_abs_of_nonneg this, repeat { rw int.of_nat_eq_coe },
          symmetry, rw [mul_comm, int.mod_add_div]
        }
    },
  rw heq₂ at h,
  simp [m₁', m₂'] at h,
  delta vec_grid_of_dep_vec_grid at h,
  rcases m₁ with ⟨⟨h₁, ⟨d₁, hd₁⟩⟩⟩, rcases m₂ with ⟨⟨h₂, ⟨d₂, hd₂⟩⟩⟩, simp at *,
  simp [
    abs_data, relpoint_of_gpoint, relative_grid.contents, grid.bl, expand_gtr,
    vector.nth
  ] at h,
  apply list.ext_le _ _,
    {simp [hd₁, hd₂]},
    {
      intros k hk₁ hk₂,
      have eq₁ : k % n < n, by
        apply nat.mod_lt; exact (gt_and_gt_of_mul_gt h₁).2,
      have eq₂ : k / n < m, {
        rw hd₁ at hk₁, rw nat.div_lt_iff_lt_mul, exact hk₁,
        exact (gt_and_gt_of_mul_gt h₁).2
      },
      specialize h ⟨k / n, eq₂⟩ ⟨k % n, eq₁⟩,
      revert h, rw ← option.some_inj, intros h,
      repeat { rw ← list.nth_le_nth at h }, simp at h,
      rw ← option.some_inj, repeat { rw ← list.nth_le_nth },
      have : k % n + k / n * n = k,
        {
          rw [
            mul_comm,
            ← int.coe_nat_eq_coe_nat_iff, int.coe_nat_add, int.coe_nat_mod,
            int.coe_nat_mul, int.coe_nat_div
          ],
          exact int.mod_add_div _ _
        },
      simp [this] at h, exact h
    }
end

end ext

section operations

variables {m n o p : ℕ} {α β γ δ : Type}

open relative_grid grid

lemma matrix_nonempty {m₁ : matrix m n α} : m * n > 0 := m₁.1.1

def matrix_string [has_to_string α] (m : matrix m n α) :=
  grid_str (vec_grid_of_dep_vec_grid m.g)

instance matrix_repr [has_to_string α] : has_repr (matrix m n α) :=
  ⟨matrix_string⟩

instance matrix_to_string [has_to_string α] : has_to_string (matrix m n α) :=
  ⟨matrix_string⟩

instance matrix_functor : functor (matrix m n) := {
  map := λα β f m, ⟨f <$> m.g⟩
}

instance matrix_functor_law : is_lawful_functor (matrix m n) := {
  id_map := λα ⟨⟨r, c, h⟩⟩, by simp [(<$>), vector.map_id],
  comp_map := λα β γ f h ⟨⟨r, c, h⟩⟩, by simp [(<$>)]
}

def m₁ : matrix 5 2 ℕ :=
  matrix.mk
    (@dep_vec_grid.mk _ 5 2 dec_trivial
                       ⟨[1, 3, 4, 5, 7, 8, 9, 10, 11, 12], dec_trivial⟩)

def m₂ : matrix 2 3 ℕ :=
  matrix.mk
    (@dep_vec_grid.mk _ 2 3 dec_trivial
                      ⟨[2, 2, 2, 2, 2, 2], dec_trivial⟩)

instance [has_add α] : has_add (matrix m n α) := {
  add := λm₁ m₂, ⟨
    @dep_vec_grid.mk _ m n m₁.1.1 (vector.zip_with (+) m₁.1.2 m₂.1.2)
  ⟩
}

def transpose {α} {m n} (m₁ : matrix m n α) : matrix n m α :=
  matrix_of_f (by rw mul_comm; exact m₁.1.1) $ λi j, matrix_at m₁ j i

theorem transpose_transpose_eq_self (A : matrix m n α) :
  transpose (transpose A) = A := by ext; simp [transpose]

end operations

end matrix


/-
Copyright (c) 2020 Kenji Nakagawa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenji Nakagawa, Anne Baanen, Filippo A. E. Nuccio

! This file was ported from Lean 3 source module ring_theory.dedekind_domain.integral_closure
! leanprover-community/mathlib commit 641b6a82006416ec431b2987b354af9311fed4f2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.FreeModule.Pid
import Mathbin.RingTheory.DedekindDomain.Basic
import Mathbin.RingTheory.Localization.Module
import Mathbin.RingTheory.Trace

/-!
# Integral closure of Dedekind domains

This file shows the integral closure of a Dedekind domain (in particular, the ring of integers
of a number field) is a Dedekind domain.

## Implementation notes

The definitions that involve a field of fractions choose a canonical field of fractions,
but are independent of that choice. The `..._iff` lemmas express this independence.

Often, definitions assume that Dedekind domains are not fields. We found it more practical
to add a `(h : ¬ is_field A)` assumption whenever this is explicitly needed.

## References

* [D. Marcus, *Number Fields*][marcus1977number]
* [J.W.S. Cassels, A. Frölich, *Algebraic Number Theory*][cassels1967algebraic]
* [J. Neukirch, *Algebraic Number Theory*][Neukirch1992]

## Tags

dedekind domain, dedekind ring
-/

variable (R A K : Type _) [CommRing R] [CommRing A] [Field K]

open nonZeroDivisors Polynomial

variable [IsDomain A]

section IsIntegralClosure

/-! ### `is_integral_closure` section

We show that an integral closure of a Dedekind domain in a finite separable
field extension is again a Dedekind domain. This implies the ring of integers
of a number field is a Dedekind domain. -/

open Algebra

open BigOperators

variable (A K) [Algebra A K] [IsFractionRing A K]

variable (L : Type _) [Field L] (C : Type _) [CommRing C]

variable [Algebra K L] [Algebra A L] [IsScalarTower A K L]

variable [Algebra C L] [IsIntegralClosure C A L] [Algebra A C] [IsScalarTower A C L]

/- If `L` is a separable extension of `K = Frac(A)` and `L` has no zero smul divisors by `A`,
then `L` is the localization of the integral closure `C` of `A` in `L` at `A⁰`. -/
theorem IsIntegralClosure.isLocalization [IsSeparable K L] [NoZeroSMulDivisors A L] :
    IsLocalization (Algebra.algebraMapSubmonoid C A⁰) L :=
  by
  haveI : IsDomain C :=
    (IsIntegralClosure.equiv A C L (integralClosure A L)).toRingEquiv.IsDomain (integralClosure A L)
  haveI : NoZeroSMulDivisors A C := IsIntegralClosure.noZeroSMulDivisors A L
  refine' ⟨_, fun z => _, fun x y => ⟨fun h => ⟨1, _⟩, _⟩⟩
  · rintro ⟨_, x, hx, rfl⟩
    rw [isUnit_iff_ne_zero, map_ne_zero_iff _ (IsIntegralClosure.algebraMap_injective C A L),
      Subtype.coe_mk, map_ne_zero_iff _ (NoZeroSMulDivisors.algebraMap_injective A C)]
    exact mem_non_zero_divisors_iff_ne_zero.mp hx
  · obtain ⟨m, hm⟩ :=
      IsIntegral.exists_multiple_integral_of_isLocalization A⁰ z (IsSeparable.isIntegral K z)
    obtain ⟨x, hx⟩ : ∃ x, algebraMap C L x = m • z := is_integral_closure.is_integral_iff.mp hm
    refine' ⟨⟨x, algebraMap A C m, m, SetLike.coe_mem m, rfl⟩, _⟩
    rw [Subtype.coe_mk, ← IsScalarTower.algebraMap_apply, hx, mul_comm, Submonoid.smul_def,
      smul_def]
  · simp only [IsIntegralClosure.algebraMap_injective C A L h]
  · rintro ⟨⟨_, m, hm, rfl⟩, h⟩
    refine' congr_arg (algebraMap C L) ((mul_right_inj' _).mp h)
    rw [Subtype.coe_mk, map_ne_zero_iff _ (NoZeroSMulDivisors.algebraMap_injective A C)]
    exact mem_non_zero_divisors_iff_ne_zero.mp hm
#align is_integral_closure.is_localization IsIntegralClosure.isLocalization

variable [FiniteDimensional K L]

variable {A K L}

theorem IsIntegralClosure.range_le_span_dualBasis [IsSeparable K L] {ι : Type _} [Fintype ι]
    [DecidableEq ι] (b : Basis ι K L) (hb_int : ∀ i, IsIntegral A (b i)) [IsIntegrallyClosed A] :
    ((Algebra.linearMap C L).restrictScalars A).range ≤
      Submodule.span A (Set.range <| (traceForm K L).dualBasis (traceFormNondegenerate K L) b) :=
  by
  let db := (trace_form K L).dualBasis (traceFormNondegenerate K L) b
  rintro _ ⟨x, rfl⟩
  simp only [LinearMap.coe_restrictScalars, Algebra.linearMap_apply]
  have hx : IsIntegral A (algebraMap C L x) := (IsIntegralClosure.isIntegral A L x).algebraMap
  rsuffices ⟨c, x_eq⟩ : ∃ c : ι → A, algebraMap C L x = ∑ i, c i • db i
  · rw [x_eq]
    refine' Submodule.sum_mem _ fun i _ => Submodule.smul_mem _ _ (Submodule.subset_span _)
    rw [Set.mem_range]
    exact ⟨i, rfl⟩
  suffices ∃ c : ι → K, (∀ i, IsIntegral A (c i)) ∧ algebraMap C L x = ∑ i, c i • db i
    by
    obtain ⟨c, hc, hx⟩ := this
    have hc' : ∀ i, IsLocalization.IsInteger A (c i) := fun i =>
      is_integrally_closed.is_integral_iff.mp (hc i)
    use fun i => Classical.choose (hc' i)
    refine' hx.trans (Finset.sum_congr rfl fun i _ => _)
    conv_lhs => rw [← Classical.choose_spec (hc' i)]
    rw [← IsScalarTower.algebraMap_smul K (Classical.choose (hc' i)) (db i)]
  refine' ⟨fun i => db.repr (algebraMap C L x) i, fun i => _, (db.sum_repr _).symm⟩
  rw [BilinForm.dualBasis_repr_apply]
  exact is_integral_trace (isIntegral_mul hx (hb_int i))
#align is_integral_closure.range_le_span_dual_basis IsIntegralClosure.range_le_span_dualBasis

theorem integralClosure_le_span_dualBasis [IsSeparable K L] {ι : Type _} [Fintype ι] [DecidableEq ι]
    (b : Basis ι K L) (hb_int : ∀ i, IsIntegral A (b i)) [IsIntegrallyClosed A] :
    (integralClosure A L).toSubmodule ≤
      Submodule.span A (Set.range <| (traceForm K L).dualBasis (traceFormNondegenerate K L) b) :=
  by
  refine' le_trans _ (IsIntegralClosure.range_le_span_dualBasis (integralClosure A L) b hb_int)
  intro x hx
  exact ⟨⟨x, hx⟩, rfl⟩
#align integral_closure_le_span_dual_basis integralClosure_le_span_dualBasis

variable (A) (K)

include K

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (y «expr ≠ » (0 : A)) -/
/-- Send a set of `x`'es in a finite extension `L` of the fraction field of `R`
to `(y : R) • x ∈ integral_closure R L`. -/
theorem exists_integral_multiples (s : Finset L) :
    ∃ (y : _)(_ : y ≠ (0 : A)), ∀ x ∈ s, IsIntegral A (y • x) :=
  by
  haveI := Classical.decEq L
  refine' s.induction _ _
  · use 1, one_ne_zero
    rintro x ⟨⟩
  · rintro x s hx ⟨y, hy, hs⟩
    obtain ⟨x', y', hy', hx'⟩ :=
      exists_integral_multiple
        ((IsFractionRing.isAlgebraic_iff A K L).mpr (is_algebraic_of_finite _ _ x))
        ((injective_iff_map_eq_zero (algebraMap A L)).mp _)
    refine' ⟨y * y', mul_ne_zero hy hy', fun x'' hx'' => _⟩
    rcases finset.mem_insert.mp hx'' with (rfl | hx'')
    · rw [mul_smul, Algebra.smul_def, Algebra.smul_def, mul_comm _ x'', hx']
      exact isIntegral_mul isIntegral_algebraMap x'.2
    · rw [mul_comm, mul_smul, Algebra.smul_def]
      exact isIntegral_mul isIntegral_algebraMap (hs _ hx'')
    · rw [IsScalarTower.algebraMap_eq A K L]
      apply (algebraMap K L).Injective.comp
      exact IsFractionRing.injective _ _
#align exists_integral_multiples exists_integral_multiples

variable (L)

/-- If `L` is a finite extension of `K = Frac(A)`,
then `L` has a basis over `A` consisting of integral elements. -/
theorem FiniteDimensional.exists_is_basis_integral :
    ∃ (s : Finset L)(b : Basis s K L), ∀ x, IsIntegral A (b x) :=
  by
  letI := Classical.decEq L
  letI : IsNoetherian K L := IsNoetherian.iff_fg.2 inferInstance
  let s' := IsNoetherian.finsetBasisIndex K L
  let bs' := IsNoetherian.finsetBasis K L
  obtain ⟨y, hy, his'⟩ := exists_integral_multiples A K (finset.univ.image bs')
  have hy' : algebraMap A L y ≠ 0 :=
    by
    refine' mt ((injective_iff_map_eq_zero (algebraMap A L)).mp _ _) hy
    rw [IsScalarTower.algebraMap_eq A K L]
    exact (algebraMap K L).Injective.comp (IsFractionRing.injective A K)
  refine'
    ⟨s',
      bs'.map
        {
          LinearMap.Algebra.lmul _ _
            (algebraMap A L y) with
          toFun := fun x => algebraMap A L y * x
          invFun := fun x => (algebraMap A L y)⁻¹ * x
          left_inv := _
          right_inv := _ },
      _⟩
  · intro x
    simp only [inv_mul_cancel_left₀ hy']
  · intro x
    simp only [mul_inv_cancel_left₀ hy']
  · rintro ⟨x', hx'⟩
    simp only [Algebra.smul_def, Finset.mem_image, exists_prop, Finset.mem_univ, true_and_iff] at
      his'
    simp only [Basis.map_apply, LinearEquiv.coe_mk]
    exact his' _ ⟨_, rfl⟩
#align finite_dimensional.exists_is_basis_integral FiniteDimensional.exists_is_basis_integral

variable (A K L) [IsSeparable K L]

include L

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is
integrally closed and Noetherian, the integral closure `C` of `A` in `L` is
Noetherian over `A`. -/
theorem IsIntegralClosure.isNoetherian [IsIntegrallyClosed A] [IsNoetherianRing A] :
    IsNoetherian A C := by
  haveI := Classical.decEq L
  obtain ⟨s, b, hb_int⟩ := FiniteDimensional.exists_is_basis_integral A K L
  let b' := (trace_form K L).dualBasis (traceFormNondegenerate K L) b
  letI := isNoetherian_span_of_finite A (Set.finite_range b')
  let f : C →ₗ[A] Submodule.span A (Set.range b') :=
    (Submodule.ofLe (IsIntegralClosure.range_le_span_dualBasis C b hb_int)).comp
      ((Algebra.linearMap C L).restrictScalars A).range_restrict
  refine' isNoetherian_of_ker_bot f _
  rw [LinearMap.ker_comp, Submodule.ker_ofLe, Submodule.comap_bot, LinearMap.ker_codRestrict]
  exact LinearMap.ker_eq_bot_of_injective (IsIntegralClosure.algebraMap_injective C A L)
#align is_integral_closure.is_noetherian IsIntegralClosure.isNoetherian

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is
integrally closed and Noetherian, the integral closure `C` of `A` in `L` is
Noetherian. -/
theorem IsIntegralClosure.isNoetherianRing [IsIntegrallyClosed A] [IsNoetherianRing A] :
    IsNoetherianRing C :=
  isNoetherianRing_iff.mpr <| isNoetherian_of_tower A (IsIntegralClosure.isNoetherian A K L C)
#align is_integral_closure.is_noetherian_ring IsIntegralClosure.isNoetherianRing

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is a principal ring
and `L` has no zero smul divisors by `A`, the integral closure `C` of `A` in `L` is
a free `A`-module. -/
theorem IsIntegralClosure.module_free [NoZeroSMulDivisors A L] [IsPrincipalIdealRing A] :
    Module.Free A C :=
  by
  haveI : NoZeroSMulDivisors A C := IsIntegralClosure.noZeroSMulDivisors A L
  haveI : IsNoetherian A C := IsIntegralClosure.isNoetherian A K L _
  exact Module.free_of_finite_type_torsion_free'
#align is_integral_closure.module_free IsIntegralClosure.module_free

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is a principal ring
and `L` has no zero smul divisors by `A`, the `A`-rank of the integral closure `C` of `A` in `L`
is equal to the `K`-rank of `L`. -/
theorem IsIntegralClosure.rank [IsPrincipalIdealRing A] [NoZeroSMulDivisors A L] :
    FiniteDimensional.finrank A C = FiniteDimensional.finrank K L :=
  by
  haveI : Module.Free A C := IsIntegralClosure.module_free A K L C
  haveI : IsNoetherian A C := IsIntegralClosure.isNoetherian A K L C
  haveI : IsLocalization (Algebra.algebraMapSubmonoid C A⁰) L :=
    IsIntegralClosure.isLocalization A K L C
  let b := Basis.localizationLocalization K A⁰ L (Module.Free.chooseBasis A C)
  rw [Module.Free.finrank_eq_card_chooseBasisIndex, FiniteDimensional.finrank_eq_card_basis b]
#align is_integral_closure.rank IsIntegralClosure.rank

variable {A K}

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is
integrally closed and Noetherian, the integral closure of `A` in `L` is
Noetherian. -/
theorem integralClosure.isNoetherianRing [IsIntegrallyClosed A] [IsNoetherianRing A] :
    IsNoetherianRing (integralClosure A L) :=
  IsIntegralClosure.isNoetherianRing A K L (integralClosure A L)
#align integral_closure.is_noetherian_ring integralClosure.isNoetherianRing

variable (A K) [IsDomain C]

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is a Dedekind domain,
the integral closure `C` of `A` in `L` is a Dedekind domain.

Can't be an instance since `A`, `K` or `L` can't be inferred. See also the instance
`integral_closure.is_dedekind_domain_fraction_ring` where `K := fraction_ring A`
and `C := integral_closure A L`.
-/
theorem IsIntegralClosure.isDedekindDomain [h : IsDedekindDomain A] : IsDedekindDomain C :=
  haveI : IsFractionRing C L := IsIntegralClosure.isFractionRing_of_finite_extension A K L C
  ⟨IsIntegralClosure.isNoetherianRing A K L C, h.dimension_le_one.is_integral_closure _ L _,
    (isIntegrallyClosed_iff L).mpr fun x hx =>
      ⟨IsIntegralClosure.mk' C x (isIntegral_trans (IsIntegralClosure.isIntegral_algebra A L) _ hx),
        IsIntegralClosure.algebraMap_mk' _ _ _⟩⟩
#align is_integral_closure.is_dedekind_domain IsIntegralClosure.isDedekindDomain

/- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is a Dedekind domain,
the integral closure of `A` in `L` is a Dedekind domain.

Can't be an instance since `K` can't be inferred. See also the instance
`integral_closure.is_dedekind_domain_fraction_ring` where `K := fraction_ring A`.
-/
theorem integralClosure.isDedekindDomain [h : IsDedekindDomain A] :
    IsDedekindDomain (integralClosure A L) :=
  IsIntegralClosure.isDedekindDomain A K L (integralClosure A L)
#align integral_closure.is_dedekind_domain integralClosure.isDedekindDomain

omit K

variable [Algebra (FractionRing A) L] [IsScalarTower A (FractionRing A) L]

variable [FiniteDimensional (FractionRing A) L] [IsSeparable (FractionRing A) L]

/- If `L` is a finite separable extension of `Frac(A)`, where `A` is a Dedekind domain,
the integral closure of `A` in `L` is a Dedekind domain.

See also the lemma `integral_closure.is_dedekind_domain` where you can choose
the field of fractions yourself.
-/
instance integralClosure.isDedekindDomainFractionRing [IsDedekindDomain A] :
    IsDedekindDomain (integralClosure A L) :=
  integralClosure.isDedekindDomain A (FractionRing A) L
#align integral_closure.is_dedekind_domain_fraction_ring integralClosure.isDedekindDomainFractionRing

end IsIntegralClosure


example (P Q : Prop) (p : P) (q : Q) : P ∧ Q :=
begin
split,
exact p,
exact q,
end


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import Init.Data.UInt

@[inline, reducible] def isValidChar (n : UInt32) : Prop :=
  n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)

namespace Char

protected def lt (a b : Char) : Prop := a.val < b.val
protected def le (a b : Char) : Prop := a.val ≤ b.val

instance : LT Char := ⟨Char.lt⟩
instance : LE Char := ⟨Char.le⟩

instance (a b : Char) :  Decidable (a < b) :=
  UInt32.decLt _ _

instance (a b : Char) : Decidable (a ≤ b) :=
  UInt32.decLe _ _

abbrev isValidCharNat (n : Nat) : Prop :=
  n < 0xd800 ∨ (0xdfff < n ∧ n < 0x110000)

theorem isValidUInt32 (n : Nat) (h : isValidCharNat n) : n < UInt32.size := by
  match h with
  | Or.inl h        =>
    apply Nat.ltTrans h
    decide
  | Or.inr ⟨h₁, h₂⟩ =>
    apply Nat.ltTrans h₂
    decide

theorem isValidCharOfValidNat (n : Nat) (h : isValidCharNat n) : isValidChar (UInt32.ofNat' n (isValidUInt32 n h)) :=
  match h with
  | Or.inl h        => Or.inl h
  | Or.inr ⟨h₁, h₂⟩ => Or.inr ⟨h₁, h₂⟩

theorem isValidChar0 : isValidChar 0 :=
  Or.inl (by decide)

@[inline] def toNat (c : Char) : Nat :=
  c.val.toNat

instance : Inhabited Char where
  default := 'A'

def isWhitespace (c : Char) : Bool :=
  c = ' ' || c = '\t' || c = '\r' || c = '\n'

def isUpper (c : Char) : Bool :=
  c.val ≥ 65 && c.val ≤ 90

def isLower (c : Char) : Bool :=
  c.val ≥ 97 && c.val ≤ 122

def isAlpha (c : Char) : Bool :=
  c.isUpper || c.isLower

def isDigit (c : Char) : Bool :=
  c.val ≥ 48 && c.val ≤ 57

def isAlphanum (c : Char) : Bool :=
  c.isAlpha || c.isDigit

def toLower (c : Char) : Char :=
  let n := toNat c;
  if n >= 65 ∧ n <= 90 then ofNat (n + 32) else c

def toUpper (c : Char) : Char :=
  let n := toNat c;
  if n >= 97 ∧ n <= 122 then ofNat (n - 32) else c

end Char


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.finite_limits
import Mathlib.category_theory.limits.shapes.binary_products
import Mathlib.category_theory.limits.shapes.terminal
import Mathlib.PostPort

universes v u

namespace Mathlib

namespace category_theory.limits

/--
A category has finite products if there is a chosen limit for every diagram
with shape `discrete J`, where we have `[decidable_eq J]` and `[fintype J]`.
-/
-- We can't simply make this an abbreviation, as we do with other `has_Xs` limits typeclasses,

-- because of https://github.com/leanprover-community/lean/issues/429

def has_finite_products (C : Type u) [category C] :=
  ∀ (J : Type v) [_inst_2 : DecidableEq J] [_inst_3 : fintype J], has_limits_of_shape (discrete J) C

protected instance has_limits_of_shape_discrete (C : Type u) [category C] (J : Type v) [fintype J]
    [has_finite_products C] : has_limits_of_shape (discrete J) C :=
  _inst_3 J

/-- If `C` has finite limits then it has finite products. -/
theorem has_finite_products_of_has_finite_limits (C : Type u) [category C] [has_finite_limits C] :
    has_finite_products C :=
  fun (J : Type v) (𝒥₁ : DecidableEq J) (𝒥₂ : fintype J) =>
    limits.has_limits_of_shape_of_has_finite_limits C (discrete J)

/--
If a category has all products then in particular it has finite products.
-/
theorem has_finite_products_of_has_products (C : Type u) [category C] [has_products C] :
    has_finite_products C :=
  id fun (J : Type v) => _inst_2 J

/--
A category has finite coproducts if there is a chosen colimit for every diagram
with shape `discrete J`, where we have `[decidable_eq J]` and `[fintype J]`.
-/
def has_finite_coproducts (C : Type u) [category C] :=
  ∀ (J : Type v) [_inst_2 : DecidableEq J] [_inst_3 : fintype J],
    has_colimits_of_shape (discrete J) C

protected instance has_colimits_of_shape_discrete (C : Type u) [category C] (J : Type v) [fintype J]
    [has_finite_coproducts C] : has_colimits_of_shape (discrete J) C :=
  _inst_3 J

/-- If `C` has finite colimits then it has finite coproducts. -/
theorem has_finite_coproducts_of_has_finite_colimits (C : Type u) [category C]
    [has_finite_colimits C] : has_finite_coproducts C :=
  fun (J : Type v) (𝒥₁ : DecidableEq J) (𝒥₂ : fintype J) =>
    limits.has_colimits_of_shape_of_has_finite_colimits C (discrete J)

/--
If a category has all coproducts then in particular it has finite coproducts.
-/
theorem has_finite_coproducts_of_has_coproducts (C : Type u) [category C] [has_coproducts C] :
    has_finite_coproducts C :=
  id fun (J : Type v) => _inst_2 J

end Mathlib


import algebra
import data.real.basic
import data.vector
import tactic.explode
import tactic.find
import tactic.induction
import tactic.linarith
import tactic.rcases
import tactic.rewrite
import tactic.ring_exp
import tactic.tidy
import tactic.where

/- # LoVe Library

This files contains a few extensions on top of Lean's core libraries and
`mathlib`. -/

namespace LoVe

/- ## Structured Proofs -/

notation `fix ` binders `, ` r:(scoped f, f) := r

/- ## Logical Connectives -/

attribute [pattern] or.intro_left or.intro_right

meta def tactic.dec_trivial := `[exact dec_trivial]

lemma not_def (a : Prop) :
  ¬ a ↔ a → false :=
by refl

@[simp] lemma not_not_iff (a : Prop) [decidable a] :
  ¬¬ a ↔ a :=
by by_cases a; simp [h]

@[simp] lemma and_imp_distrib (a b c : Prop) :
  (a ∧ b → c) ↔ (a → b → c) :=
iff.intro
  (assume h ha hb, h ⟨ha, hb⟩)
  (assume h ⟨ha, hb⟩, h ha hb)

@[simp] lemma or_imp_distrib {a b c : Prop} :
  a ∨ b → c ↔ (a → c) ∧ (b → c) :=
iff.intro
  (assume h,
   ⟨assume ha, h (or.intro_left _ ha), assume hb, h (or.intro_right _ hb)⟩)
  (assume ⟨ha, hb⟩ h, match h with or.inl h := ha h | or.inr h := hb h end)

@[simp] lemma exists_imp_distrib {α : Sort*} {p : α → Prop} {a : Prop} :
  ((∃x, p x) → a) ↔ (∀x, p x → a) :=
iff.intro
  (assume h hp ha, h ⟨hp, ha⟩)
  (assume h ⟨hp, ha⟩, h hp ha)

lemma and_exists {α : Sort*} {p : α → Prop} {a : Prop} :
  (a ∧ (∃x, p x)) ↔ (∃x, a ∧ p x) :=
iff.intro
  (assume ⟨ha, x, hp⟩, ⟨x, ha, hp⟩)
  (assume ⟨x, ha, hp⟩, ⟨ha, x, hp⟩)

@[simp] lemma exists_false {α : Sort*} :
  (∃x : α, false) ↔ false :=
iff.intro (assume ⟨a, f⟩, f) (assume h, h.elim)

/- ## Natural Numbers -/

attribute [simp] nat.add

/- ## Integers -/

@[simp] lemma int.neg_comp_neg :
  int.neg ∘ int.neg = id :=
begin
  apply funext,
  apply neg_neg
end

/- ## Reflexive Transitive Closure -/

namespace rtc

inductive star {α : Sort*} (r : α → α → Prop) (a : α) : α → Prop
| refl {}    : star a
| tail {b c} : star b → r b c → star c

attribute [refl] star.refl

namespace star

variables {α : Sort*} {r : α → α → Prop} {a b c d : α}

@[trans] lemma trans (hab : star r a b) (hbc : star r b c) :
  star r a c :=
begin
  induction' hbc,
  case refl {
    assumption },
  case tail : c d hbc hcd hac {
    exact (tail (hac hab)) hcd }
end

lemma single (hab : r a b) :
  star r a b :=
refl.tail hab

lemma head (hab : r a b) (hbc : star r b c) :
  star r a c :=
begin
  induction' hbc,
  case refl {
    exact (tail refl) hab },
  case tail : c d hbc hcd hac {
    exact (tail (hac hab)) hcd }
end

lemma head_induction_on {α : Sort*} {r : α → α → Prop} {b : α}
  {P : ∀a : α, star r a b → Prop} {a : α} (h : star r a b)
  (refl : P b refl)
  (head : ∀{a c} (h' : r a c) (h : star r c b), P c h → P a (h.head h')) :
  P a h :=
begin
  induction' h,
  case refl {
    exact refl },
  case tail : b c hab hbc ih {
    apply ih,
    show P b _, from
      head hbc _ refl,
    show ∀a a', r a a' → star r a' b → P a' _ → P a _, from
      assume a a' hab hbc, head hab _ }
end

lemma trans_induction_on {α : Sort*} {r : α → α → Prop}
    {p : ∀{a b : α}, star r a b → Prop} {a b : α} (h : star r a b)
    (ih₁ : ∀a, @p a a refl) (ih₂ : ∀{a b} (h : r a b), p (single h))
    (ih₃ : ∀{a b c} (h₁ : star r a b) (h₂ : star r b c), p h₁ →
       p h₂ → p (h₁.trans h₂)) :
  p h :=
begin
  induction' h,
  case refl {
    exact ih₁ a },
  case tail : b c hab hbc ih {
    exact ih₃ hab (single hbc) (ih ih₁ @ih₂ @ih₃) (ih₂ hbc) }
end

lemma lift {β : Sort*} {s : β → β → Prop} (f : α → β)
  (h : ∀a b, r a b → s (f a) (f b)) (hab : star r a b) :
  star s (f a) (f b) :=
hab.trans_induction_on
  (assume a, refl)
  (assume a b, single ∘ h _ _)
  (assume a b c _ _, trans)

lemma mono {p : α → α → Prop} :
  (∀a b, r a b → p a b) → star r a b → star p a b :=
lift id

lemma star_star_eq :
  star (star r) = star r :=
funext
  (assume a,
   funext
     (assume b,
      propext (iff.intro
        (assume h,
         begin
           induction' h,
           { refl },
           { transitivity;
               assumption }
         end)
        (star.mono (assume a b,
           single)))))

end star

end rtc

export rtc

/- ## States -/

def state :=
string → ℕ

def state.update (name : string) (val : ℕ) (s : state) : state :=
λname', if name' = name then val else s name'

notation s `{` name ` ↦ ` val `}` := state.update name val s

instance : has_emptyc state :=
{ emptyc := λ_, 0 }

@[simp] lemma update_apply (name : string) (val : ℕ) (s : state) :
  s{name ↦ val} name = val :=
if_pos rfl

@[simp] lemma update_apply_ne (name name' : string) (val : ℕ) (s : state)
    (h : name' ≠ name . tactic.dec_trivial) :
  s{name ↦ val} name' = s name' :=
if_neg h

@[simp] lemma update_override (name : string) (val₁ val₂ : ℕ) (s : state) :
  s{name ↦ val₂}{name ↦ val₁} = s{name ↦ val₁} :=
begin
  apply funext,
  intro name',
  by_cases name' = name;
    simp [h]
end

@[simp] lemma update_swap (name₁ name₂ : string) (val₁ val₂ : ℕ) (s : state)
    (h : name₁ ≠ name₂ . tactic.dec_trivial) :
  s{name₂ ↦ val₂}{name₁ ↦ val₁} = s{name₁ ↦ val₁}{name₂ ↦ val₂} :=
begin
  apply funext,
  intro name',
  by_cases name' = name₁;
    by_cases name' = name₂;
    simp * at *
end

@[simp] lemma update_id (name : string) (s : state) :
  s{name ↦ s name} = s :=
begin
  apply funext,
  intro name',
  by_cases name' = name;
    simp * at *
end

example (s : state) :
  s{"a" ↦ 0}{"a" ↦ 2} = s{"a" ↦ 2} :=
by simp

example (s : state) :
  s{"a" ↦ 0}{"b" ↦ 2} = s{"b" ↦ 2}{"a" ↦ 0} :=
by simp

example (s : state) :
  s{"a" ↦ s "a"}{"b" ↦ 0} = s{"b" ↦ 0} :=
by simp

/- ## Relations -/

def Id {α : Type} : set (α × α) :=
{ab | prod.snd ab = prod.fst ab}

@[simp] lemma mem_Id {α : Type} (a b : α) :
  (a, b) ∈ @Id α ↔ b = a :=
by refl

def comp {α : Type} (r₁ r₂ : set (α × α)) : set (α × α) :=
{ac | ∃b, (prod.fst ac, b) ∈ r₁ ∧ (b, prod.snd ac) ∈ r₂}

infixl ` ◯ ` : 90 := comp

@[simp] lemma mem_comp {α : Type} (r₁ r₂ : set (α × α))
    (a b : α) :
  (a, b) ∈ r₁ ◯ r₂ ↔ (∃c, (a, c) ∈ r₁ ∧ (c, b) ∈ r₂) :=
by refl

def restrict {α : Type} (r : set (α × α)) (p : α → Prop) :
  set (α × α) :=
{ab | p (prod.fst ab) ∧ ab ∈ r}

infixl ` ⇃ ` : 90 := restrict

@[simp] lemma mem_restrict {α : Type} (r : set (α × α))
    (p : α → Prop) (a b : α) :
  (a, b) ∈ r ⇃ p ↔ p a ∧ (a, b) ∈ r :=
by refl

end LoVe


/-
Copyright (c) 2020 Eric Wieser. All rights reserved.
Released under MIT license as described in the file LICENSE.
Authors: Eric Wieser
-/
import algebra.algebra.basic

/-! # Submonoids including the central ring -/

namespace algebra

set_option old_structure_cmd true

/-- A `center_submonoid` is a submonoid that includes the central ring of the algebra -/
structure center_submonoid (R : Type*) (A : Type*) [comm_semiring R] [semiring A] [algebra R A]
  extends submonoid A, sub_mul_action R A.

namespace center_submonoid

variables {R : Type*} {A : Type*}

section semiring

variables [comm_semiring R] [semiring A] [algebra R A] (S : center_submonoid R A)

instance : set_like (center_submonoid R A) A :=
{ coe := center_submonoid.carrier,
  coe_injective' := λ x y h, by { cases x, cases y, congr' } }

instance : submonoid_class (center_submonoid R A) A :=
{ one_mem := λ S, S.to_submonoid.one_mem,
  mul_mem := λ S _ _, S.to_submonoid.mul_mem, }

instance : nonempty S.to_sub_mul_action := ⟨⟨1, S.to_submonoid.one_mem⟩⟩

instance : zero_mem_class (center_submonoid R A) A :=
{ zero_mem := λ S,  S.to_sub_mul_action.zero_mem ⟨1, S.to_submonoid.one_mem⟩, }

lemma smul_mem (r : R) {a : A} : a ∈ S → r • a ∈ S := S.to_sub_mul_action.smul_mem r
protected lemma mul_mem {a b : A} : a ∈ S → b ∈ S → a * b ∈ S := S.to_submonoid.mul_mem
protected lemma one_mem : (1 : A) ∈ S := S.to_submonoid.one_mem
protected lemma zero_mem : (0 : A) ∈ S := S.to_sub_mul_action.zero_mem ⟨1, S.one_mem⟩

@[simp] lemma algebra_map_mem (r : R) : algebra_map R A r ∈ S :=
by { rw algebra_map_eq_smul_one r, exact S.smul_mem r S.one_mem, }

variables (R)
def closure (s : set A) : center_submonoid R A :=
let c := submonoid.closure (set.range (algebra_map R A) ∪ s) in
{ smul_mem' := λ r a h, begin
    rw algebra.smul_def r a,
    exact c.mul_mem (submonoid.subset_closure $ or.inl $ set.mem_range_self r) h
  end, ..c}

@[simp] lemma subset_closure {s : set A} : s ⊆ closure R s :=
λ x hx, submonoid.subset_closure $ or.inr hx

@[simp] lemma closure_to_submonoid {s : set A} :
  (closure R s).to_submonoid = submonoid.closure (set.range (algebra_map R A) ∪ s) :=
rfl

variables {R}

instance : mul_action R S := S.to_sub_mul_action.mul_action

instance : monoid_with_zero S :=
{ zero_mul := λ v, subtype.eq $ zero_mul ↑v,
  mul_zero := λ v, subtype.eq $ mul_zero ↑v,
  ..S.to_sub_mul_action.has_zero,
  ..S.to_submonoid.to_monoid }

instance [nontrivial A] : nontrivial S :=
nontrivial_of_ne 0 1 (subtype.ne_of_val_ne zero_ne_one)

@[simp, norm_cast] lemma coe_zero : ((0 : S) : A) = 0 := rfl
@[simp, norm_cast] lemma coe_smul (k : R) (v : S) : (↑(k • v) : A) = k • v := rfl

end semiring

section ring

variables [comm_ring R] [ring A] [algebra R A] (S : center_submonoid R A)

@[simp] lemma neg_mem (S : center_submonoid R A) (v : A) : v ∈ S → -v ∈ S := S.to_sub_mul_action.neg_mem

instance : has_neg (S) := S.to_sub_mul_action.has_neg

@[simp, norm_cast] lemma coe_neg (v : S) : (↑-v : A) = -v := rfl

end ring

end center_submonoid

end algebra


/-
Copyright (c) 2022 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import measure_theory.integral.integrable_on

/-!
# Locally integrable functions

A function is called *locally integrable* (`measure_theory.locally_integrable`) if it is integrable
on every compact subset of its domain.

This file contains properties of locally integrable functions and of integrability results
on compact sets.

## Main statements

* `continuous.locally_integrable`: A continuous function is locally integrable.

-/

open measure_theory measure_theory.measure set function topological_space
open_locale topological_space interval

variables {X Y E : Type*} [measurable_space X] [topological_space X]
variables [measurable_space Y] [topological_space Y]
variables [normed_group E] {f : X → E} {μ : measure X}

namespace measure_theory

/-- A function `f : X → E` is locally integrable if it is integrable on all compact sets.
  See `measure_theory.locally_integrable_iff` for the justification of this name. -/
def locally_integrable (f : X → E) (μ : measure X . volume_tac) : Prop :=
∀ ⦃K⦄, is_compact K → integrable_on f K μ

lemma integrable.locally_integrable (hf : integrable f μ) : locally_integrable f μ :=
λ K hK, hf.integrable_on

lemma locally_integrable.ae_strongly_measurable [sigma_compact_space X]
  (hf : locally_integrable f μ) :
  ae_strongly_measurable f μ :=
begin
  rw [← @restrict_univ _ _ μ, ← Union_compact_covering, ae_strongly_measurable_Union_iff],
  exact λ i, (hf $ is_compact_compact_covering X i).ae_strongly_measurable
end

section real
variables [opens_measurable_space X] {A K : set X} {g g' : X → ℝ}

lemma integrable_on.mul_continuous_on_of_subset
  (hg : integrable_on g A μ) (hg' : continuous_on g' K)
  (hA : measurable_set A) (hK : is_compact K) (hAK : A ⊆ K) :
  integrable_on (λ x, g x * g' x) A μ :=
begin
  rcases is_compact.exists_bound_of_continuous_on hK hg' with ⟨C, hC⟩,
  rw [integrable_on, ← mem_ℒp_one_iff_integrable] at hg ⊢,
  have : ∀ᵐ x ∂(μ.restrict A), ∥g x * g' x∥ ≤ C * ∥g x∥,
  { filter_upwards [ae_restrict_mem hA] with x hx,
    rw [real.norm_eq_abs, abs_mul, mul_comm, real.norm_eq_abs],
    apply mul_le_mul_of_nonneg_right (hC x (hAK hx)) (abs_nonneg _), },
  exact mem_ℒp.of_le_mul hg (hg.ae_strongly_measurable.ae_measurable.mul
    ((hg'.mono hAK).ae_measurable hA)).ae_strongly_measurable this,
end

lemma integrable_on.mul_continuous_on [t2_space X]
  (hg : integrable_on g K μ) (hg' : continuous_on g' K) (hK : is_compact K) :
  integrable_on (λ x, g x * g' x) K μ :=
hg.mul_continuous_on_of_subset hg' hK.measurable_set hK (subset.refl _)

lemma integrable_on.continuous_on_mul_of_subset
  (hg : continuous_on g K) (hg' : integrable_on g' A μ)
  (hK : is_compact K) (hA : measurable_set A) (hAK : A ⊆ K) :
  integrable_on (λ x, g x * g' x) A μ :=
by simpa [mul_comm] using hg'.mul_continuous_on_of_subset hg hA hK hAK

lemma integrable_on.continuous_on_mul [t2_space X]
  (hg : continuous_on g K) (hg' : integrable_on g' K μ) (hK : is_compact K) :
  integrable_on (λ x, g x * g' x) K μ :=
integrable_on.continuous_on_mul_of_subset hg hg' hK hK.measurable_set subset.rfl

end real

end measure_theory
open measure_theory

/-- If a function is integrable at `𝓝[s] x` for each point `x` of a compact set `s`, then it is
integrable on `s`. -/
lemma is_compact.integrable_on_of_nhds_within {K : set X} (hK : is_compact K)
  (hf : ∀ x ∈ K, integrable_at_filter f (𝓝[K] x) μ) : integrable_on f K μ :=
is_compact.induction_on hK integrable_on_empty (λ s t hst ht, ht.mono_set hst)
  (λ s t hs ht, hs.union ht) hf

section borel

variables [opens_measurable_space X] [metrizable_space X] [is_locally_finite_measure μ]
variables {K : set X} {a b : X}

/-- A function `f` continuous on a compact set `K` is integrable on this set with respect to any
locally finite measure. -/
lemma continuous_on.integrable_on_compact (hK : is_compact K) (hf : continuous_on f K) :
  integrable_on f K μ :=
begin
  letI := metrizable_space_metric X,
  apply hK.integrable_on_of_nhds_within (λ x hx, _),
  exact hf.integrable_at_nhds_within_of_is_separable hK.measurable_set hK.is_separable hx,
end

/-- A continuous function `f` is locally integrable with respect to any locally finite measure. -/
lemma continuous.locally_integrable (hf : continuous f) : locally_integrable f μ :=
λ s hs, hf.continuous_on.integrable_on_compact hs

lemma continuous_on.integrable_on_Icc [preorder X] [compact_Icc_space X]
  (hf : continuous_on f (Icc a b)) : integrable_on f (Icc a b) μ :=
hf.integrable_on_compact is_compact_Icc

lemma continuous.integrable_on_Icc [preorder X] [compact_Icc_space X] (hf : continuous f) :
  integrable_on f (Icc a b) μ :=
hf.locally_integrable is_compact_Icc

lemma continuous.integrable_on_Ioc [preorder X] [compact_Icc_space X] (hf : continuous f) :
  integrable_on f (Ioc a b) μ :=
hf.integrable_on_Icc.mono_set Ioc_subset_Icc_self

lemma continuous_on.integrable_on_interval [linear_order X] [compact_Icc_space X]
  (hf : continuous_on f [a, b]) : integrable_on f [a, b] μ :=
hf.integrable_on_Icc

lemma continuous.integrable_on_interval [linear_order X] [compact_Icc_space X] (hf : continuous f) :
  integrable_on f [a, b] μ :=
hf.integrable_on_Icc

lemma continuous.integrable_on_interval_oc [linear_order X] [compact_Icc_space X]
  (hf : continuous f) : integrable_on f (Ι a b) μ :=
hf.integrable_on_Ioc

/-- A continuous function with compact support is integrable on the whole space. -/
lemma continuous.integrable_of_has_compact_support
  (hf : continuous f) (hcf : has_compact_support f) : integrable f μ :=
(integrable_on_iff_integable_of_support_subset (subset_tsupport f) measurable_set_closure).mp $
  hf.locally_integrable hcf

end borel

section monotone

variables [borel_space X] [metrizable_space X]
  [conditionally_complete_linear_order X] [conditionally_complete_linear_order E]
  [order_topology X] [order_topology E] [second_countable_topology E]
  [is_locally_finite_measure μ] {s : set X}

lemma monotone_on.integrable_on_compact (hs : is_compact s) (hmono : monotone_on f s) :
  integrable_on f s μ :=
begin
  borelize E,
  obtain rfl | h := s.eq_empty_or_nonempty,
  { exact integrable_on_empty },
  have hbelow : bdd_below (f '' s) :=
    ⟨f (Inf s), λ x ⟨y, hy, hyx⟩, hyx ▸ hmono (hs.Inf_mem h) hy (cInf_le hs.bdd_below hy)⟩,
  have habove : bdd_above (f '' s) :=
    ⟨f (Sup s), λ x ⟨y, hy, hyx⟩, hyx ▸ hmono hy (hs.Sup_mem h) (le_cSup hs.bdd_above hy)⟩,
  have : metric.bounded (f '' s) := metric.bounded_of_bdd_above_of_bdd_below habove hbelow,
  rcases bounded_iff_forall_norm_le.mp this with ⟨C, hC⟩,
  refine integrable.mono' (continuous_const.locally_integrable hs)
    (ae_measurable_restrict_of_monotone_on hs.measurable_set hmono).ae_strongly_measurable
    ((ae_restrict_iff' hs.measurable_set).mpr $ ae_of_all _ $
      λ y hy, hC (f y) (mem_image_of_mem f hy)),
end

lemma antitone_on.integrable_on_compact (hs : is_compact s) (hanti : antitone_on f s) :
  integrable_on f s μ :=
@monotone_on.integrable_on_compact X (order_dual E) _ _ _ _ _ _ _ _ _ _ _ _ _ _ hs hanti

lemma monotone.locally_integrable (hmono : monotone f) : locally_integrable f μ :=
λ s hs, monotone_on.integrable_on_compact hs (λ x y _ _ hxy, hmono hxy)

lemma antitone.locally_integrable (hanti : antitone f) : locally_integrable f μ :=
@monotone.locally_integrable X (order_dual E) _ _ _ _ _ _ _ _ _ _ _ _ _ hanti

end monotone