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

! This file was ported from Lean 3 source module category_theory.limits.shapes.products
! leanprover-community/mathlib commit e11bafa5284544728bd3b189942e930e0d4701de
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.CategoryTheory.Limits.HasLimits
import Mathlib.CategoryTheory.DiscreteCategory

/-!
# Categorical (co)products

This file defines (co)products as special cases of (co)limits.

A product is the categorical generalization of the object `Π i, f i` where `f : ι → C`. It is a
limit cone over the diagram formed by `f`, implemented by converting `f` into a functor
`Discrete ι ⥤ C`.

A coproduct is the dual concept.

## Main definitions

* a `Fan` is a cone over a discrete category
* `Fan.mk` constructs a fan from an indexed collection of maps
* a `Pi` is a `limit (Discrete.functor f)`

Each of these has a dual.

## Implementation notes
As with the other special shapes in the limits library, all the definitions here are given as
`abbreviation`s of the general statements for limits, so all the `simp` lemmas and theorems about
general limits can be used.
-/

noncomputable section

universe w v v₂ u u₂

open CategoryTheory

namespace CategoryTheory.Limits

variable {β : Type w}

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

-- We don't need an analogue of `Pair` (for binary products), `ParallelPair` (for equalizers),
-- or `(Co)span`, since we already have `Discrete.functor`.
--attribute [local tidy] tactic.discrete_cases -- Porting note: no tidy

/-- A fan over `f : β → C` consists of a collection of maps from an object `P` to every `f b`. -/
abbrev Fan (f : β → C) :=
  Cone (Discrete.functor f)
#align category_theory.limits.fan CategoryTheory.Limits.Fan

/-- A cofan over `f : β → C` consists of a collection of maps from every `f b` to an object `P`. -/
abbrev Cofan (f : β → C) :=
  Cocone (Discrete.functor f)
#align category_theory.limits.cofan CategoryTheory.Limits.Cofan

/-- A fan over `f : β → C` consists of a collection of maps from an object `P` to every `f b`. -/
@[simps]
def Fan.mk {f : β → C} (P : C) (p : ∀ b, P ⟶ f b) : Fan f
    where
  pt := P
  π := Discrete.natTrans (fun X => p X.as)
#align category_theory.limits.fan.mk CategoryTheory.Limits.Fan.mk

/-- A cofan over `f : β → C` consists of a collection of maps from every `f b` to an object `P`. -/
@[simps]
def Cofan.mk {f : β → C} (P : C) (p : ∀ b, f b ⟶ P) : Cofan f
    where
  pt := P
  ι := Discrete.natTrans (fun X => p X.as)
#align category_theory.limits.cofan.mk CategoryTheory.Limits.Cofan.mk

-- FIXME dualize as needed below (and rename?)
/-- Get the `j`th map in the fan -/
def Fan.proj {f : β → C} (p : Fan f) (j : β) : p.pt ⟶ f j :=
  p.π.app (Discrete.mk j)
#align category_theory.limits.fan.proj CategoryTheory.Limits.Fan.proj

@[simp]
theorem fan_mk_proj {f : β → C} (P : C) (p : ∀ b, P ⟶ f b) (j : β) : (Fan.mk P p).proj j = p j :=
  rfl
#align category_theory.limits.fan_mk_proj CategoryTheory.Limits.fan_mk_proj

/-- An abbreviation for `HasLimit (Discrete.functor f)`. -/
abbrev HasProduct (f : β → C) :=
  HasLimit (Discrete.functor f)
#align category_theory.limits.has_product CategoryTheory.Limits.HasProduct

/-- An abbreviation for `HasColimit (Discrete.functor f)`. -/
abbrev HasCoproduct (f : β → C) :=
  HasColimit (Discrete.functor f)
#align category_theory.limits.has_coproduct CategoryTheory.Limits.HasCoproduct

/-- Make a fan `f` into a limit fan by providing `lift`, `fac`, and `uniq` --
  just a convenience lemma to avoid having to go through `Discrete` -/
@[simps]
def mkFanLimit {f : β → C} (t : Fan f) (lift : ∀ s : Fan f, s.pt ⟶ t.pt)
    (fac : ∀ (s : Fan f) (j : β), lift s ≫ t.proj j = s.proj j)
    (uniq : ∀ (s : Fan f) (m : s.pt ⟶ t.pt) (_ : ∀ j : β, m ≫ t.proj j = s.proj j), m = lift s) :
    IsLimit t :=
  { lift
    fac := fun s j => fac s j.as
    uniq := fun s m w => uniq s m fun j => w (Discrete.mk j) }
#align category_theory.limits.mk_fan_limit CategoryTheory.Limits.mkFanLimit

section

variable (C)

/-- An abbreviation for `HasLimitsOfShape (Discrete f)`. -/
abbrev HasProductsOfShape (β : Type v) :=
  HasLimitsOfShape.{v} (Discrete β)
#align category_theory.limits.has_products_of_shape CategoryTheory.Limits.HasProductsOfShape

/-- An abbreviation for `HasColimitsOfShape (Discrete f)`. -/
abbrev HasCoproductsOfShape (β : Type v) :=
  HasColimitsOfShape.{v} (Discrete β)
#align category_theory.limits.has_coproducts_of_shape CategoryTheory.Limits.HasCoproductsOfShape

end

/-- `piObj f` computes the product of a family of elements `f`.
(It is defined as an abbreviation for `limit (Discrete.functor f)`,
so for most facts about `piObj f`, you will just use general facts about limits.) -/
abbrev piObj (f : β → C) [HasProduct f] :=
  limit (Discrete.functor f)
#align category_theory.limits.pi_obj CategoryTheory.Limits.piObj

/-- `sigmaObj f` computes the coproduct of a family of elements `f`.
(It is defined as an abbreviation for `colimit (Discrete.functor f)`,
so for most facts about `sigmaObj f`, you will just use general facts about colimits.) -/
abbrev sigmaObj (f : β → C) [HasCoproduct f] :=
  colimit (Discrete.functor f)
#align category_theory.limits.sigma_obj CategoryTheory.Limits.sigmaObj

/-- notation for categorical products -/
notation "∏ " f:20 => piObj f

/-- notation for categorical coproducts -/
notation "∐ " f:20 => sigmaObj f

/-- The `b`-th projection from the pi object over `f` has the form `∏ f ⟶ f b`. -/
abbrev Pi.π (f : β → C) [HasProduct f] (b : β) : ∏ f ⟶ f b :=
  limit.π (Discrete.functor f) (Discrete.mk b)
#align category_theory.limits.pi.π CategoryTheory.Limits.Pi.π

/-- The `b`-th inclusion into the sigma object over `f` has the form `f b ⟶ ∐ f`. -/
abbrev Sigma.ι (f : β → C) [HasCoproduct f] (b : β) : f b ⟶ ∐ f :=
  colimit.ι (Discrete.functor f) (Discrete.mk b)
#align category_theory.limits.sigma.ι CategoryTheory.Limits.Sigma.ι

/-- The fan constructed of the projections from the product is limiting. -/
def productIsProduct (f : β → C) [HasProduct f] : IsLimit (Fan.mk _ (Pi.π f)) :=
  IsLimit.ofIsoLimit (limit.isLimit (Discrete.functor f)) (Cones.ext (Iso.refl _) (by aesop_cat))
#align category_theory.limits.product_is_product CategoryTheory.Limits.productIsProduct

/-- The cofan constructed of the inclusions from the coproduct is colimiting. -/
def coproductIsCoproduct (f : β → C) [HasCoproduct f] : IsColimit (Cofan.mk _ (Sigma.ι f)) :=
  IsColimit.ofIsoColimit (colimit.isColimit (Discrete.functor f))
    (Cocones.ext (Iso.refl _) (by aesop_cat))
#align category_theory.limits.coproduct_is_coproduct CategoryTheory.Limits.coproductIsCoproduct

/-- A collection of morphisms `P ⟶ f b` induces a morphism `P ⟶ ∏ f`. -/
abbrev Pi.lift {f : β → C} [HasProduct f] {P : C} (p : ∀ b, P ⟶ f b) : P ⟶ ∏ f :=
  limit.lift _ (Fan.mk P p)
#align category_theory.limits.pi.lift CategoryTheory.Limits.Pi.lift

/-- A collection of morphisms `f b ⟶ P` induces a morphism `∐ f ⟶ P`. -/
abbrev Sigma.desc {f : β → C} [HasCoproduct f] {P : C} (p : ∀ b, f b ⟶ P) : ∐ f ⟶ P :=
  colimit.desc _ (Cofan.mk P p)
#align category_theory.limits.sigma.desc CategoryTheory.Limits.Sigma.desc

/-- Construct a morphism between categorical products (indexed by the same type)
from a family of morphisms between the factors.
-/
abbrev Pi.map {f g : β → C} [HasProduct f] [HasProduct g] (p : ∀ b, f b ⟶ g b) : ∏ f ⟶ ∏ g :=
  limMap (Discrete.natTrans fun X => p X.as)
#align category_theory.limits.pi.map CategoryTheory.Limits.Pi.map

instance Pi.map_mono {f g : β → C} [HasProduct f] [HasProduct g] (p : ∀ b, f b ⟶ g b)
    [∀ i, Mono (p i)] : Mono <| Pi.map p :=
  @Limits.limMap_mono _ _ _ _ (Discrete.functor f) (Discrete.functor g) _ _
    (Discrete.natTrans fun X => p X.as) (by dsimp; infer_instance)
#align category_theory.limits.pi.map_mono CategoryTheory.Limits.Pi.map_mono

/-- Construct an isomorphism between categorical products (indexed by the same type)
from a family of isomorphisms between the factors.
-/
abbrev Pi.mapIso {f g : β → C} [HasProductsOfShape β C] (p : ∀ b, f b ≅ g b) : ∏ f ≅ ∏ g :=
  lim.mapIso (Discrete.natIso fun X => p X.as)
#align category_theory.limits.pi.map_iso CategoryTheory.Limits.Pi.mapIso

/-- Construct a morphism between categorical coproducts (indexed by the same type)
from a family of morphisms between the factors.
-/
abbrev Sigma.map {f g : β → C} [HasCoproduct f] [HasCoproduct g] (p : ∀ b, f b ⟶ g b) : ∐ f ⟶ ∐ g :=
  colimMap (Discrete.natTrans fun X => p X.as)
#align category_theory.limits.sigma.map CategoryTheory.Limits.Sigma.map

instance Sigma.map_epi {f g : β → C} [HasCoproduct f] [HasCoproduct g] (p : ∀ b, f b ⟶ g b)
    [∀ i, Epi (p i)] : Epi <| Sigma.map p :=
  @Limits.colimMap_epi _ _ _ _ (Discrete.functor f) (Discrete.functor g) _ _
    (Discrete.natTrans fun X => p X.as) (by dsimp; infer_instance)
#align category_theory.limits.sigma.map_epi CategoryTheory.Limits.Sigma.map_epi

/-- Construct an isomorphism between categorical coproducts (indexed by the same type)
from a family of isomorphisms between the factors.
-/
abbrev Sigma.mapIso {f g : β → C} [HasCoproductsOfShape β C] (p : ∀ b, f b ≅ g b) : ∐ f ≅ ∐ g :=
  colim.mapIso (Discrete.natIso fun X => p X.as)
#align category_theory.limits.sigma.map_iso CategoryTheory.Limits.Sigma.mapIso

section Comparison

variable {D : Type u₂} [Category.{v₂} D] (G : C ⥤ D)

variable (f : β → C)

/-- The comparison morphism for the product of `f`. This is an iso iff `G` preserves the product
of `f`, see `PreservesProduct.ofIsoComparison.of_iso_comparison`. -/
def piComparison [HasProduct f] [HasProduct fun b => G.obj (f b)] :
    G.obj (∏ f) ⟶ ∏ fun b => G.obj (f b) :=
  Pi.lift fun b => G.map (Pi.π f b)
