/-
Copyright (c) 2018 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Johannes Hölzl

Continuous linear functions -- functions between normed vector spaces which are bounded and linear.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.normed_space.multilinear
import Mathlib.PostPort

universes u_5 u_6 u_7 l u_1 u_2 u_3 u_4

namespace Mathlib

/-- A function `f` satisfies `is_bounded_linear_map 𝕜 f` if it is linear and satisfies the
inequality `∥ f x ∥ ≤ M * ∥ x ∥` for some positive constant `M`. -/
structure is_bounded_linear_map (𝕜 : Type u_5) [normed_field 𝕜] {E : Type u_6} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_7} [normed_group F] [normed_space 𝕜 F] (f : E → F)
    extends is_linear_map 𝕜 f where
  bound : ∃ (M : ℝ), 0 < M ∧ ∀ (x : E), norm (f x) ≤ M * norm x

theorem is_linear_map.with_bound {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {f : E → F} (hf : is_linear_map 𝕜 f) (M : ℝ) (h : ∀ (x : E), norm (f x) ≤ M * norm x) :
    is_bounded_linear_map 𝕜 f :=
  sorry

/-- A continuous linear map satisfies `is_bounded_linear_map` -/
theorem continuous_linear_map.is_bounded_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] (f : continuous_linear_map 𝕜 E F) : is_bounded_linear_map 𝕜 ⇑f :=
  sorry

namespace is_bounded_linear_map

/-- Construct a linear map from a function `f` satisfying `is_bounded_linear_map 𝕜 f`. -/
def to_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] (f : E → F)
    (h : is_bounded_linear_map 𝕜 f) : linear_map 𝕜 E F :=
  is_linear_map.mk' f sorry

/-- Construct a continuous linear map from is_bounded_linear_map -/
def to_continuous_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : continuous_linear_map 𝕜 E F :=
  continuous_linear_map.mk (linear_map.mk (linear_map.to_fun (to_linear_map f hf)) sorry sorry)

theorem zero {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] :
    is_bounded_linear_map 𝕜 fun (x : E) => 0 :=
  sorry

theorem id {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] : is_bounded_linear_map 𝕜 fun (x : E) => x :=
  sorry

theorem fst {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] :
    is_bounded_linear_map 𝕜 fun (x : E × F) => prod.fst x :=
  sorry

theorem snd {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] :
    is_bounded_linear_map 𝕜 fun (x : E × F) => prod.snd x :=
  sorry

