/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Chris Hughes, Mario Carneiro

! This file was ported from Lean 3 source module ring_theory.ideal.local_ring
! leanprover-community/mathlib commit ec1c7d810034d4202b0dd239112d1792be9f6fdc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Algebra.Basic
import Mathbin.RingTheory.Ideal.Operations
import Mathbin.RingTheory.JacobsonIdeal
import Mathbin.Logic.Equiv.TransferInstance

/-!

# Local rings

Define local rings as commutative rings having a unique maximal ideal.

## Main definitions

* `local_ring`: A predicate on commutative semirings, stating that for any pair of elements that
  adds up to `1`, one of them is a unit. This is shown to be equivalent to the condition that there
  exists a unique maximal ideal.
* `local_ring.maximal_ideal`: The unique maximal ideal for a local rings. Its carrier set is the
  set of non units.
* `is_local_ring_hom`: A predicate on semiring homomorphisms, requiring that it maps nonunits
  to nonunits. For local rings, this means that the image of the unique maximal ideal is again
  contained in the unique maximal ideal.
* `local_ring.residue_field`: The quotient of a local ring by its maximal ideal.

-/

universe u v w u'

variable {R : Type u} {S : Type v} {T : Type w} {K : Type u'}

/-- A semiring is local if it is nontrivial and `a` or `b` is a unit whenever `a + b = 1`.
Note that `local_ring` is a predicate. -/
class LocalRing (R : Type u) [Semiring R] extends Nontrivial R : Prop where
  of_is_unit_or_is_unit_of_add_one ::
  isUnit_or_isUnit_of_add_one {a b : R} (h : a + b = 1) : IsUnit a ∨ IsUnit b
#align local_ring LocalRing

section CommSemiring

variable [CommSemiring R]

namespace LocalRing

theorem of_isUnit_or_isUnit_of_isUnit_add [Nontrivial R]
    (h : ∀ a b : R, IsUnit (a + b) → IsUnit a ∨ IsUnit b) : LocalRing R :=
  ⟨fun a b hab => h a b <| hab.symm ▸ isUnit_one⟩
#align local_ring.of_is_unit_or_is_unit_of_is_unit_add LocalRing.of_isUnit_or_isUnit_of_isUnit_add

/-- A semiring is local if it is nontrivial and the set of nonunits is closed under the addition. -/
theorem of_nonunits_add [Nontrivial R]
    (h : ∀ a b : R, a ∈ nonunits R → b ∈ nonunits R → a + b ∈ nonunits R) : LocalRing R :=
  ⟨fun a b hab => or_iff_not_and_not.2 fun H => h a b H.1 H.2 <| hab.symm ▸ isUnit_one⟩
#align local_ring.of_nonunits_add LocalRing.of_nonunits_add

/-- A semiring is local if it has a unique maximal ideal. -/
theorem of_unique_max_ideal (h : ∃! I : Ideal R, I.IsMaximal) : LocalRing R :=
  @of_nonunits_add _ _
    (nontrivial_of_ne (0 : R) 1 <|
      let ⟨I, Imax, _⟩ := h
      fun H : 0 = 1 => Imax.1.1 <| I.eq_top_iff_one.2 <| H ▸ I.zero_mem)
    fun x y hx hy H =>
    let ⟨I, Imax, Iuniq⟩ := h
    let ⟨Ix, Ixmax, Hx⟩ := exists_max_ideal_of_mem_nonunits hx
    let ⟨Iy, Iymax, Hy⟩ := exists_max_ideal_of_mem_nonunits hy
    have xmemI : x ∈ I := Iuniq Ix Ixmax ▸ Hx
    have ymemI : y ∈ I := Iuniq Iy Iymax ▸ Hy
    Imax.1.1 <| I.eq_top_of_isUnit_mem (I.add_mem xmemI ymemI) H
#align local_ring.of_unique_max_ideal LocalRing.of_unique_max_ideal

theorem of_unique_nonzero_prime (h : ∃! P : Ideal R, P ≠ ⊥ ∧ Ideal.IsPrime P) : LocalRing R :=
  of_unique_max_ideal
    (by
      rcases h with ⟨P, ⟨hPnonzero, hPnot_top, _⟩, hPunique⟩
      refine' ⟨P, ⟨⟨hPnot_top, _⟩⟩, fun M hM => hPunique _ ⟨_, Ideal.IsMaximal.isPrime hM⟩⟩
      · refine' Ideal.maximal_of_no_maximal fun M hPM hM => ne_of_lt hPM _
        exact (hPunique _ ⟨ne_bot_of_gt hPM, Ideal.IsMaximal.isPrime hM⟩).symm
      · rintro rfl
        exact hPnot_top (hM.1.2 P (bot_lt_iff_ne_bot.2 hPnonzero)))
#align local_ring.of_unique_nonzero_prime LocalRing.of_unique_nonzero_prime

variable [LocalRing R]

theorem isUnit_or_isUnit_of_isUnit_add {a b : R} (h : IsUnit (a + b)) : IsUnit a ∨ IsUnit b :=
  by
  rcases h with ⟨u, hu⟩
  rw [← Units.inv_mul_eq_one, mul_add] at hu
  apply Or.imp _ _ (is_unit_or_is_unit_of_add_one hu) <;> exact isUnit_of_mul_isUnit_right
#align local_ring.is_unit_or_is_unit_of_is_unit_add LocalRing.isUnit_or_isUnit_of_isUnit_add

theorem nonunits_add {a b : R} (ha : a ∈ nonunits R) (hb : b ∈ nonunits R) : a + b ∈ nonunits R :=
  fun H => not_or_of_not ha hb (isUnit_or_isUnit_of_isUnit_add H)
#align local_ring.nonunits_add LocalRing.nonunits_add

variable (R)

/-- The ideal of elements that are not units. -/
def maximalIdeal : Ideal R where
  carrier := nonunits R
  zero_mem' := zero_mem_nonunits.2 <| zero_ne_one
  add_mem' x y hx hy := nonunits_add hx hy
  smul_mem' a x := mul_mem_nonunits_right
#align local_ring.maximal_ideal LocalRing.maximalIdeal

instance maximalIdeal.isMaximal : (maximalIdeal R).IsMaximal :=
  by
  rw [Ideal.isMaximal_iff]
  constructor
  · intro h
    apply h
    exact isUnit_one
  · intro I x hI hx H
    erw [Classical.not_not] at hx
    rcases hx with ⟨u, rfl⟩
    simpa using I.mul_mem_left (↑u⁻¹) H
#align local_ring.maximal_ideal.is_maximal LocalRing.maximalIdeal.isMaximal

theorem maximal_ideal_unique : ∃! I : Ideal R, I.IsMaximal :=
  ⟨maximalIdeal R, maximalIdeal.isMaximal R, fun I hI =>
    hI.eq_of_le (maximalIdeal.isMaximal R).1.1 fun x hx => hI.1.1 ∘ I.eq_top_of_isUnit_mem hx⟩
#align local_ring.maximal_ideal_unique LocalRing.maximal_ideal_unique

variable {R}

theorem eq_maximalIdeal {I : Ideal R} (hI : I.IsMaximal) : I = maximalIdeal R :=
  ExistsUnique.unique (maximal_ideal_unique R) hI <| maximalIdeal.isMaximal R
#align local_ring.eq_maximal_ideal LocalRing.eq_maximalIdeal

theorem le_maximalIdeal {J : Ideal R} (hJ : J ≠ ⊤) : J ≤ maximalIdeal R :=
  by
  rcases Ideal.exists_le_maximal J hJ with ⟨M, hM1, hM2⟩
  rwa [← eq_maximal_ideal hM1]
#align local_ring.le_maximal_ideal LocalRing.le_maximalIdeal

@[simp]
theorem mem_maximalIdeal (x) : x ∈ maximalIdeal R ↔ x ∈ nonunits R :=
  Iff.rfl
#align local_ring.mem_maximal_ideal LocalRing.mem_maximalIdeal

theorem isField_iff_maximalIdeal_eq : IsField R ↔ maximalIdeal R = ⊥ :=
  not_iff_not.mp
    ⟨Ring.ne_bot_of_isMaximal_of_not_isField inferInstance, fun h =>
      Ring.not_isField_iff_exists_prime.mpr ⟨_, h, Ideal.IsMaximal.isPrime' _⟩⟩
#align local_ring.is_field_iff_maximal_ideal_eq LocalRing.isField_iff_maximalIdeal_eq

end LocalRing

end CommSemiring

section CommRing

variable [CommRing R]

namespace LocalRing

theorem of_isUnit_or_isUnit_one_sub_self [Nontrivial R] (h : ∀ a : R, IsUnit a ∨ IsUnit (1 - a)) :
    LocalRing R :=
  ⟨fun a b hab => add_sub_cancel' a b ▸ hab.symm ▸ h a⟩
#align local_ring.of_is_unit_or_is_unit_one_sub_self LocalRing.of_isUnit_or_isUnit_one_sub_self

variable [LocalRing R]

theorem isUnit_or_isUnit_one_sub_self (a : R) : IsUnit a ∨ IsUnit (1 - a) :=
  isUnit_or_isUnit_of_isUnit_add <| (add_sub_cancel'_right a 1).symm ▸ isUnit_one
#align local_ring.is_unit_or_is_unit_one_sub_self LocalRing.isUnit_or_isUnit_one_sub_self

theorem isUnit_of_mem_nonunits_one_sub_self (a : R) (h : 1 - a ∈ nonunits R) : IsUnit a :=
  or_iff_not_imp_right.1 (isUnit_or_isUnit_one_sub_self a) h
#align local_ring.is_unit_of_mem_nonunits_one_sub_self LocalRing.isUnit_of_mem_nonunits_one_sub_self

theorem isUnit_one_sub_self_of_mem_nonunits (a : R) (h : a ∈ nonunits R) : IsUnit (1 - a) :=
  or_iff_not_imp_left.1 (isUnit_or_isUnit_one_sub_self a) h
#align local_ring.is_unit_one_sub_self_of_mem_nonunits LocalRing.isUnit_one_sub_self_of_mem_nonunits

theorem of_surjective' [CommRing S] [Nontrivial S] (f : R →+* S) (hf : Function.Surjective f) :
    LocalRing S :=
  of_isUnit_or_isUnit_one_sub_self
    (by
      intro b
      obtain ⟨a, rfl⟩ := hf b
      apply (is_unit_or_is_unit_one_sub_self a).imp f.is_unit_map _
      rw [← f.map_one, ← f.map_sub]
      apply f.is_unit_map)
#align local_ring.of_surjective' LocalRing.of_surjective'

theorem jacobson_eq_maximalIdeal (I : Ideal R) (h : I ≠ ⊤) :
    I.jacobson = LocalRing.maximalIdeal R :=
  by
  apply le_antisymm
  · exact infₛ_le ⟨LocalRing.le_maximalIdeal h, LocalRing.maximalIdeal.isMaximal R⟩
  · exact le_infₛ fun J (hJ : I ≤ J ∧ J.IsMaximal) => le_of_eq (LocalRing.eq_maximalIdeal hJ.2).symm
#align local_ring.jacobson_eq_maximal_ideal LocalRing.jacobson_eq_maximalIdeal

end LocalRing

end CommRing

/-- A local ring homomorphism is a homomorphism `f` between local rings such that `a` in the domain
  is a unit if `f a` is a unit for any `a`. See `local_ring.local_hom_tfae` for other equivalent
  definitions. -/
class IsLocalRingHom [Semiring R] [Semiring S] (f : R →+* S) : Prop where
  map_nonunit : ∀ a, IsUnit (f a) → IsUnit a
#align is_local_ring_hom IsLocalRingHom

section

variable [Semiring R] [Semiring S] [Semiring T]

instance isLocalRingHom_id (R : Type _) [Semiring R] : IsLocalRingHom (RingHom.id R)
    where map_nonunit a := id
#align is_local_ring_hom_id isLocalRingHom_id

@[simp]
theorem isUnit_map_iff (f : R →+* S) [IsLocalRingHom f] (a) : IsUnit (f a) ↔ IsUnit a :=
  ⟨IsLocalRingHom.map_nonunit a, f.isUnit_map⟩
#align is_unit_map_iff isUnit_map_iff

@[simp]
theorem map_mem_nonunits_iff (f : R →+* S) [IsLocalRingHom f] (a) :
    f a ∈ nonunits S ↔ a ∈ nonunits R :=
  ⟨fun h ha => h <| (isUnit_map_iff f a).mpr ha, fun h ha => h <| (isUnit_map_iff f a).mp ha⟩
#align map_mem_nonunits_iff map_mem_nonunits_iff

instance isLocalRingHom_comp (g : S →+* T) (f : R →+* S) [IsLocalRingHom g] [IsLocalRingHom f] :
    IsLocalRingHom (g.comp f)
    where map_nonunit a := IsLocalRingHom.map_nonunit a ∘ IsLocalRingHom.map_nonunit (f a)
#align is_local_ring_hom_comp isLocalRingHom_comp

instance isLocalRingHom_equiv (f : R ≃+* S) : IsLocalRingHom (f : R →+* S)
    where map_nonunit a ha := by
    convert(f.symm : S →+* R).isUnit_map ha
    exact (RingEquiv.symm_apply_apply f a).symm
#align is_local_ring_hom_equiv isLocalRingHom_equiv

@[simp]
theorem isUnit_of_map_unit (f : R →+* S) [IsLocalRingHom f] (a) (h : IsUnit (f a)) : IsUnit a :=
  IsLocalRingHom.map_nonunit a h
#align is_unit_of_map_unit isUnit_of_map_unit

theorem of_irreducible_map (f : R →+* S) [h : IsLocalRingHom f] {x} (hfx : Irreducible (f x)) :
    Irreducible x :=
  ⟨fun h => hfx.not_unit <| IsUnit.map f h, fun p q hx =>
    let ⟨H⟩ := h
    Or.imp (H p) (H q) <| hfx.isUnit_or_isUnit <| f.map_mul p q ▸ congr_arg f hx⟩
#align of_irreducible_map of_irreducible_map

theorem isLocalRingHom_of_comp (f : R →+* S) (g : S →+* T) [IsLocalRingHom (g.comp f)] :
    IsLocalRingHom f :=
  ⟨fun a ha => (isUnit_map_iff (g.comp f) _).mp (g.isUnit_map ha)⟩
#align is_local_ring_hom_of_comp isLocalRingHom_of_comp

/-- If `f : R →+* S` is a local ring hom, then `R` is a local ring if `S` is. -/
theorem RingHom.domain_localRing {R S : Type _} [CommSemiring R] [CommSemiring S] [H : LocalRing S]
    (f : R →+* S) [IsLocalRingHom f] : LocalRing R :=
  by
  haveI : Nontrivial R := pullback_nonzero f f.map_zero f.map_one
  apply LocalRing.of_nonunits_add
  intro a b
  simp_rw [← map_mem_nonunits_iff f, f.map_add]
  exact LocalRing.nonunits_add
#align ring_hom.domain_local_ring RingHom.domain_localRing

end

section

open LocalRing

variable [CommSemiring R] [LocalRing R] [CommSemiring S] [LocalRing S]

/--
The image of the maximal ideal of the source is contained within the maximal ideal of the target.
-/
theorem map_nonunit (f : R →+* S) [IsLocalRingHom f] (a : R) (h : a ∈ maximalIdeal R) :
    f a ∈ maximalIdeal S := fun H => h <| isUnit_of_map_unit f a H
#align map_nonunit map_nonunit

end

namespace LocalRing

section

variable [CommSemiring R] [LocalRing R] [CommSemiring S] [LocalRing S]

/-- A ring homomorphism between local rings is a local ring hom iff it reflects units,
i.e. any preimage of a unit is still a unit. https://stacks.math.columbia.edu/tag/07BJ
-/
theorem local_hom_tFAE (f : R →+* S) :
    TFAE
      [IsLocalRingHom f, f '' (maximalIdeal R).1 ⊆ maximalIdeal S,
        (maximalIdeal R).map f ≤ maximalIdeal S, maximalIdeal R ≤ (maximalIdeal S).comap f,
        (maximalIdeal S).comap f = maximalIdeal R] :=
  by
  tfae_have 1 → 2; rintro _ _ ⟨a, ha, rfl⟩
  skip; exact map_nonunit f a ha
  tfae_have 2 → 4; exact Set.image_subset_iff.1
  tfae_have 3 ↔ 4; exact Ideal.map_le_iff_le_comap
  tfae_have 4 → 1; intro h; fconstructor; exact fun x => not_imp_not.1 (@h x)
  tfae_have 1 → 5; intro ; skip; ext
  exact not_iff_not.2 (isUnit_map_iff f x)
  tfae_have 5 → 4; exact fun h => le_of_eq h.symm
  tfae_finish
#align local_ring.local_hom_tfae LocalRing.local_hom_tFAE

end

theorem of_surjective [CommSemiring R] [LocalRing R] [CommSemiring S] [Nontrivial S] (f : R →+* S)
    [IsLocalRingHom f] (hf : Function.Surjective f) : LocalRing S :=
  of_isUnit_or_isUnit_of_isUnit_add
    (by
      intro a b hab
      obtain ⟨a, rfl⟩ := hf a
      obtain ⟨b, rfl⟩ := hf b
      rw [← map_add] at hab
      exact
        (is_unit_or_is_unit_of_is_unit_add <| IsLocalRingHom.map_nonunit _ hab).imp f.is_unit_map
          f.is_unit_map)
#align local_ring.of_surjective LocalRing.of_surjective

/-- If `f : R →+* S` is a surjective local ring hom, then the induced units map is surjective. -/
theorem surjective_units_map_of_local_ringHom [CommRing R] [CommRing S] (f : R →+* S)
    (hf : Function.Surjective f) (h : IsLocalRingHom f) :
    Function.Surjective (Units.map <| f.toMonoidHom) :=
  by
  intro a
  obtain ⟨b, hb⟩ := hf (a : S)
  use
    (isUnit_of_map_unit f _
        (by
          rw [hb]
          exact Units.isUnit _)).Unit;
  ext; exact hb
#align local_ring.surjective_units_map_of_local_ring_hom LocalRing.surjective_units_map_of_local_ringHom

section

variable (R) [CommRing R] [LocalRing R] [CommRing S] [LocalRing S] [CommRing T] [LocalRing T]

/-- The residue field of a local ring is the quotient of the ring by its maximal ideal. -/
def ResidueField :=
  R ⧸ maximalIdeal R deriving Ring, CommRing, Inhabited
#align local_ring.residue_field LocalRing.ResidueField

noncomputable instance ResidueField.field : Field (ResidueField R) :=
  Ideal.Quotient.field (maximalIdeal R)
#align local_ring.residue_field.field LocalRing.ResidueField.field

/-- The quotient map from a local ring to its residue field. -/
def residue : R →+* ResidueField R :=
  Ideal.Quotient.mk _
#align local_ring.residue LocalRing.residue

instance ResidueField.algebra : Algebra R (ResidueField R) :=
  Ideal.Quotient.algebra _
#align local_ring.residue_field.algebra LocalRing.ResidueField.algebra

theorem ResidueField.algebraMap_eq : algebraMap R (ResidueField R) = residue R :=
  rfl
#align local_ring.residue_field.algebra_map_eq LocalRing.ResidueField.algebraMap_eq

instance : IsLocalRingHom (LocalRing.residue R) :=
  ⟨fun a ha =>
    Classical.not_not.mp (Ideal.Quotient.eq_zero_iff_mem.Not.mp (isUnit_iff_ne_zero.mp ha))⟩

variable {R}

namespace ResidueField

/-- A local ring homomorphism into a field can be descended onto the residue field. -/
def lift {R S : Type _} [CommRing R] [LocalRing R] [Field S] (f : R →+* S) [IsLocalRingHom f] :
    LocalRing.ResidueField R →+* S :=
  Ideal.Quotient.lift _ f fun a ha =>
    by_contradiction fun h => ha (isUnit_of_map_unit f a (isUnit_iff_ne_zero.mpr h))
#align local_ring.residue_field.lift LocalRing.ResidueField.lift

theorem lift_comp_residue {R S : Type _} [CommRing R] [LocalRing R] [Field S] (f : R →+* S)
    [IsLocalRingHom f] : (lift f).comp (residue R) = f :=
  RingHom.ext fun _ => rfl
#align local_ring.residue_field.lift_comp_residue LocalRing.ResidueField.lift_comp_residue

@[simp]
theorem lift_residue_apply {R S : Type _} [CommRing R] [LocalRing R] [Field S] (f : R →+* S)
    [IsLocalRingHom f] (x) : lift f (residue R x) = f x :=
  rfl
#align local_ring.residue_field.lift_residue_apply LocalRing.ResidueField.lift_residue_apply

/-- The map on residue fields induced by a local homomorphism between local rings -/
def map (f : R →+* S) [IsLocalRingHom f] : ResidueField R →+* ResidueField S :=
  Ideal.Quotient.lift (maximalIdeal R) ((Ideal.Quotient.mk _).comp f) fun a ha =>
    by
    erw [Ideal.Quotient.eq_zero_iff_mem]
    exact map_nonunit f a ha
#align local_ring.residue_field.map LocalRing.ResidueField.map

/-- Applying `residue_field.map` to the identity ring homomorphism gives the identity
ring homomorphism. -/
@[simp]
theorem map_id :
    LocalRing.ResidueField.map (RingHom.id R) = RingHom.id (LocalRing.ResidueField R) :=
  Ideal.Quotient.ringHom_ext <| RingHom.ext fun x => rfl
#align local_ring.residue_field.map_id LocalRing.ResidueField.map_id

/-- The composite of two `residue_field.map`s is the `residue_field.map` of the composite. -/
theorem map_comp (f : T →+* R) (g : R →+* S) [IsLocalRingHom f] [IsLocalRingHom g] :
    LocalRing.ResidueField.map (g.comp f) =
      (LocalRing.ResidueField.map g).comp (LocalRing.ResidueField.map f) :=
  Ideal.Quotient.ringHom_ext <| RingHom.ext fun x => rfl
#align local_ring.residue_field.map_comp LocalRing.ResidueField.map_comp

theorem map_comp_residue (f : R →+* S) [IsLocalRingHom f] :
    (ResidueField.map f).comp (residue R) = (residue S).comp f :=
  rfl
#align local_ring.residue_field.map_comp_residue LocalRing.ResidueField.map_comp_residue

theorem map_residue (f : R →+* S) [IsLocalRingHom f] (r : R) :
    ResidueField.map f (residue R r) = residue S (f r) :=
  rfl
#align local_ring.residue_field.map_residue LocalRing.ResidueField.map_residue

theorem map_id_apply (x : ResidueField R) : map (RingHom.id R) x = x :=
  FunLike.congr_fun map_id x
#align local_ring.residue_field.map_id_apply LocalRing.ResidueField.map_id_apply

@[simp]
theorem map_map (f : R →+* S) (g : S →+* T) (x : ResidueField R) [IsLocalRingHom f]
    [IsLocalRingHom g] : map g (map f x) = map (g.comp f) x :=
  FunLike.congr_fun (map_comp f g).symm x
#align local_ring.residue_field.map_map LocalRing.ResidueField.map_map

/-- A ring isomorphism defines an isomorphism of residue fields. -/
@[simps apply]
def mapEquiv (f : R ≃+* S) : LocalRing.ResidueField R ≃+* LocalRing.ResidueField S
    where
  toFun := map (f : R →+* S)
  invFun := map (f.symm : S →+* R)
  left_inv x := by simp only [map_map, RingEquiv.symm_comp, map_id, RingHom.id_apply]
  right_inv x := by simp only [map_map, RingEquiv.comp_symm, map_id, RingHom.id_apply]
  map_mul' := RingHom.map_mul _
  map_add' := RingHom.map_add _
#align local_ring.residue_field.map_equiv LocalRing.ResidueField.mapEquiv

@[simp]
theorem mapEquiv.symm (f : R ≃+* S) : (mapEquiv f).symm = mapEquiv f.symm :=
  rfl
#align local_ring.residue_field.map_equiv.symm LocalRing.ResidueField.mapEquiv.symm

@[simp]
theorem mapEquiv_trans (e₁ : R ≃+* S) (e₂ : S ≃+* T) :
    mapEquiv (e₁.trans e₂) = (mapEquiv e₁).trans (mapEquiv e₂) :=
  RingEquiv.toRingHom_injective <| map_comp (e₁ : R →+* S) (e₂ : S →+* T)
#align local_ring.residue_field.map_equiv_trans LocalRing.ResidueField.mapEquiv_trans

@[simp]
theorem mapEquiv_refl : mapEquiv (RingEquiv.refl R) = RingEquiv.refl _ :=
  RingEquiv.toRingHom_injective map_id
#align local_ring.residue_field.map_equiv_refl LocalRing.ResidueField.mapEquiv_refl

/-- The group homomorphism from `ring_aut R` to `ring_aut k` where `k`
is the residue field of `R`. -/
@[simps]
def mapAut : RingAut R →* RingAut (LocalRing.ResidueField R)
    where
  toFun := mapEquiv
  map_mul' e₁ e₂ := mapEquiv_trans e₂ e₁
  map_one' := mapEquiv_refl
#align local_ring.residue_field.map_aut LocalRing.ResidueField.mapAut

section MulSemiringAction

variable (G : Type _) [Group G] [MulSemiringAction G R]

/-- If `G` acts on `R` as a `mul_semiring_action`, then it also acts on `residue_field R`. -/
instance : MulSemiringAction G (LocalRing.ResidueField R) :=
  MulSemiringAction.compHom _ <| mapAut.comp (MulSemiringAction.toRingAut G R)

@[simp]
theorem residue_smul (g : G) (r : R) : residue R (g • r) = g • residue R r :=
  rfl
#align local_ring.residue_field.residue_smul LocalRing.ResidueField.residue_smul

end MulSemiringAction

end ResidueField

theorem ker_eq_maximalIdeal [Field K] (φ : R →+* K) (hφ : Function.Surjective φ) :
    φ.ker = maximalIdeal R :=
  LocalRing.eq_maximalIdeal <| (RingHom.ker_isMaximal_of_surjective φ) hφ
#align local_ring.ker_eq_maximal_ideal LocalRing.ker_eq_maximalIdeal

theorem isLocalRingHom_residue : IsLocalRingHom (LocalRing.residue R) :=
  by
  constructor
  intro a ha
  by_contra
  erw [ideal.quotient.eq_zero_iff_mem.mpr ((LocalRing.mem_maximalIdeal _).mpr h)] at ha
  exact ha.ne_zero rfl
#align local_ring.is_local_ring_hom_residue LocalRing.isLocalRingHom_residue

end

end LocalRing

namespace Field

variable (K) [Field K]

open Classical

-- see Note [lower instance priority]
instance (priority := 100) : LocalRing K :=
  LocalRing.of_isUnit_or_isUnit_one_sub_self fun a =>
    if h : a = 0 then Or.inr (by rw [h, sub_zero] <;> exact isUnit_one)
    else Or.inl <| IsUnit.mk0 a h

end Field

theorem LocalRing.maximalIdeal_eq_bot {R : Type _} [Field R] : LocalRing.maximalIdeal R = ⊥ :=
  LocalRing.isField_iff_maximalIdeal_eq.mp (Field.toIsField R)
#align local_ring.maximal_ideal_eq_bot LocalRing.maximalIdeal_eq_bot

namespace RingEquiv

@[reducible]
protected theorem localRing {A B : Type _} [CommSemiring A] [LocalRing A] [CommSemiring B]
    (e : A ≃+* B) : LocalRing B :=
  haveI := e.symm.to_equiv.nontrivial
  LocalRing.of_surjective (e : A →+* B) e.surjective
#align ring_equiv.local_ring RingEquiv.localRing

end RingEquiv


/-
Copyright (c) 2019 Kevin Kappelmann. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Kappelmann
-/
import data.seq.seq
import algebra.field
/-!
# Basic Definitions/Theorems for Continued Fractions

## Summary

We define generalised, simple, and regular continued fractions and functions to evaluate their
convergents. We follow the naming conventions from Wikipedia and [wall2018analytic], Chapter 1.

## Main definitions

1. Generalised continued fractions (gcfs)
2. Simple continued fractions (scfs)
3. (Regular) continued fractions ((r)cfs)
4. Computation of convergents using the recurrence relation in `convergents`.
5. Computation of convergents by directly evaluating the fraction described by the gcf in
`convergents'`.

## Implementation notes

1. The most commonly used kind of continued fractions in the literature are regular continued
fractions. We hence just call them `continued_fractions` in the library.
2. We use sequences from `data.seq` to encode potentially infinite sequences.

## References

- <https://en.wikipedia.org/wiki/Generalized_continued_fraction>
- [Wall, H.S., *Analytic Theory of Continued Fractions*][wall2018analytic]

## Tags

numerics, number theory, approximations, fractions
-/

-- Fix a carrier `α`.
variable (α : Type*)
/-!### Definitions-/

/-- We collect a partial numerator `aᵢ` and partial denominator `bᵢ` in a pair `⟨aᵢ,bᵢ⟩`. -/
@[derive inhabited]
protected structure generalized_continued_fraction.pair := (a : α) (b : α)

/-! Interlude: define some expected coercions and instances. -/
namespace generalized_continued_fraction.pair
open generalized_continued_fraction as gcf

variable {α}

/-- Make a `gcf.pair` printable. -/
instance [has_repr α] : has_repr (gcf.pair α) :=
⟨λ p, "(a : " ++ (repr p.a) ++ ", b : " ++ (repr p.b) ++ ")"⟩

/-- Maps a function `f` on both components of a given pair. -/
def map {β : Type*} (f : α → β) (gp : gcf.pair α) : gcf.pair β :=
⟨f gp.a, f gp.b⟩

section coe
/- Fix another type `β` which we will convert to. -/
variables {β : Type*} [has_coe α β]

/-- Coerce a pair by elementwise coercion. -/
instance has_coe_to_generalized_continued_fraction_pair :
  has_coe (gcf.pair α) (gcf.pair β) :=
⟨map coe⟩

@[simp, norm_cast]

end coe
end generalized_continued_fraction.pair

variable (α)

/--
A *generalised continued fraction* (gcf) is a potentially infinite expression of the form

                                a₀
                h + ---------------------------
                                  a₁
                      b₀ + --------------------
                                    a₂
                            b₁ + --------------
                                        a₃
                                  b₂ + --------
                                      b₃ + ...

where `h` is called the *head term* or *integer part*, the `aᵢ` are called the
*partial numerators* and the `bᵢ` the *partial denominators* of the gcf.
We store the sequence of partial numerators and denominators in a sequence of
generalized_continued_fraction.pairs `s`.
For convenience, one often writes `[h; (a₀, b₀), (a₁, b₁), (a₂, b₂),...]`.
-/
structure generalized_continued_fraction :=
(h : α) (s : seq $ generalized_continued_fraction.pair α)

variable {α}

namespace generalized_continued_fraction
open generalized_continued_fraction as gcf

/-- Constructs a generalized continued fraction without fractional part. -/
def of_integer (a : α) : gcf α :=
⟨a, seq.nil⟩

instance [inhabited α] : inhabited (gcf α) := ⟨of_integer (default _)⟩

/-- Returns the sequence of partial numerators `aᵢ` of `g`. -/
def partial_numerators (g : gcf α) : seq α := g.s.map gcf.pair.a
/-- Returns the sequence of partial denominators `bᵢ` of `g`. -/
def partial_denominators (g : gcf α) : seq α := g.s.map gcf.pair.b

/-- A gcf terminated at position `n` if its sequence terminates at position `n`. -/
def terminated_at (g : gcf α) (n : ℕ) : Prop := g.s.terminated_at n

/-- It is decidable whether a gcf terminated at a given position. -/
instance terminated_at_decidable (g : gcf α) (n : ℕ) : decidable (g.terminated_at n) :=
by { unfold terminated_at, apply_instance }

/-- A gcf terminates if its sequence terminates. -/
def terminates (g : gcf α) : Prop := g.s.terminates

section coe
/-! Interlude: define some expected coercions. -/
/- Fix another type `β` which we will convert to. -/
variables {β : Type*} [has_coe α β]

/-- Coerce a gcf by elementwise coercion. -/
instance has_coe_to_generalized_continued_fraction : has_coe (gcf α) (gcf β) :=
⟨λ g, ⟨(g.h : β), (g.s.map coe : seq $ gcf.pair β)⟩⟩

@[simp, norm_cast]
lemma coe_to_generalized_continued_fraction {g : gcf α} :
  (↑(g : gcf α) : gcf β) = ⟨(g.h : β), (g.s.map coe : seq $ gcf.pair β)⟩ :=
rfl

end coe
end generalized_continued_fraction

/--
A generalized continued fraction is a *simple continued fraction* if all partial numerators are
equal to one.

                                1
                h + ---------------------------
                                  1
                      b₀ + --------------------
                                    1
                            b₁ + --------------
                                        1
                                  b₂ + --------
                                      b₃ + ...

-/
def generalized_continued_fraction.is_simple_continued_fraction
  (g : generalized_continued_fraction α) [has_one α] : Prop :=
∀ (n : ℕ) (aₙ : α), g.partial_numerators.nth n = some aₙ → aₙ = 1

variable (α)
/--
A *simple continued fraction* (scf) is a generalized continued fraction (gcf) whose partial
numerators are equal to one.

                                1
                h + ---------------------------
                                  1
                      b₀ + --------------------
                                    1
                            b₁ + --------------
                                        1
                                  b₂ + --------
                                      b₃ + ...

For convenience, one often writes `[h; b₀, b₁, b₂,...]`.
It is encoded as the subtype of gcfs that satisfy
`generalized_continued_fraction.is_simple_continued_fraction`.
 -/
def simple_continued_fraction [has_one α] :=
{g : generalized_continued_fraction α // g.is_simple_continued_fraction}

variable {α}
/- Interlude: define some expected coercions. -/
namespace simple_continued_fraction
open generalized_continued_fraction as gcf
open simple_continued_fraction as scf
variable [has_one α]

/-- Constructs a simple continued fraction without fractional part. -/
def of_integer (a : α) : scf α :=
⟨gcf.of_integer a, λ n aₙ h, by cases h⟩

instance : inhabited (scf α) := ⟨of_integer 1⟩

/-- Lift a scf to a gcf using the inclusion map. -/
instance has_coe_to_generalized_continued_fraction : has_coe (scf α) (gcf α) :=
by {unfold scf, apply_instance}

lemma coe_to_generalized_continued_fraction {s : scf α} : (↑s : gcf α) = s.val := rfl

end simple_continued_fraction

/--
A simple continued fraction is a *(regular) continued fraction* ((r)cf) if all partial denominators
`bᵢ` are positive, i.e. `0 < bᵢ`.
-/
def simple_continued_fraction.is_continued_fraction [has_one α] [has_zero α] [has_lt α]
  (s : simple_continued_fraction α) : Prop :=
∀ (n : ℕ) (bₙ : α),
  (↑s : generalized_continued_fraction α).partial_denominators.nth n = some bₙ → 0 < bₙ

variable (α)

/--
A *(regular) continued fraction* ((r)cf) is a simple continued fraction (scf) whose partial
denominators are all positive. It is the subtype of scfs that satisfy
`simple_continued_fraction.is_continued_fraction`.
 -/
def continued_fraction [has_one α] [has_zero α] [has_lt α] :=
{s : simple_continued_fraction α // s.is_continued_fraction}

variable {α}

/-! Interlude: define some expected coercions. -/
namespace continued_fraction
open generalized_continued_fraction as gcf
open simple_continued_fraction as scf
open continued_fraction as cf
variables [has_one α] [has_zero α] [has_lt α]

/-- Constructs a continued fraction without fractional part. -/
def of_integer (a : α) : cf α :=
⟨scf.of_integer a, λ n bₙ h, by cases h⟩

instance : inhabited (cf α) := ⟨of_integer 0⟩

/-- Lift a cf to a scf using the inclusion map. -/
instance has_coe_to_simple_continued_fraction : has_coe (cf α) (scf α) :=
by {unfold cf, apply_instance}

lemma coe_to_simple_continued_fraction {c : cf α} : (↑c : scf α) = c.val := rfl

/-- Lift a cf to a scf using the inclusion map. -/
instance has_coe_to_generalized_continued_fraction : has_coe (cf α) (gcf α) := ⟨λ c, ↑(↑c : scf α)⟩

lemma coe_to_generalized_continued_fraction {c : cf α} : (↑c : gcf α) = c.val := rfl

end continued_fraction

namespace generalized_continued_fraction
/-!
### Computation of Convergents

We now define how to compute the convergents of a gcf. There are two standard ways to do this:
directly evaluating the (infinite) fraction described by the gcf or using a recurrence relation.
For (r)cfs, these computations are equivalent as shown in
`algebra.continued_fractions.convergents_equiv`.
-/
open generalized_continued_fraction as gcf

-- Fix a division ring for the computations.
variables {K : Type*} [division_ring K]

/-!
We start with the definition of the recurrence relation. Given a gcf `g`, for all `n ≥ 1`, we define
- `A₋₁ = 1,  A₀ = h,  Aₙ = bₙ₋₁ * Aₙ₋₁ + aₙ₋₁ * Aₙ₋₂`, and
- `B₋₁ = 0,  B₀ = 1,  Bₙ = bₙ₋₁ * Bₙ₋₁ + aₙ₋₁ * Bₙ₋₂`.

`Aₙ, `Bₙ` are called the *nth continuants*, Aₙ the *nth numerator*, and `Bₙ` the
*nth denominator* of `g`. The *nth convergent* of `g` is given by `Aₙ / Bₙ`.
-/

/--
Returns the next numerator `Aₙ = bₙ₋₁ * Aₙ₋₁ + aₙ₋₁ * Aₙ₋₂`, where `predA` is `Aₙ₋₁`,
`ppredA` is `Aₙ₋₂`, `a` is `aₙ₋₁`, and `b` is `bₙ₋₁`.
-/
def next_numerator (a b ppredA predA : K) : K := b * predA + a * ppredA

/--
Returns the next denominator `Bₙ = bₙ₋₁ * Bₙ₋₁ + aₙ₋₁ * Bₙ₋₂``, where `predB` is `Bₙ₋₁` and
`ppredB` is `Bₙ₋₂`, `a` is `aₙ₋₁`, and `b` is `bₙ₋₁`.
-/
def next_denominator (aₙ bₙ ppredB predB : K) : K := bₙ * predB + aₙ * ppredB

/--
Returns the next continuants `⟨Aₙ, Bₙ⟩` using `next_numerator` and `next_denominator`, where `pred`
is `⟨Aₙ₋₁, Bₙ₋₁⟩`, `ppred` is `⟨Aₙ₋₂, Bₙ₋₂⟩`, `a` is `aₙ₋₁`, and `b` is `bₙ₋₁`.
-/
def next_continuants (a b : K) (ppred pred : gcf.pair K) : gcf.pair K :=
⟨next_numerator a b ppred.a pred.a, next_denominator a b ppred.b pred.b⟩

/-- Returns the continuants `⟨Aₙ₋₁, Bₙ₋₁⟩` of `g`. -/
def continuants_aux (g : gcf K) : stream (gcf.pair K)
| 0 := ⟨1, 0⟩
| 1 := ⟨g.h, 1⟩
| (n + 2) :=
  match g.s.nth n with
  | none := continuants_aux (n + 1)
  | some gp := next_continuants gp.a gp.b (continuants_aux n) (continuants_aux $ n + 1)
  end

/-- Returns the continuants `⟨Aₙ, Bₙ⟩` of `g`. -/
def continuants (g : gcf K) : stream (gcf.pair K) := g.continuants_aux.tail

/-- Returns the numerators `Aₙ` of `g`. -/
def numerators (g : gcf K) : stream K := g.continuants.map gcf.pair.a

/-- Returns the denominators `Bₙ` of `g`. -/
def denominators (g : gcf K) : stream K := g.continuants.map gcf.pair.b

/-- Returns the convergents `Aₙ / Bₙ` of `g`, where `Aₙ, Bₙ` are the nth continuants of `g`. -/
def convergents (g : gcf K) : stream K := λ (n : ℕ), (g.numerators n) / (g.denominators n)

/--
Returns the approximation of the fraction described by the given sequence up to a given position n.
For example, `convergents'_aux [(1, 2), (3, 4), (5, 6)] 2 = 1 / (2 + 3 / 4)` and
`convergents'_aux [(1, 2), (3, 4), (5, 6)] 0 = 0`.
-/
def convergents'_aux : seq (gcf.pair K) → ℕ → K
| s 0 := 0
| s (n + 1) := match s.head with
  | none := 0
  | some gp := gp.a / (gp.b + convergents'_aux s.tail n)
  end

/--
Returns the convergents of `g` by evaluating the fraction described by `g` up to a given
position `n`. For example, `convergents' [9; (1, 2), (3, 4), (5, 6)] 2 = 9 + 1 / (2 + 3 / 4)` and
`convergents' [9; (1, 2), (3, 4), (5, 6)] 0 = 9`
-/
def convergents' (g : gcf K) (n : ℕ) : K := g.h + convergents'_aux g.s n

end generalized_continued_fraction

-- Now, some basic, general theorems
namespace generalized_continued_fraction
open generalized_continued_fraction as gcf

/-- Two gcfs `g` and `g'` are equal if and only if their components are equal. -/
protected lemma ext_iff {g g' : gcf α} : g = g' ↔ g.h = g'.h ∧ g.s = g'.s :=
by { cases g, cases g', simp }

@[ext]
protected lemma ext {g g' : gcf α} (hyp : g.h = g'.h ∧ g.s = g'.s) : g = g' :=
generalized_continued_fraction.ext_iff.elim_right hyp

end generalized_continued_fraction


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

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

/-!
# Functions differentiable on a domain and continuous on its closure

Many theorems in complex analysis assume that a function is complex differentiable on a domain and
is continuous on its closure. In this file we define a predicate `diff_cont_on_cl` that expresses
this property and prove basic facts about this predicate.
-/

open Set Filter Metric

open Topology

variable (𝕜 : Type _) {E F G : Type _} [NontriviallyNormedField 𝕜] [NormedAddCommGroup E]
  [NormedAddCommGroup F] [NormedSpace 𝕜 E] [NormedSpace 𝕜 F] [NormedAddCommGroup G]
  [NormedSpace 𝕜 G] {f g : E → F} {s t : Set E} {x : E}

/-- A predicate saying that a function is differentiable on a set and is continuous on its
closure. This is a common assumption in complex analysis. -/
@[protect_proj]
structure DiffContOnCl (f : E → F) (s : Set E) : Prop where
  DifferentiableOn : DifferentiableOn 𝕜 f s
  ContinuousOn : ContinuousOn f (closure s)
#align diff_cont_on_cl DiffContOnCl

variable {𝕜}

theorem DifferentiableOn.diffContOnCl (h : DifferentiableOn 𝕜 f (closure s)) : DiffContOnCl 𝕜 f s :=
  ⟨h.mono subset_closure, h.ContinuousOn⟩
#align differentiable_on.diff_cont_on_cl DifferentiableOn.diffContOnCl

theorem Differentiable.diffContOnCl (h : Differentiable 𝕜 f) : DiffContOnCl 𝕜 f s :=
  ⟨h.DifferentiableOn, h.Continuous.ContinuousOn⟩
#align differentiable.diff_cont_on_cl Differentiable.diffContOnCl

theorem IsClosed.diffContOnCl_iff (hs : IsClosed s) : DiffContOnCl 𝕜 f s ↔ DifferentiableOn 𝕜 f s :=
  ⟨fun h => h.DifferentiableOn, fun h => ⟨h, hs.closure_eq.symm ▸ h.ContinuousOn⟩⟩
#align is_closed.diff_cont_on_cl_iff IsClosed.diffContOnCl_iff

theorem diffContOnCl_univ : DiffContOnCl 𝕜 f univ ↔ Differentiable 𝕜 f :=
  isClosed_univ.diffContOnCl_iff.trans differentiableOn_univ
#align diff_cont_on_cl_univ diffContOnCl_univ

theorem diffContOnCl_const {c : F} : DiffContOnCl 𝕜 (fun x : E => c) s :=
  ⟨differentiableOn_const c, continuousOn_const⟩
#align diff_cont_on_cl_const diffContOnCl_const

namespace DiffContOnCl

theorem comp {g : G → E} {t : Set G} (hf : DiffContOnCl 𝕜 f s) (hg : DiffContOnCl 𝕜 g t)
    (h : MapsTo g t s) : DiffContOnCl 𝕜 (f ∘ g) t :=
  ⟨hf.1.comp hg.1 h, hf.2.comp hg.2 <| h.closure_of_continuousOn hg.2⟩
#align diff_cont_on_cl.comp DiffContOnCl.comp

theorem continuousOn_ball [NormedSpace ℝ E] {x : E} {r : ℝ} (h : DiffContOnCl 𝕜 f (ball x r)) :
    ContinuousOn f (closedBall x r) :=
  by
  rcases eq_or_ne r 0 with (rfl | hr)
  · rw [closed_ball_zero]
    exact continuousOn_singleton f x
  · rw [← closure_ball x hr]
    exact h.continuous_on
#align diff_cont_on_cl.continuous_on_ball DiffContOnCl.continuousOn_ball

theorem mk_ball {x : E} {r : ℝ} (hd : DifferentiableOn 𝕜 f (ball x r))
    (hc : ContinuousOn f (closedBall x r)) : DiffContOnCl 𝕜 f (ball x r) :=
  ⟨hd, hc.mono <| closure_ball_subset_closedBall⟩
#align diff_cont_on_cl.mk_ball DiffContOnCl.mk_ball

protected theorem differentiableAt (h : DiffContOnCl 𝕜 f s) (hs : IsOpen s) (hx : x ∈ s) :
    DifferentiableAt 𝕜 f x :=
  h.DifferentiableOn.DifferentiableAt <| hs.mem_nhds hx
#align diff_cont_on_cl.differentiable_at DiffContOnCl.differentiableAt

theorem differentiable_at' (h : DiffContOnCl 𝕜 f s) (hx : s ∈ 𝓝 x) : DifferentiableAt 𝕜 f x :=
  h.DifferentiableOn.DifferentiableAt hx
#align diff_cont_on_cl.differentiable_at' DiffContOnCl.differentiable_at'

protected theorem mono (h : DiffContOnCl 𝕜 f s) (ht : t ⊆ s) : DiffContOnCl 𝕜 f t :=
  ⟨h.DifferentiableOn.mono ht, h.ContinuousOn.mono (closure_mono ht)⟩
#align diff_cont_on_cl.mono DiffContOnCl.mono

theorem add (hf : DiffContOnCl 𝕜 f s) (hg : DiffContOnCl 𝕜 g s) : DiffContOnCl 𝕜 (f + g) s :=
  ⟨hf.1.add hg.1, hf.2.add hg.2⟩
#align diff_cont_on_cl.add DiffContOnCl.add

theorem add_const (hf : DiffContOnCl 𝕜 f s) (c : F) : DiffContOnCl 𝕜 (fun x => f x + c) s :=
  hf.add diffContOnCl_const
#align diff_cont_on_cl.add_const DiffContOnCl.add_const

theorem const_add (hf : DiffContOnCl 𝕜 f s) (c : F) : DiffContOnCl 𝕜 (fun x => c + f x) s :=
  diffContOnCl_const.add hf
#align diff_cont_on_cl.const_add DiffContOnCl.const_add

theorem neg (hf : DiffContOnCl 𝕜 f s) : DiffContOnCl 𝕜 (-f) s :=
  ⟨hf.1.neg, hf.2.neg⟩
#align diff_cont_on_cl.neg DiffContOnCl.neg

theorem sub (hf : DiffContOnCl 𝕜 f s) (hg : DiffContOnCl 𝕜 g s) : DiffContOnCl 𝕜 (f - g) s :=
  ⟨hf.1.sub hg.1, hf.2.sub hg.2⟩
#align diff_cont_on_cl.sub DiffContOnCl.sub

theorem sub_const (hf : DiffContOnCl 𝕜 f s) (c : F) : DiffContOnCl 𝕜 (fun x => f x - c) s :=
  hf.sub diffContOnCl_const
#align diff_cont_on_cl.sub_const DiffContOnCl.sub_const

theorem const_sub (hf : DiffContOnCl 𝕜 f s) (c : F) : DiffContOnCl 𝕜 (fun x => c - f x) s :=
  diffContOnCl_const.sub hf
#align diff_cont_on_cl.const_sub DiffContOnCl.const_sub

theorem const_smul {R : Type _} [Semiring R] [Module R F] [SMulCommClass 𝕜 R F]
    [ContinuousConstSMul R F] (hf : DiffContOnCl 𝕜 f s) (c : R) : DiffContOnCl 𝕜 (c • f) s :=
  ⟨hf.1.const_smul c, hf.2.const_smul c⟩
#align diff_cont_on_cl.const_smul DiffContOnCl.const_smul

theorem smul {𝕜' : Type _} [NontriviallyNormedField 𝕜'] [NormedAlgebra 𝕜 𝕜'] [NormedSpace 𝕜' F]
    [IsScalarTower 𝕜 𝕜' F] {c : E → 𝕜'} {f : E → F} {s : Set E} (hc : DiffContOnCl 𝕜 c s)
    (hf : DiffContOnCl 𝕜 f s) : DiffContOnCl 𝕜 (fun x => c x • f x) s :=
  ⟨hc.1.smul hf.1, hc.2.smul hf.2⟩
#align diff_cont_on_cl.smul DiffContOnCl.smul

theorem smul_const {𝕜' : Type _} [NontriviallyNormedField 𝕜'] [NormedAlgebra 𝕜 𝕜']
    [NormedSpace 𝕜' F] [IsScalarTower 𝕜 𝕜' F] {c : E → 𝕜'} {s : Set E} (hc : DiffContOnCl 𝕜 c s)
    (y : F) : DiffContOnCl 𝕜 (fun x => c x • y) s :=
  hc.smul diffContOnCl_const
#align diff_cont_on_cl.smul_const DiffContOnCl.smul_const

theorem inv {f : E → 𝕜} (hf : DiffContOnCl 𝕜 f s) (h₀ : ∀ x ∈ closure s, f x ≠ 0) :
    DiffContOnCl 𝕜 f⁻¹ s :=
  ⟨differentiableOn_inv.comp hf.1 fun x hx => h₀ _ (subset_closure hx), hf.2.inv₀ h₀⟩
#align diff_cont_on_cl.inv DiffContOnCl.inv

end DiffContOnCl

theorem Differentiable.comp_diffContOnCl {g : G → E} {t : Set G} (hf : Differentiable 𝕜 f)
    (hg : DiffContOnCl 𝕜 g t) : DiffContOnCl 𝕜 (f ∘ g) t :=
  hf.DiffContOnCl.comp hg (mapsTo_image _ _)
#align differentiable.comp_diff_cont_on_cl Differentiable.comp_diffContOnCl

theorem DifferentiableOn.diffContOnCl_ball {U : Set E} {c : E} {R : ℝ} (hf : DifferentiableOn 𝕜 f U)
    (hc : closedBall c R ⊆ U) : DiffContOnCl 𝕜 f (ball c R) :=
  DiffContOnCl.mk_ball (hf.mono (ball_subset_closedBall.trans hc)) (hf.ContinuousOn.mono hc)
#align differentiable_on.diff_cont_on_cl_ball DifferentiableOn.diffContOnCl_ball


import function.bijection

universes u

inductive vect (α : Type u) : ℕ → Type u
| nil {} : vect 0
| cons {n : ℕ} : α → vect n → vect (n+1)

infixr ` ∺ `:67 := vect.cons
notation `⁅`:1024 v:(foldr `, ` (h t, vect.cons h t) vect.nil `⁆`) := v

namespace vect

-- Head of a vector
attribute [reducible]
definition head {α : Type*} : Π {n : ℕ}, vect α (n+1) → α
| _ (x ∺ _) := x

-- Tail of a vector
definition tail {α : Type*} : Π {n : ℕ}, vect α (n+1) → vect α n
| _ (_ ∺ xs) := xs

--- Repeating an element
definition repeat {α : Type _} (a : α) : Π n, vect α n
| 0 := ⁅⁆
| (nat.succ n) := a ∺ (repeat n)

-- Functor
definition map {α : Type*} {β : Type*} : Π {n : ℕ}, (α → β) → vect α n → vect β n
| _ f ⁅⁆ := ⁅⁆
| _ f (x ∺ xs) := (f x) ∺ (map f xs)

--- Folding to left
definition foldl {α β : Type*} : Π {n : ℕ} (f : α → β → α), α → vect β n → α
| _ f a ⁅⁆ := a
| _ f a (x ∺ xs) := foldl f (f a x) xs

--- Folding to right
definition foldr {α β : Type*} : Π {n : ℕ} (f : α → β → β), β → vect α n → β :=
  λ n f b xs, @foldl (β → β) α _ (λ g a, f a ∘ g) id xs b

-- Zipping pairs of entries
definition zip {α : Type*} {β : Type*} : Π {n : ℕ}, vect α n → vect β n → vect (α×β) n
| _ ⁅⁆ ⁅⁆ := ⁅⁆
| _ (a ∺ as) (b ∺ bs) := (a,b) ∺ zip as bs

-- Upzipping pairs of entries
definition unzip {α : Type*} {β : Type*} : Π {n : ℕ}, vect (α×β) n → (vect α n × vect β n)
| _ ⁅⁆ := (⁅⁆, ⁅⁆)
| _ (ab ∺ abs) :=
  (ab.fst ∺ (unzip abs).fst, ab.snd ∺ (unzip abs).snd)

-- Zipping families
definition zip_fam {α : Type*} {C : α → Type*} : Π {n : ℕ}, (Π (a : α), vect (C a) n) → vect (Π (a : α), C a) n
| 0 _ := ⁅⁆
| (k+1) x := (λ a, (x a).head) ∺ zip_fam (λ a, (x a).tail)

-- Unzipping families
definition unzip_fam {α : Type*} {C : α → Type*} : Π {n : ℕ}, vect (Π a, C a) n → Π a, vect (C a) n
| _ ⁅⁆ _ := ⁅⁆
| _ (x ∺ xs) a := (x a) ∺ (unzip_fam xs a)

-- Accumulate values of functions
definition accum {α : Type*} {β : Type*} [hadd : has_add β] [defb : inhabited β] (f : α → β) : Π{n : ℕ}, vect α n → β
| _ ⁅⁆ := default β
| _ (x ∺ xs) := f x + accum xs

-- Check if an element appears in a vector
definition mem {α : Type*} : Π {n : ℕ}, vect α n → α → Prop
| _ ⁅⁆ y := false
| _ (x ∺ xs) y := (x = y) ∨ xs.mem y

-- Membership of the tail implies that on the whole vector
theorem mem_tail {α : Type*} {n : ℕ} {a : α} {xs : vect α n} : xs.mem a → ∀ {x : α}, (x ∺ xs).mem a :=
  λ h x, or.inr h

/-**************************
 -  Conversion from/to list
 -**************************-/
definition to_list {α : Type*} : Π {n : ℕ}, vect α n → list α
| _ ⁅⁆ := []
| _ (x ∺ xs) := x :: to_list xs

definition from_list {α : Type*} : Π (ls : list α), vect α (ls.length)
| [] := ⁅⁆
| (x :: xs) := x ∺ (from_list xs)

definition bij_to_list {α : Type _} : bijection (Σ n, vect α n) (list α) :=
{
  to_fun := λ v, to_list v.snd,
  inv := λ l, ⟨l.length, from_list l⟩,
  left_inverse :=
    begin
      intros v; cases v with n v,
      dsimp *,
      induction v with k a tl h_ind,
      case nil { refl },
      case cons {
        dsimp [to_list,list.length,from_list],
        let f : (Σ n, vect α n) → (Σ n, vect α n) :=
          λ x, sigma.cases_on x (λ n v, ⟨n+1,a∺v⟩),
        have : ∀ (k : ℕ) (v : vect α k), sigma.mk (k+1) (a∺v) = f ⟨k,v⟩,
          from λ _ _, rfl,
        repeat {rw [this] },
        exact congr_arg f h_ind
      }
    end,
  right_inverse :=
    begin
      intros l,
      dsimp *,
      induction l with a tl h_ind,
      case nil { refl },
      case cons {
        dsimp [from_list,to_list],
        exact congr_arg (list.cons a) h_ind
      }
    end
}

/-*********************
 -  Properties
 - *******************-/

--- `map` of `repeat` equals `repeat` of values.
theorem map_repeat {α β : Type _} {f : α → β} {a : α} : ∀ {n : ℕ}, map f (repeat a n) = repeat (f a) n
| 0 := rfl
| (n+1) := by dsimp [repeat,vect.map]; rw [map_repeat]

--- `map f` with `f` being a constant function is nothing but repeat
theorem map_const {α β : Type _} (b : β) : ∀ {n : ℕ} {as : vect α n}, map (λ _,b) as = repeat b n
| 0 ⁅⁆ := rfl
| (n+1) (a ∺ as) := by intros; dsimp [map,repeat]; rw [map_const]

-- map of compositions give rise to compositions of maps
theorem map_comp {α β γ: Type*} {f : α → β} {g : β → γ} : ∀ {n : ℕ} {t : vect α n}, map (g∘ f) t = map g (map f t)
| _ ⁅⁆ := rfl
| _ (x ∺ xs) := by unfold map; rw [map_comp]

-- map respects the identity
theorem map_id {α : Type*} : ∀ {n : ℕ} {t : vect α n}, map id t = t
| _ ⁅⁆ := rfl
| _ (x ∺ xs) := by dsimp [map,id]; rw [map_id]

-- map respects the function extensionality
theorem map_funext {α β : Type*} {f g : α → β} : (∀ a, f a = g a) → ∀ {n : ℕ} {t : vect α n}, map f t = map g t
| h _ ⁅⁆ := rfl
| h _ (x ∺ xs) := by unfold map; rw [h,map_funext h]

--- fst of unzip is map fst
theorem unzip_fst_is_map_fst {α β : Type _} : ∀ {n : ℕ} {vab : vect (α×β) n}, (unzip vab).fst = map prod.fst vab
| _ ⁅⁆ := rfl
| _ (ab ∺ abs) := by dsimp [unzip,map]; rw [unzip_fst_is_map_fst]

--- snd of unzip is map fst
theorem unzip_snd_is_map_snd {α β : Type _} : ∀ {n : ℕ} {vab : vect (α×β) n}, (unzip vab).snd = map prod.snd vab
| _ ⁅⁆ := rfl
| _ (ab ∺ abs) := by dsimp [unzip,map]; rw [unzip_snd_is_map_snd]

theorem unzip_fam_eval {α : Type _} {C : α → Type _} {a : α} : ∀ {n} {fs : vect (Π a, C a) n}, unzip_fam fs a = fs.map (λf, f a)
| _ ⁅⁆ := rfl
| _ (f ∺ fs) := by dsimp [unzip_fam,map]; rw [unzip_fam_eval]

-- vector of image is image of map
theorem image {α β : Type*} {f : α → β} : ∀ {n : ℕ} (t : vect {b // ∃ a, f a = b} n), ∃ (v : vect α n), map f v = map subtype.val t
| _ ⁅⁆ := by existsi ⁅⁆; refl
| _ (x ∺ xs) :=
  begin
    apply exists.elim x.property,
    intros a ha,
    apply exists.elim (image xs),
    intros as has,
    existsi (a ∺ as),
    unfold map,
    rw [ha,has]
  end

end vect


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

! This file was ported from Lean 3 source module category_theory.elements
! leanprover-community/mathlib commit 8a318021995877a44630c898d0b2bc376fceef3b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.StructuredArrow
import Mathlib.CategoryTheory.Groupoid
import Mathlib.CategoryTheory.PUnit

/-!
# The category of elements

This file defines the category of elements, also known as (a special case of) the Grothendieck
construction.

Given a functor `F : C ⥤ Type`, an object of `F.Elements` is a pair `(X : C, x : F.obj X)`.
A morphism `(X, x) ⟶ (Y, y)` is a morphism `f : X ⟶ Y` in `C`, so `F.map f` takes `x` to `y`.

## Implementation notes

This construction is equivalent to a special case of a comma construction, so this is mostly just a
more convenient API. We prove the equivalence in
`CategoryTheory.CategoryOfElements.structuredArrowEquivalence`.

## References
* [Emily Riehl, *Category Theory in Context*, Section 2.4][riehl2017]
* <https://en.wikipedia.org/wiki/Category_of_elements>
* <https://ncatlab.org/nlab/show/category+of+elements>

## Tags
category of elements, Grothendieck construction, comma category
-/

namespace CategoryTheory

universe w v u

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

/-- The type of objects for the category of elements of a functor `F : C ⥤ Type`
is a pair `(X : C, x : F.obj X)`.
-/
def Functor.Elements (F : C ⥤ Type w) :=
  Σc : C, F.obj c
#align category_theory.functor.elements CategoryTheory.Functor.Elements

-- porting note: added because Sigma.ext would be trigged automatically
lemma Functor.Elements.ext {F : C ⥤ Type w} (x y : F.Elements) (h₁ : x.fst = y.fst)
  (h₂ : F.map (eqToHom h₁) x.snd = y.snd) : x = y := by
  cases x
  cases y
  cases h₁
  simp at h₂
  simp [h₂]

/-- The category structure on `F.Elements`, for `F : C ⥤ Type`.
    A morphism `(X, x) ⟶ (Y, y)` is a morphism `f : X ⟶ Y` in `C`, so `F.map f` takes `x` to `y`.
 -/
instance categoryOfElements (F : C ⥤ Type w) : Category.{v} F.Elements
    where
  Hom p q := { f : p.1 ⟶ q.1 // (F.map f) p.2 = q.2 }
  id p := ⟨𝟙 p.1, by aesop_cat⟩ -- porting note: was `obviously`
  comp {X Y Z} f g := ⟨f.val ≫ g.val, by simp [f.2, g.2]⟩
#align category_theory.category_of_elements CategoryTheory.categoryOfElements

namespace CategoryOfElements

@[ext]
theorem ext (F : C ⥤ Type w) {x y : F.Elements} (f g : x ⟶ y) (w : f.val = g.val) : f = g :=
  Subtype.ext_val w
#align category_theory.category_of_elements.ext CategoryTheory.CategoryOfElements.ext

@[simp]
theorem comp_val {F : C ⥤ Type w} {p q r : F.Elements} {f : p ⟶ q} {g : q ⟶ r} :
    (f ≫ g).val = f.val ≫ g.val :=
  rfl
#align category_theory.category_of_elements.comp_val CategoryTheory.CategoryOfElements.comp_val

@[simp]
theorem id_val {F : C ⥤ Type w} {p : F.Elements} : (𝟙 p : p ⟶ p).val = 𝟙 p.1 :=
  rfl
#align category_theory.category_of_elements.id_val CategoryTheory.CategoryOfElements.id_val

end CategoryOfElements

noncomputable instance groupoidOfElements {G : Type u} [Groupoid.{v} G] (F : G ⥤ Type w) :
    Groupoid F.Elements
    where
  inv {p q} f :=
    ⟨inv f.val,
      calc
        F.map (inv f.val) q.2 = F.map (inv f.val) (F.map f.val p.2) := by rw [f.2]
        _ = (F.map f.val ≫ F.map (inv f.val)) p.2 := rfl
        _ = p.2 := by
          rw [← F.map_comp]
          simp
        ⟩
  inv_comp _ := by
    ext
    simp
  comp_inv _ := by
    ext
    simp
#align category_theory.groupoid_of_elements CategoryTheory.groupoidOfElements

namespace CategoryOfElements

variable (F : C ⥤ Type w)

/-- The functor out of the category of elements which forgets the element. -/
@[simps]
def π : F.Elements ⥤ C where
  obj X := X.1
  map f := f.val
#align category_theory.category_of_elements.π CategoryTheory.CategoryOfElements.π

/-- A natural transformation between functors induces a functor between the categories of elements.
-/
@[simps]
def map {F₁ F₂ : C ⥤ Type w} (α : F₁ ⟶ F₂) : F₁.Elements ⥤ F₂.Elements
    where
  obj t := ⟨t.1, α.app t.1 t.2⟩
  map {t₁ t₂} k := ⟨k.1, by simpa [← k.2] using (FunctorToTypes.naturality _ _ α k.1 t₁.2).symm⟩
#align category_theory.category_of_elements.map CategoryTheory.CategoryOfElements.map

@[simp]
theorem map_π {F₁ F₂ : C ⥤ Type w} (α : F₁ ⟶ F₂) : map α ⋙ π F₂ = π F₁ :=
  rfl
#align category_theory.category_of_elements.map_π CategoryTheory.CategoryOfElements.map_π

/-- The forward direction of the equivalence `F.Elements ≅ (*, F)`. -/
def toStructuredArrow : F.Elements ⥤ StructuredArrow PUnit F
    where
  obj X := StructuredArrow.mk fun _ => X.2
  map {X Y} f := StructuredArrow.homMk f.val (by funext; simp [f.2])

#align category_theory.category_of_elements.to_structured_arrow
  CategoryTheory.CategoryOfElements.toStructuredArrow

@[simp]
theorem toStructuredArrow_obj (X) :
    (toStructuredArrow F).obj X =
      { left := ⟨⟨⟩⟩
        right := X.1
        hom := fun _ => X.2 } :=
  rfl
#align category_theory.category_of_elements.to_structured_arrow_obj
  CategoryTheory.CategoryOfElements.toStructuredArrow_obj

@[simp]
theorem to_comma_map_right {X Y} (f : X ⟶ Y) : ((toStructuredArrow F).map f).right = f.val :=
  rfl
#align category_theory.category_of_elements.to_comma_map_right
  CategoryTheory.CategoryOfElements.to_comma_map_right

/-- The reverse direction of the equivalence `F.Elements ≅ (*, F)`. -/
def fromStructuredArrow : StructuredArrow PUnit F ⥤ F.Elements
    where
  obj X := ⟨X.right, X.hom PUnit.unit⟩
  map f := ⟨f.right, congr_fun f.w.symm PUnit.unit⟩
#align category_theory.category_of_elements.from_structured_arrow
  CategoryTheory.CategoryOfElements.fromStructuredArrow

@[simp]
theorem fromStructuredArrow_obj (X) : (fromStructuredArrow F).obj X = ⟨X.right, X.hom PUnit.unit⟩ :=
  rfl
#align category_theory.category_of_elements.from_structured_arrow_obj
  CategoryTheory.CategoryOfElements.fromStructuredArrow_obj

@[simp]
theorem fromStructuredArrow_map {X Y} (f : X ⟶ Y) :
    (fromStructuredArrow F).map f = ⟨f.right, congr_fun f.w.symm PUnit.unit⟩ :=
  rfl
#align category_theory.category_of_elements.from_structured_arrow_map
  CategoryTheory.CategoryOfElements.fromStructuredArrow_map

/-- The equivalence between the category of elements `F.Elements`
    and the comma category `(*, F)`. -/
@[simps! functor_obj functor_map inverse_obj inverse_map unitIso_hom
  unitIso_inv counitIso_hom counitIso_inv]
def structuredArrowEquivalence : F.Elements ≌ StructuredArrow PUnit F :=
  Equivalence.mk (toStructuredArrow F) (fromStructuredArrow F)
    (NatIso.ofComponents (fun X => eqToIso (by aesop_cat)) (by aesop_cat))
    (NatIso.ofComponents (fun X => StructuredArrow.isoMk (Iso.refl _)
    (by aesop_cat)) (by aesop_cat))
#align category_theory.category_of_elements.structured_arrow_equivalence
  CategoryTheory.CategoryOfElements.structuredArrowEquivalence

open Opposite

/-- The forward direction of the equivalence `F.Elementsᵒᵖ ≅ (yoneda, F)`,
given by `CategoryTheory.yonedaSections`.
-/
@[simps]
def toCostructuredArrow (F : Cᵒᵖ ⥤ Type v) : F.Elementsᵒᵖ ⥤ CostructuredArrow yoneda F
    where
  obj X := CostructuredArrow.mk ((yonedaSections (unop (unop X).fst) F).inv (ULift.up (unop X).2))
  map f := by
    fapply CostructuredArrow.homMk
    . exact f.unop.val.unop
    . ext Z
      funext y
      dsimp
      simp only [FunctorToTypes.map_comp_apply, ← f.unop.2]
#align category_theory.category_of_elements.to_costructured_arrow
  CategoryTheory.CategoryOfElements.toCostructuredArrow

/-- The reverse direction of the equivalence `F.Elementsᵒᵖ ≅ (yoneda, F)`,
given by `CategoryTheory.yonedaEquiv`.
-/
@[simps]
def fromCostructuredArrow (F : Cᵒᵖ ⥤ Type v) : (CostructuredArrow yoneda F)ᵒᵖ ⥤ F.Elements
    where
  obj X := ⟨op (unop X).1, yonedaEquiv.1 (unop X).3⟩
  map {X Y} f :=
    ⟨f.unop.1.op, by
      convert (congr_fun ((unop X).hom.naturality f.unop.left.op) (𝟙 _)).symm
      simp only [Equiv.toFun_as_coe, Quiver.Hom.unop_op, yonedaEquiv_apply, types_comp_apply,
        Category.comp_id, yoneda_obj_map]
      have : yoneda.map f.unop.left ≫ (unop X).hom = (unop Y).hom :=
        by
        convert f.unop.3
      erw [← this]
      simp only [yoneda_map_app, FunctorToTypes.comp]
      erw [Category.id_comp]⟩
#align category_theory.category_of_elements.from_costructured_arrow
  CategoryTheory.CategoryOfElements.fromCostructuredArrow

@[simp]
theorem fromCostructuredArrow_obj_mk (F : Cᵒᵖ ⥤ Type v) {X : C} (f : yoneda.obj X ⟶ F) :
    (fromCostructuredArrow F).obj (op (CostructuredArrow.mk f)) = ⟨op X, yonedaEquiv.1 f⟩ :=
  rfl
#align category_theory.category_of_elements.from_costructured_arrow_obj_mk
  CategoryTheory.CategoryOfElements.fromCostructuredArrow_obj_mk

/-- The unit of the equivalence `F.Elementsᵒᵖ ≅ (yoneda, F)` is indeed iso. -/
theorem from_toCostructuredArrow_eq (F : Cᵒᵖ ⥤ Type v) :
    (toCostructuredArrow F).rightOp ⋙ fromCostructuredArrow F = 𝟭 _ := by
  refine' Functor.ext _ _
  . intro X
    exact Functor.Elements.ext _ _ rfl (by simp [yonedaEquiv])
  . intro X Y f
    have : ∀ {a b : F.Elements} (H : a = b),
        (eqToHom H).1 = eqToHom (show a.fst = b.fst by cases H; rfl) := by
      rintro _ _ rfl
      simp
    ext
    simp [this]
#align category_theory.category_of_elements.from_to_costructured_arrow_eq
  CategoryTheory.CategoryOfElements.from_toCostructuredArrow_eq

/-- The counit of the equivalence `F.Elementsᵒᵖ ≅ (yoneda, F)` is indeed iso. -/
theorem to_fromCostructuredArrow_eq (F : Cᵒᵖ ⥤ Type v) :
    (fromCostructuredArrow F).rightOp ⋙ toCostructuredArrow F = 𝟭 _ := by
  refine' Functor.ext _ _
  · intro X
    cases' X with X_left X_right X_hom
    cases X_right
    simp only [Functor.id_obj, Functor.rightOp_obj, toCostructuredArrow_obj, Functor.comp_obj,
      CostructuredArrow.mk]
    congr
    ext x
    funext f
    convert congr_fun (X_hom.naturality f.op).symm (𝟙 X_left)
    simp
  . intro X Y f
    ext
    simp [CostructuredArrow.eqToHom_left]
#align category_theory.category_of_elements.to_from_costructured_arrow_eq
  CategoryTheory.CategoryOfElements.to_fromCostructuredArrow_eq

set_option maxHeartbeats 400000 in
/-- The equivalence `F.Elementsᵒᵖ ≅ (yoneda, F)` given by yoneda lemma. -/
@[simps! functor_obj functor_map inverse_obj inverse_map unitIso_hom
  unitIso_inv counitIso_hom counitIso_inv]
def costructuredArrowYonedaEquivalence (F : Cᵒᵖ ⥤ Type v) :
    F.Elementsᵒᵖ ≌ CostructuredArrow yoneda F :=
  Equivalence.mk (toCostructuredArrow F) (fromCostructuredArrow F).rightOp
    (NatIso.op (eqToIso (from_toCostructuredArrow_eq F))) (eqToIso <| to_fromCostructuredArrow_eq F)
#align category_theory.category_of_elements.costructured_arrow_yoneda_equivalence
  CategoryTheory.CategoryOfElements.costructuredArrowYonedaEquivalence

/-- The equivalence `(-.Elements)ᵒᵖ ≅ (yoneda, -)` of is actually a natural isomorphism of functors.
-/
theorem costructuredArrow_yoneda_equivalence_naturality {F₁ F₂ : Cᵒᵖ ⥤ Type v} (α : F₁ ⟶ F₂) :
    (map α).op ⋙ toCostructuredArrow F₂ = toCostructuredArrow F₁ ⋙ CostructuredArrow.map α := by
  fapply Functor.ext
  · intro X
    simp only [CostructuredArrow.map_mk, toCostructuredArrow_obj, Functor.op_obj,
      Functor.comp_obj]
    congr
    ext
    funext f
    simpa using congr_fun (α.naturality f.op).symm (unop X).snd
  · intro X Y f
    ext
    simp [CostructuredArrow.eqToHom_left]
#align category_theory.category_of_elements.costructured_arrow_yoneda_equivalence_naturality
  CategoryTheory.CategoryOfElements.costructuredArrow_yoneda_equivalence_naturality

end CategoryOfElements

end CategoryTheory


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Ken Lee, Chris Hughes
-/
import algebra.big_operators.basic
import data.fintype.basic
import data.int.gcd
import data.set.pairwise
import ring_theory.coprime.basic

/-!
# Additional lemmas about elements of a ring satisfying `is_coprime`

These lemmas are in a separate file to the definition of `is_coprime` as they require more imports.

Notably, this includes lemmas about `finset.prod` as this requires importing big_operators, and
lemmas about `has_pow` since these are easiest to prove via `finset.prod`.

-/

universes u v

variables {R : Type u} {I : Type v} [comm_semiring R] {x y z : R} {s : I → R} {t : finset I}

open_locale big_operators classical

theorem nat.is_coprime_iff_coprime {m n : ℕ} : is_coprime (m : ℤ) n ↔ nat.coprime m n :=
⟨λ ⟨a, b, H⟩, nat.eq_one_of_dvd_one $ int.coe_nat_dvd.1 $ by { rw [int.coe_nat_one, ← H],
  exact dvd_add (dvd_mul_of_dvd_right (int.coe_nat_dvd.2 $ nat.gcd_dvd_left m n) _)
    (dvd_mul_of_dvd_right (int.coe_nat_dvd.2 $ nat.gcd_dvd_right m n) _) },
λ H, ⟨nat.gcd_a m n, nat.gcd_b m n, by rw [mul_comm _ (m : ℤ), mul_comm _ (n : ℤ),
    ← nat.gcd_eq_gcd_ab, show _ = _, from H, int.coe_nat_one]⟩⟩

theorem is_coprime.prod_left : (∀ i ∈ t, is_coprime (s i) x) → is_coprime (∏ i in t, s i) x :=
finset.induction_on t (λ _, is_coprime_one_left) $ λ b t hbt ih H,
by { rw finset.prod_insert hbt, rw finset.forall_mem_insert at H, exact H.1.mul_left (ih H.2) }

theorem is_coprime.prod_right : (∀ i ∈ t, is_coprime x (s i)) → is_coprime x (∏ i in t, s i) :=
by simpa only [is_coprime_comm] using is_coprime.prod_left

theorem is_coprime.prod_left_iff : is_coprime (∏ i in t, s i) x ↔ ∀ i ∈ t, is_coprime (s i) x :=
finset.induction_on t (iff_of_true is_coprime_one_left $ λ _, false.elim) $ λ b t hbt ih,
by rw [finset.prod_insert hbt, is_coprime.mul_left_iff, ih, finset.forall_mem_insert]

theorem is_coprime.prod_right_iff : is_coprime x (∏ i in t, s i) ↔ ∀ i ∈ t, is_coprime x (s i) :=
by simpa only [is_coprime_comm] using is_coprime.prod_left_iff

theorem is_coprime.of_prod_left (H1 : is_coprime (∏ i in t, s i) x) (i : I) (hit : i ∈ t) :
  is_coprime (s i) x :=
is_coprime.prod_left_iff.1 H1 i hit

theorem is_coprime.of_prod_right (H1 : is_coprime x (∏ i in t, s i)) (i : I) (hit : i ∈ t) :
  is_coprime x (s i) :=
is_coprime.prod_right_iff.1 H1 i hit

theorem finset.prod_dvd_of_coprime :
  ∀ (Hs : set.pairwise (↑t : set I) (is_coprime on s)) (Hs1 : ∀ i ∈ t, s i ∣ z),
  ∏ x in t, s x ∣ z :=
finset.induction_on t (λ _ _, one_dvd z)
begin
  intros a r har ih Hs Hs1,
  rw finset.prod_insert har,
  have aux1 : a ∈ (↑(insert a r) : set I) := finset.mem_insert_self a r,
  refine (is_coprime.prod_right $ λ i hir, Hs a aux1 i _ (by { rintro rfl, exact har hir })).mul_dvd
    (Hs1 a aux1) (ih (Hs.mono _) $ λ i hi, Hs1 i (finset.mem_insert_of_mem hi)),
  { exact finset.mem_insert_of_mem hir },
  { simp only [finset.coe_insert, set.subset_insert] }
end

theorem fintype.prod_dvd_of_coprime [fintype I] (Hs : pairwise (is_coprime on s))
  (Hs1 : ∀ i, s i ∣ z) : ∏ x, s x ∣ z :=
finset.prod_dvd_of_coprime (Hs.set_pairwise _) (λ i _, Hs1 i)

variables {m n : ℕ}

theorem is_coprime.pow_left (H : is_coprime x y) : is_coprime (x ^ m) y :=
by { rw [← finset.card_range m, ← finset.prod_const], exact is_coprime.prod_left (λ _ _, H) }

theorem is_coprime.pow_right (H : is_coprime x y) : is_coprime x (y ^ n) :=
by { rw [← finset.card_range n, ← finset.prod_const], exact is_coprime.prod_right (λ _ _, H) }

theorem is_coprime.pow (H : is_coprime x y) : is_coprime (x ^ m) (y ^ n) :=
H.pow_left.pow_right

theorem is_coprime.pow_left_iff (hm : 0 < m) : is_coprime (x ^ m) y ↔ is_coprime x y :=
begin
  refine ⟨λ h, _, is_coprime.pow_left⟩,
  rw [← finset.card_range m, ← finset.prod_const] at h,
  exact h.of_prod_left 0 (finset.mem_range.mpr hm),
end

theorem is_coprime.pow_right_iff (hm : 0 < m) : is_coprime x (y ^ m) ↔ is_coprime x y :=
is_coprime_comm.trans $ (is_coprime.pow_left_iff hm).trans $ is_coprime_comm

theorem is_coprime.pow_iff (hm : 0 < m) (hn : 0 < n) :
  is_coprime (x ^ m) (y ^ n) ↔ is_coprime x y :=
(is_coprime.pow_left_iff hm).trans $ is_coprime.pow_right_iff hn


import data.set
open set

section
  variable U : Type
  variables A B C : set U

  example : ∀ x, x ∈ A ∩ C → x ∈ A ∪ B :=
  begin
    assume x,
    assume h,
    left, exact h.1,
  end

  example : ∀ x, x ∈ -(A ∪ B) → x ∈ -A :=
  begin
    assume x,
    assume h,
    assume xinA,
    have : x ∈ A ∪ B,
        left, assumption,
    contradiction
  end
end

section
    variable {U : Type}

    /- defining "disjoint" -/

    def disj (A B : set U) : Prop := ∀ ⦃x⦄, x ∈ A → x ∈ B → false

    example (A B : set U) (h : ∀ x, ¬ (x ∈ A ∧ x ∈ B)) :
    disj A B :=
    assume x,
    assume h1 : x ∈ A,
    assume h2 : x ∈ B,
    have h3 : x ∈ A ∧ x ∈ B, from and.intro h1 h2,
    show false, from h x h3

    -- notice that we do not have to mention x when applying
    --   h : disj A B
    example (A B : set U) (h1 : disj A B) (x : U)
        (h2 : x ∈ A) (h3 : x ∈ B) :
    false :=
    h1 h2 h3

    -- the same is true of ⊆
    example (A B : set U) (x : U) (h : A ⊆ B) (h1 : x ∈ A) :
    x ∈ B :=
    h h1

    example (A B C D : set U) (h1 : disj A B) (h2 : C ⊆ A)
        (h3 : D ⊆ B) :
    disj C D :=
    begin
        assume x,
        assume xinC xinD,
        have xinA : x ∈ A := h2 xinC,
        have xinB : x ∈ B := h3 xinD,
        exact h1 xinA xinB,
    end
end

section
    variables {I U : Type}
    variables {A B : I → set U}

    theorem Inter.intro {x : U} (h : ∀ i, x ∈ A i) : x ∈ ⋂ i, A i :=
    by simp; assumption

    @[elab_simple]
    theorem Inter.elim {x : U} (h : x ∈ ⋂ i, A i) (i : I) : x ∈ A i :=
    by simp at h; apply h

    theorem Union.intro {x : U} (i : I) (h : x ∈ A i) : x ∈ ⋃ i, A i :=
    by {simp, existsi i, exact h}

    theorem Union.elim {b : Prop} {x : U}
    (h₁ : x ∈ ⋃ i, A i) (h₂ : ∀ (i : I), x ∈ A i → b) : b :=
    by {simp at h₁, cases h₁ with i h, exact h₂ i h}

    -- BEGIN
    variables (C : set U)

    example : (⋂ i, A i) ∩ (⋂ i, B i) ⊆ (⋂ i, A i ∩ B i) :=
    begin
        assume x,
        assume h,
        -- actually, simp * at * here will do the job
        apply Inter.intro,
            assume i,
            split,
                show x ∈ A i, from Inter.elim h.1 i,
                show x ∈ B i, from Inter.elim h.2 i
    end

    example : C ∩ (⋃ i, A i) ⊆ ⋃ i, C ∩ A i :=
    begin
        assume x,
        assume h,
        -- actually, simp * at * here will do the job
        have xinC := h.1,
        apply Union.elim h.2,
            intros i xinAi,
            apply Union.intro i,
                exact ⟨xinC, xinAi⟩,
    end
    -- END
end

section
    -- BEGIN
    variable  {U : Type}
    variables A B C : set U

    -- For this exercise these two facts are useful
    example (h1 : A ⊆ B) (h2 : B ⊆ C) : A ⊆ C :=
    subset.trans h1 h2

    example : A ⊆ A :=
    subset.refl A

    example (h : A ⊆ B) : powerset A ⊆ powerset B :=
    begin
        assume s : set U,
        assume h₂ : s ∈ 𝒫 A, -- s is a subset of A, i.e. s ⊆ A, which is x ∈ s → x ∈ A
        assume x : U,
        assume xinS : x ∈ s,
        exact h (h₂ xinS),
    end

    example (h : powerset A ⊆ powerset B) : A ⊆ B :=
    begin
        have : A ∈ 𝒫 A,
            assume _ _, assumption,
        have : A ∈ 𝒫 B := h this,
        assumption, -- note : A ∈ 𝒫 B is equivalent to A ⊆ B
    end

    example : 𝒫 (A ∩ B) = 𝒫 A ∩ 𝒫 B :=
    begin
        apply set.ext,
            assume x,
            split,
                assume h,
                split,
                    assume a,
                    assume h1,
                    have : a ∈ A ∩ B := h h1,
                    exact this.1,

                    assume a,
                    assume h1,
                    have : a ∈ A ∩ B := h h1,
                    exact this.2,

                assume h,
                cases h with xinPa xinPb,
                    assume a,
                    assume h1 : a ∈ x,
                    split,
                        exact xinPa h1,
                        exact xinPb h1,
    end
end


/-
Copyright (c) 2022 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import algebra.lie.abelian
import algebra.lie.ideal_operations
import algebra.lie.quotient

/-!
# The normalizer of a Lie submodules and subalgebras.

Given a Lie module `M` over a Lie subalgebra `L`, the normalizer of a Lie submodule `N ⊆ M` is
the Lie submodule with underlying set `{ m | ∀ (x : L), ⁅x, m⁆ ∈ N }`.

The lattice of Lie submodules thus has two natural operations, the normalizer: `N ↦ N.normalizer`
and the ideal operation: `N ↦ ⁅⊤, N⁆`; these are adjoint, i.e., they form a Galois connection. This
adjointness is the reason that we may define nilpotency in terms of either the upper or lower
central series.

Given a Lie subalgebra `H ⊆ L`, we may regard `H` as a Lie submodule of `L` over `H`, and thus
consider the normalizer. This turns out to be a Lie subalgebra.

## Main definitions

  * `lie_submodule.normalizer`
  * `lie_subalgebra.normalizer`
  * `lie_submodule.gc_top_lie_normalizer`

## Tags

lie algebra, normalizer
-/

variables {R L M M' : Type*}
variables [comm_ring R] [lie_ring L] [lie_algebra R L]
variables [add_comm_group M] [module R M] [lie_ring_module L M] [lie_module R L M]
variables [add_comm_group M'] [module R M'] [lie_ring_module L M'] [lie_module R L M']

namespace lie_submodule

variables (N : lie_submodule R L M) {N₁ N₂ : lie_submodule R L M}

/-- The normalizer of a Lie submodule. -/
def normalizer : lie_submodule R L M :=
{ carrier   := { m | ∀ (x : L), ⁅x, m⁆ ∈ N },
  add_mem'  := λ m₁ m₂ hm₁ hm₂ x, by {  rw lie_add, exact N.add_mem' (hm₁ x) (hm₂ x), },
  zero_mem' := λ x, by simp,
  smul_mem' := λ t m hm x, by { rw lie_smul, exact N.smul_mem' t (hm x), },
  lie_mem   := λ x m hm y, by { rw leibniz_lie, exact N.add_mem' (hm ⁅y, x⁆) (N.lie_mem (hm y)), } }

@[simp] lemma mem_normalizer (m : M) :
  m ∈ N.normalizer ↔ ∀ (x : L), ⁅x, m⁆ ∈ N :=
iff.rfl

lemma normalizer_inf :
  (N₁ ⊓ N₂).normalizer = N₁.normalizer ⊓ N₂.normalizer :=
by { ext, simp [← forall_and_distrib], }

@[mono] lemma monotone_normalizer :
  monotone (normalizer : lie_submodule R L M → lie_submodule R L M) :=
begin
  intros N₁ N₂ h m hm,
  rw mem_normalizer at hm ⊢,
  exact λ x, h (hm x),
end

@[simp] lemma comap_normalizer (f : M' →ₗ⁅R,L⁆ M) :
  N.normalizer.comap f = (N.comap f).normalizer :=
by { ext, simp, }

lemma top_lie_le_iff_le_normalizer (N' : lie_submodule R L M) :
  ⁅(⊤ : lie_ideal R L), N⁆ ≤ N' ↔ N ≤ N'.normalizer :=
by { rw lie_le_iff, tauto, }

lemma gc_top_lie_normalizer :
  galois_connection (λ N : lie_submodule R L M, ⁅(⊤ : lie_ideal R L), N⁆) normalizer :=
top_lie_le_iff_le_normalizer

variables (R L M)

lemma normalizer_bot_eq_max_triv_submodule :
  (⊥ : lie_submodule R L M).normalizer = lie_module.max_triv_submodule R L M :=
rfl

end lie_submodule

namespace lie_subalgebra

variables (H : lie_subalgebra R L)

/-- Regarding a Lie subalgebra `H ⊆ L` as a module over itself, its normalizer is in fact a Lie
subalgebra. -/
def normalizer : lie_subalgebra R L :=
{ lie_mem' := λ y z hy hz x,
  begin
    rw [coe_bracket_of_module, mem_to_lie_submodule, leibniz_lie, ← lie_skew y, ← sub_eq_add_neg],
    exact H.sub_mem (hz ⟨_, hy x⟩) (hy ⟨_, hz x⟩),
  end,
  .. H.to_lie_submodule.normalizer }

lemma mem_normalizer_iff' (x : L) : x ∈ H.normalizer ↔ ∀ (y : L), (y ∈ H) → ⁅y, x⁆ ∈ H :=
by { rw subtype.forall', refl, }

lemma mem_normalizer_iff (x : L) : x ∈ H.normalizer ↔ ∀ (y : L), (y ∈ H) → ⁅x, y⁆ ∈ H :=
begin
  rw mem_normalizer_iff',
  refine forall₂_congr (λ y hy, _),
  rw [← lie_skew, neg_mem_iff],
end

lemma le_normalizer : H ≤ H.normalizer := H.to_lie_submodule.le_normalizer

lemma coe_normalizer_eq_normalizer :
  (H.to_lie_submodule.normalizer : submodule R L) = H.normalizer :=
rfl

variables {H}

lemma lie_mem_sup_of_mem_normalizer {x y z : L} (hx : x ∈ H.normalizer)
  (hy : y ∈ (R ∙ x) ⊔ ↑H) (hz : z ∈ (R ∙ x) ⊔ ↑H) : ⁅y, z⁆ ∈ (R ∙ x) ⊔ ↑H :=
begin
  rw submodule.mem_sup at hy hz,
  obtain ⟨u₁, hu₁, v, hv : v ∈ H, rfl⟩ := hy,
  obtain ⟨u₂, hu₂, w, hw : w ∈ H, rfl⟩ := hz,
  obtain ⟨t, rfl⟩ := submodule.mem_span_singleton.mp hu₁,
  obtain ⟨s, rfl⟩ := submodule.mem_span_singleton.mp hu₂,
  apply submodule.mem_sup_right,
  simp only [lie_subalgebra.mem_coe_submodule, smul_lie, add_lie, zero_add, lie_add, smul_zero,
    lie_smul, lie_self],
  refine H.add_mem (H.smul_mem s _) (H.add_mem (H.smul_mem t _) (H.lie_mem hv hw)),
  exacts [(H.mem_normalizer_iff' x).mp hx v hv, (H.mem_normalizer_iff x).mp hx w hw],
end

/-- A Lie subalgebra is an ideal of its normalizer. -/
lemma ideal_in_normalizer {x y : L} (hx : x ∈ H.normalizer) (hy : y ∈ H) : ⁅x,y⁆ ∈ H :=
begin
  rw [← lie_skew, neg_mem_iff],
  exact hx ⟨y, hy⟩,
end

/-- A Lie subalgebra `H` is an ideal of any Lie subalgebra `K` containing `H` and contained in the
normalizer of `H`. -/
lemma exists_nested_lie_ideal_of_le_normalizer
  {K : lie_subalgebra R L} (h₁ : H ≤ K) (h₂ : K ≤ H.normalizer) :
  ∃ (I : lie_ideal R K), (I : lie_subalgebra R K) = of_le h₁ :=
begin
  rw exists_nested_lie_ideal_coe_eq_iff,
  exact λ x y hx hy, ideal_in_normalizer (h₂ hx) hy,
end

variables (H)

lemma normalizer_eq_self_iff :
  H.normalizer = H ↔ (lie_module.max_triv_submodule R H $ L ⧸ H.to_lie_submodule) = ⊥ :=
begin
  rw lie_submodule.eq_bot_iff,
  refine ⟨λ h, _, λ h, le_antisymm (λ x hx, _) H.le_normalizer⟩,
  { rintros ⟨x⟩ hx,
    suffices : x ∈ H, by simpa,
    rw [← h, H.mem_normalizer_iff'],
    intros y hy,
    replace hx : ⁅_, lie_submodule.quotient.mk' _ x⁆ = 0 := hx ⟨y, hy⟩,
    rwa [← lie_module_hom.map_lie, lie_submodule.quotient.mk_eq_zero] at hx, },
  { let y := lie_submodule.quotient.mk' H.to_lie_submodule x,
    have hy : y ∈ lie_module.max_triv_submodule R H (L ⧸ H.to_lie_submodule),
    { rintros ⟨z, hz⟩,
      rw [← lie_module_hom.map_lie, lie_submodule.quotient.mk_eq_zero, coe_bracket_of_module,
        submodule.coe_mk, mem_to_lie_submodule],
      exact (H.mem_normalizer_iff' x).mp hx z hz, },
    simpa using h y hy, },
end

end lie_subalgebra


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

/-!
# Metric separated pairs of sets

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

In this file we define the predicate `is_metric_separated`. We say that two sets in an (extended)
metric space are *metric separated* if the (extended) distance between `x ∈ s` and `y ∈ t` is
bounded from below by a positive constant.

This notion is useful, e.g., to define metric outer measures.
-/

open emetric set
noncomputable theory

/-- Two sets in an (extended) metric space are called *metric separated* if the (extended) distance
between `x ∈ s` and `y ∈ t` is bounded from below by a positive constant. -/
def is_metric_separated {X : Type*} [emetric_space X] (s t : set X) :=
∃ r ≠ 0, ∀ (x ∈ s) (y ∈ t), r ≤ edist x y

namespace is_metric_separated

variables {X : Type*} [emetric_space X] {s t : set X} {x y : X}

@[symm] lemma symm (h : is_metric_separated s t) : is_metric_separated t s :=
let ⟨r, r0, hr⟩ := h in ⟨r, r0, λ y hy x hx, edist_comm x y ▸ hr x hx y hy⟩

lemma comm : is_metric_separated s t ↔ is_metric_separated t s := ⟨symm, symm⟩

@[simp] lemma empty_left (s : set X) : is_metric_separated ∅ s :=
⟨1, one_ne_zero, λ x, false.elim⟩

@[simp] lemma empty_right (s : set X) : is_metric_separated s ∅ :=
(empty_left s).symm

protected lemma disjoint (h : is_metric_separated s t) : disjoint s t :=
let ⟨r, r0, hr⟩ := h in
set.disjoint_left.mpr $ λ x hx1 hx2, r0 $ by simpa using hr x hx1 x hx2

lemma subset_compl_right (h : is_metric_separated s t) : s ⊆ tᶜ :=
λ x hs ht, h.disjoint.le_bot ⟨hs, ht⟩

@[mono] lemma mono {s' t'} (hs : s ⊆ s') (ht : t ⊆ t') :
  is_metric_separated s' t' → is_metric_separated s t :=
λ ⟨r, r0, hr⟩, ⟨r, r0, λ x hx y hy, hr x (hs hx) y (ht hy)⟩

lemma mono_left {s'} (h' : is_metric_separated s' t) (hs : s ⊆ s') :
  is_metric_separated s t :=
h'.mono hs subset.rfl

lemma mono_right {t'} (h' : is_metric_separated s t') (ht : t ⊆ t') :
  is_metric_separated s t :=
h'.mono subset.rfl ht

lemma union_left {s'} (h : is_metric_separated s t) (h' : is_metric_separated s' t) :
  is_metric_separated (s ∪ s') t :=
begin
  rcases ⟨h, h'⟩ with ⟨⟨r, r0, hr⟩, ⟨r', r0', hr'⟩⟩,
  refine ⟨min r r', _, λ x hx y hy, hx.elim _ _⟩,
  { rw [← pos_iff_ne_zero] at r0 r0' ⊢,
    exact lt_min r0 r0' },
  { exact λ hx, (min_le_left _ _).trans (hr _ hx _ hy) },
  { exact λ hx, (min_le_right _ _).trans (hr' _ hx _ hy) }
end

@[simp] lemma union_left_iff {s'} :
  is_metric_separated (s ∪ s') t ↔ is_metric_separated s t ∧ is_metric_separated s' t :=
⟨λ h, ⟨h.mono_left (subset_union_left _ _), h.mono_left (subset_union_right _ _)⟩,
  λ h, h.1.union_left h.2⟩

lemma union_right {t'} (h : is_metric_separated s t) (h' : is_metric_separated s t') :
  is_metric_separated s (t ∪ t') :=
(h.symm.union_left h'.symm).symm

@[simp] lemma union_right_iff {t'} :
  is_metric_separated s (t ∪ t') ↔ is_metric_separated s t ∧ is_metric_separated s t' :=
comm.trans $ union_left_iff.trans $ and_congr comm comm

lemma finite_Union_left_iff {ι : Type*} {I : set ι} (hI : I.finite) {s : ι → set X} {t : set X} :
  is_metric_separated (⋃ i ∈ I, s i) t ↔ ∀ i ∈ I, is_metric_separated (s i) t :=
begin
  refine finite.induction_on hI (by simp) (λ i I hi _ hI, _),
  rw [bUnion_insert, ball_insert_iff, union_left_iff, hI]
end

alias finite_Union_left_iff ↔ _ finite_Union_left

lemma finite_Union_right_iff {ι : Type*} {I : set ι} (hI : I.finite) {s : set X} {t : ι → set X} :
  is_metric_separated s (⋃ i ∈ I, t i) ↔ ∀ i ∈ I, is_metric_separated s (t i) :=
by simpa only [@comm _ _ s] using finite_Union_left_iff hI

@[simp] lemma finset_Union_left_iff {ι : Type*} {I : finset ι} {s : ι → set X} {t : set X} :
  is_metric_separated (⋃ i ∈ I, s i) t ↔ ∀ i ∈ I, is_metric_separated (s i) t :=
finite_Union_left_iff I.finite_to_set

alias finset_Union_left_iff ↔ _ finset_Union_left

@[simp] lemma finset_Union_right_iff {ι : Type*} {I : finset ι} {s : set X} {t : ι → set X} :
  is_metric_separated s (⋃ i ∈ I, t i) ↔ ∀ i ∈ I, is_metric_separated s (t i) :=
finite_Union_right_iff I.finite_to_set

alias finset_Union_right_iff ↔ _ finset_Union_right

end is_metric_separated