/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Floris van Doorn

! This file was ported from Lean 3 source module geometry.manifold.cont_mdiff_mfderiv
! leanprover-community/mathlib commit 0187644979f2d3e10a06e916a869c994facd9a87
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Geometry.Manifold.Mfderiv

/-!
### Interactions between differentiability, smoothness and manifold derivatives

We give the relation between `mdifferentiable`, `cont_mdiff`, `mfderiv`, `tangent_map`
and related notions.

## Main statements

* `cont_mdiff_on.cont_mdiff_on_tangent_map_within` states that the bundled derivative
  of a `Cⁿ` function in a domain is `Cᵐ` when `m + 1 ≤ n`.
* `cont_mdiff.cont_mdiff_tangent_map` states that the bundled derivative
  of a `Cⁿ` function is `Cᵐ` when `m + 1 ≤ n`.
-/

open Set Function Filter ChartedSpace SmoothManifoldWithCorners Bundle

open Topology Manifold Bundle

/-! ### Definition of smooth functions between manifolds -/

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜]
  -- declare a smooth manifold `M` over the pair `(E, H)`.
  {E : Type _}
  [NormedAddCommGroup E] [NormedSpace 𝕜 E] {H : Type _} [TopologicalSpace H]
  {I : ModelWithCorners 𝕜 E H} {M : Type _} [TopologicalSpace M] [ChartedSpace H M]
  [Is : SmoothManifoldWithCorners I M]
  -- declare a smooth manifold `M'` over the pair `(E', H')`.
  {E' : Type _}
  [NormedAddCommGroup E'] [NormedSpace 𝕜 E'] {H' : Type _} [TopologicalSpace H']
  {I' : ModelWithCorners 𝕜 E' H'} {M' : Type _} [TopologicalSpace M'] [ChartedSpace H' M']
  [I's : SmoothManifoldWithCorners I' M']
  -- declare a smooth manifold `N` over the pair `(F, G)`.
  {F : Type _}
  [NormedAddCommGroup F] [NormedSpace 𝕜 F] {G : Type _} [TopologicalSpace G]
  {J : ModelWithCorners 𝕜 F G} {N : Type _} [TopologicalSpace N] [ChartedSpace G N]
  [Js : SmoothManifoldWithCorners J N]
  -- declare a smooth manifold `N'` over the pair `(F', G')`.
  {F' : Type _}
  [NormedAddCommGroup F'] [NormedSpace 𝕜 F'] {G' : Type _} [TopologicalSpace G']
  {J' : ModelWithCorners 𝕜 F' G'} {N' : Type _} [TopologicalSpace N'] [ChartedSpace G' N']
  [J's : SmoothManifoldWithCorners J' N']
  -- declare some additional normed spaces, used for fibers of vector bundles
  {F₁ : Type _}
  [NormedAddCommGroup F₁] [NormedSpace 𝕜 F₁] {F₂ : Type _} [NormedAddCommGroup F₂]
  [NormedSpace 𝕜 F₂]
  -- declare functions, sets, points and smoothness indices
  {f f₁ : M → M'}
  {s s₁ t : Set M} {x : M} {m n : ℕ∞}

/-! ### Deducing differentiability from smoothness -/

theorem ContMdiffWithinAt.mdifferentiableWithinAt (hf : ContMdiffWithinAt I I' n f s x)
    (hn : 1 ≤ n) : MdifferentiableWithinAt I I' f s x :=
  by
  suffices h : MdifferentiableWithinAt I I' f (s ∩ f ⁻¹' (extChartAt I' (f x)).source) x
  · rwa [mdifferentiableWithinAt_inter'] at h
    apply hf.1.preimage_mem_nhdsWithin
    exact extChartAt_source_mem_nhds I' (f x)
  rw [mdifferentiableWithinAt_iff]
  exact ⟨hf.1.mono (inter_subset_left _ _), (hf.2.DifferentiableWithinAt hn).mono (by mfld_set_tac)⟩
#align cont_mdiff_within_at.mdifferentiable_within_at ContMdiffWithinAt.mdifferentiableWithinAt

theorem ContMdiffAt.mdifferentiableAt (hf : ContMdiffAt I I' n f x) (hn : 1 ≤ n) :
    MdifferentiableAt I I' f x :=
  mdifferentiableWithinAt_univ.1 <| ContMdiffWithinAt.mdifferentiableWithinAt hf hn
#align cont_mdiff_at.mdifferentiable_at ContMdiffAt.mdifferentiableAt

theorem ContMdiffOn.mdifferentiableOn (hf : ContMdiffOn I I' n f s) (hn : 1 ≤ n) :
    MdifferentiableOn I I' f s := fun x hx => (hf x hx).MdifferentiableWithinAt hn
#align cont_mdiff_on.mdifferentiable_on ContMdiffOn.mdifferentiableOn

theorem ContMdiff.mdifferentiable (hf : ContMdiff I I' n f) (hn : 1 ≤ n) : Mdifferentiable I I' f :=
  fun x => (hf x).MdifferentiableAt hn
#align cont_mdiff.mdifferentiable ContMdiff.mdifferentiable

theorem SmoothWithinAt.mdifferentiableWithinAt (hf : SmoothWithinAt I I' f s x) :
    MdifferentiableWithinAt I I' f s x :=
  hf.MdifferentiableWithinAt le_top
#align smooth_within_at.mdifferentiable_within_at SmoothWithinAt.mdifferentiableWithinAt

theorem SmoothAt.mdifferentiableAt (hf : SmoothAt I I' f x) : MdifferentiableAt I I' f x :=
  hf.MdifferentiableAt le_top
#align smooth_at.mdifferentiable_at SmoothAt.mdifferentiableAt

theorem SmoothOn.mdifferentiableOn (hf : SmoothOn I I' f s) : MdifferentiableOn I I' f s :=
  hf.MdifferentiableOn le_top
#align smooth_on.mdifferentiable_on SmoothOn.mdifferentiableOn

theorem Smooth.mdifferentiable (hf : Smooth I I' f) : Mdifferentiable I I' f :=
  ContMdiff.mdifferentiable hf le_top
#align smooth.mdifferentiable Smooth.mdifferentiable

theorem Smooth.mdifferentiableAt (hf : Smooth I I' f) : MdifferentiableAt I I' f x :=
  hf.Mdifferentiable x
#align smooth.mdifferentiable_at Smooth.mdifferentiableAt

theorem Smooth.mdifferentiableWithinAt (hf : Smooth I I' f) : MdifferentiableWithinAt I I' f s x :=
  hf.MdifferentiableAt.MdifferentiableWithinAt
#align smooth.mdifferentiable_within_at Smooth.mdifferentiableWithinAt

/-! ### The tangent map of a smooth function is smooth -/

section tangentMap

/-- If a function is `C^n` with `1 ≤ n` on a domain with unique derivatives, then its bundled
derivative is continuous. In this auxiliary lemma, we prove this fact when the source and target
space are model spaces in models with corners. The general fact is proved in
`cont_mdiff_on.continuous_on_tangent_map_within`-/
theorem ContMdiffOn.continuousOn_tangentMapWithin_aux {f : H → H'} {s : Set H}
    (hf : ContMdiffOn I I' n f s) (hn : 1 ≤ n) (hs : UniqueMdiffOn I s) :
    ContinuousOn (tangentMapWithin I I' f s) (π (TangentSpace I) ⁻¹' s) :=
  by
  suffices h :
    ContinuousOn
      (fun p : H × E =>
        (f p.fst,
          (fderivWithin 𝕜 (writtenInExtChartAt I I' p.fst f) (I.symm ⁻¹' s ∩ range I)
                ((extChartAt I p.fst) p.fst) :
              E →L[𝕜] E')
            p.snd))
      (Prod.fst ⁻¹' s)
  · have A := (tangentBundleModelSpaceHomeomorph H I).Continuous
    rw [continuous_iff_continuousOn_univ] at A
    have B :=
      ((tangentBundleModelSpaceHomeomorph H' I').symm.Continuous.comp_continuousOn h).comp' A
    have :
      univ ∩ ⇑(tangentBundleModelSpaceHomeomorph H I) ⁻¹' (Prod.fst ⁻¹' s) =
        π (TangentSpace I) ⁻¹' s :=
      by
      ext ⟨x, v⟩
      simp only [mfld_simps]
    rw [this] at B
    apply B.congr
    rintro ⟨x, v⟩ hx
    dsimp [tangentMapWithin]
    ext
    · rfl
    simp only [mfld_simps]
    apply congr_fun
    apply congr_arg
    rw [MdifferentiableWithinAt.mfderivWithin (hf.mdifferentiable_on hn x hx)]
    rfl
  suffices h :
    ContinuousOn
      (fun p : H × E =>
        (fderivWithin 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) (I p.fst) : E →L[𝕜] E') p.snd)
      (Prod.fst ⁻¹' s)
  · dsimp [writtenInExtChartAt, extChartAt]
    apply
      ContinuousOn.prod
        (ContinuousOn.comp hf.continuous_on continuous_fst.continuous_on (subset.refl _))
    apply h.congr
    intro p hp
    rfl
  suffices h : ContinuousOn (fderivWithin 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I)) (I '' s)
  · have C := ContinuousOn.comp h I.continuous_to_fun.continuous_on (subset.refl _)
    have A : Continuous fun q : (E →L[𝕜] E') × E => q.1 q.2 :=
      is_bounded_bilinear_map_apply.continuous
    have B :
      ContinuousOn
        (fun p : H × E => (fderivWithin 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) (I p.1), p.2))
        (Prod.fst ⁻¹' s) :=
      by
      apply ContinuousOn.prod _ continuous_snd.continuous_on
      refine' (ContinuousOn.comp C continuous_fst.continuous_on _ : _)
      exact preimage_mono (subset_preimage_image _ _)
    exact A.comp_continuous_on B
  rw [contMdiffOn_iff] at hf
  let x : H := I.symm (0 : E)
  let y : H' := I'.symm (0 : E')
  have A := hf.2 x y
  simp only [I.image_eq, inter_comm, mfld_simps] at A⊢
  apply A.continuous_on_fderiv_within _ hn
  convert hs.unique_diff_on_target_inter x using 1
  simp only [inter_comm, mfld_simps]
#align cont_mdiff_on.continuous_on_tangent_map_within_aux ContMdiffOn.continuousOn_tangentMapWithin_aux

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative is
`C^m` when `m+1 ≤ n`. In this auxiliary lemma, we prove this fact when the source and target space
are model spaces in models with corners. The general fact is proved in
`cont_mdiff_on.cont_mdiff_on_tangent_map_within` -/
theorem ContMdiffOn.contMdiffOn_tangentMapWithin_aux {f : H → H'} {s : Set H}
    (hf : ContMdiffOn I I' n f s) (hmn : m + 1 ≤ n) (hs : UniqueMdiffOn I s) :
    ContMdiffOn I.tangent I'.tangent m (tangentMapWithin I I' f s) (π (TangentSpace I) ⁻¹' s) :=
  by
  have m_le_n : m ≤ n := by
    apply le_trans _ hmn
    have : m + 0 ≤ m + 1 := add_le_add_left (zero_le _) _
    simpa only [add_zero] using this
  have one_le_n : 1 ≤ n := by
    apply le_trans _ hmn
    change 0 + 1 ≤ m + 1
    exact add_le_add_right (zero_le _) _
  have U' : UniqueDiffOn 𝕜 (range I ∩ I.symm ⁻¹' s) :=
    by
    intro y hy
    simpa only [UniqueMdiffOn, UniqueMdiffWithinAt, hy.1, inter_comm, mfld_simps] using
      hs (I.symm y) hy.2
  rw [contMdiffOn_iff]
  refine' ⟨hf.continuous_on_tangent_map_within_aux one_le_n hs, fun p q => _⟩
  have A :
    range I ×ˢ univ ∩
        ((Equiv.sigmaEquivProd H E).symm ∘ fun p : E × E => (I.symm p.fst, p.snd)) ⁻¹'
          (π (TangentSpace I) ⁻¹' s) =
      (range I ∩ I.symm ⁻¹' s) ×ˢ univ :=
    by
    ext ⟨x, v⟩
    simp only [mfld_simps]
  suffices h :
    ContDiffOn 𝕜 m
      (((fun p : H' × E' => (I' p.fst, p.snd)) ∘ Equiv.sigmaEquivProd H' E') ∘
        tangentMapWithin I I' f s ∘
          (Equiv.sigmaEquivProd H E).symm ∘ fun p : E × E => (I.symm p.fst, p.snd))
      ((range ⇑I ∩ ⇑I.symm ⁻¹' s) ×ˢ univ)
  · simpa [A] using h
  change
    ContDiffOn 𝕜 m
      (fun p : E × E =>
        ((I' (f (I.symm p.fst)), (mfderivWithin I I' f s (I.symm p.fst) : E → E') p.snd) : E' × E'))
      ((range I ∩ I.symm ⁻¹' s) ×ˢ univ)
  -- check that all bits in this formula are `C^n`
  have hf' := contMdiffOn_iff.1 hf
  have A : ContDiffOn 𝕜 m (I' ∘ f ∘ I.symm) (range I ∩ I.symm ⁻¹' s) := by
    simpa only [mfld_simps] using (hf'.2 (I.symm 0) (I'.symm 0)).of_le m_le_n
  have B : ContDiffOn 𝕜 m ((I' ∘ f ∘ I.symm) ∘ Prod.fst) ((range I ∩ I.symm ⁻¹' s) ×ˢ univ) :=
    A.comp cont_diff_fst.cont_diff_on (prod_subset_preimage_fst _ _)
  suffices C :
    ContDiffOn 𝕜 m
      (fun p : E × E => (fderivWithin 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) p.1 : _) p.2)
      ((range I ∩ I.symm ⁻¹' s) ×ˢ univ)
  · apply ContDiffOn.prod B _
    apply C.congr fun p hp => _
    simp only [mfld_simps] at hp
    simp only [mfderivWithin, hf.mdifferentiable_on one_le_n _ hp.2, hp.1, if_pos, mfld_simps]
  have D :
    ContDiffOn 𝕜 m (fun x => fderivWithin 𝕜 (I' ∘ f ∘ I.symm) (I.symm ⁻¹' s ∩ range I) x)
      (range I ∩ I.symm ⁻¹' s) :=
    by
    have : ContDiffOn 𝕜 n (I' ∘ f ∘ I.symm) (range I ∩ I.symm ⁻¹' s) := by
      simpa only [mfld_simps] using hf'.2 (I.symm 0) (I'.symm 0)
    simpa only [inter_comm] using this.fderiv_within U' hmn
  have := D.comp cont_diff_fst.cont_diff_on (prod_subset_preimage_fst _ _)
  have := ContDiffOn.prod this cont_diff_snd.cont_diff_on
  exact is_bounded_bilinear_map_apply.cont_diff.comp_cont_diff_on this
#align cont_mdiff_on.cont_mdiff_on_tangent_map_within_aux ContMdiffOn.contMdiffOn_tangentMapWithin_aux

include Is I's

/-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative
is `C^m` when `m+1 ≤ n`. -/
theorem ContMdiffOn.contMdiffOn_tangentMapWithin (hf : ContMdiffOn I I' n f s) (hmn : m + 1 ≤ n)
    (hs : UniqueMdiffOn I s) :
    ContMdiffOn I.tangent I'.tangent m (tangentMapWithin I I' f s) (π (TangentSpace I) ⁻¹' s) :=
  by
  /- The strategy of the proof is to avoid unfolding the definitions, and reduce by functoriality
    to the case of functions on the model spaces, where we have already proved the result.
    Let `l` and `r` be the charts to the left and to the right, so that we have
    ```
       l^{-1}      f       r
    H --------> M ---> M' ---> H'
    ```
    Then the tangent map `T(r ∘ f ∘ l)` is smooth by a previous result. Consider the composition
    ```
        Tl        T(r ∘ f ∘ l^{-1})         Tr^{-1}
    TM -----> TH -------------------> TH' ---------> TM'
    ```
    where `Tr^{-1}` and `Tl` are the tangent maps of `r^{-1}` and `l`. Writing `Tl` and `Tr^{-1}` as
    composition of charts (called `Dl` and `il` for `l` and `Dr` and `ir` in the proof below), it
    follows that they are smooth. The composition of all these maps is `Tf`, and is therefore smooth
    as a composition of smooth maps.
    -/
  have m_le_n : m ≤ n := by
    apply le_trans _ hmn
    have : m + 0 ≤ m + 1 := add_le_add_left (zero_le _) _
    simpa only [add_zero]
  have one_le_n : 1 ≤ n := by
    apply le_trans _ hmn
    change 0 + 1 ≤ m + 1
    exact add_le_add_right (zero_le _) _
  -- First step: local reduction on the space, to a set `s'` which is contained in chart domains.
  refine' contMdiffOn_of_locally_contMdiffOn fun p hp => _
  have hf' := contMdiffOn_iff.1 hf
  simp only [mfld_simps] at hp
  let l := chart_at H p.proj
  set Dl := chart_at (ModelProd H E) p with hDl
  let r := chart_at H' (f p.proj)
  let Dr := chart_at (ModelProd H' E') (tangentMapWithin I I' f s p)
  let il := chart_at (ModelProd H E) (tangentMap I I l p)
  let ir := chart_at (ModelProd H' E') (tangentMap I I' (r ∘ f) p)
  let s' := f ⁻¹' r.source ∩ s ∩ l.source
  let s'_lift := π (TangentSpace I) ⁻¹' s'
  let s'l := l.target ∩ l.symm ⁻¹' s'
  let s'l_lift := π (TangentSpace I) ⁻¹' s'l
  rcases continuousOn_iff'.1 hf'.1 r.source r.open_source with ⟨o, o_open, ho⟩
  suffices h : ContMdiffOn I.tangent I'.tangent m (tangentMapWithin I I' f s) s'_lift
  · refine' ⟨π (TangentSpace I) ⁻¹' (o ∩ l.source), _, _, _⟩
    show IsOpen (π (TangentSpace I) ⁻¹' (o ∩ l.source))
    exact (IsOpen.inter o_open l.open_source).Preimage (continuous_proj E _)
    show p ∈ π (TangentSpace I) ⁻¹' (o ∩ l.source)
    · simp
      have : p.proj ∈ f ⁻¹' r.source ∩ s := by simp [hp]
      rw [ho] at this
      exact this.1
    · have : π (TangentSpace I) ⁻¹' s ∩ π (TangentSpace I) ⁻¹' (o ∩ l.source) = s'_lift :=
        by
        dsimp only [s'_lift, s']
        rw [ho]
        mfld_set_tac
      rw [this]
      exact h
  /- Second step: check that all functions are smooth, and use the chain rule to write the bundled
    derivative as a composition of a function between model spaces and of charts.
    Convention: statements about the differentiability of `a ∘ b ∘ c` are named `diff_abc`. Statements
    about differentiability in the bundle have a `_lift` suffix. -/
  have U' : UniqueMdiffOn I s' :=
    by
    apply UniqueMdiffOn.inter _ l.open_source
    rw [ho, inter_comm]
    exact hs.inter o_open
  have U'l : UniqueMdiffOn I s'l := U'.unique_mdiff_on_preimage (mdifferentiable_chart _ _)
  have diff_f : ContMdiffOn I I' n f s' := hf.mono (by mfld_set_tac)
  have diff_r : ContMdiffOn I' I' n r r.source := contMdiffOn_chart
  have diff_rf : ContMdiffOn I I' n (r ∘ f) s' :=
    by
    apply ContMdiffOn.comp diff_r diff_f fun x hx => _
    simp only [s', mfld_simps] at hx
    simp only [hx, mfld_simps]
  have diff_l : ContMdiffOn I I n l.symm s'l :=
    haveI A : ContMdiffOn I I n l.symm l.target := contMdiffOn_chart_symm
    A.mono (by mfld_set_tac)
  have diff_rfl : ContMdiffOn I I' n (r ∘ f ∘ l.symm) s'l :=
    by
    apply ContMdiffOn.comp diff_rf diff_l
    mfld_set_tac
  have diff_rfl_lift :
    ContMdiffOn I.tangent I'.tangent m (tangentMapWithin I I' (r ∘ f ∘ l.symm) s'l) s'l_lift :=
    diff_rfl.cont_mdiff_on_tangent_map_within_aux hmn U'l
  have diff_irrfl_lift :
    ContMdiffOn I.tangent I'.tangent m (ir ∘ tangentMapWithin I I' (r ∘ f ∘ l.symm) s'l) s'l_lift :=
    haveI A : ContMdiffOn I'.tangent I'.tangent m ir ir.source := contMdiffOn_chart
    ContMdiffOn.comp A diff_rfl_lift fun p hp => by simp only [ir, mfld_simps]
  have diff_Drirrfl_lift :
    ContMdiffOn I.tangent I'.tangent m (Dr.symm ∘ ir ∘ tangentMapWithin I I' (r ∘ f ∘ l.symm) s'l)
      s'l_lift :=
    by
    have A : ContMdiffOn I'.tangent I'.tangent m Dr.symm Dr.target := contMdiffOn_chart_symm
    apply ContMdiffOn.comp A diff_irrfl_lift fun p hp => _
    simp only [s'l_lift, mfld_simps] at hp
    simp only [ir, hp, mfld_simps]
  -- conclusion of this step: the composition of all the maps above is smooth
  have diff_DrirrflilDl :
    ContMdiffOn I.tangent I'.tangent m
      (Dr.symm ∘ (ir ∘ tangentMapWithin I I' (r ∘ f ∘ l.symm) s'l) ∘ il.symm ∘ Dl) s'_lift :=
    by
    have A : ContMdiffOn I.tangent I.tangent m Dl Dl.source := contMdiffOn_chart
    have A' : ContMdiffOn I.tangent I.tangent m Dl s'_lift :=
      by
      apply A.mono fun p hp => _
      simp only [s'_lift, mfld_simps] at hp
      simp only [Dl, hp, mfld_simps]
    have B : ContMdiffOn I.tangent I.tangent m il.symm il.target := contMdiffOn_chart_symm
    have C : ContMdiffOn I.tangent I.tangent m (il.symm ∘ Dl) s'_lift :=
      ContMdiffOn.comp B A' fun p hp => by simp only [il, mfld_simps]
    apply ContMdiffOn.comp diff_Drirrfl_lift C fun p hp => _
    simp only [s'_lift, mfld_simps] at hp
    simp only [il, s'l_lift, hp, total_space.proj, mfld_simps]
  /- Third step: check that the composition of all the maps indeed coincides with the derivative we
    are looking for -/
  have eq_comp :
    ∀ q ∈ s'_lift,
      tangentMapWithin I I' f s q =
        (Dr.symm ∘ ir ∘ tangentMapWithin I I' (r ∘ f ∘ l.symm) s'l ∘ il.symm ∘ Dl) q :=
    by
    intro q hq
    simp only [s'_lift, mfld_simps] at hq
    have U'q : UniqueMdiffWithinAt I s' q.1 := by
      apply U'
      simp only [hq, s', mfld_simps]
    have U'lq : UniqueMdiffWithinAt I s'l (Dl q).1 :=
      by
      apply U'l
      simp only [hq, s'l, mfld_simps]
    have A :
      tangentMapWithin I I' ((r ∘ f) ∘ l.symm) s'l (il.symm (Dl q)) =
        tangentMapWithin I I' (r ∘ f) s' (tangentMapWithin I I l.symm s'l (il.symm (Dl q))) :=
      by
      refine' tangentMapWithin_comp_at (il.symm (Dl q)) _ _ (fun p hp => _) U'lq
      · apply diff_rf.mdifferentiable_on one_le_n
        simp only [hq, mfld_simps]
      · apply diff_l.mdifferentiable_on one_le_n
        simp only [s'l, hq, mfld_simps]
      · simp only [mfld_simps] at hp
        simp only [hp, mfld_simps]
    have B : tangentMapWithin I I l.symm s'l (il.symm (Dl q)) = q :=
      by
      have :
        tangentMapWithin I I l.symm s'l (il.symm (Dl q)) = tangentMap I I l.symm (il.symm (Dl q)) :=
        by
        refine' tangentMapWithin_eq_tangentMap U'lq _
        refine' mdifferentiableAt_atlas_symm _ (chart_mem_atlas _ _) _
        simp only [hq, mfld_simps]
      rw [this, tangentMap_chart_symm, hDl]
      · simp only [hq, mfld_simps]
        have : q ∈ (chart_at (ModelProd H E) p).source := by simp only [hq, mfld_simps]
        exact (chart_at (ModelProd H E) p).left_inv this
      · simp only [hq, mfld_simps]
    have C :
      tangentMapWithin I I' (r ∘ f) s' q =
        tangentMapWithin I' I' r r.source (tangentMapWithin I I' f s' q) :=
      by
      refine' tangentMapWithin_comp_at q _ _ (fun r hr => _) U'q
      · apply diff_r.mdifferentiable_on one_le_n
        simp only [hq, mfld_simps]
      · apply diff_f.mdifferentiable_on one_le_n
        simp only [hq, mfld_simps]
      · simp only [s', mfld_simps] at hr
        simp only [hr, mfld_simps]
    have D :
      Dr.symm (ir (tangentMapWithin I' I' r r.source (tangentMapWithin I I' f s' q))) =
        tangentMapWithin I I' f s' q :=
      by
      have A :
        tangentMapWithin I' I' r r.source (tangentMapWithin I I' f s' q) =
          tangentMap I' I' r (tangentMapWithin I I' f s' q) :=
        by
        apply tangentMapWithin_eq_tangentMap
        · apply IsOpen.uniqueMdiffWithinAt _ r.open_source
          simp [hq]
        · refine' mdifferentiableAt_atlas _ (chart_mem_atlas _ _) _
          simp only [hq, mfld_simps]
      have : f p.proj = (tangentMapWithin I I' f s p).1 := rfl
      rw [A]
      dsimp [r, Dr]
      rw [this, tangentMap_chart]
      · simp only [hq, mfld_simps]
        have :
          tangentMapWithin I I' f s' q ∈
            (chart_at (ModelProd H' E') (tangentMapWithin I I' f s p)).source :=
          by simp only [hq, mfld_simps]
        exact (chart_at (ModelProd H' E') (tangentMapWithin I I' f s p)).left_inv this
      · simp only [hq, mfld_simps]
    have E : tangentMapWithin I I' f s' q = tangentMapWithin I I' f s q :=
      by
      refine' tangentMapWithin_subset (by mfld_set_tac) U'q _
      apply hf.mdifferentiable_on one_le_n
      simp only [hq, mfld_simps]
    simp only [(· ∘ ·), A, B, C, D, E.symm]
  exact diff_DrirrflilDl.congr eq_comp
#align cont_mdiff_on.cont_mdiff_on_tangent_map_within ContMdiffOn.contMdiffOn_tangentMapWithin

/-- If a function is `C^n` on a domain with unique derivatives, with `1 ≤ n`, then its bundled
derivative is continuous there. -/
theorem ContMdiffOn.continuousOn_tangentMapWithin (hf : ContMdiffOn I I' n f s) (hmn : 1 ≤ n)
    (hs : UniqueMdiffOn I s) :
    ContinuousOn (tangentMapWithin I I' f s) (π (TangentSpace I) ⁻¹' s) :=
  haveI :
    ContMdiffOn I.tangent I'.tangent 0 (tangentMapWithin I I' f s) (π (TangentSpace I) ⁻¹' s) :=
    hf.cont_mdiff_on_tangent_map_within hmn hs
  this.continuous_on
#align cont_mdiff_on.continuous_on_tangent_map_within ContMdiffOn.continuousOn_tangentMapWithin

/-- If a function is `C^n`, then its bundled derivative is `C^m` when `m+1 ≤ n`. -/
theorem ContMdiff.contMdiff_tangentMap (hf : ContMdiff I I' n f) (hmn : m + 1 ≤ n) :
    ContMdiff I.tangent I'.tangent m (tangentMap I I' f) :=
  by
  rw [← contMdiffOn_univ] at hf⊢
  convert hf.cont_mdiff_on_tangent_map_within hmn uniqueMdiffOn_univ
  rw [tangentMapWithin_univ]
#align cont_mdiff.cont_mdiff_tangent_map ContMdiff.contMdiff_tangentMap

/-- If a function is `C^n`, with `1 ≤ n`, then its bundled derivative is continuous. -/
theorem ContMdiff.continuous_tangentMap (hf : ContMdiff I I' n f) (hmn : 1 ≤ n) :
    Continuous (tangentMap I I' f) :=
  by
  rw [← contMdiffOn_univ] at hf
  rw [continuous_iff_continuousOn_univ]
  convert hf.continuous_on_tangent_map_within hmn uniqueMdiffOn_univ
  rw [tangentMapWithin_univ]
#align cont_mdiff.continuous_tangent_map ContMdiff.continuous_tangentMap

end tangentMap

namespace TangentBundle

include Is

variable (I M)

open Bundle

/-- The derivative of the zero section of the tangent bundle maps `⟨x, v⟩` to `⟨⟨x, 0⟩, ⟨v, 0⟩⟩`.

Note that, as currently framed, this is a statement in coordinates, thus reliant on the choice
of the coordinate system we use on the tangent bundle.

However, the result itself is coordinate-dependent only to the extent that the coordinates
determine a splitting of the tangent bundle.  Moreover, there is a canonical splitting at each
point of the zero section (since there is a canonical horizontal space there, the tangent space
to the zero section, in addition to the canonical vertical space which is the kernel of the
derivative of the projection), and this canonical splitting is also the one that comes from the
coordinates on the tangent bundle in our definitions. So this statement is not as crazy as it
may seem.

TODO define splittings of vector bundles; state this result invariantly. -/
theorem tangentMap_tangentBundle_pure (p : TangentBundle I M) :
    tangentMap I I.tangent (zeroSection (TangentSpace I)) p = ⟨⟨p.proj, 0⟩, ⟨p.2, 0⟩⟩ :=
  by
  rcases p with ⟨x, v⟩
  have N : I.symm ⁻¹' (chart_at H x).target ∈ 𝓝 (I ((chart_at H x) x)) :=
    by
    apply IsOpen.mem_nhds
    apply (LocalHomeomorph.open_target _).Preimage I.continuous_inv_fun
    simp only [mfld_simps]
  have A : MdifferentiableAt I I.tangent (fun x => @total_space_mk M (TangentSpace I) x 0) x :=
    haveI : Smooth I (I.prod 𝓘(𝕜, E)) (zero_section (TangentSpace I : M → Type _)) :=
      Bundle.smooth_zeroSection 𝕜 (TangentSpace I : M → Type _)
    this.mdifferentiable_at
  have B :
    fderivWithin 𝕜 (fun x' : E => (x', (0 : E))) (Set.range ⇑I) (I ((chart_at H x) x)) v = (v, 0) :=
    by
    rw [fderivWithin_eq_fderiv, DifferentiableAt.fderiv_prod]
    · simp
    · exact differentiableAt_id'
    · exact differentiableAt_const _
    · exact ModelWithCorners.unique_diff_at_image I
    · exact differentiable_at_id'.prod (differentiableAt_const _)
  simp only [Bundle.zeroSection, tangentMap, mfderiv, total_space.proj_mk, A, if_pos, chart_at,
    FiberBundle.chartedSpace_chartAt, TangentBundle.trivializationAt_apply, tangentBundleCore,
    Function.comp, ContinuousLinearMap.map_zero, mfld_simps]
  rw [← fderivWithin_inter N (I.unique_diff (I ((chart_at H x) x)) (Set.mem_range_self _))] at B
  rw [← fderivWithin_inter N (I.unique_diff (I ((chart_at H x) x)) (Set.mem_range_self _)), ← B]
  congr 2
  apply fderivWithin_congr _ fun y hy => _
  · simp only [Prod.mk.inj_iff, mfld_simps]
  · apply UniqueDiffWithinAt.inter (I.unique_diff _ _) N
    simp only [mfld_simps]
  · simp only [mfld_simps] at hy
    simp only [hy, Prod.mk.inj_iff, mfld_simps]
#align tangent_bundle.tangent_map_tangent_bundle_pure TangentBundle.tangentMap_tangentBundle_pure

end TangentBundle


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.ring_exp
import Mathlib.algebra.algebra.basic
import Mathlib.algebra.opposites
import Mathlib.data.equiv.ring
import Mathlib.PostPort

universes u_1 l

namespace Mathlib

/-!
# Quaternions

In this file we define quaternions `ℍ[R]` over a commutative ring `R`, and define some
algebraic structures on `ℍ[R]`.

## Main definitions

* `quaternion_algebra R a b`, `ℍ[R, a, b]` :
  [quaternion algebra](https://en.wikipedia.org/wiki/Quaternion_algebra) with coefficients `a`, `b`
* `quaternion R`, `ℍ[R]` : the space of quaternions, a.k.a. `quaternion_algebra R (-1) (-1)`;
* `quaternion.norm_sq` : square of the norm of a quaternion;
* `quaternion.conj` : conjugate of a quaternion;

We also define the following algebraic structures on `ℍ[R]`:

* `ring ℍ[R, a, b]` and `algebra R ℍ[R, a, b]` : for any commutative ring `R`;
* `ring ℍ[R]` and `algebra R ℍ[R]` : for any commutative ring `R`;
* `domain ℍ[R]` : for a linear ordered commutative ring `R`;
* `division_algebra ℍ[R]` : for a linear ordered field `R`.

## Notation

The following notation is available with `open_locale quaternion`.

* `ℍ[R, c₁, c₂]` : `quaternion_algebra R  c₁ c₂`
* `ℍ[R]` : quaternions over `R`.

## Implementation notes

We define quaternions over any ring `R`, not just `ℝ` to be able to deal with, e.g., integer
or rational quaternions without using real numbers. In particular, all definitions in this file
are computable.

## Tags

quaternion
-/

/-- Quaternion algebra over a type with fixed coefficients $a=i^2$ and $b=j^2$.
Implemented as a structure with four fields: `re`, `im_i`, `im_j`, and `im_k`. -/
structure quaternion_algebra (R : Type u_1) (a : R) (b : R) where
  re : R
  im_i : R
  im_j : R
  im_k : R

namespace quaternion_algebra

@[simp] theorem mk.eta {R : Type u_1} {c₁ : R} {c₂ : R} (a : quaternion_algebra R c₁ c₂) :
    mk (re a) (im_i a) (im_j a) (im_k a) = a :=
  sorry

protected instance has_coe_t {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    has_coe_t R (quaternion_algebra R c₁ c₂) :=
  has_coe_t.mk fun (x : R) => mk x 0 0 0

@[simp] theorem coe_re {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) : re ↑x = x := rfl

@[simp] theorem coe_im_i {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) : im_i ↑x = 0 := rfl

@[simp] theorem coe_im_j {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) : im_j ↑x = 0 := rfl

@[simp] theorem coe_im_k {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) : im_k ↑x = 0 := rfl

theorem coe_injective {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : function.injective coe :=
  fun (x y : R) (h : ↑x = ↑y) => congr_arg re h

@[simp] theorem coe_inj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} {x : R} {y : R} :
    ↑x = ↑y ↔ x = y :=
  function.injective.eq_iff coe_injective

@[simp] theorem has_zero_zero_im_j {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : im_j 0 = 0 :=
  Eq.refl (im_j 0)

@[simp] theorem coe_zero {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : ↑0 = 0 := rfl

protected instance inhabited {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    Inhabited (quaternion_algebra R c₁ c₂) :=
  { default := 0 }

@[simp] theorem has_one_one_re {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : re 1 = 1 :=
  Eq.refl (re 1)

@[simp] theorem coe_one {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : ↑1 = 1 := rfl

@[simp] theorem has_add_add_im_i {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    im_i (a + b) = im_i a + im_i b :=
  Eq.refl (im_i (a + b))

@[simp] theorem mk_add_mk {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a₁ : R) (a₂ : R) (a₃ : R)
    (a₄ : R) (b₁ : R) (b₂ : R) (b₃ : R) (b₄ : R) :
    mk a₁ a₂ a₃ a₄ + mk b₁ b₂ b₃ b₄ = mk (a₁ + b₁) (a₂ + b₂) (a₃ + b₃) (a₄ + b₄) :=
  rfl

protected instance has_neg {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    Neg (quaternion_algebra R c₁ c₂) :=
  { neg := fun (a : quaternion_algebra R c₁ c₂) => mk (-re a) (-im_i a) (-im_j a) (-im_k a) }

@[simp] theorem neg_mk {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a₁ : R) (a₂ : R) (a₃ : R)
    (a₄ : R) : -mk a₁ a₂ a₃ a₄ = mk (-a₁) (-a₂) (-a₃) (-a₄) :=
  rfl

@[simp] theorem has_sub_sub_im_k {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    im_k (a - b) = im_k a - im_k b :=
  Eq.refl (im_k (a - b))

@[simp] theorem mk_sub_mk {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a₁ : R) (a₂ : R) (a₃ : R)
    (a₄ : R) (b₁ : R) (b₂ : R) (b₃ : R) (b₄ : R) :
    mk a₁ a₂ a₃ a₄ - mk b₁ b₂ b₃ b₄ = mk (a₁ - b₁) (a₂ - b₂) (a₃ - b₃) (a₄ - b₄) :=
  rfl

/-- Multiplication is given by

* `1 * x = x * 1 = x`;
* `i * i = c₁`;
* `j * j = c₂`;
* `i * j = k`, `j * i = -k`;
* `k * k = -c₁ * c₂`;
* `i * k = c₁ * j`, `k * i = `-c₁ * j`;
* `j * k = -c₂ * i`, `k * j = c₂ * i`.  -/
@[simp] theorem has_mul_mul_im_i {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    im_i (a * b) = re a * im_i b + im_i a * re b - c₂ * im_j a * im_k b + c₂ * im_k a * im_j b :=
  Eq.refl (im_i (a * b))

@[simp] theorem mk_mul_mk {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a₁ : R) (a₂ : R) (a₃ : R)
    (a₄ : R) (b₁ : R) (b₂ : R) (b₃ : R) (b₄ : R) :
    mk a₁ a₂ a₃ a₄ * mk b₁ b₂ b₃ b₄ =
        mk (a₁ * b₁ + c₁ * a₂ * b₂ + c₂ * a₃ * b₃ - c₁ * c₂ * a₄ * b₄)
          (a₁ * b₂ + a₂ * b₁ - c₂ * a₃ * b₄ + c₂ * a₄ * b₃)
          (a₁ * b₃ + c₁ * a₂ * b₄ + a₃ * b₁ - c₁ * a₄ * b₂)
          (a₁ * b₄ + a₂ * b₃ - a₃ * b₂ + a₄ * b₁) :=
  rfl

protected instance ring {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    ring (quaternion_algebra R c₁ c₂) :=
  ring.mk Add.add sorry 0 sorry sorry Neg.neg Sub.sub sorry sorry Mul.mul sorry 1 sorry sorry sorry
    sorry

protected instance algebra {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    algebra R (quaternion_algebra R c₁ c₂) :=
  algebra.mk (ring_hom.mk coe sorry sorry sorry sorry) sorry sorry

@[simp] theorem smul_re {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : re (r • a) = r • re a :=
  rfl

@[simp] theorem smul_im_i {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : im_i (r • a) = r • im_i a :=
  rfl

@[simp] theorem smul_im_j {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : im_j (r • a) = r • im_j a :=
  rfl

@[simp] theorem smul_im_k {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : im_k (r • a) = r • im_k a :=
  rfl

@[simp] theorem coe_add {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) (y : R) :
    ↑(x + y) = ↑x + ↑y :=
  ring_hom.map_add (algebra_map R (quaternion_algebra R c₁ c₂)) x y

@[simp] theorem coe_sub {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) (y : R) :
    ↑(x - y) = ↑x - ↑y :=
  ring_hom.map_sub (algebra_map R (quaternion_algebra R c₁ c₂)) x y

@[simp] theorem coe_neg {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) : ↑(-x) = -↑x :=
  ring_hom.map_neg (algebra_map R (quaternion_algebra R c₁ c₂)) x

@[simp] theorem coe_mul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) (y : R) :
    ↑(x * y) = ↑x * ↑y :=
  ring_hom.map_mul (algebra_map R (quaternion_algebra R c₁ c₂)) x y

theorem coe_commutes {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : ↑r * a = a * ↑r :=
  algebra.commutes r a

theorem coe_commute {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : commute (↑r) a :=
  coe_commutes r a

theorem coe_mul_eq_smul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : ↑r * a = r • a :=
  Eq.symm (algebra.smul_def r a)

theorem mul_coe_eq_smul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : a * ↑r = r • a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * ↑r = r • a)) (Eq.symm (coe_commutes r a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑r * a = r • a)) (coe_mul_eq_smul r a))) (Eq.refl (r • a)))

@[simp] theorem coe_algebra_map {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    ⇑(algebra_map R (quaternion_algebra R c₁ c₂)) = coe :=
  rfl

theorem smul_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) (y : R) :
    x • ↑y = ↑(x * y) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x • ↑y = ↑(x * y))) (coe_mul x y)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (x • ↑y = ↑x * ↑y)) (coe_mul_eq_smul x ↑y)))
      (Eq.refl (x • ↑y)))

/-- Quaternion conjugate. -/
def conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    linear_equiv R (quaternion_algebra R c₁ c₂) (quaternion_algebra R c₁ c₂) :=
  linear_equiv.of_involutive
    (linear_map.mk (fun (a : quaternion_algebra R c₁ c₂) => mk (re a) (-im_i a) (-im_j a) (-im_k a))
      sorry sorry)
    sorry

@[simp] theorem re_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : re (coe_fn conj a) = re a :=
  rfl

@[simp] theorem im_i_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : im_i (coe_fn conj a) = -im_i a :=
  rfl

@[simp] theorem im_j_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : im_j (coe_fn conj a) = -im_j a :=
  rfl

@[simp] theorem im_k_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : im_k (coe_fn conj a) = -im_k a :=
  rfl

@[simp] theorem conj_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj (coe_fn conj a) = a :=
  ext (coe_fn conj (coe_fn conj a)) a rfl (neg_neg (im_i a)) (neg_neg (im_j a)) (neg_neg (im_k a))

theorem conj_add {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a : quaternion_algebra R c₁ c₂)
    (b : quaternion_algebra R c₁ c₂) : coe_fn conj (a + b) = coe_fn conj a + coe_fn conj b :=
  linear_equiv.map_add conj a b

@[simp] theorem conj_mul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    coe_fn conj (a * b) = coe_fn conj b * coe_fn conj a :=
  sorry

theorem conj_conj_mul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    coe_fn conj (coe_fn conj a * b) = coe_fn conj b * a :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (coe_fn conj (coe_fn conj a * b) = coe_fn conj b * a))
        (conj_mul (coe_fn conj a) b)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (coe_fn conj b * coe_fn conj (coe_fn conj a) = coe_fn conj b * a))
          (conj_conj a)))
      (Eq.refl (coe_fn conj b * a)))

theorem conj_mul_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) (b : quaternion_algebra R c₁ c₂) :
    coe_fn conj (a * coe_fn conj b) = b * coe_fn conj a :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (coe_fn conj (a * coe_fn conj b) = b * coe_fn conj a))
        (conj_mul a (coe_fn conj b))))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (coe_fn conj (coe_fn conj b) * coe_fn conj a = b * coe_fn conj a))
          (conj_conj b)))
      (Eq.refl (b * coe_fn conj a)))

theorem self_add_conj' {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : a + coe_fn conj a = ↑(bit0 1 * re a) :=
  sorry

theorem self_add_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : a + coe_fn conj a = bit0 1 * ↑(re a) :=
  sorry

theorem conj_add_self' {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj a + a = ↑(bit0 1 * re a) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (coe_fn conj a + a = ↑(bit0 1 * re a))) (add_comm (coe_fn conj a) a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + coe_fn conj a = ↑(bit0 1 * re a))) (self_add_conj' a)))
      (Eq.refl ↑(bit0 1 * re a)))

theorem conj_add_self {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj a + a = bit0 1 * ↑(re a) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (coe_fn conj a + a = bit0 1 * ↑(re a))) (add_comm (coe_fn conj a) a)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + coe_fn conj a = bit0 1 * ↑(re a))) (self_add_conj a)))
      (Eq.refl (bit0 1 * ↑(re a))))

theorem conj_eq_two_re_sub {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj a = ↑(bit0 1 * re a) - a :=
  iff.mpr eq_sub_iff_add_eq (conj_add_self' a)

theorem commute_conj_self {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : commute (coe_fn conj a) a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (commute (coe_fn conj a) a)) (conj_eq_two_re_sub a)))
    (commute.sub_left (coe_commute (bit0 1 * re a) a) (commute.refl a))

theorem commute_self_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : commute a (coe_fn conj a) :=
  commute.symm (commute_conj_self a)

theorem commute_conj_conj {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    {a : quaternion_algebra R c₁ c₂} {b : quaternion_algebra R c₁ c₂} (h : commute a b) :
    commute (coe_fn conj a) (coe_fn conj b) :=
  sorry

@[simp] theorem conj_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (x : R) :
    coe_fn conj ↑x = ↑x :=
  sorry

theorem conj_smul {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (r : R)
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj (r • a) = r • coe_fn conj a :=
  linear_equiv.map_smul conj r a

@[simp] theorem conj_one {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : coe_fn conj 1 = 1 :=
  conj_coe 1

theorem eq_re_of_eq_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    {a : quaternion_algebra R c₁ c₂} {x : R} (h : a = ↑x) : a = ↑(re a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = ↑(re a))) h))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑x = ↑(re ↑x))) (coe_re x))) (Eq.refl ↑x))

theorem eq_re_iff_mem_range_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    {a : quaternion_algebra R c₁ c₂} : a = ↑(re a) ↔ a ∈ set.range coe :=
  sorry

@[simp] theorem conj_fixed {R : Type u_1} [comm_ring R] [no_zero_divisors R] [char_zero R] {c₁ : R}
    {c₂ : R} {a : quaternion_algebra R c₁ c₂} : coe_fn conj a = a ↔ a = ↑(re a) :=
  sorry

-- Can't use `rw ← conj_fixed` in the proof without additional assumptions

theorem conj_mul_eq_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : coe_fn conj a * a = ↑(re (coe_fn conj a * a)) :=
  sorry

theorem mul_conj_eq_coe {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : a * coe_fn conj a = ↑(re (a * coe_fn conj a)) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (a * coe_fn conj a = ↑(re (a * coe_fn conj a))))
        (commute.eq (commute_self_conj a))))
    (conj_mul_eq_coe a)

theorem conj_zero {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} : coe_fn conj 0 = 0 :=
  linear_equiv.map_zero conj

theorem conj_neg {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a : quaternion_algebra R c₁ c₂) :
    coe_fn conj (-a) = -coe_fn conj a :=
  linear_equiv.map_neg conj a

theorem conj_sub {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} (a : quaternion_algebra R c₁ c₂)
    (b : quaternion_algebra R c₁ c₂) : coe_fn conj (a - b) = coe_fn conj a - coe_fn conj b :=
  linear_equiv.map_sub conj a b

protected instance star_ring {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    star_ring (quaternion_algebra R c₁ c₂) :=
  star_ring.mk conj_add

@[simp] theorem star_def {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R}
    (a : quaternion_algebra R c₁ c₂) : star a = coe_fn conj a :=
  rfl

/-- Quaternion conjugate as an `alg_equiv` to the opposite ring. -/
def conj_alg_equiv {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    alg_equiv R (quaternion_algebra R c₁ c₂) (quaternion_algebra R c₁ c₂ᵒᵖ) :=
  alg_equiv.mk (opposite.op ∘ ⇑conj) (⇑conj ∘ opposite.unop) sorry sorry sorry sorry sorry

@[simp] theorem coe_conj_alg_equiv {R : Type u_1} [comm_ring R] {c₁ : R} {c₂ : R} :
    ⇑conj_alg_equiv = opposite.op ∘ ⇑conj :=
  rfl

end quaternion_algebra

/-- Space of quaternions over a type. Implemented as a structure with four fields:
`re`, `im_i`, `im_j`, and `im_k`. -/
def quaternion (R : Type u_1) [HasOne R] [Neg R] := quaternion_algebra R (-1) (-1)

namespace quaternion

protected instance has_coe_t {R : Type u_1} [comm_ring R] : has_coe_t R (quaternion R) :=
  quaternion_algebra.has_coe_t

protected instance ring {R : Type u_1} [comm_ring R] : ring (quaternion R) :=
  quaternion_algebra.ring

protected instance inhabited {R : Type u_1} [comm_ring R] : Inhabited (quaternion R) :=
  quaternion_algebra.inhabited

protected instance algebra {R : Type u_1} [comm_ring R] : algebra R (quaternion R) :=
  quaternion_algebra.algebra

protected instance star_ring {R : Type u_1} [comm_ring R] : star_ring (quaternion R) :=
  quaternion_algebra.star_ring

theorem ext {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    re a = re b → im_i a = im_i b → im_j a = im_j b → im_k a = im_k b → a = b :=
  quaternion_algebra.ext a b

theorem ext_iff {R : Type u_1} [comm_ring R] {a : quaternion R} {b : quaternion R} :
    a = b ↔ re a = re b ∧ im_i a = im_i b ∧ im_j a = im_j b ∧ im_k a = im_k b :=
  quaternion_algebra.ext_iff a b

@[simp] theorem coe_re {R : Type u_1} [comm_ring R] (x : R) : re ↑x = x := rfl

@[simp] theorem coe_im_i {R : Type u_1} [comm_ring R] (x : R) : im_i ↑x = 0 := rfl

@[simp] theorem coe_im_j {R : Type u_1} [comm_ring R] (x : R) : im_j ↑x = 0 := rfl

@[simp] theorem coe_im_k {R : Type u_1} [comm_ring R] (x : R) : im_k ↑x = 0 := rfl

@[simp] theorem zero_re {R : Type u_1} [comm_ring R] : re 0 = 0 := rfl

@[simp] theorem zero_im_i {R : Type u_1} [comm_ring R] : im_i 0 = 0 := rfl

@[simp] theorem zero_im_j {R : Type u_1} [comm_ring R] : im_j 0 = 0 := rfl

@[simp] theorem zero_im_k {R : Type u_1} [comm_ring R] : im_k 0 = 0 := rfl

@[simp] theorem coe_zero {R : Type u_1} [comm_ring R] : ↑0 = 0 := rfl

@[simp] theorem one_re {R : Type u_1} [comm_ring R] : re 1 = 1 := rfl

@[simp] theorem one_im_i {R : Type u_1} [comm_ring R] : im_i 1 = 0 := rfl

@[simp] theorem one_im_j {R : Type u_1} [comm_ring R] : im_j 1 = 0 := rfl

@[simp] theorem one_im_k {R : Type u_1} [comm_ring R] : im_k 1 = 0 := rfl

@[simp] theorem coe_one {R : Type u_1} [comm_ring R] : ↑1 = 1 := rfl

@[simp] theorem add_re {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    re (a + b) = re a + re b :=
  rfl

@[simp] theorem add_im_i {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_i (a + b) = im_i a + im_i b :=
  rfl

@[simp] theorem add_im_j {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_j (a + b) = im_j a + im_j b :=
  rfl

@[simp] theorem add_im_k {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_k (a + b) = im_k a + im_k b :=
  rfl

@[simp] theorem coe_add {R : Type u_1} [comm_ring R] (x : R) (y : R) : ↑(x + y) = ↑x + ↑y :=
  quaternion_algebra.coe_add x y

@[simp] theorem neg_re {R : Type u_1} [comm_ring R] (a : quaternion R) : re (-a) = -re a := rfl

@[simp] theorem neg_im_i {R : Type u_1} [comm_ring R] (a : quaternion R) : im_i (-a) = -im_i a :=
  rfl

@[simp] theorem neg_im_j {R : Type u_1} [comm_ring R] (a : quaternion R) : im_j (-a) = -im_j a :=
  rfl

@[simp] theorem neg_im_k {R : Type u_1} [comm_ring R] (a : quaternion R) : im_k (-a) = -im_k a :=
  rfl

@[simp] theorem coe_neg {R : Type u_1} [comm_ring R] (x : R) : ↑(-x) = -↑x :=
  quaternion_algebra.coe_neg x

@[simp] theorem sub_re {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    re (a - b) = re a - re b :=
  rfl

@[simp] theorem sub_im_i {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_i (a - b) = im_i a - im_i b :=
  rfl

@[simp] theorem sub_im_j {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_j (a - b) = im_j a - im_j b :=
  rfl

@[simp] theorem sub_im_k {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_k (a - b) = im_k a - im_k b :=
  rfl

@[simp] theorem coe_sub {R : Type u_1} [comm_ring R] (x : R) (y : R) : ↑(x - y) = ↑x - ↑y :=
  quaternion_algebra.coe_sub x y

@[simp] theorem mul_re {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    re (a * b) = re a * re b - im_i a * im_i b - im_j a * im_j b - im_k a * im_k b :=
  sorry

@[simp] theorem mul_im_i {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_i (a * b) = re a * im_i b + im_i a * re b + im_j a * im_k b - im_k a * im_j b :=
  sorry

@[simp] theorem mul_im_j {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_j (a * b) = re a * im_j b - im_i a * im_k b + im_j a * re b + im_k a * im_i b :=
  sorry

@[simp] theorem mul_im_k {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    im_k (a * b) = re a * im_k b + im_i a * im_j b - im_j a * im_i b + im_k a * re b :=
  sorry

@[simp] theorem coe_mul {R : Type u_1} [comm_ring R] (x : R) (y : R) : ↑(x * y) = ↑x * ↑y :=
  quaternion_algebra.coe_mul x y

theorem coe_injective {R : Type u_1} [comm_ring R] : function.injective coe :=
  quaternion_algebra.coe_injective

@[simp] theorem coe_inj {R : Type u_1} [comm_ring R] {x : R} {y : R} : ↑x = ↑y ↔ x = y :=
  function.injective.eq_iff coe_injective

@[simp] theorem smul_re {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) :
    re (r • a) = r • re a :=
  rfl

@[simp] theorem smul_im_i {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) :
    im_i (r • a) = r • im_i a :=
  rfl

@[simp] theorem smul_im_j {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) :
    im_j (r • a) = r • im_j a :=
  rfl

@[simp] theorem smul_im_k {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) :
    im_k (r • a) = r • im_k a :=
  rfl

theorem coe_commutes {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) : ↑r * a = a * ↑r :=
  quaternion_algebra.coe_commutes r a

theorem coe_commute {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) : commute (↑r) a :=
  quaternion_algebra.coe_commute r a

theorem coe_mul_eq_smul {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) : ↑r * a = r • a :=
  quaternion_algebra.coe_mul_eq_smul r a

theorem mul_coe_eq_smul {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) : a * ↑r = r • a :=
  quaternion_algebra.mul_coe_eq_smul r a

@[simp] theorem algebra_map_def {R : Type u_1} [comm_ring R] :
    ⇑(algebra_map R (quaternion R)) = coe :=
  rfl

theorem smul_coe {R : Type u_1} [comm_ring R] (x : R) (y : R) : x • ↑y = ↑(x * y) :=
  quaternion_algebra.smul_coe x y

/-- Quaternion conjugate. -/
def conj {R : Type u_1} [comm_ring R] : linear_equiv R (quaternion R) (quaternion R) :=
  quaternion_algebra.conj

@[simp] theorem conj_re {R : Type u_1} [comm_ring R] (a : quaternion R) :
    re (coe_fn conj a) = re a :=
  rfl

@[simp] theorem conj_im_i {R : Type u_1} [comm_ring R] (a : quaternion R) :
    im_i (coe_fn conj a) = -im_i a :=
  rfl

@[simp] theorem conj_im_j {R : Type u_1} [comm_ring R] (a : quaternion R) :
    im_j (coe_fn conj a) = -im_j a :=
  rfl

@[simp] theorem conj_im_k {R : Type u_1} [comm_ring R] (a : quaternion R) :
    im_k (coe_fn conj a) = -im_k a :=
  rfl

@[simp] theorem conj_conj {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj (coe_fn conj a) = a :=
  quaternion_algebra.conj_conj a

@[simp] theorem conj_add {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    coe_fn conj (a + b) = coe_fn conj a + coe_fn conj b :=
  quaternion_algebra.conj_add a b

@[simp] theorem conj_mul {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    coe_fn conj (a * b) = coe_fn conj b * coe_fn conj a :=
  quaternion_algebra.conj_mul a b

theorem conj_conj_mul {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    coe_fn conj (coe_fn conj a * b) = coe_fn conj b * a :=
  quaternion_algebra.conj_conj_mul a b

theorem conj_mul_conj {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    coe_fn conj (a * coe_fn conj b) = b * coe_fn conj a :=
  quaternion_algebra.conj_mul_conj a b

theorem self_add_conj' {R : Type u_1} [comm_ring R] (a : quaternion R) :
    a + coe_fn conj a = ↑(bit0 1 * re a) :=
  quaternion_algebra.self_add_conj' a

theorem self_add_conj {R : Type u_1} [comm_ring R] (a : quaternion R) :
    a + coe_fn conj a = bit0 1 * ↑(re a) :=
  quaternion_algebra.self_add_conj a

theorem conj_add_self' {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj a + a = ↑(bit0 1 * re a) :=
  quaternion_algebra.conj_add_self' a

theorem conj_add_self {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj a + a = bit0 1 * ↑(re a) :=
  quaternion_algebra.conj_add_self a

theorem conj_eq_two_re_sub {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj a = ↑(bit0 1 * re a) - a :=
  quaternion_algebra.conj_eq_two_re_sub a

theorem commute_conj_self {R : Type u_1} [comm_ring R] (a : quaternion R) :
    commute (coe_fn conj a) a :=
  quaternion_algebra.commute_conj_self a

theorem commute_self_conj {R : Type u_1} [comm_ring R] (a : quaternion R) :
    commute a (coe_fn conj a) :=
  quaternion_algebra.commute_self_conj a

theorem commute_conj_conj {R : Type u_1} [comm_ring R] {a : quaternion R} {b : quaternion R}
    (h : commute a b) : commute (coe_fn conj a) (coe_fn conj b) :=
  quaternion_algebra.commute_conj_conj h

theorem Mathlib.commute.quaternion_conj {R : Type u_1} [comm_ring R] {a : quaternion R}
    {b : quaternion R} (h : commute a b) : commute (coe_fn conj a) (coe_fn conj b) :=
  commute_conj_conj

@[simp] theorem conj_coe {R : Type u_1} [comm_ring R] (x : R) : coe_fn conj ↑x = ↑x :=
  quaternion_algebra.conj_coe x

@[simp] theorem conj_smul {R : Type u_1} [comm_ring R] (r : R) (a : quaternion R) :
    coe_fn conj (r • a) = r • coe_fn conj a :=
  quaternion_algebra.conj_smul r a

@[simp] theorem conj_one {R : Type u_1} [comm_ring R] : coe_fn conj 1 = 1 := conj_coe 1

theorem eq_re_of_eq_coe {R : Type u_1} [comm_ring R] {a : quaternion R} {x : R} (h : a = ↑x) :
    a = ↑(re a) :=
  quaternion_algebra.eq_re_of_eq_coe h

theorem eq_re_iff_mem_range_coe {R : Type u_1} [comm_ring R] {a : quaternion R} :
    a = ↑(re a) ↔ a ∈ set.range coe :=
  quaternion_algebra.eq_re_iff_mem_range_coe

@[simp] theorem conj_fixed {R : Type u_1} [comm_ring R] [no_zero_divisors R] [char_zero R]
    {a : quaternion R} : coe_fn conj a = a ↔ a = ↑(re a) :=
  quaternion_algebra.conj_fixed

theorem conj_mul_eq_coe {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj a * a = ↑(re (coe_fn conj a * a)) :=
  quaternion_algebra.conj_mul_eq_coe a

theorem mul_conj_eq_coe {R : Type u_1} [comm_ring R] (a : quaternion R) :
    a * coe_fn conj a = ↑(re (a * coe_fn conj a)) :=
  quaternion_algebra.mul_conj_eq_coe a

@[simp] theorem conj_zero {R : Type u_1} [comm_ring R] : coe_fn conj 0 = 0 :=
  quaternion_algebra.conj_zero

@[simp] theorem conj_neg {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj (-a) = -coe_fn conj a :=
  quaternion_algebra.conj_neg a

@[simp] theorem conj_sub {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    coe_fn conj (a - b) = coe_fn conj a - coe_fn conj b :=
  quaternion_algebra.conj_sub a b

/-- Quaternion conjugate as an `alg_equiv` to the opposite ring. -/
def conj_alg_equiv {R : Type u_1} [comm_ring R] : alg_equiv R (quaternion R) (quaternion Rᵒᵖ) :=
  quaternion_algebra.conj_alg_equiv

@[simp] theorem coe_conj_alg_equiv {R : Type u_1} [comm_ring R] :
    ⇑conj_alg_equiv = opposite.op ∘ ⇑conj :=
  rfl

/-- Square of the norm. -/
def norm_sq {R : Type u_1} [comm_ring R] : monoid_with_zero_hom (quaternion R) R :=
  monoid_with_zero_hom.mk (fun (a : quaternion R) => re (a * coe_fn conj a)) sorry sorry sorry

theorem norm_sq_def {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn norm_sq a = re (a * coe_fn conj a) :=
  rfl

theorem norm_sq_def' {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn norm_sq a = re a ^ bit0 1 + im_i a ^ bit0 1 + im_j a ^ bit0 1 + im_k a ^ bit0 1 :=
  sorry

theorem norm_sq_coe {R : Type u_1} [comm_ring R] (x : R) : coe_fn norm_sq ↑x = x ^ bit0 1 := sorry

@[simp] theorem norm_sq_neg {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn norm_sq (-a) = coe_fn norm_sq a :=
  sorry

theorem self_mul_conj {R : Type u_1} [comm_ring R] (a : quaternion R) :
    a * coe_fn conj a = ↑(coe_fn norm_sq a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a * coe_fn conj a = ↑(coe_fn norm_sq a))) (mul_conj_eq_coe a)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (↑(re (a * coe_fn conj a)) = ↑(coe_fn norm_sq a))) (norm_sq_def a)))
      (Eq.refl ↑(re (a * coe_fn conj a))))

theorem conj_mul_self {R : Type u_1} [comm_ring R] (a : quaternion R) :
    coe_fn conj a * a = ↑(coe_fn norm_sq a) :=
  sorry

theorem coe_norm_sq_add {R : Type u_1} [comm_ring R] (a : quaternion R) (b : quaternion R) :
    ↑(coe_fn norm_sq (a + b)) =
        ↑(coe_fn norm_sq a) + a * coe_fn conj b + b * coe_fn conj a + ↑(coe_fn norm_sq b) :=
  sorry

end quaternion

namespace quaternion

@[simp] theorem norm_sq_eq_zero {R : Type u_1} [linear_ordered_comm_ring R] {a : quaternion R} :
    coe_fn norm_sq a = 0 ↔ a = 0 :=
  sorry

theorem norm_sq_ne_zero {R : Type u_1} [linear_ordered_comm_ring R] {a : quaternion R} :
    coe_fn norm_sq a ≠ 0 ↔ a ≠ 0 :=
  not_congr norm_sq_eq_zero

@[simp] theorem norm_sq_nonneg {R : Type u_1} [linear_ordered_comm_ring R] {a : quaternion R} :
    0 ≤ coe_fn norm_sq a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ coe_fn norm_sq a)) (norm_sq_def' a)))
    (add_nonneg
      (add_nonneg (add_nonneg (pow_two_nonneg (re a)) (pow_two_nonneg (im_i a)))
        (pow_two_nonneg (im_j a)))
      (pow_two_nonneg (im_k a)))

@[simp] theorem norm_sq_le_zero {R : Type u_1} [linear_ordered_comm_ring R] {a : quaternion R} :
    coe_fn norm_sq a ≤ 0 ↔ a = 0 :=
  sorry

protected instance domain {R : Type u_1} [linear_ordered_comm_ring R] : domain (quaternion R) :=
  domain.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry
    ring.one sorry sorry sorry sorry sorry sorry

theorem has_inv_inv {R : Type u_1} [linear_ordered_field R] (a : quaternion R) :
    a⁻¹ = coe_fn norm_sq a⁻¹ • coe_fn conj a :=
  Eq.refl (a⁻¹)

protected instance division_ring {R : Type u_1} [linear_ordered_field R] :
    division_ring (quaternion R) :=
  division_ring.mk domain.add sorry domain.zero sorry sorry domain.neg domain.sub sorry sorry
    domain.mul sorry domain.one sorry sorry sorry sorry has_inv.inv
    (div_inv_monoid.div._default domain.mul sorry domain.one sorry sorry has_inv.inv) sorry sorry
    sorry

@[simp] theorem norm_sq_inv {R : Type u_1} [linear_ordered_field R] (a : quaternion R) :
    coe_fn norm_sq (a⁻¹) = (coe_fn norm_sq a⁻¹) :=
  monoid_with_zero_hom.map_inv' norm_sq a

@[simp] theorem norm_sq_div {R : Type u_1} [linear_ordered_field R] (a : quaternion R)
    (b : quaternion R) : coe_fn norm_sq (a / b) = coe_fn norm_sq a / coe_fn norm_sq b :=
  monoid_with_zero_hom.map_div norm_sq a b

end Mathlib