#align category_theory.limits.pi_comparison CategoryTheory.Limits.piComparison

@[reassoc (attr := simp)]
theorem piComparison_comp_π [HasProduct f] [HasProduct fun b => G.obj (f b)] (b : β) :
    piComparison G f ≫ Pi.π _ b = G.map (Pi.π f b) :=
  limit.lift_π _ (Discrete.mk b)
#align category_theory.limits.pi_comparison_comp_π CategoryTheory.Limits.piComparison_comp_π

@[reassoc (attr := simp)]
theorem map_lift_piComparison [HasProduct f] [HasProduct fun b => G.obj (f b)] (P : C)
    (g : ∀ j, P ⟶ f j) : G.map (Pi.lift g) ≫ piComparison G f = Pi.lift fun j => G.map (g j) := by
  ext ⟨j⟩
  simp only [Discrete.functor_obj, Category.assoc, piComparison_comp_π, ← G.map_comp,
    limit.lift_π, Fan.mk_pt, Fan.mk_π, Discrete.natTrans_app]
#align category_theory.limits.map_lift_pi_comparison CategoryTheory.Limits.map_lift_piComparison

/-- The comparison morphism for the coproduct of `f`. This is an iso iff `G` preserves the coproduct
of `f`, see `PreservesCoroduct.ofIsoComparison.of_iso_comparison`. -/
def sigmaComparison [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] :
    (∐ fun b => G.obj (f b)) ⟶ G.obj (∐ f) :=
  Sigma.desc fun b => G.map (Sigma.ι f b)
#align category_theory.limits.sigma_comparison CategoryTheory.Limits.sigmaComparison

@[reassoc (attr := simp)]
theorem ι_comp_sigmaComparison [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] (b : β) :
    Sigma.ι _ b ≫ sigmaComparison G f = G.map (Sigma.ι f b) :=
  colimit.ι_desc _ (Discrete.mk b)
#align category_theory.limits.ι_comp_sigma_comparison CategoryTheory.Limits.ι_comp_sigmaComparison

@[reassoc (attr := simp)]
theorem sigmaComparison_map_desc [HasCoproduct f] [HasCoproduct fun b => G.obj (f b)] (P : C)
    (g : ∀ j, f j ⟶ P) :
    sigmaComparison G f ≫ G.map (Sigma.desc g) = Sigma.desc fun j => G.map (g j) := by
  ext ⟨j⟩
  simp only [Discrete.functor_obj, ι_comp_sigmaComparison_assoc, ← G.map_comp, colimit.ι_desc,
    Cofan.mk_pt, Cofan.mk_ι, Discrete.natTrans_app]
#align category_theory.limits.sigma_comparison_map_desc CategoryTheory.Limits.sigmaComparison_map_desc

end Comparison

variable (C)

/-- An abbreviation for `Π J, HasLimitsOfShape (Discrete J) C` -/
abbrev HasProducts :=
  ∀ J : Type w, HasLimitsOfShape (Discrete J) C
#align category_theory.limits.has_products CategoryTheory.Limits.HasProducts

/-- An abbreviation for `Π J, HasColimitsOfShape (Discrete J) C` -/
abbrev HasCoproducts :=
  ∀ J : Type w, HasColimitsOfShape (Discrete J) C
#align category_theory.limits.has_coproducts CategoryTheory.Limits.HasCoproducts

variable {C}

theorem has_smallest_products_of_hasProducts [HasProducts.{w} C] : HasProducts.{0} C := fun J =>
  hasLimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift : Discrete (ULift.{w} J) ≌ _)
#align category_theory.limits.has_smallest_products_of_has_products CategoryTheory.Limits.has_smallest_products_of_hasProducts

theorem has_smallest_coproducts_of_hasCoproducts [HasCoproducts.{w} C] : HasCoproducts.{0} C :=
  fun J =>
  hasColimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift : Discrete (ULift.{w} J) ≌ _)
#align category_theory.limits.has_smallest_coproducts_of_has_coproducts CategoryTheory.Limits.has_smallest_coproducts_of_hasCoproducts

theorem hasProducts_of_limit_fans (lf : ∀ {J : Type w} (f : J → C), Fan f)
    (lf_is_limit : ∀ {J : Type w} (f : J → C), IsLimit (lf f)) : HasProducts.{w} C :=
  fun _ : Type w =>
  { has_limit := fun F =>
      HasLimit.mk
        ⟨(Cones.postcompose Discrete.natIsoFunctor.inv).obj (lf fun j => F.obj ⟨j⟩),
          (IsLimit.postcomposeInvEquiv _ _).symm (lf_is_limit _)⟩ }
#align category_theory.limits.has_products_of_limit_fans CategoryTheory.Limits.hasProducts_of_limit_fans

/-!
(Co)products over a type with a unique term.
-/

section Unique

variable [Unique β] (f : β → C)

/-- The limit cone for the product over an index type with exactly one term. -/
@[simps]
def limitConeOfUnique : LimitCone (Discrete.functor f)
    where
  cone :=
    { pt := f default
      π := Discrete.natTrans (fun ⟨j⟩ => eqToHom (by
        dsimp
        congr
        apply Subsingleton.elim)) }
  isLimit :=
    { lift := fun s => s.π.app default
      fac := fun s j  =>
        by
        have h := Subsingleton.elim j default
        subst h
        dsimp
        simp
      uniq := fun s m w => by
        specialize w default
        dsimp at w
        simpa using w }
#align category_theory.limits.limit_cone_of_unique CategoryTheory.Limits.limitConeOfUnique

instance (priority := 100) hasProduct_unique : HasProduct f :=
  HasLimit.mk (limitConeOfUnique f)
#align category_theory.limits.has_product_unique CategoryTheory.Limits.hasProduct_unique

/-- A product over a index type with exactly one term is just the object over that term. -/
@[simps!]
def productUniqueIso : ∏ f ≅ f default :=
  IsLimit.conePointUniqueUpToIso (limit.isLimit _) (limitConeOfUnique f).isLimit
#align category_theory.limits.product_unique_iso CategoryTheory.Limits.productUniqueIso

/-- The colimit cocone for the coproduct over an index type with exactly one term. -/
@[simps]
def colimitCoconeOfUnique : ColimitCocone (Discrete.functor f)
    where
  cocone :=
    { pt := f default
      ι := Discrete.natTrans (fun ⟨j⟩ => eqToHom (by
        dsimp
        congr
        apply Subsingleton.elim)) }
  isColimit :=
    { desc := fun s => s.ι.app default
      fac := fun s j =>
        by
        have h := Subsingleton.elim j default
        subst h
        apply Category.id_comp
      uniq := fun s m w => by
        specialize w default
        dsimp at w
        erw [Category.id_comp] at w
        exact w }
#align category_theory.limits.colimit_cocone_of_unique CategoryTheory.Limits.colimitCoconeOfUnique

instance (priority := 100) hasCoproduct_unique : HasCoproduct f :=
  HasColimit.mk (colimitCoconeOfUnique f)
#align category_theory.limits.has_coproduct_unique CategoryTheory.Limits.hasCoproduct_unique

/-- A coproduct over a index type with exactly one term is just the object over that term. -/
@[simps!]
def coproductUniqueIso : ∐ f ≅ f default :=
  IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) (colimitCoconeOfUnique f).isColimit
#align category_theory.limits.coproduct_unique_iso CategoryTheory.Limits.coproductUniqueIso

end Unique

section Reindex

variable {γ : Type v} (ε : β ≃ γ) (f : γ → C)

section

variable [HasProduct f] [HasProduct (f ∘ ε)]

/-- Reindex a categorical product via an equivalence of the index types. -/
def Pi.reindex : piObj (f ∘ ε) ≅ piObj f :=
  HasLimit.isoOfEquivalence (Discrete.equivalence ε) (Discrete.natIso fun _ => Iso.refl _)
#align category_theory.limits.pi.reindex CategoryTheory.Limits.Pi.reindex