theorem smul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} (c : 𝕜)
    (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 fun (e : E) => c • f e :=
  sorry

theorem neg {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F}
    (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 fun (e : E) => -f e :=
  sorry

theorem add {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} {g : E → F}
    (hf : is_bounded_linear_map 𝕜 f) (hg : is_bounded_linear_map 𝕜 g) :
    is_bounded_linear_map 𝕜 fun (e : E) => f e + g e :=
  sorry

theorem sub {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F} {g : E → F}
    (hf : is_bounded_linear_map 𝕜 f) (hg : is_bounded_linear_map 𝕜 g) :
    is_bounded_linear_map 𝕜 fun (e : E) => f e - g e :=
  sorry

theorem comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4}
    [normed_group G] [normed_space 𝕜 G] {f : E → F} {g : F → G} (hg : is_bounded_linear_map 𝕜 g)
    (hf : is_bounded_linear_map 𝕜 f) : is_bounded_linear_map 𝕜 (g ∘ f) :=
  continuous_linear_map.is_bounded_linear_map
    (continuous_linear_map.comp (to_continuous_linear_map hg) (to_continuous_linear_map hf))

protected theorem tendsto {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {f : E → F} (x : E) (hf : is_bounded_linear_map 𝕜 f) : filter.tendsto f (nhds x) (nhds (f x)) :=
  sorry

theorem continuous {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F}
    (hf : is_bounded_linear_map 𝕜 f) : continuous f :=
  iff.mpr continuous_iff_continuous_at fun (_x : E) => is_bounded_linear_map.tendsto _x hf

theorem lim_zero_bounded_linear_map {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {f : E → F} (hf : is_bounded_linear_map 𝕜 f) : filter.tendsto f (nhds 0) (nhds 0) :=
  linear_map.map_zero (is_linear_map.mk' f (to_is_linear_map hf)) ▸
    iff.mp continuous_iff_continuous_at (continuous hf) 0

theorem is_O_id {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F}
    (h : is_bounded_linear_map 𝕜 f) (l : filter E) : asymptotics.is_O f (fun (x : E) => x) l :=
  sorry

theorem is_O_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {E : Type u_2} {g : F → G}
    (hg : is_bounded_linear_map 𝕜 g) {f : E → F} (l : filter E) :
    asymptotics.is_O (fun (x' : E) => g (f x')) f l :=
  asymptotics.is_O.comp_tendsto (is_O_id hg ⊤) le_top

theorem is_O_sub {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E]
    [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {f : E → F}
    (h : is_bounded_linear_map 𝕜 f) (l : filter E) (x : E) :
    asymptotics.is_O (fun (x' : E) => f (x' - x)) (fun (x' : E) => x' - x) l :=
  is_O_comp h l

end is_bounded_linear_map

/-- Taking the cartesian product of two continuous linear maps is a bounded linear operation. -/
theorem is_bounded_linear_map_prod_iso {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] :
    is_bounded_linear_map 𝕜
        fun (p : continuous_linear_map 𝕜 E F × continuous_linear_map 𝕜 E G) =>
          continuous_linear_map.prod (prod.fst p) (prod.snd p) :=
  sorry

/-- Taking the cartesian product of two continuous multilinear maps is a bounded linear operation. -/
theorem is_bounded_linear_map_prod_multilinear {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G]
    [normed_space 𝕜 G] {ι : Type u_5} [DecidableEq ι] [fintype ι] {E : ι → Type u_2}
    [(i : ι) → normed_group (E i)] [(i : ι) → normed_space 𝕜 (E i)] :
    is_bounded_linear_map 𝕜
        fun (p : continuous_multilinear_map 𝕜 E F × continuous_multilinear_map 𝕜 E G) =>
          continuous_multilinear_map.prod (prod.fst p) (prod.snd p) :=
  sorry

/-- Given a fixed continuous linear map `g`, associating to a continuous multilinear map `f` the
continuous multilinear map `f (g m₁, ..., g mₙ)` is a bounded linear operation. -/
theorem is_bounded_linear_map_continuous_multilinear_map_comp_linear {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    {ι : Type u_5} [DecidableEq ι] [fintype ι] (g : continuous_linear_map 𝕜 G E) :
    is_bounded_linear_map 𝕜
        fun (f : continuous_multilinear_map 𝕜 (fun (i : ι) => E) F) =>
          continuous_multilinear_map.comp_continuous_linear_map f fun (_x : ι) => g :=
  sorry

/-- A map `f : E × F → G` satisfies `is_bounded_bilinear_map 𝕜 f` if it is bilinear and
continuous. -/
structure is_bounded_bilinear_map (𝕜 : Type u_1) [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] (f : E × F → G)
    where
  add_left : ∀ (x₁ x₂ : E) (y : F), f (x₁ + x₂, y) = f (x₁, y) + f (x₂, y)
  smul_left : ∀ (c : 𝕜) (x : E) (y : F), f (c • x, y) = c • f (x, y)
  add_right : ∀ (x : E) (y₁ y₂ : F), f (x, y₁ + y₂) = f (x, y₁) + f (x, y₂)
  smul_right : ∀ (c : 𝕜) (x : E) (y : F), f (x, c • y) = c • f (x, y)
  bound : ∃ (C : ℝ), ∃ (H : C > 0), ∀ (x : E) (y : F), norm (f (x, y)) ≤ C * norm x * norm y

protected theorem is_bounded_bilinear_map.is_O {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) :
    asymptotics.is_O f (fun (p : E × F) => norm (prod.fst p) * norm (prod.snd p)) ⊤ :=
  sorry

theorem is_bounded_bilinear_map.is_O_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G} {α : Type u_5}
    (H : is_bounded_bilinear_map 𝕜 f) {g : α → E} {h : α → F} {l : filter α} :
    asymptotics.is_O (fun (x : α) => f (g x, h x)) (fun (x : α) => norm (g x) * norm (h x)) l :=
  asymptotics.is_O.comp_tendsto (is_bounded_bilinear_map.is_O H) le_top

protected theorem is_bounded_bilinear_map.is_O' {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) : asymptotics.is_O f (fun (p : E × F) => norm p * norm p) ⊤ :=
  asymptotics.is_O.trans (is_bounded_bilinear_map.is_O h)
    (asymptotics.is_O.mul (asymptotics.is_O.norm_norm asymptotics.is_O_fst_prod')
      (asymptotics.is_O.norm_norm asymptotics.is_O_snd_prod'))

theorem is_bounded_bilinear_map.map_sub_left {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) {x : E} {y : E} {z : F} :
    f (x - y, z) = f (x, z) - f (y, z) :=
  sorry

theorem is_bounded_bilinear_map.map_sub_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) {x : E} {y : F} {z : F} :
    f (x, y - z) = f (x, y) - f (x, z) :=
  sorry

theorem is_bounded_bilinear_map.is_bounded_linear_map_left {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (y : F) :
    is_bounded_linear_map 𝕜 fun (x : E) => f (x, y) :=
  sorry

theorem is_bounded_bilinear_map.is_bounded_linear_map_right {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) (x : E) :
    is_bounded_linear_map 𝕜 fun (y : F) => f (x, y) :=
  sorry

theorem is_bounded_bilinear_map_smul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] :
    is_bounded_bilinear_map 𝕜 fun (p : 𝕜 × E) => prod.fst p • prod.snd p :=
  sorry

theorem is_bounded_bilinear_map_smul_algebra {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {𝕜' : Type u_2} [normed_field 𝕜'] [normed_algebra 𝕜 𝕜'] {E : Type u_3} [normed_group E]
    [normed_space 𝕜 E] [normed_space 𝕜' E] [is_scalar_tower 𝕜 𝕜' E] :
    is_bounded_bilinear_map 𝕜 fun (p : 𝕜' × E) => prod.fst p • prod.snd p :=
  sorry

theorem is_bounded_bilinear_map_mul {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] :
    is_bounded_bilinear_map 𝕜 fun (p : 𝕜 × 𝕜) => prod.fst p * prod.snd p :=
  is_bounded_bilinear_map_smul

theorem is_bounded_bilinear_map_comp {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] :
    is_bounded_bilinear_map 𝕜
        fun (p : continuous_linear_map 𝕜 E F × continuous_linear_map 𝕜 F G) =>
          continuous_linear_map.comp (prod.snd p) (prod.fst p) :=
  sorry

theorem continuous_linear_map.is_bounded_linear_map_comp_left {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    (g : continuous_linear_map 𝕜 F G) :
    is_bounded_linear_map 𝕜
        fun (f : continuous_linear_map 𝕜 E F) => continuous_linear_map.comp g f :=
  is_bounded_bilinear_map.is_bounded_linear_map_left is_bounded_bilinear_map_comp g

theorem continuous_linear_map.is_bounded_linear_map_comp_right {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    (f : continuous_linear_map 𝕜 E F) :
    is_bounded_linear_map 𝕜
        fun (g : continuous_linear_map 𝕜 F G) => continuous_linear_map.comp g f :=
  is_bounded_bilinear_map.is_bounded_linear_map_right is_bounded_bilinear_map_comp f

theorem is_bounded_bilinear_map_apply {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F] :
    is_bounded_bilinear_map 𝕜
        fun (p : continuous_linear_map 𝕜 E F × E) => coe_fn (prod.fst p) (prod.snd p) :=
  sorry

/-- The function `continuous_linear_map.smul_right`, associating to a continuous linear map
`f : E → 𝕜` and a scalar `c : F` the tensor product `f ⊗ c` as a continuous linear map from `E` to
`F`, is a bounded bilinear map. -/
theorem is_bounded_bilinear_map_smul_right {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] :
    is_bounded_bilinear_map 𝕜
        fun (p : continuous_linear_map 𝕜 E 𝕜 × F) =>
          continuous_linear_map.smul_right (prod.fst p) (prod.snd p) :=
  sorry

/-- The composition of a continuous linear map with a continuous multilinear map is a bounded
bilinear operation. -/
theorem is_bounded_bilinear_map_comp_multilinear {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {F : Type u_3} [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G]
    [normed_space 𝕜 G] {ι : Type u_2} {E : ι → Type u_5} [DecidableEq ι] [fintype ι]
    [(i : ι) → normed_group (E i)] [(i : ι) → normed_space 𝕜 (E i)] :
    is_bounded_bilinear_map 𝕜
        fun (p : continuous_linear_map 𝕜 F G × continuous_multilinear_map 𝕜 E F) =>
          continuous_linear_map.comp_continuous_multilinear_map (prod.fst p) (prod.snd p) :=
  sorry

/-- Definition of the derivative of a bilinear map `f`, given at a point `p` by
`q ↦ f(p.1, q.2) + f(q.1, p.2)` as in the standard formula for the derivative of a product.
We define this function here a bounded linear map from `E × F` to `G`. The fact that this
is indeed the derivative of `f` is proved in `is_bounded_bilinear_map.has_fderiv_at` in
`fderiv.lean`-/
def is_bounded_bilinear_map.linear_deriv {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) : linear_map 𝕜 (E × F) G :=
  linear_map.mk (fun (q : E × F) => f (prod.fst p, prod.snd q) + f (prod.fst q, prod.snd p)) sorry
    sorry

/-- The derivative of a bounded bilinear map at a point `p : E × F`, as a continuous linear map
from `E × F` to `G`. -/
def is_bounded_bilinear_map.deriv {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) : continuous_linear_map 𝕜 (E × F) G :=
  linear_map.mk_continuous_of_exists_bound (is_bounded_bilinear_map.linear_deriv h p) sorry

@[simp] theorem is_bounded_bilinear_map_deriv_coe {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜]
    {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F]
    [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G] {f : E × F → G}
    (h : is_bounded_bilinear_map 𝕜 f) (p : E × F) (q : E × F) :
    coe_fn (is_bounded_bilinear_map.deriv h p) q =
        f (prod.fst p, prod.snd q) + f (prod.fst q, prod.snd p) :=
  rfl

/-- The function `lmul_left_right : 𝕜' × 𝕜' → (𝕜' →L[𝕜] 𝕜')` is a bounded bilinear map. -/
theorem continuous_linear_map.lmul_left_right_is_bounded_bilinear (𝕜 : Type u_1)
    [nondiscrete_normed_field 𝕜] (𝕜' : Type u_2) [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] :
    is_bounded_bilinear_map 𝕜 (continuous_linear_map.lmul_left_right 𝕜 𝕜') :=
  sorry

/-- Given a bounded bilinear map `f`, the map associating to a point `p` the derivative of `f` at
`p` is itself a bounded linear map. -/
theorem is_bounded_bilinear_map.is_bounded_linear_map_deriv {𝕜 : Type u_1}
    [nondiscrete_normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] {F : Type u_3}
    [normed_group F] [normed_space 𝕜 F] {G : Type u_4} [normed_group G] [normed_space 𝕜 G]
    {f : E × F → G} (h : is_bounded_bilinear_map 𝕜 f) :
    is_bounded_linear_map 𝕜 fun (p : E × F) => is_bounded_bilinear_map.deriv h p :=
  sorry

/-- A linear isometry preserves the norm. -/
theorem linear_map.norm_apply_of_isometry {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    (f : linear_map 𝕜 E F) {x : E} (hf : isometry ⇑f) : norm (coe_fn f x) = norm x :=
  sorry

/-- Construct a continuous linear equiv from a linear map that is also an isometry with full range. -/
def continuous_linear_equiv.of_isometry {𝕜 : Type u_1} [nondiscrete_normed_field 𝕜] {E : Type u_2}
    [normed_group E] [normed_space 𝕜 E] {F : Type u_3} [normed_group F] [normed_space 𝕜 F]
    (f : linear_map 𝕜 E F) (hf : isometry ⇑f) (hfr : linear_map.range f = ⊤) :
    continuous_linear_equiv 𝕜 E F :=
  continuous_linear_equiv.of_homothety 𝕜 (linear_equiv.of_bijective f sorry hfr) 1 zero_lt_one sorry

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
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.choose.sum
import Mathlib.data.equiv.ring
import Mathlib.algebra.algebra.operations
import Mathlib.ring_theory.ideal.basic
import Mathlib.algebra.algebra.tower
import Mathlib.PostPort

universes u v w x u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# More operations on modules and ideals
-/

namespace submodule

protected instance has_scalar' {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] : has_scalar (ideal R) (submodule R M) :=
  has_scalar.mk
    fun (I : ideal R) (N : submodule R M) =>
      supr fun (r : ↥I) => map (subtype.val r • linear_map.id) N

/-- `N.annihilator` is the ideal of all elements `r : R` such that `r • N = 0`. -/
def annihilator {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (N : submodule R M) : ideal R :=
  linear_map.ker (linear_map.lsmul R ↥N)

/-- `N.colon P` is the ideal of all elements `r : R` such that `r • P ⊆ N`. -/
def colon {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (N : submodule R M) (P : submodule R M) : ideal R :=
  annihilator (map (mkq N) P)

theorem mem_annihilator {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N : submodule R M} {r : R} : r ∈ annihilator N ↔ ∀ (n : M), n ∈ N → r • n = 0 :=
  sorry

theorem mem_annihilator' {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N : submodule R M} {r : R} : r ∈ annihilator N ↔ N ≤ comap (r • linear_map.id) ⊥ :=
  iff.trans mem_annihilator
    { mp :=
        fun (H : ∀ (n : M), n ∈ N → r • n = 0) (n : M) (hn : n ∈ N) => iff.mpr (mem_bot R) (H n hn),
      mpr :=
        fun (H : N ≤ comap (r • linear_map.id) ⊥) (n : M) (hn : n ∈ N) =>
          iff.mp (mem_bot R) (H hn) }

theorem annihilator_bot {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M] :
    annihilator ⊥ = ⊤ :=
  iff.mpr (ideal.eq_top_iff_one (annihilator ⊥)) (iff.mpr mem_annihilator' bot_le)

theorem annihilator_eq_top_iff {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] {N : submodule R M} : annihilator N = ⊤ ↔ N = ⊥ :=
  sorry

theorem annihilator_mono {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N : submodule R M} {P : submodule R M} (h : N ≤ P) : annihilator P ≤ annihilator N :=
  fun (r : R) (hrp : r ∈ annihilator P) =>
    iff.mpr mem_annihilator fun (n : M) (hn : n ∈ N) => iff.mp mem_annihilator hrp n (h hn)

theorem annihilator_supr {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (ι : Sort w) (f : ι → submodule R M) :
    annihilator (supr fun (i : ι) => f i) = infi fun (i : ι) => annihilator (f i) :=
  sorry

theorem mem_colon {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N : submodule R M} {P : submodule R M} {r : R} :
    r ∈ colon N P ↔ ∀ (p : M), p ∈ P → r • p ∈ N :=
  sorry

theorem mem_colon' {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N : submodule R M} {P : submodule R M} {r : R} :
    r ∈ colon N P ↔ P ≤ comap (r • linear_map.id) N :=
  mem_colon

theorem colon_mono {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {N₁ : submodule R M} {N₂ : submodule R M} {P₁ : submodule R M} {P₂ : submodule R M}
    (hn : N₁ ≤ N₂) (hp : P₁ ≤ P₂) : colon N₁ P₂ ≤ colon N₂ P₁ :=
  fun (r : R) (hrnp : r ∈ colon N₁ P₂) =>
    iff.mpr mem_colon fun (p₁ : M) (hp₁ : p₁ ∈ P₁) => hn (iff.mp mem_colon hrnp p₁ (hp hp₁))

theorem infi_colon_supr {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (ι₁ : Sort w) (f : ι₁ → submodule R M) (ι₂ : Sort x) (g : ι₂ → submodule R M) :
    colon (infi fun (i : ι₁) => f i) (supr fun (j : ι₂) => g j) =
        infi fun (i : ι₁) => infi fun (j : ι₂) => colon (f i) (g j) :=
  sorry

theorem smul_mem_smul {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {N : submodule R M} {r : R} {n : M} (hr : r ∈ I) (hn : n ∈ N) : r • n ∈ I • N :=
  le_supr (fun (r : ↥I) => map (subtype.val r • linear_map.id) N) { val := r, property := hr }
    (r • n) (Exists.intro n { left := hn, right := rfl })

theorem smul_le {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {N : submodule R M} {P : submodule R M} :
    I • N ≤ P ↔ ∀ (r : R), r ∈ I → ∀ (n : M), n ∈ N → r • n ∈ P :=
  sorry

theorem smul_induction_on {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {N : submodule R M} {p : M → Prop} {x : M} (H : x ∈ I • N)
    (Hb : ∀ (r : R), r ∈ I → ∀ (n : M), n ∈ N → p (r • n)) (H0 : p 0)
    (H1 : ∀ (x y : M), p x → p y → p (x + y)) (H2 : ∀ (c : R) (n : M), p n → p (c • n)) : p x :=
  iff.mpr smul_le Hb x H

theorem mem_smul_span_singleton {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] {I : ideal R} {m : M} {x : M} :
    x ∈ I • span R (singleton m) ↔ ∃ (y : R), ∃ (H : y ∈ I), y • m = x :=
  sorry

theorem smul_le_right {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {N : submodule R M} : I • N ≤ N :=
  iff.mpr smul_le fun (r : R) (hr : r ∈ I) (n : M) => smul_mem N r

theorem smul_mono {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {J : ideal R} {N : submodule R M} {P : submodule R M} (hij : I ≤ J)
    (hnp : N ≤ P) : I • N ≤ J • P :=
  iff.mpr smul_le fun (r : R) (hr : r ∈ I) (n : M) (hn : n ∈ N) => smul_mem_smul (hij hr) (hnp hn)

theorem smul_mono_left {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {J : ideal R} {N : submodule R M} (h : I ≤ J) : I • N ≤ J • N :=
  smul_mono h (le_refl N)

theorem smul_mono_right {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    {I : ideal R} {N : submodule R M} {P : submodule R M} (h : N ≤ P) : I • N ≤ I • P :=
  smul_mono (le_refl I) h

@[simp] theorem smul_bot {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (I : ideal R) : I • ⊥ = ⊥ :=
  sorry

@[simp] theorem bot_smul {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (N : submodule R M) : ⊥ • N = ⊥ :=
  sorry

@[simp] theorem top_smul {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (N : submodule R M) : ⊤ • N = N :=
  le_antisymm smul_le_right fun (r : M) (hri : r ∈ N) => one_smul R r ▸ smul_mem_smul mem_top hri

theorem smul_sup {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (I : ideal R) (N : submodule R M) (P : submodule R M) : I • (N ⊔ P) = I • N ⊔ I • P :=
  sorry

theorem sup_smul {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (I : ideal R) (J : ideal R) (N : submodule R M) : (I ⊔ J) • N = I • N ⊔ J • N :=
  sorry

protected theorem smul_assoc {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (I : ideal R) (J : ideal R) (N : submodule R M) : (I • J) • N = I • J • N :=
  sorry

theorem span_smul_span {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (S : set R) (T : set M) :
    ideal.span S • span R T =
        span R
          (set.Union
            fun (s : R) =>
              set.Union
                fun (H : s ∈ S) =>
                  set.Union fun (t : M) => set.Union fun (H : t ∈ T) => singleton (s • t)) :=
  sorry

theorem map_smul'' {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M] [module R M]
    (I : ideal R) (N : submodule R M) {M' : Type w} [add_comm_group M'] [module R M']
    (f : linear_map R M M') : map f (I • N) = I • map f N :=
  sorry

end submodule

namespace ideal

theorem exists_sub_one_mem_and_mem {R : Type u} [comm_ring R] {ι : Type v} (s : finset ι)
    {f : ι → ideal R} (hf : ∀ (i : ι), i ∈ s → ∀ (j : ι), j ∈ s → i ≠ j → f i ⊔ f j = ⊤) (i : ι)
    (his : i ∈ s) : ∃ (r : R), r - 1 ∈ f i ∧ ∀ (j : ι), j ∈ s → j ≠ i → r ∈ f j :=
  sorry

theorem exists_sub_mem {R : Type u} [comm_ring R] {ι : Type v} [fintype ι] {f : ι → ideal R}
    (hf : ∀ (i j : ι), i ≠ j → f i ⊔ f j = ⊤) (g : ι → R) : ∃ (r : R), ∀ (i : ι), r - g i ∈ f i :=
  sorry

/-- The homomorphism from `R/(⋂ i, f i)` to `∏ i, (R / f i)` featured in the Chinese
  Remainder Theorem. It is bijective if the ideals `f i` are comaximal. -/
def quotient_inf_to_pi_quotient {R : Type u} [comm_ring R] {ι : Type v} (f : ι → ideal R) :
    quotient (infi fun (i : ι) => f i) →+* (i : ι) → quotient (f i) :=
  quotient.lift (infi fun (i : ι) => f i)
    (eq.mpr sorry (pi.ring_hom fun (i : ι) => quotient.mk (f i))) sorry

theorem quotient_inf_to_pi_quotient_bijective {R : Type u} [comm_ring R] {ι : Type v} [fintype ι]
    {f : ι → ideal R} (hf : ∀ (i j : ι), i ≠ j → f i ⊔ f j = ⊤) :
    function.bijective ⇑(quotient_inf_to_pi_quotient f) :=
  sorry

/-- Chinese Remainder Theorem. Eisenbud Ex.2.6. Similar to Atiyah-Macdonald 1.10 and Stacks 00DT -/
def quotient_inf_ring_equiv_pi_quotient {R : Type u} [comm_ring R] {ι : Type v} [fintype ι]
    (f : ι → ideal R) (hf : ∀ (i j : ι), i ≠ j → f i ⊔ f j = ⊤) :
    quotient (infi fun (i : ι) => f i) ≃+* ((i : ι) → quotient (f i)) :=
  ring_equiv.mk
    (equiv.to_fun (equiv.of_bijective ⇑(quotient_inf_to_pi_quotient fun (i : ι) => f i) sorry))
    (equiv.inv_fun (equiv.of_bijective ⇑(quotient_inf_to_pi_quotient fun (i : ι) => f i) sorry))
    sorry sorry sorry sorry

protected instance has_mul {R : Type u} [comm_ring R] : Mul (ideal R) := { mul := has_scalar.smul }

theorem mul_mem_mul {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {r : R} {s : R}
    (hr : r ∈ I) (hs : s ∈ J) : r * s ∈ I * J :=
  submodule.smul_mem_smul hr hs

theorem mul_mem_mul_rev {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {r : R} {s : R}
    (hr : r ∈ I) (hs : s ∈ J) : s * r ∈ I * J :=
  mul_comm r s ▸ mul_mem_mul hr hs

theorem mul_le {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {K : ideal R} :
    I * J ≤ K ↔ ∀ (r : R), r ∈ I → ∀ (s : R), s ∈ J → r * s ∈ K :=
  submodule.smul_le

theorem mul_le_left {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} : I * J ≤ J :=
  iff.mpr mul_le fun (r : R) (hr : r ∈ I) (s : R) => mul_mem_left J r

theorem mul_le_right {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} : I * J ≤ I :=
  iff.mpr mul_le fun (r : R) (hr : r ∈ I) (s : R) (hs : s ∈ J) => mul_mem_right I s hr

@[simp] theorem sup_mul_right_self {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} :
    I ⊔ I * J = I :=
  iff.mpr sup_eq_left mul_le_right

@[simp] theorem sup_mul_left_self {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} :
    I ⊔ J * I = I :=
  iff.mpr sup_eq_left mul_le_left

@[simp] theorem mul_right_self_sup {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} :
    I * J ⊔ I = I :=
  iff.mpr sup_eq_right mul_le_right

@[simp] theorem mul_left_self_sup {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} :
    J * I ⊔ I = I :=
  iff.mpr sup_eq_right mul_le_left

protected theorem mul_comm {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) : I * J = J * I :=
  le_antisymm
    (iff.mpr mul_le fun (r : R) (hrI : r ∈ I) (s : R) (hsJ : s ∈ J) => mul_mem_mul_rev hsJ hrI)
    (iff.mpr mul_le fun (r : R) (hrJ : r ∈ J) (s : R) (hsI : s ∈ I) => mul_mem_mul_rev hsI hrJ)

protected theorem mul_assoc {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) (K : ideal R) :
    I * J * K = I * (J * K) :=
  submodule.smul_assoc I J K

theorem span_mul_span {R : Type u} [comm_ring R] (S : set R) (T : set R) :
    span S * span T =
        span
          (set.Union
            fun (s : R) =>
              set.Union
                fun (H : s ∈ S) =>
                  set.Union fun (t : R) => set.Union fun (H : t ∈ T) => singleton (s * t)) :=
  submodule.span_smul_span S T

theorem span_mul_span' {R : Type u} [comm_ring R] (S : set R) (T : set R) :
    span S * span T = span (S * T) :=
  sorry

theorem span_singleton_mul_span_singleton {R : Type u} [comm_ring R] (r : R) (s : R) :
    span (singleton r) * span (singleton s) = span (singleton (r * s)) :=
  sorry

theorem mul_le_inf {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} : I * J ≤ I ⊓ J :=
  iff.mpr mul_le
    fun (r : R) (hri : r ∈ I) (s : R) (hsj : s ∈ J) =>
      { left := mul_mem_right I s hri, right := mul_mem_left J r hsj }

theorem prod_le_inf {R : Type u} {ι : Type u_1} [comm_ring R] {s : finset ι} {f : ι → ideal R} :
    finset.prod s f ≤ finset.inf s f :=
  sorry

theorem mul_eq_inf_of_coprime {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R}
    (h : I ⊔ J = ⊤) : I * J = I ⊓ J :=
  sorry

theorem mul_bot {R : Type u} [comm_ring R] (I : ideal R) : I * ⊥ = ⊥ := submodule.smul_bot I

theorem bot_mul {R : Type u} [comm_ring R] (I : ideal R) : ⊥ * I = ⊥ := submodule.bot_smul I

theorem mul_top {R : Type u} [comm_ring R] (I : ideal R) : I * ⊤ = I :=
  ideal.mul_comm ⊤ I ▸ submodule.top_smul I

theorem top_mul {R : Type u} [comm_ring R] (I : ideal R) : ⊤ * I = I := submodule.top_smul I

theorem mul_mono {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {K : ideal R} {L : ideal R}
    (hik : I ≤ K) (hjl : J ≤ L) : I * J ≤ K * L :=
  submodule.smul_mono hik hjl

theorem mul_mono_left {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {K : ideal R}
    (h : I ≤ J) : I * K ≤ J * K :=
  submodule.smul_mono_left h

theorem mul_mono_right {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {K : ideal R}
    (h : J ≤ K) : I * J ≤ I * K :=
  submodule.smul_mono_right h

theorem mul_sup {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) (K : ideal R) :
    I * (J ⊔ K) = I * J ⊔ I * K :=
  submodule.smul_sup I J K

theorem sup_mul {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) (K : ideal R) :
    (I ⊔ J) * K = I * K ⊔ J * K :=
  submodule.sup_smul I J K

theorem pow_le_pow {R : Type u} [comm_ring R] {I : ideal R} {m : ℕ} {n : ℕ} (h : m ≤ n) :
    I ^ n ≤ I ^ m :=
  sorry

theorem mul_eq_bot {R : Type u_1} [integral_domain R] {I : ideal R} {J : ideal R} :
    I * J = ⊥ ↔ I = ⊥ ∨ J = ⊥ :=
  sorry

/-- The radical of an ideal `I` consists of the elements `r` such that `r^n ∈ I` for some `n`. -/
def radical {R : Type u} [comm_ring R] (I : ideal R) : ideal R :=
  submodule.mk (set_of fun (r : R) => ∃ (n : ℕ), r ^ n ∈ I) sorry sorry sorry

theorem le_radical {R : Type u} [comm_ring R] {I : ideal R} : I ≤ radical I :=
  fun (r : R) (hri : r ∈ I) => Exists.intro 1 (Eq.symm (pow_one r) ▸ hri)

theorem radical_top (R : Type u) [comm_ring R] : radical ⊤ = ⊤ :=
  iff.mpr (eq_top_iff_one (radical ⊤)) (Exists.intro 0 submodule.mem_top)

theorem radical_mono {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} (H : I ≤ J) :
    radical I ≤ radical J :=
  sorry

@[simp] theorem radical_idem {R : Type u} [comm_ring R] (I : ideal R) :
    radical (radical I) = radical I :=
  sorry

theorem radical_eq_top {R : Type u} [comm_ring R] {I : ideal R} : radical I = ⊤ ↔ I = ⊤ := sorry

theorem is_prime.radical {R : Type u} [comm_ring R] {I : ideal R} (H : is_prime I) :
    radical I = I :=
  sorry

theorem radical_sup {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) :
    radical (I ⊔ J) = radical (radical I ⊔ radical J) :=
  sorry

theorem radical_inf {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) :
    radical (I ⊓ J) = radical I ⊓ radical J :=
  sorry

theorem radical_mul {R : Type u} [comm_ring R] (I : ideal R) (J : ideal R) :
    radical (I * J) = radical I ⊓ radical J :=
  sorry

theorem is_prime.radical_le_iff {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R}
    (hj : is_prime J) : radical I ≤ J ↔ I ≤ J :=
  sorry

theorem radical_eq_Inf {R : Type u} [comm_ring R] (I : ideal R) :
    radical I = Inf (set_of fun (J : ideal R) => I ≤ J ∧ is_prime J) :=
  sorry

@[simp] theorem radical_bot_of_integral_domain {R : Type u} [integral_domain R] : radical ⊥ = ⊥ :=
  iff.mpr eq_bot_iff
    fun (x : R) (hx : x ∈ radical ⊥) =>
      Exists.rec_on hx fun (n : ℕ) (hn : x ^ n ∈ ⊥) => pow_eq_zero hn

protected instance comm_semiring {R : Type u} [comm_ring R] : comm_semiring (ideal R) :=
  submodule.comm_semiring

@[simp] theorem add_eq_sup {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} : I + J = I ⊔ J :=
  rfl

@[simp] theorem zero_eq_bot {R : Type u} [comm_ring R] : 0 = ⊥ := rfl

@[simp] theorem one_eq_top {R : Type u} [comm_ring R] : 1 = ⊤ := sorry

theorem top_pow (R : Type u) [comm_ring R] (n : ℕ) : ⊤ ^ n = ⊤ := sorry

theorem radical_pow {R : Type u} [comm_ring R] (I : ideal R) (n : ℕ) (H : n > 0) :
    radical (I ^ n) = radical I :=
  sorry

theorem is_prime.mul_le {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {P : ideal R}
    (hp : is_prime P) : I * J ≤ P ↔ I ≤ P ∨ J ≤ P :=
  sorry

theorem is_prime.inf_le {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {P : ideal R}
    (hp : is_prime P) : I ⊓ J ≤ P ↔ I ≤ P ∨ J ≤ P :=
  { mp := fun (h : I ⊓ J ≤ P) => iff.mp (is_prime.mul_le hp) (le_trans mul_le_inf h),
    mpr := fun (h : I ≤ P ∨ J ≤ P) => or.cases_on h (le_trans inf_le_left) (le_trans inf_le_right) }

theorem is_prime.prod_le {R : Type u} {ι : Type u_1} [comm_ring R] {s : finset ι} {f : ι → ideal R}
    {P : ideal R} (hp : is_prime P) (hne : finset.nonempty s) :
    finset.prod s f ≤ P ↔ ∃ (i : ι), ∃ (H : i ∈ s), f i ≤ P :=
  sorry

theorem is_prime.inf_le' {R : Type u} {ι : Type u_1} [comm_ring R] {s : finset ι} {f : ι → ideal R}
    {P : ideal R} (hp : is_prime P) (hsne : finset.nonempty s) :
    finset.inf s f ≤ P ↔ ∃ (i : ι), ∃ (H : i ∈ s), f i ≤ P :=
  sorry

theorem subset_union {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} {K : ideal R} :
    ↑I ⊆ ↑J ∪ ↑K ↔ I ≤ J ∨ I ≤ K :=
  sorry

theorem subset_union_prime' {R : Type u} {ι : Type u_1} [comm_ring R] {s : finset ι}
    {f : ι → ideal R} {a : ι} {b : ι} (hp : ∀ (i : ι), i ∈ s → is_prime (f i)) {I : ideal R} :
    (↑I ⊆ ↑(f a) ∪ ↑(f b) ∪ set.Union fun (i : ι) => set.Union fun (H : i ∈ ↑s) => ↑(f i)) ↔
        I ≤ f a ∨ I ≤ f b ∨ ∃ (i : ι), ∃ (H : i ∈ s), I ≤ f i :=
  sorry

/-- Prime avoidance. Atiyah-Macdonald 1.11, Eisenbud 3.3, Stacks 00DS, Matsumura Ex.1.6. -/
theorem subset_union_prime {R : Type u} {ι : Type u_1} [comm_ring R] {s : finset ι}
    {f : ι → ideal R} (a : ι) (b : ι) (hp : ∀ (i : ι), i ∈ s → i ≠ a → i ≠ b → is_prime (f i))
    {I : ideal R} :
    (↑I ⊆ set.Union fun (i : ι) => set.Union fun (H : i ∈ ↑s) => ↑(f i)) ↔
        ∃ (i : ι), ∃ (H : i ∈ s), I ≤ f i :=
  sorry

/-- `I.map f` is the span of the image of the ideal `I` under `f`, which may be bigger than
  the image itself. -/
def map {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) (I : ideal R) :
    ideal S :=
  span (⇑f '' ↑I)

/-- `I.comap f` is the preimage of `I` under `f`. -/
def comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) (I : ideal S) :
    ideal R :=
  submodule.mk (⇑f ⁻¹' ↑I) sorry sorry sorry

theorem map_mono {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S} {I : ideal R}
    {J : ideal R} (h : I ≤ J) : map f I ≤ map f J :=
  span_mono (set.image_subset (⇑f) h)

theorem mem_map_of_mem {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal R} {x : R} (h : x ∈ I) : coe_fn f x ∈ map f I :=
  subset_span (Exists.intro x { left := h, right := rfl })

theorem map_le_iff_le_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal R} {K : ideal S} : map f I ≤ K ↔ I ≤ comap f K :=
  iff.trans span_le set.image_subset_iff

@[simp] theorem mem_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {K : ideal S} {x : R} : x ∈ comap f K ↔ coe_fn f x ∈ K :=
  iff.rfl

theorem comap_mono {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S} {K : ideal S}
    {L : ideal S} (h : K ≤ L) : comap f K ≤ comap f L :=
  set.preimage_mono fun (x : R) (hx : x ∈ ↑(comap f K)) => h hx

theorem comap_ne_top {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {K : ideal S} (hK : K ≠ ⊤) : comap f K ≠ ⊤ :=
  iff.mpr (ne_top_iff_one (comap f K))
    (eq.mpr (id (Eq._oldrec (Eq.refl (¬1 ∈ comap f K)) (propext mem_comap)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (¬coe_fn f 1 ∈ K)) (ring_hom.map_one f)))
        (iff.mp (ne_top_iff_one K) hK)))

theorem is_prime.comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {K : ideal S} [hK : is_prime K] : is_prime (comap f K) :=
  sorry

theorem map_top {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) : map f ⊤ = ⊤ :=
  iff.mpr (eq_top_iff_one (map f ⊤))
    (subset_span (Exists.intro 1 { left := trivial, right := ring_hom.map_one f }))

theorem map_mul {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) (I : ideal R)
    (J : ideal R) : map f (I * J) = map f I * map f J :=
  sorry

theorem gc_map_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) :
    galois_connection (map f) (comap f) :=
  fun (I : ideal R) (J : ideal S) => map_le_iff_le_comap

@[simp] theorem comap_id {R : Type u} [comm_ring R] (I : ideal R) : comap (ring_hom.id R) I = I :=
  ext fun (_x : R) => iff.rfl

@[simp] theorem map_id {R : Type u} [comm_ring R] (I : ideal R) : map (ring_hom.id R) I = I :=
  galois_connection.l_unique (gc_map_comap (ring_hom.id R)) galois_connection.id comap_id

theorem comap_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {T : Type u_1}
    [comm_ring T] {I : ideal T} (f : R →+* S) (g : S →+* T) :
    comap f (comap g I) = comap (ring_hom.comp g f) I :=
  rfl

theorem map_map {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {T : Type u_1} [comm_ring T]
    {I : ideal R} (f : R →+* S) (g : S →+* T) : map g (map f I) = map (ring_hom.comp g f) I :=
  galois_connection.l_unique
    (galois_connection.compose (map f) (comap f) (map g) (comap g) (gc_map_comap f)
      (gc_map_comap g))
    (gc_map_comap (ring_hom.comp g f)) fun (_x : ideal T) => comap_comap f g

theorem map_le_of_le_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal R} {K : ideal S} : I ≤ comap f K → map f I ≤ K :=
  galois_connection.l_le (gc_map_comap f)

theorem le_comap_of_map_le {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal R} {K : ideal S} : map f I ≤ K → I ≤ comap f K :=
  galois_connection.le_u (gc_map_comap f)

theorem le_comap_map {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal R} : I ≤ comap f (map f I) :=
  galois_connection.le_u_l (gc_map_comap f) I

theorem map_comap_le {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {K : ideal S} : map f (comap f K) ≤ K :=
  galois_connection.l_u_le (gc_map_comap f) K

@[simp] theorem comap_top {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S} :
    comap f ⊤ = ⊤ :=
  galois_connection.u_top (gc_map_comap f)

@[simp] theorem comap_eq_top_iff {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S}
    {I : ideal S} : comap f I = ⊤ ↔ I = ⊤ :=
  sorry

@[simp] theorem map_bot {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] {f : R →+* S} :
    map f ⊥ = ⊥ :=
  galois_connection.l_bot (gc_map_comap f)

@[simp] theorem map_comap_map {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (I : ideal R) : map f (comap f (map f I)) = map f I :=
  congr_fun (galois_connection.l_u_l_eq_l (gc_map_comap f)) I

@[simp] theorem comap_map_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (K : ideal S) : comap f (map f (comap f K)) = comap f K :=
  congr_fun (galois_connection.u_l_u_eq_u (gc_map_comap f)) K

theorem map_sup {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) (I : ideal R)
    (J : ideal R) : map f (I ⊔ J) = map f I ⊔ map f J :=
  galois_connection.l_sup (gc_map_comap f)

theorem comap_inf {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) (K : ideal S)
    (L : ideal S) : comap f (K ⊓ L) = comap f K ⊓ comap f L :=
  rfl

theorem map_supr {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) {ι : Sort u_1}
    (K : ι → ideal R) : map f (supr K) = supr fun (i : ι) => map f (K i) :=
  galois_connection.l_supr (gc_map_comap f)

theorem comap_infi {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {ι : Sort u_1} (K : ι → ideal S) : comap f (infi K) = infi fun (i : ι) => comap f (K i) :=
  galois_connection.u_infi (gc_map_comap f)

theorem map_Sup {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (s : set (ideal R)) :
    map f (Sup s) = supr fun (I : ideal R) => supr fun (H : I ∈ s) => map f I :=
  galois_connection.l_Sup (gc_map_comap f)

theorem comap_Inf {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (s : set (ideal S)) :
    comap f (Inf s) = infi fun (I : ideal S) => infi fun (H : I ∈ s) => comap f I :=
  galois_connection.u_Inf (gc_map_comap f)

theorem comap_Inf' {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (s : set (ideal S)) :
    comap f (Inf s) = infi fun (I : ideal R) => infi fun (H : I ∈ comap f '' s) => I :=
  sorry

theorem comap_radical {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (K : ideal S) : comap f (radical K) = radical (comap f K) :=
  sorry

theorem comap_is_prime {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (K : ideal S) [H : is_prime K] : is_prime (comap f K) :=
  sorry

@[simp] theorem map_quotient_self {R : Type u} [comm_ring R] (I : ideal R) :
    map (quotient.mk I) I = ⊥ :=
  iff.mpr eq_bot_iff
    (iff.mpr map_le_iff_le_comap
      fun (x : R) (hx : x ∈ I) =>
        iff.mpr (submodule.mem_bot (quotient I)) (iff.mpr quotient.eq_zero_iff_mem hx))

theorem map_inf_le {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) {I : ideal R}
    {J : ideal R} : map f (I ⊓ J) ≤ map f I ⊓ map f J :=
  monotone.map_inf_le (galois_connection.monotone_l (gc_map_comap f)) I J

theorem map_radical_le {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {I : ideal R} : map f (radical I) ≤ radical (map f I) :=
  sorry

theorem le_comap_sup {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {K : ideal S} {L : ideal S} : comap f K ⊔ comap f L ≤ comap f (K ⊔ L) :=
  monotone.le_map_sup (galois_connection.monotone_u (gc_map_comap f)) K L

theorem le_comap_mul {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {K : ideal S} {L : ideal S} : comap f K * comap f L ≤ comap f (K * L) :=
  iff.mp map_le_iff_le_comap
    (Eq.symm (map_mul f (comap f K) (comap f L)) ▸
      mul_mono (iff.mpr map_le_iff_le_comap (le_refl (comap f K)))
        (iff.mpr map_le_iff_le_comap (le_refl (comap f L))))

theorem map_comap_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (hf : function.surjective ⇑f) (I : ideal S) : map f (comap f I) = I :=
  sorry

/-- `map` and `comap` are adjoint, and the composition `map f ∘ comap f` is the
  identity -/
def gi_map_comap {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (hf : function.surjective ⇑f) : galois_insertion (map f) (comap f) :=
  galois_insertion.monotone_intro sorry sorry sorry (map_comap_of_surjective f hf)

theorem map_surjective_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) : function.surjective (map f) :=
  galois_insertion.l_surjective (gi_map_comap f hf)

theorem comap_injective_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) : function.injective (comap f) :=
  galois_insertion.u_injective (gi_map_comap f hf)

theorem map_sup_comap_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) (I : ideal S) (J : ideal S) :
    map f (comap f I ⊔ comap f J) = I ⊔ J :=
  galois_insertion.l_sup_u (gi_map_comap f hf) I J

theorem map_supr_comap_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {ι : Sort u_1} (hf : function.surjective ⇑f) (K : ι → ideal S) :
    map f (supr fun (i : ι) => comap f (K i)) = supr K :=
  galois_insertion.l_supr_u (gi_map_comap f hf) fun (i : ι) => K i

theorem map_inf_comap_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) (I : ideal S) (J : ideal S) :
    map f (comap f I ⊓ comap f J) = I ⊓ J :=
  galois_insertion.l_inf_u (gi_map_comap f hf) I J

theorem map_infi_comap_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {ι : Sort u_1} (hf : function.surjective ⇑f) (K : ι → ideal S) :
    map f (infi fun (i : ι) => comap f (K i)) = infi K :=
  galois_insertion.l_infi_u (gi_map_comap f hf) fun (i : ι) => K i

theorem mem_image_of_mem_map_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) {I : ideal R} {y : S} (H : y ∈ map f I) :
    y ∈ ⇑f '' ↑I :=
  sorry

theorem mem_map_iff_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) {I : ideal R} {y : S} :
    y ∈ map f I ↔ ∃ (x : R), x ∈ I ∧ coe_fn f x = y :=
  sorry

theorem comap_map_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (hf : function.surjective ⇑f) (I : ideal R) : comap f (map f I) = I ⊔ comap f ⊥ :=
  sorry

theorem le_map_of_comap_le_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {I : ideal R} {K : ideal S} (hf : function.surjective ⇑f) :
    comap f K ≤ I → K ≤ map f I :=
  fun (h : comap f K ≤ I) => map_comap_of_surjective f hf K ▸ map_mono h

/-- Correspondence theorem -/
def rel_iso_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (hf : function.surjective ⇑f) : ideal S ≃o Subtype fun (p : ideal R) => comap f ⊥ ≤ p :=
  rel_iso.mk
    (equiv.mk (fun (J : ideal S) => { val := comap f J, property := sorry })
      (fun (I : Subtype fun (p : ideal R) => comap f ⊥ ≤ p) => map f (subtype.val I)) sorry sorry)
    sorry

/-- The map on ideals induced by a surjective map preserves inclusion. -/
def order_embedding_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) (hf : function.surjective ⇑f) : ideal S ↪o ideal R :=
  rel_embedding.trans (rel_iso.to_rel_embedding (rel_iso_of_surjective f hf))
    (subtype.rel_embedding LessEq fun (p : ideal R) => comap f ⊥ ≤ p)

theorem map_eq_top_or_is_maximal_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {I : ideal R} (hf : function.surjective ⇑f) (H : is_maximal I) :
    map f I = ⊤ ∨ is_maximal (map f I) :=
  sorry

theorem comap_is_maximal_of_surjective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {K : ideal S} (hf : function.surjective ⇑f) [H : is_maximal K] :
    is_maximal (comap f K) :=
  sorry

theorem mem_quotient_iff_mem {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} (hIJ : I ≤ J)
    {x : R} : coe_fn (quotient.mk I) x ∈ map (quotient.mk I) J ↔ x ∈ J :=
  sorry

theorem comap_bot_le_of_injective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) {I : ideal R} (hf : function.injective ⇑f) : comap f ⊥ ≤ I :=
  sorry

/-- Special case of the correspondence theorem for isomorphic rings -/
def rel_iso_of_bijective {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    (hf : function.bijective ⇑f) : ideal S ≃o ideal R :=
  rel_iso.mk (equiv.mk (comap f) (map f) sorry sorry) sorry

theorem comap_le_iff_le_map {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {I : ideal R} {K : ideal S} (hf : function.bijective ⇑f) : comap f K ≤ I ↔ K ≤ map f I :=
  { mp := fun (h : comap f K ≤ I) => le_map_of_comap_le_of_surjective f (and.right hf) h,
    mpr :=
      fun (h : K ≤ map f I) =>
        equiv.right_inv (rel_iso.to_equiv (rel_iso_of_bijective f hf)) I ▸ comap_mono h }

theorem map.is_maximal {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S)
    {I : ideal R} (hf : function.bijective ⇑f) (H : is_maximal I) : is_maximal (map f I) :=
  sorry

theorem ring_equiv.bot_maximal_iff {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (e : R ≃+* S) : is_maximal ⊥ ↔ is_maximal ⊥ :=
  sorry

/-- A proper ideal `I` is primary iff `xy ∈ I` implies `x ∈ I` or `y ∈ radical I`. -/
def is_primary {R : Type u} [comm_ring R] (I : ideal R) :=
  I ≠ ⊤ ∧ ∀ {x y : R}, x * y ∈ I → x ∈ I ∨ y ∈ radical I

theorem is_primary.to_is_prime {R : Type u} [comm_ring R] (I : ideal R) (hi : is_prime I) :
    is_primary I :=
  { left := and.left hi,
    right :=
      fun (x y : R) (hxy : x * y ∈ I) =>
        or.imp id (fun (hyi : y ∈ I) => le_radical hyi) (and.right hi x y hxy) }

theorem mem_radical_of_pow_mem {R : Type u} [comm_ring R] {I : ideal R} {x : R} {m : ℕ}
    (hx : x ^ m ∈ radical I) : x ∈ radical I :=
  radical_idem I ▸ Exists.intro m hx

theorem is_prime_radical {R : Type u} [comm_ring R] {I : ideal R} (hi : is_primary I) :
    is_prime (radical I) :=
  sorry

theorem is_primary_inf {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} (hi : is_primary I)
    (hj : is_primary J) (hij : radical I = radical J) : is_primary (I ⊓ J) :=
  sorry

end ideal

namespace ring_hom

/-- Kernel of a ring homomorphism as an ideal of the domain. -/
def ker {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) : ideal R :=
  ideal.comap f ⊥

/-- An element is in the kernel if and only if it maps to zero.-/
theorem mem_ker {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) {r : R} :
    r ∈ ker f ↔ coe_fn f r = 0 :=
  sorry

theorem ker_eq {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) :
    ↑(ker f) = is_add_group_hom.ker ⇑f :=
  rfl

theorem ker_eq_comap_bot {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) :
    ker f = ideal.comap f ⊥ :=
  rfl

theorem injective_iff_ker_eq_bot {R : Type u} {S : Type v} [comm_ring R] [comm_ring S]
    (f : R →+* S) : function.injective ⇑f ↔ ker f = ⊥ :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (function.injective ⇑f ↔ ker f = ⊥)) (propext submodule.ext'_iff)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (function.injective ⇑f ↔ ↑(ker f) = ↑⊥)) (ker_eq f)))
      (is_add_group_hom.injective_iff_trivial_ker ⇑f))

theorem ker_eq_bot_iff_eq_zero {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R →+* S) :
    ker f = ⊥ ↔ ∀ (x : R), coe_fn f x = 0 → x = 0 :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (ker f = ⊥ ↔ ∀ (x : R), coe_fn f x = 0 → x = 0))
        (propext submodule.ext'_iff)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (↑(ker f) = ↑⊥ ↔ ∀ (x : R), coe_fn f x = 0 → x = 0)) (ker_eq f)))
      (is_add_group_hom.trivial_ker_iff_eq_zero ⇑f))

/-- If the target is not the zero ring, then one is not in the kernel.-/
theorem not_one_mem_ker {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] [nontrivial S]
    (f : R →+* S) : ¬1 ∈ ker f :=
  eq.mpr (id (Eq._oldrec (Eq.refl (¬1 ∈ ker f)) (propext (mem_ker f))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (¬coe_fn f 1 = 0)) (map_one f))) one_ne_zero)

@[simp] theorem ker_coe_equiv {R : Type u} {S : Type v} [comm_ring R] [comm_ring S] (f : R ≃+* S) :
    ker ↑f = ⊥ :=
  eq.mpr (id (propext (iff.symm (injective_iff_ker_eq_bot ↑f))))
    (eq.mp (Eq.refl (function.injective ⇑f)) (ring_equiv.injective f))

/-- The kernel of a homomorphism to an integral domain is a prime ideal.-/
theorem ker_is_prime {R : Type u} {S : Type v} [comm_ring R] [integral_domain S] (f : R →+* S) :
    ideal.is_prime (ker f) :=
  sorry

end ring_hom

namespace ideal

theorem map_eq_bot_iff_le_ker {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {I : ideal R} (f : R →+* S) : map f I = ⊥ ↔ I ≤ ring_hom.ker f :=
  sorry

@[simp] theorem mk_ker {R : Type u_1} [comm_ring R] {I : ideal R} :
    ring_hom.ker (quotient.mk I) = I :=
  sorry

theorem ker_le_comap {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S] {K : ideal S}
    (f : R →+* S) : ring_hom.ker f ≤ comap f K :=
  fun (x : R) (hx : x ∈ ring_hom.ker f) =>
    iff.mpr mem_comap (Eq.symm (iff.mp (ring_hom.mem_ker f) hx) ▸ ideal.zero_mem K)

theorem map_Inf {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S] {A : set (ideal R)}
    {f : R →+* S} (hf : function.surjective ⇑f) :
    (∀ (J : ideal R), J ∈ A → ring_hom.ker f ≤ J) → map f (Inf A) = Inf (map f '' A) :=
  sorry

theorem map_is_prime_of_surjective {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {f : R →+* S} (hf : function.surjective ⇑f) {I : ideal R} [H : is_prime I]
    (hk : ring_hom.ker f ≤ I) : is_prime (map f I) :=
  sorry

theorem map_is_prime_of_equiv {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    (f : R ≃+* S) {I : ideal R} [is_prime I] : is_prime (map (↑f) I) :=
  sorry

theorem map_radical_of_surjective {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {f : R →+* S} (hf : function.surjective ⇑f) {I : ideal R} (h : ring_hom.ker f ≤ I) :
    map f (radical I) = radical (map f I) :=
  sorry

@[simp] theorem bot_quotient_is_maximal_iff {R : Type u_1} [comm_ring R] (I : ideal R) :
    is_maximal ⊥ ↔ is_maximal I :=
  { mp :=
      fun (hI : is_maximal ⊥) =>
        mk_ker ▸ comap_is_maximal_of_surjective (quotient.mk I) quotient.mk_surjective,
    mpr := fun (hI : is_maximal I) => bot_is_maximal }

/-- The `R`-algebra structure on `A/I` for an `R`-algebra `A` -/
protected instance quotient.algebra (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A]
    [algebra R A] {I : ideal A} : algebra R (quotient I) :=
  ring_hom.to_algebra (ring_hom.comp (quotient.mk I) (algebra_map R A))

/-- The canonical morphism `A →ₐ[R] I.quotient` as morphism of `R`-algebras, for `I` an ideal of
`A`, where `A` is an `R`-algebra. -/
def quotient.mkₐ (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A] [algebra R A]
    (I : ideal A) : alg_hom R A (quotient I) :=
  alg_hom.mk (fun (a : A) => submodule.quotient.mk a) sorry sorry sorry sorry sorry

theorem quotient.alg_map_eq (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A] [algebra R A]
    (I : ideal A) :
    algebra_map R (quotient I) = ring_hom.comp (algebra_map A (quotient I)) (algebra_map R A) :=
  sorry

protected instance quotient.is_scalar_tower (R : Type u_1) [comm_ring R] {A : Type u_3}
    [comm_ring A] [algebra R A] {I : ideal A} : is_scalar_tower R A (quotient I) :=
  is_scalar_tower.of_algebra_map_eq' (quotient.alg_map_eq R I)

theorem quotient.mkₐ_to_ring_hom (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A]
    [algebra R A] (I : ideal A) : alg_hom.to_ring_hom (quotient.mkₐ R I) = quotient.mk I :=
  rfl

@[simp] theorem quotient.mkₐ_eq_mk (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A]
    [algebra R A] (I : ideal A) : ⇑(quotient.mkₐ R I) = ⇑(quotient.mk I) :=
  rfl

/-- The canonical morphism `A →ₐ[R] I.quotient` is surjective. -/
theorem quotient.mkₐ_surjective (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A]
    [algebra R A] (I : ideal A) : function.surjective ⇑(quotient.mkₐ R I) :=
  surjective_quot_mk setoid.r

/-- The kernel of `A →ₐ[R] I.quotient` is `I`. -/
@[simp] theorem quotient.mkₐ_ker (R : Type u_1) [comm_ring R] {A : Type u_3} [comm_ring A]
    [algebra R A] (I : ideal A) : ring_hom.ker (alg_hom.to_ring_hom (quotient.mkₐ R I)) = I :=
  mk_ker

/-- The ring hom `R/J →+* S/I` induced by a ring hom `f : R →+* S` with `J ≤ f⁻¹(I)` -/
def quotient_map {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S] {I : ideal R}
    (J : ideal S) (f : R →+* S) (hIJ : I ≤ comap f J) : quotient I →+* quotient J :=
  quotient.lift I (ring_hom.comp (quotient.mk J) f) sorry

@[simp] theorem quotient_map_mk {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {J : ideal R} {I : ideal S} {f : R →+* S} {H : J ≤ comap f I} {x : R} :
    coe_fn (quotient_map I f H) (coe_fn (quotient.mk J) x) = coe_fn (quotient.mk I) (coe_fn f x) :=
  quotient.lift_mk J (ring_hom.comp (quotient.mk I) f) (quotient_map._proof_1 I f H)

theorem quotient_map_comp_mk {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S] {J : ideal R}
    {I : ideal S} {f : R →+* S} (H : J ≤ comap f I) :
    ring_hom.comp (quotient_map I f H) (quotient.mk J) = ring_hom.comp (quotient.mk I) f :=
  sorry

/-- `H` and `h` are kept as seperate hypothesis since H is used in constructing the quotient map -/
theorem quotient_map_injective' {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {J : ideal R} {I : ideal S} {f : R →+* S} {H : J ≤ comap f I} (h : comap f I ≤ J) :
    function.injective ⇑(quotient_map I f H) :=
  sorry

/-- If we take `J = I.comap f` then `quotient_map` is injective automatically. -/
theorem quotient_map_injective {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {I : ideal S} {f : R →+* S} : function.injective ⇑(quotient_map I f le_rfl) :=
  quotient_map_injective' le_rfl

theorem quotient_map_surjective {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {J : ideal R} {I : ideal S} {f : R →+* S} {H : J ≤ comap f I} (hf : function.surjective ⇑f) :
    function.surjective ⇑(quotient_map I f H) :=
  sorry

/-- Commutativity of a square is preserved when taking quotients by an ideal. -/
theorem comp_quotient_map_eq_of_comp_eq {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {R' : Type u_3} {S' : Type u_4} [comm_ring R'] [comm_ring S'] {f : R →+* S} {f' : R' →+* S'}
    {g : R →+* R'} {g' : S →+* S'} (hfg : ring_hom.comp f' g = ring_hom.comp g' f) (I : ideal S') :
    ring_hom.comp (quotient_map I g' le_rfl) (quotient_map (comap g' I) f le_rfl) =
        ring_hom.comp (quotient_map I f' le_rfl)
          (quotient_map (comap f' I) g
            (le_of_eq (trans (comap_comap f g') (hfg ▸ comap_comap g f')))) :=
  sorry

protected instance quotient_algebra {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {J : ideal S} [algebra R S] : algebra (quotient (comap (algebra_map R S) J)) (quotient J) :=
  ring_hom.to_algebra (quotient_map J (algebra_map R S) sorry)

theorem algebra_map_quotient_injective {R : Type u_1} {S : Type u_2} [comm_ring R] [comm_ring S]
    {J : ideal S} [algebra R S] :
    function.injective ⇑(algebra_map (quotient (comap (algebra_map R S) J)) (quotient J)) :=
  sorry

end ideal

namespace submodule

-- It is even a semialgebra. But those aren't in mathlib yet.

protected instance semimodule_submodule {R : Type u} {M : Type v} [comm_ring R] [add_comm_group M]
    [module R M] : semimodule (ideal R) (submodule R M) :=
  semimodule.mk sup_smul bot_smul

end submodule

namespace ring_hom

/-- `lift_of_surjective f hf g hg` is the unique ring homomorphism `φ`

* such that `φ.comp f = g` (`lift_of_surjective_comp`),
* where `f : A →+* B` is surjective (`hf`),
* and `g : B →+* C` satisfies `hg : f.ker ≤ g.ker`.

See `lift_of_surjective_eq` for the uniqueness lemma.

```
   A .
   |  \
 f |   \ g
   |    \
   v     \⌟
   B ----> C
      ∃!φ
```
 -/
def lift_of_surjective {A : Type u_1} {B : Type u_2} {C : Type u_3} [comm_ring A] [comm_ring B]
    [comm_ring C] (f : A →+* B) (hf : function.surjective ⇑f) (g : A →+* C) (hg : ker f ≤ ker g) :
    B →+* C :=
  mk (fun (b : B) => coe_fn g (classical.some (hf b))) sorry sorry sorry sorry

@[simp] theorem lift_of_surjective_comp_apply {A : Type u_1} {B : Type u_2} {C : Type u_3}
    [comm_ring A] [comm_ring B] [comm_ring C] (f : A →+* B) (hf : function.surjective ⇑f)
    (g : A →+* C) (hg : ker f ≤ ker g) (a : A) :
    coe_fn (lift_of_surjective f hf g hg) (coe_fn f a) = coe_fn g a :=
  add_monoid_hom.lift_of_surjective_comp_apply (to_add_monoid_hom f) hf (to_add_monoid_hom g) hg a

@[simp] theorem lift_of_surjective_comp {A : Type u_1} {B : Type u_2} {C : Type u_3} [comm_ring A]
    [comm_ring B] [comm_ring C] (f : A →+* B) (hf : function.surjective ⇑f) (g : A →+* C)
    (hg : ker f ≤ ker g) : comp (lift_of_surjective f hf g hg) f = g :=
  sorry

theorem eq_lift_of_surjective {A : Type u_1} {B : Type u_2} {C : Type u_3} [comm_ring A]
    [comm_ring B] [comm_ring C] (f : A →+* B) (hf : function.surjective ⇑f) (g : A →+* C)
    (hg : ker f ≤ ker g) (h : B →+* C) (hh : comp h f = g) : h = lift_of_surjective f hf g hg :=
  sorry

end Mathlib


import morphism.projector
import linear_algebra.basis
universes  u v w w'

variables {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V]
[finite_dimensional K V](B : fin 3 → V)(hv : is_basis K B)
/-!
    Goal : For a sub-vector space `W ≤ V`, construction of a projector `p : V →ₗ[K] V`  with `range p = V`.
        Strategy :
        we take a `basis of W` it a `linear_idependant familly of V`. We construct a `basis of V`
        witch complete. An we construct `p` on this basis
-/
open is_basis linear_map
example {K : Type} {V : Type} [field K] [add_comm_group V] [vector_space K V]
[finite_dimensional K V](B : fin 3 → V)(hv : is_basis K B) : true :=
begin
    let φ := constr hv B,  --- construction d'une applocation linear sur une base !
    have R : φ  = linear_map.id,
         apply ext hv,   --- extension sur une base
         intros,
         rw linear_map.id_apply,
         rw constr_basis,           --- expression sur la base de la construction !
        -- ici on va poser quelque question ?
   -- have G :  (linear_map.range φ) = ⊤,
   --     change linear_map.range (is_basis.constr hv B) = ⊤,
   -- have f : nonempty (fin 3),
    --    use 1,
   -- rw @constr_range _ _ _ _ _ _ _  _ _  _  f hv ,
   let θ : fin 3 → V := λ a, if a ≤ 1 then (B a) else 0,
   let φ := constr hv θ,
   have R : linear_map.comp φ φ = φ ,
   apply is_basis.ext hv,
   intro i,
   rw comp_apply,
   rw constr_basis,
        sorry,
   trivial,
 end
/-!
    Todo : matrice compagnon ... matrice cyclique matrice nilpotente ! etc. Y'a pas mal de chose a faire !
-/
variables (X : Type)[fintype X] [decidable_eq X] (x : X)
def Vr (x : X) :=  (λ i, std_basis K (λ i:X, K) i 1) x x
#check (λ i, std_basis K (λ i:X, K) i 1)
#check exists_linear_independent

lemma okok (x : X) :  (std_basis K (λ i:X, K) x 1) x = 1 := begin
     exact std_basis_same K (λ i, K) x 1,
end

def ε {X : Type u}[fintype X] [decidable_eq X](K : Type v)[field K] : X → X → K := λ x, (std_basis K (λ i:X, K) x 1)

#check (ε K)
def J : fin 3 → fin 3 := λ a, if a> 1 then 2 else 0
def classical_basis ( X : Type u)[fintype X][decidable_eq X](R : Type v)[comm_ring R] (x : X) : X → R :=
λ y : X, if x = y then 1 else 0

lemma obj ( X : Type u)[fintype X][decidable_eq X](R : Type v)[comm_ring R] (x : X) :
classical_basis X R x x = 1 := begin
    dsimp only [classical_basis]; split_ifs,
    exact rfl,
    exact rfl,
end


/-
Copyright (c) 2022 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import probability.variance

/-!
# Identically distributed random variables

Two random variables defined on two (possibly different) probability spaces but taking value in
the same space are *identically distributed* if their distributions (i.e., the image probability
measures on the target space) coincide. We define this concept and establish its basic properties
in this file.

## Main definitions and results

* `ident_distrib f g μ ν` registers that the image of `μ` under `f` coincides with the image of `ν`
  under `g` (and that `f` and `g` are almost everywhere measurable, as otherwise the image measures
  don't make sense). The measures can be kept implicit as in `ident_distrib f g` if the spaces
  are registered as measure spaces.
* `ident_distrib.comp`: being identically distributed is stable under composition with measurable
  maps.

There are two main kind of lemmas, under the assumption that `f` and `g` are identically
distributed: lemmas saying that two quantities computed for `f` and `g` are the same, and lemmas
saying that if `f` has some property then `g` also has it. The first kind is registered as
`ident_distrib.foo_eq`, the second one as `ident_distrib.foo_snd` (in the latter case, to deduce
a property of `f` from one of `g`, use `h.symm.foo_snd` where `h : ident_distrib f g μ ν`). For
instance:

* `ident_distrib.measure_mem_eq`: if `f` and `g` are identically distributed, then the probabilities
  that they belong to a given measurable set are the same.
* `ident_distrib.integral_eq`: if `f` and `g` are identically distributed, then their integrals
  are the same.
* `ident_distrib.variance_eq`: if `f` and `g` are identically distributed, then their variances
  are the same.

* `ident_distrib.ae_strongly_measurable_snd`: if `f` and `g` are identically distributed and `f`
  is almost everywhere strongly measurable, then so is `g`.
* `ident_distrib.mem_ℒp_snd`: if `f` and `g` are identically distributed and `f`
  belongs to `ℒp`, then so does `g`.

We also register several dot notation shortcuts for convenience.
For instance, if `h : ident_distrib f g μ ν`, then `h.sq` states that `f^2` and `g^2` are
identically distributed, and `h.norm` states that `∥f∥` and `∥g∥` are identically distributed, and
so on.
-/

open measure_theory filter finset

noncomputable theory

open_locale topological_space big_operators measure_theory ennreal nnreal

variables {α β γ δ : Type*} [measurable_space α] [measurable_space β]
  [measurable_space γ] [measurable_space δ]

namespace probability_theory

/-- Two functions defined on two (possibly different) measure spaces are identically distributed if
their image measures coincide. This only makes sense when the functions are ae measurable
(as otherwise the image measures are not defined), so we require this as well in the definition. -/
structure ident_distrib
  (f : α → γ) (g : β → γ) (μ : measure α . volume_tac) (ν : measure β . volume_tac) : Prop :=
(ae_measurable_fst : ae_measurable f μ)
(ae_measurable_snd : ae_measurable g ν)
(map_eq : measure.map f μ = measure.map g ν)

namespace ident_distrib

open topological_space

variables {μ : measure α} {ν : measure β} {f : α → γ} {g : β → γ}

protected lemma refl (hf : ae_measurable f μ) :
  ident_distrib f f μ μ :=
{ ae_measurable_fst := hf,
  ae_measurable_snd := hf,
  map_eq := rfl }

protected lemma symm (h : ident_distrib f g μ ν) : ident_distrib g f ν μ :=
{ ae_measurable_fst := h.ae_measurable_snd,
  ae_measurable_snd := h.ae_measurable_fst,
  map_eq := h.map_eq.symm }

protected lemma trans {ρ : measure δ} {h : δ → γ}
  (h₁ : ident_distrib f g μ ν) (h₂ : ident_distrib g h ν ρ) : ident_distrib f h μ ρ :=
{ ae_measurable_fst := h₁.ae_measurable_fst,
  ae_measurable_snd := h₂.ae_measurable_snd,
  map_eq := h₁.map_eq.trans h₂.map_eq }

protected lemma comp_of_ae_measurable {u : γ → δ} (h : ident_distrib f g μ ν)
  (hu : ae_measurable u (measure.map f μ)) :
  ident_distrib (u ∘ f) (u ∘ g) μ ν :=
{ ae_measurable_fst := hu.comp_ae_measurable h.ae_measurable_fst,
  ae_measurable_snd :=
    by { rw h.map_eq at hu, exact hu.comp_ae_measurable h.ae_measurable_snd },
  map_eq :=
  begin
    rw [← ae_measurable.map_map_of_ae_measurable hu h.ae_measurable_fst,
      ← ae_measurable.map_map_of_ae_measurable _ h.ae_measurable_snd, h.map_eq],
    rwa ← h.map_eq,
  end }

protected lemma comp {u : γ → δ} (h : ident_distrib f g μ ν) (hu : measurable u) :
  ident_distrib (u ∘ f) (u ∘ g) μ ν :=
h.comp_of_ae_measurable hu.ae_measurable

lemma measure_mem_eq (h : ident_distrib f g μ ν) {s : set γ} (hs : measurable_set s) :
  μ (f ⁻¹' s) = ν (g ⁻¹' s) :=
by rw [← measure.map_apply_of_ae_measurable h.ae_measurable_fst hs,
  ← measure.map_apply_of_ae_measurable h.ae_measurable_snd hs, h.map_eq]

alias measure_mem_eq ← probability_theory.ident_distrib.measure_preimage_eq

lemma ae_snd (h : ident_distrib f g μ ν) {p : γ → Prop}
  (pmeas : measurable_set {x | p x}) (hp : ∀ᵐ x ∂μ, p (f x)) :
   ∀ᵐ x ∂ν, p (g x) :=
begin
  apply (ae_map_iff h.ae_measurable_snd pmeas).1,
  rw ← h.map_eq,
  exact (ae_map_iff h.ae_measurable_fst pmeas).2 hp,
end

lemma ae_mem_snd (h : ident_distrib f g μ ν) {t : set γ}
  (tmeas : measurable_set t) (ht : ∀ᵐ x ∂μ, f x ∈ t) :
   ∀ᵐ x ∂ν, g x ∈ t :=
h.ae_snd tmeas ht

/-- In a second countable topology, the first function in an identically distributed pair is a.e.
strongly measurable. So is the second function, but use `h.symm.ae_strongly_measurable_fst` as
`h.ae_strongly_measurable_snd` has a different meaning.-/

/-- If `f` and `g` are identically distributed and `f` is a.e. strongly measurable, so is `g`. -/
lemma ae_strongly_measurable_snd [topological_space γ] [metrizable_space γ] [borel_space γ]
  (h : ident_distrib f g μ ν) (hf : ae_strongly_measurable f μ) :
  ae_strongly_measurable g ν :=
begin
  refine ae_strongly_measurable_iff_ae_measurable_separable.2 ⟨h.ae_measurable_snd, _⟩,
  rcases (ae_strongly_measurable_iff_ae_measurable_separable.1 hf).2 with ⟨t, t_sep, ht⟩,
  refine ⟨closure t, t_sep.closure, _⟩,
  apply h.ae_mem_snd is_closed_closure.measurable_set,
  filter_upwards [ht] with x hx using subset_closure hx,
end

lemma ae_strongly_measurable_iff [topological_space γ] [metrizable_space γ] [borel_space γ]
  (h : ident_distrib f g μ ν) :
  ae_strongly_measurable f μ ↔ ae_strongly_measurable g ν :=
⟨λ hf, h.ae_strongly_measurable_snd hf, λ hg, h.symm.ae_strongly_measurable_snd hg⟩

lemma ess_sup_eq [conditionally_complete_linear_order γ] [topological_space γ]
  [opens_measurable_space γ] [order_closed_topology γ] (h : ident_distrib f g μ ν) :
  ess_sup f μ = ess_sup g ν :=
begin
  have I : ∀ a, μ {x : α | a < f x} = ν {x : β | a < g x} :=
    λ a, h.measure_mem_eq measurable_set_Ioi,
  simp_rw [ess_sup_eq_Inf, I],
end

lemma lintegral_eq {f : α → ℝ≥0∞} {g : β → ℝ≥0∞} (h : ident_distrib f g μ ν) :
  ∫⁻ x, f x ∂μ = ∫⁻ x, g x ∂ν :=
begin
  change ∫⁻ x, id (f x) ∂μ = ∫⁻ x, id (g x) ∂ν,
  rw [← lintegral_map' ae_measurable_id h.ae_measurable_fst,
      ← lintegral_map' ae_measurable_id h.ae_measurable_snd, h.map_eq],
end

lemma integral_eq [normed_group γ] [normed_space ℝ γ] [complete_space γ] [borel_space γ]
  (h : ident_distrib f g μ ν) : ∫ x, f x ∂μ = ∫ x, g x ∂ν :=
begin
  by_cases hf : ae_strongly_measurable f μ,
  { have A : ae_strongly_measurable id (measure.map f μ),
    { rw ae_strongly_measurable_iff_ae_measurable_separable,
      rcases (ae_strongly_measurable_iff_ae_measurable_separable.1 hf).2 with ⟨t, t_sep, ht⟩,
      refine ⟨ae_measurable_id, ⟨closure t, t_sep.closure, _⟩⟩,
      rw ae_map_iff h.ae_measurable_fst,
      { filter_upwards [ht] with x hx using subset_closure hx },
      { exact is_closed_closure.measurable_set } },
    change ∫ x, id (f x) ∂μ = ∫ x, id (g x) ∂ν,
    rw [← integral_map h.ae_measurable_fst A],
    rw h.map_eq at A,
    rw [← integral_map h.ae_measurable_snd A, h.map_eq] },
  { rw integral_non_ae_strongly_measurable hf,
    rw h.ae_strongly_measurable_iff at hf,
    rw integral_non_ae_strongly_measurable hf }
end

lemma snorm_eq [normed_group γ] [opens_measurable_space γ] (h : ident_distrib f g μ ν) (p : ℝ≥0∞) :
  snorm f p μ = snorm g p ν :=
begin
  by_cases h0 : p = 0,
  { simp [h0], },
  by_cases h_top : p = ∞,
  { simp only [h_top, snorm, snorm_ess_sup, ennreal.top_ne_zero, eq_self_iff_true, if_true,
      if_false],
    apply ess_sup_eq,
    exact h.comp (measurable_coe_nnreal_ennreal.comp measurable_nnnorm) },
  simp only [snorm_eq_snorm' h0 h_top, snorm', one_div],
  congr' 1,
  apply lintegral_eq,
  exact h.comp
    (measurable.pow_const (measurable_coe_nnreal_ennreal.comp measurable_nnnorm) p.to_real),
end

lemma mem_ℒp_snd [normed_group γ] [borel_space γ]
  {p : ℝ≥0∞} (h : ident_distrib f g μ ν) (hf : mem_ℒp f p μ) :
  mem_ℒp g p ν :=
begin
  refine ⟨h.ae_strongly_measurable_snd hf.ae_strongly_measurable, _⟩,
  rw ← h.snorm_eq,
  exact hf.2
end

lemma mem_ℒp_iff [normed_group γ] [borel_space γ] {p : ℝ≥0∞} (h : ident_distrib f g μ ν) :
  mem_ℒp f p μ ↔ mem_ℒp g p ν :=
⟨λ hf, h.mem_ℒp_snd hf, λ hg, h.symm.mem_ℒp_snd hg⟩

lemma integrable_snd [normed_group γ] [borel_space γ] (h : ident_distrib f g μ ν)
  (hf : integrable f μ) : integrable g ν :=
begin
  rw ← mem_ℒp_one_iff_integrable at hf ⊢,
  exact h.mem_ℒp_snd hf
end

lemma integrable_iff [normed_group γ] [borel_space γ] (h : ident_distrib f g μ ν) :
  integrable f μ ↔ integrable g ν :=
⟨λ hf, h.integrable_snd hf, λ hg, h.symm.integrable_snd hg⟩

protected lemma norm [normed_group γ] [borel_space γ] (h : ident_distrib f g μ ν) :
  ident_distrib (λ x, ∥f x∥) (λ x, ∥g x∥) μ ν :=
h.comp measurable_norm

protected lemma nnnorm [normed_group γ] [borel_space γ] (h : ident_distrib f g μ ν) :
  ident_distrib (λ x, ∥f x∥₊) (λ x, ∥g x∥₊) μ ν :=
h.comp measurable_nnnorm

protected lemma pow [has_pow γ ℕ] [has_measurable_pow γ ℕ] (h : ident_distrib f g μ ν) {n : ℕ} :
  ident_distrib (λ x, (f x) ^ n) (λ x, (g x) ^ n) μ ν :=
h.comp (measurable_id.pow_const n)

protected lemma sq [has_pow γ ℕ] [has_measurable_pow γ ℕ] (h : ident_distrib f g μ ν) :
  ident_distrib (λ x, (f x) ^ 2) (λ x, (g x) ^ 2) μ ν :=
h.comp (measurable_id.pow_const 2)

protected lemma coe_nnreal_ennreal {f : α → ℝ≥0} {g : β → ℝ≥0} (h : ident_distrib f g μ ν) :
  ident_distrib (λ x, (f x : ℝ≥0∞)) (λ x, (g x : ℝ≥0∞)) μ ν :=
h.comp measurable_coe_nnreal_ennreal

@[to_additive]
lemma mul_const [has_mul γ] [has_measurable_mul γ] (h : ident_distrib f g μ ν) (c : γ) :
  ident_distrib (λ x, f x * c) (λ x, g x * c) μ ν :=
h.comp (measurable_mul_const c)

@[to_additive]
lemma const_mul [has_mul γ] [has_measurable_mul γ] (h : ident_distrib f g μ ν) (c : γ) :
  ident_distrib (λ x, c * f x) (λ x, c * g x) μ ν :=
h.comp (measurable_const_mul c)

@[to_additive]
lemma div_const [has_div γ] [has_measurable_div γ] (h : ident_distrib f g μ ν) (c : γ) :
  ident_distrib (λ x, f x / c) (λ x, g x / c) μ ν :=
h.comp (has_measurable_div.measurable_div_const c)

@[to_additive]
lemma const_div [has_div γ] [has_measurable_div γ] (h : ident_distrib f g μ ν) (c : γ) :
  ident_distrib (λ x, c / f x) (λ x, c / g x) μ ν :=
h.comp (has_measurable_div.measurable_const_div c)

lemma variance_eq {f : α → ℝ} {g : β → ℝ} (h : ident_distrib f g μ ν) :
  variance f μ = variance g ν :=
begin
  convert (h.sub_const (∫ x, f x ∂μ)).sq.integral_eq,
  rw h.integral_eq,
  refl
end

end ident_distrib

end probability_theory


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

/-!
# Bundled hom instances for module and multiplicative actions

This file defines instances for module, mul_action and related structures on bundled `_hom` types.

These are analogous to the instances in `algebra.module.pi`, but for bundled instead of unbundled
functions.
-/

variables {R S A B : Type*}

namespace add_monoid_hom

section
variables [monoid R] [monoid S] [add_monoid A] [add_comm_monoid B]
variables [distrib_mul_action R B] [distrib_mul_action S B]

instance : distrib_mul_action R (A →+ B) :=
{ smul := λ r f,
  { to_fun := r • f,
    map_zero' := by simp,
    map_add' := λ x y, by simp [smul_add] },
  one_smul := λ f, by simp,
  mul_smul := λ r s f, by simp [mul_smul],
  smul_add := λ r f g, ext $ λ x, by simp [smul_add],
  smul_zero := λ r, ext $ λ x, by simp [smul_zero] }

@[simp] lemma coe_smul (r : R) (f : A →+ B) : ⇑(r • f) = r • f := rfl
lemma smul_apply (r : R) (f : A →+ B) (x : A) : (r • f) x = r • f x := rfl

instance [smul_comm_class R S B] : smul_comm_class R S (A →+ B) :=
⟨λ a b f, ext $ λ x, smul_comm _ _ _⟩

instance [has_scalar R S] [is_scalar_tower R S B] : is_scalar_tower R S (A →+ B) :=
⟨λ a b f, ext $ λ x, smul_assoc _ _ _⟩

end

instance [semiring R] [add_monoid A] [add_comm_monoid B] [module R B] :
  module R (A →+ B) :=
{ add_smul := λ r s x, ext $ λ y, by simp [add_smul],
  zero_smul := λ x, ext $ λ y, by simp [zero_smul],
  ..add_monoid_hom.distrib_mul_action }

end add_monoid_hom


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro

Theory of topological monoids.

TODO: generalize `topological_monoid` and `topological_add_monoid` to semigroups, or add a type class
`topological_operator α (*)`.
-/
import topology.constructions

open classical set lattice filter topological_space
local attribute [instance] classical.prop_decidable

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}

section topological_monoid

/-- A topological monoid is a monoid in which the multiplication is continuous as a function
`α × α → α`. -/
class topological_monoid (α : Type u) [topological_space α] [monoid α] : Prop :=
(continuous_mul : continuous (λp:α×α, p.1 * p.2))

/-- A topological (additive) monoid is a monoid in which the addition is
  continuous as a function `α × α → α`. -/
class topological_add_monoid (α : Type u) [topological_space α] [add_monoid α] : Prop :=
(continuous_add : continuous (λp:α×α, p.1 + p.2))

attribute [to_additive topological_add_monoid] topological_monoid
attribute [to_additive topological_add_monoid.mk] topological_monoid.mk
attribute [to_additive topological_add_monoid.continuous_add] topological_monoid.continuous_mul

section
variables [topological_space α] [monoid α] [topological_monoid α]

@[to_additive continuous_add']
lemma continuous_mul' : continuous (λp:α×α, p.1 * p.2) :=
topological_monoid.continuous_mul α

@[to_additive continuous_add]
lemma continuous_mul [topological_space β] {f : β → α} {g : β → α}
  (hf : continuous f) (hg : continuous g) :
  continuous (λx, f x * g x) :=
(hf.prod_mk hg).comp continuous_mul'

-- @[to_additive continuous_smul]
lemma continuous_pow : ∀ n : ℕ, continuous (λ a : α, a ^ n)
| 0 := by simpa using continuous_const
| (k+1) := show continuous (λ (a : α), a * a ^ k), from continuous_mul continuous_id (continuous_pow _)

@[to_additive tendsto_add']
lemma tendsto_mul' {a b : α} : tendsto (λp:α×α, p.fst * p.snd) (nhds (a, b)) (nhds (a * b)) :=
continuous_iff_continuous_at.mp (topological_monoid.continuous_mul α) (a, b)

@[to_additive tendsto_add]
lemma tendsto_mul {f : β → α} {g : β → α} {x : filter β} {a b : α}
  (hf : tendsto f x (nhds a)) (hg : tendsto g x (nhds b)) :
  tendsto (λx, f x * g x) x (nhds (a * b)) :=
(hf.prod_mk hg).comp (by rw [←nhds_prod_eq]; exact tendsto_mul')

@[to_additive tendsto_list_sum]
lemma tendsto_list_prod {f : γ → β → α} {x : filter β} {a : γ → α} :
  ∀l:list γ, (∀c∈l, tendsto (f c) x (nhds (a c))) →
    tendsto (λb, (l.map (λc, f c b)).prod) x (nhds ((l.map a).prod))
| []       _ := by simp [tendsto_const_nhds]
| (f :: l) h :=
  begin
    simp,
    exact tendsto_mul
      (h f (list.mem_cons_self _ _))
      (tendsto_list_prod l (assume c hc, h c (list.mem_cons_of_mem _ hc)))
  end

@[to_additive continuous_list_sum]
lemma continuous_list_prod [topological_space β] {f : γ → β → α} (l : list γ)
  (h : ∀c∈l, continuous (f c)) :
  continuous (λa, (l.map (λc, f c a)).prod) :=
continuous_iff_continuous_at.2 $ assume x, tendsto_list_prod l $ assume c hc,
  continuous_iff_continuous_at.1 (h c hc) x

@[to_additive prod.topological_add_monoid]
instance [topological_space β] [monoid β] [topological_monoid β] : topological_monoid (α × β) :=
⟨continuous.prod_mk
  (continuous_mul (continuous_fst.comp continuous_fst) (continuous_snd.comp continuous_fst))
  (continuous_mul (continuous_fst.comp continuous_snd) (continuous_snd.comp continuous_snd)) ⟩

attribute [instance] prod.topological_add_monoid

end

section
variables [topological_space α] [comm_monoid α] [topological_monoid α]

@[to_additive tendsto_multiset_sum]
lemma tendsto_multiset_prod {f : γ → β → α} {x : filter β} {a : γ → α} (s : multiset γ) :
  (∀c∈s, tendsto (f c) x (nhds (a c))) →
    tendsto (λb, (s.map (λc, f c b)).prod) x (nhds ((s.map a).prod)) :=
by { rcases s with ⟨l⟩, simp, exact tendsto_list_prod l }

@[to_additive tendsto_finset_sum]
lemma tendsto_finset_prod {f : γ → β → α} {x : filter β} {a : γ → α} (s : finset γ) :
  (∀c∈s, tendsto (f c) x (nhds (a c))) → tendsto (λb, s.prod (λc, f c b)) x (nhds (s.prod a)) :=
tendsto_multiset_prod _

@[to_additive continuous_multiset_sum]
lemma continuous_multiset_prod [topological_space β] {f : γ → β → α} (s : multiset γ) :
  (∀c∈s, continuous (f c)) → continuous (λa, (s.map (λc, f c a)).prod) :=
by { rcases s with ⟨l⟩, simp, exact continuous_list_prod l }

@[to_additive continuous_finset_sum]
lemma continuous_finset_prod [topological_space β] {f : γ → β → α} (s : finset γ) :
  (∀c∈s, continuous (f c)) → continuous (λa, s.prod (λc, f c a)) :=
continuous_multiset_prod _

@[to_additive is_add_submonoid.mem_nhds_zero]
lemma is_submonoid.mem_nhds_one (β : set α) [is_submonoid β] (oβ : is_open β) :
  β ∈ nhds (1 : α) :=
mem_nhds_sets_iff.2 ⟨β, (by refl), oβ, is_submonoid.one_mem _⟩

end

end topological_monoid


/-
Copyright (c) 2020 Yury G. Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury G. Kudryashov
-/
import order.filter.basic
import data.set.countable

/-!
# Filters with countable intersection property

In this file we define `countable_Inter_filter` to be the class of filters with the following
property: for any countable collection of sets `s ∈ l` their intersection belongs to `l` as well.

Two main examples are the `residual` filter defined in `topology.metric_space.baire` and
the `measure.ae` filter defined in `measure_theory.measure_space`.
-/

open set filter
open_locale filter

variables {ι α : Type*}

/-- A filter `l` has the countable intersection property if for any countable collection
of sets `s ∈ l` their intersection belongs to `l` as well. -/
class countable_Inter_filter (l : filter α) : Prop :=
(countable_sInter_mem_sets' :
  ∀ {S : set (set α)} (hSc : countable S) (hS : ∀ s ∈ S, s ∈ l), ⋂₀ S ∈ l)

variables {l : filter α} [countable_Inter_filter l]

lemma countable_sInter_mem_sets {S : set (set α)} (hSc : countable S) :
  ⋂₀ S ∈ l ↔ ∀ s ∈ S, s ∈ l :=
⟨λ hS s hs, mem_sets_of_superset hS (sInter_subset_of_mem hs),
  countable_Inter_filter.countable_sInter_mem_sets' hSc⟩

lemma countable_Inter_mem_sets [encodable ι] {s : ι → set α} :
  (⋂ i, s i) ∈ l ↔ ∀ i, s i ∈ l :=
sInter_range s ▸ (countable_sInter_mem_sets (countable_range _)).trans forall_range_iff

lemma countable_bInter_mem_sets {S : set ι} (hS : countable S) {s : Π i ∈ S, set α} :
  (⋂ i ∈ S, s i ‹_›) ∈ l ↔  ∀ i ∈ S, s i ‹_› ∈ l :=
begin
  rw [bInter_eq_Inter],
  haveI := hS.to_encodable,
  exact countable_Inter_mem_sets.trans subtype.forall
end

lemma eventually_countable_forall [encodable ι] {p : α → ι → Prop} :
  (∀ᶠ x in l, ∀ i, p x i) ↔ ∀ i, ∀ᶠ x in l, p x i :=
by simpa only [filter.eventually, set_of_forall]
  using @countable_Inter_mem_sets _ _ l _ _ (λ i, {x | p x i})

lemma eventually_countable_ball {S : set ι} (hS : countable S) {p : Π (x : α) (i ∈ S), Prop} :
  (∀ᶠ x in l, ∀ i ∈ S, p x i ‹_›) ↔ ∀ i ∈ S, ∀ᶠ x in l, p x i ‹_› :=
by simpa only [filter.eventually, set_of_forall]
  using @countable_bInter_mem_sets _ _ l _ _ hS (λ i hi, {x | p x i hi})

lemma eventually_le.countable_Union [encodable ι] {s t : ι → set α} (h : ∀ i, s i ≤ᶠ[l] t i) :
  (⋃ i, s i) ≤ᶠ[l] ⋃ i, t i :=
(eventually_countable_forall.2 h).mono $ λ x hst hs, mem_Union.2 $
  (mem_Union.1 hs).imp hst

lemma eventually_eq.countable_Union [encodable ι] {s t : ι → set α} (h : ∀ i, s i =ᶠ[l] t i) :
  (⋃ i, s i) =ᶠ[l] ⋃ i, t i :=
(eventually_le.countable_Union (λ i, (h i).le)).antisymm
  (eventually_le.countable_Union (λ i, (h i).symm.le))

lemma eventually_le.countable_bUnion {S : set ι} (hS : countable S) {s t : Π i ∈ S, set α}
  (h : ∀ i ∈ S, s i ‹_› ≤ᶠ[l] t i ‹_›) : (⋃ i ∈ S, s i ‹_›) ≤ᶠ[l] ⋃ i ∈ S, t i ‹_› :=
begin
  simp only [bUnion_eq_Union],
  haveI := hS.to_encodable,
  exact eventually_le.countable_Union (λ i, h i i.2)
end

lemma eventually_eq.countable_bUnion {S : set ι} (hS : countable S) {s t : Π i ∈ S, set α}
  (h : ∀ i ∈ S, s i ‹_› =ᶠ[l] t i ‹_›) : (⋃ i ∈ S, s i ‹_›) =ᶠ[l] ⋃ i ∈ S, t i ‹_› :=
(eventually_le.countable_bUnion hS (λ i hi, (h i hi).le)).antisymm
  (eventually_le.countable_bUnion hS (λ i hi, (h i hi).symm.le))

lemma eventually_le.countable_Inter [encodable ι] {s t : ι → set α} (h : ∀ i, s i ≤ᶠ[l] t i) :
  (⋂ i, s i) ≤ᶠ[l] ⋂ i, t i :=
(eventually_countable_forall.2 h).mono $ λ x hst hs, mem_Inter.2 $ λ i, hst _ (mem_Inter.1 hs i)

lemma eventually_eq.countable_Inter [encodable ι] {s t : ι → set α} (h : ∀ i, s i =ᶠ[l] t i) :
  (⋂ i, s i) =ᶠ[l] ⋂ i, t i :=
(eventually_le.countable_Inter (λ i, (h i).le)).antisymm
  (eventually_le.countable_Inter (λ i, (h i).symm.le))

lemma eventually_le.countable_bInter {S : set ι} (hS : countable S) {s t : Π i ∈ S, set α}
  (h : ∀ i ∈ S, s i ‹_› ≤ᶠ[l] t i ‹_›) : (⋂ i ∈ S, s i ‹_›) ≤ᶠ[l] ⋂ i ∈ S, t i ‹_› :=
begin
  simp only [bInter_eq_Inter],
  haveI := hS.to_encodable,
  exact eventually_le.countable_Inter (λ i, h i i.2)
end

lemma eventually_eq.countable_bInter {S : set ι} (hS : countable S) {s t : Π i ∈ S, set α}
  (h : ∀ i ∈ S, s i ‹_› =ᶠ[l] t i ‹_›) : (⋂ i ∈ S, s i ‹_›) =ᶠ[l] ⋂ i ∈ S, t i ‹_› :=
(eventually_le.countable_bInter hS (λ i hi, (h i hi).le)).antisymm
  (eventually_le.countable_bInter hS (λ i hi, (h i hi).symm.le))

instance countable_Inter_filter_principal (s : set α) : countable_Inter_filter (𝓟 s) :=
⟨λ S hSc hS, subset_sInter hS⟩

instance countable_Inter_filter_bot : countable_Inter_filter (⊥ : filter α) :=
by { rw ← principal_empty, apply countable_Inter_filter_principal }

instance countable_Inter_filter_top : countable_Inter_filter (⊤ : filter α) :=
by { rw ← principal_univ, apply countable_Inter_filter_principal }

/-- Infimum of two `countable_Inter_filter`s is a `countable_Inter_filter`. This is useful, e.g.,
to automatically get an instance for `residual α ⊓ 𝓟 s`. -/
instance countable_Inter_filter_inf (l₁ l₂ : filter α) [countable_Inter_filter l₁]
  [countable_Inter_filter l₂] :
  countable_Inter_filter (l₁ ⊓ l₂) :=
begin
  refine ⟨λ S hSc hS, _⟩,
  choose s hs t ht hst using hS,
  replace hs : (⋂ i ∈ S, s i ‹_›) ∈ l₁ := (countable_bInter_mem_sets hSc).2 hs,
  replace ht : (⋂ i ∈ S, t i ‹_›) ∈ l₂ := (countable_bInter_mem_sets hSc).2 ht,
  refine mem_sets_of_superset (inter_mem_inf_sets hs ht) (subset_sInter $ λ i hi, _),
  refine subset.trans (inter_subset_inter _ _) (hst i hi);
    exact Inter_subset_of_subset i (Inter_subset _ _)
end

/-- Supremum of two `countable_Inter_filter`s is a `countable_Inter_filter`. -/
instance countable_Inter_filter_sup (l₁ l₂ : filter α) [countable_Inter_filter l₁]
  [countable_Inter_filter l₂] :
  countable_Inter_filter (l₁ ⊔ l₂) :=
begin
  refine ⟨λ S hSc hS, ⟨_, _⟩⟩; refine (countable_sInter_mem_sets hSc).2 (λ s hs, _),
  exacts [(hS s hs).1, (hS s hs).2]
end


def f (x : Nat) : Nat :=
match x with
| 30  => 31
| y+1 => y
| 0   => 10

#eval f 20
#eval f 0
#eval f 30

universe u

theorem ex1 {α : Sort u} {a b : α} (h : a ≅ b) : a = b :=
match α, a, b, h with
| _, _, _, HEq.refl _ => rfl

theorem ex2 {α : Sort u} {a b : α} (h : a ≅ b) : a = b :=
match a, b, h with
| _, _, HEq.refl _ => rfl

theorem ex3 {α : Sort u} {a b : α} (h : a ≅ b) : a = b :=
match b, h with
| _, HEq.refl _ => rfl

theorem ex4  {α β : Sort u} {b : β} {a a' : α} (h₁ : a = a') (h₂ : a' ≅ b) : a ≅ b :=
match β, a', b, h₁, h₂ with
| _, _, _, rfl, HEq.refl _ => HEq.refl _

theorem ex5  {α β : Sort u} {b : β} {a a' : α} (h₁ : a = a') (h₂ : a' ≅ b) : a ≅ b :=
match a', h₁, h₂ with
| _, rfl, h₂ => h₂

theorem ex6  {α β : Sort u} {b : β} {a a' : α} (h₁ : a = a') (h₂ : a' ≅ b) : a ≅ b :=
by {
  subst h₁;
  assumption
}

theorem ex7 (a : Bool) (p q : Prop) (h₁ : a = true → p) (h₂ : a = false → q) : p ∨ q :=
  match (generalizing := false) h:a with
  | true  => Or.inl $ h₁ h
  | false => Or.inr $ h₂ h

theorem ex7' (a : Bool) (p q : Prop) (h₁ : a = true → p) (h₂ : a = false → q) : p ∨ q :=
  match a with
  | true  => Or.inl $ h₁ rfl
  | false => Or.inr $ h₂ rfl

def head {α} (xs : List α) (h : xs = [] → False) : α :=
  match he:xs with
  | []   => by contradiction
  | x::_ => x

variable {α : Type u} {p : α → Prop}

theorem ex8 {a1 a2 : {x // p x}} (h : a1.val = a2.val) : a1 = a2 :=
match a1, a2, h with
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl

universe v
variable {β : α → Type v}

theorem ex9 {p₁ p₂ : Sigma (fun a => β a)} (h₁ : p₁.1 = p₂.1) (h : p₁.2 ≅ p₂.2) : p₁ = p₂ :=
match p₁, p₂, h₁, h with
| ⟨_, _⟩, ⟨_, _⟩, rfl, HEq.refl _ => rfl

inductive F : Nat → Type
| z : {n : Nat} → F (n+1)
| s : {n : Nat} → F n → F (n+1)

def f0 {α : Sort u} (x : F 0) : α :=
nomatch x

def f0' {α : Sort u} (x : F 0) : α :=
nomatch id x

def f1 {α : Sort u} (x : F 0 × Bool) : α :=
nomatch x

def f2 {α : Sort u} (x : Sum (F 0) (F 0)) : α :=
nomatch x

def f3 {α : Sort u} (x : Bool × F 0) : α :=
nomatch x

def f4 (x : Sum (F 0 × Bool) Nat) : Nat :=
match x with
| Sum.inr x => x

#eval f4 $ Sum.inr 100