/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau

! This file was ported from Lean 3 source module field_theory.is_alg_closed.basic
! leanprover-community/mathlib commit 3e00d81bdcbf77c8188bbd18f5524ddc3ed8cac6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.FieldTheory.PerfectClosure
import Mathbin.FieldTheory.Separable
import Mathbin.RingTheory.Adjoin.Field
import Mathbin.RingTheory.Localization.Integral

/-!
# Algebraically Closed Field

In this file we define the typeclass for algebraically closed fields and algebraic closures,
and prove some of their properties.

## Main Definitions

- `is_alg_closed k` is the typeclass saying `k` is an algebraically closed field, i.e. every
polynomial in `k` splits.

- `is_alg_closure R K` is the typeclass saying `K` is an algebraic closure of `R`, where `R` is a
  commutative ring. This means that the map from `R` to `K` is injective, and `K` is
  algebraically closed and algebraic over `R`

- `is_alg_closed.lift` is a map from an algebraic extension `L` of `R`, into any algebraically
  closed extension of `R`.

- `is_alg_closure.equiv` is a proof that any two algebraic closures of the
  same field are isomorphic.

## Tags

algebraic closure, algebraically closed

-/

universe u v w

open Classical BigOperators Polynomial

open Polynomial

variable (k : Type u) [Field k]

/-- Typeclass for algebraically closed fields.

To show `polynomial.splits p f` for an arbitrary ring homomorphism `f`,
see `is_alg_closed.splits_codomain` and `is_alg_closed.splits_domain`.
-/
class IsAlgClosed : Prop where
  Splits : ∀ p : k[X], p.Splits <| RingHom.id k
#align is_alg_closed IsAlgClosed

/-- Every polynomial splits in the field extension `f : K →+* k` if `k` is algebraically closed.

See also `is_alg_closed.splits_domain` for the case where `K` is algebraically closed.
-/
theorem IsAlgClosed.splits_codomain {k K : Type _} [Field k] [IsAlgClosed k] [Field K] {f : K →+* k}
    (p : K[X]) : p.Splits f := by
  convert IsAlgClosed.splits (p.map f)
  simp [splits_map_iff]
#align is_alg_closed.splits_codomain IsAlgClosed.splits_codomain

/-- Every polynomial splits in the field extension `f : K →+* k` if `K` is algebraically closed.

See also `is_alg_closed.splits_codomain` for the case where `k` is algebraically closed.
-/
theorem IsAlgClosed.splits_domain {k K : Type _} [Field k] [IsAlgClosed k] [Field K] {f : k →+* K}
    (p : k[X]) : p.Splits f :=
  Polynomial.splits_of_splits_id _ <| IsAlgClosed.splits _
#align is_alg_closed.splits_domain IsAlgClosed.splits_domain

namespace IsAlgClosed

variable {k}

theorem exists_root [IsAlgClosed k] (p : k[X]) (hp : p.degree ≠ 0) : ∃ x, IsRoot p x :=
  exists_root_of_splits _ (IsAlgClosed.splits p) hp
#align is_alg_closed.exists_root IsAlgClosed.exists_root

theorem exists_pow_nat_eq [IsAlgClosed k] (x : k) {n : ℕ} (hn : 0 < n) : ∃ z, z ^ n = x :=
  by
  rcases exists_root (X ^ n - C x) _ with ⟨z, hz⟩; swap
  · rw [degree_X_pow_sub_C hn x]
    exact ne_of_gt (WithBot.coe_lt_coe.2 hn)
  use z
  simp only [eval_C, eval_X, eval_pow, eval_sub, is_root.def] at hz
  exact sub_eq_zero.1 hz
#align is_alg_closed.exists_pow_nat_eq IsAlgClosed.exists_pow_nat_eq

theorem exists_eq_mul_self [IsAlgClosed k] (x : k) : ∃ z, x = z * z :=
  by
  rcases exists_pow_nat_eq x zero_lt_two with ⟨z, rfl⟩
  exact ⟨z, sq z⟩
#align is_alg_closed.exists_eq_mul_self IsAlgClosed.exists_eq_mul_self

theorem roots_eq_zero_iff [IsAlgClosed k] {p : k[X]} : p.roots = 0 ↔ p = Polynomial.C (p.coeff 0) :=
  by
  refine' ⟨fun h => _, fun hp => by rw [hp, roots_C]⟩
  cases' le_or_lt (degree p) 0 with hd hd
  · exact eq_C_of_degree_le_zero hd
  · obtain ⟨z, hz⟩ := IsAlgClosed.exists_root p hd.ne'
    rw [← mem_roots (ne_zero_of_degree_gt hd), h] at hz
    simpa using hz
#align is_alg_closed.roots_eq_zero_iff IsAlgClosed.roots_eq_zero_iff

theorem exists_eval₂_eq_zero_of_injective {R : Type _} [Ring R] [IsAlgClosed k] (f : R →+* k)
    (hf : Function.Injective f) (p : R[X]) (hp : p.degree ≠ 0) : ∃ x, p.eval₂ f x = 0 :=
  let ⟨x, hx⟩ := exists_root (p.map f) (by rwa [degree_map_eq_of_injective hf])
  ⟨x, by rwa [eval₂_eq_eval_map, ← is_root]⟩
#align is_alg_closed.exists_eval₂_eq_zero_of_injective IsAlgClosed.exists_eval₂_eq_zero_of_injective

theorem exists_eval₂_eq_zero {R : Type _} [Field R] [IsAlgClosed k] (f : R →+* k) (p : R[X])
    (hp : p.degree ≠ 0) : ∃ x, p.eval₂ f x = 0 :=
  exists_eval₂_eq_zero_of_injective f f.Injective p hp
#align is_alg_closed.exists_eval₂_eq_zero IsAlgClosed.exists_eval₂_eq_zero

variable (k)

theorem exists_aeval_eq_zero_of_injective {R : Type _} [CommRing R] [IsAlgClosed k] [Algebra R k]
    (hinj : Function.Injective (algebraMap R k)) (p : R[X]) (hp : p.degree ≠ 0) :
    ∃ x : k, aeval x p = 0 :=
  exists_eval₂_eq_zero_of_injective (algebraMap R k) hinj p hp
#align is_alg_closed.exists_aeval_eq_zero_of_injective IsAlgClosed.exists_aeval_eq_zero_of_injective

theorem exists_aeval_eq_zero {R : Type _} [Field R] [IsAlgClosed k] [Algebra R k] (p : R[X])
    (hp : p.degree ≠ 0) : ∃ x : k, aeval x p = 0 :=
  exists_eval₂_eq_zero (algebraMap R k) p hp
#align is_alg_closed.exists_aeval_eq_zero IsAlgClosed.exists_aeval_eq_zero

theorem of_exists_root (H : ∀ p : k[X], p.Monic → Irreducible p → ∃ x, p.eval x = 0) :
    IsAlgClosed k :=
  ⟨fun p =>
    Or.inr fun q hq hqp =>
      have : Irreducible (q * C (leadingCoeff q)⁻¹) :=
        by
        rw [← coe_norm_unit_of_ne_zero hq.ne_zero]
        exact (associated_normalize _).Irreducible hq
      let ⟨x, hx⟩ := H (q * C (leadingCoeff q)⁻¹) (monic_mul_leadingCoeff_inv hq.NeZero) this
      degree_mul_leadingCoeff_inv q hq.NeZero ▸ degree_eq_one_of_irreducible_of_root this hx⟩
#align is_alg_closed.of_exists_root IsAlgClosed.of_exists_root

theorem degree_eq_one_of_irreducible [IsAlgClosed k] {p : k[X]} (hp : Irreducible p) :
    p.degree = 1 :=
  degree_eq_one_of_irreducible_of_splits hp (IsAlgClosed.splits_codomain _)
#align is_alg_closed.degree_eq_one_of_irreducible IsAlgClosed.degree_eq_one_of_irreducible

theorem algebraMap_surjective_of_isIntegral {k K : Type _} [Field k] [Ring K] [IsDomain K]
    [hk : IsAlgClosed k] [Algebra k K] (hf : Algebra.IsIntegral k K) :
    Function.Surjective (algebraMap k K) :=
  by
  refine' fun x => ⟨-(minpoly k x).coeff 0, _⟩
  have hq : (minpoly k x).leadingCoeff = 1 := minpoly.monic (hf x)
  have h : (minpoly k x).degree = 1 := degree_eq_one_of_irreducible k (minpoly.irreducible (hf x))
  have : aeval x (minpoly k x) = 0 := minpoly.aeval k x
  rw [eq_X_add_C_of_degree_eq_one h, hq, C_1, one_mul, aeval_add, aeval_X, aeval_C,
    add_eq_zero_iff_eq_neg] at this
  exact (RingHom.map_neg (algebraMap k K) ((minpoly k x).coeff 0)).symm ▸ this.symm
#align is_alg_closed.algebra_map_surjective_of_is_integral IsAlgClosed.algebraMap_surjective_of_isIntegral

theorem algebra_map_surjective_of_is_integral' {k K : Type _} [Field k] [CommRing K] [IsDomain K]
    [hk : IsAlgClosed k] (f : k →+* K) (hf : f.IsIntegral) : Function.Surjective f :=
  @algebraMap_surjective_of_isIntegral k K _ _ _ _ f.toAlgebra hf
#align is_alg_closed.algebra_map_surjective_of_is_integral' IsAlgClosed.algebra_map_surjective_of_is_integral'

theorem algebraMap_surjective_of_isAlgebraic {k K : Type _} [Field k] [Ring K] [IsDomain K]
    [hk : IsAlgClosed k] [Algebra k K] (hf : Algebra.IsAlgebraic k K) :
    Function.Surjective (algebraMap k K) :=
  algebraMap_surjective_of_isIntegral (Algebra.isAlgebraic_iff_isIntegral.mp hf)
#align is_alg_closed.algebra_map_surjective_of_is_algebraic IsAlgClosed.algebraMap_surjective_of_isAlgebraic

end IsAlgClosed

/-- Typeclass for an extension being an algebraic closure. -/
class IsAlgClosure (R : Type u) (K : Type v) [CommRing R] [Field K] [Algebra R K]
  [NoZeroSMulDivisors R K] : Prop where
  alg_closed : IsAlgClosed K
  algebraic : Algebra.IsAlgebraic R K
#align is_alg_closure IsAlgClosure

theorem isAlgClosure_iff (K : Type v) [Field K] [Algebra k K] :
    IsAlgClosure k K ↔ IsAlgClosed K ∧ Algebra.IsAlgebraic k K :=
  ⟨fun h => ⟨h.1, h.2⟩, fun h => ⟨h.1, h.2⟩⟩
#align is_alg_closure_iff isAlgClosure_iff

namespace lift

