/-
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, Kenny Lau, Johan Commelin, Mario Carneiro, Kevin Buzzard,
Amelia Livingston, Yury Kudryashov
-/
import data.set.lattice
import data.set_like

/-!
# Submonoids: definition and `complete_lattice` structure

This file defines bundled multiplicative and additive submonoids. We also define
a `complete_lattice` structure on `submonoid`s, define the closure of a set as the minimal submonoid
that includes this set, and prove a few results about extending properties from a dense set (i.e.
a set with `closure s = ⊤`) to the whole monoid, see `submonoid.dense_induction` and
`monoid_hom.of_mdense`.

## Main definitions

* `submonoid M`: the type of bundled submonoids of a monoid `M`; the underlying set is given in
  the `carrier` field of the structure, and should be accessed through coercion as in `(S : set M)`.
* `add_submonoid M` : the type of bundled submonoids of an additive monoid `M`.

For each of the following definitions in the `submonoid` namespace, there is a corresponding
definition in the `add_submonoid` namespace.

* `submonoid.copy` : copy of a submonoid with `carrier` replaced by a set that is equal but possibly
  not definitionally equal to the carrier of the original `submonoid`.
* `submonoid.closure` :  monoid closure of a set, i.e., the least submonoid that includes the set.
* `submonoid.gi` : `closure : set M → submonoid M` and coercion `coe : submonoid M → set M`
  form a `galois_insertion`;
* `monoid_hom.eq_mlocus`: the submonoid of elements `x : M` such that `f x = g x`;
* `monoid_hom.of_mdense`:  if a map `f : M → N` between two monoids satisfies `f 1 = 1` and
  `f (x * y) = f x * f y` for `y` from some dense set `s`, then `f` is a monoid homomorphism.
  E.g., if `f : ℕ → M` satisfies `f 0 = 0` and `f (x + 1) = f x + f 1`, then `f` is an additive
  monoid homomorphism.

## Implementation notes

Submonoid inclusion is denoted `≤` rather than `⊆`, although `∈` is defined as
membership of a submonoid's underlying set.

Note that `submonoid M` does not actually require `monoid M`, instead requiring only the weaker
`mul_one_class M`.

This file is designed to have very few dependencies. In particular, it should not use natural
numbers.

## Tags
submonoid, submonoids
-/

variables {M : Type*} {N : Type*}
variables {A : Type*}

section non_assoc
variables [mul_one_class M] {s : set M}
variables [add_zero_class A] {t : set A}

