/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri, Sebastien Gouezel, Heather Macbeth, Patrick Massot, Floris van Doorn

! This file was ported from Lean 3 source module topology.vector_bundle.basic
! 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.Analysis.NormedSpace.BoundedLinearMaps
import Mathbin.Topology.FiberBundle.Basic

/-!
# Vector bundles

In this file we define (topological) vector bundles.

Let `B` be the base space, let `F` be a normed space over a normed field `R`, and let
`E : B → Type*` be a `fiber_bundle` with fiber `F`, in which, for each `x`, the fiber `E x` is a
topological vector space over `R`.

To have a vector bundle structure on `bundle.total_space E`, one should additionally have the
following properties:

* The bundle trivializations in the trivialization atlas should be continuous linear equivs in the
fibers;
* For any two trivializations `e`, `e'` in the atlas the transition function considered as a map
from `B` into `F →L[R] F` is continuous on `e.base_set ∩ e'.base_set` with respect to the operator
norm topology on `F →L[R] F`.

If these conditions are satisfied, we register the typeclass `vector_bundle R F E`.

We define constructions on vector bundles like pullbacks and direct sums in other files.

## Implementation notes

The implementation choices in the vector bundle definition are discussed in the "Implementation
notes" section of `topology.fiber_bundle.basic`.

## Tags
Vector bundle
-/

noncomputable section

open Bundle Set

open Classical Bundle

variable (R 𝕜 : Type _) {B : Type _} (F : Type _) (E : B → Type _)

section TopologicalVectorSpace

variable {B F E} [Semiring R] [TopologicalSpace F] [TopologicalSpace B]

/-- A mixin class for `pretrivialization`, stating that a pretrivialization is fiberwise linear with
respect to given module structures on its fibers and the model fiber. -/
protected class Pretrivialization.IsLinear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)]
  [∀ x, Module R (E x)] (e : Pretrivialization F (π E)) : Prop where
  linear : ∀ b ∈ e.baseSet, IsLinearMap R fun x : E b => (e (totalSpaceMk b x)).2
#align pretrivialization.is_linear Pretrivialization.IsLinear

namespace Pretrivialization

variable {F E} (e : Pretrivialization F (π E)) {x : TotalSpace E} {b : B} {y : E b}

theorem linear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]
    [e.isLinear R] {b : B} (hb : b ∈ e.baseSet) :
    IsLinearMap R fun x : E b => (e (totalSpaceMk b x)).2 :=
  Pretrivialization.IsLinear.linear b hb
#align pretrivialization.linear Pretrivialization.linear

variable [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]

/-- A fiberwise linear inverse to `e`. -/
@[simps]
protected def symmₗ (e : Pretrivialization F (π E)) [e.isLinear R] (b : B) : F →ₗ[R] E b :=
  by
  refine' IsLinearMap.mk' (e.symm b) _
  by_cases hb : b ∈ e.base_set
  ·
    exact
      (((e.linear R hb).mk' _).inverse (e.symm b) (e.symm_apply_apply_mk hb) fun v =>
          congr_arg Prod.snd <| e.apply_mk_symm hb v).isLinear
  · rw [e.coe_symm_of_not_mem hb]
    exact (0 : F →ₗ[R] E b).isLinear
#align pretrivialization.symmₗ Pretrivialization.symmₗ

/-- A pretrivialization for a vector bundle defines linear equivalences between the
fibers and the model space. -/
@[simps (config := { fullyApplied := false })]
def linearEquivAt (e : Pretrivialization F (π E)) [e.isLinear R] (b : B) (hb : b ∈ e.baseSet) :
    E b ≃ₗ[R] F where
  toFun y := (e (totalSpaceMk b y)).2
  invFun := e.symm b
  left_inv := e.symm_apply_apply_mk hb
  right_inv v := by simp_rw [e.apply_mk_symm hb v]
  map_add' v w := (e.linear R hb).map_add v w
  map_smul' c v := (e.linear R hb).map_smul c v
#align pretrivialization.linear_equiv_at Pretrivialization.linearEquivAt

/-- A fiberwise linear map equal to `e` on `e.base_set`. -/
protected def linearMapAt (e : Pretrivialization F (π E)) [e.isLinear R] (b : B) : E b →ₗ[R] F :=
  if hb : b ∈ e.baseSet then e.linearEquivAt R b hb else 0
#align pretrivialization.linear_map_at Pretrivialization.linearMapAt

variable {R}

theorem coe_linearMapAt (e : Pretrivialization F (π E)) [e.isLinear R] (b : B) :
    ⇑(e.linearMapAt R b) = fun y => if b ∈ e.baseSet then (e (totalSpaceMk b y)).2 else 0 :=
  by
  rw [Pretrivialization.linearMapAt]
  split_ifs <;> rfl
#align pretrivialization.coe_linear_map_at Pretrivialization.coe_linearMapAt

theorem coe_linearMapAt_of_mem (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) : ⇑(e.linearMapAt R b) = fun y => (e (totalSpaceMk b y)).2 := by
  simp_rw [coe_linear_map_at, if_pos hb]
#align pretrivialization.coe_linear_map_at_of_mem Pretrivialization.coe_linearMapAt_of_mem

theorem linearMapAt_apply (e : Pretrivialization F (π E)) [e.isLinear R] {b : B} (y : E b) :
    e.linearMapAt R b y = if b ∈ e.baseSet then (e (totalSpaceMk b y)).2 else 0 := by
  rw [coe_linear_map_at]
#align pretrivialization.linear_map_at_apply Pretrivialization.linearMapAt_apply

theorem linearMapAt_def_of_mem (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) : e.linearMapAt R b = e.linearEquivAt R b hb :=
  dif_pos hb
#align pretrivialization.linear_map_at_def_of_mem Pretrivialization.linearMapAt_def_of_mem

theorem linearMapAt_def_of_not_mem (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∉ e.baseSet) : e.linearMapAt R b = 0 :=
  dif_neg hb
#align pretrivialization.linear_map_at_def_of_not_mem Pretrivialization.linearMapAt_def_of_not_mem

theorem linearMapAt_eq_zero (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∉ e.baseSet) : e.linearMapAt R b = 0 :=
  dif_neg hb
#align pretrivialization.linear_map_at_eq_zero Pretrivialization.linearMapAt_eq_zero

theorem symmₗ_linearMapAt (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) (y : E b) : e.symmₗ R b (e.linearMapAt R b y) = y :=
  by
  rw [e.linear_map_at_def_of_mem hb]
  exact (e.linear_equiv_at R b hb).left_inv y
#align pretrivialization.symmₗ_linear_map_at Pretrivialization.symmₗ_linearMapAt

theorem linearMapAt_symmₗ (e : Pretrivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) (y : F) : e.linearMapAt R b (e.symmₗ R b y) = y :=
  by
  rw [e.linear_map_at_def_of_mem hb]
  exact (e.linear_equiv_at R b hb).right_inv y
#align pretrivialization.linear_map_at_symmₗ Pretrivialization.linearMapAt_symmₗ

end Pretrivialization

variable (R) [TopologicalSpace (TotalSpace E)]

/-- A mixin class for `trivialization`, stating that a trivialization is fiberwise linear with
respect to given module structures on its fibers and the model fiber. -/
protected class Trivialization.IsLinear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)]
  [∀ x, Module R (E x)] (e : Trivialization F (π E)) : Prop where
  linear : ∀ b ∈ e.baseSet, IsLinearMap R fun x : E b => (e (totalSpaceMk b x)).2
#align trivialization.is_linear Trivialization.IsLinear

namespace Trivialization

variable (e : Trivialization F (π E)) {x : TotalSpace E} {b : B} {y : E b}

protected theorem linear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)]
    [∀ x, Module R (E x)] [e.isLinear R] {b : B} (hb : b ∈ e.baseSet) :
    IsLinearMap R fun y : E b => (e (totalSpaceMk b y)).2 :=
  Trivialization.IsLinear.linear b hb
#align trivialization.linear Trivialization.linear

instance toPretrivialization.isLinear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)]
    [∀ x, Module R (E x)] [e.isLinear R] : e.toPretrivialization.isLinear R :=
  { (‹_› : e.isLinear R) with }
#align trivialization.to_pretrivialization.is_linear Trivialization.toPretrivialization.isLinear

variable [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]

/-- A trivialization for a vector bundle defines linear equivalences between the
fibers and the model space. -/
def linearEquivAt (e : Trivialization F (π E)) [e.isLinear R] (b : B) (hb : b ∈ e.baseSet) :
    E b ≃ₗ[R] F :=
  e.toPretrivialization.linearEquivAt R b hb
#align trivialization.linear_equiv_at Trivialization.linearEquivAt

variable {R}

@[simp]
theorem linearEquivAt_apply (e : Trivialization F (π E)) [e.isLinear R] (b : B) (hb : b ∈ e.baseSet)
    (v : E b) : e.linearEquivAt R b hb v = (e (totalSpaceMk b v)).2 :=
  rfl
#align trivialization.linear_equiv_at_apply Trivialization.linearEquivAt_apply

@[simp]
theorem linearEquivAt_symm_apply (e : Trivialization F (π E)) [e.isLinear R] (b : B)
    (hb : b ∈ e.baseSet) (v : F) : (e.linearEquivAt R b hb).symm v = e.symm b v :=
  rfl
#align trivialization.linear_equiv_at_symm_apply Trivialization.linearEquivAt_symm_apply

variable (R)

/-- A fiberwise linear inverse to `e`. -/
protected def symmₗ (e : Trivialization F (π E)) [e.isLinear R] (b : B) : F →ₗ[R] E b :=
  e.toPretrivialization.symmₗ R b
#align trivialization.symmₗ Trivialization.symmₗ

variable {R}

theorem coe_symmₗ (e : Trivialization F (π E)) [e.isLinear R] (b : B) : ⇑(e.symmₗ R b) = e.symm b :=
  rfl
#align trivialization.coe_symmₗ Trivialization.coe_symmₗ

variable (R)

/-- A fiberwise linear map equal to `e` on `e.base_set`. -/
protected def linearMapAt (e : Trivialization F (π E)) [e.isLinear R] (b : B) : E b →ₗ[R] F :=
  e.toPretrivialization.linearMapAt R b
#align trivialization.linear_map_at Trivialization.linearMapAt

variable {R}

theorem coe_linearMapAt (e : Trivialization F (π E)) [e.isLinear R] (b : B) :
    ⇑(e.linearMapAt R b) = fun y => if b ∈ e.baseSet then (e (totalSpaceMk b y)).2 else 0 :=
  e.toPretrivialization.coe_linearMapAt b
#align trivialization.coe_linear_map_at Trivialization.coe_linearMapAt

theorem coe_linearMapAt_of_mem (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) : ⇑(e.linearMapAt R b) = fun y => (e (totalSpaceMk b y)).2 := by
  simp_rw [coe_linear_map_at, if_pos hb]
#align trivialization.coe_linear_map_at_of_mem Trivialization.coe_linearMapAt_of_mem

theorem linearMapAt_apply (e : Trivialization F (π E)) [e.isLinear R] {b : B} (y : E b) :
    e.linearMapAt R b y = if b ∈ e.baseSet then (e (totalSpaceMk b y)).2 else 0 := by
  rw [coe_linear_map_at]
#align trivialization.linear_map_at_apply Trivialization.linearMapAt_apply

theorem linearMapAt_def_of_mem (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) : e.linearMapAt R b = e.linearEquivAt R b hb :=
  dif_pos hb
#align trivialization.linear_map_at_def_of_mem Trivialization.linearMapAt_def_of_mem

theorem linearMapAt_def_of_not_mem (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∉ e.baseSet) : e.linearMapAt R b = 0 :=
  dif_neg hb
#align trivialization.linear_map_at_def_of_not_mem Trivialization.linearMapAt_def_of_not_mem

theorem symmₗ_linearMapAt (e : Trivialization F (π E)) [e.isLinear R] {b : B} (hb : b ∈ e.baseSet)
    (y : E b) : e.symmₗ R b (e.linearMapAt R b y) = y :=
  e.toPretrivialization.symmₗ_linearMapAt hb y
#align trivialization.symmₗ_linear_map_at Trivialization.symmₗ_linearMapAt

theorem linearMapAt_symmₗ (e : Trivialization F (π E)) [e.isLinear R] {b : B} (hb : b ∈ e.baseSet)
    (y : F) : e.linearMapAt R b (e.symmₗ R b y) = y :=
  e.toPretrivialization.linearMapAt_symmₗ hb y
#align trivialization.linear_map_at_symmₗ Trivialization.linearMapAt_symmₗ

variable (R)

/-- A coordinate change function between two trivializations, as a continuous linear equivalence.
  Defined to be the identity when `b` does not lie in the base set of both trivializations. -/
def coordChangeL (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] (b : B) :
    F ≃L[R] F :=
  {
    if hb : b ∈ e.baseSet ∩ e'.baseSet then
      (e.linearEquivAt R b (hb.1 : _)).symm.trans (e'.linearEquivAt R b hb.2)
    else
      LinearEquiv.refl R
        F with
    continuous_toFun := by
      by_cases hb : b ∈ e.base_set ∩ e'.base_set
      · simp_rw [dif_pos hb]
        refine' (e'.continuous_on.comp_continuous _ _).snd
        exact
          e.continuous_on_symm.comp_continuous (Continuous.Prod.mk b) fun y =>
            mk_mem_prod hb.1 (mem_univ y)
        exact fun y => e'.mem_source.mpr hb.2
      · rw [dif_neg hb]
        exact continuous_id
    continuous_invFun := by
      by_cases hb : b ∈ e.base_set ∩ e'.base_set
      · simp_rw [dif_pos hb]
        refine' (e.continuous_on.comp_continuous _ _).snd
        exact
          e'.continuous_on_symm.comp_continuous (Continuous.Prod.mk b) fun y =>
            mk_mem_prod hb.2 (mem_univ y)
        exact fun y => e.mem_source.mpr hb.1
      · rw [dif_neg hb]
        exact continuous_id }
#align trivialization.coord_changeL Trivialization.coordChangeL

variable {R}

theorem coe_coordChangeL (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) :
    ⇑(coordChangeL R e e' b) = (e.linearEquivAt R b hb.1).symm.trans (e'.linearEquivAt R b hb.2) :=
  congr_arg LinearEquiv.toFun (dif_pos hb)
#align trivialization.coe_coord_changeL Trivialization.coe_coordChangeL

