/-
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
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.basic
import Mathlib.PostPort

universes u v w

namespace Mathlib

/-!
# Results about big operators with values in an ordered algebraic structure.

Mostly monotonicity results for the `∑` operation.

-/

namespace finset

theorem le_sum_of_subadditive {α : Type u} {β : Type v} {γ : Type w} [add_comm_monoid α]
    [ordered_add_comm_monoid β] (f : α → β) (h_zero : f 0 = 0)
    (h_add : ∀ (x y : α), f (x + y) ≤ f x + f y) (s : finset γ) (g : γ → α) :
    f (finset.sum s fun (x : γ) => g x) ≤ finset.sum s fun (x : γ) => f (g x) :=
  sorry

theorem abs_sum_le_sum_abs {α : Type u} {β : Type v} [linear_ordered_field α] {f : β → α}
    {s : finset β} :
    abs (finset.sum s fun (x : β) => f x) ≤ finset.sum s fun (x : β) => abs (f x) :=
  le_sum_of_subadditive abs abs_zero abs_add s f

theorem abs_prod {α : Type u} {β : Type v} [linear_ordered_comm_ring α] {f : β → α} {s : finset β} :
    abs (finset.prod s fun (x : β) => f x) = finset.prod s fun (x : β) => abs (f x) :=
  monoid_hom.map_prod (monoid_with_zero_hom.to_monoid_hom abs_hom) (fun (x : β) => f x) s

theorem sum_le_sum {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β}
    [ordered_add_comm_monoid β] :
    (∀ (x : α), x ∈ s → f x ≤ g x) →
        (finset.sum s fun (x : α) => f x) ≤ finset.sum s fun (x : α) => g x :=
  sorry

theorem card_le_mul_card_image_of_maps_to {α : Type u} {γ : Type w} [DecidableEq γ] {f : α → γ}
    {s : finset α} {t : finset γ} (Hf : ∀ (a : α), a ∈ s → f a ∈ t) (n : ℕ)
    (hn : ∀ (a : γ), a ∈ t → card (filter (fun (x : α) => f x = a) s) ≤ n) : card s ≤ n * card t :=
  sorry

theorem card_le_mul_card_image {α : Type u} {γ : Type w} [DecidableEq γ] {f : α → γ} (s : finset α)
    (n : ℕ) (hn : ∀ (a : γ), a ∈ image f s → card (filter (fun (x : α) => f x = a) s) ≤ n) :
    card s ≤ n * card (image f s) :=
  card_le_mul_card_image_of_maps_to (fun (x : α) => mem_image_of_mem f) n hn

theorem mul_card_image_le_card_of_maps_to {α : Type u} {γ : Type w} [DecidableEq γ] {f : α → γ}
    {s : finset α} {t : finset γ} (Hf : ∀ (a : α), a ∈ s → f a ∈ t) (n : ℕ)
    (hn : ∀ (a : γ), a ∈ t → n ≤ card (filter (fun (x : α) => f x = a) s)) : n * card t ≤ card s :=
  sorry

theorem mul_card_image_le_card {α : Type u} {γ : Type w} [DecidableEq γ] {f : α → γ} (s : finset α)
    (n : ℕ) (hn : ∀ (a : γ), a ∈ image f s → n ≤ card (filter (fun (x : α) => f x = a) s)) :
    n * card (image f s) ≤ card s :=
  mul_card_image_le_card_of_maps_to (fun (x : α) => mem_image_of_mem f) n hn

theorem sum_nonneg {α : Type u} {β : Type v} {s : finset α} {f : α → β} [ordered_add_comm_monoid β]
    (h : ∀ (x : α), x ∈ s → 0 ≤ f x) : 0 ≤ finset.sum s fun (x : α) => f x :=
  le_trans
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ finset.sum s fun (x : α) => 0)) sum_const_zero))
      (le_refl 0))
    (sum_le_sum h)

theorem sum_nonpos {α : Type u} {β : Type v} {s : finset α} {f : α → β} [ordered_add_comm_monoid β]
    (h : ∀ (x : α), x ∈ s → f x ≤ 0) : (finset.sum s fun (x : α) => f x) ≤ 0 :=
  le_trans (sum_le_sum h)
    (eq.mpr (id (Eq._oldrec (Eq.refl ((finset.sum s fun (x : α) => 0) ≤ 0)) sum_const_zero))
      (le_refl 0))

theorem sum_le_sum_of_subset_of_nonneg {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α}
    {f : α → β} [ordered_add_comm_monoid β] (h : s₁ ⊆ s₂)
    (hf : ∀ (x : α), x ∈ s₂ → ¬x ∈ s₁ → 0 ≤ f x) :
    (finset.sum s₁ fun (x : α) => f x) ≤ finset.sum s₂ fun (x : α) => f x :=
  sorry

theorem sum_mono_set_of_nonneg {α : Type u} {β : Type v} {f : α → β} [ordered_add_comm_monoid β]
    (hf : ∀ (x : α), 0 ≤ f x) : monotone fun (s : finset α) => finset.sum s fun (x : α) => f x :=
  fun (s₁ s₂ : finset α) (hs : s₁ ≤ s₂) =>
    sum_le_sum_of_subset_of_nonneg hs fun (x : α) (_x : x ∈ s₂) (_x : ¬x ∈ s₁) => hf x

theorem sum_fiberwise_le_sum_of_sum_fiber_nonneg {α : Type u} {β : Type v} {γ : Type w}
    [ordered_add_comm_monoid β] [DecidableEq γ] {s : finset α} {t : finset γ} {g : α → γ}
    {f : α → β}
    (h :
      ∀ (y : γ), ¬y ∈ t → 0 ≤ finset.sum (filter (fun (x : α) => g x = y) s) fun (x : α) => f x) :
    (finset.sum t
          fun (y : γ) => finset.sum (filter (fun (x : α) => g x = y) s) fun (x : α) => f x) ≤
        finset.sum s fun (x : α) => f x :=
  sorry

theorem sum_le_sum_fiberwise_of_sum_fiber_nonpos {α : Type u} {β : Type v} {γ : Type w}
    [ordered_add_comm_monoid β] [DecidableEq γ] {s : finset α} {t : finset γ} {g : α → γ}
    {f : α → β}
    (h :
      ∀ (y : γ), ¬y ∈ t → (finset.sum (filter (fun (x : α) => g x = y) s) fun (x : α) => f x) ≤ 0) :
    (finset.sum s fun (x : α) => f x) ≤
        finset.sum t
          fun (y : γ) => finset.sum (filter (fun (x : α) => g x = y) s) fun (x : α) => f x :=
  sum_fiberwise_le_sum_of_sum_fiber_nonneg h

theorem sum_eq_zero_iff_of_nonneg {α : Type u} {β : Type v} {s : finset α} {f : α → β}
    [ordered_add_comm_monoid β] :
    (∀ (x : α), x ∈ s → 0 ≤ f x) →
        ((finset.sum s fun (x : α) => f x) = 0 ↔ ∀ (x : α), x ∈ s → f x = 0) :=
  sorry

theorem sum_eq_zero_iff_of_nonpos {α : Type u} {β : Type v} {s : finset α} {f : α → β}
    [ordered_add_comm_monoid β] :
    (∀ (x : α), x ∈ s → f x ≤ 0) →
        ((finset.sum s fun (x : α) => f x) = 0 ↔ ∀ (x : α), x ∈ s → f x = 0) :=
  sum_eq_zero_iff_of_nonneg

theorem single_le_sum {α : Type u} {β : Type v} {s : finset α} {f : α → β}
    [ordered_add_comm_monoid β] (hf : ∀ (x : α), x ∈ s → 0 ≤ f x) {a : α} (h : a ∈ s) :
    f a ≤ finset.sum s fun (x : α) => f x :=
  sorry

@[simp] theorem sum_eq_zero_iff {α : Type u} {β : Type v} {s : finset α} {f : α → β}
    [canonically_ordered_add_monoid β] :
    (finset.sum s fun (x : α) => f x) = 0 ↔ ∀ (x : α), x ∈ s → f x = 0 :=
  sum_eq_zero_iff_of_nonneg fun (x : α) (hx : x ∈ s) => zero_le (f x)

theorem sum_le_sum_of_subset {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β}
    [canonically_ordered_add_monoid β] (h : s₁ ⊆ s₂) :
    (finset.sum s₁ fun (x : α) => f x) ≤ finset.sum s₂ fun (x : α) => f x :=
  sum_le_sum_of_subset_of_nonneg h fun (x : α) (h₁ : x ∈ s₂) (h₂ : ¬x ∈ s₁) => zero_le (f x)

theorem sum_mono_set {α : Type u} {β : Type v} [canonically_ordered_add_monoid β] (f : α → β) :
    monotone fun (s : finset α) => finset.sum s fun (x : α) => f x :=
  fun (s₁ s₂ : finset α) (hs : s₁ ≤ s₂) => sum_le_sum_of_subset hs

theorem sum_le_sum_of_ne_zero {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β}
    [canonically_ordered_add_monoid β] (h : ∀ (x : α), x ∈ s₁ → f x ≠ 0 → x ∈ s₂) :
    (finset.sum s₁ fun (x : α) => f x) ≤ finset.sum s₂ fun (x : α) => f x :=
  sorry

theorem sum_lt_sum {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β}
    [ordered_cancel_add_comm_monoid β] (Hle : ∀ (i : α), i ∈ s → f i ≤ g i)
    (Hlt : ∃ (i : α), ∃ (H : i ∈ s), f i < g i) :
    (finset.sum s fun (x : α) => f x) < finset.sum s fun (x : α) => g x :=
  sorry

theorem sum_lt_sum_of_nonempty {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β}
    [ordered_cancel_add_comm_monoid β] (hs : finset.nonempty s)
    (Hlt : ∀ (x : α), x ∈ s → f x < g x) :
    (finset.sum s fun (x : α) => f x) < finset.sum s fun (x : α) => g x :=
  sum_lt_sum (fun (i : α) (hi : i ∈ s) => le_of_lt (Hlt i hi))
    (Exists.dcases_on hs fun (i : α) (hi : i ∈ s) => Exists.intro i (Exists.intro hi (Hlt i hi)))

theorem sum_lt_sum_of_subset {α : Type u} {β : Type v} {s₁ : finset α} {s₂ : finset α} {f : α → β}
    [ordered_cancel_add_comm_monoid β] [DecidableEq α] (h : s₁ ⊆ s₂) {i : α} (hi : i ∈ s₂ \ s₁)
    (hpos : 0 < f i) (hnonneg : ∀ (j : α), j ∈ s₂ \ s₁ → 0 ≤ f j) :
    (finset.sum s₁ fun (x : α) => f x) < finset.sum s₂ fun (x : α) => f x :=
  sorry

theorem exists_lt_of_sum_lt {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β}
    [linear_ordered_cancel_add_comm_monoid β]
    (Hlt : (finset.sum s fun (x : α) => f x) < finset.sum s fun (x : α) => g x) :
    ∃ (i : α), ∃ (H : i ∈ s), f i < g i :=
  sorry

theorem exists_le_of_sum_le {α : Type u} {β : Type v} {s : finset α} {f : α → β} {g : α → β}
    [linear_ordered_cancel_add_comm_monoid β] (hs : finset.nonempty s)
    (Hle : (finset.sum s fun (x : α) => f x) ≤ finset.sum s fun (x : α) => g x) :
    ∃ (i : α), ∃ (H : i ∈ s), f i ≤ g i :=
  sorry

theorem exists_pos_of_sum_zero_of_exists_nonzero {α : Type u} {β : Type v} {s : finset α}
    [linear_ordered_cancel_add_comm_monoid β] (f : α → β)
    (h₁ : (finset.sum s fun (e : α) => f e) = 0) (h₂ : ∃ (x : α), ∃ (H : x ∈ s), f x ≠ 0) :
    ∃ (x : α), ∃ (H : x ∈ s), 0 < f x :=
  sorry

/- this is also true for a ordered commutative multiplicative monoid -/

theorem prod_nonneg {α : Type u} {β : Type v} [linear_ordered_comm_ring β] {s : finset α}
    {f : α → β} (h0 : ∀ (x : α), x ∈ s → 0 ≤ f x) : 0 ≤ finset.prod s fun (x : α) => f x :=
  prod_induction f (fun (x : β) => 0 ≤ x)
    (fun (_x _x_1 : β) (ha : 0 ≤ _x) (hb : 0 ≤ _x_1) => mul_nonneg ha hb) zero_le_one h0

/- this is also true for a ordered commutative multiplicative monoid -/

theorem prod_pos {α : Type u} {β : Type v} [linear_ordered_comm_ring β] {s : finset α} {f : α → β}
    (h0 : ∀ (x : α), x ∈ s → 0 < f x) : 0 < finset.prod s fun (x : α) => f x :=
  prod_induction f (fun (x : β) => 0 < x)
    (fun (_x _x_1 : β) (ha : 0 < _x) (hb : 0 < _x_1) => mul_pos ha hb) zero_lt_one h0

/- this is also true for a ordered commutative multiplicative monoid -/

theorem prod_le_prod {α : Type u} {β : Type v} [linear_ordered_comm_ring β] {s : finset α}
    {f : α → β} {g : α → β} (h0 : ∀ (x : α), x ∈ s → 0 ≤ f x) (h1 : ∀ (x : α), x ∈ s → f x ≤ g x) :
    (finset.prod s fun (x : α) => f x) ≤ finset.prod s fun (x : α) => g x :=
  sorry

theorem prod_le_one {α : Type u} {β : Type v} [linear_ordered_comm_ring β] {s : finset α}
    {f : α → β} (h0 : ∀ (x : α), x ∈ s → 0 ≤ f x) (h1 : ∀ (x : α), x ∈ s → f x ≤ 1) :
    (finset.prod s fun (x : α) => f x) ≤ 1 :=
  sorry

/-- If `g, h ≤ f` and `g i + h i ≤ f i`, then the product of `f` over `s` is at least the
  sum of the products of `g` and `h`. This is the version for `linear_ordered_comm_ring`. -/
theorem prod_add_prod_le {α : Type u} {β : Type v} [linear_ordered_comm_ring β] {s : finset α}
    {i : α} {f : α → β} {g : α → β} {h : α → β} (hi : i ∈ s) (h2i : g i + h i ≤ f i)
    (hgf : ∀ (j : α), j ∈ s → j ≠ i → g j ≤ f j) (hhf : ∀ (j : α), j ∈ s → j ≠ i → h j ≤ f j)
    (hg : ∀ (i : α), i ∈ s → 0 ≤ g i) (hh : ∀ (i : α), i ∈ s → 0 ≤ h i) :
    ((finset.prod s fun (i : α) => g i) + finset.prod s fun (i : α) => h i) ≤
        finset.prod s fun (i : α) => f i :=
  sorry

theorem prod_le_prod' {α : Type u} {β : Type v} [canonically_ordered_comm_semiring β] {s : finset α}
    {f : α → β} {g : α → β} (h : ∀ (i : α), i ∈ s → f i ≤ g i) :
    (finset.prod s fun (x : α) => f x) ≤ finset.prod s fun (x : α) => g x :=
  sorry