/- In this section, the homomorphism from any algebraic extension into an algebraically
  closed extension is proven to exist. The assumption that M is algebraically closed could probably
  easily be switched to an assumption that M contains all the roots of polynomials in K -/
variable {K : Type u} {L : Type v} {M : Type w} [Field K] [Field L] [Algebra K L] [Field M]
  [Algebra K M] [IsAlgClosed M] (hL : Algebra.IsAlgebraic K L)

variable (K L M)

include hL

open Subalgebra AlgHom Function

/-- This structure is used to prove the existence of a homomorphism from any algebraic extension
into an algebraic closure -/
structure SubfieldWithHom where
  carrier : Subalgebra K L
  emb : carrier →ₐ[K] M
#align lift.subfield_with_hom lift.SubfieldWithHom

variable {K L M hL}

namespace SubfieldWithHom

variable {E₁ E₂ E₃ : SubfieldWithHom K L M hL}

instance : LE (SubfieldWithHom K L M hL)
    where le E₁ E₂ := ∃ h : E₁.carrier ≤ E₂.carrier, ∀ x, E₂.emb (inclusion h x) = E₁.emb x

noncomputable instance : Inhabited (SubfieldWithHom K L M hL) :=
  ⟨{  carrier := ⊥
      emb := (Algebra.ofId K M).comp (Algebra.botEquiv K L).toAlgHom }⟩

theorem le_def : E₁ ≤ E₂ ↔ ∃ h : E₁.carrier ≤ E₂.carrier, ∀ x, E₂.emb (inclusion h x) = E₁.emb x :=
  Iff.rfl
#align lift.subfield_with_hom.le_def lift.SubfieldWithHom.le_def

theorem compat (h : E₁ ≤ E₂) : ∀ x, E₂.emb (inclusion h.fst x) = E₁.emb x :=
  by
  rw [le_def] at h
  cases h
  assumption
#align lift.subfield_with_hom.compat lift.SubfieldWithHom.compat

instance : Preorder (SubfieldWithHom K L M hL)
    where
  le := (· ≤ ·)
  le_refl E := ⟨le_rfl, by simp⟩
  le_trans E₁ E₂ E₃ h₁₂ h₂₃ :=
    ⟨le_trans h₁₂.fst h₂₃.fst, fun _ => by
      erw [← inclusion_inclusion h₁₂.fst h₂₃.fst, compat, compat]⟩

open Lattice

theorem maximal_subfieldWithHom_chain_bounded (c : Set (SubfieldWithHom K L M hL))
    (hc : IsChain (· ≤ ·) c) : ∃ ub : SubfieldWithHom K L M hL, ∀ N, N ∈ c → N ≤ ub :=
  if hcn : c.Nonempty then
    let ub : SubfieldWithHom K L M hL :=
      haveI : Nonempty c := Set.Nonempty.to_subtype hcn
      { carrier := ⨆ i : c, (i : subfield_with_hom K L M hL).carrier
        emb :=
          Subalgebra.supᵢLift (fun i : c => (i : subfield_with_hom K L M hL).carrier)
            (fun i j =>
              let ⟨k, hik, hjk⟩ := directedOn_iff_directed.1 hc.directed_on i j
              ⟨k, hik.fst, hjk.fst⟩)
            (fun i => (i : subfield_with_hom K L M hL).emb)
            (by
              intro i j h
              ext x
              cases' hc.total i.prop j.prop with hij hji
              · simp [← hij.snd x]
              ·
                erw [AlgHom.comp_apply, ← hji.snd (inclusion h x), inclusion_inclusion,
                  inclusion_self, AlgHom.id_apply x])
            _ rfl }
    ⟨ub, fun N hN =>
      ⟨(le_supᵢ (fun i : c => (i : SubfieldWithHom K L M hL).carrier) ⟨N, hN⟩ : _),
        by
        intro x
        simp [ub]
        rfl⟩⟩
  else by
    rw [Set.not_nonempty_iff_eq_empty] at hcn
    simp [hcn]
#align lift.subfield_with_hom.maximal_subfield_with_hom_chain_bounded lift.SubfieldWithHom.maximal_subfieldWithHom_chain_bounded

variable (hL M)

theorem exists_maximal_subfieldWithHom : ∃ E : SubfieldWithHom K L M hL, ∀ N, E ≤ N → N ≤ E :=
  exists_maximal_of_chains_bounded maximal_subfieldWithHom_chain_bounded fun _ _ _ => le_trans
#align lift.subfield_with_hom.exists_maximal_subfield_with_hom lift.SubfieldWithHom.exists_maximal_subfieldWithHom

/-- The maximal `subfield_with_hom`. We later prove that this is equal to `⊤`. -/
noncomputable def maximalSubfieldWithHom : SubfieldWithHom K L M hL :=
  Classical.choose (exists_maximal_subfieldWithHom M hL)
#align lift.subfield_with_hom.maximal_subfield_with_hom lift.SubfieldWithHom.maximalSubfieldWithHom

theorem maximalSubfieldWithHom_is_maximal :
    ∀ N : SubfieldWithHom K L M hL,
      maximalSubfieldWithHom M hL ≤ N → N ≤ maximalSubfieldWithHom M hL :=
  Classical.choose_spec (exists_maximal_subfieldWithHom M hL)
#align lift.subfield_with_hom.maximal_subfield_with_hom_is_maximal lift.SubfieldWithHom.maximalSubfieldWithHom_is_maximal