@[reassoc (attr := simp)]
theorem Pi.reindex_hom_π (b : β) : (Pi.reindex ε f).hom ≫ Pi.π f (ε b) = Pi.π (f ∘ ε) b := by
  dsimp [Pi.reindex]
  simp only [HasLimit.isoOfEquivalence_hom_π, Discrete.equivalence_inverse, Discrete.functor_obj,
    Function.comp_apply, Functor.id_obj, Discrete.equivalence_functor, Functor.comp_obj,
    Discrete.natIso_inv_app, Iso.refl_inv, Category.id_comp]
  dsimp
  simpa [eqToHom_map]
    using limit.w (Discrete.functor (f ∘ ε)) (Discrete.eqToHom' (ε.symm_apply_apply b))
#align category_theory.limits.pi.reindex_hom_π CategoryTheory.Limits.Pi.reindex_hom_π

@[reassoc (attr := simp)]
theorem Pi.reindex_inv_π (b : β) : (Pi.reindex ε f).inv ≫ Pi.π (f ∘ ε) b = Pi.π f (ε b) := by
  simp [Iso.inv_comp_eq]
#align category_theory.limits.pi.reindex_inv_π CategoryTheory.Limits.Pi.reindex_inv_π

end

section

variable [HasCoproduct f] [HasCoproduct (f ∘ ε)]

/-- Reindex a categorical coproduct via an equivalence of the index types. -/
def Sigma.reindex : sigmaObj (f ∘ ε) ≅ sigmaObj f :=
  HasColimit.isoOfEquivalence (Discrete.equivalence ε) (Discrete.natIso fun _ => Iso.refl _)
#align category_theory.limits.sigma.reindex CategoryTheory.Limits.Sigma.reindex

@[reassoc (attr := simp)]
theorem Sigma.ι_reindex_hom (b : β) :
    Sigma.ι (f ∘ ε) b ≫ (Sigma.reindex ε f).hom = Sigma.ι f (ε b) := by
  dsimp [Sigma.reindex]
  simp only [HasColimit.isoOfEquivalence_hom_π, Functor.id_obj, Discrete.functor_obj,
    Function.comp_apply, Discrete.equivalence_functor, Discrete.equivalence_inverse,
    Functor.comp_obj, Discrete.natIso_inv_app, Iso.refl_inv, Category.id_comp]
  have h := colimit.w (Discrete.functor f) (Discrete.eqToHom' (ε.apply_symm_apply (ε b)))
  simp only [Discrete.functor_obj] at h
  erw [← h, eqToHom_map, eqToHom_map, eqToHom_trans_assoc]
  all_goals { simp }
#align category_theory.limits.sigma.ι_reindex_hom CategoryTheory.Limits.Sigma.ι_reindex_hom

@[reassoc (attr := simp)]
theorem Sigma.ι_reindex_inv (b : β) :
    Sigma.ι f (ε b) ≫ (Sigma.reindex ε f).inv = Sigma.ι (f ∘ ε) b := by simp [Iso.comp_inv_eq]
#align category_theory.limits.sigma.ι_reindex_inv CategoryTheory.Limits.Sigma.ι_reindex_inv

end

end Reindex

end CategoryTheory.Limits


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_176
  (x : ℝ) :
  (x + 1)^2 * x = x^3 + 2 * x^2 + x :=
begin
  ring_nf,
end


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

import mathematica
import data.list.basic
open expr tactic

/-!
In this demo we use a brief computable representation of matrices
to verify instances of LU decomposition done by Mathematica.

With some extra glue, this could operate on mathlib's matrix representation.

The LU algorithm is only specified up to permutation, which we do not address here,
so this is only correct for simple cases.
-/

def {u} dot_lists {α : Type u} [has_zero α] [has_mul α] [has_add α] : list α → list α → α
| [a] [b] := a*b
| (h1::t1) (h2::t2) := h1*h2 + dot_lists t1 t2
| _ _ := 0

def {u} mul_lists {α : Type u} [has_zero α] [has_mul α] [has_add α] [inhabited α]
       (m1 m2 : list (list α))  : list (list α) :=
list.map (λ i, (list.map (λ j, dot_lists (list.inth m1 i) (list.inth (list.transpose m2) j))
         (list.range (list.inth m1 0).length))) (list.range m1.length)

infix `**`:50 := mul_lists

local attribute [instance]
def inhabited_of_has_zero {α} [has_zero α] : inhabited α := ⟨0⟩

@[reducible]
def {u} is_lower_triangular {α : Type u} [has_lt α] [has_zero α] (m : list (list α)) : Prop :=
∀ i < m^.length, ∀ j < (list.inth m i)^.length, i < j → list.inth (list.inth m i) j = 0

@[reducible]
def {u} is_upper_triangular {α : Type u} [has_lt α] [has_zero α] (m : list (list α)) : Prop :=
∀ i < m^.length, ∀ j < (list.inth m i)^.length, i > j → list.inth (list.inth m i) j = 0

meta def lu_tac : tactic unit :=
do t ← target,
   (lam _ _ _ bd) ← return $ app_arg t,
   (lam _ _ _ ande) ← return $ app_arg bd,
   `(%%_ ∧ %%_ ∧ %%_ = %%e) ← return $ ande,
   tp ← infer_type e,
   m ← mathematica.run_command_on_using
      (λ e, e ++ " // LeanForm // Activate // LUDecomp")
       e
      "matrix_factor.m",
   m2 ← to_expr ```((%%m : list %%tp)),
   lhs ← to_expr ```(list.inth %%m2 0), rhs ← to_expr ```(list.inth %%m2 1),
   existsi lhs, existsi rhs,
   `[refine ⟨dec_trivial, dec_trivial, _⟩], reflexivity

example : ∃ l u, is_lower_triangular l ∧ is_upper_triangular u
             ∧ l ** u = [[(1 : ℤ), 2], [3, 4]] :=
by lu_tac

example : ∃ l u, is_lower_triangular l ∧ is_upper_triangular u
             ∧ l ** u = [[1, 2, 3], [1, 4, 9], [1, 8, 27]] :=
by lu_tac


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

! This file was ported from Lean 3 source module topology.instances.ereal
! leanprover-community/mathlib commit 10bf4f825ad729c5653adc039dafa3622e7f93c9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Rat.Encodable
import Mathbin.Data.Real.Ereal
import Mathbin.Topology.Algebra.Order.MonotoneContinuity
import Mathbin.Topology.Instances.Ennreal

/-!
# Topological structure on `ereal`

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

We endow `ereal` with the order topology, and prove basic properties of this topology.

## Main results

* `coe : ℝ → ereal` is an open embedding
* `coe : ℝ≥0∞ → ereal` is an embedding
* The addition on `ereal` is continuous except at `(⊥, ⊤)` and at `(⊤, ⊥)`.
* Negation is a homeomorphism on `ereal`.

## Implementation

Most proofs are adapted from the corresponding proofs on `ℝ≥0∞`.
-/

noncomputable section

open Classical Set Filter Metric TopologicalSpace

open Classical Topology ENNReal NNReal BigOperators Filter

variable {α : Type _} [TopologicalSpace α]

namespace EReal

instance : TopologicalSpace EReal :=
  Preorder.topology EReal

instance : OrderTopology EReal :=
  ⟨rfl⟩

instance : T2Space EReal := by infer_instance

instance : SecondCountableTopology EReal :=
  ⟨by
    refine'
      ⟨⋃ q : ℚ, {{ a : EReal | a < (q : ℝ) }, { a : EReal | ((q : ℝ) : EReal) < a }},
        countable_Union fun a => (countable_singleton _).insert _, _⟩
    refine'
      le_antisymm
        (le_generateFrom <| by
          simp (config := { contextual := true }) [or_imp, isOpen_lt', isOpen_gt'])
        _
    apply le_generateFrom fun s h => _
    rcases h with ⟨a, hs | hs⟩ <;>
        [rw [show s = ⋃ q ∈ { q : ℚ | a < (q : ℝ) }, { b | ((q : ℝ) : EReal) < b }
            by
            ext x
            simpa only [hs, exists_prop, mem_Union] using lt_iff_exists_rat_btwn],
        rw [show s = ⋃ q ∈ { q : ℚ | ((q : ℝ) : EReal) < a }, { b | b < ((q : ℝ) : EReal) }
            by
            ext x
            simpa only [hs, and_comm', exists_prop, mem_Union] using lt_iff_exists_rat_btwn]] <;>
      · apply isOpen_unionᵢ
        intro q
        apply isOpen_unionᵢ
        intro hq
        apply generate_open.basic
        exact mem_Union.2 ⟨q, by simp⟩⟩

/-! ### Real coercion -/

/- warning: ereal.embedding_coe -> EReal.embedding_coe is a dubious translation:
lean 3 declaration is
  Embedding.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))))
but is expected to have type
  Embedding.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.instTopologicalSpaceEReal Real.toEReal
Case conversion may be inaccurate. Consider using '#align ereal.embedding_coe EReal.embedding_coeₓ'. -/
theorem embedding_coe : Embedding (coe : ℝ → EReal) :=
  ⟨⟨by
      refine' le_antisymm _ _
      · rw [@OrderTopology.topology_eq_generate_intervals EReal _, ← coinduced_le_iff_le_induced]
        refine' le_generateFrom fun s ha => _
        rcases ha with ⟨a, rfl | rfl⟩
        show IsOpen { b : ℝ | a < ↑b }
        · induction a using EReal.rec
          · simp only [isOpen_univ, bot_lt_coe, set_of_true]
          · simp only [EReal.coe_lt_coe_iff]
            exact isOpen_Ioi
          · simp only [set_of_false, isOpen_empty, not_top_lt]
        show IsOpen { b : ℝ | ↑b < a }
        · induction a using EReal.rec
          · simp only [not_lt_bot, set_of_false, isOpen_empty]
          · simp only [EReal.coe_lt_coe_iff]
            exact isOpen_Iio
          · simp only [isOpen_univ, coe_lt_top, set_of_true]
      · rw [@OrderTopology.topology_eq_generate_intervals ℝ _]
        refine' le_generateFrom fun s ha => _
        rcases ha with ⟨a, rfl | rfl⟩
        exact ⟨Ioi a, isOpen_Ioi, by simp [Ioi]⟩
        exact ⟨Iio a, isOpen_Iio, by simp [Iio]⟩⟩, fun a b => by
    simp only [imp_self, EReal.coe_eq_coe_iff]⟩
#align ereal.embedding_coe EReal.embedding_coe

/- warning: ereal.open_embedding_coe -> EReal.openEmbedding_coe is a dubious translation:
lean 3 declaration is
  OpenEmbedding.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))))
but is expected to have type
  OpenEmbedding.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.instTopologicalSpaceEReal Real.toEReal
Case conversion may be inaccurate. Consider using '#align ereal.open_embedding_coe EReal.openEmbedding_coeₓ'. -/
theorem openEmbedding_coe : OpenEmbedding (coe : ℝ → EReal) :=
  ⟨embedding_coe, by
    convert@isOpen_Ioo EReal _ _ _ ⊥ ⊤
    ext x
    induction x using EReal.rec
    · simp only [left_mem_Ioo, mem_range, coe_ne_bot, exists_false, not_false_iff]
    · simp only [mem_range_self, mem_Ioo, bot_lt_coe, coe_lt_top, and_self_iff]
    · simp only [mem_range, right_mem_Ioo, exists_false, coe_ne_top]⟩
#align ereal.open_embedding_coe EReal.openEmbedding_coe

/- warning: ereal.tendsto_coe -> EReal.tendsto_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {f : Filter.{u1} α} {m : α -> Real} {a : Real}, Iff (Filter.Tendsto.{u1, 0} α EReal (fun (a : α) => (fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) (m a)) f (nhds.{0} EReal EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a))) (Filter.Tendsto.{u1, 0} α Real m f (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) a))
but is expected to have type
  forall {α : Type.{u1}} {f : Filter.{u1} α} {m : α -> Real} {a : Real}, Iff (Filter.Tendsto.{u1, 0} α EReal (fun (a : α) => Real.toEReal (m a)) f (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Real.toEReal a))) (Filter.Tendsto.{u1, 0} α Real m f (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) a))
Case conversion may be inaccurate. Consider using '#align ereal.tendsto_coe EReal.tendsto_coeₓ'. -/
@[norm_cast]
theorem tendsto_coe {α : Type _} {f : Filter α} {m : α → ℝ} {a : ℝ} :
    Tendsto (fun a => (m a : EReal)) f (𝓝 ↑a) ↔ Tendsto m f (𝓝 a) :=
  embedding_coe.tendsto_nhds_iff.symm
#align ereal.tendsto_coe EReal.tendsto_coe

/- warning: continuous_coe_real_ereal -> continuous_coe_real_ereal is a dubious translation:
lean 3 declaration is
  Continuous.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))))
but is expected to have type
  Continuous.{0, 0} Real EReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.instTopologicalSpaceEReal Real.toEReal
Case conversion may be inaccurate. Consider using '#align continuous_coe_real_ereal continuous_coe_real_erealₓ'. -/
theorem continuous_coe_real_ereal : Continuous (coe : ℝ → EReal) :=
  embedding_coe.Continuous
#align continuous_coe_real_ereal continuous_coe_real_ereal

/- warning: ereal.continuous_coe_iff -> EReal.continuous_coe_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {f : α -> Real}, Iff (Continuous.{u1, 0} α EReal _inst_1 EReal.topologicalSpace (fun (a : α) => (fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) (f a))) (Continuous.{u1, 0} α Real _inst_1 (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) f)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {f : α -> Real}, Iff (Continuous.{u1, 0} α EReal _inst_1 EReal.instTopologicalSpaceEReal (fun (a : α) => Real.toEReal (f a))) (Continuous.{u1, 0} α Real _inst_1 (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) f)
Case conversion may be inaccurate. Consider using '#align ereal.continuous_coe_iff EReal.continuous_coe_iffₓ'. -/
theorem continuous_coe_iff {f : α → ℝ} : (Continuous fun a => (f a : EReal)) ↔ Continuous f :=
  embedding_coe.continuous_iff.symm
#align ereal.continuous_coe_iff EReal.continuous_coe_iff

/- warning: ereal.nhds_coe -> EReal.nhds_coe is a dubious translation:
lean 3 declaration is
  forall {r : Real}, Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) r)) (Filter.map.{0, 0} Real EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe)))) (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) r))
but is expected to have type
  forall {r : Real}, Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Real.toEReal r)) (Filter.map.{0, 0} Real EReal Real.toEReal (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) r))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_coe EReal.nhds_coeₓ'. -/
theorem nhds_coe {r : ℝ} : 𝓝 (r : EReal) = (𝓝 r).map coe :=
  (openEmbedding_coe.map_nhds_eq r).symm
#align ereal.nhds_coe EReal.nhds_coe

/- warning: ereal.nhds_coe_coe -> EReal.nhds_coe_coe is a dubious translation:
lean 3 declaration is
  forall {r : Real} {p : Real}, Eq.{1} (Filter.{0} (Prod.{0, 0} EReal EReal)) (nhds.{0} (Prod.{0, 0} EReal EReal) (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) (Prod.mk.{0, 0} EReal EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) r) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) p))) (Filter.map.{0, 0} (Prod.{0, 0} Real Real) (Prod.{0, 0} EReal EReal) (fun (p : Prod.{0, 0} Real Real) => Prod.mk.{0, 0} EReal EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) (Prod.fst.{0, 0} Real Real p)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) (Prod.snd.{0, 0} Real Real p))) (nhds.{0} (Prod.{0, 0} Real Real) (Prod.topologicalSpace.{0, 0} Real Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace))) (Prod.mk.{0, 0} Real Real r p)))
but is expected to have type
  forall {r : Real} {p : Real}, Eq.{1} (Filter.{0} (Prod.{0, 0} EReal EReal)) (nhds.{0} (Prod.{0, 0} EReal EReal) (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) (Prod.mk.{0, 0} EReal EReal (Real.toEReal r) (Real.toEReal p))) (Filter.map.{0, 0} (Prod.{0, 0} Real Real) (Prod.{0, 0} EReal EReal) (fun (p : Prod.{0, 0} Real Real) => Prod.mk.{0, 0} EReal EReal (Real.toEReal (Prod.fst.{0, 0} Real Real p)) (Real.toEReal (Prod.snd.{0, 0} Real Real p))) (nhds.{0} (Prod.{0, 0} Real Real) (instTopologicalSpaceProd.{0, 0} Real Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace))) (Prod.mk.{0, 0} Real Real r p)))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_coe_coe EReal.nhds_coe_coeₓ'. -/
theorem nhds_coe_coe {r p : ℝ} :
    𝓝 ((r : EReal), (p : EReal)) = (𝓝 (r, p)).map fun p : ℝ × ℝ => (p.1, p.2) :=
  ((openEmbedding_coe.Prod openEmbedding_coe).map_nhds_eq (r, p)).symm
#align ereal.nhds_coe_coe EReal.nhds_coe_coe

/- warning: ereal.tendsto_to_real -> EReal.tendsto_toReal is a dubious translation:
lean 3 declaration is
  forall {a : EReal}, (Ne.{1} EReal a (Top.top.{0} EReal EReal.hasTop)) -> (Ne.{1} EReal a (Bot.bot.{0} EReal EReal.hasBot)) -> (Filter.Tendsto.{0, 0} EReal Real EReal.toReal (nhds.{0} EReal EReal.topologicalSpace a) (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (EReal.toReal a)))
but is expected to have type
  forall {a : EReal}, (Ne.{1} EReal a (Top.top.{0} EReal EReal.instTopEReal)) -> (Ne.{1} EReal a (Bot.bot.{0} EReal instERealBot)) -> (Filter.Tendsto.{0, 0} EReal Real EReal.toReal (nhds.{0} EReal EReal.instTopologicalSpaceEReal a) (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (EReal.toReal a)))
Case conversion may be inaccurate. Consider using '#align ereal.tendsto_to_real EReal.tendsto_toRealₓ'. -/
theorem tendsto_toReal {a : EReal} (ha : a ≠ ⊤) (h'a : a ≠ ⊥) :
    Tendsto EReal.toReal (𝓝 a) (𝓝 a.toReal) :=
  by
  lift a to ℝ using And.intro ha h'a
  rw [nhds_coe, tendsto_map'_iff]
  exact tendsto_id
#align ereal.tendsto_to_real EReal.tendsto_toReal

/- warning: ereal.continuous_on_to_real -> EReal.continuousOn_toReal is a dubious translation:
lean 3 declaration is
  ContinuousOn.{0, 0} EReal Real EReal.topologicalSpace (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.toReal (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.booleanAlgebra.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.hasInsert.{0} EReal) (Bot.bot.{0} EReal EReal.hasBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.hasSingleton.{0} EReal) (Top.top.{0} EReal EReal.hasTop))))
but is expected to have type
  ContinuousOn.{0, 0} EReal Real EReal.instTopologicalSpaceEReal (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) EReal.toReal (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.instBooleanAlgebraSet.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.instInsertSet.{0} EReal) (Bot.bot.{0} EReal instERealBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.instSingletonSet.{0} EReal) (Top.top.{0} EReal EReal.instTopEReal))))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_on_to_real EReal.continuousOn_toRealₓ'. -/
theorem continuousOn_toReal : ContinuousOn EReal.toReal ({⊥, ⊤}ᶜ : Set EReal) := fun a ha =>
  ContinuousAt.continuousWithinAt
    (tendsto_toReal
      (by
        simp [not_or] at ha
        exact ha.2)
      (by
        simp [not_or] at ha
        exact ha.1))
#align ereal.continuous_on_to_real EReal.continuousOn_toReal

/- warning: ereal.ne_bot_top_homeomorph_real -> EReal.neBotTopHomeomorphReal is a dubious translation:
lean 3 declaration is
  Homeomorph.{0, 0} (coeSort.{1, 2} (Set.{0} EReal) Type (Set.hasCoeToSort.{0} EReal) (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.booleanAlgebra.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.hasInsert.{0} EReal) (Bot.bot.{0} EReal EReal.hasBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.hasSingleton.{0} EReal) (Top.top.{0} EReal EReal.hasTop))))) Real (Subtype.topologicalSpace.{0} EReal (fun (x : EReal) => Membership.Mem.{0, 0} EReal (Set.{0} EReal) (Set.hasMem.{0} EReal) x (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.booleanAlgebra.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.hasInsert.{0} EReal) (Bot.bot.{0} EReal EReal.hasBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.hasSingleton.{0} EReal) (Top.top.{0} EReal EReal.hasTop))))) EReal.topologicalSpace) (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace))
but is expected to have type
  Homeomorph.{0, 0} (Set.Elem.{0} EReal (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.instBooleanAlgebraSet.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.instInsertSet.{0} EReal) (Bot.bot.{0} EReal instERealBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.instSingletonSet.{0} EReal) (Top.top.{0} EReal EReal.instTopEReal))))) Real (instTopologicalSpaceSubtype.{0} EReal (fun (x : EReal) => Membership.mem.{0, 0} EReal (Set.{0} EReal) (Set.instMembershipSet.{0} EReal) x (HasCompl.compl.{0} (Set.{0} EReal) (BooleanAlgebra.toHasCompl.{0} (Set.{0} EReal) (Set.instBooleanAlgebraSet.{0} EReal)) (Insert.insert.{0, 0} EReal (Set.{0} EReal) (Set.instInsertSet.{0} EReal) (Bot.bot.{0} EReal instERealBot) (Singleton.singleton.{0, 0} EReal (Set.{0} EReal) (Set.instSingletonSet.{0} EReal) (Top.top.{0} EReal EReal.instTopEReal))))) EReal.instTopologicalSpaceEReal) (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace))
Case conversion may be inaccurate. Consider using '#align ereal.ne_bot_top_homeomorph_real EReal.neBotTopHomeomorphRealₓ'. -/
/-- The set of finite `ereal` numbers is homeomorphic to `ℝ`. -/
def neBotTopHomeomorphReal : ({⊥, ⊤}ᶜ : Set EReal) ≃ₜ ℝ :=
  {
    neTopBotEquivReal with
    continuous_toFun := continuousOn_iff_continuous_restrict.1 continuousOn_toReal
    continuous_invFun := continuous_coe_real_ereal.subtype_mk _ }
#align ereal.ne_bot_top_homeomorph_real EReal.neBotTopHomeomorphReal

/-! ### ennreal coercion -/

/- warning: ereal.embedding_coe_ennreal -> EReal.embedding_coe_ennreal is a dubious translation:
lean 3 declaration is
  Embedding.{0, 0} ENNReal EReal ENNReal.topologicalSpace EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENNReal EReal (HasLiftT.mk.{1, 1} ENNReal EReal (CoeTCₓ.coe.{1, 1} ENNReal EReal (coeBase.{1, 1} ENNReal EReal EReal.hasCoeENNReal))))
but is expected to have type
  Embedding.{0, 0} ENNReal EReal ENNReal.instTopologicalSpaceENNReal EReal.instTopologicalSpaceEReal ENNReal.toEReal
Case conversion may be inaccurate. Consider using '#align ereal.embedding_coe_ennreal EReal.embedding_coe_ennrealₓ'. -/
theorem embedding_coe_ennreal : Embedding (coe : ℝ≥0∞ → EReal) :=
  ⟨⟨by
      refine' le_antisymm _ _
      · rw [@OrderTopology.topology_eq_generate_intervals EReal _, ← coinduced_le_iff_le_induced]
        refine' le_generateFrom fun s ha => _
        rcases ha with ⟨a, rfl | rfl⟩
        show IsOpen { b : ℝ≥0∞ | a < ↑b }
        · induction' a using EReal.rec with x
          · simp only [isOpen_univ, bot_lt_coe_ennreal, set_of_true]
          · rcases le_or_lt 0 x with (h | h)
            · have : (x : EReal) = ((id ⟨x, h⟩ : ℝ≥0) : ℝ≥0∞) := rfl
              rw [this]
              simp only [id.def, coe_ennreal_lt_coe_ennreal_iff]
              exact isOpen_Ioi
            · have : ∀ y : ℝ≥0∞, (x : EReal) < y := fun y =>
                (EReal.coe_lt_coe_iff.2 h).trans_le (coe_ennreal_nonneg _)
              simp only [this, isOpen_univ, set_of_true]
          · simp only [set_of_false, isOpen_empty, not_top_lt]
        show IsOpen { b : ℝ≥0∞ | ↑b < a }
        · induction' a using EReal.rec with x
          · simp only [not_lt_bot, set_of_false, isOpen_empty]
          · rcases le_or_lt 0 x with (h | h)
            · have : (x : EReal) = ((id ⟨x, h⟩ : ℝ≥0) : ℝ≥0∞) := rfl
              rw [this]
              simp only [id.def, coe_ennreal_lt_coe_ennreal_iff]
              exact isOpen_Iio
            · convert isOpen_empty
              apply eq_empty_iff_forall_not_mem.2 fun y hy => lt_irrefl (x : EReal) _
              exact ((EReal.coe_lt_coe_iff.2 h).trans_le (coe_ennreal_nonneg y)).trans hy
          · simp only [← coe_ennreal_top, coe_ennreal_lt_coe_ennreal_iff]
            exact isOpen_Iio
      · rw [@OrderTopology.topology_eq_generate_intervals ℝ≥0∞ _]
        refine' le_generateFrom fun s ha => _
        rcases ha with ⟨a, rfl | rfl⟩
        exact ⟨Ioi a, isOpen_Ioi, by simp [Ioi]⟩
        exact ⟨Iio a, isOpen_Iio, by simp [Iio]⟩⟩, fun a b => by
    simp only [imp_self, coe_ennreal_eq_coe_ennreal_iff]⟩
#align ereal.embedding_coe_ennreal EReal.embedding_coe_ennreal

/- warning: ereal.tendsto_coe_ennreal -> EReal.tendsto_coe_ennreal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {f : Filter.{u1} α} {m : α -> ENNReal} {a : ENNReal}, Iff (Filter.Tendsto.{u1, 0} α EReal (fun (a : α) => (fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENNReal EReal (HasLiftT.mk.{1, 1} ENNReal EReal (CoeTCₓ.coe.{1, 1} ENNReal EReal (coeBase.{1, 1} ENNReal EReal EReal.hasCoeENNReal))) (m a)) f (nhds.{0} EReal EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENNReal EReal (HasLiftT.mk.{1, 1} ENNReal EReal (CoeTCₓ.coe.{1, 1} ENNReal EReal (coeBase.{1, 1} ENNReal EReal EReal.hasCoeENNReal))) a))) (Filter.Tendsto.{u1, 0} α ENNReal m f (nhds.{0} ENNReal ENNReal.topologicalSpace a))
but is expected to have type
  forall {α : Type.{u1}} {f : Filter.{u1} α} {m : α -> ENNReal} {a : ENNReal}, Iff (Filter.Tendsto.{u1, 0} α EReal (fun (a : α) => ENNReal.toEReal (m a)) f (nhds.{0} EReal EReal.instTopologicalSpaceEReal (ENNReal.toEReal a))) (Filter.Tendsto.{u1, 0} α ENNReal m f (nhds.{0} ENNReal ENNReal.instTopologicalSpaceENNReal a))
Case conversion may be inaccurate. Consider using '#align ereal.tendsto_coe_ennreal EReal.tendsto_coe_ennrealₓ'. -/
@[norm_cast]
theorem tendsto_coe_ennreal {α : Type _} {f : Filter α} {m : α → ℝ≥0∞} {a : ℝ≥0∞} :
    Tendsto (fun a => (m a : EReal)) f (𝓝 ↑a) ↔ Tendsto m f (𝓝 a) :=
  embedding_coe_ennreal.tendsto_nhds_iff.symm
#align ereal.tendsto_coe_ennreal EReal.tendsto_coe_ennreal

/- warning: continuous_coe_ennreal_ereal -> continuous_coe_ennreal_ereal is a dubious translation:
lean 3 declaration is
  Continuous.{0, 0} ENNReal EReal ENNReal.topologicalSpace EReal.topologicalSpace ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENNReal EReal (HasLiftT.mk.{1, 1} ENNReal EReal (CoeTCₓ.coe.{1, 1} ENNReal EReal (coeBase.{1, 1} ENNReal EReal EReal.hasCoeENNReal))))
but is expected to have type
  Continuous.{0, 0} ENNReal EReal ENNReal.instTopologicalSpaceENNReal EReal.instTopologicalSpaceEReal ENNReal.toEReal
Case conversion may be inaccurate. Consider using '#align continuous_coe_ennreal_ereal continuous_coe_ennreal_erealₓ'. -/
theorem continuous_coe_ennreal_ereal : Continuous (coe : ℝ≥0∞ → EReal) :=
  embedding_coe_ennreal.Continuous
#align continuous_coe_ennreal_ereal continuous_coe_ennreal_ereal

/- warning: ereal.continuous_coe_ennreal_iff -> EReal.continuous_coe_ennreal_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {f : α -> ENNReal}, Iff (Continuous.{u1, 0} α EReal _inst_1 EReal.topologicalSpace (fun (a : α) => (fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENNReal EReal (HasLiftT.mk.{1, 1} ENNReal EReal (CoeTCₓ.coe.{1, 1} ENNReal EReal (coeBase.{1, 1} ENNReal EReal EReal.hasCoeENNReal))) (f a))) (Continuous.{u1, 0} α ENNReal _inst_1 ENNReal.topologicalSpace f)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] {f : α -> ENNReal}, Iff (Continuous.{u1, 0} α EReal _inst_1 EReal.instTopologicalSpaceEReal (fun (a : α) => ENNReal.toEReal (f a))) (Continuous.{u1, 0} α ENNReal _inst_1 ENNReal.instTopologicalSpaceENNReal f)
Case conversion may be inaccurate. Consider using '#align ereal.continuous_coe_ennreal_iff EReal.continuous_coe_ennreal_iffₓ'. -/
theorem continuous_coe_ennreal_iff {f : α → ℝ≥0∞} :
    (Continuous fun a => (f a : EReal)) ↔ Continuous f :=
  embedding_coe_ennreal.continuous_iff.symm
#align ereal.continuous_coe_ennreal_iff EReal.continuous_coe_ennreal_iff

/-! ### Neighborhoods of infinity -/

/- warning: ereal.nhds_top -> EReal.nhds_top is a dubious translation:
lean 3 declaration is
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.topologicalSpace (Top.top.{0} EReal EReal.hasTop)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) EReal (fun (a : EReal) => infᵢ.{0, 0} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) (Ne.{1} EReal a (Top.top.{0} EReal EReal.hasTop)) (fun (H : Ne.{1} EReal a (Top.top.{0} EReal EReal.hasTop)) => Filter.principal.{0} EReal (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) a))))
but is expected to have type
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Top.top.{0} EReal EReal.instTopEReal)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) EReal (fun (a : EReal) => infᵢ.{0, 0} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) (Ne.{1} EReal a (Top.top.{0} EReal EReal.instTopEReal)) (fun (H : Ne.{1} EReal a (Top.top.{0} EReal EReal.instTopEReal)) => Filter.principal.{0} EReal (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) a))))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_top EReal.nhds_topₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a «expr ≠ » «expr⊤»()) -/
theorem nhds_top : 𝓝 (⊤ : EReal) = ⨅ (a) (_ : a ≠ ⊤), 𝓟 (Ioi a) :=
  nhds_top_order.trans <| by simp [lt_top_iff_ne_top, Ioi]
#align ereal.nhds_top EReal.nhds_top

/- warning: ereal.nhds_top' -> EReal.nhds_top' is a dubious translation:
lean 3 declaration is
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.topologicalSpace (Top.top.{0} EReal EReal.hasTop)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) Real (fun (a : Real) => Filter.principal.{0} EReal (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a))))
but is expected to have type
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Top.top.{0} EReal EReal.instTopEReal)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) Real (fun (a : Real) => Filter.principal.{0} EReal (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) (Real.toEReal a))))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_top' EReal.nhds_top'ₓ'. -/
theorem nhds_top' : 𝓝 (⊤ : EReal) = ⨅ a : ℝ, 𝓟 (Ioi a) :=
  by
  rw [nhds_top]
  apply le_antisymm
  · exact infᵢ_mono' fun x => ⟨x, by simp⟩
  · refine' le_infᵢ fun r => le_infᵢ fun hr => _
    induction r using EReal.rec
    · exact (infᵢ_le _ 0).trans (by simp)
    · exact infᵢ_le _ _
    · simpa using hr
#align ereal.nhds_top' EReal.nhds_top'

/- warning: ereal.mem_nhds_top_iff -> EReal.mem_nhds_top_iff is a dubious translation:
lean 3 declaration is
  forall {s : Set.{0} EReal}, Iff (Membership.Mem.{0, 0} (Set.{0} EReal) (Filter.{0} EReal) (Filter.hasMem.{0} EReal) s (nhds.{0} EReal EReal.topologicalSpace (Top.top.{0} EReal EReal.hasTop))) (Exists.{1} Real (fun (y : Real) => HasSubset.Subset.{0} (Set.{0} EReal) (Set.hasSubset.{0} EReal) (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) y)) s))
but is expected to have type
  forall {s : Set.{0} EReal}, Iff (Membership.mem.{0, 0} (Set.{0} EReal) (Filter.{0} EReal) (instMembershipSetFilter.{0} EReal) s (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Top.top.{0} EReal EReal.instTopEReal))) (Exists.{1} Real (fun (y : Real) => HasSubset.Subset.{0} (Set.{0} EReal) (Set.instHasSubsetSet.{0} EReal) (Set.Ioi.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) (Real.toEReal y)) s))
Case conversion may be inaccurate. Consider using '#align ereal.mem_nhds_top_iff EReal.mem_nhds_top_iffₓ'. -/
theorem mem_nhds_top_iff {s : Set EReal} : s ∈ 𝓝 (⊤ : EReal) ↔ ∃ y : ℝ, Ioi (y : EReal) ⊆ s :=
  by
  rw [nhds_top', mem_infi_of_directed]
  · rfl
  exact fun x y => ⟨max x y, by simp [le_refl], by simp [le_refl]⟩
#align ereal.mem_nhds_top_iff EReal.mem_nhds_top_iff

/- warning: ereal.tendsto_nhds_top_iff_real -> EReal.tendsto_nhds_top_iff_real is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {m : α -> EReal} {f : Filter.{u1} α}, Iff (Filter.Tendsto.{u1, 0} α EReal m f (nhds.{0} EReal EReal.topologicalSpace (Top.top.{0} EReal EReal.hasTop))) (forall (x : Real), Filter.Eventually.{u1} α (fun (a : α) => LT.lt.{0} EReal (Preorder.toLT.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) x) (m a)) f)
but is expected to have type
  forall {α : Type.{u1}} {m : α -> EReal} {f : Filter.{u1} α}, Iff (Filter.Tendsto.{u1, 0} α EReal m f (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Top.top.{0} EReal EReal.instTopEReal))) (forall (x : Real), Filter.Eventually.{u1} α (fun (a : α) => LT.lt.{0} EReal (Preorder.toLT.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder)) (Real.toEReal x) (m a)) f)
Case conversion may be inaccurate. Consider using '#align ereal.tendsto_nhds_top_iff_real EReal.tendsto_nhds_top_iff_realₓ'. -/
theorem tendsto_nhds_top_iff_real {α : Type _} {m : α → EReal} {f : Filter α} :
    Tendsto m f (𝓝 ⊤) ↔ ∀ x : ℝ, ∀ᶠ a in f, ↑x < m a := by
  simp only [nhds_top', mem_Ioi, tendsto_infi, tendsto_principal]
#align ereal.tendsto_nhds_top_iff_real EReal.tendsto_nhds_top_iff_real

/- warning: ereal.nhds_bot -> EReal.nhds_bot is a dubious translation:
lean 3 declaration is
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.topologicalSpace (Bot.bot.{0} EReal EReal.hasBot)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) EReal (fun (a : EReal) => infᵢ.{0, 0} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) (Ne.{1} EReal a (Bot.bot.{0} EReal EReal.hasBot)) (fun (H : Ne.{1} EReal a (Bot.bot.{0} EReal EReal.hasBot)) => Filter.principal.{0} EReal (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) a))))
but is expected to have type
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Bot.bot.{0} EReal instERealBot)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) EReal (fun (a : EReal) => infᵢ.{0, 0} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) (Ne.{1} EReal a (Bot.bot.{0} EReal instERealBot)) (fun (H : Ne.{1} EReal a (Bot.bot.{0} EReal instERealBot)) => Filter.principal.{0} EReal (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) a))))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_bot EReal.nhds_botₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (a «expr ≠ » «expr⊥»()) -/
theorem nhds_bot : 𝓝 (⊥ : EReal) = ⨅ (a) (_ : a ≠ ⊥), 𝓟 (Iio a) :=
  nhds_bot_order.trans <| by simp [bot_lt_iff_ne_bot]
#align ereal.nhds_bot EReal.nhds_bot

/- warning: ereal.nhds_bot' -> EReal.nhds_bot' is a dubious translation:
lean 3 declaration is
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.topologicalSpace (Bot.bot.{0} EReal EReal.hasBot)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toHasInf.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.completeLattice.{0} EReal))) Real (fun (a : Real) => Filter.principal.{0} EReal (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a))))
but is expected to have type
  Eq.{1} (Filter.{0} EReal) (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Bot.bot.{0} EReal instERealBot)) (infᵢ.{0, 1} (Filter.{0} EReal) (ConditionallyCompleteLattice.toInfSet.{0} (Filter.{0} EReal) (CompleteLattice.toConditionallyCompleteLattice.{0} (Filter.{0} EReal) (Filter.instCompleteLatticeFilter.{0} EReal))) Real (fun (a : Real) => Filter.principal.{0} EReal (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) (Real.toEReal a))))
Case conversion may be inaccurate. Consider using '#align ereal.nhds_bot' EReal.nhds_bot'ₓ'. -/
theorem nhds_bot' : 𝓝 (⊥ : EReal) = ⨅ a : ℝ, 𝓟 (Iio a) :=
  by
  rw [nhds_bot]
  apply le_antisymm
  · exact infᵢ_mono' fun x => ⟨x, by simp⟩
  · refine' le_infᵢ fun r => le_infᵢ fun hr => _
    induction r using EReal.rec
    · simpa using hr
    · exact infᵢ_le _ _
    · exact (infᵢ_le _ 0).trans (by simp)
#align ereal.nhds_bot' EReal.nhds_bot'

/- warning: ereal.mem_nhds_bot_iff -> EReal.mem_nhds_bot_iff is a dubious translation:
lean 3 declaration is
  forall {s : Set.{0} EReal}, Iff (Membership.Mem.{0, 0} (Set.{0} EReal) (Filter.{0} EReal) (Filter.hasMem.{0} EReal) s (nhds.{0} EReal EReal.topologicalSpace (Bot.bot.{0} EReal EReal.hasBot))) (Exists.{1} Real (fun (y : Real) => HasSubset.Subset.{0} (Set.{0} EReal) (Set.hasSubset.{0} EReal) (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) y)) s))
but is expected to have type
  forall {s : Set.{0} EReal}, Iff (Membership.mem.{0, 0} (Set.{0} EReal) (Filter.{0} EReal) (instMembershipSetFilter.{0} EReal) s (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Bot.bot.{0} EReal instERealBot))) (Exists.{1} Real (fun (y : Real) => HasSubset.Subset.{0} (Set.{0} EReal) (Set.instHasSubsetSet.{0} EReal) (Set.Iio.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder) (Real.toEReal y)) s))
Case conversion may be inaccurate. Consider using '#align ereal.mem_nhds_bot_iff EReal.mem_nhds_bot_iffₓ'. -/
theorem mem_nhds_bot_iff {s : Set EReal} : s ∈ 𝓝 (⊥ : EReal) ↔ ∃ y : ℝ, Iio (y : EReal) ⊆ s :=
  by
  rw [nhds_bot', mem_infi_of_directed]
  · rfl
  exact fun x y => ⟨min x y, by simp [le_refl], by simp [le_refl]⟩
#align ereal.mem_nhds_bot_iff EReal.mem_nhds_bot_iff

/- warning: ereal.tendsto_nhds_bot_iff_real -> EReal.tendsto_nhds_bot_iff_real is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {m : α -> EReal} {f : Filter.{u1} α}, Iff (Filter.Tendsto.{u1, 0} α EReal m f (nhds.{0} EReal EReal.topologicalSpace (Bot.bot.{0} EReal EReal.hasBot))) (forall (x : Real), Filter.Eventually.{u1} α (fun (a : α) => LT.lt.{0} EReal (Preorder.toLT.{0} EReal (PartialOrder.toPreorder.{0} EReal (CompleteSemilatticeInf.toPartialOrder.{0} EReal (CompleteLattice.toCompleteSemilatticeInf.{0} EReal (CompleteLinearOrder.toCompleteLattice.{0} EReal EReal.completeLinearOrder))))) (m a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) x)) f)
but is expected to have type
  forall {α : Type.{u1}} {m : α -> EReal} {f : Filter.{u1} α}, Iff (Filter.Tendsto.{u1, 0} α EReal m f (nhds.{0} EReal EReal.instTopologicalSpaceEReal (Bot.bot.{0} EReal instERealBot))) (forall (x : Real), Filter.Eventually.{u1} α (fun (a : α) => LT.lt.{0} EReal (Preorder.toLT.{0} EReal (PartialOrder.toPreorder.{0} EReal instERealPartialOrder)) (m a) (Real.toEReal x)) f)
Case conversion may be inaccurate. Consider using '#align ereal.tendsto_nhds_bot_iff_real EReal.tendsto_nhds_bot_iff_realₓ'. -/
theorem tendsto_nhds_bot_iff_real {α : Type _} {m : α → EReal} {f : Filter α} :
    Tendsto m f (𝓝 ⊥) ↔ ∀ x : ℝ, ∀ᶠ a in f, m a < x := by
  simp only [nhds_bot', mem_Iio, tendsto_infi, tendsto_principal]
#align ereal.tendsto_nhds_bot_iff_real EReal.tendsto_nhds_bot_iff_real

/-! ### Continuity of addition -/

/- warning: ereal.continuous_at_add_coe_coe -> EReal.continuousAt_add_coe_coe is a dubious translation:
lean 3 declaration is
  forall (a : Real) (b : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) b))
but is expected to have type
  forall (a : Real) (b : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Real.toEReal a) (Real.toEReal b))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_coe_coe EReal.continuousAt_add_coe_coeₓ'. -/
theorem continuousAt_add_coe_coe (a b : ℝ) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (a, b) := by
  simp only [ContinuousAt, nhds_coe_coe, ← coe_add, tendsto_map'_iff, (· ∘ ·), tendsto_coe,
    tendsto_add]
#align ereal.continuous_at_add_coe_coe EReal.continuousAt_add_coe_coe

/- warning: ereal.continuous_at_add_top_coe -> EReal.continuousAt_add_top_coe is a dubious translation:
lean 3 declaration is
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Top.top.{0} EReal EReal.hasTop) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a))
but is expected to have type
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Top.top.{0} EReal EReal.instTopEReal) (Real.toEReal a))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_top_coe EReal.continuousAt_add_top_coeₓ'. -/
theorem continuousAt_add_top_coe (a : ℝ) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (⊤, a) :=
  by
  simp only [ContinuousAt, tendsto_nhds_top_iff_real, top_add_coe, nhds_prod_eq]
  intro r
  rw [eventually_prod_iff]
  refine'
    ⟨fun z => ((r - (a - 1) : ℝ) : EReal) < z, Ioi_mem_nhds (coe_lt_top _), fun z =>
      ((a - 1 : ℝ) : EReal) < z, Ioi_mem_nhds (by simp [-EReal.coe_sub]), fun x hx y hy => _⟩
  dsimp
  convert add_lt_add hx hy
  simp
#align ereal.continuous_at_add_top_coe EReal.continuousAt_add_top_coe

/- warning: ereal.continuous_at_add_coe_top -> EReal.continuousAt_add_coe_top is a dubious translation:
lean 3 declaration is
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a) (Top.top.{0} EReal EReal.hasTop))
but is expected to have type
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Real.toEReal a) (Top.top.{0} EReal EReal.instTopEReal))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_coe_top EReal.continuousAt_add_coe_topₓ'. -/
theorem continuousAt_add_coe_top (a : ℝ) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (a, ⊤) :=
  by
  change ContinuousAt ((fun p : EReal × EReal => p.2 + p.1) ∘ Prod.swap) (a, ⊤)
  apply ContinuousAt.comp _ continuous_swap.continuous_at
  simp_rw [add_comm]
  exact continuous_at_add_top_coe a
#align ereal.continuous_at_add_coe_top EReal.continuousAt_add_coe_top

/- warning: ereal.continuous_at_add_top_top -> EReal.continuousAt_add_top_top is a dubious translation:
lean 3 declaration is
  ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Top.top.{0} EReal EReal.hasTop) (Top.top.{0} EReal EReal.hasTop))
but is expected to have type
  ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Top.top.{0} EReal EReal.instTopEReal) (Top.top.{0} EReal EReal.instTopEReal))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_top_top EReal.continuousAt_add_top_topₓ'. -/
theorem continuousAt_add_top_top : ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (⊤, ⊤) :=
  by
  simp only [ContinuousAt, tendsto_nhds_top_iff_real, top_add_top, nhds_prod_eq]
  intro r
  rw [eventually_prod_iff]
  refine'
    ⟨fun z => (r : EReal) < z, Ioi_mem_nhds (coe_lt_top _), fun z => ((0 : ℝ) : EReal) < z,
      Ioi_mem_nhds (by simp [zero_lt_one]), fun x hx y hy => _⟩
  dsimp
  convert add_lt_add hx hy
  simp
#align ereal.continuous_at_add_top_top EReal.continuousAt_add_top_top

/- warning: ereal.continuous_at_add_bot_coe -> EReal.continuousAt_add_bot_coe is a dubious translation:
lean 3 declaration is
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Bot.bot.{0} EReal EReal.hasBot) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a))
but is expected to have type
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Bot.bot.{0} EReal instERealBot) (Real.toEReal a))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_bot_coe EReal.continuousAt_add_bot_coeₓ'. -/
theorem continuousAt_add_bot_coe (a : ℝ) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (⊥, a) :=
  by
  simp only [ContinuousAt, tendsto_nhds_bot_iff_real, nhds_prod_eq, bot_add]
  intro r
  rw [eventually_prod_iff]
  refine'
    ⟨fun z => z < ((r - (a + 1) : ℝ) : EReal), Iio_mem_nhds (bot_lt_coe _), fun z =>
      z < ((a + 1 : ℝ) : EReal), Iio_mem_nhds (by simp [-coe_add, zero_lt_one]), fun x hx y hy => _⟩
  convert add_lt_add hx hy
  rw [sub_add_cancel]
#align ereal.continuous_at_add_bot_coe EReal.continuousAt_add_bot_coe

/- warning: ereal.continuous_at_add_coe_bot -> EReal.continuousAt_add_coe_bot is a dubious translation:
lean 3 declaration is
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Real EReal (HasLiftT.mk.{1, 1} Real EReal (CoeTCₓ.coe.{1, 1} Real EReal (coeBase.{1, 1} Real EReal EReal.hasCoe))) a) (Bot.bot.{0} EReal EReal.hasBot))
but is expected to have type
  forall (a : Real), ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Real.toEReal a) (Bot.bot.{0} EReal instERealBot))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_coe_bot EReal.continuousAt_add_coe_botₓ'. -/
theorem continuousAt_add_coe_bot (a : ℝ) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (a, ⊥) :=
  by
  change ContinuousAt ((fun p : EReal × EReal => p.2 + p.1) ∘ Prod.swap) (a, ⊥)
  apply ContinuousAt.comp _ continuous_swap.continuous_at
  simp_rw [add_comm]
  exact continuous_at_add_bot_coe a
#align ereal.continuous_at_add_coe_bot EReal.continuousAt_add_coe_bot

/- warning: ereal.continuous_at_add_bot_bot -> EReal.continuousAt_add_bot_bot is a dubious translation:
lean 3 declaration is
  ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Bot.bot.{0} EReal EReal.hasBot) (Bot.bot.{0} EReal EReal.hasBot))
but is expected to have type
  ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) (Prod.mk.{0, 0} EReal EReal (Bot.bot.{0} EReal instERealBot) (Bot.bot.{0} EReal instERealBot))
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add_bot_bot EReal.continuousAt_add_bot_botₓ'. -/
theorem continuousAt_add_bot_bot : ContinuousAt (fun p : EReal × EReal => p.1 + p.2) (⊥, ⊥) :=
  by
  simp only [ContinuousAt, tendsto_nhds_bot_iff_real, nhds_prod_eq, bot_add]
  intro r
  rw [eventually_prod_iff]
  refine'
    ⟨fun z => z < r, Iio_mem_nhds (bot_lt_coe _), fun z => z < 0, Iio_mem_nhds (bot_lt_coe _),
      fun x hx y hy => _⟩
  dsimp
  convert add_lt_add hx hy
  simp
#align ereal.continuous_at_add_bot_bot EReal.continuousAt_add_bot_bot

/- warning: ereal.continuous_at_add -> EReal.continuousAt_add is a dubious translation:
lean 3 declaration is
  forall {p : Prod.{0, 0} EReal EReal}, (Or (Ne.{1} EReal (Prod.fst.{0, 0} EReal EReal p) (Top.top.{0} EReal EReal.hasTop)) (Ne.{1} EReal (Prod.snd.{0, 0} EReal EReal p) (Bot.bot.{0} EReal EReal.hasBot))) -> (Or (Ne.{1} EReal (Prod.fst.{0, 0} EReal EReal p) (Bot.bot.{0} EReal EReal.hasBot)) (Ne.{1} EReal (Prod.snd.{0, 0} EReal EReal p) (Top.top.{0} EReal EReal.hasTop))) -> (ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (Prod.topologicalSpace.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace) EReal.topologicalSpace (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toHasAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal EReal.addMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) p)
but is expected to have type
  forall {p : Prod.{0, 0} EReal EReal}, (Or (Ne.{1} EReal (Prod.fst.{0, 0} EReal EReal p) (Top.top.{0} EReal EReal.instTopEReal)) (Ne.{1} EReal (Prod.snd.{0, 0} EReal EReal p) (Bot.bot.{0} EReal instERealBot))) -> (Or (Ne.{1} EReal (Prod.fst.{0, 0} EReal EReal p) (Bot.bot.{0} EReal instERealBot)) (Ne.{1} EReal (Prod.snd.{0, 0} EReal EReal p) (Top.top.{0} EReal EReal.instTopEReal))) -> (ContinuousAt.{0, 0} (Prod.{0, 0} EReal EReal) EReal (instTopologicalSpaceProd.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal) EReal.instTopologicalSpaceEReal (fun (p : Prod.{0, 0} EReal EReal) => HAdd.hAdd.{0, 0, 0} EReal EReal EReal (instHAdd.{0} EReal (AddZeroClass.toAdd.{0} EReal (AddMonoid.toAddZeroClass.{0} EReal instERealAddMonoid))) (Prod.fst.{0, 0} EReal EReal p) (Prod.snd.{0, 0} EReal EReal p)) p)
Case conversion may be inaccurate. Consider using '#align ereal.continuous_at_add EReal.continuousAt_addₓ'. -/
/-- The addition on `ereal` is continuous except where it doesn't make sense (i.e., at `(⊥, ⊤)`
and at `(⊤, ⊥)`). -/
theorem continuousAt_add {p : EReal × EReal} (h : p.1 ≠ ⊤ ∨ p.2 ≠ ⊥) (h' : p.1 ≠ ⊥ ∨ p.2 ≠ ⊤) :
    ContinuousAt (fun p : EReal × EReal => p.1 + p.2) p :=
  by
  rcases p with ⟨x, y⟩
  induction x using EReal.rec <;> induction y using EReal.rec
  · exact continuous_at_add_bot_bot
  · exact continuous_at_add_bot_coe _
  · simpa using h'
  · exact continuous_at_add_coe_bot _
  · exact continuous_at_add_coe_coe _ _
  · exact continuous_at_add_coe_top _
  · simpa using h
  · exact continuous_at_add_top_coe _
  · exact continuous_at_add_top_top
#align ereal.continuous_at_add EReal.continuousAt_add

/-! ### Negation-/

/- warning: ereal.neg_homeo -> EReal.negHomeo is a dubious translation:
lean 3 declaration is
  Homeomorph.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace
but is expected to have type
  Homeomorph.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal
Case conversion may be inaccurate. Consider using '#align ereal.neg_homeo EReal.negHomeoₓ'. -/
/-- Negation on `ereal` as a homeomorphism -/
def negHomeo : EReal ≃ₜ EReal :=
  negOrderIso.toHomeomorph
#align ereal.neg_homeo EReal.negHomeo

/- warning: ereal.continuous_neg -> EReal.continuous_neg is a dubious translation:
lean 3 declaration is
  Continuous.{0, 0} EReal EReal EReal.topologicalSpace EReal.topologicalSpace (fun (x : EReal) => Neg.neg.{0} EReal EReal.hasNeg x)
but is expected to have type
  Continuous.{0, 0} EReal EReal EReal.instTopologicalSpaceEReal EReal.instTopologicalSpaceEReal (fun (x : EReal) => Neg.neg.{0} EReal EReal.instNegEReal x)
Case conversion may be inaccurate. Consider using '#align ereal.continuous_neg EReal.continuous_negₓ'. -/
theorem continuous_neg : Continuous fun x : EReal => -x :=
  negHomeo.Continuous
#align ereal.continuous_neg EReal.continuous_neg

end EReal


import tactic.suggest

constant α : Type
constant reverse_core : list α → list α → list α
constant reverse : list α → list α
constant concat : list α → list α → list α

axiom reverse_core.base (l : list α) : reverse_core [] l = l
axiom reverse_core.step (x : α) (xs l : list α) :
  reverse_core (x :: xs) l = reverse_core xs (x :: l)

axiom reverse.def (xs : list α) : reverse xs = reverse_core xs []

axiom concat.base (l : list α) : concat [] l = l
axiom concat.step (x : α) (xs l : list α) :
  concat (x :: xs) l = x :: concat xs l

lemma reverse.single (x : α) : reverse [x] = [x] :=
begin
  rw reverse.def,
  rw reverse_core.step,
  exact reverse_core.base _,
  -- suggest [reverse_core.base, reverse_core.step, concat.step, concat.base, reverse.def],

  -- type_check ((reverse.def [x]).symm.congr (reverse_core.base [x])).mp,
  -- type_check (reverse_core.base [x]),
  -- type_check (reverse_core.step x list.nil list.nil),

  -- exact ((reverse.def [x]).symm.congr
  --       (reverse_core.base [x])).mp
  --       (reverse_core.step x list.nil list.nil),

  -- exact ((λ {α : Type} {x₁ x₂ y₁ y₂ : α} (h₁ : x₁ = y₁) (h₂ : x₂ = y₂), (h₁.congr h₂).mp)
  --  (reverse_core.base [x])
  --  (reverse.def [x]).symm
  --  (reverse_core.step x list.nil list.nil).symm).symm,
end

#print reverse.single

example (a b c : α) : reverse [a, b, c] = [c, b, a] :=
begin
  rw reverse.def,
  repeat { rw reverse_core.step },
  rw reverse_core.base,
end

-- local notation a `+++` b := concat a b

lemma concat.list_nil (l : list α) : concat l [] = l := sorry

lemma reverse_concat'' (xs ys : list α) :
  reverse (concat xs ys) = concat (reverse ys) (reverse xs) :=
begin
  repeat { rw reverse.def },
  induction xs with x xs ih, {
    rw reverse_core.base,
    rw concat.base,
    rw concat.list_nil,
  }, {
    rw reverse_core.step,
    rw concat.step,
    rw reverse_core.step,
    sorry,
  },
end

-- lemma reverse_concat' (xs ys : list α) :
--   reverse (concat xs ys) = concat (reverse ys) (reverse xs)
-- | []        ys :=
-- begin

-- end
-- | (x :: xs) ys := begin

-- end

-- lemma reverse_concat {α : Type} : ∀ xs ys : list α,
--   reverse (xs ++ ys) = reverse ys ++ reverse xs
-- | []        ys := by simp [reverse]
-- | (x :: xs) ys := by simp [reverse, reverse_append xs]


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.bool.all_any
! leanprover-community/mathlib commit 10708587e81b68c763fcdb7505f279d52e569768
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Basic

/-!
# Boolean quantifiers

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

This proves a few properties about `list.all` and `list.any`, which are the `bool` universal and
existential quantifiers. Their definitions are in core Lean.
-/

variable {α : Type _} {p : α → Prop} [DecidablePred p] {l : List α} {a : α}

namespace List

#print List.all_nil /-
@[simp]
theorem all_nil (p : α → Bool) : all [] p = true :=
  rfl
#align list.all_nil List.all_nil
-/

@[simp]
theorem all_cons (p : α → Bool) (a : α) (l : List α) : all (a :: l) p = (p a && all l p) :=
  rfl
#align list.all_cons List.all_consₓ

#print List.all_iff_forall /-
theorem all_iff_forall {p : α → Bool} : all l p ↔ ∀ a ∈ l, p a :=
  by
  induction' l with a l ih
  · exact iff_of_true rfl (forall_mem_nil _)
  simp only [all_cons, Bool.and_coe_iff, ih, forall_mem_cons]
#align list.all_iff_forall List.all_iff_forall
-/

#print List.all_iff_forall_prop /-
theorem all_iff_forall_prop : (all l fun a => p a) ↔ ∀ a ∈ l, p a := by
  simp only [all_iff_forall, Bool.of_decide_iff]
#align list.all_iff_forall_prop List.all_iff_forall_prop
-/

#print List.any_nil /-
@[simp]
theorem any_nil (p : α → Bool) : any [] p = false :=
  rfl
#align list.any_nil List.any_nil
-/

@[simp]
theorem any_cons (p : α → Bool) (a : α) (l : List α) : any (a :: l) p = (p a || any l p) :=
  rfl
#align list.any_cons List.any_consₓ

/- warning: list.any_iff_exists -> List.any_iff_exists is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : List.{u1} α} {p : α -> Bool}, Iff (coeSort.{1, 1} Bool Prop coeSortBool (List.any.{u1} α l p)) (Exists.{succ u1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) (fun (H : Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) => coeSort.{1, 1} Bool Prop coeSortBool (p a))))
but is expected to have type
  forall {α : Type.{u1}} {l : List.{u1} α} {p : α -> Bool}, Iff (Eq.{1} Bool (List.any.{u1} α l p) Bool.true) (Exists.{succ u1} α (fun (a : α) => And (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) a l) (Eq.{1} Bool (p a) Bool.true)))
Case conversion may be inaccurate. Consider using '#align list.any_iff_exists List.any_iff_existsₓ'. -/
theorem any_iff_exists {p : α → Bool} : any l p ↔ ∃ a ∈ l, p a :=
  by
  induction' l with a l ih
  · exact iff_of_false Bool.not_false' (not_exists_mem_nil _)
  simp only [any_cons, Bool.or_coe_iff, ih, exists_mem_cons_iff]
#align list.any_iff_exists List.any_iff_exists

/- warning: list.any_iff_exists_prop -> List.any_iff_exists_prop is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {p : α -> Prop} [_inst_1 : DecidablePred.{succ u1} α p] {l : List.{u1} α}, Iff (coeSort.{1, 1} Bool Prop coeSortBool (List.any.{u1} α l (fun (a : α) => Decidable.decide (p a) (_inst_1 a)))) (Exists.{succ u1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) (fun (H : Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) => p a)))
but is expected to have type
  forall {α : Type.{u1}} {p : α -> Prop} [_inst_1 : DecidablePred.{succ u1} α p] {l : List.{u1} α}, Iff (Eq.{1} Bool (List.any.{u1} α l (fun (a : α) => Decidable.decide (p a) (_inst_1 a))) Bool.true) (Exists.{succ u1} α (fun (a : α) => And (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) a l) (p a)))
Case conversion may be inaccurate. Consider using '#align list.any_iff_exists_prop List.any_iff_exists_propₓ'. -/
theorem any_iff_exists_prop : (any l fun a => p a) ↔ ∃ a ∈ l, p a := by simp [any_iff_exists]
#align list.any_iff_exists_prop List.any_iff_exists_prop

#print List.any_of_mem /-
theorem any_of_mem {p : α → Bool} (h₁ : a ∈ l) (h₂ : p a) : any l p :=
  any_iff_exists.2 ⟨_, h₁, h₂⟩
#align list.any_of_mem List.any_of_mem
-/

end List


/-
Copyright (c) 2019 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl

! This file was ported from Lean 3 source module topology.list
! leanprover-community/mathlib commit 48085f140e684306f9e7da907cd5932056d1aded
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.Constructions
import Mathlib.Topology.Algebra.Monoid

/-!
# Topology on lists and vectors

-/

open TopologicalSpace Set Filter

open Topology Filter

variable {α : Type _} {β : Type _} [TopologicalSpace α] [TopologicalSpace β]

instance : TopologicalSpace (List α) :=
  TopologicalSpace.mkOfNhds (traverse nhds)

theorem nhds_list (as : List α) : 𝓝 as = traverse 𝓝 as := by
  refine' nhds_mkOfNhds _ _ _ _
  · intro l
    induction l
    case nil => exact le_rfl
    case cons a l ih =>
      suffices List.cons <$> pure a <*> pure l ≤ List.cons <$> 𝓝 a <*> traverse 𝓝 l by
        simpa only [functor_norm] using this
      exact Filter.seq_mono (Filter.map_mono <| pure_le_nhds a) ih
  · intro l s hs
    rcases(mem_traverse_iff _ _).1 hs with ⟨u, hu, hus⟩
    clear as hs
    have : ∃ v : List (Set α), l.Forall₂ (fun a s => IsOpen s ∧ a ∈ s) v ∧ sequence v ⊆ s
    induction hu generalizing s
    case nil _hs  =>
      exists []
      simp only [List.forall₂_nil_left_iff, exists_eq_left]
      exact ⟨trivial, hus⟩
    -- porting note -- renamed reordered variables based on previous types
    case cons a s as ss hts h ht _ ih =>
      rcases mem_nhds_iff.1 ht with ⟨u, hut, hu⟩
      rcases ih _ Subset.rfl with ⟨v, hv, hvss⟩
      exact
        ⟨u::v, List.Forall₂.cons hu hv,
          Subset.trans (Set.seq_mono (Set.image_subset _ hut) hvss) hus⟩
    rcases this with ⟨v, hv, hvs⟩
    refine' ⟨sequence v, mem_traverse _ _ _, hvs, _⟩
    · exact hv.imp fun a s ⟨hs, ha⟩ => IsOpen.mem_nhds hs ha
    · intro u hu
      have hu := (List.mem_traverse _ _).1 hu
      have : List.Forall₂ (fun a s => IsOpen s ∧ a ∈ s) u v :=
        by
        refine' List.Forall₂.flip _
        replace hv := hv.flip
        simp only [List.forall₂_and_left, flip] at hv⊢
        exact ⟨hv.1, hu.flip⟩
      refine' mem_of_superset _ hvs
      exact mem_traverse _ _ (this.imp fun a s ⟨hs, ha⟩ => IsOpen.mem_nhds hs ha)
#align nhds_list nhds_list

@[simp]
theorem nhds_nil : 𝓝 ([] : List α) = pure [] := by
  rw [nhds_list, List.traverse_nil _]
#align nhds_nil nhds_nil

theorem nhds_cons (a : α) (l : List α) : 𝓝 (a::l) = List.cons <$> 𝓝 a <*> 𝓝 l := by
  rw [nhds_list, List.traverse_cons _, ← nhds_list]
#align nhds_cons nhds_cons

theorem List.tendsto_cons {a : α} {l : List α} :
    Tendsto (fun p : α × List α => List.cons p.1 p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (a::l)) := by
  rw [nhds_cons, Tendsto, Filter.map_prod]; exact le_rfl
#align list.tendsto_cons List.tendsto_cons

theorem Filter.Tendsto.cons {α : Type _} {f : α → β} {g : α → List β} {a : Filter α} {b : β}
    {l : List β} (hf : Tendsto f a (𝓝 b)) (hg : Tendsto g a (𝓝 l)) :
    Tendsto (fun a => List.cons (f a) (g a)) a (𝓝 (b::l)) :=
  List.tendsto_cons.comp (Tendsto.prod_mk hf hg)
#align filter.tendsto.cons Filter.Tendsto.cons

namespace List

theorem tendsto_cons_iff {β : Type _} {f : List α → β} {b : Filter β} {a : α} {l : List α} :
    Tendsto f (𝓝 (a::l)) b ↔ Tendsto (fun p : α × List α => f (p.1::p.2)) (𝓝 a ×ᶠ 𝓝 l) b := by
  have : 𝓝 (a::l) = (𝓝 a ×ᶠ 𝓝 l).map fun p : α × List α => p.1::p.2 :=
    by
    simp only [nhds_cons, Filter.prod_eq, (Filter.map_def _ _).symm,
      (Filter.seq_eq_filter_seq _ _).symm]
    simp [-Filter.map_def, (· ∘ ·), functor_norm]
  rw [this, Filter.tendsto_map'_iff] ; dsimp; rfl
#align list.tendsto_cons_iff List.tendsto_cons_iff

theorem continuous_cons : Continuous fun x : α × List α => (x.1::x.2 : List α) :=
  continuous_iff_continuousAt.mpr fun ⟨_x, _y⟩ => continuousAt_fst.cons continuousAt_snd
#align list.continuous_cons List.continuous_cons

theorem tendsto_nhds {β : Type _} {f : List α → β} {r : List α → Filter β}
    (h_nil : Tendsto f (pure []) (r []))
    (h_cons :
      ∀ l a,
        Tendsto f (𝓝 l) (r l) →
          Tendsto (fun p : α × List α => f (p.1::p.2)) (𝓝 a ×ᶠ 𝓝 l) (r (a::l))) :
    ∀ l, Tendsto f (𝓝 l) (r l)
  | [] => by rwa [nhds_nil]
  | a::l => by
    rw [tendsto_cons_iff];  exact h_cons l a (@tendsto_nhds _ _ _ h_nil h_cons l)

#align list.tendsto_nhds List.tendsto_nhds

theorem continuousAt_length : ∀ l : List α, ContinuousAt List.length l := by
  simp only [ContinuousAt, nhds_discrete]
  refine' tendsto_nhds _ _
  · exact tendsto_pure_pure _ _
  · intro l a ih
    dsimp only [List.length]
    refine' Tendsto.comp (tendsto_pure_pure (fun x => x + 1) _) _
    refine' Tendsto.comp ih tendsto_snd
#align list.continuous_at_length List.continuousAt_length

theorem tendsto_insert_nth' {a : α} :
    ∀ {n : ℕ} {l : List α},
      Tendsto (fun p : α × List α => insertNth n p.1 p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (insertNth n a l))
  | 0, l => tendsto_cons
  | n + 1, [] => by simp
  | n + 1, a'::l =>
    by
    have :
      𝓝 a ×ᶠ 𝓝 (a'::l) = (𝓝 a ×ᶠ (𝓝 a' ×ᶠ 𝓝 l)).map fun p : α × α × List α => (p.1, p.2.1::p.2.2) :=
      by
      simp only [nhds_cons, Filter.prod_eq, ← Filter.map_def, ← Filter.seq_eq_filter_seq]
      simp [-Filter.map_def, (· ∘ ·), functor_norm]
    rw [this, tendsto_map'_iff]
    exact
      (tendsto_fst.comp tendsto_snd).cons
        ((@tendsto_insert_nth' _ n l).comp <| tendsto_fst.prod_mk <| tendsto_snd.comp tendsto_snd)
#align list.tendsto_insert_nth' List.tendsto_insert_nth'

theorem tendsto_insertNth {β} {n : ℕ} {a : α} {l : List α} {f : β → α} {g : β → List α}
    {b : Filter β} (hf : Tendsto f b (𝓝 a)) (hg : Tendsto g b (𝓝 l)) :
    Tendsto (fun b : β => insertNth n (f b) (g b)) b (𝓝 (insertNth n a l)) :=
  tendsto_insert_nth'.comp (Tendsto.prod_mk hf hg)
#align list.tendsto_insert_nth List.tendsto_insertNth

theorem continuous_insertNth {n : ℕ} : Continuous fun p : α × List α => insertNth n p.1 p.2 :=
  continuous_iff_continuousAt.mpr fun ⟨a, l⟩ => by
    rw [ContinuousAt, nhds_prod_eq]; exact tendsto_insert_nth'
#align list.continuous_insert_nth List.continuous_insertNth

theorem tendsto_removeNth :
    ∀ {n : ℕ} {l : List α}, Tendsto (fun l => removeNth l n) (𝓝 l) (𝓝 (removeNth l n))
  | _, [] => by rw [nhds_nil]; exact tendsto_pure_nhds _ _
  | 0, a::l => by rw [tendsto_cons_iff]; exact tendsto_snd
  | n + 1, a::l => by
    rw [tendsto_cons_iff]
    dsimp [removeNth]
    exact tendsto_fst.cons ((@tendsto_removeNth n l).comp tendsto_snd)
#align list.tendsto_remove_nth List.tendsto_removeNth

@[to_additive]
theorem tendsto_prod [Monoid α] [ContinuousMul α] {l : List α} :
    Tendsto List.prod (𝓝 l) (𝓝 l.prod) := by
  induction' l with x l ih
  · simp (config := { contextual := true }) [nhds_nil, mem_of_mem_nhds, tendsto_pure_left]
  simp_rw [tendsto_cons_iff, prod_cons]
  have := continuous_iff_continuousAt.mp continuous_mul (x, l.prod)
  rw [ContinuousAt, nhds_prod_eq] at this
  exact this.comp (tendsto_id.prod_map ih)
#align list.tendsto_prod List.tendsto_prod
#align list.tendsto_sum List.tendsto_sum

@[to_additive]
theorem continuous_prod [Monoid α] [ContinuousMul α] : Continuous (prod : List α → α) :=
  continuous_iff_continuousAt.mpr fun _l => tendsto_prod
#align list.continuous_prod List.continuous_prod
#align list.continuous_sum List.continuous_sum

end List

namespace Vector

open List

instance (n : ℕ) : TopologicalSpace (Vector α n) := by unfold Vector; infer_instance

theorem tendsto_cons {n : ℕ} {a : α} {l : Vector α n} :
    Tendsto (fun p : α × Vector α n => p.1 ::ᵥ p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (a ::ᵥ l)) := by
  rw [tendsto_subtype_rng, cons_val]
  exact tendsto_fst.cons (Tendsto.comp continuousAt_subtype_val tendsto_snd)
#align vector.tendsto_cons Vector.tendsto_cons

theorem tendsto_insertNth {n : ℕ} {i : Fin (n + 1)} {a : α} :
    ∀ {l : Vector α n},
      Tendsto (fun p : α × Vector α n => insertNth p.1 i p.2) (𝓝 a ×ᶠ 𝓝 l) (𝓝 (insertNth a i l))
  | ⟨l, hl⟩ => by
    rw [insertNth, tendsto_subtype_rng]
    simp [insertNth_val]
    exact List.tendsto_insertNth tendsto_fst (Tendsto.comp continuousAt_subtype_val tendsto_snd : _)
#align vector.tendsto_insert_nth Vector.tendsto_insertNth

theorem continuous_insert_nth' {n : ℕ} {i : Fin (n + 1)} :
    Continuous fun p : α × Vector α n => insertNth p.1 i p.2 :=
  continuous_iff_continuousAt.mpr fun ⟨a, l⟩ => by
    rw [ContinuousAt, nhds_prod_eq]; exact tendsto_insertNth
#align vector.continuous_insert_nth' Vector.continuous_insert_nth'

theorem continuous_insertNth {n : ℕ} {i : Fin (n + 1)} {f : β → α} {g : β → Vector α n}
    (hf : Continuous f) (hg : Continuous g) : Continuous fun b => insertNth (f b) i (g b) :=
  continuous_insert_nth'.comp (hf.prod_mk hg : _)
#align vector.continuous_insert_nth Vector.continuous_insertNth

theorem continuousAt_removeNth {n : ℕ} {i : Fin (n + 1)} :
    ∀ {l : Vector α (n + 1)}, ContinuousAt (removeNth i) l
  | ⟨l, hl⟩ =>--  ∀{l:vector α (n+1)}, tendsto (remove_nth i) (𝓝 l) (𝓝 (remove_nth i l))
  --| ⟨l, hl⟩ :=
  by
    rw [ContinuousAt, removeNth, tendsto_subtype_rng]
    simp only [Vector.removeNth_val]
    exact Tendsto.comp List.tendsto_removeNth continuousAt_subtype_val
#align vector.continuous_at_remove_nth Vector.continuousAt_removeNth

theorem continuous_removeNth {n : ℕ} {i : Fin (n + 1)} :
    Continuous (removeNth i : Vector α (n + 1) → Vector α n) :=
  continuous_iff_continuousAt.mpr fun ⟨_a, _l⟩ => continuousAt_removeNth
#align vector.continuous_remove_nth Vector.continuous_removeNth

end Vector