/-- A submonoid of a monoid `M` is a subset containing 1 and closed under multiplication. -/
structure submonoid (M : Type*) [mul_one_class M] :=
(carrier : set M)
(one_mem' : (1 : M) ∈ carrier)
(mul_mem' {a b} : a ∈ carrier → b ∈ carrier → a * b ∈ carrier)

/-- An additive submonoid of an additive monoid `M` is a subset containing 0 and
  closed under addition. -/
structure add_submonoid (M : Type*) [add_zero_class M] :=
(carrier : set M)
(zero_mem' : (0 : M) ∈ carrier)
(add_mem' {a b} : a ∈ carrier → b ∈ carrier → a + b ∈ carrier)

attribute [to_additive] submonoid

namespace submonoid

@[to_additive]
instance : set_like (submonoid M) M :=
⟨submonoid.carrier, λ p q h, by cases p; cases q; congr'⟩

/-- See Note [custom simps projection] -/
@[to_additive " See Note [custom simps projection]"]
def simps.coe (S : submonoid M) : set M := S

initialize_simps_projections submonoid (carrier → coe)
initialize_simps_projections add_submonoid (carrier → coe)

@[simp, to_additive]
lemma mem_carrier {s : submonoid M} {x : M} : x ∈ s.carrier ↔ x ∈ s := iff.rfl

/-- Two submonoids are equal if they have the same elements. -/
@[ext, to_additive "Two `add_submonoid`s are equal if they have the same elements."]

attribute [ext] add_submonoid.ext

/-- Copy a submonoid replacing `carrier` with a set that is equal to it. -/
@[to_additive "Copy an additive submonoid replacing `carrier` with a set that is equal to it."]
protected def copy (S : submonoid M) (s : set M) (hs : s = S) : submonoid M :=
{ carrier := s,
  one_mem' := hs.symm ▸ S.one_mem',
  mul_mem' := hs.symm ▸ S.mul_mem' }

variable {S : submonoid M}

@[simp, to_additive] lemma coe_copy {s : set M} (hs : s = S) :
  (S.copy s hs : set M) = s := rfl

@[to_additive] lemma copy_eq {s : set M} (hs : s = S) : S.copy s hs = S :=
set_like.coe_injective hs

variable (S)

/-- A submonoid contains the monoid's 1. -/
@[to_additive "An `add_submonoid` contains the monoid's 0."]
theorem one_mem : (1 : M) ∈ S := S.one_mem'

/-- A submonoid is closed under multiplication. -/
@[to_additive "An `add_submonoid` is closed under addition."]
theorem mul_mem {x y : M} : x ∈ S → y ∈ S → x * y ∈ S := submonoid.mul_mem' S

/-- The submonoid `M` of the monoid `M`. -/
@[to_additive "The additive submonoid `M` of the `add_monoid M`."]
instance : has_top (submonoid M) :=
⟨{ carrier := set.univ,
   one_mem' := set.mem_univ 1,
   mul_mem' := λ _ _ _ _, set.mem_univ _ }⟩

/-- The trivial submonoid `{1}` of an monoid `M`. -/
@[to_additive "The trivial `add_submonoid` `{0}` of an `add_monoid` `M`."]
instance : has_bot (submonoid M) :=
⟨{ carrier := {1},
   one_mem' := set.mem_singleton 1,
   mul_mem' := λ a b ha hb, by { simp only [set.mem_singleton_iff] at *, rw [ha, hb, mul_one] }}⟩

@[to_additive]
instance : inhabited (submonoid M) := ⟨⊥⟩

@[simp, to_additive] lemma mem_bot {x : M} : x ∈ (⊥ : submonoid M) ↔ x = 1 := set.mem_singleton_iff

@[simp, to_additive] lemma mem_top (x : M) : x ∈ (⊤ : submonoid M) := set.mem_univ x

@[simp, to_additive] lemma coe_top : ((⊤ : submonoid M) : set M) = set.univ := rfl

@[simp, to_additive] lemma coe_bot : ((⊥ : submonoid M) : set M) = {1} := rfl

/-- The inf of two submonoids is their intersection. -/
@[to_additive "The inf of two `add_submonoid`s is their intersection."]
instance : has_inf (submonoid M) :=
⟨λ S₁ S₂,
  { carrier := S₁ ∩ S₂,
    one_mem' := ⟨S₁.one_mem, S₂.one_mem⟩,
    mul_mem' := λ _ _ ⟨hx, hx'⟩ ⟨hy, hy'⟩,
      ⟨S₁.mul_mem hx hy, S₂.mul_mem hx' hy'⟩ }⟩

@[simp, to_additive]
lemma coe_inf (p p' : submonoid M) : ((p ⊓ p' : submonoid M) : set M) = p ∩ p' := rfl

@[simp, to_additive]
lemma mem_inf {p p' : submonoid M} {x : M} : x ∈ p ⊓ p' ↔ x ∈ p ∧ x ∈ p' := iff.rfl

@[to_additive]
instance : has_Inf (submonoid M) :=
⟨λ s, {
  carrier := ⋂ t ∈ s, ↑t,
  one_mem' := set.mem_bInter $ λ i h, i.one_mem,
  mul_mem' := λ x y hx hy, set.mem_bInter $ λ i h,
    i.mul_mem (by apply set.mem_bInter_iff.1 hx i h) (by apply set.mem_bInter_iff.1 hy i h) }⟩

@[simp, to_additive]
lemma coe_Inf (S : set (submonoid M)) : ((Inf S : submonoid M) : set M) = ⋂ s ∈ S, ↑s := rfl

attribute [norm_cast] coe_Inf add_submonoid.coe_Inf

@[to_additive]
lemma mem_Inf {S : set (submonoid M)} {x : M} : x ∈ Inf S ↔ ∀ p ∈ S, x ∈ p := set.mem_bInter_iff

@[to_additive]
lemma mem_infi {ι : Sort*} {S : ι → submonoid M} {x : M} : (x ∈ ⨅ i, S i) ↔ ∀ i, x ∈ S i :=
by simp only [infi, mem_Inf, set.forall_range_iff]

@[simp, to_additive]
lemma coe_infi {ι : Sort*} {S : ι → submonoid M} : (↑(⨅ i, S i) : set M) = ⋂ i, S i :=
by simp only [infi, coe_Inf, set.bInter_range]

attribute [norm_cast] coe_infi add_submonoid.coe_infi

/-- Submonoids of a monoid form a complete lattice. -/
@[to_additive "The `add_submonoid`s of an `add_monoid` form a complete lattice."]
instance : complete_lattice (submonoid M) :=
{ le           := (≤),
  lt           := (<),
  bot          := (⊥),
  bot_le       := λ S x hx, (mem_bot.1 hx).symm ▸ S.one_mem,
  top          := (⊤),
  le_top       := λ S x hx, mem_top x,
  inf          := (⊓),
  Inf          := has_Inf.Inf,
  le_inf       := λ a b c ha hb x hx, ⟨ha hx, hb hx⟩,
  inf_le_left  := λ a b x, and.left,
  inf_le_right := λ a b x, and.right,
  .. complete_lattice_of_Inf (submonoid M) $ λ s,
    is_glb.of_image (λ S T,
      show (S : set M) ≤ T ↔ S ≤ T, from set_like.coe_subset_coe) is_glb_binfi }

@[to_additive]
lemma subsingleton_iff : subsingleton M ↔ subsingleton (submonoid M) :=
⟨ λ h, by exactI ⟨λ x y, submonoid.ext $ λ i, subsingleton.elim 1 i ▸ by simp [submonoid.one_mem]⟩,
  λ h, by exactI ⟨λ x y,
    have ∀ i : M, i = 1 := λ i, mem_bot.mp $ subsingleton.elim (⊤ : submonoid M) ⊥ ▸ mem_top i,
    (this x).trans (this y).symm⟩⟩

@[to_additive]
lemma nontrivial_iff : nontrivial M ↔ nontrivial (submonoid M) :=
not_iff_not.mp (
  (not_nontrivial_iff_subsingleton.trans subsingleton_iff).trans
  not_nontrivial_iff_subsingleton.symm)

@[to_additive]
instance [subsingleton M] : unique (submonoid M) :=
⟨⟨⊥⟩, λ a, @subsingleton.elim _ (subsingleton_iff.mp ‹_›) a _⟩

@[to_additive]
instance [nontrivial M] : nontrivial (submonoid M) := nontrivial_iff.mp ‹_›

/-- The `submonoid` generated by a set. -/
@[to_additive "The `add_submonoid` generated by a set"]
def closure (s : set M) : submonoid M := Inf {S | s ⊆ S}

@[to_additive]
lemma mem_closure {x : M} : x ∈ closure s ↔ ∀ S : submonoid M, s ⊆ S → x ∈ S :=
mem_Inf

/-- The submonoid generated by a set includes the set. -/
@[simp, to_additive "The `add_submonoid` generated by a set includes the set."]
lemma subset_closure : s ⊆ closure s := λ x hx, mem_closure.2 $ λ S hS, hS hx

variable {S}
open set

/-- A submonoid `S` includes `closure s` if and only if it includes `s`. -/
@[simp, to_additive "An additive submonoid `S` includes `closure s` if and only if it includes `s`"]
lemma closure_le : closure s ≤ S ↔ s ⊆ S :=
⟨subset.trans subset_closure, λ h, Inf_le h⟩

/-- Submonoid closure of a set is monotone in its argument: if `s ⊆ t`,
then `closure s ≤ closure t`. -/
@[to_additive "Additive submonoid closure of a set is monotone in its argument: if `s ⊆ t`,
then `closure s ≤ closure t`"]
lemma closure_mono ⦃s t : set M⦄ (h : s ⊆ t) : closure s ≤ closure t :=
closure_le.2 $ subset.trans h subset_closure

@[to_additive]
lemma closure_eq_of_le (h₁ : s ⊆ S) (h₂ : S ≤ closure s) : closure s = S :=
le_antisymm (closure_le.2 h₁) h₂

variable (S)

/-- An induction principle for closure membership. If `p` holds for `1` and all elements of `s`, and
is preserved under multiplication, then `p` holds for all elements of the closure of `s`. -/
@[to_additive "An induction principle for additive closure membership. If `p` holds for `0` and all
elements of `s`, and is preserved under addition, then `p` holds for all elements
of the additive closure of `s`."]
lemma closure_induction {p : M → Prop} {x} (h : x ∈ closure s)
  (Hs : ∀ x ∈ s, p x) (H1 : p 1)
  (Hmul : ∀ x y, p x → p y → p (x * y)) : p x :=
(@closure_le _ _ _ ⟨p, H1, Hmul⟩).2 Hs h

attribute [elab_as_eliminator] submonoid.closure_induction add_submonoid.closure_induction

/-- If `s` is a dense set in a monoid `M`, `submonoid.closure s = ⊤`, then in order to prove that
some predicate `p` holds for all `x : M` it suffices to verify `p x` for `x ∈ s`, verify `p 1`,
and verify that `p x` and `p y` imply `p (x * y)`. -/
@[to_additive]
lemma dense_induction {p : M → Prop} (x : M) {s : set M} (hs : closure s = ⊤)
  (Hs : ∀ x ∈ s, p x) (H1 : p 1)
  (Hmul : ∀ x y, p x → p y → p (x * y)) : p x :=
have ∀ x ∈ closure s, p x, from λ x hx, closure_induction hx Hs H1 Hmul,
by simpa [hs] using this x

/-- If `s` is a dense set in an additive monoid `M`, `add_submonoid.closure s = ⊤`, then in order
to prove that some predicate `p` holds for all `x : M` it suffices to verify `p x` for `x ∈ s`,
verify `p 0`, and verify that `p x` and `p y` imply `p (x + y)`. -/
add_decl_doc add_submonoid.dense_induction

attribute [elab_as_eliminator] dense_induction add_submonoid.dense_induction

variable (M)

/-- `closure` forms a Galois insertion with the coercion to set. -/
@[to_additive "`closure` forms a Galois insertion with the coercion to set."]
protected def gi : galois_insertion (@closure M _) coe :=
{ choice := λ s _, closure s,
  gc := λ s t, closure_le,
  le_l_u := λ s, subset_closure,
  choice_eq := λ s h, rfl }

variable {M}

/-- Closure of a submonoid `S` equals `S`. -/
@[simp, to_additive "Additive closure of an additive submonoid `S` equals `S`"]
lemma closure_eq : closure (S : set M) = S := (submonoid.gi M).l_u_eq S

@[simp, to_additive] lemma closure_empty : closure (∅ : set M) = ⊥ :=
(submonoid.gi M).gc.l_bot

@[simp, to_additive] lemma closure_univ : closure (univ : set M) = ⊤ :=
@coe_top M _ ▸ closure_eq ⊤

@[to_additive]
lemma closure_union (s t : set M) : closure (s ∪ t) = closure s ⊔ closure t :=
(submonoid.gi M).gc.l_sup

@[to_additive]
lemma closure_Union {ι} (s : ι → set M) : closure (⋃ i, s i) = ⨆ i, closure (s i) :=
(submonoid.gi M).gc.l_supr

end submonoid

namespace monoid_hom

variables [mul_one_class N]

open submonoid

/-- The submonoid of elements `x : M` such that `f x = g x` -/
@[to_additive "The additive submonoid of elements `x : M` such that `f x = g x`"]
def eq_mlocus (f g : M →* N) : submonoid M :=
{ carrier := {x | f x = g x},
  one_mem' := by rw [set.mem_set_of_eq, f.map_one, g.map_one],
  mul_mem' := λ x y (hx : _ = _) (hy : _ = _), by simp [*] }

/-- If two monoid homomorphisms are equal on a set, then they are equal on its submonoid closure. -/
@[to_additive]
lemma eq_on_mclosure {f g : M →* N} {s : set M} (h : set.eq_on f g s) :
  set.eq_on f g (closure s) :=
show closure s ≤ f.eq_mlocus g, from closure_le.2 h

@[to_additive]
lemma eq_of_eq_on_mtop {f g : M →* N} (h : set.eq_on f g (⊤ : submonoid M)) :
  f = g :=
ext $ λ x, h trivial

@[to_additive]
lemma eq_of_eq_on_mdense {s : set M} (hs : closure s = ⊤) {f g : M →* N} (h : s.eq_on f g) :
  f = g :=
eq_of_eq_on_mtop $ hs ▸ eq_on_mclosure h

end monoid_hom

end non_assoc

section assoc

variables [monoid M] [monoid N] {s : set M}

section is_unit

/-- The submonoid consisting of the units of a monoid -/
def is_unit.submonoid (M : Type*) [monoid M] : submonoid M :=
{ carrier := set_of is_unit,
  one_mem' := by simp only [is_unit_one, set.mem_set_of_eq],
  mul_mem' := by { intros a b ha hb, rw set.mem_set_of_eq at *, exact is_unit.mul ha hb } }

lemma is_unit.mem_submonoid_iff {M : Type*} [monoid M] (a : M) :
  a ∈ is_unit.submonoid M ↔ is_unit a :=
begin
  change a ∈ set_of is_unit ↔ is_unit a,
  rw set.mem_set_of_eq
end

end is_unit

namespace monoid_hom

open submonoid

/-- Let `s` be a subset of a monoid `M` such that the closure of `s` is the whole monoid.
Then `monoid_hom.of_mdense` defines a monoid homomorphism from `M` asking for a proof
of `f (x * y) = f x * f y` only for `y ∈ s`. -/
@[to_additive]
def of_mdense {M N} [monoid M] [monoid N] {s : set M} (f : M → N) (hs : closure s = ⊤)
  (h1 : f 1 = 1) (hmul : ∀ x (y ∈ s), f (x * y) = f x * f y) :
  M →* N :=
{ to_fun := f,
  map_one' := h1,
  map_mul' := λ x y, dense_induction y hs (λ y hy x, hmul x y hy) (by simp [h1])
    (λ y₁ y₂ h₁ h₂ x, by simp only [← mul_assoc, h₁, h₂]) x }

/-- Let `s` be a subset of an additive monoid `M` such that the closure of `s` is the whole monoid.
Then `add_monoid_hom.of_mdense` defines an additive monoid homomorphism from `M` asking for a proof
of `f (x + y) = f x + f y` only for `y ∈ s`. -/
add_decl_doc add_monoid_hom.of_mdense

@[simp, to_additive] lemma coe_of_mdense (f : M → N) (hs : closure s = ⊤) (h1 hmul) :
  ⇑(of_mdense f hs h1 hmul) = f := rfl

attribute [norm_cast] coe_of_mdense add_monoid_hom.coe_of_mdense

end monoid_hom

end assoc


/-
Copyright (c) 2019 Lucas Allen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Lucas Allen, Scott Morrison
-/

import algebra.big_operators.basic
import data.finsupp.basic
import tactic.converter.apply_congr
import tactic.interactive

example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.sum S f = finset.sum S g :=
begin
  conv_lhs {
    -- If we just call `congr` here, in the second goal we're helpless,
    -- because we are only given the opportunity to rewrite `f`.

    -- However `apply_congr` uses the appropriate `@[congr]` lemma,
    -- so we get to rewrite `f x`, in the presence of the crucial `H : x ∈ S` hypothesis.
    apply_congr,
    skip,
    simp [h, H],
  }
end

-- Again, with some `guard` statements.
example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.sum S f = finset.sum S g :=
begin
  conv_lhs {
    apply_congr finset.sum_congr,
    -- (See the note about get_goals/set_goals inside apply_congr)
    (do ng ← tactic.num_goals, guard $ ng = 2),
    guard_target S,
    skip,
    guard_target f x,
    simp [h, H]
  }
end

-- Verify we can `rw` as well as `simp`.
example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.sum S f = finset.sum S g :=
by conv_lhs { apply_congr, skip, rw h x H, }

-- Check that the appropriate `@[congr]` lemma is automatically selected.
example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.prod S f = finset.prod S g :=
by conv_lhs { apply_congr, skip, simp [h, H], }

example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.fold (+) 0 f S = finset.fold (+) 0 g S :=
begin
  -- This time, the automatically selected congruence lemma is "too good"!
  -- `finset.sum_congr` matches, and so the `conv` block actually
  -- rewrites the left hand side into a `finset.sum`.
  conv_lhs { apply_congr, skip, simp [h, H], },
  -- So we need a `refl` to identify that we're done.
  refl,
end

-- This can be avoided by selecting the congruence lemma by hand.
example (f g : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = g m) :
  finset.fold (+) 0 f S = finset.fold (+) 0 g S :=
begin
  conv_lhs { apply_congr finset.fold_congr, simp [h, H], },
end

example (f : ℤ → ℤ) (S : finset ℤ) (h : ∀ m ∈ S, f m = 0) :
  finset.sum S f = 0 :=
begin
  conv_lhs { apply_congr, skip, simp [h, H], },
  simp,
end

-- An example using `finsupp.sum`
open_locale classical

example {k G : Type} [semiring k] [group G]
  (g : G →₀ k) (a₁ x : G) (b₁ : k)
  (t : ∀ (a₂ : G), a₁ * a₂ = x ↔ a₁⁻¹ * x = a₂) :
  g.sum (λ (a₂ : G) (b₂ : k), ite (a₁ * a₂ = x) (b₁ * b₂) 0) = b₁ * g (a₁⁻¹ * x) :=
begin
  -- In fact, `congr` works fine here, because our rewrite works globally.
  conv_lhs { apply_congr, skip, dsimp, rw t, },
  rw finset.sum_ite_eq g.support, -- it's a pity we can't just use `simp` here.
  split_ifs,
  { refl, },
  { simp [finsupp.not_mem_support_iff.1 h], },
end

example : true :=
begin
  success_if_fail { conv { apply_congr, }, },
  trivial
end


/-
Copyright (c) 2022 Jireh Loreaux. All rights reserved.
Reeased under Apache 2.0 license as described in the file LICENSE.
Authors: Jireh Loreaux
-/
import analysis.normed_space.star.spectrum
import analysis.normed.group.quotient
import analysis.normed_space.algebra
import topology.continuous_function.units
import topology.continuous_function.compact
import topology.algebra.algebra
import topology.continuous_function.stone_weierstrass

/-!
# Gelfand Duality

The `gelfand_transform` is an algebra homomorphism from a topological `𝕜`-algebra `A` to
`C(character_space 𝕜 A, 𝕜)`. In the case where `A` is a commutative complex Banach algebra, then
the Gelfand transform is actually spectrum-preserving (`spectrum.gelfand_transform_eq`). Moreover,
when `A` is a commutative C⋆-algebra over `ℂ`, then the Gelfand transform is a surjective isometry,
and even an equivalence between C⋆-algebras.

## Main definitions

* `ideal.to_character_space` : constructs an element of the character space from a maximal ideal in
  a commutative complex Banach algebra
* `weak_dual.character_space.comp_continuous_map`: The functorial map taking `ψ : A →⋆ₐ[ℂ] B` to a
  continuous function `character_space ℂ B → character_space ℂ A` given by pre-composition with `ψ`.

## Main statements

* `spectrum.gelfand_transform_eq` : the Gelfand transform is spectrum-preserving when the algebra is
  a commutative complex Banach algebra.
* `gelfand_transform_isometry` : the Gelfand transform is an isometry when the algebra is a
  commutative (unital) C⋆-algebra over `ℂ`.
* `gelfand_transform_bijective` : the Gelfand transform is bijective when the algebra is a
  commutative (unital) C⋆-algebra over `ℂ`.

## TODO

* After `star_alg_equiv` is defined, realize `gelfand_transform` as a `star_alg_equiv`.
* Prove that if `A` is the unital C⋆-algebra over `ℂ` generated by a fixed normal element `x` in
  a larger C⋆-algebra `B`, then `character_space ℂ A` is homeomorphic to `spectrum ℂ x`.
* From the previous result, construct the **continuous functional calculus**.
* Show that if `X` is a compact Hausdorff space, then `X` is (canonically) homeomorphic to
  `character_space ℂ C(X, ℂ)`.
* Conclude using the previous fact that the functors `C(⬝, ℂ)` and `character_space ℂ ⬝` along with
  the canonical homeomorphisms described above constitute a natural contravariant equivalence of
  the categories of compact Hausdorff spaces (with continuous maps) and commutative unital
  C⋆-algebras (with unital ⋆-algebra homomoprhisms); this is known as **Gelfand duality**.

## Tags

Gelfand transform, character space, C⋆-algebra
-/

open weak_dual
open_locale nnreal

section complex_banach_algebra
open ideal

variables {A : Type*} [normed_comm_ring A] [normed_algebra ℂ A] [complete_space A]
  (I : ideal A) [ideal.is_maximal I]

/-- Every maximal ideal in a commutative complex Banach algebra gives rise to a character on that
algebra. In particular, the character, which may be identified as an algebra homomorphism due to
`weak_dual.character_space.equiv_alg_hom`, is given by the composition of the quotient map and
the Gelfand-Mazur isomorphism `normed_ring.alg_equiv_complex_of_complete`. -/
noncomputable def ideal.to_character_space : character_space ℂ A :=
character_space.equiv_alg_hom.symm $ ((@normed_ring.alg_equiv_complex_of_complete (A ⧸ I) _ _
  (by { letI := quotient.field I, exact @is_unit_iff_ne_zero (A ⧸ I) _ }) _).symm :
  A ⧸ I →ₐ[ℂ] ℂ).comp
  (quotient.mkₐ ℂ I)

lemma ideal.to_character_space_apply_eq_zero_of_mem {a : A} (ha : a ∈ I) :
  I.to_character_space a = 0 :=
begin
  unfold ideal.to_character_space,
  simpa only [character_space.equiv_alg_hom_symm_coe, alg_hom.coe_comp,
    alg_equiv.coe_alg_hom, quotient.mkₐ_eq_mk, function.comp_app, quotient.eq_zero_iff_mem.mpr ha,
    spectrum.zero_eq, normed_ring.alg_equiv_complex_of_complete_symm_apply]
    using set.eq_of_mem_singleton (set.singleton_nonempty (0 : ℂ)).some_mem,
end

/-- If `a : A` is not a unit, then some character takes the value zero at `a`. This is equivlaent
to `gelfand_transform ℂ A a` takes the value zero at some character. -/
lemma weak_dual.character_space.exists_apply_eq_zero {a : A} (ha : ¬ is_unit a) :
  ∃ f : character_space ℂ A, f a = 0 :=
begin
  unfreezingI { obtain ⟨M, hM, haM⟩ := (span {a}).exists_le_maximal (span_singleton_ne_top ha) },
  exact ⟨M.to_character_space, M.to_character_space_apply_eq_zero_of_mem
    (haM (mem_span_singleton.mpr ⟨1, (mul_one a).symm⟩))⟩,
end

lemma weak_dual.character_space.mem_spectrum_iff_exists {a : A} {z : ℂ} :
  z ∈ spectrum ℂ a ↔ ∃ f : character_space ℂ A, f a = z :=
begin
  refine ⟨λ hz, _, _⟩,
  { obtain ⟨f, hf⟩ := weak_dual.character_space.exists_apply_eq_zero hz,
    simp only [map_sub, sub_eq_zero, alg_hom_class.commutes, algebra.id.map_eq_id,
      ring_hom.id_apply] at hf,
    exact (continuous_map.spectrum_eq_range (gelfand_transform ℂ A a)).symm ▸ ⟨f, hf.symm⟩ },
  { rintro ⟨f, rfl⟩,
    exact alg_hom.apply_mem_spectrum f a, }
end

/-- The Gelfand transform is spectrum-preserving. -/
lemma spectrum.gelfand_transform_eq (a : A) : spectrum ℂ (gelfand_transform ℂ A a) = spectrum ℂ a :=
begin
  ext z,
  rw [continuous_map.spectrum_eq_range, weak_dual.character_space.mem_spectrum_iff_exists],
  exact iff.rfl,
end

instance [nontrivial A] : nonempty (character_space ℂ A) :=
⟨classical.some $ weak_dual.character_space.exists_apply_eq_zero $ zero_mem_nonunits.2 zero_ne_one⟩

end complex_banach_algebra

section complex_cstar_algebra

variables {A : Type*} [normed_comm_ring A] [normed_algebra ℂ A] [complete_space A]
variables [star_ring A] [cstar_ring A] [star_module ℂ A]

lemma gelfand_transform_map_star (a : A) :
  gelfand_transform ℂ A (star a) = star (gelfand_transform ℂ A a) :=
continuous_map.ext $ λ φ, map_star φ a

variable (A)

/-- The Gelfand transform is an isometry when the algebra is a C⋆-algebra over `ℂ`. -/
lemma gelfand_transform_isometry : isometry (gelfand_transform ℂ A) :=
begin
  nontriviality A,
  refine add_monoid_hom_class.isometry_of_norm (gelfand_transform ℂ A) (λ a, _),
  /- By `spectrum.gelfand_transform_eq`, the spectra of `star a * a` and its
  `gelfand_transform` coincide. Therefore, so do their spectral radii, and since they are
  self-adjoint, so also do their norms. Applying the C⋆-property of the norm and taking square
  roots shows that the norm is preserved. -/
  have : spectral_radius ℂ (gelfand_transform ℂ A (star a * a)) = spectral_radius ℂ (star a * a),
  { unfold spectral_radius, rw spectrum.gelfand_transform_eq, },
  simp only [map_mul, (is_self_adjoint.star_mul_self _).spectral_radius_eq_nnnorm,
    gelfand_transform_map_star a, ennreal.coe_eq_coe, cstar_ring.nnnorm_star_mul_self, ←sq] at this,
  simpa only [function.comp_app, nnreal.sqrt_sq]
    using congr_arg ((coe : ℝ≥0 → ℝ) ∘ ⇑nnreal.sqrt) this,
end

/-- The Gelfand transform is bijective when the algebra is a C⋆-algebra over `ℂ`. -/
lemma gelfand_transform_bijective : function.bijective (gelfand_transform ℂ A) :=
begin
  refine ⟨(gelfand_transform_isometry A).injective, _⟩,
  suffices : (gelfand_transform ℂ A).range = ⊤,
  { exact λ x, this.symm ▸ (gelfand_transform ℂ A).mem_range.mp (this.symm ▸ algebra.mem_top) },
  /- Because the `gelfand_transform ℂ A` is an isometry, it has closed range, and so by the
  Stone-Weierstrass theorem, it suffices to show that the image of the Gelfand transform separates
  points in `C(character_space ℂ A, ℂ)` and is closed under `star`. -/
  have h : (gelfand_transform ℂ A).range.topological_closure = (gelfand_transform ℂ A).range,
  from le_antisymm (subalgebra.topological_closure_minimal _ le_rfl
    (gelfand_transform_isometry A).closed_embedding.closed_range)
    (subalgebra.le_topological_closure _),
  refine h ▸ continuous_map.subalgebra_is_R_or_C_topological_closure_eq_top_of_separates_points
    _ (λ _ _, _) (λ f hf, _),
  /- Separating points just means that elements of the `character_space` which agree at all points
  of `A` are the same functional, which is just extensionality. -/
  { contrapose!,
    exact λ h, subtype.ext (continuous_linear_map.ext $
      λ a, h (gelfand_transform ℂ A a) ⟨gelfand_transform ℂ A a, ⟨a, rfl⟩, rfl⟩), },
  /- If `f = gelfand_transform ℂ A a`, then `star f` is also in the range of `gelfand_transform ℂ A`
  using the argument `star a`. The key lemma below may be hard to spot; it's `map_star` coming from
  `weak_dual.star_hom_class`, which is a nontrivial result. -/
  { obtain ⟨f, ⟨a, rfl⟩, rfl⟩ := subalgebra.mem_map.mp hf,
    refine ⟨star a, continuous_map.ext $ λ ψ, _⟩,
    simpa only [gelfand_transform_map_star a, alg_hom.to_ring_hom_eq_coe, alg_hom.coe_to_ring_hom] }
end

/-- The Gelfand transform as a `star_alg_equiv` between a commutative unital C⋆-algebra over `ℂ`
and the continuous functions on its `character_space`. -/
@[simps]
noncomputable def gelfand_star_transform : A ≃⋆ₐ[ℂ] C(character_space ℂ A, ℂ) :=
star_alg_equiv.of_bijective
  (show A →⋆ₐ[ℂ] C(character_space ℂ A, ℂ),
    from { map_star' := λ x, gelfand_transform_map_star x, .. gelfand_transform ℂ A })
  (gelfand_transform_bijective A)

end complex_cstar_algebra

section functoriality

namespace weak_dual

namespace character_space

variables {A B C : Type*}
variables [normed_ring A] [normed_algebra ℂ A] [complete_space A] [star_ring A]
variables [normed_ring B] [normed_algebra ℂ B] [complete_space B] [star_ring B]
variables [normed_ring C] [normed_algebra ℂ C] [complete_space C] [star_ring C]

/-- The functorial map taking `ψ : A →⋆ₐ[ℂ] B` to a continuous function
`character_space ℂ B → character_space ℂ A` obtained by pre-composition with `ψ`. -/
@[simps]
noncomputable def comp_continuous_map (ψ : A →⋆ₐ[ℂ] B) :
  C(character_space ℂ B, character_space ℂ A) :=
{ to_fun := λ φ, equiv_alg_hom.symm ((equiv_alg_hom φ).comp (ψ.to_alg_hom)),
  continuous_to_fun := continuous.subtype_mk (continuous_of_continuous_eval $
    λ a, map_continuous $ gelfand_transform ℂ B (ψ a)) _ }

variables (A)

/-- `weak_dual.character_space.comp_continuous_map` sends the identity to the identity. -/
@[simp] lemma comp_continuous_map_id :
  comp_continuous_map (star_alg_hom.id ℂ A) = continuous_map.id (character_space ℂ A) :=
continuous_map.ext $ λ a, ext $ λ x, rfl

variables {A}

/-- `weak_dual.character_space.comp_continuous_map` is functorial. -/
@[simp] lemma comp_continuous_map_comp (ψ₂ : B →⋆ₐ[ℂ] C) (ψ₁ : A →⋆ₐ[ℂ] B) :
  comp_continuous_map (ψ₂.comp ψ₁) = (comp_continuous_map ψ₁).comp (comp_continuous_map ψ₂) :=
continuous_map.ext $ λ a, ext $ λ x, rfl

end character_space

end weak_dual

end functoriality


/-
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, Callum Sutton, Yury Kudryashov
-/
import data.equiv.mul_add
import algebra.field.basic
import algebra.ring.opposite
import algebra.big_operators.basic

/-!
# (Semi)ring equivs

In this file we define extension of `equiv` called `ring_equiv`, which is a datatype representing an
isomorphism of `semiring`s, `ring`s, `division_ring`s, or `field`s. We also introduce the
corresponding group of automorphisms `ring_aut`.

## Notations

* ``infix ` ≃+* `:25 := ring_equiv``

The extended equiv have coercions to functions, and the coercion is the canonical notation when
treating the isomorphism as maps.

## Implementation notes

The fields for `ring_equiv` now avoid the unbundled `is_mul_hom` and `is_add_hom`, as these are
deprecated.

Definition of multiplication in the groups of automorphisms agrees with function composition,
multiplication in `equiv.perm`, and multiplication in `category_theory.End`, not with
`category_theory.comp`.

## Tags

equiv, mul_equiv, add_equiv, ring_equiv, mul_aut, add_aut, ring_aut
-/

open_locale big_operators

variables {R : Type*} {S : Type*} {S' : Type*}

set_option old_structure_cmd true

/-- An equivalence between two (semi)rings that preserves the algebraic structure. -/
structure ring_equiv (R S : Type*) [has_mul R] [has_add R] [has_mul S] [has_add S]
  extends R ≃ S, R ≃* S, R ≃+ S

infix ` ≃+* `:25 := ring_equiv

/-- The "plain" equivalence of types underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_equiv

/-- The equivalence of additive monoids underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_add_equiv

/-- The equivalence of multiplicative monoids underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_mul_equiv

namespace ring_equiv

section basic

variables [has_mul R] [has_add R] [has_mul S] [has_add S] [has_mul S'] [has_add S']

instance : has_coe_to_fun (R ≃+* S) (λ _, R → S) := ⟨ring_equiv.to_fun⟩

@[simp] lemma to_fun_eq_coe (f : R ≃+* S) : f.to_fun = f := rfl

/-- A ring isomorphism preserves multiplication. -/
@[simp] lemma map_mul (e : R ≃+* S) (x y : R) : e (x * y) = e x * e y := e.map_mul' x y

/-- A ring isomorphism preserves addition. -/
@[simp] lemma map_add (e : R ≃+* S) (x y : R) : e (x + y) = e x + e y := e.map_add' x y

/-- Two ring isomorphisms agree if they are defined by the
    same underlying function. -/
@[ext] lemma ext {f g : R ≃+* S} (h : ∀ x, f x = g x) : f = g :=
begin
  have h₁ : f.to_equiv = g.to_equiv := equiv.ext h,
  cases f, cases g, congr,
  { exact (funext h) },
  { exact congr_arg equiv.inv_fun h₁ }
end

@[simp] theorem coe_mk (e e' h₁ h₂ h₃ h₄) :
  ⇑(⟨e, e', h₁, h₂, h₃, h₄⟩ : R ≃+* S) = e := rfl

@[simp] theorem mk_coe (e : R ≃+* S) (e' h₁ h₂ h₃ h₄) :
  (⟨e, e', h₁, h₂, h₃, h₄⟩ : R ≃+* S) = e := ext $ λ _, rfl

protected lemma congr_arg {f : R ≃+* S} : Π {x x' : R}, x = x' → f x = f x'
| _ _ rfl := rfl

protected lemma congr_fun {f g : R ≃+* S} (h : f = g) (x : R) : f x = g x := h ▸ rfl

lemma ext_iff {f g : R ≃+* S} : f = g ↔ ∀ x, f x = g x :=
⟨λ h x, h ▸ rfl, ext⟩

instance has_coe_to_mul_equiv : has_coe (R ≃+* S) (R ≃* S) := ⟨ring_equiv.to_mul_equiv⟩

instance has_coe_to_add_equiv : has_coe (R ≃+* S) (R ≃+ S) := ⟨ring_equiv.to_add_equiv⟩

@[simp] lemma to_add_equiv_eq_coe (f : R ≃+* S) : f.to_add_equiv = ↑f := rfl

@[simp] lemma to_mul_equiv_eq_coe (f : R ≃+* S) : f.to_mul_equiv = ↑f := rfl

@[simp, norm_cast] lemma coe_to_mul_equiv (f : R ≃+* S) : ⇑(f : R ≃* S) = f := rfl

@[simp, norm_cast] lemma coe_to_add_equiv (f : R ≃+* S) : ⇑(f : R ≃+ S) = f := rfl

/-- The `ring_equiv` between two semirings with a unique element. -/
def ring_equiv_of_unique_of_unique {M N}
  [unique M] [unique N] [has_add M] [has_mul M] [has_add N] [has_mul N] : M ≃+* N :=
{ ..add_equiv.add_equiv_of_unique_of_unique,
  ..mul_equiv.mul_equiv_of_unique_of_unique}

instance {M N} [unique M] [unique N] [has_add M] [has_mul M] [has_add N] [has_mul N] :
  unique (M ≃+* N) :=
{ default := ring_equiv_of_unique_of_unique,
  uniq := λ _, ext $ λ x, subsingleton.elim _ _ }

variable (R)

/-- The identity map is a ring isomorphism. -/
@[refl] protected def refl : R ≃+* R := { .. mul_equiv.refl R, .. add_equiv.refl R }

@[simp] lemma refl_apply (x : R) : ring_equiv.refl R x = x := rfl

@[simp] lemma coe_add_equiv_refl : (ring_equiv.refl R : R ≃+ R) = add_equiv.refl R := rfl

@[simp] lemma coe_mul_equiv_refl : (ring_equiv.refl R : R ≃* R) = mul_equiv.refl R := rfl

instance : inhabited (R ≃+* R) := ⟨ring_equiv.refl R⟩

variables {R}

/-- The inverse of a ring isomorphism is a ring isomorphism. -/
@[symm] protected def symm (e : R ≃+* S) : S ≃+* R :=
{ .. e.to_mul_equiv.symm, .. e.to_add_equiv.symm }

/-- See Note [custom simps projection] -/
def simps.symm_apply (e : R ≃+* S) : S → R := e.symm

initialize_simps_projections ring_equiv (to_fun → apply, inv_fun → symm_apply)

@[simp] lemma symm_symm (e : R ≃+* S) : e.symm.symm = e := ext $ λ x, rfl

lemma symm_bijective : function.bijective (ring_equiv.symm : (R ≃+* S) → (S ≃+* R)) :=
equiv.bijective ⟨ring_equiv.symm, ring_equiv.symm, symm_symm, symm_symm⟩

@[simp] lemma mk_coe' (e : R ≃+* S) (f h₁ h₂ h₃ h₄) :
  (ring_equiv.mk f ⇑e h₁ h₂ h₃ h₄ : S ≃+* R) = e.symm :=
symm_bijective.injective $ ext $ λ x, rfl

@[simp] lemma symm_mk (f : R → S) (g h₁ h₂ h₃ h₄) :
  (mk f g h₁ h₂ h₃ h₄).symm =
  { to_fun := g, inv_fun := f, ..(mk f g h₁ h₂ h₃ h₄).symm} := rfl

/-- Transitivity of `ring_equiv`. -/
@[trans] protected def trans (e₁ : R ≃+* S) (e₂ : S ≃+* S') : R ≃+* S' :=
{ .. (e₁.to_mul_equiv.trans e₂.to_mul_equiv), .. (e₁.to_add_equiv.trans e₂.to_add_equiv) }

@[simp] lemma trans_apply (e₁ : R ≃+* S) (e₂ : S ≃+* S') (a : R) :
  e₁.trans e₂ a = e₂ (e₁ a) := rfl

protected lemma bijective (e : R ≃+* S) : function.bijective e := e.to_equiv.bijective
protected lemma injective (e : R ≃+* S) : function.injective e := e.to_equiv.injective
protected lemma surjective (e : R ≃+* S) : function.surjective e := e.to_equiv.surjective

@[simp] lemma apply_symm_apply (e : R ≃+* S) : ∀ x, e (e.symm x) = x := e.to_equiv.apply_symm_apply
@[simp] lemma symm_apply_apply (e : R ≃+* S) : ∀ x, e.symm (e x) = x := e.to_equiv.symm_apply_apply

lemma image_eq_preimage (e : R ≃+* S) (s : set R) : e '' s = e.symm ⁻¹' s :=
e.to_equiv.image_eq_preimage s

end basic

section opposite
open mul_opposite

/-- A ring iso `α ≃+* β` can equivalently be viewed as a ring iso `αᵐᵒᵖ ≃+* βᵐᵒᵖ`. -/
@[simps]
protected def op {α β} [has_add α] [has_mul α] [has_add β] [has_mul β] :
  (α ≃+* β) ≃ (αᵐᵒᵖ ≃+* βᵐᵒᵖ) :=
{ to_fun    := λ f, { ..f.to_add_equiv.op, ..f.to_mul_equiv.op},
  inv_fun   := λ f, { ..(add_equiv.op.symm f.to_add_equiv), ..(mul_equiv.op.symm f.to_mul_equiv) },
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext, refl } }

/-- The 'unopposite' of a ring iso `αᵐᵒᵖ ≃+* βᵐᵒᵖ`. Inverse to `ring_equiv.op`. -/
@[simp] protected def unop {α β} [has_add α] [has_mul α] [has_add β] [has_mul β] :
  (αᵐᵒᵖ ≃+* βᵐᵒᵖ) ≃ (α ≃+* β) := ring_equiv.op.symm

section comm_semiring

variables (R) [comm_semiring R]

/-- A commutative ring is isomorphic to its opposite. -/
def to_opposite : R ≃+* Rᵐᵒᵖ :=
{ map_add' := λ x y, rfl,
  map_mul' := λ x y, mul_comm (op y) (op x),
  .. mul_opposite.op_equiv }

@[simp]
lemma to_opposite_apply (r : R) : to_opposite R r = op r := rfl

@[simp]
lemma to_opposite_symm_apply (r : Rᵐᵒᵖ) : (to_opposite R).symm r = unop r := rfl

end comm_semiring

end opposite

section non_unital_semiring

variables [non_unital_non_assoc_semiring R] [non_unital_non_assoc_semiring S]
  (f : R ≃+* S) (x y : R)

/-- A ring isomorphism sends zero to zero. -/
@[simp] lemma map_zero : f 0 = 0 := (f : R ≃+ S).map_zero

variable {x}

@[simp] lemma map_eq_zero_iff : f x = 0 ↔ x = 0 := (f : R ≃+ S).map_eq_zero_iff

lemma map_ne_zero_iff : f x ≠ 0 ↔ x ≠ 0 := (f : R ≃+ S).map_ne_zero_iff

end non_unital_semiring

section semiring

variables [non_assoc_semiring R] [non_assoc_semiring S] (f : R ≃+* S) (x y : R)

/-- A ring isomorphism sends one to one. -/
@[simp] lemma map_one : f 1 = 1 := (f : R ≃* S).map_one

variable {x}

@[simp] lemma map_eq_one_iff : f x = 1 ↔ x = 1 := (f : R ≃* S).map_eq_one_iff

lemma map_ne_one_iff : f x ≠ 1 ↔ x ≠ 1 := (f : R ≃* S).map_ne_one_iff

/-- Produce a ring isomorphism from a bijective ring homomorphism. -/
noncomputable def of_bijective (f : R →+* S) (hf : function.bijective f) : R ≃+* S :=
{ .. equiv.of_bijective f hf, .. f }

@[simp] lemma coe_of_bijective (f : R →+* S) (hf : function.bijective f) :
  (of_bijective f hf : R → S) = f := rfl

lemma of_bijective_apply (f : R →+* S) (hf : function.bijective f) (x : R) :
  of_bijective f hf x = f x := rfl

end semiring

section

variables [ring R] [ring S] (f : R ≃+* S) (x y : R)

@[simp] lemma map_neg : f (-x) = -f x := (f : R ≃+ S).map_neg x

@[simp] lemma map_sub : f (x - y) = f x - f y := (f : R ≃+ S).map_sub x y

@[simp] lemma map_neg_one : f (-1) = -1 := f.map_one ▸ f.map_neg 1

end

section semiring_hom

variables [non_assoc_semiring R] [non_assoc_semiring S] [non_assoc_semiring S']

/-- Reinterpret a ring equivalence as a ring homomorphism. -/
def to_ring_hom (e : R ≃+* S) : R →+* S :=
{ .. e.to_mul_equiv.to_monoid_hom, .. e.to_add_equiv.to_add_monoid_hom }

lemma to_ring_hom_injective : function.injective (to_ring_hom : (R ≃+* S) → R →+* S) :=
λ f g h, ring_equiv.ext (ring_hom.ext_iff.1 h)

instance has_coe_to_ring_hom : has_coe (R ≃+* S) (R →+* S) := ⟨ring_equiv.to_ring_hom⟩

lemma to_ring_hom_eq_coe (f : R ≃+* S) : f.to_ring_hom = ↑f := rfl

@[simp, norm_cast] lemma coe_to_ring_hom (f : R ≃+* S) : ⇑(f : R →+* S) = f := rfl

lemma coe_ring_hom_inj_iff {R S : Type*} [non_assoc_semiring R] [non_assoc_semiring S]
  (f g : R ≃+* S) :
  f = g ↔ (f : R →+* S) = g :=
⟨congr_arg _, λ h, ext $ ring_hom.ext_iff.mp h⟩

/-- Reinterpret a ring equivalence as a monoid homomorphism. -/
abbreviation to_monoid_hom (e : R ≃+* S) : R →* S := e.to_ring_hom.to_monoid_hom

/-- Reinterpret a ring equivalence as an `add_monoid` homomorphism. -/
abbreviation to_add_monoid_hom (e : R ≃+* S) : R →+ S := e.to_ring_hom.to_add_monoid_hom

/-- The two paths coercion can take to an `add_monoid_hom` are equivalent -/
lemma to_add_monoid_hom_commutes (f : R ≃+* S) :
  (f : R →+* S).to_add_monoid_hom = (f : R ≃+ S).to_add_monoid_hom :=
rfl

/-- The two paths coercion can take to an `monoid_hom` are equivalent -/
lemma to_monoid_hom_commutes (f : R ≃+* S) :
  (f : R →+* S).to_monoid_hom = (f : R ≃* S).to_monoid_hom :=
rfl

/-- The two paths coercion can take to an `equiv` are equivalent -/
lemma to_equiv_commutes (f : R ≃+* S) :
  (f : R ≃+ S).to_equiv = (f : R ≃* S).to_equiv :=
rfl

@[simp]
lemma to_ring_hom_refl : (ring_equiv.refl R).to_ring_hom = ring_hom.id R := rfl

@[simp]
lemma to_monoid_hom_refl : (ring_equiv.refl R).to_monoid_hom = monoid_hom.id R := rfl

@[simp]
lemma to_add_monoid_hom_refl : (ring_equiv.refl R).to_add_monoid_hom = add_monoid_hom.id R := rfl

@[simp]
lemma to_ring_hom_apply_symm_to_ring_hom_apply (e : R ≃+* S) :
  ∀ (y : S), e.to_ring_hom (e.symm.to_ring_hom y) = y :=
e.to_equiv.apply_symm_apply

@[simp]
lemma symm_to_ring_hom_apply_to_ring_hom_apply (e : R ≃+* S) :
  ∀ (x : R), e.symm.to_ring_hom (e.to_ring_hom x) = x :=
equiv.symm_apply_apply (e.to_equiv)

@[simp]
lemma to_ring_hom_trans (e₁ : R ≃+* S) (e₂ : S ≃+* S') :
  (e₁.trans e₂).to_ring_hom = e₂.to_ring_hom.comp e₁.to_ring_hom := rfl

@[simp]
lemma to_ring_hom_comp_symm_to_ring_hom (e : R ≃+* S) :
  e.to_ring_hom.comp e.symm.to_ring_hom = ring_hom.id _ :=
by { ext, simp }

@[simp]
lemma symm_to_ring_hom_comp_to_ring_hom (e : R ≃+* S) :
  e.symm.to_ring_hom.comp e.to_ring_hom = ring_hom.id _ :=
by { ext, simp }

/--
Construct an equivalence of rings from homomorphisms in both directions, which are inverses.
-/
def of_hom_inv (hom : R →+* S) (inv : S →+* R)
  (hom_inv_id : inv.comp hom = ring_hom.id R) (inv_hom_id : hom.comp inv = ring_hom.id S) :
  R ≃+* S :=
{ inv_fun := inv,
  left_inv := λ x, ring_hom.congr_fun hom_inv_id x,
  right_inv := λ x, ring_hom.congr_fun inv_hom_id x,
  ..hom }

@[simp]
lemma of_hom_inv_apply (hom : R →+* S) (inv : S →+* R) (hom_inv_id inv_hom_id) (r : R) :
  (of_hom_inv hom inv hom_inv_id inv_hom_id) r = hom r := rfl

@[simp]
lemma of_hom_inv_symm_apply (hom : R →+* S) (inv : S →+* R) (hom_inv_id inv_hom_id) (s : S) :
  (of_hom_inv hom inv hom_inv_id inv_hom_id).symm s = inv s := rfl

end semiring_hom

section big_operators

lemma map_list_prod [semiring R] [semiring S] (f : R ≃+* S) (l : list R) :
  f l.prod = (l.map f).prod := f.to_ring_hom.map_list_prod l

lemma map_list_sum [non_assoc_semiring R] [non_assoc_semiring S] (f : R ≃+* S) (l : list R) :
  f l.sum = (l.map f).sum := f.to_ring_hom.map_list_sum l

/-- An isomorphism into the opposite ring acts on the product by acting on the reversed elements -/
lemma unop_map_list_prod [semiring R] [semiring S] (f : R ≃+* Sᵐᵒᵖ) (l : list R) :
  mul_opposite.unop (f l.prod) = (l.map (mul_opposite.unop ∘ f)).reverse.prod :=
f.to_ring_hom.unop_map_list_prod l

lemma map_multiset_prod [comm_semiring R] [comm_semiring S] (f : R ≃+* S) (s : multiset R) :
  f s.prod = (s.map f).prod := f.to_ring_hom.map_multiset_prod s

lemma map_multiset_sum [non_assoc_semiring R] [non_assoc_semiring S]
  (f : R ≃+* S) (s : multiset R) : f s.sum = (s.map f).sum := f.to_ring_hom.map_multiset_sum s

lemma map_prod {α : Type*} [comm_semiring R] [comm_semiring S] (g : R ≃+* S) (f : α → R)
  (s : finset α) : g (∏ x in s, f x) = ∏ x in s, g (f x) :=
g.to_ring_hom.map_prod f s

lemma map_sum {α : Type*} [non_assoc_semiring R] [non_assoc_semiring S]
  (g : R ≃+* S) (f : α → R) (s : finset α) : g (∑ x in s, f x) = ∑ x in s, g (f x) :=
g.to_ring_hom.map_sum f s

end big_operators

section division_ring

variables {K K' : Type*} [division_ring K] [division_ring K']
  (g : K ≃+* K') (x y : K)

lemma map_inv : g x⁻¹ = (g x)⁻¹ := g.to_ring_hom.map_inv x

lemma map_div : g (x / y) = g x / g y := g.to_ring_hom.map_div x y

end division_ring

section group_power

variables [semiring R] [semiring S]

@[simp] lemma map_pow (f : R ≃+* S) (a) :
  ∀ n : ℕ, f (a ^ n) = (f a) ^ n :=
f.to_ring_hom.map_pow a

end group_power

end ring_equiv

namespace mul_equiv

/-- Gives a `ring_equiv` from a `mul_equiv` preserving addition.-/
def to_ring_equiv {R : Type*} {S : Type*} [has_add R] [has_add S] [has_mul R] [has_mul S]
  (h : R ≃* S) (H : ∀ x y : R, h (x + y) = h x + h y) : R ≃+* S :=
{..h.to_equiv, ..h, ..add_equiv.mk' h.to_equiv H }

end mul_equiv

namespace ring_equiv

variables [has_add R] [has_add S] [has_mul R] [has_mul S]

@[simp] theorem self_trans_symm (e : R ≃+* S) : e.trans e.symm = ring_equiv.refl R := ext e.3
@[simp] theorem symm_trans_self (e : R ≃+* S) : e.symm.trans e = ring_equiv.refl S := ext e.4

/-- If two rings are isomorphic, and the second is a domain, then so is the first. -/
protected lemma is_domain
  {A : Type*} (B : Type*) [ring A] [ring B] [is_domain B]
  (e : A ≃+* B) : is_domain A :=
{ eq_zero_or_eq_zero_of_mul_eq_zero := λ x y hxy,
    have e x * e y = 0, by rw [← e.map_mul, hxy, e.map_zero],
    by simpa using eq_zero_or_eq_zero_of_mul_eq_zero this,
  exists_pair_ne := ⟨e.symm 0, e.symm 1, e.symm.injective.ne zero_ne_one⟩ }

end ring_equiv


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_numbertheory_101 :
  (17 * 18) % 4 = 2 :=
begin
  norm_num,
end


import .size .induction .single
----------------------------------------------------------------
open_locale classical
noncomputable theory

universes u u' v v' w
-- Lemmas applying for a general nonempty fintype
-- TODO - lemmas about folding, so sum etc is also subsumed

section general_fintype

variables {α : Type*}{α' : Type*}{β : Type*}{β': Type*}
[fintype α] [nonempty α] [linear_order β] [linear_order β']

--instance fin_α : fintype α := by {letI := classical.choice _inst_1, apply_instance, }

/- theorem set.finite.exists_maximal_wrt {α : Type*} {β : Type*} [partial_order β]
(f : α → β) (s : set α) (h : s.finite) :
s.nonempty → (∃ (a : α) (H : a ∈ s), ∀ (a' : α), a' ∈ s → f a ≤ f a' → f a = f a') -/

lemma exists_max (f : α → β) :
  ∃ x, ∀ y, f y ≤ f x :=
fintype.exists_max f
  /-letI := classical.choice _inst_1,
  unfreezingI {obtain ⟨a₀⟩ := _inst_2},
  obtain ⟨a, -, ha⟩ :=
    set.finite.exists_maximal_wrt f set.univ (set.finite.of_fintype _) ⟨a₀, set.mem_univ a₀⟩,
  refine ⟨a, λ x, _⟩,
  rcases le_total (f x) (f a) with (h | h), exact h,
  exact le_of_eq (ha x (set.mem_univ _) h).symm, -/
--end

lemma exists_min (f : α → β) :
  ∃ x, ∀ y, f x ≤ f y :=
let f' : _ → (order_dual β) := f in exists_max f'

/-- maximum value of f -/
def max_val (f : α → β) : β :=
  f (classical.some (exists_max f))

/-- minimum value of f -/
def min_val (f : α → β) : β :=
  f (classical.some (exists_min f))

lemma max_spec (f : α → β) :
  ∃ x, f x = max_val f ∧ ∀ y, f y ≤ f x :=
⟨classical.some (exists_max f), ⟨rfl, classical.some_spec (exists_max f) ⟩⟩

lemma min_spec (f : α → β) :
  ∃ x, f x = min_val f ∧ ∀ y, f x ≤ f y :=
⟨classical.some (exists_min f), ⟨rfl, classical.some_spec (exists_min f) ⟩⟩

lemma max_is_ub (f : α → β) (x : α) :
  f x ≤ max_val f :=
by {cases max_spec f with y hy, rw ←hy.1, apply hy.2}

lemma min_is_lb (f : α → β) (x : α) :
  min_val f ≤ f x :=
by {cases min_spec f with y hy, rw ←hy.1, apply hy.2}

lemma max_of_le_is_le (f f' : α → β) (hff' : ∀ a, f a ≤ f' a) :
  max_val f ≤ max_val f' :=
begin
  cases max_spec f with a ha,
  cases max_spec f' with a' ha',
  rw [←ha.1, ←ha'.1],
  from le_trans (hff' _) (ha'.2 _),
end

lemma min_of_le_is_le (f f' : α → β) (hff' : ∀ a, f a ≤ f' a) :
  min_val f ≤ min_val f' :=
begin
  cases min_spec f with a ha,
  cases min_spec f' with a' ha',
  rw [←ha.1, ←ha'.1],
  from le_trans (ha.2 _) (hff' a'),
end

/-- taking a max over one type is equivalent to taking one over another,
given a bijection between them -/
lemma max_reindex (φ : α → α') (hφ : function.surjective φ) (f : α' → β) :
  max_val (f ∘ φ) = @max_val _ _ (fintype.of_surjective φ hφ)  (nonempty.map φ _inst_2) _ f :=
begin
  rcases @max_spec _ _ (fintype.of_surjective φ hφ ) (nonempty.map φ _inst_2) _ f
    with ⟨x', ⟨hx'1, hx'2⟩⟩,
  rcases max_spec (f ∘ φ) with ⟨x, ⟨hx1, hx2⟩ ⟩,
  rw [←hx1, ←hx'1],
  apply le_antisymm (hx'2 _),
  cases hφ x' with z hz,
  rw ←hz, apply hx2,
end

/-- taking a min over one type is equivalent to taking one over another,
given a bijection between them -/
lemma min_reindex (φ : α → α') (hφ : function.surjective φ) (f : α' → β) :
  min_val (f ∘ φ) = @min_val _ _ (fintype.of_surjective φ hφ ) (nonempty.map φ _inst_2) _ f :=
begin
  rcases @min_spec _ _ (fintype.of_surjective φ hφ ) (nonempty.map φ _inst_2) _ f
    with ⟨x', ⟨hx'1, hx'2⟩⟩,
  rcases min_spec (f ∘ φ) with ⟨x, ⟨hx1, hx2⟩ ⟩,
  rw [←hx1, ←hx'1],
  refine le_antisymm _ (hx'2 _),
  cases hφ x' with z hz,
  rw ←hz, apply hx2,
end

/-- max commutes with composing by a monotone function -/
lemma max_compose_mono (f : α → β) (g : β → β') (hg : monotone g) :
  g (max_val f) = max_val (g ∘ f) :=
begin
  rcases max_spec f with ⟨X, hX₁, hX₂⟩,
  rcases max_spec (g ∘ f) with ⟨X',hX'₁, hX'₂ ⟩,
  erw [←hX'₁ , ←hX₁],
  from le_antisymm (hX'₂ _) (hg (hX₂ _)),
end

/-- min commutes with composing by a monotone function-/
lemma min_compose_mono (f : α → β) (g : β → β') (hg : monotone g) :
  g (min_val f) = min_val (g ∘ f) :=
begin
  rcases min_spec f with ⟨X, hX₁, hX₂⟩,
  rcases min_spec (g ∘ f) with ⟨X',hX'₁, hX'₂⟩,
  rw [←hX'₁, ←hX₁],
  from le_antisymm (hg (hX₂ _)) (hX'₂ _),
end

/-- the max is at most a given upper bound for f -/
lemma max_le_ub {f : α → β} {b : β} (h_ub : ∀ x : α, f x ≤ b) :
  max_val f ≤ b :=
by {cases max_spec f with X hX, rw [←hX.1], apply h_ub}

/-- a given lower bound for f is at most the max of f-/
lemma lb_le_max {f : α → β} (b : β) (h_lb : ∀ x : α, b ≤ f x) :
   b ≤ max_val f :=
by {cases max_spec f with X hX, rw [←hX.1], apply h_lb, }

/-- the min of x is at most a given upper bound for f-/
lemma min_le_ub {f : α → β} {b : β} (h_ub : ∀ x : α, f x ≤ b) :
  min_val f ≤ b :=
by {cases min_spec f with X hX, rw [←hX.1], apply h_ub}

/-- a given lower bound for x is at most the min of x-/
lemma lb_le_min {f : α → β} {b : β} (h_lb : ∀ x : α, b ≤ f x) :
   b ≤ min_val f :=
by {cases min_spec f with X hX, rw [←hX.1], apply h_lb}

/-- an upper bound that is attained by f must be the max -/
lemma attained_ub_is_max (f : α → β) (a : α) :
  (∀ x, f x ≤ f a) → max_val f = f a :=
begin
  intros h_ub,
  rcases max_spec f with ⟨X, hX⟩,
  apply le_antisymm (max_le_ub h_ub),
  rw [←hX.1], apply hX.2,
end

lemma attained_ub_is_max' (f : α → β) (a : α) (b : β) (hba : b ≤ f a) (hub : ∀ x, f x ≤ b):
  max_val f = b :=
by {rw ← le_antisymm (hub a) hba, exact attained_ub_is_max _ _ (λ x, le_trans (hub x) hba)}

/-- a lower bound attained by f must be the min -/
lemma attained_lb_is_min (f : α → β) (a : α) :
  (∀ x, f a ≤ f x) → min_val f = f a  :=
begin
  intros h_lb,
  rcases min_spec f with ⟨X, hX⟩,
  refine le_antisymm _ (lb_le_min h_lb),
  rw [←hX.1], apply hX.2,
end

lemma attained_lb_is_min' (f : α → β) (a : α) (b : β) (hab : f a ≤ b) (hlb : ∀ x, b ≤ f x) :
  min_val f = b :=
by {rw ← le_antisymm hab (hlb a), exact attained_lb_is_min _ _ (λ x, le_trans hab (hlb x))}

/-- the max of a constant function -/
lemma max_const (b : β) :
  max_val (λ (x : α), b) = b :=
by {rcases max_spec (λ (x : α), b) with ⟨x, hx⟩, rw ←hx.1 }

/-- the min of a constant function -/
lemma min_const (b : β) :
  min_val (λ (x : α), b) = b :=
by {rcases min_spec (λ (x : α), b) with ⟨x, hx⟩, rw ←hx.1 }

/-- given a bound f(x) ≤ f(x') for all x,x', a pair a,a' for which f(a) = f(a') determines
the max and min of f,f' respectively  -/
lemma minmax_eq_cert [nonempty α'] [fintype α'] (f : α → β) (f' : α' → β) :
  (∃ a a', f a = f' a') → (∀ x x', f x ≤ f' x') → max_val f = min_val f' :=
begin
  rintros ⟨a, a', heq⟩ hbound,
  rcases max_spec f with ⟨x,hx⟩,
  rcases min_spec f' with ⟨y,hy'⟩,
  have hub := attained_ub_is_max' f a (f' a') heq.symm.le (λ x, hbound x a'),
  have hlb := attained_lb_is_min' f' a' (f a) heq.symm.le (λ x', hbound a x'),
  rw [hub, hlb, heq],
end

lemma max_compose_le_max [non_empt : nonempty α] [fintype α'] (φ : α → α') (f : α' → β) :
  max_val (f ∘ φ) ≤ @max_val _ _ _ (nonempty.map φ non_empt) _ f :=
begin
  rcases max_spec (f ∘ φ) with ⟨a, ⟨ha₁, ha₂⟩⟩,
  rcases @max_spec _ _ _ (nonempty.map φ non_empt) _ f with ⟨a', ⟨ha'₁, ha'₂⟩⟩,
  rw [←ha₁, ←ha'₁],
  apply ha'₂,
end

lemma min_le_min_compose [non_empt : nonempty α] [fintype α'] (φ : α → α') (f : α' → β) :
  @min_val _ _ _ (nonempty.map φ non_empt) _ f  ≤ min_val (f ∘ φ) :=
begin
  rcases min_spec (f ∘ φ) with ⟨a, ⟨ha₁, ha₂⟩⟩,
  rcases @min_spec _ _ _ (nonempty.map φ non_empt) _ f with ⟨a', ⟨ha'₁, ha'₂⟩⟩,
  rw [←ha₁, ←ha'₁],
  apply ha'₂,
end

--instance prod_fin [fintype α']: nonempty (fintype (α × α')) :=
--by { apply_instance, }

/-- a bimonotone function of two maxima is a maximum over a product type -/
lemma max_zip [nonempty α'] [fintype α'] (f : α → β) (f' : α' → β) (g : β × β → β')
                (g_mono : ∀ b₁ b₂ b₁' b₂', b₁ ≤ b₁' → b₂ ≤ b₂' → g ⟨b₁,b₂⟩ ≤ g ⟨b₁',b₂'⟩) :
  g ⟨max_val f, max_val f'⟩ = max_val (λ a : α × α', g ⟨f a.1,f' a.2⟩) :=
let f_prod := (λ a : α × α', g ⟨f a.1,f' a.2⟩) in
begin
  rcases max_spec f with ⟨a, ⟨ha₁, ha₂⟩⟩,
  rcases max_spec f' with ⟨a', ⟨ha'₁, ha'₂⟩⟩,
  rcases max_spec f_prod with ⟨⟨x,x'⟩,⟨hx₁, hx₂⟩⟩,
  rw [←ha₁,←ha'₁,←hx₁],
  apply le_antisymm,
  from hx₂ ⟨a,a'⟩, apply g_mono, apply ha₂, apply ha'₂,
end

/-- a bimonotone function of two minima is a minimum over a product type -/
lemma min_zip [nonempty α'] [fintype α'] (f : α → β) (f' : α' → β) (g : β × β → β')
                (g_mono : ∀ b₁ b₂ b₁' b₂', b₁ ≤ b₁' → b₂ ≤ b₂' → g ⟨b₁,b₂⟩ ≤ g ⟨b₁',b₂'⟩) :
  g ⟨min_val f, min_val f'⟩ = min_val (λ a : α × α', g ⟨f a.1,f' a.2⟩) :=
let f_prod := (λ a : α × α', g ⟨f a.1,f' a.2⟩) in
begin
  obtain ⟨a, ⟨ha₁, ha₂⟩⟩ := min_spec f,
  rcases min_spec f' with ⟨a', ⟨ha'₁, ha'₂⟩⟩,
  rcases min_spec f_prod with ⟨⟨x,x'⟩,⟨hx₁, hx₂⟩⟩,
  rw [←ha₁,←ha'₁,←hx₁],
  apply le_antisymm,
  apply g_mono, apply ha₂, apply ha'₂, from hx₂ ⟨a,a'⟩,
end

end general_fintype

section adding -- lemmas with a little more structure (eg addition) on β

variables {α α' β : Type*} [nonempty α] [fintype α] [nonempty α'] [fintype α'] [linear_ordered_semiring β]

lemma max_add_commute (f : α → β) (s : β) :
  (max_val f) + s = max_val (λ x, f x + s) :=
begin
  set g : β → β := λ x, x + s with hg,
  have hg_mono : monotone g :=
    λ x y hxy, by {rw hg, dsimp only, apply add_le_add_right hxy},
  have := max_compose_mono f g hg_mono,
  congr',
end

lemma min_add_commute (f : α → β) (s : β) :
  (min_val f) + s = min_val (λ x, f x + s) :=
begin
  set g : β → β := λ x, x + s with hg,
  have hg_mono : monotone g :=
    λ x y hxy, by {rw hg, dsimp only, apply add_le_add_right hxy},
  have := min_compose_mono f g hg_mono,
  congr',
end

lemma sum_of_max (f : α → β) (f' : α' → β) :
  max_val f + max_val f' = max_val (λ a : α × α', f a.1 + f' a.2) :=
max_zip f f' (λ (b : β × β), b.1+b.2) (λ _ _ _ _ h₁ h₂, add_le_add h₁ h₂)

lemma sum_of_min (f : α → β) (f' : α' → β) :
  min_val f + min_val f' = min_val (λ a : α × α', f a.1 + f' a.2) :=
min_zip f f' (λ (b : β × β), b.1+b.2) (λ _ _ _ _ h₁ h₂, add_le_add h₁ h₂)

end adding

/- sums, intersections, unions etc over finite types, as opposed to sets/finsets.
emphasis is on being able to reindex -/

/-

variables {α β γ: Type*} [fintype α] (op: β → β → β)[is_commutative β op] [is_associative β op]

/-- returns a list of elements of pairs (a,f(a)) for a in α -/
def as_list (f : α → β) : list (α × β) :=
  let s := (infer_instance : fintype α).elems.val in (s.map (λ a, (⟨a,f a⟩ : α × β))).to_list

def val_list (f : α → β) : list β :=
  (as_list f).unzip.2

/-- folds the commutative, associative operation op over the elements of β indexed by α
starting at b -/
def fold (b : β) (f : α → β) : β :=
  let fs := (infer_instance : fintype α).elems in fs.fold op b f

/-- fintype folding can be written as a list folding (for induction)-/
lemma fold_eq_fold_list (b : β) (f : α → β) :
  fold op b f = (as_list f).foldr (λ p q, op p.2 q) b :=
begin
  simp only [fold, as_list, finset.fold],
  set ms := (multiset.map (λ (a : α), (a, f a)) (fintype.elems α).val),
  have hl := ms.coe_to_list, rw ←hl,
  have := multiset.coe_fold_r op b (val_list f),   --b ms.to_list,
  sorry,
end

def Sum [add_comm_monoid β] (f : α → β) : β :=
  fold (+) (0 : β) f

def Prod [comm_monoid β] (f : α → β) : β :=
  fold (*) (1 : β) f

def Union (f : α → set γ) : set γ :=
  fold (∪) ∅ f

def Inter (f : α → set γ) : set γ :=
  fold (∩) univ f

lemma Inter_eq_setInter (f : α → set γ) :
  Inter f = set.Inter f :=
begin
  rw  [Inter, fold_eq_fold_list],
  --induction as_list f,
  dsimp, sorry,
end

-/

 /-

  def fin.induction {n : ℕ} {C : fin (n + 1) → Sort u_1} (h0 : C 0)
                          (hs : Π (i : fin n), C (⇑fin.cast_succ i) → C i.succ) (i : fin (n + 1)) :
    C i

Define C i by induction on i : fin (n + 1) via induction on the underlying nat value. This function has two arguments: h0 handles the base case on C 0, and hs defines the inductive step using C i.cast_succ.
 -/

-- def list.foldr {α : Type*} {β : Type*} (f : α → β → β) (b : β) :
--list α → β

--def union_over {n : ℕ} (Xs : fin n → set α) : set α :=
--  finset.fold (λ a b, a ∪ b) (∅ : set α) Xs (fin n)


inductive fml
| atom (i : ℕ)
| imp (a b : fml)
| not (a : fml)

open fml

infixr ` →' `:50 := imp -- right associative

prefix `¬' `:40 := fml.not

inductive prf : fml → Type
| axk (p q) : prf (p →' q →' p)
| axs (p q r) : prf $ (p →' q →' r) →' (p →' q) →' (p →' r)
| axX (p q) : prf $ ((¬' q) →' (¬' p)) →' p →' q
| mp {p q} : prf (p →' q) → prf p → prf q -- bracket change

open prf

-- example usage:
/-
lemma p_of_p_of_p_of_q (p q : fml) : prf $ (p →' q) →' (p →' p) :=
begin
  apply mp (axs p q p),
  exact (axk p q)
end
-/

lemma Q6a (p : fml) : prf $ p →' p := sorry

theorem Q6b (p : fml) : prf $ p →' ¬' ¬' p := sorry


import data.fintype.basic
import tactic.squeeze

variable (n : ℕ)

def birange : Type := { u : ℕ × ℕ // u.1 + u.2 = n }

namespace birange

variable {n}

def fst (u : birange n) := u.val.fst
def snd (u : birange n) := u.val.snd

lemma rel (u : birange n) : u.fst + u.snd = n := u.property

@[ext]
lemma ext (u₀ u₁ : birange n) :
 u₀ = u₁ ↔ (u₀.fst = u₁.fst ∧ u₀.snd = u₁.snd) :=
begin
 rcases u₀ with ⟨⟨i₀,j₀⟩,h₀⟩,
 rcases u₁ with ⟨⟨i₁,j₁⟩,h₁⟩,
 dsimp[fst,snd] at *,
 rw[subtype.ext_iff_val,prod.mk.inj_iff]
end

lemma ext_left (u₀ u₁ : birange n) : u₀ = u₁ ↔ u₀.fst = u₁.fst :=
 ⟨λ e, by rw[e],
  λ e, by {apply (ext u₀ u₁).mpr,split,assumption,
   let e' := u₀.rel.trans u₁.rel.symm,
   rw[e] at e',exact nat.add_left_cancel e',
  } ⟩

lemma ext_right (u₀ u₁ : birange n) : u₀ = u₁ ↔ u₀.snd = u₁.snd :=
 ⟨λ e, by rw[e],
  λ e, by {apply (ext u₀ u₁).mpr,split,
   let e' := u₀.rel.trans u₁.rel.symm,
   rw[e] at e',exact nat.add_right_cancel e',
   assumption
  } ⟩

instance : decidable_eq (birange n) :=
 λ u₀ u₁, by { apply_instance }

def swap : birange n → birange n :=
 λ ⟨⟨i,j⟩,e⟩ , ⟨⟨j,i⟩,(add_comm j i).trans e⟩

lemma swap_swap (u : birange n) :
 u.swap.swap = u :=
  by { rcases u with ⟨⟨i,j⟩,h⟩, rw[ext], split; refl, }

variable (n)

def fin_equiv : (birange n) ≃ fin n.succ := {
 to_fun := λ u, ⟨u.val.1,by {apply nat.lt_succ_of_le,
   have := nat.le_add_right u.val.1 u.val.2,
   rw[u.property] at this,exact this}⟩ ,
 inv_fun := λ i, ⟨prod.mk i.val (n - i.val),nat.add_sub_of_le (nat.le_of_lt_succ i.is_lt)⟩,
 left_inv := λ ⟨⟨i,j⟩,h⟩, begin apply (ext_left _ _).mpr,refl end,
 right_inv := λ ⟨i,i_is_lt⟩, by { apply fin.eq_of_veq, refl }
}

instance : fintype (birange n) :=
 fintype.of_equiv (fin n.succ) (fin_equiv n).symm

end birange

variable (n)
def trirange : Type :=
 { u : ℕ × ℕ × ℕ // u.1 + (u.2.1 + u.2.2) = n }

namespace trirange

variable {n}

def fst (u : trirange n) := u.val.1
def snd (u : trirange n) := u.val.2.1
def thd (u : trirange n) := u.val.2.2

@[ext]
lemma ext (u₀ u₁ : trirange n) :
 u₀ = u₁ ↔ (u₀.fst = u₁.fst ∧ u₀.snd = u₁.snd ∧ u₀.thd = u₁.thd) :=
  by {rcases u₀ with ⟨⟨i₀,j₀,k₀⟩,h₀⟩,
      rcases u₁ with ⟨⟨i₁,j₁,k₁⟩,h₁⟩,
      dsimp[fst,snd,thd],
      rw[subtype.ext_iff_val,prod.mk.inj_iff,prod.mk.inj_iff], }

instance : decidable_eq (trirange n) :=
 λ u₀ u₁, by { apply_instance }

variable (n)

def sigma_equiv : (trirange n) ≃ Σ (i : fin n.succ), birange (n - i) := {
 to_fun := λ ⟨⟨i,j,k⟩,h⟩,begin
  simp only[] at h,
  have hi : i < n.succ := by {
    apply nat.lt_succ_of_le,
    have := nat.le_add_right i (j + k),
    rw[h] at this,
    exact this, },
  have hjk : j + k = n - i := by {rw[← h,add_comm i,nat.add_sub_cancel],},
  exact ⟨⟨i,hi⟩,⟨⟨j,k⟩,hjk⟩⟩
 end,
 inv_fun := λ ⟨⟨i,hi⟩,⟨⟨j,k⟩,hjk⟩⟩,begin
  change j + k = n - i at hjk,
  have h := calc i + (j + k) = i + (n - i) : by {rw[hjk]}
   ... = n : by rw[add_comm,nat.sub_add_cancel (nat.le_of_lt_succ hi)],
  exact ⟨⟨i,j,k⟩,h⟩
 end,
 left_inv := λ ⟨⟨i,j,k⟩,h⟩, by {apply subtype.eq,refl,},
 right_inv := λ ⟨⟨i,hi⟩,⟨⟨j,k⟩,hjk⟩⟩, by {refl,}
}

def sigma_equiv' : (trirange n) ≃ Σ (k : fin n.succ), birange (n - k) := {
 to_fun := λ ⟨⟨i,j,k⟩,h⟩,begin
  simp only[] at h,
  have hk : k < n.succ := by {
    apply nat.lt_succ_of_le,
    have := nat.le_add_left k (i + j),
    rw[add_assoc,h] at this,
    exact this, },
  have hij : i + j = n - k := by {rw[← h,← add_assoc,nat.add_sub_cancel],},
  exact ⟨⟨k,hk⟩,⟨⟨i,j⟩,hij⟩⟩
 end,
 inv_fun := λ ⟨⟨k,hk⟩,⟨⟨i,j⟩,hij⟩⟩,begin
  change i + j = n - k at hij,
  have h := calc i + (j + k) = (n - k) + k : by {rw[← add_assoc,hij]}
   ... = n : by rw[nat.sub_add_cancel (nat.le_of_lt_succ hk)],
  exact ⟨⟨i,j,k⟩,h⟩
 end,
 left_inv := λ ⟨⟨i,j,k⟩,h⟩, by {apply subtype.eq,refl,},
 right_inv := λ ⟨⟨i,hi⟩,⟨⟨j,k⟩,hjk⟩⟩, by {refl,}
}

instance : fintype (trirange n) :=
 fintype.of_equiv _ (sigma_equiv n).symm

end trirange


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Yury Kudryashov

! This file was ported from Lean 3 source module measure_theory.integral.integrable_on
! leanprover-community/mathlib commit a75898643b2d774cced9ae7c0b28c21663b99666
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Function.L1Space
import Mathbin.Analysis.NormedSpace.IndicatorFunction

/-! # Functions integrable on a set and at a filter

We define `integrable_on f s μ := integrable f (μ.restrict s)` and prove theorems like
`integrable_on_union : integrable_on f (s ∪ t) μ ↔ integrable_on f s μ ∧ integrable_on f t μ`.

Next we define a predicate `integrable_at_filter (f : α → E) (l : filter α) (μ : measure α)`
saying that `f` is integrable at some set `s ∈ l` and prove that a measurable function is integrable
at `l` with respect to `μ` provided that `f` is bounded above at `l ⊓ μ.ae` and `μ` is finite
at `l`.

-/

noncomputable section

open Set Filter TopologicalSpace MeasureTheory Function

open Classical Topology Interval BigOperators Filter ENNReal MeasureTheory

variable {α β E F : Type _} [MeasurableSpace α]

section

variable [TopologicalSpace β] {l l' : Filter α} {f g : α → β} {μ ν : Measure α}

/-- A function `f` is strongly measurable at a filter `l` w.r.t. a measure `μ` if it is
ae strongly measurable w.r.t. `μ.restrict s` for some `s ∈ l`. -/
def StronglyMeasurableAtFilter (f : α → β) (l : Filter α)
    (μ : Measure α := by exact MeasureTheory.MeasureSpace.volume) :=
  ∃ s ∈ l, AeStronglyMeasurable f (μ.restrict s)
#align strongly_measurable_at_filter StronglyMeasurableAtFilter

@[simp]
theorem stronglyMeasurableAtBot {f : α → β} : StronglyMeasurableAtFilter f ⊥ μ :=
  ⟨∅, mem_bot, by simp⟩
#align strongly_measurable_at_bot stronglyMeasurableAtBot

protected theorem StronglyMeasurableAtFilter.eventually (h : StronglyMeasurableAtFilter f l μ) :
    ∀ᶠ s in l.smallSets, AeStronglyMeasurable f (μ.restrict s) :=
  (eventually_small_sets' fun s t => AeStronglyMeasurable.monoSet).2 h
#align strongly_measurable_at_filter.eventually StronglyMeasurableAtFilter.eventually

protected theorem StronglyMeasurableAtFilter.filterMono (h : StronglyMeasurableAtFilter f l μ)
    (h' : l' ≤ l) : StronglyMeasurableAtFilter f l' μ :=
  let ⟨s, hsl, hs⟩ := h
  ⟨s, h' hsl, hs⟩
#align strongly_measurable_at_filter.filter_mono StronglyMeasurableAtFilter.filterMono

protected theorem MeasureTheory.AeStronglyMeasurable.stronglyMeasurableAtFilter
    (h : AeStronglyMeasurable f μ) : StronglyMeasurableAtFilter f l μ :=
  ⟨univ, univ_mem, by rwa [measure.restrict_univ]⟩
#align measure_theory.ae_strongly_measurable.strongly_measurable_at_filter MeasureTheory.AeStronglyMeasurable.stronglyMeasurableAtFilter

theorem AeStronglyMeasurable.stronglyMeasurableAtFilterOfMem {s}
    (h : AeStronglyMeasurable f (μ.restrict s)) (hl : s ∈ l) : StronglyMeasurableAtFilter f l μ :=
  ⟨s, hl, h⟩
#align ae_strongly_measurable.strongly_measurable_at_filter_of_mem AeStronglyMeasurable.stronglyMeasurableAtFilterOfMem

protected theorem MeasureTheory.StronglyMeasurable.stronglyMeasurableAtFilter
    (h : StronglyMeasurable f) : StronglyMeasurableAtFilter f l μ :=
  h.AeStronglyMeasurable.StronglyMeasurableAtFilter
#align measure_theory.strongly_measurable.strongly_measurable_at_filter MeasureTheory.StronglyMeasurable.stronglyMeasurableAtFilter

end

namespace MeasureTheory

section NormedAddCommGroup

theorem hasFiniteIntegralRestrictOfBounded [NormedAddCommGroup E] {f : α → E} {s : Set α}
    {μ : Measure α} {C} (hs : μ s < ∞) (hf : ∀ᵐ x ∂μ.restrict s, ‖f x‖ ≤ C) :
    HasFiniteIntegral f (μ.restrict s) :=
  haveI : is_finite_measure (μ.restrict s) := ⟨by rwa [measure.restrict_apply_univ]⟩
  has_finite_integral_of_bounded hf
#align measure_theory.has_finite_integral_restrict_of_bounded MeasureTheory.hasFiniteIntegralRestrictOfBounded

variable [NormedAddCommGroup E] {f g : α → E} {s t : Set α} {μ ν : Measure α}

/-- A function is `integrable_on` a set `s` if it is almost everywhere strongly measurable on `s`
and if the integral of its pointwise norm over `s` is less than infinity. -/
def IntegrableOn (f : α → E) (s : Set α)
    (μ : Measure α := by exact MeasureTheory.MeasureSpace.volume) : Prop :=
  Integrable f (μ.restrict s)
#align measure_theory.integrable_on MeasureTheory.IntegrableOn

theorem IntegrableOn.integrable (h : IntegrableOn f s μ) : Integrable f (μ.restrict s) :=
  h
#align measure_theory.integrable_on.integrable MeasureTheory.IntegrableOn.integrable

@[simp]
theorem integrableOnEmpty : IntegrableOn f ∅ μ := by simp [integrable_on, integrable_zero_measure]
#align measure_theory.integrable_on_empty MeasureTheory.integrableOnEmpty

@[simp]
theorem integrableOn_univ : IntegrableOn f univ μ ↔ Integrable f μ := by
  rw [integrable_on, measure.restrict_univ]
#align measure_theory.integrable_on_univ MeasureTheory.integrableOn_univ

theorem integrableOnZero : IntegrableOn (fun _ => (0 : E)) s μ :=
  integrableZero _ _ _
#align measure_theory.integrable_on_zero MeasureTheory.integrableOnZero

@[simp]
theorem integrableOn_const {C : E} : IntegrableOn (fun _ => C) s μ ↔ C = 0 ∨ μ s < ∞ :=
  integrable_const_iff.trans <| by rw [measure.restrict_apply_univ]
#align measure_theory.integrable_on_const MeasureTheory.integrableOn_const

theorem IntegrableOn.mono (h : IntegrableOn f t ν) (hs : s ⊆ t) (hμ : μ ≤ ν) : IntegrableOn f s μ :=
  h.monoMeasure <| Measure.restrict_mono hs hμ
#align measure_theory.integrable_on.mono MeasureTheory.IntegrableOn.mono

theorem IntegrableOn.monoSet (h : IntegrableOn f t μ) (hst : s ⊆ t) : IntegrableOn f s μ :=
  h.mono hst le_rfl
#align measure_theory.integrable_on.mono_set MeasureTheory.IntegrableOn.monoSet

theorem IntegrableOn.monoMeasure (h : IntegrableOn f s ν) (hμ : μ ≤ ν) : IntegrableOn f s μ :=
  h.mono (Subset.refl _) hμ
#align measure_theory.integrable_on.mono_measure MeasureTheory.IntegrableOn.monoMeasure

theorem IntegrableOn.monoSetAe (h : IntegrableOn f t μ) (hst : s ≤ᵐ[μ] t) : IntegrableOn f s μ :=
  h.Integrable.monoMeasure <| Measure.restrict_mono_ae hst
#align measure_theory.integrable_on.mono_set_ae MeasureTheory.IntegrableOn.monoSetAe

theorem IntegrableOn.congrSetAe (h : IntegrableOn f t μ) (hst : s =ᵐ[μ] t) : IntegrableOn f s μ :=
  h.monoSetAe hst.le
#align measure_theory.integrable_on.congr_set_ae MeasureTheory.IntegrableOn.congrSetAe

theorem IntegrableOn.congrFun' (h : IntegrableOn f s μ) (hst : f =ᵐ[μ.restrict s] g) :
    IntegrableOn g s μ :=
  Integrable.congr h hst
#align measure_theory.integrable_on.congr_fun' MeasureTheory.IntegrableOn.congrFun'

theorem IntegrableOn.congrFun (h : IntegrableOn f s μ) (hst : EqOn f g s) (hs : MeasurableSet s) :
    IntegrableOn g s μ :=
  h.congrFun' ((ae_restrict_iff' hs).2 (eventually_of_forall hst))
#align measure_theory.integrable_on.congr_fun MeasureTheory.IntegrableOn.congrFun

theorem Integrable.integrableOn (h : Integrable f μ) : IntegrableOn f s μ :=
  h.monoMeasure <| Measure.restrict_le_self
#align measure_theory.integrable.integrable_on MeasureTheory.Integrable.integrableOn

theorem Integrable.integrableOn' (h : Integrable f (μ.restrict s)) : IntegrableOn f s μ :=
  h
#align measure_theory.integrable.integrable_on' MeasureTheory.Integrable.integrableOn'

theorem IntegrableOn.restrict (h : IntegrableOn f s μ) (hs : MeasurableSet s) :
    IntegrableOn f s (μ.restrict t) :=
  by
  rw [integrable_on, measure.restrict_restrict hs]
  exact h.mono_set (inter_subset_left _ _)
#align measure_theory.integrable_on.restrict MeasureTheory.IntegrableOn.restrict

theorem IntegrableOn.leftOfUnion (h : IntegrableOn f (s ∪ t) μ) : IntegrableOn f s μ :=
  h.monoSet <| subset_union_left _ _
#align measure_theory.integrable_on.left_of_union MeasureTheory.IntegrableOn.leftOfUnion

theorem IntegrableOn.rightOfUnion (h : IntegrableOn f (s ∪ t) μ) : IntegrableOn f t μ :=
  h.monoSet <| subset_union_right _ _
#align measure_theory.integrable_on.right_of_union MeasureTheory.IntegrableOn.rightOfUnion

theorem IntegrableOn.union (hs : IntegrableOn f s μ) (ht : IntegrableOn f t μ) :
    IntegrableOn f (s ∪ t) μ :=
  (hs.addMeasure ht).monoMeasure <| Measure.restrict_union_le _ _
#align measure_theory.integrable_on.union MeasureTheory.IntegrableOn.union

@[simp]
theorem integrableOn_union : IntegrableOn f (s ∪ t) μ ↔ IntegrableOn f s μ ∧ IntegrableOn f t μ :=
  ⟨fun h => ⟨h.leftOfUnion, h.rightOfUnion⟩, fun h => h.1.union h.2⟩
#align measure_theory.integrable_on_union MeasureTheory.integrableOn_union

@[simp]
theorem integrableOn_singleton_iff {x : α} [MeasurableSingletonClass α] :
    IntegrableOn f {x} μ ↔ f x = 0 ∨ μ {x} < ∞ :=
  by
  have : f =ᵐ[μ.restrict {x}] fun y => f x :=
    by
    filter_upwards [ae_restrict_mem (measurable_set_singleton x)]with _ ha
    simp only [mem_singleton_iff.1 ha]
  rw [integrable_on, integrable_congr this, integrable_const_iff]
  simp
#align measure_theory.integrable_on_singleton_iff MeasureTheory.integrableOn_singleton_iff

@[simp]
theorem integrableOn_finite_bUnion {s : Set β} (hs : s.Finite) {t : β → Set α} :
    IntegrableOn f (⋃ i ∈ s, t i) μ ↔ ∀ i ∈ s, IntegrableOn f (t i) μ :=
  by
  apply hs.induction_on
  · simp
  · intro a s ha hs hf
    simp [hf, or_imp, forall_and]
#align measure_theory.integrable_on_finite_bUnion MeasureTheory.integrableOn_finite_bUnion

@[simp]
theorem integrableOn_finset_unionᵢ {s : Finset β} {t : β → Set α} :
    IntegrableOn f (⋃ i ∈ s, t i) μ ↔ ∀ i ∈ s, IntegrableOn f (t i) μ :=
  integrableOn_finite_bUnion s.finite_toSet
#align measure_theory.integrable_on_finset_Union MeasureTheory.integrableOn_finset_unionᵢ

@[simp]
theorem integrableOn_finite_unionᵢ [Finite β] {t : β → Set α} :
    IntegrableOn f (⋃ i, t i) μ ↔ ∀ i, IntegrableOn f (t i) μ :=
  by
  cases nonempty_fintype β
  simpa using @integrable_on_finset_Union _ _ _ _ _ f μ Finset.univ t
#align measure_theory.integrable_on_finite_Union MeasureTheory.integrableOn_finite_unionᵢ

theorem IntegrableOn.addMeasure (hμ : IntegrableOn f s μ) (hν : IntegrableOn f s ν) :
    IntegrableOn f s (μ + ν) := by
  delta integrable_on
  rw [measure.restrict_add]
  exact hμ.integrable.add_measure hν
#align measure_theory.integrable_on.add_measure MeasureTheory.IntegrableOn.addMeasure

@[simp]
theorem integrableOn_add_measure :
    IntegrableOn f s (μ + ν) ↔ IntegrableOn f s μ ∧ IntegrableOn f s ν :=
  ⟨fun h =>
    ⟨h.monoMeasure (Measure.le_add_right le_rfl), h.monoMeasure (Measure.le_add_left le_rfl)⟩,
    fun h => h.1.addMeasure h.2⟩
#align measure_theory.integrable_on_add_measure MeasureTheory.integrableOn_add_measure

theorem MeasurableEmbedding.integrableOn_map_iff [MeasurableSpace β] {e : α → β}
    (he : MeasurableEmbedding e) {f : β → E} {μ : Measure α} {s : Set β} :
    IntegrableOn f s (Measure.map e μ) ↔ IntegrableOn (f ∘ e) (e ⁻¹' s) μ := by
  simp only [integrable_on, he.restrict_map, he.integrable_map_iff]
#align measurable_embedding.integrable_on_map_iff MeasurableEmbedding.integrableOn_map_iff

theorem integrableOn_map_equiv [MeasurableSpace β] (e : α ≃ᵐ β) {f : β → E} {μ : Measure α}
    {s : Set β} : IntegrableOn f s (Measure.map e μ) ↔ IntegrableOn (f ∘ e) (e ⁻¹' s) μ := by
  simp only [integrable_on, e.restrict_map, integrable_map_equiv e]
#align measure_theory.integrable_on_map_equiv MeasureTheory.integrableOn_map_equiv

theorem MeasurePreserving.integrableOn_comp_preimage [MeasurableSpace β] {e : α → β} {ν}
    (h₁ : MeasurePreserving e μ ν) (h₂ : MeasurableEmbedding e) {f : β → E} {s : Set β} :
    IntegrableOn (f ∘ e) (e ⁻¹' s) μ ↔ IntegrableOn f s ν :=
  (h₁.restrictPreimageEmb h₂ s).integrable_comp_emb h₂
#align measure_theory.measure_preserving.integrable_on_comp_preimage MeasureTheory.MeasurePreserving.integrableOn_comp_preimage

theorem MeasurePreserving.integrableOn_image [MeasurableSpace β] {e : α → β} {ν}
    (h₁ : MeasurePreserving e μ ν) (h₂ : MeasurableEmbedding e) {f : β → E} {s : Set α} :
    IntegrableOn f (e '' s) ν ↔ IntegrableOn (f ∘ e) s μ :=
  ((h₁.restrictImageEmb h₂ s).integrable_comp_emb h₂).symm
#align measure_theory.measure_preserving.integrable_on_image MeasureTheory.MeasurePreserving.integrableOn_image

theorem integrable_indicator_iff (hs : MeasurableSet s) :
    Integrable (indicator s f) μ ↔ IntegrableOn f s μ := by
  simp [integrable_on, integrable, has_finite_integral, nnnorm_indicator_eq_indicator_nnnorm,
    ENNReal.coe_indicator, lintegral_indicator _ hs, aeStronglyMeasurable_indicator_iff hs]
#align measure_theory.integrable_indicator_iff MeasureTheory.integrable_indicator_iff

theorem IntegrableOn.integrableIndicator (h : IntegrableOn f s μ) (hs : MeasurableSet s) :
    Integrable (indicator s f) μ :=
  (integrable_indicator_iff hs).2 h
#align measure_theory.integrable_on.integrable_indicator MeasureTheory.IntegrableOn.integrableIndicator

theorem Integrable.indicator (h : Integrable f μ) (hs : MeasurableSet s) :
    Integrable (indicator s f) μ :=
  h.IntegrableOn.integrableIndicator hs
#align measure_theory.integrable.indicator MeasureTheory.Integrable.indicator

theorem IntegrableOn.indicator (h : IntegrableOn f s μ) (ht : MeasurableSet t) :
    IntegrableOn (indicator t f) s μ :=
  Integrable.indicator h ht
#align measure_theory.integrable_on.indicator MeasureTheory.IntegrableOn.indicator

theorem integrableIndicatorConstLp {E} [NormedAddCommGroup E] {p : ℝ≥0∞} {s : Set α}
    (hs : MeasurableSet s) (hμs : μ s ≠ ∞) (c : E) : Integrable (indicatorConstLp p hs hμs c) μ :=
  by
  rw [integrable_congr indicator_const_Lp_coe_fn, integrable_indicator_iff hs, integrable_on,
    integrable_const_iff, lt_top_iff_ne_top]
  right
  simpa only [Set.univ_inter, MeasurableSet.univ, measure.restrict_apply] using hμs
#align measure_theory.integrable_indicator_const_Lp MeasureTheory.integrableIndicatorConstLp

/-- If a function is integrable on a set `s` and nonzero there, then the measurable hull of `s` is
well behaved: the restriction of the measure to `to_measurable μ s` coincides with its restriction
to `s`. -/
theorem IntegrableOn.restrict_toMeasurable (hf : IntegrableOn f s μ) (h's : ∀ x ∈ s, f x ≠ 0) :
    μ.restrict (toMeasurable μ s) = μ.restrict s :=
  by
  rcases exists_seq_strictAnti_tendsto (0 : ℝ) with ⟨u, u_anti, u_pos, u_lim⟩
  let v n := to_measurable (μ.restrict s) { x | u n ≤ ‖f x‖ }
  have A : ∀ n, μ (s ∩ v n) ≠ ∞ := by
    intro n
    rw [inter_comm, ← measure.restrict_apply (measurable_set_to_measurable _ _),
      measure_to_measurable]
    exact (hf.measure_ge_lt_top (u_pos n)).Ne
  apply measure.restrict_to_measurable_of_cover _ A
  intro x hx
  have : 0 < ‖f x‖ := by simp only [h's x hx, norm_pos_iff, Ne.def, not_false_iff]
  obtain ⟨n, hn⟩ : ∃ n, u n < ‖f x‖
  exact ((tendsto_order.1 u_lim).2 _ this).exists
  refine' mem_Union.2 ⟨n, _⟩
  exact subset_to_measurable _ _ hn.le
#align measure_theory.integrable_on.restrict_to_measurable MeasureTheory.IntegrableOn.restrict_toMeasurable

/-- If a function is integrable on a set `s`, and vanishes on `t \ s`, then it is integrable on `t`
if `t` is null-measurable. -/
theorem IntegrableOn.ofAeDiffEqZero (hf : IntegrableOn f s μ) (ht : NullMeasurableSet t μ)
    (h't : ∀ᵐ x ∂μ, x ∈ t \ s → f x = 0) : IntegrableOn f t μ :=
  by
  let u := { x ∈ s | f x ≠ 0 }
  have hu : integrable_on f u μ := hf.mono_set fun x hx => hx.1
  let v := to_measurable μ u
  have A : integrable_on f v μ :=
    by
    rw [integrable_on, hu.restrict_to_measurable]
    · exact hu
    · intro x hx
      exact hx.2
  have B : integrable_on f (t \ v) μ :=
    by
    apply integrable_on_zero.congr
    filter_upwards [ae_restrict_of_ae h't,
      ae_restrict_mem₀ (ht.diff (measurable_set_to_measurable μ u).NullMeasurableSet)]with x hxt hx
    by_cases h'x : x ∈ s
    · by_contra H
      exact hx.2 (subset_to_measurable μ u ⟨h'x, Ne.symm H⟩)
    · exact (hxt ⟨hx.1, h'x⟩).symm
  apply (A.union B).monoSet _
  rw [union_diff_self]
  exact subset_union_right _ _
#align measure_theory.integrable_on.of_ae_diff_eq_zero MeasureTheory.IntegrableOn.ofAeDiffEqZero

/-- If a function is integrable on a set `s`, and vanishes on `t \ s`, then it is integrable on `t`
if `t` is measurable. -/
theorem IntegrableOn.ofForallDiffEqZero (hf : IntegrableOn f s μ) (ht : MeasurableSet t)
    (h't : ∀ x ∈ t \ s, f x = 0) : IntegrableOn f t μ :=
  hf.ofAeDiffEqZero ht.NullMeasurableSet (eventually_of_forall h't)
#align measure_theory.integrable_on.of_forall_diff_eq_zero MeasureTheory.IntegrableOn.ofForallDiffEqZero

/-- If a function is integrable on a set `s` and vanishes almost everywhere on its complement,
then it is integrable. -/
theorem IntegrableOn.integrableOfAeNotMemEqZero (hf : IntegrableOn f s μ)
    (h't : ∀ᵐ x ∂μ, x ∉ s → f x = 0) : Integrable f μ :=
  by
  rw [← integrable_on_univ]
  apply hf.of_ae_diff_eq_zero null_measurable_set_univ
  filter_upwards [h't]with x hx h'x using hx h'x.2
#align measure_theory.integrable_on.integrable_of_ae_not_mem_eq_zero MeasureTheory.IntegrableOn.integrableOfAeNotMemEqZero

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (x «expr ∉ » s) -/
/-- If a function is integrable on a set `s` and vanishes everywhere on its complement,
then it is integrable. -/
theorem IntegrableOn.integrableOfForallNotMemEqZero (hf : IntegrableOn f s μ)
    (h't : ∀ (x) (_ : x ∉ s), f x = 0) : Integrable f μ :=
  hf.integrableOfAeNotMemEqZero (eventually_of_forall fun x hx => h't x hx)
#align measure_theory.integrable_on.integrable_of_forall_not_mem_eq_zero MeasureTheory.IntegrableOn.integrableOfForallNotMemEqZero

theorem integrableOn_iff_integrable_of_support_subset (h1s : support f ⊆ s) :
    IntegrableOn f s μ ↔ Integrable f μ :=
  by
  refine' ⟨fun h => _, fun h => h.IntegrableOn⟩
  apply h.integrable_of_forall_not_mem_eq_zero fun x hx => _
  contrapose! hx
  exact h1s (mem_support.2 hx)
#align measure_theory.integrable_on_iff_integrable_of_support_subset MeasureTheory.integrableOn_iff_integrable_of_support_subset

theorem integrableOnLpOfMeasureNeTop {E} [NormedAddCommGroup E] {p : ℝ≥0∞} {s : Set α}
    (f : lp E p μ) (hp : 1 ≤ p) (hμs : μ s ≠ ∞) : IntegrableOn f s μ :=
  by
  refine' mem_ℒp_one_iff_integrable.mp _
  have hμ_restrict_univ : (μ.restrict s) Set.univ < ∞ := by
    simpa only [Set.univ_inter, MeasurableSet.univ, measure.restrict_apply, lt_top_iff_ne_top]
  haveI hμ_finite : is_finite_measure (μ.restrict s) := ⟨hμ_restrict_univ⟩
  exact ((Lp.mem_ℒp _).restrict s).memℒpOfExponentLe hp
#align measure_theory.integrable_on_Lp_of_measure_ne_top MeasureTheory.integrableOnLpOfMeasureNeTop

theorem Integrable.lintegral_lt_top {f : α → ℝ} (hf : Integrable f μ) :
    (∫⁻ x, ENNReal.ofReal (f x) ∂μ) < ∞ :=
  calc
    (∫⁻ x, ENNReal.ofReal (f x) ∂μ) ≤ ∫⁻ x, ↑‖f x‖₊ ∂μ := lintegral_ofReal_le_lintegral_nnnorm f
    _ < ∞ := hf.2

#align measure_theory.integrable.lintegral_lt_top MeasureTheory.Integrable.lintegral_lt_top

theorem IntegrableOn.set_lintegral_lt_top {f : α → ℝ} {s : Set α} (hf : IntegrableOn f s μ) :
    (∫⁻ x in s, ENNReal.ofReal (f x) ∂μ) < ∞ :=
  Integrable.lintegral_lt_top hf
#align measure_theory.integrable_on.set_lintegral_lt_top MeasureTheory.IntegrableOn.set_lintegral_lt_top

/-- We say that a function `f` is *integrable at filter* `l` if it is integrable on some
set `s ∈ l`. Equivalently, it is eventually integrable on `s` in `l.small_sets`. -/
def IntegrableAtFilter (f : α → E) (l : Filter α)
    (μ : Measure α := by exact MeasureTheory.MeasureSpace.volume) :=
  ∃ s ∈ l, IntegrableOn f s μ
#align measure_theory.integrable_at_filter MeasureTheory.IntegrableAtFilter

variable {l l' : Filter α}

theorem Integrable.integrableAtFilter (h : Integrable f μ) (l : Filter α) :
    IntegrableAtFilter f l μ :=
  ⟨univ, Filter.univ_mem, integrableOn_univ.2 h⟩
#align measure_theory.integrable.integrable_at_filter MeasureTheory.Integrable.integrableAtFilter

protected theorem IntegrableAtFilter.eventually (h : IntegrableAtFilter f l μ) :
    ∀ᶠ s in l.smallSets, IntegrableOn f s μ :=
  Iff.mpr (eventually_small_sets' fun s t hst ht => ht.monoSet hst) h
#align measure_theory.integrable_at_filter.eventually MeasureTheory.IntegrableAtFilter.eventually

theorem IntegrableAtFilter.filterMono (hl : l ≤ l') (hl' : IntegrableAtFilter f l' μ) :
    IntegrableAtFilter f l μ :=
  let ⟨s, hs, hsf⟩ := hl'
  ⟨s, hl hs, hsf⟩
#align measure_theory.integrable_at_filter.filter_mono MeasureTheory.IntegrableAtFilter.filterMono

theorem IntegrableAtFilter.infOfLeft (hl : IntegrableAtFilter f l μ) :
    IntegrableAtFilter f (l ⊓ l') μ :=
  hl.filter_mono inf_le_left
#align measure_theory.integrable_at_filter.inf_of_left MeasureTheory.IntegrableAtFilter.infOfLeft

theorem IntegrableAtFilter.infOfRight (hl : IntegrableAtFilter f l μ) :
    IntegrableAtFilter f (l' ⊓ l) μ :=
  hl.filter_mono inf_le_right
#align measure_theory.integrable_at_filter.inf_of_right MeasureTheory.IntegrableAtFilter.infOfRight

@[simp]
theorem IntegrableAtFilter.inf_ae_iff {l : Filter α} :
    IntegrableAtFilter f (l ⊓ μ.ae) μ ↔ IntegrableAtFilter f l μ :=
  by
  refine' ⟨_, fun h => h.filter_mono inf_le_left⟩
  rintro ⟨s, ⟨t, ht, u, hu, rfl⟩, hf⟩
  refine' ⟨t, ht, _⟩
  refine' hf.integrable.mono_measure fun v hv => _
  simp only [measure.restrict_apply hv]
  refine' measure_mono_ae (mem_of_superset hu fun x hx => _)
  exact fun ⟨hv, ht⟩ => ⟨hv, ⟨ht, hx⟩⟩
#align measure_theory.integrable_at_filter.inf_ae_iff MeasureTheory.IntegrableAtFilter.inf_ae_iff

alias integrable_at_filter.inf_ae_iff ↔ integrable_at_filter.of_inf_ae _
#align measure_theory.integrable_at_filter.of_inf_ae MeasureTheory.IntegrableAtFilter.ofInfAe

/-- If `μ` is a measure finite at filter `l` and `f` is a function such that its norm is bounded
above at `l`, then `f` is integrable at `l`. -/
theorem Measure.FiniteAtFilter.integrableAtFilter {l : Filter α} [IsMeasurablyGenerated l]
    (hfm : StronglyMeasurableAtFilter f l μ) (hμ : μ.FiniteAtFilter l)
    (hf : l.IsBoundedUnder (· ≤ ·) (norm ∘ f)) : IntegrableAtFilter f l μ :=
  by
  obtain ⟨C, hC⟩ : ∃ C, ∀ᶠ s in l.small_sets, ∀ x ∈ s, ‖f x‖ ≤ C
  exact hf.imp fun C hC => eventually_small_sets.2 ⟨_, hC, fun t => id⟩
  rcases(hfm.eventually.and (hμ.eventually.and hC)).exists_measurable_mem_of_smallSets with
    ⟨s, hsl, hsm, hfm, hμ, hC⟩
  refine' ⟨s, hsl, ⟨hfm, has_finite_integral_restrict_of_bounded hμ _⟩⟩
  exact C
  rw [ae_restrict_eq hsm, eventually_inf_principal]
  exact eventually_of_forall hC
#align measure_theory.measure.finite_at_filter.integrable_at_filter MeasureTheory.Measure.FiniteAtFilter.integrableAtFilter

theorem Measure.FiniteAtFilter.integrableAtFilterOfTendstoAe {l : Filter α}
    [IsMeasurablyGenerated l] (hfm : StronglyMeasurableAtFilter f l μ) (hμ : μ.FiniteAtFilter l) {b}
    (hf : Tendsto f (l ⊓ μ.ae) (𝓝 b)) : IntegrableAtFilter f l μ :=
  (hμ.inf_of_left.IntegrableAtFilter (hfm.filter_mono inf_le_left)
      hf.norm.isBoundedUnder_le).ofInfAe
#align measure_theory.measure.finite_at_filter.integrable_at_filter_of_tendsto_ae MeasureTheory.Measure.FiniteAtFilter.integrableAtFilterOfTendstoAe

alias measure.finite_at_filter.integrable_at_filter_of_tendsto_ae ←
  _root_.filter.tendsto.integrable_at_filter_ae
#align filter.tendsto.integrable_at_filter_ae Filter.Tendsto.integrableAtFilterAe

theorem Measure.FiniteAtFilter.integrableAtFilterOfTendsto {l : Filter α} [IsMeasurablyGenerated l]
    (hfm : StronglyMeasurableAtFilter f l μ) (hμ : μ.FiniteAtFilter l) {b}
    (hf : Tendsto f l (𝓝 b)) : IntegrableAtFilter f l μ :=
  hμ.IntegrableAtFilter hfm hf.norm.isBoundedUnder_le
#align measure_theory.measure.finite_at_filter.integrable_at_filter_of_tendsto MeasureTheory.Measure.FiniteAtFilter.integrableAtFilterOfTendsto

alias measure.finite_at_filter.integrable_at_filter_of_tendsto ←
  _root_.filter.tendsto.integrable_at_filter
#align filter.tendsto.integrable_at_filter Filter.Tendsto.integrableAtFilter

theorem integrable_add_of_disjoint {f g : α → E} (h : Disjoint (support f) (support g))
    (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) :
    Integrable (f + g) μ ↔ Integrable f μ ∧ Integrable g μ :=
  by
  refine' ⟨fun hfg => ⟨_, _⟩, fun h => h.1.add h.2⟩
  · rw [← indicator_add_eq_left h]
    exact hfg.indicator hf.measurable_set_support
  · rw [← indicator_add_eq_right h]
    exact hfg.indicator hg.measurable_set_support
#align measure_theory.integrable_add_of_disjoint MeasureTheory.integrable_add_of_disjoint

end NormedAddCommGroup

end MeasureTheory

open MeasureTheory

variable [NormedAddCommGroup E]

/-- A function which is continuous on a set `s` is almost everywhere measurable with respect to
`μ.restrict s`. -/
theorem ContinuousOn.aeMeasurable [TopologicalSpace α] [OpensMeasurableSpace α] [MeasurableSpace β]
    [TopologicalSpace β] [BorelSpace β] {f : α → β} {s : Set α} {μ : Measure α}
    (hf : ContinuousOn f s) (hs : MeasurableSet s) : AeMeasurable f (μ.restrict s) :=
  by
  nontriviality α; inhabit α
  have : (piecewise s f fun _ => f default) =ᵐ[μ.restrict s] f := piecewise_ae_eq_restrict hs
  refine' ⟨piecewise s f fun _ => f default, _, this.symm⟩
  apply measurable_of_isOpen
  intro t ht
  obtain ⟨u, u_open, hu⟩ : ∃ u : Set α, IsOpen u ∧ f ⁻¹' t ∩ s = u ∩ s :=
    _root_.continuous_on_iff'.1 hf t ht
  rw [piecewise_preimage, Set.ite, hu]
  exact (u_open.measurable_set.inter hs).union ((measurable_const ht.measurable_set).diffₓ hs)
#align continuous_on.ae_measurable ContinuousOn.aeMeasurable

/-- A function which is continuous on a separable set `s` is almost everywhere strongly measurable
with respect to `μ.restrict s`. -/
theorem ContinuousOn.aeStronglyMeasurableOfIsSeparable [TopologicalSpace α]
    [PseudoMetrizableSpace α] [OpensMeasurableSpace α] [TopologicalSpace β]
    [PseudoMetrizableSpace β] {f : α → β} {s : Set α} {μ : Measure α} (hf : ContinuousOn f s)
    (hs : MeasurableSet s) (h's : TopologicalSpace.IsSeparable s) :
    AeStronglyMeasurable f (μ.restrict s) :=
  by
  letI := pseudo_metrizable_space_pseudo_metric α
  borelize β
  rw [aeStronglyMeasurable_iff_aeMeasurable_separable]
  refine' ⟨hf.ae_measurable hs, f '' s, hf.is_separable_image h's, _⟩
  exact mem_of_superset (self_mem_ae_restrict hs) (subset_preimage_image _ _)
#align continuous_on.ae_strongly_measurable_of_is_separable ContinuousOn.aeStronglyMeasurableOfIsSeparable

/-- A function which is continuous on a set `s` is almost everywhere strongly measurable with
respect to `μ.restrict s` when either the source space or the target space is second-countable. -/
theorem ContinuousOn.aeStronglyMeasurable [TopologicalSpace α] [TopologicalSpace β]
    [h : SecondCountableTopologyEither α β] [OpensMeasurableSpace α] [PseudoMetrizableSpace β]
    {f : α → β} {s : Set α} {μ : Measure α} (hf : ContinuousOn f s) (hs : MeasurableSet s) :
    AeStronglyMeasurable f (μ.restrict s) :=
  by
  borelize β
  refine'
    aeStronglyMeasurable_iff_aeMeasurable_separable.2
      ⟨hf.ae_measurable hs, f '' s, _,
        mem_of_superset (self_mem_ae_restrict hs) (subset_preimage_image _ _)⟩
  cases h.out
  · let f' : s → β := s.restrict f
    have A : Continuous f' := continuousOn_iff_continuous_restrict.1 hf
    have B : is_separable (univ : Set s) := is_separable_of_separable_space _
    convert is_separable.image B A using 1
    ext x
    simp
  · exact is_separable_of_separable_space _
#align continuous_on.ae_strongly_measurable ContinuousOn.aeStronglyMeasurable

/-- A function which is continuous on a compact set `s` is almost everywhere strongly measurable
with respect to `μ.restrict s`. -/
theorem ContinuousOn.aeStronglyMeasurableOfIsCompact [TopologicalSpace α] [OpensMeasurableSpace α]
    [TopologicalSpace β] [PseudoMetrizableSpace β] {f : α → β} {s : Set α} {μ : Measure α}
    (hf : ContinuousOn f s) (hs : IsCompact s) (h's : MeasurableSet s) :
    AeStronglyMeasurable f (μ.restrict s) :=
  by
  letI := pseudo_metrizable_space_pseudo_metric β
  borelize β
  rw [aeStronglyMeasurable_iff_aeMeasurable_separable]
  refine' ⟨hf.ae_measurable h's, f '' s, _, _⟩
  · exact (hs.image_of_continuous_on hf).IsSeparable
  · exact mem_of_superset (self_mem_ae_restrict h's) (subset_preimage_image _ _)
#align continuous_on.ae_strongly_measurable_of_is_compact ContinuousOn.aeStronglyMeasurableOfIsCompact

theorem ContinuousOn.integrableAtNhdsWithinOfIsSeparable [TopologicalSpace α]
    [PseudoMetrizableSpace α] [OpensMeasurableSpace α] {μ : Measure α} [IsLocallyFiniteMeasure μ]
    {a : α} {t : Set α} {f : α → E} (hft : ContinuousOn f t) (ht : MeasurableSet t)
    (h't : TopologicalSpace.IsSeparable t) (ha : a ∈ t) : IntegrableAtFilter f (𝓝[t] a) μ :=
  haveI : (𝓝[t] a).IsMeasurablyGenerated := ht.nhds_within_is_measurably_generated _
  (hft a ha).IntegrableAtFilter
    ⟨_, self_mem_nhdsWithin, hft.ae_strongly_measurable_of_is_separable ht h't⟩
    (μ.finite_at_nhds_within _ _)
#align continuous_on.integrable_at_nhds_within_of_is_separable ContinuousOn.integrableAtNhdsWithinOfIsSeparable

theorem ContinuousOn.integrableAtNhdsWithin [TopologicalSpace α] [SecondCountableTopologyEither α E]
    [OpensMeasurableSpace α] {μ : Measure α} [IsLocallyFiniteMeasure μ] {a : α} {t : Set α}
    {f : α → E} (hft : ContinuousOn f t) (ht : MeasurableSet t) (ha : a ∈ t) :
    IntegrableAtFilter f (𝓝[t] a) μ :=
  haveI : (𝓝[t] a).IsMeasurablyGenerated := ht.nhds_within_is_measurably_generated _
  (hft a ha).IntegrableAtFilter ⟨_, self_mem_nhdsWithin, hft.ae_strongly_measurable ht⟩
    (μ.finite_at_nhds_within _ _)
#align continuous_on.integrable_at_nhds_within ContinuousOn.integrableAtNhdsWithin

theorem Continuous.integrableAtNhds [TopologicalSpace α] [SecondCountableTopologyEither α E]
    [OpensMeasurableSpace α] {μ : Measure α} [IsLocallyFiniteMeasure μ] {f : α → E}
    (hf : Continuous f) (a : α) : IntegrableAtFilter f (𝓝 a) μ :=
  by
  rw [← nhdsWithin_univ]
  exact hf.continuous_on.integrable_at_nhds_within MeasurableSet.univ (mem_univ a)
#align continuous.integrable_at_nhds Continuous.integrableAtNhds

/-- If a function is continuous on an open set `s`, then it is strongly measurable at the filter
`𝓝 x` for all `x ∈ s` if either the source space or the target space is second-countable. -/
theorem ContinuousOn.stronglyMeasurableAtFilter [TopologicalSpace α] [OpensMeasurableSpace α]
    [TopologicalSpace β] [PseudoMetrizableSpace β] [SecondCountableTopologyEither α β] {f : α → β}
    {s : Set α} {μ : Measure α} (hs : IsOpen s) (hf : ContinuousOn f s) :
    ∀ x ∈ s, StronglyMeasurableAtFilter f (𝓝 x) μ := fun x hx =>
  ⟨s, IsOpen.mem_nhds hs hx, hf.AeStronglyMeasurable hs.MeasurableSet⟩
#align continuous_on.strongly_measurable_at_filter ContinuousOn.stronglyMeasurableAtFilter

theorem ContinuousAt.stronglyMeasurableAtFilter [TopologicalSpace α] [OpensMeasurableSpace α]
    [SecondCountableTopologyEither α E] {f : α → E} {s : Set α} {μ : Measure α} (hs : IsOpen s)
    (hf : ∀ x ∈ s, ContinuousAt f x) : ∀ x ∈ s, StronglyMeasurableAtFilter f (𝓝 x) μ :=
  ContinuousOn.stronglyMeasurableAtFilter hs <| ContinuousAt.continuousOn hf
#align continuous_at.strongly_measurable_at_filter ContinuousAt.stronglyMeasurableAtFilter

theorem Continuous.stronglyMeasurableAtFilter [TopologicalSpace α] [OpensMeasurableSpace α]
    [TopologicalSpace β] [PseudoMetrizableSpace β] [SecondCountableTopologyEither α β] {f : α → β}
    (hf : Continuous f) (μ : Measure α) (l : Filter α) : StronglyMeasurableAtFilter f l μ :=
  hf.StronglyMeasurable.StronglyMeasurableAtFilter
#align continuous.strongly_measurable_at_filter Continuous.stronglyMeasurableAtFilter

/-- If a function is continuous on a measurable set `s`, then it is measurable at the filter
  `𝓝[s] x` for all `x`. -/
theorem ContinuousOn.stronglyMeasurableAtFilterNhdsWithin {α β : Type _} [MeasurableSpace α]
    [TopologicalSpace α] [OpensMeasurableSpace α] [TopologicalSpace β] [PseudoMetrizableSpace β]
    [SecondCountableTopologyEither α β] {f : α → β} {s : Set α} {μ : Measure α}
    (hf : ContinuousOn f s) (hs : MeasurableSet s) (x : α) :
    StronglyMeasurableAtFilter f (𝓝[s] x) μ :=
  ⟨s, self_mem_nhdsWithin, hf.AeStronglyMeasurable hs⟩
#align continuous_on.strongly_measurable_at_filter_nhds_within ContinuousOn.stronglyMeasurableAtFilterNhdsWithin