/-- If `g, h ≤ f` and `g i + h i ≤ f i`, then the product of `f` over `s` is at least the
  sum of the products of `g` and `h`. This is the version for `canonically_ordered_comm_semiring`.
-/
theorem prod_add_prod_le' {α : Type u} {β : Type v} [canonically_ordered_comm_semiring β]
    {s : finset α} {i : α} {f : α → β} {g : α → β} {h : α → β} (hi : i ∈ s) (h2i : g i + h i ≤ f i)
    (hgf : ∀ (j : α), j ∈ s → j ≠ i → g j ≤ f j) (hhf : ∀ (j : α), j ∈ s → j ≠ i → h j ≤ f j) :
    ((finset.prod s fun (i : α) => g i) + finset.prod s fun (i : α) => h i) ≤
        finset.prod s fun (i : α) => f i :=
  sorry

end finset

namespace with_top

/-- A product of finite numbers is still finite -/
theorem prod_lt_top {α : Type u} {β : Type v} [canonically_ordered_comm_semiring β] [nontrivial β]
    [DecidableEq β] {s : finset α} {f : α → with_top β} (h : ∀ (a : α), a ∈ s → f a < ⊤) :
    (finset.prod s fun (x : α) => f x) < ⊤ :=
  finset.prod_induction f (fun (a : with_top β) => a < ⊤) (fun (a b : with_top β) => mul_lt_top)
    (coe_lt_top 1) h

/-- A sum of finite numbers is still finite -/
theorem sum_lt_top {α : Type u} {β : Type v} [ordered_add_comm_monoid β] {s : finset α}
    {f : α → with_top β} : (∀ (a : α), a ∈ s → f a < ⊤) → (finset.sum s fun (x : α) => f x) < ⊤ :=
  sorry

/-- A sum of finite numbers is still finite -/
theorem sum_lt_top_iff {α : Type u} {β : Type v} [canonically_ordered_add_monoid β] {s : finset α}
    {f : α → with_top β} : (finset.sum s fun (x : α) => f x) < ⊤ ↔ ∀ (a : α), a ∈ s → f a < ⊤ :=
  sorry

/-- A sum of numbers is infinite iff one of them is infinite -/
theorem sum_eq_top_iff {α : Type u} {β : Type v} [canonically_ordered_add_monoid β] {s : finset α}
    {f : α → with_top β} :
    (finset.sum s fun (x : α) => f x) = ⊤ ↔ ∃ (a : α), ∃ (H : a ∈ s), f a = ⊤ :=
  sorry

end Mathlib


/-
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, Yury Kudryashov

! This file was ported from Lean 3 source module topology.algebra.order.extend_from
! leanprover-community/mathlib commit 0a0ec35061ed9960bf0e7ffb0335f44447b58977
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.Order.Basic
import Mathlib.Topology.ExtendFrom

/-!
# Lemmas about `extendFrom` in an order topology.
-/

open Filter Set TopologicalSpace

open Topology Classical

theorem continuousOn_Icc_extendFrom_Ioo [TopologicalSpace α] [LinearOrder α] [DenselyOrdered α]
    [OrderTopology α] [TopologicalSpace β] [RegularSpace β] {f : α → β} {a b : α} {la lb : β}
    (hab : a ≠ b) (hf : ContinuousOn f (Ioo a b)) (ha : Tendsto f (𝓝[>] a) (𝓝 la))
    (hb : Tendsto f (𝓝[<] b) (𝓝 lb)) : ContinuousOn (extendFrom (Ioo a b) f) (Icc a b) := by
  apply continuousOn_extendFrom
  · rw [closure_Ioo hab]
  · intro x x_in
    rcases eq_endpoints_or_mem_Ioo_of_mem_Icc x_in with (rfl | rfl | h)
    · exact ⟨la, ha.mono_left <| nhdsWithin_mono _ Ioo_subset_Ioi_self⟩
    · exact ⟨lb, hb.mono_left <| nhdsWithin_mono _ Ioo_subset_Iio_self⟩
    · exact ⟨f x, hf x h⟩
#align continuous_on_Icc_extend_from_Ioo continuousOn_Icc_extendFrom_Ioo

theorem eq_lim_at_left_extendFrom_Ioo [TopologicalSpace α] [LinearOrder α] [DenselyOrdered α]
    [OrderTopology α] [TopologicalSpace β] [T2Space β] {f : α → β} {a b : α} {la : β} (hab : a < b)
    (ha : Tendsto f (𝓝[>] a) (𝓝 la)) : extendFrom (Ioo a b) f a = la := by
  apply extendFrom_eq
  · rw [closure_Ioo hab.ne]
    simp only [le_of_lt hab, left_mem_Icc, right_mem_Icc]
  · simpa [hab]
#align eq_lim_at_left_extend_from_Ioo eq_lim_at_left_extendFrom_Ioo

theorem eq_lim_at_right_extendFrom_Ioo [TopologicalSpace α] [LinearOrder α] [DenselyOrdered α]
    [OrderTopology α] [TopologicalSpace β] [T2Space β] {f : α → β} {a b : α} {lb : β} (hab : a < b)
    (hb : Tendsto f (𝓝[<] b) (𝓝 lb)) : extendFrom (Ioo a b) f b = lb := by
  apply extendFrom_eq
  · rw [closure_Ioo hab.ne]
    simp only [le_of_lt hab, left_mem_Icc, right_mem_Icc]
  · simpa [hab]
#align eq_lim_at_right_extend_from_Ioo eq_lim_at_right_extendFrom_Ioo

theorem continuousOn_Ico_extendFrom_Ioo [TopologicalSpace α] [LinearOrder α] [DenselyOrdered α]
    [OrderTopology α] [TopologicalSpace β] [RegularSpace β] {f : α → β} {a b : α} {la : β}
    (hab : a < b) (hf : ContinuousOn f (Ioo a b)) (ha : Tendsto f (𝓝[>] a) (𝓝 la)) :
    ContinuousOn (extendFrom (Ioo a b) f) (Ico a b) := by
  apply continuousOn_extendFrom
  · rw [closure_Ioo hab.ne]
    exact Ico_subset_Icc_self
  · intro x x_in
    rcases eq_left_or_mem_Ioo_of_mem_Ico x_in with (rfl | h)
    · use la
      simpa [hab]
    · exact ⟨f x, hf x h⟩
#align continuous_on_Ico_extend_from_Ioo continuousOn_Ico_extendFrom_Ioo

theorem continuousOn_Ioc_extendFrom_Ioo [TopologicalSpace α] [LinearOrder α] [DenselyOrdered α]
    [OrderTopology α] [TopologicalSpace β] [RegularSpace β] {f : α → β} {a b : α} {lb : β}
    (hab : a < b) (hf : ContinuousOn f (Ioo a b)) (hb : Tendsto f (𝓝[<] b) (𝓝 lb)) :
    ContinuousOn (extendFrom (Ioo a b) f) (Ioc a b) := by
  have := @continuousOn_Ico_extendFrom_Ioo αᵒᵈ _ _ _ _ _ _ _ f _ _ lb hab
  erw [dual_Ico, dual_Ioi, dual_Ioo] at this
  exact this hf hb
#align continuous_on_Ioc_extend_from_Ioo continuousOn_Ioc_extendFrom_Ioo


import algebra.module
import algebra.punit_instances
import category_theory.types
import linear_algebra.basic

universe u

open category_theory

variables (R : Type u) [ring R]

/-- An example of a module for any ring is the zero module -/
instance zero_module : module R punit := module.of_core $
  by refine {
    smul := λ _ _, punit.star,
    .. punit.comm_ring, ..};
    intros; exact subsingleton.elim _ _

/-- The category of R-modules and their morphisms. -/
structure Module :=
  (carrier : Type)
  (prop_add_comm_group : add_comm_group carrier)
  (prop_module : module R carrier)

lemma id_is_linear (M : Type) [add_comm_group M] [module R M] : is_linear_map R (@id M) := {
  add := λ x y,  (by repeat {apply id.def}),
  smul := λ c x, (by repeat {apply id.def}),
}

lemma linear_maps_comp (A B C : Type) [add_comm_group A] [module R A] [add_comm_group B] [module R B] [add_comm_group C] [module R C]
   (f : A → B) (g : B → C) (h₁ : is_linear_map R f) (h₂ : is_linear_map R g)
  : is_linear_map R (g ∘ f) := {
  add := λ x y,  (by simp; rw is_linear_map.add R f; rw is_linear_map.add R g),
  smul := λ c x, (by simp; rw is_linear_map.smul f c; rw is_linear_map.smul g c)
}

namespace Module
  instance : has_coe_to_sort (Module R) :=
    { S := Type, coe := Module.carrier}
end Module

instance Module_add_comm_group (M: Module R) : add_comm_group M := M.prop_add_comm_group
instance Module_R_module (M: Module R) : module R M := M.prop_module

namespace Module
  def of (X : Type) [h₁ : add_comm_group X] [h₂ : module R X] : Module R := ⟨ X , h₁ , h₂⟩

  instance : has_zero (Module R) := ⟨ of R punit ⟩

  variables (M N U : Module R)

  instance : category (Module R) := {
    hom := λ M N, M →ₗ[R] N,
    id := λ M, 1 ,
    comp := λ A B C f g, g.comp f ,
  }

  @[simp] lemma module_id : linear_map.to_fun (𝟙 M) = id := rfl

  @[simp] lemma module_hom_comp (f : M ⟶ N) (g : N ⟶ U) :
    ((f ≫ g) : M → U) = g.to_fun ∘ f.to_fun := rfl

  instance : has_coe_to_fun (M ⟶ N) :=
    { F   := λ f, M → N,
      coe := λ f, (f : M → N) }

  @[extensionality] lemma hom_ext  {f g : M ⟶ N} : (∀ x : M, f x = g x) → f = g :=
    λ w, linear_map.ext w

  @[extensionality] lemma hom_ext'  {f g : M ⟶ N} : (f : M → N) = g → f = g :=
    λ w, hom_ext R M N (function.funext_iff.1 w)

  @[simp] lemma coe_id {M : Module R} : ((𝟙 M) : M → M) = id := rfl

  instance hom_is_module_hom {M₁ M₂ : Module R} (f : M₁ ⟶ M₂) :
    is_linear_map R (f : M₁ → M₂) := linear_map.is_linear _

end Module

instance (M : Type) [add_comm_group M] [module R M] : has_coe (submodule R M) (Module R) := ⟨ λ N, Module.of R N ⟩

def are_isomorphic {R} [ring R] (M₁ M₂ : Module R) := nonempty (M₁ ≅ M₂)


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

import data.nat.cast.field
import algebra.group_power.lemmas

/-!
# Characteristic zero (additional theorems)

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

A ring `R` is called of characteristic zero if every natural number `n` is non-zero when considered
as an element of `R`. Since this definition doesn't mention the multiplicative structure of `R`
except for the existence of `1` in this file characteristic zero is defined for additive monoids
with `1`.

## Main statements

* Characteristic zero implies that the additive monoid is infinite.
-/

namespace nat
variables {R : Type*} [add_monoid_with_one R] [char_zero R]

/-- `nat.cast` as an embedding into monoids of characteristic `0`. -/
@[simps]
def cast_embedding : ℕ ↪ R := ⟨coe, cast_injective⟩

@[simp] lemma cast_pow_eq_one {R : Type*} [semiring R] [char_zero R] (q : ℕ) (n : ℕ) (hn : n ≠ 0) :
  (q : R) ^ n = 1 ↔ q = 1 :=
by { rw [←cast_pow, cast_eq_one], exact pow_eq_one_iff hn }

@[simp, norm_cast]
theorem cast_div_char_zero {k : Type*} [division_semiring k] [char_zero k] {m n : ℕ}
  (n_dvd : n ∣ m) : ((m / n : ℕ) : k) = m / n :=
begin
  rcases eq_or_ne n 0 with rfl | hn,
  { simp },
  { exact cast_div n_dvd (cast_ne_zero.2 hn), },
end

end nat

section

variables (M : Type*) [add_monoid_with_one M] [char_zero M]

instance char_zero.ne_zero.two : ne_zero (2 : M) :=
⟨have ((2:ℕ):M) ≠ 0, from nat.cast_ne_zero.2 dec_trivial, by rwa [nat.cast_two] at this⟩

end

section
variables {R : Type*} [non_assoc_semiring R] [no_zero_divisors R] [char_zero R] {a : R}

@[simp]
lemma add_self_eq_zero {a : R} : a + a = 0 ↔ a = 0 :=
by simp only [(two_mul a).symm, mul_eq_zero, two_ne_zero, false_or]

@[simp]
lemma bit0_eq_zero {a : R} : bit0 a = 0 ↔ a = 0 := add_self_eq_zero
@[simp]
lemma zero_eq_bit0 {a : R} : 0 = bit0 a ↔ a = 0 :=
by { rw [eq_comm], exact bit0_eq_zero }

lemma bit0_ne_zero : bit0 a ≠ 0 ↔ a ≠ 0 := bit0_eq_zero.not
lemma zero_ne_bit0 : 0 ≠ bit0 a ↔ a ≠ 0 := zero_eq_bit0.not

end

section
variables {R : Type*} [non_assoc_ring R] [no_zero_divisors R] [char_zero R]

lemma neg_eq_self_iff {a : R} : -a = a ↔ a = 0 :=
neg_eq_iff_add_eq_zero.trans add_self_eq_zero

lemma eq_neg_self_iff {a : R} : a = -a ↔ a = 0 :=
eq_neg_iff_add_eq_zero.trans add_self_eq_zero

lemma nat_mul_inj {n : ℕ} {a b : R} (h : (n : R) * a = (n : R) * b) : n = 0 ∨ a = b :=
begin
  rw [←sub_eq_zero, ←mul_sub, mul_eq_zero, sub_eq_zero] at h,
  exact_mod_cast h,
end

lemma nat_mul_inj' {n : ℕ} {a b : R} (h : (n : R) * a = (n : R) * b) (w : n ≠ 0) : a = b :=
by simpa [w] using nat_mul_inj h

lemma bit0_injective : function.injective (bit0 : R → R) :=
λ a b h, begin
  dsimp [bit0] at h,
  simp only [(two_mul a).symm, (two_mul b).symm] at h,
  refine nat_mul_inj' _ two_ne_zero,
  exact_mod_cast h,
end

lemma bit1_injective : function.injective (bit1 : R → R) :=
λ a b h, begin
  simp only [bit1, add_left_inj] at h,
  exact bit0_injective h,
end

@[simp] lemma bit0_eq_bit0 {a b : R} : bit0 a = bit0 b ↔ a = b :=
bit0_injective.eq_iff

@[simp] lemma bit1_eq_bit1 {a b : R} : bit1 a = bit1 b ↔ a = b :=
bit1_injective.eq_iff

