/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Frédéric Dupuis, Heather Macbeth

! This file was ported from Lean 3 source module analysis.normed_space.linear_isometry
! leanprover-community/mathlib commit 4601791ea62fea875b488dafc4e6dede19e8363f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Normed.Group.Basic
import Mathbin.Topology.Algebra.Module.Basic
import Mathbin.LinearAlgebra.Basis

/-!
# (Semi-)linear isometries

In this file we define `linear_isometry σ₁₂ E E₂` (notation: `E →ₛₗᵢ[σ₁₂] E₂`) to be a semilinear
isometric embedding of `E` into `E₂` and `linear_isometry_equiv` (notation: `E ≃ₛₗᵢ[σ₁₂] E₂`) to be
a semilinear isometric equivalence between `E` and `E₂`.  The notation for the associated purely
linear concepts is `E →ₗᵢ[R] E₂`, `E ≃ₗᵢ[R] E₂`, and `E →ₗᵢ⋆[R] E₂`, `E ≃ₗᵢ⋆[R] E₂` for
the star-linear versions.

We also prove some trivial lemmas and provide convenience constructors.

Since a lot of elementary properties don't require `‖x‖ = 0 → x = 0` we start setting up the
theory for `seminormed_add_comm_group` and we specialize to `normed_add_comm_group` when needed.
-/

open Function Set

variable {R R₂ R₃ R₄ E E₂ E₃ E₄ F 𝓕 : Type _} [Semiring R] [Semiring R₂] [Semiring R₃] [Semiring R₄]
  {σ₁₂ : R →+* R₂} {σ₂₁ : R₂ →+* R} {σ₁₃ : R →+* R₃} {σ₃₁ : R₃ →+* R} {σ₁₄ : R →+* R₄}
  {σ₄₁ : R₄ →+* R} {σ₂₃ : R₂ →+* R₃} {σ₃₂ : R₃ →+* R₂} {σ₂₄ : R₂ →+* R₄} {σ₄₂ : R₄ →+* R₂}
  {σ₃₄ : R₃ →+* R₄} {σ₄₃ : R₄ →+* R₃} [RingHomInvPair σ₁₂ σ₂₁] [RingHomInvPair σ₂₁ σ₁₂]
  [RingHomInvPair σ₁₃ σ₃₁] [RingHomInvPair σ₃₁ σ₁₃] [RingHomInvPair σ₂₃ σ₃₂]
  [RingHomInvPair σ₃₂ σ₂₃] [RingHomInvPair σ₁₄ σ₄₁] [RingHomInvPair σ₄₁ σ₁₄]
  [RingHomInvPair σ₂₄ σ₄₂] [RingHomInvPair σ₄₂ σ₂₄] [RingHomInvPair σ₃₄ σ₄₃]
  [RingHomInvPair σ₄₃ σ₃₄] [RingHomCompTriple σ₁₂ σ₂₃ σ₁₃] [RingHomCompTriple σ₁₂ σ₂₄ σ₁₄]
  [RingHomCompTriple σ₂₃ σ₃₄ σ₂₄] [RingHomCompTriple σ₁₃ σ₃₄ σ₁₄] [RingHomCompTriple σ₃₂ σ₂₁ σ₃₁]
  [RingHomCompTriple σ₄₂ σ₂₁ σ₄₁] [RingHomCompTriple σ₄₃ σ₃₂ σ₄₂] [RingHomCompTriple σ₄₃ σ₃₁ σ₄₁]
  [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂] [SeminormedAddCommGroup E₃]
  [SeminormedAddCommGroup E₄] [Module R E] [Module R₂ E₂] [Module R₃ E₃] [Module R₄ E₄]
  [NormedAddCommGroup F] [Module R F]

/-- A `σ₁₂`-semilinear isometric embedding of a normed `R`-module into an `R₂`-module. -/
structure LinearIsometry (σ₁₂ : R →+* R₂) (E E₂ : Type _) [SeminormedAddCommGroup E]
  [SeminormedAddCommGroup E₂] [Module R E] [Module R₂ E₂] extends E →ₛₗ[σ₁₂] E₂ where
  norm_map' : ∀ x, ‖to_linear_map x‖ = ‖x‖
#align linear_isometry LinearIsometry

-- mathport name: «expr →ₛₗᵢ[ ] »
notation:25 E " →ₛₗᵢ[" σ₁₂:25 "] " E₂:0 => LinearIsometry σ₁₂ E E₂

-- mathport name: «expr →ₗᵢ[ ] »
notation:25 E " →ₗᵢ[" R:25 "] " E₂:0 => LinearIsometry (RingHom.id R) E E₂

-- mathport name: «expr →ₗᵢ⋆[ ] »
notation:25 E " →ₗᵢ⋆[" R:25 "] " E₂:0 => LinearIsometry (starRingEnd R) E E₂

/-- `semilinear_isometry_class F σ E E₂` asserts `F` is a type of bundled `σ`-semilinear isometries
`E → E₂`.

See also `linear_isometry_class F R E E₂` for the case where `σ` is the identity map on `R`.

A map `f` between an `R`-module and an `S`-module over a ring homomorphism `σ : R →+* S`
is semilinear if it satisfies the two properties `f (x + y) = f x + f y` and
`f (c • x) = (σ c) • f x`. -/
class SemilinearIsometryClass (𝓕 : Type _) {R R₂ : outParam (Type _)} [Semiring R] [Semiring R₂]
  (σ₁₂ : outParam <| R →+* R₂) (E E₂ : outParam (Type _)) [SeminormedAddCommGroup E]
  [SeminormedAddCommGroup E₂] [Module R E] [Module R₂ E₂] extends
  SemilinearMapClass 𝓕 σ₁₂ E E₂ where
  norm_map : ∀ (f : 𝓕) (x : E), ‖f x‖ = ‖x‖
#align semilinear_isometry_class SemilinearIsometryClass

/-- `linear_isometry_class F R E E₂` asserts `F` is a type of bundled `R`-linear isometries
`M → M₂`.

This is an abbreviation for `semilinear_isometry_class F (ring_hom.id R) E E₂`.
-/
abbrev LinearIsometryClass (𝓕 : Type _) (R E E₂ : outParam (Type _)) [Semiring R]
    [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂] [Module R E] [Module R E₂] :=
  SemilinearIsometryClass 𝓕 (RingHom.id R) E E₂
#align linear_isometry_class LinearIsometryClass

namespace SemilinearIsometryClass

protected theorem isometry [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) : Isometry f :=
  AddMonoidHomClass.isometry_of_norm _ (norm_map _)
#align semilinear_isometry_class.isometry SemilinearIsometryClass.isometry

@[continuity]
protected theorem continuous [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) : Continuous f :=
  (SemilinearIsometryClass.isometry f).Continuous
#align semilinear_isometry_class.continuous SemilinearIsometryClass.continuous

@[simp]
theorem nnnorm_map [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) (x : E) : ‖f x‖₊ = ‖x‖₊ :=
  NNReal.eq <| norm_map f x
#align semilinear_isometry_class.nnnorm_map SemilinearIsometryClass.nnnorm_map

protected theorem lipschitz [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) : LipschitzWith 1 f :=
  (SemilinearIsometryClass.isometry f).lipschitz
#align semilinear_isometry_class.lipschitz SemilinearIsometryClass.lipschitz

protected theorem antilipschitz [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) :
    AntilipschitzWith 1 f :=
  (SemilinearIsometryClass.isometry f).antilipschitz
#align semilinear_isometry_class.antilipschitz SemilinearIsometryClass.antilipschitz

theorem ediam_image [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) (s : Set E) :
    EMetric.diam (f '' s) = EMetric.diam s :=
  (SemilinearIsometryClass.isometry f).ediam_image s
#align semilinear_isometry_class.ediam_image SemilinearIsometryClass.ediam_image

theorem ediam_range [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) :
    EMetric.diam (range f) = EMetric.diam (univ : Set E) :=
  (SemilinearIsometryClass.isometry f).ediam_range
#align semilinear_isometry_class.ediam_range SemilinearIsometryClass.ediam_range

theorem diam_image [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) (s : Set E) :
    Metric.diam (f '' s) = Metric.diam s :=
  (SemilinearIsometryClass.isometry f).diam_image s
#align semilinear_isometry_class.diam_image SemilinearIsometryClass.diam_image

theorem diam_range [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) :
    Metric.diam (range f) = Metric.diam (univ : Set E) :=
  (SemilinearIsometryClass.isometry f).diam_range
#align semilinear_isometry_class.diam_range SemilinearIsometryClass.diam_range

instance (priority := 100) [s : SemilinearIsometryClass 𝓕 σ₁₂ E E₂] :
    ContinuousSemilinearMapClass 𝓕 σ₁₂ E E₂ :=
  { s with map_continuous := SemilinearIsometryClass.continuous }

end SemilinearIsometryClass

namespace LinearIsometry

variable (f : E →ₛₗᵢ[σ₁₂] E₂) (f₁ : F →ₛₗᵢ[σ₁₂] E₂)

theorem toLinearMap_injective : Injective (toLinearMap : (E →ₛₗᵢ[σ₁₂] E₂) → E →ₛₗ[σ₁₂] E₂)
  | ⟨f, _⟩, ⟨g, _⟩, rfl => rfl
#align linear_isometry.to_linear_map_injective LinearIsometry.toLinearMap_injective

@[simp]
theorem toLinearMap_inj {f g : E →ₛₗᵢ[σ₁₂] E₂} : f.toLinearMap = g.toLinearMap ↔ f = g :=
  toLinearMap_injective.eq_iff
#align linear_isometry.to_linear_map_inj LinearIsometry.toLinearMap_inj

instance : SemilinearIsometryClass (E →ₛₗᵢ[σ₁₂] E₂) σ₁₂ E E₂
    where
  coe f := f.toFun
  coe_injective' f g h := toLinearMap_injective (FunLike.coe_injective h)
  map_add f := map_add f.toLinearMap
  map_smulₛₗ f := map_smulₛₗ f.toLinearMap
  norm_map f := f.norm_map'

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly.
-/
instance : CoeFun (E →ₛₗᵢ[σ₁₂] E₂) fun _ => E → E₂ :=
  ⟨fun f => f.toFun⟩

@[simp]
theorem coe_toLinearMap : ⇑f.toLinearMap = f :=
  rfl
#align linear_isometry.coe_to_linear_map LinearIsometry.coe_toLinearMap

@[simp]
theorem coe_mk (f : E →ₛₗ[σ₁₂] E₂) (hf) : ⇑(mk f hf) = f :=
  rfl
#align linear_isometry.coe_mk LinearIsometry.coe_mk

theorem coe_injective : @Injective (E →ₛₗᵢ[σ₁₂] E₂) (E → E₂) coeFn :=
  FunLike.coe_injective
#align linear_isometry.coe_injective LinearIsometry.coe_injective

/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
  because it is a composition of multiple projections. -/
def Simps.apply (σ₁₂ : R →+* R₂) (E E₂ : Type _) [SeminormedAddCommGroup E]
    [SeminormedAddCommGroup E₂] [Module R E] [Module R₂ E₂] (h : E →ₛₗᵢ[σ₁₂] E₂) : E → E₂ :=
  h
#align linear_isometry.simps.apply LinearIsometry.Simps.apply

initialize_simps_projections LinearIsometry (to_linear_map_to_fun → apply)

@[ext]
theorem ext {f g : E →ₛₗᵢ[σ₁₂] E₂} (h : ∀ x, f x = g x) : f = g :=
  coe_injective <| funext h
#align linear_isometry.ext LinearIsometry.ext