theorem coe_coord_changeL' (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) :
    (coordChangeL R e e' b).toLinearEquiv =
      (e.linearEquivAt R b hb.1).symm.trans (e'.linearEquivAt R b hb.2) :=
  LinearEquiv.coe_injective (coe_coordChangeL _ _ _)
#align trivialization.coe_coord_changeL' Trivialization.coe_coord_changeL'

theorem symm_coordChangeL (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e'.baseSet ∩ e.baseSet) : (e.coordChangeL R e' b).symm = e'.coordChangeL R e b :=
  by
  apply ContinuousLinearEquiv.toLinearEquiv_injective
  rw [coe_coord_changeL' e' e hb, (coord_changeL R e e' b).symm_toLinearEquiv,
    coe_coord_changeL' e e' hb.symm, LinearEquiv.trans_symm, LinearEquiv.symm_symm]
#align trivialization.symm_coord_changeL Trivialization.symm_coordChangeL

theorem coordChangeL_apply (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) (y : F) :
    coordChangeL R e e' b y = (e' (totalSpaceMk b (e.symm b y))).2 :=
  congr_arg (fun f => LinearEquiv.toFun f y) (dif_pos hb)
#align trivialization.coord_changeL_apply Trivialization.coordChangeL_apply

theorem mk_coordChangeL (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) (y : F) :
    (b, coordChangeL R e e' b y) = e' (totalSpaceMk b (e.symm b y)) :=
  by
  ext
  · rw [e.mk_symm hb.1 y, e'.coe_fst', e.proj_symm_apply' hb.1]
    rw [e.proj_symm_apply' hb.1]
    exact hb.2
  · exact e.coord_changeL_apply e' hb y
#align trivialization.mk_coord_changeL Trivialization.mk_coordChangeL

theorem apply_symm_apply_eq_coordChangeL (e e' : Trivialization F (π E)) [e.isLinear R]
    [e'.isLinear R] {b : B} (hb : b ∈ e.baseSet ∩ e'.baseSet) (v : F) :
    e' (e.toLocalHomeomorph.symm (b, v)) = (b, e.coordChangeL R e' b v) := by
  rw [e.mk_coord_changeL e' hb, e.mk_symm hb.1]
#align trivialization.apply_symm_apply_eq_coord_changeL Trivialization.apply_symm_apply_eq_coordChangeL

/-- A version of `coord_change_apply` that fully unfolds `coord_change`. The right-hand side is
ugly, but has good definitional properties for specifically defined trivializations. -/
theorem coordChangeL_apply' (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R] {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) (y : F) :
    coordChangeL R e e' b y = (e' (e.toLocalHomeomorph.symm (b, y))).2 := by
  rw [e.coord_changeL_apply e' hb, e.mk_symm hb.1]
#align trivialization.coord_changeL_apply' Trivialization.coordChangeL_apply'

theorem coordChangeL_symm_apply (e e' : Trivialization F (π E)) [e.isLinear R] [e'.isLinear R]
    {b : B} (hb : b ∈ e.baseSet ∩ e'.baseSet) :
    ⇑(coordChangeL R e e' b).symm =
      (e'.linearEquivAt R b hb.2).symm.trans (e.linearEquivAt R b hb.1) :=
  congr_arg LinearEquiv.invFun (dif_pos hb)
#align trivialization.coord_changeL_symm_apply Trivialization.coordChangeL_symm_apply

end Trivialization

end TopologicalVectorSpace

section

namespace Bundle

/-- The zero section of a vector bundle -/
def zeroSection [∀ x, Zero (E x)] : B → TotalSpace E := fun x => totalSpaceMk x 0
#align bundle.zero_section Bundle.zeroSection

@[simp, mfld_simps]
theorem zeroSection_proj [∀ x, Zero (E x)] (x : B) : (zeroSection E x).proj = x :=
  rfl
#align bundle.zero_section_proj Bundle.zeroSection_proj

@[simp, mfld_simps]
theorem zeroSection_snd [∀ x, Zero (E x)] (x : B) : (zeroSection E x).2 = 0 :=
  rfl
#align bundle.zero_section_snd Bundle.zeroSection_snd

end Bundle

open Bundle

variable [NontriviallyNormedField R] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]
  [NormedAddCommGroup F] [NormedSpace R F] [TopologicalSpace B] [TopologicalSpace (TotalSpace E)]
  [∀ x, TopologicalSpace (E x)] [FiberBundle F E]

/- ./././Mathport/Syntax/Translate/Command.lean:388:30: infer kinds are unsupported in Lean 4: #[`continuousOn_coord_change'] [] -/
/-- The space `total_space E` (for `E : B → Type*` such that each `E x` is a topological vector
space) has a topological vector space structure with fiber `F` (denoted with
`vector_bundle R F E`) if around every point there is a fiber bundle trivialization
which is linear in the fibers. -/
class VectorBundle : Prop where
  trivialization_linear' : ∀ (e : Trivialization F (π E)) [MemTrivializationAtlas e], e.isLinear R
  continuousOn_coord_change' :
    ∀ (e e' : Trivialization F (π E)) [MemTrivializationAtlas e] [MemTrivializationAtlas e'],
      ContinuousOn (fun b => Trivialization.coordChangeL R e e' b : B → F →L[R] F)
        (e.baseSet ∩ e'.baseSet)
#align vector_bundle VectorBundle

variable {F E}

instance (priority := 100) trivialization_linear [VectorBundle R F E] (e : Trivialization F (π E))
    [MemTrivializationAtlas e] : e.isLinear R :=
  VectorBundle.trivialization_linear' e
#align trivialization_linear trivialization_linear

theorem continuousOn_coord_change [VectorBundle R F E] (e e' : Trivialization F (π E))
    [he : MemTrivializationAtlas e] [he' : MemTrivializationAtlas e'] :
    ContinuousOn (fun b => Trivialization.coordChangeL R e e' b : B → F →L[R] F)
      (e.baseSet ∩ e'.baseSet) :=
  VectorBundle.continuousOn_coord_change' R e e'
#align continuous_on_coord_change continuousOn_coord_change

namespace Trivialization

/-- Forward map of `continuous_linear_equiv_at` (only propositionally equal),
  defined everywhere (`0` outside domain). -/
@[simps (config := { fullyApplied := false }) apply]
def continuousLinearMapAt (e : Trivialization F (π E)) [e.isLinear R] (b : B) : E b →L[R] F :=
  {-- given explicitly to help `simps`
        e.linearMapAt
      R b with
    toFun := e.linearMapAt R b
    cont := by
      dsimp
      rw [e.coe_linear_map_at b]
      refine' continuous_if_const _ (fun hb => _) fun _ => continuous_zero
      exact
        continuous_snd.comp
          (e.continuous_on.comp_continuous (FiberBundle.totalSpaceMk_inducing F E b).Continuous
            fun x => e.mem_source.mpr hb) }
#align trivialization.continuous_linear_map_at Trivialization.continuousLinearMapAt

/-- Backwards map of `continuous_linear_equiv_at`, defined everywhere. -/
@[simps (config := { fullyApplied := false }) apply]
def symmL (e : Trivialization F (π E)) [e.isLinear R] (b : B) : F →L[R] E b :=
  {-- given explicitly to help `simps`
        e.symmₗ
      R b with
    toFun := e.symm b
    cont := by
      by_cases hb : b ∈ e.base_set
      · rw [(FiberBundle.totalSpaceMk_inducing F E b).continuous_iff]
        exact
          e.continuous_on_symm.comp_continuous (continuous_const.prod_mk continuous_id) fun x =>
            mk_mem_prod hb (mem_univ x)
      · refine' continuous_zero.congr fun x => (e.symm_apply_of_not_mem hb x).symm }
#align trivialization.symmL Trivialization.symmL

variable {R}

theorem symmL_continuousLinearMapAt (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) (y : E b) : e.symmL R b (e.continuousLinearMapAt R b y) = y :=
  e.symmₗ_linearMapAt hb y
#align trivialization.symmL_continuous_linear_map_at Trivialization.symmL_continuousLinearMapAt

theorem continuousLinearMapAt_symmL (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) (y : F) : e.continuousLinearMapAt R b (e.symmL R b y) = y :=
  e.linearMapAt_symmₗ hb y
#align trivialization.continuous_linear_map_at_symmL Trivialization.continuousLinearMapAt_symmL

variable (R)

/-- In a vector bundle, a trivialization in the fiber (which is a priori only linear)
is in fact a continuous linear equiv between the fibers and the model fiber. -/
@[simps (config := { fullyApplied := false }) apply symm_apply]
def continuousLinearEquivAt (e : Trivialization F (π E)) [e.isLinear R] (b : B)
    (hb : b ∈ e.baseSet) : E b ≃L[R] F :=
  {-- given explicitly to help `simps`
          -- given explicitly to help `simps`
          e.toPretrivialization.linearEquivAt
      R b hb with
    toFun := fun y => (e (totalSpaceMk b y)).2
    invFun := e.symm b
    continuous_toFun :=
      continuous_snd.comp
        (e.ContinuousOn.comp_continuous (FiberBundle.totalSpaceMk_inducing F E b).Continuous
          fun x => e.mem_source.mpr hb)
    continuous_invFun := (e.symmL R b).Continuous }
#align trivialization.continuous_linear_equiv_at Trivialization.continuousLinearEquivAt

variable {R}

theorem coe_continuousLinearEquivAt_eq (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) :
    (e.continuousLinearEquivAt R b hb : E b → F) = e.continuousLinearMapAt R b :=
  (e.coe_linearMapAt_of_mem hb).symm
#align trivialization.coe_continuous_linear_equiv_at_eq Trivialization.coe_continuousLinearEquivAt_eq

theorem symm_continuousLinearEquivAt_eq (e : Trivialization F (π E)) [e.isLinear R] {b : B}
    (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F → E b) = e.symmL R b :=
  rfl
#align trivialization.symm_continuous_linear_equiv_at_eq Trivialization.symm_continuousLinearEquivAt_eq

@[simp]
theorem continuousLinearEquivAt_apply' (e : Trivialization F (π E)) [e.isLinear R]
    (x : TotalSpace E) (hx : x ∈ e.source) :
    e.continuousLinearEquivAt R x.proj (e.mem_source.1 hx) x.2 = (e x).2 :=
  by
  cases x
  rfl
#align trivialization.continuous_linear_equiv_at_apply' Trivialization.continuousLinearEquivAt_apply'

variable (R)

theorem apply_eq_prod_continuousLinearEquivAt (e : Trivialization F (π E)) [e.isLinear R] (b : B)
    (hb : b ∈ e.baseSet) (z : E b) : e ⟨b, z⟩ = (b, e.continuousLinearEquivAt R b hb z) :=
  by
  ext
  · refine' e.coe_fst _
    rw [e.source_eq]
    exact hb
  · simp only [coe_coe, continuous_linear_equiv_at_apply]
#align trivialization.apply_eq_prod_continuous_linear_equiv_at Trivialization.apply_eq_prod_continuousLinearEquivAt

protected theorem zeroSection (e : Trivialization F (π E)) [e.isLinear R] {x : B}
    (hx : x ∈ e.baseSet) : e (zeroSection E x) = (x, 0) := by
  simp_rw [zero_section, total_space_mk, e.apply_eq_prod_continuous_linear_equiv_at R x hx 0,
    map_zero]
#align trivialization.zero_section Trivialization.zeroSection

variable {R}

theorem symm_apply_eq_mk_continuousLinearEquivAt_symm (e : Trivialization F (π E)) [e.isLinear R]
    (b : B) (hb : b ∈ e.baseSet) (z : F) :
    e.toLocalHomeomorph.symm ⟨b, z⟩ = totalSpaceMk b ((e.continuousLinearEquivAt R b hb).symm z) :=
  by
  have h : (b, z) ∈ e.target := by
    rw [e.target_eq]
    exact ⟨hb, mem_univ _⟩
  apply e.inj_on (e.map_target h)
  · simp only [e.source_eq, hb, mem_preimage]
  simp_rw [e.right_inv h, coe_coe, e.apply_eq_prod_continuous_linear_equiv_at R b hb,
    ContinuousLinearEquiv.apply_symm_apply]
#align trivialization.symm_apply_eq_mk_continuous_linear_equiv_at_symm Trivialization.symm_apply_eq_mk_continuousLinearEquivAt_symm

theorem comp_continuousLinearEquivAt_eq_coord_change (e e' : Trivialization F (π E)) [e.isLinear R]
    [e'.isLinear R] {b : B} (hb : b ∈ e.baseSet ∩ e'.baseSet) :
    (e.continuousLinearEquivAt R b hb.1).symm.trans (e'.continuousLinearEquivAt R b hb.2) =
      coordChangeL R e e' b :=
  by
  ext v
  rw [coord_changeL_apply e e' hb]
  rfl
#align trivialization.comp_continuous_linear_equiv_at_eq_coord_change Trivialization.comp_continuousLinearEquivAt_eq_coord_change

end Trivialization

include R F

/-! ### Constructing vector bundles -/

variable (R B F)

/-- Analogous construction of `fiber_bundle_core` for vector bundles. This
construction gives a way to construct vector bundles from a structure registering how
trivialization changes act on fibers. -/
structure VectorBundleCore (ι : Type _) where
  baseSet : ι → Set B
  isOpen_baseSet : ∀ i, IsOpen (base_set i)
  indexAt : B → ι
  mem_baseSet_at : ∀ x, x ∈ base_set (index_at x)
  coordChange : ι → ι → B → F →L[R] F
  coordChange_self : ∀ i, ∀ x ∈ base_set i, ∀ v, coord_change i i x v = v
  continuousOn_coordChange : ∀ i j, ContinuousOn (coord_change i j) (base_set i ∩ base_set j)
  coordChange_comp :
    ∀ i j k,
      ∀ x ∈ base_set i ∩ base_set j ∩ base_set k,
        ∀ v, (coord_change j k x) (coord_change i j x v) = coord_change i k x v
#align vector_bundle_core VectorBundleCore

/-- The trivial vector bundle core, in which all the changes of coordinates are the
identity. -/
def trivialVectorBundleCore (ι : Type _) [Inhabited ι] : VectorBundleCore R B F ι
    where
  baseSet ι := univ
  isOpen_baseSet i := isOpen_univ
  indexAt := default
  mem_baseSet_at x := mem_univ x
  coordChange i j x := ContinuousLinearMap.id R F
  coordChange_self i x hx v := rfl
  coordChange_comp i j k x hx v := rfl
  continuousOn_coordChange i j := continuousOn_const
#align trivial_vector_bundle_core trivialVectorBundleCore

instance (ι : Type _) [Inhabited ι] : Inhabited (VectorBundleCore R B F ι) :=
  ⟨trivialVectorBundleCore R B F ι⟩

namespace VectorBundleCore

variable {R B F} {ι : Type _} (Z : VectorBundleCore R B F ι)

/-- Natural identification to a `fiber_bundle_core`. -/
@[simps (config := mfld_cfg)]
def toFiberBundleCore : FiberBundleCore ι B F :=
  { Z with
    coordChange := fun i j b => Z.coordChange i j b
    continuousOn_coordChange := fun i j =>
      isBoundedBilinearMapApply.Continuous.comp_continuousOn
        ((Z.continuousOn_coordChange i j).Prod_map continuousOn_id) }
#align vector_bundle_core.to_fiber_bundle_core VectorBundleCore.toFiberBundleCore

instance toFiberBundleCoreCoe : Coe (VectorBundleCore R B F ι) (FiberBundleCore ι B F) :=
  ⟨toFiberBundleCore⟩
#align vector_bundle_core.to_fiber_bundle_core_coe VectorBundleCore.toFiberBundleCoreCoe

include Z

theorem coordChange_linear_comp (i j k : ι) :
    ∀ x ∈ Z.baseSet i ∩ Z.baseSet j ∩ Z.baseSet k,
      (Z.coordChange j k x).comp (Z.coordChange i j x) = Z.coordChange i k x :=
  fun x hx => by
  ext v
  exact Z.coord_change_comp i j k x hx v
#align vector_bundle_core.coord_change_linear_comp VectorBundleCore.coordChange_linear_comp

/-- The index set of a vector bundle core, as a convenience function for dot notation -/
@[nolint unused_arguments has_nonempty_instance]
def Index :=
  ι
#align vector_bundle_core.index VectorBundleCore.Index

/-- The base space of a vector bundle core, as a convenience function for dot notation-/
@[nolint unused_arguments, reducible]
def Base :=
  B
#align vector_bundle_core.base VectorBundleCore.Base

/-- The fiber of a vector bundle core, as a convenience function for dot notation and
typeclass inference -/
@[nolint unused_arguments has_nonempty_instance]
def Fiber : B → Type _ :=
  Z.toFiberBundleCore.Fiber
#align vector_bundle_core.fiber VectorBundleCore.Fiber

instance topologicalSpaceFiber (x : B) : TopologicalSpace (Z.Fiber x) := by
  delta_instance vector_bundle_core.fiber
#align vector_bundle_core.topological_space_fiber VectorBundleCore.topologicalSpaceFiber

instance addCommMonoidFiber : ∀ x : B, AddCommMonoid (Z.Fiber x) := by
  dsimp [VectorBundleCore.Fiber] <;> delta_instance fiber_bundle_core.fiber
#align vector_bundle_core.add_comm_monoid_fiber VectorBundleCore.addCommMonoidFiber

instance moduleFiber : ∀ x : B, Module R (Z.Fiber x) := by
  dsimp [VectorBundleCore.Fiber] <;> delta_instance fiber_bundle_core.fiber
#align vector_bundle_core.module_fiber VectorBundleCore.moduleFiber

instance addCommGroupFiber [AddCommGroup F] : ∀ x : B, AddCommGroup (Z.Fiber x) := by
  dsimp [VectorBundleCore.Fiber] <;> delta_instance fiber_bundle_core.fiber
#align vector_bundle_core.add_comm_group_fiber VectorBundleCore.addCommGroupFiber

/-- The projection from the total space of a fiber bundle core, on its base. -/
@[reducible, simp, mfld_simps]
def proj : TotalSpace Z.Fiber → B :=
  TotalSpace.proj
#align vector_bundle_core.proj VectorBundleCore.proj

/-- The total space of the vector bundle, as a convenience function for dot notation.
It is by definition equal to `bundle.total_space Z.fiber`, a.k.a. `Σ x, Z.fiber x` but with a
different name for typeclass inference. -/
@[nolint unused_arguments, reducible]
def TotalSpace :=
  Bundle.TotalSpace Z.Fiber
#align vector_bundle_core.total_space VectorBundleCore.TotalSpace

/-- Local homeomorphism version of the trivialization change. -/
def trivChange (i j : ι) : LocalHomeomorph (B × F) (B × F) :=
  FiberBundleCore.trivChange (↑Z) i j
#align vector_bundle_core.triv_change VectorBundleCore.trivChange

@[simp, mfld_simps]
theorem mem_trivChange_source (i j : ι) (p : B × F) :
    p ∈ (Z.trivChange i j).source ↔ p.1 ∈ Z.baseSet i ∩ Z.baseSet j :=
  FiberBundleCore.mem_trivChange_source (↑Z) i j p
#align vector_bundle_core.mem_triv_change_source VectorBundleCore.mem_trivChange_source

/-- Topological structure on the total space of a vector bundle created from core, designed so
that all the local trivialization are continuous. -/
instance toTopologicalSpace : TopologicalSpace Z.TotalSpace :=
  Z.toFiberBundleCore.toTopologicalSpace
#align vector_bundle_core.to_topological_space VectorBundleCore.toTopologicalSpace

variable (b : B) (a : F)

@[simp, mfld_simps]
theorem coe_coordChange (i j : ι) : Z.toFiberBundleCore.coordChange i j b = Z.coordChange i j b :=
  rfl
#align vector_bundle_core.coe_coord_change VectorBundleCore.coe_coordChange

/-- One of the standard local trivializations of a vector bundle constructed from core, taken by
considering this in particular as a fiber bundle constructed from core. -/
def localTriv (i : ι) : Trivialization F (π Z.Fiber) := by
  dsimp [VectorBundleCore.TotalSpace, VectorBundleCore.Fiber] <;>
    exact Z.to_fiber_bundle_core.local_triv i
#align vector_bundle_core.local_triv VectorBundleCore.localTriv

/-- The standard local trivializations of a vector bundle constructed from core are linear. -/
instance localTriv.isLinear (i : ι) : (Z.localTriv i).isLinear R
    where linear x hx := by
    dsimp [VectorBundleCore.localTriv] <;>
      exact
        { map_add := fun v w => by simp only [ContinuousLinearMap.map_add, mfld_simps]
          map_smul := fun r v => by simp only [ContinuousLinearMap.map_smul, mfld_simps] }
#align vector_bundle_core.local_triv.is_linear VectorBundleCore.localTriv.isLinear

variable (i j : ι)

@[simp, mfld_simps]
theorem mem_localTriv_source (p : Z.TotalSpace) : p ∈ (Z.localTriv i).source ↔ p.1 ∈ Z.baseSet i :=
  by dsimp [VectorBundleCore.Fiber] <;> exact Iff.rfl
#align vector_bundle_core.mem_local_triv_source VectorBundleCore.mem_localTriv_source

@[simp, mfld_simps]
theorem baseSet_at : Z.baseSet i = (Z.localTriv i).baseSet :=
  rfl
#align vector_bundle_core.base_set_at VectorBundleCore.baseSet_at

@[simp, mfld_simps]
theorem localTriv_apply (p : Z.TotalSpace) :
    (Z.localTriv i) p = ⟨p.1, Z.coordChange (Z.indexAt p.1) i p.1 p.2⟩ :=
  rfl
#align vector_bundle_core.local_triv_apply VectorBundleCore.localTriv_apply

@[simp, mfld_simps]
theorem mem_localTriv_target (p : B × F) :
    p ∈ (Z.localTriv i).target ↔ p.1 ∈ (Z.localTriv i).baseSet :=
  Z.toFiberBundleCore.mem_localTriv_target i p
#align vector_bundle_core.mem_local_triv_target VectorBundleCore.mem_localTriv_target

@[simp, mfld_simps]
theorem localTriv_symm_fst (p : B × F) :
    (Z.localTriv i).toLocalHomeomorph.symm p = ⟨p.1, Z.coordChange i (Z.indexAt p.1) p.1 p.2⟩ :=
  rfl
#align vector_bundle_core.local_triv_symm_fst VectorBundleCore.localTriv_symm_fst

@[simp, mfld_simps]
theorem localTriv_symm_apply {b : B} (hb : b ∈ Z.baseSet i) (v : F) :
    (Z.localTriv i).symm b v = Z.coordChange i (Z.indexAt b) b v := by
  apply (Z.local_triv i).symm_apply hb v
#align vector_bundle_core.local_triv_symm_apply VectorBundleCore.localTriv_symm_apply

@[simp, mfld_simps]
theorem localTriv_coordChange_eq {b : B} (hb : b ∈ Z.baseSet i ∩ Z.baseSet j) (v : F) :
    (Z.localTriv i).coordChangeL R (Z.localTriv j) b v = Z.coordChange i j b v :=
  by
  rw [Trivialization.coordChangeL_apply', local_triv_symm_fst, local_triv_apply, coord_change_comp]
  exacts[⟨⟨hb.1, Z.mem_base_set_at b⟩, hb.2⟩, hb]
#align vector_bundle_core.local_triv_coord_change_eq VectorBundleCore.localTriv_coordChange_eq

/-- Preferred local trivialization of a vector bundle constructed from core, at a given point, as
a bundle trivialization -/
def localTrivAt (b : B) : Trivialization F (π Z.Fiber) :=
  Z.localTriv (Z.indexAt b)
#align vector_bundle_core.local_triv_at VectorBundleCore.localTrivAt

@[simp, mfld_simps]
theorem localTrivAt_def : Z.localTriv (Z.indexAt b) = Z.localTrivAt b :=
  rfl
#align vector_bundle_core.local_triv_at_def VectorBundleCore.localTrivAt_def

@[simp, mfld_simps]
theorem mem_source_at : (⟨b, a⟩ : Z.TotalSpace) ∈ (Z.localTrivAt b).source :=
  by
  rw [local_triv_at, mem_local_triv_source]
  exact Z.mem_base_set_at b
#align vector_bundle_core.mem_source_at VectorBundleCore.mem_source_at

@[simp, mfld_simps]
theorem localTrivAt_apply (p : Z.TotalSpace) : (Z.localTrivAt p.1) p = ⟨p.1, p.2⟩ :=
  FiberBundleCore.localTrivAt_apply Z p
#align vector_bundle_core.local_triv_at_apply VectorBundleCore.localTrivAt_apply

@[simp, mfld_simps]
theorem localTrivAt_apply_mk (b : B) (a : F) : (Z.localTrivAt b) ⟨b, a⟩ = ⟨b, a⟩ :=
  Z.localTrivAt_apply _
#align vector_bundle_core.local_triv_at_apply_mk VectorBundleCore.localTrivAt_apply_mk

@[simp, mfld_simps]
theorem mem_localTrivAt_baseSet : b ∈ (Z.localTrivAt b).baseSet :=
  FiberBundleCore.mem_localTrivAt_baseSet Z b
#align vector_bundle_core.mem_local_triv_at_base_set VectorBundleCore.mem_localTrivAt_baseSet

instance fiberBundle : FiberBundle F Z.Fiber :=
  Z.toFiberBundleCore.FiberBundle
#align vector_bundle_core.fiber_bundle VectorBundleCore.fiberBundle

instance vectorBundle : VectorBundle R F Z.Fiber
    where
  trivialization_linear' := by
    rintro _ ⟨i, rfl⟩
    apply local_triv.is_linear
  continuousOn_coord_change' := by
    rintro _ _ ⟨i, rfl⟩ ⟨i', rfl⟩
    refine' (Z.continuous_on_coord_change i i').congr fun b hb => _
    ext v
    exact Z.local_triv_coord_change_eq i i' hb v
#align vector_bundle_core.vector_bundle VectorBundleCore.vectorBundle

/-- The projection on the base of a vector bundle created from core is continuous -/
@[continuity]
theorem continuous_proj : Continuous Z.proj :=
  FiberBundleCore.continuous_proj Z
#align vector_bundle_core.continuous_proj VectorBundleCore.continuous_proj

/-- The projection on the base of a vector bundle created from core is an open map -/
theorem isOpenMap_proj : IsOpenMap Z.proj :=
  FiberBundleCore.isOpenMap_proj Z
#align vector_bundle_core.is_open_map_proj VectorBundleCore.isOpenMap_proj

end VectorBundleCore

end

/-! ### Vector prebundle -/

section

variable [NontriviallyNormedField R] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)]
  [NormedAddCommGroup F] [NormedSpace R F] [TopologicalSpace B]

open TopologicalSpace

open VectorBundle

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (e e' «expr ∈ » pretrivialization_atlas) -/
/-- This structure permits to define a vector bundle when trivializations are given as local
equivalences but there is not yet a topology on the total space or the fibers.
The total space is hence given a topology in such a way that there is a fiber bundle structure for
which the local equivalences are also local homeomorphisms and hence vector bundle trivializations.
The topology on the fibers is induced from the one on the total space.

The field `exists_coord_change` is stated as an existential statement (instead of 3 separate
fields), since it depends on propositional information (namely `e e' ∈ pretrivialization_atlas`).
This makes it inconvenient to explicitly define a `coord_change` function when constructing a
`vector_prebundle`. -/
@[nolint has_nonempty_instance]
structure VectorPrebundle where
  pretrivializationAtlas : Set (Pretrivialization F (π E))
  pretrivialization_linear' :
    ∀ (e : Pretrivialization F (π E)) (he : e ∈ pretrivialization_atlas), e.isLinear R
  pretrivializationAt : B → Pretrivialization F (π E)
  mem_base_pretrivializationAt : ∀ x : B, x ∈ (pretrivialization_at x).baseSet
  pretrivialization_mem_atlas : ∀ x : B, pretrivialization_at x ∈ pretrivialization_atlas
  exists_coord_change :
    ∀ (e) (_ : e ∈ pretrivialization_atlas) (e') (_ : e' ∈ pretrivialization_atlas),
      ∃ f : B → F →L[R] F,
        ContinuousOn f (e.baseSet ∩ e'.baseSet) ∧
          ∀ (b : B) (hb : b ∈ e.baseSet ∩ e'.baseSet) (v : F),
            f b v = (e' (totalSpaceMk b (e.symm b v))).2
#align vector_prebundle VectorPrebundle

namespace VectorPrebundle

variable {R E F}

/-- A randomly chosen coordinate change on a `vector_prebundle`, given by
  the field `exists_coord_change`. -/
def coordChange (a : VectorPrebundle R F E) {e e' : Pretrivialization F (π E)}
    (he : e ∈ a.pretrivializationAtlas) (he' : e' ∈ a.pretrivializationAtlas) (b : B) : F →L[R] F :=
  Classical.choose (a.exists_coord_change e he e' he') b
#align vector_prebundle.coord_change VectorPrebundle.coordChange

theorem continuousOn_coordChange (a : VectorPrebundle R F E) {e e' : Pretrivialization F (π E)}
    (he : e ∈ a.pretrivializationAtlas) (he' : e' ∈ a.pretrivializationAtlas) :
    ContinuousOn (a.coordChange he he') (e.baseSet ∩ e'.baseSet) :=
  (Classical.choose_spec (a.exists_coord_change e he e' he')).1
#align vector_prebundle.continuous_on_coord_change VectorPrebundle.continuousOn_coordChange

theorem coordChange_apply (a : VectorPrebundle R F E) {e e' : Pretrivialization F (π E)}
    (he : e ∈ a.pretrivializationAtlas) (he' : e' ∈ a.pretrivializationAtlas) {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) (v : F) :
    a.coordChange he he' b v = (e' (totalSpaceMk b (e.symm b v))).2 :=
  (Classical.choose_spec (a.exists_coord_change e he e' he')).2 b hb v
#align vector_prebundle.coord_change_apply VectorPrebundle.coordChange_apply

theorem mk_coordChange (a : VectorPrebundle R F E) {e e' : Pretrivialization F (π E)}
    (he : e ∈ a.pretrivializationAtlas) (he' : e' ∈ a.pretrivializationAtlas) {b : B}
    (hb : b ∈ e.baseSet ∩ e'.baseSet) (v : F) :
    (b, a.coordChange he he' b v) = e' (totalSpaceMk b (e.symm b v)) :=
  by
  ext
  · rw [e.mk_symm hb.1 v, e'.coe_fst', e.proj_symm_apply' hb.1]
    rw [e.proj_symm_apply' hb.1]
    exact hb.2
  · exact a.coord_change_apply he he' hb v
#align vector_prebundle.mk_coord_change VectorPrebundle.mk_coordChange

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Natural identification of `vector_prebundle` as a `fiber_prebundle`. -/
def toFiberPrebundle (a : VectorPrebundle R F E) : FiberPrebundle F E :=
  { a with
    continuous_triv_change := by
      intro e he e' he'
      have :=
        is_bounded_bilinear_map_apply.continuous.comp_continuous_on
          ((a.continuous_on_coord_change he' he).Prod_map continuousOn_id)
      have H :
        e'.to_local_equiv.target ∩ e'.to_local_equiv.symm ⁻¹' e.to_local_equiv.source =
          (e'.base_set ∩ e.base_set) ×ˢ univ :=
        by
        rw [e'.target_eq, e.source_eq]
        ext ⟨b, f⟩
        simp (config := { contextual := true }) only [-total_space.proj, and_congr_right_iff,
          e'.proj_symm_apply', iff_self_iff, imp_true_iff, mfld_simps]
      rw [H]
      refine' (continuous_on_fst.prod this).congr _
      rintro ⟨b, f⟩ ⟨hb, -⟩
      dsimp only [Function.comp, Prod.map]
      rw [a.mk_coord_change _ _ hb, e'.mk_symm hb.1]
      rfl }
#align vector_prebundle.to_fiber_prebundle VectorPrebundle.toFiberPrebundle

/-- Topology on the total space that will make the prebundle into a bundle. -/
def totalSpaceTopology (a : VectorPrebundle R F E) : TopologicalSpace (TotalSpace E) :=
  a.toFiberPrebundle.totalSpaceTopology
#align vector_prebundle.total_space_topology VectorPrebundle.totalSpaceTopology

/-- Promotion from a `trivialization` in the `pretrivialization_atlas` of a
`vector_prebundle` to a `trivialization`. -/
def trivializationOfMemPretrivializationAtlas (a : VectorPrebundle R F E)
    {e : Pretrivialization F (π E)} (he : e ∈ a.pretrivializationAtlas) :
    @Trivialization B F _ _ _ a.totalSpaceTopology (π E) :=
  a.toFiberPrebundle.trivializationOfMemPretrivializationAtlas he
#align vector_prebundle.trivialization_of_mem_pretrivialization_atlas VectorPrebundle.trivializationOfMemPretrivializationAtlas

theorem linear_of_mem_pretrivializationAtlas (a : VectorPrebundle R F E)
    {e : Pretrivialization F (π E)} (he : e ∈ a.pretrivializationAtlas) :
    @Trivialization.IsLinear R B F _ _ _ _ a.totalSpaceTopology _ _ _ _
      (trivializationOfMemPretrivializationAtlas a he) :=
  { linear := (a.pretrivialization_linear' e he).linear }
#align vector_prebundle.linear_of_mem_pretrivialization_atlas VectorPrebundle.linear_of_mem_pretrivializationAtlas

variable (a : VectorPrebundle R F E)

theorem mem_trivialization_at_source (b : B) (x : E b) :
    totalSpaceMk b x ∈ (a.pretrivializationAt b).source :=
  a.toFiberPrebundle.mem_pretrivializationAt_source b x
#align vector_prebundle.mem_trivialization_at_source VectorPrebundle.mem_trivialization_at_source

@[simp]
theorem totalSpaceMk_preimage_source (b : B) :
    totalSpaceMk b ⁻¹' (a.pretrivializationAt b).source = univ :=
  a.toFiberPrebundle.totalSpaceMk_preimage_source b
#align vector_prebundle.total_space_mk_preimage_source VectorPrebundle.totalSpaceMk_preimage_source

/-- Topology on the fibers `E b` induced by the map `E b → E.total_space`. -/
def fiberTopology (b : B) : TopologicalSpace (E b) :=
  a.toFiberPrebundle.fiberTopology b
#align vector_prebundle.fiber_topology VectorPrebundle.fiberTopology

@[continuity]
theorem inducing_totalSpaceMk (b : B) :
    @Inducing _ _ (a.fiberTopology b) a.totalSpaceTopology (totalSpaceMk b) :=
  a.toFiberPrebundle.inducing_totalSpaceMk b
#align vector_prebundle.inducing_total_space_mk VectorPrebundle.inducing_totalSpaceMk

@[continuity]
theorem continuous_totalSpaceMk (b : B) :
    @Continuous _ _ (a.fiberTopology b) a.totalSpaceTopology (totalSpaceMk b) :=
  a.toFiberPrebundle.continuous_totalSpaceMk b
#align vector_prebundle.continuous_total_space_mk VectorPrebundle.continuous_totalSpaceMk

/-- Make a `fiber_bundle` from a `vector_prebundle`; auxiliary construction for
`vector_prebundle.vector_bundle`. -/
def toFiberBundle : @FiberBundle B F _ _ _ a.totalSpaceTopology a.fiberTopology :=
  a.toFiberPrebundle.toFiberBundle
#align vector_prebundle.to_fiber_bundle VectorPrebundle.toFiberBundle

/-- Make a `vector_bundle` from a `vector_prebundle`.  Concretely this means
that, given a `vector_prebundle` structure for a sigma-type `E` -- which consists of a
number of "pretrivializations" identifying parts of `E` with product spaces `U × F` -- one
establishes that for the topology constructed on the sigma-type using
`vector_prebundle.total_space_topology`, these "pretrivializations" are actually
"trivializations" (i.e., homeomorphisms with respect to the constructed topology). -/
theorem to_vectorBundle :
    @VectorBundle R _ F E _ _ _ _ _ _ a.totalSpaceTopology a.fiberTopology a.toFiberBundle :=
  { trivialization_linear' := by
      rintro _ ⟨e, he, rfl⟩
      apply linear_of_mem_pretrivialization_atlas
    continuousOn_coord_change' :=
      by
      rintro _ _ ⟨e, he, rfl⟩ ⟨e', he', rfl⟩
      refine' (a.continuous_on_coord_change he he').congr _
      intro b hb
      ext v
      rw [a.coord_change_apply he he' hb v, ContinuousLinearEquiv.coe_coe,
        Trivialization.coordChangeL_apply]
      exacts[rfl, hb] }
#align vector_prebundle.to_vector_bundle VectorPrebundle.to_vectorBundle

end VectorPrebundle

end


import path.loop
import homotopy.path
import homotopy.straight_line

/-!
# Homotopy of Loops

In this file we define what it means for two `loop`s to be homotopic, and show that this is an
equivalence relation. This is important for the definition of the fundamental group.
-/

noncomputable theory

variables {X : Type _} [topological_space X] {x₀ : X}

/--
We define homotopy of loops to just be an abbreviation for a homotopy of paths.
-/
abbreviation loop_homotopy (l₀ l₁ : loop x₀) := path_homotopy l₀ l₁

/--
Two loops are homotopic if there is a homotopy between them.
-/
def loop_homotopic (l₀ l₁ : loop x₀) := nonempty (loop_homotopy l₀ l₁)

lemma loop_homotopic.equiv : equivalence (@loop_homotopic _ _ x₀) := path_homotopic.equiv

instance loop.setoid : setoid (loop x₀) :=
{ r := loop_homotopic,
  iseqv := loop_homotopic.equiv }


import data.real.basic

variables a b c d e f : ℝ

#check mul_comm e
#check mul_assoc f c a
#check sub_self

-- BEGIN
example (a b c d e f : ℝ) (h : b * c = e * f) :
  a * b * c * d = a * e * f * d :=
begin
   rw mul_assoc a b c,
   sorry,
end

example (a b c d : ℝ) (hyp : c = b * a - d) (hyp' : d = a * b) : c = 0 :=
begin
  rw hyp,
  rw hyp',
  sorry,
end
-- END


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Reid Barton, Bhavik Mehta

! This file was ported from Lean 3 source module category_theory.limits.constructions.over.connected
! leanprover-community/mathlib commit d6814c584384ddf2825ff038e868451a7c956f31
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Limits.Creates
import Mathlib.CategoryTheory.Over
import Mathlib.CategoryTheory.IsConnected

/-!
# Connected limits in the over category

Shows that the forgetful functor `Over B ⥤ C` creates connected limits, in particular `Over B` has
any connected limit which `C` has.
-/

universe v u

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

open CategoryTheory CategoryTheory.Limits

variable {J : Type v} [SmallCategory J]

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

variable {X : C}

namespace CategoryTheory.Over

namespace CreatesConnected

/-- (Impl) Given a diagram in the over category, produce a natural transformation from the
diagram legs to the specific object.
-/
def natTransInOver {B : C} (F : J ⥤ Over B) :
    F ⋙ forget B ⟶ (CategoryTheory.Functor.const J).obj B where
  app j := (F.obj j).hom
#align category_theory.over.creates_connected.nat_trans_in_over CategoryTheory.Over.CreatesConnected.natTransInOver

/-- (Impl) Given a cone in the base category, raise it to a cone in the over category. Note this is
where the connected assumption is used.
-/
@[simps]
def raiseCone [IsConnected J] {B : C} {F : J ⥤ Over B} (c : Cone (F ⋙ forget B)) :
    Cone F where
  pt := Over.mk (c.π.app (Classical.arbitrary J) ≫ (F.obj (Classical.arbitrary J)).hom)
  π :=
    { app := fun j =>
        Over.homMk (c.π.app j) (nat_trans_from_is_connected (c.π ≫ natTransInOver F) j _)
      naturality := by
        intro X Y f
        apply CommaMorphism.ext
        · simpa using (c.w f).symm
        · simp }
#align category_theory.over.creates_connected.raise_cone CategoryTheory.Over.CreatesConnected.raiseCone

theorem raised_cone_lowers_to_original [IsConnected J] {B : C} {F : J ⥤ Over B}
    (c : Cone (F ⋙ forget B)) :
  (forget B).mapCone (raiseCone c) = c := by aesop_cat
#align category_theory.over.creates_connected.raised_cone_lowers_to_original CategoryTheory.Over.CreatesConnected.raised_cone_lowers_to_original

/-- (Impl) Show that the raised cone is a limit. -/
def raisedConeIsLimit [IsConnected J] {B : C} {F : J ⥤ Over B} {c : Cone (F ⋙ forget B)}
    (t : IsLimit c) : IsLimit (raiseCone c) where
  lift s :=
    Over.homMk (t.lift ((forget B).mapCone s))
      (by
        dsimp
        simp)
  uniq s m K := by
    ext1
    apply t.hom_ext
    intro j
    simp [← K j]
#align category_theory.over.creates_connected.raised_cone_is_limit CategoryTheory.Over.CreatesConnected.raisedConeIsLimit

end CreatesConnected

/-- The forgetful functor from the over category creates any connected limit. -/
instance forgetCreatesConnectedLimits [IsConnected J] {B : C} :
    CreatesLimitsOfShape J (forget B) where
  CreatesLimit :=
    createsLimitOfReflectsIso fun c t =>
      { liftedCone := CreatesConnected.raiseCone c
        validLift := eqToIso (CreatesConnected.raised_cone_lowers_to_original c)
        makesLimit := CreatesConnected.raisedConeIsLimit t }
#align category_theory.over.forget_creates_connected_limits CategoryTheory.Over.forgetCreatesConnectedLimits

/-- The over category has any connected limit which the original category has. -/
instance has_connected_limits {B : C} [IsConnected J] [HasLimitsOfShape J C] :
    HasLimitsOfShape J (Over B) where has_limit F := hasLimit_of_created F (forget B)
#align category_theory.over.has_connected_limits CategoryTheory.Over.has_connected_limits

end CategoryTheory.Over


/-
Copyright (c) 2020 Fox Thomson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Fox Thomson.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.finset.basic
import Mathlib.PostPort

universes u_1 u v

namespace Mathlib

/-!
# Languages
This file contains the definition and operations on formal languages over an alphabet. Note strings
are implemented as lists over the alphabet.
The operations in this file define a [Kleene algebra](https://en.wikipedia.org/wiki/Kleene_algebra)
over the languages.
-/

/-- A language is a set of strings over an alphabet. -/
def language (α : Type u_1) :=
  set (List α)

namespace language

protected instance has_zero {α : Type u} : HasZero (language α) :=
  { zero := ∅ }

protected instance has_one {α : Type u} : HasOne (language α) :=
  { one := singleton [] }

protected instance inhabited {α : Type u} : Inhabited (language α) :=
  { default := 0 }

protected instance has_add {α : Type u} : Add (language α) :=
  { add := set.union }

protected instance has_mul {α : Type u} : Mul (language α) :=
  { mul := fun (l m : language α) => (fun (p : List α × List α) => prod.fst p ++ prod.snd p) '' set.prod l m }

theorem zero_def {α : Type u} : 0 = ∅ :=
  rfl

theorem one_def {α : Type u} : 1 = singleton [] :=
  rfl

theorem add_def {α : Type u} (l : language α) (m : language α) : l + m = l ∪ m :=
  rfl

theorem mul_def {α : Type u} (l : language α) (m : language α) : l * m = (fun (p : List α × List α) => prod.fst p ++ prod.snd p) '' set.prod l m :=
  rfl

/-- The star of a language `L` is the set of all strings which can be written by concatenating
  strings from `L`. -/
def star {α : Type u} (l : language α) : language α :=
  set_of fun (x : List α) => ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l

theorem star_def {α : Type u} (l : language α) : star l = set_of fun (x : List α) => ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l :=
  rfl

@[simp] theorem mem_one {α : Type u} (x : List α) : x ∈ 1 ↔ x = [] :=
  iff.refl (x ∈ 1)

@[simp] theorem mem_add {α : Type u} (l : language α) (m : language α) (x : List α) : x ∈ l + m ↔ x ∈ l ∨ x ∈ m := sorry

theorem mem_mul {α : Type u} (l : language α) (m : language α) (x : List α) : x ∈ l * m ↔ ∃ (a : List α), ∃ (b : List α), a ∈ l ∧ b ∈ m ∧ a ++ b = x := sorry

theorem mem_star {α : Type u} (l : language α) (x : List α) : x ∈ star l ↔ ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l :=
  iff.refl (x ∈ star l)

protected instance semiring {α : Type u} : semiring (language α) :=
  semiring.mk Add.add sorry 0 sorry sorry sorry Mul.mul mul_assoc_lang 1 one_mul_lang mul_one_lang sorry sorry
    left_distrib_lang right_distrib_lang

@[simp] theorem add_self {α : Type u} (l : language α) : l + l = l :=
  sup_idem

theorem star_def_nonempty {α : Type u} (l : language α) : star l = set_of fun (x : List α) => ∃ (S : List (List α)), x = list.join S ∧ ∀ (y : List α), y ∈ S → y ∈ l ∧ y ≠ [] := sorry

theorem le_iff {α : Type u} (l : language α) (m : language α) : l ≤ m ↔ l + m = m :=
  iff.symm sup_eq_right

theorem le_mul_congr {α : Type u} {l₁ : language α} {l₂ : language α} {m₁ : language α} {m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ * l₂ ≤ m₁ * m₂ := sorry

theorem le_add_congr {α : Type u} {l₁ : language α} {l₂ : language α} {m₁ : language α} {m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ + l₂ ≤ m₁ + m₂ :=
  sup_le_sup

theorem supr_mul {α : Type u} {ι : Sort v} (l : ι → language α) (m : language α) : (supr fun (i : ι) => l i) * m = supr fun (i : ι) => l i * m := sorry

theorem mul_supr {α : Type u} {ι : Sort v} (l : ι → language α) (m : language α) : (m * supr fun (i : ι) => l i) = supr fun (i : ι) => m * l i := sorry

theorem supr_add {α : Type u} {ι : Sort v} [Nonempty ι] (l : ι → language α) (m : language α) : (supr fun (i : ι) => l i) + m = supr fun (i : ι) => l i + m :=
  supr_sup

theorem add_supr {α : Type u} {ι : Sort v} [Nonempty ι] (l : ι → language α) (m : language α) : (m + supr fun (i : ι) => l i) = supr fun (i : ι) => m + l i :=
  sup_supr

theorem star_eq_supr_pow {α : Type u} (l : language α) : star l = supr fun (i : ℕ) => l ^ i := sorry

theorem mul_self_star_comm {α : Type u} (l : language α) : star l * l = l * star l := sorry

@[simp] theorem one_add_self_mul_star_eq_star {α : Type u} (l : language α) : 1 + l * star l = star l := sorry

@[simp] theorem one_add_star_mul_self_eq_star {α : Type u} (l : language α) : 1 + star l * l = star l :=
  eq.mpr (id (Eq._oldrec (Eq.refl (1 + star l * l = star l)) (mul_self_star_comm l)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 + l * star l = star l)) (one_add_self_mul_star_eq_star l))) (Eq.refl (star l)))

theorem star_mul_le_right_of_mul_le_right {α : Type u} (l : language α) (m : language α) : l * m ≤ m → star l * m ≤ m := sorry

theorem star_mul_le_left_of_mul_le_left {α : Type u} (l : language α) (m : language α) : m * l ≤ m → m * star l ≤ m := sorry


import tactic

/-!

# Definition and basic API for partitions

-/

/-- The structure of a partition on a Type α. -/
-- Let α be a type. A *partition* on α is defined to
-- be the following data.
@[ext] structure partition (α : Type) :=
-- A set C of subsets of α, called "blocks".
(C : set (set α))
-- A hypothesis (a.k.a. a proof) that all the blocks are non-empty.
(Hnonempty : ∀ X ∈ C, (X : set α).nonempty)
-- A hypothesis that every term of type α is in one of the blocks.
(Hcover : ∀ a, ∃ X ∈ C, a ∈ X)
-- A hypothesis that two blocks with non-empty intersection are equal.
(Hdisjoint : ∀ X Y ∈ C, (X ∩ Y : set α).nonempty → X = Y)

namespace partition

-- let α be a type, and fix a partition P on α. Let X and Y be subsets of α.
variables {α : Type} {P : partition α} {X Y : set α}

-- a more convenient way of putting it.
theorem Hdisjoint' (hX : X ∈ P.C) (hY : Y ∈ P.C) : (X ∩ Y).nonempty → X = Y :=
P.Hdisjoint X hX Y hY

-- another way
theorem Hdisjoint'' (hX : X ∈ P.C) (hY : Y ∈ P.C) {a : α} (haX : a ∈ X)
  (haY : a ∈ Y) : X = Y :=
P.Hdisjoint _ hX _ hY ⟨a, haX, haY⟩

end partition

section equivalence_classes

/-!

# Definition and basic API for equivalence classes

We define equivalence classes and prove a few basic results about them.

-/

-- Notation and variables for this section:

-- let α be a type, and let R be an equivalence relation on R.
variables {α : Type} {R : α → α → Prop} (hR : equivalence R)

-- Always assume R is an equivalence relation, even when we don't need it.
include hR

/-- The equivalence class of `x` is all the `y` such that `y` is related to `x`. -/
def cl (x : α) :=
{y : α | R y x}

/-- Useful for rewriting -- `y` is in the equivalence class of `x` iff
`y` is related to `x`. True by definition. -/
theorem mem_cl_iff {x y : α} : x ∈ cl hR y ↔ R x y := iff.rfl

/-- x is in cl(x) -/
lemma mem_class_self (x : α) :
  x ∈ cl hR x :=
begin
  rcases hR with ⟨hrefl, hsymm, htrans⟩,
  exact hrefl x,
end

lemma class_sub {x y : α} :
  x ∈ cl hR y →
  cl hR x ⊆ cl hR y :=
begin
  rcases hR with ⟨hrefl, hsymm, htrans⟩,
  intro hxy,
  intro z,
  intro hzx,
  exact htrans hzx hxy,
end

lemma class_eq {x y : α} :
  x ∈ cl hR y →
  cl hR x = cl hR y :=
begin
  intro hxy,
  apply set.subset.antisymm,
    apply class_sub hR hxy,
  apply class_sub hR,
  rcases hR with ⟨hrefl, hsymm, htrans⟩,
  exact hsymm hxy,
end

end equivalence_classes -- section

/-!

# Statement of the theorem

-/

open partition

-- There is a bijection between equivalence relations and partitions
example (α : Type) : {R : α → α → Prop // equivalence R} ≃ partition α :=
{ -- Let R be an equivalence relation.
  to_fun := λ R, {
    -- Let C be the set of equivalence classes for R.
    C := { B : set α | ∃ x : α, B = cl R.2 x},
    -- I claim that C is a partition. We need to check three things.
    Hnonempty := begin
      -- If c is a block then c is nonempty.
      rintros c ⟨x, rfl⟩,
      use x,
      apply mem_class_self R.2,
    end,
    Hcover := begin
      intro x,
      use (cl R.2 x),
      split,
      { use x,
      },
      { apply mem_class_self,
      }
    end,
    Hdisjoint := begin
      rintros c ⟨x, rfl⟩ d ⟨y, rfl⟩ ⟨z, hzx, hzy⟩,
      cases R with R hR,
      erw ← class_eq hR hzx,
      erw ← class_eq hR hzy,
    end },
  -- Conversely, say P is an partition.
  inv_fun := λ P,
    -- Let's define a binary relation by x ~ y iff there's a block they're both in
    ⟨λ a b, ∀ X ∈ P.C, a ∈ X → b ∈ X, begin
      -- I claim this is an equivalence relation.
    split,
    { -- It's reflexive
      intros a C hC haC,
      exact haC,
    },
    split,
    { -- it's symmetric
      intros x y h C hC hyC,
      rcases P.Hcover x with ⟨D, hD, hxD⟩,
      convert hxD,
      apply P.Hdisjoint _ hC _ hD,
      use [y, hyC],
      exact h D hD hxD,
    },
    { -- it's transitive
      intros x y z hxy hyx C hC hxC,
      apply hyx C hC,
      apply hxy C hC,
      exact hxC,
    }
  end⟩,
  -- If you start with the equivalence relation, and then make the partition
  -- and a new equivalence relation, you get back to where you started.
  left_inv := begin
    rintro ⟨R, hR⟩,
    simp,
    ext a b,
    rcases hR with ⟨hRr,hRs,hRt⟩,
    split,
    { intro f,
      specialize f a (hRr a),
      exact hRs f,
    },
    { intros hab t hat,
      refine hRt _ hat,
      exact hRs hab,
    },
  end,
  -- Similarly, if you start with the partition, and then make the
  -- equivalence relation, and then construct the corresponding partition
  -- into equivalence classes, you have the same partition you started with.
  right_inv := begin
    intro P,
    ext W,
    simp,
    split,
    { rintro ⟨a, rfl⟩,
      rcases P.Hcover a with ⟨X, hX, haX⟩,
      convert hX,
      ext b,
      rw mem_cl_iff,
      split,
      { intro h,
        rcases P.Hcover b with ⟨Y, hY, hbY⟩,
        specialize h Y hY hbY,
        rwa Hdisjoint'' hX hY haX h,
      },
      { intros hbX Y hY hbY,
        rwa Hdisjoint'' hY hX hbY hbX,
      }
    },
    { intro hW,
      cases P.Hnonempty W hW with a haW,
      use a,
      ext b,
      rw mem_cl_iff,
      split,
      { intro hbW,
        intros X hX hbX,
        rwa Hdisjoint'' hX hW hbX hbW
      },
      { intro haX,
        rcases P.Hcover b with ⟨X, hX, hbX⟩,
        specialize haX X hX hbX,
        rwa Hdisjoint'' hW hX haW haX
      }
    }
  end }


/-
Copyright (c) 2020 Aaron Anderson, Jalex Stark. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Aaron Anderson, Jalex Stark
-/
import linear_algebra.matrix.charpoly.coeff
import field_theory.finite.basic
import data.matrix.char_p

/-!
# Results on characteristic polynomials and traces over finite fields.
-/

noncomputable theory

open polynomial matrix
open_locale polynomial

variables {n : Type*} [decidable_eq n] [fintype n]

@[simp] lemma finite_field.matrix.charpoly_pow_card {K : Type*} [field K] [fintype K]
  (M : matrix n n K) : (M ^ (fintype.card K)).charpoly = M.charpoly :=
begin
  casesI (is_empty_or_nonempty n).symm,
  { cases char_p.exists K with p hp, letI := hp,
    rcases finite_field.card K p with ⟨⟨k, kpos⟩, ⟨hp, hk⟩⟩,
    haveI : fact p.prime := ⟨hp⟩,
    dsimp at hk, rw hk at *,
    apply (frobenius_inj K[X] p).iterate k,
    repeat { rw iterate_frobenius, rw ← hk },
    rw ← finite_field.expand_card,
    unfold charpoly, rw [alg_hom.map_det, ← coe_det_monoid_hom,
      ← (det_monoid_hom : matrix n n K[X] →* K[X]).map_pow],
    apply congr_arg det,
    refine mat_poly_equiv.injective _,
    rw [alg_equiv.map_pow, mat_poly_equiv_charmatrix, hk, sub_pow_char_pow_of_commute, ← C_pow],
    { exact (id (mat_poly_equiv_eq_X_pow_sub_C (p ^ k) M) : _) },
    { exact (C M).commute_X } },
  { exact congr_arg _ (subsingleton.elim _ _), },
end

@[simp] lemma zmod.charpoly_pow_card {p : ℕ} [fact p.prime] (M : matrix n n (zmod p)) :
  (M ^ p).charpoly = M.charpoly :=
by { have h := finite_field.matrix.charpoly_pow_card M, rwa zmod.card at h, }

lemma finite_field.trace_pow_card {K : Type*} [field K] [fintype K]
  (M : matrix n n K) : trace (M ^ (fintype.card K)) = trace M ^ (fintype.card K) :=
begin
  casesI is_empty_or_nonempty n,
  { simp [zero_pow fintype.card_pos, matrix.trace], },
  rw [matrix.trace_eq_neg_charpoly_coeff, matrix.trace_eq_neg_charpoly_coeff,
       finite_field.matrix.charpoly_pow_card, finite_field.pow_card]
end

lemma zmod.trace_pow_card {p : ℕ} [fact p.prime] (M : matrix n n (zmod p)) :
  trace (M ^ p) = (trace M)^p :=
by { have h := finite_field.trace_pow_card M, rwa zmod.card at h, }


/-
Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis

-/
import data.padics.padic_norm
import analysis.normed_space.basic

/-!
# p-adic numbers

This file defines the p-adic numbers (rationals) `ℚ_p` as
the completion of `ℚ` with respect to the p-adic norm.
We show that the p-adic norm on ℚ extends to `ℚ_p`, that `ℚ` is embedded in `ℚ_p`,
and that `ℚ_p` is Cauchy complete.

## Important definitions

* `padic` : the type of p-adic numbers
* `padic_norm_e` : the rational valued p-adic norm on `ℚ_p`

## Notation

We introduce the notation `ℚ_[p]` for the p-adic numbers.

## Implementation notes

Much, but not all, of this file assumes that `p` is prime. This assumption is inferred automatically
by taking `[fact (prime p)]` as a type class argument.

We use the same concrete Cauchy sequence construction that is used to construct ℝ.
`ℚ_p` inherits a field structure from this construction.
The extension of the norm on ℚ to `ℚ_p` is *not* analogous to extending the absolute value to ℝ,
and hence the proof that `ℚ_p` is complete is different from the proof that ℝ is complete.

A small special-purpose simplification tactic, `padic_index_simp`, is used to manipulate sequence
indices in the proof that the norm extends.

`padic_norm_e` is the rational-valued p-adic norm on `ℚ_p`.
To instantiate `ℚ_p` as a normed field, we must cast this into a ℝ-valued norm.
The `ℝ`-valued norm, using notation `∥ ∥` from normed spaces,
is the canonical representation of this norm.

`simp` prefers `padic_norm` to `padic_norm_e` when possible.
Since `padic_norm_e` and `∥ ∥` have different types, `simp` does not rewrite one to the other.

Coercions from `ℚ` to `ℚ_p` are set up to work with the `norm_cast` tactic.

## References

* [F. Q. Gouêva, *p-adic numbers*][gouvea1997]
* [R. Y. Lewis, *A formal proof of Hensel's lemma over the p-adic integers*][lewis2019]
* <https://en.wikipedia.org/wiki/P-adic_number>

## Tags

p-adic, p adic, padic, norm, valuation, cauchy, completion, p-adic completion
-/

noncomputable theory
open_locale classical

open nat multiplicity padic_norm cau_seq cau_seq.completion metric

/-- The type of Cauchy sequences of rationals with respect to the p-adic norm. -/
@[reducible] def padic_seq (p : ℕ) := cau_seq _ (padic_norm p)

namespace padic_seq

section
variables {p : ℕ} [fact p.prime]

/-- The p-adic norm of the entries of a nonzero Cauchy sequence of rationals is eventually
constant. -/
lemma stationary {f : cau_seq ℚ (padic_norm p)} (hf : ¬ f ≈ 0) :
  ∃ N, ∀ m n, N ≤ m → N ≤ n → padic_norm p (f n) = padic_norm p (f m) :=
have ∃ ε > 0, ∃ N1, ∀ j ≥ N1, ε ≤ padic_norm p (f j),
  from cau_seq.abv_pos_of_not_lim_zero $ not_lim_zero_of_not_congr_zero hf,
let ⟨ε, hε, N1, hN1⟩ := this,
    ⟨N2, hN2⟩ := cau_seq.cauchy₂ f hε in
⟨ max N1 N2,
  λ n m hn hm,
  have padic_norm p (f n - f m) < ε, from hN2 _ _ (max_le_iff.1 hn).2 (max_le_iff.1 hm).2,
  have padic_norm p (f n - f m) < padic_norm p (f n),
    from lt_of_lt_of_le this $ hN1 _ (max_le_iff.1 hn).1,
  have  padic_norm p (f n - f m) < max (padic_norm p (f n)) (padic_norm p (f m)),
    from lt_max_iff.2 (or.inl this),
  begin
    by_contradiction hne,
    rw ←padic_norm.neg p (f m) at hne,
    have hnam := add_eq_max_of_ne p hne,
    rw [padic_norm.neg, max_comm] at hnam,
    rw [←hnam, sub_eq_add_neg, add_comm] at this,
    apply _root_.lt_irrefl _ this
  end ⟩

/-- For all n ≥ stationary_point f hf, the p-adic norm of f n is the same. -/
def stationary_point {f : padic_seq p} (hf : ¬ f ≈ 0) : ℕ :=
classical.some $ stationary hf

lemma stationary_point_spec {f : padic_seq p} (hf : ¬ f ≈ 0) :
  ∀ {m n}, stationary_point hf ≤ m → stationary_point hf ≤ n →
    padic_norm p (f n) = padic_norm p (f m) :=
classical.some_spec $ stationary hf

/-- Since the norm of the entries of a Cauchy sequence is eventually stationary,
we can lift the norm to sequences. -/
def norm (f : padic_seq p) : ℚ :=
if hf : f ≈ 0 then 0 else padic_norm p (f (stationary_point hf))

lemma norm_zero_iff (f : padic_seq p) : f.norm = 0 ↔ f ≈ 0 :=
begin
  constructor,
  { intro h,
    by_contradiction hf,
    unfold norm at h, split_ifs at h,
    apply hf,
    intros ε hε,
    existsi stationary_point hf,
    intros j hj,
    have heq := stationary_point_spec hf (le_refl _) hj,
    simpa [h, heq] },
  { intro h,
    simp [norm, h] }
end

end

section embedding
open cau_seq
variables {p : ℕ} [fact p.prime]

lemma equiv_zero_of_val_eq_of_equiv_zero {f g : padic_seq p}
  (h : ∀ k, padic_norm p (f k) = padic_norm p (g k)) (hf : f ≈ 0) : g ≈ 0 :=
λ ε hε, let ⟨i, hi⟩ := hf _ hε in
⟨i, λ j hj, by simpa [h] using hi _ hj⟩

lemma norm_nonzero_of_not_equiv_zero {f : padic_seq p} (hf : ¬ f ≈ 0) :
  f.norm ≠ 0 :=
hf ∘ f.norm_zero_iff.1

lemma norm_eq_norm_app_of_nonzero {f : padic_seq p} (hf : ¬ f ≈ 0) :
  ∃ k, f.norm = padic_norm p k ∧ k ≠ 0 :=
have heq : f.norm = padic_norm p (f $ stationary_point hf), by simp [norm, hf],
⟨f $ stationary_point hf, heq,
  λ h, norm_nonzero_of_not_equiv_zero hf (by simpa [h] using heq)⟩

lemma not_lim_zero_const_of_nonzero {q : ℚ} (hq : q ≠ 0) : ¬ lim_zero (const (padic_norm p) q) :=
λ h', hq $ const_lim_zero.1 h'

lemma not_equiv_zero_const_of_nonzero {q : ℚ} (hq : q ≠ 0) : ¬ (const (padic_norm p) q) ≈ 0 :=
λ h : lim_zero (const (padic_norm p) q - 0), not_lim_zero_const_of_nonzero hq $ by simpa using h

lemma norm_nonneg (f : padic_seq p) : 0 ≤ f.norm :=
if hf : f ≈ 0 then by simp [hf, norm]
else by simp [norm, hf, padic_norm.nonneg]

/-- An auxiliary lemma for manipulating sequence indices. -/
lemma lift_index_left_left {f : padic_seq p} (hf : ¬ f ≈ 0) (v2 v3 : ℕ) :
  padic_norm p (f (stationary_point hf)) =
    padic_norm p (f (max (stationary_point hf) (max v2 v3))) :=
begin
  apply stationary_point_spec hf,
  { apply le_max_left },
  { apply le_refl }
end

/-- An auxiliary lemma for manipulating sequence indices. -/
lemma lift_index_left {f : padic_seq p} (hf : ¬ f ≈ 0) (v1 v3 : ℕ) :
  padic_norm p (f (stationary_point hf)) =
    padic_norm p (f (max v1 (max (stationary_point hf) v3))) :=
begin
  apply stationary_point_spec hf,
  { apply le_trans,
    { apply le_max_left _ v3 },
    { apply le_max_right } },
  { apply le_refl }
end

/-- An auxiliary lemma for manipulating sequence indices. -/
lemma lift_index_right {f : padic_seq p} (hf : ¬ f ≈ 0) (v1 v2 : ℕ) :
  padic_norm p (f (stationary_point hf)) =
    padic_norm p (f (max v1 (max v2 (stationary_point hf)))) :=
begin
  apply stationary_point_spec hf,
  { apply le_trans,
    { apply le_max_right v2 },
    { apply le_max_right } },
  { apply le_refl }
end

end embedding

section valuation
open cau_seq
variables {p : ℕ} [fact p.prime]

/-! ### Valuation on `padic_seq` -/

/--
The `p`-adic valuation on `ℚ` lifts to `padic_seq p`.
`valuation f` is defined to be the valuation of the (`ℚ`-valued) stationary point of `f`.
-/
def valuation (f : padic_seq p) : ℤ :=
if hf : f ≈ 0 then 0 else padic_val_rat p (f (stationary_point hf))

lemma norm_eq_pow_val {f : padic_seq p} (hf : ¬ f ≈ 0) :
  f.norm = p^(-f.valuation : ℤ) :=
begin
  rw [norm, valuation, dif_neg hf, dif_neg hf, padic_norm, if_neg],
  intro H,
  apply cau_seq.not_lim_zero_of_not_congr_zero hf,
  intros ε hε,
  use (stationary_point hf),
  intros n hn,
  rw stationary_point_spec hf (le_refl _) hn,
  simpa [H] using hε,
end

lemma val_eq_iff_norm_eq {f g : padic_seq p} (hf : ¬ f ≈ 0) (hg : ¬ g ≈ 0) :
  f.valuation = g.valuation ↔ f.norm = g.norm :=
begin
  rw [norm_eq_pow_val hf, norm_eq_pow_val hg, ← neg_inj, fpow_inj],
  { exact_mod_cast (fact.out p.prime).pos },
  { exact_mod_cast (fact.out p.prime).ne_one },
end

end valuation

end padic_seq

section
open padic_seq

private meta def index_simp_core (hh hf hg : expr)
  (at_ : interactive.loc := interactive.loc.ns [none]) : tactic unit :=
do [v1, v2, v3] ← [hh, hf, hg].mmap
     (λ n, tactic.mk_app ``stationary_point [n] <|> return n),
   e1 ← tactic.mk_app ``lift_index_left_left [hh, v2, v3] <|> return `(true),
   e2 ← tactic.mk_app ``lift_index_left [hf, v1, v3] <|> return `(true),
   e3 ← tactic.mk_app ``lift_index_right [hg, v1, v2] <|> return `(true),
   sl ← [e1, e2, e3].mfoldl (λ s e, simp_lemmas.add s e) simp_lemmas.mk,
   when at_.include_goal (tactic.simp_target sl >> tactic.skip),
   hs ← at_.get_locals, hs.mmap' (tactic.simp_hyp sl [])

/--
  This is a special-purpose tactic that lifts padic_norm (f (stationary_point f)) to
  padic_norm (f (max _ _ _)).
-/
meta def tactic.interactive.padic_index_simp (l : interactive.parse interactive.types.pexpr_list)
  (at_ : interactive.parse interactive.types.location) : tactic unit :=
do [h, f, g] ← l.mmap tactic.i_to_expr,
   index_simp_core h f g at_
end

namespace padic_seq
section embedding

open cau_seq
variables {p : ℕ} [hp : fact p.prime]
include hp

lemma norm_mul (f g : padic_seq p) : (f * g).norm = f.norm * g.norm :=
if hf : f ≈ 0 then
  have hg : f * g ≈ 0, from mul_equiv_zero' _ hf,
  by simp only [hf, hg, norm, dif_pos, zero_mul]
else if hg : g ≈ 0 then
  have hf : f * g ≈ 0, from mul_equiv_zero _ hg,
  by simp only [hf, hg, norm, dif_pos, mul_zero]
else
  have hfg : ¬ f * g ≈ 0, by apply mul_not_equiv_zero; assumption,
  begin
    unfold norm,
    split_ifs,
    padic_index_simp [hfg, hf, hg],
    apply padic_norm.mul
  end

lemma eq_zero_iff_equiv_zero (f : padic_seq p) : mk f = 0 ↔ f ≈ 0 :=
mk_eq

lemma ne_zero_iff_nequiv_zero (f : padic_seq p) : mk f ≠ 0 ↔ ¬ f ≈ 0 :=
not_iff_not.2 (eq_zero_iff_equiv_zero _)

lemma norm_const (q : ℚ) : norm (const (padic_norm p) q) = padic_norm p q :=
if hq : q = 0 then
  have (const (padic_norm p) q) ≈ 0,
    by simp [hq]; apply setoid.refl (const (padic_norm p) 0),
  by subst hq; simp [norm, this]
else
  have ¬ (const (padic_norm p) q) ≈ 0, from not_equiv_zero_const_of_nonzero hq,
  by simp [norm, this]

lemma norm_values_discrete (a : padic_seq p) (ha : ¬ a ≈ 0) :
  (∃ (z : ℤ), a.norm = ↑p ^ (-z)) :=
let ⟨k, hk, hk'⟩ := norm_eq_norm_app_of_nonzero ha in
by simpa [hk] using padic_norm.values_discrete p hk'

lemma norm_one : norm (1 : padic_seq p) = 1 :=
have h1 : ¬ (1 : padic_seq p) ≈ 0, from one_not_equiv_zero _,
by simp [h1, norm, hp.1.one_lt]

private lemma norm_eq_of_equiv_aux {f g : padic_seq p} (hf : ¬ f ≈ 0) (hg : ¬ g ≈ 0) (hfg : f ≈ g)
  (h : padic_norm p (f (stationary_point hf)) ≠ padic_norm p (g (stationary_point hg)))
  (hlt : padic_norm p (g (stationary_point hg)) < padic_norm p (f (stationary_point hf))) :
  false :=
begin
  have hpn : 0 < padic_norm p (f (stationary_point hf)) - padic_norm p (g (stationary_point hg)),
    from sub_pos_of_lt hlt,
  cases hfg _ hpn with N hN,
  let i := max N (max (stationary_point hf) (stationary_point hg)),
  have hi : N ≤ i, from le_max_left _ _,
  have hN' := hN _ hi,
  padic_index_simp [N, hf, hg] at hN' h hlt,
  have hpne : padic_norm p (f i) ≠ padic_norm p (-(g i)),
    by rwa [ ←padic_norm.neg p (g i)] at h,
  let hpnem := add_eq_max_of_ne p hpne,
  have hpeq : padic_norm p ((f - g) i) = max (padic_norm p (f i)) (padic_norm p (g i)),
  { rwa padic_norm.neg at hpnem },
  rw [hpeq, max_eq_left_of_lt hlt] at hN',
  have : padic_norm p (f i) < padic_norm p (f i),
  { apply lt_of_lt_of_le hN', apply sub_le_self, apply padic_norm.nonneg },
  exact lt_irrefl _ this
end

private lemma norm_eq_of_equiv {f g : padic_seq p} (hf : ¬ f ≈ 0) (hg : ¬ g ≈ 0) (hfg : f ≈ g) :
  padic_norm p (f (stationary_point hf)) = padic_norm p (g (stationary_point hg)) :=
begin
  by_contradiction h,
  cases (decidable.em (padic_norm p (g (stationary_point hg)) <
          padic_norm p (f (stationary_point hf))))
      with hlt hnlt,
  { exact norm_eq_of_equiv_aux hf hg hfg h hlt },
  { apply norm_eq_of_equiv_aux hg hf (setoid.symm hfg) (ne.symm h),
    apply lt_of_le_of_ne,
    apply le_of_not_gt hnlt,
    apply h }
end

theorem norm_equiv {f g : padic_seq p} (hfg : f ≈ g) : f.norm = g.norm :=
if hf : f ≈ 0 then
  have hg : g ≈ 0, from setoid.trans (setoid.symm hfg) hf,
  by simp [norm, hf, hg]
else have hg : ¬ g ≈ 0, from hf ∘ setoid.trans hfg,
by unfold norm; split_ifs; exact norm_eq_of_equiv hf hg hfg

private lemma norm_nonarchimedean_aux {f g : padic_seq p}
  (hfg : ¬ f + g ≈ 0) (hf : ¬ f ≈ 0) (hg : ¬ g ≈ 0) : (f + g).norm ≤ max (f.norm) (g.norm) :=
begin
  unfold norm, split_ifs,
  padic_index_simp [hfg, hf, hg],
  apply padic_norm.nonarchimedean
end

theorem norm_nonarchimedean (f g : padic_seq p) : (f + g).norm ≤ max (f.norm) (g.norm) :=
if hfg : f + g ≈ 0 then
  have 0 ≤ max (f.norm) (g.norm), from le_max_left_of_le (norm_nonneg _),
  by simpa only [hfg, norm, ne.def, le_max_iff, cau_seq.add_apply, not_true, dif_pos]
else if hf : f ≈ 0 then
  have hfg' : f + g ≈ g,
  { change lim_zero (f - 0) at hf,
    show lim_zero (f + g - g), by simpa only [sub_zero, add_sub_cancel] using hf },
  have hcfg : (f + g).norm = g.norm, from norm_equiv hfg',
  have hcl : f.norm = 0, from (norm_zero_iff f).2 hf,
  have max (f.norm) (g.norm) = g.norm,
    by rw hcl; exact max_eq_right (norm_nonneg _),
  by rw [this, hcfg]
else if hg : g ≈ 0 then
  have hfg' : f + g ≈ f,
  { change lim_zero (g - 0) at hg,
    show lim_zero (f + g - f), by simpa only [add_sub_cancel', sub_zero] using hg },
  have hcfg : (f + g).norm = f.norm, from norm_equiv hfg',
  have hcl : g.norm = 0, from (norm_zero_iff g).2 hg,
  have max (f.norm) (g.norm) = f.norm,
    by rw hcl; exact max_eq_left (norm_nonneg _),
  by rw [this, hcfg]
else norm_nonarchimedean_aux hfg hf hg

lemma norm_eq {f g : padic_seq p} (h : ∀ k, padic_norm p (f k) = padic_norm p (g k)) :
  f.norm = g.norm :=
if hf : f ≈ 0 then
  have hg : g ≈ 0, from equiv_zero_of_val_eq_of_equiv_zero h hf,
  by simp only [hf, hg, norm, dif_pos]
else
  have hg : ¬ g ≈ 0, from λ hg, hf $ equiv_zero_of_val_eq_of_equiv_zero
    (by simp only [h, forall_const, eq_self_iff_true]) hg,
  begin
    simp only [hg, hf, norm, dif_neg, not_false_iff],
    let i := max (stationary_point hf) (stationary_point hg),
    have hpf : padic_norm p (f (stationary_point hf)) = padic_norm p (f i),
    { apply stationary_point_spec, apply le_max_left, apply le_refl },
    have hpg : padic_norm p (g (stationary_point hg)) = padic_norm p (g i),
    { apply stationary_point_spec, apply le_max_right, apply le_refl },
    rw [hpf, hpg, h]
  end

lemma norm_neg (a : padic_seq p) : (-a).norm = a.norm :=
norm_eq $ by simp

lemma norm_eq_of_add_equiv_zero {f g : padic_seq p} (h : f + g ≈ 0) : f.norm = g.norm :=
have lim_zero (f + g - 0), from h,
have f ≈ -g, from show lim_zero (f - (-g)), by simpa only [sub_zero, sub_neg_eq_add],
have f.norm = (-g).norm, from norm_equiv this,
by simpa only [norm_neg] using this

lemma add_eq_max_of_ne {f g : padic_seq p} (hfgne : f.norm ≠ g.norm) :
  (f + g).norm = max f.norm g.norm :=
have hfg : ¬f + g ≈ 0, from mt norm_eq_of_add_equiv_zero hfgne,
if hf : f ≈ 0 then
  have lim_zero (f - 0), from hf,
  have f + g ≈ g, from show lim_zero ((f + g) - g), by simpa only [sub_zero, add_sub_cancel],
  have h1 : (f+g).norm = g.norm, from norm_equiv this,
  have h2 : f.norm = 0, from (norm_zero_iff _).2 hf,
  by rw [h1, h2]; rw max_eq_right (norm_nonneg _)
else if hg : g ≈ 0 then
  have lim_zero (g - 0), from hg,
  have f + g ≈ f, from show lim_zero ((f + g) - f), by rw [add_sub_cancel']; simpa only [sub_zero],
  have h1 : (f+g).norm = f.norm, from norm_equiv this,
  have h2 : g.norm = 0, from (norm_zero_iff _).2 hg,
  by rw [h1, h2]; rw max_eq_left (norm_nonneg _)
else
begin
  unfold norm at ⊢ hfgne, split_ifs at ⊢ hfgne,
  padic_index_simp [hfg, hf, hg] at ⊢ hfgne,
  exact padic_norm.add_eq_max_of_ne p hfgne
end

end embedding
end padic_seq

/-- The p-adic numbers `Q_[p]` are the Cauchy completion of `ℚ` with respect to the p-adic norm. -/
def padic (p : ℕ) [fact p.prime] := @cau_seq.completion.Cauchy _ _ _ _ (padic_norm p) _
notation `ℚ_[` p `]` := padic p

namespace padic

section completion
variables {p : ℕ} [fact p.prime]

/-- The discrete field structure on `ℚ_p` is inherited from the Cauchy completion construction. -/
instance field : field (ℚ_[p]) :=
cau_seq.completion.field

instance : inhabited ℚ_[p] := ⟨0⟩

-- short circuits

instance : has_zero ℚ_[p] := by apply_instance
instance : has_one ℚ_[p] := by apply_instance
instance : has_add ℚ_[p] := by apply_instance
instance : has_mul ℚ_[p] := by apply_instance
instance : has_sub ℚ_[p] := by apply_instance
instance : has_neg ℚ_[p] := by apply_instance
instance : has_div ℚ_[p] := by apply_instance
instance : add_comm_group ℚ_[p] := by apply_instance
instance : comm_ring ℚ_[p] := by apply_instance

/-- Builds the equivalence class of a Cauchy sequence of rationals. -/
def mk : padic_seq p → ℚ_[p] := quotient.mk
end completion

section completion
variables (p : ℕ) [fact p.prime]

lemma mk_eq {f g : padic_seq p} : mk f = mk g ↔ f ≈ g := quotient.eq

/-- Embeds the rational numbers in the p-adic numbers. -/
def of_rat : ℚ → ℚ_[p] := cau_seq.completion.of_rat

@[simp] lemma of_rat_add : ∀ (x y : ℚ), of_rat p (x + y) = of_rat p x + of_rat p y :=
cau_seq.completion.of_rat_add

@[simp] lemma of_rat_neg : ∀ (x : ℚ), of_rat p (-x) = -of_rat p x :=
cau_seq.completion.of_rat_neg

@[simp] lemma of_rat_mul : ∀ (x y : ℚ), of_rat p (x * y) = of_rat p x * of_rat p y :=
cau_seq.completion.of_rat_mul

@[simp] lemma of_rat_sub : ∀ (x y : ℚ), of_rat p (x - y) = of_rat p x - of_rat p y :=
cau_seq.completion.of_rat_sub

@[simp] lemma of_rat_div : ∀ (x y : ℚ), of_rat p (x / y) = of_rat p x / of_rat p y :=
cau_seq.completion.of_rat_div

@[simp] lemma of_rat_one : of_rat p 1 = 1 := rfl

@[simp] lemma of_rat_zero : of_rat p 0 = 0 := rfl

lemma cast_eq_of_rat_of_nat (n : ℕ) : (↑n : ℚ_[p]) = of_rat p n :=
begin
  induction n with n ih,
  { refl },
  { simpa using ih }
end

lemma cast_eq_of_rat_of_int (n : ℤ) : ↑n = of_rat p n :=
by induction n; simp [cast_eq_of_rat_of_nat]

lemma cast_eq_of_rat : ∀ (q : ℚ), (↑q : ℚ_[p]) = of_rat p q
| ⟨n, d, h1, h2⟩ :=
  show ↑n / ↑d = _, from
    have (⟨n, d, h1, h2⟩ : ℚ) = rat.mk n d, from rat.num_denom',
    by simp [this, rat.mk_eq_div, of_rat_div, cast_eq_of_rat_of_int, cast_eq_of_rat_of_nat]

@[norm_cast] lemma coe_add : ∀ {x y : ℚ}, (↑(x + y) : ℚ_[p]) = ↑x + ↑y := by simp [cast_eq_of_rat]
@[norm_cast] lemma coe_neg : ∀ {x : ℚ}, (↑(-x) : ℚ_[p]) = -↑x := by simp [cast_eq_of_rat]
@[norm_cast] lemma coe_mul : ∀ {x y : ℚ}, (↑(x * y) : ℚ_[p]) = ↑x * ↑y := by simp [cast_eq_of_rat]
@[norm_cast] lemma coe_sub : ∀ {x y : ℚ}, (↑(x - y) : ℚ_[p]) = ↑x - ↑y := by simp [cast_eq_of_rat]
@[norm_cast] lemma coe_div : ∀ {x y : ℚ}, (↑(x / y) : ℚ_[p]) = ↑x / ↑y := by simp [cast_eq_of_rat]

@[norm_cast] lemma coe_one : (↑1 : ℚ_[p]) = 1 := by simp [cast_eq_of_rat]
@[norm_cast] lemma coe_zero : (↑0 : ℚ_[p]) = 0 := rfl

lemma const_equiv {q r : ℚ} : const (padic_norm p) q ≈ const (padic_norm p) r ↔ q = r :=
⟨ λ heq : lim_zero (const (padic_norm p) (q - r)),
    eq_of_sub_eq_zero $ const_lim_zero.1 heq,
  λ heq, by rw heq; apply setoid.refl _ ⟩

lemma of_rat_eq {q r : ℚ} : of_rat p q = of_rat p r ↔ q = r :=
⟨(const_equiv p).1 ∘ quotient.eq.1, λ h, by rw h⟩

@[norm_cast] lemma coe_inj {q r : ℚ} : (↑q : ℚ_[p]) = ↑r ↔ q = r :=
by simp [cast_eq_of_rat, of_rat_eq]

instance : char_zero ℚ_[p] :=
⟨λ m n, by { rw ← rat.cast_coe_nat, norm_cast, exact id }⟩

end completion
end padic

/-- The rational-valued p-adic norm on `ℚ_p` is lifted from the norm on Cauchy sequences. The
canonical form of this function is the normed space instance, with notation `∥ ∥`. -/
def padic_norm_e {p : ℕ} [hp : fact p.prime] : ℚ_[p] → ℚ :=
quotient.lift padic_seq.norm $ @padic_seq.norm_equiv _ _

namespace padic_norm_e
section embedding
open padic_seq
variables {p : ℕ} [fact p.prime]

lemma defn (f : padic_seq p) {ε : ℚ} (hε : 0 < ε) : ∃ N, ∀ i ≥ N, padic_norm_e (⟦f⟧ - f i) < ε :=
begin
  simp only [padic.cast_eq_of_rat],
  change ∃ N, ∀ i ≥ N, (f - const _ (f i)).norm < ε,
  by_contradiction h,
  cases cauchy₂ f hε with N hN,
  have : ∀ N, ∃ i ≥ N, ε ≤ (f - const _ (f i)).norm,
    by simpa only [not_forall, not_exists, not_lt] using h,
  rcases this N with ⟨i, hi, hge⟩,
  have hne : ¬ (f - const (padic_norm p) (f i)) ≈ 0,
  { intro h, unfold padic_seq.norm at hge; split_ifs at hge, exact not_lt_of_ge hge hε },
  unfold padic_seq.norm at hge; split_ifs at hge,
  apply not_le_of_gt _ hge,
  cases decidable.em (N ≤ stationary_point hne) with hgen hngen,
  { apply hN; assumption },
  { have := stationary_point_spec hne (le_refl _) (le_of_not_le hngen),
    rw ←this,
    apply hN,
    apply le_refl, assumption }
end

protected lemma nonneg (q : ℚ_[p]) : 0 ≤ padic_norm_e q :=
quotient.induction_on q $ norm_nonneg

lemma zero_def : (0 : ℚ_[p]) = ⟦0⟧ := rfl

lemma zero_iff (q : ℚ_[p]) : padic_norm_e q = 0 ↔ q = 0 :=
quotient.induction_on q $
  by simpa only [zero_def, quotient.eq] using norm_zero_iff

@[simp] protected lemma zero : padic_norm_e (0 : ℚ_[p]) = 0 :=
(zero_iff _).2 rfl

/-- Theorems about `padic_norm_e` are named with a `'` so the names do not conflict with the
equivalent theorems about `norm` (`∥ ∥`). -/
@[simp] protected lemma one' : padic_norm_e (1 : ℚ_[p]) = 1 :=
norm_one

@[simp] protected lemma neg (q : ℚ_[p]) : padic_norm_e (-q) = padic_norm_e q :=
quotient.induction_on q $ norm_neg

/-- Theorems about `padic_norm_e` are named with a `'` so the names do not conflict with the
equivalent theorems about `norm` (`∥ ∥`). -/
theorem nonarchimedean' (q r : ℚ_[p]) :
  padic_norm_e (q + r) ≤ max (padic_norm_e q) (padic_norm_e r) :=
quotient.induction_on₂ q r $ norm_nonarchimedean

/-- Theorems about `padic_norm_e` are named with a `'` so the names do not conflict with the
equivalent theorems about `norm` (`∥ ∥`). -/
theorem add_eq_max_of_ne' {q r : ℚ_[p]} :
  padic_norm_e q ≠ padic_norm_e r → padic_norm_e (q + r) = max (padic_norm_e q) (padic_norm_e r) :=
quotient.induction_on₂ q r $ λ _ _, padic_seq.add_eq_max_of_ne

lemma triangle_ineq (x y z : ℚ_[p]) :
  padic_norm_e (x - z) ≤ padic_norm_e (x - y) + padic_norm_e (y - z) :=
calc padic_norm_e (x - z) = padic_norm_e ((x - y) + (y - z)) : by rw sub_add_sub_cancel
  ... ≤ max (padic_norm_e (x - y)) (padic_norm_e (y - z)) : padic_norm_e.nonarchimedean' _ _
  ... ≤ padic_norm_e (x - y) + padic_norm_e (y - z) :
    max_le_add_of_nonneg (padic_norm_e.nonneg _) (padic_norm_e.nonneg _)

protected lemma add (q r : ℚ_[p]) : padic_norm_e (q + r) ≤ (padic_norm_e q) + (padic_norm_e r) :=
calc
  padic_norm_e (q + r) ≤ max (padic_norm_e q) (padic_norm_e r) : nonarchimedean' _ _
                      ... ≤ (padic_norm_e q) + (padic_norm_e r) :
                              max_le_add_of_nonneg (padic_norm_e.nonneg _) (padic_norm_e.nonneg _)

protected lemma mul' (q r : ℚ_[p]) : padic_norm_e (q * r) = (padic_norm_e q) * (padic_norm_e r) :=
quotient.induction_on₂ q r $ norm_mul

instance : is_absolute_value (@padic_norm_e p _) :=
{ abv_nonneg := padic_norm_e.nonneg,
  abv_eq_zero := zero_iff,
  abv_add := padic_norm_e.add,
  abv_mul := padic_norm_e.mul' }

@[simp] lemma eq_padic_norm' (q : ℚ) : padic_norm_e (padic.of_rat p q) = padic_norm p q :=
norm_const _

protected theorem image' {q : ℚ_[p]} : q ≠ 0 → ∃ n : ℤ, padic_norm_e q = p ^ (-n) :=
quotient.induction_on q $ λ f hf,
  have ¬ f ≈ 0, from (ne_zero_iff_nequiv_zero f).1 hf,
  norm_values_discrete f this

lemma sub_rev (q r : ℚ_[p]) : padic_norm_e (q - r) = padic_norm_e (r - q) :=
by rw ←(padic_norm_e.neg); simp

end embedding
end padic_norm_e

namespace padic

section complete
open padic_seq padic

theorem rat_dense' {p : ℕ} [fact p.prime] (q : ℚ_[p]) {ε : ℚ} (hε : 0 < ε) :
  ∃ r : ℚ, padic_norm_e (q - r) < ε :=
quotient.induction_on q $ λ q',
  have ∃ N, ∀ m n ≥ N, padic_norm p (q' m - q' n) < ε, from cauchy₂ _ hε,
  let ⟨N, hN⟩ := this in
  ⟨q' N,
    begin
      simp only [padic.cast_eq_of_rat],
      change padic_seq.norm (q' - const _ (q' N)) < ε,
      cases decidable.em ((q' - const (padic_norm p) (q' N)) ≈ 0) with heq hne',
      { simpa only [heq, padic_seq.norm, dif_pos] },
      { simp only [padic_seq.norm, dif_neg hne'],
        change padic_norm p (q' _ - q' _) < ε,
        have := stationary_point_spec hne',
        cases decidable.em (stationary_point hne' ≤ N) with hle hle,
        { have := eq.symm (this (le_refl _) hle),
          simp only [const_apply, sub_apply, padic_norm.zero, sub_self] at this,
          simpa only [this] },
        { apply hN,
          apply le_of_lt, apply lt_of_not_ge, apply hle, apply le_refl }}
    end⟩

variables {p : ℕ} [fact p.prime] (f : cau_seq _ (@padic_norm_e p _))
open classical

private lemma div_nat_pos (n : ℕ) : 0 < (1 / ((n + 1): ℚ)) :=
div_pos zero_lt_one (by exact_mod_cast succ_pos _)

/-- `lim_seq f`, for `f` a Cauchy sequence of `p`-adic numbers,
is a sequence of rationals with the same limit point as `f`. -/
def lim_seq : ℕ → ℚ := λ n, classical.some (rat_dense' (f n) (div_nat_pos n))

lemma exi_rat_seq_conv {ε : ℚ} (hε : 0 < ε) :
  ∃ N, ∀ i ≥ N, padic_norm_e (f i - ((lim_seq f) i : ℚ_[p])) < ε :=
begin
  refine (exists_nat_gt (1/ε)).imp (λ N hN i hi, _),
  have h := classical.some_spec (rat_dense' (f i) (div_nat_pos i)),
  refine lt_of_lt_of_le h ((div_le_iff' $ by exact_mod_cast succ_pos _).mpr _),
  rw right_distrib,
  apply le_add_of_le_of_nonneg,
  { exact (div_le_iff hε).mp (le_trans (le_of_lt hN) (by exact_mod_cast hi)) },
  { apply le_of_lt, simpa }
end

lemma exi_rat_seq_conv_cauchy : is_cau_seq (padic_norm p) (lim_seq f) :=
assume ε hε,
have hε3 : 0 < ε / 3, from div_pos hε (by norm_num),
let ⟨N, hN⟩ := exi_rat_seq_conv f hε3,
    ⟨N2, hN2⟩ := f.cauchy₂ hε3 in
begin
  existsi max N N2,
  intros j hj,
  suffices :
    padic_norm_e ((↑(lim_seq f j) - f (max N N2)) + (f (max N N2) - lim_seq f (max N N2))) < ε,
  { ring_nf at this ⊢,
    rw [← padic_norm_e.eq_padic_norm', ← padic.cast_eq_of_rat],
    exact_mod_cast this },
  { apply lt_of_le_of_lt,
    { apply padic_norm_e.add },
    { have : (3 : ℚ) ≠ 0, by norm_num,
      have : ε = ε / 3 + ε / 3 + ε / 3,
      { field_simp [this], simp only [bit0, bit1, mul_add, mul_one] },
      rw this,
      apply add_lt_add,
      { suffices : padic_norm_e ((↑(lim_seq f j) - f j) + (f j - f (max N N2))) < ε / 3 + ε / 3,
          by simpa only [sub_add_sub_cancel],
        apply lt_of_le_of_lt,
        { apply padic_norm_e.add },
        { apply add_lt_add,
          { rw [padic_norm_e.sub_rev],
            apply_mod_cast hN,
            exact le_of_max_le_left hj },
          { apply hN2,
            exact le_of_max_le_right hj,
            apply le_max_right }}},
      { apply_mod_cast hN,
        apply le_max_left }}}
end

private def lim' : padic_seq p := ⟨_, exi_rat_seq_conv_cauchy f⟩

private def lim : ℚ_[p] := ⟦lim' f⟧

theorem complete' : ∃ q : ℚ_[p], ∀ ε > 0, ∃ N, ∀ i ≥ N, padic_norm_e (q - f i) < ε :=
⟨ lim f,
  λ ε hε,
  let ⟨N, hN⟩ := exi_rat_seq_conv f (show 0 < ε / 2, from div_pos hε (by norm_num)),
      ⟨N2, hN2⟩ := padic_norm_e.defn (lim' f) (show 0 < ε / 2, from div_pos hε (by norm_num)) in
  begin
    existsi max N N2,
    intros i hi,
    suffices : padic_norm_e ((lim f - lim' f i) + (lim' f i - f i)) < ε,
    { ring_nf at this; exact this },
    { apply lt_of_le_of_lt,
      { apply padic_norm_e.add },
      { have : ε = ε / 2 + ε / 2, by rw ←(add_self_div_two ε); simp,
        rw this,
        apply add_lt_add,
        { apply hN2, exact le_of_max_le_right hi },
        { rw_mod_cast [padic_norm_e.sub_rev],
          apply hN,
          exact le_of_max_le_left hi }}}
  end ⟩

end complete

section normed_space
variables (p : ℕ) [fact p.prime]

instance : has_dist ℚ_[p] := ⟨λ x y, padic_norm_e (x - y)⟩

instance : metric_space ℚ_[p] :=
{ dist_self := by simp [dist],
  dist_comm := λ x y, by unfold dist; rw ←padic_norm_e.neg (x - y); simp,
  dist_triangle :=
    begin
      intros, unfold dist,
      exact_mod_cast padic_norm_e.triangle_ineq _ _ _,
    end,
  eq_of_dist_eq_zero :=
    begin
      unfold dist, intros _ _ h,
      apply eq_of_sub_eq_zero,
      apply (padic_norm_e.zero_iff _).1,
      exact_mod_cast h
    end }

instance : has_norm ℚ_[p] := ⟨λ x, padic_norm_e x⟩

instance : normed_field ℚ_[p] :=
{ dist_eq := λ _ _, rfl,
  norm_mul' := by simp [has_norm.norm, padic_norm_e.mul'] }

instance is_absolute_value : is_absolute_value (λ a : ℚ_[p], ∥a∥) :=
{ abv_nonneg := norm_nonneg,
  abv_eq_zero := λ _, norm_eq_zero,
  abv_add := norm_add_le,
  abv_mul := by simp [has_norm.norm, padic_norm_e.mul'] }

theorem rat_dense {p : ℕ} {hp : fact p.prime} (q : ℚ_[p]) {ε : ℝ} (hε : 0 < ε) :
        ∃ r : ℚ, ∥q - r∥ < ε :=
let ⟨ε', hε'l, hε'r⟩ := exists_rat_btwn hε,
    ⟨r, hr⟩ := rat_dense' q (by simpa using hε'l)  in
⟨r, lt_trans (by simpa [has_norm.norm] using hr) hε'r⟩

end normed_space
end padic

namespace padic_norm_e
section normed_space
variables {p : ℕ} [hp : fact p.prime]
include hp

@[simp] protected lemma mul (q r : ℚ_[p]) : ∥q * r∥ = ∥q∥ * ∥r∥ :=
by simp [has_norm.norm, padic_norm_e.mul']

protected lemma is_norm (q : ℚ_[p]) : ↑(padic_norm_e q) = ∥q∥ := rfl

theorem nonarchimedean (q r : ℚ_[p]) : ∥q + r∥ ≤ max (∥q∥) (∥r∥) :=
begin
  unfold has_norm.norm,
  exact_mod_cast nonarchimedean' _ _
end

theorem add_eq_max_of_ne {q r : ℚ_[p]} (h : ∥q∥ ≠ ∥r∥) : ∥q+r∥ = max (∥q∥) (∥r∥) :=
begin
  unfold has_norm.norm,
  apply_mod_cast add_eq_max_of_ne',
  intro h',
  apply h,
  unfold has_norm.norm,
  exact_mod_cast h'
end

@[simp] lemma eq_padic_norm (q : ℚ) : ∥(↑q : ℚ_[p])∥ = padic_norm p q :=
begin
  unfold has_norm.norm,
  rw [← padic_norm_e.eq_padic_norm', ← padic.cast_eq_of_rat]
end

instance : nondiscrete_normed_field ℚ_[p] :=
{ non_trivial := ⟨padic.of_rat p (p⁻¹), begin
    have h0 : p ≠ 0 := ne_of_gt (hp.1.pos),
    have h1 : 1 < p := hp.1.one_lt,
    rw [← padic.cast_eq_of_rat, eq_padic_norm],
    simp only [padic_norm, inv_eq_zero],
    simp only [if_neg] {discharger := `[exact_mod_cast h0]},
    norm_cast,
    simp only [padic_val_rat.inv] {discharger := `[exact_mod_cast h0]},
    rw [neg_neg, padic_val_rat.padic_val_rat_self h1, gpow_one],
    exact_mod_cast h1,
  end⟩ }

@[simp] lemma norm_p : ∥(p : ℚ_[p])∥ = p⁻¹ :=
begin
  have p₀ : p ≠ 0 := hp.1.ne_zero,
  have p₁ : p ≠ 1 := hp.1.ne_one,
  simp [p₀, p₁, norm, padic_norm, padic_val_rat, fpow_neg, padic.cast_eq_of_rat_of_nat],
end

lemma norm_p_lt_one : ∥(p : ℚ_[p])∥ < 1 :=
begin
  rw [norm_p, inv_eq_one_div, div_lt_iff, one_mul],
  { exact_mod_cast hp.1.one_lt },
  { exact_mod_cast hp.1.pos }
end

@[simp] lemma norm_p_pow (n : ℤ) : ∥(p^n : ℚ_[p])∥ = p^-n :=
by rw [normed_field.norm_fpow, norm_p]; field_simp

protected theorem image {q : ℚ_[p]} : q ≠ 0 → ∃ n : ℤ, ∥q∥ = ↑((↑p : ℚ) ^ (-n)) :=
quotient.induction_on q $ λ f hf,
  have ¬ f ≈ 0, from (padic_seq.ne_zero_iff_nequiv_zero f).1 hf,
  let ⟨n, hn⟩ := padic_seq.norm_values_discrete f this in
  ⟨n, congr_arg coe hn⟩

protected lemma is_rat (q : ℚ_[p]) : ∃ q' : ℚ, ∥q∥ = ↑q' :=
if h : q = 0 then ⟨0, by simp [h]⟩
else let ⟨n, hn⟩ := padic_norm_e.image h in ⟨_, hn⟩

/--`rat_norm q`, for a `p`-adic number `q` is the `p`-adic norm of `q`, as rational number.

The lemma `padic_norm_e.eq_rat_norm` asserts `∥q∥ = rat_norm q`. -/
def rat_norm (q : ℚ_[p]) : ℚ := classical.some (padic_norm_e.is_rat q)

lemma eq_rat_norm (q : ℚ_[p]) : ∥q∥ = rat_norm q := classical.some_spec (padic_norm_e.is_rat q)

theorem norm_rat_le_one : ∀ {q : ℚ} (hq : ¬ p ∣ q.denom), ∥(q : ℚ_[p])∥ ≤ 1
| ⟨n, d, hn, hd⟩ := λ hq : ¬ p ∣ d,
  if hnz : n = 0 then
    have (⟨n, d, hn, hd⟩ : ℚ) = 0,
    from rat.zero_iff_num_zero.mpr hnz,
    by norm_num [this]
  else
    begin
      have hnz' : { rat . num := n, denom := d, pos := hn, cop := hd } ≠ 0,
        from mt rat.zero_iff_num_zero.1 hnz,
      rw [padic_norm_e.eq_padic_norm],
      norm_cast,
      rw [padic_norm.eq_fpow_of_nonzero p hnz', padic_val_rat_def p hnz'],
      have h : (multiplicity p d).get _ = 0, by simp [multiplicity_eq_zero_of_not_dvd, hq],
      simp only, norm_cast,
      rw_mod_cast [h, sub_zero],
      apply fpow_le_one_of_nonpos,
      { exact_mod_cast le_of_lt hp.1.one_lt, },
      { apply neg_nonpos_of_nonneg, norm_cast, simp, }
    end

theorem norm_int_le_one (z : ℤ) : ∥(z : ℚ_[p])∥ ≤ 1 :=
suffices ∥((z : ℚ) : ℚ_[p])∥ ≤ 1, by simpa,
norm_rat_le_one $ by simp [hp.1.ne_one]

lemma norm_int_lt_one_iff_dvd (k : ℤ) : ∥(k : ℚ_[p])∥ < 1 ↔ ↑p ∣ k :=
begin
  split,
  { intro h,
    contrapose! h,
    apply le_of_eq,
    rw eq_comm,
    calc ∥(k : ℚ_[p])∥ = ∥((k : ℚ) : ℚ_[p])∥ : by { norm_cast }
    ... = padic_norm p k : padic_norm_e.eq_padic_norm _
    ... = 1 : _,
    rw padic_norm,
    split_ifs with H,
    { exfalso,
      apply h,
      norm_cast at H,
      rw H,
      apply dvd_zero },
    { norm_cast at H ⊢,
      convert gpow_zero _,
      simp only [neg_eq_zero],
      rw padic_val_rat.padic_val_rat_of_int _ hp.1.ne_one H,
      norm_cast,
      rw [← enat.coe_inj, enat.coe_get, enat.coe_zero],
      apply multiplicity.multiplicity_eq_zero_of_not_dvd h } },
  { rintro ⟨x, rfl⟩,
    push_cast,
    rw padic_norm_e.mul,
    calc _ ≤ ∥(p : ℚ_[p])∥ * 1 : mul_le_mul (le_refl _) (by simpa using norm_int_le_one _)
                                            (norm_nonneg _) (norm_nonneg _)
    ... < 1 : _,
    { rw [mul_one, padic_norm_e.norm_p],
      apply inv_lt_one,
      exact_mod_cast hp.1.one_lt }, },
end

lemma norm_int_le_pow_iff_dvd (k : ℤ) (n : ℕ) : ∥(k : ℚ_[p])∥ ≤ ((↑p)^(-n : ℤ)) ↔ ↑(p^n) ∣ k :=
begin
  have : (p : ℝ) ^ (-n : ℤ) = ↑((p ^ (-n : ℤ) : ℚ)), {simp},
  rw [show (k : ℚ_[p]) = ((k : ℚ) : ℚ_[p]), by norm_cast, eq_padic_norm, this],
  norm_cast,
  rw padic_norm.dvd_iff_norm_le,
end

lemma eq_of_norm_add_lt_right {p : ℕ} {hp : fact p.prime} {z1 z2 : ℚ_[p]}
  (h : ∥z1 + z2∥ < ∥z2∥) : ∥z1∥ = ∥z2∥ :=
by_contradiction $ λ hne,
  not_lt_of_ge (by rw padic_norm_e.add_eq_max_of_ne hne; apply le_max_right) h

lemma eq_of_norm_add_lt_left {p : ℕ} {hp : fact p.prime} {z1 z2 : ℚ_[p]}
  (h : ∥z1 + z2∥ < ∥z1∥) : ∥z1∥ = ∥z2∥ :=
by_contradiction $ λ hne,
  not_lt_of_ge (by rw padic_norm_e.add_eq_max_of_ne hne; apply le_max_left) h

end normed_space
end padic_norm_e

namespace padic
variables {p : ℕ} [fact p.prime]

set_option eqn_compiler.zeta true
instance complete : cau_seq.is_complete ℚ_[p] norm :=
begin
  split, intro f,
  have cau_seq_norm_e : is_cau_seq padic_norm_e f,
  { intros ε hε,
    let h := is_cau f ε (by exact_mod_cast hε),
    unfold norm at h,
    apply_mod_cast h },
  cases padic.complete' ⟨f, cau_seq_norm_e⟩ with q hq,
  existsi q,
  intros ε hε,
  cases exists_rat_btwn hε with ε' hε',
  norm_cast at hε',
  cases hq ε' hε'.1 with N hN, existsi N,
  intros i hi, let h := hN i hi,
  unfold norm,
  rw_mod_cast [cau_seq.sub_apply, padic_norm_e.sub_rev],
  refine lt_trans _ hε'.2,
  exact_mod_cast hN i hi
end

lemma padic_norm_e_lim_le {f : cau_seq ℚ_[p] norm} {a : ℝ} (ha : 0 < a)
      (hf : ∀ i, ∥f i∥ ≤ a) : ∥f.lim∥ ≤ a :=
let ⟨N, hN⟩ := setoid.symm (cau_seq.equiv_lim f) _ ha in
calc ∥f.lim∥ = ∥f.lim - f N + f N∥ : by simp
                ... ≤ max (∥f.lim - f N∥) (∥f N∥) : padic_norm_e.nonarchimedean _ _
                ... ≤ a : max_le (le_of_lt (hN _ (le_refl _))) (hf _)

/-!
### Valuation on `ℚ_[p]`
-/

/--
`padic.valuation` lifts the p-adic valuation on rationals to `ℚ_[p]`.
-/
def valuation : ℚ_[p] → ℤ :=
quotient.lift (@padic_seq.valuation p _) (λ f g h,
begin
  by_cases hf : f ≈ 0,
  { have hg : g ≈ 0, from setoid.trans (setoid.symm h) hf,
    simp [hf, hg, padic_seq.valuation] },
  { have hg : ¬ g ≈ 0, from (λ hg, hf (setoid.trans h hg)),
    rw padic_seq.val_eq_iff_norm_eq hf hg,
    exact padic_seq.norm_equiv h },
end)

@[simp] lemma valuation_zero : valuation (0 : ℚ_[p]) = 0 :=
dif_pos ((const_equiv p).2 rfl)

@[simp] lemma valuation_one : valuation (1 : ℚ_[p]) = 0 :=
begin
  change dite (cau_seq.const (padic_norm p) 1 ≈ _) _ _ = _,
  have h : ¬ cau_seq.const (padic_norm p) 1 ≈ 0,
  { assume H, erw const_equiv p at H, exact one_ne_zero H },
  rw dif_neg h,
  simp,
end

lemma norm_eq_pow_val {x : ℚ_[p]} : x ≠ 0 → ∥x∥ = p^(-x.valuation) :=
begin
  apply quotient.induction_on' x, clear x,
  intros f hf,
  change (padic_seq.norm _ : ℝ) = (p : ℝ) ^ -padic_seq.valuation _,
  rw padic_seq.norm_eq_pow_val,
  change ↑((p : ℚ) ^ -padic_seq.valuation f) = (p : ℝ) ^ -padic_seq.valuation f,
  { rw rat.cast_fpow,
    congr' 1,
    norm_cast },
  { apply cau_seq.not_lim_zero_of_not_congr_zero,
    contrapose! hf,
    apply quotient.sound,
    simpa using hf, }
end

@[simp] lemma valuation_p : valuation (p : ℚ_[p]) = 1 :=
begin
  have h : (1 : ℝ) < p := by exact_mod_cast (fact.out p.prime).one_lt,
  rw ← neg_inj,
  apply (fpow_strict_mono h).injective,
  dsimp only,
  rw ← norm_eq_pow_val,
  { simp },
  { exact_mod_cast (fact.out p.prime).ne_zero }
end

end padic