@[simp]
lemma bit1_eq_one {a : R} : bit1 a = 1 ↔ a = 0 :=
by rw [show (1 : R) = bit1 0, by simp, bit1_eq_bit1]

@[simp]
lemma one_eq_bit1 {a : R} : 1 = bit1 a ↔ a = 0 :=
by { rw [eq_comm], exact bit1_eq_one }

end

section
variables {R : Type*} [division_ring R] [char_zero R]

@[simp] lemma half_add_self (a : R) : (a + a) / 2 = a :=
by rw [← mul_two, mul_div_cancel a two_ne_zero]

@[simp] lemma add_halves' (a : R) : a / 2 + a / 2 = a :=
by rw [← add_div, half_add_self]

lemma sub_half (a : R) : a - a / 2 = a / 2 :=
by rw [sub_eq_iff_eq_add, add_halves']

lemma half_sub (a : R) : a / 2 - a = - (a / 2) :=
by rw [← neg_sub, sub_half]

end

namespace with_top

instance {R : Type*} [add_monoid_with_one R] [char_zero R] : char_zero (with_top R) :=
{ cast_injective := λ m n h, by rwa [← coe_nat, ← coe_nat n, coe_eq_coe, nat.cast_inj] at h }

end with_top

section ring_hom

variables {R S : Type*} [non_assoc_semiring R] [non_assoc_semiring S]

lemma ring_hom.char_zero (ϕ : R →+* S) [hS : char_zero S] : char_zero R :=
⟨λ a b h, char_zero.cast_injective (by rw [←map_nat_cast ϕ, ←map_nat_cast ϕ, h])⟩

lemma ring_hom.char_zero_iff {ϕ : R →+* S} (hϕ : function.injective ϕ) :
  char_zero R ↔ char_zero S :=
⟨λ hR, ⟨by introsI a b h; rwa [← @nat.cast_inj R, ← hϕ.eq_iff, map_nat_cast ϕ, map_nat_cast ϕ]⟩,
  λ hS, by exactI ϕ.char_zero⟩

lemma ring_hom.injective_nat (f : ℕ →+* R) [char_zero R] :
  function.injective f :=
subsingleton.elim (nat.cast_ring_hom _) f ▸ nat.cast_injective

end ring_hom


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

! This file was ported from Lean 3 source module combinatorics.young.semistandard_tableau
! leanprover-community/mathlib commit b363547b3113d350d053abdf2884e9850a56b205
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Combinatorics.Young.YoungDiagram

/-!
# Semistandard Young tableaux

A semistandard Young tableau is a filling of a Young diagram by natural numbers, such that
the entries are weakly increasing left-to-right along rows (i.e. for fixed `i`), and
strictly-increasing top-to-bottom along columns (i.e. for fixed `j`).

An example of an SSYT of shape `μ = [4, 2, 1]` is:

```text
0 0 0 2
1 1
2
```

We represent an SSYT as a function `ℕ → ℕ → ℕ`, which is required to be zero for all pairs
`(i, j) ∉ μ` and to satisfy the row-weak and column-strict conditions on `μ`.

## Main definitions

- `Ssyt (μ : YoungDiagram)`: semistandard Young tableaux of shape `μ`. There is
  a `coe` instance such that `T i j` is value of the `(i, j)` entry of the SSYT `T`.
- `Ssyt.highestWeight (μ : YoungDiagram)`: the semistandard Young tableau whose `i`th row
  consists entirely of `i`s, for each `i`.

## Tags

Semistandard Young tableau

## References

<https://en.wikipedia.org/wiki/Young_tableau>

-/

/-- A semistandard Young tableau (SSYT) is a filling of the cells of a Young diagram by natural
numbers, such that the entries in each row are weakly increasing (left to right), and the entries
in each column are strictly increasing (top to bottom).

Here, an SSYT is represented as an unrestricted function `ℕ → ℕ → ℕ` that, for reasons
of extensionality, is required to vanish outside `μ`. -/
structure Ssyt (μ : YoungDiagram) where
  /-- `entry i j` is value of the `(i, j)` entry of the SSYT `μ`. -/
  entry : ℕ → ℕ → ℕ
  /-- The entries in each row are weakly increasing (left to right). -/
  row_weak' : ∀ {i j1 j2 : ℕ}, j1 < j2 → (i, j2) ∈ μ → entry i j1 ≤ entry i j2
  /-- The entries in each column are strictly increasing (top to bottom). -/
  col_strict' : ∀ {i1 i2 j : ℕ}, i1 < i2 → (i2, j) ∈ μ → entry i1 j < entry i2 j
  /-- `entry` is required to be zero for all pairs `(i, j) ∉ μ`. -/
  zeros' : ∀ {i j}, (i, j) ∉ μ → entry i j = 0
#align ssyt Ssyt

namespace Ssyt

instance funLike {μ : YoungDiagram} : FunLike (Ssyt μ) ℕ fun _ ↦ ℕ → ℕ where
  coe := Ssyt.entry
  coe_injective' T T' h := by
    cases T
    cases T'
    congr
#align ssyt.fun_like Ssyt.funLike

/-- Helper instance for when there's too many metavariables to apply `CoeFun.coe` directly. -/
instance {μ : YoungDiagram} : CoeFun (Ssyt μ) fun _ ↦ ℕ → ℕ → ℕ :=
  inferInstance

@[simp]
theorem to_fun_eq_coe {μ : YoungDiagram} {T : Ssyt μ} : T.entry = (T : ℕ → ℕ → ℕ) :=
  rfl
#align ssyt.to_fun_eq_coe Ssyt.to_fun_eq_coe

@[ext]
theorem ext {μ : YoungDiagram} {T T' : Ssyt μ} (h : ∀ i j, T i j = T' i j) : T = T' :=
  FunLike.ext T T' fun _ ↦ by
    funext
    apply h
#align ssyt.ext Ssyt.ext

/-- Copy of an `Ssyt μ` with a new `entry` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy {μ : YoungDiagram} (T : Ssyt μ) (entry' : ℕ → ℕ → ℕ) (h : entry' = T) : Ssyt μ
    where
  entry := entry'
  row_weak' := h.symm ▸ T.row_weak'
  col_strict' := h.symm ▸ T.col_strict'
  zeros' := h.symm ▸ T.zeros'
#align ssyt.copy Ssyt.copy

@[simp]
theorem coe_copy {μ : YoungDiagram} (T : Ssyt μ) (entry' : ℕ → ℕ → ℕ) (h : entry' = T) :
    ⇑(T.copy entry' h) = entry' :=
  rfl
#align ssyt.coe_copy Ssyt.coe_copy

theorem copy_eq {μ : YoungDiagram} (T : Ssyt μ) (entry' : ℕ → ℕ → ℕ) (h : entry' = T) :
    T.copy entry' h = T :=
  FunLike.ext' h
#align ssyt.copy_eq Ssyt.copy_eq

theorem row_weak {μ : YoungDiagram} (T : Ssyt μ) {i j1 j2 : ℕ} (hj : j1 < j2)
    (hcell : (i, j2) ∈ μ) : T i j1 ≤ T i j2 :=
  T.row_weak' hj hcell
#align ssyt.row_weak Ssyt.row_weak

theorem zeros {μ : YoungDiagram} (T : Ssyt μ) {i j : ℕ} (not_cell : (i, j) ∉ μ) : T i j = 0 :=
  T.zeros' not_cell
#align ssyt.zeros Ssyt.zeros

theorem row_weak_of_le {μ : YoungDiagram} (T : Ssyt μ) {i j1 j2 : ℕ} (hj : j1 ≤ j2)
    (cell : (i, j2) ∈ μ) : T i j1 ≤ T i j2 := by
  cases' eq_or_lt_of_le hj with h h
  . rw [h]
  . exact T.row_weak h cell
#align ssyt.row_weak_of_le Ssyt.row_weak_of_le

theorem col_weak {μ : YoungDiagram} (T : Ssyt μ) {i1 i2 j : ℕ} (hi : i1 ≤ i2) (cell : (i2, j) ∈ μ) :
    T i1 j ≤ T i2 j := by
  cases' eq_or_lt_of_le hi with h h
  . rw [h]
  . exact le_of_lt (T.col_strict h cell)
#align ssyt.col_weak Ssyt.col_weak

/-- The "highest weight" SSYT of a given shape has all i's in row i, for each i. -/
def highestWeight (μ : YoungDiagram) : Ssyt μ where
  entry i j := if (i, j) ∈ μ then i else 0
  row_weak' hj hcell := by
    simp only
    rw [if_pos hcell, if_pos (μ.up_left_mem (by rfl) (le_of_lt hj) hcell)]
  col_strict' hi hcell := by
    simp only
    rwa [if_pos hcell, if_pos (μ.up_left_mem (le_of_lt hi) (by rfl) hcell)]
  zeros' not_cell := if_neg not_cell
#align ssyt.highest_weight Ssyt.highestWeight

@[simp]
theorem highestWeight_apply {μ : YoungDiagram} {i j : ℕ} :
    highestWeight μ i j = if (i, j) ∈ μ then i else 0 :=
  rfl
#align ssyt.highest_weight_apply Ssyt.highestWeight_apply

instance {μ : YoungDiagram} : Inhabited (Ssyt μ) :=
  ⟨Ssyt.highestWeight μ⟩

end Ssyt


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

! This file was ported from Lean 3 source module topology.continuous_function.ideals
! leanprover-community/mathlib commit d39590fc8728fbf6743249802486f8c91ffe07bc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Algebra.Algebra
import Mathbin.Topology.ContinuousFunction.Compact
import Mathbin.Topology.UrysohnsLemma
import Mathbin.Data.IsROrC.Basic
import Mathbin.Analysis.NormedSpace.Units
import Mathbin.Topology.Algebra.Module.CharacterSpace

/-!
# Ideals of continuous functions

For a topological ring `R` and a topological space `X` there is a Galois connection between
`ideal C(X, R)` and `set X` given by sending each `I : ideal C(X, R)` to
`{x : X | ∀ f ∈ I, f x = 0}ᶜ` and mapping `s : set X` to the ideal with carrier
`{f : C(X, R) | ∀ x ∈ sᶜ, f x = 0}`, and we call these maps `continuous_map.set_of_ideal` and
`continuous_map.ideal_of_set`. As long as `R` is Hausdorff, `continuous_map.set_of_ideal I` is open,
and if, in addition, `X` is locally compact, then `continuous_map.set_of_ideal s` is closed.

When `R = 𝕜` with `is_R_or_C 𝕜` and `X` is compact Hausdorff, then this Galois connection can be
improved to a true Galois correspondence (i.e., order isomorphism) between the type `opens X` and
the subtype of closed ideals of `C(X, 𝕜)`. Because we do not have a bundled type of closed ideals,
we simply register this as a Galois insertion between `ideal C(X, 𝕜)` and `opens X`, which is
`continuous_map.ideal_opens_gi`. Consequently, the maximal ideals of `C(X, 𝕜)` are precisely those
ideals corresponding to (complements of) singletons in `X`.

In addition, when `X` is locally compact and `𝕜` is a nontrivial topological integral domain, then
there is a natural continuous map from `X` to `character_space 𝕜 C(X, 𝕜)` given by point evaluation,
which is herein called `weak_dual.character_space.continuous_map_eval`. Again, when `X` is compact
Hausdorff and `is_R_or_C 𝕜`, more can be obtained. In particular, in that context this map is
bijective, and since the domain is compact and the codomain is Hausdorff, it is a homeomorphism,
herein called `weak_dual.character_space.homeo_eval`.

## Main definitions

* `continuous_map.ideal_of_set`: ideal of functions which vanish on the complement of a set.
* `continuous_map.set_of_ideal`: complement of the set on which all functions in the ideal vanish.
* `continuous_map.opens_of_ideal`: `continuous_map.set_of_ideal` as a term of `opens X`.
* `continuous_map.ideal_opens_gi`: The Galois insertion `continuous_map.opens_of_ideal` and
  `λ s, continuous_map.ideal_of_set ↑s`.
* `weak_dual.character_space.continuous_map_eval`: the natural continuous map from a locally compact
  topological space `X` to the `character_space 𝕜 C(X, 𝕜)` which sends `x : X` to point evaluation
  at `x`, with modest hypothesis on `𝕜`.
* `weak_dual.character_space.homeo_eval`: this is `weak_dual.character_space.continuous_map_eval`
  upgraded to a homeomorphism when `X` is compact Hausdorff and `is_R_or_C 𝕜`.

## Main statements

* `continuous_map.ideal_of_set_of_ideal_eq_closure`: when `X` is compact Hausdorff and
  `is_R_or_C 𝕜`, `ideal_of_set 𝕜 (set_of_ideal I) = I.closure` for any ideal `I : ideal C(X, 𝕜)`.
* `continuous_map.set_of_ideal_of_set_eq_interior`: when `X` is compact Hausdorff and `is_R_or_C 𝕜`,
  `set_of_ideal (ideal_of_set 𝕜 s) = interior s` for any `s : set X`.
* `continuous_map.ideal_is_maximal_iff`: when `X` is compact Hausdorff and `is_R_or_C 𝕜`, a closed
  ideal of `C(X, 𝕜)` is maximal if and only if it is `ideal_of_set 𝕜 {x}ᶜ` for some `x : X`.

## Implementation details

Because there does not currently exist a bundled type of closed ideals, we don't provide the actual
order isomorphism described above, and instead we only consider the Galois insertion
`continuous_map.ideal_opens_gi`.

## Tags

ideal, continuous function, compact, Hausdorff
-/

open NNReal

namespace ContinuousMap

open TopologicalSpace

section TopologicalRing

variable {X R : Type _} [TopologicalSpace X] [Ring R] [TopologicalSpace R] [TopologicalRing R]

variable (R)

/-- Given a topological ring `R` and `s : set X`, construct the ideal in `C(X, R)` of functions
which vanish on the complement of `s`. -/
def idealOfSet (s : Set X) : Ideal C(X, R)
    where
  carrier := { f : C(X, R) | ∀ x ∈ sᶜ, f x = 0 }
  add_mem' f g hf hg x hx := by simp only [hf x hx, hg x hx, coe_add, Pi.add_apply, add_zero]
  zero_mem' _ _ := rfl
  smul_mem' c f hf x hx := MulZeroClass.mul_zero (c x) ▸ congr_arg (fun y => c x * y) (hf x hx)
#align continuous_map.ideal_of_set ContinuousMap.idealOfSet

theorem idealOfSet_closed [LocallyCompactSpace X] [T2Space R] (s : Set X) :
    IsClosed (idealOfSet R s : Set C(X, R)) :=
  by
  simp only [ideal_of_set, Submodule.coe_set_mk, Set.setOf_forall]
  exact
    isClosed_interᵢ fun x =>
      isClosed_interᵢ fun hx => isClosed_eq (continuous_eval_const' x) continuous_const
#align continuous_map.ideal_of_set_closed ContinuousMap.idealOfSet_closed

variable {R}

theorem mem_idealOfSet {s : Set X} {f : C(X, R)} :
    f ∈ idealOfSet R s ↔ ∀ ⦃x : X⦄, x ∈ sᶜ → f x = 0 :=
  Iff.rfl
#align continuous_map.mem_ideal_of_set ContinuousMap.mem_idealOfSet

theorem not_mem_idealOfSet {s : Set X} {f : C(X, R)} : f ∉ idealOfSet R s ↔ ∃ x ∈ sᶜ, f x ≠ 0 :=
  by
  simp_rw [mem_ideal_of_set, exists_prop]
  push_neg
#align continuous_map.not_mem_ideal_of_set ContinuousMap.not_mem_idealOfSet

/-- Given an ideal `I` of `C(X, R)`, construct the set of points for which every function in the
ideal vanishes on the complement. -/
def setOfIdeal (I : Ideal C(X, R)) : Set X :=
  { x : X | ∀ f ∈ I, (f : C(X, R)) x = 0 }ᶜ
#align continuous_map.set_of_ideal ContinuousMap.setOfIdeal

theorem not_mem_setOfIdeal {I : Ideal C(X, R)} {x : X} :
    x ∉ setOfIdeal I ↔ ∀ ⦃f : C(X, R)⦄, f ∈ I → f x = 0 := by
  rw [← Set.mem_compl_iff, set_of_ideal, compl_compl, Set.mem_setOf]
#align continuous_map.not_mem_set_of_ideal ContinuousMap.not_mem_setOfIdeal

theorem mem_setOfIdeal {I : Ideal C(X, R)} {x : X} :
    x ∈ setOfIdeal I ↔ ∃ f ∈ I, (f : C(X, R)) x ≠ 0 :=
  by
  simp_rw [set_of_ideal, Set.mem_compl_iff, Set.mem_setOf, exists_prop]
  push_neg
#align continuous_map.mem_set_of_ideal ContinuousMap.mem_setOfIdeal

theorem setOfIdeal_open [T2Space R] (I : Ideal C(X, R)) : IsOpen (setOfIdeal I) :=
  by
  simp only [set_of_ideal, Set.setOf_forall, isOpen_compl_iff]
  exact
    isClosed_interᵢ fun f =>
      isClosed_interᵢ fun hf => isClosed_eq (map_continuous f) continuous_const
#align continuous_map.set_of_ideal_open ContinuousMap.setOfIdeal_open

/-- The open set `set_of_ideal I` realized as a term of `opens X`. -/
@[simps]
def opensOfIdeal [T2Space R] (I : Ideal C(X, R)) : Opens X :=
  ⟨setOfIdeal I, setOfIdeal_open I⟩
#align continuous_map.opens_of_ideal ContinuousMap.opensOfIdeal

@[simp]
theorem set_of_top_eq_univ [Nontrivial R] : setOfIdeal (⊤ : Ideal C(X, R)) = Set.univ :=
  Set.univ_subset_iff.mp fun x hx => mem_setOfIdeal.mpr ⟨1, Submodule.mem_top, one_ne_zero⟩
#align continuous_map.set_of_top_eq_univ ContinuousMap.set_of_top_eq_univ

@[simp]
theorem ideal_of_empty_eq_bot : idealOfSet R (∅ : Set X) = ⊥ :=
  Ideal.ext fun f => by
    simpa only [mem_ideal_of_set, Set.compl_empty, Set.mem_univ, forall_true_left, Ideal.mem_bot,
      FunLike.ext_iff] using Iff.rfl
#align continuous_map.ideal_of_empty_eq_bot ContinuousMap.ideal_of_empty_eq_bot

@[simp]
theorem mem_idealOfSet_compl_singleton (x : X) (f : C(X, R)) :
    f ∈ idealOfSet R ({x}ᶜ : Set X) ↔ f x = 0 := by
  simp only [mem_ideal_of_set, compl_compl, Set.mem_singleton_iff, forall_eq]
#align continuous_map.mem_ideal_of_set_compl_singleton ContinuousMap.mem_idealOfSet_compl_singleton

variable (X R)

theorem ideal_gc : GaloisConnection (setOfIdeal : Ideal C(X, R) → Set X) (idealOfSet R) :=
  by
  refine' fun I s => ⟨fun h f hf => _, fun h x hx => _⟩
  · by_contra h'
    rcases not_mem_ideal_of_set.mp h' with ⟨x, hx, hfx⟩
    exact hfx (not_mem_set_of_ideal.mp (mt (@h x) hx) hf)
  · obtain ⟨f, hf, hfx⟩ := mem_set_of_ideal.mp hx
    by_contra hx'
    exact not_mem_ideal_of_set.mpr ⟨x, hx', hfx⟩ (h hf)
#align continuous_map.ideal_gc ContinuousMap.ideal_gc

end TopologicalRing

section IsROrC

open IsROrC

variable {X 𝕜 : Type _} [IsROrC 𝕜] [TopologicalSpace X]

/-- An auxiliary lemma used in the proof of `ideal_of_set_of_ideal_eq_closure` which may be useful
on its own. -/
theorem exists_mul_le_one_eqOn_ge (f : C(X, ℝ≥0)) {c : ℝ≥0} (hc : 0 < c) :
    ∃ g : C(X, ℝ≥0), (∀ x : X, (g * f) x ≤ 1) ∧ { x : X | c ≤ f x }.EqOn (g * f) 1 :=
  ⟨{  toFun := (f ⊔ const X c)⁻¹
      continuous_toFun :=
        ((map_continuous f).sup <| map_continuous _).inv₀ fun _ => (hc.trans_le le_sup_right).ne' },
    fun x =>
    (inv_mul_le_iff (hc.trans_le le_sup_right)).mpr ((mul_one (f x ⊔ c)).symm ▸ le_sup_left),
    fun x hx => by
    simpa only [coe_const, coe_mk, Pi.mul_apply, Pi.inv_apply, Pi.sup_apply, Function.const_apply,
      Pi.one_apply, sup_eq_left.mpr (set.mem_set_of.mp hx)] using
      inv_mul_cancel (hc.trans_le hx).ne'⟩
#align continuous_map.exists_mul_le_one_eq_on_ge ContinuousMap.exists_mul_le_one_eqOn_ge

variable [CompactSpace X] [T2Space X]

@[simp]
theorem idealOfSet_of_ideal_eq_closure (I : Ideal C(X, 𝕜)) :
    idealOfSet 𝕜 (setOfIdeal I) = I.closure :=
  by
  /- Since `ideal_of_set 𝕜 (set_of_ideal I)` is closed and contains `I`, it contains `I.closure`.
    For the reverse inclusion, given `f ∈ ideal_of_set 𝕜 (set_of_ideal I)` and `(ε : ℝ≥0) > 0` it
    suffices to show that `f` is within `ε` of `I`.-/
  refine'
    le_antisymm (fun f hf => metric.mem_closure_iff.mpr fun ε hε => _)
      ((ideal_of_set_closed 𝕜 <| set_of_ideal I).closure_subset_iff.mpr fun f hf x hx =>
        not_mem_set_of_ideal.mp hx hf)
  lift ε to ℝ≥0 using hε.lt.le
  replace hε := show (0 : ℝ≥0) < ε from hε
  simp_rw [dist_nndist]
  norm_cast
  -- Let `t := {x : X | ε / 2 ≤ ‖f x‖₊}}` which is closed and disjoint from `set_of_ideal I`.
  set t := { x : X | ε / 2 ≤ ‖f x‖₊ }
  have ht : IsClosed t := isClosed_le continuous_const (map_continuous f).nnnorm
  have htI : Disjoint t (set_of_ideal Iᶜ) :=
    by
    refine' set.subset_compl_iff_disjoint_left.mp fun x hx => _
    simpa only [t, Set.mem_setOf, Set.mem_compl_iff, not_le] using
      (nnnorm_eq_zero.mpr (mem_ideal_of_set.mp hf hx)).trans_lt (half_pos hε)
  /- It suffices to produce `g : C(X, ℝ≥0)` which takes values in `[0,1]` and is constantly `1` on
    `t` such that when composed with the natural embedding of `ℝ≥0` into `𝕜` lies in the ideal `I`.
    Indeed, then `‖f - f * ↑g‖ ≤ ‖f * (1 - ↑g)‖ ≤ ⨆ ‖f * (1 - ↑g) x‖`. When `x ∉ t`, `‖f x‖ < ε / 2`
    and `‖(1 - ↑g) x‖ ≤ 1`, and when `x ∈ t`, `(1 - ↑g) x = 0`, and clearly `f * ↑g ∈ I`. -/
  suffices
    ∃ g : C(X, ℝ≥0), (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g ∈ I ∧ (∀ x, g x ≤ 1) ∧ t.eq_on g 1
    by
    obtain ⟨g, hgI, hg, hgt⟩ := this
    refine' ⟨f * (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g, I.mul_mem_left f hgI, _⟩
    rw [nndist_eq_nnnorm]
    refine' (nnnorm_lt_iff _ hε).2 fun x => _
    simp only [coe_sub, coe_mul, Pi.sub_apply, Pi.mul_apply]
    by_cases hx : x ∈ t
    ·
      simpa only [hgt hx, comp_apply, Pi.one_apply, ContinuousMap.coe_coe, algebraMapClm_apply,
        map_one, mul_one, sub_self, nnnorm_zero] using hε
    · refine' lt_of_le_of_lt _ (half_lt_self hε)
      have :=
        calc
          ‖((1 - (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g) x : 𝕜)‖₊ =
              ‖1 - algebraMap ℝ≥0 𝕜 (g x)‖₊ :=
            by
            simp only [coe_sub, coe_one, coe_comp, ContinuousMap.coe_coe, Pi.sub_apply,
              Pi.one_apply, Function.comp_apply, algebraMapClm_apply]
          _ = ‖algebraMap ℝ≥0 𝕜 (1 - g x)‖₊ := by
            simp only [Algebra.algebraMap_eq_smul_one, NNReal.smul_def, NNReal.coe_sub (hg x),
              sub_smul, Nonneg.coe_one, one_smul]
          _ ≤ 1 := (nnnorm_algebraMap_nNReal 𝕜 (1 - g x)).trans_le tsub_le_self

      calc
        ‖f x - f x * (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g x‖₊ =
            ‖f x * (1 - (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g) x‖₊ :=
          by simp only [mul_sub, coe_sub, coe_one, Pi.sub_apply, Pi.one_apply, mul_one]
        _ ≤ ε / 2 * ‖(1 - (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g) x‖₊ :=
          ((nnnorm_mul_le _ _).trans
            (mul_le_mul_right' (not_le.mp <| show ¬ε / 2 ≤ ‖f x‖₊ from hx).le _))
        _ ≤ ε / 2 := by simpa only [mul_one] using mul_le_mul_left' this _

  /- There is some `g' : C(X, ℝ≥0)` which is strictly positive on `t` such that the composition
    `↑g` with the natural embedding of `ℝ≥0` into `𝕜` lies in `I`. This follows from compactness of
    `t` and that we can do it in any neighborhood of a point `x ∈ t`. Indeed, since `x ∈ t`, then
    `fₓ x ≠ 0` for some `fₓ ∈ I` and so `λ y, ‖(star fₓ * fₓ) y‖₊` is strictly posiive in a
    neighborhood of `y`. Moreover, `(‖(star fₓ * fₓ) y‖₊ : 𝕜) = (star fₓ * fₓ) y`, so composition of
    this map with the natural embedding is just `star fₓ * fₓ ∈ I`. -/
  have : ∃ g' : C(X, ℝ≥0), (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g' ∈ I ∧ ∀ x ∈ t, 0 < g' x :=
    by
    refine'
      @IsCompact.induction_on _ _ _ ht.is_compact
        (fun s =>
          ∃ g' : C(X, ℝ≥0), (algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g' ∈ I ∧ ∀ x ∈ s, 0 < g' x)
        _ _ _ _
    · refine' ⟨0, _, fun x hx => False.elim hx⟩
      convert I.zero_mem
      ext
      simp only [coe_zero, Pi.zero_apply, ContinuousMap.coe_coe, ContinuousMap.coe_comp, map_zero,
        Pi.comp_zero]
    · rintro s₁ s₂ hs ⟨g, hI, hgt⟩
      exact ⟨g, hI, fun x hx => hgt x (hs hx)⟩
    · rintro s₁ s₂ ⟨g₁, hI₁, hgt₁⟩ ⟨g₂, hI₂, hgt₂⟩
      refine' ⟨g₁ + g₂, _, fun x hx => _⟩
      · convert I.add_mem hI₁ hI₂
        ext y
        simp only [coe_add, Pi.add_apply, map_add, coe_comp, Function.comp_apply,
          ContinuousMap.coe_coe]
      · rcases hx with (hx | hx)
        simpa only [zero_add] using add_lt_add_of_lt_of_le (hgt₁ x hx) zero_le'
        simpa only [zero_add] using add_lt_add_of_le_of_lt zero_le' (hgt₂ x hx)
    · intro x hx
      replace hx := htI.subset_compl_right hx
      rw [compl_compl, mem_set_of_ideal] at hx
      obtain ⟨g, hI, hgx⟩ := hx
      have := (map_continuous g).ContinuousAt.eventually_ne hgx
      refine'
        ⟨{ y : X | g y ≠ 0 } ∩ t,
          mem_nhds_within_iff_exists_mem_nhds_inter.mpr ⟨_, this, Set.Subset.rfl⟩,
          ⟨⟨fun x => ‖g x‖₊ ^ 2, (map_continuous g).nnnorm.pow 2⟩, _, fun x hx =>
            pow_pos (norm_pos_iff.mpr hx.1) 2⟩⟩
      convert I.mul_mem_left (star g) hI
      ext
      simp only [comp_apply, coe_mk, algebraMapClm_coe, map_pow, coe_mul, coe_star, Pi.mul_apply,
        Pi.star_apply, star_def, ContinuousMap.coe_coe]
      simpa only [norm_sq_eq_def', conj_mul_eq_norm_sq_left, of_real_pow]
  /- Get the function `g'` which is guaranteed to exist above. By the extreme value theorem and
    compactness of `t`, there is some `0 < c` such that `c ≤ g' x` for all `x ∈ t`. Then by
    `main_lemma_aux` there is some `g` for which `g * g'` is the desired function. -/
  obtain ⟨g', hI', hgt'⟩ := this
  obtain ⟨c, hc, hgc'⟩ : ∃ (c : _)(hc : 0 < c), ∀ y : X, y ∈ t → c ≤ g' y :=
    t.eq_empty_or_nonempty.elim
      (fun ht' => ⟨1, zero_lt_one, fun y hy => False.elim (by rwa [ht'] at hy)⟩) fun ht' =>
      let ⟨x, hx, hx'⟩ := ht.is_compact.exists_forall_le ht' (map_continuous g').ContinuousOn
      ⟨g' x, hgt' x hx, hx'⟩
  obtain ⟨g, hg, hgc⟩ := exists_mul_le_one_eq_on_ge g' hc
  refine' ⟨g * g', _, hg, hgc.mono hgc'⟩
  convert I.mul_mem_left ((algebraMapClm ℝ≥0 𝕜 : C(ℝ≥0, 𝕜)).comp g) hI'
  ext
  simp only [algebraMapClm_coe, ContinuousMap.coe_coe, comp_apply, coe_mul, Pi.mul_apply, map_mul]
#align continuous_map.ideal_of_set_of_ideal_eq_closure ContinuousMap.idealOfSet_of_ideal_eq_closure

theorem idealOfSet_of_ideal_isClosed {I : Ideal C(X, 𝕜)} (hI : IsClosed (I : Set C(X, 𝕜))) :
    idealOfSet 𝕜 (setOfIdeal I) = I :=
  (idealOfSet_of_ideal_eq_closure I).trans (Ideal.ext <| Set.ext_iff.mp hI.closure_eq)
#align continuous_map.ideal_of_set_of_ideal_is_closed ContinuousMap.idealOfSet_of_ideal_isClosed

variable (𝕜)

@[simp]
theorem setOfIdeal_of_set_eq_interior (s : Set X) : setOfIdeal (idealOfSet 𝕜 s) = interior s :=
  by
  refine'
    Set.Subset.antisymm
      ((set_of_ideal_open (ideal_of_set 𝕜 s)).subset_interior_iff.mpr fun x hx =>
        let ⟨f, hf, hfx⟩ := mem_set_of_ideal.mp hx
        set.not_mem_compl_iff.mp (mt (@hf x) hfx))
      fun x hx => _
  -- If `x ∉ closure sᶜ`, we must produce `f : C(X, 𝕜)` which is zero on `sᶜ` and `f x ≠ 0`.
  rw [← compl_compl (interior s), ← closure_compl] at hx
  simp_rw [mem_set_of_ideal, mem_ideal_of_set]
  haveI : NormalSpace X := normalOfCompactT2
  /- Apply Urysohn's lemma to get `g : C(X, ℝ)` which is zero on `sᶜ` and `g x ≠ 0`, then compose
    with the natural embedding `ℝ ↪ 𝕜` to produce the desired `f`. -/
  obtain ⟨g, hgs, hgx : Set.EqOn g 1 {x}, -⟩ :=
    exists_continuous_zero_one_of_closed isClosed_closure isClosed_singleton
      (set.disjoint_singleton_right.mpr hx)
  exact
    ⟨⟨fun x => g x, continuous_of_real.comp (map_continuous g)⟩, by
      simpa only [coe_mk, of_real_eq_zero] using fun x hx => hgs (subset_closure hx), by
      simpa only [coe_mk, hgx (Set.mem_singleton x), Pi.one_apply, IsROrC.of_real_one] using
        one_ne_zero⟩
#align continuous_map.set_of_ideal_of_set_eq_interior ContinuousMap.setOfIdeal_of_set_eq_interior

theorem setOfIdeal_of_set_of_isOpen {s : Set X} (hs : IsOpen s) : setOfIdeal (idealOfSet 𝕜 s) = s :=
  (setOfIdeal_of_set_eq_interior 𝕜 s).trans hs.interior_eq
#align continuous_map.set_of_ideal_of_set_of_is_open ContinuousMap.setOfIdeal_of_set_of_isOpen

variable (X)

/-- The Galois insertion `continuous_map.opens_of_ideal : ideal C(X, 𝕜) → opens X` and
`λ s, continuous_map.ideal_of_set ↑s`. -/
@[simps]
def idealOpensGi : GaloisInsertion (opensOfIdeal : Ideal C(X, 𝕜) → Opens X) fun s => idealOfSet 𝕜 s
    where
  choice I hI := opensOfIdeal I.closure
  gc I s := ideal_gc X 𝕜 I s
  le_l_u s := (setOfIdeal_of_set_of_isOpen 𝕜 s.IsOpen).ge
  choice_eq I hI :=
    congr_arg _ <|
      Ideal.ext
        (Set.ext_iff.mp
          (isClosed_of_closure_subset <|
              (idealOfSet_of_ideal_eq_closure I ▸ hI : I.closure ≤ I)).closure_eq)
#align continuous_map.ideal_opens_gi ContinuousMap.idealOpensGi

variable {X}

theorem idealOfSet_isMaximal_iff (s : Opens X) :
    (idealOfSet 𝕜 (s : Set X)).IsMaximal ↔ IsCoatom s :=
  by
  rw [Ideal.isMaximal_def]
  refine' (ideal_opens_gi X 𝕜).isCoatom_iff (fun I hI => _) s
  rw [← Ideal.isMaximal_def] at hI
  skip
  exact ideal_of_set_of_ideal_is_closed inferInstance
#align continuous_map.ideal_of_set_is_maximal_iff ContinuousMap.idealOfSet_isMaximal_iff

theorem ideal_of_compl_singleton_isMaximal (x : X) : (idealOfSet 𝕜 ({x}ᶜ : Set X)).IsMaximal :=
  (idealOfSet_isMaximal_iff 𝕜 (Closeds.singleton x).compl).mpr <| Opens.isCoatom_iff.mpr ⟨x, rfl⟩
#align continuous_map.ideal_of_compl_singleton_is_maximal ContinuousMap.ideal_of_compl_singleton_isMaximal

variable {𝕜}

theorem setOfIdeal_eq_compl_singleton (I : Ideal C(X, 𝕜)) [hI : I.IsMaximal] :
    ∃ x : X, setOfIdeal I = {x}ᶜ :=
  by
  have h : (ideal_of_set 𝕜 (set_of_ideal I)).IsMaximal :=
    (ideal_of_set_of_ideal_is_closed (inferInstance : IsClosed (I : Set C(X, 𝕜)))).symm ▸ hI
  obtain ⟨x, hx⟩ := opens.is_coatom_iff.1 ((ideal_of_set_is_maximal_iff 𝕜 (opens_of_ideal I)).1 h)
  exact ⟨x, congr_arg coe hx⟩
#align continuous_map.set_of_ideal_eq_compl_singleton ContinuousMap.setOfIdeal_eq_compl_singleton

theorem ideal_isMaximal_iff (I : Ideal C(X, 𝕜)) [hI : IsClosed (I : Set C(X, 𝕜))] :
    I.IsMaximal ↔ ∃ x : X, idealOfSet 𝕜 ({x}ᶜ) = I :=
  by
  refine'
    ⟨_, fun h =>
      let ⟨x, hx⟩ := h
      hx ▸ ideal_of_compl_singleton_is_maximal 𝕜 x⟩
  intro hI'
  obtain ⟨x, hx⟩ := set_of_ideal_eq_compl_singleton I
  exact
    ⟨x, by
      simpa only [ideal_of_set_of_ideal_eq_closure, Ideal.closure_eq_of_isClosed] using
        congr_arg (ideal_of_set 𝕜) hx.symm⟩
#align continuous_map.ideal_is_maximal_iff ContinuousMap.ideal_isMaximal_iff

end IsROrC

end ContinuousMap

namespace WeakDual

namespace CharacterSpace

open Function ContinuousMap

variable (X 𝕜 : Type _) [TopologicalSpace X]

section ContinuousMapEval

variable [LocallyCompactSpace X] [CommRing 𝕜] [TopologicalSpace 𝕜] [TopologicalRing 𝕜]

variable [Nontrivial 𝕜] [NoZeroDivisors 𝕜]

/-- The natural continuous map from a locally compact topological space `X` to the
`character_space 𝕜 C(X, 𝕜)` which sends `x : X` to point evaluation at `x`. -/
def continuousMapEval : C(X, characterSpace 𝕜 C(X, 𝕜))
    where
  toFun x :=
    ⟨{  toFun := fun f => f x
        map_add' := fun f g => rfl
        map_smul' := fun z f => rfl
        cont := continuous_eval_const' x },
      by
      rw [character_space.eq_set_map_one_map_mul]
      exact ⟨rfl, fun f g => rfl⟩⟩
  continuous_toFun := Continuous.subtype_mk (continuous_of_continuous_eval map_continuous) _
#align weak_dual.character_space.continuous_map_eval WeakDual.characterSpace.continuousMapEval

@[simp]
theorem continuousMapEval_apply_apply (x : X) (f : C(X, 𝕜)) : continuousMapEval X 𝕜 x f = f x :=
  rfl
#align weak_dual.character_space.continuous_map_eval_apply_apply WeakDual.characterSpace.continuousMapEval_apply_apply

end ContinuousMapEval

variable [CompactSpace X] [T2Space X] [IsROrC 𝕜]

theorem continuousMapEval_bijective : Bijective (continuousMapEval X 𝕜) :=
  by
  refine' ⟨fun x y hxy => _, fun φ => _⟩
  · contrapose! hxy
    haveI := @normalOfCompactT2 X _ _ _
    rcases exists_continuous_zero_one_of_closed (isClosed_singleton : _root_.is_closed {x})
        (isClosed_singleton : _root_.is_closed {y}) (set.disjoint_singleton.mpr hxy) with
      ⟨f, fx, fy, -⟩
    rw [← Ne.def, FunLike.ne_iff]
    use (⟨coe, IsROrC.continuous_of_real⟩ : C(ℝ, 𝕜)).comp f
    simpa only [continuous_map_eval_apply_apply, ContinuousMap.comp_apply, coe_mk, Ne.def,
      IsROrC.of_real_inj] using
      ((fx (Set.mem_singleton x)).symm ▸ (fy (Set.mem_singleton y)).symm ▸ zero_ne_one : f x ≠ f y)
  · obtain ⟨x, hx⟩ := (ideal_is_maximal_iff (RingHom.ker φ)).mp inferInstance
    refine' ⟨x, ext_ker <| Ideal.ext fun f => _⟩
    simpa only [RingHom.mem_ker, continuous_map_eval_apply_apply, mem_ideal_of_set_compl_singleton,
      RingHom.mem_ker] using set_like.ext_iff.mp hx f
#align weak_dual.character_space.continuous_map_eval_bijective WeakDual.characterSpace.continuousMapEval_bijective

/-- This is the natural homeomorphism between a compact Hausdorff space `X` and the
`character_space 𝕜 C(X, 𝕜)`. -/
noncomputable def homeoEval : X ≃ₜ characterSpace 𝕜 C(X, 𝕜) :=
  @Continuous.homeoOfEquivCompactToT2 _ _ _ _ _ _
    { Equiv.ofBijective _ (continuousMapEval_bijective X 𝕜) with toFun := continuousMapEval X 𝕜 }
    (map_continuous (continuousMapEval X 𝕜))
#align weak_dual.character_space.homeo_eval WeakDual.characterSpace.homeoEval

end CharacterSpace

end WeakDual


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison, Floris van Doorn
Ported by: Scott Morrison

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

/-!
# Isomorphisms

This file defines isomorphisms between objects of a category.

## Main definitions

- `structure Iso` : a bundled isomorphism between two objects of a category;
- `class IsIso` : an unbundled version of `iso`;
  note that `IsIso f` is a `Prop`, and only asserts the existence of an inverse.
  Of course, this inverse is unique, so it doesn't cost us much to use choice to retrieve it.
- `inv f`, for the inverse of a morphism with `[IsIso f]`
- `asIso` : convert from `IsIso` to `Iso` (noncomputable);
- `of_iso` : convert from `Iso` to `IsIso`;
- standard operations on isomorphisms (composition, inverse etc)

## Notations

- `X ≅ Y` : same as `Iso X Y`;
- `α ≪≫ β` : composition of two isomorphisms; it is called `Iso.trans`

## Tags

category, category theory, isomorphism
-/

universe v u

-- morphism levels before object levels. See note [CategoryTheory universes].
namespace CategoryTheory

open Category

/-- An isomorphism (a.k.a. an invertible morphism) between two objects of a category.
The inverse morphism is bundled.

See also `CategoryTheory.Core` for the category with the same objects and isomorphisms playing
the role of morphisms.

See <https://stacks.math.columbia.edu/tag/0017>.
-/
structure Iso {C : Type u} [Category.{v} C] (X Y : C) where
  /-- The forward direction of an isomorphism. -/
  hom : X ⟶ Y
  /-- The backwards direction of an isomorphism. -/
  inv : Y ⟶ X
  /-- Composition of the two directions of an isomorphism is the identity on the source. -/
  hom_inv_id : hom ≫ inv = 𝟙 X := by aesop_cat
  /-- Composition of the two directions of an isomorphism in reverse order
  is the identity on the target. -/
  inv_hom_id : inv ≫ hom = 𝟙 Y := by aesop_cat
#align category_theory.iso CategoryTheory.Iso
#align category_theory.iso.hom CategoryTheory.Iso.hom
#align category_theory.iso.inv CategoryTheory.Iso.inv
#align category_theory.iso.inv_hom_id CategoryTheory.Iso.inv_hom_id
#align category_theory.iso.hom_inv_id CategoryTheory.Iso.hom_inv_id

attribute [reassoc (attr := simp)] Iso.hom_inv_id Iso.inv_hom_id
#align category_theory.iso.hom_inv_id_assoc CategoryTheory.Iso.hom_inv_id_assoc
#align category_theory.iso.inv_hom_id_assoc CategoryTheory.Iso.inv_hom_id_assoc

/-- Notation for an isomorphism in a category. -/
infixr:10 " ≅ " => Iso -- type as \cong or \iso

variable {C : Type u} [Category.{v} C] {X Y Z : C}

namespace Iso

@[ext]
theorem ext ⦃α β : X ≅ Y⦄ (w : α.hom = β.hom) : α = β :=
  suffices α.inv = β.inv by
    cases α
    cases β
    cases w
    cases this
    rfl
  calc
    α.inv = α.inv ≫ β.hom ≫ β.inv   := by rw [Iso.hom_inv_id, Category.comp_id]
    _     = (α.inv ≫ α.hom) ≫ β.inv := by rw [Category.assoc, ← w]
    _     = β.inv                   := by rw [Iso.inv_hom_id, Category.id_comp]

#align category_theory.iso.ext CategoryTheory.Iso.ext

/-- Inverse isomorphism. -/
@[symm]
def symm (I : X ≅ Y) : Y ≅ X where
  hom := I.inv
  inv := I.hom
#align category_theory.iso.symm CategoryTheory.Iso.symm

@[simp]
theorem symm_hom (α : X ≅ Y) : α.symm.hom = α.inv :=
  rfl
#align category_theory.iso.symm_hom CategoryTheory.Iso.symm_hom

@[simp]
theorem symm_inv (α : X ≅ Y) : α.symm.inv = α.hom :=
  rfl
#align category_theory.iso.symm_inv CategoryTheory.Iso.symm_inv

@[simp]
theorem symm_mk {X Y : C} (hom : X ⟶ Y) (inv : Y ⟶ X) (hom_inv_id) (inv_hom_id) :
    Iso.symm { hom, inv, hom_inv_id := hom_inv_id, inv_hom_id := inv_hom_id } =
      { hom := inv, inv := hom, hom_inv_id := inv_hom_id, inv_hom_id := hom_inv_id } :=
  rfl
#align category_theory.iso.symm_mk CategoryTheory.Iso.symm_mk

@[simp]
theorem symm_symm_eq {X Y : C} (α : X ≅ Y) : α.symm.symm = α := by cases α; rfl
#align category_theory.iso.symm_symm_eq CategoryTheory.Iso.symm_symm_eq

@[simp]
theorem symm_eq_iff {X Y : C} {α β : X ≅ Y} : α.symm = β.symm ↔ α = β :=
  ⟨fun h => symm_symm_eq α ▸ symm_symm_eq β ▸ congr_arg symm h, congr_arg symm⟩
#align category_theory.iso.symm_eq_iff CategoryTheory.Iso.symm_eq_iff

theorem nonempty_iso_symm (X Y : C) : Nonempty (X ≅ Y) ↔ Nonempty (Y ≅ X) :=
  ⟨fun h => ⟨h.some.symm⟩, fun h => ⟨h.some.symm⟩⟩
#align category_theory.iso.nonempty_iso_symm CategoryTheory.Iso.nonempty_iso_symm

/-- Identity isomorphism. -/
@[refl, simps]
def refl (X : C) : X ≅ X where
  hom := 𝟙 X
  inv := 𝟙 X
#align category_theory.iso.refl CategoryTheory.Iso.refl
#align category_theory.iso.refl_inv CategoryTheory.Iso.refl_inv
#align category_theory.iso.refl_hom CategoryTheory.Iso.refl_hom

instance : Inhabited (X ≅ X) := ⟨Iso.refl X⟩

theorem nonempty_iso_refl (X : C) : Nonempty (X ≅ X) := ⟨default⟩

@[simp]
theorem refl_symm (X : C) : (Iso.refl X).symm = Iso.refl X := rfl
#align category_theory.iso.refl_symm CategoryTheory.Iso.refl_symm

-- Porting note: It seems that the trans `trans` attribute isn't working properly
-- in this case, so we have to manually add a `Trans` instance (with a `simps` tag).
/-- Composition of two isomorphisms -/
@[trans, simps]
def trans (α : X ≅ Y) (β : Y ≅ Z) : X ≅ Z where
  hom := α.hom ≫ β.hom
  inv := β.inv ≫ α.inv
#align category_theory.iso.trans CategoryTheory.Iso.trans
#align category_theory.iso.trans_hom CategoryTheory.Iso.trans_hom
#align category_theory.iso.trans_inv CategoryTheory.Iso.trans_inv

@[simps]
instance : Trans (α := C) (· ≅ ·) (· ≅ ·) (· ≅ ·) where
  trans := trans

/-- Notation for composition of isomorphisms. -/
infixr:80 " ≪≫ " => Iso.trans -- type as `\ll \gg`.

@[simp]
theorem trans_mk {X Y Z : C} (hom : X ⟶ Y) (inv : Y ⟶ X) (hom_inv_id) (inv_hom_id)
    (hom' : Y ⟶ Z) (inv' : Z ⟶ Y) (hom_inv_id') (inv_hom_id') (hom_inv_id'') (inv_hom_id'') :
    Iso.trans ⟨hom, inv, hom_inv_id, inv_hom_id⟩ ⟨hom', inv', hom_inv_id', inv_hom_id'⟩ =
     ⟨hom ≫ hom', inv' ≫ inv, hom_inv_id'', inv_hom_id''⟩ :=
  rfl
#align category_theory.iso.trans_mk CategoryTheory.Iso.trans_mk

@[simp]
theorem trans_symm (α : X ≅ Y) (β : Y ≅ Z) : (α ≪≫ β).symm = β.symm ≪≫ α.symm :=
  rfl
#align category_theory.iso.trans_symm CategoryTheory.Iso.trans_symm

@[simp]
theorem trans_assoc {Z' : C} (α : X ≅ Y) (β : Y ≅ Z) (γ : Z ≅ Z') :
    (α ≪≫ β) ≪≫ γ = α ≪≫ β ≪≫ γ := by
  ext; simp only [trans_hom, Category.assoc]
#align category_theory.iso.trans_assoc CategoryTheory.Iso.trans_assoc

@[simp]
theorem refl_trans (α : X ≅ Y) : Iso.refl X ≪≫ α = α := by ext; apply Category.id_comp
#align category_theory.iso.refl_trans CategoryTheory.Iso.refl_trans

@[simp]
theorem trans_refl (α : X ≅ Y) : α ≪≫ Iso.refl Y = α := by ext; apply Category.comp_id
#align category_theory.iso.trans_refl CategoryTheory.Iso.trans_refl

@[simp]
theorem symm_self_id (α : X ≅ Y) : α.symm ≪≫ α = Iso.refl Y :=
  ext α.inv_hom_id
#align category_theory.iso.symm_self_id CategoryTheory.Iso.symm_self_id

@[simp]
theorem self_symm_id (α : X ≅ Y) : α ≪≫ α.symm = Iso.refl X :=
  ext α.hom_inv_id
#align category_theory.iso.self_symm_id CategoryTheory.Iso.self_symm_id

@[simp]
theorem symm_self_id_assoc (α : X ≅ Y) (β : Y ≅ Z) : α.symm ≪≫ α ≪≫ β = β := by
  rw [← trans_assoc, symm_self_id, refl_trans]
#align category_theory.iso.symm_self_id_assoc CategoryTheory.Iso.symm_self_id_assoc

@[simp]
theorem self_symm_id_assoc (α : X ≅ Y) (β : X ≅ Z) : α ≪≫ α.symm ≪≫ β = β := by
  rw [← trans_assoc, self_symm_id, refl_trans]
#align category_theory.iso.self_symm_id_assoc CategoryTheory.Iso.self_symm_id_assoc

theorem inv_comp_eq (α : X ≅ Y) {f : X ⟶ Z} {g : Y ⟶ Z} : α.inv ≫ f = g ↔ f = α.hom ≫ g :=
  ⟨fun H => by simp [H.symm], fun H => by simp [H]⟩
#align category_theory.iso.inv_comp_eq CategoryTheory.Iso.inv_comp_eq

theorem eq_inv_comp (α : X ≅ Y) {f : X ⟶ Z} {g : Y ⟶ Z} : g = α.inv ≫ f ↔ α.hom ≫ g = f :=
  (inv_comp_eq α.symm).symm
#align category_theory.iso.eq_inv_comp CategoryTheory.Iso.eq_inv_comp

theorem comp_inv_eq (α : X ≅ Y) {f : Z ⟶ Y} {g : Z ⟶ X} : f ≫ α.inv = g ↔ f = g ≫ α.hom :=
  ⟨fun H => by simp [H.symm], fun H => by simp [H]⟩
#align category_theory.iso.comp_inv_eq CategoryTheory.Iso.comp_inv_eq

theorem eq_comp_inv (α : X ≅ Y) {f : Z ⟶ Y} {g : Z ⟶ X} : g = f ≫ α.inv ↔ g ≫ α.hom = f :=
  (comp_inv_eq α.symm).symm
#align category_theory.iso.eq_comp_inv CategoryTheory.Iso.eq_comp_inv

theorem inv_eq_inv (f g : X ≅ Y) : f.inv = g.inv ↔ f.hom = g.hom :=
  have : ∀ {X Y : C} (f g : X ≅ Y), f.hom = g.hom → f.inv = g.inv := fun f g h => by rw [ext h]
  ⟨this f.symm g.symm, this f g⟩
#align category_theory.iso.inv_eq_inv CategoryTheory.Iso.inv_eq_inv

theorem hom_comp_eq_id (α : X ≅ Y) {f : Y ⟶ X} : α.hom ≫ f = 𝟙 X ↔ f = α.inv := by
  rw [← eq_inv_comp, comp_id]
#align category_theory.iso.hom_comp_eq_id CategoryTheory.Iso.hom_comp_eq_id

theorem comp_hom_eq_id (α : X ≅ Y) {f : Y ⟶ X} : f ≫ α.hom = 𝟙 Y ↔ f = α.inv := by
  rw [← eq_comp_inv, id_comp]
#align category_theory.iso.comp_hom_eq_id CategoryTheory.Iso.comp_hom_eq_id

theorem inv_comp_eq_id (α : X ≅ Y) {f : X ⟶ Y} : α.inv ≫ f = 𝟙 Y ↔ f = α.hom :=
  hom_comp_eq_id α.symm
#align category_theory.iso.inv_comp_eq_id CategoryTheory.Iso.inv_comp_eq_id

theorem comp_inv_eq_id (α : X ≅ Y) {f : X ⟶ Y} : f ≫ α.inv = 𝟙 X ↔ f = α.hom :=
  comp_hom_eq_id α.symm
#align category_theory.iso.comp_inv_eq_id CategoryTheory.Iso.comp_inv_eq_id

theorem hom_eq_inv (α : X ≅ Y) (β : Y ≅ X) : α.hom = β.inv ↔ β.hom = α.inv := by
  erw [inv_eq_inv α.symm β, eq_comm]
  rfl
#align category_theory.iso.hom_eq_inv CategoryTheory.Iso.hom_eq_inv

end Iso

/-- `IsIso` typeclass expressing that a morphism is invertible. -/
class IsIso (f : X ⟶ Y) : Prop where
  /-- The existence of an inverse morphism. -/
  out : ∃ inv : Y ⟶ X, f ≫ inv = 𝟙 X ∧ inv ≫ f = 𝟙 Y
#align category_theory.is_iso CategoryTheory.IsIso

/-- The inverse of a morphism `f` when we have `[IsIso f]`.
-/
noncomputable def inv (f : X ⟶ Y) [I : IsIso f] : Y ⟶ X :=
  Classical.choose I.1
#align category_theory.inv CategoryTheory.inv

namespace IsIso

@[simp]
theorem hom_inv_id (f : X ⟶ Y) [I : IsIso f] : f ≫ inv f = 𝟙 X :=
  (Classical.choose_spec I.1).left
#align category_theory.is_iso.hom_inv_id CategoryTheory.IsIso.hom_inv_id

@[simp]
theorem inv_hom_id (f : X ⟶ Y) [I : IsIso f] : inv f ≫ f = 𝟙 Y :=
  (Classical.choose_spec I.1).right
#align category_theory.is_iso.inv_hom_id CategoryTheory.IsIso.inv_hom_id

-- FIXME putting @[reassoc] on the `hom_inv_id` above somehow unfolds `inv`
-- This happens even if we make `inv` irreducible!
-- I don't understand how this is happening: it is likely a bug.

-- attribute [reassoc] hom_inv_id inv_hom_id
-- #print hom_inv_id_assoc
--   theorem CategoryTheory.IsIso.hom_inv_id_assoc {X Y : C} (f : X ⟶ Y) [I : IsIso f]
--     {Z : C} (h : X ⟶ Z),
--     f ≫ Classical.choose (_ : Exists fun inv ↦ f ≫ inv = 𝟙 X ∧ inv ≫ f = 𝟙 Y) ≫ h = h := ...

@[simp]
theorem hom_inv_id_assoc (f : X ⟶ Y) [I : IsIso f] {Z} (g : X ⟶ Z) : f ≫ inv f ≫ g = g := by
  simp [← Category.assoc]
#align category_theory.is_iso.hom_inv_id_assoc CategoryTheory.IsIso.hom_inv_id_assoc

@[simp]
theorem inv_hom_id_assoc (f : X ⟶ Y) [I : IsIso f] {Z} (g : Y ⟶ Z) : inv f ≫ f ≫ g = g := by
  simp [← Category.assoc]
#align category_theory.is_iso.inv_hom_id_assoc CategoryTheory.IsIso.inv_hom_id_assoc

end IsIso

open IsIso

/-- Reinterpret a morphism `f` with an `IsIso f` instance as an `Iso`. -/
noncomputable def asIso (f : X ⟶ Y) [IsIso f] : X ≅ Y :=
  ⟨f, inv f, hom_inv_id f, inv_hom_id f⟩
#align category_theory.as_iso CategoryTheory.asIso

@[simp]
theorem asIso_hom (f : X ⟶ Y) [IsIso f] : (asIso f).hom = f :=
  rfl
#align category_theory.as_iso_hom CategoryTheory.asIso_hom

@[simp]
theorem asIso_inv (f : X ⟶ Y) [IsIso f] : (asIso f).inv = inv f :=
  rfl
#align category_theory.as_iso_inv CategoryTheory.asIso_inv

namespace IsIso

-- see Note [lower instance priority]
instance (priority := 100) epi_of_iso (f : X ⟶ Y) [IsIso f] : Epi f where
  left_cancellation g h w := by
    rw [← IsIso.inv_hom_id_assoc f g, w, IsIso.inv_hom_id_assoc f h]
#align category_theory.is_iso.epi_of_iso CategoryTheory.IsIso.epi_of_iso

-- see Note [lower instance priority]
instance (priority := 100) mono_of_iso (f : X ⟶ Y) [IsIso f] : Mono f where
  right_cancellation g h w := by
    rw [← Category.comp_id g, ← Category.comp_id h, ← IsIso.hom_inv_id f,
      ← Category.assoc, w, ← Category.assoc]
#align category_theory.is_iso.mono_of_iso CategoryTheory.IsIso.mono_of_iso

-- Porting note: `@[ext]` used to accept lemmas like this. Now we add an aesop rule
@[aesop apply safe (rule_sets [CategoryTheory])]
theorem inv_eq_of_hom_inv_id {f : X ⟶ Y} [IsIso f] {g : Y ⟶ X} (hom_inv_id : f ≫ g = 𝟙 X) :
    inv f = g := by
  apply (cancel_epi f).mp
  simp [hom_inv_id]
#align category_theory.is_iso.inv_eq_of_hom_inv_id CategoryTheory.IsIso.inv_eq_of_hom_inv_id

theorem inv_eq_of_inv_hom_id {f : X ⟶ Y} [IsIso f] {g : Y ⟶ X} (inv_hom_id : g ≫ f = 𝟙 Y) :
    inv f = g := by
  apply (cancel_mono f).mp
  simp [inv_hom_id]
#align category_theory.is_iso.inv_eq_of_inv_hom_id CategoryTheory.IsIso.inv_eq_of_inv_hom_id

-- Porting note: `@[ext]` used to accept lemmas like this.
@[aesop apply safe (rule_sets [CategoryTheory])]
theorem eq_inv_of_hom_inv_id {f : X ⟶ Y} [IsIso f] {g : Y ⟶ X} (hom_inv_id : f ≫ g = 𝟙 X) :
    g = inv f :=
  (inv_eq_of_hom_inv_id hom_inv_id).symm
#align category_theory.is_iso.eq_inv_of_hom_inv_id CategoryTheory.IsIso.eq_inv_of_hom_inv_id

theorem eq_inv_of_inv_hom_id {f : X ⟶ Y} [IsIso f] {g : Y ⟶ X} (inv_hom_id : g ≫ f = 𝟙 Y) :
    g = inv f :=
  (inv_eq_of_inv_hom_id inv_hom_id).symm
#align category_theory.is_iso.eq_inv_of_inv_hom_id CategoryTheory.IsIso.eq_inv_of_inv_hom_id

instance id (X : C) : IsIso (𝟙 X) := ⟨⟨𝟙 X, by simp⟩⟩
#align category_theory.is_iso.id CategoryTheory.IsIso.id

instance of_iso (f : X ≅ Y) : IsIso f.hom := ⟨⟨f.inv, by simp⟩⟩
#align category_theory.is_iso.of_iso CategoryTheory.IsIso.of_iso

instance of_iso_inv (f : X ≅ Y) : IsIso f.inv := IsIso.of_iso f.symm
#align category_theory.is_iso.of_iso_inv CategoryTheory.IsIso.of_iso_inv

variable {f g : X ⟶ Y} {h : Y ⟶ Z}

instance inv_isIso [IsIso f] : IsIso (inv f) :=
  IsIso.of_iso_inv (asIso f)
#align category_theory.is_iso.inv_is_iso CategoryTheory.IsIso.inv_isIso

/- The following instance has lower priority for the following reason:
Suppose we are given `f : X ≅ Y` with `X Y : Type u`.
Without the lower priority, typeclass inference cannot deduce `IsIso f.hom`
because `f.hom` is defeq to `(λ x, x) ≫ f.hom`, triggering a loop. -/
instance (priority := 900) comp_isIso [IsIso f] [IsIso h] : IsIso (f ≫ h) :=
  IsIso.of_iso <| asIso f ≪≫ asIso h
#align category_theory.is_iso.comp_is_iso CategoryTheory.IsIso.comp_isIso

@[simp]
theorem inv_id : inv (𝟙 X) = 𝟙 X := by
  apply inv_eq_of_hom_inv_id
  simp
#align category_theory.is_iso.inv_id CategoryTheory.IsIso.inv_id

@[simp]
theorem inv_comp [IsIso f] [IsIso h] : inv (f ≫ h) = inv h ≫ inv f := by
  apply inv_eq_of_hom_inv_id
  simp
#align category_theory.is_iso.inv_comp CategoryTheory.IsIso.inv_comp

@[simp]
theorem inv_inv [IsIso f] : inv (inv f) = f := by
  apply inv_eq_of_hom_inv_id
  simp
#align category_theory.is_iso.inv_inv CategoryTheory.IsIso.inv_inv

@[simp]
theorem Iso.inv_inv (f : X ≅ Y) : inv f.inv = f.hom := by
  apply inv_eq_of_hom_inv_id
  simp
#align category_theory.is_iso.iso.inv_inv CategoryTheory.IsIso.Iso.inv_inv

@[simp]
theorem Iso.inv_hom (f : X ≅ Y) : inv f.hom = f.inv := by
  apply inv_eq_of_hom_inv_id
  simp
#align category_theory.is_iso.iso.inv_hom CategoryTheory.IsIso.Iso.inv_hom

@[simp]
theorem inv_comp_eq (α : X ⟶ Y) [IsIso α] {f : X ⟶ Z} {g : Y ⟶ Z} : inv α ≫ f = g ↔ f = α ≫ g :=
  (asIso α).inv_comp_eq
#align category_theory.is_iso.inv_comp_eq CategoryTheory.IsIso.inv_comp_eq

@[simp]
theorem eq_inv_comp (α : X ⟶ Y) [IsIso α] {f : X ⟶ Z} {g : Y ⟶ Z} : g = inv α ≫ f ↔ α ≫ g = f :=
  (asIso α).eq_inv_comp
#align category_theory.is_iso.eq_inv_comp CategoryTheory.IsIso.eq_inv_comp

@[simp]
theorem comp_inv_eq (α : X ⟶ Y) [IsIso α] {f : Z ⟶ Y} {g : Z ⟶ X} : f ≫ inv α = g ↔ f = g ≫ α :=
  (asIso α).comp_inv_eq
#align category_theory.is_iso.comp_inv_eq CategoryTheory.IsIso.comp_inv_eq

@[simp]
theorem eq_comp_inv (α : X ⟶ Y) [IsIso α] {f : Z ⟶ Y} {g : Z ⟶ X} : g = f ≫ inv α ↔ g ≫ α = f :=
  (asIso α).eq_comp_inv
#align category_theory.is_iso.eq_comp_inv CategoryTheory.IsIso.eq_comp_inv

theorem of_isIso_comp_left {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) [IsIso f] [IsIso (f ≫ g)] :
    IsIso g := by
  rw [← id_comp g, ← inv_hom_id f, assoc]
  infer_instance
#align category_theory.is_iso.of_is_iso_comp_left CategoryTheory.IsIso.of_isIso_comp_left

theorem of_isIso_comp_right {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) [IsIso g] [IsIso (f ≫ g)] :
    IsIso f := by
  rw [← comp_id f, ← hom_inv_id g, ← assoc]
  infer_instance
#align category_theory.is_iso.of_is_iso_comp_right CategoryTheory.IsIso.of_isIso_comp_right

theorem of_isIso_fac_left {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} {h : X ⟶ Z} [IsIso f]
    [hh : IsIso h] (w : f ≫ g = h) : IsIso g := by
  rw [← w] at hh
  haveI := hh
  exact of_isIso_comp_left f g
#align category_theory.is_iso.of_is_iso_fac_left CategoryTheory.IsIso.of_isIso_fac_left

theorem of_isIso_fac_right {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} {h : X ⟶ Z} [IsIso g]
    [hh : IsIso h] (w : f ≫ g = h) : IsIso f := by
  rw [← w] at hh
  haveI := hh
  exact of_isIso_comp_right f g
#align category_theory.is_iso.of_is_iso_fac_right CategoryTheory.IsIso.of_isIso_fac_right

end IsIso

open IsIso

theorem eq_of_inv_eq_inv {f g : X ⟶ Y} [IsIso f] [IsIso g] (p : inv f = inv g) : f = g := by
  apply (cancel_epi (inv f)).1
  erw [inv_hom_id, p, inv_hom_id]
#align category_theory.eq_of_inv_eq_inv CategoryTheory.eq_of_inv_eq_inv

theorem IsIso.inv_eq_inv {f g : X ⟶ Y} [IsIso f] [IsIso g] : inv f = inv g ↔ f = g :=
  Iso.inv_eq_inv (asIso f) (asIso g)
#align category_theory.is_iso.inv_eq_inv CategoryTheory.IsIso.inv_eq_inv

theorem hom_comp_eq_id (g : X ⟶ Y) [IsIso g] {f : Y ⟶ X} : g ≫ f = 𝟙 X ↔ f = inv g :=
  (asIso g).hom_comp_eq_id
#align category_theory.hom_comp_eq_id CategoryTheory.hom_comp_eq_id

theorem comp_hom_eq_id (g : X ⟶ Y) [IsIso g] {f : Y ⟶ X} : f ≫ g = 𝟙 Y ↔ f = inv g :=
  (asIso g).comp_hom_eq_id
#align category_theory.comp_hom_eq_id CategoryTheory.comp_hom_eq_id

theorem inv_comp_eq_id (g : X ⟶ Y) [IsIso g] {f : X ⟶ Y} : inv g ≫ f = 𝟙 Y ↔ f = g :=
  (asIso g).inv_comp_eq_id
#align category_theory.inv_comp_eq_id CategoryTheory.inv_comp_eq_id

theorem comp_inv_eq_id (g : X ⟶ Y) [IsIso g] {f : X ⟶ Y} : f ≫ inv g = 𝟙 X ↔ f = g :=
  (asIso g).comp_inv_eq_id
#align category_theory.comp_inv_eq_id CategoryTheory.comp_inv_eq_id

theorem isIso_of_hom_comp_eq_id (g : X ⟶ Y) [IsIso g] {f : Y ⟶ X} (h : g ≫ f = 𝟙 X) : IsIso f := by
  rw [(hom_comp_eq_id _).mp h]
  infer_instance
#align category_theory.is_iso_of_hom_comp_eq_id CategoryTheory.isIso_of_hom_comp_eq_id

theorem isIso_of_comp_hom_eq_id (g : X ⟶ Y) [IsIso g] {f : Y ⟶ X} (h : f ≫ g = 𝟙 Y) : IsIso f := by
  rw [(comp_hom_eq_id _).mp h]
  infer_instance
#align category_theory.is_iso_of_comp_hom_eq_id CategoryTheory.isIso_of_comp_hom_eq_id

namespace Iso

-- Porting note: `@[ext]` used to accept lemmas like this.
@[aesop apply safe (rule_sets [CategoryTheory])]
theorem inv_ext {f : X ≅ Y} {g : Y ⟶ X} (hom_inv_id : f.hom ≫ g = 𝟙 X) : f.inv = g :=
  ((hom_comp_eq_id f).1 hom_inv_id).symm
#align category_theory.iso.inv_ext CategoryTheory.Iso.inv_ext

-- Porting note: `@[ext]` used to accept lemmas like this.
@[aesop apply safe (rule_sets [CategoryTheory])]
theorem inv_ext' {f : X ≅ Y} {g : Y ⟶ X} (hom_inv_id : f.hom ≫ g = 𝟙 X) : g = f.inv :=
  (hom_comp_eq_id f).1 hom_inv_id
#align category_theory.iso.inv_ext' CategoryTheory.Iso.inv_ext'

/-!
All these cancellation lemmas can be solved by `simp [cancel_mono]` (or `simp [cancel_epi]`),
but with the current design `cancel_mono` is not a good `simp` lemma,
because it generates a typeclass search.

When we can see syntactically that a morphism is a `mono` or an `epi`
because it came from an isomorphism, it's fine to do the cancellation via `simp`.

In the longer term, it might be worth exploring making `mono` and `epi` structures,
rather than typeclasses, with coercions back to `X ⟶ Y`.
Presumably we could write `X ↪ Y` and `X ↠ Y`.
-/

@[simp]
theorem cancel_iso_hom_left {X Y Z : C} (f : X ≅ Y) (g g' : Y ⟶ Z) :
    f.hom ≫ g = f.hom ≫ g' ↔ g = g' := by
  simp only [cancel_epi]
#align category_theory.iso.cancel_iso_hom_left CategoryTheory.Iso.cancel_iso_hom_left

@[simp]
theorem cancel_iso_inv_left {X Y Z : C} (f : Y ≅ X) (g g' : Y ⟶ Z) :
    f.inv ≫ g = f.inv ≫ g' ↔ g = g' := by
  simp only [cancel_epi]
#align category_theory.iso.cancel_iso_inv_left CategoryTheory.Iso.cancel_iso_inv_left

@[simp]
theorem cancel_iso_hom_right {X Y Z : C} (f f' : X ⟶ Y) (g : Y ≅ Z) :
    f ≫ g.hom = f' ≫ g.hom ↔ f = f' := by
  simp only [cancel_mono]
#align category_theory.iso.cancel_iso_hom_right CategoryTheory.Iso.cancel_iso_hom_right

@[simp]
theorem cancel_iso_inv_right {X Y Z : C} (f f' : X ⟶ Y) (g : Z ≅ Y) :
    f ≫ g.inv = f' ≫ g.inv ↔ f = f' := by
  simp only [cancel_mono]
#align category_theory.iso.cancel_iso_inv_right CategoryTheory.Iso.cancel_iso_inv_right

/-
Unfortunately cancelling an isomorphism from the right of a chain of compositions is awkward.
We would need separate lemmas for each chain length (worse: for each pair of chain lengths).

We provide two more lemmas, for case of three morphisms, because this actually comes up in practice,
but then stop.
-/
@[simp]
theorem cancel_iso_hom_right_assoc {W X X' Y Z : C} (f : W ⟶ X) (g : X ⟶ Y) (f' : W ⟶ X')
    (g' : X' ⟶ Y) (h : Y ≅ Z) : f ≫ g ≫ h.hom = f' ≫ g' ≫ h.hom ↔ f ≫ g = f' ≫ g' := by
  simp only [← Category.assoc, cancel_mono]
#align category_theory.iso.cancel_iso_hom_right_assoc CategoryTheory.Iso.cancel_iso_hom_right_assoc

@[simp]
theorem cancel_iso_inv_right_assoc {W X X' Y Z : C} (f : W ⟶ X) (g : X ⟶ Y) (f' : W ⟶ X')
    (g' : X' ⟶ Y) (h : Z ≅ Y) : f ≫ g ≫ h.inv = f' ≫ g' ≫ h.inv ↔ f ≫ g = f' ≫ g' := by
  simp only [← Category.assoc, cancel_mono]
#align category_theory.iso.cancel_iso_inv_right_assoc CategoryTheory.Iso.cancel_iso_inv_right_assoc

end Iso

namespace Functor

universe u₁ v₁ u₂ v₂

variable {D : Type u₂}

variable [Category.{v₂} D]

/-- A functor `F : C ⥤ D` sends isomorphisms `i : X ≅ Y` to isomorphisms `F.obj X ≅ F.obj Y` -/
@[simps]
def mapIso (F : C ⥤ D) {X Y : C} (i : X ≅ Y) : F.obj X ≅ F.obj Y where
  hom := F.map i.hom
  inv := F.map i.inv
  hom_inv_id := by rw [← map_comp, Iso.hom_inv_id, ← map_id]
  inv_hom_id := by rw [← map_comp, Iso.inv_hom_id, ← map_id]
#align category_theory.functor.map_iso CategoryTheory.Functor.mapIso
#align category_theory.functor.map_iso_inv CategoryTheory.Functor.mapIso_inv
#align category_theory.functor.map_iso_hom CategoryTheory.Functor.mapIso_hom

@[simp]
theorem mapIso_symm (F : C ⥤ D) {X Y : C} (i : X ≅ Y) : F.mapIso i.symm = (F.mapIso i).symm :=
  rfl
#align category_theory.functor.map_iso_symm CategoryTheory.Functor.mapIso_symm

@[simp]
theorem mapIso_trans (F : C ⥤ D) {X Y Z : C} (i : X ≅ Y) (j : Y ≅ Z) :
    F.mapIso (i ≪≫ j) = F.mapIso i ≪≫ F.mapIso j :=
  by ext; apply Functor.map_comp
#align category_theory.functor.map_iso_trans CategoryTheory.Functor.mapIso_trans

@[simp]

instance map_isIso (F : C ⥤ D) (f : X ⟶ Y) [IsIso f] : IsIso (F.map f) :=
  IsIso.of_iso <| F.mapIso (asIso f)
#align category_theory.functor.map_is_iso CategoryTheory.Functor.map_isIso

@[simp]
theorem map_inv (F : C ⥤ D) {X Y : C} (f : X ⟶ Y) [IsIso f] : F.map (inv f) = inv (F.map f) := by
  apply eq_inv_of_hom_inv_id
  simp [← F.map_comp]
#align category_theory.functor.map_inv CategoryTheory.Functor.map_inv

theorem map_hom_inv (F : C ⥤ D) {X Y : C} (f : X ⟶ Y) [IsIso f] :
    F.map f ≫ F.map (inv f) = 𝟙 (F.obj X) := by simp
#align category_theory.functor.map_hom_inv CategoryTheory.Functor.map_hom_inv

theorem map_inv_hom (F : C ⥤ D) {X Y : C} (f : X ⟶ Y) [IsIso f] :
    F.map (inv f) ≫ F.map f = 𝟙 (F.obj Y) := by simp
#align category_theory.functor.map_inv_hom CategoryTheory.Functor.map_inv_hom

end Functor

end CategoryTheory


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

/-!
# Sections of a multiset
-/

namespace multiset
variables {α : Type*}

section sections

/--
The sections of a multiset of multisets `s` consists of all those multisets
which can be put in bijection with `s`, so each element is an member of the corresponding multiset.
-/
def sections (s : multiset (multiset α)) : multiset (multiset α) :=
multiset.rec_on s {0} (λs _ c, s.bind $ λa, c.map (multiset.cons a))
  (assume a₀ a₁ s pi, by simp [map_bind, bind_bind a₀ a₁, cons_swap])

@[simp] lemma sections_zero : sections (0 : multiset (multiset α)) = 0 ::ₘ 0 :=
rfl

@[simp] lemma sections_cons (s : multiset (multiset α)) (m : multiset α) :
  sections (m ::ₘ s) = m.bind (λa, (sections s).map (multiset.cons a)) :=
rec_on_cons m s

lemma coe_sections : ∀(l : list (list α)),
  sections ((l.map (λl:list α, (l : multiset α))) : multiset (multiset α)) =
    ((l.sections.map (λl:list α, (l : multiset α))) : multiset (multiset α))
| [] := rfl
| (a :: l) :=
  begin
    simp,
    rw [← cons_coe, sections_cons, bind_map_comm, coe_sections l],
    simp [list.sections, (∘), list.bind]
  end

@[simp] lemma sections_add (s t : multiset (multiset α)) :
  sections (s + t) = (sections s).bind (λm, (sections t).map ((+) m)) :=
multiset.induction_on s (by simp)
  (assume a s ih, by simp [ih, bind_assoc, map_bind, bind_map, -add_comm])

lemma mem_sections {s : multiset (multiset α)} :
  ∀{a}, a ∈ sections s ↔ s.rel (λs a, a ∈ s) a :=
multiset.induction_on s (by simp)
  (assume a s ih a',
    by simp [ih, rel_cons_left, -exists_and_distrib_left, exists_and_distrib_left.symm, eq_comm])

lemma card_sections {s : multiset (multiset α)} : card (sections s) = prod (s.map card) :=
multiset.induction_on s (by simp) (by simp {contextual := tt})

lemma prod_map_sum [comm_semiring α] {s : multiset (multiset α)} :
  prod (s.map sum) = sum ((sections s).map prod) :=
multiset.induction_on s (by simp)
  (assume a s ih, by simp [ih, map_bind, sum_map_mul_left, sum_map_mul_right])

end sections

end multiset


import .love02_backward_proofs_exercise_sheet

/-! # LoVe Homework 3: Forward Proofs

Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1 (6 points): Connectives and Quantifiers

1.1 (2 points). We have proved or stated three of the six possible implications
between `excluded_middle`, `peirce`, and `double_negation`. Prove the three
missing implications using structured proofs, exploiting the three theorems we
already have. -/

namespace backward_proofs

#check peirce_of_em
#check dn_of_peirce
#check sorry_lemmas.em_of_dn

lemma peirce_of_dn :
  double_negation → peirce :=
assume h : double_negation,
show peirce, from
  peirce_of_em (sorry_lemmas.em_of_dn h)

lemma em_of_peirce :
  peirce → excluded_middle :=
assume h : peirce,
show excluded_middle, from
  sorry_lemmas.em_of_dn (dn_of_peirce h)

lemma dn_of_em :
  excluded_middle → double_negation :=
assume h : excluded_middle,
show double_negation, from
  dn_of_peirce (peirce_of_em h)

end backward_proofs

/-! 1.2 (4 points). Supply a structured proof of the commutativity of `∧` under
an `∃` quantifier, using no other lemmas than the introduction and elimination
rules for `∃`, `∧`, and `↔`. -/

lemma exists_and_commute {α : Type} (p q : α → Prop) :
  (∃x, p x ∧ q x) ↔ (∃x, q x ∧ p x) :=
iff.intro
  (assume hex : ∃x, p x ∧ q x,
   show ∃x, q x ∧ p x, from
     exists.elim hex
       (fix a,
        assume hpq : p a ∧ q a,
        have hp : p a :=
          and.elim_left hpq,
        have hq : q a :=
          and.elim_right hpq,
        show ∃x, q x ∧ p x, from
          exists.intro a (and.intro hq hp)))
  (assume hex : ∃x, q x ∧ p x,
   show ∃x, p x ∧ q x, from
     exists.elim hex
       (fix a,
        assume hqp : q a ∧ p a,
        have hq : q a :=
          and.elim_left hqp,
        have hp : p a :=
          and.elim_right hqp,
        show ∃x, p x ∧ q x, from
          exists.intro a (and.intro hp hq)))

/-! ## Question 2 (3 points): Fokkink Logic Puzzles

If you have studied Logic and Sets with Prof. Fokkink, you will know he is fond
of logic puzzles. This question is a tribute.

Recall the following tactical proof: -/

lemma weak_peirce :
  ∀a b : Prop, ((((a → b) → a) → a) → b) → b :=
begin
  intros a b habaab,
  apply habaab,
  intro habaa,
  apply habaa,
  intro ha,
  apply habaab,
  intro haba,
  apply ha
end

/-! 2.1 (1 point). Prove the same lemma again, this time by providing a proof
term.

Hint: There is an easy way. -/

lemma weak_peirce₂ :
  ∀a b : Prop, ((((a → b) → a) → a) → b) → b :=
λ(a b : Prop) (habaab : (((a → b) → a) → a) → b),
  habaab (λhabaa : (a → b) → a,
    habaa (λha : a,
      habaab (λhaba : (a → b) → a,
        ha)))

/-! There are in fact three easy ways:

* Copy-paste the result of `#print weak_peirce`.

* Simply enter `weak_peirce` as the proof term for `weak_peirce₂`.

* Reuse the answer to question 3.2 of homework 1. -/

/-! 2.2 (2 points). Prove the same Fokkink lemma again, this time by providing a
structured proof, with `assume`s and `show`s. -/

lemma weak_peirce₃ :
  ∀a b : Prop, ((((a → b) → a) → a) → b) → b :=
fix a b,
assume habaab : (((a → b) → a) → a) → b,
show b, from
  habaab
    (assume habaa : (a → b) → a,
     show a, from
       habaa
         (assume ha : a,
          show b, from
            habaab
              (assume haba : (a → b) → a,
                show a, from
                  ha)))

end LoVe


/-
Copyright (c) 2021 Ashvni Narayanan. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ashvni Narayanan, Anne Baanen
-/

import algebra.char_p.algebra
import ring_theory.dedekind_domain.integral_closure

/-!
# Number fields
This file defines a number field and the ring of integers corresponding to it.

## Main definitions
 - `number_field` defines a number field as a field which has characteristic zero and is finite
    dimensional over ℚ.
 - `ring_of_integers` defines the ring of integers (or number ring) corresponding to a number field
    as the integral closure of ℤ in the number field.

## Implementation notes
The definitions that involve a field of fractions choose a canonical field of fractions,
but are independent of that choice.

## References
* [D. Marcus, *Number Fields*][marcus1977number]
* [J.W.S. Cassels, A. Frölich, *Algebraic Number Theory*][cassels1967algebraic]
* [P. Samuel, *Algebraic Theory of Numbers*][samuel1970algebraic]

## Tags
number field, ring of integers
-/

/-- A number field is a field which has characteristic zero and is finite
dimensional over ℚ. -/
class number_field (K : Type*) [field K] : Prop :=
[to_char_zero : char_zero K]
[to_finite_dimensional : finite_dimensional ℚ K]

open function module
open_locale classical big_operators non_zero_divisors

/-- `ℤ` with its usual ring structure is not a field. -/
lemma int.not_is_field : ¬ is_field ℤ :=
λ h, int.not_even_one $ (h.mul_inv_cancel two_ne_zero).imp $ λ a, (by rw ← two_mul; exact eq.symm)

namespace number_field

variables (K L : Type*) [field K] [field L] [nf : number_field K]

include nf

-- See note [lower instance priority]
attribute [priority 100, instance] number_field.to_char_zero number_field.to_finite_dimensional

protected lemma is_algebraic : algebra.is_algebraic ℚ K := algebra.is_algebraic_of_finite _ _

omit nf

/-- The ring of integers (or number ring) corresponding to a number field
is the integral closure of ℤ in the number field. -/
def ring_of_integers := integral_closure ℤ K

localized "notation (name := ring_of_integers)
  `𝓞` := number_field.ring_of_integers" in number_field

lemma mem_ring_of_integers (x : K) : x ∈ 𝓞 K ↔ is_integral ℤ x := iff.rfl

lemma is_integral_of_mem_ring_of_integers {K : Type*} [field K] {x : K} (hx : x ∈ 𝓞 K) :
  is_integral ℤ (⟨x, hx⟩ : 𝓞 K) :=
begin
  obtain ⟨P, hPm, hP⟩ := hx,
  refine ⟨P, hPm, _⟩,
  rw [← polynomial.aeval_def, ← subalgebra.coe_eq_zero, polynomial.aeval_subalgebra_coe,
    polynomial.aeval_def,  subtype.coe_mk, hP]
end

/-- Given an algebra between two fields, create an algebra between their two rings of integers.

For now, this is not an instance by default as it creates an equal-but-not-defeq diamond with
`algebra.id` when `K = L`. This is caused by `x = ⟨x, x.prop⟩` not being defeq on subtypes. This
will likely change in Lean 4. -/
def ring_of_integers_algebra [algebra K L] : algebra (𝓞 K) (𝓞 L) := ring_hom.to_algebra
{ to_fun := λ k, ⟨algebra_map K L k, is_integral.algebra_map k.2⟩,
  map_zero' := subtype.ext $ by simp only [subtype.coe_mk, subalgebra.coe_zero, map_zero],
  map_one'  := subtype.ext $ by simp only [subtype.coe_mk, subalgebra.coe_one, map_one],
  map_add' := λ x y, subtype.ext $ by simp only [map_add, subalgebra.coe_add, subtype.coe_mk],
  map_mul' := λ x y, subtype.ext $ by simp only [subalgebra.coe_mul, map_mul, subtype.coe_mk] }

namespace ring_of_integers

variables {K}

instance [number_field K] : is_fraction_ring (𝓞 K) K :=
integral_closure.is_fraction_ring_of_finite_extension ℚ _

instance : is_integral_closure (𝓞 K) ℤ K :=
integral_closure.is_integral_closure _ _

instance [number_field K] : is_integrally_closed (𝓞 K) :=
integral_closure.is_integrally_closed_of_finite_extension ℚ

lemma is_integral_coe (x : 𝓞 K) : is_integral ℤ (x : K) :=
x.2

lemma map_mem {F L : Type*} [field L] [char_zero K] [char_zero L]
  [alg_hom_class F ℚ K L] (f : F) (x : 𝓞 K) : f x ∈ 𝓞 L :=
(mem_ring_of_integers _ _).2 $ map_is_integral_int f $ ring_of_integers.is_integral_coe x

/-- The ring of integers of `K` are equivalent to any integral closure of `ℤ` in `K` -/
protected noncomputable def equiv (R : Type*) [comm_ring R] [algebra R K]
  [is_integral_closure R ℤ K] : 𝓞 K ≃+* R :=
(is_integral_closure.equiv ℤ R K _).symm.to_ring_equiv

variable (K)
include nf

instance : char_zero (𝓞 K) := char_zero.of_module _ K

instance : is_noetherian ℤ (𝓞 K) := is_integral_closure.is_noetherian _ ℚ K _

/-- The ring of integers of a number field is not a field. -/
lemma not_is_field : ¬ is_field (𝓞 K) :=
begin
  have h_inj : function.injective ⇑(algebra_map ℤ (𝓞 K)),
  { exact ring_hom.injective_int (algebra_map ℤ (𝓞 K)) },
  intro hf,
  exact int.not_is_field
    (((is_integral_closure.is_integral_algebra ℤ K).is_field_iff_is_field h_inj).mpr hf)
end

instance : is_dedekind_domain (𝓞 K) :=
is_integral_closure.is_dedekind_domain ℤ ℚ K _

instance : free ℤ (𝓞 K) := is_integral_closure.module_free ℤ ℚ K (𝓞 K)

instance : is_localization (algebra.algebra_map_submonoid (𝓞 K) ℤ⁰) K :=
is_integral_closure.is_localization ℤ ℚ K (𝓞 K)

/-- A ℤ-basis of the ring of integers of `K`. -/
noncomputable def basis : basis (free.choose_basis_index ℤ (𝓞 K)) ℤ (𝓞 K) :=
free.choose_basis ℤ (𝓞 K)

end ring_of_integers

include nf

/-- A basis of `K` over `ℚ` that is also a basis of `𝓞 K` over `ℤ`. -/
noncomputable def integral_basis : basis (free.choose_basis_index ℤ (𝓞 K)) ℚ K :=
basis.localization_localization ℚ (non_zero_divisors ℤ) K (ring_of_integers.basis K)

@[simp]
lemma integral_basis_apply (i : free.choose_basis_index ℤ (𝓞 K)) :
  integral_basis K i = algebra_map (𝓞 K) K (ring_of_integers.basis K i) :=
basis.localization_localization_apply ℚ (non_zero_divisors ℤ) K (ring_of_integers.basis K) i

lemma ring_of_integers.rank  :
  finite_dimensional.finrank ℤ (𝓞 K) = finite_dimensional.finrank ℚ K :=
is_integral_closure.rank ℤ ℚ K (𝓞 K)

end number_field

namespace rat

open number_field

instance number_field : number_field ℚ :=
{ to_char_zero := infer_instance,
  to_finite_dimensional :=
    -- The vector space structure of `ℚ` over itself can arise in multiple ways:
    -- all fields are vector spaces over themselves (used in `rat.finite_dimensional`)
    -- all char 0 fields have a canonical embedding of `ℚ` (used in `number_field`).
    -- Show that these coincide:
    by convert (infer_instance : finite_dimensional ℚ ℚ), }

/-- The ring of integers of `ℚ` as a number field is just `ℤ`. -/
noncomputable def ring_of_integers_equiv : ring_of_integers ℚ ≃+* ℤ :=
ring_of_integers.equiv ℤ

end rat

namespace adjoin_root

section

open_locale polynomial

local attribute [-instance] algebra_rat

/-- The quotient of `ℚ[X]` by the ideal generated by an irreducible polynomial of `ℚ[X]`
is a number field. -/
instance {f : ℚ[X]} [hf : fact (irreducible f)] : number_field (adjoin_root f) :=
{ to_char_zero := char_zero_of_injective_algebra_map (algebra_map ℚ _).injective,
  to_finite_dimensional := by convert (adjoin_root.power_basis hf.out.ne_zero).finite_dimensional }
end

end adjoin_root