protected theorem congr_arg [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] {f : 𝓕} :
    ∀ {x x' : E}, x = x' → f x = f x'
  | _, _, rfl => rfl
#align linear_isometry.congr_arg LinearIsometry.congr_arg

protected theorem congr_fun [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] {f g : 𝓕} (h : f = g) (x : E) :
    f x = g x :=
  h ▸ rfl
#align linear_isometry.congr_fun LinearIsometry.congr_fun

@[simp]
protected theorem map_zero : f 0 = 0 :=
  f.toLinearMap.map_zero
#align linear_isometry.map_zero LinearIsometry.map_zero

@[simp]
protected theorem map_add (x y : E) : f (x + y) = f x + f y :=
  f.toLinearMap.map_add x y
#align linear_isometry.map_add LinearIsometry.map_add

@[simp]
protected theorem map_neg (x : E) : f (-x) = -f x :=
  f.toLinearMap.map_neg x
#align linear_isometry.map_neg LinearIsometry.map_neg

@[simp]
protected theorem map_sub (x y : E) : f (x - y) = f x - f y :=
  f.toLinearMap.map_sub x y
#align linear_isometry.map_sub LinearIsometry.map_sub

@[simp]
protected theorem map_smulₛₗ (c : R) (x : E) : f (c • x) = σ₁₂ c • f x :=
  f.toLinearMap.map_smulₛₗ c x
#align linear_isometry.map_smulₛₗ LinearIsometry.map_smulₛₗ

@[simp]
protected theorem map_smul [Module R E₂] (f : E →ₗᵢ[R] E₂) (c : R) (x : E) : f (c • x) = c • f x :=
  f.toLinearMap.map_smul c x
#align linear_isometry.map_smul LinearIsometry.map_smul

@[simp]
theorem norm_map (x : E) : ‖f x‖ = ‖x‖ :=
  SemilinearIsometryClass.norm_map f x
#align linear_isometry.norm_map LinearIsometry.norm_map

@[simp]
theorem nnnorm_map (x : E) : ‖f x‖₊ = ‖x‖₊ :=
  NNReal.eq <| norm_map f x
#align linear_isometry.nnnorm_map LinearIsometry.nnnorm_map

protected theorem isometry : Isometry f :=
  AddMonoidHomClass.isometry_of_norm _ (norm_map _)
#align linear_isometry.isometry LinearIsometry.isometry

@[simp]
theorem isComplete_image_iff [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) {s : Set E} :
    IsComplete (f '' s) ↔ IsComplete s :=
  isComplete_image_iff (SemilinearIsometryClass.isometry f).UniformInducing
#align linear_isometry.is_complete_image_iff LinearIsometry.isComplete_image_iff

theorem isComplete_map_iff [RingHomSurjective σ₁₂] {p : Submodule R E} :
    IsComplete (p.map f.toLinearMap : Set E₂) ↔ IsComplete (p : Set E) :=
  f.isComplete_image_iff
#align linear_isometry.is_complete_map_iff LinearIsometry.isComplete_map_iff

theorem isComplete_map_iff' [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) [RingHomSurjective σ₁₂]
    {p : Submodule R E} : IsComplete (p.map f : Set E₂) ↔ IsComplete (p : Set E) :=
  isComplete_image_iff f
#align linear_isometry.is_complete_map_iff' LinearIsometry.isComplete_map_iff'

instance completeSpace_map [SemilinearIsometryClass 𝓕 σ₁₂ E E₂] (f : 𝓕) [RingHomSurjective σ₁₂]
    (p : Submodule R E) [CompleteSpace p] : CompleteSpace (p.map f) :=
  ((isComplete_map_iff' f).2 <| completeSpace_coe_iff_isComplete.1 ‹_›).completeSpace_coe
#align linear_isometry.complete_space_map LinearIsometry.completeSpace_map

instance completeSpace_map' [RingHomSurjective σ₁₂] (p : Submodule R E) [CompleteSpace p] :
    CompleteSpace (p.map f.toLinearMap) :=
  (f.isComplete_map_iff.2 <| completeSpace_coe_iff_isComplete.1 ‹_›).completeSpace_coe
#align linear_isometry.complete_space_map' LinearIsometry.completeSpace_map'

@[simp]
theorem dist_map (x y : E) : dist (f x) (f y) = dist x y :=
  f.Isometry.dist_eq x y
#align linear_isometry.dist_map LinearIsometry.dist_map

@[simp]
theorem edist_map (x y : E) : edist (f x) (f y) = edist x y :=
  f.Isometry.edist_eq x y
#align linear_isometry.edist_map LinearIsometry.edist_map

protected theorem injective : Injective f₁ :=
  Isometry.injective (LinearIsometry.isometry f₁)
#align linear_isometry.injective LinearIsometry.injective

@[simp]
theorem map_eq_iff {x y : F} : f₁ x = f₁ y ↔ x = y :=
  f₁.Injective.eq_iff
#align linear_isometry.map_eq_iff LinearIsometry.map_eq_iff

theorem map_ne {x y : F} (h : x ≠ y) : f₁ x ≠ f₁ y :=
  f₁.Injective.Ne h
#align linear_isometry.map_ne LinearIsometry.map_ne

protected theorem lipschitz : LipschitzWith 1 f :=
  f.Isometry.lipschitz
#align linear_isometry.lipschitz LinearIsometry.lipschitz

protected theorem antilipschitz : AntilipschitzWith 1 f :=
  f.Isometry.antilipschitz
#align linear_isometry.antilipschitz LinearIsometry.antilipschitz

@[continuity]
protected theorem continuous : Continuous f :=
  f.Isometry.Continuous
#align linear_isometry.continuous LinearIsometry.continuous

@[simp]
theorem preimage_ball (x : E) (r : ℝ) : f ⁻¹' Metric.ball (f x) r = Metric.ball x r :=
  f.Isometry.preimage_ball x r
#align linear_isometry.preimage_ball LinearIsometry.preimage_ball

@[simp]
theorem preimage_sphere (x : E) (r : ℝ) : f ⁻¹' Metric.sphere (f x) r = Metric.sphere x r :=
  f.Isometry.preimage_sphere x r
#align linear_isometry.preimage_sphere LinearIsometry.preimage_sphere

@[simp]
theorem preimage_closedBall (x : E) (r : ℝ) :
    f ⁻¹' Metric.closedBall (f x) r = Metric.closedBall x r :=
  f.Isometry.preimage_closedBall x r
#align linear_isometry.preimage_closed_ball LinearIsometry.preimage_closedBall

theorem ediam_image (s : Set E) : EMetric.diam (f '' s) = EMetric.diam s :=
  f.Isometry.ediam_image s
#align linear_isometry.ediam_image LinearIsometry.ediam_image

theorem ediam_range : EMetric.diam (range f) = EMetric.diam (univ : Set E) :=
  f.Isometry.ediam_range
#align linear_isometry.ediam_range LinearIsometry.ediam_range

theorem diam_image (s : Set E) : Metric.diam (f '' s) = Metric.diam s :=
  Isometry.diam_image (LinearIsometry.isometry f) s
#align linear_isometry.diam_image LinearIsometry.diam_image

theorem diam_range : Metric.diam (range f) = Metric.diam (univ : Set E) :=
  Isometry.diam_range (LinearIsometry.isometry f)
#align linear_isometry.diam_range LinearIsometry.diam_range

/-- Interpret a linear isometry as a continuous linear map. -/
def toContinuousLinearMap : E →SL[σ₁₂] E₂ :=
  ⟨f.toLinearMap, f.Continuous⟩
#align linear_isometry.to_continuous_linear_map LinearIsometry.toContinuousLinearMap

theorem toContinuousLinearMap_injective :
    Function.Injective (toContinuousLinearMap : _ → E →SL[σ₁₂] E₂) := fun x y h =>
  coe_injective (congr_arg _ h : ⇑x.toContinuousLinearMap = _)
#align linear_isometry.to_continuous_linear_map_injective LinearIsometry.toContinuousLinearMap_injective

@[simp]
theorem toContinuousLinearMap_inj {f g : E →ₛₗᵢ[σ₁₂] E₂} :
    f.toContinuousLinearMap = g.toContinuousLinearMap ↔ f = g :=
  toContinuousLinearMap_injective.eq_iff
#align linear_isometry.to_continuous_linear_map_inj LinearIsometry.toContinuousLinearMap_inj

@[simp]
theorem coe_toContinuousLinearMap : ⇑f.toContinuousLinearMap = f :=
  rfl
#align linear_isometry.coe_to_continuous_linear_map LinearIsometry.coe_toContinuousLinearMap

@[simp]
theorem comp_continuous_iff {α : Type _} [TopologicalSpace α] {g : α → E} :
    Continuous (f ∘ g) ↔ Continuous g :=
  f.Isometry.comp_continuous_iff
#align linear_isometry.comp_continuous_iff LinearIsometry.comp_continuous_iff

/-- The identity linear isometry. -/
def id : E →ₗᵢ[R] E :=
  ⟨LinearMap.id, fun x => rfl⟩
#align linear_isometry.id LinearIsometry.id

@[simp]
theorem coe_id : ((id : E →ₗᵢ[R] E) : E → E) = id :=
  rfl
#align linear_isometry.coe_id LinearIsometry.coe_id

@[simp]
theorem id_apply (x : E) : (id : E →ₗᵢ[R] E) x = x :=
  rfl
#align linear_isometry.id_apply LinearIsometry.id_apply

@[simp]
theorem id_toLinearMap : (id.toLinearMap : E →ₗ[R] E) = LinearMap.id :=
  rfl
#align linear_isometry.id_to_linear_map LinearIsometry.id_toLinearMap

@[simp]
theorem id_toContinuousLinearMap : id.toContinuousLinearMap = ContinuousLinearMap.id R E :=
  rfl
#align linear_isometry.id_to_continuous_linear_map LinearIsometry.id_toContinuousLinearMap

instance : Inhabited (E →ₗᵢ[R] E) :=
  ⟨id⟩

/-- Composition of linear isometries. -/
def comp (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (f : E →ₛₗᵢ[σ₁₂] E₂) : E →ₛₗᵢ[σ₁₃] E₃ :=
  ⟨g.toLinearMap.comp f.toLinearMap, fun x => (norm_map g _).trans (norm_map f _)⟩
#align linear_isometry.comp LinearIsometry.comp

include σ₁₃

@[simp]
theorem coe_comp (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (f : E →ₛₗᵢ[σ₁₂] E₂) : ⇑(g.comp f) = g ∘ f :=
  rfl
#align linear_isometry.coe_comp LinearIsometry.coe_comp

omit σ₁₃

@[simp]
theorem id_comp : (id : E₂ →ₗᵢ[R₂] E₂).comp f = f :=
  ext fun x => rfl
#align linear_isometry.id_comp LinearIsometry.id_comp

@[simp]
theorem comp_id : f.comp id = f :=
  ext fun x => rfl
#align linear_isometry.comp_id LinearIsometry.comp_id

include σ₁₃ σ₂₄ σ₁₄

theorem comp_assoc (f : E₃ →ₛₗᵢ[σ₃₄] E₄) (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (h : E →ₛₗᵢ[σ₁₂] E₂) :
    (f.comp g).comp h = f.comp (g.comp h) :=
  rfl
#align linear_isometry.comp_assoc LinearIsometry.comp_assoc

omit σ₁₃ σ₂₄ σ₁₄

instance : Monoid (E →ₗᵢ[R] E) where
  one := id
  mul := comp
  mul_assoc := comp_assoc
  one_mul := id_comp
  mul_one := comp_id

@[simp]
theorem coe_one : ((1 : E →ₗᵢ[R] E) : E → E) = id :=
  rfl
#align linear_isometry.coe_one LinearIsometry.coe_one

@[simp]
theorem coe_mul (f g : E →ₗᵢ[R] E) : ⇑(f * g) = f ∘ g :=
  rfl
#align linear_isometry.coe_mul LinearIsometry.coe_mul

theorem one_def : (1 : E →ₗᵢ[R] E) = id :=
  rfl
#align linear_isometry.one_def LinearIsometry.one_def

theorem mul_def (f g : E →ₗᵢ[R] E) : (f * g : E →ₗᵢ[R] E) = f.comp g :=
  rfl
#align linear_isometry.mul_def LinearIsometry.mul_def

end LinearIsometry

/-- Construct a `linear_isometry` from a `linear_map` satisfying `isometry`. -/
def LinearMap.toLinearIsometry (f : E →ₛₗ[σ₁₂] E₂) (hf : Isometry f) : E →ₛₗᵢ[σ₁₂] E₂ :=
  { f with
    norm_map' := by
      simp_rw [← dist_zero_right, ← f.map_zero]
      exact fun x => hf.dist_eq x _ }
#align linear_map.to_linear_isometry LinearMap.toLinearIsometry

namespace Submodule

variable {R' : Type _} [Ring R'] [Module R' E] (p : Submodule R' E)

/-- `submodule.subtype` as a `linear_isometry`. -/
def subtypeₗᵢ : p →ₗᵢ[R'] E :=
  ⟨p.Subtype, fun x => rfl⟩
#align submodule.subtypeₗᵢ Submodule.subtypeₗᵢ

@[simp]
theorem coe_subtypeₗᵢ : ⇑p.subtypeₗᵢ = p.Subtype :=
  rfl
#align submodule.coe_subtypeₗᵢ Submodule.coe_subtypeₗᵢ

@[simp]
theorem subtypeₗᵢ_toLinearMap : p.subtypeₗᵢ.toLinearMap = p.Subtype :=
  rfl
#align submodule.subtypeₗᵢ_to_linear_map Submodule.subtypeₗᵢ_toLinearMap

@[simp]
theorem subtypeₗᵢ_toContinuousLinearMap : p.subtypeₗᵢ.toContinuousLinearMap = p.subtypeL :=
  rfl
#align submodule.subtypeₗᵢ_to_continuous_linear_map Submodule.subtypeₗᵢ_toContinuousLinearMap

end Submodule

/-- A semilinear isometric equivalence between two normed vector spaces. -/
structure LinearIsometryEquiv (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [RingHomInvPair σ₁₂ σ₂₁]
  [RingHomInvPair σ₂₁ σ₁₂] (E E₂ : Type _) [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂]
  [Module R E] [Module R₂ E₂] extends E ≃ₛₗ[σ₁₂] E₂ where
  norm_map' : ∀ x, ‖to_linear_equiv x‖ = ‖x‖
#align linear_isometry_equiv LinearIsometryEquiv

-- mathport name: «expr ≃ₛₗᵢ[ ] »
notation:25 E " ≃ₛₗᵢ[" σ₁₂:25 "] " E₂:0 => LinearIsometryEquiv σ₁₂ E E₂

-- mathport name: «expr ≃ₗᵢ[ ] »
notation:25 E " ≃ₗᵢ[" R:25 "] " E₂:0 => LinearIsometryEquiv (RingHom.id R) E E₂

-- mathport name: «expr ≃ₗᵢ⋆[ ] »
notation:25 E " ≃ₗᵢ⋆[" R:25 "] " E₂:0 => LinearIsometryEquiv (starRingEnd R) E E₂

/-- `semilinear_isometry_equiv_class F σ E E₂` asserts `F` is a type of bundled `σ`-semilinear
isometric equivs `E → E₂`.

See also `linear_isometry_equiv_class F R E E₂` for the case where `σ` is the identity map on `R`.

A map `f` between an `R`-module and an `S`-module over a ring homomorphism `σ : R →+* S`
is semilinear if it satisfies the two properties `f (x + y) = f x + f y` and
`f (c • x) = (σ c) • f x`. -/
class SemilinearIsometryEquivClass (𝓕 : Type _) {R R₂ : outParam (Type _)} [Semiring R]
  [Semiring R₂] (σ₁₂ : outParam <| R →+* R₂) {σ₂₁ : outParam <| R₂ →+* R} [RingHomInvPair σ₁₂ σ₂₁]
  [RingHomInvPair σ₂₁ σ₁₂] (E E₂ : outParam (Type _)) [SeminormedAddCommGroup E]
  [SeminormedAddCommGroup E₂] [Module R E] [Module R₂ E₂] extends
  SemilinearEquivClass 𝓕 σ₁₂ E E₂ where
  norm_map : ∀ (f : 𝓕) (x : E), ‖f x‖ = ‖x‖
#align semilinear_isometry_equiv_class SemilinearIsometryEquivClass

/-- `linear_isometry_equiv_class F R E E₂` asserts `F` is a type of bundled `R`-linear isometries
`M → M₂`.

This is an abbreviation for `semilinear_isometry_equiv_class F (ring_hom.id R) E E₂`.
-/
abbrev LinearIsometryEquivClass (𝓕 : Type _) (R E E₂ : outParam (Type _)) [Semiring R]
    [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂] [Module R E] [Module R E₂] :=
  SemilinearIsometryEquivClass 𝓕 (RingHom.id R) E E₂
#align linear_isometry_equiv_class LinearIsometryEquivClass

namespace SemilinearIsometryEquivClass

variable (𝓕)

include σ₂₁

-- `σ₂₁` becomes a metavariable, but it's OK since it's an outparam
@[nolint dangerous_instance]
instance (priority := 100) [s : SemilinearIsometryEquivClass 𝓕 σ₁₂ E E₂] :
    SemilinearIsometryClass 𝓕 σ₁₂ E E₂ :=
  { s with
    coe := (coe : 𝓕 → E → E₂)
    coe_injective' := @FunLike.coe_injective 𝓕 _ _ _ }

omit σ₂₁

end SemilinearIsometryEquivClass

namespace LinearIsometryEquiv

variable (e : E ≃ₛₗᵢ[σ₁₂] E₂)

include σ₂₁

theorem toLinearEquiv_injective : Injective (toLinearEquiv : (E ≃ₛₗᵢ[σ₁₂] E₂) → E ≃ₛₗ[σ₁₂] E₂)
  | ⟨e, _⟩, ⟨_, _⟩, rfl => rfl
#align linear_isometry_equiv.to_linear_equiv_injective LinearIsometryEquiv.toLinearEquiv_injective

@[simp]
theorem toLinearEquiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} : f.toLinearEquiv = g.toLinearEquiv ↔ f = g :=
  toLinearEquiv_injective.eq_iff
#align linear_isometry_equiv.to_linear_equiv_inj LinearIsometryEquiv.toLinearEquiv_inj

instance : SemilinearIsometryEquivClass (E ≃ₛₗᵢ[σ₁₂] E₂) σ₁₂ E E₂
    where
  coe e := e.toFun
  inv e := e.invFun
  coe_injective' f g h₁ h₂ := by
    cases' f with f' _
    cases' g with g' _
    cases f'
    cases g'
    congr
  left_inv e := e.left_inv
  right_inv e := e.right_inv
  map_add f := map_add f.toLinearEquiv
  map_smulₛₗ e := map_smulₛₗ e.toLinearEquiv
  norm_map e := e.norm_map'

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly.
-/
instance : CoeFun (E ≃ₛₗᵢ[σ₁₂] E₂) fun _ => E → E₂ :=
  ⟨fun f => f.toFun⟩

theorem coe_injective : @Function.Injective (E ≃ₛₗᵢ[σ₁₂] E₂) (E → E₂) coeFn :=
  FunLike.coe_injective
#align linear_isometry_equiv.coe_injective LinearIsometryEquiv.coe_injective

@[simp]
theorem coe_mk (e : E ≃ₛₗ[σ₁₂] E₂) (he : ∀ x, ‖e x‖ = ‖x‖) : ⇑(mk e he) = e :=
  rfl
#align linear_isometry_equiv.coe_mk LinearIsometryEquiv.coe_mk

@[simp]
theorem coe_toLinearEquiv (e : E ≃ₛₗᵢ[σ₁₂] E₂) : ⇑e.toLinearEquiv = e :=
  rfl
#align linear_isometry_equiv.coe_to_linear_equiv LinearIsometryEquiv.coe_toLinearEquiv

@[ext]
theorem ext {e e' : E ≃ₛₗᵢ[σ₁₂] E₂} (h : ∀ x, e x = e' x) : e = e' :=
  toLinearEquiv_injective <| LinearEquiv.ext h
#align linear_isometry_equiv.ext LinearIsometryEquiv.ext

protected theorem congr_arg {f : E ≃ₛₗᵢ[σ₁₂] E₂} : ∀ {x x' : E}, x = x' → f x = f x'
  | _, _, rfl => rfl
#align linear_isometry_equiv.congr_arg LinearIsometryEquiv.congr_arg

protected theorem congr_fun {f g : E ≃ₛₗᵢ[σ₁₂] E₂} (h : f = g) (x : E) : f x = g x :=
  h ▸ rfl
#align linear_isometry_equiv.congr_fun LinearIsometryEquiv.congr_fun

/-- Construct a `linear_isometry_equiv` from a `linear_equiv` and two inequalities:
`∀ x, ‖e x‖ ≤ ‖x‖` and `∀ y, ‖e.symm y‖ ≤ ‖y‖`. -/
def ofBounds (e : E ≃ₛₗ[σ₁₂] E₂) (h₁ : ∀ x, ‖e x‖ ≤ ‖x‖) (h₂ : ∀ y, ‖e.symm y‖ ≤ ‖y‖) :
    E ≃ₛₗᵢ[σ₁₂] E₂ :=
  ⟨e, fun x => le_antisymm (h₁ x) <| by simpa only [e.symm_apply_apply] using h₂ (e x)⟩
#align linear_isometry_equiv.of_bounds LinearIsometryEquiv.ofBounds

@[simp]
theorem norm_map (x : E) : ‖e x‖ = ‖x‖ :=
  e.norm_map' x
#align linear_isometry_equiv.norm_map LinearIsometryEquiv.norm_map

/-- Reinterpret a `linear_isometry_equiv` as a `linear_isometry`. -/
def toLinearIsometry : E →ₛₗᵢ[σ₁₂] E₂ :=
  ⟨e.1, e.2⟩
#align linear_isometry_equiv.to_linear_isometry LinearIsometryEquiv.toLinearIsometry

theorem toLinearIsometry_injective : Function.Injective (toLinearIsometry : _ → E →ₛₗᵢ[σ₁₂] E₂) :=
  fun x y h => coe_injective (congr_arg _ h : ⇑x.toLinearIsometry = _)
#align linear_isometry_equiv.to_linear_isometry_injective LinearIsometryEquiv.toLinearIsometry_injective

@[simp]
theorem toLinearIsometry_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
    f.toLinearIsometry = g.toLinearIsometry ↔ f = g :=
  toLinearIsometry_injective.eq_iff
#align linear_isometry_equiv.to_linear_isometry_inj LinearIsometryEquiv.toLinearIsometry_inj

@[simp]
theorem coe_toLinearIsometry : ⇑e.toLinearIsometry = e :=
  rfl
#align linear_isometry_equiv.coe_to_linear_isometry LinearIsometryEquiv.coe_toLinearIsometry

protected theorem isometry : Isometry e :=
  e.toLinearIsometry.Isometry
#align linear_isometry_equiv.isometry LinearIsometryEquiv.isometry

/-- Reinterpret a `linear_isometry_equiv` as an `isometry_equiv`. -/
def toIsometryEquiv : E ≃ᵢ E₂ :=
  ⟨e.toLinearEquiv.toEquiv, e.Isometry⟩
#align linear_isometry_equiv.to_isometry_equiv LinearIsometryEquiv.toIsometryEquiv

theorem toIsometryEquiv_injective :
    Function.Injective (toIsometryEquiv : (E ≃ₛₗᵢ[σ₁₂] E₂) → E ≃ᵢ E₂) := fun x y h =>
  coe_injective (congr_arg _ h : ⇑x.toIsometryEquiv = _)
#align linear_isometry_equiv.to_isometry_equiv_injective LinearIsometryEquiv.toIsometryEquiv_injective

@[simp]
theorem toIsometryEquiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
    f.toIsometryEquiv = g.toIsometryEquiv ↔ f = g :=
  toIsometryEquiv_injective.eq_iff
#align linear_isometry_equiv.to_isometry_equiv_inj LinearIsometryEquiv.toIsometryEquiv_inj

@[simp]
theorem coe_toIsometryEquiv : ⇑e.toIsometryEquiv = e :=
  rfl
#align linear_isometry_equiv.coe_to_isometry_equiv LinearIsometryEquiv.coe_toIsometryEquiv

theorem range_eq_univ (e : E ≃ₛₗᵢ[σ₁₂] E₂) : Set.range e = Set.univ :=
  by
  rw [← coe_to_isometry_equiv]
  exact IsometryEquiv.range_eq_univ _
#align linear_isometry_equiv.range_eq_univ LinearIsometryEquiv.range_eq_univ

/-- Reinterpret a `linear_isometry_equiv` as an `homeomorph`. -/
def toHomeomorph : E ≃ₜ E₂ :=
  e.toIsometryEquiv.toHomeomorph
#align linear_isometry_equiv.to_homeomorph LinearIsometryEquiv.toHomeomorph

theorem toHomeomorph_injective : Function.Injective (toHomeomorph : (E ≃ₛₗᵢ[σ₁₂] E₂) → E ≃ₜ E₂) :=
  fun x y h => coe_injective (congr_arg _ h : ⇑x.toHomeomorph = _)
#align linear_isometry_equiv.to_homeomorph_injective LinearIsometryEquiv.toHomeomorph_injective

@[simp]
theorem toHomeomorph_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} : f.toHomeomorph = g.toHomeomorph ↔ f = g :=
  toHomeomorph_injective.eq_iff
#align linear_isometry_equiv.to_homeomorph_inj LinearIsometryEquiv.toHomeomorph_inj

@[simp]
theorem coe_toHomeomorph : ⇑e.toHomeomorph = e :=
  rfl
#align linear_isometry_equiv.coe_to_homeomorph LinearIsometryEquiv.coe_toHomeomorph

protected theorem continuous : Continuous e :=
  e.Isometry.Continuous
#align linear_isometry_equiv.continuous LinearIsometryEquiv.continuous

protected theorem continuousAt {x} : ContinuousAt e x :=
  e.Continuous.ContinuousAt
#align linear_isometry_equiv.continuous_at LinearIsometryEquiv.continuousAt

protected theorem continuousOn {s} : ContinuousOn e s :=
  e.Continuous.ContinuousOn
#align linear_isometry_equiv.continuous_on LinearIsometryEquiv.continuousOn

protected theorem continuousWithinAt {s x} : ContinuousWithinAt e s x :=
  e.Continuous.ContinuousWithinAt
#align linear_isometry_equiv.continuous_within_at LinearIsometryEquiv.continuousWithinAt

/-- Interpret a `linear_isometry_equiv` as a continuous linear equiv. -/
def toContinuousLinearEquiv : E ≃SL[σ₁₂] E₂ :=
  { e.toLinearIsometry.toContinuousLinearMap, e.toHomeomorph with }
#align linear_isometry_equiv.to_continuous_linear_equiv LinearIsometryEquiv.toContinuousLinearEquiv

theorem toContinuousLinearEquiv_injective :
    Function.Injective (toContinuousLinearEquiv : _ → E ≃SL[σ₁₂] E₂) := fun x y h =>
  coe_injective (congr_arg _ h : ⇑x.toContinuousLinearEquiv = _)
#align linear_isometry_equiv.to_continuous_linear_equiv_injective LinearIsometryEquiv.toContinuousLinearEquiv_injective

@[simp]
theorem toContinuousLinearEquiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
    f.toContinuousLinearEquiv = g.toContinuousLinearEquiv ↔ f = g :=
  toContinuousLinearEquiv_injective.eq_iff
#align linear_isometry_equiv.to_continuous_linear_equiv_inj LinearIsometryEquiv.toContinuousLinearEquiv_inj

@[simp]
theorem coe_toContinuousLinearEquiv : ⇑e.toContinuousLinearEquiv = e :=
  rfl
#align linear_isometry_equiv.coe_to_continuous_linear_equiv LinearIsometryEquiv.coe_toContinuousLinearEquiv

omit σ₂₁

variable (R E)

/-- Identity map as a `linear_isometry_equiv`. -/
def refl : E ≃ₗᵢ[R] E :=
  ⟨LinearEquiv.refl R E, fun x => rfl⟩
#align linear_isometry_equiv.refl LinearIsometryEquiv.refl

/-- Linear isometry equiv between a space and its lift to another universe. -/
def ulift : ULift E ≃ₗᵢ[R] E :=
  { ContinuousLinearEquiv.ulift with norm_map' := fun x => rfl }
#align linear_isometry_equiv.ulift LinearIsometryEquiv.ulift

variable {R E}

instance : Inhabited (E ≃ₗᵢ[R] E) :=
  ⟨refl R E⟩

@[simp]
theorem coe_refl : ⇑(refl R E) = id :=
  rfl
#align linear_isometry_equiv.coe_refl LinearIsometryEquiv.coe_refl

/-- The inverse `linear_isometry_equiv`. -/
def symm : E₂ ≃ₛₗᵢ[σ₂₁] E :=
  ⟨e.toLinearEquiv.symm, fun x =>
    (e.norm_map _).symm.trans <| congr_arg norm <| e.toLinearEquiv.apply_symm_apply x⟩
#align linear_isometry_equiv.symm LinearIsometryEquiv.symm

@[simp]
theorem apply_symm_apply (x : E₂) : e (e.symm x) = x :=
  e.toLinearEquiv.apply_symm_apply x
#align linear_isometry_equiv.apply_symm_apply LinearIsometryEquiv.apply_symm_apply

@[simp]
theorem symm_apply_apply (x : E) : e.symm (e x) = x :=
  e.toLinearEquiv.symm_apply_apply x
#align linear_isometry_equiv.symm_apply_apply LinearIsometryEquiv.symm_apply_apply

@[simp]
theorem map_eq_zero_iff {x : E} : e x = 0 ↔ x = 0 :=
  e.toLinearEquiv.map_eq_zero_iff
#align linear_isometry_equiv.map_eq_zero_iff LinearIsometryEquiv.map_eq_zero_iff

@[simp]
theorem symm_symm : e.symm.symm = e :=
  ext fun x => rfl
#align linear_isometry_equiv.symm_symm LinearIsometryEquiv.symm_symm

@[simp]
theorem toLinearEquiv_symm : e.toLinearEquiv.symm = e.symm.toLinearEquiv :=
  rfl
#align linear_isometry_equiv.to_linear_equiv_symm LinearIsometryEquiv.toLinearEquiv_symm

@[simp]
theorem toIsometryEquiv_symm : e.toIsometryEquiv.symm = e.symm.toIsometryEquiv :=
  rfl
#align linear_isometry_equiv.to_isometry_equiv_symm LinearIsometryEquiv.toIsometryEquiv_symm

@[simp]
theorem toHomeomorph_symm : e.toHomeomorph.symm = e.symm.toHomeomorph :=
  rfl
#align linear_isometry_equiv.to_homeomorph_symm LinearIsometryEquiv.toHomeomorph_symm

/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
  because it is a composition of multiple projections. -/
def Simps.apply (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [RingHomInvPair σ₁₂ σ₂₁] [RingHomInvPair σ₂₁ σ₁₂]
    (E E₂ : Type _) [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂] [Module R E]
    [Module R₂ E₂] (h : E ≃ₛₗᵢ[σ₁₂] E₂) : E → E₂ :=
  h
#align linear_isometry_equiv.simps.apply LinearIsometryEquiv.Simps.apply

/-- See Note [custom simps projection] -/
def Simps.symmApply (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [RingHomInvPair σ₁₂ σ₂₁]
    [RingHomInvPair σ₂₁ σ₁₂] (E E₂ : Type _) [SeminormedAddCommGroup E] [SeminormedAddCommGroup E₂]
    [Module R E] [Module R₂ E₂] (h : E ≃ₛₗᵢ[σ₁₂] E₂) : E₂ → E :=
  h.symm
#align linear_isometry_equiv.simps.symm_apply LinearIsometryEquiv.Simps.symmApply

initialize_simps_projections LinearIsometryEquiv (to_linear_equiv_to_fun → apply,
  to_linear_equiv_inv_fun → symm_apply)

include σ₃₁ σ₃₂

/-- Composition of `linear_isometry_equiv`s as a `linear_isometry_equiv`. -/
def trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : E ≃ₛₗᵢ[σ₁₃] E₃ :=
  ⟨e.toLinearEquiv.trans e'.toLinearEquiv, fun x => (e'.norm_map _).trans (e.norm_map _)⟩
#align linear_isometry_equiv.trans LinearIsometryEquiv.trans

include σ₁₃ σ₂₁

@[simp]
theorem coe_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : ⇑(e₁.trans e₂) = e₂ ∘ e₁ :=
  rfl
#align linear_isometry_equiv.coe_trans LinearIsometryEquiv.coe_trans

@[simp]
theorem trans_apply (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) (c : E) :
    (e₁.trans e₂ : E ≃ₛₗᵢ[σ₁₃] E₃) c = e₂ (e₁ c) :=
  rfl
#align linear_isometry_equiv.trans_apply LinearIsometryEquiv.trans_apply

@[simp]
theorem toLinearEquiv_trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
    (e.trans e').toLinearEquiv = e.toLinearEquiv.trans e'.toLinearEquiv :=
  rfl
#align linear_isometry_equiv.to_linear_equiv_trans LinearIsometryEquiv.toLinearEquiv_trans

omit σ₁₃ σ₂₁ σ₃₁ σ₃₂

@[simp]
theorem trans_refl : e.trans (refl R₂ E₂) = e :=
  ext fun x => rfl
#align linear_isometry_equiv.trans_refl LinearIsometryEquiv.trans_refl

@[simp]
theorem refl_trans : (refl R E).trans e = e :=
  ext fun x => rfl
#align linear_isometry_equiv.refl_trans LinearIsometryEquiv.refl_trans

@[simp]
theorem self_trans_symm : e.trans e.symm = refl R E :=
  ext e.symm_apply_apply
#align linear_isometry_equiv.self_trans_symm LinearIsometryEquiv.self_trans_symm

@[simp]
theorem symm_trans_self : e.symm.trans e = refl R₂ E₂ :=
  ext e.apply_symm_apply
#align linear_isometry_equiv.symm_trans_self LinearIsometryEquiv.symm_trans_self

@[simp]
theorem symm_comp_self : e.symm ∘ e = id :=
  funext e.symm_apply_apply
#align linear_isometry_equiv.symm_comp_self LinearIsometryEquiv.symm_comp_self

@[simp]
theorem self_comp_symm : e ∘ e.symm = id :=
  e.symm.symm_comp_self
#align linear_isometry_equiv.self_comp_symm LinearIsometryEquiv.self_comp_symm

include σ₁₃ σ₂₁ σ₃₂ σ₃₁

@[simp]
theorem symm_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
    (e₁.trans e₂).symm = e₂.symm.trans e₁.symm :=
  rfl
#align linear_isometry_equiv.symm_trans LinearIsometryEquiv.symm_trans

theorem coe_symm_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
    ⇑(e₁.trans e₂).symm = e₁.symm ∘ e₂.symm :=
  rfl
#align linear_isometry_equiv.coe_symm_trans LinearIsometryEquiv.coe_symm_trans

include σ₁₄ σ₄₁ σ₄₂ σ₄₃ σ₂₄

theorem trans_assoc (eEE₂ : E ≃ₛₗᵢ[σ₁₂] E₂) (eE₂E₃ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) (eE₃E₄ : E₃ ≃ₛₗᵢ[σ₃₄] E₄) :
    eEE₂.trans (eE₂E₃.trans eE₃E₄) = (eEE₂.trans eE₂E₃).trans eE₃E₄ :=
  rfl
#align linear_isometry_equiv.trans_assoc LinearIsometryEquiv.trans_assoc

omit σ₂₁ σ₃₁ σ₄₁ σ₃₂ σ₄₂ σ₄₃ σ₁₃ σ₂₄ σ₁₄

instance : Group (E ≃ₗᵢ[R] E) where
  mul e₁ e₂ := e₂.trans e₁
  one := refl _ _
  inv := symm
  one_mul := trans_refl
  mul_one := refl_trans
  mul_assoc _ _ _ := trans_assoc _ _ _
  mul_left_inv := self_trans_symm

@[simp]
theorem coe_one : ⇑(1 : E ≃ₗᵢ[R] E) = id :=
  rfl
#align linear_isometry_equiv.coe_one LinearIsometryEquiv.coe_one

@[simp]
theorem coe_mul (e e' : E ≃ₗᵢ[R] E) : ⇑(e * e') = e ∘ e' :=
  rfl
#align linear_isometry_equiv.coe_mul LinearIsometryEquiv.coe_mul

@[simp]
theorem coe_inv (e : E ≃ₗᵢ[R] E) : ⇑e⁻¹ = e.symm :=
  rfl
#align linear_isometry_equiv.coe_inv LinearIsometryEquiv.coe_inv

theorem one_def : (1 : E ≃ₗᵢ[R] E) = refl _ _ :=
  rfl
#align linear_isometry_equiv.one_def LinearIsometryEquiv.one_def

theorem mul_def (e e' : E ≃ₗᵢ[R] E) : (e * e' : E ≃ₗᵢ[R] E) = e'.trans e :=
  rfl
#align linear_isometry_equiv.mul_def LinearIsometryEquiv.mul_def

theorem inv_def (e : E ≃ₗᵢ[R] E) : (e⁻¹ : E ≃ₗᵢ[R] E) = e.symm :=
  rfl
#align linear_isometry_equiv.inv_def LinearIsometryEquiv.inv_def

/-! Lemmas about mixing the group structure with definitions. Because we have multiple ways to
express `linear_isometry_equiv.refl`, `linear_isometry_equiv.symm`, and
`linear_isometry_equiv.trans`, we want simp lemmas for every combination.
The assumption made here is that if you're using the group structure, you want to preserve it
after simp.

This copies the approach used by the lemmas near `equiv.perm.trans_one`. -/

@[simp]
theorem trans_one : e.trans (1 : E₂ ≃ₗᵢ[R₂] E₂) = e :=
  trans_refl _
#align linear_isometry_equiv.trans_one LinearIsometryEquiv.trans_one

@[simp]
theorem one_trans : (1 : E ≃ₗᵢ[R] E).trans e = e :=
  refl_trans _
#align linear_isometry_equiv.one_trans LinearIsometryEquiv.one_trans

@[simp]
theorem refl_mul (e : E ≃ₗᵢ[R] E) : refl _ _ * e = e :=
  trans_refl _
#align linear_isometry_equiv.refl_mul LinearIsometryEquiv.refl_mul

@[simp]
theorem mul_refl (e : E ≃ₗᵢ[R] E) : e * refl _ _ = e :=
  refl_trans _
#align linear_isometry_equiv.mul_refl LinearIsometryEquiv.mul_refl

include σ₂₁

/-- Reinterpret a `linear_isometry_equiv` as a `continuous_linear_equiv`. -/
instance : CoeTC (E ≃ₛₗᵢ[σ₁₂] E₂) (E ≃SL[σ₁₂] E₂) :=
  ⟨fun e => ⟨e.toLinearEquiv, e.Continuous, e.toIsometryEquiv.symm.Continuous⟩⟩

instance : CoeTC (E ≃ₛₗᵢ[σ₁₂] E₂) (E →SL[σ₁₂] E₂) :=
  ⟨fun e => ↑(e : E ≃SL[σ₁₂] E₂)⟩

@[simp]
theorem coe_coe : ⇑(e : E ≃SL[σ₁₂] E₂) = e :=
  rfl
#align linear_isometry_equiv.coe_coe LinearIsometryEquiv.coe_coe

@[simp]
theorem coe_coe' : ((e : E ≃SL[σ₁₂] E₂) : E →SL[σ₁₂] E₂) = e :=
  rfl
#align linear_isometry_equiv.coe_coe' LinearIsometryEquiv.coe_coe'

@[simp]
theorem coe_coe'' : ⇑(e : E →SL[σ₁₂] E₂) = e :=
  rfl
#align linear_isometry_equiv.coe_coe'' LinearIsometryEquiv.coe_coe''

omit σ₂₁

@[simp]
theorem map_zero : e 0 = 0 :=
  e.1.map_zero
#align linear_isometry_equiv.map_zero LinearIsometryEquiv.map_zero

@[simp]
theorem map_add (x y : E) : e (x + y) = e x + e y :=
  e.1.map_add x y
#align linear_isometry_equiv.map_add LinearIsometryEquiv.map_add

@[simp]
theorem map_sub (x y : E) : e (x - y) = e x - e y :=
  e.1.map_sub x y
#align linear_isometry_equiv.map_sub LinearIsometryEquiv.map_sub

@[simp]
theorem map_smulₛₗ (c : R) (x : E) : e (c • x) = σ₁₂ c • e x :=
  e.1.map_smulₛₗ c x
#align linear_isometry_equiv.map_smulₛₗ LinearIsometryEquiv.map_smulₛₗ

@[simp]
theorem map_smul [Module R E₂] {e : E ≃ₗᵢ[R] E₂} (c : R) (x : E) : e (c • x) = c • e x :=
  e.1.map_smul c x
#align linear_isometry_equiv.map_smul LinearIsometryEquiv.map_smul

@[simp]
theorem nnnorm_map (x : E) : ‖e x‖₊ = ‖x‖₊ :=
  SemilinearIsometryClass.nnnorm_map e x
#align linear_isometry_equiv.nnnorm_map LinearIsometryEquiv.nnnorm_map

@[simp]
theorem dist_map (x y : E) : dist (e x) (e y) = dist x y :=
  e.toLinearIsometry.dist_map x y
#align linear_isometry_equiv.dist_map LinearIsometryEquiv.dist_map

@[simp]
theorem edist_map (x y : E) : edist (e x) (e y) = edist x y :=
  e.toLinearIsometry.edist_map x y
#align linear_isometry_equiv.edist_map LinearIsometryEquiv.edist_map

protected theorem bijective : Bijective e :=
  e.1.Bijective
#align linear_isometry_equiv.bijective LinearIsometryEquiv.bijective

protected theorem injective : Injective e :=
  e.1.Injective
#align linear_isometry_equiv.injective LinearIsometryEquiv.injective

protected theorem surjective : Surjective e :=
  e.1.Surjective
#align linear_isometry_equiv.surjective LinearIsometryEquiv.surjective

@[simp]
theorem map_eq_iff {x y : E} : e x = e y ↔ x = y :=
  e.Injective.eq_iff
#align linear_isometry_equiv.map_eq_iff LinearIsometryEquiv.map_eq_iff

theorem map_ne {x y : E} (h : x ≠ y) : e x ≠ e y :=
  e.Injective.Ne h
#align linear_isometry_equiv.map_ne LinearIsometryEquiv.map_ne

protected theorem lipschitz : LipschitzWith 1 e :=
  e.Isometry.lipschitz
#align linear_isometry_equiv.lipschitz LinearIsometryEquiv.lipschitz

protected theorem antilipschitz : AntilipschitzWith 1 e :=
  e.Isometry.antilipschitz
#align linear_isometry_equiv.antilipschitz LinearIsometryEquiv.antilipschitz

theorem image_eq_preimage (s : Set E) : e '' s = e.symm ⁻¹' s :=
  e.toLinearEquiv.image_eq_preimage s
#align linear_isometry_equiv.image_eq_preimage LinearIsometryEquiv.image_eq_preimage

@[simp]
theorem ediam_image (s : Set E) : EMetric.diam (e '' s) = EMetric.diam s :=
  e.Isometry.ediam_image s
#align linear_isometry_equiv.ediam_image LinearIsometryEquiv.ediam_image

@[simp]
theorem diam_image (s : Set E) : Metric.diam (e '' s) = Metric.diam s :=
  e.Isometry.diam_image s
#align linear_isometry_equiv.diam_image LinearIsometryEquiv.diam_image

@[simp]
theorem preimage_ball (x : E₂) (r : ℝ) : e ⁻¹' Metric.ball x r = Metric.ball (e.symm x) r :=
  e.toIsometryEquiv.preimage_ball x r
#align linear_isometry_equiv.preimage_ball LinearIsometryEquiv.preimage_ball

@[simp]
theorem preimage_sphere (x : E₂) (r : ℝ) : e ⁻¹' Metric.sphere x r = Metric.sphere (e.symm x) r :=
  e.toIsometryEquiv.preimage_sphere x r
#align linear_isometry_equiv.preimage_sphere LinearIsometryEquiv.preimage_sphere

@[simp]
theorem preimage_closedBall (x : E₂) (r : ℝ) :
    e ⁻¹' Metric.closedBall x r = Metric.closedBall (e.symm x) r :=
  e.toIsometryEquiv.preimage_closedBall x r
#align linear_isometry_equiv.preimage_closed_ball LinearIsometryEquiv.preimage_closedBall

@[simp]
theorem image_ball (x : E) (r : ℝ) : e '' Metric.ball x r = Metric.ball (e x) r :=
  e.toIsometryEquiv.image_ball x r
#align linear_isometry_equiv.image_ball LinearIsometryEquiv.image_ball

@[simp]
theorem image_sphere (x : E) (r : ℝ) : e '' Metric.sphere x r = Metric.sphere (e x) r :=
  e.toIsometryEquiv.image_sphere x r
#align linear_isometry_equiv.image_sphere LinearIsometryEquiv.image_sphere

@[simp]
theorem image_closedBall (x : E) (r : ℝ) : e '' Metric.closedBall x r = Metric.closedBall (e x) r :=
  e.toIsometryEquiv.image_closedBall x r
#align linear_isometry_equiv.image_closed_ball LinearIsometryEquiv.image_closedBall

variable {α : Type _} [TopologicalSpace α]

@[simp]
theorem comp_continuousOn_iff {f : α → E} {s : Set α} : ContinuousOn (e ∘ f) s ↔ ContinuousOn f s :=
  e.Isometry.comp_continuousOn_iff
#align linear_isometry_equiv.comp_continuous_on_iff LinearIsometryEquiv.comp_continuousOn_iff

@[simp]
theorem comp_continuous_iff {f : α → E} : Continuous (e ∘ f) ↔ Continuous f :=
  e.Isometry.comp_continuous_iff
#align linear_isometry_equiv.comp_continuous_iff LinearIsometryEquiv.comp_continuous_iff

instance completeSpace_map (p : Submodule R E) [CompleteSpace p] :
    CompleteSpace (p.map (e.toLinearEquiv : E →ₛₗ[σ₁₂] E₂)) :=
  e.toLinearIsometry.completeSpace_map' p
#align linear_isometry_equiv.complete_space_map LinearIsometryEquiv.completeSpace_map

include σ₂₁

/-- Construct a linear isometry equiv from a surjective linear isometry. -/
noncomputable def ofSurjective (f : F →ₛₗᵢ[σ₁₂] E₂) (hfr : Function.Surjective f) :
    F ≃ₛₗᵢ[σ₁₂] E₂ :=
  { LinearEquiv.ofBijective f.toLinearMap ⟨f.Injective, hfr⟩ with norm_map' := f.norm_map }
#align linear_isometry_equiv.of_surjective LinearIsometryEquiv.ofSurjective

@[simp]
theorem coe_ofSurjective (f : F →ₛₗᵢ[σ₁₂] E₂) (hfr : Function.Surjective f) :
    ⇑(LinearIsometryEquiv.ofSurjective f hfr) = f :=
  by
  ext
  rfl
#align linear_isometry_equiv.coe_of_surjective LinearIsometryEquiv.coe_ofSurjective

/-- If a linear isometry has an inverse, it is a linear isometric equivalence. -/
def ofLinearIsometry (f : E →ₛₗᵢ[σ₁₂] E₂) (g : E₂ →ₛₗ[σ₂₁] E)
    (h₁ : f.toLinearMap.comp g = LinearMap.id) (h₂ : g.comp f.toLinearMap = LinearMap.id) :
    E ≃ₛₗᵢ[σ₁₂] E₂ :=
  { LinearEquiv.ofLinear f.toLinearMap g h₁ h₂ with norm_map' := fun x => f.norm_map x }
#align linear_isometry_equiv.of_linear_isometry LinearIsometryEquiv.ofLinearIsometry

@[simp]
theorem coe_ofLinearIsometry (f : E →ₛₗᵢ[σ₁₂] E₂) (g : E₂ →ₛₗ[σ₂₁] E)
    (h₁ : f.toLinearMap.comp g = LinearMap.id) (h₂ : g.comp f.toLinearMap = LinearMap.id) :
    (ofLinearIsometry f g h₁ h₂ : E → E₂) = (f : E → E₂) :=
  rfl
#align linear_isometry_equiv.coe_of_linear_isometry LinearIsometryEquiv.coe_ofLinearIsometry

@[simp]
theorem coe_ofLinearIsometry_symm (f : E →ₛₗᵢ[σ₁₂] E₂) (g : E₂ →ₛₗ[σ₂₁] E)
    (h₁ : f.toLinearMap.comp g = LinearMap.id) (h₂ : g.comp f.toLinearMap = LinearMap.id) :
    ((ofLinearIsometry f g h₁ h₂).symm : E₂ → E) = (g : E₂ → E) :=
  rfl
#align linear_isometry_equiv.coe_of_linear_isometry_symm LinearIsometryEquiv.coe_ofLinearIsometry_symm

omit σ₂₁

variable (R)

/-- The negation operation on a normed space `E`, considered as a linear isometry equivalence. -/
def neg : E ≃ₗᵢ[R] E :=
  { LinearEquiv.neg R with norm_map' := norm_neg }
#align linear_isometry_equiv.neg LinearIsometryEquiv.neg

variable {R}

@[simp]
theorem coe_neg : (neg R : E → E) = fun x => -x :=
  rfl
#align linear_isometry_equiv.coe_neg LinearIsometryEquiv.coe_neg

@[simp]
theorem symm_neg : (neg R : E ≃ₗᵢ[R] E).symm = neg R :=
  rfl
#align linear_isometry_equiv.symm_neg LinearIsometryEquiv.symm_neg

variable (R E E₂ E₃)

/-- The natural equivalence `(E × E₂) × E₃ ≃ E × (E₂ × E₃)` is a linear isometry. -/
def prodAssoc [Module R E₂] [Module R E₃] : (E × E₂) × E₃ ≃ₗᵢ[R] E × E₂ × E₃ :=
  { Equiv.prodAssoc E E₂ E₃ with
    toFun := Equiv.prodAssoc E E₂ E₃
    invFun := (Equiv.prodAssoc E E₂ E₃).symm
    map_add' := by simp
    map_smul' := by simp
    norm_map' := by
      rintro ⟨⟨e, f⟩, g⟩
      simp only [LinearEquiv.coe_mk, Equiv.prodAssoc_apply, Prod.norm_def, max_assoc] }
#align linear_isometry_equiv.prod_assoc LinearIsometryEquiv.prodAssoc

@[simp]
theorem coe_prodAssoc [Module R E₂] [Module R E₃] :
    (prodAssoc R E E₂ E₃ : (E × E₂) × E₃ → E × E₂ × E₃) = Equiv.prodAssoc E E₂ E₃ :=
  rfl
#align linear_isometry_equiv.coe_prod_assoc LinearIsometryEquiv.coe_prodAssoc

@[simp]
theorem coe_prodAssoc_symm [Module R E₂] [Module R E₃] :
    ((prodAssoc R E E₂ E₃).symm : E × E₂ × E₃ → (E × E₂) × E₃) = (Equiv.prodAssoc E E₂ E₃).symm :=
  rfl
#align linear_isometry_equiv.coe_prod_assoc_symm LinearIsometryEquiv.coe_prodAssoc_symm

/-- If `p` is a submodule that is equal to `⊤`, then `linear_isometry_equiv.of_top p hp` is the
"identity" equivalence between `p` and `E`. -/
@[simps toLinearEquiv apply symm_apply_coe]
def ofTop {R : Type _} [Ring R] [Module R E] (p : Submodule R E) (hp : p = ⊤) : p ≃ₗᵢ[R] E :=
  { p.subtypeₗᵢ with toLinearEquiv := LinearEquiv.ofTop p hp }
#align linear_isometry_equiv.of_top LinearIsometryEquiv.ofTop

variable {R E E₂ E₃} {R' : Type _} [Ring R'] [Module R' E] (p q : Submodule R' E)

/-- `linear_equiv.of_eq` as a `linear_isometry_equiv`. -/
def ofEq (hpq : p = q) : p ≃ₗᵢ[R'] q :=
  { LinearEquiv.ofEq p q hpq with norm_map' := fun x => rfl }
#align linear_isometry_equiv.of_eq LinearIsometryEquiv.ofEq

variable {p q}

@[simp]
theorem coe_ofEq_apply (h : p = q) (x : p) : (ofEq p q h x : E) = x :=
  rfl
#align linear_isometry_equiv.coe_of_eq_apply LinearIsometryEquiv.coe_ofEq_apply

@[simp]
theorem ofEq_symm (h : p = q) : (ofEq p q h).symm = ofEq q p h.symm :=
  rfl
#align linear_isometry_equiv.of_eq_symm LinearIsometryEquiv.ofEq_symm

@[simp]
theorem ofEq_rfl : ofEq p p rfl = LinearIsometryEquiv.refl R' p := by ext <;> rfl
#align linear_isometry_equiv.of_eq_rfl LinearIsometryEquiv.ofEq_rfl

end LinearIsometryEquiv

/-- Two linear isometries are equal if they are equal on basis vectors. -/
theorem Basis.ext_linearIsometry {ι : Type _} (b : Basis ι R E) {f₁ f₂ : E →ₛₗᵢ[σ₁₂] E₂}
    (h : ∀ i, f₁ (b i) = f₂ (b i)) : f₁ = f₂ :=
  LinearIsometry.toLinearMap_injective <| b.ext h
#align basis.ext_linear_isometry Basis.ext_linearIsometry

include σ₂₁

/-- Two linear isometric equivalences are equal if they are equal on basis vectors. -/
theorem Basis.ext_linearIsometryEquiv {ι : Type _} (b : Basis ι R E) {f₁ f₂ : E ≃ₛₗᵢ[σ₁₂] E₂}
    (h : ∀ i, f₁ (b i) = f₂ (b i)) : f₁ = f₂ :=
  LinearIsometryEquiv.toLinearEquiv_injective <| b.ext' h
#align basis.ext_linear_isometry_equiv Basis.ext_linearIsometryEquiv

omit σ₂₁

/-- Reinterpret a `linear_isometry` as a `linear_isometry_equiv` to the range. -/
@[simps toLinearEquiv apply_coe]
noncomputable def LinearIsometry.equivRange {R S : Type _} [Semiring R] [Ring S] [Module S E]
    [Module R F] {σ₁₂ : R →+* S} {σ₂₁ : S →+* R} [RingHomInvPair σ₁₂ σ₂₁] [RingHomInvPair σ₂₁ σ₁₂]
    (f : F →ₛₗᵢ[σ₁₂] E) : F ≃ₛₗᵢ[σ₁₂] f.toLinearMap.range :=
  { f with toLinearEquiv := LinearEquiv.ofInjective f.toLinearMap f.Injective }
#align linear_isometry.equiv_range LinearIsometry.equivRange


-- 5.8 Exercises
-- #1

-- 4.6 Exercises
-- #2

variables (α : Type*) (p q : α → Prop)
variable r : Prop

example : α → ((∀ x : α, r) ↔ r) :=
begin
  intro a,
  apply iff.intro,
    intro h,
    apply h,
    assumption,
  intro h,
  intro a',
  assumption,
end

open classical

example : (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r :=
begin
  apply iff.intro,
    intro h,
    cases (em r) with hr hnr,
      right,
      assumption,
    left,
    intro a,
    have ha : p a ∨ r := h a,
    cases ha with hpa hr,
      assumption,
    contradiction,
  intros h a,
  cases h with hp hr,
    left,
    exact hp a,
  right,
  assumption,
end

example : (∀ x, r → p x) ↔ (r → ∀ x, p x) :=
begin
  apply iff.intro,
    intros h hr a,
    exact (h a) hr,
  intros h a hr,
  exact (h hr) a,
end


/-
Copyright (c) 2014 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Gabriel Ebner
-/
import algebra.group.defs
import algebra.ne_zero

/-!
# Cast of natural numbers

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

This file defines the *canonical* homomorphism from the natural numbers into an
`add_monoid` with a one.  In additive monoids with one, there exists a unique
such homomorphism and we store it in the `nat_cast : ℕ → R` field.

Preferentially, the homomorphism is written as a coercion.

## Main declarations

* `add_monoid_with_one`: Type class for `nat.cast`.
* `nat.cast`: Canonical homomorphism `ℕ → R`.
-/

universes u
set_option old_structure_cmd true

/-- The numeral `((0+1)+⋯)+1`. -/
protected def nat.unary_cast {R : Type u} [has_one R] [has_zero R] [has_add R] : ℕ → R
| 0 := 0
| (n + 1) := nat.unary_cast n + 1

/--
Type class for the canonical homomorphism `ℕ → R`.
-/
@[protect_proj]
class has_nat_cast (R : Type u) :=
(nat_cast : ℕ → R)

/--
An `add_monoid_with_one` is an `add_monoid` with a `1`.
It also contains data for the unique homomorphism `ℕ → R`.
-/
@[protect_proj, ancestor has_nat_cast add_monoid has_one]
class add_monoid_with_one (R : Type u) extends has_nat_cast R, add_monoid R, has_one R :=
(nat_cast := nat.unary_cast)
(nat_cast_zero : nat_cast 0 = (0 : R) . control_laws_tac)
(nat_cast_succ : ∀ n, nat_cast (n + 1) = (nat_cast n + 1 : R) . control_laws_tac)

/-- Canonical homomorphism from `ℕ` to a additive monoid `R` with a `1`. -/
protected def nat.cast {R : Type u} [has_nat_cast R] : ℕ → R := has_nat_cast.nat_cast

/-- An `add_comm_monoid_with_one` is an `add_monoid_with_one` satisfying `a + b = b + a`.  -/
@[protect_proj, ancestor add_monoid_with_one add_comm_monoid]
class add_comm_monoid_with_one (R : Type*) extends add_monoid_with_one R, add_comm_monoid R

section
variables {R : Type*} [add_monoid_with_one R]

/--
Coercions such as `nat.cast_coe` that go from a concrete structure such as
`ℕ` to an arbitrary ring `R` should be set up as follows:
```lean
@[priority 900] instance : has_coe_t ℕ R := ⟨...⟩
```

It needs to be `has_coe_t` instead of `has_coe` because otherwise type-class
inference would loop when constructing the transitive coercion `ℕ → ℕ → ℕ → ...`.
The reduced priority is necessary so that it doesn't conflict with instances
such as `has_coe_t R (option R)`.

For this to work, we reduce the priority of the `coe_base` and `coe_trans`
instances because we want the instances for `has_coe_t` to be tried in the
following order:

 1. `has_coe_t` instances declared in mathlib (such as `has_coe_t R (with_top R)`, etc.)
 2. `coe_base`, which contains instances such as `has_coe (fin n) n`
 3. `nat.cast_coe : has_coe_t ℕ R` etc.
 4. `coe_trans`

If `coe_trans` is tried first, then `nat.cast_coe` doesn't get a chance to apply.
-/
library_note "coercion into rings"
attribute [instance, priority 950] coe_base
attribute [instance, priority 500] coe_trans

namespace nat

-- see note [coercion into rings]
@[priority 900] instance cast_coe {R} [has_nat_cast R] : has_coe_t ℕ R := ⟨nat.cast⟩

@[simp, norm_cast]

-- Lemmas about nat.succ need to get a low priority, so that they are tried last.
-- This is because `nat.succ _` matches `1`, `3`, `x+1`, etc.
-- Rewriting would then produce really wrong terms.
@[simp, norm_cast, priority 500]
theorem cast_succ (n : ℕ) : ((succ n : ℕ) : R) = n + 1 := add_monoid_with_one.nat_cast_succ _

theorem cast_add_one (n : ℕ) : ((n + 1 : ℕ) : R) = n + 1 := cast_succ _

@[simp, norm_cast] theorem cast_ite (P : Prop) [decidable P] (m n : ℕ) :
  (((ite P m n) : ℕ) : R) = ite P (m : R) (n : R) :=
by { split_ifs; refl, }

end nat

end

namespace nat
variables {R : Type*}

@[simp, norm_cast] theorem cast_one [add_monoid_with_one R] : ((1 : ℕ) : R) = 1 :=
by rw [cast_succ, cast_zero, zero_add]

@[simp, norm_cast] theorem cast_add [add_monoid_with_one R] (m n : ℕ) : ((m + n : ℕ) : R) = m + n :=
by induction n; simp [add_succ, add_assoc, nat.add_zero, *]

/-- Computationally friendlier cast than `nat.unary_cast`, using binary representation. -/
protected def bin_cast [has_zero R] [has_one R] [has_add R] (n : ℕ) : R :=
@nat.binary_rec (λ _, R) 0 (λ odd k a, cond odd (a + a + 1) (a + a)) n

@[simp] lemma bin_cast_eq [add_monoid_with_one R] (n : ℕ) : (nat.bin_cast n : R) = ((n : ℕ) : R) :=
begin
  rw nat.bin_cast,
  apply binary_rec _ _ n,
  { rw [binary_rec_zero, cast_zero] },
  { intros b k h,
    rw [binary_rec_eq, h],
    { cases b; simp [bit, bit0, bit1] },
    { simp } },
end

@[simp, norm_cast] theorem cast_bit0 [add_monoid_with_one R] (n : ℕ) :
  ((bit0 n : ℕ) : R) = bit0 n := cast_add _ _

@[simp, norm_cast] theorem cast_bit1 [add_monoid_with_one R] (n : ℕ) :
  ((bit1 n : ℕ) : R) = bit1 n :=
by rw [bit1, cast_add_one, cast_bit0]; refl

lemma cast_two [add_monoid_with_one R] : ((2 : ℕ) : R) = 2 :=
by rw [cast_add_one, cast_one, bit0]

attribute [simp, norm_cast] int.nat_abs_of_nat

end nat

/-- `add_monoid_with_one` implementation using unary recursion. -/
@[reducible] protected def add_monoid_with_one.unary {R : Type*} [add_monoid R] [has_one R] :
  add_monoid_with_one R :=
{ .. ‹has_one R›, .. ‹add_monoid R› }

/-- `add_monoid_with_one` implementation using binary recursion. -/
@[reducible] protected def add_monoid_with_one.binary {R : Type*} [add_monoid R] [has_one R] :
  add_monoid_with_one R :=
{ nat_cast := nat.bin_cast,
  nat_cast_zero := by simp [nat.bin_cast, nat.cast],
  nat_cast_succ := λ n, begin
    simp only [nat.cast],
    letI : add_monoid_with_one R := add_monoid_with_one.unary,
    erw [nat.bin_cast_eq, nat.bin_cast_eq, nat.cast_succ],
    refl,
  end,
  .. ‹has_one R›, .. ‹add_monoid R› }

namespace ne_zero

lemma nat_cast_ne (n : ℕ) (R) [add_monoid_with_one R] [h : ne_zero (n : R)] :
  (n : R) ≠ 0 := h.out

lemma of_ne_zero_coe (R) [add_monoid_with_one R] {n : ℕ} [h : ne_zero (n : R)] : ne_zero n :=
⟨by {casesI h, rintro rfl, by simpa using h}⟩

lemma pos_of_ne_zero_coe (R) [add_monoid_with_one R] {n : ℕ} [ne_zero (n : R)] : 0 < n :=
nat.pos_of_ne_zero (of_ne_zero_coe R).out

end ne_zero


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

! This file was ported from Lean 3 source module category_theory.limits.shapes.regular_mono
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Shapes.Pullbacks
import Mathbin.CategoryTheory.Limits.Shapes.StrongEpi
import Mathbin.CategoryTheory.Limits.Shapes.Equalizers

/-!
# Definitions and basic properties of regular monomorphisms and epimorphisms.

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

A regular monomorphism is a morphism that is the equalizer of some parallel pair.

We give the constructions
* `is_split_mono → regular_mono` and
* `regular_mono → mono`
as well as the dual constructions for regular epimorphisms. Additionally, we give the construction
* `regular_epi ⟶ strong_epi`.

We also define classes `regular_mono_category` and `regular_epi_category` for categories in which
every monomorphism or epimorphism is regular, and deduce that these categories are
`strong_mono_category`s resp. `strong_epi_category`s.

-/

noncomputable section

namespace CategoryTheory

open CategoryTheory.Limits

universe v₁ u₁ u₂

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

variable {X Y : C}

#print CategoryTheory.RegularMono /-
/-- A regular monomorphism is a morphism which is the equalizer of some parallel pair. -/
class RegularMono (f : X ⟶ Y) where
  z : C
  (left right : Y ⟶ Z)
  w : f ≫ left = f ≫ right
  IsLimit : IsLimit (Fork.ofι f w)
#align category_theory.regular_mono CategoryTheory.RegularMono
-/

attribute [reassoc.1] regular_mono.w

#print CategoryTheory.RegularMono.mono /-
/-- Every regular monomorphism is a monomorphism. -/
instance (priority := 100) RegularMono.mono (f : X ⟶ Y) [RegularMono f] : Mono f :=
  mono_of_isLimit_fork RegularMono.isLimit
#align category_theory.regular_mono.mono CategoryTheory.RegularMono.mono
-/

#print CategoryTheory.equalizerRegular /-
instance equalizerRegular (g h : X ⟶ Y) [HasLimit (parallelPair g h)] :
    RegularMono (equalizer.ι g h) where
  z := Y
  left := g
  right := h
  w := equalizer.condition g h
  IsLimit :=
    Fork.IsLimit.mk _ (fun s => limit.lift _ s) (by simp) fun s m w =>
      by
      ext1
      simp [← w]
#align category_theory.equalizer_regular CategoryTheory.equalizerRegular
-/

#print CategoryTheory.RegularMono.ofIsSplitMono /-
/-- Every split monomorphism is a regular monomorphism. -/
instance (priority := 100) RegularMono.ofIsSplitMono (f : X ⟶ Y) [IsSplitMono f] : RegularMono f
    where
  z := Y
  left := 𝟙 Y
  right := retraction f ≫ f
  w := by tidy
  IsLimit := isSplitMonoEqualizes f
#align category_theory.regular_mono.of_is_split_mono CategoryTheory.RegularMono.ofIsSplitMono
-/

#print CategoryTheory.RegularMono.lift' /-
/-- If `f` is a regular mono, then any map `k : W ⟶ Y` equalizing `regular_mono.left` and
    `regular_mono.right` induces a morphism `l : W ⟶ X` such that `l ≫ f = k`. -/
def RegularMono.lift' {W : C} (f : X ⟶ Y) [RegularMono f] (k : W ⟶ Y)
    (h : k ≫ (RegularMono.left : Y ⟶ @RegularMono.z _ _ _ _ f _) = k ≫ RegularMono.right) :
    { l : W ⟶ X // l ≫ f = k } :=
  Fork.IsLimit.lift' RegularMono.isLimit _ h
#align category_theory.regular_mono.lift' CategoryTheory.RegularMono.lift'
-/

#print CategoryTheory.regularOfIsPullbackSndOfRegular /-
/-- The second leg of a pullback cone is a regular monomorphism if the right component is too.

See also `pullback.snd_of_mono` for the basic monomorphism version, and
`regular_of_is_pullback_fst_of_regular` for the flipped version.
-/
def regularOfIsPullbackSndOfRegular {P Q R S : C} {f : P ⟶ Q} {g : P ⟶ R} {h : Q ⟶ S} {k : R ⟶ S}
    [hr : RegularMono h] (comm : f ≫ h = g ≫ k) (t : IsLimit (PullbackCone.mk _ _ comm)) :
    RegularMono g where
  z := hr.z
  left := k ≫ hr.left
  right := k ≫ hr.right
  w := by rw [← reassoc_of comm, ← reassoc_of comm, hr.w]
  IsLimit := by
    apply fork.is_limit.mk' _ _
    intro s
    have l₁ : (fork.ι s ≫ k) ≫ regular_mono.left = (fork.ι s ≫ k) ≫ regular_mono.right
    rw [category.assoc, s.condition, category.assoc]
    obtain ⟨l, hl⟩ := fork.is_limit.lift' hr.is_limit _ l₁
    obtain ⟨p, hp₁, hp₂⟩ := pullback_cone.is_limit.lift' t _ _ hl
    refine' ⟨p, hp₂, _⟩
    intro m w
    have z : m ≫ g = p ≫ g := w.trans hp₂.symm
    apply t.hom_ext
    apply (pullback_cone.mk f g comm).equalizer_ext
    · erw [← cancel_mono h, category.assoc, category.assoc, comm, reassoc_of z]
    · exact z
#align category_theory.regular_of_is_pullback_snd_of_regular CategoryTheory.regularOfIsPullbackSndOfRegular
-/

#print CategoryTheory.regularOfIsPullbackFstOfRegular /-
/-- The first leg of a pullback cone is a regular monomorphism if the left component is too.

See also `pullback.fst_of_mono` for the basic monomorphism version, and
`regular_of_is_pullback_snd_of_regular` for the flipped version.
-/
def regularOfIsPullbackFstOfRegular {P Q R S : C} {f : P ⟶ Q} {g : P ⟶ R} {h : Q ⟶ S} {k : R ⟶ S}
    [hr : RegularMono k] (comm : f ≫ h = g ≫ k) (t : IsLimit (PullbackCone.mk _ _ comm)) :
    RegularMono f :=
  regularOfIsPullbackSndOfRegular comm.symm (PullbackCone.flipIsLimit t)
#align category_theory.regular_of_is_pullback_fst_of_regular CategoryTheory.regularOfIsPullbackFstOfRegular
-/

#print CategoryTheory.strongMono_of_regularMono /-
instance (priority := 100) strongMono_of_regularMono (f : X ⟶ Y) [RegularMono f] : StrongMono f :=
  StrongMono.mk'
    (by
      intro A B z hz u v sq
      have : v ≫ (regular_mono.left : Y ⟶ regular_mono.Z f) = v ≫ regular_mono.right :=
        by
        apply (cancel_epi z).1
        simp only [regular_mono.w, ← reassoc_of sq.w]
      obtain ⟨t, ht⟩ := regular_mono.lift' _ _ this
      refine' comm_sq.has_lift.mk' ⟨t, (cancel_mono f).1 _, ht⟩
      simp only [arrow.mk_hom, arrow.hom_mk'_left, category.assoc, ht, sq.w])
#align category_theory.strong_mono_of_regular_mono CategoryTheory.strongMono_of_regularMono
-/

#print CategoryTheory.isIso_of_regularMono_of_epi /-
/-- A regular monomorphism is an isomorphism if it is an epimorphism. -/
theorem isIso_of_regularMono_of_epi (f : X ⟶ Y) [RegularMono f] [e : Epi f] : IsIso f :=
  isIso_of_epi_of_strongMono _
#align category_theory.is_iso_of_regular_mono_of_epi CategoryTheory.isIso_of_regularMono_of_epi
-/

section

variable (C)

#print CategoryTheory.RegularMonoCategory /-
/-- A regular mono category is a category in which every monomorphism is regular. -/
class RegularMonoCategory where
  regularMonoOfMono : ∀ {X Y : C} (f : X ⟶ Y) [Mono f], RegularMono f
#align category_theory.regular_mono_category CategoryTheory.RegularMonoCategory
-/

end

#print CategoryTheory.regularMonoOfMono /-
/-- In a category in which every monomorphism is regular, we can express every monomorphism as
    an equalizer. This is not an instance because it would create an instance loop. -/
def regularMonoOfMono [RegularMonoCategory C] (f : X ⟶ Y) [Mono f] : RegularMono f :=
  RegularMonoCategory.regularMonoOfMono _
#align category_theory.regular_mono_of_mono CategoryTheory.regularMonoOfMono
-/

#print CategoryTheory.regularMonoCategoryOfSplitMonoCategory /-
instance (priority := 100) regularMonoCategoryOfSplitMonoCategory [SplitMonoCategory C] :
    RegularMonoCategory C
    where regularMonoOfMono _ _ f _ :=
    by
    haveI := is_split_mono_of_mono f
    infer_instance
#align category_theory.regular_mono_category_of_split_mono_category CategoryTheory.regularMonoCategoryOfSplitMonoCategory
-/

#print CategoryTheory.strongMonoCategory_of_regularMonoCategory /-
instance (priority := 100) strongMonoCategory_of_regularMonoCategory [RegularMonoCategory C] :
    StrongMonoCategory C
    where strongMono_of_mono _ _ f _ :=
    by
    haveI := regular_mono_of_mono f
    infer_instance
#align category_theory.strong_mono_category_of_regular_mono_category CategoryTheory.strongMonoCategory_of_regularMonoCategory
-/

#print CategoryTheory.RegularEpi /-
/-- A regular epimorphism is a morphism which is the coequalizer of some parallel pair. -/
class RegularEpi (f : X ⟶ Y) where
  w : C
  (left right : W ⟶ X)
  w : left ≫ f = right ≫ f
  IsColimit : IsColimit (Cofork.ofπ f w)
#align category_theory.regular_epi CategoryTheory.RegularEpi
-/

attribute [reassoc.1] regular_epi.w

#print CategoryTheory.RegularEpi.epi /-
/-- Every regular epimorphism is an epimorphism. -/
instance (priority := 100) RegularEpi.epi (f : X ⟶ Y) [RegularEpi f] : Epi f :=
  epi_of_isColimit_cofork RegularEpi.isColimit
#align category_theory.regular_epi.epi CategoryTheory.RegularEpi.epi
-/

#print CategoryTheory.coequalizerRegular /-
instance coequalizerRegular (g h : X ⟶ Y) [HasColimit (parallelPair g h)] :
    RegularEpi (coequalizer.π g h) where
  w := X
  left := g
  right := h
  w := coequalizer.condition g h
  IsColimit :=
    Cofork.IsColimit.mk _ (fun s => colimit.desc _ s) (by simp) fun s m w =>
      by
      ext1
      simp [← w]
#align category_theory.coequalizer_regular CategoryTheory.coequalizerRegular
-/

#print CategoryTheory.RegularEpi.ofSplitEpi /-
/-- Every split epimorphism is a regular epimorphism. -/
instance (priority := 100) RegularEpi.ofSplitEpi (f : X ⟶ Y) [IsSplitEpi f] : RegularEpi f
    where
  w := X
  left := 𝟙 X
  right := f ≫ section_ f
  w := by tidy
  IsColimit := isSplitEpiCoequalizes f
#align category_theory.regular_epi.of_split_epi CategoryTheory.RegularEpi.ofSplitEpi
-/

/- warning: category_theory.regular_epi.desc' -> CategoryTheory.RegularEpi.desc' is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {X : C} {Y : C} {W : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) [_inst_2 : CategoryTheory.RegularEpi.{u1, u2} C _inst_1 X Y f] (k : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X W), (Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.RegularEpi.w.{u1, u2} C _inst_1 X Y f _inst_2) W) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) (CategoryTheory.RegularEpi.w.{u1, u2} C _inst_1 X Y f _inst_2) X W (CategoryTheory.RegularEpi.left.{u1, u2} C _inst_1 X Y f _inst_2) k) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) (CategoryTheory.RegularEpi.w.{u1, u2} C _inst_1 X Y f _inst_2) X W (CategoryTheory.RegularEpi.right.{u1, u2} C _inst_1 X Y f _inst_2) k)) -> (Subtype.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y W) (fun (l : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y W) => Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X W) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y W f l) k))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] {X : C} {Y : C} {W : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) [_inst_2 : CategoryTheory.RegularEpi.{u1, u2} C _inst_1 X Y f] (k : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X W), (Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) (CategoryTheory.RegularEpi.W.{u1, u2} C _inst_1 X Y f _inst_2) W) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) (CategoryTheory.RegularEpi.W.{u1, u2} C _inst_1 X Y f _inst_2) X W (CategoryTheory.RegularEpi.left.{u1, u2} C _inst_1 X Y f _inst_2) k) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) (CategoryTheory.RegularEpi.W.{u1, u2} C _inst_1 X Y f _inst_2) X W (CategoryTheory.RegularEpi.right.{u1, u2} C _inst_1 X Y f _inst_2) k)) -> (Subtype.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y W) (fun (l : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y W) => Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X W) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y W f l) k))
Case conversion may be inaccurate. Consider using '#align category_theory.regular_epi.desc' CategoryTheory.RegularEpi.desc'ₓ'. -/
/-- If `f` is a regular epi, then every morphism `k : X ⟶ W` coequalizing `regular_epi.left` and
    `regular_epi.right` induces `l : Y ⟶ W` such that `f ≫ l = k`. -/
def RegularEpi.desc' {W : C} (f : X ⟶ Y) [RegularEpi f] (k : X ⟶ W)
    (h : (RegularEpi.left : RegularEpi.w f ⟶ X) ≫ k = RegularEpi.right ≫ k) :
    { l : Y ⟶ W // f ≫ l = k } :=
  Cofork.IsColimit.desc' RegularEpi.isColimit _ h
#align category_theory.regular_epi.desc' CategoryTheory.RegularEpi.desc'

#print CategoryTheory.regularOfIsPushoutSndOfRegular /-
/-- The second leg of a pushout cocone is a regular epimorphism if the right component is too.

See also `pushout.snd_of_epi` for the basic epimorphism version, and
`regular_of_is_pushout_fst_of_regular` for the flipped version.
-/
def regularOfIsPushoutSndOfRegular {P Q R S : C} {f : P ⟶ Q} {g : P ⟶ R} {h : Q ⟶ S} {k : R ⟶ S}
    [gr : RegularEpi g] (comm : f ≫ h = g ≫ k) (t : IsColimit (PushoutCocone.mk _ _ comm)) :
    RegularEpi h where
  w := gr.w
  left := gr.left ≫ f
  right := gr.right ≫ f
  w := by rw [category.assoc, category.assoc, comm, reassoc_of gr.w]
  IsColimit := by
    apply cofork.is_colimit.mk' _ _
    intro s
    have l₁ : gr.left ≫ f ≫ s.π = gr.right ≫ f ≫ s.π
    rw [← category.assoc, ← category.assoc, s.condition]
    obtain ⟨l, hl⟩ := cofork.is_colimit.desc' gr.is_colimit (f ≫ cofork.π s) l₁
    obtain ⟨p, hp₁, hp₂⟩ := pushout_cocone.is_colimit.desc' t _ _ hl.symm
    refine' ⟨p, hp₁, _⟩
    intro m w
    have z := w.trans hp₁.symm
    apply t.hom_ext
    apply (pushout_cocone.mk _ _ comm).coequalizer_ext
    · exact z
    · erw [← cancel_epi g, ← reassoc_of comm, ← reassoc_of comm, z]
      rfl
#align category_theory.regular_of_is_pushout_snd_of_regular CategoryTheory.regularOfIsPushoutSndOfRegular
-/

#print CategoryTheory.regularOfIsPushoutFstOfRegular /-
/-- The first leg of a pushout cocone is a regular epimorphism if the left component is too.

See also `pushout.fst_of_epi` for the basic epimorphism version, and
`regular_of_is_pushout_snd_of_regular` for the flipped version.
-/
def regularOfIsPushoutFstOfRegular {P Q R S : C} {f : P ⟶ Q} {g : P ⟶ R} {h : Q ⟶ S} {k : R ⟶ S}
    [fr : RegularEpi f] (comm : f ≫ h = g ≫ k) (t : IsColimit (PushoutCocone.mk _ _ comm)) :
    RegularEpi k :=
  regularOfIsPushoutSndOfRegular comm.symm (PushoutCocone.flipIsColimit t)
#align category_theory.regular_of_is_pushout_fst_of_regular CategoryTheory.regularOfIsPushoutFstOfRegular
-/

#print CategoryTheory.strongEpi_of_regularEpi /-
instance (priority := 100) strongEpi_of_regularEpi (f : X ⟶ Y) [RegularEpi f] : StrongEpi f :=
  StrongEpi.mk'
    (by
      intro A B z hz u v sq
      have : (regular_epi.left : regular_epi.W f ⟶ X) ≫ u = regular_epi.right ≫ u :=
        by
        apply (cancel_mono z).1
        simp only [category.assoc, sq.w, regular_epi.w_assoc]
      obtain ⟨t, ht⟩ := regular_epi.desc' f u this
      exact
        comm_sq.has_lift.mk'
          ⟨t, ht,
            (cancel_epi f).1
              (by simp only [← category.assoc, ht, ← sq.w, arrow.mk_hom, arrow.hom_mk'_right])⟩)
#align category_theory.strong_epi_of_regular_epi CategoryTheory.strongEpi_of_regularEpi
-/

#print CategoryTheory.isIso_of_regularEpi_of_mono /-
/-- A regular epimorphism is an isomorphism if it is a monomorphism. -/
theorem isIso_of_regularEpi_of_mono (f : X ⟶ Y) [RegularEpi f] [m : Mono f] : IsIso f :=
  isIso_of_mono_of_strongEpi _
#align category_theory.is_iso_of_regular_epi_of_mono CategoryTheory.isIso_of_regularEpi_of_mono
-/

section

variable (C)

#print CategoryTheory.RegularEpiCategory /-
/-- A regular epi category is a category in which every epimorphism is regular. -/
class RegularEpiCategory where
  regularEpiOfEpi : ∀ {X Y : C} (f : X ⟶ Y) [Epi f], RegularEpi f
#align category_theory.regular_epi_category CategoryTheory.RegularEpiCategory
-/

end

#print CategoryTheory.regularEpiOfEpi /-
/-- In a category in which every epimorphism is regular, we can express every epimorphism as
    a coequalizer. This is not an instance because it would create an instance loop. -/
def regularEpiOfEpi [RegularEpiCategory C] (f : X ⟶ Y) [Epi f] : RegularEpi f :=
  RegularEpiCategory.regularEpiOfEpi _
#align category_theory.regular_epi_of_epi CategoryTheory.regularEpiOfEpi
-/

#print CategoryTheory.regularEpiCategoryOfSplitEpiCategory /-
instance (priority := 100) regularEpiCategoryOfSplitEpiCategory [SplitEpiCategory C] :
    RegularEpiCategory C
    where regularEpiOfEpi _ _ f _ :=
    by
    haveI := is_split_epi_of_epi f
    infer_instance
#align category_theory.regular_epi_category_of_split_epi_category CategoryTheory.regularEpiCategoryOfSplitEpiCategory
-/

#print CategoryTheory.strongEpiCategory_of_regularEpiCategory /-
instance (priority := 100) strongEpiCategory_of_regularEpiCategory [RegularEpiCategory C] :
    StrongEpiCategory C
    where strongEpi_of_epi _ _ f _ :=
    by
    haveI := regular_epi_of_epi f
    infer_instance
#align category_theory.strong_epi_category_of_regular_epi_category CategoryTheory.strongEpiCategory_of_regularEpiCategory
-/

end CategoryTheory


/-
Copyright (c) 2021 Adam Topaz. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Adam Topaz
-/
import algebraic_topology.simplex_category
import topology.category.Top.basic
import topology.instances.nnreal

/-!
# Topological simplices

We define the natural functor from `simplex_category` to `Top` sending `[n]` to the
topological `n`-simplex.
This is used to define `Top.to_sSet` in `algebraic_topology.simpliciaL_set`.
-/

noncomputable theory

namespace simplex_category

open_locale simplicial nnreal big_operators classical

local attribute [instance]
  category_theory.concrete_category.has_coe_to_sort
  category_theory.concrete_category.has_coe_to_fun

/-- The topological simplex associated to `x : simplex_category`.
  This is the object part of the functor `simplex_category.to_Top`. -/
def to_Top_obj (x : simplex_category) := { f : x → ℝ≥0 | ∑ i, f i = 1 }

instance (x : simplex_category) : has_coe_to_fun x.to_Top_obj (λ _, x → ℝ≥0) :=
⟨λ f, (f : x → ℝ≥0)⟩

@[ext]
lemma to_Top_obj.ext {x : simplex_category} (f g : x.to_Top_obj) :
  (f : x → ℝ≥0) = g → f = g := subtype.ext

/-- A morphism in `simplex_category` induces a map on the associated topological spaces. -/
def to_Top_map {x y : simplex_category} (f : x ⟶ y) : x.to_Top_obj → y.to_Top_obj :=
λ g, ⟨λ i, ∑ j in (finset.univ.filter (λ k, f k = i)), g j,
begin
  dsimp [to_Top_obj],
  simp only [finset.filter_congr_decidable, finset.sum_congr],
  rw ← finset.sum_bUnion,
  convert g.2,
  { rw finset.eq_univ_iff_forall,
    intros i,
    rw finset.mem_bUnion,
    exact ⟨f i, by simp, by simp⟩ },
  { intros i hi j hj h e he,
    apply h,
    simp only [true_and, finset.inf_eq_inter,
      finset.mem_univ, finset.mem_filter, finset.mem_inter] at he,
    rw [← he.1, ← he.2] }
end⟩

@[simp]
lemma coe_to_Top_map {x y : simplex_category} (f : x ⟶ y) (g : x.to_Top_obj) (i : y) :
  to_Top_map f g i = ∑ j in (finset.univ.filter (λ k, f k = i)), g j := rfl

@[continuity]
lemma continuous_to_Top_map {x y : simplex_category} (f : x ⟶ y) :
  continuous (to_Top_map f) :=
continuous_subtype_mk _ $ continuous_pi $ λ i, continuous_finset_sum _ $
  λ j hj, continuous.comp (continuous_apply _) continuous_subtype_val

/-- The functor associating the topological `n`-simplex to `[n] : simplex_category`. -/
@[simps]
def to_Top : simplex_category ⥤ Top :=
{ obj := λ x, Top.of x.to_Top_obj,
  map := λ x y f, ⟨to_Top_map f⟩,
  map_id' := begin
    intros x,
    ext f i : 3,
    change (finset.univ.filter (λ k, k = i)).sum _ = _,
    simp [finset.sum_filter]
  end,
  map_comp' := begin
    intros x y z f g,
    ext h i : 3,
    dsimp,
    erw ← finset.sum_bUnion,
    apply finset.sum_congr,
    { exact finset.ext (λ j, ⟨λ hj, by simpa using hj, λ hj, by simpa using hj⟩) },
    { tauto },
    { intros j hj k hk h e he,
      apply h,
      simp only [true_and, finset.inf_eq_inter,
        finset.mem_univ, finset.mem_filter, finset.mem_inter] at he,
      rw [← he.1, ← he.2] },
  end }

end simplex_category