theorem maximalSubfieldWithHom_eq_top : (maximalSubfieldWithHom M hL).carrier = ⊤ :=
  by
  rw [eq_top_iff]
  intro x _
  let p := minpoly K x
  let N : Subalgebra K L := (maximal_subfield_with_hom M hL).carrier
  letI : Field N := (Subalgebra.isField_of_algebraic N hL).toField
  letI : Algebra N M := (maximal_subfield_with_hom M hL).emb.toRingHom.toAlgebra
  cases'
    IsAlgClosed.exists_aeval_eq_zero M (minpoly N x)
      (ne_of_gt
        (minpoly.degree_pos
          (isAlgebraic_iff_isIntegral.1 (Algebra.isAlgebraic_of_larger_base _ _ hL x)))) with
    y hy
  let O : Subalgebra N L := Algebra.adjoin N {(x : L)}
  let larger_emb :=
    (AdjoinRoot.liftHom (minpoly N x) y hy).comp
      (AlgEquiv.adjoinSingletonEquivAdjoinRootMinpoly N x).toAlgHom
  have hNO : N ≤ O.restrict_scalars K := by
    intro z hz
    show algebraMap N L ⟨z, hz⟩ ∈ O
    exact O.algebra_map_mem _
  let O' : subfield_with_hom K L M hL :=
    { carrier := O.restrict_scalars K
      emb := larger_emb.restrict_scalars K }
  have hO' : maximal_subfield_with_hom M hL ≤ O' :=
    by
    refine' ⟨hNO, _⟩
    intro z
    show O'.emb (algebraMap N O z) = algebraMap N M z
    simp only [O', restrict_scalars_apply, AlgHom.commutes]
  refine' (maximal_subfield_with_hom_is_maximal M hL O' hO').fst _
  exact Algebra.subset_adjoin (Set.mem_singleton x)
#align lift.subfield_with_hom.maximal_subfield_with_hom_eq_top lift.SubfieldWithHom.maximalSubfieldWithHom_eq_top

end SubfieldWithHom

end lift

namespace IsAlgClosed

variable {K : Type u} [Field K] {L : Type v} {M : Type w} [Field L] [Algebra K L] [Field M]
  [Algebra K M] [IsAlgClosed M] (hL : Algebra.IsAlgebraic K L)

variable (K L M)

include hL

/-- Less general version of `lift`. -/
private noncomputable irreducible_def lift_aux : L →ₐ[K] M :=
  (lift.SubfieldWithHom.maximalSubfieldWithHom M hL).emb.comp <|
    Eq.recOn (lift.SubfieldWithHom.maximalSubfieldWithHom_eq_top M hL).symm Algebra.toTop
#align is_alg_closed.lift_aux is_alg_closed.lift_aux

omit hL

variable {R : Type u} [CommRing R]

variable {S : Type v} [CommRing S] [IsDomain S] [Algebra R S] [Algebra R M] [NoZeroSMulDivisors R S]
  [NoZeroSMulDivisors R M] (hS : Algebra.IsAlgebraic R S)

variable {M}

include hS

private theorem fraction_ring.is_algebraic :
    letI : IsDomain R := (NoZeroSMulDivisors.algebraMap_injective R S).IsDomain _
    Algebra.IsAlgebraic (FractionRing R) (FractionRing S) :=
  by
  intro inst x
  exact
    (IsFractionRing.isAlgebraic_iff R (FractionRing R) (FractionRing S)).1
      ((IsFractionRing.isAlgebraic_iff' R S (FractionRing S)).1 hS x)
#align is_alg_closed.fraction_ring.is_algebraic is_alg_closed.fraction_ring.is_algebraic

/-- A (random) homomorphism from an algebraic extension of R into an algebraically
  closed extension of R. -/
noncomputable irreducible_def lift : S →ₐ[R] M :=
  by
  letI : IsDomain R := (NoZeroSMulDivisors.algebraMap_injective R S).IsDomain _
  have hfRfS : Algebra.IsAlgebraic (FractionRing R) (FractionRing S) :=
    fraction_ring.is_algebraic hS
  let f : FractionRing S →ₐ[FractionRing R] M := lift_aux (FractionRing R) (FractionRing S) M hfRfS
  exact (f.restrict_scalars R).comp ((Algebra.ofId S (FractionRing S)).restrictScalars R)
#align is_alg_closed.lift IsAlgClosed.lift

omit hS

noncomputable instance (priority := 100) perfectRing (p : ℕ) [Fact p.Prime] [CharP k p]
    [IsAlgClosed k] : PerfectRing k p :=
  PerfectRing.ofSurjective k p fun x => IsAlgClosed.exists_pow_nat_eq _ <| NeZero.pos p
#align is_alg_closed.perfect_ring IsAlgClosed.perfectRing

/-- Algebraically closed fields are infinite since `Xⁿ⁺¹ - 1` is separable when `#K = n` -/
instance (priority := 500) {K : Type _} [Field K] [IsAlgClosed K] : Infinite K :=
  by
  apply Infinite.of_not_fintype
  intro hfin
  set n := Fintype.card K with hn
  set f := (X : K[X]) ^ (n + 1) - 1 with hf
  have hfsep : separable f := separable_X_pow_sub_C 1 (by simp) one_ne_zero
  apply Nat.not_succ_le_self (Fintype.card K)
  have hroot : n.succ = Fintype.card (f.root_set K) := by
    erw [card_root_set_eq_nat_degree hfsep (IsAlgClosed.splits_domain _), nat_degree_X_pow_sub_C]
  rw [hroot]
  exact Fintype.card_le_of_injective coe Subtype.coe_injective

end IsAlgClosed

namespace IsAlgClosure

variable (K : Type _) (J : Type _) (R : Type u) (S : Type _) [Field K] [Field J] [CommRing R]
  (L : Type v) (M : Type w) [Field L] [Field M] [Algebra R M] [NoZeroSMulDivisors R M]
  [IsAlgClosure R M] [Algebra K M] [IsAlgClosure K M] [CommRing S] [Algebra S L]
  [NoZeroSMulDivisors S L] [IsAlgClosure S L]

attribute [local instance] IsAlgClosure.alg_closed

section

variable [Algebra R L] [NoZeroSMulDivisors R L] [IsAlgClosure R L]

/-- A (random) isomorphism between two algebraic closures of `R`. -/
noncomputable def equiv : L ≃ₐ[R] M :=
  let f : L →ₐ[R] M := IsAlgClosed.lift IsAlgClosure.algebraic
  AlgEquiv.ofBijective f
    ⟨RingHom.injective f.toRingHom,
      by
      letI : Algebra L M := RingHom.toAlgebra f
      letI : IsScalarTower R L M :=
        IsScalarTower.of_algebraMap_eq (by simp [RingHom.algebraMap_toAlgebra])
      show Function.Surjective (algebraMap L M)
      exact
        IsAlgClosed.algebraMap_surjective_of_isAlgebraic
          (Algebra.isAlgebraic_of_larger_base_of_injective
            (NoZeroSMulDivisors.algebraMap_injective R _) IsAlgClosure.algebraic)⟩
#align is_alg_closure.equiv IsAlgClosure.equiv

end

section EquivOfAlgebraic

variable [Algebra R S] [Algebra R L] [IsScalarTower R S L]

variable [Algebra K J] [Algebra J L] [IsAlgClosure J L] [Algebra K L] [IsScalarTower K J L]

/-- A (random) isomorphism between an algebraic closure of `R` and an algebraic closure of
  an algebraic extension of `R` -/
noncomputable def equivOfAlgebraic' [Nontrivial S] [NoZeroSMulDivisors R S]
    (hRL : Algebra.IsAlgebraic R L) : L ≃ₐ[R] M :=
  by
  letI : NoZeroSMulDivisors R L :=
    NoZeroSMulDivisors.of_algebraMap_injective
      (by
        rw [IsScalarTower.algebraMap_eq R S L]
        exact
          Function.Injective.comp (NoZeroSMulDivisors.algebraMap_injective _ _)
            (NoZeroSMulDivisors.algebraMap_injective _ _))
  letI : IsAlgClosure R L :=
    { alg_closed := by infer_instance
      algebraic := hRL }
  exact IsAlgClosure.equiv _ _ _
#align is_alg_closure.equiv_of_algebraic' IsAlgClosure.equivOfAlgebraic'

/-- A (random) isomorphism between an algebraic closure of `K` and an algebraic closure
  of an algebraic extension of `K` -/
noncomputable def equivOfAlgebraic (hKJ : Algebra.IsAlgebraic K J) : L ≃ₐ[K] M :=
  equivOfAlgebraic' K J _ _ (Algebra.isAlgebraic_trans hKJ IsAlgClosure.algebraic)
#align is_alg_closure.equiv_of_algebraic IsAlgClosure.equivOfAlgebraic

end EquivOfAlgebraic

section EquivOfEquiv

variable {R S}

/-- Used in the definition of `equiv_of_equiv` -/
noncomputable def equivOfEquivAux (hSR : S ≃+* R) :
    { e : L ≃+* M // e.toRingHom.comp (algebraMap S L) = (algebraMap R M).comp hSR.toRingHom } :=
  by
  letI : Algebra R S := RingHom.toAlgebra hSR.symm.to_ring_hom
  letI : Algebra S R := RingHom.toAlgebra hSR.to_ring_hom
  letI : IsDomain R := (NoZeroSMulDivisors.algebraMap_injective R M).IsDomain _
  letI : IsDomain S := (NoZeroSMulDivisors.algebraMap_injective S L).IsDomain _
  have : Algebra.IsAlgebraic R S := fun x =>
    by
    rw [← RingEquiv.symm_apply_apply hSR x]
    exact isAlgebraic_algebraMap _
  letI : Algebra R L := RingHom.toAlgebra ((algebraMap S L).comp (algebraMap R S))
  haveI : IsScalarTower R S L := IsScalarTower.of_algebraMap_eq fun _ => rfl
  haveI : IsScalarTower S R L :=
    IsScalarTower.of_algebraMap_eq (by simp [RingHom.algebraMap_toAlgebra])
  haveI : NoZeroSMulDivisors R S := NoZeroSMulDivisors.of_algebraMap_injective hSR.symm.injective
  refine'
    ⟨equiv_of_algebraic' R S L M
        (Algebra.isAlgebraic_of_larger_base_of_injective
          (show Function.Injective (algebraMap S R) from hSR.injective) IsAlgClosure.algebraic),
      _⟩
  ext
  simp only [RingEquiv.toRingHom_eq_coe, Function.comp_apply, RingHom.coe_comp,
    AlgEquiv.coe_ringEquiv, RingEquiv.coe_toRingHom]
  conv_lhs => rw [← hSR.symm_apply_apply x]
  show equiv_of_algebraic' R S L M _ (algebraMap R L (hSR x)) = _
  rw [AlgEquiv.commutes]
#align is_alg_closure.equiv_of_equiv_aux IsAlgClosure.equivOfEquivAux

/-- Algebraic closure of isomorphic fields are isomorphic -/
noncomputable def equivOfEquiv (hSR : S ≃+* R) : L ≃+* M :=
  equivOfEquivAux L M hSR
#align is_alg_closure.equiv_of_equiv IsAlgClosure.equivOfEquiv

@[simp]
theorem equivOfEquiv_comp_algebraMap (hSR : S ≃+* R) :
    (↑(equivOfEquiv L M hSR) : L →+* M).comp (algebraMap S L) = (algebraMap R M).comp hSR :=
  (equivOfEquivAux L M hSR).2
#align is_alg_closure.equiv_of_equiv_comp_algebra_map IsAlgClosure.equivOfEquiv_comp_algebraMap

@[simp]
theorem equivOfEquiv_algebraMap (hSR : S ≃+* R) (s : S) :
    equivOfEquiv L M hSR (algebraMap S L s) = algebraMap R M (hSR s) :=
  RingHom.ext_iff.1 (equivOfEquiv_comp_algebraMap L M hSR) s
#align is_alg_closure.equiv_of_equiv_algebra_map IsAlgClosure.equivOfEquiv_algebraMap

@[simp]
theorem equivOfEquiv_symm_algebraMap (hSR : S ≃+* R) (r : R) :
    (equivOfEquiv L M hSR).symm (algebraMap R M r) = algebraMap S L (hSR.symm r) :=
  (equivOfEquiv L M hSR).Injective (by simp)
#align is_alg_closure.equiv_of_equiv_symm_algebra_map IsAlgClosure.equivOfEquiv_symm_algebraMap

@[simp]
theorem equivOfEquiv_symm_comp_algebraMap (hSR : S ≃+* R) :
    ((equivOfEquiv L M hSR).symm : M →+* L).comp (algebraMap R M) =
      (algebraMap S L).comp hSR.symm :=
  RingHom.ext_iff.2 (equivOfEquiv_symm_algebraMap L M hSR)
#align is_alg_closure.equiv_of_equiv_symm_comp_algebra_map IsAlgClosure.equivOfEquiv_symm_comp_algebraMap

end EquivOfEquiv

end IsAlgClosure

/-- Let `A` be an algebraically closed field and let `x ∈ K`, with `K/F` an algebraic extension
  of fields. Then the images of `x` by the `F`-algebra morphisms from `K` to `A` are exactly
  the roots in `A` of the minimal polynomial of `x` over `F`. -/
theorem Algebra.IsAlgebraic.range_eval_eq_rootSet_minpoly {F K} (A) [Field F] [Field K] [Field A]
    [IsAlgClosed A] [Algebra F K] (hK : Algebra.IsAlgebraic F K) [Algebra F A] (x : K) :
    (Set.range fun ψ : K →ₐ[F] A => ψ x) = (minpoly F x).rootSet A :=
  by
  have := Algebra.isAlgebraic_iff_isIntegral.1 hK
  ext a; rw [mem_root_set_of_ne (minpoly.ne_zero (this x))] <;> [skip, infer_instance]
  refine' ⟨_, fun ha => _⟩
  · rintro ⟨ψ, rfl⟩
    rw [aeval_alg_hom_apply ψ x, minpoly.aeval, map_zero]
  let Fx := AdjoinRoot (minpoly F x)
  have hx : aeval x (minpoly F x) = 0 := minpoly.aeval F x
  letI : Algebra Fx A := (AdjoinRoot.lift (algebraMap F A) a ha).toAlgebra
  letI : Algebra Fx K := (AdjoinRoot.lift (algebraMap F K) x hx).toAlgebra
  haveI : IsScalarTower F Fx A := IsScalarTower.of_ring_hom (AdjoinRoot.liftHom _ a ha)
  haveI : IsScalarTower F Fx K := IsScalarTower.of_ring_hom (AdjoinRoot.liftHom _ x hx)
  haveI : Fact (Irreducible <| minpoly F x) := ⟨minpoly.irreducible <| this x⟩
  let ψ₀ : K →ₐ[Fx] A := IsAlgClosed.lift (Algebra.isAlgebraic_of_larger_base F Fx hK)
  exact
    ⟨ψ₀.restrict_scalars F,
      (congr_arg ψ₀ (AdjoinRoot.lift_root hx).symm).trans <|
        (ψ₀.commutes _).trans <| AdjoinRoot.lift_root ha⟩
#align algebra.is_algebraic.range_eval_eq_root_set_minpoly Algebra.IsAlgebraic.range_eval_eq_rootSet_minpoly


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

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

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

open set filter metric
open_locale topological_space

variables (𝕜 : Type*) {E F G : Type*} [nondiscrete_normed_field 𝕜] [normed_group E]
  [normed_group F] [normed_space 𝕜 E] [normed_space 𝕜 F] [normed_group G] [normed_space 𝕜 G]
  {f g : E → F} {s t : set E} {x : E}

/-- A predicate saying that a function is continuous on a set and is differentiable on its interior.
This assumption naturally appears in many theorems in complex analysis. -/
@[protect_proj] structure diff_on_int_cont (f : E → F) (s : set E) : Prop :=
(differentiable_on : differentiable_on 𝕜 f (interior s))
(continuous_on : continuous_on f s)

variable {𝕜}

lemma differentiable_on.diff_on_int_cont (h : differentiable_on 𝕜 f s) :
  diff_on_int_cont 𝕜 f s :=
⟨h.mono interior_subset, h.continuous_on⟩

lemma differentiable.diff_on_int_cont (h : differentiable 𝕜 f) : diff_on_int_cont 𝕜 f s :=
h.differentiable_on.diff_on_int_cont

lemma diff_on_int_cont_open (hs : is_open s) :
  diff_on_int_cont 𝕜 f s ↔ differentiable_on 𝕜 f s :=
⟨λ h, hs.interior_eq ▸ h.differentiable_on, λ h, h.diff_on_int_cont⟩

lemma diff_on_int_cont_univ : diff_on_int_cont 𝕜 f univ ↔ differentiable 𝕜 f :=
(diff_on_int_cont_open is_open_univ).trans differentiable_on_univ

lemma diff_on_int_cont_const {c : F} :
  diff_on_int_cont 𝕜 (λ x : E, c) s :=
⟨differentiable_on_const c, continuous_on_const⟩

lemma differentiable_on.comp_diff_on_int_cont {g : G → E} {t : set G}
  (hf : differentiable_on 𝕜 f s) (hg : diff_on_int_cont 𝕜 g t) (h : maps_to g t s) :
  diff_on_int_cont 𝕜 (f ∘ g) t :=
⟨hf.comp hg.differentiable_on $ h.mono_left interior_subset, hf.continuous_on.comp hg.2 h⟩

lemma differentiable.comp_diff_on_int_cont {g : G → E} {t : set G}
  (hf : differentiable 𝕜 f) (hg : diff_on_int_cont 𝕜 g t) :
  diff_on_int_cont 𝕜 (f ∘ g) t :=
hf.differentiable_on.comp_diff_on_int_cont hg (maps_to_image _ _)

namespace diff_on_int_cont

lemma comp {g : G → E} {t : set G} (hf : diff_on_int_cont 𝕜 f s) (hg : diff_on_int_cont 𝕜 g t)
  (h : maps_to g t s) (h' : maps_to g (interior t) (interior s)) :
  diff_on_int_cont 𝕜 (f ∘ g) t :=
⟨hf.1.comp hg.1 h', hf.2.comp hg.2 h⟩

lemma differentiable_on_ball {x : E} {r : ℝ} (h : diff_on_int_cont 𝕜 f (closed_ball x r)) :
  differentiable_on 𝕜 f (ball x r) :=
h.differentiable_on.mono ball_subset_interior_closed_ball

lemma mk_ball [normed_space ℝ E] {x : E} {r : ℝ} (hd : differentiable_on 𝕜 f (ball x r))
  (hc : continuous_on f (closed_ball x r)) : diff_on_int_cont 𝕜 f (closed_ball x r) :=
begin
  refine ⟨_, hc⟩,
  rcases eq_or_ne r 0 with rfl|hr,
  { rw [closed_ball_zero],
    exact (subsingleton_singleton.mono interior_subset).differentiable_on },
  { rwa interior_closed_ball x hr }
end

protected lemma differentiable_at (h : diff_on_int_cont 𝕜 f s) (hx : x ∈ interior s) :
  differentiable_at 𝕜 f x :=
h.differentiable_on.differentiable_at $ is_open_interior.mem_nhds hx

lemma differentiable_at' (h : diff_on_int_cont 𝕜 f s) (hx : s ∈ 𝓝 x) :
  differentiable_at 𝕜 f x :=
h.differentiable_at (mem_interior_iff_mem_nhds.2 hx)

protected lemma mono (h : diff_on_int_cont 𝕜 f s) (ht : t ⊆ s) : diff_on_int_cont 𝕜 f t :=
⟨h.differentiable_on.mono (interior_mono ht), h.continuous_on.mono ht⟩

lemma add (hf : diff_on_int_cont 𝕜 f s) (hg : diff_on_int_cont 𝕜 g s) :
  diff_on_int_cont 𝕜 (f + g) s :=
⟨hf.1.add hg.1, hf.2.add hg.2⟩

lemma add_const (hf : diff_on_int_cont 𝕜 f s) (c : F) :
  diff_on_int_cont 𝕜 (λ x, f x + c) s :=
hf.add diff_on_int_cont_const

lemma const_add (hf : diff_on_int_cont 𝕜 f s) (c : F) :
  diff_on_int_cont 𝕜 (λ x, c + f x) s :=
diff_on_int_cont_const.add hf

lemma neg (hf : diff_on_int_cont 𝕜 f s) : diff_on_int_cont 𝕜 (-f) s := ⟨hf.1.neg, hf.2.neg⟩

lemma sub (hf : diff_on_int_cont 𝕜 f s) (hg : diff_on_int_cont 𝕜 g s) :
  diff_on_int_cont 𝕜 (f - g) s :=
⟨hf.1.sub hg.1, hf.2.sub hg.2⟩

lemma sub_const (hf : diff_on_int_cont 𝕜 f s) (c : F) : diff_on_int_cont 𝕜 (λ x, f x - c) s :=
hf.sub diff_on_int_cont_const

lemma const_sub (hf : diff_on_int_cont 𝕜 f s) (c : F) : diff_on_int_cont 𝕜 (λ x, c - f x) s :=
diff_on_int_cont_const.sub hf

lemma const_smul {R : Type*} [semiring R] [module R F] [smul_comm_class 𝕜 R F]
  [has_continuous_const_smul R F] (hf : diff_on_int_cont 𝕜 f s) (c : R) :
  diff_on_int_cont 𝕜 (c • f) s :=
⟨hf.1.const_smul c, hf.2.const_smul c⟩

lemma smul {𝕜' : Type*} [nondiscrete_normed_field 𝕜'] [normed_algebra 𝕜 𝕜']
  [normed_space 𝕜' F] [is_scalar_tower 𝕜 𝕜' F] {c : E → 𝕜'} {f : E → F} {s : set E}
  (hc : diff_on_int_cont 𝕜 c s) (hf : diff_on_int_cont 𝕜 f s) :
  diff_on_int_cont 𝕜 (λ x, c x • f x) s :=
⟨hc.1.smul hf.1, hc.2.smul hf.2⟩

lemma smul_const {𝕜' : Type*} [nondiscrete_normed_field 𝕜'] [normed_algebra 𝕜 𝕜']
  [normed_space 𝕜' F] [is_scalar_tower 𝕜 𝕜' F] {c : E → 𝕜'} {s : set E}
  (hc : diff_on_int_cont 𝕜 c s) (y : F) :
  diff_on_int_cont 𝕜 (λ x, c x • y) s :=
hc.smul diff_on_int_cont_const

lemma inv {f : E → 𝕜} (hf : diff_on_int_cont 𝕜 f s) (h₀ : ∀ x ∈ s, f x ≠ 0) :
  diff_on_int_cont 𝕜 f⁻¹ s :=
⟨differentiable_on_inv.comp hf.1 $ λ x hx, h₀ _ (interior_subset hx), hf.2.inv₀ h₀⟩

end diff_on_int_cont


/-
Copyright (c) 2022 Moritz Doll. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Moritz Doll, Anatole Dedecker

! This file was ported from Lean 3 source module analysis.locally_convex.with_seminorms
! leanprover-community/mathlib commit b31173ee05c911d61ad6a05bd2196835c932e0ec
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Seminorm
import Mathbin.Analysis.LocallyConvex.Bounded
import Mathbin.Topology.Algebra.FilterBasis
import Mathbin.Topology.Algebra.Module.LocallyConvex

/-!
# Topology induced by a family of seminorms

## Main definitions

* `seminorm_family.basis_sets`: The set of open seminorm balls for a family of seminorms.
* `seminorm_family.module_filter_basis`: A module filter basis formed by the open balls.
* `seminorm.is_bounded`: A linear map `f : E →ₗ[𝕜] F` is bounded iff every seminorm in `F` can be
bounded by a finite number of seminorms in `E`.

## Main statements

* `with_seminorms.to_locally_convex_space`: A space equipped with a family of seminorms is locally
convex.
* `with_seminorms.first_countable`: A space is first countable if it's topology is induced by a
countable family of seminorms.

## Continuity of semilinear maps

If `E` and `F` are topological vector space with the topology induced by a family of seminorms, then
we have a direct method to prove that a linear map is continuous:
* `seminorm.continuous_from_bounded`: A bounded linear map `f : E →ₗ[𝕜] F` is continuous.

If the topology of a space `E` is induced by a family of seminorms, then we can characterize von
Neumann boundedness in terms of that seminorm family. Together with
`linear_map.continuous_of_locally_bounded` this gives general criterion for continuity.

* `with_seminorms.is_vonN_bounded_iff_finset_seminorm_bounded`
* `with_seminorms.is_vonN_bounded_iff_seminorm_bounded`
* `with_seminorms.image_is_vonN_bounded_iff_finset_seminorm_bounded`
* `with_seminorms.image_is_vonN_bounded_iff_seminorm_bounded`

## Tags

seminorm, locally convex
-/

open NormedField Set Seminorm TopologicalSpace

open BigOperators NNReal Pointwise Topology

variable {𝕜 𝕜₂ 𝕝 𝕝₂ E F G ι ι' : Type _}

section FilterBasis

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E]

variable (𝕜 E ι)

/-- An abbreviation for indexed families of seminorms. This is mainly to allow for dot-notation. -/
abbrev SeminormFamily :=
  ι → Seminorm 𝕜 E
#align seminorm_family SeminormFamily

variable {𝕜 E ι}

namespace SeminormFamily

/-- The sets of a filter basis for the neighborhood filter of 0. -/
def basisSets (p : SeminormFamily 𝕜 E ι) : Set (Set E) :=
  ⋃ (s : Finset ι) (r) (hr : 0 < r), singleton <| ball (s.sup p) (0 : E) r
#align seminorm_family.basis_sets SeminormFamily.basisSets

variable (p : SeminormFamily 𝕜 E ι)

theorem basisSets_iff {U : Set E} :
    U ∈ p.basis_sets ↔ ∃ (i : Finset ι)(r : _)(hr : 0 < r), U = ball (i.sup p) 0 r := by
  simp only [basis_sets, mem_Union, mem_singleton_iff]
#align seminorm_family.basis_sets_iff SeminormFamily.basisSets_iff

theorem basisSets_mem (i : Finset ι) {r : ℝ} (hr : 0 < r) : (i.sup p).ball 0 r ∈ p.basis_sets :=
  (basisSets_iff _).mpr ⟨i, _, hr, rfl⟩
#align seminorm_family.basis_sets_mem SeminormFamily.basisSets_mem

theorem basisSets_singleton_mem (i : ι) {r : ℝ} (hr : 0 < r) : (p i).ball 0 r ∈ p.basis_sets :=
  (basisSets_iff _).mpr ⟨{i}, _, hr, by rw [Finset.sup_singleton]⟩
#align seminorm_family.basis_sets_singleton_mem SeminormFamily.basisSets_singleton_mem

theorem basisSets_nonempty [Nonempty ι] : p.basis_sets.Nonempty :=
  by
  let i := Classical.arbitrary ι
  refine' set.nonempty_def.mpr ⟨(p i).ball 0 1, _⟩
  exact p.basis_sets_singleton_mem i zero_lt_one
#align seminorm_family.basis_sets_nonempty SeminormFamily.basisSets_nonempty

theorem basisSets_intersect (U V : Set E) (hU : U ∈ p.basis_sets) (hV : V ∈ p.basis_sets) :
    ∃ (z : Set E)(H : z ∈ p.basis_sets), z ⊆ U ∩ V := by
  classical
    rcases p.basis_sets_iff.mp hU with ⟨s, r₁, hr₁, hU⟩
    rcases p.basis_sets_iff.mp hV with ⟨t, r₂, hr₂, hV⟩
    use ((s ∪ t).sup p).ball 0 (min r₁ r₂)
    refine' ⟨p.basis_sets_mem (s ∪ t) (lt_min_iff.mpr ⟨hr₁, hr₂⟩), _⟩
    rw [hU, hV, ball_finset_sup_eq_Inter _ _ _ (lt_min_iff.mpr ⟨hr₁, hr₂⟩),
      ball_finset_sup_eq_Inter _ _ _ hr₁, ball_finset_sup_eq_Inter _ _ _ hr₂]
    exact
      Set.subset_inter
        (Set.interᵢ₂_mono' fun i hi =>
          ⟨i, Finset.subset_union_left _ _ hi, ball_mono <| min_le_left _ _⟩)
        (Set.interᵢ₂_mono' fun i hi =>
          ⟨i, Finset.subset_union_right _ _ hi, ball_mono <| min_le_right _ _⟩)
#align seminorm_family.basis_sets_intersect SeminormFamily.basisSets_intersect

theorem basisSets_zero (U) (hU : U ∈ p.basis_sets) : (0 : E) ∈ U :=
  by
  rcases p.basis_sets_iff.mp hU with ⟨ι', r, hr, hU⟩
  rw [hU, mem_ball_zero, map_zero]
  exact hr
#align seminorm_family.basis_sets_zero SeminormFamily.basisSets_zero

theorem basisSets_add (U) (hU : U ∈ p.basis_sets) :
    ∃ (V : Set E)(H : V ∈ p.basis_sets), V + V ⊆ U :=
  by
  rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, hU⟩
  use (s.sup p).ball 0 (r / 2)
  refine' ⟨p.basis_sets_mem s (div_pos hr zero_lt_two), _⟩
  refine' Set.Subset.trans (ball_add_ball_subset (s.sup p) (r / 2) (r / 2) 0 0) _
  rw [hU, add_zero, add_halves']
#align seminorm_family.basis_sets_add SeminormFamily.basisSets_add

theorem basisSets_neg (U) (hU' : U ∈ p.basis_sets) :
    ∃ (V : Set E)(H : V ∈ p.basis_sets), V ⊆ (fun x : E => -x) ⁻¹' U :=
  by
  rcases p.basis_sets_iff.mp hU' with ⟨s, r, hr, hU⟩
  rw [hU, neg_preimage, neg_ball (s.sup p), neg_zero]
  exact ⟨U, hU', Eq.subset hU⟩
#align seminorm_family.basis_sets_neg SeminormFamily.basisSets_neg

/-- The `add_group_filter_basis` induced by the filter basis `seminorm_basis_zero`. -/
protected def addGroupFilterBasis [Nonempty ι] : AddGroupFilterBasis E :=
  addGroupFilterBasisOfComm p.basis_sets p.basisSets_nonempty p.basisSets_intersect p.basisSets_zero
    p.basisSets_add p.basisSets_neg
#align seminorm_family.add_group_filter_basis SeminormFamily.addGroupFilterBasis

theorem basisSets_smul_right (v : E) (U : Set E) (hU : U ∈ p.basis_sets) :
    ∀ᶠ x : 𝕜 in 𝓝 0, x • v ∈ U :=
  by
  rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, hU⟩
  rw [hU, Filter.eventually_iff]
  simp_rw [(s.sup p).mem_ball_zero, map_smul_eq_mul]
  by_cases h : 0 < (s.sup p) v
  · simp_rw [(lt_div_iff h).symm]
    rw [← _root_.ball_zero_eq]
    exact Metric.ball_mem_nhds 0 (div_pos hr h)
  simp_rw [le_antisymm (not_lt.mp h) (map_nonneg _ v), MulZeroClass.mul_zero, hr]
  exact IsOpen.mem_nhds isOpen_univ (mem_univ 0)
#align seminorm_family.basis_sets_smul_right SeminormFamily.basisSets_smul_right

variable [Nonempty ι]

theorem basisSets_smul (U) (hU : U ∈ p.basis_sets) :
    ∃ (V : Set 𝕜)(H : V ∈ 𝓝 (0 : 𝕜))(W : Set E)(H : W ∈ p.AddGroupFilterBasis.sets), V • W ⊆ U :=
  by
  rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, hU⟩
  refine' ⟨Metric.ball 0 r.sqrt, Metric.ball_mem_nhds 0 (real.sqrt_pos.mpr hr), _⟩
  refine' ⟨(s.sup p).ball 0 r.sqrt, p.basis_sets_mem s (real.sqrt_pos.mpr hr), _⟩
  refine' Set.Subset.trans (ball_smul_ball (s.sup p) r.sqrt r.sqrt) _
  rw [hU, Real.mul_self_sqrt (le_of_lt hr)]
#align seminorm_family.basis_sets_smul SeminormFamily.basisSets_smul

theorem basisSets_smul_left (x : 𝕜) (U : Set E) (hU : U ∈ p.basis_sets) :
    ∃ (V : Set E)(H : V ∈ p.AddGroupFilterBasis.sets), V ⊆ (fun y : E => x • y) ⁻¹' U :=
  by
  rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, hU⟩
  rw [hU]
  by_cases h : x ≠ 0
  · rw [(s.sup p).smul_ball_preimage 0 r x h, smul_zero]
    use (s.sup p).ball 0 (r / ‖x‖)
    exact ⟨p.basis_sets_mem s (div_pos hr (norm_pos_iff.mpr h)), subset.rfl⟩
  refine' ⟨(s.sup p).ball 0 r, p.basis_sets_mem s hr, _⟩
  simp only [not_ne_iff.mp h, subset_def, mem_ball_zero, hr, mem_univ, map_zero, imp_true_iff,
    preimage_const_of_mem, zero_smul]
#align seminorm_family.basis_sets_smul_left SeminormFamily.basisSets_smul_left

/-- The `module_filter_basis` induced by the filter basis `seminorm_basis_zero`. -/
protected def moduleFilterBasis : ModuleFilterBasis 𝕜 E
    where
  toAddGroupFilterBasis := p.AddGroupFilterBasis
  smul' := p.basisSets_smul
  smul_left' := p.basisSets_smul_left
  smul_right' := p.basisSets_smul_right
#align seminorm_family.module_filter_basis SeminormFamily.moduleFilterBasis

theorem filter_eq_infᵢ (p : SeminormFamily 𝕜 E ι) :
    p.ModuleFilterBasis.toFilterBasis.filterₓ = ⨅ i, (𝓝 0).comap (p i) :=
  by
  refine' le_antisymm (le_infᵢ fun i => _) _
  · rw [p.module_filter_basis.to_filter_basis.has_basis.le_basis_iff
        (metric.nhds_basis_ball.comap _)]
    intro ε hε
    refine' ⟨(p i).ball 0 ε, _, _⟩
    · rw [← (Finset.sup_singleton : _ = p i)]
      exact p.basis_sets_mem {i} hε
    · rw [id, (p i).ball_zero_eq_preimage_ball]
  · rw [p.module_filter_basis.to_filter_basis.has_basis.ge_iff]
    rintro U (hU : U ∈ p.basis_sets)
    rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, rfl⟩
    rw [id, Seminorm.ball_finset_sup_eq_interᵢ _ _ _ hr, s.Inter_mem_sets]
    exact fun i hi =>
      Filter.mem_infᵢ_of_mem i
        ⟨Metric.ball 0 r, Metric.ball_mem_nhds 0 hr,
          Eq.subset (p i).ball_zero_eq_preimage_ball.symm⟩
#align seminorm_family.filter_eq_infi SeminormFamily.filter_eq_infᵢ

end SeminormFamily

end FilterBasis

section Bounded

namespace Seminorm

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E]

variable [NormedField 𝕜₂] [AddCommGroup F] [Module 𝕜₂ F]

variable {σ₁₂ : 𝕜 →+* 𝕜₂} [RingHomIsometric σ₁₂]

-- Todo: This should be phrased entirely in terms of the von Neumann bornology.
/-- The proposition that a linear map is bounded between spaces with families of seminorms. -/
def IsBounded (p : ι → Seminorm 𝕜 E) (q : ι' → Seminorm 𝕜₂ F) (f : E →ₛₗ[σ₁₂] F) : Prop :=
  ∀ i, ∃ s : Finset ι, ∃ C : ℝ≥0, (q i).comp f ≤ C • s.sup p
#align seminorm.is_bounded Seminorm.IsBounded

theorem isBounded_const (ι' : Type _) [Nonempty ι'] {p : ι → Seminorm 𝕜 E} {q : Seminorm 𝕜₂ F}
    (f : E →ₛₗ[σ₁₂] F) :
    IsBounded p (fun _ : ι' => q) f ↔ ∃ (s : Finset ι)(C : ℝ≥0), q.comp f ≤ C • s.sup p := by
  simp only [is_bounded, forall_const]
#align seminorm.is_bounded_const Seminorm.isBounded_const

theorem const_isBounded (ι : Type _) [Nonempty ι] {p : Seminorm 𝕜 E} {q : ι' → Seminorm 𝕜₂ F}
    (f : E →ₛₗ[σ₁₂] F) : IsBounded (fun _ : ι => p) q f ↔ ∀ i, ∃ C : ℝ≥0, (q i).comp f ≤ C • p :=
  by
  constructor <;> intro h i
  · rcases h i with ⟨s, C, h⟩
    exact ⟨C, le_trans h (smul_le_smul (Finset.sup_le fun _ _ => le_rfl) le_rfl)⟩
  use {Classical.arbitrary ι}
  simp only [h, Finset.sup_singleton]
#align seminorm.const_is_bounded Seminorm.const_isBounded

theorem isBounded_sup {p : ι → Seminorm 𝕜 E} {q : ι' → Seminorm 𝕜₂ F} {f : E →ₛₗ[σ₁₂] F}
    (hf : IsBounded p q f) (s' : Finset ι') :
    ∃ (C : ℝ≥0)(s : Finset ι), (s'.sup q).comp f ≤ C • s.sup p := by
  classical
    obtain rfl | hs' := s'.eq_empty_or_nonempty
    · exact ⟨1, ∅, by simp [Seminorm.bot_eq_zero]⟩
    choose fₛ fC hf using hf
    use s'.card • s'.sup fC, Finset.bunionᵢ s' fₛ
    have hs : ∀ i : ι', i ∈ s' → (q i).comp f ≤ s'.sup fC • (Finset.bunionᵢ s' fₛ).sup p :=
      by
      intro i hi
      refine' (hf i).trans (smul_le_smul _ (Finset.le_sup hi))
      exact Finset.sup_mono (Finset.subset_bunionᵢ_of_mem fₛ hi)
    refine' (comp_mono f (finset_sup_le_sum q s')).trans _
    simp_rw [← pullback_apply, AddMonoidHom.map_sum, pullback_apply]
    refine' (Finset.sum_le_sum hs).trans _
    rw [Finset.sum_const, smul_assoc]
    exact le_rfl
#align seminorm.is_bounded_sup Seminorm.isBounded_sup

end Seminorm

end Bounded

section Topology

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [Nonempty ι]

/-- The proposition that the topology of `E` is induced by a family of seminorms `p`. -/
structure WithSeminorms (p : SeminormFamily 𝕜 E ι) [t : TopologicalSpace E] : Prop where
  topology_eq_withSeminorms : t = p.ModuleFilterBasis.topology
#align with_seminorms WithSeminorms

theorem WithSeminorms.withSeminorms_eq {p : SeminormFamily 𝕜 E ι} [t : TopologicalSpace E]
    (hp : WithSeminorms p) : t = p.ModuleFilterBasis.topology :=
  hp.1
#align with_seminorms.with_seminorms_eq WithSeminorms.withSeminorms_eq

variable [TopologicalSpace E]

variable {p : SeminormFamily 𝕜 E ι}

theorem WithSeminorms.topologicalAddGroup (hp : WithSeminorms p) : TopologicalAddGroup E :=
  by
  rw [hp.with_seminorms_eq]
  exact AddGroupFilterBasis.is_topological_add_group _
#align with_seminorms.topological_add_group WithSeminorms.topologicalAddGroup

theorem WithSeminorms.hasBasis (hp : WithSeminorms p) :
    (𝓝 (0 : E)).HasBasis (fun s : Set E => s ∈ p.basis_sets) id :=
  by
  rw [congr_fun (congr_arg (@nhds E) hp.1) 0]
  exact AddGroupFilterBasis.nhds_zero_hasBasis _
#align with_seminorms.has_basis WithSeminorms.hasBasis

theorem WithSeminorms.hasBasis_zero_ball (hp : WithSeminorms p) :
    (𝓝 (0 : E)).HasBasis (fun sr : Finset ι × ℝ => 0 < sr.2) fun sr => (sr.1.sup p).ball 0 sr.2 :=
  by
  refine' ⟨fun V => _⟩
  simp only [hp.has_basis.mem_iff, SeminormFamily.basisSets_iff, Prod.exists]
  constructor
  · rintro ⟨-, ⟨s, r, hr, rfl⟩, hV⟩
    exact ⟨s, r, hr, hV⟩
  · rintro ⟨s, r, hr, hV⟩
    exact ⟨_, ⟨s, r, hr, rfl⟩, hV⟩
#align with_seminorms.has_basis_zero_ball WithSeminorms.hasBasis_zero_ball

theorem WithSeminorms.hasBasis_ball (hp : WithSeminorms p) {x : E} :
    (𝓝 (x : E)).HasBasis (fun sr : Finset ι × ℝ => 0 < sr.2) fun sr => (sr.1.sup p).ball x sr.2 :=
  by
  haveI : TopologicalAddGroup E := hp.topological_add_group
  rw [← map_add_left_nhds_zero]
  convert hp.has_basis_zero_ball.map ((· + ·) x)
  ext sr : 1
  have : (sr.fst.sup p).ball (x +ᵥ 0) sr.snd = x +ᵥ (sr.fst.sup p).ball 0 sr.snd :=
    Eq.symm (Seminorm.vadd_ball (sr.fst.sup p))
  rwa [vadd_eq_add, add_zero] at this
#align with_seminorms.has_basis_ball WithSeminorms.hasBasis_ball

/-- The `x`-neighbourhoods of a space whose topology is induced by a family of seminorms
are exactly the sets which contain seminorm balls around `x`.-/
theorem WithSeminorms.mem_nhds_iff (hp : WithSeminorms p) (x : E) (U : Set E) :
    U ∈ nhds x ↔ ∃ s : Finset ι, ∃ r > 0, (s.sup p).ball x r ⊆ U := by
  rw [hp.has_basis_ball.mem_iff, Prod.exists]
#align with_seminorms.mem_nhds_iff WithSeminorms.mem_nhds_iff

/-- The open sets of a space whose topology is induced by a family of seminorms
are exactly the sets which contain seminorm balls around all of their points.-/
theorem WithSeminorms.isOpen_iff_mem_balls (hp : WithSeminorms p) (U : Set E) :
    IsOpen U ↔ ∀ x ∈ U, ∃ s : Finset ι, ∃ r > 0, (s.sup p).ball x r ⊆ U := by
  simp_rw [← WithSeminorms.mem_nhds_iff hp _ U, isOpen_iff_mem_nhds]
#align with_seminorms.is_open_iff_mem_balls WithSeminorms.isOpen_iff_mem_balls

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (x «expr ≠ » 0) -/
/- Note that through the following lemmas, one also immediately has that separating families
of seminorms induce T₂ and T₃ topologies by `topological_add_group.t2_space`
and `topological_add_group.t3_space` -/
/-- A separating family of seminorms induces a T₁ topology. -/
theorem WithSeminorms.t1_of_separating (hp : WithSeminorms p)
    (h : ∀ (x) (_ : x ≠ 0), ∃ i, p i x ≠ 0) : T1Space E :=
  by
  haveI := hp.topological_add_group
  refine' TopologicalAddGroup.t1Space _ _
  rw [← isOpen_compl_iff, hp.is_open_iff_mem_balls]
  rintro x (hx : x ≠ 0)
  cases' h x hx with i pi_nonzero
  refine' ⟨{i}, p i x, by positivity, subset_compl_singleton_iff.mpr _⟩
  rw [Finset.sup_singleton, mem_ball, zero_sub, map_neg_eq_map, not_lt]
#align with_seminorms.t1_of_separating WithSeminorms.t1_of_separating

/-- A family of seminorms inducing a T₁ topology is separating. -/
theorem WithSeminorms.separating_of_t1 [T1Space E] (hp : WithSeminorms p) (x : E) (hx : x ≠ 0) :
    ∃ i, p i x ≠ 0 := by
  have := ((t1Space_TFAE E).out 0 9).mp inferInstance
  by_contra' h
  refine' hx (this _)
  rw [hp.has_basis_zero_ball.specializes_iff]
  rintro ⟨s, r⟩ (hr : 0 < r)
  simp only [ball_finset_sup_eq_Inter _ _ _ hr, mem_Inter₂, mem_ball_zero, h, hr, forall_true_iff]
#align with_seminorms.separating_of_t1 WithSeminorms.separating_of_t1

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (x «expr ≠ » 0) -/
/-- A family of seminorms is separating iff it induces a T₁ topology. -/
theorem WithSeminorms.separating_iff_t1 (hp : WithSeminorms p) :
    (∀ (x) (_ : x ≠ 0), ∃ i, p i x ≠ 0) ↔ T1Space E :=
  by
  refine' ⟨WithSeminorms.t1_of_separating hp, _⟩
  intro
  exact WithSeminorms.separating_of_t1 hp
#align with_seminorms.separating_iff_t1 WithSeminorms.separating_iff_t1

end Topology

section Tendsto

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [Nonempty ι] [TopologicalSpace E]

variable {p : SeminormFamily 𝕜 E ι}

/-- Convergence along filters for `with_seminorms`.

Variant with `finset.sup`. -/
theorem WithSeminorms.tendsto_nhds' (hp : WithSeminorms p) (u : F → E) {f : Filter F} (y₀ : E) :
    Filter.Tendsto u f (𝓝 y₀) ↔ ∀ (s : Finset ι) (ε), 0 < ε → ∀ᶠ x in f, s.sup p (u x - y₀) < ε :=
  by simp [hp.has_basis_ball.tendsto_right_iff]
#align with_seminorms.tendsto_nhds' WithSeminorms.tendsto_nhds'

/-- Convergence along filters for `with_seminorms`. -/
theorem WithSeminorms.tendsto_nhds (hp : WithSeminorms p) (u : F → E) {f : Filter F} (y₀ : E) :
    Filter.Tendsto u f (𝓝 y₀) ↔ ∀ i ε, 0 < ε → ∀ᶠ x in f, p i (u x - y₀) < ε :=
  by
  rw [hp.tendsto_nhds' u y₀]
  exact
    ⟨fun h i => by simpa only [Finset.sup_singleton] using h {i}, fun h s ε hε =>
      (s.eventually_all.2 fun i _ => h i ε hε).mono fun _ => finset_sup_apply_lt hε⟩
#align with_seminorms.tendsto_nhds WithSeminorms.tendsto_nhds

variable [SemilatticeSup F] [Nonempty F]

/-- Limit `→ ∞` for `with_seminorms`. -/
theorem WithSeminorms.tendsto_nhds_atTop (hp : WithSeminorms p) (u : F → E) (y₀ : E) :
    Filter.Tendsto u Filter.atTop (𝓝 y₀) ↔ ∀ i ε, 0 < ε → ∃ x₀, ∀ x, x₀ ≤ x → p i (u x - y₀) < ε :=
  by
  rw [hp.tendsto_nhds u y₀]
  exact forall₃_congr fun _ _ _ => Filter.eventually_atTop
#align with_seminorms.tendsto_nhds_at_top WithSeminorms.tendsto_nhds_atTop

end Tendsto

section TopologicalAddGroup

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E]

variable [t : TopologicalSpace E] [TopologicalAddGroup E]

variable [Nonempty ι]

include t

theorem SeminormFamily.withSeminormsOfNhds (p : SeminormFamily 𝕜 E ι)
    (h : 𝓝 (0 : E) = p.ModuleFilterBasis.toFilterBasis.filterₓ) : WithSeminorms p :=
  by
  refine'
    ⟨TopologicalAddGroup.ext inferInstance p.add_group_filter_basis.is_topological_add_group _⟩
  rw [AddGroupFilterBasis.nhds_zero_eq]
  exact h
#align seminorm_family.with_seminorms_of_nhds SeminormFamily.withSeminormsOfNhds

theorem SeminormFamily.withSeminormsOfHasBasis (p : SeminormFamily 𝕜 E ι)
    (h : (𝓝 (0 : E)).HasBasis (fun s : Set E => s ∈ p.basis_sets) id) : WithSeminorms p :=
  p.withSeminormsOfNhds <|
    Filter.HasBasis.eq_of_same_basis h p.AddGroupFilterBasis.toFilterBasis.HasBasis
#align seminorm_family.with_seminorms_of_has_basis SeminormFamily.withSeminormsOfHasBasis

theorem SeminormFamily.withSeminorms_iff_nhds_eq_infᵢ (p : SeminormFamily 𝕜 E ι) :
    WithSeminorms p ↔ (𝓝 0 : Filter E) = ⨅ i, (𝓝 0).comap (p i) :=
  by
  rw [← p.filter_eq_infi]
  refine' ⟨fun h => _, p.with_seminorms_of_nhds⟩
  rw [h.topology_eq_with_seminorms]
  exact AddGroupFilterBasis.nhds_zero_eq _
#align seminorm_family.with_seminorms_iff_nhds_eq_infi SeminormFamily.withSeminorms_iff_nhds_eq_infᵢ

theorem WithSeminorms.continuous_seminorm [NontriviallyNormedField 𝕝] [Module 𝕝 E]
    [ContinuousConstSMul 𝕝 E] {p : SeminormFamily 𝕝 E ι} (hp : WithSeminorms p) (i : ι) :
    Continuous (p i) := by
  refine' Seminorm.continuous one_pos _
  rw [p.with_seminorms_iff_nhds_eq_infi.mp hp, ball_zero_eq_preimage_ball]
  exact Filter.mem_infᵢ_of_mem i (Filter.preimage_mem_comap <| Metric.ball_mem_nhds _ one_pos)
#align with_seminorms.continuous_seminorm WithSeminorms.continuous_seminorm

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `congrm #[[expr «expr = »(_, «expr⨅ , »((i), _))]] -/
/-- The topology induced by a family of seminorms is exactly the infimum of the ones induced by
each seminorm individually. We express this as a characterization of `with_seminorms p`. -/
theorem SeminormFamily.withSeminorms_iff_topologicalSpace_eq_infᵢ (p : SeminormFamily 𝕜 E ι) :
    WithSeminorms p ↔
      t =
        ⨅ i, (p i).toAddGroupSeminorm.toSeminormedAddCommGroup.toUniformSpace.toTopologicalSpace :=
  by
  rw [p.with_seminorms_iff_nhds_eq_infi,
    TopologicalAddGroup.ext_iff inferInstance (topologicalAddGroup_infᵢ fun i => inferInstance),
    nhds_infᵢ]
  trace
    "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `congrm #[[expr «expr = »(_, «expr⨅ , »((i), _))]]"
  exact @comap_norm_nhds_zero _ (p i).toAddGroupSeminorm.toSeminormedAddGroup
  all_goals infer_instance
#align seminorm_family.with_seminorms_iff_topological_space_eq_infi SeminormFamily.withSeminorms_iff_topologicalSpace_eq_infᵢ

omit t

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `congrm #[[expr «expr = »(_, «expr⨅ , »((i), _))]] -/
/-- The uniform structure induced by a family of seminorms is exactly the infimum of the ones
induced by each seminorm individually. We express this as a characterization of
`with_seminorms p`. -/
theorem SeminormFamily.withSeminorms_iff_uniformSpace_eq_infᵢ [u : UniformSpace E]
    [UniformAddGroup E] (p : SeminormFamily 𝕜 E ι) :
    WithSeminorms p ↔ u = ⨅ i, (p i).toAddGroupSeminorm.toSeminormedAddCommGroup.toUniformSpace :=
  by
  rw [p.with_seminorms_iff_nhds_eq_infi,
    UniformAddGroup.ext_iff inferInstance (uniformAddGroup_infᵢ fun i => inferInstance),
    toTopologicalSpace_infᵢ, nhds_infᵢ]
  trace
    "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `congrm #[[expr «expr = »(_, «expr⨅ , »((i), _))]]"
  exact @comap_norm_nhds_zero _ (p i).toAddGroupSeminorm.toSeminormedAddGroup
  all_goals infer_instance
#align seminorm_family.with_seminorms_iff_uniform_space_eq_infi SeminormFamily.withSeminorms_iff_uniformSpace_eq_infᵢ

end TopologicalAddGroup

section NormedSpace

/-- The topology of a `normed_space 𝕜 E` is induced by the seminorm `norm_seminorm 𝕜 E`. -/
theorem normWithSeminorms (𝕜 E) [NormedField 𝕜] [SeminormedAddCommGroup E] [NormedSpace 𝕜 E] :
    WithSeminorms fun _ : Fin 1 => normSeminorm 𝕜 E :=
  by
  let p : SeminormFamily 𝕜 E (Fin 1) := fun _ => normSeminorm 𝕜 E
  refine'
    ⟨seminormed_add_comm_group.to_topological_add_group.ext
        p.add_group_filter_basis.is_topological_add_group _⟩
  refine' Filter.HasBasis.eq_of_same_basis Metric.nhds_basis_ball _
  rw [← ball_normSeminorm 𝕜 E]
  refine'
    Filter.HasBasis.to_hasBasis p.add_group_filter_basis.nhds_zero_has_basis _ fun r hr =>
      ⟨(normSeminorm 𝕜 E).ball 0 r, p.basis_sets_singleton_mem 0 hr, rfl.subset⟩
  rintro U (hU : U ∈ p.basis_sets)
  rcases p.basis_sets_iff.mp hU with ⟨s, r, hr, hU⟩
  use r, hr
  rw [hU, id.def]
  by_cases h : s.nonempty
  · rw [Finset.sup_const h]
  rw [finset.not_nonempty_iff_eq_empty.mp h, Finset.sup_empty, ball_bot _ hr]
  exact Set.subset_univ _
#align norm_with_seminorms normWithSeminorms

end NormedSpace

section NontriviallyNormedField

variable [NontriviallyNormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [Nonempty ι]

variable {p : SeminormFamily 𝕜 E ι}

variable [TopologicalSpace E]

theorem WithSeminorms.isVonNBounded_iff_finset_seminorm_bounded {s : Set E} (hp : WithSeminorms p) :
    Bornology.IsVonNBounded 𝕜 s ↔ ∀ I : Finset ι, ∃ (r : _)(hr : 0 < r), ∀ x ∈ s, I.sup p x < r :=
  by
  rw [hp.has_basis.isVonNBounded_basis_iff]
  constructor
  · intro h I
    simp only [id.def] at h
    specialize h ((I.sup p).ball 0 1) (p.basis_sets_mem I zero_lt_one)
    rcases h with ⟨r, hr, h⟩
    cases' NormedField.exists_lt_norm 𝕜 r with a ha
    specialize h a (le_of_lt ha)
    rw [Seminorm.smul_ball_zero (norm_pos_iff.1 <| hr.trans ha), mul_one] at h
    refine' ⟨‖a‖, lt_trans hr ha, _⟩
    intro x hx
    specialize h hx
    exact (Finset.sup I p).mem_ball_zero.mp h
  intro h s' hs'
  rcases p.basis_sets_iff.mp hs' with ⟨I, r, hr, hs'⟩
  rw [id.def, hs']
  rcases h I with ⟨r', hr', h'⟩
  simp_rw [← (I.sup p).mem_ball_zero] at h'
  refine' Absorbs.mono_right _ h'
  exact (Finset.sup I p).ball_zero_absorbs_ball_zero hr
#align with_seminorms.is_vonN_bounded_iff_finset_seminorm_bounded WithSeminorms.isVonNBounded_iff_finset_seminorm_bounded

theorem WithSeminorms.image_isVonNBounded_iff_finset_seminorm_bounded (f : G → E) {s : Set G}
    (hp : WithSeminorms p) :
    Bornology.IsVonNBounded 𝕜 (f '' s) ↔
      ∀ I : Finset ι, ∃ (r : _)(hr : 0 < r), ∀ x ∈ s, I.sup p (f x) < r :=
  by simp_rw [hp.is_vonN_bounded_iff_finset_seminorm_bounded, Set.ball_image_iff]
#align with_seminorms.image_is_vonN_bounded_iff_finset_seminorm_bounded WithSeminorms.image_isVonNBounded_iff_finset_seminorm_bounded

theorem WithSeminorms.isVonNBounded_iff_seminorm_bounded {s : Set E} (hp : WithSeminorms p) :
    Bornology.IsVonNBounded 𝕜 s ↔ ∀ i : ι, ∃ (r : _)(hr : 0 < r), ∀ x ∈ s, p i x < r :=
  by
  rw [hp.is_vonN_bounded_iff_finset_seminorm_bounded]
  constructor
  · intro hI i
    convert hI {i}
    rw [Finset.sup_singleton]
  intro hi I
  by_cases hI : I.nonempty
  · choose r hr h using hi
    have h' : 0 < I.sup' hI r := by
      rcases hI.bex with ⟨i, hi⟩
      exact lt_of_lt_of_le (hr i) (Finset.le_sup' r hi)
    refine' ⟨I.sup' hI r, h', fun x hx => finset_sup_apply_lt h' fun i hi => _⟩
    refine' lt_of_lt_of_le (h i x hx) _
    simp only [Finset.le_sup'_iff, exists_prop]
    exact ⟨i, hi, (Eq.refl _).le⟩
  simp only [finset.not_nonempty_iff_eq_empty.mp hI, Finset.sup_empty, coe_bot, Pi.zero_apply,
    exists_prop]
  exact ⟨1, zero_lt_one, fun _ _ => zero_lt_one⟩
#align with_seminorms.is_vonN_bounded_iff_seminorm_bounded WithSeminorms.isVonNBounded_iff_seminorm_bounded

theorem WithSeminorms.image_isVonNBounded_iff_seminorm_bounded (f : G → E) {s : Set G}
    (hp : WithSeminorms p) :
    Bornology.IsVonNBounded 𝕜 (f '' s) ↔ ∀ i : ι, ∃ (r : _)(hr : 0 < r), ∀ x ∈ s, p i (f x) < r :=
  by simp_rw [hp.is_vonN_bounded_iff_seminorm_bounded, Set.ball_image_iff]
#align with_seminorms.image_is_vonN_bounded_iff_seminorm_bounded WithSeminorms.image_isVonNBounded_iff_seminorm_bounded

end NontriviallyNormedField

section ContinuousBounded

namespace Seminorm

variable [NontriviallyNormedField 𝕜] [AddCommGroup E] [Module 𝕜 E]

variable [NormedField 𝕝] [Module 𝕝 E]

variable [NontriviallyNormedField 𝕜₂] [AddCommGroup F] [Module 𝕜₂ F]

variable [NormedField 𝕝₂] [Module 𝕝₂ F]

variable {σ₁₂ : 𝕜 →+* 𝕜₂} [RingHomIsometric σ₁₂]

variable {τ₁₂ : 𝕝 →+* 𝕝₂} [RingHomIsometric τ₁₂]

variable [Nonempty ι] [Nonempty ι']

theorem continuous_of_continuous_comp {q : SeminormFamily 𝕝₂ F ι'} [TopologicalSpace E]
    [TopologicalAddGroup E] [TopologicalSpace F] [TopologicalAddGroup F] (hq : WithSeminorms q)
    (f : E →ₛₗ[τ₁₂] F) (hf : ∀ i, Continuous ((q i).comp f)) : Continuous f :=
  by
  refine' continuous_of_continuousAt_zero f _
  simp_rw [ContinuousAt, f.map_zero, q.with_seminorms_iff_nhds_eq_infi.mp hq, Filter.tendsto_infᵢ,
    Filter.tendsto_comap_iff]
  intro i
  convert(hf i).ContinuousAt
  exact (map_zero _).symm
#align seminorm.continuous_of_continuous_comp Seminorm.continuous_of_continuous_comp

theorem continuous_iff_continuous_comp {q : SeminormFamily 𝕜₂ F ι'} [TopologicalSpace E]
    [TopologicalAddGroup E] [TopologicalSpace F] [TopologicalAddGroup F] [ContinuousConstSMul 𝕜₂ F]
    (hq : WithSeminorms q) (f : E →ₛₗ[σ₁₂] F) : Continuous f ↔ ∀ i, Continuous ((q i).comp f) :=
  ⟨fun h i => Continuous.comp (hq.continuous_seminorm i) h, continuous_of_continuous_comp hq f⟩
#align seminorm.continuous_iff_continuous_comp Seminorm.continuous_iff_continuous_comp

theorem continuous_from_bounded {p : SeminormFamily 𝕝 E ι} {q : SeminormFamily 𝕝₂ F ι'}
    [TopologicalSpace E] [TopologicalAddGroup E] (hp : WithSeminorms p) [TopologicalSpace F]
    [TopologicalAddGroup F] (hq : WithSeminorms q) (f : E →ₛₗ[τ₁₂] F)
    (hf : Seminorm.IsBounded p q f) : Continuous f :=
  by
  refine' continuous_of_continuous_comp hq _ fun i => Seminorm.continuous_of_continuousAt_zero _
  rw [Metric.continuousAt_iff', map_zero]
  intro r hr
  rcases hf i with ⟨s₁, C, hf⟩
  have hC' : 0 < C + 1 := by positivity
  rw [hp.has_basis.eventually_iff]
  refine' ⟨(s₁.sup p).ball 0 (r / (C + 1)), p.basis_sets_mem _ (by positivity), _⟩
  simp_rw [← Metric.mem_ball, ← mem_preimage, ← ball_zero_eq_preimage_ball]
  refine' subset.trans _ (ball_antitone hf)
  norm_cast
  rw [← ball_smul (s₁.sup p) hC']
  refine' ball_antitone (smul_le_smul le_rfl _)
  simp only [le_add_iff_nonneg_right, zero_le']
#align seminorm.continuous_from_bounded Seminorm.continuous_from_bounded

theorem cont_withSeminorms_normedSpace (F) [SeminormedAddCommGroup F] [NormedSpace 𝕝₂ F]
    [UniformSpace E] [UniformAddGroup E] {p : ι → Seminorm 𝕝 E} (hp : WithSeminorms p)
    (f : E →ₛₗ[τ₁₂] F) (hf : ∃ (s : Finset ι)(C : ℝ≥0), (normSeminorm 𝕝₂ F).comp f ≤ C • s.sup p) :
    Continuous f := by
  rw [← Seminorm.isBounded_const (Fin 1)] at hf
  exact continuous_from_bounded hp (normWithSeminorms 𝕝₂ F) f hf
#align seminorm.cont_with_seminorms_normed_space Seminorm.cont_withSeminorms_normedSpace

theorem cont_normedSpace_to_withSeminorms (E) [SeminormedAddCommGroup E] [NormedSpace 𝕝 E]
    [UniformSpace F] [UniformAddGroup F] {q : ι → Seminorm 𝕝₂ F} (hq : WithSeminorms q)
    (f : E →ₛₗ[τ₁₂] F) (hf : ∀ i : ι, ∃ C : ℝ≥0, (q i).comp f ≤ C • normSeminorm 𝕝 E) :
    Continuous f := by
  rw [← Seminorm.const_isBounded (Fin 1)] at hf
  exact continuous_from_bounded (normWithSeminorms 𝕝 E) hq f hf
#align seminorm.cont_normed_space_to_with_seminorms Seminorm.cont_normedSpace_to_withSeminorms

end Seminorm

end ContinuousBounded

section LocallyConvexSpace

open LocallyConvexSpace

variable [Nonempty ι] [NormedField 𝕜] [NormedSpace ℝ 𝕜] [AddCommGroup E] [Module 𝕜 E] [Module ℝ E]
  [IsScalarTower ℝ 𝕜 E] [TopologicalSpace E] [TopologicalAddGroup E]

theorem WithSeminorms.toLocallyConvexSpace {p : SeminormFamily 𝕜 E ι} (hp : WithSeminorms p) :
    LocallyConvexSpace ℝ E :=
  by
  apply of_basis_zero ℝ E id fun s => s ∈ p.basis_sets
  · rw [hp.1, AddGroupFilterBasis.nhds_eq _, AddGroupFilterBasis.n_zero]
    exact FilterBasis.hasBasis _
  · intro s hs
    change s ∈ Set.unionᵢ _ at hs
    simp_rw [Set.mem_unionᵢ, Set.mem_singleton_iff] at hs
    rcases hs with ⟨I, r, hr, rfl⟩
    exact convex_ball _ _ _
#align with_seminorms.to_locally_convex_space WithSeminorms.toLocallyConvexSpace

end LocallyConvexSpace

section NormedSpace

variable (𝕜) [NormedField 𝕜] [NormedSpace ℝ 𝕜] [SeminormedAddCommGroup E]

/-- Not an instance since `𝕜` can't be inferred. See `normed_space.to_locally_convex_space` for a
slightly weaker instance version. -/
theorem NormedSpace.toLocallyConvexSpace' [NormedSpace 𝕜 E] [Module ℝ E] [IsScalarTower ℝ 𝕜 E] :
    LocallyConvexSpace ℝ E :=
  (normWithSeminorms 𝕜 E).toLocallyConvexSpace
#align normed_space.to_locally_convex_space' NormedSpace.toLocallyConvexSpace'

/-- See `normed_space.to_locally_convex_space'` for a slightly stronger version which is not an
instance. -/
instance NormedSpace.toLocallyConvexSpace [NormedSpace ℝ E] : LocallyConvexSpace ℝ E :=
  NormedSpace.toLocallyConvexSpace' ℝ
#align normed_space.to_locally_convex_space NormedSpace.toLocallyConvexSpace

end NormedSpace

section TopologicalConstructions

variable [NormedField 𝕜] [AddCommGroup E] [Module 𝕜 E]

variable [NormedField 𝕜₂] [AddCommGroup F] [Module 𝕜₂ F]

variable {σ₁₂ : 𝕜 →+* 𝕜₂} [RingHomIsometric σ₁₂]

/-- The family of seminorms obtained by composing each seminorm by a linear map. -/
def SeminormFamily.comp (q : SeminormFamily 𝕜₂ F ι) (f : E →ₛₗ[σ₁₂] F) : SeminormFamily 𝕜 E ι :=
  fun i => (q i).comp f
#align seminorm_family.comp SeminormFamily.comp

theorem SeminormFamily.comp_apply (q : SeminormFamily 𝕜₂ F ι) (i : ι) (f : E →ₛₗ[σ₁₂] F) :
    q.comp f i = (q i).comp f :=
  rfl
#align seminorm_family.comp_apply SeminormFamily.comp_apply

theorem SeminormFamily.finset_sup_comp (q : SeminormFamily 𝕜₂ F ι) (s : Finset ι)
    (f : E →ₛₗ[σ₁₂] F) : (s.sup q).comp f = s.sup (q.comp f) :=
  by
  ext x
  rw [Seminorm.comp_apply, Seminorm.finset_sup_apply, Seminorm.finset_sup_apply]
  rfl
#align seminorm_family.finset_sup_comp SeminormFamily.finset_sup_comp

variable [TopologicalSpace F] [TopologicalAddGroup F]

theorem LinearMap.withSeminormsInduced [hι : Nonempty ι] {q : SeminormFamily 𝕜₂ F ι}
    (hq : WithSeminorms q) (f : E →ₛₗ[σ₁₂] F) :
    @WithSeminorms 𝕜 E ι _ _ _ _ (q.comp f) (induced f inferInstance) :=
  by
  letI : TopologicalSpace E := induced f inferInstance
  letI : TopologicalAddGroup E := topologicalAddGroup_induced f
  rw [(q.comp f).withSeminorms_iff_nhds_eq_infᵢ, nhds_induced, map_zero,
    q.with_seminorms_iff_nhds_eq_infi.mp hq, Filter.comap_infᵢ]
  refine' infᵢ_congr fun i => _
  exact Filter.comap_comap
#align linear_map.with_seminorms_induced LinearMap.withSeminormsInduced

theorem Inducing.withSeminorms [hι : Nonempty ι] {q : SeminormFamily 𝕜₂ F ι} (hq : WithSeminorms q)
    [TopologicalSpace E] {f : E →ₛₗ[σ₁₂] F} (hf : Inducing f) : WithSeminorms (q.comp f) :=
  by
  rw [hf.induced]
  exact f.with_seminorms_induced hq
#align inducing.with_seminorms Inducing.withSeminorms

end TopologicalConstructions

section TopologicalProperties

variable [NontriviallyNormedField 𝕜] [AddCommGroup E] [Module 𝕜 E] [Nonempty ι] [Countable ι]

variable {p : SeminormFamily 𝕜 E ι}

variable [UniformSpace E] [UniformAddGroup E]

/-- If the topology of a space is induced by a countable family of seminorms, then the topology
is first countable. -/
theorem WithSeminorms.first_countable (hp : WithSeminorms p) :
    TopologicalSpace.FirstCountableTopology E :=
  by
  have : (𝓝 (0 : E)).IsCountablyGenerated :=
    by
    rw [p.with_seminorms_iff_nhds_eq_infi.mp hp]
    exact Filter.infᵢ.isCountablyGenerated _
  haveI : (uniformity E).IsCountablyGenerated := UniformAddGroup.uniformity_countably_generated
  exact UniformSpace.firstCountableTopology E
#align with_seminorms.first_countable WithSeminorms.first_countable

end TopologicalProperties